Optimize asset references to support direct registration to reduce Delegate memory allocations and overhead

This commit is contained in:
Wojtek Figat
2025-06-08 00:58:15 +02:00
parent bffb175a9b
commit 73c30d3d89
26 changed files with 365 additions and 141 deletions

View File

@@ -73,9 +73,9 @@ void BehaviorService::Dispose()
Behavior::Behavior(const SpawnParams& params) Behavior::Behavior(const SpawnParams& params)
: Script(params) : Script(params)
, Tree(this)
{ {
_knowledge.Behavior = this; _knowledge.Behavior = this;
Tree.Changed.Bind<Behavior, &Behavior::ResetLogic>(this);
} }
void Behavior::UpdateAsync() void Behavior::UpdateAsync()
@@ -175,6 +175,19 @@ void Behavior::OnDisable()
BehaviorServiceInstance.UpdateList.Remove(this); BehaviorServiceInstance.UpdateList.Remove(this);
} }
void Behavior::OnAssetChanged(Asset* asset, void* caller)
{
ResetLogic();
}
void Behavior::OnAssetLoaded(Asset* asset, void* caller)
{
}
void Behavior::OnAssetUnloaded(Asset* asset, void* caller)
{
}
#if USE_EDITOR #if USE_EDITOR
bool Behavior::GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior) bool Behavior::GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior)

View File

@@ -11,7 +11,7 @@
/// <summary> /// <summary>
/// Behavior instance script that runs Behavior Tree execution. /// Behavior instance script that runs Behavior Tree execution.
/// </summary> /// </summary>
API_CLASS(Attributes="Category(\"Flax Engine\")") class FLAXENGINE_API Behavior : public Script API_CLASS(Attributes="Category(\"Flax Engine\")") class FLAXENGINE_API Behavior : public Script, private IAssetReference
{ {
API_AUTO_SERIALIZATION(); API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE(Behavior); DECLARE_SCRIPTING_TYPE(Behavior);
@@ -92,6 +92,11 @@ public:
void OnDisable() override; void OnDisable() override;
private: private:
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
#if USE_EDITOR #if USE_EDITOR
// Editor-only utilities to debug nodes state. // Editor-only utilities to debug nodes state.
API_FUNCTION(Internal) static bool GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior); API_FUNCTION(Internal) static bool GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior);

View File

