Merge remote-tracking branch 'origin/1.11' into work
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled

This commit is contained in:
2025-07-05 14:19:52 +03:00
286 changed files with 4528 additions and 1308 deletions

View File

@@ -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

View File

@@ -15,6 +15,7 @@
#include "./Flax/Common.hlsl" #include "./Flax/Common.hlsl"
#include "./Flax/MaterialCommon.hlsl" #include "./Flax/MaterialCommon.hlsl"
#include "./Flax/GBufferCommon.hlsl" #include "./Flax/GBufferCommon.hlsl"
#include "./Flax/TerrainCommon.hlsl"
@7 @7
// Primary constant buffer (with additional material parameters) // Primary constant buffer (with additional material parameters)
META_CB_BEGIN(0, Data) META_CB_BEGIN(0, Data)
@@ -334,7 +335,7 @@ VertexOutput VS(TerrainVertexInput input)
float lodValue = CurrentLOD; float lodValue = CurrentLOD;
float morphAlpha = lodCalculated - CurrentLOD; float morphAlpha = lodCalculated - CurrentLOD;
// Sample heightmap // Sample heightmap and splatmaps
float2 heightmapUVs = input.TexCoord * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw; float2 heightmapUVs = input.TexCoord * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw;
#if USE_SMOOTH_LOD_TRANSITION #if USE_SMOOTH_LOD_TRANSITION
float4 heightmapValueThisLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); float4 heightmapValueThisLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
@@ -342,7 +343,6 @@ VertexOutput VS(TerrainVertexInput input)
float2 heightmapUVsNextLOD = nextLODPos * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw; float2 heightmapUVsNextLOD = nextLODPos * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw;
float4 heightmapValueNextLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1); float4 heightmapValueNextLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1);
float4 heightmapValue = lerp(heightmapValueThisLOD, heightmapValueNextLOD, morphAlpha); float4 heightmapValue = lerp(heightmapValueThisLOD, heightmapValueNextLOD, morphAlpha);
bool isHole = max(heightmapValueThisLOD.b + heightmapValueThisLOD.a, heightmapValueNextLOD.b + heightmapValueNextLOD.a) >= 1.9f;
#if USE_TERRAIN_LAYERS #if USE_TERRAIN_LAYERS
float4 splatmapValueThisLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); float4 splatmapValueThisLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
float4 splatmapValueNextLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1); float4 splatmapValueNextLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1);
@@ -355,7 +355,6 @@ VertexOutput VS(TerrainVertexInput input)
#endif #endif
#else #else
float4 heightmapValue = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); float4 heightmapValue = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
bool isHole = (heightmapValue.b + heightmapValue.a) >= 1.9f;
#if USE_TERRAIN_LAYERS #if USE_TERRAIN_LAYERS
float4 splatmap0Value = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); float4 splatmap0Value = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
#if TERRAIN_LAYERS_DATA_SIZE > 1 #if TERRAIN_LAYERS_DATA_SIZE > 1
@@ -363,12 +362,11 @@ VertexOutput VS(TerrainVertexInput input)
#endif #endif
#endif #endif
#endif #endif
float height = (float)((int)(heightmapValue.x * 255.0) + ((int)(heightmapValue.y * 255) << 8)) / 65535.0; float height = DecodeHeightmapHeight(heightmapValue);
// Extract normal and the holes mask // Extract normal and the holes mask
float2 normalTemp = float2(heightmapValue.b, heightmapValue.a) * 2.0f - 1.0f; bool isHole;
float3 normal = float3(normalTemp.x, sqrt(1.0 - saturate(dot(normalTemp, normalTemp))), normalTemp.y); float3 normal = DecodeHeightmapNormal(heightmapValue, isHole);
normal = normalize(normal);
output.Geometry.HolesMask = isHole ? 0 : 1; output.Geometry.HolesMask = isHole ? 0 : 1;
if (isHole) if (isHole)
{ {

BIN
Content/Shaders/ProbesFilter.flax (Stored with Git LFS)

Binary file not shown.

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.",

View File

@@ -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
{ {

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"
@@ -380,6 +381,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 +473,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 +627,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 +655,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));
@@ -778,6 +783,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 +796,7 @@ void GameCookerService::Update()
{ {
if (IsRunning) if (IsRunning)
{ {
PROFILE_MEM(Editor);
ScopeLock lock(ProgressLocker); ScopeLock lock(ProgressLocker);
if (ProgressMsg.HasChars()) if (ProgressMsg.HasChars())

View File

@@ -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);

View File

@@ -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

View File

@@ -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

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();

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.

View File

@@ -13,6 +13,7 @@
#include "Engine/Scripting/Internal/MainThreadManagedInvokeAction.h" #include "Engine/Scripting/Internal/MainThreadManagedInvokeAction.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"
@@ -74,7 +75,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)
{ {
@@ -82,7 +83,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);
@@ -90,12 +91,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);
} }
@@ -156,7 +157,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>();
} }
@@ -172,7 +175,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>();
} }

View File

@@ -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++)

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;
@@ -658,6 +659,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>
@@ -709,6 +752,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 />
@@ -726,6 +770,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();

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))
{ {

View File

@@ -76,9 +76,13 @@ namespace FlaxEditor.SceneGraph.Actors
// Skip removing this terrain file sif it's still referenced // Skip removing this terrain file sif it's still referenced
var sceneReferences = Editor.GetAssetReferences(e.SceneId); var sceneReferences = Editor.GetAssetReferences(e.SceneId);
if (sceneReferences != null && sceneReferences.Contains(e.TerrainId)) if (sceneReferences != null && sceneReferences.Contains(e.TerrainId))
{
Debug.Log($"Skip removing files used by terrain {e.TerrainId} on scene {e.SceneId} as it's still in use");
continue; continue;
}
// Delete files // Delete files
Debug.Log($"Removing files used by removed terrain {e.TerrainId} on scene {e.SceneId}");
foreach (var file in e.Files) foreach (var file in e.Files)
{ {
if (file != null && File.Exists(file)) if (file != null && File.Exists(file))

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.

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);

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);

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

View File

@@ -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); \

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();
} }

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;
}
}
} }
} }
} }

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();
} }

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)

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);

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)

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());

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"
@@ -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))
{ {
@@ -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++)

View File

@@ -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);

View File

@@ -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))

View File

@@ -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];

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;

View File

@@ -10,6 +10,7 @@
#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"
@@ -18,6 +19,7 @@ REGISTER_BINARY_ASSET_WITH_UPGRADER(AudioClip, "FlaxEngine.AudioClip", AudioClip
bool AudioClip::StreamingTask::Run() bool AudioClip::StreamingTask::Run()
{ {
PROFILE_MEM(Audio);
AssetReference<AudioClip> ref = _asset.Get(); AssetReference<AudioClip> ref = _asset.Get();
if (ref == nullptr || AudioBackend::Instance == nullptr) if (ref == nullptr || AudioBackend::Instance == nullptr)
return true; return true;
@@ -318,6 +320,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)
{ {

View File

@@ -21,9 +21,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)
@@ -264,7 +263,7 @@ void AudioSource::RequestStreamingBuffersUpdate()
_needToUpdateStreamingBuffers = true; _needToUpdateStreamingBuffers = true;
} }
void AudioSource::OnClipChanged() void AudioSource::OnAssetChanged(Asset* asset, void* caller)
{ {
Stop(); Stop();
@@ -276,7 +275,7 @@ void AudioSource::OnClipChanged()
} }
} }
void AudioSource::OnClipLoaded() void AudioSource::OnAssetLoaded(Asset* asset, void* caller)
{ {
if (!SourceID) if (!SourceID)
return; return;
@@ -302,6 +301,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())

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.

View File

@@ -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());

