merging latest dev

Signed-off-by: kberg-amzn <karlberg@amazon.com>
This commit is contained in:
kberg-amzn
2021-10-01 09:53:16 -07:00
782 changed files with 16514 additions and 12024 deletions
@@ -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>
+7 -10
View File
@@ -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
{
+14 -23
View File
@@ -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;
};
@@ -10,6 +10,8 @@
#include <unistd.h>
#include <sched.h>
#include <AzCore/std/tuple.h>
namespace AZStd
{
namespace Internal
@@ -22,12 +24,20 @@ namespace AZStd
//////////////////////////////////////////////////////////////////////////
// thread
template <class F>
inline thread::thread(F&& f, const thread_desc* desc)
template<class F, class... Args, typename>
thread::thread(F&& f, Args&&... args)
: thread(thread_desc{}, AZStd::forward<F>(f), AZStd::forward<Args>(args)...)
{}
template<class F, class... Args>
thread::thread(const thread_desc& desc, F&& f, Args&&... args)
{
Internal::thread_info* ti = Internal::create_thread_info(AZStd::forward<F>(f));
ti->m_name = desc ? desc->m_name : nullptr;
m_thread = Internal::create_thread(desc, ti);
auto threadfunc = [fn = AZStd::forward<F>(f), argsTuple = AZStd::make_tuple(AZStd::forward<Args>(args)...)]() mutable -> void
{
AZStd::apply(AZStd::move(fn), AZStd::move(argsTuple));
};
Internal::thread_info* ti = Internal::create_thread_info(AZStd::move(threadfunc));
m_thread = Internal::create_thread(&desc, ti);
}
inline bool thread::joinable() const
@@ -18,6 +18,8 @@ extern "C"
AZ_DLL_IMPORT unsigned long __stdcall GetCurrentThreadId(void);
}
#include <AzCore/std/tuple.h>
namespace AZStd
{
namespace Internal
@@ -30,11 +32,20 @@ namespace AZStd
//////////////////////////////////////////////////////////////////////////
// thread
template <class F>
inline thread::thread(F&& f, const thread_desc* desc)
template<class F, class... Args, typename>
thread::thread(F&& f, Args&&... args)
: thread(thread_desc{}, AZStd::forward<F>(f), AZStd::forward<Args>(args)...)
{}
template<class F, class... Args>
thread::thread(const thread_desc& desc, F&& f, Args&&... args)
{
Internal::thread_info* ti = Internal::create_thread_info(AZStd::forward<F>(f));
m_thread.m_handle = Internal::create_thread(desc, ti, &m_thread.m_id);
auto threadfunc = [fn = AZStd::forward<F>(f), argsTuple = AZStd::make_tuple(AZStd::forward<Args>(args)...)]() mutable -> void
{
AZStd::apply(AZStd::move(fn), AZStd::move(argsTuple));
};
Internal::thread_info* ti = Internal::create_thread_info(AZStd::move(threadfunc));
m_thread.m_handle = Internal::create_thread(&desc, ti, &m_thread.m_id);
}
inline bool thread::joinable() const
+77 -77
View File
@@ -195,18 +195,18 @@ namespace UnitTest
void test_thread_id_for_running_thread_is_not_default_constructed_id()
{
const thread_desc* desc = m_numThreadDesc ? &m_desc[0] : nullptr;
AZStd::thread t(AZStd::bind(&Parallel_Thread::do_nothing, this), desc);
const thread_desc desc = m_numThreadDesc ? m_desc[0] : thread_desc{};
AZStd::thread t(desc, AZStd::bind(&Parallel_Thread::do_nothing, this));
AZ_TEST_ASSERT(t.get_id() != AZStd::thread::id());
t.join();
}
void test_different_threads_have_different_ids()
{
const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
const thread_desc* desc2 = m_numThreadDesc ? &m_desc[1] : nullptr;
AZStd::thread t(AZStd::bind(&Parallel_Thread::do_nothing, this), desc1);
AZStd::thread t2(AZStd::bind(&Parallel_Thread::do_nothing, this), desc2);
const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
const thread_desc desc2 = m_numThreadDesc ? m_desc[1] : thread_desc{};
AZStd::thread t(desc1, AZStd::bind(&Parallel_Thread::do_nothing, this));
AZStd::thread t2(desc2, AZStd::bind(&Parallel_Thread::do_nothing, this));
AZ_TEST_ASSERT(t.get_id() != t2.get_id());
t.join();
t2.join();
@@ -214,13 +214,13 @@ namespace UnitTest
void test_thread_ids_have_a_total_order()
{
const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
const thread_desc* desc2 = m_numThreadDesc ? &m_desc[1] : nullptr;
const thread_desc* desc3 = m_numThreadDesc ? &m_desc[2] : nullptr;
const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
const thread_desc desc2 = m_numThreadDesc ? m_desc[1] : thread_desc{};
const thread_desc desc3 = m_numThreadDesc ? m_desc[2] : thread_desc{};
AZStd::thread t(AZStd::bind(&Parallel_Thread::do_nothing, this), desc1);
AZStd::thread t2(AZStd::bind(&Parallel_Thread::do_nothing, this), desc2);
AZStd::thread t3(AZStd::bind(&Parallel_Thread::do_nothing, this), desc3);
AZStd::thread t(desc1, AZStd::bind(&Parallel_Thread::do_nothing, this));
AZStd::thread t2(desc2, AZStd::bind(&Parallel_Thread::do_nothing, this));
AZStd::thread t3(desc3, AZStd::bind(&Parallel_Thread::do_nothing, this));
AZ_TEST_ASSERT(t.get_id() != t2.get_id());
AZ_TEST_ASSERT(t.get_id() != t3.get_id());
AZ_TEST_ASSERT(t2.get_id() != t3.get_id());
@@ -313,10 +313,10 @@ namespace UnitTest
void test_thread_id_of_running_thread_returned_by_this_thread_get_id()
{
const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
AZStd::thread::id id;
AZStd::thread t(AZStd::bind(&Parallel_Thread::get_thread_id, this, &id), desc1);
AZStd::thread t(desc1, AZStd::bind(&Parallel_Thread::get_thread_id, this, &id));
AZStd::thread::id t_id = t.get_id();
t.join();
AZ_TEST_ASSERT(id == t_id);
@@ -366,10 +366,10 @@ namespace UnitTest
void test_move_on_construction()
{
const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
AZStd::thread::id the_id;
AZStd::thread x;
x = AZStd::thread(AZStd::bind(&Parallel_Thread::do_nothing_id, this, &the_id), desc1);
x = AZStd::thread(desc1, AZStd::bind(&Parallel_Thread::do_nothing_id, this, &the_id));
AZStd::thread::id x_id = x.get_id();
x.join();
AZ_TEST_ASSERT(the_id == x_id);
@@ -377,8 +377,8 @@ namespace UnitTest
AZStd::thread make_thread(AZStd::thread::id* the_id)
{
const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
return AZStd::thread(AZStd::bind(&Parallel_Thread::do_nothing_id, this, the_id), desc1);
const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
return AZStd::thread(desc1, AZStd::bind(&Parallel_Thread::do_nothing_id, this, the_id));
}
void test_move_from_function_return()
@@ -430,9 +430,9 @@ namespace UnitTest
void do_test_creation()
{
const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
m_data = 0;
AZStd::thread t(AZStd::bind(&Parallel_Thread::simple_thread, this), desc1);
AZStd::thread t(desc1, AZStd::bind(&Parallel_Thread::simple_thread, this));
t.join();
AZ_TEST_ASSERT(m_data == 999);
}
@@ -445,9 +445,9 @@ namespace UnitTest
void do_test_id_comparison()
{
const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
AZStd::thread::id self = this_thread::get_id();
AZStd::thread thrd(AZStd::bind(&Parallel_Thread::comparison_thread, this, self), desc1);
AZStd::thread thrd(desc1, AZStd::bind(&Parallel_Thread::comparison_thread, this, self));
thrd.join();
}
@@ -476,10 +476,10 @@ namespace UnitTest
void do_test_creation_through_reference_wrapper()
{
const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
non_copyable_functor f;
AZStd::thread thrd(AZStd::ref(f), desc1);
AZStd::thread thrd(desc1, AZStd::ref(f));
thrd.join();
AZ_TEST_ASSERT(f.value == 999);
}
@@ -491,10 +491,10 @@ namespace UnitTest
void test_swap()
{
const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
const thread_desc* desc2 = m_numThreadDesc ? &m_desc[1] : nullptr;
AZStd::thread t(AZStd::bind(&Parallel_Thread::simple_thread, this), desc1);
AZStd::thread t2(AZStd::bind(&Parallel_Thread::simple_thread, this), desc2);
const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
const thread_desc desc2 = m_numThreadDesc ? m_desc[1] : thread_desc{};
AZStd::thread t(desc1, AZStd::bind(&Parallel_Thread::simple_thread, this));
AZStd::thread t2(desc2, AZStd::bind(&Parallel_Thread::simple_thread, this));
AZStd::thread::id id1 = t.get_id();
AZStd::thread::id id2 = t2.get_id();
@@ -512,7 +512,7 @@ namespace UnitTest
void run()
{
const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
// We need to have at least one processor
AZ_TEST_ASSERT(AZStd::thread::hardware_concurrency() >= 1);
@@ -520,18 +520,18 @@ namespace UnitTest
// Create thread to increment data till we need to
m_data = 0;
m_dataMax = 10;
AZStd::thread tr(AZStd::bind(&Parallel_Thread::increment_data, this), desc1);
AZStd::thread tr(desc1, AZStd::bind(&Parallel_Thread::increment_data, this));
tr.join();
AZ_TEST_ASSERT(m_data == m_dataMax);
m_data = 0;
AZStd::thread trDel(make_delegate(this, &Parallel_Thread::increment_data), desc1);
AZStd::thread trDel(desc1, make_delegate(this, &Parallel_Thread::increment_data));
trDel.join();
AZ_TEST_ASSERT(m_data == m_dataMax);
chrono::system_clock::time_point startTime = chrono::system_clock::now();
{
AZStd::thread tr1(AZStd::bind(&Parallel_Thread::sleep_thread, this, chrono::milliseconds(100)), desc1);
AZStd::thread tr1(desc1, AZStd::bind(&Parallel_Thread::sleep_thread, this, chrono::milliseconds(100)));
tr1.join();
}
auto sleepTime = chrono::system_clock::now() - startTime;
@@ -563,71 +563,71 @@ namespace UnitTest
{
MfTest x;
AZStd::function<void ()> func = AZStd::bind(&MfTest::f0, &x);
AZStd::thread(func, desc1).join();
AZStd::thread(desc1, func).join();
func = AZStd::bind(&MfTest::f0, AZStd::ref(x));
AZStd::thread(func, desc1).join();
AZStd::thread(desc1, func).join();
func = AZStd::bind(&MfTest::g0, &x);
AZStd::thread(func, desc1).join();
AZStd::thread(desc1, func).join();
func = AZStd::bind(&MfTest::g0, x);
AZStd::thread(func, desc1).join();
AZStd::thread(desc1, func).join();
func = AZStd::bind(&MfTest::g0, AZStd::ref(x));
AZStd::thread(func, desc1).join();
AZStd::thread(desc1, func).join();
//// 1
//thread( AZStd::bind(&MfTest::f1, &x, 1) , desc1).join();
//thread( AZStd::bind(&MfTest::f1, AZStd::ref(x), 1) , desc1).join();
//thread( AZStd::bind(&MfTest::g1, &x, 1) , desc1).join();
//thread( AZStd::bind(&MfTest::g1, x, 1) , desc1).join();
//thread( AZStd::bind(&MfTest::g1, AZStd::ref(x), 1) , desc1).join();
//thread( AZStd::bind(desc1, &MfTest::f1, &x, 1)).join();
//thread( AZStd::bind(desc1, &MfTest::f1, AZStd::ref(x), 1)).join();
//thread( AZStd::bind(desc1, &MfTest::g1, &x, 1)).join();
//thread( AZStd::bind(desc1, &MfTest::g1, x, 1)).join();
//thread( AZStd::bind(desc1, &MfTest::g1, AZStd::ref(x), 1)).join();
//// 2
//thread( AZStd::bind(&MfTest::f2, &x, 1, 2) , desc1).join();
//thread( AZStd::bind(&MfTest::f2, AZStd::ref(x), 1, 2) , desc1).join();
//thread( AZStd::bind(&MfTest::g2, &x, 1, 2) , desc1).join();
//thread( AZStd::bind(&MfTest::g2, x, 1, 2) , desc1).join();
//thread( AZStd::bind(&MfTest::g2, AZStd::ref(x), 1, 2) , desc1).join();
//thread( AZStd::bind(desc1, &MfTest::f2, &x, 1, 2)).join();
//thread( AZStd::bind(desc1, &MfTest::f2, AZStd::ref(x), 1, 2)).join();
//thread( AZStd::bind(desc1, &MfTest::g2, &x, 1, 2)).join();
//thread( AZStd::bind(desc1, &MfTest::g2, x, 1, 2)).join();
//thread( AZStd::bind(desc1, &MfTest::g2, AZStd::ref(x), 1, 2)).join();
//// 3
//thread( AZStd::bind(&MfTest::f3, &x, 1, 2, 3) , desc1).join();
//thread( AZStd::bind(&MfTest::f3, AZStd::ref(x), 1, 2, 3) , desc1).join();
//thread( AZStd::bind(&MfTest::g3, &x, 1, 2, 3) , desc1).join();
//thread( AZStd::bind(&MfTest::g3, x, 1, 2, 3) , desc1).join();
//thread( AZStd::bind(&MfTest::g3, AZStd::ref(x), 1, 2, 3) , desc1).join();
//thread( AZStd::bind(desc1, &MfTest::f3, &x, 1, 2, 3)).join();
//thread( AZStd::bind(desc1, &MfTest::f3, AZStd::ref(x), 1, 2, 3)).join();
//thread( AZStd::bind(desc1, &MfTest::g3, &x, 1, 2, 3)).join();
//thread( AZStd::bind(desc1, &MfTest::g3, x, 1, 2, 3)).join();
//thread( AZStd::bind(desc1, &MfTest::g3, AZStd::ref(x), 1, 2, 3)).join();
//// 4
//thread( AZStd::bind(&MfTest::f4, &x, 1, 2, 3, 4) , desc1).join();
//thread( AZStd::bind(&MfTest::f4, AZStd::ref(x), 1, 2, 3, 4) , desc1).join();
//thread( AZStd::bind(&MfTest::g4, &x, 1, 2, 3, 4) , desc1).join();
//thread( AZStd::bind(&MfTest::g4, x, 1, 2, 3, 4) , desc1).join();
//thread( AZStd::bind(&MfTest::g4, AZStd::ref(x), 1, 2, 3, 4) , desc1).join();
//thread( AZStd::bind(desc1, &MfTest::f4, &x, 1, 2, 3, 4)).join();
//thread( AZStd::bind(desc1, &MfTest::f4, AZStd::ref(x), 1, 2, 3, 4)).join();
//thread( AZStd::bind(desc1, &MfTest::g4, &x, 1, 2, 3, 4)).join();
//thread( AZStd::bind(desc1, &MfTest::g4, x, 1, 2, 3, 4)).join();
//thread( AZStd::bind(desc1, &MfTest::g4, AZStd::ref(x), 1, 2, 3, 4)).join();
//// 5
//thread( AZStd::bind(&MfTest::f5, &x, 1, 2, 3, 4, 5) , desc1).join();
//thread( AZStd::bind(&MfTest::f5, AZStd::ref(x), 1, 2, 3, 4, 5) , desc1).join();
//thread( AZStd::bind(&MfTest::g5, &x, 1, 2, 3, 4, 5) , desc1).join();
//thread( AZStd::bind(&MfTest::g5, x, 1, 2, 3, 4, 5) , desc1).join();
//thread( AZStd::bind(&MfTest::g5, AZStd::ref(x), 1, 2, 3, 4, 5) , desc1).join();
//thread( AZStd::bind(desc1, &MfTest::f5, &x, 1, 2, 3, 4, 5)).join();
//thread( AZStd::bind(desc1, &MfTest::f5, AZStd::ref(x), 1, 2, 3, 4, 5)).join();
//thread( AZStd::bind(desc1, &MfTest::g5, &x, 1, 2, 3, 4, 5)).join();
//thread( AZStd::bind(desc1, &MfTest::g5, x, 1, 2, 3, 4, 5)).join();
//thread( AZStd::bind(desc1, &MfTest::g5, AZStd::ref(x), 1, 2, 3, 4, 5)).join();
//// 6
//thread( AZStd::bind(&MfTest::f6, &x, 1, 2, 3, 4, 5, 6) , desc1).join();
//thread( AZStd::bind(&MfTest::f6, AZStd::ref(x), 1, 2, 3, 4, 5, 6) , desc1).join();
//thread( AZStd::bind(&MfTest::g6, &x, 1, 2, 3, 4, 5, 6) , desc1).join();
//thread( AZStd::bind(&MfTest::g6, x, 1, 2, 3, 4, 5, 6) , desc1).join();
//thread( AZStd::bind(&MfTest::g6, AZStd::ref(x), 1, 2, 3, 4, 5, 6) , desc1).join();
//thread( AZStd::bind(desc1, &MfTest::f6, &x, 1, 2, 3, 4, 5, 6)).join();
//thread( AZStd::bind(desc1, &MfTest::f6, AZStd::ref(x), 1, 2, 3, 4, 5, 6)).join();
//thread( AZStd::bind(desc1, &MfTest::g6, &x, 1, 2, 3, 4, 5, 6)).join();
//thread( AZStd::bind(desc1, &MfTest::g6, x, 1, 2, 3, 4, 5, 6)).join();
//thread( AZStd::bind(desc1, &MfTest::g6, AZStd::ref(x), 1, 2, 3, 4, 5, 6)).join();
//// 7
//thread( AZStd::bind(&MfTest::f7, &x, 1, 2, 3, 4, 5, 6, 7), desc1).join();
//thread( AZStd::bind(&MfTest::f7, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7), desc1).join();
//thread( AZStd::bind(&MfTest::g7, &x, 1, 2, 3, 4, 5, 6, 7), desc1).join();
//thread( AZStd::bind(&MfTest::g7, x, 1, 2, 3, 4, 5, 6, 7), desc1).join();
//thread( AZStd::bind(&MfTest::g7, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7), desc1).join();
//thread( AZStd::bind(desc1, &MfTest::f7, &x, 1, 2, 3, 4, 5, 6, 7)).join();
//thread( AZStd::bind(desc1, &MfTest::f7, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7)).join();
//thread( AZStd::bind(desc1, &MfTest::g7, &x, 1, 2, 3, 4, 5, 6, 7)).join();
//thread( AZStd::bind(desc1, &MfTest::g7, x, 1, 2, 3, 4, 5, 6, 7)).join();
//thread( AZStd::bind(desc1, &MfTest::g7, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7)).join();
//// 8
//thread( AZStd::bind(&MfTest::f8, &x, 1, 2, 3, 4, 5, 6, 7, 8) , desc1).join();
//thread( AZStd::bind(&MfTest::f8, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7, 8) , desc1).join();
//thread( AZStd::bind(&MfTest::g8, &x, 1, 2, 3, 4, 5, 6, 7, 8) , desc1).join();
//thread( AZStd::bind(&MfTest::g8, x, 1, 2, 3, 4, 5, 6, 7, 8) , desc1).join();
//thread( AZStd::bind(&MfTest::g8, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7, 8) , desc1).join();
//thread( AZStd::bind(desc1, &MfTest::f8, &x, 1, 2, 3, 4, 5, 6, 7, 8)).join();
//thread( AZStd::bind(desc1, &MfTest::f8, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7, 8)).join();
//thread( AZStd::bind(desc1, &MfTest::g8, &x, 1, 2, 3, 4, 5, 6, 7, 8)).join();
//thread( AZStd::bind(desc1, &MfTest::g8, x, 1, 2, 3, 4, 5, 6, 7, 8)).join();
//thread( AZStd::bind(desc1, &MfTest::g8, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7, 8)).join();
AZ_TEST_ASSERT(x.m_hash == 1366);
}
@@ -8,6 +8,7 @@
#include <SerializeContextFixture.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/std/any.h>
#include <AzCore/std/containers/variant.h>
#include <AzCore/Component/Entity.h>
@@ -6,6 +6,7 @@
*
*/
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/Console.h>
#include <AzCore/Interface/Interface.h>
@@ -365,6 +366,42 @@ namespace UnitTest
};
static constexpr AZStd::chrono::seconds MaxDispatchTimeoutSeconds = BaseAssetManagerTest::DefaultTimeoutSeconds * 12;
template <typename Pred>
bool DispatchEventsUntilCondition(AZ::Data::AssetManager& assetManager, Pred&& conditionPredicate,
AZStd::chrono::seconds logIntervalSeconds = BaseAssetManagerTest::DefaultTimeoutSeconds,
AZStd::chrono::seconds maxTimeoutSeconds = MaxDispatchTimeoutSeconds)
{
// If the Max Timeout is hit the test will be marked as a failure
AZStd::chrono::time_point dispatchEventTimeStart = AZStd::chrono::system_clock::now();
AZStd::chrono::seconds dispatchEventNextLogTime = logIntervalSeconds;
while (!conditionPredicate())
{
AZStd::chrono::time_point currentTime = AZStd::chrono::system_clock::now();
if (AZStd::chrono::seconds elapsedTime{ currentTime - dispatchEventTimeStart };
elapsedTime >= dispatchEventNextLogTime)
{
const testing::TestInfo* test_info = ::testing::UnitTest::GetInstance()->current_test_info();
AZ_Printf("AssetManagerLoadingTest", "The DispatchEventsUntiTimeout function has been waiting for %llu seconds"
" in test %s.%s", elapsedTime.count(), test_info->test_case_name(), test_info->name());
// Update the next log time to be the next multiple of DefaultTimeout Seconds
// after current elapsed time
dispatchEventNextLogTime = elapsedTime + logIntervalSeconds - ((elapsedTime + logIntervalSeconds) % logIntervalSeconds);
if (elapsedTime >= maxTimeoutSeconds)
{
return false;
}
}
assetManager.DispatchEvents();
AZStd::this_thread::yield();
}
return true;
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_ASSET_MANAGER_FLOOD_TEST
TEST_F(AssetJobsFloodTest, DISABLED_FloodTest)
#else
@@ -1357,42 +1394,74 @@ namespace UnitTest
m_assetHandlerAndCatalog->m_numCreations = 0;
m_assetHandlerAndCatalog->m_numDestructions = 0;
{
ContainerReadyListener containerLoadingCompleteListener(NoLoadAssetId);
OnAssetReadyListener readyListener(NoLoadAssetId, azrtti_typeid<AssetWithAssetReference>());
OnAssetReadyListener depenencyListener(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>());
OnAssetReadyListener dependencyListener(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>());
SCOPED_TRACE("LoadDependencies_BehaviorObeyed");
auto AssetOnlyReady = [&readyListener]() -> bool
{
return readyListener.m_ready;
};
auto AssetAndDependencyReady = [&readyListener, &dependencyListener]() -> bool
{
return readyListener.m_ready && dependencyListener.m_ready;
};
auto AssetContainerReady = [&containerLoadingCompleteListener]() -> bool
{
return containerLoadingCompleteListener.m_ready;
};
auto noLoadRef = m_testAssetManager->GetAsset(NoLoadAssetId, azrtti_typeid<AssetWithAssetReference>(),
AZ::Data::AssetLoadBehavior::Default);
auto maxTimeout = AZStd::chrono::system_clock::now() + DefaultTimeoutSeconds;
// Dispatch AssetBus events until the NoLoadAssetId has signaled an OnAssetReady
// event or the timeout has been reached
EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetOnlyReady))
<< "The DispatchEventsUntiTimeout function has not completed in "
<< MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n";
// Dispatch AssetBus events until the asset container used to load
// NoLoadAssetId has signaled an OnAssetContainerReady event
// or the timeout has been reached
// Wait until the current asset container has finished loading the NoLoadAssetId
// before trigger another load
// If the wait does not occur here, most likely what would occur is
// the AssetManager::m_ownedAssetContainers object is still loading the NoLoadAssetId
// using the default AssetLoadParameters
// If a call to GetAsset occurs at this point while the Asset is still loading
// it will ignore the new loadParams below and instead just re-use the existing
// AssetContainerReader instance, resulting in the dependent MyAsset2Id not
// being loaded
// The function that can return an existing AssetContainer instance is the
// AssetManager::GetAssetContainer. Since it can be in the middle of a load,
// updating the AssetLoadParams would have an effect on the current in progress
// load
EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetContainerReady))
<< "The DispatchEventsUntiTimeout function has not completed in "
<< MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n";
// Reset the ContainerLoadingComplete ready status back to 0
containerLoadingCompleteListener.m_ready = 0;
while (!readyListener.m_ready)
{
m_testAssetManager->DispatchEvents();
if (AZStd::chrono::system_clock::now() > maxTimeout)
{
break;
}
AZStd::this_thread::yield();
}
EXPECT_EQ(readyListener.m_ready, 1);
EXPECT_EQ(depenencyListener.m_ready, 0);
AZ::Data::AssetLoadParameters loadParams(nullptr, AZ::Data::AssetDependencyLoadRules::LoadAll);
loadParams.m_reloadMissingDependencies = true;
auto loadDependencyRef = m_testAssetManager->GetAsset(NoLoadAssetId, azrtti_typeid<AssetWithAssetReference>(),
AZ::Data::AssetLoadBehavior::Default, loadParams);
while (!depenencyListener.m_ready || !readyListener.m_ready)
{
m_testAssetManager->DispatchEvents();
if (AZStd::chrono::system_clock::now() > maxTimeout)
{
break;
}
AZStd::this_thread::yield();
}
// Dispatch AssetBus events until the NoLoadAssetId and the MyAsset2Id has signaled
// an OnAssetReady event or the timeout has been reached
EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetAndDependencyReady))
<< "The DispatchEventsUntiTimeout function has not completed in "
<< MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n";
EXPECT_EQ(readyListener.m_ready, 1);
EXPECT_EQ(depenencyListener.m_ready, 1);
EXPECT_EQ(dependencyListener.m_ready, 1);
EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetContainerReady))
<< "The DispatchEventsUntiTimeout function has not completed in "
<< MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n";
}
CheckFinishedCreationsAndDestructions();
@@ -6,6 +6,7 @@
*
*/
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/IO/SystemFile.h>
@@ -7,6 +7,7 @@
*/
#include <Tests/Asset/BaseAssetManagerTest.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/IO/SystemFile.h>
@@ -10,6 +10,7 @@
#include <AzCore/Asset/AssetJsonSerializer.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/IO/Streamer/StreamerComponent.h>
#include <AzCore/Jobs/JobManagerComponent.h>
#include <AzCore/Jobs/JobManager.h>
@@ -6,6 +6,7 @@
*
*/
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/IO/SystemFile.h>
+5 -5
View File
@@ -151,7 +151,7 @@ namespace UnitTest
AZStd::thread m_threads[m_maxNumThreads];
for (unsigned int i = 0; i < m_maxNumThreads; ++i)
{
m_threads[i] = AZStd::thread(AZStd::bind(&SystemAllocatorTest::ThreadFunc, this), &m_desc[i]);
m_threads[i] = AZStd::thread(m_desc[i], AZStd::bind(&SystemAllocatorTest::ThreadFunc, this));
// give some time offset to the threads so we can test alloc and dealloc at the same time.
//AZStd::this_thread::sleep_for(AZStd::chrono::microseconds(500));
}
@@ -286,7 +286,7 @@ namespace UnitTest
AZStd::thread m_threads[m_maxNumThreads];
for (unsigned int i = 0; i < m_maxNumThreads; ++i)
{
m_threads[i] = AZStd::thread(AZStd::bind(&SystemAllocatorTest::ThreadFunc, this), &m_desc[i]);
m_threads[i] = AZStd::thread(m_desc[i], AZStd::bind(&SystemAllocatorTest::ThreadFunc, this));
// give some time offset to the threads so we can test alloc and dealloc at the same time.
AZStd::this_thread::sleep_for(AZStd::chrono::microseconds(500));
}
@@ -724,7 +724,7 @@ namespace UnitTest
AZStd::thread m_threads[m_maxNumThreads];
for (unsigned int i = 0; i < m_maxNumThreads; ++i)
{
m_threads[i] = AZStd::thread(AZStd::bind(&ThreadPoolAllocatorTest::AllocDeallocFunc, this), &m_desc[i]);
m_threads[i] = AZStd::thread(m_desc[i], AZStd::bind(&ThreadPoolAllocatorTest::AllocDeallocFunc, this));
}
for (unsigned int i = 0; i < m_maxNumThreads; ++i)
@@ -743,12 +743,12 @@ namespace UnitTest
for (unsigned int i = m_maxNumThreads/2; i <m_maxNumThreads; ++i)
{
m_threads[i] = AZStd::thread(AZStd::bind(&ThreadPoolAllocatorTest::SharedDeAlloc, this), &m_desc[i]);
m_threads[i] = AZStd::thread(m_desc[i], AZStd::bind(&ThreadPoolAllocatorTest::SharedDeAlloc, this));
}
for (unsigned int i = 0; i < m_maxNumThreads/2; ++i)
{
m_threads[i] = AZStd::thread(AZStd::bind(&ThreadPoolAllocatorTest::SharedAlloc, this), &m_desc[i]);
m_threads[i] = AZStd::thread(m_desc[i], AZStd::bind(&ThreadPoolAllocatorTest::SharedAlloc, this));
}
for (unsigned int i = 0; i < m_maxNumThreads/2; ++i)
@@ -9,6 +9,7 @@
#include "FileIOBaseTestTypes.h"
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/SerializeContext.h>
+81 -3
View File
@@ -34,7 +34,7 @@ namespace UnitTest
AZ::AllocatorInstance<AZ::PoolAllocator>::Create();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
m_executor = aznew TaskExecutor(4);
m_executor = aznew TaskExecutor();
}
void TearDown() override
@@ -236,6 +236,82 @@ namespace UnitTest
EXPECT_EQ(x, 1);
}
TEST_F(TaskGraphTestFixture, SingleTask)
{
AZStd::atomic_int32_t x = 0;
TaskGraph graph;
graph.AddTask(
defaultTD,
[&x]
{
x = 1;
});
TaskGraphEvent ev;
graph.SubmitOnExecutor(*m_executor, &ev);
ev.Wait();
EXPECT_EQ(1, x);
}
TEST_F(TaskGraphTestFixture, SingleTaskChain)
{
AZStd::atomic_int32_t x = 0;
TaskGraph graph;
auto a = graph.AddTask(
defaultTD,
[&x]
{
x += 1;
});
auto b = graph.AddTask(
defaultTD,
[&x]
{
x += 1;
});
b.Precedes(a);
TaskGraphEvent ev;
graph.SubmitOnExecutor(*m_executor, &ev);
ev.Wait();
EXPECT_EQ(2, x);
}
TEST_F(TaskGraphTestFixture, MultipleIndependentTaskChains)
{
AZStd::atomic_int32_t x = 0;
constexpr int numChains = 5;
TaskGraph graph;
for( int i = 0; i < numChains; ++i)
{
auto a = graph.AddTask(
defaultTD,
[&x]
{
x += 1;
});
auto b = graph.AddTask(
defaultTD,
[&x]
{
x += 1;
});
b.Precedes(a);
}
TaskGraphEvent ev;
graph.SubmitOnExecutor(*m_executor, &ev);
ev.Wait();
EXPECT_EQ(2*numChains, x);
}
TEST_F(TaskGraphTestFixture, VariadicInterface)
{
int x = 0;
@@ -388,6 +464,7 @@ namespace UnitTest
EXPECT_EQ(3, x);
}
// Waiting inside a task is disallowed , test that it fails correctly
TEST_F(TaskGraphTestFixture, SpawnSubgraph)
{
AZStd::atomic<int> x = 0;
@@ -434,7 +511,10 @@ namespace UnitTest
f.Precedes(g);
TaskGraphEvent ev;
subgraph.SubmitOnExecutor(*m_executor, &ev);
// TaskGraphEvent::Wait asserts if called on a worker thread, suppress & validate assert
AZ_TEST_START_TRACE_SUPPRESSION;
ev.Wait();
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
});
auto d = graph.AddTask(
defaultTD,
@@ -464,8 +544,6 @@ namespace UnitTest
TaskGraphEvent ev;
graph.SubmitOnExecutor(*m_executor, &ev);
ev.Wait();
EXPECT_EQ(3 | 0b100000, x);
}
TEST_F(TaskGraphTestFixture, RetainedGraph)
@@ -7,6 +7,7 @@
*/
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/std/containers/unordered_set.h>
using namespace AZ;