@@ -21,9 +21,8 @@ AudioSource::AudioSource(const SpawnParams& params)
, _playOnStart(false) , _playOnStart(false)
, _startTime(0.0f) , _startTime(0.0f)
, _allowSpatialization(true) , _allowSpatialization(true)
, Clip(this)
{ {
Clip.Changed.Bind<AudioSource, &AudioSource::OnClipChanged>(this);
Clip.Loaded.Bind<AudioSource, &AudioSource::OnClipLoaded>(this);
} }
void AudioSource::SetVolume(float value) void AudioSource::SetVolume(float value)
@@ -264,7 +263,7 @@ void AudioSource::RequestStreamingBuffersUpdate()
_needToUpdateStreamingBuffers = true; _needToUpdateStreamingBuffers = true;
} }
void AudioSource::OnClipChanged() void AudioSource::OnAssetChanged(Asset* asset, void* caller)
{ {
Stop(); Stop();
@@ -276,7 +275,7 @@ void AudioSource::OnClipChanged()
} }
} }
void AudioSource::OnClipLoaded() void AudioSource::OnAssetLoaded(Asset* asset, void* caller)
{ {
if (!SourceID) if (!SourceID)
return; return;
@@ -302,6 +301,10 @@ void AudioSource::OnClipLoaded()
} }
} }
void AudioSource::OnAssetUnloaded(Asset* asset, void* caller)
{
}
bool AudioSource::UseStreaming() const bool AudioSource::UseStreaming() const
{ {
if (Clip == nullptr || Clip->WaitForLoaded()) if (Clip == nullptr || Clip->WaitForLoaded())

View File

@@ -13,7 +13,7 @@
/// Whether or not an audio source is spatial is controlled by the assigned AudioClip.The volume and the pitch of a spatial audio source is controlled by its position and the AudioListener's position/direction/velocity. /// Whether or not an audio source is spatial is controlled by the assigned AudioClip.The volume and the pitch of a spatial audio source is controlled by its position and the AudioListener's position/direction/velocity.
/// </remarks> /// </remarks>
API_CLASS(Attributes="ActorContextMenu(\"New/Audio/Audio Source\"), ActorToolbox(\"Other\")") API_CLASS(Attributes="ActorContextMenu(\"New/Audio/Audio Source\"), ActorToolbox(\"Other\")")
class FLAXENGINE_API AudioSource : public Actor class FLAXENGINE_API AudioSource : public Actor, IAssetReference
{ {
DECLARE_SCENE_OBJECT(AudioSource); DECLARE_SCENE_OBJECT(AudioSource);
friend class AudioStreamingHandler; friend class AudioStreamingHandler;
@@ -293,8 +293,10 @@ public:
void RequestStreamingBuffersUpdate(); void RequestStreamingBuffersUpdate();
private: private:
void OnClipChanged(); // [IAssetReference]
void OnClipLoaded(); void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
/// <summary> /// <summary>
/// Plays the audio source. Should have buffer(s) binded before. /// Plays the audio source. Should have buffer(s) binded before.

View File

@@ -9,6 +9,7 @@
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/LogContext.h" #include "Engine/Core/LogContext.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/ManagedCLR/MCore.h" #include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Threading/MainThreadTask.h" #include "Engine/Threading/MainThreadTask.h"
#include "Engine/Threading/ThreadLocal.h" #include "Engine/Threading/ThreadLocal.h"
@@ -34,15 +35,18 @@ bool ContentDeprecated::Clear(bool newValue)
#endif #endif
AssetReferenceBase::AssetReferenceBase(IAssetReference* owner)
: _owner(owner)
{
}
AssetReferenceBase::~AssetReferenceBase() AssetReferenceBase::~AssetReferenceBase()
{ {
Asset* asset = _asset; Asset* asset = _asset;
if (asset) if (asset)
{ {
_asset = nullptr; _asset = nullptr;
asset->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this); asset->RemoveReference(this);
asset->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
asset->RemoveReference();
} }
} }
@@ -51,52 +55,60 @@ String AssetReferenceBase::ToString() const
return _asset ? _asset->ToString() : TEXT("<null>"); return _asset ? _asset->ToString() : TEXT("<null>");
} }
void AssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
{
if (_owner)
_owner->OnAssetChanged(asset, this);
}
void AssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
Loaded();
if (_owner)
_owner->OnAssetLoaded(asset, this);
}
void AssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
Unload();
OnSet(nullptr);
if (_owner)
_owner->OnAssetUnloaded(asset, this);
}
void AssetReferenceBase::OnSet(Asset* asset) void AssetReferenceBase::OnSet(Asset* asset)
{ {
auto e = _asset; auto e = _asset;
if (e != asset) if (e != asset)
{ {
if (e) if (e)
{ e->RemoveReference(this);
e->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
e->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
e->RemoveReference();
}
_asset = e = asset; _asset = e = asset;
if (e) if (e)
{ e->AddReference(this);
e->AddReference();
e->OnLoaded.Bind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
e->OnUnloaded.Bind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
}
Changed(); Changed();
if (_owner)
_owner->OnAssetChanged(asset, this);
if (e && e->IsLoaded()) if (e && e->IsLoaded())
{
Loaded(); Loaded();
if (_owner)
_owner->OnAssetLoaded(asset, this);
}
} }
} }
void AssetReferenceBase::OnLoaded(Asset* asset)
{
if (_asset != asset)
return;
Loaded();
}
void AssetReferenceBase::OnUnloaded(Asset* asset)
{
if (_asset != asset)
return;
Unload();
OnSet(nullptr);
}
WeakAssetReferenceBase::~WeakAssetReferenceBase() WeakAssetReferenceBase::~WeakAssetReferenceBase()
{ {
Asset* asset = _asset; Asset* asset = _asset;
if (asset) if (asset)
{ {
_asset = nullptr; _asset = nullptr;
asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this); asset->RemoveReference(this, true);
} }
} }
@@ -105,36 +117,43 @@ String WeakAssetReferenceBase::ToString() const
return _asset ? _asset->ToString() : TEXT("<null>"); return _asset ? _asset->ToString() : TEXT("<null>");
} }
void WeakAssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
{
}
void WeakAssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
{
}
void WeakAssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
Unload();
asset->RemoveReference(this, true);
_asset = nullptr;
}
void WeakAssetReferenceBase::OnSet(Asset* asset) void WeakAssetReferenceBase::OnSet(Asset* asset)
{ {
auto e = _asset; auto e = _asset;
if (e != asset) if (e != asset)
{ {
if (e) if (e)
e->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this); e->RemoveReference(this, true);
_asset = e = asset; _asset = e = asset;
if (e) if (e)
e->OnUnloaded.Bind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this); e->AddReference(this, true);
} }
} }
void WeakAssetReferenceBase::OnUnloaded(Asset* asset)
{
if (_asset != asset)
return;
Unload();
asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
_asset = nullptr;
}
SoftAssetReferenceBase::~SoftAssetReferenceBase() SoftAssetReferenceBase::~SoftAssetReferenceBase()
{ {
Asset* asset = _asset; Asset* asset = _asset;
if (asset) if (asset)
{ {
_asset = nullptr; _asset = nullptr;
asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this); asset->RemoveReference(this);
asset->RemoveReference();
} }
#if !BUILD_RELEASE #if !BUILD_RELEASE
_id = Guid::Empty; _id = Guid::Empty;
@@ -146,22 +165,34 @@ String SoftAssetReferenceBase::ToString() const
return _asset ? _asset->ToString() : (_id.IsValid() ? _id.ToString() : TEXT("<null>")); return _asset ? _asset->ToString() : (_id.IsValid() ? _id.ToString() : TEXT("<null>"));
} }
void SoftAssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
{
}
void SoftAssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
{
}
void SoftAssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
_asset->RemoveReference(this);
_asset = nullptr;
_id = Guid::Empty;
Changed();
}
void SoftAssetReferenceBase::OnSet(Asset* asset) void SoftAssetReferenceBase::OnSet(Asset* asset)
{ {
if (_asset == asset) if (_asset == asset)
return; return;
if (_asset) if (_asset)
{ _asset->RemoveReference(this);
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->RemoveReference();
}
_asset = asset; _asset = asset;
_id = asset ? asset->GetID() : Guid::Empty; _id = asset ? asset->GetID() : Guid::Empty;
if (asset) if (asset)
{ asset->AddReference(this);
asset->AddReference();
asset->OnUnloaded.Bind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
}
Changed(); Changed();
} }
@@ -170,10 +201,7 @@ void SoftAssetReferenceBase::OnSet(const Guid& id)
if (_id == id) if (_id == id)
return; return;
if (_asset) if (_asset)
{ _asset->RemoveReference(this);
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->RemoveReference();
}
_asset = nullptr; _asset = nullptr;
_id = id; _id = id;
Changed(); Changed();
@@ -184,21 +212,7 @@ void SoftAssetReferenceBase::OnResolve(const ScriptingTypeHandle& type)
ASSERT(!_asset); ASSERT(!_asset);
_asset = ::LoadAsset(_id, type); _asset = ::LoadAsset(_id, type);
if (_asset) if (_asset)
{ _asset->AddReference(this);
_asset->OnUnloaded.Bind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->AddReference();
}
}
void SoftAssetReferenceBase::OnUnloaded(Asset* asset)
{
if (_asset != asset)
return;
_asset->RemoveReference();
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset = nullptr;
_id = Guid::Empty;
Changed();
} }
Asset::Asset(const SpawnParams& params, const AssetInfo* info) Asset::Asset(const SpawnParams& params, const AssetInfo* info)
@@ -216,6 +230,41 @@ int32 Asset::GetReferencesCount() const
return (int32)Platform::AtomicRead(const_cast<int64 volatile*>(&_refCount)); return (int32)Platform::AtomicRead(const_cast<int64 volatile*>(&_refCount));
} }
void Asset::AddReference()
{
Platform::InterlockedIncrement(&_refCount);
}
void Asset::AddReference(IAssetReference* ref, bool week)
{
if (!week)
Platform::InterlockedIncrement(&_refCount);
if (ref)
{
//PROFILE_MEM(EngineDelegate); // Include references tracking memory within Delegate memory
Locker.Lock();
_references.Add(ref);
Locker.Unlock();
}
}
void Asset::RemoveReference()
{
Platform::InterlockedDecrement(&_refCount);
}
void Asset::RemoveReference(IAssetReference* ref, bool week)
{
if (ref)
{
Locker.Lock();
_references.Remove(ref);
Locker.Unlock();
}
if (!week)
Platform::InterlockedDecrement(&_refCount);
}
String Asset::ToString() const String Asset::ToString() const
{ {
return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath()); return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath());
@@ -354,6 +403,7 @@ uint64 Asset::GetMemoryUsage() const
if (Platform::AtomicRead(&_loadingTask)) if (Platform::AtomicRead(&_loadingTask))
result += sizeof(ContentLoadTask); result += sizeof(ContentLoadTask);
result += (OnLoaded.Capacity() + OnReloading.Capacity() + OnUnloaded.Capacity()) * sizeof(EventType::FunctionType); result += (OnLoaded.Capacity() + OnReloading.Capacity() + OnUnloaded.Capacity()) * sizeof(EventType::FunctionType);
result += _references.Capacity() * sizeof(HashSet<IAssetReference*>::Bucket);
Locker.Unlock(); Locker.Unlock();
return result; return result;
} }
@@ -628,6 +678,8 @@ void Asset::onLoaded_MainThread()
ASSERT(IsInMainThread()); ASSERT(IsInMainThread());
// Send event // Send event
for (const auto& e : _references)
e.Item->OnAssetLoaded(this, this);
OnLoaded(this); OnLoaded(this);
} }
@@ -641,6 +693,8 @@ void Asset::onUnload_MainThread()
CancelStreaming(); CancelStreaming();
// Send event // Send event
for (const auto& e : _references)
e.Item->OnAssetUnloaded(this, this);
OnUnloaded(this); OnUnloaded(this);
} }

