From bfab9d68dbe652202c373f1f69221e67170d9a26 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 9 Oct 2023 14:39:22 +0200 Subject: [PATCH 001/198] Bump up version for future --- Flax.flaxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index ebcdc2830..9f851a457 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -2,8 +2,8 @@ "Name": "Flax", "Version": { "Major": 1, - "Minor": 7, - "Build": 6401 + "Minor": 8, + "Build": 6500 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.", From cfec5bb8018729e1f290e1570283e3db1b14ec34 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Thu, 12 Oct 2023 20:43:28 +0200 Subject: [PATCH 002/198] Fix #63 Seems to fix #63 , but it might have small side-effects if the mouse position tracking is not expected to go outside the client area. --- Source/Engine/Platform/Windows/WindowsInput.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Engine/Platform/Windows/WindowsInput.cpp b/Source/Engine/Platform/Windows/WindowsInput.cpp index 80bff7ca5..0ec0b5e25 100644 --- a/Source/Engine/Platform/Windows/WindowsInput.cpp +++ b/Source/Engine/Platform/Windows/WindowsInput.cpp @@ -196,6 +196,12 @@ bool WindowsMouse::WndProc(Window* window, const UINT msg, WPARAM wParam, LPARAM switch (msg) { case WM_MOUSEMOVE: + { + OnMouseMove(mousePos, window); + result = true; + break; + } + case WM_NCMOUSEMOVE: { OnMouseMove(mousePos, window); result = true; From 80a30f504abf780a5c79e0a25e23e63ad21daa59 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 15 Nov 2023 10:30:59 +0100 Subject: [PATCH 003/198] Add initial support for Precompiled Header Files (PCH) in MSVC compilation --- Source/FlaxEngine.pch.h | 25 +++++ Source/Tools/Flax.Build/Build/EngineModule.cs | 4 + .../Build/NativeCpp/BuildOptions.cs | 21 ++++ .../Build/NativeCpp/Builder.NativeCpp.cs | 13 +++ .../Build/NativeCpp/CompileEnvironment.cs | 20 +++- .../Build/NativeCpp/CompileOutput.cs | 5 + .../Platforms/Windows/WindowsToolchainBase.cs | 98 ++++++++++++++++++- 7 files changed, 180 insertions(+), 6 deletions(-) create mode 100644 Source/FlaxEngine.pch.h diff --git a/Source/FlaxEngine.pch.h b/Source/FlaxEngine.pch.h new file mode 100644 index 000000000..0a73fc464 --- /dev/null +++ b/Source/FlaxEngine.pch.h @@ -0,0 +1,25 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#pragma once + +// Include common engine headers +#include "Engine/Platform/Platform.h" +#include "Engine/Platform/StringUtils.h" +#include "Engine/Platform/CriticalSection.h" +#include "Engine/Core/Delegate.h" +#include "Engine/Core/Types/Guid.h" +#include "Engine/Core/Types/String.h" +#include "Engine/Core/Types/StringView.h" +#include "Engine/Core/Types/Variant.h" +#include "Engine/Core/Math/Vector2.h" +#include "Engine/Core/Math/Vector3.h" +#include "Engine/Core/Math/Vector4.h" +#include "Engine/Core/Math/Quaternion.h" +#include "Engine/Core/Math/BoundingBox.h" +#include "Engine/Core/Math/BoundingSphere.h" +#include "Engine/Core/Math/Transform.h" +#include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Collections/Dictionary.h" +#include "Engine/Core/Log.h" +#include "Engine/Scripting/ScriptingObject.h" +#include "Engine/Serialization/SerializationFwd.h" diff --git a/Source/Tools/Flax.Build/Build/EngineModule.cs b/Source/Tools/Flax.Build/Build/EngineModule.cs index 5d84761fb..8ee4050f9 100644 --- a/Source/Tools/Flax.Build/Build/EngineModule.cs +++ b/Source/Tools/Flax.Build/Build/EngineModule.cs @@ -35,6 +35,10 @@ namespace Flax.Build options.ScriptingAPI.Defines.Add("FLAX_GAME"); } + // Use custom precompiled header file for the engine to boost compilation time + options.CompileEnv.PrecompiledHeaderUsage = PrecompiledHeaderFileUsage.CreateManual; + options.CompileEnv.PrecompiledHeaderSource = Utilities.NormalizePath(Path.Combine(Globals.EngineRoot, "Source/FlaxEngine.pch.h")); + BinaryModuleName = "FlaxEngine"; options.ScriptingAPI.Defines.Add("FLAX"); options.ScriptingAPI.Defines.Add("FLAX_ASSERTIONS"); diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs b/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs index e161024b4..05871a331 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/BuildOptions.cs @@ -23,6 +23,27 @@ namespace Flax.Build.NativeCpp GenerateProject = 1, } + /// + /// Precompiled Headers Files (PCH) usage modes. + /// + public enum PrecompiledHeaderFileUsage + { + /// + /// Precompiled Headers Files (PCH) feature is disabled. + /// + None, + + /// + /// Enables creation and usage of the header file. The input source PCH will be precompiled and included. + /// + CreateManual, + + /// + /// Enables usage of the header file. The input source PCH will be included in the build (assuming it exists). + /// + UseManual, + } + /// /// The nullable context type used with reference types (C#). /// diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs index 225e46ba7..232b52ba0 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs @@ -32,6 +32,7 @@ namespace Flax.Build public IGrouping[] BinaryModules; public BuildTargetInfo BuildInfo; public Dictionary ReferenceBuilds = new Dictionary(); + public Dictionary PrecompiledHeaderFiles = new(); public BuildTargetBinaryModuleInfo FinReferenceBuildModule(string name) { @@ -484,6 +485,13 @@ namespace Flax.Build } } + // If the PCH was already created (eg. by other engine module) then simply reuse the same file + if (moduleOptions.CompileEnv.PrecompiledHeaderUsage == PrecompiledHeaderFileUsage.CreateManual && buildData.PrecompiledHeaderFiles.TryGetValue(moduleOptions.CompileEnv.PrecompiledHeaderSource, out var pch)) + { + moduleOptions.CompileEnv.PrecompiledHeaderUsage = PrecompiledHeaderFileUsage.UseManual; + moduleOptions.CompileEnv.PrecompiledHeaderFile = pch; + } + // Compile all source files var compilationOutput = buildData.Toolchain.CompileCppFiles(buildData.Graph, moduleOptions, cppFiles, moduleOptions.OutputFolder); foreach (var e in compilationOutput.ObjectFiles) @@ -493,6 +501,11 @@ namespace Flax.Build // TODO: find better way to add generated doc files to the target linker (module exports the output doc files?) buildData.TargetOptions.LinkEnv.DocumentationFiles.AddRange(compilationOutput.DocumentationFiles); } + if (moduleOptions.CompileEnv.PrecompiledHeaderUsage == PrecompiledHeaderFileUsage.CreateManual && !string.IsNullOrEmpty(compilationOutput.PrecompiledHeaderFile)) + { + // Cache PCH file to be used by other modules that reference the same file + buildData.PrecompiledHeaderFiles.Add(moduleOptions.CompileEnv.PrecompiledHeaderSource, compilationOutput.PrecompiledHeaderFile); + } if (buildData.Target.LinkType != TargetLinkType.Monolithic) { diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs b/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs index 72d0380e4..ad418019b 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs @@ -162,6 +162,21 @@ namespace Flax.Build.NativeCpp /// public readonly HashSet CustomArgs = new HashSet(); + /// + /// The Precompiled Header File (PCH) usage. + /// + public PrecompiledHeaderFileUsage PrecompiledHeaderUsage = PrecompiledHeaderFileUsage.None; + + /// + /// The Precompiled Header File (PCH) binary path. Null if not created. + /// + public string PrecompiledHeaderFile; + + /// + /// The Precompiled Header File (PCH) source path. Null if not provided. + /// + public string PrecompiledHeaderSource; + /// public object Clone() { @@ -184,7 +199,10 @@ namespace Flax.Build.NativeCpp StringPooling = StringPooling, IntrinsicFunctions = IntrinsicFunctions, BufferSecurityCheck = BufferSecurityCheck, - TreatWarningsAsErrors = TreatWarningsAsErrors + TreatWarningsAsErrors = TreatWarningsAsErrors, + PrecompiledHeaderUsage = PrecompiledHeaderUsage, + PrecompiledHeaderFile = PrecompiledHeaderFile, + PrecompiledHeaderSource = PrecompiledHeaderSource, }; clone.PreprocessorDefinitions.AddRange(PreprocessorDefinitions); clone.IncludePaths.AddRange(IncludePaths); diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/CompileOutput.cs b/Source/Tools/Flax.Build/Build/NativeCpp/CompileOutput.cs index ea75fa51c..bdef3a531 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/CompileOutput.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/CompileOutput.cs @@ -23,5 +23,10 @@ namespace Flax.Build.NativeCpp /// The result documentation files. /// public readonly List DocumentationFiles = new List(); + + /// + /// The result precompiled header file (PCH) created during compilation. Can be used in other compilations (as shared). + /// + public string PrecompiledHeaderFile; } } diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index e7dbaac19..877d4c389 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -430,6 +430,7 @@ namespace Flax.Build.Platforms var commonArgs = new List(); commonArgs.AddRange(options.CompileEnv.CustomArgs); SetupCompileCppFilesArgs(graph, options, commonArgs); + var useSeparatePdb = true; //compileEnvironment.PrecompiledHeaderUsage == PrecompiledHeaderFileUsage.None; { // Suppress Startup Banner commonArgs.Add("/nologo"); @@ -490,7 +491,10 @@ namespace Flax.Build.Platforms if (compileEnvironment.DebugInformation) { // Debug Information Format - commonArgs.Add("/Zi"); + if (useSeparatePdb) + commonArgs.Add("/Zi"); + else + commonArgs.Add("/Z7"); // Enhance Optimized Debugging commonArgs.Add("/Zo"); @@ -611,8 +615,65 @@ namespace Flax.Build.Platforms AddIncludePath(commonArgs, includePath); } - // Compile all C++ files var args = new List(); + + // Create precompiled header + string pchFile = null, pchSource = null; + if (compileEnvironment.PrecompiledHeaderUsage == PrecompiledHeaderFileUsage.UseManual) + { + pchFile = compileEnvironment.PrecompiledHeaderFile; + pchSource = compileEnvironment.PrecompiledHeaderSource; + } + else if (compileEnvironment.PrecompiledHeaderUsage == PrecompiledHeaderFileUsage.CreateManual) + { + // Use intermediate cpp file that includes the PCH path but also contains compiler info to properly recompile when it's modified + pchSource = compileEnvironment.PrecompiledHeaderSource; + var pchFilName = Path.GetFileName(pchSource); + var pchSourceFile = Path.Combine(options.IntermediateFolder, Path.ChangeExtension(pchFilName, "cpp")); + var contents = Bindings.BindingsGenerator.GetStringBuilder(); + contents.AppendLine("// This code was auto-generated. Do not modify it."); + contents.Append("// Compiler: ").AppendLine(_compilerPath); + contents.Append("#include \"").Append(pchSource).AppendLine("\""); + Utilities.WriteFileIfChanged(pchSourceFile, contents.ToString()); + Bindings.BindingsGenerator.PutStringBuilder(contents); + + // Compile intermediate cpp file into actual PCH (and obj+pdb files) + pchFile = Path.Combine(options.IntermediateFolder, Path.ChangeExtension(pchFilName, "pch")); + if (pchFile.EndsWith(".pch.pch")) + pchFile = pchFile.Substring(0, pchFile.Length - 4); + var pchPdbFile = Path.Combine(options.IntermediateFolder, Path.ChangeExtension(pchFilName, "pdb")); + var pchObjFile = Path.Combine(options.IntermediateFolder, Path.ChangeExtension(pchFilName, "obj")); + var task = graph.Add(); + task.PrerequisiteFiles.Add(pchSourceFile); + task.PrerequisiteFiles.Add(pchSource); + task.PrerequisiteFiles.AddRange(IncludesCache.FindAllIncludedFiles(pchSource)); + task.ProducedFiles.Add(pchFile); + task.ProducedFiles.Add(pchObjFile); + args.AddRange(commonArgs); + args.Add(string.Format("/Yc\"{0}\"", pchSource)); + args.Add(string.Format("/Fp\"{0}\"", pchFile)); + args.Add(string.Format("/Fd\"{0}\"", pchPdbFile)); + args.Add(string.Format("/Fo\"{0}\"", pchObjFile)); + args.Add("/FS"); + args.Add(string.Format("\"{0}\"", pchSourceFile)); + task.WorkingDirectory = options.WorkingDirectory; + task.CommandPath = _compilerPath; + task.CommandArguments = string.Join(" ", args); + task.Cost = int.MaxValue; // Run it before any other tasks + + // Setup outputs + output.PrecompiledHeaderFile = pchFile; + output.ObjectFiles.Add(pchObjFile); + } + if (pchFile != null) + { + // Include PCH file + commonArgs.Add(string.Format("/FI\"{0}\"", pchSource)); + commonArgs.Add(string.Format("/Yu\"{0}\"", pchSource)); + commonArgs.Add(string.Format("/Fp\"{0}\"", pchFile)); + } + + // Compile all C++ files foreach (var sourceFile in sourceFiles) { var sourceFilename = Path.GetFileNameWithoutExtension(sourceFile); @@ -625,9 +686,25 @@ namespace Flax.Build.Platforms if (compileEnvironment.DebugInformation) { // Program Database File Name - var pdbFile = Path.Combine(outputPath, sourceFilename + ".pdb"); - args.Add(string.Format("/Fd\"{0}\"", pdbFile)); - output.DebugDataFiles.Add(pdbFile); + string pdbFile = null; + if (pchFile != null) + { + // When using PCH we need to share the same PDB file that was used when building PCH + pdbFile = pchFile + ".pdb"; + + // Turn on sync for file access to prevent issues when compiling on multiple threads at once + if (useSeparatePdb) + args.Add("/FS"); + } + else if (useSeparatePdb) + { + pdbFile = Path.Combine(outputPath, sourceFilename + ".pdb"); + } + if (pdbFile != null) + { + args.Add(string.Format("/Fd\"{0}\"", pdbFile)); + output.DebugDataFiles.Add(pdbFile); + } } if (compileEnvironment.GenerateDocumentation) @@ -650,6 +727,10 @@ namespace Flax.Build.Platforms // Request included files to exist var includes = IncludesCache.FindAllIncludedFiles(sourceFile); task.PrerequisiteFiles.AddRange(includes); + if (pchFile != null) + { + task.PrerequisiteFiles.Add(pchFile); + } // Compile task.WorkingDirectory = options.WorkingDirectory; @@ -853,6 +934,13 @@ namespace Flax.Build.Platforms task.PrerequisiteFiles.AddRange(linkEnvironment.InputFiles); foreach (var file in linkEnvironment.InputFiles) { + if (file.EndsWith(".pch", StringComparison.OrdinalIgnoreCase)) + { + // PCH file + args.Add(string.Format("/Yu:\"{0}\"", file)); + continue; + } + args.Add(string.Format("\"{0}\"", file)); } From ee6a3114061d180ad9a31e8a7c95cc51c1c95a19 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 15 Nov 2023 22:56:23 +0100 Subject: [PATCH 004/198] Add support for building engine target as shared library on all platforms --- Source/Engine/Main/Windows/main.cpp | 4 +-- Source/Engine/Platform/Win32/Win32Thread.cpp | 6 +--- .../Platform/Windows/WindowsPlatform.cpp | 2 +- .../Engine/Platform/Windows/WindowsPlatform.h | 3 ++ Source/Engine/Scripting/Runtime/DotNet.cpp | 4 +++ Source/Engine/Scripting/Scripting.Build.cs | 2 +- Source/Tools/Flax.Build/Build/EngineTarget.cs | 36 ++++++++++++++++--- 7 files changed, 42 insertions(+), 15 deletions(-) diff --git a/Source/Engine/Main/Windows/main.cpp b/Source/Engine/Main/Windows/main.cpp index d9eb6db2a..3acb4ba42 100644 --- a/Source/Engine/Main/Windows/main.cpp +++ b/Source/Engine/Main/Windows/main.cpp @@ -25,8 +25,6 @@ extern "C" { __declspec(dllexport) int32 AmdPowerXpressRequestHighPerformance = 1; } -extern LONG CALLBACK SehExceptionHandler(EXCEPTION_POINTERS* ep); - #if FLAX_TESTS int main(int argc, char* argv[]) #else @@ -54,7 +52,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd { return Engine::Main(lpCmdLine); } - __except (SehExceptionHandler(GetExceptionInformation())) + __except (Platform::SehExceptionHandler(GetExceptionInformation())) { return -1; } diff --git a/Source/Engine/Platform/Win32/Win32Thread.cpp b/Source/Engine/Platform/Win32/Win32Thread.cpp index 0cd8faaf2..0b6f4635e 100644 --- a/Source/Engine/Platform/Win32/Win32Thread.cpp +++ b/Source/Engine/Platform/Win32/Win32Thread.cpp @@ -88,10 +88,6 @@ bool Win32Thread::Start(uint32 stackSize) return false; } -#if PLATFORM_WINDOWS -extern LONG CALLBACK SehExceptionHandler(EXCEPTION_POINTERS* ep); -#endif - unsigned long Win32Thread::ThreadProc(void* pThis) { auto thread = (Win32Thread*)pThis; @@ -103,7 +99,7 @@ unsigned long Win32Thread::ThreadProc(void* pThis) return static_cast(exitCode); } #if PLATFORM_WINDOWS - __except (SehExceptionHandler(GetExceptionInformation())) + __except (Platform::SehExceptionHandler(GetExceptionInformation())) { return -1; } diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index cc2e52725..9a15f9267 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -273,7 +273,7 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return DefWindowProc(hwnd, msg, wParam, lParam); } -LONG CALLBACK SehExceptionHandler(EXCEPTION_POINTERS* ep) +long __stdcall WindowsPlatform::SehExceptionHandler(EXCEPTION_POINTERS* ep) { if (ep->ExceptionRecord->ExceptionCode == CLR_EXCEPTION) { diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.h b/Source/Engine/Platform/Windows/WindowsPlatform.h index b4fd87a35..b7a8c5f07 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.h +++ b/Source/Engine/Platform/Windows/WindowsPlatform.h @@ -23,6 +23,9 @@ public: /// static void* Instance; + // Native exceptions handling function. + static long __stdcall SehExceptionHandler(struct _EXCEPTION_POINTERS* ep); + public: /// diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index c4371736d..35411f635 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -281,6 +281,10 @@ bool MCore::LoadEngine() flaxLibraryPath = ::String(StringUtils::GetDirectoryName(Platform::GetExecutableFilePath())) / StringUtils::GetFileName(flaxLibraryPath); } #endif + if (!FileSystem::FileExists(flaxLibraryPath)) + { + LOG(Error, "Flax Engine native library file is missing ({0})", flaxLibraryPath); + } RegisterNativeLibrary("FlaxEngine", flaxLibraryPath.Get()); MRootDomain = New("Root"); diff --git a/Source/Engine/Scripting/Scripting.Build.cs b/Source/Engine/Scripting/Scripting.Build.cs index 10fc2aff9..941eb3e73 100644 --- a/Source/Engine/Scripting/Scripting.Build.cs +++ b/Source/Engine/Scripting/Scripting.Build.cs @@ -43,7 +43,7 @@ public class Scripting : EngineModule if (options.Target is EngineTarget engineTarget && engineTarget.UseSeparateMainExecutable(options)) { // Build target doesn't support linking again main executable (eg. Linux) thus additional shared library is used for the engine (eg. libFlaxEditor.so) - var fileName = options.Platform.GetLinkOutputFileName(engineTarget.OutputName, LinkerOutput.SharedLibrary); + var fileName = options.Platform.GetLinkOutputFileName(EngineTarget.LibraryName, LinkerOutput.SharedLibrary); options.CompileEnv.PreprocessorDefinitions.Add("MCORE_MAIN_MODULE_NAME=" + fileName); } } diff --git a/Source/Tools/Flax.Build/Build/EngineTarget.cs b/Source/Tools/Flax.Build/Build/EngineTarget.cs index 30e0c92f8..b8dff9ce0 100644 --- a/Source/Tools/Flax.Build/Build/EngineTarget.cs +++ b/Source/Tools/Flax.Build/Build/EngineTarget.cs @@ -16,6 +16,11 @@ namespace Flax.Build { private static Version _engineVersion; + /// + /// Name of the native engine library. + /// + public const string LibraryName = "FlaxEngine"; + /// /// Gets the engine project. /// @@ -50,6 +55,11 @@ namespace Flax.Build defines.Add(string.Format("FLAX_{0}_{1}_OR_NEWER", engineVersion.Major, minor)); } + /// + /// True if target is built as monolithic executable with Main module inside, otherwise built as shared library with separate executable made of Main module only. + /// + public bool IsMonolithicExecutable = true; + /// public override void Init() { @@ -65,14 +75,23 @@ namespace Flax.Build /// public override string GetOutputFilePath(BuildOptions options, TargetOutputType? outputType) { + var useSeparateMainExe = UseSeparateMainExecutable(options); + // If building engine executable for platform doesn't support referencing it when linking game shared libraries - if (outputType == null && UseSeparateMainExecutable(options)) + if (outputType == null && useSeparateMainExe) { // Build into shared library outputType = TargetOutputType.Library; } - return base.GetOutputFilePath(options, outputType); + // Override output name to shared library name when building library for the separate main executable + var outputName = OutputName; + if (useSeparateMainExe && (outputType ?? OutputType) == TargetOutputType.Library) + OutputName = LibraryName; + + var result = base.GetOutputFilePath(options, outputType); + OutputName = outputName; + return result; } /// @@ -123,9 +142,13 @@ namespace Flax.Build /// /// Returns true if this build target should use separate (aka main-only) executable file and separate runtime (in shared library). Used on platforms that don't support linking again executable file but only shared library (see HasExecutableFileReferenceSupport). /// - public bool UseSeparateMainExecutable(BuildOptions buildOptions) + public virtual bool UseSeparateMainExecutable(BuildOptions buildOptions) { - return UseSymbolsExports && OutputType == TargetOutputType.Executable && !buildOptions.Platform.HasExecutableFileReferenceSupport && !Configuration.BuildBindingsOnly; + if (OutputType == TargetOutputType.Executable && !Configuration.BuildBindingsOnly) + { + return !IsMonolithicExecutable || (!buildOptions.Platform.HasExecutableFileReferenceSupport && UseSymbolsExports); + } + return false; } private void BuildMainExecutable(TaskGraph graph, BuildOptions buildOptions) @@ -175,7 +198,10 @@ namespace Flax.Build Builder.BuildModuleInner(buildData, mainModule, mainModuleOptions, false); // Link executable - exeBuildOptions.LinkEnv.InputLibraries.Add(Path.Combine(buildOptions.OutputFolder, buildOptions.Platform.GetLinkOutputFileName(OutputName, LinkerOutput.SharedLibrary))); + var engineLibraryType = LinkerOutput.SharedLibrary; + if (buildOptions.Toolchain?.Compiler == TargetCompiler.MSVC) + engineLibraryType = LinkerOutput.ImportLibrary; // MSVC links DLL against import library + exeBuildOptions.LinkEnv.InputLibraries.Add(Path.Combine(buildOptions.OutputFolder, buildOptions.Platform.GetLinkOutputFileName(LibraryName, engineLibraryType))); exeBuildOptions.LinkEnv.InputFiles.AddRange(mainModuleOptions.OutputFiles); exeBuildOptions.DependencyFiles.AddRange(mainModuleOptions.DependencyFiles); exeBuildOptions.OptionalDependencyFiles.AddRange(mainModuleOptions.OptionalDependencyFiles); From 6cad1e33093d53579586954eb6b108c3fa154e19 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 15 Nov 2023 23:22:26 +0100 Subject: [PATCH 005/198] Add better win32 resource file injection to be done during linker call --- Source/FlaxEditor.Build.cs | 2 - Source/{FlaxEditor.rc => FlaxEngine.rc} | 8 +- Source/FlaxGame.Build.cs | 3 +- Source/FlaxGame.rc | 102 ------------------ Source/Tools/Flax.Build/Build/EngineTarget.cs | 2 + .../Build/NativeCpp/LinkEnvironment.cs | 3 +- .../Platforms/Windows/WindowsToolchain.cs | 22 ++-- 7 files changed, 19 insertions(+), 123 deletions(-) rename Source/{FlaxEditor.rc => FlaxEngine.rc} (91%) delete mode 100644 Source/FlaxGame.rc diff --git a/Source/FlaxEditor.Build.cs b/Source/FlaxEditor.Build.cs index 85facb2a6..a27ae42b3 100644 --- a/Source/FlaxEditor.Build.cs +++ b/Source/FlaxEditor.Build.cs @@ -14,7 +14,6 @@ public class FlaxEditor : EngineTarget { base.Init(); - // Initialize IsEditor = true; OutputName = "FlaxEditor"; ConfigurationName = "Editor"; @@ -31,7 +30,6 @@ public class FlaxEditor : EngineTarget TargetArchitecture.ARM64, }; GlobalDefinitions.Add("USE_EDITOR"); - Win32ResourceFile = Path.Combine(Globals.EngineRoot, "Source", "FlaxEditor.rc"); Modules.Add("Editor"); Modules.Add("CSG"); diff --git a/Source/FlaxEditor.rc b/Source/FlaxEngine.rc similarity index 91% rename from Source/FlaxEditor.rc rename to Source/FlaxEngine.rc index fc44418ba..0097e76a7 100644 --- a/Source/FlaxEditor.rc +++ b/Source/FlaxEngine.rc @@ -70,12 +70,12 @@ BEGIN BLOCK "040004b0" BEGIN VALUE "CompanyName", FLAXENGINE_COMPANY - VALUE "FileDescription", "Flax Editor" + VALUE "FileDescription", PRODUCT_NAME VALUE "FileVersion", FLAXENGINE_VERSION_TEXT - VALUE "InternalName", "FlaxEditor" + VALUE "InternalName", PRODUCT_NAME_INTERNAL VALUE "LegalCopyright", FLAXENGINE_COPYRIGHT - VALUE "OriginalFilename", "FlaxEditor.exe" - VALUE "ProductName", "Flax Editor" + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME VALUE "ProductVersion", FLAXENGINE_VERSION_TEXT END END diff --git a/Source/FlaxGame.Build.cs b/Source/FlaxGame.Build.cs index 8f2bc2b3d..590773244 100644 --- a/Source/FlaxGame.Build.cs +++ b/Source/FlaxGame.Build.cs @@ -14,11 +14,10 @@ public class FlaxGame : EngineTarget { base.Init(); - // Initialize OutputName = "FlaxGame"; ConfigurationName = "Game"; IsPreBuilt = false; - Win32ResourceFile = Path.Combine(Globals.EngineRoot, "Source", "FlaxGame.rc"); + IsMonolithicExecutable = false; } /// diff --git a/Source/FlaxGame.rc b/Source/FlaxGame.rc deleted file mode 100644 index 6d1e4d480..000000000 --- a/Source/FlaxGame.rc +++ /dev/null @@ -1,102 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" -#include "FlaxEngine.Gen.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// Polish (Poland) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PLK) -LANGUAGE LANG_POLISH, SUBLANG_DEFAULT - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -// Icon -IDR_MAINFRAME ICON "Icon.ico" - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION FLAXENGINE_VERSION_MAJOR,FLAXENGINE_VERSION_MINOR,FLAXENGINE_VERSION_BUILD - PRODUCTVERSION FLAXENGINE_VERSION_MAJOR,FLAXENGINE_VERSION_MINOR,FLAXENGINE_VERSION_BUILD - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040004b0" - BEGIN - VALUE "CompanyName", FLAXENGINE_COMPANY - VALUE "FileDescription", "Flax Engine" - VALUE "FileVersion", FLAXENGINE_VERSION_TEXT - VALUE "InternalName", "FlaxEngine" - VALUE "LegalCopyright", FLAXENGINE_COPYRIGHT - VALUE "OriginalFilename", "FlaxGame.exe" - VALUE "ProductName", "Flax Engine" - VALUE "ProductVersion", FLAXENGINE_VERSION_TEXT - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x400, 1200 - END -END - -#endif -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/Source/Tools/Flax.Build/Build/EngineTarget.cs b/Source/Tools/Flax.Build/Build/EngineTarget.cs index b8dff9ce0..8c7c4a58a 100644 --- a/Source/Tools/Flax.Build/Build/EngineTarget.cs +++ b/Source/Tools/Flax.Build/Build/EngineTarget.cs @@ -70,6 +70,7 @@ namespace Flax.Build Modules.Add("Main"); Modules.Add("Engine"); + Win32ResourceFile = Path.Combine(Globals.EngineRoot, "Source", "FlaxEngine.rc"); } /// @@ -194,6 +195,7 @@ namespace Flax.Build mainModuleOptions.SourcePaths.Add(mainModule.FolderPath); mainModule.Setup(mainModuleOptions); mainModuleOptions.MergeSourcePathsIntoSourceFiles(); + mainModuleOptions.CompileEnv.PrecompiledHeaderUsage = PrecompiledHeaderFileUsage.None; mainModuleOptions.CompileEnv.PreprocessorDefinitions.Add("FLAXENGINE_API=" + buildOptions.Toolchain.DllImport); Builder.BuildModuleInner(buildData, mainModule, mainModuleOptions, false); diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/LinkEnvironment.cs b/Source/Tools/Flax.Build/Build/NativeCpp/LinkEnvironment.cs index 92f08d71f..2734034c9 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/LinkEnvironment.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/LinkEnvironment.cs @@ -127,8 +127,7 @@ namespace Flax.Build.NativeCpp LinkAsConsoleProgram = LinkAsConsoleProgram, GenerateDocumentation = GenerateDocumentation }; - foreach (var e in InputFiles) - clone.InputFiles.Add(e); + clone.InputFiles.AddRange(InputFiles); clone.DocumentationFiles.AddRange(DocumentationFiles); clone.InputLibraries.AddRange(InputLibraries); clone.LibraryPaths.AddRange(LibraryPaths); diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs index e21a1ce1a..2807dcb11 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs @@ -43,12 +43,12 @@ namespace Flax.Build.Platforms } /// - public override void PreBuild(TaskGraph graph, BuildOptions options) + public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath) { - base.PreBuild(graph, options); - // Compile and include resource file if need to - if (options.Target.Win32ResourceFile != null && !options.Target.IsPreBuilt && options.Target.OutputType == TargetOutputType.Executable) + if (options.Target.Win32ResourceFile != null && + !options.Target.IsPreBuilt && + (options.LinkEnv.Output == LinkerOutput.Executable || options.LinkEnv.Output == LinkerOutput.SharedLibrary)) { var task = graph.Add(); var args = new List(); @@ -62,24 +62,22 @@ namespace Flax.Build.Platforms // Add preprocessor definitions foreach (var definition in options.CompileEnv.PreprocessorDefinitions) - { args.Add(string.Format("/D \"{0}\"", definition)); - } + args.Add(string.Format("/D \"ORIGINAL_FILENAME=\\\"{0}\\\"\"", Path.GetFileName(outputFilePath))); + args.Add(string.Format("/D \"PRODUCT_NAME=\\\"{0}\\\"\"", options.Target.ProjectName + " " + options.Target.ConfigurationName)); + args.Add(string.Format("/D \"PRODUCT_NAME_INTERNAL=\\\"{0}\\\"\"", options.Target.Name)); // Add include paths foreach (var includePath in options.CompileEnv.IncludePaths) - { AddIncludePath(args, includePath); - } // Add the resource file to the produced item list - var outputFile = Path.Combine(options.IntermediateFolder, Path.GetFileNameWithoutExtension(sourceFile) + ".res"); + var outputFile = Path.Combine(options.IntermediateFolder, Path.GetFileName(outputFilePath) + ".res"); args.Add(string.Format("/Fo\"{0}\"", outputFile)); options.LinkEnv.InputFiles.Add(outputFile); // Request included files to exist - var includes = IncludesCache.FindAllIncludedFiles(sourceFile); - task.PrerequisiteFiles.AddRange(includes); + task.PrerequisiteFiles.AddRange(IncludesCache.FindAllIncludedFiles(sourceFile)); // Add the source file args.Add(string.Format("\"{0}\"", sourceFile)); @@ -91,6 +89,8 @@ namespace Flax.Build.Platforms task.PrerequisiteFiles.Add(sourceFile); task.Cost = 1; } + + base.LinkFiles(graph, options, outputFilePath); } } } From 6af6edb4feffd53ea0b6151a49851aa8358b9fd6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 15 Nov 2023 23:39:52 +0100 Subject: [PATCH 006/198] Add engine lib to codesign on windows and disable perf event spam in deployment build --- Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs | 3 +++ Source/Tools/Flax.Build/Deploy/FlaxBuild.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs b/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs index e1d159ed5..1fc33da51 100644 --- a/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs +++ b/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs @@ -45,14 +45,17 @@ namespace Flax.Deploy { var binaries = Path.Combine(dst, "Binaries", "Game", "x64", "Debug"); CodeSign(Path.Combine(binaries, "FlaxGame.exe")); + CodeSign(Path.Combine(binaries, "FlaxEngine.dll")); CodeSign(Path.Combine(binaries, "FlaxEngine.CSharp.dll")); binaries = Path.Combine(dst, "Binaries", "Game", "x64", "Development"); CodeSign(Path.Combine(binaries, "FlaxGame.exe")); + CodeSign(Path.Combine(binaries, "FlaxEngine.dll")); CodeSign(Path.Combine(binaries, "FlaxEngine.CSharp.dll")); binaries = Path.Combine(dst, "Binaries", "Game", "x64", "Release"); CodeSign(Path.Combine(binaries, "FlaxGame.exe")); + CodeSign(Path.Combine(binaries, "FlaxEngine.dll")); CodeSign(Path.Combine(binaries, "FlaxEngine.CSharp.dll")); } else if (platform == TargetPlatform.Mac) diff --git a/Source/Tools/Flax.Build/Deploy/FlaxBuild.cs b/Source/Tools/Flax.Build/Deploy/FlaxBuild.cs index b4e8b8680..f08c6fd0a 100644 --- a/Source/Tools/Flax.Build/Deploy/FlaxBuild.cs +++ b/Source/Tools/Flax.Build/Deploy/FlaxBuild.cs @@ -15,7 +15,7 @@ namespace Flax.Deploy { var buildPlatform = Platform.BuildPlatform.Target; var flaxBuildTool = Path.Combine(Globals.EngineRoot, buildPlatform == TargetPlatform.Windows ? "Binaries/Tools/Flax.Build.exe" : "Binaries/Tools/Flax.Build"); - var format = "-build -buildtargets={0} -log -logfile= -perf -platform={1} -arch={2} -configuration={3}"; + var format = "-build -buildtargets={0} -log -logfile= -platform={1} -arch={2} -configuration={3}"; var cmdLine = string.Format(format, target, platform, architecture, configuration); if (!string.IsNullOrEmpty(Configuration.Compiler)) cmdLine += " -compiler=" + Configuration.Compiler; From 640f3802375f994658a42338842dfa51ceadf675 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 16 Nov 2023 10:29:58 +0100 Subject: [PATCH 007/198] Add `OutputName` to Build Settings for game output exe/package renaming #1174 --- .../Platform/Android/AndroidPlatformTools.cpp | 2 +- .../Platform/Windows/WindowsPlatformTools.cpp | 22 ++++++++++++++++++- .../Platform/Windows/WindowsPlatformTools.h | 1 + Source/Editor/Utilities/EditorUtilities.cpp | 13 +++++++++++ Source/Editor/Utilities/EditorUtilities.h | 1 + Source/Engine/Core/Config/BuildSettings.h | 6 +++++ .../java/com/flaxengine/GameActivity.java | 2 +- Source/Tools/Flax.Build/Build/EngineTarget.cs | 20 ++++++++++++----- .../Flax.Build/Deploy/Deployment.Platforms.cs | 15 ++++++++----- .../Platforms/Apple/AppleToolchain.cs | 2 +- 10 files changed, 69 insertions(+), 15 deletions(-) diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp index 7d05409e5..52273ec45 100644 --- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp @@ -295,7 +295,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) // Copy result package const String apk = data.OriginalOutputPath / (distributionPackage ? TEXT("app/build/outputs/apk/release/app-release-unsigned.apk") : TEXT("app/build/outputs/apk/debug/app-debug.apk")); - const String outputApk = data.OriginalOutputPath / gameSettings->ProductName + TEXT(".apk"); + const String outputApk = data.OriginalOutputPath / EditorUtilities::GetOutputName() + TEXT(".apk"); if (FileSystem::CopyFile(outputApk, apk)) { LOG(Error, "Failed to copy package from {0} to {1}", apk, outputApk); diff --git a/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp b/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp index feca5c0a0..c9317c683 100644 --- a/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp @@ -42,11 +42,11 @@ bool WindowsPlatformTools::OnDeployBinaries(CookingData& data) { const auto platformSettings = WindowsPlatformSettings::Get(); - // Apply executable icon Array files; FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*.exe"), DirectorySearchOption::TopDirectoryOnly); if (files.HasItems()) { + // Apply executable icon TextureData iconData; if (!EditorUtilities::GetApplicationImage(platformSettings->OverrideIcon, iconData)) { @@ -56,11 +56,31 @@ bool WindowsPlatformTools::OnDeployBinaries(CookingData& data) return true; } } + + // Rename app + const String newName = EditorUtilities::GetOutputName(); + if (newName != StringUtils::GetFileNameWithoutExtension(files[0])) + { + if (FileSystem::MoveFile(data.NativeCodeOutputPath / newName + TEXT(".exe"), files[0], true)) + { + data.Error(TEXT("Failed to change output executable name.")); + return true; + } + } } return false; } +void WindowsPlatformTools::OnBuildStarted(CookingData& data) +{ + // Remove old executable + Array files; + FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*.exe"), DirectorySearchOption::TopDirectoryOnly); + for (auto& file : files) + FileSystem::DeleteFile(file); +} + void WindowsPlatformTools::OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir) { // Pick the first executable file diff --git a/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.h b/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.h index 5f03c9575..350ded7bd 100644 --- a/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.h +++ b/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.h @@ -31,6 +31,7 @@ public: ArchitectureType GetArchitecture() const override; bool UseSystemDotnet() const override; bool OnDeployBinaries(CookingData& data) override; + void OnBuildStarted(CookingData& data) override; void OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir) override; }; diff --git a/Source/Editor/Utilities/EditorUtilities.cpp b/Source/Editor/Utilities/EditorUtilities.cpp index a2418ceb1..c1c01be40 100644 --- a/Source/Editor/Utilities/EditorUtilities.cpp +++ b/Source/Editor/Utilities/EditorUtilities.cpp @@ -11,6 +11,7 @@ #include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Core/Math/Color32.h" #include "Engine/Core/Config/GameSettings.h" +#include "Engine/Core/Config/BuildSettings.h" #include "Engine/Content/Content.h" #include "Engine/Content/AssetReference.h" #include "Engine/Content/Assets/Texture.h" @@ -511,6 +512,18 @@ bool EditorUtilities::UpdateExeIcon(const String& path, const TextureData& icon) return false; } +String EditorUtilities::GetOutputName() +{ + const auto gameSettings = GameSettings::Get(); + const auto buildSettings = BuildSettings::Get(); + String outputName = buildSettings->OutputName; + outputName.Replace(TEXT("${PROJECT_NAME}"), *gameSettings->ProductName, StringSearchCase::IgnoreCase); + outputName.Replace(TEXT("${COMPANY_NAME}"), *gameSettings->CompanyName, StringSearchCase::IgnoreCase); + if (outputName.IsEmpty()) + outputName = TEXT("FlaxGame"); + return outputName; +} + bool EditorUtilities::FormatAppPackageName(String& packageName) { const auto gameSettings = GameSettings::Get(); diff --git a/Source/Editor/Utilities/EditorUtilities.h b/Source/Editor/Utilities/EditorUtilities.h index 881bfe3b8..40518ed88 100644 --- a/Source/Editor/Utilities/EditorUtilities.h +++ b/Source/Editor/Utilities/EditorUtilities.h @@ -30,6 +30,7 @@ public: /// True if fails, otherwise false. static bool UpdateExeIcon(const String& path, const TextureData& icon); + static String GetOutputName(); static bool FormatAppPackageName(String& packageName); static bool GetApplicationImage(const Guid& imageId, TextureData& imageData, ApplicationImageType type = ApplicationImageType::Icon); static bool GetTexture(const Guid& textureId, TextureData& textureData); diff --git a/Source/Engine/Core/Config/BuildSettings.h b/Source/Engine/Core/Config/BuildSettings.h index 3232c6b02..075e4655e 100644 --- a/Source/Engine/Core/Config/BuildSettings.h +++ b/Source/Engine/Core/Config/BuildSettings.h @@ -17,6 +17,12 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API DECLARE_SCRIPTING_TYPE_MINIMAL(BuildSettings); public: + /// + /// Name of the output app created by the build system. Used to rename main executable (eg. MyGame.exe) or final package name (eg. MyGame.apk). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}. + /// + API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"General\")") + String OutputName = TEXT("${PROJECT_NAME}"); + /// /// The maximum amount of assets to include into a single assets package. Asset packages will split into several packages if need to. /// diff --git a/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java b/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java index 2a7fc8631..bee34b99d 100644 --- a/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java +++ b/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java @@ -44,7 +44,7 @@ public class GameActivity extends NativeActivity { static { try { // Load native libraries - System.loadLibrary("FlaxGame"); + System.loadLibrary("FlaxEngine"); } catch (UnsatisfiedLinkError error) { Log.e("Flax", error.getMessage()); } diff --git a/Source/Tools/Flax.Build/Build/EngineTarget.cs b/Source/Tools/Flax.Build/Build/EngineTarget.cs index 8c7c4a58a..435453a64 100644 --- a/Source/Tools/Flax.Build/Build/EngineTarget.cs +++ b/Source/Tools/Flax.Build/Build/EngineTarget.cs @@ -76,10 +76,10 @@ namespace Flax.Build /// public override string GetOutputFilePath(BuildOptions options, TargetOutputType? outputType) { - var useSeparateMainExe = UseSeparateMainExecutable(options); + var asLib = UseSeparateMainExecutable(options) || BuildAsLibrary(options); // If building engine executable for platform doesn't support referencing it when linking game shared libraries - if (outputType == null && useSeparateMainExe) + if (outputType == null && asLib) { // Build into shared library outputType = TargetOutputType.Library; @@ -87,7 +87,7 @@ namespace Flax.Build // Override output name to shared library name when building library for the separate main executable var outputName = OutputName; - if (useSeparateMainExe && (outputType ?? OutputType) == TargetOutputType.Library) + if (asLib && (outputType ?? OutputType) == TargetOutputType.Library) OutputName = LibraryName; var result = base.GetOutputFilePath(options, outputType); @@ -101,7 +101,7 @@ namespace Flax.Build base.SetupTargetEnvironment(options); // If building engine executable for platform doesn't support referencing it when linking game shared libraries - if (UseSeparateMainExecutable(options)) + if (UseSeparateMainExecutable(options) || BuildAsLibrary(options)) { // Build into shared library options.LinkEnv.Output = LinkerOutput.SharedLibrary; @@ -127,7 +127,7 @@ namespace Flax.Build base.PostBuild(graph, buildOptions); // If building engine executable for platform doesn't support referencing it when linking game shared libraries - if (UseSeparateMainExecutable(buildOptions)) + if (UseSeparateMainExecutable(buildOptions) && !BuildAsLibrary(buildOptions)) { // Build additional executable with Main module only that uses shared library using (new ProfileEventScope("BuildExecutable")) @@ -152,6 +152,16 @@ namespace Flax.Build return false; } + private bool BuildAsLibrary(BuildOptions buildOptions) + { + switch (buildOptions.Platform.Target) + { + case TargetPlatform.UWP: + case TargetPlatform.Android: return true; + default: return false; + } + } + private void BuildMainExecutable(TaskGraph graph, BuildOptions buildOptions) { if (IsPreBuilt) diff --git a/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs b/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs index 1fc33da51..be8f04ed5 100644 --- a/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs +++ b/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs @@ -35,9 +35,12 @@ namespace Flax.Deploy // For Linux don't deploy engine libs used by C++ scripting linking (engine source required) if (platform == TargetPlatform.Linux) { - File.Delete(Path.Combine(dst, "Binaries", "Game", "x64", "Debug", "FlaxGame.a")); - File.Delete(Path.Combine(dst, "Binaries", "Game", "x64", "Development", "FlaxGame.a")); - File.Delete(Path.Combine(dst, "Binaries", "Game", "x64", "Release", "FlaxGame.a")); + Utilities.FileDelete(Path.Combine(dst, "Binaries", "Game", "x64", "Debug", "FlaxGame.a")); + Utilities.FileDelete(Path.Combine(dst, "Binaries", "Game", "x64", "Development", "FlaxGame.a")); + Utilities.FileDelete(Path.Combine(dst, "Binaries", "Game", "x64", "Release", "FlaxGame.a")); + Utilities.FileDelete(Path.Combine(dst, "Binaries", "Game", "x64", "Debug", "FlaxEngine.a")); + Utilities.FileDelete(Path.Combine(dst, "Binaries", "Game", "x64", "Development", "FlaxEngine.a")); + Utilities.FileDelete(Path.Combine(dst, "Binaries", "Game", "x64", "Release", "FlaxEngine.a")); } // Sign binaries @@ -62,15 +65,15 @@ namespace Flax.Deploy { var binaries = Path.Combine(dst, "Binaries", "Game", "arm64", "Debug"); CodeSign(Path.Combine(binaries, "FlaxGame")); - CodeSign(Path.Combine(binaries, "FlaxGame.dylib")); + CodeSign(Path.Combine(binaries, "FlaxEngine.dylib")); binaries = Path.Combine(dst, "Binaries", "Game", "arm64", "Development"); CodeSign(Path.Combine(binaries, "FlaxGame")); - CodeSign(Path.Combine(binaries, "FlaxGame.dylib")); + CodeSign(Path.Combine(binaries, "FlaxEngine.dylib")); binaries = Path.Combine(dst, "Binaries", "Game", "arm64", "Release"); CodeSign(Path.Combine(binaries, "FlaxGame")); - CodeSign(Path.Combine(binaries, "FlaxGame.dylib")); + CodeSign(Path.Combine(binaries, "FlaxEngine.dylib")); } // Don't distribute engine deps diff --git a/Source/Tools/Flax.Build/Platforms/Apple/AppleToolchain.cs b/Source/Tools/Flax.Build/Platforms/Apple/AppleToolchain.cs index 6a0f42cfd..ea3ab71e6 100644 --- a/Source/Tools/Flax.Build/Platforms/Apple/AppleToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Apple/AppleToolchain.cs @@ -396,7 +396,7 @@ namespace Flax.Build.Platforms rpathTask.DependentTasks.Add(lastTask); lastTask = rpathTask; } - // TODO: fix dylib ID: 'install_name_tool -id @rpath/FlaxGame.dylib FlaxGame.dylib' + // TODO: fix dylib ID: 'install_name_tool -id @rpath/FlaxEngine.dylib FlaxEngine.dylib' if (!options.LinkEnv.DebugInformation) { // Strip debug symbols From 1bc68428313398c1e46c7ccf07843f7ad66ee755 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 16 Nov 2023 10:32:31 +0100 Subject: [PATCH 008/198] Update build number --- Flax.flaxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index ed47674fd..16ec7fb24 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 8, "Revision": 0, - "Build": 6500 + "Build": 6501 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.", From 6c6e8be20e0b9a75001232d5db3a0c068e9c22b7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 20 Nov 2023 11:16:29 +0100 Subject: [PATCH 009/198] Update build number --- Flax.flaxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 16ec7fb24..a7ce55520 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 8, "Revision": 0, - "Build": 6501 + "Build": 6502 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.", From f5acba7d43c0c24604cdf361c97e14ecb8c3a1ef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 20 Nov 2023 14:20:26 +0100 Subject: [PATCH 010/198] Add remaining clang options for Apple and Unix toolchains --- .../Platforms/Apple/AppleToolchain.cs | 16 +++++++----- .../Platforms/Unix/UnixToolchain.cs | 26 +++++++------------ 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/Source/Tools/Flax.Build/Platforms/Apple/AppleToolchain.cs b/Source/Tools/Flax.Build/Platforms/Apple/AppleToolchain.cs index ea3ab71e6..fa6e896f3 100644 --- a/Source/Tools/Flax.Build/Platforms/Apple/AppleToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Apple/AppleToolchain.cs @@ -150,23 +150,25 @@ namespace Flax.Build.Platforms if (compileEnvironment.TreatWarningsAsErrors) commonArgs.Add("-Wall -Werror"); - // TODO: compileEnvironment.IntrinsicFunctions - // TODO: compileEnvironment.FunctionLevelLinking - // TODO: compileEnvironment.FavorSizeOrSpeed - // TODO: compileEnvironment.RuntimeChecks - // TODO: compileEnvironment.StringPooling - // TODO: compileEnvironment.BufferSecurityCheck - if (compileEnvironment.DebugInformation) commonArgs.Add("-gdwarf-2"); commonArgs.Add("-pthread"); + if (compileEnvironment.FavorSizeOrSpeed == FavorSizeOrSpeed.FastCode) + commonArgs.Add("-Ofast"); + else if (compileEnvironment.FavorSizeOrSpeed == FavorSizeOrSpeed.SmallCode) + commonArgs.Add("-Os"); if (compileEnvironment.Optimization) commonArgs.Add("-O3"); else commonArgs.Add("-O0"); + if (compileEnvironment.BufferSecurityCheck) + commonArgs.Add("-fstack-protector"); + else + commonArgs.Add("fno-stack-protector"); + if (!compileEnvironment.Inlining) { commonArgs.Add("-fno-inline-functions"); diff --git a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs index fc409858a..607450cb1 100644 --- a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs @@ -363,28 +363,24 @@ namespace Flax.Build.Platforms if (compileEnvironment.TreatWarningsAsErrors) commonArgs.Add("-Wall -Werror"); - // TODO: compileEnvironment.IntrinsicFunctions - // TODO: compileEnvironment.FunctionLevelLinking - // TODO: compileEnvironment.FavorSizeOrSpeed - // TODO: compileEnvironment.RuntimeChecks - // TODO: compileEnvironment.StringPooling - // TODO: compileEnvironment.BufferSecurityCheck - if (compileEnvironment.DebugInformation) - { commonArgs.Add("-glldb"); - } commonArgs.Add("-pthread"); + if (compileEnvironment.FavorSizeOrSpeed == FavorSizeOrSpeed.FastCode) + commonArgs.Add("-Ofast"); + else if (compileEnvironment.FavorSizeOrSpeed == FavorSizeOrSpeed.SmallCode) + commonArgs.Add("-Os"); if (compileEnvironment.Optimization) - { commonArgs.Add("-O2"); - } else - { commonArgs.Add("-O0"); - } + + if (compileEnvironment.BufferSecurityCheck) + commonArgs.Add("-fstack-protector"); + else + commonArgs.Add("fno-stack-protector"); if (!compileEnvironment.Inlining) { @@ -393,13 +389,9 @@ namespace Flax.Build.Platforms } if (compileEnvironment.EnableExceptions) - { commonArgs.Add("-fexceptions"); - } else - { commonArgs.Add("-fno-exceptions"); - } } // Add preprocessor definitions From 37d4b0f8632ca35562c05ff2651fbeb29f6e85cd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 20 Nov 2023 17:27:47 +0100 Subject: [PATCH 011/198] Remove invalid clang option --- Source/Tools/Flax.Build/Platforms/Apple/AppleToolchain.cs | 2 -- Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/Source/Tools/Flax.Build/Platforms/Apple/AppleToolchain.cs b/Source/Tools/Flax.Build/Platforms/Apple/AppleToolchain.cs index fa6e896f3..b38cb9f4f 100644 --- a/Source/Tools/Flax.Build/Platforms/Apple/AppleToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Apple/AppleToolchain.cs @@ -166,8 +166,6 @@ namespace Flax.Build.Platforms if (compileEnvironment.BufferSecurityCheck) commonArgs.Add("-fstack-protector"); - else - commonArgs.Add("fno-stack-protector"); if (!compileEnvironment.Inlining) { diff --git a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs index 607450cb1..e3dc222ed 100644 --- a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs @@ -379,8 +379,6 @@ namespace Flax.Build.Platforms if (compileEnvironment.BufferSecurityCheck) commonArgs.Add("-fstack-protector"); - else - commonArgs.Add("fno-stack-protector"); if (!compileEnvironment.Inlining) { From 06ad316f94ac636a0791887c4a50626eac98f9a4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 26 Nov 2023 15:09:46 +0100 Subject: [PATCH 012/198] Update to .NET 8 --- .github/workflows/build_android.yml | 4 +-- .github/workflows/build_ios.yml | 4 +-- .github/workflows/build_linux.yml | 8 +++--- .github/workflows/build_mac.yml | 8 +++--- .github/workflows/build_windows.yml | 8 +++--- .github/workflows/cd.yml | 12 ++++---- .github/workflows/tests.yml | 22 +++++++-------- PackageAll.bat | 2 +- PackageEditor.bat | 2 +- PackageEditor.command | 2 +- PackageEditor.sh | 2 +- PackagePlatforms.bat | 2 +- PackagePlatforms.command | 2 +- PackagePlatforms.sh | 2 +- README.md | 12 ++++---- Source/Editor/Cooker/CookingData.h | 2 +- Source/Editor/Cooker/Steps/DeployDataStep.cpp | 16 +++++------ Source/Engine/Scripting/Runtime/DotNet.cpp | 6 ++-- .../FlaxEngine.CSharp.runtimeconfig.json | 4 +-- .../Flax.Build.Tests/Flax.Build.Tests.csproj | 2 +- .../Flax.Build/Build/DotNet/DotNetSdk.cs | 2 +- .../Build/NativeCpp/Builder.NativeCpp.cs | 28 +++++++++++++++++-- .../Flax.Build/Deps/Dependencies/nethost.cs | 4 +-- Source/Tools/Flax.Build/Flax.Build.csproj | 2 +- .../VisualStudio/CSSDKProjectGenerator.cs | 2 +- Source/Tools/Flax.Build/global.json | 2 +- global.json | 2 +- 27 files changed, 94 insertions(+), 70 deletions(-) diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index b0d4633a8..772e3f67c 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Setup .NET Workload run: | dotnet workload install android @@ -33,4 +33,4 @@ jobs: git lfs pull - name: Build run: | - .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=7 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame + .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_ios.yml b/.github/workflows/build_ios.yml index 2aec46320..5a0d285fe 100644 --- a/.github/workflows/build_ios.yml +++ b/.github/workflows/build_ios.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Setup .NET Workload run: | dotnet workload install ios @@ -33,4 +33,4 @@ jobs: git lfs pull - name: Build run: | - ./Development/Scripts/Mac/CallBuildTool.sh -build -log -dotnet=7 -arch=ARM64 -platform=iOS -configuration=Release -buildtargets=FlaxGame + ./Development/Scripts/Mac/CallBuildTool.sh -build -log -dotnet=8 -arch=ARM64 -platform=iOS -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 56accba84..b3348c288 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -25,7 +25,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -36,7 +36,7 @@ jobs: git lfs pull - name: Build run: | - ./Development/Scripts/Linux/CallBuildTool.sh -build -log -printSDKs -dotnet=7 -arch=x64 -platform=Linux -configuration=Development -buildtargets=FlaxEditor + ./Development/Scripts/Linux/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Linux -configuration=Development -buildtargets=FlaxEditor # Game game-linux: @@ -53,7 +53,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -64,4 +64,4 @@ jobs: git lfs pull - name: Build run: | - ./Development/Scripts/Linux/CallBuildTool.sh -build -log -printSDKs -dotnet=7 -arch=x64 -platform=Linux -configuration=Release -buildtargets=FlaxGame + ./Development/Scripts/Linux/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Linux -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_mac.yml b/.github/workflows/build_mac.yml index 54bdb77b5..139bf2416 100644 --- a/.github/workflows/build_mac.yml +++ b/.github/workflows/build_mac.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -30,7 +30,7 @@ jobs: git lfs pull - name: Build run: | - ./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=7 -arch=x64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor + ./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor # Game game-mac: @@ -44,7 +44,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -55,4 +55,4 @@ jobs: git lfs pull - name: Build run: | - ./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=7 -arch=x64 -platform=Mac -configuration=Release -buildtargets=FlaxGame + ./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Mac -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index b6131fb53..85f4e0c79 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -30,7 +30,7 @@ jobs: git lfs pull - name: Build run: | - .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=7 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor + .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor # Game game-windows: @@ -44,7 +44,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -55,4 +55,4 @@ jobs: git lfs pull - name: Build run: | - .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=7 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame + .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index a5d8bc043..70f275eac 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -26,7 +26,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -59,7 +59,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -95,7 +95,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -129,7 +129,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -159,7 +159,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -187,7 +187,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a524bdca2..e36642cdf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,7 +17,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -34,21 +34,21 @@ jobs: sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev - name: Build run: | - ./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=7 - ./Development/Scripts/Linux/CallBuildTool.sh -build -log -dotnet=7 -arch=x64 -platform=Linux -configuration=Development -buildtargets=FlaxTestsTarget + ./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8 + ./Development/Scripts/Linux/CallBuildTool.sh -build -log -dotnet=8 -arch=x64 -platform=Linux -configuration=Development -buildtargets=FlaxTestsTarget dotnet msbuild Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo dotnet msbuild Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo - name: Test run: | ${GITHUB_WORKSPACE}/Binaries/Editor/Linux/Development/FlaxTests - dotnet test -f net7.0 Binaries/Tests/Flax.Build.Tests.dll + dotnet test -f net8.0 Binaries/Tests/Flax.Build.Tests.dll cp Binaries/Editor/Linux/Development/FlaxEngine.CSharp.dll Binaries/Tests cp Binaries/Editor/Linux/Development/FlaxEngine.CSharp.runtimeconfig.json Binaries/Tests cp Binaries/Editor/Linux/Development/Newtonsoft.Json.dll Binaries/Tests - dotnet test -f net7.0 Binaries/Tests/FlaxEngine.CSharp.dll + dotnet test -f net8.0 Binaries/Tests/FlaxEngine.CSharp.dll - name: Test UseLargeWorlds run: | - ./Development/Scripts/Linux/CallBuildTool.sh -build -log -dotnet=7 -arch=x64 -platform=Linux -configuration=Development -buildtargets=FlaxTestsTarget -UseLargeWorlds=true + ./Development/Scripts/Linux/CallBuildTool.sh -build -log -dotnet=8 -arch=x64 -platform=Linux -configuration=Development -buildtargets=FlaxTestsTarget -UseLargeWorlds=true ${GITHUB_WORKSPACE}/Binaries/Editor/Linux/Development/FlaxTests # Tests on Windows @@ -61,7 +61,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Print .NET info run: | dotnet --info @@ -72,14 +72,14 @@ jobs: git lfs pull - name: Build run: | - .\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=7 - .\Development\Scripts\Windows\CallBuildTool.bat -build -log -dotnet=7 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxTestsTarget + .\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=8 + .\Development\Scripts\Windows\CallBuildTool.bat -build -log -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxTestsTarget dotnet msbuild Source\Tools\Flax.Build.Tests\Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo - name: Test run: | .\Binaries\Editor\Win64\Development\FlaxTests.exe - dotnet test -f net7.0 Binaries\Tests\Flax.Build.Tests.dll + dotnet test -f net8.0 Binaries\Tests\Flax.Build.Tests.dll xcopy /y Binaries\Editor\Win64\Development\FlaxEngine.CSharp.dll Binaries\Tests xcopy /y Binaries\Editor\Win64\Development\FlaxEngine.CSharp.runtimeconfig.json Binaries\Tests xcopy /y Binaries\Editor\Win64\Development\Newtonsoft.Json.dll Binaries\Tests - dotnet test -f net7.0 Binaries\Tests\FlaxEngine.CSharp.dll + dotnet test -f net8.0 Binaries\Tests\FlaxEngine.CSharp.dll diff --git a/PackageAll.bat b/PackageAll.bat index 0325f2244..bfe9bce27 100644 --- a/PackageAll.bat +++ b/PackageAll.bat @@ -7,7 +7,7 @@ pushd echo Performing the full package... rem Run the build tool. -call "Development\Scripts\Windows\CallBuildTool.bat" -deploy -deployEditor -deployPlatforms -dotnet=7 -verbose -log -logFile="Cache\Intermediate\PackageLog.txt" %* +call "Development\Scripts\Windows\CallBuildTool.bat" -deploy -deployEditor -deployPlatforms -dotnet=8 -verbose -log -logFile="Cache\Intermediate\PackageLog.txt" %* if errorlevel 1 goto BuildToolFailed popd diff --git a/PackageEditor.bat b/PackageEditor.bat index 515b81871..be01a59b9 100644 --- a/PackageEditor.bat +++ b/PackageEditor.bat @@ -7,7 +7,7 @@ pushd echo Building and packaging Flax Editor... rem Run the build tool. -call "Development\Scripts\Windows\CallBuildTool.bat" -deploy -deployEditor -dotnet=7 -verbose -log -logFile="Cache\Intermediate\PackageLog.txt" %* +call "Development\Scripts\Windows\CallBuildTool.bat" -deploy -deployEditor -dotnet=8 -verbose -log -logFile="Cache\Intermediate\PackageLog.txt" %* if errorlevel 1 goto BuildToolFailed popd diff --git a/PackageEditor.command b/PackageEditor.command index eb62ae1a4..4e0ff71a1 100755 --- a/PackageEditor.command +++ b/PackageEditor.command @@ -9,4 +9,4 @@ echo Building and packaging Flax Editor... cd "`dirname "$0"`" # Run Flax.Build (also pass the arguments) -bash ./Development/Scripts/Mac/CallBuildTool.sh --deploy --deployEditor --dotnet=7 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@" +bash ./Development/Scripts/Mac/CallBuildTool.sh --deploy --deployEditor --dotnet=8 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@" diff --git a/PackageEditor.sh b/PackageEditor.sh index 151147b6a..819e101c4 100755 --- a/PackageEditor.sh +++ b/PackageEditor.sh @@ -9,4 +9,4 @@ echo Building and packaging Flax Editor... cd "`dirname "$0"`" # Run Flax.Build (also pass the arguments) -bash ./Development/Scripts/Linux/CallBuildTool.sh --deploy --deployEditor --dotnet=7 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@" +bash ./Development/Scripts/Linux/CallBuildTool.sh --deploy --deployEditor --dotnet=8 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@" diff --git a/PackagePlatforms.bat b/PackagePlatforms.bat index eb9c42d34..81cbad20b 100644 --- a/PackagePlatforms.bat +++ b/PackagePlatforms.bat @@ -7,7 +7,7 @@ pushd echo Building and packaging platforms data... rem Run the build tool. -call "Development\Scripts\Windows\CallBuildTool.bat" -deploy -deployPlatforms -dotnet=7 -verbose -log -logFile="Cache\Intermediate\PackageLog.txt" %* +call "Development\Scripts\Windows\CallBuildTool.bat" -deploy -deployPlatforms -dotnet=8 -verbose -log -logFile="Cache\Intermediate\PackageLog.txt" %* if errorlevel 1 goto BuildToolFailed popd diff --git a/PackagePlatforms.command b/PackagePlatforms.command index e9182a627..8950981e9 100755 --- a/PackagePlatforms.command +++ b/PackagePlatforms.command @@ -9,4 +9,4 @@ echo Building and packaging platforms data... cd "`dirname "$0"`" # Run Flax.Build (also pass the arguments) -bash ./Development/Scripts/Mac/CallBuildTool.sh --deploy --deployPlatforms --dotnet=7 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@" +bash ./Development/Scripts/Mac/CallBuildTool.sh --deploy --deployPlatforms --dotnet=8 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@" diff --git a/PackagePlatforms.sh b/PackagePlatforms.sh index f7e43cc42..c8e5609f5 100755 --- a/PackagePlatforms.sh +++ b/PackagePlatforms.sh @@ -9,4 +9,4 @@ echo Building and packaging platforms data... cd "`dirname "$0"`" # Run Flax.Build (also pass the arguments) -bash ./Development/Scripts/Linux/CallBuildTool.sh --deploy --deployPlatforms --dotnet=7 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@" +bash ./Development/Scripts/Linux/CallBuildTool.sh --deploy --deployPlatforms --dotnet=8 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@" diff --git a/README.md b/README.md index d6688bd03..1b0d06bf6 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Follow the instructions below to compile and run the engine from source. * Install Visual Studio 2022 or newer * Install Windows 8.1 SDK or newer (via Visual Studio Installer) * Install Microsoft Visual C++ 2015 v140 toolset or newer (via Visual Studio Installer) -* Install .NET 7 SDK for **Windows x64** (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)) +* Install .NET 8 SDK for **Windows x64** (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) * Install Git with LFS * Clone repo (with LFS) * Run **GenerateProjectFiles.bat** @@ -44,8 +44,8 @@ Follow the instructions below to compile and run the engine from source. ## Linux * Install Visual Studio Code -* Install .NET 7 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)) - * Ubuntu: `sudo apt install dotnet-sdk-7.0` +* Install .NET 8 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) + * Ubuntu: `sudo apt install dotnet-sdk-8.0` * Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) * Ubuntu: `sudo apt install vulkan-sdk` * Arch: `sudo pacman -S spirv-tools vulkan-headers vulkan-tools vulkan-validation-layers` @@ -67,7 +67,7 @@ Follow the instructions below to compile and run the engine from source. ## Mac * Install XCode -* Install .NET 7 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)) +* Install .NET 8 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) * Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) * Clone repo (with LFS) * Run `GenerateProjectFiles.command` @@ -80,9 +80,9 @@ Follow the instructions below to compile and run the engine from source. Restart PC - ensure DotNet is added to PATH for command line tools execution. -* `Microsoft.NET.TargetFrameworkInference.targets(141,5): error NETSDK1045: The current .NET SDK does not support targeting .NET 7.0. Either target .NET 5.0 or lower, or use a version of the .NET SDK that supports .NET 7.0` +* `Microsoft.NET.TargetFrameworkInference.targets(141,5): error NETSDK1045: The current .NET SDK does not support targeting .NET 8.0. Either target .NET 5.0 or lower, or use a version of the .NET SDK that supports .NET 8.0` -Use Visual Studio 2022, older versions are not supported by .NET SDK 7. +Use Visual Studio 2022, older versions are not supported by .NET SDK 8. * `Building for Windows without Vulkan rendering backend (Vulkan SDK is missing)` diff --git a/Source/Editor/Cooker/CookingData.h b/Source/Editor/Cooker/CookingData.h index 1924b0c56..e4c071e8b 100644 --- a/Source/Editor/Cooker/CookingData.h +++ b/Source/Editor/Cooker/CookingData.h @@ -14,7 +14,7 @@ class PlatformTools; #if OFFICIAL_BUILD // Use the fixed .NET SDK version in packaged builds for compatibility (FlaxGame is precompiled with it) -#define GAME_BUILD_DOTNET_VER TEXT("-dotnet=7") +#define GAME_BUILD_DOTNET_VER TEXT("-dotnet=8") #else #define GAME_BUILD_DOTNET_VER TEXT("") #endif diff --git a/Source/Editor/Cooker/Steps/DeployDataStep.cpp b/Source/Editor/Cooker/Steps/DeployDataStep.cpp index c1179c77a..845651bed 100644 --- a/Source/Editor/Cooker/Steps/DeployDataStep.cpp +++ b/Source/Editor/Cooker/Steps/DeployDataStep.cpp @@ -116,7 +116,7 @@ bool DeployDataStep::Perform(CookingData& data) for (String& version : versions) { version = String(StringUtils::GetFileName(version)); - if (!version.StartsWith(TEXT("7."))) + if (!version.StartsWith(TEXT("8."))) // Check for major part of 8.0 version.Clear(); } Sorting::QuickSort(versions.Get(), versions.Count()); @@ -198,14 +198,14 @@ bool DeployDataStep::Perform(CookingData& data) { // AOT runtime files inside Engine Platform folder packFolder /= TEXT("Dotnet"); - dstDotnetLibs /= TEXT("lib/net7.0"); - srcDotnetLibs = packFolder / TEXT("lib/net7.0"); + dstDotnetLibs /= TEXT("lib/net8.0"); + srcDotnetLibs = packFolder / TEXT("lib/net8.0"); } else { // Runtime files inside Dotnet SDK folder but placed for AOT - dstDotnetLibs /= TEXT("lib/net7.0"); - srcDotnetLibs /= TEXT("../lib/net7.0"); + dstDotnetLibs /= TEXT("lib/net8.0"); + srcDotnetLibs /= TEXT("../lib/net8.0"); } } else @@ -213,14 +213,14 @@ bool DeployDataStep::Perform(CookingData& data) if (srcDotnetFromEngine) { // Runtime files inside Engine Platform folder - dstDotnetLibs /= TEXT("lib/net7.0"); - srcDotnetLibs /= TEXT("lib/net7.0"); + dstDotnetLibs /= TEXT("lib/net8.0"); + srcDotnetLibs /= TEXT("lib/net8.0"); } else { // Runtime files inside Dotnet SDK folder dstDotnetLibs /= TEXT("shared/Microsoft.NETCore.App"); - srcDotnetLibs /= TEXT("../lib/net7.0"); + srcDotnetLibs /= TEXT("../lib/net8.0"); } } FileSystem::CopyFile(dstDotnet / TEXT("LICENSE.TXT"), packFolder / TEXT("LICENSE.txt")); diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 8d564ef6a..3774c0452 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -1704,12 +1704,12 @@ bool InitHostfxr() // Warn user about missing .Net #if PLATFORM_DESKTOP - Platform::OpenUrl(TEXT("https://dotnet.microsoft.com/en-us/download/dotnet/7.0")); + Platform::OpenUrl(TEXT("https://dotnet.microsoft.com/en-us/download/dotnet/8.0")); #endif #if USE_EDITOR - LOG(Fatal, "Missing .NET 7 or later SDK installation required to run Flax Editor."); + LOG(Fatal, "Missing .NET 8 or later SDK installation required to run Flax Editor."); #else - LOG(Fatal, "Missing .NET 7 or later Runtime installation required to run this application."); + LOG(Fatal, "Missing .NET 8 or later Runtime installation required to run this application."); #endif return true; } diff --git a/Source/ThirdParty/nethost/FlaxEngine.CSharp.runtimeconfig.json b/Source/ThirdParty/nethost/FlaxEngine.CSharp.runtimeconfig.json index 134a0ef98..f34374fb7 100644 --- a/Source/ThirdParty/nethost/FlaxEngine.CSharp.runtimeconfig.json +++ b/Source/ThirdParty/nethost/FlaxEngine.CSharp.runtimeconfig.json @@ -1,9 +1,9 @@ { "runtimeOptions": { - "tfm": "net7.0", + "tfm": "net8.0", "framework": { "name": "Microsoft.NETCore.App", - "version": "7.0.0", + "version": "8.0.0", "rollForward": "latestMajor" } } diff --git a/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj b/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj index df3b9851b..873f916c4 100644 --- a/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj +++ b/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 11.0 disable annotations diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index 240c95c16..b1fa7d446 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -130,7 +130,7 @@ namespace Flax.Build /// /// The minimum SDK version. /// - public static Version MinimumVersion => new Version(7, 0); + public static Version MinimumVersion => new Version(8, 0); /// /// The maximum SDK version. diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs index f6df4068e..f2146c82a 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs @@ -164,6 +164,30 @@ namespace Flax.Build } } + public string Serialize() + { + // Null any empty fields to exclude them from serialization + if (HotReloadPostfix?.Length == 0) + HotReloadPostfix = null; + foreach (var binaryModule in BinaryModules) + { + if (binaryModule.NativePathProcessed?.Length == 0) + binaryModule.NativePathProcessed = null; + if (binaryModule.ManagedPathProcessed?.Length == 0) + binaryModule.ManagedPathProcessed = null; + } + + // Convert to Json + var options = new JsonSerializerOptions + { + WriteIndented = true, + IncludeFields = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + TypeInfoResolver = BuildTargetInfoSourceGenerationContext.Default, + }; + return JsonSerializer.Serialize(this, options); + } + public static string ProcessPath(string path, string projectPath) { if (string.IsNullOrEmpty(path)) @@ -1017,7 +1041,7 @@ namespace Flax.Build buildData.BuildInfo.AddReferencedBuilds(ref i, project.ProjectFolderPath, buildData.ReferenceBuilds); if (!buildData.Target.IsPreBuilt) - Utilities.WriteFileIfChanged(Path.Combine(outputPath, target.Name + ".Build.json"), JsonSerializer.Serialize(buildData.BuildInfo, new JsonSerializerOptions() { WriteIndented = true, IncludeFields = true, TypeInfoResolver = BuildTargetInfoSourceGenerationContext.Default })); + Utilities.WriteFileIfChanged(Path.Combine(outputPath, target.Name + ".Build.json"), buildData.BuildInfo.Serialize()); } // Deploy files @@ -1216,7 +1240,7 @@ namespace Flax.Build buildData.BuildInfo.AddReferencedBuilds(ref i, project.ProjectFolderPath, buildData.ReferenceBuilds); if (!buildData.Target.IsPreBuilt) - Utilities.WriteFileIfChanged(Path.Combine(outputPath, target.Name + ".Build.json"), JsonSerializer.Serialize(buildData.BuildInfo, new JsonSerializerOptions() { WriteIndented = true, IncludeFields = true, TypeInfoResolver = BuildTargetInfoSourceGenerationContext.Default })); + Utilities.WriteFileIfChanged(Path.Combine(outputPath, target.Name + ".Build.json"), buildData.BuildInfo.Serialize()); } // Deploy files diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs index df808a063..1fcbb6ded 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs @@ -50,7 +50,7 @@ namespace Flax.Deps.Dependencies { // Build configuration (see build.cmd -help) string configuration = "Release"; - string framework = "net7.0"; + string framework = "net8.0"; // Clean output directory var artifacts = Path.Combine(root, "artifacts"); @@ -224,7 +224,7 @@ namespace Flax.Deps.Dependencies } var privateCorelib = "System.Private.CoreLib.dll"; Utilities.FileCopy(Path.Combine(unpackTemp, "runtimes", hostRuntimeName, "native", privateCorelib), Path.Combine(dstClassLibrary, privateCorelib)); - Utilities.DirectoryCopy(Path.Combine(unpackTemp, "runtimes", hostRuntimeName, "lib", "net7.0"), dstClassLibrary, false, true); + Utilities.DirectoryCopy(Path.Combine(unpackTemp, "runtimes", hostRuntimeName, "lib", "net8.0"), dstClassLibrary, false, true); // TODO: host/fxr//hostfxr.dll // TODO: shared/Microsoft.NETCore.App//hostpolicy.dl // TODO: shared/Microsoft.NETCore.App//System.IO.Compression.Native.dll diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj index 9c837ee89..abce85366 100644 --- a/Source/Tools/Flax.Build/Flax.Build.csproj +++ b/Source/Tools/Flax.Build/Flax.Build.csproj @@ -1,7 +1,7 @@  Exe - net7.0 + net8.0 11.0 disable annotations diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs index 77b7abf46..63fade299 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs @@ -96,7 +96,7 @@ namespace Flax.Build.Projects.VisualStudio var cacheProjectsPath = Utilities.MakePathRelativeTo(Path.Combine(Globals.Root, "Cache", "Projects"), projectDirectory); var flaxBuildTargetsPath = !string.IsNullOrEmpty(cacheProjectsPath) ? Path.Combine(cacheProjectsPath, flaxBuildTargetsFilename) : flaxBuildTargetsFilename; - csProjectFileContent.AppendLine(" net7.0"); + csProjectFileContent.AppendLine(" net8.0"); csProjectFileContent.AppendLine(" disable"); csProjectFileContent.AppendLine(string.Format(" {0}", baseConfiguration.TargetBuildOptions.ScriptingAPI.CSharpNullableReferences.ToString().ToLowerInvariant())); csProjectFileContent.AppendLine(" false"); diff --git a/Source/Tools/Flax.Build/global.json b/Source/Tools/Flax.Build/global.json index 3fe9e80f6..9498eeef6 100644 --- a/Source/Tools/Flax.Build/global.json +++ b/Source/Tools/Flax.Build/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.0", + "version": "8.0.0", "rollForward": "latestMajor" } } \ No newline at end of file diff --git a/global.json b/global.json index 3fe9e80f6..9498eeef6 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.0", + "version": "8.0.0", "rollForward": "latestMajor" } } \ No newline at end of file From f16406df8a8550b000291a8ba7155c5bc1bdc278 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 26 Nov 2023 17:23:48 +0100 Subject: [PATCH 013/198] Update `Newtonsoft.Json.dll` to dotnet8 --- Source/Platforms/DotNet/AOT/Newtonsoft.Json.dll | 4 ++-- Source/Platforms/DotNet/Newtonsoft.Json.dll | 4 ++-- Source/Platforms/DotNet/Newtonsoft.Json.pdb | 4 ++-- Source/Tools/Flax.Build/Deps/Dependencies/NewtonsoftJson.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Platforms/DotNet/AOT/Newtonsoft.Json.dll b/Source/Platforms/DotNet/AOT/Newtonsoft.Json.dll index ef2f325da..24087dd29 100644 --- a/Source/Platforms/DotNet/AOT/Newtonsoft.Json.dll +++ b/Source/Platforms/DotNet/AOT/Newtonsoft.Json.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e045d6ae2a72b9b6e8922fa37537f7f14fdc9d5ced502e48a5b4ab5064a2458 -size 540672 +oid sha256:22f78856541397ffe932df4db572d6a4ea3b4f3c843202f1b2b947a925cc2649 +size 541184 diff --git a/Source/Platforms/DotNet/Newtonsoft.Json.dll b/Source/Platforms/DotNet/Newtonsoft.Json.dll index db1ecd531..b56719858 100644 --- a/Source/Platforms/DotNet/Newtonsoft.Json.dll +++ b/Source/Platforms/DotNet/Newtonsoft.Json.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf0320e0e2e754a77d7cbeeaed16fc6c4e0dfac9953e258b86a09edbe93627d3 -size 604160 +oid sha256:2189e00df470e0fa7e3294a90a2faf286af51bd25f224605faf53cc42a7ff381 +size 604672 diff --git a/Source/Platforms/DotNet/Newtonsoft.Json.pdb b/Source/Platforms/DotNet/Newtonsoft.Json.pdb index b3feda587..97d99bb47 100644 --- a/Source/Platforms/DotNet/Newtonsoft.Json.pdb +++ b/Source/Platforms/DotNet/Newtonsoft.Json.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca10fa9bd4f681410afd8c935930826511d98b33c729b721c0dba818239208bf -size 239532 +oid sha256:cd30824b5d1593411aa7c47816afb37f63ba4ec4f82c5777324d0411c853a912 +size 239516 diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/NewtonsoftJson.cs b/Source/Tools/Flax.Build/Deps/Dependencies/NewtonsoftJson.cs index dd40d5f87..08bd42257 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/NewtonsoftJson.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/NewtonsoftJson.cs @@ -50,13 +50,13 @@ namespace Flax.Deps.Dependencies "Newtonsoft.Json.pdb", "Newtonsoft.Json.xml", }; - var binFolder = Path.Combine(root, "Src", "Newtonsoft.Json", "bin", configuration, "net7.0"); + var binFolder = Path.Combine(root, "Src", "Newtonsoft.Json", "bin", configuration, "net8.0"); // Get the source CloneGitRepo(root, "https://github.com/FlaxEngine/Newtonsoft.Json.git"); // Default build - GitCheckout(root, "flax-net70"); + GitCheckout(root, "flax-net80"); Deploy.VCEnvironment.BuildSolution(solutionPath, configuration, buildPlatform); foreach (var platform in options.Platforms) { From 666e6d880ba25cae31eb4ba772cbd9ac900740ea Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 2 Dec 2023 13:33:04 -0600 Subject: [PATCH 014/198] Changes to default post process settings. --- Source/Engine/Graphics/PostProcessSettings.h | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 1d695f353..b4432af0d 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -948,7 +948,7 @@ API_STRUCT() struct FLAXENGINE_API EyeAdaptationSettings : ISerializable /// The effect rendering mode used for the exposure processing. /// API_FIELD(Attributes="EditorOrder(0), PostProcessSetting((int)EyeAdaptationSettingsOverride.Mode)") - EyeAdaptationMode Mode = EyeAdaptationMode::AutomaticHistogram; + EyeAdaptationMode Mode = EyeAdaptationMode::None; /// /// The speed at which the exposure changes when the scene brightness moves from a dark area to a bright area (brightness goes up). @@ -960,7 +960,7 @@ API_STRUCT() struct FLAXENGINE_API EyeAdaptationSettings : ISerializable /// The speed at which the exposure changes when the scene brightness moves from a bright area to a dark area (brightness goes down). /// API_FIELD(Attributes="Limit(0, 100.0f, 0.01f), EditorOrder(2), PostProcessSetting((int)EyeAdaptationSettingsOverride.SpeedDown)") - float SpeedDown = 1.0f; + float SpeedDown = 10.0f; /// /// The pre-exposure value applied to the scene color before performing post-processing (such as bloom, lens flares, etc.). @@ -984,7 +984,7 @@ API_STRUCT() struct FLAXENGINE_API EyeAdaptationSettings : ISerializable /// The maximum brightness for the auto exposure which limits the upper brightness the eye can adapt within. /// API_FIELD(Attributes="Limit(0, 100.0f, 0.01f), EditorOrder(6), PostProcessSetting((int)EyeAdaptationSettingsOverride.MaxBrightness), EditorDisplay(null, \"Maximum Brightness\")") - float MaxBrightness = 2.0f; + float MaxBrightness = 15.0f; /// /// The lower bound for the luminance histogram of the scene color. This value is in percent and limits the pixels below this brightness. Use values in the range of 60-80. Used only in AutomaticHistogram mode. @@ -996,7 +996,7 @@ API_STRUCT() struct FLAXENGINE_API EyeAdaptationSettings : ISerializable /// The upper bound for the luminance histogram of the scene color. This value is in percent and limits the pixels above this brightness. Use values in the range of 80-95. Used only in AutomaticHistogram mode. /// API_FIELD(Attributes="Limit(1, 99, 0.001f), EditorOrder(3), PostProcessSetting((int)EyeAdaptationSettingsOverride.HistogramHighPercent)") - float HistogramHighPercent = 98.0f; + float HistogramHighPercent = 90.0f; public: /// @@ -1082,13 +1082,13 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable /// Strength of the vignette effect. Value 0 hides it. The default value is 0.8. /// API_FIELD(Attributes="Limit(0, 2, 0.001f), EditorOrder(0), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteIntensity)") - float VignetteIntensity = 0.8f; + float VignetteIntensity = 0.0f; /// /// Color of the vignette. /// API_FIELD(Attributes="EditorOrder(1), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteColor)") - Float3 VignetteColor = Float3(0, 0, 0.001f); + Float3 VignetteColor = Float3::Zero; /// /// Controls the shape of the vignette. Values near 0 produce a rectangular shape. Higher values result in a rounder shape. The default value is 0.125. @@ -1230,25 +1230,25 @@ API_STRUCT() struct FLAXENGINE_API LensFlaresSettings : ISerializable /// Strength of the effect. A value of 0 disables it. /// API_FIELD(Attributes="Limit(0, 10.0f, 0.01f), EditorOrder(0), PostProcessSetting((int)LensFlaresSettingsOverride.Intensity)") - float Intensity = 1.0f; + float Intensity = 0.5f; /// /// Amount of lens flares ghosts. /// API_FIELD(Attributes="Limit(0, 16), EditorOrder(1), PostProcessSetting((int)LensFlaresSettingsOverride.Ghosts)") - int32 Ghosts = 8; + int32 Ghosts = 4; /// /// Lens flares halo width. /// API_FIELD(Attributes="EditorOrder(2), PostProcessSetting((int)LensFlaresSettingsOverride.HaloWidth)") - float HaloWidth = 0.16f; + float HaloWidth = 0.04f; /// /// Lens flares halo intensity. /// API_FIELD(Attributes="Limit(0, 10.0f, 0.01f), EditorOrder(3), PostProcessSetting((int)LensFlaresSettingsOverride.HaloIntensity)") - float HaloIntensity = 0.666f; + float HaloIntensity = 0.5f; /// /// Ghost samples dispersal parameter. @@ -1584,7 +1584,7 @@ API_STRUCT() struct FLAXENGINE_API MotionBlurSettings : ISerializable /// The blur effect strength. A value of 0 disables it, while higher values increase the effect. /// API_FIELD(Attributes="Limit(0, 5, 0.01f), EditorOrder(1), PostProcessSetting((int)MotionBlurSettingsOverride.Scale)") - float Scale = 1.0f; + float Scale = 0.5f; /// /// The amount of sample points used during motion blur rendering. It affects blur quality and performance. From 74baffd76cc107002d145ec1e1a2bf09807d6ccd Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 2 Dec 2023 13:39:16 -0600 Subject: [PATCH 015/198] Fix comment --- Source/Engine/Graphics/PostProcessSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index b4432af0d..649ec46df 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -1079,7 +1079,7 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable CameraArtifactsSettingsOverride OverrideFlags = Override::None; /// - /// Strength of the vignette effect. Value 0 hides it. The default value is 0.8. + /// Strength of the vignette effect. Value 0 hides it. The default value is 0. /// API_FIELD(Attributes="Limit(0, 2, 0.001f), EditorOrder(0), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteIntensity)") float VignetteIntensity = 0.0f; From 7d36cdb67d4374cfa336c535520ac29d43f54ef9 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 6 Dec 2023 09:33:59 -0600 Subject: [PATCH 016/198] Change vignette settings. --- Source/Engine/Graphics/PostProcessSettings.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 649ec46df..52db566da 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -1079,16 +1079,16 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable CameraArtifactsSettingsOverride OverrideFlags = Override::None; /// - /// Strength of the vignette effect. Value 0 hides it. The default value is 0. + /// Strength of the vignette effect. Value 0 hides it. The default value is 0.4. /// API_FIELD(Attributes="Limit(0, 2, 0.001f), EditorOrder(0), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteIntensity)") - float VignetteIntensity = 0.0f; + float VignetteIntensity = 0.4f; /// /// Color of the vignette. /// API_FIELD(Attributes="EditorOrder(1), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteColor)") - Float3 VignetteColor = Float3::Zero; + Float3 VignetteColor = Float3(0 ,0, 0.001f); /// /// Controls the shape of the vignette. Values near 0 produce a rectangular shape. Higher values result in a rounder shape. The default value is 0.125. From 1f6b63291de310d8fa84d1a88ad952273eb7494b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 6 Dec 2023 09:39:16 -0600 Subject: [PATCH 017/198] Fix code style error --- Source/Engine/Graphics/PostProcessSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 52db566da..1c86b9f48 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -1088,7 +1088,7 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable /// Color of the vignette. /// API_FIELD(Attributes="EditorOrder(1), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteColor)") - Float3 VignetteColor = Float3(0 ,0, 0.001f); + Float3 VignetteColor = Float3(0, 0, 0.001f); /// /// Controls the shape of the vignette. Values near 0 produce a rectangular shape. Higher values result in a rounder shape. The default value is 0.125. From 3e0496122c54405d9a8b22f44abefe1f17d8d667 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 8 Dec 2023 12:50:57 +0100 Subject: [PATCH 018/198] Fix c# compilation warning in generated glue code --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 51ce5b647..c541f93cd 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -601,7 +601,11 @@ namespace Flax.Build.Bindings else if (parameterInfo.Type.Type == "Array" && parameterInfo.Type.GenericArgs.Count > 0 && parameterInfo.Type.GenericArgs[0].Type == "bool") parameterMarshalType = $"MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = {(!functionInfo.IsStatic ? 1 : 0) + functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters.FindIndex(x => x.Name == $"__{parameterInfo.Name}Count"))})"; else if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer" || nativeType == "Array") + { parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"__{parameterInfo.Name}Count\")"; + if (!parameterInfo.IsOut) + parameterMarshalType += ", In"; // The usage of 'LibraryImportAttribute' does not follow recommendations. It is recommended to use explicit '[In]' and '[Out]' attributes on array parameters. + } else if (parameterInfo.Type.Type == "Dictionary") parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)"; else if (nativeType == "bool") From 1746db60e60ae75ad9d0b34fb14d515018502132 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 8 Dec 2023 12:59:52 +0100 Subject: [PATCH 019/198] Fix `HeaderAttribute` usage with default values --- Source/Editor/CustomEditors/LayoutElementsContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/LayoutElementsContainer.cs b/Source/Editor/CustomEditors/LayoutElementsContainer.cs index 936851b15..143e180ee 100644 --- a/Source/Editor/CustomEditors/LayoutElementsContainer.cs +++ b/Source/Editor/CustomEditors/LayoutElementsContainer.cs @@ -283,9 +283,9 @@ namespace FlaxEditor.CustomEditors internal LabelElement Header(HeaderAttribute header) { var element = Header(header.Text); - if (header.FontSize != -1) + if (header.FontSize > 0) element.Label.Font = new FontReference(element.Label.Font.Font, header.FontSize); - if (header.Color != 0) + if (header.Color > 0) element.Label.TextColor = Color.FromRGBA(header.Color); return element; } From 34ab33259b064a7d914f2900b66d295c68ddb5e5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 8 Dec 2023 14:13:52 +0100 Subject: [PATCH 020/198] Refactor Visject Surface attributes data storage to use `JsonSerializer` instead of deprecated `BinaryFormatter` --- .../Editor/Content/Items/VisualScriptItem.cs | 11 +-- Source/Editor/Surface/AttributesEditor.cs | 29 ++---- Source/Editor/Surface/SurfaceMeta.cs | 98 +++++++++++-------- 3 files changed, 68 insertions(+), 70 deletions(-) diff --git a/Source/Editor/Content/Items/VisualScriptItem.cs b/Source/Editor/Content/Items/VisualScriptItem.cs index 4ae4d06ba..e9646a446 100644 --- a/Source/Editor/Content/Items/VisualScriptItem.cs +++ b/Source/Editor/Content/Items/VisualScriptItem.cs @@ -213,7 +213,8 @@ namespace FlaxEditor.Content if (_attributes == null) { var data = _type.Asset.GetMethodMetaData(_index, Surface.SurfaceMeta.AttributeMetaTypeID); - _attributes = Surface.SurfaceMeta.GetAttributes(data); + var dataOld = _type.Asset.GetMethodMetaData(_index, Surface.SurfaceMeta.OldAttributeMetaTypeID); + _attributes = Surface.SurfaceMeta.GetAttributes(data, dataOld); } return _attributes; } @@ -290,13 +291,11 @@ namespace FlaxEditor.Content _methods = Utils.GetEmptyArray(); // Cache Visual Script attributes - var attributesData = _asset.GetMetaData(Surface.SurfaceMeta.AttributeMetaTypeID); - if (attributesData != null && attributesData.Length != 0) { - _attributes = Surface.SurfaceMeta.GetAttributes(attributesData); + var data = _asset.GetMetaData(Surface.SurfaceMeta.AttributeMetaTypeID); + var dataOld = _asset.GetMetaData(Surface.SurfaceMeta.OldAttributeMetaTypeID); + _attributes = Surface.SurfaceMeta.GetAttributes(data, dataOld); } - else - _attributes = Utils.GetEmptyArray(); } private void OnAssetReloading(Asset asset) diff --git a/Source/Editor/Surface/AttributesEditor.cs b/Source/Editor/Surface/AttributesEditor.cs index 81b11bb68..6631d0150 100644 --- a/Source/Editor/Surface/AttributesEditor.cs +++ b/Source/Editor/Surface/AttributesEditor.cs @@ -117,17 +117,9 @@ namespace FlaxEditor.Surface editor.Panel.Tag = attributeType; _presenter = editor; - using (var stream = new MemoryStream()) - { - // Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041) - using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly); + // Cache 'previous' state to check if attributes were edited after operation + _oldData = SurfaceMeta.GetAttributesData(attributes); - var formatter = new BinaryFormatter(); -#pragma warning disable SYSLIB0011 - formatter.Serialize(stream, attributes); -#pragma warning restore SYSLIB0011 - _oldData = stream.ToArray(); - } editor.Select(new Proxy { Value = attributes, @@ -145,20 +137,11 @@ namespace FlaxEditor.Surface return; } } - using (var stream = new MemoryStream()) - { - // Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041) - using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly); - var formatter = new BinaryFormatter(); -#pragma warning disable SYSLIB0011 - formatter.Serialize(stream, newValue); -#pragma warning restore SYSLIB0011 - var newData = stream.ToArray(); - if (!_oldData.SequenceEqual(newData)) - { - Edited?.Invoke(newValue); - } + var newData = SurfaceMeta.GetAttributesData(newValue); + if (!_oldData.SequenceEqual(newData)) + { + Edited?.Invoke(newValue); } Hide(); diff --git a/Source/Editor/Surface/SurfaceMeta.cs b/Source/Editor/Surface/SurfaceMeta.cs index 460dcb302..6420ae749 100644 --- a/Source/Editor/Surface/SurfaceMeta.cs +++ b/Source/Editor/Surface/SurfaceMeta.cs @@ -4,9 +4,9 @@ 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 System.Text; using FlaxEngine; namespace FlaxEditor.Surface @@ -39,28 +39,48 @@ namespace FlaxEditor.Surface public readonly List Entries = new List(); /// - /// The attribute meta type identifier. + /// The attribute meta type identifier. Uses byte[] as storage for Attribute[] serialized with BinaryFormatter (deprecated in .NET 5). + /// [Deprecated on 8.12.2023, expires on 8.12.2025] /// - public const int AttributeMetaTypeID = 12; + public const int OldAttributeMetaTypeID = 12; + + /// + /// The attribute meta type identifier. Uses byte[] as storage for Attribute[] serialized with JsonSerializer. + /// + public const int AttributeMetaTypeID = 13; /// /// Gets the attributes collection from the data. /// - /// The graph metadata. + /// The graph metadata serialized with JsonSerializer. + /// The graph metadata serialized with BinaryFormatter. /// The attributes collection. - public static Attribute[] GetAttributes(byte[] data) + public static Attribute[] GetAttributes(byte[] data, byte[] oldData) { if (data != null && data.Length != 0) { - using (var stream = new MemoryStream(data)) + try + { + var json = Encoding.Unicode.GetString(data); + return FlaxEngine.Json.JsonSerializer.Deserialize(json); + } + catch (Exception ex) + { + Editor.LogError("Failed to deserialize Visject attributes array."); + Editor.LogWarning(ex); + } + } + if (oldData != null && oldData.Length != 0) + { + // [Deprecated on 8.12.2023, expires on 8.12.2025] + using (var stream = new MemoryStream(oldData)) { try { // Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041) using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly); - - var formatter = new BinaryFormatter(); #pragma warning disable SYSLIB0011 + var formatter = new BinaryFormatter(); return (Attribute[])formatter.Deserialize(stream); #pragma warning restore SYSLIB0011 } @@ -74,6 +94,21 @@ namespace FlaxEditor.Surface return Utils.GetEmptyArray(); } + /// + /// Serializes surface attributes into byte[] data using the current format. + /// + /// The input attributes. + /// The result array with bytes. Can be empty but not null. + internal static byte[] GetAttributesData(Attribute[] attributes) + { + if (attributes != null && attributes.Length != 0) + { + var json = FlaxEngine.Json.JsonSerializer.Serialize(attributes); + return Encoding.Unicode.GetBytes(json); + } + return Utils.GetEmptyArray(); + } + /// /// Determines whether the specified attribute was defined for this member. /// @@ -93,7 +128,8 @@ namespace FlaxEditor.Surface public static Attribute[] GetAttributes(GraphParameter parameter) { var data = parameter.GetMetaData(AttributeMetaTypeID); - return GetAttributes(data); + var dataOld = parameter.GetMetaData(OldAttributeMetaTypeID); + return GetAttributes(data, dataOld); } /// @@ -102,12 +138,7 @@ namespace FlaxEditor.Surface /// The attributes collection. public Attribute[] GetAttributes() { - for (int i = 0; i < Entries.Count; i++) - { - if (Entries[i].TypeID == AttributeMetaTypeID) - return GetAttributes(Entries[i].Data); - } - return Utils.GetEmptyArray(); + return GetAttributes(GetEntry(AttributeMetaTypeID).Data, GetEntry(OldAttributeMetaTypeID).Data); } /// @@ -119,25 +150,12 @@ namespace FlaxEditor.Surface if (attributes == null || attributes.Length == 0) { RemoveEntry(AttributeMetaTypeID); + RemoveEntry(OldAttributeMetaTypeID); } else { - for (int i = 0; i < attributes.Length; i++) - { - if (attributes[i] == null) - throw new NullReferenceException("One of the Visject attributes is null."); - } - using (var stream = new MemoryStream()) - { - // Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041) - using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly); - - var formatter = new BinaryFormatter(); -#pragma warning disable SYSLIB0011 - formatter.Serialize(stream, attributes); -#pragma warning restore SYSLIB0011 - AddEntry(AttributeMetaTypeID, stream.ToArray()); - } + AddEntry(AttributeMetaTypeID, GetAttributesData(attributes)); + RemoveEntry(OldAttributeMetaTypeID); } } @@ -180,11 +198,11 @@ namespace FlaxEditor.Surface /// True if cannot save data public void Save(BinaryWriter stream) { - stream.Write(Entries.Count); - - for (int i = 0; i < Entries.Count; i++) + var entries = Entries; + stream.Write(entries.Count); + for (int i = 0; i < entries.Count; i++) { - Entry e = Entries[i]; + Entry e = entries[i]; stream.Write(e.TypeID); stream.Write((long)0); @@ -214,14 +232,12 @@ namespace FlaxEditor.Surface /// Entry public Entry GetEntry(int typeID) { - for (int i = 0; i < Entries.Count; i++) + var entries = Entries; + for (int i = 0; i < entries.Count; i++) { - if (Entries[i].TypeID == typeID) - { - return Entries[i]; - } + if (entries[i].TypeID == typeID) + return entries[i]; } - return new Entry(); } From 2f11f5f5ab6cdefb08b0f4167e9f1c6e74597f4e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 8 Dec 2023 14:48:45 +0100 Subject: [PATCH 021/198] Fix 3e0496122c54405d9a8b22f44abefe1f17d8d667 for `ref` parameters --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index c541f93cd..1587fce79 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -603,7 +603,7 @@ namespace Flax.Build.Bindings else if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer" || nativeType == "Array") { parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"__{parameterInfo.Name}Count\")"; - if (!parameterInfo.IsOut) + if (!parameterInfo.IsOut && !parameterInfo.IsRef) parameterMarshalType += ", In"; // The usage of 'LibraryImportAttribute' does not follow recommendations. It is recommended to use explicit '[In]' and '[Out]' attributes on array parameters. } else if (parameterInfo.Type.Type == "Dictionary") From 8eb68a905e2985539cd517c4a7f7b5b5aab6f1d5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 15 Dec 2023 13:38:03 +0100 Subject: [PATCH 022/198] Add initial ASTC pixel format support --- Source/Engine/Graphics/PixelFormat.h | 10 ++ .../Engine/Graphics/PixelFormatExtensions.cpp | 50 ++++--- .../Engine/Graphics/PixelFormatExtensions.h | 11 +- .../Vulkan/RenderToolsVulkan.cpp | 126 +++++++----------- 4 files changed, 98 insertions(+), 99 deletions(-) diff --git a/Source/Engine/Graphics/PixelFormat.h b/Source/Engine/Graphics/PixelFormat.h index 1257e42b7..f6793e543 100644 --- a/Source/Engine/Graphics/PixelFormat.h +++ b/Source/Engine/Graphics/PixelFormat.h @@ -513,6 +513,16 @@ API_ENUM() enum class PixelFormat : uint32 /// BC7_UNorm_sRGB = 99, + /// + /// A four-component ASTC (4x4 pixel block in 128 bits) block-compression format that supports RGBA channels. + /// + ASTC_4x4_UNorm = 100, + + /// + /// A four-component ASTC (4x4 pixel block in 128 bits) block-compression format that supports RGBA channels. + /// + ASTC_4x4_UNorm_sRGB = 101, + /// /// The maximum format value (for internal use only). /// diff --git a/Source/Engine/Graphics/PixelFormatExtensions.cpp b/Source/Engine/Graphics/PixelFormatExtensions.cpp index 991844b22..4369ebb8b 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.cpp +++ b/Source/Engine/Graphics/PixelFormatExtensions.cpp @@ -5,19 +5,12 @@ // ReSharper disable CppClangTidyClangDiagnosticSwitchEnum -#define MAX_PIXEL_FORMATS 256 - namespace { - int32 sizeOfInBits[MAX_PIXEL_FORMATS]; - - int32 GetIndex(const PixelFormat format) - { - return (int32)format; - } + int32 sizeOfInBits[(int32)PixelFormat::MAX]; } -#define InitFormat(formats, bitCount) for(int i = 0; i < ARRAY_COUNT(formats); i++) { sizeOfInBits[GetIndex(formats[i])] = bitCount; } +#define InitFormat(formats, bitCount) for(int i = 0; i < ARRAY_COUNT(formats); i++) { sizeOfInBits[(int32)formats[i]] = bitCount; } void PixelFormatExtensions::Init() { @@ -35,7 +28,9 @@ void PixelFormatExtensions::Init() PixelFormat::R8_SNorm, PixelFormat::R8_Typeless, PixelFormat::R8_UInt, - PixelFormat::R8_UNorm + PixelFormat::R8_UNorm, + PixelFormat::ASTC_4x4_UNorm, + PixelFormat::ASTC_4x4_UNorm_sRGB, }; InitFormat(formats2, 8); @@ -53,8 +48,7 @@ void PixelFormatExtensions::Init() PixelFormat::R8G8_SNorm, PixelFormat::R8G8_Typeless, PixelFormat::R8G8_UInt, - PixelFormat::R8G8_UNorm - + PixelFormat::R8G8_UNorm, }; InitFormat(formats3, 16); @@ -163,7 +157,7 @@ void PixelFormatExtensions::Init() int32 PixelFormatExtensions::SizeInBits(PixelFormat format) { - return sizeOfInBits[GetIndex(format)]; + return sizeOfInBits[(int32)format]; } int32 PixelFormatExtensions::AlphaSizeInBits(const PixelFormat format) @@ -319,6 +313,8 @@ bool PixelFormatExtensions::IsCompressed(const PixelFormat format) case PixelFormat::BC7_Typeless: case PixelFormat::BC7_UNorm: case PixelFormat::BC7_UNorm_sRGB: + case PixelFormat::ASTC_4x4_UNorm: + case PixelFormat::ASTC_4x4_UNorm_sRGB: return true; default: return false; @@ -356,6 +352,18 @@ bool PixelFormatExtensions::IsCompressedBC(PixelFormat format) } } +bool PixelFormatExtensions::IsCompressedASTC(PixelFormat format) +{ + switch (format) + { + case PixelFormat::ASTC_4x4_UNorm: + case PixelFormat::ASTC_4x4_UNorm_sRGB: + return true; + default: + return false; + } +} + bool PixelFormatExtensions::IsPacked(const PixelFormat format) { return format == PixelFormat::R8G8_B8G8_UNorm || format == PixelFormat::G8R8_G8B8_UNorm; @@ -382,6 +390,7 @@ bool PixelFormatExtensions::IsSRGB(const PixelFormat format) case PixelFormat::B8G8R8A8_UNorm_sRGB: case PixelFormat::B8G8R8X8_UNorm_sRGB: case PixelFormat::BC7_UNorm_sRGB: + case PixelFormat::ASTC_4x4_UNorm_sRGB: return true; default: return false; @@ -392,6 +401,8 @@ bool PixelFormatExtensions::IsHDR(const PixelFormat format) { switch (format) { + case PixelFormat::R11G11B10_Float: + case PixelFormat::R10G10B10A2_UNorm: case PixelFormat::R16G16B16A16_Float: case PixelFormat::R32G32B32A32_Float: case PixelFormat::R16G16_Float: @@ -399,7 +410,6 @@ bool PixelFormatExtensions::IsHDR(const PixelFormat format) case PixelFormat::BC6H_Sf16: case PixelFormat::BC6H_Uf16: return true; - default: return false; } @@ -527,7 +537,7 @@ bool PixelFormatExtensions::IsInteger(const PixelFormat format) } } -int PixelFormatExtensions::ComputeScanlineCount(const PixelFormat format, int32 height) +int32 PixelFormatExtensions::ComputeScanlineCount(const PixelFormat format, int32 height) { switch (format) { @@ -552,13 +562,15 @@ int PixelFormatExtensions::ComputeScanlineCount(const PixelFormat format, int32 case PixelFormat::BC7_Typeless: case PixelFormat::BC7_UNorm: case PixelFormat::BC7_UNorm_sRGB: + case PixelFormat::ASTC_4x4_UNorm: + case PixelFormat::ASTC_4x4_UNorm_sRGB: return Math::Max(1, (height + 3) / 4); default: return height; } } -int PixelFormatExtensions::ComputeComponentsCount(const PixelFormat format) +int32 PixelFormatExtensions::ComputeComponentsCount(const PixelFormat format) { switch (format) { @@ -599,6 +611,8 @@ int PixelFormatExtensions::ComputeComponentsCount(const PixelFormat format) case PixelFormat::B8G8R8A8_UNorm_sRGB: case PixelFormat::B8G8R8X8_Typeless: case PixelFormat::B8G8R8X8_UNorm_sRGB: + case PixelFormat::ASTC_4x4_UNorm: + case PixelFormat::ASTC_4x4_UNorm_sRGB: return 4; case PixelFormat::R32G32B32_Typeless: case PixelFormat::R32G32B32_Float: @@ -678,6 +692,8 @@ PixelFormat PixelFormatExtensions::TosRGB(const PixelFormat format) return PixelFormat::B8G8R8X8_UNorm_sRGB; case PixelFormat::BC7_UNorm: return PixelFormat::BC7_UNorm_sRGB; + case PixelFormat::ASTC_4x4_UNorm: + return PixelFormat::ASTC_4x4_UNorm_sRGB; default: return format; } @@ -701,6 +717,8 @@ PixelFormat PixelFormatExtensions::ToNonsRGB(const PixelFormat format) return PixelFormat::B8G8R8X8_UNorm; case PixelFormat::BC7_UNorm_sRGB: return PixelFormat::BC7_UNorm; + case PixelFormat::ASTC_4x4_UNorm_sRGB: + return PixelFormat::ASTC_4x4_UNorm; default: return format; } diff --git a/Source/Engine/Graphics/PixelFormatExtensions.h b/Source/Engine/Graphics/PixelFormatExtensions.h index 604bcb2c3..67049cd5a 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.h +++ b/Source/Engine/Graphics/PixelFormatExtensions.h @@ -95,6 +95,13 @@ public: /// True if the is a compressed format from BC formats family. API_FUNCTION() static bool IsCompressedBC(PixelFormat format); + /// + /// Returns true if the is a compressed format from ASTC formats family (various block sizes). + /// + /// The format to check for compressed. + /// True if the is a compressed format from ASTC formats family. + API_FUNCTION() static bool IsCompressedASTC(PixelFormat format); + /// /// Determines whether the specified is packed. /// @@ -164,14 +171,14 @@ public: /// The . /// The height. /// The scanline count. - API_FUNCTION() static int ComputeScanlineCount(PixelFormat format, int32 height); + API_FUNCTION() static int32 ComputeScanlineCount(PixelFormat format, int32 height); /// /// Computes the format components count (number of R, G, B, A channels). /// /// The . /// The components count. - API_FUNCTION() static int ComputeComponentsCount(PixelFormat format); + API_FUNCTION() static int32 ComputeComponentsCount(PixelFormat format); /// /// Finds the equivalent sRGB format to the provided format. diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp index 7bf254f6a..3a5a91e5e 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp @@ -6,8 +6,8 @@ #include "Engine/Core/Types/StringBuilder.h" #include "Engine/Core/Log.h" -VkFormat RenderToolsVulkan::PixelFormatToVkFormat[static_cast(PixelFormat::MAX)] = { - +VkFormat RenderToolsVulkan::PixelFormatToVkFormat[102] = +{ VK_FORMAT_UNDEFINED, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT, @@ -27,13 +27,10 @@ VkFormat RenderToolsVulkan::PixelFormatToVkFormat[static_cast(PixelFormat VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32_UINT, VK_FORMAT_R32G32_SINT, - VK_FORMAT_UNDEFINED, - // TODO: R32G8X24_Typeless + VK_FORMAT_UNDEFINED, // TODO: R32G8X24_Typeless VK_FORMAT_D32_SFLOAT_S8_UINT, - VK_FORMAT_UNDEFINED, - // TODO: R32_Float_X8X24_Typeless - VK_FORMAT_UNDEFINED, - // TODO: X32_Typeless_G8X24_UInt + VK_FORMAT_UNDEFINED, // TODO: R32_Float_X8X24_Typeless + VK_FORMAT_UNDEFINED, // TODO: X32_Typeless_G8X24_UInt VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_FORMAT_A2B10G10R10_UINT_PACK32, @@ -76,15 +73,11 @@ VkFormat RenderToolsVulkan::PixelFormatToVkFormat[static_cast(PixelFormat VK_FORMAT_R8_UINT, VK_FORMAT_R8_SNORM, VK_FORMAT_R8_SINT, - VK_FORMAT_UNDEFINED, - // TODO: A8_UNorm - VK_FORMAT_UNDEFINED, - // TODO: R1_UNorm + VK_FORMAT_UNDEFINED, // TODO: A8_UNorm + VK_FORMAT_UNDEFINED, // TODO: R1_UNorm VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, - VK_FORMAT_UNDEFINED, - // TODO: R8G8_B8G8_UNorm - VK_FORMAT_UNDEFINED, - // TODO: G8R8_G8B8_UNorm + VK_FORMAT_UNDEFINED, // TODO: R8G8_B8G8_UNorm + VK_FORMAT_UNDEFINED, // TODO: G8R8_G8B8_UNorm VK_FORMAT_BC1_RGBA_UNORM_BLOCK, VK_FORMAT_BC1_RGBA_UNORM_BLOCK, VK_FORMAT_BC1_RGBA_SRGB_BLOCK, @@ -104,8 +97,7 @@ VkFormat RenderToolsVulkan::PixelFormatToVkFormat[static_cast(PixelFormat VK_FORMAT_B5G5R5A1_UNORM_PACK16, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM, - VK_FORMAT_UNDEFINED, - // TODO: R10G10B10_Xr_Bias_A2_UNorm + VK_FORMAT_UNDEFINED, // TODO: R10G10B10_Xr_Bias_A2_UNorm VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, @@ -116,81 +108,53 @@ VkFormat RenderToolsVulkan::PixelFormatToVkFormat[static_cast(PixelFormat VK_FORMAT_BC7_UNORM_BLOCK, VK_FORMAT_BC7_UNORM_BLOCK, VK_FORMAT_BC7_SRGB_BLOCK, + VK_FORMAT_ASTC_4x4_UNORM_BLOCK, + VK_FORMAT_ASTC_4x4_SRGB_BLOCK, }; -VkBlendFactor RenderToolsVulkan::BlendToVkBlendFactor[static_cast(BlendingMode::Blend::MAX)] = +VkBlendFactor RenderToolsVulkan::BlendToVkBlendFactor[20] = { VK_BLEND_FACTOR_MAX_ENUM, - VK_BLEND_FACTOR_ZERO, - // Zero - VK_BLEND_FACTOR_ONE, - // One - VK_BLEND_FACTOR_SRC_COLOR, - // SrcColor - VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR, - // InvSrcColor - VK_BLEND_FACTOR_SRC_ALPHA, - // SrcAlpha - VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, - // InvSrcAlpha - VK_BLEND_FACTOR_DST_ALPHA, - // DestAlpha - VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, - // InvDestAlpha - VK_BLEND_FACTOR_DST_COLOR, - // DestColor, - VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR, - // InvDestColor - VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, - // SrcAlphaSat - VK_BLEND_FACTOR_CONSTANT_ALPHA, - // BlendFactor - VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, - // BlendInvFactor - VK_BLEND_FACTOR_SRC1_COLOR, - // Src1Color - VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR, - // InvSrc1Color - VK_BLEND_FACTOR_SRC1_ALPHA, - // Src1Alpha - VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA, - // InvSrc1Alpha + VK_BLEND_FACTOR_ZERO, // Zero + VK_BLEND_FACTOR_ONE, // One + VK_BLEND_FACTOR_SRC_COLOR, // SrcColor + VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR, // InvSrcColor + VK_BLEND_FACTOR_SRC_ALPHA, // SrcAlpha + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, // InvSrcAlpha + VK_BLEND_FACTOR_DST_ALPHA, // DestAlpha + VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, // InvDestAlpha + VK_BLEND_FACTOR_DST_COLOR, // DestColor, + VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR, // InvDestColor + VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, // SrcAlphaSat + VK_BLEND_FACTOR_CONSTANT_ALPHA, // BlendFactor + VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, // BlendInvFactor + VK_BLEND_FACTOR_SRC1_COLOR, // Src1Color + VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR, // InvSrc1Color + VK_BLEND_FACTOR_SRC1_ALPHA, // Src1Alpha + VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA, // InvSrc1Alpha }; -VkBlendOp RenderToolsVulkan::OperationToVkBlendOp[static_cast(BlendingMode::Operation::MAX)] = +VkBlendOp RenderToolsVulkan::OperationToVkBlendOp[6] = { VK_BLEND_OP_MAX_ENUM, - VK_BLEND_OP_ADD, - // Add - VK_BLEND_OP_SUBTRACT, - // Subtract - VK_BLEND_OP_REVERSE_SUBTRACT, - // RevSubtract - VK_BLEND_OP_MIN, - // Min - VK_BLEND_OP_MAX, - // Max + VK_BLEND_OP_ADD, // Add + VK_BLEND_OP_SUBTRACT, // Subtract + VK_BLEND_OP_REVERSE_SUBTRACT, // RevSubtract + VK_BLEND_OP_MIN, // Min + VK_BLEND_OP_MAX, // Max }; -VkCompareOp RenderToolsVulkan::ComparisonFuncToVkCompareOp[static_cast(ComparisonFunc::MAX)] = +VkCompareOp RenderToolsVulkan::ComparisonFuncToVkCompareOp[9] = { VK_COMPARE_OP_MAX_ENUM, - VK_COMPARE_OP_NEVER, - // Never - VK_COMPARE_OP_LESS, - // Less - VK_COMPARE_OP_EQUAL, - // Equal - VK_COMPARE_OP_LESS_OR_EQUAL, - // LessEqual - VK_COMPARE_OP_GREATER, - // Grather - VK_COMPARE_OP_NOT_EQUAL, - // NotEqual - VK_COMPARE_OP_GREATER_OR_EQUAL, - // GratherEqual - VK_COMPARE_OP_ALWAYS, - // Always + VK_COMPARE_OP_NEVER, // Never + VK_COMPARE_OP_LESS, // Less + VK_COMPARE_OP_EQUAL, // Equal + VK_COMPARE_OP_LESS_OR_EQUAL, // LessEqual + VK_COMPARE_OP_GREATER, // Grather + VK_COMPARE_OP_NOT_EQUAL, // NotEqual + VK_COMPARE_OP_GREATER_OR_EQUAL, // GratherEqual + VK_COMPARE_OP_ALWAYS, // Always }; #define VKERR(x) case x: sb.Append(TEXT(#x)); break From 3761eb5a1e00a0e0feb8d7458aa36166023b9623 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 15 Dec 2023 13:41:37 +0100 Subject: [PATCH 023/198] Add normal logs for deps build subcommands --- .../Flax.Build/Deps/Dependencies/OpenAL.cs | 18 ++++++------- .../Flax.Build/Deps/Dependencies/PhysX.cs | 8 +++--- .../Flax.Build/Deps/Dependencies/freetype.cs | 8 +++--- .../Flax.Build/Deps/Dependencies/glslang.cs | 16 ++++++------ .../Tools/Flax.Build/Deps/Dependencies/ogg.cs | 12 ++++----- .../Flax.Build/Deps/Dependencies/vorbis.cs | 20 +++++++------- Source/Tools/Flax.Build/Deps/Dependency.cs | 26 +++++++++---------- 7 files changed, 54 insertions(+), 54 deletions(-) diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs index 1adb73bb3..96ef05dd8 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs @@ -102,7 +102,7 @@ namespace Flax.Deps.Dependencies var packagePath = Path.Combine(root, "package.zip"); File.Delete(packagePath); Downloader.DownloadFileFromUrlToPath("https://openal-soft.org/openal-releases/openal-soft-" + version + ".tar.bz2", packagePath); - Utilities.Run("tar", "xjf " + packagePath.Replace('\\', '/'), null, root, Utilities.RunOptions.None); + Utilities.Run("tar", "xjf " + packagePath.Replace('\\', '/'), null, root, Utilities.RunOptions.ConsoleLogOutput); // Use separate build directory root = Path.Combine(root, "openal-soft-" + version); @@ -110,8 +110,8 @@ namespace Flax.Deps.Dependencies SetupDirectory(buildDir, true); // Build for Linux - Utilities.Run("cmake", "-G \"Unix Makefiles\" -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DLIBTYPE=STATIC " + config + " ..", null, buildDir, Utilities.RunOptions.None, envVars); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None, envVars); + Utilities.Run("cmake", "-G \"Unix Makefiles\" -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DLIBTYPE=STATIC " + config + " ..", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); @@ -137,7 +137,7 @@ namespace Flax.Deps.Dependencies } else { - Utilities.Run("tar", "xjf " + packagePath.Replace('\\', '/'), null, root, Utilities.RunOptions.None); + Utilities.Run("tar", "xjf " + packagePath.Replace('\\', '/'), null, root, Utilities.RunOptions.ConsoleLogOutput); } // Use separate build directory @@ -147,7 +147,7 @@ namespace Flax.Deps.Dependencies // Build RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); @@ -165,7 +165,7 @@ namespace Flax.Deps.Dependencies var packagePath = Path.Combine(root, "package.zip"); File.Delete(packagePath); Downloader.DownloadFileFromUrlToPath("https://openal-soft.org/openal-releases/openal-soft-" + version + ".tar.bz2", packagePath); - Utilities.Run("tar", "xjf " + packagePath.Replace('\\', '/'), null, root, Utilities.RunOptions.None); + Utilities.Run("tar", "xjf " + packagePath.Replace('\\', '/'), null, root, Utilities.RunOptions.ConsoleLogOutput); // Use separate build directory root = Path.Combine(root, "openal-soft-" + version); @@ -176,7 +176,7 @@ namespace Flax.Deps.Dependencies { SetupDirectory(buildDir, true); RunCmake(buildDir, platform, architecture, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, architecture); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); @@ -196,7 +196,7 @@ namespace Flax.Deps.Dependencies if (!File.Exists(packagePath)) { Downloader.DownloadFileFromUrlToPath("https://openal-soft.org/openal-releases/openal-soft-" + version + ".tar.bz2", packagePath); - Utilities.Run("tar", "xjf " + packagePath.Replace('\\', '/'), null, root, Utilities.RunOptions.None); + Utilities.Run("tar", "xjf " + packagePath.Replace('\\', '/'), null, root, Utilities.RunOptions.ConsoleLogOutput); } // Use separate build directory @@ -206,7 +206,7 @@ namespace Flax.Deps.Dependencies // Build for iOS SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_SYSTEM_NAME=iOS -DALSOFT_OSX_FRAMEWORK=ON -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs index 2de1f64a5..f26440482 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs @@ -283,7 +283,7 @@ namespace Flax.Deps.Dependencies switch (targetPlatform) { case TargetPlatform.Android: - Utilities.Run("cmake", "--build .", null, Path.Combine(root, "physx\\compiler\\android-" + configuration), Utilities.RunOptions.None, envVars); + Utilities.Run("cmake", "--build .", null, Path.Combine(root, "physx\\compiler\\android-" + configuration), Utilities.RunOptions.ConsoleLogOutput, envVars); break; default: VCEnvironment.BuildSolution(Path.Combine(solutionFilesRoot, preset, "PhysXSDK.sln"), configuration, buildPlatform, msBuildProps, msBuild); @@ -291,10 +291,10 @@ namespace Flax.Deps.Dependencies } break; case TargetPlatform.Linux: - Utilities.Run("make", null, null, Path.Combine(projectGenDir, "compiler", "linux-" + configuration), Utilities.RunOptions.None); + Utilities.Run("make", null, null, Path.Combine(projectGenDir, "compiler", "linux-" + configuration), Utilities.RunOptions.ConsoleLogOutput); break; case TargetPlatform.Mac: - Utilities.Run("xcodebuild", "-project PhysXSDK.xcodeproj -alltargets -configuration " + configuration, null, Path.Combine(projectGenDir, "compiler", preset), Utilities.RunOptions.None); + Utilities.Run("xcodebuild", "-project PhysXSDK.xcodeproj -alltargets -configuration " + configuration, null, Path.Combine(projectGenDir, "compiler", preset), Utilities.RunOptions.ConsoleLogOutput); break; default: throw new InvalidPlatformException(BuildPlatform); } @@ -321,7 +321,7 @@ namespace Flax.Deps.Dependencies { case TargetPlatform.Mac: case TargetPlatform.Android: - Utilities.Run("strip", "\"" + filename + "\"", null, dstBinaries, Utilities.RunOptions.None); + Utilities.Run("strip", "\"" + filename + "\"", null, dstBinaries, Utilities.RunOptions.ConsoleLogOutput); break; } break; diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs index 165317180..d2d31a613 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs @@ -229,7 +229,7 @@ namespace Flax.Deps.Dependencies // Build for Android SetupDirectory(buildDir, true); RunCmake(buildDir, TargetPlatform.Android, TargetArchitecture.ARM64, ".. -DFT_WITH_BZIP2=OFF -DFT_WITH_ZLIB=OFF -DFT_WITH_PNG=OFF -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; @@ -239,7 +239,7 @@ namespace Flax.Deps.Dependencies // Build for Switch SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; @@ -251,7 +251,7 @@ namespace Flax.Deps.Dependencies { SetupDirectory(buildDir, true); RunCmake(buildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, architecture); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); } @@ -268,7 +268,7 @@ namespace Flax.Deps.Dependencies // Build for iOS SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DIOS_PLATFORM=OS -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_BUILD_TYPE=Release -DFT_WITH_BZIP2=OFF -DFT_WITH_ZLIB=OFF -DFT_WITH_PNG=OFF"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/glslang.cs b/Source/Tools/Flax.Build/Deps/Dependencies/glslang.cs index f88ded6a1..46863ec08 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/glslang.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/glslang.cs @@ -53,7 +53,7 @@ namespace Flax.Deps.Dependencies CloneGitRepoFast(root, "https://github.com/FlaxEngine/glslang.git"); // Setup the external sources - Utilities.Run("python", "update_glslang_sources.py", null, root, Utilities.RunOptions.None); + Utilities.Run("python", "update_glslang_sources.py", null, root, Utilities.RunOptions.ConsoleLogOutput); foreach (var platform in options.Platforms) { @@ -77,7 +77,7 @@ namespace Flax.Deps.Dependencies // Build for Win64 File.Delete(Path.Combine(buildDir, "CMakeCache.txt")); RunCmake(buildDir, platform, TargetArchitecture.x64, cmakeArgs); - Utilities.Run("cmake", string.Format("--build . --config {0} --target install", configuration), null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", string.Format("--build . --config {0} --target install", configuration), null, buildDir, Utilities.RunOptions.ConsoleLogOutput); Deploy.VCEnvironment.BuildSolution(solutionPath, configuration, "x64"); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); foreach (var file in outputFiles) @@ -103,14 +103,14 @@ namespace Flax.Deps.Dependencies // Build for Linux RunCmake(root, platform, TargetArchitecture.x64, cmakeArgs); - Utilities.Run("cmake", string.Format("--build . --config {0} --target install", configuration), null, buildDir, Utilities.RunOptions.None); - Utilities.Run("make", null, null, root, Utilities.RunOptions.None); + Utilities.Run("cmake", string.Format("--build . --config {0} --target install", configuration), null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("make", null, null, root, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); foreach (var file in outputFiles) { var dst = Path.Combine(depsFolder, Path.GetFileName(file)); Utilities.FileCopy(file, dst); - //Utilities.Run("strip", string.Format("-s \"{0}\"", dst), null, null, Utilities.RunOptions.None); + //Utilities.Run("strip", string.Format("-s \"{0}\"", dst), null, null, Utilities.RunOptions.ConsoleLogOutput); } break; } @@ -133,14 +133,14 @@ namespace Flax.Deps.Dependencies foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 }) { RunCmake(root, platform, architecture, cmakeArgs); - Utilities.Run("cmake", string.Format("--build . --config {0} --target install", configuration), null, buildDir, Utilities.RunOptions.None); - Utilities.Run("make", null, null, root, Utilities.RunOptions.None); + Utilities.Run("cmake", string.Format("--build . --config {0} --target install", configuration), null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("make", null, null, root, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, architecture); foreach (var file in outputFiles) { var dst = Path.Combine(depsFolder, Path.GetFileName(file)); Utilities.FileCopy(file, dst); - Utilities.Run("strip", string.Format("\"{0}\"", dst), null, null, Utilities.RunOptions.None); + Utilities.Run("strip", string.Format("\"{0}\"", dst), null, null, Utilities.RunOptions.ConsoleLogOutput); } } break; diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs b/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs index fe6800001..eb02accef 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs @@ -128,8 +128,8 @@ namespace Flax.Deps.Dependencies var toolchain = UnixToolchain.GetToolchainName(platform, TargetArchitecture.x64); Utilities.Run(Path.Combine(root, "configure"), string.Format("--host={0}", toolchain), null, root, Utilities.RunOptions.Default, envVars); SetupDirectory(buildDir, true); - Utilities.Run("cmake", "-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release ..", null, buildDir, Utilities.RunOptions.None, envVars); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None, envVars); + Utilities.Run("cmake", "-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release ..", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); @@ -196,7 +196,7 @@ namespace Flax.Deps.Dependencies // Build for Android SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; @@ -209,7 +209,7 @@ namespace Flax.Deps.Dependencies // Build for Switch SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; @@ -221,7 +221,7 @@ namespace Flax.Deps.Dependencies { SetupDirectory(buildDir, true); RunCmake(buildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, architecture); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); } @@ -231,7 +231,7 @@ namespace Flax.Deps.Dependencies { SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs index 75b2810be..c3a53ebd4 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs @@ -292,8 +292,8 @@ namespace Flax.Deps.Dependencies var toolchain = UnixToolchain.GetToolchainName(platform, TargetArchitecture.x64); Utilities.Run(Path.Combine(root, "configure"), string.Format("--host={0}", toolchain), null, root, Utilities.RunOptions.Default, envVars); SetupDirectory(buildDir, true); - Utilities.Run("cmake", "-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release ..", null, buildDir, Utilities.RunOptions.None, envVars); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None, envVars); + Utilities.Run("cmake", "-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release ..", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); foreach (var file in binariesToCopyUnix) Utilities.FileCopy(Path.Combine(buildDir, file.SrcFolder, file.Filename), Path.Combine(depsFolder, file.Filename)); @@ -328,10 +328,10 @@ namespace Flax.Deps.Dependencies // Build for Android SetupDirectory(oggBuildDir, true); RunCmake(oggBuildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=\"../install\""); - Utilities.Run("cmake", "--build . --target install", null, oggBuildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build . --target install", null, oggBuildDir, Utilities.RunOptions.ConsoleLogOutput); SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, string.Format(".. -DCMAKE_BUILD_TYPE=Release -DOGG_INCLUDE_DIR=\"{0}/install/include\" -DOGG_LIBRARY=\"{0}/install/lib\"", oggRoot)); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopyUnix) Utilities.FileCopy(Path.Combine(buildDir, file.SrcFolder, file.Filename), Path.Combine(depsFolder, file.Filename)); @@ -354,11 +354,11 @@ namespace Flax.Deps.Dependencies // Build for Switch SetupDirectory(oggBuildDir, true); RunCmake(oggBuildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=\"../install\""); - Utilities.Run("cmake", "--build . --target install", null, oggBuildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build . --target install", null, oggBuildDir, Utilities.RunOptions.ConsoleLogOutput); Utilities.FileCopy(Path.Combine(GetBinariesFolder(options, platform), "ogg", "include", "ogg", "config_types.h"), Path.Combine(oggRoot, "install", "include", "ogg", "config_types.h")); SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, string.Format(".. -DCMAKE_BUILD_TYPE=Release -DOGG_INCLUDE_DIR=\"{0}/install/include\" -DOGG_LIBRARY=\"{0}/install/lib\"", oggRoot)); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopyUnix) Utilities.FileCopy(Path.Combine(buildDir, file.SrcFolder, file.Filename), Path.Combine(depsFolder, file.Filename)); @@ -380,10 +380,10 @@ namespace Flax.Deps.Dependencies { SetupDirectory(oggBuildDir, true); RunCmake(oggBuildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=\"../install\""); - Utilities.Run("cmake", "--build . --target install", null, oggBuildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build . --target install", null, oggBuildDir, Utilities.RunOptions.ConsoleLogOutput); SetupDirectory(buildDir, true); RunCmake(buildDir, platform, architecture, string.Format(".. -DCMAKE_BUILD_TYPE=Release -DOGG_INCLUDE_DIR=\"{0}/install/include\" -DOGG_LIBRARY=\"{0}/install/lib\"", oggRoot)); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, architecture); foreach (var file in binariesToCopyUnix) Utilities.FileCopy(Path.Combine(buildDir, file.SrcFolder, file.Filename), Path.Combine(depsFolder, file.Filename)); @@ -404,10 +404,10 @@ namespace Flax.Deps.Dependencies // Build for Mac SetupDirectory(oggBuildDir, true); RunCmake(oggBuildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=\"../install\""); - Utilities.Run("cmake", "--build . --target install", null, oggBuildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build . --target install", null, oggBuildDir, Utilities.RunOptions.ConsoleLogOutput); SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, string.Format(".. -DCMAKE_BUILD_TYPE=Release -DOGG_INCLUDE_DIR=\"{0}/install/include\" -DOGG_LIBRARY=\"{0}/install/lib\"", oggRoot)); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopyUnix) Utilities.FileCopy(Path.Combine(buildDir, file.SrcFolder, file.Filename), Path.Combine(depsFolder, file.Filename)); diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index 46422179e..e9b646a6b 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -136,14 +136,14 @@ namespace Flax.Deps if (submodules) cmdLine += " --recurse-submodules"; - Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.None); + Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.ConsoleLogOutput); if (submodules) - Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.None); + Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.ConsoleLogOutput); } if (commit != null) { - Utilities.Run("git", string.Format("reset --hard {0}", commit), null, null, Utilities.RunOptions.None); + Utilities.Run("git", string.Format("reset --hard {0}", commit), null, null, Utilities.RunOptions.ConsoleLogOutput); } } @@ -164,9 +164,9 @@ namespace Flax.Deps if (submodules) cmdLine += " --recurse-submodules"; - Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.None); + Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.ConsoleLogOutput); if (submodules) - Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.None); + Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.ConsoleLogOutput); } } @@ -191,14 +191,14 @@ namespace Flax.Deps if (submodules) cmdLine += " --recurse-submodules"; - Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.None); + Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.ConsoleLogOutput); if (submodules) - Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.None); + Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.ConsoleLogOutput); } if (commit != null) { - Utilities.Run("git", string.Format("reset --hard {0}", commit), null, path, Utilities.RunOptions.None); + Utilities.Run("git", string.Format("reset --hard {0}", commit), null, path, Utilities.RunOptions.ConsoleLogOutput); } } @@ -218,13 +218,13 @@ namespace Flax.Deps if (submodules) cmdLine += " --recurse-submodules"; - Utilities.Run("git", cmdLine, null, path, Utilities.RunOptions.None); + Utilities.Run("git", cmdLine, null, path, Utilities.RunOptions.ConsoleLogOutput); if (submodules) - Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.None); + Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.ConsoleLogOutput); if (commit != null) { - Utilities.Run("git", string.Format("reset --hard {0}", commit), null, path, Utilities.RunOptions.None); + Utilities.Run("git", string.Format("reset --hard {0}", commit), null, path, Utilities.RunOptions.ConsoleLogOutput); } } @@ -234,7 +234,7 @@ namespace Flax.Deps /// The local path that contains git repository. public static void GitResetLocalChanges(string path) { - Utilities.Run("git", "reset --hard", null, path, Utilities.RunOptions.None); + Utilities.Run("git", "reset --hard", null, path, Utilities.RunOptions.ConsoleLogOutput); } /// @@ -313,7 +313,7 @@ namespace Flax.Deps if (customArgs != null) cmdLine += " " + customArgs; - Utilities.Run("cmake", cmdLine, null, path, Utilities.RunOptions.None, envVars); + Utilities.Run("cmake", cmdLine, null, path, Utilities.RunOptions.ConsoleLogOutput, envVars); } /// From 799fde1a2685ba443ebf70f5cb6bdbcf96fc4cf8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 15 Dec 2023 14:33:09 +0100 Subject: [PATCH 024/198] =?UTF-8?q?Add=20=E2=80=98astc=E2=80=99=20encoder?= =?UTF-8?q?=20lib?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/Engine/Graphics/RenderTools.cpp | 13 +- .../Tools/TextureTool/TextureTool.Build.cs | 5 + .../TextureTool/TextureTool.DirectXTex.cpp | 4 + .../Tools/TextureTool/TextureTool.astc.cpp | 117 +++ .../Engine/Tools/TextureTool/TextureTool.cpp | 26 + Source/Engine/Tools/TextureTool/TextureTool.h | 4 + .../Tools/TextureTool/TextureTool.stb.cpp | 11 +- .../Binaries/ThirdParty/ARM64/libastcenc.a | 3 + .../Mac/Binaries/ThirdParty/x64/libastcenc.a | 3 + Source/ThirdParty/astc/LICENSE.txt | 175 ++++ Source/ThirdParty/astc/astc.Build.cs | 39 + Source/ThirdParty/astc/astcenc.h | 829 ++++++++++++++++++ .../Flax.Build/Deps/Dependencies/astc.cs | 65 ++ 13 files changed, 1290 insertions(+), 4 deletions(-) create mode 100644 Source/Engine/Tools/TextureTool/TextureTool.astc.cpp create mode 100644 Source/Platforms/Mac/Binaries/ThirdParty/ARM64/libastcenc.a create mode 100644 Source/Platforms/Mac/Binaries/ThirdParty/x64/libastcenc.a create mode 100644 Source/ThirdParty/astc/LICENSE.txt create mode 100644 Source/ThirdParty/astc/astc.Build.cs create mode 100644 Source/ThirdParty/astc/astcenc.h create mode 100644 Source/Tools/Flax.Build/Deps/Dependencies/astc.cs diff --git a/Source/Engine/Graphics/RenderTools.cpp b/Source/Engine/Graphics/RenderTools.cpp index 55f4df7d2..7ffecd62c 100644 --- a/Source/Engine/Graphics/RenderTools.cpp +++ b/Source/Engine/Graphics/RenderTools.cpp @@ -306,7 +306,6 @@ void RenderTools::ComputePitch(PixelFormat format, int32 width, int32 height, ui slicePitch = rowPitch * nbh; } break; - case PixelFormat::BC2_Typeless: case PixelFormat::BC2_UNorm: case PixelFormat::BC2_UNorm_sRGB: @@ -330,14 +329,22 @@ void RenderTools::ComputePitch(PixelFormat format, int32 width, int32 height, ui slicePitch = rowPitch * nbh; } break; - + case PixelFormat::ASTC_4x4_UNorm: + case PixelFormat::ASTC_4x4_UNorm_sRGB: + { + const int32 blockSize = 4; // TODO: use PixelFormatExtensions to get block size for a format and handle different astc + uint32 nbw = Math::Max(1, Math::DivideAndRoundUp(width, blockSize)); + uint32 nbh = Math::Max(1, Math::DivideAndRoundUp(height, blockSize)); + rowPitch = nbw * 16; + slicePitch = rowPitch * nbh; + } + break; case PixelFormat::R8G8_B8G8_UNorm: case PixelFormat::G8R8_G8B8_UNorm: ASSERT(PixelFormatExtensions::IsPacked(format)); rowPitch = ((width + 1) >> 1) * 4; slicePitch = rowPitch * height; break; - default: ASSERT(PixelFormatExtensions::IsValid(format)); ASSERT(!PixelFormatExtensions::IsCompressed(format) && !PixelFormatExtensions::IsPacked(format) && !PixelFormatExtensions::IsPlanar(format)); diff --git a/Source/Engine/Tools/TextureTool/TextureTool.Build.cs b/Source/Engine/Tools/TextureTool/TextureTool.Build.cs index f2e675038..005ca9c96 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.Build.cs +++ b/Source/Engine/Tools/TextureTool/TextureTool.Build.cs @@ -58,6 +58,11 @@ public class TextureTool : EngineModule options.PrivateDependencies.Add("bc7enc16"); } } + if (options.Target.IsEditor) + { + options.SourceFiles.Add(Path.Combine(FolderPath, "TextureTool.astc.cpp")); + options.PrivateDependencies.Add("astc"); + } options.PublicDefinitions.Add("COMPILE_WITH_TEXTURE_TOOL"); } diff --git a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp index dc5b0be49..c8febf0f8 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp @@ -924,6 +924,10 @@ bool TextureTool::ConvertDirectXTex(TextureData& dst, const TextureData& src, co return true; } } + else if (PixelFormatExtensions::IsCompressedASTC(dstFormat)) + { + todo_astc_compression_on_windows; + } // Check if convert data else if (inImage->GetMetadata().format != dstFormatDxgi) { diff --git a/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp new file mode 100644 index 000000000..21f4f0679 --- /dev/null +++ b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#if COMPILE_WITH_TEXTURE_TOOL && COMPILE_WITH_ASTC + +#include "TextureTool.h" +#include "Engine/Core/Log.h" +#include "Engine/Core/Math/Math.h" +#include "Engine/Core/Math/Color32.h" +#include "Engine/Graphics/Textures/TextureData.h" +#include "Engine/Graphics/PixelFormatExtensions.h" +#include + +bool TextureTool::ConvertAstc(TextureData& dst, const TextureData& src, const PixelFormat dstFormat) +{ + int32 blockSize, bytesPerBlock = 16; + // TODO: use block size from PixelFormatExtensions + switch (dstFormat) + { + case PixelFormat::ASTC_4x4_UNorm: + case PixelFormat::ASTC_4x4_UNorm_sRGB: + blockSize = 4; + break; + default: + LOG(Warning, "Cannot compress image. Unsupported format {0}", static_cast(dstFormat)); + return true; + } + + // Configure the compressor run + const bool isSRGB = PixelFormatExtensions::IsSRGB(dstFormat); + const bool isHDR = PixelFormatExtensions::IsHDR(src.Format); + astcenc_profile astcProfile = isHDR ? ASTCENC_PRF_HDR_RGB_LDR_A : (isSRGB ? ASTCENC_PRF_LDR_SRGB : ASTCENC_PRF_LDR); + float astcQuality = ASTCENC_PRE_MEDIUM; + unsigned int astcFlags = 0; // TODO: add custom flags support for converter to handle ASTCENC_FLG_MAP_NORMAL + astcenc_config astcConfig; + astcenc_error astcError = astcenc_config_init(astcProfile, blockSize, blockSize, 1, astcQuality, astcFlags, &astcConfig); + if (astcError != ASTCENC_SUCCESS) + { + LOG(Warning, "Cannot compress image. ASTC failed with error: {}", String(astcenc_get_error_string(astcError))); + return true; + } + astcenc_swizzle astcSwizzle = { ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A }; + + // Allocate working state given config and thread_count + astcenc_context* astcContext; + astcError = astcenc_context_alloc(&astcConfig, 1, &astcContext); + if (astcError != ASTCENC_SUCCESS) + { + LOG(Warning, "Cannot compress image. ASTC failed with error: {}", String(astcenc_get_error_string(astcError))); + return true; + } + + // When converting from non-sRGB to sRGB we need to change the color-space manually (otherwise image is dark) + TextureData const* textureData = &src; + TextureData converted; + if (PixelFormatExtensions::IsSRGB(src.Format) != isSRGB) + { + converted = src; + Function transform = [](Color& c) + { + c = Color::LinearToSrgb(c); + }; + if (!TextureTool::Transform(converted, transform)) + { + textureData = &converted; + } + } + + // Compress all array slices + for (int32 arrayIndex = 0; arrayIndex < textureData->Items.Count() && astcError == ASTCENC_SUCCESS; arrayIndex++) + { + const auto& srcSlice = textureData->Items[arrayIndex]; + auto& dstSlice = dst.Items[arrayIndex]; + auto mipLevels = srcSlice.Mips.Count(); + dstSlice.Mips.Resize(mipLevels, false); + + // Compress all mip levels + for (int32 mipIndex = 0; mipIndex < mipLevels && astcError == ASTCENC_SUCCESS; mipIndex++) + { + const auto& srcMip = srcSlice.Mips[mipIndex]; + // TODO: validate if source mip data is row and pitch aligned (astcenc_image operates on whole pixel count withotu a slack) + auto& dstMip = dstSlice.Mips[mipIndex]; + auto mipWidth = Math::Max(textureData->Width >> mipIndex, 1); + auto mipHeight = Math::Max(textureData->Height >> mipIndex, 1); + auto blocksWidth = Math::Max(Math::DivideAndRoundUp(mipWidth, blockSize), 1); + auto blocksHeight = Math::Max(Math::DivideAndRoundUp(mipHeight, blockSize), 1); + + // Allocate memory + dstMip.RowPitch = blocksWidth * bytesPerBlock; + dstMip.DepthPitch = dstMip.RowPitch * blocksHeight; + dstMip.Lines = blocksHeight; + dstMip.Data.Allocate(dstMip.DepthPitch); + + // Compress image + astcenc_image astcInput; + astcInput.dim_x = mipWidth; + astcInput.dim_y = mipHeight; + astcInput.dim_z = 1; + astcInput.data_type = isHDR ? ASTCENC_TYPE_F16 : ASTCENC_TYPE_U8; + void* srcData = (void*)srcMip.Data.Get(); + astcInput.data = &srcData; + astcError = astcenc_compress_image(astcContext, &astcInput, &astcSwizzle, dstMip.Data.Get(), dstMip.Data.Length(), 0); + if (astcError == ASTCENC_SUCCESS) + astcError = astcenc_compress_reset(astcContext); + } + } + + // Clean up + if (astcError != ASTCENC_SUCCESS) + { + LOG(Warning, "Cannot compress image. ASTC failed with error: {}", String(astcenc_get_error_string(astcError))); + return true; + } + astcenc_context_free(astcContext); + return astcError != ASTCENC_SUCCESS; +} + +#endif \ No newline at end of file diff --git a/Source/Engine/Tools/TextureTool/TextureTool.cpp b/Source/Engine/Tools/TextureTool/TextureTool.cpp index c64134e46..c1257df55 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.cpp @@ -727,4 +727,30 @@ bool TextureTool::GetImageType(const StringView& path, ImageType& type) return false; } +bool TextureTool::Transform(TextureData& texture, const Function& transformation) +{ + auto sampler = TextureTool::GetSampler(texture.Format); + if (!sampler) + return true; + for (auto& slice : texture.Items) + { + for (int32 mipIndex = 0; mipIndex < slice.Mips.Count(); mipIndex++) + { + auto& mip = slice.Mips[mipIndex]; + auto mipWidth = Math::Max(texture.Width >> mipIndex, 1); + auto mipHeight = Math::Max(texture.Height >> mipIndex, 1); + for (int32 y = 0; y < mipHeight; y++) + { + for (int32 x = 0; x < mipWidth; x++) + { + Color color = TextureTool::SamplePoint(sampler, x, y, mip.Data.Get(), mip.RowPitch); + transformation(color); + TextureTool::Store(sampler, x, y, mip.Data.Get(), mip.RowPitch, color); + } + } + } + } + return false; +} + #endif diff --git a/Source/Engine/Tools/TextureTool/TextureTool.h b/Source/Engine/Tools/TextureTool/TextureTool.h index 351167b3c..061fc2466 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.h +++ b/Source/Engine/Tools/TextureTool/TextureTool.h @@ -256,6 +256,7 @@ private: }; static bool GetImageType(const StringView& path, ImageType& type); + static bool Transform(TextureData& texture, const Function& transformation); #if COMPILE_WITH_DIRECTXTEX static bool ExportTextureDirectXTex(ImageType type, const StringView& path, const TextureData& textureData); @@ -272,6 +273,9 @@ private: static bool ResizeStb(PixelFormat format, TextureMipData& dstMip, const TextureMipData& srcMip, int32 dstMipWidth, int32 dstMipHeight); static bool ResizeStb(TextureData& dst, const TextureData& src, int32 dstWidth, int32 dstHeight); #endif +#if COMPILE_WITH_ASTC + static bool ConvertAstc(TextureData& dst, const TextureData& src, const PixelFormat dstFormat); +#endif }; #endif diff --git a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp index 9fd7f554b..00d1536f9 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp @@ -562,7 +562,7 @@ bool TextureTool::ConvertStb(TextureData& dst, const TextureData& src, const Pix } #if USE_EDITOR - if (PixelFormatExtensions::IsCompressed(dstFormat)) + if (PixelFormatExtensions::IsCompressedBC(dstFormat)) { int32 bytesPerBlock; switch (dstFormat) @@ -663,6 +663,15 @@ bool TextureTool::ConvertStb(TextureData& dst, const TextureData& src, const Pix } } } + else if (PixelFormatExtensions::IsCompressedASTC(dstFormat)) + { +#if COMPILE_WITH_ASTC + if (ConvertAstc(dst, src, dstFormat)) +#endif + { + return true; + } + } else #endif { diff --git a/Source/Platforms/Mac/Binaries/ThirdParty/ARM64/libastcenc.a b/Source/Platforms/Mac/Binaries/ThirdParty/ARM64/libastcenc.a new file mode 100644 index 000000000..7d5b9c24c --- /dev/null +++ b/Source/Platforms/Mac/Binaries/ThirdParty/ARM64/libastcenc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43c102505dd86c26284896d3cb63f15feeec2f48b554a8b7e2b6dea7c0742e75 +size 2025552 diff --git a/Source/Platforms/Mac/Binaries/ThirdParty/x64/libastcenc.a b/Source/Platforms/Mac/Binaries/ThirdParty/x64/libastcenc.a new file mode 100644 index 000000000..f492c80cf --- /dev/null +++ b/Source/Platforms/Mac/Binaries/ThirdParty/x64/libastcenc.a @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ef0defa218e369da6d680e7753be1831b8317fbc8b8ad5c709155c5a632fe74 +size 2106072 diff --git a/Source/ThirdParty/astc/LICENSE.txt b/Source/ThirdParty/astc/LICENSE.txt new file mode 100644 index 000000000..b82735a31 --- /dev/null +++ b/Source/ThirdParty/astc/LICENSE.txt @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/Source/ThirdParty/astc/astc.Build.cs b/Source/ThirdParty/astc/astc.Build.cs new file mode 100644 index 000000000..e885c9150 --- /dev/null +++ b/Source/ThirdParty/astc/astc.Build.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System.IO; +using Flax.Build; +using Flax.Build.NativeCpp; + +/// +/// https://github.com/ARM-software/astc-encoder +/// +public class astc : ThirdPartyModule +{ + /// + public override void Init() + { + base.Init(); + + LicenseType = LicenseTypes.Apache2; + LicenseFilePath = "LICENSE.txt"; + + // Merge third-party modules into engine binary + BinaryModuleName = "FlaxEngine"; + } + + /// + public override void Setup(BuildOptions options) + { + base.Setup(options); + + options.PublicDefinitions.Add("COMPILE_WITH_ASTC"); + var depsRoot = options.DepsFolder; + switch (options.Platform.Target) + { + case TargetPlatform.Mac: + options.OutputFiles.Add(Path.Combine(depsRoot, "libastcenc.a")); + break; + default: throw new InvalidPlatformException(options.Platform.Target); + } + } +} diff --git a/Source/ThirdParty/astc/astcenc.h b/Source/ThirdParty/astc/astcenc.h new file mode 100644 index 000000000..c6c8c14a2 --- /dev/null +++ b/Source/ThirdParty/astc/astcenc.h @@ -0,0 +1,829 @@ +// SPDX-License-Identifier: Apache-2.0 +// ---------------------------------------------------------------------------- +// Copyright 2020-2023 Arm Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// ---------------------------------------------------------------------------- + +/** + * @brief The core astcenc codec library interface. + * + * This interface is the entry point to the core astcenc codec. It aims to be easy to use for + * non-experts, but also to allow experts to have fine control over the compressor heuristics if + * needed. The core codec only handles compression and decompression, transferring all inputs and + * outputs via memory buffers. To catch obvious input/output buffer sizing issues, which can cause + * security and stability problems, all transfer buffers are explicitly sized. + * + * While the aim is that we keep this interface mostly stable, it should be viewed as a mutable + * interface tied to a specific source version. We are not trying to maintain backwards + * compatibility across codec versions. + * + * The API state management is based around an explicit context object, which is the context for all + * allocated memory resources needed to compress and decompress a single image. A context can be + * used to sequentially compress multiple images using the same configuration, allowing setup + * overheads to be amortized over multiple images, which is particularly important when images are + * small. + * + * Multi-threading can be used two ways. + * + * * An application wishing to process multiple images in parallel can allocate multiple + * contexts and assign each context to a thread. + * * An application wishing to process a single image in using multiple threads can configure + * contexts for multi-threaded use, and invoke astcenc_compress/decompress() once per thread + * for faster processing. The caller is responsible for creating the worker threads, and + * synchronizing between images. + * + * Extended instruction set support + * ================================ + * + * This library supports use of extended instruction sets, such as SSE4.1 and AVX2. These are + * enabled at compile time when building the library. There is no runtime checking in the core + * library that the instruction sets used are actually available. Checking compatibility is the + * responsibility of the calling code. + * + * Threading + * ========= + * + * In pseudo-code, the usage for manual user threading looks like this: + * + * // Configure the compressor run + * astcenc_config my_config; + * astcenc_config_init(..., &my_config); + * + * // Power users can tweak settings here ... + * + * // Allocate working state given config and thread_count + * astcenc_context* my_context; + * astcenc_context_alloc(&my_config, thread_count, &my_context); + * + * // Compress each image using these config settings + * foreach image: + * // For each thread in the thread pool + * for i in range(0, thread_count): + * astcenc_compress_image(my_context, &my_input, my_output, i); + * + * astcenc_compress_reset(my_context); + * + * // Clean up + * astcenc_context_free(my_context); + * + * Images + * ====== + * + * The codec supports compressing single images, which can be either 2D images or volumetric 3D + * images. Calling code is responsible for any handling of aggregate types, such as mipmap chains, + * texture arrays, or sliced 3D textures. + * + * Images are passed in as an astcenc_image structure. Inputs can be either 8-bit unorm, 16-bit + * half-float, or 32-bit float, as indicated by the data_type field. + * + * Images can be any dimension; there is no requirement to be a multiple of the ASTC block size. + * + * Data is always passed in as 4 color components, and accessed as an array of 2D image slices. Data + * within an image slice is always tightly packed without padding. Addressing looks like this: + * + * data[z_coord][y_coord * x_dim * 4 + x_coord * 4 ] // Red + * data[z_coord][y_coord * x_dim * 4 + x_coord * 4 + 1] // Green + * data[z_coord][y_coord * x_dim * 4 + x_coord * 4 + 2] // Blue + * data[z_coord][y_coord * x_dim * 4 + x_coord * 4 + 3] // Alpha + * + * Common compressor usage + * ======================= + * + * One of the most important things for coding image quality is to align the input data component + * count with the ASTC color endpoint mode. This avoids wasting bits encoding components you don't + * actually need in the endpoint colors. + * + * | Input data | Encoding swizzle | Sampling swizzle | + * | ------------ | ---------------- | ---------------- | + * | 1 component | RRR1 | .[rgb] | + * | 2 components | RRRG | .[rgb]a | + * | 3 components | RGB1 | .rgb | + * | 4 components | RGBA | .rgba | + * + * The 1 and 2 component modes recommend sampling from "g" to recover the luminance value as this + * provide best compatibility with other texture formats where the green component may be stored at + * higher precision than the others, such as RGB565. For ASTC any of the RGB components can be used; + * the luminance endpoint component will be returned for all three. + * + * When using the normal map compression mode ASTC will store normals as a two component X+Y map. + * Input images must contain unit-length normalized and should be passed in using a two component + * swizzle. The astcenc command line tool defaults to an RRRG swizzle, but some developers prefer + * to use GGGR for compatability with BC5n which will work just as well. The Z component can be + * recovered programmatically in shader code, using knowledge that the vector is unit length and + * that Z must be positive for a tangent-space normal map. + * + * Decompress-only usage + * ===================== + * + * For some use cases it is useful to have a cut-down context and/or library which supports + * decompression but not compression. + * + * A context can be made decompress-only using the ASTCENC_FLG_DECOMPRESS_ONLY flag when the context + * is allocated. These contexts have lower dynamic memory footprint than a full context. + * + * The entire library can be made decompress-only by building the files with the define + * ASTCENC_DECOMPRESS_ONLY set. In this build the context will be smaller, and the library will + * exclude the functionality which is only needed for compression. This reduces the binary size by + * ~180KB. For these builds contexts must be created with the ASTCENC_FLG_DECOMPRESS_ONLY flag. + * + * Note that context structures returned by a library built as decompress-only are incompatible with + * a library built with compression included, and visa versa, as they have different sizes and + * memory layout. + * + * Self-decompress-only usage + * ========================== + * + * ASTC is a complex format with a large search space. The parts of this search space that are + * searched is determined by heuristics that are, in part, tied to the quality level used when + * creating the context. + * + * A normal context is capable of decompressing any ASTC texture, including those generated by other + * compressors with unknown heuristics. This is the most flexible implementation, but forces the + * data tables used by the codec to include entries that are not needed during compression. This + * can slow down context creation by a significant amount, especially for the faster compression + * modes where few data table entries are actually used. To optimize this use case the context can + * be created with the ASTCENC_FLG_SELF_DECOMPRESS_ONLY flag. This tells the compressor that it will + * only be asked to decompress images that it compressed itself, allowing the data tables to + * exclude entries that are not needed by the current compression configuration. This reduces the + * size of the context data tables in memory and improves context creation performance. Note that, + * as of the 3.6 release, this flag no longer affects compression performance. + * + * Using this flag while attempting to decompress an valid image which was created by another + * compressor, or even another astcenc compressor version or configuration, may result in blocks + * returning as solid magenta or NaN value error blocks. + */ + +#ifndef ASTCENC_INCLUDED +#define ASTCENC_INCLUDED + +#include +#include + +#if defined(ASTCENC_DYNAMIC_LIBRARY) + #if defined(_MSC_VER) + #define ASTCENC_PUBLIC extern "C" __declspec(dllexport) + #else + #define ASTCENC_PUBLIC extern "C" __attribute__ ((visibility ("default"))) + #endif +#else + #define ASTCENC_PUBLIC +#endif + +/* ============================================================================ + Data declarations +============================================================================ */ + +/** + * @brief An opaque structure; see astcenc_internal.h for definition. + */ +struct astcenc_context; + +/** + * @brief A codec API error code. + */ +enum astcenc_error { + /** @brief The call was successful. */ + ASTCENC_SUCCESS = 0, + /** @brief The call failed due to low memory, or undersized I/O buffers. */ + ASTCENC_ERR_OUT_OF_MEM, + /** @brief The call failed due to the build using fast math. */ + ASTCENC_ERR_BAD_CPU_FLOAT, + /** @brief The call failed due to an out-of-spec parameter. */ + ASTCENC_ERR_BAD_PARAM, + /** @brief The call failed due to an out-of-spec block size. */ + ASTCENC_ERR_BAD_BLOCK_SIZE, + /** @brief The call failed due to an out-of-spec color profile. */ + ASTCENC_ERR_BAD_PROFILE, + /** @brief The call failed due to an out-of-spec quality value. */ + ASTCENC_ERR_BAD_QUALITY, + /** @brief The call failed due to an out-of-spec component swizzle. */ + ASTCENC_ERR_BAD_SWIZZLE, + /** @brief The call failed due to an out-of-spec flag set. */ + ASTCENC_ERR_BAD_FLAGS, + /** @brief The call failed due to the context not supporting the operation. */ + ASTCENC_ERR_BAD_CONTEXT, + /** @brief The call failed due to unimplemented functionality. */ + ASTCENC_ERR_NOT_IMPLEMENTED, +#if defined(ASTCENC_DIAGNOSTICS) + /** @brief The call failed due to an issue with diagnostic tracing. */ + ASTCENC_ERR_DTRACE_FAILURE, +#endif +}; + +/** + * @brief A codec color profile. + */ +enum astcenc_profile { + /** @brief The LDR sRGB color profile. */ + ASTCENC_PRF_LDR_SRGB = 0, + /** @brief The LDR linear color profile. */ + ASTCENC_PRF_LDR, + /** @brief The HDR RGB with LDR alpha color profile. */ + ASTCENC_PRF_HDR_RGB_LDR_A, + /** @brief The HDR RGBA color profile. */ + ASTCENC_PRF_HDR +}; + +/** @brief The fastest, lowest quality, search preset. */ +static const float ASTCENC_PRE_FASTEST = 0.0f; + +/** @brief The fast search preset. */ +static const float ASTCENC_PRE_FAST = 10.0f; + +/** @brief The medium quality search preset. */ +static const float ASTCENC_PRE_MEDIUM = 60.0f; + +/** @brief The thorough quality search preset. */ +static const float ASTCENC_PRE_THOROUGH = 98.0f; + +/** @brief The thorough quality search preset. */ +static const float ASTCENC_PRE_VERYTHOROUGH = 99.0f; + +/** @brief The exhaustive, highest quality, search preset. */ +static const float ASTCENC_PRE_EXHAUSTIVE = 100.0f; + +/** + * @brief A codec component swizzle selector. + */ +enum astcenc_swz +{ + /** @brief Select the red component. */ + ASTCENC_SWZ_R = 0, + /** @brief Select the green component. */ + ASTCENC_SWZ_G = 1, + /** @brief Select the blue component. */ + ASTCENC_SWZ_B = 2, + /** @brief Select the alpha component. */ + ASTCENC_SWZ_A = 3, + /** @brief Use a constant zero component. */ + ASTCENC_SWZ_0 = 4, + /** @brief Use a constant one component. */ + ASTCENC_SWZ_1 = 5, + /** @brief Use a reconstructed normal vector Z component. */ + ASTCENC_SWZ_Z = 6 +}; + +/** + * @brief A texel component swizzle. + */ +struct astcenc_swizzle +{ + /** @brief The red component selector. */ + astcenc_swz r; + /** @brief The green component selector. */ + astcenc_swz g; + /** @brief The blue component selector. */ + astcenc_swz b; + /** @brief The alpha component selector. */ + astcenc_swz a; +}; + +/** + * @brief A texel component data format. + */ +enum astcenc_type +{ + /** @brief Unorm 8-bit data per component. */ + ASTCENC_TYPE_U8 = 0, + /** @brief 16-bit float per component. */ + ASTCENC_TYPE_F16 = 1, + /** @brief 32-bit float per component. */ + ASTCENC_TYPE_F32 = 2 +}; + +/** + * @brief Enable normal map compression. + * + * Input data will be treated a two component normal map, storing X and Y, and the codec will + * optimize for angular error rather than simple linear PSNR. In this mode the input swizzle should + * be e.g. rrrg (the default ordering for ASTC normals on the command line) or gggr (the ordering + * used by BC5n). + */ +static const unsigned int ASTCENC_FLG_MAP_NORMAL = 1 << 0; + +/** + * @brief Enable alpha weighting. + * + * The input alpha value is used for transparency, so errors in the RGB components are weighted by + * the transparency level. This allows the codec to more accurately encode the alpha value in areas + * where the color value is less significant. + */ +static const unsigned int ASTCENC_FLG_USE_ALPHA_WEIGHT = 1 << 2; + +/** + * @brief Enable perceptual error metrics. + * + * This mode enables perceptual compression mode, which will optimize for perceptual error rather + * than best PSNR. Only some input modes support perceptual error metrics. + */ +static const unsigned int ASTCENC_FLG_USE_PERCEPTUAL = 1 << 3; + +/** + * @brief Create a decompression-only context. + * + * This mode disables support for compression. This enables context allocation to skip some + * transient buffer allocation, resulting in lower memory usage. + */ +static const unsigned int ASTCENC_FLG_DECOMPRESS_ONLY = 1 << 4; + +/** + * @brief Create a self-decompression context. + * + * This mode configures the compressor so that it is only guaranteed to be able to decompress images + * that were actually created using the current context. This is the common case for compression use + * cases, and setting this flag enables additional optimizations, but does mean that the context + * cannot reliably decompress arbitrary ASTC images. + */ +static const unsigned int ASTCENC_FLG_SELF_DECOMPRESS_ONLY = 1 << 5; + +/** + * @brief Enable RGBM map compression. + * + * Input data will be treated as HDR data that has been stored in an LDR RGBM-encoded wrapper + * format. Data must be preprocessed by the user to be in LDR RGBM format before calling the + * compression function, this flag is only used to control the use of RGBM-specific heuristics and + * error metrics. + * + * IMPORTANT: The ASTC format is prone to bad failure modes with unconstrained RGBM data; very small + * M values can round to zero due to quantization and result in black or white pixels. It is highly + * recommended that the minimum value of M used in the encoding is kept above a lower threshold (try + * 16 or 32). Applying this threshold reduces the number of very dark colors that can be + * represented, but is still higher precision than 8-bit LDR. + * + * When this flag is set the value of @c rgbm_m_scale in the context must be set to the RGBM scale + * factor used during reconstruction. This defaults to 5 when in RGBM mode. + * + * It is recommended that the value of @c cw_a_weight is set to twice the value of the multiplier + * scale, ensuring that the M value is accurately encoded. This defaults to 10 when in RGBM mode, + * matching the default scale factor. + */ +static const unsigned int ASTCENC_FLG_MAP_RGBM = 1 << 6; + +/** + * @brief The bit mask of all valid flags. + */ +static const unsigned int ASTCENC_ALL_FLAGS = + ASTCENC_FLG_MAP_NORMAL | + ASTCENC_FLG_MAP_RGBM | + ASTCENC_FLG_USE_ALPHA_WEIGHT | + ASTCENC_FLG_USE_PERCEPTUAL | + ASTCENC_FLG_DECOMPRESS_ONLY | + ASTCENC_FLG_SELF_DECOMPRESS_ONLY; + +/** + * @brief The config structure. + * + * This structure will initially be populated by a call to astcenc_config_init, but power users may + * modify it before calling astcenc_context_alloc. See astcenccli_toplevel_help.cpp for full user + * documentation of the power-user settings. + * + * Note for any settings which are associated with a specific color component, the value in the + * config applies to the component that exists after any compression data swizzle is applied. + */ +struct astcenc_config +{ + /** @brief The color profile. */ + astcenc_profile profile; + + /** @brief The set of set flags. */ + unsigned int flags; + + /** @brief The ASTC block size X dimension. */ + unsigned int block_x; + + /** @brief The ASTC block size Y dimension. */ + unsigned int block_y; + + /** @brief The ASTC block size Z dimension. */ + unsigned int block_z; + + /** @brief The red component weight scale for error weighting (-cw). */ + float cw_r_weight; + + /** @brief The green component weight scale for error weighting (-cw). */ + float cw_g_weight; + + /** @brief The blue component weight scale for error weighting (-cw). */ + float cw_b_weight; + + /** @brief The alpha component weight scale for error weighting (-cw). */ + float cw_a_weight; + + /** + * @brief The radius for any alpha-weight scaling (-a). + * + * It is recommended that this is set to 1 when using FLG_USE_ALPHA_WEIGHT on a texture that + * will be sampled using linear texture filtering to minimize color bleed out of transparent + * texels that are adjacent to non-transparent texels. + */ + unsigned int a_scale_radius; + + /** @brief The RGBM scale factor for the shared multiplier (-rgbm). */ + float rgbm_m_scale; + + /** + * @brief The maximum number of partitions searched (-partitioncountlimit). + * + * Valid values are between 1 and 4. + */ + unsigned int tune_partition_count_limit; + + /** + * @brief The maximum number of partitions searched (-2partitionindexlimit). + * + * Valid values are between 1 and 1024. + */ + unsigned int tune_2partition_index_limit; + + /** + * @brief The maximum number of partitions searched (-3partitionindexlimit). + * + * Valid values are between 1 and 1024. + */ + unsigned int tune_3partition_index_limit; + + /** + * @brief The maximum number of partitions searched (-4partitionindexlimit). + * + * Valid values are between 1 and 1024. + */ + unsigned int tune_4partition_index_limit; + + /** + * @brief The maximum centile for block modes searched (-blockmodelimit). + * + * Valid values are between 1 and 100. + */ + unsigned int tune_block_mode_limit; + + /** + * @brief The maximum iterative refinements applied (-refinementlimit). + * + * Valid values are between 1 and N; there is no technical upper limit + * but little benefit is expected after N=4. + */ + unsigned int tune_refinement_limit; + + /** + * @brief The number of trial candidates per mode search (-candidatelimit). + * + * Valid values are between 1 and TUNE_MAX_TRIAL_CANDIDATES. + */ + unsigned int tune_candidate_limit; + + /** + * @brief The number of trial partitionings per search (-2partitioncandidatelimit). + * + * Valid values are between 1 and TUNE_MAX_PARTITIONING_CANDIDATES. + */ + unsigned int tune_2partitioning_candidate_limit; + + /** + * @brief The number of trial partitionings per search (-3partitioncandidatelimit). + * + * Valid values are between 1 and TUNE_MAX_PARTITIONING_CANDIDATES. + */ + unsigned int tune_3partitioning_candidate_limit; + + /** + * @brief The number of trial partitionings per search (-4partitioncandidatelimit). + * + * Valid values are between 1 and TUNE_MAX_PARTITIONING_CANDIDATES. + */ + unsigned int tune_4partitioning_candidate_limit; + + /** + * @brief The dB threshold for stopping block search (-dblimit). + * + * This option is ineffective for HDR textures. + */ + float tune_db_limit; + + /** + * @brief The amount of MSE overshoot needed to early-out trials. + * + * The first early-out is for 1 partition, 1 plane trials, where we try a minimal encode using + * the high probability block modes. This can short-cut compression for simple blocks. + * + * The second early-out is for refinement trials, where we can exit refinement once quality is + * reached. + */ + float tune_mse_overshoot; + + /** + * @brief The threshold for skipping 3.1/4.1 trials (-2partitionlimitfactor). + * + * This option is further scaled for normal maps, so it skips less often. + */ + float tune_2partition_early_out_limit_factor; + + /** + * @brief The threshold for skipping 4.1 trials (-3partitionlimitfactor). + * + * This option is further scaled for normal maps, so it skips less often. + */ + float tune_3partition_early_out_limit_factor; + + /** + * @brief The threshold for skipping two weight planes (-2planelimitcorrelation). + * + * This option is ineffective for normal maps. + */ + float tune_2plane_early_out_limit_correlation; + + /** + * @brief The config enable for the mode0 fast-path search. + * + * If this is set to TUNE_MIN_TEXELS_MODE0 or higher then the early-out fast mode0 + * search is enabled. This option is ineffective for 3D block sizes. + */ + float tune_search_mode0_enable; + +#if defined(ASTCENC_DIAGNOSTICS) + /** + * @brief The path to save the diagnostic trace data to. + * + * This option is not part of the public API, and requires special builds + * of the library. + */ + const char* trace_file_path; +#endif +}; + +/** + * @brief An uncompressed 2D or 3D image. + * + * 3D image are passed in as an array of 2D slices. Each slice has identical + * size and color format. + */ +struct astcenc_image +{ + /** @brief The X dimension of the image, in texels. */ + unsigned int dim_x; + + /** @brief The Y dimension of the image, in texels. */ + unsigned int dim_y; + + /** @brief The Z dimension of the image, in texels. */ + unsigned int dim_z; + + /** @brief The data type per component. */ + astcenc_type data_type; + + /** @brief The array of 2D slices, of length @c dim_z. */ + void** data; +}; + +/** + * @brief A block encoding metadata query result. + * + * If the block is an error block or a constant color block or an error block all fields other than + * the profile, block dimensions, and error/constant indicator will be zero. + */ +struct astcenc_block_info +{ + /** @brief The block encoding color profile. */ + astcenc_profile profile; + + /** @brief The number of texels in the X dimension. */ + unsigned int block_x; + + /** @brief The number of texels in the Y dimension. */ + unsigned int block_y; + + /** @brief The number of texel in the Z dimension. */ + unsigned int block_z; + + /** @brief The number of texels in the block. */ + unsigned int texel_count; + + /** @brief True if this block is an error block. */ + bool is_error_block; + + /** @brief True if this block is a constant color block. */ + bool is_constant_block; + + /** @brief True if this block is an HDR block. */ + bool is_hdr_block; + + /** @brief True if this block uses two weight planes. */ + bool is_dual_plane_block; + + /** @brief The number of partitions if not constant color. */ + unsigned int partition_count; + + /** @brief The partition index if 2 - 4 partitions used. */ + unsigned int partition_index; + + /** @brief The component index of the second plane if dual plane. */ + unsigned int dual_plane_component; + + /** @brief The color endpoint encoding mode for each partition. */ + unsigned int color_endpoint_modes[4]; + + /** @brief The number of color endpoint quantization levels. */ + unsigned int color_level_count; + + /** @brief The number of weight quantization levels. */ + unsigned int weight_level_count; + + /** @brief The number of weights in the X dimension. */ + unsigned int weight_x; + + /** @brief The number of weights in the Y dimension. */ + unsigned int weight_y; + + /** @brief The number of weights in the Z dimension. */ + unsigned int weight_z; + + /** @brief The unpacked color endpoints for each partition. */ + float color_endpoints[4][2][4]; + + /** @brief The per-texel interpolation weights for the block. */ + float weight_values_plane1[216]; + + /** @brief The per-texel interpolation weights for the block. */ + float weight_values_plane2[216]; + + /** @brief The per-texel partition assignments for the block. */ + uint8_t partition_assignment[216]; +}; + +/** + * Populate a codec config based on default settings. + * + * Power users can edit the returned config struct to fine tune before allocating the context. + * + * @param profile Color profile. + * @param block_x ASTC block size X dimension. + * @param block_y ASTC block size Y dimension. + * @param block_z ASTC block size Z dimension. + * @param quality Search quality preset / effort level. Either an + * @c ASTCENC_PRE_* value, or a effort level between 0 + * and 100. Performance is not linear between 0 and 100. + + * @param flags A valid set of @c ASTCENC_FLG_* flag bits. + * @param[out] config Output config struct to populate. + * + * @return @c ASTCENC_SUCCESS on success, or an error if the inputs are invalid + * either individually, or in combination. + */ +ASTCENC_PUBLIC astcenc_error astcenc_config_init( + astcenc_profile profile, + unsigned int block_x, + unsigned int block_y, + unsigned int block_z, + float quality, + unsigned int flags, + astcenc_config* config); + +/** + * @brief Allocate a new codec context based on a config. + * + * This function allocates all of the memory resources and threads needed by the codec. This can be + * slow, so it is recommended that contexts are reused to serially compress or decompress multiple + * images to amortize setup cost. + * + * Contexts can be allocated to support only decompression using the @c ASTCENC_FLG_DECOMPRESS_ONLY + * flag when creating the configuration. The compression functions will fail if invoked. For a + * decompress-only library build the @c ASTCENC_FLG_DECOMPRESS_ONLY flag must be set when creating + * any context. + * + * @param[in] config Codec config. + * @param thread_count Thread count to configure for. + * @param[out] context Location to store an opaque context pointer. + * + * @return @c ASTCENC_SUCCESS on success, or an error if context creation failed. + */ +ASTCENC_PUBLIC astcenc_error astcenc_context_alloc( + const astcenc_config* config, + unsigned int thread_count, + astcenc_context** context); + +/** + * @brief Compress an image. + * + * A single context can only compress or decompress a single image at a time. + * + * For a context configured for multi-threading, any set of the N threads can call this function. + * Work will be dynamically scheduled across the threads available. Each thread must have a unique + * @c thread_index. + * + * @param context Codec context. + * @param[in,out] image An input image, in 2D slices. + * @param swizzle Compression data swizzle, applied before compression. + * @param[out] data_out Pointer to output data array. + * @param data_len Length of the output data array. + * @param thread_index Thread index [0..N-1] of calling thread. + * + * @return @c ASTCENC_SUCCESS on success, or an error if compression failed. + */ +ASTCENC_PUBLIC astcenc_error astcenc_compress_image( + astcenc_context* context, + astcenc_image* image, + const astcenc_swizzle* swizzle, + uint8_t* data_out, + size_t data_len, + unsigned int thread_index); + +/** + * @brief Reset the codec state for a new compression. + * + * The caller is responsible for synchronizing threads in the worker thread pool. This function must + * only be called when all threads have exited the @c astcenc_compress_image() function for image N, + * but before any thread enters it for image N + 1. + * + * Calling this is not required (but won't hurt), if the context is created for single threaded use. + * + * @param context Codec context. + * + * @return @c ASTCENC_SUCCESS on success, or an error if reset failed. + */ +ASTCENC_PUBLIC astcenc_error astcenc_compress_reset( + astcenc_context* context); + +/** + * @brief Decompress an image. + * + * @param context Codec context. + * @param[in] data Pointer to compressed data. + * @param data_len Length of the compressed data, in bytes. + * @param[in,out] image_out Output image. + * @param swizzle Decompression data swizzle, applied after decompression. + * @param thread_index Thread index [0..N-1] of calling thread. + * + * @return @c ASTCENC_SUCCESS on success, or an error if decompression failed. + */ +ASTCENC_PUBLIC astcenc_error astcenc_decompress_image( + astcenc_context* context, + const uint8_t* data, + size_t data_len, + astcenc_image* image_out, + const astcenc_swizzle* swizzle, + unsigned int thread_index); + +/** + * @brief Reset the codec state for a new decompression. + * + * The caller is responsible for synchronizing threads in the worker thread pool. This function must + * only be called when all threads have exited the @c astcenc_decompress_image() function for image + * N, but before any thread enters it for image N + 1. + * + * Calling this is not required (but won't hurt), if the context is created for single threaded use. + * + * @param context Codec context. + * + * @return @c ASTCENC_SUCCESS on success, or an error if reset failed. + */ +ASTCENC_PUBLIC astcenc_error astcenc_decompress_reset( + astcenc_context* context); + +/** + * Free the compressor context. + * + * @param context The codec context. + */ +ASTCENC_PUBLIC void astcenc_context_free( + astcenc_context* context); + +/** + * @brief Provide a high level summary of a block's encoding. + * + * This feature is primarily useful for codec developers but may be useful for developers building + * advanced content packaging pipelines. + * + * @param context Codec context. + * @param data One block of compressed ASTC data. + * @param info The output info structure to populate. + * + * @return @c ASTCENC_SUCCESS if the block was decoded, or an error otherwise. Note that this + * function will return success even if the block itself was an error block encoding, as the + * decode was correctly handled. + */ +ASTCENC_PUBLIC astcenc_error astcenc_get_block_info( + astcenc_context* context, + const uint8_t data[16], + astcenc_block_info* info); + +/** + * @brief Get a printable string for specific status code. + * + * @param status The status value. + * + * @return A human readable nul-terminated string. + */ +ASTCENC_PUBLIC const char* astcenc_get_error_string( + astcenc_error status); + +#endif diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs b/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs new file mode 100644 index 000000000..adbc54438 --- /dev/null +++ b/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs @@ -0,0 +1,65 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System.IO; +using Flax.Build; + +namespace Flax.Deps.Dependencies +{ + /// + /// ASTC texture format compression lib. + /// + /// + class astc : Dependency + { + /// + public override TargetPlatform[] Platforms + { + get + { + switch (BuildPlatform) + { + case TargetPlatform.Mac: + return new[] + { + TargetPlatform.Mac, + }; + default: return new TargetPlatform[0]; + } + } + } + + /// + public override void Build(BuildOptions options) + { + var root = options.IntermediateFolder; + var buildDir = Path.Combine(root, "build"); + + // Get the source + var commit = "aeece2f609db959d1c5e43e4f00bd177ea130575"; // 4.6.1 + CloneGitRepo(root, "https://github.com/ARM-software/astc-encoder.git", commit); + + foreach (var platform in options.Platforms) + { + switch (platform) + { + case TargetPlatform.Mac: + foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 }) + { + var isa = architecture == TargetArchitecture.ARM64 ? "-DASTCENC_ISA_NEON=ON" : "-DASTCENC_ISA_SSE2=ON"; + var lib = architecture == TargetArchitecture.ARM64 ? "libastcenc-neon-static.a" : "libastcenc-sse2-static.a"; + SetupDirectory(buildDir, true); + RunCmake(buildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release -DASTCENC_UNIVERSAL_BUILD=OFF -DASTCENC_UNIVERSAL_BINARY=OFF " + isa); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + var depsFolder = GetThirdPartyFolder(options, platform, architecture); + Utilities.FileCopy(Path.Combine(buildDir, "Source", lib), Path.Combine(depsFolder, "libastcenc.a")); + } + break; + } + } + + // Copy header and license + Utilities.FileCopy(Path.Combine(root, "LICENSE.txt"), Path.Combine(options.ThirdPartyFolder, "astc/LICENSE.txt")); + Utilities.FileCopy(Path.Combine(root, "Source/astcenc.h"), Path.Combine(options.ThirdPartyFolder, "astc/astcenc.h")); + } + } +} From ed6220005f87dbdb57e355fcb1e65cff1c3ebbe4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 15 Dec 2023 15:33:09 +0100 Subject: [PATCH 025/198] Fixes for astc compression usage --- .../Tools/TextureTool/TextureTool.astc.cpp | 27 ++++++++++++++----- .../Tools/TextureTool/TextureTool.stb.cpp | 2 +- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp index 21f4f0679..a175d679b 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp @@ -39,6 +39,11 @@ bool TextureTool::ConvertAstc(TextureData& dst, const TextureData& src, const Pi return true; } astcenc_swizzle astcSwizzle = { ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A }; + if (!PixelFormatExtensions::HasAlpha(src.Format)) + { + // No alpha channel in use so fill with 1 + astcSwizzle.a = ASTCENC_SWZ_1; + } // Allocate working state given config and thread_count astcenc_context* astcContext; @@ -48,21 +53,30 @@ bool TextureTool::ConvertAstc(TextureData& dst, const TextureData& src, const Pi LOG(Warning, "Cannot compress image. ASTC failed with error: {}", String(astcenc_get_error_string(astcError))); return true; } - - // When converting from non-sRGB to sRGB we need to change the color-space manually (otherwise image is dark) TextureData const* textureData = &src; TextureData converted; + + // Encoder uses full 4-component RGBA input image so convert it if needed + if (PixelFormatExtensions::ComputeComponentsCount(src.Format) != 4) + { + if (textureData != &src) + converted = src; + const PixelFormat tempFormat = isHDR ? PixelFormat::R16G16B16A16_Float : (PixelFormatExtensions::IsSRGB(src.Format) ? PixelFormat::R8G8B8A8_UNorm_sRGB : PixelFormat::R8G8B8A8_UNorm); + if (!TextureTool::Convert(converted, *textureData, tempFormat)) + textureData = &converted; + } + + // When converting from non-sRGB to sRGB we need to change the color-space manually (otherwise image is dark) if (PixelFormatExtensions::IsSRGB(src.Format) != isSRGB) { - converted = src; + if (textureData != &src) + converted = src; Function transform = [](Color& c) { c = Color::LinearToSrgb(c); }; if (!TextureTool::Transform(converted, transform)) - { textureData = &converted; - } } // Compress all array slices @@ -77,12 +91,13 @@ bool TextureTool::ConvertAstc(TextureData& dst, const TextureData& src, const Pi for (int32 mipIndex = 0; mipIndex < mipLevels && astcError == ASTCENC_SUCCESS; mipIndex++) { const auto& srcMip = srcSlice.Mips[mipIndex]; - // TODO: validate if source mip data is row and pitch aligned (astcenc_image operates on whole pixel count withotu a slack) auto& dstMip = dstSlice.Mips[mipIndex]; auto mipWidth = Math::Max(textureData->Width >> mipIndex, 1); auto mipHeight = Math::Max(textureData->Height >> mipIndex, 1); auto blocksWidth = Math::Max(Math::DivideAndRoundUp(mipWidth, blockSize), 1); auto blocksHeight = Math::Max(Math::DivideAndRoundUp(mipHeight, blockSize), 1); + ASSERT(srcMip.RowPitch == mipWidth * PixelFormatExtensions::SizeInBytes(textureData->Format)); + ASSERT(srcMip.Lines == mipHeight); // Allocate memory dstMip.RowPitch = blocksWidth * bytesPerBlock; diff --git a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp index 00d1536f9..ce10a7c61 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp @@ -666,7 +666,7 @@ bool TextureTool::ConvertStb(TextureData& dst, const TextureData& src, const Pix else if (PixelFormatExtensions::IsCompressedASTC(dstFormat)) { #if COMPILE_WITH_ASTC - if (ConvertAstc(dst, src, dstFormat)) + if (ConvertAstc(dst, *textureData, dstFormat)) #endif { return true; From 5958b2f4eafdf25cf65a3137e53aa42ef7c0bbb3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Dec 2023 09:55:17 +0100 Subject: [PATCH 026/198] Remove unused 'PixelFormatExtensions::ComputeScanlineCount' --- .../Engine/Graphics/PixelFormatExtensions.cpp | 33 ------------------- .../Engine/Graphics/PixelFormatExtensions.h | 8 ----- 2 files changed, 41 deletions(-) diff --git a/Source/Engine/Graphics/PixelFormatExtensions.cpp b/Source/Engine/Graphics/PixelFormatExtensions.cpp index 00aa76f5f..fafda87bf 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.cpp +++ b/Source/Engine/Graphics/PixelFormatExtensions.cpp @@ -537,39 +537,6 @@ bool PixelFormatExtensions::IsInteger(const PixelFormat format) } } -int32 PixelFormatExtensions::ComputeScanlineCount(const PixelFormat format, int32 height) -{ - switch (format) - { - case PixelFormat::BC1_Typeless: - case PixelFormat::BC1_UNorm: - case PixelFormat::BC1_UNorm_sRGB: - case PixelFormat::BC2_Typeless: - case PixelFormat::BC2_UNorm: - case PixelFormat::BC2_UNorm_sRGB: - case PixelFormat::BC3_Typeless: - case PixelFormat::BC3_UNorm: - case PixelFormat::BC3_UNorm_sRGB: - case PixelFormat::BC4_Typeless: - case PixelFormat::BC4_UNorm: - case PixelFormat::BC4_SNorm: - case PixelFormat::BC5_Typeless: - case PixelFormat::BC5_UNorm: - case PixelFormat::BC5_SNorm: - case PixelFormat::BC6H_Typeless: - case PixelFormat::BC6H_Uf16: - case PixelFormat::BC6H_Sf16: - case PixelFormat::BC7_Typeless: - case PixelFormat::BC7_UNorm: - case PixelFormat::BC7_UNorm_sRGB: - case PixelFormat::ASTC_4x4_UNorm: - case PixelFormat::ASTC_4x4_UNorm_sRGB: - return Math::Max(1, (height + 3) / 4); - default: - return height; - } -} - int32 PixelFormatExtensions::ComputeComponentsCount(const PixelFormat format) { switch (format) diff --git a/Source/Engine/Graphics/PixelFormatExtensions.h b/Source/Engine/Graphics/PixelFormatExtensions.h index 8739cbadc..7b793cdfd 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.h +++ b/Source/Engine/Graphics/PixelFormatExtensions.h @@ -165,14 +165,6 @@ public: /// True if given format contains integer data type (signed or unsigned), otherwise false. API_FUNCTION() static bool IsInteger(PixelFormat format); - /// - /// Computes the scanline count (number of scanlines). - /// - /// The . - /// The height. - /// The scanline count. - API_FUNCTION() static int32 ComputeScanlineCount(PixelFormat format, int32 height); - /// /// Computes the format components count (number of R, G, B, A channels). /// From c6c53baff2417b6dfc16de68664502b7840b90d7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Dec 2023 09:59:29 +0100 Subject: [PATCH 027/198] Improvements for astc format --- Source/Engine/Graphics/PixelFormatExtensions.cpp | 2 ++ Source/Engine/Graphics/RenderTools.cpp | 4 ++-- .../Engine/Tools/TextureTool/TextureTool.astc.cpp | 15 +++------------ 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/Source/Engine/Graphics/PixelFormatExtensions.cpp b/Source/Engine/Graphics/PixelFormatExtensions.cpp index fafda87bf..4431870ee 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.cpp +++ b/Source/Engine/Graphics/PixelFormatExtensions.cpp @@ -666,6 +666,8 @@ int32 PixelFormatExtensions::ComputeBlockSize(PixelFormat format) case PixelFormat::BC7_Typeless: case PixelFormat::BC7_UNorm: case PixelFormat::BC7_UNorm_sRGB: + case PixelFormat::ASTC_4x4_UNorm: + case PixelFormat::ASTC_4x4_UNorm_sRGB: return 4; default: return 1; diff --git a/Source/Engine/Graphics/RenderTools.cpp b/Source/Engine/Graphics/RenderTools.cpp index 7ffecd62c..5fc6c7d65 100644 --- a/Source/Engine/Graphics/RenderTools.cpp +++ b/Source/Engine/Graphics/RenderTools.cpp @@ -332,10 +332,10 @@ void RenderTools::ComputePitch(PixelFormat format, int32 width, int32 height, ui case PixelFormat::ASTC_4x4_UNorm: case PixelFormat::ASTC_4x4_UNorm_sRGB: { - const int32 blockSize = 4; // TODO: use PixelFormatExtensions to get block size for a format and handle different astc + const int32 blockSize = PixelFormatExtensions::ComputeBlockSize(format); uint32 nbw = Math::Max(1, Math::DivideAndRoundUp(width, blockSize)); uint32 nbh = Math::Max(1, Math::DivideAndRoundUp(height, blockSize)); - rowPitch = nbw * 16; + rowPitch = nbw * 16; // All ASTC blocks use 128 bits slicePitch = rowPitch * nbh; } break; diff --git a/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp index a175d679b..3dcd8bb72 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp @@ -12,18 +12,9 @@ bool TextureTool::ConvertAstc(TextureData& dst, const TextureData& src, const PixelFormat dstFormat) { - int32 blockSize, bytesPerBlock = 16; - // TODO: use block size from PixelFormatExtensions - switch (dstFormat) - { - case PixelFormat::ASTC_4x4_UNorm: - case PixelFormat::ASTC_4x4_UNorm_sRGB: - blockSize = 4; - break; - default: - LOG(Warning, "Cannot compress image. Unsupported format {0}", static_cast(dstFormat)); - return true; - } + ASSERT(PixelFormatExtensions::IsCompressedASTC(dstFormat)); + const int32 blockSize = PixelFormatExtensions::ComputeBlockSize(dstFormat); + const int32 bytesPerBlock = 16; // All ASTC blocks use 128 bits // Configure the compressor run const bool isSRGB = PixelFormatExtensions::IsSRGB(dstFormat); From e4df1fc7566cf87ddffa5ea682edd90fd61913a9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Dec 2023 11:07:11 +0100 Subject: [PATCH 028/198] Add support for storing custom platform tools data in Game Cooker cache --- Flax.flaxproj | 2 +- Source/Editor/Cooker/PlatformTools.h | 52 +++++++++++++++++ Source/Editor/Cooker/Steps/CookAssetsStep.cpp | 56 ++++++++++++------- Source/Editor/Cooker/Steps/CookAssetsStep.h | 19 +++---- 4 files changed, 97 insertions(+), 32 deletions(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 81a63a1fc..8b643ed8d 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 7, "Revision": 1, - "Build": 6406 + "Build": 6407 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.", diff --git a/Source/Editor/Cooker/PlatformTools.h b/Source/Editor/Cooker/PlatformTools.h index 533dad805..d7953cfd9 100644 --- a/Source/Editor/Cooker/PlatformTools.h +++ b/Source/Editor/Cooker/PlatformTools.h @@ -8,6 +8,37 @@ class TextureBase; +/// +/// The game cooker cache interface. +/// +class FLAXENGINE_API IBuildCache +{ +public: + /// + /// Removes all cached entries for assets that contain a given asset type. This forces rebuild for them. + /// + virtual void InvalidateCachePerType(const StringView& typeName) = 0; + + /// + /// Removes all cached entries for assets that contain a given asset type. This forces rebuild for them. + /// + template + FORCE_INLINE void InvalidateCachePerType() + { + InvalidateCachePerType(T::TypeName); + } + + /// + /// Removes all cached entries for assets that contain a shader. This forces rebuild for them. + /// + void InvalidateCacheShaders(); + + /// + /// Removes all cached entries for assets that contain a texture. This forces rebuild for them. + /// + void InvalidateCacheTextures(); +}; + /// /// The platform support tools base interface. /// @@ -76,6 +107,27 @@ public: virtual bool IsNativeCodeFile(CookingData& data, const String& file); public: + /// + /// Loads the build cache. Allows to invalidate any cached asset types based on the build settings for incremental builds (eg. invalidate textures/shaders). + /// + /// The cooking data. + /// The build cache interface. + /// The loaded cache data. Can be empty when starting a fresh build. + virtual void LoadCache(CookingData& data, IBuildCache* cache, const Span& bytes) + { + } + + /// + /// Saves the build cache. Allows to store any build settings to be used for cache invalidation on incremental builds. + /// + /// The cooking data. + /// The build cache interface. + /// Data to cache, will be restored during next incremental build. + virtual Array SaveCache(CookingData& data, IBuildCache* cache) + { + return Array(); + } + /// /// Called when game building starts. /// diff --git a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp index 8fd87bcfd..366a4faef 100644 --- a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp +++ b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp @@ -35,6 +35,7 @@ #include "Engine/Engine/Base/GameBase.h" #include "Engine/Engine/Globals.h" #include "Engine/Tools/TextureTool/TextureTool.h" +#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Scripting/Enums.h" #if PLATFORM_TOOLS_WINDOWS #include "Engine/Platform/Windows/WindowsPlatformSettings.h" @@ -49,6 +50,20 @@ Dictionary CookAssetsStep::AssetProcessors; +void IBuildCache::InvalidateCacheShaders() +{ + InvalidateCachePerType(); + InvalidateCachePerType(); + InvalidateCachePerType(); +} + +void IBuildCache::InvalidateCacheTextures() +{ + InvalidateCachePerType(); + InvalidateCachePerType(); + InvalidateCachePerType(); +} + bool CookAssetsStep::CacheEntry::IsValid(bool withDependencies) { AssetInfo assetInfo; @@ -113,15 +128,13 @@ void CookAssetsStep::CacheData::InvalidateCachePerType(const StringView& typeNam void CookAssetsStep::CacheData::Load(CookingData& data) { + PROFILE_CPU(); HeaderFilePath = data.CacheDirectory / String::Format(TEXT("CookedHeader_{0}.bin"), FLAXENGINE_VERSION_BUILD); CacheFolder = data.CacheDirectory / TEXT("Cooked"); Entries.Clear(); if (!FileSystem::DirectoryExists(CacheFolder)) - { FileSystem::CreateDirectory(CacheFolder); - } - if (!FileSystem::FileExists(HeaderFilePath)) { LOG(Warning, "Missing incremental build cooking assets cache."); @@ -143,9 +156,7 @@ void CookAssetsStep::CacheData::Load(CookingData& data) return; LOG(Info, "Loading incremental build cooking cache (entries count: {0})", entriesCount); - file->ReadBytes(&Settings, sizeof(Settings)); - Entries.EnsureCapacity(Math::RoundUpToPowerOf2(static_cast(entriesCount * 3.0f))); Array> fileDependencies; @@ -179,6 +190,9 @@ void CookAssetsStep::CacheData::Load(CookingData& data) e.FileDependencies = fileDependencies; } + Array platformCache; + file->Read(platformCache); + int32 checkChar; file->ReadInt32(&checkChar); if (checkChar != 13) @@ -187,6 +201,9 @@ void CookAssetsStep::CacheData::Load(CookingData& data) Entries.Clear(); } + // Per-platform custom data loading (eg. to invalidate textures/shaders options) + data.Tools->LoadCache(data, this, ToSpan(platformCache)); + const auto buildSettings = BuildSettings::Get(); const auto gameSettings = GameSettings::Get(); @@ -200,12 +217,12 @@ void CookAssetsStep::CacheData::Load(CookingData& data) if (MATERIAL_GRAPH_VERSION != Settings.Global.MaterialGraphVersion) { LOG(Info, "{0} option has been modified.", TEXT("MaterialGraphVersion")); - InvalidateCachePerType(Material::TypeName); + InvalidateCachePerType(); } if (PARTICLE_GPU_GRAPH_VERSION != Settings.Global.ParticleGraphVersion) { LOG(Info, "{0} option has been modified.", TEXT("ParticleGraphVersion")); - InvalidateCachePerType(ParticleEmitter::TypeName); + InvalidateCachePerType(); } if (buildSettings->ShadersNoOptimize != Settings.Global.ShadersNoOptimize) { @@ -262,24 +279,24 @@ void CookAssetsStep::CacheData::Load(CookingData& data) #endif if (invalidateShaders) { - InvalidateCachePerType(Shader::TypeName); - InvalidateCachePerType(Material::TypeName); - InvalidateCachePerType(ParticleEmitter::TypeName); + InvalidateCachePerType(); + InvalidateCachePerType(); + InvalidateCachePerType(); } // Invalidate textures if streaming settings gets modified if (Settings.Global.StreamingSettingsAssetId != gameSettings->Streaming || (Entries.ContainsKey(gameSettings->Streaming) && !Entries[gameSettings->Streaming].IsValid())) { - InvalidateCachePerType(Texture::TypeName); - InvalidateCachePerType(CubeTexture::TypeName); - InvalidateCachePerType(SpriteAtlas::TypeName); + InvalidateCachePerType(); + InvalidateCachePerType(); + InvalidateCachePerType(); } } -void CookAssetsStep::CacheData::Save() +void CookAssetsStep::CacheData::Save(CookingData& data) { + PROFILE_CPU(); LOG(Info, "Saving incremental build cooking cache (entries count: {0})", Entries.Count()); - auto file = FileWriteStream::Open(HeaderFilePath); if (file == nullptr) return; @@ -302,6 +319,7 @@ void CookAssetsStep::CacheData::Save() file->Write(f.Second); } } + file->Write(data.Tools->SaveCache(data, this)); file->WriteInt32(13); } @@ -961,6 +979,7 @@ public: const int32 count = addedEntries.Count(); if (count == 0) return false; + PROFILE_CPU(); // Get assets init data and load all chunks Array assetsData; @@ -1143,7 +1162,7 @@ bool CookAssetsStep::Perform(CookingData& data) // Cook asset if (Process(data, cache, assetRef.Get())) { - cache.Save(); + cache.Save(data); return true; } data.Stats.CookedAssets++; @@ -1151,12 +1170,12 @@ bool CookAssetsStep::Perform(CookingData& data) // Auto save build cache after every few cooked assets (reduces next build time if cooking fails later) if (data.Stats.CookedAssets % 50 == 0) { - cache.Save(); + cache.Save(data); } } // Save build cache header - cache.Save(); + cache.Save(data); // Create build game header { @@ -1173,7 +1192,6 @@ bool CookAssetsStep::Perform(CookingData& data) } stream->WriteInt32(('x' + 'D') * 131); // think about it as '131 times xD' - stream->WriteInt32(FLAXENGINE_VERSION_BUILD); Array bytes; diff --git a/Source/Editor/Cooker/Steps/CookAssetsStep.h b/Source/Editor/Cooker/Steps/CookAssetsStep.h index c552e532c..ae6bf7a6f 100644 --- a/Source/Editor/Cooker/Steps/CookAssetsStep.h +++ b/Source/Editor/Cooker/Steps/CookAssetsStep.h @@ -3,6 +3,7 @@ #pragma once #include "Editor/Cooker/GameCooker.h" +#include "Editor/Cooker/PlatformTools.h" #include "Engine/Core/Types/Pair.h" #include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Collections/Dictionary.h" @@ -56,7 +57,7 @@ public: /// /// Assets cooking cache data (incremental building feature). /// - struct FLAXENGINE_API CacheData + struct FLAXENGINE_API CacheData : public IBuildCache { /// /// The cache header file path. @@ -136,16 +137,6 @@ public: /// The added entry reference. CacheEntry& CreateEntry(const Asset* asset, String& cachedFilePath); - /// - /// Removes all cached entries for assets that contain a shader. This forces rebuild for them. - /// - void InvalidateShaders(); - - /// - /// Removes all cached entries for assets that contain a texture. This forces rebuild for them. - /// - void InvalidateCachePerType(const StringView& typeName); - /// /// Loads the cache for the given cooking data. /// @@ -155,7 +146,11 @@ public: /// /// Saves this cache (header file). /// - void Save(); + /// The data. + void Save(CookingData& data); + + using IBuildCache::InvalidateCachePerType; + void InvalidateCachePerType(const StringView& typeName) override; }; struct FLAXENGINE_API AssetCookData From 53a2ebbd17e21d1ab44beebacfd2dfe84b19ad5f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Dec 2023 15:12:12 +0100 Subject: [PATCH 029/198] Add more ASTC texture formats with larger block sizes --- .../Engine/Graphics/PixelFormatExtensions.cpp | 76 ++++++++++++++----- Source/Engine/Graphics/RenderTools.cpp | 6 ++ .../Engine/Graphics/Textures/GPUTexture.cpp | 9 ++- .../Vulkan/RenderToolsVulkan.cpp | 8 +- .../Tools/TextureTool/TextureTool.astc.cpp | 6 +- 5 files changed, 80 insertions(+), 25 deletions(-) diff --git a/Source/Engine/Graphics/PixelFormatExtensions.cpp b/Source/Engine/Graphics/PixelFormatExtensions.cpp index 4431870ee..2f3aa3931 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.cpp +++ b/Source/Engine/Graphics/PixelFormatExtensions.cpp @@ -29,6 +29,21 @@ void PixelFormatExtensions::Init() PixelFormat::R8_Typeless, PixelFormat::R8_UInt, PixelFormat::R8_UNorm, + PixelFormat::BC2_Typeless, + PixelFormat::BC2_UNorm, + PixelFormat::BC2_UNorm_sRGB, + PixelFormat::BC3_Typeless, + PixelFormat::BC3_UNorm, + PixelFormat::BC3_UNorm_sRGB, + PixelFormat::BC5_SNorm, + PixelFormat::BC5_Typeless, + PixelFormat::BC5_UNorm, + PixelFormat::BC6H_Sf16, + PixelFormat::BC6H_Typeless, + PixelFormat::BC6H_Uf16, + PixelFormat::BC7_Typeless, + PixelFormat::BC7_UNorm, + PixelFormat::BC7_UNorm_sRGB, PixelFormat::ASTC_4x4_UNorm, PixelFormat::ASTC_4x4_UNorm_sRGB, }; @@ -134,25 +149,6 @@ void PixelFormatExtensions::Init() PixelFormat::BC4_UNorm, }; InitFormat(formats8, 4); - - PixelFormat formats9[] = { - PixelFormat::BC2_Typeless, - PixelFormat::BC2_UNorm, - PixelFormat::BC2_UNorm_sRGB, - PixelFormat::BC3_Typeless, - PixelFormat::BC3_UNorm, - PixelFormat::BC3_UNorm_sRGB, - PixelFormat::BC5_SNorm, - PixelFormat::BC5_Typeless, - PixelFormat::BC5_UNorm, - PixelFormat::BC6H_Sf16, - PixelFormat::BC6H_Typeless, - PixelFormat::BC6H_Uf16, - PixelFormat::BC7_Typeless, - PixelFormat::BC7_UNorm, - PixelFormat::BC7_UNorm_sRGB, - }; - InitFormat(formats9, 8); } int32 PixelFormatExtensions::SizeInBits(PixelFormat format) @@ -315,6 +311,12 @@ bool PixelFormatExtensions::IsCompressed(const PixelFormat format) case PixelFormat::BC7_UNorm_sRGB: case PixelFormat::ASTC_4x4_UNorm: case PixelFormat::ASTC_4x4_UNorm_sRGB: + case PixelFormat::ASTC_6x6_UNorm: + case PixelFormat::ASTC_6x6_UNorm_sRGB: + case PixelFormat::ASTC_8x8_UNorm: + case PixelFormat::ASTC_8x8_UNorm_sRGB: + case PixelFormat::ASTC_10x10_UNorm: + case PixelFormat::ASTC_10x10_UNorm_sRGB: return true; default: return false; @@ -358,6 +360,12 @@ bool PixelFormatExtensions::IsCompressedASTC(PixelFormat format) { case PixelFormat::ASTC_4x4_UNorm: case PixelFormat::ASTC_4x4_UNorm_sRGB: + case PixelFormat::ASTC_6x6_UNorm: + case PixelFormat::ASTC_6x6_UNorm_sRGB: + case PixelFormat::ASTC_8x8_UNorm: + case PixelFormat::ASTC_8x8_UNorm_sRGB: + case PixelFormat::ASTC_10x10_UNorm: + case PixelFormat::ASTC_10x10_UNorm_sRGB: return true; default: return false; @@ -391,6 +399,9 @@ bool PixelFormatExtensions::IsSRGB(const PixelFormat format) case PixelFormat::B8G8R8X8_UNorm_sRGB: case PixelFormat::BC7_UNorm_sRGB: case PixelFormat::ASTC_4x4_UNorm_sRGB: + case PixelFormat::ASTC_6x6_UNorm_sRGB: + case PixelFormat::ASTC_8x8_UNorm_sRGB: + case PixelFormat::ASTC_10x10_UNorm_sRGB: return true; default: return false; @@ -580,6 +591,12 @@ int32 PixelFormatExtensions::ComputeComponentsCount(const PixelFormat format) case PixelFormat::B8G8R8X8_UNorm_sRGB: case PixelFormat::ASTC_4x4_UNorm: case PixelFormat::ASTC_4x4_UNorm_sRGB: + case PixelFormat::ASTC_6x6_UNorm: + case PixelFormat::ASTC_6x6_UNorm_sRGB: + case PixelFormat::ASTC_8x8_UNorm: + case PixelFormat::ASTC_8x8_UNorm_sRGB: + case PixelFormat::ASTC_10x10_UNorm: + case PixelFormat::ASTC_10x10_UNorm_sRGB: return 4; case PixelFormat::R32G32B32_Typeless: case PixelFormat::R32G32B32_Float: @@ -669,6 +686,15 @@ int32 PixelFormatExtensions::ComputeBlockSize(PixelFormat format) case PixelFormat::ASTC_4x4_UNorm: case PixelFormat::ASTC_4x4_UNorm_sRGB: return 4; + case PixelFormat::ASTC_6x6_UNorm: + case PixelFormat::ASTC_6x6_UNorm_sRGB: + return 6; + case PixelFormat::ASTC_8x8_UNorm: + case PixelFormat::ASTC_8x8_UNorm_sRGB: + return 8; + case PixelFormat::ASTC_10x10_UNorm: + case PixelFormat::ASTC_10x10_UNorm_sRGB: + return 10; default: return 1; } @@ -694,6 +720,12 @@ PixelFormat PixelFormatExtensions::TosRGB(const PixelFormat format) return PixelFormat::BC7_UNorm_sRGB; case PixelFormat::ASTC_4x4_UNorm: return PixelFormat::ASTC_4x4_UNorm_sRGB; + case PixelFormat::ASTC_6x6_UNorm: + return PixelFormat::ASTC_6x6_UNorm_sRGB; + case PixelFormat::ASTC_8x8_UNorm: + return PixelFormat::ASTC_8x8_UNorm_sRGB; + case PixelFormat::ASTC_10x10_UNorm: + return PixelFormat::ASTC_10x10_UNorm_sRGB; default: return format; } @@ -719,6 +751,12 @@ PixelFormat PixelFormatExtensions::ToNonsRGB(const PixelFormat format) return PixelFormat::BC7_UNorm; case PixelFormat::ASTC_4x4_UNorm_sRGB: return PixelFormat::ASTC_4x4_UNorm; + case PixelFormat::ASTC_6x6_UNorm_sRGB: + return PixelFormat::ASTC_6x6_UNorm; + case PixelFormat::ASTC_8x8_UNorm_sRGB: + return PixelFormat::ASTC_8x8_UNorm; + case PixelFormat::ASTC_10x10_UNorm_sRGB: + return PixelFormat::ASTC_10x10_UNorm; default: return format; } diff --git a/Source/Engine/Graphics/RenderTools.cpp b/Source/Engine/Graphics/RenderTools.cpp index 5fc6c7d65..66e17896d 100644 --- a/Source/Engine/Graphics/RenderTools.cpp +++ b/Source/Engine/Graphics/RenderTools.cpp @@ -331,6 +331,12 @@ void RenderTools::ComputePitch(PixelFormat format, int32 width, int32 height, ui break; case PixelFormat::ASTC_4x4_UNorm: case PixelFormat::ASTC_4x4_UNorm_sRGB: + case PixelFormat::ASTC_6x6_UNorm: + case PixelFormat::ASTC_6x6_UNorm_sRGB: + case PixelFormat::ASTC_8x8_UNorm: + case PixelFormat::ASTC_8x8_UNorm_sRGB: + case PixelFormat::ASTC_10x10_UNorm: + case PixelFormat::ASTC_10x10_UNorm_sRGB: { const int32 blockSize = PixelFormatExtensions::ComputeBlockSize(format); uint32 nbw = Math::Max(1, Math::DivideAndRoundUp(width, blockSize)); diff --git a/Source/Engine/Graphics/Textures/GPUTexture.cpp b/Source/Engine/Graphics/Textures/GPUTexture.cpp index 403f545ba..4d43e20ec 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.cpp +++ b/Source/Engine/Graphics/Textures/GPUTexture.cpp @@ -337,14 +337,12 @@ int32 GPUTexture::ComputeBufferOffset(int32 subresource, int32 rowAlign, int32 s int32 GPUTexture::ComputeBufferTotalSize(int32 rowAlign, int32 sliceAlign) const { int32 result = 0; - for (int32 mipLevel = 0; mipLevel < MipLevels(); mipLevel++) { const int32 slicePitch = ComputeSlicePitch(mipLevel, rowAlign); const int32 depth = CalculateMipSize(Depth(), mipLevel); result += Math::AlignUp(slicePitch * depth, sliceAlign); } - return result * ArraySize(); } @@ -355,8 +353,11 @@ int32 GPUTexture::ComputeSlicePitch(int32 mipLevel, int32 rowAlign) const int32 GPUTexture::ComputeRowPitch(int32 mipLevel, int32 rowAlign) const { - const int32 formatSize = PixelFormatExtensions::SizeInBytes(Format()); - return Math::AlignUp(CalculateMipSize(Width(), mipLevel) * formatSize, rowAlign); + int32 mipWidth = CalculateMipSize(Width(), mipLevel); + int32 mipHeight = CalculateMipSize(Height(), mipLevel); + uint32 rowPitch, slicePitch; + RenderTools::ComputePitch(Format(), mipWidth, mipHeight, rowPitch, slicePitch); + return Math::AlignUp(rowPitch, rowAlign); } bool GPUTexture::Init(const GPUTextureDescription& desc) diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp index 3a5a91e5e..f9c9237f6 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp @@ -6,7 +6,7 @@ #include "Engine/Core/Types/StringBuilder.h" #include "Engine/Core/Log.h" -VkFormat RenderToolsVulkan::PixelFormatToVkFormat[102] = +VkFormat RenderToolsVulkan::PixelFormatToVkFormat[108] = { VK_FORMAT_UNDEFINED, VK_FORMAT_R32G32B32A32_SFLOAT, @@ -110,6 +110,12 @@ VkFormat RenderToolsVulkan::PixelFormatToVkFormat[102] = VK_FORMAT_BC7_SRGB_BLOCK, VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_4x4_SRGB_BLOCK, + VK_FORMAT_ASTC_6x6_UNORM_BLOCK, + VK_FORMAT_ASTC_6x6_SRGB_BLOCK, + VK_FORMAT_ASTC_8x8_UNORM_BLOCK, + VK_FORMAT_ASTC_8x8_SRGB_BLOCK, + VK_FORMAT_ASTC_10x10_UNORM_BLOCK, + VK_FORMAT_ASTC_10x10_SRGB_BLOCK, }; VkBlendFactor RenderToolsVulkan::BlendToVkBlendFactor[20] = diff --git a/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp index 3dcd8bb72..3eceb837e 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp @@ -8,6 +8,7 @@ #include "Engine/Core/Math/Color32.h" #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Graphics/PixelFormatExtensions.h" +#include "Engine/Graphics/RenderTools.h" #include bool TextureTool::ConvertAstc(TextureData& dst, const TextureData& src, const PixelFormat dstFormat) @@ -87,7 +88,10 @@ bool TextureTool::ConvertAstc(TextureData& dst, const TextureData& src, const Pi auto mipHeight = Math::Max(textureData->Height >> mipIndex, 1); auto blocksWidth = Math::Max(Math::DivideAndRoundUp(mipWidth, blockSize), 1); auto blocksHeight = Math::Max(Math::DivideAndRoundUp(mipHeight, blockSize), 1); - ASSERT(srcMip.RowPitch == mipWidth * PixelFormatExtensions::SizeInBytes(textureData->Format)); + uint32 mipRowPitch, mipSlicePitch; + RenderTools::ComputePitch(textureData->Format, mipWidth, mipHeight, mipRowPitch, mipSlicePitch); + ASSERT(srcMip.RowPitch == mipRowPitch); + ASSERT(srcMip.DepthPitch == mipSlicePitch); ASSERT(srcMip.Lines == mipHeight); // Allocate memory From b2f9b9e14d7cab024b7d78b1fac60ef37e39325e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Dec 2023 15:12:48 +0100 Subject: [PATCH 030/198] Add texture quality option for iOS --- .../Cooker/Platform/iOS/iOSPlatformTools.cpp | 119 +++++++++++------- .../Cooker/Platform/iOS/iOSPlatformTools.h | 2 + Source/Engine/Graphics/PixelFormat.h | 32 ++++- .../Engine/Platform/iOS/iOSPlatformSettings.h | 22 ++++ 4 files changed, 131 insertions(+), 44 deletions(-) diff --git a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp index 85a7f99fa..4d50401bd 100644 --- a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp @@ -24,6 +24,11 @@ IMPLEMENT_SETTINGS_GETTER(iOSPlatformSettings, iOSPlatform); namespace { + struct iOSPlatformCache + { + iOSPlatformSettings::TextureQuality TexturesQuality; + }; + String GetAppName() { const auto gameSettings = GameSettings::Get(); @@ -60,6 +65,22 @@ namespace result = result.TrimTrailing(); return result; } + + PixelFormat GetQualityTextureFormat(bool sRGB) + { + const auto platformSettings = iOSPlatformSettings::Get(); + switch (platformSettings->TexturesQuality) + { + case iOSPlatformSettings::TextureQuality::ASTC_High: + return sRGB ? PixelFormat::ASTC_4x4_UNorm_sRGB : PixelFormat::ASTC_4x4_UNorm; + case iOSPlatformSettings::TextureQuality::ASTC_Medium: + return sRGB ? PixelFormat::ASTC_6x6_UNorm_sRGB : PixelFormat::ASTC_6x6_UNorm; + case iOSPlatformSettings::TextureQuality::ASTC_Low: + return sRGB ? PixelFormat::ASTC_8x8_UNorm_sRGB : PixelFormat::ASTC_8x8_UNorm; + default: + CRASH; + } + } } const Char* iOSPlatformTools::GetDisplayName() const @@ -89,51 +110,37 @@ DotNetAOTModes iOSPlatformTools::UseAOT() const PixelFormat iOSPlatformTools::GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) { - // TODO: add ETC compression support for iOS - // TODO: add ASTC compression support for iOS - - if (PixelFormatExtensions::IsCompressedBC(format)) + switch (format) { - switch (format) - { - case PixelFormat::BC1_Typeless: - case PixelFormat::BC2_Typeless: - case PixelFormat::BC3_Typeless: - return PixelFormat::R8G8B8A8_Typeless; - case PixelFormat::BC1_UNorm: - case PixelFormat::BC2_UNorm: - case PixelFormat::BC3_UNorm: - return PixelFormat::R8G8B8A8_UNorm; - case PixelFormat::BC1_UNorm_sRGB: - case PixelFormat::BC2_UNorm_sRGB: - case PixelFormat::BC3_UNorm_sRGB: - return PixelFormat::R8G8B8A8_UNorm_sRGB; - case PixelFormat::BC4_Typeless: - return PixelFormat::R8_Typeless; - case PixelFormat::BC4_UNorm: - return PixelFormat::R8_UNorm; - case PixelFormat::BC4_SNorm: - return PixelFormat::R8_SNorm; - case PixelFormat::BC5_Typeless: - return PixelFormat::R16G16_Typeless; - case PixelFormat::BC5_UNorm: - return PixelFormat::R16G16_UNorm; - case PixelFormat::BC5_SNorm: - return PixelFormat::R16G16_SNorm; - case PixelFormat::BC7_Typeless: - case PixelFormat::BC6H_Typeless: - return PixelFormat::R16G16B16A16_Typeless; - case PixelFormat::BC7_UNorm: - case PixelFormat::BC6H_Uf16: - case PixelFormat::BC6H_Sf16: - return PixelFormat::R16G16B16A16_Float; - case PixelFormat::BC7_UNorm_sRGB: - return PixelFormat::R16G16B16A16_UNorm; - default: - return format; - } + case PixelFormat::BC1_Typeless: + case PixelFormat::BC2_Typeless: + case PixelFormat::BC3_Typeless: + case PixelFormat::BC4_Typeless: + case PixelFormat::BC5_Typeless: + case PixelFormat::BC1_UNorm: + case PixelFormat::BC2_UNorm: + case PixelFormat::BC3_UNorm: + case PixelFormat::BC4_UNorm: + case PixelFormat::BC5_UNorm: + return GetQualityTextureFormat(false); + case PixelFormat::BC1_UNorm_sRGB: + case PixelFormat::BC2_UNorm_sRGB: + case PixelFormat::BC3_UNorm_sRGB: + case PixelFormat::BC7_UNorm_sRGB: + return GetQualityTextureFormat(true); + case PixelFormat::BC4_SNorm: + return PixelFormat::R8_SNorm; + case PixelFormat::BC5_SNorm: + return PixelFormat::R16G16_SNorm; + case PixelFormat::BC6H_Typeless: + case PixelFormat::BC6H_Uf16: + case PixelFormat::BC6H_Sf16: + case PixelFormat::BC7_Typeless: + case PixelFormat::BC7_UNorm: + return PixelFormat::R16G16B16A16_Typeless; // TODO: ASTC HDR + default: + return format; } - return format; } @@ -143,6 +150,32 @@ bool iOSPlatformTools::IsNativeCodeFile(CookingData& data, const String& file) return extension.IsEmpty() || extension == TEXT("dylib"); } +void iOSPlatformTools::LoadCache(CookingData& data, IBuildCache* cache, const Span& bytes) +{ + const auto platformSettings = iOSPlatformSettings::Get(); + bool invalidTextures = true; + if (bytes.Length() == sizeof(iOSPlatformCache)) + { + auto* platformCache = (iOSPlatformCache*)bytes.Get(); + invalidTextures = platformCache->TexturesQuality != platformSettings->TexturesQuality; + } + if (invalidTextures) + { + LOG(Info, "{0} option has been modified.", TEXT("TexturesQuality")); + cache->InvalidateCacheTextures(); + } +} + +Array iOSPlatformTools::SaveCache(CookingData& data, IBuildCache* cache) +{ + const auto platformSettings = iOSPlatformSettings::Get(); + iOSPlatformCache platformCache; + platformCache.TexturesQuality = platformSettings->TexturesQuality; + Array result; + result.Add((const byte*)&platformCache, sizeof(platformCache)); + return result; +} + void iOSPlatformTools::OnBuildStarted(CookingData& data) { // Adjust the cooking output folders for packaging app diff --git a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.h b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.h index 854961923..0bc7018b8 100644 --- a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.h +++ b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.h @@ -19,6 +19,8 @@ public: ArchitectureType GetArchitecture() const override; DotNetAOTModes UseAOT() const override; PixelFormat GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) override; + void LoadCache(CookingData& data, IBuildCache* cache, const Span& bytes) override; + Array SaveCache(CookingData& data, IBuildCache* cache) override; bool IsNativeCodeFile(CookingData& data, const String& file) override; void OnBuildStarted(CookingData& data) override; bool OnPostProcess(CookingData& data) override; diff --git a/Source/Engine/Graphics/PixelFormat.h b/Source/Engine/Graphics/PixelFormat.h index f6793e543..908864c58 100644 --- a/Source/Engine/Graphics/PixelFormat.h +++ b/Source/Engine/Graphics/PixelFormat.h @@ -519,10 +519,40 @@ API_ENUM() enum class PixelFormat : uint32 ASTC_4x4_UNorm = 100, /// - /// A four-component ASTC (4x4 pixel block in 128 bits) block-compression format that supports RGBA channels. + /// A four-component ASTC (4x4 pixel block in 128 bits) block-compression format that supports RGBA channels. Data in sRGB color space. /// ASTC_4x4_UNorm_sRGB = 101, + /// + /// A four-component ASTC (6x6 pixel block in 128 bits) block-compression format that supports RGBA channels. + /// + ASTC_6x6_UNorm = 102, + + /// + /// A four-component ASTC (6x6 pixel block in 128 bits) block-compression format that supports RGBA channels. Data in sRGB color space. + /// + ASTC_6x6_UNorm_sRGB = 103, + + /// + /// A four-component ASTC (8x8 pixel block in 128 bits) block-compression format that supports RGBA channels. + /// + ASTC_8x8_UNorm = 104, + + /// + /// A four-component ASTC (8x8 pixel block in 128 bits) block-compression format that supports RGBA channels. Data in sRGB color space. + /// + ASTC_8x8_UNorm_sRGB = 105, + + /// + /// A four-component ASTC (10x10 pixel block in 128 bits) block-compression format that supports RGBA channels. + /// + ASTC_10x10_UNorm = 106, + + /// + /// A four-component ASTC (10x10 pixel block in 128 bits) block-compression format that supports RGBA channels. Data in sRGB color space. + /// + ASTC_10x10_UNorm_sRGB = 107, + /// /// The maximum format value (for internal use only). /// diff --git a/Source/Engine/Platform/iOS/iOSPlatformSettings.h b/Source/Engine/Platform/iOS/iOSPlatformSettings.h index 9eed39f41..4016f2767 100644 --- a/Source/Engine/Platform/iOS/iOSPlatformSettings.h +++ b/Source/Engine/Platform/iOS/iOSPlatformSettings.h @@ -46,6 +46,22 @@ API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API All = Portrait | PortraitUpsideDown | LandscapeLeft | LandscapeRight }; + /// + /// The output textures quality (compression). + /// + API_ENUM() enum class TextureQuality + { + // ASTC 4x4 block compression. + API_ENUM(Attributes="EditorDisplay(null, \"ASTC High\")") + ASTC_High = 0, + // ASTC 6x6 block compression. + API_ENUM(Attributes="EditorDisplay(null, \"ASTC Medium\")") + ASTC_Medium = 1, + // ASTC 8x8 block compression. + API_ENUM(Attributes="EditorDisplay(null, \"ASTC Low\")") + ASTC_Low = 2, + }; + /// /// The app developer name - App Store Team ID. For example: 'VG6K6HT8B'. /// @@ -64,6 +80,12 @@ API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API API_FIELD(Attributes="EditorOrder(50), EditorDisplay(\"General\")") ExportMethods ExportMethod = ExportMethods::Development; + /// + /// The output textures quality (compression). + /// + API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"General\")") + TextureQuality TexturesQuality = TextureQuality::ASTC_Medium; + /// /// The UI interface orientation modes supported on iPhone devices. /// From fe711405ac1949f04ff4abd2a16fe968543929d4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 22 Dec 2023 11:21:29 +0100 Subject: [PATCH 031/198] Fix iOS build with dotnet8 on arm64 macOS --- Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs | 11 +++-------- .../Tools/Flax.Build/Platforms/Mac/MacPlatform.cs | 13 +++++++++++++ .../Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs | 5 ++++- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index 61132e4be..2ea4f3dc1 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -243,19 +243,12 @@ namespace Flax.Build dotnetPath = string.Empty; } } - - bool isRunningOnArm64Targetx64 = architecture == TargetArchitecture.ARM64 && (Configuration.BuildArchitectures != null && Configuration.BuildArchitectures[0] == TargetArchitecture.x64); - - // We need to support two paths here: - // 1. We are running an x64 binary and we are running on an arm64 host machine - // 2. We are running an Arm64 binary and we are targeting an x64 host machine - if (Flax.Build.Platforms.MacPlatform.GetProcessIsTranslated() || isRunningOnArm64Targetx64) + if (Flax.Build.Platforms.MacPlatform.BuildingForx64) { rid = "osx-x64"; dotnetPath = Path.Combine(dotnetPath, "x64"); architecture = TargetArchitecture.x64; } - break; } default: throw new InvalidPlatformException(platform); @@ -472,6 +465,8 @@ namespace Flax.Build { var versions = GetVersions(root); var version = GetVersion(versions); + if (version == null) + throw new Exception($"Failed to select dotnet version from '{root}' ({string.Join(", ", versions)})"); return Path.Combine(root, version); } diff --git a/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs b/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs index 4a8115fcd..764ac81c7 100644 --- a/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/Mac/MacPlatform.cs @@ -73,6 +73,19 @@ namespace Flax.Build.Platforms Utilities.Run("codesign", cmdLine, null, null, Utilities.RunOptions.Default | Utilities.RunOptions.ThrowExceptionOnError); } + internal static bool BuildingForx64 + { + get + { + // We need to support two paths here: + // 1. We are running an x64 binary and we are running on an arm64 host machine + // 2. We are running an Arm64 binary and we are targeting an x64 host machine + var architecture = Platform.BuildTargetArchitecture; + bool isRunningOnArm64Targetx64 = architecture == TargetArchitecture.ARM64 && (Configuration.BuildArchitectures != null && Configuration.BuildArchitectures[0] == TargetArchitecture.x64); + return GetProcessIsTranslated() || isRunningOnArm64Targetx64; + } + } + /// /// Returns true if running an x64 binary an arm64 host machine. /// diff --git a/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs b/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs index fd4370e05..fb25560ec 100644 --- a/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs @@ -65,8 +65,11 @@ namespace Flax.Build.Platforms switch (options.Action) { case CSharpOptions.ActionTypes.GetPlatformTools: - options.PlatformToolsPath = Path.Combine(DotNetSdk.SelectVersionFolder(Path.Combine(DotNetSdk.Instance.RootPath, "packs/Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.ios-arm64")), "tools"); + { + var arch = Flax.Build.Platforms.MacPlatform.BuildingForx64 ? "x64" : "arm64"; + options.PlatformToolsPath = Path.Combine(DotNetSdk.SelectVersionFolder(Path.Combine(DotNetSdk.Instance.RootPath, $"packs/Microsoft.NETCore.App.Runtime.AOT.osx-{arch}.Cross.ios-arm64")), "tools"); return false; + } case CSharpOptions.ActionTypes.MonoCompile: { var aotCompilerPath = Path.Combine(options.PlatformToolsPath, "mono-aot-cross"); From 3c6ed303fe26889a90ed4308654ee2f83988da5c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 24 Dec 2023 22:45:44 +0100 Subject: [PATCH 032/198] Dont use astc on windows/limux yet --- .../Engine/Tools/TextureTool/TextureTool.Build.cs | 3 ++- .../Tools/TextureTool/TextureTool.DirectXTex.cpp | 14 ++++++++++---- Source/ThirdParty/astc/astc.Build.cs | 12 ++---------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Source/Engine/Tools/TextureTool/TextureTool.Build.cs b/Source/Engine/Tools/TextureTool/TextureTool.Build.cs index 005ca9c96..00e0a18a9 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.Build.cs +++ b/Source/Engine/Tools/TextureTool/TextureTool.Build.cs @@ -58,8 +58,9 @@ public class TextureTool : EngineModule options.PrivateDependencies.Add("bc7enc16"); } } - if (options.Target.IsEditor) + if (options.Target.IsEditor && options.Platform.Target == TargetPlatform.Mac) // TODO: add ASTC for Editor on Linux and Windows { + // ASTC for mobile (iOS and Android) options.SourceFiles.Add(Path.Combine(FolderPath, "TextureTool.astc.cpp")); options.PrivateDependencies.Add("astc"); } diff --git a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp index 47988f4e8..84e1c5adf 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp @@ -834,6 +834,16 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path bool TextureTool::ConvertDirectXTex(TextureData& dst, const TextureData& src, const PixelFormat dstFormat) { + if (PixelFormatExtensions::IsCompressedASTC(dstFormat)) + { + // TODO: decompress if need to +#if COMPILE_WITH_ASTC + return ConvertAstc(dst, *textureData, dstFormat); +#else + return true; +#endif + } + HRESULT result; DirectX::ScratchImage dstImage; DirectX::ScratchImage tmpImage; @@ -919,10 +929,6 @@ bool TextureTool::ConvertDirectXTex(TextureData& dst, const TextureData& src, co return true; } } - else if (PixelFormatExtensions::IsCompressedASTC(dstFormat)) - { - todo_astc_compression_on_windows; - } // Check if convert data else if (inImage->GetMetadata().format != dstFormatDxgi) { diff --git a/Source/ThirdParty/astc/astc.Build.cs b/Source/ThirdParty/astc/astc.Build.cs index e885c9150..237d0ac81 100644 --- a/Source/ThirdParty/astc/astc.Build.cs +++ b/Source/ThirdParty/astc/astc.Build.cs @@ -1,13 +1,12 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using System.IO; using Flax.Build; using Flax.Build.NativeCpp; /// /// https://github.com/ARM-software/astc-encoder /// -public class astc : ThirdPartyModule +public class astc : DepsModule { /// public override void Init() @@ -27,13 +26,6 @@ public class astc : ThirdPartyModule base.Setup(options); options.PublicDefinitions.Add("COMPILE_WITH_ASTC"); - var depsRoot = options.DepsFolder; - switch (options.Platform.Target) - { - case TargetPlatform.Mac: - options.OutputFiles.Add(Path.Combine(depsRoot, "libastcenc.a")); - break; - default: throw new InvalidPlatformException(options.Platform.Target); - } + AddLib(options, options.DepsFolder, "astcenc"); } } From f2d1b75d1f135f04b0e9718ba28b9adfb640efa0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 24 Dec 2023 22:46:30 +0100 Subject: [PATCH 033/198] Improve build tool api for deps modules --- .../Flax.Build/Deps/Dependencies/OpenAL.cs | 6 ++-- .../Flax.Build/Deps/Dependencies/astc.cs | 2 +- .../Flax.Build/Deps/Dependencies/freetype.cs | 8 ++--- .../Tools/Flax.Build/Deps/Dependencies/ogg.cs | 8 ++--- .../Flax.Build/Deps/Dependencies/vorbis.cs | 8 ++--- Source/Tools/Flax.Build/Deps/Dependency.cs | 36 ++++++++++++------- .../Tools/Flax.Build/Utilities/Utilities.cs | 5 +++ 7 files changed, 44 insertions(+), 29 deletions(-) diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs index 96ef05dd8..d0390eff7 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs @@ -147,7 +147,7 @@ namespace Flax.Deps.Dependencies // Build RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); @@ -176,7 +176,7 @@ namespace Flax.Deps.Dependencies { SetupDirectory(buildDir, true); RunCmake(buildDir, platform, architecture, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, architecture); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); @@ -206,7 +206,7 @@ namespace Flax.Deps.Dependencies // Build for iOS SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_SYSTEM_NAME=iOS -DALSOFT_OSX_FRAMEWORK=ON -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs b/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs index adbc54438..f062cd91d 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs @@ -49,7 +49,7 @@ namespace Flax.Deps.Dependencies var lib = architecture == TargetArchitecture.ARM64 ? "libastcenc-neon-static.a" : "libastcenc-sse2-static.a"; SetupDirectory(buildDir, true); RunCmake(buildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release -DASTCENC_UNIVERSAL_BUILD=OFF -DASTCENC_UNIVERSAL_BINARY=OFF " + isa); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, architecture); Utilities.FileCopy(Path.Combine(buildDir, "Source", lib), Path.Combine(depsFolder, "libastcenc.a")); } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs index d2d31a613..4efe4ed43 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs @@ -229,7 +229,7 @@ namespace Flax.Deps.Dependencies // Build for Android SetupDirectory(buildDir, true); RunCmake(buildDir, TargetPlatform.Android, TargetArchitecture.ARM64, ".. -DFT_WITH_BZIP2=OFF -DFT_WITH_ZLIB=OFF -DFT_WITH_PNG=OFF -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; @@ -239,7 +239,7 @@ namespace Flax.Deps.Dependencies // Build for Switch SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; @@ -251,7 +251,7 @@ namespace Flax.Deps.Dependencies { SetupDirectory(buildDir, true); RunCmake(buildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, architecture); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); } @@ -268,7 +268,7 @@ namespace Flax.Deps.Dependencies // Build for iOS SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DIOS_PLATFORM=OS -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_BUILD_TYPE=Release -DFT_WITH_BZIP2=OFF -DFT_WITH_ZLIB=OFF -DFT_WITH_PNG=OFF"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs b/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs index eb02accef..1716ff08d 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs @@ -196,7 +196,7 @@ namespace Flax.Deps.Dependencies // Build for Android SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; @@ -209,7 +209,7 @@ namespace Flax.Deps.Dependencies // Build for Switch SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; @@ -221,7 +221,7 @@ namespace Flax.Deps.Dependencies { SetupDirectory(buildDir, true); RunCmake(buildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, architecture); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); } @@ -231,7 +231,7 @@ namespace Flax.Deps.Dependencies { SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release"); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs index c3a53ebd4..b6acc4bf9 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs @@ -331,7 +331,7 @@ namespace Flax.Deps.Dependencies Utilities.Run("cmake", "--build . --target install", null, oggBuildDir, Utilities.RunOptions.ConsoleLogOutput); SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, string.Format(".. -DCMAKE_BUILD_TYPE=Release -DOGG_INCLUDE_DIR=\"{0}/install/include\" -DOGG_LIBRARY=\"{0}/install/lib\"", oggRoot)); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopyUnix) Utilities.FileCopy(Path.Combine(buildDir, file.SrcFolder, file.Filename), Path.Combine(depsFolder, file.Filename)); @@ -358,7 +358,7 @@ namespace Flax.Deps.Dependencies Utilities.FileCopy(Path.Combine(GetBinariesFolder(options, platform), "ogg", "include", "ogg", "config_types.h"), Path.Combine(oggRoot, "install", "include", "ogg", "config_types.h")); SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, string.Format(".. -DCMAKE_BUILD_TYPE=Release -DOGG_INCLUDE_DIR=\"{0}/install/include\" -DOGG_LIBRARY=\"{0}/install/lib\"", oggRoot)); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopyUnix) Utilities.FileCopy(Path.Combine(buildDir, file.SrcFolder, file.Filename), Path.Combine(depsFolder, file.Filename)); @@ -383,7 +383,7 @@ namespace Flax.Deps.Dependencies Utilities.Run("cmake", "--build . --target install", null, oggBuildDir, Utilities.RunOptions.ConsoleLogOutput); SetupDirectory(buildDir, true); RunCmake(buildDir, platform, architecture, string.Format(".. -DCMAKE_BUILD_TYPE=Release -DOGG_INCLUDE_DIR=\"{0}/install/include\" -DOGG_LIBRARY=\"{0}/install/lib\"", oggRoot)); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, architecture); foreach (var file in binariesToCopyUnix) Utilities.FileCopy(Path.Combine(buildDir, file.SrcFolder, file.Filename), Path.Combine(depsFolder, file.Filename)); @@ -407,7 +407,7 @@ namespace Flax.Deps.Dependencies Utilities.Run("cmake", "--build . --target install", null, oggBuildDir, Utilities.RunOptions.ConsoleLogOutput); SetupDirectory(buildDir, true); RunCmake(buildDir, platform, TargetArchitecture.ARM64, string.Format(".. -DCMAKE_BUILD_TYPE=Release -DOGG_INCLUDE_DIR=\"{0}/install/include\" -DOGG_LIBRARY=\"{0}/install/lib\"", oggRoot)); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput); + BuildCmake(buildDir); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopyUnix) Utilities.FileCopy(Path.Combine(buildDir, file.SrcFolder, file.Filename), Path.Combine(depsFolder, file.Filename)); diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index e9b646a6b..db65289c4 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -136,14 +136,14 @@ namespace Flax.Deps if (submodules) cmdLine += " --recurse-submodules"; - Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.DefaultTool); if (submodules) - Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.DefaultTool); } if (commit != null) { - Utilities.Run("git", string.Format("reset --hard {0}", commit), null, null, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("git", string.Format("reset --hard {0}", commit), null, null, Utilities.RunOptions.DefaultTool); } } @@ -164,9 +164,9 @@ namespace Flax.Deps if (submodules) cmdLine += " --recurse-submodules"; - Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.DefaultTool); if (submodules) - Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.DefaultTool); } } @@ -191,14 +191,14 @@ namespace Flax.Deps if (submodules) cmdLine += " --recurse-submodules"; - Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.DefaultTool); if (submodules) - Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.DefaultTool); } if (commit != null) { - Utilities.Run("git", string.Format("reset --hard {0}", commit), null, path, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("git", string.Format("reset --hard {0}", commit), null, path, Utilities.RunOptions.DefaultTool); } } @@ -218,13 +218,13 @@ namespace Flax.Deps if (submodules) cmdLine += " --recurse-submodules"; - Utilities.Run("git", cmdLine, null, path, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("git", cmdLine, null, path, Utilities.RunOptions.DefaultTool); if (submodules) - Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.DefaultTool); if (commit != null) { - Utilities.Run("git", string.Format("reset --hard {0}", commit), null, path, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("git", string.Format("reset --hard {0}", commit), null, path, Utilities.RunOptions.DefaultTool); } } @@ -234,7 +234,17 @@ namespace Flax.Deps /// The local path that contains git repository. public static void GitResetLocalChanges(string path) { - Utilities.Run("git", "reset --hard", null, path, Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("git", "reset --hard", null, path, Utilities.RunOptions.DefaultTool); + } + + /// + /// Builds the cmake project. + /// + /// The path. + /// Custom environment variables to pass to the child process. + public static void BuildCmake(string path, Dictionary envVars = null) + { + Utilities.Run("cmake", "--build .", null, path, Utilities.RunOptions.DefaultTool, envVars); } /// @@ -313,7 +323,7 @@ namespace Flax.Deps if (customArgs != null) cmdLine += " " + customArgs; - Utilities.Run("cmake", cmdLine, null, path, Utilities.RunOptions.ConsoleLogOutput, envVars); + Utilities.Run("cmake", cmdLine, null, path, Utilities.RunOptions.DefaultTool, envVars); } /// diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index b001eea3f..bf7c24e1a 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -324,6 +324,11 @@ namespace Flax.Build /// The default options. /// Default = AppMustExist, + + /// + /// The default options for tools execution in build tool. + /// + DefaultTool = ConsoleLogOutput | ThrowExceptionOnError, } private static void StdLogInfo(object sender, DataReceivedEventArgs e) From c49c875165dc7954054eccd203a0ed0b9abd14c6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Jan 2024 22:05:25 +0100 Subject: [PATCH 034/198] Add `astc` lib for Windows x64 --- .../Tools/TextureTool/TextureTool.Build.cs | 2 +- .../TextureTool/TextureTool.DirectXTex.cpp | 4 ++-- .../Tools/TextureTool/TextureTool.astc.cpp | 14 +++++++++++--- .../Tools/TextureTool/TextureTool.stb.cpp | 2 ++ .../Windows/Binaries/ThirdParty/x64/astcenc.lib | 3 +++ .../Tools/Flax.Build/Deps/Dependencies/astc.cs | 17 +++++++++++++++++ Source/Tools/Flax.Build/Deps/Dependency.cs | 2 +- 7 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 Source/Platforms/Windows/Binaries/ThirdParty/x64/astcenc.lib diff --git a/Source/Engine/Tools/TextureTool/TextureTool.Build.cs b/Source/Engine/Tools/TextureTool/TextureTool.Build.cs index 00e0a18a9..a6a68617c 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.Build.cs +++ b/Source/Engine/Tools/TextureTool/TextureTool.Build.cs @@ -58,7 +58,7 @@ public class TextureTool : EngineModule options.PrivateDependencies.Add("bc7enc16"); } } - if (options.Target.IsEditor && options.Platform.Target == TargetPlatform.Mac) // TODO: add ASTC for Editor on Linux and Windows + if (options.Target.IsEditor && options.Platform.Target != TargetPlatform.Linux) // TODO: add ASTC for Editor on Linux { // ASTC for mobile (iOS and Android) options.SourceFiles.Add(Path.Combine(FolderPath, "TextureTool.astc.cpp")); diff --git a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp index 84e1c5adf..cf81aed50 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp @@ -836,10 +836,10 @@ bool TextureTool::ConvertDirectXTex(TextureData& dst, const TextureData& src, co { if (PixelFormatExtensions::IsCompressedASTC(dstFormat)) { - // TODO: decompress if need to #if COMPILE_WITH_ASTC - return ConvertAstc(dst, *textureData, dstFormat); + return ConvertAstc(dst, src, dstFormat); #else + LOG(Error, "Missing ASTC texture format compression lib."); return true; #endif } diff --git a/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp index 3eceb837e..ebb9c07af 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp @@ -5,7 +5,6 @@ #include "TextureTool.h" #include "Engine/Core/Log.h" #include "Engine/Core/Math/Math.h" -#include "Engine/Core/Math/Color32.h" #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Graphics/RenderTools.h" @@ -49,7 +48,9 @@ bool TextureTool::ConvertAstc(TextureData& dst, const TextureData& src, const Pi TextureData converted; // Encoder uses full 4-component RGBA input image so convert it if needed - if (PixelFormatExtensions::ComputeComponentsCount(src.Format) != 4) + if (PixelFormatExtensions::ComputeComponentsCount(src.Format) != 4 || + PixelFormatExtensions::IsCompressed(textureData->Format) || + !PixelFormatExtensions::IsRgbAOrder(textureData->Format)) { if (textureData != &src) converted = src; @@ -71,6 +72,13 @@ bool TextureTool::ConvertAstc(TextureData& dst, const TextureData& src, const Pi textureData = &converted; } + // Setup output + dst.Items.Resize(textureData->Items.Count()); + dst.Width = textureData->Width; + dst.Height = textureData->Height; + dst.Depth = 1; + dst.Format = dstFormat; + // Compress all array slices for (int32 arrayIndex = 0; arrayIndex < textureData->Items.Count() && astcError == ASTCENC_SUCCESS; arrayIndex++) { @@ -124,4 +132,4 @@ bool TextureTool::ConvertAstc(TextureData& dst, const TextureData& src, const Pi return astcError != ASTCENC_SUCCESS; } -#endif \ No newline at end of file +#endif diff --git a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp index d45c9d465..53b8621ab 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp @@ -666,6 +666,8 @@ bool TextureTool::ConvertStb(TextureData& dst, const TextureData& src, const Pix { #if COMPILE_WITH_ASTC if (ConvertAstc(dst, *textureData, dstFormat)) +#else + LOG(Error, "Missing ASTC texture format compression lib."); #endif { return true; diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/astcenc.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/astcenc.lib new file mode 100644 index 000000000..d89395539 --- /dev/null +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/astcenc.lib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:38500ac222bd4132dd81ea03016462add2b0c8188b7cb7101223d19bf209cd01 +size 536548 diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs b/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs index f062cd91d..a1c9edcee 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs @@ -18,6 +18,11 @@ namespace Flax.Deps.Dependencies { switch (BuildPlatform) { + case TargetPlatform.Windows: + return new[] + { + TargetPlatform.Windows, + }; case TargetPlatform.Mac: return new[] { @@ -42,6 +47,18 @@ namespace Flax.Deps.Dependencies { switch (platform) { + case TargetPlatform.Windows: + foreach (var architecture in new []{ TargetArchitecture.x64 }) + { + var isa = "-DASTCENC_ISA_SSE2=ON"; + var lib = "astcenc-sse2-static.lib"; + SetupDirectory(buildDir, true); + RunCmake(buildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release -DASTCENC_CLI=OFF -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL " + isa); + BuildCmake(buildDir); + var depsFolder = GetThirdPartyFolder(options, platform, architecture); + Utilities.FileCopy(Path.Combine(buildDir, "Source/Release", lib), Path.Combine(depsFolder, "astcenc.lib")); + } + break; case TargetPlatform.Mac: foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 }) { diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index db65289c4..4e940c824 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -244,7 +244,7 @@ namespace Flax.Deps /// Custom environment variables to pass to the child process. public static void BuildCmake(string path, Dictionary envVars = null) { - Utilities.Run("cmake", "--build .", null, path, Utilities.RunOptions.DefaultTool, envVars); + Utilities.Run("cmake", "--build . --config Release", null, path, Utilities.RunOptions.DefaultTool, envVars); } /// From 85afde43af9120365a4a9ea6899eb7928f4a9269 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Jan 2024 22:08:41 +0100 Subject: [PATCH 035/198] Add profiler events to texture tool --- Source/Engine/Tools/TextureTool/TextureTool.astc.cpp | 2 ++ Source/Engine/Tools/TextureTool/TextureTool.cpp | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp index ebb9c07af..725c79152 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp @@ -8,10 +8,12 @@ #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Graphics/RenderTools.h" +#include "Engine/Profiler/ProfilerCPU.h" #include bool TextureTool::ConvertAstc(TextureData& dst, const TextureData& src, const PixelFormat dstFormat) { + PROFILE_CPU(); ASSERT(PixelFormatExtensions::IsCompressedASTC(dstFormat)); const int32 blockSize = PixelFormatExtensions::ComputeBlockSize(dstFormat); const int32 bytesPerBlock = 16; // All ASTC blocks use 128 bits diff --git a/Source/Engine/Tools/TextureTool/TextureTool.cpp b/Source/Engine/Tools/TextureTool/TextureTool.cpp index 72e9f4558..f5b925255 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.cpp @@ -15,6 +15,7 @@ #include "Engine/Scripting/Enums.h" #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Graphics/PixelFormatExtensions.h" +#include "Engine/Profiler/ProfilerCPU.h" #if USE_EDITOR #include "Engine/Core/Collections/Dictionary.h" @@ -183,6 +184,7 @@ bool TextureTool::HasAlpha(const StringView& path) bool TextureTool::ImportTexture(const StringView& path, TextureData& textureData) { + PROFILE_CPU(); LOG(Info, "Importing texture from \'{0}\'", path); const auto startTime = DateTime::NowUTC(); @@ -219,6 +221,7 @@ bool TextureTool::ImportTexture(const StringView& path, TextureData& textureData bool TextureTool::ImportTexture(const StringView& path, TextureData& textureData, Options options, String& errorMsg) { + PROFILE_CPU(); LOG(Info, "Importing texture from \'{0}\'. Options: {1}", path, options.ToString()); const auto startTime = DateTime::NowUTC(); @@ -267,9 +270,9 @@ bool TextureTool::ImportTexture(const StringView& path, TextureData& textureData bool TextureTool::ExportTexture(const StringView& path, const TextureData& textureData) { + PROFILE_CPU(); LOG(Info, "Exporting texture to \'{0}\'.", path); const auto startTime = DateTime::NowUTC(); - ImageType type; if (GetImageType(path, type)) return true; @@ -279,7 +282,6 @@ bool TextureTool::ExportTexture(const StringView& path, const TextureData& textu return true; } - // Export #if COMPILE_WITH_DIRECTXTEX const auto failed = ExportTextureDirectXTex(type, path, textureData); #elif COMPILE_WITH_STB @@ -303,7 +305,6 @@ bool TextureTool::ExportTexture(const StringView& path, const TextureData& textu bool TextureTool::Convert(TextureData& dst, const TextureData& src, const PixelFormat dstFormat) { - // Validate input if (src.GetMipLevels() == 0) { LOG(Warning, "Missing source data."); @@ -319,6 +320,7 @@ bool TextureTool::Convert(TextureData& dst, const TextureData& src, const PixelF LOG(Warning, "Converting volume texture data is not supported."); return true; } + PROFILE_CPU(); #if COMPILE_WITH_DIRECTXTEX return ConvertDirectXTex(dst, src, dstFormat); @@ -332,7 +334,6 @@ bool TextureTool::Convert(TextureData& dst, const TextureData& src, const PixelF bool TextureTool::Resize(TextureData& dst, const TextureData& src, int32 dstWidth, int32 dstHeight) { - // Validate input if (src.GetMipLevels() == 0) { LOG(Warning, "Missing source data."); @@ -348,7 +349,7 @@ bool TextureTool::Resize(TextureData& dst, const TextureData& src, int32 dstWidt LOG(Warning, "Resizing volume texture data is not supported."); return true; } - + PROFILE_CPU(); #if COMPILE_WITH_DIRECTXTEX return ResizeDirectXTex(dst, src, dstWidth, dstHeight); #elif COMPILE_WITH_STB @@ -777,6 +778,7 @@ bool TextureTool::GetImageType(const StringView& path, ImageType& type) bool TextureTool::Transform(TextureData& texture, const Function& transformation) { + PROFILE_CPU(); auto sampler = TextureTool::GetSampler(texture.Format); if (!sampler) return true; From df33de71765e1764ae404bec34c57986e36bd5d1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Jan 2024 23:01:33 +0100 Subject: [PATCH 036/198] Add ASTC texture compression on Android --- .../Platform/Android/AndroidPlatformTools.cpp | 122 +++++++++++------- .../Platform/Android/AndroidPlatformTools.h | 2 + .../Cooker/Platform/iOS/iOSPlatformTools.cpp | 12 +- Source/Editor/Cooker/Steps/CookAssetsStep.cpp | 2 + .../Engine/Graphics/PixelFormatExtensions.cpp | 56 +++++++- .../Engine/Graphics/PixelFormatExtensions.h | 5 +- .../Graphics/Textures/StreamingTexture.cpp | 2 +- .../Android/AndroidPlatformSettings.h | 24 ++++ .../Engine/Platform/iOS/iOSPlatformSettings.h | 8 +- 9 files changed, 173 insertions(+), 60 deletions(-) diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp index bd187ed1d..d55ad01e3 100644 --- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp @@ -22,6 +22,11 @@ IMPLEMENT_ENGINE_SETTINGS_GETTER(AndroidPlatformSettings, AndroidPlatform); namespace { + struct AndroidPlatformCache + { + AndroidPlatformSettings::TextureQuality TexturesQuality; + }; + void DeployIcon(const CookingData& data, const TextureData& iconData, const Char* subDir, int32 iconSize, int32 adaptiveIconSize) { const String mipmapPath = data.OriginalOutputPath / TEXT("app/src/main/res") / subDir; @@ -30,6 +35,24 @@ namespace FileSystem::CreateDirectory(mipmapPath); EditorUtilities::ExportApplicationImage(iconData, iconSize, iconSize, PixelFormat::B8G8R8A8_UNorm, iconPath); } + + PixelFormat GetQualityTextureFormat(bool sRGB, PixelFormat format) + { + const auto platformSettings = AndroidPlatformSettings::Get(); + switch (platformSettings->TexturesQuality) + { + case AndroidPlatformSettings::TextureQuality::Uncompressed: + return PixelFormatExtensions::FindUncompressedFormat(format); + case AndroidPlatformSettings::TextureQuality::ASTC_High: + return sRGB ? PixelFormat::ASTC_4x4_UNorm_sRGB : PixelFormat::ASTC_4x4_UNorm; + case AndroidPlatformSettings::TextureQuality::ASTC_Medium: + return sRGB ? PixelFormat::ASTC_6x6_UNorm_sRGB : PixelFormat::ASTC_6x6_UNorm; + case AndroidPlatformSettings::TextureQuality::ASTC_Low: + return sRGB ? PixelFormat::ASTC_8x8_UNorm_sRGB : PixelFormat::ASTC_8x8_UNorm; + default: + return format; + } + } } const Char* AndroidPlatformTools::GetDisplayName() const @@ -54,62 +77,67 @@ ArchitectureType AndroidPlatformTools::GetArchitecture() const PixelFormat AndroidPlatformTools::GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) { - // TODO: add ETC compression support for Android - // TODO: add ASTC compression support for Android - - // BC formats are not widely supported on Android - if (PixelFormatExtensions::IsCompressedBC(format)) - { - switch (format) - { - case PixelFormat::BC1_Typeless: - case PixelFormat::BC2_Typeless: - case PixelFormat::BC3_Typeless: - return PixelFormat::R8G8B8A8_Typeless; - case PixelFormat::BC1_UNorm: - case PixelFormat::BC2_UNorm: - case PixelFormat::BC3_UNorm: - return PixelFormat::R8G8B8A8_UNorm; - case PixelFormat::BC1_UNorm_sRGB: - case PixelFormat::BC2_UNorm_sRGB: - case PixelFormat::BC3_UNorm_sRGB: - return PixelFormat::R8G8B8A8_UNorm_sRGB; - case PixelFormat::BC4_Typeless: - return PixelFormat::R8_Typeless; - case PixelFormat::BC4_UNorm: - return PixelFormat::R8_UNorm; - case PixelFormat::BC4_SNorm: - return PixelFormat::R8_SNorm; - case PixelFormat::BC5_Typeless: - return PixelFormat::R16G16_Typeless; - case PixelFormat::BC5_UNorm: - return PixelFormat::R16G16_UNorm; - case PixelFormat::BC5_SNorm: - return PixelFormat::R16G16_SNorm; - case PixelFormat::BC7_Typeless: - case PixelFormat::BC6H_Typeless: - return PixelFormat::R16G16B16A16_Typeless; - case PixelFormat::BC7_UNorm: - case PixelFormat::BC6H_Uf16: - case PixelFormat::BC6H_Sf16: - return PixelFormat::R16G16B16A16_Float; - case PixelFormat::BC7_UNorm_sRGB: - return PixelFormat::R16G16B16A16_UNorm; - default: - return format; - } - } - switch (format) { - // Not all Android devices support R11G11B10 textures (eg. M6 Note) case PixelFormat::R11G11B10_Float: + // Not all Android devices support R11G11B10 textures (eg. M6 Note) return PixelFormat::R16G16B16A16_UNorm; + case PixelFormat::BC1_Typeless: + case PixelFormat::BC2_Typeless: + case PixelFormat::BC3_Typeless: + case PixelFormat::BC4_Typeless: + case PixelFormat::BC5_Typeless: + case PixelFormat::BC1_UNorm: + case PixelFormat::BC2_UNorm: + case PixelFormat::BC3_UNorm: + case PixelFormat::BC4_UNorm: + case PixelFormat::BC5_UNorm: + return GetQualityTextureFormat(false, format); + case PixelFormat::BC1_UNorm_sRGB: + case PixelFormat::BC2_UNorm_sRGB: + case PixelFormat::BC3_UNorm_sRGB: + case PixelFormat::BC7_UNorm_sRGB: + return GetQualityTextureFormat(true, format); + case PixelFormat::BC4_SNorm: + return PixelFormat::R8_SNorm; + case PixelFormat::BC5_SNorm: + return PixelFormat::R16G16_SNorm; + case PixelFormat::BC6H_Typeless: + case PixelFormat::BC6H_Uf16: + case PixelFormat::BC6H_Sf16: + case PixelFormat::BC7_Typeless: + case PixelFormat::BC7_UNorm: + return PixelFormat::R16G16B16A16_Float; // TODO: ASTC HDR default: return format; } } +void AndroidPlatformTools::LoadCache(CookingData& data, IBuildCache* cache, const Span& bytes) +{ + const auto platformSettings = AndroidPlatformSettings::Get(); + bool invalidTextures = true; + if (bytes.Length() == sizeof(AndroidPlatformCache)) + { + auto* platformCache = (AndroidPlatformCache*)bytes.Get(); + invalidTextures = platformCache->TexturesQuality != platformSettings->TexturesQuality; + } + if (invalidTextures) + { + LOG(Info, "{0} option has been modified.", TEXT("TexturesQuality")); + cache->InvalidateCacheTextures(); + } +} + +Array AndroidPlatformTools::SaveCache(CookingData& data, IBuildCache* cache) +{ + const auto platformSettings = AndroidPlatformSettings::Get(); + AndroidPlatformCache platformCache; + platformCache.TexturesQuality = platformSettings->TexturesQuality; + Array result; + result.Add((const byte*)&platformCache, sizeof(platformCache)); + return result; +} void AndroidPlatformTools::OnBuildStarted(CookingData& data) { // Adjust the cooking output folder to be located inside the Gradle assets directory diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.h b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.h index 59697e758..e782fb54d 100644 --- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.h +++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.h @@ -30,6 +30,8 @@ public: PlatformType GetPlatform() const override; ArchitectureType GetArchitecture() const override; PixelFormat GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) override; + void LoadCache(CookingData& data, IBuildCache* cache, const Span& bytes) override; + Array SaveCache(CookingData& data, IBuildCache* cache) override; void OnBuildStarted(CookingData& data) override; bool OnPostProcess(CookingData& data) override; }; diff --git a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp index 4d50401bd..78f8fdf1f 100644 --- a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp @@ -66,11 +66,13 @@ namespace return result; } - PixelFormat GetQualityTextureFormat(bool sRGB) + PixelFormat GetQualityTextureFormat(bool sRGB, PixelFormat format) { const auto platformSettings = iOSPlatformSettings::Get(); switch (platformSettings->TexturesQuality) { + case iOSPlatformSettings::TextureQuality::Uncompressed: + return PixelFormatExtensions::FindUncompressedFormat(format); case iOSPlatformSettings::TextureQuality::ASTC_High: return sRGB ? PixelFormat::ASTC_4x4_UNorm_sRGB : PixelFormat::ASTC_4x4_UNorm; case iOSPlatformSettings::TextureQuality::ASTC_Medium: @@ -78,7 +80,7 @@ namespace case iOSPlatformSettings::TextureQuality::ASTC_Low: return sRGB ? PixelFormat::ASTC_8x8_UNorm_sRGB : PixelFormat::ASTC_8x8_UNorm; default: - CRASH; + return format; } } } @@ -122,12 +124,12 @@ PixelFormat iOSPlatformTools::GetTextureFormat(CookingData& data, TextureBase* t case PixelFormat::BC3_UNorm: case PixelFormat::BC4_UNorm: case PixelFormat::BC5_UNorm: - return GetQualityTextureFormat(false); + return GetQualityTextureFormat(false, format); case PixelFormat::BC1_UNorm_sRGB: case PixelFormat::BC2_UNorm_sRGB: case PixelFormat::BC3_UNorm_sRGB: case PixelFormat::BC7_UNorm_sRGB: - return GetQualityTextureFormat(true); + return GetQualityTextureFormat(true, format); case PixelFormat::BC4_SNorm: return PixelFormat::R8_SNorm; case PixelFormat::BC5_SNorm: @@ -137,7 +139,7 @@ PixelFormat iOSPlatformTools::GetTextureFormat(CookingData& data, TextureBase* t case PixelFormat::BC6H_Sf16: case PixelFormat::BC7_Typeless: case PixelFormat::BC7_UNorm: - return PixelFormat::R16G16B16A16_Typeless; // TODO: ASTC HDR + return PixelFormat::R16G16B16A16_Float; // TODO: ASTC HDR default: return format; } diff --git a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp index 366a4faef..f53ceae1d 100644 --- a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp +++ b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp @@ -31,6 +31,7 @@ #include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Graphics/Materials/MaterialShader.h" +#include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.h" #include "Engine/Engine/Base/GameBase.h" #include "Engine/Engine/Globals.h" @@ -643,6 +644,7 @@ bool ProcessTextureBase(CookAssetsStep::AssetCookData& data) const auto& assetHeader = asset->StreamingTexture()->GetHeader(); const auto format = asset->Format(); const auto targetFormat = data.Data.Tools->GetTextureFormat(data.Data, asset, format); + CHECK_RETURN(!PixelFormatExtensions::IsTypeless(targetFormat), true); const auto streamingSettings = StreamingSettings::Get(); int32 mipLevelsMax = GPU_MAX_TEXTURE_MIP_LEVELS; if (assetHeader->TextureGroup >= 0 && assetHeader->TextureGroup < streamingSettings->TextureGroups.Count()) diff --git a/Source/Engine/Graphics/PixelFormatExtensions.cpp b/Source/Engine/Graphics/PixelFormatExtensions.cpp index 2f3aa3931..9d9c21fcd 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.cpp +++ b/Source/Engine/Graphics/PixelFormatExtensions.cpp @@ -915,9 +915,9 @@ PixelFormat PixelFormatExtensions::MakeTypelessUNorm(const PixelFormat format) } } -PixelFormat PixelFormatExtensions::FindShaderResourceFormat(const PixelFormat format, bool isSRGB) +PixelFormat PixelFormatExtensions::FindShaderResourceFormat(const PixelFormat format, bool sRGB) { - if (isSRGB) + if (sRGB) { switch (format) { @@ -1000,3 +1000,55 @@ PixelFormat PixelFormatExtensions::FindDepthStencilFormat(const PixelFormat form } return format; } + +PixelFormat PixelFormatExtensions::FindUncompressedFormat(PixelFormat format) +{ + switch (format) + { + case PixelFormat::BC1_Typeless: + case PixelFormat::BC2_Typeless: + case PixelFormat::BC3_Typeless: + return PixelFormat::R8G8B8A8_Typeless; + case PixelFormat::BC1_UNorm: + case PixelFormat::BC2_UNorm: + case PixelFormat::BC3_UNorm: + return PixelFormat::R8G8B8A8_UNorm; + case PixelFormat::BC1_UNorm_sRGB: + case PixelFormat::BC2_UNorm_sRGB: + case PixelFormat::BC3_UNorm_sRGB: + return PixelFormat::R8G8B8A8_UNorm_sRGB; + case PixelFormat::BC4_Typeless: + return PixelFormat::R8_Typeless; + case PixelFormat::BC4_UNorm: + return PixelFormat::R8_UNorm; + case PixelFormat::BC4_SNorm: + return PixelFormat::R8_SNorm; + case PixelFormat::BC5_Typeless: + return PixelFormat::R16G16_Typeless; + case PixelFormat::BC5_UNorm: + return PixelFormat::R16G16_UNorm; + case PixelFormat::BC5_SNorm: + return PixelFormat::R16G16_SNorm; + case PixelFormat::BC7_Typeless: + case PixelFormat::BC6H_Typeless: + return PixelFormat::R16G16B16A16_Typeless; + case PixelFormat::BC7_UNorm: + case PixelFormat::BC6H_Uf16: + case PixelFormat::BC6H_Sf16: + return PixelFormat::R16G16B16A16_Float; + case PixelFormat::BC7_UNorm_sRGB: + return PixelFormat::R16G16B16A16_UNorm; + case PixelFormat::ASTC_4x4_UNorm: + case PixelFormat::ASTC_6x6_UNorm: + case PixelFormat::ASTC_8x8_UNorm: + case PixelFormat::ASTC_10x10_UNorm: + return PixelFormat::R8G8B8A8_UNorm; + case PixelFormat::ASTC_4x4_UNorm_sRGB: + case PixelFormat::ASTC_6x6_UNorm_sRGB: + case PixelFormat::ASTC_8x8_UNorm_sRGB: + case PixelFormat::ASTC_10x10_UNorm_sRGB: + return PixelFormat::R8G8B8A8_UNorm_sRGB; + default: + return format; + } +} diff --git a/Source/Engine/Graphics/PixelFormatExtensions.h b/Source/Engine/Graphics/PixelFormatExtensions.h index 7b793cdfd..7ba460e57 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.h +++ b/Source/Engine/Graphics/PixelFormatExtensions.h @@ -72,7 +72,7 @@ public: /// The . /// Enable/disable partially typeless formats. /// true if the specified is Typeless; otherwise, false. - API_FUNCTION() static bool IsTypeless(PixelFormat format, bool partialTypeless); + API_FUNCTION() static bool IsTypeless(PixelFormat format, bool partialTypeless = true); /// /// Returns true if the is valid. @@ -215,7 +215,8 @@ public: API_FUNCTION() static PixelFormat MakeTypelessUNorm(PixelFormat format); public: - static PixelFormat FindShaderResourceFormat(PixelFormat format, bool bSRGB); + static PixelFormat FindShaderResourceFormat(PixelFormat format, bool sRGB); static PixelFormat FindUnorderedAccessFormat(PixelFormat format); static PixelFormat FindDepthStencilFormat(PixelFormat format); + static PixelFormat FindUncompressedFormat(PixelFormat format); }; diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index ea70a9d9e..bb4273826 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -297,7 +297,7 @@ Task* StreamingTexture::UpdateAllocation(int32 residency) if (texture->Init(desc)) { Streaming.Error = true; - LOG(Error, "Cannot allocate texture {0}.", ToString()); + LOG(Error, "Cannot allocate texture {0}.", texture->GetName()); } if (allocatedResidency != 0) { diff --git a/Source/Engine/Platform/Android/AndroidPlatformSettings.h b/Source/Engine/Platform/Android/AndroidPlatformSettings.h index 974a4c488..08d63b2ef 100644 --- a/Source/Engine/Platform/Android/AndroidPlatformSettings.h +++ b/Source/Engine/Platform/Android/AndroidPlatformSettings.h @@ -48,6 +48,24 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API AutoRotation, }; + /// + /// The output textures quality (compression). + /// + API_ENUM() enum class TextureQuality + { + // Raw image data without any compression algorithm. Mostly for testing or compatibility. + Uncompressed, + // ASTC 4x4 block compression. + API_ENUM(Attributes="EditorDisplay(null, \"ASTC High\")") + ASTC_High, + // ASTC 6x6 block compression. + API_ENUM(Attributes="EditorDisplay(null, \"ASTC Medium\")") + ASTC_Medium, + // ASTC 8x8 block compression. + API_ENUM(Attributes="EditorDisplay(null, \"ASTC Low\")") + ASTC_Low, + }; + /// /// The application package name (eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}. /// @@ -66,6 +84,12 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API API_FIELD(Attributes = "EditorOrder(110), EditorDisplay(\"General\")") ScreenOrientation DefaultOrientation = ScreenOrientation::AutoRotation; + /// + /// The output textures quality (compression). + /// + API_FIELD(Attributes="EditorOrder(500), EditorDisplay(\"General\")") + TextureQuality TexturesQuality = TextureQuality::ASTC_Medium; + /// /// Custom icon texture to use for the application (overrides the default one). /// diff --git a/Source/Engine/Platform/iOS/iOSPlatformSettings.h b/Source/Engine/Platform/iOS/iOSPlatformSettings.h index 4016f2767..ab381fdb9 100644 --- a/Source/Engine/Platform/iOS/iOSPlatformSettings.h +++ b/Source/Engine/Platform/iOS/iOSPlatformSettings.h @@ -51,15 +51,17 @@ API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API /// API_ENUM() enum class TextureQuality { + // Raw image data without any compression algorithm. Mostly for testing or compatibility. + Uncompressed, // ASTC 4x4 block compression. API_ENUM(Attributes="EditorDisplay(null, \"ASTC High\")") - ASTC_High = 0, + ASTC_High, // ASTC 6x6 block compression. API_ENUM(Attributes="EditorDisplay(null, \"ASTC Medium\")") - ASTC_Medium = 1, + ASTC_Medium, // ASTC 8x8 block compression. API_ENUM(Attributes="EditorDisplay(null, \"ASTC Low\")") - ASTC_Low = 2, + ASTC_Low, }; /// From c7baded2db0925faed287da787045824f49a748b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 4 Jan 2024 00:07:35 +0100 Subject: [PATCH 037/198] Fix building engine with `IsMonolithicExecutable` disabled on platforms without `HasModularBuildSupport` --- Source/Tools/Flax.Build/Build/EngineTarget.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Tools/Flax.Build/Build/EngineTarget.cs b/Source/Tools/Flax.Build/Build/EngineTarget.cs index 435453a64..8e6f0fd06 100644 --- a/Source/Tools/Flax.Build/Build/EngineTarget.cs +++ b/Source/Tools/Flax.Build/Build/EngineTarget.cs @@ -58,6 +58,7 @@ namespace Flax.Build /// /// True if target is built as monolithic executable with Main module inside, otherwise built as shared library with separate executable made of Main module only. /// + /// Some platforms might not support modular build and enforce monolithic executable. See public bool IsMonolithicExecutable = true; /// @@ -147,6 +148,8 @@ namespace Flax.Build { if (OutputType == TargetOutputType.Executable && !Configuration.BuildBindingsOnly) { + if (!buildOptions.Platform.HasModularBuildSupport) + return false; return !IsMonolithicExecutable || (!buildOptions.Platform.HasExecutableFileReferenceSupport && UseSymbolsExports); } return false; From e04c98020c11af763ef476045e071aedf4a2d074 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 4 Jan 2024 09:03:39 +0100 Subject: [PATCH 038/198] Fix Android runtime --- .../app/src/main/java/com/flaxengine/GameActivity.java | 2 +- Source/Tools/Flax.Build/Build/EngineTarget.cs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java b/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java index bee34b99d..2a7fc8631 100644 --- a/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java +++ b/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java @@ -44,7 +44,7 @@ public class GameActivity extends NativeActivity { static { try { // Load native libraries - System.loadLibrary("FlaxEngine"); + System.loadLibrary("FlaxGame"); } catch (UnsatisfiedLinkError error) { Log.e("Flax", error.getMessage()); } diff --git a/Source/Tools/Flax.Build/Build/EngineTarget.cs b/Source/Tools/Flax.Build/Build/EngineTarget.cs index 8e6f0fd06..ae3fda23b 100644 --- a/Source/Tools/Flax.Build/Build/EngineTarget.cs +++ b/Source/Tools/Flax.Build/Build/EngineTarget.cs @@ -128,7 +128,7 @@ namespace Flax.Build base.PostBuild(graph, buildOptions); // If building engine executable for platform doesn't support referencing it when linking game shared libraries - if (UseSeparateMainExecutable(buildOptions) && !BuildAsLibrary(buildOptions)) + if (UseSeparateMainExecutable(buildOptions)) { // Build additional executable with Main module only that uses shared library using (new ProfileEventScope("BuildExecutable")) @@ -148,6 +148,8 @@ namespace Flax.Build { if (OutputType == TargetOutputType.Executable && !Configuration.BuildBindingsOnly) { + if (buildOptions.Platform.Target == TargetPlatform.Android) + return false; if (!buildOptions.Platform.HasModularBuildSupport) return false; return !IsMonolithicExecutable || (!buildOptions.Platform.HasExecutableFileReferenceSupport && UseSymbolsExports); @@ -159,8 +161,7 @@ namespace Flax.Build { switch (buildOptions.Platform.Target) { - case TargetPlatform.UWP: - case TargetPlatform.Android: return true; + case TargetPlatform.UWP: return true; default: return false; } } From af91c13b5c0b5601508092a5fedecfb35699afb5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 4 Jan 2024 09:14:27 +0100 Subject: [PATCH 039/198] Fix crash on Mono --- Source/Engine/Scripting/ScriptingObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index 59ac26484..2f0570658 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -433,7 +433,7 @@ bool ScriptingObject::CanCast(const MClass* from, const MClass* to) return true; CHECK_RETURN(from && to, false); -#if PLATFORM_LINUX || PLATFORM_MAC +#if DOTNET_HOST_MONO // Cannot enter GC unsafe region if the thread is not attached MCore::Thread::Attach(); #endif From 697db443ac20f38dd3c81203de2814dda066f9cb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 11 Jan 2024 17:58:02 +0100 Subject: [PATCH 040/198] Skip astc on macOS x64 due to precompiled lib with too new clang ver --- .../Tools/TextureTool/TextureTool.Build.cs | 2 +- Source/ThirdParty/astc/astc.Build.cs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Tools/TextureTool/TextureTool.Build.cs b/Source/Engine/Tools/TextureTool/TextureTool.Build.cs index a6a68617c..f40f28e64 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.Build.cs +++ b/Source/Engine/Tools/TextureTool/TextureTool.Build.cs @@ -58,7 +58,7 @@ public class TextureTool : EngineModule options.PrivateDependencies.Add("bc7enc16"); } } - if (options.Target.IsEditor && options.Platform.Target != TargetPlatform.Linux) // TODO: add ASTC for Editor on Linux + if (options.Target.IsEditor && astc.IsSupported(options)) { // ASTC for mobile (iOS and Android) options.SourceFiles.Add(Path.Combine(FolderPath, "TextureTool.astc.cpp")); diff --git a/Source/ThirdParty/astc/astc.Build.cs b/Source/ThirdParty/astc/astc.Build.cs index 237d0ac81..90494402b 100644 --- a/Source/ThirdParty/astc/astc.Build.cs +++ b/Source/ThirdParty/astc/astc.Build.cs @@ -8,6 +8,22 @@ using Flax.Build.NativeCpp; /// public class astc : DepsModule { + /// + /// Returns true if can use astc lib for a given build setup. + /// + public static bool IsSupported(BuildOptions options) + { + switch (options.Platform.Target) + { + case TargetPlatform.Windows: + return true; + case TargetPlatform.Mac: + return options.Architecture == TargetArchitecture.ARM64; + default: + return false; + } + } + /// public override void Init() { From 1da803c5fb486d747b0521043a8c369bf4c62996 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 11 Jan 2024 17:59:34 +0100 Subject: [PATCH 041/198] Fix compilation without GPU resources naming enabled --- Source/Engine/Graphics/Textures/StreamingTexture.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index bb4273826..9dc1ef50c 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -297,7 +297,11 @@ Task* StreamingTexture::UpdateAllocation(int32 residency) if (texture->Init(desc)) { Streaming.Error = true; - LOG(Error, "Cannot allocate texture {0}.", texture->GetName()); +#if GPU_ENABLE_RESOURCE_NAMING + LOG(Error, "Cannot allocate texture {0}", texture->GetName()); +#else + LOG(Error, "Cannot allocate texture"); +#endif } if (allocatedResidency != 0) { From 5b3e7f0bf89144403bd14ca230bd720232b3894f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Jan 2024 13:44:01 +0100 Subject: [PATCH 042/198] Fix `FileSystem::CopyDirectory` not failing when source folder is missing --- Source/Engine/Platform/Base/FileSystemBase.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Source/Engine/Platform/Base/FileSystemBase.cpp b/Source/Engine/Platform/Base/FileSystemBase.cpp index 3d0c053d5..d4179e544 100644 --- a/Source/Engine/Platform/Base/FileSystemBase.cpp +++ b/Source/Engine/Platform/Base/FileSystemBase.cpp @@ -240,12 +240,7 @@ bool FileSystemBase::CopyFile(const String& dst, const String& src) bool FileSystemBase::CopyDirectory(const String& dst, const String& src, bool withSubDirectories) { - // Check if source exists - if (!FileSystem::DirectoryExists(*src)) - return false; - - // Copy - return FileSystemBase::DirectoryCopyHelper(dst, src, withSubDirectories); + return !FileSystem::DirectoryExists(*src) || FileSystemBase::DirectoryCopyHelper(dst, src, withSubDirectories); } uint64 FileSystemBase::GetDirectorySize(const StringView& path) From 9a70f698b9cf0fb3cd0fa3923e59696baf72b31f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Jan 2024 13:44:50 +0100 Subject: [PATCH 043/198] Various improvements to building nethost --- Source/Editor/Cooker/Steps/DeployDataStep.cpp | 2 ++ Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs | 2 +- Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs | 7 ++++++- Source/Tools/Flax.Build/Deps/Dependency.cs | 3 --- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Cooker/Steps/DeployDataStep.cpp b/Source/Editor/Cooker/Steps/DeployDataStep.cpp index 5d7416080..9f95dbdb5 100644 --- a/Source/Editor/Cooker/Steps/DeployDataStep.cpp +++ b/Source/Editor/Cooker/Steps/DeployDataStep.cpp @@ -229,6 +229,8 @@ bool DeployDataStep::Perform(CookingData& data) srcDotnetLibs /= TEXT("../lib/net8.0"); } } + LOG(Info, "Copying .NET files from {} to {}", packFolder, dstDotnet); + LOG(Info, "Copying .NET files from {} to {}", srcDotnetLibs, dstDotnetLibs); FileSystem::CopyFile(dstDotnet / TEXT("LICENSE.TXT"), packFolder / TEXT("LICENSE.txt")); FileSystem::CopyFile(dstDotnet / TEXT("LICENSE.TXT"), packFolder / TEXT("LICENSE.TXT")); FileSystem::CopyFile(dstDotnet / TEXT("THIRD-PARTY-NOTICES.TXT"), packFolder / TEXT("ThirdPartyNotices.txt")); diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs index 8a7050c9c..0775e66da 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs @@ -309,7 +309,7 @@ namespace Flax.Build // Peek class library folder var coreLibPaths = Directory.GetFiles(aotAssembliesPath, "System.Private.CoreLib.dll", SearchOption.AllDirectories); if (coreLibPaths.Length != 1) - throw new Exception("Invalid C# class library setup in " + aotAssembliesPath); + throw new Exception($"Invalid C# class library setup in '{aotAssembliesPath}' (missing C# dll files)"); var dotnetLibPath = Utilities.NormalizePath(Path.GetDirectoryName(coreLibPaths[0])); Log.Info("Class library found in: " + dotnetLibPath); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs index 1fcbb6ded..f5eed36d7 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs @@ -30,6 +30,7 @@ namespace Flax.Deps.Dependencies return new[] { TargetPlatform.PS4, + TargetPlatform.Switch, }; case TargetPlatform.Linux: return new[] @@ -254,7 +255,8 @@ namespace Flax.Deps.Dependencies Utilities.Run("cmake", "--version", null, null, Utilities.RunOptions.ThrowExceptionOnError); // Get the source - CloneGitRepo(root, "https://github.com/FlaxEngine/dotnet-runtime.git", "flax-master", null, true); + CloneGitRepo(root, "https://github.com/FlaxEngine/dotnet-runtime.git", null, null, true); + GitCheckout(root, "flax-master"); SetupDirectory(Path.Combine(root, "src", "external"), false); /* @@ -285,6 +287,9 @@ namespace Flax.Deps.Dependencies case TargetPlatform.Android: Build(options, platform, TargetArchitecture.ARM64); break; + case TargetPlatform.Switch: + Build(options, platform, TargetArchitecture.ARM64); + break; } } diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index 4e940c824..bcc5ce056 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -140,11 +140,8 @@ namespace Flax.Deps if (submodules) Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.DefaultTool); } - if (commit != null) - { Utilities.Run("git", string.Format("reset --hard {0}", commit), null, null, Utilities.RunOptions.DefaultTool); - } } /// From 3bdc70a4c6a2ebc3a6d9ea2535b112cd0b8641df Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 15 Jan 2024 17:30:45 +0100 Subject: [PATCH 044/198] Add automatic building of `nethost` for consoles --- .../Flax.Build/Deps/Dependencies/nethost.cs | 192 ++++++++++-------- .../Tools/Flax.Build/Utilities/Utilities.cs | 20 +- 2 files changed, 122 insertions(+), 90 deletions(-) diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs index f5eed36d7..7966870a3 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs @@ -9,8 +9,6 @@ using Flax.Build.Platforms; using Flax.Deploy; using System.IO.Compression; -#pragma warning disable 0219 - namespace Flax.Deps.Dependencies { /// @@ -30,6 +28,7 @@ namespace Flax.Deps.Dependencies return new[] { TargetPlatform.PS4, + TargetPlatform.PS5, TargetPlatform.Switch, }; case TargetPlatform.Linux: @@ -46,6 +45,7 @@ namespace Flax.Deps.Dependencies public override bool BuildByDefault => false; private string root; + private bool cleanArtifacts; private void Build(BuildOptions options, TargetPlatform targetPlatform, TargetArchitecture architecture) { @@ -55,13 +55,14 @@ namespace Flax.Deps.Dependencies // Clean output directory var artifacts = Path.Combine(root, "artifacts"); - SetupDirectory(artifacts, true); + SetupDirectory(artifacts, cleanArtifacts); + cleanArtifacts = true; // Peek options - string os, arch, runtimeFlavor, subset, hostRuntimeName = DotNetSdk.GetHostRuntimeIdentifier(targetPlatform, architecture), buildArgsBase = string.Empty; - bool setupVersion = false; - string[] hostRuntimeFiles = Array.Empty(); + string os, arch, runtimeFlavor, hostRuntimeName = DotNetSdk.GetHostRuntimeIdentifier(targetPlatform, architecture), buildArgs = string.Empty, buildMonoAotCrossArgs = string.Empty; + bool setupVersion = false, buildMonoAotCross = false; var envVars = new Dictionary(); + envVars.Add("VisualStudioVersion", null); // Unset this so 'init-vs-env.cmd' will properly init VS Environment switch (architecture) { case TargetArchitecture.x86: @@ -83,36 +84,39 @@ namespace Flax.Deps.Dependencies case TargetPlatform.Windows: os = "windows"; runtimeFlavor = "CoreCLR"; - subset = "clr"; break; case TargetPlatform.Linux: - os = "Linux"; + os = "linux"; runtimeFlavor = "CoreCLR"; - subset = "clr"; break; case TargetPlatform.Mac: - os = "OSX"; + os = "osx"; runtimeFlavor = "CoreCLR"; - subset = "clr"; break; case TargetPlatform.Android: - os = "Android"; + os = "android"; runtimeFlavor = "Mono"; - subset = "mono+libs"; break; case TargetPlatform.PS4: - os = "PS4"; + case TargetPlatform.PS5: + case TargetPlatform.Switch: runtimeFlavor = "Mono"; - subset = "mono+libs"; setupVersion = true; - buildArgsBase = " /p:RuntimeOS=ps4 -cmakeargs \"-DCLR_CROSS_COMPONENTS_BUILD=1\""; - hostRuntimeFiles = new[] + buildMonoAotCross = true; + switch (targetPlatform) { - "coreclr_delegates.h", - "hostfxr.h", - "nethost.h", - "libnethost.a", - }; + case TargetPlatform.PS4: + os = "ps4"; + break; + case TargetPlatform.PS5: + os = "ps5"; + break; + case TargetPlatform.Switch: + os = "switch"; + break; + default: throw new InvalidPlatformException(targetPlatform); + } + buildArgs = $" /p:RuntimeOS={os} -subset mono+libs -cmakeargs \"-DDISABLE_JIT=1-DENABLE_PERFTRACING=0-DDISABLE_REFLECTION_EMIT=1-DDISABLE_EVENTPIPE=1-DDISABLE_COM=1-DDISABLE_PROFILER=1-DDISABLE_COMPONENTS=1\" /p:FeaturePerfTracing=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false"; break; default: throw new InvalidPlatformException(targetPlatform); } @@ -175,74 +179,85 @@ namespace Flax.Deps.Dependencies } // Build - buildArgsBase = $"-os {os} -a {arch} -f {framework} -c {configuration} -lc {configuration} -rc {configuration} -rf {runtimeFlavor}{buildArgsBase}"; - //foreach (var buildStep in new[] { subset, "host.pkg", "packs.product" }) - /*var buildStep = "host.pkg"; + if (runtimeFlavor == "CoreCLR") + buildArgs = $"-os {os} -a {arch} -f {framework} -c {configuration} -lc {configuration} -rc {configuration} -rf {runtimeFlavor}{buildArgs}"; + else if (runtimeFlavor == "Mono") + buildArgs = $"-os {os} -a {arch} -c {configuration} -rf {runtimeFlavor}{buildArgs}"; + Utilities.Run(Path.Combine(root, buildScript), buildArgs, null, root, Utilities.RunOptions.DefaultTool, envVars); + if (buildMonoAotCross) { - var buildArgs = $"{buildArgsBase} -s {buildStep}"; - if (BuildPlatform == TargetPlatform.Windows) - { - // For some reason running from Visual Studio fails the build so use command shell - //buildArgs = $"/C {buildScript} {buildArgs}"; - //buildApp = "cmd.exe"; - // TODO: maybe write command line into bat file and run it here? - WinAPI.SetClipboard($"{buildScript} {buildArgs}"); - WinAPI.MessageBox.Show($"Open console command in folder '{root}' and run command from clipboard. Then close this dialog.", "Run command manually", WinAPI.MessageBox.Buttons.Ok); - } - else - { - //Utilities.Run(Path.Combine(root, buildScript), buildArgs, null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); - } - }*/ - Utilities.Run(Path.Combine(root, buildScript), buildArgsBase, null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); + buildMonoAotCrossArgs = $"-c {configuration} -rf {runtimeFlavor} -subset mono /p:BuildMonoAotCrossCompiler=true /p:BuildMonoAOTCrossCompilerOnly=true /p:TargetOS={os} /p:HostOS=windows -cmakeargs \"-DCMAKE_CROSSCOMPILING=True\"{buildMonoAotCrossArgs}"; + Utilities.Run(Path.Combine(root, buildScript), buildMonoAotCrossArgs, null, root, Utilities.RunOptions.DefaultTool, envVars); + } // Deploy build products + var privateCoreLib = "System.Private.CoreLib.dll"; var dstBinaries = GetThirdPartyFolder(options, targetPlatform, architecture); - var srcHostRuntime = Path.Combine(artifacts, "bin", $"{hostRuntimeName}.{configuration}", "corehost"); - foreach (var file in hostRuntimeFiles) - { - Utilities.FileCopy(Path.Combine(srcHostRuntime, file), Path.Combine(dstBinaries, file)); - } - var dstDotnet = Path.Combine(GetBinariesFolder(options, targetPlatform), "Dotnet"); - var dstClassLibrary = Path.Combine(dstDotnet, "shared", "Microsoft.NETCore.App", version); - SetupDirectory(dstClassLibrary, true); - foreach (var file in new[] - { - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - }) - { + var dstPlatform = Path.Combine(options.PlatformsFolder, targetPlatform.ToString()); + var dstDotnet = Path.Combine(dstPlatform, "Dotnet"); + foreach (var file in new[] { "LICENSE.TXT", "THIRD-PARTY-NOTICES.TXT" }) Utilities.FileCopy(Path.Combine(root, file), Path.Combine(dstDotnet, file)); - } - var srcDotnetLibsPkg = Path.Combine(artifacts, "packages", "Release", "Shipping", $"Microsoft.NETCore.App.Runtime.Mono.{hostRuntimeName}.{version}.nupkg"); - if (!File.Exists(srcDotnetLibsPkg)) - throw new Exception($"Missing .NET Core App class library package at '{srcDotnetLibsPkg}'"); - var unpackTemp = Path.Combine(Path.GetDirectoryName(srcDotnetLibsPkg), "UnpackTemp"); - SetupDirectory(unpackTemp, true); - using (var zip = ZipFile.Open(srcDotnetLibsPkg, ZipArchiveMode.Read)) + if (runtimeFlavor == "CoreCLR") { - zip.ExtractToDirectory(unpackTemp); - } - var privateCorelib = "System.Private.CoreLib.dll"; - Utilities.FileCopy(Path.Combine(unpackTemp, "runtimes", hostRuntimeName, "native", privateCorelib), Path.Combine(dstClassLibrary, privateCorelib)); - Utilities.DirectoryCopy(Path.Combine(unpackTemp, "runtimes", hostRuntimeName, "lib", "net8.0"), dstClassLibrary, false, true); - // TODO: host/fxr//hostfxr.dll - // TODO: shared/Microsoft.NETCore.App//hostpolicy.dl - // TODO: shared/Microsoft.NETCore.App//System.IO.Compression.Native.dll - if (runtimeFlavor == "Mono") - { - // TODO: implement automatic deployment based on the setup: - // PS4 outputs mono into artifacts\obj\mono\PS4.x64.Release\out - // PS4 outputs native libs into artifacts\bin\native\net7.0-PS4-Release-x64\lib - // PS4 outputs System.Private.CoreLib lib into artifacts\bin\mono\PS4.x64.Release - // PS4 outputs C# libs into artifacts\bin\runtime\net7.0-PS4.Release.x64 - // PS4 outputs AOT compiler into artifacts\bin\mono\PS4.x64.Release\cross\ps4-x64 + var srcHostRuntime = Path.Combine(artifacts, "bin", $"{hostRuntimeName}.{configuration}", "corehost"); + var dstClassLibrary = Path.Combine(dstDotnet, "shared", "Microsoft.NETCore.App", version); + SetupDirectory(dstClassLibrary, true); + var srcDotnetLibsPkg = Path.Combine(artifacts, "packages", "Release", "Shipping", $"Microsoft.NETCore.App.Runtime.Mono.{hostRuntimeName}.{version}.nupkg"); + if (!File.Exists(srcDotnetLibsPkg)) + throw new Exception($"Missing .NET Core App class library package at '{srcDotnetLibsPkg}'"); + var unpackTemp = Path.Combine(Path.GetDirectoryName(srcDotnetLibsPkg), "UnpackTemp"); + SetupDirectory(unpackTemp, true); + using (var zip = ZipFile.Open(srcDotnetLibsPkg, ZipArchiveMode.Read)) + zip.ExtractToDirectory(unpackTemp); + Utilities.FileCopy(Path.Combine(unpackTemp, "runtimes", hostRuntimeName, "native", privateCoreLib), Path.Combine(dstClassLibrary, privateCoreLib)); + Utilities.DirectoryCopy(Path.Combine(unpackTemp, "runtimes", hostRuntimeName, "lib", framework), dstClassLibrary, false, true); + // TODO: host/fxr//hostfxr.dll + // TODO: shared/Microsoft.NETCore.App//hostpolicy.dl + // TODO: shared/Microsoft.NETCore.App//System.IO.Compression.Native.dll Utilities.DirectoryCopy(Path.Combine(unpackTemp, "runtimes", hostRuntimeName, "native"), Path.Combine(dstDotnet, "native"), true, true); - Utilities.FileDelete(Path.Combine(dstDotnet, "native", privateCorelib)); + Utilities.FileDelete(Path.Combine(dstDotnet, "native", privateCoreLib)); + Utilities.DirectoriesDelete(unpackTemp); + } + else if (runtimeFlavor == "Mono") + { + // Native libs + var src1 = Path.Combine(artifacts, "obj", "mono", $"{os}.{arch}.{configuration}", "out"); + if (!Directory.Exists(src1)) + throw new DirectoryNotFoundException(src1); + var src2 = Path.Combine(artifacts, "bin", "native", $"{framework}-{os}-{configuration}-{arch}"); + if (!Directory.Exists(src1)) + throw new DirectoryNotFoundException(src2); + foreach (var file in new[] + { + "libmonosgen-2.0.a", + "libmono-profiler-aot.a", + }) + Utilities.FileCopy(Path.Combine(src1, "lib", file), Path.Combine(dstBinaries, file)); + foreach (var file in new[] + { + "libSystem.Globalization.Native.a", + "libSystem.IO.Compression.Native.a", + "libSystem.IO.Ports.Native.a", + "libSystem.Native.a", + }) + Utilities.FileCopy(Path.Combine(src2, "lib", file), Path.Combine(dstBinaries, file)); + + // Include headers + Utilities.DirectoryDelete(Path.Combine(dstBinaries, "include")); + Utilities.DirectoryCopy(Path.Combine(src1, "include"), Path.Combine(dstBinaries, "include"), true, true); + + if (buildMonoAotCross) + { + // AOT compiler + Utilities.FileCopy(Path.Combine(artifacts, "bin", "mono", $"{os}.x64.{configuration}", "cross", $"{os}-x64", "mono-aot-cross.exe"), Path.Combine(dstPlatform, "Binaries", "Tools", "mono-aot-cross.exe")); + } + + // Class library + var dstDotnetLib = Path.Combine(dstPlatform, "Dotnet", "lib", framework); + SetupDirectory(dstDotnetLib, true); + Utilities.FileCopy(Path.Combine(artifacts, "bin", "mono", $"{os}.{arch}.{configuration}", privateCoreLib), Path.Combine(dstDotnetLib, privateCoreLib)); + Utilities.DirectoryCopy(Path.Combine(artifacts, "bin", "runtime", $"{framework}-{os}-{configuration}-{arch}"), dstDotnetLib, false, true, "*.dll"); } - else - throw new InvalidPlatformException(targetPlatform); - Utilities.DirectoriesDelete(unpackTemp); } /// @@ -255,9 +270,12 @@ namespace Flax.Deps.Dependencies Utilities.Run("cmake", "--version", null, null, Utilities.RunOptions.ThrowExceptionOnError); // Get the source - CloneGitRepo(root, "https://github.com/FlaxEngine/dotnet-runtime.git", null, null, true); - GitCheckout(root, "flax-master"); - SetupDirectory(Path.Combine(root, "src", "external"), false); + if (!Directory.Exists(Path.Combine(root, ".git"))) + { + CloneGitRepo(root, "https://github.com/FlaxEngine/dotnet-runtime.git", null, null, true); + GitCheckout(root, "flax-master-8"); + SetupDirectory(Path.Combine(root, "src", "external"), false); + } /* * Mono AOT for Windows: @@ -267,10 +285,10 @@ namespace Flax.Deps.Dependencies * .\build.cmd -c release -runtimeFlavor mono -subset mono /p:BuildMonoAotCrossCompiler=true /p:BuildMonoAOTCrossCompilerOnly=true * * Mono AOT for PS4: - * .\build.cmd -os PS4 -a x64 /p:RuntimeOS=ps4 -c release -runtimeFlavor mono -subset mono+libs -cmakeargs "-DDISABLE_JIT=1-DENABLE_PERFTRACING=0-DDISABLE_REFLECTION_EMIT=1-DDISABLE_EVENTPIPE=1-DDISABLE_COM=1-DDISABLE_PROFILER=1-DDISABLE_COMPONENTS=1" /p:FeaturePerfTracing=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false + * .\build.cmd -os ps4 -a x64 /p:RuntimeOS=ps4 -c release -runtimeFlavor mono -subset mono+libs -cmakeargs "-DDISABLE_JIT=1-DENABLE_PERFTRACING=0-DDISABLE_REFLECTION_EMIT=1-DDISABLE_EVENTPIPE=1-DDISABLE_COM=1-DDISABLE_PROFILER=1-DDISABLE_COMPONENTS=1" /p:FeaturePerfTracing=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false * * Mono AOT cross-compiler for PS4: - * .\build.cmd -c release -runtimeFlavor mono -subset mono /p:BuildMonoAotCrossCompiler=true /p:BuildMonoAOTCrossCompilerOnly=true /p:TargetOS=PS4 /p:HostOS=windows -cmakeargs "-DCMAKE_CROSSCOMPILING=True" + * .\build.cmd -c release -runtimeFlavor mono -subset mono /p:BuildMonoAotCrossCompiler=true /p:BuildMonoAOTCrossCompilerOnly=true /p:TargetOS=ps4 /p:HostOS=windows -cmakeargs "-DCMAKE_CROSSCOMPILING=True" */ foreach (var platform in options.Platforms) diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index bf7c24e1a..02019b30d 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -320,6 +320,11 @@ namespace Flax.Build /// ConsoleLogOutput = 1 << 7, + /// + /// Enables to run process via Shell instead of standard process startup. + /// + Shell = 1 << 8, + /// /// The default options. /// @@ -398,11 +403,12 @@ namespace Flax.Build } bool redirectStdOut = (options & RunOptions.NoStdOutRedirect) != RunOptions.NoStdOutRedirect; + bool shell = options.HasFlag(RunOptions.Shell); Process proc = new Process(); proc.StartInfo.FileName = app; proc.StartInfo.Arguments = commandLine != null ? commandLine : ""; - proc.StartInfo.UseShellExecute = false; + proc.StartInfo.UseShellExecute = shell; proc.StartInfo.RedirectStandardInput = input != null; proc.StartInfo.CreateNoWindow = true; @@ -411,7 +417,7 @@ namespace Flax.Build proc.StartInfo.WorkingDirectory = workspace; } - if (redirectStdOut) + if (redirectStdOut && !shell) { proc.StartInfo.RedirectStandardOutput = true; proc.StartInfo.RedirectStandardError = true; @@ -427,16 +433,24 @@ namespace Flax.Build } } - if (envVars != null) + if (envVars != null && !shell) { foreach (var env in envVars) { if (env.Key == "PATH") { + // Append to path proc.StartInfo.EnvironmentVariables[env.Key] = env.Value + ';' + proc.StartInfo.EnvironmentVariables[env.Key]; } + else if (env.Value == null) + { + // Remove variable + if (proc.StartInfo.EnvironmentVariables.ContainsKey(env.Key)) + proc.StartInfo.EnvironmentVariables.Remove(env.Key); + } else { + // Set variable proc.StartInfo.EnvironmentVariables[env.Key] = env.Value; } } From 6d6b678b2975a44c6125b5453802e81f67f8c3a6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 18 Jan 2024 16:03:27 +0100 Subject: [PATCH 045/198] Minor changes from porting --- Source/Engine/Content/Loading/ContentLoadingManager.cpp | 2 +- Source/Engine/Scripting/Runtime/DotNet.cpp | 7 +++++++ .../Flax.Build/Platforms/Windows/WindowsToolchainBase.cs | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Content/Loading/ContentLoadingManager.cpp b/Source/Engine/Content/Loading/ContentLoadingManager.cpp index 7fd63a217..222fe2424 100644 --- a/Source/Engine/Content/Loading/ContentLoadingManager.cpp +++ b/Source/Engine/Content/Loading/ContentLoadingManager.cpp @@ -163,7 +163,7 @@ bool ContentLoadingManagerService::Init() // Calculate amount of loading threads to use const CPUInfo cpuInfo = Platform::GetCPUInfo(); - const int32 count = Math::Clamp(static_cast(LOADING_THREAD_PER_LOGICAL_CORE * (float)cpuInfo.LogicalProcessorCount), 1, 12); + const int32 count = Math::Clamp(Math::CeilToInt(LOADING_THREAD_PER_LOGICAL_CORE * (float)cpuInfo.LogicalProcessorCount), 1, 12); LOG(Info, "Creating {0} content loading threads...", count); // Create loading threads diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index c0b93c2cf..426fe3c28 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -1891,6 +1891,13 @@ void OnLogCallback(const char* logDomain, const char* logLevel, const char* mess { LOG(Info, "Message: {0} | Domain: {1}", msg, currentDomain); } +#if DOTNET_HOST_MONO && !BUILD_RELEASE + if (errorLevel <= 2) + { + // Mono backend ends with fatal assertions so capture crash info (eg. stack trace) + CRASH; + } +#endif } void OnPrintCallback(const char* string, mono_bool isStdout) diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index bf761da60..aa0854e9e 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -633,6 +633,7 @@ namespace Flax.Build.Platforms var pchSourceFile = Path.Combine(options.IntermediateFolder, Path.ChangeExtension(pchFilName, "cpp")); var contents = Bindings.BindingsGenerator.GetStringBuilder(); contents.AppendLine("// This code was auto-generated. Do not modify it."); + // TODO: write compiler version to properly rebuild pch on Visual Studio updates contents.Append("// Compiler: ").AppendLine(_compilerPath); contents.Append("#include \"").Append(pchSource).AppendLine("\""); Utilities.WriteFileIfChanged(pchSourceFile, contents.ToString()); From b4186052df27dc16382b3d15bfe34975d7d2b99a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 18 Jan 2024 16:04:22 +0100 Subject: [PATCH 046/198] Add printing stack trace even when not using log file --- Source/Engine/Platform/Base/PlatformBase.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Base/PlatformBase.cpp b/Source/Engine/Platform/Base/PlatformBase.cpp index 9a6b1b9dc..2c9f5bb5c 100644 --- a/Source/Engine/Platform/Base/PlatformBase.cpp +++ b/Source/Engine/Platform/Base/PlatformBase.cpp @@ -269,7 +269,6 @@ void PlatformBase::Fatal(const Char* msg, void* context) Globals::ExitCode = -1; // Collect crash info (platform-dependant implementation that might collect stack trace and/or create memory dump) - if (Log::Logger::LogFilePath.HasChars()) { // Log separation for crash info Log::Logger::WriteFloor(); @@ -320,7 +319,9 @@ void PlatformBase::Fatal(const Char* msg, void* context) LOG(Error, "Process Used Physical Memory: {0}", Utilities::BytesToText(processMemoryStats.UsedPhysicalMemory)); LOG(Error, "Process Used Virtual Memory: {0}", Utilities::BytesToText(processMemoryStats.UsedVirtualMemory)); } - + } + if (Log::Logger::LogFilePath.HasChars()) + { // Create separate folder with crash info const String crashDataFolder = String(StringUtils::GetDirectoryName(Log::Logger::LogFilePath)) / TEXT("Crash_") + StringUtils::GetFileNameWithoutExtension(Log::Logger::LogFilePath).Substring(4); FileSystem::CreateDirectory(crashDataFolder); From 8e3123a129afabb632252865d10f5068406aa847 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 18 Jan 2024 16:06:34 +0100 Subject: [PATCH 047/198] Add better stack trace reporting in crashes when running on non-Windows platforms --- Source/Engine/Platform/Base/PlatformBase.cpp | 22 +++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Platform/Base/PlatformBase.cpp b/Source/Engine/Platform/Base/PlatformBase.cpp index 2c9f5bb5c..e2244d306 100644 --- a/Source/Engine/Platform/Base/PlatformBase.cpp +++ b/Source/Engine/Platform/Base/PlatformBase.cpp @@ -283,24 +283,32 @@ void PlatformBase::Fatal(const Char* msg, void* context) LOG(Error, "Stack trace:"); for (const auto& frame : stackFrames) { - char chr = 0; + // Remove any path from the module name int32 num = StringUtils::Length(frame.ModuleName); - while (num > 0 && chr != '\\' && chr != '/' && chr != ':') - chr = frame.ModuleName[--num]; - StringAsUTF16 moduleName(frame.ModuleName + num + 1); + while (num > 0 && frame.ModuleName[num - 1] != '\\' && frame.ModuleName[num - 1] != '/' && frame.ModuleName[num - 1] != ':') + num--; + StringAsUTF16 moduleName(frame.ModuleName + num); + num = moduleName.Length(); + if (num != 0 && num < ARRAY_COUNT(StackFrame::ModuleName) - 2) + { + // Append separator between module name and the function name + ((Char*)moduleName.Get())[num++] = '!'; + ((Char*)moduleName.Get())[num] = 0; + } + StringAsUTF16 functionName(frame.FunctionName); if (StringUtils::Length(frame.FileName) != 0) { StringAsUTF16 fileName(frame.FileName); - LOG(Error, " at {0}!{1}() in {2}:line {3}", moduleName.Get(), functionName.Get(), fileName.Get(), frame.LineNumber); + LOG(Error, " at {0}{1}() in {2}:line {3}", moduleName.Get(), functionName.Get(), fileName.Get(), frame.LineNumber); } else if (StringUtils::Length(frame.FunctionName) != 0) { - LOG(Error, " at {0}!{1}()", moduleName.Get(), functionName.Get()); + LOG(Error, " at {0}{1}()", moduleName.Get(), functionName.Get()); } else if (StringUtils::Length(frame.ModuleName) != 0) { - LOG(Error, " at {0} 0x{1:x}", moduleName.Get(), (uint64)frame.ProgramCounter); + LOG(Error, " at {0}0x{1:x}", moduleName.Get(), (uint64)frame.ProgramCounter); } else { From 37fe11e19f0fef745c42ea80e50f915706bcb5eb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 19 Jan 2024 20:33:28 +0100 Subject: [PATCH 048/198] Add missing mono lib to consoles build --- Source/ThirdParty/nethost/nethost.Build.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/ThirdParty/nethost/nethost.Build.cs b/Source/ThirdParty/nethost/nethost.Build.cs index 75a8a3ab6..b6dfdb1fa 100644 --- a/Source/ThirdParty/nethost/nethost.Build.cs +++ b/Source/ThirdParty/nethost/nethost.Build.cs @@ -78,6 +78,7 @@ public class nethost : ThirdPartyModule options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libSystem.Native.a")); options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libSystem.IO.Ports.Native.a")); options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libSystem.IO.Compression.Native.a")); + options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libSystem.Globalization.Native.a")); break; case TargetPlatform.Android: options.PublicDefinitions.Add("USE_MONO_DYNAMIC_LIB"); From a0e127b9ccaafbf1f6750c7a1ad938069b6b9903 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Jan 2024 21:17:58 +0100 Subject: [PATCH 049/198] Various porting patches --- Source/Engine/Scripting/Runtime/DotNet.cpp | 7 +++--- Source/Engine/Scripting/Scripting.cs | 8 +++++-- Source/ThirdParty/nethost/nethost.Build.cs | 22 ++++++++++++++----- .../Tools/Flax.Build/Utilities/Utilities.cs | 10 +++++++++ 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 426fe3c28..0b5993477 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -281,17 +281,18 @@ bool MCore::LoadEngine() flaxLibraryPath = ::String(StringUtils::GetDirectoryName(Platform::GetExecutableFilePath())) / StringUtils::GetFileName(flaxLibraryPath); } #endif +#if !PLATFORM_SWITCH if (!FileSystem::FileExists(flaxLibraryPath)) { LOG(Error, "Flax Engine native library file is missing ({0})", flaxLibraryPath); } +#endif RegisterNativeLibrary("FlaxEngine", flaxLibraryPath.Get()); MRootDomain = New("Root"); MDomains.Add(MRootDomain); - void* GetRuntimeInformationPtr = GetStaticMethodPointer(TEXT("GetRuntimeInformation")); - char* buildInfo = CallStaticMethod(GetRuntimeInformationPtr); + char* buildInfo = CallStaticMethod(GetStaticMethodPointer(TEXT("GetRuntimeInformation"))); LOG(Info, ".NET runtime version: {0}", ::String(buildInfo)); MCore::GC::FreeMemory(buildInfo); @@ -2047,7 +2048,7 @@ bool InitHostfxr() #endif // Platform-specific setup -#if PLATFORM_IOS +#if PLATFORM_IOS || PLATFORM_SWITCH setenv("MONO_AOT_MODE", "aot", 1); setenv("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1", 1); #endif diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index 188333ff1..516a85628 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -180,8 +180,8 @@ namespace FlaxEngine private static void OnLocalizationChanged() { - // iOS uses globalization-invariant mode so ignore it -#if !PLATFORM_IOS + // Invariant-globalization only (see InitHostfxr with Mono) +#if !(PLATFORM_IOS || PLATFORM_SWITCH) var currentThread = Thread.CurrentThread; var language = Localization.CurrentLanguage; if (language != null) @@ -238,6 +238,10 @@ namespace FlaxEngine internal static ManagedHandle CultureInfoToManaged(int lcid) { +#if PLATFORM_IOS || PLATFORM_SWITCH + // Invariant-globalization only (see InitHostfxr with Mono) + lcid = 0; +#endif return ManagedHandle.Alloc(new CultureInfo(lcid)); } diff --git a/Source/ThirdParty/nethost/nethost.Build.cs b/Source/ThirdParty/nethost/nethost.Build.cs index b6dfdb1fa..53d974b44 100644 --- a/Source/ThirdParty/nethost/nethost.Build.cs +++ b/Source/ThirdParty/nethost/nethost.Build.cs @@ -74,12 +74,24 @@ public class nethost : ThirdPartyModule case TargetPlatform.Switch: case TargetPlatform.PS4: case TargetPlatform.PS5: - options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libmonosgen-2.0.a")); - options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libSystem.Native.a")); - options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libSystem.IO.Ports.Native.a")); - options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libSystem.IO.Compression.Native.a")); - options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libSystem.Globalization.Native.a")); + { + var type = Flax.Build.Utilities.GetType($"Flax.Build.Platforms.{options.Platform.Target}.nethost"); + var onLink = type?.GetMethod("OnLink"); + if (onLink != null) + { + // Custom linking logic overriden by platform tools + onLink.Invoke(null, new object[] { options, hostRuntime.Path }); + } + else + { + options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libmonosgen-2.0.a")); + options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libSystem.Native.a")); + options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libSystem.IO.Ports.Native.a")); + options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libSystem.IO.Compression.Native.a")); + options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libSystem.Globalization.Native.a")); + } break; + } case TargetPlatform.Android: options.PublicDefinitions.Add("USE_MONO_DYNAMIC_LIB"); options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "libmonosgen-2.0.so")); diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index 02019b30d..0ae68ca93 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -352,6 +352,16 @@ namespace Flax.Build } } + /// + /// Calls within Flax.Build context - can be used by build scripts to properly find a type by name. + /// + /// The full name (namespace with class name). + /// Type or null if not found. + public static Type GetType(string name) + { + return Type.GetType(name); + } + /// /// Runs the external program. /// From c60058758bd78d87c378f32f1322b50040b171ca Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 Jan 2024 01:15:29 +0100 Subject: [PATCH 050/198] Minor fixes --- Source/Engine/Engine/NativeInterop.Unmanaged.cs | 8 ++++---- Source/Engine/Scripting/Runtime/DotNet.cpp | 6 +----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index e99276a4e..a35cce42e 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -525,11 +525,11 @@ namespace FlaxEngine.Interop internal static void GetMethodParameterTypes(ManagedHandle methodHandle, IntPtr* typeHandles) { MethodHolder methodHolder = Unsafe.As(methodHandle.Target); - Type returnType = methodHolder.returnType; - IntPtr arr = (IntPtr)NativeAlloc(methodHolder.parameterTypes.Length, IntPtr.Size); - for (int i = 0; i < methodHolder.parameterTypes.Length; i++) + Type[] parameterTypes = methodHolder.parameterTypes; + IntPtr arr = (IntPtr)NativeAlloc(parameterTypes.Length, IntPtr.Size); + for (int i = 0; i < parameterTypes.Length; i++) { - ManagedHandle typeHandle = GetTypeManagedHandle(methodHolder.parameterTypes[i]); + ManagedHandle typeHandle = GetTypeManagedHandle(parameterTypes[i]); Unsafe.Write(IntPtr.Add(new IntPtr(arr), IntPtr.Size * i).ToPointer(), typeHandle); } *typeHandles = arr; diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 0b5993477..2092ea30c 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -1876,11 +1876,7 @@ void OnLogCallback(const char* logDomain, const char* logLevel, const char* mess } #endif - if (errorLevel == 0) - { - Log::CLRInnerException(String::Format(TEXT("Message: {0} | Domain: {1}"), msg, currentDomain)).SetLevel(LogType::Error); - } - else if (errorLevel <= 2) + if (errorLevel <= 2) { Log::CLRInnerException(String::Format(TEXT("Message: {0} | Domain: {1}"), msg, currentDomain)).SetLevel(LogType::Error); } From 2853af20f6c7c358d11a595d10b7717fc0026632 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 Jan 2024 01:23:58 +0100 Subject: [PATCH 051/198] Tweak iOS AOT options --- Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs b/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs index fb25560ec..c663946be 100644 --- a/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/iOS/iOSToolchain.cs @@ -84,7 +84,7 @@ namespace Flax.Build.Platforms // Setup options var monoAotMode = "full"; var monoDebugMode = options.EnableDebugSymbols ? "soft-debug" : "nodebug"; - var aotCompilerArgs = $"--aot={monoAotMode},asmonly,verbose,stats,print-skipped,{monoDebugMode} -O=all"; + var aotCompilerArgs = $"--aot={monoAotMode},asmonly,verbose,stats,print-skipped,{monoDebugMode} -O=float32"; if (options.EnableDebugSymbols || options.EnableToolDebug) aotCompilerArgs = "--debug " + aotCompilerArgs; var envVars = new Dictionary(); @@ -97,13 +97,7 @@ namespace Flax.Build.Platforms // Run cross-compiler compiler (outputs assembly code) int result = Utilities.Run(aotCompilerPath, $"{aotCompilerArgs} \"{inputFile}\"", null, inputFileFolder, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput, envVars); if (result != 0) - { - // Try without optimizations as a fallback - aotCompilerArgs = aotCompilerArgs.Replace("-O=all", ""); - result = Utilities.Run(aotCompilerPath, $"{aotCompilerArgs} \"{inputFile}\"", null, inputFileFolder, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput, envVars); - if (result != 0) - return true; - } + return true; // Get build args for iOS var clangArgs = new List(); From 2a70d0c011ef0146d483b2fe7aa9a22f82a7030f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 Jan 2024 19:52:31 +0100 Subject: [PATCH 052/198] Fix invalid log timing when log file is disabled in cooked build --- Source/Engine/Core/Log.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Core/Log.cpp b/Source/Engine/Core/Log.cpp index b6b4fa3e2..0cea82b4c 100644 --- a/Source/Engine/Core/Log.cpp +++ b/Source/Engine/Core/Log.cpp @@ -32,6 +32,8 @@ Delegate Log::Logger::OnError; bool Log::Logger::Init() { + LogStartTime = Time::StartupTime; + // Skip if disabled if (!IsLogEnabled()) return false; @@ -73,7 +75,6 @@ bool Log::Logger::Init() #endif // Create log file path - LogStartTime = Time::StartupTime; const String filename = TEXT("Log_") + LogStartTime.ToFileNameString() + TEXT(".txt"); LogFilePath = logsDirectory / filename; @@ -198,12 +199,11 @@ void Log::Logger::WriteFloor() void Log::Logger::ProcessLogMessage(LogType type, const StringView& msg, fmt_flax::memory_buffer& w) { const TimeSpan time = DateTime::Now() - LogStartTime; - const int32 msgLength = msg.Length(); - fmt_flax::format(w, TEXT("[ {0} ]: [{1}] "), *time.ToString('a'), ToString(type)); // On Windows convert all '\n' into '\r\n' #if PLATFORM_WINDOWS + const int32 msgLength = msg.Length(); bool hasWindowsNewLine = false; for (int32 i = 1; i < msgLength && !hasWindowsNewLine; i++) hasWindowsNewLine |= msg.Get()[i - 1] != '\r' && msg.Get()[i] == '\n'; From ecf593e7a463e8b2ed81e056e513d350b59d6458 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 Jan 2024 20:28:29 +0100 Subject: [PATCH 053/198] Remove unused `SaveTime`/`LoadTime` from Scene --- Source/Engine/Level/Level.cpp | 3 --- Source/Engine/Level/Scene/Scene.cpp | 3 --- Source/Engine/Level/Scene/Scene.h | 10 ---------- 3 files changed, 16 deletions(-) diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 41033876d..160243e24 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -925,7 +925,6 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou // Create scene actor // Note: the first object in the scene file data is a Scene Actor auto scene = New(ScriptingObjectSpawnParams(sceneId, Scene::TypeInitializer)); - scene->LoadTime = startTime; scene->RegisterObject(); scene->Deserialize(data[0], modifier.Value); @@ -1114,7 +1113,6 @@ bool LevelImpl::saveScene(Scene* scene, const String& path) LOG(Info, "Saving scene {0} to \'{1}\'", scene->GetName(), path); const DateTime startTime = DateTime::NowUTC(); - scene->SaveTime = startTime; // Serialize to json rapidjson_flax::StringBuffer buffer; @@ -1215,7 +1213,6 @@ bool Level::SaveSceneToBytes(Scene* scene, rapidjson_flax::StringBuffer& outData LOG(Info, "Saving scene {0} to bytes", scene->GetName()); const DateTime startTime = DateTime::NowUTC(); - scene->SaveTime = startTime; // Serialize to json if (saveScene(scene, outData, prettyJson)) diff --git a/Source/Engine/Level/Scene/Scene.cpp b/Source/Engine/Level/Scene/Scene.cpp index 6c1541240..97f98e288 100644 --- a/Source/Engine/Level/Scene/Scene.cpp +++ b/Source/Engine/Level/Scene/Scene.cpp @@ -273,9 +273,6 @@ void Scene::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_GET_OTHER_OBJ(Scene); - // Update scene info object - SaveTime = DateTime::NowUTC(); - LightmapsData.SaveLightmaps(Info.Lightmaps); Info.Serialize(stream, other ? &other->Info : nullptr); diff --git a/Source/Engine/Level/Scene/Scene.h b/Source/Engine/Level/Scene/Scene.h index 3fc33f19d..83281b397 100644 --- a/Source/Engine/Level/Scene/Scene.h +++ b/Source/Engine/Level/Scene/Scene.h @@ -32,16 +32,6 @@ public: /// SceneInfo Info; - /// - /// The last load time. - /// - DateTime LoadTime; - - /// - /// The last save time. - /// - DateTime SaveTime; - /// /// The scene rendering manager. /// From 69b7a10c1f20624c2b10fd485651bb1a319c3afd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 Jan 2024 20:31:03 +0100 Subject: [PATCH 054/198] Add `Stopwatch` for accurate duration measuring --- .../CustomEditors/CustomEditorsUtil.cpp | 7 +- Source/Engine/Content/Cache/AssetsCache.cpp | 8 ++- Source/Engine/Core/Types/Stopwatch.h | 64 +++++++++++++++++++ Source/Engine/Level/Level.cpp | 22 +++---- Source/Engine/Level/Scene/SceneCSGData.h | 3 +- .../Engine/Scripting/ManagedCLR/MAssembly.h | 2 +- Source/Engine/Scripting/ManagedCLR/MCore.cpp | 11 ++-- Source/Engine/Scripting/Runtime/DotNet.cpp | 16 +++-- Source/Engine/Scripting/Runtime/Mono.cpp | 5 +- Source/Engine/Scripting/Scripting.cpp | 16 ++--- 10 files changed, 113 insertions(+), 41 deletions(-) create mode 100644 Source/Engine/Core/Types/Stopwatch.h diff --git a/Source/Editor/CustomEditors/CustomEditorsUtil.cpp b/Source/Editor/CustomEditors/CustomEditorsUtil.cpp index d0c10c13d..cf0b6e4d7 100644 --- a/Source/Editor/CustomEditors/CustomEditorsUtil.cpp +++ b/Source/Editor/CustomEditors/CustomEditorsUtil.cpp @@ -4,6 +4,7 @@ #include "Engine/Core/Log.h" #include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Types/TimeSpan.h" +#include "Engine/Core/Types/Stopwatch.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Engine/EngineService.h" #include "Engine/Scripting/Scripting.h" @@ -81,7 +82,7 @@ bool CustomEditorsUtilService::Init() void OnAssemblyLoaded(MAssembly* assembly) { - const auto startTime = DateTime::NowUTC(); + Stopwatch stopwatch; // Prepare FlaxEngine auto engineAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly; @@ -162,8 +163,8 @@ void OnAssemblyLoaded(MAssembly* assembly) } } - const auto endTime = DateTime::NowUTC(); - LOG(Info, "Assembly \'{0}\' scanned for custom editors in {1} ms", assembly->ToString(), (int32)(endTime - startTime).GetTotalMilliseconds()); + stopwatch.Stop(); + LOG(Info, "Assembly \'{0}\' scanned for custom editors in {1} ms", assembly->ToString(), stopwatch.GetMilliseconds()); } void OnAssemblyUnloading(MAssembly* assembly) diff --git a/Source/Engine/Content/Cache/AssetsCache.cpp b/Source/Engine/Content/Cache/AssetsCache.cpp index c9a10f721..e9ce23e1a 100644 --- a/Source/Engine/Content/Cache/AssetsCache.cpp +++ b/Source/Engine/Content/Cache/AssetsCache.cpp @@ -4,6 +4,8 @@ #include "Engine/Core/Log.h" #include "Engine/Core/DeleteMe.h" #include "Engine/Core/Types/TimeSpan.h" +#include "Engine/Core/Types/DateTime.h" +#include "Engine/Core/Types/Stopwatch.h" #include "Engine/Platform/FileSystem.h" #include "Engine/Serialization/FileWriteStream.h" #include "Engine/Serialization/FileReadStream.h" @@ -19,7 +21,7 @@ void AssetsCache::Init() { Entry e; int32 count; - const DateTime loadStartTime = DateTime::Now(); + Stopwatch stopwatch; #if USE_EDITOR _path = Globals::ProjectCacheFolder / TEXT("AssetsCache.dat"); #else @@ -138,8 +140,8 @@ void AssetsCache::Init() } } - const int32 loadTimeInMs = static_cast((DateTime::Now() - loadStartTime).GetTotalMilliseconds()); - LOG(Info, "Asset Cache loaded {0} entries in {1} ms ({2} rejected)", _registry.Count(), loadTimeInMs, rejectedCount); + stopwatch.Stop(); + LOG(Info, "Asset Cache loaded {0} entries in {1} ms ({2} rejected)", _registry.Count(), stopwatch.GetMilliseconds(), rejectedCount); } bool AssetsCache::Save() diff --git a/Source/Engine/Core/Types/Stopwatch.h b/Source/Engine/Core/Types/Stopwatch.h new file mode 100644 index 000000000..675f826a4 --- /dev/null +++ b/Source/Engine/Core/Types/Stopwatch.h @@ -0,0 +1,64 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#pragma once + +#include "BaseTypes.h" +#include "Engine/Platform/Platform.h" + +/// +/// High-resolution performance counter based on Platform::GetTimeSeconds. +/// +API_STRUCT(InBuild, Namespace="System.Diagnostics") struct FLAXENGINE_API Stopwatch +{ +private: + double _start, _end; + +public: + Stopwatch() + { + _start = _end = Platform::GetTimeSeconds(); + } + +public: + // Starts the counter. + void Start() + { + _start = Platform::GetTimeSeconds(); + } + + // Stops the counter. + void Stop() + { + _end = Platform::GetTimeSeconds(); + } + + /// + /// Gets the milliseconds time. + /// + FORCE_INLINE int32 GetMilliseconds() const + { + return (int32)((_end - _start) * 1000.0); + } + + /// + /// Gets the total number of milliseconds. + /// + FORCE_INLINE double GetTotalMilliseconds() const + { + return (float)((_end - _start) * 1000.0); + } + + /// + /// Gets the total number of seconds. + /// + FORCE_INLINE float GetTotalSeconds() const + { + return (float)(_end - _start); + } +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 160243e24..e7f193ea8 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -12,6 +12,7 @@ #include "Engine/Core/ObjectsRemovalService.h" #include "Engine/Core/Config/LayersTagsSettings.h" #include "Engine/Core/Types/LayersMask.h" +#include "Engine/Core/Types/Stopwatch.h" #include "Engine/Debug/Exceptions/ArgumentException.h" #include "Engine/Debug/Exceptions/ArgumentNullException.h" #include "Engine/Debug/Exceptions/InvalidOperationException.h" @@ -868,13 +869,11 @@ bool Level::loadScene(rapidjson_flax::Document& document, Scene** outScene) bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene) { PROFILE_CPU_NAMED("Level.LoadScene"); - if (outScene) *outScene = nullptr; - LOG(Info, "Loading scene..."); - const DateTime startTime = DateTime::NowUTC(); - _lastSceneLoadTime = startTime; + Stopwatch stopwatch; + _lastSceneLoadTime = DateTime::Now(); // Here whole scripting backend should be loaded for current project // Later scripts will setup attached scripts and restore initial vars @@ -1083,7 +1082,8 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou // Fire event CallSceneEvent(SceneEventType::OnSceneLoaded, scene, sceneId); - LOG(Info, "Scene loaded in {0} ms", (int32)(DateTime::NowUTC() - startTime).GetTotalMilliseconds()); + stopwatch.Stop(); + LOG(Info, "Scene loaded in {0}ms", stopwatch.GetMilliseconds()); if (outScene) *outScene = scene; return false; @@ -1112,7 +1112,7 @@ bool LevelImpl::saveScene(Scene* scene, const String& path) auto sceneId = scene->GetID(); LOG(Info, "Saving scene {0} to \'{1}\'", scene->GetName(), path); - const DateTime startTime = DateTime::NowUTC(); + Stopwatch stopwatch; // Serialize to json rapidjson_flax::StringBuffer buffer; @@ -1130,7 +1130,8 @@ bool LevelImpl::saveScene(Scene* scene, const String& path) return true; } - LOG(Info, "Scene saved! Time {0} ms", Math::CeilToInt((float)(DateTime::NowUTC() - startTime).GetTotalMilliseconds())); + stopwatch.Stop(); + LOG(Info, "Scene saved! Time {0}ms", stopwatch.GetMilliseconds()); #if USE_EDITOR // Reload asset at the target location if is loaded @@ -1210,9 +1211,8 @@ bool Level::SaveSceneToBytes(Scene* scene, rapidjson_flax::StringBuffer& outData { ASSERT(scene); ScopeLock lock(_sceneActionsLocker); - + Stopwatch stopwatch; LOG(Info, "Saving scene {0} to bytes", scene->GetName()); - const DateTime startTime = DateTime::NowUTC(); // Serialize to json if (saveScene(scene, outData, prettyJson)) @@ -1221,8 +1221,8 @@ bool Level::SaveSceneToBytes(Scene* scene, rapidjson_flax::StringBuffer& outData return true; } - // Info - LOG(Info, "Scene saved! Time {0} ms", Math::CeilToInt(static_cast((DateTime::NowUTC() - startTime).GetTotalMilliseconds()))); + stopwatch.Stop(); + LOG(Info, "Scene saved! Time {0}ms", stopwatch.GetMilliseconds()); // Fire event CallSceneEvent(SceneEventType::OnSceneSaved, scene, scene->GetID()); diff --git a/Source/Engine/Level/Scene/SceneCSGData.h b/Source/Engine/Level/Scene/SceneCSGData.h index ca6ed0540..baf01cacf 100644 --- a/Source/Engine/Level/Scene/SceneCSGData.h +++ b/Source/Engine/Level/Scene/SceneCSGData.h @@ -2,10 +2,11 @@ #pragma once +#include "Engine/Core/ISerializable.h" +#include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Math/Triangle.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Physics/CollisionData.h" -#include "Engine/Core/ISerializable.h" #include "Engine/Content/AssetReference.h" #include "Engine/Content/Assets/RawDataAsset.h" #include "Engine/Content/Assets/Model.h" diff --git a/Source/Engine/Scripting/ManagedCLR/MAssembly.h b/Source/Engine/Scripting/ManagedCLR/MAssembly.h index ff1645e5e..dff3284fb 100644 --- a/Source/Engine/Scripting/ManagedCLR/MAssembly.h +++ b/Source/Engine/Scripting/ManagedCLR/MAssembly.h @@ -219,7 +219,7 @@ private: bool LoadImage(const String& assemblyPath, const StringView& nativePath); bool UnloadImage(bool isReloading); void OnLoading(); - void OnLoaded(const struct DateTime& startTime); + void OnLoaded(struct Stopwatch& stopwatch); void OnLoadFailed(); bool ResolveMissingFile(String& assemblyPath) const; }; diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.cpp b/Source/Engine/Scripting/ManagedCLR/MCore.cpp index 02a834a9f..35bec8b8e 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MCore.cpp @@ -10,6 +10,7 @@ #include "MProperty.h" #include "Engine/Core/Math/Math.h" #include "Engine/Core/Types/DateTime.h" +#include "Engine/Core/Types/Stopwatch.h" #include "Engine/Core/Types/TimeSpan.h" #include "Engine/Platform/FileSystem.h" #include "Engine/Profiler/ProfilerCPU.h" @@ -80,6 +81,7 @@ bool MAssembly::Load(const String& assemblyPath, const StringView& nativePath) return false; PROFILE_CPU(); ZoneText(*assemblyPath, assemblyPath.Length()); + Stopwatch stopwatch; const String* pathPtr = &assemblyPath; String path; @@ -94,7 +96,6 @@ bool MAssembly::Load(const String& assemblyPath, const StringView& nativePath) } } - const auto startTime = DateTime::NowUTC(); OnLoading(); if (LoadImage(*pathPtr, nativePath)) @@ -103,7 +104,7 @@ bool MAssembly::Load(const String& assemblyPath, const StringView& nativePath) return true; } - OnLoaded(startTime); + OnLoaded(stopwatch); return false; } @@ -173,7 +174,7 @@ void MAssembly::OnLoading() _domain = MCore::GetActiveDomain(); } -void MAssembly::OnLoaded(const DateTime& startTime) +void MAssembly::OnLoaded(Stopwatch& stopwatch) { // Register in domain _domain->_assemblies[_name] = this; @@ -181,8 +182,8 @@ void MAssembly::OnLoaded(const DateTime& startTime) _isLoaded = true; _isLoading = false; - const auto endTime = DateTime::NowUTC(); - LOG(Info, "Assembly {0} loaded in {1}ms", String(_name), (int32)(endTime - startTime).GetTotalMilliseconds()); + stopwatch.Stop(); + LOG(Info, "Assembly {0} loaded in {1}ms", String(_name), stopwatch.GetMilliseconds()); // Pre-cache classes GetClasses(); diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 2092ea30c..6845941b2 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -9,6 +9,7 @@ #include "Engine/Core/Log.h" #include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Types/TimeSpan.h" +#include "Engine/Core/Types/Stopwatch.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Platform/Platform.h" #include "Engine/Platform/File.h" @@ -663,7 +664,7 @@ const MAssembly::ClassesDictionary& MAssembly::GetClasses() const if (_hasCachedClasses || !IsLoaded()) return _classes; PROFILE_CPU(); - const auto startTime = DateTime::NowUTC(); + Stopwatch stopwatch; #if TRACY_ENABLE ZoneText(*_name, _name.Length()); @@ -698,8 +699,8 @@ const MAssembly::ClassesDictionary& MAssembly::GetClasses() const MCore::GC::FreeMemory(managedClasses); - const auto endTime = DateTime::NowUTC(); - LOG(Info, "Caching classes for assembly {0} took {1}ms", String(_name), (int32)(endTime - startTime).GetTotalMilliseconds()); + stopwatch.Stop(); + LOG(Info, "Caching classes for assembly {0} took {1}ms", String(_name), stopwatch.GetMilliseconds()); #if 0 for (auto i = _classes.Begin(); i.IsNotEnd(); ++i) @@ -768,7 +769,7 @@ bool MAssembly::LoadCorlib() Unload(); // Start - const auto startTime = DateTime::NowUTC(); + Stopwatch stopwatch; OnLoading(); // Load @@ -786,7 +787,7 @@ bool MAssembly::LoadCorlib() CachedAssemblyHandles.Add(_handle, this); // End - OnLoaded(startTime); + OnLoaded(stopwatch); return false; } @@ -2051,7 +2052,7 @@ bool InitHostfxr() #ifdef USE_MONO_AOT_MODULE // Load AOT module - const DateTime aotModuleLoadStartTime = DateTime::Now(); + Stopwatch aotModuleLoadStopwatch; LOG(Info, "Loading Mono AOT module..."); void* libAotModule = Platform::LoadLibrary(TEXT(USE_MONO_AOT_MODULE)); if (libAotModule == nullptr) @@ -2076,7 +2077,8 @@ bool InitHostfxr() mono_aot_register_module((void**)modules[i]); } Allocator::Free(modules); - LOG(Info, "Mono AOT module loaded in {0}ms", (int32)(DateTime::Now() - aotModuleLoadStartTime).GetTotalMilliseconds()); + aotModuleLoadStopwatch.Stop(); + LOG(Info, "Mono AOT module loaded in {0}ms", aotModuleLoadStopwatch.GetMilliseconds()); #endif // Setup debugger diff --git a/Source/Engine/Scripting/Runtime/Mono.cpp b/Source/Engine/Scripting/Runtime/Mono.cpp index 00f16fd44..f20e1f514 100644 --- a/Source/Engine/Scripting/Runtime/Mono.cpp +++ b/Source/Engine/Scripting/Runtime/Mono.cpp @@ -7,6 +7,7 @@ #include "Engine/Core/Log.h" #include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Types/TimeSpan.h" +#include "Engine/Core/Types/Stopwatch.h" #include "Engine/Platform/File.h" #include "Engine/Platform/FileSystem.h" #include "Engine/Engine/Globals.h" @@ -1089,7 +1090,7 @@ bool MAssembly::Load(MonoImage* monoImage) Unload(); // Start - const auto startTime = DateTime::NowUTC(); + Stopwatch stopwatch; OnLoading(); // Load @@ -1103,7 +1104,7 @@ bool MAssembly::Load(MonoImage* monoImage) _hasCachedClasses = false; // End - OnLoaded(startTime); + OnLoaded(stopwatch); return false; } diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 6cabbc0dd..f375b7bf0 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -23,7 +23,7 @@ #include "Internal/StdTypesContainer.h" #include "Engine/Core/ObjectsRemovalService.h" #include "Engine/Core/Types/TimeSpan.h" -#include "Engine/Profiler/ProfilerCPU.h" +#include "Engine/Core/Types/Stopwatch.h" #include "Engine/Content/Asset.h" #include "Engine/Content/Content.h" #include "Engine/Engine/EngineService.h" @@ -31,6 +31,7 @@ #include "Engine/Engine/Time.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Serialization/JsonTools.h" +#include "Engine/Profiler/ProfilerCPU.h" extern void registerFlaxEngineInternalCalls(); @@ -126,7 +127,7 @@ void onEngineUnloading(MAssembly* assembly); bool ScriptingService::Init() { - const auto startTime = DateTime::NowUTC(); + Stopwatch stopwatch; // Initialize managed runtime if (MCore::LoadEngine()) @@ -158,9 +159,8 @@ bool ScriptingService::Init() return true; } - auto endTime = DateTime::NowUTC(); - LOG(Info, "Scripting Engine initializated! (time: {0}ms)", (int32)((endTime - startTime).GetTotalMilliseconds())); - + stopwatch.Stop(); + LOG(Info, "Scripting Engine initializated! (time: {0}ms)", stopwatch.GetMilliseconds()); return false; } @@ -357,7 +357,7 @@ bool Scripting::LoadBinaryModules(const String& path, const String& projectFolde if (!module) { // Load library - const auto startTime = DateTime::NowUTC(); + Stopwatch stopwatch; #if PLATFORM_ANDROID || PLATFORM_MAC // On some platforms all native binaries are side-by-side with the app in a different folder if (!FileSystem::FileExists(nativePath)) @@ -390,8 +390,8 @@ bool Scripting::LoadBinaryModules(const String& path, const String& projectFolde LOG(Error, "Failed to setup library '{0}' for binary module {1}.", nativePath, name); return true; } - const auto endTime = DateTime::NowUTC(); - LOG(Info, "Module {0} loaded in {1}ms", name, (int32)(endTime - startTime).GetTotalMilliseconds()); + stopwatch.Stop(); + LOG(Info, "Module {0} loaded in {1}ms", name, stopwatch.GetMilliseconds()); // Get the binary module module = getBinaryFunc(); From 9e38a01acc45dca23eb031c6fa7baf0c5958dca4 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:52:25 +0100 Subject: [PATCH 055/198] add units support in float, double and Float3 input --- .../CustomEditors/Editors/DoubleEditor.cs | 5 ++ .../CustomEditors/Editors/FloatEditor.cs | 5 ++ .../CustomEditors/Editors/Vector3Editor.cs | 15 +++++ .../Elements/DoubleValueElement.cs | 10 +++ .../Elements/FloatValueElement.cs | 10 +++ Source/Editor/GUI/Input/DoubleValueBox.cs | 8 ++- Source/Editor/GUI/Input/FloatValueBox.cs | 8 ++- Source/Editor/Utilities/ShuntingYardParser.cs | 17 ++++- Source/Editor/Utilities/Units.cs | 9 +++ Source/Editor/Utilities/Utils.cs | 64 ++++++++++++++++++- .../Editor/ValueCategoryAttribute.cs | 35 ++++++++++ .../Flax.Build.Tests/Flax.Build.Tests.csproj | 2 +- Source/Tools/Flax.Build/Flax.Build.csproj | 1 - 13 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 Source/Editor/Utilities/Units.cs create mode 100644 Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs diff --git a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs index c490046b9..0906d22af 100644 --- a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -25,6 +26,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); + var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var limit = attributes.FirstOrDefault(x => x is LimitAttribute); @@ -32,6 +35,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Use double value editor with limit var doubleValue = layout.DoubleValue(); + doubleValue.SetCategory(valueCategory); doubleValue.SetLimits((LimitAttribute)limit); doubleValue.ValueBox.ValueChanged += OnValueChanged; doubleValue.ValueBox.SlidingEnd += ClearToken; @@ -43,6 +47,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Use double value editor var doubleValue = layout.DoubleValue(); + doubleValue.SetCategory(valueCategory); doubleValue.ValueBox.ValueChanged += OnValueChanged; doubleValue.ValueBox.SlidingEnd += ClearToken; _element = doubleValue; diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index 07c07030e..525541e99 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -30,6 +31,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); + var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var range = attributes.FirstOrDefault(x => x is RangeAttribute); @@ -49,6 +52,7 @@ namespace FlaxEditor.CustomEditors.Editors // Use float value editor with limit var floatValue = layout.FloatValue(); floatValue.SetLimits((LimitAttribute)limit); + floatValue.SetCategory(valueCategory); floatValue.ValueBox.ValueChanged += OnValueChanged; floatValue.ValueBox.SlidingEnd += ClearToken; _element = floatValue; @@ -59,6 +63,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Use float value editor var floatValue = layout.FloatValue(); + floatValue.SetCategory(valueCategory); floatValue.ValueBox.ValueChanged += OnValueChanged; floatValue.ValueBox.SlidingEnd += ClearToken; _element = floatValue; diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index c3edd3913..9826c339f 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -4,6 +4,7 @@ using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; using FlaxEngine.GUI; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -70,23 +71,30 @@ namespace FlaxEditor.CustomEditors.Editors LimitAttribute limit = null; var attributes = Values.GetAttributes(); + var category = Utils.ValueCategory.None; if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); + var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + if (categoryAttribute != null) + category = categoryAttribute.Category; } XElement = grid.FloatValue(); XElement.SetLimits(limit); + XElement.SetCategory(category); XElement.ValueBox.ValueChanged += OnXValueChanged; XElement.ValueBox.SlidingEnd += ClearToken; YElement = grid.FloatValue(); YElement.SetLimits(limit); + YElement.SetCategory(category); YElement.ValueBox.ValueChanged += OnYValueChanged; YElement.ValueBox.SlidingEnd += ClearToken; ZElement = grid.FloatValue(); ZElement.SetLimits(limit); + ZElement.SetCategory(category); ZElement.ValueBox.ValueChanged += OnZValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; } @@ -248,24 +256,31 @@ namespace FlaxEditor.CustomEditors.Editors gridControl.SlotsVertically = 1; LimitAttribute limit = null; + Utils.ValueCategory category = Utils.ValueCategory.None; var attributes = Values.GetAttributes(); if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); + var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + if (categoryAttribute != null) + category = categoryAttribute.Category; } XElement = grid.DoubleValue(); XElement.SetLimits(limit); + XElement.SetCategory(category); XElement.ValueBox.ValueChanged += OnValueChanged; XElement.ValueBox.SlidingEnd += ClearToken; YElement = grid.DoubleValue(); YElement.SetLimits(limit); + YElement.SetCategory(category); YElement.ValueBox.ValueChanged += OnValueChanged; YElement.ValueBox.SlidingEnd += ClearToken; ZElement = grid.DoubleValue(); ZElement.SetLimits(limit); + ZElement.SetCategory(category); ZElement.ValueBox.ValueChanged += OnValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; } diff --git a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs index 07af5e991..460a344ff 100644 --- a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs @@ -5,6 +5,7 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Elements { @@ -51,6 +52,15 @@ namespace FlaxEditor.CustomEditors.Elements } } + /// + /// Set the value category of this float element + /// + /// + public void SetCategory(Utils.ValueCategory category) + { + ValueBox.Category = category; + } + /// /// Sets the editor limits from member . /// diff --git a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs index 789d8966e..413a341c1 100644 --- a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs @@ -5,6 +5,7 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Elements { @@ -51,6 +52,15 @@ namespace FlaxEditor.CustomEditors.Elements } } + /// + /// Set the value category of this float element + /// + /// + public void SetCategory(Utils.ValueCategory category) + { + ValueBox.Category = category; + } + /// /// Sets the editor limits from member . /// diff --git a/Source/Editor/GUI/Input/DoubleValueBox.cs b/Source/Editor/GUI/Input/DoubleValueBox.cs index 0ceb10114..46e6402cb 100644 --- a/Source/Editor/GUI/Input/DoubleValueBox.cs +++ b/Source/Editor/GUI/Input/DoubleValueBox.cs @@ -3,6 +3,7 @@ using System; using FlaxEditor.Utilities; using FlaxEngine; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.GUI.Input { @@ -129,10 +130,15 @@ namespace FlaxEditor.GUI.Input Value = Value; } + /// + /// Get or set the category of the value. This can either be none for just a number, a distance or an angle. + /// + public Utils.ValueCategory Category = Utils.ValueCategory.None; + /// protected sealed override void UpdateText() { - SetText(Utilities.Utils.FormatFloat(_value)); + SetText(Utilities.Utils.FormatFloat(_value, Category)); } /// diff --git a/Source/Editor/GUI/Input/FloatValueBox.cs b/Source/Editor/GUI/Input/FloatValueBox.cs index cdb3b08b1..64ea000fa 100644 --- a/Source/Editor/GUI/Input/FloatValueBox.cs +++ b/Source/Editor/GUI/Input/FloatValueBox.cs @@ -4,6 +4,7 @@ using System; using System.Globalization; using FlaxEditor.Utilities; using FlaxEngine; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.GUI.Input { @@ -137,10 +138,15 @@ namespace FlaxEditor.GUI.Input Value = Value; } + /// + /// Get or set the category of the value. This can either be none for just a number, a distance or an angle. + /// + public Utils.ValueCategory Category = Utils.ValueCategory.None; + /// protected sealed override void UpdateText() { - SetText(Utilities.Utils.FormatFloat(_value)); + SetText(Utilities.Utils.FormatFloat(_value, Category)); } /// diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index d139b003a..e7c5b5298 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -121,6 +121,9 @@ namespace FlaxEditor.Utilities ["e"] = Math.E, ["infinity"] = double.MaxValue, ["-infinity"] = -double.MaxValue, + ["m"] = Units.Meters2Units, + ["cm"] = Units.Meters2Units / 100, + ["km"] = Units.Meters2Units * 1000 }; /// @@ -170,7 +173,7 @@ namespace FlaxEditor.Utilities public static IEnumerable Tokenize(string text) { // Prepare text - text = text.Replace(',', '.'); + text = text.Replace(',', '.').Replace("°", ""); // Necessary to correctly parse negative numbers var previous = TokenType.WhiteSpace; @@ -372,6 +375,18 @@ namespace FlaxEditor.Utilities } } + // if stack has more than one item we're not finished with evaluating + // we assume the remaining values are all factors to be multiplied + if (stack.Count > 1) + { + var stackContent = string.Join(",", stack.ToList()); + Debug.Log($"parsing numbers, stack is {stackContent}"); + var v1 = stack.Pop(); + Debug.Log($"first on stack: {v1}"); + while (stack.Count > 0) + v1 *= stack.Pop(); + return v1; + } return stack.Pop(); } diff --git a/Source/Editor/Utilities/Units.cs b/Source/Editor/Utilities/Units.cs new file mode 100644 index 000000000..00d53a463 --- /dev/null +++ b/Source/Editor/Utilities/Units.cs @@ -0,0 +1,9 @@ +namespace FlaxEditor.Utilities; + +public class Units +{ + /// + /// Factor of units per meter. + /// + public static readonly float Meters2Units = 100f; +} diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 3d790ff81..0df4fe573 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -58,6 +58,16 @@ namespace FlaxEditor.Utilities /// public static readonly string FlaxEngineAssemblyName = "FlaxEngine.CSharp"; + /// + /// A category of number values used for formatting and input boxes + /// + public enum ValueCategory + { + None, + Distance, + Angle + } + /// /// Tries to parse number in the name brackets at the end of the value and then increment it to create a new name. /// Supports numbers at the end without brackets. @@ -1171,6 +1181,56 @@ namespace FlaxEditor.Utilities return StringUtils.GetPathWithoutExtension(path); } + /// + /// Format a float value either as-is, with a distance unit or with a degree sign + /// + /// the value to format + /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign + /// the formatted string + public static string FormatFloat(float value, ValueCategory category) + { + switch (category) + { + case ValueCategory.Distance: + var absValue = Mathf.Abs(value); + // in case a unit != cm this would be (value / Maters2Units * 100) + if (absValue < Units.Meters2Units) + return value.ToString("g7", CultureInfo.InvariantCulture) + "cm"; + if (absValue < Units.Meters2Units * 1000) + return (value / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "m"; + return (value / 1000 / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "km"; + case ValueCategory.Angle: return value.ToString("g7", CultureInfo.InvariantCulture) + "°"; + case ValueCategory.None: + default: + return FormatFloat(value); + } + } + + /// + /// Format a double value either as-is, with a distance unit or with a degree sign + /// + /// the value to format + /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign + /// the formatted string + public static string FormatFloat(double value, ValueCategory category) + { + switch (category) + { + case ValueCategory.Distance: + var absValue = Mathf.Abs(value); + // in case a unit != cm this would be (value / Maters2Units * 100) + if (absValue < Units.Meters2Units) + return value.ToString("g17", CultureInfo.InvariantCulture) + "cm"; + if (absValue < Units.Meters2Units * 1000) + return (value / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "m"; + return (value / 1000 / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "km"; + case ValueCategory.Angle: return value.ToString("g17", CultureInfo.InvariantCulture) + "°"; + case ValueCategory.None: + default: + return FormatFloat(value); + } + } + /// /// Formats the floating point value (double precision) into the readable text representation. /// @@ -1182,7 +1242,7 @@ namespace FlaxEditor.Utilities return "Infinity"; if (float.IsNegativeInfinity(value) || value == float.MinValue) return "-Infinity"; - string str = value.ToString("r", CultureInfo.InvariantCulture); + string str = value.ToString("g7", CultureInfo.InvariantCulture); return FormatFloat(str, value < 0); } @@ -1197,7 +1257,7 @@ namespace FlaxEditor.Utilities return "Infinity"; if (double.IsNegativeInfinity(value) || value == double.MinValue) return "-Infinity"; - string str = value.ToString("r", CultureInfo.InvariantCulture); + string str = value.ToString("g17", CultureInfo.InvariantCulture); return FormatFloat(str, value < 0); } diff --git a/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs new file mode 100644 index 000000000..3c6da2286 --- /dev/null +++ b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; + +namespace FlaxEngine +{ + /// + /// Used to specify the value category of a numeric value as either as-is (a scalar), a distance (formatted as cm/m/km) + /// or an angle (formatted with a degree sign). + /// + /// + [Serializable] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public sealed class ValueCategoryAttribute : Attribute + { + /// + /// The value category used for formatting. + /// + public FlaxEditor.Utilities.Utils.ValueCategory Category; + + private ValueCategoryAttribute() + { + Category = FlaxEditor.Utilities.Utils.ValueCategory.None; + } + + /// + /// Initializes a new instance of the class. + /// + /// The value category. + public ValueCategoryAttribute(FlaxEditor.Utilities.Utils.ValueCategory category) + { + Category = category; + } + } +} diff --git a/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj b/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj index df3b9851b..78b23ec07 100644 --- a/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj +++ b/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj @@ -2,7 +2,7 @@ net7.0 - 11.0 + 12 disable annotations true diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj index 1837bf0ec..7b189ce56 100644 --- a/Source/Tools/Flax.Build/Flax.Build.csproj +++ b/Source/Tools/Flax.Build/Flax.Build.csproj @@ -2,7 +2,6 @@ Exe net7.0 - 11.0 disable annotations false From d28b0ab2a6d354336d6178b92e849f5950b880bb Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Sun, 28 Jan 2024 21:41:08 +0100 Subject: [PATCH 056/198] revert unrelated change in .csproj --- Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj | 2 +- Source/Tools/Flax.Build/Flax.Build.csproj | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj b/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj index 78b23ec07..df3b9851b 100644 --- a/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj +++ b/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj @@ -2,7 +2,7 @@ net7.0 - 12 + 11.0 disable annotations true diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj index 7b189ce56..1837bf0ec 100644 --- a/Source/Tools/Flax.Build/Flax.Build.csproj +++ b/Source/Tools/Flax.Build/Flax.Build.csproj @@ -2,6 +2,7 @@ Exe net7.0 + 11.0 disable annotations false From 10bed94357b7ed60b3c08d2f39c42db5d9b800e7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 Jan 2024 22:15:34 +0100 Subject: [PATCH 057/198] Refactor engine systems to use platform time seconds rather than date time --- Source/Engine/Content/Cache/AssetsCache.cpp | 2 +- Source/Engine/Content/Storage/AssetHeader.h | 1 + Source/Engine/Content/Storage/FlaxChunk.h | 4 ++-- Source/Engine/Content/Storage/FlaxStorage.cpp | 13 +++++++----- Source/Engine/Content/Storage/FlaxStorage.h | 5 ++--- Source/Engine/Core/ObjectsRemovalService.cpp | 8 +++---- Source/Engine/ShadowsOfMordor/Builder.h | 1 + Source/Engine/Streaming/IStreamingHandler.h | 6 +----- Source/Engine/Streaming/StreamableResource.h | 4 ++-- Source/Engine/Streaming/Streaming.cpp | 21 +++++++++---------- Source/Engine/Streaming/StreamingHandlers.cpp | 8 +++---- Source/Engine/Streaming/StreamingHandlers.h | 8 +++---- Source/Engine/Utilities/Screenshot.cpp | 1 + 13 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Source/Engine/Content/Cache/AssetsCache.cpp b/Source/Engine/Content/Cache/AssetsCache.cpp index e9ce23e1a..188a52583 100644 --- a/Source/Engine/Content/Cache/AssetsCache.cpp +++ b/Source/Engine/Content/Cache/AssetsCache.cpp @@ -141,7 +141,7 @@ void AssetsCache::Init() } stopwatch.Stop(); - LOG(Info, "Asset Cache loaded {0} entries in {1} ms ({2} rejected)", _registry.Count(), stopwatch.GetMilliseconds(), rejectedCount); + LOG(Info, "Asset Cache loaded {0} entries in {1}ms ({2} rejected)", _registry.Count(), stopwatch.GetMilliseconds(), rejectedCount); } bool AssetsCache::Save() diff --git a/Source/Engine/Content/Storage/AssetHeader.h b/Source/Engine/Content/Storage/AssetHeader.h index 3c37ce35e..5c4031993 100644 --- a/Source/Engine/Content/Storage/AssetHeader.h +++ b/Source/Engine/Content/Storage/AssetHeader.h @@ -6,6 +6,7 @@ #include "Engine/Core/Types/Pair.h" #include "Engine/Core/Types/String.h" #if USE_EDITOR +#include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Collections/Array.h" #endif #include "FlaxChunk.h" diff --git a/Source/Engine/Content/Storage/FlaxChunk.h b/Source/Engine/Content/Storage/FlaxChunk.h index 710540a02..df6a8974a 100644 --- a/Source/Engine/Content/Storage/FlaxChunk.h +++ b/Source/Engine/Content/Storage/FlaxChunk.h @@ -78,9 +78,9 @@ public: FlaxChunkFlags Flags = FlaxChunkFlags::None; /// - /// The last usage time (atomic, ticks of DateTime in UTC). + /// The last usage time. /// - int64 LastAccessTime = 0; + double LastAccessTime = 0.0; /// /// The chunk data. diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index df99418bb..a24e01884 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -27,7 +27,7 @@ String AssetHeader::ToString() const void FlaxChunk::RegisterUsage() { - Platform::AtomicStore(&LastAccessTime, DateTime::NowUTC().Ticks); + LastAccessTime = Platform::GetTimeSeconds(); } const int32 FlaxStorage::MagicCode = 1180124739; @@ -229,7 +229,9 @@ uint32 FlaxStorage::GetRefCount() const bool FlaxStorage::ShouldDispose() const { - return Platform::AtomicRead((int64*)&_refCount) == 0 && Platform::AtomicRead((int64*)&_chunksLock) == 0 && DateTime::NowUTC() - _lastRefLostTime >= TimeSpan::FromMilliseconds(500); + return Platform::AtomicRead((int64*)&_refCount) == 0 && + Platform::AtomicRead((int64*)&_chunksLock) == 0 && + Platform::GetTimeSeconds() - _lastRefLostTime >= 0.5; // TTL in seconds } uint32 FlaxStorage::GetMemoryUsage() const @@ -1363,12 +1365,13 @@ void FlaxStorage::Tick() if (Platform::AtomicRead(&_chunksLock) != 0) return; - const auto now = DateTime::NowUTC(); + const double now = Platform::GetTimeSeconds(); bool wasAnyUsed = false; + const float unusedDataChunksLifetime = ContentStorageManager::UnusedDataChunksLifetime.GetTotalSeconds(); for (int32 i = 0; i < _chunks.Count(); i++) { - auto chunk = _chunks[i]; - const bool wasUsed = (now - DateTime(Platform::AtomicRead(&chunk->LastAccessTime))) < ContentStorageManager::UnusedDataChunksLifetime; + auto chunk = _chunks.Get()[i]; + const bool wasUsed = (now - chunk->LastAccessTime) < unusedDataChunksLifetime; if (!wasUsed && chunk->IsLoaded()) { chunk->Unload(); diff --git a/Source/Engine/Content/Storage/FlaxStorage.h b/Source/Engine/Content/Storage/FlaxStorage.h index 77c912c5a..23fb7a35f 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.h +++ b/Source/Engine/Content/Storage/FlaxStorage.h @@ -5,7 +5,6 @@ #include "Engine/Core/Object.h" #include "Engine/Core/Delegate.h" #include "Engine/Core/Types/String.h" -#include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Platform/CriticalSection.h" #include "Engine/Serialization/FileReadStream.h" @@ -90,7 +89,7 @@ protected: // State int64 _refCount; int64 _chunksLock; - DateTime _lastRefLostTime; + double _lastRefLostTime; CriticalSection _loadLocker; // Storage @@ -115,7 +114,7 @@ private: Platform::InterlockedDecrement(&_refCount); if (Platform::AtomicRead(&_refCount) == 0) { - _lastRefLostTime = DateTime::NowUTC(); + _lastRefLostTime = Platform::GetTimeSeconds(); } } diff --git a/Source/Engine/Core/ObjectsRemovalService.cpp b/Source/Engine/Core/ObjectsRemovalService.cpp index a185ce8b3..454ce7d43 100644 --- a/Source/Engine/Core/ObjectsRemovalService.cpp +++ b/Source/Engine/Core/ObjectsRemovalService.cpp @@ -17,7 +17,7 @@ Span Utilities::Private::HertzSizes(HertzSizesData, ARRAY_COUNT(Her namespace { CriticalSection PoolLocker; - DateTime LastUpdate; + double LastUpdate; float LastUpdateGameTime; Dictionary Pool(8192); uint64 PoolCounter = 0; @@ -114,7 +114,7 @@ void ObjectsRemovalService::Flush(float dt, float gameDelta) bool ObjectsRemoval::Init() { - LastUpdate = DateTime::NowUTC(); + LastUpdate = Platform::GetTimeSeconds(); LastUpdateGameTime = 0; return false; } @@ -124,8 +124,8 @@ void ObjectsRemoval::LateUpdate() PROFILE_CPU(); // Delete all objects - const auto now = DateTime::NowUTC(); - const float dt = (now - LastUpdate).GetTotalSeconds(); + const double now = Platform::GetTimeSeconds(); + const float dt = (float)(now - LastUpdate); float gameDelta = Time::Update.DeltaTime.GetTotalSeconds(); if (Time::GetGamePaused()) gameDelta = 0; diff --git a/Source/Engine/ShadowsOfMordor/Builder.h b/Source/Engine/ShadowsOfMordor/Builder.h index 5475e8028..cf4000389 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.h +++ b/Source/Engine/ShadowsOfMordor/Builder.h @@ -4,6 +4,7 @@ #include "Engine/Content/Assets/Model.h" #include "Engine/Content/Assets/Shader.h" +#include "Engine/Core/Types/DateTime.h" #include "Engine/CSG/CSGMesh.h" #include "Builder.Config.h" diff --git a/Source/Engine/Streaming/IStreamingHandler.h b/Source/Engine/Streaming/IStreamingHandler.h index 0dd615c2a..91a06cb77 100644 --- a/Source/Engine/Streaming/IStreamingHandler.h +++ b/Source/Engine/Streaming/IStreamingHandler.h @@ -13,19 +13,15 @@ class StreamableResource; class FLAXENGINE_API IStreamingHandler { public: - virtual ~IStreamingHandler() = default; -public: - /// /// Calculates target quality level (0-1) for the given resource. /// /// The resource. - /// The current time and date. /// The current platform time (seconds). /// Target quality (0-1). - virtual float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) = 0; + virtual float CalculateTargetQuality(StreamableResource* resource, double currentTime) = 0; /// /// Calculates the residency level for a given resource and quality level. diff --git a/Source/Engine/Streaming/StreamableResource.h b/Source/Engine/Streaming/StreamableResource.h index 27ae0a089..d65b8f95c 100644 --- a/Source/Engine/Streaming/StreamableResource.h +++ b/Source/Engine/Streaming/StreamableResource.h @@ -110,8 +110,8 @@ public: struct StreamingCache { - int64 LastUpdate = 0; - int64 TargetResidencyChange = 0; + double LastUpdateTime = 0.0; + double TargetResidencyChangeTime = 0; int32 TargetResidency = 0; bool Error = false; SamplesBuffer QualitySamples; diff --git a/Source/Engine/Streaming/Streaming.cpp b/Source/Engine/Streaming/Streaming.cpp index a7b2cad44..32d19f3d5 100644 --- a/Source/Engine/Streaming/Streaming.cpp +++ b/Source/Engine/Streaming/Streaming.cpp @@ -81,14 +81,14 @@ StreamableResource::~StreamableResource() void StreamableResource::RequestStreamingUpdate() { - Streaming.LastUpdate = 0; + Streaming.LastUpdateTime = 0.0; } void StreamableResource::ResetStreaming(bool error) { Streaming.Error = error; Streaming.TargetResidency = 0; - Streaming.LastUpdate = DateTime::MaxValue().Ticks; + Streaming.LastUpdateTime = 3e+30f; // Very large number to skip any updates } void StreamableResource::StartStreaming(bool isDynamic) @@ -115,7 +115,7 @@ void StreamableResource::StopStreaming() } } -void UpdateResource(StreamableResource* resource, DateTime now, double currentTime) +void UpdateResource(StreamableResource* resource, double currentTime) { ASSERT(resource && resource->CanBeUpdated()); @@ -127,7 +127,7 @@ void UpdateResource(StreamableResource* resource, DateTime now, double currentTi float targetQuality = 1.0f; if (resource->IsDynamic()) { - targetQuality = handler->CalculateTargetQuality(resource, now, currentTime); + targetQuality = handler->CalculateTargetQuality(resource, currentTime); targetQuality = Math::Saturate(targetQuality); } @@ -142,14 +142,14 @@ void UpdateResource(StreamableResource* resource, DateTime now, double currentTi auto allocatedResidency = resource->GetAllocatedResidency(); auto targetResidency = handler->CalculateResidency(resource, targetQuality); ASSERT(allocatedResidency >= currentResidency && allocatedResidency >= 0); - resource->Streaming.LastUpdate = now.Ticks; + resource->Streaming.LastUpdateTime = currentTime; // Check if a target residency level has been changed if (targetResidency != resource->Streaming.TargetResidency) { // Register change resource->Streaming.TargetResidency = targetResidency; - resource->Streaming.TargetResidencyChange = now.Ticks; + resource->Streaming.TargetResidencyChangeTime = currentTime; } // Check if need to change resource current residency @@ -224,15 +224,14 @@ void StreamingSystem::Job(int32 index) PROFILE_CPU_NAMED("Streaming.Job"); // TODO: use streaming settings - TimeSpan ResourceUpdatesInterval = TimeSpan::FromMilliseconds(100); + const double ResourceUpdatesInterval = 0.1; int32 MaxResourcesPerUpdate = 50; // Start update ScopeLock lock(ResourcesLock); - auto now = DateTime::NowUTC(); const int32 resourcesCount = Resources.Count(); int32 resourcesUpdates = Math::Min(MaxResourcesPerUpdate, resourcesCount); - double currentTime = Platform::GetTimeSeconds(); + const double currentTime = Platform::GetTimeSeconds(); // Update high priority queue and then rest of the resources // Note: resources in the update queue are updated always, while others only between specified intervals @@ -248,9 +247,9 @@ void StreamingSystem::Job(int32 index) const auto resource = Resources[LastUpdateResourcesIndex]; // Try to update it - if (now - DateTime(resource->Streaming.LastUpdate) >= ResourceUpdatesInterval && resource->CanBeUpdated()) + if (currentTime - resource->Streaming.LastUpdateTime >= ResourceUpdatesInterval && resource->CanBeUpdated()) { - UpdateResource(resource, now, currentTime); + UpdateResource(resource, currentTime); resourcesUpdates--; } } diff --git a/Source/Engine/Streaming/StreamingHandlers.cpp b/Source/Engine/Streaming/StreamingHandlers.cpp index c2f7693ca..a0a966cfe 100644 --- a/Source/Engine/Streaming/StreamingHandlers.cpp +++ b/Source/Engine/Streaming/StreamingHandlers.cpp @@ -11,7 +11,7 @@ #include "Engine/Audio/Audio.h" #include "Engine/Audio/AudioSource.h" -float TexturesStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) +float TexturesStreamingHandler::CalculateTargetQuality(StreamableResource* resource, double currentTime) { ASSERT(resource); auto& texture = *(StreamingTexture*)resource; @@ -93,7 +93,7 @@ int32 TexturesStreamingHandler::CalculateRequestedResidency(StreamableResource* return residency; } -float ModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) +float ModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, double currentTime) { // TODO: calculate a proper quality levels for models based on render time and streaming enable/disable options return 1.0f; @@ -132,7 +132,7 @@ int32 ModelsStreamingHandler::CalculateRequestedResidency(StreamableResource* re return residency; } -float SkinnedModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) +float SkinnedModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, double currentTime) { // TODO: calculate a proper quality levels for models based on render time and streaming enable/disable options return 1.0f; @@ -171,7 +171,7 @@ int32 SkinnedModelsStreamingHandler::CalculateRequestedResidency(StreamableResou return residency; } -float AudioStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) +float AudioStreamingHandler::CalculateTargetQuality(StreamableResource* resource, double currentTime) { // Audio clips don't use quality but only residency return 1.0f; diff --git a/Source/Engine/Streaming/StreamingHandlers.h b/Source/Engine/Streaming/StreamingHandlers.h index 6ec4a678a..2e70a31ef 100644 --- a/Source/Engine/Streaming/StreamingHandlers.h +++ b/Source/Engine/Streaming/StreamingHandlers.h @@ -11,7 +11,7 @@ class FLAXENGINE_API TexturesStreamingHandler : public IStreamingHandler { public: // [IStreamingHandler] - float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) override; + float CalculateTargetQuality(StreamableResource* resource, double currentTime) override; int32 CalculateResidency(StreamableResource* resource, float quality) override; int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; }; @@ -23,7 +23,7 @@ class FLAXENGINE_API ModelsStreamingHandler : public IStreamingHandler { public: // [IStreamingHandler] - float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) override; + float CalculateTargetQuality(StreamableResource* resource, double currentTime) override; int32 CalculateResidency(StreamableResource* resource, float quality) override; int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; }; @@ -35,7 +35,7 @@ class FLAXENGINE_API SkinnedModelsStreamingHandler : public IStreamingHandler { public: // [IStreamingHandler] - float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) override; + float CalculateTargetQuality(StreamableResource* resource, double currentTime) override; int32 CalculateResidency(StreamableResource* resource, float quality) override; int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; }; @@ -47,7 +47,7 @@ class FLAXENGINE_API AudioStreamingHandler : public IStreamingHandler { public: // [IStreamingHandler] - float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) override; + float CalculateTargetQuality(StreamableResource* resource, double currentTime) override; int32 CalculateResidency(StreamableResource* resource, float quality) override; int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; bool RequiresStreaming(StreamableResource* resource, int32 currentResidency, int32 targetResidency) override; diff --git a/Source/Engine/Utilities/Screenshot.cpp b/Source/Engine/Utilities/Screenshot.cpp index 7e1d3d902..408eb95f7 100644 --- a/Source/Engine/Utilities/Screenshot.cpp +++ b/Source/Engine/Utilities/Screenshot.cpp @@ -2,6 +2,7 @@ #include "Screenshot.h" #include "Engine/Core/Log.h" +#include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Math/Math.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Platform/FileSystem.h" From 1b33a2aa0d1d4ad6d839d864013b035a3404bcbf Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 Jan 2024 23:11:24 +0100 Subject: [PATCH 058/198] Optimize render targets freeing on the start of the game --- Source/Engine/Graphics/RenderTargetPool.cpp | 9 ++++++--- .../GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp | 19 ++++++++----------- .../GraphicsDevice/Vulkan/GPUDeviceVulkan.h | 10 ++-------- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/Source/Engine/Graphics/RenderTargetPool.cpp b/Source/Engine/Graphics/RenderTargetPool.cpp index 891610ab8..6f9507d33 100644 --- a/Source/Engine/Graphics/RenderTargetPool.cpp +++ b/Source/Engine/Graphics/RenderTargetPool.cpp @@ -4,6 +4,7 @@ #include "GPUDevice.h" #include "Engine/Core/Log.h" #include "Engine/Engine/Engine.h" +#include "Engine/Profiler/ProfilerCPU.h" struct Entry { @@ -20,9 +21,11 @@ namespace void RenderTargetPool::Flush(bool force, int32 framesOffset) { + PROFILE_CPU(); if (framesOffset < 0) framesOffset = 3 * 60; // For how many frames RTs should be cached (by default) - const uint64 maxReleaseFrame = Engine::FrameCount - framesOffset; + const uint64 frameCount = Engine::FrameCount; + const uint64 maxReleaseFrame = frameCount - Math::Min(frameCount, framesOffset); force |= Engine::ShouldExit(); for (int32 i = 0; i < TemporaryRTs.Count(); i++) @@ -40,6 +43,8 @@ void RenderTargetPool::Flush(bool force, int32 framesOffset) GPUTexture* RenderTargetPool::Get(const GPUTextureDescription& desc) { + PROFILE_CPU(); + // Find free render target with the same properties const uint32 descHash = GetHash(desc); for (int32 i = 0; i < TemporaryRTs.Count(); i++) @@ -85,7 +90,6 @@ void RenderTargetPool::Release(GPUTexture* rt) { if (!rt) return; - for (int32 i = 0; i < TemporaryRTs.Count(); i++) { auto& e = TemporaryRTs[i]; @@ -98,6 +102,5 @@ void RenderTargetPool::Release(GPUTexture* rt) return; } } - LOG(Error, "Trying to release temporary render target which has not been registered in service!"); } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index 9c553060f..19578df89 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -356,14 +356,12 @@ DeferredDeletionQueueVulkan::~DeferredDeletionQueueVulkan() void DeferredDeletionQueueVulkan::ReleaseResources(bool deleteImmediately) { - ScopeLock lock(_locker); const uint64 checkFrame = Engine::FrameCount - VULKAN_RESOURCE_DELETE_SAFE_FRAMES_COUNT; + ScopeLock lock(_locker); for (int32 i = 0; i < _entries.Count(); i++) { - Entry* e = &_entries[i]; - - if (deleteImmediately || (checkFrame > e->FrameNumber && (e->CmdBuffer == nullptr || e->FenceCounter < e->CmdBuffer->GetFenceSignaledCounter())) - ) + Entry* e = &_entries.Get()[i]; + if (deleteImmediately || (checkFrame > e->FrameNumber && (e->CmdBuffer == nullptr || e->FenceCounter < e->CmdBuffer->GetFenceSignaledCounter()))) { if (e->AllocationHandle == VK_NULL_HANDLE) { @@ -402,14 +400,15 @@ void DeferredDeletionQueueVulkan::ReleaseResources(bool deleteImmediately) { vmaDestroyBuffer(_device->Allocator, (VkBuffer)e->Handle, e->AllocationHandle); } +#if !BUILD_RELEASE else { CRASH; } +#endif } _entries.RemoveAt(i--); - if (_entries.IsEmpty()) break; } @@ -418,19 +417,17 @@ void DeferredDeletionQueueVulkan::ReleaseResources(bool deleteImmediately) void DeferredDeletionQueueVulkan::EnqueueGenericResource(Type type, uint64 handle, VmaAllocation allocation) { - ASSERT(handle != 0); - const auto queue = _device->GraphicsQueue; + ASSERT_LOW_LAYER(handle != 0); Entry entry; - queue->GetLastSubmittedInfo(entry.CmdBuffer, entry.FenceCounter); + _device->GraphicsQueue->GetLastSubmittedInfo(entry.CmdBuffer, entry.FenceCounter); entry.Handle = handle; entry.AllocationHandle = allocation; entry.StructureType = type; entry.FrameNumber = Engine::FrameCount; ScopeLock lock(_locker); - -#if BUILD_DEBUG +#if BUILD_DEBUG && 0 const Function ContainsHandle = [handle](const Entry& e) { return e.Handle == handle; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h index d1195c5db..3daefa1f3 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h @@ -63,19 +63,15 @@ class FenceVulkan friend FenceManagerVulkan; private: - VkFence _handle; bool _signaled; FenceManagerVulkan* _owner; public: - FenceVulkan(GPUDeviceVulkan* device, FenceManagerVulkan* owner, bool createSignaled); - ~FenceVulkan(); public: - inline VkFence GetHandle() const { return _handle; @@ -220,7 +216,7 @@ public: EnqueueGenericResource(type, (uint64)handle, allocation); } - auto ReleaseResources(bool deleteImmediately = false) -> void; + void ReleaseResources(bool deleteImmediately = false); private: @@ -629,11 +625,9 @@ public: // Try to use pool with available space inside for (int32 i = 0; i < pools.Count(); i++) { - auto pool = pools[i]; + auto pool = pools.Get()[i]; if (pool->HasRoom()) - { return pool; - } } // Create new pool From 2625144945e42da581a2285a1d81e280bb3561b5 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Sun, 28 Jan 2024 23:28:35 +0100 Subject: [PATCH 059/198] rename attribute and fix parsing --- .../CustomEditors/Editors/DoubleEditor.cs | 6 ++--- .../CustomEditors/Editors/FloatEditor.cs | 6 ++--- .../CustomEditors/Editors/Vector3Editor.cs | 6 ++--- .../Elements/DoubleValueElement.cs | 2 +- .../Elements/FloatValueElement.cs | 2 +- Source/Editor/GUI/Input/DoubleValueBox.cs | 2 +- Source/Editor/GUI/Input/FloatValueBox.cs | 2 +- Source/Editor/Utilities/ShuntingYardParser.cs | 8 +++--- Source/Editor/Utilities/Utils.cs | 26 ++++++------------- ...ttribute.cs => NumberCategoryAttribute.cs} | 12 ++++----- Source/Engine/Utilities/Utils.cs | 10 +++++++ 11 files changed, 42 insertions(+), 40 deletions(-) rename Source/Engine/Scripting/Attributes/Editor/{ValueCategoryAttribute.cs => NumberCategoryAttribute.cs} (64%) diff --git a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs index 0906d22af..a428fd448 100644 --- a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs @@ -4,7 +4,7 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -26,8 +26,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); - var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); - var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; + var categoryAttribute = attributes.FirstOrDefault(x => x is NumberCategoryAttribute); + var valueCategory = ((NumberCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var limit = attributes.FirstOrDefault(x => x is LimitAttribute); diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index 525541e99..c739269ab 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -4,7 +4,7 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -31,8 +31,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); - var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); - var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; + var categoryAttribute = attributes.FirstOrDefault(x => x is NumberCategoryAttribute); + var valueCategory = ((NumberCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var range = attributes.FirstOrDefault(x => x is RangeAttribute); diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index 9826c339f..fe3ff2b74 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -4,7 +4,7 @@ using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; using FlaxEngine.GUI; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -75,7 +75,7 @@ namespace FlaxEditor.CustomEditors.Editors if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); - var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var categoryAttribute = (NumberCategoryAttribute)attributes.FirstOrDefault(x => x is NumberCategoryAttribute); if (categoryAttribute != null) category = categoryAttribute.Category; } @@ -261,7 +261,7 @@ namespace FlaxEditor.CustomEditors.Editors if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); - var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var categoryAttribute = (NumberCategoryAttribute)attributes.FirstOrDefault(x => x is NumberCategoryAttribute); if (categoryAttribute != null) category = categoryAttribute.Category; } diff --git a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs index 460a344ff..4f121204f 100644 --- a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs @@ -5,7 +5,7 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Elements { diff --git a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs index 413a341c1..9661e391d 100644 --- a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs @@ -5,7 +5,7 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Elements { diff --git a/Source/Editor/GUI/Input/DoubleValueBox.cs b/Source/Editor/GUI/Input/DoubleValueBox.cs index 46e6402cb..d7c4950f6 100644 --- a/Source/Editor/GUI/Input/DoubleValueBox.cs +++ b/Source/Editor/GUI/Input/DoubleValueBox.cs @@ -3,7 +3,7 @@ using System; using FlaxEditor.Utilities; using FlaxEngine; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.GUI.Input { diff --git a/Source/Editor/GUI/Input/FloatValueBox.cs b/Source/Editor/GUI/Input/FloatValueBox.cs index 64ea000fa..2fccba8e8 100644 --- a/Source/Editor/GUI/Input/FloatValueBox.cs +++ b/Source/Editor/GUI/Input/FloatValueBox.cs @@ -4,7 +4,7 @@ using System; using System.Globalization; using FlaxEditor.Utilities; using FlaxEngine; -using Utils = FlaxEditor.Utilities.Utils; +using Utils = FlaxEngine.Utils; namespace FlaxEditor.GUI.Input { diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index e7c5b5298..f8fa05e5b 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -243,6 +243,11 @@ namespace FlaxEditor.Utilities } else if (type == TokenType.Variable) { + if (previous == TokenType.Number) + { + previous = TokenType.Operator; + yield return new Token(TokenType.Operator, "*"); + } // Continue till the end of the variable while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Variable) { @@ -379,10 +384,7 @@ namespace FlaxEditor.Utilities // we assume the remaining values are all factors to be multiplied if (stack.Count > 1) { - var stackContent = string.Join(",", stack.ToList()); - Debug.Log($"parsing numbers, stack is {stackContent}"); var v1 = stack.Pop(); - Debug.Log($"first on stack: {v1}"); while (stack.Count > 0) v1 *= stack.Pop(); return v1; diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 0df4fe573..d1db68046 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -58,16 +58,6 @@ namespace FlaxEditor.Utilities /// public static readonly string FlaxEngineAssemblyName = "FlaxEngine.CSharp"; - /// - /// A category of number values used for formatting and input boxes - /// - public enum ValueCategory - { - None, - Distance, - Angle - } - /// /// Tries to parse number in the name brackets at the end of the value and then increment it to create a new name. /// Supports numbers at the end without brackets. @@ -1187,11 +1177,11 @@ namespace FlaxEditor.Utilities /// the value to format /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign /// the formatted string - public static string FormatFloat(float value, ValueCategory category) + public static string FormatFloat(float value, FlaxEngine.Utils.ValueCategory category) { switch (category) { - case ValueCategory.Distance: + case FlaxEngine.Utils.ValueCategory.Distance: var absValue = Mathf.Abs(value); // in case a unit != cm this would be (value / Maters2Units * 100) if (absValue < Units.Meters2Units) @@ -1199,8 +1189,8 @@ namespace FlaxEditor.Utilities if (absValue < Units.Meters2Units * 1000) return (value / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "m"; return (value / 1000 / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "km"; - case ValueCategory.Angle: return value.ToString("g7", CultureInfo.InvariantCulture) + "°"; - case ValueCategory.None: + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString("g7", CultureInfo.InvariantCulture) + "°"; + case FlaxEngine.Utils.ValueCategory.None: default: return FormatFloat(value); } @@ -1212,11 +1202,11 @@ namespace FlaxEditor.Utilities /// the value to format /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign /// the formatted string - public static string FormatFloat(double value, ValueCategory category) + public static string FormatFloat(double value, FlaxEngine.Utils.ValueCategory category) { switch (category) { - case ValueCategory.Distance: + case FlaxEngine.Utils.ValueCategory.Distance: var absValue = Mathf.Abs(value); // in case a unit != cm this would be (value / Maters2Units * 100) if (absValue < Units.Meters2Units) @@ -1224,8 +1214,8 @@ namespace FlaxEditor.Utilities if (absValue < Units.Meters2Units * 1000) return (value / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "m"; return (value / 1000 / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "km"; - case ValueCategory.Angle: return value.ToString("g17", CultureInfo.InvariantCulture) + "°"; - case ValueCategory.None: + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString("g17", CultureInfo.InvariantCulture) + "°"; + case FlaxEngine.Utils.ValueCategory.None: default: return FormatFloat(value); } diff --git a/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/NumberCategoryAttribute.cs similarity index 64% rename from Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs rename to Source/Engine/Scripting/Attributes/Editor/NumberCategoryAttribute.cs index 3c6da2286..6b5726bb1 100644 --- a/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/NumberCategoryAttribute.cs @@ -11,23 +11,23 @@ namespace FlaxEngine /// [Serializable] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public sealed class ValueCategoryAttribute : Attribute + public sealed class NumberCategoryAttribute : Attribute { /// /// The value category used for formatting. /// - public FlaxEditor.Utilities.Utils.ValueCategory Category; + public Utils.ValueCategory Category; - private ValueCategoryAttribute() + private NumberCategoryAttribute() { - Category = FlaxEditor.Utilities.Utils.ValueCategory.None; + Category = Utils.ValueCategory.None; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The value category. - public ValueCategoryAttribute(FlaxEditor.Utilities.Utils.ValueCategory category) + public NumberCategoryAttribute(Utils.ValueCategory category) { Category = category; } diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs index cb385efcf..195bacf96 100644 --- a/Source/Engine/Utilities/Utils.cs +++ b/Source/Engine/Utilities/Utils.cs @@ -1038,5 +1038,15 @@ namespace FlaxEngine parameterTypes = Array.Empty(); return parameterTypes; } + + /// + /// A category of number values used for formatting and input boxes + /// + public enum ValueCategory + { + None, + Distance, + Angle + } } } From 20dbe1565198b8241a81451a4472232364f067d2 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Sun, 28 Jan 2024 23:52:52 +0100 Subject: [PATCH 060/198] add configuration options --- Source/Editor/Utilities/Units.cs | 12 ++++++++++++ Source/Editor/Utilities/Utils.cs | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/Source/Editor/Utilities/Units.cs b/Source/Editor/Utilities/Units.cs index 00d53a463..1256ccd63 100644 --- a/Source/Editor/Utilities/Units.cs +++ b/Source/Editor/Utilities/Units.cs @@ -6,4 +6,16 @@ public class Units /// Factor of units per meter. /// public static readonly float Meters2Units = 100f; + + // the next two bools could be cached values in the user preferences + + /// + /// Set it to false to always show game units without any postfix + /// + public static bool UseUnitsFormatting = true; + + /// + /// If set to true, the distance unit is chosen on the magnitude, otherwise it's meters + /// + public static bool AutomaticUnitsFormatting = true; } diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index d1db68046..c5e418ae2 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1179,9 +1179,13 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(float value, FlaxEngine.Utils.ValueCategory category) { + if (!Units.UseUnitsFormatting) + return FormatFloat(value); switch (category) { case FlaxEngine.Utils.ValueCategory.Distance: + if (!Units.AutomaticUnitsFormatting) + return (value / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "m"; var absValue = Mathf.Abs(value); // in case a unit != cm this would be (value / Maters2Units * 100) if (absValue < Units.Meters2Units) @@ -1204,9 +1208,13 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(double value, FlaxEngine.Utils.ValueCategory category) { + if (!Units.UseUnitsFormatting) + return FormatFloat(value); switch (category) { case FlaxEngine.Utils.ValueCategory.Distance: + if (!Units.AutomaticUnitsFormatting) + return (value / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "m"; var absValue = Mathf.Abs(value); // in case a unit != cm this would be (value / Maters2Units * 100) if (absValue < Units.Meters2Units) From ef41ad6074a9e157496b2be3d46e0c195668ddc3 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Mon, 29 Jan 2024 07:34:58 +0100 Subject: [PATCH 061/198] add units to transform editor, some collider values --- Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs | 6 ++++++ Source/Engine/Physics/Colliders/Collider.h | 4 ++-- Source/Engine/Physics/Colliders/SphereCollider.h | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 4c153e759..a33163aab 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -42,10 +42,13 @@ namespace FlaxEditor.CustomEditors.Editors var grayOutFactor = 0.6f; XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor); XElement.ValueBox.BorderSelectedColor = AxisColorX; + XElement.ValueBox.Category = Utils.ValueCategory.Distance; YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor); YElement.ValueBox.BorderSelectedColor = AxisColorY; + YElement.ValueBox.Category = Utils.ValueCategory.Distance; ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor); ZElement.ValueBox.BorderSelectedColor = AxisColorZ; + ZElement.ValueBox.Category = Utils.ValueCategory.Distance; } } @@ -65,10 +68,13 @@ namespace FlaxEditor.CustomEditors.Editors var grayOutFactor = 0.6f; XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor); XElement.ValueBox.BorderSelectedColor = AxisColorX; + XElement.ValueBox.Category = Utils.ValueCategory.Angle; YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor); YElement.ValueBox.BorderSelectedColor = AxisColorY; + YElement.ValueBox.Category = Utils.ValueCategory.Angle; ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor); ZElement.ValueBox.BorderSelectedColor = AxisColorZ; + ZElement.ValueBox.Category = Utils.ValueCategory.Angle; } } diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h index bd17aa27a..e5a873b77 100644 --- a/Source/Engine/Physics/Colliders/Collider.h +++ b/Source/Engine/Physics/Colliders/Collider.h @@ -52,7 +52,7 @@ public: /// /// Gets the center of the collider, measured in the object's local space. /// - API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE Vector3 GetCenter() const { return _center; @@ -66,7 +66,7 @@ public: /// /// Gets the contact offset. Colliders whose distance is less than the sum of their ContactOffset values will generate contacts. The contact offset must be positive. Contact offset allows the collision detection system to predictively enforce the contact constraint even when the objects are slightly separated. /// - API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(2.0f), Limit(0, 100), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(2.0f), Limit(0, 100), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetContactOffset() const { return _contactOffset; diff --git a/Source/Engine/Physics/Colliders/SphereCollider.h b/Source/Engine/Physics/Colliders/SphereCollider.h index 061372af4..6adaf76ba 100644 --- a/Source/Engine/Physics/Colliders/SphereCollider.h +++ b/Source/Engine/Physics/Colliders/SphereCollider.h @@ -21,7 +21,7 @@ public: /// Gets the radius of the sphere, measured in the object's local space. /// /// The sphere radius will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetRadius() const { return _radius; From a0741dd9de409a1090e216623072a077952d7d19 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Mon, 29 Jan 2024 08:24:55 +0100 Subject: [PATCH 062/198] units for quaternion, transform and more collider values --- .../Editor/CustomEditors/Editors/QuaternionEditor.cs | 3 +++ Source/Engine/Core/Math/Transform.h | 4 ++-- Source/Engine/Physics/Colliders/BoxCollider.h | 2 +- Source/Engine/Physics/Colliders/CapsuleCollider.h | 4 ++-- Source/Engine/Physics/Colliders/CharacterController.h | 10 +++++----- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs index 3a469dd1a..1e41264d0 100644 --- a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs @@ -45,14 +45,17 @@ namespace FlaxEditor.CustomEditors.Editors gridControl.SlotsVertically = 1; XElement = grid.FloatValue(); + XElement.ValueBox.Category = Utils.ValueCategory.Angle; XElement.ValueBox.ValueChanged += OnValueChanged; XElement.ValueBox.SlidingEnd += ClearToken; YElement = grid.FloatValue(); + YElement.ValueBox.Category = Utils.ValueCategory.Angle; YElement.ValueBox.ValueChanged += OnValueChanged; YElement.ValueBox.SlidingEnd += ClearToken; ZElement = grid.FloatValue(); + ZElement.ValueBox.Category = Utils.ValueCategory.Angle; ZElement.ValueBox.ValueChanged += OnValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; diff --git a/Source/Engine/Core/Math/Transform.h b/Source/Engine/Core/Math/Transform.h index 1079b4c23..46b3168c4 100644 --- a/Source/Engine/Core/Math/Transform.h +++ b/Source/Engine/Core/Math/Transform.h @@ -18,12 +18,12 @@ API_STRUCT() struct FLAXENGINE_API Transform /// /// The translation vector of the transform. /// - API_FIELD(Attributes="EditorOrder(10), EditorDisplay(null, \"Position\")") Vector3 Translation; + API_FIELD(Attributes="EditorOrder(10), EditorDisplay(null, \"Position\"), NumberCategory(Utils.ValueCategory.Distance)") Vector3 Translation; /// /// The rotation of the transform. /// - API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Rotation\")") Quaternion Orientation; + API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Rotation\"), NumberCategory(Utils.ValueCategory.Angle)") Quaternion Orientation; /// /// The scale vector of the transform. diff --git a/Source/Engine/Physics/Colliders/BoxCollider.h b/Source/Engine/Physics/Colliders/BoxCollider.h index 3c15ce640..a52817d66 100644 --- a/Source/Engine/Physics/Colliders/BoxCollider.h +++ b/Source/Engine/Physics/Colliders/BoxCollider.h @@ -23,7 +23,7 @@ public: /// Gets the size of the box, measured in the object's local space. /// /// The box size will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(typeof(Float3), \"100,100,100\"), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(typeof(Float3), \"100,100,100\"), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE Float3 GetSize() const { return _size; diff --git a/Source/Engine/Physics/Colliders/CapsuleCollider.h b/Source/Engine/Physics/Colliders/CapsuleCollider.h index 8eff1c164..e6be1e2c2 100644 --- a/Source/Engine/Physics/Colliders/CapsuleCollider.h +++ b/Source/Engine/Physics/Colliders/CapsuleCollider.h @@ -25,7 +25,7 @@ public: /// Gets the radius of the sphere, measured in the object's local space. /// /// The sphere radius will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(20.0f), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(20.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetRadius() const { return _radius; @@ -41,7 +41,7 @@ public: /// Gets the height of the capsule, measured in the object's local space between the centers of the hemispherical ends. /// /// The capsule height will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(100.0f), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(100.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetHeight() const { return _height; diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h index 737d98728..5eb4df965 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.h +++ b/Source/Engine/Physics/Colliders/CharacterController.h @@ -73,7 +73,7 @@ public: /// /// Gets the radius of the sphere, measured in the object's local space. The sphere radius will be scaled by the actor's world scale. /// - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") float GetRadius() const; /// @@ -84,7 +84,7 @@ public: /// /// Gets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale. /// - API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") float GetHeight() const; /// @@ -95,7 +95,7 @@ public: /// /// Gets the slope limit (in degrees). Limits the collider to only climb slopes that are less steep (in degrees) than the indicated value. /// - API_PROPERTY(Attributes="EditorOrder(210), DefaultValue(45.0f), Limit(0, 100), EditorDisplay(\"Character Controller\")") + API_PROPERTY(Attributes="EditorOrder(210), DefaultValue(45.0f), Limit(0, 100), EditorDisplay(\"Character Controller\"), NumberCategory(Utils.ValueCategory.Angle)") float GetSlopeLimit() const; /// @@ -117,7 +117,7 @@ public: /// /// Gets the step height. The character will step up a stair only if it is closer to the ground than the indicated value. This should not be greater than the Character Controller’s height or it will generate an error. /// - API_PROPERTY(Attributes="EditorOrder(220), DefaultValue(30.0f), Limit(0), EditorDisplay(\"Character Controller\")") + API_PROPERTY(Attributes="EditorOrder(220), DefaultValue(30.0f), Limit(0), EditorDisplay(\"Character Controller\"), NumberCategory(Utils.ValueCategory.Distance)") float GetStepOffset() const; /// @@ -139,7 +139,7 @@ public: /// /// Gets the minimum move distance of the character controller. The minimum traveled distance to consider. If traveled distance is smaller, the character doesn't move. This is used to stop the recursive motion algorithm when remaining distance to travel is small. /// - API_PROPERTY(Attributes="EditorOrder(230), DefaultValue(0.0f), Limit(0, 1000), EditorDisplay(\"Character Controller\")") + API_PROPERTY(Attributes="EditorOrder(230), DefaultValue(0.0f), Limit(0, 1000), EditorDisplay(\"Character Controller\"), NumberCategory(Utils.ValueCategory.Distance)") float GetMinMoveDistance() const; /// From dcda7d96bbaea79143a59d44b94ce237598beb5e Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Mon, 29 Jan 2024 09:06:05 +0100 Subject: [PATCH 063/198] use C# default for double formatting --- Source/Editor/Utilities/Utils.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index c5e418ae2..ac5a3c3c6 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1214,15 +1214,15 @@ namespace FlaxEditor.Utilities { case FlaxEngine.Utils.ValueCategory.Distance: if (!Units.AutomaticUnitsFormatting) - return (value / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "m"; + return (value / Units.Meters2Units).ToString("g15", CultureInfo.InvariantCulture) + "m"; var absValue = Mathf.Abs(value); // in case a unit != cm this would be (value / Maters2Units * 100) if (absValue < Units.Meters2Units) - return value.ToString("g17", CultureInfo.InvariantCulture) + "cm"; + return value.ToString("g15", CultureInfo.InvariantCulture) + "cm"; if (absValue < Units.Meters2Units * 1000) - return (value / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "m"; - return (value / 1000 / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "km"; - case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString("g17", CultureInfo.InvariantCulture) + "°"; + return (value / Units.Meters2Units).ToString("g15", CultureInfo.InvariantCulture) + "m"; + return (value / 1000 / Units.Meters2Units).ToString("g15", CultureInfo.InvariantCulture) + "km"; + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString("g15", CultureInfo.InvariantCulture) + "°"; case FlaxEngine.Utils.ValueCategory.None: default: return FormatFloat(value); @@ -1255,7 +1255,7 @@ namespace FlaxEditor.Utilities return "Infinity"; if (double.IsNegativeInfinity(value) || value == double.MinValue) return "-Infinity"; - string str = value.ToString("g17", CultureInfo.InvariantCulture); + string str = value.ToString("g15", CultureInfo.InvariantCulture); return FormatFloat(str, value < 0); } From bb00d29098f977ae9739e86a2cfb8604f81a43dd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 29 Jan 2024 13:46:50 +0100 Subject: [PATCH 064/198] Codestyle fixes and optimizations --- .../GraphicsDevice/Vulkan/CmdBufferVulkan.cpp | 21 ++- .../GraphicsDevice/Vulkan/CmdBufferVulkan.h | 15 +- Source/Engine/GraphicsDevice/Vulkan/Config.h | 2 +- .../Vulkan/DescriptorSetVulkan.cpp | 129 +++++++++--------- .../Vulkan/DescriptorSetVulkan.h | 104 +++----------- .../GraphicsDevice/Vulkan/GPUAdapterVulkan.h | 3 - .../GraphicsDevice/Vulkan/GPUBufferVulkan.h | 10 -- .../Vulkan/GPUContextVulkan.cpp | 8 +- .../GraphicsDevice/Vulkan/GPUContextVulkan.h | 7 - .../GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp | 95 ++++--------- .../GraphicsDevice/Vulkan/GPUDeviceVulkan.h | 107 ++------------- .../Vulkan/GPUPipelineStateVulkan.cpp | 8 +- .../Vulkan/GPUPipelineStateVulkan.h | 21 --- .../GraphicsDevice/Vulkan/GPUSamplerVulkan.h | 2 - .../Vulkan/GPUShaderProgramVulkan.h | 14 -- .../GraphicsDevice/Vulkan/GPUShaderVulkan.h | 11 -- .../Vulkan/GPUSwapChainVulkan.h | 10 -- .../GraphicsDevice/Vulkan/GPUTextureVulkan.h | 10 -- .../Vulkan/GPUTimerQueryVulkan.h | 6 - .../GraphicsDevice/Vulkan/QueueVulkan.cpp | 14 +- .../GraphicsDevice/Vulkan/QueueVulkan.h | 11 +- .../Vulkan/RenderToolsVulkan.cpp | 4 + .../GraphicsDevice/Vulkan/RenderToolsVulkan.h | 32 ++--- .../Vulkan/ResourceOwnerVulkan.h | 4 - .../Vulkan/VulkanPlatformBase.h | 1 - 25 files changed, 163 insertions(+), 486 deletions(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp index 205f4d092..22471ed8c 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp @@ -129,15 +129,13 @@ void CmdBufferVulkan::RefreshFenceStatus() { if (_state == State::Submitted) { - auto fenceManager = _fence->GetOwner(); - if (fenceManager->IsFenceSignaled(_fence)) + if (_device->FenceManager.IsFenceSignaled(_fence)) { _state = State::ReadyForBegin; - _submittedWaitSemaphores.Clear(); vkResetCommandBuffer(_commandBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); - _fence->GetOwner()->ResetFence(_fence); + _device->FenceManager.ResetFence(_fence); _fenceSignaledCounter++; if (_descriptorPoolSetContainer) @@ -149,7 +147,7 @@ void CmdBufferVulkan::RefreshFenceStatus() } else { - ASSERT(!_fence->IsSignaled()); + ASSERT(!_fence->IsSignaled); } } @@ -158,8 +156,8 @@ CmdBufferVulkan::CmdBufferVulkan(GPUDeviceVulkan* device, CmdBufferPoolVulkan* p , _commandBuffer(VK_NULL_HANDLE) , _state(State::ReadyForBegin) , _fence(nullptr) - , _fenceSignaledCounter(0) , _submittedFenceCounter(0) + , _fenceSignaledCounter(0) , _commandBufferPool(pool) { VkCommandBufferAllocateInfo createCmdBufInfo; @@ -167,7 +165,6 @@ CmdBufferVulkan::CmdBufferVulkan(GPUDeviceVulkan* device, CmdBufferPoolVulkan* p createCmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; createCmdBufInfo.commandBufferCount = 1; createCmdBufInfo.commandPool = _commandBufferPool->GetHandle(); - VALIDATE_VULKAN_RESULT(vkAllocateCommandBuffers(_device->Device, &createCmdBufInfo, &_commandBuffer)); _fence = _device->FenceManager.AllocateFence(); } @@ -217,17 +214,16 @@ CmdBufferPoolVulkan::~CmdBufferPoolVulkan() { for (int32 i = 0; i < _cmdBuffers.Count(); i++) { - Delete(_cmdBuffers[i]); + Delete(_cmdBuffers.Get()[i]); } - vkDestroyCommandPool(_device->Device, _handle, nullptr); } -void CmdBufferPoolVulkan::RefreshFenceStatus(CmdBufferVulkan* skipCmdBuffer) +void CmdBufferPoolVulkan::RefreshFenceStatus(const CmdBufferVulkan* skipCmdBuffer) { for (int32 i = 0; i < _cmdBuffers.Count(); i++) { - auto cmdBuffer = _cmdBuffers[i]; + const auto cmdBuffer = _cmdBuffers.Get()[i]; if (cmdBuffer != skipCmdBuffer) { cmdBuffer->RefreshFenceStatus(); @@ -250,9 +246,8 @@ void CmdBufferManagerVulkan::SubmitActiveCmdBuffer(SemaphoreVulkan* signalSemaph if (!_activeCmdBuffer->IsSubmitted() && _activeCmdBuffer->HasBegun()) { if (_activeCmdBuffer->IsInsideRenderPass()) - { _activeCmdBuffer->EndRenderPass(); - } + #if VULKAN_USE_QUERIES // Pause all active queries diff --git a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h index e54085ecd..6d0f7d83b 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h @@ -21,7 +21,6 @@ class CmdBufferVulkan friend QueueVulkan; public: - enum class State { ReadyForBegin, @@ -32,7 +31,6 @@ public: }; private: - GPUDeviceVulkan* _device; VkCommandBuffer _commandBuffer; State _state; @@ -57,13 +55,11 @@ private: DescriptorPoolSetContainerVulkan* _descriptorPoolSetContainer = nullptr; public: - CmdBufferVulkan(GPUDeviceVulkan* device, CmdBufferPoolVulkan* pool); ~CmdBufferVulkan(); public: - CmdBufferPoolVulkan* GetOwner() const { return _commandBufferPool; @@ -120,7 +116,6 @@ public: } public: - void AddWaitSemaphore(VkPipelineStageFlags waitFlags, SemaphoreVulkan* waitSemaphore); void Begin(); @@ -147,8 +142,8 @@ public: class CmdBufferPoolVulkan { friend class CmdBufferManagerVulkan; -private: +private: GPUDeviceVulkan* _device; VkCommandPool _handle; Array _cmdBuffers; @@ -158,25 +153,22 @@ private: void Create(uint32 queueFamilyIndex); public: - CmdBufferPoolVulkan(GPUDeviceVulkan* device); ~CmdBufferPoolVulkan(); public: - inline VkCommandPool GetHandle() const { ASSERT(_handle != VK_NULL_HANDLE); return _handle; } - void RefreshFenceStatus(CmdBufferVulkan* skipCmdBuffer = nullptr); + void RefreshFenceStatus(const CmdBufferVulkan* skipCmdBuffer = nullptr); }; class CmdBufferManagerVulkan { private: - GPUDeviceVulkan* _device; CmdBufferPoolVulkan _pool; QueueVulkan* _queue; @@ -184,11 +176,9 @@ private: Array _queriesInProgress; public: - CmdBufferManagerVulkan(GPUDeviceVulkan* device, GPUContextVulkan* context); public: - FORCE_INLINE VkCommandPool GetHandle() const { return _pool.GetHandle(); @@ -217,7 +207,6 @@ public: } public: - void SubmitActiveCmdBuffer(SemaphoreVulkan* signalSemaphore = nullptr); void WaitForCmdBuffer(CmdBufferVulkan* cmdBuffer, float timeInSecondsToWait = 1.0f); diff --git a/Source/Engine/GraphicsDevice/Vulkan/Config.h b/Source/Engine/GraphicsDevice/Vulkan/Config.h index 55ffb5d58..95162de9f 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Config.h +++ b/Source/Engine/GraphicsDevice/Vulkan/Config.h @@ -23,7 +23,7 @@ /// /// Default amount of frames to wait until resource delete. /// -#define VULKAN_RESOURCE_DELETE_SAFE_FRAMES_COUNT 10 +#define VULKAN_RESOURCE_DELETE_SAFE_FRAMES_COUNT 20 #define VULKAN_ENABLE_API_DUMP 0 #define VULKAN_RESET_QUERY_POOLS 0 diff --git a/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.cpp index 2dad4bc9b..fdd844536 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.cpp @@ -12,29 +12,12 @@ #include "Engine/Threading/Threading.h" #include "Engine/Engine/Engine.h" -void DescriptorSetLayoutInfoVulkan::CacheTypesUsageID() -{ - static CriticalSection locker; - static uint32 uniqueID = 1; - static Dictionary typesUsageHashMap; - - const uint32 typesUsageHash = Crc::MemCrc32(_layoutTypes, sizeof(_layoutTypes)); - ScopeLock lock(locker); - uint32 id; - if (!typesUsageHashMap.TryGet(typesUsageHash, id)) - { - id = uniqueID++; - typesUsageHashMap.Add(typesUsageHash, id); - } - _typesUsageID = id; -} - void DescriptorSetLayoutInfoVulkan::AddBindingsForStage(VkShaderStageFlagBits stageFlags, DescriptorSet::Stage descSet, const SpirvShaderDescriptorInfo* descriptorInfo) { const int32 descriptorSetIndex = descSet; - if (descriptorSetIndex >= _setLayouts.Count()) - _setLayouts.Resize(descriptorSetIndex + 1); - SetLayout& descSetLayout = _setLayouts[descriptorSetIndex]; + if (descriptorSetIndex >= SetLayouts.Count()) + SetLayouts.Resize(descriptorSetIndex + 1); + SetLayout& descSetLayout = SetLayouts[descriptorSetIndex]; VkDescriptorSetLayoutBinding binding; binding.stageFlags = stageFlags; @@ -46,25 +29,26 @@ void DescriptorSetLayoutInfoVulkan::AddBindingsForStage(VkShaderStageFlagBits st binding.descriptorType = descriptor.DescriptorType; binding.descriptorCount = descriptor.Count; - _layoutTypes[binding.descriptorType]++; - descSetLayout.LayoutBindings.Add(binding); - _hash = Crc::MemCrc32(&binding, sizeof(binding), _hash); + LayoutTypes[binding.descriptorType]++; + descSetLayout.Add(binding); + Hash = Crc::MemCrc32(&binding, sizeof(binding), Hash); } } bool DescriptorSetLayoutInfoVulkan::operator==(const DescriptorSetLayoutInfoVulkan& other) const { - if (other._setLayouts.Count() != _setLayouts.Count()) + if (other.SetLayouts.Count() != SetLayouts.Count()) return false; - if (other._typesUsageID != _typesUsageID) + if (other.TypesUsageID != TypesUsageID) return false; - for (int32 index = 0; index < other._setLayouts.Count(); index++) + for (int32 index = 0; index < other.SetLayouts.Count(); index++) { - const int32 bindingsCount = _setLayouts[index].LayoutBindings.Count(); - if (other._setLayouts[index].LayoutBindings.Count() != bindingsCount) + auto& setLayout = SetLayouts[index]; + auto& setLayoutOther = other.SetLayouts[index]; + if (setLayoutOther.Count() != setLayout.Count()) return false; - if (bindingsCount != 0 && Platform::MemoryCompare(other._setLayouts[index].LayoutBindings.Get(), _setLayouts[index].LayoutBindings.Get(), bindingsCount * sizeof(VkDescriptorSetLayoutBinding))) + if (setLayout.Count() != 0 && Platform::MemoryCompare(setLayoutOther.Get(), setLayout.Get(), setLayout.Count() * sizeof(VkDescriptorSetLayoutBinding))) return false; } @@ -72,51 +56,63 @@ bool DescriptorSetLayoutInfoVulkan::operator==(const DescriptorSetLayoutInfoVulk } DescriptorSetLayoutVulkan::DescriptorSetLayoutVulkan(GPUDeviceVulkan* device) - : _device(device) + : Device(device) { } DescriptorSetLayoutVulkan::~DescriptorSetLayoutVulkan() { - for (VkDescriptorSetLayout& handle : _handles) + for (VkDescriptorSetLayout& handle : Handles) { - _device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::DescriptorSetLayout, handle); + Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::DescriptorSetLayout, handle); } } void DescriptorSetLayoutVulkan::Compile() { - ASSERT(_handles.IsEmpty()); + ASSERT(Handles.IsEmpty()); // Validate device limits for the engine - const VkPhysicalDeviceLimits& limits = _device->PhysicalDeviceLimits; - ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_SAMPLER] + _layoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER] < limits.maxDescriptorSetSamplers); - ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER]+ _layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] < limits.maxDescriptorSetUniformBuffers); - ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] < limits.maxDescriptorSetUniformBuffersDynamic); - ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER] + _layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC] < limits.maxDescriptorSetStorageBuffers); - ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC] < limits.maxDescriptorSetStorageBuffersDynamic); - ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER] + _layoutTypes[VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE] + _layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER] < limits.maxDescriptorSetSampledImages); - ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_IMAGE] + _layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]< limits.maxDescriptorSetStorageImages); + const VkPhysicalDeviceLimits& limits = Device->PhysicalDeviceLimits; + ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_SAMPLER] + LayoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER] < limits.maxDescriptorSetSamplers); + ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER]+ LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] < limits.maxDescriptorSetUniformBuffers); + ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] < limits.maxDescriptorSetUniformBuffersDynamic); + ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER] + LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC] < limits.maxDescriptorSetStorageBuffers); + ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC] < limits.maxDescriptorSetStorageBuffersDynamic); + ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER] + LayoutTypes[VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE] + LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER] < limits.maxDescriptorSetSampledImages); + ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_IMAGE] + LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]< limits.maxDescriptorSetStorageImages); - _handles.Resize(_setLayouts.Count()); - for (int32 i = 0; i < _setLayouts.Count(); i++) + Handles.Resize(SetLayouts.Count()); + for (int32 i = 0; i < SetLayouts.Count(); i++) { - auto& layout = _setLayouts[i]; + auto& layout = SetLayouts[i]; VkDescriptorSetLayoutCreateInfo layoutInfo; RenderToolsVulkan::ZeroStruct(layoutInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO); - layoutInfo.bindingCount = layout.LayoutBindings.Count(); - layoutInfo.pBindings = layout.LayoutBindings.Get(); - VALIDATE_VULKAN_RESULT(vkCreateDescriptorSetLayout(_device->Device, &layoutInfo, nullptr, &_handles[i])); + layoutInfo.bindingCount = layout.Count(); + layoutInfo.pBindings = layout.Get(); + VALIDATE_VULKAN_RESULT(vkCreateDescriptorSetLayout(Device->Device, &layoutInfo, nullptr, &Handles[i])); } - if (_typesUsageID == ~0) + if (TypesUsageID == ~0) { - CacheTypesUsageID(); + // Cache usage ID + static uint32 uniqueID = 1; + static Dictionary typesUsageHashMap; + const uint32 typesUsageHash = Crc::MemCrc32(LayoutTypes, sizeof(LayoutTypes)); + uint32 id; + Device->Locker.Lock(); + if (!typesUsageHashMap.TryGet(typesUsageHash, id)) + { + id = uniqueID++; + typesUsageHashMap.Add(typesUsageHash, id); + } + Device->Locker.Unlock(); + TypesUsageID = id; } - RenderToolsVulkan::ZeroStruct(_allocateInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO); - _allocateInfo.descriptorSetCount = _handles.Count(); - _allocateInfo.pSetLayouts = _handles.Get(); + RenderToolsVulkan::ZeroStruct(AllocateInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO); + AllocateInfo.descriptorSetCount = Handles.Count(); + AllocateInfo.pSetLayouts = Handles.Get(); } DescriptorPoolVulkan::DescriptorPoolVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutVulkan& layout) @@ -131,11 +127,11 @@ DescriptorPoolVulkan::DescriptorPoolVulkan(GPUDeviceVulkan* device, const Descri // The maximum amount of descriptor sets layout allocations to hold const uint32 MaxSetsAllocations = 256; - _descriptorSetsMax = MaxSetsAllocations * (VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? 1 : _layout.GetLayouts().Count()); + _descriptorSetsMax = MaxSetsAllocations * (VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? 1 : _layout.SetLayouts.Count()); for (uint32 typeIndex = VULKAN_DESCRIPTOR_TYPE_BEGIN; typeIndex <= VULKAN_DESCRIPTOR_TYPE_END; typeIndex++) { const VkDescriptorType descriptorType = (VkDescriptorType)typeIndex; - const uint32 typesUsed = _layout.GetTypesUsed(descriptorType); + const uint32 typesUsed = _layout.LayoutTypes[descriptorType]; if (typesUsed > 0) { VkDescriptorPoolSize& type = types.AddOne(); @@ -167,22 +163,22 @@ void DescriptorPoolVulkan::Track(const DescriptorSetLayoutVulkan& layout) #if !BUILD_RELEASE for (uint32 typeIndex = VULKAN_DESCRIPTOR_TYPE_BEGIN; typeIndex <= VULKAN_DESCRIPTOR_TYPE_END; typeIndex++) { - ASSERT(_layout.GetTypesUsed((VkDescriptorType)typeIndex) == layout.GetTypesUsed((VkDescriptorType)typeIndex)); + ASSERT(_layout.LayoutTypes[typeIndex] == layout.LayoutTypes[typeIndex]); } #endif - _allocatedDescriptorSetsCount += layout.GetLayouts().Count(); + _allocatedDescriptorSetsCount += layout.SetLayouts.Count(); _allocatedDescriptorSetsCountMax = Math::Max(_allocatedDescriptorSetsCount, _allocatedDescriptorSetsCountMax); } void DescriptorPoolVulkan::TrackRemoveUsage(const DescriptorSetLayoutVulkan& layout) { - // Check and increment our current type usage +#if !BUILD_RELEASE for (uint32 typeIndex = VULKAN_DESCRIPTOR_TYPE_BEGIN; typeIndex <= VULKAN_DESCRIPTOR_TYPE_END; typeIndex++) { - ASSERT(_layout.GetTypesUsed((VkDescriptorType)typeIndex) == layout.GetTypesUsed((VkDescriptorType)typeIndex)); + ASSERT(_layout.LayoutTypes[typeIndex] == layout.LayoutTypes[typeIndex]); } - - _allocatedDescriptorSetsCount -= layout.GetLayouts().Count(); +#endif + _allocatedDescriptorSetsCount -= layout.SetLayouts.Count(); } void DescriptorPoolVulkan::Reset() @@ -214,11 +210,10 @@ TypedDescriptorPoolSetVulkan::~TypedDescriptorPoolSetVulkan() bool TypedDescriptorPoolSetVulkan::AllocateDescriptorSets(const DescriptorSetLayoutVulkan& layout, VkDescriptorSet* outSets) { - const auto& layoutHandles = layout.GetHandles(); - if (layoutHandles.HasItems()) + if (layout.Handles.HasItems()) { auto* pool = _poolListCurrent->Element; - while (!pool->AllocateDescriptorSets(layout.GetAllocateInfo(), outSets)) + while (!pool->AllocateDescriptorSets(layout.AllocateInfo, outSets)) { pool = GetFreePool(true); } @@ -279,7 +274,7 @@ DescriptorPoolSetContainerVulkan::~DescriptorPoolSetContainerVulkan() TypedDescriptorPoolSetVulkan* DescriptorPoolSetContainerVulkan::AcquireTypedPoolSet(const DescriptorSetLayoutVulkan& layout) { - const uint32 hash = VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? layout.GetTypesUsageID() : GetHash(layout); + const uint32 hash = VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? layout.TypesUsageID : layout.Hash; TypedDescriptorPoolSetVulkan* typedPool; if (!_typedDescriptorPools.TryGet(hash, typedPool)) { @@ -361,12 +356,10 @@ PipelineLayoutVulkan::PipelineLayoutVulkan(GPUDeviceVulkan* device, const Descri _descriptorSetLayout.CopyFrom(layout); _descriptorSetLayout.Compile(); - const auto& layoutHandles = _descriptorSetLayout.GetHandles(); - VkPipelineLayoutCreateInfo createInfo; RenderToolsVulkan::ZeroStruct(createInfo, VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO); - createInfo.setLayoutCount = layoutHandles.Count(); - createInfo.pSetLayouts = layoutHandles.Get(); + createInfo.setLayoutCount = _descriptorSetLayout.Handles.Count(); + createInfo.pSetLayouts = _descriptorSetLayout.Handles.Get(); VALIDATE_VULKAN_RESULT(vkCreatePipelineLayout(_device->Device, &createInfo, nullptr, &_handle)); } diff --git a/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.h index d8d80b51e..c5ab265d2 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.h @@ -54,106 +54,62 @@ namespace DescriptorSet class DescriptorSetLayoutInfoVulkan { public: + typedef Array SetLayout; - struct SetLayout - { - Array LayoutBindings; - }; - -protected: - - uint32 _layoutTypes[VULKAN_DESCRIPTOR_TYPE_END]; - Array _setLayouts; - uint32 _hash = 0; - uint32 _typesUsageID = ~0; + uint32 Hash = 0; + uint32 TypesUsageID = ~0; + Array SetLayouts; + uint32 LayoutTypes[VULKAN_DESCRIPTOR_TYPE_END]; void CacheTypesUsageID(); public: - DescriptorSetLayoutInfoVulkan() { - Platform::MemoryClear(_layoutTypes, sizeof(_layoutTypes)); + Platform::MemoryClear(LayoutTypes, sizeof(LayoutTypes)); } -public: - - inline uint32 GetTypesUsed(VkDescriptorType type) const - { - return _layoutTypes[type]; - } - - const Array& GetLayouts() const - { - return _setLayouts; - } - - inline uint32 GetTypesUsageID() const - { - return _typesUsageID; - } - -public: - void AddBindingsForStage(VkShaderStageFlagBits stageFlags, DescriptorSet::Stage descSet, const SpirvShaderDescriptorInfo* descriptorInfo); void CopyFrom(const DescriptorSetLayoutInfoVulkan& info) { - Platform::MemoryCopy(_layoutTypes, info._layoutTypes, sizeof(_layoutTypes)); - _hash = info._hash; - _typesUsageID = info._typesUsageID; - _setLayouts = info._setLayouts; + Platform::MemoryCopy(LayoutTypes, info.LayoutTypes, sizeof(LayoutTypes)); + Hash = info.Hash; + TypesUsageID = info.TypesUsageID; + SetLayouts = info.SetLayouts; } bool operator==(const DescriptorSetLayoutInfoVulkan& other) const; friend inline uint32 GetHash(const DescriptorSetLayoutInfoVulkan& key) { - return key._hash; + return key.Hash; } }; class DescriptorSetLayoutVulkan : public DescriptorSetLayoutInfoVulkan { public: + typedef Array> HandlesArray; - typedef Array> DescriptorSetLayoutHandlesArray; - -private: - - GPUDeviceVulkan* _device; - DescriptorSetLayoutHandlesArray _handles; - VkDescriptorSetAllocateInfo _allocateInfo; - -public: + GPUDeviceVulkan* Device; + HandlesArray Handles; + VkDescriptorSetAllocateInfo AllocateInfo; DescriptorSetLayoutVulkan(GPUDeviceVulkan* device); ~DescriptorSetLayoutVulkan(); -public: - - inline const DescriptorSetLayoutHandlesArray& GetHandles() const - { - return _handles; - } - - inline const VkDescriptorSetAllocateInfo& GetAllocateInfo() const - { - return _allocateInfo; - } + void Compile(); friend inline uint32 GetHash(const DescriptorSetLayoutVulkan& key) { - return key._hash; + return key.Hash; } - - void Compile(); }; class DescriptorPoolVulkan { private: - GPUDeviceVulkan* _device; VkDescriptorPool _handle; @@ -164,12 +120,10 @@ private: const DescriptorSetLayoutVulkan& _layout; public: - DescriptorPoolVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutVulkan& layout); ~DescriptorPoolVulkan(); public: - inline VkDescriptorPool GetHandle() const { return _handle; @@ -182,7 +136,7 @@ public: inline bool CanAllocate(const DescriptorSetLayoutVulkan& layout) const { - return _descriptorSetsMax > _allocatedDescriptorSetsCount + layout.GetLayouts().Count(); + return _descriptorSetsMax > _allocatedDescriptorSetsCount + layout.SetLayouts.Count(); } inline uint32 GetAllocatedDescriptorSetsCount() const @@ -191,7 +145,6 @@ public: } public: - void Track(const DescriptorSetLayoutVulkan& layout); void TrackRemoveUsage(const DescriptorSetLayoutVulkan& layout); void Reset(); @@ -205,7 +158,6 @@ class TypedDescriptorPoolSetVulkan friend DescriptorPoolSetContainerVulkan; private: - GPUDeviceVulkan* _device; const DescriptorPoolSetContainerVulkan* _owner; const DescriptorSetLayoutVulkan& _layout; @@ -213,7 +165,6 @@ private: class PoolList { public: - DescriptorPoolVulkan* Element; PoolList* Next; @@ -228,7 +179,6 @@ private: PoolList* _poolListCurrent = nullptr; public: - TypedDescriptorPoolSetVulkan(GPUDeviceVulkan* device, const DescriptorPoolSetContainerVulkan* owner, const DescriptorSetLayoutVulkan& layout) : _device(device) , _owner(owner) @@ -247,7 +197,6 @@ public: } private: - DescriptorPoolVulkan* GetFreePool(bool forceNewPool = false); DescriptorPoolVulkan* PushNewPool(); void Reset(); @@ -256,20 +205,16 @@ private: class DescriptorPoolSetContainerVulkan { private: - GPUDeviceVulkan* _device; Dictionary _typedDescriptorPools; uint64 _lastFrameUsed; bool _used; public: - DescriptorPoolSetContainerVulkan(GPUDeviceVulkan* device); - ~DescriptorPoolSetContainerVulkan(); public: - TypedDescriptorPoolSetVulkan* AcquireTypedPoolSet(const DescriptorSetLayoutVulkan& layout); void Reset(); void SetUsed(bool used); @@ -288,13 +233,11 @@ public: class DescriptorPoolsManagerVulkan { private: - GPUDeviceVulkan* _device = nullptr; CriticalSection _locker; Array _poolSets; public: - DescriptorPoolsManagerVulkan(GPUDeviceVulkan* device); ~DescriptorPoolsManagerVulkan(); @@ -306,25 +249,20 @@ public: class PipelineLayoutVulkan { private: - GPUDeviceVulkan* _device; VkPipelineLayout _handle; DescriptorSetLayoutVulkan _descriptorSetLayout; public: - PipelineLayoutVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutInfoVulkan& layout); ~PipelineLayoutVulkan(); public: - inline VkPipelineLayout GetHandle() const { return _handle; } -public: - inline const DescriptorSetLayoutVulkan& GetDescriptorSetLayout() const { return _descriptorSetLayout; @@ -332,7 +270,7 @@ public: inline bool HasDescriptors() const { - return _descriptorSetLayout.GetLayouts().HasItems(); + return _descriptorSetLayout.SetLayouts.HasItems(); } }; @@ -357,14 +295,12 @@ struct DescriptorSetWriteContainerVulkan class DescriptorSetWriterVulkan { public: - VkWriteDescriptorSet* WriteDescriptors = nullptr; byte* BindingToDynamicOffset = nullptr; uint32* DynamicOffsets = nullptr; uint32 WritesCount = 0; public: - uint32 SetupDescriptorWrites(const SpirvShaderDescriptorInfo& info, VkWriteDescriptorSet* writeDescriptors, VkDescriptorImageInfo* imageInfo, VkDescriptorBufferInfo* bufferInfo, VkBufferView* texelBufferView, byte* bindingToDynamicOffset); bool WriteUniformBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, uint32 index = 0) const @@ -451,9 +387,7 @@ public: void SetDescriptorSet(VkDescriptorSet descriptorSet) const { for (uint32 i = 0; i < WritesCount; i++) - { WriteDescriptors[i].dstSet = descriptorSet; - } } }; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.h index b5b6f1dbc..c03676d9c 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.h @@ -14,7 +14,6 @@ class GPUAdapterVulkan : public GPUAdapter { public: - /// /// Initializes a new instance of the class. /// @@ -38,7 +37,6 @@ public: } public: - /// /// The GPU device handle. /// @@ -55,7 +53,6 @@ public: String Description; public: - // [GPUAdapter] bool IsValid() const override { diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUBufferVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUBufferVulkan.h index 3bcf0a978..716daf5e3 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUBufferVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUBufferVulkan.h @@ -13,7 +13,6 @@ class GPUBufferViewVulkan : public GPUBufferView, public DescriptorOwnerResourceVulkan { public: - GPUBufferViewVulkan() { } @@ -26,7 +25,6 @@ public: #endif public: - GPUDeviceVulkan* Device = nullptr; GPUBufferVulkan* Owner = nullptr; VkBuffer Buffer = VK_NULL_HANDLE; @@ -34,13 +32,10 @@ public: VkDeviceSize Size = 0; public: - void Init(GPUDeviceVulkan* device, GPUBufferVulkan* owner, VkBuffer buffer, VkDeviceSize size, VkBufferUsageFlags usage, PixelFormat format); - void Release(); public: - // [GPUResourceView] void* GetNativePtr() const override { @@ -59,13 +54,11 @@ public: class GPUBufferVulkan : public GPUResourceVulkan { private: - VkBuffer _buffer = VK_NULL_HANDLE; VmaAllocation _allocation = VK_NULL_HANDLE; GPUBufferViewVulkan _view; public: - /// /// Initializes a new instance of the class. /// @@ -77,7 +70,6 @@ public: } public: - /// /// Gets the Vulkan buffer handle. /// @@ -105,14 +97,12 @@ public: GPUBufferVulkan* Counter = nullptr; public: - // [GPUBuffer] GPUBufferView* View() const override; void* Map(GPUResourceMapMode mode) override; void Unmap() override; protected: - // [GPUBuffer] bool OnInit() override; void OnReleaseGPU() override; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index 79e882f6d..138454e36 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -20,18 +20,18 @@ // Ensure to match the indirect commands arguments layout static_assert(sizeof(GPUDispatchIndirectArgs) == sizeof(VkDispatchIndirectCommand), "Wrong size of GPUDrawIndirectArgs."); static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountX) == OFFSET_OF(VkDispatchIndirectCommand, x), "Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountX"); -static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountY) == OFFSET_OF(VkDispatchIndirectCommand, y),"Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountY"); +static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountY) == OFFSET_OF(VkDispatchIndirectCommand, y), "Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountY"); static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountZ) == OFFSET_OF(VkDispatchIndirectCommand, z), "Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountZ"); // static_assert(sizeof(GPUDrawIndirectArgs) == sizeof(VkDrawIndirectCommand), "Wrong size of GPUDrawIndirectArgs."); static_assert(OFFSET_OF(GPUDrawIndirectArgs, VerticesCount) == OFFSET_OF(VkDrawIndirectCommand, vertexCount), "Wrong offset for GPUDrawIndirectArgs::VerticesCount"); -static_assert(OFFSET_OF(GPUDrawIndirectArgs, InstanceCount) == OFFSET_OF(VkDrawIndirectCommand, instanceCount),"Wrong offset for GPUDrawIndirectArgs::InstanceCount"); +static_assert(OFFSET_OF(GPUDrawIndirectArgs, InstanceCount) == OFFSET_OF(VkDrawIndirectCommand, instanceCount), "Wrong offset for GPUDrawIndirectArgs::InstanceCount"); static_assert(OFFSET_OF(GPUDrawIndirectArgs, StartVertex) == OFFSET_OF(VkDrawIndirectCommand, firstVertex), "Wrong offset for GPUDrawIndirectArgs::StartVertex"); static_assert(OFFSET_OF(GPUDrawIndirectArgs, StartInstance) == OFFSET_OF(VkDrawIndirectCommand, firstInstance), "Wrong offset for GPUDrawIndirectArgs::StartInstance"); // static_assert(sizeof(GPUDrawIndexedIndirectArgs) == sizeof(VkDrawIndexedIndirectCommand), "Wrong size of GPUDrawIndexedIndirectArgs."); static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, IndicesCount) == OFFSET_OF(VkDrawIndexedIndirectCommand, indexCount), "Wrong offset for GPUDrawIndexedIndirectArgs::IndicesCount"); -static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, InstanceCount) == OFFSET_OF(VkDrawIndexedIndirectCommand, instanceCount),"Wrong offset for GPUDrawIndexedIndirectArgs::InstanceCount"); +static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, InstanceCount) == OFFSET_OF(VkDrawIndexedIndirectCommand, instanceCount), "Wrong offset for GPUDrawIndexedIndirectArgs::InstanceCount"); static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartIndex) == OFFSET_OF(VkDrawIndexedIndirectCommand, firstIndex), "Wrong offset for GPUDrawIndexedIndirectArgs::StartIndex"); static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartVertex) == OFFSET_OF(VkDrawIndexedIndirectCommand, vertexOffset), "Wrong offset for GPUDrawIndexedIndirectArgs::StartVertex"); static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartInstance) == OFFSET_OF(VkDrawIndexedIndirectCommand, firstInstance), "Wrong offset for GPUDrawIndexedIndirectArgs::StartInstance"); @@ -337,7 +337,7 @@ DescriptorPoolVulkan* GPUContextVulkan::AllocateDescriptorSets(const VkDescripto VkDescriptorSetAllocateInfo allocateInfo = descriptorSetAllocateInfo; DescriptorPoolVulkan* pool = nullptr; - const uint32 hash = VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? layout.GetTypesUsageID() : GetHash(layout); + const uint32 hash = VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? layout.TypesUsageID : layout.Hash; DescriptorPoolArray* typedDescriptorPools = _descriptorPools.TryGet(hash); if (typedDescriptorPools != nullptr) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h index 125bba8cf..c0968e742 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h @@ -41,7 +41,6 @@ class DescriptorSetLayoutVulkan; struct PipelineBarrierVulkan { public: - VkPipelineStageFlags SourceStage = 0; VkPipelineStageFlags DestStage = 0; Array> ImageBarriers; @@ -51,7 +50,6 @@ public: #endif public: - inline void Reset() { SourceStage = 0; @@ -85,7 +83,6 @@ public: class GPUContextVulkan : public GPUContext { private: - GPUDeviceVulkan* _device; QueueVulkan* _queue; CmdBufferManagerVulkan* _cmdBufferManager; @@ -116,7 +113,6 @@ private: Dictionary _descriptorPools; public: - /// /// Initializes a new instance of the class. /// @@ -130,7 +126,6 @@ public: ~GPUContextVulkan(); public: - QueueVulkan* GetQueue() const { return _queue; @@ -157,14 +152,12 @@ public: void EndRenderPass(); private: - void UpdateDescriptorSets(const struct SpirvShaderDescriptorInfo& descriptorInfo, class DescriptorSetWriterVulkan& dsWriter, bool& needsWrite); void UpdateDescriptorSets(GPUPipelineStateVulkan* pipelineState); void UpdateDescriptorSets(ComputePipelineStateVulkan* pipelineState); void OnDrawCall(); public: - // [GPUContext] void FrameBegin() override; void FrameEnd() override; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index 19578df89..7d8c3a46f 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -250,23 +250,18 @@ void SetupDebugLayerCallback() { default: createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; - // Fall-through... case 4: createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; - // Fall-through... case 3: createInfo.messageType |= VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; - // Fall-through... case 2: createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; createInfo.messageType |= VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; - // Fall-through... case 1: createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; createInfo.messageType |= VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT; break; case 0: - // Nothing to do break; } const VkResult result = vkCreateDebugUtilsMessengerEXT(GPUDeviceVulkan::Instance, &createInfo, nullptr, &Messenger); @@ -288,21 +283,16 @@ void SetupDebugLayerCallback() { default: createInfo.flags |= VK_DEBUG_REPORT_DEBUG_BIT_EXT; - // Fall-through... case 4: createInfo.flags |= VK_DEBUG_REPORT_INFORMATION_BIT_EXT; - // Fall-through... case 3: createInfo.flags |= VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; - // Fall-through... case 2: createInfo.flags |= VK_DEBUG_REPORT_WARNING_BIT_EXT; - // Fall-through... case 1: createInfo.flags |= VK_DEBUG_REPORT_ERROR_BIT_EXT; break; case 0: - // Nothing to do break; } const VkResult result = vkCreateDebugReportCallbackEXT(GPUDeviceVulkan::Instance, &createInfo, nullptr, &MsgCallback); @@ -354,14 +344,14 @@ DeferredDeletionQueueVulkan::~DeferredDeletionQueueVulkan() ASSERT(_entries.IsEmpty()); } -void DeferredDeletionQueueVulkan::ReleaseResources(bool deleteImmediately) +void DeferredDeletionQueueVulkan::ReleaseResources(bool immediately) { const uint64 checkFrame = Engine::FrameCount - VULKAN_RESOURCE_DELETE_SAFE_FRAMES_COUNT; ScopeLock lock(_locker); for (int32 i = 0; i < _entries.Count(); i++) { Entry* e = &_entries.Get()[i]; - if (deleteImmediately || (checkFrame > e->FrameNumber && (e->CmdBuffer == nullptr || e->FenceCounter < e->CmdBuffer->GetFenceSignaledCounter()))) + if (immediately || (checkFrame > e->FrameNumber && (e->CmdBuffer == nullptr || e->FenceCounter < e->CmdBuffer->GetFenceSignaledCounter()))) { if (e->AllocationHandle == VK_NULL_HANDLE) { @@ -2077,7 +2067,6 @@ GPUConstantBuffer* GPUDeviceVulkan::CreateConstantBuffer(uint32 size, const Stri SemaphoreVulkan::SemaphoreVulkan(GPUDeviceVulkan* device) : _device(device) { - // Create semaphore VkSemaphoreCreateInfo info; RenderToolsVulkan::ZeroStruct(info, VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO); VALIDATE_VULKAN_RESULT(vkCreateSemaphore(device->Device, &info, nullptr, &_semaphoreHandle)); @@ -2090,21 +2079,6 @@ SemaphoreVulkan::~SemaphoreVulkan() _semaphoreHandle = VK_NULL_HANDLE; } -FenceVulkan::~FenceVulkan() -{ - ASSERT(_handle == VK_NULL_HANDLE); -} - -FenceVulkan::FenceVulkan(GPUDeviceVulkan* device, FenceManagerVulkan* owner, bool createSignaled) - : _signaled(createSignaled) - , _owner(owner) -{ - VkFenceCreateInfo info; - RenderToolsVulkan::ZeroStruct(info, VK_STRUCTURE_TYPE_FENCE_CREATE_INFO); - info.flags = createSignaled ? VK_FENCE_CREATE_SIGNALED_BIT : 0; - VALIDATE_VULKAN_RESULT(vkCreateFence(device->Device, &info, nullptr, &_handle)); -} - FenceManagerVulkan::~FenceManagerVulkan() { ASSERT(_usedFences.IsEmpty()); @@ -2113,68 +2087,63 @@ FenceManagerVulkan::~FenceManagerVulkan() void FenceManagerVulkan::Dispose() { ScopeLock lock(_device->_fenceLock); - ASSERT(_usedFences.IsEmpty()); for (FenceVulkan* fence : _freeFences) - { DestroyFence(fence); - } _freeFences.Clear(); } FenceVulkan* FenceManagerVulkan::AllocateFence(bool createSignaled) { ScopeLock lock(_device->_fenceLock); - FenceVulkan* fence; if (_freeFences.HasItems()) { fence = _freeFences.Last(); _freeFences.RemoveLast(); _usedFences.Add(fence); - if (createSignaled) - { - fence->_signaled = true; - } - - return fence; + fence->IsSignaled = true; + } + else + { + fence = New(); + fence->IsSignaled = createSignaled; + VkFenceCreateInfo info; + RenderToolsVulkan::ZeroStruct(info, VK_STRUCTURE_TYPE_FENCE_CREATE_INFO); + info.flags = createSignaled ? VK_FENCE_CREATE_SIGNALED_BIT : 0; + VALIDATE_VULKAN_RESULT(vkCreateFence(_device->Device, &info, nullptr, &fence->Handle)); + _usedFences.Add(fence); } - - fence = New(_device, this, createSignaled); - _usedFences.Add(fence); return fence; } -bool FenceManagerVulkan::WaitForFence(FenceVulkan* fence, uint64 timeInNanoseconds) +bool FenceManagerVulkan::WaitForFence(FenceVulkan* fence, uint64 timeInNanoseconds) const { ASSERT(_usedFences.Contains(fence)); - ASSERT(!fence->_signaled); - - const VkResult result = vkWaitForFences(_device->Device, 1, &fence->_handle, true, timeInNanoseconds); + ASSERT(!fence->IsSignaled); + const VkResult result = vkWaitForFences(_device->Device, 1, &fence->Handle, true, timeInNanoseconds); LOG_VULKAN_RESULT(result); if (result == VK_SUCCESS) { - fence->_signaled = true; + fence->IsSignaled = true; return false; } - return true; } -void FenceManagerVulkan::ResetFence(FenceVulkan* fence) +void FenceManagerVulkan::ResetFence(FenceVulkan* fence) const { - if (fence->_signaled) + if (fence->IsSignaled) { - VALIDATE_VULKAN_RESULT(vkResetFences(_device->Device, 1, &fence->_handle)); - fence->_signaled = false; + VALIDATE_VULKAN_RESULT(vkResetFences(_device->Device, 1, &fence->Handle)); + fence->IsSignaled = false; } } void FenceManagerVulkan::ReleaseFence(FenceVulkan*& fence) { ScopeLock lock(_device->_fenceLock); - ResetFence(fence); _usedFences.Remove(fence); _freeFences.Add(fence); @@ -2184,37 +2153,31 @@ void FenceManagerVulkan::ReleaseFence(FenceVulkan*& fence) void FenceManagerVulkan::WaitAndReleaseFence(FenceVulkan*& fence, uint64 timeInNanoseconds) { ScopeLock lock(_device->_fenceLock); - - if (!fence->IsSignaled()) - { + if (!fence->IsSignaled) WaitForFence(fence, timeInNanoseconds); - } - ResetFence(fence); _usedFences.Remove(fence); _freeFences.Add(fence); fence = nullptr; } -bool FenceManagerVulkan::CheckFenceState(FenceVulkan* fence) +bool FenceManagerVulkan::CheckFenceState(FenceVulkan* fence) const { ASSERT(_usedFences.Contains(fence)); - ASSERT(!fence->_signaled); - - const VkResult result = vkGetFenceStatus(_device->Device, fence->GetHandle()); + ASSERT(!fence->IsSignaled); + const VkResult result = vkGetFenceStatus(_device->Device, fence->Handle); if (result == VK_SUCCESS) { - fence->_signaled = true; + fence->IsSignaled = true; return true; } - return false; } -void FenceManagerVulkan::DestroyFence(FenceVulkan* fence) +void FenceManagerVulkan::DestroyFence(FenceVulkan* fence) const { - vkDestroyFence(_device->Device, fence->GetHandle(), nullptr); - fence->_handle = VK_NULL_HANDLE; + vkDestroyFence(_device->Device, fence->Handle, nullptr); + fence->Handle = VK_NULL_HANDLE; Delete(fence); } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h index 3daefa1f3..c3664b438 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h @@ -18,7 +18,6 @@ class GPUAdapterVulkan; class GPUSwapChainVulkan; class CmdBufferVulkan; class QueueVulkan; -class FenceVulkan; class GPUTextureVulkan; class GPUBufferVulkan; class GPUTimerQueryVulkan; @@ -31,12 +30,10 @@ class DescriptorPoolsManagerVulkan; class SemaphoreVulkan { private: - GPUDeviceVulkan* _device; VkSemaphore _semaphoreHandle; public: - /// /// Initializes a new instance of the class. /// @@ -58,59 +55,23 @@ public: } }; -class FenceVulkan +struct FenceVulkan { - friend FenceManagerVulkan; - -private: - VkFence _handle; - bool _signaled; - FenceManagerVulkan* _owner; - -public: - FenceVulkan(GPUDeviceVulkan* device, FenceManagerVulkan* owner, bool createSignaled); - ~FenceVulkan(); - -public: - inline VkFence GetHandle() const - { - return _handle; - } - - inline bool IsSignaled() const - { - return _signaled; - } - - FenceManagerVulkan* GetOwner() const - { - return _owner; - } + VkFence Handle; + bool IsSignaled; }; class FenceManagerVulkan { private: - - GPUDeviceVulkan* _device; + GPUDeviceVulkan* _device = nullptr; Array _freeFences; Array _usedFences; public: - - FenceManagerVulkan() - : _device(nullptr) - { - } - ~FenceManagerVulkan(); public: - - /// - /// Initializes the specified device. - /// - /// The graphics device. void Init(GPUDeviceVulkan* device) { _device = device; @@ -120,20 +81,15 @@ public: FenceVulkan* AllocateFence(bool createSignaled = false); - inline bool IsFenceSignaled(FenceVulkan* fence) + FORCE_INLINE bool IsFenceSignaled(FenceVulkan* fence) const { - if (fence->IsSignaled()) - { - return true; - } - - return CheckFenceState(fence); + return fence->IsSignaled || CheckFenceState(fence); } // Returns true if waiting timed out or failed, false otherwise. - bool WaitForFence(FenceVulkan* fence, uint64 timeInNanoseconds); + bool WaitForFence(FenceVulkan* fence, uint64 timeInNanoseconds) const; - void ResetFence(FenceVulkan* fence); + void ResetFence(FenceVulkan* fence) const; // Sets the fence handle to null void ReleaseFence(FenceVulkan*& fence); @@ -142,17 +98,15 @@ public: void WaitAndReleaseFence(FenceVulkan*& fence, uint64 timeInNanoseconds); private: - // Returns true if fence was signaled, otherwise false. - bool CheckFenceState(FenceVulkan* fence); + bool CheckFenceState(FenceVulkan* fence) const; - void DestroyFence(FenceVulkan* fence); + void DestroyFence(FenceVulkan* fence) const; }; class DeferredDeletionQueueVulkan { public: - enum Type { RenderPass, @@ -172,7 +126,6 @@ public: }; private: - struct Entry { uint64 FenceCounter; @@ -188,7 +141,6 @@ private: Array _entries; public: - /// /// Initializes a new instance of the class. /// @@ -201,7 +153,6 @@ public: ~DeferredDeletionQueueVulkan(); public: - template inline void EnqueueResource(Type type, T handle) { @@ -216,17 +167,15 @@ public: EnqueueGenericResource(type, (uint64)handle, allocation); } - void ReleaseResources(bool deleteImmediately = false); + void ReleaseResources(bool immediately = false); private: - void EnqueueGenericResource(Type type, uint64 handle, VmaAllocation allocation); }; class RenderTargetLayoutVulkan { public: - int32 RTsCount; MSAALevel MSAA; bool ReadDepth; @@ -238,7 +187,6 @@ public: uint32 Layers; public: - bool operator==(const RenderTargetLayoutVulkan& other) const { return Platform::MemoryCompare(this, &other, sizeof(RenderTargetLayoutVulkan)) == 0; @@ -250,7 +198,6 @@ uint32 GetHash(const RenderTargetLayoutVulkan& key); class FramebufferVulkan { public: - struct Key { RenderPassVulkan* RenderPass; @@ -258,7 +205,6 @@ public: VkImageView Attachments[GPU_MAX_RT_BINDED + 1]; public: - bool operator==(const Key& other) const { return Platform::MemoryCompare(this, &other, sizeof(Key)) == 0; @@ -266,23 +212,19 @@ public: }; private: - GPUDeviceVulkan* _device; VkFramebuffer _handle; public: - FramebufferVulkan(GPUDeviceVulkan* device, Key& key, VkExtent2D& extent, uint32 layers); ~FramebufferVulkan(); public: - VkImageView Attachments[GPU_MAX_RT_BINDED + 1]; VkExtent2D Extent; uint32 Layers; public: - inline VkFramebuffer GetHandle() { return _handle; @@ -296,21 +238,17 @@ uint32 GetHash(const FramebufferVulkan::Key& key); class RenderPassVulkan { private: - GPUDeviceVulkan* _device; VkRenderPass _handle; public: - RenderTargetLayoutVulkan Layout; public: - RenderPassVulkan(GPUDeviceVulkan* device, const RenderTargetLayoutVulkan& layout); ~RenderPassVulkan(); public: - inline VkRenderPass GetHandle() const { return _handle; @@ -320,7 +258,6 @@ public: class QueryPoolVulkan { protected: - struct Range { uint32 Start; @@ -338,12 +275,10 @@ protected: #endif public: - QueryPoolVulkan(GPUDeviceVulkan* device, int32 capacity, VkQueryType type); ~QueryPoolVulkan(); public: - inline VkQueryPool GetHandle() const { return _handle; @@ -357,7 +292,6 @@ public: class BufferedQueryPoolVulkan : public QueryPoolVulkan { private: - Array _queryOutput; Array _usedQueryBits; Array _startedQueryBits; @@ -367,7 +301,6 @@ private: int32 _lastBeginIndex; public: - BufferedQueryPoolVulkan(GPUDeviceVulkan* device, int32 capacity, VkQueryType type); bool AcquireQuery(uint32& resultIndex); void ReleaseQuery(uint32 queryIndex); @@ -382,7 +315,6 @@ public: class HelperResourcesVulkan { private: - GPUDeviceVulkan* _device; GPUTextureVulkan* _dummyTextures[6]; GPUBufferVulkan* _dummyBuffer; @@ -390,11 +322,9 @@ private: VkSampler _staticSamplers[GPU_STATIC_SAMPLERS_COUNT]; public: - HelperResourcesVulkan(GPUDeviceVulkan* device); public: - VkSampler* GetStaticSamplers(); GPUTextureVulkan* GetDummyTexture(SpirvShaderResourceType type); GPUBufferVulkan* GetDummyBuffer(); @@ -408,7 +338,6 @@ public: class StagingManagerVulkan { private: - struct PendingEntry { GPUBuffer* Buffer; @@ -435,7 +364,6 @@ private: #endif public: - StagingManagerVulkan(GPUDeviceVulkan* device); GPUBuffer* AcquireBuffer(uint32 size, GPUResourceUsage usage); void ReleaseBuffer(CmdBufferVulkan* cmdBuffer, GPUBuffer*& buffer); @@ -453,7 +381,6 @@ class GPUDeviceVulkan : public GPUDevice friend FenceManagerVulkan; private: - CriticalSection _fenceLock; mutable void* _nativePtr[2]; @@ -463,7 +390,6 @@ private: // TODO: use mutex to protect those collections BUT use 2 pools per cache: one lock-free with lookup only and second protected with mutex synced on frame end! public: - static GPUDevice* Create(); /// @@ -479,7 +405,6 @@ public: ~GPUDeviceVulkan(); public: - struct OptionalVulkanDeviceExtensions { uint32 HasKHRMaintenance1 : 1; @@ -497,7 +422,6 @@ public: static OptionalVulkanDeviceExtensions OptionalDeviceExtensions; public: - /// /// The Vulkan instance. /// @@ -514,7 +438,6 @@ public: static Array InstanceLayers; public: - /// /// The main Vulkan commands context. /// @@ -652,7 +575,6 @@ public: void OnImageViewDestroy(VkImageView imageView); public: - /// /// Setups the present queue to be ready for the given window surface. /// @@ -667,7 +589,7 @@ public: /// If set to true the optimal tiling should be used, otherwise use linear tiling. /// The output format. PixelFormat GetClosestSupportedPixelFormat(PixelFormat format, GPUTextureFlags flags, bool optimalTiling); - + /// /// Saves the pipeline cache. /// @@ -683,11 +605,9 @@ public: #endif private: - bool IsVkFormatSupported(VkFormat vkFormat, VkFormatFeatureFlags wantedFeatureFlags, bool optimalTiling) const; public: - // [GPUDevice] GPUContext* GetMainContext() override; GPUAdapter* GetAdapter() const override; @@ -713,7 +633,6 @@ template class GPUResourceVulkan : public GPUResourceBase { public: - /// /// Initializes a new instance of the class. /// @@ -731,7 +650,6 @@ public: class DescriptorOwnerResourceVulkan { public: - /// /// Finalizes an instance of the class. /// @@ -740,7 +658,6 @@ public: } public: - /// /// Gets the sampler descriptor. /// diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp index c5c1970e0..e2568ce95 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp @@ -73,7 +73,7 @@ ComputePipelineStateVulkan* GPUShaderProgramCSVulkan::GetOrCreateState() _pipelineState = New(_device, pipeline, layout); _pipelineState->DescriptorInfo = &DescriptorInfo; _pipelineState->DescriptorSetsLayout = &layout->GetDescriptorSetLayout(); - _pipelineState->DescriptorSetHandles.AddZeroed(_pipelineState->DescriptorSetsLayout->GetHandles().Count()); + _pipelineState->DescriptorSetHandles.AddZeroed(_pipelineState->DescriptorSetsLayout->Handles.Count()); uint32 dynamicOffsetsCount = 0; if (DescriptorInfo.DescriptorTypesCount != 0) { @@ -136,9 +136,7 @@ PipelineLayoutVulkan* GPUPipelineStateVulkan::GetLayout() #define INIT_SHADER_STAGE(set, bit) \ if (DescriptorInfoPerStage[DescriptorSet::set]) \ - { \ - descriptorSetLayoutInfo.AddBindingsForStage(bit, DescriptorSet::set, DescriptorInfoPerStage[DescriptorSet::set]); \ - } + descriptorSetLayoutInfo.AddBindingsForStage(bit, DescriptorSet::set, DescriptorInfoPerStage[DescriptorSet::set]) INIT_SHADER_STAGE(Vertex, VK_SHADER_STAGE_VERTEX_BIT); INIT_SHADER_STAGE(Hull, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT); INIT_SHADER_STAGE(Domain, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT); @@ -149,7 +147,7 @@ PipelineLayoutVulkan* GPUPipelineStateVulkan::GetLayout() _layout = _device->GetOrCreateLayout(descriptorSetLayoutInfo); ASSERT(_layout); DescriptorSetsLayout = &_layout->GetDescriptorSetLayout(); - DescriptorSetHandles.AddZeroed(DescriptorSetsLayout->GetHandles().Count()); + DescriptorSetHandles.AddZeroed(DescriptorSetsLayout->Handles.Count()); return _layout; } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h index 0557d3d1c..392375224 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h @@ -14,28 +14,15 @@ class PipelineLayoutVulkan; class ComputePipelineStateVulkan { private: - GPUDeviceVulkan* _device; VkPipeline _handle; PipelineLayoutVulkan* _layout; public: - - /// - /// Initializes a new instance of the class. - /// - /// The graphics device. - /// The pipeline object. - /// The pipeline layout. ComputePipelineStateVulkan(GPUDeviceVulkan* device, VkPipeline pipeline, PipelineLayoutVulkan* layout); - - /// - /// Finalizes an instance of the class. - /// ~ComputePipelineStateVulkan(); public: - /// /// The cached shader descriptor infos for compute shader. /// @@ -71,7 +58,6 @@ public: Array DynamicOffsets; public: - void Bind(CmdBufferVulkan* cmdBuffer) { vkCmdBindDescriptorSets( @@ -86,7 +72,6 @@ public: } public: - VkPipeline GetHandle() const { return _handle; @@ -104,7 +89,6 @@ public: class GPUPipelineStateVulkan : public GPUResourceVulkan { private: - Dictionary _pipelines; VkGraphicsPipelineCreateInfo _desc; VkPipelineShaderStageCreateInfo _shaderStages[ShaderStage_Count - 1]; @@ -121,7 +105,6 @@ private: PipelineLayoutVulkan* _layout; public: - /// /// Initializes a new instance of the class. /// @@ -129,7 +112,6 @@ public: GPUPipelineStateVulkan(GPUDeviceVulkan* device); public: - /// /// The bitmask of stages that exist in this pipeline. /// @@ -187,7 +169,6 @@ public: Array DynamicOffsets; public: - void Bind(CmdBufferVulkan* cmdBuffer) { vkCmdBindDescriptorSets( @@ -215,13 +196,11 @@ public: VkPipeline GetState(RenderPassVulkan* renderPass); public: - // [GPUPipelineState] bool IsValid() const final override; bool Init(const Description& desc) final override; protected: - // [GPUResourceVulkan] void OnReleaseGPU() override; }; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSamplerVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUSamplerVulkan.h index 89375d481..b30a54126 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSamplerVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSamplerVulkan.h @@ -13,7 +13,6 @@ class GPUSamplerVulkan : public GPUResourceVulkan { public: - GPUSamplerVulkan(GPUDeviceVulkan* device) : GPUResourceVulkan(device, StringView::Empty) { @@ -22,7 +21,6 @@ public: VkSampler Sampler = VK_NULL_HANDLE; protected: - // [GPUSamplerVulkan] bool OnInit() override; void OnReleaseGPU() override; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUShaderProgramVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUShaderProgramVulkan.h index faee4a64f..c630328e7 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUShaderProgramVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUShaderProgramVulkan.h @@ -18,11 +18,9 @@ template class GPUShaderProgramVulkan : public BaseType { protected: - GPUDeviceVulkan* _device; public: - /// /// Initializes a new instance of the class. /// @@ -50,7 +48,6 @@ public: } public: - /// /// The Vulkan shader module. /// @@ -62,7 +59,6 @@ public: SpirvShaderDescriptorInfo DescriptorInfo; public: - // [BaseType] uint32 GetBufferSize() const override { @@ -81,7 +77,6 @@ public: class GPUShaderProgramVSVulkan : public GPUShaderProgramVulkan { public: - /// /// Initializes a new instance of the class. /// @@ -95,13 +90,11 @@ public: } public: - VkPipelineVertexInputStateCreateInfo VertexInputState; VkVertexInputBindingDescription VertexBindingDescriptions[VERTEX_SHADER_MAX_INPUT_ELEMENTS]; VkVertexInputAttributeDescription VertexAttributeDescriptions[VERTEX_SHADER_MAX_INPUT_ELEMENTS]; public: - // [GPUShaderProgramVulkan] void* GetInputLayout() const override { @@ -120,7 +113,6 @@ public: class GPUShaderProgramHSVulkan : public GPUShaderProgramVulkan { public: - /// /// Initializes a new instance of the class. /// @@ -142,7 +134,6 @@ public: class GPUShaderProgramDSVulkan : public GPUShaderProgramVulkan { public: - /// /// Initializes a new instance of the class. /// @@ -162,7 +153,6 @@ public: class GPUShaderProgramGSVulkan : public GPUShaderProgramVulkan { public: - /// /// Initializes a new instance of the class. /// @@ -182,7 +172,6 @@ public: class GPUShaderProgramPSVulkan : public GPUShaderProgramVulkan { public: - /// /// Initializes a new instance of the class. /// @@ -202,11 +191,9 @@ public: class GPUShaderProgramCSVulkan : public GPUShaderProgramVulkan { private: - ComputePipelineStateVulkan* _pipelineState; public: - /// /// Initializes a new instance of the class. /// @@ -226,7 +213,6 @@ public: ~GPUShaderProgramCSVulkan(); public: - /// /// Gets the state of the pipeline for the compute shader execution or creates a new one if missing. /// diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.h index ebb5da204..4ff467984 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.h @@ -17,7 +17,6 @@ class UniformBufferUploaderVulkan : public GPUResourceVulkan, public ResourceOwnerVulkan { public: - struct Allocation { /// @@ -42,7 +41,6 @@ public: }; private: - VkBuffer _buffer; VmaAllocation _allocation; uint64 _size; @@ -53,7 +51,6 @@ private: uint64 _fenceCounter; public: - /// /// Initializes a new instance of the class. /// @@ -61,11 +58,9 @@ public: UniformBufferUploaderVulkan(GPUDeviceVulkan* device); public: - Allocation Allocate(uint64 size, uint32 alignment, GPUContextVulkan* context); public: - // [GPUResourceVulkan] GPUResourceType GetResourceType() const final override { @@ -79,7 +74,6 @@ public: } protected: - // [GPUResourceVulkan] void OnReleaseGPU() override; }; @@ -90,7 +84,6 @@ protected: class GPUConstantBufferVulkan : public GPUResourceVulkan, public DescriptorOwnerResourceVulkan { public: - /// /// Initializes a new instance of the class. /// @@ -103,14 +96,12 @@ public: } public: - /// /// The last uploaded data inside the shared uniforms uploading ring buffer. /// UniformBufferUploaderVulkan::Allocation Allocation; public: - // [DescriptorOwnerResourceVulkan] void DescriptorAsDynamicUniformBuffer(GPUContextVulkan* context, VkBuffer& buffer, VkDeviceSize& offset, VkDeviceSize& range, uint32& dynamicOffset) override { @@ -127,7 +118,6 @@ public: class GPUShaderVulkan : public GPUResourceVulkan { public: - /// /// Initializes a new instance of the class. /// @@ -139,7 +129,6 @@ public: } protected: - // [GPUShader] GPUShaderProgram* CreateGPUShaderProgram(ShaderStage type, const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize, MemoryReadStream& stream) override; }; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h index 0dd9528e1..2e20326bb 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h @@ -14,7 +14,6 @@ class BackBufferVulkan : public ResourceOwnerVulkan { public: - /// /// The device. /// @@ -36,12 +35,10 @@ public: GPUTextureViewVulkan Handle; public: - void Setup(GPUSwapChainVulkan* window, VkImage backbuffer, PixelFormat format, VkExtent3D extent); void Release(); public: - // [ResourceOwnerVulkan] GPUResource* AsGPUResource() const override { @@ -58,7 +55,6 @@ class GPUSwapChainVulkan : public GPUResourceVulkan, public Resour friend GPUDeviceVulkan; private: - VkSurfaceKHR _surface; VkSwapchainKHR _swapChain; int32 _currentImageIndex; @@ -68,11 +64,9 @@ private: SemaphoreVulkan* _acquiredSemaphore; public: - GPUSwapChainVulkan(GPUDeviceVulkan* device, Window* window); public: - /// /// Gets the Vulkan surface. /// @@ -92,7 +86,6 @@ public: } public: - enum class Status { Ok = 0, @@ -108,12 +101,10 @@ public: int32 AcquireNextImage(SemaphoreVulkan** outSemaphore); private: - void ReleaseBackBuffer(); bool CreateSwapChain(int32 width, int32 height); public: - // [GPUSwapChain] bool IsFullscreen() override; void SetFullscreen(bool isFullscreen) override; @@ -129,7 +120,6 @@ public: } protected: - // [GPUResourceVulkan] void OnReleaseGPU() override; }; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h index ec64eaac4..6180e08f0 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h @@ -15,7 +15,6 @@ class GPUTextureViewVulkan : public GPUTextureView, public DescriptorOwnerResourceVulkan { public: - GPUTextureViewVulkan() { } @@ -44,7 +43,6 @@ public: #endif public: - GPUDeviceVulkan* Device = nullptr; ResourceOwnerVulkan* Owner = nullptr; VkImage Image = VK_NULL_HANDLE; @@ -58,7 +56,6 @@ public: VkImageLayout LayoutSRV; public: - void Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* owner, VkImage image, int32 totalMipLevels, PixelFormat format, MSAALevel msaa, VkExtent3D extent, VkImageViewType viewType, int32 mipLevels = 1, int32 firstMipIndex = 0, int32 arraySize = 1, int32 firstArraySlice = 0, bool readOnlyDepth = false); VkImageView GetFramebufferView(); @@ -66,7 +63,6 @@ public: void Release(); public: - // [GPUResourceView] void* GetNativePtr() const override { @@ -84,7 +80,6 @@ public: class GPUTextureVulkan : public GPUResourceVulkan, public ResourceOwnerVulkan, public DescriptorOwnerResourceVulkan { private: - VkImage _image = VK_NULL_HANDLE; VmaAllocation _allocation = VK_NULL_HANDLE; GPUTextureViewVulkan _handleArray; @@ -95,7 +90,6 @@ private: Array> _handlesPerMip; // [slice][mip] public: - /// /// Initializes a new instance of the class. /// @@ -107,7 +101,6 @@ public: } public: - /// /// Gets the Vulkan image handle. /// @@ -127,11 +120,9 @@ public: VkImageAspectFlags DefaultAspectMask; private: - void initHandles(); public: - // [GPUTexture] GPUTextureView* View(int32 arrayOrDepthIndex) const override { @@ -178,7 +169,6 @@ public: void DescriptorAsStorageImage(GPUContextVulkan* context, VkImageView& imageView, VkImageLayout& layout) override; protected: - // [GPUTexture] bool OnInit() override; void OnResidentMipsChanged() override; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTimerQueryVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUTimerQueryVulkan.h index ddf461b13..da9b45ceb 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUTimerQueryVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTimerQueryVulkan.h @@ -13,7 +13,6 @@ class GPUTimerQueryVulkan : public GPUResourceVulkan { private: - struct Query { BufferedQueryPoolVulkan* Pool; @@ -35,7 +34,6 @@ private: Array> _queries; public: - /// /// Initializes a new instance of the class. /// @@ -43,7 +41,6 @@ public: GPUTimerQueryVulkan(GPUDeviceVulkan* device); public: - /// /// Interrupts an in-progress query, allowing the command buffer to submitted. Interrupted queries must be resumed using Resume(). /// @@ -57,14 +54,12 @@ public: void Resume(CmdBufferVulkan* cmdBuffer); private: - bool GetResult(Query& query); void WriteTimestamp(CmdBufferVulkan* cmdBuffer, Query& query, VkPipelineStageFlagBits stage) const; bool TryGetResult(); bool UseQueries(); public: - // [GPUTimerQuery] void Begin() override; void End() override; @@ -72,7 +67,6 @@ public: float GetResult() override; protected: - // [GPUResourceVulkan] void OnReleaseGPU() override; }; diff --git a/Source/Engine/GraphicsDevice/Vulkan/QueueVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/QueueVulkan.cpp index e77ad9d46..ce36a901a 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/QueueVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/QueueVulkan.cpp @@ -14,17 +14,16 @@ QueueVulkan::QueueVulkan(GPUDeviceVulkan* device, uint32 familyIndex) , _device(device) , _lastSubmittedCmdBuffer(nullptr) , _lastSubmittedCmdBufferFenceCounter(0) - , _submitCounter(0) { vkGetDeviceQueue(device->Device, familyIndex, 0, &_queue); } -void QueueVulkan::Submit(CmdBufferVulkan* cmdBuffer, uint32 numSignalSemaphores, VkSemaphore* signalSemaphores) +void QueueVulkan::Submit(CmdBufferVulkan* cmdBuffer, uint32 signalSemaphoresCount, const VkSemaphore* signalSemaphores) { ASSERT(cmdBuffer->HasEnded()); auto fence = cmdBuffer->GetFence(); - ASSERT(!fence->IsSignaled()); + ASSERT(!fence->IsSignaled); const VkCommandBuffer cmdBuffers[] = { cmdBuffer->GetHandle() }; @@ -32,7 +31,7 @@ void QueueVulkan::Submit(CmdBufferVulkan* cmdBuffer, uint32 numSignalSemaphores, RenderToolsVulkan::ZeroStruct(submitInfo, VK_STRUCTURE_TYPE_SUBMIT_INFO); submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = cmdBuffers; - submitInfo.signalSemaphoreCount = numSignalSemaphores; + submitInfo.signalSemaphoreCount = signalSemaphoresCount; submitInfo.pSignalSemaphores = signalSemaphores; Array> waitSemaphores; @@ -48,7 +47,7 @@ void QueueVulkan::Submit(CmdBufferVulkan* cmdBuffer, uint32 numSignalSemaphores, submitInfo.pWaitDstStageMask = cmdBuffer->_waitFlags.Get(); } - VALIDATE_VULKAN_RESULT(vkQueueSubmit(_queue, 1, &submitInfo, fence->GetHandle())); + VALIDATE_VULKAN_RESULT(vkQueueSubmit(_queue, 1, &submitInfo, fence->Handle)); // Mark semaphores as submitted cmdBuffer->_state = CmdBufferVulkan::State::Submitted; @@ -78,21 +77,16 @@ void QueueVulkan::Submit(CmdBufferVulkan* cmdBuffer, uint32 numSignalSemaphores, void QueueVulkan::GetLastSubmittedInfo(CmdBufferVulkan*& cmdBuffer, uint64& fenceCounter) const { _locker.Lock(); - cmdBuffer = _lastSubmittedCmdBuffer; fenceCounter = _lastSubmittedCmdBufferFenceCounter; - _locker.Unlock(); } void QueueVulkan::UpdateLastSubmittedCommandBuffer(CmdBufferVulkan* cmdBuffer) { _locker.Lock(); - _lastSubmittedCmdBuffer = cmdBuffer; _lastSubmittedCmdBufferFenceCounter = cmdBuffer->GetFenceSignaledCounter(); - _submitCounter++; - _locker.Unlock(); } diff --git a/Source/Engine/GraphicsDevice/Vulkan/QueueVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/QueueVulkan.h index 37d9e6a68..81fbefd9e 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/QueueVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/QueueVulkan.h @@ -17,7 +17,6 @@ class CmdBufferVulkan; class QueueVulkan { private: - VkQueue _queue; uint32 _familyIndex; uint32 _queueIndex; @@ -25,10 +24,8 @@ private: CriticalSection _locker; CmdBufferVulkan* _lastSubmittedCmdBuffer; uint64 _lastSubmittedCmdBufferFenceCounter; - uint64 _submitCounter; public: - QueueVulkan(GPUDeviceVulkan* device, uint32 familyIndex); inline uint32 GetFamilyIndex() const @@ -36,7 +33,7 @@ public: return _familyIndex; } - void Submit(CmdBufferVulkan* cmdBuffer, uint32 numSignalSemaphores = 0, VkSemaphore* signalSemaphores = nullptr); + void Submit(CmdBufferVulkan* cmdBuffer, uint32 signalSemaphoresCount = 0, const VkSemaphore* signalSemaphores = nullptr); inline void Submit(CmdBufferVulkan* cmdBuffer, VkSemaphore signalSemaphore) { @@ -50,13 +47,7 @@ public: void GetLastSubmittedInfo(CmdBufferVulkan*& cmdBuffer, uint64& fenceCounter) const; - inline uint64 GetSubmitCount() const - { - return _submitCounter; - } - private: - void UpdateLastSubmittedCommandBuffer(CmdBufferVulkan* cmdBuffer); }; diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp index f9c9237f6..be801869b 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp @@ -6,6 +6,8 @@ #include "Engine/Core/Types/StringBuilder.h" #include "Engine/Core/Log.h" +// @formatter:off + VkFormat RenderToolsVulkan::PixelFormatToVkFormat[108] = { VK_FORMAT_UNDEFINED, @@ -163,6 +165,8 @@ VkCompareOp RenderToolsVulkan::ComparisonFuncToVkCompareOp[9] = VK_COMPARE_OP_ALWAYS, // Always }; +// @formatter:on + #define VKERR(x) case x: sb.Append(TEXT(#x)); break #if GPU_ENABLE_RESOURCE_NAMING diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h index 835ae883c..2f7eb88ee 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h @@ -34,14 +34,12 @@ class RenderToolsVulkan { private: - static VkFormat PixelFormatToVkFormat[static_cast(PixelFormat::MAX)]; static VkBlendFactor BlendToVkBlendFactor[static_cast(BlendingMode::Blend::MAX)]; static VkBlendOp OperationToVkBlendOp[static_cast(BlendingMode::Operation::MAX)]; static VkCompareOp ComparisonFuncToVkCompareOp[static_cast(ComparisonFunc::MAX)]; public: - #if GPU_ENABLE_RESOURCE_NAMING static void SetObjectName(VkDevice device, uint64 objectHandle, VkObjectType objectType, const String& name); static void SetObjectName(VkDevice device, uint64 objectHandle, VkObjectType objectType, const char* name); @@ -80,13 +78,13 @@ public: stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; #if VK_KHR_maintenance2 - case VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT: - case VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT: - stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - break; + case VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT: + case VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT: + stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + break; #endif default: - CRASH; + CRASH; break; } return stageFlags; @@ -130,17 +128,17 @@ public: stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; #if VK_KHR_maintenance2 - case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR: - accessFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - break; + case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR: + accessFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + break; #endif case VK_IMAGE_LAYOUT_GENERAL: accessFlags = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; default: - CRASH; + CRASH; break; } return stageFlags; @@ -213,7 +211,7 @@ public: result = VK_SAMPLER_MIPMAP_MODE_LINEAR; break; default: - CRASH; + CRASH; break; } return result; @@ -237,7 +235,7 @@ public: result = VK_FILTER_LINEAR; break; default: - CRASH; + CRASH; break; } return result; @@ -261,7 +259,7 @@ public: result = VK_FILTER_LINEAR; break; default: - CRASH; + CRASH; break; } return result; @@ -285,7 +283,7 @@ public: result = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; break; default: - CRASH; + CRASH; break; } return result; @@ -303,7 +301,7 @@ public: result = VK_COMPARE_OP_NEVER; break; default: - CRASH; + CRASH; break; } return result; diff --git a/Source/Engine/GraphicsDevice/Vulkan/ResourceOwnerVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/ResourceOwnerVulkan.h index 3fc0f2c9c..9047a5ada 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/ResourceOwnerVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/ResourceOwnerVulkan.h @@ -30,13 +30,11 @@ class ResourceOwnerVulkan friend GPUContextVulkan; public: - virtual ~ResourceOwnerVulkan() { } public: - /// /// The resource state tracking helper. Used for resource barriers. /// @@ -48,7 +46,6 @@ public: int32 ArraySlices; public: - /// /// Gets resource owner object as a GPUResource type or returns null if cannot perform cast. /// @@ -56,7 +53,6 @@ public: virtual GPUResource* AsGPUResource() const = 0; protected: - void initResource(VkImageLayout initialState, int32 mipLevels = 1, int32 arraySize = 1, bool usePerSubresourceTracking = false) { State.Initialize(mipLevels * arraySize, initialState, usePerSubresourceTracking); diff --git a/Source/Engine/GraphicsDevice/Vulkan/VulkanPlatformBase.h b/Source/Engine/GraphicsDevice/Vulkan/VulkanPlatformBase.h index 5519b7a2f..abdcf29ec 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/VulkanPlatformBase.h +++ b/Source/Engine/GraphicsDevice/Vulkan/VulkanPlatformBase.h @@ -23,7 +23,6 @@ enum class VulkanValidationLevel class VulkanPlatformBase { public: - static void GetInstanceExtensions(Array& extensions, Array& layers) { } From 5bad7c79c444fb99c01a11654e9ea912151bff0d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 29 Jan 2024 17:53:42 +0100 Subject: [PATCH 065/198] Add removing dotnet libs for iterative rebuilds when AOT cache gets cleared --- Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp index 5d566efb5..8921fd78e 100644 --- a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp +++ b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp @@ -35,7 +35,8 @@ void PrecompileAssembliesStep::OnBuildStarted(CookingData& data) if (cachedData != aotModeCacheValue) { LOG(Info, "AOT cache invalidation"); - FileSystem::DeleteDirectory(data.ManagedCodeOutputPath); + FileSystem::DeleteDirectory(data.ManagedCodeOutputPath); // Remove AOT cache + FileSystem::DeleteDirectory(data.DataOutputPath / TEXT("Dotnet")); // Remove deployed Dotnet libs (be sure to remove any leftovers from previous build) } } if (!FileSystem::DirectoryExists(data.ManagedCodeOutputPath)) From 3dc34e2d9c6d585bd20a4a7a0a822cdcad482d63 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Mon, 29 Jan 2024 22:51:02 +0100 Subject: [PATCH 066/198] add more units and categories --- Source/Editor/Utilities/ShuntingYardParser.cs | 46 ++++++++++++++++++- Source/Editor/Utilities/Utils.cs | 21 ++++++--- Source/Engine/Utilities/Utils.cs | 9 +++- 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index f8fa05e5b..0034896e1 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -123,7 +123,34 @@ namespace FlaxEditor.Utilities ["-infinity"] = -double.MaxValue, ["m"] = Units.Meters2Units, ["cm"] = Units.Meters2Units / 100, - ["km"] = Units.Meters2Units * 1000 + ["km"] = Units.Meters2Units * 1000, + ["s"] = 1, + ["ms"] = 0.001, + ["min"] = 60, + ["h"] = 3600, + ["cm²"] = (Units.Meters2Units / 100) * (Units.Meters2Units / 100), + ["cm³"] = (Units.Meters2Units / 100) * (Units.Meters2Units / 100) * (Units.Meters2Units / 100), + ["dm²"] = (Units.Meters2Units / 10) * (Units.Meters2Units / 10), + ["dm³"] = (Units.Meters2Units / 10) * (Units.Meters2Units / 10) * (Units.Meters2Units / 10), + ["l"] = (Units.Meters2Units / 10) * (Units.Meters2Units / 10) * (Units.Meters2Units / 10), + ["m²"] = Units.Meters2Units * Units.Meters2Units, + ["m³"] = Units.Meters2Units * Units.Meters2Units * Units.Meters2Units, + ["kg"] = 1, + ["g"] = 0.001, + ["N"] = Units.Meters2Units + }; + + /// + /// List known units which cannot be handled as a variable easily because they contain operator + /// symbols (mostly a forward slash). The value is the factor to calculate game units. + /// + private static readonly IDictionary UnitSymbols = new Dictionary + { + ["cm/s"] = Units.Meters2Units / 100, + ["cm/s²"] = Units.Meters2Units / 100, + ["m/s"] = Units.Meters2Units, + ["m/s²"] = Units.Meters2Units, + ["km/h"] = 1/3.6 * Units.Meters2Units }; /// @@ -174,6 +201,23 @@ namespace FlaxEditor.Utilities { // Prepare text text = text.Replace(',', '.').Replace("°", ""); + foreach (var kv in UnitSymbols) + { + int idx; + do + { + idx = text.IndexOf(kv.Key, StringComparison.InvariantCulture); + if (idx > 0) + { + if (DetermineType(text[idx - 1]) != TokenType.Number) + throw new ParsingException($"unit found without a number: {kv.Key} at {idx} in {text}"); + if (Mathf.Abs(kv.Value - 1) < Mathf.Epsilon) + text = text.Remove(idx, kv.Key.Length); + else + text = text.Replace(kv.Key, "*" + kv.Value); + } + } while (idx > 0); + } // Necessary to correctly parse negative numbers var previous = TokenType.WhiteSpace; diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index ac5a3c3c6..be5399111 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1179,21 +1179,30 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(float value, FlaxEngine.Utils.ValueCategory category) { + const string format = "g7"; if (!Units.UseUnitsFormatting) return FormatFloat(value); switch (category) { case FlaxEngine.Utils.ValueCategory.Distance: if (!Units.AutomaticUnitsFormatting) - return (value / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "m"; + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; var absValue = Mathf.Abs(value); - // in case a unit != cm this would be (value / Maters2Units * 100) + // in case a unit != cm this would be (value / Meters2Units * 100) if (absValue < Units.Meters2Units) - return value.ToString("g7", CultureInfo.InvariantCulture) + "cm"; + return value.ToString(format, CultureInfo.InvariantCulture) + "cm"; if (absValue < Units.Meters2Units * 1000) - return (value / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "m"; - return (value / 1000 / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "km"; - case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString("g7", CultureInfo.InvariantCulture) + "°"; + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; + return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "km"; + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; + case FlaxEngine.Utils.ValueCategory.Time: return value.ToString(format, CultureInfo.InvariantCulture) + "s"; + // some fonts have a symbol for that: "\u33A7" + case FlaxEngine.Utils.ValueCategory.Speed: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m/s"; + case FlaxEngine.Utils.ValueCategory.Acceleration: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m/s²"; + case FlaxEngine.Utils.ValueCategory.Area: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m²"; + case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m³"; + case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + "kg"; + case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "N"; case FlaxEngine.Utils.ValueCategory.None: default: return FormatFloat(value); diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs index 195bacf96..9ae42fa43 100644 --- a/Source/Engine/Utilities/Utils.cs +++ b/Source/Engine/Utilities/Utils.cs @@ -1046,7 +1046,14 @@ namespace FlaxEngine { None, Distance, - Angle + Area, + Volume, + Mass, + Angle, + Speed, + Acceleration, + Time, + Force } } } From 9e5f866b75011dd0d0b49ce069a598c69c990a67 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Tue, 30 Jan 2024 07:44:50 +0100 Subject: [PATCH 067/198] fix Newton parsing --- Source/Editor/Utilities/ShuntingYardParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index 0034896e1..ca3780e8f 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -137,7 +137,7 @@ namespace FlaxEditor.Utilities ["m³"] = Units.Meters2Units * Units.Meters2Units * Units.Meters2Units, ["kg"] = 1, ["g"] = 0.001, - ["N"] = Units.Meters2Units + ["n"] = Units.Meters2Units }; /// @@ -387,7 +387,7 @@ namespace FlaxEditor.Utilities } else { - throw new ParsingException("unknown variable"); + throw new ParsingException($"unknown variable : {token.Value}"); } } else From e7b0375a0e062c43a30b626273b7f81989e4f139 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Tue, 30 Jan 2024 07:45:19 +0100 Subject: [PATCH 068/198] code cleanup --- Source/Editor/Utilities/Utils.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index be5399111..531f2b50b 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1179,7 +1179,7 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(float value, FlaxEngine.Utils.ValueCategory category) { - const string format = "g7"; + const string format = "g7"; if (!Units.UseUnitsFormatting) return FormatFloat(value); switch (category) @@ -1217,21 +1217,22 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(double value, FlaxEngine.Utils.ValueCategory category) { + const string format = "g15"; if (!Units.UseUnitsFormatting) return FormatFloat(value); switch (category) { case FlaxEngine.Utils.ValueCategory.Distance: if (!Units.AutomaticUnitsFormatting) - return (value / Units.Meters2Units).ToString("g15", CultureInfo.InvariantCulture) + "m"; + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; var absValue = Mathf.Abs(value); // in case a unit != cm this would be (value / Maters2Units * 100) if (absValue < Units.Meters2Units) - return value.ToString("g15", CultureInfo.InvariantCulture) + "cm"; + return value.ToString(format, CultureInfo.InvariantCulture) + "cm"; if (absValue < Units.Meters2Units * 1000) - return (value / Units.Meters2Units).ToString("g15", CultureInfo.InvariantCulture) + "m"; - return (value / 1000 / Units.Meters2Units).ToString("g15", CultureInfo.InvariantCulture) + "km"; - case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString("g15", CultureInfo.InvariantCulture) + "°"; + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; + return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "km"; + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; case FlaxEngine.Utils.ValueCategory.None: default: return FormatFloat(value); From 49beb2c7bab76d75a993249182d9380d257c35ed Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Tue, 30 Jan 2024 08:52:27 +0100 Subject: [PATCH 069/198] add toggle formatting --- .../CustomEditors/Editors/DoubleEditor.cs | 14 +++++++++++ .../CustomEditors/Editors/FloatEditor.cs | 15 ++++++++++++ .../CustomEditors/Editors/Vector3Editor.cs | 24 +++++++++++++++++++ Source/Editor/GUI/Input/FloatValueBox.cs | 10 +++++--- 4 files changed, 60 insertions(+), 3 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs index a428fd448..97c0d4782 100644 --- a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs @@ -40,6 +40,13 @@ namespace FlaxEditor.CustomEditors.Editors doubleValue.ValueBox.ValueChanged += OnValueChanged; doubleValue.ValueBox.SlidingEnd += ClearToken; _element = doubleValue; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); + mb.AutoCheck = true; + mb.Checked = doubleValue.ValueBox.Category != Utils.ValueCategory.None; + }; return; } } @@ -50,6 +57,13 @@ namespace FlaxEditor.CustomEditors.Editors doubleValue.SetCategory(valueCategory); doubleValue.ValueBox.ValueChanged += OnValueChanged; doubleValue.ValueBox.SlidingEnd += ClearToken; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); + mb.AutoCheck = true; + mb.Checked = doubleValue.ValueBox.Category != Utils.ValueCategory.None; + }; _element = doubleValue; } } diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index c739269ab..88777474b 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; +using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using Utils = FlaxEngine.Utils; @@ -56,6 +57,13 @@ namespace FlaxEditor.CustomEditors.Editors floatValue.ValueBox.ValueChanged += OnValueChanged; floatValue.ValueBox.SlidingEnd += ClearToken; _element = floatValue; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); + mb.AutoCheck = true; + mb.Checked = floatValue.ValueBox.Category != Utils.ValueCategory.None; + }; return; } } @@ -67,6 +75,13 @@ namespace FlaxEditor.CustomEditors.Editors floatValue.ValueBox.ValueChanged += OnValueChanged; floatValue.ValueBox.SlidingEnd += ClearToken; _element = floatValue; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); + mb.AutoCheck = true; + mb.Checked = floatValue.ValueBox.Category != Utils.ValueCategory.None; + }; } } diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index fe3ff2b74..c01a841c2 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -97,6 +97,18 @@ namespace FlaxEditor.CustomEditors.Editors ZElement.SetCategory(category); ZElement.ValueBox.ValueChanged += OnZValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => + { + XElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + YElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + ZElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + }); + mb.AutoCheck = true; + mb.Checked = XElement.ValueBox.Category != Utils.ValueCategory.None; + }; } private void OnXValueChanged() @@ -283,6 +295,18 @@ namespace FlaxEditor.CustomEditors.Editors ZElement.SetCategory(category); ZElement.ValueBox.ValueChanged += OnValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => + { + XElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + YElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + ZElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + }); + mb.AutoCheck = true; + mb.Checked = XElement.ValueBox.Category != Utils.ValueCategory.None; + }; } private void OnValueChanged() diff --git a/Source/Editor/GUI/Input/FloatValueBox.cs b/Source/Editor/GUI/Input/FloatValueBox.cs index 2fccba8e8..5495fe534 100644 --- a/Source/Editor/GUI/Input/FloatValueBox.cs +++ b/Source/Editor/GUI/Input/FloatValueBox.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; -using System.Globalization; using FlaxEditor.Utilities; using FlaxEngine; using Utils = FlaxEngine.Utils; @@ -138,10 +137,15 @@ namespace FlaxEditor.GUI.Input Value = Value; } + private Utils.ValueCategory _category = Utils.ValueCategory.None; + /// - /// Get or set the category of the value. This can either be none for just a number, a distance or an angle. + /// Get or set the category of the value. This can be none for just a number or a more specific one like a distance. /// - public Utils.ValueCategory Category = Utils.ValueCategory.None; + public Utils.ValueCategory Category { + get => _category; + set { _category = value; UpdateText(); } + } /// protected sealed override void UpdateText() From 2a0320677849698e388dcf95cb2e6e769f2634a5 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Tue, 30 Jan 2024 09:24:44 +0100 Subject: [PATCH 070/198] add formatting preference to editor options --- Source/Editor/Options/InterfaceOptions.cs | 37 +++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 95a273f19..482946e9a 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using FlaxEditor.GUI.Docking; +using FlaxEditor.Utilities; using FlaxEngine; namespace FlaxEditor.Options @@ -90,6 +91,25 @@ namespace FlaxEditor.Options PlayScenes, } + /// + /// Options for formatting numerical values + /// + public enum ValueFormattingType + { + /// + /// No formatting + /// + None, + /// + /// Format using the base SI unit + /// + BaseUnit, + /// + /// Format using a unit that matches the value best + /// + AutoUnit + } + /// /// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required. /// @@ -148,6 +168,23 @@ namespace FlaxEditor.Options [EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")] public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal; + private ValueFormattingType _valueFormatting = ValueFormattingType.None; + + /// + /// Gets or sets the formatting option for numeric values in the editor. + /// + [DefaultValue(ValueFormattingType.None)] + [EditorDisplay("Interface"), EditorOrder(300), Tooltip("Formatting of numeric values.")] + public ValueFormattingType ValueFormating { + get => _valueFormatting; + set + { + _valueFormatting = value; + Units.UseUnitsFormatting = _valueFormatting != ValueFormattingType.None; + Units.AutomaticUnitsFormatting = _valueFormatting == ValueFormattingType.AutoUnit; + } + } + /// /// Gets or sets the timestamps prefix mode for output log messages. /// From 1f8314b66302f2a5242ce818e1ec96933c963160 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Tue, 30 Jan 2024 09:29:05 +0100 Subject: [PATCH 071/198] fixed typo --- Source/Editor/Options/InterfaceOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 482946e9a..5c4a88b1e 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -175,7 +175,7 @@ namespace FlaxEditor.Options /// [DefaultValue(ValueFormattingType.None)] [EditorDisplay("Interface"), EditorOrder(300), Tooltip("Formatting of numeric values.")] - public ValueFormattingType ValueFormating { + public ValueFormattingType ValueFormatting { get => _valueFormatting; set { From 9bad65e3599bc671760d0f1965b04252d66abfbc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Jan 2024 10:04:10 +0100 Subject: [PATCH 072/198] Add better logging for mono messages --- Source/Engine/Scripting/Runtime/DotNet.cpp | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 6845941b2..b92f5fda2 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -1823,7 +1823,6 @@ void* GetStaticMethodPointer(const String& methodName) void OnLogCallback(const char* logDomain, const char* logLevel, const char* message, mono_bool fatal, void* userData) { - String currentDomain(logDomain); String msg(message); msg.Replace('\n', ' '); @@ -1851,19 +1850,6 @@ void OnLogCallback(const char* logDomain, const char* logLevel, const char* mess } } - if (currentDomain.IsEmpty()) - { - auto domain = MCore::GetActiveDomain(); - if (domain != nullptr) - { - currentDomain = domain->GetName().Get(); - } - else - { - currentDomain = "null"; - } - } - #if 0 // Print C# stack trace (crash may be caused by the managed code) if (mono_domain_get() && Assemblies::FlaxEngine.Assembly->IsLoaded()) @@ -1879,15 +1865,15 @@ void OnLogCallback(const char* logDomain, const char* logLevel, const char* mess if (errorLevel <= 2) { - Log::CLRInnerException(String::Format(TEXT("Message: {0} | Domain: {1}"), msg, currentDomain)).SetLevel(LogType::Error); + Log::CLRInnerException(String::Format(TEXT("[Mono] {0}"), msg)).SetLevel(LogType::Error); } else if (errorLevel <= 3) { - LOG(Warning, "Message: {0} | Domain: {1}", msg, currentDomain); + LOG(Warning, "[Mono] {0}", msg); } else { - LOG(Info, "Message: {0} | Domain: {1}", msg, currentDomain); + LOG(Info, "[Mono] {0}", msg); } #if DOTNET_HOST_MONO && !BUILD_RELEASE if (errorLevel <= 2) From 320024399df093b7a56a7511a67afe0196aad2e6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Jan 2024 15:43:39 +0100 Subject: [PATCH 073/198] Add support for overriding some defaults for GPU per-platform --- Source/Engine/Graphics/Config.h | 11 ++++---- .../Vulkan/GPUSwapChainVulkan.cpp | 26 ++----------------- 2 files changed, 7 insertions(+), 30 deletions(-) diff --git a/Source/Engine/Graphics/Config.h b/Source/Engine/Graphics/Config.h index 02d95c947..1b2dc3427 100644 --- a/Source/Engine/Graphics/Config.h +++ b/Source/Engine/Graphics/Config.h @@ -2,6 +2,8 @@ #pragma once +#include "Engine/Platform/Defines.h" + // Maximum amount of binded render targets at the same time #define GPU_MAX_RT_BINDED 6 @@ -44,11 +46,10 @@ // Enable/disable force shaders recompilation #define GPU_FORCE_RECOMPILE_SHADERS 0 -// True if use BGRA back buffer format -#define GPU_USE_BGRA_BACK_BUFFER 1 - // Default back buffer pixel format +#ifndef GPU_DEPTH_BUFFER_PIXEL_FORMAT #define GPU_DEPTH_BUFFER_PIXEL_FORMAT PixelFormat::D32_Float +#endif // Enable/disable gpu resources naming #define GPU_ENABLE_RESOURCE_NAMING (!BUILD_RELEASE) @@ -62,10 +63,8 @@ #define GPU_MAX_TEXTURE_ARRAY_SIZE 1024 // Define default back buffer(s) format -#if GPU_USE_BGRA_BACK_BUFFER +#ifndef GPU_BACK_BUFFER_PIXEL_FORMAT #define GPU_BACK_BUFFER_PIXEL_FORMAT PixelFormat::B8G8R8A8_UNorm -#else -#define GPU_BACK_BUFFER_PIXEL_FORMAT PixelFormat::R8G8B8A8_UNorm #endif // Validate configuration diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp index bb89399a6..7f604a14a 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp @@ -208,8 +208,6 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) { uint32 surfaceFormatsCount; VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, _surface, &surfaceFormatsCount, nullptr)); - ASSERT(surfaceFormatsCount > 0); - Array> surfaceFormats; surfaceFormats.AddZeroed(surfaceFormatsCount); VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, _surface, &surfaceFormatsCount, surfaceFormats.Get())); @@ -229,7 +227,6 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) break; } } - if (!found) { LOG(Warning, "Requested pixel format {0} not supported by this swapchain. Falling back to supported swapchain formats...", ScriptingEnum::ToString(resultFormat)); @@ -255,22 +252,18 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) { resultFormat = static_cast(pixelFormat); result = surfaceFormats[i]; - LOG(Info, "No swapchain format requested, picking up Vulkan format {0}", (uint32)result.format); + LOG(Info, "No swapchain format requested, picking up format {} (vk={})", ScriptingEnum::ToString(resultFormat), (int32)result.format); break; } } - if (resultFormat != PixelFormat::Unknown) - { break; - } } } if (resultFormat == PixelFormat::Unknown) { LOG(Warning, "Can't find a proper pixel format for the swapchain, trying to pick up the first available"); - const VkFormat format = RenderToolsVulkan::ToVulkanFormat(resultFormat); bool supported = false; for (int32 i = 0; i < surfaceFormats.Count(); i++) @@ -283,24 +276,14 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) } } ASSERT(supported); - String msg; for (int32 index = 0; index < surfaceFormats.Count(); index++) { - if (index == 0) - { - msg += TEXT("("); - } - else - { - msg += TEXT(", "); - } + msg += index == 0 ? TEXT("(") : TEXT(", "); msg += StringUtils::ToString((int32)surfaceFormats[index].format); } if (surfaceFormats.HasItems()) - { msg += TEXT(")"); - } LOG(Error, "Unable to find a pixel format for the swapchain; swapchain returned {0} Vulkan formats {1}", surfaceFormats.Count(), *msg); } } @@ -315,16 +298,12 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) { uint32 presentModesCount = 0; VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, _surface, &presentModesCount, nullptr)); - ASSERT(presentModesCount > 0); - Array> presentModes; presentModes.Resize(presentModesCount); VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, _surface, &presentModesCount, presentModes.Get())); - bool foundPresentModeMailbox = false; bool foundPresentModeImmediate = false; bool foundPresentModeFifo = false; - for (size_t i = 0; i < presentModesCount; i++) { switch (presentModes[(int32)i]) @@ -340,7 +319,6 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) break; } } - if (foundPresentModeMailbox) { presentMode = VK_PRESENT_MODE_MAILBOX_KHR; From fa58b171ec4e1c3257c41bbad5a86a0737f01420 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Jan 2024 18:14:53 +0100 Subject: [PATCH 074/198] Add support for using Tracy profiler on Switch --- Source/Engine/Profiler/Profiler.Build.cs | 1 + Source/ThirdParty/tracy/client/TracyProfiler.cpp | 14 +++++++++++--- Source/ThirdParty/tracy/common/TracyAlloc.hpp | 2 +- Source/ThirdParty/tracy/common/TracySocket.cpp | 3 +++ Source/ThirdParty/tracy/common/TracySystem.cpp | 6 +++++- Source/ThirdParty/tracy/tracy.Build.cs | 14 ++++++++++---- 6 files changed, 31 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Profiler/Profiler.Build.cs b/Source/Engine/Profiler/Profiler.Build.cs index 1f324fb48..ae6577473 100644 --- a/Source/Engine/Profiler/Profiler.Build.cs +++ b/Source/Engine/Profiler/Profiler.Build.cs @@ -34,6 +34,7 @@ public class Profiler : EngineModule case TargetPlatform.Android: case TargetPlatform.Linux: case TargetPlatform.Windows: + case TargetPlatform.Switch: options.PublicDependencies.Add("tracy"); break; } diff --git a/Source/ThirdParty/tracy/client/TracyProfiler.cpp b/Source/ThirdParty/tracy/client/TracyProfiler.cpp index 59c31d6f8..6d67eabf2 100644 --- a/Source/ThirdParty/tracy/client/TracyProfiler.cpp +++ b/Source/ThirdParty/tracy/client/TracyProfiler.cpp @@ -113,6 +113,11 @@ extern "C" typedef BOOL (WINAPI *t_GetLogicalProcessorInformationEx)( LOGICAL_PR # include #endif +#if !defined _WIN32 && !defined __linux__ && !defined __APPLE__ +#include "Engine/Core/Types/String.h" +#include "Engine/Platform/MemoryStats.h" +#endif + namespace tracy { @@ -383,7 +388,7 @@ static int64_t SetupHwTimer() static const char* GetProcessName() { - const char* processName = "unknown"; + const char* processName = "FlaxEngine"; #ifdef _WIN32 static char buf[_MAX_PATH]; GetModuleFileNameA( nullptr, buf, _MAX_PATH ); @@ -518,7 +523,10 @@ static const char* GetHostInfo() #elif defined __OpenBSD__ ptr += sprintf( ptr, "OS: BSD (OpenBSD)\n" ); #else - ptr += sprintf( ptr, "OS: unknown\n" ); + String computerName = Platform::GetComputerName(); + char computerNameBuf[60]; + StringUtils::ConvertUTF162ANSI(computerName.Get(), computerNameBuf, computerName.Length()); + ptr += sprintf( ptr, "OS: %s\n", computerNameBuf ); #endif #if defined _MSC_VER @@ -690,7 +698,7 @@ static const char* GetHostInfo() sysctlbyname( "hw.physmem", &memSize, &sz, nullptr, 0 ); ptr += sprintf( ptr, "RAM: %zu MB\n", memSize / 1024 / 1024 ); #else - ptr += sprintf( ptr, "RAM: unknown\n" ); + ptr += sprintf( ptr, "RAM: %zu MB\n", (size_t)Platform::GetMemoryStats().TotalPhysicalMemory / 1024 / 1024 ); #endif return buf; diff --git a/Source/ThirdParty/tracy/common/TracyAlloc.hpp b/Source/ThirdParty/tracy/common/TracyAlloc.hpp index 4e49df84d..a352c8478 100644 --- a/Source/ThirdParty/tracy/common/TracyAlloc.hpp +++ b/Source/ThirdParty/tracy/common/TracyAlloc.hpp @@ -3,7 +3,7 @@ #include -#if defined TRACY_ENABLE && !defined __EMSCRIPTEN__ +#if defined TRACY_ENABLE && !defined __EMSCRIPTEN__ && !defined TRACY_USE_MALLOC # include "../client/tracy_rpmalloc.hpp" # define TRACY_USE_RPMALLOC #endif diff --git a/Source/ThirdParty/tracy/common/TracySocket.cpp b/Source/ThirdParty/tracy/common/TracySocket.cpp index 259678989..42e815232 100644 --- a/Source/ThirdParty/tracy/common/TracySocket.cpp +++ b/Source/ThirdParty/tracy/common/TracySocket.cpp @@ -450,6 +450,9 @@ ListenSocket::~ListenSocket() static int addrinfo_and_socket_for_family( uint16_t port, int ai_family, struct addrinfo** res ) { +#if PLATFORM_SWITCH + Platform::GetNetworkConnectionType(); // Ensure to have network service initialized before using sockets +#endif struct addrinfo hints; memset( &hints, 0, sizeof( hints ) ); hints.ai_family = ai_family; diff --git a/Source/ThirdParty/tracy/common/TracySystem.cpp b/Source/ThirdParty/tracy/common/TracySystem.cpp index 9a477aa31..4de80185d 100644 --- a/Source/ThirdParty/tracy/common/TracySystem.cpp +++ b/Source/ThirdParty/tracy/common/TracySystem.cpp @@ -47,6 +47,10 @@ extern "C" typedef HRESULT (WINAPI *t_GetThreadDescription)( HANDLE, PWSTR* ); #ifdef TRACY_ENABLE # include # include "TracyAlloc.hpp" + +#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__linux__) +#include "Engine/Platform/Platform.h" +#endif #endif namespace tracy @@ -88,7 +92,7 @@ TRACY_API uint32_t GetThreadHandleImpl() // thread identifier. It is a pointer to a library-allocated data structure instead. // Such pointers will be reused heavily, making the pthread_t non-unique. Additionally // a 64-bit pointer cannot be reliably truncated to 32 bits. - #error "Unsupported platform!" + return Platform::GetCurrentThreadID(); #endif } diff --git a/Source/ThirdParty/tracy/tracy.Build.cs b/Source/ThirdParty/tracy/tracy.Build.cs index ca5f485b2..0edda1679 100644 --- a/Source/ThirdParty/tracy/tracy.Build.cs +++ b/Source/ThirdParty/tracy/tracy.Build.cs @@ -40,14 +40,20 @@ public class tracy : ThirdPartyModule options.PublicDefinitions.Add("TRACY_ENABLE"); options.PrivateDefinitions.Add("TRACY_NO_INVARIANT_CHECK"); options.PrivateDefinitions.Add("TRACY_NO_FRAME_IMAGE"); - if (options.Platform.Target == TargetPlatform.Windows) - { - options.PrivateDefinitions.Add("TRACY_DBGHELP_LOCK=DbgHelp"); - } if (OnDemand) { options.PublicDefinitions.Add("TRACY_ON_DEMAND"); } + switch (options.Platform.Target) + { + case TargetPlatform.Windows: + options.PrivateDefinitions.Add("TRACY_DBGHELP_LOCK=DbgHelp"); + break; + case TargetPlatform.Switch: + options.PrivateDefinitions.Add("TRACY_USE_MALLOC"); + options.PrivateDefinitions.Add("TRACY_ONLY_IPV4"); + break; + } } /// From 7fd6c43f84f92c469a8608c60c94e257f9c9358b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Jan 2024 18:45:34 +0100 Subject: [PATCH 075/198] Add more profier events to Vulkan backed --- .../GraphicsDevice/Vulkan/CmdBufferVulkan.cpp | 10 ++++++-- .../GraphicsDevice/Vulkan/CmdBufferVulkan.h | 2 +- .../Vulkan/DescriptorSetVulkan.cpp | 2 -- .../GraphicsDevice/Vulkan/GPUContextVulkan.h | 4 +-- .../GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp | 1 + .../Vulkan/GPUSwapChainVulkan.cpp | 25 +++++++------------ .../Vulkan/GPUSwapChainVulkan.h | 2 +- 7 files changed, 22 insertions(+), 24 deletions(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp index 22471ed8c..b02beeb5d 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp @@ -6,8 +6,11 @@ #include "RenderToolsVulkan.h" #include "QueueVulkan.h" #include "GPUContextVulkan.h" +#if VULKAN_USE_QUERIES #include "GPUTimerQueryVulkan.h" +#endif #include "DescriptorSetVulkan.h" +#include "Engine/Profiler/ProfilerCPU.h" void CmdBufferVulkan::AddWaitSemaphore(VkPipelineStageFlags waitFlags, SemaphoreVulkan* waitSemaphore) { @@ -18,6 +21,7 @@ void CmdBufferVulkan::AddWaitSemaphore(VkPipelineStageFlags waitFlags, Semaphore void CmdBufferVulkan::Begin() { + PROFILE_CPU(); ASSERT(_state == State::ReadyForBegin); VkCommandBufferBeginInfo beginInfo; @@ -41,6 +45,7 @@ void CmdBufferVulkan::Begin() void CmdBufferVulkan::End() { + PROFILE_CPU(); ASSERT(IsOutsideRenderPass()); #if GPU_ALLOW_PROFILE_EVENTS && VK_EXT_debug_utils @@ -242,13 +247,13 @@ CmdBufferManagerVulkan::CmdBufferManagerVulkan(GPUDeviceVulkan* device, GPUConte void CmdBufferManagerVulkan::SubmitActiveCmdBuffer(SemaphoreVulkan* signalSemaphore) { + PROFILE_CPU(); ASSERT(_activeCmdBuffer); if (!_activeCmdBuffer->IsSubmitted() && _activeCmdBuffer->HasBegun()) { if (_activeCmdBuffer->IsInsideRenderPass()) _activeCmdBuffer->EndRenderPass(); - #if VULKAN_USE_QUERIES // Pause all active queries for (int32 i = 0; i < _queriesInProgress.Count(); i++) @@ -268,12 +273,12 @@ void CmdBufferManagerVulkan::SubmitActiveCmdBuffer(SemaphoreVulkan* signalSemaph _queue->Submit(_activeCmdBuffer); } } - _activeCmdBuffer = nullptr; } void CmdBufferManagerVulkan::WaitForCmdBuffer(CmdBufferVulkan* cmdBuffer, float timeInSecondsToWait) { + PROFILE_CPU(); ASSERT(cmdBuffer->IsSubmitted()); const bool failed = _device->FenceManager.WaitForFence(cmdBuffer->GetFence(), (uint64)(timeInSecondsToWait * 1e9)); ASSERT(!failed); @@ -282,6 +287,7 @@ void CmdBufferManagerVulkan::WaitForCmdBuffer(CmdBufferVulkan* cmdBuffer, float void CmdBufferManagerVulkan::PrepareForNewActiveCommandBuffer() { + PROFILE_CPU(); for (int32 i = 0; i < _pool._cmdBuffers.Count(); i++) { auto cmdBuffer = _pool._cmdBuffers[i]; diff --git a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h index 6d0f7d83b..72732b439 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h @@ -199,7 +199,7 @@ public: return _queriesInProgress.Count() != 0; } - CmdBufferVulkan* GetCmdBuffer() + FORCE_INLINE CmdBufferVulkan* GetCmdBuffer() { if (!_activeCmdBuffer) PrepareForNewActiveCommandBuffer(); diff --git a/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.cpp index fdd844536..ebede5354 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.cpp @@ -312,7 +312,6 @@ DescriptorPoolsManagerVulkan::~DescriptorPoolsManagerVulkan() DescriptorPoolSetContainerVulkan& DescriptorPoolsManagerVulkan::AcquirePoolSetContainer() { ScopeLock lock(_locker); - for (auto* poolSet : _poolSets) { if (poolSet->IsUnused()) @@ -322,7 +321,6 @@ DescriptorPoolSetContainerVulkan& DescriptorPoolsManagerVulkan::AcquirePoolSetCo return *poolSet; } } - const auto poolSet = New(_device); _poolSets.Add(poolSet); return *poolSet; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h index c0968e742..595bc08e2 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h @@ -126,12 +126,12 @@ public: ~GPUContextVulkan(); public: - QueueVulkan* GetQueue() const + FORCE_INLINE QueueVulkan* GetQueue() const { return _queue; } - CmdBufferManagerVulkan* GetCmdBufferManager() const + FORCE_INLINE CmdBufferManagerVulkan* GetCmdBufferManager() const { return _cmdBufferManager; } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index 7d8c3a46f..f747b42b9 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -2020,6 +2020,7 @@ void GPUDeviceVulkan::WaitForGPU() { if (Device != VK_NULL_HANDLE) { + PROFILE_CPU(); VALIDATE_VULKAN_RESULT(vkDeviceWaitIdle(Device)); } } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp index 7f604a14a..e035be292 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp @@ -11,6 +11,7 @@ #include "Engine/Core/Log.h" #include "Engine/Graphics/GPULimits.h" #include "Engine/Scripting/Enums.h" +#include "Engine/Profiler/ProfilerCPU.h" void BackBufferVulkan::Setup(GPUSwapChainVulkan* window, VkImage backbuffer, PixelFormat format, VkExtent3D extent) { @@ -103,6 +104,7 @@ GPUTextureView* GPUSwapChainVulkan::GetBackBufferView() { if (_acquiredImageIndex == -1) { + PROFILE_CPU(); if (TryPresent(DoAcquireImageIndex) < 0) { LOG(Fatal, "Swapchain acquire image index failed!"); @@ -125,7 +127,6 @@ GPUTextureView* GPUSwapChainVulkan::GetBackBufferView() cmdBufferManager->PrepareForNewActiveCommandBuffer(); ASSERT(cmdBufferManager->HasPendingActiveCmdBuffer() && cmdBufferManager->GetActiveCmdBuffer()->GetState() == CmdBufferVulkan::State::IsInsideBegin); } - return &_backBuffers[_acquiredImageIndex].Handle; } @@ -174,7 +175,7 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) auto windowHandle = _window->GetNativePtr(); if (!windowHandle) return false; - + PROFILE_CPU(); GPUDeviceLock lock(_device); const auto device = _device->Device; @@ -411,9 +412,7 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) GPUSwapChainVulkan::Status GPUSwapChainVulkan::Present(QueueVulkan* presentQueue, SemaphoreVulkan* backBufferRenderingDoneSemaphore) { if (_currentImageIndex == -1) - { return Status::Ok; - } VkPresentInfoKHR presentInfo; RenderToolsVulkan::ZeroStruct(presentInfo, VK_STRUCTURE_TYPE_PRESENT_INFO_KHR); @@ -429,7 +428,6 @@ GPUSwapChainVulkan::Status GPUSwapChainVulkan::Present(QueueVulkan* presentQueue presentInfo.pImageIndices = (uint32*)&_currentImageIndex; const VkResult presentResult = vkQueuePresentKHR(presentQueue->GetHandle(), &presentInfo); - if (presentResult == VK_ERROR_OUT_OF_DATE_KHR) { return Status::Outdated; @@ -450,7 +448,7 @@ GPUSwapChainVulkan::Status GPUSwapChainVulkan::Present(QueueVulkan* presentQueue int32 GPUSwapChainVulkan::DoAcquireImageIndex(GPUSwapChainVulkan* viewport, void* customData) { - return viewport->_acquiredImageIndex = viewport->AcquireNextImage(&viewport->_acquiredSemaphore); + return viewport->_acquiredImageIndex = viewport->AcquireNextImage(viewport->_acquiredSemaphore); } int32 GPUSwapChainVulkan::DoPresent(GPUSwapChainVulkan* viewport, void* customData) @@ -462,7 +460,6 @@ int32 GPUSwapChainVulkan::TryPresent(Function { int32 attemptsPending = 4; int32 status = job(this, customData); - while (status < 0 && attemptsPending > 0) { if (status == (int32)Status::Outdated) @@ -491,18 +488,17 @@ int32 GPUSwapChainVulkan::TryPresent(Function _device->WaitForGPU(); status = job(this, customData); - attemptsPending--; } - return status; } -int32 GPUSwapChainVulkan::AcquireNextImage(SemaphoreVulkan** outSemaphore) +int32 GPUSwapChainVulkan::AcquireNextImage(SemaphoreVulkan*& outSemaphore) { + PROFILE_CPU(); ASSERT(_swapChain && _backBuffers.HasItems()); - uint32 imageIndex = 0; + uint32 imageIndex = _currentImageIndex; const int32 prevSemaphoreIndex = _semaphoreIndex; _semaphoreIndex = (_semaphoreIndex + 1) % _backBuffers.Count(); const auto semaphore = _backBuffers[_semaphoreIndex].ImageAcquiredSemaphore; @@ -514,21 +510,17 @@ int32 GPUSwapChainVulkan::AcquireNextImage(SemaphoreVulkan** outSemaphore) semaphore->GetHandle(), VK_NULL_HANDLE, &imageIndex); - if (result == VK_ERROR_OUT_OF_DATE_KHR) { _semaphoreIndex = prevSemaphoreIndex; return (int32)Status::Outdated; } - if (result == VK_ERROR_SURFACE_LOST_KHR) { _semaphoreIndex = prevSemaphoreIndex; return (int32)Status::LostSurface; } - - *outSemaphore = semaphore; - + outSemaphore = semaphore; if (result == VK_ERROR_VALIDATION_FAILED_EXT) { LOG(Fatal, "vkAcquireNextImageKHR failed with validation error"); @@ -549,6 +541,7 @@ void GPUSwapChainVulkan::Present(bool vsync) // Skip if there was no rendering to the backbuffer if (_acquiredImageIndex == -1) return; + PROFILE_CPU(); // Ensure that backbuffer has been acquired before presenting it to the window const auto backBuffer = (GPUTextureViewVulkan*)GetBackBufferView(); diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h index 2e20326bb..f0768915e 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h @@ -98,7 +98,7 @@ public: static int32 DoAcquireImageIndex(GPUSwapChainVulkan* viewport, void* customData); static int32 DoPresent(GPUSwapChainVulkan* viewport, void* customData); int32 TryPresent(Function job, void* customData = nullptr, bool skipOnOutOfDate = false); - int32 AcquireNextImage(SemaphoreVulkan** outSemaphore); + int32 AcquireNextImage(SemaphoreVulkan*& outSemaphore); private: void ReleaseBackBuffer(); From 22600a2c29e6a005e6d9ba0f8aba23bc78af99f2 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:01:22 +0100 Subject: [PATCH 076/198] add torque unit --- Source/Editor/Utilities/ShuntingYardParser.cs | 5 ++++- Source/Editor/Utilities/Utils.cs | 1 + Source/Engine/Physics/Actors/RigidBody.h | 2 +- Source/Engine/Utilities/Utils.cs | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index ca3780e8f..356fec2b5 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -144,13 +144,16 @@ namespace FlaxEditor.Utilities /// List known units which cannot be handled as a variable easily because they contain operator /// symbols (mostly a forward slash). The value is the factor to calculate game units. /// + // Nm is here because these values are compared case-sensitive and we don't want to confuse + // nanometers and Newtonmeters private static readonly IDictionary UnitSymbols = new Dictionary { ["cm/s"] = Units.Meters2Units / 100, ["cm/s²"] = Units.Meters2Units / 100, ["m/s"] = Units.Meters2Units, ["m/s²"] = Units.Meters2Units, - ["km/h"] = 1/3.6 * Units.Meters2Units + ["km/h"] = 1/3.6 * Units.Meters2Units, + ["Nm"] = Units.Meters2Units * Units.Meters2Units }; /// diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 531f2b50b..a0422bf7d 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1203,6 +1203,7 @@ namespace FlaxEditor.Utilities case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m³"; case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + "kg"; case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "N"; + case FlaxEngine.Utils.ValueCategory.Torque: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "Nm"; case FlaxEngine.Utils.ValueCategory.None: default: return FormatFloat(value); diff --git a/Source/Engine/Physics/Actors/RigidBody.h b/Source/Engine/Physics/Actors/RigidBody.h index 2ef50ca7a..c7fb20038 100644 --- a/Source/Engine/Physics/Actors/RigidBody.h +++ b/Source/Engine/Physics/Actors/RigidBody.h @@ -181,7 +181,7 @@ public: /// /// Gets the mass value measured in kilograms (use override value only if OverrideMass is checked). /// - API_PROPERTY(Attributes="EditorOrder(110), Limit(0), EditorDisplay(\"Rigid Body\")") + API_PROPERTY(Attributes="EditorOrder(110), Limit(0), EditorDisplay(\"Rigid Body\"), NumberCategory(Utils.ValueCategory.Mass)") float GetMass() const; /// diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs index 9ae42fa43..e4b7c544b 100644 --- a/Source/Engine/Utilities/Utils.cs +++ b/Source/Engine/Utilities/Utils.cs @@ -1053,7 +1053,8 @@ namespace FlaxEngine Speed, Acceleration, Time, - Force + Force, + Torque } } } From 7a802a2eeef381904300c3cc69e4ec3cbe5c1979 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Wed, 31 Jan 2024 20:39:39 +0100 Subject: [PATCH 077/198] format +/- infinity --- Source/Editor/Utilities/Utils.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index a0422bf7d..445520f76 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1179,6 +1179,10 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(float value, FlaxEngine.Utils.ValueCategory category) { + if (float.IsPositiveInfinity(value) || value == float.MaxValue) + return "Infinity"; + if (float.IsNegativeInfinity(value) || value == float.MinValue) + return "-Infinity"; const string format = "g7"; if (!Units.UseUnitsFormatting) return FormatFloat(value); @@ -1218,6 +1222,10 @@ namespace FlaxEditor.Utilities /// the formatted string public static string FormatFloat(double value, FlaxEngine.Utils.ValueCategory category) { + if (double.IsPositiveInfinity(value) || value == double.MaxValue) + return "Infinity"; + if (double.IsNegativeInfinity(value) || value == double.MinValue) + return "-Infinity"; const string format = "g15"; if (!Units.UseUnitsFormatting) return FormatFloat(value); From 245cf63b0b5c269a248def238897f7f85c888352 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Wed, 31 Jan 2024 20:40:06 +0100 Subject: [PATCH 078/198] format break limits on joints --- Source/Engine/Physics/Joints/Joint.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Physics/Joints/Joint.h b/Source/Engine/Physics/Joints/Joint.h index f96e47b71..e5805631d 100644 --- a/Source/Engine/Physics/Joints/Joint.h +++ b/Source/Engine/Physics/Joints/Joint.h @@ -38,7 +38,7 @@ public: /// /// Gets the break force. Determines the maximum force the joint can apply before breaking. Broken joints no longer participate in physics simulation. /// - API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\")") + API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Force)") FORCE_INLINE float GetBreakForce() const { return _breakForce; @@ -52,7 +52,7 @@ public: /// /// Gets the break torque. Determines the maximum torque the joint can apply before breaking. Broken joints no longer participate in physics simulation. /// - API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\")") + API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Torque)") FORCE_INLINE float GetBreakTorque() const { return _breakTorque; From 54658cbb3b7083385817b4f35dbae965882c08da Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Wed, 31 Jan 2024 22:05:24 +0100 Subject: [PATCH 079/198] =?UTF-8?q?fix=20=C2=B2=20and=20=C2=B3=20in=20pars?= =?UTF-8?q?er?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/Editor/Utilities/ShuntingYardParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index 356fec2b5..b362823eb 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -189,7 +189,7 @@ namespace FlaxEditor.Utilities if (Operators.ContainsKey(str)) return TokenType.Operator; - if (char.IsLetter(c)) + if (char.IsLetter(c) || c=='²' || c=='³') return TokenType.Variable; throw new ParsingException("wrong character"); From 7f95dab511cf38990520cd2b9c2641d6a4f138f2 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Wed, 31 Jan 2024 22:05:54 +0100 Subject: [PATCH 080/198] more joint values formatted --- Source/Engine/Physics/Joints/DistanceJoint.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Physics/Joints/DistanceJoint.h b/Source/Engine/Physics/Joints/DistanceJoint.h index 96ddc9538..0ae98414e 100644 --- a/Source/Engine/Physics/Joints/DistanceJoint.h +++ b/Source/Engine/Physics/Joints/DistanceJoint.h @@ -67,7 +67,7 @@ public: /// Gets the allowed minimum distance for the joint. /// /// Used only when DistanceJointFlag.MinDistance flag is set. The minimum distance must be no more than the maximum distance. Default: 0, Range: [0, float.MaxValue]. - API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(0.0f), Limit(0.0f), EditorDisplay(\"Joint\")") + API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(0.0f), Limit(0.0f), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetMinDistance() const { return _minDistance; @@ -83,7 +83,7 @@ public: /// Gets the allowed maximum distance for the joint. /// /// Used only when DistanceJointFlag.MaxDistance flag is set. The maximum distance must be no less than the minimum distance. Default: 0, Range: [0, float.MaxValue]. - API_PROPERTY(Attributes="EditorOrder(120), DefaultValue(10.0f), Limit(0.0f), EditorDisplay(\"Joint\")") + API_PROPERTY(Attributes="EditorOrder(120), DefaultValue(10.0f), Limit(0.0f), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetMaxDistance() const { return _maxDistance; From 8aa402bcb9b7ec22cb68d98011c6832d85b1c381 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Wed, 31 Jan 2024 23:21:57 +0100 Subject: [PATCH 081/198] add space option and avoid duplicated code --- Source/Editor/Options/InterfaceOptions.cs | 15 ++++ Source/Editor/Utilities/Units.cs | 23 ++++-- Source/Editor/Utilities/Utils.cs | 87 ++++++++++------------- 3 files changed, 72 insertions(+), 53 deletions(-) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 5c4a88b1e..3e32b0933 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -185,6 +185,21 @@ namespace FlaxEditor.Options } } + private bool _spaceNumberAndUnits = false; + + /// + /// Gets or sets the option to put a space between numbers and units for unit formatting + /// + [DefaultValue(false)] + [EditorDisplay("Interface"), EditorOrder(310), Tooltip("Put a space between numbers and units.")] + public bool SpaceNumberAndUnits { get => _spaceNumberAndUnits; + set + { + _spaceNumberAndUnits = value; + Units.SpaceNumberAndUnits = _spaceNumberAndUnits; + } + } + /// /// Gets or sets the timestamps prefix mode for output log messages. /// diff --git a/Source/Editor/Utilities/Units.cs b/Source/Editor/Utilities/Units.cs index 1256ccd63..cd4e67882 100644 --- a/Source/Editor/Utilities/Units.cs +++ b/Source/Editor/Utilities/Units.cs @@ -7,15 +7,30 @@ public class Units /// public static readonly float Meters2Units = 100f; - // the next two bools could be cached values in the user preferences - /// - /// Set it to false to always show game units without any postfix + /// Set it to false to always show game units without any postfix. /// public static bool UseUnitsFormatting = true; /// - /// If set to true, the distance unit is chosen on the magnitude, otherwise it's meters + /// Add a space between numbers and units for readability. + /// + public static bool SpaceNumberAndUnits = true; + + /// + /// If set to true, the distance unit is chosen on the magnitude, otherwise it's meters. /// public static bool AutomaticUnitsFormatting = true; + + /// + /// Return the unit according to user settings. + /// + /// + /// + public static string Unit(string unit) + { + if (SpaceNumberAndUnits) + return $" {unit}"; + return unit; + } } diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 445520f76..17456babf 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1171,6 +1171,36 @@ namespace FlaxEditor.Utilities return StringUtils.GetPathWithoutExtension(path); } + private static string InternalFormat(double value, string format, FlaxEngine.Utils.ValueCategory category) + { + switch (category) + { + case FlaxEngine.Utils.ValueCategory.Distance: + if (!Units.AutomaticUnitsFormatting) + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m"); + var absValue = Mathf.Abs(value); + // in case a unit != cm this would be (value / Meters2Units * 100) + if (absValue < Units.Meters2Units) + return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("cm"); + if (absValue < Units.Meters2Units * 1000) + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m"); + return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("km"); + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; + case FlaxEngine.Utils.ValueCategory.Time: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("s"); + // some fonts have a symbol for that: "\u33A7" + case FlaxEngine.Utils.ValueCategory.Speed: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s"); + case FlaxEngine.Utils.ValueCategory.Acceleration: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s²"); + case FlaxEngine.Utils.ValueCategory.Area: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m²"); + case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m³"); + case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("kg"); + case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("N"); + case FlaxEngine.Utils.ValueCategory.Torque: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("Nm"); + case FlaxEngine.Utils.ValueCategory.None: + default: + return FormatFloat(value); + } + } + /// /// Format a float value either as-is, with a distance unit or with a degree sign /// @@ -1183,35 +1213,10 @@ namespace FlaxEditor.Utilities return "Infinity"; if (float.IsNegativeInfinity(value) || value == float.MinValue) return "-Infinity"; - const string format = "g7"; - if (!Units.UseUnitsFormatting) + if (!Units.UseUnitsFormatting || category == FlaxEngine.Utils.ValueCategory.None) return FormatFloat(value); - switch (category) - { - case FlaxEngine.Utils.ValueCategory.Distance: - if (!Units.AutomaticUnitsFormatting) - return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; - var absValue = Mathf.Abs(value); - // in case a unit != cm this would be (value / Meters2Units * 100) - if (absValue < Units.Meters2Units) - return value.ToString(format, CultureInfo.InvariantCulture) + "cm"; - if (absValue < Units.Meters2Units * 1000) - return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; - return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "km"; - case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; - case FlaxEngine.Utils.ValueCategory.Time: return value.ToString(format, CultureInfo.InvariantCulture) + "s"; - // some fonts have a symbol for that: "\u33A7" - case FlaxEngine.Utils.ValueCategory.Speed: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m/s"; - case FlaxEngine.Utils.ValueCategory.Acceleration: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m/s²"; - case FlaxEngine.Utils.ValueCategory.Area: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m²"; - case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m³"; - case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + "kg"; - case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "N"; - case FlaxEngine.Utils.ValueCategory.Torque: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "Nm"; - case FlaxEngine.Utils.ValueCategory.None: - default: - return FormatFloat(value); - } + const string format = "G7"; + return InternalFormat(value, format, category); } /// @@ -1226,26 +1231,10 @@ namespace FlaxEditor.Utilities return "Infinity"; if (double.IsNegativeInfinity(value) || value == double.MinValue) return "-Infinity"; - const string format = "g15"; - if (!Units.UseUnitsFormatting) + if (!Units.UseUnitsFormatting || category == FlaxEngine.Utils.ValueCategory.None) return FormatFloat(value); - switch (category) - { - case FlaxEngine.Utils.ValueCategory.Distance: - if (!Units.AutomaticUnitsFormatting) - return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; - var absValue = Mathf.Abs(value); - // in case a unit != cm this would be (value / Maters2Units * 100) - if (absValue < Units.Meters2Units) - return value.ToString(format, CultureInfo.InvariantCulture) + "cm"; - if (absValue < Units.Meters2Units * 1000) - return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "m"; - return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + "km"; - case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; - case FlaxEngine.Utils.ValueCategory.None: - default: - return FormatFloat(value); - } + const string format = "G15"; + return InternalFormat(value, format, category); } /// @@ -1259,7 +1248,7 @@ namespace FlaxEditor.Utilities return "Infinity"; if (float.IsNegativeInfinity(value) || value == float.MinValue) return "-Infinity"; - string str = value.ToString("g7", CultureInfo.InvariantCulture); + string str = value.ToString("R", CultureInfo.InvariantCulture); return FormatFloat(str, value < 0); } @@ -1274,7 +1263,7 @@ namespace FlaxEditor.Utilities return "Infinity"; if (double.IsNegativeInfinity(value) || value == double.MinValue) return "-Infinity"; - string str = value.ToString("g15", CultureInfo.InvariantCulture); + string str = value.ToString("R", CultureInfo.InvariantCulture); return FormatFloat(str, value < 0); } From 8488a8d387461cb1932158fc461eedd025aa8460 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 1 Feb 2024 14:47:17 +0100 Subject: [PATCH 082/198] Add support for stencil buffer on Vulkan and D3D11/D3D12 --- .../Engine/Graphics/PixelFormatExtensions.cpp | 5 + .../GraphicsDevice/Vulkan/CmdBufferVulkan.cpp | 21 +- .../GraphicsDevice/Vulkan/CmdBufferVulkan.h | 4 +- Source/Engine/GraphicsDevice/Vulkan/Config.h | 1 + .../Vulkan/GPUContextVulkan.cpp | 209 +++++++++--------- .../GraphicsDevice/Vulkan/GPUContextVulkan.h | 27 +-- .../GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp | 78 ++++--- .../GraphicsDevice/Vulkan/GPUDeviceVulkan.h | 60 +++-- .../Vulkan/GPUPipelineStateVulkan.cpp | 6 +- .../Vulkan/GPUPipelineStateVulkan.h | 37 +--- .../Vulkan/GPUTextureVulkan.cpp | 46 +++- .../GraphicsDevice/Vulkan/GPUTextureVulkan.h | 5 + .../GraphicsDevice/Vulkan/RenderToolsVulkan.h | 23 +- Source/Engine/Renderer/MotionBlurPass.cpp | 3 - Source/Engine/Renderer/ReflectionsPass.cpp | 6 +- 15 files changed, 268 insertions(+), 263 deletions(-) diff --git a/Source/Engine/Graphics/PixelFormatExtensions.cpp b/Source/Engine/Graphics/PixelFormatExtensions.cpp index 9d9c21fcd..32c269e0a 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.cpp +++ b/Source/Engine/Graphics/PixelFormatExtensions.cpp @@ -236,6 +236,7 @@ bool PixelFormatExtensions::HasStencil(const PixelFormat format) switch (format) { case PixelFormat::D24_UNorm_S8_UInt: + case PixelFormat::D32_Float_S8X24_UInt: return true; default: return false; @@ -848,6 +849,10 @@ PixelFormat PixelFormatExtensions::MakeTypeless(const PixelFormat format) case PixelFormat::BC7_UNorm: case PixelFormat::BC7_UNorm_sRGB: return PixelFormat::BC7_Typeless; + case PixelFormat::D24_UNorm_S8_UInt: + return PixelFormat::R24G8_Typeless; + case PixelFormat::D32_Float_S8X24_UInt: + return PixelFormat::R32G8X24_Typeless; default: return format; } diff --git a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp index b02beeb5d..ee5fbc1dc 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp @@ -32,7 +32,7 @@ void CmdBufferVulkan::Begin() // Acquire a descriptor pool set on if (_descriptorPoolSetContainer == nullptr) { - AcquirePoolSet(); + _descriptorPoolSetContainer = &_device->DescriptorPoolsManager->AcquirePoolSetContainer(); } _state = State::IsInsideBegin; @@ -61,18 +61,16 @@ void CmdBufferVulkan::End() void CmdBufferVulkan::BeginRenderPass(RenderPassVulkan* renderPass, FramebufferVulkan* framebuffer, uint32 clearValueCount, VkClearValue* clearValues) { ASSERT(IsOutsideRenderPass()); - VkRenderPassBeginInfo info; RenderToolsVulkan::ZeroStruct(info, VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO); - info.renderPass = renderPass->GetHandle(); - info.framebuffer = framebuffer->GetHandle(); + info.renderPass = renderPass->Handle; + info.framebuffer = framebuffer->Handle; info.renderArea.offset.x = 0; info.renderArea.offset.y = 0; info.renderArea.extent.width = framebuffer->Extent.width; info.renderArea.extent.height = framebuffer->Extent.height; info.clearValueCount = clearValueCount; info.pClearValues = clearValues; - vkCmdBeginRenderPass(_commandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); _state = State::IsInsideRenderPass; } @@ -84,12 +82,6 @@ void CmdBufferVulkan::EndRenderPass() _state = State::IsInsideBegin; } -void CmdBufferVulkan::AcquirePoolSet() -{ - ASSERT(!_descriptorPoolSetContainer); - _descriptorPoolSetContainer = &_device->DescriptorPoolsManager->AcquirePoolSetContainer(); -} - #if GPU_ALLOW_PROFILE_EVENTS void CmdBufferVulkan::BeginEvent(const Char* name) @@ -134,6 +126,7 @@ void CmdBufferVulkan::RefreshFenceStatus() { if (_state == State::Submitted) { + PROFILE_CPU(); if (_device->FenceManager.IsFenceSignaled(_fence)) { _state = State::ReadyForBegin; @@ -258,7 +251,7 @@ void CmdBufferManagerVulkan::SubmitActiveCmdBuffer(SemaphoreVulkan* signalSemaph // Pause all active queries for (int32 i = 0; i < _queriesInProgress.Count(); i++) { - _queriesInProgress[i]->Interrupt(_activeCmdBuffer); + _queriesInProgress.Get()[i]->Interrupt(_activeCmdBuffer); } #endif @@ -290,7 +283,7 @@ void CmdBufferManagerVulkan::PrepareForNewActiveCommandBuffer() PROFILE_CPU(); for (int32 i = 0; i < _pool._cmdBuffers.Count(); i++) { - auto cmdBuffer = _pool._cmdBuffers[i]; + auto cmdBuffer = _pool._cmdBuffers.Get()[i]; cmdBuffer->RefreshFenceStatus(); if (cmdBuffer->GetState() == CmdBufferVulkan::State::ReadyForBegin) { @@ -312,7 +305,7 @@ void CmdBufferManagerVulkan::PrepareForNewActiveCommandBuffer() // Resume any paused queries with the new command buffer for (int32 i = 0; i < _queriesInProgress.Count(); i++) { - _queriesInProgress[i]->Resume(_activeCmdBuffer); + _queriesInProgress.Get()[i]->Resume(_activeCmdBuffer); } #endif } diff --git a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h index 72732b439..c1b694ec1 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h @@ -124,13 +124,11 @@ public: void BeginRenderPass(RenderPassVulkan* renderPass, FramebufferVulkan* framebuffer, uint32 clearValueCount, VkClearValue* clearValues); void EndRenderPass(); - DescriptorPoolSetContainerVulkan* GetDescriptorPoolSet() const + FORCE_INLINE DescriptorPoolSetContainerVulkan* GetDescriptorPoolSet() const { return _descriptorPoolSetContainer; } - void AcquirePoolSet(); - #if GPU_ALLOW_PROFILE_EVENTS void BeginEvent(const Char* name); void EndEvent(); diff --git a/Source/Engine/GraphicsDevice/Vulkan/Config.h b/Source/Engine/GraphicsDevice/Vulkan/Config.h index 95162de9f..ade8d96be 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Config.h +++ b/Source/Engine/GraphicsDevice/Vulkan/Config.h @@ -29,6 +29,7 @@ #define VULKAN_RESET_QUERY_POOLS 0 #define VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID 1 #define VULKAN_USE_DEBUG_LAYER GPU_ENABLE_DIAGNOSTICS +#define VULKAN_USE_DEBUG_DATA (GPU_ENABLE_DIAGNOSTICS && COMPILE_WITH_DEV_ENV) #ifndef VULKAN_USE_QUERIES #define VULKAN_USE_QUERIES 1 diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index 138454e36..a0c153cc5 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -72,55 +72,19 @@ const Char* ToString(VkImageLayout layout) #endif -void PipelineBarrierVulkan::AddImageBarrier(VkImage image, const VkImageSubresourceRange& range, VkImageLayout srcLayout, VkImageLayout dstLayout, GPUTextureViewVulkan* handle) -{ -#if VK_ENABLE_BARRIERS_DEBUG - ImageBarriersDebug.Add(handle); -#endif - VkImageMemoryBarrier& imageBarrier = ImageBarriers.AddOne(); - RenderToolsVulkan::ZeroStruct(imageBarrier, VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER); - imageBarrier.image = image; - imageBarrier.subresourceRange = range; - imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - imageBarrier.oldLayout = srcLayout; - imageBarrier.newLayout = dstLayout; - SourceStage |= RenderToolsVulkan::GetImageBarrierFlags(srcLayout, imageBarrier.srcAccessMask); - DestStage |= RenderToolsVulkan::GetImageBarrierFlags(dstLayout, imageBarrier.dstAccessMask); -#if VK_ENABLE_BARRIERS_DEBUG - LOG(Warning, "Image Barrier: 0x{0:x}, {1} -> {2} for baseMipLevel: {3}, baseArrayLayer: {4}, levelCount: {5}, layerCount: {6} ({7})", - (uintptr)image, - ToString(srcLayout), - ToString(dstLayout), - range.baseMipLevel, - range.baseArrayLayer, - range.levelCount, - range.layerCount, - handle && handle->Owner->AsGPUResource() ? handle->Owner->AsGPUResource()->ToString() : String::Empty - ); -#endif -} - -void PipelineBarrierVulkan::AddBufferBarrier(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, VkAccessFlags srcAccess, VkAccessFlags dstAccess) -{ - VkBufferMemoryBarrier& bufferBarrier = BufferBarriers.AddOne(); - RenderToolsVulkan::ZeroStruct(bufferBarrier, VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER); - bufferBarrier.buffer = buffer; - bufferBarrier.offset = offset; - bufferBarrier.size = size; - bufferBarrier.srcAccessMask = srcAccess; - bufferBarrier.dstAccessMask = dstAccess; - bufferBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - bufferBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - SourceStage |= RenderToolsVulkan::GetBufferBarrierFlags(srcAccess); - DestStage |= RenderToolsVulkan::GetBufferBarrierFlags(dstAccess); -} - -void PipelineBarrierVulkan::Execute(CmdBufferVulkan* cmdBuffer) +void PipelineBarrierVulkan::Execute(const CmdBufferVulkan* cmdBuffer) { ASSERT(cmdBuffer->IsOutsideRenderPass()); vkCmdPipelineBarrier(cmdBuffer->GetHandle(), SourceStage, DestStage, 0, 0, nullptr, BufferBarriers.Count(), BufferBarriers.Get(), ImageBarriers.Count(), ImageBarriers.Get()); - Reset(); + + // Reset + SourceStage = 0; + DestStage = 0; + ImageBarriers.Clear(); + BufferBarriers.Clear(); +#if VK_ENABLE_BARRIERS_DEBUG + ImageBarriersDebug.Clear(); +#endif } GPUContextVulkan::GPUContextVulkan(GPUDeviceVulkan* device, QueueVulkan* queue) @@ -154,7 +118,7 @@ GPUContextVulkan::~GPUContextVulkan() Delete(_cmdBufferManager); } -void GPUContextVulkan::AddImageBarrier(VkImage image, VkImageLayout srcLayout, VkImageLayout dstLayout, VkImageSubresourceRange& subresourceRange, GPUTextureViewVulkan* handle) +void GPUContextVulkan::AddImageBarrier(VkImage image, VkImageLayout srcLayout, VkImageLayout dstLayout, const VkImageSubresourceRange& subresourceRange, GPUTextureViewVulkan* handle) { #if VK_ENABLE_BARRIERS_BATCHING // Auto-flush on overflow @@ -168,7 +132,31 @@ void GPUContextVulkan::AddImageBarrier(VkImage image, VkImageLayout srcLayout, V #endif // Insert barrier - _barriers.AddImageBarrier(image, subresourceRange, srcLayout, dstLayout, handle); +#if VK_ENABLE_BARRIERS_DEBUG + _barriers.ImageBarriersDebug.Add(handle); +#endif + VkImageMemoryBarrier& imageBarrier = _barriers.ImageBarriers.AddOne(); + RenderToolsVulkan::ZeroStruct(imageBarrier, VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER); + imageBarrier.image = image; + imageBarrier.subresourceRange = subresourceRange; + imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageBarrier.oldLayout = srcLayout; + imageBarrier.newLayout = dstLayout; + _barriers.SourceStage |= RenderToolsVulkan::GetImageBarrierFlags(srcLayout, imageBarrier.srcAccessMask); + _barriers.DestStage |= RenderToolsVulkan::GetImageBarrierFlags(dstLayout, imageBarrier.dstAccessMask); +#if VK_ENABLE_BARRIERS_DEBUG + LOG(Warning, "Image Barrier: 0x{0:x}, {1} -> {2} for baseMipLevel: {3}, baseArrayLayer: {4}, levelCount: {5}, layerCount: {6} ({7})", + (uintptr)image, + ::ToString(srcLayout), + ::ToString(dstLayout), + subresourceRange.baseMipLevel, + subresourceRange.baseArrayLayer, + subresourceRange.levelCount, + subresourceRange.layerCount, + handle && handle->Owner->AsGPUResource() ? handle->Owner->AsGPUResource()->ToString() : String::Empty + ); +#endif #if !VK_ENABLE_BARRIERS_BATCHING // Auto-flush without batching @@ -306,7 +294,17 @@ void GPUContextVulkan::AddBufferBarrier(GPUBufferVulkan* buffer, VkAccessFlags d #endif // Insert barrier - _barriers.AddBufferBarrier(buffer->GetHandle(), 0, buffer->GetSize(), buffer->Access, dstAccess); + VkBufferMemoryBarrier& bufferBarrier = _barriers.BufferBarriers.AddOne(); + RenderToolsVulkan::ZeroStruct(bufferBarrier, VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER); + bufferBarrier.buffer = buffer->GetHandle(); + bufferBarrier.offset = 0; + bufferBarrier.size = buffer->GetSize(); + bufferBarrier.srcAccessMask = buffer->Access; + bufferBarrier.dstAccessMask = dstAccess; + bufferBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + bufferBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + _barriers.SourceStage |= RenderToolsVulkan::GetBufferBarrierFlags(buffer->Access); + _barriers.DestStage |= RenderToolsVulkan::GetBufferBarrierFlags(dstAccess); buffer->Access = dstAccess; #if !VK_ENABLE_BARRIERS_BATCHING @@ -404,8 +402,18 @@ void GPUContextVulkan::BeginRenderPass() if (_rtDepth) { handle = _rtDepth; - layout.ReadDepth = true; // TODO: use proper depthStencilAccess flags - layout.WriteDepth = handle->LayoutRTV == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // TODO: do it in a proper way + layout.ReadDepth = true; + layout.ReadStencil = PixelFormatExtensions::HasStencil(handle->GetFormat()); + layout.WriteDepth = handle->LayoutRTV == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || handle->LayoutRTV == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL || handle->LayoutRTV == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; + layout.WriteStencil = handle->LayoutRTV == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || handle->LayoutRTV == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL || handle->LayoutRTV == VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL; + if (_currentState && 0) + { + // TODO: use this but only if state doesn't change during whole render pass (eg. 1st draw call might not draw depth but 2nd might) + layout.ReadDepth &= _currentState->DepthReadEnable; + layout.ReadStencil &= _currentState->StencilReadEnable; + layout.WriteDepth &= _currentState->DepthWriteEnable; + layout.WriteStencil &= _currentState->StencilWriteEnable; + } framebufferKey.AttachmentCount++; framebufferKey.Attachments[_rtCount] = handle->GetFramebufferView(); AddImageBarrier(handle, handle->LayoutRTV); @@ -571,55 +579,6 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des } } -void GPUContextVulkan::UpdateDescriptorSets(GPUPipelineStateVulkan* pipelineState) -{ - const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); - const auto pipelineLayout = pipelineState->GetLayout(); - ASSERT(pipelineLayout); - bool needsWrite = false; - - // No current descriptor pools set - acquire one and reset - const bool newDescriptorPool = pipelineState->AcquirePoolSet(cmdBuffer); - needsWrite |= newDescriptorPool; - - // Update descriptors for every used shader stage - uint32 remainingHasDescriptorsPerStageMask = pipelineState->HasDescriptorsPerStageMask; - for (int32 stage = 0; stage < DescriptorSet::GraphicsStagesCount && remainingHasDescriptorsPerStageMask; stage++) - { - // Only process stages that exist in this pipeline and use descriptors - if (remainingHasDescriptorsPerStageMask & 1) - { - UpdateDescriptorSets(*pipelineState->DescriptorInfoPerStage[stage], pipelineState->DSWriter[stage], needsWrite); - } - - remainingHasDescriptorsPerStageMask >>= 1; - } - - // Allocate sets if need to - //if (needsWrite) // TODO: write on change only? - { - if (!pipelineState->AllocateDescriptorSets()) - { - return; - } - uint32 remainingStagesMask = pipelineState->HasDescriptorsPerStageMask; - uint32 stage = 0; - while (remainingStagesMask) - { - if (remainingStagesMask & 1) - { - const VkDescriptorSet descriptorSet = pipelineState->DescriptorSetHandles[stage]; - pipelineState->DSWriter[stage].SetDescriptorSet(descriptorSet); - } - - stage++; - remainingStagesMask >>= 1; - } - - vkUpdateDescriptorSets(_device->Device, pipelineState->DSWriteContainer.DescriptorWrites.Count(), pipelineState->DSWriteContainer.DescriptorWrites.Get(), 0, nullptr); - } -} - void GPUContextVulkan::UpdateDescriptorSets(ComputePipelineStateVulkan* pipelineState) { const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); @@ -654,8 +613,6 @@ void GPUContextVulkan::OnDrawCall() GPUPipelineStateVulkan* pipelineState = _currentState; ASSERT(pipelineState && pipelineState->IsValid()); const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); - const auto pipelineLayout = pipelineState->GetLayout(); - ASSERT(pipelineLayout); // End previous render pass if render targets layout was modified if (_rtDirtyFlag && cmdBuffer->IsInsideRenderPass()) @@ -663,7 +620,35 @@ void GPUContextVulkan::OnDrawCall() if (pipelineState->HasDescriptorsPerStageMask) { - UpdateDescriptorSets(pipelineState); + // Get descriptor pools set + bool needsWrite = pipelineState->AcquirePoolSet(cmdBuffer); + + // Update descriptors for every used shader stage + uint32 remainingHasDescriptorsPerStageMask = pipelineState->HasDescriptorsPerStageMask; + for (int32 stage = 0; stage < DescriptorSet::GraphicsStagesCount && remainingHasDescriptorsPerStageMask; stage++) + { + if (remainingHasDescriptorsPerStageMask & 1) + UpdateDescriptorSets(*pipelineState->DescriptorInfoPerStage[stage], pipelineState->DSWriter[stage], needsWrite); + remainingHasDescriptorsPerStageMask >>= 1; + } + + // Allocate sets if need to + //if (needsWrite) // TODO: write on change only? + { + if (!pipelineState->CurrentTypedDescriptorPoolSet->AllocateDescriptorSets(*pipelineState->DescriptorSetsLayout, pipelineState->DescriptorSetHandles.Get())) + return; + uint32 remainingStagesMask = pipelineState->HasDescriptorsPerStageMask; + uint32 stage = 0; + while (remainingStagesMask) + { + if (remainingStagesMask & 1) + pipelineState->DSWriter[stage].SetDescriptorSet(pipelineState->DescriptorSetHandles[stage]); + remainingStagesMask >>= 1; + stage++; + } + + vkUpdateDescriptorSets(_device->Device, pipelineState->DSWriteContainer.DescriptorWrites.Count(), pipelineState->DSWriteContainer.DescriptorWrites.Get(), 0, nullptr); + } } // Bind any missing vertex buffers to null if required by the current state @@ -689,26 +674,30 @@ void GPUContextVulkan::OnDrawCall() } // Bind pipeline - if (_psDirtyFlag && _currentState && (_rtDepth || _rtCount)) + if (_psDirtyFlag && pipelineState && (_rtDepth || _rtCount)) { _psDirtyFlag = false; const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); - const auto pipeline = _currentState->GetState(_renderPass); + const auto pipeline = pipelineState->GetState(_renderPass); vkCmdBindPipeline(cmdBuffer->GetHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); RENDER_STAT_PS_STATE_CHANGE(); } - //UpdateDynamicStates(); - // Bind descriptors sets to the graphics pipeline if (pipelineState->HasDescriptorsPerStageMask) { - pipelineState->Bind(cmdBuffer); + vkCmdBindDescriptorSets( + cmdBuffer->GetHandle(), + VK_PIPELINE_BIND_POINT_GRAPHICS, + pipelineState->GetLayout()->GetHandle(), + 0, + pipelineState->DescriptorSetHandles.Count(), + pipelineState->DescriptorSetHandles.Get(), + pipelineState->DynamicOffsets.Count(), + pipelineState->DynamicOffsets.Get()); } - // Clear flag _rtDirtyFlag = false; - #if VK_ENABLE_BARRIERS_DEBUG LOG(Warning, "Draw"); #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h index 595bc08e2..2be6b4fef 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h @@ -40,41 +40,25 @@ class DescriptorSetLayoutVulkan; /// struct PipelineBarrierVulkan { -public: VkPipelineStageFlags SourceStage = 0; VkPipelineStageFlags DestStage = 0; Array> ImageBarriers; Array> BufferBarriers; #if VK_ENABLE_BARRIERS_DEBUG - Array> ImageBarriersDebug; + Array> ImageBarriersDebug; #endif -public: - inline void Reset() - { - SourceStage = 0; - DestStage = 0; - ImageBarriers.Clear(); - BufferBarriers.Clear(); -#if VK_ENABLE_BARRIERS_DEBUG - ImageBarriersDebug.Clear(); -#endif - } - - void AddImageBarrier(VkImage image, const VkImageSubresourceRange& range, VkImageLayout srcLayout, VkImageLayout dstLayout, GPUTextureViewVulkan* handle); - void AddBufferBarrier(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, VkAccessFlags srcAccess, VkAccessFlags dstAccess); - - inline bool IsFull() const + FORCE_INLINE bool IsFull() const { return ImageBarriers.Count() == VK_BARRIER_BUFFER_SIZE || BufferBarriers.Count() == VK_BARRIER_BUFFER_SIZE; } - inline bool HasBarrier() const + FORCE_INLINE bool HasBarrier() const { return ImageBarriers.Count() + BufferBarriers.Count() != 0; } - void Execute(CmdBufferVulkan* cmdBuffer); + void Execute(const CmdBufferVulkan* cmdBuffer); }; /// @@ -136,7 +120,7 @@ public: return _cmdBufferManager; } - void AddImageBarrier(VkImage image, VkImageLayout srcLayout, VkImageLayout dstLayout, VkImageSubresourceRange& subresourceRange, GPUTextureViewVulkan* handle); + void AddImageBarrier(VkImage image, VkImageLayout srcLayout, VkImageLayout dstLayout, const VkImageSubresourceRange& subresourceRange, GPUTextureViewVulkan* handle); void AddImageBarrier(GPUTextureViewVulkan* handle, VkImageLayout dstLayout); void AddImageBarrier(GPUTextureVulkan* texture, int32 mipSlice, int32 arraySlice, VkImageLayout dstLayout); void AddImageBarrier(GPUTextureVulkan* texture, VkImageLayout dstLayout); @@ -153,7 +137,6 @@ public: private: void UpdateDescriptorSets(const struct SpirvShaderDescriptorInfo& descriptorInfo, class DescriptorSetWriterVulkan& dsWriter, bool& needsWrite); - void UpdateDescriptorSets(GPUPipelineStateVulkan* pipelineState); void UpdateDescriptorSets(ComputePipelineStateVulkan* pipelineState); void OnDrawCall(); diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index f747b42b9..a6a412ce2 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -430,14 +430,10 @@ void DeferredDeletionQueueVulkan::EnqueueGenericResource(Type type, uint64 handl uint32 GetHash(const RenderTargetLayoutVulkan& key) { uint32 hash = (int32)key.MSAA * 11; - CombineHash(hash, (uint32)key.ReadDepth); - CombineHash(hash, (uint32)key.WriteDepth); - CombineHash(hash, (uint32)key.BlendEnable); + CombineHash(hash, key.Flags); CombineHash(hash, (uint32)key.DepthFormat * 93473262); - CombineHash(hash, key.RTsCount * 136); CombineHash(hash, key.Extent.width); CombineHash(hash, key.Extent.height); - CombineHash(hash, key.Layers); for (int32 i = 0; i < ARRAY_COUNT(key.RTVsFormats); i++) CombineHash(hash, (uint32)key.RTVsFormats[i]); return hash; @@ -452,9 +448,9 @@ uint32 GetHash(const FramebufferVulkan::Key& key) return hash; } -FramebufferVulkan::FramebufferVulkan(GPUDeviceVulkan* device, Key& key, VkExtent2D& extent, uint32 layers) - : _device(device) - , _handle(VK_NULL_HANDLE) +FramebufferVulkan::FramebufferVulkan(GPUDeviceVulkan* device, const Key& key, const VkExtent2D& extent, uint32 layers) + : Device(device) + , Handle(VK_NULL_HANDLE) , Extent(extent) , Layers(layers) { @@ -462,18 +458,18 @@ FramebufferVulkan::FramebufferVulkan(GPUDeviceVulkan* device, Key& key, VkExtent VkFramebufferCreateInfo createInfo; RenderToolsVulkan::ZeroStruct(createInfo, VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO); - createInfo.renderPass = key.RenderPass->GetHandle(); + createInfo.renderPass = key.RenderPass->Handle; createInfo.attachmentCount = key.AttachmentCount; createInfo.pAttachments = key.Attachments; createInfo.width = extent.width; createInfo.height = extent.height; createInfo.layers = layers; - VALIDATE_VULKAN_RESULT(vkCreateFramebuffer(device->Device, &createInfo, nullptr, &_handle)); + VALIDATE_VULKAN_RESULT(vkCreateFramebuffer(device->Device, &createInfo, nullptr, &Handle)); } FramebufferVulkan::~FramebufferVulkan() { - _device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::Framebuffer, _handle); + Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::Framebuffer, Handle); } bool FramebufferVulkan::HasReference(VkImageView imageView) const @@ -487,8 +483,8 @@ bool FramebufferVulkan::HasReference(VkImageView imageView) const } RenderPassVulkan::RenderPassVulkan(GPUDeviceVulkan* device, const RenderTargetLayoutVulkan& layout) - : _device(device) - , _handle(VK_NULL_HANDLE) + : Device(device) + , Handle(VK_NULL_HANDLE) , Layout(layout) { const int32 colorAttachmentsCount = layout.RTsCount; @@ -531,23 +527,48 @@ RenderPassVulkan::RenderPassVulkan(GPUDeviceVulkan* device, const RenderTargetLa if (hasDepthStencilAttachment) { VkImageLayout depthStencilLayout; - if (layout.ReadDepth && !layout.WriteDepth) +#if 0 + // TODO: enable extension and use separateDepthStencilLayouts from Vulkan 1.2 + if (layout.ReadStencil || layout.WriteStencil) + { + if (layout.WriteDepth && layout.WriteStencil) + depthStencilLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + else if (layout.WriteDepth && !layout.WriteStencil) + depthStencilLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL; + else if (layout.WriteStencil && !layout.WriteDepth) + depthStencilLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL; + else if (layout.ReadDepth) + depthStencilLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + else + depthStencilLayout = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL; + } + else + { + // Depth-only + if (layout.ReadDepth && !layout.WriteDepth) + depthStencilLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL; + else + depthStencilLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; + } +#else + if ((layout.ReadDepth || layout.ReadStencil) && !(layout.WriteDepth || layout.WriteStencil)) depthStencilLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; else depthStencilLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; +#endif // Use last slot for depth stencil attachment - VkAttachmentDescription& depthAttachment = attachments[colorAttachmentsCount]; - depthAttachment.flags = 0; - depthAttachment.format = RenderToolsVulkan::ToVulkanFormat(layout.DepthFormat); - depthAttachment.samples = (VkSampleCountFlagBits)layout.MSAA; - // TODO: fix those operations for load and store - depthAttachment.loadOp = layout.ReadDepth || true ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE; - depthAttachment.storeOp = layout.WriteDepth || true ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; - depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // TODO: Handle stencil - depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - depthAttachment.initialLayout = depthStencilLayout; - depthAttachment.finalLayout = depthStencilLayout; + VkAttachmentDescription& attachment = attachments[colorAttachmentsCount]; + attachment.flags = 0; + attachment.format = RenderToolsVulkan::ToVulkanFormat(layout.DepthFormat); + attachment.samples = (VkSampleCountFlagBits)layout.MSAA; + attachment.loadOp = layout.ReadDepth || layout.ReadStencil ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE; + //attachment.storeOp = layout.WriteDepth || layout.WriteStencil ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; // For some reason, read-only depth results in artifacts + attachment.stencilLoadOp = layout.ReadStencil ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = layout.WriteStencil ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = depthStencilLayout; + attachment.finalLayout = depthStencilLayout; depthStencilReference.attachment = colorAttachmentsCount; depthStencilReference.layout = depthStencilLayout; subpassDesc.pDepthStencilAttachment = &depthStencilReference; @@ -559,12 +580,15 @@ RenderPassVulkan::RenderPassVulkan(GPUDeviceVulkan* device, const RenderTargetLa createInfo.pAttachments = attachments; createInfo.subpassCount = 1; createInfo.pSubpasses = &subpassDesc; - VALIDATE_VULKAN_RESULT(vkCreateRenderPass(device->Device, &createInfo, nullptr, &_handle)); + VALIDATE_VULKAN_RESULT(vkCreateRenderPass(device->Device, &createInfo, nullptr, &Handle)); +#if VULKAN_USE_DEBUG_DATA + DebugCreateInfo = createInfo; +#endif } RenderPassVulkan::~RenderPassVulkan() { - _device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::RenderPass, _handle); + Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::RenderPass, Handle); } QueryPoolVulkan::QueryPoolVulkan(GPUDeviceVulkan* device, int32 capacity, VkQueryType type) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h index c3664b438..ceed84dc2 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h @@ -173,21 +173,28 @@ private: void EnqueueGenericResource(Type type, uint64 handle, VmaAllocation allocation); }; -class RenderTargetLayoutVulkan +struct RenderTargetLayoutVulkan { -public: - int32 RTsCount; + union + { + struct + { + uint32 Layers : 10; // Limited by GPU_MAX_TEXTURE_ARRAY_SIZE + uint32 RTsCount : 3; // Limited by GPU_MAX_RT_BINDED + uint32 ReadDepth : 1; + uint32 WriteDepth : 1; + uint32 ReadStencil : 1; + uint32 WriteStencil : 1; + uint32 BlendEnable : 1; + }; + uint32 Flags; + }; MSAALevel MSAA; - bool ReadDepth; - bool WriteDepth; - bool BlendEnable; PixelFormat DepthFormat; PixelFormat RTVsFormats[GPU_MAX_RT_BINDED]; VkExtent2D Extent; - uint32 Layers; -public: - bool operator==(const RenderTargetLayoutVulkan& other) const + FORCE_INLINE bool operator==(const RenderTargetLayoutVulkan& other) const { return Platform::MemoryCompare(this, &other, sizeof(RenderTargetLayoutVulkan)) == 0; } @@ -204,32 +211,21 @@ public: int32 AttachmentCount; VkImageView Attachments[GPU_MAX_RT_BINDED + 1]; - public: - bool operator==(const Key& other) const + FORCE_INLINE bool operator==(const Key& other) const { return Platform::MemoryCompare(this, &other, sizeof(Key)) == 0; } }; -private: - GPUDeviceVulkan* _device; - VkFramebuffer _handle; - -public: - FramebufferVulkan(GPUDeviceVulkan* device, Key& key, VkExtent2D& extent, uint32 layers); + FramebufferVulkan(GPUDeviceVulkan* device, const Key& key, const VkExtent2D& extent, uint32 layers); ~FramebufferVulkan(); -public: + GPUDeviceVulkan* Device; + VkFramebuffer Handle; VkImageView Attachments[GPU_MAX_RT_BINDED + 1]; VkExtent2D Extent; uint32 Layers; -public: - inline VkFramebuffer GetHandle() - { - return _handle; - } - bool HasReference(VkImageView imageView) const; }; @@ -237,22 +233,16 @@ uint32 GetHash(const FramebufferVulkan::Key& key); class RenderPassVulkan { -private: - GPUDeviceVulkan* _device; - VkRenderPass _handle; - public: + GPUDeviceVulkan* Device; + VkRenderPass Handle; RenderTargetLayoutVulkan Layout; +#if VULKAN_USE_DEBUG_DATA + VkRenderPassCreateInfo DebugCreateInfo; +#endif -public: RenderPassVulkan(GPUDeviceVulkan* device, const RenderTargetLayoutVulkan& layout); ~RenderPassVulkan(); - -public: - inline VkRenderPass GetHandle() const - { - return _handle; - } }; class QueryPoolVulkan diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp index e2568ce95..67f0ac70b 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp @@ -174,7 +174,7 @@ VkPipeline GPUPipelineStateVulkan::GetState(RenderPassVulkan* renderPass) // Update description to match the pipeline _descColorBlend.attachmentCount = renderPass->Layout.RTsCount; _descMultisample.rasterizationSamples = (VkSampleCountFlagBits)renderPass->Layout.MSAA; - _desc.renderPass = renderPass->GetHandle(); + _desc.renderPass = renderPass->Handle; // Check if has missing layout if (_desc.layout == VK_NULL_HANDLE) @@ -321,6 +321,10 @@ bool GPUPipelineStateVulkan::Init(const Description& desc) _descDepthStencil.front.passOp = ToVulkanStencilOp(desc.StencilPassOp); _descDepthStencil.front = _descDepthStencil.back; _desc.pDepthStencilState = &_descDepthStencil; + DepthReadEnable = desc.DepthEnable && desc.DepthFunc != ComparisonFunc::Always; + DepthWriteEnable = _descDepthStencil.depthWriteEnable; + StencilReadEnable = desc.StencilEnable && desc.StencilReadMask != 0 && desc.StencilFunc != ComparisonFunc::Always; + StencilWriteEnable = desc.StencilEnable && desc.StencilWriteMask != 0; // Rasterization RenderToolsVulkan::ZeroStruct(_descRasterization, VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO); diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h index 392375224..0300380a3 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h @@ -117,7 +117,11 @@ public: /// uint32 UsedStagesMask; - bool BlendEnable; + uint32 BlendEnable : 1; + uint32 DepthReadEnable : 1; + uint32 DepthWriteEnable : 1; + uint32 StencilReadEnable : 1; + uint32 StencilWriteEnable : 1; /// /// The bitmask of stages that have descriptors. @@ -146,42 +150,25 @@ public: TypedDescriptorPoolSetVulkan* CurrentTypedDescriptorPoolSet = nullptr; Array DescriptorSetHandles; + Array DynamicOffsets; + +public: inline bool AcquirePoolSet(CmdBufferVulkan* cmdBuffer) { + // Lazy init + if (!DescriptorSetsLayout) + GetLayout(); + // Pipeline state has no current descriptor pools set or set owner is not current - acquire a new pool set DescriptorPoolSetContainerVulkan* cmdBufferPoolSet = cmdBuffer->GetDescriptorPoolSet(); if (CurrentTypedDescriptorPoolSet == nullptr || CurrentTypedDescriptorPoolSet->GetOwner() != cmdBufferPoolSet) { - ASSERT(cmdBufferPoolSet); CurrentTypedDescriptorPoolSet = cmdBufferPoolSet->AcquireTypedPoolSet(*DescriptorSetsLayout); return true; } - return false; } - inline bool AllocateDescriptorSets() - { - ASSERT(CurrentTypedDescriptorPoolSet); - return CurrentTypedDescriptorPoolSet->AllocateDescriptorSets(*DescriptorSetsLayout, DescriptorSetHandles.Get()); - } - - Array DynamicOffsets; - -public: - void Bind(CmdBufferVulkan* cmdBuffer) - { - vkCmdBindDescriptorSets( - cmdBuffer->GetHandle(), - VK_PIPELINE_BIND_POINT_GRAPHICS, - GetLayout()->GetHandle(), - 0, - DescriptorSetHandles.Count(), - DescriptorSetHandles.Get(), - DynamicOffsets.Count(), - DynamicOffsets.Get()); - } - /// /// Gets the Vulkan pipeline layout for this pipeline state. /// diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp index e754573a8..2cd698430 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp @@ -24,6 +24,10 @@ void GPUTextureViewVulkan::Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* ow Extent.height = Math::Max(1, extent.height >> firstMipIndex); Extent.depth = Math::Max(1, extent.depth >> firstMipIndex); Layers = arraySize; +#if VULKAN_USE_DEBUG_DATA + Format = format; + ReadOnlyDepth = readOnlyDepth; +#endif RenderToolsVulkan::ZeroStruct(Info, VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO); Info.image = image; @@ -56,12 +60,26 @@ void GPUTextureViewVulkan::Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* ow if (PixelFormatExtensions::IsDepthStencil(format)) { range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; +#if 0 + // TODO: enable extension and use separateDepthStencilLayouts from Vulkan 1.2 if (PixelFormatExtensions::HasStencil(format)) { range.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + LayoutRTV = readOnlyDepth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + LayoutSRV = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; } + else + { + LayoutRTV = readOnlyDepth ? VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; + LayoutSRV = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL; + } +#else + + if (PixelFormatExtensions::HasStencil(format)) + range.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; LayoutRTV = readOnlyDepth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - LayoutSRV = readOnlyDepth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + LayoutSRV = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; +#endif } else { @@ -113,13 +131,18 @@ void GPUTextureViewVulkan::Release() { Device->OnImageViewDestroy(ViewFramebuffer); Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, ViewFramebuffer); + ViewFramebuffer = VK_NULL_HANDLE; + } + if (ViewSRV != View && ViewSRV != VK_NULL_HANDLE) + { + Device->OnImageViewDestroy(ViewSRV); + Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, ViewSRV); + ViewSRV = VK_NULL_HANDLE; } Device->OnImageViewDestroy(View); Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, View); - View = VK_NULL_HANDLE; - ViewFramebuffer = VK_NULL_HANDLE; #if BUILD_DEBUG Device = nullptr; @@ -133,15 +156,26 @@ void GPUTextureViewVulkan::DescriptorAsImage(GPUContextVulkan* context, VkImageV { imageView = View; layout = LayoutSRV; - + const VkImageAspectFlags aspectMask = Info.subresourceRange.aspectMask; + if (aspectMask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) + { + // Transition depth-only when binding depth buffer with stencil + if (ViewSRV == VK_NULL_HANDLE) + { + VkImageViewCreateInfo createInfo = Info; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + VALIDATE_VULKAN_RESULT(vkCreateImageView(Device->Device, &createInfo, nullptr, &ViewSRV)); + } + imageView = ViewSRV; + } context->AddImageBarrier(this, LayoutSRV); + Info.subresourceRange.aspectMask = aspectMask; } void GPUTextureViewVulkan::DescriptorAsStorageImage(GPUContextVulkan* context, VkImageView& imageView, VkImageLayout& layout) { imageView = View; layout = VK_IMAGE_LAYOUT_GENERAL; - context->AddImageBarrier(this, VK_IMAGE_LAYOUT_GENERAL); } @@ -152,7 +186,6 @@ bool GPUTextureVulkan::GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, LOG(Warning, "Texture::GetData is valid only for staging resources."); return true; } - GPUDeviceLock lock(_device); // Internally it's a buffer, so adapt resource index and offset @@ -209,7 +242,6 @@ void GPUTextureVulkan::DescriptorAsStorageImage(GPUContextVulkan* context, VkIma ASSERT(_handleUAV.Owner == this); imageView = _handleUAV.View; layout = VK_IMAGE_LAYOUT_GENERAL; - context->AddImageBarrier(this, VK_IMAGE_LAYOUT_GENERAL); } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h index 6180e08f0..808623adb 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h @@ -48,12 +48,17 @@ public: VkImage Image = VK_NULL_HANDLE; VkImageView View = VK_NULL_HANDLE; VkImageView ViewFramebuffer = VK_NULL_HANDLE; + VkImageView ViewSRV = VK_NULL_HANDLE; VkExtent3D Extent; uint32 Layers; VkImageViewCreateInfo Info; int32 SubresourceIndex; VkImageLayout LayoutRTV; VkImageLayout LayoutSRV; +#if VULKAN_USE_DEBUG_DATA + PixelFormat Format; + bool ReadOnlyDepth; +#endif public: void Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* owner, VkImage image, int32 totalMipLevels, PixelFormat format, MSAALevel msaa, VkExtent3D extent, VkImageViewType viewType, int32 mipLevels = 1, int32 firstMipIndex = 0, int32 arraySize = 1, int32 firstArraySlice = 0, bool readOnlyDepth = false); diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h index 2f7eb88ee..d97f24340 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h @@ -77,12 +77,10 @@ public: case VK_ACCESS_SHADER_WRITE_BIT: stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; -#if VK_KHR_maintenance2 case VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT: case VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT: stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break; -#endif default: CRASH; break; @@ -108,7 +106,9 @@ public: stageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - accessFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL: + case VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL: + accessFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: @@ -124,15 +124,16 @@ public: stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL: - accessFlags = VK_ACCESS_SHADER_READ_BIT; - stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL: + case VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL: + accessFlags = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; + stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break; -#if VK_KHR_maintenance2 - case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR: - accessFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL: + case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL: + accessFlags = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break; -#endif case VK_IMAGE_LAYOUT_GENERAL: accessFlags = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; @@ -150,7 +151,7 @@ public: static_assert(!TIsPointer::Value, "Don't use a pointer."); static_assert(OFFSET_OF(T, sType) == 0, "Assumes type is the first member in the Vulkan type."); data.sType = type; - Platform::MemoryClear(((uint8*)&data) + sizeof(VkStructureType), sizeof(T) - sizeof(VkStructureType)); + Platform::MemoryClear((uint8*)&data + sizeof(VkStructureType), sizeof(T) - sizeof(VkStructureType)); } /// diff --git a/Source/Engine/Renderer/MotionBlurPass.cpp b/Source/Engine/Renderer/MotionBlurPass.cpp index 2030027ed..523c31c6e 100644 --- a/Source/Engine/Renderer/MotionBlurPass.cpp +++ b/Source/Engine/Renderer/MotionBlurPass.cpp @@ -151,7 +151,6 @@ void MotionBlurPass::Dispose() void MotionBlurPass::RenderMotionVectors(RenderContext& renderContext) { - // Prepare auto motionVectors = renderContext.Buffers->MotionVectors; ASSERT(motionVectors); MotionBlurSettings& settings = renderContext.List->Settings.MotionBlur; @@ -160,8 +159,6 @@ void MotionBlurPass::RenderMotionVectors(RenderContext& renderContext) const int32 screenHeight = renderContext.Buffers->GetHeight(); const int32 motionVectorsWidth = screenWidth / static_cast(settings.MotionVectorsResolution); const int32 motionVectorsHeight = screenHeight / static_cast(settings.MotionVectorsResolution); - - // Ensure to have valid data if (!renderContext.List->Setup.UseMotionVectors || checkIfSkipPass()) { // Skip pass (just clear motion vectors if texture is allocated) diff --git a/Source/Engine/Renderer/ReflectionsPass.cpp b/Source/Engine/Renderer/ReflectionsPass.cpp index d419742b4..9897240ac 100644 --- a/Source/Engine/Renderer/ReflectionsPass.cpp +++ b/Source/Engine/Renderer/ReflectionsPass.cpp @@ -356,15 +356,11 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light { auto device = GPUDevice::Instance; auto context = device->GetMainContext(); - - // Skip pass if resources aren't ready if (checkIfSkipPass()) { + // Skip pass (just clear buffer when doing debug preview) if (renderContext.View.Mode == ViewMode::Reflections) - { context->Clear(lightBuffer, Color::Black); - } - return; } From 7d4004a42aefb6aa7be807284b66e3064ccca228 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 2 Feb 2024 14:54:50 +0100 Subject: [PATCH 083/198] Tweak shadows rendering --- Source/Engine/Renderer/ShadowsPass.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index 937cd9e99..a4c69c8af 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -83,6 +83,7 @@ bool ShadowsPass::Init() // Select format for shadow maps _shadowMapFormat = PixelFormat::Unknown; +#if !PLATFORM_SWITCH // TODO: fix shadows performance issue on Switch for (const PixelFormat format : { PixelFormat::D16_UNorm, PixelFormat::D24_UNorm_S8_UInt, PixelFormat::D32_Float }) { const auto formatTexture = PixelFormatExtensions::FindShaderResourceFormat(format, false); @@ -95,6 +96,7 @@ bool ShadowsPass::Init() break; } } +#endif if (_shadowMapFormat == PixelFormat::Unknown) LOG(Warning, "GPU doesn't support shadows rendering"); @@ -229,6 +231,9 @@ void ShadowsPass::SetupLight(RenderContext& renderContext, RenderContextBatch& r #if USE_EDITOR if (IsRunningRadiancePass) blendCSM = false; +#elif PLATFORM_SWITCH || PLATFORM_IOS || PLATFORM_ANDROID + // Disable cascades blending on low-end platforms + blendCSM = false; #endif // Views with orthographic cameras cannot use cascades, we force it to 1 shadow map here From 969d0390cee7651a2799ae9df1880c33f515fb02 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 5 Feb 2024 09:27:28 +0100 Subject: [PATCH 084/198] Add auto focus to Editor Window when nothing clicked inside it --- Source/Editor/Windows/EditorWindow.cs | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Source/Editor/Windows/EditorWindow.cs b/Source/Editor/Windows/EditorWindow.cs index ff0673c11..445287cab 100644 --- a/Source/Editor/Windows/EditorWindow.cs +++ b/Source/Editor/Windows/EditorWindow.cs @@ -14,6 +14,8 @@ namespace FlaxEditor.Windows /// public abstract class EditorWindow : DockWindow { + private bool _mouseDown; + /// /// Gets the editor object. /// @@ -223,6 +225,41 @@ namespace FlaxEditor.Windows return false; } + /// + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (base.OnMouseDown(location, button)) + return true; + if (button == MouseButton.Left) + { + _mouseDown = true; + return true; + } + return false; + } + + /// + public override bool OnMouseUp(Float2 location, MouseButton button) + { + if (base.OnMouseUp(location, button)) + return true; + if (button == MouseButton.Left && _mouseDown) + { + _mouseDown = false; + Focus(); + return true; + } + return false; + } + + /// + public override void OnMouseLeave() + { + _mouseDown = false; + + base.OnMouseLeave(); + } + /// public override void OnDestroy() { From b69d54a5ca620f87eaacb71c7b5fd87e26da91a8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 5 Feb 2024 16:03:31 +0100 Subject: [PATCH 085/198] Add `AudioBackendTools` for sharing spatial audio impl for consoles --- Source/Engine/Audio/AudioBackendTools.h | 128 ++++++++++++++++++ .../Audio/XAudio2/AudioBackendXAudio2.cpp | 2 +- 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 Source/Engine/Audio/AudioBackendTools.h diff --git a/Source/Engine/Audio/AudioBackendTools.h b/Source/Engine/Audio/AudioBackendTools.h new file mode 100644 index 000000000..3229490ed --- /dev/null +++ b/Source/Engine/Audio/AudioBackendTools.h @@ -0,0 +1,128 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/Math/Transform.h" + +/// +/// The helper class for that handles active audio backend operations. +/// +class AudioBackendTools +{ +public: + struct Settings + { + float Volume = 1.0f; + float DopplerFactor = 1.0f; + }; + + struct Listener + { + Vector3 Velocity; + Vector3 Position; + Quaternion Orientation; + }; + + struct Source + { + bool Is3D; + float Volume; + float Pitch; + float Pan; + float MinDistance; + float Attenuation; + float DopplerFactor; + Vector3 Velocity; + Vector3 Position; + Quaternion Orientation; + }; + + enum class Channels + { + FrontLeft = 0, + FrontRight = 1, + Center = 2, + BackLeft = 3, + BackRight = 4, + SideLeft = 5, + SideRight = 6, + MAX + }; + + struct SoundMix + { + float Pitch; + float Volume; + float Channels[(int32)Channels::MAX]; + }; + + static SoundMix CalculateSoundMix(const Settings& settings, const Listener& listener, const Source& source, int32 channelCount = 2) + { + SoundMix mix; + mix.Pitch = source.Pitch; + mix.Volume = source.Volume * settings.Volume; + if (source.Is3D) + { + const Transform listenerTransform(listener.Position, listener.Orientation); + float distance = (float)Vector3::Distance(listener.Position, source.Position); + float gain = 1; + + // Calculate attenuation (OpenAL formula for mode: AL_INVERSE_DISTANCE_CLAMPED) + // [https://www.openal.org/documentation/openal-1.1-specification.pdf] + distance = Math::Clamp(distance, source.MinDistance, MAX_float); + const float dst = source.MinDistance + source.Attenuation * (distance - source.MinDistance); + if (dst > 0) + gain = source.MinDistance / dst; + mix.Volume *= Math::Saturate(gain); + + // Calculate panning + // Ramy Sadek and Chris Kyriakakis, 2004, "A Novel Multichannel Panning Method for Standard and Arbitrary Loudspeaker Configurations" + // [https://www.researchgate.net/publication/235080603_A_Novel_Multichannel_Panning_Method_for_Standard_and_Arbitrary_Loudspeaker_Configurations] + //TODO: hardcoded main speaker directions for 2, 3.1, 5.1 and 7.1 setups - these are simplified and could also be made configurable + static const Float3 ChannelDirections[(int32)Channels::MAX] = + { + Float3(-1.0, 0.0, -1.0).GetNormalized(), + Float3(1.0, 0.0, -1.0).GetNormalized(), + Float3(0.0, 0.0, -1.0).GetNormalized(), + Float3(-1.0, 0.0, 1.0).GetNormalized(), + Float3(1.0, 0.0, 1.0).GetNormalized(), + Float3(-1.0, 0.0, 0.0).GetNormalized(), + Float3(1.0, 0.0, 0.0).GetNormalized(), + }; + const Float3 sourceInListenerSpace = (Float3)listenerTransform.WorldToLocal(source.Position); + const Float3 sourceToListener = Float3::Normalize(sourceInListenerSpace); + float sqGainsSum = 0.0f; + for (int32 i = 0; i < channelCount; i++) + { + float othersSum = 0.0f; + for (int32 j = 0; j < channelCount; j++) + othersSum += (1.0f + Float3::Dot(ChannelDirections[i], ChannelDirections[j])) * 0.5f; + const float sqGain = Math::Square(0.5f * Math::Pow(1.0f + Float3::Dot(ChannelDirections[i], sourceToListener), 2.0f) / othersSum); + sqGainsSum += sqGain; + mix.Channels[i] = sqGain; + } + for (int32 i = 0; i < channelCount; i++) + mix.Channels[i] = Math::Sqrt(mix.Channels[i] / sqGainsSum); + + // Calculate doppler + const Float3 velocityInListenerSpace = (Float3)listenerTransform.WorldToLocalVector(source.Velocity - listener.Velocity); + const float velocity = velocityInListenerSpace.Length(); + const float dopplerFactor = settings.DopplerFactor * source.DopplerFactor; + if (dopplerFactor > 0.0f && velocity > 0.0f) + { + constexpr float speedOfSound = 343.3f * 100.0f * 100.0f; // in air, in Flax units + const float approachingFactor = Float3::Dot(sourceToListener, velocityInListenerSpace.GetNormalized()); + const float dopplerPitch = speedOfSound / (speedOfSound + velocity * approachingFactor); + mix.Pitch *= Math::Clamp(dopplerPitch, 0.1f, 10.0f); + } + } + else + { + mix.Channels[(int32)Channels::FrontLeft] = Math::Min(1.0f - source.Pan, 1.0f); + mix.Channels[(int32)Channels::FrontRight] = Math::Min(1.0f + source.Pan, 1.0f); + for (int32 i = 2; i < channelCount; i++) + mix.Channels[i] = 0.0f; + } + return mix; + } +}; diff --git a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp index 3092a2229..dc3a7e7ad 100644 --- a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp +++ b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp @@ -387,7 +387,7 @@ void AudioBackendXAudio2::Source_OnAdd(AudioSource* source) const auto& header = clip->AudioHeader; auto& format = aSource->Format; format.wFormatTag = WAVE_FORMAT_PCM; - format.nChannels = clip->Is3D() ? 1 : header.Info.NumChannels; // 3d audio is always mono (AudioClip auto-converts before buffer write) + format.nChannels = clip->Is3D() ? 1 : header.Info.NumChannels; // 3d audio is always mono (AudioClip auto-converts before buffer write if FeatureFlags::SpatialMultiChannel is unset) format.nSamplesPerSec = header.Info.SampleRate; format.wBitsPerSample = header.Info.BitDepth; format.nBlockAlign = (WORD)(format.nChannels * (format.wBitsPerSample / 8)); From 1d4bb7a4f4f57f3fe9927e5041afa1dd2758d6e2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 5 Feb 2024 16:30:58 +0100 Subject: [PATCH 086/198] Remove unused doc comment --- Source/Engine/Audio/AudioBackendTools.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Audio/AudioBackendTools.h b/Source/Engine/Audio/AudioBackendTools.h index 3229490ed..019fc127d 100644 --- a/Source/Engine/Audio/AudioBackendTools.h +++ b/Source/Engine/Audio/AudioBackendTools.h @@ -78,7 +78,6 @@ public: // Calculate panning // Ramy Sadek and Chris Kyriakakis, 2004, "A Novel Multichannel Panning Method for Standard and Arbitrary Loudspeaker Configurations" // [https://www.researchgate.net/publication/235080603_A_Novel_Multichannel_Panning_Method_for_Standard_and_Arbitrary_Loudspeaker_Configurations] - //TODO: hardcoded main speaker directions for 2, 3.1, 5.1 and 7.1 setups - these are simplified and could also be made configurable static const Float3 ChannelDirections[(int32)Channels::MAX] = { Float3(-1.0, 0.0, -1.0).GetNormalized(), From d4a483e6560e4a082c93a5befe2da1460343e6e4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 5 Feb 2024 18:42:09 +0100 Subject: [PATCH 087/198] Add missing check for `FormatSupport::TextureCube` for shadowmap --- Source/Engine/Renderer/ShadowsPass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index a4c69c8af..19a91eeae 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -89,7 +89,7 @@ bool ShadowsPass::Init() const auto formatTexture = PixelFormatExtensions::FindShaderResourceFormat(format, false); const auto formatFeaturesDepth = GPUDevice::Instance->GetFormatFeatures(format); const auto formatFeaturesTexture = GPUDevice::Instance->GetFormatFeatures(formatTexture); - if (EnumHasAllFlags(formatFeaturesDepth.Support, FormatSupport::DepthStencil | FormatSupport::Texture2D) && + if (EnumHasAllFlags(formatFeaturesDepth.Support, FormatSupport::DepthStencil | FormatSupport::Texture2D | FormatSupport::TextureCube) && EnumHasAllFlags(formatFeaturesTexture.Support, FormatSupport::ShaderSample | FormatSupport::ShaderSampleComparison)) { _shadowMapFormat = format; From 5b8846c8f0df98c24306640a0c316c20147e7555 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 5 Feb 2024 18:45:01 +0100 Subject: [PATCH 088/198] Update Vulkan backend --- Source/Engine/GraphicsDevice/Vulkan/Config.h | 5 +-- .../Vulkan/GPUDeviceVulkan.Layers.cpp | 18 +++-------- .../GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp | 31 +++++++------------ .../GraphicsDevice/Vulkan/GPUDeviceVulkan.h | 2 -- .../Vulkan/IncludeVulkanHeaders.h | 1 - .../Vulkan/Linux/LinuxVulkanPlatform.h | 2 -- .../Vulkan/RenderToolsVulkan.cpp | 11 +++++++ .../GraphicsDevice/Vulkan/RenderToolsVulkan.h | 2 ++ .../Vulkan/Win32/Win32VulkanPlatform.h | 1 - 9 files changed, 29 insertions(+), 44 deletions(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/Config.h b/Source/Engine/GraphicsDevice/Vulkan/Config.h index ade8d96be..71245070b 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Config.h +++ b/Source/Engine/GraphicsDevice/Vulkan/Config.h @@ -18,7 +18,7 @@ #ifndef VULKAN_BACK_BUFFERS_COUNT #define VULKAN_BACK_BUFFERS_COUNT 2 #endif -#define VULKAN_BACK_BUFFERS_COUNT_MAX 16 +#define VULKAN_BACK_BUFFERS_COUNT_MAX 4 /// /// Default amount of frames to wait until resource delete. @@ -34,8 +34,5 @@ #ifndef VULKAN_USE_QUERIES #define VULKAN_USE_QUERIES 1 #endif -#ifndef VULKAN_HAS_PHYSICAL_DEVICE_PROPERTIES2 -#define VULKAN_HAS_PHYSICAL_DEVICE_PROPERTIES2 0 -#endif #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp index a69187f6f..9cd6f6b47 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp @@ -575,25 +575,15 @@ void GPUDeviceVulkan::GetDeviceExtensionsAndLayers(VkPhysicalDevice gpu, Array& deviceExtensions) { Platform::MemoryClear(&OptionalDeviceExtensions, sizeof(OptionalDeviceExtensions)); - - const auto HasExtension = [&deviceExtensions](const char* name) -> bool - { - const Function CheckCallback = [&name](const char* const& extension) -> bool - { - return StringUtils::Compare(extension, name) == 0; - }; - return ArrayExtensions::Any(deviceExtensions, CheckCallback); - }; - #if VK_KHR_maintenance1 - OptionalDeviceExtensions.HasKHRMaintenance1 = HasExtension(VK_KHR_MAINTENANCE1_EXTENSION_NAME); + OptionalDeviceExtensions.HasKHRMaintenance1 = RenderToolsVulkan::HasExtension(deviceExtensions, VK_KHR_MAINTENANCE1_EXTENSION_NAME); #endif #if VK_KHR_maintenance2 - OptionalDeviceExtensions.HasKHRMaintenance2 = HasExtension(VK_KHR_MAINTENANCE2_EXTENSION_NAME); + OptionalDeviceExtensions.HasKHRMaintenance2 = RenderToolsVulkan::HasExtension(deviceExtensions, VK_KHR_MAINTENANCE2_EXTENSION_NAME); #endif - OptionalDeviceExtensions.HasMirrorClampToEdge = HasExtension(VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME); + OptionalDeviceExtensions.HasMirrorClampToEdge = RenderToolsVulkan::HasExtension(deviceExtensions, VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME); #if VK_EXT_validation_cache - OptionalDeviceExtensions.HasEXTValidationCache = HasExtension(VK_EXT_VALIDATION_CACHE_EXTENSION_NAME); + OptionalDeviceExtensions.HasEXTValidationCache = RenderToolsVulkan::HasExtension(deviceExtensions, VK_EXT_VALIDATION_CACHE_EXTENSION_NAME); #endif } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index a6a412ce2..826efbd6c 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -998,11 +998,10 @@ void StagingManagerVulkan::ProcessPendingFree() } // Free staging buffers that has not been used for a few frames - const uint64 SafeFramesCount = 30; for (int32 i = _freeBuffers.Count() - 1; i >= 0; i--) { - auto& e = _freeBuffers[i]; - if (e.FrameNumber + SafeFramesCount < Engine::FrameCount) + auto& e = _freeBuffers.Get()[i]; + if (e.FrameNumber + VULKAN_RESOURCE_DELETE_SAFE_FRAMES_COUNT < Engine::FrameCount) { auto buffer = e.Buffer; @@ -1027,7 +1026,7 @@ void StagingManagerVulkan::Dispose() { ScopeLock lock(_locker); -#if !BUILD_RELEASE +#if BUILD_DEBUG LOG(Info, "Vulkan staging buffers peek memory usage: {0}, allocs: {1}, frees: {2}", Utilities::BytesToText(_allBuffersPeekSize), Utilities::BytesToText(_allBuffersAllocSize), Utilities::BytesToText(_allBuffersFreeSize)); #endif @@ -1078,7 +1077,7 @@ GPUDevice* GPUDeviceVulkan::Create() #endif // Engine registration - const StringAsANSI<256> appName(*Globals::ProductName); + const StringAsANSI<> appName(*Globals::ProductName); VkApplicationInfo appInfo; RenderToolsVulkan::ZeroStruct(appInfo, VK_STRUCTURE_TYPE_APPLICATION_INFO); appInfo.pApplicationName = appName.Get(); @@ -1093,24 +1092,13 @@ GPUDevice* GPUDeviceVulkan::Create() instInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; #endif instInfo.pApplicationInfo = &appInfo; - GetInstanceLayersAndExtensions(InstanceExtensions, InstanceLayers, SupportsDebugUtilsExt); - - const auto hasExtension = [](const Array& extensions, const char* name) -> bool - { - const Function callback = [&name](const char* const& extension) -> bool - { - return extension && StringUtils::Compare(extension, name) == 0; - }; - return ArrayExtensions::Any(extensions, callback); - }; - instInfo.enabledExtensionCount = InstanceExtensions.Count(); instInfo.ppEnabledExtensionNames = instInfo.enabledExtensionCount > 0 ? static_cast(InstanceExtensions.Get()) : nullptr; instInfo.enabledLayerCount = InstanceLayers.Count(); instInfo.ppEnabledLayerNames = instInfo.enabledLayerCount > 0 ? InstanceLayers.Get() : nullptr; #if VULKAN_USE_DEBUG_LAYER - SupportsDebugCallbackExt = !SupportsDebugUtilsExt && hasExtension(InstanceExtensions, VK_EXT_DEBUG_REPORT_EXTENSION_NAME); + SupportsDebugCallbackExt = !SupportsDebugUtilsExt && RenderToolsVulkan::HasExtension(InstanceExtensions, VK_EXT_DEBUG_REPORT_EXTENSION_NAME); #endif // Create Vulkan instance @@ -1658,9 +1646,7 @@ bool GPUDeviceVulkan::Init() queue.pQueuePriorities = currentPriority; const VkQueueFamilyProperties& properties = QueueFamilyProps[queue.queueFamilyIndex]; for (int32 queueIndex = 0; queueIndex < (int32)properties.queueCount; queueIndex++) - { *currentPriority++ = 1.0f; - } } deviceInfo.queueCreateInfoCount = queueFamilyInfos.Count(); deviceInfo.pQueueCreateInfos = queueFamilyInfos.Get(); @@ -1847,12 +1833,17 @@ bool GPUDeviceVulkan::Init() INIT_FUNC(vkDestroyImage); INIT_FUNC(vkCmdCopyBuffer); #if VMA_DEDICATED_ALLOCATION +#if PLATFORM_SWITCH + vulkanFunctions.vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2; + vulkanFunctions.vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2; +#else INIT_FUNC(vkGetBufferMemoryRequirements2KHR); INIT_FUNC(vkGetImageMemoryRequirements2KHR); #endif +#endif #undef INIT_FUNC VmaAllocatorCreateInfo allocatorInfo = {}; - allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_0; + allocatorInfo.vulkanApiVersion = VULKAN_API_VERSION; allocatorInfo.physicalDevice = gpu; allocatorInfo.instance = Instance; allocatorInfo.device = Device; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h index ceed84dc2..b177ecf2b 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h @@ -400,8 +400,6 @@ public: uint32 HasKHRMaintenance1 : 1; uint32 HasKHRMaintenance2 : 1; uint32 HasMirrorClampToEdge : 1; - uint32 HasKHRExternalMemoryCapabilities : 1; - uint32 HasKHRGetPhysicalDeviceProperties2 : 1; uint32 HasEXTValidationCache : 1; }; diff --git a/Source/Engine/GraphicsDevice/Vulkan/IncludeVulkanHeaders.h b/Source/Engine/GraphicsDevice/Vulkan/IncludeVulkanHeaders.h index 4e2694aab..882e1ba0e 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/IncludeVulkanHeaders.h +++ b/Source/Engine/GraphicsDevice/Vulkan/IncludeVulkanHeaders.h @@ -14,7 +14,6 @@ #include #undef VK_EXT_debug_utils #undef VK_EXT_validation_cache -#define VMA_DEDICATED_ALLOCATION 0 #pragma clang diagnostic ignored "-Wpointer-bool-conversion" #pragma clang diagnostic ignored "-Wtautological-pointer-compare" diff --git a/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h b/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h index 6da593da4..9a564237c 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h +++ b/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h @@ -6,8 +6,6 @@ #if GRAPHICS_API_VULKAN && PLATFORM_LINUX -#define VULKAN_HAS_PHYSICAL_DEVICE_PROPERTIES2 1 - /// /// The implementation for the Vulkan API support for Linux platform. /// diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp index be801869b..185c3632c 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp @@ -277,4 +277,15 @@ void RenderToolsVulkan::LogVkResult(VkResult result) LogVkResult(result, "", 0); } +bool RenderToolsVulkan::HasExtension(const Array& extensions, const char* name) +{ + for (int32 i = 0; i < extensions.Count(); i++) + { + const char* extension = extensions[i]; + if (extension && StringUtils::Compare(extension, name) == 0) + return true; + } + return false; +} + #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h index d97f24340..bdb4ca654 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h @@ -307,6 +307,8 @@ public: } return result; } + + static bool HasExtension(const Array& extensions, const char* name); }; #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/Win32/Win32VulkanPlatform.h b/Source/Engine/GraphicsDevice/Vulkan/Win32/Win32VulkanPlatform.h index 34bcfaff1..f194a8300 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Win32/Win32VulkanPlatform.h +++ b/Source/Engine/GraphicsDevice/Vulkan/Win32/Win32VulkanPlatform.h @@ -8,7 +8,6 @@ #define VULKAN_USE_PLATFORM_WIN32_KHR 1 #define VULKAN_USE_PLATFORM_WIN32_KHX 1 -#define VULKAN_HAS_PHYSICAL_DEVICE_PROPERTIES2 1 #define VULKAN_USE_CREATE_WIN32_SURFACE 1 /// From 9f279d4b5e34b322239a0aec83402cb33343b4d9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 7 Feb 2024 09:58:31 +0100 Subject: [PATCH 089/198] Add hiding various material properties based on the material domain --- .../Editor/Windows/Assets/MaterialWindow.cs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Source/Editor/Windows/Assets/MaterialWindow.cs b/Source/Editor/Windows/Assets/MaterialWindow.cs index 5aa77dbc3..b8870455f 100644 --- a/Source/Editor/Windows/Assets/MaterialWindow.cs +++ b/Source/Editor/Windows/Assets/MaterialWindow.cs @@ -76,37 +76,37 @@ namespace FlaxEditor.Windows.Assets // Transparency - [EditorOrder(200), DefaultValue(MaterialTransparentLightingMode.Surface), EditorDisplay("Transparency"), Tooltip("Transparent material lighting mode.")] + [EditorOrder(200), DefaultValue(MaterialTransparentLightingMode.Surface), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Transparent material lighting mode.")] public MaterialTransparentLightingMode TransparentLightingMode; - [EditorOrder(205), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables reflections when rendering material.")] + [EditorOrder(205), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables reflections when rendering material.")] public bool EnableReflections; [VisibleIf(nameof(EnableReflections))] - [EditorOrder(210), DefaultValue(false), EditorDisplay("Transparency"), Tooltip("Enables Screen Space Reflections when rendering material.")] + [EditorOrder(210), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables Screen Space Reflections when rendering material.")] public bool EnableScreenSpaceReflections; - [EditorOrder(210), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables fog effects when rendering material.")] + [EditorOrder(210), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables fog effects when rendering material.")] public bool EnableFog; - [EditorOrder(220), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables distortion effect when rendering.")] + [EditorOrder(220), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables distortion effect when rendering.")] public bool EnableDistortion; - [EditorOrder(224), DefaultValue(false), EditorDisplay("Transparency"), Tooltip("Enables sampling Global Illumination in material (eg. light probes or volumetric lightmap).")] + [EditorOrder(224), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables sampling Global Illumination in material (eg. light probes or volumetric lightmap).")] public bool EnableGlobalIllumination; - [EditorOrder(225), DefaultValue(false), EditorDisplay("Transparency"), Tooltip("Enables refraction offset based on the difference between the per-pixel normal and the per-vertex normal. Useful for large water-like surfaces.")] + [EditorOrder(225), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables refraction offset based on the difference between the per-pixel normal and the per-vertex normal. Useful for large water-like surfaces.")] public bool PixelNormalOffsetRefraction; - [EditorOrder(230), DefaultValue(0.12f), EditorDisplay("Transparency"), Tooltip("Controls opacity values clipping point."), Limit(0.0f, 1.0f, 0.01f)] + [EditorOrder(230), DefaultValue(0.12f), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Controls opacity values clipping point."), Limit(0.0f, 1.0f, 0.01f)] public float OpacityThreshold; // Tessellation - [EditorOrder(300), DefaultValue(TessellationMethod.None), EditorDisplay("Tessellation"), Tooltip("Mesh tessellation method.")] + [EditorOrder(300), DefaultValue(TessellationMethod.None), VisibleIf(nameof(IsStandard)), EditorDisplay("Tessellation"), Tooltip("Mesh tessellation method.")] public TessellationMethod TessellationMode; - [EditorOrder(310), DefaultValue(15), EditorDisplay("Tessellation"), Tooltip("Maximum triangle tessellation factor."), Limit(1, 60, 0.01f)] + [EditorOrder(310), DefaultValue(15), VisibleIf(nameof(IsStandard)), EditorDisplay("Tessellation"), Tooltip("Maximum triangle tessellation factor."), Limit(1, 60, 0.01f)] public int MaxTessellationFactor; // Misc @@ -120,10 +120,10 @@ namespace FlaxEditor.Windows.Assets [EditorOrder(420), DefaultValue(0.3f), EditorDisplay("Misc"), Tooltip("Controls mask values clipping point."), Limit(0.0f, 1.0f, 0.01f)] public float MaskThreshold; - [EditorOrder(430), DefaultValue(MaterialDecalBlendingMode.Translucent), EditorDisplay("Misc"), Tooltip("The decal material blending mode.")] + [EditorOrder(430), DefaultValue(MaterialDecalBlendingMode.Translucent), VisibleIf(nameof(IsDecal)), EditorDisplay("Misc"), Tooltip("The decal material blending mode.")] public MaterialDecalBlendingMode DecalBlendingMode; - [EditorOrder(440), DefaultValue(MaterialPostFxLocation.AfterPostProcessingPass), EditorDisplay("Misc"), Tooltip("The post fx material rendering location.")] + [EditorOrder(440), DefaultValue(MaterialPostFxLocation.AfterPostProcessingPass), VisibleIf(nameof(IsPostProcess)), EditorDisplay("Misc"), Tooltip("The post fx material rendering location.")] public MaterialPostFxLocation PostFxLocation; // Parameters @@ -140,6 +140,12 @@ namespace FlaxEditor.Windows.Assets set => throw new Exception("No setter."); } + // Visibility conditionals + + private bool IsPostProcess => Domain == MaterialDomain.PostProcess; + private bool IsDecal => Domain == MaterialDomain.Decal; + private bool IsStandard => Domain == MaterialDomain.Surface || Domain == MaterialDomain.Terrain || Domain == MaterialDomain.Particle || Domain == MaterialDomain.Deformable; + /// /// Gathers parameters from the specified material. /// From 17361d6bc1bbb90b50a30a6e46ae2b931f06d56b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 7 Feb 2024 12:16:11 +0100 Subject: [PATCH 090/198] Revert some VUlkan backend changes to fix regression on Metal devices --- .../GraphicsDevice/Vulkan/CmdBufferVulkan.cpp | 6 +- .../GraphicsDevice/Vulkan/CmdBufferVulkan.h | 6 -- Source/Engine/GraphicsDevice/Vulkan/Config.h | 2 +- .../Vulkan/DescriptorSetVulkan.cpp | 73 +++++++------------ .../Vulkan/DescriptorSetVulkan.h | 38 +++------- .../Vulkan/GPUContextVulkan.cpp | 6 +- .../Vulkan/GPUPipelineStateVulkan.cpp | 8 +- .../Vulkan/GPUPipelineStateVulkan.h | 2 +- .../GraphicsDevice/Vulkan/QueueVulkan.cpp | 1 - 9 files changed, 49 insertions(+), 93 deletions(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp index ee5fbc1dc..690456aa8 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp @@ -211,9 +211,7 @@ CmdBufferPoolVulkan::CmdBufferPoolVulkan(GPUDeviceVulkan* device) CmdBufferPoolVulkan::~CmdBufferPoolVulkan() { for (int32 i = 0; i < _cmdBuffers.Count(); i++) - { - Delete(_cmdBuffers.Get()[i]); - } + Delete(_cmdBuffers[i]); vkDestroyCommandPool(_device->Device, _handle, nullptr); } @@ -221,7 +219,7 @@ void CmdBufferPoolVulkan::RefreshFenceStatus(const CmdBufferVulkan* skipCmdBuffe { for (int32 i = 0; i < _cmdBuffers.Count(); i++) { - const auto cmdBuffer = _cmdBuffers.Get()[i]; + auto cmdBuffer = _cmdBuffers[i]; if (cmdBuffer != skipCmdBuffer) { cmdBuffer->RefreshFenceStatus(); diff --git a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h index c1b694ec1..c7a68dae8 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.h @@ -56,7 +56,6 @@ private: public: CmdBufferVulkan(GPUDeviceVulkan* device, CmdBufferPoolVulkan* pool); - ~CmdBufferVulkan(); public: @@ -140,7 +139,6 @@ public: class CmdBufferPoolVulkan { friend class CmdBufferManagerVulkan; - private: GPUDeviceVulkan* _device; VkCommandPool _handle; @@ -157,7 +155,6 @@ public: public: inline VkCommandPool GetHandle() const { - ASSERT(_handle != VK_NULL_HANDLE); return _handle; } @@ -206,14 +203,11 @@ public: public: void SubmitActiveCmdBuffer(SemaphoreVulkan* signalSemaphore = nullptr); - void WaitForCmdBuffer(CmdBufferVulkan* cmdBuffer, float timeInSecondsToWait = 1.0f); - void RefreshFenceStatus(CmdBufferVulkan* skipCmdBuffer = nullptr) { _pool.RefreshFenceStatus(skipCmdBuffer); } - void PrepareForNewActiveCommandBuffer(); void OnQueryBegin(GPUTimerQueryVulkan* query); diff --git a/Source/Engine/GraphicsDevice/Vulkan/Config.h b/Source/Engine/GraphicsDevice/Vulkan/Config.h index 71245070b..c93951af2 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Config.h +++ b/Source/Engine/GraphicsDevice/Vulkan/Config.h @@ -27,7 +27,7 @@ #define VULKAN_ENABLE_API_DUMP 0 #define VULKAN_RESET_QUERY_POOLS 0 -#define VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID 1 +#define VULKAN_HASH_POOLS_WITH_LAYOUT_TYPES 1 #define VULKAN_USE_DEBUG_LAYER GPU_ENABLE_DIAGNOSTICS #define VULKAN_USE_DEBUG_DATA (GPU_ENABLE_DIAGNOSTICS && COMPILE_WITH_DEV_ENV) diff --git a/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.cpp index ebede5354..885ddb5a6 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.cpp @@ -30,7 +30,7 @@ void DescriptorSetLayoutInfoVulkan::AddBindingsForStage(VkShaderStageFlagBits st binding.descriptorCount = descriptor.Count; LayoutTypes[binding.descriptorType]++; - descSetLayout.Add(binding); + descSetLayout.LayoutBindings.Add(binding); Hash = Crc::MemCrc32(&binding, sizeof(binding), Hash); } } @@ -39,19 +39,18 @@ bool DescriptorSetLayoutInfoVulkan::operator==(const DescriptorSetLayoutInfoVulk { if (other.SetLayouts.Count() != SetLayouts.Count()) return false; - if (other.TypesUsageID != TypesUsageID) +#if VULKAN_HASH_POOLS_WITH_LAYOUT_TYPES + if (other.SetLayoutsHash != SetLayoutsHash) return false; - +#endif for (int32 index = 0; index < other.SetLayouts.Count(); index++) { - auto& setLayout = SetLayouts[index]; - auto& setLayoutOther = other.SetLayouts[index]; - if (setLayoutOther.Count() != setLayout.Count()) + const int32 bindingsCount = SetLayouts[index].LayoutBindings.Count(); + if (other.SetLayouts[index].LayoutBindings.Count() != bindingsCount) return false; - if (setLayout.Count() != 0 && Platform::MemoryCompare(setLayoutOther.Get(), setLayout.Get(), setLayout.Count() * sizeof(VkDescriptorSetLayoutBinding))) + if (bindingsCount != 0 && Platform::MemoryCompare(other.SetLayouts[index].LayoutBindings.Get(), SetLayouts[index].LayoutBindings.Get(), bindingsCount * sizeof(VkDescriptorSetLayoutBinding))) return false; } - return true; } @@ -63,16 +62,13 @@ DescriptorSetLayoutVulkan::DescriptorSetLayoutVulkan(GPUDeviceVulkan* device) DescriptorSetLayoutVulkan::~DescriptorSetLayoutVulkan() { for (VkDescriptorSetLayout& handle : Handles) - { Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::DescriptorSetLayout, handle); - } } void DescriptorSetLayoutVulkan::Compile() { +#if !BUILD_RELEASE ASSERT(Handles.IsEmpty()); - - // Validate device limits for the engine const VkPhysicalDeviceLimits& limits = Device->PhysicalDeviceLimits; ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_SAMPLER] + LayoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER] < limits.maxDescriptorSetSamplers); ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER]+ LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] < limits.maxDescriptorSetUniformBuffers); @@ -81,34 +77,23 @@ void DescriptorSetLayoutVulkan::Compile() ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC] < limits.maxDescriptorSetStorageBuffersDynamic); ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER] + LayoutTypes[VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE] + LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER] < limits.maxDescriptorSetSampledImages); ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_IMAGE] + LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]< limits.maxDescriptorSetStorageImages); +#endif Handles.Resize(SetLayouts.Count()); for (int32 i = 0; i < SetLayouts.Count(); i++) { - auto& layout = SetLayouts[i]; + auto& layout = SetLayouts.Get()[i]; VkDescriptorSetLayoutCreateInfo layoutInfo; RenderToolsVulkan::ZeroStruct(layoutInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO); - layoutInfo.bindingCount = layout.Count(); - layoutInfo.pBindings = layout.Get(); + layoutInfo.bindingCount = layout.LayoutBindings.Count(); + layoutInfo.pBindings = layout.LayoutBindings.Get(); VALIDATE_VULKAN_RESULT(vkCreateDescriptorSetLayout(Device->Device, &layoutInfo, nullptr, &Handles[i])); } - if (TypesUsageID == ~0) - { - // Cache usage ID - static uint32 uniqueID = 1; - static Dictionary typesUsageHashMap; - const uint32 typesUsageHash = Crc::MemCrc32(LayoutTypes, sizeof(LayoutTypes)); - uint32 id; - Device->Locker.Lock(); - if (!typesUsageHashMap.TryGet(typesUsageHash, id)) - { - id = uniqueID++; - typesUsageHashMap.Add(typesUsageHash, id); - } - Device->Locker.Unlock(); - TypesUsageID = id; - } +#if VULKAN_HASH_POOLS_WITH_LAYOUT_TYPES + if (SetLayoutsHash == 0) + SetLayoutsHash = Crc::MemCrc32(LayoutTypes, sizeof(LayoutTypes)); +#endif RenderToolsVulkan::ZeroStruct(AllocateInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO); AllocateInfo.descriptorSetCount = Handles.Count(); @@ -127,7 +112,7 @@ DescriptorPoolVulkan::DescriptorPoolVulkan(GPUDeviceVulkan* device, const Descri // The maximum amount of descriptor sets layout allocations to hold const uint32 MaxSetsAllocations = 256; - _descriptorSetsMax = MaxSetsAllocations * (VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? 1 : _layout.SetLayouts.Count()); + _descriptorSetsMax = MaxSetsAllocations * (VULKAN_HASH_POOLS_WITH_LAYOUT_TYPES ? 1 : _layout.SetLayouts.Count()); for (uint32 typeIndex = VULKAN_DESCRIPTOR_TYPE_BEGIN; typeIndex <= VULKAN_DESCRIPTOR_TYPE_END; typeIndex++) { const VkDescriptorType descriptorType = (VkDescriptorType)typeIndex; @@ -274,7 +259,7 @@ DescriptorPoolSetContainerVulkan::~DescriptorPoolSetContainerVulkan() TypedDescriptorPoolSetVulkan* DescriptorPoolSetContainerVulkan::AcquireTypedPoolSet(const DescriptorSetLayoutVulkan& layout) { - const uint32 hash = VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? layout.TypesUsageID : layout.Hash; + const uint32 hash = VULKAN_HASH_POOLS_WITH_LAYOUT_TYPES ? layout.SetLayoutsHash : GetHash(layout); TypedDescriptorPoolSetVulkan* typedPool; if (!_typedDescriptorPools.TryGet(hash, typedPool)) { @@ -347,26 +332,24 @@ void DescriptorPoolsManagerVulkan::GC() } PipelineLayoutVulkan::PipelineLayoutVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutInfoVulkan& layout) - : _device(device) - , _handle(VK_NULL_HANDLE) - , _descriptorSetLayout(device) + : Device(device) + , Handle(VK_NULL_HANDLE) + , DescriptorSetLayout(device) { - _descriptorSetLayout.CopyFrom(layout); - _descriptorSetLayout.Compile(); + DescriptorSetLayout.CopyFrom(layout); + DescriptorSetLayout.Compile(); VkPipelineLayoutCreateInfo createInfo; RenderToolsVulkan::ZeroStruct(createInfo, VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO); - createInfo.setLayoutCount = _descriptorSetLayout.Handles.Count(); - createInfo.pSetLayouts = _descriptorSetLayout.Handles.Get(); - VALIDATE_VULKAN_RESULT(vkCreatePipelineLayout(_device->Device, &createInfo, nullptr, &_handle)); + createInfo.setLayoutCount = DescriptorSetLayout.Handles.Count(); + createInfo.pSetLayouts = DescriptorSetLayout.Handles.Get(); + VALIDATE_VULKAN_RESULT(vkCreatePipelineLayout(device->Device, &createInfo, nullptr, &Handle)); } PipelineLayoutVulkan::~PipelineLayoutVulkan() { - if (_handle != VK_NULL_HANDLE) - { - _device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::PipelineLayout, _handle); - } + if (Handle != VK_NULL_HANDLE) + Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::PipelineLayout, Handle); } uint32 DescriptorSetWriterVulkan::SetupDescriptorWrites(const SpirvShaderDescriptorInfo& info, VkWriteDescriptorSet* writeDescriptors, VkDescriptorImageInfo* imageInfo, VkDescriptorBufferInfo* bufferInfo, VkBufferView* texelBufferView, uint8* bindingToDynamicOffset) diff --git a/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.h index c5ab265d2..203302ba0 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/DescriptorSetVulkan.h @@ -54,14 +54,15 @@ namespace DescriptorSet class DescriptorSetLayoutInfoVulkan { public: - typedef Array SetLayout; + struct SetLayout + { + Array LayoutBindings; + }; uint32 Hash = 0; - uint32 TypesUsageID = ~0; - Array SetLayouts; + uint32 SetLayoutsHash = 0; uint32 LayoutTypes[VULKAN_DESCRIPTOR_TYPE_END]; - - void CacheTypesUsageID(); + Array SetLayouts; public: DescriptorSetLayoutInfoVulkan() @@ -75,7 +76,7 @@ public: { Platform::MemoryCopy(LayoutTypes, info.LayoutTypes, sizeof(LayoutTypes)); Hash = info.Hash; - TypesUsageID = info.TypesUsageID; + SetLayoutsHash = info.SetLayoutsHash; SetLayouts = info.SetLayouts; } @@ -248,30 +249,13 @@ public: class PipelineLayoutVulkan { -private: - GPUDeviceVulkan* _device; - VkPipelineLayout _handle; - DescriptorSetLayoutVulkan _descriptorSetLayout; - public: + GPUDeviceVulkan* Device; + VkPipelineLayout Handle; + DescriptorSetLayoutVulkan DescriptorSetLayout; + PipelineLayoutVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutInfoVulkan& layout); ~PipelineLayoutVulkan(); - -public: - inline VkPipelineLayout GetHandle() const - { - return _handle; - } - - inline const DescriptorSetLayoutVulkan& GetDescriptorSetLayout() const - { - return _descriptorSetLayout; - } - - inline bool HasDescriptors() const - { - return _descriptorSetLayout.SetLayouts.HasItems(); - } }; struct DescriptorSetWriteContainerVulkan diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index a0c153cc5..521038681 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -335,13 +335,12 @@ DescriptorPoolVulkan* GPUContextVulkan::AllocateDescriptorSets(const VkDescripto VkDescriptorSetAllocateInfo allocateInfo = descriptorSetAllocateInfo; DescriptorPoolVulkan* pool = nullptr; - const uint32 hash = VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? layout.TypesUsageID : layout.Hash; + const uint32 hash = VULKAN_HASH_POOLS_WITH_LAYOUT_TYPES ? layout.SetLayoutsHash : GetHash(layout); DescriptorPoolArray* typedDescriptorPools = _descriptorPools.TryGet(hash); if (typedDescriptorPools != nullptr) { pool = typedDescriptorPools->HasItems() ? typedDescriptorPools->Last() : nullptr; - if (pool && pool->CanAllocate(layout)) { allocateInfo.descriptorPool = pool->GetHandle(); @@ -361,7 +360,6 @@ DescriptorPoolVulkan* GPUContextVulkan::AllocateDescriptorSets(const VkDescripto } else { - // Spec says any negative value could be due to fragmentation, so create a new Pool. If it fails here then we really are out of memory! pool = New(_device, layout); typedDescriptorPools->Add(pool); allocateInfo.descriptorPool = pool->GetHandle(); @@ -689,7 +687,7 @@ void GPUContextVulkan::OnDrawCall() vkCmdBindDescriptorSets( cmdBuffer->GetHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, - pipelineState->GetLayout()->GetHandle(), + pipelineState->GetLayout()->Handle, 0, pipelineState->DescriptorSetHandles.Count(), pipelineState->DescriptorSetHandles.Get(), diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp index 67f0ac70b..c8aa13549 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp @@ -54,7 +54,7 @@ ComputePipelineStateVulkan* GPUShaderProgramCSVulkan::GetOrCreateState() VkComputePipelineCreateInfo desc; RenderToolsVulkan::ZeroStruct(desc, VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO); desc.basePipelineIndex = -1; - desc.layout = layout->GetHandle(); + desc.layout = layout->Handle; RenderToolsVulkan::ZeroStruct(desc.stage, VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO); auto& stage = desc.stage; RenderToolsVulkan::ZeroStruct(stage, VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO); @@ -72,7 +72,7 @@ ComputePipelineStateVulkan* GPUShaderProgramCSVulkan::GetOrCreateState() // Setup the state _pipelineState = New(_device, pipeline, layout); _pipelineState->DescriptorInfo = &DescriptorInfo; - _pipelineState->DescriptorSetsLayout = &layout->GetDescriptorSetLayout(); + _pipelineState->DescriptorSetsLayout = &layout->DescriptorSetLayout; _pipelineState->DescriptorSetHandles.AddZeroed(_pipelineState->DescriptorSetsLayout->Handles.Count()); uint32 dynamicOffsetsCount = 0; if (DescriptorInfo.DescriptorTypesCount != 0) @@ -146,7 +146,7 @@ PipelineLayoutVulkan* GPUPipelineStateVulkan::GetLayout() _layout = _device->GetOrCreateLayout(descriptorSetLayoutInfo); ASSERT(_layout); - DescriptorSetsLayout = &_layout->GetDescriptorSetLayout(); + DescriptorSetsLayout = &_layout->DescriptorSetLayout; DescriptorSetHandles.AddZeroed(DescriptorSetsLayout->Handles.Count()); return _layout; @@ -179,7 +179,7 @@ VkPipeline GPUPipelineStateVulkan::GetState(RenderPassVulkan* renderPass) // Check if has missing layout if (_desc.layout == VK_NULL_HANDLE) { - _desc.layout = GetLayout()->GetHandle(); + _desc.layout = GetLayout()->Handle; } // Create object diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h index 0300380a3..17baea9b1 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h @@ -63,7 +63,7 @@ public: vkCmdBindDescriptorSets( cmdBuffer->GetHandle(), VK_PIPELINE_BIND_POINT_COMPUTE, - GetLayout()->GetHandle(), + GetLayout()->Handle, 0, DescriptorSetHandles.Count(), DescriptorSetHandles.Get(), diff --git a/Source/Engine/GraphicsDevice/Vulkan/QueueVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/QueueVulkan.cpp index ce36a901a..3104774da 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/QueueVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/QueueVulkan.cpp @@ -21,7 +21,6 @@ QueueVulkan::QueueVulkan(GPUDeviceVulkan* device, uint32 familyIndex) void QueueVulkan::Submit(CmdBufferVulkan* cmdBuffer, uint32 signalSemaphoresCount, const VkSemaphore* signalSemaphores) { ASSERT(cmdBuffer->HasEnded()); - auto fence = cmdBuffer->GetFence(); ASSERT(!fence->IsSignaled); From 5de5e576e5037d7617e8f6ffed43954a338609d9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 17 Feb 2024 09:07:37 +0100 Subject: [PATCH 091/198] Rename `Navigation.ProjectPoint` into `Navigation.FindClosestPoint` to have consistent API naming #2034 --- Source/Engine/AI/BehaviorTreeNodes.cpp | 4 ++-- Source/Engine/Navigation/NavMeshRuntime.cpp | 2 +- Source/Engine/Navigation/NavMeshRuntime.h | 16 ++++++++++++++-- Source/Engine/Navigation/Navigation.cpp | 4 ++-- Source/Engine/Navigation/Navigation.h | 16 ++++++++++++++-- 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/Source/Engine/AI/BehaviorTreeNodes.cpp b/Source/Engine/AI/BehaviorTreeNodes.cpp index 42b12d911..34d0c7295 100644 --- a/Source/Engine/AI/BehaviorTreeNodes.cpp +++ b/Source/Engine/AI/BehaviorTreeNodes.cpp @@ -474,8 +474,8 @@ BehaviorUpdateResult BehaviorTreeMoveToNode::Update(const BehaviorUpdateContext& state->NavAgentRadius = navMesh->Properties.Agent.Radius; // Place start and end on navmesh - navMesh->ProjectPoint(state->Path.First(), state->Path.First()); - navMesh->ProjectPoint(state->Path.Last(), state->Path.Last()); + navMesh->FindClosestPoint(state->Path.First(), state->Path.First()); + navMesh->FindClosestPoint(state->Path.Last(), state->Path.Last()); // Calculate offset between path and the agent (aka feet offset) state->AgentOffset = state->Path.First() - agentLocation; diff --git a/Source/Engine/Navigation/NavMeshRuntime.cpp b/Source/Engine/Navigation/NavMeshRuntime.cpp index 4fcf0e038..21c42dc5a 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.cpp +++ b/Source/Engine/Navigation/NavMeshRuntime.cpp @@ -194,7 +194,7 @@ bool NavMeshRuntime::TestPath(const Vector3& startPosition, const Vector3& endPo return true; } -bool NavMeshRuntime::ProjectPoint(const Vector3& point, Vector3& result) const +bool NavMeshRuntime::FindClosestPoint(const Vector3& point, Vector3& result) const { ScopeLock lock(Locker); const auto query = GetNavMeshQuery(); diff --git a/Source/Engine/Navigation/NavMeshRuntime.h b/Source/Engine/Navigation/NavMeshRuntime.h index 612cba285..ec8b7dbae 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.h +++ b/Source/Engine/Navigation/NavMeshRuntime.h @@ -146,12 +146,24 @@ public: API_FUNCTION() bool TestPath(const Vector3& startPosition, const Vector3& endPosition) const; /// - /// Projects the point to nav mesh surface (finds the nearest polygon). + /// Finds the nearest point on a nav mesh surface. /// /// The source point. /// The result position on the navmesh (valid only if method returns true). /// True if found valid location on the navmesh, otherwise false. - API_FUNCTION() bool ProjectPoint(const Vector3& point, API_PARAM(Out) Vector3& result) const; + API_FUNCTION() bool FindClosestPoint(const Vector3& point, API_PARAM(Out) Vector3& result) const; + + /// + /// Projects the point to nav mesh surface (finds the nearest polygon). + /// [Deprecated in v1.8] + /// + /// The source point. + /// The result position on the navmesh (valid only if method returns true). + /// True if found valid location on the navmesh, otherwise false. + API_FUNCTION() bool ProjectPoint(const Vector3& point, API_PARAM(Out) Vector3& result) const + { + return FindClosestPoint(point, result); + } /// /// Finds random location on nav mesh. diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index 73e11e630..2f1e6f989 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -325,11 +325,11 @@ bool Navigation::TestPath(const Vector3& startPosition, const Vector3& endPositi return NavMeshes.First()->TestPath(startPosition, endPosition); } -bool Navigation::ProjectPoint(const Vector3& point, Vector3& result) +bool Navigation::FindClosestPoint(const Vector3& point, Vector3& result) { if (NavMeshes.IsEmpty()) return false; - return NavMeshes.First()->ProjectPoint(point, result); + return NavMeshes.First()->FindClosestPoint(point, result); } bool Navigation::FindRandomPoint(Vector3& result) diff --git a/Source/Engine/Navigation/Navigation.h b/Source/Engine/Navigation/Navigation.h index 4ca6aa470..8d4ee1a21 100644 --- a/Source/Engine/Navigation/Navigation.h +++ b/Source/Engine/Navigation/Navigation.h @@ -40,12 +40,24 @@ public: API_FUNCTION() static bool TestPath(const Vector3& startPosition, const Vector3& endPosition); /// - /// Projects the point to nav mesh surface (finds the nearest polygon). + /// Finds the nearest point on a nav mesh surface. /// /// The source point. /// The result position on the navmesh (valid only if method returns true). /// True if found valid location on the navmesh, otherwise false. - API_FUNCTION() static bool ProjectPoint(const Vector3& point, API_PARAM(Out) Vector3& result); + API_FUNCTION() static bool FindClosestPoint(const Vector3& point, API_PARAM(Out) Vector3& result); + + /// + /// Projects the point to nav mesh surface (finds the nearest polygon). + /// [Deprecated in v1.8] + /// + /// The source point. + /// The result position on the navmesh (valid only if method returns true). + /// True if found valid location on the navmesh, otherwise false. + API_FUNCTION() DEPRECATED static bool ProjectPoint(const Vector3& point, API_PARAM(Out) Vector3& result) + { + return FindClosestPoint(point, result); + } /// /// Finds random location on nav mesh. From 0a0bb997e46d4b5a4569bb524302ed9f5e85ff5d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 26 Feb 2024 19:53:58 +0100 Subject: [PATCH 092/198] Update year in copyright note --- Source/FlaxEngine.pch.h | 2 +- Source/Tools/Flax.Build/Deps/Dependencies/astc.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/FlaxEngine.pch.h b/Source/FlaxEngine.pch.h index 0a73fc464..2e3fc11d3 100644 --- a/Source/FlaxEngine.pch.h +++ b/Source/FlaxEngine.pch.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #pragma once diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs b/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs index a1c9edcee..01e369d62 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/astc.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System.IO; using Flax.Build; From 901b043909e94864be55803ece8ccac28fe93028 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 28 Feb 2024 02:05:06 +0100 Subject: [PATCH 093/198] Fix Temporal Anti Aliasing ghosting and improve quality of blending #1786 --- Content/Shaders/TAA.flax | 4 +- Source/Engine/Graphics/PostProcessSettings.h | 6 +-- Source/Engine/Graphics/RenderView.cpp | 2 +- Source/Engine/Renderer/AntiAliasing/TAA.cpp | 3 ++ Source/Shaders/TAA.shader | 50 ++++++++++++++------ 5 files changed, 45 insertions(+), 20 deletions(-) diff --git a/Content/Shaders/TAA.flax b/Content/Shaders/TAA.flax index c8e96e9ed..e719850ac 100644 --- a/Content/Shaders/TAA.flax +++ b/Content/Shaders/TAA.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:707c2630916ad6f0d2b604e6cda290fdc24e027a1ee6e435f62e6efb393e8ada -size 3265 +oid sha256:97f24b13b752313dae0eda0540748554fa98ffbb6c05e0fca95ee5f4b37f3a03 +size 4285 diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 17bcdb3a9..ad28f1dce 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -1889,13 +1889,13 @@ API_STRUCT() struct FLAXENGINE_API AntiAliasingSettings : ISerializable /// The diameter (in texels) inside which jitter samples are spread. Smaller values result in crisper but more aliased output, while larger values result in more stable but blurrier output. /// API_FIELD(Attributes="Limit(0.1f, 1f, 0.001f), EditorOrder(1), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_JitterSpread), EditorDisplay(null, \"TAA Jitter Spread\")") - float TAA_JitterSpread = 0.75f; + float TAA_JitterSpread = 1.0f; /// /// Controls the amount of sharpening applied to the color buffer. TAA can induce a slight loss of details in high frequency regions. Sharpening alleviates this issue. High values may introduce dark-border artifacts. /// API_FIELD(Attributes="Limit(0, 3f, 0.001f), EditorOrder(2), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_Sharpness), EditorDisplay(null, \"TAA Sharpness\")") - float TAA_Sharpness = 0.0f; + float TAA_Sharpness = 0.1f; /// /// The blend coefficient for stationary fragments. Controls the percentage of history samples blended into the final color for fragments with minimal active motion. @@ -1907,7 +1907,7 @@ API_STRUCT() struct FLAXENGINE_API AntiAliasingSettings : ISerializable /// The blending coefficient for moving fragments. Controls the percentage of history samples blended into the final color for fragments with significant active motion. /// API_FIELD(Attributes="Limit(0, 0.99f, 0.001f), EditorOrder(4), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_MotionBlending), EditorDisplay(null, \"TAA Motion Blending\")") - float TAA_MotionBlending = 0.7f; + float TAA_MotionBlending = 0.85f; public: /// diff --git a/Source/Engine/Graphics/RenderView.cpp b/Source/Engine/Graphics/RenderView.cpp index d59a16463..95a09f6c4 100644 --- a/Source/Engine/Graphics/RenderView.cpp +++ b/Source/Engine/Graphics/RenderView.cpp @@ -26,7 +26,7 @@ void RenderView::Prepare(RenderContext& renderContext) TaaFrameIndex = 0; // Calculate jitter - const float jitterSpread = renderContext.List->Settings.AntiAliasing.TAA_JitterSpread / 0.75f; + const float jitterSpread = renderContext.List->Settings.AntiAliasing.TAA_JitterSpread; const float jitterX = (RendererUtils::TemporalHalton(TaaFrameIndex + 1, 2) - 0.5f) * jitterSpread; const float jitterY = (RendererUtils::TemporalHalton(TaaFrameIndex + 1, 3) - 0.5f) * jitterSpread; taaJitter = Float2(jitterX * 2.0f / width, jitterY * 2.0f / height); diff --git a/Source/Engine/Renderer/AntiAliasing/TAA.cpp b/Source/Engine/Renderer/AntiAliasing/TAA.cpp index f48e0955a..e83870d9d 100644 --- a/Source/Engine/Renderer/AntiAliasing/TAA.cpp +++ b/Source/Engine/Renderer/AntiAliasing/TAA.cpp @@ -8,6 +8,7 @@ #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Renderer/RenderList.h" +#include "Engine/Renderer/GBufferPass.h" #include "Engine/Engine/Engine.h" PACK_STRUCT(struct Data @@ -18,6 +19,7 @@ PACK_STRUCT(struct Data float StationaryBlending; float MotionBlending; float Dummy0; + GBufferData GBuffer; }); bool TAA::Init() @@ -125,6 +127,7 @@ void TAA::Render(const RenderContext& renderContext, GPUTexture* input, GPUTextu data.Sharpness = settings.TAA_Sharpness; data.StationaryBlending = settings.TAA_StationaryBlending * blendStrength; data.MotionBlending = settings.TAA_MotionBlending * blendStrength; + GBufferPass::SetInputs(renderContext.View, data.GBuffer); const auto cb = _shader->GetShader()->GetCB(0); context->UpdateCB(cb, &data); context->BindCB(0, cb); diff --git a/Source/Shaders/TAA.shader b/Source/Shaders/TAA.shader index 04a5006e2..5ae3444b7 100644 --- a/Source/Shaders/TAA.shader +++ b/Source/Shaders/TAA.shader @@ -1,6 +1,10 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +#define DEBUG_HISTORY_REJECTION 0 +#define NO_GBUFFER_SAMPLING + #include "./Flax/Common.hlsl" +#include "./Flax/GBuffer.hlsl" META_CB_BEGIN(0, Data) float2 ScreenSizeInv; @@ -9,6 +13,7 @@ float Sharpness; float StationaryBlending; float MotionBlending; float Dummy0; +GBufferData GBuffer; META_CB_END Texture2D Input : register(t0); @@ -31,37 +36,42 @@ float4 ClipToAABB(float4 color, float4 minimum, float4 maximum) META_PS(true, FEATURE_LEVEL_ES2) float4 PS(Quad_VS2PS input) : SV_Target0 { + float2 tanHalfFOV = float2(GBuffer.InvProjectionMatrix[0][0], GBuffer.InvProjectionMatrix[1][1]); + + // Calculate previous frame UVs based on per-pixel velocity + float2 velocity = SAMPLE_RT_LINEAR(MotionVectors, input.TexCoord).xy; + float velocityLength = length(velocity); + float2 prevUV = input.TexCoord - velocity; + float prevDepth = LinearizeZ(GBuffer, SAMPLE_RT(Depth, prevUV).r); + // Find the closest pixel in 3x3 neighborhood - float bestDepth = 1; - float2 bestUV = float2(0, 0); + float currentDepth = 1; float4 neighborhoodMin = 100000; float4 neighborhoodMax = -10000; float4 current; float4 neighborhoodSum = 0; + float minDepthDiff = 100000; for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { float2 sampleUV = input.TexCoord + float2(x, y) * ScreenSizeInv; - float4 neighbor = SAMPLE_RT_LINEAR(Input, sampleUV); + float4 neighbor = SAMPLE_RT(Input, sampleUV); neighborhoodMin = min(neighborhoodMin, neighbor); neighborhoodMax = max(neighborhoodMax, neighbor); - if (x == 0 && y == 0) - current = neighbor; neighborhoodSum += neighbor; - float depth = SAMPLE_RT(Depth, sampleUV).r; - if (depth < bestDepth) + float neighborDepth = LinearizeZ(GBuffer, SAMPLE_RT(Depth, sampleUV).r); + float depthDiff = abs(max(neighborDepth - prevDepth, 0)); + minDepthDiff = min(minDepthDiff, depthDiff); + if (x == 0 && y == 0) { - bestDepth = depth; - bestUV = sampleUV; + current = neighbor; + currentDepth = neighborDepth; } } } - float2 velocity = SAMPLE_RT_LINEAR(MotionVectors, bestUV).xy; - float velocityLength = length(velocity); - float2 prevUV = input.TexCoord - velocity; // Apply sharpening float4 neighborhoodAvg = neighborhoodSum / 9.0; @@ -76,11 +86,23 @@ float4 PS(Quad_VS2PS input) : SV_Target0 // Calculate history blending factor float motion = saturate(velocityLength * 1000.0f); - float blendfactor = any(abs(prevUV * 2 - 1) >= 1.0f) ? 0.0f : lerp(StationaryBlending, MotionBlending, motion); + float blendfactor = lerp(StationaryBlending, MotionBlending, motion); // Perform linear accumulation of the previous samples with a current one float4 color = lerp(current, history, blendfactor); - color = clamp(color, 0, HDR_CLAMP_MAX); + // Reduce history blend in favor of neighborhood blend when sample has no valid prevous frame data + float miss = any(abs(prevUV * 2 - 1) >= 1.0f) ? 1 : 0; + float currentDepthWorld = currentDepth * GBuffer.ViewFar; + float minDepthDiffWorld = minDepthDiff * GBuffer.ViewFar; + float depthError = tanHalfFOV.x * ScreenSizeInv.x * 200.0f * (currentDepthWorld + 10.0f); + miss += minDepthDiffWorld > depthError ? 1 : 0; + float4 neighborhoodSharp = lerp(neighborhoodAvg, current, 0.5f); +#if DEBUG_HISTORY_REJECTION + neighborhoodSharp = float4(1, 0, 0, 1); +#endif + color = lerp(color, neighborhoodSharp, saturate(miss)); + + color = clamp(color, 0, HDR_CLAMP_MAX); return color; } From 53bd576adebd1994dd59271e0a81b0c951215606 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 28 Feb 2024 02:05:26 +0100 Subject: [PATCH 094/198] Fix game settings serialization bug --- Source/Engine/Audio/AudioSettings.h | 10 +--------- Source/Engine/Core/Config/BuildSettings.h | 19 +------------------ Source/Engine/Core/Config/GameSettings.cpp | 1 + Source/Engine/Core/Config/GameSettings.h | 6 ++---- .../Engine/Core/Config/LayersTagsSettings.h | 5 ++--- Source/Engine/Core/Config/TimeSettings.h | 7 +++---- Source/Engine/Engine/Time.cpp | 9 --------- Source/Engine/Input/InputSettings.h | 1 + .../Localization/LocalizationSettings.h | 1 + Source/Engine/Navigation/NavigationSettings.h | 1 + Source/Engine/Networking/NetworkSettings.h | 3 ++- Source/Engine/Physics/PhysicsSettings.h | 1 + 12 files changed, 16 insertions(+), 48 deletions(-) diff --git a/Source/Engine/Audio/AudioSettings.h b/Source/Engine/Audio/AudioSettings.h index f4f901b97..edff00390 100644 --- a/Source/Engine/Audio/AudioSettings.h +++ b/Source/Engine/Audio/AudioSettings.h @@ -3,7 +3,6 @@ #pragma once #include "Engine/Core/Config/Settings.h" -#include "Engine/Serialization/Serialization.h" /// /// Audio settings container. @@ -11,6 +10,7 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API AudioSettings : public SettingsBase { DECLARE_SCRIPTING_TYPE_MINIMAL(AudioSettings); + API_AUTO_SERIALIZATION(); public: /// @@ -46,12 +46,4 @@ public: // [SettingsBase] void Apply() override; - - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override - { - DESERIALIZE(DisableAudio); - DESERIALIZE(DopplerFactor); - DESERIALIZE(MuteOnFocusLoss); - DESERIALIZE(EnableHRTF); - } }; diff --git a/Source/Engine/Core/Config/BuildSettings.h b/Source/Engine/Core/Config/BuildSettings.h index 075fd926f..86bc9571f 100644 --- a/Source/Engine/Core/Config/BuildSettings.h +++ b/Source/Engine/Core/Config/BuildSettings.h @@ -3,7 +3,6 @@ #pragma once #include "Engine/Core/Config/Settings.h" -#include "Engine/Serialization/Serialization.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Content/Asset.h" #include "Engine/Content/AssetReference.h" @@ -15,6 +14,7 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API BuildSettings : public SettingsBase { DECLARE_SCRIPTING_TYPE_MINIMAL(BuildSettings); + API_AUTO_SERIALIZATION(); public: /// @@ -106,21 +106,4 @@ public: /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. /// static BuildSettings* Get(); - - // [SettingsBase] - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override - { - DESERIALIZE(MaxAssetsPerPackage); - DESERIALIZE(MaxPackageSizeMB); - DESERIALIZE(ContentKey); - DESERIALIZE(ForDistribution); - DESERIALIZE(SkipPackaging); - DESERIALIZE(AdditionalAssets); - DESERIALIZE(AdditionalAssetFolders); - DESERIALIZE(ShadersNoOptimize); - DESERIALIZE(ShadersGenerateDebugData); - DESERIALIZE(SkipDefaultFonts); - DESERIALIZE(SkipDotnetPackaging); - DESERIALIZE(SkipUnusedDotnetLibsPackaging); - } }; diff --git a/Source/Engine/Core/Config/GameSettings.cpp b/Source/Engine/Core/Config/GameSettings.cpp index 76fb0f437..11617612f 100644 --- a/Source/Engine/Core/Config/GameSettings.cpp +++ b/Source/Engine/Core/Config/GameSettings.cpp @@ -22,6 +22,7 @@ #include "Engine/Engine/Globals.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Streaming/StreamingSettings.h" +#include "Engine/Serialization/Serialization.h" #if FLAX_TESTS || USE_EDITOR #include "Engine/Platform/FileSystem.h" #endif diff --git a/Source/Engine/Core/Config/GameSettings.h b/Source/Engine/Core/Config/GameSettings.h index d28dcaa56..59e504b6b 100644 --- a/Source/Engine/Core/Config/GameSettings.h +++ b/Source/Engine/Core/Config/GameSettings.h @@ -12,9 +12,9 @@ /// API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API GameSettings : public SettingsBase { -DECLARE_SCRIPTING_TYPE_MINIMAL(GameSettings); -public: + DECLARE_SCRIPTING_TYPE_MINIMAL(GameSettings); +public: /// /// The product full name. /// @@ -59,7 +59,6 @@ public: Dictionary CustomSettings; public: - // Settings containers Guid Time; Guid Audio; @@ -87,7 +86,6 @@ public: Guid iOSPlatform; public: - /// /// Gets the instance of the game settings asset (null if missing). Object returned by this method is always loaded with valid data to use. /// diff --git a/Source/Engine/Core/Config/LayersTagsSettings.h b/Source/Engine/Core/Config/LayersTagsSettings.h index 39d7506d5..20e3977a7 100644 --- a/Source/Engine/Core/Config/LayersTagsSettings.h +++ b/Source/Engine/Core/Config/LayersTagsSettings.h @@ -11,9 +11,9 @@ /// API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API LayersAndTagsSettings : public SettingsBase { -DECLARE_SCRIPTING_TYPE_MINIMAL(LayersAndTagsSettings); -public: + DECLARE_SCRIPTING_TYPE_MINIMAL(LayersAndTagsSettings); +public: /// /// The tag names. /// @@ -25,7 +25,6 @@ public: String Layers[32]; public: - /// /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. /// diff --git a/Source/Engine/Core/Config/TimeSettings.h b/Source/Engine/Core/Config/TimeSettings.h index 2a5a39a72..3edd83195 100644 --- a/Source/Engine/Core/Config/TimeSettings.h +++ b/Source/Engine/Core/Config/TimeSettings.h @@ -9,9 +9,10 @@ /// API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API TimeSettings : public SettingsBase { -DECLARE_SCRIPTING_TYPE_MINIMAL(TimeSettings); -public: + DECLARE_SCRIPTING_TYPE_MINIMAL(TimeSettings); + API_AUTO_SERIALIZATION(); +public: /// /// The target amount of the game logic updates per second (script updates frequency). /// @@ -43,7 +44,6 @@ public: float MaxUpdateDeltaTime = 0.1f; public: - /// /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. /// @@ -51,5 +51,4 @@ public: // [SettingsBase] void Apply() override; - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override; }; diff --git a/Source/Engine/Engine/Time.cpp b/Source/Engine/Engine/Time.cpp index 4903a576e..6df5275f5 100644 --- a/Source/Engine/Engine/Time.cpp +++ b/Source/Engine/Engine/Time.cpp @@ -54,15 +54,6 @@ void TimeSettings::Apply() ::MaxUpdateDeltaTime = MaxUpdateDeltaTime; } -void TimeSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) -{ - DESERIALIZE(UpdateFPS); - DESERIALIZE(PhysicsFPS); - DESERIALIZE(DrawFPS); - DESERIALIZE(TimeScale); - DESERIALIZE(MaxUpdateDeltaTime); -} - void Time::TickData::OnBeforeRun(float targetFps, double currentTime) { Time = UnscaledTime = TimeSpan::Zero(); diff --git a/Source/Engine/Input/InputSettings.h b/Source/Engine/Input/InputSettings.h index 9f03db243..b251eaf3c 100644 --- a/Source/Engine/Input/InputSettings.h +++ b/Source/Engine/Input/InputSettings.h @@ -12,6 +12,7 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API InputSettings : public SettingsBase { DECLARE_SCRIPTING_TYPE_MINIMAL(InputSettings); + public: /// /// Maps a discrete button or key press events to a "friendly name" that will later be bound to event-driven behavior. The end effect is that pressing (and/or releasing) a key, mouse button, or keypad button. diff --git a/Source/Engine/Localization/LocalizationSettings.h b/Source/Engine/Localization/LocalizationSettings.h index 0f5e1d99c..cfd391cc3 100644 --- a/Source/Engine/Localization/LocalizationSettings.h +++ b/Source/Engine/Localization/LocalizationSettings.h @@ -12,6 +12,7 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API LocalizationSettings : public SettingsBase { DECLARE_SCRIPTING_TYPE_MINIMAL(LocalizationSettings); + public: /// /// The list of the string localization tables used by the game. diff --git a/Source/Engine/Navigation/NavigationSettings.h b/Source/Engine/Navigation/NavigationSettings.h index 62671433f..f4e6c26fa 100644 --- a/Source/Engine/Navigation/NavigationSettings.h +++ b/Source/Engine/Navigation/NavigationSettings.h @@ -12,6 +12,7 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings", NoConstructor) class FLAXENGINE_API NavigationSettings : public SettingsBase { DECLARE_SCRIPTING_TYPE_MINIMAL(NavigationSettings); + public: /// /// If checked, enables automatic navmesh actors spawning on a scenes that are using it during navigation building. diff --git a/Source/Engine/Networking/NetworkSettings.h b/Source/Engine/Networking/NetworkSettings.h index 98340e369..c861114f9 100644 --- a/Source/Engine/Networking/NetworkSettings.h +++ b/Source/Engine/Networking/NetworkSettings.h @@ -10,8 +10,9 @@ /// API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API NetworkSettings : public SettingsBase { - API_AUTO_SERIALIZATION(); DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkSettings); + API_AUTO_SERIALIZATION(); + public: /// /// Maximum amount of active network clients in a game session. Used by server or host to limit amount of players and spectators. diff --git a/Source/Engine/Physics/PhysicsSettings.h b/Source/Engine/Physics/PhysicsSettings.h index eefbacc87..fca1ea2d4 100644 --- a/Source/Engine/Physics/PhysicsSettings.h +++ b/Source/Engine/Physics/PhysicsSettings.h @@ -56,6 +56,7 @@ API_ENUM() enum class PhysicsSolverType API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings", NoConstructor) class FLAXENGINE_API PhysicsSettings : public SettingsBase { DECLARE_SCRIPTING_TYPE_MINIMAL(PhysicsSettings); + public: /// /// The default gravity force value (in cm^2/s). From 4df56cb50683fc7271baaaf1b6dbdddcb009fcd2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 29 Feb 2024 01:41:40 +0100 Subject: [PATCH 095/198] Refactor 3D audio implementation in XAudio2 backend to match OpenAL #1612 --- Source/Engine/Audio/AudioBackendTools.h | 62 ++++++- .../Audio/XAudio2/AudioBackendXAudio2.cpp | 162 +++++------------- 2 files changed, 96 insertions(+), 128 deletions(-) diff --git a/Source/Engine/Audio/AudioBackendTools.h b/Source/Engine/Audio/AudioBackendTools.h index 019fc127d..0ccb1085d 100644 --- a/Source/Engine/Audio/AudioBackendTools.h +++ b/Source/Engine/Audio/AudioBackendTools.h @@ -37,30 +37,39 @@ public: Quaternion Orientation; }; - enum class Channels + enum Channels { FrontLeft = 0, FrontRight = 1, - Center = 2, + FontCenter = 2, BackLeft = 3, BackRight = 4, SideLeft = 5, SideRight = 6, - MAX + MaxChannels }; struct SoundMix { float Pitch; float Volume; - float Channels[(int32)Channels::MAX]; + float Channels[MaxChannels]; + + void VolumeIntoChannels() + { + for (float& c : Channels) + c *= Volume; + Volume = 1.0f; + } }; static SoundMix CalculateSoundMix(const Settings& settings, const Listener& listener, const Source& source, int32 channelCount = 2) { + ASSERT_LOW_LAYER(channelCount <= MaxChannels); SoundMix mix; mix.Pitch = source.Pitch; mix.Volume = source.Volume * settings.Volume; + Platform::MemoryClear(mix.Channels, sizeof(mix.Channels)); if (source.Is3D) { const Transform listenerTransform(listener.Position, listener.Orientation); @@ -78,7 +87,7 @@ public: // Calculate panning // Ramy Sadek and Chris Kyriakakis, 2004, "A Novel Multichannel Panning Method for Standard and Arbitrary Loudspeaker Configurations" // [https://www.researchgate.net/publication/235080603_A_Novel_Multichannel_Panning_Method_for_Standard_and_Arbitrary_Loudspeaker_Configurations] - static const Float3 ChannelDirections[(int32)Channels::MAX] = + static const Float3 ChannelDirections[MaxChannels] = { Float3(-1.0, 0.0, -1.0).GetNormalized(), Float3(1.0, 0.0, -1.0).GetNormalized(), @@ -117,11 +126,46 @@ public: } else { - mix.Channels[(int32)Channels::FrontLeft] = Math::Min(1.0f - source.Pan, 1.0f); - mix.Channels[(int32)Channels::FrontRight] = Math::Min(1.0f + source.Pan, 1.0f); - for (int32 i = 2; i < channelCount; i++) - mix.Channels[i] = 0.0f; + const float panLeft = Math::Min(1.0f - source.Pan, 1.0f); + const float panRight = Math::Min(1.0f + source.Pan, 1.0f); + switch (channelCount) + { + case 1: + mix.Channels[0] = 1.0f; + break; + case 2: + default: // TODO: handle other channel configuration (eg. 7.1 or 5.1) + mix.Channels[FrontLeft] = panLeft; + mix.Channels[FrontRight] = panRight; + break; + } } return mix; } + + static void MapChannels(int32 sourceChannels, int32 outputChannels, float channels[MaxChannels], float* outputMatrix) + { + Platform::MemoryClear(outputMatrix, sizeof(float) * sourceChannels * outputChannels); + switch (outputChannels) + { + case 1: + outputMatrix[0] = channels[FrontLeft]; + break; + case 2: + default: // TODO: implement multi-channel support (eg. 5.1, 7.1) + if (sourceChannels == 1) + { + outputMatrix[0] = channels[FrontLeft]; + outputMatrix[1] = channels[FrontRight]; + } + else if (sourceChannels == 2) + { + outputMatrix[0] = channels[FrontLeft]; + outputMatrix[1] = 0.0f; + outputMatrix[2] = 0.0f; + outputMatrix[3] = channels[FrontRight]; + } + break; + } + } }; diff --git a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp index d174c27cc..7c16f05ca 100644 --- a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp +++ b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp @@ -3,7 +3,7 @@ #if AUDIO_API_XAUDIO2 #include "AudioBackendXAudio2.h" -#include "Engine/Audio/AudioSettings.h" +#include "Engine/Audio/AudioBackendTools.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/ChunkedArray.h" #include "Engine/Core/Log.h" @@ -22,10 +22,11 @@ // Documentation: https://docs.microsoft.com/en-us/windows/desktop/xaudio2/xaudio2-apis-portal #include //#include -#include +//#include +// TODO: implement multi-channel support (eg. 5.1, 7.1) #define MAX_INPUT_CHANNELS 2 -#define MAX_OUTPUT_CHANNELS 8 +#define MAX_OUTPUT_CHANNELS 2 #define MAX_CHANNELS_MATRIX_SIZE (MAX_INPUT_CHANNELS*MAX_OUTPUT_CHANNELS) #if ENABLE_ASSERTION #define XAUDIO2_CHECK_ERROR(method) \ @@ -36,18 +37,12 @@ #else #define XAUDIO2_CHECK_ERROR(method) #endif -#define FLAX_COORD_SCALE 0.01f // units are meters -#define FLAX_DST_TO_XAUDIO(x) x * FLAX_COORD_SCALE -#define FLAX_POS_TO_XAUDIO(vec) X3DAUDIO_VECTOR(vec.X * FLAX_COORD_SCALE, vec.Y * FLAX_COORD_SCALE, vec.Z * FLAX_COORD_SCALE) -#define FLAX_VEL_TO_XAUDIO(vec) X3DAUDIO_VECTOR(vec.X * (FLAX_COORD_SCALE*FLAX_COORD_SCALE), vec.Y * (FLAX_COORD_SCALE*FLAX_COORD_SCALE), vec.Z * (FLAX_COORD_SCALE*FLAX_COORD_SCALE)) -#define FLAX_VEC_TO_XAUDIO(vec) (*((X3DAUDIO_VECTOR*)&vec)) namespace XAudio2 { - struct Listener + struct Listener : AudioBackendTools::Listener { AudioListener* AudioListener; - X3DAUDIO_LISTENER Data; Listener() { @@ -57,7 +52,6 @@ namespace XAudio2 void Init() { AudioListener = nullptr; - Data.pCone = nullptr; } bool IsFree() const @@ -67,21 +61,13 @@ namespace XAudio2 void UpdateTransform() { - const Vector3& position = AudioListener->GetPosition(); - const Quaternion& orientation = AudioListener->GetOrientation(); - const Vector3 front = orientation * Vector3::Forward; - const Vector3 top = orientation * Vector3::Up; - - Data.OrientFront = FLAX_VEC_TO_XAUDIO(front); - Data.OrientTop = FLAX_VEC_TO_XAUDIO(top); - Data.Position = FLAX_POS_TO_XAUDIO(position); + Position = AudioListener->GetPosition(); + Orientation = AudioListener->GetOrientation(); } void UpdateVelocity() { - const Vector3& velocity = AudioListener->GetVelocity(); - - Data.Velocity = FLAX_VEL_TO_XAUDIO(velocity); + Velocity = AudioListener->GetVelocity(); } }; @@ -123,23 +109,18 @@ namespace XAudio2 void PeekSamples(); }; - struct Source + struct Source : AudioBackendTools::Source { IXAudio2SourceVoice* Voice; - X3DAUDIO_EMITTER Data; WAVEFORMATEX Format; XAUDIO2_SEND_DESCRIPTOR Destination; - float Pitch; - float Pan; float StartTimeForQueueBuffer; float LastBufferStartTime; - float DopplerFactor; uint64 LastBufferStartSamplesPlayed; int32 BuffersProcessed; + int32 Channels; bool IsDirty; - bool Is3D; bool IsPlaying; - bool IsForceMono3D; VoiceCallback Callback; Source() @@ -150,8 +131,6 @@ namespace XAudio2 void Init() { Voice = nullptr; - Platform::MemoryClear(&Data, sizeof(Data)); - Data.CurveDistanceScaler = 1.0f; Destination.Flags = 0; Destination.pOutputVoice = nullptr; Pitch = 1.0f; @@ -172,21 +151,13 @@ namespace XAudio2 void UpdateTransform(const AudioSource* source) { - const Vector3& position = source->GetPosition(); - const Quaternion& orientation = source->GetOrientation(); - const Vector3 front = orientation * Vector3::Forward; - const Vector3 top = orientation * Vector3::Up; - - Data.OrientFront = FLAX_VEC_TO_XAUDIO(front); - Data.OrientTop = FLAX_VEC_TO_XAUDIO(top); - Data.Position = FLAX_POS_TO_XAUDIO(position); + Position = source->GetPosition(); + Orientation = source->GetOrientation(); } void UpdateVelocity(const AudioSource* source) { - const Vector3& velocity = source->GetVelocity(); - - Data.Velocity = FLAX_VEL_TO_XAUDIO(velocity); + Velocity = source->GetVelocity(); } }; @@ -214,11 +185,9 @@ namespace XAudio2 IXAudio2* Instance = nullptr; IXAudio2MasteringVoice* MasteringVoice = nullptr; - X3DAUDIO_HANDLE X3DInstance; - DWORD ChannelMask; - UINT32 SampleRate; - UINT32 Channels; + int32 Channels; bool ForceDirty = true; + AudioBackendTools::Settings Settings; Listener Listeners[AUDIO_MAX_LISTENERS]; CriticalSection Locker; ChunkedArray Sources; @@ -408,26 +377,25 @@ void AudioBackendXAudio2::Source_OnAdd(AudioSource* source) if (FAILED(hr)) return; + sourceID++; // 0 is invalid ID so shift them + source->SourceIDs.Add(sourceID); + // Prepare source state aSource->Callback.Source = source; aSource->IsDirty = true; - aSource->Data.ChannelCount = format.nChannels; - aSource->Data.InnerRadius = FLAX_DST_TO_XAUDIO(source->GetMinDistance()); aSource->Is3D = source->Is3D(); - aSource->IsForceMono3D = header.Is3D && header.Info.NumChannels > 1; aSource->Pitch = source->GetPitch(); aSource->Pan = source->GetPan(); aSource->DopplerFactor = source->GetDopplerFactor(); + aSource->Volume = source->GetVolume(); + aSource->MinDistance = source->GetMinDistance(); + aSource->Attenuation = source->GetAttenuation(); + aSource->Channels = format.nChannels; aSource->UpdateTransform(source); aSource->UpdateVelocity(source); hr = aSource->Voice->SetVolume(source->GetVolume()); XAUDIO2_CHECK_ERROR(SetVolume); - // 0 is invalid ID so shift them - sourceID++; - - source->SourceIDs.Add(sourceID); - source->Restore(); } @@ -462,6 +430,7 @@ void AudioBackendXAudio2::Source_VolumeChanged(AudioSource* source) auto aSource = XAudio2::GetSource(source); if (aSource && aSource->Voice) { + aSource->Volume = source->GetVolume(); const HRESULT hr = aSource->Voice->SetVolume(source->GetVolume()); XAUDIO2_CHECK_ERROR(SetVolume); } @@ -546,17 +515,10 @@ void AudioBackendXAudio2::Source_SpatialSetupChanged(AudioSource* source) auto aSource = XAudio2::GetSource(source); if (aSource) { - // TODO: implement attenuation settings for 3d audio - auto clip = source->Clip.Get(); - if (clip && clip->IsLoaded()) - { - const auto& header = clip->AudioHeader; - aSource->Data.ChannelCount = source->Is3D() ? 1 : header.Info.NumChannels; // 3d audio is always mono (AudioClip auto-converts before buffer write) - aSource->IsForceMono3D = header.Is3D && header.Info.NumChannels > 1; - } aSource->Is3D = source->Is3D(); + aSource->MinDistance = source->GetMinDistance(); + aSource->Attenuation = source->GetAttenuation(); aSource->DopplerFactor = source->GetDopplerFactor(); - aSource->Data.InnerRadius = FLAX_DST_TO_XAUDIO(source->GetMinDistance()); aSource->IsDirty = true; } } @@ -655,7 +617,7 @@ float AudioBackendXAudio2::Source_GetCurrentBufferTime(const AudioSource* source aSource->Voice->GetState(&state); const uint32 numChannels = clipInfo.NumChannels; const uint32 totalSamples = clipInfo.NumSamples / numChannels; - const uint32 sampleRate = clipInfo.SampleRate;// / clipInfo.NumChannels; + const uint32 sampleRate = clipInfo.SampleRate; // / clipInfo.NumChannels; state.SamplesPlayed -= aSource->LastBufferStartSamplesPlayed % totalSamples; // Offset by the last buffer start to get time relative to its begin time = aSource->LastBufferStartTime + (state.SamplesPlayed % totalSamples) / static_cast(Math::Max(1U, sampleRate)); } @@ -770,6 +732,8 @@ void AudioBackendXAudio2::Buffer_Delete(uint32 bufferId) void AudioBackendXAudio2::Buffer_Write(uint32 bufferId, byte* samples, const AudioDataInfo& info) { + CHECK(info.NumChannels <= MAX_INPUT_CHANNELS); + XAudio2::Locker.Lock(); XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferId - 1]; XAudio2::Locker.Unlock(); @@ -796,6 +760,7 @@ void AudioBackendXAudio2::Base_OnActiveDeviceChanged() void AudioBackendXAudio2::Base_SetDopplerFactor(float value) { + XAudio2::Settings.DopplerFactor = value; XAudio2::MarkAllDirty(); } @@ -803,6 +768,7 @@ void AudioBackendXAudio2::Base_SetVolume(float value) { if (XAudio2::MasteringVoice) { + XAudio2::Settings.Volume = 1.0f; // Volume is applied via MasteringVoice const HRESULT hr = XAudio2::MasteringVoice->SetVolume(value); XAUDIO2_CHECK_ERROR(SetVolume); } @@ -830,7 +796,8 @@ bool AudioBackendXAudio2::Base_Init() } XAUDIO2_VOICE_DETAILS details; XAudio2::MasteringVoice->GetVoiceDetails(&details); - XAudio2::SampleRate = details.InputSampleRate; +#if 0 + // TODO: implement multi-channel support (eg. 5.1, 7.1) XAudio2::Channels = details.InputChannels; hr = XAudio2::MasteringVoice->GetChannelMask(&XAudio2::ChannelMask); if (FAILED(hr)) @@ -838,19 +805,10 @@ bool AudioBackendXAudio2::Base_Init() LOG(Error, "Failed to get XAudio2 mastering voice channel mask. Error: 0x{0:x}", hr); return true; } - - // Initialize spatial audio subsystem - DWORD dwChannelMask; - XAudio2::MasteringVoice->GetChannelMask(&dwChannelMask); - hr = X3DAudioInitialize(dwChannelMask, X3DAUDIO_SPEED_OF_SOUND, XAudio2::X3DInstance); - if (FAILED(hr)) - { - LOG(Error, "Failed to initalize XAudio2 3D support. Error: 0x{0:x}", hr); - return true; - } - - // Info - LOG(Info, "XAudio2: {0} channels at {1} kHz (channel mask {2})", XAudio2::Channels, XAudio2::SampleRate / 1000.0f, XAudio2::ChannelMask); +#else + XAudio2::Channels = 2; +#endif + LOG(Info, "XAudio2: {0} channels at {1} kHz", XAudio2::Channels, details.InputSampleRate / 1000.0f); // Dummy device devices.Resize(1); @@ -864,53 +822,19 @@ void AudioBackendXAudio2::Base_Update() { // Update dirty voices const auto listener = XAudio2::GetListener(); - const float dopplerFactor = AudioSettings::Get()->DopplerFactor; - float matrixCoefficients[MAX_CHANNELS_MATRIX_SIZE]; - X3DAUDIO_DSP_SETTINGS dsp = { 0 }; - dsp.DstChannelCount = XAudio2::Channels; - dsp.pMatrixCoefficients = matrixCoefficients; + float outputMatrix[MAX_CHANNELS_MATRIX_SIZE]; for (int32 i = 0; i < XAudio2::Sources.Count(); i++) { auto& source = XAudio2::Sources[i]; if (source.IsFree() || !(source.IsDirty || XAudio2::ForceDirty)) continue; - dsp.SrcChannelCount = source.Data.ChannelCount; - if (source.Is3D && listener) - { - // 3D sound - X3DAudioCalculate(XAudio2::X3DInstance, &listener->Data, &source.Data, X3DAUDIO_CALCULATE_MATRIX | X3DAUDIO_CALCULATE_DOPPLER, &dsp); - } - else - { - // 2D sound - dsp.DopplerFactor = 1.0f; - Platform::MemoryClear(dsp.pMatrixCoefficients, sizeof(matrixCoefficients)); - dsp.pMatrixCoefficients[0] = 1.0f; - if (source.Format.nChannels == 1) - { - dsp.pMatrixCoefficients[1] = 1.0f; - } - else - { - dsp.pMatrixCoefficients[3] = 1.0f; - } - const float panLeft = Math::Min(1.0f - source.Pan, 1.0f); - const float panRight = Math::Min(1.0f + source.Pan, 1.0f); - if (source.Format.nChannels >= 2) - { - dsp.pMatrixCoefficients[0] *= panLeft; - dsp.pMatrixCoefficients[3] *= panRight; - } - } - if (source.IsForceMono3D) - { - // Hack to fix playback speed for 3D clip that has auto-converted stereo to mono at runtime - dsp.DopplerFactor *= 0.5f; - } - const float frequencyRatio = dopplerFactor * source.Pitch * dsp.DopplerFactor * source.DopplerFactor; - source.Voice->SetFrequencyRatio(frequencyRatio); - source.Voice->SetOutputMatrix(XAudio2::MasteringVoice, dsp.SrcChannelCount, dsp.DstChannelCount, dsp.pMatrixCoefficients); + auto mix = AudioBackendTools::CalculateSoundMix(XAudio2::Settings, *listener, source, XAudio2::Channels); + mix.VolumeIntoChannels(); + AudioBackendTools::MapChannels(source.Channels, XAudio2::Channels, mix.Channels, outputMatrix); + + source.Voice->SetFrequencyRatio(mix.Pitch); + source.Voice->SetOutputMatrix(XAudio2::MasteringVoice, source.Channels, XAudio2::Channels, outputMatrix); source.IsDirty = false; } From 161773a8bef656ee3912d1e458c871bbfe6966af Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 29 Feb 2024 01:41:53 +0100 Subject: [PATCH 096/198] Fix serialization bug when compiling with Clang --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 681483cf9..7a1963bd6 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -3132,6 +3132,8 @@ namespace Flax.Build.Bindings CppIncludeFilesList.Add(fileInfo.Name); CppIncludeFilesList.AddRange(CppIncludeFiles); CppIncludeFilesList.Sort(); + if (CppIncludeFilesList.Remove("Engine/Serialization/Serialization.h")) + CppIncludeFilesList.Add("Engine/Serialization/Serialization.h"); foreach (var path in CppIncludeFilesList) header.AppendFormat("#include \"{0}\"", path).AppendLine(); contents.Insert(headerPos, header.ToString()); From 816fb9da6f4ccc48cbe98e436682ff9b5ff98704 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 7 Mar 2024 20:41:53 +0100 Subject: [PATCH 097/198] Fix crash on Vulkan Swapchain init if GPU driver reports different image count --- .../GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp index 039efbc74..296d6ebb1 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp @@ -346,7 +346,7 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) height = Math::Clamp(height, surfProperties.minImageExtent.height, surfProperties.maxImageExtent.height); if (width <= 0 || height <= 0) { - LOG(Error, "Vulkan SwapChain dimensions are invalid {}x{} (minImageExtent={}x{}, maxImageExtent={}x{})", width, height, surfProperties.minImageExtent.width, surfProperties.minImageExtent.height, surfProperties.maxImageExtent.width, surfProperties.maxImageExtent.height); + LOG(Error, "Vulkan swapchain dimensions are invalid {}x{} (minImageExtent={}x{}, maxImageExtent={}x{})", width, height, surfProperties.minImageExtent.width, surfProperties.minImageExtent.height, surfProperties.maxImageExtent.width, surfProperties.maxImageExtent.height); return true; } VkSwapchainCreateInfoKHR swapChainInfo; @@ -386,11 +386,12 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) { uint32 imagesCount; VALIDATE_VULKAN_RESULT(vkGetSwapchainImagesKHR(device, _swapChain, &imagesCount, nullptr)); - ASSERT(imagesCount >= VULKAN_BACK_BUFFERS_COUNT && imagesCount <= VULKAN_BACK_BUFFERS_COUNT_MAX); + imagesCount = Math::Min(imagesCount, VULKAN_BACK_BUFFERS_COUNT_MAX); + if (imagesCount != VULKAN_BACK_BUFFERS_COUNT) + LOG(Warning, "Vulkan swapchain got less backbuffers than requried {} (instead of {})", imagesCount, VULKAN_BACK_BUFFERS_COUNT); - Array> images; - images.Resize(imagesCount); - VALIDATE_VULKAN_RESULT(vkGetSwapchainImagesKHR(device, _swapChain, &imagesCount, images.Get())); + VkImage images[VULKAN_BACK_BUFFERS_COUNT_MAX]; + VALIDATE_VULKAN_RESULT(vkGetSwapchainImagesKHR(device, _swapChain, &imagesCount, images)); _backBuffers.Resize(imagesCount); VkExtent3D extent; @@ -399,7 +400,7 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) extent.depth = 1; for (uint32 i = 0; i < imagesCount; i++) { - _backBuffers[i].Setup(this, images[i], _format, extent); + _backBuffers.Get()[i].Setup(this, images[i], _format, extent); } } From f4a352ec8de8d792b74859d60b929fbcd37f31a9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 8 Mar 2024 13:10:36 +0100 Subject: [PATCH 098/198] Add support for animating `LocalizedString` value in Scene Animation --- .../Timeline/Tracks/KeyframesPropertyTrack.cs | 88 ++++++++---- .../Editor/GUI/Timeline/Tracks/ObjectTrack.cs | 1 + .../SceneAnimations/SceneAnimation.cpp | 20 ++- .../SceneAnimations/SceneAnimation.h | 1 + .../SceneAnimations/SceneAnimationPlayer.cpp | 126 +++++++++++++----- 5 files changed, 175 insertions(+), 61 deletions(-) diff --git a/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs b/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs index dbff7267a..ed1a53981 100644 --- a/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs @@ -7,6 +7,8 @@ using System.Reflection; using System.Runtime.InteropServices; using System.Text; using FlaxEditor.GUI.Timeline.Undo; +using FlaxEditor.Scripting; +using FlaxEditor.Utilities; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Utilities; @@ -56,7 +58,6 @@ namespace FlaxEditor.GUI.Timeline.Tracks throw new Exception("Invalid track data."); var keyframes = new KeyframesEditor.Keyframe[keyframesCount]; - var dataBuffer = new byte[e.ValueSize]; var propertyType = TypeUtils.GetManagedType(e.MemberTypeName); if (propertyType == null) { @@ -67,20 +68,40 @@ namespace FlaxEditor.GUI.Timeline.Tracks return; } - GCHandle handle = GCHandle.Alloc(dataBuffer, GCHandleType.Pinned); - for (int i = 0; i < keyframesCount; i++) + if (e.ValueSize != 0) { - var time = stream.ReadSingle(); - stream.Read(dataBuffer, 0, e.ValueSize); - var value = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), propertyType); - - keyframes[i] = new KeyframesEditor.Keyframe + // POD value type - use raw memory + var dataBuffer = new byte[e.ValueSize]; + GCHandle handle = GCHandle.Alloc(dataBuffer, GCHandleType.Pinned); + for (int i = 0; i < keyframesCount; i++) { - Time = time, - Value = value, - }; + var time = stream.ReadSingle(); + stream.Read(dataBuffer, 0, e.ValueSize); + var value = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), propertyType); + + keyframes[i] = new KeyframesEditor.Keyframe + { + Time = time, + Value = value, + }; + } + handle.Free(); + } + else + { + // Generic value - use Json storage (as UTF-8) + for (int i = 0; i < keyframesCount; i++) + { + var time = stream.ReadSingle(); + var len = stream.ReadInt32(); + var value = len != 0 ? FlaxEngine.Json.JsonSerializer.Deserialize(Encoding.UTF8.GetString(stream.ReadBytes(len)), propertyType) : null; + keyframes[i] = new KeyframesEditor.Keyframe + { + Time = time, + Value = value, + }; + } } - handle.Free(); e.Keyframes.DefaultValue = e.GetDefaultValue(propertyType); e.Keyframes.SetKeyframes(keyframes); @@ -113,17 +134,35 @@ namespace FlaxEditor.GUI.Timeline.Tracks stream.Write(propertyTypeNameData); stream.Write('\0'); - var dataBuffer = new byte[e.ValueSize]; - IntPtr ptr = Marshal.AllocHGlobal(e.ValueSize); - for (int i = 0; i < keyframes.Count; i++) + if (e.ValueSize != 0) { - var keyframe = keyframes[i]; - Marshal.StructureToPtr(keyframe.Value, ptr, true); - Marshal.Copy(ptr, dataBuffer, 0, e.ValueSize); - stream.Write(keyframe.Time); - stream.Write(dataBuffer); + // POD value type - use raw memory + var dataBuffer = new byte[e.ValueSize]; + IntPtr ptr = Marshal.AllocHGlobal(e.ValueSize); + for (int i = 0; i < keyframes.Count; i++) + { + var keyframe = keyframes[i]; + Marshal.StructureToPtr(keyframe.Value, ptr, true); + Marshal.Copy(ptr, dataBuffer, 0, e.ValueSize); + stream.Write(keyframe.Time); + stream.Write(dataBuffer); + } + Marshal.FreeHGlobal(ptr); + } + else + { + // Generic value - use Json storage (as UTF-8) + for (int i = 0; i < keyframes.Count; i++) + { + var keyframe = keyframes[i]; + stream.Write(keyframe.Time); + var json = keyframe.Value != null ? FlaxEngine.Json.JsonSerializer.Serialize(keyframe.Value) : null; + var len = json?.Length ?? 0; + stream.Write(len); + if (len > 0) + stream.Write(Encoding.UTF8.GetBytes(json)); + } } - Marshal.FreeHGlobal(ptr); } private byte[] _keyframesEditingStartData; @@ -281,7 +320,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks private void OnKeyframesEditingEnd() { var after = EditTrackAction.CaptureData(this); - if (!Utils.ArraysEqual(_keyframesEditingStartData, after)) + if (!FlaxEngine.Utils.ArraysEqual(_keyframesEditingStartData, after)) Timeline.AddBatchedUndoAction(new EditTrackAction(Timeline, this, _keyframesEditingStartData, after)); _keyframesEditingStartData = null; } @@ -308,7 +347,10 @@ namespace FlaxEditor.GUI.Timeline.Tracks /// The default value. protected virtual object GetDefaultValue(Type propertyType) { - return Activator.CreateInstance(propertyType); + var value = TypeUtils.GetDefaultValue(new ScriptType(propertyType)); + if (value == null) + value = Activator.CreateInstance(propertyType); + return value; } /// diff --git a/Source/Editor/GUI/Timeline/Tracks/ObjectTrack.cs b/Source/Editor/GUI/Timeline/Tracks/ObjectTrack.cs index eccd9ab06..f6fb4b4b2 100644 --- a/Source/Editor/GUI/Timeline/Tracks/ObjectTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/ObjectTrack.cs @@ -424,6 +424,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks { typeof(Guid), KeyframesPropertyTrack.GetArchetype() }, { typeof(DateTime), KeyframesPropertyTrack.GetArchetype() }, { typeof(TimeSpan), KeyframesPropertyTrack.GetArchetype() }, + { typeof(LocalizedString), KeyframesPropertyTrack.GetArchetype() }, { typeof(string), StringPropertyTrack.GetArchetype() }, }; } diff --git a/Source/Engine/Animations/SceneAnimations/SceneAnimation.cpp b/Source/Engine/Animations/SceneAnimations/SceneAnimation.cpp index 20924b5cc..5d75b676c 100644 --- a/Source/Engine/Animations/SceneAnimations/SceneAnimation.cpp +++ b/Source/Engine/Animations/SceneAnimations/SceneAnimation.cpp @@ -281,10 +281,26 @@ Asset::LoadResult SceneAnimation::load() track.TrackStateIndex = TrackStatesCount++; trackRuntime->PropertyName = stream.Move(trackData->PropertyNameLength + 1); trackRuntime->PropertyTypeName = stream.Move(trackData->PropertyTypeNameLength + 1); - const int32 keyframesDataSize = trackData->KeyframesCount * (sizeof(float) + trackData->ValueSize); + int32 keyframesDataSize = trackData->KeyframesCount * (sizeof(float) + trackData->ValueSize); + if (trackData->ValueSize == 0) + { + // When using json data (from non-POD types) read the sum of all keyframes data + const int32 keyframesDataStart = stream.GetPosition(); + for (int32 j = 0; j < trackData->KeyframesCount; j++) + { + stream.Move(); // Time + int32 jsonLen; + stream.ReadInt32(&jsonLen); + stream.Move(jsonLen); + } + const int32 keyframesDataEnd = stream.GetPosition(); + stream.SetPosition(keyframesDataStart); + keyframesDataSize = keyframesDataEnd - keyframesDataStart; + } trackRuntime->ValueSize = trackData->ValueSize; trackRuntime->KeyframesCount = trackData->KeyframesCount; trackRuntime->Keyframes = stream.Move(keyframesDataSize); + trackRuntime->KeyframesSize = keyframesDataSize; needsParent = true; break; } @@ -298,6 +314,7 @@ Asset::LoadResult SceneAnimation::load() trackRuntime->PropertyName = stream.Move(trackData->PropertyNameLength + 1); trackRuntime->PropertyTypeName = stream.Move(trackData->PropertyTypeNameLength + 1); const int32 keyframesDataSize = trackData->KeyframesCount * (sizeof(float) + trackData->ValueSize * 3); + ASSERT(trackData->ValueSize > 0); trackRuntime->ValueSize = trackData->ValueSize; trackRuntime->KeyframesCount = trackData->KeyframesCount; trackRuntime->Keyframes = stream.Move(keyframesDataSize); @@ -375,6 +392,7 @@ Asset::LoadResult SceneAnimation::load() trackRuntime->PropertyName = stream.Move(trackData->PropertyNameLength + 1); trackRuntime->PropertyTypeName = stream.Move(trackData->PropertyTypeNameLength + 1); trackRuntime->ValueSize = trackData->ValueSize; + ASSERT(trackData->ValueSize > 0); trackRuntime->KeyframesCount = trackData->KeyframesCount; const auto keyframesTimes = (float*)((byte*)trackRuntime + sizeof(StringPropertyTrack::Runtime)); const auto keyframesLengths = (int32*)((byte*)keyframesTimes + sizeof(float) * trackData->KeyframesCount); diff --git a/Source/Engine/Animations/SceneAnimations/SceneAnimation.h b/Source/Engine/Animations/SceneAnimations/SceneAnimation.h index bd1091214..f6beeb416 100644 --- a/Source/Engine/Animations/SceneAnimations/SceneAnimation.h +++ b/Source/Engine/Animations/SceneAnimations/SceneAnimation.h @@ -289,6 +289,7 @@ public: /// The keyframes array (items count is KeyframesCount). Each keyframe is represented by pair of time (of type float) and the value data (of size ValueSize). /// void* Keyframes; + int32 KeyframesSize; }; }; diff --git a/Source/Engine/Animations/SceneAnimations/SceneAnimationPlayer.cpp b/Source/Engine/Animations/SceneAnimations/SceneAnimationPlayer.cpp index 982428057..ce23b92a4 100644 --- a/Source/Engine/Animations/SceneAnimations/SceneAnimationPlayer.cpp +++ b/Source/Engine/Animations/SceneAnimations/SceneAnimationPlayer.cpp @@ -7,6 +7,7 @@ #include "Engine/Level/SceneObjectsFactory.h" #include "Engine/Level/Actors/Camera.h" #include "Engine/Serialization/Serialization.h" +#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Audio/AudioClip.h" #include "Engine/Audio/AudioSource.h" #include "Engine/Graphics/RenderTask.h" @@ -19,6 +20,7 @@ #include "Engine/Scripting/ManagedCLR/MField.h" #include "Engine/Scripting/ManagedCLR/MClass.h" #include "Engine/Scripting/ManagedCLR/MMethod.h" +#include "Engine/Scripting/Internal/ManagedSerialization.h" // This could be Update, LateUpdate or FixedUpdate #define UPDATE_POINT Update @@ -370,47 +372,96 @@ bool SceneAnimationPlayer::TickPropertyTrack(int32 trackIndex, int32 stateIndexO case SceneAnimation::Track::Types::KeyframesProperty: case SceneAnimation::Track::Types::ObjectReferenceProperty: { - const auto trackDataKeyframes = track.GetRuntimeData(); - const int32 count = trackDataKeyframes->KeyframesCount; + const auto trackRuntime = track.GetRuntimeData(); + const int32 count = trackRuntime->KeyframesCount; if (count == 0) return false; - // Find the keyframe at time - int32 keyframeSize = sizeof(float) + trackDataKeyframes->ValueSize; -#define GET_KEY_TIME(idx) *(float*)((byte*)trackDataKeyframes->Keyframes + keyframeSize * (idx)) - const float keyTime = Math::Clamp(time, 0.0f, GET_KEY_TIME(count - 1)); - int32 start = 0; - int32 searchLength = count; - while (searchLength > 0) + // If size is 0 then track uses Json storage for keyframes data (variable memory length of keyframes), otherwise it's optimized simple data with O(1) access + if (trackRuntime->ValueSize != 0) { - const int32 half = searchLength >> 1; - int32 mid = start + half; - if (keyTime < GET_KEY_TIME(mid)) + // Find the keyframe at time (binary search) + int32 keyframeSize = sizeof(float) + trackRuntime->ValueSize; +#define GET_KEY_TIME(idx) *(float*)((byte*)trackRuntime->Keyframes + keyframeSize * (idx)) + const float keyTime = Math::Clamp(time, 0.0f, GET_KEY_TIME(count - 1)); + int32 start = 0; + int32 searchLength = count; + while (searchLength > 0) { - searchLength = half; + const int32 half = searchLength >> 1; + int32 mid = start + half; + if (keyTime < GET_KEY_TIME(mid)) + { + searchLength = half; + } + else + { + start = mid + 1; + searchLength -= half + 1; + } + } + int32 leftKey = Math::Max(0, start - 1); +#undef GET_KEY_TIME + + // Return the value + void* value = (void*)((byte*)trackRuntime->Keyframes + keyframeSize * (leftKey) + sizeof(float)); + if (track.Type == SceneAnimation::Track::Types::ObjectReferenceProperty) + { + // Object ref track uses Guid for object Id storage + Guid id = *(Guid*)value; + _objectsMapping.TryGet(id, id); + auto obj = Scripting::FindObject(id); + value = obj ? obj->GetOrCreateManagedInstance() : nullptr; + *(void**)target = value; } else { - start = mid + 1; - searchLength -= half + 1; + // POD memory + Platform::MemoryCopy(target, value, trackRuntime->ValueSize); } } - int32 leftKey = Math::Max(0, start - 1); -#undef GET_KEY_TIME - - // Return the value - void* value = (void*)((byte*)trackDataKeyframes->Keyframes + keyframeSize * (leftKey) + sizeof(float)); - if (track.Type == SceneAnimation::Track::Types::ObjectReferenceProperty) - { - Guid id = *(Guid*)value; - _objectsMapping.TryGet(id, id); - auto obj = Scripting::FindObject(id); - value = obj ? obj->GetOrCreateManagedInstance() : nullptr; - *(void**)target = value; - } else { - Platform::MemoryCopy(target, value, trackDataKeyframes->ValueSize); + // Clear pointer + *(void**)target = nullptr; + + // Find the keyframe at time (linear search) + MemoryReadStream stream((byte*)trackRuntime->Keyframes, trackRuntime->KeyframesSize); + int32 prevKeyPos = sizeof(float); + int32 jsonLen; + for (int32 key = 0; key < count; key++) + { + float keyTime; + stream.ReadFloat(&keyTime); + if (keyTime > time) + break; + prevKeyPos = stream.GetPosition(); + stream.ReadInt32(&jsonLen); + stream.Move(jsonLen); + } + + // Read json text + stream.SetPosition(prevKeyPos); + stream.ReadInt32(&jsonLen); + const StringAnsiView json((const char*)stream.GetPositionHandle(), jsonLen); + + // Create empty value of the keyframe type + const auto trackData = track.GetData(); + const StringAnsiView propertyTypeName(trackRuntime->PropertyTypeName, trackData->PropertyTypeNameLength); + MClass* klass = Scripting::FindClass(propertyTypeName); + if (!klass) + return false; + MObject* obj = MCore::Object::New(klass); + if (!obj) + return false; + if (!klass->IsValueType()) + MCore::Object::Init(obj); + + // Deserialize value from json + ManagedSerialization::Deserialize(json, obj); + + // Set value + *(void**)target = obj; } break; } @@ -479,13 +530,13 @@ bool SceneAnimationPlayer::TickPropertyTrack(int32 trackIndex, int32 stateIndexO } case SceneAnimation::Track::Types::StringProperty: { - const auto trackDataKeyframes = track.GetRuntimeData(); - const int32 count = trackDataKeyframes->KeyframesCount; + const auto trackRuntime = track.GetRuntimeData(); + const int32 count = trackRuntime->KeyframesCount; if (count == 0) return false; - const auto keyframesTimes = (float*)((byte*)trackDataKeyframes + sizeof(SceneAnimation::StringPropertyTrack::Runtime)); - const auto keyframesLengths = (int32*)((byte*)keyframesTimes + sizeof(float) * trackDataKeyframes->KeyframesCount); - const auto keyframesValues = (Char**)((byte*)keyframesLengths + sizeof(int32) * trackDataKeyframes->KeyframesCount); + const auto keyframesTimes = (float*)((byte*)trackRuntime + sizeof(SceneAnimation::StringPropertyTrack::Runtime)); + const auto keyframesLengths = (int32*)((byte*)keyframesTimes + sizeof(float) * trackRuntime->KeyframesCount); + const auto keyframesValues = (Char**)((byte*)keyframesLengths + sizeof(int32) * trackRuntime->KeyframesCount); // Find the keyframe at time #define GET_KEY_TIME(idx) keyframesTimes[idx] @@ -522,7 +573,7 @@ bool SceneAnimationPlayer::TickPropertyTrack(int32 trackIndex, int32 stateIndexO auto& childTrack = anim->Tracks[childTrackIndex]; if (childTrack.Disabled || childTrack.ParentIndex != trackIndex) continue; - const auto childTrackRuntimeData = childTrack.GetRuntimeData(); + const auto childTrackRuntime = childTrack.GetRuntimeData(); auto& childTrackState = _tracks[stateIndexOffset + childTrack.TrackStateIndex]; // Cache field @@ -532,7 +583,7 @@ bool SceneAnimationPlayer::TickPropertyTrack(int32 trackIndex, int32 stateIndexO if (!type) continue; MClass* mclass = MCore::Type::GetClass(type); - childTrackState.Field = mclass->GetField(childTrackRuntimeData->PropertyName); + childTrackState.Field = mclass->GetField(childTrackRuntime->PropertyName); if (!childTrackState.Field) continue; } @@ -956,7 +1007,8 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3 if (TickPropertyTrack(j, stateIndexOffset, anim, time, track, state, value)) { // Set the value - if (MCore::Type::IsPointer(valueType)) + auto valueTypes = MCore::Type::GetType(valueType); + if (valueTypes == MTypes::Object || MCore::Type::IsPointer(valueType)) value = (void*)*(intptr*)value; if (state.Property) { From fef2e09915443adbdbccb10f5d3cbe39856d97ba Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 8 Mar 2024 13:12:55 +0100 Subject: [PATCH 099/198] Another fix attemp for vulkan driver on Linux --- Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp index 296d6ebb1..f32625203 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp @@ -386,8 +386,8 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) { uint32 imagesCount; VALIDATE_VULKAN_RESULT(vkGetSwapchainImagesKHR(device, _swapChain, &imagesCount, nullptr)); - imagesCount = Math::Min(imagesCount, VULKAN_BACK_BUFFERS_COUNT_MAX); - if (imagesCount != VULKAN_BACK_BUFFERS_COUNT) + imagesCount = Math::Min(imagesCount, VULKAN_BACK_BUFFERS_COUNT); + if (imagesCount < VULKAN_BACK_BUFFERS_COUNT) LOG(Warning, "Vulkan swapchain got less backbuffers than requried {} (instead of {})", imagesCount, VULKAN_BACK_BUFFERS_COUNT); VkImage images[VULKAN_BACK_BUFFERS_COUNT_MAX]; From 584c073df1ee6e97d4ecc0ec887b9a1ec2721e49 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 8 Mar 2024 14:26:48 +0100 Subject: [PATCH 100/198] Reuse various serialization code --- Source/Engine/Serialization/JsonSerializer.cs | 10 +- Source/Engine/UI/UICanvas.cpp | 7 +- Source/Engine/UI/UICanvas.cs | 124 +++--------------- 3 files changed, 23 insertions(+), 118 deletions(-) diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs index b86b556dc..0e4b88e50 100644 --- a/Source/Engine/Serialization/JsonSerializer.cs +++ b/Source/Engine/Serialization/JsonSerializer.cs @@ -246,15 +246,7 @@ namespace FlaxEngine.Json /// The output json string. public static string Serialize(object obj, bool isManagedOnly = false) { - Type type = obj.GetType(); - var cache = isManagedOnly ? CacheManagedOnly.Value : Cache.Value; - Current.Value = cache; - - cache.WriteBegin(); - cache.SerializerWriter.Serialize(cache.JsonWriter, obj, type); - cache.WriteEnd(); - - return cache.StringBuilder.ToString(); + return Serialize(obj, obj.GetType(), isManagedOnly); } /// diff --git a/Source/Engine/UI/UICanvas.cpp b/Source/Engine/UI/UICanvas.cpp index 964c4a85f..d307bf7d6 100644 --- a/Source/Engine/UI/UICanvas.cpp +++ b/Source/Engine/UI/UICanvas.cpp @@ -12,7 +12,6 @@ #else // Cached methods (FlaxEngine.CSharp.dll is loaded only once) MMethod* UICanvas_Serialize = nullptr; -MMethod* UICanvas_SerializeDiff = nullptr; MMethod* UICanvas_Deserialize = nullptr; MMethod* UICanvas_PostDeserialize = nullptr; MMethod* UICanvas_Enable = nullptr; @@ -45,7 +44,6 @@ UICanvas::UICanvas(const SpawnParams& params) if (UICanvas_Serialize == nullptr) { MClass* mclass = GetClass(); - UICanvas_SerializeDiff = mclass->GetMethod("SerializeDiff", 1); UICanvas_Deserialize = mclass->GetMethod("Deserialize", 1); UICanvas_PostDeserialize = mclass->GetMethod("PostDeserialize"); UICanvas_Enable = mclass->GetMethod("Enable"); @@ -55,7 +53,7 @@ UICanvas::UICanvas(const SpawnParams& params) #endif UICanvas_EndPlay = mclass->GetMethod("EndPlay"); UICanvas_ParentChanged = mclass->GetMethod("ParentChanged"); - UICanvas_Serialize = mclass->GetMethod("Serialize"); + UICanvas_Serialize = mclass->GetMethod("Serialize", 1); Platform::MemoryBarrier(); } #endif @@ -83,8 +81,7 @@ void UICanvas::Serialize(SerializeStream& stream, const void* otherObj) void* params[1]; params[0] = other ? other->GetOrCreateManagedInstance() : nullptr; MObject* exception = nullptr; - auto method = other ? UICanvas_SerializeDiff : UICanvas_Serialize; - auto invokeResultStr = (MString*)method->Invoke(GetOrCreateManagedInstance(), params, &exception); + auto invokeResultStr = (MString*)UICanvas_Serialize->Invoke(GetOrCreateManagedInstance(), params, &exception); if (exception) { MException ex(exception); diff --git a/Source/Engine/UI/UICanvas.cs b/Source/Engine/UI/UICanvas.cs index 2d12dc12c..db78e428f 100644 --- a/Source/Engine/UI/UICanvas.cs +++ b/Source/Engine/UI/UICanvas.cs @@ -575,8 +575,9 @@ namespace FlaxEngine } } - internal string Serialize() + internal string Serialize(UICanvas other) { + bool noOther = other == null; StringBuilder sb = new StringBuilder(256); StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture); using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) @@ -587,137 +588,52 @@ namespace FlaxEngine jsonWriter.WriteStartObject(); - jsonWriter.WritePropertyName("RenderMode"); - jsonWriter.WriteValue(_renderMode); - - jsonWriter.WritePropertyName("RenderLocation"); - jsonWriter.WriteValue(RenderLocation); - - jsonWriter.WritePropertyName("Order"); - jsonWriter.WriteValue(Order); - - jsonWriter.WritePropertyName("ReceivesEvents"); - jsonWriter.WriteValue(ReceivesEvents); - - jsonWriter.WritePropertyName("IgnoreDepth"); - jsonWriter.WriteValue(IgnoreDepth); - - jsonWriter.WritePropertyName("RenderCamera"); - jsonWriter.WriteValue(Json.JsonSerializer.GetStringID(RenderCamera)); - - jsonWriter.WritePropertyName("Distance"); - jsonWriter.WriteValue(Distance); - - if (RenderMode == CanvasRenderMode.WorldSpace || RenderMode == CanvasRenderMode.WorldSpaceFaceCamera) - { - jsonWriter.WritePropertyName("Size"); - jsonWriter.WriteStartObject(); - jsonWriter.WritePropertyName("X"); - jsonWriter.WriteValue(Size.X); - jsonWriter.WritePropertyName("Y"); - jsonWriter.WriteValue(Size.Y); - jsonWriter.WriteEndObject(); - } - - jsonWriter.WritePropertyName("NavigationInputRepeatDelay"); - jsonWriter.WriteValue(NavigationInputRepeatDelay); - jsonWriter.WritePropertyName("NavigationInputRepeatRate"); - jsonWriter.WriteValue(NavigationInputRepeatRate); - - jsonWriter.WritePropertyName("NavigateUp"); - jsonWriter.WriteStartObject(); - jsonWriter.WritePropertyName("Name"); - jsonWriter.WriteValue(NavigateUp.Name); - jsonWriter.WriteEndObject(); - - jsonWriter.WritePropertyName("NavigateDown"); - jsonWriter.WriteStartObject(); - jsonWriter.WritePropertyName("Name"); - jsonWriter.WriteValue(NavigateDown.Name); - jsonWriter.WriteEndObject(); - - jsonWriter.WritePropertyName("NavigateLeft"); - jsonWriter.WriteStartObject(); - jsonWriter.WritePropertyName("Name"); - jsonWriter.WriteValue(NavigateLeft.Name); - jsonWriter.WriteEndObject(); - - jsonWriter.WritePropertyName("NavigateRight"); - jsonWriter.WriteStartObject(); - jsonWriter.WritePropertyName("Name"); - jsonWriter.WriteValue(NavigateRight.Name); - jsonWriter.WriteEndObject(); - - jsonWriter.WritePropertyName("NavigateSubmit"); - jsonWriter.WriteStartObject(); - jsonWriter.WritePropertyName("Name"); - jsonWriter.WriteValue(NavigateSubmit.Name); - jsonWriter.WriteEndObject(); - - jsonWriter.WriteEndObject(); - } - - return sw.ToString(); - } - - internal string SerializeDiff(UICanvas other) - { - StringBuilder sb = new StringBuilder(256); - StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture); - using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) - { - jsonWriter.IndentChar = '\t'; - jsonWriter.Indentation = 1; - jsonWriter.Formatting = Formatting.Indented; - - jsonWriter.WriteStartObject(); - - if (_renderMode != other._renderMode) + if (noOther || _renderMode != other._renderMode) { jsonWriter.WritePropertyName("RenderMode"); jsonWriter.WriteValue(_renderMode); } - if (RenderLocation != other.RenderLocation) + if (noOther || RenderLocation != other.RenderLocation) { jsonWriter.WritePropertyName("RenderLocation"); jsonWriter.WriteValue(RenderLocation); } - if (Order != other.Order) + if (noOther || Order != other.Order) { jsonWriter.WritePropertyName("Order"); jsonWriter.WriteValue(Order); } - if (ReceivesEvents != other.ReceivesEvents) + if (noOther || ReceivesEvents != other.ReceivesEvents) { jsonWriter.WritePropertyName("ReceivesEvents"); jsonWriter.WriteValue(ReceivesEvents); } - if (IgnoreDepth != other.IgnoreDepth) + if (noOther || IgnoreDepth != other.IgnoreDepth) { jsonWriter.WritePropertyName("IgnoreDepth"); jsonWriter.WriteValue(IgnoreDepth); } - if (RenderCamera != other.RenderCamera) + if (noOther || RenderCamera != other.RenderCamera) { jsonWriter.WritePropertyName("RenderCamera"); jsonWriter.WriteValue(Json.JsonSerializer.GetStringID(RenderCamera)); } - if (Mathf.Abs(Distance - other.Distance) > Mathf.Epsilon) + if (noOther || Mathf.Abs(Distance - other.Distance) > Mathf.Epsilon) { jsonWriter.WritePropertyName("Distance"); jsonWriter.WriteValue(Distance); } - if ((RenderMode == CanvasRenderMode.WorldSpace || - RenderMode == CanvasRenderMode.WorldSpaceFaceCamera || - other.RenderMode == CanvasRenderMode.WorldSpace || - other.RenderMode == CanvasRenderMode.WorldSpaceFaceCamera) && Size != other.Size) + bool saveSize = RenderMode == CanvasRenderMode.WorldSpace || RenderMode == CanvasRenderMode.WorldSpaceFaceCamera; + if (!noOther) + saveSize = (saveSize || other.RenderMode == CanvasRenderMode.WorldSpace || other.RenderMode == CanvasRenderMode.WorldSpaceFaceCamera) && Size != other.Size; + if (saveSize) { jsonWriter.WritePropertyName("Size"); jsonWriter.WriteStartObject(); @@ -728,17 +644,17 @@ namespace FlaxEngine jsonWriter.WriteEndObject(); } - if (!Mathf.NearEqual(NavigationInputRepeatDelay, other.NavigationInputRepeatDelay)) + if (noOther || !Mathf.NearEqual(NavigationInputRepeatDelay, other.NavigationInputRepeatDelay)) { jsonWriter.WritePropertyName("NavigationInputRepeatDelay"); jsonWriter.WriteValue(NavigationInputRepeatDelay); } - if (!Mathf.NearEqual(NavigationInputRepeatRate, other.NavigationInputRepeatRate)) + if (noOther || !Mathf.NearEqual(NavigationInputRepeatRate, other.NavigationInputRepeatRate)) { jsonWriter.WritePropertyName("NavigationInputRepeatRate"); jsonWriter.WriteValue(NavigationInputRepeatRate); } - if (NavigateUp.Name != other.NavigateUp.Name) + if (noOther || NavigateUp.Name != other.NavigateUp.Name) { jsonWriter.WritePropertyName("NavigateUp"); jsonWriter.WriteStartObject(); @@ -746,7 +662,7 @@ namespace FlaxEngine jsonWriter.WriteValue(NavigateUp.Name); jsonWriter.WriteEndObject(); } - if (NavigateDown.Name != other.NavigateDown.Name) + if (noOther || NavigateDown.Name != other.NavigateDown.Name) { jsonWriter.WritePropertyName("NavigateDown"); jsonWriter.WriteStartObject(); @@ -754,7 +670,7 @@ namespace FlaxEngine jsonWriter.WriteValue(NavigateDown.Name); jsonWriter.WriteEndObject(); } - if (NavigateLeft.Name != other.NavigateLeft.Name) + if (noOther || NavigateLeft.Name != other.NavigateLeft.Name) { jsonWriter.WritePropertyName("NavigateLeft"); jsonWriter.WriteStartObject(); @@ -762,7 +678,7 @@ namespace FlaxEngine jsonWriter.WriteValue(NavigateLeft.Name); jsonWriter.WriteEndObject(); } - if (NavigateRight.Name != other.NavigateRight.Name) + if (noOther || NavigateRight.Name != other.NavigateRight.Name) { jsonWriter.WritePropertyName("NavigateRight"); jsonWriter.WriteStartObject(); @@ -770,7 +686,7 @@ namespace FlaxEngine jsonWriter.WriteValue(NavigateRight.Name); jsonWriter.WriteEndObject(); } - if (NavigateSubmit.Name != other.NavigateSubmit.Name) + if (noOther || NavigateSubmit.Name != other.NavigateSubmit.Name) { jsonWriter.WritePropertyName("NavigateSubmit"); jsonWriter.WriteStartObject(); From d390938fb6887e8f7ebd4579f878653f32d0c1ae Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 8 Mar 2024 14:39:18 +0100 Subject: [PATCH 101/198] Minor improvements to usability --- .../Dedicated/UIControlEditor.cs | 54 +++++++++++-------- .../Editors/ActorTransformEditor.cs | 19 ++++--- .../GUI/Timeline/GUI/KeyframesEditor.cs | 2 +- .../Editor/GUI/Timeline/Tracks/ActorTrack.cs | 2 +- Source/Engine/Level/Actor.cs | 2 +- 5 files changed, 45 insertions(+), 34 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index cd0b534e0..27be8e538 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -589,25 +589,27 @@ namespace FlaxEditor.CustomEditors.Dedicated LayoutElementsContainer yEl; LayoutElementsContainer hEl; LayoutElementsContainer vEl; + Color axisColorX = ActorTransformEditor.AxisColorX; + Color axisColorY = ActorTransformEditor.AxisColorY; if (xEq) { - xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values)); - vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values)); + xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values), axisColorX); + vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values), axisColorX); } else { - xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values)); - vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values)); + xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values), axisColorX); + vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values), axisColorX); } if (yEq) { - yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values)); - hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values)); + yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values), axisColorY); + hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values), axisColorY); } else { - yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values)); - hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values)); + yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values), axisColorY); + hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values), axisColorY); } xEl.Control.AnchorMin = new Float2(0, xEl.Control.AnchorMin.Y); xEl.Control.AnchorMax = new Float2(0.5f, xEl.Control.AnchorMax.Y); @@ -624,28 +626,34 @@ namespace FlaxEditor.CustomEditors.Dedicated private VerticalPanelElement VerticalPanelWithoutMargin(LayoutElementsContainer cont) { - var horUp = cont.VerticalPanel(); - horUp.Panel.Margin = Margin.Zero; - return horUp; + var panel = cont.VerticalPanel(); + panel.Panel.Margin = Margin.Zero; + return panel; } private CustomElementsContainer UniformGridTwoByOne(LayoutElementsContainer cont) { - var horUp = cont.CustomContainer(); - horUp.CustomControl.SlotsHorizontally = 2; - horUp.CustomControl.SlotsVertically = 1; - horUp.CustomControl.SlotPadding = Margin.Zero; - horUp.CustomControl.ClipChildren = false; - return horUp; + var grid = cont.CustomContainer(); + grid.CustomControl.SlotsHorizontally = 2; + grid.CustomControl.SlotsVertically = 1; + grid.CustomControl.SlotPadding = Margin.Zero; + grid.CustomControl.ClipChildren = false; + return grid; } - private CustomElementsContainer UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values) + private CustomElementsContainer UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values, Color borderColor) { - CustomElementsContainer hor = UniformGridTwoByOne(el); - hor.CustomControl.SlotPadding = new Margin(5, 5, 0, 0); - LabelElement lab = hor.Label(text); - hor.Object(values); - return hor; + var grid = UniformGridTwoByOne(el); + grid.CustomControl.SlotPadding = new Margin(5, 5, 1, 1); + var label = grid.Label(text); + var editor = grid.Object(values); + if (editor is FloatEditor floatEditor && floatEditor.Element is FloatValueElement floatEditorElement) + { + var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; + floatEditorElement.ValueBox.BorderColor = Color.Lerp(borderColor, back, ActorTransformEditor.AxisGreyOutFactor); + floatEditorElement.ValueBox.BorderSelectedColor = borderColor; + } + return grid; } private bool _cachedXEq; diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 29a9f45e8..fb33d470c 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -26,6 +26,11 @@ namespace FlaxEditor.CustomEditors.Editors /// public static Color AxisColorZ = new Color(0.0f, 0.0235294f, 1.0f, 1.0f); + /// + /// The axes colors grey out scale when input field is not focused. + /// + public static float AxisGreyOutFactor = 0.6f; + /// /// Custom editor for actor position property. /// @@ -39,12 +44,11 @@ namespace FlaxEditor.CustomEditors.Editors // Override colors var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; - var grayOutFactor = 0.6f; - XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor); + XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor); XElement.ValueBox.BorderSelectedColor = AxisColorX; - YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor); + YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor); YElement.ValueBox.BorderSelectedColor = AxisColorY; - ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor); + ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor); ZElement.ValueBox.BorderSelectedColor = AxisColorZ; } } @@ -62,12 +66,11 @@ namespace FlaxEditor.CustomEditors.Editors // Override colors var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; - var grayOutFactor = 0.6f; - XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor); + XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor); XElement.ValueBox.BorderSelectedColor = AxisColorX; - YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor); + YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor); YElement.ValueBox.BorderSelectedColor = AxisColorY; - ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor); + ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor); ZElement.ValueBox.BorderSelectedColor = AxisColorZ; } } diff --git a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs index 5eb289e0a..797a0a044 100644 --- a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs +++ b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs @@ -904,7 +904,7 @@ namespace FlaxEditor.GUI var k = new Keyframe { Time = keyframesPos.X, - Value = DefaultValue, + Value = Utilities.Utils.CloneValue(DefaultValue), }; OnEditingStart(); AddKeyframe(k); diff --git a/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs b/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs index 95b5dc57f..795674289 100644 --- a/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs @@ -204,7 +204,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks b.TooltipText = Utilities.Utils.GetTooltip(actorNode.Actor); } } - menu.AddButton("Select...", OnClickedSelect).TooltipText = "Opens actor picker dialog to select the target actor for this track"; + menu.AddButton("Retarget...", OnClickedSelect).TooltipText = "Opens actor picker dialog to select the target actor for this track"; } /// diff --git a/Source/Engine/Level/Actor.cs b/Source/Engine/Level/Actor.cs index ca8485ffc..f7f03afff 100644 --- a/Source/Engine/Level/Actor.cs +++ b/Source/Engine/Level/Actor.cs @@ -390,7 +390,7 @@ namespace FlaxEngine } #if FLAX_EDITOR - internal bool ShowTransform => !(this is UIControl); + private bool ShowTransform => !(this is UIControl); #endif } } From fc029b018ef7f4452d8f4f0d3bd6103778599912 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 8 Mar 2024 14:56:34 +0100 Subject: [PATCH 102/198] Fix UI size changes when saving scenes or prefabs in Editor with different layout --- Source/Editor/Modules/SceneModule.cs | 42 ++++++++++++++++++++ Source/Editor/Windows/Assets/PrefabWindow.cs | 6 +++ 2 files changed, 48 insertions(+) diff --git a/Source/Editor/Modules/SceneModule.cs b/Source/Editor/Modules/SceneModule.cs index 302d1e02a..9aae0b72f 100644 --- a/Source/Editor/Modules/SceneModule.cs +++ b/Source/Editor/Modules/SceneModule.cs @@ -6,6 +6,7 @@ using System.IO; using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph.Actors; using FlaxEngine; +using FlaxEngine.GUI; using Object = FlaxEngine.Object; namespace FlaxEditor.Modules @@ -454,6 +455,41 @@ namespace FlaxEditor.Modules Profiler.EndEvent(); } + private Dictionary _uiRootSizes; + + internal void OnSaveStart(ContainerControl uiRoot) + { + // Force viewport UI to have fixed size during scene/prefabs saving to result in stable data (less mess in version control diffs) + if (_uiRootSizes == null) + _uiRootSizes = new Dictionary(); + _uiRootSizes[uiRoot] = uiRoot.Size; + uiRoot.Size = new Float2(1920, 1080); + } + + internal void OnSaveEnd(ContainerControl uiRoot) + { + // Restore cached size of the UI root container + if (_uiRootSizes != null && _uiRootSizes.Remove(uiRoot, out var size)) + { + uiRoot.Size = size; + } + } + + private void OnSceneSaving(Scene scene, Guid sceneId) + { + OnSaveStart(RootControl.GameRoot); + } + + private void OnSceneSaved(Scene scene, Guid sceneId) + { + OnSaveEnd(RootControl.GameRoot); + } + + private void OnSceneSaveError(Scene scene, Guid sceneId) + { + OnSaveEnd(RootControl.GameRoot); + } + private void OnSceneLoaded(Scene scene, Guid sceneId) { var startTime = DateTime.UtcNow; @@ -659,6 +695,9 @@ namespace FlaxEditor.Modules Root = new ScenesRootNode(); // Bind events + Level.SceneSaving += OnSceneSaving; + Level.SceneSaved += OnSceneSaved; + Level.SceneSaveError += OnSceneSaveError; Level.SceneLoaded += OnSceneLoaded; Level.SceneUnloading += OnSceneUnloading; Level.ActorSpawned += OnActorSpawned; @@ -673,6 +712,9 @@ namespace FlaxEditor.Modules public override void OnExit() { // Unbind events + Level.SceneSaving -= OnSceneSaving; + Level.SceneSaved -= OnSceneSaved; + Level.SceneSaveError -= OnSceneSaveError; Level.SceneLoaded -= OnSceneLoaded; Level.SceneUnloading -= OnSceneUnloading; Level.ActorSpawned -= OnActorSpawned; diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 5aa1486ee..2e3e700a2 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -355,6 +355,8 @@ namespace FlaxEditor.Windows.Assets try { + Editor.Scene.OnSaveStart(_viewport); + // Simply update changes Editor.Prefabs.ApplyAll(_viewport.Instance); @@ -371,6 +373,10 @@ namespace FlaxEditor.Windows.Assets throw; } + finally + { + Editor.Scene.OnSaveEnd(_viewport); + } } /// From 92c109c00368ad572edf3e6af27cd5546617a1eb Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 9 Mar 2024 23:47:39 +0200 Subject: [PATCH 103/198] Fix `PhysicsScene` change not working in physics actors --- Source/Engine/Physics/Actors/RigidBody.cpp | 2 +- Source/Engine/Physics/Colliders/Collider.cpp | 2 +- Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp | 7 +++++-- Source/Engine/Physics/PhysicsBackend.h | 2 +- Source/Engine/Physics/PhysicsBackendEmpty.cpp | 2 +- Source/Engine/Terrain/TerrainPatch.cpp | 2 +- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Physics/Actors/RigidBody.cpp b/Source/Engine/Physics/Actors/RigidBody.cpp index 870bba867..04672552c 100644 --- a/Source/Engine/Physics/Actors/RigidBody.cpp +++ b/Source/Engine/Physics/Actors/RigidBody.cpp @@ -569,7 +569,7 @@ void RigidBody::OnTransformChanged() void RigidBody::OnPhysicsSceneChanged(PhysicsScene* previous) { - PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _actor); + PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _actor, true); void* scene = GetPhysicsScene()->GetPhysicsScene(); PhysicsBackend::AddSceneActor(scene, _actor); const bool putToSleep = !_startAwake && GetEnableSimulation() && !GetIsKinematic() && IsActiveInHierarchy(); diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index 1ad6b4946..d8541a222 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -438,7 +438,7 @@ void Collider::OnPhysicsSceneChanged(PhysicsScene* previous) if (_staticActor != nullptr) { - PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _staticActor); + PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _staticActor, true); void* scene = GetPhysicsScene()->GetPhysicsScene(); PhysicsBackend::AddSceneActor(scene, _staticActor); } diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index 0c77738f6..5d86ed8c9 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -1998,11 +1998,14 @@ void PhysicsBackend::AddSceneActor(void* scene, void* actor) FlushLocker.Unlock(); } -void PhysicsBackend::RemoveSceneActor(void* scene, void* actor) +void PhysicsBackend::RemoveSceneActor(void* scene, void* actor, bool immediately) { auto scenePhysX = (ScenePhysX*)scene; FlushLocker.Lock(); - scenePhysX->RemoveActors.Add((PxActor*)actor); + if (immediately) + scenePhysX->Scene->removeActor(*(PxActor*)actor); + else + scenePhysX->RemoveActors.Add((PxActor*)actor); FlushLocker.Unlock(); } diff --git a/Source/Engine/Physics/PhysicsBackend.h b/Source/Engine/Physics/PhysicsBackend.h index 518201573..8bd493267 100644 --- a/Source/Engine/Physics/PhysicsBackend.h +++ b/Source/Engine/Physics/PhysicsBackend.h @@ -113,7 +113,7 @@ public: static void SetSceneBounceThresholdVelocity(void* scene, float value); static void SetSceneOrigin(void* scene, const Vector3& oldOrigin, const Vector3& newOrigin); static void AddSceneActor(void* scene, void* actor); - static void RemoveSceneActor(void* scene, void* actor); + static void RemoveSceneActor(void* scene, void* actor, bool immediately = false); static void AddSceneActorAction(void* scene, void* actor, ActionType action); #if COMPILE_WITH_PROFILER static void GetSceneStatistics(void* scene, PhysicsStatistics& result); diff --git a/Source/Engine/Physics/PhysicsBackendEmpty.cpp b/Source/Engine/Physics/PhysicsBackendEmpty.cpp index 26054c342..941813b2e 100644 --- a/Source/Engine/Physics/PhysicsBackendEmpty.cpp +++ b/Source/Engine/Physics/PhysicsBackendEmpty.cpp @@ -115,7 +115,7 @@ void PhysicsBackend::AddSceneActor(void* scene, void* actor) { } -void PhysicsBackend::RemoveSceneActor(void* scene, void* actor) +void PhysicsBackend::RemoveSceneActor(void* scene, void* actor, bool immediately) { } diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 053ab10e0..792b6c899 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -2580,7 +2580,7 @@ void TerrainPatch::Deserialize(DeserializeStream& stream, ISerializeModifier* mo void TerrainPatch::OnPhysicsSceneChanged(PhysicsScene* previous) { - PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _physicsActor); + PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _physicsActor, true); void* scene = _terrain->GetPhysicsScene()->GetPhysicsScene(); PhysicsBackend::AddSceneActor(scene, _physicsActor); } From b00b5bed0085fd59e1c515c15e3e658c9c611aa2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Mar 2024 21:11:35 +0100 Subject: [PATCH 104/198] Add `InputAxis` and `InputEvent` integration with stdlib features --- Source/Engine/Engine/InputAxis.cs | 38 ++++++++++++++++++++++++++---- Source/Engine/Engine/InputEvent.cs | 38 ++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Engine/InputAxis.cs b/Source/Engine/Engine/InputAxis.cs index 7dc51f025..ca77d2465 100644 --- a/Source/Engine/Engine/InputAxis.cs +++ b/Source/Engine/Engine/InputAxis.cs @@ -7,7 +7,7 @@ namespace FlaxEngine /// /// Virtual input axis binding. Helps with listening for a selected axis input. /// - public class InputAxis + public class InputAxis : IComparable, IComparable { /// /// The name of the axis to use. See . @@ -47,13 +47,13 @@ namespace FlaxEngine Input.AxisValueChanged += Handler; Name = name; } - + private void Handler(string name) { if (string.Equals(Name, name, StringComparison.OrdinalIgnoreCase)) ValueChanged?.Invoke(); } - + /// /// Finalizes an instance of the class. /// @@ -61,7 +61,7 @@ namespace FlaxEngine { Input.AxisValueChanged -= Handler; } - + /// /// Releases this object. /// @@ -70,5 +70,35 @@ namespace FlaxEngine Input.AxisValueChanged -= Handler; GC.SuppressFinalize(this); } + + /// + public int CompareTo(InputAxis other) + { + return string.Compare(Name, other.Name, StringComparison.Ordinal); + } + + /// + public int CompareTo(object obj) + { + return obj is InputAxis other ? CompareTo(other) : -1; + } + + /// + public override int GetHashCode() + { + return Name?.GetHashCode() ?? 0; + } + + /// + public override bool Equals(object obj) + { + return obj is InputAxis other && string.Equals(Name, other.Name, StringComparison.Ordinal); + } + + /// + public override string ToString() + { + return Name; + } } } diff --git a/Source/Engine/Engine/InputEvent.cs b/Source/Engine/Engine/InputEvent.cs index 10af5d531..e5653631f 100644 --- a/Source/Engine/Engine/InputEvent.cs +++ b/Source/Engine/Engine/InputEvent.cs @@ -7,7 +7,7 @@ namespace FlaxEngine /// /// Virtual input action binding. Helps with listening for a selected input event. /// - public class InputEvent + public class InputEvent : IComparable, IComparable { /// /// The name of the action to use. See . @@ -21,7 +21,7 @@ namespace FlaxEngine public bool Active => Input.GetAction(Name); /// - /// Returns the event state. Use Use , , to catch events without active waiting. + /// Returns the event state. Use , , to catch events without active waiting. /// public InputActionState State => Input.GetActionState(Name); @@ -35,12 +35,12 @@ namespace FlaxEngine /// Occurs when event is pressed (e.g. user pressed a key). Called before scripts update. /// public event Action Pressed; - + /// /// Occurs when event is being pressing (e.g. user pressing a key). Called before scripts update. /// public event Action Pressing; - + /// /// Occurs when event is released (e.g. user releases a key). Called before scripts update. /// @@ -102,5 +102,35 @@ namespace FlaxEngine Input.ActionTriggered -= Handler; GC.SuppressFinalize(this); } + + /// + public int CompareTo(InputEvent other) + { + return string.Compare(Name, other.Name, StringComparison.Ordinal); + } + + /// + public int CompareTo(object obj) + { + return obj is InputEvent other ? CompareTo(other) : -1; + } + + /// + public override int GetHashCode() + { + return Name?.GetHashCode() ?? 0; + } + + /// + public override bool Equals(object obj) + { + return obj is InputEvent other && string.Equals(Name, other.Name, StringComparison.Ordinal); + } + + /// + public override string ToString() + { + return Name; + } } } From 4a5ded0849a5ca9f9a98b2be7a8411e2ff3b5b2e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Mar 2024 23:28:16 +0100 Subject: [PATCH 105/198] Reuse various serialization code --- Source/Engine/UI/UIControl.cpp | 9 +++---- Source/Engine/UI/UIControl.cs | 49 +++++----------------------------- 2 files changed, 10 insertions(+), 48 deletions(-) diff --git a/Source/Engine/UI/UIControl.cpp b/Source/Engine/UI/UIControl.cpp index bfe820eba..aa54e3fc2 100644 --- a/Source/Engine/UI/UIControl.cpp +++ b/Source/Engine/UI/UIControl.cpp @@ -13,7 +13,6 @@ #else // Cached methods (FlaxEngine.CSharp.dll is loaded only once) MMethod* UIControl_Serialize = nullptr; -MMethod* UIControl_SerializeDiff = nullptr; MMethod* UIControl_Deserialize = nullptr; MMethod* UIControl_ParentChanged = nullptr; MMethod* UIControl_TransformChanged = nullptr; @@ -39,10 +38,10 @@ UIControl::UIControl(const SpawnParams& params) : Actor(params) { #if !COMPILE_WITHOUT_CSHARP + Platform::MemoryBarrier(); if (UIControl_Serialize == nullptr) { MClass* mclass = GetClass(); - UIControl_SerializeDiff = mclass->GetMethod("SerializeDiff", 2); UIControl_Deserialize = mclass->GetMethod("Deserialize", 2); UIControl_ParentChanged = mclass->GetMethod("ParentChanged"); UIControl_TransformChanged = mclass->GetMethod("TransformChanged"); @@ -50,7 +49,8 @@ UIControl::UIControl(const SpawnParams& params) UIControl_ActiveInTreeChanged = mclass->GetMethod("ActiveInTreeChanged"); UIControl_BeginPlay = mclass->GetMethod("BeginPlay"); UIControl_EndPlay = mclass->GetMethod("EndPlay"); - UIControl_Serialize = mclass->GetMethod("Serialize", 1); + UIControl_Serialize = mclass->GetMethod("Serialize", 2); + Platform::MemoryBarrier(); } #endif } @@ -82,8 +82,7 @@ void UIControl::Serialize(SerializeStream& stream, const void* otherObj) params[0] = &controlType; params[1] = other ? other->GetOrCreateManagedInstance() : nullptr; MObject* exception = nullptr; - const auto method = other ? UIControl_SerializeDiff : UIControl_Serialize; - const auto invokeResultStr = (MString*)method->Invoke(GetOrCreateManagedInstance(), params, &exception); + const auto invokeResultStr = (MString*)UIControl_Serialize->Invoke(GetOrCreateManagedInstance(), params, &exception); if (exception) { MException ex(exception); diff --git a/Source/Engine/UI/UIControl.cs b/Source/Engine/UI/UIControl.cs index 9cea0f6de..100f7f582 100644 --- a/Source/Engine/UI/UIControl.cs +++ b/Source/Engine/UI/UIControl.cs @@ -314,15 +314,15 @@ namespace FlaxEngine return FallbackParentGetDelegate?.Invoke(this); } - internal string Serialize(out string controlType) + internal string Serialize(out string controlType, UIControl other) { if (_control == null) { controlType = null; return null; } - var type = _control.GetType(); + var noDiff = other._control == null || other._control.GetType() != type; JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(Json.JsonSerializer.Settings); jsonSerializer.Formatting = Formatting.Indented; @@ -344,47 +344,10 @@ namespace FlaxEngine JsonSerializerInternalWriter serializerWriter = new JsonSerializerInternalWriter(jsonSerializer); - serializerWriter.Serialize(jsonWriter, _control, type); - } - - controlType = type.FullName; - return sw.ToString(); - } - - internal string SerializeDiff(out string controlType, UIControl other) - { - if (_control == null) - { - controlType = null; - return null; - } - var type = _control.GetType(); - if (other._control == null || other._control.GetType() != type) - { - return Serialize(out controlType); - } - - JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(Json.JsonSerializer.Settings); - jsonSerializer.Formatting = Formatting.Indented; - - StringBuilder sb = new StringBuilder(1024); - StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture); - using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) - { - // Prepare writer settings - jsonWriter.IndentChar = '\t'; - jsonWriter.Indentation = 1; - jsonWriter.Formatting = jsonSerializer.Formatting; - jsonWriter.DateFormatHandling = jsonSerializer.DateFormatHandling; - jsonWriter.DateTimeZoneHandling = jsonSerializer.DateTimeZoneHandling; - jsonWriter.FloatFormatHandling = jsonSerializer.FloatFormatHandling; - jsonWriter.StringEscapeHandling = jsonSerializer.StringEscapeHandling; - jsonWriter.Culture = jsonSerializer.Culture; - jsonWriter.DateFormatString = jsonSerializer.DateFormatString; - - JsonSerializerInternalWriter serializerWriter = new JsonSerializerInternalWriter(jsonSerializer); - - serializerWriter.SerializeDiff(jsonWriter, _control, type, other._control); + if (noDiff) + serializerWriter.Serialize(jsonWriter, _control, type); + else + serializerWriter.SerializeDiff(jsonWriter, _control, type, other._control); } controlType = string.Empty; From 1bbb2727b8a6c7e89d855ef7ee82fec9cb815b1d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Mar 2024 12:47:29 +0100 Subject: [PATCH 106/198] Further improve 4a5ded0849a5ca9f9a98b2be7a8411e2ff3b5b2e to use cached json serializer for UI --- Source/Engine/UI/UIControl.cs | 44 +++++++++++------------------------ 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/Source/Engine/UI/UIControl.cs b/Source/Engine/UI/UIControl.cs index 100f7f582..981c18685 100644 --- a/Source/Engine/UI/UIControl.cs +++ b/Source/Engine/UI/UIControl.cs @@ -318,48 +318,30 @@ namespace FlaxEngine { if (_control == null) { + // No control assigned controlType = null; return null; } + var type = _control.GetType(); - var noDiff = other._control == null || other._control.GetType() != type; + var prefabDiff = other != null && other._control != null && other._control.GetType() == type; - JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(Json.JsonSerializer.Settings); - jsonSerializer.Formatting = Formatting.Indented; + // Serialize control type when not using prefab diff serialization + controlType = prefabDiff ? string.Empty : type.FullName; - StringBuilder sb = new StringBuilder(1024); - StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture); - using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) - { - // Prepare writer settings - jsonWriter.IndentChar = '\t'; - jsonWriter.Indentation = 1; - jsonWriter.Formatting = jsonSerializer.Formatting; - jsonWriter.DateFormatHandling = jsonSerializer.DateFormatHandling; - jsonWriter.DateTimeZoneHandling = jsonSerializer.DateTimeZoneHandling; - jsonWriter.FloatFormatHandling = jsonSerializer.FloatFormatHandling; - jsonWriter.StringEscapeHandling = jsonSerializer.StringEscapeHandling; - jsonWriter.Culture = jsonSerializer.Culture; - jsonWriter.DateFormatString = jsonSerializer.DateFormatString; - - JsonSerializerInternalWriter serializerWriter = new JsonSerializerInternalWriter(jsonSerializer); - - if (noDiff) - serializerWriter.Serialize(jsonWriter, _control, type); - else - serializerWriter.SerializeDiff(jsonWriter, _control, type, other._control); - } - - controlType = string.Empty; - return sw.ToString(); + string json; + if (prefabDiff) + json = Json.JsonSerializer.SerializeDiff(_control, other._control, true); + else + json = Json.JsonSerializer.Serialize(_control, type, true); + return json; } internal void Deserialize(string json, Type controlType) { - if (_control == null || _control.GetType() != controlType) + if ((_control == null || _control.GetType() != controlType) && controlType != null) { - if (controlType != null) - Control = (Control)Activator.CreateInstance(controlType); + Control = (Control)Activator.CreateInstance(controlType); } if (_control != null) From 0a9f746abb662975ff6bddf00aa7ac8600b141c5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Mar 2024 19:10:13 +0100 Subject: [PATCH 107/198] Refactor UI Control actor active state mirroring in UI via `Visible` properties rather than unlinked from parent #2242 #851 --- Source/Engine/UI/UIControl.cpp | 10 +++++----- Source/Engine/UI/UIControl.cs | 35 +++++++--------------------------- Source/Engine/UI/UIControl.h | 2 +- 3 files changed, 13 insertions(+), 34 deletions(-) diff --git a/Source/Engine/UI/UIControl.cpp b/Source/Engine/UI/UIControl.cpp index aa54e3fc2..72eed1f27 100644 --- a/Source/Engine/UI/UIControl.cpp +++ b/Source/Engine/UI/UIControl.cpp @@ -17,7 +17,7 @@ MMethod* UIControl_Deserialize = nullptr; MMethod* UIControl_ParentChanged = nullptr; MMethod* UIControl_TransformChanged = nullptr; MMethod* UIControl_OrderInParentChanged = nullptr; -MMethod* UIControl_ActiveInTreeChanged = nullptr; +MMethod* UIControl_ActiveChanged = nullptr; MMethod* UIControl_BeginPlay = nullptr; MMethod* UIControl_EndPlay = nullptr; @@ -46,7 +46,7 @@ UIControl::UIControl(const SpawnParams& params) UIControl_ParentChanged = mclass->GetMethod("ParentChanged"); UIControl_TransformChanged = mclass->GetMethod("TransformChanged"); UIControl_OrderInParentChanged = mclass->GetMethod("OrderInParentChanged"); - UIControl_ActiveInTreeChanged = mclass->GetMethod("ActiveInTreeChanged"); + UIControl_ActiveChanged = mclass->GetMethod("ActiveChanged"); UIControl_BeginPlay = mclass->GetMethod("BeginPlay"); UIControl_EndPlay = mclass->GetMethod("EndPlay"); UIControl_Serialize = mclass->GetMethod("Serialize", 2); @@ -207,12 +207,12 @@ void UIControl::OnOrderInParentChanged() UICONTROL_INVOKE(OrderInParentChanged); } -void UIControl::OnActiveInTreeChanged() +void UIControl::OnActiveChanged() { - UICONTROL_INVOKE(ActiveInTreeChanged); + UICONTROL_INVOKE(ActiveChanged); // Base - Actor::OnActiveInTreeChanged(); + Actor::OnActiveChanged(); } #if !COMPILE_WITHOUT_CSHARP diff --git a/Source/Engine/UI/UIControl.cs b/Source/Engine/UI/UIControl.cs index 981c18685..b2c6a4cce 100644 --- a/Source/Engine/UI/UIControl.cs +++ b/Source/Engine/UI/UIControl.cs @@ -1,12 +1,7 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; -using System.Globalization; -using System.IO; -using System.Text; using FlaxEngine.GUI; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; namespace FlaxEngine { @@ -49,20 +44,21 @@ namespace FlaxEngine var containerControl = _control as ContainerControl; if (containerControl != null) containerControl.UnlockChildrenRecursive(); + _control.Visible = IsActive; _control.Parent = GetParent(); _control.IndexInParent = OrderInParent; _control.Location = new Float2(LocalPosition); _control.LocationChanged += OnControlLocationChanged; // Link children UI controls - if (containerControl != null && IsActiveInHierarchy) + if (containerControl != null) { var children = ChildrenCount; var parent = Parent; for (int i = 0; i < children; i++) { var child = GetChild(i) as UIControl; - if (child != null && child.IsActiveInHierarchy && child.HasControl && child != parent) + if (child != null && child.HasControl && child != parent) { child.Control.Parent = containerControl; } @@ -108,7 +104,6 @@ namespace FlaxEngine p2 = c.PointToParent(ref p2); p3 = c.PointToParent(ref p3); p4 = c.PointToParent(ref p4); - c = c.Parent; } var min = Float2.Min(Float2.Min(p1, p2), Float2.Min(p3, p4)); @@ -120,7 +115,6 @@ namespace FlaxEngine { Extents = new Vector3(size * 0.5f, Mathf.Epsilon) }; - canvasRoot.Canvas.GetWorldMatrix(out Matrix world); Matrix.Translation(min.X + size.X * 0.5f, min.Y + size.Y * 0.5f, 0, out Matrix offset); Matrix.Multiply(ref offset, ref world, out var boxWorld); @@ -297,15 +291,6 @@ namespace FlaxEngine private ContainerControl GetParent() { - // Don't link disabled actors - if (!IsActiveInHierarchy) - return null; -#if FLAX_EDITOR - // Prefab editor doesn't fire BeginPlay so for disabled actors we don't unlink them so do it here - if (!IsActive) - return null; -#endif - var parent = Parent; if (parent is UIControl uiControl && uiControl.Control is ContainerControl uiContainerControl) return uiContainerControl; @@ -357,6 +342,7 @@ namespace FlaxEngine { if (_control != null && !_blockEvents) { + _control.Visible = IsActive; _control.Parent = GetParent(); _control.IndexInParent = OrderInParent; } @@ -370,19 +356,11 @@ namespace FlaxEngine } } - internal void ActiveInTreeChanged() + internal void ActiveChanged() { if (_control != null && !_blockEvents) { - // Skip if this control is inactive and it's parent too (parent will unlink from hierarchy but children will stay connected while being inactive) - if (!IsActiveInHierarchy && Parent && !Parent.IsActive) - { - return; - } - - // Link or unlink control (won't modify Enable/Visible state) - _control.Parent = GetParent(); - _control.IndexInParent = OrderInParent; + _control.Visible = IsActive; } } @@ -398,6 +376,7 @@ namespace FlaxEngine { if (_control != null) { + _control.Visible = IsActive; _control.Parent = GetParent(); _control.IndexInParent = OrderInParent; Internal_GetNavTargets(__unmanagedPtr, out UIControl up, out UIControl down, out UIControl left, out UIControl right); diff --git a/Source/Engine/UI/UIControl.h b/Source/Engine/UI/UIControl.h index 8df4a578d..e76aca65a 100644 --- a/Source/Engine/UI/UIControl.h +++ b/Source/Engine/UI/UIControl.h @@ -30,7 +30,7 @@ protected: void OnBeginPlay() override; void OnEndPlay() override; void OnOrderInParentChanged() override; - void OnActiveInTreeChanged() override; + void OnActiveChanged() override; private: #if !COMPILE_WITHOUT_CSHARP From 4ee15f95bb2427ee90076df90197413677a19f71 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Mar 2024 23:31:45 +0100 Subject: [PATCH 108/198] Fix missing debug shapes in prefab window --- Source/Editor/Viewport/PrefabWindowViewport.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 97be5911a..94c6a4c79 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; @@ -799,6 +798,15 @@ namespace FlaxEditor.Viewport { base.OnDebugDraw(context, ref renderContext); + // Collect selected objects debug shapes and visuals + _debugDrawData.Clear(); + var selectedParents = TransformGizmo.SelectedParents; + for (int i = 0; i < selectedParents.Count; i++) + { + if (selectedParents[i].IsActiveInHierarchy) + selectedParents[i].OnDebugDraw(_debugDrawData); + } + unsafe { fixed (IntPtr* actors = _debugDrawData.ActorsPtrs) From 8f2bc17a942aed076508b8e87ba1f478a116ce3f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Mar 2024 23:34:35 +0100 Subject: [PATCH 109/198] Improve UI Control selection outline drawing --- .../Editor/Viewport/PrefabWindowViewport.cs | 25 ++++++++++++++++--- .../Editor/Viewport/Previews/AssetPreview.cs | 2 ++ Source/Editor/Windows/GameWindow.cs | 2 +- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 94c6a4c79..b9ee1c730 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -590,15 +590,32 @@ namespace FlaxEditor.Viewport base.Draw(); // Selected UI controls outline + bool drawAnySelectedControl = false; + // TODO: optimize this (eg. cache list of selected UIControl's when selection gets changed) for (var i = 0; i < _window.Selection.Count; i++) { - if (_window.Selection[i]?.EditableObject is UIControl controlActor && controlActor && controlActor.Control != null) + if (_window.Selection[i]?.EditableObject is UIControl controlActor && controlActor && controlActor.Control != null && controlActor.Control.VisibleInHierarchy && controlActor.Control.RootWindow != null) { + if (!drawAnySelectedControl) + { + drawAnySelectedControl = true; + Render2D.PushTransform(ref _cachedTransform); + } var control = controlActor.Control; - var bounds = Rectangle.FromPoints(control.PointToParent(this, Float2.Zero), control.PointToParent(this, control.Size)); - Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize); + var bounds = control.EditorBounds; + var p1 = control.PointToParent(this, bounds.UpperLeft); + var p2 = control.PointToParent(this, bounds.UpperRight); + var p3 = control.PointToParent(this, bounds.BottomLeft); + var p4 = control.PointToParent(this, bounds.BottomRight); + var min = Float2.Min(Float2.Min(p1, p2), Float2.Min(p3, p4)); + var max = Float2.Max(Float2.Max(p1, p2), Float2.Max(p3, p4)); + bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero)); + var options = Editor.Instance.Options.Options.Visual; + Render2D.DrawRectangle(bounds, options.SelectionOutlineColor0, options.UISelectionOutlineSize); } } + if (drawAnySelectedControl) + Render2D.PopTransform(); } /// @@ -798,7 +815,7 @@ namespace FlaxEditor.Viewport { base.OnDebugDraw(context, ref renderContext); - // Collect selected objects debug shapes and visuals + // Collect selected objects debug shapes again when DebugDraw is active with a custom context _debugDrawData.Clear(); var selectedParents = TransformGizmo.SelectedParents; for (int i = 0; i < selectedParents.Count; i++) diff --git a/Source/Editor/Viewport/Previews/AssetPreview.cs b/Source/Editor/Viewport/Previews/AssetPreview.cs index 8c88fa85d..ee9ee3195 100644 --- a/Source/Editor/Viewport/Previews/AssetPreview.cs +++ b/Source/Editor/Viewport/Previews/AssetPreview.cs @@ -239,6 +239,8 @@ namespace FlaxEditor.Viewport.Previews /// public override void OnDestroy() { + if (IsDisposing) + return; Object.Destroy(ref PreviewLight); Object.Destroy(ref EnvProbe); Object.Destroy(ref Sky); diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 88c42d99a..f1ea43d9b 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -922,7 +922,7 @@ namespace FlaxEditor.Windows var selection = Editor.SceneEditing.Selection; for (var i = 0; i < selection.Count; i++) { - if (selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null) + if (selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null && controlActor.Control.VisibleInHierarchy && controlActor.Control.RootWindow != null) { if (!drawAnySelectedControl) { From 9d0b659520d11801bd4ceefb1350cb3b1c6ac739 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Mar 2024 23:56:02 +0100 Subject: [PATCH 110/198] Use default backbuffers for Vulkan if `maxImageCount` is `0` --- Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp index f32625203..9bae5750d 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp @@ -352,7 +352,7 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) VkSwapchainCreateInfoKHR swapChainInfo; RenderToolsVulkan::ZeroStruct(swapChainInfo, VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); swapChainInfo.surface = _surface; - swapChainInfo.minImageCount = Math::Clamp(VULKAN_BACK_BUFFERS_COUNT, surfProperties.minImageCount, Math::Min(surfProperties.maxImageCount, VULKAN_BACK_BUFFERS_COUNT_MAX)); + swapChainInfo.minImageCount = surfProperties.maxImageCount > 0 ? Math::Clamp(VULKAN_BACK_BUFFERS_COUNT, surfProperties.minImageCount, Math::Min(surfProperties.maxImageCount, VULKAN_BACK_BUFFERS_COUNT_MAX)) : VULKAN_BACK_BUFFERS_COUNT; swapChainInfo.imageFormat = result.format; swapChainInfo.imageColorSpace = result.colorSpace; swapChainInfo.imageExtent.width = width; From 806fee70114520609224040408f55dcdd56c82ce Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 09:52:56 +0100 Subject: [PATCH 111/198] Redo 969d0390cee7651a2799ae9df1880c33f515fb02 to use in-built AutoFocus but fix focus issue --- Source/Editor/Content/GUI/ContentView.cs | 2 ++ Source/Editor/Windows/EditorWindow.cs | 38 +----------------------- 2 files changed, 3 insertions(+), 37 deletions(-) diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs index 105df76d4..fee36bbb0 100644 --- a/Source/Editor/Content/GUI/ContentView.cs +++ b/Source/Editor/Content/GUI/ContentView.cs @@ -646,7 +646,9 @@ namespace FlaxEditor.Content.GUI _rubberBandRectangle = new Rectangle(_mousePressLocation, 0, 0); _isRubberBandSpanning = true; StartMouseCapture(); + return true; } + return AutoFocus && Focus(this); } diff --git a/Source/Editor/Windows/EditorWindow.cs b/Source/Editor/Windows/EditorWindow.cs index 220fe0814..30bcd131a 100644 --- a/Source/Editor/Windows/EditorWindow.cs +++ b/Source/Editor/Windows/EditorWindow.cs @@ -14,8 +14,6 @@ namespace FlaxEditor.Windows /// public abstract class EditorWindow : DockWindow { - private bool _mouseDown; - /// /// Gets the editor object. /// @@ -40,6 +38,7 @@ namespace FlaxEditor.Windows protected EditorWindow(Editor editor, bool hideOnClose, ScrollBars scrollBars) : base(editor.UI.MasterPanel, hideOnClose, scrollBars) { + AutoFocus = true; Editor = editor; InputActions.Add(options => options.ContentFinder, () => @@ -225,41 +224,6 @@ namespace FlaxEditor.Windows return false; } - /// - public override bool OnMouseDown(Float2 location, MouseButton button) - { - if (base.OnMouseDown(location, button)) - return true; - if (button == MouseButton.Left) - { - _mouseDown = true; - return true; - } - return false; - } - - /// - public override bool OnMouseUp(Float2 location, MouseButton button) - { - if (base.OnMouseUp(location, button)) - return true; - if (button == MouseButton.Left && _mouseDown) - { - _mouseDown = false; - Focus(); - return true; - } - return false; - } - - /// - public override void OnMouseLeave() - { - _mouseDown = false; - - base.OnMouseLeave(); - } - /// public override void OnDestroy() { From a4e693b6a65c26f1b903c7f0231970640947625e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 10:01:04 +0100 Subject: [PATCH 112/198] Fix small GPU buffer update on Vulkan if data size align up overflows buffer size --- .../Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index 2d6771a27..cb223124f 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -1311,13 +1311,17 @@ void GPUContextVulkan::UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 vkCmdPipelineBarrier(cmdBuffer->GetHandle(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, &barrierBefore, 0, nullptr, 0, nullptr); // Use direct update for small buffers - if (size <= 16 * 1024) + const uint32 alignedSize = Math::AlignUp(size, 4); + if (alignedSize > buffer->GetSize()) + { + int a= 1; + } + if (size <= 16 * 1024 && alignedSize <= buffer->GetSize()) { //AddBufferBarrier(bufferVulkan, VK_ACCESS_TRANSFER_WRITE_BIT); //FlushBarriers(); - size = Math::AlignUp(size, 4); - vkCmdUpdateBuffer(cmdBuffer->GetHandle(), bufferVulkan->GetHandle(), offset, size, data); + vkCmdUpdateBuffer(cmdBuffer->GetHandle(), bufferVulkan->GetHandle(), offset, alignedSize, data); } else { From b4d3eeb771b32c643d20f808244df41db443b308 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 12:11:03 +0100 Subject: [PATCH 113/198] Another attempt for Vulkan swapchain setup if `minImageCount` is larger than `VULKAN_BACK_BUFFERS_COUNT` --- .../GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp index 9bae5750d..ddc1474cf 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp @@ -349,10 +349,15 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) LOG(Error, "Vulkan swapchain dimensions are invalid {}x{} (minImageExtent={}x{}, maxImageExtent={}x{})", width, height, surfProperties.minImageExtent.width, surfProperties.minImageExtent.height, surfProperties.maxImageExtent.width, surfProperties.maxImageExtent.height); return true; } + ASSERT(surfProperties.minImageCount <= VULKAN_BACK_BUFFERS_COUNT_MAX); VkSwapchainCreateInfoKHR swapChainInfo; RenderToolsVulkan::ZeroStruct(swapChainInfo, VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); swapChainInfo.surface = _surface; - swapChainInfo.minImageCount = surfProperties.maxImageCount > 0 ? Math::Clamp(VULKAN_BACK_BUFFERS_COUNT, surfProperties.minImageCount, Math::Min(surfProperties.maxImageCount, VULKAN_BACK_BUFFERS_COUNT_MAX)) : VULKAN_BACK_BUFFERS_COUNT; + swapChainInfo.minImageCount = surfProperties.maxImageCount > 0 // A value of 0 means that there is no limit on the number of image + ? Math::Min(VULKAN_BACK_BUFFERS_COUNT, surfProperties.maxImageCount) + : VULKAN_BACK_BUFFERS_COUNT; + swapChainInfo.minImageCount = Math::Max(swapChainInfo.minImageCount, surfProperties.minImageCount); + swapChainInfo.minImageCount = Math::Min(swapChainInfo.minImageCount, VULKAN_BACK_BUFFERS_COUNT_MAX); swapChainInfo.imageFormat = result.format; swapChainInfo.imageColorSpace = result.colorSpace; swapChainInfo.imageExtent.width = width; @@ -386,10 +391,11 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) { uint32 imagesCount; VALIDATE_VULKAN_RESULT(vkGetSwapchainImagesKHR(device, _swapChain, &imagesCount, nullptr)); - imagesCount = Math::Min(imagesCount, VULKAN_BACK_BUFFERS_COUNT); - if (imagesCount < VULKAN_BACK_BUFFERS_COUNT) - LOG(Warning, "Vulkan swapchain got less backbuffers than requried {} (instead of {})", imagesCount, VULKAN_BACK_BUFFERS_COUNT); - + if (imagesCount < 1 || imagesCount > VULKAN_BACK_BUFFERS_COUNT_MAX) + { + LOG(Warning, "Vulkan swapchain got invalid amount of backbuffers {} instead of {} (min {})", imagesCount, VULKAN_BACK_BUFFERS_COUNT, swapChainInfo.minImageCount); + return true; + } VkImage images[VULKAN_BACK_BUFFERS_COUNT_MAX]; VALIDATE_VULKAN_RESULT(vkGetSwapchainImagesKHR(device, _swapChain, &imagesCount, images)); From 83e8f8cb94bd1a074be0611e27b51f558a1ab348 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 12:39:01 +0100 Subject: [PATCH 114/198] Increase backbuffers support for Vulkan on Linux to `8` --- Source/Engine/GraphicsDevice/Vulkan/Config.h | 2 ++ Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp | 2 +- .../Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/Config.h b/Source/Engine/GraphicsDevice/Vulkan/Config.h index 34e5fe686..d31cb9a06 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Config.h +++ b/Source/Engine/GraphicsDevice/Vulkan/Config.h @@ -18,7 +18,9 @@ #ifndef VULKAN_BACK_BUFFERS_COUNT #define VULKAN_BACK_BUFFERS_COUNT 2 #endif +#ifndef VULKAN_BACK_BUFFERS_COUNT_MAX #define VULKAN_BACK_BUFFERS_COUNT_MAX 4 +#endif /// /// Default amount of frames to wait until resource delete. diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp index ddc1474cf..2379f9b33 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp @@ -393,7 +393,7 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) VALIDATE_VULKAN_RESULT(vkGetSwapchainImagesKHR(device, _swapChain, &imagesCount, nullptr)); if (imagesCount < 1 || imagesCount > VULKAN_BACK_BUFFERS_COUNT_MAX) { - LOG(Warning, "Vulkan swapchain got invalid amount of backbuffers {} instead of {} (min {})", imagesCount, VULKAN_BACK_BUFFERS_COUNT, swapChainInfo.minImageCount); + LOG(Warning, "Vulkan swapchain got invalid amount of backbuffers {} instead of {} (min {})", imagesCount, VULKAN_BACK_BUFFERS_COUNT, swapChainInfo.minImageCount); return true; } VkImage images[VULKAN_BACK_BUFFERS_COUNT_MAX]; diff --git a/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h b/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h index e58903c33..3c8ee101e 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h +++ b/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h @@ -6,6 +6,9 @@ #if GRAPHICS_API_VULKAN && PLATFORM_LINUX +// Support more backbuffers in case driver decides to use more (https://gitlab.freedesktop.org/apinheiro/mesa/-/issues/9) +#define VULKAN_BACK_BUFFERS_COUNT_MAX 8 + /// /// The implementation for the Vulkan API support for Linux platform. /// From e12919da98d9828e257017c2c5885f792db5337d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 13:05:02 +0100 Subject: [PATCH 115/198] Fix crash in `StaticModel::GetMaterial` when model is not loaded or has invalid entries count --- Source/Engine/Level/Actors/StaticModel.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index aa6772904..07ff609bd 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -553,13 +553,11 @@ const Span StaticModel::GetMaterialSlots() const MaterialBase* StaticModel::GetMaterial(int32 entryIndex) { - if (Model) - Model->WaitForLoaded(); - else + if (!Model || Model->WaitForLoaded()) return nullptr; CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr); MaterialBase* material = Entries[entryIndex].Material.Get(); - if (!material) + if (!material && entryIndex < Model->MaterialSlots.Count()) { material = Model->MaterialSlots[entryIndex].Material.Get(); if (!material) From 94520d30adfa5121123d0712d643e6e2a9297936 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 13:22:01 +0100 Subject: [PATCH 116/198] Fix debug shapes from prefab window visible in main viewport --- Source/Editor/Viewport/PrefabWindowViewport.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index b9ee1c730..2fdb39b4f 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -54,6 +54,7 @@ namespace FlaxEditor.Viewport private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32); private PrefabSpritesRenderer _spritesRenderer; + private IntPtr _tempDebugDrawContext; /// /// Drag and drop handlers @@ -258,11 +259,19 @@ namespace FlaxEditor.Viewport var selectedParents = TransformGizmo.SelectedParents; if (selectedParents.Count > 0) { + // Use temporary Debug Draw context to pull any debug shapes drawing in Scene Graph Nodes - those are used in OnDebugDraw down below + if (_tempDebugDrawContext == IntPtr.Zero) + _tempDebugDrawContext = DebugDraw.AllocateContext(); + DebugDraw.SetContext(_tempDebugDrawContext); + DebugDraw.UpdateContext(_tempDebugDrawContext, 1.0f); + for (int i = 0; i < selectedParents.Count; i++) { if (selectedParents[i].IsActiveInHierarchy) selectedParents[i].OnDebugDraw(_debugDrawData); } + + DebugDraw.SetContext(IntPtr.Zero); } } @@ -792,6 +801,13 @@ namespace FlaxEditor.Viewport /// public override void OnDestroy() { + if (IsDisposing) + return; + if (_tempDebugDrawContext != IntPtr.Zero) + { + DebugDraw.FreeContext(_tempDebugDrawContext); + _tempDebugDrawContext = IntPtr.Zero; + } FlaxEngine.Object.Destroy(ref SelectionOutline); FlaxEngine.Object.Destroy(ref _spritesRenderer); From fc29d687b5132be9258de8282430ddb6790564ef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 13:25:25 +0100 Subject: [PATCH 117/198] Add more insights on potentially missing asset file data chunks --- Source/Engine/Content/BinaryAsset.cpp | 27 ++++++++++++++++++- Source/Engine/Content/Storage/AssetHeader.h | 25 +++-------------- Source/Engine/Content/Storage/FlaxStorage.cpp | 24 +++++++++++++++++ 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/Source/Engine/Content/BinaryAsset.cpp b/Source/Engine/Content/BinaryAsset.cpp index 688a3f7b5..85ffd66ec 100644 --- a/Source/Engine/Content/BinaryAsset.cpp +++ b/Source/Engine/Content/BinaryAsset.cpp @@ -584,7 +584,32 @@ Asset::LoadResult BinaryAsset::loadAsset() ASSERT(Storage && _header.ID.IsValid() && _header.TypeName.HasChars()); auto lock = Storage->Lock(); - return load(); + const LoadResult result = load(); +#if !BUILD_RELEASE + if (result == LoadResult::MissingDataChunk) + { + // Provide more insights on potentially missing asset data chunk + Char chunksBitMask[ASSET_FILE_DATA_CHUNKS + 1]; + Char chunksExistBitMask[ASSET_FILE_DATA_CHUNKS + 1]; + Char chunksLoadBitMask[ASSET_FILE_DATA_CHUNKS + 1]; + for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++) + { + if (const FlaxChunk* chunk = _header.Chunks[i]) + { + chunksBitMask[i] = '1'; + chunksExistBitMask[i] = chunk->ExistsInFile() ? '1' : '0'; + chunksLoadBitMask[i] = chunk->IsLoaded() ? '1' : '0'; + } + else + { + chunksBitMask[i] = chunksExistBitMask[i] = chunksLoadBitMask[i] = '0'; + } + } + chunksBitMask[ASSET_FILE_DATA_CHUNKS] = chunksExistBitMask[ASSET_FILE_DATA_CHUNKS] = chunksLoadBitMask[ASSET_FILE_DATA_CHUNKS] = 0; + LOG(Warning, "Asset reports missing data chunk. Chunks bitmask: {}, existing chunks: {} loaded chunks: {}. '{}'", chunksBitMask, chunksExistBitMask, chunksLoadBitMask, ToString()); + } +#endif + return result; } void BinaryAsset::releaseStorage() diff --git a/Source/Engine/Content/Storage/AssetHeader.h b/Source/Engine/Content/Storage/AssetHeader.h index 0730be025..883a16f16 100644 --- a/Source/Engine/Content/Storage/AssetHeader.h +++ b/Source/Engine/Content/Storage/AssetHeader.h @@ -81,36 +81,17 @@ public: /// /// Gets the amount of created asset chunks. /// - /// Created asset chunks - int32 GetChunksCount() const - { - int32 result = 0; - for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++) - { - if (Chunks[i] != nullptr) - result++; - } - return result; - } + int32 GetChunksCount() const; /// /// Deletes all chunks. Warning! Chunks are managed internally, use with caution! /// - void DeleteChunks() - { - for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++) - { - SAFE_DELETE(Chunks[i]); - } - } + void DeleteChunks(); /// /// Unlinks all chunks. /// - void UnlinkChunks() - { - Platform::MemoryClear(Chunks, sizeof(Chunks)); - } + void UnlinkChunks(); /// /// Gets string with a human-readable info about that header diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index 0ba268e21..acc5be881 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -20,6 +20,30 @@ #endif #include +int32 AssetHeader::GetChunksCount() const +{ + int32 result = 0; + for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++) + { + if (Chunks[i] != nullptr) + result++; + } + return result; +} + +void AssetHeader::DeleteChunks() +{ + for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++) + { + SAFE_DELETE(Chunks[i]); + } +} + +void AssetHeader::UnlinkChunks() +{ + Platform::MemoryClear(Chunks, sizeof(Chunks)); +} + String AssetHeader::ToString() const { return String::Format(TEXT("ID: {0}, TypeName: {1}, Chunks Count: {2}"), ID, TypeName, GetChunksCount()); From 66b68bff27005a97fc5b8288400377b0831ebea8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 17:48:16 +0100 Subject: [PATCH 118/198] Cleanup editor viewports code --- Source/Editor/Gizmo/GizmosCollection.cs | 15 ++++ Source/Editor/Viewport/EditorGizmoViewport.cs | 37 ++++++++++ .../Viewport/MainEditorGizmoViewport.cs | 69 +++---------------- .../Editor/Viewport/PrefabWindowViewport.cs | 66 +++--------------- .../Editor/Viewport/ViewportDraggingHelper.cs | 4 +- Source/Editor/Windows/Assets/PrefabWindow.cs | 36 ++++------ 6 files changed, 88 insertions(+), 139 deletions(-) diff --git a/Source/Editor/Gizmo/GizmosCollection.cs b/Source/Editor/Gizmo/GizmosCollection.cs index b77d22e84..0f69c0756 100644 --- a/Source/Editor/Gizmo/GizmosCollection.cs +++ b/Source/Editor/Gizmo/GizmosCollection.cs @@ -161,5 +161,20 @@ namespace FlaxEditor.Gizmo } throw new ArgumentException("Not added mode to activate."); } + + /// + /// Gets the gizmo of a given type or returns null if not added. + /// + /// Type of the gizmo. + /// Found gizmo or null. + public T Get() where T : GizmoBase + { + foreach (var e in this) + { + if (e is T asT) + return asT; + } + return null; + } } } diff --git a/Source/Editor/Viewport/EditorGizmoViewport.cs b/Source/Editor/Viewport/EditorGizmoViewport.cs index 8f038ce17..f1c4fed70 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -41,6 +41,7 @@ namespace FlaxEditor.Viewport Gizmos[i].Update(deltaTime); } } + /// public EditorViewport Viewport => this; @@ -121,5 +122,41 @@ namespace FlaxEditor.Viewport base.OnDestroy(); } + + internal static readonly float[] TranslateSnapValues = + { + 0.1f, + 0.5f, + 1.0f, + 5.0f, + 10.0f, + 100.0f, + 1000.0f, + }; + + internal static readonly float[] RotateSnapValues = + { + 1.0f, + 5.0f, + 10.0f, + 15.0f, + 30.0f, + 45.0f, + 60.0f, + 90.0f, + }; + + internal static readonly float[] ScaleSnapValues = + { + 0.05f, + 0.1f, + 0.25f, + 0.5f, + 1.0f, + 2.0f, + 4.0f, + 6.0f, + 8.0f, + }; } } diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 541bbcfba..1e63888bf 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -268,16 +268,14 @@ namespace FlaxEditor.Viewport Parent = scaleSnappingWidget }; enableScaleSnapping.Toggled += OnScaleSnappingToggle; - var scaleSnappingCM = new ContextMenu(); _scaleSnapping = new ViewportWidgetButton(TransformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM) { TooltipText = "Scale snapping values" }; - - for (int i = 0; i < EditorViewportScaleSnapValues.Length; i++) + for (int i = 0; i < ScaleSnapValues.Length; i++) { - var v = EditorViewportScaleSnapValues[i]; + var v = ScaleSnapValues[i]; var button = scaleSnappingCM.AddButton(v.ToString()); button.Tag = v; } @@ -295,16 +293,14 @@ namespace FlaxEditor.Viewport Parent = rotateSnappingWidget }; enableRotateSnapping.Toggled += OnRotateSnappingToggle; - var rotateSnappingCM = new ContextMenu(); _rotateSnapping = new ViewportWidgetButton(TransformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM) { TooltipText = "Rotation snapping values" }; - - for (int i = 0; i < EditorViewportRotateSnapValues.Length; i++) + for (int i = 0; i < RotateSnapValues.Length; i++) { - var v = EditorViewportRotateSnapValues[i]; + var v = RotateSnapValues[i]; var button = rotateSnappingCM.AddButton(v.ToString()); button.Tag = v; } @@ -322,7 +318,6 @@ namespace FlaxEditor.Viewport Parent = translateSnappingWidget }; enableTranslateSnapping.Toggled += OnTranslateSnappingToggle; - var translateSnappingCM = new ContextMenu(); _translateSnapping = new ViewportWidgetButton(TransformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM) { @@ -330,16 +325,14 @@ namespace FlaxEditor.Viewport }; if (TransformGizmo.TranslationSnapValue < 0.0f) _translateSnapping.Text = "Bounding Box"; - - for (int i = 0; i < EditorViewportTranslateSnapValues.Length; i++) + for (int i = 0; i < TranslateSnapValues.Length; i++) { - var v = EditorViewportTranslateSnapValues[i]; + var v = TranslateSnapValues[i]; var button = translateSnappingCM.AddButton(v.ToString()); button.Tag = v; } var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume"); buttonBB.Tag = -1.0f; - translateSnappingCM.ButtonClicked += OnWidgetTranslateSnapClick; translateSnappingCM.VisibleChanged += OnWidgetTranslateSnapShowHide; _translateSnapping.Parent = translateSnappingWidget; @@ -552,7 +545,7 @@ namespace FlaxEditor.Viewport var task = renderContext.Task; // Render editor primitives, gizmo and debug shapes in debug view modes - // Note: can use Output buffer as both input and output because EditorPrimitives is using a intermediate buffers + // Note: can use Output buffer as both input and output because EditorPrimitives is using an intermediate buffer if (EditorPrimitives && EditorPrimitives.CanRender()) { EditorPrimitives.Render(context, ref renderContext, task.Output, task.Output); @@ -619,19 +612,6 @@ namespace FlaxEditor.Viewport _gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale; } - private static readonly float[] EditorViewportScaleSnapValues = - { - 0.05f, - 0.1f, - 0.25f, - 0.5f, - 1.0f, - 2.0f, - 4.0f, - 6.0f, - 8.0f, - }; - private void OnWidgetScaleSnapClick(ContextMenuButton button) { var v = (float)button.Tag; @@ -651,25 +631,11 @@ namespace FlaxEditor.Viewport if (e is ContextMenuButton b) { var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } - private static readonly float[] EditorViewportRotateSnapValues = - { - 1.0f, - 5.0f, - 10.0f, - 15.0f, - 30.0f, - 45.0f, - 60.0f, - 90.0f, - }; - private void OnWidgetRotateSnapClick(ContextMenuButton button) { var v = (float)button.Tag; @@ -689,24 +655,11 @@ namespace FlaxEditor.Viewport if (e is ContextMenuButton b) { var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } - private static readonly float[] EditorViewportTranslateSnapValues = - { - 0.1f, - 0.5f, - 1.0f, - 5.0f, - 10.0f, - 100.0f, - 1000.0f, - }; - private void OnWidgetTranslateSnapClick(ContextMenuButton button) { var v = (float)button.Tag; @@ -729,9 +682,7 @@ namespace FlaxEditor.Viewport if (e is ContextMenuButton b) { var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 2fdb39b4f..f66d4e861 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -131,16 +131,14 @@ namespace FlaxEditor.Viewport Parent = scaleSnappingWidget }; enableScaleSnapping.Toggled += OnScaleSnappingToggle; - var scaleSnappingCM = new ContextMenu(); _scaleSnapping = new ViewportWidgetButton(TransformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM) { TooltipText = "Scale snapping values" }; - - for (int i = 0; i < EditorViewportScaleSnapValues.Length; i++) + for (int i = 0; i < EditorGizmoViewport.ScaleSnapValues.Length; i++) { - var v = EditorViewportScaleSnapValues[i]; + var v = EditorGizmoViewport.ScaleSnapValues[i]; var button = scaleSnappingCM.AddButton(v.ToString()); button.Tag = v; } @@ -158,16 +156,14 @@ namespace FlaxEditor.Viewport Parent = rotateSnappingWidget }; enableRotateSnapping.Toggled += OnRotateSnappingToggle; - var rotateSnappingCM = new ContextMenu(); _rotateSnapping = new ViewportWidgetButton(TransformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM) { TooltipText = "Rotation snapping values" }; - - for (int i = 0; i < EditorViewportRotateSnapValues.Length; i++) + for (int i = 0; i < EditorGizmoViewport.RotateSnapValues.Length; i++) { - var v = EditorViewportRotateSnapValues[i]; + var v = EditorGizmoViewport.RotateSnapValues[i]; var button = rotateSnappingCM.AddButton(v.ToString()); button.Tag = v; } @@ -185,16 +181,14 @@ namespace FlaxEditor.Viewport Parent = translateSnappingWidget }; enableTranslateSnapping.Toggled += OnTranslateSnappingToggle; - var translateSnappingCM = new ContextMenu(); _translateSnappng = new ViewportWidgetButton(TransformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM) { TooltipText = "Position snapping values" }; - - for (int i = 0; i < EditorViewportTranslateSnapValues.Length; i++) + for (int i = 0; i < EditorGizmoViewport.TranslateSnapValues.Length; i++) { - var v = EditorViewportTranslateSnapValues[i]; + var v = EditorGizmoViewport.TranslateSnapValues[i]; var button = translateSnappingCM.AddButton(v.ToString()); button.Tag = v; } @@ -428,19 +422,6 @@ namespace FlaxEditor.Viewport _gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale; } - private static readonly float[] EditorViewportScaleSnapValues = - { - 0.05f, - 0.1f, - 0.25f, - 0.5f, - 1.0f, - 2.0f, - 4.0f, - 6.0f, - 8.0f, - }; - private void OnWidgetScaleSnapClick(ContextMenuButton button) { var v = (float)button.Tag; @@ -459,25 +440,11 @@ namespace FlaxEditor.Viewport if (e is ContextMenuButton b) { var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } - private static readonly float[] EditorViewportRotateSnapValues = - { - 1.0f, - 5.0f, - 10.0f, - 15.0f, - 30.0f, - 45.0f, - 60.0f, - 90.0f, - }; - private void OnWidgetRotateSnapClick(ContextMenuButton button) { var v = (float)button.Tag; @@ -496,24 +463,11 @@ namespace FlaxEditor.Viewport if (e is ContextMenuButton b) { var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } - private static readonly float[] EditorViewportTranslateSnapValues = - { - 0.1f, - 0.5f, - 1.0f, - 5.0f, - 10.0f, - 100.0f, - 1000.0f, - }; - private void OnWidgetTranslateSnapClick(ContextMenuButton button) { var v = (float)button.Tag; @@ -532,9 +486,7 @@ namespace FlaxEditor.Viewport if (e is ContextMenuButton b) { var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } diff --git a/Source/Editor/Viewport/ViewportDraggingHelper.cs b/Source/Editor/Viewport/ViewportDraggingHelper.cs index 72fcce828..86a127647 100644 --- a/Source/Editor/Viewport/ViewportDraggingHelper.cs +++ b/Source/Editor/Viewport/ViewportDraggingHelper.cs @@ -146,7 +146,7 @@ namespace FlaxEditor.Viewport var gridPlane = new Plane(Vector3.Zero, Vector3.Up); var flags = SceneGraphNode.RayCastData.FlagTypes.SkipColliders | SceneGraphNode.RayCastData.FlagTypes.SkipEditorPrimitives; hit = _owner.SceneGraphRoot.RayCast(ref ray, ref view, out var closest, out var normal, flags); - var girdGizmo = (GridGizmo)_owner.Gizmos.FirstOrDefault(x => x is GridGizmo); + var girdGizmo = _owner.Gizmos.Get(); if (hit != null) { // Use hit location @@ -180,7 +180,7 @@ namespace FlaxEditor.Viewport var location = hitLocation + new Vector3(0, bottomToCenter, 0); // Apply grid snapping if enabled - var transformGizmo = (TransformGizmo)_owner.Gizmos.FirstOrDefault(x => x is TransformGizmo); + var transformGizmo = _owner.Gizmos.Get(); if (transformGizmo != null && (_owner.UseSnapping || transformGizmo.TranslationSnapEnable)) { float snapValue = transformGizmo.TranslationSnapValue; diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 2e3e700a2..eb5070cb1 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -132,7 +132,7 @@ namespace FlaxEditor.Windows.Assets IsScrollable = false, Offsets = new Margin(0, 0, 0, 18 + 6), }; - _searchBox = new SearchBox() + _searchBox = new SearchBox { AnchorPreset = AnchorPresets.HorizontalStretchMiddle, Parent = headerPanel, @@ -140,7 +140,8 @@ namespace FlaxEditor.Windows.Assets }; _searchBox.TextChanged += OnSearchBoxTextChanged; - _treePanel = new Panel() + // Prefab structure tree + _treePanel = new Panel { AnchorPreset = AnchorPresets.StretchAll, Offsets = new Margin(0.0f, 0.0f, headerPanel.Bottom, 0.0f), @@ -148,8 +149,6 @@ namespace FlaxEditor.Windows.Assets IsScrollable = true, Parent = sceneTreePanel, }; - - // Prefab structure tree Graph = new LocalSceneGraph(new CustomRootNode(this)); Graph.Root.TreeNode.Expand(true); _tree = new PrefabTree @@ -316,11 +315,7 @@ namespace FlaxEditor.Windows.Assets return; // Restore - _viewport.Prefab = _asset; - Graph.MainActor = _viewport.Instance; - Selection.Clear(); - Select(Graph.Main); - Graph.Root.TreeNode.Expand(true); + OnPrefabOpened(); _undo.Clear(); ClearEditedFlag(); } @@ -346,6 +341,15 @@ namespace FlaxEditor.Windows.Assets } } + private void OnPrefabOpened() + { + _viewport.Prefab = _asset; + Graph.MainActor = _viewport.Instance; + Selection.Clear(); + Select(Graph.Main); + Graph.Root.TreeNode.Expand(true); + } + /// public override void Save() { @@ -417,13 +421,8 @@ namespace FlaxEditor.Windows.Assets return; } - _viewport.Prefab = _asset; - Graph.MainActor = _viewport.Instance; + OnPrefabOpened(); _focusCamera = true; - Selection.Clear(); - Select(Graph.Main); - Graph.Root.TreeNode.Expand(true); - _undo.Clear(); ClearEditedFlag(); @@ -468,11 +467,7 @@ namespace FlaxEditor.Windows.Assets _viewport.Prefab = null; if (_asset.IsLoaded) { - _viewport.Prefab = _asset; - Graph.MainActor = _viewport.Instance; - Selection.Clear(); - Select(Graph.Main); - Graph.Root.TreeNode.ExpandAll(true); + OnPrefabOpened(); } } finally @@ -484,7 +479,6 @@ namespace FlaxEditor.Windows.Assets if (_focusCamera && _viewport.Task.FrameCount > 1) { _focusCamera = false; - Editor.GetActorEditorSphere(_viewport.Instance, out BoundingSphere bounds); _viewport.ViewPosition = bounds.Center - _viewport.ViewDirection * (bounds.Radius * 1.2f); } From f4033578c3d97794506f844e66b20992605e69d2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 17:48:51 +0100 Subject: [PATCH 119/198] Refactor UI Control linkage in the prefab previews to prevent bugs --- Source/Editor/Editor.cs | 22 -------- .../Editor/Viewport/Previews/PrefabPreview.cs | 53 +++++++++---------- 2 files changed, 25 insertions(+), 50 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 4cee6d971..d0076068e 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -290,7 +290,6 @@ namespace FlaxEditor StateMachine = new EditorStateMachine(this); Undo = new EditorUndo(this); - UIControl.FallbackParentGetDelegate += OnUIControlFallbackParentGet; if (newProject) InitProject(); @@ -355,27 +354,6 @@ namespace FlaxEditor StateMachine.LoadingState.StartInitEnding(skipCompile); } - private ContainerControl OnUIControlFallbackParentGet(UIControl control) - { - // Check if prefab root control is this UIControl - var loadingPreview = Viewport.Previews.PrefabPreview.LoadingPreview; - var activePreviews = Viewport.Previews.PrefabPreview.ActivePreviews; - if (activePreviews != null) - { - foreach (var preview in activePreviews) - { - if (preview == loadingPreview || - (preview.Instance != null && (preview.Instance == control || preview.Instance.HasActorInHierarchy(control)))) - { - // Link it to the prefab preview to see it in the editor - preview.customControlLinked = control; - return preview; - } - } - } - return null; - } - internal void RegisterModule(EditorModule module) { Log("Register Editor module " + module); diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index ff235bd04..a17850557 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; -using System.Collections.Generic; using FlaxEngine; using Object = FlaxEngine.Object; @@ -13,19 +12,9 @@ namespace FlaxEditor.Viewport.Previews /// public class PrefabPreview : AssetPreview { - /// - /// The currently spawned prefab instance owner. Used to link some actors such as UIControl to preview scene and view. - /// - internal static PrefabPreview LoadingPreview; - - /// - /// The list of active prefab previews. Used to link some actors such as UIControl to preview scene and view. - /// - internal static List ActivePreviews; - private Prefab _prefab; private Actor _instance; - internal UIControl customControlLinked; + internal UIControl _uiControlLinked; /// /// Gets or sets the prefab asset to preview. @@ -54,13 +43,10 @@ namespace FlaxEditor.Viewport.Previews _prefab.WaitForLoaded(); // Spawn prefab - LoadingPreview = this; var instance = PrefabManager.SpawnPrefab(_prefab, null); - LoadingPreview = null; if (instance == null) { _prefab = null; - ActivePreviews.Remove(this); throw new Exception("Failed to spawn a prefab for the preview."); } @@ -84,11 +70,11 @@ namespace FlaxEditor.Viewport.Previews if (_instance) { // Unlink UI control - if (customControlLinked) + if (_uiControlLinked) { - if (customControlLinked.Control?.Parent == this) - customControlLinked.Control.Parent = null; - customControlLinked = null; + if (_uiControlLinked.Control?.Parent == this) + _uiControlLinked.Control.Parent = null; + _uiControlLinked = null; } // Remove for the preview @@ -101,13 +87,27 @@ namespace FlaxEditor.Viewport.Previews { // Add to the preview Task.AddCustomActor(_instance); - - // Link UI canvases to the preview - LinkCanvas(_instance); + UpdateLinkage(); } } } + private void UpdateLinkage() + { + // Link UI canvases to the preview (eg. after canvas added to the prefab) + LinkCanvas(_instance); + + // Link UI control to the preview + if (_uiControlLinked == null && + _instance is UIControl uiControl && + uiControl.Control != null && + uiControl.Control.Parent == null) + { + uiControl.Control.Parent = this; + _uiControlLinked = uiControl; + } + } + private void LinkCanvas(Actor actor) { if (actor is UICanvas uiCanvas) @@ -126,9 +126,6 @@ namespace FlaxEditor.Viewport.Previews public PrefabPreview(bool useWidgets) : base(useWidgets) { - if (ActivePreviews == null) - ActivePreviews = new List(); - ActivePreviews.Add(this); } /// @@ -138,15 +135,15 @@ namespace FlaxEditor.Viewport.Previews if (_instance != null) { - // Link UI canvases to the preview (eg. after canvas added to the prefab) - LinkCanvas(_instance); + UpdateLinkage(); } } /// public override void OnDestroy() { - ActivePreviews.Remove(this); + if (IsDisposing) + return; Prefab = null; base.OnDestroy(); From 6d792f1f74ad1dc1fa84fd58ec758e61aaef9372 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Mar 2024 17:59:41 +0100 Subject: [PATCH 120/198] Fix critical regression in terrain heightmap data upgrade --- Source/Engine/Terrain/TerrainPatch.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 053ab10e0..9ca25cf14 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -2148,7 +2148,9 @@ bool TerrainPatch::CreateHeightField() if (collisionHeader->CheckOldMagicNumber != MAX_int32 || collisionHeader->Version != TerrainCollisionDataHeader::CurrentVersion) { // Reset height map - return InitializeHeightMap(); + PROFILE_CPU_NAMED("ResetHeightMap"); + const float* data = GetHeightmapData(); + return SetupHeightMap(_cachedHeightMap.Count(), data); } // Create heightfield object from the data From 574e0ab1f805e7b04bfaaf4a9654a5e0a1bde70e Mon Sep 17 00:00:00 2001 From: Terence Lee Date: Wed, 13 Mar 2024 17:34:26 +0800 Subject: [PATCH 121/198] Imports the IntPtr type to the local module. Sometimes a "foreign member exception" was being created in Cecil when using external libraries. --- .../Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs index 5cf7bab62..8b4e7e6c9 100644 --- a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs +++ b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs @@ -839,10 +839,11 @@ namespace Flax.Build.Plugins module.GetType("System.IntPtr", out var intPtrType); module.GetType("FlaxEngine.Object", out var scriptingObjectType); var fromUnmanagedPtr = scriptingObjectType.Resolve().GetMethod("FromUnmanagedPtr"); + TypeReference intPtr = module.ImportReference(intPtrType); var m = new MethodDefinition(name + "Native", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, context.VoidType); - m.Parameters.Add(new ParameterDefinition("instancePtr", ParameterAttributes.None, intPtrType)); - m.Parameters.Add(new ParameterDefinition("streamPtr", ParameterAttributes.None, intPtrType)); + m.Parameters.Add(new ParameterDefinition("instancePtr", ParameterAttributes.None, intPtr)); + m.Parameters.Add(new ParameterDefinition("streamPtr", ParameterAttributes.None, intPtr)); TypeReference networkStream = module.ImportReference(context.NetworkStreamType); ILProcessor il = m.Body.GetILProcessor(); il.Emit(OpCodes.Nop); @@ -1645,12 +1646,13 @@ namespace Flax.Build.Plugins module.GetType("FlaxEngine.Object", out var scriptingObjectType); var fromUnmanagedPtr = scriptingObjectType.Resolve().GetMethod("FromUnmanagedPtr"); TypeReference networkStream = module.ImportReference(networkStreamType); + TypeReference intPtr = module.ImportReference(intPtrType); // Generate static method to execute RPC locally { var m = new MethodDefinition(method.Name + "_Execute", MethodAttributes.Static | MethodAttributes.Assembly | MethodAttributes.HideBySig, voidType); - m.Parameters.Add(new ParameterDefinition("instancePtr", ParameterAttributes.None, intPtrType)); - m.Parameters.Add(new ParameterDefinition("streamPtr", ParameterAttributes.None, module.ImportReference(intPtrType))); + m.Parameters.Add(new ParameterDefinition("instancePtr", ParameterAttributes.None, intPtr)); + m.Parameters.Add(new ParameterDefinition("streamPtr", ParameterAttributes.None, intPtr)); ILProcessor ilp = m.Body.GetILProcessor(); var il = new DotnetIlContext(ilp, method); il.Emit(OpCodes.Nop); From 2b82da95117c80c6cd81d2c2fb45a1efe59f2f48 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Mar 2024 17:29:10 +0100 Subject: [PATCH 122/198] Fix bug with null object being selected in prefab --- .../Editor/Windows/Assets/PrefabWindow.Selection.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs b/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs index cb4c7fd0f..be20c176c 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs @@ -3,11 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; -using FlaxEditor.Gizmo; using FlaxEditor.GUI.Tree; using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph.GUI; -using FlaxEditor.Viewport.Cameras; using FlaxEngine; namespace FlaxEditor.Windows.Assets @@ -64,8 +62,11 @@ namespace FlaxEditor.Windows.Assets private void OnSelectionUndo(SceneGraphNode[] toSelect) { Selection.Clear(); - Selection.AddRange(toSelect); - + foreach (var e in toSelect) + { + if (e != null) + Selection.Add(e); + } OnSelectionChanges(); } @@ -118,11 +119,13 @@ namespace FlaxEditor.Windows.Assets /// The nodes. public void Select(List nodes) { + nodes?.RemoveAll(x => x == null); if (nodes == null || nodes.Count == 0) { Deselect(); return; } + if (Utils.ArraysEqual(Selection, nodes)) return; From 7545e49284d9e08e027102ba46722bb2f424fc2d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Mar 2024 17:30:28 +0100 Subject: [PATCH 123/198] Add manual chunks loading before Binary Asset load in case content streaming flushed them out --- Source/Engine/Content/BinaryAsset.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Source/Engine/Content/BinaryAsset.cpp b/Source/Engine/Content/BinaryAsset.cpp index 85ffd66ec..7735d8b28 100644 --- a/Source/Engine/Content/BinaryAsset.cpp +++ b/Source/Engine/Content/BinaryAsset.cpp @@ -584,6 +584,17 @@ Asset::LoadResult BinaryAsset::loadAsset() ASSERT(Storage && _header.ID.IsValid() && _header.TypeName.HasChars()); auto lock = Storage->Lock(); + auto chunksToPreload = getChunksToPreload(); + if (chunksToPreload != 0) + { + // Ensure that any chunks that were requested before are loaded in memory (in case streaming flushed them out after timeout) + for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++) + { + const auto chunk = _header.Chunks[i]; + if (GET_CHUNK_FLAG(i) & chunksToPreload && chunk && chunk->IsMissing()) + Storage->LoadAssetChunk(chunk); + } + } const LoadResult result = load(); #if !BUILD_RELEASE if (result == LoadResult::MissingDataChunk) From 3ba6867cd21a5e32ea6c03b43d89e9a54a7ff88a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Mar 2024 17:30:54 +0100 Subject: [PATCH 124/198] Minor fixes --- Source/Editor/Windows/SceneTreeWindow.cs | 1 - Source/Engine/UI/GUI/ContainerControl.cs | 2 +- Source/Engine/UI/GUI/Control.Bounds.cs | 3 +++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index f47cd4243..9231ce970 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -221,7 +221,6 @@ namespace FlaxEditor.Windows { if (!Editor.StateMachine.CurrentState.CanEditScene) return; - ShowContextMenu(node, location); } diff --git a/Source/Engine/UI/GUI/ContainerControl.cs b/Source/Engine/UI/GUI/ContainerControl.cs index 917d200da..c53307c65 100644 --- a/Source/Engine/UI/GUI/ContainerControl.cs +++ b/Source/Engine/UI/GUI/ContainerControl.cs @@ -356,7 +356,7 @@ namespace FlaxEngine.GUI for (int i = _children.Count - 1; i >= 0; i--) { var child = _children[i]; - if (IntersectsChildContent(child, point, out var childLocation)) + if (child.Visible && IntersectsChildContent(child, point, out var childLocation)) { var containerControl = child as ContainerControl; var childAtRecursive = containerControl?.GetChildAtRecursive(childLocation); diff --git a/Source/Engine/UI/GUI/Control.Bounds.cs b/Source/Engine/UI/GUI/Control.Bounds.cs index d76f82308..ec96f89f2 100644 --- a/Source/Engine/UI/GUI/Control.Bounds.cs +++ b/Source/Engine/UI/GUI/Control.Bounds.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; +using System.ComponentModel; namespace FlaxEngine.GUI { @@ -382,6 +383,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point. /// + [DefaultValue(0.0f)] [ExpandGroups, EditorDisplay("Transform"), EditorOrder(1040), Tooltip("The shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point.")] public Float2 Shear { @@ -398,6 +400,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the rotation angle (in degrees). Control is rotated around it's pivot point (middle of the control by default). /// + [DefaultValue(0.0f)] [ExpandGroups, EditorDisplay("Transform"), EditorOrder(1050), Tooltip("The control rotation angle (in degrees). Control is rotated around it's pivot point (middle of the control by default).")] public float Rotation { From 7ec490443eb641e9355f59792428aeab5a7aa26d Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 15 Mar 2024 09:25:19 -0500 Subject: [PATCH 125/198] Fix wrong default value type. --- Source/Engine/UI/GUI/Control.Bounds.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Control.Bounds.cs b/Source/Engine/UI/GUI/Control.Bounds.cs index ec96f89f2..cbb3ba80b 100644 --- a/Source/Engine/UI/GUI/Control.Bounds.cs +++ b/Source/Engine/UI/GUI/Control.Bounds.cs @@ -383,7 +383,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point. /// - [DefaultValue(0.0f)] + [DefaultValue(typeof(Float2), "0,0")] [ExpandGroups, EditorDisplay("Transform"), EditorOrder(1040), Tooltip("The shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point.")] public Float2 Shear { From 7652feabfadbbda3f0b41b4a6c9f8e2edd72b4a4 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 15 Mar 2024 15:15:40 -0500 Subject: [PATCH 126/198] Clamp location of multiblend data. --- Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index 2747e1023..1178f54de 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -617,8 +617,9 @@ namespace FlaxEditor.Surface.Archetypes public override void SetLocation(int index, Float2 location) { var dataA = (Float4)_node.Values[4 + index * 2]; + var ranges = (Float4)_node.Values[0]; - dataA.X = location.X; + dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y); _node.Values[4 + index * 2] = dataA; _node.Surface.MarkAsEdited(); @@ -750,9 +751,10 @@ namespace FlaxEditor.Surface.Archetypes public override void SetLocation(int index, Float2 location) { var dataA = (Float4)_node.Values[4 + index * 2]; + var ranges = (Float4)_node.Values[0]; - dataA.X = location.X; - dataA.Y = location.Y; + dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y); + dataA.Y = Mathf.Clamp(location.Y, ranges.Z, ranges.W); _node.Values[4 + index * 2] = dataA; _node.Surface.MarkAsEdited(); From f81e89d7d4ecbb31b8f736c41f959011ac6fe1fe Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 16 Mar 2024 16:44:57 +0100 Subject: [PATCH 127/198] Refactor timeline grid drawing to share code between Editor UI elements --- Source/Editor/GUI/CurveEditor.cs | 91 +++++--------------- Source/Editor/GUI/Timeline/GUI/Background.cs | 58 +++---------- Source/Editor/Utilities/Utils.cs | 57 ++++++++++++ 3 files changed, 91 insertions(+), 115 deletions(-) diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 141b3aa60..bc96cf3bc 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -431,7 +431,6 @@ namespace FlaxEditor.GUI /// protected CurveEditor() { - _tickStrengths = new float[TickSteps.Length]; Accessor.GetDefaultValue(out DefaultValue); var style = Style.Current; @@ -780,75 +779,31 @@ namespace FlaxEditor.GUI return _mainPanel.PointToParent(point); } - private void DrawAxis(Float2 axis, ref Rectangle viewRect, float min, float max, float pixelRange) + private void DrawAxis(Float2 axis, Rectangle viewRect, float min, float max, float pixelRange) { - int minDistanceBetweenTicks = 20; - int maxDistanceBetweenTicks = 60; - var range = max - min; - - // Find the strength for each modulo number tick marker - int smallestTick = 0; - int biggestTick = TickSteps.Length - 1; - for (int i = TickSteps.Length - 1; i >= 0; i--) + Utilities.Utils.DrawCurveTicks((float tick, float strength) => { - // Calculate how far apart these modulo tick steps are spaced - float tickSpacing = TickSteps[i] * pixelRange / range; + var p = PointFromKeyframes(axis * tick, ref viewRect); - // Calculate the strength of the tick markers based on the spacing - _tickStrengths[i] = Mathf.Saturate((tickSpacing - minDistanceBetweenTicks) / (maxDistanceBetweenTicks - minDistanceBetweenTicks)); + // Draw line + var lineRect = new Rectangle + ( + viewRect.Location + (p - 0.5f) * axis, + Float2.Lerp(viewRect.Size, Float2.One, axis) + ); + Render2D.FillRectangle(lineRect, _linesColor.AlphaMultiplied(strength)); - // Beyond threshold the ticks don't get any bigger or fatter - if (_tickStrengths[i] >= 1) - biggestTick = i; - - // Do not show small tick markers - if (tickSpacing <= minDistanceBetweenTicks) - { - smallestTick = i; - break; - } - } - - // Draw all tick levels - int tickLevels = biggestTick - smallestTick + 1; - for (int level = 0; level < tickLevels; level++) - { - float strength = _tickStrengths[smallestTick + level]; - if (strength <= Mathf.Epsilon) - continue; - - // Draw all ticks - int l = Mathf.Clamp(smallestTick + level, 0, TickSteps.Length - 1); - int startTick = Mathf.FloorToInt(min / TickSteps[l]); - int endTick = Mathf.CeilToInt(max / TickSteps[l]); - for (int i = startTick; i <= endTick; i++) - { - if (l < biggestTick && (i % Mathf.RoundToInt(TickSteps[l + 1] / TickSteps[l]) == 0)) - continue; - - var tick = i * TickSteps[l]; - var p = PointFromKeyframes(axis * tick, ref viewRect); - - // Draw line - var lineRect = new Rectangle - ( - viewRect.Location + (p - 0.5f) * axis, - Float2.Lerp(viewRect.Size, Float2.One, axis) - ); - Render2D.FillRectangle(lineRect, _linesColor.AlphaMultiplied(strength)); - - // Draw label - string label = tick.ToString(CultureInfo.InvariantCulture); - var labelRect = new Rectangle - ( - viewRect.X + 4.0f + (p.X * axis.X), - viewRect.Y - LabelsSize + (p.Y * axis.Y) + (viewRect.Size.Y * axis.X), - 50, - LabelsSize - ); - Render2D.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); - } - } + // Draw label + string label = tick.ToString(CultureInfo.InvariantCulture); + var labelRect = new Rectangle + ( + viewRect.X + 4.0f + (p.X * axis.X), + viewRect.Y - LabelsSize + (p.Y * axis.Y) + (viewRect.Size.Y * axis.X), + 50, + LabelsSize + ); + Render2D.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); + }, TickSteps, ref _tickStrengths, min, max, pixelRange); } /// @@ -890,9 +845,9 @@ namespace FlaxEditor.GUI Render2D.PushClip(ref viewRect); if ((ShowAxes & UseMode.Vertical) == UseMode.Vertical) - DrawAxis(Float2.UnitX, ref viewRect, min.X, max.X, pixelRange.X); + DrawAxis(Float2.UnitX, viewRect, min.X, max.X, pixelRange.X); if ((ShowAxes & UseMode.Horizontal) == UseMode.Horizontal) - DrawAxis(Float2.UnitY, ref viewRect, min.Y, max.Y, pixelRange.Y); + DrawAxis(Float2.UnitY, viewRect, min.Y, max.Y, pixelRange.Y); Render2D.PopClip(); } diff --git a/Source/Editor/GUI/Timeline/GUI/Background.cs b/Source/Editor/GUI/Timeline/GUI/Background.cs index 4ec634a0b..7bda8f4c0 100644 --- a/Source/Editor/GUI/Timeline/GUI/Background.cs +++ b/Source/Editor/GUI/Timeline/GUI/Background.cs @@ -28,7 +28,6 @@ namespace FlaxEditor.GUI.Timeline.GUI { _timeline = timeline; _tickSteps = Utilities.Utils.CurveTickSteps; - _tickStrengths = new float[_tickSteps.Length]; } private void UpdateSelectionRectangle() @@ -173,55 +172,20 @@ namespace FlaxEditor.GUI.Timeline.GUI var rightFrame = Mathf.Ceil((right - Timeline.StartOffset) / zoom) * _timeline.FramesPerSecond; var min = leftFrame; var max = rightFrame; - int smallestTick = 0; - int biggestTick = _tickSteps.Length - 1; - for (int i = _tickSteps.Length - 1; i >= 0; i--) - { - // Calculate how far apart these modulo tick steps are spaced - float tickSpacing = _tickSteps[i] * _timeline.Zoom; - - // Calculate the strength of the tick markers based on the spacing - _tickStrengths[i] = Mathf.Saturate((tickSpacing - minDistanceBetweenTicks) / (maxDistanceBetweenTicks - minDistanceBetweenTicks)); - - // Beyond threshold the ticks don't get any bigger or fatter - if (_tickStrengths[i] >= 1) - biggestTick = i; - - // Do not show small tick markers - if (tickSpacing <= minDistanceBetweenTicks) - { - smallestTick = i; - break; - } - } - int tickLevels = biggestTick - smallestTick + 1; // Draw vertical lines for time axis - for (int level = 0; level < tickLevels; level++) + var pixelsInRange = _timeline.Zoom; + var pixelRange = pixelsInRange * (max - min); + var tickRange = Utilities.Utils.DrawCurveTicks((float tick, float strength) => { - float strength = _tickStrengths[smallestTick + level]; - if (strength <= Mathf.Epsilon) - continue; - - // Draw all ticks - int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1); - var lStep = _tickSteps[l]; - var lNextStep = _tickSteps[l + 1]; - int startTick = Mathf.FloorToInt(min / lStep); - int endTick = Mathf.CeilToInt(max / lStep); - Color lineColor = style.ForegroundDisabled.RGBMultiplied(0.7f).AlphaMultiplied(strength); - for (int i = startTick; i <= endTick; i++) - { - if (l < biggestTick && (i % Mathf.RoundToInt(lNextStep / lStep) == 0)) - continue; - var tick = i * lStep; - var time = tick / _timeline.FramesPerSecond; - var x = time * zoom + Timeline.StartOffset; - - // Draw line - Render2D.FillRectangle(new Rectangle(x - 0.5f, 0, 1.0f, height), lineColor); - } - } + var time = tick / _timeline.FramesPerSecond; + var x = time * zoom + Timeline.StartOffset; + var lineColor = style.ForegroundDisabled.RGBMultiplied(0.7f).AlphaMultiplied(strength); + Render2D.FillRectangle(new Rectangle(x - 0.5f, 0, 1.0f, height), lineColor); + }, _tickSteps, ref _tickStrengths, min, max, pixelRange, minDistanceBetweenTicks, maxDistanceBetweenTicks); + var smallestTick = tickRange.X; + var biggestTick = tickRange.Y; + var tickLevels = biggestTick - smallestTick + 1; // Draw selection rectangle if (_isSelecting) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index b403c1489..576c30f89 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -243,6 +243,63 @@ namespace FlaxEditor.Utilities 500000, 1000000, 5000000, 10000000, 100000000 }; + internal delegate void DrawCurveTick(float tick, float strength); + + internal static Int2 DrawCurveTicks(DrawCurveTick drawTick, float[] tickSteps, ref float[] tickStrengths, float min, float max, float pixelRange, float minDistanceBetweenTicks = 20, float maxDistanceBetweenTicks = 60) + { + if (tickStrengths == null || tickStrengths.Length != tickSteps.Length) + tickStrengths = new float[tickSteps.Length]; + + // Find the strength for each modulo number tick marker + var pixelsInRange = pixelRange / (max - min); + var smallestTick = 0; + var biggestTick = tickSteps.Length - 1; + for (int i = tickSteps.Length - 1; i >= 0; i--) + { + // Calculate how far apart these modulo tick steps are spaced + float tickSpacing = tickSteps[i] * pixelsInRange; + + // Calculate the strength of the tick markers based on the spacing + tickStrengths[i] = Mathf.Saturate((tickSpacing - minDistanceBetweenTicks) / (maxDistanceBetweenTicks - minDistanceBetweenTicks)); + + // Beyond threshold the ticks don't get any bigger or fatter + if (tickStrengths[i] >= 1) + biggestTick = i; + + // Do not show small tick markers + if (tickSpacing <= minDistanceBetweenTicks) + { + smallestTick = i; + break; + } + } + var tickLevels = biggestTick - smallestTick + 1; + + // Draw all tick levels + for (int level = 0; level < tickLevels; level++) + { + float strength = tickStrengths[smallestTick + level]; + if (strength <= Mathf.Epsilon) + continue; + + // Draw all ticks + int l = Mathf.Clamp(smallestTick + level, 0, tickSteps.Length - 1); + var lStep = tickSteps[l]; + var lNextStep = tickSteps[l + 1]; + int startTick = Mathf.FloorToInt(min / lStep); + int endTick = Mathf.CeilToInt(max / lStep); + for (int i = startTick; i <= endTick; i++) + { + if (l < biggestTick && (i % Mathf.RoundToInt(lNextStep / lStep) == 0)) + continue; + var tick = i * lStep; + drawTick(tick, strength); + } + } + + return new Int2(smallestTick, biggestTick); + } + /// /// Determines whether the specified path string contains any invalid character. /// From 8d149b94f18fb90f70665e0b9cd35a9e9ff3685c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 16 Mar 2024 16:46:10 +0100 Subject: [PATCH 128/198] Refactor editor viepwort camera to unify focusing on selected objects --- Source/Editor/Viewport/Cameras/FPSCamera.cs | 84 +----------------- .../Editor/Viewport/Cameras/ViewportCamera.cs | 87 +++++++++++++++++++ .../Viewport/MainEditorGizmoViewport.cs | 9 +- .../Editor/Viewport/PrefabWindowViewport.cs | 11 +-- 4 files changed, 92 insertions(+), 99 deletions(-) diff --git a/Source/Editor/Viewport/Cameras/FPSCamera.cs b/Source/Editor/Viewport/Cameras/FPSCamera.cs index 83fc49cd6..45770b007 100644 --- a/Source/Editor/Viewport/Cameras/FPSCamera.cs +++ b/Source/Editor/Viewport/Cameras/FPSCamera.cs @@ -6,9 +6,7 @@ using Real = System.Double; using Real = System.Single; #endif -using System.Collections.Generic; using FlaxEditor.Gizmo; -using FlaxEditor.SceneGraph; using FlaxEngine; namespace FlaxEditor.Viewport.Cameras @@ -85,86 +83,8 @@ namespace FlaxEditor.Viewport.Cameras _moveStartTime = Time.UnscaledGameTime; } - /// - /// Moves the viewport to visualize the actor. - /// - /// The actor to preview. - public void ShowActor(Actor actor) - { - Editor.GetActorEditorSphere(actor, out BoundingSphere sphere); - ShowSphere(ref sphere); - } - - /// - /// Moves the viewport to visualize selected actors. - /// - /// The actors to show. - /// The used orientation. - public void ShowActor(Actor actor, ref Quaternion orientation) - { - Editor.GetActorEditorSphere(actor, out BoundingSphere sphere); - ShowSphere(ref sphere, ref orientation); - } - - /// - /// Moves the viewport to visualize selected actors. - /// - /// The actors to show. - public void ShowActors(List selection) - { - if (selection.Count == 0) - return; - - BoundingSphere mergesSphere = BoundingSphere.Empty; - for (int i = 0; i < selection.Count; i++) - { - selection[i].GetEditorSphere(out var sphere); - BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere); - } - - if (mergesSphere == BoundingSphere.Empty) - return; - ShowSphere(ref mergesSphere); - } - - /// - /// Moves the viewport to visualize selected actors. - /// - /// The actors to show. - /// The used orientation. - public void ShowActors(List selection, ref Quaternion orientation) - { - if (selection.Count == 0) - return; - - BoundingSphere mergesSphere = BoundingSphere.Empty; - for (int i = 0; i < selection.Count; i++) - { - selection[i].GetEditorSphere(out var sphere); - BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere); - } - - if (mergesSphere == BoundingSphere.Empty) - return; - ShowSphere(ref mergesSphere, ref orientation); - } - - /// - /// Moves the camera to visualize given world area defined by the sphere. - /// - /// The sphere. - public void ShowSphere(ref BoundingSphere sphere) - { - var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f); - ShowSphere(ref sphere, ref q); - } - - /// - /// Moves the camera to visualize given world area defined by the sphere. - /// - /// The sphere. - /// The camera orientation. - public void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation) + /// + public override void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation) { Vector3 position; if (Viewport.UseOrthographicProjection) diff --git a/Source/Editor/Viewport/Cameras/ViewportCamera.cs b/Source/Editor/Viewport/Cameras/ViewportCamera.cs index 5e25a29bb..f8019a481 100644 --- a/Source/Editor/Viewport/Cameras/ViewportCamera.cs +++ b/Source/Editor/Viewport/Cameras/ViewportCamera.cs @@ -6,6 +6,9 @@ using Real = System.Double; using Real = System.Single; #endif +using System.Collections.Generic; +using FlaxEditor.Gizmo; +using FlaxEditor.SceneGraph; using FlaxEngine; namespace FlaxEditor.Viewport.Cameras @@ -33,6 +36,90 @@ namespace FlaxEditor.Viewport.Cameras /// public virtual bool UseMovementSpeed => true; + /// + /// Focuses the viewport on the current selection of the gizmo. + /// + /// The gizmo collection (from viewport). + /// The target view orientation. + public virtual void FocusSelection(GizmosCollection gizmos, ref Quaternion orientation) + { + var transformGizmo = gizmos.Get(); + if (transformGizmo == null || transformGizmo.SelectedParents.Count == 0) + return; + if (gizmos.Active != null) + { + var gizmoBounds = gizmos.Active.FocusBounds; + if (gizmoBounds != BoundingSphere.Empty) + { + ShowSphere(ref gizmoBounds, ref orientation); + return; + } + } + ShowActors(transformGizmo.SelectedParents, ref orientation); + } + + /// + /// Moves the viewport to visualize the actor. + /// + /// The actor to preview. + public virtual void ShowActor(Actor actor) + { + Editor.GetActorEditorSphere(actor, out BoundingSphere sphere); + ShowSphere(ref sphere); + } + + /// + /// Moves the viewport to visualize selected actors. + /// + /// The actors to show. + public void ShowActors(List selection) + { + var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f); + ShowActors(selection, ref q); + } + + /// + /// Moves the viewport to visualize selected actors. + /// + /// The actors to show. + /// The used orientation. + public virtual void ShowActors(List selection, ref Quaternion orientation) + { + if (selection.Count == 0) + return; + + BoundingSphere mergesSphere = BoundingSphere.Empty; + for (int i = 0; i < selection.Count; i++) + { + selection[i].GetEditorSphere(out var sphere); + BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere); + } + + if (mergesSphere == BoundingSphere.Empty) + return; + ShowSphere(ref mergesSphere, ref orientation); + } + + /// + /// Moves the camera to visualize given world area defined by the sphere. + /// + /// The sphere. + public void ShowSphere(ref BoundingSphere sphere) + { + var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f); + ShowSphere(ref sphere, ref q); + } + + /// + /// Moves the camera to visualize given world area defined by the sphere. + /// + /// The sphere. + /// The camera orientation. + public virtual void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation) + { + SetArcBallView(orientation, sphere.Center, sphere.Radius); + } + /// /// Sets view orientation and position to match the arc ball camera style view for the given target object bounds. /// diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 1e63888bf..6d8b572a4 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -770,14 +770,7 @@ namespace FlaxEditor.Viewport /// The target view orientation. public void FocusSelection(ref Quaternion orientation) { - if (TransformGizmo.SelectedParents.Count == 0) - return; - - var gizmoBounds = Gizmos.Active.FocusBounds; - if (gizmoBounds != BoundingSphere.Empty) - ((FPSCamera)ViewportCamera).ShowSphere(ref gizmoBounds, ref orientation); - else - ((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orientation); + ViewportCamera.FocusSelection(Gizmos, ref orientation); } /// diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index f66d4e861..4166d6163 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -309,7 +309,7 @@ namespace FlaxEditor.Viewport public void ShowSelectedActors() { var orient = ViewOrientation; - ((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orient); + ViewportCamera.ShowActors(TransformGizmo.SelectedParents, ref orient); } /// @@ -721,14 +721,7 @@ namespace FlaxEditor.Viewport /// The target view orientation. public void FocusSelection(ref Quaternion orientation) { - if (TransformGizmo.SelectedParents.Count == 0) - return; - - var gizmoBounds = Gizmos.Active.FocusBounds; - if (gizmoBounds != BoundingSphere.Empty) - ((FPSCamera)ViewportCamera).ShowSphere(ref gizmoBounds, ref orientation); - else - ((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orientation); + ViewportCamera.FocusSelection(Gizmos, ref orientation); } /// From c6aa18c47cff37e1f10361c93908ebbd68e9ece4 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 16 Mar 2024 13:20:18 -0500 Subject: [PATCH 129/198] Remove import UI on Animation not created by an import. --- Source/Editor/Windows/Assets/AnimationWindow.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs index ae87826b0..2411daf86 100644 --- a/Source/Editor/Windows/Assets/AnimationWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationWindow.cs @@ -186,6 +186,10 @@ namespace FlaxEditor.Windows.Assets base.Initialize(layout); + // Ignore import settings GUI if the type is not animation. This removes the import UI if the animation asset was not created using an import. + if (proxy.ImportSettings.Settings.Type != FlaxEngine.Tools.ModelTool.ModelType.Animation) + return; + // Import Settings { var group = layout.Group("Import Settings"); From c60244878d33cd719eaa342bcea83d0bb6500abc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 16 Mar 2024 22:00:40 +0100 Subject: [PATCH 130/198] Add **UI Control gizmo for editing UIs** --- Source/Editor/Gizmo/IGizmoOwner.cs | 5 + Source/Editor/Gizmo/TransformGizmo.cs | 5 + Source/Editor/Gizmo/UIEditorGizmo.cs | 724 ++++++++++++++++++ Source/Editor/Surface/VisjectSurface.Draw.cs | 10 +- Source/Editor/Viewport/EditorGizmoViewport.cs | 3 + Source/Editor/Viewport/EditorViewport.cs | 5 +- .../Viewport/MainEditorGizmoViewport.cs | 18 +- .../Editor/Viewport/PrefabWindowViewport.cs | 114 ++- .../Editor/Viewport/Previews/AssetPreview.cs | 2 +- .../Editor/Viewport/Previews/PrefabPreview.cs | 24 +- .../Windows/Assets/PrefabWindow.Hierarchy.cs | 3 +- Source/Editor/Windows/Assets/PrefabWindow.cs | 5 +- Source/Editor/Windows/GameWindow.cs | 169 +--- .../Windows/SceneTreeWindow.ContextMenu.cs | 4 +- Source/Engine/UI/GUI/Control.Bounds.cs | 2 +- 15 files changed, 871 insertions(+), 222 deletions(-) create mode 100644 Source/Editor/Gizmo/UIEditorGizmo.cs diff --git a/Source/Editor/Gizmo/IGizmoOwner.cs b/Source/Editor/Gizmo/IGizmoOwner.cs index 7237d724b..0a5c520a7 100644 --- a/Source/Editor/Gizmo/IGizmoOwner.cs +++ b/Source/Editor/Gizmo/IGizmoOwner.cs @@ -117,5 +117,10 @@ namespace FlaxEditor.Gizmo /// /// The new actor to spawn. void Spawn(Actor actor); + + /// + /// Opens the context menu at the current mouse location (using current selection). + /// + void OpenContextMenu(); } } diff --git a/Source/Editor/Gizmo/TransformGizmo.cs b/Source/Editor/Gizmo/TransformGizmo.cs index 64f969990..cd5e16e92 100644 --- a/Source/Editor/Gizmo/TransformGizmo.cs +++ b/Source/Editor/Gizmo/TransformGizmo.cs @@ -42,6 +42,11 @@ namespace FlaxEditor.Gizmo /// public Action Duplicate; + /// + /// Gets the array of selected objects. + /// + public List Selection => _selection; + /// /// Gets the array of selected parent objects (as actors). /// diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs new file mode 100644 index 000000000..4fd059202 --- /dev/null +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -0,0 +1,724 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + +using System.Collections.Generic; +using System.Linq; +using FlaxEditor.Gizmo; +using FlaxEditor.SceneGraph; +using FlaxEditor.SceneGraph.Actors; +using FlaxEditor.Viewport.Cameras; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor +{ + /// + /// UI editor camera. + /// + [HideInEditor] + internal sealed class UIEditorCamera : ViewportCamera + { + public UIEditorRoot UIEditor; + + public void ShowActors(IEnumerable actors) + { + // Calculate bounds of all selected objects + var areaRect = Rectangle.Empty; + var root = UIEditor.UIRoot; + foreach (var actor in actors) + { + Rectangle bounds; + if (actor is UIControl uiControl && uiControl.HasControl && uiControl.IsActive) + { + var control = uiControl.Control; + bounds = control.EditorBounds; + + var ul = control.PointToParent(root, bounds.UpperLeft); + var ur = control.PointToParent(root, bounds.UpperRight); + var bl = control.PointToParent(root, bounds.BottomLeft); + var br = control.PointToParent(root, bounds.BottomRight); + + var min = Float2.Min(Float2.Min(ul, ur), Float2.Min(bl, br)); + var max = Float2.Max(Float2.Max(ul, ur), Float2.Max(bl, br)); + bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero)); + } + else if (actor is UICanvas uiCanvas && uiCanvas.IsActive && uiCanvas.GUI.Parent == root) + { + bounds = uiCanvas.GUI.Bounds; + } + else + continue; + + if (areaRect == Rectangle.Empty) + areaRect = bounds; + else + areaRect = Rectangle.Union(areaRect, bounds); + } + if (areaRect == Rectangle.Empty) + return; + + // Add margin + areaRect = areaRect.MakeExpanded(100.0f); + + // Show bounds + UIEditor.ViewScale = (UIEditor.Size / areaRect.Size).MinValue * 0.95f; + UIEditor.ViewCenterPosition = areaRect.Center; + } + + public override void FocusSelection(GizmosCollection gizmos, ref Quaternion orientation) + { + ShowActors(gizmos.Get().Selection, ref orientation); + } + + public override void ShowActor(Actor actor) + { + ShowActors(new[] { actor }); + } + + public override void ShowActors(List selection, ref Quaternion orientation) + { + ShowActors(selection.ConvertAll(x => (Actor)x.EditableObject)); + } + + public override void UpdateView(float dt, ref Vector3 moveDelta, ref Float2 mouseDelta, out bool centerMouse) + { + centerMouse = false; + } + } + + /// + /// Root control for UI Controls presentation in the game/prefab viewport. + /// + [HideInEditor] + internal class UIEditorRoot : InputsPassThrough + { + /// + /// View for the UI structure to be linked in for camera zoom and panning operations. + /// + private sealed class View : ContainerControl + { + public View(UIEditorRoot parent) + { + AutoFocus = false; + ClipChildren = false; + CullChildren = false; + Pivot = Float2.Zero; + Size = new Float2(1920, 1080); + Parent = parent; + } + + public override bool RayCast(ref Float2 location, out Control hit) + { + // Ignore self + return RayCastChildren(ref location, out hit); + } + + public override bool IntersectsContent(ref Float2 locationParent, out Float2 location) + { + location = PointFromParent(ref locationParent); + return true; + } + + public override void DrawSelf() + { + var uiRoot = (UIEditorRoot)Parent; + if (!uiRoot.EnableBackground) + return; + + // Draw canvas area + var bounds = new Rectangle(Float2.Zero, Size); + Render2D.FillRectangle(bounds, new Color(0, 0, 0, 0.2f)); + } + } + + private bool _mouseMovesControl, _mouseMovesView; + private Float2 _mouseMovesPos, _moveSnapDelta; + private float _mouseMoveSum; + private UndoMultiBlock _undoBlock; + private View _view; + private float[] _gridTickSteps = Utilities.Utils.CurveTickSteps, _gridTickStrengths; + + /// + /// True if enable displaying UI editing background and grid elements. + /// + public virtual bool EnableBackground => false; + + /// + /// True if enable selecting controls with mouse button. + /// + public virtual bool EnableSelecting => false; + + /// + /// True if enable panning and zooming the view. + /// + public bool EnableCamera => _view != null; + + /// + /// Transform gizmo to use sync with (selection, snapping, transformation settings). + /// + public virtual TransformGizmo TransformGizmo => null; + + /// + /// The root control for controls to be linked in. + /// + public readonly ContainerControl UIRoot; + + internal Float2 ViewPosition + { + get => _view.Location / -ViewScale; + set => _view.Location = value * -ViewScale; + } + + internal Float2 ViewCenterPosition + { + get => (_view.Location - Size * 0.5f) / -ViewScale; + set => _view.Location = Size * 0.5f + value * -ViewScale; + } + + internal float ViewScale + { + get => _view.Scale.X; + set + { + value = Mathf.Clamp(value, 0.1f, 4.0f); + _view.Scale = new Float2(value); + } + } + + public UIEditorRoot(bool enableCamera = false) + { + AnchorPreset = AnchorPresets.StretchAll; + Offsets = Margin.Zero; + AutoFocus = false; + UIRoot = this; + CullChildren = false; + ClipChildren = true; + if (enableCamera) + { + _view = new View(this); + UIRoot = _view; + } + } + + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (base.OnMouseDown(location, button)) + return true; + + var transformGizmo = TransformGizmo; + var owner = transformGizmo?.Owner; + if (EnableSelecting && owner != null && !_mouseMovesControl && button == MouseButton.Left) + { + // Raycast the control under the mouse + var mousePos = PointFromWindow(RootWindow.MousePosition); + if (RayCastControl(ref mousePos, out var hitControl)) + { + var uiControlNode = FindUIControlNode(hitControl); + if (uiControlNode != null) + { + // Select node (with additive mode) + var selection = new List(); + if (Root.GetKey(KeyboardKeys.Control)) + { + // Add/remove from selection + selection.AddRange(transformGizmo.Selection); + if (transformGizmo.Selection.Contains(uiControlNode)) + selection.Remove(uiControlNode); + else + selection.Add(uiControlNode); + } + else + { + // Select + selection.Add(uiControlNode); + } + owner.Select(selection); + + // Initialize control movement + _mouseMovesControl = true; + _mouseMovesPos = location; + _mouseMoveSum = 0.0f; + _moveSnapDelta = Float2.Zero; + Focus(); + StartMouseCapture(); + return true; + } + } + } + if (EnableCamera && (button == MouseButton.Right || button == MouseButton.Middle)) + { + // Initialize surface movement + _mouseMovesView = true; + _mouseMovesPos = location; + _mouseMoveSum = 0.0f; + Focus(); + StartMouseCapture(); + return true; + } + + return false; + } + + public override void OnMouseMove(Float2 location) + { + base.OnMouseMove(location); + + var transformGizmo = TransformGizmo; + if (_mouseMovesControl && transformGizmo != null) + { + // Calculate transform delta + var delta = location - _mouseMovesPos; + if (transformGizmo.TranslationSnapEnable || transformGizmo.Owner.UseSnapping) + { + _moveSnapDelta += delta; + delta = Float2.SnapToGrid(_moveSnapDelta, new Float2(transformGizmo.TranslationSnapValue * ViewScale)); + _moveSnapDelta -= delta; + } + + // Move selected controls + if (delta.LengthSquared > 0.0f) + { + StartUndo(); + var moved = false; + var moveLocation = _mouseMovesPos + delta; + var selection = transformGizmo.Selection; + for (var i = 0; i < selection.Count; i++) + { + if (IsValidControl(selection[i], out var uiControl)) + { + // Move control (handle any control transformations by moving in editor's local-space) + var control = uiControl.Control; + var localLocation = control.LocalLocation; + var pointOrigin = control.Parent ?? control; + var startPos = pointOrigin.PointFromParent(this, _mouseMovesPos); + var endPos = pointOrigin.PointFromParent(this, moveLocation); + var uiControlDelta = endPos - startPos; + control.LocalLocation = localLocation + uiControlDelta; + + // Don't move if layout doesn't allow it + if (control.Parent != null) + control.Parent.PerformLayout(); + else + control.PerformLayout(); + + // Check if control was moved (parent container could block it) + if (localLocation != control.LocalLocation) + moved = true; + } + } + _mouseMovesPos = location; + _mouseMoveSum += delta.Length; + if (moved) + Cursor = CursorType.SizeAll; + } + } + if (_mouseMovesView) + { + // Move view + var delta = location - _mouseMovesPos; + if (delta.LengthSquared > 4.0f) + { + _mouseMovesPos = location; + _mouseMoveSum += delta.Length; + _view.Location += delta; + Cursor = CursorType.SizeAll; + } + } + } + + public override bool OnMouseUp(Float2 location, MouseButton button) + { + EndMovingControls(); + if (_mouseMovesView) + { + EndMovingView(); + if (button == MouseButton.Right && _mouseMoveSum < 2.0f) + TransformGizmo.Owner.OpenContextMenu(); + } + + return base.OnMouseUp(location, button); + } + + public override void OnMouseLeave() + { + EndMovingControls(); + EndMovingView(); + + base.OnMouseLeave(); + } + + public override void OnLostFocus() + { + EndMovingControls(); + EndMovingView(); + + base.OnLostFocus(); + } + + public override bool OnMouseWheel(Float2 location, float delta) + { + if (base.OnMouseWheel(location, delta)) + return true; + + if (EnableCamera && !_mouseMovesControl) + { + // Zoom view + var nextViewScale = ViewScale + delta * 0.1f; + if (delta > 0 && !_mouseMovesControl) + { + // Scale towards mouse when zooming in + var nextCenterPosition = ViewPosition + location / ViewScale; + ViewScale = nextViewScale; + ViewPosition = nextCenterPosition - (location / ViewScale); + } + else + { + // Scale while keeping center position when zooming out or when dragging view + var viewCenter = ViewCenterPosition; + ViewScale = nextViewScale; + ViewCenterPosition = viewCenter; + } + + return true; + } + + return false; + } + + public override void Draw() + { + if (EnableBackground) + { + // Draw background + Surface.VisjectSurface.DrawBackgroundDefault(Editor.Instance.UI.VisjectSurfaceBackground, Width, Height); + + // Draw grid + var viewRect = GetClientArea(); + var upperLeft = _view.PointFromParent(viewRect.Location); + var bottomRight = _view.PointFromParent(viewRect.Size); + var min = Float2.Min(upperLeft, bottomRight); + var max = Float2.Max(upperLeft, bottomRight); + var pixelRange = (max - min) * ViewScale; + Render2D.PushClip(ref viewRect); + DrawAxis(Float2.UnitX, viewRect, min.X, max.X, pixelRange.X); + DrawAxis(Float2.UnitY, viewRect, min.Y, max.Y, pixelRange.Y); + Render2D.PopClip(); + } + + base.Draw(); + + bool drawAnySelectedControl = false; + var transformGizmo = TransformGizmo; + if (transformGizmo != null) + { + // Selected UI controls outline + var selection = transformGizmo.Selection; + for (var i = 0; i < selection.Count; i++) + { + if (IsValidControl(selection[i], out var controlActor)) + { + DrawControlBounds(controlActor.Control, true, ref drawAnySelectedControl); + // TODO: draw anchors + } + } + } + if (EnableSelecting && !_mouseMovesControl && IsMouseOver) + { + // Highlight control under mouse for easier selecting (except if already selected) + var mousePos = PointFromWindow(RootWindow.MousePosition); + if (RayCastControl(ref mousePos, out var hitControl) && + (transformGizmo == null || !transformGizmo.Selection.Any(x => x.EditableObject is UIControl controlActor && controlActor.Control == hitControl))) + { + DrawControlBounds(hitControl, false, ref drawAnySelectedControl); + } + } + if (drawAnySelectedControl) + Render2D.PopTransform(); + + if (EnableBackground) + { + // Draw border + if (ContainsFocus) + { + Render2D.DrawRectangle(new Rectangle(1, 1, Width - 2, Height - 2), Editor.IsPlayMode ? Color.OrangeRed : Style.Current.BackgroundSelected); + } + } + } + + public override void OnDestroy() + { + if (IsDisposing) + return; + EndMovingControls(); + EndMovingView(); + + base.OnDestroy(); + } + + private void DrawAxis(Float2 axis, Rectangle viewRect, float min, float max, float pixelRange) + { + var style = Style.Current; + var linesColor = style.ForegroundDisabled.RGBMultiplied(0.5f); + var labelsColor = style.ForegroundDisabled; + var labelsSize = 10.0f; + Utilities.Utils.DrawCurveTicks((float tick, float strength) => + { + var p = _view.PointToParent(axis * tick); + + // Draw line + var lineRect = new Rectangle + ( + viewRect.Location + (p - 0.5f) * axis, + Float2.Lerp(viewRect.Size, Float2.One, axis) + ); + Render2D.FillRectangle(lineRect, linesColor.AlphaMultiplied(strength)); + + // Draw label + string label = tick.ToString(System.Globalization.CultureInfo.InvariantCulture); + var labelRect = new Rectangle + ( + viewRect.X + 4.0f + (p.X * axis.X), + viewRect.Y - labelsSize + (p.Y * axis.Y) + (viewRect.Size.Y * axis.X), + 50, + labelsSize + ); + Render2D.DrawText(style.FontSmall, label, labelRect, labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); + }, _gridTickSteps, ref _gridTickStrengths, min, max, pixelRange); + } + + private void DrawControlBounds(Control control, bool selection, ref bool drawAnySelectedControl) + { + if (!drawAnySelectedControl) + { + drawAnySelectedControl = true; + Render2D.PushTransform(ref _cachedTransform); + } + var options = Editor.Instance.Options.Options.Visual; + var bounds = control.EditorBounds; + var ul = control.PointToParent(this, bounds.UpperLeft); + var ur = control.PointToParent(this, bounds.UpperRight); + var bl = control.PointToParent(this, bounds.BottomLeft); + var br = control.PointToParent(this, bounds.BottomRight); + var color = selection ? options.SelectionOutlineColor0 : Style.Current.SelectionBorder; +#if false + // AABB + var min = Float2.Min(Float2.Min(ul, ur), Float2.Min(bl, br)); + var max = Float2.Max(Float2.Max(ul, ur), Float2.Max(bl, br)); + bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero)); + Render2D.DrawRectangle(bounds, color, options.UISelectionOutlineSize); +#else + // OBB + Render2D.DrawLine(ul, ur, color, options.UISelectionOutlineSize); + Render2D.DrawLine(ur, br, color, options.UISelectionOutlineSize); + Render2D.DrawLine(br, bl, color, options.UISelectionOutlineSize); + Render2D.DrawLine(bl, ul, color, options.UISelectionOutlineSize); +#endif + } + + private bool IsValidControl(SceneGraphNode node, out UIControl uiControl) + { + uiControl = null; + if (node.EditableObject is UIControl controlActor) + uiControl = controlActor; + return uiControl != null && + uiControl.Control != null && + uiControl.Control.VisibleInHierarchy && + uiControl.Control.RootWindow != null; + } + + private bool RayCastControl(ref Float2 location, out Control hit) + { +#if false + // Raycast only controls with content (eg. skips transparent panels) + return RayCastChildren(ref location, out hit); +#else + // Find any control under mouse (hierarchical) + hit = GetChildAtRecursive(location); + if (hit is View) + hit = null; + return hit != null; +#endif + } + + private UIControlNode FindUIControlNode(Control control) + { + return FindUIControlNode(TransformGizmo.Owner.SceneGraphRoot, control); + } + + private UIControlNode FindUIControlNode(SceneGraphNode node, Control control) + { + var result = node as UIControlNode; + if (result != null && ((UIControl)result.Actor).Control == control) + return result; + foreach (var e in node.ChildNodes) + { + result = FindUIControlNode(e, control); + if (result != null) + return result; + } + return null; + } + + private void StartUndo() + { + var undo = TransformGizmo?.Owner?.Undo; + if (undo == null || _undoBlock != null) + return; + _undoBlock = new UndoMultiBlock(undo, TransformGizmo.Selection.ConvertAll(x => x.EditableObject), "Edit control"); + } + + private void EndUndo() + { + if (_undoBlock == null) + return; + _undoBlock.Dispose(); + _undoBlock = null; + } + + private void EndMovingControls() + { + if (!_mouseMovesControl) + return; + _mouseMovesControl = false; + EndMouseCapture(); + Cursor = CursorType.Default; + EndUndo(); + } + + private void EndMovingView() + { + if (!_mouseMovesView) + return; + _mouseMovesView = false; + EndMouseCapture(); + Cursor = CursorType.Default; + } + } + + /// + /// Control that can optionally disable inputs to the children. + /// + [HideInEditor] + internal class InputsPassThrough : ContainerControl + { + private bool _isMouseOver; + + /// + /// True if enable input events passing to the UI. + /// + public virtual bool EnableInputs => true; + + public override bool RayCast(ref Float2 location, out Control hit) + { + return RayCastChildren(ref location, out hit); + } + + public override bool ContainsPoint(ref Float2 location, bool precise = false) + { + if (precise) + return false; + return base.ContainsPoint(ref location, precise); + } + + public override bool OnCharInput(char c) + { + if (!EnableInputs) + return false; + return base.OnCharInput(c); + } + + public override DragDropEffect OnDragDrop(ref Float2 location, DragData data) + { + if (!EnableInputs) + return DragDropEffect.None; + return base.OnDragDrop(ref location, data); + } + + public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) + { + if (!EnableInputs) + return DragDropEffect.None; + return base.OnDragEnter(ref location, data); + } + + public override void OnDragLeave() + { + if (!EnableInputs) + return; + base.OnDragLeave(); + } + + public override DragDropEffect OnDragMove(ref Float2 location, DragData data) + { + if (!EnableInputs) + return DragDropEffect.None; + return base.OnDragMove(ref location, data); + } + + public override bool OnKeyDown(KeyboardKeys key) + { + if (!EnableInputs) + return false; + return base.OnKeyDown(key); + } + + public override void OnKeyUp(KeyboardKeys key) + { + if (!EnableInputs) + return; + base.OnKeyUp(key); + } + + public override bool OnMouseDoubleClick(Float2 location, MouseButton button) + { + if (!EnableInputs) + return false; + return base.OnMouseDoubleClick(location, button); + } + + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (!EnableInputs) + return false; + return base.OnMouseDown(location, button); + } + + public override bool IsMouseOver => _isMouseOver; + + public override void OnMouseEnter(Float2 location) + { + _isMouseOver = true; + if (!EnableInputs) + return; + base.OnMouseEnter(location); + } + + public override void OnMouseLeave() + { + _isMouseOver = false; + if (!EnableInputs) + return; + base.OnMouseLeave(); + } + + public override void OnMouseMove(Float2 location) + { + if (!EnableInputs) + return; + base.OnMouseMove(location); + } + + public override bool OnMouseUp(Float2 location, MouseButton button) + { + if (!EnableInputs) + return false; + return base.OnMouseUp(location, button); + } + + public override bool OnMouseWheel(Float2 location, float delta) + { + if (!EnableInputs) + return false; + return base.OnMouseWheel(location, delta); + } + } +} diff --git a/Source/Editor/Surface/VisjectSurface.Draw.cs b/Source/Editor/Surface/VisjectSurface.Draw.cs index 13da1fb97..59a56e3f3 100644 --- a/Source/Editor/Surface/VisjectSurface.Draw.cs +++ b/Source/Editor/Surface/VisjectSurface.Draw.cs @@ -64,7 +64,11 @@ namespace FlaxEditor.Surface /// protected virtual void DrawBackground() { - var background = Style.Background; + DrawBackgroundDefault(Style.Background, Width, Height); + } + + internal static void DrawBackgroundDefault(Texture background, float width, float height) + { if (background && background.ResidentMipLevels > 0) { var bSize = background.Size; @@ -77,8 +81,8 @@ namespace FlaxEditor.Surface if (pos.Y > 0) pos.Y -= bh; - int maxI = Mathf.CeilToInt(Width / bw + 1.0f); - int maxJ = Mathf.CeilToInt(Height / bh + 1.0f); + int maxI = Mathf.CeilToInt(width / bw + 1.0f); + int maxJ = Mathf.CeilToInt(height / bh + 1.0f); for (int i = 0; i < maxI; i++) { diff --git a/Source/Editor/Viewport/EditorGizmoViewport.cs b/Source/Editor/Viewport/EditorGizmoViewport.cs index f1c4fed70..fec1154a5 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -93,6 +93,9 @@ namespace FlaxEditor.Viewport /// public abstract void Spawn(Actor actor); + /// + public abstract void OpenContextMenu(); + /// protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false; diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 785f0e651..59fad6b11 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -10,7 +10,6 @@ using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Widgets; using FlaxEngine; using FlaxEngine.GUI; -using Newtonsoft.Json; using JsonSerializer = FlaxEngine.Json.JsonSerializer; namespace FlaxEditor.Viewport @@ -154,6 +153,7 @@ namespace FlaxEditor.Viewport // Input + internal bool _disableInputUpdate; private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown; private int _deltaFilteringStep; private Float2 _startPos; @@ -1496,6 +1496,9 @@ namespace FlaxEditor.Viewport { base.Update(deltaTime); + if (_disableInputUpdate) + return; + // Update camera bool useMovementSpeed = false; if (_camera != null) diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 6d8b572a4..c9bb4c88a 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; @@ -223,7 +222,7 @@ namespace FlaxEditor.Viewport TransformGizmo = new TransformGizmo(this); TransformGizmo.ApplyTransformation += ApplyTransform; TransformGizmo.ModeChanged += OnGizmoModeChanged; - TransformGizmo.Duplicate += Editor.Instance.SceneEditing.Duplicate; + TransformGizmo.Duplicate += _editor.SceneEditing.Duplicate; Gizmos.Active = TransformGizmo; // Add grid @@ -479,7 +478,7 @@ namespace FlaxEditor.Viewport }; // Spawn - Editor.Instance.SceneEditing.Spawn(actor, parent); + _editor.SceneEditing.Spawn(actor, parent); } private void OnBegin(RenderTask task, GPUContext context) @@ -712,7 +711,7 @@ namespace FlaxEditor.Viewport Vector3 gizmoPosition = TransformGizmo.Position; // Rotate selected objects - bool isPlayMode = Editor.Instance.StateMachine.IsPlayMode; + bool isPlayMode = _editor.StateMachine.IsPlayMode; TransformGizmo.StartTransforming(); for (int i = 0; i < selection.Count; i++) { @@ -787,7 +786,7 @@ namespace FlaxEditor.Viewport Vector3 gizmoPosition = TransformGizmo.Position; // Transform selected objects - bool isPlayMode = Editor.Instance.StateMachine.IsPlayMode; + bool isPlayMode = _editor.StateMachine.IsPlayMode; for (int i = 0; i < selection.Count; i++) { var obj = selection[i]; @@ -929,7 +928,14 @@ namespace FlaxEditor.Viewport { var parent = actor.Parent ?? Level.GetScene(0); actor.Name = Utilities.Utils.IncrementNameNumber(actor.Name, x => parent.GetChild(x) == null); - Editor.Instance.SceneEditing.Spawn(actor); + _editor.SceneEditing.Spawn(actor); + } + + /// + public override void OpenContextMenu() + { + var mouse = PointFromWindow(Root.MousePosition); + _editor.Windows.SceneWin.ShowContextMenu(this, mouse); } /// diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 4166d6163..31f27f708 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -29,7 +29,6 @@ namespace FlaxEditor.Viewport { public PrefabWindowViewport Viewport; - /// public override bool CanRender() { return (Task.View.Flags & ViewFlags.EditorSprites) == ViewFlags.EditorSprites && Enabled; @@ -41,6 +40,24 @@ namespace FlaxEditor.Viewport } } + [HideInEditor] + private sealed class PrefabUIEditorRoot : UIEditorRoot + { + private readonly PrefabWindowViewport _viewport; + + public PrefabUIEditorRoot(PrefabWindowViewport viewport) + : base(true) + { + _viewport = viewport; + Parent = viewport; + } + + public override bool EnableInputs => false; + public override bool EnableSelecting => true; + public override bool EnableBackground => _viewport._hasUILinkedCached; + public override TransformGizmo TransformGizmo => _viewport.TransformGizmo; + } + private readonly PrefabWindow _window; private UpdateDelegate _update; @@ -56,6 +73,9 @@ namespace FlaxEditor.Viewport private PrefabSpritesRenderer _spritesRenderer; private IntPtr _tempDebugDrawContext; + private bool _hasUILinkedCached; + private PrefabUIEditorRoot _uiRoot; + /// /// Drag and drop handlers /// @@ -111,6 +131,11 @@ namespace FlaxEditor.Viewport TransformGizmo.Duplicate += _window.Duplicate; Gizmos.Active = TransformGizmo; + // Use custom root for UI controls + _uiRoot = new PrefabUIEditorRoot(this); + _uiRoot.IndexInParent = 0; // Move viewport down below other widgets in the viewport + _uiParentLink = _uiRoot.UIRoot; + // Transform space widget var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); var transformSpaceToggle = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Globe32, null, true) @@ -237,8 +262,54 @@ namespace FlaxEditor.Viewport SetUpdate(ref _update, OnUpdate); } + /// + /// Updates the viewport's gizmos, especially to toggle between 3D and UI editing modes. + /// + internal void UpdateGizmoMode() + { + // Skip if gizmo mode was unmodified + if (_hasUILinked == _hasUILinkedCached) + return; + _hasUILinkedCached = _hasUILinked; + + if (_hasUILinked) + { + // UI widget + Gizmos.Active = null; + ViewportCamera = new UIEditorCamera { UIEditor = _uiRoot }; + + // Hide 3D visuals + ShowEditorPrimitives = false; + ShowDefaultSceneActors = false; + ShowDebugDraw = false; + + // Show whole UI on startup + ViewportCamera.ShowActor(Instance); + } + else + { + // Generic prefab + Gizmos.Active = TransformGizmo; + ViewportCamera = new FPSCamera(); + } + + // Update default components usage + bool defaultFeatures = !_hasUILinked; + _disableInputUpdate = _hasUILinked; + _spritesRenderer.Enabled = defaultFeatures; + SelectionOutline.Enabled = defaultFeatures; + _showDefaultSceneButton.Visible = defaultFeatures; + _cameraWidget.Visible = defaultFeatures; + _cameraButton.Visible = defaultFeatures; + _orthographicModeButton.Visible = defaultFeatures; + Task.Enabled = defaultFeatures; + UseAutomaticTaskManagement = defaultFeatures; + TintColor = defaultFeatures ? Color.White : Color.Transparent; + } + private void OnUpdate(float deltaTime) { + UpdateGizmoMode(); for (int i = 0; i < Gizmos.Count; i++) { Gizmos[i].Update(deltaTime); @@ -369,6 +440,13 @@ namespace FlaxEditor.Viewport _window.Spawn(actor); } + /// + public void OpenContextMenu() + { + var mouse = PointFromWindow(Root.MousePosition); + _window.ShowContextMenu(this, ref mouse); + } + /// protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false; @@ -545,40 +623,6 @@ namespace FlaxEditor.Viewport } } - /// - public override void Draw() - { - base.Draw(); - - // Selected UI controls outline - bool drawAnySelectedControl = false; - // TODO: optimize this (eg. cache list of selected UIControl's when selection gets changed) - for (var i = 0; i < _window.Selection.Count; i++) - { - if (_window.Selection[i]?.EditableObject is UIControl controlActor && controlActor && controlActor.Control != null && controlActor.Control.VisibleInHierarchy && controlActor.Control.RootWindow != null) - { - if (!drawAnySelectedControl) - { - drawAnySelectedControl = true; - Render2D.PushTransform(ref _cachedTransform); - } - var control = controlActor.Control; - var bounds = control.EditorBounds; - var p1 = control.PointToParent(this, bounds.UpperLeft); - var p2 = control.PointToParent(this, bounds.UpperRight); - var p3 = control.PointToParent(this, bounds.BottomLeft); - var p4 = control.PointToParent(this, bounds.BottomRight); - var min = Float2.Min(Float2.Min(p1, p2), Float2.Min(p3, p4)); - var max = Float2.Max(Float2.Max(p1, p2), Float2.Max(p3, p4)); - bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero)); - var options = Editor.Instance.Options.Options.Visual; - Render2D.DrawRectangle(bounds, options.SelectionOutlineColor0, options.UISelectionOutlineSize); - } - } - if (drawAnySelectedControl) - Render2D.PopTransform(); - } - /// protected override void OnLeftMouseButtonUp() { diff --git a/Source/Editor/Viewport/Previews/AssetPreview.cs b/Source/Editor/Viewport/Previews/AssetPreview.cs index ee9ee3195..c460a24de 100644 --- a/Source/Editor/Viewport/Previews/AssetPreview.cs +++ b/Source/Editor/Viewport/Previews/AssetPreview.cs @@ -21,7 +21,7 @@ namespace FlaxEditor.Viewport.Previews /// public abstract class AssetPreview : EditorViewport, IEditorPrimitivesOwner { - private ContextMenuButton _showDefaultSceneButton; + internal ContextMenuButton _showDefaultSceneButton; private IntPtr _debugDrawContext; private bool _debugDrawEnable; private bool _editorPrimitivesEnable; diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index a17850557..2c7b60a1f 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -2,6 +2,7 @@ using System; using FlaxEngine; +using FlaxEngine.GUI; using Object = FlaxEngine.Object; namespace FlaxEditor.Viewport.Previews @@ -14,7 +15,9 @@ namespace FlaxEditor.Viewport.Previews { private Prefab _prefab; private Actor _instance; - internal UIControl _uiControlLinked; + private UIControl _uiControlLinked; + internal bool _hasUILinked; + internal ContainerControl _uiParentLink; /// /// Gets or sets the prefab asset to preview. @@ -72,7 +75,7 @@ namespace FlaxEditor.Viewport.Previews // Unlink UI control if (_uiControlLinked) { - if (_uiControlLinked.Control?.Parent == this) + if (_uiControlLinked.Control?.Parent == _uiParentLink) _uiControlLinked.Control.Parent = null; _uiControlLinked = null; } @@ -82,6 +85,7 @@ namespace FlaxEditor.Viewport.Previews } _instance = value; + _hasUILinked = false; if (_instance) { @@ -103,20 +107,24 @@ namespace FlaxEditor.Viewport.Previews uiControl.Control != null && uiControl.Control.Parent == null) { - uiControl.Control.Parent = this; + uiControl.Control.Parent = _uiParentLink; _uiControlLinked = uiControl; + _hasUILinked = true; } } private void LinkCanvas(Actor actor) { if (actor is UICanvas uiCanvas) - uiCanvas.EditorOverride(Task, this); + { + uiCanvas.EditorOverride(Task, _uiParentLink); + if (uiCanvas.GUI.Parent == _uiParentLink) + _hasUILinked = true; + } + var children = actor.ChildrenCount; for (int i = 0; i < children; i++) - { LinkCanvas(actor.GetChild(i)); - } } /// @@ -126,6 +134,8 @@ namespace FlaxEditor.Viewport.Previews public PrefabPreview(bool useWidgets) : base(useWidgets) { + // Link to itself by default + _uiParentLink = this; } /// @@ -142,8 +152,6 @@ namespace FlaxEditor.Viewport.Previews /// public override void OnDestroy() { - if (IsDisposing) - return; Prefab = null; base.OnDestroy(); diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index d7efb1260..21e037fbc 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -360,10 +360,9 @@ namespace FlaxEditor.Windows.Assets /// /// The parent control. /// The location (within a given control). - private void ShowContextMenu(Control parent, ref Float2 location) + internal void ShowContextMenu(Control parent, ref Float2 location) { var contextMenu = CreateContextMenu(); - contextMenu.Show(parent, location); } diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index eb5070cb1..b788821cf 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -344,6 +344,7 @@ namespace FlaxEditor.Windows.Assets private void OnPrefabOpened() { _viewport.Prefab = _asset; + _viewport.UpdateGizmoMode(); Graph.MainActor = _viewport.Instance; Selection.Clear(); Select(Graph.Main); @@ -359,7 +360,7 @@ namespace FlaxEditor.Windows.Assets try { - Editor.Scene.OnSaveStart(_viewport); + Editor.Scene.OnSaveStart(_viewport._uiParentLink); // Simply update changes Editor.Prefabs.ApplyAll(_viewport.Instance); @@ -379,7 +380,7 @@ namespace FlaxEditor.Windows.Assets } finally { - Editor.Scene.OnSaveEnd(_viewport); + Editor.Scene.OnSaveEnd(_viewport._uiParentLink); } } diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index f1ea43d9b..71addb3ed 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Xml; +using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; using FlaxEditor.Options; @@ -194,133 +195,14 @@ namespace FlaxEditor.Windows public bool Active; } - private class GameRoot : ContainerControl + /// + /// Root control for game UI preview in Editor. Supports basic UI editing via . + /// + private class GameRoot : UIEditorRoot { - public bool EnableEvents => !Time.GamePaused; - - public override bool RayCast(ref Float2 location, out Control hit) - { - return RayCastChildren(ref location, out hit); - } - - public override bool ContainsPoint(ref Float2 location, bool precise = false) - { - if (precise) - return false; - return base.ContainsPoint(ref location, precise); - } - - public override bool OnCharInput(char c) - { - if (!EnableEvents) - return false; - - return base.OnCharInput(c); - } - - public override DragDropEffect OnDragDrop(ref Float2 location, DragData data) - { - if (!EnableEvents) - return DragDropEffect.None; - - return base.OnDragDrop(ref location, data); - } - - public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) - { - if (!EnableEvents) - return DragDropEffect.None; - - return base.OnDragEnter(ref location, data); - } - - public override void OnDragLeave() - { - if (!EnableEvents) - return; - - base.OnDragLeave(); - } - - public override DragDropEffect OnDragMove(ref Float2 location, DragData data) - { - if (!EnableEvents) - return DragDropEffect.None; - - return base.OnDragMove(ref location, data); - } - - public override bool OnKeyDown(KeyboardKeys key) - { - if (!EnableEvents) - return false; - - return base.OnKeyDown(key); - } - - public override void OnKeyUp(KeyboardKeys key) - { - if (!EnableEvents) - return; - - base.OnKeyUp(key); - } - - public override bool OnMouseDoubleClick(Float2 location, MouseButton button) - { - if (!EnableEvents) - return false; - - return base.OnMouseDoubleClick(location, button); - } - - public override bool OnMouseDown(Float2 location, MouseButton button) - { - if (!EnableEvents) - return false; - - return base.OnMouseDown(location, button); - } - - public override void OnMouseEnter(Float2 location) - { - if (!EnableEvents) - return; - - base.OnMouseEnter(location); - } - - public override void OnMouseLeave() - { - if (!EnableEvents) - return; - - base.OnMouseLeave(); - } - - public override void OnMouseMove(Float2 location) - { - if (!EnableEvents) - return; - - base.OnMouseMove(location); - } - - public override bool OnMouseUp(Float2 location, MouseButton button) - { - if (!EnableEvents) - return false; - - return base.OnMouseUp(location, button); - } - - public override bool OnMouseWheel(Float2 location, float delta) - { - if (!EnableEvents) - return false; - - return base.OnMouseWheel(location, delta); - } + public override bool EnableInputs => !Time.GamePaused; + public override bool EnableSelecting => !Editor.IsPlayMode || Time.GamePaused; + public override TransformGizmo TransformGizmo => Editor.Instance.MainTransformGizmo; } /// @@ -348,13 +230,9 @@ namespace FlaxEditor.Windows // Override the game GUI root _guiRoot = new GameRoot { - AnchorPreset = AnchorPresets.StretchAll, - Offsets = Margin.Zero, - //Visible = false, - AutoFocus = false, Parent = _viewport }; - RootControl.GameRoot = _guiRoot; + RootControl.GameRoot = _guiRoot.UIRoot; SizeChanged += control => { ResizeViewport(); }; @@ -916,35 +794,6 @@ namespace FlaxEditor.Windows Render2D.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } - // Selected UI controls outline - bool drawAnySelectedControl = false; - // TODO: optimize this (eg. cache list of selected UIControl's when selection gets changed) - var selection = Editor.SceneEditing.Selection; - for (var i = 0; i < selection.Count; i++) - { - if (selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null && controlActor.Control.VisibleInHierarchy && controlActor.Control.RootWindow != null) - { - if (!drawAnySelectedControl) - { - drawAnySelectedControl = true; - Render2D.PushTransform(ref _viewport._cachedTransform); - } - var options = Editor.Options.Options.Visual; - var control = controlActor.Control; - var bounds = control.EditorBounds; - var p1 = control.PointToParent(_viewport, bounds.UpperLeft); - var p2 = control.PointToParent(_viewport, bounds.UpperRight); - var p3 = control.PointToParent(_viewport, bounds.BottomLeft); - var p4 = control.PointToParent(_viewport, bounds.BottomRight); - var min = Float2.Min(Float2.Min(p1, p2), Float2.Min(p3, p4)); - var max = Float2.Max(Float2.Max(p1, p2), Float2.Max(p3, p4)); - bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero)); - Render2D.DrawRectangle(bounds, options.SelectionOutlineColor0, options.UISelectionOutlineSize); - } - } - if (drawAnySelectedControl) - Render2D.PopTransform(); - // Play mode hints and overlay if (Editor.StateMachine.IsPlayMode) { diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index 4ff4752ac..f4a49195f 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; -using System.Collections.Generic; using System.Linq; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.SceneGraph; @@ -258,10 +257,9 @@ namespace FlaxEditor.Windows /// /// The parent control. /// The location (within a given control). - private void ShowContextMenu(Control parent, Float2 location) + internal void ShowContextMenu(Control parent, Float2 location) { var contextMenu = CreateContextMenu(); - contextMenu.Show(parent, location); } diff --git a/Source/Engine/UI/GUI/Control.Bounds.cs b/Source/Engine/UI/GUI/Control.Bounds.cs index ec96f89f2..cbb3ba80b 100644 --- a/Source/Engine/UI/GUI/Control.Bounds.cs +++ b/Source/Engine/UI/GUI/Control.Bounds.cs @@ -383,7 +383,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point. /// - [DefaultValue(0.0f)] + [DefaultValue(typeof(Float2), "0,0")] [ExpandGroups, EditorDisplay("Transform"), EditorOrder(1040), Tooltip("The shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point.")] public Float2 Shear { From 700ed25d3db11e0d64a8b660fc787d9658c8d7ce Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 17 Mar 2024 13:46:39 +0100 Subject: [PATCH 131/198] Add resizing UI Control via widgets --- Source/Editor/Gizmo/UIEditorGizmo.cs | 159 +++++++++++++++++++++++++-- 1 file changed, 149 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index 4fd059202..084a64080 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -130,12 +130,25 @@ namespace FlaxEditor } } - private bool _mouseMovesControl, _mouseMovesView; + /// + /// Cached placement of the widget used to size/edit control + /// + private struct Widget + { + public UIControl UIControl; + public Rectangle Bounds; + public Float2 ResizeAxis; + public CursorType Cursor; + } + + private bool _mouseMovesControl, _mouseMovesView, _mouseMovesWidget; private Float2 _mouseMovesPos, _moveSnapDelta; private float _mouseMoveSum; private UndoMultiBlock _undoBlock; private View _view; private float[] _gridTickSteps = Utilities.Utils.CurveTickSteps, _gridTickStrengths; + private List _widgets; + private Widget _activeWidget; /// /// True if enable displaying UI editing background and grid elements. @@ -206,6 +219,24 @@ namespace FlaxEditor var transformGizmo = TransformGizmo; var owner = transformGizmo?.Owner; + if (_widgets != null && _widgets.Count != 0 && button == MouseButton.Left) + { + foreach (var widget in _widgets) + { + if (widget.Bounds.Contains(ref location)) + { + // Initialize widget movement + _activeWidget = widget; + _mouseMovesWidget = true; + _mouseMovesPos = location; + Cursor = widget.Cursor; + StartUndo(); + Focus(); + StartMouseCapture(); + return true; + } + } + } if (EnableSelecting && owner != null && !_mouseMovesControl && button == MouseButton.Left) { // Raycast the control under the mouse @@ -288,10 +319,7 @@ namespace FlaxEditor // Move control (handle any control transformations by moving in editor's local-space) var control = uiControl.Control; var localLocation = control.LocalLocation; - var pointOrigin = control.Parent ?? control; - var startPos = pointOrigin.PointFromParent(this, _mouseMovesPos); - var endPos = pointOrigin.PointFromParent(this, moveLocation); - var uiControlDelta = endPos - startPos; + var uiControlDelta = GetControlDelta(control, ref _mouseMovesPos, ref moveLocation); control.LocalLocation = localLocation + uiControlDelta; // Don't move if layout doesn't allow it @@ -311,6 +339,31 @@ namespace FlaxEditor Cursor = CursorType.SizeAll; } } + if (_mouseMovesWidget && _activeWidget.UIControl) + { + // Calculate transform delta + var resizeAxisAbs = _activeWidget.ResizeAxis.Absolute; + var resizeAxisPos = Float2.Clamp(_activeWidget.ResizeAxis, Float2.Zero, Float2.One); + var resizeAxisNeg = Float2.Clamp(-_activeWidget.ResizeAxis, Float2.Zero, Float2.One); + var delta = location - _mouseMovesPos; + // TODO: scale/size snapping? + delta *= resizeAxisAbs; + + // Resize control via widget + var moveLocation = _mouseMovesPos + delta; + var control = _activeWidget.UIControl.Control; + var uiControlDelta = GetControlDelta(control, ref _mouseMovesPos, ref moveLocation); + control.LocalLocation += uiControlDelta * resizeAxisNeg; + control.Size += uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg; + + // Don't move if layout doesn't allow it + if (control.Parent != null) + control.Parent.PerformLayout(); + else + control.PerformLayout(); + + _mouseMovesPos = location; + } if (_mouseMovesView) { // Move view @@ -328,6 +381,7 @@ namespace FlaxEditor public override bool OnMouseUp(Float2 location, MouseButton button) { EndMovingControls(); + EndMovingWidget(); if (_mouseMovesView) { EndMovingView(); @@ -342,6 +396,7 @@ namespace FlaxEditor { EndMovingControls(); EndMovingView(); + EndMovingWidget(); base.OnMouseLeave(); } @@ -350,6 +405,7 @@ namespace FlaxEditor { EndMovingControls(); EndMovingView(); + EndMovingWidget(); base.OnLostFocus(); } @@ -406,8 +462,15 @@ namespace FlaxEditor base.Draw(); + if (!_mouseMovesWidget) + { + // Clear widgets to collect them during drawing + _widgets?.Clear(); + } + bool drawAnySelectedControl = false; var transformGizmo = TransformGizmo; + var mousePos = PointFromWindow(RootWindow.MousePosition); if (transformGizmo != null) { // Selected UI controls outline @@ -416,19 +479,17 @@ namespace FlaxEditor { if (IsValidControl(selection[i], out var controlActor)) { - DrawControlBounds(controlActor.Control, true, ref drawAnySelectedControl); - // TODO: draw anchors + DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, true); } } } if (EnableSelecting && !_mouseMovesControl && IsMouseOver) { // Highlight control under mouse for easier selecting (except if already selected) - var mousePos = PointFromWindow(RootWindow.MousePosition); if (RayCastControl(ref mousePos, out var hitControl) && (transformGizmo == null || !transformGizmo.Selection.Any(x => x.EditableObject is UIControl controlActor && controlActor.Control == hitControl))) { - DrawControlBounds(hitControl, false, ref drawAnySelectedControl); + DrawControl(null, hitControl, false, ref mousePos, ref drawAnySelectedControl); } } if (drawAnySelectedControl) @@ -450,10 +511,19 @@ namespace FlaxEditor return; EndMovingControls(); EndMovingView(); + EndMovingWidget(); base.OnDestroy(); } + private Float2 GetControlDelta(Control control, ref Float2 start, ref Float2 end) + { + var pointOrigin = control.Parent ?? control; + var startPos = pointOrigin.PointFromParent(this, start); + var endPos = pointOrigin.PointFromParent(this, end); + return endPos - startPos; + } + private void DrawAxis(Float2 axis, Rectangle viewRect, float min, float max, float pixelRange) { var style = Style.Current; @@ -485,7 +555,7 @@ namespace FlaxEditor }, _gridTickSteps, ref _gridTickStrengths, min, max, pixelRange); } - private void DrawControlBounds(Control control, bool selection, ref bool drawAnySelectedControl) + private void DrawControl(UIControl uiControl, Control control, bool selection, ref Float2 mousePos, ref bool drawAnySelectedControl, bool withWidgets = false) { if (!drawAnySelectedControl) { @@ -493,6 +563,8 @@ namespace FlaxEditor Render2D.PushTransform(ref _cachedTransform); } var options = Editor.Instance.Options.Options.Visual; + + // Draw bounds var bounds = control.EditorBounds; var ul = control.PointToParent(this, bounds.UpperLeft); var ur = control.PointToParent(this, bounds.UpperRight); @@ -512,6 +584,62 @@ namespace FlaxEditor Render2D.DrawLine(br, bl, color, options.UISelectionOutlineSize); Render2D.DrawLine(bl, ul, color, options.UISelectionOutlineSize); #endif + if (withWidgets) + { + // Draw sizing widgets + if (_widgets == null) + _widgets = new List(); + var widgetSize = 8.0f; + var viewScale = ViewScale; + if (viewScale < 0.7f) + widgetSize *= viewScale; + var controlSize = control.Size.Absolute.MinValue / 50.0f; + if (controlSize < 1.0f) + widgetSize *= Mathf.Clamp(controlSize + 0.1f, 0.1f, 1.0f); + var cornerSize = new Float2(widgetSize); + DrawControlWidget(uiControl, ref ul, ref mousePos, ref cornerSize, new Float2(-1, -1), CursorType.SizeNWSE); + DrawControlWidget(uiControl, ref ur, ref mousePos, ref cornerSize, new Float2(1, -1), CursorType.SizeNESW); + DrawControlWidget(uiControl, ref bl, ref mousePos, ref cornerSize, new Float2(-1, 1), CursorType.SizeNESW); + DrawControlWidget(uiControl, ref br, ref mousePos, ref cornerSize, new Float2(1, 1), CursorType.SizeNWSE); + var edgeSizeV = new Float2(widgetSize * 2, widgetSize); + var edgeSizeH = new Float2(edgeSizeV.Y, edgeSizeV.X); + Float2.Lerp(ref ul, ref bl, 0.5f, out var el); + Float2.Lerp(ref ur, ref br, 0.5f, out var er); + Float2.Lerp(ref ul, ref ur, 0.5f, out var eu); + Float2.Lerp(ref bl, ref br, 0.5f, out var eb); + DrawControlWidget(uiControl, ref el, ref mousePos, ref edgeSizeH, new Float2(-1, 0), CursorType.SizeWE); + DrawControlWidget(uiControl, ref er, ref mousePos, ref edgeSizeH, new Float2(1, 0), CursorType.SizeWE); + DrawControlWidget(uiControl, ref eu, ref mousePos, ref edgeSizeV, new Float2(0, -1), CursorType.SizeNS); + DrawControlWidget(uiControl, ref eb, ref mousePos, ref edgeSizeV, new Float2(0, 1), CursorType.SizeNS); + + // TODO: draw anchors + } + } + + private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size, Float2 resizeAxis, CursorType cursor) + { + var style = Style.Current; + var rect = new Rectangle(pos - size * 0.5f, size); + if (rect.Contains(ref mousePos)) + { + Render2D.FillRectangle(rect, style.Foreground); + } + else + { + Render2D.FillRectangle(rect, style.ForegroundGrey); + Render2D.DrawRectangle(rect, style.Foreground); + } + if (!_mouseMovesWidget && uiControl != null) + { + // Collect widget + _widgets.Add(new Widget + { + UIControl = uiControl, + Bounds = rect, + ResizeAxis = resizeAxis, + Cursor = cursor, + }); + } } private bool IsValidControl(SceneGraphNode node, out UIControl uiControl) @@ -592,6 +720,17 @@ namespace FlaxEditor EndMouseCapture(); Cursor = CursorType.Default; } + + private void EndMovingWidget() + { + if (!_mouseMovesWidget) + return; + _mouseMovesWidget = false; + _activeWidget = new Widget(); + EndMouseCapture(); + Cursor = CursorType.Default; + EndUndo(); + } } /// From b847c2d056482c372beed491d16995ec2932c5f6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 17 Mar 2024 18:27:59 +0100 Subject: [PATCH 132/198] Fixes --- Source/Editor/Gizmo/UIEditorGizmo.cs | 6 ++++-- Source/Editor/Options/InputBinding.cs | 4 ++-- Source/Editor/Viewport/EditorGizmoViewport.cs | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index 084a64080..c7d37d78d 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -189,9 +189,11 @@ namespace FlaxEditor internal float ViewScale { - get => _view.Scale.X; + get => _view?.Scale.X ?? 1; set { + if (_view == null) + return; value = Mathf.Clamp(value, 0.1f, 4.0f); _view.Scale = new Float2(value); } @@ -483,7 +485,7 @@ namespace FlaxEditor } } } - if (EnableSelecting && !_mouseMovesControl && IsMouseOver) + if (EnableSelecting && !_mouseMovesControl && !_mouseMovesWidget && IsMouseOver) { // Highlight control under mouse for easier selecting (except if already selected) if (RayCastControl(ref mousePos, out var hitControl) && diff --git a/Source/Editor/Options/InputBinding.cs b/Source/Editor/Options/InputBinding.cs index 47147eaa8..59954dec5 100644 --- a/Source/Editor/Options/InputBinding.cs +++ b/Source/Editor/Options/InputBinding.cs @@ -201,8 +201,8 @@ namespace FlaxEditor.Options /// True if input has been processed, otherwise false. public bool Process(Control control) { - var root = control.Root; - return root.GetKey(Key) && ProcessModifiers(control); + var root = control?.Root; + return root != null && root.GetKey(Key) && ProcessModifiers(control); } /// diff --git a/Source/Editor/Viewport/EditorGizmoViewport.cs b/Source/Editor/Viewport/EditorGizmoViewport.cs index fec1154a5..25dcd76e7 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -76,10 +76,10 @@ namespace FlaxEditor.Viewport public Float2 MouseDelta => _mouseDelta * 1000; /// - public bool UseSnapping => Root.GetKey(KeyboardKeys.Control); + public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false; /// - public bool UseDuplicate => Root.GetKey(KeyboardKeys.Shift); + public bool UseDuplicate => Root?.GetKey(KeyboardKeys.Shift) ?? false; /// public Undo Undo { get; } From 16aef0f705c5c7d6db017689f5d113bdbee14502 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 17 Mar 2024 17:11:36 -0500 Subject: [PATCH 133/198] Add deselecting by clicking no control. Add cursor changes when over widgets. --- Source/Editor/Gizmo/UIEditorGizmo.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index c7d37d78d..1ff7e687e 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -276,6 +276,11 @@ namespace FlaxEditor return true; } } + // Allow deselecting if user clicks on nothing + else + { + owner.Select(null); + } } if (EnableCamera && (button == MouseButton.Right || button == MouseButton.Middle)) { @@ -295,6 +300,24 @@ namespace FlaxEditor { base.OnMouseMove(location); + // Change cursor if mouse is over active control widget + bool cursorChanged = false; + if (_widgets != null && _widgets.Count != 0 && !_mouseMovesControl && !_mouseMovesWidget && !_mouseMovesView) + { + foreach (var widget in _widgets) + { + if (widget.Bounds.Contains(ref location)) + { + Cursor = widget.Cursor; + cursorChanged = true; + } + else if (Cursor != CursorType.Default && !cursorChanged) + { + Cursor = CursorType.Default; + } + } + } + var transformGizmo = TransformGizmo; if (_mouseMovesControl && transformGizmo != null) { From 795527576b1ff810b17d00388aa2122b87c2e93d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 15:20:05 +0100 Subject: [PATCH 134/198] Add dynamic UI mode checking for Prefab Viewport --- Source/Editor/Viewport/PrefabWindowViewport.cs | 7 ++++--- Source/Editor/Viewport/Previews/PrefabPreview.cs | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 31f27f708..1bf121ce2 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -44,6 +44,7 @@ namespace FlaxEditor.Viewport private sealed class PrefabUIEditorRoot : UIEditorRoot { private readonly PrefabWindowViewport _viewport; + private bool UI => _viewport._hasUILinkedCached; public PrefabUIEditorRoot(PrefabWindowViewport viewport) : base(true) @@ -52,9 +53,9 @@ namespace FlaxEditor.Viewport Parent = viewport; } - public override bool EnableInputs => false; - public override bool EnableSelecting => true; - public override bool EnableBackground => _viewport._hasUILinkedCached; + public override bool EnableInputs => !UI; + public override bool EnableSelecting => UI; + public override bool EnableBackground => UI; public override TransformGizmo TransformGizmo => _viewport.TransformGizmo; } diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index 2c7b60a1f..7df524a72 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -98,6 +98,9 @@ namespace FlaxEditor.Viewport.Previews private void UpdateLinkage() { + // Clear flag + _hasUILinked = false; + // Link UI canvases to the preview (eg. after canvas added to the prefab) LinkCanvas(_instance); From 91298fee19ac6e15d6a8095a56c1f0d5c432c5f1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 15:32:42 +0100 Subject: [PATCH 135/198] Fixes for UI editor changes --- Source/Editor/Gizmo/UIEditorGizmo.cs | 11 +++++++---- Source/Editor/Viewport/EditorViewport.cs | 2 +- Source/Editor/Viewport/Previews/PrefabPreview.cs | 2 ++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index 1ff7e687e..5d1f27651 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -21,9 +21,12 @@ namespace FlaxEditor public void ShowActors(IEnumerable actors) { + var root = UIEditor.UIRoot; + if (root == null) + return; + // Calculate bounds of all selected objects var areaRect = Rectangle.Empty; - var root = UIEditor.UIRoot; foreach (var actor in actors) { Rectangle bounds; @@ -163,7 +166,7 @@ namespace FlaxEditor /// /// True if enable panning and zooming the view. /// - public bool EnableCamera => _view != null; + public bool EnableCamera => _view != null && EnableBackground; /// /// Transform gizmo to use sync with (selection, snapping, transformation settings). @@ -293,7 +296,7 @@ namespace FlaxEditor return true; } - return false; + return Focus(this); } public override void OnMouseMove(Float2 location) @@ -467,7 +470,7 @@ namespace FlaxEditor public override void Draw() { - if (EnableBackground) + if (EnableBackground && _view != null) { // Draw background Surface.VisjectSurface.DrawBackgroundDefault(Editor.Instance.UI.VisjectSurfaceBackground, Width, Height); diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 59fad6b11..a53f95ea8 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1537,7 +1537,7 @@ namespace FlaxEditor.Viewport } bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height)); _prevInput = _input; - var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl)); + var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot)); if (canUseInput && ContainsFocus && hit == null) _input.Gather(win.Window, useMouse); else diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index 7df524a72..16ec8f132 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -114,6 +114,8 @@ namespace FlaxEditor.Viewport.Previews _uiControlLinked = uiControl; _hasUILinked = true; } + else if (_uiControlLinked != null) + _hasUILinked = true; } private void LinkCanvas(Actor actor) From e606ccec57a6800596e0b12978fdda198b92f6a1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 15:33:01 +0100 Subject: [PATCH 136/198] Fix actor icons drawing in prefab viewport to skip disabled actors --- Source/Editor/Utilities/ViewportIconsRenderer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.cpp b/Source/Editor/Utilities/ViewportIconsRenderer.cpp index d3cd2c940..797b4edea 100644 --- a/Source/Editor/Utilities/ViewportIconsRenderer.cpp +++ b/Source/Editor/Utilities/ViewportIconsRenderer.cpp @@ -187,6 +187,8 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor* actor, Mesh::DrawInfo& draw) { + if (!actor || !actor->IsActiveInHierarchy()) + return; auto& view = renderContext.View; const BoundingFrustum frustum = view.Frustum; Matrix m1, m2, world; @@ -208,8 +210,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor draw.DrawState = &drawState; draw.Deformation = nullptr; - // Support custom icons through types, but not onces that were added through actors, - // since they cant register while in prefab view anyway + // Support custom icons through types, but not ones that were added through actors, since they cant register while in prefab view anyway if (ActorTypeToTexture.TryGet(actor->GetTypeHandle(), texture)) { // Use custom texture From ed21876bb89a50e001968ce5af80a7accb24996f Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Mon, 18 Mar 2024 17:34:21 +0200 Subject: [PATCH 137/198] Fix duplicate generation of native variant conversion helpers --- .../Bindings/BindingsGenerator.Cpp.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 681483cf9..4bf6ae35a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -24,7 +24,7 @@ namespace Flax.Build.Bindings private static readonly List CppAutoSerializeProperties = new List(); public static readonly HashSet CppIncludeFiles = new HashSet(); private static readonly List CppIncludeFilesList = new List(); - private static readonly HashSet CppVariantToTypes = new HashSet(); + private static readonly Dictionary CppVariantToTypes = new Dictionary(); private static readonly Dictionary CppVariantFromTypes = new Dictionary(); private static bool CppNonPodTypesConvertingGeneration = false; private static StringBuilder CppContentsEnd; @@ -231,13 +231,15 @@ namespace Flax.Build.Bindings throw new Exception($"Not supported type to convert from the Variant to fixed-size array '{typeInfo}[{typeInfo.ArraySize}]'."); if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null) { - CppVariantToTypes.Add(typeInfo); - return $"MoveTemp(VariantTo{GenerateCppWrapperNativeToVariantMethodName(typeInfo)}({value}))"; + var wrapperName = GenerateCppWrapperNativeToVariantMethodName(typeInfo); + CppVariantToTypes[wrapperName] = typeInfo; + return $"MoveTemp(VariantTo{wrapperName}({value}))"; } if (typeInfo.Type == "Dictionary" && typeInfo.GenericArgs != null) { - CppVariantToTypes.Add(typeInfo); - return $"MoveTemp(VariantTo{GenerateCppWrapperNativeToVariantMethodName(typeInfo)}({value}))"; + var wrapperName = GenerateCppWrapperNativeToVariantMethodName(typeInfo); + CppVariantToTypes[wrapperName] = typeInfo; + return $"MoveTemp(VariantTo{wrapperName}({value}))"; } if (typeInfo.Type == "Span" && typeInfo.GenericArgs != null) { @@ -2790,12 +2792,14 @@ namespace Flax.Build.Bindings var header = GetStringBuilder(); // Variant converting helper methods - foreach (var typeInfo in CppVariantToTypes) + foreach (var e in CppVariantToTypes) { + var wrapperName = e.Key; + var typeInfo = e.Value; var name = typeInfo.ToString(false); header.AppendLine(); header.AppendLine("namespace {"); - header.Append($"{name} VariantTo{GenerateCppWrapperNativeToVariantMethodName(typeInfo)}(const Variant& v)").AppendLine(); + header.Append($"{name} VariantTo{wrapperName}(const Variant& v)").AppendLine(); header.Append('{').AppendLine(); header.Append($" {name} result;").AppendLine(); if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null) From 3b502086f897d99cf1ade8fa66ec7b47ffdd9b12 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 17:38:05 +0100 Subject: [PATCH 138/198] Fix UI coordinate convertion in `CanvasScaler` --- Source/Engine/UI/GUI/CanvasScaler.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Engine/UI/GUI/CanvasScaler.cs b/Source/Engine/UI/GUI/CanvasScaler.cs index f0cc59b10..b36a62f27 100644 --- a/Source/Engine/UI/GUI/CanvasScaler.cs +++ b/Source/Engine/UI/GUI/CanvasScaler.cs @@ -429,6 +429,13 @@ namespace FlaxEngine.GUI return ContainsPoint(ref location); } + /// + public override bool IntersectsChildContent(Control child, Float2 location, out Float2 childSpaceLocation) + { + location /= _scale; + return base.IntersectsChildContent(child, location, out childSpaceLocation); + } + /// public override bool ContainsPoint(ref Float2 location, bool precise = false) { From 34f35ebaac190513804d81d1cda4e58af7be8fbd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 17:38:24 +0100 Subject: [PATCH 139/198] Fix loading shader cache when opened with different engine version --- Flax.flaxproj | 2 +- Source/Engine/Graphics/Shaders/Cache/ShaderCacheManager.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 7153e51d5..9afdfad42 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 7, "Revision": 2, - "Build": 6408 + "Build": 6409 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.", diff --git a/Source/Engine/Graphics/Shaders/Cache/ShaderCacheManager.cpp b/Source/Engine/Graphics/Shaders/Cache/ShaderCacheManager.cpp index 0e6ad8f3a..2af300f06 100644 --- a/Source/Engine/Graphics/Shaders/Cache/ShaderCacheManager.cpp +++ b/Source/Engine/Graphics/Shaders/Cache/ShaderCacheManager.cpp @@ -10,6 +10,7 @@ #include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Graphics/Materials/MaterialShader.h" #include "Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.h" +#include "FlaxEngine.Gen.h" const Char* ShaderProfileCacheDirNames[] = { @@ -179,6 +180,7 @@ bool ShaderCacheManagerService::Init() // Validate the database cache version (need to recompile all shaders on shader cache format change) struct CacheVersion { + int32 EngineVersion = -1; int32 ShaderCacheVersion = -1; int32 MaterialGraphVersion = -1; int32 ParticleGraphVersion = -1; @@ -193,7 +195,8 @@ bool ShaderCacheManagerService::Init() LOG(Warning, "Failed to read the shaders cache database version file."); } } - if (cacheVersion.ShaderCacheVersion != GPU_SHADER_CACHE_VERSION + if (cacheVersion.EngineVersion != FLAXENGINE_VERSION_BUILD + || cacheVersion.ShaderCacheVersion != GPU_SHADER_CACHE_VERSION || cacheVersion.MaterialGraphVersion != MATERIAL_GRAPH_VERSION || cacheVersion.ParticleGraphVersion != PARTICLE_GPU_GRAPH_VERSION ) @@ -209,6 +212,7 @@ bool ShaderCacheManagerService::Init() LOG(Error, "Failed to createe the shaders cache database directory."); } + cacheVersion.EngineVersion = FLAXENGINE_VERSION_BUILD; cacheVersion.ShaderCacheVersion = GPU_SHADER_CACHE_VERSION; cacheVersion.MaterialGraphVersion = MATERIAL_GRAPH_VERSION; cacheVersion.ParticleGraphVersion = PARTICLE_GPU_GRAPH_VERSION; From 12e2b8156f5d78c797cf02d104102d055d1d0bc5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 20:31:17 +0100 Subject: [PATCH 140/198] Fix UI prefab focus on canvas not being added as root --- Source/Editor/Viewport/PrefabWindowViewport.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 1bf121ce2..252981555 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; @@ -285,7 +286,11 @@ namespace FlaxEditor.Viewport ShowDebugDraw = false; // Show whole UI on startup - ViewportCamera.ShowActor(Instance); + var canvas = (CanvasRootControl)_uiParentLink.Children.FirstOrDefault(x => x is CanvasRootControl); + if (canvas != null) + ViewportCamera.ShowActor(canvas.Canvas); + else if (Instance is UIControl) + ViewportCamera.ShowActor(Instance); } else { From eb8db60ead137fe49f61142ef8e11fc6125e98c1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 21:12:54 +0100 Subject: [PATCH 141/198] Revert eye adaptation back to original value (in favor of #2023) --- Source/Engine/Graphics/PostProcessSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 3e8c83973..86c693e81 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -948,7 +948,7 @@ API_STRUCT() struct FLAXENGINE_API EyeAdaptationSettings : ISerializable /// The effect rendering mode used for the exposure processing. /// API_FIELD(Attributes="EditorOrder(0), PostProcessSetting((int)EyeAdaptationSettingsOverride.Mode)") - EyeAdaptationMode Mode = EyeAdaptationMode::None; + EyeAdaptationMode Mode = EyeAdaptationMode::AutomaticHistogram; /// /// The speed at which the exposure changes when the scene brightness moves from a dark area to a bright area (brightness goes up). From 24299dc5ccbbb7a76a18ec5fc78e5e34f0388e5c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 21:36:43 +0100 Subject: [PATCH 142/198] Fix game ui inputs to UI when game is paused in Editor --- Source/Editor/Windows/GameWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 71addb3ed..c804c26dc 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -200,7 +200,7 @@ namespace FlaxEditor.Windows /// private class GameRoot : UIEditorRoot { - public override bool EnableInputs => !Time.GamePaused; + public override bool EnableInputs => !Time.GamePaused && Editor.IsPlayMode; public override bool EnableSelecting => !Editor.IsPlayMode || Time.GamePaused; public override TransformGizmo TransformGizmo => Editor.Instance.MainTransformGizmo; } From 2b824ce22a4e120f745cae6b7f85589c4c0e92d9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Mar 2024 21:50:08 +0100 Subject: [PATCH 143/198] Fix UI Canvas double scale apply --- Source/Engine/UI/GUI/CanvasScaler.cs | 91 ---------------------------- 1 file changed, 91 deletions(-) diff --git a/Source/Engine/UI/GUI/CanvasScaler.cs b/Source/Engine/UI/GUI/CanvasScaler.cs index b36a62f27..612de3f59 100644 --- a/Source/Engine/UI/GUI/CanvasScaler.cs +++ b/Source/Engine/UI/GUI/CanvasScaler.cs @@ -469,97 +469,6 @@ namespace FlaxEngine.GUI return result; } - /// - public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) - { - location /= _scale; - return base.OnDragEnter(ref location, data); - } - - /// - public override DragDropEffect OnDragMove(ref Float2 location, DragData data) - { - location /= _scale; - return base.OnDragMove(ref location, data); - } - - /// - public override DragDropEffect OnDragDrop(ref Float2 location, DragData data) - { - location /= _scale; - return base.OnDragDrop(ref location, data); - } - - /// - public override void OnMouseEnter(Float2 location) - { - location /= _scale; - base.OnMouseEnter(location); - } - - /// - public override void OnMouseMove(Float2 location) - { - location /= _scale; - base.OnMouseMove(location); - } - - /// - public override bool OnMouseDown(Float2 location, MouseButton button) - { - location /= _scale; - return base.OnMouseDown(location, button); - } - - /// - public override bool OnMouseUp(Float2 location, MouseButton button) - { - location /= _scale; - return base.OnMouseUp(location, button); - } - - /// - public override bool OnMouseDoubleClick(Float2 location, MouseButton button) - { - location /= _scale; - return base.OnMouseDoubleClick(location, button); - } - - /// - public override bool OnMouseWheel(Float2 location, float delta) - { - location /= _scale; - return base.OnMouseWheel(location, delta); - } - - /// - public override void OnTouchEnter(Float2 location, int pointerId) - { - location /= _scale; - base.OnTouchEnter(location, pointerId); - } - - /// - public override void OnTouchMove(Float2 location, int pointerId) - { - location /= _scale; - base.OnTouchMove(location, pointerId); - } - - /// - public override bool OnTouchDown(Float2 location, int pointerId) - { - location /= _scale; - return base.OnTouchDown(location, pointerId); - } - - /// - public override bool OnTouchUp(Float2 location, int pointerId) - { - location /= _scale; - return base.OnTouchUp(location, pointerId); - } - #endregion } } From cf44d6580df708505feb34058efadf4f0e925ad5 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 18 Mar 2024 18:50:53 -0500 Subject: [PATCH 144/198] Remove widgets when EnableSelecting is false. --- Source/Editor/Gizmo/UIEditorGizmo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index 5d1f27651..ea4b75d28 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -507,7 +507,7 @@ namespace FlaxEditor { if (IsValidControl(selection[i], out var controlActor)) { - DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, true); + DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, EnableSelecting); } } } From 4b9bfc5bb4318af84eaece7768c6ae4d739d10e7 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 18 Mar 2024 18:58:59 -0500 Subject: [PATCH 145/198] Allow to still show widget in prefabs when in play mode --- Source/Editor/Gizmo/UIEditorGizmo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index ea4b75d28..e39bd2684 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -507,7 +507,7 @@ namespace FlaxEditor { if (IsValidControl(selection[i], out var controlActor)) { - DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, EnableSelecting); + DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, EnableSelecting || (!EnableSelecting && Parent?.Parent is not Windows.GameWindow)); } } } From 211da6d8e733a2f3d48b46688d0285c472807544 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 19 Mar 2024 07:17:05 -0500 Subject: [PATCH 146/198] Revert last commit. Not needed. --- Source/Editor/Gizmo/UIEditorGizmo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index e39bd2684..ea4b75d28 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -507,7 +507,7 @@ namespace FlaxEditor { if (IsValidControl(selection[i], out var controlActor)) { - DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, EnableSelecting || (!EnableSelecting && Parent?.Parent is not Windows.GameWindow)); + DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, EnableSelecting); } } } From 41637232ea41990f2f66fbf288dbd6815cf151c2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 19 Mar 2024 07:28:30 -0500 Subject: [PATCH 147/198] Add back some more game window inputs but dont run the action if the editor is in play mode. --- Source/Editor/Windows/GameWindow.cs | 50 +++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 71addb3ed..52e0e7362 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -260,6 +260,56 @@ namespace FlaxEditor.Windows Editor.Instance.Windows.ProfilerWin.Clear(); Editor.Instance.UI.AddStatusMessage($"Profiling results cleared."); }); + InputActions.Add(options => options.Save, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.SaveAll(); + }); + InputActions.Add(options => options.Undo, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.PerformUndo(); + Focus(); + }); + InputActions.Add(options => options.Redo, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.PerformRedo(); + Focus(); + }); + InputActions.Add(options => options.Cut, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.SceneEditing.Cut(); + }); + InputActions.Add(options => options.Copy, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.SceneEditing.Copy(); + }); + InputActions.Add(options => options.Paste, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.SceneEditing.Paste(); + }); + InputActions.Add(options => options.Duplicate, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.SceneEditing.Duplicate(); + }); + InputActions.Add(options => options.Delete, () => + { + if (Editor.IsPlayMode) + return; + Editor.Instance.SceneEditing.Delete(); + }); } private void ChangeViewportRatio(ViewportScaleOptions v) From 1a68a52611c3c0e64111b1549e1833e34f151967 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 16:01:27 +0100 Subject: [PATCH 148/198] Add better grid gizmo rendering in Editor viewport #1859 --- Source/Editor/Gizmo/GridGizmo.cs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Gizmo/GridGizmo.cs b/Source/Editor/Gizmo/GridGizmo.cs index 8638e1b54..a96651472 100644 --- a/Source/Editor/Gizmo/GridGizmo.cs +++ b/Source/Editor/Gizmo/GridGizmo.cs @@ -21,7 +21,7 @@ namespace FlaxEditor.Gizmo { Order = -100; UseSingleTarget = true; - Location = PostProcessEffectLocation.BeforeForwardPass; + Location = PostProcessEffectLocation.AfterAntiAliasingPass; } ~Renderer() @@ -46,7 +46,8 @@ namespace FlaxEditor.Gizmo var plane = new Plane(Vector3.Zero, Vector3.UnitY); var dst = CollisionsHelper.DistancePlanePoint(ref plane, ref viewPos); - float space = Editor.Instance.Options.Options.Viewport.ViewportGridScale, size; + var options = Editor.Instance.Options.Options; + float space = options.Viewport.ViewportGridScale, size; if (dst <= 500.0f) { size = 8000; @@ -62,8 +63,12 @@ namespace FlaxEditor.Gizmo size = 100000; } - Color color = Color.Gray * 0.7f; + float bigLineIntensity = 0.8f; + Color bigColor = Color.Gray * bigLineIntensity; + Color color = bigColor * 0.8f; int count = (int)(size / space); + int midLine = count / 2; + int bigLinesMod = count / 8; Vector3 start = new Vector3(0, 0, size * -0.5f); Vector3 end = new Vector3(0, 0, size * 0.5f); @@ -71,7 +76,12 @@ namespace FlaxEditor.Gizmo for (int i = 0; i <= count; i++) { start.X = end.X = i * space + start.Z; - DebugDraw.DrawLine(start, end, color); + Color lineColor = color; + if (i == midLine) + lineColor = Color.Blue * bigLineIntensity; + else if (i % bigLinesMod == 0) + lineColor = bigColor; + DebugDraw.DrawLine(start, end, lineColor); } start = new Vector3(size * -0.5f, 0, 0); @@ -80,7 +90,12 @@ namespace FlaxEditor.Gizmo for (int i = 0; i <= count; i++) { start.Z = end.Z = i * space + start.X; - DebugDraw.DrawLine(start, end, color); + Color lineColor = color; + if (i == midLine) + lineColor = Color.Red * bigLineIntensity; + else if (i % bigLinesMod == 0) + lineColor = bigColor; + DebugDraw.DrawLine(start, end, lineColor); } DebugDraw.Draw(ref renderContext, input.View(), null, true); From cf3145273faa74ba5d12e604d72623f06589a3dc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 16:02:50 +0100 Subject: [PATCH 149/198] Fix TAA jitter in post-resolve passes such as editor primitives and debug gizmos --- Source/Engine/Debug/DebugDraw.cpp | 3 +- Source/Engine/Graphics/RenderView.cpp | 37 +++++++++++++++++++++ Source/Engine/Graphics/RenderView.h | 24 ++++++++++++- Source/Engine/Renderer/AntiAliasing/TAA.cpp | 3 ++ Source/Engine/Renderer/RenderList.cpp | 1 + Source/Engine/Renderer/Renderer.cpp | 2 +- 6 files changed, 67 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 46b15a6e2..b0164124c 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -739,6 +739,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe } Context->LastViewPos = view.Position; Context->LastViewProj = view.Projection; + TaaJitterRemoveContext taaJitterRemove(view); // Fallback to task buffers if (target == nullptr && renderContext.Task) @@ -766,7 +767,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe const auto cb = DebugDrawShader->GetShader()->GetCB(0); Data data; Matrix vp; - Matrix::Multiply(view.View, view.NonJitteredProjection, vp); + Matrix::Multiply(view.View, view.Projection, vp); Matrix::Transpose(vp, data.ViewProjection); data.EnableDepthTest = enableDepthTest; context->UpdateCB(cb, &data); diff --git a/Source/Engine/Graphics/RenderView.cpp b/Source/Engine/Graphics/RenderView.cpp index d59a16463..f72c8f406 100644 --- a/Source/Engine/Graphics/RenderView.cpp +++ b/Source/Engine/Graphics/RenderView.cpp @@ -18,6 +18,7 @@ void RenderView::Prepare(RenderContext& renderContext) // Check if use TAA (need to modify the projection matrix) Float2 taaJitter; NonJitteredProjection = Projection; + IsTaaResolved = false; if (renderContext.List->Setup.UseTemporalAAJitter) { // Move to the next frame @@ -82,6 +83,18 @@ void RenderView::PrepareCache(const RenderContext& renderContext, float width, f MainScreenSize = mainView->ScreenSize; } +void RenderView::UpdateCachedData() +{ + Matrix::Invert(View, IV); + Matrix::Invert(Projection, IP); + Matrix viewProjection; + Matrix::Multiply(View, Projection, viewProjection); + Frustum.SetMatrix(viewProjection); + Matrix::Invert(viewProjection, IVP); + CullingFrustum = Frustum; + NonJitteredProjection = Projection; +} + void RenderView::SetUp(const Matrix& viewProjection) { // Copy data @@ -201,3 +214,27 @@ void RenderView::GetWorldMatrix(const Transform& transform, Matrix& world) const const Float3 translation = transform.Translation - Origin; Matrix::Transformation(transform.Scale, transform.Orientation, translation, world); } + +TaaJitterRemoveContext::TaaJitterRemoveContext(const RenderView& view) +{ + if (view.IsTaaResolved) + { + // Cancel-out sub-pixel jitter when drawing geometry after TAA has been resolved + _view = (RenderView*)&view; + _prevProjection = view.Projection; + _prevNonJitteredProjection = view.NonJitteredProjection; + _view->Projection = _prevNonJitteredProjection; + _view->UpdateCachedData(); + } +} + +TaaJitterRemoveContext::~TaaJitterRemoveContext() +{ + if (_view) + { + // Restore projection + _view->Projection = _prevProjection; + _view->UpdateCachedData(); + _view->NonJitteredProjection = _prevNonJitteredProjection; + } +} diff --git a/Source/Engine/Graphics/RenderView.h b/Source/Engine/Graphics/RenderView.h index d73b45c53..0b37e8b28 100644 --- a/Source/Engine/Graphics/RenderView.h +++ b/Source/Engine/Graphics/RenderView.h @@ -117,6 +117,11 @@ public: /// API_FIELD() bool IsCullingDisabled = false; + /// + /// True if TAA has been resolved when rendering view and frame doesn't contain jitter anymore. Rendering geometry after this point should not use jitter anymore (eg. editor gizmos or custom geometry as overlay). + /// + API_FIELD() bool IsTaaResolved = false; + /// /// The static flags mask used to hide objects that don't have a given static flags. Eg. use StaticFlags::Lightmap to render only objects that can use lightmap. /// @@ -160,7 +165,7 @@ public: API_FIELD() DEPRECATED float ShadowModelLODDistanceFactor = 1.0f; /// - /// The Temporal Anti-Aliasing jitter frame index. + /// Temporal Anti-Aliasing jitter frame index. /// API_FIELD() int32 TaaFrameIndex = 0; @@ -261,6 +266,11 @@ public: RenderView& operator=(const RenderView& other) = default; PRAGMA_ENABLE_DEPRECATION_WARNINGS + /// + /// Updates the cached data for the view (inverse matrices, etc.). + /// + void UpdateCachedData(); + // Set up view with custom params // @param viewProjection View * Projection matrix void SetUp(const Matrix& viewProjection); @@ -344,3 +354,15 @@ public: world.M43 -= Origin.Z; } }; + +// Removes TAA jitter from the RenderView when drawing geometry after TAA has been resolved to prevent unwanted jittering. +struct TaaJitterRemoveContext +{ +private: + RenderView* _view = nullptr; + Matrix _prevProjection, _prevNonJitteredProjection; + +public: + TaaJitterRemoveContext(const RenderView& view); + ~TaaJitterRemoveContext(); +}; diff --git a/Source/Engine/Renderer/AntiAliasing/TAA.cpp b/Source/Engine/Renderer/AntiAliasing/TAA.cpp index f48e0955a..22ed7a806 100644 --- a/Source/Engine/Renderer/AntiAliasing/TAA.cpp +++ b/Source/Engine/Renderer/AntiAliasing/TAA.cpp @@ -146,4 +146,7 @@ void TAA::Render(const RenderContext& renderContext, GPUTexture* input, GPUTextu context->Draw(output); renderContext.Buffers->TemporalAA = outputHistory; } + + // Mark TAA jitter as resolved for future drawing + (bool&)renderContext.View.IsTaaResolved = true; } diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index af2495b67..01da9e2ea 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -652,6 +652,7 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL const auto* batchesData = list.Batches.Get(); const auto context = GPUDevice::Instance->GetMainContext(); bool useInstancing = list.CanUseInstancing && CanUseInstancing(renderContext.View.Pass) && GPUDevice::Instance->Limits.HasInstancing; + TaaJitterRemoveContext taaJitterRemove(renderContext.View); // Clear SR slots to prevent any resources binding issues (leftovers from the previous passes) context->ResetSR(); diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 3246b6d85..f3c4ac6d2 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -612,7 +612,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont // Color Grading LUT generation auto colorGradingLUT = ColorGradingPass::Instance()->RenderLUT(renderContext); - // Post processing + // Post-processing EyeAdaptationPass::Instance()->Render(renderContext, frameBuffer); PostProcessingPass::Instance()->Render(renderContext, frameBuffer, tempBuffer, colorGradingLUT); RenderTargetPool::Release(colorGradingLUT); From 388b64a46db186b875e972fddd9610c8af326923 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 16:03:19 +0100 Subject: [PATCH 150/198] Add support for hot-reloading Debug Draw shader --- Source/Engine/Debug/DebugDraw.cpp | 32 ++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index b0164124c..fe6424565 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -356,6 +356,19 @@ namespace Float3 CircleCache[DEBUG_DRAW_CIRCLE_VERTICES]; Array SphereTriangleCache; DebugSphereCache SphereCache[3]; + +#if COMPILE_WITH_DEV_ENV + void OnShaderReloading(Asset* obj) + { + DebugDrawPsLinesDefault.Release(); + DebugDrawPsLinesDepthTest.Release(); + DebugDrawPsWireTrianglesDefault.Release(); + DebugDrawPsWireTrianglesDepthTest.Release(); + DebugDrawPsTrianglesDefault.Release(); + DebugDrawPsTrianglesDepthTest.Release(); + } + +#endif }; extern int32 BoxTrianglesIndicesCache[]; @@ -615,17 +628,19 @@ void DebugDrawService::Update() GlobalContext.DebugDrawDefault.Update(deltaTime); GlobalContext.DebugDrawDepthTest.Update(deltaTime); - // Check if need to setup a resources + // Lazy-init resources if (DebugDrawShader == nullptr) { - // Shader DebugDrawShader = Content::LoadAsyncInternal(TEXT("Shaders/DebugDraw")); if (DebugDrawShader == nullptr) { LOG(Fatal, "Cannot load DebugDraw shader"); } +#if COMPILE_WITH_DEV_ENV + DebugDrawShader->OnReloading.Bind(&OnShaderReloading); +#endif } - if (DebugDrawVB == nullptr && DebugDrawShader && DebugDrawShader->IsLoaded()) + if (DebugDrawPsWireTrianglesDepthTest.Depth == nullptr && DebugDrawShader && DebugDrawShader->IsLoaded()) { bool failed = false; const auto shader = DebugDrawShader->GetShader(); @@ -661,10 +676,11 @@ void DebugDrawService::Update() { LOG(Fatal, "Cannot setup DebugDraw service!"); } - - // Vertex buffer - DebugDrawVB = New((uint32)(DEBUG_DRAW_INITIAL_VB_CAPACITY * sizeof(Vertex)), (uint32)sizeof(Vertex), TEXT("DebugDraw.VB")); } + + // Vertex buffer + if (DebugDrawVB == nullptr) + DebugDrawVB = New((uint32)(DEBUG_DRAW_INITIAL_VB_CAPACITY * sizeof(Vertex)), (uint32)sizeof(Vertex), TEXT("DebugDraw.VB")); } void DebugDrawService::Dispose() @@ -723,7 +739,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe // Ensure to have shader loaded and any lines to render const int32 debugDrawDepthTestCount = Context->DebugDrawDepthTest.Count(); const int32 debugDrawDefaultCount = Context->DebugDrawDefault.Count(); - if (DebugDrawShader == nullptr || !DebugDrawShader->IsLoaded() || debugDrawDepthTestCount + debugDrawDefaultCount == 0) + if (DebugDrawShader == nullptr || !DebugDrawShader->IsLoaded() || debugDrawDepthTestCount + debugDrawDefaultCount == 0 || DebugDrawPsWireTrianglesDepthTest.Depth == nullptr) return; if (renderContext.Buffers == nullptr || !DebugDrawVB) return; @@ -849,6 +865,8 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe { PROFILE_GPU_CPU_NAMED("Text"); auto features = Render2D::Features; + + // Disable vertex snapping when rendering 3D text Render2D::Features = (Render2D::RenderingFeatures)((uint32)features & ~(uint32)Render2D::RenderingFeatures::VertexSnapping); if (!DebugDrawFont) From 71dad849084b4478b0080952bbd6f4aeb45bc655 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 17:53:14 +0100 Subject: [PATCH 151/198] Fix Z-fighting issues in Debug Draw when using TAA --- Content/Shaders/DebugDraw.flax | 4 ++-- Source/Engine/Debug/DebugDraw.cpp | 4 +++- Source/Shaders/DebugDraw.shader | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Content/Shaders/DebugDraw.flax b/Content/Shaders/DebugDraw.flax index 284c55f64..455cf8c20 100644 --- a/Content/Shaders/DebugDraw.flax +++ b/Content/Shaders/DebugDraw.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:10fefac1db81c41041c95970dc13694a17dfbca09819d6f7344b5d45f5e8c0df -size 2060 +oid sha256:7f8833a76cdda54b6c1de3b98f7993b835d17a5ac60b0bf11a9ee9faa42cc177 +size 2108 diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index fe6424565..ea8cdb196 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -127,7 +127,8 @@ PACK_STRUCT(struct Vertex { PACK_STRUCT(struct Data { Matrix ViewProjection; - Float3 Padding; + Float2 Padding; + float ClipPosZBias; bool EnableDepthTest; }); @@ -785,6 +786,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe Matrix vp; Matrix::Multiply(view.View, view.Projection, vp); Matrix::Transpose(vp, data.ViewProjection); + data.ClipPosZBias = -0.2f; // Reduce Z-fighting artifacts (eg. editor grid) data.EnableDepthTest = enableDepthTest; context->UpdateCB(cb, &data); context->BindCB(0, cb); diff --git a/Source/Shaders/DebugDraw.shader b/Source/Shaders/DebugDraw.shader index 6495cb1a3..d10dbbf06 100644 --- a/Source/Shaders/DebugDraw.shader +++ b/Source/Shaders/DebugDraw.shader @@ -4,7 +4,8 @@ META_CB_BEGIN(0, Data) float4x4 ViewProjection; -float3 Padding; +float2 Padding; +float ClipPosZBias; bool EnableDepthTest; META_CB_END @@ -23,6 +24,7 @@ VS2PS VS(float3 Position : POSITION, float4 Color : COLOR) { VS2PS output; output.Position = mul(float4(Position, 1), ViewProjection); + output.Position.z += ClipPosZBias; output.Color = Color; return output; } From 155b4a3ba92bb05a9c8a27890ca2aac6e4a4d4b8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 17:53:45 +0100 Subject: [PATCH 152/198] Refactor editor gizmo viewports to use shared code for widgets --- Source/Editor/Viewport/EditorGizmoViewport.cs | 246 ++++++++++++++++ Source/Editor/Viewport/EditorViewport.cs | 36 ++- .../Viewport/MainEditorGizmoViewport.cs | 265 +----------------- .../Editor/Viewport/PrefabWindowViewport.cs | 234 +--------------- 4 files changed, 265 insertions(+), 516 deletions(-) diff --git a/Source/Editor/Viewport/EditorGizmoViewport.cs b/Source/Editor/Viewport/EditorGizmoViewport.cs index 25dcd76e7..856ae20da 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -1,9 +1,12 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using System; using System.Collections.Generic; using FlaxEditor.Gizmo; +using FlaxEditor.GUI.ContextMenu; using FlaxEditor.SceneGraph; using FlaxEditor.Viewport.Cameras; +using FlaxEditor.Viewport.Widgets; using FlaxEngine; using FlaxEngine.GUI; @@ -126,6 +129,249 @@ namespace FlaxEditor.Viewport base.OnDestroy(); } + internal static void AddGizmoViewportWidgets(EditorViewport viewport, TransformGizmo transformGizmo, bool useProjectCache = false) + { + var editor = Editor.Instance; + var inputOptions = editor.Options.Options.Input; + + if (useProjectCache) + { + // Initialize snapping enabled from cached values + if (editor.ProjectCache.TryGetCustomData("TranslateSnapState", out var cachedState)) + transformGizmo.TranslationSnapEnable = bool.Parse(cachedState); + if (editor.ProjectCache.TryGetCustomData("RotationSnapState", out cachedState)) + transformGizmo.RotationSnapEnabled = bool.Parse(cachedState); + if (editor.ProjectCache.TryGetCustomData("ScaleSnapState", out cachedState)) + transformGizmo.ScaleSnapEnabled = bool.Parse(cachedState); + if (editor.ProjectCache.TryGetCustomData("TranslateSnapValue", out cachedState)) + transformGizmo.TranslationSnapValue = float.Parse(cachedState); + if (editor.ProjectCache.TryGetCustomData("RotationSnapValue", out cachedState)) + transformGizmo.RotationSnapValue = float.Parse(cachedState); + if (editor.ProjectCache.TryGetCustomData("ScaleSnapValue", out cachedState)) + transformGizmo.ScaleSnapValue = float.Parse(cachedState); + if (editor.ProjectCache.TryGetCustomData("TransformSpaceState", out cachedState) && Enum.TryParse(cachedState, out TransformGizmoBase.TransformSpace space)) + transformGizmo.ActiveTransformSpace = space; + } + + // Transform space widget + var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); + var transformSpaceToggle = new ViewportWidgetButton(string.Empty, editor.Icons.Globe32, null, true) + { + Checked = transformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World, + TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})", + Parent = transformSpaceWidget + }; + transformSpaceToggle.Toggled += _ => + { + transformGizmo.ToggleTransformSpace(); + if (useProjectCache) + editor.ProjectCache.SetCustomData("TransformSpaceState", transformGizmo.ActiveTransformSpace.ToString()); + }; + transformSpaceWidget.Parent = viewport; + + // Scale snapping widget + var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); + var enableScaleSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.ScaleSnap32, null, true) + { + Checked = transformGizmo.ScaleSnapEnabled, + TooltipText = "Enable scale snapping", + Parent = scaleSnappingWidget + }; + enableScaleSnapping.Toggled += _ => + { + transformGizmo.ScaleSnapEnabled = !transformGizmo.ScaleSnapEnabled; + if (useProjectCache) + editor.ProjectCache.SetCustomData("ScaleSnapState", transformGizmo.ScaleSnapEnabled.ToString()); + }; + var scaleSnappingCM = new ContextMenu(); + var scaleSnapping = new ViewportWidgetButton(transformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM) + { + TooltipText = "Scale snapping values" + }; + for (int i = 0; i < ScaleSnapValues.Length; i++) + { + var v = ScaleSnapValues[i]; + var button = scaleSnappingCM.AddButton(v.ToString()); + button.Tag = v; + } + scaleSnappingCM.ButtonClicked += button => + { + var v = (float)button.Tag; + transformGizmo.ScaleSnapValue = v; + scaleSnapping.Text = v.ToString(); + if (useProjectCache) + editor.ProjectCache.SetCustomData("ScaleSnapValue", transformGizmo.ScaleSnapValue.ToString("N")); + }; + scaleSnappingCM.VisibleChanged += control => + { + if (control.Visible == false) + return; + var ccm = (ContextMenu)control; + foreach (var e in ccm.Items) + { + if (e is ContextMenuButton b) + { + var v = (float)b.Tag; + b.Icon = Mathf.Abs(transformGizmo.ScaleSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + } + } + }; + scaleSnapping.Parent = scaleSnappingWidget; + scaleSnappingWidget.Parent = viewport; + + // Rotation snapping widget + var rotateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); + var enableRotateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.RotateSnap32, null, true) + { + Checked = transformGizmo.RotationSnapEnabled, + TooltipText = "Enable rotation snapping", + Parent = rotateSnappingWidget + }; + enableRotateSnapping.Toggled += _ => + { + transformGizmo.RotationSnapEnabled = !transformGizmo.RotationSnapEnabled; + if (useProjectCache) + editor.ProjectCache.SetCustomData("RotationSnapState", transformGizmo.RotationSnapEnabled.ToString()); + }; + var rotateSnappingCM = new ContextMenu(); + var rotateSnapping = new ViewportWidgetButton(transformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM) + { + TooltipText = "Rotation snapping values" + }; + for (int i = 0; i < RotateSnapValues.Length; i++) + { + var v = RotateSnapValues[i]; + var button = rotateSnappingCM.AddButton(v.ToString()); + button.Tag = v; + } + rotateSnappingCM.ButtonClicked += button => + { + var v = (float)button.Tag; + transformGizmo.RotationSnapValue = v; + rotateSnapping.Text = v.ToString(); + if (useProjectCache) + editor.ProjectCache.SetCustomData("RotationSnapValue", transformGizmo.RotationSnapValue.ToString("N")); + }; + rotateSnappingCM.VisibleChanged += control => + { + if (control.Visible == false) + return; + var ccm = (ContextMenu)control; + foreach (var e in ccm.Items) + { + if (e is ContextMenuButton b) + { + var v = (float)b.Tag; + b.Icon = Mathf.Abs(transformGizmo.RotationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + } + } + }; + rotateSnapping.Parent = rotateSnappingWidget; + rotateSnappingWidget.Parent = viewport; + + // Translation snapping widget + var translateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); + var enableTranslateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.Grid32, null, true) + { + Checked = transformGizmo.TranslationSnapEnable, + TooltipText = "Enable position snapping", + Parent = translateSnappingWidget + }; + enableTranslateSnapping.Toggled += _ => + { + transformGizmo.TranslationSnapEnable = !transformGizmo.TranslationSnapEnable; + if (useProjectCache) + editor.ProjectCache.SetCustomData("TranslateSnapState", transformGizmo.TranslationSnapEnable.ToString()); + }; + var translateSnappingCM = new ContextMenu(); + var translateSnapping = new ViewportWidgetButton(transformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM) + { + TooltipText = "Position snapping values" + }; + if (transformGizmo.TranslationSnapValue < 0.0f) + translateSnapping.Text = "Bounding Box"; + for (int i = 0; i < TranslateSnapValues.Length; i++) + { + var v = TranslateSnapValues[i]; + var button = translateSnappingCM.AddButton(v.ToString()); + button.Tag = v; + } + var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume"); + buttonBB.Tag = -1.0f; + translateSnappingCM.ButtonClicked += button => + { + var v = (float)button.Tag; + transformGizmo.TranslationSnapValue = v; + if (v < 0.0f) + translateSnapping.Text = "Bounding Box"; + else + translateSnapping.Text = v.ToString(); + if (useProjectCache) + editor.ProjectCache.SetCustomData("TranslateSnapValue", transformGizmo.TranslationSnapValue.ToString("N")); + }; + translateSnappingCM.VisibleChanged += control => + { + if (control.Visible == false) + return; + var ccm = (ContextMenu)control; + foreach (var e in ccm.Items) + { + if (e is ContextMenuButton b) + { + var v = (float)b.Tag; + b.Icon = Mathf.Abs(transformGizmo.TranslationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; + } + } + }; + translateSnapping.Parent = translateSnappingWidget; + translateSnappingWidget.Parent = viewport; + + // Gizmo mode widget + var gizmoMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); + var gizmoModeTranslate = new ViewportWidgetButton(string.Empty, editor.Icons.Translate32, null, true) + { + Tag = TransformGizmoBase.Mode.Translate, + TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})", + Checked = true, + Parent = gizmoMode + }; + gizmoModeTranslate.Toggled += _ => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate; + var gizmoModeRotate = new ViewportWidgetButton(string.Empty, editor.Icons.Rotate32, null, true) + { + Tag = TransformGizmoBase.Mode.Rotate, + TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})", + Parent = gizmoMode + }; + gizmoModeRotate.Toggled += _ => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate; + var gizmoModeScale = new ViewportWidgetButton(string.Empty, editor.Icons.Scale32, null, true) + { + Tag = TransformGizmoBase.Mode.Scale, + TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})", + Parent = gizmoMode + }; + gizmoModeScale.Toggled += _ => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale; + gizmoMode.Parent = viewport; + transformGizmo.ModeChanged += () => + { + var mode = transformGizmo.ActiveMode; + gizmoModeTranslate.Checked = mode == TransformGizmoBase.Mode.Translate; + gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate; + gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale; + }; + + // Setup input actions + viewport.InputActions.Add(options => options.TranslateMode, () => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); + viewport.InputActions.Add(options => options.RotateMode, () => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); + viewport.InputActions.Add(options => options.ScaleMode, () => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale); + viewport.InputActions.Add(options => options.ToggleTransformSpace, () => + { + transformGizmo.ToggleTransformSpace(); + if (useProjectCache) + editor.ProjectCache.SetCustomData("TransformSpaceState", transformGizmo.ActiveTransformSpace.ToString()); + transformSpaceToggle.Checked = !transformSpaceToggle.Checked; + }); + } + internal static readonly float[] TranslateSnapValues = { 0.1f, diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index a53f95ea8..2eeb1e8c0 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -704,9 +704,9 @@ namespace FlaxEditor.Viewport // Camera Viewpoints { var cameraView = cameraCM.AddChildMenu("Viewpoints").ContextMenu; - for (int i = 0; i < EditorViewportCameraViewpointValues.Length; i++) + for (int i = 0; i < CameraViewpointValues.Length; i++) { - var co = EditorViewportCameraViewpointValues[i]; + var co = CameraViewpointValues[i]; var button = cameraView.AddButton(co.Name); button.Tag = co.Orientation; } @@ -899,9 +899,9 @@ namespace FlaxEditor.Viewport viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32; viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None).Icon = Editor.Instance.Icons.Rotate32; viewFlags.AddSeparator(); - for (int i = 0; i < EditorViewportViewFlagsValues.Length; i++) + for (int i = 0; i < ViewFlagsValues.Length; i++) { - var v = EditorViewportViewFlagsValues[i]; + var v = ViewFlagsValues[i]; var button = viewFlags.AddButton(v.Name); button.CloseMenuOnClick = false; button.Tag = v.Mode; @@ -933,9 +933,9 @@ namespace FlaxEditor.Viewport } }); debugView.AddSeparator(); - for (int i = 0; i < EditorViewportViewModeValues.Length; i++) + for (int i = 0; i < ViewModeValues.Length; i++) { - ref var v = ref EditorViewportViewModeValues[i]; + ref var v = ref ViewModeValues[i]; if (v.Options != null) { var childMenu = debugView.AddChildMenu(v.Name).ContextMenu; @@ -989,12 +989,12 @@ namespace FlaxEditor.Viewport #endregion View mode widget } - InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Top").Orientation))); - InputActions.Add(options => options.ViewpointBottom, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Bottom").Orientation))); - InputActions.Add(options => options.ViewpointFront, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Front").Orientation))); - InputActions.Add(options => options.ViewpointBack, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Back").Orientation))); - InputActions.Add(options => options.ViewpointRight, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Right").Orientation))); - InputActions.Add(options => options.ViewpointLeft, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Left").Orientation))); + InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Top").Orientation))); + InputActions.Add(options => options.ViewpointBottom, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Bottom").Orientation))); + InputActions.Add(options => options.ViewpointFront, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Front").Orientation))); + InputActions.Add(options => options.ViewpointBack, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Back").Orientation))); + InputActions.Add(options => options.ViewpointRight, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Right").Orientation))); + InputActions.Add(options => options.ViewpointLeft, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Left").Orientation))); InputActions.Add(options => options.CameraToggleRotation, () => _isVirtualMouseRightDown = !_isVirtualMouseRightDown); InputActions.Add(options => options.CameraIncreaseMoveSpeed, () => AdjustCameraMoveSpeed(1)); InputActions.Add(options => options.CameraDecreaseMoveSpeed, () => AdjustCameraMoveSpeed(-1)); @@ -1870,7 +1870,7 @@ namespace FlaxEditor.Viewport } } - private readonly CameraViewpoint[] EditorViewportCameraViewpointValues = + private readonly CameraViewpoint[] CameraViewpointValues = { new CameraViewpoint("Front", new Float3(0, 180, 0)), new CameraViewpoint("Back", new Float3(0, 0, 0)), @@ -1901,7 +1901,7 @@ namespace FlaxEditor.Viewport } } - private static readonly ViewModeOptions[] EditorViewportViewModeValues = + private static readonly ViewModeOptions[] ViewModeValues = { new ViewModeOptions(ViewMode.Default, "Default"), new ViewModeOptions(ViewMode.Unlit, "Unlit"), @@ -1973,7 +1973,7 @@ namespace FlaxEditor.Viewport } } - private static readonly ViewFlagOptions[] EditorViewportViewFlagsValues = + private static readonly ViewFlagOptions[] ViewFlagsValues = { new ViewFlagOptions(ViewFlags.AntiAliasing, "Anti Aliasing"), new ViewFlagOptions(ViewFlags.Shadows, "Shadows"), @@ -2008,16 +2008,13 @@ namespace FlaxEditor.Viewport { if (cm.Visible == false) return; - var ccm = (ContextMenu)cm; foreach (var e in ccm.Items) { if (e is ContextMenuButton b && b.Tag != null) { var v = (ViewFlags)b.Tag; - b.Icon = (Task.View.Flags & v) != 0 - ? Style.Current.CheckBoxTick - : SpriteHandle.Invalid; + b.Icon = (Task.View.Flags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; } } } @@ -2026,7 +2023,6 @@ namespace FlaxEditor.Viewport { if (cm.Visible == false) return; - var ccm = (ContextMenu)cm; var layersMask = Task.ViewLayersMask; foreach (var e in ccm.Items) diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index c9bb4c88a..7d08213fa 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -7,9 +7,7 @@ using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.SceneGraph; using FlaxEditor.Scripting; -using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Modes; -using FlaxEditor.Viewport.Widgets; using FlaxEditor.Windows; using FlaxEngine; using FlaxEngine.GUI; @@ -27,13 +25,6 @@ namespace FlaxEditor.Viewport private readonly ContextMenuButton _showGridButton; private readonly ContextMenuButton _showNavigationButton; - private readonly ViewportWidgetButton _gizmoModeTranslate; - private readonly ViewportWidgetButton _gizmoModeRotate; - private readonly ViewportWidgetButton _gizmoModeScale; - - private readonly ViewportWidgetButton _translateSnapping; - private readonly ViewportWidgetButton _rotateSnapping; - private readonly ViewportWidgetButton _scaleSnapping; private SelectionOutline _customSelectionOutline; @@ -195,7 +186,6 @@ namespace FlaxEditor.Viewport { _editor = editor; DragHandlers = new ViewportDragHandlers(this, this, ValidateDragItem, ValidateDragActorType, ValidateDragScriptItem); - var inputOptions = editor.Options.Options.Input; // Prepare rendering task Task.ActorsSource = ActorsSources.Scenes; @@ -221,7 +211,6 @@ namespace FlaxEditor.Viewport // Add transformation gizmo TransformGizmo = new TransformGizmo(this); TransformGizmo.ApplyTransformation += ApplyTransform; - TransformGizmo.ModeChanged += OnGizmoModeChanged; TransformGizmo.Duplicate += _editor.SceneEditing.Duplicate; Gizmos.Active = TransformGizmo; @@ -231,137 +220,8 @@ namespace FlaxEditor.Viewport editor.SceneEditing.SelectionChanged += OnSelectionChanged; - // Initialize snapping enabled from cached values - if (_editor.ProjectCache.TryGetCustomData("TranslateSnapState", out var cachedState)) - TransformGizmo.TranslationSnapEnable = bool.Parse(cachedState); - if (_editor.ProjectCache.TryGetCustomData("RotationSnapState", out cachedState)) - TransformGizmo.RotationSnapEnabled = bool.Parse(cachedState); - if (_editor.ProjectCache.TryGetCustomData("ScaleSnapState", out cachedState)) - TransformGizmo.ScaleSnapEnabled = bool.Parse(cachedState); - if (_editor.ProjectCache.TryGetCustomData("TranslateSnapValue", out cachedState)) - TransformGizmo.TranslationSnapValue = float.Parse(cachedState); - if (_editor.ProjectCache.TryGetCustomData("RotationSnapValue", out cachedState)) - TransformGizmo.RotationSnapValue = float.Parse(cachedState); - if (_editor.ProjectCache.TryGetCustomData("ScaleSnapValue", out cachedState)) - TransformGizmo.ScaleSnapValue = float.Parse(cachedState); - if (_editor.ProjectCache.TryGetCustomData("TransformSpaceState", out cachedState) && Enum.TryParse(cachedState, out TransformGizmoBase.TransformSpace space)) - TransformGizmo.ActiveTransformSpace = space; - - // Transform space widget - var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var transformSpaceToggle = new ViewportWidgetButton(string.Empty, editor.Icons.Globe32, null, true) - { - Checked = TransformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World, - TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})", - Parent = transformSpaceWidget - }; - transformSpaceToggle.Toggled += OnTransformSpaceToggle; - transformSpaceWidget.Parent = this; - - // Scale snapping widget - var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var enableScaleSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.ScaleSnap32, null, true) - { - Checked = TransformGizmo.ScaleSnapEnabled, - TooltipText = "Enable scale snapping", - Parent = scaleSnappingWidget - }; - enableScaleSnapping.Toggled += OnScaleSnappingToggle; - var scaleSnappingCM = new ContextMenu(); - _scaleSnapping = new ViewportWidgetButton(TransformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM) - { - TooltipText = "Scale snapping values" - }; - for (int i = 0; i < ScaleSnapValues.Length; i++) - { - var v = ScaleSnapValues[i]; - var button = scaleSnappingCM.AddButton(v.ToString()); - button.Tag = v; - } - scaleSnappingCM.ButtonClicked += OnWidgetScaleSnapClick; - scaleSnappingCM.VisibleChanged += OnWidgetScaleSnapShowHide; - _scaleSnapping.Parent = scaleSnappingWidget; - scaleSnappingWidget.Parent = this; - - // Rotation snapping widget - var rotateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var enableRotateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.RotateSnap32, null, true) - { - Checked = TransformGizmo.RotationSnapEnabled, - TooltipText = "Enable rotation snapping", - Parent = rotateSnappingWidget - }; - enableRotateSnapping.Toggled += OnRotateSnappingToggle; - var rotateSnappingCM = new ContextMenu(); - _rotateSnapping = new ViewportWidgetButton(TransformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM) - { - TooltipText = "Rotation snapping values" - }; - for (int i = 0; i < RotateSnapValues.Length; i++) - { - var v = RotateSnapValues[i]; - var button = rotateSnappingCM.AddButton(v.ToString()); - button.Tag = v; - } - rotateSnappingCM.ButtonClicked += OnWidgetRotateSnapClick; - rotateSnappingCM.VisibleChanged += OnWidgetRotateSnapShowHide; - _rotateSnapping.Parent = rotateSnappingWidget; - rotateSnappingWidget.Parent = this; - - // Translation snapping widget - var translateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var enableTranslateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.Grid32, null, true) - { - Checked = TransformGizmo.TranslationSnapEnable, - TooltipText = "Enable position snapping", - Parent = translateSnappingWidget - }; - enableTranslateSnapping.Toggled += OnTranslateSnappingToggle; - var translateSnappingCM = new ContextMenu(); - _translateSnapping = new ViewportWidgetButton(TransformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM) - { - TooltipText = "Position snapping values" - }; - if (TransformGizmo.TranslationSnapValue < 0.0f) - _translateSnapping.Text = "Bounding Box"; - for (int i = 0; i < TranslateSnapValues.Length; i++) - { - var v = TranslateSnapValues[i]; - var button = translateSnappingCM.AddButton(v.ToString()); - button.Tag = v; - } - var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume"); - buttonBB.Tag = -1.0f; - translateSnappingCM.ButtonClicked += OnWidgetTranslateSnapClick; - translateSnappingCM.VisibleChanged += OnWidgetTranslateSnapShowHide; - _translateSnapping.Parent = translateSnappingWidget; - translateSnappingWidget.Parent = this; - - // Gizmo mode widget - var gizmoMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - _gizmoModeTranslate = new ViewportWidgetButton(string.Empty, editor.Icons.Translate32, null, true) - { - Tag = TransformGizmoBase.Mode.Translate, - TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})", - Checked = true, - Parent = gizmoMode - }; - _gizmoModeTranslate.Toggled += OnGizmoModeToggle; - _gizmoModeRotate = new ViewportWidgetButton(string.Empty, editor.Icons.Rotate32, null, true) - { - Tag = TransformGizmoBase.Mode.Rotate, - TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})", - Parent = gizmoMode - }; - _gizmoModeRotate.Toggled += OnGizmoModeToggle; - _gizmoModeScale = new ViewportWidgetButton(string.Empty, editor.Icons.Scale32, null, true) - { - Tag = TransformGizmoBase.Mode.Scale, - TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})", - Parent = gizmoMode - }; - _gizmoModeScale.Toggled += OnGizmoModeToggle; - gizmoMode.Parent = this; + // Gizmo widgets + AddGizmoViewportWidgets(this, TransformGizmo); // Show grid widget _showGridButton = ViewWidgetShowMenu.AddButton("Grid", () => Grid.Enabled = !Grid.Enabled); @@ -392,14 +252,6 @@ namespace FlaxEditor.Viewport } // Setup input actions - InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); - InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); - InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale); - InputActions.Add(options => options.ToggleTransformSpace, () => - { - OnTransformSpaceToggle(transformSpaceToggle); - transformSpaceToggle.Checked = !transformSpaceToggle.Checked; - }); InputActions.Add(options => options.LockFocusSelection, LockFocusSelection); InputActions.Add(options => options.FocusSelection, FocusSelection); InputActions.Add(options => options.RotateSelection, RotateSelection); @@ -573,119 +425,6 @@ namespace FlaxEditor.Viewport } } - private void OnGizmoModeToggle(ViewportWidgetButton button) - { - TransformGizmo.ActiveMode = (TransformGizmoBase.Mode)(int)button.Tag; - } - - private void OnTranslateSnappingToggle(ViewportWidgetButton button) - { - TransformGizmo.TranslationSnapEnable = !TransformGizmo.TranslationSnapEnable; - _editor.ProjectCache.SetCustomData("TranslateSnapState", TransformGizmo.TranslationSnapEnable.ToString()); - } - - private void OnRotateSnappingToggle(ViewportWidgetButton button) - { - TransformGizmo.RotationSnapEnabled = !TransformGizmo.RotationSnapEnabled; - _editor.ProjectCache.SetCustomData("RotationSnapState", TransformGizmo.RotationSnapEnabled.ToString()); - } - - private void OnScaleSnappingToggle(ViewportWidgetButton button) - { - TransformGizmo.ScaleSnapEnabled = !TransformGizmo.ScaleSnapEnabled; - _editor.ProjectCache.SetCustomData("ScaleSnapState", TransformGizmo.ScaleSnapEnabled.ToString()); - } - - private void OnTransformSpaceToggle(ViewportWidgetButton button) - { - TransformGizmo.ToggleTransformSpace(); - _editor.ProjectCache.SetCustomData("TransformSpaceState", TransformGizmo.ActiveTransformSpace.ToString()); - } - - private void OnGizmoModeChanged() - { - // Update all viewport widgets status - var mode = TransformGizmo.ActiveMode; - _gizmoModeTranslate.Checked = mode == TransformGizmoBase.Mode.Translate; - _gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate; - _gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale; - } - - private void OnWidgetScaleSnapClick(ContextMenuButton button) - { - var v = (float)button.Tag; - TransformGizmo.ScaleSnapValue = v; - _scaleSnapping.Text = v.ToString(); - _editor.ProjectCache.SetCustomData("ScaleSnapValue", TransformGizmo.ScaleSnapValue.ToString("N")); - } - - private void OnWidgetScaleSnapShowHide(Control control) - { - if (control.Visible == false) - return; - - var ccm = (ContextMenu)control; - foreach (var e in ccm.Items) - { - if (e is ContextMenuButton b) - { - var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; - } - } - } - - private void OnWidgetRotateSnapClick(ContextMenuButton button) - { - var v = (float)button.Tag; - TransformGizmo.RotationSnapValue = v; - _rotateSnapping.Text = v.ToString(); - _editor.ProjectCache.SetCustomData("RotationSnapValue", TransformGizmo.RotationSnapValue.ToString("N")); - } - - private void OnWidgetRotateSnapShowHide(Control control) - { - if (control.Visible == false) - return; - - var ccm = (ContextMenu)control; - foreach (var e in ccm.Items) - { - if (e is ContextMenuButton b) - { - var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; - } - } - } - - private void OnWidgetTranslateSnapClick(ContextMenuButton button) - { - var v = (float)button.Tag; - TransformGizmo.TranslationSnapValue = v; - if (v < 0.0f) - _translateSnapping.Text = "Bounding Box"; - else - _translateSnapping.Text = v.ToString(); - _editor.ProjectCache.SetCustomData("TranslateSnapValue", TransformGizmo.TranslationSnapValue.ToString("N")); - } - - private void OnWidgetTranslateSnapShowHide(Control control) - { - if (control.Visible == false) - return; - - var ccm = (ContextMenu)control; - foreach (var e in ccm.Items) - { - if (e is ContextMenuButton b) - { - var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; - } - } - } - private void OnSelectionChanged() { var selection = _editor.SceneEditing.Selection; diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 252981555..081c1d4f1 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -5,12 +5,10 @@ using System.Collections.Generic; using System.Linq; using FlaxEditor.Content; using FlaxEditor.Gizmo; -using FlaxEditor.GUI.ContextMenu; using FlaxEditor.SceneGraph; using FlaxEditor.Scripting; using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Previews; -using FlaxEditor.Viewport.Widgets; using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; @@ -63,14 +61,6 @@ namespace FlaxEditor.Viewport private readonly PrefabWindow _window; private UpdateDelegate _update; - private readonly ViewportWidgetButton _gizmoModeTranslate; - private readonly ViewportWidgetButton _gizmoModeRotate; - private readonly ViewportWidgetButton _gizmoModeScale; - - private ViewportWidgetButton _translateSnappng; - private ViewportWidgetButton _rotateSnapping; - private ViewportWidgetButton _scaleSnapping; - private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32); private PrefabSpritesRenderer _spritesRenderer; private IntPtr _tempDebugDrawContext; @@ -129,7 +119,6 @@ namespace FlaxEditor.Viewport // Add transformation gizmo TransformGizmo = new TransformGizmo(this); TransformGizmo.ApplyTransformation += ApplyTransform; - TransformGizmo.ModeChanged += OnGizmoModeChanged; TransformGizmo.Duplicate += _window.Duplicate; Gizmos.Active = TransformGizmo; @@ -138,127 +127,9 @@ namespace FlaxEditor.Viewport _uiRoot.IndexInParent = 0; // Move viewport down below other widgets in the viewport _uiParentLink = _uiRoot.UIRoot; - // Transform space widget - var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var transformSpaceToggle = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Globe32, null, true) - { - Checked = TransformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World, - TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})", - Parent = transformSpaceWidget - }; - transformSpaceToggle.Toggled += OnTransformSpaceToggle; - transformSpaceWidget.Parent = this; - - // Scale snapping widget - var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var enableScaleSnapping = new ViewportWidgetButton(string.Empty, window.Editor.Icons.ScaleSnap32, null, true) - { - Checked = TransformGizmo.ScaleSnapEnabled, - TooltipText = "Enable scale snapping", - Parent = scaleSnappingWidget - }; - enableScaleSnapping.Toggled += OnScaleSnappingToggle; - var scaleSnappingCM = new ContextMenu(); - _scaleSnapping = new ViewportWidgetButton(TransformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM) - { - TooltipText = "Scale snapping values" - }; - for (int i = 0; i < EditorGizmoViewport.ScaleSnapValues.Length; i++) - { - var v = EditorGizmoViewport.ScaleSnapValues[i]; - var button = scaleSnappingCM.AddButton(v.ToString()); - button.Tag = v; - } - scaleSnappingCM.ButtonClicked += OnWidgetScaleSnapClick; - scaleSnappingCM.VisibleChanged += OnWidgetScaleSnapShowHide; - _scaleSnapping.Parent = scaleSnappingWidget; - scaleSnappingWidget.Parent = this; - - // Rotation snapping widget - var rotateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var enableRotateSnapping = new ViewportWidgetButton(string.Empty, window.Editor.Icons.RotateSnap32, null, true) - { - Checked = TransformGizmo.RotationSnapEnabled, - TooltipText = "Enable rotation snapping", - Parent = rotateSnappingWidget - }; - enableRotateSnapping.Toggled += OnRotateSnappingToggle; - var rotateSnappingCM = new ContextMenu(); - _rotateSnapping = new ViewportWidgetButton(TransformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM) - { - TooltipText = "Rotation snapping values" - }; - for (int i = 0; i < EditorGizmoViewport.RotateSnapValues.Length; i++) - { - var v = EditorGizmoViewport.RotateSnapValues[i]; - var button = rotateSnappingCM.AddButton(v.ToString()); - button.Tag = v; - } - rotateSnappingCM.ButtonClicked += OnWidgetRotateSnapClick; - rotateSnappingCM.VisibleChanged += OnWidgetRotateSnapShowHide; - _rotateSnapping.Parent = rotateSnappingWidget; - rotateSnappingWidget.Parent = this; - - // Translation snapping widget - var translateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - var enableTranslateSnapping = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Grid32, null, true) - { - Checked = TransformGizmo.TranslationSnapEnable, - TooltipText = "Enable position snapping", - Parent = translateSnappingWidget - }; - enableTranslateSnapping.Toggled += OnTranslateSnappingToggle; - var translateSnappingCM = new ContextMenu(); - _translateSnappng = new ViewportWidgetButton(TransformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM) - { - TooltipText = "Position snapping values" - }; - for (int i = 0; i < EditorGizmoViewport.TranslateSnapValues.Length; i++) - { - var v = EditorGizmoViewport.TranslateSnapValues[i]; - var button = translateSnappingCM.AddButton(v.ToString()); - button.Tag = v; - } - translateSnappingCM.ButtonClicked += OnWidgetTranslateSnapClick; - translateSnappingCM.VisibleChanged += OnWidgetTranslateSnapShowHide; - _translateSnappng.Parent = translateSnappingWidget; - translateSnappingWidget.Parent = this; - - // Gizmo mode widget - var gizmoMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); - _gizmoModeTranslate = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Translate32, null, true) - { - Tag = TransformGizmoBase.Mode.Translate, - TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})", - Checked = true, - Parent = gizmoMode - }; - _gizmoModeTranslate.Toggled += OnGizmoModeToggle; - _gizmoModeRotate = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Rotate32, null, true) - { - Tag = TransformGizmoBase.Mode.Rotate, - TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})", - Parent = gizmoMode - }; - _gizmoModeRotate.Toggled += OnGizmoModeToggle; - _gizmoModeScale = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Scale32, null, true) - { - Tag = TransformGizmoBase.Mode.Scale, - TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})", - Parent = gizmoMode - }; - _gizmoModeScale.Toggled += OnGizmoModeToggle; - gizmoMode.Parent = this; + EditorGizmoViewport.AddGizmoViewportWidgets(this, TransformGizmo); // Setup input actions - InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); - InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); - InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale); - InputActions.Add(options => options.ToggleTransformSpace, () => - { - OnTransformSpaceToggle(transformSpaceToggle); - transformSpaceToggle.Checked = !transformSpaceToggle.Checked; - }); InputActions.Add(options => options.FocusSelection, ShowSelectedActors); SetUpdate(ref _update, OnUpdate); @@ -472,109 +343,6 @@ namespace FlaxEditor.Viewport root.UpdateCallbacksToRemove.Add(_update); } - private void OnGizmoModeToggle(ViewportWidgetButton button) - { - TransformGizmo.ActiveMode = (TransformGizmoBase.Mode)(int)button.Tag; - } - - private void OnTranslateSnappingToggle(ViewportWidgetButton button) - { - TransformGizmo.TranslationSnapEnable = !TransformGizmo.TranslationSnapEnable; - } - - private void OnRotateSnappingToggle(ViewportWidgetButton button) - { - TransformGizmo.RotationSnapEnabled = !TransformGizmo.RotationSnapEnabled; - } - - private void OnScaleSnappingToggle(ViewportWidgetButton button) - { - TransformGizmo.ScaleSnapEnabled = !TransformGizmo.ScaleSnapEnabled; - } - - private void OnTransformSpaceToggle(ViewportWidgetButton button) - { - TransformGizmo.ToggleTransformSpace(); - } - - private void OnGizmoModeChanged() - { - // Update all viewport widgets status - var mode = TransformGizmo.ActiveMode; - _gizmoModeTranslate.Checked = mode == TransformGizmoBase.Mode.Translate; - _gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate; - _gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale; - } - - private void OnWidgetScaleSnapClick(ContextMenuButton button) - { - var v = (float)button.Tag; - TransformGizmo.ScaleSnapValue = v; - _scaleSnapping.Text = v.ToString(); - } - - private void OnWidgetScaleSnapShowHide(Control control) - { - if (control.Visible == false) - return; - - var ccm = (ContextMenu)control; - foreach (var e in ccm.Items) - { - if (e is ContextMenuButton b) - { - var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; - } - } - } - - private void OnWidgetRotateSnapClick(ContextMenuButton button) - { - var v = (float)button.Tag; - TransformGizmo.RotationSnapValue = v; - _rotateSnapping.Text = v.ToString(); - } - - private void OnWidgetRotateSnapShowHide(Control control) - { - if (control.Visible == false) - return; - - var ccm = (ContextMenu)control; - foreach (var e in ccm.Items) - { - if (e is ContextMenuButton b) - { - var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; - } - } - } - - private void OnWidgetTranslateSnapClick(ContextMenuButton button) - { - var v = (float)button.Tag; - TransformGizmo.TranslationSnapValue = v; - _translateSnappng.Text = v.ToString(); - } - - private void OnWidgetTranslateSnapShowHide(Control control) - { - if (control.Visible == false) - return; - - var ccm = (ContextMenu)control; - foreach (var e in ccm.Items) - { - if (e is ContextMenuButton b) - { - var v = (float)b.Tag; - b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; - } - } - } - private void OnSelectionChanged() { Gizmos.ForEach(x => x.OnSelectionChanged(_window.Selection)); From 35d1d97840b84b59679608dab8ef1c9a05def8f5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 18:30:02 +0100 Subject: [PATCH 153/198] Rename `NumberCategoryAttribute` to `ValueCategoryAttribute` #2213 --- Source/Editor/CustomEditors/Editors/DoubleEditor.cs | 4 ++-- Source/Editor/CustomEditors/Editors/FloatEditor.cs | 4 ++-- Source/Editor/CustomEditors/Editors/Vector3Editor.cs | 4 ++-- Source/Engine/Core/Math/Transform.h | 4 ++-- ...CategoryAttribute.cs => ValueCategoryAttribute.cs} | 11 +++++------ 5 files changed, 13 insertions(+), 14 deletions(-) rename Source/Engine/Scripting/Attributes/Editor/{NumberCategoryAttribute.cs => ValueCategoryAttribute.cs} (66%) diff --git a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs index a2d2cfb25..477f68855 100644 --- a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs @@ -26,8 +26,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); - var categoryAttribute = attributes.FirstOrDefault(x => x is NumberCategoryAttribute); - var valueCategory = ((NumberCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; + var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var limit = attributes.FirstOrDefault(x => x is LimitAttribute); diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index 1ea1ab15f..af6a95c4d 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -32,8 +32,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); - var categoryAttribute = attributes.FirstOrDefault(x => x is NumberCategoryAttribute); - var valueCategory = ((NumberCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; + var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var range = attributes.FirstOrDefault(x => x is RangeAttribute); diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index 8d156f075..2850fa4f3 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -75,7 +75,7 @@ namespace FlaxEditor.CustomEditors.Editors if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); - var categoryAttribute = (NumberCategoryAttribute)attributes.FirstOrDefault(x => x is NumberCategoryAttribute); + var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); if (categoryAttribute != null) category = categoryAttribute.Category; } @@ -273,7 +273,7 @@ namespace FlaxEditor.CustomEditors.Editors if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); - var categoryAttribute = (NumberCategoryAttribute)attributes.FirstOrDefault(x => x is NumberCategoryAttribute); + var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); if (categoryAttribute != null) category = categoryAttribute.Category; } diff --git a/Source/Engine/Core/Math/Transform.h b/Source/Engine/Core/Math/Transform.h index 3dd47df11..2c51bfa4a 100644 --- a/Source/Engine/Core/Math/Transform.h +++ b/Source/Engine/Core/Math/Transform.h @@ -18,13 +18,13 @@ API_STRUCT() struct FLAXENGINE_API Transform /// /// The translation vector of the transform. /// - API_FIELD(Attributes="EditorOrder(10), EditorDisplay(null, \"Position\"), NumberCategory(Utils.ValueCategory.Distance)") + API_FIELD(Attributes="EditorOrder(10), EditorDisplay(null, \"Position\"), ValueCategory(Utils.ValueCategory.Distance)") Vector3 Translation; /// /// The rotation of the transform. /// - API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Rotation\"), NumberCategory(Utils.ValueCategory.Angle)") + API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Rotation\"), ValueCategory(Utils.ValueCategory.Angle)") Quaternion Orientation; /// diff --git a/Source/Engine/Scripting/Attributes/Editor/NumberCategoryAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs similarity index 66% rename from Source/Engine/Scripting/Attributes/Editor/NumberCategoryAttribute.cs rename to Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs index 6b5726bb1..8ba91e9d0 100644 --- a/Source/Engine/Scripting/Attributes/Editor/NumberCategoryAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs @@ -5,29 +5,28 @@ using System; namespace FlaxEngine { /// - /// Used to specify the value category of a numeric value as either as-is (a scalar), a distance (formatted as cm/m/km) - /// or an angle (formatted with a degree sign). + /// Used to specify the value category of a numeric value as either as-is (a scalar), a distance (formatted as cm/m/km) or an angle (formatted with a degree sign). /// /// [Serializable] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public sealed class NumberCategoryAttribute : Attribute + public sealed class ValueCategoryAttribute : Attribute { /// /// The value category used for formatting. /// public Utils.ValueCategory Category; - private NumberCategoryAttribute() + private ValueCategoryAttribute() { Category = Utils.ValueCategory.None; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The value category. - public NumberCategoryAttribute(Utils.ValueCategory category) + public ValueCategoryAttribute(Utils.ValueCategory category) { Category = category; } From 127e8b077d21a4f6b45149005e7ab7cb701076f3 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 19 Mar 2024 12:30:46 -0500 Subject: [PATCH 154/198] Add focusing search bar in content view and output textbox. --- Source/Editor/Windows/ContentWindow.cs | 3 +++ Source/Editor/Windows/OutputLogWindow.cs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index c96aa8e59..3e29b979f 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -281,6 +281,9 @@ namespace FlaxEditor.Windows _view.OnDelete += Delete; _view.OnDuplicate += Duplicate; _view.OnPaste += Paste; + + _view.InputActions.Add(options => options.Search, () => _itemsSearchBox.Focus()); + InputActions.Add(options => options.Search, () => _itemsSearchBox.Focus()); } private ContextMenu OnViewDropdownPopupCreate(ComboBox comboBox) diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index 87d18246a..cd76412c8 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -65,6 +65,11 @@ namespace FlaxEditor.Windows /// public OutputLogWindow Window; + /// + /// The input actions collection to processed during user input. + /// + public InputActionsContainer InputActions = new InputActionsContainer(); + /// /// The default text style. /// @@ -80,6 +85,14 @@ namespace FlaxEditor.Windows /// public TextBlockStyle ErrorStyle; + /// + public override bool OnKeyDown(KeyboardKeys key) + { + if (InputActions.Process(Editor.Instance, this, key)) + return true; + return base.OnKeyDown(key); + } + /// protected override void OnParseTextBlocks() { @@ -201,6 +214,9 @@ namespace FlaxEditor.Windows // Setup editor options Editor.Options.OptionsChanged += OnEditorOptionsChanged; OnEditorOptionsChanged(Editor.Options.Options); + + _output.InputActions.Add(options => options.Search, () => _searchBox.Focus()); + InputActions.Add(options => options.Search, () => _searchBox.Focus()); GameCooker.Event += OnGameCookerEvent; ScriptsBuilder.CompilationFailed += OnScriptsCompilationFailed; From bfbabbc39587338e1e5dc5f3181c80ad2ef2b3cd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 20:04:00 +0100 Subject: [PATCH 155/198] Refactor value editors to simplify code after many new features added --- .../CustomEditors/Editors/DoubleEditor.cs | 41 +++--------- .../CustomEditors/Editors/FloatEditor.cs | 63 +++++++------------ 2 files changed, 30 insertions(+), 74 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs index 477f68855..2049860c6 100644 --- a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; -using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -22,50 +21,28 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - _element = null; - - // Try get limit attribute for value min/max range setting and slider speed + var doubleValue = layout.DoubleValue(); + doubleValue.ValueBox.ValueChanged += OnValueChanged; + doubleValue.ValueBox.SlidingEnd += ClearToken; + _element = doubleValue; var attributes = Values.GetAttributes(); - var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); - var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { - var limit = attributes.FirstOrDefault(x => x is LimitAttribute); - if (limit != null) + var limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); + doubleValue.SetLimits(limit); + var valueCategory = ((ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute))?.Category ?? Utils.ValueCategory.None; + if (valueCategory != Utils.ValueCategory.None) { - // Use double value editor with limit - var doubleValue = layout.DoubleValue(); doubleValue.SetCategory(valueCategory); - doubleValue.SetLimits((LimitAttribute)limit); - doubleValue.ValueBox.ValueChanged += OnValueChanged; - doubleValue.ValueBox.SlidingEnd += ClearToken; - _element = doubleValue; LinkedLabel.SetupContextMenu += (label, menu, editor) => { menu.AddSeparator(); - var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); + var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None); }); mb.AutoCheck = true; mb.Checked = doubleValue.ValueBox.Category != Utils.ValueCategory.None; }; - return; } } - if (_element == null) - { - // Use double value editor - var doubleValue = layout.DoubleValue(); - doubleValue.SetCategory(valueCategory); - doubleValue.ValueBox.ValueChanged += OnValueChanged; - doubleValue.ValueBox.SlidingEnd += ClearToken; - LinkedLabel.SetupContextMenu += (label, menu, editor) => - { - menu.AddSeparator(); - var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); - mb.AutoCheck = true; - mb.Checked = doubleValue.ValueBox.Category != Utils.ValueCategory.None; - }; - _element = doubleValue; - } } private void OnValueChanged() diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index af6a95c4d..9d3ff1490 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -3,7 +3,6 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; -using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using Utils = FlaxEngine.Utils; @@ -29,60 +28,40 @@ namespace FlaxEditor.CustomEditors.Editors public override void Initialize(LayoutElementsContainer layout) { _element = null; - - // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); - var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); - var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; + var range = (RangeAttribute)attributes?.FirstOrDefault(x => x is RangeAttribute); + if (range != null) + { + // Use slider + var slider = layout.Slider(); + slider.Slider.SetLimits(range); + slider.Slider.ValueChanged += OnValueChanged; + slider.Slider.SlidingEnd += ClearToken; + _element = slider; + return; + } + + var floatValue = layout.FloatValue(); + floatValue.ValueBox.ValueChanged += OnValueChanged; + floatValue.ValueBox.SlidingEnd += ClearToken; + _element = floatValue; if (attributes != null) { - var range = attributes.FirstOrDefault(x => x is RangeAttribute); - if (range != null) + var limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); + floatValue.SetLimits(limit); + var valueCategory = ((ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute))?.Category ?? Utils.ValueCategory.None; + if (valueCategory != Utils.ValueCategory.None) { - // Use slider - var slider = layout.Slider(); - slider.SetLimits((RangeAttribute)range); - slider.Slider.ValueChanged += OnValueChanged; - slider.Slider.SlidingEnd += ClearToken; - _element = slider; - return; - } - var limit = attributes.FirstOrDefault(x => x is LimitAttribute); - if (limit != null) - { - // Use float value editor with limit - var floatValue = layout.FloatValue(); - floatValue.SetLimits((LimitAttribute)limit); floatValue.SetCategory(valueCategory); - floatValue.ValueBox.ValueChanged += OnValueChanged; - floatValue.ValueBox.SlidingEnd += ClearToken; - _element = floatValue; LinkedLabel.SetupContextMenu += (label, menu, editor) => { menu.AddSeparator(); - var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); + var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None); }); mb.AutoCheck = true; mb.Checked = floatValue.ValueBox.Category != Utils.ValueCategory.None; }; - return; } } - if (_element == null) - { - // Use float value editor - var floatValue = layout.FloatValue(); - floatValue.SetCategory(valueCategory); - floatValue.ValueBox.ValueChanged += OnValueChanged; - floatValue.ValueBox.SlidingEnd += ClearToken; - _element = floatValue; - LinkedLabel.SetupContextMenu += (label, menu, editor) => - { - menu.AddSeparator(); - var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None);}); - mb.AutoCheck = true; - mb.Checked = floatValue.ValueBox.Category != Utils.ValueCategory.None; - }; - } } private void OnValueChanged() From 9e54827cb3d1acab4b43c07f4f6b912635913e5a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 20:13:41 +0100 Subject: [PATCH 156/198] Cleanup code #2213 --- .../CustomEditors/Editors/Vector3Editor.cs | 1 - .../Elements/DoubleValueElement.cs | 5 +- .../Elements/FloatValueElement.cs | 5 +- Source/Editor/GUI/Input/DoubleValueBox.cs | 16 ++++- Source/Editor/GUI/Input/FloatValueBox.cs | 17 ++++-- Source/Editor/Options/InterfaceOptions.cs | 14 +++-- Source/Editor/Utilities/ShuntingYardParser.cs | 12 ++-- Source/Editor/Utilities/Units.cs | 11 +++- Source/Editor/Utilities/Utils.cs | 59 +++++++++---------- Source/Engine/Level/Actors/Camera.h | 6 +- Source/Engine/Physics/Actors/RigidBody.h | 2 +- Source/Engine/Physics/Colliders/BoxCollider.h | 2 +- .../Physics/Colliders/CapsuleCollider.h | 4 +- .../Physics/Colliders/CharacterController.h | 10 ++-- Source/Engine/Physics/Colliders/Collider.h | 4 +- .../Engine/Physics/Colliders/SphereCollider.h | 2 +- Source/Engine/Physics/Joints/DistanceJoint.h | 4 +- Source/Engine/Physics/Joints/Joint.h | 4 +- .../Editor/ValueCategoryAttribute.cs | 8 ++- Source/Engine/Utilities/Utils.cs | 48 ++++++++++++++- 20 files changed, 150 insertions(+), 84 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index 2850fa4f3..e7170a22d 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -4,7 +4,6 @@ using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; using FlaxEngine.GUI; -using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Editors { diff --git a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs index a8bfe42ba..cf16a1194 100644 --- a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs @@ -5,7 +5,6 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; -using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Elements { @@ -53,9 +52,9 @@ namespace FlaxEditor.CustomEditors.Elements } /// - /// Set the value category of this float element + /// Sets the editor value category. /// - /// + /// The category. public void SetCategory(Utils.ValueCategory category) { ValueBox.Category = category; diff --git a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs index a14c796b1..aabdb79e4 100644 --- a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs @@ -5,7 +5,6 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; -using Utils = FlaxEngine.Utils; namespace FlaxEditor.CustomEditors.Elements { @@ -53,9 +52,9 @@ namespace FlaxEditor.CustomEditors.Elements } /// - /// Set the value category of this float element + /// Sets the editor value category. /// - /// + /// The category. public void SetCategory(Utils.ValueCategory category) { ValueBox.Category = category; diff --git a/Source/Editor/GUI/Input/DoubleValueBox.cs b/Source/Editor/GUI/Input/DoubleValueBox.cs index ce1eab400..86acc1203 100644 --- a/Source/Editor/GUI/Input/DoubleValueBox.cs +++ b/Source/Editor/GUI/Input/DoubleValueBox.cs @@ -14,6 +14,8 @@ namespace FlaxEditor.GUI.Input [HideInEditor] public class DoubleValueBox : ValueBox { + private Utils.ValueCategory _category = Utils.ValueCategory.None; + /// public override double Value { @@ -131,9 +133,19 @@ namespace FlaxEditor.GUI.Input } /// - /// Get or set the category of the value. This can either be none for just a number, a distance or an angle. + /// Gets or sets the category of the value. This can be none for just a number or a more specific one like a distance. /// - public Utils.ValueCategory Category = Utils.ValueCategory.None; + public Utils.ValueCategory Category + { + get => _category; + set + { + if (_category == value) + return; + _category = value; + UpdateText(); + } + } /// protected sealed override void UpdateText() diff --git a/Source/Editor/GUI/Input/FloatValueBox.cs b/Source/Editor/GUI/Input/FloatValueBox.cs index 1b81df45f..f22ecd89c 100644 --- a/Source/Editor/GUI/Input/FloatValueBox.cs +++ b/Source/Editor/GUI/Input/FloatValueBox.cs @@ -14,6 +14,8 @@ namespace FlaxEditor.GUI.Input [HideInEditor] public class FloatValueBox : ValueBox { + private Utils.ValueCategory _category = Utils.ValueCategory.None; + /// public override float Value { @@ -137,14 +139,19 @@ namespace FlaxEditor.GUI.Input Value = Value; } - private Utils.ValueCategory _category = Utils.ValueCategory.None; - /// - /// Get or set the category of the value. This can be none for just a number or a more specific one like a distance. + /// Gets or sets the category of the value. This can be none for just a number or a more specific one like a distance. /// - public Utils.ValueCategory Category { + public Utils.ValueCategory Category + { get => _category; - set { _category = value; UpdateText(); } + set + { + if (_category == value) + return; + _category = value; + UpdateText(); + } } /// diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 8d8e63899..42f3a00d2 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -118,22 +118,24 @@ namespace FlaxEditor.Options } /// - /// Options for formatting numerical values + /// Options for formatting numerical values. /// public enum ValueFormattingType { /// - /// No formatting + /// No formatting. /// None, + /// - /// Format using the base SI unit + /// Format using the base SI unit. /// BaseUnit, + /// - /// Format using a unit that matches the value best + /// Format using a unit that matches the value best. /// - AutoUnit + AutoUnit, } /// @@ -214,7 +216,7 @@ namespace FlaxEditor.Options private bool _spaceNumberAndUnits = false; /// - /// Gets or sets the option to put a space between numbers and units for unit formatting + /// Gets or sets the option to put a space between numbers and units for unit formatting. /// [DefaultValue(false)] [EditorDisplay("Interface"), EditorOrder(310), Tooltip("Put a space between numbers and units.")] diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index 85279d0a4..5ba8e969c 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -141,19 +141,17 @@ namespace FlaxEditor.Utilities }; /// - /// List known units which cannot be handled as a variable easily because they contain operator - /// symbols (mostly a forward slash). The value is the factor to calculate game units. + /// List known units which cannot be handled as a variable easily because they contain operator symbols (mostly a forward slash). The value is the factor to calculate game units. /// - // Nm is here because these values are compared case-sensitive and we don't want to confuse - // nanometers and Newtonmeters private static readonly IDictionary UnitSymbols = new Dictionary { ["cm/s"] = Units.Meters2Units / 100, ["cm/s²"] = Units.Meters2Units / 100, ["m/s"] = Units.Meters2Units, ["m/s²"] = Units.Meters2Units, - ["km/h"] = 1/3.6 * Units.Meters2Units, - ["Nm"] = Units.Meters2Units * Units.Meters2Units + ["km/h"] = 1 / 3.6 * Units.Meters2Units, + // Nm is here because these values are compared case-sensitive, and we don't want to confuse nanometers and Newtonmeters + ["Nm"] = Units.Meters2Units * Units.Meters2Units, }; /// @@ -189,7 +187,7 @@ namespace FlaxEditor.Utilities if (Operators.ContainsKey(str)) return TokenType.Operator; - if (char.IsLetter(c) || c=='²' || c=='³') + if (char.IsLetter(c) || c == '²' || c == '³') return TokenType.Variable; throw new ParsingException("wrong character"); diff --git a/Source/Editor/Utilities/Units.cs b/Source/Editor/Utilities/Units.cs index cd4e67882..4c4131337 100644 --- a/Source/Editor/Utilities/Units.cs +++ b/Source/Editor/Utilities/Units.cs @@ -1,5 +1,10 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + namespace FlaxEditor.Utilities; +/// +/// Units display utilities for Editor. +/// public class Units { /// @@ -8,7 +13,7 @@ public class Units public static readonly float Meters2Units = 100f; /// - /// Set it to false to always show game units without any postfix. + /// False to always show game units without any postfix. /// public static bool UseUnitsFormatting = true; @@ -25,8 +30,8 @@ public class Units /// /// Return the unit according to user settings. /// - /// - /// + /// The unit name. + /// The formatted text. public static string Unit(string unit) { if (SpaceNumberAndUnits) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 39e371b28..1b76de47e 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1248,38 +1248,37 @@ namespace FlaxEditor.Utilities { switch (category) { - case FlaxEngine.Utils.ValueCategory.Distance: - if (!Units.AutomaticUnitsFormatting) - return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m"); - var absValue = Mathf.Abs(value); - // in case a unit != cm this would be (value / Meters2Units * 100) - if (absValue < Units.Meters2Units) - return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("cm"); - if (absValue < Units.Meters2Units * 1000) - return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m"); - return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("km"); - case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; - case FlaxEngine.Utils.ValueCategory.Time: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("s"); - // some fonts have a symbol for that: "\u33A7" - case FlaxEngine.Utils.ValueCategory.Speed: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s"); - case FlaxEngine.Utils.ValueCategory.Acceleration: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s²"); - case FlaxEngine.Utils.ValueCategory.Area: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m²"); - case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m³"); - case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("kg"); - case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("N"); - case FlaxEngine.Utils.ValueCategory.Torque: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("Nm"); - case FlaxEngine.Utils.ValueCategory.None: - default: - return FormatFloat(value); + case FlaxEngine.Utils.ValueCategory.Distance: + if (!Units.AutomaticUnitsFormatting) + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m"); + var absValue = Mathf.Abs(value); + // in case a unit != cm this would be (value / Meters2Units * 100) + if (absValue < Units.Meters2Units) + return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("cm"); + if (absValue < Units.Meters2Units * 1000) + return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m"); + return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("km"); + case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°"; + case FlaxEngine.Utils.ValueCategory.Time: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("s"); + // some fonts have a symbol for that: "\u33A7" + case FlaxEngine.Utils.ValueCategory.Speed: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s"); + case FlaxEngine.Utils.ValueCategory.Acceleration: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s²"); + case FlaxEngine.Utils.ValueCategory.Area: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m²"); + case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m³"); + case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("kg"); + case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("N"); + case FlaxEngine.Utils.ValueCategory.Torque: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("Nm"); + case FlaxEngine.Utils.ValueCategory.None: + default: return FormatFloat(value); } } /// - /// Format a float value either as-is, with a distance unit or with a degree sign + /// Format a float value either as-is, with a distance unit or with a degree sign. /// - /// the value to format - /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign - /// the formatted string + /// The value to format. + /// The value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign. + /// The formatted string. public static string FormatFloat(float value, FlaxEngine.Utils.ValueCategory category) { if (float.IsPositiveInfinity(value) || value == float.MaxValue) @@ -1295,9 +1294,9 @@ namespace FlaxEditor.Utilities /// /// Format a double value either as-is, with a distance unit or with a degree sign /// - /// the value to format - /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign - /// the formatted string + /// The value to format. + /// The value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign. + /// The formatted string. public static string FormatFloat(double value, FlaxEngine.Utils.ValueCategory category) { if (double.IsPositiveInfinity(value) || value == double.MaxValue) diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h index e7c7971b7..190126e54 100644 --- a/Source/Engine/Level/Actors/Camera.h +++ b/Source/Engine/Level/Actors/Camera.h @@ -77,7 +77,7 @@ public: /// /// Gets the camera's field of view (in degrees). /// - API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(60.0f), Limit(0, 179), EditorDisplay(\"Camera\", \"Field Of View\"), VisibleIf(nameof(UsePerspective))") + API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(60.0f), Limit(0, 179), EditorDisplay(\"Camera\", \"Field Of View\"), VisibleIf(nameof(UsePerspective)), ValueCategory(Utils.ValueCategory.Angle)") float GetFieldOfView() const; /// @@ -99,7 +99,7 @@ public: /// /// Gets camera's near plane distance. /// - API_PROPERTY(Attributes="EditorOrder(30), DefaultValue(10.0f), Limit(0, 1000, 0.05f), EditorDisplay(\"Camera\")") + API_PROPERTY(Attributes="EditorOrder(30), DefaultValue(10.0f), Limit(0, 1000, 0.05f), EditorDisplay(\"Camera\"), ValueCategory(Utils.ValueCategory.Distance)") float GetNearPlane() const; /// @@ -110,7 +110,7 @@ public: /// /// Gets camera's far plane distance. /// - API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(40000.0f), Limit(0, float.MaxValue, 5), EditorDisplay(\"Camera\")") + API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(40000.0f), Limit(0, float.MaxValue, 5), EditorDisplay(\"Camera\"), ValueCategory(Utils.ValueCategory.Distance)") float GetFarPlane() const; /// diff --git a/Source/Engine/Physics/Actors/RigidBody.h b/Source/Engine/Physics/Actors/RigidBody.h index e23c4dde8..c862a8e97 100644 --- a/Source/Engine/Physics/Actors/RigidBody.h +++ b/Source/Engine/Physics/Actors/RigidBody.h @@ -181,7 +181,7 @@ public: /// /// Gets the mass value measured in kilograms (use override value only if OverrideMass is checked). /// - API_PROPERTY(Attributes="EditorOrder(110), Limit(0), EditorDisplay(\"Rigid Body\"), NumberCategory(Utils.ValueCategory.Mass)") + API_PROPERTY(Attributes="EditorOrder(110), Limit(0), EditorDisplay(\"Rigid Body\"), ValueCategory(Utils.ValueCategory.Mass)") float GetMass() const; /// diff --git a/Source/Engine/Physics/Colliders/BoxCollider.h b/Source/Engine/Physics/Colliders/BoxCollider.h index e875217a4..58298ada8 100644 --- a/Source/Engine/Physics/Colliders/BoxCollider.h +++ b/Source/Engine/Physics/Colliders/BoxCollider.h @@ -23,7 +23,7 @@ public: /// Gets the size of the box, measured in the object's local space. /// /// The box size will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(typeof(Float3), \"100,100,100\"), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(typeof(Float3), \"100,100,100\"), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE Float3 GetSize() const { return _size; diff --git a/Source/Engine/Physics/Colliders/CapsuleCollider.h b/Source/Engine/Physics/Colliders/CapsuleCollider.h index f7870e29c..c225ac2a5 100644 --- a/Source/Engine/Physics/Colliders/CapsuleCollider.h +++ b/Source/Engine/Physics/Colliders/CapsuleCollider.h @@ -25,7 +25,7 @@ public: /// Gets the radius of the sphere, measured in the object's local space. /// /// The sphere radius will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(20.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(20.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetRadius() const { return _radius; @@ -41,7 +41,7 @@ public: /// Gets the height of the capsule, measured in the object's local space between the centers of the hemispherical ends. /// /// The capsule height will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(100.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(100.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetHeight() const { return _height; diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h index 829d1617d..545f4e9d9 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.h +++ b/Source/Engine/Physics/Colliders/CharacterController.h @@ -73,7 +73,7 @@ public: /// /// Gets the radius of the sphere, measured in the object's local space. The sphere radius will be scaled by the actor's world scale. /// - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") float GetRadius() const; /// @@ -84,7 +84,7 @@ public: /// /// Gets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale. /// - API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") float GetHeight() const; /// @@ -95,7 +95,7 @@ public: /// /// Gets the slope limit (in degrees). Limits the collider to only climb slopes that are less steep (in degrees) than the indicated value. /// - API_PROPERTY(Attributes="EditorOrder(210), DefaultValue(45.0f), Limit(0, 100), EditorDisplay(\"Character Controller\"), NumberCategory(Utils.ValueCategory.Angle)") + API_PROPERTY(Attributes="EditorOrder(210), DefaultValue(45.0f), Limit(0, 100), EditorDisplay(\"Character Controller\"), ValueCategory(Utils.ValueCategory.Angle)") float GetSlopeLimit() const; /// @@ -117,7 +117,7 @@ public: /// /// Gets the step height. The character will step up a stair only if it is closer to the ground than the indicated value. This should not be greater than the Character Controller’s height or it will generate an error. /// - API_PROPERTY(Attributes="EditorOrder(220), DefaultValue(30.0f), Limit(0), EditorDisplay(\"Character Controller\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(220), DefaultValue(30.0f), Limit(0), EditorDisplay(\"Character Controller\"), ValueCategory(Utils.ValueCategory.Distance)") float GetStepOffset() const; /// @@ -139,7 +139,7 @@ public: /// /// Gets the minimum move distance of the character controller. The minimum traveled distance to consider. If traveled distance is smaller, the character doesn't move. This is used to stop the recursive motion algorithm when remaining distance to travel is small. /// - API_PROPERTY(Attributes="EditorOrder(230), DefaultValue(0.0f), Limit(0, 1000), EditorDisplay(\"Character Controller\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(230), DefaultValue(0.0f), Limit(0, 1000), EditorDisplay(\"Character Controller\"), ValueCategory(Utils.ValueCategory.Distance)") float GetMinMoveDistance() const; /// diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h index 36db78119..05f2a6eb2 100644 --- a/Source/Engine/Physics/Colliders/Collider.h +++ b/Source/Engine/Physics/Colliders/Collider.h @@ -52,7 +52,7 @@ public: /// /// Gets the center of the collider, measured in the object's local space. /// - API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE Vector3 GetCenter() const { return _center; @@ -66,7 +66,7 @@ public: /// /// Gets the contact offset. Colliders whose distance is less than the sum of their ContactOffset values will generate contacts. The contact offset must be positive. Contact offset allows the collision detection system to predictively enforce the contact constraint even when the objects are slightly separated. /// - API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(2.0f), Limit(0, 100), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(2.0f), Limit(0, 100), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetContactOffset() const { return _contactOffset; diff --git a/Source/Engine/Physics/Colliders/SphereCollider.h b/Source/Engine/Physics/Colliders/SphereCollider.h index 5c97bc553..e3b295eda 100644 --- a/Source/Engine/Physics/Colliders/SphereCollider.h +++ b/Source/Engine/Physics/Colliders/SphereCollider.h @@ -21,7 +21,7 @@ public: /// Gets the radius of the sphere, measured in the object's local space. /// /// The sphere radius will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetRadius() const { return _radius; diff --git a/Source/Engine/Physics/Joints/DistanceJoint.h b/Source/Engine/Physics/Joints/DistanceJoint.h index f4a60cb62..0f0f59b01 100644 --- a/Source/Engine/Physics/Joints/DistanceJoint.h +++ b/Source/Engine/Physics/Joints/DistanceJoint.h @@ -67,7 +67,7 @@ public: /// Gets the allowed minimum distance for the joint. /// /// Used only when DistanceJointFlag.MinDistance flag is set. The minimum distance must be no more than the maximum distance. Default: 0, Range: [0, float.MaxValue]. - API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(0.0f), Limit(0.0f), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(0.0f), Limit(0.0f), EditorDisplay(\"Joint\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetMinDistance() const { return _minDistance; @@ -83,7 +83,7 @@ public: /// Gets the allowed maximum distance for the joint. /// /// Used only when DistanceJointFlag.MaxDistance flag is set. The maximum distance must be no less than the minimum distance. Default: 0, Range: [0, float.MaxValue]. - API_PROPERTY(Attributes="EditorOrder(120), DefaultValue(10.0f), Limit(0.0f), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Distance)") + API_PROPERTY(Attributes="EditorOrder(120), DefaultValue(10.0f), Limit(0.0f), EditorDisplay(\"Joint\"), ValueCategory(Utils.ValueCategory.Distance)") FORCE_INLINE float GetMaxDistance() const { return _maxDistance; diff --git a/Source/Engine/Physics/Joints/Joint.h b/Source/Engine/Physics/Joints/Joint.h index 408d19ae5..a4584a07e 100644 --- a/Source/Engine/Physics/Joints/Joint.h +++ b/Source/Engine/Physics/Joints/Joint.h @@ -38,7 +38,7 @@ public: /// /// Gets the break force. Determines the maximum force the joint can apply before breaking. Broken joints no longer participate in physics simulation. /// - API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Force)") + API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), ValueCategory(Utils.ValueCategory.Force)") FORCE_INLINE float GetBreakForce() const { return _breakForce; @@ -52,7 +52,7 @@ public: /// /// Gets the break torque. Determines the maximum torque the joint can apply before breaking. Broken joints no longer participate in physics simulation. /// - API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Torque)") + API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), ValueCategory(Utils.ValueCategory.Torque)") FORCE_INLINE float GetBreakTorque() const { return _breakTorque; diff --git a/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs index 8ba91e9d0..4f51efbe3 100644 --- a/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs @@ -1,13 +1,12 @@ -// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; namespace FlaxEngine { /// - /// Used to specify the value category of a numeric value as either as-is (a scalar), a distance (formatted as cm/m/km) or an angle (formatted with a degree sign). + /// Specifies the value category of a numeric value as either as-is (a scalar), a distance (formatted as cm/m/km) or an angle (formatted with a degree sign). /// - /// [Serializable] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class ValueCategoryAttribute : Attribute @@ -17,6 +16,9 @@ namespace FlaxEngine /// public Utils.ValueCategory Category; + /// + /// Initializes a new instance of the class. + /// private ValueCategoryAttribute() { Category = Utils.ValueCategory.None; diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs index 50e2a7d50..0767ab8f4 100644 --- a/Source/Engine/Utilities/Utils.cs +++ b/Source/Engine/Utilities/Utils.cs @@ -257,6 +257,7 @@ namespace FlaxEngine { public static FieldInfo itemsField; } + internal static T[] ExtractArrayFromList(List list) { if (list == null) @@ -1040,21 +1041,64 @@ namespace FlaxEngine } /// - /// A category of number values used for formatting and input boxes + /// A category of number values used for formatting and input fields. /// public enum ValueCategory { + /// + /// Nothing. + /// None, + + /// + /// Distance (eg. meters). + /// Distance, + + /// + /// Area (eg. m^2). + /// Area, + + /// + /// Volume (eg. m^3). + /// Volume, + + /// + /// Mass (eg. kilograms). + /// Mass, + + /// + /// Angle (eg. degrees). + /// Angle, + + /// + /// Speed (distance / time). + /// Speed, + + /// + /// Acceleration (distance^2 / time). + /// Acceleration, + + /// + /// Time (eg. seconds). + /// Time, + + /// + /// Force (mass * distance / time^2). + /// Force, - Torque + + /// + /// Torque (mass * distance^2 / time^2). + /// + Torque, } } } From fc37cb3986f5c8aa4e7db9b2b1ee66cc93975434 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 20:14:07 +0100 Subject: [PATCH 157/198] Update values formatting on editor options save #2213 --- Source/Editor/Options/InterfaceOptions.cs | 26 ++++------------------- Source/Editor/Options/OptionsModule.cs | 21 ++++++++++++++++++ Source/Editor/Utilities/Units.cs | 4 ++-- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 42f3a00d2..4cf1fe050 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -196,37 +196,19 @@ namespace FlaxEditor.Options [EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")] public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal; - private ValueFormattingType _valueFormatting = ValueFormattingType.None; - /// /// Gets or sets the formatting option for numeric values in the editor. /// [DefaultValue(ValueFormattingType.None)] - [EditorDisplay("Interface"), EditorOrder(300), Tooltip("Formatting of numeric values.")] - public ValueFormattingType ValueFormatting { - get => _valueFormatting; - set - { - _valueFormatting = value; - Units.UseUnitsFormatting = _valueFormatting != ValueFormattingType.None; - Units.AutomaticUnitsFormatting = _valueFormatting == ValueFormattingType.AutoUnit; - } - } - - private bool _spaceNumberAndUnits = false; + [EditorDisplay("Interface"), EditorOrder(300)] + public ValueFormattingType ValueFormatting { get; set; } /// /// Gets or sets the option to put a space between numbers and units for unit formatting. /// [DefaultValue(false)] - [EditorDisplay("Interface"), EditorOrder(310), Tooltip("Put a space between numbers and units.")] - public bool SpaceNumberAndUnits { get => _spaceNumberAndUnits; - set - { - _spaceNumberAndUnits = value; - Units.SpaceNumberAndUnits = _spaceNumberAndUnits; - } - } + [EditorDisplay("Interface"), EditorOrder(310)] + public bool SeparateValueAndUnit { get; set; } /// /// Gets or sets the timestamps prefix mode for output log messages. diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index 2dceb3400..debc0a663 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -200,6 +200,27 @@ namespace FlaxEditor.Options EditorAssets.Cache.OnEditorOptionsChanged(Options); + // Units formatting options + bool useUnitsFormatting = Options.Interface.ValueFormatting != InterfaceOptions.ValueFormattingType.None; + bool automaticUnitsFormatting = Options.Interface.ValueFormatting == InterfaceOptions.ValueFormattingType.AutoUnit; + bool separateValueAndUnit = Options.Interface.SeparateValueAndUnit; + if (useUnitsFormatting != Utilities.Units.UseUnitsFormatting || + automaticUnitsFormatting != Utilities.Units.AutomaticUnitsFormatting || + separateValueAndUnit != Utilities.Units.SeparateValueAndUnit) + { + Utilities.Units.UseUnitsFormatting = useUnitsFormatting; + Utilities.Units.AutomaticUnitsFormatting = automaticUnitsFormatting; + Utilities.Units.SeparateValueAndUnit = separateValueAndUnit; + + // Refresh UI in property panels + Editor.Windows.PropertiesWin?.Presenter.BuildLayoutOnUpdate(); + foreach (var window in Editor.Windows.Windows) + { + if (window is Windows.Assets.PrefabWindow prefabWindow) + prefabWindow.Presenter.BuildLayoutOnUpdate(); + } + } + // Send event OptionsChanged?.Invoke(Options); } diff --git a/Source/Editor/Utilities/Units.cs b/Source/Editor/Utilities/Units.cs index 4c4131337..64977c18b 100644 --- a/Source/Editor/Utilities/Units.cs +++ b/Source/Editor/Utilities/Units.cs @@ -20,7 +20,7 @@ public class Units /// /// Add a space between numbers and units for readability. /// - public static bool SpaceNumberAndUnits = true; + public static bool SeparateValueAndUnit = true; /// /// If set to true, the distance unit is chosen on the magnitude, otherwise it's meters. @@ -34,7 +34,7 @@ public class Units /// The formatted text. public static string Unit(string unit) { - if (SpaceNumberAndUnits) + if (SeparateValueAndUnit) return $" {unit}"; return unit; } From 292694d89c40e6ea9ac55aa01f4e04ad2649c76d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 20:15:56 +0100 Subject: [PATCH 158/198] Fix selecting `CanvasContainer` in UI Editor queries --- Source/Editor/Gizmo/UIEditorGizmo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index ea4b75d28..43461b0a6 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -689,7 +689,7 @@ namespace FlaxEditor #else // Find any control under mouse (hierarchical) hit = GetChildAtRecursive(location); - if (hit is View) + if (hit is View || hit is CanvasContainer) hit = null; return hit != null; #endif From d6f0062198ceabb21cd2000e78a269c806e75f37 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 21:47:00 +0100 Subject: [PATCH 159/198] Fix deselecting error --- Source/Editor/Modules/SceneEditingModule.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs index df9009cc8..13a920fa3 100644 --- a/Source/Editor/Modules/SceneEditingModule.cs +++ b/Source/Editor/Modules/SceneEditingModule.cs @@ -101,7 +101,10 @@ namespace FlaxEditor.Modules public void Select(List selection, bool additive = false) { if (selection == null) - throw new ArgumentNullException(); + { + Deselect(); + return; + } // Prevent from selecting null nodes selection.RemoveAll(x => x == null); From 9e11d45c1cb2fa6faae02c989a4ca10fa004b380 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Mar 2024 21:52:46 +0100 Subject: [PATCH 160/198] Update copyright year --- Source/Engine/Audio/AudioBackendTools.h | 2 +- Source/Engine/Core/Types/Stopwatch.h | 2 +- Source/Engine/Tools/TextureTool/TextureTool.astc.cpp | 2 +- Source/ThirdParty/astc/astc.Build.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Audio/AudioBackendTools.h b/Source/Engine/Audio/AudioBackendTools.h index 0ccb1085d..2630dca6f 100644 --- a/Source/Engine/Audio/AudioBackendTools.h +++ b/Source/Engine/Audio/AudioBackendTools.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #pragma once diff --git a/Source/Engine/Core/Types/Stopwatch.h b/Source/Engine/Core/Types/Stopwatch.h index 675f826a4..facf19181 100644 --- a/Source/Engine/Core/Types/Stopwatch.h +++ b/Source/Engine/Core/Types/Stopwatch.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #pragma once diff --git a/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp index 725c79152..32fd9ecf1 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.astc.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #if COMPILE_WITH_TEXTURE_TOOL && COMPILE_WITH_ASTC diff --git a/Source/ThirdParty/astc/astc.Build.cs b/Source/ThirdParty/astc/astc.Build.cs index 90494402b..4f3ec7d22 100644 --- a/Source/ThirdParty/astc/astc.Build.cs +++ b/Source/ThirdParty/astc/astc.Build.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using Flax.Build; using Flax.Build.NativeCpp; From 70b2e4aa5a69f67812853a03a599c9e780ace06a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Mar 2024 12:13:09 +0100 Subject: [PATCH 161/198] Fix snapping to the ground actor when viewport is not focused #2342 --- Source/Editor/Viewport/EditorGizmoViewport.cs | 4 ++-- Source/Editor/Viewport/PrefabWindowViewport.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Viewport/EditorGizmoViewport.cs b/Source/Editor/Viewport/EditorGizmoViewport.cs index 856ae20da..9fb3d962e 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -70,10 +70,10 @@ namespace FlaxEditor.Viewport public bool IsControlDown => _input.IsControlDown; /// - public bool SnapToGround => Editor.Instance.Options.Options.Input.SnapToGround.Process(Root); + public bool SnapToGround => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToGround.Process(Root); /// - public bool SnapToVertex => Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root); + public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root); /// public Float2 MouseDelta => _mouseDelta * 1000; diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 081c1d4f1..32473c5ea 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -288,7 +288,7 @@ namespace FlaxEditor.Viewport public bool SnapToGround => false; /// - public bool SnapToVertex => Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root); + public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root); /// public Float2 MouseDelta => _mouseDelta * 1000; From 3939444e05e43c4508f6d5cb4774bd0901e321c7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Mar 2024 12:24:35 +0100 Subject: [PATCH 162/198] Fix potential issues with shader resources bindings when using custom post processing effect --- Source/Engine/Renderer/RenderList.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index 01da9e2ea..577d58255 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -266,6 +266,8 @@ void RenderList::RunPostFxPass(GPUContext* context, RenderContext& renderContext { if (fx->Location == locationB) { + context->ResetSR(); + context->ResetUA(); if (fx->UseSingleTarget || output == nullptr) { fx->Render(context, renderContext, input, nullptr); From 6ccec2f5ff1c1e16b6e92ae49e8c84328fc7b969 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Mar 2024 16:48:15 +0100 Subject: [PATCH 163/198] Fix d3d12 warning on initial gpu buffer state --- Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp index 0fd25001b..c38b3edef 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp @@ -135,7 +135,7 @@ bool GPUBufferDX12::OnInit() // Create resource ID3D12Resource* resource; - D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COPY_DEST; + D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON; VALIDATE_DIRECTX_CALL(_device->GetDevice()->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, initialState, nullptr, IID_PPV_ARGS(&resource))); // Set state From 8f63f686c405a9f8b79f747fad181a5305018c64 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Mar 2024 17:28:43 +0100 Subject: [PATCH 164/198] Fix timeline playback control buttons visibility during seeking-only mode --- Source/Editor/GUI/Timeline/Timeline.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Editor/GUI/Timeline/Timeline.cs b/Source/Editor/GUI/Timeline/Timeline.cs index f86730b99..81fe950b9 100644 --- a/Source/Editor/GUI/Timeline/Timeline.cs +++ b/Source/Editor/GUI/Timeline/Timeline.cs @@ -1141,17 +1141,19 @@ namespace FlaxEditor.GUI.Timeline { foreach (var e in _playbackNavigation) { - e.Enabled = false; - e.Visible = false; + e.Enabled = true; + e.Visible = true; } } if (_playbackStop != null) { - _playbackStop.Visible = false; + _playbackStop.Visible = true; + _playbackStop.Enabled = false; } if (_playbackPlay != null) { - _playbackPlay.Visible = false; + _playbackPlay.Visible = true; + _playbackPlay.Enabled = false; } if (_positionHandle != null) { From a3c088bb05474be1d5927e32878580dfee648520 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Mar 2024 17:41:25 +0100 Subject: [PATCH 165/198] Fix Bone Socket transform when Animated Model pose is not yet evaluated #2341 --- Source/Engine/Level/Actors/BoneSocket.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Level/Actors/BoneSocket.cpp b/Source/Engine/Level/Actors/BoneSocket.cpp index c5743de22..a4f338b2f 100644 --- a/Source/Engine/Level/Actors/BoneSocket.cpp +++ b/Source/Engine/Level/Actors/BoneSocket.cpp @@ -45,14 +45,14 @@ void BoneSocket::UpdateTransformation() } auto& nodes = parent->GraphInstance.NodesPose; - if (nodes.HasItems() && nodes.Count() > _index) - { - Transform t; - nodes[_index].Decompose(t); - if (!_useScale) - t.Scale = _localTransform.Scale; - SetLocalTransform(t); - } + Transform t; + if (nodes.IsValidIndex(_index)) + nodes.Get()[_index].Decompose(t); + else + t = parent->SkinnedModel->Skeleton.GetNodeTransform(_index); + if (!_useScale) + t.Scale = _localTransform.Scale; + SetLocalTransform(t); } } From 8398b35b0dc75e69441baefcc5e40c386c962f45 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 20 Mar 2024 12:22:01 -0500 Subject: [PATCH 166/198] Add visibility check to begin play on uicontrol --- Source/Engine/UI/UIControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/UIControl.cs b/Source/Engine/UI/UIControl.cs index b2c6a4cce..72e3f9625 100644 --- a/Source/Engine/UI/UIControl.cs +++ b/Source/Engine/UI/UIControl.cs @@ -376,7 +376,7 @@ namespace FlaxEngine { if (_control != null) { - _control.Visible = IsActive; + _control.Visible = IsActive && _control.Visible; _control.Parent = GetParent(); _control.IndexInParent = OrderInParent; Internal_GetNavTargets(__unmanagedPtr, out UIControl up, out UIControl down, out UIControl left, out UIControl right); From 9251b80b785395d803b2465e4592d932f917faee Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 21 Mar 2024 18:07:21 +0100 Subject: [PATCH 167/198] Update to build for Xbox --- Source/Tools/Flax.Build/Build/Platform.cs | 2 +- .../Flax.Build/Deps/Dependencies/nethost.cs | 65 ++++++++++++++----- .../Flax.Build/Platforms/GDK/GDKPlatform.cs | 6 +- .../Flax.Build/Platforms/GDK/GDKToolchain.cs | 8 +-- .../Platforms/Windows/WindowsPlatform.cs | 4 +- 5 files changed, 58 insertions(+), 27 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/Platform.cs b/Source/Tools/Flax.Build/Build/Platform.cs index cceed9958..5fe01a93c 100644 --- a/Source/Tools/Flax.Build/Build/Platform.cs +++ b/Source/Tools/Flax.Build/Build/Platform.cs @@ -211,7 +211,7 @@ namespace Flax.Build /// /// The target platform. /// True if return null platform if it's missing, otherwise will invoke an exception. - /// The toolchain. + /// The platform. public static Platform GetPlatform(TargetPlatform targetPlatform, bool nullIfMissing = false) { if (_platforms == null) diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs index f9926135e..090edf4be 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs @@ -30,6 +30,8 @@ namespace Flax.Deps.Dependencies TargetPlatform.PS4, TargetPlatform.PS5, TargetPlatform.Switch, + TargetPlatform.XboxOne, + TargetPlatform.XboxScarlett, }; case TargetPlatform.Linux: return new[] @@ -85,6 +87,13 @@ namespace Flax.Deps.Dependencies os = "windows"; runtimeFlavor = "CoreCLR"; break; + case TargetPlatform.XboxOne: + case TargetPlatform.XboxScarlett: + os = "windows"; + runtimeFlavor = "Mono"; + buildMonoAotCross = true; + buildArgs = $" -subset mono+libs -cmakeargs \"-DDISABLE_JIT=1-DENABLE_PERFTRACING=0-DDISABLE_REFLECTION_EMIT=1-DDISABLE_EVENTPIPE=1-DDISABLE_COM=1-DDISABLE_PROFILER=1-DDISABLE_COMPONENTS=1\" /p:FeaturePerfTracing=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false"; + break; case TargetPlatform.Linux: os = "linux"; runtimeFlavor = "CoreCLR"; @@ -225,22 +234,42 @@ namespace Flax.Deps.Dependencies if (!Directory.Exists(src1)) throw new DirectoryNotFoundException(src1); var src2 = Path.Combine(artifacts, "bin", "native", $"{framework}-{os}-{configuration}-{arch}"); - if (!Directory.Exists(src1)) + if (!Directory.Exists(src2)) throw new DirectoryNotFoundException(src2); - foreach (var file in new[] - { - "libmonosgen-2.0.a", - "libmono-profiler-aot.a", - }) - Utilities.FileCopy(Path.Combine(src1, "lib", file), Path.Combine(dstBinaries, file)); - foreach (var file in new[] - { - "libSystem.Globalization.Native.a", - "libSystem.IO.Compression.Native.a", - "libSystem.IO.Ports.Native.a", - "libSystem.Native.a", - }) - Utilities.FileCopy(Path.Combine(src2, "lib", file), Path.Combine(dstBinaries, file)); + string[] libs1, libs2; + switch (targetPlatform) + { + case TargetPlatform.Windows: + case TargetPlatform.XboxOne: + case TargetPlatform.XboxScarlett: + libs1 = new[] + { + "lib/coreclr.dll", + "lib/coreclr.import.lib", + }; + libs2 = new string[] + { + }; + break; + default: + libs1 = new[] + { + "lib/libmonosgen-2.0.a", + "lib/libmono-profiler-aot.a", + }; + libs2 = new[] + { + "lib/libSystem.Globalization.Native.a", + "lib/libSystem.IO.Compression.Native.a", + "lib/libSystem.IO.Ports.Native.a", + "lib/libSystem.Native.a", + }; + break; + } + foreach (var file in libs1) + Utilities.FileCopy(Path.Combine(src1, file), Path.Combine(dstBinaries, Path.GetFileName(file))); + foreach (var file in libs2) + Utilities.FileCopy(Path.Combine(src2, file), Path.Combine(dstBinaries, Path.GetFileName(file))); // Include headers Utilities.DirectoryDelete(Path.Combine(dstBinaries, "include")); @@ -249,11 +278,13 @@ namespace Flax.Deps.Dependencies if (buildMonoAotCross) { // AOT compiler - Utilities.FileCopy(Path.Combine(artifacts, "bin", "mono", $"{os}.x64.{configuration}", "cross", $"{os}-x64", "mono-aot-cross.exe"), Path.Combine(dstPlatform, "Binaries", "Tools", "mono-aot-cross.exe")); + Utilities.FileCopy(Path.Combine(artifacts, "bin", "mono", $"{os}.x64.{configuration}", "cross", $"{(os == "windows" ? "win" : os)}-x64", "mono-aot-cross.exe"), Path.Combine(dstPlatform, "Binaries", "Tools", "mono-aot-cross.exe")); } // Class library var dstDotnetLib = Path.Combine(dstPlatform, "Dotnet", "lib", framework); + foreach (var subDir in Directory.GetDirectories(Path.Combine(dstPlatform, "Dotnet", "lib"))) + Utilities.DirectoryDelete(subDir); SetupDirectory(dstDotnetLib, true); Utilities.FileCopy(Path.Combine(artifacts, "bin", "mono", $"{os}.{arch}.{configuration}", privateCoreLib), Path.Combine(dstDotnetLib, privateCoreLib)); Utilities.DirectoryCopy(Path.Combine(artifacts, "bin", "runtime", $"{framework}-{os}-{configuration}-{arch}"), dstDotnetLib, false, true, "*.dll"); @@ -300,6 +331,8 @@ namespace Flax.Deps.Dependencies { case TargetPlatform.PS4: case TargetPlatform.PS5: + case TargetPlatform.XboxOne: + case TargetPlatform.XboxScarlett: Build(options, platform, TargetArchitecture.x64); break; case TargetPlatform.Android: diff --git a/Source/Tools/Flax.Build/Platforms/GDK/GDKPlatform.cs b/Source/Tools/Flax.Build/Platforms/GDK/GDKPlatform.cs index 3a2cfeba7..62d22911a 100644 --- a/Source/Tools/Flax.Build/Platforms/GDK/GDKPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/GDK/GDKPlatform.cs @@ -18,13 +18,13 @@ namespace Flax.Build.Platforms protected GDKPlatform() { // Visual Studio 2017 or newer required - var visualStudio = VisualStudioInstance.GetInstances().FirstOrDefault(x => x.Version == VisualStudioVersion.VisualStudio2017 || x.Version == VisualStudioVersion.VisualStudio2019); + var visualStudio = VisualStudioInstance.GetInstances().FirstOrDefault(x => x.Version >= VisualStudioVersion.VisualStudio2017); if (visualStudio == null) _hasRequiredSDKsInstalled = false; // Windows 10.0.19041.0 SDK or newer required var sdks = GetSDKs(); - if (!sdks.ContainsKey(WindowsPlatformSDK.v10_0_19041_0)) + if (sdks.All(x => x.Key < WindowsPlatformSDK.v10_0_19041_0)) _hasRequiredSDKsInstalled = false; // v141 toolset or newer required @@ -33,9 +33,7 @@ namespace Flax.Build.Platforms !toolsets.ContainsKey(WindowsPlatformToolset.v142) && !toolsets.ContainsKey(WindowsPlatformToolset.v143) && !toolsets.ContainsKey(WindowsPlatformToolset.v144)) - { _hasRequiredSDKsInstalled = false; - } } } } diff --git a/Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs b/Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs index 79fdf5f04..9da10ccc0 100644 --- a/Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs @@ -2,7 +2,6 @@ using System; using System.IO; -using System.Linq; using Flax.Build.NativeCpp; namespace Flax.Build.Platforms @@ -19,8 +18,9 @@ namespace Flax.Build.Platforms /// /// The platform. /// The architecture. - protected GDKToolchain(GDKPlatform platform, TargetArchitecture architecture) - : base(platform, architecture, WindowsPlatformBase.GetToolsets().Keys.Where(x => x <= WindowsPlatformToolset.v142).Max(), WindowsPlatformSDK.v10_0_19041_0) + /// The Windows toolset to use. + protected GDKToolchain(GDKPlatform platform, TargetArchitecture architecture, WindowsPlatformToolset toolset = WindowsPlatformToolset.Latest) + : base(platform, architecture, toolset, WindowsPlatformSDK.Latest) { // Setup system paths SystemIncludePaths.Add(Path.Combine(GDK.Instance.RootPath, "GRDK\\GameKit\\Include")); @@ -42,7 +42,7 @@ namespace Flax.Build.Platforms options.LinkEnv.InputLibraries.Add("xgameruntime.lib"); options.LinkEnv.InputLibraries.Add("xgameplatform.lib"); - options.LinkEnv.InputLibraries.Add("Microsoft.Xbox.Services.142.GDK.C.lib"); + options.LinkEnv.InputLibraries.Add($"Microsoft.Xbox.Services.{(int)Toolset}.GDK.C.lib"); var toolsetPath = WindowsPlatformBase.GetToolsets()[Toolset]; var toolsPath = WindowsPlatformBase.GetVCToolPath64(Toolset); diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatform.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatform.cs index bfae02224..9d9a6913b 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsPlatform.cs @@ -62,10 +62,10 @@ namespace Flax.Build.Platforms case TargetPlatform.UWP: return GetSDKs().FirstOrDefault(x => x.Key != WindowsPlatformSDK.v8_1).Value != null; case TargetPlatform.PS4: return Sdk.HasValid("PS4Sdk"); case TargetPlatform.PS5: return Sdk.HasValid("PS5Sdk"); - case TargetPlatform.XboxOne: - case TargetPlatform.XboxScarlett: return GetSDKs().ContainsKey(WindowsPlatformSDK.v10_0_19041_0) && Sdk.HasValid("GDK"); case TargetPlatform.Android: return AndroidSdk.Instance.IsValid && AndroidNdk.Instance.IsValid; case TargetPlatform.Switch: return Sdk.HasValid("SwitchSdk"); + case TargetPlatform.XboxOne: + case TargetPlatform.XboxScarlett: return GetPlatform(platform, true)?.HasRequiredSDKsInstalled ?? false; default: return false; } } From 876c383af0028f44d625f811eec27f3bfee198c2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 21 Mar 2024 21:12:00 -0500 Subject: [PATCH 168/198] Allow save input and undo redo in Editor Options --- Source/Editor/Windows/EditorOptionsWindow.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/EditorOptionsWindow.cs b/Source/Editor/Windows/EditorOptionsWindow.cs index 31dea22e3..e9c4385b5 100644 --- a/Source/Editor/Windows/EditorOptionsWindow.cs +++ b/Source/Editor/Windows/EditorOptionsWindow.cs @@ -23,6 +23,7 @@ namespace FlaxEditor.Windows private Tabs _tabs; private EditorOptions _options; private ToolStripButton _saveButton; + private readonly Undo _undo; private readonly List _customTabs = new List(); /// @@ -33,6 +34,12 @@ namespace FlaxEditor.Windows : base(editor, true, ScrollBars.None) { Title = "Editor Options"; + + // Undo + _undo = new Undo(); + _undo.UndoDone += OnUndoRedo; + _undo.RedoDone += OnUndoRedo; + _undo.ActionDone += OnUndoRedo; var toolstrip = new ToolStrip { @@ -58,9 +65,19 @@ namespace FlaxEditor.Windows CreateTab("Visual", () => _options.Visual); CreateTab("Source Code", () => _options.SourceCode); CreateTab("Theme", () => _options.Theme); + + // Setup input actions + InputActions.Add(options => options.Undo, _undo.PerformUndo); + InputActions.Add(options => options.Redo, _undo.PerformRedo); + InputActions.Add(options => options.Save, SaveData); _tabs.SelectedTabIndex = 0; } + + private void OnUndoRedo(IUndoAction action) + { + MarkAsEdited(); + } private Tab CreateTab(string name, Func getValue) { @@ -73,7 +90,7 @@ namespace FlaxEditor.Windows Parent = tab }; - var settings = new CustomEditorPresenter(null); + var settings = new CustomEditorPresenter(_undo); settings.Panel.Parent = panel; settings.Panel.Tag = getValue; settings.Modified += MarkAsEdited; From 4b9f50add0e41170348cf91e54970ef4306a9c43 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 21 Mar 2024 21:23:05 -0500 Subject: [PATCH 169/198] Fix carrot and selection height based on dpi scale to correctly scale with interface options. --- Source/Engine/UI/GUI/Common/TextBox.cs | 8 ++++---- Source/Engine/UI/GUI/Common/TextBoxBase.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index ec44a3ca8..4409d7b85 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -181,7 +181,7 @@ namespace FlaxEngine.GUI if (selectedLinesCount == 1) { // Selected is part of single line - Rectangle r1 = new Rectangle(leftEdge.X, leftEdge.Y, rightEdge.X - leftEdge.X, fontHeight); + Rectangle r1 = new Rectangle(leftEdge.X, leftEdge.Y, rightEdge.X - leftEdge.X, fontHeight * DpiScale); Render2D.FillRectangle(r1, selectionColor); } else @@ -189,17 +189,17 @@ namespace FlaxEngine.GUI float leftMargin = _layout.Bounds.Location.X; // Selected is more than one line - Rectangle r1 = new Rectangle(leftEdge.X, leftEdge.Y, 1000000000, fontHeight); + Rectangle r1 = new Rectangle(leftEdge.X, leftEdge.Y, 1000000000, fontHeight * DpiScale); Render2D.FillRectangle(r1, selectionColor); // for (int i = 3; i <= selectedLinesCount; i++) { leftEdge.Y += fontHeight; - Rectangle r = new Rectangle(leftMargin, leftEdge.Y, 1000000000, fontHeight); + Rectangle r = new Rectangle(leftMargin, leftEdge.Y, 1000000000, fontHeight * DpiScale); Render2D.FillRectangle(r, selectionColor); } // - Rectangle r2 = new Rectangle(leftMargin, rightEdge.Y, rightEdge.X - leftMargin, fontHeight); + Rectangle r2 = new Rectangle(leftMargin, rightEdge.Y, rightEdge.X - leftMargin, fontHeight * DpiScale); Render2D.FillRectangle(r2, selectionColor); } } diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index c22099011..a7393a121 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -474,7 +474,7 @@ namespace FlaxEngine.GUI caretPos.X - (caretWidth * 0.5f), caretPos.Y, caretWidth, - height); + height * DpiScale); } } From 4cd788cedca1a8ba51db3ba34220dd475ff00f44 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 22 Mar 2024 11:59:04 +0100 Subject: [PATCH 170/198] Add `UI Widget` to new asset creation for quick UI setup --- Source/Editor/Content/Proxy/PrefabProxy.cs | 61 +++++++++++++++++++ .../Editor/Modules/ContentDatabaseModule.cs | 1 + 2 files changed, 62 insertions(+) diff --git a/Source/Editor/Content/Proxy/PrefabProxy.cs b/Source/Editor/Content/Proxy/PrefabProxy.cs index ee3674daa..18860995e 100644 --- a/Source/Editor/Content/Proxy/PrefabProxy.cs +++ b/Source/Editor/Content/Proxy/PrefabProxy.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; +using System.IO; using FlaxEditor.Content.Thumbnails; using FlaxEditor.Viewport.Previews; using FlaxEditor.Windows; @@ -194,4 +195,64 @@ namespace FlaxEditor.Content base.Dispose(); } } + + /// + /// Content proxy for quick UI Control prefab creation as widget. + /// + [ContentContextMenu("New/Widget")] + internal sealed class WidgetProxy : AssetProxy + { + /// + public override string Name => "UI Widget"; + + /// + public override bool IsProxyFor(ContentItem item) + { + return false; + } + + /// + public override string FileExtension => PrefabProxy.Extension; + + /// + public override EditorWindow Open(Editor editor, ContentItem item) + { + return null; + } + + /// + public override Color AccentColor => Color.Transparent; + + /// + public override string TypeName => PrefabProxy.AssetTypename; + + /// + public override AssetItem ConstructItem(string path, string typeName, ref Guid id) + { + return null; + } + + /// + public override bool CanCreate(ContentFolder targetLocation) + { + return targetLocation.CanHaveAssets; + } + + /// + public override void Create(string outputPath, object arg) + { + // Create prefab with UI Control + var actor = new UIControl + { + Name = Path.GetFileNameWithoutExtension(outputPath), + StaticFlags = StaticFlags.None, + }; + actor.Control = new Button + { + Text = "Button", + }; + PrefabManager.CreatePrefab(actor, outputPath, false); + Object.Destroy(actor, 20.0f); + } + } } diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs index fd830db86..0e4e96d30 100644 --- a/Source/Editor/Modules/ContentDatabaseModule.cs +++ b/Source/Editor/Modules/ContentDatabaseModule.cs @@ -1106,6 +1106,7 @@ namespace FlaxEditor.Modules Proxy.Add(new VisualScriptProxy()); Proxy.Add(new BehaviorTreeProxy()); Proxy.Add(new LocalizedStringTableProxy()); + Proxy.Add(new WidgetProxy()); Proxy.Add(new FileProxy()); Proxy.Add(new SpawnableJsonAssetProxy()); From fd756f7baed970c411e0fdec305f7cc1314cba68 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 22 Mar 2024 10:35:53 -0500 Subject: [PATCH 171/198] Allow multiple collision actors to be created from context menu. --- .../SceneGraph/Actors/StaticModelNode.cs | 138 ++++++++++-------- 1 file changed, 77 insertions(+), 61 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs index 60be5bef9..d2fcd510b 100644 --- a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs +++ b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs @@ -89,71 +89,87 @@ namespace FlaxEditor.SceneGraph.Actors private void OnAddMeshCollider() { - var model = ((StaticModel)Actor).Model; - if (!model) - return; + // Allow collider to be added to evey static model selection + var selection = Editor.Instance.SceneEditing.Selection.ToArray(); + var createdNodes = new List(); + foreach (var node in selection) + { + if (node is not StaticModelNode staticModelNode) + continue; - // Special case for in-built Editor models that can use analytical collision - var modelPath = model.Path; - if (modelPath.EndsWith("/Primitives/Cube.flax", StringComparison.Ordinal)) - { - var actor = new BoxCollider - { - StaticFlags = Actor.StaticFlags, - Transform = Actor.Transform, - }; - Root.Spawn(actor, Actor); - return; - } - if (modelPath.EndsWith("/Primitives/Sphere.flax", StringComparison.Ordinal)) - { - var actor = new SphereCollider - { - StaticFlags = Actor.StaticFlags, - Transform = Actor.Transform, - }; - Root.Spawn(actor, Actor); - return; - } - if (modelPath.EndsWith("/Primitives/Plane.flax", StringComparison.Ordinal)) - { - var actor = new BoxCollider - { - StaticFlags = Actor.StaticFlags, - Transform = Actor.Transform, - Size = new Float3(100.0f, 100.0f, 1.0f), - }; - Root.Spawn(actor, Actor); - return; - } - if (modelPath.EndsWith("/Primitives/Capsule.flax", StringComparison.Ordinal)) - { - var actor = new CapsuleCollider - { - StaticFlags = Actor.StaticFlags, - Transform = Actor.Transform, - Radius = 25.0f, - Height = 50.0f, - }; - Editor.Instance.SceneEditing.Spawn(actor, Actor); - actor.LocalPosition = new Vector3(0, 50.0f, 0); - actor.LocalOrientation = Quaternion.Euler(0, 0, 90.0f); - return; - } + var model = ((StaticModel)staticModelNode.Actor).Model; + if (!model) + continue; - // Create collision data (or reuse) and add collision actor - Action created = collisionData => - { - var actor = new MeshCollider + // Special case for in-built Editor models that can use analytical collision + var modelPath = model.Path; + if (modelPath.EndsWith("/Primitives/Cube.flax", StringComparison.Ordinal)) { - StaticFlags = Actor.StaticFlags, - Transform = Actor.Transform, - CollisionData = collisionData, + var actor = new BoxCollider + { + StaticFlags = staticModelNode.Actor.StaticFlags, + Transform = staticModelNode.Actor.Transform, + }; + staticModelNode.Root.Spawn(actor, staticModelNode.Actor); + createdNodes.Add(Editor.Instance.Scene.GetActorNode(actor)); + continue; + } + if (modelPath.EndsWith("/Primitives/Sphere.flax", StringComparison.Ordinal)) + { + var actor = new SphereCollider + { + StaticFlags = staticModelNode.Actor.StaticFlags, + Transform = staticModelNode.Actor.Transform, + }; + staticModelNode.Root.Spawn(actor, staticModelNode.Actor); + createdNodes.Add(Editor.Instance.Scene.GetActorNode(actor)); + continue; + } + if (modelPath.EndsWith("/Primitives/Plane.flax", StringComparison.Ordinal)) + { + var actor = new BoxCollider + { + StaticFlags = staticModelNode.Actor.StaticFlags, + Transform = staticModelNode.Actor.Transform, + Size = new Float3(100.0f, 100.0f, 1.0f), + }; + staticModelNode.Root.Spawn(actor, staticModelNode.Actor); + createdNodes.Add(Editor.Instance.Scene.GetActorNode(actor)); + continue; + } + if (modelPath.EndsWith("/Primitives/Capsule.flax", StringComparison.Ordinal)) + { + var actor = new CapsuleCollider + { + StaticFlags = staticModelNode.Actor.StaticFlags, + Transform = staticModelNode.Actor.Transform, + Radius = 25.0f, + Height = 50.0f, + }; + Editor.Instance.SceneEditing.Spawn(actor, staticModelNode.Actor); + actor.LocalPosition = new Vector3(0, 50.0f, 0); + actor.LocalOrientation = Quaternion.Euler(0, 0, 90.0f); + createdNodes.Add(Editor.Instance.Scene.GetActorNode(actor)); + continue; + } + + // Create collision data (or reuse) and add collision actor + Action created = collisionData => + { + var actor = new MeshCollider + { + StaticFlags = staticModelNode.Actor.StaticFlags, + Transform = staticModelNode.Actor.Transform, + CollisionData = collisionData, + }; + staticModelNode.Root.Spawn(actor, staticModelNode.Actor); + createdNodes.Add(Editor.Instance.Scene.GetActorNode(actor)); }; - Root.Spawn(actor, Actor); - }; - var collisionDataProxy = (CollisionDataProxy)Editor.Instance.ContentDatabase.GetProxy(); - collisionDataProxy.CreateCollisionDataFromModel(model, created); + var collisionDataProxy = (CollisionDataProxy)Editor.Instance.ContentDatabase.GetProxy(); + collisionDataProxy.CreateCollisionDataFromModel(model, created); + } + // Select all created nodes + Editor.Instance.SceneEditing.Select(createdNodes); } } } From 495de38c48c467a3627b791f01177a0e19f04e78 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 22 Mar 2024 12:09:41 -0500 Subject: [PATCH 172/198] Make TargetViewOffset only go to bottom of text. Add Scroll to Carrot for new lines. --- Source/Engine/UI/GUI/Common/TextBoxBase.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index a7393a121..460d6f4b1 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -1265,7 +1265,7 @@ namespace FlaxEngine.GUI // Multiline scroll if (IsMultiline && _text.Length != 0 && IsMultilineScrollable) { - TargetViewOffset = Float2.Clamp(_targetViewOffset - new Float2(0, delta * 10.0f), Float2.Zero, new Float2(_targetViewOffset.X, _textSize.Y)); + TargetViewOffset = Float2.Clamp(_targetViewOffset - new Float2(0, delta * 10.0f), Float2.Zero, new Float2(_targetViewOffset.X, _textSize.Y - Height)); return true; } @@ -1456,6 +1456,7 @@ namespace FlaxEngine.GUI { // Insert new line Insert('\n'); + ScrollToCaret(); } else if (!IsNavFocused) { From 6709fcd95dcf20208c706beae0248de126a5c8cd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 23 Mar 2024 13:09:29 +0100 Subject: [PATCH 173/198] Fix sorting directories in build tool --- Source/Tools/Flax.Build/Utilities/Utilities.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index 610e8ed3d..c4e58f629 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -801,6 +801,12 @@ namespace Flax.Build Array.Sort(paths, (a, b) => { Version va, vb; + var fa = Path.GetFileName(a); + var fb = Path.GetFileName(b); + if (!string.IsNullOrEmpty(fa)) + a = fa; + if (!string.IsNullOrEmpty(fb)) + b = fb; if (Version.TryParse(a, out va)) { if (Version.TryParse(b, out vb)) From 96f76fb14f64adc46702de5d374798a7dcb3ff7d Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 23 Mar 2024 15:37:05 +0200 Subject: [PATCH 174/198] Fix typos --- Source/Engine/Platform/Win32/Win32FileSystem.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32FileSystem.h b/Source/Engine/Platform/Win32/Win32FileSystem.h index 0af67e4bc..42ac5f4a1 100644 --- a/Source/Engine/Platform/Win32/Win32FileSystem.h +++ b/Source/Engine/Platform/Win32/Win32FileSystem.h @@ -16,12 +16,12 @@ class FLAXENGINE_API Win32FileSystem : public FileSystemBase public: // Creates a new directory - // @param path Drectory path + // @param path Directory path // @returns True if cannot create directory, otherwise false static bool CreateDirectory(const StringView& path); // Deletes an existing directory - // @param path Drectory path + // @param path Directory path // @param deleteSubdirectories True if delete all subdirectories and files, otherwise false // @returns True if cannot delete directory, otherwise false static bool DeleteDirectory(const String& path, bool deleteContents = true); @@ -32,15 +32,15 @@ public: static bool DirectoryExists(const StringView& path); // Finds the names of files (including their paths) that match the specified search pattern in the specified directory, using a value to determine whether to search subdirectories - // @param results When this metod completes, this list contains list of all filenames that match the specified search pattern + // @param results When this method completes, this list contains list of all filenames that match the specified search pattern // @param path Path of the directory to search in it - // @param searchPattern Custo msearch pattern to use during that operation - // @param option Addidtional search options + // @param searchPattern Custom search pattern to use during that operation + // @param option Additional search options // @returns True if an error occurred, otherwise false static bool DirectoryGetFiles(Array& results, const String& path, const Char* searchPattern, DirectorySearchOption option = DirectorySearchOption::AllDirectories); // Finds the names of directories (including their paths) that are inside the specified directory - // @param results When this metod completes, this list contains list of all filenames that match the specified search pattern + // @param results When this method completes, this list contains list of all filenames that match the specified search pattern // @param directory Path of the directory to search in it // @returns True if an error occurred, otherwise false static bool GetChildDirectories(Array& results, const String& directory); From 5633d1a18ab1751b992fbe324722490a70353c88 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 23 Mar 2024 15:37:19 +0200 Subject: [PATCH 175/198] Prevent creating new project into non-empty folder --- Source/Editor/Editor.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp index aa9f014ba..c4a08e825 100644 --- a/Source/Editor/Editor.cpp +++ b/Source/Editor/Editor.cpp @@ -406,12 +406,11 @@ int32 Editor::LoadProduct() if (CommandLine::Options.NewProject) { Array projectFiles; - FileSystem::DirectoryGetFiles(projectFiles, projectPath, TEXT("*.flaxproj"), DirectorySearchOption::TopDirectoryOnly); - if (projectFiles.Count() == 1) + FileSystem::DirectoryGetFiles(projectFiles, projectPath, TEXT("*"), DirectorySearchOption::TopDirectoryOnly); + if (projectFiles.Count() > 0) { - // Skip creating new project if it already exists - LOG(Info, "Skip creatinng new project because it already exists"); - CommandLine::Options.NewProject.Reset(); + Platform::Fatal(String::Format(TEXT("Target project folder '{0}' is not empty."), projectPath)); + return -1; } } if (CommandLine::Options.NewProject) From c366a9793cd429e5b51deb5eac0693ff103e88bd Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 23 Mar 2024 11:38:21 -0500 Subject: [PATCH 176/198] Add cursor change and showing timeline edge duration when moving it. --- .../Editor/GUI/Timeline/GUI/TimelineEdge.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Source/Editor/GUI/Timeline/GUI/TimelineEdge.cs b/Source/Editor/GUI/Timeline/GUI/TimelineEdge.cs index 1bada224a..779eb7ddf 100644 --- a/Source/Editor/GUI/Timeline/GUI/TimelineEdge.cs +++ b/Source/Editor/GUI/Timeline/GUI/TimelineEdge.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using System; +using System.Globalization; using FlaxEditor.GUI.Timeline.Undo; using FlaxEngine; using FlaxEngine.GUI; @@ -44,6 +46,25 @@ namespace FlaxEditor.GUI.Timeline.GUI var thickness = 2.0f; var borderColor = _isMoving ? moveColor : (IsMouseOver && _canEdit ? Color.Yellow : style.BorderNormal); Render2D.FillRectangle(new Rectangle((Width - thickness) * 0.5f, timeAxisHeaderOffset, thickness, Height - timeAxisHeaderOffset), borderColor); + if (_canEdit && _isMoving) + { + // TODO: handle start + string labelText; + switch (_timeline.TimeShowMode) + { + case Timeline.TimeShowModes.Frames: + labelText = _timeline.DurationFrames.ToString("###0", CultureInfo.InvariantCulture); + break; + case Timeline.TimeShowModes.Seconds: + labelText = _timeline.Duration.ToString("###0.##'s'", CultureInfo.InvariantCulture); + break; + case Timeline.TimeShowModes.Time: + labelText = TimeSpan.FromSeconds(_timeline.DurationFrames / _timeline.FramesPerSecond).ToString("g"); + break; + default: throw new ArgumentOutOfRangeException(); + } + Render2D.DrawText(style.FontSmall, labelText, style.Foreground, new Float2((Width - thickness) * 0.5f + 4, timeAxisHeaderOffset)); + } } /// @@ -90,13 +111,26 @@ namespace FlaxEditor.GUI.Timeline.GUI { _timeline.MarkAsEdited(); } + Cursor = CursorType.SizeWE; + } + else if (IsMouseOver && _canEdit) + { + Cursor = CursorType.SizeWE; } else { + Cursor = CursorType.Default; base.OnMouseMove(location); } } + /// + public override void OnMouseLeave() + { + Cursor = CursorType.Default; + base.OnMouseLeave(); + } + /// public override bool OnMouseUp(Float2 location, MouseButton button) { @@ -127,6 +161,7 @@ namespace FlaxEditor.GUI.Timeline.GUI { EndMoving(); } + Cursor = CursorType.Default; base.OnLostFocus(); } From e5e675f0897ececb5272355317c9cf595bbc9efa Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 23 Mar 2024 17:46:46 -0500 Subject: [PATCH 177/198] Fix splitters not working. --- Source/Engine/UI/GUI/Panels/SplitPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Panels/SplitPanel.cs b/Source/Engine/UI/GUI/Panels/SplitPanel.cs index f215b1ccf..e721ee8a3 100644 --- a/Source/Engine/UI/GUI/Panels/SplitPanel.cs +++ b/Source/Engine/UI/GUI/Panels/SplitPanel.cs @@ -187,7 +187,7 @@ namespace FlaxEngine.GUI // Start moving splitter StartTracking(); Focus(); - return false; + return true; } return base.OnMouseDown(location, button); From 51504d0d9261dc257ce320b9fec9f813706066d7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 24 Mar 2024 19:39:16 +0100 Subject: [PATCH 178/198] Fix building macOS/iOS with the latest Vulkan SDK --- .../Cooker/Platform/iOS/iOSPlatformTools.cpp | 14 ++++- .../Vulkan/GraphicsDeviceVulkan.Build.cs | 53 +++++++++++++++++++ Source/ThirdParty/volk/volk.Build.cs | 5 +- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp index 74ad99f1e..a54676a64 100644 --- a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp @@ -202,13 +202,25 @@ bool iOSPlatformTools::OnPostProcess(CookingData& data) if (EditorUtilities::FormatAppPackageName(appIdentifier)) return true; - // Copy fresh Gradle project template + // Copy fresh XCode project template if (FileSystem::CopyDirectory(data.OriginalOutputPath, platformDataPath / TEXT("Project"), true)) { LOG(Error, "Failed to deploy XCode project to {0} from {1}", data.OriginalOutputPath, platformDataPath); return true; } + // Fix MoltenVK lib (copied from VulkanSDK xcframework) + FileSystem::MoveFile(data.DataOutputPath / TEXT("libMoltenVK.dylib"), data.DataOutputPath / TEXT("MoltenVK"), true); + { + // Fix rpath to point into dynamic library (rather than framework location) + CreateProcessSettings procSettings; + procSettings.HiddenWindow = true; + procSettings.WorkingDirectory = data.DataOutputPath; + procSettings.FileName = TEXT("/usr/bin/install_name_tool"); + procSettings.Arguments = TEXT("-id \"@rpath/libMoltenVK.dylib\" libMoltenVK.dylib"); + Platform::CreateProcess(procSettings); + } + // Format project template files Dictionary configReplaceMap; configReplaceMap[TEXT("${AppName}")] = appName; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GraphicsDeviceVulkan.Build.cs b/Source/Engine/GraphicsDevice/Vulkan/GraphicsDeviceVulkan.Build.cs index a76d94ee3..663ff97a0 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GraphicsDeviceVulkan.Build.cs +++ b/Source/Engine/GraphicsDevice/Vulkan/GraphicsDeviceVulkan.Build.cs @@ -133,6 +133,59 @@ public sealed class VulkanSdk : Sdk } return false; } + + /// + /// Adds any runtime dependency files to the build that uses Vulkan SDK. + /// + /// Build options. + public void AddDependencyFiles(BuildOptions options) + { + switch (options.Platform.Target) + { + case TargetPlatform.Mac: + case TargetPlatform.iOS: + { + // MoltenVK + var platformName = options.Platform.Target == TargetPlatform.iOS ? "iOS" : "macOS"; + var location1 = Path.Combine(RootPath, "../MoltenVK/dylib/" + platformName); + if (Directory.Exists(location1)) + { + // Initial location + options.DependencyFiles.Add(Path.Combine(location1, "libMoltenVK.dylib")); + options.DependencyFiles.Add(Path.Combine(location1, "MoltenVK_icd.json")); + return; + } + + // New location from SDK 1.3.275 + if (options.Platform.Target == TargetPlatform.iOS) + { + var location2 = Path.Combine(RootPath, "../iOS/lib/MoltenVK.xcframework/ios-arm64/MoltenVK.framework"); + var location3 = Path.Combine(RootPath, "../iOS/share/vulkan/icd.d"); + if (Directory.Exists(location2) && Directory.Exists(location3)) + { + // iOS + options.DependencyFiles.Add(Path.Combine(location2, "MoltenVK")); + options.DependencyFiles.Add(Path.Combine(location3, "MoltenVK_icd.json")); + return; + } + } + else + { + var location2 = Path.Combine(RootPath, "lib"); + var location3 = Path.Combine(RootPath, "share/vulkan/icd.d"); + if (Directory.Exists(location2) && Directory.Exists(location3)) + { + // macOS + options.DependencyFiles.Add(Path.Combine(location2, "libMoltenVK.dylib")); + options.DependencyFiles.Add(Path.Combine(location3, "MoltenVK_icd.json")); + return; + } + } + Log.Error($"Missing MoltenVK files for {platformName} in VulkanSDK '{RootPath}'"); + break; + } + } + } } /// diff --git a/Source/ThirdParty/volk/volk.Build.cs b/Source/ThirdParty/volk/volk.Build.cs index 667ecf3af..bd6ee1f58 100644 --- a/Source/ThirdParty/volk/volk.Build.cs +++ b/Source/ThirdParty/volk/volk.Build.cs @@ -42,16 +42,13 @@ public class volk : ThirdPartyModule break; case TargetPlatform.Mac: options.PublicDefinitions.Add("VK_USE_PLATFORM_MACOS_MVK"); - options.DependencyFiles.Add(Path.Combine(VulkanSdk.Instance.RootPath, "../MoltenVK/dylib/macOS/libMoltenVK.dylib")); - options.DependencyFiles.Add(Path.Combine(VulkanSdk.Instance.RootPath, "../MoltenVK/dylib/macOS/MoltenVK_icd.json")); break; case TargetPlatform.iOS: options.PublicDefinitions.Add("VK_USE_PLATFORM_IOS_MVK"); - options.DependencyFiles.Add(Path.Combine(VulkanSdk.Instance.RootPath, "../MoltenVK/dylib/iOS/libMoltenVK.dylib")); - options.DependencyFiles.Add(Path.Combine(VulkanSdk.Instance.RootPath, "../MoltenVK/dylib/iOS/MoltenVK_icd.json")); break; default: throw new InvalidPlatformException(options.Platform.Target); } + VulkanSdk.Instance.AddDependencyFiles(options); string includesFolderPath; if (VulkanSdk.Instance.TryGetIncludePath(options.Platform.Target, out includesFolderPath)) From b821bb7a54b3584d2132e3f3cb76312be8713921 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 24 Mar 2024 13:47:04 +0100 Subject: [PATCH 179/198] Simplify code --- Source/Engine/UI/GUI/Common/TextBox.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index 4409d7b85..2553dc439 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -170,18 +170,19 @@ namespace FlaxEngine.GUI { var leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout); var rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout); - float fontHeight = font.Height / DpiScale; + var fontHeight = font.Height; + var textHeight = fontHeight / DpiScale; // Draw selection background float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f); alpha *= alpha; Color selectionColor = SelectionColor * alpha; // - int selectedLinesCount = 1 + Mathf.FloorToInt((rightEdge.Y - leftEdge.Y) / fontHeight); + int selectedLinesCount = 1 + Mathf.FloorToInt((rightEdge.Y - leftEdge.Y) / textHeight); if (selectedLinesCount == 1) { // Selected is part of single line - Rectangle r1 = new Rectangle(leftEdge.X, leftEdge.Y, rightEdge.X - leftEdge.X, fontHeight * DpiScale); + Rectangle r1 = new Rectangle(leftEdge.X, leftEdge.Y, rightEdge.X - leftEdge.X, fontHeight); Render2D.FillRectangle(r1, selectionColor); } else @@ -189,17 +190,17 @@ namespace FlaxEngine.GUI float leftMargin = _layout.Bounds.Location.X; // Selected is more than one line - Rectangle r1 = new Rectangle(leftEdge.X, leftEdge.Y, 1000000000, fontHeight * DpiScale); + Rectangle r1 = new Rectangle(leftEdge.X, leftEdge.Y, 1000000000, fontHeight); Render2D.FillRectangle(r1, selectionColor); // for (int i = 3; i <= selectedLinesCount; i++) { - leftEdge.Y += fontHeight; - Rectangle r = new Rectangle(leftMargin, leftEdge.Y, 1000000000, fontHeight * DpiScale); + leftEdge.Y += textHeight; + Rectangle r = new Rectangle(leftMargin, leftEdge.Y, 1000000000, fontHeight); Render2D.FillRectangle(r, selectionColor); } // - Rectangle r2 = new Rectangle(leftMargin, rightEdge.Y, rightEdge.X - leftMargin, fontHeight * DpiScale); + Rectangle r2 = new Rectangle(leftMargin, rightEdge.Y, rightEdge.X - leftMargin, fontHeight); Render2D.FillRectangle(r2, selectionColor); } } From c08ca33a9af4f935538ce118f3f7f8c600c0fa7e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 24 Mar 2024 20:57:19 +0100 Subject: [PATCH 180/198] Fix `Revert to Default` option when property is marked as `ReadOnly` --- Source/Editor/CustomEditors/CustomEditor.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index ef6302e8e..48ada9150 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -385,6 +385,15 @@ namespace FlaxEditor.CustomEditors LinkedLabel = label; } + internal bool CanEditValue + { + get + { + var readOnly = Values.Info.GetAttribute(); + return readOnly == null; + } + } + /// /// If true, the value reverting to default/reference will be handled via iteration over children editors, instead of for a whole object at once. /// @@ -413,7 +422,7 @@ namespace FlaxEditor.CustomEditors { if (!Values.IsDefaultValueModified) return false; - return true; + return CanEditValue; } } @@ -422,7 +431,7 @@ namespace FlaxEditor.CustomEditors /// public void RevertToDefaultValue() { - if (!Values.HasDefaultValue) + if (!Values.HasDefaultValue || !CanEditValue) return; RevertDiffToDefault(); } @@ -471,7 +480,7 @@ namespace FlaxEditor.CustomEditors } else { - if (Values.IsReferenceValueModified) + if (CanRevertReferenceValue) SetValueToReference(); } } @@ -485,7 +494,7 @@ namespace FlaxEditor.CustomEditors { if (!Values.IsReferenceValueModified) return false; - return true; + return CanEditValue; } } @@ -494,7 +503,7 @@ namespace FlaxEditor.CustomEditors /// public void RevertToReferenceValue() { - if (!Values.HasReferenceValue) + if (!Values.HasReferenceValue || !CanEditValue) return; RevertDiffToReference(); } From 981fe9482cc38d90be308d46de63817becbffad3 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 24 Mar 2024 15:51:33 -0500 Subject: [PATCH 181/198] Fix multi collider to work in prefab window. --- .../SceneGraph/Actors/StaticModelNode.cs | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs index d2fcd510b..65bea6c32 100644 --- a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs +++ b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using FlaxEditor.Content; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.Windows; +using FlaxEditor.Windows.Assets; using FlaxEngine; namespace FlaxEditor.SceneGraph.Actors @@ -84,13 +85,22 @@ namespace FlaxEditor.SceneGraph.Actors { base.OnContextMenu(contextMenu, window); - contextMenu.AddButton("Add collider", OnAddMeshCollider).Enabled = ((StaticModel)Actor).Model != null; + contextMenu.AddButton("Add collider", () => OnAddMeshCollider(window)).Enabled = ((StaticModel)Actor).Model != null; } - private void OnAddMeshCollider() + private void OnAddMeshCollider(EditorWindow window) { // Allow collider to be added to evey static model selection - var selection = Editor.Instance.SceneEditing.Selection.ToArray(); + SceneGraphNode[] selection = Array.Empty(); + if (window is SceneTreeWindow) + { + selection = Editor.Instance.SceneEditing.Selection.ToArray(); + } + else if (window is PrefabWindow prefabWindow) + { + selection = prefabWindow.Selection.ToArray(); + } + var createdNodes = new List(); foreach (var node in selection) { @@ -111,7 +121,7 @@ namespace FlaxEditor.SceneGraph.Actors Transform = staticModelNode.Actor.Transform, }; staticModelNode.Root.Spawn(actor, staticModelNode.Actor); - createdNodes.Add(Editor.Instance.Scene.GetActorNode(actor)); + createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor)); continue; } if (modelPath.EndsWith("/Primitives/Sphere.flax", StringComparison.Ordinal)) @@ -122,7 +132,7 @@ namespace FlaxEditor.SceneGraph.Actors Transform = staticModelNode.Actor.Transform, }; staticModelNode.Root.Spawn(actor, staticModelNode.Actor); - createdNodes.Add(Editor.Instance.Scene.GetActorNode(actor)); + createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor)); continue; } if (modelPath.EndsWith("/Primitives/Plane.flax", StringComparison.Ordinal)) @@ -134,7 +144,7 @@ namespace FlaxEditor.SceneGraph.Actors Size = new Float3(100.0f, 100.0f, 1.0f), }; staticModelNode.Root.Spawn(actor, staticModelNode.Actor); - createdNodes.Add(Editor.Instance.Scene.GetActorNode(actor)); + createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor)); continue; } if (modelPath.EndsWith("/Primitives/Capsule.flax", StringComparison.Ordinal)) @@ -149,7 +159,7 @@ namespace FlaxEditor.SceneGraph.Actors Editor.Instance.SceneEditing.Spawn(actor, staticModelNode.Actor); actor.LocalPosition = new Vector3(0, 50.0f, 0); actor.LocalOrientation = Quaternion.Euler(0, 0, 90.0f); - createdNodes.Add(Editor.Instance.Scene.GetActorNode(actor)); + createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor)); continue; } @@ -163,13 +173,21 @@ namespace FlaxEditor.SceneGraph.Actors CollisionData = collisionData, }; staticModelNode.Root.Spawn(actor, staticModelNode.Actor); - createdNodes.Add(Editor.Instance.Scene.GetActorNode(actor)); + createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor)); }; var collisionDataProxy = (CollisionDataProxy)Editor.Instance.ContentDatabase.GetProxy(); - collisionDataProxy.CreateCollisionDataFromModel(model, created); + collisionDataProxy.CreateCollisionDataFromModel(model, created, selection.Length == 1); } + // Select all created nodes - Editor.Instance.SceneEditing.Select(createdNodes); + if (window is SceneTreeWindow) + { + Editor.Instance.SceneEditing.Select(createdNodes); + } + else if (window is PrefabWindow pWindow) + { + pWindow.Select(createdNodes); + } } } } From 07522823e5c789af728cee74a36e0c4332cc7135 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 24 Mar 2024 23:57:02 +0200 Subject: [PATCH 182/198] Restore opening existing projects with `-new` --- Source/Editor/Editor.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp index c4a08e825..4da379fac 100644 --- a/Source/Editor/Editor.cpp +++ b/Source/Editor/Editor.cpp @@ -406,11 +406,26 @@ int32 Editor::LoadProduct() if (CommandLine::Options.NewProject) { Array projectFiles; - FileSystem::DirectoryGetFiles(projectFiles, projectPath, TEXT("*"), DirectorySearchOption::TopDirectoryOnly); - if (projectFiles.Count() > 0) + FileSystem::DirectoryGetFiles(projectFiles, projectPath, TEXT("*.flaxproj"), DirectorySearchOption::TopDirectoryOnly); + if (projectFiles.Count() > 1) { - Platform::Fatal(String::Format(TEXT("Target project folder '{0}' is not empty."), projectPath)); - return -1; + Platform::Fatal(TEXT("Too many project files.")); + return -2; + } + else if (projectFiles.Count() == 1) + { + LOG(Info, "Skip creating new project because it already exists"); + CommandLine::Options.NewProject.Reset(); + } + else + { + Array files; + FileSystem::DirectoryGetFiles(files, projectPath, TEXT("*"), DirectorySearchOption::TopDirectoryOnly); + if (files.Count() > 0) + { + Platform::Fatal(String::Format(TEXT("Target project folder '{0}' is not empty."), projectPath)); + return -1; + } } } if (CommandLine::Options.NewProject) From 5f96a74ce23a87e92aac322d8e717f60c3e5903e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 25 Mar 2024 12:02:40 +0100 Subject: [PATCH 183/198] Revert 1a68a52611c3c0e64111b1549e1833e34f151967 to draw after geometry #1859 --- Source/Editor/Gizmo/GridGizmo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Gizmo/GridGizmo.cs b/Source/Editor/Gizmo/GridGizmo.cs index a96651472..74a16449e 100644 --- a/Source/Editor/Gizmo/GridGizmo.cs +++ b/Source/Editor/Gizmo/GridGizmo.cs @@ -21,7 +21,7 @@ namespace FlaxEditor.Gizmo { Order = -100; UseSingleTarget = true; - Location = PostProcessEffectLocation.AfterAntiAliasingPass; + Location = PostProcessEffectLocation.BeforeForwardPass; } ~Renderer() From 0b801225e1023ceed086f2e08947daf6675099d9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Mar 2024 16:44:54 +0100 Subject: [PATCH 184/198] Fix incorrect build number increment --- Flax.flaxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 209887b20..f7806caac 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 8, "Revision": 0, - "Build": 65046 + "Build": 6510 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.", From f60446c37b1a5c4d9acce5789907f1560b40c903 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Mar 2024 16:50:47 +0100 Subject: [PATCH 185/198] Fix shader source code preview in editor --- Source/Editor/Managed/ManagedEditor.Internal.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp index 43c805bf4..b2867c2df 100644 --- a/Source/Editor/Managed/ManagedEditor.Internal.cpp +++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp @@ -263,22 +263,22 @@ DEFINE_INTERNAL_CALL(MString*) EditorInternal_GetShaderAssetSourceCode(BinaryAss INTERNAL_CALL_CHECK_RETURN(obj, nullptr); if (obj->WaitForLoaded()) DebugLog::ThrowNullReference(); - auto lock = obj->Storage->Lock(); - if (obj->LoadChunk(SHADER_FILE_CHUNK_SOURCE)) return nullptr; + // Decrypt source code BytesContainer data; obj->GetChunkData(SHADER_FILE_CHUNK_SOURCE, data); + auto source = data.Get(); + auto sourceLength = data.Length(); + Encryption::DecryptBytes(data.Get(), data.Length()); + source[sourceLength - 1] = 0; - Encryption::DecryptBytes((byte*)data.Get(), data.Length()); - + // Get source and encrypt it back const StringAnsiView srcData((const char*)data.Get(), data.Length()); - const String source(srcData); - const auto str = MUtils::ToString(source); - - Encryption::EncryptBytes((byte*)data.Get(), data.Length()); + const auto str = MUtils::ToString(srcData); + Encryption::EncryptBytes(data.Get(), data.Length()); return str; } From 24e56a825eb0a07b0679b2fd022d7576f48dfe9d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Mar 2024 18:07:53 +0100 Subject: [PATCH 186/198] Fix streaming astc 6x6 textures that have higher block size --- Source/Engine/Graphics/Textures/StreamingTexture.cpp | 3 ++- Source/Engine/Streaming/StreamingHandlers.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index a3927a30c..14874abf1 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -113,8 +113,9 @@ bool StreamingTexture::Create(const TextureHeader& header) if (_isBlockCompressed) { // Ensure that streaming doesn't go too low because the hardware expects the texture to be min in size of compressed texture block + const int32 blockSize = PixelFormatExtensions::ComputeBlockSize(_header.Format); int32 lastMip = header.MipLevels - 1; - while ((header.Width >> lastMip) < 4 && (header.Height >> lastMip) < 4 && lastMip > 0) + while ((header.Width >> lastMip) < blockSize && (header.Height >> lastMip) < blockSize && lastMip > 0) lastMip--; _minMipCountBlockCompressed = Math::Min(header.MipLevels - lastMip + 1, header.MipLevels); } diff --git a/Source/Engine/Streaming/StreamingHandlers.cpp b/Source/Engine/Streaming/StreamingHandlers.cpp index 44ea60774..0b10fba1b 100644 --- a/Source/Engine/Streaming/StreamingHandlers.cpp +++ b/Source/Engine/Streaming/StreamingHandlers.cpp @@ -59,7 +59,7 @@ int32 TexturesStreamingHandler::CalculateResidency(StreamableResource* resource, if (mipLevels > 0 && mipLevels < texture._minMipCountBlockCompressed && texture._isBlockCompressed) { - // Block compressed textures require minimum size of 4 + // Block compressed textures require minimum size of block size (eg. 4 for BC formats) mipLevels = texture._minMipCountBlockCompressed; } From b8a287c4d5ed459eeac349d6f3830a0bef9e9885 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Mar 2024 18:08:22 +0100 Subject: [PATCH 187/198] Fix Vulkan swapchain init on Android device with more surfaces reported by the driver --- .../GraphicsDevice/Vulkan/Android/AndroidVulkanPlatform.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Engine/GraphicsDevice/Vulkan/Android/AndroidVulkanPlatform.h b/Source/Engine/GraphicsDevice/Vulkan/Android/AndroidVulkanPlatform.h index ad0c49b0a..ad2d68652 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Android/AndroidVulkanPlatform.h +++ b/Source/Engine/GraphicsDevice/Vulkan/Android/AndroidVulkanPlatform.h @@ -6,6 +6,9 @@ #if GRAPHICS_API_VULKAN && PLATFORM_ANDROID +// Support more backbuffers in case driver decides to use more +#define VULKAN_BACK_BUFFERS_COUNT_MAX 8 + /// /// The implementation for the Vulkan API support for Android platform. /// From 21d5e63b933d477dbcc8a08d3020155dc0d93d0a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Mar 2024 18:24:37 +0100 Subject: [PATCH 188/198] Fix cooking small textures for Android/iOS with ASTC enabled --- Source/Editor/Cooker/Steps/CookAssetsStep.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp index 2aafda1a5..2558181aa 100644 --- a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp +++ b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp @@ -643,7 +643,7 @@ bool ProcessTextureBase(CookAssetsStep::AssetCookData& data) const auto asset = static_cast(data.Asset); const auto& assetHeader = asset->StreamingTexture()->GetHeader(); const auto format = asset->Format(); - const auto targetFormat = data.Data.Tools->GetTextureFormat(data.Data, asset, format); + auto targetFormat = data.Data.Tools->GetTextureFormat(data.Data, asset, format); CHECK_RETURN(!PixelFormatExtensions::IsTypeless(targetFormat), true); const auto streamingSettings = StreamingSettings::Get(); int32 mipLevelsMax = GPU_MAX_TEXTURE_MIP_LEVELS; @@ -654,6 +654,11 @@ bool ProcessTextureBase(CookAssetsStep::AssetCookData& data) group.MipLevelsMaxPerPlatform.TryGet(data.Data.Tools->GetPlatform(), mipLevelsMax); } + // If texture is smaller than the block size of the target format (eg. 4x4 texture using ASTC_6x6) then fallback to uncompressed + int32 blockSize = PixelFormatExtensions::ComputeBlockSize(targetFormat); + if (assetHeader->Width < blockSize || assetHeader->Height < blockSize || (blockSize != 1 && mipLevelsMax < 4)) + targetFormat = PixelFormatExtensions::FindUncompressedFormat(format); + // Faster path if don't need to modify texture for the target platform if (format == targetFormat && assetHeader->MipLevels <= mipLevelsMax) { From 5525e895cf811f47de4d1f4b7f6f91f89290610f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Mar 2024 18:55:05 +0100 Subject: [PATCH 189/198] Fix codesign on Windows with the latest SDK --- .../Tools/Flax.Build/Deploy/VCEnvironment.cs | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/Source/Tools/Flax.Build/Deploy/VCEnvironment.cs b/Source/Tools/Flax.Build/Deploy/VCEnvironment.cs index f75c89c8d..4aa5cc060 100644 --- a/Source/Tools/Flax.Build/Deploy/VCEnvironment.cs +++ b/Source/Tools/Flax.Build/Deploy/VCEnvironment.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using Flax.Build; using Flax.Build.Platforms; using Flax.Build.Projects.VisualStudio; @@ -288,10 +287,25 @@ namespace Flax.Deploy var sdks = WindowsPlatformBase.GetSDKs(); if (sdks.Count == 0) throw new Exception("No Windows SDK found. Cannot sign file."); - var sdkKeys = sdks.Keys.ToList(); - sdkKeys.Sort(); - var sdk = sdks[sdkKeys.Last()]; - var signtool = Path.Combine(sdk, "bin", "x64", "signtool.exe"); + var signtool = string.Empty; + foreach (var e in sdks) + { + try + { + var sdk = e.Value; + signtool = Path.Combine(sdk, "bin", "x64", "signtool.exe"); + if (File.Exists(signtool)) + break; + var ver = WindowsPlatformBase.GetSDKVersion(e.Key); + signtool = Path.Combine(sdk, "bin", ver.ToString(4), "x64", "signtool.exe"); + if (File.Exists(signtool)) + break; + } + catch + { + // Ignore version formatting exception + } + } var cmdLine = string.Format("sign /debug /f \"{0}\" /p \"{1}\" /tr http://timestamp.comodoca.com /td sha256 /fd sha256 \"{2}\"", certificatePath, certificatePass, file); Utilities.Run(signtool, cmdLine, null, null, Utilities.RunOptions.Default | Utilities.RunOptions.ThrowExceptionOnError); } From 9e962dcb8963b8dafdd5e4fd135fcc6080275e53 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 28 Mar 2024 17:04:55 -0500 Subject: [PATCH 190/198] Add focus selection lock input to scene tree window. --- Source/Editor/Windows/SceneTreeWindow.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 9231ce970..a7e4e0e44 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -93,6 +93,7 @@ namespace FlaxEditor.Windows InputActions.Add(options => options.RotateMode, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); InputActions.Add(options => options.ScaleMode, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale); InputActions.Add(options => options.FocusSelection, () => Editor.Windows.EditWin.Viewport.FocusSelection()); + InputActions.Add(options => options.LockFocusSelection, () => Editor.Windows.EditWin.Viewport.LockFocusSelection()); InputActions.Add(options => options.Rename, Rename); } From 09f5ac0ed6610da643f9039846a2cbe7da94d3c9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 29 Mar 2024 13:02:23 +0100 Subject: [PATCH 191/198] Fix networked objects dirtying for replication --- .../Networking/NetworkReplicationHierarchy.cpp | 16 +++++++++++----- .../Networking/NetworkReplicationHierarchy.h | 5 +++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.cpp b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp index 03d7e61a6..e3ee09829 100644 --- a/Source/Engine/Networking/NetworkReplicationHierarchy.cpp +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp @@ -167,7 +167,7 @@ void NetworkReplicationGridNode::AddObject(NetworkReplicationHierarchyObject obj cell->MinCullDistance = obj.CullDistance; } cell->Node->AddObject(obj); - _objectToCell[obj.Object] = coord; + _objectToCell[obj.Object.Get()] = coord; // Cache minimum culling distance for a whole cell to skip it at once cell->MinCullDistance = Math::Min(cell->MinCullDistance, obj.CullDistance); @@ -176,12 +176,10 @@ void NetworkReplicationGridNode::AddObject(NetworkReplicationHierarchyObject obj bool NetworkReplicationGridNode::RemoveObject(ScriptingObject* obj) { Int3 coord; - if (!_objectToCell.TryGet(obj, coord)) { return false; } - if (_children[coord].Node->RemoveObject(obj)) { _objectToCell.Remove(obj); @@ -195,12 +193,10 @@ bool NetworkReplicationGridNode::RemoveObject(ScriptingObject* obj) bool NetworkReplicationGridNode::GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result) { Int3 coord; - if (!_objectToCell.TryGet(obj, coord)) { return false; } - if (_children[coord].Node->GetObject(obj, result)) { return true; @@ -208,6 +204,16 @@ bool NetworkReplicationGridNode::GetObject(ScriptingObject* obj, NetworkReplicat return false; } +bool NetworkReplicationGridNode::DirtyObject(ScriptingObject* obj) +{ + Int3 coord; + if (_objectToCell.TryGet(obj, coord)) + { + return _children[coord].Node->DirtyObject(obj); + } + return NetworkReplicationNode::DirtyObject(obj); +} + void NetworkReplicationGridNode::Update(NetworkReplicationHierarchyUpdateResult* result) { CHECK(result); diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.h b/Source/Engine/Networking/NetworkReplicationHierarchy.h index ba2665622..78f9cad1f 100644 --- a/Source/Engine/Networking/NetworkReplicationHierarchy.h +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.h @@ -20,11 +20,11 @@ API_STRUCT(NoDefault, Namespace = "FlaxEngine.Networking") struct FLAXENGINE_API // The object to replicate. API_FIELD() ScriptingObjectReference Object; - // The target amount of the replication updates per second (frequency of the replication). Constrained by NetworkManager::NetworkFPS. Use 0 for 'always relevant' object and less than 0 (eg. -1) for 'never relevant' objects that would only get synched on client join once. + // The target amount of the replication updates per second (frequency of the replication). Constrained by NetworkManager::NetworkFPS. Use 0 for 'always relevant' object and less than 0 (eg. -1) for 'never relevant' objects that would only get synced on client join once (or upon DirtyObject). API_FIELD() float ReplicationFPS = 60; // The minimum distance from the player to the object at which it can process replication. For example, players further away won't receive object data. Use 0 if unused. API_FIELD() float CullDistance = 15000; - // Runtime value for update frames left for the next replication of this object. Matches NetworkManager::NetworkFPS calculated from ReplicationFPS. + // Runtime value for update frames left for the next replication of this object. Matches NetworkManager::NetworkFPS calculated from ReplicationFPS. Set to 1 if ReplicationFPS less than 0 to indicate dirty object. API_FIELD(Attributes="HideInEditor") uint16 ReplicationUpdatesLeft = 0; FORCE_INLINE NetworkReplicationHierarchyObject(const ScriptingObjectReference& obj) @@ -257,6 +257,7 @@ public: void AddObject(NetworkReplicationHierarchyObject obj) override; bool RemoveObject(ScriptingObject* obj) override; bool GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result) override; + bool DirtyObject(ScriptingObject* obj) override; void Update(NetworkReplicationHierarchyUpdateResult* result) override; }; From 069445b94e936a9cf90a52cca6117eccc8cb834e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 29 Mar 2024 13:03:11 +0100 Subject: [PATCH 192/198] Fix bug in `NetworkReplicator::RemoveObject` --- Source/Engine/Networking/NetworkReplicator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 85e6bcdd3..5c8979d42 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -1203,7 +1203,7 @@ void NetworkReplicator::RemoveObject(ScriptingObject* obj) return; ScopeLock lock(ObjectsLock); const auto it = Objects.Find(obj->GetID()); - if (it != Objects.End()) + if (it == Objects.End()) return; // Remove object from the list From 6929a91312e9cb281ca1a42c097b20a64dd31d32 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 29 Mar 2024 13:03:38 +0100 Subject: [PATCH 193/198] Add support for manually dirtying objects that use `ReplicationFPS < 0` for manual-only replication --- .../Networking/NetworkReplicationHierarchy.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.cpp b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp index e3ee09829..224ee9f31 100644 --- a/Source/Engine/Networking/NetworkReplicationHierarchy.cpp +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp @@ -80,7 +80,16 @@ bool NetworkReplicationNode::DirtyObject(ScriptingObject* obj) if (index != -1) { NetworkReplicationHierarchyObject& e = Objects[index]; - e.ReplicationUpdatesLeft = 0; + if (e.ReplicationFPS < -ZeroTolerance) // < 0 + { + // Indicate for manual sync (see logic in Update) + e.ReplicationUpdatesLeft = 1; + } + else + { + // Replicate it next frame + e.ReplicationUpdatesLeft = 0; + } } return index != -1; } @@ -93,6 +102,12 @@ void NetworkReplicationNode::Update(NetworkReplicationHierarchyUpdateResult* res { if (obj.ReplicationFPS < -ZeroTolerance) // < 0 { + if (obj.ReplicationUpdatesLeft) + { + // Marked as dirty to sync manually + obj.ReplicationUpdatesLeft = 0; + result->AddObject(obj.Object); + } continue; } else if (obj.ReplicationFPS < ZeroTolerance) // == 0 From 551337f7a084321aacbe4039d17f0e2b3d28cc1f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 29 Mar 2024 17:14:35 +0100 Subject: [PATCH 194/198] Minor improvement in spline editor --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index c57119e00..0b999d6b1 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -308,11 +308,14 @@ namespace FlaxEditor.SceneGraph.Actors var selection = Editor.Instance.SceneEditing.Selection; if (selection.Count == 1 && selection[0] is SplinePointNode selectedPoint && selectedPoint.ParentNode == this) { - if (Input.Keyboard.GetKey(KeyboardKeys.Shift)) + var mouse = Input.Mouse; + var keyboard = Input.Keyboard; + + if (keyboard.GetKey(KeyboardKeys.Shift)) EditSplineWithSnap(selectedPoint); - var canAddSplinePoint = Input.Mouse.PositionDelta == Float2.Zero && Input.Mouse.Position != Float2.Zero; - var requestAddSplinePoint = Input.Keyboard.GetKey(KeyboardKeys.Control) && Input.Mouse.GetButtonDown(MouseButton.Right); + var canAddSplinePoint = mouse.PositionDelta == Float2.Zero && mouse.Position != Float2.Zero; + var requestAddSplinePoint = Input.Keyboard.GetKey(KeyboardKeys.Control) && mouse.GetButtonDown(MouseButton.Right); if (requestAddSplinePoint && canAddSplinePoint) AddSplinePoint(selectedPoint); } From 08e88587e69ced7fc3fbc8c3da2934afffeb122e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 29 Mar 2024 17:27:00 +0100 Subject: [PATCH 195/198] Add safe check for `LinkedLabel` to prevent errors --- .../Editors/ActorTransformEditor.cs | 17 ++++--- .../CustomEditors/Editors/DoubleEditor.cs | 15 +++--- .../CustomEditors/Editors/FloatEditor.cs | 17 ++++--- .../CustomEditors/Editors/InputEditor.cs | 3 +- .../CustomEditors/Editors/QuaternionEditor.cs | 13 +++-- .../CustomEditors/Editors/Vector3Editor.cs | 48 +++++++++++-------- 6 files changed, 67 insertions(+), 46 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 88ce53b78..3794bffc8 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -111,14 +111,17 @@ namespace FlaxEditor.CustomEditors.Editors SetLinkStyle(); var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value); _linkButton.LocalX += textSize.X + 10; - LinkedLabel.SetupContextMenu += (label, menu, editor) => + if (LinkedLabel != null) { - menu.AddSeparator(); - if (LinkValues) - menu.AddButton("Unlink", ToggleLink).LinkTooltip("Unlinks scale components from uniform scaling"); - else - menu.AddButton("Link", ToggleLink).LinkTooltip("Links scale components for uniform scaling"); - }; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + if (LinkValues) + menu.AddButton("Unlink", ToggleLink).LinkTooltip("Unlinks scale components from uniform scaling"); + else + menu.AddButton("Link", ToggleLink).LinkTooltip("Links scale components for uniform scaling"); + }; + } // Override colors var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; diff --git a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs index 2049860c6..906e6fff0 100644 --- a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs @@ -34,13 +34,16 @@ namespace FlaxEditor.CustomEditors.Editors if (valueCategory != Utils.ValueCategory.None) { doubleValue.SetCategory(valueCategory); - LinkedLabel.SetupContextMenu += (label, menu, editor) => + if (LinkedLabel != null) { - menu.AddSeparator(); - var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None); }); - mb.AutoCheck = true; - mb.Checked = doubleValue.ValueBox.Category != Utils.ValueCategory.None; - }; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None); }); + mb.AutoCheck = true; + mb.Checked = doubleValue.ValueBox.Category != Utils.ValueCategory.None; + }; + } } } } diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index 9d3ff1490..85f8bb5a3 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -40,7 +40,7 @@ namespace FlaxEditor.CustomEditors.Editors _element = slider; return; } - + var floatValue = layout.FloatValue(); floatValue.ValueBox.ValueChanged += OnValueChanged; floatValue.ValueBox.SlidingEnd += ClearToken; @@ -53,13 +53,16 @@ namespace FlaxEditor.CustomEditors.Editors if (valueCategory != Utils.ValueCategory.None) { floatValue.SetCategory(valueCategory); - LinkedLabel.SetupContextMenu += (label, menu, editor) => + if (LinkedLabel != null) { - menu.AddSeparator(); - var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None); }); - mb.AutoCheck = true; - mb.Checked = floatValue.ValueBox.Category != Utils.ValueCategory.None; - }; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None); }); + mb.AutoCheck = true; + mb.Checked = floatValue.ValueBox.Category != Utils.ValueCategory.None; + }; + } } } } diff --git a/Source/Editor/CustomEditors/Editors/InputEditor.cs b/Source/Editor/CustomEditors/Editors/InputEditor.cs index 3521f35a5..5a4905206 100644 --- a/Source/Editor/CustomEditors/Editors/InputEditor.cs +++ b/Source/Editor/CustomEditors/Editors/InputEditor.cs @@ -22,7 +22,8 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - LinkedLabel.SetupContextMenu += OnSetupContextMenu; + if (LinkedLabel != null) + LinkedLabel.SetupContextMenu += OnSetupContextMenu; var comboBoxElement = layout.ComboBox(); _comboBox = comboBoxElement.ComboBox; var names = new List(); diff --git a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs index 7712f1a08..96f9d0646 100644 --- a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs @@ -59,12 +59,15 @@ namespace FlaxEditor.CustomEditors.Editors ZElement.ValueBox.ValueChanged += OnValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; - LinkedLabel.SetupContextMenu += (label, menu, editor) => + if (LinkedLabel != null) { - menu.AddSeparator(); - var value = ((Quaternion)Values[0]).EulerAngles; - menu.AddButton("Copy Euler", () => { Clipboard.Text = JsonSerializer.Serialize(value); }).TooltipText = "Copy the Euler Angles in Degrees"; - }; + LinkedLabel.SetupContextMenu += (label, menu, editor) => + { + menu.AddSeparator(); + var value = ((Quaternion)Values[0]).EulerAngles; + menu.AddButton("Copy Euler", () => { Clipboard.Text = JsonSerializer.Serialize(value); }).TooltipText = "Copy the Euler Angles in Degrees"; + }; + } } private void OnValueChanged() diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index e7170a22d..719fbf05d 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -96,18 +96,22 @@ namespace FlaxEditor.CustomEditors.Editors ZElement.SetCategory(category); ZElement.ValueBox.ValueChanged += OnZValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; - LinkedLabel.SetupContextMenu += (label, menu, editor) => + + if (LinkedLabel != null) { - menu.AddSeparator(); - var mb = menu.AddButton("Show formatted", bt => + LinkedLabel.SetupContextMenu += (label, menu, editor) => { - XElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); - YElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); - ZElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); - }); - mb.AutoCheck = true; - mb.Checked = XElement.ValueBox.Category != Utils.ValueCategory.None; - }; + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => + { + XElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + YElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + ZElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + }); + mb.AutoCheck = true; + mb.Checked = XElement.ValueBox.Category != Utils.ValueCategory.None; + }; + } } private void OnXValueChanged() @@ -294,18 +298,22 @@ namespace FlaxEditor.CustomEditors.Editors ZElement.SetCategory(category); ZElement.ValueBox.ValueChanged += OnValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; - LinkedLabel.SetupContextMenu += (label, menu, editor) => + + if (LinkedLabel != null) { - menu.AddSeparator(); - var mb = menu.AddButton("Show formatted", bt => + LinkedLabel.SetupContextMenu += (label, menu, editor) => { - XElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); - YElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); - ZElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); - }); - mb.AutoCheck = true; - mb.Checked = XElement.ValueBox.Category != Utils.ValueCategory.None; - }; + menu.AddSeparator(); + var mb = menu.AddButton("Show formatted", bt => + { + XElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + YElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + ZElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None); + }); + mb.AutoCheck = true; + mb.Checked = XElement.ValueBox.Category != Utils.ValueCategory.None; + }; + } } private void OnValueChanged() From 3696501050f48f5637cd5c86706fcaeaeb402f4e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 29 Mar 2024 18:07:10 +0100 Subject: [PATCH 196/198] Fix invalid shader codegen when using `ddx`/`ddy` in material vertex shader #2376 --- .../MaterialGenerator.Material.cpp | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 508417895..66d24d4f3 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -421,15 +421,31 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // DDX case 30: { - const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); - value = writeLocal(inValue.Type, String::Format(TEXT("ddx({0})"), inValue.Value), node); + if (_treeType == MaterialTreeType::PixelShader) + { + const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + value = writeLocal(inValue.Type, String::Format(TEXT("ddx({0})"), inValue.Value), node); + } + else + { + // No derivatives support in VS/DS + value = Value::Zero; + } break; } // DDY case 31: { - const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); - value = writeLocal(inValue.Type, String::Format(TEXT("ddy({0})"), inValue.Value), node); + if (_treeType == MaterialTreeType::PixelShader) + { + const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + value = writeLocal(inValue.Type, String::Format(TEXT("ddy({0})"), inValue.Value), node); + } + else + { + // No derivatives support in VS/DS + value = Value::Zero; + } break; } // Sign From 0b0115aa10d117d50652468cd81f64136c5a8545 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 29 Mar 2024 18:46:00 +0100 Subject: [PATCH 197/198] Fix missing scripting api --- Source/Engine/Graphics/Models/SkeletonData.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Graphics/Models/SkeletonData.h b/Source/Engine/Graphics/Models/SkeletonData.h index c0e3d107f..16e2cf732 100644 --- a/Source/Engine/Graphics/Models/SkeletonData.h +++ b/Source/Engine/Graphics/Models/SkeletonData.h @@ -11,7 +11,7 @@ /// /// Describes a single skeleton node data. Used by the runtime. /// -API_STRUCT() struct SkeletonNode +API_STRUCT() struct FLAXENGINE_API SkeletonNode { DECLARE_SCRIPTING_TYPE_MINIMAL(SkeletonNode); @@ -34,7 +34,7 @@ API_STRUCT() struct SkeletonNode /// /// Describes a single skeleton bone data. Used by the runtime. Skeleton bones are subset of the skeleton nodes collection that are actually used by the skinned model meshes. /// -API_STRUCT() struct SkeletonBone +API_STRUCT() struct FLAXENGINE_API SkeletonBone { DECLARE_SCRIPTING_TYPE_MINIMAL(SkeletonBone); @@ -71,7 +71,7 @@ struct TIsPODType /// /// Bones are ordered so that parents always come first, allowing for hierarchical updates in a simple loop. /// -class SkeletonData +class FLAXENGINE_API SkeletonData { public: /// From ce07edd1ec093fa3c7696c29d401d0292b0005c5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 29 Mar 2024 21:08:43 +0100 Subject: [PATCH 198/198] Fix packaging for macOS --- Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs b/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs index ee682576b..98a9ef530 100644 --- a/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs +++ b/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs @@ -345,12 +345,12 @@ namespace Flax.Deploy // Optimize package size Utilities.Run("strip", "FlaxEditor", null, dst, Utilities.RunOptions.None); - Utilities.Run("strip", "FlaxEditor.dylib", null, dst, Utilities.RunOptions.None); + Utilities.Run("strip", "FlaxEngine.dylib", null, dst, Utilities.RunOptions.None); Utilities.Run("strip", "libMoltenVK.dylib", null, dst, Utilities.RunOptions.None); // Sign binaries CodeSign(Path.Combine(dst, "FlaxEditor")); - CodeSign(Path.Combine(dst, "FlaxEditor.dylib")); + CodeSign(Path.Combine(dst, "FlaxEngine.dylib")); CodeSign(Path.Combine(dst, "libMoltenVK.dylib")); } }