View File

@@ -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();

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());
@@ -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);
} }

View File

@@ -7,6 +7,7 @@
#include "Engine/Core/Types/String.h" #include "Engine/Core/Types/String.h"
#include "Engine/Platform/CriticalSection.h" #include "Engine/Platform/CriticalSection.h"
#include "Engine/Scripting/ScriptingObject.h" #include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Threading/ConcurrentSystemLocker.h"
#include "Config.h" #include "Config.h"
#include "Types.h" #include "Types.h"
@@ -18,6 +19,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 +63,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,18 +106,22 @@ 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>

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>

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,6 +599,7 @@ void Animation::OnScriptingDispose()
Asset::LoadResult Animation::load() Asset::LoadResult Animation::load()
{ {
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Get stream with animations data // Get stream with animations data

View File

@@ -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,6 +26,7 @@ AnimationGraph::AnimationGraph(const SpawnParams& params, const AssetInfo* info)
Asset::LoadResult AnimationGraph::load() Asset::LoadResult AnimationGraph::load()
{ {
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Get stream with graph data // Get stream with graph data
@@ -83,6 +85,7 @@ bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, b
Log::ArgumentNullException(); Log::ArgumentNullException();
return true; return true;
} }
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Create Graph data // Create Graph data

View File

@@ -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,6 +21,7 @@ AnimationGraphFunction::AnimationGraphFunction(const SpawnParams& params, const
Asset::LoadResult AnimationGraphFunction::load() Asset::LoadResult AnimationGraphFunction::load()
{ {
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker); ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Get graph data from chunk // Get graph data from chunk

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
} }

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())

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

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"
@@ -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

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();
@@ -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
@@ -416,6 +421,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 +471,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();
@@ -593,6 +600,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 +643,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 +697,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)
{ {
@@ -791,6 +800,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)
@@ -1023,6 +1049,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 +1072,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 +1083,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();
@@ -1097,6 +1128,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 +1242,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 +1311,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());

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>

View File

@@ -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
}; };

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;
@@ -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());

View File

@@ -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>

View File

@@ -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();

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>

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;
}
}; };

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

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);
} }

View File

@@ -13,6 +13,7 @@
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Platform/FileSystem.h" #include "Engine/Platform/FileSystem.h"
#include "Engine/Platform/Platform.h" #include "Engine/Platform/Platform.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Engine/Globals.h" #include "Engine/Engine/Globals.h"
#include "ImportTexture.h" #include "ImportTexture.h"
#include "ImportModel.h" #include "ImportModel.h"
@@ -151,6 +152,7 @@ bool CreateAssetContext::AllocateChunk(int32 index)
} }
// Create new chunk // Create new chunk
PROFILE_MEM(ContentFiles);
Data.Header.Chunks[index] = New<FlaxChunk>(); Data.Header.Chunks[index] = New<FlaxChunk>();
return false; return false;
} }

View File

@@ -3,7 +3,7 @@
#include "Cache.h" #include "Cache.h"
#include "FlaxEngine.Gen.h" #include "FlaxEngine.Gen.h"
CollectionPoolCache<ISerializeModifier, Cache::ISerializeModifierClearCallback> Cache::ISerializeModifier; Cache::ISerializeModifierCache Cache::ISerializeModifier;
void Cache::ISerializeModifierClearCallback(::ISerializeModifier* obj) void Cache::ISerializeModifierClearCallback(::ISerializeModifier* obj)
{ {

View File

@@ -15,11 +15,12 @@ public:
static void ISerializeModifierClearCallback(ISerializeModifier* obj); static void ISerializeModifierClearCallback(ISerializeModifier* obj);
public: public:
typedef CollectionPoolCache<ISerializeModifier, ISerializeModifierClearCallback> ISerializeModifierCache;
/// <summary> /// <summary>
/// Gets the ISerializeModifier lookup cache. Safe allocation, per thread, uses caching. /// Gets the ISerializeModifier lookup cache. Safe allocation, per thread, uses caching.
/// </summary> /// </summary>
static CollectionPoolCache<ISerializeModifier, ISerializeModifierClearCallback> ISerializeModifier; static ISerializeModifierCache ISerializeModifier;
public: public:

View File

@@ -20,6 +20,7 @@ API_CLASS(InBuild) class Array
public: public:
using ItemType = T; using ItemType = T;
using AllocationData = typename AllocationType::template Data<T>; using AllocationData = typename AllocationType::template Data<T>;
using AllocationTag = typename AllocationType::Tag;
private: private:
int32 _count; int32 _count;
@@ -36,6 +37,17 @@ public:
{ {
} }
/// <summary>
/// Initializes an empty <see cref="Array"/> without reserving any space.
/// </summary>
/// <param name="tag">The custom allocation tag.</param>
Array(AllocationTag tag)
: _count(0)
, _capacity(0)
, _allocation(tag)
{
}
/// <summary> /// <summary>
/// Initializes <see cref="Array"/> by reserving space. /// Initializes <see cref="Array"/> by reserving space.
/// </summary> /// </summary>

View File

@@ -30,13 +30,17 @@
#endif #endif
// Enable logging service (saving log to file, can be disabled using -nolog command line) // Enable logging service (saving log to file, can be disabled using -nolog command line)
#ifndef LOG_ENABLE
#define LOG_ENABLE 1 #define LOG_ENABLE 1
#endif
// Enable crash reporting service (stack trace and crash dump collecting) // Enable crash reporting service (stack trace and crash dump collecting)
#define CRASH_LOG_ENABLE (!BUILD_RELEASE) #define CRASH_LOG_ENABLE (!BUILD_RELEASE)
// Enable/disable assertion // Enable/disable assertion
#ifndef ENABLE_ASSERTION
#define ENABLE_ASSERTION (!BUILD_RELEASE) #define ENABLE_ASSERTION (!BUILD_RELEASE)
#endif
// Enable/disable assertion for Engine low layers // Enable/disable assertion for Engine low layers
#define ENABLE_ASSERTION_LOW_LAYERS ENABLE_ASSERTION && (BUILD_DEBUG || FLAX_TESTS) #define ENABLE_ASSERTION_LOW_LAYERS ENABLE_ASSERTION && (BUILD_DEBUG || FLAX_TESTS)

View File

@@ -12,6 +12,9 @@
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Core/Collections/HashSet.h" #include "Engine/Core/Collections/HashSet.h"
#endif #endif
#if COMPILE_WITH_PROFILER
#include "Engine/Profiler/ProfilerMemory.h"
#endif
/// <summary> /// <summary>
/// The function object that supports binding static, member and lambda functions. /// The function object that supports binding static, member and lambda functions.
@@ -457,6 +460,9 @@ public:
/// <param name="f">The function to bind.</param> /// <param name="f">The function to bind.</param>
void Bind(const FunctionType& f) void Bind(const FunctionType& f)
{ {
#if COMPILE_WITH_PROFILER
PROFILE_MEM(EngineDelegate);
#endif
#if DELEGATE_USE_ATOMIC #if DELEGATE_USE_ATOMIC
const intptr size = Platform::AtomicRead(&_size); const intptr size = Platform::AtomicRead(&_size);
FunctionType* bindings = (FunctionType*)Platform::AtomicRead(&_ptr); FunctionType* bindings = (FunctionType*)Platform::AtomicRead(&_ptr);

View File

@@ -1,6 +1,7 @@
// Copyright (c) Wojciech Figat. All rights reserved. // Copyright (c) Wojciech Figat. All rights reserved.
#include "Log.h" #include "Log.h"
#if LOG_ENABLE
#include "Engine/Engine/CommandLine.h" #include "Engine/Engine/CommandLine.h"
#include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Types/DateTime.h"
#include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Array.h"
@@ -8,6 +9,7 @@
#include "Engine/Engine/Globals.h" #include "Engine/Engine/Globals.h"
#include "Engine/Platform/FileSystem.h" #include "Engine/Platform/FileSystem.h"
#include "Engine/Platform/CriticalSection.h" #include "Engine/Platform/CriticalSection.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Serialization/FileWriteStream.h" #include "Engine/Serialization/FileWriteStream.h"
#include "Engine/Debug/Exceptions/Exceptions.h" #include "Engine/Debug/Exceptions/Exceptions.h"
#if USE_EDITOR #if USE_EDITOR
@@ -42,6 +44,7 @@ bool Log::Logger::Init()
// Skip if disabled // Skip if disabled
if (!IsLogEnabled()) if (!IsLogEnabled())
return false; return false;
PROFILE_MEM(Engine);
// Create logs directory (if is missing) // Create logs directory (if is missing)
#if USE_EDITOR #if USE_EDITOR
@@ -119,6 +122,7 @@ void Log::Logger::Write(const StringView& msg)
const auto length = msg.Length(); const auto length = msg.Length();
if (length <= 0) if (length <= 0)
return; return;
PROFILE_MEM(Engine);
LogLocker.Lock(); LogLocker.Lock();
if (IsDuringLog) if (IsDuringLog)
@@ -258,6 +262,7 @@ void Log::Logger::Write(LogType type, const StringView& msg)
{ {
if (msg.Length() <= 0) if (msg.Length() <= 0)
return; return;
PROFILE_MEM(Engine);
const bool isError = IsError(type); const bool isError = IsError(type);
// Create message for the log file // Create message for the log file
@@ -306,3 +311,5 @@ const Char* ToString(LogType e)
} }
return result; return result;
} }
#endif

View File

@@ -7,27 +7,6 @@
#include "Engine/Core/Types/String.h" #include "Engine/Core/Types/String.h"
#include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/StringView.h"
// Enable/disable auto flush function
#define LOG_ENABLE_AUTO_FLUSH 1
/// <summary>
/// Sends a formatted message to the log file (message type - describes level of the log (see LogType enum))
/// </summary>
#define LOG(messageType, format, ...) Log::Logger::Write(LogType::messageType, ::String::Format(TEXT(format), ##__VA_ARGS__))
/// <summary>
/// Sends a string message to the log file (message type - describes level of the log (see LogType enum))
/// </summary>
#define LOG_STR(messageType, str) Log::Logger::Write(LogType::messageType, str)
#if LOG_ENABLE_AUTO_FLUSH
// Noop as log is auto-flushed on write
#define LOG_FLUSH()
#else
// Flushes the log file buffer
#define LOG_FLUSH() Log::Logger::Flush()
#endif
/// <summary> /// <summary>
/// The log message types. /// The log message types.
/// </summary> /// </summary>
@@ -54,6 +33,31 @@ API_ENUM() enum class LogType
Fatal = 8, Fatal = 8,
}; };
#if LOG_ENABLE
// Enable/disable auto flush function
#define LOG_ENABLE_AUTO_FLUSH 1
/// <summary>
/// Sends a formatted message to the log file (message type - describes level of the log (see LogType enum))
/// </summary>
#define LOG(messageType, format, ...) Log::Logger::Write(LogType::messageType, ::String::Format(TEXT(format), ##__VA_ARGS__))
/// <summary>
/// Sends a string message to the log file (message type - describes level of the log (see LogType enum))
/// </summary>
#define LOG_STR(messageType, str) Log::Logger::Write(LogType::messageType, str)
#if LOG_ENABLE_AUTO_FLUSH
// Noop as log is auto-flushed on write
#define LOG_FLUSH()
#else
// Flushes the log file buffer
#define LOG_FLUSH() Log::Logger::Flush()
#endif
#define LOG_FLOOR() Log::Logger::WriteFloor()
extern const Char* ToString(LogType e); extern const Char* ToString(LogType e);
namespace Log namespace Log
@@ -186,3 +190,12 @@ namespace Log
static void ProcessLogMessage(LogType type, const StringView& msg, fmt_flax::memory_buffer& w); static void ProcessLogMessage(LogType type, const StringView& msg, fmt_flax::memory_buffer& w);
}; };
} }
#else
#define LOG(messageType, format, ...) {}
#define LOG_STR(messageType, str) {}
#define LOG_FLUSH() {}
#define LOG_FLOOR() {}
#endif

