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__);
Texture2D<float> ShadowMap : register(t__SRV__);
@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
// Pixel Shader function for Forward Pass
@@ -76,9 +83,8 @@ void PS_Forward(
gBuffer.ShadingModel = MATERIAL_SHADING_MODEL;
// Calculate lighting from a single directional light
float4 shadowMask = 1.0f;
ShadowSample shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, gBuffer);
shadowMask = GetShadowMask(shadow);
float4 shadowMask = GetShadowMask(shadow);
float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false);
// Calculate lighting from sky light

View File

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

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

Binary file not shown.

View File

@@ -2,9 +2,9 @@
"Name": "Flax",
"Version": {
"Major": 1,
"Minor": 10,
"Minor": 11,
"Revision": 0,
"Build": 6705
"Build": 6800
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.",

View File

@@ -174,7 +174,9 @@ void EditorAnalytics::StartSession()
// Bind events
GameCooker::OnEvent.Bind<RegisterGameCookingStart>();
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Bind<RegisterLightmapsBuildingStart>();
#if LOG_ENABLE
Log::Logger::OnError.Bind<RegisterError>();
#endif
}
void EditorAnalytics::EndSession()
@@ -187,7 +189,9 @@ void EditorAnalytics::EndSession()
// Unbind events
GameCooker::OnEvent.Unbind<RegisterGameCookingStart>();
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Unbind<RegisterLightmapsBuildingStart>();
#if LOG_ENABLE
Log::Logger::OnError.Unbind<RegisterError>();
#endif
// End session
{

View File

@@ -30,6 +30,7 @@
#include "Engine/Scripting/ManagedCLR/MAssembly.h"
#include "Engine/Content/JsonAsset.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if PLATFORM_TOOLS_WINDOWS
#include "Platform/Windows/WindowsPlatformTools.h"
#include "Engine/Platform/Windows/WindowsPlatformSettings.h"
@@ -380,6 +381,7 @@ bool GameCooker::IsCancelRequested()
PlatformTools* GameCooker::GetTools(BuildPlatform platform)
{
PROFILE_MEM(Editor);
PlatformTools* result = nullptr;
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));
return true;
}
PROFILE_MEM(Editor);
// Setup
CancelFlag = 0;
@@ -624,6 +627,7 @@ void GameCookerImpl::ReportProgress(const String& info, float totalProgress)
void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets)
{
PROFILE_MEM(Editor);
if (Internal_OnCollectAssets == nullptr)
{
auto c = GameCooker::GetStaticClass();
@@ -651,6 +655,7 @@ void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets)
bool GameCookerImpl::Build()
{
PROFILE_MEM(Editor);
CookingData& data = *Data;
LOG(Info, "Starting Game Cooker...");
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()
{
PROFILE_MEM(Editor);
auto editorAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
editorAssembly->Unloading.Bind(OnEditorAssemblyUnloading);
GameCooker::OnCollectAssets.Bind(OnCollectAssets);
@@ -789,6 +796,7 @@ void GameCookerService::Update()
{
if (IsRunning)
{
PROFILE_MEM(Editor);
ScopeLock lock(ProgressLocker);
if (ProgressMsg.HasChars())

View File

@@ -186,7 +186,7 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
ADD_ENTRY("CFBundlePackageType", "APPL");
ADD_ENTRY("NSPrincipalClass", "NSApplication");
ADD_ENTRY("LSApplicationCategoryType", "public.app-category.games");
ADD_ENTRY("LSMinimumSystemVersion", "10.15");
ADD_ENTRY("LSMinimumSystemVersion", "13");
ADD_ENTRY("CFBundleIconFile", "icon.icns");
ADD_ENTRY_STR("CFBundleExecutable", executableName);
ADD_ENTRY_STR("CFBundleIdentifier", appIdentifier);

View File

@@ -36,6 +36,7 @@
#include "Engine/Engine/Base/GameBase.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Tools/TextureTool/TextureTool.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/Enums.h"
#if PLATFORM_TOOLS_WINDOWS

View File

@@ -6,6 +6,8 @@
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Core/Types/Stopwatch.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/BinaryModule.h"
@@ -69,6 +71,7 @@ MTypeObject* CustomEditorsUtil::GetCustomEditor(MTypeObject* refType)
bool CustomEditorsUtilService::Init()
{
PROFILE_MEM(Editor);
TRACK_ASSEMBLY(((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly);
Scripting::BinaryModuleLoaded.Bind(&OnBinaryModuleLoaded);
@@ -77,6 +80,8 @@ bool CustomEditorsUtilService::Init()
void OnAssemblyLoaded(MAssembly* assembly)
{
PROFILE_CPU_NAMED("CustomEditors.OnAssemblyLoaded");
PROFILE_MEM(Editor);
Stopwatch stopwatch;
// Prepare FlaxEngine

View File

@@ -20,6 +20,7 @@
#include "Engine/Engine/Engine.h"
#include "Engine/ShadowsOfMordor/Builder.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "FlaxEngine.Gen.h"
#if PLATFORM_LINUX
#include "Engine/Tools/TextureTool/TextureTool.h"
@@ -47,6 +48,7 @@ void Editor::CloseSplashScreen()
bool Editor::CheckProjectUpgrade()
{
PROFILE_MEM(Editor);
const auto versionFilePath = Globals::ProjectCacheFolder / TEXT("version");
// Load version cache file
@@ -366,6 +368,8 @@ bool Editor::BackupProject()
int32 Editor::LoadProduct()
{
PROFILE_MEM(Editor);
// Flax Editor product
Globals::ProductName = TEXT("Flax Editor");
Globals::CompanyName = TEXT("Flax");
@@ -626,6 +630,7 @@ int32 Editor::LoadProduct()
Window* Editor::CreateMainWindow()
{
PROFILE_MEM(Editor);
Window* window = Managed->GetMainWindow();
#if PLATFORM_LINUX
@@ -662,6 +667,7 @@ bool Editor::Init()
return true;
}
PROFILE_CPU();
PROFILE_MEM(Editor);
// If during last lightmaps baking engine crashed we could try to restore the progress
ShadowsOfMordor::Builder::Instance()->CheckIfRestoreState();
@@ -693,11 +699,13 @@ bool Editor::Init()
void Editor::BeforeRun()
{
PROFILE_MEM(Editor);
Managed->BeforeRun();
}
void Editor::BeforeExit()
{
PROFILE_MEM(Editor);
CloseSplashScreen();
Managed->Exit();
@@ -708,6 +716,8 @@ void Editor::BeforeExit()
void EditorImpl::OnUpdate()
{
PROFILE_MEM(Editor);
// Update c# editor
Editor::Managed->Update();

View File

@@ -51,7 +51,7 @@ namespace FlaxEditor.GUI
/// <summary>
/// The column title horizontal text alignment
/// </summary>
public TextAlignment TitleAlignment = TextAlignment.Near;
public TextAlignment TitleAlignment = TextAlignment.Center;
/// <summary>
/// The column title margin.

View File

@@ -13,6 +13,7 @@
#include "Engine/Scripting/Internal/MainThreadManagedInvokeAction.h"
#include "Engine/Content/Assets/VisualScript.h"
#include "Engine/Content/Content.h"
#include "Engine/Level/Actor.h"
#include "Engine/CSG/CSGBuilder.h"
#include "Engine/Engine/CommandLine.h"
#include "Engine/Renderer/ProbesRenderer.h"
@@ -74,7 +75,7 @@ void OnLightmapsBuildFinished(bool failed)
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)
{
@@ -82,7 +83,7 @@ void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
ASSERT(Internal_EnvProbeBake);
}
MObject* probeObj = e.Actor ? e.Actor->GetManagedInstance() : nullptr;
MObject* probeObj = e ? e->GetManagedInstance() : nullptr;
MainThreadManagedInvokeAction::ParamsBuilder params;
params.AddParam(started);
@@ -90,12 +91,12 @@ void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
MainThreadManagedInvokeAction::Invoke(Internal_EnvProbeBake, params);
}
void OnRegisterBake(const ProbesRenderer::Entry& e)
void OnRegisterBake(Actor* e)
{
OnBakeEvent(true, e);
}
void OnFinishBake(const ProbesRenderer::Entry& e)
void OnFinishBake(Actor* e)
{
OnBakeEvent(false, e);
}
@@ -156,7 +157,9 @@ ManagedEditor::ManagedEditor()
lightmapsBuilder->OnBuildProgress.Bind<OnLightmapsBuildProgress>();
lightmapsBuilder->OnBuildFinished.Bind<OnLightmapsBuildFinished>();
CSG::Builder::OnBrushModified.Bind<OnBrushModified>();
#if LOG_ENABLE
Log::Logger::OnMessage.Bind<OnLogMessage>();
#endif
VisualScripting::DebugFlow.Bind<OnVisualScriptingDebugFlow>();
}
@@ -172,7 +175,9 @@ ManagedEditor::~ManagedEditor()
lightmapsBuilder->OnBuildProgress.Unbind<OnLightmapsBuildProgress>();
lightmapsBuilder->OnBuildFinished.Unbind<OnLightmapsBuildFinished>();
CSG::Builder::OnBrushModified.Unbind<OnBrushModified>();
#if LOG_ENABLE
Log::Logger::OnMessage.Unbind<OnLogMessage>();
#endif
VisualScripting::DebugFlow.Unbind<OnVisualScriptingDebugFlow>();
}

View File

@@ -711,7 +711,11 @@ namespace FlaxEditor.Modules
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: or build a hash set with selected nodes for quick O(1) checks (cached until selection changes)
// Deselect child nodes
for (int i = 0; i < node.ChildNodes.Count; i++)

View File

@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FlaxEditor.SceneGraph;
using FlaxEditor.SceneGraph.Actors;
using FlaxEngine;
@@ -658,6 +659,48 @@ namespace FlaxEditor.Modules
//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>
/// Gets the actor node.
/// </summary>
@@ -709,6 +752,7 @@ namespace FlaxEditor.Modules
Level.ActorOrderInParentChanged += OnActorOrderInParentChanged;
Level.ActorNameChanged += OnActorNameChanged;
Level.ActorActiveChanged += OnActorActiveChanged;
Level.ActorDestroyChildren += OnActorDestroyChildren;
}
/// <inheritdoc />
@@ -726,6 +770,7 @@ namespace FlaxEditor.Modules
Level.ActorOrderInParentChanged -= OnActorOrderInParentChanged;
Level.ActorNameChanged -= OnActorNameChanged;
Level.ActorActiveChanged -= OnActorActiveChanged;
Level.ActorDestroyChildren -= OnActorDestroyChildren;
// Cleanup graph
Root.Dispose();

View File

@@ -6,6 +6,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Core/Math/Quaternion.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Serialization/JsonWriters.h"
#include "Engine/Serialization/JsonTools.h"
#include <ThirdParty/pugixml/pugixml.hpp>
@@ -327,6 +328,7 @@ ProjectInfo* ProjectInfo::Load(const String& path)
}
// Load
PROFILE_MEM(Editor);
auto project = New<ProjectInfo>();
if (project->LoadProject(path))
{

View File

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

View File

@@ -27,7 +27,7 @@ namespace FlaxEditor.SceneGraph
/// <summary>
/// The parent node.
/// </summary>
protected SceneGraphNode parentNode;
internal SceneGraphNode parentNode;
/// <summary>
/// Gets the children list.

View File

@@ -13,6 +13,7 @@
#include "Engine/Engine/EngineService.h"
#include "Engine/Platform/Thread.h"
#include "Engine/Threading/IRunnable.h"
#include "Engine/Profiler/ProfilerMemory.h"
void OnAsyncBegin(Thread* thread);
void OnAsyncEnd();
@@ -232,6 +233,8 @@ void OnAsyncEnd()
bool CodeEditingManagerService::Init()
{
PROFILE_MEM(Editor);
// Try get editors
#if USE_VISUAL_STUDIO_DTE
VisualStudioEditor::FindEditors(&CodeEditors);

View File

@@ -23,6 +23,7 @@
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/Script.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Level/Level.h"
#include "FlaxEngine.Gen.h"
@@ -77,7 +78,7 @@ namespace ScriptsBuilderImpl
void onScriptsReloadEnd();
void onScriptsLoaded();
void GetClassName(const StringAnsi& fullname, StringAnsi& className);
void GetClassName(const StringAnsiView fullname, StringAnsi& className);
void onCodeEditorAsyncOpenBegin()
{
@@ -276,7 +277,7 @@ bool ScriptsBuilder::GenerateProject(const StringView& customArgs)
return RunBuildTool(args);
}
void ScriptsBuilderImpl::GetClassName(const StringAnsi& fullname, StringAnsi& className)
void ScriptsBuilderImpl::GetClassName(const StringAnsiView fullname, StringAnsi& className)
{
const auto lastDotIndex = fullname.FindLast('.');
if (lastDotIndex != -1)
@@ -417,6 +418,7 @@ void ScriptsBuilder::GetBinariesConfiguration(const Char*& target, const Char*&
bool ScriptsBuilderImpl::compileGameScriptsAsyncInner()
{
PROFILE_MEM(Editor);
LOG(Info, "Starting scripts compilation...");
CallEvent(EventType::CompileStarted);
@@ -523,6 +525,8 @@ void ScriptsBuilderImpl::onEditorAssemblyUnloading(MAssembly* assembly)
bool ScriptsBuilderImpl::compileGameScriptsAsync()
{
PROFILE_MEM(Editor);
// Start
{
ScopeLock scopeLock(_locker);
@@ -566,6 +570,7 @@ bool ScriptsBuilderService::Init()
// Check flag
if (_isInited)
return false;
PROFILE_MEM(Editor);
_isInited = true;
// Link for Editor assembly unload event to clear cached Internal_OnCompilationEnd to prevent errors
@@ -663,6 +668,9 @@ bool ScriptsBuilderService::Init()
void ScriptsBuilderService::Update()
{
PROFILE_CPU();
PROFILE_MEM(Editor);
// Send compilation events
{
ScopeLock scopeLock(_compileEventsLocker);

View File

@@ -6,6 +6,7 @@ using FlaxEditor.Scripting;
using FlaxEditor.Surface.Elements;
using FlaxEditor.Windows.Assets;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.Surface.Archetypes
{
@@ -123,7 +124,8 @@ namespace FlaxEditor.Surface.Archetypes
case MaterialDomain.Particle:
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;
GetBox(MaterialNodeBoxes.Color).IsActive = isNotUnlit;
@@ -134,8 +136,8 @@ namespace FlaxEditor.Surface.Archetypes
GetBox(MaterialNodeBoxes.Roughness).IsActive = isNotUnlit;
GetBox(MaterialNodeBoxes.AmbientOcclusion).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.Refraction).IsActive = info.BlendMode != MaterialBlendMode.Opaque;
GetBox(MaterialNodeBoxes.Opacity).IsActive = info.ShadingModel == MaterialShadingModel.Subsurface || info.ShadingModel == MaterialShadingModel.Foliage || !isOpaque;
GetBox(MaterialNodeBoxes.Refraction).IsActive = !isOpaque;
GetBox(MaterialNodeBoxes.PositionOffset).IsActive = true;
GetBox(MaterialNodeBoxes.TessellationMultiplier).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
{
/// <summary>
@@ -410,13 +617,15 @@ namespace FlaxEditor.Surface.Archetypes
new NodeArchetype
{
TypeID = 8,
Create = (id, context, arch, groupArch) => new CustomCodeNode(id, context, arch, groupArch),
Title = "Custom Code",
Description = "Custom HLSL shader code expression",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(300, 200),
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[]
{
@@ -433,8 +642,6 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Output(1, "Output1", typeof(Float4), 9),
NodeElementArchetype.Factory.Output(2, "Output2", typeof(Float4), 10),
NodeElementArchetype.Factory.Output(3, "Output3", typeof(Float4), 11),
NodeElementArchetype.Factory.TextBox(60, 0, 175, 200, 0),
}
},
new NodeArchetype
@@ -874,6 +1081,7 @@ namespace FlaxEditor.Surface.Archetypes
new NodeArchetype
{
TypeID = 38,
Create = (id, context, arch, groupArch) => new CustomCodeNode(id, context, arch, groupArch),
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.",
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}",
true,
(int)MaterialTemplateInputsMapping.Utilities,
new Float2(300, 240),
},
Elements = new[]
{
@@ -890,7 +1099,6 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Text(20, 0, "Enabled"),
NodeElementArchetype.Factory.Text(0, 20, "Location"),
NodeElementArchetype.Factory.Enum(50, 20, 120, 2, typeof(MaterialTemplateInputsMapping)),
NodeElementArchetype.Factory.TextBox(0, 40, 300, 200, 0),
}
},
new NodeArchetype

View File

@@ -5,6 +5,7 @@
#include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Assets/MaterialInstance.h"
#include "Engine/Content/Content.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Level/Level.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Level/Actors/PointLight.h"
@@ -263,6 +264,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor
bool ViewportIconsRendererService::Init()
{
PROFILE_MEM(Editor);
QuadModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Quad"));
#define INIT(type, path) \
InstanceBuffers[static_cast<int32>(IconTypes::type)].Setup(1); \

View File

@@ -34,6 +34,7 @@ namespace FlaxEditor.Windows.Profiler
private List<ClickableRow> _tableRowsCache;
private Dictionary<Guid, Resource> _resourceCache;
private StringBuilder _stringBuilder;
private Asset[] _assetsCache;
public Assets()
: base("Assets")
@@ -138,12 +139,12 @@ namespace FlaxEditor.Windows.Profiler
_stringBuilder = new StringBuilder();
// Capture current assets usage info
var assets = FlaxEngine.Content.Assets;
var resources = new Resource[assets.Length];
FlaxEngine.Content.GetAssets(ref _assetsCache, out var count);
var resources = new Resource[count];
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];
if (!asset)
continue;
@@ -179,6 +180,7 @@ namespace FlaxEditor.Windows.Profiler
if (_resources == null)
_resources = new SamplesBuffer<Resource[]>();
_resources.Add(resources);
Array.Clear(_assetsCache);
}
/// <inheritdoc />
@@ -200,6 +202,7 @@ namespace FlaxEditor.Windows.Profiler
_resourceCache?.Clear();
_tableRowsCache?.Clear();
_stringBuilder?.Clear();
_assetsCache = null;
base.OnDestroy();
}

View File

@@ -2,6 +2,8 @@
#if USE_PROFILER
using System;
using System.Collections.Generic;
using FlaxEditor.GUI;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -13,9 +15,22 @@ namespace FlaxEditor.Windows.Profiler
/// <seealso cref="FlaxEditor.Windows.Profiler.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 _managedAllocationsChart;
private readonly Table _table;
private SamplesBuffer<FrameData> _frames;
private List<Row> _tableRowsCache;
private string[] _groupNames;
private int[] _groupOrder;
private Label _warningText;
public Memory()
: base("Memory")
{
@@ -50,6 +65,70 @@ namespace FlaxEditor.Windows.Profiler
Parent = layout,
};
_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 />
@@ -57,6 +136,7 @@ namespace FlaxEditor.Windows.Profiler
{
_nativeAllocationsChart.Clear();
_managedAllocationsChart.Clear();
_frames?.Clear();
}
/// <inheritdoc />
@@ -84,6 +164,19 @@ namespace FlaxEditor.Windows.Profiler
_nativeAllocationsChart.AddSample(nativeMemoryAllocation);
_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 />
@@ -91,6 +184,112 @@ namespace FlaxEditor.Windows.Profiler
{
_nativeAllocationsChart.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<Guid, Resource> _resourceCache;
private StringBuilder _stringBuilder;
private GPUResource[] _gpuResourcesCached;
public MemoryGPU()
: base("GPU Memory")
@@ -138,13 +139,15 @@ namespace FlaxEditor.Windows.Profiler
// Capture current GPU resources usage info
var contentDatabase = Editor.Instance.ContentDatabase;
var gpuResources = GPUDevice.Instance.Resources;
var resources = new Resource[gpuResources.Length];
GPUDevice.Instance.GetResources(ref _gpuResourcesCached, out var count);
var resources = new Resource[count];
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];
if (!gpuResource)
continue;
// Try to reuse cached resource info
var gpuResourceId = gpuResource.ID;
@@ -219,6 +222,7 @@ namespace FlaxEditor.Windows.Profiler
if (_resources == null)
_resources = new SamplesBuffer<Resource[]>();
_resources.Add(resources);
Array.Clear(_gpuResourcesCached);
}
/// <inheritdoc />
@@ -255,6 +259,7 @@ namespace FlaxEditor.Windows.Profiler
_assetPathToId?.Clear();
_tableRowsCache?.Clear();
_stringBuilder?.Clear();
_gpuResourcesCached = null;
base.OnDestroy();
}

View File

@@ -7,6 +7,7 @@
#include "Engine/Engine/Time.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Threading/TaskGraph.h"
class BehaviorSystem : public TaskGraphSystem
@@ -38,6 +39,7 @@ TaskGraphSystem* Behavior::System = nullptr;
void BehaviorSystem::Job(int32 index)
{
PROFILE_CPU_NAMED("Behavior.Job");
PROFILE_MEM(AI);
Behaviors[index]->UpdateAsync();
}
@@ -57,6 +59,7 @@ void BehaviorSystem::Execute(TaskGraph* graph)
bool BehaviorService::Init()
{
PROFILE_MEM(AI);
Behavior::System = New<BehaviorSystem>();
Engine::UpdateGraph->AddSystem(Behavior::System);
return false;
@@ -70,9 +73,9 @@ void BehaviorService::Dispose()
Behavior::Behavior(const SpawnParams& params)
: Script(params)
, Tree(this)
{
_knowledge.Behavior = this;
Tree.Changed.Bind<Behavior, &Behavior::ResetLogic>(this);
}
void Behavior::UpdateAsync()
@@ -172,6 +175,19 @@ void Behavior::OnDisable()
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
bool Behavior::GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior)

View File

@@ -11,7 +11,7 @@
/// <summary>
/// Behavior instance script that runs Behavior Tree execution.
/// </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();
DECLARE_SCRIPTING_TYPE(Behavior);
@@ -92,6 +92,11 @@ public:
void OnDisable() override;
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
// Editor-only utilities to debug nodes state.
API_FUNCTION(Internal) static bool GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior);

View File

@@ -4,6 +4,7 @@
#include "BehaviorTree.h"
#include "BehaviorTreeNodes.h"
#include "BehaviorKnowledgeSelector.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/BinaryModule.h"
#include "Engine/Scripting/ManagedCLR/MProperty.h"
@@ -144,6 +145,7 @@ BehaviorKnowledge::~BehaviorKnowledge()
void BehaviorKnowledge::InitMemory(BehaviorTree* tree)
{
PROFILE_MEM(AI);
if (Tree)
FreeMemory();
if (!tree)

View File

@@ -10,6 +10,7 @@
#include "Engine/Serialization/JsonSerializer.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "FlaxEngine.Gen.h"
#if USE_EDITOR
#include "Engine/Level/Level.h"
@@ -275,6 +276,7 @@ Asset::LoadResult BehaviorTree::load()
if (surfaceChunk == nullptr)
return LoadResult::MissingDataChunk;
MemoryReadStream surfaceStream(surfaceChunk->Get(), surfaceChunk->Size());
PROFILE_MEM(AI);
if (Graph.Load(&surfaceStream, true))
{
LOG(Warning, "Failed to load graph \'{0}\'", ToString());

View File

@@ -4,6 +4,7 @@
#include "AnimEvent.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Level/Actors/AnimatedModel.h"
#include "Engine/Engine/Time.h"
#include "Engine/Engine/EngineService.h"
@@ -69,6 +70,7 @@ AnimContinuousEvent::AnimContinuousEvent(const SpawnParams& params)
bool AnimationsService::Init()
{
PROFILE_MEM(Animations);
Animations::System = New<AnimationsSystem>();
Engine::UpdateGraph->AddSystem(Animations::System);
return false;
@@ -83,6 +85,7 @@ void AnimationsService::Dispose()
void AnimationsSystem::Job(int32 index)
{
PROFILE_CPU_NAMED("Animations.Job");
PROFILE_MEM(Animations);
auto animatedModel = AnimationManagerInstance.UpdateList[index];
if (CanUpdateModel(animatedModel))
{
@@ -147,6 +150,7 @@ void AnimationsSystem::PostExecute(TaskGraph* graph)
if (!Active)
return;
PROFILE_CPU_NAMED("Animations.PostExecute");
PROFILE_MEM(Animations);
// Update gameplay
for (int32 index = 0; index < AnimationManagerInstance.UpdateList.Count(); index++)

View File

@@ -6,6 +6,7 @@
#include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Graphics/Models/SkeletonData.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);

View File

@@ -7,6 +7,7 @@
#include "Engine/Content/Content.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Audio/AudioClip.h"
#include "Engine/Graphics/PostProcessSettings.h"
#if USE_EDITOR
@@ -249,6 +250,7 @@ bool SceneAnimation::Save(const StringView& path)
Asset::LoadResult SceneAnimation::load()
{
TrackStatesCount = 0;
PROFILE_MEM(AnimationsData);
// Get the data chunk
if (LoadChunk(0))

View File

@@ -12,6 +12,7 @@
#include "Engine/Audio/AudioSource.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/Script.h"
#include "Engine/Scripting/ManagedCLR/MException.h"
@@ -151,6 +152,7 @@ void SceneAnimationPlayer::Tick(float dt)
SceneAnimation* anim = Animation.Get();
if (!anim || !anim->IsLoaded())
return;
PROFILE_MEM(Animations);
// Setup state
if (_tracks.Count() != anim->TrackStatesCount)
@@ -229,6 +231,7 @@ void SceneAnimationPlayer::MapTrack(const StringView& from, const Guid& to)
SceneAnimation* anim = Animation.Get();
if (!anim || !anim->IsLoaded())
return;
PROFILE_MEM(Animations);
for (int32 j = 0; j < anim->Tracks.Count(); j++)
{
const auto& track = anim->Tracks[j];

View File

@@ -8,6 +8,7 @@
#include "Engine/Scripting/BinaryModule.h"
#include "Engine/Level/Level.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Engine/CommandLine.h"
#include "Engine/Core/Log.h"
@@ -151,6 +152,7 @@ void Audio::SetEnableHRTF(bool value)
bool AudioService::Init()
{
PROFILE_CPU_NAMED("Audio.Init");
PROFILE_MEM(Audio);
const auto settings = AudioSettings::Get();
const bool mute = CommandLine::Options.Mute.IsTrue() || settings->DisableAudio;
@@ -211,6 +213,7 @@ bool AudioService::Init()
void AudioService::Update()
{
PROFILE_CPU_NAMED("Audio.Update");
PROFILE_MEM(Audio);
// Update the master volume
float masterVolume = MasterVolume;

View File

@@ -10,6 +10,7 @@
#include "Engine/Scripting/ManagedCLR/MUtils.h"
#include "Engine/Streaming/StreamingGroup.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
#include "Engine/Tools/AudioTool/AudioTool.h"
#include "Engine/Threading/Threading.h"
@@ -18,6 +19,7 @@ REGISTER_BINARY_ASSET_WITH_UPGRADER(AudioClip, "FlaxEngine.AudioClip", AudioClip
bool AudioClip::StreamingTask::Run()
{
PROFILE_MEM(Audio);
AssetReference<AudioClip> ref = _asset.Get();
if (ref == nullptr || AudioBackend::Instance == nullptr)
return true;
@@ -318,6 +320,7 @@ bool AudioClip::init(AssetInitData& initData)
Asset::LoadResult AudioClip::load()
{
PROFILE_MEM(Audio);
#if !COMPILE_WITH_OGG_VORBIS
if (AudioHeader.Format == AudioFormat::Vorbis)
{

View File

@@ -21,9 +21,8 @@ AudioSource::AudioSource(const SpawnParams& params)
, _playOnStart(false)
, _startTime(0.0f)
, _allowSpatialization(true)
, Clip(this)
{
Clip.Changed.Bind<AudioSource, &AudioSource::OnClipChanged>(this);
Clip.Loaded.Bind<AudioSource, &AudioSource::OnClipLoaded>(this);
}
void AudioSource::SetVolume(float value)
@@ -264,7 +263,7 @@ void AudioSource::RequestStreamingBuffersUpdate()
_needToUpdateStreamingBuffers = true;
}
void AudioSource::OnClipChanged()
void AudioSource::OnAssetChanged(Asset* asset, void* caller)
{
Stop();
@@ -276,7 +275,7 @@ void AudioSource::OnClipChanged()
}
}
void AudioSource::OnClipLoaded()
void AudioSource::OnAssetLoaded(Asset* asset, void* caller)
{
if (!SourceID)
return;
@@ -302,6 +301,10 @@ void AudioSource::OnClipLoaded()
}
}
void AudioSource::OnAssetUnloaded(Asset* asset, void* caller)
{
}
bool AudioSource::UseStreaming() const
{
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.
/// </remarks>
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);
friend class AudioStreamingHandler;
@@ -293,8 +293,10 @@ public:
void RequestStreamingBuffersUpdate();
private:
void OnClipChanged();
void OnClipLoaded();
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
/// <summary>
/// Plays the audio source. Should have buffer(s) binded before.

View File

@@ -9,6 +9,7 @@
#include "Engine/Tools/AudioTool/AudioTool.h"
#include "Engine/Engine/Units.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Audio/Audio.h"
#include "Engine/Audio/AudioListener.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)
{
PROFILE_MEM(Audio);
uint32 sourceID = 0;
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)
{
PROFILE_CPU();
PROFILE_MEM(Audio);
// Pick the format for the audio data (it might not be supported natively)
ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth);
@@ -625,6 +629,8 @@ AudioBackend::FeatureFlags AudioBackendOAL::Base_Features()
void AudioBackendOAL::Base_OnActiveDeviceChanged()
{
PROFILE_MEM(Audio);
// Cleanup
Array<ALC::AudioSourceState> states;
states.EnsureCapacity(Audio::Sources.Count());

View File

@@ -9,6 +9,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Audio/Audio.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if PLATFORM_WINDOWS
// 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)
{
PROFILE_MEM(Audio);
ScopeLock lock(XAudio2::Locker);
// Get first free source
@@ -580,6 +582,7 @@ void AudioBackendXAudio2::Source_DequeueProcessedBuffers(uint32 sourceID)
uint32 AudioBackendXAudio2::Buffer_Create()
{
PROFILE_MEM(Audio);
uint32 bufferID;
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)
{
PROFILE_MEM(Audio);
CHECK(info.NumChannels <= MAX_INPUT_CHANNELS);
XAudio2::Locker.Lock();

View File

@@ -9,6 +9,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Core/LogContext.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Threading/MainThreadTask.h"
#include "Engine/Threading/ThreadLocal.h"
@@ -34,15 +35,18 @@ bool ContentDeprecated::Clear(bool newValue)
#endif
AssetReferenceBase::AssetReferenceBase(IAssetReference* owner)
: _owner(owner)
{
}
AssetReferenceBase::~AssetReferenceBase()
{
Asset* asset = _asset;
if (asset)
{
_asset = nullptr;
asset->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
asset->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
asset->RemoveReference();
asset->RemoveReference(this);
}
}
@@ -51,52 +55,60 @@ String AssetReferenceBase::ToString() const
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)
{
auto e = _asset;
if (e != asset)
{
if (e)
{
e->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
e->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
e->RemoveReference();
}
e->RemoveReference(this);
_asset = e = asset;
if (e)
{
e->AddReference();
e->OnLoaded.Bind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
e->OnUnloaded.Bind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
}
e->AddReference(this);
Changed();
if (_owner)
_owner->OnAssetChanged(asset, this);
if (e && e->IsLoaded())
{
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()
{
Asset* asset = _asset;
if (asset)
{
_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>");
}
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)
{
auto e = _asset;
if (e != asset)
{
if (e)
e->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
e->RemoveReference(this, true);
_asset = e = asset;
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()
{
Asset* asset = _asset;
if (asset)
{
_asset = nullptr;
asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
asset->RemoveReference();
asset->RemoveReference(this);
}
#if !BUILD_RELEASE
_id = Guid::Empty;
@@ -146,22 +165,34 @@ String SoftAssetReferenceBase::ToString() const
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)
{
if (_asset == asset)
return;
if (_asset)
{
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->RemoveReference();
}
_asset->RemoveReference(this);
_asset = asset;
_id = asset ? asset->GetID() : Guid::Empty;
if (asset)
{
asset->AddReference();
asset->OnUnloaded.Bind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
}
asset->AddReference(this);
Changed();
}
@@ -170,10 +201,7 @@ void SoftAssetReferenceBase::OnSet(const Guid& id)
if (_id == id)
return;
if (_asset)
{
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->RemoveReference();
}
_asset->RemoveReference(this);
_asset = nullptr;
_id = id;
Changed();
@@ -184,21 +212,7 @@ void SoftAssetReferenceBase::OnResolve(const ScriptingTypeHandle& type)
ASSERT(!_asset);
_asset = ::LoadAsset(_id, type);
if (_asset)
{
_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->AddReference(this);
}
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));
}
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
{
return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath());
@@ -354,6 +401,7 @@ uint64 Asset::GetMemoryUsage() const
if (Platform::AtomicRead(&_loadingTask))
result += sizeof(ContentLoadTask);
result += (OnLoaded.Capacity() + OnReloading.Capacity() + OnUnloaded.Capacity()) * sizeof(EventType::FunctionType);
result += _references.Capacity() * sizeof(HashSet<IAssetReference*>::Bucket);
Locker.Unlock();
return result;
}
@@ -444,6 +492,9 @@ bool Asset::WaitForLoaded(double timeoutInMilliseconds) const
}
PROFILE_CPU();
ZoneColor(TracyWaitZoneColor);
const StringView path(GetPath());
ZoneText(*path, path.Length());
Content::WaitForTask(loadingTask, timeoutInMilliseconds);
@@ -528,6 +579,7 @@ ContentLoadTask* Asset::createLoadingTask()
void Asset::startLoading()
{
PROFILE_MEM(ContentAssets);
ASSERT(!IsLoaded());
ASSERT(Platform::AtomicRead(&_loadingTask) == 0);
auto loadingTask = createLoadingTask();
@@ -627,6 +679,9 @@ void Asset::onLoaded_MainThread()
ASSERT(IsInMainThread());
// Send event
ScopeLock lock(_referencesLocker);
for (const auto& e : _references)
e.Item->OnAssetLoaded(this, this);
OnLoaded(this);
}
@@ -640,6 +695,9 @@ void Asset::onUnload_MainThread()
CancelStreaming();
// Send event
ScopeLock lock(_referencesLocker);
for (const auto& e : _references)
e.Item->OnAssetUnloaded(this, this);
OnUnloaded(this);
}

View File

@@ -7,6 +7,7 @@
#include "Engine/Core/Types/String.h"
#include "Engine/Platform/CriticalSection.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Threading/ConcurrentSystemLocker.h"
#include "Config.h"
#include "Types.h"
@@ -18,6 +19,20 @@
public: \
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>
/// Asset objects base class.
/// </summary>
@@ -48,6 +63,9 @@ protected:
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)
HashSet<IAssetReference*> _references;
CriticalSection _referencesLocker; // TODO: convert into a single interlocked exchange for the current thread owning lock
public:
/// <summary>
/// Initializes a new instance of the <see cref="Asset"/> class.
@@ -88,18 +106,22 @@ public:
/// <summary>
/// Adds reference to that asset.
/// </summary>
FORCE_INLINE void AddReference()
{
Platform::InterlockedIncrement(&_refCount);
}
void AddReference();
/// <summary>
/// Adds reference to that asset.
/// </summary>
void AddReference(IAssetReference* ref, bool week = false);
/// <summary>
/// Removes reference from that asset.
/// </summary>
FORCE_INLINE void RemoveReference()
{
Platform::InterlockedDecrement(&_refCount);
}
void RemoveReference();
/// <summary>
/// Removes reference from that asset.
/// </summary>
void RemoveReference(IAssetReference* ref, bool week = false);
public:
/// <summary>

View File

@@ -7,10 +7,11 @@
/// <summary>
/// Asset reference utility. Keeps reference to the linked asset object and handles load/unload events.
/// </summary>
class FLAXENGINE_API AssetReferenceBase
class FLAXENGINE_API AssetReferenceBase : public IAssetReference
{
protected:
Asset* _asset = nullptr;
IAssetReference* _owner = nullptr;
public:
/// <summary>
@@ -36,6 +37,12 @@ public:
/// </summary>
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>
/// Finalizes an instance of the <see cref="AssetReferenceBase"/> class.
/// </summary>
@@ -63,10 +70,14 @@ public:
/// </summary>
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:
void OnSet(Asset* asset);
void OnLoaded(Asset* asset);
void OnUnloaded(Asset* asset);
};
/// <summary>
@@ -87,6 +98,13 @@ public:
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary>
explicit AssetReference(decltype(__nullptr))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary>
@@ -96,6 +114,15 @@ public:
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>
/// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary>

View File

@@ -9,6 +9,7 @@
#include "Engine/Animations/Animations.h"
#include "Engine/Animations/SceneAnimations/SceneAnimation.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Serialization/MemoryReadStream.h"
#if USE_EDITOR
@@ -598,6 +599,7 @@ void Animation::OnScriptingDispose()
Asset::LoadResult Animation::load()
{
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Get stream with animations data

View File

@@ -9,6 +9,7 @@
#include "Engine/Core/Types/DataContainer.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Animations/Animations.h"
#include "Engine/Threading/Threading.h"
@@ -25,6 +26,7 @@ AnimationGraph::AnimationGraph(const SpawnParams& params, const AssetInfo* info)
Asset::LoadResult AnimationGraph::load()
{
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Get stream with graph data
@@ -83,6 +85,7 @@ bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, b
Log::ArgumentNullException();
return true;
}
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Create Graph data

View File

@@ -8,6 +8,7 @@
#include "Engine/Serialization/MemoryWriteStream.h"
#endif
#include "Engine/Animations/Animations.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Threading/Threading.h"
@@ -20,6 +21,7 @@ AnimationGraphFunction::AnimationGraphFunction(const SpawnParams& params, const
Asset::LoadResult AnimationGraphFunction::load()
{
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Get graph data from chunk

View File

@@ -165,9 +165,13 @@ Asset::LoadResult Material::load()
MaterialGenerator generator;
generator.Error.Bind(&OnGeneratorError);
if (_shaderHeader.Material.GraphVersion != MATERIAL_GRAPH_VERSION)
{
LOG(Info, "Converting material \'{0}\', from version {1} to {2}...", name, _shaderHeader.Material.GraphVersion, MATERIAL_GRAPH_VERSION);
}
else
{
LOG(Info, "Updating material \'{0}\'...", name);
}
// Load or create material surface
MaterialLayer* layer;
@@ -410,16 +414,18 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
// Prepare
auto& info = _shaderHeader.Material.Info;
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 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 =
info.TessellationMode != TessellationMethod::None &&
RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrainOrDeformable;
const bool useDistortion =
(info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) &&
info.BlendMode != MaterialBlendMode::Opaque &&
!isOpaque &&
EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseRefraction) &&
(info.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None;
const MaterialShadingModel shadingModel = info.ShadingModel == MaterialShadingModel::CustomLit ? MaterialShadingModel::Unlit : info.ShadingModel;
// @formatter:off
static const char* Numbers[] =
@@ -431,7 +437,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
// Setup shader macros
options.Macros.Add({ "MATERIAL_DOMAIN", Numbers[(int32)info.Domain] });
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({ "DECAL_BLEND_MODE", Numbers[(int32)info.DecalBlendingMode] });
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_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 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] });
#endif
}

View File

@@ -18,6 +18,7 @@
#include "Engine/Graphics/Models/MeshDeformation.h"
#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Renderer/DrawCall.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Tools/ModelTool/ModelTool.h"
@@ -304,6 +305,7 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
Log::ArgumentOutOfRangeException();
return true;
}
PROFILE_MEM(GraphicsMeshes);
// Dispose previous data and disable streaming (will start data uploading tasks manually)
StopStreaming();
@@ -343,6 +345,7 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
bool Model::LoadHeader(ReadStream& stream, byte& headerVersion)
{
PROFILE_MEM(GraphicsMeshes);
if (ModelBase::LoadHeader(stream, headerVersion))
return true;
@@ -509,6 +512,7 @@ bool Model::Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk
void Model::SetupMaterialSlots(int32 slotsCount)
{
PROFILE_MEM(GraphicsMeshes);
ModelBase::SetupMaterialSlots(slotsCount);
// Adjust meshes indices for slots
@@ -584,6 +588,8 @@ int32 Model::GetAllocatedResidency() const
Asset::LoadResult Model::load()
{
PROFILE_MEM(GraphicsMeshes);
// Get header chunk
auto chunk0 = GetChunk(0);
if (chunk0 == nullptr || chunk0->IsMissing())

View File

@@ -5,10 +5,12 @@
#include "Engine/Core/Math/Transform.h"
#include "Engine/Content/WeakAssetReference.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Graphics/Config.h"
#include "Engine/Graphics/Models/MeshBase.h"
#include "Engine/Graphics/Models/MeshDeformation.h"
#include "Engine/Graphics/Shaders/GPUVertexLayout.h"
#include "Engine/Threading/Threading.h"
#if GPU_ENABLE_ASYNC_RESOURCES_CREATION
#include "Engine/Threading/ThreadPoolTask.h"
#define STREAM_TASK_BASE ThreadPoolTask
@@ -51,6 +53,7 @@ public:
AssetReference<ModelBase> model = _model.Get();
if (model == nullptr)
return true;
PROFILE_MEM(GraphicsMeshes);
// Get 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)
{
PROFILE_MEM(GraphicsMeshes);
// Load descriptor
static_assert(MODEL_MESH_VERSION == 2, "Update code");
uint32 vertices, triangles;

View File

@@ -6,6 +6,7 @@
#include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Content/Upgraders/SkeletonMaskUpgrader.h"
#include "Engine/Threading/Threading.h"
REGISTER_BINARY_ASSET_WITH_UPGRADER(SkeletonMask, "FlaxEngine.SkeletonMask", SkeletonMaskUpgrader, true);

View File

@@ -18,6 +18,7 @@
#include "Engine/Content/Upgraders/SkinnedModelAssetUpgrader.h"
#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Renderer/DrawCall.h"
#if USE_EDITOR
#include "Engine/Graphics/Models/ModelData.h"
@@ -458,6 +459,7 @@ bool SkinnedModel::Init(const Span<int32>& meshesCountPerLod)
Log::ArgumentOutOfRangeException();
return true;
}
PROFILE_MEM(GraphicsMeshes);
// Dispose previous data and disable streaming (will start data uploading tasks manually)
StopStreaming();
@@ -501,6 +503,7 @@ void BlendShape::LoadHeader(ReadStream& stream, byte headerVersion)
void BlendShape::Load(ReadStream& stream, byte meshVersion)
{
PROFILE_MEM(GraphicsMeshes);
UseNormals = stream.ReadBool();
stream.ReadUint32(&MinVertexIndex);
stream.ReadUint32(&MaxVertexIndex);
@@ -531,6 +534,7 @@ void BlendShape::Save(WriteStream& stream) const
bool SkinnedModel::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly)
{
PROFILE_MEM(GraphicsMeshes);
if (ModelBase::LoadMesh(stream, meshVersion, mesh, dataIfReadOnly))
return true;
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)
{
PROFILE_MEM(GraphicsMeshes);
if (ModelBase::LoadHeader(stream, headerVersion))
return true;
static_assert(MODEL_HEADER_VERSION == 2, "Update code");
@@ -861,6 +866,7 @@ uint64 SkinnedModel::GetMemoryUsage() const
void SkinnedModel::SetupMaterialSlots(int32 slotsCount)
{
PROFILE_MEM(GraphicsMeshes);
ModelBase::SetupMaterialSlots(slotsCount);
// Adjust meshes indices for slots
@@ -954,6 +960,7 @@ Asset::LoadResult SkinnedModel::load()
if (chunk0 == nullptr || chunk0->IsMissing())
return LoadResult::MissingDataChunk;
MemoryReadStream headerStream(chunk0->Get(), chunk0->Size());
PROFILE_MEM(GraphicsMeshes);
// Load asset data (anything but mesh contents that use streaming)
byte headerVersion;

View File

@@ -18,6 +18,7 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Serialization/JsonWriter.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Utilities/StringConverter.h"
#include "Engine/Threading/MainThreadTask.h"
#include "Engine/Level/SceneObject.h"
@@ -37,10 +38,12 @@ namespace
void PrintStack(LogType type)
{
#if LOG_ENABLE
const String stack = VisualScripting::GetStackTrace();
Log::Logger::Write(type, TEXT("Visual Script stack trace:"));
Log::Logger::Write(type, stack);
Log::Logger::Write(type, TEXT(""));
#endif
}
bool SerializeValue(const Variant& a, const Variant& b)
@@ -1340,6 +1343,8 @@ bool VisualScript::Save(const StringView& path)
Asset::LoadResult VisualScript::load()
{
PROFILE_MEM(ScriptingVisual);
// Build Visual Script typename that is based on asset id
String typeName = _id.ToString();
StringUtils::ConvertUTF162ANSI(typeName.Get(), _typenameChars, 32);
@@ -1532,6 +1537,7 @@ Asset::LoadResult VisualScript::load()
void VisualScript::unload(bool isReloading)
{
PROFILE_MEM(ScriptingVisual);
#if USE_EDITOR
if (isReloading)
{
@@ -1588,6 +1594,7 @@ AssetChunksFlag VisualScript::getChunksToPreload() const
void VisualScript::CacheScriptingType()
{
PROFILE_MEM(ScriptingVisual);
ScopeLock lock(VisualScriptingBinaryModule::Locker);
auto& binaryModule = VisualScriptingModule;
@@ -1723,6 +1730,7 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri
VisualScript* visualScript = VisualScriptingModule.Scripts[params.Type.TypeIndex];
// Initialize instance data
PROFILE_MEM(ScriptingVisual);
ScopeLock lock(visualScript->Locker);
auto& instanceParams = visualScript->_instances[object->GetID()].Params;
instanceParams.Resize(visualScript->Graph.Parameters.Count());
@@ -1747,6 +1755,8 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri
void VisualScriptingBinaryModule::OnScriptsReloading()
{
PROFILE_MEM(ScriptingVisual);
// Clear any cached types from that module across all loaded Visual Scripts
for (auto& script : Scripts)
{
@@ -1795,6 +1805,7 @@ void VisualScriptingBinaryModule::OnScriptsReloading()
void VisualScriptingBinaryModule::OnEvent(ScriptingObject* object, Span<Variant> parameters, ScriptingTypeHandle eventType, StringView eventName)
{
PROFILE_MEM(ScriptingVisual);
if (object)
{
// Object event
@@ -1900,9 +1911,13 @@ bool VisualScriptingBinaryModule::InvokeMethod(void* method, const Variant& inst
if (!instanceObject || instanceObject->GetTypeHandle() != vsMethod->Script->GetScriptingType())
{
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());
}
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));
}
return true;
}
}
@@ -1952,6 +1967,7 @@ bool VisualScriptingBinaryModule::GetFieldValue(void* field, const Variant& inst
bool VisualScriptingBinaryModule::SetFieldValue(void* field, const Variant& instance, Variant& value)
{
PROFILE_MEM(ScriptingVisual);
const auto vsFiled = (VisualScript::Field*)field;
const auto instanceObject = (ScriptingObject*)instance;
if (!instanceObject)
@@ -2038,6 +2054,7 @@ void VisualScriptingBinaryModule::SerializeObject(JsonWriter& stream, ScriptingO
void VisualScriptingBinaryModule::DeserializeObject(ISerializable::DeserializeStream& stream, ScriptingObject* object, ISerializeModifier* modifier)
{
PROFILE_MEM(ScriptingVisual);
ASSERT(stream.IsObject());
Locker.Lock();
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)
{
PROFILE_MEM(ScriptingVisual);
CHECK(instance);
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)
{
PROFILE_MEM(ScriptingVisual);
CHECK(instance);
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)
{
PROFILE_MEM(ScriptingVisual);
CHECK_RETURN(method && method->Script->IsLoaded(), Variant::Zero);
PROFILE_CPU_SRC_LOC(method->ProfilerData);
@@ -2419,6 +2439,7 @@ bool VisualScripting::Evaluate(VisualScript* script, ScriptingObject* instance,
const auto box = node->GetBox(boxId);
if (!box)
return false;
PROFILE_MEM(ScriptingVisual);
// Add to the calling stack
ScopeContext scope;

View File

@@ -10,6 +10,7 @@
#include "Engine/Serialization/JsonTools.h"
#include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Engine/Threading/ThreadPoolTask.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if USE_EDITOR
#include "Engine/Platform/FileSystem.h"
#include "Engine/Threading/Threading.h"
@@ -527,6 +528,7 @@ protected:
auto storage = ref->Storage;
auto factory = (BinaryAssetFactoryBase*)Content::GetAssetFactory(ref->GetTypeName());
ASSERT(factory);
PROFILE_MEM(ContentAssets);
// Here we should open storage and extract AssetInitData
// This would also allow to convert/upgrade data

View File

@@ -28,6 +28,7 @@
#include "Engine/Engine/Globals.h"
#include "Engine/Level/Types.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/Scripting.h"
#if USE_EDITOR
@@ -117,6 +118,8 @@ ContentService ContentServiceInstance;
bool ContentService::Init()
{
PROFILE_MEM(Content);
// Load assets registry
Cache.Init();
@@ -159,6 +162,7 @@ void ContentService::Update()
void ContentService::LateUpdate()
{
PROFILE_CPU();
PROFILE_MEM(Content);
// Check if need to perform an update of unloading assets
const TimeSpan timeNow = Time::Update.UnscaledTime;
@@ -324,6 +328,7 @@ String LoadingThread::ToString() const
int32 LoadingThread::Run()
{
PROFILE_MEM(Content);
#if USE_EDITOR && PLATFORM_WINDOWS
// Initialize COM
// 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))
return true;
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)
ScopeLock lock(WorkspaceDiscoveryLocker);
@@ -465,6 +471,7 @@ bool Content::GetAssetInfo(const StringView& path, AssetInfo& info)
if (!FileSystem::FileExists(path))
return false;
PROFILE_CPU();
PROFILE_MEM(Content);
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)
{
PROFILE_MEM(Content);
#if USE_EDITOR
const String path = Globals::EngineContentFolder / internalPath + ASSET_FILES_EXTENSION_WITH_DOT;
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)
{
PROFILE_MEM(Content);
// Ensure path is in a valid format
String pathNorm(path);
ContentStorageManager::FormatPath(pathNorm);
@@ -687,7 +697,6 @@ Asset* Content::GetAsset(const StringView& outputPath)
{
if (outputPath.IsEmpty())
return nullptr;
ScopeLock lock(AssetsLocker);
for (auto i = Assets.Begin(); i.IsNotEnd(); ++i)
{
@@ -791,6 +800,23 @@ void Content::deleteFileSafety(const StringView& path, const Guid& id)
#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
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)
{
PROFILE_CPU();
PROFILE_MEM(Content);
auto& assetType = type.GetType();
// Init mock asset info
@@ -1045,7 +1072,9 @@ Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type)
}
// Create asset object
PROFILE_MEM_BEGIN(ContentAssets);
auto asset = factory->NewVirtual(info);
PROFILE_MEM_END();
if (asset == nullptr)
{
LOG(Error, "Cannot create virtual asset object.");
@@ -1054,7 +1083,9 @@ Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type)
asset->RegisterObject();
// Call initializer function
PROFILE_MEM_BEGIN(ContentAssets);
asset->InitAsVirtual();
PROFILE_MEM_END();
// Register asset
AssetsLocker.Lock();
@@ -1097,6 +1128,8 @@ void Content::WaitForTask(ContentLoadTask* loadingTask, double timeoutInMillisec
localQueue.Clear();
}
PROFILE_CPU_NAMED("Inline");
ZoneColor(0xffaaaaaa);
thread->Run(tmp);
}
else
@@ -1209,6 +1242,7 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
{
if (!id.IsValid())
return nullptr;
PROFILE_MEM(Content);
// Check if asset has been already loaded
Asset* result = nullptr;
@@ -1277,7 +1311,9 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
}
// Create asset object
PROFILE_MEM_BEGIN(ContentAssets);
result = factory->New(assetInfo);
PROFILE_MEM_END();
if (result == nullptr)
{
LOG(Error, "Cannot create asset object. Info: {0}", assetInfo.ToString());

View File

@@ -1,5 +1,6 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using FlaxEngine.Interop;
using System;
using System.Runtime.CompilerServices;
@@ -7,6 +8,33 @@ namespace FlaxEngine
{
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>
/// 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>

View File

@@ -122,7 +122,7 @@ public:
/// Gets the assets (loaded or during load).
/// </summary>
/// <returns>The collection of assets.</returns>
API_PROPERTY() static Array<Asset*, HeapAllocation> GetAssets();
static Array<Asset*, HeapAllocation> GetAssets();
/// <summary>
/// Gets the raw dictionary of assets (loaded or during load).
@@ -368,4 +368,9 @@ private:
static void onAssetUnload(Asset* asset);
static void onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId);
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/Debug/Exceptions/JsonParseException.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/ManagedCLR/MField.h"
@@ -39,6 +40,7 @@ String JsonAssetBase::GetData() const
if (Data == nullptr)
return String::Empty;
PROFILE_CPU_NAMED("JsonAsset.GetData");
PROFILE_MEM(ContentAssets);
rapidjson_flax::StringBuffer buffer;
OnGetData(buffer);
return String((const char*)buffer.GetString(), (int32)buffer.GetSize());
@@ -49,6 +51,7 @@ void JsonAssetBase::SetData(const StringView& value)
if (!IsLoaded())
return;
PROFILE_CPU_NAMED("JsonAsset.SetData");
PROFILE_MEM(ContentAssets);
const StringAnsi dataJson(value);
ScopeLock lock(Locker);
const StringView dataTypeName = DataTypeName;
@@ -60,6 +63,7 @@ void JsonAssetBase::SetData(const StringView& value)
bool JsonAssetBase::Init(const StringView& dataTypeName, const StringAnsiView& dataJson)
{
PROFILE_MEM(ContentAssets);
unload(true);
DataTypeName = dataTypeName;
DataEngineBuild = FLAXENGINE_VERSION_BUILD;
@@ -239,6 +243,7 @@ Asset::LoadResult JsonAssetBase::loadAsset()
{
if (IsVirtual() || _isVirtualDocument)
return LoadResult::Ok;
PROFILE_MEM(ContentAssets);
// Load data (raw json file in editor, cooked asset in build game)
#if USE_EDITOR
@@ -305,6 +310,7 @@ Asset::LoadResult JsonAssetBase::loadAsset()
void JsonAssetBase::unload(bool isReloading)
{
PROFILE_MEM(ContentAssets);
ISerializable::SerializeDocument tmp;
Document.Swap(tmp);
Data = nullptr;
@@ -453,6 +459,7 @@ bool JsonAsset::CreateInstance()
ScopeLock lock(Locker);
if (Instance)
return false;
PROFILE_MEM(ContentAssets);
// Try to scripting type for this data
const StringAsANSI<> dataTypeNameAnsi(DataTypeName.Get(), DataTypeName.Length());

View File

@@ -19,6 +19,15 @@ API_STRUCT(NoDefault, Template, MarshalAs=JsonAsset*) struct JsonAssetReference
OnSet(asset);
}
explicit JsonAssetReference(decltype(__nullptr))
{
}
explicit JsonAssetReference(IAssetReference* owner)
: AssetReference<JsonAsset>(owner)
{
}
/// <summary>
/// Gets the deserialized native object instance of the given type. Returns null if asset is not loaded or loaded object has different type.
/// </summary>

View File

@@ -8,6 +8,7 @@
#include "Engine/Content/WeakAssetReference.h"
#include "Engine/Core/Log.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
/// <summary>
/// Asset loading task object.
@@ -44,6 +45,7 @@ protected:
Result run() override
{
PROFILE_CPU();
PROFILE_MEM(ContentAssets);
// Keep valid ref to the asset
AssetReference<::Asset> ref = Asset.Get();

View File

@@ -7,7 +7,7 @@
/// <summary>
/// The asset soft reference. Asset gets referenced (loaded) on actual use (ID reference is resolving it).
/// </summary>
class FLAXENGINE_API SoftAssetReferenceBase
class FLAXENGINE_API SoftAssetReferenceBase : public IAssetReference
{
protected:
Asset* _asset = nullptr;
@@ -46,11 +46,16 @@ public:
/// </summary>
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:
void OnSet(Asset* asset);
void OnSet(const Guid& id);
void OnResolve(const ScriptingTypeHandle& type);
void OnUnloaded(Asset* asset);
};
/// <summary>
@@ -71,6 +76,13 @@ public:
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SoftAssetReference"/> class.
/// </summary>
explicit SoftAssetReference(decltype(__nullptr))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SoftAssetReference"/> class.
/// </summary>

View File

@@ -182,10 +182,5 @@ public:
/// Clones this chunk data (doesn't copy location in file).
/// </summary>
/// <returns>The cloned chunk.</returns>
FlaxChunk* Clone() const
{
auto chunk = New<FlaxChunk>();
chunk->Data.Copy(Data);
return chunk;
}
FlaxChunk* Clone() const;
};

View File

@@ -8,6 +8,7 @@
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Platform/File.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Serialization/FileWriteStream.h"
#include "Engine/Content/Asset.h"
#include "Engine/Content/Content.h"
@@ -63,6 +64,14 @@ void FlaxChunk::RegisterUsage()
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;
FlaxStorage::LockData FlaxStorage::LockData::Invalid(nullptr);
@@ -281,19 +290,12 @@ uint32 FlaxStorage::GetMemoryUsage() const
bool FlaxStorage::Load()
{
// Check if was already loaded
if (IsLoaded())
{
return false;
}
// Prevent loading by more than one thread
PROFILE_MEM(ContentFiles);
ScopeLock lock(_loadLocker);
if (IsLoaded())
{
// Other thread loaded it
return false;
}
ASSERT(GetEntriesCount() == 0);
// Open file
@@ -693,6 +695,7 @@ bool FlaxStorage::LoadAssetHeader(const Guid& id, AssetInitData& data)
bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk)
{
PROFILE_MEM(ContentFiles);
ASSERT(IsLoaded());
ASSERT(chunk != nullptr && _chunks.Contains(chunk));
@@ -866,6 +869,7 @@ FlaxChunk* FlaxStorage::AllocateChunk()
{
if (AllowDataModifications())
{
PROFILE_MEM(ContentFiles);
auto chunk = New<FlaxChunk>();
_chunks.Add(chunk);
return chunk;
@@ -1125,6 +1129,7 @@ bool FlaxStorage::Save(const AssetInitData& data, bool silentMode)
bool FlaxStorage::LoadAssetHeader(const Entry& e, AssetInitData& data)
{
PROFILE_MEM(ContentFiles);
ASSERT(IsLoaded());
auto lock = Lock();
@@ -1396,6 +1401,8 @@ FileReadStream* FlaxStorage::OpenFile()
auto& stream = _file.Get();
if (stream == nullptr)
{
PROFILE_MEM(ContentFiles);
// Open file
auto file = File::Open(_path, FileMode::OpenExisting, FileAccess::Read, FileShare::Read);
if (file == nullptr)
@@ -1418,6 +1425,7 @@ bool FlaxStorage::CloseFileHandles()
return false;
}
PROFILE_CPU();
PROFILE_MEM(ContentFiles);
// 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

View File

@@ -7,7 +7,7 @@
/// <summary>
/// Asset reference utility that doesn't add reference to that asset. Handles asset unload event.
/// </summary>
API_CLASS(InBuild) class WeakAssetReferenceBase
API_CLASS(InBuild) class WeakAssetReferenceBase : public IAssetReference
{
public:
typedef Delegate<> EventType;
@@ -56,9 +56,14 @@ public:
/// </summary>
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:
void OnSet(Asset* asset);
void OnUnloaded(Asset* asset);
};
/// <summary>
@@ -72,7 +77,13 @@ public:
/// Initializes a new instance of the <see cref="WeakAssetReference"/> class.
/// </summary>
WeakAssetReference()
: WeakAssetReferenceBase()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WeakAssetReference"/> class.
/// </summary>
explicit WeakAssetReference(decltype(__nullptr))
{
}
@@ -81,7 +92,6 @@ public:
/// </summary>
/// <param name="asset">The asset to set.</param>
WeakAssetReference(T* asset)
: WeakAssetReferenceBase()
{
OnSet(asset);
}

View File

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

View File

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

View File

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

View File

@@ -20,6 +20,7 @@ API_CLASS(InBuild) class Array
public:
using ItemType = T;
using AllocationData = typename AllocationType::template Data<T>;
using AllocationTag = typename AllocationType::Tag;
private:
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>
/// Initializes <see cref="Array"/> by reserving space.
/// </summary>

View File

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

View File

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

View File

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

View File

@@ -7,27 +7,6 @@
#include "Engine/Core/Types/String.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>
/// The log message types.
/// </summary>
@@ -54,6 +33,31 @@ API_ENUM() enum class LogType
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);
namespace Log
@@ -186,3 +190,12 @@ namespace Log
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)
{
#if LOG_ENABLE
auto& stack = GlobalLogContexts.Get();
if (stack.Count == 0)
return;
@@ -102,6 +103,7 @@ void LogContext::Print(LogType verbosity)
// Print message
Log::Logger::Write(verbosity, msg.ToStringView());
}
#endif
}
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++;
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>
@@ -46,6 +57,7 @@ class FixedAllocation
{
public:
enum { HasSwap = false };
typedef void* Tag;
template<typename T>
class alignas(sizeof(void*)) Data
@@ -58,6 +70,10 @@ public:
{
}
FORCE_INLINE Data(Tag tag)
{
}
FORCE_INLINE ~Data()
{
}
@@ -106,6 +122,7 @@ class HeapAllocation
{
public:
enum { HasSwap = true };
typedef void* Tag;
template<typename T>
class Data
@@ -118,6 +135,10 @@ public:
{
}
FORCE_INLINE Data(Tag tag)
{
}
FORCE_INLINE ~Data()
{
Allocator::Free(_data);
@@ -135,13 +156,7 @@ public:
FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, const int32 minCapacity) const
{
if (capacity < minCapacity)
capacity = minCapacity;
if (capacity < 8)
capacity = 8;
else
capacity = AllocationUtils::RoundUpToPowerOf2(capacity);
return capacity;
return AllocationUtils::CalculateCapacityGrow(capacity, minCapacity);
}
FORCE_INLINE void Allocate(const int32 capacity)
@@ -184,6 +199,7 @@ class InlinedAllocation
{
public:
enum { HasSwap = false };
typedef void* Tag;
template<typename T>
class alignas(sizeof(void*)) Data
@@ -200,6 +216,10 @@ public:
{
}
FORCE_INLINE Data(Tag tag)
{
}
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:
enum { HasSwap = true };
typedef void* Tag;
template<typename T>
class Data
@@ -23,6 +24,10 @@ public:
{
}
FORCE_INLINE Data(Tag tag)
{
}
FORCE_INLINE ~Data()
{
if (_data)

View File

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

View File

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

View File

@@ -208,6 +208,14 @@ public:
return StringUtils::CompareIgnoreCase(&(*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>

View File

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

View File

@@ -215,7 +215,7 @@ namespace
{
if (!method->IsStatic())
continue;
const StringAnsi& name = method->GetName();
const StringAnsiView name = method->GetName();
if (name.Contains("Internal_") ||
mclass->GetFullName().Contains(".Interop."))
continue;
@@ -438,6 +438,8 @@ void DebugCommands::InitAsync()
DebugCommands::CommandFlags DebugCommands::GetCommandFlags(StringView command)
{
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)
String commandCopy = command;
command = commandCopy;

View File

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

View File

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

View File

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

View File

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

View File

@@ -195,14 +195,19 @@ namespace FlaxEngine.Interop
/// </summary>
/// <typeparam name="T">Array element type.</typeparam>
/// <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>
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>();
T[] managedArray = new T[ptrArray.Length];
for (int i = 0; i < managedArray.Length; i++)
managedArray[i] = span[i] != IntPtr.Zero ? (T)ManagedHandle.FromIntPtr(span[i]).Target : default;
return managedArray;
if (buffer == null || buffer.Length < ptrArray.Length)
buffer = new T[ptrArray.Length];
for (int i = 0; i < ptrArray.Length; i++)
{
IntPtr ptr = span[i];
buffer[i] = ptr != IntPtr.Zero ? (T)ManagedHandle.FromIntPtr(ptr).Target : default;
}
return buffer;
}
/// <summary>

View File

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

View File

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

View File

@@ -15,6 +15,7 @@
#include "Engine/Debug/Exceptions/ArgumentNullException.h"
#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Enums.h"
#include "Engine/Threading/ThreadPoolTask.h"
#include "Engine/Threading/Threading.h"
@@ -188,6 +189,8 @@ bool GPUBuffer::IsDynamic() const
bool GPUBuffer::Init(const GPUBufferDescription& desc)
{
PROFILE_MEM(GraphicsBuffers);
// Validate description
#if !BUILD_RELEASE
#define GET_NAME() GetName()
@@ -242,6 +245,15 @@ bool GPUBuffer::Init(const GPUBufferDescription& desc)
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;
}
@@ -476,6 +488,15 @@ GPUResourceType GPUBuffer::GetResourceType() const
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();
_isLocked = false;
}

View File

@@ -1,9 +1,41 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using System.Runtime.CompilerServices;
using FlaxEngine.Interop;
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>
{
/// <summary>

View File

@@ -648,8 +648,26 @@ GPUTasksExecutor* GPUDevice::CreateTasksExecutor()
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()
{
PROFILE_MEM(Graphics);
DrawBegin();
auto context = GetMainContext();

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,7 +25,7 @@ DrawPass ForwardMaterialShader::GetDrawModes() const
bool ForwardMaterialShader::CanUseInstancing(InstancingHandler& handler) const
{
handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, };
return true;
return false; // TODO: support instancing when using ForwardShadingFeature
}
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.
/// </summary>
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>

View File

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

View File

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

View File

@@ -14,6 +14,7 @@
#include "Engine/Renderer/RenderList.h"
#include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if USE_EDITOR
#include "Engine/Renderer/GBufferPass.h"
#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)
{
PROFILE_MEM(GraphicsMeshes);
auto model = mesh->GetModelBase();
CHECK_RETURN(model && model->IsVirtual(), 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)
{
PROFILE_MEM(GraphicsMeshes);
Release();
// Setup GPU resources

View File

@@ -16,6 +16,7 @@
#include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Serialization/MemoryReadStream.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.");

View File

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

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