Merge branch 'development' of https://github.com/o3de/o3de into Network/olexl/nettransform_local_for_children_cr
This commit is contained in:
@@ -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>(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -205,12 +207,29 @@ namespace AZ
|
||||
|
||||
m_thread = AZStd::thread{ [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()
|
||||
{
|
||||
m_active.store(false, AZStd::memory_order_release);
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <AzCore/NativeUI/NativeUISystemComponent.h>
|
||||
#include <AzCore/Module/ModuleManagerBus.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Task/TaskGraphSystemComponent.h>
|
||||
|
||||
#include <AzFramework/Asset/SimpleAsset.h>
|
||||
#include <AzFramework/Asset/AssetBundleManifest.h>
|
||||
@@ -295,6 +296,7 @@ namespace AzFramework
|
||||
azrtti_typeid<AZ::ScriptSystemComponent>(),
|
||||
azrtti_typeid<AZ::JobManagerComponent>(),
|
||||
azrtti_typeid<AZ::SliceSystemComponent>(),
|
||||
azrtti_typeid<AZ::TaskGraphSystemComponent>(),
|
||||
|
||||
azrtti_typeid<AzFramework::AssetCatalogComponent>(),
|
||||
azrtti_typeid<AzFramework::CustomAssetTypeComponent>(),
|
||||
|
||||
+13
-1
@@ -11,6 +11,7 @@
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeNotificationBus.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -73,7 +74,18 @@ namespace AzToolsFramework
|
||||
m_focusRoot = entityId;
|
||||
FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, m_focusRoot);
|
||||
|
||||
// TODO - If m_focusRoot != AZ::EntityId(), activate focus mode via ViewportEditorModeTrackerInterface; else, deactivate focus mode
|
||||
if (auto tracker = AZ::Interface<ViewportEditorModeTrackerInterface>::Get();
|
||||
tracker != nullptr)
|
||||
{
|
||||
if (!m_focusRoot.IsValid() && entityId.IsValid())
|
||||
{
|
||||
tracker->ActivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Focus);
|
||||
}
|
||||
else if (m_focusRoot.IsValid() && !entityId.IsValid())
|
||||
{
|
||||
tracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Focus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::ClearFocusRoot([[maybe_unused]] AzFramework::EntityContextId entityContextId)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <Atom/Features/MatrixUtility.azsli>
|
||||
#include <Atom/Features/Decals/DecalTextureUtil.azsli>
|
||||
#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
|
||||
#include <Atom/RPI/TangentSpace.azsli>
|
||||
|
||||
void ApplyDecal(uint currDecalIndex, inout Surface surface);
|
||||
|
||||
@@ -47,9 +48,10 @@ void ApplyDecal(uint currDecalIndex, inout Surface surface)
|
||||
ViewSrg::Decal decal = ViewSrg::m_decals[currDecalIndex];
|
||||
|
||||
float3x3 decalRot = MatrixFromQuaternion(decal.m_quaternion);
|
||||
|
||||
decalRot = transpose(decalRot);
|
||||
|
||||
float3 localPos = surface.position - decal.m_position;
|
||||
localPos = mul(localPos, decalRot);
|
||||
localPos = mul(decalRot, localPos);
|
||||
|
||||
float3 decalUVW = localPos * rcp(decal.m_halfSize);
|
||||
if(decalUVW.x >= -1.0f && decalUVW.x <= 1.0f &&
|
||||
@@ -63,30 +65,39 @@ void ApplyDecal(uint currDecalIndex, inout Surface surface)
|
||||
decalUVW.y *= -1;
|
||||
|
||||
float3 decalUV = float3(decalUVW.xy * 0.5f + 0.5f, textureIndex);
|
||||
|
||||
|
||||
float3 decalSample;
|
||||
|
||||
float4 baseMap = 0;
|
||||
float2 normalMap = 0;
|
||||
switch(textureArrayIndex)
|
||||
{
|
||||
case 0:
|
||||
baseMap = ViewSrg::m_decalTextureArray0.Sample(PassSrg::LinearSampler, decalUV);
|
||||
baseMap = ViewSrg::m_decalTextureArrayDiffuse0.Sample(PassSrg::LinearSampler, decalUV);
|
||||
normalMap = ViewSrg::m_decalTextureArrayNormalMaps0.Sample(PassSrg::LinearSampler, decalUV);
|
||||
break;
|
||||
case 1:
|
||||
baseMap = ViewSrg::m_decalTextureArray1.Sample(PassSrg::LinearSampler, decalUV);
|
||||
baseMap = ViewSrg::m_decalTextureArrayDiffuse1.Sample(PassSrg::LinearSampler, decalUV);
|
||||
normalMap = ViewSrg::m_decalTextureArrayNormalMaps1.Sample(PassSrg::LinearSampler, decalUV);
|
||||
break;
|
||||
case 2:
|
||||
baseMap = ViewSrg::m_decalTextureArray2.Sample(PassSrg::LinearSampler, decalUV);
|
||||
baseMap = ViewSrg::m_decalTextureArrayDiffuse2.Sample(PassSrg::LinearSampler, decalUV);
|
||||
normalMap = ViewSrg::m_decalTextureArrayNormalMaps2.Sample(PassSrg::LinearSampler, decalUV);
|
||||
break;
|
||||
case 3:
|
||||
baseMap = ViewSrg::m_decalTextureArray3.Sample(PassSrg::LinearSampler, decalUV);
|
||||
baseMap = ViewSrg::m_decalTextureArrayDiffuse3.Sample(PassSrg::LinearSampler, decalUV);
|
||||
normalMap = ViewSrg::m_decalTextureArrayNormalMaps3.Sample(PassSrg::LinearSampler, decalUV);
|
||||
break;
|
||||
case 4:
|
||||
baseMap = ViewSrg::m_decalTextureArray4.Sample(PassSrg::LinearSampler, decalUV);
|
||||
break;
|
||||
baseMap = ViewSrg::m_decalTextureArrayDiffuse4.Sample(PassSrg::LinearSampler, decalUV);
|
||||
normalMap = ViewSrg::m_decalTextureArrayNormalMaps4.Sample(PassSrg::LinearSampler, decalUV);
|
||||
break;
|
||||
}
|
||||
|
||||
float opacity = baseMap.a * decal.m_opacity * GetDecalAttenuation(surface.normal, decalRot[2], decal.m_angleAttenuation);
|
||||
surface.albedo = lerp(surface.albedo, baseMap.rgb, opacity);
|
||||
surface.albedo = lerp(surface.albedo, baseMap.rgb, opacity);
|
||||
|
||||
float3 normalMapWS = GetWorldSpaceNormal(normalMap, decalRot[2], decalRot[0], decalRot[1], 1.0f);
|
||||
surface.normal = normalize(lerp(surface.normal, normalMapWS, opacity));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,18 @@ partial ShaderResourceGroup ViewSrg
|
||||
// e.g. m_decalTextureArray0 might store 24 textures @128x128,
|
||||
// m_decalTextureArray1 might store 16 * 256x256
|
||||
// and m_decalTextureArray2 might store 4 @ 512x512
|
||||
|
||||
Texture2DArray<float4> m_decalTextureArray0;
|
||||
Texture2DArray<float4> m_decalTextureArray1;
|
||||
Texture2DArray<float4> m_decalTextureArray2;
|
||||
Texture2DArray<float4> m_decalTextureArray3;
|
||||
Texture2DArray<float4> m_decalTextureArray4;
|
||||
// This must match the variable NumTextureArrays in DecalTextureArrayFeatureProcessor.h
|
||||
Texture2DArray<float4> m_decalTextureArrayDiffuse0;
|
||||
Texture2DArray<float4> m_decalTextureArrayDiffuse1;
|
||||
Texture2DArray<float4> m_decalTextureArrayDiffuse2;
|
||||
Texture2DArray<float4> m_decalTextureArrayDiffuse3;
|
||||
Texture2DArray<float4> m_decalTextureArrayDiffuse4;
|
||||
|
||||
Texture2DArray<float4> m_decalTextureArrayNormalMaps0;
|
||||
Texture2DArray<float4> m_decalTextureArrayNormalMaps1;
|
||||
Texture2DArray<float4> m_decalTextureArrayNormalMaps2;
|
||||
Texture2DArray<float4> m_decalTextureArrayNormalMaps3;
|
||||
Texture2DArray<float4> m_decalTextureArrayNormalMaps4;
|
||||
|
||||
uint m_decalCount;
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -5,7 +5,6 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "DecalTextureArray.h"
|
||||
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
|
||||
#include <Atom/RPI.Reflect/Image/StreamingImageAssetCreator.h>
|
||||
@@ -24,7 +23,16 @@ namespace AZ
|
||||
{
|
||||
namespace
|
||||
{
|
||||
static const char* BaseColorTextureMapName = "baseColor.textureMap";
|
||||
static AZ::Name GetMapName(const DecalMapType mapType)
|
||||
{
|
||||
// Using local static to avoid cost of creating AZ::Name. Also so that this can be called from other static functions
|
||||
static AZStd::array<AZ::Name, DecalMapType_Num> mapNames =
|
||||
{
|
||||
AZ::Name("baseColor.textureMap"),
|
||||
AZ::Name("normal.textureMap")
|
||||
};
|
||||
return mapNames[mapType];
|
||||
}
|
||||
|
||||
static AZ::Data::AssetId GetImagePoolId()
|
||||
{
|
||||
@@ -40,6 +48,7 @@ namespace AZ
|
||||
return asset;
|
||||
}
|
||||
|
||||
// Extract exactly which texture asset we need to load from the given material and map type (diffuse, normal, etc).
|
||||
static AZ::Data::Asset<AZ::RPI::StreamingImageAsset> GetStreamingImageAsset(const AZ::RPI::MaterialAsset& materialAsset, const AZ::Name& propertyName)
|
||||
{
|
||||
if (!materialAsset.IsReady())
|
||||
@@ -78,11 +87,6 @@ namespace AZ
|
||||
const AZ::RPI::MaterialAsset* materialAsset = materialAssetData.GetAs<AZ::RPI::MaterialAsset>();
|
||||
return GetStreamingImageAsset(*materialAsset, propertyName);
|
||||
}
|
||||
|
||||
AZ::Data::Asset<AZ::RPI::StreamingImageAsset> GetBaseColorImageAsset(const AZ::Data::Asset<Data::AssetData> materialAssetData)
|
||||
{
|
||||
return GetStreamingImageAsset(materialAssetData, AZ::Name(BaseColorTextureMapName));
|
||||
}
|
||||
}
|
||||
|
||||
int DecalTextureArray::FindMaterial(const AZ::Data::AssetId materialAssetId) const
|
||||
@@ -103,7 +107,7 @@ namespace AZ
|
||||
{
|
||||
AZ_Error("DecalTextureArray", FindMaterial(materialAssetId) == -1, "Adding material when it already exists in the array");
|
||||
// Invalidate the existing texture array, as we need to repack it taking into account the new material.
|
||||
m_textureArrayPacked = nullptr;
|
||||
AZStd::fill(m_textureArrayPacked.begin(), m_textureArrayPacked.end(), nullptr);
|
||||
|
||||
MaterialData materialData;
|
||||
materialData.m_materialAssetId = materialAssetId;
|
||||
@@ -122,42 +126,42 @@ namespace AZ
|
||||
return m_materials[index].m_materialAssetId;
|
||||
}
|
||||
|
||||
RHI::Size DecalTextureArray::GetImageDimensions() const
|
||||
RHI::Size DecalTextureArray::GetImageDimensions(const DecalMapType mapType) const
|
||||
{
|
||||
AZ_Assert(m_materials.size() > 0, "GetImageDimensions() cannot be called until at least one material has been added");
|
||||
const int iter = m_materials.begin();
|
||||
// All textures in a texture array must have the same size, so just pick the first
|
||||
const MaterialData& firstMaterial = m_materials[iter];
|
||||
const auto& baseColorAsset = GetBaseColorImageAsset(firstMaterial.m_materialAssetData);
|
||||
const auto& baseColorAsset = GetStreamingImageAsset(firstMaterial.m_materialAssetData, GetMapName(mapType));
|
||||
return baseColorAsset->GetImageDescriptor().m_size;
|
||||
}
|
||||
|
||||
const AZ::Data::Instance<AZ::RPI::StreamingImage>& DecalTextureArray::GetPackedTexture() const
|
||||
const AZ::Data::Instance<AZ::RPI::StreamingImage>& DecalTextureArray::GetPackedTexture(const DecalMapType mapType) const
|
||||
{
|
||||
return m_textureArrayPacked;
|
||||
return m_textureArrayPacked[mapType];
|
||||
}
|
||||
|
||||
bool DecalTextureArray::IsValidDecalMaterial(const AZ::RPI::MaterialAsset& materialAsset)
|
||||
{
|
||||
return GetStreamingImageAsset(materialAsset, AZ::Name(BaseColorTextureMapName)).IsReady();
|
||||
return GetStreamingImageAsset(materialAsset, GetMapName(DecalMapType_Diffuse)).IsReady();
|
||||
}
|
||||
|
||||
AZ::Data::Asset<AZ::RPI::ImageMipChainAsset> DecalTextureArray::BuildPackedMipChainAsset(const size_t numTexturesToCreate)
|
||||
AZ::Data::Asset<AZ::RPI::ImageMipChainAsset> DecalTextureArray::BuildPackedMipChainAsset(const DecalMapType mapType, const size_t numTexturesToCreate)
|
||||
{
|
||||
RPI::ImageMipChainAssetCreator assetCreator;
|
||||
const uint32_t mipLevels = GetNumMipLevels();
|
||||
const uint32_t mipLevels = GetNumMipLevels(mapType);
|
||||
|
||||
assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), static_cast<uint16_t>(mipLevels), aznumeric_cast<uint16_t>(numTexturesToCreate));
|
||||
assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), aznumeric_cast<uint16_t>(mipLevels), aznumeric_cast<uint16_t>(numTexturesToCreate));
|
||||
|
||||
for (uint32_t mipLevel = 0; mipLevel < mipLevels; ++mipLevel)
|
||||
{
|
||||
const auto& layout = GetLayout(mipLevel);
|
||||
const auto& layout = GetLayout(mapType, mipLevel);
|
||||
assetCreator.BeginMip(layout);
|
||||
|
||||
for (int i = 0; i < m_materials.array_size(); ++i)
|
||||
{
|
||||
const auto rawData = GetRawImageData(i, mipLevel);
|
||||
assetCreator.AddSubImage(rawData.data(), rawData.size());
|
||||
const auto imageData = GetRawImageData(GetMapName(mapType), i, mipLevel);
|
||||
assetCreator.AddSubImage(imageData.data(), imageData.size());
|
||||
}
|
||||
|
||||
assetCreator.EndMip();
|
||||
@@ -169,10 +173,12 @@ namespace AZ
|
||||
return AZStd::move(asset);
|
||||
}
|
||||
|
||||
RHI::ImageDescriptor DecalTextureArray::CreatePackedImageDescriptor(const uint16_t arraySize, const uint16_t mipLevels) const
|
||||
RHI::ImageDescriptor DecalTextureArray::CreatePackedImageDescriptor(
|
||||
const DecalMapType mapType, const uint16_t arraySize, const uint16_t mipLevels) const
|
||||
{
|
||||
const RHI::Size imageDimensions = GetImageDimensions();
|
||||
RHI::ImageDescriptor imageDescriptor = RHI::ImageDescriptor::Create2DArray(RHI::ImageBindFlags::ShaderRead, imageDimensions.m_width, imageDimensions.m_height, arraySize, GetFormat());
|
||||
const RHI::Size imageDimensions = GetImageDimensions(mapType);
|
||||
RHI::ImageDescriptor imageDescriptor = RHI::ImageDescriptor::Create2DArray(
|
||||
RHI::ImageBindFlags::ShaderRead, imageDimensions.m_width, imageDimensions.m_height, arraySize, GetFormat(mapType));
|
||||
imageDescriptor.m_mipLevels = mipLevels;
|
||||
return imageDescriptor;
|
||||
}
|
||||
@@ -189,21 +195,34 @@ namespace AZ
|
||||
}
|
||||
|
||||
const size_t numTexturesToCreate = m_materials.array_size();
|
||||
const auto mipChainAsset = BuildPackedMipChainAsset(numTexturesToCreate);
|
||||
RHI::ImageViewDescriptor imageViewDescriptor;
|
||||
imageViewDescriptor.m_isArray = true;
|
||||
for (int i = 0; i < DecalMapType_Num; ++i)
|
||||
{
|
||||
const DecalMapType mapType = aznumeric_cast<DecalMapType>(i);
|
||||
if (!AreAllTextureMapsPresent(mapType))
|
||||
{
|
||||
AZ_Warning("DecalTextureArray", true, "Missing decal texture maps for %s. Please make sure all maps of this type are present.\n", GetMapName(mapType).GetCStr());
|
||||
m_textureArrayPacked[i] = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
RPI::StreamingImageAssetCreator assetCreator;
|
||||
assetCreator.Begin(Data::AssetId(Uuid::CreateRandom()));
|
||||
assetCreator.SetPoolAssetId(GetImagePoolId());
|
||||
assetCreator.SetFlags(RPI::StreamingImageFlags::None);
|
||||
assetCreator.SetImageDescriptor(CreatePackedImageDescriptor(aznumeric_cast<uint16_t>(numTexturesToCreate), GetNumMipLevels()));
|
||||
assetCreator.SetImageViewDescriptor(imageViewDescriptor);
|
||||
assetCreator.AddMipChainAsset(*mipChainAsset);
|
||||
Data::Asset<RPI::StreamingImageAsset> packedAsset;
|
||||
const bool createdOk = assetCreator.End(packedAsset);
|
||||
AZ_Error("TextureArrayData", createdOk, "Pack() call failed.");
|
||||
m_textureArrayPacked = createdOk ? RPI::StreamingImage::FindOrCreate(packedAsset) : nullptr;
|
||||
const auto mipChainAsset = BuildPackedMipChainAsset(mapType, numTexturesToCreate);
|
||||
RHI::ImageViewDescriptor imageViewDescriptor;
|
||||
imageViewDescriptor.m_isArray = true;
|
||||
|
||||
RPI::StreamingImageAssetCreator assetCreator;
|
||||
assetCreator.Begin(Data::AssetId(Uuid::CreateRandom()));
|
||||
assetCreator.SetPoolAssetId(GetImagePoolId());
|
||||
assetCreator.SetFlags(RPI::StreamingImageFlags::None);
|
||||
assetCreator.SetImageDescriptor(
|
||||
CreatePackedImageDescriptor(mapType, aznumeric_cast<uint16_t>(numTexturesToCreate), GetNumMipLevels(mapType)));
|
||||
assetCreator.SetImageViewDescriptor(imageViewDescriptor);
|
||||
assetCreator.AddMipChainAsset(*mipChainAsset);
|
||||
Data::Asset<RPI::StreamingImageAsset> packedAsset;
|
||||
const bool createdOk = assetCreator.End(packedAsset);
|
||||
AZ_Error("TextureArrayData", createdOk, "Pack() call failed.");
|
||||
m_textureArrayPacked[i] = createdOk ? RPI::StreamingImage::FindOrCreate(packedAsset) : nullptr;
|
||||
|
||||
}
|
||||
|
||||
// Free unused memory
|
||||
ClearAssets();
|
||||
@@ -225,29 +244,30 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t DecalTextureArray::GetNumMipLevels() const
|
||||
uint16_t DecalTextureArray::GetNumMipLevels(const DecalMapType mapType) const
|
||||
{
|
||||
AZ_Assert(m_materials.size() > 0, "GetNumMipLevels() cannot be called until at least one material has been added");
|
||||
// All decals in a texture array must have the same number of mips, so just pick the first
|
||||
const int iter = m_materials.begin();
|
||||
const MaterialData& firstMaterial = m_materials[iter];
|
||||
const auto& baseColorAsset = GetBaseColorImageAsset(firstMaterial.m_materialAssetData);
|
||||
return baseColorAsset->GetImageDescriptor().m_mipLevels;
|
||||
const auto& imageAsset = GetStreamingImageAsset(firstMaterial.m_materialAssetData, GetMapName(mapType));
|
||||
return imageAsset->GetImageDescriptor().m_mipLevels;
|
||||
}
|
||||
|
||||
RHI::ImageSubresourceLayout DecalTextureArray::GetLayout(int mip) const
|
||||
RHI::ImageSubresourceLayout DecalTextureArray::GetLayout(const DecalMapType mapType, int mip) const
|
||||
{
|
||||
AZ_Assert(m_materials.size() > 0, "GetLayout() cannot be called unless at least one material has been added");
|
||||
|
||||
const int iter = m_materials.begin();
|
||||
const auto& descriptor = GetBaseColorImageAsset(m_materials[iter].m_materialAssetData)->GetImageDescriptor();
|
||||
const auto& descriptor =
|
||||
GetStreamingImageAsset(m_materials[iter].m_materialAssetData, GetMapName(mapType))->GetImageDescriptor();
|
||||
RHI::Size mipSize = descriptor.m_size;
|
||||
mipSize.m_width >>= mip;
|
||||
mipSize.m_height >>= mip;
|
||||
return AZ::RHI::GetImageSubresourceLayout(mipSize, descriptor.m_format);
|
||||
}
|
||||
|
||||
AZStd::array_view<uint8_t> DecalTextureArray::GetRawImageData(int arrayLevel, const int mip) const
|
||||
AZStd::array_view<uint8_t> DecalTextureArray::GetRawImageData(const AZ::Name& mapName, int arrayLevel, const int mip) const
|
||||
{
|
||||
// We always want to provide valid data to the AssetCreator for each texture.
|
||||
// If this spot in the array is empty, just provide some random image as filler.
|
||||
@@ -257,17 +277,20 @@ namespace AZ
|
||||
{
|
||||
arrayLevel = m_materials.begin();
|
||||
}
|
||||
|
||||
const auto image = GetBaseColorImageAsset(m_materials[arrayLevel].m_materialAssetData);
|
||||
const auto image = GetStreamingImageAsset(m_materials[arrayLevel].m_materialAssetData, mapName);
|
||||
if (!image)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
const auto srcData = image->GetSubImageData(mip, 0);
|
||||
return srcData;
|
||||
}
|
||||
|
||||
AZ::RHI::Format DecalTextureArray::GetFormat() const
|
||||
AZ::RHI::Format DecalTextureArray::GetFormat(const DecalMapType mapType) const
|
||||
{
|
||||
AZ_Assert(m_materials.size() > 0, "GetFormat() can only be called after at least one material has been added.");
|
||||
const int iter = m_materials.begin();
|
||||
const auto& baseColorAsset = GetBaseColorImageAsset(m_materials[iter].m_materialAssetData);
|
||||
const auto& baseColorAsset = GetStreamingImageAsset(m_materials[iter].m_materialAssetData, GetMapName(mapType));
|
||||
return baseColorAsset->GetImageDescriptor().m_format;
|
||||
}
|
||||
|
||||
@@ -290,6 +313,25 @@ namespace AZ
|
||||
return id.IsValid() && materialData.m_materialAssetData.IsReady();
|
||||
}
|
||||
|
||||
bool DecalTextureArray::AreAllTextureMapsPresent(const DecalMapType mapType) const
|
||||
{
|
||||
int iter = m_materials.begin();
|
||||
while (iter != -1)
|
||||
{
|
||||
if (!IsTextureMapPresentInMaterial(m_materials[iter], mapType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
iter = m_materials.next(iter);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecalTextureArray::IsTextureMapPresentInMaterial(const MaterialData& materialData, const DecalMapType mapType) const
|
||||
{
|
||||
return GetStreamingImageAsset(materialData.m_materialAssetData, GetMapName(mapType)).IsReady();
|
||||
}
|
||||
|
||||
void DecalTextureArray::ClearAssets()
|
||||
{
|
||||
int iter = m_materials.begin();
|
||||
@@ -330,7 +372,8 @@ namespace AZ
|
||||
if (m_materials.size() == 0)
|
||||
return false;
|
||||
|
||||
return m_textureArrayPacked == nullptr;
|
||||
// We pack all diffuse/normal/etc in one go, so just check to see if the diffusemaps need packing
|
||||
return m_textureArrayPacked[DecalMapType_Diffuse] == nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,8 +28,18 @@ namespace AZ
|
||||
|
||||
namespace Render
|
||||
{
|
||||
enum DecalMapType : uint32_t
|
||||
{
|
||||
DecalMapType_Diffuse,
|
||||
DecalMapType_Normal,
|
||||
DecalMapType_Num
|
||||
};
|
||||
|
||||
//! Helper class used by DecalTextureArrayFeatureProcessor.
|
||||
//! Given a set of images (all with the same dimensions and format), it can pack them together into a single textureArray that can be sent to the GPU.
|
||||
//! Note that once textures are packed, this class will release any material references
|
||||
//! This might free memory if nothing else is holding onto them
|
||||
//! The class DOES keep note of which material asset ids were added, so it can load them again if necessary if the whole thing needs to be repacked
|
||||
class DecalTextureArray : public Data::AssetBus::MultiHandler
|
||||
{
|
||||
public:
|
||||
@@ -40,8 +50,12 @@ namespace AZ
|
||||
|
||||
AZ::Data::AssetId GetMaterialAssetId(const int index) const;
|
||||
|
||||
// Packs all the added materials into one texture array per DecalMapType.
|
||||
void Pack();
|
||||
const Data::Instance<RPI::StreamingImage>& GetPackedTexture() const;
|
||||
|
||||
// Note that we pack each type into a separate texture array. This is because formats are
|
||||
// often different (BC5 for normals, BC7 for diffuse, etc)
|
||||
const Data::Instance<RPI::StreamingImage>& GetPackedTexture(const DecalMapType mapType) const;
|
||||
|
||||
static bool IsValidDecalMaterial(const RPI::MaterialAsset& materialAsset);
|
||||
|
||||
@@ -56,22 +70,25 @@ namespace AZ
|
||||
|
||||
void OnAssetReady(Data::Asset<Data::AssetData> asset) override;
|
||||
|
||||
// Returns the index of the material in the m_materials container. -1 if not present.
|
||||
int FindMaterial(const AZ::Data::AssetId materialAssetId) const;
|
||||
|
||||
// packs the contents of the source images into a texture array readable by the GPU and returns it
|
||||
AZ::Data::Asset<AZ::RPI::ImageMipChainAsset> BuildPackedMipChainAsset(const size_t numTexturesToCreate);
|
||||
AZ::Data::Asset<AZ::RPI::ImageMipChainAsset> BuildPackedMipChainAsset(const DecalMapType mapType, const size_t numTexturesToCreate);
|
||||
RHI::ImageDescriptor CreatePackedImageDescriptor(const DecalMapType mapType, const uint16_t arraySize, const uint16_t mipLevels) const;
|
||||
|
||||
RHI::ImageDescriptor CreatePackedImageDescriptor(const uint16_t arraySize, const uint16_t mipLevels) const;
|
||||
|
||||
uint16_t GetNumMipLevels() const;
|
||||
RHI::Size GetImageDimensions() const;
|
||||
RHI::Format GetFormat() const;
|
||||
RHI::ImageSubresourceLayout GetLayout(int mip) const;
|
||||
AZStd::array_view<uint8_t> GetRawImageData(int arrayLevel, int mip) const;
|
||||
uint16_t GetNumMipLevels(const DecalMapType mapType) const;
|
||||
RHI::Size GetImageDimensions(const DecalMapType mapType) const;
|
||||
RHI::Format GetFormat(const DecalMapType mapType) const;
|
||||
RHI::ImageSubresourceLayout GetLayout(const DecalMapType mapType, int mip) const;
|
||||
AZStd::array_view<uint8_t> GetRawImageData(const AZ::Name& mapName, int arrayLevel, int mip) const;
|
||||
|
||||
bool AreAllAssetsReady() const;
|
||||
bool IsAssetReady(const MaterialData& materialData) const;
|
||||
|
||||
bool AreAllTextureMapsPresent(const DecalMapType mapType) const;
|
||||
bool IsTextureMapPresentInMaterial(const MaterialData& materialData, const DecalMapType mapType) const;
|
||||
|
||||
void ClearAssets();
|
||||
void ClearAsset(MaterialData& materialData);
|
||||
|
||||
@@ -81,7 +98,7 @@ namespace AZ
|
||||
bool NeedsPacking() const;
|
||||
|
||||
IndexableList<MaterialData> m_materials;
|
||||
Data::Instance<RPI::StreamingImage> m_textureArrayPacked;
|
||||
AZStd::array<Data::Instance<RPI::StreamingImage>, DecalMapType_Num> m_textureArrayPacked;
|
||||
|
||||
AZStd::unordered_set<AZ::Data::AssetId> m_assetsCurrentlyLoading;
|
||||
};
|
||||
|
||||
@@ -322,13 +322,30 @@ namespace AZ
|
||||
|
||||
void DecalTextureArrayFeatureProcessor::CacheShaderIndices()
|
||||
{
|
||||
for (int i = 0; i < NumTextureArrays; ++i)
|
||||
{
|
||||
const RHI::ShaderResourceGroupLayout* viewSrgLayout = RPI::RPISystemInterface::Get()->GetViewSrgLayout().get();
|
||||
const AZStd::string baseName = "m_decalTextureArray" + AZStd::to_string(i);
|
||||
// The azsl shader should define several texture arrays such as:
|
||||
// Texture2DArray<float4> m_decalTextureArrayDiffuse0;
|
||||
// Texture2DArray<float4> m_decalTextureArrayDiffuse1;
|
||||
// Texture2DArray<float4> m_decalTextureArrayDiffuse2;
|
||||
// and
|
||||
// Texture2DArray<float2> m_decalTextureArrayNormalMaps0;
|
||||
// Texture2DArray<float2> m_decalTextureArrayNormalMaps1;
|
||||
// Texture2DArray<float2> m_decalTextureArrayNormalMaps2;
|
||||
static const AZStd::array<AZStd::string, DecalMapType_Num> ShaderNames = { "m_decalTextureArrayDiffuse",
|
||||
"m_decalTextureArrayNormalMaps" };
|
||||
|
||||
m_decalTextureArrayIndices[i] = viewSrgLayout->FindShaderInputImageIndex(Name(baseName.c_str()));
|
||||
AZ_Warning("DecalTextureArrayFeatureProcessor", m_decalTextureArrayIndices[i].IsValid(), "Unable to find %s in decal shader.", baseName.c_str());
|
||||
for (int mapType = 0; mapType < DecalMapType_Num; ++mapType)
|
||||
{
|
||||
for (int texArrayIdx = 0; texArrayIdx < NumTextureArrays; ++texArrayIdx)
|
||||
{
|
||||
const RHI::ShaderResourceGroupLayout* viewSrgLayout = RPI::RPISystemInterface::Get()->GetViewSrgLayout().get();
|
||||
const AZStd::string baseName = ShaderNames[mapType] + AZStd::to_string(texArrayIdx);
|
||||
|
||||
m_decalTextureArrayIndices[texArrayIdx][mapType] = viewSrgLayout->FindShaderInputImageIndex(Name(baseName.c_str()));
|
||||
AZ_Warning(
|
||||
"DecalTextureArrayFeatureProcessor", m_decalTextureArrayIndices[texArrayIdx][mapType].IsValid(),
|
||||
"Unable to find %s in decal shader.",
|
||||
baseName.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,8 +428,11 @@ namespace AZ
|
||||
int iter = m_textureArrayList.begin();
|
||||
while (iter != -1)
|
||||
{
|
||||
const auto& packedTexture = m_textureArrayList[iter].second.GetPackedTexture();
|
||||
view->GetShaderResourceGroup()->SetImage(m_decalTextureArrayIndices[iter], packedTexture);
|
||||
for (int mapType = 0 ; mapType < DecalMapType_Num ; ++mapType)
|
||||
{
|
||||
const auto& packedTexture = m_textureArrayList[iter].second.GetPackedTexture(aznumeric_cast<DecalMapType>(mapType));
|
||||
view->GetShaderResourceGroup()->SetImage(m_decalTextureArrayIndices[iter][mapType], packedTexture);
|
||||
}
|
||||
iter = m_textureArrayList.next(iter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ namespace AZ
|
||||
private:
|
||||
|
||||
// Number of size and format permutations
|
||||
// This number should match the number of texture arrays in Decals/ViewSrg.azsli
|
||||
static constexpr int NumTextureArrays = 5;
|
||||
static constexpr const char* FeatureProcessorName = "DecalTextureArrayFeatureProcessor";
|
||||
|
||||
@@ -128,7 +129,7 @@ namespace AZ
|
||||
// 4 textures @ 512x512
|
||||
IndexableList < AZStd::pair < AZ::RHI::Size, DecalTextureArray>> m_textureArrayList;
|
||||
|
||||
AZStd::array<RHI::ShaderInputImageIndex, NumTextureArrays> m_decalTextureArrayIndices;
|
||||
AZStd::array<AZStd::array<RHI::ShaderInputImageIndex, DecalMapType_Num>, NumTextureArrays> m_decalTextureArrayIndices;
|
||||
GpuBufferHandler m_decalBufferHandler;
|
||||
|
||||
AsyncLoadTracker<DecalHandle> m_materialLoadTracker;
|
||||
|
||||
+17
-12
@@ -128,8 +128,8 @@ namespace AZ
|
||||
|
||||
void DiffuseProbeGrid::SetTransform(const AZ::Transform& transform)
|
||||
{
|
||||
m_position = transform.GetTranslation();
|
||||
m_aabbWs = Aabb::CreateCenterHalfExtents(m_position, m_extents / 2.0f);
|
||||
m_transform = transform;
|
||||
m_obbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_extents / 2.0f);
|
||||
|
||||
// probes need to be relocated since the grid position changed
|
||||
m_remainingRelocationIterations = DefaultNumRelocationIterations;
|
||||
@@ -145,7 +145,7 @@ namespace AZ
|
||||
void DiffuseProbeGrid::SetExtents(const AZ::Vector3& extents)
|
||||
{
|
||||
m_extents = extents;
|
||||
m_aabbWs = Aabb::CreateCenterHalfExtents(m_position, m_extents / 2.0f);
|
||||
m_obbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_extents / 2.0f);
|
||||
|
||||
// recompute the number of probes since the extents changed
|
||||
UpdateProbeCount();
|
||||
@@ -467,7 +467,10 @@ namespace AZ
|
||||
RHI::ShaderInputConstantIndex constantIndex;
|
||||
|
||||
constantIndex = srgLayout->FindShaderInputConstantIndex(AZ::Name("m_probeGrid.origin"));
|
||||
srg->SetConstant(constantIndex, m_position);
|
||||
srg->SetConstant(constantIndex, m_transform.GetTranslation());
|
||||
|
||||
constantIndex = srgLayout->FindShaderInputConstantIndex(AZ::Name("m_probeGrid.rotation"));
|
||||
srg->SetConstant(constantIndex, m_transform.GetRotation());
|
||||
|
||||
constantIndex = srgLayout->FindShaderInputConstantIndex(AZ::Name("m_probeGrid.numRaysPerProbe"));
|
||||
srg->SetConstant(constantIndex, m_numRaysPerProbe);
|
||||
@@ -760,14 +763,15 @@ namespace AZ
|
||||
RHI::ShaderInputImageIndex imageIndex;
|
||||
|
||||
constantIndex = srgLayout->FindShaderInputConstantIndex(Name("m_modelToWorld"));
|
||||
AZ::Matrix3x4 modelToWorld = AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_position) * AZ::Matrix3x4::CreateScale(m_extents);
|
||||
AZ::Matrix3x4 modelToWorld = AZ::Matrix3x4::CreateFromTransform(m_transform) * AZ::Matrix3x4::CreateScale(m_extents);
|
||||
m_renderObjectSrg->SetConstant(constantIndex, modelToWorld);
|
||||
|
||||
constantIndex = srgLayout->FindShaderInputConstantIndex(Name("m_aabbMin"));
|
||||
m_renderObjectSrg->SetConstant(constantIndex, m_aabbWs.GetMin());
|
||||
constantIndex = srgLayout->FindShaderInputConstantIndex(Name("m_modelToWorldInverse"));
|
||||
AZ::Matrix3x4 modelToWorldInverse = AZ::Matrix3x4::CreateFromTransform(m_transform).GetInverseFull();
|
||||
m_renderObjectSrg->SetConstant(constantIndex, modelToWorldInverse);
|
||||
|
||||
constantIndex = srgLayout->FindShaderInputConstantIndex(Name("m_aabbMax"));
|
||||
m_renderObjectSrg->SetConstant(constantIndex, m_aabbWs.GetMax());
|
||||
constantIndex = srgLayout->FindShaderInputConstantIndex(Name("m_obbHalfLengths"));
|
||||
m_renderObjectSrg->SetConstant(constantIndex, m_obbWs.GetHalfLengths());
|
||||
|
||||
constantIndex = srgLayout->FindShaderInputConstantIndex(Name("m_enableDiffuseGI"));
|
||||
m_renderObjectSrg->SetConstant(constantIndex, m_enabled);
|
||||
@@ -821,13 +825,14 @@ namespace AZ
|
||||
lod.m_screenCoverageMax = 1.0f;
|
||||
|
||||
// update cullable bounds
|
||||
Aabb aabbWs = Aabb::CreateFromObb(m_obbWs);
|
||||
Vector3 center;
|
||||
float radius;
|
||||
m_aabbWs.GetAsSphere(center, radius);
|
||||
aabbWs.GetAsSphere(center, radius);
|
||||
|
||||
m_cullable.m_cullData.m_boundingSphere = Sphere(center, radius);
|
||||
m_cullable.m_cullData.m_boundingObb = m_aabbWs.GetTransformedObb(AZ::Transform::CreateIdentity());
|
||||
m_cullable.m_cullData.m_visibilityEntry.m_boundingVolume = m_aabbWs;
|
||||
m_cullable.m_cullData.m_boundingObb = m_obbWs;
|
||||
m_cullable.m_cullData.m_visibilityEntry.m_boundingVolume = aabbWs;
|
||||
m_cullable.m_cullData.m_visibilityEntry.m_userData = &m_cullable;
|
||||
m_cullable.m_cullData.m_visibilityEntry.m_typeFlags = AzFramework::VisibilityEntry::TYPE_RPI_Cullable;
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace AZ
|
||||
const AZ::Vector3& GetExtents() const { return m_extents; }
|
||||
void SetExtents(const AZ::Vector3& extents);
|
||||
|
||||
const AZ::Aabb& GetAabbWs() const { return m_aabbWs; }
|
||||
const AZ::Obb& GetObbWs() const { return m_obbWs; }
|
||||
|
||||
bool ValidateProbeSpacing(const AZ::Vector3& newSpacing);
|
||||
const AZ::Vector3& GetProbeSpacing() const { return m_probeSpacing; }
|
||||
@@ -183,14 +183,14 @@ namespace AZ
|
||||
// scene
|
||||
RPI::Scene* m_scene = nullptr;
|
||||
|
||||
// probe grid position
|
||||
AZ::Vector3 m_position = AZ::Vector3(0.0f, 0.0f, 0.0f);
|
||||
// probe grid transform
|
||||
AZ::Transform m_transform = AZ::Transform::CreateIdentity();
|
||||
|
||||
// extents of the probe grid
|
||||
AZ::Vector3 m_extents = AZ::Vector3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
// probe grid AABB (world space), built from position and extents
|
||||
AZ::Aabb m_aabbWs = AZ::Aabb::CreateNull();
|
||||
// probe grid OBB (world space), built from transform and extents
|
||||
AZ::Obb m_obbWs;
|
||||
|
||||
// per-axis spacing of probes in the grid
|
||||
AZ::Vector3 m_probeSpacing;
|
||||
|
||||
+5
-4
@@ -154,10 +154,11 @@ namespace AZ
|
||||
// sort the probes by descending inner volume size, so the smallest volumes are rendered last
|
||||
auto sortFn = [](AZStd::shared_ptr<DiffuseProbeGrid> const& probe1, AZStd::shared_ptr<DiffuseProbeGrid> const& probe2) -> bool
|
||||
{
|
||||
const Aabb& aabb1 = probe1->GetAabbWs();
|
||||
const Aabb& aabb2 = probe2->GetAabbWs();
|
||||
float size1 = aabb1.GetXExtent() * aabb1.GetZExtent() * aabb1.GetYExtent();
|
||||
float size2 = aabb2.GetXExtent() * aabb2.GetZExtent() * aabb2.GetYExtent();
|
||||
const Obb& obb1 = probe1->GetObbWs();
|
||||
const Obb& obb2 = probe2->GetObbWs();
|
||||
float size1 = obb1.GetHalfLengthX() * obb1.GetHalfLengthZ() * obb1.GetHalfLengthY();
|
||||
float size2 = obb2.GetHalfLengthX() * obb2.GetHalfLengthZ() * obb2.GetHalfLengthY();
|
||||
|
||||
return (size1 > size2);
|
||||
};
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace UnitTest
|
||||
{
|
||||
AZ::Render::DecalTextureArray decalTextureArray;
|
||||
decalTextureArray.Pack();
|
||||
auto nothing = decalTextureArray.GetPackedTexture();
|
||||
auto nothing = decalTextureArray.GetPackedTexture(AZ::Render::DecalMapType_Diffuse);
|
||||
EXPECT_EQ(nothing, nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
namespace AZ
|
||||
{
|
||||
class Job;
|
||||
class TaskGraphActiveInterface;
|
||||
|
||||
namespace RHI
|
||||
{
|
||||
@@ -228,6 +229,8 @@ namespace AZ
|
||||
|
||||
// list of RayTracingShaderTables that should be built this frame
|
||||
AZStd::vector<RHI::Ptr<RayTracingShaderTable>> m_rayTracingShaderTablesToBuild;
|
||||
|
||||
AZ::TaskGraphActiveInterface* m_taskGraphActive = nullptr;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,11 @@
|
||||
#include <Atom/RHI/RayTracingShaderTable.h>
|
||||
|
||||
#include <AzCore/Debug/EventTrace.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Jobs/Algorithms.h>
|
||||
#include <AzCore/Jobs/JobCompletion.h>
|
||||
#include <AzCore/Jobs/JobFunction.h>
|
||||
#include <AzCore/Task/TaskGraph.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -77,6 +79,8 @@ namespace AZ
|
||||
m_rootScope = m_rootScopeProducer->GetScope();
|
||||
m_device = &device;
|
||||
|
||||
m_taskGraphActive = AZ::Interface<AZ::TaskGraphActiveInterface>::Get();
|
||||
|
||||
m_lastFrameEndTime = AZStd::GetTimeNowTicks();
|
||||
|
||||
return ResultCode::Success;
|
||||
@@ -85,6 +89,7 @@ namespace AZ
|
||||
void FrameScheduler::Shutdown()
|
||||
{
|
||||
m_device = nullptr;
|
||||
m_taskGraphActive = nullptr;
|
||||
m_rootScopeProducer = nullptr;
|
||||
m_rootScope = nullptr;
|
||||
m_frameGraphExecuter = nullptr;
|
||||
@@ -258,50 +263,98 @@ namespace AZ
|
||||
|
||||
if (m_compileRequest.m_jobPolicy == JobPolicy::Parallel)
|
||||
{
|
||||
const auto compileGroupsBeginFunction = [](ShaderResourceGroupPool* srgPool)
|
||||
{
|
||||
srgPool->CompileGroupsBegin();
|
||||
};
|
||||
|
||||
resourcePoolDatabase.ForEachShaderResourceGroupPool<decltype(compileGroupsBeginFunction)>(compileGroupsBeginFunction);
|
||||
|
||||
// Iterate over each SRG pool and fork jobs to compile SRGs.
|
||||
const uint32_t compilesPerJob = m_compileRequest.m_shaderResourceGroupCompilesPerJob;
|
||||
AZ::JobCompletion jobCompletion;
|
||||
|
||||
const auto compileIntervalsFunction = [compilesPerJob, &jobCompletion](ShaderResourceGroupPool* srgPool)
|
||||
if (m_taskGraphActive && m_taskGraphActive->IsTaskGraphActive())
|
||||
{
|
||||
const uint32_t compilesInPool = srgPool->GetGroupsToCompileCount();
|
||||
const uint32_t jobCount = DivideByMultiple(compilesInPool, compilesPerJob);
|
||||
AZ::TaskGraph taskGraph;
|
||||
|
||||
for (uint32_t i = 0; i < jobCount; ++i)
|
||||
const auto compileIntervalsFunction = [compilesPerJob, &taskGraph](ShaderResourceGroupPool* srgPool)
|
||||
{
|
||||
Interval interval;
|
||||
interval.m_min = i * compilesPerJob;
|
||||
interval.m_max = AZStd::min(interval.m_min + compilesPerJob, compilesInPool);
|
||||
srgPool->CompileGroupsBegin();
|
||||
const uint32_t compilesInPool = srgPool->GetGroupsToCompileCount();
|
||||
const uint32_t jobCount = DivideByMultiple(compilesInPool, compilesPerJob);
|
||||
AZ::TaskDescriptor srgCompileDesc{"SrgCompile", "Graphics"};
|
||||
AZ::TaskDescriptor srgCompileEndDesc{"SrgCompileEnd", "Graphics"};
|
||||
|
||||
const auto compileGroupsForIntervalLambda = [srgPool, interval]()
|
||||
auto srgCompileEndTask = taskGraph.AddTask(
|
||||
srgCompileEndDesc,
|
||||
[srgPool]()
|
||||
{
|
||||
srgPool->CompileGroupsEnd();
|
||||
});
|
||||
|
||||
for (uint32_t i = 0; i < jobCount; ++i)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RHI, "FrameScheduler : compileGroupsForIntervalLambda");
|
||||
srgPool->CompileGroupsForInterval(interval);
|
||||
};
|
||||
Interval interval;
|
||||
interval.m_min = i * compilesPerJob;
|
||||
interval.m_max = AZStd::min(interval.m_min + compilesPerJob, compilesInPool);
|
||||
|
||||
AZ::Job* executeGroupJob = AZ::CreateJobFunction(AZStd::move(compileGroupsForIntervalLambda), true, nullptr);
|
||||
executeGroupJob->SetDependent(&jobCompletion);
|
||||
executeGroupJob->Start();
|
||||
auto compileTask = taskGraph.AddTask(
|
||||
srgCompileDesc,
|
||||
[srgPool, interval]()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RHI, "FrameScheduler : compileGroupsForIntervalLambda");
|
||||
srgPool->CompileGroupsForInterval(interval);
|
||||
});
|
||||
compileTask.Precedes(srgCompileEndTask);
|
||||
}
|
||||
};
|
||||
|
||||
resourcePoolDatabase.ForEachShaderResourceGroupPool<decltype(compileIntervalsFunction)>(AZStd::move(compileIntervalsFunction));
|
||||
if (!taskGraph.IsEmpty())
|
||||
{
|
||||
AZ::TaskGraphEvent finishedEvent;
|
||||
taskGraph.Submit(&finishedEvent);
|
||||
finishedEvent.Wait();
|
||||
}
|
||||
};
|
||||
|
||||
resourcePoolDatabase.ForEachShaderResourceGroupPool<decltype(compileIntervalsFunction)>(AZStd::move(compileIntervalsFunction));
|
||||
|
||||
jobCompletion.StartAndWaitForCompletion();
|
||||
|
||||
const auto compileGroupsEndFunction = [](ShaderResourceGroupPool* srgPool)
|
||||
}
|
||||
else // use Job system
|
||||
{
|
||||
srgPool->CompileGroupsEnd();
|
||||
};
|
||||
const auto compileGroupsBeginFunction = [](ShaderResourceGroupPool* srgPool)
|
||||
{
|
||||
srgPool->CompileGroupsBegin();
|
||||
};
|
||||
|
||||
resourcePoolDatabase.ForEachShaderResourceGroupPool<decltype(compileGroupsEndFunction)>(compileGroupsEndFunction);
|
||||
resourcePoolDatabase.ForEachShaderResourceGroupPool<decltype(compileGroupsBeginFunction)>(compileGroupsBeginFunction);
|
||||
|
||||
// Iterate over each SRG pool and fork jobs to compile SRGs.
|
||||
AZ::JobCompletion jobCompletion;
|
||||
|
||||
const auto compileIntervalsFunction = [compilesPerJob, &jobCompletion](ShaderResourceGroupPool* srgPool)
|
||||
{
|
||||
const uint32_t compilesInPool = srgPool->GetGroupsToCompileCount();
|
||||
const uint32_t jobCount = DivideByMultiple(compilesInPool, compilesPerJob);
|
||||
|
||||
for (uint32_t i = 0; i < jobCount; ++i)
|
||||
{
|
||||
Interval interval;
|
||||
interval.m_min = i * compilesPerJob;
|
||||
interval.m_max = AZStd::min(interval.m_min + compilesPerJob, compilesInPool);
|
||||
|
||||
const auto compileGroupsForIntervalLambda = [srgPool, interval]()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RHI, "FrameScheduler : compileGroupsForIntervalLambda");
|
||||
srgPool->CompileGroupsForInterval(interval);
|
||||
};
|
||||
|
||||
AZ::Job* executeGroupJob = AZ::CreateJobFunction(AZStd::move(compileGroupsForIntervalLambda), true, nullptr);
|
||||
executeGroupJob->SetDependent(&jobCompletion);
|
||||
executeGroupJob->Start();
|
||||
}
|
||||
};
|
||||
|
||||
resourcePoolDatabase.ForEachShaderResourceGroupPool<decltype(compileIntervalsFunction)>(AZStd::move(compileIntervalsFunction));
|
||||
|
||||
jobCompletion.StartAndWaitForCompletion();
|
||||
|
||||
const auto compileGroupsEndFunction = [](ShaderResourceGroupPool* srgPool)
|
||||
{
|
||||
srgPool->CompileGroupsEnd();
|
||||
};
|
||||
|
||||
resourcePoolDatabase.ForEachShaderResourceGroupPool<decltype(compileGroupsEndFunction)>(compileGroupsEndFunction);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace AZ
|
||||
|
||||
// Use separate work submission queue from the hw copy queue to avoid the per frame sync.
|
||||
m_copyQueue = CommandQueue::Create();
|
||||
m_copyQueue->SetName(AZ::Name("AsyncUpload Queue"));
|
||||
|
||||
RHI::CommandQueueDescriptor commandQueueDescriptor;
|
||||
commandQueueDescriptor.m_hardwareQueueClass = RHI::HardwareQueueClass::Copy;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/Script/ScriptTimePoint.h>
|
||||
#include <AzCore/Task/TaskGraph.h>
|
||||
|
||||
#include <AzFramework/Scene/Scene.h>
|
||||
#include <AzFramework/Scene/SceneSystemInterface.h>
|
||||
@@ -194,6 +195,9 @@ namespace AZ
|
||||
// This function is called every time scene's render pipelines change.
|
||||
void RebuildPipelineStatesLookup();
|
||||
|
||||
// Helper function to wait for end of TaskGraph
|
||||
void WaitTGEvent(AZ::TaskGraphEvent& completionTGEvent, AZStd::atomic_bool* workToWaitOn = nullptr);
|
||||
|
||||
// Helper function for wait and clean up a completion job
|
||||
void WaitAndCleanCompletionJob(AZ::JobCompletion*& completionJob);
|
||||
|
||||
@@ -204,12 +208,26 @@ namespace AZ
|
||||
// This happens in UpdateSrgs()
|
||||
void PrepareSceneSrg();
|
||||
|
||||
// Implementation functions that allow scene to switch between using Jobs or TaskGraphs
|
||||
void SimulateTaskGraph();
|
||||
void SimulateJobs();
|
||||
|
||||
void CollectDrawPacketsTaskGraph();
|
||||
void CollectDrawPacketsJobs();
|
||||
|
||||
void FinalizeDrawListsTaskGraph();
|
||||
void FinalizeDrawListsJobs();
|
||||
|
||||
// List of feature processors that are active for this scene
|
||||
AZStd::vector<FeatureProcessorPtr> m_featureProcessors;
|
||||
|
||||
// List of pipelines of this scene. Each pipeline has an unique pipeline Id.
|
||||
AZStd::vector<RenderPipelinePtr> m_pipelines;
|
||||
|
||||
// CPU simulation TaskGraphEvent to wait for completion of all the simulation tasks
|
||||
AZ::TaskGraphEvent m_simulationFinishedTGEvent;
|
||||
AZStd::atomic_bool m_simulationFinishedWorkActive = false;
|
||||
|
||||
// CPU simulation job completion for track all feature processors' simulation jobs
|
||||
AZ::JobCompletion* m_simulationCompletion = nullptr;
|
||||
|
||||
@@ -228,6 +246,7 @@ namespace AZ
|
||||
SceneId m_id;
|
||||
|
||||
bool m_activated = false;
|
||||
bool m_taskGraphActive = false; // update during tick, to ensure it only changes on frame boundaries
|
||||
|
||||
RenderPipelinePtr m_defaultPipeline;
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <AzCore/Jobs/JobFunction.h>
|
||||
#include <AzCore/Jobs/JobEmpty.h>
|
||||
|
||||
#include <AzCore/Task/TaskGraph.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
|
||||
namespace AZ
|
||||
@@ -92,7 +94,14 @@ namespace AZ
|
||||
|
||||
Scene::~Scene()
|
||||
{
|
||||
WaitAndCleanCompletionJob(m_simulationCompletion);
|
||||
if (m_taskGraphActive)
|
||||
{
|
||||
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitAndCleanCompletionJob(m_simulationCompletion);
|
||||
}
|
||||
SceneRequestBus::Handler::BusDisconnect();
|
||||
|
||||
// Remove all the render pipelines. Need to process queued changes with pass system before and after remove render pipelines
|
||||
@@ -346,6 +355,47 @@ namespace AZ
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Scene::SimulateTaskGraph()
|
||||
{
|
||||
static const AZ::TaskDescriptor simulationTGDesc{"RPI::Scene::Simulate", "Graphics"};
|
||||
AZ::TaskGraph simulationTG;
|
||||
|
||||
for (FeatureProcessorPtr& fp : m_featureProcessors)
|
||||
{
|
||||
FeatureProcessor* featureProcessor = fp.get();
|
||||
simulationTG.AddTask(
|
||||
simulationTGDesc,
|
||||
[this, featureProcessor]()
|
||||
{
|
||||
featureProcessor->Simulate(m_simulatePacket);
|
||||
});
|
||||
}
|
||||
simulationTG.Detach();
|
||||
m_simulationFinishedWorkActive = true;
|
||||
simulationTG.Submit(&m_simulationFinishedTGEvent);
|
||||
}
|
||||
|
||||
void Scene::SimulateJobs()
|
||||
{
|
||||
// Create a new job to track completion.
|
||||
m_simulationCompletion = aznew AZ::JobCompletion();
|
||||
|
||||
for (FeatureProcessorPtr& fp : m_featureProcessors)
|
||||
{
|
||||
FeatureProcessor* featureProcessor = fp.get();
|
||||
const auto jobLambda = [this, featureProcessor]()
|
||||
{
|
||||
|
||||
featureProcessor->Simulate(m_simulatePacket);
|
||||
};
|
||||
|
||||
AZ::Job* simulationJob = AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr); //auto-deletes
|
||||
simulationJob->SetDependent(m_simulationCompletion);
|
||||
simulationJob->Start();
|
||||
}
|
||||
//[GFX TODO]: the completion job should start here
|
||||
}
|
||||
|
||||
void Scene::Simulate([[maybe_unused]] const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "Scene: Simulate");
|
||||
@@ -353,7 +403,17 @@ namespace AZ
|
||||
m_simulationTime = tickInfo.m_currentGameTime;
|
||||
|
||||
// If previous simulation job wasn't done, wait for it to finish.
|
||||
WaitAndCleanCompletionJob(m_simulationCompletion);
|
||||
if (m_taskGraphActive)
|
||||
{
|
||||
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitAndCleanCompletionJob(m_simulationCompletion);
|
||||
}
|
||||
|
||||
auto taskGraphActiveInterface = AZ::Interface<AZ::TaskGraphActiveInterface>::Get();
|
||||
m_taskGraphActive = taskGraphActiveInterface && taskGraphActiveInterface->IsTaskGraphActive();
|
||||
|
||||
if (jobPolicy == RHI::JobPolicy::Serial)
|
||||
{
|
||||
@@ -364,22 +424,27 @@ namespace AZ
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new job to track completion.
|
||||
m_simulationCompletion = aznew AZ::JobCompletion();
|
||||
|
||||
for (FeatureProcessorPtr& fp : m_featureProcessors)
|
||||
if (m_taskGraphActive)
|
||||
{
|
||||
FeatureProcessor* featureProcessor = fp.get();
|
||||
const auto jobLambda = [this, featureProcessor]()
|
||||
{
|
||||
featureProcessor->Simulate(m_simulatePacket);
|
||||
};
|
||||
|
||||
AZ::Job* simulationJob = AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr); //auto-deletes
|
||||
simulationJob->SetDependent(m_simulationCompletion);
|
||||
simulationJob->Start();
|
||||
SimulateTaskGraph();
|
||||
}
|
||||
//[GFX TODO]: the completion job should start here
|
||||
else
|
||||
{
|
||||
SimulateJobs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::WaitTGEvent(AZ::TaskGraphEvent& completionTGEvent, AZStd::atomic_bool* workToWaitOn )
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "Scene: WaitAndCleanCompletionJob");
|
||||
if (!workToWaitOn || workToWaitOn->load())
|
||||
{
|
||||
completionTGEvent.Wait();
|
||||
}
|
||||
if (workToWaitOn)
|
||||
{
|
||||
workToWaitOn->store(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,7 +459,7 @@ namespace AZ
|
||||
completionJob = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Scene::ConnectEvent(PrepareSceneSrgEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_prepareSrgEvent);
|
||||
@@ -418,12 +483,139 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::CollectDrawPacketsTaskGraph()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets");
|
||||
AZ::TaskGraphEvent collectDrawPacketsTGEvent;
|
||||
static const AZ::TaskDescriptor collectDrawPacketsTGDesc{"RPI_Scene_PrepareRender_CollectDrawPackets", "Graphics"};
|
||||
|
||||
AZ::TaskGraph collectDrawPacketsTG;
|
||||
// Launch FeatureProcessor::Render() jobs
|
||||
for (auto& fp : m_featureProcessors)
|
||||
{
|
||||
collectDrawPacketsTG.AddTask(
|
||||
collectDrawPacketsTGDesc,
|
||||
[this, &fp]()
|
||||
{
|
||||
fp->Render(m_renderPacket);
|
||||
});
|
||||
|
||||
}
|
||||
collectDrawPacketsTG.Submit(&collectDrawPacketsTGEvent);
|
||||
|
||||
// Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs if m_parallelOctreeTraversal)
|
||||
bool parallelOctreeTraversal = m_cullingScene->GetDebugContext().m_parallelOctreeTraversal;
|
||||
m_cullingScene->BeginCulling(m_renderPacket.m_views);
|
||||
AZ::JobCompletion processCullablesCompletion;
|
||||
for (ViewPtr& viewPtr : m_renderPacket.m_views)
|
||||
{
|
||||
AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob)
|
||||
{
|
||||
m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job
|
||||
},
|
||||
true, nullptr); //auto-deletes
|
||||
if (parallelOctreeTraversal)
|
||||
{
|
||||
processCullablesJob->SetDependent(&processCullablesCompletion);
|
||||
processCullablesJob->Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
processCullablesJob->StartAndWaitForCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
WaitTGEvent(collectDrawPacketsTGEvent);
|
||||
processCullablesCompletion.StartAndWaitForCompletion();
|
||||
}
|
||||
|
||||
void Scene::CollectDrawPacketsJobs()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets");
|
||||
AZ::JobCompletion* collectDrawPacketsCompletion = aznew AZ::JobCompletion();
|
||||
|
||||
// Launch FeatureProcessor::Render() jobs
|
||||
for (auto& fp : m_featureProcessors)
|
||||
{
|
||||
const auto renderLambda = [this, &fp]()
|
||||
{
|
||||
fp->Render(m_renderPacket);
|
||||
};
|
||||
|
||||
AZ::Job* renderJob = AZ::CreateJobFunction(AZStd::move(renderLambda), true, nullptr); //auto-deletes
|
||||
renderJob->SetDependent(collectDrawPacketsCompletion);
|
||||
renderJob->Start();
|
||||
}
|
||||
|
||||
// Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs)
|
||||
m_cullingScene->BeginCulling(m_renderPacket.m_views);
|
||||
for (ViewPtr& viewPtr : m_renderPacket.m_views)
|
||||
{
|
||||
AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob)
|
||||
{
|
||||
m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job
|
||||
},
|
||||
true, nullptr); //auto-deletes
|
||||
if (m_cullingScene->GetDebugContext().m_parallelOctreeTraversal)
|
||||
{
|
||||
processCullablesJob->SetDependent(collectDrawPacketsCompletion);
|
||||
processCullablesJob->Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
processCullablesJob->StartAndWaitForCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
WaitAndCleanCompletionJob(collectDrawPacketsCompletion);
|
||||
}
|
||||
|
||||
void Scene::FinalizeDrawListsTaskGraph()
|
||||
{
|
||||
AZ::TaskGraphEvent finalizeDrawListsTGEvent;
|
||||
static const AZ::TaskDescriptor finalizeDrawListsTGDesc{"RPI_Scene_PrepareRender_FinalizeDrawLists", "Graphics"};
|
||||
|
||||
AZ::TaskGraph finalizeDrawListsTG;
|
||||
for (auto& view : m_renderPacket.m_views)
|
||||
{
|
||||
finalizeDrawListsTG.AddTask(
|
||||
finalizeDrawListsTGDesc,
|
||||
[view]()
|
||||
{
|
||||
view->FinalizeDrawLists();
|
||||
});
|
||||
}
|
||||
finalizeDrawListsTG.Submit(&finalizeDrawListsTGEvent);
|
||||
WaitTGEvent(finalizeDrawListsTGEvent);
|
||||
}
|
||||
|
||||
void Scene::FinalizeDrawListsJobs()
|
||||
{
|
||||
AZ::JobCompletion* finalizeDrawListsCompletion = aznew AZ::JobCompletion();
|
||||
for (auto& view : m_renderPacket.m_views)
|
||||
{
|
||||
const auto finalizeDrawListsLambda = [view]()
|
||||
{
|
||||
view->FinalizeDrawLists();
|
||||
};
|
||||
|
||||
AZ::Job* finalizeDrawListsJob = AZ::CreateJobFunction(AZStd::move(finalizeDrawListsLambda), true, nullptr); //auto-deletes
|
||||
finalizeDrawListsJob->SetDependent(finalizeDrawListsCompletion);
|
||||
finalizeDrawListsJob->Start();
|
||||
}
|
||||
WaitAndCleanCompletionJob(finalizeDrawListsCompletion);
|
||||
}
|
||||
|
||||
void Scene::PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender");
|
||||
|
||||
if (m_taskGraphActive)
|
||||
{
|
||||
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "WaitForSimulationCompletion");
|
||||
WaitAndCleanCompletionJob(m_simulationCompletion);
|
||||
}
|
||||
|
||||
@@ -496,44 +688,16 @@ namespace AZ
|
||||
}
|
||||
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets");
|
||||
AZ::JobCompletion* collectDrawPacketsCompletion = aznew AZ::JobCompletion();
|
||||
|
||||
// Launch FeatureProcessor::Render() jobs
|
||||
for (auto& fp : m_featureProcessors)
|
||||
if (m_taskGraphActive)
|
||||
{
|
||||
const auto renderLambda = [this, &fp]()
|
||||
{
|
||||
fp->Render(m_renderPacket);
|
||||
};
|
||||
|
||||
AZ::Job* renderJob = AZ::CreateJobFunction(AZStd::move(renderLambda), true, nullptr); //auto-deletes
|
||||
renderJob->SetDependent(collectDrawPacketsCompletion);
|
||||
renderJob->Start();
|
||||
CollectDrawPacketsTaskGraph();
|
||||
}
|
||||
|
||||
// Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs)
|
||||
m_cullingScene->BeginCulling(m_renderPacket.m_views);
|
||||
for (ViewPtr& viewPtr : m_renderPacket.m_views)
|
||||
else
|
||||
{
|
||||
AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob)
|
||||
{
|
||||
m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob);
|
||||
},
|
||||
true, nullptr); //auto-deletes
|
||||
if (m_cullingScene->GetDebugContext().m_parallelOctreeTraversal)
|
||||
{
|
||||
processCullablesJob->SetDependent(collectDrawPacketsCompletion);
|
||||
processCullablesJob->Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
processCullablesJob->StartAndWaitForCompletion();
|
||||
}
|
||||
CollectDrawPacketsJobs();
|
||||
}
|
||||
|
||||
WaitAndCleanCompletionJob(collectDrawPacketsCompletion);
|
||||
|
||||
m_cullingScene->EndCulling();
|
||||
|
||||
// Add dynamic draw data for all the views
|
||||
@@ -556,20 +720,15 @@ namespace AZ
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ::JobCompletion* finalizeDrawListsCompletion = aznew AZ::JobCompletion();
|
||||
for (auto& view : m_renderPacket.m_views)
|
||||
if (m_taskGraphActive)
|
||||
{
|
||||
const auto finalizeDrawListsLambda = [view]()
|
||||
{
|
||||
view->FinalizeDrawLists();
|
||||
};
|
||||
|
||||
AZ::Job* finalizeDrawListsJob = AZ::CreateJobFunction(AZStd::move(finalizeDrawListsLambda), true, nullptr); //auto-deletes
|
||||
finalizeDrawListsJob->SetDependent(finalizeDrawListsCompletion);
|
||||
finalizeDrawListsJob->Start();
|
||||
FinalizeDrawListsTaskGraph();
|
||||
}
|
||||
else
|
||||
{
|
||||
FinalizeDrawListsJobs();
|
||||
}
|
||||
AZ_PROFILE_END(RPI);
|
||||
WaitAndCleanCompletionJob(finalizeDrawListsCompletion);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "Materials\\Types\\StandardPBR.materialtype",
|
||||
"materialType": "Materials/Types/StandardPBR.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 3
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "Materials\\Types\\StandardPBR.materialtype",
|
||||
"materialType": "Materials/Types/StandardPBR.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 3,
|
||||
"properties": {
|
||||
@@ -16,4 +16,4 @@
|
||||
"textureMap": "TestData/Textures/TextureHaven/4k_castle_brick_02_red/4k_castle_brick_02_red_hp_bc.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+56
-4
@@ -69,6 +69,9 @@ AZ_PUSH_DISABLE_WARNING(4267, "-Wconversion")
|
||||
AZ_POP_DISABLE_WARNING
|
||||
#include <LyViewPaneNames.h>
|
||||
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <IEditor.h>
|
||||
|
||||
namespace EMStudio
|
||||
{
|
||||
class SaveDirtyWorkspaceCallback
|
||||
@@ -257,10 +260,10 @@ namespace EMStudio
|
||||
m_saveWorkspaceCallback = nullptr;
|
||||
}
|
||||
|
||||
|
||||
// destructor
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
DisableUpdatingPlugins();
|
||||
|
||||
if (m_nativeEventFilter)
|
||||
{
|
||||
QAbstractEventDispatcher::instance()->removeNativeEventFilter(m_nativeEventFilter);
|
||||
@@ -577,6 +580,8 @@ namespace EMStudio
|
||||
AZ_Assert(!m_nativeEventFilter, "Double initialization?");
|
||||
m_nativeEventFilter = new NativeEventFilter(this);
|
||||
QAbstractEventDispatcher::instance()->installNativeEventFilter(m_nativeEventFilter);
|
||||
|
||||
EnableUpdatingPlugins();
|
||||
}
|
||||
|
||||
MainWindow::MainWindowCommandManagerCallback::MainWindowCommandManagerCallback()
|
||||
@@ -2813,6 +2818,53 @@ namespace EMStudio
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace EMStudio
|
||||
void MainWindow::UpdatePlugins(float timeDelta)
|
||||
{
|
||||
EMStudio::PluginManager* pluginManager = EMStudio::GetPluginManager();
|
||||
if (!pluginManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/moc_MainWindow.cpp>
|
||||
const size_t numPlugins = pluginManager->GetNumActivePlugins();
|
||||
for (size_t i = 0; i < numPlugins; ++i)
|
||||
{
|
||||
EMStudio::EMStudioPlugin* plugin = pluginManager->GetActivePlugin(i);
|
||||
plugin->ProcessFrame(timeDelta);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::EnableUpdatingPlugins()
|
||||
{
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
void MainWindow::DisableUpdatingPlugins()
|
||||
{
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void MainWindow::OnTick(float delta, AZ::ScriptTimePoint timePoint)
|
||||
{
|
||||
AZ_UNUSED(timePoint);
|
||||
|
||||
// Check if we are in game mode.
|
||||
IEditor* editor = nullptr;
|
||||
AzToolsFramework::EditorRequestBus::BroadcastResult(editor, &AzToolsFramework::EditorRequests::GetEditor);
|
||||
const bool inGameMode = editor ? editor->IsInGameMode() : false;
|
||||
|
||||
// Update all the animation editor plugins (redraw viewports, timeline, and graph windows etc).
|
||||
// But only update this when the main window is visible and we are in game mode.
|
||||
const bool isEditorActive = !visibleRegion().isEmpty() && !inGameMode;
|
||||
|
||||
if (isEditorActive)
|
||||
{
|
||||
UpdatePlugins(delta);
|
||||
}
|
||||
}
|
||||
|
||||
int MainWindow::GetTickOrder()
|
||||
{
|
||||
return AZ::TICK_UI;
|
||||
}
|
||||
} // namespace EMStudio
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <EMotionStudio/EMStudioSDK/Source/EMStudioConfig.h>
|
||||
#include <EMotionStudio/EMStudioSDK/Source/GUIOptions.h>
|
||||
#include <EMotionStudio/EMStudioSDK/Source/PluginOptionsBus.h>
|
||||
@@ -99,8 +100,9 @@ namespace EMStudio
|
||||
: public AzQtComponents::DockMainWindow
|
||||
, private PluginOptionsNotificationsBus::Router
|
||||
, public EMotionFX::ActorEditorRequestBus::Handler
|
||||
, private AZ::TickBus::Handler
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT // AUTOMOC
|
||||
MCORE_MEMORYOBJECTCATEGORY(MainWindow, MCore::MCORE_DEFAULT_ALIGNMENT, MEMCATEGORY_EMSTUDIOSDK)
|
||||
|
||||
public:
|
||||
@@ -304,6 +306,16 @@ namespace EMStudio
|
||||
|
||||
MainWindowCommandManagerCallback m_mainWindowCommandManagerCallback;
|
||||
|
||||
private:
|
||||
// AZ::TickBus::Handler overrides
|
||||
void OnTick(float delta, AZ::ScriptTimePoint timePoint) override;
|
||||
int GetTickOrder() override;
|
||||
|
||||
void UpdatePlugins(float timeDelta);
|
||||
|
||||
void EnableUpdatingPlugins();
|
||||
void DisableUpdatingPlugins();
|
||||
|
||||
public slots:
|
||||
void OnFileOpenActor();
|
||||
void OnFileSaveSelectedActors();
|
||||
|
||||
@@ -62,7 +62,6 @@
|
||||
|
||||
|
||||
#if defined(EMOTIONFXANIMATION_EDITOR) // EMFX tools / editor includes
|
||||
# include <IEditor.h>
|
||||
// Qt
|
||||
# include <QtGui/QSurfaceFormat>
|
||||
// EMStudio tools and main window registration
|
||||
@@ -603,31 +602,6 @@ namespace EMotionFX
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#if defined (EMOTIONFXANIMATION_EDITOR)
|
||||
void SystemComponent::UpdateAnimationEditorPlugins(float delta)
|
||||
{
|
||||
if (!EMStudio::GetManager())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EMStudio::PluginManager* pluginManager = EMStudio::GetPluginManager();
|
||||
if (!pluginManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Process the plugins.
|
||||
const size_t numPlugins = pluginManager->GetNumActivePlugins();
|
||||
for (size_t i = 0; i < numPlugins; ++i)
|
||||
{
|
||||
EMStudio::EMStudioPlugin* plugin = pluginManager->GetActivePlugin(i);
|
||||
plugin->ProcessFrame(delta);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void SystemComponent::OnTick(float delta, AZ::ScriptTimePoint timePoint)
|
||||
{
|
||||
@@ -635,47 +609,18 @@ namespace EMotionFX
|
||||
|
||||
#if defined (EMOTIONFXANIMATION_EDITOR)
|
||||
AZ_UNUSED(delta);
|
||||
const float realDelta = m_updateTimer.StampAndGetDeltaTimeInSeconds();
|
||||
delta = m_updateTimer.StampAndGetDeltaTimeInSeconds();
|
||||
#endif
|
||||
|
||||
// Flush events prior to updating EMotion FX.
|
||||
ActorNotificationBus::ExecuteQueuedEvents();
|
||||
|
||||
if (CVars::emfx_updateEnabled)
|
||||
{
|
||||
// Main EMotionFX runtime update.
|
||||
GetEMotionFX().Update(realDelta);
|
||||
}
|
||||
|
||||
// Check if we are in game mode.
|
||||
IEditor* editor = nullptr;
|
||||
EBUS_EVENT_RESULT(editor, AzToolsFramework::EditorRequests::Bus, GetEditor);
|
||||
const bool inGameMode = editor ? editor->IsInGameMode() : false;
|
||||
|
||||
// Update all the animation editor plugins (redraw viewports, timeline, and graph windows etc).
|
||||
// But only update this when the main window is visible and we are in game mode.
|
||||
const bool isEditorActive =
|
||||
EMotionFX::GetEMotionFX().GetIsInEditorMode() &&
|
||||
EMStudio::GetManager() &&
|
||||
EMStudio::HasMainWindow() &&
|
||||
!EMStudio::GetMainWindow()->visibleRegion().isEmpty() &&
|
||||
!inGameMode;
|
||||
|
||||
if (isEditorActive)
|
||||
{
|
||||
UpdateAnimationEditorPlugins(realDelta);
|
||||
}
|
||||
#else
|
||||
// Flush events prior to updating EMotion FX.
|
||||
ActorNotificationBus::ExecuteQueuedEvents();
|
||||
|
||||
if (CVars::emfx_updateEnabled)
|
||||
{
|
||||
// Main EMotionFX runtime update.
|
||||
GetEMotionFX().Update(delta);
|
||||
}
|
||||
#endif
|
||||
|
||||
const float timeDelta = delta;
|
||||
const ActorManager* actorManager = GetEMotionFX().GetActorManager();
|
||||
const size_t numActorInstances = actorManager->GetNumActorInstances();
|
||||
for (size_t i = 0; i < numActorInstances; ++i)
|
||||
@@ -704,7 +649,7 @@ namespace EMotionFX
|
||||
// If we have a physics controller.
|
||||
if (hasCustomMotionExtractionController || hasPhysicsController)
|
||||
{
|
||||
const float deltaTimeInv = (timeDelta > 0.0f) ? (1.0f / timeDelta) : 0.0f;
|
||||
const float deltaTimeInv = (delta > 0.0f) ? (1.0f / delta) : 0.0f;
|
||||
|
||||
AZ::Transform currentTransform = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM);
|
||||
@@ -719,7 +664,7 @@ namespace EMotionFX
|
||||
}
|
||||
else if (hasCustomMotionExtractionController)
|
||||
{
|
||||
MotionExtractionRequestBus::Event(entityId, &MotionExtractionRequestBus::Events::ExtractMotion, positionDelta, timeDelta);
|
||||
MotionExtractionRequestBus::Event(entityId, &MotionExtractionRequestBus::Events::ExtractMotion, positionDelta, delta);
|
||||
AZ::TransformBus::EventResult(currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM);
|
||||
}
|
||||
|
||||
@@ -884,7 +829,7 @@ namespace EMotionFX
|
||||
|
||||
// Register EMotionFX window with the main editor.
|
||||
AzToolsFramework::ViewPaneOptions emotionFXWindowOptions;
|
||||
emotionFXWindowOptions.isPreview = true;
|
||||
emotionFXWindowOptions.isPreview = false;
|
||||
emotionFXWindowOptions.isDeletable = true;
|
||||
emotionFXWindowOptions.isDockable = false;
|
||||
#if AZ_TRAIT_EMOTIONFX_MAIN_WINDOW_DETACHED
|
||||
|
||||
@@ -108,7 +108,6 @@ namespace EMotionFX
|
||||
void SetMediaRoot(const char* alias);
|
||||
|
||||
#if defined (EMOTIONFXANIMATION_EDITOR)
|
||||
void UpdateAnimationEditorPlugins(float delta);
|
||||
void NotifyRegisterViews() override;
|
||||
bool IsSystemActive(EditorAnimationSystemRequests::AnimationSystem systemType) override;
|
||||
|
||||
|
||||
@@ -32,6 +32,9 @@ set(_addtional_defines
|
||||
-dCPACK_RESOURCE_PATH=${CPACK_SOURCE_DIR}/Platform/Windows/Packaging
|
||||
)
|
||||
|
||||
file(REAL_PATH "${CPACK_SOURCE_DIR}/.." _root_path)
|
||||
file(TO_NATIVE_PATH "${_root_path}/scripts/signer/Platform/Windows/signer.ps1" _sign_script)
|
||||
|
||||
if(CPACK_LICENSE_URL)
|
||||
list(APPEND _addtional_defines -dCPACK_LICENSE_URL=${CPACK_LICENSE_URL})
|
||||
endif()
|
||||
@@ -55,6 +58,30 @@ set(_light_command
|
||||
-o "${_bootstrap_output_file}"
|
||||
)
|
||||
|
||||
set(_signing_command
|
||||
psexec.exe
|
||||
-accepteula
|
||||
-nobanner
|
||||
-s
|
||||
powershell.exe
|
||||
-NoLogo
|
||||
-ExecutionPolicy Bypass
|
||||
-File ${_sign_script}
|
||||
)
|
||||
|
||||
message(STATUS "Signing package files in ${_cpack_wix_out_dir}")
|
||||
execute_process(
|
||||
COMMAND ${_signing_command} -packagePath ${_cpack_wix_out_dir}
|
||||
RESULT_VARIABLE _signing_result
|
||||
ERROR_VARIABLE _signing_errors
|
||||
OUTPUT_VARIABLE _signing_output
|
||||
ECHO_OUTPUT_VARIABLE
|
||||
)
|
||||
|
||||
if(NOT ${_signing_result} EQUAL 0)
|
||||
message(FATAL_ERROR "An error occurred during signing package files. ${_signing_errors}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Creating Bootstrap Installer...")
|
||||
execute_process(
|
||||
COMMAND ${_candle_command}
|
||||
@@ -80,6 +107,19 @@ file(COPY ${_bootstrap_output_file}
|
||||
|
||||
message(STATUS "Bootstrap installer generated to ${CPACK_PACKAGE_DIRECTORY}/${_bootstrap_filename}")
|
||||
|
||||
message(STATUS "Signing bootstrap installer in ${CPACK_PACKAGE_DIRECTORY}")
|
||||
execute_process(
|
||||
COMMAND ${_signing_command} -bootstrapPath ${CPACK_PACKAGE_DIRECTORY}/${_bootstrap_filename}
|
||||
RESULT_VARIABLE _signing_result
|
||||
ERROR_VARIABLE _signing_errors
|
||||
OUTPUT_VARIABLE _signing_output
|
||||
ECHO_OUTPUT_VARIABLE
|
||||
)
|
||||
|
||||
if(NOT ${_signing_result} EQUAL 0)
|
||||
message(FATAL_ERROR "An error occurred during signing bootstrap installer. ${_signing_errors}")
|
||||
endif()
|
||||
|
||||
# use the internal default path if somehow not specified from cpack_configure_downloads
|
||||
if(NOT CPACK_UPLOAD_DIRECTORY)
|
||||
set(CPACK_UPLOAD_DIRECTORY ${CPACK_PACKAGE_DIRECTORY}/CPackUploads)
|
||||
@@ -100,11 +140,9 @@ if(NOT CPACK_UPLOAD_URL)
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(REAL_PATH "${CPACK_SOURCE_DIR}/.." _root_path)
|
||||
|
||||
file(TO_NATIVE_PATH "${_cpack_wix_out_dir}" _cpack_wix_out_dir)
|
||||
file(TO_NATIVE_PATH "${_root_path}/python/python.cmd" _python_cmd)
|
||||
file(TO_NATIVE_PATH "${_root_path}/scripts/build/tools/upload_to_s3.py" _upload_script)
|
||||
file(TO_NATIVE_PATH "${_cpack_wix_out_dir}" _cpack_wix_out_dir)
|
||||
|
||||
function(upload_to_s3 in_url in_local_path in_file_regex)
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
|
||||
file(REAL_PATH "${CPACK_SOURCE_DIR}/.." _root_path)
|
||||
set(_cpack_wix_out_dir ${CPACK_TOPLEVEL_DIRECTORY})
|
||||
file(TO_NATIVE_PATH "${_root_path}/scripts/signer/Platform/Windows/signer.ps1" _sign_script)
|
||||
|
||||
set(_signing_command
|
||||
psexec.exe
|
||||
-accepteula
|
||||
-nobanner
|
||||
-s
|
||||
powershell.exe
|
||||
-NoLogo
|
||||
-ExecutionPolicy Bypass
|
||||
-File ${_sign_script}
|
||||
)
|
||||
|
||||
message(STATUS "Signing executable files in ${_cpack_wix_out_dir}")
|
||||
execute_process(
|
||||
COMMAND ${_signing_command} -exePath ${_cpack_wix_out_dir}
|
||||
RESULT_VARIABLE _signing_result
|
||||
ERROR_VARIABLE _signing_errors
|
||||
OUTPUT_VARIABLE _signing_output
|
||||
ECHO_OUTPUT_VARIABLE
|
||||
)
|
||||
|
||||
if(NOT ${_signing_result} EQUAL 0)
|
||||
message(FATAL_ERROR "An error occurred during signing executable files. ${_signing_errors}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Signing exes complete!")
|
||||
@@ -108,7 +108,6 @@ set(_raw_text_license [[
|
||||
]])
|
||||
|
||||
if(LY_INSTALLER_DOWNLOAD_URL)
|
||||
|
||||
set(WIX_THEME_WARNING_IMAGE ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/warning.png)
|
||||
|
||||
if(LY_INSTALLER_LICENSE_URL)
|
||||
@@ -138,6 +137,10 @@ if(LY_INSTALLER_DOWNLOAD_URL)
|
||||
# the bootstrapper will at the very least need a different upgrade guid
|
||||
generate_wix_guid(CPACK_WIX_BOOTSTRAP_UPGRADE_GUID "${_guid_seed_base}_Bootstrap_UpgradeCode")
|
||||
|
||||
set(CPACK_PRE_BUILD_SCRIPTS
|
||||
${CPACK_SOURCE_DIR}/Platform/Windows/PackagingPreBuild.cmake
|
||||
)
|
||||
|
||||
set(CPACK_POST_BUILD_SCRIPTS
|
||||
${CPACK_SOURCE_DIR}/Platform/Windows/PackagingPostBuild.cmake
|
||||
)
|
||||
|
||||
@@ -351,17 +351,21 @@
|
||||
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo"
|
||||
}
|
||||
},
|
||||
"windows_installer": {
|
||||
"installer_vs2019": {
|
||||
"TAGS": [
|
||||
"nightly-clean"
|
||||
"nightly-clean",
|
||||
"nightly-installer"
|
||||
],
|
||||
"PIPELINE_ENV":{
|
||||
"NODE_LABEL":"windows-packaging"
|
||||
},
|
||||
"COMMAND": "build_installer_windows.cmd",
|
||||
"PARAMETERS": {
|
||||
"CONFIGURATION": "profile",
|
||||
"OUTPUT_DIRECTORY": "build\\windows_vs2019",
|
||||
"CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"!WIX! \"",
|
||||
"EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=https://www.o3debinaries.org -DLY_INSTALLER_LICENSE_URL=https://www.o3debinaries.org/license",
|
||||
"CPACK_BUCKET": "spectra-prism-staging-us-west-2",
|
||||
"EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=!INSTALLER_DOWNLOAD_URL! -DLY_INSTALLER_LICENSE_URL=!INSTALLER_DOWNLOAD_URL!/license",
|
||||
"CPACK_BUCKET": "!INSTALLER_BUCKET!",
|
||||
"CMAKE_LY_PROJECTS": "",
|
||||
"CMAKE_TARGET": "ALL_BUILD",
|
||||
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo"
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
|
||||
param (
|
||||
[String[]] $exePath,
|
||||
[String[]] $packagePath,
|
||||
[String[]] $bootstrapPath,
|
||||
[String[]] $certificate
|
||||
)
|
||||
|
||||
# Get prerequisites, certs, and paths ready
|
||||
$tempPath = [System.IO.Path]::GetTempPath() # Order of operations defined here: https://docs.microsoft.com/en-us/dotnet/api/system.io.path.gettemppath?view=net-5.0&tabs=windows#remarks
|
||||
$certThumbprint = Get-ChildItem -Path Cert:LocalMachine\MY -CodeSigningCert -ErrorAction Stop | Select-Object -ExpandProperty Thumbprint # Grab first certificate from local machine store
|
||||
|
||||
if ($certificate) {
|
||||
Write-Output "Checking certificate thumbprint $certificate"
|
||||
Get-ChildItem -Path Cert:LocalMachine\MY -ErrorAction SilentlyContinue | Where-Object {$_.Thumbprint -eq $certificate} # Prints certificate Thumbprint and Subject if found
|
||||
if($?) {
|
||||
$certThumbprint = $certificate
|
||||
}
|
||||
else {
|
||||
Write-Error "$certificate thumbprint not found, using $certThumbprint thumbprint instead"
|
||||
}
|
||||
}
|
||||
|
||||
Try {
|
||||
$signtoolPath = Resolve-Path "C:\Program Files*\Windows Kits\10\bin\*\x64\signtool.exe" -ErrorAction Stop | Select-Object -Last 1 -ExpandProperty Path
|
||||
$insigniaPath = Resolve-Path "C:\Program Files*\WiX*\bin\insignia.exe" -ErrorAction Stop | Select-Object -Last 1 -ExpandProperty Path
|
||||
}
|
||||
Catch {
|
||||
Write-Error "Signtool or Wix insignia not found! Exiting."
|
||||
}
|
||||
|
||||
function Write-Signature {
|
||||
param (
|
||||
$signtool,
|
||||
$thumbprint,
|
||||
$filename
|
||||
)
|
||||
|
||||
$attempts = 2
|
||||
$sleepSec = 5
|
||||
|
||||
Do {
|
||||
$attempts--
|
||||
Try {
|
||||
& $signtool sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 /sha1 $thumbprint /sm $filename
|
||||
& $signtool verify /pa /v $filename
|
||||
return
|
||||
}
|
||||
Catch {
|
||||
Write-Error $_.Exception.InnerException.Message -ErrorAction Continue
|
||||
Start-Sleep -Seconds $sleepSec
|
||||
}
|
||||
} while ($attempts -lt 0)
|
||||
|
||||
throw "Failed to sign $filename" # Bypassed in try block if the command is successful
|
||||
}
|
||||
|
||||
# Looping through each path insteaad of globbing to prevent hitting maximum command string length limit
|
||||
if ($exePath) {
|
||||
Write-Output "### Signing EXE files ###"
|
||||
$files = @(Get-ChildItem $exePath -Recurse *.exe | % { $_.FullName })
|
||||
foreach ($file in $files) {
|
||||
Write-Signature -signtool $signtoolPath -thumbprint $certThumbprint -filename $file
|
||||
}
|
||||
}
|
||||
|
||||
if ($packagePath) {
|
||||
Write-Output "### Signing CAB files ###"
|
||||
$files = @(Get-ChildItem $packagePath -Recurse *.cab | % { $_.FullName })
|
||||
foreach ($file in $files) {
|
||||
Write-Signature -signtool $signtoolPath -thumbprint $certThumbprint -filename $file
|
||||
}
|
||||
|
||||
Write-Output "### Signing MSI files ###"
|
||||
$files = @(Get-ChildItem $packagePath -Recurse *.msi | % { $_.FullName })
|
||||
foreach ($file in $files) {
|
||||
& $insigniaPath -im $files
|
||||
Write-Signature -signtool $signtoolPath -thumbprint $certThumbprint -filename $file
|
||||
}
|
||||
}
|
||||
|
||||
if ($bootstrapPath) {
|
||||
Write-Output "### Signing bootstrapper EXE ###"
|
||||
$files = @(Get-ChildItem $bootstrapPath -Recurse *.exe | % { $_.FullName })
|
||||
foreach ($file in $files) {
|
||||
& $insigniaPath -ib $file -o $tempPath\engine.exe
|
||||
Write-Signature -signtool $signtoolPath -thumbprint $certThumbprint -filename $tempPath\engine.exe
|
||||
& $insigniaPath -ab $tempPath\engine.exe $file -o $file
|
||||
Write-Signature -signtool $signtoolPath -thumbprint $certThumbprint -filename $file
|
||||
Remove-Item -Force $tempPath\engine.exe
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user