View File

@@ -47,6 +47,7 @@ ThreadLocal<LogContextThreadData> GlobalLogContexts;
void LogContext::Print(LogType verbosity) void LogContext::Print(LogType verbosity)
{ {
#if LOG_ENABLE
auto& stack = GlobalLogContexts.Get(); auto& stack = GlobalLogContexts.Get();
if (stack.Count == 0) if (stack.Count == 0)
return; return;
@@ -102,6 +103,7 @@ void LogContext::Print(LogType verbosity)
// Print message // Print message
Log::Logger::Write(verbosity, msg.ToStringView()); Log::Logger::Write(verbosity, msg.ToStringView());
} }
#endif
} }
void LogContext::Push(const Guid& id) void LogContext::Push(const Guid& id)

View File

@@ -0,0 +1,54 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#include "ArenaAllocation.h"
#include "../Math/Math.h"
#include "Engine/Profiler/ProfilerMemory.h"
void ArenaAllocator::Free()
{
// Free all pages
Page* page = _first;
while (page)
{
#if COMPILE_WITH_PROFILER
ProfilerMemory::OnGroupUpdate(ProfilerMemory::Groups::MallocArena, -(int64)page->Size, -1);
#endif
Allocator::Free(page->Memory);
Page* next = page->Next;
Allocator::Free(page);
page = next;
}
// Unlink
_first = nullptr;
}
void* ArenaAllocator::Allocate(uint64 size, uint64 alignment)
{
// Find the first page that has some space left
Page* page = _first;
while (page && page->Offset + size + alignment > page->Size)
page = page->Next;
// Create a new page if need to
if (!page)
{
uint64 pageSize = Math::Max<uint64>(_pageSize, size);
#if COMPILE_WITH_PROFILER
ProfilerMemory::OnGroupUpdate(ProfilerMemory::Groups::MallocArena, (int64)pageSize, 1);
#endif
page = (Page*)Allocator::Allocate(sizeof(Page));
page->Memory = Allocator::Allocate(pageSize);
page->Next = _first;
page->Offset = 0;
page->Size = (uint32)pageSize;
_first = page;
}
// Allocate within a page
page->Offset = Math::AlignUp(page->Offset, (uint32)alignment);
void* mem = (byte*)page->Memory + page->Offset;
page->Offset += (uint32)size;
return mem;
}

View File

