diff --git a/Code/Framework/AzCore/AzCore/AzCoreModule.cpp b/Code/Framework/AzCore/AzCore/AzCoreModule.cpp index c60ea1bd72..ec45335f95 100644 --- a/Code/Framework/AzCore/AzCore/AzCoreModule.cpp +++ b/Code/Framework/AzCore/AzCore/AzCoreModule.cpp @@ -22,6 +22,7 @@ #include #include #include +#include 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(), azrtti_typeid(), azrtti_typeid(), + azrtti_typeid(), }; } } diff --git a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp index 2bb88fbfa2..d2d19d1359 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp +++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp @@ -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 m_active; - AZStd::atomic m_busy; + AZStd::atomic 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 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("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(s_executorName, executor); + } } TaskExecutor::TaskExecutor(uint32_t threadCount) @@ -307,14 +325,12 @@ namespace AZ m_workers = reinterpret_cast(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 diff --git a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h index dc2fa5a4c8..7e1ff80902 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h +++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h @@ -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; diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp b/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp index 3fb93903c9..f57b06890a 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp @@ -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) { diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.h b/Code/Framework/AzCore/AzCore/Task/TaskGraph.h index 4b454c63de..9553013a4b 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.h +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.h @@ -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. diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl b/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl index e0ac74ba9d..7b2f0cefdc 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl @@ -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))... }; } + inline bool TaskGraph::IsEmpty() + { + return m_tasks.empty(); + } + inline void TaskGraph::Detach() { m_retained = false; diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp new file mode 100644 index 0000000000..eed461ecb4 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp @@ -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 +#include +#include +#include +#include + +// 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::Get() == nullptr) + { + Interface::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::Get() == this) + { + Interface::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(context)) + { + serializeContext->Class() + ->Version(1) + ; + + if (AZ::EditContext* ec = serializeContext->GetEditContext()) + { + ec->Class + ("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 diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.h b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.h new file mode 100644 index 0000000000..a4c6da9539 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.h @@ -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 +#include +#include +#include + +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; + }; +} diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index aa07959997..14579cbf33 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -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 diff --git a/Code/Framework/AzCore/Tests/TaskTests.cpp b/Code/Framework/AzCore/Tests/TaskTests.cpp index e743ab6643..9e839f60ee 100644 --- a/Code/Framework/AzCore/Tests/TaskTests.cpp +++ b/Code/Framework/AzCore/Tests/TaskTests.cpp @@ -34,7 +34,7 @@ namespace UnitTest AZ::AllocatorInstance::Create(); AZ::AllocatorInstance::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 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) diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp index 12974d03cf..5c7398a95c 100644 --- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp +++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -295,6 +296,7 @@ namespace AzFramework azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), + azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index d1c4d37821..ceb516d86f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -11,6 +11,7 @@ #include #include #include +#include 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::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) diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli index 1bd8e0b9a5..5e7088617b 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli @@ -16,6 +16,7 @@ #include #include #include +#include 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)); } } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/Decals/ViewSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/Decals/ViewSrg.azsli index 16b23432e1..260614b0f7 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/Decals/ViewSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/Decals/ViewSrg.azsli @@ -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 m_decalTextureArray0; - Texture2DArray m_decalTextureArray1; - Texture2DArray m_decalTextureArray2; - Texture2DArray m_decalTextureArray3; - Texture2DArray m_decalTextureArray4; + // This must match the variable NumTextureArrays in DecalTextureArrayFeatureProcessor.h + Texture2DArray m_decalTextureArrayDiffuse0; + Texture2DArray m_decalTextureArrayDiffuse1; + Texture2DArray m_decalTextureArrayDiffuse2; + Texture2DArray m_decalTextureArrayDiffuse3; + Texture2DArray m_decalTextureArrayDiffuse4; + + Texture2DArray m_decalTextureArrayNormalMaps0; + Texture2DArray m_decalTextureArrayNormalMaps1; + Texture2DArray m_decalTextureArrayNormalMaps2; + Texture2DArray m_decalTextureArrayNormalMaps3; + Texture2DArray m_decalTextureArrayNormalMaps4; uint m_decalCount; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance.azshader index 596f9a383e..31fae5d98b 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance_dx12_0.azshadervariant index 8481bdb15b..19e9fdfc8d 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance_null_0.azshadervariant index 2f2cd5cccf..43c4a615cf 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance_vulkan_0.azshadervariant index cb5de53e97..75f070a03e 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblenddistance_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance.azshader index fc11f9de64..0025388bc1 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance_dx12_0.azshadervariant index a35179ef7f..34a9b3659f 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance_null_0.azshadervariant index 2f2cd5cccf..4a2b0e9944 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance_vulkan_0.azshadervariant index 450f5595a4..c053a7db19 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridblendirradiance_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn.azshader index 3f3d113787..c507b12563 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn_dx12_0.azshadervariant index 76f61d2fd4..f60c713597 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn_null_0.azshadervariant index 2f2cd5cccf..3e810bcfb5 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn_vulkan_0.azshadervariant index 8b6ec377a6..5918f277b5 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdatecolumn_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow.azshader index 9050f90943..120eb70e54 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow_dx12_0.azshadervariant index 89ab946fd0..d38d779696 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow_null_0.azshadervariant index 2f2cd5cccf..6d7a604701 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow_vulkan_0.azshadervariant index 8fb8e92117..dee941cfae 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridborderupdaterow_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification.azshader index b9552a85db..e2e0fa90f5 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification_dx12_0.azshadervariant index c5d569a0a6..7a92fc2de5 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification_null_0.azshadervariant index 2f2cd5cccf..43408b26f3 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification_vulkan_0.azshadervariant index ae1bae8220..877085446d 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridclassification_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader index 66895758de..2c403c77f8 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_dx12_0.azshadervariant index fd22ac4fd3..4bcc47ee43 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_null_0.azshadervariant index 0bf9ac53d7..f173416210 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_vulkan_0.azshadervariant index 58ce16948b..0eb04a25b8 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader index 44a8b1426d..2847d0035a 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_dx12_0.azshadervariant index acfb2aa716..93cfb47819 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant index 0bf9ac53d7..4a16e24211 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant index 8c66d30c98..df52c9c8d2 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss.azshader index fad06893e9..d95fd5b3b2 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant index 1c0894bd23..c853de4d14 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_null_0.azshadervariant index 0bf9ac53d7..40e18c215c 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_vulkan_0.azshadervariant index cf8bbbd824..34761ccf98 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation.azshader index b46a476221..c19020dc84 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation_dx12_0.azshadervariant index d0287e91ec..a7d44b5541 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation_null_0.azshadervariant index 2f2cd5cccf..82e0065216 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation_vulkan_0.azshadervariant index f74f35eb99..20b81aee6c 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrelocation_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender.azshader index e309733d11..aec2786540 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender_dx12_0.azshadervariant index d015d52280..de851f187e 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender_null_0.azshadervariant index 7922055a82..5d8207dfdb 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender_vulkan_0.azshadervariant index 780163eb07..c819e57c4c 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridrender_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.cpp index ebdc884ecf..87ac0a9679 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.cpp @@ -5,7 +5,6 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ - #include "DecalTextureArray.h" #include #include @@ -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 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 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(); return GetStreamingImageAsset(*materialAsset, propertyName); } - - AZ::Data::Asset GetBaseColorImageAsset(const AZ::Data::Asset 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& DecalTextureArray::GetPackedTexture() const + const AZ::Data::Instance& 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 DecalTextureArray::BuildPackedMipChainAsset(const size_t numTexturesToCreate) + AZ::Data::Asset 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(mipLevels), aznumeric_cast(numTexturesToCreate)); + assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), aznumeric_cast(mipLevels), aznumeric_cast(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; - - RPI::StreamingImageAssetCreator assetCreator; - assetCreator.Begin(Data::AssetId(Uuid::CreateRandom())); - assetCreator.SetPoolAssetId(GetImagePoolId()); - assetCreator.SetFlags(RPI::StreamingImageFlags::None); - assetCreator.SetImageDescriptor(CreatePackedImageDescriptor(aznumeric_cast(numTexturesToCreate), GetNumMipLevels())); - assetCreator.SetImageViewDescriptor(imageViewDescriptor); - assetCreator.AddMipChainAsset(*mipChainAsset); - Data::Asset packedAsset; - const bool createdOk = assetCreator.End(packedAsset); - AZ_Error("TextureArrayData", createdOk, "Pack() call failed."); - m_textureArrayPacked = createdOk ? RPI::StreamingImage::FindOrCreate(packedAsset) : nullptr; + for (int i = 0; i < DecalMapType_Num; ++i) + { + const DecalMapType mapType = aznumeric_cast(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; + } + + 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(numTexturesToCreate), GetNumMipLevels(mapType))); + assetCreator.SetImageViewDescriptor(imageViewDescriptor); + assetCreator.AddMipChainAsset(*mipChainAsset); + Data::Asset 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 DecalTextureArray::GetRawImageData(int arrayLevel, const int mip) const + AZStd::array_view 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; } } diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.h b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.h index eb0f825e08..97bd8b9cbe 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.h +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.h @@ -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& 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& GetPackedTexture(const DecalMapType mapType) const; static bool IsValidDecalMaterial(const RPI::MaterialAsset& materialAsset); @@ -56,22 +70,25 @@ namespace AZ void OnAssetReady(Data::Asset 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 BuildPackedMipChainAsset(const size_t numTexturesToCreate); - - RHI::ImageDescriptor CreatePackedImageDescriptor(const uint16_t arraySize, const uint16_t mipLevels) const; + AZ::Data::Asset BuildPackedMipChainAsset(const DecalMapType mapType, const size_t numTexturesToCreate); + RHI::ImageDescriptor CreatePackedImageDescriptor(const DecalMapType mapType, 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 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 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 m_materials; - Data::Instance m_textureArrayPacked; + AZStd::array, DecalMapType_Num> m_textureArrayPacked; AZStd::unordered_set m_assetsCurrentlyLoading; }; diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp index c0b8f3315a..e5bf0bd9fa 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp @@ -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); - - 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()); + // The azsl shader should define several texture arrays such as: + // Texture2DArray m_decalTextureArrayDiffuse0; + // Texture2DArray m_decalTextureArrayDiffuse1; + // Texture2DArray m_decalTextureArrayDiffuse2; + // and + // Texture2DArray m_decalTextureArrayNormalMaps0; + // Texture2DArray m_decalTextureArrayNormalMaps1; + // Texture2DArray m_decalTextureArrayNormalMaps2; + static const AZStd::array ShaderNames = { "m_decalTextureArrayDiffuse", + "m_decalTextureArrayNormalMaps" }; + + 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(mapType)); + view->GetShaderResourceGroup()->SetImage(m_decalTextureArrayIndices[iter][mapType], packedTexture); + } iter = m_textureArrayList.next(iter); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h index 57c5c69e0d..13a6682629 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h @@ -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 m_decalTextureArrayIndices; + AZStd::array, NumTextureArrays> m_decalTextureArrayIndices; GpuBufferHandler m_decalBufferHandler; AsyncLoadTracker m_materialLoadTracker; diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp index 4d58948b98..b4f91e58d9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp @@ -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; diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h index 89e7173dd5..97c336ed41 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h @@ -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; diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp index d79240d4f1..4329fd556c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp @@ -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 const& probe1, AZStd::shared_ptr 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); }; diff --git a/Gems/Atom/Feature/Common/Code/Tests/Decals/DecalTextureArrayTests.cpp b/Gems/Atom/Feature/Common/Code/Tests/Decals/DecalTextureArrayTests.cpp index fcbcec0256..84c2b88507 100644 --- a/Gems/Atom/Feature/Common/Code/Tests/Decals/DecalTextureArrayTests.cpp +++ b/Gems/Atom/Feature/Common/Code/Tests/Decals/DecalTextureArrayTests.cpp @@ -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); } diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameScheduler.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameScheduler.h index ddcee53e69..48e7e0f339 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameScheduler.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameScheduler.h @@ -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> m_rayTracingShaderTablesToBuild; + + AZ::TaskGraphActiveInterface* m_taskGraphActive = nullptr; }; } } diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp index 5d2feb1e34..3363675d0e 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp @@ -25,9 +25,11 @@ #include #include +#include #include #include #include +#include namespace AZ { @@ -77,6 +79,8 @@ namespace AZ m_rootScope = m_rootScopeProducer->GetScope(); m_device = &device; + m_taskGraphActive = AZ::Interface::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(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); - - const auto compileGroupsForIntervalLambda = [srgPool, interval]() + 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"}; + + 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); + + auto compileTask = taskGraph.AddTask( + srgCompileDesc, + [srgPool, interval]() + { + AZ_PROFILE_SCOPE(RHI, "FrameScheduler : compileGroupsForIntervalLambda"); + srgPool->CompileGroupsForInterval(interval); + }); + compileTask.Precedes(srgCompileEndTask); + } + }; - AZ::Job* executeGroupJob = AZ::CreateJobFunction(AZStd::move(compileGroupsForIntervalLambda), true, nullptr); - executeGroupJob->SetDependent(&jobCompletion); - executeGroupJob->Start(); + resourcePoolDatabase.ForEachShaderResourceGroupPool(AZStd::move(compileIntervalsFunction)); + if (!taskGraph.IsEmpty()) + { + AZ::TaskGraphEvent finishedEvent; + taskGraph.Submit(&finishedEvent); + finishedEvent.Wait(); } - }; + } + else // use Job system + { + const auto compileGroupsBeginFunction = [](ShaderResourceGroupPool* srgPool) + { + srgPool->CompileGroupsBegin(); + }; - resourcePoolDatabase.ForEachShaderResourceGroupPool(AZStd::move(compileIntervalsFunction)); + resourcePoolDatabase.ForEachShaderResourceGroupPool(compileGroupsBeginFunction); - jobCompletion.StartAndWaitForCompletion(); + // Iterate over each SRG pool and fork jobs to compile SRGs. + AZ::JobCompletion jobCompletion; - const auto compileGroupsEndFunction = [](ShaderResourceGroupPool* srgPool) - { - srgPool->CompileGroupsEnd(); - }; + const auto compileIntervalsFunction = [compilesPerJob, &jobCompletion](ShaderResourceGroupPool* srgPool) + { + const uint32_t compilesInPool = srgPool->GetGroupsToCompileCount(); + const uint32_t jobCount = DivideByMultiple(compilesInPool, compilesPerJob); - resourcePoolDatabase.ForEachShaderResourceGroupPool(compileGroupsEndFunction); + 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(AZStd::move(compileIntervalsFunction)); + + jobCompletion.StartAndWaitForCompletion(); + + const auto compileGroupsEndFunction = [](ShaderResourceGroupPool* srgPool) + { + srgPool->CompileGroupsEnd(); + }; + + resourcePoolDatabase.ForEachShaderResourceGroupPool(compileGroupsEndFunction); + } } else { diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp index bd163f1419..eadd571452 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp @@ -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; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h index b1c6aac92d..fc81368331 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -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 m_featureProcessors; // List of pipelines of this scene. Each pipeline has an unique pipeline Id. AZStd::vector 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; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 646cba1999..c5d82c6f29 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -23,6 +23,8 @@ #include #include +#include + #include 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::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); } } diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/001_DefaultWhite.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/001_DefaultWhite.material index 8fe732cb09..2e4eee7f8e 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/001_DefaultWhite.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/001_DefaultWhite.material @@ -1,6 +1,6 @@ { "description": "", - "materialType": "Materials\\Types\\StandardPBR.materialtype", + "materialType": "Materials/Types/StandardPBR.materialtype", "parentMaterial": "", "propertyLayoutVersion": 3 -} +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/002_BaseColorLerp.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/002_BaseColorLerp.material index 8509e08d78..f8214e1b2e 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/002_BaseColorLerp.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/002_BaseColorLerp.material @@ -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" } } -} +} \ No newline at end of file diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp index 8ca4e1de81..06088790d9 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp @@ -69,6 +69,9 @@ AZ_PUSH_DISABLE_WARNING(4267, "-Wconversion") AZ_POP_DISABLE_WARNING #include +#include +#include + 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; + } + + const size_t numPlugins = pluginManager->GetNumActivePlugins(); + for (size_t i = 0; i < numPlugins; ++i) + { + EMStudio::EMStudioPlugin* plugin = pluginManager->GetActivePlugin(i); + plugin->ProcessFrame(timeDelta); + } + } -#include + 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 diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.h index a11f11c6a8..585101f7d2 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.h @@ -9,6 +9,7 @@ #pragma once #if !defined(Q_MOC_RUN) +#include #include #include #include @@ -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(); diff --git a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp index 56c22bb1ff..0f87c488b9 100644 --- a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp @@ -62,7 +62,6 @@ #if defined(EMOTIONFXANIMATION_EDITOR) // EMFX tools / editor includes -# include // Qt # include // 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,36 +609,9 @@ namespace EMotionFX #if defined (EMOTIONFXANIMATION_EDITOR) AZ_UNUSED(delta); - const float realDelta = m_updateTimer.StampAndGetDeltaTimeInSeconds(); - - // 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; + delta = m_updateTimer.StampAndGetDeltaTimeInSeconds(); +#endif - if (isEditorActive) - { - UpdateAnimationEditorPlugins(realDelta); - } -#else // Flush events prior to updating EMotion FX. ActorNotificationBus::ExecuteQueuedEvents(); @@ -673,9 +620,7 @@ namespace EMotionFX // 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 diff --git a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h index a716f0aa9b..5e30820ca3 100644 --- a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h @@ -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; diff --git a/cmake/Platform/Windows/PackagingPostBuild.cmake b/cmake/Platform/Windows/PackagingPostBuild.cmake index 5e09743373..377a9fb221 100644 --- a/cmake/Platform/Windows/PackagingPostBuild.cmake +++ b/cmake/Platform/Windows/PackagingPostBuild.cmake @@ -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) diff --git a/cmake/Platform/Windows/PackagingPreBuild.cmake b/cmake/Platform/Windows/PackagingPreBuild.cmake new file mode 100644 index 0000000000..d3924c7a02 --- /dev/null +++ b/cmake/Platform/Windows/PackagingPreBuild.cmake @@ -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!") diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 5bb9928b61..ced7757852 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -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 ) diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 3848e6f980..e33025a4d9 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -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" diff --git a/scripts/signer/Platform/Windows/signer.ps1 b/scripts/signer/Platform/Windows/signer.ps1 new file mode 100644 index 0000000000..5366564140 --- /dev/null +++ b/scripts/signer/Platform/Windows/signer.ps1 @@ -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 + } +} \ No newline at end of file