@@ -340,6 +340,14 @@ namespace AZ
|
||||
// (Load jobs will attempt to reuse blocked threads before spinning off new job threads)
|
||||
ProcessLoadJob();
|
||||
}
|
||||
|
||||
// Pump the AssetBus function queue once more after the load has completed in case additional
|
||||
// functions have been queued between the last call to DispatchEvents and the completion
|
||||
// of the current load job
|
||||
if (m_shouldDispatchEvents)
|
||||
{
|
||||
AssetManager::Instance().DispatchEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void Finish()
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzCore/Slice/SliceAssetHandler.h>
|
||||
#include <AzCore/Slice/SliceComponent.h>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
|
||||
namespace AZ {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <AzCore/Time/TimeSystemComponent.h>
|
||||
#include <AzCore/Console/LoggerSystemComponent.h>
|
||||
#include <AzCore/EBus/EventSchedulerSystemComponent.h>
|
||||
#include <AzCore/Task/TaskGraphSystemComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -41,6 +42,7 @@ namespace AZ
|
||||
TimeSystemComponent::CreateDescriptor(),
|
||||
LoggerSystemComponent::CreateDescriptor(),
|
||||
EventSchedulerSystemComponent::CreateDescriptor(),
|
||||
TaskGraphSystemComponent::CreateDescriptor(),
|
||||
|
||||
#if !defined(AZCORE_EXCLUDE_LUA)
|
||||
ScriptSystemComponent::CreateDescriptor(),
|
||||
@@ -55,6 +57,7 @@ namespace AZ
|
||||
azrtti_typeid<TimeSystemComponent>(),
|
||||
azrtti_typeid<LoggerSystemComponent>(),
|
||||
azrtti_typeid<EventSchedulerSystemComponent>(),
|
||||
azrtti_typeid<TaskGraphSystemComponent>(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h> // Used as the allocator for most components.
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
@@ -74,8 +74,6 @@
|
||||
#include <AzCore/Module/Environment.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
|
||||
AZ_CVAR(float, g_simulation_tick_rate, 0, nullptr, AZ::ConsoleFunctorFlags::Null, "The rate at which the game simulation tick loop runs, or 0 for as fast as possible");
|
||||
|
||||
static void PrintEntityName(const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
if (arguments.empty())
|
||||
@@ -1393,23 +1391,6 @@ namespace AZ
|
||||
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick");
|
||||
EBUS_EVENT(TickBus, OnTick, m_deltaTime, ScriptTimePoint(now));
|
||||
}
|
||||
|
||||
// If tick rate limiting is on, ensure (1 / g_simulation_tick_rate) ms has elapsed since the last frame,
|
||||
// sleeping if there's still time remaining.
|
||||
if (g_simulation_tick_rate > 0.f)
|
||||
{
|
||||
now = AZStd::chrono::system_clock::now();
|
||||
|
||||
// Work in microsecond durations here as that's the native measurement time for time_point
|
||||
constexpr float microsecondsPerSecond = 1000.f * 1000.f;
|
||||
const AZStd::chrono::microseconds timeBudgetPerTick(static_cast<int>(microsecondsPerSecond / g_simulation_tick_rate));
|
||||
AZStd::chrono::microseconds timeUntilNextTick = m_currentTime + timeBudgetPerTick - now;
|
||||
|
||||
if (timeUntilNextTick.count() > 0)
|
||||
{
|
||||
AZStd::this_thread::sleep_for(timeUntilNextTick);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -649,6 +649,16 @@ namespace AZ
|
||||
m_stateEvent.Signal(oldState, m_state);
|
||||
}
|
||||
|
||||
void Entity::SetSpawnTicketId(u32 spawnTicketId)
|
||||
{
|
||||
m_spawnTicketId = spawnTicketId;
|
||||
}
|
||||
|
||||
u32 Entity::GetSpawnTicketId() const
|
||||
{
|
||||
return m_spawnTicketId;
|
||||
}
|
||||
|
||||
void Entity::OnNameChanged() const
|
||||
{
|
||||
EBUS_EVENT_ID(GetId(), EntityBus, OnEntityNameChanged, m_name);
|
||||
|
||||
@@ -133,6 +133,14 @@ namespace AZ
|
||||
//! @return The state of the entity. For example, the entity has been initialized, the entity is active, and so on.
|
||||
State GetState() const { return m_state; }
|
||||
|
||||
//! Gets the ticket id used to spawn the entity.
|
||||
//! @return the ticket id used to spawn the entity. If entity is not spawned, the id will be 0.
|
||||
u32 GetSpawnTicketId() const;
|
||||
|
||||
//! Sets the ticket id used to spawn the entity. The ticket id in the entity will remain 0 unless it's set using this function.
|
||||
//! @param spawnTicketId the ticket id used to spawn the entity.
|
||||
void SetSpawnTicketId(u32 spawnTicketId);
|
||||
|
||||
//! Connects an entity state event handler to the entity.
|
||||
//! All state changes will be signaled through this event.
|
||||
//! @param handler reference to the EntityStateEvent handler to attach to the entities state event.
|
||||
@@ -410,6 +418,8 @@ namespace AZ
|
||||
//! A user-friendly name for the entity. This makes error messages easier to read.
|
||||
AZStd::string m_name;
|
||||
|
||||
u32 m_spawnTicketId = 0;
|
||||
|
||||
//! The state of the entity.
|
||||
State m_state;
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZCORE_ENTITY_UTILS_H
|
||||
#define AZCORE_ENTITY_UTILS_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
@@ -217,6 +216,3 @@ namespace AZ
|
||||
|
||||
} // namespace EntityUtils
|
||||
} // namespace AZ
|
||||
|
||||
#endif // AZCORE_ENTITY_UTILS_H
|
||||
#pragma once
|
||||
|
||||
@@ -46,8 +46,6 @@ namespace AZ
|
||||
|
||||
TICK_PRE_RENDER = 750, ///< Suggested tick handler position to update render-related data.
|
||||
|
||||
TICK_RENDER = 800, ///< Suggested tick handler position for rendering.
|
||||
|
||||
TICK_DEFAULT = 1000, ///< Default tick handler position when the handler is constructed.
|
||||
|
||||
TICK_UI = 2000, ///< Suggested tick handler position for UI components.
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/EBus/Policies.h>
|
||||
|
||||
|
||||
@@ -19,14 +19,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/EBus/BusImpl.h>
|
||||
#include <AzCore/EBus/Environment.h>
|
||||
#include <AzCore/EBus/Results.h>
|
||||
#include <AzCore/EBus/Internal/Debug.h>
|
||||
|
||||
// Included for backwards compatibility purposes
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
#include <AzCore/std/typetraits/is_same.h>
|
||||
// End backwards compat
|
||||
|
||||
#include <AzCore/std/utils.h>
|
||||
#include <AzCore/std/parallel/scoped_lock.h>
|
||||
@@ -90,14 +87,14 @@ namespace AZ
|
||||
* For available settings, see AZ::EBusHandlerPolicy.
|
||||
* By default, an EBus supports any number of handlers.
|
||||
*/
|
||||
static const EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::Multiple;
|
||||
static constexpr EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::Multiple;
|
||||
|
||||
/**
|
||||
* Defines how many addresses exist on the EBus.
|
||||
* For available settings, see AZ::EBusAddressPolicy.
|
||||
* By default, an EBus uses a single address.
|
||||
*/
|
||||
static const EBusAddressPolicy AddressPolicy = EBusAddressPolicy::Single;
|
||||
static constexpr EBusAddressPolicy AddressPolicy = EBusAddressPolicy::Single;
|
||||
|
||||
/**
|
||||
* The type of ID that is used to address the EBus.
|
||||
@@ -152,14 +149,14 @@ namespace AZ
|
||||
* `<BusName>::ExecuteQueuedEvents()`.
|
||||
* By default, the event queue is disabled.
|
||||
*/
|
||||
static const bool EnableEventQueue = false;
|
||||
static constexpr bool EnableEventQueue = false;
|
||||
|
||||
/**
|
||||
* Specifies whether the bus should accept queued messages by default or not.
|
||||
* If set to false, Bus::AllowFunctionQueuing(true) must be called before events are accepted.
|
||||
* Used only when #EnableEventQueue is true.
|
||||
*/
|
||||
static const bool EventQueueingActiveByDefault = true;
|
||||
static constexpr bool EventQueueingActiveByDefault = true;
|
||||
|
||||
/**
|
||||
* Specifies whether the EBus supports queueing functions which take reference
|
||||
@@ -168,7 +165,7 @@ namespace AZ
|
||||
* You should only use this if you know that the data being passed as arguments will
|
||||
* outlive the dispatch of the queued event.
|
||||
*/
|
||||
static const bool EnableQueuedReferences = false;
|
||||
static constexpr bool EnableQueuedReferences = false;
|
||||
|
||||
/**
|
||||
* Locking primitive that is used when adding and removing
|
||||
@@ -197,7 +194,7 @@ namespace AZ
|
||||
* to do.
|
||||
* By default, the standard policy is used, which locks around all dispatches
|
||||
*/
|
||||
static const bool LocklessDispatch = false;
|
||||
static constexpr bool LocklessDispatch = false;
|
||||
|
||||
/**
|
||||
* Specifies where EBus data is stored.
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/RTTI/TypeSafeIntegral.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
@@ -18,9 +18,8 @@
|
||||
#include <AzCore/std/function/invoke.h>
|
||||
#include <AzCore/std/containers/queue.h>
|
||||
#include <AzCore/std/containers/intrusive_set.h>
|
||||
#include <AzCore/std/parallel/scoped_lock.h>
|
||||
|
||||
#include <AzCore/Module/Environment.h>
|
||||
#include <AzCore/EBus/Environment.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -251,29 +250,21 @@ namespace AZ
|
||||
void Execute()
|
||||
{
|
||||
AZ_Warning("System", m_isActive, "You are calling execute queued functions on a bus which has not activated its function queuing! Call YourBus::AllowFunctionQueuing(true)!");
|
||||
while (true)
|
||||
|
||||
MessageQueueType localMessages;
|
||||
|
||||
// Swap the current list of queue functions with a local instance
|
||||
{
|
||||
BusMessageCall invoke;
|
||||
AZStd::scoped_lock lock(m_messagesMutex);
|
||||
AZStd::swap(localMessages, m_messages);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Pop element from the queue.
|
||||
{
|
||||
AZStd::lock_guard<MutexType> lock(m_messagesMutex);
|
||||
size_t numMessages = m_messages.size();
|
||||
if (numMessages == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
AZStd::swap(invoke, m_messages.front());
|
||||
m_messages.pop();
|
||||
if (numMessages == 1)
|
||||
{
|
||||
m_messages = {};
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
invoke();
|
||||
// Execute the queue functions safely now that are owned by the function
|
||||
while (!localMessages.empty())
|
||||
{
|
||||
const BusMessageCall& localMessage = localMessages.front();
|
||||
localMessage();
|
||||
localMessages.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,10 +43,12 @@ namespace AZ::IO
|
||||
|
||||
m_mainLoopDesc = threadDesc;
|
||||
m_mainLoopDesc.m_name = "IO Scheduler";
|
||||
m_mainLoop = AZStd::thread([this]()
|
||||
{
|
||||
Thread_MainLoop();
|
||||
}, &m_mainLoopDesc);
|
||||
m_mainLoop = AZStd::thread(
|
||||
m_mainLoopDesc,
|
||||
[this]()
|
||||
{
|
||||
Thread_MainLoop();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -644,11 +644,11 @@ JobManagerWorkStealing::ThreadList JobManagerWorkStealing::CreateWorkerThreads(c
|
||||
}
|
||||
|
||||
info->m_thread = AZStd::thread(
|
||||
threadDesc,
|
||||
[this, info]()
|
||||
{
|
||||
this->ProcessJobsWorker(info);
|
||||
},
|
||||
&threadDesc
|
||||
}
|
||||
);
|
||||
|
||||
info->m_threadId = info->m_thread.get_id();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Name/NameSerializer.h>
|
||||
#include <AzCore/IO/GenericStreams.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
#include <AzCore/std/function/invoke.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/Script/ScriptContext.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Script/lua/lua.h>
|
||||
#include <AzCore/Script/ScriptProperty.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZCORE_SCRIPT_SCRIPTPROPERTY_H
|
||||
#define AZCORE_SCRIPT_SCRIPTPROPERTY_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
@@ -490,5 +489,4 @@ namespace AZ
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/Serialization/DataPatch.h>
|
||||
#include <AzCore/Serialization/DataPatchBus.h>
|
||||
#include <AzCore/Serialization/DataPatchUpgradeManager.h>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "AzCore/RTTI/TypeInfo.h"
|
||||
#include <AzCore/Math/UuidSerializer.h>
|
||||
#include <AzCore/RTTI/AttributeReader.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/Json/CastingHelpers.h>
|
||||
#include <AzCore/Serialization/Json/JsonDeserializer.h>
|
||||
#include <AzCore/Serialization/Json/JsonStringConversionUtils.h>
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/RTTI/AttributeReader.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerializer.h>
|
||||
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/Json/BasicContainerSerializer.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Serialization/Json/MapSerializer.h>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/RTTI/AttributeReader.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/Serialization/ObjectStream.h>
|
||||
#include <AzCore/Serialization/DataOverlayInstanceMsgs.h>
|
||||
#include <AzCore/Serialization/DataOverlayProviderMsgs.h>
|
||||
|
||||
@@ -37,7 +37,8 @@ namespace AZ
|
||||
class GenericStream;
|
||||
}
|
||||
|
||||
namespace ObjectStreamInternal {
|
||||
namespace ObjectStreamInternal
|
||||
{
|
||||
class ObjectStreamImpl;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/DataOverlay.h>
|
||||
#include <AzCore/Serialization/DynamicSerializableField.h>
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZCORE_SERIALIZE_CONTEXT_H
|
||||
#define AZCORE_SERIALIZE_CONTEXT_H
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
|
||||
@@ -43,6 +42,12 @@
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Data
|
||||
{
|
||||
template<typename T>
|
||||
class Asset;
|
||||
}
|
||||
|
||||
class EditContext;
|
||||
|
||||
class ObjectStream;
|
||||
@@ -2562,11 +2567,13 @@ namespace AZ
|
||||
#include <AzCore/Serialization/AZStdContainers.inl>
|
||||
#include <AzCore/Serialization/std/VariantReflection.inl>
|
||||
|
||||
/// include asset generics
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
// Forward declare asset serialization helper specialization
|
||||
namespace AZ
|
||||
{
|
||||
template<typename T>
|
||||
struct SerializeGenericTypeInfo< Data::Asset<T> >;
|
||||
}
|
||||
|
||||
/// include implementation of SerializeContext::EnumBuilder
|
||||
#include <AzCore/Serialization/SerializeContextEnum.inl>
|
||||
|
||||
#endif // AZCORE_SERIALIZE_CONTEXT_H
|
||||
#pragma once
|
||||
|
||||
@@ -717,7 +717,9 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
if (registry.Get(cacheRootPath, FilePathKey_CacheRootFolder))
|
||||
{
|
||||
mergePath = AZStd::move(cacheRootPath);
|
||||
mergePath /= SettingsRegistryInterface::RegistryFolder;
|
||||
AZStd::fixed_string<32> registryFolderLower(SettingsRegistryInterface::RegistryFolder);
|
||||
AZStd::to_lower(registryFolderLower.begin(), registryFolderLower.end());
|
||||
mergePath /= registryFolderLower;
|
||||
registry.MergeSettingsFolder(mergePath.Native(), specializations, platform, "", scratchBuffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <AzCore/EBus/BusImpl.h> //Just to get AZ::NullMutex
|
||||
#include <AzCore/std/chrono/types.h>
|
||||
#include <AzCore/Statistics/StatisticsManager.h>
|
||||
#include <AzCore/std/chrono/chrono.h>
|
||||
#include <AzCore/std/parallel/scoped_lock.h>
|
||||
|
||||
namespace AZ
|
||||
@@ -243,7 +244,7 @@ namespace AZ
|
||||
|
||||
//! This one is needed because running statistics are collected many times across
|
||||
//! several frames. This value is used to calculate a per frame sample for @m_totalTimePerFrameStat,
|
||||
//! by subtracting @m_prevAccumulatedSums from the accumulated sum in @m_statisticsManager.
|
||||
//! by subtracting @m_prevAccumulatedSums from the accumulated sum in @m_statisticsManager.
|
||||
double m_prevAccumulatedSums;
|
||||
};
|
||||
|
||||
|
||||
@@ -190,11 +190,13 @@ namespace AZ
|
||||
class TaskWorker
|
||||
{
|
||||
public:
|
||||
void Spawn(::AZ::TaskExecutor& executor, size_t id, AZStd::semaphore& initSemaphore, bool affinitize)
|
||||
static thread_local TaskWorker* t_worker;
|
||||
|
||||
void Spawn(::AZ::TaskExecutor& executor, uint32_t id, AZStd::semaphore& initSemaphore, bool affinitize)
|
||||
{
|
||||
m_executor = &executor;
|
||||
|
||||
AZStd::string threadName = AZStd::string::format("TaskWorker %zu", id);
|
||||
AZStd::string threadName = AZStd::string::format("TaskWorker %u", id);
|
||||
AZStd::thread_desc desc = {};
|
||||
desc.m_name = threadName.c_str();
|
||||
if (affinitize)
|
||||
@@ -203,12 +205,29 @@ namespace AZ
|
||||
}
|
||||
m_active.store(true, AZStd::memory_order_release);
|
||||
|
||||
m_thread = AZStd::thread{ [this, &initSemaphore]
|
||||
m_thread = AZStd::thread{ desc,
|
||||
[this, &initSemaphore]
|
||||
{
|
||||
t_worker = this;
|
||||
initSemaphore.release();
|
||||
Run();
|
||||
},
|
||||
&desc };
|
||||
} };
|
||||
}
|
||||
|
||||
// Threads that wait on a graph to complete are disqualified from receiving tasks until the wait finishes
|
||||
void Disable()
|
||||
{
|
||||
m_enabled = false;
|
||||
}
|
||||
|
||||
void Enable()
|
||||
{
|
||||
m_enabled = true;
|
||||
}
|
||||
|
||||
bool Enabled() const
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
void Join()
|
||||
@@ -222,11 +241,7 @@ namespace AZ
|
||||
{
|
||||
m_queue.Enqueue(task);
|
||||
|
||||
if (!m_busy.exchange(true))
|
||||
{
|
||||
// The worker was idle prior to enqueueing the task, release the semaphore
|
||||
m_semaphore.release();
|
||||
}
|
||||
m_semaphore.release();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -234,7 +249,6 @@ namespace AZ
|
||||
{
|
||||
while (m_active)
|
||||
{
|
||||
m_busy = false;
|
||||
m_semaphore.acquire();
|
||||
|
||||
if (!m_active)
|
||||
@@ -242,8 +256,6 @@ namespace AZ
|
||||
return;
|
||||
}
|
||||
|
||||
m_busy = true;
|
||||
|
||||
Task* task = m_queue.TryDequeue();
|
||||
while (task)
|
||||
{
|
||||
@@ -271,12 +283,15 @@ namespace AZ
|
||||
|
||||
AZStd::thread m_thread;
|
||||
AZStd::atomic<bool> m_active;
|
||||
AZStd::atomic<bool> m_busy;
|
||||
AZStd::atomic<bool> m_enabled = true;
|
||||
AZStd::binary_semaphore m_semaphore;
|
||||
|
||||
::AZ::TaskExecutor* m_executor;
|
||||
TaskQueue m_queue;
|
||||
friend class ::AZ::TaskExecutor;
|
||||
};
|
||||
|
||||
thread_local TaskWorker* TaskWorker::t_worker = nullptr;
|
||||
} // namespace Internal
|
||||
|
||||
static EnvironmentVariable<TaskExecutor*> s_executor;
|
||||
@@ -291,13 +306,16 @@ namespace AZ
|
||||
return **s_executor;
|
||||
}
|
||||
|
||||
// TODO: Create the default executor as part of a component (as in TaskManagerComponent)
|
||||
void TaskExecutor::SetInstance(TaskExecutor* executor)
|
||||
{
|
||||
AZ_Assert(!s_executor, "Attempting to set the global task executor more than once");
|
||||
|
||||
s_executor = AZ::Environment::CreateVariable<TaskExecutor*>("GlobalTaskExecutor");
|
||||
s_executor.Set(executor);
|
||||
if (!executor)
|
||||
{
|
||||
s_executor.Reset();
|
||||
}
|
||||
else if (!s_executor) // ignore any calls to set after the first (this happens in unit tests that create new system entities)
|
||||
{
|
||||
s_executor = AZ::Environment::CreateVariable<TaskExecutor*>(s_executorName, executor);
|
||||
}
|
||||
}
|
||||
|
||||
TaskExecutor::TaskExecutor(uint32_t threadCount)
|
||||
@@ -307,14 +325,12 @@ namespace AZ
|
||||
|
||||
m_workers = reinterpret_cast<Internal::TaskWorker*>(azmalloc(m_threadCount * sizeof(Internal::TaskWorker)));
|
||||
|
||||
bool affinitize = m_threadCount == AZStd::thread::hardware_concurrency();
|
||||
|
||||
AZStd::semaphore initSemaphore;
|
||||
|
||||
for (size_t i = 0; i != m_threadCount; ++i)
|
||||
for (uint32_t i = 0; i != m_threadCount; ++i)
|
||||
{
|
||||
new (m_workers + i) Internal::TaskWorker{};
|
||||
m_workers[i].Spawn(*this, i, initSemaphore, affinitize);
|
||||
m_workers[i].Spawn(*this, i, initSemaphore, false);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i != m_threadCount; ++i)
|
||||
@@ -334,9 +350,21 @@ namespace AZ
|
||||
azfree(m_workers);
|
||||
}
|
||||
|
||||
void TaskExecutor::Submit(Internal::CompiledTaskGraph& graph)
|
||||
Internal::TaskWorker* TaskExecutor::GetTaskWorker()
|
||||
{
|
||||
if (Internal::TaskWorker::t_worker && Internal::TaskWorker::t_worker->m_executor == this)
|
||||
{
|
||||
return Internal::TaskWorker::t_worker;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TaskExecutor::Submit(Internal::CompiledTaskGraph& graph, TaskGraphEvent* event)
|
||||
{
|
||||
++m_graphsRemaining;
|
||||
|
||||
event->m_executor = this; // Used to validate event is not waited for inside a job
|
||||
|
||||
// Submit all tasks that have no inbound edges
|
||||
for (Internal::Task& task : graph.Tasks())
|
||||
{
|
||||
@@ -352,11 +380,24 @@ namespace AZ
|
||||
// TODO: Something more sophisticated is likely needed here.
|
||||
// First, we are completely ignoring affinity.
|
||||
// Second, some heuristics on core availability will help distribute work more effectively
|
||||
m_workers[++m_lastSubmission % m_threadCount].Enqueue(&task);
|
||||
uint32_t nextWorker = ++m_lastSubmission % m_threadCount;
|
||||
while (!m_workers[nextWorker].Enabled())
|
||||
{
|
||||
// Graphs that are waiting for the completion of a task graph cannot enqueue tasks onto
|
||||
// the thread issuing the wait.
|
||||
nextWorker = ++m_lastSubmission % m_threadCount;
|
||||
}
|
||||
|
||||
m_workers[nextWorker].Enqueue(&task);
|
||||
}
|
||||
|
||||
void TaskExecutor::ReleaseGraph()
|
||||
{
|
||||
--m_graphsRemaining;
|
||||
}
|
||||
|
||||
void TaskExecutor::ReactivateTaskWorker()
|
||||
{
|
||||
GetTaskWorker()->Enable();
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
@@ -72,14 +72,19 @@ namespace AZ
|
||||
explicit TaskExecutor(uint32_t threadCount = 0);
|
||||
~TaskExecutor();
|
||||
|
||||
void Submit(Internal::CompiledTaskGraph& graph);
|
||||
// Submit a task graph for execution. Waitable task graphs cannot enqueue work on the task thread
|
||||
// that is currently active
|
||||
void Submit(Internal::CompiledTaskGraph& graph, TaskGraphEvent* event);
|
||||
|
||||
void Submit(Internal::Task& task);
|
||||
|
||||
private:
|
||||
friend class Internal::TaskWorker;
|
||||
friend class TaskGraphEvent;
|
||||
|
||||
Internal::TaskWorker* GetTaskWorker();
|
||||
void ReleaseGraph();
|
||||
void ReactivateTaskWorker();
|
||||
|
||||
Internal::TaskWorker* m_workers;
|
||||
uint32_t m_threadCount = 0;
|
||||
|
||||
@@ -14,6 +14,12 @@ namespace AZ
|
||||
{
|
||||
using Internal::CompiledTaskGraph;
|
||||
|
||||
void TaskGraphEvent::Wait()
|
||||
{
|
||||
AZ_Assert(m_executor->GetTaskWorker() == nullptr, "Waiting in a task is unsupported");
|
||||
m_semaphore.acquire();
|
||||
}
|
||||
|
||||
void TaskToken::PrecedesInternal(TaskToken& comesAfter)
|
||||
{
|
||||
AZ_Assert(!m_parent.m_submitted, "Cannot mutate a TaskGraph that was previously submitted.");
|
||||
@@ -71,7 +77,7 @@ namespace AZ
|
||||
m_compiledTaskGraph->m_tasks[i].Init();
|
||||
}
|
||||
|
||||
executor.Submit(*m_compiledTaskGraph);
|
||||
executor.Submit(*m_compiledTaskGraph, waitEvent);
|
||||
|
||||
if (m_retained)
|
||||
{
|
||||
|
||||
@@ -22,10 +22,19 @@ namespace AZ
|
||||
namespace Internal
|
||||
{
|
||||
class CompiledTaskGraph;
|
||||
class TaskWorker;
|
||||
}
|
||||
class TaskExecutor;
|
||||
class TaskGraph;
|
||||
|
||||
class TaskGraphActiveInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(TaskGraphActiveInterface, "{08118074-B139-4EF9-B8FD-29F1D6DC9233}");
|
||||
|
||||
virtual bool IsTaskGraphActive() const = 0;
|
||||
};
|
||||
|
||||
// A TaskToken is returned each time a Task is added to the TaskGraph. TaskTokens are used to
|
||||
// express dependencies between tasks within the graph, and have no purpose after the graph
|
||||
// is submitted (simply let them go out of scope)
|
||||
@@ -70,9 +79,12 @@ namespace AZ
|
||||
private:
|
||||
friend class ::AZ::Internal::CompiledTaskGraph;
|
||||
friend class TaskGraph;
|
||||
friend class TaskExecutor;
|
||||
|
||||
void Signal();
|
||||
|
||||
AZStd::binary_semaphore m_semaphore;
|
||||
TaskExecutor* m_executor = nullptr;
|
||||
};
|
||||
|
||||
// The TaskGraph encapsulates a set of tasks and their interdependencies. After adding
|
||||
@@ -89,6 +101,9 @@ namespace AZ
|
||||
// Reset the state of the task graph to begin recording tasks and edges again
|
||||
// NOTE: Graph must be in a "settled" state (cannot be in-flight)
|
||||
void Reset();
|
||||
|
||||
// Returns false if 1 or more tasks have been added to the graph
|
||||
bool IsEmpty();
|
||||
|
||||
// Add a task to the graph, retrieiving a token that can be used to express dependencies
|
||||
// between tasks. The first argument specifies the TaskKind, used for tracking the task.
|
||||
|
||||
@@ -33,11 +33,6 @@ namespace AZ
|
||||
return m_semaphore.try_acquire_for(AZStd::chrono::milliseconds{ 0 });
|
||||
}
|
||||
|
||||
inline void TaskGraphEvent::Wait()
|
||||
{
|
||||
m_semaphore.acquire();
|
||||
}
|
||||
|
||||
inline void TaskGraphEvent::Signal()
|
||||
{
|
||||
m_semaphore.release();
|
||||
@@ -59,6 +54,11 @@ namespace AZ
|
||||
return { AddTask(descriptor, AZStd::forward<Lambdas>(lambdas))... };
|
||||
}
|
||||
|
||||
inline bool TaskGraph::IsEmpty()
|
||||
{
|
||||
return m_tasks.empty();
|
||||
}
|
||||
|
||||
inline void TaskGraph::Detach()
|
||||
{
|
||||
m_retained = false;
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Task/TaskGraphSystemComponent.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
|
||||
// Create a cvar as a central location for experimentation with switching from the Job system to TaskGraph system.
|
||||
AZ_CVAR(bool, cl_activateTaskGraph, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Flag clients of TaskGraph to switch between jobs/taskgraph (Note does not disable task graph system)");
|
||||
static constexpr uint32_t TaskExecutorServiceCrc = AZ_CRC_CE("TaskExecutorService");
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
void TaskGraphSystemComponent::Activate()
|
||||
{
|
||||
AZ_Assert(m_taskExecutor == nullptr, "Error multiple activation of the TaskGraphSystemComponent");
|
||||
|
||||
if (Interface<TaskGraphActiveInterface>::Get() == nullptr)
|
||||
{
|
||||
Interface<TaskGraphActiveInterface>::Register(this);
|
||||
m_taskExecutor = aznew TaskExecutor();
|
||||
TaskExecutor::SetInstance(m_taskExecutor);
|
||||
}
|
||||
}
|
||||
|
||||
void TaskGraphSystemComponent::Deactivate()
|
||||
{
|
||||
if (&TaskExecutor::Instance() == m_taskExecutor) // check that our instance is the global instance (not always true in unit tests)
|
||||
{
|
||||
m_taskExecutor->SetInstance(nullptr);
|
||||
}
|
||||
if (m_taskExecutor)
|
||||
{
|
||||
azdestroy(m_taskExecutor);
|
||||
m_taskExecutor = nullptr;
|
||||
}
|
||||
if (Interface<TaskGraphActiveInterface>::Get() == this)
|
||||
{
|
||||
Interface<TaskGraphActiveInterface>::Unregister(this);
|
||||
}
|
||||
}
|
||||
|
||||
void TaskGraphSystemComponent::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(TaskExecutorServiceCrc);
|
||||
}
|
||||
|
||||
void TaskGraphSystemComponent::GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(TaskExecutorServiceCrc);
|
||||
}
|
||||
|
||||
void TaskGraphSystemComponent::GetDependentServices([[maybe_unused]] ComponentDescriptor::DependencyArrayType& dependent)
|
||||
{
|
||||
}
|
||||
|
||||
void TaskGraphSystemComponent::Reflect(ReflectContext* context)
|
||||
{
|
||||
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<TaskGraphSystemComponent, AZ::Component>()
|
||||
->Version(1)
|
||||
;
|
||||
|
||||
if (AZ::EditContext* ec = serializeContext->GetEditContext())
|
||||
{
|
||||
ec->Class<TaskGraphSystemComponent>
|
||||
("TaskGraph", "System component to create the default executor")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Category, "Engine")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TaskGraphSystemComponent::IsTaskGraphActive() const
|
||||
{
|
||||
return cl_activateTaskGraph;
|
||||
}
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Math/Crc.h>
|
||||
#include <AzCore/Task/TaskExecutor.h>
|
||||
#include <AzCore/Task/TaskGraph.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class TaskGraphSystemComponent
|
||||
: public Component
|
||||
, public TaskGraphActiveInterface
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AZ::TaskGraphSystemComponent, "{5D56B829-1FEB-43D5-A0BD-E33C0497EFE2}")
|
||||
|
||||
TaskGraphSystemComponent() = default;
|
||||
|
||||
// Implement TaskGraphActiveInterface
|
||||
bool IsTaskGraphActive() const override;
|
||||
|
||||
private:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Component base
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// \ref ComponentDescriptor::GetProvidedServices
|
||||
static void GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided);
|
||||
/// \ref ComponentDescriptor::GetIncompatibleServices
|
||||
static void GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
/// \ref ComponentDescriptor::GetDependentServices
|
||||
static void GetDependentServices(ComponentDescriptor::DependencyArrayType& dependent);
|
||||
/// \red ComponentDescriptor::Reflect
|
||||
static void Reflect(ReflectContext* reflection);
|
||||
|
||||
AZ::TaskExecutor* m_taskExecutor = nullptr;
|
||||
};
|
||||
}
|
||||
@@ -633,6 +633,8 @@ set(FILES
|
||||
Task/TaskGraph.cpp
|
||||
Task/TaskGraph.h
|
||||
Task/TaskGraph.inl
|
||||
Task/TaskGraphSystemComponent.h
|
||||
Task/TaskGraphSystemComponent.cpp
|
||||
Threading/ThreadSafeDeque.h
|
||||
Threading/ThreadSafeDeque.inl
|
||||
Threading/ThreadSafeObject.h
|
||||
|
||||
@@ -87,12 +87,6 @@ namespace AZStd
|
||||
|
||||
// construct/copy/destroy:
|
||||
thread();
|
||||
/**
|
||||
* \note thread_desc is AZStd extension.
|
||||
*/
|
||||
template <class F>
|
||||
explicit thread(F&& f, const thread_desc* desc = 0);
|
||||
|
||||
~thread();
|
||||
|
||||
thread(thread&& rhs)
|
||||
@@ -108,6 +102,15 @@ namespace AZStd
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class F, class... Args, typename = AZStd::enable_if_t<!AZStd::is_convertible_v<AZStd::decay_t<F>, thread_desc>>>
|
||||
explicit thread(F&& f, Args&&... args);
|
||||
|
||||
/**
|
||||
* \note thread_desc is AZStd extension.
|
||||
*/
|
||||
template<class F, class... Args>
|
||||
thread(const thread_desc& desc, F&& f, Args&&... args);
|
||||
|
||||
// Till we fully have RVALUES
|
||||
template <class F>
|
||||
explicit thread(Internal::thread_move_t<F> f);
|
||||
@@ -138,8 +141,8 @@ namespace AZStd
|
||||
//thread(AZStd::delegate<void ()> d,const thread_desc* desc = 0);
|
||||
|
||||
private:
|
||||
thread(thread&);
|
||||
thread& operator=(thread&);
|
||||
thread(const thread&) = delete;
|
||||
thread& operator=(const thread&) = delete;
|
||||
|
||||
native_thread_data_type m_thread;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user