View File

@@ -18,6 +18,18 @@
public: \ public: \
explicit type(const SpawnParams& params, const AssetInfo* info) explicit type(const SpawnParams& params, const AssetInfo* info)
// Utility interface for objects that reference asset and want to get notified about asset reference changes.
class FLAXENGINE_API IAssetReference
{
public:
// Asset reference got changed.
virtual void OnAssetChanged(Asset* asset, void* caller) = 0;
// Asset got loaded.
virtual void OnAssetLoaded(Asset* asset, void* caller) = 0;
// Asset gets unloaded.
virtual void OnAssetUnloaded(Asset* asset, void* caller) = 0;
};
/// <summary> /// <summary>
/// Asset objects base class. /// Asset objects base class.
/// </summary> /// </summary>
@@ -48,6 +60,8 @@ protected:
int8 _deleteFileOnUnload : 1; // Indicates that asset source file should be removed on asset unload int8 _deleteFileOnUnload : 1; // Indicates that asset source file should be removed on asset unload
int8 _isVirtual : 1; // Indicates that asset is pure virtual (generated or temporary, has no storage so won't be saved) int8 _isVirtual : 1; // Indicates that asset is pure virtual (generated or temporary, has no storage so won't be saved)
HashSet<IAssetReference*> _references;
public: public:
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Asset"/> class. /// Initializes a new instance of the <see cref="Asset"/> class.
@@ -88,18 +102,22 @@ public:
/// <summary> /// <summary>
/// Adds reference to that asset. /// Adds reference to that asset.
/// </summary> /// </summary>
FORCE_INLINE void AddReference() void AddReference();
{
Platform::InterlockedIncrement(&_refCount); /// <summary>
} /// Adds reference to that asset.
/// </summary>
void AddReference(IAssetReference* ref, bool week = false);
/// <summary> /// <summary>
/// Removes reference from that asset. /// Removes reference from that asset.
/// </summary> /// </summary>
FORCE_INLINE void RemoveReference() void RemoveReference();
{
Platform::InterlockedDecrement(&_refCount); /// <summary>
} /// Removes reference from that asset.
/// </summary>
void RemoveReference(IAssetReference* ref, bool week = false);
public: public:
/// <summary> /// <summary>

