Merge remote-tracking branch 'origin/1.11' into sdl_platform_1.11

# Conflicts:
#	Source/Engine/Platform/Windows/WindowsPlatform.cpp
#	Source/Tools/Flax.Build/Build/ProjectTarget.cs
#	Source/Tools/Flax.Build/Configuration.cs
This commit is contained in:
Ari Vuollet
2025-09-07 19:31:16 +03:00
470 changed files with 53354 additions and 3349 deletions
Binary file not shown.
@@ -28,6 +28,13 @@ TextureCube SkyLightTexture : register(t__SRV__);
Buffer<float4> ShadowsBuffer : register(t__SRV__); Buffer<float4> ShadowsBuffer : register(t__SRV__);
Texture2D<float> ShadowMap : register(t__SRV__); Texture2D<float> ShadowMap : register(t__SRV__);
@4// Forward Shading: Utilities @4// Forward Shading: Utilities
// Public accessors for lighting data, use them as data binding might change but those methods will remain.
LightData GetDirectionalLight() { return DirectionalLight; }
LightData GetSkyLight() { return SkyLight; }
ProbeData GetEnvironmentProbe() { return EnvironmentProbe; }
ExponentialHeightFogData GetExponentialHeightFog() { return ExponentialHeightFog; }
uint GetLocalLightsCount() { return LocalLightsCount; }
LightData GetLocalLight(uint i) { return LocalLights[i]; }
@5// Forward Shading: Shaders @5// Forward Shading: Shaders
// Pixel Shader function for Forward Pass // Pixel Shader function for Forward Pass
@@ -76,9 +83,8 @@ void PS_Forward(
gBuffer.ShadingModel = MATERIAL_SHADING_MODEL; gBuffer.ShadingModel = MATERIAL_SHADING_MODEL;
// Calculate lighting from a single directional light // Calculate lighting from a single directional light
float4 shadowMask = 1.0f;
ShadowSample shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, gBuffer); ShadowSample shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, gBuffer);
shadowMask = GetShadowMask(shadow); float4 shadowMask = GetShadowMask(shadow);
float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false); float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false);
// Calculate lighting from sky light // Calculate lighting from sky light
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -2
View File
@@ -2,9 +2,9 @@
"Name": "Flax", "Name": "Flax",
"Version": { "Version": {
"Major": 1, "Major": 1,
"Minor": 10, "Minor": 11,
"Revision": 0, "Revision": 0,
"Build": 6705 "Build": 6800
}, },
"Company": "Flax", "Company": "Flax",
"Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.", "Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.",
@@ -174,7 +174,9 @@ void EditorAnalytics::StartSession()
// Bind events // Bind events
GameCooker::OnEvent.Bind<RegisterGameCookingStart>(); GameCooker::OnEvent.Bind<RegisterGameCookingStart>();
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Bind<RegisterLightmapsBuildingStart>(); ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Bind<RegisterLightmapsBuildingStart>();
#if LOG_ENABLE
Log::Logger::OnError.Bind<RegisterError>(); Log::Logger::OnError.Bind<RegisterError>();
#endif
} }
void EditorAnalytics::EndSession() void EditorAnalytics::EndSession()
@@ -187,7 +189,9 @@ void EditorAnalytics::EndSession()
// Unbind events // Unbind events
GameCooker::OnEvent.Unbind<RegisterGameCookingStart>(); GameCooker::OnEvent.Unbind<RegisterGameCookingStart>();
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Unbind<RegisterLightmapsBuildingStart>(); ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Unbind<RegisterLightmapsBuildingStart>();
#if LOG_ENABLE
Log::Logger::OnError.Unbind<RegisterError>(); Log::Logger::OnError.Unbind<RegisterError>();
#endif
// End session // End session
{ {
+2 -7
View File
@@ -20,13 +20,6 @@ class PlatformTools;
#define GAME_BUILD_DOTNET_RUNTIME_MAX_VER 9 #define GAME_BUILD_DOTNET_RUNTIME_MAX_VER 9
#endif #endif
#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=" MACRO_TO_STR(GAME_BUILD_DOTNET_RUNTIME_MIN_VER))
#else
#define GAME_BUILD_DOTNET_VER TEXT("")
#endif
/// <summary> /// <summary>
/// Game building options. Used as flags. /// Game building options. Used as flags.
/// </summary> /// </summary>
@@ -374,6 +367,8 @@ public:
/// </summary> /// </summary>
void GetBuildPlatformName(const Char*& platform, const Char*& architecture) const; void GetBuildPlatformName(const Char*& platform, const Char*& architecture) const;
String GetDotnetCommandArg() const;
public: public:
/// <summary> /// <summary>
+18 -3
View File
@@ -30,6 +30,7 @@
#include "Engine/Scripting/ManagedCLR/MAssembly.h" #include "Engine/Scripting/ManagedCLR/MAssembly.h"
#include "Engine/Content/JsonAsset.h" #include "Engine/Content/JsonAsset.h"
#include "Engine/Content/AssetReference.h" #include "Engine/Content/AssetReference.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if PLATFORM_TOOLS_WINDOWS #if PLATFORM_TOOLS_WINDOWS
#include "Platform/Windows/WindowsPlatformTools.h" #include "Platform/Windows/WindowsPlatformTools.h"
#include "Engine/Platform/Windows/WindowsPlatformSettings.h" #include "Engine/Platform/Windows/WindowsPlatformSettings.h"
@@ -311,6 +312,14 @@ void CookingData::GetBuildPlatformName(const Char*& platform, const Char*& archi
} }
} }
String CookingData::GetDotnetCommandArg() const
{
int32 version = Tools->GetDotnetVersion();
if (version == 0)
return String::Empty;
return String::Format(TEXT("-dotnet={}"), version);
}
void CookingData::StepProgress(const String& info, const float stepProgress) const void CookingData::StepProgress(const String& info, const float stepProgress) const
{ {
const float singleStepProgress = 1.0f / (StepsCount + 1); const float singleStepProgress = 1.0f / (StepsCount + 1);
@@ -380,6 +389,7 @@ bool GameCooker::IsCancelRequested()
PlatformTools* GameCooker::GetTools(BuildPlatform platform) PlatformTools* GameCooker::GetTools(BuildPlatform platform)
{ {
PROFILE_MEM(Editor);
PlatformTools* result = nullptr; PlatformTools* result = nullptr;
if (!Tools.TryGet(platform, result)) if (!Tools.TryGet(platform, result))
{ {
@@ -471,6 +481,7 @@ bool GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration,
LOG(Error, "Build platform {0} is not supported.", ::ToString(platform)); LOG(Error, "Build platform {0} is not supported.", ::ToString(platform));
return true; return true;
} }
PROFILE_MEM(Editor);
// Setup // Setup
CancelFlag = 0; CancelFlag = 0;
@@ -624,6 +635,7 @@ void GameCookerImpl::ReportProgress(const String& info, float totalProgress)
void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets) void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets)
{ {
PROFILE_MEM(Editor);
if (Internal_OnCollectAssets == nullptr) if (Internal_OnCollectAssets == nullptr)
{ {
auto c = GameCooker::GetStaticClass(); auto c = GameCooker::GetStaticClass();
@@ -651,6 +663,7 @@ void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets)
bool GameCookerImpl::Build() bool GameCookerImpl::Build()
{ {
PROFILE_MEM(Editor);
CookingData& data = *Data; CookingData& data = *Data;
LOG(Info, "Starting Game Cooker..."); LOG(Info, "Starting Game Cooker...");
LOG(Info, "Platform: {0}, Configuration: {2}, Options: {1}", ::ToString(data.Platform), (int32)data.Options, ::ToString(data.Configuration)); LOG(Info, "Platform: {0}, Configuration: {2}, Options: {1}", ::ToString(data.Platform), (int32)data.Options, ::ToString(data.Configuration));
@@ -670,8 +683,7 @@ bool GameCookerImpl::Build()
MCore::Thread::Attach(); MCore::Thread::Attach();
// Build Started // Build start
if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook))
{ {
CallEvent(GameCooker::EventType::BuildStarted); CallEvent(GameCooker::EventType::BuildStarted);
data.Tools->OnBuildStarted(data); data.Tools->OnBuildStarted(data);
@@ -744,8 +756,8 @@ bool GameCookerImpl::Build()
} }
IsRunning = false; IsRunning = false;
CancelFlag = 0; CancelFlag = 0;
if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook))
{ {
// Build end
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++) for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
Steps[stepIndex]->OnBuildEnded(data, failed); Steps[stepIndex]->OnBuildEnded(data, failed);
data.Tools->OnBuildEnded(data, failed); data.Tools->OnBuildEnded(data, failed);
@@ -778,6 +790,8 @@ int32 GameCookerImpl::ThreadFunction()
bool GameCookerService::Init() bool GameCookerService::Init()
{ {
PROFILE_MEM(Editor);
auto editorAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly; auto editorAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
editorAssembly->Unloading.Bind(OnEditorAssemblyUnloading); editorAssembly->Unloading.Bind(OnEditorAssemblyUnloading);
GameCooker::OnCollectAssets.Bind(OnCollectAssets); GameCooker::OnCollectAssets.Bind(OnCollectAssets);
@@ -789,6 +803,7 @@ void GameCookerService::Update()
{ {
if (IsRunning) if (IsRunning)
{ {
PROFILE_MEM(Editor);
ScopeLock lock(ProgressLocker); ScopeLock lock(ProgressLocker);
if (ProgressMsg.HasChars()) if (ProgressMsg.HasChars())
@@ -195,4 +195,9 @@ bool GDKPlatformTools::OnPostProcess(CookingData& data, GDKPlatformSettings* pla
return false; return false;
} }
int32 GDKPlatformTools::GetDotnetVersion() const
{
return GAME_BUILD_DOTNET_RUNTIME_MIN_VER;
}
#endif #endif
@@ -26,6 +26,7 @@ public:
public: public:
// [PlatformTools] // [PlatformTools]
int32 GetDotnetVersion() const override;
DotNetAOTModes UseAOT() const override; DotNetAOTModes UseAOT() const override;
bool OnDeployBinaries(CookingData& data) override; bool OnDeployBinaries(CookingData& data) override;
}; };
@@ -186,7 +186,7 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
ADD_ENTRY("CFBundlePackageType", "APPL"); ADD_ENTRY("CFBundlePackageType", "APPL");
ADD_ENTRY("NSPrincipalClass", "NSApplication"); ADD_ENTRY("NSPrincipalClass", "NSApplication");
ADD_ENTRY("LSApplicationCategoryType", "public.app-category.games"); ADD_ENTRY("LSApplicationCategoryType", "public.app-category.games");
ADD_ENTRY("LSMinimumSystemVersion", "10.15"); ADD_ENTRY("LSMinimumSystemVersion", "13");
ADD_ENTRY("CFBundleIconFile", "icon.icns"); ADD_ENTRY("CFBundleIconFile", "icon.icns");
ADD_ENTRY_STR("CFBundleExecutable", executableName); ADD_ENTRY_STR("CFBundleExecutable", executableName);
ADD_ENTRY_STR("CFBundleIdentifier", appIdentifier); ADD_ENTRY_STR("CFBundleIdentifier", appIdentifier);
@@ -231,6 +231,8 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
LOG(Info, "Building app package..."); LOG(Info, "Building app package...");
{ {
const String dmgPath = data.OriginalOutputPath / appName + TEXT(".dmg"); const String dmgPath = data.OriginalOutputPath / appName + TEXT(".dmg");
if (FileSystem::FileExists(dmgPath))
FileSystem::DeleteFile(dmgPath);
CreateProcessSettings procSettings; CreateProcessSettings procSettings;
procSettings.HiddenWindow = true; procSettings.HiddenWindow = true;
procSettings.WorkingDirectory = data.OriginalOutputPath; procSettings.WorkingDirectory = data.OriginalOutputPath;
@@ -528,6 +528,9 @@ bool WindowsPlatformTools::OnDeployBinaries(CookingData& data)
void WindowsPlatformTools::OnBuildStarted(CookingData& data) void WindowsPlatformTools::OnBuildStarted(CookingData& data)
{ {
if (EnumHasAllFlags(data.Options, BuildOptions::NoCook))
return;
// Remove old executable // Remove old executable
Array<String> files; Array<String> files;
FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*.exe"), DirectorySearchOption::TopDirectoryOnly); FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*.exe"), DirectorySearchOption::TopDirectoryOnly);
+14
View File
@@ -70,6 +70,20 @@ public:
/// </summary> /// </summary>
virtual ArchitectureType GetArchitecture() const = 0; virtual ArchitectureType GetArchitecture() const = 0;
/// <summary>
/// Gets the .Net version to use for the cooked game.
/// </summary>
virtual int32 GetDotnetVersion() const
{
#if OFFICIAL_BUILD
// Use the fixed .NET SDK version in packaged builds for compatibility (FlaxGame is precompiled with it)
return GAME_BUILD_DOTNET_RUNTIME_MIN_VER;
#else
// Use the highest version found on a system (Flax.Build will decide)
return 0;
#endif
}
/// <summary> /// <summary>
/// Gets the value indicating whenever platform requires AOT (needs C# assemblies to be precompiled). /// Gets the value indicating whenever platform requires AOT (needs C# assemblies to be precompiled).
/// </summary> /// </summary>
@@ -189,7 +189,7 @@ bool CompileScriptsStep::Perform(CookingData& data)
const String logFile = data.CacheDirectory / TEXT("CompileLog.txt"); const String logFile = data.CacheDirectory / TEXT("CompileLog.txt");
auto args = String::Format( auto args = String::Format(
TEXT("-log -logfile=\"{4}\" -build -mutex -buildtargets={0} -platform={1} -arch={2} -configuration={3} -aotMode={5} {6}"), TEXT("-log -logfile=\"{4}\" -build -mutex -buildtargets={0} -platform={1} -arch={2} -configuration={3} -aotMode={5} {6}"),
target, platform, architecture, configuration, logFile, ToString(data.Tools->UseAOT()), GAME_BUILD_DOTNET_VER); target, platform, architecture, configuration, logFile, ToString(data.Tools->UseAOT()), data.GetDotnetCommandArg());
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS
if (data.Platform == BuildPlatform::LinuxX64) if (data.Platform == BuildPlatform::LinuxX64)
#elif PLATFORM_LINUX #elif PLATFORM_LINUX
@@ -36,6 +36,7 @@
#include "Engine/Engine/Base/GameBase.h" #include "Engine/Engine/Base/GameBase.h"
#include "Engine/Engine/Globals.h" #include "Engine/Engine/Globals.h"
#include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Tools/TextureTool/TextureTool.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/Enums.h" #include "Engine/Scripting/Enums.h"
#if PLATFORM_TOOLS_WINDOWS #if PLATFORM_TOOLS_WINDOWS
@@ -88,7 +88,7 @@ bool DeployDataStep::Perform(CookingData& data)
{ {
// Ask Flax.Build to provide .NET SDK location for the current platform // Ask Flax.Build to provide .NET SDK location for the current platform
String sdks; String sdks;
bool failed = ScriptsBuilder::RunBuildTool(String::Format(TEXT("-log -logMessagesOnly -logFileWithConsole -logfile=SDKs.txt -printSDKs {}"), GAME_BUILD_DOTNET_VER), data.CacheDirectory); bool failed = ScriptsBuilder::RunBuildTool(String::Format(TEXT("-log -logMessagesOnly -logFileWithConsole -logfile=SDKs.txt -printSDKs {}"), data.GetDotnetCommandArg()), data.CacheDirectory);
failed |= File::ReadAllText(data.CacheDirectory / TEXT("SDKs.txt"), sdks); failed |= File::ReadAllText(data.CacheDirectory / TEXT("SDKs.txt"), sdks);
int32 idx = sdks.Find(TEXT("DotNetSdk, "), StringSearchCase::CaseSensitive); int32 idx = sdks.Find(TEXT("DotNetSdk, "), StringSearchCase::CaseSensitive);
if (idx != -1) if (idx != -1)
@@ -200,7 +200,7 @@ bool DeployDataStep::Perform(CookingData& data)
String sdks; String sdks;
const Char *platformName, *archName; const Char *platformName, *archName;
data.GetBuildPlatformName(platformName, archName); data.GetBuildPlatformName(platformName, archName);
String args = String::Format(TEXT("-log -logMessagesOnly -logFileWithConsole -logfile=SDKs.txt -printDotNetRuntime -platform={} -arch={} {}"), platformName, archName, GAME_BUILD_DOTNET_VER); String args = String::Format(TEXT("-log -logMessagesOnly -logFileWithConsole -logfile=SDKs.txt -printDotNetRuntime -platform={} -arch={} {}"), platformName, archName, data.GetDotnetCommandArg());
bool failed = ScriptsBuilder::RunBuildTool(args, data.CacheDirectory); bool failed = ScriptsBuilder::RunBuildTool(args, data.CacheDirectory);
failed |= File::ReadAllText(data.CacheDirectory / TEXT("SDKs.txt"), sdks); failed |= File::ReadAllText(data.CacheDirectory / TEXT("SDKs.txt"), sdks);
Array<String> parts; Array<String> parts;
@@ -244,10 +244,13 @@ bool DeployDataStep::Perform(CookingData& data)
} }
if (version.IsEmpty()) if (version.IsEmpty())
{ {
int32 minVer = GAME_BUILD_DOTNET_RUNTIME_MIN_VER, maxVer = GAME_BUILD_DOTNET_RUNTIME_MAX_VER;
if (srcDotnetFromEngine) if (srcDotnetFromEngine)
{ {
// Detect version from runtime files inside Engine Platform folder // Detect version from runtime files inside Engine Platform folder
for (int32 i = GAME_BUILD_DOTNET_RUNTIME_MAX_VER; i >= GAME_BUILD_DOTNET_RUNTIME_MIN_VER; i--) if (data.Tools->GetDotnetVersion() != 0)
minVer = maxVer = data.Tools->GetDotnetVersion();
for (int32 i = maxVer; i >= minVer; i--)
{ {
// Check runtime files inside Engine Platform folder // Check runtime files inside Engine Platform folder
String testPath1 = srcDotnet / String::Format(TEXT("lib/net{}.0"), i); String testPath1 = srcDotnet / String::Format(TEXT("lib/net{}.0"), i);
@@ -262,7 +265,7 @@ bool DeployDataStep::Perform(CookingData& data)
} }
if (version.IsEmpty()) if (version.IsEmpty())
{ {
data.Error(String::Format(TEXT("Failed to find supported .NET {} version for the current host platform."), GAME_BUILD_DOTNET_RUNTIME_MIN_VER)); data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for the current host platform."), maxVer, minVer));
return true; return true;
} }
} }
@@ -364,7 +367,7 @@ bool DeployDataStep::Perform(CookingData& data)
const String logFile = data.CacheDirectory / TEXT("StripDotnetLibs.txt"); const String logFile = data.CacheDirectory / TEXT("StripDotnetLibs.txt");
String args = String::Format( String args = String::Format(
TEXT("-log -logfile=\"{}\" -runDotNetClassLibStripping -mutex -binaries=\"{}\" {}"), TEXT("-log -logfile=\"{}\" -runDotNetClassLibStripping -mutex -binaries=\"{}\" {}"),
logFile, data.DataOutputPath, GAME_BUILD_DOTNET_VER); logFile, data.DataOutputPath, data.GetDotnetCommandArg());
for (const String& define : data.CustomDefines) for (const String& define : data.CustomDefines)
{ {
args += TEXT(" -D"); args += TEXT(" -D");
@@ -12,7 +12,7 @@
void PrecompileAssembliesStep::OnBuildStarted(CookingData& data) void PrecompileAssembliesStep::OnBuildStarted(CookingData& data)
{ {
const DotNetAOTModes aotMode = data.Tools->UseAOT(); const DotNetAOTModes aotMode = data.Tools->UseAOT();
if (aotMode == DotNetAOTModes::None) if (aotMode == DotNetAOTModes::None || EnumHasAllFlags(data.Options, BuildOptions::NoCook))
return; return;
const auto& buildSettings = *BuildSettings::Get(); const auto& buildSettings = *BuildSettings::Get();
@@ -69,7 +69,7 @@ bool PrecompileAssembliesStep::Perform(CookingData& data)
const String logFile = data.CacheDirectory / TEXT("AOTLog.txt"); const String logFile = data.CacheDirectory / TEXT("AOTLog.txt");
String args = String::Format( String args = String::Format(
TEXT("-log -logfile=\"{}\" -runDotNetAOT -mutex -platform={} -arch={} -configuration={} -aotMode={} -binaries=\"{}\" -intermediate=\"{}\" {}"), TEXT("-log -logfile=\"{}\" -runDotNetAOT -mutex -platform={} -arch={} -configuration={} -aotMode={} -binaries=\"{}\" -intermediate=\"{}\" {}"),
logFile, platform, architecture, configuration, ToString(aotMode), data.DataOutputPath, data.ManagedCodeOutputPath, GAME_BUILD_DOTNET_VER); logFile, platform, architecture, configuration, ToString(aotMode), data.DataOutputPath, data.ManagedCodeOutputPath, data.GetDotnetCommandArg());
if (!buildSettings.SkipUnusedDotnetLibsPackaging) if (!buildSettings.SkipUnusedDotnetLibsPackaging)
args += TEXT(" -skipUnusedDotnetLibs=false"); // Run AOT on whole class library (not just used libs) args += TEXT(" -skipUnusedDotnetLibs=false"); // Run AOT on whole class library (not just used libs)
for (const String& define : data.CustomDefines) for (const String& define : data.CustomDefines)
@@ -6,6 +6,8 @@
#include "Engine/Core/Types/TimeSpan.h" #include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Core/Types/Stopwatch.h" #include "Engine/Core/Types/Stopwatch.h"
#include "Engine/Core/Collections/Dictionary.h" #include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/BinaryModule.h" #include "Engine/Scripting/BinaryModule.h"
@@ -69,6 +71,7 @@ MTypeObject* CustomEditorsUtil::GetCustomEditor(MTypeObject* refType)
bool CustomEditorsUtilService::Init() bool CustomEditorsUtilService::Init()
{ {
PROFILE_MEM(Editor);
TRACK_ASSEMBLY(((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly); TRACK_ASSEMBLY(((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly);
Scripting::BinaryModuleLoaded.Bind(&OnBinaryModuleLoaded); Scripting::BinaryModuleLoaded.Bind(&OnBinaryModuleLoaded);
@@ -77,6 +80,8 @@ bool CustomEditorsUtilService::Init()
void OnAssemblyLoaded(MAssembly* assembly) void OnAssemblyLoaded(MAssembly* assembly)
{ {
PROFILE_CPU_NAMED("CustomEditors.OnAssemblyLoaded");
PROFILE_MEM(Editor);
Stopwatch stopwatch; Stopwatch stopwatch;
// Prepare FlaxEngine // Prepare FlaxEngine
@@ -22,7 +22,7 @@ internal class UIControlRefPickerControl : FlaxObjectRefPickerControl
/// <inheritdoc /> /// <inheritdoc />
protected override bool IsValid(Object obj) protected override bool IsValid(Object obj)
{ {
return obj == null || (obj is UIControl control && control.Control.GetType() == ControlType); return obj == null || (obj is UIControl control && ControlType.IsAssignableFrom(control.Control.GetType()));
} }
} }
+10
View File
@@ -20,6 +20,7 @@
#include "Engine/Engine/Engine.h" #include "Engine/Engine/Engine.h"
#include "Engine/ShadowsOfMordor/Builder.h" #include "Engine/ShadowsOfMordor/Builder.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "FlaxEngine.Gen.h" #include "FlaxEngine.Gen.h"
#if PLATFORM_LINUX #if PLATFORM_LINUX
#include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Tools/TextureTool/TextureTool.h"
@@ -47,6 +48,7 @@ void Editor::CloseSplashScreen()
bool Editor::CheckProjectUpgrade() bool Editor::CheckProjectUpgrade()
{ {
PROFILE_MEM(Editor);
const auto versionFilePath = Globals::ProjectCacheFolder / TEXT("version"); const auto versionFilePath = Globals::ProjectCacheFolder / TEXT("version");
// Load version cache file // Load version cache file
@@ -366,6 +368,8 @@ bool Editor::BackupProject()
int32 Editor::LoadProduct() int32 Editor::LoadProduct()
{ {
PROFILE_MEM(Editor);
// Flax Editor product // Flax Editor product
Globals::ProductName = TEXT("Flax Editor"); Globals::ProductName = TEXT("Flax Editor");
Globals::CompanyName = TEXT("Flax"); Globals::CompanyName = TEXT("Flax");
@@ -626,6 +630,7 @@ int32 Editor::LoadProduct()
Window* Editor::CreateMainWindow() Window* Editor::CreateMainWindow()
{ {
PROFILE_MEM(Editor);
Window* window = Managed->GetMainWindow(); Window* window = Managed->GetMainWindow();
#if PLATFORM_LINUX #if PLATFORM_LINUX
@@ -662,6 +667,7 @@ bool Editor::Init()
return true; return true;
} }
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Editor);
// If during last lightmaps baking engine crashed we could try to restore the progress // If during last lightmaps baking engine crashed we could try to restore the progress
ShadowsOfMordor::Builder::Instance()->CheckIfRestoreState(); ShadowsOfMordor::Builder::Instance()->CheckIfRestoreState();
@@ -693,11 +699,13 @@ bool Editor::Init()
void Editor::BeforeRun() void Editor::BeforeRun()
{ {
PROFILE_MEM(Editor);
Managed->BeforeRun(); Managed->BeforeRun();
} }
void Editor::BeforeExit() void Editor::BeforeExit()
{ {
PROFILE_MEM(Editor);
CloseSplashScreen(); CloseSplashScreen();
Managed->Exit(); Managed->Exit();
@@ -708,6 +716,8 @@ void Editor::BeforeExit()
void EditorImpl::OnUpdate() void EditorImpl::OnUpdate()
{ {
PROFILE_MEM(Editor);
// Update c# editor // Update c# editor
Editor::Managed->Update(); Editor::Managed->Update();
+1 -1
View File
@@ -51,7 +51,7 @@ namespace FlaxEditor.GUI
/// <summary> /// <summary>
/// The column title horizontal text alignment /// The column title horizontal text alignment
/// </summary> /// </summary>
public TextAlignment TitleAlignment = TextAlignment.Near; public TextAlignment TitleAlignment = TextAlignment.Center;
/// <summary> /// <summary>
/// The column title margin. /// The column title margin.
@@ -117,9 +117,10 @@ namespace FlaxEditor.GUI.ContextMenu
public ContextMenuBase() public ContextMenuBase()
: base(0, 0, 120, 32) : base(0, 0, 120, 32)
{ {
_direction = ContextMenuDirection.RightDown;
Visible = false; Visible = false;
AutoFocus = true;
_direction = ContextMenuDirection.RightDown;
_isSubMenu = true; _isSubMenu = true;
} }
@@ -76,6 +76,8 @@ namespace FlaxEditor.GUI.Dialogs
public ColorSelector(float wheelSize) public ColorSelector(float wheelSize)
: base(0, 0, wheelSize, wheelSize) : base(0, 0, wheelSize, wheelSize)
{ {
AutoFocus = true;
_colorWheelSprite = Editor.Instance.Icons.ColorWheel128; _colorWheelSprite = Editor.Instance.Icons.ColorWheel128;
_wheelRect = new Rectangle(0, 0, wheelSize, wheelSize); _wheelRect = new Rectangle(0, 0, wheelSize, wheelSize);
} }
@@ -71,8 +71,6 @@ namespace FlaxEditor.GUI.Docking
internal DockPanelProxy(DockPanel panel) internal DockPanelProxy(DockPanel panel)
: base(0, 0, 64, 64) : base(0, 0, 64, 64)
{ {
AutoFocus = false;
_panel = panel; _panel = panel;
AnchorPreset = AnchorPresets.StretchAll; AnchorPreset = AnchorPresets.StretchAll;
Offsets = Margin.Zero; Offsets = Margin.Zero;
+2
View File
@@ -368,6 +368,8 @@ namespace FlaxEditor.GUI.Input
public SliderControl(float value, float x = 0, float y = 0, float width = 120, float min = float.MinValue, float max = float.MaxValue) public SliderControl(float value, float x = 0, float y = 0, float width = 120, float min = float.MinValue, float max = float.MaxValue)
: base(x, y, width, TextBox.DefaultHeight) : base(x, y, width, TextBox.DefaultHeight)
{ {
AutoFocus = true;
_min = min; _min = min;
_max = max; _max = max;
_value = Mathf.Clamp(value, min, max); _value = Mathf.Clamp(value, min, max);
+2
View File
@@ -319,6 +319,8 @@ namespace FlaxEditor.GUI.Tree
public TreeNode(bool canChangeOrder, SpriteHandle iconCollapsed, SpriteHandle iconOpened) public TreeNode(bool canChangeOrder, SpriteHandle iconCollapsed, SpriteHandle iconOpened)
: base(0, 0, 64, 16) : base(0, 0, 64, 16)
{ {
AutoFocus = true;
_canChangeOrder = canChangeOrder; _canChangeOrder = canChangeOrder;
_animationProgress = 1.0f; _animationProgress = 1.0f;
_cachedHeight = _headerHeight; _cachedHeight = _headerHeight;
+3
View File
@@ -155,6 +155,7 @@ namespace FlaxEditor.Gizmo
// Ensure player is not moving objects // Ensure player is not moving objects
if (ActiveAxis != Axis.None) if (ActiveAxis != Axis.None)
return; return;
Profiler.BeginEvent("Pick");
// Get mouse ray and try to hit any object // Get mouse ray and try to hit any object
var ray = Owner.MouseRay; var ray = Owner.MouseRay;
@@ -243,6 +244,8 @@ namespace FlaxEditor.Gizmo
{ {
sceneEditing.Deselect(); sceneEditing.Deselect();
} }
Profiler.EndEvent();
} }
/// <inheritdoc /> /// <inheritdoc />
+9 -4
View File
@@ -14,6 +14,7 @@
#include "Engine/Platform/WindowsManager.h" #include "Engine/Platform/WindowsManager.h"
#include "Engine/Content/Assets/VisualScript.h" #include "Engine/Content/Assets/VisualScript.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Level/Actor.h"
#include "Engine/CSG/CSGBuilder.h" #include "Engine/CSG/CSGBuilder.h"
#include "Engine/Engine/CommandLine.h" #include "Engine/Engine/CommandLine.h"
#include "Engine/Renderer/ProbesRenderer.h" #include "Engine/Renderer/ProbesRenderer.h"
@@ -75,7 +76,7 @@ void OnLightmapsBuildFinished(bool failed)
OnLightmapsBake(ShadowsOfMordor::BuildProgressStep::GenerateLightmapCharts, 0, 0, false); OnLightmapsBake(ShadowsOfMordor::BuildProgressStep::GenerateLightmapCharts, 0, 0, false);
} }
void OnBakeEvent(bool started, const ProbesRenderer::Entry& e) void OnBakeEvent(bool started, Actor* e)
{ {
if (Internal_EnvProbeBake == nullptr) if (Internal_EnvProbeBake == nullptr)
{ {
@@ -83,7 +84,7 @@ void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
ASSERT(Internal_EnvProbeBake); ASSERT(Internal_EnvProbeBake);
} }
MObject* probeObj = e.Actor ? e.Actor->GetManagedInstance() : nullptr; MObject* probeObj = e ? e->GetManagedInstance() : nullptr;
MainThreadManagedInvokeAction::ParamsBuilder params; MainThreadManagedInvokeAction::ParamsBuilder params;
params.AddParam(started); params.AddParam(started);
@@ -91,12 +92,12 @@ void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
MainThreadManagedInvokeAction::Invoke(Internal_EnvProbeBake, params); MainThreadManagedInvokeAction::Invoke(Internal_EnvProbeBake, params);
} }
void OnRegisterBake(const ProbesRenderer::Entry& e) void OnRegisterBake(Actor* e)
{ {
OnBakeEvent(true, e); OnBakeEvent(true, e);
} }
void OnFinishBake(const ProbesRenderer::Entry& e) void OnFinishBake(Actor* e)
{ {
OnBakeEvent(false, e); OnBakeEvent(false, e);
} }
@@ -157,7 +158,9 @@ ManagedEditor::ManagedEditor()
lightmapsBuilder->OnBuildProgress.Bind<OnLightmapsBuildProgress>(); lightmapsBuilder->OnBuildProgress.Bind<OnLightmapsBuildProgress>();
lightmapsBuilder->OnBuildFinished.Bind<OnLightmapsBuildFinished>(); lightmapsBuilder->OnBuildFinished.Bind<OnLightmapsBuildFinished>();
CSG::Builder::OnBrushModified.Bind<OnBrushModified>(); CSG::Builder::OnBrushModified.Bind<OnBrushModified>();
#if LOG_ENABLE
Log::Logger::OnMessage.Bind<OnLogMessage>(); Log::Logger::OnMessage.Bind<OnLogMessage>();
#endif
VisualScripting::DebugFlow.Bind<OnVisualScriptingDebugFlow>(); VisualScripting::DebugFlow.Bind<OnVisualScriptingDebugFlow>();
} }
@@ -173,7 +176,9 @@ ManagedEditor::~ManagedEditor()
lightmapsBuilder->OnBuildProgress.Unbind<OnLightmapsBuildProgress>(); lightmapsBuilder->OnBuildProgress.Unbind<OnLightmapsBuildProgress>();
lightmapsBuilder->OnBuildFinished.Unbind<OnLightmapsBuildFinished>(); lightmapsBuilder->OnBuildFinished.Unbind<OnLightmapsBuildFinished>();
CSG::Builder::OnBrushModified.Unbind<OnBrushModified>(); CSG::Builder::OnBrushModified.Unbind<OnBrushModified>();
#if LOG_ENABLE
Log::Logger::OnMessage.Unbind<OnLogMessage>(); Log::Logger::OnMessage.Unbind<OnLogMessage>();
#endif
VisualScripting::DebugFlow.Unbind<OnVisualScriptingDebugFlow>(); VisualScripting::DebugFlow.Unbind<OnVisualScriptingDebugFlow>();
} }
@@ -711,7 +711,11 @@ namespace FlaxEditor.Modules
private void OnActorChildNodesDispose(ActorNode node) private void OnActorChildNodesDispose(ActorNode node)
{ {
if (Selection.Count == 0)
return;
// TODO: cache if selection contains any actor child node and skip this loop if no need to iterate // TODO: cache if selection contains any actor child node and skip this loop if no need to iterate
// TODO: or build a hash set with selected nodes for quick O(1) checks (cached until selection changes)
// Deselect child nodes // Deselect child nodes
for (int i = 0; i < node.ChildNodes.Count; i++) for (int i = 0; i < node.ChildNodes.Count; i++)
+45
View File
@@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph;
using FlaxEditor.SceneGraph.Actors; using FlaxEditor.SceneGraph.Actors;
using FlaxEngine; using FlaxEngine;
@@ -662,6 +663,48 @@ namespace FlaxEditor.Modules
//node?.TreeNode.OnActiveChanged(); //node?.TreeNode.OnActiveChanged();
} }
private void OnActorDestroyChildren(Actor actor)
{
// Instead of doing OnActorParentChanged for every child lets remove all of them at once from that actor
ActorNode node = GetActorNode(actor);
if (node != null)
{
if (Editor.SceneEditing.HasSthSelected)
{
// Clear selection if one of the removed actors is selected
var selection = new HashSet<Actor>();
foreach (var e in Editor.SceneEditing.Selection)
{
if (e is ActorNode q && q.Actor)
selection.Add(q.Actor);
}
var count = actor.ChildrenCount;
for (int i = 0; i < count; i++)
{
var child = actor.GetChild(i);
if (selection.Contains(child))
{
Editor.SceneEditing.Deselect();
break;
}
}
}
// Remove all child nodes (upfront remove all nodes to run faster)
for (int i = 0; i < node.ChildNodes.Count; i++)
{
if (node.ChildNodes[i] is ActorNode child)
child.parentNode = null;
}
node.TreeNode.DisposeChildren();
for (int i = 0; i < node.ChildNodes.Count; i++)
{
node.ChildNodes[i].Dispose();
}
node.ChildNodes.Clear();
}
}
/// <summary> /// <summary>
/// Gets the actor node. /// Gets the actor node.
/// </summary> /// </summary>
@@ -713,6 +756,7 @@ namespace FlaxEditor.Modules
Level.ActorOrderInParentChanged += OnActorOrderInParentChanged; Level.ActorOrderInParentChanged += OnActorOrderInParentChanged;
Level.ActorNameChanged += OnActorNameChanged; Level.ActorNameChanged += OnActorNameChanged;
Level.ActorActiveChanged += OnActorActiveChanged; Level.ActorActiveChanged += OnActorActiveChanged;
Level.ActorDestroyChildren += OnActorDestroyChildren;
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -730,6 +774,7 @@ namespace FlaxEditor.Modules
Level.ActorOrderInParentChanged -= OnActorOrderInParentChanged; Level.ActorOrderInParentChanged -= OnActorOrderInParentChanged;
Level.ActorNameChanged -= OnActorNameChanged; Level.ActorNameChanged -= OnActorNameChanged;
Level.ActorActiveChanged -= OnActorActiveChanged; Level.ActorActiveChanged -= OnActorActiveChanged;
Level.ActorDestroyChildren -= OnActorDestroyChildren;
// Cleanup graph // Cleanup graph
Root.Dispose(); Root.Dispose();
+2
View File
@@ -6,6 +6,7 @@
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Engine/Globals.h" #include "Engine/Engine/Globals.h"
#include "Engine/Core/Math/Quaternion.h" #include "Engine/Core/Math/Quaternion.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Serialization/JsonWriters.h" #include "Engine/Serialization/JsonWriters.h"
#include "Engine/Serialization/JsonTools.h" #include "Engine/Serialization/JsonTools.h"
#include <ThirdParty/pugixml/pugixml.hpp> #include <ThirdParty/pugixml/pugixml.hpp>
@@ -327,6 +328,7 @@ ProjectInfo* ProjectInfo::Load(const String& path)
} }
// Load // Load
PROFILE_MEM(Editor);
auto project = New<ProjectInfo>(); auto project = New<ProjectInfo>();
if (project->LoadProject(path)) if (project->LoadProject(path))
{ {
+8 -2
View File
@@ -97,13 +97,16 @@ namespace FlaxEditor.SceneGraph
/// <returns>Hit object or null if there is no intersection at all.</returns> /// <returns>Hit object or null if there is no intersection at all.</returns>
public SceneGraphNode RayCast(ref Ray ray, ref Ray view, out Real distance, RayCastData.FlagTypes flags = RayCastData.FlagTypes.None) public SceneGraphNode RayCast(ref Ray ray, ref Ray view, out Real distance, RayCastData.FlagTypes flags = RayCastData.FlagTypes.None)
{ {
Profiler.BeginEvent("RayCastScene");
var data = new RayCastData var data = new RayCastData
{ {
Ray = ray, Ray = ray,
View = view, View = view,
Flags = flags Flags = flags
}; };
return RayCast(ref data, out distance, out _); var result = RayCast(ref data, out distance, out _);
Profiler.EndEvent();
return result;
} }
/// <summary> /// <summary>
@@ -117,13 +120,16 @@ namespace FlaxEditor.SceneGraph
/// <returns>Hit object or null if there is no intersection at all.</returns> /// <returns>Hit object or null if there is no intersection at all.</returns>
public SceneGraphNode RayCast(ref Ray ray, ref Ray view, out Real distance, out Vector3 normal, RayCastData.FlagTypes flags = RayCastData.FlagTypes.None) public SceneGraphNode RayCast(ref Ray ray, ref Ray view, out Real distance, out Vector3 normal, RayCastData.FlagTypes flags = RayCastData.FlagTypes.None)
{ {
Profiler.BeginEvent("RayCastScene");
var data = new RayCastData var data = new RayCastData
{ {
Ray = ray, Ray = ray,
View = view, View = view,
Flags = flags Flags = flags
}; };
return RayCast(ref data, out distance, out normal); var result = RayCast(ref data, out distance, out normal);
Profiler.EndEvent();
return result;
} }
internal static Quaternion RaycastNormalRotation(ref Vector3 normal) internal static Quaternion RaycastNormalRotation(ref Vector3 normal)
+1 -1
View File
@@ -27,7 +27,7 @@ namespace FlaxEditor.SceneGraph
/// <summary> /// <summary>
/// The parent node. /// The parent node.
/// </summary> /// </summary>
protected SceneGraphNode parentNode; internal SceneGraphNode parentNode;
/// <summary> /// <summary>
/// Gets the children list. /// Gets the children list.
+3
View File
@@ -13,6 +13,7 @@
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Platform/Thread.h" #include "Engine/Platform/Thread.h"
#include "Engine/Threading/IRunnable.h" #include "Engine/Threading/IRunnable.h"
#include "Engine/Profiler/ProfilerMemory.h"
void OnAsyncBegin(Thread* thread); void OnAsyncBegin(Thread* thread);
void OnAsyncEnd(); void OnAsyncEnd();
@@ -232,6 +233,8 @@ void OnAsyncEnd()
bool CodeEditingManagerService::Init() bool CodeEditingManagerService::Init()
{ {
PROFILE_MEM(Editor);
// Try get editors // Try get editors
#if USE_VISUAL_STUDIO_DTE #if USE_VISUAL_STUDIO_DTE
VisualStudioEditor::FindEditors(&CodeEditors); VisualStudioEditor::FindEditors(&CodeEditors);
+10 -2
View File
@@ -23,6 +23,7 @@
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/Script.h" #include "Engine/Scripting/Script.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Level/Level.h" #include "Engine/Level/Level.h"
#include "FlaxEngine.Gen.h" #include "FlaxEngine.Gen.h"
@@ -77,7 +78,7 @@ namespace ScriptsBuilderImpl
void onScriptsReloadEnd(); void onScriptsReloadEnd();
void onScriptsLoaded(); void onScriptsLoaded();
void GetClassName(const StringAnsi& fullname, StringAnsi& className); void GetClassName(const StringAnsiView fullname, StringAnsi& className);
void onCodeEditorAsyncOpenBegin() void onCodeEditorAsyncOpenBegin()
{ {
@@ -276,7 +277,7 @@ bool ScriptsBuilder::GenerateProject(const StringView& customArgs)
return RunBuildTool(args); return RunBuildTool(args);
} }
void ScriptsBuilderImpl::GetClassName(const StringAnsi& fullname, StringAnsi& className) void ScriptsBuilderImpl::GetClassName(const StringAnsiView fullname, StringAnsi& className)
{ {
const auto lastDotIndex = fullname.FindLast('.'); const auto lastDotIndex = fullname.FindLast('.');
if (lastDotIndex != -1) if (lastDotIndex != -1)
@@ -417,6 +418,7 @@ void ScriptsBuilder::GetBinariesConfiguration(const Char*& target, const Char*&
bool ScriptsBuilderImpl::compileGameScriptsAsyncInner() bool ScriptsBuilderImpl::compileGameScriptsAsyncInner()
{ {
PROFILE_MEM(Editor);
LOG(Info, "Starting scripts compilation..."); LOG(Info, "Starting scripts compilation...");
CallEvent(EventType::CompileStarted); CallEvent(EventType::CompileStarted);
@@ -523,6 +525,8 @@ void ScriptsBuilderImpl::onEditorAssemblyUnloading(MAssembly* assembly)
bool ScriptsBuilderImpl::compileGameScriptsAsync() bool ScriptsBuilderImpl::compileGameScriptsAsync()
{ {
PROFILE_MEM(Editor);
// Start // Start
{ {
ScopeLock scopeLock(_locker); ScopeLock scopeLock(_locker);
@@ -566,6 +570,7 @@ bool ScriptsBuilderService::Init()
// Check flag // Check flag
if (_isInited) if (_isInited)
return false; return false;
PROFILE_MEM(Editor);
_isInited = true; _isInited = true;
// Link for Editor assembly unload event to clear cached Internal_OnCompilationEnd to prevent errors // Link for Editor assembly unload event to clear cached Internal_OnCompilationEnd to prevent errors
@@ -663,6 +668,9 @@ bool ScriptsBuilderService::Init()
void ScriptsBuilderService::Update() void ScriptsBuilderService::Update()
{ {
PROFILE_CPU();
PROFILE_MEM(Editor);
// Send compilation events // Send compilation events
{ {
ScopeLock scopeLock(_compileEventsLocker); ScopeLock scopeLock(_compileEventsLocker);
@@ -233,6 +233,8 @@ namespace FlaxEditor.Surface.Archetypes
public BlendPointsEditor(Animation.MultiBlend node, bool is2D, float x, float y, float width, float height) public BlendPointsEditor(Animation.MultiBlend node, bool is2D, float x, float y, float width, float height)
: base(x, y, width, height) : base(x, y, width, height)
{ {
AutoFocus = true;
_node = node; _node = node;
_is2D = is2D; _is2D = is2D;
} }
+215 -7
View File
@@ -6,6 +6,7 @@ using FlaxEditor.Scripting;
using FlaxEditor.Surface.Elements; using FlaxEditor.Surface.Elements;
using FlaxEditor.Windows.Assets; using FlaxEditor.Windows.Assets;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.Surface.Archetypes namespace FlaxEditor.Surface.Archetypes
{ {
@@ -123,7 +124,8 @@ namespace FlaxEditor.Surface.Archetypes
case MaterialDomain.Particle: case MaterialDomain.Particle:
case MaterialDomain.Deformable: case MaterialDomain.Deformable:
{ {
bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit; bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit && info.ShadingModel != MaterialShadingModel.CustomLit;
bool isOpaque = info.BlendMode == MaterialBlendMode.Opaque;
bool withTess = info.TessellationMode != TessellationMethod.None; bool withTess = info.TessellationMode != TessellationMethod.None;
GetBox(MaterialNodeBoxes.Color).IsActive = isNotUnlit; GetBox(MaterialNodeBoxes.Color).IsActive = isNotUnlit;
@@ -134,8 +136,8 @@ namespace FlaxEditor.Surface.Archetypes
GetBox(MaterialNodeBoxes.Roughness).IsActive = isNotUnlit; GetBox(MaterialNodeBoxes.Roughness).IsActive = isNotUnlit;
GetBox(MaterialNodeBoxes.AmbientOcclusion).IsActive = isNotUnlit; GetBox(MaterialNodeBoxes.AmbientOcclusion).IsActive = isNotUnlit;
GetBox(MaterialNodeBoxes.Normal).IsActive = isNotUnlit; GetBox(MaterialNodeBoxes.Normal).IsActive = isNotUnlit;
GetBox(MaterialNodeBoxes.Opacity).IsActive = info.ShadingModel == MaterialShadingModel.Subsurface || info.ShadingModel == MaterialShadingModel.Foliage || info.BlendMode != MaterialBlendMode.Opaque; GetBox(MaterialNodeBoxes.Opacity).IsActive = info.ShadingModel == MaterialShadingModel.Subsurface || info.ShadingModel == MaterialShadingModel.Foliage || !isOpaque;
GetBox(MaterialNodeBoxes.Refraction).IsActive = info.BlendMode != MaterialBlendMode.Opaque; GetBox(MaterialNodeBoxes.Refraction).IsActive = !isOpaque;
GetBox(MaterialNodeBoxes.PositionOffset).IsActive = true; GetBox(MaterialNodeBoxes.PositionOffset).IsActive = true;
GetBox(MaterialNodeBoxes.TessellationMultiplier).IsActive = withTess; GetBox(MaterialNodeBoxes.TessellationMultiplier).IsActive = withTess;
GetBox(MaterialNodeBoxes.WorldDisplacement).IsActive = withTess; GetBox(MaterialNodeBoxes.WorldDisplacement).IsActive = withTess;
@@ -260,6 +262,211 @@ namespace FlaxEditor.Surface.Archetypes
} }
} }
#if false // TODO: finish code editor based on RichTextBoxBase with text block parsing for custom styling
internal sealed class CustomCodeTextBox : RichTextBoxBase
{
protected override void OnParseTextBlocks()
{
base.OnParseTextBlocks();
// Single block for a whole text
// TODO: implement code parsing with HLSL syntax
var font = Style.Current.FontMedium;
var style = new TextBlockStyle
{
Font = new FontReference(font),
Color = Style.Current.Foreground,
BackgroundSelectedBrush = new SolidColorBrush(Style.Current.BackgroundSelected),
};
_textBlocks.Clear();
_textBlocks.Add(new TextBlock
{
Range = new TextRange
{
StartIndex = 0,
EndIndex = TextLength,
},
Style = style,
Bounds = new Rectangle(Float2.Zero, font.MeasureText(Text)),
});
}
#else
internal sealed class CustomCodeTextBox : TextBox
{
#endif
public override void Draw()
{
base.Draw();
// Draw border
if (!IsFocused)
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.BorderNormal);
}
}
internal sealed class CustomCodeNode : SurfaceNode
{
private Rectangle _resizeButtonRect;
private Float2 _startResizingSize;
private Float2 _startResizingCornerOffset;
private bool _isResizing;
private CustomCodeTextBox _textBox;
private int SizeValueIndex => Archetype.TypeID == 8 ? 1 : 3; // Index of the Size stored in Values array
private Float2 SizeValue
{
get => (Float2)Values[SizeValueIndex];
set => SetValue(SizeValueIndex, value, false);
}
public CustomCodeNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
: base(id, context, nodeArch, groupArch)
{
Float2 pos = new Float2(FlaxEditor.Surface.Constants.NodeMarginX, FlaxEditor.Surface.Constants.NodeMarginY + FlaxEditor.Surface.Constants.NodeHeaderSize), size;
if (nodeArch.TypeID == 8)
{
pos += new Float2(60, 0);
size = new Float2(172, 200);
}
else
{
pos += new Float2(0, 40);
size = new Float2(300, 200);
}
_textBox = new CustomCodeTextBox
{
IsMultiline = true,
Location = pos,
Size = size,
Parent = this,
AnchorMax = Float2.One,
};
_textBox.EditEnd += () => SetValue(0, _textBox.Text);
}
public override bool CanSelect(ref Float2 location)
{
return base.CanSelect(ref location) && !_resizeButtonRect.MakeOffsetted(Location).Contains(ref location);
}
public override void OnSurfaceLoaded(SurfaceNodeActions action)
{
base.OnSurfaceLoaded(action);
_textBox.Text = (string)Values[0];
var size = SizeValue;
if (Surface != null && Surface.GridSnappingEnabled)
size = Surface.SnapToGrid(size, true);
Resize(size.X, size.Y);
}
public override void OnValuesChanged()
{
base.OnValuesChanged();
var size = SizeValue;
Resize(size.X, size.Y);
_textBox.Text = (string)Values[0];
}
protected override void UpdateRectangles()
{
base.UpdateRectangles();
const float buttonMargin = FlaxEditor.Surface.Constants.NodeCloseButtonMargin;
const float buttonSize = FlaxEditor.Surface.Constants.NodeCloseButtonSize;
_resizeButtonRect = new Rectangle(_closeButtonRect.Left, Height - buttonSize - buttonMargin - 4, buttonSize, buttonSize);
}
public override void Draw()
{
base.Draw();
var style = Style.Current;
if (_isResizing)
{
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
}
Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
}
public override void OnLostFocus()
{
if (_isResizing)
EndResizing();
base.OnLostFocus();
}
public override void OnEndMouseCapture()
{
if (_isResizing)
EndResizing();
base.OnEndMouseCapture();
}
public override bool OnMouseDown(Float2 location, MouseButton button)
{
if (base.OnMouseDown(location, button))
return true;
if (button == MouseButton.Left && _resizeButtonRect.Contains(ref location) && Surface.CanEdit)
{
// Start sliding
_isResizing = true;
_startResizingSize = Size;
_startResizingCornerOffset = Size - location;
StartMouseCapture();
Cursor = CursorType.SizeNWSE;
return true;
}
return false;
}
public override void OnMouseMove(Float2 location)
{
if (_isResizing)
{
var emptySize = CalculateNodeSize(0, 0);
var size = Float2.Max(location - emptySize + _startResizingCornerOffset, new Float2(240, 160));
Resize(size.X, size.Y);
}
else
{
base.OnMouseMove(location);
}
}
public override bool OnMouseUp(Float2 location, MouseButton button)
{
if (button == MouseButton.Left && _isResizing)
{
EndResizing();
return true;
}
return base.OnMouseUp(location, button);
}
private void EndResizing()
{
Cursor = CursorType.Default;
EndMouseCapture();
_isResizing = false;
if (_startResizingSize != Size)
{
var emptySize = CalculateNodeSize(0, 0);
SizeValue = Size - emptySize;
Surface.MarkAsEdited(false);
}
}
}
internal enum MaterialTemplateInputsMapping internal enum MaterialTemplateInputsMapping
{ {
/// <summary> /// <summary>
@@ -410,13 +617,15 @@ namespace FlaxEditor.Surface.Archetypes
new NodeArchetype new NodeArchetype
{ {
TypeID = 8, TypeID = 8,
Create = (id, context, arch, groupArch) => new CustomCodeNode(id, context, arch, groupArch),
Title = "Custom Code", Title = "Custom Code",
Description = "Custom HLSL shader code expression", Description = "Custom HLSL shader code expression",
Flags = NodeFlags.MaterialGraph, Flags = NodeFlags.MaterialGraph,
Size = new Float2(300, 200), Size = new Float2(300, 200),
DefaultValues = new object[] DefaultValues = new object[]
{ {
"// Here you can add HLSL code\nOutput0 = Input0;" "// Here you can add HLSL code\nOutput0 = Input0;",
new Float2(300, 200),
}, },
Elements = new[] Elements = new[]
{ {
@@ -433,8 +642,6 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Output(1, "Output1", typeof(Float4), 9), NodeElementArchetype.Factory.Output(1, "Output1", typeof(Float4), 9),
NodeElementArchetype.Factory.Output(2, "Output2", typeof(Float4), 10), NodeElementArchetype.Factory.Output(2, "Output2", typeof(Float4), 10),
NodeElementArchetype.Factory.Output(3, "Output3", typeof(Float4), 11), NodeElementArchetype.Factory.Output(3, "Output3", typeof(Float4), 11),
NodeElementArchetype.Factory.TextBox(60, 0, 175, 200, 0),
} }
}, },
new NodeArchetype new NodeArchetype
@@ -874,6 +1081,7 @@ namespace FlaxEditor.Surface.Archetypes
new NodeArchetype new NodeArchetype
{ {
TypeID = 38, TypeID = 38,
Create = (id, context, arch, groupArch) => new CustomCodeNode(id, context, arch, groupArch),
Title = "Custom Global Code", Title = "Custom Global Code",
Description = "Custom global HLSL shader code expression (placed before material shader code). Can contain includes to shader utilities or declare functions to reuse later.", Description = "Custom global HLSL shader code expression (placed before material shader code). Can contain includes to shader utilities or declare functions to reuse later.",
Flags = NodeFlags.MaterialGraph, Flags = NodeFlags.MaterialGraph,
@@ -883,6 +1091,7 @@ namespace FlaxEditor.Surface.Archetypes
"// Here you can add HLSL code\nfloat4 GetCustomColor()\n{\n\treturn float4(1, 0, 0, 1);\n}", "// Here you can add HLSL code\nfloat4 GetCustomColor()\n{\n\treturn float4(1, 0, 0, 1);\n}",
true, true,
(int)MaterialTemplateInputsMapping.Utilities, (int)MaterialTemplateInputsMapping.Utilities,
new Float2(300, 240),
}, },
Elements = new[] Elements = new[]
{ {
@@ -890,7 +1099,6 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Text(20, 0, "Enabled"), NodeElementArchetype.Factory.Text(20, 0, "Enabled"),
NodeElementArchetype.Factory.Text(0, 20, "Location"), NodeElementArchetype.Factory.Text(0, 20, "Location"),
NodeElementArchetype.Factory.Enum(50, 20, 120, 2, typeof(MaterialTemplateInputsMapping)), NodeElementArchetype.Factory.Enum(50, 20, 120, 2, typeof(MaterialTemplateInputsMapping)),
NodeElementArchetype.Factory.TextBox(0, 40, 300, 200, 0),
} }
}, },
new NodeArchetype new NodeArchetype
@@ -64,6 +64,7 @@ namespace FlaxEditor.Surface.Elements
{ {
ParentNode = parentNode; ParentNode = parentNode;
Archetype = archetype; Archetype = archetype;
AutoFocus = true;
var back = Style.Current.TextBoxBackground; var back = Style.Current.TextBoxBackground;
var grayOutFactor = 0.6f; var grayOutFactor = 0.6f;
+1
View File
@@ -59,6 +59,7 @@ namespace FlaxEditor.Surface
protected SurfaceControl(VisjectSurfaceContext context, float width, float height) protected SurfaceControl(VisjectSurfaceContext context, float width, float height)
: base(0, 0, width, height) : base(0, 0, width, height)
{ {
AutoFocus = true;
ClipChildren = false; ClipChildren = false;
Surface = context.Surface; Surface = context.Surface;
@@ -120,6 +120,8 @@ namespace FlaxEditor.Surface
private void UpdateSelectionRectangle() private void UpdateSelectionRectangle()
{ {
if (Root == null)
return;
var p1 = _rootControl.PointFromParent(ref _leftMouseDownPos); var p1 = _rootControl.PointFromParent(ref _leftMouseDownPos);
var p2 = _rootControl.PointFromParent(ref _mousePos); var p2 = _rootControl.PointFromParent(ref _mousePos);
var selectionRect = Rectangle.FromPoints(p1, p2); var selectionRect = Rectangle.FromPoints(p1, p2);
@@ -5,6 +5,7 @@
#include "Engine/Content/Assets/Model.h" #include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Assets/MaterialInstance.h" #include "Engine/Content/Assets/MaterialInstance.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Level/Level.h" #include "Engine/Level/Level.h"
#include "Engine/Level/Scene/Scene.h" #include "Engine/Level/Scene/Scene.h"
#include "Engine/Level/Actors/PointLight.h" #include "Engine/Level/Actors/PointLight.h"
@@ -263,6 +264,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor
bool ViewportIconsRendererService::Init() bool ViewportIconsRendererService::Init()
{ {
PROFILE_MEM(Editor);
QuadModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Quad")); QuadModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Quad"));
#define INIT(type, path) \ #define INIT(type, path) \
InstanceBuffers[static_cast<int32>(IconTypes::type)].Setup(1); \ InstanceBuffers[static_cast<int32>(IconTypes::type)].Setup(1); \
@@ -340,6 +340,13 @@ namespace FlaxEditor.Viewport
{ {
_debugDrawData.Clear(); _debugDrawData.Clear();
if (task is SceneRenderTask sceneRenderTask)
{
// Sync debug view to avoid lag on culling/LODing
var view = sceneRenderTask.View;
DebugDraw.SetView(ref view);
}
// Collect selected objects debug shapes and visuals // Collect selected objects debug shapes and visuals
var selectedParents = TransformGizmo.SelectedParents; var selectedParents = TransformGizmo.SelectedParents;
if (selectedParents.Count > 0) if (selectedParents.Count > 0)
@@ -243,7 +243,12 @@ namespace FlaxEditor.Viewport
_tempDebugDrawContext = DebugDraw.AllocateContext(); _tempDebugDrawContext = DebugDraw.AllocateContext();
DebugDraw.SetContext(_tempDebugDrawContext); DebugDraw.SetContext(_tempDebugDrawContext);
DebugDraw.UpdateContext(_tempDebugDrawContext, 1.0f); DebugDraw.UpdateContext(_tempDebugDrawContext, 1.0f);
if (task is SceneRenderTask sceneRenderTask)
{
// Sync debug view to avoid lag on culling/LODing
var view = sceneRenderTask.View;
DebugDraw.SetView(ref view);
}
for (int i = 0; i < selectedParents.Count; i++) for (int i = 0; i < selectedParents.Count; i++)
{ {
if (selectedParents[i].IsActiveInHierarchy) if (selectedParents[i].IsActiveInHierarchy)
@@ -264,6 +264,7 @@ namespace FlaxEditor.Viewport.Previews
{ {
DebugDraw.SetContext(_debugDrawContext); DebugDraw.SetContext(_debugDrawContext);
DebugDraw.UpdateContext(_debugDrawContext, 1.0f / Mathf.Max(Engine.FramesPerSecond, 1)); DebugDraw.UpdateContext(_debugDrawContext, 1.0f / Mathf.Max(Engine.FramesPerSecond, 1));
DebugDraw.SetView(ref renderContext.View);
CustomDebugDraw?.Invoke(context, ref renderContext); CustomDebugDraw?.Invoke(context, ref renderContext);
OnDebugDraw(context, ref renderContext); OnDebugDraw(context, ref renderContext);
DebugDraw.Draw(ref renderContext, target.View(), targetDepth.View(), true); DebugDraw.Draw(ref renderContext, target.View(), targetDepth.View(), true);
@@ -181,15 +181,8 @@ namespace FlaxEditor.Windows.Assets
private class CollisionDataPreview : ModelBasePreview private class CollisionDataPreview : ModelBasePreview
{ {
public bool ShowCollisionData = false; public bool ShowInfo;
private int _verticesCount = 0; public string Info;
private int _trianglesCount = 0;
public void SetVerticesAndTriangleCount(int verticesCount, int triangleCount)
{
_verticesCount = verticesCount;
_trianglesCount = triangleCount;
}
/// <inheritdoc /> /// <inheritdoc />
public CollisionDataPreview(bool useWidgets) public CollisionDataPreview(bool useWidgets)
@@ -204,13 +197,12 @@ namespace FlaxEditor.Windows.Assets
{ {
base.Draw(); base.Draw();
if (ShowCollisionData) if (ShowInfo)
{ {
var text = string.Format("\nTriangles: {0:N0}\nVertices: {1:N0}\nMemory Size: {2}", _trianglesCount, _verticesCount, Utilities.Utils.FormatBytesCount(Asset.MemoryUsage));
var font = Style.Current.FontMedium; var font = Style.Current.FontMedium;
var pos = new Float2(10, 50); var pos = new Float2(10, 50);
Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black); Render2D.DrawText(font, Info, new Rectangle(pos + Float2.One, Size), Color.Black);
Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White); Render2D.DrawText(font, Info, new Rectangle(pos, Size), Color.White);
} }
} }
} }
@@ -222,11 +214,11 @@ namespace FlaxEditor.Windows.Assets
// Toolstrip // Toolstrip
_toolstrip.AddSeparator(); _toolstrip.AddSeparator();
_toolstrip.AddButton(editor.Icons.CenterView64, () => _preview.ResetCamera()).LinkTooltip("Show whole collision"); _toolstrip.AddButton(editor.Icons.CenterView64, () => _preview.ResetCamera()).LinkTooltip("Show whole collision");
var infoButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Info64).LinkTooltip("Show Collision Data"); var infoButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Info64).LinkTooltip("Show Collision Data info");
infoButton.Clicked += () => infoButton.Clicked += () =>
{ {
_preview.ShowCollisionData = !_preview.ShowCollisionData; _preview.ShowInfo = !_preview.ShowInfo;
infoButton.Checked = _preview.ShowCollisionData; infoButton.Checked = _preview.ShowInfo;
}; };
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/physics/colliders/collision-data.html")).LinkTooltip("See documentation to learn more"); _toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/physics/colliders/collision-data.html")).LinkTooltip("See documentation to learn more");
@@ -293,7 +285,7 @@ namespace FlaxEditor.Windows.Assets
} }
_collisionWiresShowActor.Model = _collisionWiresModel; _collisionWiresShowActor.Model = _collisionWiresModel;
_collisionWiresShowActor.SetMaterial(0, FlaxEngine.Content.LoadAsyncInternal<MaterialBase>(EditorAssets.WiresDebugMaterial)); _collisionWiresShowActor.SetMaterial(0, FlaxEngine.Content.LoadAsyncInternal<MaterialBase>(EditorAssets.WiresDebugMaterial));
_preview.SetVerticesAndTriangleCount(triangleCount, indicesCount / 3); _preview.Info = string.Format("\nTriangles: {0:N0}\nVertices: {1:N0}\nMemory Size: {2}", triangleCount, indicesCount / 3, Utilities.Utils.FormatBytesCount(Asset.MemoryUsage));
_preview.Asset = FlaxEngine.Content.LoadAsync<ModelBase>(_asset.Options.Model); _preview.Asset = FlaxEngine.Content.LoadAsync<ModelBase>(_asset.Options.Model);
} }
+20 -24
View File
@@ -59,17 +59,11 @@ namespace FlaxEditor.Windows
GameCookerWin = win; GameCookerWin = win;
Selector = platformSelector; Selector = platformSelector;
PerPlatformOptions[PlatformType.Windows].Init("Output/Windows", "Windows"); foreach (var e in PerPlatformOptions)
PerPlatformOptions[PlatformType.XboxOne].Init("Output/XboxOne", "XboxOne"); {
PerPlatformOptions[PlatformType.UWP].Init("Output/UWP", "UWP"); var str = e.Key.ToString();
PerPlatformOptions[PlatformType.Linux].Init("Output/Linux", "Linux"); e.Value.Init("Output/" + str, str);
PerPlatformOptions[PlatformType.PS4].Init("Output/PS4", "PS4"); }
PerPlatformOptions[PlatformType.XboxScarlett].Init("Output/XboxScarlett", "XboxScarlett");
PerPlatformOptions[PlatformType.Android].Init("Output/Android", "Android");
PerPlatformOptions[PlatformType.Switch].Init("Output/Switch", "Switch");
PerPlatformOptions[PlatformType.PS5].Init("Output/PS5", "PS5");
PerPlatformOptions[PlatformType.Mac].Init("Output/Mac", "Mac");
PerPlatformOptions[PlatformType.iOS].Init("Output/iOS", "iOS");
} }
[HideInEditor] [HideInEditor]
@@ -196,12 +190,14 @@ namespace FlaxEditor.Windows
var label = layout.Label(text, TextAlignment.Center); var label = layout.Label(text, TextAlignment.Center);
label.Label.AutoHeight = true; label.Label.AutoHeight = true;
} }
/// <summary> /// <summary>
/// Used to add platform specific tools if available. /// Used to add platform specific tools if available.
/// </summary> /// </summary>
/// <param name="layout">The layout to start the tools at.</param> /// <param name="layout">The layout to start the tools at.</param>
public virtual void OnCustomToolsLayout(LayoutElementsContainer layout) { } public virtual void OnCustomToolsLayout(LayoutElementsContainer layout)
{
}
public virtual void Build() public virtual void Build()
{ {
@@ -256,7 +252,7 @@ namespace FlaxEditor.Windows
if (string.IsNullOrEmpty(sdkPath)) if (string.IsNullOrEmpty(sdkPath))
sdkPath = Environment.GetEnvironmentVariable("ANDROID_SDK"); sdkPath = Environment.GetEnvironmentVariable("ANDROID_SDK");
emulatorGroup.Label($"SDK path: {sdkPath}"); emulatorGroup.Label($"SDK path: {sdkPath}");
// AVD and starting emulator // AVD and starting emulator
var avdGroup = emulatorGroup.Group("AVD Emulator"); var avdGroup = emulatorGroup.Group("AVD Emulator");
avdGroup.Label("Note: Create AVDs using Android Studio."); avdGroup.Label("Note: Create AVDs using Android Studio.");
@@ -273,7 +269,7 @@ namespace FlaxEditor.Windows
{ {
if (avdListTree.Children.Count > 0) if (avdListTree.Children.Count > 0)
avdListTree.DisposeChildren(); avdListTree.DisposeChildren();
var processStartInfo = new System.Diagnostics.ProcessStartInfo var processStartInfo = new System.Diagnostics.ProcessStartInfo
{ {
FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"), FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"),
@@ -299,7 +295,7 @@ namespace FlaxEditor.Windows
}; };
//processSettings.ShellExecute = true; //processSettings.ShellExecute = true;
FlaxEngine.Platform.CreateProcess(ref processSettings); FlaxEngine.Platform.CreateProcess(ref processSettings);
var output = new string(processSettings.Output);*/ var output = new string(processSettings.Output);*/
if (output.Length == 0) if (output.Length == 0)
{ {
@@ -345,9 +341,9 @@ namespace FlaxEditor.Windows
processSettings.ShellExecute = true; processSettings.ShellExecute = true;
FlaxEngine.Platform.CreateProcess(ref processSettings); FlaxEngine.Platform.CreateProcess(ref processSettings);
}; };
emulatorGroup.Space(2); emulatorGroup.Space(2);
// Device // Device
var installGroup = emulatorGroup.Group("Install"); var installGroup = emulatorGroup.Group("Install");
installGroup.Panel.IsClosed = false; installGroup.Panel.IsClosed = false;
@@ -391,10 +387,10 @@ namespace FlaxEditor.Windows
processSettings.SaveOutput = true; processSettings.SaveOutput = true;
processSettings.ShellExecute = false; processSettings.ShellExecute = false;
FlaxEngine.Platform.CreateProcess(ref processSettings); FlaxEngine.Platform.CreateProcess(ref processSettings);
var output = new string(processSettings.Output); var output = new string(processSettings.Output);
*/ */
if (output.Length > 0 && !output.Equals("List of devices attached", StringComparison.Ordinal)) if (output.Length > 0 && !output.Equals("List of devices attached", StringComparison.Ordinal))
{ {
noDevicesLabel.Visible = false; noDevicesLabel.Visible = false;
@@ -403,7 +399,7 @@ namespace FlaxEditor.Windows
{ {
if (line.Trim().Equals("List of devices attached", StringComparison.Ordinal) || string.IsNullOrEmpty(line.Trim())) if (line.Trim().Equals("List of devices attached", StringComparison.Ordinal) || string.IsNullOrEmpty(line.Trim()))
continue; continue;
var tab = line.Split("device "); var tab = line.Split("device ");
if (tab.Length < 2) if (tab.Length < 2)
continue; continue;
@@ -430,7 +426,7 @@ namespace FlaxEditor.Windows
{ {
if (deviceListTree.Selection.Count == 0) if (deviceListTree.Selection.Count == 0)
return; return;
// Get built APK at output path // Get built APK at output path
string output = StringUtils.ConvertRelativePathToAbsolute(Globals.ProjectFolder, StringUtils.NormalizePath(Output)); string output = StringUtils.ConvertRelativePathToAbsolute(Globals.ProjectFolder, StringUtils.NormalizePath(Output));
if (!Directory.Exists(output)) if (!Directory.Exists(output))
@@ -438,7 +434,7 @@ namespace FlaxEditor.Windows
FlaxEditor.Editor.LogWarning("Can not copy APK because output folder does not exist."); FlaxEditor.Editor.LogWarning("Can not copy APK because output folder does not exist.");
return; return;
} }
var apkFiles = Directory.GetFiles(output, "*.apk"); var apkFiles = Directory.GetFiles(output, "*.apk");
if (apkFiles.Length == 0) if (apkFiles.Length == 0)
{ {
@@ -457,7 +453,7 @@ namespace FlaxEditor.Windows
} }
apkFilesString += $" \"{file}\""; apkFilesString += $" \"{file}\"";
} }
CreateProcessSettings processSettings = new CreateProcessSettings CreateProcessSettings processSettings = new CreateProcessSettings
{ {
FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"),
+6
View File
@@ -1192,6 +1192,12 @@ namespace FlaxEditor.Windows
Screen.CursorVisible = true; Screen.CursorVisible = true;
Screen.CursorLock = CursorLockMode.None; Screen.CursorLock = CursorLockMode.None;
} }
if (Editor.IsPlayMode && IsDocked && IsSelected && RootWindow.FocusedControl == null)
{
// Game UI cleared focus so regain it to maintain UI navigation just like game window does
FlaxEngine.Scripting.InvokeOnUpdate(Focus);
}
} }
/// <inheritdoc /> /// <inheritdoc />
+8 -2
View File
@@ -235,6 +235,8 @@ namespace FlaxEditor.Windows
// Add items // Add items
ItemsListContextMenu.Item lastItem = null; ItemsListContextMenu.Item lastItem = null;
var itemFont = Style.Current.FontSmall;
var maxWidth = 0.0f;
foreach (var command in commands) foreach (var command in commands)
{ {
cm.AddItem(lastItem = new Item cm.AddItem(lastItem = new Item
@@ -252,6 +254,7 @@ namespace FlaxEditor.Windows
// Set command // Set command
Set(item.Name); Set(item.Name);
}; };
maxWidth = Mathf.Max(maxWidth, itemFont.MeasureText(command).X);
} }
cm.ItemClicked += item => cm.ItemClicked += item =>
{ {
@@ -265,6 +268,9 @@ namespace FlaxEditor.Windows
cm.Height = 220; cm.Height = 220;
if (cm.Height > totalHeight) if (cm.Height > totalHeight)
cm.Height = totalHeight; // Limit popup height if list is small cm.Height = totalHeight; // Limit popup height if list is small
maxWidth += 8.0f + ScrollBar.DefaultSize; // Margin
if (cm.Width < maxWidth)
cm.Width = maxWidth;
if (searchText != null) if (searchText != null)
{ {
cm.SortItems(); cm.SortItems();
@@ -344,8 +350,8 @@ namespace FlaxEditor.Windows
// Update history buffer // Update history buffer
if (_window._commandHistory == null) if (_window._commandHistory == null)
_window._commandHistory = new List<string>(); _window._commandHistory = new List<string>();
else if (_window._commandHistory.Count != 0 && _window._commandHistory.Last() == command) else if (_window._commandHistory.Count != 0 && _window._commandHistory.Contains(command))
_window._commandHistory.RemoveAt(_window._commandHistory.Count - 1); _window._commandHistory.Remove(command);
_window._commandHistory.Add(command); _window._commandHistory.Add(command);
if (_window._commandHistory.Count > CommandHistoryLimit) if (_window._commandHistory.Count > CommandHistoryLimit)
_window._commandHistory.RemoveAt(0); _window._commandHistory.RemoveAt(0);
+7 -4
View File
@@ -34,6 +34,7 @@ namespace FlaxEditor.Windows.Profiler
private List<ClickableRow> _tableRowsCache; private List<ClickableRow> _tableRowsCache;
private Dictionary<Guid, Resource> _resourceCache; private Dictionary<Guid, Resource> _resourceCache;
private StringBuilder _stringBuilder; private StringBuilder _stringBuilder;
private Asset[] _assetsCache;
public Assets() public Assets()
: base("Assets") : base("Assets")
@@ -138,12 +139,12 @@ namespace FlaxEditor.Windows.Profiler
_stringBuilder = new StringBuilder(); _stringBuilder = new StringBuilder();
// Capture current assets usage info // Capture current assets usage info
var assets = FlaxEngine.Content.Assets; FlaxEngine.Content.GetAssets(ref _assetsCache, out var count);
var resources = new Resource[assets.Length]; var resources = new Resource[count];
ulong totalMemoryUsage = 0; ulong totalMemoryUsage = 0;
for (int i = 0; i < resources.Length; i++) for (int i = 0; i < count; i++)
{ {
var asset = assets[i]; var asset = _assetsCache[i];
ref var resource = ref resources[i]; ref var resource = ref resources[i];
if (!asset) if (!asset)
continue; continue;
@@ -179,6 +180,7 @@ namespace FlaxEditor.Windows.Profiler
if (_resources == null) if (_resources == null)
_resources = new SamplesBuffer<Resource[]>(); _resources = new SamplesBuffer<Resource[]>();
_resources.Add(resources); _resources.Add(resources);
Array.Clear(_assetsCache);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -200,6 +202,7 @@ namespace FlaxEditor.Windows.Profiler
_resourceCache?.Clear(); _resourceCache?.Clear();
_tableRowsCache?.Clear(); _tableRowsCache?.Clear();
_stringBuilder?.Clear(); _stringBuilder?.Clear();
_assetsCache = null;
base.OnDestroy(); base.OnDestroy();
} }
+200 -1
View File
@@ -2,6 +2,8 @@
#if USE_PROFILER #if USE_PROFILER
using System; using System;
using System.Collections.Generic;
using FlaxEditor.GUI;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
@@ -13,9 +15,22 @@ namespace FlaxEditor.Windows.Profiler
/// <seealso cref="FlaxEditor.Windows.Profiler.ProfilerMode" /> /// <seealso cref="FlaxEditor.Windows.Profiler.ProfilerMode" />
internal sealed class Memory : ProfilerMode internal sealed class Memory : ProfilerMode
{ {
private struct FrameData
{
public ProfilerMemory.GroupsArray Usage;
public ProfilerMemory.GroupsArray Peek;
public ProfilerMemory.GroupsArray Count;
}
private readonly SingleChart _nativeAllocationsChart; private readonly SingleChart _nativeAllocationsChart;
private readonly SingleChart _managedAllocationsChart; private readonly SingleChart _managedAllocationsChart;
private readonly Table _table;
private SamplesBuffer<FrameData> _frames;
private List<Row> _tableRowsCache;
private string[] _groupNames;
private int[] _groupOrder;
private Label _warningText;
public Memory() public Memory()
: base("Memory") : base("Memory")
{ {
@@ -50,6 +65,70 @@ namespace FlaxEditor.Windows.Profiler
Parent = layout, Parent = layout,
}; };
_managedAllocationsChart.SelectedSampleChanged += OnSelectedSampleChanged; _managedAllocationsChart.SelectedSampleChanged += OnSelectedSampleChanged;
// Warning text
if (!ProfilerMemory.Enabled)
{
_warningText = new Label
{
Text = "Detailed memory profiling is disabled. Run with command line '-mem'",
TextColor = Color.Red,
Visible = false,
Parent = layout,
};
}
// Table
var style = Style.Current;
var headerColor = style.LightBackground;
var textColor = style.Foreground;
_table = new Table
{
Columns = new[]
{
new ColumnDefinition
{
UseExpandCollapseMode = true,
CellAlignment = TextAlignment.Near,
Title = "Group",
TitleBackgroundColor = headerColor,
TitleColor = textColor,
},
new ColumnDefinition
{
Title = "Usage",
TitleBackgroundColor = headerColor,
FormatValue = FormatCellBytes,
TitleColor = textColor,
},
new ColumnDefinition
{
Title = "Peek",
TitleBackgroundColor = headerColor,
FormatValue = FormatCellBytes,
TitleColor = textColor,
},
new ColumnDefinition
{
Title = "Count",
TitleBackgroundColor = headerColor,
TitleColor = textColor,
},
},
Parent = layout,
};
_table.Splits = new[]
{
0.5f,
0.2f,
0.2f,
0.1f,
};
}
private string FormatCellBytes(object x)
{
return Utilities.Utils.FormatBytesCount(Convert.ToUInt64(x));
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -57,6 +136,7 @@ namespace FlaxEditor.Windows.Profiler
{ {
_nativeAllocationsChart.Clear(); _nativeAllocationsChart.Clear();
_managedAllocationsChart.Clear(); _managedAllocationsChart.Clear();
_frames?.Clear();
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -84,6 +164,19 @@ namespace FlaxEditor.Windows.Profiler
_nativeAllocationsChart.AddSample(nativeMemoryAllocation); _nativeAllocationsChart.AddSample(nativeMemoryAllocation);
_managedAllocationsChart.AddSample(managedMemoryAllocation); _managedAllocationsChart.AddSample(managedMemoryAllocation);
// Gather memory profiler stats for groups
var frame = new FrameData
{
Usage = ProfilerMemory.GetGroups(0),
Peek = ProfilerMemory.GetGroups(1),
Count = ProfilerMemory.GetGroups(2),
};
if (_frames == null)
_frames = new SamplesBuffer<FrameData>();
if (_groupNames == null)
_groupNames = ProfilerMemory.GetGroupNames();
_frames.Add(frame);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -91,6 +184,112 @@ namespace FlaxEditor.Windows.Profiler
{ {
_nativeAllocationsChart.SelectedSampleIndex = selectedFrame; _nativeAllocationsChart.SelectedSampleIndex = selectedFrame;
_managedAllocationsChart.SelectedSampleIndex = selectedFrame; _managedAllocationsChart.SelectedSampleIndex = selectedFrame;
UpdateTable(selectedFrame);
}
/// <inheritdoc />
public override void OnDestroy()
{
_tableRowsCache?.Clear();
_groupNames = null;
_groupOrder = null;
base.OnDestroy();
}
private void UpdateTable(int selectedFrame)
{
if (_frames == null)
return;
if (_tableRowsCache == null)
_tableRowsCache = new List<Row>();
_table.IsLayoutLocked = true;
RecycleTableRows(_table, _tableRowsCache);
UpdateTableInner(selectedFrame);
_table.UnlockChildrenRecursive();
_table.PerformLayout();
}
private unsafe void UpdateTableInner(int selectedFrame)
{
if (_frames.Count == 0)
return;
if (_warningText != null)
_warningText.Visible = true;
var frame = _frames.Get(selectedFrame);
var totalUage = frame.Usage.Values0[(int)ProfilerMemory.Groups.TotalTracked];
var totalPeek = frame.Peek.Values0[(int)ProfilerMemory.Groups.TotalTracked];
var totalCount = frame.Count.Values0[(int)ProfilerMemory.Groups.TotalTracked];
// Sort by memory size
if (_groupOrder == null)
_groupOrder = new int[(int)ProfilerMemory.Groups.MAX];
for (int i = 0; i < (int)ProfilerMemory.Groups.MAX; i++)
_groupOrder[i] = i;
Array.Sort(_groupOrder, (x, y) =>
{
var tmp = _frames.Get(selectedFrame);
return tmp.Usage.Values0[y].CompareTo(tmp.Usage.Values0[x]);
});
// Add rows
var rowColor2 = Style.Current.Background * 1.4f;
for (int i = 0; i < (int)ProfilerMemory.Groups.MAX; i++)
{
var group = _groupOrder[i];
var groupUsage = frame.Usage.Values0[group];
if (groupUsage <= 0)
continue;
var groupPeek = frame.Peek.Values0[group];
var groupCount = frame.Count.Values0[group];
Row row;
if (_tableRowsCache.Count != 0)
{
var last = _tableRowsCache.Count - 1;
row = _tableRowsCache[last];
_tableRowsCache.RemoveAt(last);
}
else
{
row = new Row
{
Values = new object[4],
BackgroundColors = new Color[4],
};
}
{
// Group
row.Values[0] = _groupNames[group];
// Usage
row.Values[1] = groupUsage;
row.BackgroundColors[1] = Color.Red.AlphaMultiplied(Mathf.Min(1, (float)groupUsage / totalUage) * 0.5f);
// Peek
row.Values[2] = groupPeek;
row.BackgroundColors[2] = Color.Red.AlphaMultiplied(Mathf.Min(1, (float)groupPeek / totalPeek) * 0.5f);
// Count
row.Values[3] = groupCount;
row.BackgroundColors[3] = Color.Red.AlphaMultiplied(Mathf.Min(1, (float)groupCount / totalCount) * 0.5f);
}
row.Width = _table.Width;
row.BackgroundColor = i % 2 == 1 ? rowColor2 : Color.Transparent;
row.Parent = _table;
var useBackground = group != (int)ProfilerMemory.Groups.Total &&
group != (int)ProfilerMemory.Groups.TotalTracked &&
group != (int)ProfilerMemory.Groups.Malloc;
if (!useBackground)
{
for (int k = 1; k < row.BackgroundColors.Length; k++)
row.BackgroundColors[k] = Color.Transparent;
}
}
} }
} }
} }
+9 -4
View File
@@ -35,6 +35,7 @@ namespace FlaxEditor.Windows.Profiler
private Dictionary<string, Guid> _assetPathToId; private Dictionary<string, Guid> _assetPathToId;
private Dictionary<Guid, Resource> _resourceCache; private Dictionary<Guid, Resource> _resourceCache;
private StringBuilder _stringBuilder; private StringBuilder _stringBuilder;
private GPUResource[] _gpuResourcesCached;
public MemoryGPU() public MemoryGPU()
: base("GPU Memory") : base("GPU Memory")
@@ -138,13 +139,15 @@ namespace FlaxEditor.Windows.Profiler
// Capture current GPU resources usage info // Capture current GPU resources usage info
var contentDatabase = Editor.Instance.ContentDatabase; var contentDatabase = Editor.Instance.ContentDatabase;
var gpuResources = GPUDevice.Instance.Resources; GPUDevice.Instance.GetResources(ref _gpuResourcesCached, out var count);
var resources = new Resource[gpuResources.Length]; var resources = new Resource[count];
var sb = _stringBuilder; var sb = _stringBuilder;
for (int i = 0; i < resources.Length; i++) for (int i = 0; i < count; i++)
{ {
var gpuResource = gpuResources[i]; var gpuResource = _gpuResourcesCached[i];
ref var resource = ref resources[i]; ref var resource = ref resources[i];
if (!gpuResource)
continue;
// Try to reuse cached resource info // Try to reuse cached resource info
var gpuResourceId = gpuResource.ID; var gpuResourceId = gpuResource.ID;
@@ -219,6 +222,7 @@ namespace FlaxEditor.Windows.Profiler
if (_resources == null) if (_resources == null)
_resources = new SamplesBuffer<Resource[]>(); _resources = new SamplesBuffer<Resource[]>();
_resources.Add(resources); _resources.Add(resources);
Array.Clear(_gpuResourcesCached);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -255,6 +259,7 @@ namespace FlaxEditor.Windows.Profiler
_assetPathToId?.Clear(); _assetPathToId?.Clear();
_tableRowsCache?.Clear(); _tableRowsCache?.Clear();
_stringBuilder?.Clear(); _stringBuilder?.Clear();
_gpuResourcesCached = null;
base.OnDestroy(); base.OnDestroy();
} }
+17 -1
View File
@@ -7,6 +7,7 @@
#include "Engine/Engine/Time.h" #include "Engine/Engine/Time.h"
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Threading/TaskGraph.h" #include "Engine/Threading/TaskGraph.h"
class BehaviorSystem : public TaskGraphSystem class BehaviorSystem : public TaskGraphSystem
@@ -38,6 +39,7 @@ TaskGraphSystem* Behavior::System = nullptr;
void BehaviorSystem::Job(int32 index) void BehaviorSystem::Job(int32 index)
{ {
PROFILE_CPU_NAMED("Behavior.Job"); PROFILE_CPU_NAMED("Behavior.Job");
PROFILE_MEM(AI);
Behaviors[index]->UpdateAsync(); Behaviors[index]->UpdateAsync();
} }
@@ -57,6 +59,7 @@ void BehaviorSystem::Execute(TaskGraph* graph)
bool BehaviorService::Init() bool BehaviorService::Init()
{ {
PROFILE_MEM(AI);
Behavior::System = New<BehaviorSystem>(); Behavior::System = New<BehaviorSystem>();
Engine::UpdateGraph->AddSystem(Behavior::System); Engine::UpdateGraph->AddSystem(Behavior::System);
return false; return false;
@@ -70,9 +73,9 @@ void BehaviorService::Dispose()
Behavior::Behavior(const SpawnParams& params) Behavior::Behavior(const SpawnParams& params)
: Script(params) : Script(params)
, Tree(this)
{ {
_knowledge.Behavior = this; _knowledge.Behavior = this;
Tree.Changed.Bind<Behavior, &Behavior::ResetLogic>(this);
} }
void Behavior::UpdateAsync() void Behavior::UpdateAsync()
@@ -172,6 +175,19 @@ void Behavior::OnDisable()
BehaviorServiceInstance.UpdateList.Remove(this); BehaviorServiceInstance.UpdateList.Remove(this);
} }
void Behavior::OnAssetChanged(Asset* asset, void* caller)
{
ResetLogic();
}
void Behavior::OnAssetLoaded(Asset* asset, void* caller)
{
}
void Behavior::OnAssetUnloaded(Asset* asset, void* caller)
{
}
#if USE_EDITOR #if USE_EDITOR
bool Behavior::GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior) bool Behavior::GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior)
+6 -1
View File
@@ -11,7 +11,7 @@
/// <summary> /// <summary>
/// Behavior instance script that runs Behavior Tree execution. /// Behavior instance script that runs Behavior Tree execution.
/// </summary> /// </summary>
API_CLASS(Attributes="Category(\"Flax Engine\")") class FLAXENGINE_API Behavior : public Script API_CLASS(Attributes="Category(\"Flax Engine\")") class FLAXENGINE_API Behavior : public Script, private IAssetReference
{ {
API_AUTO_SERIALIZATION(); API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE(Behavior); DECLARE_SCRIPTING_TYPE(Behavior);
@@ -92,6 +92,11 @@ public:
void OnDisable() override; void OnDisable() override;
private: private:
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
#if USE_EDITOR #if USE_EDITOR
// Editor-only utilities to debug nodes state. // Editor-only utilities to debug nodes state.
API_FUNCTION(Internal) static bool GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior); API_FUNCTION(Internal) static bool GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior);
+2
View File
@@ -4,6 +4,7 @@
#include "BehaviorTree.h" #include "BehaviorTree.h"
#include "BehaviorTreeNodes.h" #include "BehaviorTreeNodes.h"
#include "BehaviorKnowledgeSelector.h" #include "BehaviorKnowledgeSelector.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/BinaryModule.h" #include "Engine/Scripting/BinaryModule.h"
#include "Engine/Scripting/ManagedCLR/MProperty.h" #include "Engine/Scripting/ManagedCLR/MProperty.h"
@@ -144,6 +145,7 @@ BehaviorKnowledge::~BehaviorKnowledge()
void BehaviorKnowledge::InitMemory(BehaviorTree* tree) void BehaviorKnowledge::InitMemory(BehaviorTree* tree)
{ {
PROFILE_MEM(AI);
if (Tree) if (Tree)
FreeMemory(); FreeMemory();
if (!tree) if (!tree)
+2
View File
@@ -10,6 +10,7 @@
#include "Engine/Serialization/JsonSerializer.h" #include "Engine/Serialization/JsonSerializer.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "FlaxEngine.Gen.h" #include "FlaxEngine.Gen.h"
#if USE_EDITOR #if USE_EDITOR
#include "Engine/Level/Level.h" #include "Engine/Level/Level.h"
@@ -275,6 +276,7 @@ Asset::LoadResult BehaviorTree::load()
if (surfaceChunk == nullptr) if (surfaceChunk == nullptr)
return LoadResult::MissingDataChunk; return LoadResult::MissingDataChunk;
MemoryReadStream surfaceStream(surfaceChunk->Get(), surfaceChunk->Size()); MemoryReadStream surfaceStream(surfaceChunk->Get(), surfaceChunk->Size());
PROFILE_MEM(AI);
if (Graph.Load(&surfaceStream, true)) if (Graph.Load(&surfaceStream, true))
{ {
LOG(Warning, "Failed to load graph \'{0}\'", ToString()); LOG(Warning, "Failed to load graph \'{0}\'", ToString());
+9 -3
View File
@@ -4,6 +4,7 @@
#include "AnimEvent.h" #include "AnimEvent.h"
#include "Engine/Engine/Engine.h" #include "Engine/Engine/Engine.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Level/Actors/AnimatedModel.h" #include "Engine/Level/Actors/AnimatedModel.h"
#include "Engine/Engine/Time.h" #include "Engine/Engine/Time.h"
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
@@ -52,7 +53,7 @@ namespace
AnimationsService AnimationManagerInstance; AnimationsService AnimationManagerInstance;
TaskGraphSystem* Animations::System = nullptr; TaskGraphSystem* Animations::System = nullptr;
ConcurrentSystemLocker Animations::SystemLocker; ReadWriteLock Animations::SystemLocker;
#if USE_EDITOR #if USE_EDITOR
Delegate<Animations::DebugFlowInfo> Animations::DebugFlow; Delegate<Animations::DebugFlowInfo> Animations::DebugFlow;
#endif #endif
@@ -69,6 +70,7 @@ AnimContinuousEvent::AnimContinuousEvent(const SpawnParams& params)
bool AnimationsService::Init() bool AnimationsService::Init()
{ {
PROFILE_MEM(Animations);
Animations::System = New<AnimationsSystem>(); Animations::System = New<AnimationsSystem>();
Engine::UpdateGraph->AddSystem(Animations::System); Engine::UpdateGraph->AddSystem(Animations::System);
return false; return false;
@@ -83,6 +85,7 @@ void AnimationsService::Dispose()
void AnimationsSystem::Job(int32 index) void AnimationsSystem::Job(int32 index)
{ {
PROFILE_CPU_NAMED("Animations.Job"); PROFILE_CPU_NAMED("Animations.Job");
PROFILE_MEM(Animations);
auto animatedModel = AnimationManagerInstance.UpdateList[index]; auto animatedModel = AnimationManagerInstance.UpdateList[index];
if (CanUpdateModel(animatedModel)) if (CanUpdateModel(animatedModel))
{ {
@@ -121,7 +124,7 @@ void AnimationsSystem::Execute(TaskGraph* graph)
Active = true; Active = true;
// Ensure no animation assets can be reloaded/modified during async update // Ensure no animation assets can be reloaded/modified during async update
Animations::SystemLocker.Begin(false); Animations::SystemLocker.ReadLock();
// Setup data for async update // Setup data for async update
const auto& tickData = Time::Update; const auto& tickData = Time::Update;
@@ -147,6 +150,7 @@ void AnimationsSystem::PostExecute(TaskGraph* graph)
if (!Active) if (!Active)
return; return;
PROFILE_CPU_NAMED("Animations.PostExecute"); PROFILE_CPU_NAMED("Animations.PostExecute");
PROFILE_MEM(Animations);
// Update gameplay // Update gameplay
for (int32 index = 0; index < AnimationManagerInstance.UpdateList.Count(); index++) for (int32 index = 0; index < AnimationManagerInstance.UpdateList.Count(); index++)
@@ -161,16 +165,18 @@ void AnimationsSystem::PostExecute(TaskGraph* graph)
// Cleanup // Cleanup
AnimationManagerInstance.UpdateList.Clear(); AnimationManagerInstance.UpdateList.Clear();
Animations::SystemLocker.End(false); Animations::SystemLocker.ReadUnlock();
Active = false; Active = false;
} }
void Animations::AddToUpdate(AnimatedModel* obj) void Animations::AddToUpdate(AnimatedModel* obj)
{ {
ScopeWriteLock lock(SystemLocker);
AnimationManagerInstance.UpdateList.Add(obj); AnimationManagerInstance.UpdateList.Add(obj);
} }
void Animations::RemoveFromUpdate(AnimatedModel* obj) void Animations::RemoveFromUpdate(AnimatedModel* obj)
{ {
ScopeWriteLock lock(SystemLocker);
AnimationManagerInstance.UpdateList.Remove(obj); AnimationManagerInstance.UpdateList.Remove(obj);
} }
+1 -2
View File
@@ -4,7 +4,6 @@
#include "Engine/Scripting/ScriptingType.h" #include "Engine/Scripting/ScriptingType.h"
#include "Engine/Core/Delegate.h" #include "Engine/Core/Delegate.h"
#include "Engine/Threading/ConcurrentSystemLocker.h"
class TaskGraphSystem; class TaskGraphSystem;
class AnimatedModel; class AnimatedModel;
@@ -23,7 +22,7 @@ API_CLASS(Static) class FLAXENGINE_API Animations
API_FIELD(ReadOnly) static TaskGraphSystem* System; API_FIELD(ReadOnly) static TaskGraphSystem* System;
// Data access locker for animations data. // Data access locker for animations data.
static ConcurrentSystemLocker SystemLocker; static ReadWriteLock SystemLocker;
#if USE_EDITOR #if USE_EDITOR
// Data wrapper for the debug flow information. // Data wrapper for the debug flow information.
@@ -6,6 +6,7 @@
#include "Engine/Content/Assets/SkinnedModel.h" #include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Graphics/Models/SkeletonData.h" #include "Engine/Graphics/Models/SkeletonData.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Threading/Threading.h"
extern void RetargetSkeletonPose(const SkeletonData& sourceSkeleton, const SkeletonData& targetSkeleton, const SkinnedModel::SkeletonMapping& mapping, const Transform* sourceNodes, Transform* targetNodes); extern void RetargetSkeletonPose(const SkeletonData& sourceSkeleton, const SkeletonData& targetSkeleton, const SkinnedModel::SkeletonMapping& mapping, const Transform* sourceNodes, Transform* targetNodes);
@@ -7,6 +7,7 @@
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Content/Deprecated.h" #include "Engine/Content/Deprecated.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Audio/AudioClip.h" #include "Engine/Audio/AudioClip.h"
#include "Engine/Graphics/PostProcessSettings.h" #include "Engine/Graphics/PostProcessSettings.h"
#if USE_EDITOR #if USE_EDITOR
@@ -249,6 +250,7 @@ bool SceneAnimation::Save(const StringView& path)
Asset::LoadResult SceneAnimation::load() Asset::LoadResult SceneAnimation::load()
{ {
TrackStatesCount = 0; TrackStatesCount = 0;
PROFILE_MEM(AnimationsData);
// Get the data chunk // Get the data chunk
if (LoadChunk(0)) if (LoadChunk(0))
@@ -12,6 +12,7 @@
#include "Engine/Audio/AudioSource.h" #include "Engine/Audio/AudioSource.h"
#include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderTask.h"
#include "Engine/Renderer/RenderList.h" #include "Engine/Renderer/RenderList.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/Script.h" #include "Engine/Scripting/Script.h"
#include "Engine/Scripting/ManagedCLR/MException.h" #include "Engine/Scripting/ManagedCLR/MException.h"
@@ -151,6 +152,7 @@ void SceneAnimationPlayer::Tick(float dt)
SceneAnimation* anim = Animation.Get(); SceneAnimation* anim = Animation.Get();
if (!anim || !anim->IsLoaded()) if (!anim || !anim->IsLoaded())
return; return;
PROFILE_MEM(Animations);
// Setup state // Setup state
if (_tracks.Count() != anim->TrackStatesCount) if (_tracks.Count() != anim->TrackStatesCount)
@@ -229,6 +231,7 @@ void SceneAnimationPlayer::MapTrack(const StringView& from, const Guid& to)
SceneAnimation* anim = Animation.Get(); SceneAnimation* anim = Animation.Get();
if (!anim || !anim->IsLoaded()) if (!anim || !anim->IsLoaded())
return; return;
PROFILE_MEM(Animations);
for (int32 j = 0; j < anim->Tracks.Count(); j++) for (int32 j = 0; j < anim->Tracks.Count(); j++)
{ {
const auto& track = anim->Tracks[j]; const auto& track = anim->Tracks[j];
+3
View File
@@ -8,6 +8,7 @@
#include "Engine/Scripting/BinaryModule.h" #include "Engine/Scripting/BinaryModule.h"
#include "Engine/Level/Level.h" #include "Engine/Level/Level.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Engine/Engine.h" #include "Engine/Engine/Engine.h"
#include "Engine/Engine/CommandLine.h" #include "Engine/Engine/CommandLine.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
@@ -151,6 +152,7 @@ void Audio::SetEnableHRTF(bool value)
bool AudioService::Init() bool AudioService::Init()
{ {
PROFILE_CPU_NAMED("Audio.Init"); PROFILE_CPU_NAMED("Audio.Init");
PROFILE_MEM(Audio);
const auto settings = AudioSettings::Get(); const auto settings = AudioSettings::Get();
const bool mute = CommandLine::Options.Mute.IsTrue() || settings->DisableAudio; const bool mute = CommandLine::Options.Mute.IsTrue() || settings->DisableAudio;
@@ -211,6 +213,7 @@ bool AudioService::Init()
void AudioService::Update() void AudioService::Update()
{ {
PROFILE_CPU_NAMED("Audio.Update"); PROFILE_CPU_NAMED("Audio.Update");
PROFILE_MEM(Audio);
// Update the master volume // Update the master volume
float masterVolume = MasterVolume; float masterVolume = MasterVolume;
+44 -21
View File
@@ -7,50 +7,67 @@
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Content/Upgraders/AudioClipUpgrader.h" #include "Engine/Content/Upgraders/AudioClipUpgrader.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/ManagedCLR/MUtils.h" #include "Engine/Scripting/ManagedCLR/MUtils.h"
#include "Engine/Streaming/StreamingGroup.h" #include "Engine/Streaming/StreamingGroup.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h" #include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
#include "Engine/Tools/AudioTool/AudioTool.h" #include "Engine/Tools/AudioTool/AudioTool.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
REGISTER_BINARY_ASSET_WITH_UPGRADER(AudioClip, "FlaxEngine.AudioClip", AudioClipUpgrader, false); REGISTER_BINARY_ASSET_WITH_UPGRADER(AudioClip, "FlaxEngine.AudioClip", AudioClipUpgrader, false);
AudioClip::StreamingTask::StreamingTask(AudioClip* asset)
: _asset(asset)
, _dataLock(asset->Storage->Lock())
{
}
bool AudioClip::StreamingTask::HasReference(Object* resource) const
{
return _asset == resource;
}
bool AudioClip::StreamingTask::Run() bool AudioClip::StreamingTask::Run()
{ {
AssetReference<AudioClip> ref = _asset.Get(); PROFILE_CPU_NAMED("AudioStreaming");
if (ref == nullptr || AudioBackend::Instance == nullptr) PROFILE_MEM(Audio);
AssetReference<AudioClip> clip = _asset.Get();
if (clip == nullptr || AudioBackend::Instance == nullptr)
return true; return true;
ScopeLock lock(ref->Locker); #if TRACY_ENABLE
const auto& queue = ref->StreamingQueue; const StringView name(clip->GetPath());
if (queue.Count() == 0) ZoneName(*name, name.Length());
return false; #endif
auto clip = ref.Get();
// Update the buffers // Process the loading queue (hold the asset lock)
clip->Locker.Lock();
const auto& queue = clip->StreamingQueue;
Array<int32, FixedAllocation<ASSET_FILE_DATA_CHUNKS>> loadQueue;
for (int32 i = 0; i < queue.Count(); i++) for (int32 i = 0; i < queue.Count(); i++)
{ {
const auto idx = queue[i]; const int32 idx = queue[i];
uint32& bufferID = clip->Buffers[idx]; uint32& bufferID = clip->Buffers[idx];
if (bufferID == 0) if (bufferID == 0)
{ {
bufferID = AudioBackend::Buffer::Create(); // Load buffers outside the asset lock to prevent lock contention
loadQueue.Add(idx);
} }
else else
{ {
// Release unused data // Release unused buffer
AudioBackend::Buffer::Delete(bufferID); AudioBackend::Buffer::Delete(bufferID);
bufferID = 0; bufferID = 0;
} }
} }
clip->Locker.Unlock();
// Load missing buffers data (from asset chunks) // Load missing buffers data (from asset chunks)
for (int32 i = 0; i < queue.Count(); i++) for (int32 i = 0; i < loadQueue.Count(); i++)
{ {
if (clip->WriteBuffer(queue[i])) if (clip->WriteBuffer(loadQueue[i]))
{
return true; return true;
}
} }
// Update the sources // Update the sources
@@ -318,6 +335,7 @@ bool AudioClip::init(AssetInitData& initData)
Asset::LoadResult AudioClip::load() Asset::LoadResult AudioClip::load()
{ {
PROFILE_MEM(Audio);
#if !COMPILE_WITH_OGG_VORBIS #if !COMPILE_WITH_OGG_VORBIS
if (AudioHeader.Format == AudioFormat::Vorbis) if (AudioHeader.Format == AudioFormat::Vorbis)
{ {
@@ -410,14 +428,11 @@ void AudioClip::unload(bool isReloading)
bool AudioClip::WriteBuffer(int32 chunkIndex) bool AudioClip::WriteBuffer(int32 chunkIndex)
{ {
// Ignore if buffer is not created
const uint32 bufferID = Buffers[chunkIndex];
if (bufferID == 0)
return false;
// Ensure audio backend exists // Ensure audio backend exists
if (AudioBackend::Instance == nullptr) if (AudioBackend::Instance == nullptr)
return true; return true;
PROFILE_CPU();
PROFILE_MEM(Audio);
const auto chunk = GetChunk(chunkIndex); const auto chunk = GetChunk(chunkIndex);
if (chunk == nullptr || chunk->IsMissing()) if (chunk == nullptr || chunk->IsMissing())
@@ -429,6 +444,7 @@ bool AudioClip::WriteBuffer(int32 chunkIndex)
Array<byte> tmp1, tmp2; Array<byte> tmp1, tmp2;
AudioDataInfo info = AudioHeader.Info; AudioDataInfo info = AudioHeader.Info;
const uint32 bytesPerSample = info.BitDepth / 8; const uint32 bytesPerSample = info.BitDepth / 8;
ZoneValue(chunk->Size() / 1024); // Audio data size (in kB)
// Get raw data or decompress it // Get raw data or decompress it
switch (Format()) switch (Format())
@@ -436,6 +452,7 @@ bool AudioClip::WriteBuffer(int32 chunkIndex)
case AudioFormat::Vorbis: case AudioFormat::Vorbis:
{ {
#if COMPILE_WITH_OGG_VORBIS #if COMPILE_WITH_OGG_VORBIS
PROFILE_CPU_NAMED("OggVorbisDecode");
OggVorbisDecoder decoder; OggVorbisDecoder decoder;
MemoryReadStream stream(chunk->Get(), chunk->Size()); MemoryReadStream stream(chunk->Get(), chunk->Size());
AudioDataInfo tmpInfo; AudioDataInfo tmpInfo;
@@ -472,7 +489,13 @@ bool AudioClip::WriteBuffer(int32 chunkIndex)
data = Span<byte>(tmp2.Get(), tmp2.Count()); data = Span<byte>(tmp2.Get(), tmp2.Count());
} }
// Write samples to the audio buffer // Write samples to the audio buffer (create one if missing)
Locker.Lock(); // StreamingTask loads buffers without lock so do it here
uint32& bufferID = Buffers[chunkIndex];
if (bufferID == 0)
bufferID = AudioBackend::Buffer::Create();
AudioBackend::Buffer::Write(bufferID, data.Get(), info); AudioBackend::Buffer::Write(bufferID, data.Get(), info);
Locker.Unlock();
return false; return false;
} }
+2 -13
View File
@@ -44,22 +44,11 @@ public:
FlaxStorage::LockData _dataLock; FlaxStorage::LockData _dataLock;
public: public:
/// <summary> StreamingTask(AudioClip* asset);
/// Init
/// </summary>
/// <param name="asset">Parent asset</param>
StreamingTask(AudioClip* asset)
: _asset(asset)
, _dataLock(asset->Storage->Lock())
{
}
public: public:
// [ThreadPoolTask] // [ThreadPoolTask]
bool HasReference(Object* resource) const override bool HasReference(Object* resource) const override;
{
return _asset == resource;
}
protected: protected:
// [ThreadPoolTask] // [ThreadPoolTask]
+11 -4
View File
@@ -8,6 +8,7 @@
#include "Engine/Engine/Time.h" #include "Engine/Engine/Time.h"
#include "Engine/Level/Scene/Scene.h" #include "Engine/Level/Scene/Scene.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "AudioBackend.h" #include "AudioBackend.h"
#include "Audio.h" #include "Audio.h"
@@ -21,9 +22,8 @@ AudioSource::AudioSource(const SpawnParams& params)
, _playOnStart(false) , _playOnStart(false)
, _startTime(0.0f) , _startTime(0.0f)
, _allowSpatialization(true) , _allowSpatialization(true)
, Clip(this)
{ {
Clip.Changed.Bind<AudioSource, &AudioSource::OnClipChanged>(this);
Clip.Loaded.Bind<AudioSource, &AudioSource::OnClipLoaded>(this);
} }
void AudioSource::SetVolume(float value) void AudioSource::SetVolume(float value)
@@ -121,6 +121,7 @@ void AudioSource::Play()
auto state = _state; auto state = _state;
if (state == States::Playing) if (state == States::Playing)
return; return;
PROFILE_CPU();
if (Clip == nullptr || Clip->WaitForLoaded()) if (Clip == nullptr || Clip->WaitForLoaded())
{ {
LOG(Warning, "Cannot play audio source without a clip ({0})", GetNamePath()); LOG(Warning, "Cannot play audio source without a clip ({0})", GetNamePath());
@@ -189,6 +190,7 @@ void AudioSource::Stop()
{ {
if (_state == States::Stopped) if (_state == States::Stopped)
return; return;
PROFILE_CPU();
_state = States::Stopped; _state = States::Stopped;
_isActuallyPlayingSth = false; _isActuallyPlayingSth = false;
@@ -264,7 +266,7 @@ void AudioSource::RequestStreamingBuffersUpdate()
_needToUpdateStreamingBuffers = true; _needToUpdateStreamingBuffers = true;
} }
void AudioSource::OnClipChanged() void AudioSource::OnAssetChanged(Asset* asset, void* caller)
{ {
Stop(); Stop();
@@ -276,7 +278,7 @@ void AudioSource::OnClipChanged()
} }
} }
void AudioSource::OnClipLoaded() void AudioSource::OnAssetLoaded(Asset* asset, void* caller)
{ {
if (!SourceID) if (!SourceID)
return; return;
@@ -302,6 +304,10 @@ void AudioSource::OnClipLoaded()
} }
} }
void AudioSource::OnAssetUnloaded(Asset* asset, void* caller)
{
}
bool AudioSource::UseStreaming() const bool AudioSource::UseStreaming() const
{ {
if (Clip == nullptr || Clip->WaitForLoaded()) if (Clip == nullptr || Clip->WaitForLoaded())
@@ -383,6 +389,7 @@ bool AudioSource::IntersectsItself(const Ray& ray, Real& distance, Vector3& norm
void AudioSource::Update() void AudioSource::Update()
{ {
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Audio);
// Update the velocity // Update the velocity
const Vector3 pos = GetPosition(); const Vector3 pos = GetPosition();
+5 -3
View File
@@ -13,7 +13,7 @@
/// Whether or not an audio source is spatial is controlled by the assigned AudioClip.The volume and the pitch of a spatial audio source is controlled by its position and the AudioListener's position/direction/velocity. /// Whether or not an audio source is spatial is controlled by the assigned AudioClip.The volume and the pitch of a spatial audio source is controlled by its position and the AudioListener's position/direction/velocity.
/// </remarks> /// </remarks>
API_CLASS(Attributes="ActorContextMenu(\"New/Audio/Audio Source\"), ActorToolbox(\"Other\")") API_CLASS(Attributes="ActorContextMenu(\"New/Audio/Audio Source\"), ActorToolbox(\"Other\")")
class FLAXENGINE_API AudioSource : public Actor class FLAXENGINE_API AudioSource : public Actor, IAssetReference
{ {
DECLARE_SCENE_OBJECT(AudioSource); DECLARE_SCENE_OBJECT(AudioSource);
friend class AudioStreamingHandler; friend class AudioStreamingHandler;
@@ -293,8 +293,10 @@ public:
void RequestStreamingBuffersUpdate(); void RequestStreamingBuffersUpdate();
private: private:
void OnClipChanged(); // [IAssetReference]
void OnClipLoaded(); void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
/// <summary> /// <summary>
/// Plays the audio source. Should have buffer(s) binded before. /// Plays the audio source. Should have buffer(s) binded before.
@@ -9,6 +9,7 @@
#include "Engine/Tools/AudioTool/AudioTool.h" #include "Engine/Tools/AudioTool/AudioTool.h"
#include "Engine/Engine/Units.h" #include "Engine/Engine/Units.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Audio/Audio.h" #include "Engine/Audio/Audio.h"
#include "Engine/Audio/AudioListener.h" #include "Engine/Audio/AudioListener.h"
#include "Engine/Audio/AudioSource.h" #include "Engine/Audio/AudioSource.h"
@@ -321,6 +322,8 @@ void AudioBackendOAL::Listener_ReinitializeAll()
uint32 AudioBackendOAL::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler) uint32 AudioBackendOAL::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
{ {
PROFILE_MEM(Audio);
uint32 sourceID = 0; uint32 sourceID = 0;
ALC::Source::Rebuild(sourceID, position, orientation, volume, pitch, pan, loop, spatial, attenuation, minDistance, doppler); ALC::Source::Rebuild(sourceID, position, orientation, volume, pitch, pan, loop, spatial, attenuation, minDistance, doppler);
@@ -516,6 +519,7 @@ void AudioBackendOAL::Buffer_Delete(uint32 bufferID)
void AudioBackendOAL::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info) void AudioBackendOAL::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info)
{ {
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Audio);
// Pick the format for the audio data (it might not be supported natively) // Pick the format for the audio data (it might not be supported natively)
ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth); ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth);
@@ -625,6 +629,8 @@ AudioBackend::FeatureFlags AudioBackendOAL::Base_Features()
void AudioBackendOAL::Base_OnActiveDeviceChanged() void AudioBackendOAL::Base_OnActiveDeviceChanged()
{ {
PROFILE_MEM(Audio);
// Cleanup // Cleanup
Array<ALC::AudioSourceState> states; Array<ALC::AudioSourceState> states;
states.EnsureCapacity(Audio::Sources.Count()); states.EnsureCapacity(Audio::Sources.Count());
@@ -9,6 +9,7 @@
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Audio/Audio.h" #include "Engine/Audio/Audio.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS
// Tweak Win ver // Tweak Win ver
@@ -232,6 +233,7 @@ void AudioBackendXAudio2::Listener_ReinitializeAll()
uint32 AudioBackendXAudio2::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler) uint32 AudioBackendXAudio2::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
{ {
PROFILE_MEM(Audio);
ScopeLock lock(XAudio2::Locker); ScopeLock lock(XAudio2::Locker);
// Get first free source // Get first free source
@@ -580,6 +582,7 @@ void AudioBackendXAudio2::Source_DequeueProcessedBuffers(uint32 sourceID)
uint32 AudioBackendXAudio2::Buffer_Create() uint32 AudioBackendXAudio2::Buffer_Create()
{ {
PROFILE_MEM(Audio);
uint32 bufferID; uint32 bufferID;
ScopeLock lock(XAudio2::Locker); ScopeLock lock(XAudio2::Locker);
@@ -618,6 +621,7 @@ void AudioBackendXAudio2::Buffer_Delete(uint32 bufferID)
void AudioBackendXAudio2::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info) void AudioBackendXAudio2::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info)
{ {
PROFILE_MEM(Audio);
CHECK(info.NumChannels <= MAX_INPUT_CHANNELS); CHECK(info.NumChannels <= MAX_INPUT_CHANNELS);
XAudio2::Locker.Lock(); XAudio2::Locker.Lock();
+128 -70
View File
@@ -9,6 +9,7 @@
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/LogContext.h" #include "Engine/Core/LogContext.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/ManagedCLR/MCore.h" #include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Threading/MainThreadTask.h" #include "Engine/Threading/MainThreadTask.h"
#include "Engine/Threading/ThreadLocal.h" #include "Engine/Threading/ThreadLocal.h"
@@ -34,15 +35,18 @@ bool ContentDeprecated::Clear(bool newValue)
#endif #endif
AssetReferenceBase::AssetReferenceBase(IAssetReference* owner)
: _owner(owner)
{
}
AssetReferenceBase::~AssetReferenceBase() AssetReferenceBase::~AssetReferenceBase()
{ {
Asset* asset = _asset; Asset* asset = _asset;
if (asset) if (asset)
{ {
_asset = nullptr; _asset = nullptr;
asset->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this); asset->RemoveReference(this);
asset->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
asset->RemoveReference();
} }
} }
@@ -51,52 +55,60 @@ String AssetReferenceBase::ToString() const
return _asset ? _asset->ToString() : TEXT("<null>"); return _asset ? _asset->ToString() : TEXT("<null>");
} }
void AssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
{
if (_owner)
_owner->OnAssetChanged(asset, this);
}
void AssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
Loaded();
if (_owner)
_owner->OnAssetLoaded(asset, this);
}
void AssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
Unload();
OnSet(nullptr);
if (_owner)
_owner->OnAssetUnloaded(asset, this);
}
void AssetReferenceBase::OnSet(Asset* asset) void AssetReferenceBase::OnSet(Asset* asset)
{ {
auto e = _asset; auto e = _asset;
if (e != asset) if (e != asset)
{ {
if (e) if (e)
{ e->RemoveReference(this);
e->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
e->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
e->RemoveReference();
}
_asset = e = asset; _asset = e = asset;
if (e) if (e)
{ e->AddReference(this);
e->AddReference();
e->OnLoaded.Bind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
e->OnUnloaded.Bind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
}
Changed(); Changed();
if (_owner)
_owner->OnAssetChanged(asset, this);
if (e && e->IsLoaded()) if (e && e->IsLoaded())
{
Loaded(); Loaded();
if (_owner)
_owner->OnAssetLoaded(asset, this);
}
} }
} }
void AssetReferenceBase::OnLoaded(Asset* asset)
{
if (_asset != asset)
return;
Loaded();
}
void AssetReferenceBase::OnUnloaded(Asset* asset)
{
if (_asset != asset)
return;
Unload();
OnSet(nullptr);
}
WeakAssetReferenceBase::~WeakAssetReferenceBase() WeakAssetReferenceBase::~WeakAssetReferenceBase()
{ {
Asset* asset = _asset; Asset* asset = _asset;
if (asset) if (asset)
{ {
_asset = nullptr; _asset = nullptr;
asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this); asset->RemoveReference(this, true);
} }
} }
@@ -105,36 +117,43 @@ String WeakAssetReferenceBase::ToString() const
return _asset ? _asset->ToString() : TEXT("<null>"); return _asset ? _asset->ToString() : TEXT("<null>");
} }
void WeakAssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
{
}
void WeakAssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
{
}
void WeakAssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
Unload();
asset->RemoveReference(this, true);
_asset = nullptr;
}
void WeakAssetReferenceBase::OnSet(Asset* asset) void WeakAssetReferenceBase::OnSet(Asset* asset)
{ {
auto e = _asset; auto e = _asset;
if (e != asset) if (e != asset)
{ {
if (e) if (e)
e->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this); e->RemoveReference(this, true);
_asset = e = asset; _asset = e = asset;
if (e) if (e)
e->OnUnloaded.Bind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this); e->AddReference(this, true);
} }
} }
void WeakAssetReferenceBase::OnUnloaded(Asset* asset)
{
if (_asset != asset)
return;
Unload();
asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
_asset = nullptr;
}
SoftAssetReferenceBase::~SoftAssetReferenceBase() SoftAssetReferenceBase::~SoftAssetReferenceBase()
{ {
Asset* asset = _asset; Asset* asset = _asset;
if (asset) if (asset)
{ {
_asset = nullptr; _asset = nullptr;
asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this); asset->RemoveReference(this);
asset->RemoveReference();
} }
#if !BUILD_RELEASE #if !BUILD_RELEASE
_id = Guid::Empty; _id = Guid::Empty;
@@ -146,22 +165,34 @@ String SoftAssetReferenceBase::ToString() const
return _asset ? _asset->ToString() : (_id.IsValid() ? _id.ToString() : TEXT("<null>")); return _asset ? _asset->ToString() : (_id.IsValid() ? _id.ToString() : TEXT("<null>"));
} }
void SoftAssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
{
}
void SoftAssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
{
}
void SoftAssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
_asset->RemoveReference(this);
_asset = nullptr;
_id = Guid::Empty;
Changed();
}
void SoftAssetReferenceBase::OnSet(Asset* asset) void SoftAssetReferenceBase::OnSet(Asset* asset)
{ {
if (_asset == asset) if (_asset == asset)
return; return;
if (_asset) if (_asset)
{ _asset->RemoveReference(this);
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->RemoveReference();
}
_asset = asset; _asset = asset;
_id = asset ? asset->GetID() : Guid::Empty; _id = asset ? asset->GetID() : Guid::Empty;
if (asset) if (asset)
{ asset->AddReference(this);
asset->AddReference();
asset->OnUnloaded.Bind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
}
Changed(); Changed();
} }
@@ -170,10 +201,7 @@ void SoftAssetReferenceBase::OnSet(const Guid& id)
if (_id == id) if (_id == id)
return; return;
if (_asset) if (_asset)
{ _asset->RemoveReference(this);
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->RemoveReference();
}
_asset = nullptr; _asset = nullptr;
_id = id; _id = id;
Changed(); Changed();
@@ -184,21 +212,7 @@ void SoftAssetReferenceBase::OnResolve(const ScriptingTypeHandle& type)
ASSERT(!_asset); ASSERT(!_asset);
_asset = ::LoadAsset(_id, type); _asset = ::LoadAsset(_id, type);
if (_asset) if (_asset)
{ _asset->AddReference(this);
_asset->OnUnloaded.Bind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->AddReference();
}
}
void SoftAssetReferenceBase::OnUnloaded(Asset* asset)
{
if (_asset != asset)
return;
_asset->RemoveReference();
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset = nullptr;
_id = Guid::Empty;
Changed();
} }
Asset::Asset(const SpawnParams& params, const AssetInfo* info) Asset::Asset(const SpawnParams& params, const AssetInfo* info)
@@ -216,6 +230,39 @@ int32 Asset::GetReferencesCount() const
return (int32)Platform::AtomicRead(const_cast<int64 volatile*>(&_refCount)); return (int32)Platform::AtomicRead(const_cast<int64 volatile*>(&_refCount));
} }
void Asset::AddReference()
{
Platform::InterlockedIncrement(&_refCount);
}
void Asset::AddReference(IAssetReference* ref, bool week)
{
if (!week)
Platform::InterlockedIncrement(&_refCount);
if (ref)
{
//PROFILE_MEM(EngineDelegate); // Include references tracking memory within Delegate memory
ScopeLock lock(_referencesLocker);
_references.Add(ref);
}
}
void Asset::RemoveReference()
{
Platform::InterlockedDecrement(&_refCount);
}
void Asset::RemoveReference(IAssetReference* ref, bool week)
{
if (ref)
{
ScopeLock lock(_referencesLocker);
_references.Remove(ref);
}
if (!week)
Platform::InterlockedDecrement(&_refCount);
}
String Asset::ToString() const String Asset::ToString() const
{ {
return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath()); return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath());
@@ -232,7 +279,7 @@ void Asset::OnDeleteObject()
const bool wasMarkedToDelete = _deleteFileOnUnload != 0; const bool wasMarkedToDelete = _deleteFileOnUnload != 0;
#if USE_EDITOR #if USE_EDITOR
const String path = wasMarkedToDelete ? GetPath() : String::Empty; const String path = wasMarkedToDelete ? String(GetPath()) : String::Empty;
#endif #endif
const Guid id = GetID(); const Guid id = GetID();
@@ -354,6 +401,7 @@ uint64 Asset::GetMemoryUsage() const
if (Platform::AtomicRead(&_loadingTask)) if (Platform::AtomicRead(&_loadingTask))
result += sizeof(ContentLoadTask); result += sizeof(ContentLoadTask);
result += (OnLoaded.Capacity() + OnReloading.Capacity() + OnUnloaded.Capacity()) * sizeof(EventType::FunctionType); result += (OnLoaded.Capacity() + OnReloading.Capacity() + OnUnloaded.Capacity()) * sizeof(EventType::FunctionType);
result += _references.Capacity() * sizeof(HashSet<IAssetReference*>::Bucket);
Locker.Unlock(); Locker.Unlock();
return result; return result;
} }
@@ -444,6 +492,9 @@ bool Asset::WaitForLoaded(double timeoutInMilliseconds) const
} }
PROFILE_CPU(); PROFILE_CPU();
ZoneColor(TracyWaitZoneColor);
const StringView path(GetPath());
ZoneText(*path, path.Length());
Content::WaitForTask(loadingTask, timeoutInMilliseconds); Content::WaitForTask(loadingTask, timeoutInMilliseconds);
@@ -528,6 +579,7 @@ ContentLoadTask* Asset::createLoadingTask()
void Asset::startLoading() void Asset::startLoading()
{ {
PROFILE_MEM(ContentAssets);
ASSERT(!IsLoaded()); ASSERT(!IsLoaded());
ASSERT(Platform::AtomicRead(&_loadingTask) == 0); ASSERT(Platform::AtomicRead(&_loadingTask) == 0);
auto loadingTask = createLoadingTask(); auto loadingTask = createLoadingTask();
@@ -627,6 +679,9 @@ void Asset::onLoaded_MainThread()
ASSERT(IsInMainThread()); ASSERT(IsInMainThread());
// Send event // Send event
ScopeLock lock(_referencesLocker);
for (const auto& e : _references)
e.Item->OnAssetLoaded(this, this);
OnLoaded(this); OnLoaded(this);
} }
@@ -640,6 +695,9 @@ void Asset::onUnload_MainThread()
CancelStreaming(); CancelStreaming();
// Send event // Send event
ScopeLock lock(_referencesLocker);
for (const auto& e : _references)
e.Item->OnAssetUnloaded(this, this);
OnUnloaded(this); OnUnloaded(this);
} }
+30 -9
View File
@@ -18,6 +18,20 @@
public: \ public: \
explicit type(const SpawnParams& params, const AssetInfo* info) explicit type(const SpawnParams& params, const AssetInfo* info)
// Utility interface for objects that reference asset and want to get notified about asset reference changes.
class FLAXENGINE_API IAssetReference
{
public:
virtual ~IAssetReference() = default;
// Asset reference got changed.
virtual void OnAssetChanged(Asset* asset, void* caller) = 0;
// Asset got loaded.
virtual void OnAssetLoaded(Asset* asset, void* caller) = 0;
// Asset gets unloaded.
virtual void OnAssetUnloaded(Asset* asset, void* caller) = 0;
};
/// <summary> /// <summary>
/// Asset objects base class. /// Asset objects base class.
/// </summary> /// </summary>
@@ -48,6 +62,9 @@ protected:
int8 _deleteFileOnUnload : 1; // Indicates that asset source file should be removed on asset unload int8 _deleteFileOnUnload : 1; // Indicates that asset source file should be removed on asset unload
int8 _isVirtual : 1; // Indicates that asset is pure virtual (generated or temporary, has no storage so won't be saved) int8 _isVirtual : 1; // Indicates that asset is pure virtual (generated or temporary, has no storage so won't be saved)
HashSet<IAssetReference*> _references;
CriticalSection _referencesLocker; // TODO: convert into a single interlocked exchange for the current thread owning lock
public: public:
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Asset"/> class. /// Initializes a new instance of the <see cref="Asset"/> class.
@@ -88,24 +105,28 @@ public:
/// <summary> /// <summary>
/// Adds reference to that asset. /// Adds reference to that asset.
/// </summary> /// </summary>
FORCE_INLINE void AddReference() void AddReference();
{
Platform::InterlockedIncrement(&_refCount); /// <summary>
} /// Adds reference to that asset.
/// </summary>
void AddReference(IAssetReference* ref, bool week = false);
/// <summary> /// <summary>
/// Removes reference from that asset. /// Removes reference from that asset.
/// </summary> /// </summary>
FORCE_INLINE void RemoveReference() void RemoveReference();
{
Platform::InterlockedDecrement(&_refCount); /// <summary>
} /// Removes reference from that asset.
/// </summary>
void RemoveReference(IAssetReference* ref, bool week = false);
public: public:
/// <summary> /// <summary>
/// Gets the path to the asset storage file. In Editor, it reflects the actual file, in cooked Game, it fakes the Editor path to be informative for developers. /// Gets the path to the asset storage file. In Editor, it reflects the actual file, in cooked Game, it fakes the Editor path to be informative for developers.
/// </summary> /// </summary>
API_PROPERTY() virtual const String& GetPath() const = 0; API_PROPERTY() virtual StringView GetPath() const = 0;
/// <summary> /// <summary>
/// Gets the asset type name. /// Gets the asset type name.
+30 -3
View File
@@ -7,10 +7,11 @@
/// <summary> /// <summary>
/// Asset reference utility. Keeps reference to the linked asset object and handles load/unload events. /// Asset reference utility. Keeps reference to the linked asset object and handles load/unload events.
/// </summary> /// </summary>
class FLAXENGINE_API AssetReferenceBase class FLAXENGINE_API AssetReferenceBase : public IAssetReference
{ {
protected: protected:
Asset* _asset = nullptr; Asset* _asset = nullptr;
IAssetReference* _owner = nullptr;
public: public:
/// <summary> /// <summary>
@@ -36,6 +37,12 @@ public:
/// </summary> /// </summary>
AssetReferenceBase() = default; AssetReferenceBase() = default;
/// <summary>
/// Initializes a new instance of the <see cref="AssetReferenceBase"/> class.
/// </summary>
/// <param name="owner">The reference owner to keep notified about asset changes.</param>
AssetReferenceBase(IAssetReference* owner);
/// <summary> /// <summary>
/// Finalizes an instance of the <see cref="AssetReferenceBase"/> class. /// Finalizes an instance of the <see cref="AssetReferenceBase"/> class.
/// </summary> /// </summary>
@@ -63,10 +70,14 @@ public:
/// </summary> /// </summary>
String ToString() const; String ToString() const;
public:
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
protected: protected:
void OnSet(Asset* asset); void OnSet(Asset* asset);
void OnLoaded(Asset* asset);
void OnUnloaded(Asset* asset);
}; };
/// <summary> /// <summary>
@@ -87,6 +98,13 @@ public:
{ {
} }
/// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary>
explicit AssetReference(decltype(__nullptr))
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class. /// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary> /// </summary>
@@ -96,6 +114,15 @@ public:
OnSet((Asset*)asset); OnSet((Asset*)asset);
} }
/// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary>
/// <param name="owner">The reference owner to keep notified about asset changes.</param>
explicit AssetReference(IAssetReference* owner)
: AssetReferenceBase(owner)
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class. /// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary> /// </summary>
+4 -2
View File
@@ -9,6 +9,7 @@
#include "Engine/Animations/Animations.h" #include "Engine/Animations/Animations.h"
#include "Engine/Animations/SceneAnimations/SceneAnimation.h" #include "Engine/Animations/SceneAnimations/SceneAnimation.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#if USE_EDITOR #if USE_EDITOR
@@ -598,7 +599,8 @@ void Animation::OnScriptingDispose()
Asset::LoadResult Animation::load() Asset::LoadResult Animation::load()
{ {
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); PROFILE_MEM(AnimationsData);
ScopeWriteLock systemScope(Animations::SystemLocker);
// Get stream with animations data // Get stream with animations data
const auto dataChunk = GetChunk(0); const auto dataChunk = GetChunk(0);
@@ -730,7 +732,7 @@ Asset::LoadResult Animation::load()
void Animation::unload(bool isReloading) void Animation::unload(bool isReloading)
{ {
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); ScopeWriteLock systemScope(Animations::SystemLocker);
#if USE_EDITOR #if USE_EDITOR
if (_registeredForScriptingReload) if (_registeredForScriptingReload)
{ {
@@ -9,6 +9,7 @@
#include "Engine/Core/Types/DataContainer.h" #include "Engine/Core/Types/DataContainer.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Animations/Animations.h" #include "Engine/Animations/Animations.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
@@ -25,7 +26,8 @@ AnimationGraph::AnimationGraph(const SpawnParams& params, const AssetInfo* info)
Asset::LoadResult AnimationGraph::load() Asset::LoadResult AnimationGraph::load()
{ {
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); PROFILE_MEM(AnimationsData);
ScopeWriteLock systemScope(Animations::SystemLocker);
// Get stream with graph data // Get stream with graph data
const auto surfaceChunk = GetChunk(0); const auto surfaceChunk = GetChunk(0);
@@ -51,7 +53,7 @@ Asset::LoadResult AnimationGraph::load()
void AnimationGraph::unload(bool isReloading) void AnimationGraph::unload(bool isReloading)
{ {
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); ScopeWriteLock systemScope(Animations::SystemLocker);
Graph.Clear(); Graph.Clear();
} }
@@ -83,7 +85,8 @@ bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, b
Log::ArgumentNullException(); Log::ArgumentNullException();
return true; return true;
} }
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); PROFILE_MEM(AnimationsData);
ScopeWriteLock systemScope(Animations::SystemLocker);
// Create Graph data // Create Graph data
MemoryWriteStream writeStream(512); MemoryWriteStream writeStream(512);
@@ -169,7 +172,7 @@ bool AnimationGraph::SaveSurface(const BytesContainer& data)
{ {
if (OnCheckSave()) if (OnCheckSave())
return true; return true;
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); ScopeWriteLock systemScope(Animations::SystemLocker);
ScopeLock lock(Locker); ScopeLock lock(Locker);
if (IsVirtual()) if (IsVirtual())
@@ -8,6 +8,7 @@
#include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Serialization/MemoryWriteStream.h"
#endif #endif
#include "Engine/Animations/Animations.h" #include "Engine/Animations/Animations.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
@@ -20,7 +21,8 @@ AnimationGraphFunction::AnimationGraphFunction(const SpawnParams& params, const
Asset::LoadResult AnimationGraphFunction::load() Asset::LoadResult AnimationGraphFunction::load()
{ {
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); PROFILE_MEM(AnimationsData);
ScopeWriteLock systemScope(Animations::SystemLocker);
// Get graph data from chunk // Get graph data from chunk
const auto surfaceChunk = GetChunk(0); const auto surfaceChunk = GetChunk(0);
@@ -47,7 +49,7 @@ Asset::LoadResult AnimationGraphFunction::load()
void AnimationGraphFunction::unload(bool isReloading) void AnimationGraphFunction::unload(bool isReloading)
{ {
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); ScopeWriteLock systemScope(Animations::SystemLocker);
GraphData.Release(); GraphData.Release();
Inputs.Clear(); Inputs.Clear();
Outputs.Clear(); Outputs.Clear();
@@ -96,7 +98,7 @@ bool AnimationGraphFunction::SaveSurface(const BytesContainer& data) const
{ {
if (OnCheckSave()) if (OnCheckSave())
return true; return true;
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); ScopeWriteLock systemScope(Animations::SystemLocker);
ScopeLock lock(Locker); ScopeLock lock(Locker);
// Set Visject Surface data // Set Visject Surface data
+10 -4
View File
@@ -165,9 +165,13 @@ Asset::LoadResult Material::load()
MaterialGenerator generator; MaterialGenerator generator;
generator.Error.Bind(&OnGeneratorError); generator.Error.Bind(&OnGeneratorError);
if (_shaderHeader.Material.GraphVersion != MATERIAL_GRAPH_VERSION) if (_shaderHeader.Material.GraphVersion != MATERIAL_GRAPH_VERSION)
{
LOG(Info, "Converting material \'{0}\', from version {1} to {2}...", name, _shaderHeader.Material.GraphVersion, MATERIAL_GRAPH_VERSION); LOG(Info, "Converting material \'{0}\', from version {1} to {2}...", name, _shaderHeader.Material.GraphVersion, MATERIAL_GRAPH_VERSION);
}
else else
{
LOG(Info, "Updating material \'{0}\'...", name); LOG(Info, "Updating material \'{0}\'...", name);
}
// Load or create material surface // Load or create material surface
MaterialLayer* layer; MaterialLayer* layer;
@@ -410,16 +414,18 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
// Prepare // Prepare
auto& info = _shaderHeader.Material.Info; auto& info = _shaderHeader.Material.Info;
const bool isSurfaceOrTerrainOrDeformable = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain || info.Domain == MaterialDomain::Deformable; const bool isSurfaceOrTerrainOrDeformable = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain || info.Domain == MaterialDomain::Deformable;
const bool isOpaque = info.BlendMode == MaterialBlendMode::Opaque;
const bool useCustomData = info.ShadingModel == MaterialShadingModel::Subsurface || info.ShadingModel == MaterialShadingModel::Foliage; const bool useCustomData = info.ShadingModel == MaterialShadingModel::Subsurface || info.ShadingModel == MaterialShadingModel::Foliage;
const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && info.BlendMode != MaterialBlendMode::Opaque) || info.Domain == MaterialDomain::Particle; const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && !isOpaque) || info.Domain == MaterialDomain::Particle;
const bool useTess = const bool useTess =
info.TessellationMode != TessellationMethod::None && info.TessellationMode != TessellationMethod::None &&
RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrainOrDeformable; RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrainOrDeformable;
const bool useDistortion = const bool useDistortion =
(info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) && (info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) &&
info.BlendMode != MaterialBlendMode::Opaque && !isOpaque &&
EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseRefraction) && EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseRefraction) &&
(info.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None; (info.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None;
const MaterialShadingModel shadingModel = info.ShadingModel == MaterialShadingModel::CustomLit ? MaterialShadingModel::Unlit : info.ShadingModel;
// @formatter:off // @formatter:off
static const char* Numbers[] = static const char* Numbers[] =
@@ -431,7 +437,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
// Setup shader macros // Setup shader macros
options.Macros.Add({ "MATERIAL_DOMAIN", Numbers[(int32)info.Domain] }); options.Macros.Add({ "MATERIAL_DOMAIN", Numbers[(int32)info.Domain] });
options.Macros.Add({ "MATERIAL_BLEND", Numbers[(int32)info.BlendMode] }); options.Macros.Add({ "MATERIAL_BLEND", Numbers[(int32)info.BlendMode] });
options.Macros.Add({ "MATERIAL_SHADING_MODEL", Numbers[(int32)info.ShadingModel] }); options.Macros.Add({ "MATERIAL_SHADING_MODEL", Numbers[(int32)shadingModel] });
options.Macros.Add({ "MATERIAL_MASKED", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseMask) ? 1 : 0] }); options.Macros.Add({ "MATERIAL_MASKED", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseMask) ? 1 : 0] });
options.Macros.Add({ "DECAL_BLEND_MODE", Numbers[(int32)info.DecalBlendingMode] }); options.Macros.Add({ "DECAL_BLEND_MODE", Numbers[(int32)info.DecalBlendingMode] });
options.Macros.Add({ "USE_EMISSIVE", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseEmissive) ? 1 : 0] }); options.Macros.Add({ "USE_EMISSIVE", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseEmissive) ? 1 : 0] });
@@ -488,7 +494,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
options.Macros.Add({ "IS_PARTICLE", Numbers[info.Domain == MaterialDomain::Particle ? 1 : 0] }); options.Macros.Add({ "IS_PARTICLE", Numbers[info.Domain == MaterialDomain::Particle ? 1 : 0] });
options.Macros.Add({ "IS_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 1 : 0] }); options.Macros.Add({ "IS_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 1 : 0] });
options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] }); options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] });
options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && info.BlendMode == MaterialBlendMode::Opaque ? 1 : 0] }); options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && isOpaque ? 1 : 0] });
options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] }); options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] });
#endif #endif
} }
+7 -1
View File
@@ -18,6 +18,7 @@
#include "Engine/Graphics/Models/MeshDeformation.h" #include "Engine/Graphics/Models/MeshDeformation.h"
#include "Engine/Graphics/Textures/GPUTexture.h" #include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Renderer/DrawCall.h" #include "Engine/Renderer/DrawCall.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Tools/ModelTool/ModelTool.h" #include "Engine/Tools/ModelTool/ModelTool.h"
@@ -304,6 +305,7 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
Log::ArgumentOutOfRangeException(); Log::ArgumentOutOfRangeException();
return true; return true;
} }
PROFILE_MEM(GraphicsMeshes);
// Dispose previous data and disable streaming (will start data uploading tasks manually) // Dispose previous data and disable streaming (will start data uploading tasks manually)
StopStreaming(); StopStreaming();
@@ -343,6 +345,7 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
bool Model::LoadHeader(ReadStream& stream, byte& headerVersion) bool Model::LoadHeader(ReadStream& stream, byte& headerVersion)
{ {
PROFILE_MEM(GraphicsMeshes);
if (ModelBase::LoadHeader(stream, headerVersion)) if (ModelBase::LoadHeader(stream, headerVersion))
return true; return true;
@@ -509,6 +512,7 @@ bool Model::Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk
void Model::SetupMaterialSlots(int32 slotsCount) void Model::SetupMaterialSlots(int32 slotsCount)
{ {
PROFILE_MEM(GraphicsMeshes);
ModelBase::SetupMaterialSlots(slotsCount); ModelBase::SetupMaterialSlots(slotsCount);
// Adjust meshes indices for slots // Adjust meshes indices for slots
@@ -584,6 +588,8 @@ int32 Model::GetAllocatedResidency() const
Asset::LoadResult Model::load() Asset::LoadResult Model::load()
{ {
PROFILE_MEM(GraphicsMeshes);
// Get header chunk // Get header chunk
auto chunk0 = GetChunk(0); auto chunk0 = GetChunk(0);
if (chunk0 == nullptr || chunk0->IsMissing()) if (chunk0 == nullptr || chunk0->IsMissing())
@@ -613,7 +619,7 @@ Asset::LoadResult Model::load()
{ {
String name; String name;
#if !BUILD_RELEASE #if !BUILD_RELEASE
name = GetPath() + TEXT(".SDF"); name = String(GetPath()) + TEXT(".SDF");
#endif #endif
SDF.Texture = GPUDevice::Instance->CreateTexture(name); SDF.Texture = GPUDevice::Instance->CreateTexture(name);
} }
@@ -5,10 +5,12 @@
#include "Engine/Core/Math/Transform.h" #include "Engine/Core/Math/Transform.h"
#include "Engine/Content/WeakAssetReference.h" #include "Engine/Content/WeakAssetReference.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Graphics/Config.h" #include "Engine/Graphics/Config.h"
#include "Engine/Graphics/Models/MeshBase.h" #include "Engine/Graphics/Models/MeshBase.h"
#include "Engine/Graphics/Models/MeshDeformation.h" #include "Engine/Graphics/Models/MeshDeformation.h"
#include "Engine/Graphics/Shaders/GPUVertexLayout.h" #include "Engine/Graphics/Shaders/GPUVertexLayout.h"
#include "Engine/Threading/Threading.h"
#if GPU_ENABLE_ASYNC_RESOURCES_CREATION #if GPU_ENABLE_ASYNC_RESOURCES_CREATION
#include "Engine/Threading/ThreadPoolTask.h" #include "Engine/Threading/ThreadPoolTask.h"
#define STREAM_TASK_BASE ThreadPoolTask #define STREAM_TASK_BASE ThreadPoolTask
@@ -51,6 +53,7 @@ public:
AssetReference<ModelBase> model = _model.Get(); AssetReference<ModelBase> model = _model.Get();
if (model == nullptr) if (model == nullptr)
return true; return true;
PROFILE_MEM(GraphicsMeshes);
// Get data // Get data
BytesContainer data; BytesContainer data;
@@ -334,6 +337,8 @@ bool ModelBase::LoadHeader(ReadStream& stream, byte& headerVersion)
bool ModelBase::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly) bool ModelBase::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly)
{ {
PROFILE_MEM(GraphicsMeshes);
// Load descriptor // Load descriptor
static_assert(MODEL_MESH_VERSION == 2, "Update code"); static_assert(MODEL_MESH_VERSION == 2, "Update code");
uint32 vertices, triangles; uint32 vertices, triangles;
+9 -1
View File
@@ -4,6 +4,10 @@
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Graphics/Shaders/GPUShader.h"
#if GPU_ENABLE_RESOURCE_NAMING && !USE_EDITOR
#include "Engine/Content/Content.h"
#include "Engine/Content/Cache/AssetsCache.h"
#endif
#include "Engine/Content/Upgraders/ShaderAssetUpgrader.h" #include "Engine/Content/Upgraders/ShaderAssetUpgrader.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
@@ -14,7 +18,11 @@ Shader::Shader(const SpawnParams& params, const AssetInfo* info)
: ShaderAssetTypeBase<BinaryAsset>(params, info) : ShaderAssetTypeBase<BinaryAsset>(params, info)
{ {
ASSERT(GPUDevice::Instance); ASSERT(GPUDevice::Instance);
_shader = GPUDevice::Instance->CreateShader(info->Path); StringView name = info->Path;
#if GPU_ENABLE_RESOURCE_NAMING && !USE_EDITOR
name = Content::GetRegistry()->GetEditorAssetPath(info->ID);
#endif
_shader = GPUDevice::Instance->CreateShader(name);
ASSERT(_shader); ASSERT(_shader);
GPU = _shader; GPU = _shader;
} }
@@ -6,6 +6,7 @@
#include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Content/Upgraders/SkeletonMaskUpgrader.h" #include "Engine/Content/Upgraders/SkeletonMaskUpgrader.h"
#include "Engine/Threading/Threading.h"
REGISTER_BINARY_ASSET_WITH_UPGRADER(SkeletonMask, "FlaxEngine.SkeletonMask", SkeletonMaskUpgrader, true); REGISTER_BINARY_ASSET_WITH_UPGRADER(SkeletonMask, "FlaxEngine.SkeletonMask", SkeletonMaskUpgrader, true);
@@ -18,6 +18,7 @@
#include "Engine/Content/Upgraders/SkinnedModelAssetUpgrader.h" #include "Engine/Content/Upgraders/SkinnedModelAssetUpgrader.h"
#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h" #include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Renderer/DrawCall.h" #include "Engine/Renderer/DrawCall.h"
#if USE_EDITOR #if USE_EDITOR
#include "Engine/Graphics/Models/ModelData.h" #include "Engine/Graphics/Models/ModelData.h"
@@ -458,6 +459,7 @@ bool SkinnedModel::Init(const Span<int32>& meshesCountPerLod)
Log::ArgumentOutOfRangeException(); Log::ArgumentOutOfRangeException();
return true; return true;
} }
PROFILE_MEM(GraphicsMeshes);
// Dispose previous data and disable streaming (will start data uploading tasks manually) // Dispose previous data and disable streaming (will start data uploading tasks manually)
StopStreaming(); StopStreaming();
@@ -501,6 +503,7 @@ void BlendShape::LoadHeader(ReadStream& stream, byte headerVersion)
void BlendShape::Load(ReadStream& stream, byte meshVersion) void BlendShape::Load(ReadStream& stream, byte meshVersion)
{ {
PROFILE_MEM(GraphicsMeshes);
UseNormals = stream.ReadBool(); UseNormals = stream.ReadBool();
stream.ReadUint32(&MinVertexIndex); stream.ReadUint32(&MinVertexIndex);
stream.ReadUint32(&MaxVertexIndex); stream.ReadUint32(&MaxVertexIndex);
@@ -531,6 +534,7 @@ void BlendShape::Save(WriteStream& stream) const
bool SkinnedModel::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly) bool SkinnedModel::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly)
{ {
PROFILE_MEM(GraphicsMeshes);
if (ModelBase::LoadMesh(stream, meshVersion, mesh, dataIfReadOnly)) if (ModelBase::LoadMesh(stream, meshVersion, mesh, dataIfReadOnly))
return true; return true;
static_assert(MODEL_MESH_VERSION == 2, "Update code"); static_assert(MODEL_MESH_VERSION == 2, "Update code");
@@ -560,6 +564,7 @@ bool SkinnedModel::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase
bool SkinnedModel::LoadHeader(ReadStream& stream, byte& headerVersion) bool SkinnedModel::LoadHeader(ReadStream& stream, byte& headerVersion)
{ {
PROFILE_MEM(GraphicsMeshes);
if (ModelBase::LoadHeader(stream, headerVersion)) if (ModelBase::LoadHeader(stream, headerVersion))
return true; return true;
static_assert(MODEL_HEADER_VERSION == 2, "Update code"); static_assert(MODEL_HEADER_VERSION == 2, "Update code");
@@ -861,6 +866,7 @@ uint64 SkinnedModel::GetMemoryUsage() const
void SkinnedModel::SetupMaterialSlots(int32 slotsCount) void SkinnedModel::SetupMaterialSlots(int32 slotsCount)
{ {
PROFILE_MEM(GraphicsMeshes);
ModelBase::SetupMaterialSlots(slotsCount); ModelBase::SetupMaterialSlots(slotsCount);
// Adjust meshes indices for slots // Adjust meshes indices for slots
@@ -954,6 +960,7 @@ Asset::LoadResult SkinnedModel::load()
if (chunk0 == nullptr || chunk0->IsMissing()) if (chunk0 == nullptr || chunk0->IsMissing())
return LoadResult::MissingDataChunk; return LoadResult::MissingDataChunk;
MemoryReadStream headerStream(chunk0->Get(), chunk0->Size()); MemoryReadStream headerStream(chunk0->Get(), chunk0->Size());
PROFILE_MEM(GraphicsMeshes);
// Load asset data (anything but mesh contents that use streaming) // Load asset data (anything but mesh contents that use streaming)
byte headerVersion; byte headerVersion;
@@ -18,6 +18,7 @@
#include "Engine/Serialization/Serialization.h" #include "Engine/Serialization/Serialization.h"
#include "Engine/Serialization/JsonWriter.h" #include "Engine/Serialization/JsonWriter.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Utilities/StringConverter.h" #include "Engine/Utilities/StringConverter.h"
#include "Engine/Threading/MainThreadTask.h" #include "Engine/Threading/MainThreadTask.h"
#include "Engine/Level/SceneObject.h" #include "Engine/Level/SceneObject.h"
@@ -37,10 +38,12 @@ namespace
void PrintStack(LogType type) void PrintStack(LogType type)
{ {
#if LOG_ENABLE
const String stack = VisualScripting::GetStackTrace(); const String stack = VisualScripting::GetStackTrace();
Log::Logger::Write(type, TEXT("Visual Script stack trace:")); Log::Logger::Write(type, TEXT("Visual Script stack trace:"));
Log::Logger::Write(type, stack); Log::Logger::Write(type, stack);
Log::Logger::Write(type, TEXT("")); Log::Logger::Write(type, TEXT(""));
#endif
} }
bool SerializeValue(const Variant& a, const Variant& b) bool SerializeValue(const Variant& a, const Variant& b)
@@ -1340,6 +1343,8 @@ bool VisualScript::Save(const StringView& path)
Asset::LoadResult VisualScript::load() Asset::LoadResult VisualScript::load()
{ {
PROFILE_MEM(ScriptingVisual);
// Build Visual Script typename that is based on asset id // Build Visual Script typename that is based on asset id
String typeName = _id.ToString(); String typeName = _id.ToString();
StringUtils::ConvertUTF162ANSI(typeName.Get(), _typenameChars, 32); StringUtils::ConvertUTF162ANSI(typeName.Get(), _typenameChars, 32);
@@ -1532,6 +1537,7 @@ Asset::LoadResult VisualScript::load()
void VisualScript::unload(bool isReloading) void VisualScript::unload(bool isReloading)
{ {
PROFILE_MEM(ScriptingVisual);
#if USE_EDITOR #if USE_EDITOR
if (isReloading) if (isReloading)
{ {
@@ -1588,6 +1594,7 @@ AssetChunksFlag VisualScript::getChunksToPreload() const
void VisualScript::CacheScriptingType() void VisualScript::CacheScriptingType()
{ {
PROFILE_MEM(ScriptingVisual);
ScopeLock lock(VisualScriptingBinaryModule::Locker); ScopeLock lock(VisualScriptingBinaryModule::Locker);
auto& binaryModule = VisualScriptingModule; auto& binaryModule = VisualScriptingModule;
@@ -1723,6 +1730,7 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri
VisualScript* visualScript = VisualScriptingModule.Scripts[params.Type.TypeIndex]; VisualScript* visualScript = VisualScriptingModule.Scripts[params.Type.TypeIndex];
// Initialize instance data // Initialize instance data
PROFILE_MEM(ScriptingVisual);
ScopeLock lock(visualScript->Locker); ScopeLock lock(visualScript->Locker);
auto& instanceParams = visualScript->_instances[object->GetID()].Params; auto& instanceParams = visualScript->_instances[object->GetID()].Params;
instanceParams.Resize(visualScript->Graph.Parameters.Count()); instanceParams.Resize(visualScript->Graph.Parameters.Count());
@@ -1747,6 +1755,8 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri
void VisualScriptingBinaryModule::OnScriptsReloading() void VisualScriptingBinaryModule::OnScriptsReloading()
{ {
PROFILE_MEM(ScriptingVisual);
// Clear any cached types from that module across all loaded Visual Scripts // Clear any cached types from that module across all loaded Visual Scripts
for (auto& script : Scripts) for (auto& script : Scripts)
{ {
@@ -1795,6 +1805,7 @@ void VisualScriptingBinaryModule::OnScriptsReloading()
void VisualScriptingBinaryModule::OnEvent(ScriptingObject* object, Span<Variant> parameters, ScriptingTypeHandle eventType, StringView eventName) void VisualScriptingBinaryModule::OnEvent(ScriptingObject* object, Span<Variant> parameters, ScriptingTypeHandle eventType, StringView eventName)
{ {
PROFILE_MEM(ScriptingVisual);
if (object) if (object)
{ {
// Object event // Object event
@@ -1900,9 +1911,13 @@ bool VisualScriptingBinaryModule::InvokeMethod(void* method, const Variant& inst
if (!instanceObject || instanceObject->GetTypeHandle() != vsMethod->Script->GetScriptingType()) if (!instanceObject || instanceObject->GetTypeHandle() != vsMethod->Script->GetScriptingType())
{ {
if (!instanceObject) if (!instanceObject)
{
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) without object instance", String(vsMethod->Script->GetScriptTypeName()), String(vsMethod->Name), vsMethod->ParamNames.Count()); LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) without object instance", String(vsMethod->Script->GetScriptTypeName()), String(vsMethod->Name), vsMethod->ParamNames.Count());
}
else else
{
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) with invalid object instance of type '{3}'", String(vsMethod->Script->GetScriptTypeName()), String(vsMethod->Name), vsMethod->ParamNames.Count(), String(instanceObject->GetType().Fullname)); LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) with invalid object instance of type '{3}'", String(vsMethod->Script->GetScriptTypeName()), String(vsMethod->Name), vsMethod->ParamNames.Count(), String(instanceObject->GetType().Fullname));
}
return true; return true;
} }
} }
@@ -1952,6 +1967,7 @@ bool VisualScriptingBinaryModule::GetFieldValue(void* field, const Variant& inst
bool VisualScriptingBinaryModule::SetFieldValue(void* field, const Variant& instance, Variant& value) bool VisualScriptingBinaryModule::SetFieldValue(void* field, const Variant& instance, Variant& value)
{ {
PROFILE_MEM(ScriptingVisual);
const auto vsFiled = (VisualScript::Field*)field; const auto vsFiled = (VisualScript::Field*)field;
const auto instanceObject = (ScriptingObject*)instance; const auto instanceObject = (ScriptingObject*)instance;
if (!instanceObject) if (!instanceObject)
@@ -2038,6 +2054,7 @@ void VisualScriptingBinaryModule::SerializeObject(JsonWriter& stream, ScriptingO
void VisualScriptingBinaryModule::DeserializeObject(ISerializable::DeserializeStream& stream, ScriptingObject* object, ISerializeModifier* modifier) void VisualScriptingBinaryModule::DeserializeObject(ISerializable::DeserializeStream& stream, ScriptingObject* object, ISerializeModifier* modifier)
{ {
PROFILE_MEM(ScriptingVisual);
ASSERT(stream.IsObject()); ASSERT(stream.IsObject());
Locker.Lock(); Locker.Lock();
const auto asset = Scripts[object->GetTypeHandle().TypeIndex].Get(); const auto asset = Scripts[object->GetTypeHandle().TypeIndex].Get();
@@ -2161,6 +2178,7 @@ const Variant& VisualScript::GetScriptInstanceParameterValue(const StringView& n
void VisualScript::SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, const Variant& value) void VisualScript::SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, const Variant& value)
{ {
PROFILE_MEM(ScriptingVisual);
CHECK(instance); CHECK(instance);
for (int32 paramIndex = 0; paramIndex < Graph.Parameters.Count(); paramIndex++) for (int32 paramIndex = 0; paramIndex < Graph.Parameters.Count(); paramIndex++)
{ {
@@ -2182,6 +2200,7 @@ void VisualScript::SetScriptInstanceParameterValue(const StringView& name, Scrip
void VisualScript::SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, Variant&& value) void VisualScript::SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, Variant&& value)
{ {
PROFILE_MEM(ScriptingVisual);
CHECK(instance); CHECK(instance);
for (int32 paramIndex = 0; paramIndex < Graph.Parameters.Count(); paramIndex++) for (int32 paramIndex = 0; paramIndex < Graph.Parameters.Count(); paramIndex++)
{ {
@@ -2379,6 +2398,7 @@ VisualScriptingBinaryModule* VisualScripting::GetBinaryModule()
Variant VisualScripting::Invoke(VisualScript::Method* method, ScriptingObject* instance, Span<Variant> parameters) Variant VisualScripting::Invoke(VisualScript::Method* method, ScriptingObject* instance, Span<Variant> parameters)
{ {
PROFILE_MEM(ScriptingVisual);
CHECK_RETURN(method && method->Script->IsLoaded(), Variant::Zero); CHECK_RETURN(method && method->Script->IsLoaded(), Variant::Zero);
PROFILE_CPU_SRC_LOC(method->ProfilerData); PROFILE_CPU_SRC_LOC(method->ProfilerData);
@@ -2419,6 +2439,7 @@ bool VisualScripting::Evaluate(VisualScript* script, ScriptingObject* instance,
const auto box = node->GetBox(boxId); const auto box = node->GetBox(boxId);
if (!box) if (!box)
return false; return false;
PROFILE_MEM(ScriptingVisual);
// Add to the calling stack // Add to the calling stack
ScopeContext scope; ScopeContext scope;
+4 -2
View File
@@ -10,6 +10,7 @@
#include "Engine/Serialization/JsonTools.h" #include "Engine/Serialization/JsonTools.h"
#include "Engine/Debug/Exceptions/JsonParseException.h" #include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Engine/Threading/ThreadPoolTask.h" #include "Engine/Threading/ThreadPoolTask.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if USE_EDITOR #if USE_EDITOR
#include "Engine/Platform/FileSystem.h" #include "Engine/Platform/FileSystem.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
@@ -463,10 +464,10 @@ void BinaryAsset::OnDeleteObject()
#endif #endif
const String& BinaryAsset::GetPath() const StringView BinaryAsset::GetPath() const
{ {
#if USE_EDITOR #if USE_EDITOR
return Storage ? Storage->GetPath() : String::Empty; return Storage ? StringView(Storage->GetPath()) : StringView::Empty;
#else #else
// In build all assets are packed into packages so use ID for original path lookup // In build all assets are packed into packages so use ID for original path lookup
return Content::GetRegistry()->GetEditorAssetPath(_id); return Content::GetRegistry()->GetEditorAssetPath(_id);
@@ -527,6 +528,7 @@ protected:
auto storage = ref->Storage; auto storage = ref->Storage;
auto factory = (BinaryAssetFactoryBase*)Content::GetAssetFactory(ref->GetTypeName()); auto factory = (BinaryAssetFactoryBase*)Content::GetAssetFactory(ref->GetTypeName());
ASSERT(factory); ASSERT(factory);
PROFILE_MEM(ContentAssets);
// Here we should open storage and extract AssetInitData // Here we should open storage and extract AssetInitData
// This would also allow to convert/upgrade data // This would also allow to convert/upgrade data
+1 -1
View File
@@ -292,7 +292,7 @@ public:
#if USE_EDITOR #if USE_EDITOR
void OnDeleteObject() override; void OnDeleteObject() override;
#endif #endif
const String& GetPath() const final override; StringView GetPath() const final override;
uint64 GetMemoryUsage() const override; uint64 GetMemoryUsage() const override;
protected: protected:
+47 -27
View File
@@ -10,15 +10,28 @@
#include "Engine/Serialization/FileWriteStream.h" #include "Engine/Serialization/FileWriteStream.h"
#include "Engine/Serialization/FileReadStream.h" #include "Engine/Serialization/FileReadStream.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Content/Storage/ContentStorageManager.h"
#include "Engine/Content/Storage/JsonStorageProxy.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Engine/Globals.h" #include "Engine/Engine/Globals.h"
#include "FlaxEngine.Gen.h" #include "FlaxEngine.Gen.h"
#if ASSETS_CACHE_EDITABLE
#include "Engine/Content/Storage/ContentStorageManager.h"
#include "Engine/Content/Storage/JsonStorageProxy.h"
#include "Engine/Threading/Threading.h"
#define ASSETS_CACHE_LOCK() ScopeLock lock(_locker)
#else
#define ASSETS_CACHE_LOCK()
#endif
int32 AssetsCache::Size() const
{
ASSETS_CACHE_LOCK();
const int32 result = _registry.Count();
return result;
}
void AssetsCache::Init() void AssetsCache::Init()
{ {
PROFILE_CPU();
Entry e; Entry e;
int32 count; int32 count;
Stopwatch stopwatch; Stopwatch stopwatch;
@@ -71,7 +84,7 @@ void AssetsCache::Init()
return; return;
} }
ScopeLock lock(_locker); ASSETS_CACHE_LOCK();
_isDirty = false; _isDirty = false;
// Load elements count // Load elements count
@@ -126,6 +139,16 @@ void AssetsCache::Init()
_pathsMapping.Add(mappedPath, id); _pathsMapping.Add(mappedPath, id);
} }
#if !USE_EDITOR && !BUILD_RELEASE
// Build inverse path mapping in development builds for faster GetEditorAssetPath (eg. used by PROFILE_CPU_ASSET)
_pathsMappingInv.Clear();
_pathsMappingInv.EnsureCapacity(count);
for (auto& mapping : _pathsMapping)
{
_pathsMappingInv.Add(mapping.Value, StringView(mapping.Key));
}
#endif
// Check errors // Check errors
const bool hasError = stream->HasError(); const bool hasError = stream->HasError();
deleteStream.Delete(); deleteStream.Delete();
@@ -153,7 +176,7 @@ bool AssetsCache::Save()
if (!_isDirty && FileSystem::FileExists(_path)) if (!_isDirty && FileSystem::FileExists(_path))
return false; return false;
ScopeLock lock(_locker); ASSETS_CACHE_LOCK();
if (Save(_path, _registry, _pathsMapping)) if (Save(_path, _registry, _pathsMapping))
return true; return true;
@@ -222,12 +245,16 @@ bool AssetsCache::Save(const StringView& path, const Registry& entries, const Pa
return false; return false;
} }
const String& AssetsCache::GetEditorAssetPath(const Guid& id) const StringView AssetsCache::GetEditorAssetPath(const Guid& id) const
{ {
ScopeLock lock(_locker); ASSETS_CACHE_LOCK();
#if USE_EDITOR #if USE_EDITOR
auto e = _registry.TryGet(id); auto e = _registry.TryGet(id);
return e ? e->Info.Path : String::Empty; return e ? e->Info.Path : String::Empty;
#elif !BUILD_RELEASE
StringView result;
_pathsMappingInv.TryGet(id, result);
return result;
#else #else
for (auto& e : _pathsMapping) for (auto& e : _pathsMapping)
{ {
@@ -241,10 +268,8 @@ const String& AssetsCache::GetEditorAssetPath(const Guid& id) const
bool AssetsCache::FindAsset(const StringView& path, AssetInfo& info) bool AssetsCache::FindAsset(const StringView& path, AssetInfo& info)
{ {
PROFILE_CPU(); PROFILE_CPU();
bool result = false; bool result = false;
ASSETS_CACHE_LOCK();
ScopeLock lock(_locker);
// Check if asset has direct mapping to id (used for some cooked assets) // Check if asset has direct mapping to id (used for some cooked assets)
Guid id; Guid id;
@@ -293,7 +318,7 @@ bool AssetsCache::FindAsset(const Guid& id, AssetInfo& info)
{ {
PROFILE_CPU(); PROFILE_CPU();
bool result = false; bool result = false;
ScopeLock lock(_locker); ASSETS_CACHE_LOCK();
auto e = _registry.TryGet(id); auto e = _registry.TryGet(id);
if (e != nullptr) if (e != nullptr)
{ {
@@ -315,14 +340,14 @@ bool AssetsCache::FindAsset(const Guid& id, AssetInfo& info)
void AssetsCache::GetAll(Array<Guid>& result) const void AssetsCache::GetAll(Array<Guid>& result) const
{ {
PROFILE_CPU(); PROFILE_CPU();
ScopeLock lock(_locker); ASSETS_CACHE_LOCK();
_registry.GetKeys(result); _registry.GetKeys(result);
} }
void AssetsCache::GetAllByTypeName(const StringView& typeName, Array<Guid>& result) const void AssetsCache::GetAllByTypeName(const StringView& typeName, Array<Guid>& result) const
{ {
PROFILE_CPU(); PROFILE_CPU();
ScopeLock lock(_locker); ASSETS_CACHE_LOCK();
for (auto i = _registry.Begin(); i.IsNotEnd(); ++i) for (auto i = _registry.Begin(); i.IsNotEnd(); ++i)
{ {
if (i->Value.Info.TypeName == typeName) if (i->Value.Info.TypeName == typeName)
@@ -330,6 +355,8 @@ void AssetsCache::GetAllByTypeName(const StringView& typeName, Array<Guid>& resu
} }
} }
#if ASSETS_CACHE_EDITABLE
void AssetsCache::RegisterAssets(FlaxStorage* storage) void AssetsCache::RegisterAssets(FlaxStorage* storage)
{ {
PROFILE_CPU(); PROFILE_CPU();
@@ -341,7 +368,7 @@ void AssetsCache::RegisterAssets(FlaxStorage* storage)
storage->GetEntries(entries); storage->GetEntries(entries);
ASSERT(entries.HasItems()); ASSERT(entries.HasItems());
ScopeLock lock(_locker); ASSETS_CACHE_LOCK();
auto storagePath = storage->GetPath(); auto storagePath = storage->GetPath();
// Remove all old entries from that location // Remove all old entries from that location
@@ -439,7 +466,7 @@ void AssetsCache::RegisterAssets(const FlaxStorageReference& storage)
void AssetsCache::RegisterAsset(const Guid& id, const String& typeName, const StringView& path) void AssetsCache::RegisterAsset(const Guid& id, const String& typeName, const StringView& path)
{ {
PROFILE_CPU(); PROFILE_CPU();
ScopeLock lock(_locker); ASSETS_CACHE_LOCK();
// Check if asset has been already added to the registry // Check if asset has been already added to the registry
bool isMissing = true; bool isMissing = true;
@@ -491,8 +518,7 @@ void AssetsCache::RegisterAsset(const Guid& id, const String& typeName, const St
bool AssetsCache::DeleteAsset(const StringView& path, AssetInfo* info) bool AssetsCache::DeleteAsset(const StringView& path, AssetInfo* info)
{ {
bool result = false; bool result = false;
_locker.Lock(); ASSETS_CACHE_LOCK();
for (auto i = _registry.Begin(); i.IsNotEnd(); ++i) for (auto i = _registry.Begin(); i.IsNotEnd(); ++i)
{ {
if (i->Value.Info.Path == path) if (i->Value.Info.Path == path)
@@ -505,16 +531,13 @@ bool AssetsCache::DeleteAsset(const StringView& path, AssetInfo* info)
break; break;
} }
} }
_locker.Unlock();
return result; return result;
} }
bool AssetsCache::DeleteAsset(const Guid& id, AssetInfo* info) bool AssetsCache::DeleteAsset(const Guid& id, AssetInfo* info)
{ {
bool result = false; bool result = false;
_locker.Lock(); ASSETS_CACHE_LOCK();
const auto e = _registry.TryGet(id); const auto e = _registry.TryGet(id);
if (e != nullptr) if (e != nullptr)
{ {
@@ -524,16 +547,13 @@ bool AssetsCache::DeleteAsset(const Guid& id, AssetInfo* info)
_isDirty = true; _isDirty = true;
result = true; result = true;
} }
_locker.Unlock();
return result; return result;
} }
bool AssetsCache::RenameAsset(const StringView& oldPath, const StringView& newPath) bool AssetsCache::RenameAsset(const StringView& oldPath, const StringView& newPath)
{ {
bool result = false; bool result = false;
_locker.Lock(); ASSETS_CACHE_LOCK();
for (auto i = _registry.Begin(); i.IsNotEnd(); ++i) for (auto i = _registry.Begin(); i.IsNotEnd(); ++i)
{ {
if (i->Value.Info.Path == oldPath) if (i->Value.Info.Path == oldPath)
@@ -544,11 +564,11 @@ bool AssetsCache::RenameAsset(const StringView& oldPath, const StringView& newPa
break; break;
} }
} }
_locker.Unlock();
return result; return result;
} }
#endif
bool AssetsCache::IsEntryValid(Entry& e) bool AssetsCache::IsEntryValid(Entry& e)
{ {
#if ENABLE_ASSETS_DISCOVERY #if ENABLE_ASSETS_DISCOVERY
+13 -9
View File
@@ -16,6 +16,9 @@ struct AssetHeader;
struct FlaxStorageReference; struct FlaxStorageReference;
class FlaxStorage; class FlaxStorage;
// In cooked game all assets are there and all access to registry is read-only so can be multithreaded
#define ASSETS_CACHE_EDITABLE (USE_EDITOR)
/// <summary> /// <summary>
/// Assets cache flags. /// Assets cache flags.
/// </summary> /// </summary>
@@ -75,22 +78,21 @@ public:
private: private:
bool _isDirty = false; bool _isDirty = false;
#if ASSETS_CACHE_EDITABLE
CriticalSection _locker; CriticalSection _locker;
#endif
Registry _registry; Registry _registry;
PathsMapping _pathsMapping; PathsMapping _pathsMapping;
#if !USE_EDITOR && !BUILD_RELEASE
Dictionary<Guid, StringView> _pathsMappingInv;
#endif
String _path; String _path;
public: public:
/// <summary> /// <summary>
/// Gets amount of registered assets. /// Gets amount of registered assets.
/// </summary> /// </summary>
int32 Size() const int32 Size() const;
{
_locker.Lock();
const int32 result = _registry.Count();
_locker.Unlock();
return result;
}
public: public:
/// <summary> /// <summary>
@@ -116,11 +118,11 @@ public:
public: public:
/// <summary> /// <summary>
/// Finds the asset path by id. In editor it returns the actual asset path, at runtime it returns the mapped asset path. /// Finds the asset path by id. In editor, it returns the actual asset path, at runtime it returns the mapped asset path.
/// </summary> /// </summary>
/// <param name="id">The asset id.</param> /// <param name="id">The asset id.</param>
/// <returns>The asset path, or empty if failed to find.</returns> /// <returns>The asset path, or empty if failed to find.</returns>
const String& GetEditorAssetPath(const Guid& id) const; StringView GetEditorAssetPath(const Guid& id) const;
/// <summary> /// <summary>
/// Finds the asset info by path. /// Finds the asset info by path.
@@ -173,6 +175,7 @@ public:
/// <param name="result">The result array.</param> /// <param name="result">The result array.</param>
void GetAllByTypeName(const StringView& typeName, Array<Guid, HeapAllocation>& result) const; void GetAllByTypeName(const StringView& typeName, Array<Guid, HeapAllocation>& result) const;
#if ASSETS_CACHE_EDITABLE
/// <summary> /// <summary>
/// Register assets in the cache /// Register assets in the cache
/// </summary> /// </summary>
@@ -223,6 +226,7 @@ public:
/// <param name="newPath">New path</param> /// <param name="newPath">New path</param>
/// <returns>True if has been deleted, otherwise false</returns> /// <returns>True if has been deleted, otherwise false</returns>
bool RenameAsset(const StringView& oldPath, const StringView& newPath); bool RenameAsset(const StringView& oldPath, const StringView& newPath);
#endif
/// <summary> /// <summary>
/// Determines whether cached asset entry is valid. /// Determines whether cached asset entry is valid.
+6 -1
View File
@@ -4,8 +4,13 @@
#include "Engine/Core/Config.h" #include "Engine/Core/Config.h"
// Amount of content loading threads per single physical CPU core // Amount of content loading threads per single logical CPU core
#ifndef LOADING_THREAD_PER_LOGICAL_CORE
#define LOADING_THREAD_PER_LOGICAL_CORE 0.5f #define LOADING_THREAD_PER_LOGICAL_CORE 0.5f
#endif
// Enables pinning loading threads to the logical CPU cores with affinity mask
//#define LOADING_THREAD_AFFINITY_MASK(thread) (1 << (thread + 1))
// Enables additional assets metadata verification // Enables additional assets metadata verification
#define ASSETS_LOADING_EXTRA_VERIFICATION (BUILD_DEBUG || USE_EDITOR) #define ASSETS_LOADING_EXTRA_VERIFICATION (BUILD_DEBUG || USE_EDITOR)
+56 -5
View File
@@ -28,6 +28,7 @@
#include "Engine/Engine/Globals.h" #include "Engine/Engine/Globals.h"
#include "Engine/Level/Types.h" #include "Engine/Level/Types.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/ManagedCLR/MClass.h" #include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#if USE_EDITOR #if USE_EDITOR
@@ -117,6 +118,8 @@ ContentService ContentServiceInstance;
bool ContentService::Init() bool ContentService::Init()
{ {
PROFILE_MEM(Content);
// Load assets registry // Load assets registry
Cache.Init(); Cache.Init();
@@ -126,17 +129,17 @@ bool ContentService::Init()
LOG(Info, "Creating {0} content loading threads...", count); LOG(Info, "Creating {0} content loading threads...", count);
MainLoadThread = New<LoadingThread>(); MainLoadThread = New<LoadingThread>();
ThisLoadThread = MainLoadThread; ThisLoadThread = MainLoadThread;
LoadThreads.EnsureCapacity(count); LoadThreads.Resize(count);
for (int32 i = 0; i < count; i++) for (int32 i = 0; i < count; i++)
{ {
auto thread = New<LoadingThread>(); auto thread = New<LoadingThread>();
LoadThreads[i] = thread;
if (thread->Start(String::Format(TEXT("Load Thread {0}"), i))) if (thread->Start(String::Format(TEXT("Load Thread {0}"), i)))
{ {
LOG(Fatal, "Cannot spawn content thread {0}/{1}", i, count); LOG(Fatal, "Cannot spawn content thread {0}/{1}", i, count);
Delete(thread); Delete(thread);
return true; return true;
} }
LoadThreads.Add(thread);
} }
return false; return false;
@@ -159,6 +162,7 @@ void ContentService::Update()
void ContentService::LateUpdate() void ContentService::LateUpdate()
{ {
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Content);
// Check if need to perform an update of unloading assets // Check if need to perform an update of unloading assets
const TimeSpan timeNow = Time::Update.UnscaledTime; const TimeSpan timeNow = Time::Update.UnscaledTime;
@@ -324,6 +328,7 @@ String LoadingThread::ToString() const
int32 LoadingThread::Run() int32 LoadingThread::Run()
{ {
PROFILE_MEM(Content);
#if USE_EDITOR && PLATFORM_WINDOWS #if USE_EDITOR && PLATFORM_WINDOWS
// Initialize COM // Initialize COM
// TODO: maybe add sth to Thread::Create to indicate that thread will use COM stuff // TODO: maybe add sth to Thread::Create to indicate that thread will use COM stuff
@@ -334,6 +339,9 @@ int32 LoadingThread::Run()
return -1; return -1;
} }
#endif #endif
#ifdef LOADING_THREAD_AFFINITY_MASK
Platform::SetThreadAffinityMask(LOADING_THREAD_AFFINITY_MASK(LoadThreads.Find(this)));
#endif
ContentLoadTask* task; ContentLoadTask* task;
ThisLoadThread = this; ThisLoadThread = this;
@@ -416,6 +424,7 @@ bool Content::GetAssetInfo(const Guid& id, AssetInfo& info)
if (Cache.FindAsset(id, info)) if (Cache.FindAsset(id, info))
return true; return true;
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Content);
// Locking injects some stalls but we need to make it safe (only one thread can pass though it at once) // Locking injects some stalls but we need to make it safe (only one thread can pass though it at once)
ScopeLock lock(WorkspaceDiscoveryLocker); ScopeLock lock(WorkspaceDiscoveryLocker);
@@ -465,6 +474,7 @@ bool Content::GetAssetInfo(const StringView& path, AssetInfo& info)
if (!FileSystem::FileExists(path)) if (!FileSystem::FileExists(path))
return false; return false;
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Content);
const auto extension = FileSystem::GetExtension(path).ToLower(); const auto extension = FileSystem::GetExtension(path).ToLower();
@@ -511,7 +521,7 @@ bool Content::GetAssetInfo(const StringView& path, AssetInfo& info)
#endif #endif
} }
String Content::GetEditorAssetPath(const Guid& id) StringView Content::GetEditorAssetPath(const Guid& id)
{ {
return Cache.GetEditorAssetPath(id); return Cache.GetEditorAssetPath(id);
} }
@@ -593,6 +603,7 @@ Asset* Content::LoadAsyncInternal(const StringView& internalPath, const MClass*
Asset* Content::LoadAsyncInternal(const StringView& internalPath, const ScriptingTypeHandle& type) Asset* Content::LoadAsyncInternal(const StringView& internalPath, const ScriptingTypeHandle& type)
{ {
PROFILE_MEM(Content);
#if USE_EDITOR #if USE_EDITOR
const String path = Globals::EngineContentFolder / internalPath + ASSET_FILES_EXTENSION_WITH_DOT; const String path = Globals::EngineContentFolder / internalPath + ASSET_FILES_EXTENSION_WITH_DOT;
if (!FileSystem::FileExists(path)) if (!FileSystem::FileExists(path))
@@ -635,6 +646,8 @@ Asset* Content::LoadAsync(const StringView& path, const MClass* type)
Asset* Content::LoadAsync(const StringView& path, const ScriptingTypeHandle& type) Asset* Content::LoadAsync(const StringView& path, const ScriptingTypeHandle& type)
{ {
PROFILE_MEM(Content);
// Ensure path is in a valid format // Ensure path is in a valid format
String pathNorm(path); String pathNorm(path);
ContentStorageManager::FormatPath(pathNorm); ContentStorageManager::FormatPath(pathNorm);
@@ -687,7 +700,6 @@ Asset* Content::GetAsset(const StringView& outputPath)
{ {
if (outputPath.IsEmpty()) if (outputPath.IsEmpty())
return nullptr; return nullptr;
ScopeLock lock(AssetsLocker); ScopeLock lock(AssetsLocker);
for (auto i = Assets.Begin(); i.IsNotEnd(); ++i) for (auto i = Assets.Begin(); i.IsNotEnd(); ++i)
{ {
@@ -737,6 +749,7 @@ void Content::DeleteAsset(const StringView& path)
return; return;
} }
#if USE_EDITOR
ScopeLock locker(AssetsLocker); ScopeLock locker(AssetsLocker);
// Remove from registry // Remove from registry
@@ -753,6 +766,7 @@ void Content::DeleteAsset(const StringView& path)
// Delete file // Delete file
deleteFileSafety(path, info.ID); deleteFileSafety(path, info.ID);
#endif
} }
void Content::deleteFileSafety(const StringView& path, const Guid& id) void Content::deleteFileSafety(const StringView& path, const Guid& id)
@@ -791,6 +805,23 @@ void Content::deleteFileSafety(const StringView& path, const Guid& id)
#endif #endif
} }
#if !COMPILE_WITHOUT_CSHARP
#include "Engine/Scripting/ManagedCLR/MUtils.h"
void* Content::GetAssetsInternal()
{
AssetsLocker.Lock();
MArray* result = MCore::Array::New(Asset::TypeInitializer.GetClass(), Assets.Count());
int32 i = 0;
for (const auto& e : Assets)
MCore::GC::WriteArrayRef(result, e.Value->GetOrCreateManagedInstance(), i++);
AssetsLocker.Unlock();
return result;
}
#endif
#if USE_EDITOR #if USE_EDITOR
bool Content::RenameAsset(const StringView& oldPath, const StringView& newPath) bool Content::RenameAsset(const StringView& oldPath, const StringView& newPath)
@@ -977,7 +1008,7 @@ bool Content::CloneAssetFile(const StringView& dstPath, const StringView& srcPat
FileSystem::DeleteFile(tmpPath); FileSystem::DeleteFile(tmpPath);
// Reload storage // Reload storage
if (auto storage = ContentStorageManager::GetStorage(dstPath)) if (auto storage = ContentStorageManager::GetStorage(dstPath, false))
{ {
storage->Reload(); storage->Reload();
} }
@@ -1023,6 +1054,7 @@ Asset* Content::CreateVirtualAsset(const MClass* type)
Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type) Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type)
{ {
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Content);
auto& assetType = type.GetType(); auto& assetType = type.GetType();
// Init mock asset info // Init mock asset info
@@ -1045,7 +1077,9 @@ Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type)
} }
// Create asset object // Create asset object
PROFILE_MEM_BEGIN(ContentAssets);
auto asset = factory->NewVirtual(info); auto asset = factory->NewVirtual(info);
PROFILE_MEM_END();
if (asset == nullptr) if (asset == nullptr)
{ {
LOG(Error, "Cannot create virtual asset object."); LOG(Error, "Cannot create virtual asset object.");
@@ -1054,7 +1088,9 @@ Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type)
asset->RegisterObject(); asset->RegisterObject();
// Call initializer function // Call initializer function
PROFILE_MEM_BEGIN(ContentAssets);
asset->InitAsVirtual(); asset->InitAsVirtual();
PROFILE_MEM_END();
// Register asset // Register asset
AssetsLocker.Lock(); AssetsLocker.Lock();
@@ -1076,11 +1112,21 @@ void Content::WaitForTask(ContentLoadTask* loadingTask, double timeoutInMillisec
const double timeoutInSeconds = timeoutInMilliseconds * 0.001; const double timeoutInSeconds = timeoutInMilliseconds * 0.001;
const double startTime = Platform::GetTimeSeconds(); const double startTime = Platform::GetTimeSeconds();
int32 loopCounter = 0;
Task* task = loadingTask; Task* task = loadingTask;
Array<ContentLoadTask*, InlinedAllocation<64>> localQueue; Array<ContentLoadTask*, InlinedAllocation<64>> localQueue;
#define CHECK_CONDITIONS() (!Engine::ShouldExit() && (timeoutInSeconds <= 0.0 || Platform::GetTimeSeconds() - startTime < timeoutInSeconds)) #define CHECK_CONDITIONS() (!Engine::ShouldExit() && (timeoutInSeconds <= 0.0 || Platform::GetTimeSeconds() - startTime < timeoutInSeconds))
do do
{ {
// Give opportunity for other threads to use the current core
if (loopCounter == 0)
; // First run is fast
else if (loopCounter < 10)
Platform::Yield();
else
Platform::Sleep(1);
loopCounter++;
// Try to execute content tasks // Try to execute content tasks
while (task->IsQueued() && CHECK_CONDITIONS()) while (task->IsQueued() && CHECK_CONDITIONS())
{ {
@@ -1097,6 +1143,8 @@ void Content::WaitForTask(ContentLoadTask* loadingTask, double timeoutInMillisec
localQueue.Clear(); localQueue.Clear();
} }
PROFILE_CPU_NAMED("Inline");
ZoneColor(0xffaaaaaa);
thread->Run(tmp); thread->Run(tmp);
} }
else else
@@ -1209,6 +1257,7 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
{ {
if (!id.IsValid()) if (!id.IsValid())
return nullptr; return nullptr;
PROFILE_MEM(Content);
// Check if asset has been already loaded // Check if asset has been already loaded
Asset* result = nullptr; Asset* result = nullptr;
@@ -1277,7 +1326,9 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
} }
// Create asset object // Create asset object
PROFILE_MEM_BEGIN(ContentAssets);
result = factory->New(assetInfo); result = factory->New(assetInfo);
PROFILE_MEM_END();
if (result == nullptr) if (result == nullptr)
{ {
LOG(Error, "Cannot create asset object. Info: {0}", assetInfo.ToString()); LOG(Error, "Cannot create asset object. Info: {0}", assetInfo.ToString());
+28
View File
@@ -1,5 +1,6 @@
// Copyright (c) Wojciech Figat. All rights reserved. // Copyright (c) Wojciech Figat. All rights reserved.
using FlaxEngine.Interop;
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -7,6 +8,33 @@ namespace FlaxEngine
{ {
partial class Content partial class Content
{ {
/// <summary>
/// Gets the assets (loaded or during load).
/// </summary>
public static Asset[] Assets
{
get
{
IntPtr ptr = Internal_GetAssetsInternal();
ManagedArray array = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(ptr).Target);
return NativeInterop.GCHandleArrayToManagedArray<Asset>(array);
}
}
/// <summary>
/// Gets the assets (loaded or during load).
/// </summary>
/// <param name="buffer">Output buffer to fill with asset pointers. Can be provided by a user to avoid memory allocation. Buffer might be larger than actual list size. Use <paramref name="count"/> for actual item count.></param>
/// <param name="count">Amount of valid items inside <paramref name="buffer"/>.</param>
public static void GetAssets(ref Asset[] buffer, out int count)
{
count = 0;
IntPtr ptr = Internal_GetAssetsInternal();
ManagedArray array = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(ptr).Target);
buffer = NativeInterop.GCHandleArrayToManagedArray<Asset>(array, buffer);
count = buffer.Length;
}
/// <summary> /// <summary>
/// Loads asset to the Content Pool and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async. /// Loads asset to the Content Pool and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async.
/// </summary> /// </summary>
+7 -2
View File
@@ -75,7 +75,7 @@ public:
/// </summary> /// </summary>
/// <param name="id">The asset id.</param> /// <param name="id">The asset id.</param>
/// <returns>The asset path, or empty if failed to find.</returns> /// <returns>The asset path, or empty if failed to find.</returns>
API_FUNCTION() static String GetEditorAssetPath(const Guid& id); API_FUNCTION() static StringView GetEditorAssetPath(const Guid& id);
/// <summary> /// <summary>
/// Finds all the asset IDs. Uses asset registry. /// Finds all the asset IDs. Uses asset registry.
@@ -122,7 +122,7 @@ public:
/// Gets the assets (loaded or during load). /// Gets the assets (loaded or during load).
/// </summary> /// </summary>
/// <returns>The collection of assets.</returns> /// <returns>The collection of assets.</returns>
API_PROPERTY() static Array<Asset*, HeapAllocation> GetAssets(); static Array<Asset*, HeapAllocation> GetAssets();
/// <summary> /// <summary>
/// Gets the raw dictionary of assets (loaded or during load). /// Gets the raw dictionary of assets (loaded or during load).
@@ -368,4 +368,9 @@ private:
static void onAssetUnload(Asset* asset); static void onAssetUnload(Asset* asset);
static void onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId); static void onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId);
static void deleteFileSafety(const StringView& path, const Guid& id); static void deleteFileSafety(const StringView& path, const Guid& id);
// Internal bindings
#if !COMPILE_WITHOUT_CSHARP
API_FUNCTION(NoProxy) static void* GetAssetsInternal();
#endif
}; };
+9 -2
View File
@@ -20,6 +20,7 @@
#include "Engine/Core/Cache.h" #include "Engine/Core/Cache.h"
#include "Engine/Debug/Exceptions/JsonParseException.h" #include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/ManagedCLR/MClass.h" #include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/ManagedCLR/MField.h" #include "Engine/Scripting/ManagedCLR/MField.h"
@@ -39,6 +40,7 @@ String JsonAssetBase::GetData() const
if (Data == nullptr) if (Data == nullptr)
return String::Empty; return String::Empty;
PROFILE_CPU_NAMED("JsonAsset.GetData"); PROFILE_CPU_NAMED("JsonAsset.GetData");
PROFILE_MEM(ContentAssets);
rapidjson_flax::StringBuffer buffer; rapidjson_flax::StringBuffer buffer;
OnGetData(buffer); OnGetData(buffer);
return String((const char*)buffer.GetString(), (int32)buffer.GetSize()); return String((const char*)buffer.GetString(), (int32)buffer.GetSize());
@@ -49,6 +51,7 @@ void JsonAssetBase::SetData(const StringView& value)
if (!IsLoaded()) if (!IsLoaded())
return; return;
PROFILE_CPU_NAMED("JsonAsset.SetData"); PROFILE_CPU_NAMED("JsonAsset.SetData");
PROFILE_MEM(ContentAssets);
const StringAnsi dataJson(value); const StringAnsi dataJson(value);
ScopeLock lock(Locker); ScopeLock lock(Locker);
const StringView dataTypeName = DataTypeName; const StringView dataTypeName = DataTypeName;
@@ -60,6 +63,7 @@ void JsonAssetBase::SetData(const StringView& value)
bool JsonAssetBase::Init(const StringView& dataTypeName, const StringAnsiView& dataJson) bool JsonAssetBase::Init(const StringView& dataTypeName, const StringAnsiView& dataJson)
{ {
PROFILE_MEM(ContentAssets);
unload(true); unload(true);
DataTypeName = dataTypeName; DataTypeName = dataTypeName;
DataEngineBuild = FLAXENGINE_VERSION_BUILD; DataEngineBuild = FLAXENGINE_VERSION_BUILD;
@@ -87,7 +91,7 @@ void JsonAssetBase::OnGetData(rapidjson_flax::StringBuffer& buffer) const
Data->Accept(writerObj.GetWriter()); Data->Accept(writerObj.GetWriter());
} }
const String& JsonAssetBase::GetPath() const StringView JsonAssetBase::GetPath() const
{ {
#if USE_EDITOR #if USE_EDITOR
return _path; return _path;
@@ -176,7 +180,7 @@ bool JsonAssetBase::Save(const StringView& path)
_isResaving = false; _isResaving = false;
// Save json to file // Save json to file
if (File::WriteAllBytes(path.HasChars() ? path : StringView(GetPath()), (byte*)buffer.GetString(), (int32)buffer.GetSize())) if (File::WriteAllBytes(path.HasChars() ? path : GetPath(), (byte*)buffer.GetString(), (int32)buffer.GetSize()))
{ {
LOG(Error, "Cannot save \'{0}\'", ToString()); LOG(Error, "Cannot save \'{0}\'", ToString());
return true; return true;
@@ -239,6 +243,7 @@ Asset::LoadResult JsonAssetBase::loadAsset()
{ {
if (IsVirtual() || _isVirtualDocument) if (IsVirtual() || _isVirtualDocument)
return LoadResult::Ok; return LoadResult::Ok;
PROFILE_MEM(ContentAssets);
// Load data (raw json file in editor, cooked asset in build game) // Load data (raw json file in editor, cooked asset in build game)
#if USE_EDITOR #if USE_EDITOR
@@ -305,6 +310,7 @@ Asset::LoadResult JsonAssetBase::loadAsset()
void JsonAssetBase::unload(bool isReloading) void JsonAssetBase::unload(bool isReloading)
{ {
PROFILE_MEM(ContentAssets);
ISerializable::SerializeDocument tmp; ISerializable::SerializeDocument tmp;
Document.Swap(tmp); Document.Swap(tmp);
Data = nullptr; Data = nullptr;
@@ -453,6 +459,7 @@ bool JsonAsset::CreateInstance()
ScopeLock lock(Locker); ScopeLock lock(Locker);
if (Instance) if (Instance)
return false; return false;
PROFILE_MEM(ContentAssets);
// Try to scripting type for this data // Try to scripting type for this data
const StringAsANSI<> dataTypeNameAnsi(DataTypeName.Get(), DataTypeName.Length()); const StringAsANSI<> dataTypeNameAnsi(DataTypeName.Get(), DataTypeName.Length());
+1 -1
View File
@@ -88,7 +88,7 @@ protected:
public: public:
// [Asset] // [Asset]
const String& GetPath() const override; StringView GetPath() const override;
uint64 GetMemoryUsage() const override; uint64 GetMemoryUsage() const override;
#if USE_EDITOR #if USE_EDITOR
void GetReferences(Array<Guid, HeapAllocation>& assets, Array<String, HeapAllocation>& files) const override; void GetReferences(Array<Guid, HeapAllocation>& assets, Array<String, HeapAllocation>& files) const override;
@@ -19,6 +19,15 @@ API_STRUCT(NoDefault, Template, MarshalAs=JsonAsset*) struct JsonAssetReference
OnSet(asset); OnSet(asset);
} }
explicit JsonAssetReference(decltype(__nullptr))
{
}
explicit JsonAssetReference(IAssetReference* owner)
: AssetReference<JsonAsset>(owner)
{
}
/// <summary> /// <summary>
/// Gets the deserialized native object instance of the given type. Returns null if asset is not loaded or loaded object has different type. /// Gets the deserialized native object instance of the given type. Returns null if asset is not loaded or loaded object has different type.
/// </summary> /// </summary>
@@ -36,7 +36,7 @@ public:
// [ContentLoadTask] // [ContentLoadTask]
String ToString() const override String ToString() const override
{ {
return String::Format(TEXT("Load Asset Data Task ({}, {}, {})"), (int32)GetState(), _chunks, _asset ? _asset->GetPath() : String::Empty); return String::Format(TEXT("Load Asset Data Task ({}, {}, {})"), (int32)GetState(), _chunks, _asset ? _asset->GetPath() : StringView::Empty);
} }
bool HasReference(Object* obj) const override bool HasReference(Object* obj) const override
{ {
@@ -8,6 +8,7 @@
#include "Engine/Content/WeakAssetReference.h" #include "Engine/Content/WeakAssetReference.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
/// <summary> /// <summary>
/// Asset loading task object. /// Asset loading task object.
@@ -44,6 +45,7 @@ protected:
Result run() override Result run() override
{ {
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(ContentAssets);
// Keep valid ref to the asset // Keep valid ref to the asset
AssetReference<::Asset> ref = Asset.Get(); AssetReference<::Asset> ref = Asset.Get();
+14 -2
View File
@@ -7,7 +7,7 @@
/// <summary> /// <summary>
/// The asset soft reference. Asset gets referenced (loaded) on actual use (ID reference is resolving it). /// The asset soft reference. Asset gets referenced (loaded) on actual use (ID reference is resolving it).
/// </summary> /// </summary>
class FLAXENGINE_API SoftAssetReferenceBase class FLAXENGINE_API SoftAssetReferenceBase : public IAssetReference
{ {
protected: protected:
Asset* _asset = nullptr; Asset* _asset = nullptr;
@@ -46,11 +46,16 @@ public:
/// </summary> /// </summary>
String ToString() const; String ToString() const;
public:
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
protected: protected:
void OnSet(Asset* asset); void OnSet(Asset* asset);
void OnSet(const Guid& id); void OnSet(const Guid& id);
void OnResolve(const ScriptingTypeHandle& type); void OnResolve(const ScriptingTypeHandle& type);
void OnUnloaded(Asset* asset);
}; };
/// <summary> /// <summary>
@@ -71,6 +76,13 @@ public:
{ {
} }
/// <summary>
/// Initializes a new instance of the <see cref="SoftAssetReference"/> class.
/// </summary>
explicit SoftAssetReference(decltype(__nullptr))
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SoftAssetReference"/> class. /// Initializes a new instance of the <see cref="SoftAssetReference"/> class.
/// </summary> /// </summary>
@@ -15,14 +15,9 @@
namespace namespace
{ {
CriticalSection Locker; CriticalSection Locker;
#if USE_EDITOR
Array<FlaxFile*> Files(1024);
Array<FlaxPackage*> Packages;
#else
Array<FlaxFile*> Files; Array<FlaxFile*> Files;
Array<FlaxPackage*> Packages(64); Array<FlaxPackage*> Packages;
#endif Dictionary<String, FlaxStorage*> StorageMap;
Dictionary<String, FlaxStorage*> StorageMap(2048);
} }
class ContentStorageService : public EngineService class ContentStorageService : public EngineService
@@ -231,6 +226,12 @@ void ContentStorageManager::GetStorage(Array<FlaxStorage*>& result)
bool ContentStorageService::Init() bool ContentStorageService::Init()
{ {
#if USE_EDITOR
Files.EnsureCapacity(1024);
#else
Packages.EnsureCapacity(64);
#endif
StorageMap.EnsureCapacity(2048);
System = New<ContentStorageSystem>(); System = New<ContentStorageSystem>();
Engine::UpdateGraph->AddSystem(System); Engine::UpdateGraph->AddSystem(System);
return false; return false;
+1 -6
View File
@@ -182,10 +182,5 @@ public:
/// Clones this chunk data (doesn't copy location in file). /// Clones this chunk data (doesn't copy location in file).
/// </summary> /// </summary>
/// <returns>The cloned chunk.</returns> /// <returns>The cloned chunk.</returns>
FlaxChunk* Clone() const FlaxChunk* Clone() const;
{
auto chunk = New<FlaxChunk>();
chunk->Data.Copy(Data);
return chunk;
}
}; };
+16 -8
View File
@@ -8,6 +8,7 @@
#include "Engine/Core/Types/TimeSpan.h" #include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Platform/File.h" #include "Engine/Platform/File.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Serialization/FileWriteStream.h" #include "Engine/Serialization/FileWriteStream.h"
#include "Engine/Content/Asset.h" #include "Engine/Content/Asset.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
@@ -63,6 +64,14 @@ void FlaxChunk::RegisterUsage()
LastAccessTime = Platform::GetTimeSeconds(); LastAccessTime = Platform::GetTimeSeconds();
} }
FlaxChunk* FlaxChunk::Clone() const
{
PROFILE_MEM(ContentFiles);
auto chunk = New<FlaxChunk>();
chunk->Data.Copy(Data);
return chunk;
}
const int32 FlaxStorage::MagicCode = 1180124739; const int32 FlaxStorage::MagicCode = 1180124739;
FlaxStorage::LockData FlaxStorage::LockData::Invalid(nullptr); FlaxStorage::LockData FlaxStorage::LockData::Invalid(nullptr);
@@ -281,19 +290,12 @@ uint32 FlaxStorage::GetMemoryUsage() const
bool FlaxStorage::Load() bool FlaxStorage::Load()
{ {
// Check if was already loaded
if (IsLoaded()) if (IsLoaded())
{
return false; return false;
} PROFILE_MEM(ContentFiles);
// Prevent loading by more than one thread
ScopeLock lock(_loadLocker); ScopeLock lock(_loadLocker);
if (IsLoaded()) if (IsLoaded())
{
// Other thread loaded it
return false; return false;
}
ASSERT(GetEntriesCount() == 0); ASSERT(GetEntriesCount() == 0);
// Open file // Open file
@@ -693,6 +695,7 @@ bool FlaxStorage::LoadAssetHeader(const Guid& id, AssetInitData& data)
bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk) bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk)
{ {
PROFILE_MEM(ContentFiles);
ASSERT(IsLoaded()); ASSERT(IsLoaded());
ASSERT(chunk != nullptr && _chunks.Contains(chunk)); ASSERT(chunk != nullptr && _chunks.Contains(chunk));
@@ -866,6 +869,7 @@ FlaxChunk* FlaxStorage::AllocateChunk()
{ {
if (AllowDataModifications()) if (AllowDataModifications())
{ {
PROFILE_MEM(ContentFiles);
auto chunk = New<FlaxChunk>(); auto chunk = New<FlaxChunk>();
_chunks.Add(chunk); _chunks.Add(chunk);
return chunk; return chunk;
@@ -1125,6 +1129,7 @@ bool FlaxStorage::Save(const AssetInitData& data, bool silentMode)
bool FlaxStorage::LoadAssetHeader(const Entry& e, AssetInitData& data) bool FlaxStorage::LoadAssetHeader(const Entry& e, AssetInitData& data)
{ {
PROFILE_MEM(ContentFiles);
ASSERT(IsLoaded()); ASSERT(IsLoaded());
auto lock = Lock(); auto lock = Lock();
@@ -1396,6 +1401,8 @@ FileReadStream* FlaxStorage::OpenFile()
auto& stream = _file.Get(); auto& stream = _file.Get();
if (stream == nullptr) if (stream == nullptr)
{ {
PROFILE_MEM(ContentFiles);
// Open file // Open file
auto file = File::Open(_path, FileMode::OpenExisting, FileAccess::Read, FileShare::Read); auto file = File::Open(_path, FileMode::OpenExisting, FileAccess::Read, FileShare::Read);
if (file == nullptr) if (file == nullptr)
@@ -1418,6 +1425,7 @@ bool FlaxStorage::CloseFileHandles()
return false; return false;
} }
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(ContentFiles);
// Note: this is usually called by the content manager when this file is not used or on exit // Note: this is usually called by the content manager when this file is not used or on exit
// In those situations all the async tasks using this storage should be cancelled externally // In those situations all the async tasks using this storage should be cancelled externally
+14 -4
View File
@@ -7,7 +7,7 @@
/// <summary> /// <summary>
/// Asset reference utility that doesn't add reference to that asset. Handles asset unload event. /// Asset reference utility that doesn't add reference to that asset. Handles asset unload event.
/// </summary> /// </summary>
API_CLASS(InBuild) class WeakAssetReferenceBase API_CLASS(InBuild) class WeakAssetReferenceBase : public IAssetReference
{ {
public: public:
typedef Delegate<> EventType; typedef Delegate<> EventType;
@@ -56,9 +56,14 @@ public:
/// </summary> /// </summary>
String ToString() const; String ToString() const;
public:
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
protected: protected:
void OnSet(Asset* asset); void OnSet(Asset* asset);
void OnUnloaded(Asset* asset);
}; };
/// <summary> /// <summary>
@@ -72,7 +77,13 @@ public:
/// Initializes a new instance of the <see cref="WeakAssetReference"/> class. /// Initializes a new instance of the <see cref="WeakAssetReference"/> class.
/// </summary> /// </summary>
WeakAssetReference() WeakAssetReference()
: WeakAssetReferenceBase() {
}
/// <summary>
/// Initializes a new instance of the <see cref="WeakAssetReference"/> class.
/// </summary>
explicit WeakAssetReference(decltype(__nullptr))
{ {
} }
@@ -81,7 +92,6 @@ public:
/// </summary> /// </summary>
/// <param name="asset">The asset to set.</param> /// <param name="asset">The asset to set.</param>
WeakAssetReference(T* asset) WeakAssetReference(T* asset)
: WeakAssetReferenceBase()
{ {
OnSet(asset); OnSet(asset);
} }

Some files were not shown because too many files have changed in this diff Show More