@@ -36,6 +36,17 @@ namespace AllocationUtils
capacity++; capacity++;
return capacity; return capacity;
} }
inline int32 CalculateCapacityGrow(int32 capacity, int32 minCapacity)
{
if (capacity < minCapacity)
capacity = minCapacity;
if (capacity < 8)
capacity = 8;
else
capacity = RoundUpToPowerOf2(capacity);
return capacity;
}
} }
/// <summary> /// <summary>
@@ -46,6 +57,7 @@ class FixedAllocation
{ {
public: public:
enum { HasSwap = false }; enum { HasSwap = false };
typedef void* Tag;
template<typename T> template<typename T>
class alignas(sizeof(void*)) Data class alignas(sizeof(void*)) Data
@@ -58,6 +70,10 @@ public:
{ {
} }
FORCE_INLINE Data(Tag tag)
{
}
FORCE_INLINE ~Data() FORCE_INLINE ~Data()
{ {
} }
@@ -106,6 +122,7 @@ class HeapAllocation
{ {
public: public:
enum { HasSwap = true }; enum { HasSwap = true };
typedef void* Tag;
template<typename T> template<typename T>
class Data class Data
@@ -118,6 +135,10 @@ public:
{ {
} }
FORCE_INLINE Data(Tag tag)
{
}
FORCE_INLINE ~Data() FORCE_INLINE ~Data()
{ {
Allocator::Free(_data); Allocator::Free(_data);
@@ -135,13 +156,7 @@ public:
FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, const int32 minCapacity) const FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, const int32 minCapacity) const
{ {
if (capacity < minCapacity) return AllocationUtils::CalculateCapacityGrow(capacity, minCapacity);
capacity = minCapacity;
if (capacity < 8)
capacity = 8;
else
capacity = AllocationUtils::RoundUpToPowerOf2(capacity);
return capacity;
} }
FORCE_INLINE void Allocate(const int32 capacity) FORCE_INLINE void Allocate(const int32 capacity)
@@ -184,6 +199,7 @@ class InlinedAllocation
{ {
public: public:
enum { HasSwap = false }; enum { HasSwap = false };
typedef void* Tag;
template<typename T> template<typename T>
class alignas(sizeof(void*)) Data class alignas(sizeof(void*)) Data
@@ -200,6 +216,10 @@ public:
{ {
} }
FORCE_INLINE Data(Tag tag)
{
}
FORCE_INLINE ~Data() FORCE_INLINE ~Data()
{ {
} }

View File

@@ -0,0 +1,144 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#include "Allocation.h"
/// <summary>
/// Allocator that uses pages for stack-based allocs without freeing memory during it's lifetime.
/// </summary>
class ArenaAllocator
{
private:
struct Page
{
void* Memory;
Page* Next;
uint32 Offset, Size;
};
int32 _pageSize;
Page* _first = nullptr;
public:
ArenaAllocator(int32 pageSizeBytes = 1024 * 1024) // 1 MB by default
: _pageSize(pageSizeBytes)
{
}
~ArenaAllocator()
{
Free();
}
// Allocates a chunk of unitialized memory.
void* Allocate(uint64 size, uint64 alignment = 1);
// Frees all memory allocations within allocator.
void Free();
// Creates a new object within the arena allocator.
template<class T, class... Args>
inline T* New(Args&&...args)
{
T* ptr = (T*)Allocate(sizeof(T));
new(ptr) T(Forward<Args>(args)...);
return ptr;
}
// Invokes destructor on values in an array and clears it.
template<typename Value, typename Allocator>
static void ClearDelete(Array<Value, Allocator>& collection)
{
Value* ptr = collection.Get();
for (int32 i = 0; i < collection.Count(); i++)
Memory::DestructItem(ptr[i]);
collection.Clear();
}
// Invokes destructor on values in a dictionary and clears it.
template<typename Key, typename Value, typename Allocator>
static void ClearDelete(Dictionary<Key, Value, Allocator>& collection)
{
for (auto it = collection.Begin(); it.IsNotEnd(); ++it)
Memory::DestructItem(it->Value);
collection.Clear();
}
};
/// <summary>
/// The memory allocation policy that uses a part of shared page allocator. Allocations are performed in stack-manner, and free is no-op.
/// </summary>
class ArenaAllocation
{
public:
enum { HasSwap = true };
typedef ArenaAllocator* Tag;
template<typename T>
class Data
{
private:
T* _data = nullptr;
ArenaAllocator* _arena = nullptr;
public:
FORCE_INLINE Data()
{
}
FORCE_INLINE Data(Tag tag)
{
_arena = tag;
}
FORCE_INLINE ~Data()
{
}
FORCE_INLINE T* Get()
{
return _data;
}
FORCE_INLINE const T* Get() const
{
return _data;
}
FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, const int32 minCapacity) const
{
return AllocationUtils::CalculateCapacityGrow(capacity, minCapacity);
}
FORCE_INLINE void Allocate(const int32 capacity)
{
ASSERT_LOW_LAYER(!_data && _arena);
_data = (T*)_arena->Allocate(capacity * sizeof(T), alignof(T));
}
FORCE_INLINE void Relocate(const int32 capacity, int32 oldCount, int32 newCount)
{
ASSERT_LOW_LAYER(_arena);
T* newData = capacity != 0 ? (T*)_arena->Allocate(capacity * sizeof(T), alignof(T)) : nullptr;
if (oldCount)
{
if (newCount > 0)
Memory::MoveItems(newData, _data, newCount);
Memory::DestructItems(_data, oldCount);
}
_data = newData;
}
FORCE_INLINE void Free()
{
_data = nullptr;
}
FORCE_INLINE void Swap(Data& other)
{
::Swap(_data, other._data);
::Swap(_arena, other._arena);
}
};
};

View File

@@ -11,6 +11,7 @@ class SimpleHeapAllocation
{ {
public: public:
enum { HasSwap = true }; enum { HasSwap = true };
typedef void* Tag;
template<typename T> template<typename T>
class Data class Data
@@ -23,6 +24,10 @@ public:
{ {
} }
FORCE_INLINE Data(Tag tag)
{
}
FORCE_INLINE ~Data() FORCE_INLINE ~Data()
{ {
if (_data) if (_data)

View File

@@ -154,9 +154,9 @@ void ObjectsRemoval::Dispose()
Object::~Object() Object::~Object()
{ {
#if BUILD_DEBUG #if BUILD_DEBUG && 0
// Prevent removing object that is still reverenced by the removal service // Prevent removing object that is still reverenced by the removal service
ASSERT(!ObjectsRemovalService::IsInPool(this)); //ASSERT(!ObjectsRemovalService::IsInPool(this));
#endif #endif
} }

View File

@@ -43,7 +43,7 @@ public:
/// <summary> /// <summary>
/// Gets the total number of milliseconds. /// Gets the total number of milliseconds.
/// </summary> /// </summary>
FORCE_INLINE double GetTotalMilliseconds() const FORCE_INLINE float GetTotalMilliseconds() const
{ {
return (float)((_end - _start) * 1000.0); return (float)((_end - _start) * 1000.0);
} }

View File

@@ -208,6 +208,14 @@ public:
return StringUtils::CompareIgnoreCase(&(*this)[Length() - suffix.Length()], *suffix) == 0; return StringUtils::CompareIgnoreCase(&(*this)[Length() - suffix.Length()], *suffix) == 0;
return StringUtils::Compare(&(*this)[Length() - suffix.Length()], *suffix) == 0; return StringUtils::Compare(&(*this)[Length() - suffix.Length()], *suffix) == 0;
} }
bool Contains(const T* subStr, StringSearchCase searchCase = StringSearchCase::CaseSensitive) const
{
const int32 length = Length();
if (subStr == nullptr || length == 0)
return false;
return (searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(_data, subStr) : StringUtils::Find(_data, subStr)) != nullptr;
}
}; };
/// <summary> /// <summary>

View File

@@ -118,7 +118,7 @@ VariantType::VariantType(Types type, const MClass* klass)
#if USE_CSHARP #if USE_CSHARP
if (klass) if (klass)
{ {
const StringAnsi& typeName = klass->GetFullName(); const StringAnsiView typeName = klass->GetFullName();
const int32 length = typeName.Length(); const int32 length = typeName.Length();
TypeName = static_cast<char*>(Allocator::Allocate(length + 1)); TypeName = static_cast<char*>(Allocator::Allocate(length + 1));
Platform::MemoryCopy(TypeName, typeName.Get(), length); Platform::MemoryCopy(TypeName, typeName.Get(), length);

View File

@@ -215,7 +215,7 @@ namespace
{ {
if (!method->IsStatic()) if (!method->IsStatic())
continue; continue;
const StringAnsi& name = method->GetName(); const StringAnsiView name = method->GetName();
if (name.Contains("Internal_") || if (name.Contains("Internal_") ||
mclass->GetFullName().Contains(".Interop.")) mclass->GetFullName().Contains(".Interop."))
continue; continue;
@@ -438,6 +438,8 @@ void DebugCommands::InitAsync()
DebugCommands::CommandFlags DebugCommands::GetCommandFlags(StringView command) DebugCommands::CommandFlags DebugCommands::GetCommandFlags(StringView command)
{ {
CommandFlags result = CommandFlags::None; CommandFlags result = CommandFlags::None;
if (command.FindLast(' ') != -1)
command = command.Left(command.Find(' '));
// TODO: fix missing string handle on 1st command execution (command gets invalid after InitCommands due to dotnet GC or dotnet interop handles flush) // TODO: fix missing string handle on 1st command execution (command gets invalid after InitCommands due to dotnet GC or dotnet interop handles flush)
String commandCopy = command; String commandCopy = command;
command = commandCopy; command = commandCopy;

View File

@@ -525,6 +525,7 @@ DebugDrawService DebugDrawServiceInstance;
bool DebugDrawService::Init() bool DebugDrawService::Init()
{ {
PROFILE_MEM(Graphics);
Context = &GlobalContext; Context = &GlobalContext;
// Init wireframe sphere cache // Init wireframe sphere cache
@@ -643,6 +644,7 @@ void DebugDrawService::Update()
} }
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Graphics);
// Update lists // Update lists
float deltaTime = Time::Update.DeltaTime.GetTotalSeconds(); float deltaTime = Time::Update.DeltaTime.GetTotalSeconds();

View File

@@ -4,6 +4,8 @@
Log::Exception::~Exception() Log::Exception::~Exception()
{ {
#if LOG_ENABLE
// Always write exception to the log // Always write exception to the log
Logger::Write(_level, ToString()); Logger::Write(_level, ToString());
#endif
} }

View File

@@ -76,9 +76,15 @@ FatalErrorType Engine::FatalError = FatalErrorType::None;
bool Engine::IsRequestingExit = false; bool Engine::IsRequestingExit = false;
int32 Engine::ExitCode = 0; int32 Engine::ExitCode = 0;
Window* Engine::MainWindow = nullptr; Window* Engine::MainWindow = nullptr;
double EngineIdleTime = 0;
int32 Engine::Main(const Char* cmdLine) int32 Engine::Main(const Char* cmdLine)
{ {
#if COMPILE_WITH_PROFILER
extern void InitProfilerMemory(const Char* cmdLine, int32 stage);
InitProfilerMemory(cmdLine, 0);
#endif
PROFILE_MEM_BEGIN(Engine);
EngineImpl::CommandLine = cmdLine; EngineImpl::CommandLine = cmdLine;
Globals::MainThreadID = Platform::GetCurrentThreadID(); Globals::MainThreadID = Platform::GetCurrentThreadID();
StartupTime = DateTime::Now(); StartupTime = DateTime::Now();
@@ -106,6 +112,9 @@ int32 Engine::Main(const Char* cmdLine)
Platform::Fatal(TEXT("Cannot init platform.")); Platform::Fatal(TEXT("Cannot init platform."));
return -1; return -1;
} }
#if COMPILE_WITH_PROFILER
InitProfilerMemory(cmdLine, 1);
#endif
Time::StartupTime = DateTime::Now(); Time::StartupTime = DateTime::Now();
Globals::StartupFolder = Globals::BinariesFolder = Platform::GetMainDirectory(); Globals::StartupFolder = Globals::BinariesFolder = Platform::GetMainDirectory();
@@ -143,7 +152,9 @@ int32 Engine::Main(const Char* cmdLine)
{ {
// End // End
LOG(Warning, "Loading project cancelled. Closing..."); LOG(Warning, "Loading project cancelled. Closing...");
#if LOG_ENABLE
Log::Logger::Dispose(); Log::Logger::Dispose();
#endif
return 0; return 0;
} }
#endif #endif
@@ -161,10 +172,11 @@ int32 Engine::Main(const Char* cmdLine)
#if !USE_EDITOR && (PLATFORM_WINDOWS || PLATFORM_LINUX || PLATFORM_MAC) #if !USE_EDITOR && (PLATFORM_WINDOWS || PLATFORM_LINUX || PLATFORM_MAC)
EngineImpl::RunInBackground = PlatformSettings::Get()->RunInBackground; EngineImpl::RunInBackground = PlatformSettings::Get()->RunInBackground;
#endif #endif
Log::Logger::WriteFloor(); LOG_FLOOR();
LOG_FLUSH(); LOG_FLUSH();
Time::Synchronize(); Time::Synchronize();
EngineImpl::IsReady = true; EngineImpl::IsReady = true;
PROFILE_MEM_END();
// Main engine loop // Main engine loop
const bool useSleep = true; // TODO: this should probably be a platform setting const bool useSleep = true; // TODO: this should probably be a platform setting
@@ -180,7 +192,10 @@ int32 Engine::Main(const Char* cmdLine)
if (timeToTick > 0.002) if (timeToTick > 0.002)
{ {
PROFILE_CPU_NAMED("Idle"); PROFILE_CPU_NAMED("Idle");
auto sleepStart = Platform::GetTimeSeconds();
Platform::Sleep(1); Platform::Sleep(1);
auto sleepEnd = Platform::GetTimeSeconds();
EngineIdleTime += sleepEnd - sleepStart;
} }
} }
@@ -205,6 +220,10 @@ int32 Engine::Main(const Char* cmdLine)
{ {
PROFILE_CPU_NAMED("Platform.Tick"); PROFILE_CPU_NAMED("Platform.Tick");
Platform::Tick(); Platform::Tick();
#if COMPILE_WITH_PROFILER
extern void TickProfilerMemory();
TickProfilerMemory();
#endif
} }
// Update game logic // Update game logic
@@ -213,6 +232,7 @@ int32 Engine::Main(const Char* cmdLine)
OnUpdate(); OnUpdate();
OnLateUpdate(); OnLateUpdate();
Time::OnEndUpdate(); Time::OnEndUpdate();
EngineIdleTime = 0;
} }
// Start physics simulation // Start physics simulation
@@ -541,16 +561,20 @@ void Engine::OnExit()
#if COMPILE_WITH_PROFILER #if COMPILE_WITH_PROFILER
ProfilerCPU::Dispose(); ProfilerCPU::Dispose();
ProfilerGPU::Dispose(); ProfilerGPU::Dispose();
ProfilerMemory::Enabled = false;
#endif #endif
#if LOG_ENABLE
// Close logging service // Close logging service
Log::Logger::Dispose(); Log::Logger::Dispose();
#endif
Platform::Exit(); Platform::Exit();
} }
void EngineImpl::InitLog() void EngineImpl::InitLog()
{ {
#if LOG_ENABLE
// Initialize logger // Initialize logger
Log::Logger::Init(); Log::Logger::Init();
@@ -604,6 +628,7 @@ void EngineImpl::InitLog()
Platform::LogInfo(); Platform::LogInfo();
LOG_FLUSH(); LOG_FLUSH();
#endif
} }
void EngineImpl::InitPaths() void EngineImpl::InitPaths()

View File

@@ -1277,6 +1277,14 @@ namespace FlaxEngine.Interop
return GC.MaxGeneration; return GC.MaxGeneration;
} }
[UnmanagedCallersOnly]
internal static void GCMemoryInfo(long* totalCommitted, long* heapSize)
{
GCMemoryInfo gcMemoryInfo = GC.GetGCMemoryInfo();
*totalCommitted = gcMemoryInfo.TotalCommittedBytes;
*heapSize = gcMemoryInfo.HeapSizeBytes;
}
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
internal static void GCWaitForPendingFinalizers() internal static void GCWaitForPendingFinalizers()
{ {

View File

@@ -195,14 +195,19 @@ namespace FlaxEngine.Interop
/// </summary> /// </summary>
/// <typeparam name="T">Array element type.</typeparam> /// <typeparam name="T">Array element type.</typeparam>
/// <param name="ptrArray">Input array.</param> /// <param name="ptrArray">Input array.</param>
/// <param name="buffer">Cached memory allocation buffer to use for the result (if size fits).</param>
/// <returns>Output array.</returns> /// <returns>Output array.</returns>
public static T[] GCHandleArrayToManagedArray<T>(ManagedArray ptrArray) where T : class public static T[] GCHandleArrayToManagedArray<T>(ManagedArray ptrArray, T[] buffer = null) where T : class
{ {
Span<IntPtr> span = ptrArray.ToSpan<IntPtr>(); Span<IntPtr> span = ptrArray.ToSpan<IntPtr>();
T[] managedArray = new T[ptrArray.Length]; if (buffer == null || buffer.Length < ptrArray.Length)
for (int i = 0; i < managedArray.Length; i++) buffer = new T[ptrArray.Length];
managedArray[i] = span[i] != IntPtr.Zero ? (T)ManagedHandle.FromIntPtr(span[i]).Target : default; for (int i = 0; i < ptrArray.Length; i++)
return managedArray; {
IntPtr ptr = span[i];
buffer[i] = ptr != IntPtr.Zero ? (T)ManagedHandle.FromIntPtr(ptr).Target : default;
}
return buffer;
} }
/// <summary> /// <summary>

View File

@@ -400,6 +400,7 @@ void Foliage::DrawClusterGlobalSA(GlobalSurfaceAtlasPass* globalSA, const Vector
void Foliage::DrawFoliageJob(int32 i) void Foliage::DrawFoliageJob(int32 i)
{ {
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(Graphics);
const FoliageType& type = FoliageTypes[i]; const FoliageType& type = FoliageTypes[i];
if (type.IsReady() && type.Model->CanBeRendered()) if (type.IsReady() && type.Model->CanBeRendered())
{ {
@@ -551,6 +552,7 @@ FoliageType* Foliage::GetFoliageType(int32 index)
void Foliage::AddFoliageType(Model* model) void Foliage::AddFoliageType(Model* model)
{ {
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(LevelFoliage);
// Ensure to have unique model // Ensure to have unique model
CHECK(model); CHECK(model);
@@ -629,6 +631,7 @@ int32 Foliage::GetFoliageTypeInstancesCount(int32 index) const
void Foliage::AddInstance(const FoliageInstance& instance) void Foliage::AddInstance(const FoliageInstance& instance)
{ {
PROFILE_MEM(LevelFoliage);
ASSERT(instance.Type >= 0 && instance.Type < FoliageTypes.Count()); ASSERT(instance.Type >= 0 && instance.Type < FoliageTypes.Count());
auto type = &FoliageTypes[instance.Type]; auto type = &FoliageTypes[instance.Type];
@@ -705,6 +708,7 @@ void Foliage::OnFoliageTypeModelLoaded(int32 index)
if (_disableFoliageTypeEvents) if (_disableFoliageTypeEvents)
return; return;
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(LevelFoliage);
auto& type = FoliageTypes[index]; auto& type = FoliageTypes[index];
ASSERT(type.IsReady()); ASSERT(type.IsReady());
@@ -803,6 +807,7 @@ void Foliage::OnFoliageTypeModelLoaded(int32 index)
void Foliage::RebuildClusters() void Foliage::RebuildClusters()
{ {
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(LevelFoliage);
// Faster path if foliage is empty or no types is ready // Faster path if foliage is empty or no types is ready
bool anyTypeReady = false; bool anyTypeReady = false;
@@ -1334,6 +1339,7 @@ void Foliage::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie
Actor::Deserialize(stream, modifier); Actor::Deserialize(stream, modifier);
PROFILE_CPU(); PROFILE_CPU();
PROFILE_MEM(LevelFoliage);
// Clear // Clear
#if FOLIAGE_USE_SINGLE_QUAD_TREE #if FOLIAGE_USE_SINGLE_QUAD_TREE

View File

@@ -4,6 +4,7 @@
#include "Engine/Core/Collections/ArrayExtensions.h" #include "Engine/Core/Collections/ArrayExtensions.h"
#include "Engine/Core/Random.h" #include "Engine/Core/Random.h"
#include "Engine/Serialization/Serialization.h" #include "Engine/Serialization/Serialization.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Foliage.h" #include "Foliage.h"
FoliageType::FoliageType() FoliageType::FoliageType()
@@ -62,6 +63,7 @@ Array<MaterialBase*> FoliageType::GetMaterials() const
void FoliageType::SetMaterials(const Array<MaterialBase*>& value) void FoliageType::SetMaterials(const Array<MaterialBase*>& value)
{ {
PROFILE_MEM(LevelFoliage);
CHECK(value.Count() == Entries.Count()); CHECK(value.Count() == Entries.Count());
for (int32 i = 0; i < value.Count(); i++) for (int32 i = 0; i < value.Count(); i++)
Entries[i].Material = value[i]; Entries[i].Material = value[i];
@@ -114,6 +116,8 @@ void FoliageType::OnModelChanged()
void FoliageType::OnModelLoaded() void FoliageType::OnModelLoaded()
{ {
PROFILE_MEM(LevelFoliage);
// Now it's ready // Now it's ready
_isReady = 1; _isReady = 1;
@@ -169,6 +173,7 @@ void FoliageType::Serialize(SerializeStream& stream, const void* otherObj)
void FoliageType::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) void FoliageType::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{ {
PROFILE_MEM(LevelFoliage);
DESERIALIZE(Model); DESERIALIZE(Model);
const auto member = stream.FindMember("Materials"); const auto member = stream.FindMember("Materials");

View File

@@ -15,6 +15,7 @@
#include "Engine/Debug/Exceptions/ArgumentNullException.h" #include "Engine/Debug/Exceptions/ArgumentNullException.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/Scripting/Enums.h" #include "Engine/Scripting/Enums.h"
#include "Engine/Threading/ThreadPoolTask.h" #include "Engine/Threading/ThreadPoolTask.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
@@ -188,6 +189,8 @@ bool GPUBuffer::IsDynamic() const
bool GPUBuffer::Init(const GPUBufferDescription& desc) bool GPUBuffer::Init(const GPUBufferDescription& desc)
{ {
PROFILE_MEM(GraphicsBuffers);
// Validate description // Validate description
#if !BUILD_RELEASE #if !BUILD_RELEASE
#define GET_NAME() GetName() #define GET_NAME() GetName()
@@ -242,6 +245,15 @@ bool GPUBuffer::Init(const GPUBufferDescription& desc)
return true; return true;
} }
#if COMPILE_WITH_PROFILER
auto group = ProfilerMemory::Groups::GraphicsBuffers;
if (EnumHasAnyFlags(_desc.Flags, GPUBufferFlags::VertexBuffer))
group = ProfilerMemory::Groups::GraphicsVertexBuffers;
else if (EnumHasAnyFlags(_desc.Flags, GPUBufferFlags::IndexBuffer))
group = ProfilerMemory::Groups::GraphicsIndexBuffers;
ProfilerMemory::IncrementGroup(group, _memoryUsage);
#endif
return false; return false;
} }
@@ -476,6 +488,15 @@ GPUResourceType GPUBuffer::GetResourceType() const
void GPUBuffer::OnReleaseGPU() void GPUBuffer::OnReleaseGPU()
{ {
#if COMPILE_WITH_PROFILER
auto group = ProfilerMemory::Groups::GraphicsBuffers;
if (EnumHasAnyFlags(_desc.Flags, GPUBufferFlags::VertexBuffer))
group = ProfilerMemory::Groups::GraphicsVertexBuffers;
else if (EnumHasAnyFlags(_desc.Flags, GPUBufferFlags::IndexBuffer))
group = ProfilerMemory::Groups::GraphicsIndexBuffers;
ProfilerMemory::IncrementGroup(group, _memoryUsage);
#endif
_desc.Clear(); _desc.Clear();
_isLocked = false; _isLocked = false;
} }

View File

@@ -1,9 +1,41 @@
// Copyright (c) Wojciech Figat. All rights reserved. // Copyright (c) Wojciech Figat. All rights reserved.
using System; using System;
using System.Runtime.CompilerServices;
using FlaxEngine.Interop;
namespace FlaxEngine namespace FlaxEngine
{ {
partial class GPUDevice
{
/// <summary>
/// Gets the list with all active GPU resources.
/// </summary>
public GPUResource[] Resources
{
get
{
IntPtr ptr = Internal_GetResourcesInternal(__unmanagedPtr);
ManagedArray array = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(ptr).Target);
return NativeInterop.GCHandleArrayToManagedArray<GPUResource>(array);
}
}
/// <summary>
/// Gets the list with all active GPU resources.
/// </summary>
/// <param name="buffer">Output buffer to fill with resource 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 void GetResources(ref GPUResource[] buffer, out int count)
{
count = 0;
IntPtr ptr = Internal_GetResourcesInternal(__unmanagedPtr);
ManagedArray array = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(ptr).Target);
buffer = NativeInterop.GCHandleArrayToManagedArray<GPUResource>(array, buffer);
count = buffer.Length;
}
}
partial struct GPUBufferDescription : IEquatable<GPUBufferDescription> partial struct GPUBufferDescription : IEquatable<GPUBufferDescription>
{ {
/// <summary> /// <summary>

View File

@@ -648,8 +648,26 @@ GPUTasksExecutor* GPUDevice::CreateTasksExecutor()
return New<DefaultGPUTasksExecutor>(); return New<DefaultGPUTasksExecutor>();
} }
#if !COMPILE_WITHOUT_CSHARP
#include "Engine/Scripting/ManagedCLR/MUtils.h"
void* GPUDevice::GetResourcesInternal()
{
_resourcesLock.Lock();
MArray* result = MCore::Array::New(GPUResource::TypeInitializer.GetClass(), _resources.Count());
int32 i = 0;
for (const auto& e : _resources)
MCore::GC::WriteArrayRef(result, e->GetOrCreateManagedInstance(), i++);
_resourcesLock.Unlock();
return result;
}
#endif
void GPUDevice::Draw() void GPUDevice::Draw()
{ {
PROFILE_MEM(Graphics);
DrawBegin(); DrawBegin();
auto context = GetMainContext(); auto context = GetMainContext();

View File

@@ -236,7 +236,7 @@ public:
/// <summary> /// <summary>
/// Gets the list with all active GPU resources. /// Gets the list with all active GPU resources.
/// </summary> /// </summary>
API_PROPERTY() Array<GPUResource*> GetResources() const; Array<GPUResource*> GetResources() const;
/// <summary> /// <summary>
/// Gets the GPU asynchronous work manager. /// Gets the GPU asynchronous work manager.
@@ -432,6 +432,12 @@ public:
/// </summary> /// </summary>
/// <returns>The GPU tasks executor.</returns> /// <returns>The GPU tasks executor.</returns>
virtual GPUTasksExecutor* CreateTasksExecutor(); virtual GPUTasksExecutor* CreateTasksExecutor();
private:
// Internal bindings
#if !COMPILE_WITHOUT_CSHARP
API_FUNCTION(NoProxy) void* GetResourcesInternal();
#endif
}; };
/// <summary> /// <summary>

View File

@@ -9,6 +9,7 @@
#include "Engine/Engine/CommandLine.h" #include "Engine/Engine/CommandLine.h"
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Profiler/ProfilerGPU.h" #include "Engine/Profiler/ProfilerGPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Render2D/Font.h" #include "Engine/Render2D/Font.h"
bool Graphics::UseVSync = false; bool Graphics::UseVSync = false;
@@ -97,9 +98,10 @@ void Graphics::DisposeDevice()
bool GraphicsService::Init() bool GraphicsService::Init()
{ {
ASSERT(GPUDevice::Instance == nullptr); ASSERT(GPUDevice::Instance == nullptr);
PROFILE_MEM(Graphics);
// Create and initialize graphics device // Create and initialize graphics device
Log::Logger::WriteFloor(); LOG_FLOOR();
LOG(Info, "Creating Graphics Device..."); LOG(Info, "Creating Graphics Device...");
PixelFormatExtensions::Init(); PixelFormatExtensions::Init();
GPUDevice* device = nullptr; GPUDevice* device = nullptr;
@@ -214,7 +216,7 @@ bool GraphicsService::Init()
{ {
return true; return true;
} }
Log::Logger::WriteFloor(); LOG_FLOOR();
return false; return false;
} }

View File

@@ -29,7 +29,7 @@ bool DeferredMaterialShader::CanUseLightmap() const
bool DeferredMaterialShader::CanUseInstancing(InstancingHandler& handler) const bool DeferredMaterialShader::CanUseInstancing(InstancingHandler& handler) const
{ {
handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, }; handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, };
return true; return _instanced;
} }
void DeferredMaterialShader::Bind(BindParameters& params) void DeferredMaterialShader::Bind(BindParameters& params)
@@ -42,6 +42,8 @@ void DeferredMaterialShader::Bind(BindParameters& params)
// Setup features // Setup features
const bool useLightmap = _info.BlendMode == MaterialBlendMode::Opaque && LightmapFeature::Bind(params, cb, srv); const bool useLightmap = _info.BlendMode == MaterialBlendMode::Opaque && LightmapFeature::Bind(params, cb, srv);
if (_info.ShadingModel == MaterialShadingModel::CustomLit)
ForwardShadingFeature::Bind(params, cb, srv);
// Setup parameters // Setup parameters
MaterialParameter::BindMeta bindMeta; MaterialParameter::BindMeta bindMeta;
@@ -112,6 +114,9 @@ void DeferredMaterialShader::Unload()
bool DeferredMaterialShader::Load() bool DeferredMaterialShader::Load()
{ {
// TODO: support instancing when using ForwardShadingFeature
_instanced = _info.BlendMode == MaterialBlendMode::Opaque && _info.ShadingModel != MaterialShadingModel::CustomLit;
bool failed = false; bool failed = false;
auto psDesc = GPUPipelineState::Description::Default; auto psDesc = GPUPipelineState::Description::Default;
psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == MaterialFeaturesFlags::None; psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == MaterialFeaturesFlags::None;

View File

@@ -65,6 +65,7 @@ private:
private: private:
Cache _cache; Cache _cache;
Cache _cacheInstanced; Cache _cacheInstanced;
bool _instanced;
public: public:
DeferredMaterialShader(const StringView& name) DeferredMaterialShader(const StringView& name)

View File

@@ -25,7 +25,7 @@ DrawPass ForwardMaterialShader::GetDrawModes() const
bool ForwardMaterialShader::CanUseInstancing(InstancingHandler& handler) const bool ForwardMaterialShader::CanUseInstancing(InstancingHandler& handler) const
{ {
handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, }; handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, };
return true; return false; // TODO: support instancing when using ForwardShadingFeature
} }
void ForwardMaterialShader::Bind(BindParameters& params) void ForwardMaterialShader::Bind(BindParameters& params)

View File

@@ -103,6 +103,11 @@ API_ENUM() enum class MaterialShadingModel : byte
/// The foliage material. Intended for foliage materials like leaves and grass that need light scattering to transport simulation through the thin object. /// The foliage material. Intended for foliage materials like leaves and grass that need light scattering to transport simulation through the thin object.
/// </summary> /// </summary>
Foliage = 3, Foliage = 3,
/// <summary>
/// The custom lit shader that calculates own lighting such as Cel Shading. It has access to the scene lights data during both GBuffer and Forward pass rendering.
/// </summary>
CustomLit = 5,
}; };
/// <summary> /// <summary>

View File

@@ -15,6 +15,7 @@
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h" #include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
#include "Engine/Scripting/Enums.h" #include "Engine/Scripting/Enums.h"
#include "Engine/Streaming/Streaming.h" #include "Engine/Streaming/Streaming.h"
#include "Engine/Profiler/ProfilerMemory.h"
bool MaterialInfo8::operator==(const MaterialInfo8& other) const bool MaterialInfo8::operator==(const MaterialInfo8& other) const
{ {
@@ -604,10 +605,11 @@ int32 MaterialParams::GetVersionHash() const
void MaterialParams::Bind(MaterialParamsLink* link, MaterialParameter::BindMeta& meta) void MaterialParams::Bind(MaterialParamsLink* link, MaterialParameter::BindMeta& meta)
{ {
ASSERT(link && link->This); ASSERT(link && link->This);
for (int32 i = 0; i < link->This->Count(); i++) const int32 count = link->This->Count();
for (int32 i = 0; i < count; i++)
{ {
MaterialParamsLink* l = link; MaterialParamsLink* l = link;
while (l->Down && !l->This->At(i).IsOverride()) while (l->Down && !l->This->At(i).IsOverride() && l->Down->This->Count() == count)
{ {
l = l->Down; l = l->Down;
} }
@@ -638,6 +640,7 @@ void MaterialParams::Dispose()
bool MaterialParams::Load(ReadStream* stream) bool MaterialParams::Load(ReadStream* stream)
{ {
PROFILE_MEM(GraphicsMaterials);
bool result = false; bool result = false;
// Release // Release

View File

@@ -11,6 +11,7 @@
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h" #include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
#include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Engine/Time.h" #include "Engine/Engine/Time.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "DecalMaterialShader.h" #include "DecalMaterialShader.h"
#include "PostFxMaterialShader.h" #include "PostFxMaterialShader.h"
#include "ForwardMaterialShader.h" #include "ForwardMaterialShader.h"
@@ -136,6 +137,7 @@ MaterialShader::~MaterialShader()
MaterialShader* MaterialShader::Create(const StringView& name, MemoryReadStream& shaderCacheStream, const MaterialInfo& info) MaterialShader* MaterialShader::Create(const StringView& name, MemoryReadStream& shaderCacheStream, const MaterialInfo& info)
{ {
PROFILE_MEM(GraphicsMaterials);
MaterialShader* material; MaterialShader* material;
switch (info.Domain) switch (info.Domain)
{ {
@@ -199,6 +201,7 @@ protected:
MaterialShader* MaterialShader::CreateDummy(MemoryReadStream& shaderCacheStream, const MaterialInfo& info) MaterialShader* MaterialShader::CreateDummy(MemoryReadStream& shaderCacheStream, const MaterialInfo& info)
{ {
PROFILE_MEM(GraphicsMaterials);
MaterialShader* material = New<DummyMaterial>(); MaterialShader* material = New<DummyMaterial>();
if (material->Load(shaderCacheStream, info)) if (material->Load(shaderCacheStream, info))
{ {
@@ -225,6 +228,7 @@ bool MaterialShader::IsReady() const
bool MaterialShader::Load(MemoryReadStream& shaderCacheStream, const MaterialInfo& info) bool MaterialShader::Load(MemoryReadStream& shaderCacheStream, const MaterialInfo& info)
{ {
PROFILE_MEM(GraphicsMaterials);
ASSERT(!_isLoaded); ASSERT(!_isLoaded);
// Cache material info // Cache material info

View File

@@ -14,6 +14,7 @@
#include "Engine/Renderer/RenderList.h" #include "Engine/Renderer/RenderList.h"
#include "Engine/Scripting/ManagedCLR/MCore.h" #include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if USE_EDITOR #if USE_EDITOR
#include "Engine/Renderer/GBufferPass.h" #include "Engine/Renderer/GBufferPass.h"
#endif #endif
@@ -48,6 +49,7 @@ namespace
{ {
bool UpdateMesh(MeshBase* mesh, uint32 vertexCount, uint32 triangleCount, PixelFormat indexFormat, const Float3* vertices, const void* triangles, const Float3* normals, const Float3* tangents, const Float2* uvs, const Color32* colors) bool UpdateMesh(MeshBase* mesh, uint32 vertexCount, uint32 triangleCount, PixelFormat indexFormat, const Float3* vertices, const void* triangles, const Float3* normals, const Float3* tangents, const Float2* uvs, const Color32* colors)
{ {
PROFILE_MEM(GraphicsMeshes);
auto model = mesh->GetModelBase(); auto model = mesh->GetModelBase();
CHECK_RETURN(model && model->IsVirtual(), true); CHECK_RETURN(model && model->IsVirtual(), true);
CHECK_RETURN(triangles && vertices, true); CHECK_RETURN(triangles && vertices, true);
@@ -172,6 +174,7 @@ bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0Element
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0ElementType* vb0, const VB1ElementType* vb1, const VB2ElementType* vb2, const void* ib, bool use16BitIndices) bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0ElementType* vb0, const VB1ElementType* vb1, const VB2ElementType* vb2, const void* ib, bool use16BitIndices)
{ {
PROFILE_MEM(GraphicsMeshes);
Release(); Release();
// Setup GPU resources // Setup GPU resources

View File

@@ -16,6 +16,7 @@
#include "Engine/Scripting/ManagedCLR/MCore.h" #include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Threading/Task.h" #include "Engine/Threading/Task.h"
#include "Engine/Threading/Threading.h"
static_assert(MODEL_MAX_VB == 3, "Update code in mesh to match amount of vertex buffers."); static_assert(MODEL_MAX_VB == 3, "Update code in mesh to match amount of vertex buffers.");

View File

@@ -4,6 +4,7 @@
#include "Engine/Serialization/Serialization.h" #include "Engine/Serialization/Serialization.h"
#include "Engine/Content/Assets/Model.h" #include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Assets/SkinnedModel.h" #include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Profiler/ProfilerMemory.h"
bool ModelInstanceEntries::HasContentLoaded() const bool ModelInstanceEntries::HasContentLoaded() const
{ {
@@ -41,6 +42,7 @@ void ModelInstanceEntries::Serialize(SerializeStream& stream, const void* otherO
void ModelInstanceEntries::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) void ModelInstanceEntries::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{ {
PROFILE_MEM(Graphics);
const DeserializeStream& entries = stream["Entries"]; const DeserializeStream& entries = stream["Entries"];
ASSERT(entries.IsArray()); ASSERT(entries.IsArray());
Resize(entries.Size()); Resize(entries.Size());
@@ -85,6 +87,7 @@ void ModelInstanceEntries::Setup(const SkinnedModel* model)
void ModelInstanceEntries::Setup(int32 slotsCount) void ModelInstanceEntries::Setup(int32 slotsCount)
{ {
PROFILE_MEM(Graphics);
Clear(); Clear();
Resize(slotsCount); Resize(slotsCount);
} }

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