View File

@@ -7,10 +7,11 @@
/// <summary> /// <summary>
/// Asset reference utility. Keeps reference to the linked asset object and handles load/unload events. /// Asset reference utility. Keeps reference to the linked asset object and handles load/unload events.
/// </summary> /// </summary>
class FLAXENGINE_API AssetReferenceBase class FLAXENGINE_API AssetReferenceBase : public IAssetReference
{ {
protected: protected:
Asset* _asset = nullptr; Asset* _asset = nullptr;
IAssetReference* _owner = nullptr;
public: public:
/// <summary> /// <summary>
@@ -36,6 +37,12 @@ public:
/// </summary> /// </summary>
AssetReferenceBase() = default; AssetReferenceBase() = default;
/// <summary>
/// Initializes a new instance of the <see cref="AssetReferenceBase"/> class.
/// </summary>
/// <param name="owner">The reference owner to keep notified about asset changes.</param>
AssetReferenceBase(IAssetReference* owner);
/// <summary> /// <summary>
/// Finalizes an instance of the <see cref="AssetReferenceBase"/> class. /// Finalizes an instance of the <see cref="AssetReferenceBase"/> class.
/// </summary> /// </summary>
@@ -63,10 +70,14 @@ public:
/// </summary> /// </summary>
String ToString() const; String ToString() const;
public:
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
protected: protected:
void OnSet(Asset* asset); void OnSet(Asset* asset);
void OnLoaded(Asset* asset);
void OnUnloaded(Asset* asset);
}; };
/// <summary> /// <summary>
@@ -87,6 +98,13 @@ public:
{ {
} }
/// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary>
explicit AssetReference(decltype(__nullptr))
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class. /// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary> /// </summary>
@@ -96,6 +114,15 @@ public:
OnSet((Asset*)asset); OnSet((Asset*)asset);
} }
/// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary>
/// <param name="owner">The reference owner to keep notified about asset changes.</param>
explicit AssetReference(IAssetReference* owner)
: AssetReferenceBase(owner)
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class. /// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary> /// </summary>

View File

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

View File

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

View File

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

View File

@@ -27,16 +27,13 @@ AnimatedModel::AnimatedModel(const SpawnParams& params)
, _counter(0) , _counter(0)
, _lastMinDstSqr(MAX_Real) , _lastMinDstSqr(MAX_Real)
, _lastUpdateFrame(0) , _lastUpdateFrame(0)
, SkinnedModel(this)
, AnimationGraph(this)
{ {
_drawCategory = SceneRendering::SceneDrawAsync; _drawCategory = SceneRendering::SceneDrawAsync;
GraphInstance.Object = this; GraphInstance.Object = this;
_box = BoundingBox(Vector3::Zero); _box = BoundingBox(Vector3::Zero);
_sphere = BoundingSphere(Vector3::Zero, 0.0f); _sphere = BoundingSphere(Vector3::Zero, 0.0f);
SkinnedModel.Changed.Bind<AnimatedModel, &AnimatedModel::OnSkinnedModelChanged>(this);
SkinnedModel.Loaded.Bind<AnimatedModel, &AnimatedModel::OnSkinnedModelLoaded>(this);
AnimationGraph.Changed.Bind<AnimatedModel, &AnimatedModel::OnGraphChanged>(this);
AnimationGraph.Loaded.Bind<AnimatedModel, &AnimatedModel::OnGraphLoaded>(this);
} }
AnimatedModel::~AnimatedModel() AnimatedModel::~AnimatedModel()
@@ -889,6 +886,26 @@ void AnimatedModel::OnGraphLoaded()
SyncParameters(); SyncParameters();
} }
void AnimatedModel::OnAssetChanged(Asset* asset, void* caller)
{
if (caller == &SkinnedModel)
OnSkinnedModelChanged();
else if (caller == &AnimationGraph)
OnGraphChanged();
}
void AnimatedModel::OnAssetLoaded(Asset* asset, void* caller)
{
if (caller == &SkinnedModel)
OnSkinnedModelLoaded();
else if (caller == &AnimationGraph)
OnGraphLoaded();
}
void AnimatedModel::OnAssetUnloaded(Asset* asset, void* caller)
{
}
bool AnimatedModel::HasContentLoaded() const bool AnimatedModel::HasContentLoaded() const
{ {
return (SkinnedModel == nullptr || SkinnedModel->IsLoaded()) && Entries.HasContentLoaded(); return (SkinnedModel == nullptr || SkinnedModel->IsLoaded()) && Entries.HasContentLoaded();

View File

@@ -13,7 +13,7 @@
/// Performs an animation and renders a skinned model. /// Performs an animation and renders a skinned model.
/// </summary> /// </summary>
API_CLASS(Attributes="ActorContextMenu(\"New/Animation/Animated Model\"), ActorToolbox(\"Visuals\")") API_CLASS(Attributes="ActorContextMenu(\"New/Animation/Animated Model\"), ActorToolbox(\"Visuals\")")
class FLAXENGINE_API AnimatedModel : public ModelInstanceActor class FLAXENGINE_API AnimatedModel : public ModelInstanceActor, IAssetReference
{ {
DECLARE_SCENE_OBJECT(AnimatedModel); DECLARE_SCENE_OBJECT(AnimatedModel);
friend class AnimationsSystem; friend class AnimationsSystem;
@@ -422,6 +422,11 @@ private:
void OnGraphChanged(); void OnGraphChanged();
void OnGraphLoaded(); void OnGraphLoaded();
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
public: public:
// [ModelInstanceActor] // [ModelInstanceActor]
bool HasContentLoaded() const override; bool HasContentLoaded() const override;

View File

@@ -30,7 +30,6 @@ GPU_CB_STRUCT(Data {
Sky::Sky(const SpawnParams& params) Sky::Sky(const SpawnParams& params)
: Actor(params) : Actor(params)
, _shader(nullptr)
, _psSky(nullptr) , _psSky(nullptr)
, _psFog(nullptr) , _psFog(nullptr)
{ {

View File

@@ -29,10 +29,9 @@ StaticModel::StaticModel(const SpawnParams& params)
, _vertexColorsDirty(false) , _vertexColorsDirty(false)
, _vertexColorsCount(0) , _vertexColorsCount(0)
, _sortOrder(0) , _sortOrder(0)
, Model(this)
{ {
_drawCategory = SceneRendering::SceneDrawAsync; _drawCategory = SceneRendering::SceneDrawAsync;
Model.Changed.Bind<StaticModel, &StaticModel::OnModelChanged>(this);
Model.Loaded.Bind<StaticModel, &StaticModel::OnModelLoaded>(this);
} }
StaticModel::~StaticModel() StaticModel::~StaticModel()
@@ -224,7 +223,7 @@ void StaticModel::RemoveVertexColors()
_vertexColorsDirty = false; _vertexColorsDirty = false;
} }
void StaticModel::OnModelChanged() void StaticModel::OnAssetChanged(Asset* asset, void* caller)
{ {
if (_residencyChangedModel) if (_residencyChangedModel)
{ {
@@ -241,7 +240,7 @@ void StaticModel::OnModelChanged()
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey); GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
} }
void StaticModel::OnModelLoaded() void StaticModel::OnAssetLoaded(Asset* asset, void* caller)
{ {
Entries.SetupIfInvalid(Model); Entries.SetupIfInvalid(Model);
UpdateBounds(); UpdateBounds();
@@ -316,6 +315,10 @@ void StaticModel::FlushVertexColors()
RenderContext::GPULocker.Unlock(); RenderContext::GPULocker.Unlock();
} }
void StaticModel::OnAssetUnloaded(Asset* asset, void* caller)
{
}
bool StaticModel::HasContentLoaded() const bool StaticModel::HasContentLoaded() const
{ {
return (Model == nullptr || Model->IsLoaded()) && Entries.HasContentLoaded(); return (Model == nullptr || Model->IsLoaded()) && Entries.HasContentLoaded();

View File

@@ -11,7 +11,7 @@
/// Renders model on the screen. /// Renders model on the screen.
/// </summary> /// </summary>
API_CLASS(Attributes="ActorContextMenu(\"New/Model\"), ActorToolbox(\"Visuals\")") API_CLASS(Attributes="ActorContextMenu(\"New/Model\"), ActorToolbox(\"Visuals\")")
class FLAXENGINE_API StaticModel : public ModelInstanceActor class FLAXENGINE_API StaticModel : public ModelInstanceActor, IAssetReference
{ {
DECLARE_SCENE_OBJECT(StaticModel); DECLARE_SCENE_OBJECT(StaticModel);
private: private:
@@ -154,11 +154,14 @@ public:
API_FUNCTION() void RemoveVertexColors(); API_FUNCTION() void RemoveVertexColors();
private: private:
void OnModelChanged();
void OnModelLoaded();
void OnModelResidencyChanged(); void OnModelResidencyChanged();
void FlushVertexColors(); void FlushVertexColors();
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
public: public:
// [ModelInstanceActor] // [ModelInstanceActor]
bool HasContentLoaded() const override; bool HasContentLoaded() const override;

View File

@@ -17,8 +17,8 @@
NavMesh::NavMesh(const SpawnParams& params) NavMesh::NavMesh(const SpawnParams& params)
: Actor(params) : Actor(params)
, IsDataDirty(false) , IsDataDirty(false)
, DataAsset(this)
{ {
DataAsset.Loaded.Bind<NavMesh, &NavMesh::OnDataAssetLoaded>(this);
} }
void NavMesh::SaveNavMesh() void NavMesh::SaveNavMesh()
@@ -100,7 +100,11 @@ void NavMesh::RemoveTiles()
navMesh->RemoveTiles(this); navMesh->RemoveTiles(this);
} }
void NavMesh::OnDataAssetLoaded() void NavMesh::OnAssetChanged(Asset* asset, void* caller)
{
}
void NavMesh::OnAssetLoaded(Asset* asset, void* caller)
{ {
// Skip if already has data (prevent reloading navmesh on saving) // Skip if already has data (prevent reloading navmesh on saving)
if (Data.Tiles.HasItems()) if (Data.Tiles.HasItems())
@@ -126,6 +130,10 @@ void NavMesh::OnDataAssetLoaded()
} }
} }
void NavMesh::OnAssetUnloaded(Asset* asset, void* caller)
{
}
void NavMesh::Serialize(SerializeStream& stream, const void* otherObj) void NavMesh::Serialize(SerializeStream& stream, const void* otherObj)
{ {
// Base // Base

View File

@@ -15,7 +15,7 @@ class NavMeshRuntime;
/// The navigation mesh actor that holds a navigation data for a scene. /// The navigation mesh actor that holds a navigation data for a scene.
/// </summary> /// </summary>
API_CLASS(Attributes="ActorContextMenu(\"New/Navigation/Nav Mesh\")") API_CLASS(Attributes="ActorContextMenu(\"New/Navigation/Nav Mesh\")")
class FLAXENGINE_API NavMesh : public Actor class FLAXENGINE_API NavMesh : public Actor, IAssetReference
{ {
DECLARE_SCENE_OBJECT(NavMesh); DECLARE_SCENE_OBJECT(NavMesh);
public: public:
@@ -67,7 +67,11 @@ public:
private: private:
void AddTiles(); void AddTiles();
void RemoveTiles(); void RemoveTiles();
void OnDataAssetLoaded();
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
private: private:
bool _navMeshActive = false; bool _navMeshActive = false;

View File

@@ -16,12 +16,10 @@ ParticleEffect::ParticleEffect(const SpawnParams& params)
: Actor(params) : Actor(params)
, _lastUpdateFrame(0) , _lastUpdateFrame(0)
, _lastMinDstSqr(MAX_Real) , _lastMinDstSqr(MAX_Real)
, ParticleSystem(this)
{ {
_box = BoundingBox(_transform.Translation); _box = BoundingBox(_transform.Translation);
BoundingSphere::FromBox(_box, _sphere); BoundingSphere::FromBox(_box, _sphere);
ParticleSystem.Changed.Bind<ParticleEffect, &ParticleEffect::OnParticleSystemModified>(this);
ParticleSystem.Loaded.Bind<ParticleEffect, &ParticleEffect::OnParticleSystemLoaded>(this);
} }
void ParticleEffectParameter::Init(ParticleEffect* effect, int32 emitterIndex, int32 paramIndex) void ParticleEffectParameter::Init(ParticleEffect* effect, int32 emitterIndex, int32 paramIndex)
@@ -542,18 +540,22 @@ void ParticleEffect::ApplyModifiedParameters()
} }
} }
void ParticleEffect::OnParticleSystemModified() void ParticleEffect::OnAssetChanged(Asset* asset, void* caller)
{ {
Instance.ClearState(); Instance.ClearState();
_parameters.Resize(0); _parameters.Resize(0);
_parametersVersion = 0; _parametersVersion = 0;
} }
void ParticleEffect::OnParticleSystemLoaded() void ParticleEffect::OnAssetLoaded(Asset* asset, void* caller)
{ {
ApplyModifiedParameters(); ApplyModifiedParameters();
} }
void ParticleEffect::OnAssetUnloaded(Asset* asset, void* caller)
{
}
bool ParticleEffect::HasContentLoaded() const bool ParticleEffect::HasContentLoaded() const
{ {
if (ParticleSystem == nullptr) if (ParticleSystem == nullptr)

View File

@@ -118,7 +118,7 @@ public:
/// The particle system instance that plays the particles simulation in the game. /// The particle system instance that plays the particles simulation in the game.
/// </summary> /// </summary>
API_CLASS(Attributes="ActorContextMenu(\"New/Visuals/Particle Effect\"), ActorToolbox(\"Visuals\")") API_CLASS(Attributes="ActorContextMenu(\"New/Visuals/Particle Effect\"), ActorToolbox(\"Visuals\")")
class FLAXENGINE_API ParticleEffect : public Actor class FLAXENGINE_API ParticleEffect : public Actor, IAssetReference
{ {
DECLARE_SCENE_OBJECT(ParticleEffect); DECLARE_SCENE_OBJECT(ParticleEffect);
public: public:
@@ -388,6 +388,11 @@ private:
void OnParticleSystemModified(); void OnParticleSystemModified();
void OnParticleSystemLoaded(); void OnParticleSystemLoaded();
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
public: public:
// [Actor] // [Actor]
bool HasContentLoaded() const override; bool HasContentLoaded() const override;

View File

@@ -20,10 +20,8 @@ Collider::Collider(const SpawnParams& params)
, _staticActor(nullptr) , _staticActor(nullptr)
, _cachedScale(1.0f) , _cachedScale(1.0f)
, _contactOffset(2.0f) , _contactOffset(2.0f)
, Material(this)
{ {
Material.Loaded.Bind<Collider, &Collider::OnMaterialChanged>(this);
Material.Unload.Bind<Collider, &Collider::OnMaterialChanged>(this);
Material.Changed.Bind<Collider, &Collider::OnMaterialChanged>(this);
} }
void* Collider::GetPhysicsShape() const void* Collider::GetPhysicsShape() const
@@ -294,13 +292,6 @@ void Collider::DrawPhysicsDebug(RenderView& view)
#endif #endif
void Collider::OnMaterialChanged()
{
// Update the shape material
if (_shape)
PhysicsBackend::SetShapeMaterial(_shape, Material);
}
void Collider::BeginPlay(SceneBeginData* data) void Collider::BeginPlay(SceneBeginData* data)
{ {
// Check if has no shape created (it means no rigidbody requested it but also collider may be spawned at runtime) // Check if has no shape created (it means no rigidbody requested it but also collider may be spawned at runtime)
@@ -466,3 +457,20 @@ void Collider::OnPhysicsSceneChanged(PhysicsScene* previous)
PhysicsBackend::AddSceneActor(scene, _staticActor); PhysicsBackend::AddSceneActor(scene, _staticActor);
} }
} }
void Collider::OnAssetChanged(Asset* asset, void* caller)
{
// Update the shape material
if (_shape && caller == &Material)
PhysicsBackend::SetShapeMaterial(_shape, Material);
}
void Collider::OnAssetLoaded(Asset* asset, void* caller)
{
Collider::OnAssetChanged(asset, caller);
}
void Collider::OnAssetUnloaded(Asset* asset, void* caller)
{
Collider::OnAssetChanged(asset, caller);
}

View File

@@ -15,7 +15,7 @@ class RigidBody;
/// </summary> /// </summary>
/// <seealso cref="Actor" /> /// <seealso cref="Actor" />
/// <seealso cref="PhysicsColliderActor" /> /// <seealso cref="PhysicsColliderActor" />
API_CLASS(Abstract) class FLAXENGINE_API Collider : public PhysicsColliderActor API_CLASS(Abstract) class FLAXENGINE_API Collider : public PhysicsColliderActor, protected IAssetReference
{ {
API_AUTO_SERIALIZATION(); API_AUTO_SERIALIZATION();
DECLARE_SCENE_OBJECT_ABSTRACT(Collider); DECLARE_SCENE_OBJECT_ABSTRACT(Collider);
@@ -154,9 +154,6 @@ protected:
/// </summary> /// </summary>
void RemoveStaticActor(); void RemoveStaticActor();
private:
void OnMaterialChanged();
public: public:
// [PhysicsColliderActor] // [PhysicsColliderActor]
RigidBody* GetAttachedRigidBody() const override; RigidBody* GetAttachedRigidBody() const override;
@@ -181,4 +178,9 @@ protected:
void OnLayerChanged() override; void OnLayerChanged() override;
void OnStaticFlagsChanged() override; void OnStaticFlagsChanged() override;
void OnPhysicsSceneChanged(PhysicsScene* previous) override; void OnPhysicsSceneChanged(PhysicsScene* previous) override;
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
}; };

View File

@@ -11,9 +11,8 @@
MeshCollider::MeshCollider(const SpawnParams& params) MeshCollider::MeshCollider(const SpawnParams& params)
: Collider(params) : Collider(params)
, CollisionData(this)
{ {
CollisionData.Changed.Bind<MeshCollider, &MeshCollider::OnCollisionDataChanged>(this);
CollisionData.Loaded.Bind<MeshCollider, &MeshCollider::OnCollisionDataLoaded>(this);
} }
void MeshCollider::OnCollisionDataChanged() void MeshCollider::OnCollisionDataChanged()
@@ -33,8 +32,9 @@ void MeshCollider::OnCollisionDataChanged()
void MeshCollider::OnCollisionDataLoaded() void MeshCollider::OnCollisionDataLoaded()
{ {
UpdateGeometry(); // Not needed as OnCollisionDataChanged waits for it to be loaded
UpdateBounds(); //UpdateGeometry();
//UpdateBounds();
} }
bool MeshCollider::CanAttach(RigidBody* rigidBody) const bool MeshCollider::CanAttach(RigidBody* rigidBody) const
@@ -152,3 +152,19 @@ void MeshCollider::GetGeometry(CollisionShape& collision)
else else
collision.SetSphere(minSize); collision.SetSphere(minSize);
} }
void MeshCollider::OnAssetChanged(Asset* asset, void* caller)
{
Collider::OnAssetChanged(asset, caller);
if (caller == &CollisionData)
OnCollisionDataChanged();
}
void MeshCollider::OnAssetLoaded(Asset* asset, void* caller)
{
Collider::OnAssetLoaded(asset, caller);
if (caller == &CollisionData)
OnCollisionDataLoaded();
}

View File

@@ -42,4 +42,6 @@ protected:
#endif #endif
void UpdateBounds() override; void UpdateBounds() override;
void GetGeometry(CollisionShape& collision) override; void GetGeometry(CollisionShape& collision) override;
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
}; };

View File

@@ -39,7 +39,6 @@ GPU_CB_STRUCT(Data {
ColorGradingPass::ColorGradingPass() ColorGradingPass::ColorGradingPass()
: _useVolumeTexture(false) : _useVolumeTexture(false)
, _lutFormat() , _lutFormat()
, _shader(nullptr)
{ {
} }

View File

@@ -14,8 +14,7 @@
#include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Graphics/Shaders/GPUShader.h"
ForwardPass::ForwardPass() ForwardPass::ForwardPass()
: _shader(nullptr) : _psApplyDistortion(nullptr)
, _psApplyDistortion(nullptr)
{ {
} }

View File

@@ -19,7 +19,6 @@ int32 VolumetricFogGridInjectionGroupSize = 4;
int32 VolumetricFogIntegrationGroupSize = 8; int32 VolumetricFogIntegrationGroupSize = 8;
VolumetricFogPass::VolumetricFogPass() VolumetricFogPass::VolumetricFogPass()
: _shader(nullptr)
{ {
} }