From 17e9c17f311bca70b61b3295c4aef49d23a2dd46 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 26 May 2021 19:18:18 -0700 Subject: [PATCH 01/71] Added Occlusion Culling Planes and RPI Culling support for Masked Occlusion Culling --- ...ionCullingPlaneFeatureProcessorInterface.h | 41 +++++ .../Code/Source/CommonSystemComponent.cpp | 4 + .../OcclusionCullingPlaneFeatureProcessor.cpp | 96 ++++++++++ .../OcclusionCullingPlaneFeatureProcessor.h | 75 ++++++++ .../Code/atom_feature_common_files.cmake | 2 + .../atom_feature_common_public_files.cmake | 1 + .../Code/Include/Atom/RPI.Public/Culling.h | 12 +- .../RPI/Code/Include/Atom/RPI.Public/View.h | 12 +- .../Source/Platform/Windows/PAL_windows.cmake | 13 ++ .../RPI/Code/Source/RPI.Public/Culling.cpp | 171 +++++++++++++----- .../Atom/RPI/Code/Source/RPI.Public/Scene.cpp | 3 +- Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp | 68 ++++++- .../Atom/RPI/Code/atom_rpi_public_files.cmake | 4 + .../CommonFeatures/Code/Source/Module.cpp | 4 + .../EditorOcclusionCullingPlaneComponent.cpp | 91 ++++++++++ .../EditorOcclusionCullingPlaneComponent.h | 43 +++++ .../OcclusionCullingPlaneComponent.cpp | 43 +++++ .../OcclusionCullingPlaneComponent.h | 37 ++++ .../OcclusionCullingPlaneComponentConstants.h | 22 +++ ...clusionCullingPlaneComponentController.cpp | 137 ++++++++++++++ ...OcclusionCullingPlaneComponentController.h | 78 ++++++++ ...egration_commonfeatures_editor_files.cmake | 2 + ...omlyintegration_commonfeatures_files.cmake | 4 + 23 files changed, 913 insertions(+), 50 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h create mode 100644 Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp create mode 100644 Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h new file mode 100644 index 0000000000..07a6179e78 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h @@ -0,0 +1,41 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class OcclusionCullingPlane; + + using OcclusionCullingPlaneHandle = AZStd::shared_ptr; + + // OcclusionCullingPlaneFeatureProcessorInterface provides an interface to the feature processor for code outside of Atom + class OcclusionCullingPlaneFeatureProcessorInterface + : public RPI::FeatureProcessor + { + public: + AZ_RTTI(AZ::Render::OcclusionCullingPlaneFeatureProcessorInterface, "{50F6B45E-A622-44EC-B962-DE25FBD44095}"); + + virtual OcclusionCullingPlaneHandle AddOcclusionCullingPlane(const AZ::Transform& transform) = 0; + virtual void RemoveOcclusionCullingPlane(OcclusionCullingPlaneHandle& handle) = 0; + virtual bool IsValidOcclusionCullingPlaneHandle(const OcclusionCullingPlaneHandle& occlusionCullingPlane) const = 0; + virtual void SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) = 0; + virtual void SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enabled) = 0; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index 089a6168b1..614e614c06 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -102,6 +102,7 @@ #include #include #include +#include namespace AZ { @@ -137,6 +138,7 @@ namespace AZ ModelPreset::Reflect(context); DiffuseProbeGridFeatureProcessor::Reflect(context); RayTracingFeatureProcessor::Reflect(context); + OcclusionCullingPlaneFeatureProcessor::Reflect(context); if (SerializeContext* serialize = azrtti_cast(context)) { @@ -193,6 +195,7 @@ namespace AZ AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); + AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); // Add SkyBox pass auto* passSystem = RPI::PassSystemInterface::Get(); @@ -295,6 +298,7 @@ namespace AZ AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); + AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); } void CommonSystemComponent::LoadPassTemplateMappings() diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp new file mode 100644 index 0000000000..d4a1a37521 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp @@ -0,0 +1,96 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void OcclusionCullingPlaneFeatureProcessor::Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext + ->Class() + ->Version(0); + } + } + + void OcclusionCullingPlaneFeatureProcessor::Activate() + { + m_occlusionCullingPlanes.reserve(InitialOcclusionCullingPlanesAllocationSize); + + EnableSceneNotification(); + } + + void OcclusionCullingPlaneFeatureProcessor::Deactivate() + { + AZ_Warning("OcclusionCullingPlaneFeatureProcessor", m_occlusionCullingPlanes.size() == 0, + "Deactivating the OcclusionCullingPlaneFeatureProcessor, but there are still outstanding occlusion planes. Components\n" + "using OcclusionCullingPlaneHandles should free them before the OcclusionCullingPlaneFeatureProcessor is deactivated.\n" + ); + + DisableSceneNotification(); + } + + void OcclusionCullingPlaneFeatureProcessor::Simulate([[maybe_unused]] const FeatureProcessor::SimulatePacket& packet) + { + AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); + + AZStd::vector occlusionCullingPlanes; + for (auto& occlusionCullingPlane : m_occlusionCullingPlanes) + { + occlusionCullingPlanes.push_back(occlusionCullingPlane->GetTransform()); + } + GetParentScene()->GetCullingScene()->SetOcclusionCullingPlanes(occlusionCullingPlanes); + } + + OcclusionCullingPlaneHandle OcclusionCullingPlaneFeatureProcessor::AddOcclusionCullingPlane(const AZ::Transform& transform) + { + AZStd::shared_ptr occlusionCullingPlane = AZStd::make_shared(); + occlusionCullingPlane->SetTransform(transform); + m_occlusionCullingPlanes.push_back(occlusionCullingPlane); + return occlusionCullingPlane; + } + + void OcclusionCullingPlaneFeatureProcessor::RemoveOcclusionCullingPlane(OcclusionCullingPlaneHandle& occlusionCullingPlane) + { + AZ_Assert(occlusionCullingPlane.get(), "RemoveOcclusionCullingPlane called with an invalid handle"); + + auto itEntry = AZStd::find_if(m_occlusionCullingPlanes.begin(), m_occlusionCullingPlanes.end(), [&](AZStd::shared_ptr const& entry) + { + return (entry == occlusionCullingPlane); + }); + + AZ_Assert(itEntry != m_occlusionCullingPlanes.end(), "RemoveOcclusionCullingPlane called with an occlusion plane that is not in the occlusion plane list"); + m_occlusionCullingPlanes.erase(itEntry); + occlusionCullingPlane = nullptr; + } + + void OcclusionCullingPlaneFeatureProcessor::SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) + { + AZ_Assert(occlusionCullingPlane.get(), "SetTransform called with an invalid handle"); + occlusionCullingPlane->SetTransform(transform); + } + + void OcclusionCullingPlaneFeatureProcessor::SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enabled) + { + AZ_Assert(occlusionCullingPlane.get(), "Enable called with an invalid handle"); + occlusionCullingPlane->SetEnabled(enabled); + } + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h new file mode 100644 index 0000000000..c54c816bfd --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h @@ -0,0 +1,75 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace AZ +{ + namespace Render + { + //! This class represents an OcclusionCullingPlane which is used to cull meshes that are inside the view frustum + class OcclusionCullingPlane final + { + public: + OcclusionCullingPlane() = default; + ~OcclusionCullingPlane() = default; + + void SetTransform(const AZ::Transform& transform) { m_transform = transform; } + const AZ::Transform& GetTransform() const { return m_transform; } + + void SetEnabled(bool enabled) { m_enabled = enabled; } + bool GetEnabled() const { return m_enabled; } + + private: + AZ::Transform m_transform; + bool m_enabled = true; + }; + + //! This class manages OcclusionCullingPlanes which are used to cull meshes that are inside the view frustum + class OcclusionCullingPlaneFeatureProcessor final + : public OcclusionCullingPlaneFeatureProcessorInterface + { + public: + AZ_RTTI(AZ::Render::OcclusionCullingPlaneFeatureProcessor, "{C3DE91D7-EF7A-4A82-A55F-E22BC52074EA}", OcclusionCullingPlaneFeatureProcessorInterface); + + static void Reflect(AZ::ReflectContext* context); + + OcclusionCullingPlaneFeatureProcessor() = default; + virtual ~OcclusionCullingPlaneFeatureProcessor() = default; + + // OcclusionCullingPlaneFeatureProcessorInterface overrides + OcclusionCullingPlaneHandle AddOcclusionCullingPlane(const AZ::Transform& transform) override; + void RemoveOcclusionCullingPlane(OcclusionCullingPlaneHandle& handle) override; + bool IsValidOcclusionCullingPlaneHandle(const OcclusionCullingPlaneHandle& occlusionCullingPlane) const override { return (occlusionCullingPlane.get() != nullptr); } + void SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) override; + void SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enable) override; + + // FeatureProcessor overrides + void Activate() override; + void Deactivate() override; + void Simulate(const FeatureProcessor::SimulatePacket& packet) override; + + // retrieve the full list of occlusion planes + using OcclusionCullingPlaneVector = AZStd::vector>; + OcclusionCullingPlaneVector& GetOcclusionCullingPlanes() { return m_occlusionCullingPlanes; } + + private: + AZ_DISABLE_COPY_MOVE(OcclusionCullingPlaneFeatureProcessor); + + // list of occlusion planes + const size_t InitialOcclusionCullingPlanesAllocationSize = 64; + OcclusionCullingPlaneVector m_occlusionCullingPlanes; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 8926b0c19f..f3ee1b4757 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -175,6 +175,8 @@ set(FILES Source/MorphTargets/MorphTargetComputePass.h Source/MorphTargets/MorphTargetDispatchItem.cpp Source/MorphTargets/MorphTargetDispatchItem.h + Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h + Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp Source/PostProcess/PostProcessBase.cpp Source/PostProcess/PostProcessBase.h Source/PostProcess/PostProcessFeatureProcessor.cpp diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake index 9034859707..3df15f9a70 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake @@ -44,6 +44,7 @@ set(FILES Include/Atom/Feature/ParamMacros/StartParamFunctionsVirtual.inl Include/Atom/Feature/ParamMacros/StartParamMembers.inl Include/Atom/Feature/ParamMacros/StartParamSerializeContext.inl + Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h Include/Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h Include/Atom/Feature/PostProcess/PostProcessParams.inl Include/Atom/Feature/PostProcess/PostProcessSettings.inl diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index c7e9cc4706..17e0a1f82d 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -31,7 +31,7 @@ #include #include - +#include #include #include @@ -213,8 +213,11 @@ namespace AZ void Activate(const class Scene* parentScene); void Deactivate(); + //! Sets a list of occlusion planes to be used during the culling process. + void SetOcclusionCullingPlanes(const AZStd::vector& occlusionCullingPlanes) { m_occlusionCullingPlanes = occlusionCullingPlanes; } + //! Notifies the CullingScene that culling will begin for this frame. - void BeginCulling(const AZStd::vector& views); + void BeginCulling(const AZStd::vector& views, const AZStd::vector& activePipelines); //! Notifies the CullingScene that the culling is done for this frame. void EndCulling(); @@ -251,12 +254,9 @@ namespace AZ const Scene* m_parentScene = nullptr; AzFramework::IVisibilityScene* m_visScene = nullptr; - CullingDebugContext m_debugCtx; - AZStd::concurrency_checker m_cullDataConcurrencyCheck; - - AZStd::mutex m_mutex; + AZStd::vector m_occlusionCullingPlanes; }; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h index aad099dc23..74c841d2a5 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -57,7 +58,7 @@ namespace AZ //! Only use this function to create a new view object. And force using smart pointer to manage view's life time static ViewPtr CreateView(const AZ::Name& name, UsageFlags usage); - ~View() = default; + ~View(); void SetDrawListMask(const RHI::DrawListMask& drawListMask); RHI::DrawListMask GetDrawListMask() const { return m_drawListMask; } @@ -126,6 +127,12 @@ namespace AZ //! Notifies consumers when the world to clip matrix has changed. void ConnectWorldToClipMatrixChangedHandler(MatrixChangedEvent::Handler& handler); + //! Prepare for view culling + void BeginCulling(const AZStd::vector& activePipelines); + + //! Returns the masked occlusion culling interface + MaskedOcclusionCulling* GetMaskedOcclusionCulling(); + private: View() = delete; View(const AZ::Name& name, UsageFlags usage); @@ -193,6 +200,9 @@ namespace AZ MatrixChangedEvent m_onWorldToClipMatrixChange; MatrixChangedEvent m_onWorldToViewMatrixChange; + + // Software occlusion culling + MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr; }; AZ_DEFINE_ENUM_BITWISE_OPERATORS(View::UsageFlags); diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake b/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake index c060b8bbaa..51e42d5216 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake @@ -10,3 +10,16 @@ # set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED TRUE) + +ly_add_source_properties( + SOURCES Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp + PROPERTY COMPILE_OPTIONS + VALUES /arch:AVX2 /W3 +) +ly_add_source_properties( + SOURCES + Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp + Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp + PROPERTY COMPILE_OPTIONS + VALUES /W3 +) \ No newline at end of file diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index c64f08e4f8..ab25fb14fb 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -262,21 +262,24 @@ namespace AZ public: AZ_CLASS_ALLOCATOR(AddObjectsToViewJob, ThreadPoolAllocator, 0); + struct JobData + { + CullingDebugContext* m_debugCtx = nullptr; + MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr; + const Scene* m_scene = nullptr; + View* m_view = nullptr; + Frustum m_frustum; + }; + private: - CullingDebugContext* m_debugCtx; - const Scene* m_scene; - View* m_view; - Frustum m_frustum; + const AZStd::shared_ptr m_jobData; CullingScene::WorkListType m_worklist; public: - AddObjectsToViewJob(CullingDebugContext& debugCtx, const Scene& scene, View& view, Frustum& frustum, CullingScene::WorkListType& worklist) + AddObjectsToViewJob(const AZStd::shared_ptr& jobData, CullingScene::WorkListType& worklist) : Job(true, nullptr) //auto-deletes, no JobContext - , m_debugCtx(&debugCtx) - , m_scene(&scene) - , m_view(&view) - , m_frustum(frustum) //capture by value - , m_worklist(AZStd::move(worklist)) //capture by value + , m_jobData(jobData) + , m_worklist(worklist) { } @@ -285,37 +288,40 @@ namespace AZ { AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); - const View::UsageFlags viewFlags = m_view->GetUsageFlags(); - const RHI::DrawListMask drawListMask = m_view->GetDrawListMask(); + const View::UsageFlags viewFlags = m_jobData->m_view->GetUsageFlags(); + const RHI::DrawListMask drawListMask = m_jobData->m_view->GetDrawListMask(); uint32_t numDrawPackets = 0; uint32_t numVisibleCullables = 0; for (const AzFramework::IVisibilityScene::NodeData& nodeData : m_worklist) { //If a node is entirely contained within the frustum, then we can skip the fine grained culling. - bool nodeIsContainedInFrustum = ShapeIntersection::Contains(m_frustum, nodeData.m_bounds); + bool nodeIsContainedInFrustum = ShapeIntersection::Contains(m_jobData->m_frustum, nodeData.m_bounds); #ifdef AZ_CULL_PROFILE_VERBOSE AZ_PROFILE_SCOPE_DYNAMIC(Debug::ProfileCategory::AzRender, "process node (view: %s, skip fine cull: %d", m_view->GetName().GetCStr(), nodeIsContainedInFrustum ? 1 : 0); #endif - if (nodeIsContainedInFrustum || !m_debugCtx->m_enableFrustumCulling) + if (nodeIsContainedInFrustum || !m_jobData->m_debugCtx->m_enableFrustumCulling) { //Add all objects within this node to the view, without any extra culling for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries) { - if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) + if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) { - Cullable* c = static_cast(visibleEntry->m_userData); - if ((c->m_cullData.m_drawListMask & drawListMask).none() || - c->m_cullData.m_hideFlags & viewFlags || - c->m_cullData.m_scene != m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this + if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { - continue; + Cullable* c = static_cast(visibleEntry->m_userData); + if ((c->m_cullData.m_drawListMask & drawListMask).none() || + c->m_cullData.m_hideFlags & viewFlags || + c->m_cullData.m_scene != m_jobData->m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this + { + continue; + } + numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); + ++numVisibleCullables; } - numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_view); - ++numVisibleCullables; } } } @@ -329,66 +335,69 @@ namespace AZ Cullable* c = static_cast(visibleEntry->m_userData); if ((c->m_cullData.m_drawListMask & drawListMask).none() || c->m_cullData.m_hideFlags & viewFlags || - c->m_cullData.m_scene != m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this + c->m_cullData.m_scene != m_jobData->m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this { continue; } - IntersectResult res = ShapeIntersection::Classify(m_frustum, c->m_cullData.m_boundingSphere); + IntersectResult res = ShapeIntersection::Classify(m_jobData->m_frustum, c->m_cullData.m_boundingSphere); if (res == IntersectResult::Exterior) { continue; } - else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(m_frustum, c->m_cullData.m_boundingObb)) + else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(m_jobData->m_frustum, c->m_cullData.m_boundingObb)) { - numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_view); - ++numVisibleCullables; + if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) + { + numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); + ++numVisibleCullables; + } } } } } - if (m_debugCtx->m_debugDraw && (m_view->GetName() == m_debugCtx->m_currentViewSelectionName)) + if (m_jobData->m_debugCtx->m_debugDraw && (m_jobData->m_view->GetName() == m_jobData->m_debugCtx->m_currentViewSelectionName)) { AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "debug draw culling"); - AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(m_scene); + AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(m_jobData->m_scene); if (auxGeomPtr) { //Draw the node bounds // "Fully visible" nodes are nodes that are fully inside the frustum. "Partially visible" nodes intersect the edges of the frustum. // Since the nodes of an octree have lots of overlapping boxes with coplanar edges, it's easier to view these separately, so // we have a few debug booleans to toggle which ones to draw. - if (nodeIsContainedInFrustum && m_debugCtx->m_drawFullyVisibleNodes) + if (nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawFullyVisibleNodes) { auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Lime, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off); } - else if (!nodeIsContainedInFrustum && m_debugCtx->m_drawPartiallyVisibleNodes) + else if (!nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawPartiallyVisibleNodes) { auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Yellow, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off); } //Draw bounds on individual objects - if (m_debugCtx->m_drawBoundingBoxes || m_debugCtx->m_drawBoundingSpheres || m_debugCtx->m_drawLodRadii) + if (m_jobData->m_debugCtx->m_drawBoundingBoxes || m_jobData->m_debugCtx->m_drawBoundingSpheres || m_jobData->m_debugCtx->m_drawLodRadii) { for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries) { if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { Cullable* c = static_cast(visibleEntry->m_userData); - if (m_debugCtx->m_drawBoundingBoxes) + if (m_jobData->m_debugCtx->m_drawBoundingBoxes) { auxGeomPtr->DrawObb(c->m_cullData.m_boundingObb, Matrix3x4::Identity(), nodeIsContainedInFrustum ? Colors::Lime : Colors::Yellow, AuxGeomDraw::DrawStyle::Line); } - if (m_debugCtx->m_drawBoundingSpheres) + if (m_jobData->m_debugCtx->m_drawBoundingSpheres) { auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(), c->m_cullData.m_boundingSphere.GetRadius(), Color(0.5f, 0.5f, 0.5f, 0.3f), AuxGeomDraw::DrawStyle::Shaded); } - if (m_debugCtx->m_drawLodRadii) + if (m_jobData->m_debugCtx->m_drawLodRadii) { auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData.m_lodSelectionRadius, @@ -401,9 +410,9 @@ namespace AZ } } - if (m_debugCtx->m_enableStats) + if (m_jobData->m_debugCtx->m_enableStats) { - CullingDebugContext::CullStats& cullStats = m_debugCtx->GetCullStatsForView(m_view); + CullingDebugContext::CullStats& cullStats = m_jobData->m_debugCtx->GetCullStatsForView(m_jobData->m_view); //no need for mutex here since these are all atomics cullStats.m_numVisibleDrawPackets += numDrawPackets; @@ -411,6 +420,29 @@ namespace AZ ++cullStats.m_numJobs; } } + + MaskedOcclusionCulling::CullingResult TestOcclusionCulling(AzFramework::VisibilityEntry* visibleEntry) + { + if (!m_jobData->m_maskedOcclusionCulling) + { + return MaskedOcclusionCulling::CullingResult::VISIBLE; + } + + // convert the bounding box of the visibility entry to NDC + AZ::Vector4 clipSpaceMin = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(visibleEntry->m_boundingVolume.GetMin()); + float depth = clipSpaceMin.GetW(); + AZ::Vector4 ndcMin = clipSpaceMin / clipSpaceMin.GetW(); + + AZ::Vector4 clipSpaceMax = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(visibleEntry->m_boundingVolume.GetMax()); + depth = AZStd::min(depth, clipSpaceMax.GetW()); + AZ::Vector4 ndcMax = clipSpaceMax / clipSpaceMax.GetW(); + + Vector2 rectMin(AZStd::min(ndcMin.GetX(), ndcMax.GetX()), AZStd::min(ndcMin.GetY(), ndcMax.GetY())); + Vector2 rectMax(AZStd::max(ndcMin.GetX(), ndcMax.GetX()), AZStd::max(ndcMin.GetY(), ndcMax.GetY())); + + // test against the occlusion buffer, which contains only the manually placed occlusion planes + return m_jobData->m_maskedOcclusionCulling->TestRect(rectMin.GetX(), rectMin.GetY(), rectMax.GetX(), rectMax.GetY(), depth); + } }; void CullingScene::ProcessCullables(const Scene& scene, View& view, AZ::Job& parentJob) @@ -444,8 +476,53 @@ namespace AZ cullStats.m_cameraViewToWorld = view.GetViewToWorldMatrix(); } + // setup occlusion culling, if necessary + MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionCullingPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); + if (maskedOcclusionCulling) + { + for (const AZ::Transform& transform : m_occlusionCullingPlanes) + { + // find the corners of the plane + static const Vector3 BL = Vector3(-0.5f, -0.5f, 0.0f); + static const Vector3 BR = Vector3(0.5f, -0.5f, 0.0f); + static const Vector3 TL = Vector3(-0.5f, 0.5f, 0.0f); + static const Vector3 TR = Vector3(0.5f, 0.5f, 0.0f); + + Vector3 planeBL = transform.TransformPoint(BL); + Vector3 planeBR = transform.TransformPoint(BR); + Vector3 planeTL = transform.TransformPoint(TL); + Vector3 planeTR = transform.TransformPoint(TR); + + // convert to clip-space + Vector4 projectedBL = view.GetWorldToClipMatrix() * Vector4(planeBL); + Vector4 projectedBR = view.GetWorldToClipMatrix() * Vector4(planeBR); + Vector4 projectedTL = view.GetWorldToClipMatrix() * Vector4(planeTL); + Vector4 projectedTR = view.GetWorldToClipMatrix() * Vector4(planeTR); + + // store to float array + float verts[16]; + projectedBL.StoreToFloat4(&verts[0]); + projectedBR.StoreToFloat4(&verts[4]); + projectedTL.StoreToFloat4(&verts[8]); + projectedTR.StoreToFloat4(&verts[12]); + + static uint32_t indices[6] = { 0, 2, 1, 2, 3, 1 }; + + // render into the occlusion buffer, specifying BACKFACE_NONE so it functions as a double-sided occluder + maskedOcclusionCulling->RenderTriangles((float*)verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE); + } + } + WorkListType worklist; - auto nodeVisitorLambda = [this, &scene, &view, &parentJob, &frustum, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void + + AZStd::shared_ptr jobData = AZStd::make_shared(); + jobData->m_debugCtx = &m_debugCtx; + jobData->m_maskedOcclusionCulling = maskedOcclusionCulling; + jobData->m_scene = &scene; + jobData->m_view = &view; + jobData->m_frustum = frustum; + + auto nodeVisitorLambda = [this, jobData, &parentJob, &frustum, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void { AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "nodeVisitorLambda()"); AZ_Assert(nodeData.m_entries.size() > 0, "should not get called with 0 entries"); @@ -458,7 +535,7 @@ namespace AZ if (worklist.size() == worklist.capacity()) { //Kick off a job to process the (full) worklist - AddObjectsToViewJob* job = aznew AddObjectsToViewJob(m_debugCtx, scene, view, frustum, worklist); //pool allocated (cheap), auto-deletes when job finishes + AddObjectsToViewJob* job = aznew AddObjectsToViewJob(jobData, worklist); //pool allocated (cheap), auto-deletes when job finishes worklist.clear(); parentJob.SetContinuation(job); job->Start(); @@ -476,8 +553,15 @@ namespace AZ if (worklist.size() > 0) { + AZStd::shared_ptr remainingJobData = AZStd::make_shared(); + remainingJobData->m_debugCtx = &m_debugCtx; + remainingJobData->m_maskedOcclusionCulling = maskedOcclusionCulling; + remainingJobData->m_scene = &scene; + remainingJobData->m_view = &view; + remainingJobData->m_frustum = frustum; + //Kick off a job to process any remaining workitems - AddObjectsToViewJob* job = aznew AddObjectsToViewJob(m_debugCtx, scene, view, frustum, worklist); //pool allocated (cheap), auto-deletes when job finishes + AddObjectsToViewJob* job = aznew AddObjectsToViewJob(remainingJobData, worklist); //pool allocated (cheap), auto-deletes when job finishes parentJob.SetContinuation(job); job->Start(); } @@ -559,13 +643,18 @@ namespace AZ } } - void CullingScene::BeginCulling(const AZStd::vector& views) + void CullingScene::BeginCulling(const AZStd::vector& views, const AZStd::vector& activePipelines) { m_cullDataConcurrencyCheck.soft_lock(); m_debugCtx.ResetCullStats(); m_debugCtx.m_numCullablesInScene = GetNumCullables(); + for (auto& view : views) + { + view->BeginCulling(activePipelines); + } + AuxGeomDrawPtr auxGeom; if (m_debugCtx.m_debugDraw) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 7e33750eb5..16c2189a00 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -499,7 +500,7 @@ namespace AZ } // Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs) - m_cullingScene->BeginCulling(m_renderPacket.m_views); + m_cullingScene->BeginCulling(m_renderPacket.m_views, activePipelines); for (ViewPtr& viewPtr : m_renderPacket.m_views) { AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp index 21a46693d5..3060fc34a3 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp @@ -15,7 +15,8 @@ #include #include #include - +#include +#include #include #include @@ -51,6 +52,18 @@ namespace AZ { m_shaderResourceGroup = ShaderResourceGroup::Create(viewSrgAsset); } + + m_maskedOcclusionCulling = MaskedOcclusionCulling::Create(); + m_maskedOcclusionCulling->SetNearClipPlane(0.1f); + } + + View::~View() + { + if (m_maskedOcclusionCulling) + { + MaskedOcclusionCulling::Destroy(m_maskedOcclusionCulling); + m_maskedOcclusionCulling = nullptr; + } } void View::SetDrawListMask(const RHI::DrawListMask& drawListMask) @@ -374,5 +387,58 @@ namespace AZ m_shaderResourceGroup->Compile(); m_needBuildSrg = false; } + + void View::BeginCulling(const AZStd::vector& activePipelines) + { + // retrieve current resolution + Vector2 resolution(0.0f, 0.0f); + for (auto& pipeline : activePipelines) + { + ViewPtr pipelineView = pipeline->GetDefaultView(); + if (pipelineView.get() == this) + { + RPI::SwapChainPass* pass = AZ::RPI::PassSystemInterface::Get()->FindSwapChainPass(pipeline->GetWindowHandle()); + if (pass) + { + const RHI::Viewport& viewport = pass->GetViewport(); + resolution.SetX(viewport.m_maxX); + resolution.SetY(viewport.m_maxY); + } + break; + } + } + + // calculate culling resolution based on required tile size for MaskedOcclusionCulling + static const uint32_t MaskedOcclusionCullingSubTileWidth = 8; + static const uint32_t MaskedOcclusionCullingSubTileHeight = 4; + + uint32_t cullingWidth = RHI::AlignUp(resolution.GetX(), MaskedOcclusionCullingSubTileWidth); + uint32_t cullingHeight = RHI::AlignUp(resolution.GetY(), MaskedOcclusionCullingSubTileHeight); + + m_maskedOcclusionCulling->SetResolution(cullingWidth, cullingHeight); + + if (cullingWidth > 0 && cullingHeight > 0) + { + m_maskedOcclusionCulling->ClearBuffer(); + } + } + + MaskedOcclusionCulling* View::GetMaskedOcclusionCulling() + { + if (m_maskedOcclusionCulling) + { + uint32_t width = 0; + uint32_t height = 0; + + m_maskedOcclusionCulling->GetResolution(width, height); + if (width > 0 && height > 0) + { + return m_maskedOcclusionCulling; + } + } + + return nullptr; + } + } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake index 0d5c19758b..5ae72fe42f 100644 --- a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake +++ b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake @@ -101,6 +101,7 @@ set(FILES Include/Atom/RPI.Public/GpuQuery/Query.h Include/Atom/RPI.Public/GpuQuery/QueryPool.h Include/Atom/RPI.Public/GpuQuery/TimestampQueryPool.h + Include/Atom/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h Source/RPI.Public/Culling.cpp Source/RPI.Public/FeatureProcessor.cpp Source/RPI.Public/FeatureProcessorFactory.cpp @@ -178,4 +179,7 @@ set(FILES Source/RPI.Public/GpuQuery/Query.cpp Source/RPI.Public/GpuQuery/QueryPool.cpp Source/RPI.Public/GpuQuery/TimestampQueryPool.cpp + Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp + Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp + Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp ) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp index 2ef4e1e229..368d8e76e7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -114,6 +116,7 @@ namespace AZ DeferredFogComponent::CreateDescriptor(), SurfaceData::SurfaceDataMeshComponent::CreateDescriptor(), AttachmentComponent::CreateDescriptor(), + OcclusionCullingPlaneComponent::CreateDescriptor(), #ifdef ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR EditorAreaLightComponent::CreateDescriptor(), @@ -145,6 +148,7 @@ namespace AZ EditorDeferredFogComponent::CreateDescriptor(), SurfaceData::EditorSurfaceDataMeshComponent::CreateDescriptor(), EditorAttachmentComponent::CreateDescriptor(), + EditorOcclusionCullingPlaneComponent::CreateDescriptor(), #endif }); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp new file mode 100644 index 0000000000..9a655727d6 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp @@ -0,0 +1,91 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void EditorOcclusionCullingPlaneComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1, ConvertToEditorRenderComponentAdapter<1>) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Occlusion Culling Plane", "The OcclusionCullingPlane component is used to cull meshes that are inside the view frustum and behind the occlusion plane") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Atom") + ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.png") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ; + + editContext->Class( + "OcclusionCullingPlaneComponentController", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OcclusionCullingPlaneComponentController::m_configuration, "Configuration", "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ; + + editContext->Class( + "OcclusionCullingPlaneComponentConfig", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ; + } + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("EditorOcclusionCullingPlaneComponentTypeId", BehaviorConstant(Uuid(EditorOcclusionCullingPlaneComponentTypeId))) + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); + } + } + + EditorOcclusionCullingPlaneComponent::EditorOcclusionCullingPlaneComponent() + { + } + + EditorOcclusionCullingPlaneComponent::EditorOcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config) + : BaseClass(config) + { + } + + void EditorOcclusionCullingPlaneComponent::Activate() + { + BaseClass::Activate(); + AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); + } + + void EditorOcclusionCullingPlaneComponent::Deactivate() + { + AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); + BaseClass::Deactivate(); + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h new file mode 100644 index 0000000000..8070c1d553 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h @@ -0,0 +1,43 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class EditorOcclusionCullingPlaneComponent final + : public EditorRenderComponentAdapter + , private AzFramework::EntityDebugDisplayEventBus::Handler + { + public: + using BaseClass = EditorRenderComponentAdapter; + AZ_EDITOR_COMPONENT(AZ::Render::EditorOcclusionCullingPlaneComponent, EditorOcclusionCullingPlaneComponentTypeId, BaseClass); + + static void Reflect(AZ::ReflectContext* context); + + EditorOcclusionCullingPlaneComponent(); + EditorOcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config); + + // AZ::Component overrides + void Activate() override; + void Deactivate() override; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp new file mode 100644 index 0000000000..567809266d --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp @@ -0,0 +1,43 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +namespace AZ +{ + namespace Render + { + OcclusionCullingPlaneComponent::OcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config) + : BaseClass(config) + { + } + + void OcclusionCullingPlaneComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ; + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("OcclusionCullingPlaneComponentTypeId", BehaviorConstant(Uuid(OcclusionCullingPlaneComponentTypeId))) + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common); + } + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h new file mode 100644 index 0000000000..7e7b48bd45 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h @@ -0,0 +1,37 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class OcclusionCullingPlaneComponent final + : public AzFramework::Components::ComponentAdapter + { + public: + using BaseClass = AzFramework::Components::ComponentAdapter; + AZ_COMPONENT(AZ::Render::OcclusionCullingPlaneComponent, OcclusionCullingPlaneComponentTypeId, BaseClass); + + OcclusionCullingPlaneComponent() = default; + OcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config); + + static void Reflect(AZ::ReflectContext* context); + }; + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h new file mode 100644 index 0000000000..59276de9ee --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h @@ -0,0 +1,22 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +namespace AZ +{ + namespace Render + { + static constexpr const char* const OcclusionCullingPlaneComponentTypeId = "{F7537387-15A8-48F0-A1F3-D19C5886B886}"; + static constexpr const char* const EditorOcclusionCullingPlaneComponentTypeId = "{BE7CC17B-32EB-49B0-BAD9-D26E3A059012}"; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp new file mode 100644 index 0000000000..bd03bec0f4 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp @@ -0,0 +1,137 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace AZ +{ + namespace Render + { + void OcclusionCullingPlaneComponentConfig::Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ; + } + } + + void OcclusionCullingPlaneComponentController::Reflect(ReflectContext* context) + { + OcclusionCullingPlaneComponentConfig::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("Configuration", &OcclusionCullingPlaneComponentController::m_configuration); + } + } + + void OcclusionCullingPlaneComponentController::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + dependent.push_back(AZ_CRC("TransformService", 0x8ee22c50)); + } + + void OcclusionCullingPlaneComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x9123f33d)); + } + + void OcclusionCullingPlaneComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x9123f33d)); + } + + void OcclusionCullingPlaneComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC("TransformService")); + } + + OcclusionCullingPlaneComponentController::OcclusionCullingPlaneComponentController(const OcclusionCullingPlaneComponentConfig& config) + : m_configuration(config) + { + } + + void OcclusionCullingPlaneComponentController::Activate(AZ::EntityId entityId) + { + m_entityId = entityId; + + TransformNotificationBus::Handler::BusConnect(m_entityId); + + m_featureProcessor = RPI::Scene::GetFeatureProcessorForEntity(entityId); + AZ_Assert(m_featureProcessor, "OcclusionCullingPlaneComponentController was unable to find a OcclusionCullingPlaneFeatureProcessor on the EntityContext provided."); + + m_transformInterface = TransformBus::FindFirstHandler(entityId); + AZ_Assert(m_transformInterface, "Unable to attach to a TransformBus handler"); + if (!m_transformInterface) + { + return; + } + + // add this occlusion plane to the feature processor + const AZ::Transform& transform = m_transformInterface->GetWorldTM(); + m_handle = m_featureProcessor->AddOcclusionCullingPlane(transform); + } + + void OcclusionCullingPlaneComponentController::Deactivate() + { + if (m_featureProcessor) + { + m_featureProcessor->RemoveOcclusionCullingPlane(m_handle); + } + + Data::AssetBus::MultiHandler::BusDisconnect(); + TransformNotificationBus::Handler::BusDisconnect(); + + m_transformInterface = nullptr; + m_featureProcessor = nullptr; + } + + void OcclusionCullingPlaneComponentController::SetConfiguration(const OcclusionCullingPlaneComponentConfig& config) + { + m_configuration = config; + } + + const OcclusionCullingPlaneComponentConfig& OcclusionCullingPlaneComponentController::GetConfiguration() const + { + return m_configuration; + } + + void OcclusionCullingPlaneComponentController::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world) + { + if (!m_featureProcessor) + { + return; + } + + m_featureProcessor->SetTransform(m_handle, world); + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h new file mode 100644 index 0000000000..5f0be5315f --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h @@ -0,0 +1,78 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class OcclusionCullingPlaneComponentConfig final + : public AZ::ComponentConfig + { + public: + AZ_RTTI(AZ::Render::OcclusionCullingPlaneComponentConfig, "{D0E107CA-5AFB-4675-BC97-94BCA5F248DB}", ComponentConfig); + AZ_CLASS_ALLOCATOR(OcclusionCullingPlaneComponentConfig, SystemAllocator, 0); + static void Reflect(AZ::ReflectContext* context); + + OcclusionCullingPlaneComponentConfig() = default; + }; + + class OcclusionCullingPlaneComponentController final + : public Data::AssetBus::MultiHandler + , private TransformNotificationBus::Handler + { + public: + friend class EditorOcclusionCullingPlaneComponent; + + AZ_CLASS_ALLOCATOR(OcclusionCullingPlaneComponentController, AZ::SystemAllocator, 0); + AZ_RTTI(AZ::Render::OcclusionCullingPlaneComponentController, "{8EDA3C7D-5171-4843-9969-4D84DB13F221}"); + + static void Reflect(AZ::ReflectContext* context); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + + OcclusionCullingPlaneComponentController() = default; + OcclusionCullingPlaneComponentController(const OcclusionCullingPlaneComponentConfig& config); + + void Activate(AZ::EntityId entityId); + void Deactivate(); + void SetConfiguration(const OcclusionCullingPlaneComponentConfig& config); + const OcclusionCullingPlaneComponentConfig& GetConfiguration() const; + + private: + + AZ_DISABLE_COPY(OcclusionCullingPlaneComponentController); + + // TransformNotificationBus overrides + void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; + + // handle for this occlusion plane in the feature processor + OcclusionCullingPlaneHandle m_handle; + + OcclusionCullingPlaneFeatureProcessorInterface* m_featureProcessor = nullptr; + TransformInterface* m_transformInterface = nullptr; + AZ::EntityId m_entityId; + OcclusionCullingPlaneComponentConfig m_configuration; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake index e58f72a121..360511aaea 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake @@ -53,6 +53,8 @@ set(FILES Source/Mesh/EditorMeshSystemComponent.h Source/Mesh/MeshThumbnail.h Source/Mesh/MeshThumbnail.cpp + Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h + Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp Source/PostProcess/EditorPostFxLayerComponent.cpp Source/PostProcess/EditorPostFxLayerComponent.h Source/PostProcess/Bloom/EditorBloomComponent.cpp diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake index e13d1d37d6..deb8ab1b74 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake @@ -66,6 +66,10 @@ set(FILES Source/Mesh/MeshComponent.cpp Source/Mesh/MeshComponentController.h Source/Mesh/MeshComponentController.cpp + Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h + Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp + Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h + Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp Source/PostProcess/PostFxLayerComponent.cpp Source/PostProcess/PostFxLayerComponent.h Source/PostProcess/PostFxLayerComponentConfig.cpp From bee811ae4ffa6767853233bfe32416ba2a709450 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Thu, 27 May 2021 19:22:46 -0700 Subject: [PATCH 02/71] Moved Simulate to OnBeginPrepareRender. --- .../OcclusionCullingPlaneFeatureProcessor.cpp | 6 ++---- .../OcclusionCullingPlaneFeatureProcessor.h | 4 +++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp index d4a1a37521..bed008a3da 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp @@ -47,10 +47,8 @@ namespace AZ DisableSceneNotification(); } - void OcclusionCullingPlaneFeatureProcessor::Simulate([[maybe_unused]] const FeatureProcessor::SimulatePacket& packet) - { - AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); - + void OcclusionCullingPlaneFeatureProcessor::OnBeginPrepareRender() + { AZStd::vector occlusionCullingPlanes; for (auto& occlusionCullingPlane : m_occlusionCullingPlanes) { diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h index c54c816bfd..5319666745 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h @@ -58,7 +58,9 @@ namespace AZ // FeatureProcessor overrides void Activate() override; void Deactivate() override; - void Simulate(const FeatureProcessor::SimulatePacket& packet) override; + + // RPI::SceneNotificationBus overrides ... + void OnBeginPrepareRender() override; // retrieve the full list of occlusion planes using OcclusionCullingPlaneVector = AZStd::vector>; From c84882869bc57887d7e534e803a0c006d516d9fd Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Fri, 28 May 2021 03:41:44 -0700 Subject: [PATCH 03/71] Changed to a fixed-size occlusion buffer --- .../Code/Include/Atom/RPI.Public/Culling.h | 2 +- .../RPI/Code/Include/Atom/RPI.Public/View.h | 2 +- .../RPI/Code/Source/RPI.Public/Culling.cpp | 4 +- .../Atom/RPI/Code/Source/RPI.Public/Scene.cpp | 2 +- Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp | 50 ++----------------- 5 files changed, 9 insertions(+), 51 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index 17e0a1f82d..8892266683 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -217,7 +217,7 @@ namespace AZ void SetOcclusionCullingPlanes(const AZStd::vector& occlusionCullingPlanes) { m_occlusionCullingPlanes = occlusionCullingPlanes; } //! Notifies the CullingScene that culling will begin for this frame. - void BeginCulling(const AZStd::vector& views, const AZStd::vector& activePipelines); + void BeginCulling(const AZStd::vector& views); //! Notifies the CullingScene that the culling is done for this frame. void EndCulling(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h index 74c841d2a5..0b6b41c8b0 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h @@ -128,7 +128,7 @@ namespace AZ void ConnectWorldToClipMatrixChangedHandler(MatrixChangedEvent::Handler& handler); //! Prepare for view culling - void BeginCulling(const AZStd::vector& activePipelines); + void BeginCulling(); //! Returns the masked occlusion culling interface MaskedOcclusionCulling* GetMaskedOcclusionCulling(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index ab25fb14fb..7bed2d3232 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -643,7 +643,7 @@ namespace AZ } } - void CullingScene::BeginCulling(const AZStd::vector& views, const AZStd::vector& activePipelines) + void CullingScene::BeginCulling(const AZStd::vector& views) { m_cullDataConcurrencyCheck.soft_lock(); @@ -652,7 +652,7 @@ namespace AZ for (auto& view : views) { - view->BeginCulling(activePipelines); + view->BeginCulling(); } AuxGeomDrawPtr auxGeom; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 16c2189a00..c02ac0713c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -500,7 +500,7 @@ namespace AZ } // Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs) - m_cullingScene->BeginCulling(m_renderPacket.m_views, activePipelines); + m_cullingScene->BeginCulling(m_renderPacket.m_views); for (ViewPtr& viewPtr : m_renderPacket.m_views) { AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp index 3060fc34a3..edae0f88b7 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp @@ -54,7 +54,7 @@ namespace AZ } m_maskedOcclusionCulling = MaskedOcclusionCulling::Create(); - m_maskedOcclusionCulling->SetNearClipPlane(0.1f); + m_maskedOcclusionCulling->SetResolution(1920, 1080); } View::~View() @@ -388,56 +388,14 @@ namespace AZ m_needBuildSrg = false; } - void View::BeginCulling(const AZStd::vector& activePipelines) + void View::BeginCulling() { - // retrieve current resolution - Vector2 resolution(0.0f, 0.0f); - for (auto& pipeline : activePipelines) - { - ViewPtr pipelineView = pipeline->GetDefaultView(); - if (pipelineView.get() == this) - { - RPI::SwapChainPass* pass = AZ::RPI::PassSystemInterface::Get()->FindSwapChainPass(pipeline->GetWindowHandle()); - if (pass) - { - const RHI::Viewport& viewport = pass->GetViewport(); - resolution.SetX(viewport.m_maxX); - resolution.SetY(viewport.m_maxY); - } - break; - } - } - - // calculate culling resolution based on required tile size for MaskedOcclusionCulling - static const uint32_t MaskedOcclusionCullingSubTileWidth = 8; - static const uint32_t MaskedOcclusionCullingSubTileHeight = 4; - - uint32_t cullingWidth = RHI::AlignUp(resolution.GetX(), MaskedOcclusionCullingSubTileWidth); - uint32_t cullingHeight = RHI::AlignUp(resolution.GetY(), MaskedOcclusionCullingSubTileHeight); - - m_maskedOcclusionCulling->SetResolution(cullingWidth, cullingHeight); - - if (cullingWidth > 0 && cullingHeight > 0) - { - m_maskedOcclusionCulling->ClearBuffer(); - } + m_maskedOcclusionCulling->ClearBuffer(); } MaskedOcclusionCulling* View::GetMaskedOcclusionCulling() { - if (m_maskedOcclusionCulling) - { - uint32_t width = 0; - uint32_t height = 0; - - m_maskedOcclusionCulling->GetResolution(width, height); - if (width > 0 && height > 0) - { - return m_maskedOcclusionCulling; - } - } - - return nullptr; + return m_maskedOcclusionCulling; } } // namespace RPI From ab45ea7efa3cacea1e0e809a3bc8e638bf183900 Mon Sep 17 00:00:00 2001 From: mnaumov Date: Fri, 28 May 2021 17:15:20 -0700 Subject: [PATCH 04/71] [ATOM-15631] First pass on exposing Display Mapper properties to Behavior Context --- .../Common/Code/3rdParty/ACES/ACES/Aces.h | 1 + .../DisplayMapperConfigurationDescriptor.h | 8 ++- .../DisplayMapperConfigurationDescriptor.cpp | 26 +++++++++ .../DisplayMapper/DisplayMapperComponentBus.h | 55 +++++++++++++++++++ .../DisplayMapper/DisplayMapperComponent.cpp | 1 - .../DisplayMapperComponentController.cpp | 33 +++++++++++ .../DisplayMapperComponentController.h | 10 ++++ .../EditorDisplayMapperComponent.cpp | 13 ++++- ...egration_commonfeatures_public_files.cmake | 1 + 9 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h diff --git a/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h b/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h index 472ef160be..cd9dca68e3 100644 --- a/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h +++ b/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h @@ -177,4 +177,5 @@ namespace AZ } // namespace Render AZ_TYPE_INFO_SPECIALIZE(Render::DisplayMapperOperationType, "{41CA80B1-9E0D-41FB-A235-9638D2A905A5}"); + AZ_TYPE_INFO_SPECIALIZE(Render::OutputDeviceTransformType, "{B94085B7-C0D4-466A-A791-188A4559EC8D}"); } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h index 4dc090b831..22c866447e 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h @@ -12,11 +12,13 @@ #pragma once +#include + #include + #include #include #include -#include namespace AZ { @@ -33,6 +35,7 @@ namespace AZ AZ_TYPE_INFO(AcesParameterOverrides, "{3EE8C0D4-3792-46C0-B91C-B89A81C36B91}"); static void Reflect(ReflectContext* context); + // Load preconfigured preset for specific ODT mode defined by m_preset void LoadPreset(); // When enabled allows parameter overrides for ACES configuration @@ -98,6 +101,5 @@ namespace AZ DisplayMapperConfigurationDescriptor m_config; }; - - } // namespace RPI + } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp index e91e125b40..0858381fc5 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp @@ -23,6 +23,15 @@ namespace AZ { if (auto serializeContext = azrtti_cast(context)) { + serializeContext->Enum() + ->Version(0) + ->Value("48Nits", OutputDeviceTransformType::OutputDeviceTransformType_48Nits) + ->Value("1000Nits", OutputDeviceTransformType::OutputDeviceTransformType_1000Nits) + ->Value("2000Nits", OutputDeviceTransformType::OutputDeviceTransformType_2000Nits) + ->Value("4000Nits", OutputDeviceTransformType::OutputDeviceTransformType_4000Nits) + ->Value("NumOutputDeviceTransformTypes", OutputDeviceTransformType::NumOutputDeviceTransformTypes) + ; + serializeContext->Class() ->Version(0) ->Field("OverrideDefaults", &AcesParameterOverrides::m_overrideDefaults) @@ -38,6 +47,22 @@ namespace AZ ->Field("SurroundGamma", &AcesParameterOverrides::m_surroundGamma) ->Field("Gamma", &AcesParameterOverrides::m_gamma); } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class("AcesParameterOverrides") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "render") + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Constructor() + ->Method("LoadPreset", &AcesParameterOverrides::LoadPreset) + ->Property("overrideDefaults", BehaviorValueProperty(&AcesParameterOverrides::m_overrideDefaults)) + ->Property("preset", BehaviorValueProperty(&AcesParameterOverrides::m_preset)) + ->Property("alterSurround", BehaviorValueProperty(&AcesParameterOverrides::m_alterSurround)) + ->Property("applyDesaturation", BehaviorValueProperty(&AcesParameterOverrides::m_applyDesaturation)) + ->Property("applyCATD60toD65", BehaviorValueProperty(&AcesParameterOverrides::m_applyCATD60toD65)) + ; + } } void AcesParameterOverrides::LoadPreset() @@ -76,6 +101,7 @@ namespace AZ ->Field("OperationType", &DisplayMapperConfigurationDescriptor::m_operationType) ->Field("LdrGradingLutEnabled", &DisplayMapperConfigurationDescriptor::m_ldrGradingLutEnabled) ->Field("LdrColorGradingLut", &DisplayMapperConfigurationDescriptor::m_ldrColorGradingLut) + ->Field("AcesParameterOverrides", &DisplayMapperConfigurationDescriptor::m_acesParameterOverrides) ; } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h new file mode 100644 index 0000000000..af57b69d3b --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h @@ -0,0 +1,55 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + struct AcesParameterOverrides; + + //! DisplayMapperComponentRequests provides an interface to request operations on a DisplayMapperComponent + class DisplayMapperComponentRequests + : public ComponentBus + { + public: + //! Load preconfigured preset for specific ODT mode + virtual void LoadPreset(OutputDeviceTransformType preset) = 0; + //! Set display mapper type + virtual void SetDisplayMapperOperationType(DisplayMapperOperationType displayMapperOperationType) = 0; + //! Set custom ACES parameters for ACES mapping, display mapper must be set to Aces to see the difference + virtual void SetAcesParameterOverrides(const AcesParameterOverrides& parameterOverrides) = 0; + }; + using DisplayMapperComponentRequestBus = EBus; + + //! DisplayMapperComponent can send out notifications on the DisplayMapperComponentNotifications + class DisplayMapperComponentNotifications : public ComponentBus + { + public: + //! Notifies that display mapper type changed + virtual void OntDisplayMapperOperationTypeUpdated([[maybe_unused]] const DisplayMapperOperationType& displayMapperOperationType) + { + } + + //! Notifies that ACES parameter overrides changed + virtual void OnAcesParameterOverridesUpdated([[maybe_unused]] const AcesParameterOverrides& acesParameterOverrides) + { + } + }; + using DisplayMapperComponentNotificationBus = EBus; + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponent.cpp index 9642072cd2..b8d8336c30 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponent.cpp @@ -39,6 +39,5 @@ namespace AZ ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common); } } - } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp index 0e283199e7..7831c0a4c6 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp @@ -76,6 +76,39 @@ namespace AZ return m_configuration; } + void DisplayMapperComponentController::LoadPreset(OutputDeviceTransformType preset) + { + AcesParameterOverrides propertyOverrides; + propertyOverrides.m_preset = preset; + propertyOverrides.m_overrideDefaults = true; + propertyOverrides.LoadPreset(); + SetAcesParameterOverrides(propertyOverrides); + } + + void DisplayMapperComponentController::SetDisplayMapperOperationType(DisplayMapperOperationType displayMapperOperationType) + { + if (m_configuration.m_displayMapperOperation != displayMapperOperationType) + { + m_configuration.m_displayMapperOperation = displayMapperOperationType; + OnConfigChanged(); + DisplayMapperComponentNotificationBus::Broadcast( + &DisplayMapperComponentNotificationBus::Handler::OntDisplayMapperOperationTypeUpdated, + m_configuration.m_displayMapperOperation); + } + } + + void DisplayMapperComponentController::SetAcesParameterOverrides(const AcesParameterOverrides& parameterOverrides) + { + m_configuration.m_acesParameterOverrides = parameterOverrides; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + DisplayMapperComponentNotificationBus::Broadcast( + &DisplayMapperComponentNotificationBus::Handler::OnAcesParameterOverridesUpdated, + m_configuration.m_acesParameterOverrides); + } + void DisplayMapperComponentController::OnConfigChanged() { // Register the configuration with the AcesDisplayMapperFeatureProcessor for this scene. diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h index 3cc6a7e5d9..efa2070828 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h @@ -12,10 +12,12 @@ #pragma once + #include #include #include +#include #include #include @@ -24,7 +26,10 @@ namespace AZ { namespace Render { + struct AcesParameterOverrides; + class DisplayMapperComponentController final + : DisplayMapperComponentRequestBus::Handler { public: friend class EditorDisplayMapperComponent; @@ -43,6 +48,11 @@ namespace AZ void SetConfiguration(const DisplayMapperComponentConfig& config); const DisplayMapperComponentConfig& GetConfiguration() const; + //! DisplayMapperComponentRequestBus::Handler overrides... + void LoadPreset(OutputDeviceTransformType preset) override; + void SetDisplayMapperOperationType(DisplayMapperOperationType displayMapperOperationType) override; + void SetAcesParameterOverrides(const AcesParameterOverrides& parameterOverrides) override; + private: AZ_DISABLE_COPY(DisplayMapperComponentController); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp index 64cd450940..aadb0cc22b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp @@ -76,23 +76,32 @@ namespace AZ Edit::UIHandlers::Default, &AcesParameterOverrides::m_cinemaLimitsBlack, "Cinema Limit (black)", "Reference black luminance value") + ->Attribute(AZ::Edit::Attributes::Min, 0.02f) + ->Attribute(AZ::Edit::Attributes::Max, &AcesParameterOverrides::m_cinemaLimitsWhite) ->DataElement( Edit::UIHandlers::Default, &AcesParameterOverrides::m_cinemaLimitsWhite, "Cinema Limit (white)", "Reference white luminance value") + ->Attribute(AZ::Edit::Attributes::Min, &AcesParameterOverrides::m_cinemaLimitsBlack) + ->Attribute(AZ::Edit::Attributes::Max, 4000) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement( Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_minPoint, "Min Point (luminance)", "Linear extension below this") + ->Attribute(AZ::Edit::Attributes::Min, 0.002f) + ->Attribute(AZ::Edit::Attributes::Max, &AcesParameterOverrides::m_midPoint) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement( - Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_midPoint, "Mid Point (luminance)", - "Middle gray") + Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_midPoint, "Mid Point (luminance)", "Middle gray") + ->Attribute(AZ::Edit::Attributes::Min, &AcesParameterOverrides::m_minPoint) + ->Attribute(AZ::Edit::Attributes::Max, &AcesParameterOverrides::m_maxPoint) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement( Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_maxPoint, "Max Point (luminance)", "Linear extension above this") + ->Attribute(AZ::Edit::Attributes::Min, &AcesParameterOverrides::m_midPoint) + ->Attribute(AZ::Edit::Attributes::Max, 4000) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake index 7b8d0a6e21..31a90e7ceb 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake @@ -36,6 +36,7 @@ set(FILES Include/AtomLyIntegration/CommonFeatures/PostProcess/Bloom/BloomComponentConfig.h Include/AtomLyIntegration/CommonFeatures/PostProcess/DepthOfField/DepthOfFieldBus.h Include/AtomLyIntegration/CommonFeatures/PostProcess/DepthOfField/DepthOfFieldComponentConfig.h + Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentConfig.h Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentConstants.h Include/AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlBus.h From ddbed2f222ec279ef5472df56cc305ef8b29df72 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Fri, 28 May 2021 17:31:33 -0700 Subject: [PATCH 05/71] Sorting occlusion planes. --- .../RPI/Code/Source/RPI.Public/Culling.cpp | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index 7bed2d3232..e105f7bca5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -480,8 +480,33 @@ namespace AZ MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionCullingPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); if (maskedOcclusionCulling) { + // frustum cull and sort the occlusion planes by view space distance, front-to-back + using OccluderEntry = AZStd::pair; + AZStd::vector visibleOccluders; for (const AZ::Transform& transform : m_occlusionCullingPlanes) { + Aabb occluderAabb = Aabb::CreateCenterHalfExtents(transform.GetTranslation(), AZ::Vector3(AZ::Vector2(transform.GetUniformScale() / 2.0f))); + occluderAabb.SetMin(transform.TransformPoint(occluderAabb.GetMin())); + occluderAabb.SetMax(transform.TransformPoint(occluderAabb.GetMax())); + if (ShapeIntersection::Contains(frustum, occluderAabb)) + { + // occluder is visible, compute view space distance and add to list + float depth = (view.GetWorldToViewMatrix() * occluderAabb.GetMin()).GetZ(); + depth = AZStd::min(depth, (view.GetWorldToViewMatrix() * occluderAabb.GetMax()).GetZ()); + + visibleOccluders.push_back(AZStd::make_pair(transform, depth)); + } + } + + AZStd::sort(visibleOccluders.begin(), visibleOccluders.end(), [](const OccluderEntry& LHS, const OccluderEntry& RHS) + { + return LHS.second < RHS.second; + }); + + for (const OccluderEntry& occluder : visibleOccluders) + { + const AZ::Transform& transform = occluder.first; + // find the corners of the plane static const Vector3 BL = Vector3(-0.5f, -0.5f, 0.0f); static const Vector3 BR = Vector3(0.5f, -0.5f, 0.0f); From 59ab6edaefc08768f2b1f933097df07c339103fa Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Mon, 31 May 2021 01:38:13 -0700 Subject: [PATCH 06/71] Added occlusion culling plane visualization --- ...lingPlaneTransparentVisualization.material | 22 ++++ ...cclusionCullingPlaneVisualization.material | 22 ++++ .../Assets/Models/OcclusionCullingPlane.fbx | 3 + ...ionCullingPlaneFeatureProcessorInterface.h | 2 + .../OcclusionCullingPlane.cpp | 113 ++++++++++++++++++ .../OcclusionCullingPlane.h | 65 ++++++++++ .../OcclusionCullingPlaneFeatureProcessor.cpp | 13 ++ .../OcclusionCullingPlaneFeatureProcessor.h | 21 +--- .../Code/atom_feature_common_files.cmake | 2 + .../RPI/Code/Source/RPI.Public/Culling.cpp | 72 ++++++++--- Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp | 6 +- .../EditorOcclusionCullingPlaneComponent.cpp | 6 +- ...clusionCullingPlaneComponentController.cpp | 8 +- ...OcclusionCullingPlaneComponentController.h | 3 + 14 files changed, 321 insertions(+), 37 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.material create mode 100644 Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.material create mode 100644 Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx create mode 100644 Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp create mode 100644 Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.h diff --git a/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.material b/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.material new file mode 100644 index 0000000000..981e392eef --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.material @@ -0,0 +1,22 @@ +{ + "materialType": "Materials\\Types\\StandardPBR.materialtype", + "propertyLayoutVersion": 3, + "properties": { + "general": { + "enableShadows": false, + "enableDirectionalLights": false, + "enablePunctualLights": false, + "enableAreaLights": false, + "enableIBL": true + }, + "baseColor": { + "color": [ 0.0, 1.0, 0.0 ] + }, + "opacity": { + "alphaSource": "None", + "doubleSided": true, + "factor": 0.25, + "mode": "TintedTransparent" + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.material b/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.material new file mode 100644 index 0000000000..4446cc2d9d --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.material @@ -0,0 +1,22 @@ +{ + "materialType": "Materials\\Types\\StandardPBR.materialtype", + "propertyLayoutVersion": 3, + "properties": { + "general": { + "enableShadows": false, + "enableDirectionalLights": false, + "enablePunctualLights": false, + "enableAreaLights": false, + "enableIBL": true + }, + "baseColor": { + "color": [ 0.0, 1.0, 0.0 ] + }, + "opacity": { + "alphaSource": "None", + "doubleSided": true, + "factor": 1.0, + "mode": "TintedTransparent" + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx b/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx new file mode 100644 index 0000000000..b274bfa282 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a1f8d75dcd85e8b4aa57f6c0c81af0300ff96915ba3c2b591095c215d5e1d8c +size 12072 diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h index 07a6179e78..8ffbb7f235 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h @@ -36,6 +36,8 @@ namespace AZ virtual bool IsValidOcclusionCullingPlaneHandle(const OcclusionCullingPlaneHandle& occlusionCullingPlane) const = 0; virtual void SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) = 0; virtual void SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enabled) = 0; + virtual void ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization) = 0; + virtual void SetTransparentVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool transparentVisualization) = 0; }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp new file mode 100644 index 0000000000..10004a72e4 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp @@ -0,0 +1,113 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + static const char* OcclusionCullingPlaneDrawListTag("occlusioncullingplanevisualization"); + + OcclusionCullingPlane::~OcclusionCullingPlane() + { + Data::AssetBus::MultiHandler::BusDisconnect(); + m_meshFeatureProcessor->ReleaseMesh(m_visualizationMeshHandle); + } + + void OcclusionCullingPlane::Init(RPI::Scene* scene) + { + AZ_Assert(scene, "OcclusionCullingPlane::Init called with a null Scene pointer"); + + m_meshFeatureProcessor = scene->GetFeatureProcessor(); + + // load visualization plane model and material + m_visualizationModelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath( + "Models/OcclusionCullingPlane.azmodel", + AZ::RPI::AssetUtils::TraceLevel::Assert); + + m_visualizationMeshHandle = m_meshFeatureProcessor->AcquireMesh(m_visualizationModelAsset); + m_meshFeatureProcessor->SetExcludeFromReflectionCubeMaps(m_visualizationMeshHandle, true); + m_meshFeatureProcessor->SetRayTracingEnabled(m_visualizationMeshHandle, false); + m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, AZ::Transform::CreateIdentity()); + + SetVisualizationMaterial(); + } + + void OcclusionCullingPlane::SetVisualizationMaterial() + { + AZStd::string materialAssetPath; + if (m_transparentVisualization) + { + materialAssetPath = "Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.azmaterial"; + } + else + { + materialAssetPath = "Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.azmaterial"; + } + + RPI::AssetUtils::TraceLevel traceLevel = AZ::RPI::AssetUtils::TraceLevel::Assert; + m_visualizationMaterialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath(materialAssetPath.c_str(), traceLevel); + m_visualizationMaterialAsset.QueueLoad(); + Data::AssetBus::MultiHandler::BusConnect(m_visualizationMaterialAsset.GetId()); + } + + void OcclusionCullingPlane::OnAssetReady(Data::Asset asset) + { + if (m_visualizationMaterialAsset.GetId() == asset.GetId()) + { + m_visualizationMaterialAsset = asset; + Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId()); + + m_visualizationMaterial = AZ::RPI::Material::FindOrCreate(m_visualizationMaterialAsset); + m_meshFeatureProcessor->SetMaterialAssignmentMap(m_visualizationMeshHandle, m_visualizationMaterial); + } + } + + void OcclusionCullingPlane::OnAssetError(Data::Asset asset) + { + AZ_Error("OcclusionCullingPlane", false, "Failed to load OcclusionCullingPlane visualization asset %s", asset.ToString().c_str()); + Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId()); + } + + void OcclusionCullingPlane::SetTransform(const AZ::Transform& transform) + { + m_transform = transform; + + // update visualization plane transform + m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, transform); + } + + void OcclusionCullingPlane::ShowVisualization(bool showVisualization) + { + if (m_showVisualization != showVisualization) + { + m_meshFeatureProcessor->SetVisible(m_visualizationMeshHandle, showVisualization); + SetVisualizationMaterial(); + } + } + + void OcclusionCullingPlane::SetTransparentVisualization(bool transparentVisualization) + { + if (m_transparentVisualization != transparentVisualization) + { + m_transparentVisualization = transparentVisualization; + SetVisualizationMaterial(); + } + } + + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.h b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.h new file mode 100644 index 0000000000..4701c4977f --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.h @@ -0,0 +1,65 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + //! This class represents an OcclusionCullingPlane which is used to cull meshes that are inside the view frustum + class OcclusionCullingPlane final + : public AZ::Data::AssetBus::MultiHandler + { + public: + OcclusionCullingPlane() = default; + ~OcclusionCullingPlane(); + + void Init(RPI::Scene* scene); + + void SetTransform(const AZ::Transform& transform); + const AZ::Transform& GetTransform() const { return m_transform; } + + void SetEnabled(bool enabled) { m_enabled = enabled; } + bool GetEnabled() const { return m_enabled; } + + // enables or disables rendering of the visualization plane + void ShowVisualization(bool showVisualization); + + // sets the visualization to transparent mode + void SetTransparentVisualization(bool transparentVisualization); + + private: + + void SetVisualizationMaterial(); + + // AZ::Data::AssetBus::Handler overrides... + void OnAssetReady(Data::Asset asset) override; + void OnAssetError(Data::Asset asset) override; + + AZ::Transform m_transform; + bool m_enabled = true; + bool m_showVisualization = true; + bool m_transparentVisualization = false; + + // visualization + AZ::Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr; + Data::Asset m_visualizationModelAsset; + Data::Asset m_visualizationMaterialAsset; + Data::Instance m_visualizationMaterial; + AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_visualizationMeshHandle; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp index bed008a3da..b9866a925f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp @@ -60,6 +60,7 @@ namespace AZ OcclusionCullingPlaneHandle OcclusionCullingPlaneFeatureProcessor::AddOcclusionCullingPlane(const AZ::Transform& transform) { AZStd::shared_ptr occlusionCullingPlane = AZStd::make_shared(); + occlusionCullingPlane->Init(GetParentScene()); occlusionCullingPlane->SetTransform(transform); m_occlusionCullingPlanes.push_back(occlusionCullingPlane); return occlusionCullingPlane; @@ -90,5 +91,17 @@ namespace AZ AZ_Assert(occlusionCullingPlane.get(), "Enable called with an invalid handle"); occlusionCullingPlane->SetEnabled(enabled); } + + void OcclusionCullingPlaneFeatureProcessor::ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization) + { + AZ_Assert(occlusionCullingPlane.get(), "ShowVisualization called with an invalid handle"); + occlusionCullingPlane->ShowVisualization(showVisualization); + } + + void OcclusionCullingPlaneFeatureProcessor::SetTransparentVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool transparentVisualization) + { + AZ_Assert(occlusionCullingPlane.get(), "SetTransparentVisualization called with an invalid handle"); + occlusionCullingPlane->SetTransparentVisualization(transparentVisualization); + } } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h index 5319666745..211254742f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h @@ -13,29 +13,12 @@ #pragma once #include +#include namespace AZ { namespace Render { - //! This class represents an OcclusionCullingPlane which is used to cull meshes that are inside the view frustum - class OcclusionCullingPlane final - { - public: - OcclusionCullingPlane() = default; - ~OcclusionCullingPlane() = default; - - void SetTransform(const AZ::Transform& transform) { m_transform = transform; } - const AZ::Transform& GetTransform() const { return m_transform; } - - void SetEnabled(bool enabled) { m_enabled = enabled; } - bool GetEnabled() const { return m_enabled; } - - private: - AZ::Transform m_transform; - bool m_enabled = true; - }; - //! This class manages OcclusionCullingPlanes which are used to cull meshes that are inside the view frustum class OcclusionCullingPlaneFeatureProcessor final : public OcclusionCullingPlaneFeatureProcessorInterface @@ -54,6 +37,8 @@ namespace AZ bool IsValidOcclusionCullingPlaneHandle(const OcclusionCullingPlaneHandle& occlusionCullingPlane) const override { return (occlusionCullingPlane.get() != nullptr); } void SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) override; void SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enable) override; + void ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization) override; + void SetTransparentVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool transparentVisualization) override; // FeatureProcessor overrides void Activate() override; diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index c545a2c974..b796a5b356 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -175,6 +175,8 @@ set(FILES Source/MorphTargets/MorphTargetDispatchItem.h Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp + Source/OcclusionCullingPlane/OcclusionCullingPlane.h + Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp Source/PostProcess/PostProcessBase.cpp Source/PostProcess/PostProcessBase.h Source/PostProcess/PostProcessFeatureProcessor.cpp diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index e105f7bca5..d8ed850309 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -428,20 +428,52 @@ namespace AZ return MaskedOcclusionCulling::CullingResult::VISIBLE; } - // convert the bounding box of the visibility entry to NDC - AZ::Vector4 clipSpaceMin = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(visibleEntry->m_boundingVolume.GetMin()); - float depth = clipSpaceMin.GetW(); - AZ::Vector4 ndcMin = clipSpaceMin / clipSpaceMin.GetW(); + if (visibleEntry->m_boundingVolume.Contains(m_jobData->m_view->GetCameraTransform().GetTranslation())) + { + // camera is inside bounding volume + return MaskedOcclusionCulling::CullingResult::VISIBLE; + } - AZ::Vector4 clipSpaceMax = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(visibleEntry->m_boundingVolume.GetMax()); - depth = AZStd::min(depth, clipSpaceMax.GetW()); - AZ::Vector4 ndcMax = clipSpaceMax / clipSpaceMax.GetW(); + const Vector3& minBound = visibleEntry->m_boundingVolume.GetMin(); + const Vector3& maxBound = visibleEntry->m_boundingVolume.GetMax(); + + // compute bounding volume corners + Vector4 corners[8]; + corners[0] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f); + corners[1] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f); + corners[2] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f); + corners[3] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f); + corners[4] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f); + corners[5] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f); + corners[6] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f); + corners[7] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f); + + // find min clip-space depth and NDC min/max + float ndcMinX = FLT_MAX; + float ndcMinY = FLT_MAX; + float ndcMaxX = -FLT_MAX; + float ndcMaxY = -FLT_MAX; + float minDepth = FLT_MAX; + for (uint32_t index = 0; index < 8; ++index) + { + minDepth = AZStd::min(minDepth, corners[index].GetW()); + + // convert to NDC + corners[index] /= corners[index].GetW(); - Vector2 rectMin(AZStd::min(ndcMin.GetX(), ndcMax.GetX()), AZStd::min(ndcMin.GetY(), ndcMax.GetY())); - Vector2 rectMax(AZStd::max(ndcMin.GetX(), ndcMax.GetX()), AZStd::max(ndcMin.GetY(), ndcMax.GetY())); + ndcMinX = AZStd::min(ndcMinX, corners[index].GetX()); + ndcMinY = AZStd::min(ndcMinY, corners[index].GetY()); + ndcMaxX = AZStd::max(ndcMaxX, corners[index].GetX()); + ndcMaxY = AZStd::max(ndcMaxY, corners[index].GetY()); + } + if (minDepth < 0.00000001f) + { + return MaskedOcclusionCulling::VISIBLE; + } + // test against the occlusion buffer, which contains only the manually placed occlusion planes - return m_jobData->m_maskedOcclusionCulling->TestRect(rectMin.GetX(), rectMin.GetY(), rectMax.GetX(), rectMax.GetY(), depth); + return m_jobData->m_maskedOcclusionCulling->TestRect(ndcMinX, ndcMinY, ndcMaxX, ndcMaxY, minDepth); } }; @@ -480,15 +512,22 @@ namespace AZ MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionCullingPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); if (maskedOcclusionCulling) { - // frustum cull and sort the occlusion planes by view space distance, front-to-back + // frustum cull occlusion planes using OccluderEntry = AZStd::pair; AZStd::vector visibleOccluders; for (const AZ::Transform& transform : m_occlusionCullingPlanes) { - Aabb occluderAabb = Aabb::CreateCenterHalfExtents(transform.GetTranslation(), AZ::Vector3(AZ::Vector2(transform.GetUniformScale() / 2.0f))); - occluderAabb.SetMin(transform.TransformPoint(occluderAabb.GetMin())); - occluderAabb.SetMax(transform.TransformPoint(occluderAabb.GetMax())); - if (ShapeIntersection::Contains(frustum, occluderAabb)) + static const AZ::Vector3 BL(-0.5f, -0.5f, 0.0f); + static const AZ::Vector3 TR(0.5f, 0.5f, 0.0f); + + AZ::Vector3 P1 = transform.TransformPoint(BL); + AZ::Vector3 P2 = transform.TransformPoint(TR); + + AZ::Vector3 aabbMin = P1.GetMin(P2); + AZ::Vector3 aabbMax = P1.GetMax(P2); + + AZ::Aabb occluderAabb = Aabb::CreateFromMinMax(aabbMin, aabbMax); + if (ShapeIntersection::Overlaps(frustum, occluderAabb)) { // occluder is visible, compute view space distance and add to list float depth = (view.GetWorldToViewMatrix() * occluderAabb.GetMin()).GetZ(); @@ -498,9 +537,10 @@ namespace AZ } } + // sort the occlusion planes by view space distance, front-to-back AZStd::sort(visibleOccluders.begin(), visibleOccluders.end(), [](const OccluderEntry& LHS, const OccluderEntry& RHS) { - return LHS.second < RHS.second; + return LHS.second > RHS.second; }); for (const OccluderEntry& occluder : visibleOccluders) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp index edae0f88b7..6e4dec112f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp @@ -28,6 +28,10 @@ namespace AZ { namespace RPI { + // fixed-size software occlusion culling buffer + const uint32_t MaskedSoftwareOcclusionCullingWidth = 1920; + const uint32_t MaskedSoftwareOcclusionCullingHeight = 1080; + ViewPtr View::CreateView(const AZ::Name& name, UsageFlags usage) { View* view = aznew View(name, usage); @@ -54,7 +58,7 @@ namespace AZ } m_maskedOcclusionCulling = MaskedOcclusionCulling::Create(); - m_maskedOcclusionCulling->SetResolution(1920, 1080); + m_maskedOcclusionCulling->SetResolution(MaskedSoftwareOcclusionCullingWidth, MaskedSoftwareOcclusionCullingHeight); } View::~View() diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp index 9a655727d6..b027be3171 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp @@ -53,8 +53,12 @@ namespace AZ editContext->Class( "OcclusionCullingPlaneComponentConfig", "") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->ClassElement(AZ::Edit::ClassElements::Group, "Settings") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::CheckBox, &OcclusionCullingPlaneComponentConfig::m_showVisualization, "Show Visualization", "Show the occlusion culling plane visualization") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->DataElement(AZ::Edit::UIHandlers::CheckBox, &OcclusionCullingPlaneComponentConfig::m_transparentVisualization, "Transparent Visualization", "Sets the occlusion culling plane visualization as transparent") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ; } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp index bd03bec0f4..cf8107776f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp @@ -38,7 +38,9 @@ namespace AZ { serializeContext->Class() ->Version(0) - ; + ->Field("ShowVisualization", &OcclusionCullingPlaneComponentConfig::m_showVisualization) + ->Field("TransparentVisualization", &OcclusionCullingPlaneComponentConfig::m_transparentVisualization) + ; } } @@ -98,6 +100,10 @@ namespace AZ // add this occlusion plane to the feature processor const AZ::Transform& transform = m_transformInterface->GetWorldTM(); m_handle = m_featureProcessor->AddOcclusionCullingPlane(transform); + + // set visualization + m_featureProcessor->ShowVisualization(m_handle, m_configuration.m_showVisualization); + m_featureProcessor->SetTransparentVisualization(m_handle, m_configuration.m_transparentVisualization); } void OcclusionCullingPlaneComponentController::Deactivate() diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h index 5f0be5315f..2d977a2cf3 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h @@ -32,6 +32,9 @@ namespace AZ AZ_CLASS_ALLOCATOR(OcclusionCullingPlaneComponentConfig, SystemAllocator, 0); static void Reflect(AZ::ReflectContext* context); + bool m_showVisualization = true; + bool m_transparentVisualization = false; + OcclusionCullingPlaneComponentConfig() = default; }; From a9a42a540550507258b77b5fed933bf73a267734 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Tue, 1 Jun 2021 12:17:48 -0700 Subject: [PATCH 07/71] Added AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED --- Gems/Atom/RPI/Code/CMakeLists.txt | 17 ++++++++-- .../Code/Include/Atom/RPI.Public/Culling.h | 1 - .../RPI/Code/Include/Atom/RPI.Public/View.h | 3 +- .../Android/Atom_RPI_Traits_Android.h | 14 +++++++++ .../Android/Atom_RPI_Traits_Platform.h | 14 +++++++++ .../Android/platform_android_files.cmake | 15 +++++++++ .../Platform/Linux/Atom_RPI_Traits_Linux.h | 14 +++++++++ .../Platform/Linux/Atom_RPI_Traits_Platform.h | 14 +++++++++ .../Platform/Linux/platform_linux_files.cmake | 15 +++++++++ .../Source/Platform/Mac/Atom_RPI_Traits_Mac.h | 14 +++++++++ .../Platform/Mac/Atom_RPI_Traits_Platform.h | 14 +++++++++ .../Platform/Mac/platform_mac_files.cmake | 15 +++++++++ .../Windows/Atom_RPI_Traits_Platform.h | 14 +++++++++ .../Windows/Atom_RPI_Traits_Windows.h | 14 +++++++++ .../Source/Platform/Windows/PAL_windows.cmake | 7 +++-- .../Windows/platform_windows_files.cmake | 15 +++++++++ .../Platform/iOS/Atom_RPI_Traits_Platform.h | 14 +++++++++ .../Source/Platform/iOS/Atom_RPI_Traits_iOS.h | 14 +++++++++ .../Platform/iOS/platform_ios_files.cmake | 15 +++++++++ .../RPI/Code/Source/RPI.Public/Culling.cpp | 31 +++++++++++++++---- Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp | 13 ++++++-- .../atom_rpi_masked_occlusion_files.cmake | 18 +++++++++++ .../Atom/RPI/Code/atom_rpi_public_files.cmake | 6 +--- 23 files changed, 291 insertions(+), 20 deletions(-) create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Android.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Linux.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Mac.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Platform.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Windows.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Windows/platform_windows_files.cmake create mode 100644 Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_iOS.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake create mode 100644 Gems/Atom/RPI/Code/atom_rpi_masked_occlusion_files.cmake diff --git a/Gems/Atom/RPI/Code/CMakeLists.txt b/Gems/Atom/RPI/Code/CMakeLists.txt index d2b7fba071..2898967add 100644 --- a/Gems/Atom/RPI/Code/CMakeLists.txt +++ b/Gems/Atom/RPI/Code/CMakeLists.txt @@ -9,6 +9,17 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME}) + +#for PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED and PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED +include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + +if(PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED) + set(MASKED_OCCLUSION_CULLING_FILES "atom_rpi_masked_occlusion_files.cmake") +else() + set(MASKED_OCCLUSION_CULLING_FILES "") +endif() + ly_add_target( NAME Atom_RPI.Public STATIC NAMESPACE Gem @@ -16,11 +27,15 @@ ly_add_target( atom_rpi_reflect_files.cmake atom_rpi_public_files.cmake ../Assets/atom_rpi_asset_files.cmake + ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + ${MASKED_OCCLUSION_CULLING_FILES} INCLUDE_DIRECTORIES PRIVATE Source + ${pal_source_dir} PUBLIC Include + External BUILD_DEPENDENCIES PRIVATE AZ::AtomCore @@ -159,8 +174,6 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME}) ly_get_list_relative_pal_filename(common_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/Common) - include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) #for PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED - if(NOT PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED) # Create a stub diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index 8892266683..3f03fb9dcb 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -31,7 +31,6 @@ #include #include -#include #include #include diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h index 0b6b41c8b0..7d192ea62a 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h @@ -18,13 +18,14 @@ #include #include #include -#include #include #include #include #include +class MaskedOcclusionCulling; + namespace AZ { namespace RHI diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Android.h b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Android.h new file mode 100644 index 0000000000..e1c8d827bf --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Android.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0 diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..27e0af7f35 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_Feature_Traits_Android.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake new file mode 100644 index 0000000000..357d8f0381 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_Feature_Traits_Platform.h + Atom_Feature_Traits_Android.h +) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Linux.h b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Linux.h new file mode 100644 index 0000000000..e1c8d827bf --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Linux.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0 diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..39c6a3e572 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_Feature_Traits_Linux.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake new file mode 100644 index 0000000000..19be7951f6 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_Feature_Traits_Platform.h + Atom_Feature_Traits_Linux.h +) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Mac.h b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Mac.h new file mode 100644 index 0000000000..e1c8d827bf --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Mac.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0 diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..19816f2bd1 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_Feature_Traits_Mac.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake new file mode 100644 index 0000000000..bde67ff340 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_Feature_Traits_Platform.h + Atom_Feature_Traits_Mac.h +) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..dc655ed3a9 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_RPI_Traits_Windows.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Windows.h b/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Windows.h new file mode 100644 index 0000000000..0deebe4706 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Windows.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 1 diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake b/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake index 51e42d5216..b989233ccd 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake @@ -10,16 +10,17 @@ # set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED TRUE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED TRUE) ly_add_source_properties( - SOURCES Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp + SOURCES External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp PROPERTY COMPILE_OPTIONS VALUES /arch:AVX2 /W3 ) ly_add_source_properties( SOURCES - Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp - Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp + External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp + External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp PROPERTY COMPILE_OPTIONS VALUES /W3 ) \ No newline at end of file diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/platform_windows_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Windows/platform_windows_files.cmake new file mode 100644 index 0000000000..e49944d8ef --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/platform_windows_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_Windows.h +) diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..4403d741dc --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_Feature_Traits_iOS.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_iOS.h b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_iOS.h new file mode 100644 index 0000000000..e1c8d827bf --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_iOS.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0 diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake new file mode 100644 index 0000000000..7f603e4bfd --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_Feature_Traits_Platform.h + Atom_Feature_Traits_iOS.h +) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index d8ed850309..79d152f661 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -20,15 +20,20 @@ #include +#include #include #include - #include #include #include #include #include #include +#include + +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED +#include +#endif //Enables more inner-loop profiling scopes (can create high overhead in RadTelemetry if there are many-many objects in a scene) //#define AZ_CULL_PROFILE_DETAILED @@ -265,10 +270,12 @@ namespace AZ struct JobData { CullingDebugContext* m_debugCtx = nullptr; - MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr; const Scene* m_scene = nullptr; View* m_view = nullptr; Frustum m_frustum; +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr; +#endif }; private: @@ -308,7 +315,9 @@ namespace AZ //Add all objects within this node to the view, without any extra culling for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries) { +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) +#endif { if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { @@ -347,7 +356,9 @@ namespace AZ } else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(m_jobData->m_frustum, c->m_cullData.m_boundingObb)) { +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) +#endif { numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); ++numVisibleCullables; @@ -421,6 +432,7 @@ namespace AZ } } +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED MaskedOcclusionCulling::CullingResult TestOcclusionCulling(AzFramework::VisibilityEntry* visibleEntry) { if (!m_jobData->m_maskedOcclusionCulling) @@ -471,10 +483,11 @@ namespace AZ { return MaskedOcclusionCulling::VISIBLE; } - + // test against the occlusion buffer, which contains only the manually placed occlusion planes return m_jobData->m_maskedOcclusionCulling->TestRect(ndcMinX, ndcMinY, ndcMaxX, ndcMaxY, minDepth); } +#endif }; void CullingScene::ProcessCullables(const Scene& scene, View& view, AZ::Job& parentJob) @@ -508,6 +521,7 @@ namespace AZ cullStats.m_cameraViewToWorld = view.GetViewToWorldMatrix(); } +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED // setup occlusion culling, if necessary MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionCullingPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); if (maskedOcclusionCulling) @@ -577,15 +591,19 @@ namespace AZ maskedOcclusionCulling->RenderTriangles((float*)verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE); } } +#endif WorkListType worklist; AZStd::shared_ptr jobData = AZStd::make_shared(); jobData->m_debugCtx = &m_debugCtx; - jobData->m_maskedOcclusionCulling = maskedOcclusionCulling; jobData->m_scene = &scene; jobData->m_view = &view; jobData->m_frustum = frustum; +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + jobData->m_maskedOcclusionCulling = maskedOcclusionCulling; +#endif + auto nodeVisitorLambda = [this, jobData, &parentJob, &frustum, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void { @@ -620,11 +638,12 @@ namespace AZ { AZStd::shared_ptr remainingJobData = AZStd::make_shared(); remainingJobData->m_debugCtx = &m_debugCtx; - remainingJobData->m_maskedOcclusionCulling = maskedOcclusionCulling; remainingJobData->m_scene = &scene; remainingJobData->m_view = &view; remainingJobData->m_frustum = frustum; - +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + remainingJobData->m_maskedOcclusionCulling = maskedOcclusionCulling; +#endif //Kick off a job to process any remaining workitems AddObjectsToViewJob* job = aznew AddObjectsToViewJob(remainingJobData, worklist); //pool allocated (cheap), auto-deletes when job finishes parentJob.SetContinuation(job); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp index 6e4dec112f..bd0e15fb2e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp @@ -23,6 +23,11 @@ #include #include #include +#include + +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED +#include +#endif namespace AZ { @@ -56,18 +61,21 @@ namespace AZ { m_shaderResourceGroup = ShaderResourceGroup::Create(viewSrgAsset); } - +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED m_maskedOcclusionCulling = MaskedOcclusionCulling::Create(); m_maskedOcclusionCulling->SetResolution(MaskedSoftwareOcclusionCullingWidth, MaskedSoftwareOcclusionCullingHeight); +#endif } View::~View() { +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED if (m_maskedOcclusionCulling) { MaskedOcclusionCulling::Destroy(m_maskedOcclusionCulling); m_maskedOcclusionCulling = nullptr; } +#endif } void View::SetDrawListMask(const RHI::DrawListMask& drawListMask) @@ -394,13 +402,14 @@ namespace AZ void View::BeginCulling() { +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED m_maskedOcclusionCulling->ClearBuffer(); +#endif } MaskedOcclusionCulling* View::GetMaskedOcclusionCulling() { return m_maskedOcclusionCulling; } - } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/atom_rpi_masked_occlusion_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_masked_occlusion_files.cmake new file mode 100644 index 0000000000..5828848c81 --- /dev/null +++ b/Gems/Atom/RPI/Code/atom_rpi_masked_occlusion_files.cmake @@ -0,0 +1,18 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + External/MaskedOcclusionCulling/MaskedOcclusionCulling.h + External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl + External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp + External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp + External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp +) \ No newline at end of file diff --git a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake index 35003d268a..a1d98bbd38 100644 --- a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake +++ b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake @@ -102,7 +102,6 @@ set(FILES Include/Atom/RPI.Public/GpuQuery/Query.h Include/Atom/RPI.Public/GpuQuery/QueryPool.h Include/Atom/RPI.Public/GpuQuery/TimestampQueryPool.h - Include/Atom/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h Source/RPI.Public/Culling.cpp Source/RPI.Public/FeatureProcessor.cpp Source/RPI.Public/FeatureProcessorFactory.cpp @@ -181,7 +180,4 @@ set(FILES Source/RPI.Public/GpuQuery/Query.cpp Source/RPI.Public/GpuQuery/QueryPool.cpp Source/RPI.Public/GpuQuery/TimestampQueryPool.cpp - Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp - Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp - Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp -) +) \ No newline at end of file From 38819c630aa7313c49cb8073876b6f401df95efb Mon Sep 17 00:00:00 2001 From: mnaumov Date: Tue, 1 Jun 2021 15:34:01 -0700 Subject: [PATCH 08/71] PR feedback --- .../DisplayMapper/DisplayMapperComponentBus.h | 63 ++++- .../DisplayMapperComponentController.cpp | 231 +++++++++++++++++- .../DisplayMapperComponentController.h | 24 ++ .../EditorDisplayMapperComponent.cpp | 56 +++-- 4 files changed, 357 insertions(+), 17 deletions(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h index af57b69d3b..4a01f71325 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h @@ -30,8 +30,67 @@ namespace AZ virtual void LoadPreset(OutputDeviceTransformType preset) = 0; //! Set display mapper type virtual void SetDisplayMapperOperationType(DisplayMapperOperationType displayMapperOperationType) = 0; - //! Set custom ACES parameters for ACES mapping, display mapper must be set to Aces to see the difference + //! Get display mapper type + virtual DisplayMapperOperationType GetDisplayMapperOperationType() const = 0; + //! Set ACES parameter overrides for ACES mapping, display mapper must be set to Aces to see the difference virtual void SetAcesParameterOverrides(const AcesParameterOverrides& parameterOverrides) = 0; + //! Get ACES parameter overrides + virtual const AcesParameterOverrides& GetAcesParameterOverrides() const = 0; + + // Enable or disable ACES parameter overrides + virtual void SetOverrideAcesParameters(bool value) = 0; + // Check if ACES parameters are overriding default preset values + virtual bool GetOverrideAcesParameters() const = 0; + + // Set gamma adjustment to compensate for dim surround + virtual void SetAlterSurround(bool value) = 0; + // Get gamma adjustment to compensate for dim surround + virtual bool GetAlterSurround() const = 0; + + // Set desaturation to compensate for luminance difference + virtual void SetApplyDesaturation(bool value) = 0; + // Get desaturation to compensate for luminance difference + virtual bool GetApplyDesaturation() const = 0; + + // Set color appearance transform (CAT) from ACES white point to assumed observer adapted white point + virtual void SetApplyCATD60toD65(bool value) = 0; + // Get color appearance transform (CAT) from ACES white point to assumed observer adapted white point + virtual bool GetApplyCATD60toD65() const = 0; + + // Set reference black luminance value + virtual void SetCinemaLimitsBlack(float value) = 0; + // Get reference black luminance value + virtual float GetCinemaLimitsBlack() const = 0; + + // Set reference white luminance value + virtual void SetCinemaLimitsWhite(float value) = 0; + // Get reference white luminance value + virtual float GetCinemaLimitsWhite() const = 0; + + // Set min luminance value + virtual void SetMinPoint(float value) = 0; + // Get min luminance value + virtual float GetMinPoint() const = 0; + + // Set mid luminance value + virtual void SetMidPoint(float value) = 0; + // Get mid luminance value + virtual float GetMidPoint() const = 0; + + // Set max luminance value + virtual void SetMaxPoint(float value) = 0; + // Get max luminance value + virtual float GetMaxPoint() const = 0; + + // Set gamma adjustment value + virtual void SetSurroundGamma(float value) = 0; + // Get gamma adjustment value + virtual float GetSurroundGamma() const = 0; + + // Set optional gamma value that is applied as basic gamma curve OETF + virtual void SetGamma(float value) = 0; + // Get optional gamma value that is applied as basic gamma curve OETF + virtual float GetGamma() const = 0; }; using DisplayMapperComponentRequestBus = EBus; @@ -40,7 +99,7 @@ namespace AZ { public: //! Notifies that display mapper type changed - virtual void OntDisplayMapperOperationTypeUpdated([[maybe_unused]] const DisplayMapperOperationType& displayMapperOperationType) + virtual void OnDisplayMapperOperationTypeUpdated([[maybe_unused]] const DisplayMapperOperationType& displayMapperOperationType) { } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp index 7831c0a4c6..06c549f560 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp @@ -10,6 +10,8 @@ * */ +#include "AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h" + #include #include @@ -32,6 +34,69 @@ namespace AZ ->Version(0) ->Field("Configuration", &DisplayMapperComponentController::m_configuration); } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("DisplayMapperComponentRequestBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "render") + ->Attribute(AZ::Script::Attributes::Module, "render") + // LoadPreset + ->Event("LoadPreset", &DisplayMapperComponentRequestBus::Events::LoadPreset) + // DisplayMapperOperationType + ->Event("SetDisplayMapperOperationType", &DisplayMapperComponentRequestBus::Events::SetDisplayMapperOperationType) + ->Event("GetDisplayMapperOperationType", &DisplayMapperComponentRequestBus::Events::GetDisplayMapperOperationType) + ->VirtualProperty("DisplayMapperOperationType", "GetDisplayMapperOperationType", "SetDisplayMapperOperationType") + // AcesParameterOverrides + ->Event("SetAcesParameterOverrides", &DisplayMapperComponentRequestBus::Events::SetAcesParameterOverrides) + ->Event("GetAcesParameterOverrides", &DisplayMapperComponentRequestBus::Events::GetAcesParameterOverrides) + ->VirtualProperty("AcesParameterOverrides", "GetAcesParameterOverrides", "SetAcesParameterOverrides") + // OverrideAcesParameters + ->Event("SetOverrideAcesParameters", &DisplayMapperComponentRequestBus::Events::SetOverrideAcesParameters) + ->Event("GetOverrideAcesParameters", &DisplayMapperComponentRequestBus::Events::GetOverrideAcesParameters) + ->VirtualProperty("OverrideAcesParameters", "GetOverrideAcesParameters", "SetOverrideAcesParameters") + // AlterSurround + ->Event("SetAlterSurround", &DisplayMapperComponentRequestBus::Events::SetAlterSurround) + ->Event("GetAlterSurround", &DisplayMapperComponentRequestBus::Events::GetAlterSurround) + ->VirtualProperty("AlterSurround", "GetAlterSurround", "SetAlterSurround") + // ApplyDesaturation + ->Event("SetApplyDesaturation", &DisplayMapperComponentRequestBus::Events::SetApplyDesaturation) + ->Event("GetApplyDesaturation", &DisplayMapperComponentRequestBus::Events::GetApplyDesaturation) + ->VirtualProperty("ApplyDesaturation", "GetApplyDesaturation", "SetApplyDesaturation") + // ApplyCATD60toD65 + ->Event("SetApplyCATD60toD65", &DisplayMapperComponentRequestBus::Events::SetApplyCATD60toD65) + ->Event("GetApplyCATD60toD65", &DisplayMapperComponentRequestBus::Events::GetApplyCATD60toD65) + ->VirtualProperty("ApplyCATD60toD65", "GetApplyCATD60toD65", "SetApplyCATD60toD65") + // CinemaLimitsBlack + ->Event("SetCinemaLimitsBlack", &DisplayMapperComponentRequestBus::Events::SetCinemaLimitsBlack) + ->Event("GetCinemaLimitsBlack", &DisplayMapperComponentRequestBus::Events::GetCinemaLimitsBlack) + ->VirtualProperty("CinemaLimitsBlack", "GetCinemaLimitsBlack", "SetCinemaLimitsBlack") + // CinemaLimitsWhite + ->Event("SetCinemaLimitsWhite", &DisplayMapperComponentRequestBus::Events::SetCinemaLimitsWhite) + ->Event("GetCinemaLimitsWhite", &DisplayMapperComponentRequestBus::Events::GetCinemaLimitsWhite) + ->VirtualProperty("CinemaLimitsWhite", "GetCinemaLimitsWhite", "SetCinemaLimitsWhite") + // MinPoint + ->Event("SetMinPoint", &DisplayMapperComponentRequestBus::Events::SetMinPoint) + ->Event("GetMinPoint", &DisplayMapperComponentRequestBus::Events::GetMinPoint) + ->VirtualProperty("MinPoint", "GetMinPoint", "SetMinPoint") + // MidPoint + ->Event("SetMidPoint", &DisplayMapperComponentRequestBus::Events::SetMidPoint) + ->Event("GetMidPoint", &DisplayMapperComponentRequestBus::Events::GetMidPoint) + ->VirtualProperty("MidPoint", "GetMidPoint", "SetMidPoint") + // MaxPoint + ->Event("SetMaxPoint", &DisplayMapperComponentRequestBus::Events::SetMaxPoint) + ->Event("GetMaxPoint", &DisplayMapperComponentRequestBus::Events::GetMaxPoint) + ->VirtualProperty("MaxPoint", "GetMaxPoint", "SetMaxPoint") + // SurroundGamma + ->Event("SetSurroundGamma", &DisplayMapperComponentRequestBus::Events::SetSurroundGamma) + ->Event("GetSurroundGamma", &DisplayMapperComponentRequestBus::Events::GetSurroundGamma) + ->VirtualProperty("SurroundGamma", "GetSurroundGamma", "SetSurroundGamma") + // Gamma + ->Event("SetGamma", &DisplayMapperComponentRequestBus::Events::SetGamma) + ->Event("GetGamma", &DisplayMapperComponentRequestBus::Events::GetGamma) + ->VirtualProperty("Gamma", "GetGamma", "SetGamma") + ; + } } void DisplayMapperComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) @@ -92,11 +157,16 @@ namespace AZ m_configuration.m_displayMapperOperation = displayMapperOperationType; OnConfigChanged(); DisplayMapperComponentNotificationBus::Broadcast( - &DisplayMapperComponentNotificationBus::Handler::OntDisplayMapperOperationTypeUpdated, + &DisplayMapperComponentNotificationBus::Handler::OnDisplayMapperOperationTypeUpdated, m_configuration.m_displayMapperOperation); } } + DisplayMapperOperationType DisplayMapperComponentController::GetDisplayMapperOperationType() const + { + return m_configuration.m_displayMapperOperation; + } + void DisplayMapperComponentController::SetAcesParameterOverrides(const AcesParameterOverrides& parameterOverrides) { m_configuration.m_acesParameterOverrides = parameterOverrides; @@ -109,6 +179,165 @@ namespace AZ m_configuration.m_acesParameterOverrides); } + const AcesParameterOverrides& DisplayMapperComponentController::GetAcesParameterOverrides() const + { + return m_configuration.m_acesParameterOverrides; + } + + void DisplayMapperComponentController::SetOverrideAcesParameters(bool value) + { + m_configuration.m_acesParameterOverrides.m_overrideDefaults = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + bool DisplayMapperComponentController::GetOverrideAcesParameters() const + { + return m_configuration.m_acesParameterOverrides.m_overrideDefaults; + } + + void DisplayMapperComponentController::SetAlterSurround(bool value) + { + m_configuration.m_acesParameterOverrides.m_alterSurround = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + bool DisplayMapperComponentController::GetAlterSurround() const + { + return m_configuration.m_acesParameterOverrides.m_alterSurround; + } + + void DisplayMapperComponentController::SetApplyDesaturation(bool value) + { + m_configuration.m_acesParameterOverrides.m_applyDesaturation = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + bool DisplayMapperComponentController::GetApplyDesaturation() const + { + return m_configuration.m_acesParameterOverrides.m_applyDesaturation; + } + + void DisplayMapperComponentController::SetApplyCATD60toD65(bool value) + { + m_configuration.m_acesParameterOverrides.m_applyCATD60toD65 = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + bool DisplayMapperComponentController::GetApplyCATD60toD65() const + { + return m_configuration.m_acesParameterOverrides.m_applyCATD60toD65; + } + + void DisplayMapperComponentController::SetCinemaLimitsBlack(float value) + { + m_configuration.m_acesParameterOverrides.m_cinemaLimitsBlack = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetCinemaLimitsBlack() const + { + return m_configuration.m_acesParameterOverrides.m_cinemaLimitsBlack; + } + + void DisplayMapperComponentController::SetCinemaLimitsWhite(float value) + { + m_configuration.m_acesParameterOverrides.m_cinemaLimitsWhite = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetCinemaLimitsWhite() const + { + return m_configuration.m_acesParameterOverrides.m_cinemaLimitsWhite; + } + + void DisplayMapperComponentController::SetMinPoint(float value) + { + m_configuration.m_acesParameterOverrides.m_minPoint = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetMinPoint() const + { + return m_configuration.m_acesParameterOverrides.m_minPoint; + } + + void DisplayMapperComponentController::SetMidPoint(float value) + { + m_configuration.m_acesParameterOverrides.m_midPoint = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetMidPoint() const + { + return m_configuration.m_acesParameterOverrides.m_midPoint; + } + + void DisplayMapperComponentController::SetMaxPoint(float value) + { + m_configuration.m_acesParameterOverrides.m_maxPoint = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetMaxPoint() const + { + return m_configuration.m_acesParameterOverrides.m_maxPoint; + } + + void DisplayMapperComponentController::SetSurroundGamma(float value) + { + m_configuration.m_acesParameterOverrides.m_surroundGamma = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetSurroundGamma() const + { + return m_configuration.m_acesParameterOverrides.m_surroundGamma; + } + + void DisplayMapperComponentController::SetGamma(float value) + { + m_configuration.m_acesParameterOverrides.m_gamma = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetGamma() const + { + return m_configuration.m_acesParameterOverrides.m_gamma; + } + void DisplayMapperComponentController::OnConfigChanged() { // Register the configuration with the AcesDisplayMapperFeatureProcessor for this scene. diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h index efa2070828..412bdc8524 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h @@ -51,7 +51,31 @@ namespace AZ //! DisplayMapperComponentRequestBus::Handler overrides... void LoadPreset(OutputDeviceTransformType preset) override; void SetDisplayMapperOperationType(DisplayMapperOperationType displayMapperOperationType) override; + DisplayMapperOperationType GetDisplayMapperOperationType() const override; void SetAcesParameterOverrides(const AcesParameterOverrides& parameterOverrides) override; + const AcesParameterOverrides& GetAcesParameterOverrides() const override; + void SetOverrideAcesParameters(bool value) override; + bool GetOverrideAcesParameters() const override; + void SetAlterSurround(bool value) override; + bool GetAlterSurround() const override; + void SetApplyDesaturation(bool value) override; + bool GetApplyDesaturation() const override; + void SetApplyCATD60toD65(bool value) override; + bool GetApplyCATD60toD65() const override; + void SetCinemaLimitsBlack(float value) override; + float GetCinemaLimitsBlack() const override; + void SetCinemaLimitsWhite(float value) override; + float GetCinemaLimitsWhite() const override; + void SetMinPoint(float value) override; + float GetMinPoint() const override; + void SetMidPoint(float value) override; + float GetMidPoint() const override; + void SetMaxPoint(float value) override; + float GetMaxPoint() const override; + void SetSurroundGamma(float value) override; + float GetSurroundGamma() const override; + void SetGamma(float value) override; + float GetGamma() const override; private: AZ_DISABLE_COPY(DisplayMapperComponentController); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp index aadb0cc22b..4a03c6f712 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp @@ -54,63 +54,89 @@ namespace AZ ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + // m_overrideDefaults ->DataElement( AZ::Edit::UIHandlers::CheckBox, &AcesParameterOverrides::m_overrideDefaults, "Override Defaults", "When enabled allows parameter overrides for ACES configuration") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + // m_alterSurround ->DataElement( AZ::Edit::UIHandlers::CheckBox, &AcesParameterOverrides::m_alterSurround, "Alter Surround", "Apply gamma adjustment to compensate for dim surround") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + + // m_applyDesaturation ->DataElement( AZ::Edit::UIHandlers::CheckBox, &AcesParameterOverrides::m_applyDesaturation, "Alter Desaturation", "Apply desaturation to compensate for luminance difference") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + + // m_applyCATD60toD65 ->DataElement( AZ::Edit::UIHandlers::CheckBox, &AcesParameterOverrides::m_applyCATD60toD65, "Alter CAT D60 to D65", "Apply Color appearance transform (CAT) from ACES white point to assumed observer adapted white point") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - + + // m_cinemaLimitsBlack ->DataElement( - Edit::UIHandlers::Default, &AcesParameterOverrides::m_cinemaLimitsBlack, + Edit::UIHandlers::Slider, &AcesParameterOverrides::m_cinemaLimitsBlack, "Cinema Limit (black)", "Reference black luminance value") ->Attribute(AZ::Edit::Attributes::Min, 0.02f) ->Attribute(AZ::Edit::Attributes::Max, &AcesParameterOverrides::m_cinemaLimitsWhite) + ->Attribute(AZ::Edit::Attributes::Step, 0.005f) + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + + // m_cinemaLimitsWhite ->DataElement( - Edit::UIHandlers::Default, &AcesParameterOverrides::m_cinemaLimitsWhite, + Edit::UIHandlers::Slider, &AcesParameterOverrides::m_cinemaLimitsWhite, "Cinema Limit (white)", "Reference white luminance value") ->Attribute(AZ::Edit::Attributes::Min, &AcesParameterOverrides::m_cinemaLimitsBlack) - ->Attribute(AZ::Edit::Attributes::Max, 4000) + ->Attribute(AZ::Edit::Attributes::Max, 4000.f) + ->Attribute(AZ::Edit::Attributes::Step, 0.005f) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + // m_minPoint ->DataElement( - Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_minPoint, "Min Point (luminance)", + Edit::UIHandlers::Slider, &AcesParameterOverrides::m_minPoint, "Min Point (luminance)", "Linear extension below this") ->Attribute(AZ::Edit::Attributes::Min, 0.002f) ->Attribute(AZ::Edit::Attributes::Max, &AcesParameterOverrides::m_midPoint) - ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - ->DataElement( - Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_midPoint, "Mid Point (luminance)", "Middle gray") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::AttributesAndValues) + + // m_midPoint + ->DataElement(Edit::UIHandlers::Slider, &AcesParameterOverrides::m_midPoint, + "Mid Point (luminance)", "Middle gray") ->Attribute(AZ::Edit::Attributes::Min, &AcesParameterOverrides::m_minPoint) ->Attribute(AZ::Edit::Attributes::Max, &AcesParameterOverrides::m_maxPoint) - ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::AttributesAndValues) + + // m_maxPoint ->DataElement( - Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_maxPoint, "Max Point (luminance)", + Edit::UIHandlers::Slider, &AcesParameterOverrides::m_maxPoint, "Max Point (luminance)", "Linear extension above this") ->Attribute(AZ::Edit::Attributes::Min, &AcesParameterOverrides::m_midPoint) - ->Attribute(AZ::Edit::Attributes::Max, 4000) - ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->Attribute(AZ::Edit::Attributes::Max, 4000.f) + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::AttributesAndValues) + // m_surroundGamma ->DataElement( - AZ::Edit::UIHandlers::Default, &AcesParameterOverrides::m_surroundGamma, "Surround Gamma", + AZ::Edit::UIHandlers::Slider, &AcesParameterOverrides::m_surroundGamma, "Surround Gamma", "Gamma adjustment to be applied to compensate for the condition of the viewing environment") + ->Attribute(AZ::Edit::Attributes::Min, 0.6f) + ->Attribute(AZ::Edit::Attributes::Max, 1.2f) + ->Attribute(AZ::Edit::Attributes::Step, 0.005f) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + + // m_gamma ->DataElement( - AZ::Edit::UIHandlers::Default, &AcesParameterOverrides::m_gamma, "Gamma", + AZ::Edit::UIHandlers::Slider, &AcesParameterOverrides::m_gamma, "Gamma", "Optional gamma value that is applied as basic gamma curve OETF") + ->Attribute(AZ::Edit::Attributes::Min, 0.2f) + ->Attribute(AZ::Edit::Attributes::Max, 4.0f) + ->Attribute(AZ::Edit::Attributes::Step, 0.005f) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) // Load preset group @@ -154,6 +180,8 @@ namespace AZ if (auto behaviorContext = azrtti_cast(context)) { + behaviorContext->Class()->RequestBus("DisplayMapperComponentRequestBus"); + behaviorContext->ConstantProperty("EditorDisplayMapperComponentTypeId", BehaviorConstant(Uuid(EditorDisplayMapperComponentTypeId))) ->Attribute(AZ::Script::Attributes::Module, "render") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); From 982c30eefdbe3364c55265e690bcec576b6a2dc6 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Tue, 1 Jun 2021 23:43:27 -0700 Subject: [PATCH 09/71] Added a visibility result flag to RPI::Cullable, set to true if the object passed all culling tests. --- .../Code/Include/Atom/RPI.Public/Culling.h | 3 +++ .../RPI/Code/Source/RPI.Public/Culling.cpp | 25 ++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index 3f03fb9dcb..295797d2dd 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -96,6 +96,9 @@ namespace AZ }; LodData m_lodData; + //! Flag indicating if the object is visible, i.e., was not culled out in the last frame + bool m_isVisible = true; + void SetDebugName([[maybe_unused]] const AZ::Name& debugName) { #ifdef AZ_CULL_DEBUG_ENABLED diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index 79d152f661..85b6bf07b8 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -315,21 +315,29 @@ namespace AZ //Add all objects within this node to the view, without any extra culling for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries) { -#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED - if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) -#endif { if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { Cullable* c = static_cast(visibleEntry->m_userData); + + // reset visibility flag to false, update to true if all culling checks pass + c->m_isVisible = false; + if ((c->m_cullData.m_drawListMask & drawListMask).none() || c->m_cullData.m_hideFlags & viewFlags || c->m_cullData.m_scene != m_jobData->m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this { continue; } - numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); - ++numVisibleCullables; + +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) +#endif + { + numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); + ++numVisibleCullables; + c->m_isVisible = true; + } } } } @@ -342,6 +350,10 @@ namespace AZ if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { Cullable* c = static_cast(visibleEntry->m_userData); + + // reset visibility flag to false, update to true if all culling checks pass + c->m_isVisible = false; + if ((c->m_cullData.m_drawListMask & drawListMask).none() || c->m_cullData.m_hideFlags & viewFlags || c->m_cullData.m_scene != m_jobData->m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this @@ -362,6 +374,7 @@ namespace AZ { numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); ++numVisibleCullables; + c->m_isVisible = true; } } } @@ -461,11 +474,11 @@ namespace AZ corners[7] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f); // find min clip-space depth and NDC min/max + float minDepth = FLT_MAX; float ndcMinX = FLT_MAX; float ndcMinY = FLT_MAX; float ndcMaxX = -FLT_MAX; float ndcMaxY = -FLT_MAX; - float minDepth = FLT_MAX; for (uint32_t index = 0; index < 8; ++index) { minDepth = AZStd::min(minDepth, corners[index].GetW()); From a1b8d1233cb75a330adfd260ad85c827f163f84d Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 11:28:22 -0700 Subject: [PATCH 10/71] [cpack_installer] initial work for installer Jenkins jobs --- .../build/Platform/Windows/build_config.json | 15 ++++++++ .../Windows/build_installer_windows.cmd | 22 +++++++++++ .../build/Platform/Windows/build_windows.cmd | 9 +++++ .../Platform/Windows/installer_windows.cmd | 38 +++++++++++++++++++ .../Platform/Windows/install_utiltools.ps1 | 3 ++ 5 files changed, 87 insertions(+) create mode 100644 scripts/build/Platform/Windows/build_installer_windows.cmd create mode 100644 scripts/build/Platform/Windows/installer_windows.cmd diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 38cd7d6ad8..b0d16f1fb6 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -306,6 +306,21 @@ "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" } }, + "windows_installer": { + "TAGS": [ + "package" + ], + "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", + "CMAKE_INCLUDE_WIX": "True", + "CMAKE_LY_PROJECTS": "", + "CMAKE_TARGET": "ALL_BUILD", + "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" + } + }, "project_enginesource_profile_vs2019": { "TAGS": [ "project" diff --git a/scripts/build/Platform/Windows/build_installer_windows.cmd b/scripts/build/Platform/Windows/build_installer_windows.cmd new file mode 100644 index 0000000000..0d50f4b57a --- /dev/null +++ b/scripts/build/Platform/Windows/build_installer_windows.cmd @@ -0,0 +1,22 @@ +@ECHO OFF +REM +REM All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +REM its licensors. +REM +REM For complete copyright and license terms please see the LICENSE at the root of this +REM distribution (the "License"). All use of this software is governed by the License, +REM or, if provided, by the license below or the license accompanying this file. Do not +REM remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM + +CALL "%~dp0build_windows.cmd" +IF NOT %ERRORLEVEL%==0 GOTO :error + +CALL "%~dp0installer_windows.cmd" +IF NOT %ERRORLEVEL%==0 GOTO :error + +EXIT /b 0 + +:error +EXIT /b 1 \ No newline at end of file diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index 3e995e1905..109db3438e 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -38,6 +38,15 @@ IF NOT EXIST %TMP% ( REM Compute half the amount of processors so some jobs can run SET /a HALF_PROCESSORS = NUMBER_OF_PROCESSORS / 2 +IF %CMAKE_INCLUDE_WIX%=="True" ( + REM Explicitly enable wix via command line arg for forensic logging + SET EXTRA_CMAKE_OPTIONS=%EXTRA_CMAKE_OPTIONS% -DLY_WIX_PATH="%WIX%" +) +ELSE ( + REM Disable implicit enabling of windows packing by clearing out the wix variable + SET WIX= +) + SET LAST_CONFIGURE_CMD_FILE=ci_last_configure_cmd.txt SET CONFIGURE_CMD=cmake %SOURCE_DIRECTORY% %CMAKE_OPTIONS% %EXTRA_CMAKE_OPTIONS% -DLY_3RDPARTY_PATH="%LY_3RDPARTY_PATH%" -DLY_PROJECTS=%CMAKE_LY_PROJECTS% IF NOT EXIST CMakeCache.txt ( diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd new file mode 100644 index 0000000000..c613f0a1e3 --- /dev/null +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -0,0 +1,38 @@ +@ECHO OFF +REM +REM All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +REM its licensors. +REM +REM For complete copyright and license terms please see the LICENSE at the root of this +REM distribution (the "License"). All use of this software is governed by the License, +REM or, if provided, by the license below or the license accompanying this file. Do not +REM remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM + +SETLOCAL EnableDelayedExpansion + +CALL %~dp0env_windows.cmd + +IF NOT EXIST %OUTPUT_DIRECTORY% ( + ECHO [ci_build] Error: $OUTPUT_DIRECTORY was not found + GOTO :error +) +PUSHD %OUTPUT_DIRECTORY% + +REM Override the temporary directory used by wix to the EBS volume +SET "WIX_TEMP=!WORKSPACE!/temp/wix" + +REM Run cpack +ECHO [ci_build] cpack -C %CONFIGURATION% +cpack -C %CONFIGURATION% +IF NOT %ERRORLEVEL%==0 GOTO :popd_error + +POPD +EXIT /b 0 + +:popd_error +POPD + +:error +EXIT /b 1 \ No newline at end of file diff --git a/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 b/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 index 4e3695d35d..35ae04cc09 100644 --- a/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 +++ b/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 @@ -29,3 +29,6 @@ choco install corretto8jdk -y --ia INSTALLDIR="c:\jdk8" # Custom directory to ha # Install CMake choco install cmake -y --installargs 'ADD_CMAKE_TO_PATH=System' + +# Install WIX +choco install wixtoolset -y From cfd06f2e4a46869052dd5d6e5baee03d21860d35 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 13:10:11 -0700 Subject: [PATCH 11/71] [cpack_installer] added check for desired cmake version to be at least greater than minimum required plus minor cleanup --- cmake/Packaging.cmake | 10 ++++++++-- cmake/Platform/Windows/Packaging_windows.cmake | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index 84bad13687..e7136eab12 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -51,6 +51,12 @@ if(NOT CPACK_GENERATOR) return() endif() +if(${CPACK_DESIRED_CMAKE_VERSION} VERSION_LESS ${CMAKE_MINIMUM_REQUIRED_VERSION}) + message(FATAL_ERROR + "The desired version of CMake to be included in the package is " + "is below the minium required version of CMake to run") +endif() + # pull down the desired copy of CMake so it can be included in the package if(NOT (CPACK_CMAKE_PACKAGE_FILE AND CPACK_CMAKE_PACKAGE_HASH)) message(FATAL_ERROR @@ -67,7 +73,7 @@ list(GET _version_componets 1 _minor_version) set(_url_version_tag "v${_major_version}.${_minor_version}") set(_package_url "https://cmake.org/files/${_url_version_tag}/${CPACK_CMAKE_PACKAGE_FILE}") -message(STATUS "Ensuring CMake ${CPACK_DESIRED_CMAKE_VERSION} is available for packaging...") +message(STATUS "Downloading CMake ${CPACK_DESIRED_CMAKE_VERSION} for packaging...") download_file( URL ${_package_url} TARGET_FILE ${_cmake_package_dest} @@ -77,7 +83,7 @@ download_file( list(GET _results 0 _status_code) if (${_status_code} EQUAL 0 AND EXISTS ${_cmake_package_dest}) - message(STATUS "-> Package found and verified!") + message(STATUS "Package found and verified!") else() file(REMOVE ${_cmake_package_dest}) list(REMOVE_AT _results 0) diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index db9c7fc906..5210c24e7b 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -12,7 +12,7 @@ set(LY_WIX_PATH "" CACHE PATH "Path to the WiX install path") if(LY_WIX_PATH) - file(TO_CMAKE_PATH ${LY_QTIFW_PATH} CPACK_WIX_ROOT) + file(TO_CMAKE_PATH ${LY_WIX_PATH} CPACK_WIX_ROOT) elseif(DEFINED ENV{WIX}) file(TO_CMAKE_PATH $ENV{WIX} CPACK_WIX_ROOT) endif() @@ -26,7 +26,7 @@ else() return() endif() -set(CPACK_GENERATOR "WIX") +set(CPACK_GENERATOR WIX) set(_cmake_package_name "cmake-${CPACK_DESIRED_CMAKE_VERSION}-windows-x86_64") set(CPACK_CMAKE_PACKAGE_FILE "${_cmake_package_name}.zip") From 12cdaed03e0c968630c4b27d25d86cdeede3c389 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 13:58:37 -0700 Subject: [PATCH 12/71] [cpack_installer] updated installer icon/logo --- cmake/Platform/Windows/Packaging/product_icon.ico | 4 ++-- cmake/Platform/Windows/Packaging/product_logo.png | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/Platform/Windows/Packaging/product_icon.ico b/cmake/Platform/Windows/Packaging/product_icon.ico index 0680ceea19..e7b77c35bf 100644 --- a/cmake/Platform/Windows/Packaging/product_icon.ico +++ b/cmake/Platform/Windows/Packaging/product_icon.ico @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c042fce57915fc749abc7b37de765fd697c3c4d7de045a3d44805aa0ce29901a -size 107016 +oid sha256:d717f77fe01f45df934a61bbc215e5322447d21e16f3cebcf2a02f148178f266 +size 106449 diff --git a/cmake/Platform/Windows/Packaging/product_logo.png b/cmake/Platform/Windows/Packaging/product_logo.png index d5fd60ffb8..ac9c06f8f1 100644 --- a/cmake/Platform/Windows/Packaging/product_logo.png +++ b/cmake/Platform/Windows/Packaging/product_logo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac0348c906c91de864cba91c0231b4794d8a00fafa630d13f2232351b90aa59b +oid sha256:8c804a6be619b9f35cad46eab30b94def7a4ac7142a92cb3f7c78a659381d834 size 11074 From 4b40f23d0b63cdf5b75188000c843e08c168c139 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 14:06:27 -0700 Subject: [PATCH 13/71] [cpack_installer] couple small fixes to installer Jenkins scripts --- scripts/build/Platform/Windows/build_windows.cmd | 5 ++--- scripts/build/Platform/Windows/installer_windows.cmd | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index 109db3438e..a2f42b75cf 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -38,11 +38,10 @@ IF NOT EXIST %TMP% ( REM Compute half the amount of processors so some jobs can run SET /a HALF_PROCESSORS = NUMBER_OF_PROCESSORS / 2 -IF %CMAKE_INCLUDE_WIX%=="True" ( +IF "%CMAKE_INCLUDE_WIX%"=="True" ( REM Explicitly enable wix via command line arg for forensic logging SET EXTRA_CMAKE_OPTIONS=%EXTRA_CMAKE_OPTIONS% -DLY_WIX_PATH="%WIX%" -) -ELSE ( +) ELSE ( REM Disable implicit enabling of windows packing by clearing out the wix variable SET WIX= ) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index c613f0a1e3..e8ce10ff14 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -22,6 +22,9 @@ PUSHD %OUTPUT_DIRECTORY% REM Override the temporary directory used by wix to the EBS volume SET "WIX_TEMP=!WORKSPACE!/temp/wix" +IF NOT EXIST "%WIX_TEMP%" ( + MKDIR %WIX_TEMP% +) REM Run cpack ECHO [ci_build] cpack -C %CONFIGURATION% From c6e4e3ed1fd549d88e27a0aac254ec3ab267bc98 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 14:57:18 -0700 Subject: [PATCH 14/71] [cpack_installer] few more small fixes to installer Jenkins scripts --- scripts/build/Platform/Windows/build_windows.cmd | 2 +- scripts/build/build_node/Platform/Windows/install_utiltools.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index a2f42b75cf..799e6828b2 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -40,7 +40,7 @@ SET /a HALF_PROCESSORS = NUMBER_OF_PROCESSORS / 2 IF "%CMAKE_INCLUDE_WIX%"=="True" ( REM Explicitly enable wix via command line arg for forensic logging - SET EXTRA_CMAKE_OPTIONS=%EXTRA_CMAKE_OPTIONS% -DLY_WIX_PATH="%WIX%" + SET CMAKE_OPTIONS=%CMAKE_OPTIONS% -DLY_WIX_PATH="%WIX%" ) ELSE ( REM Disable implicit enabling of windows packing by clearing out the wix variable SET WIX= diff --git a/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 b/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 index 35ae04cc09..050e446f52 100644 --- a/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 +++ b/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 @@ -30,5 +30,5 @@ choco install corretto8jdk -y --ia INSTALLDIR="c:\jdk8" # Custom directory to ha # Install CMake choco install cmake -y --installargs 'ADD_CMAKE_TO_PATH=System' -# Install WIX +# Install Windows Installer XML toolkit (WiX) choco install wixtoolset -y From 134258c18acff77588b7f57d8200d069144ecc2a Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 15:03:26 -0700 Subject: [PATCH 15/71] [cpack_installer] add trailing newline to some new files --- scripts/build/Platform/Windows/build_installer_windows.cmd | 2 +- scripts/build/Platform/Windows/installer_windows.cmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build/Platform/Windows/build_installer_windows.cmd b/scripts/build/Platform/Windows/build_installer_windows.cmd index 0d50f4b57a..4f31fee085 100644 --- a/scripts/build/Platform/Windows/build_installer_windows.cmd +++ b/scripts/build/Platform/Windows/build_installer_windows.cmd @@ -19,4 +19,4 @@ IF NOT %ERRORLEVEL%==0 GOTO :error EXIT /b 0 :error -EXIT /b 1 \ No newline at end of file +EXIT /b 1 diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index e8ce10ff14..e3a60fee1c 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -38,4 +38,4 @@ EXIT /b 0 POPD :error -EXIT /b 1 \ No newline at end of file +EXIT /b 1 From 197241f16d4a7f0ec6bfc33af715d43aff93e6e8 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 15:40:41 -0700 Subject: [PATCH 16/71] [cpack_installer] fixed issue with cpack selection --- scripts/build/Platform/Windows/installer_windows.cmd | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index e3a60fee1c..824a461a4b 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -26,9 +26,17 @@ IF NOT EXIST "%WIX_TEMP%" ( MKDIR %WIX_TEMP% ) +REM Make sure we are using the CMake version of CPack and not the one that comes with chocolaty +IF "%LY_CMAKE_PATH%"=="" ( + for /f %%i in ('where cmake') do SET "CMAKE_EXE_PATH=%%i" + for %%F in ("%CMAKE_EXE_PATH%") do SET "CMAKE_INSTALL_PATH=%%~dpF" +) ELSE ( + SET "CMAKE_INSTALL_PATH=%LY_CMAKE_PATH%\" +) + REM Run cpack -ECHO [ci_build] cpack -C %CONFIGURATION% -cpack -C %CONFIGURATION% +ECHO [ci_build] "%CMAKE_INSTALL_PATH%cpack" -C %CONFIGURATION% +"%CMAKE_INSTALL_PATH%cpack" -C %CONFIGURATION% IF NOT %ERRORLEVEL%==0 GOTO :popd_error POPD From fd8cff6aecb2c93b93e6e09f9927feac563392c7 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 16:10:41 -0700 Subject: [PATCH 17/71] [cpack_installer] second attempt to fix cpack selection --- scripts/build/Platform/Windows/installer_windows.cmd | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index 824a461a4b..b1fecaa2cb 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -23,10 +23,11 @@ PUSHD %OUTPUT_DIRECTORY% REM Override the temporary directory used by wix to the EBS volume SET "WIX_TEMP=!WORKSPACE!/temp/wix" IF NOT EXIST "%WIX_TEMP%" ( - MKDIR %WIX_TEMP% + MKDIR "WIX_TEMP%" ) REM Make sure we are using the CMake version of CPack and not the one that comes with chocolaty +SET CMAKE_INSTALL_PATH= IF "%LY_CMAKE_PATH%"=="" ( for /f %%i in ('where cmake') do SET "CMAKE_EXE_PATH=%%i" for %%F in ("%CMAKE_EXE_PATH%") do SET "CMAKE_INSTALL_PATH=%%~dpF" @@ -34,6 +35,11 @@ IF "%LY_CMAKE_PATH%"=="" ( SET "CMAKE_INSTALL_PATH=%LY_CMAKE_PATH%\" ) +IF "%CMAKE_INSTALL_PATH%"=="" ( + ECHO [ci_build] CPack path not found + GOTO :popd_error +) + REM Run cpack ECHO [ci_build] "%CMAKE_INSTALL_PATH%cpack" -C %CONFIGURATION% "%CMAKE_INSTALL_PATH%cpack" -C %CONFIGURATION% From 201d6b1b72ec579c980c5e38d58fc354bfcc9a29 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 17:29:50 -0700 Subject: [PATCH 18/71] [cpack_installer] third attempt to fix cpack selection --- .../Platform/Windows/installer_windows.cmd | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index b1fecaa2cb..41cc21b35b 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -26,23 +26,29 @@ IF NOT EXIST "%WIX_TEMP%" ( MKDIR "WIX_TEMP%" ) -REM Make sure we are using the CMake version of CPack and not the one that comes with chocolaty -SET CMAKE_INSTALL_PATH= +REM Make sure we are using the CMake version of CPack and not the one that comes with chocolatey +SET CPACK_PATH= IF "%LY_CMAKE_PATH%"=="" ( - for /f %%i in ('where cmake') do SET "CMAKE_EXE_PATH=%%i" - for %%F in ("%CMAKE_EXE_PATH%") do SET "CMAKE_INSTALL_PATH=%%~dpF" + FOR /F %%i in ('where cpack') DO ( + REM The cpack in chocolatey expects a number supplied with --version so it will error + %%i --version > NUL + IF !ERRORLEVEL!==0 ( + SET "CPACK_PATH=%%i" + ) + ) ) ELSE ( - SET "CMAKE_INSTALL_PATH=%LY_CMAKE_PATH%\" + SET "CPACK_PATH=%LY_CMAKE_PATH%\cpack.exe" ) -IF "%CMAKE_INSTALL_PATH%"=="" ( - ECHO [ci_build] CPack path not found - GOTO :popd_error +ECHO [ci_build] "%CPACK_PATH%" --version +"%CPACK_PATH%" --version +IF ERRORLEVEL 1 ( + ECHO [ci_build] CPack not found! + exit /b 1 ) - REM Run cpack -ECHO [ci_build] "%CMAKE_INSTALL_PATH%cpack" -C %CONFIGURATION% -"%CMAKE_INSTALL_PATH%cpack" -C %CONFIGURATION% +ECHO [ci_build] "%CPACK_PATH%" -C %CONFIGURATION% +"%CPACK_PATH%" -C %CONFIGURATION% IF NOT %ERRORLEVEL%==0 GOTO :popd_error POPD From 01f3ba560819fcba0d3a5575510e597904cced4f Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 17:58:58 -0700 Subject: [PATCH 19/71] [cpack_installer] fourth attempt to fix cpack selection --- scripts/build/Platform/Windows/installer_windows.cmd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index 41cc21b35b..ef284290f3 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -23,7 +23,7 @@ PUSHD %OUTPUT_DIRECTORY% REM Override the temporary directory used by wix to the EBS volume SET "WIX_TEMP=!WORKSPACE!/temp/wix" IF NOT EXIST "%WIX_TEMP%" ( - MKDIR "WIX_TEMP%" + MKDIR "%WIX_TEMP%" ) REM Make sure we are using the CMake version of CPack and not the one that comes with chocolatey @@ -40,15 +40,15 @@ IF "%LY_CMAKE_PATH%"=="" ( SET "CPACK_PATH=%LY_CMAKE_PATH%\cpack.exe" ) -ECHO [ci_build] "%CPACK_PATH%" --version -"%CPACK_PATH%" --version +ECHO [ci_build] "!CPACK_PATH!" --version +"!CPACK_PATH!" --version IF ERRORLEVEL 1 ( ECHO [ci_build] CPack not found! exit /b 1 ) -REM Run cpack -ECHO [ci_build] "%CPACK_PATH%" -C %CONFIGURATION% -"%CPACK_PATH%" -C %CONFIGURATION% + +ECHO [ci_build] "!CPACK_PATH!" -C %CONFIGURATION% +"!CPACK_PATH!" -C %CONFIGURATION% IF NOT %ERRORLEVEL%==0 GOTO :popd_error POPD From 3f9811e498efdb07e9275a3395f201b65ce8aa0f Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 18:40:22 -0700 Subject: [PATCH 20/71] [cpack_installer] fifth attempt to fix cpack selection --- scripts/build/Platform/Windows/installer_windows.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index ef284290f3..73d1a3d88c 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -31,7 +31,7 @@ SET CPACK_PATH= IF "%LY_CMAKE_PATH%"=="" ( FOR /F %%i in ('where cpack') DO ( REM The cpack in chocolatey expects a number supplied with --version so it will error - %%i --version > NUL + "%%i" --version > NUL IF !ERRORLEVEL!==0 ( SET "CPACK_PATH=%%i" ) From c3df73bed8f4b052659086e69127e90ae794bb46 Mon Sep 17 00:00:00 2001 From: mnaumov Date: Wed, 2 Jun 2021 18:48:50 -0700 Subject: [PATCH 21/71] PR feedback and fixing TrackView --- .../DisplayMapperConfigurationDescriptor.cpp | 2 +- .../DisplayMapper/DisplayMapperComponentBus.h | 5 +++++ .../DisplayMapperComponentController.cpp | 20 +++++++++++++++++++ .../EditorDisplayMapperComponent.cpp | 5 ++--- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp index 0858381fc5..a064b61a1a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp @@ -96,7 +96,7 @@ namespace AZ ; serializeContext->Class() - ->Version(1) + ->Version(2) ->Field("Name", &DisplayMapperConfigurationDescriptor::m_name) ->Field("OperationType", &DisplayMapperConfigurationDescriptor::m_operationType) ->Field("LdrGradingLutEnabled", &DisplayMapperConfigurationDescriptor::m_ldrGradingLutEnabled) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h index 4a01f71325..448c6cd32a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h @@ -26,6 +26,11 @@ namespace AZ : public ComponentBus { public: + AZ_RTTI(AZ::Render::DisplayMapperComponentRequests, "{9E2E8AF5-1176-44B4-A461-E09867753349}"); + + /// Overrides the default AZ::EBusTraits handler policy to allow one listener only. + static const EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::Single; + //! Load preconfigured preset for specific ODT mode virtual void LoadPreset(OutputDeviceTransformType preset) = 0; //! Set display mapper type diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp index 06c549f560..30c0ac8b1d 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp @@ -122,10 +122,14 @@ namespace AZ void DisplayMapperComponentController::Activate(EntityId entityId) { m_entityId = entityId; + + DisplayMapperComponentRequestBus::Handler::BusConnect(m_entityId); } void DisplayMapperComponentController::Deactivate() { + DisplayMapperComponentRequestBus::Handler::BusDisconnect(m_entityId); + m_postProcessInterface = nullptr; m_entityId.SetInvalid(); } @@ -186,6 +190,10 @@ namespace AZ void DisplayMapperComponentController::SetOverrideAcesParameters(bool value) { + if (m_configuration.m_acesParameterOverrides.m_overrideDefaults == value) + { + return; // prevents flickering when set via TrackView + } m_configuration.m_acesParameterOverrides.m_overrideDefaults = value; if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) { @@ -200,6 +208,10 @@ namespace AZ void DisplayMapperComponentController::SetAlterSurround(bool value) { + if (m_configuration.m_acesParameterOverrides.m_alterSurround != value) + { + return; // prevents flickering when set via TrackView + } m_configuration.m_acesParameterOverrides.m_alterSurround = value; if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) { @@ -214,6 +226,10 @@ namespace AZ void DisplayMapperComponentController::SetApplyDesaturation(bool value) { + if (m_configuration.m_acesParameterOverrides.m_applyDesaturation != value) + { + return; // prevents flickering when set via TrackView + } m_configuration.m_acesParameterOverrides.m_applyDesaturation = value; if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) { @@ -228,6 +244,10 @@ namespace AZ void DisplayMapperComponentController::SetApplyCATD60toD65(bool value) { + if (m_configuration.m_acesParameterOverrides.m_applyCATD60toD65 != value) + { + return; // prevents flickering when set via TrackView + } m_configuration.m_acesParameterOverrides.m_applyCATD60toD65 = value; if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp index 4a03c6f712..8b59cfc9ea 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp @@ -10,10 +10,9 @@ * */ -#include "Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h" - #include #include +#include namespace AZ { @@ -180,7 +179,7 @@ namespace AZ if (auto behaviorContext = azrtti_cast(context)) { - behaviorContext->Class()->RequestBus("DisplayMapperComponentRequestBus"); + behaviorContext->Class()->RequestBus("DisplayMapperComponentRequestBus"); behaviorContext->ConstantProperty("EditorDisplayMapperComponentTypeId", BehaviorConstant(Uuid(EditorDisplayMapperComponentTypeId))) ->Attribute(AZ::Script::Attributes::Module, "render") From 86234841689b7c48de57b3d7b9c3a60f129637a0 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 19:02:42 -0700 Subject: [PATCH 22/71] Added Masked Occlusion Culling external files --- .../CompilerSpecific.inl | 98 + .../MaskedOcclusionCulling/LICENSE.txt | 181 ++ .../MaskedOcclusionCulling.cpp | 456 ++++ .../MaskedOcclusionCulling.h | 592 +++++ .../MaskedOcclusionCullingAVX2.cpp | 243 ++ .../MaskedOcclusionCullingAVX512.cpp | 309 +++ .../MaskedOcclusionCullingCommon.inl | 2053 +++++++++++++++++ .../MaskedOcclusionCulling/PackageInfo.json | 6 + 8 files changed, 3938 insertions(+) create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/CompilerSpecific.inl create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/LICENSE.txt create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/PackageInfo.json diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/CompilerSpecific.inl b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/CompilerSpecific.inl new file mode 100644 index 0000000000..a6203ff939 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/CompilerSpecific.inl @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common shared include file to hide compiler/os specific functions from the rest of the code. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) + #define __MICROSOFT_COMPILER +#endif + +#if defined(_WIN32) && (defined(_MSC_VER) || defined(__INTEL_COMPILER) || defined(__clang__)) // Windows: MSVC / Intel compiler / clang + #include + #include + + #define FORCE_INLINE __forceinline + + FORCE_INLINE unsigned long find_clear_lsb(unsigned int *mask) + { + unsigned long idx; + _BitScanForward(&idx, *mask); + *mask &= *mask - 1; + return idx; + } + + FORCE_INLINE void *aligned_alloc(size_t alignment, size_t size) + { + return _aligned_malloc(size, alignment); + } + + FORCE_INLINE void aligned_free(void *ptr) + { + _aligned_free(ptr); + } + +#elif defined(__GNUG__) || defined(__clang__) // G++ or clang + #include +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) + #include // memalign +#else + #include // memalign +#endif + #include + #include + #include + + #define FORCE_INLINE inline + + FORCE_INLINE unsigned long find_clear_lsb(unsigned int *mask) + { + unsigned long idx; + idx = __builtin_ctzl(*mask); + *mask &= *mask - 1; + return idx; + } + + FORCE_INLINE void *aligned_alloc(size_t alignment, size_t size) + { + return memalign(alignment, size); + } + + FORCE_INLINE void aligned_free(void *ptr) + { + free(ptr); + } + + FORCE_INLINE void __cpuidex(int* cpuinfo, int function, int subfunction) + { + __cpuid_count(function, subfunction, cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]); + } + + FORCE_INLINE unsigned long long _xgetbv(unsigned int index) + { + unsigned int eax, edx; + __asm__ __volatile__( + "xgetbv;" + : "=a" (eax), "=d"(edx) + : "c" (index) + ); + return ((unsigned long long)edx << 32) | eax; + } + +#else + #error Unsupported compiler +#endif diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/LICENSE.txt b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/LICENSE.txt new file mode 100644 index 0000000000..f1b08a582c --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/LICENSE.txt @@ -0,0 +1,181 @@ + +Apache License + Version 2.0, January 2004 + + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + You must give any other recipients of the Work or Derivative Works a copy of + this License; and + + + You must cause any modified files to carry prominent notices stating that You + changed the files; and + + + You must retain, in the Source form of any Derivative Works that You + distribute, all copyright, patent, trademark, and attribution notices from the + Source form of the Work, excluding those notices that do not pertain to any + part of the Derivative Works; and + + + If the Work includes a "NOTICE" text file as part of its distribution, then + any Derivative Works that You distribute must include a readable copy of the + attribution notices contained within such NOTICE file, excluding those notices + that do not pertain to any part of the Derivative Works, in at least one of + the following places: within a NOTICE text file distributed as part of the + Derivative Works; within the Source form or documentation, if provided along + with the Derivative Works; or, within a display generated by the Derivative + Works, if and wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and do not modify the + License. You may add Your own attribution notices within Derivative Works that + You distribute, alongside or as an addendum to the NOTICE text from the Work, + provided that such additional attribution notices cannot be construed as + modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + +Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, +Version 2.0 (the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or +agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +or implied. See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp new file mode 100644 index 0000000000..2844fbde00 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp @@ -0,0 +1,456 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include "MaskedOcclusionCulling.h" +#include "CompilerSpecific.inl" + +#if MOC_RECORDER_ENABLE +#include "FrameRecorder.h" +#endif + +#if defined(__AVX__) || defined(__AVX2__) + // For performance reasons, the MaskedOcclusionCullingAVX2/512.cpp files should be compiled with VEX encoding for SSE instructions (to avoid + // AVX-SSE transition penalties, see https://software.intel.com/en-us/articles/avoiding-avx-sse-transition-penalties). However, this file + // _must_ be compiled without VEX encoding to allow backwards compatibility. Best practice is to use lowest supported target platform + // (/arch:SSE2) as project default, and elevate only the MaskedOcclusionCullingAVX2/512.cpp files. + #error The MaskedOcclusionCulling.cpp should be compiled with lowest supported target platform, e.g. /arch:SSE2 +#endif + +static MaskedOcclusionCulling::Implementation DetectCPUFeatures(MaskedOcclusionCulling::pfnAlignedAlloc alignedAlloc, MaskedOcclusionCulling::pfnAlignedFree alignedFree) +{ + struct CpuInfo { int regs[4]; }; + + // Get regular CPUID values + int regs[4]; + __cpuidex(regs, 0, 0); + + // MOCVectorAllocator mocalloc( alignedAlloc, alignedFree ); + // std::vector> cpuId( mocalloc ), cpuIdEx( mocalloc ); + // cpuId.resize( regs[0] ); + size_t cpuIdCount = regs[0]; + CpuInfo * cpuId = (CpuInfo*)alignedAlloc( 64, sizeof(CpuInfo) * cpuIdCount ); + + for (size_t i = 0; i < cpuIdCount; ++i) + __cpuidex(cpuId[i].regs, (int)i, 0); + + // Get extended CPUID values + __cpuidex(regs, 0x80000000, 0); + + //cpuIdEx.resize(regs[0] - 0x80000000); + size_t cpuIdExCount = regs[0] - 0x80000000; + CpuInfo * cpuIdEx = (CpuInfo*)alignedAlloc( 64, sizeof( CpuInfo ) * cpuIdExCount ); + + for (size_t i = 0; i < cpuIdExCount; ++i) + __cpuidex(cpuIdEx[i].regs, 0x80000000 + (int)i, 0); + + #define TEST_BITS(A, B) (((A) & (B)) == (B)) + #define TEST_FMA_MOVE_OXSAVE (cpuIdCount >= 1 && TEST_BITS(cpuId[1].regs[2], (1 << 12) | (1 << 22) | (1 << 27))) + #define TEST_LZCNT (cpuIdExCount >= 1 && TEST_BITS(cpuIdEx[1].regs[2], 0x20)) + #define TEST_SSE41 (cpuIdCount >= 1 && TEST_BITS(cpuId[1].regs[2], (1 << 19))) + #define TEST_XMM_YMM (cpuIdCount >= 1 && TEST_BITS(_xgetbv(0), (1 << 2) | (1 << 1))) + #define TEST_OPMASK_ZMM (cpuIdCount >= 1 && TEST_BITS(_xgetbv(0), (1 << 7) | (1 << 6) | (1 << 5))) + #define TEST_BMI1_BMI2_AVX2 (cpuIdCount >= 7 && TEST_BITS(cpuId[7].regs[1], (1 << 3) | (1 << 5) | (1 << 8))) + #define TEST_AVX512_F_BW_DQ (cpuIdCount >= 7 && TEST_BITS(cpuId[7].regs[1], (1 << 16) | (1 << 17) | (1 << 30))) + + MaskedOcclusionCulling::Implementation retVal = MaskedOcclusionCulling::SSE2; + if (TEST_FMA_MOVE_OXSAVE && TEST_LZCNT && TEST_SSE41) + { + if (TEST_XMM_YMM && TEST_OPMASK_ZMM && TEST_BMI1_BMI2_AVX2 && TEST_AVX512_F_BW_DQ) + retVal = MaskedOcclusionCulling::AVX512; + else if (TEST_XMM_YMM && TEST_BMI1_BMI2_AVX2) + retVal = MaskedOcclusionCulling::AVX2; + } + else if (TEST_SSE41) + retVal = MaskedOcclusionCulling::SSE41; + alignedFree( cpuId ); + alignedFree( cpuIdEx ); + return retVal; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Utility functions (not directly related to the algorithm/rasterizer) +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void MaskedOcclusionCulling::TransformVertices(const float *mtx, const float *inVtx, float *xfVtx, unsigned int nVtx, const VertexLayout &vtxLayout) +{ + // This function pretty slow, about 10-20% slower than if the vertices are stored in aligned SOA form. + if (nVtx == 0) + return; + + // Load matrix and swizzle out the z component. For post-multiplication (OGL), the matrix is assumed to be column + // major, with one column per SSE register. For pre-multiplication (DX), the matrix is assumed to be row major. + __m128 mtxCol0 = _mm_loadu_ps(mtx); + __m128 mtxCol1 = _mm_loadu_ps(mtx + 4); + __m128 mtxCol2 = _mm_loadu_ps(mtx + 8); + __m128 mtxCol3 = _mm_loadu_ps(mtx + 12); + + int stride = vtxLayout.mStride; + const char *vPtr = (const char *)inVtx; + float *outPtr = xfVtx; + + // Iterate through all vertices and transform + for (unsigned int vtx = 0; vtx < nVtx; ++vtx) + { + __m128 xVal = _mm_load1_ps((float*)(vPtr)); + __m128 yVal = _mm_load1_ps((float*)(vPtr + vtxLayout.mOffsetY)); + __m128 zVal = _mm_load1_ps((float*)(vPtr + vtxLayout.mOffsetZ)); + + __m128 xform = _mm_add_ps(_mm_mul_ps(mtxCol0, xVal), _mm_add_ps(_mm_mul_ps(mtxCol1, yVal), _mm_add_ps(_mm_mul_ps(mtxCol2, zVal), mtxCol3))); + _mm_storeu_ps(outPtr, xform); + vPtr += stride; + outPtr += 4; + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Typedefs +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; +typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; +typedef MaskedOcclusionCulling::VertexLayout VertexLayout; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common SSE2/SSE4.1 defines +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_LANES 4 +#define TILE_HEIGHT_SHIFT 2 + +#define SIMD_LANE_IDX _mm_setr_epi32(0, 1, 2, 3) + +#define SIMD_SUB_TILE_COL_OFFSET _mm_setr_epi32(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET _mm_setzero_si128() +#define SIMD_SUB_TILE_COL_OFFSET_F _mm_setr_ps(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET_F _mm_setzero_ps() + +#define SIMD_LANE_YCOORD_I _mm_setr_epi32(128, 384, 640, 896) +#define SIMD_LANE_YCOORD_F _mm_setr_ps(128.0f, 384.0f, 640.0f, 896.0f) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common SSE2/SSE4.1 functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef __m128 __mw; +typedef __m128i __mwi; + +#define _mmw_set1_ps _mm_set1_ps +#define _mmw_setzero_ps _mm_setzero_ps +#define _mmw_and_ps _mm_and_ps +#define _mmw_or_ps _mm_or_ps +#define _mmw_xor_ps _mm_xor_ps +#define _mmw_not_ps(a) _mm_xor_ps((a), _mm_castsi128_ps(_mm_set1_epi32(~0))) +#define _mmw_andnot_ps _mm_andnot_ps +#define _mmw_neg_ps(a) _mm_xor_ps((a), _mm_set1_ps(-0.0f)) +#define _mmw_abs_ps(a) _mm_and_ps((a), _mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF))) +#define _mmw_add_ps _mm_add_ps +#define _mmw_sub_ps _mm_sub_ps +#define _mmw_mul_ps _mm_mul_ps +#define _mmw_div_ps _mm_div_ps +#define _mmw_min_ps _mm_min_ps +#define _mmw_max_ps _mm_max_ps +#define _mmw_movemask_ps _mm_movemask_ps +#define _mmw_cmpge_ps(a,b) _mm_cmpge_ps(a, b) +#define _mmw_cmpgt_ps(a,b) _mm_cmpgt_ps(a, b) +#define _mmw_cmpeq_ps(a,b) _mm_cmpeq_ps(a, b) +#define _mmw_fmadd_ps(a,b,c) _mm_add_ps(_mm_mul_ps(a,b), c) +#define _mmw_fmsub_ps(a,b,c) _mm_sub_ps(_mm_mul_ps(a,b), c) +#define _mmw_shuffle_ps _mm_shuffle_ps +#define _mmw_insertf32x4_ps(a,b,c) (b) +#define _mmw_cvtepi32_ps _mm_cvtepi32_ps +#define _mmw_blendv_epi32(a,b,c) simd_cast<__mwi>(_mmw_blendv_ps(simd_cast<__mw>(a), simd_cast<__mw>(b), simd_cast<__mw>(c))) + +#define _mmw_set1_epi32 _mm_set1_epi32 +#define _mmw_setzero_epi32 _mm_setzero_si128 +#define _mmw_and_epi32 _mm_and_si128 +#define _mmw_or_epi32 _mm_or_si128 +#define _mmw_xor_epi32 _mm_xor_si128 +#define _mmw_not_epi32(a) _mm_xor_si128((a), _mm_set1_epi32(~0)) +#define _mmw_andnot_epi32 _mm_andnot_si128 +#define _mmw_neg_epi32(a) _mm_sub_epi32(_mm_set1_epi32(0), (a)) +#define _mmw_add_epi32 _mm_add_epi32 +#define _mmw_sub_epi32 _mm_sub_epi32 +#define _mmw_subs_epu16 _mm_subs_epu16 +#define _mmw_cmpeq_epi32 _mm_cmpeq_epi32 +#define _mmw_cmpgt_epi32 _mm_cmpgt_epi32 +#define _mmw_srai_epi32 _mm_srai_epi32 +#define _mmw_srli_epi32 _mm_srli_epi32 +#define _mmw_slli_epi32 _mm_slli_epi32 +#define _mmw_cvtps_epi32 _mm_cvtps_epi32 +#define _mmw_cvttps_epi32 _mm_cvttps_epi32 + +#define _mmx_fmadd_ps _mmw_fmadd_ps +#define _mmx_max_epi32 _mmw_max_epi32 +#define _mmx_min_epi32 _mmw_min_epi32 + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SIMD casting functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE T simd_cast(Y A); +template<> FORCE_INLINE __m128 simd_cast<__m128>(float A) { return _mm_set1_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128i A) { return _mm_castsi128_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128 A) { return A; } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(int A) { return _mm_set1_epi32(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128 A) { return _mm_castps_si128(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128i A) { return A; } + +#define MAKE_ACCESSOR(name, simd_type, base_type, is_const, elements) \ + FORCE_INLINE is_const base_type * name(is_const simd_type &a) { \ + union accessor { simd_type m_native; base_type m_array[elements]; }; \ + is_const accessor *acs = reinterpret_cast(&a); \ + return acs->m_array; \ + } + +MAKE_ACCESSOR(simd_f32, __m128, float, , 4) +MAKE_ACCESSOR(simd_f32, __m128, float, const, 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, , 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, const, 4) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Specialized SSE input assembly function for general vertex gather +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +FORCE_INLINE void GatherVertices(__m128 *vtxX, __m128 *vtxY, __m128 *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes, const VertexLayout &vtxLayout) +{ + for (int lane = 0; lane < numLanes; lane++) + { + for (int i = 0; i < 3; i++) + { + char *vPtrX = (char *)inVtx + inTrisPtr[lane * 3 + i] * vtxLayout.mStride; + char *vPtrY = vPtrX + vtxLayout.mOffsetY; + char *vPtrW = vPtrX + vtxLayout.mOffsetW; + + simd_f32(vtxX[i])[lane] = *((float*)vPtrX); + simd_f32(vtxY[i])[lane] = *((float*)vPtrY); + simd_f32(vtxW[i])[lane] = *((float*)vPtrW); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SSE4.1 version +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace MaskedOcclusionCullingSSE41 +{ + FORCE_INLINE __m128i _mmw_mullo_epi32(const __m128i &a, const __m128i &b) { return _mm_mullo_epi32(a, b); } + FORCE_INLINE __m128i _mmw_min_epi32(const __m128i &a, const __m128i &b) { return _mm_min_epi32(a, b); } + FORCE_INLINE __m128i _mmw_max_epi32(const __m128i &a, const __m128i &b) { return _mm_max_epi32(a, b); } + FORCE_INLINE __m128i _mmw_abs_epi32(const __m128i &a) { return _mm_abs_epi32(a); } + FORCE_INLINE __m128 _mmw_blendv_ps(const __m128 &a, const __m128 &b, const __m128 &c) { return _mm_blendv_ps(a, b, c); } + FORCE_INLINE int _mmw_testz_epi32(const __m128i &a, const __m128i &b) { return _mm_testz_si128(a, b); } + FORCE_INLINE __m128 _mmx_dp4_ps(const __m128 &a, const __m128 &b) { return _mm_dp_ps(a, b, 0xFF); } + FORCE_INLINE __m128 _mmw_floor_ps(const __m128 &a) { return _mm_round_ps(a, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC); } + FORCE_INLINE __m128 _mmw_ceil_ps(const __m128 &a) { return _mm_round_ps(a, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC); } + FORCE_INLINE __m128i _mmw_transpose_epi8(const __m128i &a) + { + const __m128i shuff = _mm_setr_epi8(0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD, 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF); + return _mm_shuffle_epi8(a, shuff); + } + FORCE_INLINE __m128i _mmw_sllv_ones(const __m128i &ishift) + { + __m128i shift = _mm_min_epi32(ishift, _mm_set1_epi32(32)); + + // Uses lookup tables and _mm_shuffle_epi8 to perform _mm_sllv_epi32(~0, shift) + const __m128i byteShiftLUT = _mm_setr_epi8((char)0xFF, (char)0xFE, (char)0xFC, (char)0xF8, (char)0xF0, (char)0xE0, (char)0xC0, (char)0x80, 0, 0, 0, 0, 0, 0, 0, 0); + const __m128i byteShiftOffset = _mm_setr_epi8(0, 8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24); + const __m128i byteShiftShuffle = _mm_setr_epi8(0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x8, 0x8, 0x8, 0x8, 0xC, 0xC, 0xC, 0xC); + + __m128i byteShift = _mm_shuffle_epi8(shift, byteShiftShuffle); + byteShift = _mm_min_epi8(_mm_subs_epu8(byteShift, byteShiftOffset), _mm_set1_epi8(8)); + __m128i retMask = _mm_shuffle_epi8(byteShiftLUT, byteShift); + + return retMask; + } + + static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::SSE41; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Include common algorithm implementation (general, SIMD independent code) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + #include "MaskedOcclusionCullingCommon.inl" + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Utility function to create a new object using the allocator callbacks + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate)); + new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree); + return object; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SSE2 version +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace MaskedOcclusionCullingSSE2 +{ + FORCE_INLINE __m128i _mmw_mullo_epi32(const __m128i &a, const __m128i &b) + { + // Do products for even / odd lanes & merge the result + __m128i even = _mm_and_si128(_mm_mul_epu32(a, b), _mm_setr_epi32(~0, 0, ~0, 0)); + __m128i odd = _mm_slli_epi64(_mm_mul_epu32(_mm_srli_epi64(a, 32), _mm_srli_epi64(b, 32)), 32); + return _mm_or_si128(even, odd); + } + FORCE_INLINE __m128i _mmw_min_epi32(const __m128i &a, const __m128i &b) + { + __m128i cond = _mm_cmpgt_epi32(a, b); + return _mm_or_si128(_mm_andnot_si128(cond, a), _mm_and_si128(cond, b)); + } + FORCE_INLINE __m128i _mmw_max_epi32(const __m128i &a, const __m128i &b) + { + __m128i cond = _mm_cmpgt_epi32(b, a); + return _mm_or_si128(_mm_andnot_si128(cond, a), _mm_and_si128(cond, b)); + } + FORCE_INLINE __m128i _mmw_abs_epi32(const __m128i &a) + { + __m128i mask = _mm_cmplt_epi32(a, _mm_setzero_si128()); + return _mm_add_epi32(_mm_xor_si128(a, mask), _mm_srli_epi32(mask, 31)); + } + FORCE_INLINE int _mmw_testz_epi32(const __m128i &a, const __m128i &b) + { + return _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(a, b), _mm_setzero_si128())) == 0xFFFF; + } + FORCE_INLINE __m128 _mmw_blendv_ps(const __m128 &a, const __m128 &b, const __m128 &c) + { + __m128 cond = _mm_castsi128_ps(_mm_srai_epi32(_mm_castps_si128(c), 31)); + return _mm_or_ps(_mm_andnot_ps(cond, a), _mm_and_ps(cond, b)); + } + FORCE_INLINE __m128 _mmx_dp4_ps(const __m128 &a, const __m128 &b) + { + // Product and two shuffle/adds pairs (similar to hadd_ps) + __m128 prod = _mm_mul_ps(a, b); + __m128 dp = _mm_add_ps(prod, _mm_shuffle_ps(prod, prod, _MM_SHUFFLE(2, 3, 0, 1))); + dp = _mm_add_ps(dp, _mm_shuffle_ps(dp, dp, _MM_SHUFFLE(0, 1, 2, 3))); + return dp; + } + FORCE_INLINE __m128 _mmw_floor_ps(const __m128 &a) + { + int originalMode = _MM_GET_ROUNDING_MODE(); + _MM_SET_ROUNDING_MODE(_MM_ROUND_DOWN); + __m128 rounded = _mm_cvtepi32_ps(_mm_cvtps_epi32(a)); + _MM_SET_ROUNDING_MODE(originalMode); + return rounded; + } + FORCE_INLINE __m128 _mmw_ceil_ps(const __m128 &a) + { + int originalMode = _MM_GET_ROUNDING_MODE(); + _MM_SET_ROUNDING_MODE(_MM_ROUND_UP); + __m128 rounded = _mm_cvtepi32_ps(_mm_cvtps_epi32(a)); + _MM_SET_ROUNDING_MODE(originalMode); + return rounded; + } + FORCE_INLINE __m128i _mmw_transpose_epi8(const __m128i &a) + { + // Perform transpose through two 16->8 bit pack and byte shifts + __m128i res = a; + const __m128i mask = _mm_setr_epi8(~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0); + res = _mm_packus_epi16(_mm_and_si128(res, mask), _mm_srli_epi16(res, 8)); + res = _mm_packus_epi16(_mm_and_si128(res, mask), _mm_srli_epi16(res, 8)); + return res; + } + FORCE_INLINE __m128i _mmw_sllv_ones(const __m128i &ishift) + { + __m128i shift = _mmw_min_epi32(ishift, _mm_set1_epi32(32)); + + // Uses scalar approach to perform _mm_sllv_epi32(~0, shift) + static const unsigned int maskLUT[33] = { + ~0U << 0, ~0U << 1, ~0U << 2 , ~0U << 3, ~0U << 4, ~0U << 5, ~0U << 6 , ~0U << 7, ~0U << 8, ~0U << 9, ~0U << 10 , ~0U << 11, ~0U << 12, ~0U << 13, ~0U << 14 , ~0U << 15, + ~0U << 16, ~0U << 17, ~0U << 18 , ~0U << 19, ~0U << 20, ~0U << 21, ~0U << 22 , ~0U << 23, ~0U << 24, ~0U << 25, ~0U << 26 , ~0U << 27, ~0U << 28, ~0U << 29, ~0U << 30 , ~0U << 31, + 0U }; + + __m128i retMask; + simd_i32(retMask)[0] = (int)maskLUT[simd_i32(shift)[0]]; + simd_i32(retMask)[1] = (int)maskLUT[simd_i32(shift)[1]]; + simd_i32(retMask)[2] = (int)maskLUT[simd_i32(shift)[2]]; + simd_i32(retMask)[3] = (int)maskLUT[simd_i32(shift)[3]]; + return retMask; + } + + static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::SSE2; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Include common algorithm implementation (general, SIMD independent code) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + #include "MaskedOcclusionCullingCommon.inl" + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Utility function to create a new object using the allocator callbacks + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate)); + new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree); + return object; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Object construction and allocation +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +namespace MaskedOcclusionCullingAVX512 +{ + extern MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree); +} + +namespace MaskedOcclusionCullingAVX2 +{ + extern MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree); +} + +MaskedOcclusionCulling *MaskedOcclusionCulling::Create(Implementation RequestedSIMD) +{ + return Create(RequestedSIMD, aligned_alloc, aligned_free); +} + +MaskedOcclusionCulling *MaskedOcclusionCulling::Create(Implementation RequestedSIMD, pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) +{ + MaskedOcclusionCulling *object = nullptr; + + MaskedOcclusionCulling::Implementation impl = DetectCPUFeatures(alignedAlloc, alignedFree); + + if (RequestedSIMD < impl) + impl = RequestedSIMD; + + // Return best supported version + if (object == nullptr && impl >= AVX512) + object = MaskedOcclusionCullingAVX512::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use AVX512 version + if (object == nullptr && impl >= AVX2) + object = MaskedOcclusionCullingAVX2::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use AVX2 version + if (object == nullptr && impl >= SSE41) + object = MaskedOcclusionCullingSSE41::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use SSE4.1 version + if (object == nullptr) + object = MaskedOcclusionCullingSSE2::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use SSE2 (slow) version + + return object; +} + +void MaskedOcclusionCulling::Destroy(MaskedOcclusionCulling *moc) +{ + pfnAlignedFree alignedFreeCallback = moc->mAlignedFreeCallback; + moc->~MaskedOcclusionCulling(); + alignedFreeCallback(moc); +} diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h new file mode 100644 index 0000000000..4ace525887 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h @@ -0,0 +1,592 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// +#pragma once + +/*! + * \file MaskedOcclusionCulling.h + * \brief Masked Occlusion Culling + * + * General information + * - Input to all API functions are (x,y,w) clip-space coordinates (x positive left, y positive up, w positive away from camera). + * We entirely skip the z component and instead compute it as 1 / w, see next bullet. For TestRect the input is NDC (x/w, y/w). + * - We use a simple z = 1 / w transform, which is a bit faster than OGL/DX depth transforms. Thus, depth is REVERSED and z = 0 at + * the far plane and z = inf at w = 0. We also have to use a GREATER depth function, which explains why all the conservative + * tests will be reversed compared to what you might be used to (for example zMaxTri >= zMinBuffer is a visibility test) + * - We support different layouts for vertex data (basic AoS and SoA), but note that it's beneficial to store the position data + * as tightly in memory as possible to reduce cache misses. Big strides are bad, so it's beneficial to keep position as a separate + * stream (rather than bundled with attributes) or to keep a copy of the position data for the occlusion culling system. + * - The resolution width must be a multiple of 8 and height a multiple of 4. + * - The hierarchical Z buffer is stored OpenGL-style with the y axis pointing up. This includes the scissor box. + * - This code is only tested with Visual Studio 2015, but should hopefully be easy to port to other compilers. + */ + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Defines used to configure the implementation +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef QUICK_MASK +/*! + * Configure the algorithm used for updating and merging hierarchical z buffer entries. If QUICK_MASK + * is defined to 1, use the algorithm from the paper "Masked Software Occlusion Culling", which has good + * balance between performance and low leakage. If QUICK_MASK is defined to 0, use the algorithm from + * "Masked Depth Culling for Graphics Hardware" which has less leakage, but also lower performance. + */ +#define QUICK_MASK 1 + +#endif + +#ifndef USE_D3D +/*! + * Configures the library for use with Direct3D (default) or OpenGL rendering. This changes whether the + * screen space Y axis points downwards (D3D) or upwards (OGL), and is primarily important in combination + * with the PRECISE_COVERAGE define, where this is important to ensure correct rounding and tie-breaker + * behaviour. It also affects the ScissorRect screen space coordinates. + */ +#define USE_D3D 1 + +#endif + +#ifndef PRECISE_COVERAGE +/*! + * Define PRECISE_COVERAGE to 1 to more closely match GPU rasterization rules. The increased precision comes + * at a cost of slightly lower performance. + */ +#define PRECISE_COVERAGE 1 + +#endif + +#ifndef USE_AVX512 +/*! + * Define USE_AVX512 to 1 to enable experimental AVX-512 support. It's currently mostly untested and only + * validated on simple examples using Intel SDE. Older compilers may not support AVX-512 intrinsics. + */ +#define USE_AVX512 0 + +#endif + +#ifndef CLIPPING_PRESERVES_ORDER +/*! + * Define CLIPPING_PRESERVES_ORDER to 1 to prevent clipping from reordering triangle rasterization + * order; This comes at a cost (approx 3-4%) but removes one source of temporal frame-to-frame instability. + */ +#define CLIPPING_PRESERVES_ORDER 1 + +#endif + +#ifndef ENABLE_STATS +/*! + * Define ENABLE_STATS to 1 to gather various statistics during occlusion culling. Can be used for profiling + * and debugging. Note that enabling this function will reduce performance significantly. + */ +#define ENABLE_STATS 0 + +#endif + +#ifndef MOC_RECORDER_ENABLE +/*! + * Define MOC_RECORDER_ENABLE to 1 to enable frame recorder (see FrameRecorder.h/cpp for details) + */ +#define MOC_RECORDER_ENABLE 0 + +#endif + +#if MOC_RECORDER_ENABLE +#ifndef MOC_RECORDER_ENABLE_PLAYBACK +/*! + * Define MOC_RECORDER_ENABLE_PLAYBACK to 1 to enable compilation of the playback code (not needed + for recording) + */ +#define MOC_RECORDER_ENABLE_PLAYBACK 0 +#endif +#endif + + +#if MOC_RECORDER_ENABLE + +#include + +class FrameRecorder; + +#endif // #if MOC_RECORDER_ENABLE + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Masked occlusion culling class +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class MaskedOcclusionCulling +{ +public: + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Memory management callback functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + typedef void *(*pfnAlignedAlloc)(size_t alignment, size_t size); + typedef void (*pfnAlignedFree) (void *ptr); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Enums + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + enum Implementation + { + SSE2 = 0, + SSE41 = 1, + AVX2 = 2, + AVX512 = 3 + }; + + enum BackfaceWinding + { + BACKFACE_NONE = 0, + BACKFACE_CW = 1, + BACKFACE_CCW = 2, + }; + + enum CullingResult + { + VISIBLE = 0x0, + OCCLUDED = 0x1, + VIEW_CULLED = 0x3 + }; + + enum ClipPlanes + { + CLIP_PLANE_NONE = 0x00, + CLIP_PLANE_NEAR = 0x01, + CLIP_PLANE_LEFT = 0x02, + CLIP_PLANE_RIGHT = 0x04, + CLIP_PLANE_BOTTOM = 0x08, + CLIP_PLANE_TOP = 0x10, + CLIP_PLANE_SIDES = (CLIP_PLANE_LEFT | CLIP_PLANE_RIGHT | CLIP_PLANE_BOTTOM | CLIP_PLANE_TOP), + CLIP_PLANE_ALL = (CLIP_PLANE_LEFT | CLIP_PLANE_RIGHT | CLIP_PLANE_BOTTOM | CLIP_PLANE_TOP | CLIP_PLANE_NEAR) + }; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Structs + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /*! + * Used to specify custom vertex layout. Memory offsets to y and z coordinates are set through + * mOffsetY and mOffsetW, and vertex stride is given by mStride. It's possible to configure both + * AoS and SoA layouts. Note that large strides may cause more cache misses and decrease + * performance. It is advisable to store position data as compactly in memory as possible. + */ + struct VertexLayout + { + VertexLayout() {} + VertexLayout(int stride, int offsetY, int offsetZW) : + mStride(stride), mOffsetY(offsetY), mOffsetW(offsetZW) {} + + int mStride; //!< byte stride between vertices + int mOffsetY; //!< byte offset from X to Y coordinate + union { + int mOffsetZ; //!< byte offset from X to Z coordinate + int mOffsetW; //!< byte offset from X to W coordinate + }; + }; + + /*! + * Used to control scissoring during rasterization. Note that we only provide coarse scissor support. + * The scissor box x coordinates must be a multiple of 32, and the y coordinates a multiple of 8. + * Scissoring is mainly meant as a means of enabling binning (sort middle) rasterizers in case + * application developers want to use that approach for multithreading. + */ + struct ScissorRect + { + ScissorRect() {} + ScissorRect(int minX, int minY, int maxX, int maxY) : + mMinX(minX), mMinY(minY), mMaxX(maxX), mMaxY(maxY) {} + + int mMinX; //!< Screen space X coordinate for left side of scissor rect, inclusive and must be a multiple of 32 + int mMinY; //!< Screen space Y coordinate for bottom side of scissor rect, inclusive and must be a multiple of 8 + int mMaxX; //!< Screen space X coordinate for right side of scissor rect, non inclusive and must be a multiple of 32 + int mMaxY; //!< Screen space Y coordinate for top side of scissor rect, non inclusive and must be a multiple of 8 + }; + + /*! + * Used to specify storage area for a binlist, containing triangles. This struct is used for binning + * and multithreading. The host application is responsible for allocating memory for the binlists. + */ + struct TriList + { + unsigned int mNumTriangles; //!< Maximum number of triangles that may be stored in mPtr + unsigned int mTriIdx; //!< Index of next triangle to be written, clear before calling BinTriangles to start from the beginning of the list + float *mPtr; //!< Scratchpad buffer allocated by the host application + }; + + /*! + * Statistics that can be gathered during occluder rendering and visibility to aid debugging + * and profiling. Must be enabled by changing the ENABLE_STATS define. + */ + struct OcclusionCullingStatistics + { + struct + { + long long mNumProcessedTriangles; //!< Number of occluder triangles processed in total + long long mNumRasterizedTriangles; //!< Number of occluder triangles passing view frustum and backface culling + long long mNumTilesTraversed; //!< Number of tiles traversed by the rasterizer + long long mNumTilesUpdated; //!< Number of tiles where the hierarchical z buffer was updated + long long mNumTilesMerged; //!< Number of tiles where the hierarchical z buffer was updated + } mOccluders; + + struct + { + long long mNumProcessedRectangles; //!< Number of rects processed (TestRect()) + long long mNumProcessedTriangles; //!< Number of ocludee triangles processed (TestTriangles()) + long long mNumRasterizedTriangles; //!< Number of ocludee triangle passing view frustum and backface culling + long long mNumTilesTraversed; //!< Number of tiles traversed by triangle & rect rasterizers + } mOccludees; + }; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /*! + * \brief Creates a new object with default state, no z buffer attached/allocated. + */ + static MaskedOcclusionCulling *Create(Implementation RequestedSIMD = AVX512); + + /*! + * \brief Creates a new object with default state, no z buffer attached/allocated. + * \param alignedAlloc Pointer to a callback function used when allocating memory + * \param alignedFree Pointer to a callback function used when freeing memory + */ + static MaskedOcclusionCulling *Create(Implementation RequestedSIMD, pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree); + + /*! + * \brief Destroys an object and frees the z buffer memory. Note that you cannot + * use the delete operator, and should rather use this function to free up memory. + */ + static void Destroy(MaskedOcclusionCulling *moc); + + /*! + * \brief Sets the resolution of the hierarchical depth buffer. This function will + * re-allocate the current depth buffer (if present). The contents of the + * buffer is undefined until ClearBuffer() is called. + * + * \param witdh The width of the buffer in pixels, must be a multiple of 8 + * \param height The height of the buffer in pixels, must be a multiple of 4 + */ + virtual void SetResolution(unsigned int width, unsigned int height) = 0; + + /*! + * \brief Gets the resolution of the hierarchical depth buffer. + * + * \param witdh Output: The width of the buffer in pixels + * \param height Output: The height of the buffer in pixels + */ + virtual void GetResolution(unsigned int &width, unsigned int &height) const = 0; + + /*! + * \brief Returns the tile size for the current implementation. + * + * \param nBinsW Number of vertical bins, the screen is divided into nBinsW x nBinsH + * rectangular bins. + * \param nBinsH Number of horizontal bins, the screen is divided into nBinsW x nBinsH + * rectangular bins. + * \param outBinWidth Output: The width of the single bin in pixels (except for the + * rightmost bin width, which is extended to resolution width) + * \param outBinHeight Output: The height of the single bin in pixels (except for the + * bottommost bin height, which is extended to resolution height) + */ + virtual void ComputeBinWidthHeight(unsigned int nBinsW, unsigned int nBinsH, unsigned int & outBinWidth, unsigned int & outBinHeight) = 0; + + /*! + * \brief Sets the distance for the near clipping plane. Default is nearDist = 0. + * + * \param nearDist The distance to the near clipping plane, given as clip space w + */ + virtual void SetNearClipPlane(float nearDist) = 0; + + /*! + * \brief Gets the distance for the near clipping plane. + */ + virtual float GetNearClipPlane() const = 0; + + /*! + * \brief Clears the hierarchical depth buffer. + */ + virtual void ClearBuffer() = 0; + + /*! + * \brief Merge a second hierarchical depth buffer into the main buffer. + */ + virtual void MergeBuffer(MaskedOcclusionCulling* BufferB) = 0; + + /*! + * \brief Renders a mesh of occluder triangles and updates the hierarchical z buffer + * with conservative depth values. + * + * This function is optimized for vertex layouts with stride 16 and y and w + * offsets of 4 and 12 bytes, respectively. + * + * \param inVtx Pointer to an array of input vertices, should point to the x component + * of the first vertex. The input vertices are given as (x,y,w) coordinates + * in clip space. The memory layout can be changed using vtxLayout. + * \param inTris Pointer to an array of vertex indices. Each triangle is created + * from three indices consecutively fetched from the array. + * \param nTris The number of triangles to render (inTris must contain atleast 3*nTris + * entries) + * \param modelToClipMatrix all vertices will be transformed by this matrix before + * performing projection. If nullptr is passed the transform step will be skipped + * \param bfWinding Sets triangle winding order to consider backfacing, must be one one + * of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled + * and will not be rasterized. You may use BACKFACE_NONE to disable culling for + * double sided geometry + * \param clipPlaneMask A mask indicating which clip planes should be considered by the + * triangle clipper. Can be used as an optimization if your application can + * determine (for example during culling) that a group of triangles does not + * intersect a certain frustum plane. However, setting an incorrect mask may + * cause out of bounds memory accesses. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. + * \return Will return VIEW_CULLED if all triangles are either outside the frustum or + * backface culled, returns VISIBLE otherwise. + */ + virtual CullingResult RenderTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix = nullptr, BackfaceWinding bfWinding = BACKFACE_CW, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, const VertexLayout &vtxLayout = VertexLayout(16, 4, 12)) = 0; + + /*! + * \brief Occlusion query for a rectangle with a given depth. The rectangle is given + * in normalized device coordinates where (x,y) coordinates between [-1,1] map + * to the visible screen area. The query uses a GREATER_EQUAL (reversed) depth + * test meaning that depth values equal to the contents of the depth buffer are + * counted as visible. + * + * \param xmin NDC coordinate of the left side of the rectangle. + * \param ymin NDC coordinate of the bottom side of the rectangle. + * \param xmax NDC coordinate of the right side of the rectangle. + * \param ymax NDC coordinate of the top side of the rectangle. + * \param ymax NDC coordinate of the top side of the rectangle. + * \param wmin Clip space W coordinate for the rectangle. + * \return The query will return VISIBLE if the rectangle may be visible, OCCLUDED + * if the rectangle is occluded by a previously rendered object, or VIEW_CULLED + * if the rectangle is outside the view frustum. + */ + virtual CullingResult TestRect(float xmin, float ymin, float xmax, float ymax, float wmin) const = 0; + + /*! + * \brief This function is similar to RenderTriangles(), but performs an occlusion + * query instead and does not update the hierarchical z buffer. The query uses + * a GREATER_EQUAL (reversed) depth test meaning that depth values equal to the + * contents of the depth buffer are counted as visible. + * + * This function is optimized for vertex layouts with stride 16 and y and w + * offsets of 4 and 12 bytes, respectively. + * + * \param inVtx Pointer to an array of input vertices, should point to the x component + * of the first vertex. The input vertices are given as (x,y,w) coordinates + * in clip space. The memory layout can be changed using vtxLayout. + * \param inTris Pointer to an array of triangle indices. Each triangle is created + * from three indices consecutively fetched from the array. + * \param nTris The number of triangles to render (inTris must contain atleast 3*nTris + * entries) + * \param modelToClipMatrix all vertices will be transformed by this matrix before + * performing projection. If nullptr is passed the transform step will be skipped + * \param bfWinding Sets triangle winding order to consider backfacing, must be one one + * of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled + * and will not be occlusion tested. You may use BACKFACE_NONE to disable culling + * for double sided geometry + * \param clipPlaneMask A mask indicating which clip planes should be considered by the + * triangle clipper. Can be used as an optimization if your application can + * determine (for example during culling) that a group of triangles does not + * intersect a certain frustum plane. However, setting an incorrect mask may + * cause out of bounds memory accesses. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. + * \return The query will return VISIBLE if the triangle mesh may be visible, OCCLUDED + * if the mesh is occluded by a previously rendered object, or VIEW_CULLED if all + * triangles are entirely outside the view frustum or backface culled. + */ + virtual CullingResult TestTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix = nullptr, BackfaceWinding bfWinding = BACKFACE_CW, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, const VertexLayout &vtxLayout = VertexLayout(16, 4, 12)) = 0; + + /*! + * \brief Perform input assembly, clipping , projection, triangle setup, and write + * triangles to the screen space bins they overlap. This function can be used to + * distribute work for threading (See the CullingThreadpool class for an example) + * + * \param inVtx Pointer to an array of input vertices, should point to the x component + * of the first vertex. The input vertices are given as (x,y,w) coordinates + * in clip space. The memory layout can be changed using vtxLayout. + * \param inTris Pointer to an array of vertex indices. Each triangle is created + * from three indices consecutively fetched from the array. + * \param nTris The number of triangles to render (inTris must contain atleast 3*nTris + * entries) + * \param triLists Pointer to an array of TriList objects with one TriList object per + * bin. If a triangle overlaps a bin, it will be written to the corresponding + * trilist. Note that this method appends the triangles to the current list, to + * start writing from the beginning of the list, set triList.mTriIdx = 0 + * \param nBinsW Number of vertical bins, the screen is divided into nBinsW x nBinsH + * rectangular bins. + * \param nBinsH Number of horizontal bins, the screen is divided into nBinsW x nBinsH + * rectangular bins. + * \param modelToClipMatrix all vertices will be transformed by this matrix before + * performing projection. If nullptr is passed the transform step will be skipped + * \param clipPlaneMask A mask indicating which clip planes should be considered by the + * triangle clipper. Can be used as an optimization if your application can + * determine (for example during culling) that a group of triangles does not + * intersect a certain frustum plane. However, setting an incorrect mask may + * cause out of bounds memory accesses. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. + * \param bfWinding Sets triangle winding order to consider backfacing, must be one one + * of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled + * and will not be binned / rasterized. You may use BACKFACE_NONE to disable culling + * for double sided geometry + */ + virtual void BinTriangles(const float *inVtx, const unsigned int *inTris, int nTris, TriList *triLists, unsigned int nBinsW, unsigned int nBinsH, const float *modelToClipMatrix = nullptr, BackfaceWinding bfWinding = BACKFACE_CW, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, const VertexLayout &vtxLayout = VertexLayout(16, 4, 12)) = 0; + + /*! + * \brief Renders all occluder triangles in a trilist. This function can be used in + * combination with BinTriangles() to create a threded (binning) rasterizer. The + * bins can be processed independently by different threads without risking writing + * to overlapping memory regions. + * + * \param triLists A triangle list, filled using the BinTriangles() function that is to + * be rendered. + * \param scissor A scissor box limiting the rendering region to the bin. The size of each + * bin must be a multiple of 32x8 pixels due to implementation constraints. For a + * render target with (width, height) resolution and (nBinsW, nBinsH) bins, the + * size of a bin is: + * binWidth = (width / nBinsW) - (width / nBinsW) % 32; + * binHeight = (height / nBinsH) - (height / nBinsH) % 8; + * The last row and column of tiles have a different size: + * lastColBinWidth = width - (nBinsW-1)*binWidth; + * lastRowBinHeight = height - (nBinsH-1)*binHeight; + */ + virtual void RenderTrilist(const TriList &triList, const ScissorRect *scissor) = 0; + + /*! + * \brief Creates a per-pixel depth buffer from the hierarchical z buffer representation. + * Intended for visualizing the hierarchical depth buffer for debugging. The + * buffer is written in scanline order, from the top to bottom (D3D) or bottom to + * top (OGL) of the surface. See the USE_D3D define. + * + * \param depthData Pointer to memory where the per-pixel depth data is written. Must + * hold storage for atleast width*height elements as set by setResolution. + */ + virtual void ComputePixelDepthBuffer(float *depthData, bool flipY) = 0; + + /*! + * \brief Fetch occlusion culling statistics, returns zeroes if ENABLE_STATS define is + * not defined. The statistics can be used for profiling or debugging. + */ + virtual OcclusionCullingStatistics GetStatistics() = 0; + + /*! + * \brief Returns the implementation (CPU instruction set) version of this object. + */ + virtual Implementation GetImplementation() = 0; + + /*! + * \brief Utility function for transforming vertices and outputting them to an (x,y,z,w) + * format suitable for the occluder rasterization and occludee testing functions. + * + * \param mtx Pointer to matrix data. The matrix should column major for post + * multiplication (OGL) and row major for pre-multiplication (DX). This is + * consistent with OpenGL / DirectX behavior. + * \param inVtx Pointer to an array of input vertices. The input vertices are given as + * (x,y,z) coordinates. The memory layout can be changed using vtxLayout. + * \param xfVtx Pointer to an array to store transformed vertices. The transformed + * vertices are always stored as array of structs (AoS) (x,y,z,w) packed in memory. + * \param nVtx Number of vertices to transform. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. Note that for this function, the + * w-component is assumed to be 1.0. + */ + static void TransformVertices(const float *mtx, const float *inVtx, float *xfVtx, unsigned int nVtx, const VertexLayout &vtxLayout = VertexLayout(12, 4, 8)); + + /*! + * \brief Get used memory alloc/free callbacks. + */ + void GetAllocFreeCallback( pfnAlignedAlloc & allocCallback, pfnAlignedFree & freeCallback ) { allocCallback = mAlignedAllocCallback, freeCallback = mAlignedFreeCallback; } + +#if MOC_RECORDER_ENABLE + /*! + * \brief Start recording subsequent rasterization and testing calls using the FrameRecorder. + * The function calls that are recorded are: + * - ClearBuffer + * - RenderTriangles + * - TestTriangles + * - TestRect + * All inputs and outputs are recorded, which can be used for correctness validation + * and performance testing. + * + * \param outputFilePath Pointer to name of the output file. + * \return 'true' if recording was started successfully, 'false' otherwise (file access error). + */ + bool RecorderStart( const char * outputFilePath ) const; + + /*! + * \brief Stop recording, flush output and release used memory. + */ + void RecorderStop( ) const; + + /*! + * \brief Manually record triangles. This is called automatically from MaskedOcclusionCulling::RenderTriangles + * if the recording is started, but not from BinTriangles/RenderTrilist (used in multithreaded codepath), in + * which case it has to be called manually. + * + * \param inVtx Pointer to an array of input vertices, should point to the x component + * of the first vertex. The input vertices are given as (x,y,w) coordinates + * in clip space. The memory layout can be changed using vtxLayout. + * \param inTris Pointer to an array of triangle indices. Each triangle is created + * from three indices consecutively fetched from the array. + * \param nTris The number of triangles to render (inTris must contain atleast 3*nTris + * entries) + * \param modelToClipMatrix all vertices will be transformed by this matrix before + * performing projection. If nullptr is passed the transform step will be skipped + * \param bfWinding Sets triangle winding order to consider backfacing, must be one one + * of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled + * and will not be occlusion tested. You may use BACKFACE_NONE to disable culling + * for double sided geometry + * \param clipPlaneMask A mask indicating which clip planes should be considered by the + * triangle clipper. Can be used as an optimization if your application can + * determine (for example during culling) that a group of triangles does not + * intersect a certain frustum plane. However, setting an incorrect mask may + * cause out of bounds memory accesses. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. + * \param cullingResult cull result value expected to be returned by executing the + * RenderTriangles call with recorded parameters. + */ + // + // merge the binned data back into original layout; in this case, call it manually from your Threadpool implementation (already added to CullingThreadpool). + // If recording is not enabled, calling this function will do nothing. + void RecordRenderTriangles( const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix = nullptr, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, BackfaceWinding bfWinding = BACKFACE_CW, const VertexLayout &vtxLayout = VertexLayout( 16, 4, 12 ), CullingResult cullingResult = (CullingResult)-1 ); +#endif // #if MOC_RECORDER_ENABLE + +protected: + pfnAlignedAlloc mAlignedAllocCallback; + pfnAlignedFree mAlignedFreeCallback; + + mutable OcclusionCullingStatistics mStats; + +#if MOC_RECORDER_ENABLE + mutable FrameRecorder * mRecorder; + mutable std::mutex mRecorderMutex; +#endif // #if MOC_RECORDER_ENABLE + + virtual ~MaskedOcclusionCulling() {} +}; diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp new file mode 100644 index 0000000000..b129f12943 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp @@ -0,0 +1,243 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include "MaskedOcclusionCulling.h" +#include "CompilerSpecific.inl" + +#if MOC_RECORDER_ENABLE +#include "FrameRecorder.h" +#endif + +#if defined(__MICROSOFT_COMPILER) && _MSC_VER < 1900 + // If you remove/comment this error, the code will compile & use the SSE41 version instead. + #error Older versions than visual studio 2015 not supported due to compiler bug(s) +#endif + +#if !defined(__MICROSOFT_COMPILER) || _MSC_VER >= 1900 + +// For performance reasons, the MaskedOcclusionCullingAVX2.cpp file should be compiled with VEX encoding for SSE instructions (to avoid +// AVX-SSE transition penalties, see https://software.intel.com/en-us/articles/avoiding-avx-sse-transition-penalties). However, the SSE +// version in MaskedOcclusionCulling.cpp _must_ be compiled without VEX encoding to allow backwards compatibility. Best practice is to +// use lowest supported target platform (e.g. /arch:SSE2) as project default, and elevate only the MaskedOcclusionCullingAVX2/512.cpp files. +#ifndef __AVX2__ + #error For best performance, MaskedOcclusionCullingAVX2.cpp should be compiled with /arch:AVX2 +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// AVX specific defines and constants +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_LANES 8 +#define TILE_HEIGHT_SHIFT 3 + +#define SIMD_LANE_IDX _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7) + +#define SIMD_SUB_TILE_COL_OFFSET _mm256_setr_epi32(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET _mm256_setr_epi32(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT) +#define SIMD_SUB_TILE_COL_OFFSET_F _mm256_setr_ps(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET_F _mm256_setr_ps(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT) + +#define SIMD_SHUFFLE_SCANLINE_TO_SUBTILES _mm256_setr_epi8(0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD, 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF, 0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD, 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF) + +#define SIMD_LANE_YCOORD_I _mm256_setr_epi32(128, 384, 640, 896, 1152, 1408, 1664, 1920) +#define SIMD_LANE_YCOORD_F _mm256_setr_ps(128.0f, 384.0f, 640.0f, 896.0f, 1152.0f, 1408.0f, 1664.0f, 1920.0f) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// AVX specific typedefs and functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef __m256 __mw; +typedef __m256i __mwi; + +#define _mmw_set1_ps _mm256_set1_ps +#define _mmw_setzero_ps _mm256_setzero_ps +#define _mmw_and_ps _mm256_and_ps +#define _mmw_or_ps _mm256_or_ps +#define _mmw_xor_ps _mm256_xor_ps +#define _mmw_not_ps(a) _mm256_xor_ps((a), _mm256_castsi256_ps(_mm256_set1_epi32(~0))) +#define _mmw_andnot_ps _mm256_andnot_ps +#define _mmw_neg_ps(a) _mm256_xor_ps((a), _mm256_set1_ps(-0.0f)) +#define _mmw_abs_ps(a) _mm256_and_ps((a), _mm256_castsi256_ps(_mm256_set1_epi32(0x7FFFFFFF))) +#define _mmw_add_ps _mm256_add_ps +#define _mmw_sub_ps _mm256_sub_ps +#define _mmw_mul_ps _mm256_mul_ps +#define _mmw_div_ps _mm256_div_ps +#define _mmw_min_ps _mm256_min_ps +#define _mmw_max_ps _mm256_max_ps +#define _mmw_fmadd_ps _mm256_fmadd_ps +#define _mmw_fmsub_ps _mm256_fmsub_ps +#define _mmw_movemask_ps _mm256_movemask_ps +#define _mmw_blendv_ps _mm256_blendv_ps +#define _mmw_cmpge_ps(a,b) _mm256_cmp_ps(a, b, _CMP_GE_OQ) +#define _mmw_cmpgt_ps(a,b) _mm256_cmp_ps(a, b, _CMP_GT_OQ) +#define _mmw_cmpeq_ps(a,b) _mm256_cmp_ps(a, b, _CMP_EQ_OQ) +#define _mmw_floor_ps(x) _mm256_round_ps(x, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC) +#define _mmw_ceil_ps(x) _mm256_round_ps(x, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC) +#define _mmw_shuffle_ps _mm256_shuffle_ps +#define _mmw_insertf32x4_ps _mm256_insertf128_ps +#define _mmw_cvtepi32_ps _mm256_cvtepi32_ps +#define _mmw_blendv_epi32(a,b,c) simd_cast<__mwi>(_mmw_blendv_ps(simd_cast<__mw>(a), simd_cast<__mw>(b), simd_cast<__mw>(c))) + +#define _mmw_set1_epi32 _mm256_set1_epi32 +#define _mmw_setzero_epi32 _mm256_setzero_si256 +#define _mmw_and_epi32 _mm256_and_si256 +#define _mmw_or_epi32 _mm256_or_si256 +#define _mmw_xor_epi32 _mm256_xor_si256 +#define _mmw_not_epi32(a) _mm256_xor_si256((a), _mm256_set1_epi32(~0)) +#define _mmw_andnot_epi32 _mm256_andnot_si256 +#define _mmw_neg_epi32(a) _mm256_sub_epi32(_mm256_set1_epi32(0), (a)) +#define _mmw_add_epi32 _mm256_add_epi32 +#define _mmw_sub_epi32 _mm256_sub_epi32 +#define _mmw_min_epi32 _mm256_min_epi32 +#define _mmw_max_epi32 _mm256_max_epi32 +#define _mmw_subs_epu16 _mm256_subs_epu16 +#define _mmw_mullo_epi32 _mm256_mullo_epi32 +#define _mmw_cmpeq_epi32 _mm256_cmpeq_epi32 +#define _mmw_testz_epi32 _mm256_testz_si256 +#define _mmw_cmpgt_epi32 _mm256_cmpgt_epi32 +#define _mmw_srai_epi32 _mm256_srai_epi32 +#define _mmw_srli_epi32 _mm256_srli_epi32 +#define _mmw_slli_epi32 _mm256_slli_epi32 +#define _mmw_sllv_ones(x) _mm256_sllv_epi32(SIMD_BITS_ONE, x) +#define _mmw_transpose_epi8(x) _mm256_shuffle_epi8(x, SIMD_SHUFFLE_SCANLINE_TO_SUBTILES) +#define _mmw_abs_epi32 _mm256_abs_epi32 +#define _mmw_cvtps_epi32 _mm256_cvtps_epi32 +#define _mmw_cvttps_epi32 _mm256_cvttps_epi32 + +#define _mmx_dp4_ps(a, b) _mm_dp_ps(a, b, 0xFF) +#define _mmx_fmadd_ps _mm_fmadd_ps +#define _mmx_max_epi32 _mm_max_epi32 +#define _mmx_min_epi32 _mm_min_epi32 + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SIMD casting functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE T simd_cast(Y A); +template<> FORCE_INLINE __m128 simd_cast<__m128>(float A) { return _mm_set1_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128i A) { return _mm_castsi128_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128 A) { return A; } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(int A) { return _mm_set1_epi32(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128 A) { return _mm_castps_si128(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128i A) { return A; } +template<> FORCE_INLINE __m256 simd_cast<__m256>(float A) { return _mm256_set1_ps(A); } +template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256i A) { return _mm256_castsi256_ps(A); } +template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256 A) { return A; } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(int A) { return _mm256_set1_epi32(A); } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256 A) { return _mm256_castps_si256(A); } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256i A) { return A; } + +#define MAKE_ACCESSOR(name, simd_type, base_type, is_const, elements) \ + FORCE_INLINE is_const base_type * name(is_const simd_type &a) { \ + union accessor { simd_type m_native; base_type m_array[elements]; }; \ + is_const accessor *acs = reinterpret_cast(&a); \ + return acs->m_array; \ + } + +MAKE_ACCESSOR(simd_f32, __m128, float, , 4) +MAKE_ACCESSOR(simd_f32, __m128, float, const, 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, , 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, const, 4) + +MAKE_ACCESSOR(simd_f32, __m256, float, , 8) +MAKE_ACCESSOR(simd_f32, __m256, float, const, 8) +MAKE_ACCESSOR(simd_i32, __m256i, int, , 8) +MAKE_ACCESSOR(simd_i32, __m256i, int, const, 8) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Specialized AVX input assembly function for general vertex gather +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef MaskedOcclusionCulling::VertexLayout VertexLayout; + +FORCE_INLINE void GatherVertices(__m256 *vtxX, __m256 *vtxY, __m256 *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes, const VertexLayout &vtxLayout) +{ + assert(numLanes >= 1); + + const __m256i SIMD_TRI_IDX_OFFSET = _mm256_setr_epi32(0, 3, 6, 9, 12, 15, 18, 21); + static const __m256i SIMD_LANE_MASK[9] = { + _mm256_setr_epi32( 0, 0, 0, 0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, 0, 0, 0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, 0, 0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, 0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0) + }; + + // Compute per-lane index list offset that guards against out of bounds memory accesses + __m256i safeTriIdxOffset = _mm256_and_si256(SIMD_TRI_IDX_OFFSET, SIMD_LANE_MASK[numLanes]); + + // Fetch triangle indices. + __m256i vtxIdx[3]; + vtxIdx[0] = _mmw_mullo_epi32(_mm256_i32gather_epi32((const int*)inTrisPtr + 0, safeTriIdxOffset, 4), _mmw_set1_epi32(vtxLayout.mStride)); + vtxIdx[1] = _mmw_mullo_epi32(_mm256_i32gather_epi32((const int*)inTrisPtr + 1, safeTriIdxOffset, 4), _mmw_set1_epi32(vtxLayout.mStride)); + vtxIdx[2] = _mmw_mullo_epi32(_mm256_i32gather_epi32((const int*)inTrisPtr + 2, safeTriIdxOffset, 4), _mmw_set1_epi32(vtxLayout.mStride)); + + char *vPtr = (char *)inVtx; + + // Fetch triangle vertices + for (int i = 0; i < 3; i++) + { + vtxX[i] = _mm256_i32gather_ps((float *)vPtr, vtxIdx[i], 1); + vtxY[i] = _mm256_i32gather_ps((float *)(vPtr + vtxLayout.mOffsetY), vtxIdx[i], 1); + vtxW[i] = _mm256_i32gather_ps((float *)(vPtr + vtxLayout.mOffsetW), vtxIdx[i], 1); + } +} + +namespace MaskedOcclusionCullingAVX2 +{ + static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::AVX2; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Include common algorithm implementation (general, SIMD independent code) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + #include "MaskedOcclusionCullingCommon.inl" + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Utility function to create a new object using the allocator callbacks + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; + typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate)); + new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree); + return object; + } +}; + +#else + +namespace MaskedOcclusionCullingAVX2 +{ + typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; + typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + return nullptr; + } +}; + +#endif diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp new file mode 100644 index 0000000000..1dccccd83e --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp @@ -0,0 +1,309 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include "MaskedOcclusionCulling.h" +#include "CompilerSpecific.inl" + +#if MOC_RECORDER_ENABLE +#include "FrameRecorder.h" +#endif + +// Make sure compiler supports AVX-512 intrinsics: Visual Studio 2017 (Update 3) || Intel C++ Compiler 16.0 || Clang 4.0 || GCC 5.0 +#if USE_AVX512 != 0 && ((defined(_MSC_VER) && _MSC_VER >= 1911) || (defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1600) || (defined(__clang__) && __clang_major__ >= 4) || (defined(__GNUC__) && __GNUC__ >= 5)) + +// The MaskedOcclusionCullingAVX512.cpp file should be compiled avx2/avx512 architecture options turned on in the compiler. However, the SSE +// version in MaskedOcclusionCulling.cpp _must_ be compiled with SSE2 architecture allow backwards compatibility. Best practice is to +// use lowest supported target platform (e.g. /arch:SSE2) as project default, and elevate only the MaskedOcclusionCullingAVX2/512.cpp files. +#ifndef __AVX2__ + #error For best performance, MaskedOcclusionCullingAVX512.cpp should be compiled with /arch:AVX2 +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// AVX specific defines and constants +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_LANES 16 +#define TILE_HEIGHT_SHIFT 4 + +#define SIMD_LANE_IDX _mm512_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) + +#define SIMD_SUB_TILE_COL_OFFSET _mm512_setr_epi32(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET _mm512_setr_epi32(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3) +#define SIMD_SUB_TILE_COL_OFFSET_F _mm512_setr_ps(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET_F _mm512_setr_ps(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3) + +#define SIMD_SHUFFLE_SCANLINE_TO_SUBTILES _mm512_set_epi32(0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400, 0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400, 0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400, 0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400) + +#define SIMD_LANE_YCOORD_I _mm512_setr_epi32(128, 384, 640, 896, 1152, 1408, 1664, 1920, 2176, 2432, 2688, 2944, 3200, 3456, 3712, 3968) +#define SIMD_LANE_YCOORD_F _mm512_setr_ps(128.0f, 384.0f, 640.0f, 896.0f, 1152.0f, 1408.0f, 1664.0f, 1920.0f, 2176.0f, 2432.0f, 2688.0f, 2944.0f, 3200.0f, 3456.0f, 3712.0f, 3968.0f) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// AVX specific typedefs and functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef __m512 __mw; +typedef __m512i __mwi; + +#define _mmw_set1_ps _mm512_set1_ps +#define _mmw_setzero_ps _mm512_setzero_ps +#define _mmw_and_ps _mm512_and_ps +#define _mmw_or_ps _mm512_or_ps +#define _mmw_xor_ps _mm512_xor_ps +#define _mmw_not_ps(a) _mm512_xor_ps((a), _mm512_castsi512_ps(_mm512_set1_epi32(~0))) +#define _mmw_andnot_ps _mm512_andnot_ps +#define _mmw_neg_ps(a) _mm512_xor_ps((a), _mm512_set1_ps(-0.0f)) +#define _mmw_abs_ps(a) _mm512_and_ps((a), _mm512_castsi512_ps(_mm512_set1_epi32(0x7FFFFFFF))) +#define _mmw_add_ps _mm512_add_ps +#define _mmw_sub_ps _mm512_sub_ps +#define _mmw_mul_ps _mm512_mul_ps +#define _mmw_div_ps _mm512_div_ps +#define _mmw_min_ps _mm512_min_ps +#define _mmw_max_ps _mm512_max_ps +#define _mmw_fmadd_ps _mm512_fmadd_ps +#define _mmw_fmsub_ps _mm512_fmsub_ps +#define _mmw_shuffle_ps _mm512_shuffle_ps +#define _mmw_insertf32x4_ps _mm512_insertf32x4 +#define _mmw_cvtepi32_ps _mm512_cvtepi32_ps +#define _mmw_blendv_epi32(a,b,c) simd_cast<__mwi>(_mmw_blendv_ps(simd_cast<__mw>(a), simd_cast<__mw>(b), simd_cast<__mw>(c))) + +#define _mmw_set1_epi32 _mm512_set1_epi32 +#define _mmw_setzero_epi32 _mm512_setzero_si512 +#define _mmw_and_epi32 _mm512_and_si512 +#define _mmw_or_epi32 _mm512_or_si512 +#define _mmw_xor_epi32 _mm512_xor_si512 +#define _mmw_not_epi32(a) _mm512_xor_si512((a), _mm512_set1_epi32(~0)) +#define _mmw_andnot_epi32 _mm512_andnot_si512 +#define _mmw_neg_epi32(a) _mm512_sub_epi32(_mm512_set1_epi32(0), (a)) +#define _mmw_add_epi32 _mm512_add_epi32 +#define _mmw_sub_epi32 _mm512_sub_epi32 +#define _mmw_min_epi32 _mm512_min_epi32 +#define _mmw_max_epi32 _mm512_max_epi32 +#define _mmw_subs_epu16 _mm512_subs_epu16 +#define _mmw_mullo_epi32 _mm512_mullo_epi32 +#define _mmw_srai_epi32 _mm512_srai_epi32 +#define _mmw_srli_epi32 _mm512_srli_epi32 +#define _mmw_slli_epi32 _mm512_slli_epi32 +#define _mmw_sllv_ones(x) _mm512_sllv_epi32(SIMD_BITS_ONE, x) +#define _mmw_transpose_epi8(x) _mm512_shuffle_epi8(x, SIMD_SHUFFLE_SCANLINE_TO_SUBTILES) +#define _mmw_abs_epi32 _mm512_abs_epi32 +#define _mmw_cvtps_epi32 _mm512_cvtps_epi32 +#define _mmw_cvttps_epi32 _mm512_cvttps_epi32 + +#define _mmx_dp4_ps(a, b) _mm_dp_ps(a, b, 0xFF) +#define _mmx_fmadd_ps _mm_fmadd_ps +#define _mmx_max_epi32 _mm_max_epi32 +#define _mmx_min_epi32 _mm_min_epi32 + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SIMD casting functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE T simd_cast(Y A); +template<> FORCE_INLINE __m128 simd_cast<__m128>(float A) { return _mm_set1_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128i A) { return _mm_castsi128_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128 A) { return A; } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(int A) { return _mm_set1_epi32(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128 A) { return _mm_castps_si128(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128i A) { return A; } +template<> FORCE_INLINE __m256 simd_cast<__m256>(float A) { return _mm256_set1_ps(A); } +template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256i A) { return _mm256_castsi256_ps(A); } +template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256 A) { return A; } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(int A) { return _mm256_set1_epi32(A); } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256 A) { return _mm256_castps_si256(A); } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256i A) { return A; } +template<> FORCE_INLINE __m512 simd_cast<__m512>(float A) { return _mm512_set1_ps(A); } +template<> FORCE_INLINE __m512 simd_cast<__m512>(__m512i A) { return _mm512_castsi512_ps(A); } +template<> FORCE_INLINE __m512 simd_cast<__m512>(__m512 A) { return A; } +template<> FORCE_INLINE __m512i simd_cast<__m512i>(int A) { return _mm512_set1_epi32(A); } +template<> FORCE_INLINE __m512i simd_cast<__m512i>(__m512 A) { return _mm512_castps_si512(A); } +template<> FORCE_INLINE __m512i simd_cast<__m512i>(__m512i A) { return A; } + +#define MAKE_ACCESSOR(name, simd_type, base_type, is_const, elements) \ + FORCE_INLINE is_const base_type * name(is_const simd_type &a) { \ + union accessor { simd_type m_native; base_type m_array[elements]; }; \ + is_const accessor *acs = reinterpret_cast(&a); \ + return acs->m_array; \ + } + +MAKE_ACCESSOR(simd_f32, __m128, float, , 4) +MAKE_ACCESSOR(simd_f32, __m128, float, const, 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, , 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, const, 4) + +MAKE_ACCESSOR(simd_f32, __m256, float, , 8) +MAKE_ACCESSOR(simd_f32, __m256, float, const, 8) +MAKE_ACCESSOR(simd_i32, __m256i, int, , 8) +MAKE_ACCESSOR(simd_i32, __m256i, int, const, 8) + +MAKE_ACCESSOR(simd_f32, __m512, float, , 16) +MAKE_ACCESSOR(simd_f32, __m512, float, const, 16) +MAKE_ACCESSOR(simd_i32, __m512i, int, , 16) +MAKE_ACCESSOR(simd_i32, __m512i, int, const, 16) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Specialized AVX input assembly function for general vertex gather +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef MaskedOcclusionCulling::VertexLayout VertexLayout; + +FORCE_INLINE void GatherVertices(__m512 *vtxX, __m512 *vtxY, __m512 *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes, const VertexLayout &vtxLayout) +{ + assert(numLanes >= 1); + + const __m512i SIMD_TRI_IDX_OFFSET = _mm512_setr_epi32(0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45); + static const __m512i SIMD_LANE_MASK[17] = { + _mm512_setr_epi32( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0) + }; + + // Compute per-lane index list offset that guards against out of bounds memory accesses + __m512i safeTriIdxOffset = _mm512_and_si512(SIMD_TRI_IDX_OFFSET, SIMD_LANE_MASK[numLanes]); + + // Fetch triangle indices. + __m512i vtxIdx[3]; + vtxIdx[0] = _mmw_mullo_epi32(_mm512_i32gather_epi32(safeTriIdxOffset, (const int*)inTrisPtr + 0, 4), _mmw_set1_epi32(vtxLayout.mStride)); + vtxIdx[1] = _mmw_mullo_epi32(_mm512_i32gather_epi32(safeTriIdxOffset, (const int*)inTrisPtr + 1, 4), _mmw_set1_epi32(vtxLayout.mStride)); + vtxIdx[2] = _mmw_mullo_epi32(_mm512_i32gather_epi32(safeTriIdxOffset, (const int*)inTrisPtr + 2, 4), _mmw_set1_epi32(vtxLayout.mStride)); + + char *vPtr = (char *)inVtx; + + // Fetch triangle vertices + for (int i = 0; i < 3; i++) + { + vtxX[i] = _mm512_i32gather_ps(vtxIdx[i], (float *)vPtr, 1); + vtxY[i] = _mm512_i32gather_ps(vtxIdx[i], (float *)(vPtr + vtxLayout.mOffsetY), 1); + vtxW[i] = _mm512_i32gather_ps(vtxIdx[i], (float *)(vPtr + vtxLayout.mOffsetW), 1); + } +} + +namespace MaskedOcclusionCullingAVX512 +{ + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Poorly implemented functions. TODO: fix common (maskedOcclusionCullingCommon.inl) code to improve perf + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE __m512 _mmw_floor_ps(__m512 x) + { + return _mm512_roundscale_ps(x, 1); // 1 = floor + } + + FORCE_INLINE __m512 _mmw_ceil_ps(__m512 x) + { + return _mm512_roundscale_ps(x, 2); // 2 = ceil + } + + FORCE_INLINE __m512i _mmw_cmpeq_epi32(__m512i a, __m512i b) + { + __mmask16 mask = _mm512_cmpeq_epi32_mask(a, b); + return _mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0)); + } + + FORCE_INLINE __m512i _mmw_cmpgt_epi32(__m512i a, __m512i b) + { + __mmask16 mask = _mm512_cmpgt_epi32_mask(a, b); + return _mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0)); + } + + FORCE_INLINE bool _mmw_testz_epi32(__m512i a, __m512i b) + { + __mmask16 mask = _mm512_cmpeq_epi32_mask(_mm512_and_si512(a, b), _mm512_set1_epi32(0)); + return mask == 0xFFFF; + } + + FORCE_INLINE __m512 _mmw_cmpge_ps(__m512 a, __m512 b) + { + __mmask16 mask = _mm512_cmp_ps_mask(a, b, _CMP_GE_OQ); + return _mm512_castsi512_ps(_mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0))); + } + + FORCE_INLINE __m512 _mmw_cmpgt_ps(__m512 a, __m512 b) + { + __mmask16 mask = _mm512_cmp_ps_mask(a, b, _CMP_GT_OQ); + return _mm512_castsi512_ps(_mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0))); + } + + FORCE_INLINE __m512 _mmw_cmpeq_ps(__m512 a, __m512 b) + { + __mmask16 mask = _mm512_cmp_ps_mask(a, b, _CMP_EQ_OQ); + return _mm512_castsi512_ps(_mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0))); + } + + FORCE_INLINE __mmask16 _mmw_movemask_ps(const __m512 &a) + { + __mmask16 mask = _mm512_cmp_epi32_mask(_mm512_and_si512(_mm512_castps_si512(a), _mm512_set1_epi32(0x80000000)), _mm512_set1_epi32(0), 4); // a & 0x8000000 != 0 + return mask; + } + + FORCE_INLINE __m512 _mmw_blendv_ps(const __m512 &a, const __m512 &b, const __m512 &c) + { + __mmask16 mask = _mmw_movemask_ps(c); + return _mm512_mask_mov_ps(a, mask, b); + } + + static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::AVX512; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Include common algorithm implementation (general, SIMD independent code) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + #include "MaskedOcclusionCullingCommon.inl" + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Utility function to create a new object using the allocator callbacks + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; + typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate)); + new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree); + return object; + } +}; + +#else + +namespace MaskedOcclusionCullingAVX512 +{ + typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; + typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + return nullptr; + } +}; + +#endif diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl new file mode 100644 index 0000000000..331ca69964 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl @@ -0,0 +1,2053 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common SIMD math utility functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE T max(const T &a, const T &b) { return a > b ? a : b; } +template FORCE_INLINE T min(const T &a, const T &b) { return a < b ? a : b; } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common defines and constants +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_ALL_LANES_MASK ((1 << SIMD_LANES) - 1) + +// Tile dimensions are 32xN pixels. These values are not tweakable and the code must also be modified +// to support different tile sizes as it is tightly coupled with the SSE/AVX register size +#define TILE_WIDTH_SHIFT 5 +#define TILE_WIDTH (1 << TILE_WIDTH_SHIFT) +#define TILE_HEIGHT (1 << TILE_HEIGHT_SHIFT) + +// Sub-tiles (used for updating the masked HiZ buffer) are 8x4 tiles, so there are 4x2 sub-tiles in a tile +#define SUB_TILE_WIDTH 8 +#define SUB_TILE_HEIGHT 4 + +// The number of fixed point bits used to represent vertex coordinates / edge slopes. +#if PRECISE_COVERAGE != 0 + #define FP_BITS 8 + #define FP_HALF_PIXEL (1 << (FP_BITS - 1)) + #define FP_INV (1.0f / (float)(1 << FP_BITS)) +#else + // Note that too low precision, without precise coverage, may cause overshoots / false coverage during rasterization. + // This is configured for 14 bits for AVX512 and 16 bits for SSE. Max tile slope delta is roughly + // (screenWidth + 2*(GUARD_BAND_PIXEL_SIZE + 1)) * (2^FP_BITS * (TILE_HEIGHT + GUARD_BAND_PIXEL_SIZE + 1)) + // and must fit in 31 bits. With this config, max image resolution (width) is ~3272, so stay well clear of this limit. + #define FP_BITS (19 - TILE_HEIGHT_SHIFT) +#endif + +// Tile dimensions in fixed point coordinates +#define FP_TILE_HEIGHT_SHIFT (FP_BITS + TILE_HEIGHT_SHIFT) +#define FP_TILE_HEIGHT (1 << FP_TILE_HEIGHT_SHIFT) + +// Maximum number of triangles that may be generated during clipping. We process SIMD_LANES triangles at a time and +// clip against 5 planes, so the max should be 5*8 = 40 (we immediately draw the first clipped triangle). +// This number must be a power of two. +#define MAX_CLIPPED (8*SIMD_LANES) +#define MAX_CLIPPED_WRAP (MAX_CLIPPED - 1) + +// Size of guard band in pixels. Clipping doesn't seem to be very expensive so we use a small guard band +// to improve rasterization performance. It's not recommended to set the guard band to zero, as this may +// cause leakage along the screen border due to precision/rounding. +#define GUARD_BAND_PIXEL_SIZE 1.0f + +// We classify triangles as big if the bounding box is wider than this given threshold and use a tighter +// but slightly more expensive traversal algorithm. This improves performance greatly for sliver triangles +#define BIG_TRIANGLE 3 + +// Only gather statistics if enabled. +#if ENABLE_STATS != 0 + #define STATS_ADD(var, val) _InterlockedExchangeAdd64( &var, val ) +#else + #define STATS_ADD(var, val) +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SIMD common defines (constant values) +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_BITS_ONE _mmw_set1_epi32(~0) +#define SIMD_BITS_ZERO _mmw_setzero_epi32() +#define SIMD_TILE_WIDTH _mmw_set1_epi32(TILE_WIDTH) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Vertex fetch utility function, need to be in global namespace due to template specialization +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE void VtxFetch4(__mw *v, const unsigned int *inTrisPtr, int triVtx, const float *inVtx, int numLanes) +{ + // Fetch 4 vectors (matching 1 sse part of the SIMD register), and continue to the next + const int ssePart = (SIMD_LANES / 4) - N; + for (int k = 0; k < 4; k++) + { + int lane = 4 * ssePart + k; + if (numLanes > lane) + v[k] = _mmw_insertf32x4_ps(v[k], _mm_loadu_ps(&inVtx[inTrisPtr[lane * 3 + triVtx] << 2]), ssePart); + } + VtxFetch4(v, inTrisPtr, triVtx, inVtx, numLanes); +} + +template<> FORCE_INLINE void VtxFetch4<0>(__mw *v, const unsigned int *inTrisPtr, int triVtx, const float *inVtx, int numLanes) +{ + // Workaround for unused parameter warning + (void)v; (void)inTrisPtr; (void)triVtx; (void)inVtx; (void)numLanes; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Private class containing the implementation +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class MaskedOcclusionCullingPrivate : public MaskedOcclusionCulling +{ +public: + struct ZTile + { + __mw mZMin[2]; + __mwi mMask; + }; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Member variables + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + __mw mHalfWidth; + __mw mHalfHeight; + __mw mCenterX; + __mw mCenterY; + __m128 mCSFrustumPlanes[5]; + __m128 mIHalfSize; + __m128 mICenter; + __m128i mIScreenSize; + + float mNearDist; + int mWidth; + int mHeight; + int mTilesWidth; + int mTilesHeight; + + ZTile *mMaskedHiZBuffer; + ScissorRect mFullscreenScissor; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors and state handling + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MaskedOcclusionCullingPrivate(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) : mFullscreenScissor(0, 0, 0, 0) + { + mMaskedHiZBuffer = nullptr; + mAlignedAllocCallback = alignedAlloc; + mAlignedFreeCallback = alignedFree; +#if MOC_RECORDER_ENABLE + mRecorder = nullptr; +#endif + + SetNearClipPlane(0.0f); + mCSFrustumPlanes[0] = _mm_setr_ps(0.0f, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[1] = _mm_setr_ps(1.0f, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[2] = _mm_setr_ps(-1.0f, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[3] = _mm_setr_ps(0.0f, 1.0f, 1.0f, 0.0f); + mCSFrustumPlanes[4] = _mm_setr_ps(0.0f, -1.0f, 1.0f, 0.0f); + + memset(&mStats, 0, sizeof(OcclusionCullingStatistics)); + + SetResolution(0, 0); + } + + ~MaskedOcclusionCullingPrivate() override + { + if (mMaskedHiZBuffer != nullptr) + mAlignedFreeCallback(mMaskedHiZBuffer); + mMaskedHiZBuffer = nullptr; + +#if MOC_RECORDER_ENABLE + assert( mRecorder == nullptr ); // forgot to call StopRecording()? +#endif + } + + void SetResolution(unsigned int width, unsigned int height) override + { + // Resolution must be a multiple of the subtile size + assert(width % SUB_TILE_WIDTH == 0 && height % SUB_TILE_HEIGHT == 0); +#if PRECISE_COVERAGE == 0 + // Test if combination of resolution & SLOPE_FP_BITS bits may cause 32-bit overflow. Note that the maximum resolution estimate + // is only an estimate (not conservative). It's advicable to stay well below the limit. + assert(width < ((1U << 31) - 1U) / ((1U << FP_BITS) * (TILE_HEIGHT + (unsigned int)(GUARD_BAND_PIXEL_SIZE + 1.0f))) - (2U * (unsigned int)(GUARD_BAND_PIXEL_SIZE + 1.0f))); +#endif + + // Delete current masked hierarchical Z buffer + if (mMaskedHiZBuffer != nullptr) + mAlignedFreeCallback(mMaskedHiZBuffer); + mMaskedHiZBuffer = nullptr; + + // Setup various resolution dependent constant values + mWidth = (int)width; + mHeight = (int)height; + mTilesWidth = (int)(width + TILE_WIDTH - 1) >> TILE_WIDTH_SHIFT; + mTilesHeight = (int)(height + TILE_HEIGHT - 1) >> TILE_HEIGHT_SHIFT; + mCenterX = _mmw_set1_ps((float)mWidth * 0.5f); + mCenterY = _mmw_set1_ps((float)mHeight * 0.5f); + mICenter = _mm_setr_ps((float)mWidth * 0.5f, (float)mWidth * 0.5f, (float)mHeight * 0.5f, (float)mHeight * 0.5f); + mHalfWidth = _mmw_set1_ps((float)mWidth * 0.5f); +#if USE_D3D != 0 + mHalfHeight = _mmw_set1_ps((float)-mHeight * 0.5f); + mIHalfSize = _mm_setr_ps((float)mWidth * 0.5f, (float)mWidth * 0.5f, (float)-mHeight * 0.5f, (float)-mHeight * 0.5f); +#else + mHalfHeight = _mmw_set1_ps((float)mHeight * 0.5f); + mIHalfSize = _mm_setr_ps((float)mWidth * 0.5f, (float)mWidth * 0.5f, (float)mHeight * 0.5f, (float)mHeight * 0.5f); +#endif + mIScreenSize = _mm_setr_epi32(mWidth - 1, mWidth - 1, mHeight - 1, mHeight - 1); + + // Setup a full screen scissor rectangle + mFullscreenScissor.mMinX = 0; + mFullscreenScissor.mMinY = 0; + mFullscreenScissor.mMaxX = mTilesWidth << TILE_WIDTH_SHIFT; + mFullscreenScissor.mMaxY = mTilesHeight << TILE_HEIGHT_SHIFT; + + // Adjust clip planes to include a small guard band to avoid clipping leaks + if (mWidth > 0.0f && mHeight > 0.0f) + { + float guardBandWidth = (2.0f / (float)mWidth) * GUARD_BAND_PIXEL_SIZE; + float guardBandHeight = (2.0f / (float)mHeight) * GUARD_BAND_PIXEL_SIZE; + mCSFrustumPlanes[1] = _mm_setr_ps(1.0f - guardBandWidth, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[2] = _mm_setr_ps(-1.0f + guardBandWidth, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[3] = _mm_setr_ps(0.0f, 1.0f - guardBandHeight, 1.0f, 0.0f); + mCSFrustumPlanes[4] = _mm_setr_ps(0.0f, -1.0f + guardBandHeight, 1.0f, 0.0f); + } + + // Allocate masked hierarchical Z buffer (if zero size leave at nullptr) + if(mTilesWidth * mTilesHeight > 0) + mMaskedHiZBuffer = (ZTile *)mAlignedAllocCallback(64, sizeof(ZTile) * mTilesWidth * mTilesHeight); + } + + void GetResolution(unsigned int &width, unsigned int &height) const override + { + width = mWidth; + height = mHeight; + } + + void ComputeBinWidthHeight(unsigned int nBinsW, unsigned int nBinsH, unsigned int & outBinWidth, unsigned int & outBinHeight) override + { + outBinWidth = (mWidth / nBinsW) - ((mWidth / nBinsW) % TILE_WIDTH); + outBinHeight = (mHeight / nBinsH) - ((mHeight / nBinsH) % TILE_HEIGHT); + } + + void SetNearClipPlane(float nearDist) override + { + // Setup the near frustum plane + mNearDist = nearDist; + mCSFrustumPlanes[0] = _mm_setr_ps(0.0f, 0.0f, 1.0f, -nearDist); + } + + float GetNearClipPlane() const override + { + return mNearDist; + } + + void ClearBuffer() override + { + assert(mMaskedHiZBuffer != nullptr); + + // Iterate through all depth tiles and clear to default values + for (int i = 0; i < mTilesWidth * mTilesHeight; i++) + { + mMaskedHiZBuffer[i].mMask = _mmw_setzero_epi32(); + + // Clear z0 to beyond infinity to ensure we never merge with clear data + mMaskedHiZBuffer[i].mZMin[0] = _mmw_set1_ps(-1.0f); +#if QUICK_MASK != 0 + // Clear z1 to nearest depth value as it is pushed back on each update + mMaskedHiZBuffer[i].mZMin[1] = _mmw_set1_ps(FLT_MAX); +#else + mMaskedHiZBuffer[i].mZMin[1] = _mmw_setzero_ps(); +#endif + } + +#if ENABLE_STATS != 0 + memset(&mStats, 0, sizeof(OcclusionCullingStatistics)); +#endif + +#if MOC_RECORDER_ENABLE != 0 + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordClearBuffer(); + } +#endif + } + + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // MergeBuffer + // Utility Function merges another MOC buffer into the existing one + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void MergeBuffer(MaskedOcclusionCulling* BufferB) override + { + assert(mMaskedHiZBuffer != nullptr); + + //// Iterate through all depth tiles and merge the 2 tiles + for (int i = 0; i < mTilesWidth * mTilesHeight; i++) + { + __mw *zMinB = ((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mZMin; + __mw *zMinA = mMaskedHiZBuffer[i].mZMin; + __mwi RastMaskB = ((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask; + +#if QUICK_MASK != 0 + // Clear z0 to beyond infinity to ensure we never merge with clear data + __mwi sign0 = _mmw_srai_epi32(simd_cast<__mwi>(zMinB[0]), 31); + // Only merge tiles that have data in zMinB[0], use the sign bit to determine if they are still in a clear state + sign0 = _mmw_cmpeq_epi32(sign0, SIMD_BITS_ZERO); + if (!_mmw_testz_epi32(sign0, sign0)) + { + STATS_ADD(mStats.mOccluders.mNumTilesMerged, 1); + zMinA[0] = _mmw_max_ps(zMinA[0], zMinB[0]); + + __mwi rastMask = mMaskedHiZBuffer[i].mMask; + __mwi deadLane = _mmw_cmpeq_epi32(rastMask, SIMD_BITS_ZERO); + // Mask out all subtiles failing the depth test (don't update these subtiles) + deadLane = _mmw_or_epi32(deadLane, _mmw_srai_epi32(simd_cast<__mwi>(_mmw_sub_ps(zMinA[1], zMinA[0])), 31)); + mMaskedHiZBuffer[i].mMask = _mmw_andnot_epi32(deadLane, rastMask); + } + + // Set 32bit value to -1 if any pixels are set incide the coverage mask for a subtile + __mwi LiveTile = _mmw_cmpeq_epi32(RastMaskB, SIMD_BITS_ZERO); + // invert to have bits set for clear subtiles + __mwi t0inv = _mmw_not_epi32(LiveTile); + // VPTEST sets the ZF flag if all the resulting bits are 0 (ie if all tiles are clear) + if (!_mmw_testz_epi32(t0inv, t0inv)) + { + STATS_ADD(mStats.mOccluders.mNumTilesMerged, 1); + UpdateTileQuick(i, RastMaskB, zMinB[1]); + } +#else + // Clear z0 to beyond infinity to ensure we never merge with clear data + __mwi sign1 = _mmw_srai_epi32(simd_cast<__mwi>(mMaskedHiZBuffer[i].mZMin[0]), 31); + // Only merge tiles that have data in zMinB[0], use the sign bit to determine if they are still in a clear state + sign1 = _mmw_cmpeq_epi32(sign1, SIMD_BITS_ZERO); + + // Set 32bit value to -1 if any pixels are set incide the coverage mask for a subtile + __mwi LiveTile1 = _mmw_cmpeq_epi32(mMaskedHiZBuffer[i].mMask, SIMD_BITS_ZERO); + // invert to have bits set for clear subtiles + __mwi t1inv = _mmw_not_epi32(LiveTile1); + // VPTEST sets the ZF flag if all the resulting bits are 0 (ie if all tiles are clear) + if (_mmw_testz_epi32(sign1, sign1) && _mmw_testz_epi32(t1inv, t1inv)) + { + mMaskedHiZBuffer[i].mMask = ((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask; + mMaskedHiZBuffer[i].mZMin[0] = zMinB[0]; + mMaskedHiZBuffer[i].mZMin[1] = zMinB[1]; + } + else + { + // Clear z0 to beyond infinity to ensure we never merge with clear data + __mwi sign0 = _mmw_srai_epi32(simd_cast<__mwi>(zMinB[0]), 31); + sign0 = _mmw_cmpeq_epi32(sign0, SIMD_BITS_ZERO); + // Only merge tiles that have data in zMinB[0], use the sign bit to determine if they are still in a clear state + if (!_mmw_testz_epi32(sign0, sign0)) + { + // build a mask for Zmin[0], full if the layer has been completed, or partial if tile is still partly filled. + // cant just use the completement of the mask, as tiles might not get updated by merge + __mwi sign1 = _mmw_srai_epi32(simd_cast<__mwi>(zMinB[1]), 31); + __mwi LayerMask0 = _mmw_not_epi32(sign1); + __mwi LayerMask1 = _mmw_not_epi32(((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask); + __mwi rastMask = _mmw_or_epi32(LayerMask0, LayerMask1); + + UpdateTileAccurate(i, rastMask, zMinB[0]); + } + + // Set 32bit value to -1 if any pixels are set incide the coverage mask for a subtile + __mwi LiveTile = _mmw_cmpeq_epi32(((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask, SIMD_BITS_ZERO); + // invert to have bits set for clear subtiles + __mwi t0inv = _mmw_not_epi32(LiveTile); + // VPTEST sets the ZF flag if all the resulting bits are 0 (ie if all tiles are clear) + if (!_mmw_testz_epi32(t0inv, t0inv)) + { + UpdateTileAccurate(i, ((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask, zMinB[1]); + } + + //if (_mmw_testz_epi32(sign0, sign0) && _mmw_testz_epi32(t0inv, t0inv)) + // STATS_ADD(mStats.mOccluders.mNumTilesMerged, 1); + + } + +#endif + } + } + + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Polygon clipping functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE int ClipPolygon(__m128 *outVtx, __m128 *inVtx, const __m128 &plane, int n) const + { + __m128 p0 = inVtx[n - 1]; + __m128 dist0 = _mmx_dp4_ps(p0, plane); + + // Loop over all polygon edges and compute intersection with clip plane (if any) + int nout = 0; + for (int k = 0; k < n; k++) + { + __m128 p1 = inVtx[k]; + __m128 dist1 = _mmx_dp4_ps(p1, plane); + int dist0Neg = _mm_movemask_ps(dist0); + if (!dist0Neg) // dist0 > 0.0f + outVtx[nout++] = p0; + + // Edge intersects the clip plane if dist0 and dist1 have opposing signs + if (_mm_movemask_ps(_mm_xor_ps(dist0, dist1))) + { + // Always clip from the positive side to avoid T-junctions + if (!dist0Neg) + { + __m128 t = _mm_div_ps(dist0, _mm_sub_ps(dist0, dist1)); + outVtx[nout++] = _mmx_fmadd_ps(_mm_sub_ps(p1, p0), t, p0); + } + else + { + __m128 t = _mm_div_ps(dist1, _mm_sub_ps(dist1, dist0)); + outVtx[nout++] = _mmx_fmadd_ps(_mm_sub_ps(p0, p1), t, p1); + } + } + + dist0 = dist1; + p0 = p1; + } + return nout; + } + + template void TestClipPlane(__mw *vtxX, __mw *vtxY, __mw *vtxW, unsigned int &straddleMask, unsigned int &triMask, ClipPlanes clipPlaneMask) + { + straddleMask = 0; + // Skip masked clip planes + if (!(clipPlaneMask & CLIP_PLANE)) + return; + + // Evaluate all 3 vertices against the frustum plane + __mw planeDp[3]; + for (int i = 0; i < 3; ++i) + { + switch (CLIP_PLANE) + { + case ClipPlanes::CLIP_PLANE_LEFT: planeDp[i] = _mmw_add_ps(vtxW[i], vtxX[i]); break; + case ClipPlanes::CLIP_PLANE_RIGHT: planeDp[i] = _mmw_sub_ps(vtxW[i], vtxX[i]); break; + case ClipPlanes::CLIP_PLANE_BOTTOM: planeDp[i] = _mmw_add_ps(vtxW[i], vtxY[i]); break; + case ClipPlanes::CLIP_PLANE_TOP: planeDp[i] = _mmw_sub_ps(vtxW[i], vtxY[i]); break; + case ClipPlanes::CLIP_PLANE_NEAR: planeDp[i] = _mmw_sub_ps(vtxW[i], _mmw_set1_ps(mNearDist)); break; + } + } + + // Look at FP sign and determine if tri is inside, outside or straddles the frustum plane + __mw inside = _mmw_andnot_ps(planeDp[0], _mmw_andnot_ps(planeDp[1], _mmw_not_ps(planeDp[2]))); + __mw outside = _mmw_and_ps(planeDp[0], _mmw_and_ps(planeDp[1], planeDp[2])); + unsigned int inMask = (unsigned int)_mmw_movemask_ps(inside); + unsigned int outMask = (unsigned int)_mmw_movemask_ps(outside); + straddleMask = (~outMask) & (~inMask); + triMask &= ~outMask; + } + + FORCE_INLINE void ClipTriangleAndAddToBuffer(__mw *vtxX, __mw *vtxY, __mw *vtxW, __m128 *clippedTrisBuffer, int &clipWriteIdx, unsigned int &triMask, unsigned int triClipMask, ClipPlanes clipPlaneMask) + { + if (!triClipMask) + return; + + // Inside test all 3 triangle vertices against all active frustum planes + unsigned int straddleMask[5]; + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[0], triMask, clipPlaneMask); + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[1], triMask, clipPlaneMask); + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[2], triMask, clipPlaneMask); + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[3], triMask, clipPlaneMask); + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[4], triMask, clipPlaneMask); + + // Clip triangle against straddling planes and add to the clipped triangle buffer + __m128 vtxBuf[2][8]; + +#if CLIPPING_PRESERVES_ORDER != 0 + unsigned int clipMask = triClipMask & triMask; + unsigned int clipAndStraddleMask = (straddleMask[0] | straddleMask[1] | straddleMask[2] | straddleMask[3] | straddleMask[4]) & clipMask; + // no clipping needed after all - early out + if (clipAndStraddleMask == 0) + return; + while( clipMask ) + { + // Find and setup next triangle to clip + unsigned int triIdx = find_clear_lsb(&clipMask); + unsigned int triBit = (1U << triIdx); + assert(triIdx < SIMD_LANES); + + int bufIdx = 0; + int nClippedVerts = 3; + for (int i = 0; i < 3; i++) + vtxBuf[0][i] = _mm_setr_ps(simd_f32(vtxX[i])[triIdx], simd_f32(vtxY[i])[triIdx], simd_f32(vtxW[i])[triIdx], 1.0f); + + // Clip triangle with straddling planes. + for (int i = 0; i < 5; ++i) + { + if ((straddleMask[i] & triBit) && (clipPlaneMask & (1 << i))) // <- second part maybe not needed? + { + nClippedVerts = ClipPolygon(vtxBuf[bufIdx ^ 1], vtxBuf[bufIdx], mCSFrustumPlanes[i], nClippedVerts); + bufIdx ^= 1; + } + } + + if (nClippedVerts >= 3) + { + // Write all triangles into the clip buffer and process them next loop iteration + clippedTrisBuffer[clipWriteIdx * 3 + 0] = vtxBuf[bufIdx][0]; + clippedTrisBuffer[clipWriteIdx * 3 + 1] = vtxBuf[bufIdx][1]; + clippedTrisBuffer[clipWriteIdx * 3 + 2] = vtxBuf[bufIdx][2]; + clipWriteIdx = (clipWriteIdx + 1) & (MAX_CLIPPED - 1); + for (int i = 2; i < nClippedVerts - 1; i++) + { + clippedTrisBuffer[clipWriteIdx * 3 + 0] = vtxBuf[bufIdx][0]; + clippedTrisBuffer[clipWriteIdx * 3 + 1] = vtxBuf[bufIdx][i]; + clippedTrisBuffer[clipWriteIdx * 3 + 2] = vtxBuf[bufIdx][i + 1]; + clipWriteIdx = (clipWriteIdx + 1) & (MAX_CLIPPED - 1); + } + } + } + // since all triangles were copied to clip buffer for next iteration, skip further processing + triMask = 0; +#else + unsigned int clipMask = (straddleMask[0] | straddleMask[1] | straddleMask[2] | straddleMask[3] | straddleMask[4]) & (triClipMask & triMask); + while (clipMask) + { + // Find and setup next triangle to clip + unsigned int triIdx = find_clear_lsb(&clipMask); + unsigned int triBit = (1U << triIdx); + assert(triIdx < SIMD_LANES); + + int bufIdx = 0; + int nClippedVerts = 3; + for (int i = 0; i < 3; i++) + vtxBuf[0][i] = _mm_setr_ps(simd_f32(vtxX[i])[triIdx], simd_f32(vtxY[i])[triIdx], simd_f32(vtxW[i])[triIdx], 1.0f); + + // Clip triangle with straddling planes. + for (int i = 0; i < 5; ++i) + { + if ((straddleMask[i] & triBit) && (clipPlaneMask & (1 << i))) + { + nClippedVerts = ClipPolygon(vtxBuf[bufIdx ^ 1], vtxBuf[bufIdx], mCSFrustumPlanes[i], nClippedVerts); + bufIdx ^= 1; + } + } + + if (nClippedVerts >= 3) + { + // Write the first triangle back into the list of currently processed triangles + for (int i = 0; i < 3; i++) + { + simd_f32(vtxX[i])[triIdx] = simd_f32(vtxBuf[bufIdx][i])[0]; + simd_f32(vtxY[i])[triIdx] = simd_f32(vtxBuf[bufIdx][i])[1]; + simd_f32(vtxW[i])[triIdx] = simd_f32(vtxBuf[bufIdx][i])[2]; + } + // Write the remaining triangles into the clip buffer and process them next loop iteration + for (int i = 2; i < nClippedVerts - 1; i++) + { + clippedTrisBuffer[clipWriteIdx * 3 + 0] = vtxBuf[bufIdx][0]; + clippedTrisBuffer[clipWriteIdx * 3 + 1] = vtxBuf[bufIdx][i]; + clippedTrisBuffer[clipWriteIdx * 3 + 2] = vtxBuf[bufIdx][i + 1]; + clipWriteIdx = (clipWriteIdx + 1) & (MAX_CLIPPED - 1); + } + } + else // Kill triangles that was removed by clipping + triMask &= ~triBit; + } +#endif + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Vertex transform & projection + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE void TransformVerts(__mw *vtxX, __mw *vtxY, __mw *vtxW, const float *modelToClipMatrix) + { + if (modelToClipMatrix != nullptr) + { + for (int i = 0; i < 3; ++i) + { + __mw tmpX, tmpY, tmpW; + tmpX = _mmw_fmadd_ps(vtxX[i], _mmw_set1_ps(modelToClipMatrix[0]), _mmw_fmadd_ps(vtxY[i], _mmw_set1_ps(modelToClipMatrix[4]), _mmw_fmadd_ps(vtxW[i], _mmw_set1_ps(modelToClipMatrix[8]), _mmw_set1_ps(modelToClipMatrix[12])))); + tmpY = _mmw_fmadd_ps(vtxX[i], _mmw_set1_ps(modelToClipMatrix[1]), _mmw_fmadd_ps(vtxY[i], _mmw_set1_ps(modelToClipMatrix[5]), _mmw_fmadd_ps(vtxW[i], _mmw_set1_ps(modelToClipMatrix[9]), _mmw_set1_ps(modelToClipMatrix[13])))); + tmpW = _mmw_fmadd_ps(vtxX[i], _mmw_set1_ps(modelToClipMatrix[3]), _mmw_fmadd_ps(vtxY[i], _mmw_set1_ps(modelToClipMatrix[7]), _mmw_fmadd_ps(vtxW[i], _mmw_set1_ps(modelToClipMatrix[11]), _mmw_set1_ps(modelToClipMatrix[15])))); + vtxX[i] = tmpX; vtxY[i] = tmpY; vtxW[i] = tmpW; + } + } + } + +#if PRECISE_COVERAGE != 0 + FORCE_INLINE void ProjectVertices(__mwi *ipVtxX, __mwi *ipVtxY, __mw *pVtxX, __mw *pVtxY, __mw *pVtxZ, const __mw *vtxX, const __mw *vtxY, const __mw *vtxW) + { +#if USE_D3D != 0 + static const int vertexOrder[] = {2, 1, 0}; +#else + static const int vertexOrder[] = {0, 1, 2}; +#endif + + // Project vertices and transform to screen space. Snap to sub-pixel coordinates with FP_BITS precision. + for (int i = 0; i < 3; i++) + { + int idx = vertexOrder[i]; + __mw rcpW = _mmw_div_ps(_mmw_set1_ps(1.0f), vtxW[i]); + __mw screenX = _mmw_fmadd_ps(_mmw_mul_ps(vtxX[i], mHalfWidth), rcpW, mCenterX); + __mw screenY = _mmw_fmadd_ps(_mmw_mul_ps(vtxY[i], mHalfHeight), rcpW, mCenterY); + ipVtxX[idx] = _mmw_cvtps_epi32(_mmw_mul_ps(screenX, _mmw_set1_ps(float(1 << FP_BITS)))); + ipVtxY[idx] = _mmw_cvtps_epi32(_mmw_mul_ps(screenY, _mmw_set1_ps(float(1 << FP_BITS)))); + pVtxX[idx] = _mmw_mul_ps(_mmw_cvtepi32_ps(ipVtxX[idx]), _mmw_set1_ps(FP_INV)); + pVtxY[idx] = _mmw_mul_ps(_mmw_cvtepi32_ps(ipVtxY[idx]), _mmw_set1_ps(FP_INV)); + pVtxZ[idx] = rcpW; + } + } +#else + FORCE_INLINE void ProjectVertices(__mw *pVtxX, __mw *pVtxY, __mw *pVtxZ, const __mw *vtxX, const __mw *vtxY, const __mw *vtxW) + { +#if USE_D3D != 0 + static const int vertexOrder[] = {2, 1, 0}; +#else + static const int vertexOrder[] = {0, 1, 2}; +#endif + // Project vertices and transform to screen space. Round to nearest integer pixel coordinate + for (int i = 0; i < 3; i++) + { + int idx = vertexOrder[i]; + __mw rcpW = _mmw_div_ps(_mmw_set1_ps(1.0f), vtxW[i]); + + // The rounding modes are set to match HW rasterization with OpenGL. In practice our samples are placed + // in the (1,0) corner of each pixel, while HW rasterizer uses (0.5, 0.5). We get (1,0) because of the + // floor used when interpolating along triangle edges. The rounding modes match an offset of (0.5, -0.5) + pVtxX[idx] = _mmw_ceil_ps(_mmw_fmadd_ps(_mmw_mul_ps(vtxX[i], mHalfWidth), rcpW, mCenterX)); + pVtxY[idx] = _mmw_floor_ps(_mmw_fmadd_ps(_mmw_mul_ps(vtxY[i], mHalfHeight), rcpW, mCenterY)); + pVtxZ[idx] = rcpW; + } + } +#endif + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Common SSE/AVX input assembly functions, note that there are specialized gathers for the general case in the SSE/AVX specific files + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE void GatherVerticesFast(__mw *vtxX, __mw *vtxY, __mw *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes) + { + // This function assumes that the vertex layout is four packed x, y, z, w-values. + // Since the layout is known we can get some additional performance by using a + // more optimized gather strategy. + assert(numLanes >= 1); + + // Gather vertices + __mw v[4], swz[4]; + for (int i = 0; i < 3; i++) + { + // Load 4 (x,y,z,w) vectors per SSE part of the SIMD register (so 4 vectors for SSE, 8 vectors for AVX) + // this fetch uses templates to unroll the loop + VtxFetch4(v, inTrisPtr, i, inVtx, numLanes); + + // Transpose each individual SSE part of the SSE/AVX register (similar to _MM_TRANSPOSE4_PS) + swz[0] = _mmw_shuffle_ps(v[0], v[1], 0x44); + swz[2] = _mmw_shuffle_ps(v[0], v[1], 0xEE); + swz[1] = _mmw_shuffle_ps(v[2], v[3], 0x44); + swz[3] = _mmw_shuffle_ps(v[2], v[3], 0xEE); + + vtxX[i] = _mmw_shuffle_ps(swz[0], swz[1], 0x88); + vtxY[i] = _mmw_shuffle_ps(swz[0], swz[1], 0xDD); + vtxW[i] = _mmw_shuffle_ps(swz[2], swz[3], 0xDD); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Rasterization functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE void ComputeBoundingBox(__mwi &bbminX, __mwi &bbminY, __mwi &bbmaxX, __mwi &bbmaxY, const __mw *vX, const __mw *vY, const ScissorRect *scissor) + { + static const __mwi SIMD_PAD_W_MASK = _mmw_set1_epi32(~(TILE_WIDTH - 1)); + static const __mwi SIMD_PAD_H_MASK = _mmw_set1_epi32(~(TILE_HEIGHT - 1)); + + // Find Min/Max vertices + bbminX = _mmw_cvttps_epi32(_mmw_min_ps(vX[0], _mmw_min_ps(vX[1], vX[2]))); + bbminY = _mmw_cvttps_epi32(_mmw_min_ps(vY[0], _mmw_min_ps(vY[1], vY[2]))); + bbmaxX = _mmw_cvttps_epi32(_mmw_max_ps(vX[0], _mmw_max_ps(vX[1], vX[2]))); + bbmaxY = _mmw_cvttps_epi32(_mmw_max_ps(vY[0], _mmw_max_ps(vY[1], vY[2]))); + + // Clamp to tile boundaries + bbminX = _mmw_and_epi32(bbminX, SIMD_PAD_W_MASK); + bbmaxX = _mmw_and_epi32(_mmw_add_epi32(bbmaxX, _mmw_set1_epi32(TILE_WIDTH)), SIMD_PAD_W_MASK); + bbminY = _mmw_and_epi32(bbminY, SIMD_PAD_H_MASK); + bbmaxY = _mmw_and_epi32(_mmw_add_epi32(bbmaxY, _mmw_set1_epi32(TILE_HEIGHT)), SIMD_PAD_H_MASK); + + // Clip to scissor + bbminX = _mmw_max_epi32(bbminX, _mmw_set1_epi32(scissor->mMinX)); + bbmaxX = _mmw_min_epi32(bbmaxX, _mmw_set1_epi32(scissor->mMaxX)); + bbminY = _mmw_max_epi32(bbminY, _mmw_set1_epi32(scissor->mMinY)); + bbmaxY = _mmw_min_epi32(bbmaxY, _mmw_set1_epi32(scissor->mMaxY)); + } + +#if PRECISE_COVERAGE != 0 + FORCE_INLINE void SortVertices(__mwi *vX, __mwi *vY) + { + // Rotate the triangle in the winding order until v0 is the vertex with lowest Y value + for (int i = 0; i < 2; i++) + { + __mwi ey1 = _mmw_sub_epi32(vY[1], vY[0]); + __mwi ey2 = _mmw_sub_epi32(vY[2], vY[0]); + __mwi swapMask = _mmw_or_epi32(_mmw_or_epi32(ey1, ey2), _mmw_cmpeq_epi32(simd_cast<__mwi>(ey2), SIMD_BITS_ZERO)); + __mwi sX, sY; + sX = _mmw_blendv_epi32(vX[2], vX[0], swapMask); + vX[0] = _mmw_blendv_epi32(vX[0], vX[1], swapMask); + vX[1] = _mmw_blendv_epi32(vX[1], vX[2], swapMask); + vX[2] = sX; + sY = _mmw_blendv_epi32(vY[2], vY[0], swapMask); + vY[0] = _mmw_blendv_epi32(vY[0], vY[1], swapMask); + vY[1] = _mmw_blendv_epi32(vY[1], vY[2], swapMask); + vY[2] = sY; + } + } + + FORCE_INLINE int CullBackfaces(__mwi *ipVtxX, __mwi *ipVtxY, __mw *pVtxX, __mw *pVtxY, __mw *pVtxZ, const __mw &ccwMask, BackfaceWinding bfWinding) + { + // Reverse vertex order if non cw faces are considered front facing (rasterizer code requires CCW order) + if (!(bfWinding & BACKFACE_CW)) + { + __mw tmpX, tmpY, tmpZ; + __mwi itmpX, itmpY; + itmpX = _mmw_blendv_epi32(ipVtxX[2], ipVtxX[0], simd_cast<__mwi>(ccwMask)); + itmpY = _mmw_blendv_epi32(ipVtxY[2], ipVtxY[0], simd_cast<__mwi>(ccwMask)); + tmpX = _mmw_blendv_ps(pVtxX[2], pVtxX[0], ccwMask); + tmpY = _mmw_blendv_ps(pVtxY[2], pVtxY[0], ccwMask); + tmpZ = _mmw_blendv_ps(pVtxZ[2], pVtxZ[0], ccwMask); + ipVtxX[2] = _mmw_blendv_epi32(ipVtxX[0], ipVtxX[2], simd_cast<__mwi>(ccwMask)); + ipVtxY[2] = _mmw_blendv_epi32(ipVtxY[0], ipVtxY[2], simd_cast<__mwi>(ccwMask)); + pVtxX[2] = _mmw_blendv_ps(pVtxX[0], pVtxX[2], ccwMask); + pVtxY[2] = _mmw_blendv_ps(pVtxY[0], pVtxY[2], ccwMask); + pVtxZ[2] = _mmw_blendv_ps(pVtxZ[0], pVtxZ[2], ccwMask); + ipVtxX[0] = itmpX; + ipVtxY[0] = itmpY; + pVtxX[0] = tmpX; + pVtxY[0] = tmpY; + pVtxZ[0] = tmpZ; + } + + // Return a lane mask with all front faces set + return ((bfWinding & BACKFACE_CCW) ? 0 : _mmw_movemask_ps(ccwMask)) | ((bfWinding & BACKFACE_CW) ? 0 : ~_mmw_movemask_ps(ccwMask)); + } +#else + FORCE_INLINE void SortVertices(__mw *vX, __mw *vY) + { + // Rotate the triangle in the winding order until v0 is the vertex with lowest Y value + for (int i = 0; i < 2; i++) + { + __mw ey1 = _mmw_sub_ps(vY[1], vY[0]); + __mw ey2 = _mmw_sub_ps(vY[2], vY[0]); + __mw swapMask = _mmw_or_ps(_mmw_or_ps(ey1, ey2), simd_cast<__mw>(_mmw_cmpeq_epi32(simd_cast<__mwi>(ey2), SIMD_BITS_ZERO))); + __mw sX, sY; + sX = _mmw_blendv_ps(vX[2], vX[0], swapMask); + vX[0] = _mmw_blendv_ps(vX[0], vX[1], swapMask); + vX[1] = _mmw_blendv_ps(vX[1], vX[2], swapMask); + vX[2] = sX; + sY = _mmw_blendv_ps(vY[2], vY[0], swapMask); + vY[0] = _mmw_blendv_ps(vY[0], vY[1], swapMask); + vY[1] = _mmw_blendv_ps(vY[1], vY[2], swapMask); + vY[2] = sY; + } + } + + FORCE_INLINE int CullBackfaces(__mw *pVtxX, __mw *pVtxY, __mw *pVtxZ, const __mw &ccwMask, BackfaceWinding bfWinding) + { + // Reverse vertex order if non cw faces are considered front facing (rasterizer code requires CCW order) + if (!(bfWinding & BACKFACE_CW)) + { + __mw tmpX, tmpY, tmpZ; + tmpX = _mmw_blendv_ps(pVtxX[2], pVtxX[0], ccwMask); + tmpY = _mmw_blendv_ps(pVtxY[2], pVtxY[0], ccwMask); + tmpZ = _mmw_blendv_ps(pVtxZ[2], pVtxZ[0], ccwMask); + pVtxX[2] = _mmw_blendv_ps(pVtxX[0], pVtxX[2], ccwMask); + pVtxY[2] = _mmw_blendv_ps(pVtxY[0], pVtxY[2], ccwMask); + pVtxZ[2] = _mmw_blendv_ps(pVtxZ[0], pVtxZ[2], ccwMask); + pVtxX[0] = tmpX; + pVtxY[0] = tmpY; + pVtxZ[0] = tmpZ; + } + + // Return a lane mask with all front faces set + return ((bfWinding & BACKFACE_CCW) ? 0 : _mmw_movemask_ps(ccwMask)) | ((bfWinding & BACKFACE_CW) ? 0 : ~_mmw_movemask_ps(ccwMask)); + } +#endif + + FORCE_INLINE void ComputeDepthPlane(const __mw *pVtxX, const __mw *pVtxY, const __mw *pVtxZ, __mw &zPixelDx, __mw &zPixelDy) const + { + // Setup z(x,y) = z0 + dx*x + dy*y screen space depth plane equation + __mw x2 = _mmw_sub_ps(pVtxX[2], pVtxX[0]); + __mw x1 = _mmw_sub_ps(pVtxX[1], pVtxX[0]); + __mw y1 = _mmw_sub_ps(pVtxY[1], pVtxY[0]); + __mw y2 = _mmw_sub_ps(pVtxY[2], pVtxY[0]); + __mw z1 = _mmw_sub_ps(pVtxZ[1], pVtxZ[0]); + __mw z2 = _mmw_sub_ps(pVtxZ[2], pVtxZ[0]); + __mw d = _mmw_div_ps(_mmw_set1_ps(1.0f), _mmw_fmsub_ps(x1, y2, _mmw_mul_ps(y1, x2))); + zPixelDx = _mmw_mul_ps(_mmw_fmsub_ps(z1, y2, _mmw_mul_ps(y1, z2)), d); + zPixelDy = _mmw_mul_ps(_mmw_fmsub_ps(x1, z2, _mmw_mul_ps(z1, x2)), d); + } + + FORCE_INLINE void UpdateTileQuick(int tileIdx, const __mwi &coverage, const __mw &zTriv) + { + // Update heuristic used in the paper "Masked Software Occlusion Culling", + // good balance between performance and accuracy + STATS_ADD(mStats.mOccluders.mNumTilesUpdated, 1); + assert(tileIdx >= 0 && tileIdx < mTilesWidth*mTilesHeight); + + __mwi mask = mMaskedHiZBuffer[tileIdx].mMask; + __mw *zMin = mMaskedHiZBuffer[tileIdx].mZMin; + + // Swizzle coverage mask to 8x4 subtiles and test if any subtiles are not covered at all + __mwi rastMask = coverage; + __mwi deadLane = _mmw_cmpeq_epi32(rastMask, SIMD_BITS_ZERO); + + // Mask out all subtiles failing the depth test (don't update these subtiles) + deadLane = _mmw_or_epi32(deadLane, _mmw_srai_epi32(simd_cast<__mwi>(_mmw_sub_ps(zTriv, zMin[0])), 31)); + rastMask = _mmw_andnot_epi32(deadLane, rastMask); + + // Use distance heuristic to discard layer 1 if incoming triangle is significantly nearer to observer + // than the buffer contents. See Section 3.2 in "Masked Software Occlusion Culling" + __mwi coveredLane = _mmw_cmpeq_epi32(rastMask, SIMD_BITS_ONE); + __mw diff = _mmw_fmsub_ps(zMin[1], _mmw_set1_ps(2.0f), _mmw_add_ps(zTriv, zMin[0])); + __mwi discardLayerMask = _mmw_andnot_epi32(deadLane, _mmw_or_epi32(_mmw_srai_epi32(simd_cast<__mwi>(diff), 31), coveredLane)); + + // Update the mask with incoming triangle coverage + mask = _mmw_or_epi32(_mmw_andnot_epi32(discardLayerMask, mask), rastMask); + + __mwi maskFull = _mmw_cmpeq_epi32(mask, SIMD_BITS_ONE); + + // Compute new value for zMin[1]. This has one of four outcomes: zMin[1] = min(zMin[1], zTriv), zMin[1] = zTriv, + // zMin[1] = FLT_MAX or unchanged, depending on if the layer is updated, discarded, fully covered, or not updated + __mw opA = _mmw_blendv_ps(zTriv, zMin[1], simd_cast<__mw>(deadLane)); + __mw opB = _mmw_blendv_ps(zMin[1], zTriv, simd_cast<__mw>(discardLayerMask)); + __mw z1min = _mmw_min_ps(opA, opB); + zMin[1] = _mmw_blendv_ps(z1min, _mmw_set1_ps(FLT_MAX), simd_cast<__mw>(maskFull)); + + // Propagate zMin[1] back to zMin[0] if tile was fully covered, and update the mask + zMin[0] = _mmw_blendv_ps(zMin[0], z1min, simd_cast<__mw>(maskFull)); + mMaskedHiZBuffer[tileIdx].mMask = _mmw_andnot_epi32(maskFull, mask); + } + + FORCE_INLINE void UpdateTileAccurate(int tileIdx, const __mwi &coverage, const __mw &zTriv) + { + assert(tileIdx >= 0 && tileIdx < mTilesWidth*mTilesHeight); + + __mw *zMin = mMaskedHiZBuffer[tileIdx].mZMin; + __mwi &mask = mMaskedHiZBuffer[tileIdx].mMask; + + // Swizzle coverage mask to 8x4 subtiles + __mwi rastMask = coverage; + + // Perform individual depth tests with layer 0 & 1 and mask out all failing pixels + __mw sdist0 = _mmw_sub_ps(zMin[0], zTriv); + __mw sdist1 = _mmw_sub_ps(zMin[1], zTriv); + __mwi sign0 = _mmw_srai_epi32(simd_cast<__mwi>(sdist0), 31); + __mwi sign1 = _mmw_srai_epi32(simd_cast<__mwi>(sdist1), 31); + __mwi triMask = _mmw_and_epi32(rastMask, _mmw_or_epi32(_mmw_andnot_epi32(mask, sign0), _mmw_and_epi32(mask, sign1))); + + // Early out if no pixels survived the depth test (this test is more accurate than + // the early culling test in TraverseScanline()) + __mwi t0 = _mmw_cmpeq_epi32(triMask, SIMD_BITS_ZERO); + __mwi t0inv = _mmw_not_epi32(t0); + if (_mmw_testz_epi32(t0inv, t0inv)) + return; + + STATS_ADD(mStats.mOccluders.mNumTilesUpdated, 1); + + __mw zTri = _mmw_blendv_ps(zTriv, zMin[0], simd_cast<__mw>(t0)); + + // Test if incoming triangle completely overwrites layer 0 or 1 + __mwi layerMask0 = _mmw_andnot_epi32(triMask, _mmw_not_epi32(mask)); + __mwi layerMask1 = _mmw_andnot_epi32(triMask, mask); + __mwi lm0 = _mmw_cmpeq_epi32(layerMask0, SIMD_BITS_ZERO); + __mwi lm1 = _mmw_cmpeq_epi32(layerMask1, SIMD_BITS_ZERO); + __mw z0 = _mmw_blendv_ps(zMin[0], zTri, simd_cast<__mw>(lm0)); + __mw z1 = _mmw_blendv_ps(zMin[1], zTri, simd_cast<__mw>(lm1)); + + // Compute distances used for merging heuristic + __mw d0 = _mmw_abs_ps(sdist0); + __mw d1 = _mmw_abs_ps(sdist1); + __mw d2 = _mmw_abs_ps(_mmw_sub_ps(z0, z1)); + + // Find minimum distance + __mwi c01 = simd_cast<__mwi>(_mmw_sub_ps(d0, d1)); + __mwi c02 = simd_cast<__mwi>(_mmw_sub_ps(d0, d2)); + __mwi c12 = simd_cast<__mwi>(_mmw_sub_ps(d1, d2)); + // Two tests indicating which layer the incoming triangle will merge with or + // overwrite. d0min indicates that the triangle will overwrite layer 0, and + // d1min flags that the triangle will overwrite layer 1. + __mwi d0min = _mmw_or_epi32(_mmw_and_epi32(c01, c02), _mmw_or_epi32(lm0, t0)); + __mwi d1min = _mmw_andnot_epi32(d0min, _mmw_or_epi32(c12, lm1)); + + /////////////////////////////////////////////////////////////////////////////// + // Update depth buffer entry. NOTE: we always merge into layer 0, so if the + // triangle should be merged with layer 1, we first swap layer 0 & 1 and then + // merge into layer 0. + /////////////////////////////////////////////////////////////////////////////// + + // Update mask based on which layer the triangle overwrites or was merged into + __mw inner = _mmw_blendv_ps(simd_cast<__mw>(triMask), simd_cast<__mw>(layerMask1), simd_cast<__mw>(d0min)); + mask = simd_cast<__mwi>(_mmw_blendv_ps(inner, simd_cast<__mw>(layerMask0), simd_cast<__mw>(d1min))); + + // Update the zMin[0] value. There are four outcomes: overwrite with layer 1, + // merge with layer 1, merge with zTri or overwrite with layer 1 and then merge + // with zTri. + __mw e0 = _mmw_blendv_ps(z0, z1, simd_cast<__mw>(d1min)); + __mw e1 = _mmw_blendv_ps(z1, zTri, simd_cast<__mw>(_mmw_or_epi32(d1min, d0min))); + zMin[0] = _mmw_min_ps(e0, e1); + + // Update the zMin[1] value. There are three outcomes: keep current value, + // overwrite with zTri, or overwrite with z1 + __mw z1t = _mmw_blendv_ps(zTri, z1, simd_cast<__mw>(d0min)); + zMin[1] = _mmw_blendv_ps(z1t, z0, simd_cast<__mw>(d1min)); + } + + template + FORCE_INLINE int TraverseScanline(int leftOffset, int rightOffset, int tileIdx, int rightEvent, int leftEvent, const __mwi *events, const __mw &zTriMin, const __mw &zTriMax, const __mw &iz0, float zx) + { + // Floor edge events to integer pixel coordinates (shift out fixed point bits) + int eventOffset = leftOffset << TILE_WIDTH_SHIFT; + __mwi right[NRIGHT], left[NLEFT]; + for (int i = 0; i < NRIGHT; ++i) + right[i] = _mmw_max_epi32(_mmw_sub_epi32(_mmw_srai_epi32(events[rightEvent + i], FP_BITS), _mmw_set1_epi32(eventOffset)), SIMD_BITS_ZERO); + for (int i = 0; i < NLEFT; ++i) + left[i] = _mmw_max_epi32(_mmw_sub_epi32(_mmw_srai_epi32(events[leftEvent - i], FP_BITS), _mmw_set1_epi32(eventOffset)), SIMD_BITS_ZERO); + + __mw z0 = _mmw_add_ps(iz0, _mmw_set1_ps(zx*leftOffset)); + int tileIdxEnd = tileIdx + rightOffset; + tileIdx += leftOffset; + for (;;) + { + if (TEST_Z) + STATS_ADD(mStats.mOccludees.mNumTilesTraversed, 1); + else + STATS_ADD(mStats.mOccluders.mNumTilesTraversed, 1); + + // Perform a coarse test to quickly discard occluded tiles +#if QUICK_MASK != 0 + // Only use the reference layer (layer 0) to cull as it is always conservative + __mw zMinBuf = mMaskedHiZBuffer[tileIdx].mZMin[0]; +#else + // Compute zMin for the overlapped layers + __mwi mask = mMaskedHiZBuffer[tileIdx].mMask; + __mw zMin0 = _mmw_blendv_ps(mMaskedHiZBuffer[tileIdx].mZMin[0], mMaskedHiZBuffer[tileIdx].mZMin[1], simd_cast<__mw>(_mmw_cmpeq_epi32(mask, _mmw_set1_epi32(~0)))); + __mw zMin1 = _mmw_blendv_ps(mMaskedHiZBuffer[tileIdx].mZMin[1], mMaskedHiZBuffer[tileIdx].mZMin[0], simd_cast<__mw>(_mmw_cmpeq_epi32(mask, _mmw_setzero_epi32()))); + __mw zMinBuf = _mmw_min_ps(zMin0, zMin1); +#endif + __mw dist0 = _mmw_sub_ps(zTriMax, zMinBuf); + if (_mmw_movemask_ps(dist0) != SIMD_ALL_LANES_MASK) + { + // Compute coverage mask for entire 32xN using shift operations + __mwi accumulatedMask = _mmw_sllv_ones(left[0]); + for (int i = 1; i < NLEFT; ++i) + accumulatedMask = _mmw_and_epi32(accumulatedMask, _mmw_sllv_ones(left[i])); + for (int i = 0; i < NRIGHT; ++i) + accumulatedMask = _mmw_andnot_epi32(_mmw_sllv_ones(right[i]), accumulatedMask); + + if (TEST_Z) + { + // Perform a conservative visibility test (test zMax against buffer for each covered 8x4 subtile) + __mw zSubTileMax = _mmw_min_ps(z0, zTriMax); + __mwi zPass = simd_cast<__mwi>(_mmw_cmpge_ps(zSubTileMax, zMinBuf)); + + __mwi rastMask = _mmw_transpose_epi8(accumulatedMask); + __mwi deadLane = _mmw_cmpeq_epi32(rastMask, SIMD_BITS_ZERO); + zPass = _mmw_andnot_epi32(deadLane, zPass); + + if (!_mmw_testz_epi32(zPass, zPass)) + return CullingResult::VISIBLE; + } + else + { + // Compute interpolated min for each 8x4 subtile and update the masked hierarchical z buffer entry + __mw zSubTileMin = _mmw_max_ps(z0, zTriMin); +#if QUICK_MASK != 0 + UpdateTileQuick(tileIdx, _mmw_transpose_epi8(accumulatedMask), zSubTileMin); +#else + UpdateTileAccurate(tileIdx, _mmw_transpose_epi8(accumulatedMask), zSubTileMin); +#endif + } + } + + // Update buffer address, interpolate z and edge events + tileIdx++; + if (tileIdx >= tileIdxEnd) + break; + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zx)); + for (int i = 0; i < NRIGHT; ++i) + right[i] = _mmw_subs_epu16(right[i], SIMD_TILE_WIDTH); // Trick, use sub saturated to avoid checking against < 0 for shift (values should fit in 16 bits) + for (int i = 0; i < NLEFT; ++i) + left[i] = _mmw_subs_epu16(left[i], SIMD_TILE_WIDTH); + } + + return TEST_Z ? CullingResult::OCCLUDED : CullingResult::VISIBLE; + } + + + template +#if PRECISE_COVERAGE != 0 + FORCE_INLINE int RasterizeTriangle(unsigned int triIdx, int bbWidth, int tileRowIdx, int tileMidRowIdx, int tileEndRowIdx, const __mwi *eventStart, const __mw *slope, const __mwi *slopeTileDelta, const __mw &zTriMin, const __mw &zTriMax, __mw &z0, float zx, float zy, const __mwi *edgeY, const __mwi *absEdgeX, const __mwi *slopeSign, const __mwi *eventStartRemainder, const __mwi *slopeTileRemainder) +#else + FORCE_INLINE int RasterizeTriangle(unsigned int triIdx, int bbWidth, int tileRowIdx, int tileMidRowIdx, int tileEndRowIdx, const __mwi *eventStart, const __mwi *slope, const __mwi *slopeTileDelta, const __mw &zTriMin, const __mw &zTriMax, __mw &z0, float zx, float zy) +#endif + { + if (TEST_Z) + STATS_ADD(mStats.mOccludees.mNumRasterizedTriangles, 1); + else + STATS_ADD(mStats.mOccluders.mNumRasterizedTriangles, 1); + + int cullResult; + +#if PRECISE_COVERAGE != 0 + #define LEFT_EDGE_BIAS -1 + #define RIGHT_EDGE_BIAS 1 + #define UPDATE_TILE_EVENTS_Y(i) \ + triEventRemainder[i] = _mmw_sub_epi32(triEventRemainder[i], triSlopeTileRemainder[i]); \ + __mwi overflow##i = _mmw_srai_epi32(triEventRemainder[i], 31); \ + triEventRemainder[i] = _mmw_add_epi32(triEventRemainder[i], _mmw_and_epi32(overflow##i, triEdgeY[i])); \ + triEvent[i] = _mmw_add_epi32(triEvent[i], _mmw_add_epi32(triSlopeTileDelta[i], _mmw_and_epi32(overflow##i, triSlopeSign[i]))) + + __mwi triEvent[3], triSlopeSign[3], triSlopeTileDelta[3], triEdgeY[3], triSlopeTileRemainder[3], triEventRemainder[3]; + for (int i = 0; i < 3; ++i) + { + triSlopeSign[i] = _mmw_set1_epi32(simd_i32(slopeSign[i])[triIdx]); + triSlopeTileDelta[i] = _mmw_set1_epi32(simd_i32(slopeTileDelta[i])[triIdx]); + triEdgeY[i] = _mmw_set1_epi32(simd_i32(edgeY[i])[triIdx]); + triSlopeTileRemainder[i] = _mmw_set1_epi32(simd_i32(slopeTileRemainder[i])[triIdx]); + + __mw triSlope = _mmw_set1_ps(simd_f32(slope[i])[triIdx]); + __mwi triAbsEdgeX = _mmw_set1_epi32(simd_i32(absEdgeX[i])[triIdx]); + __mwi triStartRemainder = _mmw_set1_epi32(simd_i32(eventStartRemainder[i])[triIdx]); + __mwi triEventStart = _mmw_set1_epi32(simd_i32(eventStart[i])[triIdx]); + + __mwi scanlineDelta = _mmw_cvttps_epi32(_mmw_mul_ps(triSlope, SIMD_LANE_YCOORD_F)); + __mwi scanlineSlopeRemainder = _mmw_sub_epi32(_mmw_mullo_epi32(triAbsEdgeX, SIMD_LANE_YCOORD_I), _mmw_mullo_epi32(_mmw_abs_epi32(scanlineDelta), triEdgeY[i])); + + triEventRemainder[i] = _mmw_sub_epi32(triStartRemainder, scanlineSlopeRemainder); + __mwi overflow = _mmw_srai_epi32(triEventRemainder[i], 31); + triEventRemainder[i] = _mmw_add_epi32(triEventRemainder[i], _mmw_and_epi32(overflow, triEdgeY[i])); + triEvent[i] = _mmw_add_epi32(_mmw_add_epi32(triEventStart, scanlineDelta), _mmw_and_epi32(overflow, triSlopeSign[i])); + } + +#else + #define LEFT_EDGE_BIAS 0 + #define RIGHT_EDGE_BIAS 0 + #define UPDATE_TILE_EVENTS_Y(i) triEvent[i] = _mmw_add_epi32(triEvent[i], triSlopeTileDelta[i]); + + // Get deltas used to increment edge events each time we traverse one scanline of tiles + __mwi triSlopeTileDelta[3]; + triSlopeTileDelta[0] = _mmw_set1_epi32(simd_i32(slopeTileDelta[0])[triIdx]); + triSlopeTileDelta[1] = _mmw_set1_epi32(simd_i32(slopeTileDelta[1])[triIdx]); + triSlopeTileDelta[2] = _mmw_set1_epi32(simd_i32(slopeTileDelta[2])[triIdx]); + + // Setup edge events for first batch of SIMD_LANES scanlines + __mwi triEvent[3]; + triEvent[0] = _mmw_add_epi32(_mmw_set1_epi32(simd_i32(eventStart[0])[triIdx]), _mmw_mullo_epi32(SIMD_LANE_IDX, _mmw_set1_epi32(simd_i32(slope[0])[triIdx]))); + triEvent[1] = _mmw_add_epi32(_mmw_set1_epi32(simd_i32(eventStart[1])[triIdx]), _mmw_mullo_epi32(SIMD_LANE_IDX, _mmw_set1_epi32(simd_i32(slope[1])[triIdx]))); + triEvent[2] = _mmw_add_epi32(_mmw_set1_epi32(simd_i32(eventStart[2])[triIdx]), _mmw_mullo_epi32(SIMD_LANE_IDX, _mmw_set1_epi32(simd_i32(slope[2])[triIdx]))); +#endif + + // For big triangles track start & end tile for each scanline and only traverse the valid region + int startDelta, endDelta, topDelta, startEvent, endEvent, topEvent; + if (TIGHT_TRAVERSAL) + { + startDelta = simd_i32(slopeTileDelta[2])[triIdx] + LEFT_EDGE_BIAS; + endDelta = simd_i32(slopeTileDelta[0])[triIdx] + RIGHT_EDGE_BIAS; + topDelta = simd_i32(slopeTileDelta[1])[triIdx] + (MID_VTX_RIGHT ? RIGHT_EDGE_BIAS : LEFT_EDGE_BIAS); + + // Compute conservative bounds for the edge events over a 32xN tile + startEvent = simd_i32(eventStart[2])[triIdx] + min(0, startDelta); + endEvent = simd_i32(eventStart[0])[triIdx] + max(0, endDelta) + (TILE_WIDTH << FP_BITS); + if (MID_VTX_RIGHT) + topEvent = simd_i32(eventStart[1])[triIdx] + max(0, topDelta) + (TILE_WIDTH << FP_BITS); + else + topEvent = simd_i32(eventStart[1])[triIdx] + min(0, topDelta); + } + + if (tileRowIdx <= tileMidRowIdx) + { + int tileStopIdx = min(tileEndRowIdx, tileMidRowIdx); + // Traverse the bottom half of the triangle + while (tileRowIdx < tileStopIdx) + { + int start = 0, end = bbWidth; + if (TIGHT_TRAVERSAL) + { + // Compute tighter start and endpoints to avoid traversing empty space + start = max(0, min(bbWidth - 1, startEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + end = min(bbWidth, ((int)endEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + startEvent += startDelta; + endEvent += endDelta; + } + + // Traverse the scanline and update the masked hierarchical z buffer + cullResult = TraverseScanline(start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, z0, zx); + + if (TEST_Z && cullResult == CullingResult::VISIBLE) // Early out if performing occlusion query + return CullingResult::VISIBLE; + + // move to the next scanline of tiles, update edge events and interpolate z + tileRowIdx += mTilesWidth; + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zy)); + UPDATE_TILE_EVENTS_Y(0); + UPDATE_TILE_EVENTS_Y(2); + } + + // Traverse the middle scanline of tiles. We must consider all three edges only in this region + if (tileRowIdx < tileEndRowIdx) + { + int start = 0, end = bbWidth; + if (TIGHT_TRAVERSAL) + { + // Compute tighter start and endpoints to avoid traversing lots of empty space + start = max(0, min(bbWidth - 1, startEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + end = min(bbWidth, ((int)endEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + + // Switch the traversal start / end to account for the upper side edge + endEvent = MID_VTX_RIGHT ? topEvent : endEvent; + endDelta = MID_VTX_RIGHT ? topDelta : endDelta; + startEvent = MID_VTX_RIGHT ? startEvent : topEvent; + startDelta = MID_VTX_RIGHT ? startDelta : topDelta; + startEvent += startDelta; + endEvent += endDelta; + } + + // Traverse the scanline and update the masked hierarchical z buffer. + if (MID_VTX_RIGHT) + cullResult = TraverseScanline(start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, z0, zx); + else + cullResult = TraverseScanline(start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, z0, zx); + + if (TEST_Z && cullResult == CullingResult::VISIBLE) // Early out if performing occlusion query + return CullingResult::VISIBLE; + + tileRowIdx += mTilesWidth; + } + + // Traverse the top half of the triangle + if (tileRowIdx < tileEndRowIdx) + { + // move to the next scanline of tiles, update edge events and interpolate z + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zy)); + int i0 = MID_VTX_RIGHT + 0; + int i1 = MID_VTX_RIGHT + 1; + UPDATE_TILE_EVENTS_Y(i0); + UPDATE_TILE_EVENTS_Y(i1); + for (;;) + { + int start = 0, end = bbWidth; + if (TIGHT_TRAVERSAL) + { + // Compute tighter start and endpoints to avoid traversing lots of empty space + start = max(0, min(bbWidth - 1, startEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + end = min(bbWidth, ((int)endEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + startEvent += startDelta; + endEvent += endDelta; + } + + // Traverse the scanline and update the masked hierarchical z buffer + cullResult = TraverseScanline(start, end, tileRowIdx, MID_VTX_RIGHT + 0, MID_VTX_RIGHT + 1, triEvent, zTriMin, zTriMax, z0, zx); + + if (TEST_Z && cullResult == CullingResult::VISIBLE) // Early out if performing occlusion query + return CullingResult::VISIBLE; + + // move to the next scanline of tiles, update edge events and interpolate z + tileRowIdx += mTilesWidth; + if (tileRowIdx >= tileEndRowIdx) + break; + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zy)); + UPDATE_TILE_EVENTS_Y(i0); + UPDATE_TILE_EVENTS_Y(i1); + } + } + } + else + { + if (TIGHT_TRAVERSAL) + { + // For large triangles, switch the traversal start / end to account for the upper side edge + endEvent = MID_VTX_RIGHT ? topEvent : endEvent; + endDelta = MID_VTX_RIGHT ? topDelta : endDelta; + startEvent = MID_VTX_RIGHT ? startEvent : topEvent; + startDelta = MID_VTX_RIGHT ? startDelta : topDelta; + } + + // Traverse the top half of the triangle + if (tileRowIdx < tileEndRowIdx) + { + int i0 = MID_VTX_RIGHT + 0; + int i1 = MID_VTX_RIGHT + 1; + for (;;) + { + int start = 0, end = bbWidth; + if (TIGHT_TRAVERSAL) + { + // Compute tighter start and endpoints to avoid traversing lots of empty space + start = max(0, min(bbWidth - 1, startEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + end = min(bbWidth, ((int)endEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + startEvent += startDelta; + endEvent += endDelta; + } + + // Traverse the scanline and update the masked hierarchical z buffer + cullResult = TraverseScanline(start, end, tileRowIdx, MID_VTX_RIGHT + 0, MID_VTX_RIGHT + 1, triEvent, zTriMin, zTriMax, z0, zx); + + if (TEST_Z && cullResult == CullingResult::VISIBLE) // Early out if performing occlusion query + return CullingResult::VISIBLE; + + // move to the next scanline of tiles, update edge events and interpolate z + tileRowIdx += mTilesWidth; + if (tileRowIdx >= tileEndRowIdx) + break; + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zy)); + UPDATE_TILE_EVENTS_Y(i0); + UPDATE_TILE_EVENTS_Y(i1); + } + } + } + + return TEST_Z ? CullingResult::OCCLUDED : CullingResult::VISIBLE; + } + + template +#if PRECISE_COVERAGE != 0 + FORCE_INLINE int RasterizeTriangleBatch(__mwi ipVtxX[3], __mwi ipVtxY[3], __mw pVtxX[3], __mw pVtxY[3], __mw pVtxZ[3], unsigned int triMask, const ScissorRect *scissor) +#else + FORCE_INLINE int RasterizeTriangleBatch(__mw pVtxX[3], __mw pVtxY[3], __mw pVtxZ[3], unsigned int triMask, const ScissorRect *scissor) +#endif + { + int cullResult = CullingResult::VIEW_CULLED; + + ////////////////////////////////////////////////////////////////////////////// + // Compute bounding box and clamp to tile coordinates + ////////////////////////////////////////////////////////////////////////////// + + __mwi bbPixelMinX, bbPixelMinY, bbPixelMaxX, bbPixelMaxY; + ComputeBoundingBox(bbPixelMinX, bbPixelMinY, bbPixelMaxX, bbPixelMaxY, pVtxX, pVtxY, scissor); + + // Clamp bounding box to tiles (it's already padded in computeBoundingBox) + __mwi bbTileMinX = _mmw_srai_epi32(bbPixelMinX, TILE_WIDTH_SHIFT); + __mwi bbTileMinY = _mmw_srai_epi32(bbPixelMinY, TILE_HEIGHT_SHIFT); + __mwi bbTileMaxX = _mmw_srai_epi32(bbPixelMaxX, TILE_WIDTH_SHIFT); + __mwi bbTileMaxY = _mmw_srai_epi32(bbPixelMaxY, TILE_HEIGHT_SHIFT); + __mwi bbTileSizeX = _mmw_sub_epi32(bbTileMaxX, bbTileMinX); + __mwi bbTileSizeY = _mmw_sub_epi32(bbTileMaxY, bbTileMinY); + + // Cull triangles with zero bounding box + __mwi bboxSign = _mmw_or_epi32(_mmw_sub_epi32(bbTileSizeX, _mmw_set1_epi32(1)), _mmw_sub_epi32(bbTileSizeY, _mmw_set1_epi32(1))); + triMask &= ~_mmw_movemask_ps(simd_cast<__mw>(bboxSign)) & SIMD_ALL_LANES_MASK; + if (triMask == 0x0) + return cullResult; + + if (!TEST_Z) + cullResult = CullingResult::VISIBLE; + + ////////////////////////////////////////////////////////////////////////////// + // Set up screen space depth plane + ////////////////////////////////////////////////////////////////////////////// + + __mw zPixelDx, zPixelDy; + ComputeDepthPlane(pVtxX, pVtxY, pVtxZ, zPixelDx, zPixelDy); + + // Compute z value at min corner of bounding box. Offset to make sure z is conservative for all 8x4 subtiles + __mw bbMinXV0 = _mmw_sub_ps(_mmw_cvtepi32_ps(bbPixelMinX), pVtxX[0]); + __mw bbMinYV0 = _mmw_sub_ps(_mmw_cvtepi32_ps(bbPixelMinY), pVtxY[0]); + __mw zPlaneOffset = _mmw_fmadd_ps(zPixelDx, bbMinXV0, _mmw_fmadd_ps(zPixelDy, bbMinYV0, pVtxZ[0])); + __mw zTileDx = _mmw_mul_ps(zPixelDx, _mmw_set1_ps((float)TILE_WIDTH)); + __mw zTileDy = _mmw_mul_ps(zPixelDy, _mmw_set1_ps((float)TILE_HEIGHT)); + if (TEST_Z) + { + zPlaneOffset = _mmw_add_ps(zPlaneOffset, _mmw_max_ps(_mmw_setzero_ps(), _mmw_mul_ps(zPixelDx, _mmw_set1_ps(SUB_TILE_WIDTH)))); + zPlaneOffset = _mmw_add_ps(zPlaneOffset, _mmw_max_ps(_mmw_setzero_ps(), _mmw_mul_ps(zPixelDy, _mmw_set1_ps(SUB_TILE_HEIGHT)))); + } + else + { + zPlaneOffset = _mmw_add_ps(zPlaneOffset, _mmw_min_ps(_mmw_setzero_ps(), _mmw_mul_ps(zPixelDx, _mmw_set1_ps(SUB_TILE_WIDTH)))); + zPlaneOffset = _mmw_add_ps(zPlaneOffset, _mmw_min_ps(_mmw_setzero_ps(), _mmw_mul_ps(zPixelDy, _mmw_set1_ps(SUB_TILE_HEIGHT)))); + } + + // Compute Zmin and Zmax for the triangle (used to narrow the range for difficult tiles) + __mw zMin = _mmw_min_ps(pVtxZ[0], _mmw_min_ps(pVtxZ[1], pVtxZ[2])); + __mw zMax = _mmw_max_ps(pVtxZ[0], _mmw_max_ps(pVtxZ[1], pVtxZ[2])); + + ////////////////////////////////////////////////////////////////////////////// + // Sort vertices (v0 has lowest Y, and the rest is in winding order) and + // compute edges. Also find the middle vertex and compute tile + ////////////////////////////////////////////////////////////////////////////// + +#if PRECISE_COVERAGE != 0 + + // Rotate the triangle in the winding order until v0 is the vertex with lowest Y value + SortVertices(ipVtxX, ipVtxY); + + // Compute edges + __mwi edgeX[3] = { _mmw_sub_epi32(ipVtxX[1], ipVtxX[0]), _mmw_sub_epi32(ipVtxX[2], ipVtxX[1]), _mmw_sub_epi32(ipVtxX[2], ipVtxX[0]) }; + __mwi edgeY[3] = { _mmw_sub_epi32(ipVtxY[1], ipVtxY[0]), _mmw_sub_epi32(ipVtxY[2], ipVtxY[1]), _mmw_sub_epi32(ipVtxY[2], ipVtxY[0]) }; + + // Classify if the middle vertex is on the left or right and compute its position + int midVtxRight = ~_mmw_movemask_ps(simd_cast<__mw>(edgeY[1])); + __mwi midPixelX = _mmw_blendv_epi32(ipVtxX[1], ipVtxX[2], edgeY[1]); + __mwi midPixelY = _mmw_blendv_epi32(ipVtxY[1], ipVtxY[2], edgeY[1]); + __mwi midTileY = _mmw_srai_epi32(_mmw_max_epi32(midPixelY, SIMD_BITS_ZERO), TILE_HEIGHT_SHIFT + FP_BITS); + __mwi bbMidTileY = _mmw_max_epi32(bbTileMinY, _mmw_min_epi32(bbTileMaxY, midTileY)); + + // Compute edge events for the bottom of the bounding box, or for the middle tile in case of + // the edge originating from the middle vertex. + __mwi xDiffi[2], yDiffi[2]; + xDiffi[0] = _mmw_sub_epi32(ipVtxX[0], _mmw_slli_epi32(bbPixelMinX, FP_BITS)); + xDiffi[1] = _mmw_sub_epi32(midPixelX, _mmw_slli_epi32(bbPixelMinX, FP_BITS)); + yDiffi[0] = _mmw_sub_epi32(ipVtxY[0], _mmw_slli_epi32(bbPixelMinY, FP_BITS)); + yDiffi[1] = _mmw_sub_epi32(midPixelY, _mmw_slli_epi32(bbMidTileY, FP_BITS + TILE_HEIGHT_SHIFT)); + + ////////////////////////////////////////////////////////////////////////////// + // Edge slope setup - Note we do not conform to DX/GL rasterization rules + ////////////////////////////////////////////////////////////////////////////// + + // Potentially flip edge to ensure that all edges have positive Y slope. + edgeX[1] = _mmw_blendv_epi32(edgeX[1], _mmw_neg_epi32(edgeX[1]), edgeY[1]); + edgeY[1] = _mmw_abs_epi32(edgeY[1]); + + // Compute floating point slopes + __mw slope[3]; + slope[0] = _mmw_div_ps(_mmw_cvtepi32_ps(edgeX[0]), _mmw_cvtepi32_ps(edgeY[0])); + slope[1] = _mmw_div_ps(_mmw_cvtepi32_ps(edgeX[1]), _mmw_cvtepi32_ps(edgeY[1])); + slope[2] = _mmw_div_ps(_mmw_cvtepi32_ps(edgeX[2]), _mmw_cvtepi32_ps(edgeY[2])); + + // Modify slope of horizontal edges to make sure they mask out pixels above/below the edge. The slope is set to screen + // width to mask out all pixels above or below the horizontal edge. We must also add a small bias to acount for that + // vertices may end up off screen due to clipping. We're assuming that the round off error is no bigger than 1.0 + __mw horizontalSlopeDelta = _mmw_set1_ps(2.0f * ((float)mWidth + 2.0f*(GUARD_BAND_PIXEL_SIZE + 1.0f))); + __mwi horizontalSlope0 = _mmw_cmpeq_epi32(edgeY[0], _mmw_setzero_epi32()); + __mwi horizontalSlope1 = _mmw_cmpeq_epi32(edgeY[1], _mmw_setzero_epi32()); + slope[0] = _mmw_blendv_ps(slope[0], horizontalSlopeDelta, simd_cast<__mw>(horizontalSlope0)); + slope[1] = _mmw_blendv_ps(slope[1], _mmw_neg_ps(horizontalSlopeDelta), simd_cast<__mw>(horizontalSlope1)); + + __mwi vy[3] = { yDiffi[0], yDiffi[1], yDiffi[0] }; + __mwi offset0 = _mmw_and_epi32(_mmw_add_epi32(yDiffi[0], _mmw_set1_epi32(FP_HALF_PIXEL - 1)), _mmw_set1_epi32((int)((~0u) << FP_BITS))); + __mwi offset1 = _mmw_and_epi32(_mmw_add_epi32(yDiffi[1], _mmw_set1_epi32(FP_HALF_PIXEL - 1)), _mmw_set1_epi32((int)((~0u) << FP_BITS))); + vy[0] = _mmw_blendv_epi32(yDiffi[0], offset0, horizontalSlope0); + vy[1] = _mmw_blendv_epi32(yDiffi[1], offset1, horizontalSlope1); + + // Compute edge events for the bottom of the bounding box, or for the middle tile in case of + // the edge originating from the middle vertex. + __mwi slopeSign[3], absEdgeX[3]; + __mwi slopeTileDelta[3], eventStartRemainder[3], slopeTileRemainder[3], eventStart[3]; + for (int i = 0; i < 3; i++) + { + // Common, compute slope sign (used to propagate the remainder term when overflowing) is postive or negative x-direction + slopeSign[i] = _mmw_blendv_epi32(_mmw_set1_epi32(1), _mmw_set1_epi32(-1), edgeX[i]); + absEdgeX[i] = _mmw_abs_epi32(edgeX[i]); + + // Delta and error term for one vertical tile step. The exact delta is exactDelta = edgeX / edgeY, due to limited precision we + // repersent the delta as delta = qoutient + remainder / edgeY, where quotient = int(edgeX / edgeY). In this case, since we step + // one tile of scanlines at a time, the slope is computed for a tile-sized step. + slopeTileDelta[i] = _mmw_cvttps_epi32(_mmw_mul_ps(slope[i], _mmw_set1_ps(FP_TILE_HEIGHT))); + slopeTileRemainder[i] = _mmw_sub_epi32(_mmw_slli_epi32(absEdgeX[i], FP_TILE_HEIGHT_SHIFT), _mmw_mullo_epi32(_mmw_abs_epi32(slopeTileDelta[i]), edgeY[i])); + + // Jump to bottom scanline of tile row, this is the bottom of the bounding box, or the middle vertex of the triangle. + // The jump can be in both positive and negative y-direction due to clipping / offscreen vertices. + __mwi tileStartDir = _mmw_blendv_epi32(slopeSign[i], _mmw_neg_epi32(slopeSign[i]), vy[i]); + __mwi tieBreaker = _mmw_blendv_epi32(_mmw_set1_epi32(0), _mmw_set1_epi32(1), tileStartDir); + __mwi tileStartSlope = _mmw_cvttps_epi32(_mmw_mul_ps(slope[i], _mmw_cvtepi32_ps(_mmw_neg_epi32(vy[i])))); + __mwi tileStartRemainder = _mmw_sub_epi32(_mmw_mullo_epi32(absEdgeX[i], _mmw_abs_epi32(vy[i])), _mmw_mullo_epi32(_mmw_abs_epi32(tileStartSlope), edgeY[i])); + + eventStartRemainder[i] = _mmw_sub_epi32(tileStartRemainder, tieBreaker); + __mwi overflow = _mmw_srai_epi32(eventStartRemainder[i], 31); + eventStartRemainder[i] = _mmw_add_epi32(eventStartRemainder[i], _mmw_and_epi32(overflow, edgeY[i])); + eventStartRemainder[i] = _mmw_blendv_epi32(eventStartRemainder[i], _mmw_sub_epi32(_mmw_sub_epi32(edgeY[i], eventStartRemainder[i]), _mmw_set1_epi32(1)), vy[i]); + + //eventStart[i] = xDiffi[i & 1] + tileStartSlope + (overflow & tileStartDir) + _mmw_set1_epi32(FP_HALF_PIXEL - 1) + tieBreaker; + eventStart[i] = _mmw_add_epi32(_mmw_add_epi32(xDiffi[i & 1], tileStartSlope), _mmw_and_epi32(overflow, tileStartDir)); + eventStart[i] = _mmw_add_epi32(_mmw_add_epi32(eventStart[i], _mmw_set1_epi32(FP_HALF_PIXEL - 1)), tieBreaker); + } + +#else // PRECISE_COVERAGE + + SortVertices(pVtxX, pVtxY); + + // Compute edges + __mw edgeX[3] = { _mmw_sub_ps(pVtxX[1], pVtxX[0]), _mmw_sub_ps(pVtxX[2], pVtxX[1]), _mmw_sub_ps(pVtxX[2], pVtxX[0]) }; + __mw edgeY[3] = { _mmw_sub_ps(pVtxY[1], pVtxY[0]), _mmw_sub_ps(pVtxY[2], pVtxY[1]), _mmw_sub_ps(pVtxY[2], pVtxY[0]) }; + + // Classify if the middle vertex is on the left or right and compute its position + int midVtxRight = ~_mmw_movemask_ps(edgeY[1]); + __mw midPixelX = _mmw_blendv_ps(pVtxX[1], pVtxX[2], edgeY[1]); + __mw midPixelY = _mmw_blendv_ps(pVtxY[1], pVtxY[2], edgeY[1]); + __mwi midTileY = _mmw_srai_epi32(_mmw_max_epi32(_mmw_cvttps_epi32(midPixelY), SIMD_BITS_ZERO), TILE_HEIGHT_SHIFT); + __mwi bbMidTileY = _mmw_max_epi32(bbTileMinY, _mmw_min_epi32(bbTileMaxY, midTileY)); + + ////////////////////////////////////////////////////////////////////////////// + // Edge slope setup - Note we do not conform to DX/GL rasterization rules + ////////////////////////////////////////////////////////////////////////////// + + // Compute floating point slopes + __mw slope[3]; + slope[0] = _mmw_div_ps(edgeX[0], edgeY[0]); + slope[1] = _mmw_div_ps(edgeX[1], edgeY[1]); + slope[2] = _mmw_div_ps(edgeX[2], edgeY[2]); + + // Modify slope of horizontal edges to make sure they mask out pixels above/below the edge. The slope is set to screen + // width to mask out all pixels above or below the horizontal edge. We must also add a small bias to acount for that + // vertices may end up off screen due to clipping. We're assuming that the round off error is no bigger than 1.0 + __mw horizontalSlopeDelta = _mmw_set1_ps((float)mWidth + 2.0f*(GUARD_BAND_PIXEL_SIZE + 1.0f)); + slope[0] = _mmw_blendv_ps(slope[0], horizontalSlopeDelta, _mmw_cmpeq_ps(edgeY[0], _mmw_setzero_ps())); + slope[1] = _mmw_blendv_ps(slope[1], _mmw_neg_ps(horizontalSlopeDelta), _mmw_cmpeq_ps(edgeY[1], _mmw_setzero_ps())); + + // Convert floaing point slopes to fixed point + __mwi slopeFP[3]; + slopeFP[0] = _mmw_cvttps_epi32(_mmw_mul_ps(slope[0], _mmw_set1_ps(1 << FP_BITS))); + slopeFP[1] = _mmw_cvttps_epi32(_mmw_mul_ps(slope[1], _mmw_set1_ps(1 << FP_BITS))); + slopeFP[2] = _mmw_cvttps_epi32(_mmw_mul_ps(slope[2], _mmw_set1_ps(1 << FP_BITS))); + + // Fan out edge slopes to avoid (rare) cracks at vertices. We increase right facing slopes + // by 1 LSB, which results in overshooting vertices slightly, increasing triangle coverage. + // e0 is always right facing, e1 depends on if the middle vertex is on the left or right + slopeFP[0] = _mmw_add_epi32(slopeFP[0], _mmw_set1_epi32(1)); + slopeFP[1] = _mmw_add_epi32(slopeFP[1], _mmw_srli_epi32(_mmw_not_epi32(simd_cast<__mwi>(edgeY[1])), 31)); + + // Compute slope deltas for an SIMD_LANES scanline step (tile height) + __mwi slopeTileDelta[3]; + slopeTileDelta[0] = _mmw_slli_epi32(slopeFP[0], TILE_HEIGHT_SHIFT); + slopeTileDelta[1] = _mmw_slli_epi32(slopeFP[1], TILE_HEIGHT_SHIFT); + slopeTileDelta[2] = _mmw_slli_epi32(slopeFP[2], TILE_HEIGHT_SHIFT); + + // Compute edge events for the bottom of the bounding box, or for the middle tile in case of + // the edge originating from the middle vertex. + __mwi xDiffi[2], yDiffi[2]; + xDiffi[0] = _mmw_slli_epi32(_mmw_sub_epi32(_mmw_cvttps_epi32(pVtxX[0]), bbPixelMinX), FP_BITS); + xDiffi[1] = _mmw_slli_epi32(_mmw_sub_epi32(_mmw_cvttps_epi32(midPixelX), bbPixelMinX), FP_BITS); + yDiffi[0] = _mmw_sub_epi32(_mmw_cvttps_epi32(pVtxY[0]), bbPixelMinY); + yDiffi[1] = _mmw_sub_epi32(_mmw_cvttps_epi32(midPixelY), _mmw_slli_epi32(bbMidTileY, TILE_HEIGHT_SHIFT)); + + __mwi eventStart[3]; + eventStart[0] = _mmw_sub_epi32(xDiffi[0], _mmw_mullo_epi32(slopeFP[0], yDiffi[0])); + eventStart[1] = _mmw_sub_epi32(xDiffi[1], _mmw_mullo_epi32(slopeFP[1], yDiffi[1])); + eventStart[2] = _mmw_sub_epi32(xDiffi[0], _mmw_mullo_epi32(slopeFP[2], yDiffi[0])); +#endif + + ////////////////////////////////////////////////////////////////////////////// + // Split bounding box into bottom - middle - top region. + ////////////////////////////////////////////////////////////////////////////// + + __mwi bbBottomIdx = _mmw_add_epi32(bbTileMinX, _mmw_mullo_epi32(bbTileMinY, _mmw_set1_epi32(mTilesWidth))); + __mwi bbTopIdx = _mmw_add_epi32(bbTileMinX, _mmw_mullo_epi32(_mmw_add_epi32(bbTileMinY, bbTileSizeY), _mmw_set1_epi32(mTilesWidth))); + __mwi bbMidIdx = _mmw_add_epi32(bbTileMinX, _mmw_mullo_epi32(midTileY, _mmw_set1_epi32(mTilesWidth))); + + ////////////////////////////////////////////////////////////////////////////// + // Loop over non-culled triangle and change SIMD axis to per-pixel + ////////////////////////////////////////////////////////////////////////////// + while (triMask) + { + unsigned int triIdx = find_clear_lsb(&triMask); + int triMidVtxRight = (midVtxRight >> triIdx) & 1; + + // Get Triangle Zmin zMax + __mw zTriMax = _mmw_set1_ps(simd_f32(zMax)[triIdx]); + __mw zTriMin = _mmw_set1_ps(simd_f32(zMin)[triIdx]); + + // Setup Zmin value for first set of 8x4 subtiles + __mw z0 = _mmw_fmadd_ps(_mmw_set1_ps(simd_f32(zPixelDx)[triIdx]), SIMD_SUB_TILE_COL_OFFSET_F, + _mmw_fmadd_ps(_mmw_set1_ps(simd_f32(zPixelDy)[triIdx]), SIMD_SUB_TILE_ROW_OFFSET_F, _mmw_set1_ps(simd_f32(zPlaneOffset)[triIdx]))); + float zx = simd_f32(zTileDx)[triIdx]; + float zy = simd_f32(zTileDy)[triIdx]; + + // Get dimension of bounding box bottom, mid & top segments + int bbWidth = simd_i32(bbTileSizeX)[triIdx]; + int bbHeight = simd_i32(bbTileSizeY)[triIdx]; + int tileRowIdx = simd_i32(bbBottomIdx)[triIdx]; + int tileMidRowIdx = simd_i32(bbMidIdx)[triIdx]; + int tileEndRowIdx = simd_i32(bbTopIdx)[triIdx]; + + if (bbWidth > BIG_TRIANGLE && bbHeight > BIG_TRIANGLE) // For big triangles we use a more expensive but tighter traversal algorithm + { +#if PRECISE_COVERAGE != 0 + if (triMidVtxRight) + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); + else + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); +#else + if (triMidVtxRight) + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slopeFP, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy); + else + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slopeFP, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy); +#endif + } + else + { +#if PRECISE_COVERAGE != 0 + if (triMidVtxRight) + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); + else + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); +#else + if (triMidVtxRight) + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slopeFP, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy); + else + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slopeFP, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy); +#endif + } + + if (TEST_Z && cullResult == CullingResult::VISIBLE) + return CullingResult::VISIBLE; + } + + return cullResult; + } + + template + FORCE_INLINE CullingResult RenderTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) + { + assert(mMaskedHiZBuffer != nullptr); + + if (TEST_Z) + STATS_ADD(mStats.mOccludees.mNumProcessedTriangles, nTris); + else + STATS_ADD(mStats.mOccluders.mNumProcessedTriangles, nTris); + +#if PRECISE_COVERAGE != 0 + int originalRoundingMode = _MM_GET_ROUNDING_MODE(); + _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); +#endif + + int clipHead = 0; + int clipTail = 0; + __m128 clipTriBuffer[MAX_CLIPPED * 3]; + int cullResult = CullingResult::VIEW_CULLED; + + const unsigned int *inTrisPtr = inTris; + int numLanes = SIMD_LANES; + int triIndex = 0; + while (triIndex < nTris || clipHead != clipTail) + { + __mw vtxX[3], vtxY[3], vtxW[3]; + unsigned int triMask = SIMD_ALL_LANES_MASK; + + GatherTransformClip( clipHead, clipTail, numLanes, nTris, triIndex, vtxX, vtxY, vtxW, inVtx, inTrisPtr, vtxLayout, modelToClipMatrix, clipTriBuffer, triMask, clipPlaneMask ); + + if (triMask == 0x0) + continue; + + ////////////////////////////////////////////////////////////////////////////// + // Project, transform to screen space and perform backface culling. Note + // that we use z = 1.0 / vtx.w for depth, which means that z = 0 is far and + // z = 1 is near. We must also use a greater than depth test, and in effect + // everything is reversed compared to regular z implementations. + ////////////////////////////////////////////////////////////////////////////// + + __mw pVtxX[3], pVtxY[3], pVtxZ[3]; + +#if PRECISE_COVERAGE != 0 + __mwi ipVtxX[3], ipVtxY[3]; + ProjectVertices(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); +#else + ProjectVertices(pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); +#endif + + // Perform backface test. + __mw triArea1 = _mmw_mul_ps(_mmw_sub_ps(pVtxX[1], pVtxX[0]), _mmw_sub_ps(pVtxY[2], pVtxY[0])); + __mw triArea2 = _mmw_mul_ps(_mmw_sub_ps(pVtxX[0], pVtxX[2]), _mmw_sub_ps(pVtxY[0], pVtxY[1])); + __mw triArea = _mmw_sub_ps(triArea1, triArea2); + __mw ccwMask = _mmw_cmpgt_ps(triArea, _mmw_setzero_ps()); + +#if PRECISE_COVERAGE != 0 + triMask &= CullBackfaces(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, ccwMask, bfWinding); +#else + triMask &= CullBackfaces(pVtxX, pVtxY, pVtxZ, ccwMask, bfWinding); +#endif + + if (triMask == 0x0) + continue; + + ////////////////////////////////////////////////////////////////////////////// + // Setup and rasterize a SIMD batch of triangles + ////////////////////////////////////////////////////////////////////////////// +#if PRECISE_COVERAGE != 0 + cullResult &= RasterizeTriangleBatch(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, triMask, &mFullscreenScissor); +#else + cullResult &= RasterizeTriangleBatch(pVtxX, pVtxY, pVtxZ, triMask, &mFullscreenScissor); +#endif + + if (TEST_Z && cullResult == CullingResult::VISIBLE) { +#if PRECISE_COVERAGE != 0 + _MM_SET_ROUNDING_MODE(originalRoundingMode); +#endif + return CullingResult::VISIBLE; + } + } + +#if PRECISE_COVERAGE != 0 + _MM_SET_ROUNDING_MODE(originalRoundingMode); +#endif + return (CullingResult)cullResult; + } + + CullingResult RenderTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) override + { + CullingResult retVal; + + if (vtxLayout.mStride == 16 && vtxLayout.mOffsetY == 4 && vtxLayout.mOffsetW == 12) + retVal = (CullingResult)RenderTriangles<0, 1>(inVtx, inTris, nTris, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + else + retVal = (CullingResult)RenderTriangles<0, 0>(inVtx, inTris, nTris, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + +#if MOC_RECORDER_ENABLE + RecordRenderTriangles( inVtx, inTris, nTris, modelToClipMatrix, clipPlaneMask, bfWinding, vtxLayout, retVal ); +#endif + return retVal; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Occlusion query functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + CullingResult TestTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) override + { + CullingResult retVal; + + if (vtxLayout.mStride == 16 && vtxLayout.mOffsetY == 4 && vtxLayout.mOffsetW == 12) + retVal = (CullingResult)RenderTriangles<1, 1>(inVtx, inTris, nTris, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + else + retVal = (CullingResult)RenderTriangles<1, 0>(inVtx, inTris, nTris, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + +#if MOC_RECORDER_ENABLE + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordTestTriangles( retVal, inVtx, inTris, nTris, modelToClipMatrix, clipPlaneMask, bfWinding, vtxLayout ); + } +#endif + return retVal; + } + + CullingResult TestRect( float xmin, float ymin, float xmax, float ymax, float wmin ) const override + { + STATS_ADD(mStats.mOccludees.mNumProcessedRectangles, 1); + assert(mMaskedHiZBuffer != nullptr); + + static const __m128i SIMD_TILE_PAD = _mm_setr_epi32(0, TILE_WIDTH, 0, TILE_HEIGHT); + static const __m128i SIMD_TILE_PAD_MASK = _mm_setr_epi32(~(TILE_WIDTH - 1), ~(TILE_WIDTH - 1), ~(TILE_HEIGHT - 1), ~(TILE_HEIGHT - 1)); + static const __m128i SIMD_SUB_TILE_PAD = _mm_setr_epi32(0, SUB_TILE_WIDTH, 0, SUB_TILE_HEIGHT); + static const __m128i SIMD_SUB_TILE_PAD_MASK = _mm_setr_epi32(~(SUB_TILE_WIDTH - 1), ~(SUB_TILE_WIDTH - 1), ~(SUB_TILE_HEIGHT - 1), ~(SUB_TILE_HEIGHT - 1)); + + ////////////////////////////////////////////////////////////////////////////// + // Compute screen space bounding box and guard for out of bounds + ////////////////////////////////////////////////////////////////////////////// +#if USE_D3D != 0 + __m128 pixelBBox = _mmx_fmadd_ps(_mm_setr_ps(xmin, xmax, ymax, ymin), mIHalfSize, mICenter); +#else + __m128 pixelBBox = _mmx_fmadd_ps(_mm_setr_ps(xmin, xmax, ymin, ymax), mIHalfSize, mICenter); +#endif + __m128i pixelBBoxi = _mm_cvttps_epi32(pixelBBox); + pixelBBoxi = _mmx_max_epi32(_mm_setzero_si128(), _mmx_min_epi32(mIScreenSize, pixelBBoxi)); + + ////////////////////////////////////////////////////////////////////////////// + // Pad bounding box to (32xN) tiles. Tile BB is used for looping / traversal + ////////////////////////////////////////////////////////////////////////////// + __m128i tileBBoxi = _mm_and_si128(_mm_add_epi32(pixelBBoxi, SIMD_TILE_PAD), SIMD_TILE_PAD_MASK); + int txMin = simd_i32(tileBBoxi)[0] >> TILE_WIDTH_SHIFT; + int txMax = simd_i32(tileBBoxi)[1] >> TILE_WIDTH_SHIFT; + int tileRowIdx = (simd_i32(tileBBoxi)[2] >> TILE_HEIGHT_SHIFT)*mTilesWidth; + int tileRowIdxEnd = (simd_i32(tileBBoxi)[3] >> TILE_HEIGHT_SHIFT)*mTilesWidth; + + if (simd_i32(tileBBoxi)[0] == simd_i32(tileBBoxi)[1] || simd_i32(tileBBoxi)[2] == simd_i32(tileBBoxi)[3]) + { +#if MOC_RECORDER_ENABLE + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordTestRect( CullingResult::VIEW_CULLED, xmin, ymin, xmax, ymax, wmin ); + } +#endif + return CullingResult::VIEW_CULLED; + } + + /////////////////////////////////////////////////////////////////////////////// + // Pad bounding box to (8x4) subtiles. Skip SIMD lanes outside the subtile BB + /////////////////////////////////////////////////////////////////////////////// + __m128i subTileBBoxi = _mm_and_si128(_mm_add_epi32(pixelBBoxi, SIMD_SUB_TILE_PAD), SIMD_SUB_TILE_PAD_MASK); + __mwi stxmin = _mmw_set1_epi32(simd_i32(subTileBBoxi)[0] - 1); // - 1 to be able to use GT test + __mwi stymin = _mmw_set1_epi32(simd_i32(subTileBBoxi)[2] - 1); // - 1 to be able to use GT test + __mwi stxmax = _mmw_set1_epi32(simd_i32(subTileBBoxi)[1]); + __mwi stymax = _mmw_set1_epi32(simd_i32(subTileBBoxi)[3]); + + // Setup pixel coordinates used to discard lanes outside subtile BB + __mwi startPixelX = _mmw_add_epi32(SIMD_SUB_TILE_COL_OFFSET, _mmw_set1_epi32(simd_i32(tileBBoxi)[0])); + __mwi pixelY = _mmw_add_epi32(SIMD_SUB_TILE_ROW_OFFSET, _mmw_set1_epi32(simd_i32(tileBBoxi)[2])); + + ////////////////////////////////////////////////////////////////////////////// + // Compute z from w. Note that z is reversed order, 0 = far, 1 = near, which + // means we use a greater than test, so zMax is used to test for visibility. + ////////////////////////////////////////////////////////////////////////////// + __mw zMax = _mmw_div_ps(_mmw_set1_ps(1.0f), _mmw_set1_ps(wmin)); + + for (;;) + { + __mwi pixelX = startPixelX; + for (int tx = txMin;;) + { + STATS_ADD(mStats.mOccludees.mNumTilesTraversed, 1); + + int tileIdx = tileRowIdx + tx; + assert(tileIdx >= 0 && tileIdx < mTilesWidth*mTilesHeight); + + // Fetch zMin from masked hierarchical Z buffer +#if QUICK_MASK != 0 + __mw zBuf = mMaskedHiZBuffer[tileIdx].mZMin[0]; +#else + __mwi mask = mMaskedHiZBuffer[tileIdx].mMask; + __mw zMin0 = _mmw_blendv_ps(mMaskedHiZBuffer[tileIdx].mZMin[0], mMaskedHiZBuffer[tileIdx].mZMin[1], simd_cast<__mw>(_mmw_cmpeq_epi32(mask, _mmw_set1_epi32(~0)))); + __mw zMin1 = _mmw_blendv_ps(mMaskedHiZBuffer[tileIdx].mZMin[1], mMaskedHiZBuffer[tileIdx].mZMin[0], simd_cast<__mw>(_mmw_cmpeq_epi32(mask, _mmw_setzero_epi32()))); + __mw zBuf = _mmw_min_ps(zMin0, zMin1); +#endif + // Perform conservative greater than test against hierarchical Z buffer (zMax >= zBuf means the subtile is visible) + __mwi zPass = simd_cast<__mwi>(_mmw_cmpge_ps(zMax, zBuf)); //zPass = zMax >= zBuf ? ~0 : 0 + + // Mask out lanes corresponding to subtiles outside the bounding box + __mwi bboxTestMin = _mmw_and_epi32(_mmw_cmpgt_epi32(pixelX, stxmin), _mmw_cmpgt_epi32(pixelY, stymin)); + __mwi bboxTestMax = _mmw_and_epi32(_mmw_cmpgt_epi32(stxmax, pixelX), _mmw_cmpgt_epi32(stymax, pixelY)); + __mwi boxMask = _mmw_and_epi32(bboxTestMin, bboxTestMax); + zPass = _mmw_and_epi32(zPass, boxMask); + + // If not all tiles failed the conservative z test we can immediately terminate the test + if (!_mmw_testz_epi32(zPass, zPass)) + { +#if MOC_RECORDER_ENABLE + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordTestRect( CullingResult::VISIBLE, xmin, ymin, xmax, ymax, wmin ); + } +#endif + return CullingResult::VISIBLE; + } + + if (++tx >= txMax) + break; + pixelX = _mmw_add_epi32(pixelX, _mmw_set1_epi32(TILE_WIDTH)); + } + + tileRowIdx += mTilesWidth; + if (tileRowIdx >= tileRowIdxEnd) + break; + pixelY = _mmw_add_epi32(pixelY, _mmw_set1_epi32(TILE_HEIGHT)); + } +#if MOC_RECORDER_ENABLE + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordTestRect( CullingResult::OCCLUDED, xmin, ymin, xmax, ymax, wmin ); + } +#endif + return CullingResult::OCCLUDED; + } + + template + FORCE_INLINE void BinTriangles(const float *inVtx, const unsigned int *inTris, int nTris, TriList *triLists, unsigned int nBinsW, unsigned int nBinsH, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) + { + assert(mMaskedHiZBuffer != nullptr); + +#if PRECISE_COVERAGE != 0 + int originalRoundingMode = _MM_GET_ROUNDING_MODE(); + _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); +#endif + + STATS_ADD(mStats.mOccluders.mNumProcessedTriangles, nTris); + + int clipHead = 0; + int clipTail = 0; + __m128 clipTriBuffer[MAX_CLIPPED * 3]; + + const unsigned int *inTrisPtr = inTris; + int numLanes = SIMD_LANES; + int triIndex = 0; + while (triIndex < nTris || clipHead != clipTail) + { + unsigned int triMask = SIMD_ALL_LANES_MASK; + __mw vtxX[3], vtxY[3], vtxW[3]; + + GatherTransformClip( clipHead, clipTail, numLanes, nTris, triIndex, vtxX, vtxY, vtxW, inVtx, inTrisPtr, vtxLayout, modelToClipMatrix, clipTriBuffer, triMask, clipPlaneMask ); + + if (triMask == 0x0) + continue; + + ////////////////////////////////////////////////////////////////////////////// + // Project, transform to screen space and perform backface culling. Note + // that we use z = 1.0 / vtx.w for depth, which means that z = 0 is far and + // z = 1 is near. We must also use a greater than depth test, and in effect + // everything is reversed compared to regular z implementations. + ////////////////////////////////////////////////////////////////////////////// + + __mw pVtxX[3], pVtxY[3], pVtxZ[3]; + +#if PRECISE_COVERAGE != 0 + __mwi ipVtxX[3], ipVtxY[3]; + ProjectVertices(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); +#else + ProjectVertices(pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); +#endif + + // Perform backface test. + __mw triArea1 = _mmw_mul_ps(_mmw_sub_ps(pVtxX[1], pVtxX[0]), _mmw_sub_ps(pVtxY[2], pVtxY[0])); + __mw triArea2 = _mmw_mul_ps(_mmw_sub_ps(pVtxX[0], pVtxX[2]), _mmw_sub_ps(pVtxY[0], pVtxY[1])); + __mw triArea = _mmw_sub_ps(triArea1, triArea2); + __mw ccwMask = _mmw_cmpgt_ps(triArea, _mmw_setzero_ps()); + +#if PRECISE_COVERAGE != 0 + triMask &= CullBackfaces(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, ccwMask, bfWinding); +#else + triMask &= CullBackfaces(pVtxX, pVtxY, pVtxZ, ccwMask, bfWinding); +#endif + + if (triMask == 0x0) + continue; + + ////////////////////////////////////////////////////////////////////////////// + // Bin triangles + ////////////////////////////////////////////////////////////////////////////// + + unsigned int binWidth; + unsigned int binHeight; + ComputeBinWidthHeight(nBinsW, nBinsH, binWidth, binHeight); + + // Compute pixel bounding box + __mwi bbPixelMinX, bbPixelMinY, bbPixelMaxX, bbPixelMaxY; + ComputeBoundingBox(bbPixelMinX, bbPixelMinY, bbPixelMaxX, bbPixelMaxY, pVtxX, pVtxY, &mFullscreenScissor); + + while (triMask) + { + unsigned int triIdx = find_clear_lsb(&triMask); + + // Clamp bounding box to bins + int startX = min(nBinsW-1, simd_i32(bbPixelMinX)[triIdx] / binWidth); + int startY = min(nBinsH-1, simd_i32(bbPixelMinY)[triIdx] / binHeight); + int endX = min(nBinsW, (simd_i32(bbPixelMaxX)[triIdx] + binWidth - 1) / binWidth); + int endY = min(nBinsH, (simd_i32(bbPixelMaxY)[triIdx] + binHeight - 1) / binHeight); + + for (int y = startY; y < endY; ++y) + { + for (int x = startX; x < endX; ++x) + { + int binIdx = x + y * nBinsW; + unsigned int writeTriIdx = triLists[binIdx].mTriIdx; + for (int i = 0; i < 3; ++i) + { +#if PRECISE_COVERAGE != 0 + ((int*)triLists[binIdx].mPtr)[i * 3 + writeTriIdx * 9 + 0] = simd_i32(ipVtxX[i])[triIdx]; + ((int*)triLists[binIdx].mPtr)[i * 3 + writeTriIdx * 9 + 1] = simd_i32(ipVtxY[i])[triIdx]; +#else + triLists[binIdx].mPtr[i * 3 + writeTriIdx * 9 + 0] = simd_f32(pVtxX[i])[triIdx]; + triLists[binIdx].mPtr[i * 3 + writeTriIdx * 9 + 1] = simd_f32(pVtxY[i])[triIdx]; +#endif + triLists[binIdx].mPtr[i * 3 + writeTriIdx * 9 + 2] = simd_f32(pVtxZ[i])[triIdx]; + } + triLists[binIdx].mTriIdx++; + } + } + } + } +#if PRECISE_COVERAGE != 0 + _MM_SET_ROUNDING_MODE(originalRoundingMode); +#endif + } + + void BinTriangles(const float *inVtx, const unsigned int *inTris, int nTris, TriList *triLists, unsigned int nBinsW, unsigned int nBinsH, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) override + { + if (vtxLayout.mStride == 16 && vtxLayout.mOffsetY == 4 && vtxLayout.mOffsetW == 12) + BinTriangles(inVtx, inTris, nTris, triLists, nBinsW, nBinsH, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + else + BinTriangles(inVtx, inTris, nTris, triLists, nBinsW, nBinsH, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + } + + template + void GatherTransformClip( int & clipHead, int & clipTail, int & numLanes, int nTris, int & triIndex, __mw * vtxX, __mw * vtxY, __mw * vtxW, const float * inVtx, const unsigned int * &inTrisPtr, const VertexLayout & vtxLayout, const float * modelToClipMatrix, __m128 * clipTriBuffer, unsigned int &triMask, ClipPlanes clipPlaneMask ) + { + ////////////////////////////////////////////////////////////////////////////// + // Assemble triangles from the index list + ////////////////////////////////////////////////////////////////////////////// + unsigned int triClipMask = SIMD_ALL_LANES_MASK; + + if( clipHead != clipTail ) + { + int clippedTris = clipHead > clipTail ? clipHead - clipTail : MAX_CLIPPED + clipHead - clipTail; + clippedTris = min( clippedTris, SIMD_LANES ); + +#if CLIPPING_PRESERVES_ORDER != 0 + // if preserving order, don't mix clipped and new triangles, handle the clip buffer fully + // and then continue gathering; this is not as efficient - ideally we want to gather + // at the end (if clip buffer has less than SIMD_LANES triangles) but that requires + // more modifications below - something to do in the future. + numLanes = 0; +#else + // Fill out SIMD registers by fetching more triangles. + numLanes = max( 0, min( SIMD_LANES - clippedTris, nTris - triIndex ) ); +#endif + + if( numLanes > 0 ) { + if( FAST_GATHER ) + GatherVerticesFast( vtxX, vtxY, vtxW, inVtx, inTrisPtr, numLanes ); + else + GatherVertices( vtxX, vtxY, vtxW, inVtx, inTrisPtr, numLanes, vtxLayout ); + + TransformVerts( vtxX, vtxY, vtxW, modelToClipMatrix ); + } + + for( int clipTri = numLanes; clipTri < numLanes + clippedTris; clipTri++ ) + { + int triIdx = clipTail * 3; + for( int i = 0; i < 3; i++ ) + { + simd_f32( vtxX[i] )[clipTri] = simd_f32( clipTriBuffer[triIdx + i] )[0]; + simd_f32( vtxY[i] )[clipTri] = simd_f32( clipTriBuffer[triIdx + i] )[1]; + simd_f32( vtxW[i] )[clipTri] = simd_f32( clipTriBuffer[triIdx + i] )[2]; + } + clipTail = ( clipTail + 1 ) & ( MAX_CLIPPED - 1 ); + } + + triIndex += numLanes; + inTrisPtr += numLanes * 3; + + triMask = ( 1U << ( clippedTris + numLanes ) ) - 1; + triClipMask = ( 1U << numLanes ) - 1; // Don't re-clip already clipped triangles + } + else + { + numLanes = min( SIMD_LANES, nTris - triIndex ); + triMask = ( 1U << numLanes ) - 1; + triClipMask = triMask; + + if( FAST_GATHER ) + GatherVerticesFast( vtxX, vtxY, vtxW, inVtx, inTrisPtr, numLanes ); + else + GatherVertices( vtxX, vtxY, vtxW, inVtx, inTrisPtr, numLanes, vtxLayout ); + + TransformVerts( vtxX, vtxY, vtxW, modelToClipMatrix ); + + triIndex += SIMD_LANES; + inTrisPtr += SIMD_LANES * 3; + } + + ////////////////////////////////////////////////////////////////////////////// + // Clip transformed triangles + ////////////////////////////////////////////////////////////////////////////// + + if( clipPlaneMask != ClipPlanes::CLIP_PLANE_NONE ) + ClipTriangleAndAddToBuffer( vtxX, vtxY, vtxW, clipTriBuffer, clipHead, triMask, triClipMask, clipPlaneMask ); + } + + void RenderTrilist(const TriList &triList, const ScissorRect *scissor) override + { + assert(mMaskedHiZBuffer != nullptr); + + // Setup fullscreen scissor rect as default + scissor = scissor == nullptr ? &mFullscreenScissor : scissor; + + for (unsigned int i = 0; i < triList.mTriIdx; i += SIMD_LANES) + { + ////////////////////////////////////////////////////////////////////////////// + // Fetch triangle vertices + ////////////////////////////////////////////////////////////////////////////// + + unsigned int numLanes = min((unsigned int)SIMD_LANES, triList.mTriIdx - i); + unsigned int triMask = (1U << numLanes) - 1; + + __mw pVtxX[3], pVtxY[3], pVtxZ[3]; +#if PRECISE_COVERAGE != 0 + __mwi ipVtxX[3], ipVtxY[3]; + for (unsigned int l = 0; l < numLanes; ++l) + { + unsigned int triIdx = i + l; + for (int v = 0; v < 3; ++v) + { + simd_i32(ipVtxX[v])[l] = ((int*)triList.mPtr)[v * 3 + triIdx * 9 + 0]; + simd_i32(ipVtxY[v])[l] = ((int*)triList.mPtr)[v * 3 + triIdx * 9 + 1]; + simd_f32(pVtxZ[v])[l] = triList.mPtr[v * 3 + triIdx * 9 + 2]; + } + } + + for (int v = 0; v < 3; ++v) + { + pVtxX[v] = _mmw_mul_ps(_mmw_cvtepi32_ps(ipVtxX[v]), _mmw_set1_ps(FP_INV)); + pVtxY[v] = _mmw_mul_ps(_mmw_cvtepi32_ps(ipVtxY[v]), _mmw_set1_ps(FP_INV)); + } + + ////////////////////////////////////////////////////////////////////////////// + // Setup and rasterize a SIMD batch of triangles + ////////////////////////////////////////////////////////////////////////////// + + RasterizeTriangleBatch(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, triMask, scissor); +#else + for (unsigned int l = 0; l < numLanes; ++l) + { + unsigned int triIdx = i + l; + for (int v = 0; v < 3; ++v) + { + simd_f32(pVtxX[v])[l] = triList.mPtr[v * 3 + triIdx * 9 + 0]; + simd_f32(pVtxY[v])[l] = triList.mPtr[v * 3 + triIdx * 9 + 1]; + simd_f32(pVtxZ[v])[l] = triList.mPtr[v * 3 + triIdx * 9 + 2]; + } + } + + ////////////////////////////////////////////////////////////////////////////// + // Setup and rasterize a SIMD batch of triangles + ////////////////////////////////////////////////////////////////////////////// + + RasterizeTriangleBatch(pVtxX, pVtxY, pVtxZ, triMask, scissor); +#endif + + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Debugging and statistics + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MaskedOcclusionCulling::Implementation GetImplementation() override + { + return gInstructionSet; + } + + void ComputePixelDepthBuffer(float *depthData, bool flipY) override + { + assert(mMaskedHiZBuffer != nullptr); + for (int y = 0; y < mHeight; y++) + { + for (int x = 0; x < mWidth; x++) + { + // Compute 32xN tile index (SIMD value offset) + int tx = x / TILE_WIDTH; + int ty = y / TILE_HEIGHT; + int tileIdx = ty * mTilesWidth + tx; + + // Compute 8x4 subtile index (SIMD lane offset) + int stx = (x % TILE_WIDTH) / SUB_TILE_WIDTH; + int sty = (y % TILE_HEIGHT) / SUB_TILE_HEIGHT; + int subTileIdx = sty * 4 + stx; + + // Compute pixel index in subtile (bit index in 32-bit word) + int px = (x % SUB_TILE_WIDTH); + int py = (y % SUB_TILE_HEIGHT); + int bitIdx = py * 8 + px; + + int pixelLayer = (simd_i32(mMaskedHiZBuffer[tileIdx].mMask)[subTileIdx] >> bitIdx) & 1; + float pixelDepth = simd_f32(mMaskedHiZBuffer[tileIdx].mZMin[pixelLayer])[subTileIdx]; + + if( flipY ) + depthData[( mHeight - y - 1 ) * mWidth + x] = pixelDepth; + else + depthData[y * mWidth + x] = pixelDepth; + } + } + } + + OcclusionCullingStatistics GetStatistics() override + { + return mStats; + } + +}; diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/PackageInfo.json b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/PackageInfo.json new file mode 100644 index 0000000000..08b29fd566 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/PackageInfo.json @@ -0,0 +1,6 @@ +{ + "PackageName": "Masked Occlusion Culling", + "URL": "https://software.intel.com/content/www/us/en/develop/articles/masked-software-occlusion-culling.html", + "License": "Apache 2.0", + "LicenseFile": "LICENSE.txt" +} From 69e2d6bba1022f9e439a8c55edf1bbabf89887da Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 19:44:55 -0700 Subject: [PATCH 23/71] Minor comment update --- Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h index 7d192ea62a..e611ecf0d6 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h @@ -202,7 +202,7 @@ namespace AZ MatrixChangedEvent m_onWorldToClipMatrixChange; MatrixChangedEvent m_onWorldToViewMatrixChange; - // Software occlusion culling + // Masked Occlusion Culling interface MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr; }; From d737fcd3d3acd03edd184086e0e4b3754f2c6ed2 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 19:52:28 -0700 Subject: [PATCH 24/71] Removed extra newline --- Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index 85b6bf07b8..e97805ceb2 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -617,7 +617,6 @@ namespace AZ jobData->m_maskedOcclusionCulling = maskedOcclusionCulling; #endif - auto nodeVisitorLambda = [this, jobData, &parentJob, &frustum, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void { AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "nodeVisitorLambda()"); From 113073ca31fcf10b9d2d1ff8e02962c5264c9ff9 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 19:54:22 -0700 Subject: [PATCH 25/71] Removed unnecessary include --- Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index c02ac0713c..7e33750eb5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include From b09f73378f3efc08f7fa775e1f49bd38b30f63f3 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 20:49:39 -0700 Subject: [PATCH 26/71] [cpack_installer] replaced LY_DEFAULT_INSTALL_COMPONENT with built-in CMAKE_INSTALL_DEFAULT_COMPONENT_NAME. updated stale references to ly_install_target_COMPONENT with a get_prop call --- cmake/3rdParty.cmake | 2 - cmake/Packaging.cmake | 4 +- cmake/Platform/Common/Install_common.cmake | 46 ++++++++-------------- 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/cmake/3rdParty.cmake b/cmake/3rdParty.cmake index eb11404237..ebf640af23 100644 --- a/cmake/3rdParty.cmake +++ b/cmake/3rdParty.cmake @@ -291,7 +291,6 @@ function(ly_install_external_target 3RDPARTY_ROOT_DIRECTORY) # Install the Find file to our /cmake directory install(FILES ${CMAKE_CURRENT_LIST_FILE} DESTINATION cmake - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # We only want to install external targets that are part of our source tree @@ -302,7 +301,6 @@ function(ly_install_external_target 3RDPARTY_ROOT_DIRECTORY) get_filename_component(rel_path ${rel_path} DIRECTORY) install(DIRECTORY ${3RDPARTY_ROOT_DIRECTORY} DESTINATION ${rel_path} - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endif() diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index e7136eab12..1bcb251271 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -101,7 +101,7 @@ endif() install(FILES ${_cmake_package_dest} DESTINATION ./Tools/Redistributables/CMake - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) # IMPORTANT: required to be included AFTER setting all property overrides @@ -141,7 +141,7 @@ endfunction() # configure ALL components here ly_configure_cpack_component( - ${LY_DEFAULT_INSTALL_COMPONENT} REQUIRED + ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} REQUIRED DISPLAY_NAME "${PROJECT_NAME} Core" DESCRIPTION "${PROJECT_NAME} Headers, Libraries and Tools" ) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 710a8b266f..e0301bb914 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -11,7 +11,7 @@ set(CMAKE_INSTALL_MESSAGE NEVER) # Simplify messages to reduce output noise -ly_set(LY_DEFAULT_INSTALL_COMPONENT Core) +ly_set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core) file(RELATIVE_PATH runtime_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) file(RELATIVE_PATH library_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) @@ -27,6 +27,12 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME) # De-alias target name ly_de_alias_target(${ALIAS_TARGET_NAME} TARGET_NAME) + # get the component ID. if the property isn't set for the target, fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME + get_target_property(install_componet ${TARGET_NAME} INSTALL_COMPONENT) + if("${install_componet}" STREQUAL "install_componet-NOTFOUND") + unset(install_componet) + endif() + # All include directories marked PUBLIC or INTERFACE will be installed. We dont use PUBLIC_HEADER because in order to do that # we need to set the PUBLIC_HEADER property of the target for all the headers we are exporting. After doing that, installing the # headers end up in one folder instead of duplicating the folder structure of the public/interface include directory. @@ -41,7 +47,7 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME) unset(current_public_headers) install(DIRECTORY ${include_directory} DESTINATION ${include_location}/${target_source_dir} - COMPONENT ${ly_install_target_COMPONENT} + COMPONENT ${install_componet} FILES_MATCHING PATTERN *.h PATTERN *.hpp @@ -68,13 +74,13 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME) TARGETS ${TARGET_NAME} ARCHIVE DESTINATION ${archive_output_directory}/${PAL_PLATFORM_NAME}/$ - COMPONENT ${ly_install_target_COMPONENT} + COMPONENT ${install_componet} LIBRARY DESTINATION ${library_output_directory}/${PAL_PLATFORM_NAME}/$/${target_library_output_subdirectory} - COMPONENT ${ly_install_target_COMPONENT} + COMPONENT ${install_componet} RUNTIME DESTINATION ${runtime_output_directory}/${PAL_PLATFORM_NAME}/$/${target_runtime_output_subdirectory} - COMPONENT ${ly_install_target_COMPONENT} + COMPONENT ${install_componet} ) # CMakeLists.txt file @@ -182,7 +188,7 @@ set_property(TARGET ${TARGET_NAME} file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" CONTENT "${target_file_contents}") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" DESTINATION ${target_source_dir} - COMPONENT ${ly_install_target_COMPONENT} + COMPONENT ${install_componet} ) # Since a CMakeLists.txt could contain multiple targets, we generate it in a folder per target @@ -239,9 +245,13 @@ function(ly_setup_subdirectory absolute_target_source_dir) "\n" "${CREATE_ALIASES_PLACEHOLDER}" ) + + # get the component ID. if the property isn't set for the directory, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME + get_property(install_componet DIRECTORY ${absolute_target_source_dir} PROPERTY INSTALL_COMPONENT) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/CMakeLists.txt" DESTINATION ${target_source_dir} - COMPONENT ${ly_install_target_COMPONENT} + COMPONENT ${install_componet} ) endfunction() @@ -262,7 +272,6 @@ function(ly_setup_cmake_install) install(DIRECTORY "${LY_ROOT_FOLDER}/cmake" DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} PATTERN "__pycache__" EXCLUDE REGEX "Findo3de.cmake" EXCLUDE REGEX "Platform\/.*\/BuiltInPackages_.*\.cmake" EXCLUDE @@ -290,7 +299,6 @@ function(ly_setup_cmake_install) "${LY_ROOT_FOLDER}/CMakeLists.txt" "${CMAKE_CURRENT_BINARY_DIR}/cmake/engine.json" DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Collect all Find files that were added with ly_add_external_target_path @@ -303,7 +311,6 @@ function(ly_setup_cmake_install) endforeach() install(FILES ${additional_find_files} DESTINATION cmake/3rdParty - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Findo3de.cmake file: we generate a different Findo3de.camke file than the one we have in cmake. This one is going to expose all @@ -320,7 +327,6 @@ function(ly_setup_cmake_install) configure_file(${LY_ROOT_FOLDER}/cmake/install/Findo3de.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake" DESTINATION cmake - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # BuiltInPackage_.cmake: since associations could happen in any cmake file across the engine. We collect @@ -340,7 +346,6 @@ function(ly_setup_cmake_install) ) install(FILES "${pal_builtin_file}" DESTINATION cmake/3rdParty/Platform/${PAL_PLATFORM_NAME} - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endfunction() @@ -362,7 +367,6 @@ endfunction() function(ly_copy source_file target_directory) file(COPY \"\${source_file}\" DESTINATION \"\${target_directory}\" FILE_PERMISSIONS ${LY_COPY_PERMISSIONS}) endfunction()" - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) unset(runtime_commands) @@ -408,7 +412,6 @@ endfunction()" list(REMOVE_DUPLICATES runtime_commands) list(JOIN runtime_commands " " runtime_commands_str) # the spaces are just to see the right identation in the cmake_install.cmake file install(CODE "${runtime_commands_str}" - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endfunction() @@ -427,7 +430,6 @@ function(ly_setup_others) install(DIRECTORY "${LY_ROOT_FOLDER}/${dir}" DESTINATION ${install_path} - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} PATTERN "__pycache__" EXCLUDE ) @@ -438,7 +440,6 @@ function(ly_setup_others) install(FILES ${o3de_scripts} DESTINATION ./scripts - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) install(DIRECTORY @@ -446,7 +447,6 @@ function(ly_setup_others) ${LY_ROOT_FOLDER}/scripts/project_manager ${LY_ROOT_FOLDER}/scripts/o3de DESTINATION ./scripts - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} PATTERN "__pycache__" EXCLUDE PATTERN "CMakeLists.txt" EXCLUDE PATTERN "tests" EXCLUDE @@ -454,7 +454,6 @@ function(ly_setup_others) install(DIRECTORY "${LY_ROOT_FOLDER}/python" DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} REGEX "downloaded_packages" EXCLUDE REGEX "runtime" EXCLUDE ) @@ -463,19 +462,16 @@ function(ly_setup_others) install(DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/Registry DESTINATION ./${runtime_output_directory}/${PAL_PLATFORM_NAME}/$ - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) install(DIRECTORY ${LY_ROOT_FOLDER}/Registry DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Engine Source Assets install(DIRECTORY ${LY_ROOT_FOLDER}/Assets DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Gem Source Assets and Registry @@ -495,7 +491,6 @@ function(ly_setup_others) # the "Assets" folder from being copied underneath the /Assets folder install(DIRECTORY ${gem_abs_assets_path} DESTINATION ${gem_assets_path} - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endif() endforeach() @@ -511,7 +506,6 @@ function(ly_setup_others) get_filename_component(gem_relative_path ${gem_json_path} DIRECTORY) install(FILES ${gem_json_path} DESTINATION ${gem_relative_path} - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endforeach() @@ -519,14 +513,12 @@ function(ly_setup_others) install(DIRECTORY ${LY_ROOT_FOLDER}/Gems/Atom/Asset/ImageProcessingAtom/Config DESTINATION Gems/Atom/Asset/ImageProcessingAtom - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Templates install(DIRECTORY ${LY_ROOT_FOLDER}/Templates DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Misc @@ -535,7 +527,6 @@ function(ly_setup_others) ${LY_ROOT_FOLDER}/LICENSE.txt ${LY_ROOT_FOLDER}/README.md DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endfunction() @@ -549,15 +540,12 @@ function(ly_setup_target_generator) ${LY_ROOT_FOLDER}/Code/LauncherUnified/LauncherProject.cpp ${LY_ROOT_FOLDER}/Code/LauncherUnified/StaticModules.in DESTINATION LauncherGenerator - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) install(DIRECTORY ${LY_ROOT_FOLDER}/Code/LauncherUnified/Platform DESTINATION LauncherGenerator - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) install(FILES ${LY_ROOT_FOLDER}/Code/LauncherUnified/FindLauncherGenerator.cmake DESTINATION cmake - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endfunction() From 99497672f4188479a92f45411ee66a09012c23ec Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 20:51:07 -0700 Subject: [PATCH 27/71] Fixed cmake platform files for non-Windows platforms --- .../Code/Source/Platform/Android/platform_android_files.cmake | 4 ++-- .../RPI/Code/Source/Platform/Linux/platform_linux_files.cmake | 4 ++-- .../RPI/Code/Source/Platform/Mac/platform_mac_files.cmake | 4 ++-- .../RPI/Code/Source/Platform/iOS/platform_ios_files.cmake | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake index 357d8f0381..83ddc410d6 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake @@ -10,6 +10,6 @@ # set(FILES - Atom_Feature_Traits_Platform.h - Atom_Feature_Traits_Android.h + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_Android.h ) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake index 19be7951f6..99df861f9c 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake @@ -10,6 +10,6 @@ # set(FILES - Atom_Feature_Traits_Platform.h - Atom_Feature_Traits_Linux.h + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_Linux.h ) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake index bde67ff340..b1baca036e 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake @@ -10,6 +10,6 @@ # set(FILES - Atom_Feature_Traits_Platform.h - Atom_Feature_Traits_Mac.h + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_Mac.h ) diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake index 7f603e4bfd..3eae72e612 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake @@ -10,6 +10,6 @@ # set(FILES - Atom_Feature_Traits_Platform.h - Atom_Feature_Traits_iOS.h + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_iOS.h ) From 9afd9b0992befcd19390cea11ec74bdb0080103a Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 20:58:22 -0700 Subject: [PATCH 28/71] Fixed PAL cmake files for non-Windows builds --- .../Code/Source/Platform/Android/PAL_android.cmake | 13 +++++++++++++ .../RPI/Code/Source/Platform/Linux/PAL_linux.cmake | 1 + .../Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake | 1 + .../Atom/RPI/Code/Source/Platform/iOS/PAL_ios.cmake | 13 +++++++++++++ 4 files changed, 28 insertions(+) create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Android/PAL_android.cmake create mode 100644 Gems/Atom/RPI/Code/Source/Platform/iOS/PAL_ios.cmake diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/PAL_android.cmake b/Gems/Atom/RPI/Code/Source/Platform/Android/PAL_android.cmake new file mode 100644 index 0000000000..4542e7c707 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/PAL_android.cmake @@ -0,0 +1,13 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED FALSE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/PAL_linux.cmake b/Gems/Atom/RPI/Code/Source/Platform/Linux/PAL_linux.cmake index e9a14ba928..4542e7c707 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Linux/PAL_linux.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/PAL_linux.cmake @@ -10,3 +10,4 @@ # set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED FALSE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake b/Gems/Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake index c060b8bbaa..f177b9dfb9 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake @@ -10,3 +10,4 @@ # set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED TRUE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE) diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/PAL_ios.cmake b/Gems/Atom/RPI/Code/Source/Platform/iOS/PAL_ios.cmake new file mode 100644 index 0000000000..4542e7c707 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/PAL_ios.cmake @@ -0,0 +1,13 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED FALSE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE) From 5695681ed3518d3e7c4ab7ed3aad92c4bb01d422 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 21:03:41 -0700 Subject: [PATCH 29/71] [cpack_installer] fifth attempt to fix cpack selection --- scripts/build/Platform/Windows/installer_windows.cmd | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index 73d1a3d88c..c71b091de9 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -29,22 +29,25 @@ IF NOT EXIST "%WIX_TEMP%" ( REM Make sure we are using the CMake version of CPack and not the one that comes with chocolatey SET CPACK_PATH= IF "%LY_CMAKE_PATH%"=="" ( - FOR /F %%i in ('where cpack') DO ( + REM quote the paths from 'where' so we can properly tokenize ones in the list with spaces + FOR /F delims^=^"^ tokens^=1 %%i in ('where /F cpack') DO ( REM The cpack in chocolatey expects a number supplied with --version so it will error "%%i" --version > NUL IF !ERRORLEVEL!==0 ( SET "CPACK_PATH=%%i" + GOTO :run_cpack ) ) ) ELSE ( SET "CPACK_PATH=%LY_CMAKE_PATH%\cpack.exe" ) +:run_cpack ECHO [ci_build] "!CPACK_PATH!" --version "!CPACK_PATH!" --version IF ERRORLEVEL 1 ( ECHO [ci_build] CPack not found! - exit /b 1 + GOTO :popd_error ) ECHO [ci_build] "!CPACK_PATH!" -C %CONFIGURATION% From 091d6894cb928d51d61a6d026b6854ca304da47b Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 22:02:10 -0700 Subject: [PATCH 30/71] Fixed non-Windows platform include --- .../RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h | 2 +- .../RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h | 2 +- .../RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h | 2 +- .../RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h index 27e0af7f35..60835c7025 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h @@ -11,4 +11,4 @@ */ #pragma once -#include "Atom_Feature_Traits_Android.h" +#include "Atom_RPI_Traits_Android.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h index 39c6a3e572..f7a51ddbf7 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h @@ -11,4 +11,4 @@ */ #pragma once -#include "Atom_Feature_Traits_Linux.h" +#include "Atom_RPI_Traits_Linux.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h index 19816f2bd1..87bc2190f3 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h @@ -11,4 +11,4 @@ */ #pragma once -#include "Atom_Feature_Traits_Mac.h" +#include "Atom_RPI_Traits_Mac.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h index 4403d741dc..48fe26cc61 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h @@ -11,4 +11,4 @@ */ #pragma once -#include "Atom_Feature_Traits_iOS.h" +#include "Atom_RPI_Traits_iOS.h" From 602dd01434848ad04e32123e1dbd6d0aeb717824 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 22:02:11 -0700 Subject: [PATCH 31/71] [cpack_installer] fixed typo in error message --- cmake/Packaging.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index 1bcb251271..e2aa0f9348 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -54,7 +54,7 @@ endif() if(${CPACK_DESIRED_CMAKE_VERSION} VERSION_LESS ${CMAKE_MINIMUM_REQUIRED_VERSION}) message(FATAL_ERROR "The desired version of CMake to be included in the package is " - "is below the minium required version of CMake to run") + "is below the minimum required version of CMake to run") endif() # pull down the desired copy of CMake so it can be included in the package From 881c51dc9c9ff3caa1f4b404c91e9cbaf74bd6ab Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 22:05:20 -0700 Subject: [PATCH 32/71] [cpack_installer] removed unnecessary explicit use of CMAKE_INSTALL_DEFAULT_COMPONENT_NAME --- cmake/Packaging.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index e2aa0f9348..e5799c8ff1 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -101,7 +101,6 @@ endif() install(FILES ${_cmake_package_dest} DESTINATION ./Tools/Redistributables/CMake - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) # IMPORTANT: required to be included AFTER setting all property overrides From 328ced0059f90f66d740d97e2f50721c9724995f Mon Sep 17 00:00:00 2001 From: scottr Date: Thu, 3 Jun 2021 07:24:30 -0700 Subject: [PATCH 33/71] [cpack_installer] replaced missing get_target_property hack and fixed a typo --- cmake/Platform/Common/Install_common.cmake | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index e0301bb914..7e6917287f 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -27,11 +27,8 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME) # De-alias target name ly_de_alias_target(${ALIAS_TARGET_NAME} TARGET_NAME) - # get the component ID. if the property isn't set for the target, fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME - get_target_property(install_componet ${TARGET_NAME} INSTALL_COMPONENT) - if("${install_componet}" STREQUAL "install_componet-NOTFOUND") - unset(install_componet) - endif() + # get the component ID. if the property isn't set for the target, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME + get_property(install_component TARGET ${TARGET_NAME} PROPERTY INSTALL_COMPONENT) # All include directories marked PUBLIC or INTERFACE will be installed. We dont use PUBLIC_HEADER because in order to do that # we need to set the PUBLIC_HEADER property of the target for all the headers we are exporting. After doing that, installing the @@ -47,7 +44,7 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME) unset(current_public_headers) install(DIRECTORY ${include_directory} DESTINATION ${include_location}/${target_source_dir} - COMPONENT ${install_componet} + COMPONENT ${install_component} FILES_MATCHING PATTERN *.h PATTERN *.hpp @@ -74,13 +71,13 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME) TARGETS ${TARGET_NAME} ARCHIVE DESTINATION ${archive_output_directory}/${PAL_PLATFORM_NAME}/$ - COMPONENT ${install_componet} + COMPONENT ${install_component} LIBRARY DESTINATION ${library_output_directory}/${PAL_PLATFORM_NAME}/$/${target_library_output_subdirectory} - COMPONENT ${install_componet} + COMPONENT ${install_component} RUNTIME DESTINATION ${runtime_output_directory}/${PAL_PLATFORM_NAME}/$/${target_runtime_output_subdirectory} - COMPONENT ${install_componet} + COMPONENT ${install_component} ) # CMakeLists.txt file @@ -188,7 +185,7 @@ set_property(TARGET ${TARGET_NAME} file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" CONTENT "${target_file_contents}") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" DESTINATION ${target_source_dir} - COMPONENT ${install_componet} + COMPONENT ${install_component} ) # Since a CMakeLists.txt could contain multiple targets, we generate it in a folder per target @@ -247,11 +244,11 @@ function(ly_setup_subdirectory absolute_target_source_dir) ) # get the component ID. if the property isn't set for the directory, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME - get_property(install_componet DIRECTORY ${absolute_target_source_dir} PROPERTY INSTALL_COMPONENT) + get_property(install_component DIRECTORY ${absolute_target_source_dir} PROPERTY INSTALL_COMPONENT) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/CMakeLists.txt" DESTINATION ${target_source_dir} - COMPONENT ${install_componet} + COMPONENT ${install_component} ) endfunction() From 8214706ff9ab2c49cfafc7164bb6dbe0d296112c Mon Sep 17 00:00:00 2001 From: scottr Date: Thu, 3 Jun 2021 08:24:01 -0700 Subject: [PATCH 34/71] [cpack_installer] reworked how packaging is enabled for windows --- cmake/Platform/Windows/Packaging_windows.cmake | 10 ++-------- scripts/build/Platform/Windows/build_config.json | 3 +-- scripts/build/Platform/Windows/build_windows.cmd | 8 -------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 5210c24e7b..2fd281ad51 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -9,17 +9,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -set(LY_WIX_PATH "" CACHE PATH "Path to the WiX install path") - -if(LY_WIX_PATH) - file(TO_CMAKE_PATH ${LY_WIX_PATH} CPACK_WIX_ROOT) -elseif(DEFINED ENV{WIX}) - file(TO_CMAKE_PATH $ENV{WIX} CPACK_WIX_ROOT) -endif() +set(CPACK_WIX_ROOT "" CACHE PATH "Path to the WiX install path") if(CPACK_WIX_ROOT) if(NOT EXISTS ${CPACK_WIX_ROOT}) - message(FATAL_ERROR "Invalid path supplied for LY_WIX_PATH argument or WIX environment variable") + message(FATAL_ERROR "Invalid path supplied for CPACK_WIX_ROOT argument") endif() else() # early out as no path to WiX has been supplied effectively disabling support diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index b0d16f1fb6..fc4668de0a 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -314,8 +314,7 @@ "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", - "CMAKE_INCLUDE_WIX": "True", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE -DCPACK_WIX_ROOT=\"!WIX!\"", "CMAKE_LY_PROJECTS": "", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index 799e6828b2..3e995e1905 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -38,14 +38,6 @@ IF NOT EXIST %TMP% ( REM Compute half the amount of processors so some jobs can run SET /a HALF_PROCESSORS = NUMBER_OF_PROCESSORS / 2 -IF "%CMAKE_INCLUDE_WIX%"=="True" ( - REM Explicitly enable wix via command line arg for forensic logging - SET CMAKE_OPTIONS=%CMAKE_OPTIONS% -DLY_WIX_PATH="%WIX%" -) ELSE ( - REM Disable implicit enabling of windows packing by clearing out the wix variable - SET WIX= -) - SET LAST_CONFIGURE_CMD_FILE=ci_last_configure_cmd.txt SET CONFIGURE_CMD=cmake %SOURCE_DIRECTORY% %CMAKE_OPTIONS% %EXTRA_CMAKE_OPTIONS% -DLY_3RDPARTY_PATH="%LY_3RDPARTY_PATH%" -DLY_PROJECTS=%CMAKE_LY_PROJECTS% IF NOT EXIST CMakeCache.txt ( From 5fbf587b9e34a88a7b26d8ccbde77f6ae9fa15a4 Mon Sep 17 00:00:00 2001 From: jckand-amzn Date: Thu, 3 Jun 2021 10:50:27 -0500 Subject: [PATCH 35/71] Updating File Menu actions in test_Menus_FileMenuOptions_Work. Temporarily marking test as xfail due to LYN-4208 --- .../EditorScripts/Menus_FileMenuOptions.py | 10 ++++----- .../Gem/PythonTests/editor/test_Menus.py | 21 ++++++++++--------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py index 3e174af2bd..734226a9d1 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py @@ -9,11 +9,6 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ -""" -C24064528: The File menu options function normally -C16780778: The File menu options function normally-New view interaction Model enabled -""" - import os import sys @@ -54,7 +49,10 @@ class TestFileMenuOptions(EditorTestHelper): ("Save",), ("Save As",), ("Save Level Statistics",), - ("Project Settings", "Project Settings Tool"), + ("Edit Project Settings",), + ("Edit Platform Settings",), + ("New Project",), + ("Open Project",), ("Show Log File",), ("Resave All Slices",), ("Exit",), diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py index c2da1343de..2b3fdcbdf3 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py +++ b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py @@ -7,8 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens or, if provided, by the license below or the license accompanying this file. Do not remove or modify any license notices. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - -C16780783: Base Edit Menu Options (New Viewport Interaction Model) """ import os @@ -17,6 +15,7 @@ import pytest # Bail on the test if ly_test_tools doesn't exist. pytest.importorskip('ly_test_tools') import ly_test_tools.environment.file_system as file_system +import ly_test_tools.environment.process_utils as process_utils import editor_python_test_tools.hydra_test_utils as hydra test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") @@ -33,6 +32,7 @@ class TestMenus(object): def setup_teardown(self, request, workspace, project, level): def teardown(): file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) + process_utils.kill_processes_named("o3de", ignore_extensions=True) # Kill ProjectManager windows request.addfinalizer(teardown) @@ -80,8 +80,7 @@ class TestMenus(object): expected_lines, cfg_args=[level], run_python="--runpython", - auto_test_mode=True, - timeout=log_monitor_timeout, + timeout=log_monitor_timeout ) @pytest.mark.test_case_id("C16780807") @@ -107,13 +106,13 @@ class TestMenus(object): "Menus_ViewMenuOptions.py", expected_lines, cfg_args=[level], - auto_test_mode=True, run_python="--runpython", - timeout=log_monitor_timeout, + timeout=log_monitor_timeout ) @pytest.mark.test_case_id("C16780778") @pytest.mark.SUITE_sandbox + @pytest.mark.xfail # LYN-4208 def test_Menus_FileMenuOptions_Work(self, request, editor, level, launcher_platform): expected_lines = [ "New Level Action triggered", @@ -122,7 +121,10 @@ class TestMenus(object): "Save Action triggered", "Save As Action triggered", "Save Level Statistics Action triggered", - "Project Settings Tool Action triggered", + "Edit Project Settings Action triggered", + "Edit Platform Settings Action triggered", + "New Project Action triggered", + "Open Project Action triggered", "Show Log File Action triggered", "Resave All Slices Action triggered", "Exit Action triggered", @@ -135,7 +137,6 @@ class TestMenus(object): "Menus_FileMenuOptions.py", expected_lines, cfg_args=[level], - auto_test_mode=True, run_python="--runpython", - timeout=log_monitor_timeout, - ) \ No newline at end of file + timeout=log_monitor_timeout + ) From bcef8856ff2143fa137078525d98d47ddc6fe348 Mon Sep 17 00:00:00 2001 From: scottr Date: Thu, 3 Jun 2021 10:51:13 -0700 Subject: [PATCH 36/71] [cpack_installer] minor wording fixes --- cmake/Packaging.cmake | 2 +- scripts/build/Platform/Windows/installer_windows.cmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index e5799c8ff1..3e23511fa1 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -54,7 +54,7 @@ endif() if(${CPACK_DESIRED_CMAKE_VERSION} VERSION_LESS ${CMAKE_MINIMUM_REQUIRED_VERSION}) message(FATAL_ERROR "The desired version of CMake to be included in the package is " - "is below the minimum required version of CMake to run") + "below the minimum required version of CMake to run") endif() # pull down the desired copy of CMake so it can be included in the package diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index c71b091de9..a6ce15d59e 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -20,7 +20,7 @@ IF NOT EXIST %OUTPUT_DIRECTORY% ( ) PUSHD %OUTPUT_DIRECTORY% -REM Override the temporary directory used by wix to the EBS volume +REM Override the temporary directory used by wix to the workspace SET "WIX_TEMP=!WORKSPACE!/temp/wix" IF NOT EXIST "%WIX_TEMP%" ( MKDIR "%WIX_TEMP%" From 1bf8c599e3789999e2c33da5a2f82d77e48ea9df Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Thu, 27 May 2021 09:56:11 -0700 Subject: [PATCH 37/71] External Serialize Context for spawning This change makes it possible to provide a Serialize Context for spawning entities from spawnables. This also removes the need for the Serialize Context to be retrieved multiple times per frame. --- .../Spawnable/SpawnableEntitiesInterface.h | 54 +++++--- .../Spawnable/SpawnableEntitiesManager.cpp | 126 +++++++++--------- .../Spawnable/SpawnableEntitiesManager.h | 33 ++--- .../SpawnableEntitiesManagerTests.cpp | 24 +++- 4 files changed, 139 insertions(+), 98 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index 27f45064b6..cfa1f3b464 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -21,6 +21,7 @@ namespace AZ { class Entity; + class SerializeContext; } namespace AzFramework @@ -171,6 +172,34 @@ namespace AzFramework using ClaimEntitiesCallback = AZStd::function; using BarrierCallback = AZStd::function; + struct SpawnEntitiesOptionalArgs + { + //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This + //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. + EntityPreInsertionCallback m_preInsertionCallback; + //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that + //! made the function call. The returned list of entities contains all the newly created entities. + EntitySpawnCallback m_completionCallback; + //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. + AZ::SerializeContext* m_serializeContext { nullptr }; + }; + + struct DespawnAllEntitiesOptionalArgs + { + //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that + //! made the function call. The returned list of entities contains all the newly created entities. + EntityDespawnCallback m_completionCallback; + }; + + struct ReloadSpawnableOptionalArgs + { + //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that + //! made the function call. The returned list of entities contains all the newly created entities. + ReloadSpawnableCallback m_completionCallback; + //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. + AZ::SerializeContext* m_serializeContext { nullptr }; + }; + //! Interface definition to (de)spawn entities from a spawnable into the game world. //! //! While the callbacks of the individual calls are being processed they will block processing any other request. Callbacks can be @@ -197,40 +226,31 @@ namespace AzFramework //! Spawn instances of all entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. //! @param priority The priority at which this call will be executed. - //! @param completionCallback Optional callback that's called when spawning entities has completed. This can be called from - //! a different thread than the one that made the function call. The returned list of entities contains all the newly - //! created entities. + //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs virtual void SpawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback = {}, - EntitySpawnCallback completionCallback = {}) = 0; + EntitySpawnTicket& ticket, SpawnablePriority priority, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; //! Spawn instances of some entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. //! @param priority The priority at which this call will be executed. //! @param entityIndices The indices into the template entities stored in the spawnable that will be used to spawn entities from. - //! @param completionCallback Optional callback that's called when spawning entities has completed. This can be called from - //! a different thread than the one that made this function call. The returned list of entities contains all the newly - //! created entities. + //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs virtual void SpawnEntities( EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, - EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) = 0; + SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; //! Removes all entities in the provided list from the environment. //! @param ticket The ticket previously used to spawn entities with. //! @param priority The priority at which this call will be executed. - //! @param completionCallback Optional callback that's called when despawning entities has completed. This can be called from - //! a different thread than the one that made this function call. + //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs virtual void DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityDespawnCallback completionCallback = {}) = 0; - + EntitySpawnTicket& ticket, SpawnablePriority priority, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; //! Removes all entities in the provided list from the environment and reconstructs the entities from the provided spawnable. //! @param ticket Holds the information on the entities to reload. //! @param priority The priority at which this call will be executed. //! @param spawnable The spawnable that will replace the existing spawnable. Both need to have the same asset id. - //! @param completionCallback Optional callback that's called when the entities have been reloaded. This can be called from - //! a different thread than the one that made this function call. The returned list of entities contains all the replacement - //! entities. + //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs virtual void ReloadSpawnable( EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableCallback completionCallback = {}) = 0; + ReloadSpawnableOptionalArgs optionalArgs = {}) = 0; //! List all entities that are spawned using this ticket. //! @param ticket Only the entities associated with this ticket will be listed. diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 7b767d2a72..77106172f6 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -38,6 +38,10 @@ namespace AzFramework SpawnableEntitiesManager::SpawnableEntitiesManager() { + AZ::ComponentApplicationBus::BroadcastResult(m_defaultSerializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + AZ_Assert( + m_defaultSerializeContext, "Failed to retrieve serialization context during construction of the Spawnable Entities Manager."); + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { AZ::u64 value = aznumeric_caster(m_highPriorityThreshold); @@ -47,53 +51,57 @@ namespace AzFramework } void SpawnableEntitiesManager::SpawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback, - EntitySpawnCallback completionCallback) + EntitySpawnTicket& ticket, SpawnablePriority priority, SpawnEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnAllEntities hasn't been initialized."); SpawnAllEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); - queueEntry.m_completionCallback = AZStd::move(completionCallback); - queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback); + queueEntry.m_serializeContext = + optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; + queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); + queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback); QueueRequest(ticket, priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::SpawnEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, - EntityPreInsertionCallback preInsertionCallback, EntitySpawnCallback completionCallback) + EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnEntities hasn't been initialized."); SpawnEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_entityIndices = AZStd::move(entityIndices); - queueEntry.m_completionCallback = AZStd::move(completionCallback); - queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback); + queueEntry.m_serializeContext = + optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; + queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); + queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback); QueueRequest(ticket, priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityDespawnCallback completionCallback) + EntitySpawnTicket& ticket, SpawnablePriority priority, DespawnAllEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to DespawnAllEntities hasn't been initialized."); DespawnAllEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); - queueEntry.m_completionCallback = AZStd::move(completionCallback); + queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); QueueRequest(ticket, priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::ReloadSpawnable( EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableCallback completionCallback) + ReloadSpawnableOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to ReloadSpawnable hasn't been initialized."); ReloadSpawnableCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_spawnable = AZStd::move(spawnable); - queueEntry.m_completionCallback = AZStd::move(completionCallback); + queueEntry.m_serializeContext = + optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; + queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); QueueRequest(ticket, priority, AZStd::move(queueEntry)); } @@ -174,58 +182,57 @@ namespace AzFramework auto SpawnableEntitiesManager::ProcessQueue(Queue& queue) -> CommandQueueStatus { - AZStd::queue pendingRequestQueue; + // Process delayed requests first. + // Only process the requests that are currently in this queue, not the ones that could be re-added if they still can't complete. + size_t delayedSize = queue.m_delayed.size(); + for (size_t i = 0; i < delayedSize; ++i) { - AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex); - queue.m_pendingRequest.swap(pendingRequestQueue); + Requests& request = queue.m_delayed.front(); + bool result = AZStd::visit( + [this](auto&& args) -> bool + { + return ProcessRequest(args); + }, + request); + if (!result) + { + queue.m_delayed.emplace_back(AZStd::move(request)); + } + queue.m_delayed.pop_front(); } - if (!pendingRequestQueue.empty() || !queue.m_delayed.empty()) + // Process newly added requests. + while (true) { - AZ::SerializeContext* serializeContext = nullptr; - AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); - AZ_Assert(serializeContext, "Failed to retrieve serialization context."); - - // Only process the requests that are currently in this queue, not the ones that could be re-added if they still can't complete. - size_t delayedSize = queue.m_delayed.size(); - for (size_t i = 0; i < delayedSize; ++i) + AZStd::queue pendingRequestQueue; { - Requests& request = queue.m_delayed.front(); - bool result = AZStd::visit([this, serializeContext](auto&& args) -> bool - { - return ProcessRequest(args, *serializeContext); - }, request); - if (!result) - { - queue.m_delayed.emplace_back(AZStd::move(request)); - } - queue.m_delayed.pop_front(); + AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex); + queue.m_pendingRequest.swap(pendingRequestQueue); } - do + if (!pendingRequestQueue.empty()) { while (!pendingRequestQueue.empty()) { Requests& request = pendingRequestQueue.front(); - bool result = AZStd::visit([this, serializeContext](auto&& args) -> bool + bool result = AZStd::visit( + [this](auto&& args) -> bool { - return ProcessRequest(args, *serializeContext); - }, request); + return ProcessRequest(args); + }, + request); if (!result) { queue.m_delayed.emplace_back(AZStd::move(request)); } pendingRequestQueue.pop(); } - - // Spawning entities can result in more entities being queued to spawn. Repeat spawning until the queue is - // empty to avoid a chain of entity spawning getting dragged out over multiple frames. - { - AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex); - queue.m_pendingRequest.swap(pendingRequestQueue); - } - } while (!pendingRequestQueue.empty()); - } + } + else + { + break; + } + }; return queue.m_delayed.empty() ? CommandQueueStatus::NoCommandsLeft : CommandQueueStatus::HasCommandsLeft; } @@ -267,7 +274,7 @@ namespace AzFramework &entityTemplate, templateToCloneEntityIdMap, &serializeContext); } - bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) @@ -296,7 +303,7 @@ namespace AzFramework { const AZ::Entity& entityTemplate = *entitiesToSpawn[i]; - AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext); + AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, *request.m_serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); @@ -347,7 +354,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(SpawnEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) @@ -371,7 +378,7 @@ namespace AzFramework { const AZ::Entity& entityTemplate = *entitiesToSpawn[index]; - AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate); + AZ::Entity* clone = request.m_serializeContext->CloneObject(&entityTemplate); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); clone->SetId(AZ::Entity::MakeId()); @@ -413,8 +420,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(DespawnAllEntitiesCommand& request, - [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(DespawnAllEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -447,7 +453,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request, AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request) { Ticket& ticket = *request.m_ticket; AZ_Assert(ticket.m_spawnable.GetId() == request.m_spawnable.GetId(), @@ -488,7 +494,7 @@ namespace AzFramework { const AZ::Entity& entityTemplate = *entities[i]; - AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext); + AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, *request.m_serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); @@ -502,7 +508,7 @@ namespace AzFramework for (size_t index : ticket.m_spawnedEntityIndices) { ticket.m_spawnedEntities.push_back( - index < entitiesSize ? SpawnSingleEntity(*entities[index], serializeContext) : nullptr); + index < entitiesSize ? SpawnSingleEntity(*entities[index], *request.m_serializeContext) : nullptr); } } ticket.m_spawnable = AZStd::move(request.m_spawnable); @@ -525,7 +531,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(ListEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(ListEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -541,7 +547,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -560,7 +566,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -580,7 +586,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(BarrierCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(BarrierCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -599,7 +605,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(DestroyTicketCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(DestroyTicketCommand& request) { if (request.m_requestId == request.m_ticket->m_currentRequestId) { diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index afffdab8b5..b40ec20aa3 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -57,19 +57,16 @@ namespace AzFramework // The following functions are thread safe // - void SpawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback = {}, - EntitySpawnCallback completionCallback = {}) override; + void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, SpawnEntitiesOptionalArgs optionalArgs = {}) override; void SpawnEntities( EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, - EntityPreInsertionCallback preInsertionCallback = {}, - EntitySpawnCallback completionCallback = {}) override; + SpawnEntitiesOptionalArgs optionalArgs = {}) override; void DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityDespawnCallback completionCallback = {}) override; + EntitySpawnTicket& ticket, SpawnablePriority priority, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override; void ReloadSpawnable( EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableCallback completionCallback = {}) override; + ReloadSpawnableOptionalArgs optionalArgs = {}) override; void ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) override; void ListIndicesAndEntities( @@ -105,6 +102,7 @@ namespace AzFramework { EntitySpawnCallback m_completionCallback; EntityPreInsertionCallback m_preInsertionCallback; + AZ::SerializeContext* m_serializeContext; Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; @@ -114,6 +112,7 @@ namespace AzFramework AZStd::vector m_entityIndices; EntitySpawnCallback m_completionCallback; EntityPreInsertionCallback m_preInsertionCallback; + AZ::SerializeContext* m_serializeContext; Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; @@ -129,6 +128,7 @@ namespace AzFramework { AZ::Data::Asset m_spawnable; ReloadSpawnableCallback m_completionCallback; + AZ::SerializeContext* m_serializeContext; Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; @@ -191,15 +191,15 @@ namespace AzFramework AZ::Entity* CloneSingleEntity(const AZ::Entity& entityTemplate, EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext); - bool ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(DespawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(ReloadSpawnableCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(ListEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(ListIndicesEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(ClaimEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(BarrierCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(DestroyTicketCommand& request, AZ::SerializeContext& serializeContext); + bool ProcessRequest(SpawnAllEntitiesCommand& request); + bool ProcessRequest(SpawnEntitiesCommand& request); + bool ProcessRequest(DespawnAllEntitiesCommand& request); + bool ProcessRequest(ReloadSpawnableCommand& request); + bool ProcessRequest(ListEntitiesCommand& request); + bool ProcessRequest(ListIndicesEntitiesCommand& request); + bool ProcessRequest(ClaimEntitiesCommand& request); + bool ProcessRequest(BarrierCommand& request); + bool ProcessRequest(DestroyTicketCommand& request); Queue m_highPriorityQueue; Queue m_regularPriorityQueue; @@ -207,6 +207,7 @@ namespace AzFramework AZ::Event> m_onSpawnedEvent; AZ::Event> m_onDespawnedEvent; + AZ::SerializeContext* m_defaultSerializeContext { nullptr }; //! The threshold used to determine if a request goes in the regular (if bigger than the value) or high priority queue (if smaller //! or equal to this value). The starting value of 64 is chosen as it's between default values SpawnablePriority_High and //! SpawnablePriority_Default which gives users a bit of room to fine tune the priorities as this value can be configured diff --git a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 484b7f46d7..7a2e7f614b 100644 --- a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -104,7 +104,9 @@ namespace UnitTest { spawnedEntitiesCount += entities.size(); }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(callback)); + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(optionalArgs)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_EQ(NumEntities, spawnedEntitiesCount); @@ -305,8 +307,14 @@ namespace UnitTest defaultPriorityCallId = callCounter++; }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(defaultCallback)); - m_manager->SpawnAllEntities(highPriorityTicket, AzFramework::SpawnablePriority_High, {}, AZStd::move(highCallback)); + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(defaultCallback); + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(optionalArgs)); + + AzFramework::SpawnEntitiesOptionalArgs highPriortyOptionalArgs; + highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); + m_manager->SpawnAllEntities(highPriorityTicket, AzFramework::SpawnablePriority_High, AZStd::move(highPriortyOptionalArgs)); + m_manager->ProcessQueue( AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); @@ -333,8 +341,14 @@ namespace UnitTest defaultPriorityCallId = callCounter++; }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(defaultCallback)); - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_High, {}, AZStd::move(highCallback)); + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(defaultCallback); + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(optionalArgs)); + + AzFramework::SpawnEntitiesOptionalArgs highPriortyOptionalArgs; + highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_High, AZStd::move(highPriortyOptionalArgs)); + m_manager->ProcessQueue( AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); From 8c541b5205dd7605965e9719df6bfcfc1470f52a Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Tue, 1 Jun 2021 16:11:55 -0700 Subject: [PATCH 38/71] Moved default values to object in the Spawnable Entities Interface --- .../Spawnable/SpawnableEntitiesContainer.cpp | 11 ++-- .../Spawnable/SpawnableEntitiesInterface.h | 53 +++++++++++++------ .../Spawnable/SpawnableEntitiesManager.cpp | 37 +++++++------ .../Spawnable/SpawnableEntitiesManager.h | 22 ++++---- .../SpawnableEntitiesManagerTests.cpp | 26 +++++---- .../Libraries/Spawning/SpawnNodeable.cpp | 6 ++- 6 files changed, 88 insertions(+), 67 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp index 808de74e71..a945ea4edc 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp @@ -38,20 +38,20 @@ namespace AzFramework void SpawnableEntitiesContainer::SpawnAllEntities() { AZ_Assert(m_threadData, "Calling SpawnAllEntities on a Spawnable container that's not set."); - SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default); + SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_threadData->m_spawnedEntitiesTicket); } void SpawnableEntitiesContainer::SpawnEntities(AZStd::vector entityIndices) { AZ_Assert(m_threadData, "Calling SpawnEntities on a Spawnable container that's not set."); SpawnableEntitiesInterface::Get()->SpawnEntities( - m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default, AZStd::move(entityIndices)); + m_threadData->m_spawnedEntitiesTicket, AZStd::move(entityIndices)); } void SpawnableEntitiesContainer::DespawnAllEntities() { AZ_Assert(m_threadData, "Calling DespawnEntities on a Spawnable container that's not set."); - SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default); + SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_threadData->m_spawnedEntitiesTicket); } void SpawnableEntitiesContainer::Reset(AZ::Data::Asset spawnable) @@ -69,7 +69,6 @@ namespace AzFramework SpawnableEntitiesInterface::Get()->Barrier( m_threadData->m_spawnedEntitiesTicket, - SpawnablePriority_Default, [threadData = m_threadData](EntitySpawnTicket::Id) mutable { threadData.reset(); @@ -88,7 +87,6 @@ namespace AzFramework AZ_Assert(m_threadData, "Calling DespawnEntities on a Spawnable container that's not set."); SpawnableEntitiesInterface::Get()->Barrier( m_threadData->m_spawnedEntitiesTicket, - SpawnablePriority_Default, [generation = m_threadData->m_generation, callback = AZStd::move(callback)](EntitySpawnTicket::Id) { callback(generation); @@ -115,7 +113,6 @@ namespace AzFramework AZ_Assert(m_threadData, "SpawnableEntitiesContainer is monitoring a spawnable, but doesn't have the associated data."); AZ_TracePrintf("Spawnables", "Reloading spawnable '%s'.\n", replacementAsset.GetHint().c_str()); - SpawnableEntitiesInterface::Get()->ReloadSpawnable( - m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default, AZStd::move(replacementAsset)); + SpawnableEntitiesInterface::Get()->ReloadSpawnable(m_threadData->m_spawnedEntitiesTicket, AZStd::move(replacementAsset)); } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index cfa1f3b464..5d7ce79641 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -172,7 +172,7 @@ namespace AzFramework using ClaimEntitiesCallback = AZStd::function; using BarrierCallback = AZStd::function; - struct SpawnEntitiesOptionalArgs + struct SpawnEntitiesOptionalArgs final { //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. @@ -182,22 +182,46 @@ namespace AzFramework EntitySpawnCallback m_completionCallback; //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. AZ::SerializeContext* m_serializeContext { nullptr }; + //! The priority at which this call will be executed. + SpawnablePriority m_priority { SpawnablePriority_Default }; }; - struct DespawnAllEntitiesOptionalArgs + struct DespawnAllEntitiesOptionalArgs final { //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that //! made the function call. The returned list of entities contains all the newly created entities. EntityDespawnCallback m_completionCallback; + //! The priority at which this call will be executed. + SpawnablePriority m_priority { SpawnablePriority_Default }; }; - struct ReloadSpawnableOptionalArgs + struct ReloadSpawnableOptionalArgs final { //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that //! made the function call. The returned list of entities contains all the newly created entities. ReloadSpawnableCallback m_completionCallback; //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. AZ::SerializeContext* m_serializeContext { nullptr }; + //! The priority at which this call will be executed. + SpawnablePriority m_priority { SpawnablePriority_Default }; + }; + + struct ListEntitiesOptionalArgs final + { + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; + }; + + struct ClaimEntitiesOptionalArgs final + { + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; + }; + + struct BarrierOptionalArgs final + { + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; }; //! Interface definition to (de)spawn entities from a spawnable into the game world. @@ -225,38 +249,34 @@ namespace AzFramework //! Spawn instances of all entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. - //! @param priority The priority at which this call will be executed. //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs - virtual void SpawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; + virtual void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; //! Spawn instances of some entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. //! @param priority The priority at which this call will be executed. //! @param entityIndices The indices into the template entities stored in the spawnable that will be used to spawn entities from. //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs virtual void SpawnEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, - SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; + EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; //! Removes all entities in the provided list from the environment. //! @param ticket The ticket previously used to spawn entities with. //! @param priority The priority at which this call will be executed. //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs - virtual void DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; + virtual void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; //! Removes all entities in the provided list from the environment and reconstructs the entities from the provided spawnable. //! @param ticket Holds the information on the entities to reload. //! @param priority The priority at which this call will be executed. //! @param spawnable The spawnable that will replace the existing spawnable. Both need to have the same asset id. //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs virtual void ReloadSpawnable( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableOptionalArgs optionalArgs = {}) = 0; + EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) = 0; //! List all entities that are spawned using this ticket. //! @param ticket Only the entities associated with this ticket will be listed. //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to list the entities on. - virtual void ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) = 0; + virtual void ListEntities( + EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0; //! List all entities that are spawned using this ticket with their spawnable index. //! Spawnables contain a flat list of entities, which are used as templates to spawn entities from. For every spawned entity //! the index of the entity in the spawnable that was used as a template is stored. This version of ListEntities will return @@ -267,20 +287,21 @@ namespace AzFramework //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to list the entities and indices on. virtual void ListIndicesAndEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) = 0; + EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0; //! Claim all entities that are spawned using this ticket. Ownership of the entities is transferred from the ticket to the //! caller through the callback. After this call the ticket will have no entities associated with it. The caller of //! this function will need to manage the entities after this call. //! @param ticket Only the entities associated with this ticket will be released. //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to transfer the entities through. - virtual void ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ClaimEntitiesCallback listCallback) = 0; + virtual void ClaimEntities( + EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs = {}) = 0; //! Blocks until all operations made on the provided ticket before the barrier call have completed. //! @param ticket The ticket to monitor. //! @param priority The priority at which this call will be executed. //! @param completionCallback Required callback that will be called as soon as the barrier has been reached. - virtual void Barrier(EntitySpawnTicket& ticket, SpawnablePriority priority, BarrierCallback completionCallback) = 0; + virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) = 0; //! Register a handler for OnSpawned events. virtual void AddOnSpawnedHandler(AZ::Event>::Handler& handler) = 0; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 77106172f6..c11ce94bb2 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -50,8 +50,7 @@ namespace AzFramework } } - void SpawnableEntitiesManager::SpawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, SpawnEntitiesOptionalArgs optionalArgs) + void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, SpawnEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnAllEntities hasn't been initialized."); @@ -61,11 +60,11 @@ namespace AzFramework optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::SpawnEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs) + EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnEntities hasn't been initialized."); @@ -76,23 +75,21 @@ namespace AzFramework optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, DespawnAllEntitiesOptionalArgs optionalArgs) + void SpawnableEntitiesManager::DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to DespawnAllEntities hasn't been initialized."); DespawnAllEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::ReloadSpawnable( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableOptionalArgs optionalArgs) + EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to ReloadSpawnable hasn't been initialized."); @@ -102,10 +99,11 @@ namespace AzFramework queueEntry.m_serializeContext = optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) + void SpawnableEntitiesManager::ListEntities( + EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs) { AZ_Assert(listCallback, "ListEntities called on spawnable entities without a valid callback to use."); AZ_Assert(ticket.IsValid(), "Ticket provided to ListEntities hasn't been initialized."); @@ -113,11 +111,11 @@ namespace AzFramework ListEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_listCallback = AZStd::move(listCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::ListIndicesAndEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) + EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs) { AZ_Assert(listCallback, "ListEntities called on spawnable entities without a valid callback to use."); AZ_Assert(ticket.IsValid(), "Ticket provided to ListEntities hasn't been initialized."); @@ -125,10 +123,11 @@ namespace AzFramework ListIndicesEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_listCallback = AZStd::move(listCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ClaimEntitiesCallback listCallback) + void SpawnableEntitiesManager::ClaimEntities( + EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs) { AZ_Assert(listCallback, "ClaimEntities called on spawnable entities without a valid callback to use."); AZ_Assert(ticket.IsValid(), "Ticket provided to ClaimEntities hasn't been initialized."); @@ -136,10 +135,10 @@ namespace AzFramework ClaimEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_listCallback = AZStd::move(listCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::Barrier(EntitySpawnTicket& ticket, SpawnablePriority priority, BarrierCallback completionCallback) + void SpawnableEntitiesManager::Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs) { AZ_Assert(completionCallback, "Barrier on spawnable entities called without a valid callback to use."); AZ_Assert(ticket.IsValid(), "Ticket provided to Barrier hasn't been initialized."); @@ -147,7 +146,7 @@ namespace AzFramework BarrierCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_completionCallback = AZStd::move(completionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::AddOnSpawnedHandler(AZ::Event>::Handler& handler) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index b40ec20aa3..e6db19557f 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -57,23 +57,21 @@ namespace AzFramework // The following functions are thread safe // - void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, SpawnEntitiesOptionalArgs optionalArgs = {}) override; + void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnEntitiesOptionalArgs optionalArgs = {}) override; void SpawnEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, - SpawnEntitiesOptionalArgs optionalArgs = {}) override; - void DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override; - + EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) override; + void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override; void ReloadSpawnable( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableOptionalArgs optionalArgs = {}) override; + EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) override; - void ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) override; + void ListEntities( + EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) override; void ListIndicesAndEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) override; - void ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ClaimEntitiesCallback listCallback) override; + EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) override; + void ClaimEntities( + EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs = {}) override; - void Barrier(EntitySpawnTicket& spawnInfo, SpawnablePriority priority, BarrierCallback completionCallback) override; + void Barrier(EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) override; void AddOnSpawnedHandler(AZ::Event>::Handler& handler) override; void AddOnDespawnedHandler(AZ::Event>::Handler& handler) override; diff --git a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 7a2e7f614b..8637684b3c 100644 --- a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -106,7 +106,7 @@ namespace UnitTest }; AzFramework::SpawnEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(callback); - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(optionalArgs)); + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_EQ(NumEntities, spawnedEntitiesCount); @@ -116,7 +116,7 @@ namespace UnitTest { { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->SpawnAllEntities(ticket, AzFramework::SpawnablePriority_Default); + m_manager->SpawnAllEntities(ticket); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -130,7 +130,7 @@ namespace UnitTest { { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->SpawnEntities(ticket, AzFramework::SpawnablePriority_Default, {}); + m_manager->SpawnEntities(ticket, {/* Deliberate empty list of indices. */}); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -144,7 +144,7 @@ namespace UnitTest { { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->DespawnAllEntities(ticket, AzFramework::SpawnablePriority_Default); + m_manager->DespawnAllEntities(ticket); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -158,7 +158,7 @@ namespace UnitTest { { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->ReloadSpawnable(ticket, AzFramework::SpawnablePriority_Default, *m_spawnableAsset); + m_manager->ReloadSpawnable(ticket, *m_spawnableAsset); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -185,7 +185,7 @@ namespace UnitTest spawnedEntitiesCount += entities.size(); }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default); + m_manager->SpawnAllEntities(*m_ticket); m_manager->ListEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); @@ -230,7 +230,7 @@ namespace UnitTest } }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default); + m_manager->SpawnAllEntities(*m_ticket); m_manager->ListIndicesAndEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); @@ -309,11 +309,13 @@ namespace UnitTest AzFramework::SpawnEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(defaultCallback); - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(optionalArgs)); + optionalArgs.m_priority = AzFramework::SpawnablePriority_Default; + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); AzFramework::SpawnEntitiesOptionalArgs highPriortyOptionalArgs; highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); - m_manager->SpawnAllEntities(highPriorityTicket, AzFramework::SpawnablePriority_High, AZStd::move(highPriortyOptionalArgs)); + highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High; + m_manager->SpawnAllEntities(highPriorityTicket, AZStd::move(highPriortyOptionalArgs)); m_manager->ProcessQueue( AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | @@ -343,11 +345,13 @@ namespace UnitTest AzFramework::SpawnEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(defaultCallback); - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(optionalArgs)); + optionalArgs.m_priority = AzFramework::SpawnablePriority_Default; + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); AzFramework::SpawnEntitiesOptionalArgs highPriortyOptionalArgs; highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_High, AZStd::move(highPriortyOptionalArgs)); + highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High; + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(highPriortyOptionalArgs)); m_manager->ProcessQueue( AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp index ad53236108..39afa46a19 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp @@ -134,7 +134,9 @@ namespace ScriptCanvas::Nodeables::Spawning m_spawnBatchSizes.push_back(view.size()); }; - AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities( - m_spawnTicket, AzFramework::SpawnablePriority_Default, preSpawnCB, spawnCompleteCB); + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_preInsertionCallback = AZStd::move(preSpawnCB); + optionalArgs.m_completionCallback = AZStd::move(spawnCompleteCB); + AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_spawnTicket, AZStd::move(optionalArgs)); } } From 1e7ac6094982b78a262da2989507fcbf0ab0d566 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Thu, 3 Jun 2021 10:49:25 -0700 Subject: [PATCH 39/71] Reintroduced spawning multiple instances of the same entity The following was changed: - The remapper in AZ::IdUtils now has an additional argument to tell it what to do when it encounters the same source entity id. The original behavior of ignoring the new entity id and returning the first occurrence is the default. The alternative behavior is to store the last known entity id and return that instead. - Split the optional arguments for SpawnAllEntities and SpawnEntities. - SpawnEntities now has an option to continue with the entity mapping from a previous spawn call or to start with a fresh mapping. The latter is the default as the former will come at a performance cost since the mapping table has to be reconstructed. - Entities spawned using SpawnEntities and ReloadEntities now also get the correct entity mapping applied. - Added several new unit tests to cover most of the new functionality. - Fixed some places where the older API version was still called. --- .../AzCore/AzCore/Serialization/IdUtils.h | 28 +- .../AzCore/AzCore/Serialization/IdUtils.inl | 16 +- .../Spawnable/SpawnableEntitiesInterface.h | 23 +- .../Spawnable/SpawnableEntitiesManager.cpp | 111 ++++---- .../Spawnable/SpawnableEntitiesManager.h | 14 +- .../SpawnableEntitiesManagerTests.cpp | 254 +++++++++++++++++- .../Pipeline/NetBindMarkerComponent.cpp | 6 +- .../Libraries/Spawning/SpawnNodeable.cpp | 2 +- 8 files changed, 362 insertions(+), 92 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h index a571379883..98959fe4bd 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h +++ b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h @@ -28,7 +28,13 @@ namespace AZ { namespace IdUtils { - template + /** + * \param AllowDuplicates - If true allows the same id to be registered multiple time, + with the newer value overwriting the stored value. If false, duplicates are not allowed and + the first stored value is kept.The default is false. + */ + + template struct Remapper { /** @@ -138,14 +144,18 @@ namespace AZ * \param context - The serialize context for enumerating the @classPtr elements */ template - static void GenerateNewIdsAndFixRefs(T* object, MapType& newIdMap, AZ::SerializeContext* context = nullptr) + static void GenerateNewIdsAndFixRefs( + T* object, MapType& newIdMap, AZ::SerializeContext* context = nullptr) { if (!context) { AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext); if (!context) { - AZ_Error("Serialization", false, "No serialize context provided! Failed to get component application default serialize context! ComponentApp is not started or input serialize context should not be null!"); + AZ_Error( + "Serialization", false, + "No serialize context provided! Failed to get component application default serialize context! ComponentApp is " + "not started or input serialize context should not be null!"); return; } } @@ -156,8 +166,16 @@ namespace AZ { if (idGenerator) { - auto it = newIdMap.emplace(originalId, idGenerator()); - return it.first->second; + if constexpr(AllowDuplicates) + { + auto it = newIdMap.insert_or_assign(originalId, idGenerator()); + return it.first->second; + } + else + { + auto it = newIdMap.emplace(originalId, idGenerator()); + return it.first->second; + } } return originalId; } diff --git a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.inl b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.inl index 01617cf5aa..0dc87dfe18 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.inl +++ b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.inl @@ -30,8 +30,10 @@ namespace AZ bool m_isModifiedContainer; }; - template - unsigned int Remapper::RemapIds(void* classPtr, const AZ::Uuid& classUuid, const typename Remapper::IdMapper& mapper, AZ::SerializeContext* context, bool replaceId) + template + unsigned int Remapper::RemapIds( + void* classPtr, const AZ::Uuid& classUuid, const typename Remapper::IdMapper& mapper, + AZ::SerializeContext* context, bool replaceId) { if (!context) { @@ -152,16 +154,18 @@ namespace AZ return replaced; } - template - unsigned int Remapper::ReplaceIdsAndIdRefs(void* classPtr, const AZ::Uuid& classUuid, const IdMapper& mapper, AZ::SerializeContext* context /*= nullptr*/) + template + unsigned int Remapper::ReplaceIdsAndIdRefs(void* classPtr, const AZ::Uuid& classUuid, const IdMapper& mapper, AZ::SerializeContext* context /*= nullptr*/) { unsigned int replaced = RemapIds(classPtr, classUuid, mapper, context, true); replaced += RemapIds(classPtr, classUuid, mapper, context, false); return replaced; } - template - unsigned int Remapper::RemapIdsAndIdRefs(void* classPtr, const AZ::Uuid& classUuid, const typename Remapper::IdReplacer& mapper, AZ::SerializeContext* context) + template + unsigned int Remapper::RemapIdsAndIdRefs( + void* classPtr, const AZ::Uuid& classUuid, const typename Remapper::IdReplacer& mapper, + AZ::SerializeContext* context) { if (!context) { diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index 5d7ce79641..2ad1db60a4 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -172,7 +172,7 @@ namespace AzFramework using ClaimEntitiesCallback = AZStd::function; using BarrierCallback = AZStd::function; - struct SpawnEntitiesOptionalArgs final + struct SpawnAllEntitiesOptionalArgs final { //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. @@ -186,6 +186,25 @@ namespace AzFramework SpawnablePriority m_priority { SpawnablePriority_Default }; }; + struct SpawnEntitiesOptionalArgs final + { + //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This + //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. + EntityPreInsertionCallback m_preInsertionCallback; + //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that + //! made the function call. The returned list of entities contains all the newly created entities. + EntitySpawnCallback m_completionCallback; + //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. + AZ::SerializeContext* m_serializeContext{ nullptr }; + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; + //! Entity references are resolved by referring to the last entity spawned from a template entity in the spawnable. If this + //! is set to false entities from previous spawn calls are not taken into account. If set to true entity references may be + //! resolved to a previously spawned entity. A lookup table has to be constructed when true, which may negatively impact + //! performance, especially if a large number of entities are present on a ticket. + bool m_referencePreviouslySpawnedEntities{ false }; + }; + struct DespawnAllEntitiesOptionalArgs final { //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that @@ -250,7 +269,7 @@ namespace AzFramework //! Spawn instances of all entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs - virtual void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; + virtual void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; //! Spawn instances of some entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. //! @param priority The priority at which this call will be executed. diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index c11ce94bb2..a482c1d4f9 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -50,7 +50,7 @@ namespace AzFramework } } - void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, SpawnEntitiesOptionalArgs optionalArgs) + void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnAllEntities hasn't been initialized."); @@ -75,6 +75,7 @@ namespace AzFramework optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback); + queueEntry.m_referencePreviouslySpawnedEntities = optionalArgs.m_referencePreviouslySpawnedEntities; QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } @@ -256,21 +257,11 @@ namespace AzFramework } } - AZ::Entity* SpawnableEntitiesManager::SpawnSingleEntity(const AZ::Entity& entityTemplate, AZ::SerializeContext& serializeContext) - { - AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate); - AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - clone->SetId(AZ::Entity::MakeId()); - - GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, clone); - return clone; - } - AZ::Entity* SpawnableEntitiesManager::CloneSingleEntity(const AZ::Entity& entityTemplate, - EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext) + EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext) { - return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( - &entityTemplate, templateToCloneEntityIdMap, &serializeContext); + return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( + &entityTemplate, templateToCloneMap, &serializeContext); } bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request) @@ -297,13 +288,9 @@ namespace AzFramework spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize); templateToCloneEntityIdMap.reserve(entitiesToSpawnSize); - // Mark all indices as spawned for (size_t i = 0; i < entitiesToSpawnSize; ++i) { - const AZ::Entity& entityTemplate = *entitiesToSpawn[i]; - - AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, *request.m_serializeContext); - + AZ::Entity* clone = CloneSingleEntity(*entitiesToSpawn[i], templateToCloneEntityIdMap, *request.m_serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); spawnedEntities.emplace_back(clone); @@ -311,16 +298,8 @@ namespace AzFramework } // loadAll is true if every entity has been spawned only once - if (spawnedEntities.size() == entitiesToSpawnSize) - { - ticket.m_loadAll = true; - } - else - { - // Case where there were already spawns from a previous request - ticket.m_loadAll = false; - } - + ticket.m_loadAll = (spawnedEntities.size() == entitiesToSpawnSize); + // Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context. if (request.m_preInsertionCallback) { @@ -329,11 +308,10 @@ namespace AzFramework } // Add to the game context, now the entities are active - AZStd::for_each(ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end(), - [](AZ::Entity* entity) + for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it) { - GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, entity); - }); + GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); + } // Let other systems know about newly spawned entities for any post-processing after adding to the scene/game context. if (request.m_completionCallback) @@ -360,14 +338,34 @@ namespace AzFramework { AZStd::vector& spawnedEntities = ticket.m_spawnedEntities; AZStd::vector& spawnedEntityIndices = ticket.m_spawnedEntityIndices; + AZ_Assert( + spawnedEntities.size() == spawnedEntityIndices.size(), + "The indices for the spawned entities has gone out of sync with the entities."); - // Keep track how many entities there were in the array initially + // Keep track of how many entities there were in the array initially size_t spawnedEntitiesInitialCount = spawnedEntities.size(); // These are 'template' entities we'll be cloning from const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities(); size_t entitiesToSpawnSize = request.m_entityIndices.size(); + // Reconstruct the template to entity mapping. + EntityIdMap templateToCloneEntityIdMap; + if (!request.m_referencePreviouslySpawnedEntities) + { + templateToCloneEntityIdMap.reserve(entitiesToSpawnSize); + } + else + { + templateToCloneEntityIdMap.reserve(spawnedEntitiesInitialCount + entitiesToSpawnSize); + SpawnableConstIndexEntityContainerView indexEntityView( + spawnedEntities.begin(), spawnedEntityIndices.begin(), spawnedEntities.size()); + for (auto& entry : indexEntityView) + { + templateToCloneEntityIdMap.insert_or_assign(entitiesToSpawn[entry.GetIndex()]->GetId(), entry.GetEntity()->GetId()); + } + } + spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize); spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize); @@ -375,15 +373,11 @@ namespace AzFramework { if (index < entitiesToSpawn.size()) { - const AZ::Entity& entityTemplate = *entitiesToSpawn[index]; - - AZ::Entity* clone = request.m_serializeContext->CloneObject(&entityTemplate); + AZ::Entity* clone = CloneSingleEntity(*entitiesToSpawn[index], templateToCloneEntityIdMap, *request.m_serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - clone->SetId(AZ::Entity::MakeId()); spawnedEntities.push_back(clone); spawnedEntityIndices.push_back(index); - } } ticket.m_loadAll = false; @@ -396,11 +390,10 @@ namespace AzFramework } // Add to the game context, now the entities are active - AZStd::for_each(ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end(), - [](AZ::Entity* entity) + for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it) { - GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, entity); - }); + GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); + } if (request.m_completionCallback) { @@ -475,39 +468,43 @@ namespace AzFramework // Rebuild the list of entities. ticket.m_spawnedEntities.clear(); const Spawnable::EntityList& entities = request.m_spawnable->GetEntities(); + + // Map keeps track of ids from template (spawnable) to clone (instance) + // Allowing patch ups of fields referring to entityIds outside of a given entity + EntityIdMap templateToCloneEntityIdMap; + if (ticket.m_loadAll) { // The new spawnable may have a different number of entities and since the intent of the user was - // to load every, simply start over. + // to spawn every entity, simply start over. ticket.m_spawnedEntityIndices.clear(); - size_t entitiesToSpawnSize = entities.size(); - - // Map keeps track of ids from template (spawnable) to clone (instance) - // Allowing patch ups of fields referring to entityIds outside of a given entity - EntityIdMap templateToCloneEntityIdMap; templateToCloneEntityIdMap.reserve(entitiesToSpawnSize); - // Mark all indices as spawned for (size_t i = 0; i < entitiesToSpawnSize; ++i) { - const AZ::Entity& entityTemplate = *entities[i]; - - AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, *request.m_serializeContext); - + AZ::Entity* clone = CloneSingleEntity(*entities[i], templateToCloneEntityIdMap, *request.m_serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - ticket.m_spawnedEntities.emplace_back(clone); + ticket.m_spawnedEntities.push_back(clone); ticket.m_spawnedEntityIndices.push_back(i); } } else { size_t entitiesSize = entities.size(); + templateToCloneEntityIdMap.reserve(entitiesSize); for (size_t index : ticket.m_spawnedEntityIndices) { - ticket.m_spawnedEntities.push_back( - index < entitiesSize ? SpawnSingleEntity(*entities[index], *request.m_serializeContext) : nullptr); + // It's possible for the new spawnable to have a different number of entities, so guard against this. + // It's also possible that the entities have moved within the spawnable to a new index. This can't be + // detected and will result in the incorrect entities being spawned. + if (index < entitiesSize) + { + AZ::Entity* clone = CloneSingleEntity(*entities[index], templateToCloneEntityIdMap, *request.m_serializeContext); + AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); + ticket.m_spawnedEntities.push_back(clone); + } } } ticket.m_spawnable = AZStd::move(request.m_spawnable); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index e6db19557f..638559f3f1 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -37,7 +37,7 @@ namespace AzFramework AZ_CLASS_ALLOCATOR(SpawnableEntitiesManager, AZ::SystemAllocator, 0); using EntityIdMap = AZStd::unordered_map; - + enum class CommandQueueStatus : bool { HasCommandsLeft, @@ -57,7 +57,7 @@ namespace AzFramework // The following functions are thread safe // - void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnEntitiesOptionalArgs optionalArgs = {}) override; + void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs = {}) override; void SpawnEntities( EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) override; void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override; @@ -114,6 +114,7 @@ namespace AzFramework Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; + bool m_referencePreviouslySpawnedEntities; }; struct DespawnAllEntitiesCommand { @@ -183,12 +184,9 @@ namespace AzFramework CommandQueueStatus ProcessQueue(Queue& queue); - AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate, - AZ::SerializeContext& serializeContext); - - AZ::Entity* CloneSingleEntity(const AZ::Entity& entityTemplate, - EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext); - + AZ::Entity* CloneSingleEntity( + const AZ::Entity& entityTemplate, EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext); + bool ProcessRequest(SpawnAllEntitiesCommand& request); bool ProcessRequest(SpawnEntitiesCommand& request); bool ProcessRequest(DespawnAllEntitiesCommand& request); diff --git a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 8637684b3c..32ad7d8ea9 100644 --- a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace UnitTest @@ -81,6 +82,42 @@ namespace UnitTest } } + void CreateRecursiveHierarchy() + { + AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities(); + size_t numElements = entities.size(); + AZ::EntityId parent; + for (size_t i=0; i& entity = entities[i]; + auto component = entity->CreateComponent(); + if (i > 0) + { + component->SetParent(parent); + } + parent = entity->GetId(); + } + } + + void CreateSingleParent() + { + AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities(); + size_t numElements = entities.size(); + if (numElements > 0) + { + AZ::EntityId parent = entities[0]->GetId(); + for (size_t i = 0; i < numElements; ++i) + { + AZStd::unique_ptr& entity = entities[i]; + auto component = entity->CreateComponent(); + if (i > 0) + { + component->SetParent(parent); + } + } + } + } + protected: AZ::Data::Asset* m_spawnableAsset { nullptr }; AzFramework::SpawnableEntitiesManager* m_manager { nullptr }; @@ -104,7 +141,7 @@ namespace UnitTest { spawnedEntitiesCount += entities.size(); }; - AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(callback); m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); @@ -112,6 +149,37 @@ namespace UnitTest EXPECT_EQ(NumEntities, spawnedEntitiesCount); } + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_SetParentOnSpawnedEntities_LineageIsPreserved) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + CreateRecursiveHierarchy(); + + auto callback = [](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + AZ::EntityId parentId; + bool isFirst = true; + for (const AZ::Entity* entity : entities) + { + if (!isFirst) + { + auto transform = entity->GetTransform(); + ASSERT_NE(nullptr, transform); + EXPECT_EQ(parentId, transform->GetParentId()); + } + else + { + isFirst = false; + } + parentId = entity->GetId(); + } + }; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_DeleteTicketBeforeCall_NoCrash) { { @@ -126,6 +194,170 @@ namespace UnitTest // SpawnEntities // + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_Call_AllEntitiesSpawned) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + AZStd::vector indices = { 0, 2, 3, 1 }; + + size_t spawnedEntitiesCount = 0; + auto callback = [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(NumEntities, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_SpawnTheSameEntity_AllEntitiesSpawned) + { + static constexpr size_t NumEntities = 1; + FillSpawnable(NumEntities); + + AZStd::vector indices = { 0, 0 }; + + size_t spawnedEntitiesCount = 0; + auto callback = + [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(NumEntities * 2, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_MultipleSpawns_AllEntitiesSpawned) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + AZStd::vector indices = { 0, 2, 3, 1 }; + + size_t spawnedEntitiesCount = 0; + auto callback = + [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, indices, optionalArgs); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(NumEntities * 2, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedForNewBatch_AllPointToLatestParent) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + CreateSingleParent(); + + AZStd::vector indices = { 0, 1, 2, 3 }; + AZStd::vector parents; + + auto callback = [&parents](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + AZ::EntityId parent = (*entities.begin())->GetId(); + parents.push_back(parent); + auto it = entities.begin(); + ++it; // Skip the first as that is the parent. + for (; it != entities.end(); ++it) + { + AZ::TransformInterface* transform = (*it)->GetTransform(); + ASSERT_NE(nullptr, transform); + ASSERT_EQ(parent, transform->GetParentId()); + } + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + optionalArgs.m_referencePreviouslySpawnedEntities = false; + m_manager->SpawnEntities(*m_ticket, indices, optionalArgs); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_NE(parents[0], parents[1]); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedForContinuedBatch_AllPointToLatestParent) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + CreateSingleParent(); + + AZStd::vector indices = { 0, 1, 2, 3 }; + AZStd::vector parents; + + auto callback = + [&parents](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + AZ::EntityId parent = (*entities.begin())->GetId(); + parents.push_back(parent); + auto it = entities.begin(); + ++it; // Skip the first as that is the parent. + for (; it!=entities.end(); ++it) + { + AZ::TransformInterface* transform = (*it)->GetTransform(); + ASSERT_NE(nullptr, transform); + ASSERT_EQ(parent, transform->GetParentId()); + } + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + optionalArgs.m_referencePreviouslySpawnedEntities = true; + m_manager->SpawnEntities(*m_ticket, indices, optionalArgs); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_NE(parents[0], parents[1]); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedAcrossBatches_AllPointToLatestParent) + { + FillSpawnable(4); + CreateSingleParent(); + + // Spawn a regular batch but with two parents and store the id of the last entity. This will the parent for the next batch. + AZ::EntityId parent; + auto getParent = [&parent](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + ASSERT_NE(entities.begin(), entities.end()); + parent = (*AZStd::prev(entities.end()))->GetId(); + }; + + AzFramework::SpawnEntitiesOptionalArgs optionalArgsFirstBatch; + optionalArgsFirstBatch.m_completionCallback = AZStd::move(getParent); + optionalArgsFirstBatch.m_referencePreviouslySpawnedEntities = true; + m_manager->SpawnEntities(*m_ticket, {0, 1, 2, 3, 0}, AZStd::move(optionalArgsFirstBatch)); + + // Next, spawn all the entities that have a reference to the parent that was just stored. + auto parentCheck = [&parent](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + for (auto& it : entities) + { + AZ::TransformInterface* transform = it->GetTransform(); + ASSERT_NE(nullptr, transform); + ASSERT_EQ(parent, transform->GetParentId()); + } + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgsSecondBatch; + optionalArgsSecondBatch.m_completionCallback = AZStd::move(parentCheck); + optionalArgsSecondBatch.m_referencePreviouslySpawnedEntities = true; + m_manager->SpawnEntities(*m_ticket, {1, 2, 3}, AZStd::move(optionalArgsSecondBatch)); + + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_DeleteTicketBeforeCall_NoCrash) { { @@ -186,7 +418,7 @@ namespace UnitTest }; m_manager->SpawnAllEntities(*m_ticket); - m_manager->ListEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ListEntities(*m_ticket, AZStd::move(callback)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_TRUE(allValidEntityIds); @@ -199,7 +431,7 @@ namespace UnitTest { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->ListEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ListEntities(ticket, AZStd::move(callback)); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -231,7 +463,7 @@ namespace UnitTest }; m_manager->SpawnAllEntities(*m_ticket); - m_manager->ListIndicesAndEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ListIndicesAndEntities(*m_ticket, AZStd::move(callback)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_TRUE(allValidEntityIds); @@ -244,7 +476,7 @@ namespace UnitTest { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->ListIndicesAndEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ListIndicesAndEntities(ticket, AZStd::move(callback)); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -260,7 +492,7 @@ namespace UnitTest { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->ClaimEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ClaimEntities(ticket, AZStd::move(callback)); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -276,7 +508,7 @@ namespace UnitTest { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->Barrier(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->Barrier(ticket, AZStd::move(callback)); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -307,12 +539,12 @@ namespace UnitTest defaultPriorityCallId = callCounter++; }; - AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(defaultCallback); optionalArgs.m_priority = AzFramework::SpawnablePriority_Default; m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); - AzFramework::SpawnEntitiesOptionalArgs highPriortyOptionalArgs; + AzFramework::SpawnAllEntitiesOptionalArgs highPriortyOptionalArgs; highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High; m_manager->SpawnAllEntities(highPriorityTicket, AZStd::move(highPriortyOptionalArgs)); @@ -343,12 +575,12 @@ namespace UnitTest defaultPriorityCallId = callCounter++; }; - AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(defaultCallback); optionalArgs.m_priority = AzFramework::SpawnablePriority_Default; m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); - AzFramework::SpawnEntitiesOptionalArgs highPriortyOptionalArgs; + AzFramework::SpawnAllEntitiesOptionalArgs highPriortyOptionalArgs; highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High; m_manager->SpawnAllEntities(*m_ticket, AZStd::move(highPriortyOptionalArgs)); diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp index c93c09cfbc..bd1cf40da8 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp @@ -81,8 +81,10 @@ namespace Multiplayer }; m_netSpawnTicket = AzFramework::EntitySpawnTicket(m_networkSpawnableAsset); + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_preInsertionCallback = AZStd::move(preInsertionCallback); AzFramework::SpawnableEntitiesInterface::Get()->SpawnEntities( - m_netSpawnTicket, AzFramework::SpawnablePriority_Default, { m_netEntityIndex }, preInsertionCallback); + m_netSpawnTicket, { m_netEntityIndex }, AZStd::move(optionalArgs)); } } @@ -90,7 +92,7 @@ namespace Multiplayer { if(m_netSpawnTicket.IsValid()) { - AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket, AzFramework::SpawnablePriority_Default); + AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket); } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp index 39afa46a19..a7875c2615 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp @@ -134,7 +134,7 @@ namespace ScriptCanvas::Nodeables::Spawning m_spawnBatchSizes.push_back(view.size()); }; - AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_preInsertionCallback = AZStd::move(preSpawnCB); optionalArgs.m_completionCallback = AZStd::move(spawnCompleteCB); AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_spawnTicket, AZStd::move(optionalArgs)); From dbeee91e7bc8d654e41fb420132036d94513a6b5 Mon Sep 17 00:00:00 2001 From: jckand-amzn Date: Thu, 3 Jun 2021 15:14:19 -0500 Subject: [PATCH 40/71] SPEC-7008: Setting up LargeWorlds main tests to be skipped in Debug builds --- .../dyn_veg/test_DynamicSliceInstanceSpawner.py | 6 ++++++ .../largeworlds/dyn_veg/test_EmptyInstanceSpawner.py | 6 ++++++ .../landscape_canvas/test_GraphComponentSync.py | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py index ead1e8779c..9235b302b8 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py @@ -16,6 +16,7 @@ import logging # Bail on the test if ly_test_tools doesn't exist. pytest.importorskip('ly_test_tools') import ly_test_tools.environment.file_system as file_system +import ly_test_tools._internal.pytest_plugin as internal_plugin import editor_python_test_tools.hydra_test_utils as hydra from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole @@ -46,6 +47,11 @@ class TestDynamicSliceInstanceSpawner(object): @pytest.mark.parametrize("launcher_platform", ['windows_editor']) def test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(self, request, editor, level, workspace, project, launcher_platform): + + # Skip test if running against Debug build + if "debug" in internal_plugin.build_directory: + pytest.skip("Does not execute against debug builds.") + # Ensure temp level does not already exist file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py index ca71cd2137..83263c614a 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py @@ -16,6 +16,7 @@ import logging # Bail on the test if ly_test_tools doesn't exist. pytest.importorskip('ly_test_tools') import ly_test_tools.environment.file_system as file_system +import ly_test_tools._internal.pytest_plugin as internal_plugin import editor_python_test_tools.hydra_test_utils as hydra logger = logging.getLogger(__name__) @@ -40,6 +41,11 @@ class TestEmptyInstanceSpawner(object): @pytest.mark.SUITE_main @pytest.mark.dynveg_area def test_EmptyInstanceSpawner_EmptySpawnerWorks(self, request, editor, level, launcher_platform): + + # Skip test if running against Debug build + if "debug" in internal_plugin.build_directory: + pytest.skip("Does not execute against debug builds.") + cfg_args = [level] expected_lines = [ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py index 855764fa6f..943d0cb985 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py @@ -23,6 +23,7 @@ import pytest # Bail on the test if ly_test_tools doesn't exist. pytest.importorskip('ly_test_tools') import ly_test_tools.environment.file_system as file_system +import ly_test_tools._internal.pytest_plugin as internal_plugin import editor_python_test_tools.hydra_test_utils as hydra test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') @@ -46,6 +47,11 @@ class TestGraphComponentSync(object): @pytest.mark.BAT @pytest.mark.SUITE_main def test_LandscapeCanvas_SlotConnections_UpdateComponentReferences(self, request, editor, level, launcher_platform): + + # Skip test if running against Debug build + if "debug" in internal_plugin.build_directory: + pytest.skip("Does not execute against debug builds.") + cfg_args = [level] expected_lines = [ @@ -122,6 +128,11 @@ class TestGraphComponentSync(object): """ Verifies a Gradient Mixer can be setup in Landscape Canvas and all references are property set. """ + + # Skip test if running against Debug build + if "debug" in internal_plugin.build_directory: + pytest.skip("Does not execute against debug builds.") + cfg_args = [level] expected_lines = [ From ed0fab894b6cad4d8e151c4c109bf69307b8a285 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Thu, 3 Jun 2021 13:30:28 -0700 Subject: [PATCH 41/71] Added DiffuseGlobalIllumination level component --- .../DiffuseGlobalIlluminationComponent.cpp | 44 +++++++++ .../DiffuseGlobalIlluminationComponent.h | 38 ++++++++ ...ffuseGlobalIlluminationComponentConfig.cpp | 32 +++++++ ...DiffuseGlobalIlluminationComponentConfig.h | 43 +++++++++ ...fuseGlobalIlluminationComponentConstants.h | 23 +++++ ...eGlobalIlluminationComponentController.cpp | 92 +++++++++++++++++++ ...useGlobalIlluminationComponentController.h | 55 +++++++++++ .../DiffuseProbeGridComponent.cpp | 2 +- .../DiffuseProbeGridComponent.h | 4 +- .../DiffuseProbeGridComponentConstants.h | 0 .../DiffuseProbeGridComponentController.cpp | 4 +- .../DiffuseProbeGridComponentController.h | 2 +- ...itorDiffuseGlobalIlluminationComponent.cpp | 82 +++++++++++++++++ ...EditorDiffuseGlobalIlluminationComponent.h | 40 ++++++++ .../EditorDiffuseProbeGridComponent.cpp | 2 +- .../EditorDiffuseProbeGridComponent.h | 4 +- .../CommonFeatures/Code/Source/Module.cpp | 8 +- ...egration_commonfeatures_editor_files.cmake | 6 +- ...omlyintegration_commonfeatures_files.cmake | 14 ++- 19 files changed, 478 insertions(+), 17 deletions(-) create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConstants.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridComponent.cpp (96%) rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridComponent.h (90%) rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridComponentConstants.h (100%) rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridComponentController.cpp (99%) rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridComponentController.h (98%) create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/EditorDiffuseProbeGridComponent.cpp (99%) rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/EditorDiffuseProbeGridComponent.h (97%) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp new file mode 100644 index 0000000000..b14bcf28b8 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp @@ -0,0 +1,44 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +namespace AZ +{ + namespace Render + { + + DiffuseGlobalIlluminationComponent::DiffuseGlobalIlluminationComponent(const DiffuseGlobalIlluminationComponentConfig& config) + : BaseClass(config) + { + } + + void DiffuseGlobalIlluminationComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class(); + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("DiffuseGlobalIlluminationComponentTypeId", BehaviorConstant(Uuid(DiffuseGlobalIlluminationComponentTypeId))) + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common); + } + } + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h new file mode 100644 index 0000000000..a54e5868cd --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h @@ -0,0 +1,38 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class DiffuseGlobalIlluminationComponent final + : public AzFramework::Components::ComponentAdapter + { + public: + using BaseClass = AzFramework::Components::ComponentAdapter; + AZ_COMPONENT(AZ::Render::DiffuseGlobalIlluminationComponent, DiffuseGlobalIlluminationComponentTypeId , BaseClass); + + DiffuseGlobalIlluminationComponent() = default; + DiffuseGlobalIlluminationComponent(const DiffuseGlobalIlluminationComponentConfig& config); + + static void Reflect(AZ::ReflectContext* context); + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp new file mode 100644 index 0000000000..406da9db2e --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp @@ -0,0 +1,32 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void DiffuseGlobalIlluminationComponentConfig::Reflect(ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("QualityLevel", &DiffuseGlobalIlluminationComponentConfig::m_qualityLevel) + ; + } + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h new file mode 100644 index 0000000000..23296967a5 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h @@ -0,0 +1,43 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + enum class DiffuseGlobalIlluminationQualityLevel : uint32_t + { + Low, + Medium, + High, + + Count + }; + + class DiffuseGlobalIlluminationComponentConfig final + : public ComponentConfig + { + public: + AZ_RTTI(DiffuseGlobalIlluminationComponentConfig, "{0D0835D6-6094-4EF8-BEAC-5FF8A4E4C119}", ComponentConfig); + AZ_CLASS_ALLOCATOR(DiffuseGlobalIlluminationComponentConfig, SystemAllocator, 0); + + static void Reflect(ReflectContext* context); + + DiffuseGlobalIlluminationQualityLevel m_qualityLevel; + }; + } +} diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConstants.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConstants.h new file mode 100644 index 0000000000..e89aa65584 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConstants.h @@ -0,0 +1,23 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +namespace AZ +{ + namespace Render + { + static constexpr const char* const DiffuseGlobalIlluminationComponentTypeId = "{D51F8033-EF0D-4A13-BED3-5B193555B8D2}"; + static constexpr const char* const EditorDiffuseGlobalIlluminationComponentTypeId = "{169378DD-4052-4A60-BD63-90B02CFA69C1}"; + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp new file mode 100644 index 0000000000..4c7f37bd4a --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp @@ -0,0 +1,92 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +//#include + +#include +//#include + +namespace AZ +{ + namespace Render + { + void DiffuseGlobalIlluminationComponentController::Reflect(ReflectContext* context) + { + DiffuseGlobalIlluminationComponentConfig::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("Configuration", &DiffuseGlobalIlluminationComponentController::m_configuration); + } + } + + void DiffuseGlobalIlluminationComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC("DiffuseGlobalIlluminationService", 0x11b9cbe1)); + } + + void DiffuseGlobalIlluminationComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC("DiffuseGlobalIlluminationService", 0x11b9cbe1)); + } + + void DiffuseGlobalIlluminationComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + AZ_UNUSED(required); + } + + DiffuseGlobalIlluminationComponentController::DiffuseGlobalIlluminationComponentController(const DiffuseGlobalIlluminationComponentConfig& config) + : m_configuration(config) + { + } + + void DiffuseGlobalIlluminationComponentController::Activate(EntityId entityId) + { + m_entityId = entityId; + } + + void DiffuseGlobalIlluminationComponentController::Deactivate() + { + //m_postProcessInterface = nullptr; + m_entityId.SetInvalid(); + } + + void DiffuseGlobalIlluminationComponentController::SetConfiguration(const DiffuseGlobalIlluminationComponentConfig& config) + { + m_configuration = config; + OnConfigChanged(); + } + + const DiffuseGlobalIlluminationComponentConfig& DiffuseGlobalIlluminationComponentController::GetConfiguration() const + { + return m_configuration; + } + + void DiffuseGlobalIlluminationComponentController::OnConfigChanged() + { + // Register the configuration with the AcesDisplayMapperFeatureProcessor for this scene. + //const AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); + //DisplayMapperFeatureProcessorInterface* fp = scene->GetFeatureProcessor(); + //DisplayMapperConfigurationDescriptor desc; + //desc.m_operationType = m_configuration.m_displayMapperOperation; + //desc.m_ldrGradingLutEnabled = m_configuration.m_ldrColorGradingLutEnabled; + //desc.m_ldrColorGradingLut = m_configuration.m_ldrColorGradingLut; + //desc.m_acesParameterOverrides = m_configuration.m_acesParameterOverrides; + //fp->RegisterDisplayMapperConfiguration(desc); + } + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h new file mode 100644 index 0000000000..8700e1ffb5 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h @@ -0,0 +1,55 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +#include + +//#include +//#include + +namespace AZ +{ + namespace Render + { + class DiffuseGlobalIlluminationComponentController final + { + public: + friend class EditorDiffuseGlobalIlluminationComponent; + + AZ_TYPE_INFO(AZ::Render::DiffuseGlobalIlluminationComponentController, "{7DE7D2A0-2526-447C-A11F-C31EE1332C26}"); + static void Reflect(AZ::ReflectContext* context); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + + DiffuseGlobalIlluminationComponentController() = default; + DiffuseGlobalIlluminationComponentController(const DiffuseGlobalIlluminationComponentConfig& config); + + void Activate(EntityId entityId); + void Deactivate(); + void SetConfiguration(const DiffuseGlobalIlluminationComponentConfig& config); + const DiffuseGlobalIlluminationComponentConfig& GetConfiguration() const; + + private: + AZ_DISABLE_COPY(DiffuseGlobalIlluminationComponentController); + + void OnConfigChanged(); + + DiffuseGlobalIlluminationComponentConfig m_configuration; + EntityId m_entityId; + }; + } +} diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.cpp similarity index 96% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.cpp rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.cpp index 509cbb86e6..b042f594c6 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.cpp @@ -10,7 +10,7 @@ * */ -#include +#include namespace AZ { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.h similarity index 90% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.h index 964ce157bd..b77dceefac 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.h @@ -12,8 +12,8 @@ #pragma once -#include -#include +#include +#include #include namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentConstants.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentConstants.h similarity index 100% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentConstants.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentConstants.h diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp similarity index 99% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp index 5fb835de15..f5ac36e7f6 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp @@ -10,8 +10,8 @@ * */ -#include -#include +#include +#include #include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h similarity index 98% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h index 4122a07ba2..ef606d2170 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp new file mode 100644 index 0000000000..bdb5686e89 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp @@ -0,0 +1,82 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +//#include "Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h" + +#include +#include + +namespace AZ +{ + namespace Render + { + void EditorDiffuseGlobalIlluminationComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Diffuse Global Illumination", "Diffuse Global Illumination configuration") + ->ClassElement(Edit::ClassElements::EditorData, "") + ->Attribute(Edit::Attributes::Category, "Atom") + ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.png") + ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC("Level", 0x9aeacc13), AZ_CRC("Game", 0x232b318c) })) + ->Attribute(Edit::Attributes::AutoExpand, true) + ->Attribute(Edit::Attributes::HelpPageURL, "https://") + ; + + editContext->Class( + "ToneMapperComponentControl", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &DiffuseGlobalIlluminationComponentController::m_configuration, "Configuration", "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ; + + editContext->Class("DiffuseGlobalIlluminationComponentConfig", "") + ->ClassElement(Edit::ClassElements::EditorData, "") + ->DataElement(Edit::UIHandlers::ComboBox, &DiffuseGlobalIlluminationComponentConfig::m_qualityLevel, "Quality Level", "Quality Level") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->EnumAttribute(DiffuseGlobalIlluminationQualityLevel::Low, "Low") + ->EnumAttribute(DiffuseGlobalIlluminationQualityLevel::Medium, "Medium") + ->EnumAttribute(DiffuseGlobalIlluminationQualityLevel::High, "High") + ; + } + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("EditorDiffuseGlobalIlluminationComponentTypeId", BehaviorConstant(Uuid(EditorDiffuseGlobalIlluminationComponentTypeId))) + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); + } + } + + EditorDiffuseGlobalIlluminationComponent::EditorDiffuseGlobalIlluminationComponent(const DiffuseGlobalIlluminationComponentConfig& config) + : BaseClass(config) + { + } + + u32 EditorDiffuseGlobalIlluminationComponent::OnConfigurationChanged() + { + m_controller.OnConfigChanged(); + return Edit::PropertyRefreshLevels::AttributesAndValues; + } + } +} diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h new file mode 100644 index 0000000000..2a478665c5 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h @@ -0,0 +1,40 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + class EditorDiffuseGlobalIlluminationComponent final + : public AzToolsFramework::Components::EditorComponentAdapter + { + public: + + using BaseClass = AzToolsFramework::Components::EditorComponentAdapter; + AZ_EDITOR_COMPONENT(AZ::Render::EditorDiffuseGlobalIlluminationComponent, EditorDiffuseGlobalIlluminationComponentTypeId, BaseClass); + + static void Reflect(AZ::ReflectContext* context); + + EditorDiffuseGlobalIlluminationComponent() = default; + EditorDiffuseGlobalIlluminationComponent(const DiffuseGlobalIlluminationComponentConfig& config); + + //! EditorRenderComponentAdapter overrides... + AZ::u32 OnConfigurationChanged() override; + }; + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp similarity index 99% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp index 1e5b959803..caf9ecd007 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp @@ -10,7 +10,7 @@ * */ -#include +#include #include #include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.h similarity index 97% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.h index 15c46d45ba..2e013ca479 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.h @@ -16,8 +16,8 @@ #include #include #include -#include -#include +#include +#include #include namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp index 2ef4e1e229..df8552e5c9 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp @@ -18,7 +18,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -47,7 +48,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -111,6 +113,7 @@ namespace AZ EntityReferenceComponent::CreateDescriptor(), GradientWeightModifierComponent::CreateDescriptor(), DiffuseProbeGridComponent::CreateDescriptor(), + DiffuseGlobalIlluminationComponent::CreateDescriptor(), DeferredFogComponent::CreateDescriptor(), SurfaceData::SurfaceDataMeshComponent::CreateDescriptor(), AttachmentComponent::CreateDescriptor(), @@ -142,6 +145,7 @@ namespace AZ EditorEntityReferenceComponent::CreateDescriptor(), EditorGradientWeightModifierComponent::CreateDescriptor(), EditorDiffuseProbeGridComponent::CreateDescriptor(), + EditorDiffuseGlobalIlluminationComponent::CreateDescriptor(), EditorDeferredFogComponent::CreateDescriptor(), SurfaceData::EditorSurfaceDataMeshComponent::CreateDescriptor(), EditorAttachmentComponent::CreateDescriptor(), diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake index e58f72a121..a68c54e85f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake @@ -23,8 +23,10 @@ set(FILES Source/CoreLights/EditorDirectionalLightComponent.cpp Source/Decals/EditorDecalComponent.h Source/Decals/EditorDecalComponent.cpp - Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h - Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp + Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.h + Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp + Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h + Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp Source/Grid/EditorGridComponent.h Source/Grid/EditorGridComponent.cpp Source/ImageBasedLights/EditorImageBasedLightComponent.h diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake index e13d1d37d6..7745306785 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake @@ -43,10 +43,16 @@ set(FILES Source/Decals/DecalComponent.cpp Source/Decals/DecalComponentController.h Source/Decals/DecalComponentController.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridComponent.h - Source/DiffuseProbeGrid/DiffuseProbeGridComponent.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h - Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp Source/Grid/GridComponent.h Source/Grid/GridComponent.cpp Source/Grid/GridComponentConfig.cpp From d1f23aff62c4644ba7b3f9f8386c8014479f8b3b Mon Sep 17 00:00:00 2001 From: jckand-amzn Date: Thu, 3 Jun 2021 17:38:22 -0500 Subject: [PATCH 42/71] SPEC-7008: Excluding more main tests from Debug test runs --- .../Gem/PythonTests/editor/test_BasicEditorWorkflows.py | 5 +++++ .../largeworlds/landscape_canvas/test_GraphComponentSync.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_BasicEditorWorkflows.py b/AutomatedTesting/Gem/PythonTests/editor/test_BasicEditorWorkflows.py index b045b364a3..f65401f007 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/test_BasicEditorWorkflows.py +++ b/AutomatedTesting/Gem/PythonTests/editor/test_BasicEditorWorkflows.py @@ -15,6 +15,7 @@ import pytest # Bail on the test if ly_test_tools doesn't exist. pytest.importorskip('ly_test_tools') import ly_test_tools.environment.file_system as file_system +import ly_test_tools._internal.pytest_plugin as internal_plugin import editor_python_test_tools.hydra_test_utils as hydra test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") @@ -40,6 +41,10 @@ class TestBasicEditorWorkflows(object): @pytest.mark.SUITE_main def test_BasicEditorWorkflows_LevelEntityComponentCRUD(self, request, editor, level, launcher_platform): + # Skip test if running against Debug build + if "debug" in internal_plugin.build_directory: + pytest.skip("Does not execute against debug builds.") + expected_lines = [ "Create and load new level: True", "New entity creation: True", diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py index 943d0cb985..e7dc046480 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py @@ -132,7 +132,7 @@ class TestGraphComponentSync(object): # Skip test if running against Debug build if "debug" in internal_plugin.build_directory: pytest.skip("Does not execute against debug builds.") - + cfg_args = [level] expected_lines = [ From 792176d7640d3b5ac80f8da82c9a407da3705272 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Thu, 3 Jun 2021 16:02:16 -0700 Subject: [PATCH 43/71] Cached occlusion plane corner points and AABB in the feature processor --- .../OcclusionCullingPlaneFeatureProcessor.cpp | 49 ++++++++++++++++-- .../OcclusionCullingPlaneFeatureProcessor.h | 4 ++ .../Code/Include/Atom/RPI.Public/Culling.h | 16 +++++- .../RPI/Code/Source/RPI.Public/Culling.cpp | 51 +++++-------------- 4 files changed, 76 insertions(+), 44 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp index b9866a925f..ff7c32ba08 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp @@ -33,6 +33,7 @@ namespace AZ void OcclusionCullingPlaneFeatureProcessor::Activate() { m_occlusionCullingPlanes.reserve(InitialOcclusionCullingPlanesAllocationSize); + m_rpiOcclusionPlanes.reserve(InitialOcclusionCullingPlanesAllocationSize); EnableSceneNotification(); } @@ -48,13 +49,46 @@ namespace AZ } void OcclusionCullingPlaneFeatureProcessor::OnBeginPrepareRender() - { - AZStd::vector occlusionCullingPlanes; - for (auto& occlusionCullingPlane : m_occlusionCullingPlanes) + { + if (m_rpiListNeedsUpdate) { - occlusionCullingPlanes.push_back(occlusionCullingPlane->GetTransform()); + // rebuild the RPI occlusion list + m_rpiOcclusionPlanes.clear(); + + for (auto& occlusionCullingPlane : m_occlusionCullingPlanes) + { + if (!occlusionCullingPlane->GetEnabled()) + { + continue; + } + + RPI::CullingScene::OcclusionPlane rpiOcclusionPlane; + + static const Vector3 BL = Vector3(-0.5f, -0.5f, 0.0f); + static const Vector3 BR = Vector3(0.5f, -0.5f, 0.0f); + static const Vector3 TL = Vector3(-0.5f, 0.5f, 0.0f); + static const Vector3 TR = Vector3(0.5f, 0.5f, 0.0f); + + const AZ::Transform& transform = occlusionCullingPlane->GetTransform(); + + // convert corners to world space + rpiOcclusionPlane.m_cornerBL = transform.TransformPoint(BL); + rpiOcclusionPlane.m_cornerBR = transform.TransformPoint(BR); + rpiOcclusionPlane.m_cornerTL = transform.TransformPoint(TL); + rpiOcclusionPlane.m_cornerTR = transform.TransformPoint(TR); + + // build world space AABB + AZ::Vector3 aabbMin = rpiOcclusionPlane.m_cornerBL.GetMin(rpiOcclusionPlane.m_cornerTR); + AZ::Vector3 aabbMax = rpiOcclusionPlane.m_cornerBL.GetMax(rpiOcclusionPlane.m_cornerTR); + rpiOcclusionPlane.m_aabb = Aabb::CreateFromMinMax(aabbMin, aabbMax); + + m_rpiOcclusionPlanes.push_back(rpiOcclusionPlane); + } + + GetParentScene()->GetCullingScene()->SetOcclusionPlanes(m_rpiOcclusionPlanes); + + m_rpiListNeedsUpdate = false; } - GetParentScene()->GetCullingScene()->SetOcclusionCullingPlanes(occlusionCullingPlanes); } OcclusionCullingPlaneHandle OcclusionCullingPlaneFeatureProcessor::AddOcclusionCullingPlane(const AZ::Transform& transform) @@ -63,6 +97,8 @@ namespace AZ occlusionCullingPlane->Init(GetParentScene()); occlusionCullingPlane->SetTransform(transform); m_occlusionCullingPlanes.push_back(occlusionCullingPlane); + m_rpiListNeedsUpdate = true; + return occlusionCullingPlane; } @@ -78,18 +114,21 @@ namespace AZ AZ_Assert(itEntry != m_occlusionCullingPlanes.end(), "RemoveOcclusionCullingPlane called with an occlusion plane that is not in the occlusion plane list"); m_occlusionCullingPlanes.erase(itEntry); occlusionCullingPlane = nullptr; + m_rpiListNeedsUpdate = true; } void OcclusionCullingPlaneFeatureProcessor::SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) { AZ_Assert(occlusionCullingPlane.get(), "SetTransform called with an invalid handle"); occlusionCullingPlane->SetTransform(transform); + m_rpiListNeedsUpdate = true; } void OcclusionCullingPlaneFeatureProcessor::SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enabled) { AZ_Assert(occlusionCullingPlane.get(), "Enable called with an invalid handle"); occlusionCullingPlane->SetEnabled(enabled); + m_rpiListNeedsUpdate = true; } void OcclusionCullingPlaneFeatureProcessor::ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization) diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h index 211254742f..8b3ac3f58d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h @@ -57,6 +57,10 @@ namespace AZ // list of occlusion planes const size_t InitialOcclusionCullingPlanesAllocationSize = 64; OcclusionCullingPlaneVector m_occlusionCullingPlanes; + + // prebuilt list of RPI scene occlusion planes + RPI::CullingScene::OcclusionPlaneVector m_rpiOcclusionPlanes; + bool m_rpiListNeedsUpdate = false; }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index 295797d2dd..2a9c133b5c 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -215,8 +215,20 @@ namespace AZ void Activate(const class Scene* parentScene); void Deactivate(); + struct OcclusionPlane + { + // World space corners of the occluson plane + Vector3 m_cornerBL; + Vector3 m_cornerBR; + Vector3 m_cornerTL; + Vector3 m_cornerTR; + + Aabb m_aabb; + }; + using OcclusionPlaneVector = AZStd::vector; + //! Sets a list of occlusion planes to be used during the culling process. - void SetOcclusionCullingPlanes(const AZStd::vector& occlusionCullingPlanes) { m_occlusionCullingPlanes = occlusionCullingPlanes; } + void SetOcclusionPlanes(const OcclusionPlaneVector& occlusionPlanes) { m_occlusionPlanes = occlusionPlanes; } //! Notifies the CullingScene that culling will begin for this frame. void BeginCulling(const AZStd::vector& views); @@ -258,7 +270,7 @@ namespace AZ AzFramework::IVisibilityScene* m_visScene = nullptr; CullingDebugContext m_debugCtx; AZStd::concurrency_checker m_cullDataConcurrencyCheck; - AZStd::vector m_occlusionCullingPlanes; + OcclusionPlaneVector m_occlusionPlanes; }; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index 3f28805888..7ee2c4a8d2 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -546,60 +546,37 @@ namespace AZ #if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED // setup occlusion culling, if necessary - MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionCullingPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); + MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); if (maskedOcclusionCulling) { // frustum cull occlusion planes - using OccluderEntry = AZStd::pair; - AZStd::vector visibleOccluders; - for (const AZ::Transform& transform : m_occlusionCullingPlanes) + using VisibleOcclusionPlane = AZStd::pair; + AZStd::vector visibleOccluders; + for (const auto& occlusionPlane : m_occlusionPlanes) { - static const AZ::Vector3 BL(-0.5f, -0.5f, 0.0f); - static const AZ::Vector3 TR(0.5f, 0.5f, 0.0f); - - AZ::Vector3 P1 = transform.TransformPoint(BL); - AZ::Vector3 P2 = transform.TransformPoint(TR); - - AZ::Vector3 aabbMin = P1.GetMin(P2); - AZ::Vector3 aabbMax = P1.GetMax(P2); - - AZ::Aabb occluderAabb = Aabb::CreateFromMinMax(aabbMin, aabbMax); - if (ShapeIntersection::Overlaps(frustum, occluderAabb)) + if (ShapeIntersection::Overlaps(frustum, occlusionPlane.m_aabb)) { // occluder is visible, compute view space distance and add to list - float depth = (view.GetWorldToViewMatrix() * occluderAabb.GetMin()).GetZ(); - depth = AZStd::min(depth, (view.GetWorldToViewMatrix() * occluderAabb.GetMax()).GetZ()); + float depth = (view.GetWorldToViewMatrix() * occlusionPlane.m_aabb.GetMin()).GetZ(); + depth = AZStd::min(depth, (view.GetWorldToViewMatrix() * occlusionPlane.m_aabb.GetMax()).GetZ()); - visibleOccluders.push_back(AZStd::make_pair(transform, depth)); + visibleOccluders.push_back(AZStd::make_pair(occlusionPlane, depth)); } } // sort the occlusion planes by view space distance, front-to-back - AZStd::sort(visibleOccluders.begin(), visibleOccluders.end(), [](const OccluderEntry& LHS, const OccluderEntry& RHS) + AZStd::sort(visibleOccluders.begin(), visibleOccluders.end(), [](const VisibleOcclusionPlane& LHS, const VisibleOcclusionPlane& RHS) { return LHS.second > RHS.second; }); - for (const OccluderEntry& occluder : visibleOccluders) + for (const VisibleOcclusionPlane& occlusionPlane: visibleOccluders) { - const AZ::Transform& transform = occluder.first; - - // find the corners of the plane - static const Vector3 BL = Vector3(-0.5f, -0.5f, 0.0f); - static const Vector3 BR = Vector3(0.5f, -0.5f, 0.0f); - static const Vector3 TL = Vector3(-0.5f, 0.5f, 0.0f); - static const Vector3 TR = Vector3(0.5f, 0.5f, 0.0f); - - Vector3 planeBL = transform.TransformPoint(BL); - Vector3 planeBR = transform.TransformPoint(BR); - Vector3 planeTL = transform.TransformPoint(TL); - Vector3 planeTR = transform.TransformPoint(TR); - // convert to clip-space - Vector4 projectedBL = view.GetWorldToClipMatrix() * Vector4(planeBL); - Vector4 projectedBR = view.GetWorldToClipMatrix() * Vector4(planeBR); - Vector4 projectedTL = view.GetWorldToClipMatrix() * Vector4(planeTL); - Vector4 projectedTR = view.GetWorldToClipMatrix() * Vector4(planeTR); + Vector4 projectedBL = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBL); + Vector4 projectedBR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBR); + Vector4 projectedTL = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerTL); + Vector4 projectedTR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerTR); // store to float array float verts[16]; From 2449a9322d20d8e91afd4b7cb8813fbcd628429f Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Thu, 3 Jun 2021 18:40:44 -0700 Subject: [PATCH 44/71] Changed the occlusion culling plane model to be on the XZ plane and adjusted the corner point computations --- .../Common/Assets/Models/OcclusionCullingPlane.fbx | 4 ++-- .../OcclusionCullingPlaneFeatureProcessor.cpp | 10 +++++----- Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h | 2 +- Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx b/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx index b274bfa282..f91d1015f9 100644 --- a/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx +++ b/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a1f8d75dcd85e8b4aa57f6c0c81af0300ff96915ba3c2b591095c215d5e1d8c -size 12072 +oid sha256:75cdf73fcb9698a76a38294a1cf927a4fb41a34869e0429e1f02bf8d361a7258 +size 20400 diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp index ff7c32ba08..3c61f616d9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp @@ -64,18 +64,18 @@ namespace AZ RPI::CullingScene::OcclusionPlane rpiOcclusionPlane; - static const Vector3 BL = Vector3(-0.5f, -0.5f, 0.0f); - static const Vector3 BR = Vector3(0.5f, -0.5f, 0.0f); - static const Vector3 TL = Vector3(-0.5f, 0.5f, 0.0f); - static const Vector3 TR = Vector3(0.5f, 0.5f, 0.0f); + static const Vector3 BL = Vector3(-0.5f, 0.0f, -0.5f); + static const Vector3 TL = Vector3(-0.5f, 0.0f, 0.5f); + static const Vector3 TR = Vector3( 0.5f, 0.0f, 0.5f); + static const Vector3 BR = Vector3( 0.5f, 0.0f, -0.5f); const AZ::Transform& transform = occlusionCullingPlane->GetTransform(); // convert corners to world space rpiOcclusionPlane.m_cornerBL = transform.TransformPoint(BL); - rpiOcclusionPlane.m_cornerBR = transform.TransformPoint(BR); rpiOcclusionPlane.m_cornerTL = transform.TransformPoint(TL); rpiOcclusionPlane.m_cornerTR = transform.TransformPoint(TR); + rpiOcclusionPlane.m_cornerBR = transform.TransformPoint(BR); // build world space AABB AZ::Vector3 aabbMin = rpiOcclusionPlane.m_cornerBL.GetMin(rpiOcclusionPlane.m_cornerTR); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index 2a9c133b5c..2354a4feea 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -219,9 +219,9 @@ namespace AZ { // World space corners of the occluson plane Vector3 m_cornerBL; - Vector3 m_cornerBR; Vector3 m_cornerTL; Vector3 m_cornerTR; + Vector3 m_cornerBR; Aabb m_aabb; }; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index 7ee2c4a8d2..9f0a17f294 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -574,18 +574,18 @@ namespace AZ { // convert to clip-space Vector4 projectedBL = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBL); - Vector4 projectedBR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBR); Vector4 projectedTL = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerTL); Vector4 projectedTR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerTR); + Vector4 projectedBR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBR); // store to float array float verts[16]; projectedBL.StoreToFloat4(&verts[0]); - projectedBR.StoreToFloat4(&verts[4]); - projectedTL.StoreToFloat4(&verts[8]); - projectedTR.StoreToFloat4(&verts[12]); + projectedTL.StoreToFloat4(&verts[4]); + projectedTR.StoreToFloat4(&verts[8]); + projectedBR.StoreToFloat4(&verts[12]); - static uint32_t indices[6] = { 0, 2, 1, 2, 3, 1 }; + static uint32_t indices[6] = { 0, 1, 2, 2, 3, 0 }; // render into the occlusion buffer, specifying BACKFACE_NONE so it functions as a double-sided occluder maskedOcclusionCulling->RenderTriangles((float*)verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE); From fefa46dd6a8c563853b2cae7d3498ad617252881 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Fri, 4 Jun 2021 02:39:25 -0700 Subject: [PATCH 45/71] Added DiffuseGlobalIlluminationFeatureProcessor and moved the DiffuseProbeGrid files to the DiffuseGlobalIllumination directory --- .../DiffuseComposite.azsl | 12 +- .../DiffuseProbeGridDownsample.azsl | 12 +- ...balIlluminationFeatureProcessorInterface.h | 40 +++++++ ...iffuseProbeGridFeatureProcessorInterface.h | 0 .../Code/Source/CommonSystemComponent.cpp | 20 ++-- ...fuseGlobalIlluminationFeatureProcessor.cpp | 113 ++++++++++++++++++ ...iffuseGlobalIlluminationFeatureProcessor.h | 52 ++++++++ .../DiffuseProbeGrid.cpp | 2 +- .../DiffuseProbeGrid.h | 2 +- .../DiffuseProbeGridBlendDistancePass.cpp | 4 +- .../DiffuseProbeGridBlendDistancePass.h | 0 .../DiffuseProbeGridBlendIrradiancePass.cpp | 4 +- .../DiffuseProbeGridBlendIrradiancePass.h | 0 .../DiffuseProbeGridBorderUpdatePass.cpp | 4 +- .../DiffuseProbeGridBorderUpdatePass.h | 0 .../DiffuseProbeGridClassificationPass.cpp | 4 +- .../DiffuseProbeGridClassificationPass.h | 3 +- .../DiffuseProbeGridFeatureProcessor.cpp | 2 +- .../DiffuseProbeGridFeatureProcessor.h | 4 +- .../DiffuseProbeGridRayTracingPass.cpp | 4 +- .../DiffuseProbeGridRayTracingPass.h | 2 +- .../DiffuseProbeGridRelocationPass.cpp | 4 +- .../DiffuseProbeGridRelocationPass.h | 3 +- .../DiffuseProbeGridRenderPass.cpp | 4 +- .../DiffuseProbeGridRenderPass.h | 0 .../DiffuseProbeGridTextureReadback.cpp | 4 +- .../DiffuseProbeGridTextureReadback.h | 2 +- .../Code/atom_feature_common_files.cmake | 42 +++---- .../atom_feature_common_public_files.cmake | 3 +- ...DiffuseGlobalIlluminationComponentConfig.h | 10 +- ...eGlobalIlluminationComponentController.cpp | 25 ++-- ...useGlobalIlluminationComponentController.h | 7 +- .../DiffuseProbeGridComponentController.h | 2 +- 33 files changed, 291 insertions(+), 99 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h rename Gems/Atom/Feature/Common/Code/Include/Atom/Feature/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridFeatureProcessorInterface.h (100%) create mode 100644 Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp create mode 100644 Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGrid.cpp (99%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGrid.h (99%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridBlendDistancePass.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridBlendDistancePass.h (100%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridBlendIrradiancePass.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridBlendIrradiancePass.h (100%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridBorderUpdatePass.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridBorderUpdatePass.h (100%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridClassificationPass.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridClassificationPass.h (97%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridFeatureProcessor.cpp (99%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridFeatureProcessor.h (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridRayTracingPass.cpp (99%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridRayTracingPass.h (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridRelocationPass.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridRelocationPass.h (97%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridRenderPass.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridRenderPass.h (100%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridTextureReadback.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridTextureReadback.h (96%) diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl index b3b26c4d42..3e2fda8d5d 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl @@ -37,6 +37,9 @@ ShaderResourceGroup PassSrg : SRG_PerPass AddressV = Clamp; AddressW = Clamp; }; + + // scale multiplier of the downsampled size to the fullscreen size (e.g., 4) + uint m_imageScale; } #include @@ -148,13 +151,10 @@ float3 SampleGlobalIBL(uint sampleIndex, uint2 screenCoords, float depth, float3 PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) { uint2 screenCoords = IN.m_position.xy; - - // [GFX TODO][ATOM-6172] Add image scale PassSrg constant to the DiffuseProbeGrid downsample/upsample - const uint ImageScale = 4; - const float ImageScaleInverse = 1.0f / ImageScale; + float imageScaleInverse = 1.0f / PassSrg::m_imageScale; // compute image coords for the downsampled probe irradiance image - uint2 probeIrradianceCoords = screenCoords * ImageScaleInverse; + uint2 probeIrradianceCoords = screenCoords * imageScaleInverse; float depth = PassSrg::m_depth.Load(screenCoords, sampleIndex).r; float4 encodedNormal = PassSrg::m_normal.Load(screenCoords, sampleIndex); @@ -165,7 +165,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) float3 diffuse = float3(0.0f, 0.0f, 0.0f); if (useProbeIrradiance > 0.0f) { - float3 irradiance = SampleProbeIrradiance(sampleIndex, probeIrradianceCoords, depth, normal, albedo, ImageScale); + float3 irradiance = SampleProbeIrradiance(sampleIndex, probeIrradianceCoords, depth, normal, albedo, PassSrg::m_imageScale); diffuse = (albedo.rgb / PI) * irradiance; } else diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl index e051317567..85772c6577 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl @@ -31,6 +31,9 @@ ShaderResourceGroup PassSrg : SRG_PerPass AddressV = Clamp; AddressW = Clamp; }; + + // scale multiplier of the downsampled size to the fullscreen size (e.g., 4) + uint m_outputImageScale; } #include @@ -56,16 +59,13 @@ struct PSOutput // Pixel Shader PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) { - // the downsample is 1/4 resolution - // [GFX TODO][ATOM-6172] Add image scale PassSrg constant to the DiffuseProbeGrid downsample/upsample - const uint ImageScale = 4; - uint2 screenCoords = IN.m_position.xy * ImageScale; + uint2 screenCoords = IN.m_position.xy * PassSrg::m_outputImageScale; float downsampledDepth = 0; float4 downsampledEncodedNormal; - for (uint y = 0; y < ImageScale; ++y) + for (uint y = 0; y < PassSrg::m_outputImageScale; ++y) { - for (uint x = 0; x < ImageScale; ++x) + for (uint x = 0; x < PassSrg::m_outputImageScale; ++x) { float depth = PassSrg::m_depth.Load(screenCoords + int2(x, y), sampleIndex).r; float4 encodedNormal = PassSrg::m_normal.Load(screenCoords + int2(x, y), sampleIndex); diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h new file mode 100644 index 0000000000..88faac1728 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h @@ -0,0 +1,40 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + enum class DiffuseGlobalIlluminationQualityLevel : uint8_t + { + Low, + Medium, + High + }; + + //! This class provides general features and configuration for the diffuse global illumination environment, + //! which consists of DiffuseProbeGrids and the diffuse Global IBL cubemap. + class DiffuseGlobalIlluminationFeatureProcessorInterface + : public RPI::FeatureProcessor + { + public: + AZ_RTTI(AZ::Render::DiffuseProbeGridFeatureProcessorInterface, "{BD8CA35A-47C3-4FD8-932B-18495EF07527}"); + + virtual void SetQualityLevel(DiffuseGlobalIlluminationQualityLevel qualityLevel) = 0; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessorInterface.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h rename to Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessorInterface.h diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index 00c55cbade..87e134477a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -91,14 +91,15 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -135,6 +136,7 @@ namespace AZ LightingPreset::Reflect(context); ModelPreset::Reflect(context); DiffuseProbeGridFeatureProcessor::Reflect(context); + DiffuseGlobalIlluminationFeatureProcessor::Reflect(context); RayTracingFeatureProcessor::Reflect(context); if (SerializeContext* serialize = azrtti_cast(context)) @@ -191,6 +193,7 @@ namespace AZ AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); + AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); // Add SkyBox pass @@ -285,6 +288,7 @@ namespace AZ void CommonSystemComponent::Deactivate() { AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); + AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp new file mode 100644 index 0000000000..5040665456 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp @@ -0,0 +1,113 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void DiffuseGlobalIlluminationFeatureProcessor::Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext + ->Class() + ->Version(0); + } + } + + void DiffuseGlobalIlluminationFeatureProcessor::Activate() + { + EnableSceneNotification(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::Deactivate() + { + DisableSceneNotification(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::SetQualityLevel(DiffuseGlobalIlluminationQualityLevel qualityLevel) + { + m_qualityLevel = qualityLevel; + + UpdatePasses(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::OnRenderPipelinePassesChanged([[maybe_unused]] RPI::RenderPipeline* renderPipeline) + { + UpdatePasses(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::OnRenderPipelineAdded([[maybe_unused]] RPI::RenderPipelinePtr pipeline) + { + UpdatePasses(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::UpdatePasses() + { + float sizeMultiplier = 0.0f; + switch (m_qualityLevel) + { + case DiffuseGlobalIlluminationQualityLevel::Low: + sizeMultiplier = 0.25f; + break; + case DiffuseGlobalIlluminationQualityLevel::Medium: + sizeMultiplier = 0.5f; + break; + case DiffuseGlobalIlluminationQualityLevel::High: + sizeMultiplier = 1.0f; + break; + default: + AZ_Assert(false, "Unknown DiffuseGlobalIlluminationQualityLevel [%d]", m_qualityLevel); + break; + } + + // update the size multiplier on the DiffuseProbeGridDownsamplePass output + AZStd::vector downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") }; + RPI::PassHierarchyFilter downsamplePassFilter(downsamplePassHierarchy); + const AZStd::vector& downsamplePasses = RPI::PassSystemInterface::Get()->FindPasses(downsamplePassFilter); + for (RPI::Pass* pass : downsamplePasses) + { + for (uint32_t outputIndex = 0; outputIndex < pass->GetOutputCount(); ++outputIndex) + { + RPI::Ptr outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment; + RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers; + + sizeMultipliers.m_widthMultiplier = sizeMultiplier; + sizeMultipliers.m_heightMultiplier = sizeMultiplier; + } + + // set the output scale on the PassSrg + RPI::FullscreenTrianglePass* downsamplePass = static_cast(pass); + auto constantIndex = downsamplePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_outputImageScale")); + downsamplePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + } + + // update the image scale on the DiffuseComposite pass + AZStd::vector compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") }; + RPI::PassHierarchyFilter compositePassFilter(compositePassHierarchy); + const AZStd::vector& compositePasses = RPI::PassSystemInterface::Get()->FindPasses(compositePassFilter); + for (RPI::Pass* pass : compositePasses) + { + RPI::FullscreenTrianglePass* compositePass = static_cast(pass); + auto constantIndex = compositePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_imageScale")); + compositePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + } + } + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h new file mode 100644 index 0000000000..81dc1487fa --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h @@ -0,0 +1,52 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace AZ +{ + namespace Render + { + //! This class provides general features and configuration for the diffuse global illumination environment, + //! which consists of DiffuseProbeGrids and the diffuse Global IBL cubemap. + class DiffuseGlobalIlluminationFeatureProcessor final + : public DiffuseGlobalIlluminationFeatureProcessorInterface + { + public: + AZ_RTTI(AZ::Render::DiffuseGlobalIlluminationFeatureProcessor, "{14F7DF46-AA2C-49EF-8A2C-0A7CB7390BB7}", DiffuseGlobalIlluminationFeatureProcessorInterface); + + static void Reflect(AZ::ReflectContext* context); + + DiffuseGlobalIlluminationFeatureProcessor() = default; + virtual ~DiffuseGlobalIlluminationFeatureProcessor() = default; + + void Activate() override; + void Deactivate() override; + + // DiffuseGlobalIlluminationFeatureProcessorInterface overrides + void SetQualityLevel(DiffuseGlobalIlluminationQualityLevel qualityLevel) override; + + private: + AZ_DISABLE_COPY_MOVE(DiffuseGlobalIlluminationFeatureProcessor); + + // RPI::SceneNotificationBus::Handler overrides + void OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) override; + void OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) override; + + void UpdatePasses(); + + DiffuseGlobalIlluminationQualityLevel m_qualityLevel = DiffuseGlobalIlluminationQualityLevel::Low; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp similarity index 99% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp index a55d8fc78c..cfdfd9efa4 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h similarity index 99% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h index ff6ad719cf..4a732ae7af 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h @@ -17,7 +17,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp index 2a06dacf3f..04e68136f1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -18,7 +17,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.h diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp index 4818018ea3..f1fe792542 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -18,7 +17,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.h diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp index 8821de8a9d..a24d54d9b7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -18,7 +17,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.h diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp index 65f1c2dd5a..8d39e6fcc8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -22,7 +21,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.h similarity index 97% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.h index 677e16ac42..eef59c3753 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.h @@ -12,7 +12,6 @@ #pragma once #include - #include #include #include @@ -22,7 +21,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp similarity index 99% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp index 060d51d1d0..ba24bcc001 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h index 19e9bf1b1d..3fb70830d0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h @@ -12,8 +12,8 @@ #pragma once -#include -#include +#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.cpp similarity index 99% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.cpp index 65d71b8272..c77f66645e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -25,7 +24,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.h similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.h index bb35803e51..910537a2f1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp index 86a26f002d..2f0ed373b9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -22,7 +21,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.h similarity index 97% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.h index 437ac2f3f5..1900f8b786 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.h @@ -12,7 +12,6 @@ #pragma once #include - #include #include #include @@ -22,7 +21,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.cpp index 4f9221a65f..f025079994 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.cpp @@ -10,12 +10,12 @@ * */ -#include -#include #include #include #include #include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.h diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.cpp index bc619cc277..5465d0da33 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.cpp @@ -10,8 +10,8 @@ * */ -#include -#include +#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.h similarity index 96% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.h index 1becd6fb3e..6f7ebb9240 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.h @@ -14,7 +14,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 76b71e5fae..0df5501803 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -119,26 +119,28 @@ set(FILES Source/Decals/AsyncLoadTracker.h Source/Decals/DecalTextureArrayFeatureProcessor.h Source/Decals/DecalTextureArrayFeatureProcessor.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h - Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.h - Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.h - Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.h - Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.h - Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.h - Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.h - Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp - Source/DiffuseProbeGrid/DiffuseProbeGrid.h - Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h - Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h - Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp Source/DisplayMapper/AcesOutputTransformPass.cpp Source/DisplayMapper/AcesOutputTransformLutPass.cpp Source/DisplayMapper/ApplyShaperLookupTablePass.cpp diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake index 9034859707..9f9c64dd46 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake @@ -22,7 +22,8 @@ set(FILES Include/Atom/Feature/CoreLights/SimpleSpotLightFeatureProcessorInterface.h Include/Atom/Feature/CoreLights/ShadowConstants.h Include/Atom/Feature/Decals/DecalFeatureProcessorInterface.h - Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h + Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessorInterface.h + Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h Include/Atom/Feature/DisplayMapper/DisplayMapperFeatureProcessorInterface.h Include/Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h index 23296967a5..fb99a99e1a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h @@ -14,20 +14,12 @@ #include #include +#include namespace AZ { namespace Render { - enum class DiffuseGlobalIlluminationQualityLevel : uint32_t - { - Low, - Medium, - High, - - Count - }; - class DiffuseGlobalIlluminationComponentConfig final : public ComponentConfig { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp index 4c7f37bd4a..a9feb0185c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp @@ -11,11 +11,8 @@ */ #include - -//#include - +#include #include -//#include namespace AZ { @@ -55,18 +52,22 @@ namespace AZ void DiffuseGlobalIlluminationComponentController::Activate(EntityId entityId) { - m_entityId = entityId; + AZ_UNUSED(entityId); + + const RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); + m_featureProcessor = scene->GetFeatureProcessor(); + + OnConfigChanged(); } void DiffuseGlobalIlluminationComponentController::Deactivate() { - //m_postProcessInterface = nullptr; - m_entityId.SetInvalid(); } void DiffuseGlobalIlluminationComponentController::SetConfiguration(const DiffuseGlobalIlluminationComponentConfig& config) { m_configuration = config; + OnConfigChanged(); } @@ -77,15 +78,7 @@ namespace AZ void DiffuseGlobalIlluminationComponentController::OnConfigChanged() { - // Register the configuration with the AcesDisplayMapperFeatureProcessor for this scene. - //const AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); - //DisplayMapperFeatureProcessorInterface* fp = scene->GetFeatureProcessor(); - //DisplayMapperConfigurationDescriptor desc; - //desc.m_operationType = m_configuration.m_displayMapperOperation; - //desc.m_ldrGradingLutEnabled = m_configuration.m_ldrColorGradingLutEnabled; - //desc.m_ldrColorGradingLut = m_configuration.m_ldrColorGradingLut; - //desc.m_acesParameterOverrides = m_configuration.m_acesParameterOverrides; - //fp->RegisterDisplayMapperConfiguration(desc); + m_featureProcessor->SetQualityLevel(m_configuration.m_qualityLevel); } } // namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h index 8700e1ffb5..81da772129 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h @@ -14,12 +14,9 @@ #include #include - +#include #include -//#include -//#include - namespace AZ { namespace Render @@ -49,7 +46,7 @@ namespace AZ void OnConfigChanged(); DiffuseGlobalIlluminationComponentConfig m_configuration; - EntityId m_entityId; + DiffuseGlobalIlluminationFeatureProcessorInterface* m_featureProcessor = nullptr; }; } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h index ef606d2170..51a17cb2cf 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include From 5d4226df16a404708b69cb5a46e8aa297adb1767 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Fri, 4 Jun 2021 17:28:43 +0000 Subject: [PATCH 46/71] Get a mesh's morph targets based on the scene graph hierarchy, instead of mesh name (#1128) When building a mesh's morph targets, the exporter has to identify the base mesh in addition to each morph target mesh. Previously this was done by searching the entire scene graph for nodes * of type IBlendShapeData * whose parent's name matches the name of the Atom model This is problematic for a few reasons. The first is that the Atom model's name may have been based on the optimized mesh node. When this happens, the `OptimizedMeshSuffix` that is used in the Scene Graph node's name is stripped off of the Atom model's name. The result is that the *unoptimized* mesh is used as the base mesh for the blend shapes, instead of the optimized blend shape. This of course results in disaster, since the optimizer reorders the vertices, and the base mesh will not match the optimized one. The second is that it is not really necessary to do the search based on the node name at all. All of a mesh's blend shapes are child nodes of the base IMeshData node. With this change, the base mesh is located based on the node data pointer, and all of its child IBlendShapeData nodes are added to the set of blend shapes to process. This way, the Atom model's name isn't involved in the lookup. --- .../Model/MorphTargetExporter.cpp | 83 ++++++++----------- .../RPI.Builders/Model/MorphTargetExporter.h | 5 +- 2 files changed, 36 insertions(+), 52 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.cpp index 3d0cbca8e6..f14d73a9cf 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.cpp @@ -14,9 +14,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -27,62 +29,42 @@ namespace AZ::RPI AZStd::unordered_map MorphTargetExporter::GetBlendShapeInfos( const Containers::Scene& scene, - const AZStd::optional& filterMeshName) const + const MeshData* meshData) const { const Containers::SceneGraph& sceneGraph = scene.GetGraph(); - const auto contentStorage = sceneGraph.GetContentStorage(); - const auto nameStorage = sceneGraph.GetNameStorage(); - AZStd::unordered_map result; + const auto foundBaseMeshIter = AZStd::find_if(sceneGraph.GetContentStorage().cbegin(), sceneGraph.GetContentStorage().cend(), [meshData](const auto& nodeData) + { + return nodeData.get() == meshData; + }); + if (foundBaseMeshIter == sceneGraph.GetContentStorage().cend()) + { + return {}; + } + + const auto baseMeshNodeIndex = sceneGraph.ConvertToNodeIndex(foundBaseMeshIter); - const auto keyValueView = Containers::Views::MakePairView(nameStorage, contentStorage); - const auto filteredView = Containers::Views::MakeFilterView(keyValueView, Containers::DerivedTypeFilter()); - for (const auto& [name, object] : filteredView) + const auto childBlendShapeDatas = Containers::MakeDerivedFilterView( + Containers::Views::MakeSceneGraphChildView(sceneGraph, baseMeshNodeIndex, sceneGraph.GetContentStorage().cbegin(), true) + ); + + AZStd::unordered_map result; + for (auto it = childBlendShapeDatas.cbegin(); it != childBlendShapeDatas.cend(); ++it) { - const Containers::SceneGraph::NodeIndex sceneNodeIndex = sceneGraph.Find(name.GetPath()); + const Containers::SceneGraph::NodeIndex blendShapeNodeIndex = sceneGraph.ConvertToNodeIndex(it.GetBaseIterator().GetBaseIterator().GetHierarchyIterator()); AZStd::set types; - Events::GraphMetaInfoBus::Broadcast(&Events::GraphMetaInfo::GetVirtualTypes, types, scene, sceneNodeIndex); - if (types.find(Events::GraphMetaInfo::GetIgnoreVirtualType()) == types.end()) + Events::GraphMetaInfoBus::Broadcast(&Events::GraphMetaInfo::GetVirtualTypes, types, scene, blendShapeNodeIndex); + if (!types.contains(Events::GraphMetaInfo::GetIgnoreVirtualType())) { - const char* sceneNodePath = name.GetPath(); - const Containers::SceneGraph::NodeIndex nodeIndex = sceneGraph.Find(sceneNodePath); - if (nodeIndex.IsValid()) - { - const AZStd::string meshNodeName = SourceBlendShapeInfo::GetMeshNodeName(sceneGraph, nodeIndex); - if (!filterMeshName.has_value() || - (filterMeshName.has_value() && filterMeshName.value() == meshNodeName)) - { - const AZStd::string blendShapeName = sceneGraph.GetNodeName(nodeIndex).GetName(); - SourceBlendShapeInfo& blendShapeInfo = result[blendShapeName]; - blendShapeInfo.m_sceneNodeIndices.push_back(nodeIndex); - } - } - else - { - AZ_Warning(ModelAssetBuilderComponent::s_builderName, false, "Cannot retrieve scene graph index for blend shape node with path %s.", sceneNodePath); - } + const AZStd::string blendShapeName{sceneGraph.GetNodeName(blendShapeNodeIndex).GetName(), sceneGraph.GetNodeName(blendShapeNodeIndex).GetNameLength()}; + result[blendShapeName].m_sceneNodeIndices.emplace_back(blendShapeNodeIndex); } } return result; } - AZStd::string MorphTargetExporter::SourceBlendShapeInfo::GetMeshNodeName(const Containers::SceneGraph& sceneGraph, - const Containers::SceneGraph::NodeIndex& sceneNodeIndex) - { - const auto* blendShapeData = - azrtti_cast(sceneGraph.GetNodeContent(sceneNodeIndex).get()); - AZ_Assert(blendShapeData, "Cannot get mesh node name from scene node. Node is expected to be a blend shape."); - if (blendShapeData) - { - Containers::SceneGraph::NodeIndex morphMeshParentIndex = sceneGraph.GetNodeParent(sceneNodeIndex); - return sceneGraph.GetNodeName(morphMeshParentIndex).GetName(); - } - - return {}; - } - void MorphTargetExporter::ProduceMorphTargets(const Containers::Scene& scene, uint32_t vertexOffset, const ModelAssetBuilderComponent::SourceMeshContent& sourceMesh, @@ -92,9 +74,14 @@ namespace AZ::RPI { const Containers::SceneGraph& sceneGraph = scene.GetGraph(); +#if defined(AZ_ENABLE_TRACING) + const auto baseMeshIt = AZStd::find(sceneGraph.GetContentStorage().cbegin(), sceneGraph.GetContentStorage().cend(), sourceMesh.m_meshData); + const Containers::SceneGraph::NodeIndex baseMeshIndex = sceneGraph.ConvertToNodeIndex(baseMeshIt); + const AZStd::string_view baseMeshName{sceneGraph.GetNodeName(baseMeshIndex).GetName(), sceneGraph.GetNodeName(baseMeshIndex).GetNameLength()}; +#endif + // Get the blend shapes for the given mesh - const AZStd::string_view meshName = sourceMesh.m_name.GetStringView(); - AZStd::unordered_map blendShapeInfos = GetBlendShapeInfos(scene, meshName); + AZStd::unordered_map blendShapeInfos = GetBlendShapeInfos(scene, sourceMesh.m_meshData.get()); for (const auto& iter : blendShapeInfos) { @@ -109,12 +96,12 @@ namespace AZ::RPI { #if defined(AZ_ENABLE_TRACING) const Containers::SceneGraph::NodeIndex morphMeshParentIndex = sceneGraph.GetNodeParent(sceneNodeIndex); - const char* meshNodeName = sceneGraph.GetNodeName(morphMeshParentIndex).GetName(); + const AZStd::string_view sourceMeshName{sceneGraph.GetNodeName(morphMeshParentIndex).GetName(), sceneGraph.GetNodeName(morphMeshParentIndex).GetNameLength()}; #endif - AZ_Assert(AZ::StringFunc::Equal(sourceMesh.m_name.GetCStr(), meshNodeName, /*bCaseSensitive=*/true), - "Scene graph mesh node (%s) has a different name than the product mesh (%s).", - meshNodeName, sourceMesh.m_name.GetCStr()); + AZ_Assert(AZ::StringFunc::Equal(baseMeshName, sourceMeshName, /*bCaseSensitive=*/true), + "Scene graph mesh node (%.*s) has a different name than the product mesh (%.*s).", + AZ_STRING_ARG(sourceMeshName), AZ_STRING_ARG(baseMeshName)); const DataTypes::MatrixType globalTransform = Utilities::BuildWorldTransform(sceneGraph, sceneNodeIndex); BuildMorphTargetMesh(vertexOffset, sourceMesh, productMesh, metaAssetCreator, blendShapeName, blendShapeData, globalTransform, coordSysConverter, scene.GetSourceFilename()); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.h b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.h index 4845d7d1da..32f9e2e508 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.h +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.h @@ -39,12 +39,9 @@ namespace AZ struct SourceBlendShapeInfo { AZStd::vector m_sceneNodeIndices; - - static AZStd::string GetMeshNodeName(const AZ::SceneAPI::Containers::SceneGraph& sceneGraph, - const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& sceneNodeIndex); }; //! Retrieve all scene graph nodes per blend shape for all available blend shapes. - AZStd::unordered_map GetBlendShapeInfos(const AZ::SceneAPI::Containers::Scene& scene, const AZStd::optional& filterMeshName = AZStd::nullopt) const; + AZStd::unordered_map GetBlendShapeInfos(const AZ::SceneAPI::Containers::Scene& scene, const MeshData* meshData) const; //! Calculate position delta tolerance that is used to indicate whether a given vertex is part of the sparse set of morphed vertices //! or if it will be skipped and optimized out due to a hardly visible or no movement at all. From dcdd63966ed43ec80f56aca75c4ba9a80fcc201a Mon Sep 17 00:00:00 2001 From: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com> Date: Fri, 4 Jun 2021 10:28:56 -0700 Subject: [PATCH 47/71] ATOM-15658 Better option of CreateCommonBuffer requires unique buffer name (#1133) * ATOM-15658 Better option of CreateCommonBuffer requires unique buffer name - Change the CreateCommonBuffer function to not require an unique name by default. - Remove the code for generating unique buffer names. - Add buffer name to BufferAsset so it can be used for device object name instead of using asset file name. - Change RPI::Buffer to use BufferName_AssetUuid as attachment id. --- .../Source/CoreLights/LightCullingPass.cpp | 7 +---- .../Source/CoreLights/LightCullingRemap.cpp | 7 +---- .../Common/Code/Source/Math/MathFilter.cpp | 1 + .../ExposureControlSettings.cpp | 11 ++------ .../ExposureControl/ExposureControlSettings.h | 4 +-- .../DepthOfFieldReadBackFocusDepthPass.cpp | 3 +- .../ExposureControlRenderProxy.cpp | 1 + .../PostProcessing/EyeAdaptationPass.cpp | 6 ++-- .../LuminanceHistogramGeneratorPass.cpp | 7 +---- .../RayTracing/RayTracingFeatureProcessor.cpp | 8 ++---- .../Source/SkyBox/SkyBoxFeatureProcessor.cpp | 2 +- .../TransformServiceFeatureProcessor.cpp | 6 ++-- .../Code/Source/Utils/GpuBufferHandler.cpp | 4 +-- .../Include/Atom/RPI.Public/Buffer/Buffer.h | 2 ++ .../Atom/RPI.Public/Buffer/BufferSystem.h | 2 +- .../RPI.Public/Buffer/BufferSystemInterface.h | 5 +++- .../Atom/RPI.Reflect/Buffer/BufferAsset.h | 4 +++ .../Model/ModelAssetBuilderComponent.cpp | 2 +- .../Code/Source/RPI.Public/Buffer/Buffer.cpp | 17 +++++++---- .../Source/RPI.Public/Buffer/BufferSystem.cpp | 27 +++++++++++------- .../DynamicDraw/DynamicBufferAllocator.cpp | 2 +- .../Source/RPI.Reflect/Buffer/BufferAsset.cpp | 8 +++++- .../RPI.Reflect/Buffer/BufferAssetCreator.cpp | 5 +++- .../RPI/Code/Tests/Buffer/BufferTests.cpp | 28 +++++++++++++++++-- .../EMotionFXAtom/Code/Source/ActorAsset.cpp | 2 +- 25 files changed, 99 insertions(+), 72 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.cpp index 987ed299b3..a9b43ef1e7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.cpp @@ -311,14 +311,9 @@ namespace AZ { auto tileBufferResolution = GetTileDataBufferResolution(); - // generate a UUID for the buffer name to keep it unique when there are multiple render pipelines - AZ::Uuid uuid = AZ::Uuid::CreateRandom(); - AZStd::string uuidString; - uuid.ToString(uuidString); - RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadWrite; - desc.m_bufferName = AZStd::string::format("LightList_%s", uuidString.c_str()); + desc.m_bufferName = "LightList"; desc.m_elementSize = sizeof(uint32_t); desc.m_byteCount = tileBufferResolution.m_width * tileBufferResolution.m_height * 256 * sizeof(uint32_t); m_lightList = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.cpp index 42882cec6e..26e4ed9f6e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.cpp @@ -118,14 +118,9 @@ namespace AZ void LightCullingRemap::CreateRemappedLightListBuffer() { - // generate a UUID for the buffer name to keep it unique when there are multiple render pipelines - AZ::Uuid uuid = AZ::Uuid::CreateRandom(); - AZStd::string uuidString; - uuid.ToString(uuidString); - RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadWrite; - desc.m_bufferName = AZStd::string::format("LightListRemapped_%s", uuidString.c_str()); + desc.m_bufferName = "LightListRemapped"; desc.m_elementSize = RHI::GetFormatSize(LightListRemappedFormat); desc.m_byteCount = m_tileDim.m_width * m_tileDim.m_height * NumBins * MaxLightsPerTile * desc.m_elementSize; m_lightListRemapped = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); diff --git a/Gems/Atom/Feature/Common/Code/Source/Math/MathFilter.cpp b/Gems/Atom/Feature/Common/Code/Source/Math/MathFilter.cpp index 8190c3af98..cb6f3e3522 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Math/MathFilter.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Math/MathFilter.cpp @@ -74,6 +74,7 @@ namespace AZ desc.m_elementFormat = filters.front()->GetElementFormat(); desc.m_byteCount = totalElementCount * elementSize; desc.m_bufferData = data.data(); + desc.m_isUniqueName = true; auto buffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp index 056f7b7da4..288dd54e2b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp @@ -100,16 +100,9 @@ namespace AZ bool ExposureControlSettings::InitCommonBuffer() { - // generate a UUID for the buffer name to keep it unique - AZ::Uuid uuid = AZ::Uuid::CreateRandom(); - AZStd::string uuidString; - uuid.ToString(uuidString); - - AZStd::string bufferName = AZStd::string::format("%s_%s", ExposureControlBufferBaseName, uuidString.c_str()); - RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::Constant; - desc.m_bufferName = bufferName; + desc.m_bufferName = ExposureControlBufferName; desc.m_byteCount = sizeof(ShaderParameters); desc.m_elementSize = sizeof(ShaderParameters); @@ -117,7 +110,7 @@ namespace AZ if (!m_buffer) { - AZ_Assert(false, "Failed to create the RPI::Buffer[%s] which is used for the exposure control feature.", bufferName.c_str()); + AZ_Assert(false, "Failed to create the RPI::Buffer[%s] which is used for the exposure control feature.", desc.m_bufferName.c_str()); return false; } diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.h b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.h index 8344d6aa09..bfb4237b96 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.h @@ -28,8 +28,8 @@ namespace AZ { class PostProcessSettings; - // Base name of the buffer used for the exposure control feature. Usually distinct identifier will be added to this name for each exposure control settings. - static const char* const ExposureControlBufferBaseName = "ExposureControlBuffer"; + // Name of the buffer used for the exposure control feature + static const char* const ExposureControlBufferName = "ExposureControlBuffer"; // The post process sub-settings class for the exposure control feature class ExposureControlSettings final diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.cpp index abd916fcc9..c355b7c8a7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.cpp @@ -47,9 +47,8 @@ namespace AZ m_getDepthPass = static_cast(pass.get()); // Create buffer for read back focus depth. We append static counter to avoid name conflicts. - AZStd::string bufferName = AZStd::string::format("DepthOfFieldReadBackAutoFocusDepthBuffer_%d", s_bufferInstance++); RPI::CommonBufferDescriptor desc; - desc.m_bufferName = bufferName; + desc.m_bufferName = "DepthOfFieldReadBackAutoFocusDepthBuffer"; desc.m_poolType = RPI::CommonBufferPoolType::ReadWrite; desc.m_byteCount = sizeof(float); desc.m_elementSize = aznumeric_cast(desc.m_byteCount); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/ExposureControlRenderProxy.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/ExposureControlRenderProxy.cpp index eb7a3b527f..c70d56aa90 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/ExposureControlRenderProxy.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/ExposureControlRenderProxy.cpp @@ -71,6 +71,7 @@ namespace AZ desc.m_bufferName = bufferName; desc.m_byteCount = sizeof(ShaderParameters); desc.m_elementSize = sizeof(ShaderParameters); + desc.m_isUniqueName = true; m_buffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); } diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.cpp index e7d4c47f02..bf293fd3d2 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.cpp @@ -34,7 +34,7 @@ namespace AZ { namespace Render { - static const char* const EyeAdaptationBufferBaseName = "EyeAdaptationBuffer"; + static const char* const EyeAdaptationBufferName = "EyeAdaptationBuffer"; RPI::Ptr EyeAdaptationPass::Create(const RPI::PassDescriptor& descriptor) { @@ -49,12 +49,10 @@ namespace AZ void EyeAdaptationPass::InitBuffer() { - AZStd::string bufferName = AZStd::string::format("%s_%p", EyeAdaptationBufferBaseName, this); - ExposureCalculationData defaultData; RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadWrite; - desc.m_bufferName = bufferName; + desc.m_bufferName = EyeAdaptationBufferName; desc.m_byteCount = sizeof(ExposureCalculationData); desc.m_elementSize = aznumeric_cast(desc.m_byteCount); desc.m_bufferData = &defaultData; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp index 758c21bc4e..715ebf2945 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp @@ -62,14 +62,9 @@ namespace AZ void LuminanceHistogramGeneratorPass::CreateHistogramBuffer() { - // generate a UUID for the buffer name to keep it unique when there are multiple render pipelines - AZ::Uuid uuid = AZ::Uuid::CreateRandom(); - AZStd::string uuidString; - uuid.ToString(uuidString); - RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadWrite; - desc.m_bufferName = AZStd::string::format("LuminanceHistogramBuffer_%s", uuidString.c_str()); + desc.m_bufferName = "LuminanceHistogramBuffer"; desc.m_elementSize = sizeof(uint32_t); desc.m_byteCount = NumHistogramBins * sizeof(uint32_t); desc.m_elementFormat = RHI::Format::R32_UINT; diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp index 10c7c2d378..2db396e36d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp @@ -210,12 +210,10 @@ namespace AZ if (m_meshInfoBuffer == nullptr) { - AZStd::string uuidString = AZ::Uuid::CreateRandom().ToString(); - // allocate the MeshInfo structured buffer RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; - desc.m_bufferName = AZStd::string::format("RayTracingMeshInfo_%s", uuidString.c_str()); + desc.m_bufferName = "RayTracingMeshInfo"; desc.m_byteCount = newMeshByteCount; desc.m_elementSize = sizeof(MeshInfo); m_meshInfoBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); @@ -283,12 +281,10 @@ namespace AZ if (m_materialInfoBuffer == nullptr) { - AZStd::string uuidString = AZ::Uuid::CreateRandom().ToString(); - // allocate the MaterialInfo structured buffer RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; - desc.m_bufferName = AZStd::string::format("RayTracingMaterialInfo_%s", uuidString.c_str()); + desc.m_bufferName = "RayTracingMaterialInfo"; desc.m_byteCount = newMaterialByteCount; desc.m_elementSize = sizeof(MaterialInfo); m_materialInfoBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); diff --git a/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp index 058e243fb7..4a3586a799 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp @@ -193,7 +193,7 @@ namespace AZ RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::Constant; - desc.m_bufferName = AZStd::string::format("SkyboxBuffer_%p", this); + desc.m_bufferName = "SkyboxBuffer"; desc.m_byteCount = byteCount; desc.m_elementSize = byteCount; desc.m_bufferData = &m_physicalSkyData; diff --git a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp index fb73d0f416..074b09e35d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp @@ -89,13 +89,13 @@ namespace AZ // Create the transform buffer, grow by powers of two RPI::CommonBufferDescriptor desc2; desc2.m_poolType = RPI::CommonBufferPoolType::ReadOnly; - desc2.m_bufferName = AZStd::string::format("'m_objectToWorldBuffer_%" PRIXPTR, reinterpret_cast(this)); + desc2.m_bufferName = "m_objectToWorldBuffer"; desc2.m_byteCount = byteCount; desc2.m_elementSize = elementSize; m_objectToWorldBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc2); - desc2.m_bufferName = AZStd::string::format("'m_objectToWorldHistoryBuffer_%p", this); + desc2.m_bufferName = "m_objectToWorldHistoryBuffer"; m_objectToWorldHistoryBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc2); } else @@ -119,7 +119,7 @@ namespace AZ // Create the normal buffer, grow by powers of two RPI::CommonBufferDescriptor desc2; desc2.m_poolType = RPI::CommonBufferPoolType::ReadOnly; - desc2.m_bufferName = AZStd::string::format("'m_objectToWorldInverseTransposeBuffer_%" PRIXPTR, reinterpret_cast(this)); + desc2.m_bufferName = "m_objectToWorldInverseTransposeBuffer"; desc2.m_byteCount = byteCount; desc2.m_elementSize = elementSize; diff --git a/Gems/Atom/Feature/Common/Code/Source/Utils/GpuBufferHandler.cpp b/Gems/Atom/Feature/Common/Code/Source/Utils/GpuBufferHandler.cpp index 13a151f8ac..db78247251 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Utils/GpuBufferHandler.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Utils/GpuBufferHandler.cpp @@ -40,13 +40,11 @@ namespace AZ if (m_bufferIndex.IsValid()) { - AZStd::string bufferName = AZStd::string::format("%s_%" PRIXPTR, descriptor.m_bufferName.c_str(), reinterpret_cast(this)); - uint32_t byteCount = RHI::NextPowerOfTwo(GetMax(BufferMinSize, m_elementCount * m_elementSize)); RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; - desc.m_bufferName = bufferName; + desc.m_bufferName = descriptor.m_bufferName; desc.m_byteCount = byteCount; desc.m_elementSize = descriptor.m_elementSize; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/Buffer.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/Buffer.h index 3fef502e92..df24c7591b 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/Buffer.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/Buffer.h @@ -112,6 +112,8 @@ namespace AZ AZStd::mutex m_pendingUploadMutex; RHI::BufferViewDescriptor m_bufferViewDescriptor; + + RHI::AttachmentId m_attachmentId; }; template diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystem.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystem.h index c4b6aa74b4..9f5b150752 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystem.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystem.h @@ -35,7 +35,7 @@ namespace AZ // BufferSystemInterface overrides... RHI::Ptr GetCommonBufferPool(CommonBufferPoolType poolType) override; Data::Instance CreateBufferFromCommonPool(const CommonBufferDescriptor& descriptor) override; - Data::Instance FindCommonBuffer(AZStd::string_view bufferName) override; + Data::Instance FindCommonBuffer(AZStd::string_view uniqueBufferName) override; void Init(); void Shutdown(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystemInterface.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystemInterface.h index 1469eed060..39b4b09691 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystemInterface.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystemInterface.h @@ -53,6 +53,9 @@ namespace AZ RHI::Format m_elementFormat = RHI::Format::Unknown; // CreateBufferFromCommonPool(const CommonBufferDescriptor& descriptor) = 0; //! Find a buffer by name. The buffer has to be created by CreateBufferFromCommonPool function - virtual Data::Instance FindCommonBuffer(AZStd::string_view bufferName) = 0; + virtual Data::Instance FindCommonBuffer(AZStd::string_view uniqueBufferName) = 0; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Buffer/BufferAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Buffer/BufferAsset.h index d8a6fbb44d..4776933de6 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Buffer/BufferAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Buffer/BufferAsset.h @@ -60,11 +60,15 @@ namespace AZ const Data::Asset& GetPoolAsset() const; CommonBufferPoolType GetCommonPoolType() const; + + const AZStd::string& GetName() const; private: // Called by asset creators to assign the asset to a ready state. void SetReady(); + AZStd::string m_name; + AZStd::vector m_buffer; RHI::BufferDescriptor m_bufferDescriptor; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/ModelAssetBuilderComponent.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/ModelAssetBuilderComponent.cpp index ea2bdd0d83..f559a0aba6 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/ModelAssetBuilderComponent.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/ModelAssetBuilderComponent.cpp @@ -114,7 +114,7 @@ namespace AZ if (auto* serialize = azrtti_cast(context)) { serialize->Class() - ->Version(26); // [ATOM-14992] + ->Version(27); // [ATOM-15658] } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/Buffer.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/Buffer.cpp index 470b66c28e..81f02b7435 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/Buffer.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/Buffer.cpp @@ -32,10 +32,6 @@ namespace AZ auto buffer = Data::InstanceDatabase::Instance().FindOrCreate( Data::InstanceId::CreateFromAssetId(bufferAsset.GetId()), bufferAsset); - if (buffer && buffer->m_rhiBuffer) - { - buffer->m_rhiBuffer->SetName(Name(bufferAsset.GetHint())); - } return buffer; } @@ -170,6 +166,16 @@ namespace AZ return resultCode; } } + + m_rhiBuffer->SetName(Name(bufferAsset.GetName())); + + // Only generate buffer's attachment id if the buffer is writable + if (RHI::CheckBitsAny(m_rhiBuffer->GetDescriptor().m_bindFlags, + RHI::BufferBindFlags::ShaderWrite | RHI::BufferBindFlags::CopyWrite | RHI::BufferBindFlags::DynamicInputAssembly)) + { + // attachment id = bufferName_bufferInstanceId + m_attachmentId = Name(bufferAsset.GetName() + "_" + bufferAsset.GetId().m_guid.ToString(false, false)); + } return RHI::ResultCode::Success; } @@ -312,7 +318,8 @@ namespace AZ const RHI::AttachmentId& Buffer::GetAttachmentId() const { - return m_rhiBuffer->GetName(); + AZ_Assert(!m_attachmentId.GetStringView().empty(), "Read-only buffer doesn't need attachment id"); + return m_attachmentId; } const RHI::BufferViewDescriptor& Buffer::GetBufferViewDescriptor() const diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/BufferSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/BufferSystem.cpp index 9a9254b0d6..c4b4b28d44 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/BufferSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/BufferSystem.cpp @@ -152,15 +152,22 @@ namespace AZ } Data::Instance BufferSystem::CreateBufferFromCommonPool(const CommonBufferDescriptor& descriptor) - { - Uuid bufferId = Uuid::CreateName(descriptor.m_bufferName.c_str()); - - // Report error if there is a buffer with same name. - // Note: this shouldn't return the existing buffer because users are expecting a newly created buffer. - if (Data::InstanceDatabase::Instance().Find(Data::InstanceId(bufferId))) + { + Uuid bufferId; + if (descriptor.m_isUniqueName) { - AZ_Error("BufferSystem", false, "Buffer with same name '%s' already exist", descriptor.m_bufferName.c_str()); - return nullptr; + bufferId = Uuid::CreateName(descriptor.m_bufferName.c_str()); + // Report error if there is a buffer with same name. + // Note: this shouldn't return the existing buffer because users are expecting a newly created buffer. + if (Data::InstanceDatabase::Instance().Find(Data::InstanceId(bufferId))) + { + AZ_Error("BufferSystem", false, "Buffer with same name '%s' already exist", descriptor.m_bufferName.c_str()); + return nullptr; + } + } + else + { + bufferId = Uuid::CreateRandom(); } RHI::Ptr bufferPool = GetCommonBufferPool(descriptor.m_poolType); @@ -207,9 +214,9 @@ namespace AZ return nullptr; } - Data::Instance BufferSystem::FindCommonBuffer(AZStd::string_view bufferName) + Data::Instance BufferSystem::FindCommonBuffer(AZStd::string_view uniqueBufferName) { - Uuid bufferId = Uuid::CreateName(bufferName.data()); + Uuid bufferId = Uuid::CreateName(uniqueBufferName.data()); return Data::InstanceDatabase::Instance().Find(Data::InstanceId(bufferId)); } } // namespace RPI diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicBufferAllocator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicBufferAllocator.cpp index e79aa2e1bb..627b1e9912 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicBufferAllocator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicBufferAllocator.cpp @@ -30,7 +30,7 @@ namespace AZ // Create the ring buffer from common pool RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::DynamicInputAssembly; - desc.m_bufferName = AZStd::string::format("DyanmicBufferRing_%p", this); + desc.m_bufferName = "DyanmicBufferRing"; desc.m_elementSize = 1; desc.m_byteCount = ringBufferSize; m_ringBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAsset.cpp index 6f9d3a9d64..69444ae466 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAsset.cpp @@ -30,7 +30,8 @@ namespace AZ if (auto* serializeContext = azrtti_cast(context)) { serializeContext->Class() - ->Version(1) + ->Version(2) + ->Field("Name", &BufferAsset::m_name) ->Field("Buffer", &BufferAsset::m_buffer) ->Field("BufferDescriptor", &BufferAsset::m_bufferDescriptor) ->Field("BufferViewDescriptor", &BufferAsset::m_bufferViewDescriptor) @@ -80,5 +81,10 @@ namespace AZ { return m_poolType; } + + const AZStd::string& BufferAsset::GetName() const + { + return m_name; + } } //namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp index 486be9860e..4bd1b53f57 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp @@ -152,7 +152,10 @@ namespace AZ void BufferAssetCreator::SetBufferName(AZStd::string_view name) { - m_asset.SetHint(name); + if (ValidateIsReady()) + { + m_asset->m_name = name; + } } bool BufferAssetCreator::Clone(const Data::Asset& sourceAsset, Data::Asset& clonedResult, Data::AssetId& inOutLastCreatedAssetId) diff --git a/Gems/Atom/RPI/Code/Tests/Buffer/BufferTests.cpp b/Gems/Atom/RPI/Code/Tests/Buffer/BufferTests.cpp index df3af5f0d2..261b57a568 100644 --- a/Gems/Atom/RPI/Code/Tests/Buffer/BufferTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Buffer/BufferTests.cpp @@ -474,6 +474,7 @@ namespace UnitTest desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; desc.m_bufferName = "Buffer1"; desc.m_byteCount = bufferInfo.m_bufferDescriptor.m_byteCount; + desc.m_isUniqueName = true; Data::Instance bufferInst = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); // buffer created @@ -488,8 +489,9 @@ namespace UnitTest EXPECT_EQ(bufferFound2.get(), nullptr); } - // Failed if creates a buffer with duplicated name with existing buffer - TEST_F(BufferTests, BufferSystem_CreateDuplicatedNamedBuffer_Fail) + // Failed if creates a buffe which has a same name with existing buffer + // and has m_isUniqueName is enabled + TEST_F(BufferTests, BufferSystem_CreateDuplicatedNamedBufferEnableUniqueName_Fail) { using namespace AZ; @@ -499,6 +501,7 @@ namespace UnitTest desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; desc.m_bufferName = "Buffer1"; desc.m_byteCount = bufferInfo.m_bufferDescriptor.m_byteCount; + desc.m_isUniqueName = true; Data::Instance bufferInst = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); // buffer created @@ -511,6 +514,27 @@ namespace UnitTest EXPECT_EQ(bufferInst2.get(), nullptr); } + // create a buffer which has a same name with existing buffer + TEST_F(BufferTests, BufferSystem_CreateDuplicatedNamedBuffers_Success) + { + using namespace AZ; + + ExpectedBuffer bufferInfo = CreateValidBuffer(); + + RPI::CommonBufferDescriptor desc; + desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; + desc.m_bufferName = "Buffer1"; + desc.m_byteCount = bufferInfo.m_bufferDescriptor.m_byteCount; + + Data::Instance bufferInst = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); + // buffer created + EXPECT_NE(bufferInst.get(), nullptr); + + Data::Instance bufferInst2 = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); + // buffer created + EXPECT_NE(bufferInst2.get(), nullptr); + } + // Buffer instance creation unit tests TEST_F(BufferTests, Buffer_CreationUsingPoolAsset_Success) { diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp index 8dc7f9387c..9f68a7d12c 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp @@ -595,7 +595,7 @@ namespace AZ // Create a buffer and populate it with the transforms RPI::CommonBufferDescriptor descriptor; descriptor.m_bufferData = boneTransforms.data(); - descriptor.m_bufferName = AZStd::string::format("BoneTransformBuffer_%s_%s", actorInstance->GetActor()->GetName(), Uuid::CreateRandom().ToString().c_str()); + descriptor.m_bufferName = AZStd::string::format("BoneTransformBuffer_%s", actorInstance->GetActor()->GetName()); descriptor.m_byteCount = boneTransforms.size() * sizeof(float); descriptor.m_elementSize = floatsPerBone * sizeof(float); descriptor.m_poolType = RPI::CommonBufferPoolType::ReadOnly; From 76a6df341b0b05eafb53f4977cd706a80a3b2b3d Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 10:51:47 -0700 Subject: [PATCH 48/71] SPEC-2513 Fixes to enable w4457 --- .../UdpTransport/UdpFragmentQueue.cpp | 4 +- Code/Sandbox/Editor/CVarMenu.cpp | 14 ++-- .../Editor/TrackView/TrackViewAnimNode.cpp | 4 +- .../Editor/TrackView/TrackViewTrack.cpp | 6 +- .../Vulkan/Code/Source/RHI/DescriptorSet.cpp | 4 +- .../Window/ShaderManagementConsoleWindow.cpp | 4 +- .../Source/Editor/QATLControlsTreeModel.cpp | 8 +- .../EMotionFX/Rendering/Common/RenderUtil.cpp | 12 +-- .../EMotionFX/Source/MultiThreadScheduler.cpp | 4 +- .../PropertyWidgets/MotionDataHandler.cpp | 6 +- .../StaticLib/GraphCanvas/Styling/Parser.cpp | 80 +++++++++---------- .../GraphCanvas/Utils/GraphUtils.cpp | 6 +- Gems/GraphModel/Code/Source/Model/Graph.cpp | 4 +- .../Editor/Animation/UiAnimViewAnimNode.cpp | 12 +-- .../Code/Editor/Animation/UiAnimViewTrack.cpp | 6 +- .../MicrophoneSystemComponent_Windows.cpp | 22 ++--- .../NetworkEntity/NetworkEntityManager.cpp | 4 +- .../Widgets/NodePalette/NodePaletteModel.cpp | 12 +-- .../View/Windows/ScriptCanvasContextMenus.cpp | 4 +- .../Grammar/AbstractCodeModel.cpp | 10 +-- .../Internal/Nodes/ExpressionNodeBase.cpp | 6 +- .../Code/Source/Core/WhiteBoxToolApi.cpp | 16 ++-- .../Common/MSVC/Configurations_msvc.cmake | 1 - 23 files changed, 124 insertions(+), 125 deletions(-) diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp index 2a90509187..042f19aa70 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp @@ -139,7 +139,7 @@ namespace AzNetworking NetworkOutputSerializer networkSerializer(buffer.GetBuffer(), buffer.GetSize()); { - ISerializer& serializer = networkSerializer; // To get the default typeinfo parameters in ISerializer + ISerializer& networkISerializer = networkSerializer; // To get the default typeinfo parameters in ISerializer // First, serialize out the header if (!header.SerializePacketFlags(networkSerializer)) @@ -148,7 +148,7 @@ namespace AzNetworking return false; } - if (!serializer.Serialize(header, "Header")) + if (!networkISerializer.Serialize(header, "Header")) { AZLOG(NET_FragmentQueue, "Reconstructed fragmented packet failed header serialization"); return false; diff --git a/Code/Sandbox/Editor/CVarMenu.cpp b/Code/Sandbox/Editor/CVarMenu.cpp index f3dd4cf065..bbce0c63fc 100644 --- a/Code/Sandbox/Editor/CVarMenu.cpp +++ b/Code/Sandbox/Editor/CVarMenu.cpp @@ -118,10 +118,10 @@ void CVarMenu::AddUniqueCVarsItem(QString displayName, // Otherwise we could have just used the action's currently checked // state and updated the CVar's value only bool cVarOn = (cVar->GetFVal() == availableCVar.m_onValue); - bool checked = !cVarOn; - SetCVar(cVar, checked ? availableCVar.m_onValue : availableCVar.m_offValue); - action->setChecked(checked); - if (checked) + bool cVarChecked = !cVarOn; + SetCVar(cVar, cVarChecked ? availableCVar.m_onValue : availableCVar.m_offValue); + action->setChecked(cVarChecked); + if (cVarChecked) { // Set the rest of the CVars in the group to their off values SetCVarsToOffValue(availableCVars, availableCVar); @@ -132,9 +132,9 @@ void CVarMenu::AddUniqueCVarsItem(QString displayName, // Initialize the action's checked state based on its associated CVar's current value ICVar* cVar = gEnv->pConsole->GetCVar(availableCVar.m_cVarName.toUtf8().data()); - bool checked = (cVar && cVar->GetFVal() == availableCVar.m_onValue); - action->setChecked(checked); - if (checked) + bool cVarChecked = (cVar && cVar->GetFVal() == availableCVar.m_onValue); + action->setChecked(cVarChecked); + if (cVarChecked) { // Set the rest of the CVars in the group to their off values SetCVarsToOffValue(availableCVars, availableCVar); diff --git a/Code/Sandbox/Editor/TrackView/TrackViewAnimNode.cpp b/Code/Sandbox/Editor/TrackView/TrackViewAnimNode.cpp index 35306b9535..2b4622f5ec 100644 --- a/Code/Sandbox/Editor/TrackView/TrackViewAnimNode.cpp +++ b/Code/Sandbox/Editor/TrackView/TrackViewAnimNode.cpp @@ -205,10 +205,10 @@ CTrackViewAnimNode::CTrackViewAnimNode(IAnimSequence* pSequence, IAnimNode* anim for (int i = 0; i < nodeCount; ++i) { IAnimNode* node = pSequence->GetNode(i); - IAnimNode* pParentNode = node->GetParent(); + IAnimNode* pNodeParentNode = node->GetParent(); // If our node is the parent, then the current node is a child of it - if (animNode == pParentNode) + if (animNode == pNodeParentNode) { CTrackViewAnimNodeFactory animNodeFactory; CTrackViewAnimNode* pNewTVAnimNode = animNodeFactory.BuildAnimNode(pSequence, node, this); diff --git a/Code/Sandbox/Editor/TrackView/TrackViewTrack.cpp b/Code/Sandbox/Editor/TrackView/TrackViewTrack.cpp index 94559921c3..76a836f86c 100644 --- a/Code/Sandbox/Editor/TrackView/TrackViewTrack.cpp +++ b/Code/Sandbox/Editor/TrackView/TrackViewTrack.cpp @@ -68,12 +68,12 @@ CTrackViewTrack::CTrackViewTrack(IAnimTrack* pTrack, CTrackViewAnimNode* pTrackA { // Search for child tracks const unsigned int subTrackCount = m_pAnimTrack->GetSubTrackCount(); - for (unsigned int subTrackIndex = 0; subTrackIndex < subTrackCount; ++subTrackIndex) + for (unsigned int subTrackI = 0; subTrackI < subTrackCount; ++subTrackI) { - IAnimTrack* pSubTrack = m_pAnimTrack->GetSubTrack(subTrackIndex); + IAnimTrack* pSubTrack = m_pAnimTrack->GetSubTrack(subTrackI); CTrackViewTrackFactory trackFactory; - CTrackViewTrack* pNewTVTrack = trackFactory.BuildTrack(pSubTrack, pTrackAnimNode, this, true, subTrackIndex); + CTrackViewTrack* pNewTVTrack = trackFactory.BuildTrack(pSubTrack, pTrackAnimNode, this, true, subTrackI); m_childNodes.push_back(std::unique_ptr(pNewTVTrack)); } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/DescriptorSet.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/DescriptorSet.cpp index 634f5a51ac..37f1e31542 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/DescriptorSet.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/DescriptorSet.cpp @@ -261,8 +261,8 @@ namespace AZ if (vulkanDescriptor.m_constantDataPool && constantDataSize) { m_constantDataBuffer = Buffer::Create(); - const RHI::BufferDescriptor descriptor(RHI::BufferBindFlags::Constant, constantDataSize); - RHI::BufferInitRequest request(*m_constantDataBuffer, descriptor); + const RHI::BufferDescriptor bufferDescriptor(RHI::BufferBindFlags::Constant, constantDataSize); + RHI::BufferInitRequest request(*m_constantDataBuffer, bufferDescriptor); RHI::ResultCode rhiResult = vulkanDescriptor.m_constantDataPool->InitBuffer(request); if (rhiResult != RHI::ResultCode::Success) { diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp index 6ab297bfc5..08d406884f 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp @@ -510,9 +510,9 @@ namespace ShaderManagementConsole AZStd::vector documentIdsToClose; documentIdsToClose.reserve(m_tabWidget->count()); const AZ::Uuid documentIdToKeepOpen = GetDocumentIdFromTab(tabIndex); - for (int tabIndex = 0; tabIndex < m_tabWidget->count(); ++tabIndex) + for (int tabI = 0; tabI < m_tabWidget->count(); ++tabI) { - const AZ::Uuid documentId = GetDocumentIdFromTab(tabIndex); + const AZ::Uuid documentId = GetDocumentIdFromTab(tabI); if (documentId != documentIdToKeepOpen) { documentIdsToClose.push_back(documentId); diff --git a/Gems/AudioSystem/Code/Source/Editor/QATLControlsTreeModel.cpp b/Gems/AudioSystem/Code/Source/Editor/QATLControlsTreeModel.cpp index d05f7830a4..32446237a3 100644 --- a/Gems/AudioSystem/Code/Source/Editor/QATLControlsTreeModel.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/QATLControlsTreeModel.cpp @@ -287,9 +287,9 @@ namespace AudioControls QDataStream stream(&encoded, QIODevice::ReadOnly); while (!stream.atEnd()) { - int row, col; + int streamRow, streamCol; QMap roleDataMap; - stream >> row >> col >> roleDataMap; + stream >> streamRow >> streamCol >> roleDataMap; if (!roleDataMap.isEmpty()) { // If dropping a folder, make sure that folder name doesn't already exist where it is being dropped @@ -341,9 +341,9 @@ namespace AudioControls { QByteArray data = mimeData->data(format); QDataStream stream(&data, QIODevice::ReadOnly); - int row, col; + int streamRow, streamCol; QMap roleDataMap; - stream >> row >> col >> roleDataMap; + stream >> streamRow >> streamCol >> roleDataMap; if (!roleDataMap.isEmpty() && roleDataMap[eDR_TYPE] != eIT_FOLDER) { return false; diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp index b2389a3086..90752c7604 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp @@ -1628,18 +1628,18 @@ namespace MCommon } else { - const float screenWidth = static_cast(camera->GetScreenWidth()); - const float screenHeight = static_cast(camera->GetScreenHeight()); + const float cameraScreenWidth = static_cast(camera->GetScreenWidth()); + const float cameraScreenHeight = static_cast(camera->GetScreenHeight()); // find the 4 corners of the frustum AZ::Vector3 corners[4]; const AZ::Matrix4x4 inversedProjectionMatrix = MCore::InvertProjectionMatrix(camera->GetProjectionMatrix()); const AZ::Matrix4x4 inversedViewMatrix = MCore::InvertProjectionMatrix(camera->GetViewMatrix()); - corners[0] = MCore::Unproject(0.0f, 0.0f, screenWidth, screenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); - corners[1] = MCore::Unproject(screenWidth, 0.0f, screenWidth, screenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); - corners[2] = MCore::Unproject(screenWidth, screenHeight, screenWidth, screenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); - corners[3] = MCore::Unproject(0.0f, screenHeight, screenWidth, screenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); + corners[0] = MCore::Unproject(0.0f, 0.0f, cameraScreenWidth, cameraScreenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); + corners[1] = MCore::Unproject(cameraScreenWidth, 0.0f, cameraScreenWidth, cameraScreenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); + corners[2] = MCore::Unproject(cameraScreenWidth, cameraScreenHeight, cameraScreenWidth, cameraScreenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); + corners[3] = MCore::Unproject(0.0f, cameraScreenHeight, cameraScreenWidth, cameraScreenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); // calculate the intersection points with the ground plane and create an AABB around those // if there is no intersection point then use the ray target as point, which is the projection onto the far plane basically diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/MultiThreadScheduler.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/MultiThreadScheduler.cpp index 327f9d27ae..1094ad72a3 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/MultiThreadScheduler.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/MultiThreadScheduler.cpp @@ -319,9 +319,9 @@ namespace EMotionFX step.mDependencies.Clear(false); // calculate the new dependencies for this step - for (ActorInstance* actorInstance : step.mActorInstances) + for (ActorInstance* stepActorInstance : step.mActorInstances) { - AddDependenciesToStep(actorInstance, &step); + AddDependenciesToStep(stepActorInstance, &step); } // assume that there is only one of the same actor instance in the whole schedule diff --git a/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/MotionDataHandler.cpp b/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/MotionDataHandler.cpp index 7597cb888d..6060a81bd0 100644 --- a/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/MotionDataHandler.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/MotionDataHandler.cpp @@ -91,10 +91,10 @@ namespace EMotionFX } else { - AZ::Outcome index = factory.FindRegisteredIndexByTypeId(instance); - if (index.IsSuccess()) + AZ::Outcome motionIndex = factory.FindRegisteredIndexByTypeId(instance); + if (motionIndex.IsSuccess()) { - GUI->setCurrentIndex(static_cast(index.GetValue() + 1)); // +1 because we inserted an 'Automatic' one as first entry. + GUI->setCurrentIndex(static_cast(motionIndex.GetValue() + 1)); // +1 because we inserted an 'Automatic' one as first entry. } else { diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp index db07c3ca8e..23d28857e1 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp @@ -725,23 +725,23 @@ namespace GraphCanvas case Attribute::LineColor: case Attribute::StripeColor: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (IsColorValid(value)) + if (IsColorValid(valueStr)) { - style->SetAttribute(attribute, ParseColor(value)); + style->SetAttribute(attribute, ParseColor(valueStr)); } break; } case Attribute::BackgroundImage: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (value.startsWith(QStringLiteral(":/"))) + if (valueStr.startsWith(QStringLiteral(":/"))) { - value = QString("qrc%1").arg(value); + valueStr = QString("qrc%1").arg(valueStr); } - QUrl url(value); + QUrl url(valueStr); if (url.isValid()) { style->SetAttribute(attribute, url); @@ -844,103 +844,103 @@ namespace GraphCanvas case Attribute::BorderStyle: case Attribute::LineStyle: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (IsLineStyleValid(value)) + if (IsLineStyleValid(valueStr)) { - style->SetAttribute(attribute, QVariant::fromValue(ParseLineStyle(value))); + style->SetAttribute(attribute, QVariant::fromValue(ParseLineStyle(valueStr))); } break; } case Attribute::LineCurve: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (IsLineCurveValid(value)) + if (IsLineCurveValid(valueStr)) { - style->SetAttribute(attribute, QVariant::fromValue(ParseLineCurve(value))); + style->SetAttribute(attribute, QVariant::fromValue(ParseLineCurve(valueStr))); } break; } case Attribute::CapStyle: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (IsCapStyleValid(value)) + if (IsCapStyleValid(valueStr)) { - style->SetAttribute(attribute, QVariant::fromValue(ParseCapStyle(value))); + style->SetAttribute(attribute, QVariant::fromValue(ParseCapStyle(valueStr))); } break; } case Attribute::FontFamily: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (QString::compare(value, QLatin1String("default"), Qt::CaseInsensitive) == 0) + if (QString::compare(valueStr, QLatin1String("default"), Qt::CaseInsensitive) == 0) { - value = defaultFontInfo.family(); + valueStr = defaultFontInfo.family(); } else { - QFont font(value); + QFont font(valueStr); QFontInfo info(font); if (!info.exactMatch()) { - qWarning() << "Invalid font-family:" << value; + qWarning() << "Invalid font-family:" << valueStr; } } - style->SetAttribute(attribute, value); + style->SetAttribute(attribute, valueStr); } case Attribute::FontStyle: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (QString::compare(value, QLatin1String("default"), Qt::CaseInsensitive) == 0) + if (QString::compare(valueStr, QLatin1String("default"), Qt::CaseInsensitive) == 0) { style->SetAttribute(attribute, defaultFontInfo.style()); } else { - if (IsFontStyleValid(value)) + if (IsFontStyleValid(valueStr)) { - style->SetAttribute(attribute, ParseFontStyle(value)); + style->SetAttribute(attribute, ParseFontStyle(valueStr)); } } break; } case Attribute::FontWeight: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (QString::compare(value, QLatin1String("default"), Qt::CaseInsensitive) == 0) + if (QString::compare(valueStr, QLatin1String("default"), Qt::CaseInsensitive) == 0) { style->SetAttribute(attribute, defaultFontInfo.weight()); } else { - if (IsFontWeightValid(value)) + if (IsFontWeightValid(valueStr)) { - style->SetAttribute(attribute, ParseFontWeight(value)); + style->SetAttribute(attribute, ParseFontWeight(valueStr)); } } break; } case Attribute::FontVariant: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (QString::compare(value, QLatin1String("default"), Qt::CaseInsensitive) == 0) + if (QString::compare(valueStr, QLatin1String("default"), Qt::CaseInsensitive) == 0) { style->SetAttribute(attribute, defaultFont.capitalization()); } else { - if (IsFontVariantValid(value)) + if (IsFontVariantValid(valueStr)) { - style->SetAttribute(attribute, value); + style->SetAttribute(attribute, valueStr); } } break; @@ -965,23 +965,23 @@ namespace GraphCanvas break; case Attribute::PaletteStyle: { - QString value(member->value.GetString()); - style->SetAttribute(attribute, QVariant::fromValue(ParsePaletteStyle(value))); + QString valueStr(member->value.GetString()); + style->SetAttribute(attribute, QVariant::fromValue(ParsePaletteStyle(valueStr))); break; } case Attribute::PatternTemplate: case Attribute::PatternPalettes: { - QString value(member->value.GetString()); - style->SetAttribute(attribute, value); + QString valueStr(member->value.GetString()); + style->SetAttribute(attribute, valueStr); break; } case Attribute::Steps: { QList stepList; - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - QStringList splitValues = value.split("|"); + QStringList splitValues = valueStr.split("|"); for (QString currentString : splitValues) { diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/GraphUtils.cpp b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/GraphUtils.cpp index c2574a69f6..16bc3dd86a 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/GraphUtils.cpp +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/GraphUtils.cpp @@ -1325,12 +1325,12 @@ namespace GraphCanvas AZStd::vector< Endpoint > endpoints; SlotRequestBus::EventResult(endpoints, currentEndpoint.GetSlotId(), &SlotRequests::GetRemappedModelEndpoints); - for (const Endpoint& endpoint : endpoints) + for (const Endpoint& e : endpoints) { // If we haven't already processed the node, add it to our explore set so we can recurse. - if (retVal.count(endpoint) == 0) + if (retVal.count(e) == 0) { - exploreSet.insert(endpoint); + exploreSet.insert(e); } } } diff --git a/Gems/GraphModel/Code/Source/Model/Graph.cpp b/Gems/GraphModel/Code/Source/Model/Graph.cpp index 25ad61db2c..809679e9f6 100644 --- a/Gems/GraphModel/Code/Source/Model/Graph.cpp +++ b/Gems/GraphModel/Code/Source/Model/Graph.cpp @@ -279,8 +279,8 @@ namespace GraphModel m_connections.erase(iter); #if defined(AZ_ENABLE_TRACING) - auto iter = AZStd::find(m_connections.begin(), m_connections.end(), connection); - AZ_Assert(iter == m_connections.end(), "Graph is broken. The same connection object was found multiple times."); + auto iterConnection = AZStd::find(m_connections.begin(), m_connections.end(), connection); + AZ_Assert(iterConnection == m_connections.end(), "Graph is broken. The same connection object was found multiple times."); #endif return true; diff --git a/Gems/LyShine/Code/Editor/Animation/UiAnimViewAnimNode.cpp b/Gems/LyShine/Code/Editor/Animation/UiAnimViewAnimNode.cpp index f99eea5fa2..37629fac37 100644 --- a/Gems/LyShine/Code/Editor/Animation/UiAnimViewAnimNode.cpp +++ b/Gems/LyShine/Code/Editor/Animation/UiAnimViewAnimNode.cpp @@ -110,10 +110,10 @@ CUiAnimViewAnimNode::CUiAnimViewAnimNode(IUiAnimSequence* pSequence, IUiAnimNode for (int i = 0; i < nodeCount; ++i) { IUiAnimNode* pNode = pSequence->GetNode(i); - IUiAnimNode* pParentNode = pNode->GetParent(); + IUiAnimNode* pNodeParentNode = pNode->GetParent(); // If our node is the parent, then the current node is a child of it - if (pAnimNode == pParentNode) + if (pAnimNode == pNodeParentNode) { CUiAnimViewAnimNodeFactory animNodeFactory; CUiAnimViewAnimNode* pNewUiAVAnimNode = animNodeFactory.BuildAnimNode(pSequence, pNode, this); @@ -510,20 +510,20 @@ bool CUiAnimViewAnimNode::BaseClassPropertyPotentiallyChanged( { for (const AZ::SerializeContext::ClassElement& baseElement : baseClassData->m_elements) { - size_t offset = baseClassOffset + baseElement.m_offset; + size_t baseOffset = baseClassOffset + baseElement.m_offset; if (baseElement.m_flags & AZ::SerializeContext::ClassElement::FLG_BASE_CLASS) { - if (BaseClassPropertyPotentiallyChanged(context, dstComponent, srcComponent, baseElement, offset)) + if (BaseClassPropertyPotentiallyChanged(context, dstComponent, srcComponent, baseElement, baseOffset)) { valueChanged = true; } } else { - if (HasComponentParamValueAzChanged(dstComponent, srcComponent, baseElement, offset)) + if (HasComponentParamValueAzChanged(dstComponent, srcComponent, baseElement, baseOffset)) { valueChanged = true; - AzEntityPropertyChanged(srcComponent, dstComponent, baseElement, offset); + AzEntityPropertyChanged(srcComponent, dstComponent, baseElement, baseOffset); } } } diff --git a/Gems/LyShine/Code/Editor/Animation/UiAnimViewTrack.cpp b/Gems/LyShine/Code/Editor/Animation/UiAnimViewTrack.cpp index 797154a50b..fc77a370d3 100644 --- a/Gems/LyShine/Code/Editor/Animation/UiAnimViewTrack.cpp +++ b/Gems/LyShine/Code/Editor/Animation/UiAnimViewTrack.cpp @@ -59,12 +59,12 @@ CUiAnimViewTrack::CUiAnimViewTrack(IUiAnimTrack* pTrack, CUiAnimViewAnimNode* pT { // Search for child tracks const unsigned int subTrackCount = m_pAnimTrack->GetSubTrackCount(); - for (unsigned int subTrackIndex = 0; subTrackIndex < subTrackCount; ++subTrackIndex) + for (unsigned int subTrackI = 0; subTrackI < subTrackCount; ++subTrackI) { - IUiAnimTrack* pSubTrack = m_pAnimTrack->GetSubTrack(subTrackIndex); + IUiAnimTrack* pSubTrack = m_pAnimTrack->GetSubTrack(subTrackI); CUiAnimViewTrackFactory trackFactory; - CUiAnimViewTrack* pNewUiAVTrack = trackFactory.BuildTrack(pSubTrack, pTrackAnimNode, this, true, subTrackIndex); + CUiAnimViewTrack* pNewUiAVTrack = trackFactory.BuildTrack(pSubTrack, pTrackAnimNode, this, true, subTrackI); m_childNodes.push_back(std::unique_ptr(pNewUiAVTrack)); } diff --git a/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp b/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp index 2eb840b253..5a080084e1 100644 --- a/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp +++ b/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp @@ -422,12 +422,12 @@ namespace Audio if (stereoToMono) { // Samples are interleaved now, copy only left channel to the output - float* inputData = reinterpret_cast(m_conversionBufferIn.m_data); - float* outputData = reinterpret_cast(m_conversionBufferOut.m_data); + float* bufferInputData = reinterpret_cast(m_conversionBufferIn.m_data); + float* bufferOutputData = reinterpret_cast(m_conversionBufferOut.m_data); for (AZ::u32 frame = 0; frame < numFrames; ++frame) { - outputData[frame] = *inputData++; - ++inputData; + bufferOutputData[frame] = *bufferInputData++; + ++bufferInputData; } } else // monoToStereo @@ -435,21 +435,21 @@ namespace Audio // Split single samples to both left and right channels if (shouldDeinterleave) { - float* inputData = reinterpret_cast(m_conversionBufferIn.m_data); - float** outputData = reinterpret_cast(m_conversionBufferOut.m_data); + float* bufferInputData = reinterpret_cast(m_conversionBufferIn.m_data); + float** bufferOutputData = reinterpret_cast(m_conversionBufferOut.m_data); for (AZ::u32 frame = 0; frame < numFrames; ++frame) { - outputData[0][frame] = outputData[1][frame] = inputData[frame]; + bufferOutputData[0][frame] = bufferOutputData[1][frame] = bufferInputData[frame]; } } else { - float* inputData = reinterpret_cast(m_conversionBufferIn.m_data); - float* outputData = reinterpret_cast(m_conversionBufferOut.m_data); + float* bufferInputData = reinterpret_cast(m_conversionBufferIn.m_data); + float* bufferOutputData = reinterpret_cast(m_conversionBufferOut.m_data); for (AZ::u32 frame = 0; frame < numFrames; ++frame) { - *outputData++ = inputData[frame]; - *outputData++ = inputData[frame]; + *bufferOutputData++ = bufferInputData[frame]; + *bufferOutputData++ = bufferInputData[frame]; } } } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index eaf89f3489..b0f1b221e8 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -263,9 +263,9 @@ namespace Multiplayer // Validate that we aren't already planning to remove this entity if (safeToExit) { - for (auto entityId : m_removeList) + for (auto remoteEntityId : m_removeList) { - if (entityId == entityId) + if (remoteEntityId == remoteEntityId) { safeToExit = false; } diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp index 8b00b5b71b..a5c0287cfd 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp @@ -340,31 +340,31 @@ namespace } // Pass in the associated class data so we can do more intensive lookups? - const AZ::SerializeContext::ClassData* classData = serializeContext.FindClassData(node.first); + const AZ::SerializeContext::ClassData* nodeClassData = serializeContext.FindClassData(node.first); - if (classData == nullptr) + if (nodeClassData == nullptr) { continue; } // Detect primitive types os we avoid making nodes out of them. // Or anything that is 'pure data' and should be populated through a different mechanism. - if (classData->m_azRtti && classData->m_azRtti->IsTypeOf()) + if (nodeClassData->m_azRtti && nodeClassData->m_azRtti->IsTypeOf()) { continue; } // Skip over some of our more dynamic nodes that we want to populate using different means - else if (classData->m_azRtti && classData->m_azRtti->IsTypeOf()) + else if (nodeClassData->m_azRtti && nodeClassData->m_azRtti->IsTypeOf()) { continue; } - else if (classData->m_azRtti && classData->m_azRtti->IsTypeOf()) + else if (nodeClassData->m_azRtti && nodeClassData->m_azRtti->IsTypeOf()) { continue; } else { - nodePaletteModel.RegisterCustomNode(categoryPath, node.first, node.second, classData); + nodePaletteModel.RegisterCustomNode(categoryPath, node.first, node.second, nodeClassData); } } diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/ScriptCanvasContextMenus.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/ScriptCanvasContextMenus.cpp index 428f5eb290..3c21701fb0 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/ScriptCanvasContextMenus.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/ScriptCanvasContextMenus.cpp @@ -563,13 +563,13 @@ namespace ScriptCanvasEditor else if (slotType == GraphCanvas::SlotTypes::DataSlot) { const AZ::EntityId& slotId2 = GetTargetId(); - const GraphCanvas::GraphId& graphId = GetGraphId(); + const GraphCanvas::GraphId& graphId2 = GetGraphId(); GraphCanvas::Endpoint endpoint; GraphCanvas::SlotRequestBus::EventResult(endpoint, slotId2, &GraphCanvas::SlotRequests::GetEndpoint); bool promotedElement = false; - GraphCanvas::GraphModelRequestBus::EventResult(promotedElement, graphId, &GraphCanvas::GraphModelRequests::PromoteToVariableAction, endpoint); + GraphCanvas::GraphModelRequestBus::EventResult(promotedElement, graphId2, &GraphCanvas::GraphModelRequests::PromoteToVariableAction, endpoint); if (promotedElement) { diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp index db45f6bfe3..2a485c9501 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp @@ -4090,23 +4090,23 @@ namespace ScriptCanvas auto userFunctionIter = m_userInsThatRequireTopology.find(nodeling); if (userFunctionIter != m_userInsThatRequireTopology.end()) { - auto& node = *userFunctionIter->first; - auto outSlots = node.GetSlotsByType(CombinedSlotType::ExecutionOut); + auto& userFunctionNode = *userFunctionIter->first; + auto outSlots = userFunctionNode.GetSlotsByType(CombinedSlotType::ExecutionOut); if (outSlots.empty() || !outSlots.front()) { - AddError(node.GetEntityId(), nullptr, ScriptCanvas::ParseErrors::NoOutSlotInFunctionDefinitionStart); + AddError(userFunctionNode.GetEntityId(), nullptr, ScriptCanvas::ParseErrors::NoOutSlotInFunctionDefinitionStart); return; } - if (!ExecutionContainsCyclesCheck(node, *outSlots.front())) + if (!ExecutionContainsCyclesCheck(userFunctionNode, *outSlots.front())) { auto definition = userFunctionIter->second; auto entrySlot = definition->GetId().m_slot; AZ_Assert(entrySlot, "Bad accounting in user function definition node"); AZStd::vector returnValues; UserOutCallCollector userOutCallCollector; - TraverseExecutionConnections(node, *entrySlot, userOutCallCollector); + TraverseExecutionConnections(userFunctionNode, *entrySlot, userOutCallCollector); const AZStd::unordered_set& uniqueNodelingsOut = userOutCallCollector.GetOutCalls(); for (const auto& returnCall : uniqueNodelingsOut) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Internal/Nodes/ExpressionNodeBase.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Internal/Nodes/ExpressionNodeBase.cpp index e7edcce002..8c0f5760a8 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Internal/Nodes/ExpressionNodeBase.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Internal/Nodes/ExpressionNodeBase.cpp @@ -121,13 +121,13 @@ namespace ScriptCanvas { if (slotId == ExpressionNodeBaseProperty::GetInSlotId(this)) { - for (const SlotId& slotId : m_dirtyInputs) + for (const SlotId& dirtySlotId : m_dirtyInputs) { - auto variableIter = m_slotToVariableMap.find(slotId); + auto variableIter = m_slotToVariableMap.find(dirtySlotId); if (variableIter != m_slotToVariableMap.end()) { - PushVariable(variableIter->second, (*FindDatum(slotId))); + PushVariable(variableIter->second, (*FindDatum(dirtySlotId))); } } diff --git a/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp b/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp index 5803f066ae..a6b9456f98 100644 --- a/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp +++ b/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp @@ -2060,23 +2060,23 @@ namespace WhiteBox polygonHandle.m_faceHandles.push_back(faceHandleToVisit); // for all halfedges - for (const auto halfedgeHandle : faceHalfedges) + for (const auto faceHalfedgeHandle : faceHalfedges) { - const EdgeHandle edgeHandle = HalfedgeEdgeHandle(whiteBox, halfedgeHandle); + const EdgeHandle edgeHandle = HalfedgeEdgeHandle(whiteBox, faceHalfedgeHandle); // if we haven't seen this halfedge before and we want to track it, // store it in visited halfedges - if (halfedgeHandle != oppositeHalfedgeHandle + if (faceHalfedgeHandle != oppositeHalfedgeHandle // ignore border halfedges (not inside the polygon) - && AZStd::find(borderHalfedgeHandles.cbegin(), borderHalfedgeHandles.cend(), halfedgeHandle) == + && AZStd::find(borderHalfedgeHandles.cbegin(), borderHalfedgeHandles.cend(), faceHalfedgeHandle) == borderHalfedgeHandles.cend() // ensure we do not visit the same halfedge again - && AZStd::find(visitedHalfedges.cbegin(), visitedHalfedges.cend(), halfedgeHandle) == + && AZStd::find(visitedHalfedges.cbegin(), visitedHalfedges.cend(), faceHalfedgeHandle) == visitedHalfedges.cend() // ignore the halfedge if we've already tracked it in our 'building' list && AZStd::find(buildingEdgeHandles.cbegin(), buildingEdgeHandles.cend(), edgeHandle) == buildingEdgeHandles.cend()) { - halfedgesToVisit.push_back(HalfedgeOppositeHalfedgeHandle(whiteBox, halfedgeHandle)); + halfedgesToVisit.push_back(HalfedgeOppositeHalfedgeHandle(whiteBox, faceHalfedgeHandle)); } } } @@ -3198,10 +3198,10 @@ namespace WhiteBox // - add bottom faces if mesh was 2d previously (reverse winding order) FaceHandles allFacesToRemove = polygonHandle.m_faceHandles; - for (const auto& polygonHandle : polygonHandlesToRemove) + for (const auto& polygonHandleToRemove : polygonHandlesToRemove) { allFacesToRemove.insert( - allFacesToRemove.end(), polygonHandle.m_faceHandles.cbegin(), polygonHandle.m_faceHandles.cend()); + allFacesToRemove.end(), polygonHandleToRemove.m_faceHandles.cbegin(), polygonHandleToRemove.m_faceHandles.cend()); } // remove all faces that were already there diff --git a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake index f53b8aa769..357d578c44 100644 --- a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake +++ b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake @@ -73,7 +73,6 @@ ly_append_configurations_options( /wd4389 # comparison, signed/unsigned mismatch /wd4436 # the result of unary operator may be unaligned /wd4450 # declaration hides global declaration - /wd4457 # declaration hides function parameter # Enabling warnings that are disabled by default from /W4 # https://docs.microsoft.com/en-us/cpp/preprocessor/compiler-warnings-that-are-off-by-default?view=vs-2019 From 1f48985a0e325164af62561f6983ebac4a620e0c Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Fri, 4 Jun 2021 18:09:26 +0000 Subject: [PATCH 49/71] Update Blast to the latest version, eb169fe (#1076) --- cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index fa1326b63d..1a2cfa4049 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -30,11 +30,11 @@ ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) -ly_associate_package(PACKAGE_NAME Blast-1.1.7-rev1-multiplatform TARGETS Blast PACKAGE_HASH 36b8f393bcd25d0f85cfc7a831ebbdac881e6054c4f0735649966aa6aa86e6f0) ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) # platform-specific: ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-windows TARGETS AWSGameLiftServerSDK PACKAGE_HASH a0586b006e4def65cc25f388de17dc475e417dc1e6f9d96749777c88aa8271b0) +ly_associate_package(PACKAGE_NAME Blast-v1.1.7_rc2-9-geb169fe-rev1-windows TARGETS Blast PACKAGE_HASH 216df71f4ffaf4a6ea3f2e77e5f27d68f2325e717fbd1626b00c785b82cd1b67) ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev2-windows TARGETS DirectXShaderCompilerDxc PACKAGE_HASH decc53e97c7ddda9c7f853a30af7808a7b652a912f59ad2cd4bca5d308aae2c4) ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-windows TARGETS SPIRVCross PACKAGE_HASH 7d601ea9d625b1d509d38bd132a1f433d7e895b16adab76bac6103567a7a6817) ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-windows TARGETS freetype PACKAGE_HASH 88dedc86ccb8c92f14c2c033e51ee7d828fa08eafd6475c6aa963938a99f4bf3) From febf53671eacbe0f67a60feb5317ef93d70f43f5 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Fri, 4 Jun 2021 11:09:44 -0700 Subject: [PATCH 50/71] Addressed PR feedback. --- .../AzCore/AzCore/Serialization/IdUtils.h | 2 +- .../Spawnable/SpawnableEntitiesInterface.h | 34 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h index 98959fe4bd..15ce299000 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h +++ b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h @@ -29,7 +29,7 @@ namespace AZ namespace IdUtils { /** - * \param AllowDuplicates - If true allows the same id to be registered multiple time, + * \param AllowDuplicates - If true allows the same id to be registered multiple times, with the newer value overwriting the stored value. If false, duplicates are not allowed and the first stored value is kept.The default is false. */ diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index 2ad1db60a4..d2a5872fc8 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -177,8 +177,8 @@ namespace AzFramework //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. EntityPreInsertionCallback m_preInsertionCallback; - //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that - //! made the function call. The returned list of entities contains all the newly created entities. + //! Callback that's called when spawning entities has completed. This can be triggered from a different thread than the one that + //! made the function call to spawn. The returned list of entities contains all the newly created entities. EntitySpawnCallback m_completionCallback; //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. AZ::SerializeContext* m_serializeContext { nullptr }; @@ -191,8 +191,8 @@ namespace AzFramework //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. EntityPreInsertionCallback m_preInsertionCallback; - //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that - //! made the function call. The returned list of entities contains all the newly created entities. + //! Callback that's called when spawning entities has completed. This can be triggered from a different thread than the one that + //! made the function call to spawn. The returned list of entities contains all the newly created entities. EntitySpawnCallback m_completionCallback; //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. AZ::SerializeContext* m_serializeContext{ nullptr }; @@ -207,8 +207,8 @@ namespace AzFramework struct DespawnAllEntitiesOptionalArgs final { - //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that - //! made the function call. The returned list of entities contains all the newly created entities. + //! Callback that's called when despawning entities has completed. This can be triggered from a different thread than the one that + //! made the function call to despawn. The returned list of entities contains all the newly created entities. EntityDespawnCallback m_completionCallback; //! The priority at which this call will be executed. SpawnablePriority m_priority { SpawnablePriority_Default }; @@ -216,10 +216,10 @@ namespace AzFramework struct ReloadSpawnableOptionalArgs final { - //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that - //! made the function call. The returned list of entities contains all the newly created entities. + //! Callback that's called when respawning entities has completed. This can be triggered from a different thread than the one that + //! made the function call to respawn. The returned list of entities contains all the newly created entities. ReloadSpawnableCallback m_completionCallback; - //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. + //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Context will be used. AZ::SerializeContext* m_serializeContext { nullptr }; //! The priority at which this call will be executed. SpawnablePriority m_priority { SpawnablePriority_Default }; @@ -268,32 +268,32 @@ namespace AzFramework //! Spawn instances of all entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. - //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs + //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs. virtual void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; //! Spawn instances of some entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. //! @param priority The priority at which this call will be executed. //! @param entityIndices The indices into the template entities stored in the spawnable that will be used to spawn entities from. - //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs + //! @param optionalArgs Optional additional arguments, see SpawnEntitiesOptionalArgs. virtual void SpawnEntities( EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; //! Removes all entities in the provided list from the environment. //! @param ticket The ticket previously used to spawn entities with. //! @param priority The priority at which this call will be executed. - //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs + //! @param optionalArgs Optional additional arguments, see DespawnAllEntitiesOptionalArgs. virtual void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; //! Removes all entities in the provided list from the environment and reconstructs the entities from the provided spawnable. //! @param ticket Holds the information on the entities to reload. //! @param priority The priority at which this call will be executed. //! @param spawnable The spawnable that will replace the existing spawnable. Both need to have the same asset id. - //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs + //! @param optionalArgs Optional additional arguments, see ReloadSpawnableOptionalArgs. virtual void ReloadSpawnable( EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) = 0; //! List all entities that are spawned using this ticket. //! @param ticket Only the entities associated with this ticket will be listed. - //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to list the entities on. + //! @param optionalArgs Optional additional arguments, see ListEntitiesOptionalArgs. virtual void ListEntities( EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0; //! List all entities that are spawned using this ticket with their spawnable index. @@ -303,23 +303,23 @@ namespace AzFramework //! the same index may appear multiple times as there are no restriction on how many instance of a specific entity can be //! created. //! @param ticket Only the entities associated with this ticket will be listed. - //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to list the entities and indices on. + //! @param optionalArgs Optional additional arguments, see ListEntitiesOptionalArgs. virtual void ListIndicesAndEntities( EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0; //! Claim all entities that are spawned using this ticket. Ownership of the entities is transferred from the ticket to the //! caller through the callback. After this call the ticket will have no entities associated with it. The caller of //! this function will need to manage the entities after this call. //! @param ticket Only the entities associated with this ticket will be released. - //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to transfer the entities through. + //! @param optionalArgs Optional additional arguments, see ClaimEntitiesOptionalArgs. virtual void ClaimEntities( EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs = {}) = 0; //! Blocks until all operations made on the provided ticket before the barrier call have completed. //! @param ticket The ticket to monitor. - //! @param priority The priority at which this call will be executed. //! @param completionCallback Required callback that will be called as soon as the barrier has been reached. + //! @param optionalArgs Optional additional arguments, see BarrierOptionalArgs. virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) = 0; //! Register a handler for OnSpawned events. From b10ed227c0d7a59d51b6397a4f25a4b395293ae8 Mon Sep 17 00:00:00 2001 From: guthadam Date: Fri, 4 Jun 2021 13:54:45 -0500 Subject: [PATCH 51/71] Added version handling for removed fields --- .../Code/Source/Material/EditorMaterialComponentSlot.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp index bdc82acda6..65fa7bc286 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp @@ -70,6 +70,12 @@ namespace AZ } } + if (classElement.GetVersion() < 5) + { + classElement.RemoveElementByName(AZ_CRC_CE("matModUvOverrides")); + classElement.RemoveElementByName(AZ_CRC_CE("propertyOverrides")); + } + return true; } From 758f62a5531b3a28c10d8e410645a022868976cd Mon Sep 17 00:00:00 2001 From: amzn-sj Date: Fri, 4 Jun 2021 12:34:50 -0700 Subject: [PATCH 52/71] Fix Editor crash in Mac --- Registry/Platform/Mac/streamer.editor.setreg | 28 ++++++++++++++++++++ Registry/Platform/Mac/streamer.test.setreg | 24 +++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 Registry/Platform/Mac/streamer.editor.setreg create mode 100644 Registry/Platform/Mac/streamer.test.setreg diff --git a/Registry/Platform/Mac/streamer.editor.setreg b/Registry/Platform/Mac/streamer.editor.setreg new file mode 100644 index 0000000000..85360d128e --- /dev/null +++ b/Registry/Platform/Mac/streamer.editor.setreg @@ -0,0 +1,28 @@ +{ + "Amazon": + { + "AzCore": + { + "Streamer": + { + "Profiles": + { + "Generic": + { + "Stack": + [ + { + "$type": "AZ::IO::StorageDriveConfig", + // The maximum number of file handles that the drive will cache. + // On Mac the default limit for the number of file handles an application + // can have open is set to 256. So we need to set this to a lower value than on PC. + // This limit is set by "launchctl limit maxfiles" + "MaxFileHandles": 65 + } + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/Registry/Platform/Mac/streamer.test.setreg b/Registry/Platform/Mac/streamer.test.setreg new file mode 100644 index 0000000000..df41b7a350 --- /dev/null +++ b/Registry/Platform/Mac/streamer.test.setreg @@ -0,0 +1,24 @@ +{ + "Amazon": + { + "AzCore": + { + "Streamer": + { + "Profiles": + { + "Generic": + { + "Stack": + [ + { + "$type": "AZ::IO::StorageDriveConfig", + "MaxFileHandles": 65 + } + ] + } + } + } + } + } +} \ No newline at end of file From 16eb3bd82c9fa842b6f654115607e50bf7a5a65e Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Fri, 4 Jun 2021 15:25:49 -0500 Subject: [PATCH 53/71] Adding newline to streamer.editor.setreg --- Registry/Platform/Mac/streamer.editor.setreg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Registry/Platform/Mac/streamer.editor.setreg b/Registry/Platform/Mac/streamer.editor.setreg index 85360d128e..2fc8197c5f 100644 --- a/Registry/Platform/Mac/streamer.editor.setreg +++ b/Registry/Platform/Mac/streamer.editor.setreg @@ -25,4 +25,4 @@ } } } -} \ No newline at end of file +} From f7caa988081eca13d0680eb89eed5af5a795224f Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Fri, 4 Jun 2021 15:26:16 -0500 Subject: [PATCH 55/71] Adding newline to streamer.test.setreg --- Registry/Platform/Mac/streamer.test.setreg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Registry/Platform/Mac/streamer.test.setreg b/Registry/Platform/Mac/streamer.test.setreg index df41b7a350..2b053a497e 100644 --- a/Registry/Platform/Mac/streamer.test.setreg +++ b/Registry/Platform/Mac/streamer.test.setreg @@ -21,4 +21,4 @@ } } } -} \ No newline at end of file +} From 40d90c49a378bb82c1632fb1e29fbc6b7bd76774 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Fri, 4 Jun 2021 13:42:25 -0700 Subject: [PATCH 56/71] Disabled writing UserSettings.xml in Spawnable tests. --- .../Tests/Spawnable/SpawnableEntitiesManagerTests.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 484b7f46d7..8bb39449f1 100644 --- a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -40,6 +41,10 @@ namespace UnitTest m_application = new TestApplication(); AZ::ComponentApplication::Descriptor descriptor; m_application->Start(descriptor); + // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // in the unit tests. + AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); m_spawnable = aznew AzFramework::Spawnable( AZ::Data::AssetId::CreateString("{EB2E8A2B-F253-4A90-BBF4-55F2EED786B8}:0"), AZ::Data::AssetData::AssetStatus::Ready); From 55a46806590d04c45b3203fa27b5216536677100 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Fri, 4 Jun 2021 14:10:30 -0700 Subject: [PATCH 57/71] Fixed Multiplayer unit tests. The multiplayer unit tests created a SpawnableSystemComponent without an application to provide the Serialize Context. This caused an assert which failed the unit tests. Since the entity spawning system doesn't seem to be directly used the component was removed. --- Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp index 6ace4db592..7b09b65de4 100644 --- a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp +++ b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp @@ -30,7 +30,6 @@ namespace UnitTest { SetupAllocator(); AZ::NameDictionary::Create(); - m_spawnableComponent = new AzFramework::SpawnableSystemComponent(); m_netComponent = new AzNetworking::NetworkingSystemComponent(); m_mpComponent = new Multiplayer::MultiplayerSystemComponent(); @@ -46,7 +45,6 @@ namespace UnitTest { delete m_mpComponent; delete m_netComponent; - delete m_spawnableComponent; AZ::NameDictionary::Destroy(); TeardownAllocator(); } @@ -76,7 +74,6 @@ namespace UnitTest AzNetworking::NetworkingSystemComponent* m_netComponent = nullptr; Multiplayer::MultiplayerSystemComponent* m_mpComponent = nullptr; - AzFramework::SpawnableSystemComponent* m_spawnableComponent = nullptr; }; TEST_F(MultiplayerSystemTests, TestInitEvent) From 50d6e36ccd17c9214f057e749dc73db5a2c148b8 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 14:36:46 -0700 Subject: [PATCH 58/71] Bug and improvements to Editor/AP debugging settings (#1146) --- Code/Sandbox/Editor/CMakeLists.txt | 4 ++-- Code/Tools/AssetProcessor/CMakeLists.txt | 4 ++-- cmake/Projects.cmake | 12 +++++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Code/Sandbox/Editor/CMakeLists.txt b/Code/Sandbox/Editor/CMakeLists.txt index 01b58e3f77..58d5e86a2f 100644 --- a/Code/Sandbox/Editor/CMakeLists.txt +++ b/Code/Sandbox/Editor/CMakeLists.txt @@ -191,8 +191,8 @@ ly_add_translations( ) ly_add_dependencies(Editor AssetProcessor) -if(LY_FIRST_PROJECT_PATH) - set_property(TARGET Editor APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_FIRST_PROJECT_PATH}\"") +if(LY_DEFAULT_PROJECT_PATH) + set_property(TARGET Editor APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\"") endif() ################################################################################ diff --git a/Code/Tools/AssetProcessor/CMakeLists.txt b/Code/Tools/AssetProcessor/CMakeLists.txt index 6c12ab6024..2db888218b 100644 --- a/Code/Tools/AssetProcessor/CMakeLists.txt +++ b/Code/Tools/AssetProcessor/CMakeLists.txt @@ -125,8 +125,8 @@ ly_add_target( AZ::AssetProcessorBatch.Static ) -if(LY_FIRST_PROJECT_PATH) - set_property(TARGET AssetProcessor AssetProcessorBatch APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_FIRST_PROJECT_PATH}\"") +if(LY_DEFAULT_PROJECT_PATH) + set_property(TARGET AssetProcessor AssetProcessorBatch APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\"") endif() # Adds the AssetProcessorBatch target as a C preprocessor define so that it can be used as a Settings Registry diff --git a/cmake/Projects.cmake b/cmake/Projects.cmake index adaf7ee15f..eadfa79c1b 100644 --- a/cmake/Projects.cmake +++ b/cmake/Projects.cmake @@ -167,9 +167,6 @@ endfunction() # Add the projects here so the above function is found foreach(project ${LY_PROJECTS}) file(REAL_PATH ${project} full_directory_path BASE_DIRECTORY ${CMAKE_SOURCE_DIR}) - if(NOT LY_FIRST_PROJECT) - ly_set(LY_FIRST_PROJECT_PATH ${full_directory_path}) - endif() string(SHA256 full_directory_hash ${full_directory_path}) # Truncate the full_directory_hash down to 8 characters to avoid hitting the Windows 260 character path limit @@ -182,4 +179,13 @@ foreach(project ${LY_PROJECTS}) ly_generate_project_build_path_setreg(${full_directory_path}) add_project_json_external_subdirectories(${full_directory_path}) endforeach() + +# If just one project is defined we pass it as a parameter to the applications +list(LENGTH LY_PROJECTS projects_length) +if(projects_length EQUAL "1") + list(GET LY_PROJECTS 0 project) + file(REAL_PATH ${project} full_directory_path BASE_DIRECTORY ${CMAKE_SOURCE_DIR}) + ly_set(LY_DEFAULT_PROJECT_PATH ${full_directory_path}) +endif() + ly_set(LY_PROJECTS_FOLDER_NAME ${LY_PROJECTS_FOLDER_NAME}) From 8a079da914ceadb07846d66230dd031a3b8cccc6 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Sat, 5 Jun 2021 00:04:26 +0200 Subject: [PATCH 59/71] GemCatalog: Gem cart widget and overlay window * [LYN-4174] Added icons for gem catalog summary cart * [LYN-4174] Gem Catalog: Text eliding for too long gem names and creators * [LYN-4174] Gem catalog: Resetting filters when re-initializing for another project * [LYN-4174] Gem Catalog: Fixed a bug with filters being applied/remembered after leaving gem catalog and coming back editing another project * [LYN-4174] GemCatalog: Gem cart widget and overlay window * Added cart button with dynamic label to display the number of gems to be enabled/disabled and a arrow down button to indicate some sort of pop-up/overlay window will appear on click. * Overlay gem tags update dynamically while the dialog is open based on the gem model. * Moved some styling from C++ to the style sheet. --- .../Resources/CarrotArrowDown.svg | 3 + .../Resources/ProjectManager.qrc | 3 + .../Resources/ProjectManager.qss | 40 ++- .../ProjectManager/Resources/Summary.svg | 3 + .../ProjectManager/Resources/WindowClose.svg | 4 + .../GemCatalog/GemCatalogHeaderWidget.cpp | 235 +++++++++++++++++- .../GemCatalog/GemCatalogHeaderWidget.h | 64 ++++- .../Source/GemCatalog/GemCatalogScreen.cpp | 8 +- .../Source/GemCatalog/GemCatalogScreen.h | 2 + .../Source/GemCatalog/GemItemDelegate.cpp | 8 +- .../GemCatalog/GemSortFilterProxyModel.cpp | 11 + .../GemCatalog/GemSortFilterProxyModel.h | 1 + 12 files changed, 350 insertions(+), 32 deletions(-) create mode 100644 Code/Tools/ProjectManager/Resources/CarrotArrowDown.svg create mode 100644 Code/Tools/ProjectManager/Resources/Summary.svg create mode 100644 Code/Tools/ProjectManager/Resources/WindowClose.svg diff --git a/Code/Tools/ProjectManager/Resources/CarrotArrowDown.svg b/Code/Tools/ProjectManager/Resources/CarrotArrowDown.svg new file mode 100644 index 0000000000..73545968d5 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/CarrotArrowDown.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index 04d5e98a10..62b7d23e9c 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -26,5 +26,8 @@ Backgrounds/FirstTimeBackgroundImage.jpg ArrowDownLine.svg ArrowUpLine.svg + CarrotArrowDown.svg + Summary.svg + WindowClose.svg diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 224574f522..6fd4086c58 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -115,7 +115,7 @@ QTabBar::tab:pressed /************** General (Modal windows) **************/ #header { - background-color:#111111; + background-color:#111111; min-height:80px; max-height:80px; } @@ -172,8 +172,8 @@ QTabBar::tab:pressed #footer > QPushButton { qproperty-flat: true; - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #0095f2, stop: 1.0 #1e70eb); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #0095f2, stop: 1.0 #1e70eb); border-radius: 3px; min-height: 28px; max-height: 28px; @@ -181,26 +181,26 @@ QTabBar::tab:pressed margin-right:30px; } #footer > QPushButton:hover { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #10A5f2, stop: 1.0 #2e80eb); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #10A5f2, stop: 1.0 #2e80eb); } #footer > QPushButton:pressed { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #0085e2, stop: 1.0 #0e60db); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #0085e2, stop: 1.0 #0e60db); } #footer > QPushButton[secondary="true"] { margin-right: 10px; - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #888888, stop: 1.0 #555555); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #888888, stop: 1.0 #555555); } #footer > QPushButton[secondary="true"]:hover { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #999999, stop: 1.0 #666666); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #999999, stop: 1.0 #666666); } #footer > QPushButton[secondary="true"]:pressed { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #555555, stop: 1.0 #777777); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #555555, stop: 1.0 #777777); } /************** Project Settings **************/ @@ -356,6 +356,20 @@ QTabBar::tab:pressed font-size: 18px; } +#GemCatalogCart { + background-color: #555555; +} + +#GemCatalogCartCountLabel { + font-size: 12px; + background-color: #4285F4; + border-radius: 3px; +} + +#GemCatalogCartOverlaySectionLabel { + font-weight: 600; +} + /************** Gem Catalog (Inspector) **************/ #GemCatalogInspector { diff --git a/Code/Tools/ProjectManager/Resources/Summary.svg b/Code/Tools/ProjectManager/Resources/Summary.svg new file mode 100644 index 0000000000..fe26718aff --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Summary.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/WindowClose.svg b/Code/Tools/ProjectManager/Resources/WindowClose.svg new file mode 100644 index 0000000000..0485ff95cd --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/WindowClose.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 6402121e4a..a98135d3e6 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -10,24 +10,229 @@ * */ -#include #include #include +#include #include +#include +#include namespace O3DE::ProjectManager { - GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent) + CartOverlayWidget::CartOverlayWidget(GemModel* gemModel, QWidget* parent) + : QWidget(parent) + , m_gemModel(gemModel) + { + setObjectName("GemCatalogCart"); + + m_layout = new QVBoxLayout(); + m_layout->setSpacing(0); + m_layout->setMargin(0); + m_layout->setAlignment(Qt::AlignTop); + setLayout(m_layout); + + QHBoxLayout* hLayout = new QHBoxLayout(); + + QPushButton* closeButton = new QPushButton(); + closeButton->setFlat(true); + closeButton->setFocusPolicy(Qt::NoFocus); + closeButton->setIcon(QIcon(":/WindowClose.svg")); + connect(closeButton, &QPushButton::clicked, this, [=] + { + deleteLater(); + }); + hLayout->addSpacerItem(new QSpacerItem(10, 0, QSizePolicy::Expanding)); + hLayout->addWidget(closeButton); + m_layout->addLayout(hLayout); + + // enabled + { + m_enabledWidget = new QWidget(); + m_enabledWidget->setFixedWidth(s_width); + m_layout->addWidget(m_enabledWidget); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + m_enabledWidget->setLayout(layout); + + m_enabledLabel = new QLabel(); + m_enabledLabel->setObjectName("GemCatalogCartOverlaySectionLabel"); + layout->addWidget(m_enabledLabel); + m_enabledTagContainer = new TagContainerWidget(); + layout->addWidget(m_enabledTagContainer); + } + + // disabled + { + m_disabledWidget = new QWidget(); + m_disabledWidget->setFixedWidth(s_width); + m_layout->addWidget(m_disabledWidget); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + m_disabledWidget->setLayout(layout); + + m_disabledLabel = new QLabel(); + m_disabledLabel->setObjectName("GemCatalogCartOverlaySectionLabel"); + layout->addWidget(m_disabledLabel); + m_disabledTagContainer = new TagContainerWidget(); + layout->addWidget(m_disabledTagContainer); + } + + setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog); + + Update(); + connect(gemModel, &GemModel::dataChanged, this, [=] + { + Update(); + }); + } + + void CartOverlayWidget::Update() + { + const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); + if (toBeAdded.isEmpty()) + { + m_enabledWidget->hide(); + } + else + { + m_enabledTagContainer->Update(ConvertFromModelIndices(toBeAdded)); + m_enabledLabel->setText(QString("%1 %2").arg(QString::number(toBeAdded.size()), tr("Gems to be enabled"))); + m_enabledWidget->show(); + } + + const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); + if (toBeRemoved.isEmpty()) + { + m_disabledWidget->hide(); + } + else + { + m_disabledTagContainer->Update(ConvertFromModelIndices(toBeRemoved)); + m_disabledLabel->setText(QString("%1 %2").arg(QString::number(toBeRemoved.size()), tr("Gems to be disabled"))); + m_disabledWidget->show(); + } + } + + QStringList CartOverlayWidget::ConvertFromModelIndices(const QVector& gems) const + { + QStringList gemNames; + gemNames.reserve(gems.size()); + for (const QModelIndex& modelIndex : gems) + { + gemNames.push_back(GemModel::GetName(modelIndex)); + } + return gemNames; + } + + CartButton::CartButton(GemModel* gemModel, QWidget* parent) + : QWidget(parent) + , m_gemModel(gemModel) + { + m_layout = new QHBoxLayout(); + m_layout->setMargin(0); + setLayout(m_layout); + + QPushButton* iconButton = new QPushButton(); + iconButton->setFlat(true); + iconButton->setFocusPolicy(Qt::NoFocus); + iconButton->setIcon(QIcon(":/Summary.svg")); + iconButton->setFixedSize(s_iconSize, s_iconSize); + connect(iconButton, &QPushButton::clicked, this, &CartButton::ShowOverlay); + m_layout->addWidget(iconButton); + + m_countLabel = new QLabel(); + m_countLabel->setObjectName("GemCatalogCartCountLabel"); + m_countLabel->setFixedHeight(s_iconSize - 1); // Compensate for the empty icon space by using a slightly smaller label height. + m_layout->addWidget(m_countLabel); + + m_dropDownButton = new QPushButton(); + m_dropDownButton->setFlat(true); + m_dropDownButton->setFocusPolicy(Qt::NoFocus); + m_dropDownButton->setIcon(QIcon(":/CarrotArrowDown.svg")); + m_dropDownButton->setFixedSize(s_arrowDownIconSize, s_arrowDownIconSize); + connect(m_dropDownButton, &QPushButton::clicked, this, &CartButton::ShowOverlay); + m_layout->addWidget(m_dropDownButton); + + // Adjust the label text whenever the model gets updated. + connect(gemModel, &GemModel::dataChanged, [=] + { + const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); + const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); + + const int count = toBeAdded.size() + toBeRemoved.size(); + m_countLabel->setText(QString::number(count)); + + m_dropDownButton->setVisible(!toBeAdded.isEmpty() || !toBeRemoved.isEmpty()); + + // Automatically close the overlay window in case there are no gems to be enabled or disabled anymore. + if (m_cartOverlay && toBeAdded.isEmpty() && toBeRemoved.isEmpty()) + { + m_cartOverlay->deleteLater(); + m_cartOverlay = nullptr; + } + }); + } + + void CartButton::mousePressEvent([[maybe_unused]] QMouseEvent* event) + { + ShowOverlay(); + } + + void CartButton::ShowOverlay() + { + const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); + const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); + if (toBeAdded.isEmpty() && toBeRemoved.isEmpty()) + { + return; + } + + if (m_cartOverlay) + { + // Directly delete the former overlay before creating the new one. + // Don't use deleteLater() here. This might overwrite the new overlay pointer + // depending on the event queue. + delete m_cartOverlay; + } + + m_cartOverlay = new CartOverlayWidget(m_gemModel, this); + connect(m_cartOverlay, &QWidget::destroyed, this, [=] + { + // Reset the overlay pointer on destruction to prevent dangling pointers. + m_cartOverlay = nullptr; + }); + m_cartOverlay->show(); + + const QPoint parentPos = m_dropDownButton->mapToParent(m_dropDownButton->pos()); + const QPoint globalPos = m_dropDownButton->mapToGlobal(m_dropDownButton->pos()); + const QPoint offset(-4, 10); + m_cartOverlay->setGeometry(globalPos.x() - parentPos.x() - m_cartOverlay->width() + width() + offset.x(), + globalPos.y() + offset.y(), + m_cartOverlay->width(), + m_cartOverlay->height()); + } + + CartButton::~CartButton() + { + // Make sure the overlay window is automatically closed in case the gem catalog is destroyed. + if (m_cartOverlay) + { + m_cartOverlay->deleteLater(); + } + } + + GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, QWidget* parent) : QFrame(parent) { QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setAlignment(Qt::AlignLeft); - hLayout->setMargin(0); + hLayout->setContentsMargins(10, 7, 10, 7); setLayout(hLayout); setObjectName("GemCatalogHeaderWidget"); - - hLayout->addSpacing(7); + setFixedHeight(s_height); QLabel* titleLabel = new QLabel(tr("Gem Catalog")); titleLabel->setObjectName("GemCatalogTitle"); @@ -35,17 +240,23 @@ namespace O3DE::ProjectManager hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); - AzQtComponents::SearchLineEdit* filterLineEdit = new AzQtComponents::SearchLineEdit(); - filterLineEdit->setStyleSheet("background-color: #DDDDDD;"); - connect(filterLineEdit, &QLineEdit::textChanged, this, [=](const QString& text) + m_filterLineEdit = new AzQtComponents::SearchLineEdit(); + m_filterLineEdit->setStyleSheet("background-color: #DDDDDD;"); + connect(m_filterLineEdit, &QLineEdit::textChanged, this, [=](const QString& text) { filterProxyModel->SetSearchString(text); }); - hLayout->addWidget(filterLineEdit); + hLayout->addWidget(m_filterLineEdit); hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); - hLayout->addSpacerItem(new QSpacerItem(140, 0, QSizePolicy::Fixed)); - - setFixedHeight(60); + hLayout->addSpacerItem(new QSpacerItem(75, 0, QSizePolicy::Fixed)); + + CartButton* cartButton = new CartButton(gemModel); + hLayout->addWidget(cartButton); + } + + void GemCatalogHeaderWidget::ReinitForProject() + { + m_filterLineEdit->setText({}); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index 3e065edd8f..bef7555618 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -13,19 +13,81 @@ #pragma once #if !defined(Q_MOC_RUN) +#include +#include #include +#include #include +#include +#include +#include +#include #endif namespace O3DE::ProjectManager { + class CartOverlayWidget + : public QWidget + { + Q_OBJECT // AUTOMOC + + public: + CartOverlayWidget(GemModel* gemModel, QWidget* parent = nullptr); + void Update(); + + private: + QStringList ConvertFromModelIndices(const QVector& gems) const; + + QVBoxLayout* m_layout = nullptr; + GemModel* m_gemModel = nullptr; + + QWidget* m_enabledWidget = nullptr; + QLabel* m_enabledLabel = nullptr; + TagContainerWidget* m_enabledTagContainer = nullptr; + + QWidget* m_disabledWidget = nullptr; + QLabel* m_disabledLabel = nullptr; + TagContainerWidget* m_disabledTagContainer = nullptr; + + inline constexpr static int s_width = 240; + }; + + class CartButton + : public QWidget + { + Q_OBJECT // AUTOMOC + + public: + CartButton(GemModel* gemModel, QWidget* parent = nullptr); + ~CartButton(); + void ShowOverlay(); + + private: + void mousePressEvent(QMouseEvent* event) override; + + GemModel* m_gemModel = nullptr; + QHBoxLayout* m_layout = nullptr; + QLabel* m_countLabel = nullptr; + QPushButton* m_dropDownButton = nullptr; + CartOverlayWidget* m_cartOverlay = nullptr; + + inline constexpr static int s_iconSize = 24; + inline constexpr static int s_arrowDownIconSize = 8; + }; + class GemCatalogHeaderWidget : public QFrame { Q_OBJECT // AUTOMOC public: - explicit GemCatalogHeaderWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr); + explicit GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr); ~GemCatalogHeaderWidget() = default; + + void ReinitForProject(); + + private: + AzQtComponents::SearchLineEdit* m_filterLineEdit = nullptr; + inline constexpr static int s_height = 60; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index aa36c1b0ab..4424767c0b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -35,8 +34,8 @@ namespace O3DE::ProjectManager vLayout->setSpacing(0); setLayout(vLayout); - GemCatalogHeaderWidget* headerWidget = new GemCatalogHeaderWidget(m_proxModel); - vLayout->addWidget(headerWidget); + m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel); + vLayout->addWidget(m_headerWidget); QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setMargin(0); @@ -77,10 +76,11 @@ namespace O3DE::ProjectManager m_filterWidget->deleteLater(); } + m_proxModel->ResetFilters(); m_filterWidget = new GemFilterWidget(m_proxModel); m_filterWidgetLayout->addWidget(m_filterWidget); - m_proxModel->InvalidateFilter(); + m_headerWidget->ReinitForProject(); // Select the first entry after everything got correctly sized QTimer::singleShot(200, [=]{ diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 0847d9b74e..f5092e837a 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -14,6 +14,7 @@ #if !defined(Q_MOC_RUN) #include +#include #include #include #include @@ -40,6 +41,7 @@ namespace O3DE::ProjectManager GemListView* m_gemListView = nullptr; GemInspector* m_gemInspector = nullptr; GemModel* m_gemModel = nullptr; + GemCatalogHeaderWidget* m_headerWidget = nullptr; GemSortFilterProxyModel* m_proxModel = nullptr; QVBoxLayout* m_filterWidgetLayout = nullptr; GemFilterWidget* m_filterWidget = nullptr; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp index 0fc0d89fcb..8aa68fb7a2 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -53,6 +53,7 @@ namespace O3DE::ProjectManager QFont standardFont(options.font); standardFont.setPixelSize(s_fontSize); + QFontMetrics standardFontMetrics(standardFont); painter->save(); painter->setClipping(true); @@ -78,8 +79,10 @@ namespace O3DE::ProjectManager } // Gem name - const QString gemName = GemModel::GetName(modelIndex); + QString gemName = GemModel::GetName(modelIndex); QFont gemNameFont(options.font); + const int firstColumnMaxTextWidth = s_summaryStartX - 30; + gemName = QFontMetrics(gemNameFont).elidedText(gemName, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth); gemNameFont.setPixelSize(s_gemNameFontSize); gemNameFont.setBold(true); QRect gemNameRect = GetTextRect(gemNameFont, gemName, s_gemNameFontSize); @@ -90,7 +93,8 @@ namespace O3DE::ProjectManager painter->drawText(gemNameRect, Qt::TextSingleLine, gemName); // Gem creator - const QString gemCreator = GemModel::GetCreator(modelIndex); + QString gemCreator = GemModel::GetCreator(modelIndex); + gemCreator = standardFontMetrics.elidedText(gemCreator, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth); QRect gemCreatorRect = GetTextRect(standardFont, gemCreator, s_fontSize); gemCreatorRect.moveTo(contentRect.left(), contentRect.top() + gemNameRect.height()); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp index 33936f417e..d8f41c077e 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp @@ -130,4 +130,15 @@ namespace O3DE::ProjectManager invalidate(); emit OnInvalidated(); } + + void GemSortFilterProxyModel::ResetFilters() + { + m_searchString.clear(); + m_gemOriginFilter = {}; + m_platformFilter = {}; + m_typeFilter = {}; + m_featureFilter = {}; + + InvalidateFilter(); + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h index e5554c020c..f24a724ecf 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h @@ -51,6 +51,7 @@ namespace O3DE::ProjectManager void SetFeatures(const QSet& features) { m_featureFilter = features; InvalidateFilter(); } void InvalidateFilter(); + void ResetFilters(); signals: void OnInvalidated(); From e71a4656bc1a3a304ee124c426d0936dafc22a38 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 15:10:20 -0700 Subject: [PATCH 60/71] SPEC-2513 Fixes to enable w4450 (#1145) * Fix for w4457 * Nothing to fix, seems we deleted all the code that was causing this offense --- cmake/Platform/Common/MSVC/Configurations_msvc.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake index 357d578c44..d0e4d9ed73 100644 --- a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake +++ b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake @@ -72,7 +72,6 @@ ly_append_configurations_options( /wd4366 # the result of unary operator may be unaligned /wd4389 # comparison, signed/unsigned mismatch /wd4436 # the result of unary operator may be unaligned - /wd4450 # declaration hides global declaration # Enabling warnings that are disabled by default from /W4 # https://docs.microsoft.com/en-us/cpp/preprocessor/compiler-warnings-that-are-off-by-default?view=vs-2019 From cf35585bc0d50e6e09c6e712bdb66351cf40fc94 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 15:25:57 -0700 Subject: [PATCH 61/71] Making incremental linking off by default (#1154) --- cmake/Platform/Common/MSVC/Configurations_msvc.cmake | 2 +- scripts/build/Platform/Windows/build_config.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake index d0e4d9ed73..8c22677f91 100644 --- a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake +++ b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake @@ -126,7 +126,7 @@ ly_append_configurations_options( /INCREMENTAL:NO ) -set(LY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG TRUE CACHE BOOL "Indicates if incremental linking is used in debug configurations (default = TRUE)") +set(LY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG FALSE CACHE BOOL "Indicates if incremental linking is used in debug configurations (default = FALSE)") if(LY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG) ly_append_configurations_options( COMPILATION_DEBUG diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index fc4668de0a..552ef2c6fd 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -87,7 +87,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -101,7 +101,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", From 1396110f6d0edc8795ae7a439945b399b3684e5a Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 15:46:19 -0700 Subject: [PATCH 62/71] Preventing builds from cleaning on each step (#1151) --- scripts/build/Jenkins/Jenkinsfile | 14 +++++++------- scripts/build/Platform/Linux/build_linux.sh | 1 - scripts/build/Platform/Mac/build_mac.sh | 1 - scripts/build/Platform/Windows/build_windows.cmd | 1 - 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 2f6be2b060..3cf7d92ba6 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -313,13 +313,13 @@ def PreBuildCommonSteps(Map pipelineConfig, String repositoryName, String projec script: 'python/get_python.bat' } - if(env.CLEAN_OUTPUT_DIRECTORY?.toBoolean() || env.CLEAN_ASSETS?.toBoolean()) { - def command = "${pipelineConfig.PYTHON_DIR}/python" - if(env.IS_UNIX) command += '.sh' - else command += '.cmd' - command += " -u ${pipelineConfig.BUILD_ENTRY_POINT} --platform ${platform} --type clean" - palSh(command, "Running ${platform} clean") - } + // Always run the clean step, the scripts detect what variables were set, but it also cleans if + // the NODE_LABEL has changed + def command = "${pipelineConfig.PYTHON_DIR}/python" + if(env.IS_UNIX) command += '.sh' + else command += '.cmd' + command += " -u ${pipelineConfig.BUILD_ENTRY_POINT} --platform ${platform} --type clean" + palSh(command, "Running ${platform} clean") } } diff --git a/scripts/build/Platform/Linux/build_linux.sh b/scripts/build/Platform/Linux/build_linux.sh index ef1b1dcb96..f88d3ab116 100755 --- a/scripts/build/Platform/Linux/build_linux.sh +++ b/scripts/build/Platform/Linux/build_linux.sh @@ -14,7 +14,6 @@ set -o errexit # exit on the first failure encountered BASEDIR=$(dirname "$0") source $BASEDIR/env_linux.sh -source $BASEDIR/clean_linux.sh mkdir -p ${OUTPUT_DIRECTORY} SOURCE_DIRECTORY=${PWD} diff --git a/scripts/build/Platform/Mac/build_mac.sh b/scripts/build/Platform/Mac/build_mac.sh index 4a61f97fe4..473a968d98 100755 --- a/scripts/build/Platform/Mac/build_mac.sh +++ b/scripts/build/Platform/Mac/build_mac.sh @@ -14,7 +14,6 @@ set -o errexit # exit on the first failure encountered BASEDIR=$(dirname "$0") source $BASEDIR/env_mac.sh -source $BASEDIR/clean_mac.sh mkdir -p ${OUTPUT_DIRECTORY} SOURCE_DIRECTORY=${PWD} diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index 474c1720df..4dcd12d008 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -13,7 +13,6 @@ REM SETLOCAL EnableDelayedExpansion CALL %~dp0env_windows.cmd -CALL %~dp0clean_windows.cmd IF NOT EXIST "%OUTPUT_DIRECTORY%" ( MKDIR %OUTPUT_DIRECTORY%. From 74f474aae2084c0489fd9dfa8b5025970b197e03 Mon Sep 17 00:00:00 2001 From: rgba16f <82187279+rgba16f@users.noreply.github.com> Date: Fri, 4 Jun 2021 18:02:09 -0500 Subject: [PATCH 63/71] Add unit tests for the ViewportScreen ndc <-> worldspace utility functions (#1149) Add ScreenNdcToWorld function to enable round trip testing. --- .../AzFramework/Viewport/ViewportScreen.cpp | 17 +++- .../AzFramework/Viewport/ViewportScreen.h | 8 +- .../Tests/Viewport/ViewportScreenTests.cpp | 87 ++++++++++++++++++- 3 files changed, 104 insertions(+), 8 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp index 5d2d02a398..8283e0b1f2 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp @@ -128,12 +128,12 @@ namespace AzFramework worldPosition, CameraView(cameraState), CameraProjection(cameraState), cameraState.m_viewportSize); } - AZ::Vector3 ScreenToWorld( - const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView, - const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize) + AZ::Vector3 ScreenNDCToWorld( + const AZ::Vector2& normalizedScreenPosition, const AZ::Matrix4x4& inverseCameraView, + const AZ::Matrix4x4& inverseCameraProjection) { // convert screen space coordinates from <0, 1> to <-1,1> range - const auto ndcPosition = NDCFromScreenPoint(screenPosition, viewportSize) * 2.0f - AZ::Vector2::CreateOne(); + const auto ndcPosition = normalizedScreenPosition * 2.0f - AZ::Vector2::CreateOne(); // transform ndc space position to clip space const auto clipSpacePosition = inverseCameraProjection * Vector2ToVector4(ndcPosition, -1.0f, 1.0f); @@ -145,6 +145,15 @@ namespace AzFramework return worldPosition; } + AZ::Vector3 ScreenToWorld( + const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView, + const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize) + { + const auto normalizedScreenPosition = NDCFromScreenPoint(screenPosition, viewportSize); + + return ScreenNDCToWorld(normalizedScreenPosition, inverseCameraView, inverseCameraProjection); + } + AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState) { return ScreenToWorld( diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h index a2c650465f..844f52b565 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h @@ -42,7 +42,7 @@ namespace AzFramework const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection, const AZ::Vector2& viewportSize); - //! Unprojects a position in screen space to world space. + //! Unprojects a position in screen space pixel coordinates to world space. //! Note: The position returned will be on the near clip plane of the camera in world space. AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState); @@ -52,6 +52,12 @@ namespace AzFramework const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView, const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize); + //! Unprojects a position in screen space normalized device coordinates to world space. + //! Note: The position returned will be on the near clip plane of the camera in world space. + AZ::Vector3 ScreenNDCToWorld( + const AZ::Vector2& ndcPosition, const AZ::Matrix4x4& inverseCameraView, + const AZ::Matrix4x4& inverseCameraProjection); + //! Returns the camera projection for the current camera state. AZ::Matrix4x4 CameraProjection(const CameraState& cameraState); diff --git a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp index 18462bc97b..d82c7ec425 100644 --- a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,15 @@ namespace UnitTest { + // transform a point from normalized device coordinates to world space, and then from world space back to normalized device coordinates + AZ::Vector2 ScreenNDCToWorldToScreenNDC( + const AZ::Vector2& ndcPoint, const AzFramework::CameraState& cameraState) + { + const auto worldResult = AzFramework::ScreenNDCToWorld(ndcPoint, InverseCameraView(cameraState), InverseCameraProjection(cameraState)); + const auto ndcResult = AzFramework::WorldToScreenNDC(worldResult, CameraView(cameraState), CameraProjection(cameraState)); + return AZ::Vector3ToVector2(ndcResult); + } + // transform a point from screen space to world space, and then from world space back to screen space AzFramework::ScreenPoint ScreenToWorldToScreen( const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState) @@ -30,7 +40,8 @@ namespace UnitTest const auto worldResult = AzFramework::ScreenToWorld(screenPoint, cameraState); return AzFramework::WorldToScreen(worldResult, cameraState); } - + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // ScreenPoint tests TEST(ViewportScreen, WorldToScreenAndScreenToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin) { using AzFramework::ScreenPoint; @@ -38,8 +49,6 @@ namespace UnitTest const auto screenDimensions = AZ::Vector2(800.0f, 600.0f); const auto cameraPosition = AZ::Vector3::CreateAxisY(-10.0f); - // note: nearClip is 0.1 - the world space value returned will be aligned to the near clip - // plane of the camera so use that to confirm the mapping to/from is correct const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions); { const auto expectedScreenPoint = ScreenPoint{600, 450}; @@ -81,6 +90,8 @@ namespace UnitTest EXPECT_EQ(resultScreenPoint, expectedScreenPoint); } + // note: nearClip is 0.1 - the world space value returned will be aligned to the near clip + // plane of the camera so use that to confirm the mapping to/from is correct TEST(ViewportScreen, ScreenToWorldReturnsPositionOnNearClipPlaneInWorldSpace) { using AzFramework::ScreenPoint; @@ -94,7 +105,75 @@ namespace UnitTest const auto worldResult = AzFramework::ScreenToWorld(ScreenPoint{400, 300}, cameraState); EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f))); } + + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // NDC tests + TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin) + { + using NdcPoint = AZ::Vector2; + + const auto screenDimensions = AZ::Vector2(800.0f, 600.0f); + const auto cameraPosition = AZ::Vector3::CreateAxisY(-10.0f); + + const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions); + { + const auto expectedNdcPoint = NdcPoint{0.75f, 0.75f}; + const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + + { + const auto expectedNdcPoint = NdcPoint{0.5f, 0.5f}; + const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + + { + const auto expectedNdcPoint = NdcPoint{0.0f, 0.0f}; + const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + + { + const auto expectedNdcPoint = NdcPoint{1.0f, 1.0f}; + const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + } + + TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueOrientatedCamera) + { + using NdcPoint = AZ::Vector2; + + const auto screenDimensions = AZ::Vector2(800.0f, 600.0f); + const auto cameraTransform = + AZ::Transform::CreateRotationX(AZ::DegToRad(45.0f)) * AZ::Transform::CreateRotationZ(AZ::DegToRad(90.0f)); + + const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions); + + const auto expectedNdcPoint = NdcPoint{0.25f, 0.5f}; + const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + + // note: nearClip is 0.1 - the world space value returned will be aligned to the near clip + // plane of the camera so use that to confirm the mapping to/from is correct + TEST(ViewportScreen, ScreenNDCToWorldReturnsPositionOnNearClipPlaneInWorldSpace) + { + using NdcPoint = AZ::Vector2; + + const auto screenDimensions = AZ::Vector2(800.0f, 600.0f); + const auto cameraTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) * + AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f)); + + const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions); + + const auto worldResult = AzFramework::ScreenNDCToWorld(NdcPoint{0.5f, 0.5f}, InverseCameraView(cameraState), InverseCameraProjection(cameraState)); + EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f))); + } + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // ScreenVector tests TEST(ViewportScreen, SubstractingScreenPointGivesScreenVector) { using AzFramework::ScreenPoint; @@ -220,6 +299,8 @@ namespace UnitTest EXPECT_NEAR(AzFramework::ScreenVectorLength(ScreenVector(12, 15)), 19.20937f, 0.001f); } + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // Other tests TEST(ViewportScreen, CanGetCameraTransformFromCameraViewAndBack) { const auto screenDimensions = AZ::Vector2(1024.0f, 768.0f); From 6ee58b7b641ea9b7bd8727217920e0f9477bc664 Mon Sep 17 00:00:00 2001 From: amzn-sj Date: Fri, 4 Jun 2021 16:09:37 -0700 Subject: [PATCH 64/71] Fix overrides for array in editor setreg. Remove the test overrides. --- Registry/Platform/Mac/streamer.editor.setreg | 5 ++++ Registry/Platform/Mac/streamer.test.setreg | 24 -------------------- 2 files changed, 5 insertions(+), 24 deletions(-) delete mode 100644 Registry/Platform/Mac/streamer.test.setreg diff --git a/Registry/Platform/Mac/streamer.editor.setreg b/Registry/Platform/Mac/streamer.editor.setreg index 85360d128e..dafcfdc9d9 100644 --- a/Registry/Platform/Mac/streamer.editor.setreg +++ b/Registry/Platform/Mac/streamer.editor.setreg @@ -18,6 +18,11 @@ // can have open is set to 256. So we need to set this to a lower value than on PC. // This limit is set by "launchctl limit maxfiles" "MaxFileHandles": 65 + }, + { + "$type": "AzFramework::RemoteStorageDriveConfig", + // The maximum number of file handles that the drive will cache. + "MaxFileHandles": 1024 } ] } diff --git a/Registry/Platform/Mac/streamer.test.setreg b/Registry/Platform/Mac/streamer.test.setreg deleted file mode 100644 index df41b7a350..0000000000 --- a/Registry/Platform/Mac/streamer.test.setreg +++ /dev/null @@ -1,24 +0,0 @@ -{ - "Amazon": - { - "AzCore": - { - "Streamer": - { - "Profiles": - { - "Generic": - { - "Stack": - [ - { - "$type": "AZ::IO::StorageDriveConfig", - "MaxFileHandles": 65 - } - ] - } - } - } - } - } -} \ No newline at end of file From 9b1775427895177c6d0125dd6d6ed6bc95d6c823 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Fri, 4 Jun 2021 16:14:25 -0700 Subject: [PATCH 65/71] Project Template details and preview changes --- .../Resources/ArrowBack_Hover.svg | 3 + .../Resources/DefaultTemplate.png | 3 + .../Resources/ProjectManager.qrc | 2 + .../Resources/ProjectManager.qss | 67 ++++++- .../Source/CreateProjectCtrl.cpp | 167 ++++++++++++------ .../ProjectManager/Source/CreateProjectCtrl.h | 36 +++- .../Source/NewProjectSettingsScreen.cpp | 125 +++++++++++-- .../Source/NewProjectSettingsScreen.h | 14 ++ .../Source/ProjectTemplateInfo.h | 1 + .../ProjectManager/Source/PythonBindings.cpp | 9 +- .../Source/TemplateButtonWidget.cpp | 65 +++++++ .../Source/TemplateButtonWidget.h | 37 ++++ .../project_manager_files.cmake | 2 + Templates/DefaultProject/template.json | 5 +- 14 files changed, 455 insertions(+), 81 deletions(-) create mode 100644 Code/Tools/ProjectManager/Resources/ArrowBack_Hover.svg create mode 100644 Code/Tools/ProjectManager/Resources/DefaultTemplate.png create mode 100644 Code/Tools/ProjectManager/Source/TemplateButtonWidget.cpp create mode 100644 Code/Tools/ProjectManager/Source/TemplateButtonWidget.h diff --git a/Code/Tools/ProjectManager/Resources/ArrowBack_Hover.svg b/Code/Tools/ProjectManager/Resources/ArrowBack_Hover.svg new file mode 100644 index 0000000000..5b3b14f09a --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/ArrowBack_Hover.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/DefaultTemplate.png b/Code/Tools/ProjectManager/Resources/DefaultTemplate.png new file mode 100644 index 0000000000..2634c383fc --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/DefaultTemplate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8358f4dad9878c662b9819b2b346622af691eb45f8eddc28fff79a50650ae6cf +size 2503 diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index 62b7d23e9c..33acfa9e1b 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -7,6 +7,7 @@ AddOffset.svg AddOffset_Hover.svg ArrowBack.svg + ArrowBack_Hover.svg build.svg FolderOffset.svg FolderOffset_Hover.svg @@ -18,6 +19,7 @@ Linux.svg macOS.svg DefaultProjectImage.png + DefaultTemplate.png ArrowDownLine.svg ArrowUpLine.svg o3de.svg diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 6fd4086c58..8b7470051c 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -136,11 +136,9 @@ QTabBar::tab:pressed #header QPushButton:focus { border:none; } -#header QPushButton:hover { - background:#333333 url(:/ArrowBack.svg) no-repeat center; -} +#header QPushButton:hover, #header QPushButton:pressed { - background:#222222 url(:/ArrowBack.svg) no-repeat center; + background:transparent url(:/ArrowBack_Hover.svg) no-repeat center; } #headerTitle { @@ -210,9 +208,6 @@ QTabBar::tab:pressed #projectTemplate { margin: 55px 0 0 50px; - max-width: 780px; - min-height:200px; - max-height:200px; } #projectTemplateLabel { font-size:16px; @@ -227,11 +222,67 @@ QTabBar::tab:pressed #projectTemplateDetails { background-color:#444444; - max-width:240px; + max-width:20%; min-width:240px; margin-left:30px; } +#projectTemplateDetails #displayName, +#projectTemplateDetails #includedGemsTitle { + font-size:18px; +} + +#projectTemplateDetails #moreGems { + font-size:14px; + margin-top:20px; +} + +#projectTemplateDetails #includedGemsTitle { + margin-top:5px; + margin-bottom:5px; +} + +#projectTemplateDetails #summary { + padding-bottom:0px; + border-bottom:2px solid #555555; + min-height:80px; + qproperty-alignment: AlignTop; +} + +#projectTemplateDetails #browseCatalog { + margin:5px 0px 15px 0px; +} + +#projectTemplate QPushButton { + qproperty-flat: true; + min-width: 96px; + max-width: 96px; + min-height: 160px; + max-height: 160px; +} +#projectTemplate #templateLabel { + qproperty-alignment: AlignCenter; +} +#projectTemplate QPushButton #templateImage { + border:3px solid transparent; + border-radius: 4px; +} +#projectTemplate QPushButton[Checked="true"] #templateImage { + border:3px solid #1e70eb; +} +#projectTemplate QPushButton[Checked="true"] #templateLabel { + font-weight:bold; +} +#projectTemplate QPushButton:hover { + background-color: #444444; +} +#projectTemplate QPushButton:focus { + outline: none; + border:none; +} + + + #projectSettingsTab::tab-bar { left: 60px; } diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index 85e34aeced..4498a6bc82 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -41,24 +42,33 @@ namespace O3DE::ProjectManager m_stack = new QStackedWidget(this); m_stack->setObjectName("body"); - m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); - m_stack->addWidget(new NewProjectSettingsScreen()); - m_gemCatalog = new GemCatalogScreen(); - m_stack->addWidget(m_gemCatalog); + m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred,QSizePolicy::Expanding)); + + m_newProjectSettingsScreen = new NewProjectSettingsScreen(this); + m_stack->addWidget(m_newProjectSettingsScreen); + + m_gemCatalogScreen = new GemCatalogScreen(this); + m_stack->addWidget(m_gemCatalogScreen); vLayout->addWidget(m_stack); - QDialogButtonBox* backNextButtons = new QDialogButtonBox(); - backNextButtons->setObjectName("footer"); - vLayout->addWidget(backNextButtons); + QDialogButtonBox* buttons = new QDialogButtonBox(); + buttons->setObjectName("footer"); + vLayout->addWidget(buttons); - m_backButton = backNextButtons->addButton(tr("Back"), QDialogButtonBox::RejectRole); - m_backButton->setProperty("secondary", true); - m_nextButton = backNextButtons->addButton(tr("Next"), QDialogButtonBox::ApplyRole); +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + connect(m_newProjectSettingsScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest); - connect(m_backButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton); - connect(m_nextButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleNextButton); + m_secondaryButton = buttons->addButton(tr("Back"), QDialogButtonBox::RejectRole); + m_secondaryButton->setProperty("secondary", true); + m_secondaryButton->setVisible(false); + connect(m_secondaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleSecondaryButton); Update(); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + + m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole); + connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton); + setLayout(vLayout); } @@ -80,8 +90,10 @@ namespace O3DE::ProjectManager { if (m_stack->currentIndex() > 0) { - m_stack->setCurrentIndex(m_stack->currentIndex() - 1); - Update(); +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + PreviousScreen(); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + } else { @@ -89,70 +101,121 @@ namespace O3DE::ProjectManager } } - void CreateProjectCtrl::HandleNextButton() +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + void CreateProjectCtrl::HandleSecondaryButton() { - ScreenWidget* currentScreen = reinterpret_cast(m_stack->currentWidget()); - ProjectManagerScreen screenEnum = currentScreen->GetScreenEnum(); + if (m_stack->currentIndex() > 0) + { + // return to Project Settings page + PreviousScreen(); + } + else + { + // Configure Gems + NextScreen(); + } + } + + void CreateProjectCtrl::Update() + { + if (m_stack->currentWidget() == m_gemCatalogScreen) + { + m_header->setSubTitle(tr("Configure project with Gems")); + m_secondaryButton->setVisible(false); + } + else + { + m_header->setSubTitle(tr("Enter Project Details")); + m_secondaryButton->setVisible(true); + m_secondaryButton->setText(tr("Configure Gems")); + } + } - if (screenEnum == ProjectManagerScreen::NewProjectSettings) + void CreateProjectCtrl::OnChangeScreenRequest(ProjectManagerScreen screen) + { + if (screen == ProjectManagerScreen::GemCatalog) + { + HandleSecondaryButton(); + } + else { - auto newProjectScreen = reinterpret_cast(currentScreen); - if (newProjectScreen) + emit ChangeScreenRequest(screen); + } + } + + void CreateProjectCtrl::NextScreen() + { + if (m_stack->currentIndex() < m_stack->count()) + { + if(CurrentScreenIsValid()) { - if (!newProjectScreen->Validate()) - { - QMessageBox::critical(this, tr("Invalid project settings"), tr("Invalid project settings")); - return; - } + m_stack->setCurrentIndex(m_stack->currentIndex() + 1); - m_projectInfo = newProjectScreen->GetProjectInfo(); - m_projectTemplatePath = newProjectScreen->GetProjectTemplatePath(); + QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath(); + m_gemCatalogScreen->ReinitForProject(projectTemplatePath + "/Template", /*isNewProject=*/true); - // The next page is the gem catalog. Gather the available gems that will be shown in the gem catalog. - m_gemCatalog->ReinitForProject(m_projectInfo.m_path, /*isNewProject=*/true); + Update(); + } + else + { + QMessageBox::warning(this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again.")); } } + } - if (m_stack->currentIndex() != m_stack->count() - 1) + void CreateProjectCtrl::PreviousScreen() + { + // we don't require the current screen to be valid when moving back + if (m_stack->currentIndex() > 0) { - m_stack->setCurrentIndex(m_stack->currentIndex() + 1); + m_stack->setCurrentIndex(m_stack->currentIndex() - 1); Update(); } - else + } +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + + void CreateProjectCtrl::HandlePrimaryButton() + { + CreateProject(); + } + + bool CreateProjectCtrl::CurrentScreenIsValid() + { + if (m_stack->currentWidget() == m_newProjectSettingsScreen) { - auto result = PythonBindingsInterface::Get()->CreateProject(m_projectTemplatePath, m_projectInfo); + return m_newProjectSettingsScreen->Validate(); + } + + return true; + } + + void CreateProjectCtrl::CreateProject() + { + if (m_newProjectSettingsScreen->Validate()) + { + ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo(); + QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath(); + + auto result = PythonBindingsInterface::Get()->CreateProject(projectTemplatePath, projectInfo); if (result.IsSuccess()) { // automatically register the project - PythonBindingsInterface::Get()->AddProject(m_projectInfo.m_path); + PythonBindingsInterface::Get()->AddProject(projectInfo.m_path); + +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + m_gemCatalogScreen->EnableDisableGemsForProject(projectInfo.m_path); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED - // adding gems is not implemented yet because we don't know what targets to add or how to add them emit ChangeScreenRequest(ProjectManagerScreen::Projects); } else { QMessageBox::critical(this, tr("Project creation failed"), tr("Failed to create project.")); } - - // Enable/disable gems for the newly created project. - m_gemCatalog->EnableDisableGemsForProject(m_projectInfo.m_path); - } - } - - void CreateProjectCtrl::Update() - { - ScreenWidget* currentScreen = reinterpret_cast(m_stack->currentWidget()); - if (currentScreen && currentScreen->GetScreenEnum() == ProjectManagerScreen::GemCatalog) - { - m_header->setTitle(tr("Create Project")); - m_header->setSubTitle(tr("Configure project with Gems")); - m_nextButton->setText(tr("Create Project")); } else { - m_header->setTitle(tr("Create Project")); - m_header->setSubTitle(tr("Enter Project Details")); - m_nextButton->setText(tr("Next")); + QMessageBox::warning(this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again.")); } } diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h index 89d18a9ebc..e802b6845a 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h @@ -14,9 +14,11 @@ #if !defined(Q_MOC_RUN) #include #include -#include #endif +// due to current limitations, customizing template Gems is disabled +#define TEMPLATE_GEM_CONFIGURATION_ENABLED + QT_FORWARD_DECLARE_CLASS(QStackedWidget) QT_FORWARD_DECLARE_CLASS(QPushButton) QT_FORWARD_DECLARE_CLASS(QLabel) @@ -24,6 +26,8 @@ QT_FORWARD_DECLARE_CLASS(QLabel) namespace O3DE::ProjectManager { QT_FORWARD_DECLARE_CLASS(ScreenHeader) + QT_FORWARD_DECLARE_CLASS(NewProjectSettingsScreen) + QT_FORWARD_DECLARE_CLASS(GemCatalogScreen) class CreateProjectCtrl : public ScreenWidget @@ -36,21 +40,37 @@ namespace O3DE::ProjectManager protected slots: void HandleBackButton(); - void HandleNextButton(); + void HandlePrimaryButton(); + +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + void OnChangeScreenRequest(ProjectManagerScreen screen); + void HandleSecondaryButton(); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED private: +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED void Update(); + void NextScreen(); + void PreviousScreen(); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + + bool CurrentScreenIsValid(); + void CreateProject(); - QStackedWidget* m_stack; - ScreenHeader* m_header; + QStackedWidget* m_stack = nullptr; + ScreenHeader* m_header = nullptr; - QPushButton* m_backButton; - QPushButton* m_nextButton; + QPushButton* m_primaryButton = nullptr; + +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + QPushButton* m_secondaryButton = nullptr; +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED QString m_projectTemplatePath; ProjectInfo m_projectInfo; - - GemCatalogScreen* m_gemCatalog = nullptr; + + NewProjectSettingsScreen* m_newProjectSettingsScreen = nullptr; + GemCatalogScreen* m_gemCatalogScreen = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp index c8dc8451ae..5faa6cb8bd 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp @@ -14,8 +14,12 @@ #include #include #include +#include #include #include +#include +#include +#include #include #include @@ -28,10 +32,12 @@ #include #include #include +#include +#include namespace O3DE::ProjectManager { - constexpr const char* k_pathProperty = "Path"; + constexpr const char* k_templateIndexProperty = "TemplateIndex"; NewProjectSettingsScreen::NewProjectSettingsScreen(QWidget* parent) : ProjectSettingsScreen(parent) @@ -59,30 +65,69 @@ namespace O3DE::ProjectManager projectTemplateDetailsLabel->setObjectName("projectTemplateDetailsLabel"); containerLayout->addWidget(projectTemplateDetailsLabel); - QHBoxLayout* templateLayout = new QHBoxLayout(this); - containerLayout->addItem(templateLayout); + + // we might have enough templates that we need to scroll + QScrollArea* templatesScrollArea = new QScrollArea(this); + QWidget* scrollWidget = new QWidget(); + + FlowLayout* flowLayout = new FlowLayout(0, s_spacerSize, s_spacerSize); + scrollWidget->setLayout(flowLayout); + + templatesScrollArea->setWidget(scrollWidget); + templatesScrollArea->setWidgetResizable(true); m_projectTemplateButtonGroup = new QButtonGroup(this); m_projectTemplateButtonGroup->setObjectName("templateButtonGroup"); + + // QButtonGroup has overloaded buttonClicked methods so we need the QOverload + connect( + m_projectTemplateButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), this, + [=](QAbstractButton* button) + { + if (button && button->property(k_templateIndexProperty).isValid()) + { + int projectIndex = button->property(k_templateIndexProperty).toInt(); + UpdateTemplateDetails(m_templates.at(projectIndex)); + } + }); + auto templatesResult = PythonBindingsInterface::Get()->GetProjectTemplates(); if (templatesResult.IsSuccess() && !templatesResult.GetValue().isEmpty()) { - for (const ProjectTemplateInfo& projectTemplate : templatesResult.GetValue()) + m_templates = templatesResult.GetValue(); + + // sort alphabetically by display name because they could be in any order + std::sort(m_templates.begin(), m_templates.end(), [](const ProjectTemplateInfo& arg1, const ProjectTemplateInfo& arg2) { - QRadioButton* radioButton = new QRadioButton(projectTemplate.m_name, this); - radioButton->setProperty(k_pathProperty, projectTemplate.m_path); - m_projectTemplateButtonGroup->addButton(radioButton); + return arg1.m_displayName.toLower() < arg2.m_displayName.toLower(); + }); - containerLayout->addWidget(radioButton); + for (int index = 0; index < m_templates.size(); ++index) + { + ProjectTemplateInfo projectTemplate = m_templates.at(index); + QString projectPreviewPath = projectTemplate.m_path + "/Template/preview.png"; + QFileInfo doesPreviewExist(projectPreviewPath); + if (!doesPreviewExist.exists() || !doesPreviewExist.isFile()) + { + projectPreviewPath = ":/DefaultTemplate.png"; + } + TemplateButton* templateButton = new TemplateButton(projectPreviewPath, projectTemplate.m_displayName, this); + templateButton->setCheckable(true); + templateButton->setProperty(k_templateIndexProperty, index); + + m_projectTemplateButtonGroup->addButton(templateButton); + + flowLayout->addWidget(templateButton); } m_projectTemplateButtonGroup->buttons().first()->setChecked(true); } + containerLayout->addWidget(templatesScrollArea); } projectTemplateWidget->setLayout(containerLayout); m_verticalLayout->addWidget(projectTemplateWidget); - QWidget* projectTemplateDetails = new QWidget(this); + QFrame* projectTemplateDetails = CreateTemplateDetails(s_templateDetailsContentMargin); projectTemplateDetails->setObjectName("projectTemplateDetails"); m_horizontalLayout->addWidget(projectTemplateDetails); } @@ -109,11 +154,71 @@ namespace O3DE::ProjectManager void NewProjectSettingsScreen::NotifyCurrentScreen() { + if (!m_templates.isEmpty()) + { + UpdateTemplateDetails(m_templates.first()); + } + Validate(); } QString NewProjectSettingsScreen::GetProjectTemplatePath() { - return m_projectTemplateButtonGroup->checkedButton()->property(k_pathProperty).toString(); + const int templateIndex = m_projectTemplateButtonGroup->checkedButton()->property(k_templateIndexProperty).toInt(); + return m_templates.at(templateIndex).m_path; + } + + QFrame* NewProjectSettingsScreen::CreateTemplateDetails(int margin) + { + QFrame* projectTemplateDetails = new QFrame(this); + projectTemplateDetails->setObjectName("projectTemplateDetails"); + QVBoxLayout* templateDetailsLayout = new QVBoxLayout(); + templateDetailsLayout->setContentsMargins(margin, margin, margin, margin); + templateDetailsLayout->setAlignment(Qt::AlignTop); + { + m_templateDisplayName = new QLabel(this); + m_templateDisplayName->setObjectName("displayName"); + templateDetailsLayout->addWidget(m_templateDisplayName); + + m_templateSummary = new QLabel(this); + m_templateSummary->setObjectName("summary"); + m_templateSummary->setWordWrap(true); + templateDetailsLayout->addWidget(m_templateSummary); + + QLabel* includedGemsTitle = new QLabel(tr("Included Gems"), this); + includedGemsTitle->setObjectName("includedGemsTitle"); + templateDetailsLayout->addWidget(includedGemsTitle); + + m_templateIncludedGems = new TagContainerWidget(this); + m_templateIncludedGems->setObjectName("includedGems"); + templateDetailsLayout->addWidget(m_templateIncludedGems); + +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + QLabel* moreGemsLabel = new QLabel(tr("Looking for more Gems?"), this); + moreGemsLabel->setObjectName("moreGems"); + templateDetailsLayout->addWidget(moreGemsLabel); + + QLabel* browseCatalogLabel = new QLabel(tr("Browse the Gems Catalog to further customize your project."), this); + browseCatalogLabel->setObjectName("browseCatalog"); + browseCatalogLabel->setWordWrap(true); + templateDetailsLayout->addWidget(browseCatalogLabel); + + QPushButton* configureGemsButton = new QPushButton(tr("Configure with more Gems"), this); + connect(configureGemsButton, &QPushButton::clicked, this, [=]() + { + emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog); + }); + templateDetailsLayout->addWidget(configureGemsButton); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + } + projectTemplateDetails->setLayout(templateDetailsLayout); + return projectTemplateDetails; + } + + void NewProjectSettingsScreen::UpdateTemplateDetails(const ProjectTemplateInfo& templateInfo) + { + m_templateDisplayName->setText(templateInfo.m_displayName); + m_templateSummary->setText(templateInfo.m_summary); + m_templateIncludedGems->Update(templateInfo.m_includedGems); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h index 6a4b6ec57d..ce77915404 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h @@ -13,12 +13,17 @@ #if !defined(Q_MOC_RUN) #include +#include +#include #endif QT_FORWARD_DECLARE_CLASS(QButtonGroup) +QT_FORWARD_DECLARE_CLASS(QLabel) +QT_FORWARD_DECLARE_CLASS(QFrame) namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(TagContainerWidget) class NewProjectSettingsScreen : public ProjectSettingsScreen { @@ -33,8 +38,17 @@ namespace O3DE::ProjectManager private: QString GetDefaultProjectPath(); + QFrame* CreateTemplateDetails(int margin); + void UpdateTemplateDetails(const ProjectTemplateInfo& templateInfo); QButtonGroup* m_projectTemplateButtonGroup; + QLabel* m_templateDisplayName; + QLabel* m_templateSummary; + TagContainerWidget* m_templateIncludedGems; + QVector m_templates; + + inline constexpr static int s_spacerSize = 20; + inline constexpr static int s_templateDetailsContentMargin = 20; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectTemplateInfo.h b/Code/Tools/ProjectManager/Source/ProjectTemplateInfo.h index 0477968050..e75c64ec90 100644 --- a/Code/Tools/ProjectManager/Source/ProjectTemplateInfo.h +++ b/Code/Tools/ProjectManager/Source/ProjectTemplateInfo.h @@ -31,6 +31,7 @@ namespace O3DE::ProjectManager QString m_name; QString m_path; QString m_summary; + QStringList m_includedGems; QStringList m_canonicalTags; QStringList m_userTags; }; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 37e636caef..3263505f9e 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -754,7 +754,7 @@ namespace O3DE::ProjectManager ProjectTemplateInfo PythonBindings::ProjectTemplateInfoFromPath(pybind11::handle path) { ProjectTemplateInfo templateInfo; - templateInfo.m_path = Py_To_String(path); + templateInfo.m_path = Py_To_String(pybind11::str(path)); auto data = m_manifest.attr("get_template_json_data")(pybind11::none(), path); if (pybind11::isinstance(data)) @@ -781,6 +781,13 @@ namespace O3DE::ProjectManager templateInfo.m_canonicalTags.push_back(Py_To_String(tag)); } } + if (data.contains("included_gems")) + { + for (auto gem : data["included_gems"]) + { + templateInfo.m_includedGems.push_back(Py_To_String(gem)); + } + } } catch ([[maybe_unused]] const std::exception& e) { diff --git a/Code/Tools/ProjectManager/Source/TemplateButtonWidget.cpp b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.cpp new file mode 100644 index 0000000000..41b5e51c99 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.cpp @@ -0,0 +1,65 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + + TemplateButton::TemplateButton(const QString& imagePath, const QString& labelText, QWidget* parent) + : QPushButton(parent) + { + setAutoExclusive(true); + + setObjectName("templateButton"); + + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setSpacing(0); + vLayout->setContentsMargins(0, 0, 0, 0); + setLayout(vLayout); + + QLabel* image = new QLabel(this); + image->setObjectName("templateImage"); + image->setPixmap( + QPixmap(imagePath).scaled(QSize(s_templateImageWidth,s_templateImageHeight) , Qt::KeepAspectRatio, Qt::SmoothTransformation)); + vLayout->addWidget(image); + + QLabel* label = new QLabel(labelText, this); + label->setObjectName("templateLabel"); + vLayout->addWidget(label); + + connect(this, &QAbstractButton::toggled, this, &TemplateButton::onToggled); + } + + void TemplateButton::onToggled() + { + setProperty("Checked", isChecked()); + + // we must unpolish/polish every child after changing a property + // or else they won't use the correct stylesheet selector + for (auto child : findChildren()) + { + child->style()->unpolish(child); + child->style()->polish(child); + } + + style()->unpolish(this); + style()->polish(this); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/TemplateButtonWidget.h b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.h new file mode 100644 index 0000000000..db0f5f39c8 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.h @@ -0,0 +1,37 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#endif + +namespace O3DE::ProjectManager +{ + class TemplateButton + : public QPushButton + { + Q_OBJECT // AUTOMOC + + public: + explicit TemplateButton(const QString& imagePath, const QString& labelText, QWidget* parent = nullptr); + ~TemplateButton() = default; + + protected slots: + void onToggled(); + + private: + inline constexpr static int s_templateImageWidth = 92; + inline constexpr static int s_templateImageHeight = 122; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index a7a36f26ab..40f450ab6f 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -60,6 +60,8 @@ set(FILES Source/LinkWidget.cpp Source/TagWidget.h Source/TagWidget.cpp + Source/TemplateButtonWidget.h + Source/TemplateButtonWidget.cpp Source/GemCatalog/GemCatalogHeaderWidget.h Source/GemCatalog/GemCatalogHeaderWidget.cpp Source/GemCatalog/GemCatalogScreen.h diff --git a/Templates/DefaultProject/template.json b/Templates/DefaultProject/template.json index 26b868d315..6f74fb6b26 100644 --- a/Templates/DefaultProject/template.json +++ b/Templates/DefaultProject/template.json @@ -4,8 +4,9 @@ "restricted_platform_relative_path": "Templates", "origin": "The primary repo for DefaultProject goes here: i.e. http://www.mydomain.com", "license": "What license DefaultProject uses goes here: i.e. https://opensource.org/licenses/MIT", - "display_name": "DefaultProject", + "display_name": "Default", "summary": "A short description of DefaultProject.", + "included_gems": ["Atom","Camera","EMotionFX","UI","Maestro","Input","ImGui"], "canonical_tags": [], "user_tags": [ "DefaultProject" @@ -651,4 +652,4 @@ "origin": "Shaders" } ] -} \ No newline at end of file +} From 08db0584762545b87d09a48c897a8787852bcdbd Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 16:41:24 -0700 Subject: [PATCH 66/71] SPEC-2513 Fixes to enable w4436 and w4366 (#1157) * Fix for w4457 * Nothing to fix, seems we deleted all the code that was causing this offense * removing warning * another warning that doesnt trigger --- cmake/Platform/Common/MSVC/Configurations_msvc.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake index 8c22677f91..bcff2adeb7 100644 --- a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake +++ b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake @@ -69,9 +69,7 @@ ly_append_configurations_options( /wd4267 # conversion, possible loss of data /wd4310 # cast truncates constant value /wd4324 # structure was padded due to alignment specifier - /wd4366 # the result of unary operator may be unaligned /wd4389 # comparison, signed/unsigned mismatch - /wd4436 # the result of unary operator may be unaligned # Enabling warnings that are disabled by default from /W4 # https://docs.microsoft.com/en-us/cpp/preprocessor/compiler-warnings-that-are-off-by-default?view=vs-2019 From d9b57bce678d11ef9a2f73cf45c3bd37422c8f0b Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Fri, 4 Jun 2021 19:34:22 -0500 Subject: [PATCH 67/71] Fixed configuring of cmake when a project resides on a different drive than the engine (#1153) --- cmake/Platform/Common/Install_common.cmake | 38 ++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 016c0d623d..4aeaf21e95 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -182,8 +182,20 @@ set_property(TARGET ${TARGET_NAME} endif() endif() - file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" CONTENT "${target_file_contents}") - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" + if(IS_ABSOLUTE ${target_source_dir}) + # This normally applies the target_source_dir is outside of the engine root + # such as when invoking ly_setup_subdirectory from the project + # Therefore the final directory component of the target source directory is used first 8 characters + # of a SHA256 hash + string(SHA256 target_source_hash ${target_source_dir}) + string(SUBSTRING ${target_source_hash} 0 8 target_source_hash) + get_filename_component(target_source_folder_name ${target_source_dir} NAME) + set(target_source_dir "${target_source_folder_name}-${target_source_hash}") + endif() + + set(target_install_source_dir ${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}) + file(GENERATE OUTPUT "${target_install_source_dir}/${NAME_PLACEHOLDER}_$.cmake" CONTENT "${target_file_contents}") + install(FILES "${target_install_source_dir}/${NAME_PLACEHOLDER}_$.cmake" DESTINATION ${target_source_dir} COMPONENT ${install_component} ) @@ -235,18 +247,32 @@ function(ly_setup_subdirectory absolute_target_source_dir) endforeach() file(READ ${LY_ROOT_FOLDER}/cmake/install/Copyright.in cmake_copyright_comment) - # Write out all the agreegated ly_add_target function calls and the final ly_create_alias() calls to the target CMakeList.txt - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/CMakeLists.txt + + if(IS_ABSOLUTE ${target_source_dir}) + # This normally applies the target_source_dir is outside of the engine root + # such as when invoking ly_setup_subdirectory from the project + # Therefore the final directory component of the target source directory is used first 8 characters + # of a SHA256 hash + string(SHA256 target_source_hash ${target_source_dir}) + string(SUBSTRING ${target_source_hash} 0 8 target_source_hash) + get_filename_component(target_source_folder_name ${target_source_dir} NAME) + set(target_source_dir "${target_source_folder_name}-${target_source_hash}") + endif() + + # Initialize the target install source directory to path underneath the current binary directory + set(target_install_source_dir ${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}) + # Write out all the aggregated ly_add_target function calls and the final ly_create_alias() calls to the target CMakeLists.txt + file(WRITE ${target_install_source_dir}/CMakeLists.txt "${cmake_copyright_comment}" "${all_configured_targets}" "\n" "${CREATE_ALIASES_PLACEHOLDER}" ) - # get the component ID. if the property isn't set for the directory, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME + # get the component ID. if the property isn't set for the directory, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME get_property(install_component DIRECTORY ${absolute_target_source_dir} PROPERTY INSTALL_COMPONENT) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/CMakeLists.txt" + install(FILES "${target_install_source_dir}/CMakeLists.txt" DESTINATION ${target_source_dir} COMPONENT ${install_component} ) From 77f0d983c8475f24c874ccdd634a51c1ae8942ef Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Fri, 4 Jun 2021 19:34:28 -0500 Subject: [PATCH 68/71] Mac SystemFile_Apple.h build fix (#1159) --- .../AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h b/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h index 3967cafc90..2ebb79634a 100644 --- a/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h +++ b/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h @@ -14,6 +14,9 @@ #include #include #include +#include + +#include namespace AZ { From 74e5090f26f957adf75f141d70cc7d3da9c0bfdd Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 18:08:52 -0700 Subject: [PATCH 69/71] Adding ExternalWarningLevel to the Directory.Build.props to get the default warning level for external headers to match the one we define through compile options (#1160) --- cmake/Platform/Common/Directory.Build.props | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/Platform/Common/Directory.Build.props b/cmake/Platform/Common/Directory.Build.props index b74fa48471..951d3c6605 100644 --- a/cmake/Platform/Common/Directory.Build.props +++ b/cmake/Platform/Common/Directory.Build.props @@ -15,4 +15,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. true true + + + TurnOffAllWarnings + + \ No newline at end of file From 3b60bcc0f1ab6707017d5698f55aa471e9ff598b Mon Sep 17 00:00:00 2001 From: AMZN-nggieber <52797929+AMZN-nggieber@users.noreply.github.com> Date: Fri, 4 Jun 2021 18:41:30 -0700 Subject: [PATCH 70/71] Project Manager Build Project from Projects Page (#1142) * Added loading bar mode to project button * Added ProjectBuilder files * commmit current progress for project building * Push current project building work * Full build commands built out and message boxes for lots of situation * Replaced defaultProjectImage placeholder * Added installed cmake path to builder process env PATH --- .../Resources/DefaultProjectImage.png | 4 +- .../Resources/ProjectManager.qss | 13 + .../Source/CreateProjectCtrl.cpp | 2 + .../ProjectManager/Source/ProjectBuilder.cpp | 250 ++++++++++++ .../ProjectManager/Source/ProjectBuilder.h | 73 ++++ .../Source/ProjectButtonWidget.cpp | 109 +++++- .../Source/ProjectButtonWidget.h | 19 +- .../ProjectManager/Source/ProjectInfo.cpp | 4 +- .../Tools/ProjectManager/Source/ProjectInfo.h | 4 +- .../ProjectManager/Source/ProjectUtils.cpp | 48 ++- .../ProjectManager/Source/ProjectUtils.h | 3 + .../ProjectManager/Source/ProjectsScreen.cpp | 361 ++++++++++++++---- .../ProjectManager/Source/ProjectsScreen.h | 35 +- .../ProjectManager/Source/PythonBindings.cpp | 2 +- .../ProjectManager/Source/ScreenWidget.h | 2 + .../ProjectManager/Source/ScreensCtrl.cpp | 1 + .../Tools/ProjectManager/Source/ScreensCtrl.h | 2 + .../Source/UpdateProjectCtrl.cpp | 14 +- .../project_manager_files.cmake | 2 + 19 files changed, 834 insertions(+), 114 deletions(-) create mode 100644 Code/Tools/ProjectManager/Source/ProjectBuilder.cpp create mode 100644 Code/Tools/ProjectManager/Source/ProjectBuilder.h diff --git a/Code/Tools/ProjectManager/Resources/DefaultProjectImage.png b/Code/Tools/ProjectManager/Resources/DefaultProjectImage.png index cc1eda5bb8..a3e13481c9 100644 --- a/Code/Tools/ProjectManager/Resources/DefaultProjectImage.png +++ b/Code/Tools/ProjectManager/Resources/DefaultProjectImage.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f82f22df64b93d4bec91e56b60efa3d5ce2915ce388a2dc627f1ab720678e3d5 -size 334987 +oid sha256:4a5881b8d6cfbc4ceefb14ab96844484fe19407ee030824768f9fcce2f729d35 +size 2949 diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 8b7470051c..c18d61fc24 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -362,6 +362,7 @@ QTabBar::tab:pressed #projectButton > #labelButton { border:1px solid white; } + #projectButton > #labelButton:hover, #projectButton > #labelButton:pressed { border:1px solid #1e70eb; @@ -401,6 +402,18 @@ QTabBar::tab:pressed max-height:278px; } +QProgressBar { + border: none; + background-color: transparent; + padding: 0px; + min-height: 14px; + font-size: 2px; +} + +QProgressBar::chunk { + background-color: #1E70EB; +} + /************** Gem Catalog **************/ #GemCatalogTitle { diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index 4498a6bc82..c8ed3954ac 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -206,6 +206,8 @@ namespace O3DE::ProjectManager m_gemCatalogScreen->EnableDisableGemsForProject(projectInfo.m_path); #endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + projectInfo.m_needsBuild = true; + emit NotifyBuildProject(projectInfo); emit ChangeScreenRequest(ProjectManagerScreen::Projects); } else diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilder.cpp b/Code/Tools/ProjectManager/Source/ProjectBuilder.cpp new file mode 100644 index 0000000000..8cdab93c6a --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ProjectBuilder.cpp @@ -0,0 +1,250 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +//#define MOCK_BUILD_PROJECT true + +namespace O3DE::ProjectManager +{ + // 10 Minutes + constexpr int MaxBuildTimeMSecs = 600000; + static const QString BuildPathPostfix = "windows_vs2019"; + static const QString ErrorLogPathPostfix = "CMakeFiles/CMakeProjectBuildError.log"; + + ProjectBuilderWorker::ProjectBuilderWorker(const ProjectInfo& projectInfo) + : QObject() + , m_projectInfo(projectInfo) + { + } + + void ProjectBuilderWorker::BuildProject() + { +#ifdef MOCK_BUILD_PROJECT + for (int i = 0; i < 10; ++i) + { + QThread::sleep(1); + UpdateProgress(i * 10); + } + Done(m_projectPath); +#else + EngineInfo engineInfo; + + AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); + if (engineInfoResult.IsSuccess()) + { + engineInfo = engineInfoResult.GetValue(); + } + else + { + emit Done(tr("Failed to get engine info.")); + return; + } + + // Show some kind of progress with very approximate estimates + UpdateProgress(1); + + QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); + // Append cmake path to PATH incase it is missing + QDir cmakePath(engineInfo.m_path); + cmakePath.cd("cmake/runtime/bin"); + QString pathValue = currentEnvironment.value("PATH"); + pathValue += ";" + cmakePath.path(); + currentEnvironment.insert("PATH", pathValue); + + QProcess configProjectProcess; + configProjectProcess.setProcessChannelMode(QProcess::MergedChannels); + configProjectProcess.setWorkingDirectory(m_projectInfo.m_path); + configProjectProcess.setProcessEnvironment(currentEnvironment); + + configProjectProcess.start( + "cmake", + QStringList + { + "-B", + QDir(m_projectInfo.m_path).filePath(BuildPathPostfix), + "-S", + m_projectInfo.m_path, + "-G", + "Visual Studio 16", + "-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath + }); + + if (!configProjectProcess.waitForStarted()) + { + emit Done(tr("Configuring project failed to start.")); + return; + } + if (!configProjectProcess.waitForFinished(MaxBuildTimeMSecs)) + { + WriteErrorLog(configProjectProcess.readAllStandardOutput()); + emit Done(tr("Configuring project timed out. See log for details")); + return; + } + + QString configProjectOutput(configProjectProcess.readAllStandardOutput()); + if (configProjectProcess.exitCode() != 0 || !configProjectOutput.contains("Generating done")) + { + WriteErrorLog(configProjectOutput); + emit Done(tr("Configuring project failed. See log for details.")); + return; + } + + UpdateProgress(20); + + QProcess buildProjectProcess; + buildProjectProcess.setProcessChannelMode(QProcess::MergedChannels); + buildProjectProcess.setWorkingDirectory(m_projectInfo.m_path); + buildProjectProcess.setProcessEnvironment(currentEnvironment); + + buildProjectProcess.start( + "cmake", + QStringList + { + "--build", + QDir(m_projectInfo.m_path).filePath(BuildPathPostfix), + "--target", + m_projectInfo.m_projectName + ".GameLauncher", + "Editor", + "--config", + "profile" + }); + + if (!buildProjectProcess.waitForStarted()) + { + emit Done(tr("Building project failed to start.")); + return; + } + if (!buildProjectProcess.waitForFinished(MaxBuildTimeMSecs)) + { + WriteErrorLog(configProjectProcess.readAllStandardOutput()); + emit Done(tr("Building project timed out. See log for details")); + return; + } + + QString buildProjectOutput(buildProjectProcess.readAllStandardOutput()); + if (configProjectProcess.exitCode() != 0) + { + WriteErrorLog(buildProjectOutput); + emit Done(tr("Building project failed. See log for details.")); + } + else + { + emit Done(""); + } +#endif + } + + QString ProjectBuilderWorker::LogFilePath() const + { + QDir logFilePath(m_projectInfo.m_path); + logFilePath.cd(BuildPathPostfix); + return logFilePath.filePath(ErrorLogPathPostfix); + } + + void ProjectBuilderWorker::WriteErrorLog(const QString& log) + { + QFile logFile(LogFilePath()); + // Overwrite file with truncate + if (logFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + { + QTextStream output(&logFile); + output << log; + logFile.close(); + } + } + + ProjectBuilderController::ProjectBuilderController(const ProjectInfo& projectInfo, ProjectButton* projectButton, QWidget* parent) + : QObject() + , m_projectInfo(projectInfo) + , m_projectButton(projectButton) + , m_parent(parent) + { + m_worker = new ProjectBuilderWorker(m_projectInfo); + m_worker->moveToThread(&m_workerThread); + + connect(&m_workerThread, &QThread::finished, m_worker, &ProjectBuilderWorker::deleteLater); + connect(&m_workerThread, &QThread::started, m_worker, &ProjectBuilderWorker::BuildProject); + connect(m_worker, &ProjectBuilderWorker::Done, this, &ProjectBuilderController::HandleResults); + connect(m_worker, &ProjectBuilderWorker::UpdateProgress, this, &ProjectBuilderController::UpdateUIProgress); + } + + ProjectBuilderController::~ProjectBuilderController() + { + m_workerThread.quit(); + m_workerThread.wait(); + } + + void ProjectBuilderController::Start() + { + m_workerThread.start(); + UpdateUIProgress(0); + } + + void ProjectBuilderController::SetProjectButton(ProjectButton* projectButton) + { + m_projectButton = projectButton; + } + + QString ProjectBuilderController::GetProjectPath() const + { + return m_projectInfo.m_path; + } + + void ProjectBuilderController::UpdateUIProgress(int progress) + { + if (m_projectButton) + { + m_projectButton->SetButtonOverlayText(QString("%1 (%2%)\n\n").arg(tr("Building Project..."), QString::number(progress))); + m_projectButton->SetProgressBarValue(progress); + } + } + + void ProjectBuilderController::HandleResults(const QString& result) + { + if (!result.isEmpty()) + { + if (result.contains(tr("log"))) + { + QMessageBox::StandardButton openLog = QMessageBox::critical( + m_parent, + tr("Project Failed to Build!"), + result + tr("\n\nWould you like to view log?"), + QMessageBox::No | QMessageBox::Yes); + + if (openLog == QMessageBox::Yes) + { + // Open application assigned to this file type + QDesktopServices::openUrl(QUrl("file:///" + m_worker->LogFilePath())); + } + } + else + { + QMessageBox::critical(m_parent, tr("Project Failed to Build!"), result); + } + } + + emit Done(); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilder.h b/Code/Tools/ProjectManager/Source/ProjectBuilder.h new file mode 100644 index 0000000000..de84a351ee --- /dev/null +++ b/Code/Tools/ProjectManager/Source/ProjectBuilder.h @@ -0,0 +1,73 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#pragma once + +#if !defined(Q_MOC_RUN) +#include + +#include +#endif + +namespace O3DE::ProjectManager +{ + QT_FORWARD_DECLARE_CLASS(ProjectButton) + + class ProjectBuilderWorker : public QObject + { + Q_OBJECT + + public: + explicit ProjectBuilderWorker(const ProjectInfo& projectInfo); + ~ProjectBuilderWorker() = default; + + QString LogFilePath() const; + + public slots: + void BuildProject(); + + signals: + void UpdateProgress(int progress); + void Done(QString result); + + private: + void WriteErrorLog(const QString& log); + + ProjectInfo m_projectInfo; + }; + + class ProjectBuilderController : public QObject + { + Q_OBJECT + + public: + explicit ProjectBuilderController(const ProjectInfo& projectInfo, ProjectButton* projectButton, QWidget* parent = nullptr); + ~ProjectBuilderController(); + + void SetProjectButton(ProjectButton* projectButton); + QString GetProjectPath() const; + + public slots: + void Start(); + void UpdateUIProgress(int progress); + void HandleResults(const QString& result); + + signals: + void Done(); + + private: + ProjectInfo m_projectInfo; + ProjectBuilderWorker* m_worker; + QThread m_workerThread; + ProjectButton* m_projectButton; + QWidget* m_parent; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp index ee4d48fe7f..db1b1a4850 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp @@ -21,6 +21,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -31,11 +32,26 @@ namespace O3DE::ProjectManager : QLabel(parent) { setObjectName("labelButton"); + + QVBoxLayout* vLayout = new QVBoxLayout(this); + vLayout->setContentsMargins(0, 0, 0, 0); + vLayout->setSpacing(5); + + setLayout(vLayout); m_overlayLabel = new QLabel("", this); m_overlayLabel->setObjectName("labelButtonOverlay"); m_overlayLabel->setWordWrap(true); m_overlayLabel->setAlignment(Qt::AlignCenter); m_overlayLabel->setVisible(false); + vLayout->addWidget(m_overlayLabel); + + m_buildButton = new QPushButton(tr("Build Project"), this); + m_buildButton->setVisible(false); + + m_progressBar = new QProgressBar(this); + m_progressBar->setObjectName("labelButtonProgressBar"); + m_progressBar->setVisible(false); + vLayout->addWidget(m_progressBar); } void LabelButton::mousePressEvent([[maybe_unused]] QMouseEvent* event) @@ -57,7 +73,22 @@ namespace O3DE::ProjectManager m_overlayLabel->setText(text); } - ProjectButton::ProjectButton(const ProjectInfo& projectInfo, QWidget* parent) + QLabel* LabelButton::GetOverlayLabel() + { + return m_overlayLabel; + } + + QProgressBar* LabelButton::GetProgressBar() + { + return m_progressBar; + } + + QPushButton* LabelButton::GetBuildButton() + { + return m_buildButton; + } + + ProjectButton::ProjectButton(const ProjectInfo& projectInfo, QWidget* parent, bool processing) : QFrame(parent) , m_projectInfo(projectInfo) { @@ -66,10 +97,18 @@ namespace O3DE::ProjectManager m_projectInfo.m_imagePath = ":/DefaultProjectImage.png"; } - Setup(); + BaseSetup(); + if (processing) + { + ProcessingSetup(); + } + else + { + ReadySetup(); + } } - void ProjectButton::Setup() + void ProjectButton::BaseSetup() { setObjectName("projectButton"); @@ -87,8 +126,37 @@ namespace O3DE::ProjectManager m_projectImageLabel->setPixmap( QPixmap(m_projectInfo.m_imagePath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding)); + m_projectFooter = new QFrame(this); + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setContentsMargins(0, 0, 0, 0); + m_projectFooter->setLayout(hLayout); + { + QLabel* projectNameLabel = new QLabel(m_projectInfo.m_displayName, this); + hLayout->addWidget(projectNameLabel); + } + + vLayout->addWidget(m_projectFooter); + } + + void ProjectButton::ProcessingSetup() + { + m_projectImageLabel->GetOverlayLabel()->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + m_projectImageLabel->SetEnabled(false); + m_projectImageLabel->SetOverlayText(tr("Processing...\n\n")); + + QProgressBar* progressBar = m_projectImageLabel->GetProgressBar(); + progressBar->setVisible(true); + progressBar->setValue(0); + } + + void ProjectButton::ReadySetup() + { + connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectInfo.m_path); }); + connect(m_projectImageLabel->GetBuildButton(), &QPushButton::clicked, [this](){ emit BuildProject(m_projectInfo); }); + QMenu* menu = new QMenu(this); menu->addAction(tr("Edit Project Settings..."), this, [this]() { emit EditProject(m_projectInfo.m_path); }); + menu->addAction(tr("Build"), this, [this]() { emit BuildProject(m_projectInfo); }); menu->addSeparator(); menu->addAction(tr("Open Project folder..."), this, [this]() { @@ -100,30 +168,33 @@ namespace O3DE::ProjectManager menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); }); menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); }); - QFrame* footer = new QFrame(this); - QHBoxLayout* hLayout = new QHBoxLayout(); - hLayout->setContentsMargins(0, 0, 0, 0); - footer->setLayout(hLayout); - { - QLabel* projectNameLabel = new QLabel(m_projectInfo.m_displayName, this); - hLayout->addWidget(projectNameLabel); - - QPushButton* projectMenuButton = new QPushButton(this); - projectMenuButton->setObjectName("projectMenuButton"); - projectMenuButton->setMenu(menu); - hLayout->addWidget(projectMenuButton); - } - - vLayout->addWidget(footer); + QPushButton* projectMenuButton = new QPushButton(this); + projectMenuButton->setObjectName("projectMenuButton"); + projectMenuButton->setMenu(menu); + m_projectFooter->layout()->addWidget(projectMenuButton); } - void ProjectButton::SetButtonEnabled(bool enabled) + void ProjectButton::SetLaunchButtonEnabled(bool enabled) { m_projectImageLabel->SetEnabled(enabled); } + void ProjectButton::ShowBuildButton(bool show) + { + QSpacerItem* buttonSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding); + + m_projectImageLabel->layout()->addItem(buttonSpacer); + m_projectImageLabel->layout()->addWidget(m_projectImageLabel->GetBuildButton()); + m_projectImageLabel->GetBuildButton()->setVisible(show); + } + void ProjectButton::SetButtonOverlayText(const QString& text) { m_projectImageLabel->SetOverlayText(text); } + + void ProjectButton::SetProgressBarValue(int progress) + { + m_projectImageLabel->GetProgressBar()->setValue(progress); + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h index bb61f7354b..1178c8ea76 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h @@ -21,6 +21,7 @@ QT_FORWARD_DECLARE_CLASS(QPixmap) QT_FORWARD_DECLARE_CLASS(QPushButton) QT_FORWARD_DECLARE_CLASS(QAction) +QT_FORWARD_DECLARE_CLASS(QProgressBar) namespace O3DE::ProjectManager { @@ -36,6 +37,10 @@ namespace O3DE::ProjectManager void SetEnabled(bool enabled); void SetOverlayText(const QString& text); + QLabel* GetOverlayLabel(); + QProgressBar* GetProgressBar(); + QPushButton* GetBuildButton(); + signals: void triggered(); @@ -44,6 +49,8 @@ namespace O3DE::ProjectManager private: QLabel* m_overlayLabel; + QProgressBar* m_progressBar; + QPushButton* m_buildButton; bool m_enabled = true; }; @@ -53,11 +60,13 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - explicit ProjectButton(const ProjectInfo& m_projectInfo, QWidget* parent = nullptr); + explicit ProjectButton(const ProjectInfo& m_projectInfo, QWidget* parent = nullptr, bool processing = false); ~ProjectButton() = default; - void SetButtonEnabled(bool enabled); + void SetLaunchButtonEnabled(bool enabled); + void ShowBuildButton(bool show); void SetButtonOverlayText(const QString& text); + void SetProgressBarValue(int progress); signals: void OpenProject(const QString& projectName); @@ -65,11 +74,15 @@ namespace O3DE::ProjectManager void CopyProject(const QString& projectName); void RemoveProject(const QString& projectName); void DeleteProject(const QString& projectName); + void BuildProject(const ProjectInfo& projectInfo); private: - void Setup(); + void BaseSetup(); + void ProcessingSetup(); + void ReadySetup(); ProjectInfo m_projectInfo; LabelButton* m_projectImageLabel; + QFrame* m_projectFooter; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectInfo.cpp b/Code/Tools/ProjectManager/Source/ProjectInfo.cpp index f0dc05cc62..da0b4ebd61 100644 --- a/Code/Tools/ProjectManager/Source/ProjectInfo.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectInfo.cpp @@ -15,13 +15,13 @@ namespace O3DE::ProjectManager { ProjectInfo::ProjectInfo(const QString& path, const QString& projectName, const QString& displayName, - const QString& imagePath, const QString& backgroundImagePath, bool isNew) + const QString& imagePath, const QString& backgroundImagePath, bool needsBuild) : m_path(path) , m_projectName(projectName) , m_displayName(displayName) , m_imagePath(imagePath) , m_backgroundImagePath(backgroundImagePath) - , m_isNew(isNew) + , m_needsBuild(needsBuild) { } diff --git a/Code/Tools/ProjectManager/Source/ProjectInfo.h b/Code/Tools/ProjectManager/Source/ProjectInfo.h index 71fa12b344..857e6ea4d5 100644 --- a/Code/Tools/ProjectManager/Source/ProjectInfo.h +++ b/Code/Tools/ProjectManager/Source/ProjectInfo.h @@ -24,7 +24,7 @@ namespace O3DE::ProjectManager public: ProjectInfo() = default; ProjectInfo(const QString& path, const QString& projectName, const QString& displayName, - const QString& imagePath, const QString& backgroundImagePath, bool isNew); + const QString& imagePath, const QString& backgroundImagePath, bool needsBuild); bool operator==(const ProjectInfo& rhs); bool operator!=(const ProjectInfo& rhs); @@ -42,6 +42,6 @@ namespace O3DE::ProjectManager QString m_backgroundImagePath; // Used in project creation - bool m_isNew = false; //! Is this a new project or existing + bool m_needsBuild = false; //! Does this project need to be built }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp index 58e4c5c60f..3e2b3c13e1 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp @@ -16,7 +16,9 @@ #include #include #include -#include +#include +#include +#include namespace O3DE::ProjectManager { @@ -192,6 +194,49 @@ namespace O3DE::ProjectManager return true; } + static bool IsVS2019Installed_internal() + { + QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); + QString programFilesPath = environment.value("ProgramFiles(x86)"); + QString vsWherePath = programFilesPath + "\\Microsoft Visual Studio\\Installer\\vswhere.exe"; + + QFileInfo vsWhereFile(vsWherePath); + if (vsWhereFile.exists() && vsWhereFile.isFile()) + { + QProcess vsWhereProcess; + vsWhereProcess.setProcessChannelMode(QProcess::MergedChannels); + + vsWhereProcess.start( + vsWherePath, + QStringList{ "-version", "16.0", "-latest", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", "isComplete" }); + + if (!vsWhereProcess.waitForStarted()) + { + return false; + } + + while (vsWhereProcess.waitForReadyRead()) + { + } + + QString vsWhereOutput(vsWhereProcess.readAllStandardOutput()); + if (vsWhereOutput.startsWith("1")) + { + return true; + } + } + + return false; + } + + bool IsVS2019Installed() + { + static bool vs2019Installed = IsVS2019Installed_internal(); + + return vs2019Installed; + } + ProjectManagerScreen GetProjectManagerScreen(const QString& screen) { auto iter = s_ProjectManagerStringNames.find(screen); @@ -202,6 +247,5 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::Invalid; } - } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.h b/Code/Tools/ProjectManager/Source/ProjectUtils.h index d556d682f2..9c711ad187 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.h +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.h @@ -25,6 +25,9 @@ namespace O3DE::ProjectManager bool CopyProject(const QString& origPath, const QString& newPath); bool DeleteProjectFiles(const QString& path, bool force = false); bool MoveProject(const QString& origPath, const QString& newPath, QWidget* parent = nullptr); + + bool IsVS2019Installed(); + ProjectManagerScreen GetProjectManagerScreen(const QString& screen); } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp index 425aa8514d..8e41e52643 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include @@ -42,6 +44,8 @@ #include #include #include +#include +#include //#define DISPLAY_PROJECT_DEV_DATA true @@ -66,6 +70,14 @@ namespace O3DE::ProjectManager m_stack->addWidget(m_projectsContent); vLayout->addWidget(m_stack); + + connect(reinterpret_cast(parent), &ScreensCtrl::NotifyBuildProject, this, &ProjectsScreen::SuggestBuildProject); + } + + ProjectsScreen::~ProjectsScreen() + + { + delete m_currentBuilder; } QFrame* ProjectsScreen::CreateFirstTimeContent() @@ -110,7 +122,7 @@ namespace O3DE::ProjectManager return frame; } - QFrame* ProjectsScreen::CreateProjectsContent() + QFrame* ProjectsScreen::CreateProjectsContent(QString buildProjectPath, ProjectButton** projectButton) { QFrame* frame = new QFrame(this); frame->setObjectName("projectsContent"); @@ -158,30 +170,43 @@ namespace O3DE::ProjectManager projectsScrollArea->setWidgetResizable(true); #ifndef DISPLAY_PROJECT_DEV_DATA + // Iterate once to insert building project first + if (!buildProjectPath.isEmpty()) + { + buildProjectPath = QDir::fromNativeSeparators(buildProjectPath); + for (auto project : projectsResult.GetValue()) + { + if (QDir::fromNativeSeparators(project.m_path) == buildProjectPath) + { + ProjectButton* buildingProjectButton = CreateProjectButton(project, flowLayout, true); + + if (projectButton) + { + *projectButton = buildingProjectButton; + } + + break; + } + } + } + for (auto project : projectsResult.GetValue()) #else ProjectInfo project = projectsResult.GetValue().at(0); for (int i = 0; i < 15; i++) #endif { - ProjectButton* projectButton; - - QString projectPreviewPath = project.m_path + m_projectPreviewImagePath; - QFileInfo doesPreviewExist(projectPreviewPath); - if (doesPreviewExist.exists() && doesPreviewExist.isFile()) + // Add all other projects skipping building project + // Safe if no building project because it is just an empty string + if (project.m_path != buildProjectPath) { - project.m_imagePath = projectPreviewPath; - } - - projectButton = new ProjectButton(project, this); - - flowLayout->addWidget(projectButton); + ProjectButton* projectButtonWidget = CreateProjectButton(project, flowLayout); - connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject); - connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject); - connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject); - connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject); - connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject); + if (RequiresBuildProjectIterator(project.m_path) != m_requiresBuild.end()) + { + projectButtonWidget->ShowBuildButton(true); + } + } } layout->addWidget(projectsScrollArea); @@ -191,6 +216,60 @@ namespace O3DE::ProjectManager return frame; } + ProjectButton* ProjectsScreen::CreateProjectButton(ProjectInfo& project, QLayout* flowLayout, bool processing) + { + ProjectButton* projectButton; + + QString projectPreviewPath = project.m_path + m_projectPreviewImagePath; + QFileInfo doesPreviewExist(projectPreviewPath); + if (doesPreviewExist.exists() && doesPreviewExist.isFile()) + { + project.m_imagePath = projectPreviewPath; + } + + projectButton = new ProjectButton(project, this, processing); + + flowLayout->addWidget(projectButton); + + if (!processing) + { + connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject); + connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject); + connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject); + connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject); + connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject); + } + connect(projectButton, &ProjectButton::BuildProject, this, &ProjectsScreen::QueueBuildProject); + + return projectButton; + } + + void ProjectsScreen::ResetProjectsContent() + { + // refresh the projects content by re-creating it for now + if (m_projectsContent) + { + m_stack->removeWidget(m_projectsContent); + m_projectsContent->deleteLater(); + } + + // Make sure to update builder with latest Project Button + if (m_currentBuilder) + { + ProjectButton* projectButtonPtr; + + m_projectsContent = CreateProjectsContent(m_currentBuilder->GetProjectPath(), &projectButtonPtr); + m_currentBuilder->SetProjectButton(projectButtonPtr); + } + else + { + m_projectsContent = CreateProjectsContent(); + } + + m_stack->addWidget(m_projectsContent); + m_stack->setCurrentWidget(m_projectsContent); + } + ProjectManagerScreen ProjectsScreen::GetScreenEnum() { return ProjectManagerScreen::Projects; @@ -237,7 +316,7 @@ namespace O3DE::ProjectManager { if (ProjectUtils::AddProjectDialog(this)) { - emit ResetScreenRequest(ProjectManagerScreen::Projects); + ResetProjectsContent(); emit ChangeScreenRequest(ProjectManagerScreen::Projects); } } @@ -245,38 +324,47 @@ namespace O3DE::ProjectManager { if (!projectPath.isEmpty()) { - AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); - AZStd::string executableFilename = "Editor"; - AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION); - auto cmdPath = AZ::IO::FixedMaxPathString::format("%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), projectPath.toStdString().c_str()); - - AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - processLaunchInfo.m_commandlineParameters = cmdPath; - bool launchSucceeded = AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); - if (!launchSucceeded) + if (!WarnIfInBuildQueue(projectPath)) { - AZ_Error("ProjectManager", false, "Failed to launch editor"); - QMessageBox::critical( this, tr("Error"), tr("Failed to launch the Editor, please verify the project settings are valid.")); - } - else - { - // prevent the user from accidentally pressing the button while the editor is launching - // and let them know what's happening - ProjectButton* button = qobject_cast(sender()); - if (button) + AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); + AZStd::string executableFilename = "Editor"; + AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION); + auto cmdPath = AZ::IO::FixedMaxPathString::format( + "%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), + projectPath.toStdString().c_str()); + + AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; + processLaunchInfo.m_commandlineParameters = cmdPath; + bool launchSucceeded = AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); + if (!launchSucceeded) { - button->SetButtonEnabled(false); - button->SetButtonOverlayText(tr("Opening Editor...")); + AZ_Error("ProjectManager", false, "Failed to launch editor"); + QMessageBox::critical( + this, tr("Error"), tr("Failed to launch the Editor, please verify the project settings are valid.")); } + else + { + // prevent the user from accidentally pressing the button while the editor is launching + // and let them know what's happening + ProjectButton* button = qobject_cast(sender()); + if (button) + { + button->SetLaunchButtonEnabled(false); + button->SetButtonOverlayText(tr("Opening Editor...")); + } - // enable the button after 3 seconds - constexpr int waitTimeInMs = 3000; - QTimer::singleShot(waitTimeInMs, this, [this, button] { - if (button) + // enable the button after 3 seconds + constexpr int waitTimeInMs = 3000; + QTimer::singleShot( + waitTimeInMs, this, + [this, button] { - button->SetButtonEnabled(true); - } - }); + if (button) + { + button->SetLaunchButtonEnabled(true); + } + }); + } } } else @@ -288,38 +376,90 @@ namespace O3DE::ProjectManager } void ProjectsScreen::HandleEditProject(const QString& projectPath) { - emit NotifyCurrentProject(projectPath); - emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject); + if (!WarnIfInBuildQueue(projectPath)) + { + emit NotifyCurrentProject(projectPath); + emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject); + } } void ProjectsScreen::HandleCopyProject(const QString& projectPath) { - // Open file dialog and choose location for copied project then register copy with O3DE - if (ProjectUtils::CopyProjectDialog(projectPath, this)) + if (!WarnIfInBuildQueue(projectPath)) { - emit ResetScreenRequest(ProjectManagerScreen::Projects); - emit ChangeScreenRequest(ProjectManagerScreen::Projects); + // Open file dialog and choose location for copied project then register copy with O3DE + if (ProjectUtils::CopyProjectDialog(projectPath, this)) + { + ResetProjectsContent(); + emit ChangeScreenRequest(ProjectManagerScreen::Projects); + } } } void ProjectsScreen::HandleRemoveProject(const QString& projectPath) { - // Unregister Project from O3DE and reload projects - if (ProjectUtils::UnregisterProject(projectPath)) + if (!WarnIfInBuildQueue(projectPath)) { - emit ResetScreenRequest(ProjectManagerScreen::Projects); - emit ChangeScreenRequest(ProjectManagerScreen::Projects); + // Unregister Project from O3DE and reload projects + if (ProjectUtils::UnregisterProject(projectPath)) + { + ResetProjectsContent(); + emit ChangeScreenRequest(ProjectManagerScreen::Projects); + } } } void ProjectsScreen::HandleDeleteProject(const QString& projectPath) { - QMessageBox::StandardButton warningResult = QMessageBox::warning( - this, tr("Delete Project"), tr("Are you sure?\nProject will be removed from O3DE and directory will be deleted!"), - QMessageBox::No | QMessageBox::Yes); + if (!WarnIfInBuildQueue(projectPath)) + { + QMessageBox::StandardButton warningResult = QMessageBox::warning(this, + tr("Delete Project"), + tr("Are you sure?\nProject will be unregistered from O3DE and project directory will be deleted from your disk."), + QMessageBox::No | QMessageBox::Yes); + + if (warningResult == QMessageBox::Yes) + { + // Remove project from O3DE and delete from disk + HandleRemoveProject(projectPath); + ProjectUtils::DeleteProjectFiles(projectPath); + } + } + } - if (warningResult == QMessageBox::Yes) + void ProjectsScreen::SuggestBuildProject(const ProjectInfo& projectInfo) + { + if (projectInfo.m_needsBuild) { - // Remove project from O3DE and delete from disk - HandleRemoveProject(projectPath); - ProjectUtils::DeleteProjectFiles(projectPath); + if (RequiresBuildProjectIterator(projectInfo.m_path) == m_requiresBuild.end()) + { + m_requiresBuild.append(projectInfo); + } + ResetProjectsContent(); + } + else + { + QMessageBox::information(this, + tr("Project Should be rebuilt."), + projectInfo.m_projectName + tr(" project likely needs to be rebuilt.")); + } + } + + void ProjectsScreen::QueueBuildProject(const ProjectInfo& projectInfo) + { + auto requiredIter = RequiresBuildProjectIterator(projectInfo.m_path); + if (requiredIter != m_requiresBuild.end()) + { + m_requiresBuild.erase(requiredIter); + } + + if (!BuildQueueContainsProject(projectInfo.m_path)) + { + if (m_buildQueue.empty() && !m_currentBuilder) + { + StartProjectBuild(projectInfo); + } + else + { + m_buildQueue.append(projectInfo); + } } } @@ -331,17 +471,7 @@ namespace O3DE::ProjectManager } else { - // refresh the projects content by re-creating it for now - if (m_projectsContent) - { - m_stack->removeWidget(m_projectsContent); - m_projectsContent->deleteLater(); - } - - m_projectsContent = CreateProjectsContent(); - - m_stack->addWidget(m_projectsContent); - m_stack->setCurrentWidget(m_projectsContent); + ResetProjectsContent(); } } @@ -363,4 +493,89 @@ namespace O3DE::ProjectManager return displayFirstTimeContent; } + void ProjectsScreen::StartProjectBuild(const ProjectInfo& projectInfo) + { + if (ProjectUtils::IsVS2019Installed()) + { + QMessageBox::StandardButton buildProject = QMessageBox::information( + this, + tr("Building \"%1\"").arg(projectInfo.m_projectName), + tr("Ready to build \"%1\"?").arg(projectInfo.m_projectName), + QMessageBox::No | QMessageBox::Yes); + + if (buildProject == QMessageBox::Yes) + { + m_currentBuilder = new ProjectBuilderController(projectInfo, nullptr, this); + ResetProjectsContent(); + connect(m_currentBuilder, &ProjectBuilderController::Done, this, &ProjectsScreen::ProjectBuildDone); + + m_currentBuilder->Start(); + } + else + { + ProjectBuildDone(); + } + } + } + + void ProjectsScreen::ProjectBuildDone() + { + delete m_currentBuilder; + m_currentBuilder = nullptr; + + if (!m_buildQueue.empty()) + { + StartProjectBuild(m_buildQueue.front()); + m_buildQueue.pop_front(); + } + else + { + ResetProjectsContent(); + } + } + + QList::iterator ProjectsScreen::RequiresBuildProjectIterator(const QString& projectPath) + { + QString nativeProjPath(QDir::toNativeSeparators(projectPath)); + auto projectIter = m_requiresBuild.begin(); + for (; projectIter != m_requiresBuild.end(); ++projectIter) + { + if (QDir::toNativeSeparators(projectIter->m_path) == nativeProjPath) + { + break; + } + } + + return projectIter; + } + + bool ProjectsScreen::BuildQueueContainsProject(const QString& projectPath) + { + QString nativeProjPath(QDir::toNativeSeparators(projectPath)); + for (const ProjectInfo& project : m_buildQueue) + { + if (QDir::toNativeSeparators(project.m_path) == nativeProjPath) + { + return true; + } + } + + return false; + } + + bool ProjectsScreen::WarnIfInBuildQueue(const QString& projectPath) + { + if (BuildQueueContainsProject(projectPath)) + { + QMessageBox::warning( + this, + tr("Action Temporarily Disabled!"), + tr("Action not allowed on projects in build queue.")); + + return true; + } + + return false; + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.h b/Code/Tools/ProjectManager/Source/ProjectsScreen.h index e02b34525b..bc28d4ef30 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.h +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.h @@ -13,21 +13,28 @@ #if !defined(Q_MOC_RUN) #include +#include + +#include #endif QT_FORWARD_DECLARE_CLASS(QPaintEvent) QT_FORWARD_DECLARE_CLASS(QFrame) QT_FORWARD_DECLARE_CLASS(QStackedWidget) +QT_FORWARD_DECLARE_CLASS(QLayout) namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(ProjectBuilderController); + QT_FORWARD_DECLARE_CLASS(ProjectButton); + class ProjectsScreen : public ScreenWidget { public: explicit ProjectsScreen(QWidget* parent = nullptr); - ~ProjectsScreen() = default; + ~ProjectsScreen(); ProjectManagerScreen GetScreenEnum() override; QString GetTabText() override; @@ -35,6 +42,7 @@ namespace O3DE::ProjectManager protected: void NotifyCurrentScreen() override; + void ProjectBuildDone(); protected slots: void HandleNewProjectButton(); @@ -45,19 +53,32 @@ namespace O3DE::ProjectManager void HandleRemoveProject(const QString& projectPath); void HandleDeleteProject(const QString& projectPath); + void SuggestBuildProject(const ProjectInfo& projectInfo); + void QueueBuildProject(const ProjectInfo& projectInfo); + void paintEvent(QPaintEvent* event) override; private: QFrame* CreateFirstTimeContent(); - QFrame* CreateProjectsContent(); + QFrame* CreateProjectsContent(QString buildProjectPath = "", ProjectButton** projectButton = nullptr); + ProjectButton* CreateProjectButton(ProjectInfo& project, QLayout* flowLayout, bool processing = false); + void ResetProjectsContent(); bool ShouldDisplayFirstTimeContent(); - QAction* m_createNewProjectAction; - QAction* m_addExistingProjectAction; + void StartProjectBuild(const ProjectInfo& projectInfo); + QList::iterator RequiresBuildProjectIterator(const QString& projectPath); + bool BuildQueueContainsProject(const QString& projectPath); + bool WarnIfInBuildQueue(const QString& projectPath); + + QAction* m_createNewProjectAction = nullptr; + QAction* m_addExistingProjectAction = nullptr; QPixmap m_background; - QFrame* m_firstTimeContent; - QFrame* m_projectsContent; - QStackedWidget* m_stack; + QFrame* m_firstTimeContent = nullptr; + QFrame* m_projectsContent = nullptr; + QStackedWidget* m_stack = nullptr; + QList m_requiresBuild; + QQueue m_buildQueue; + ProjectBuilderController* m_currentBuilder = nullptr; const QString m_projectPreviewImagePath = "/preview.png"; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 3263505f9e..5f4bb833d8 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -667,7 +667,7 @@ namespace O3DE::ProjectManager { ProjectInfo projectInfo; projectInfo.m_path = Py_To_String(path); - projectInfo.m_isNew = false; + projectInfo.m_needsBuild = false; auto projectData = m_manifest.attr("get_project_json_data")(pybind11::none(), path); if (pybind11::isinstance(projectData)) diff --git a/Code/Tools/ProjectManager/Source/ScreenWidget.h b/Code/Tools/ProjectManager/Source/ScreenWidget.h index 2ad6d30201..47baed261c 100644 --- a/Code/Tools/ProjectManager/Source/ScreenWidget.h +++ b/Code/Tools/ProjectManager/Source/ScreenWidget.h @@ -13,6 +13,7 @@ #if !defined(Q_MOC_RUN) #include +#include #include #include @@ -61,6 +62,7 @@ namespace O3DE::ProjectManager void GotoPreviousScreenRequest(); void ResetScreenRequest(ProjectManagerScreen screen); void NotifyCurrentProject(const QString& projectPath); + void NotifyBuildProject(const ProjectInfo& projectInfo); }; diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp index 52fcbf354a..646f66a557 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp @@ -177,6 +177,7 @@ namespace O3DE::ProjectManager connect(newScreen, &ScreenWidget::GotoPreviousScreenRequest, this, &ScreensCtrl::GotoPreviousScreen); connect(newScreen, &ScreenWidget::ResetScreenRequest, this, &ScreensCtrl::ResetScreen); connect(newScreen, &ScreenWidget::NotifyCurrentProject, this, &ScreensCtrl::NotifyCurrentProject); + connect(newScreen, &ScreenWidget::NotifyBuildProject, this, &ScreensCtrl::NotifyBuildProject); } void ScreensCtrl::ResetAllScreens() diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.h b/Code/Tools/ProjectManager/Source/ScreensCtrl.h index 3b51ed529a..841108dff7 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.h +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.h @@ -13,6 +13,7 @@ #if !defined(Q_MOC_RUN) #include +#include #include #include @@ -39,6 +40,7 @@ namespace O3DE::ProjectManager signals: void NotifyCurrentProject(const QString& projectPath); + void NotifyBuildProject(const ProjectInfo& projectInfo); public slots: bool ChangeToScreen(ProjectManagerScreen screen); diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index a383a0f93b..6fcb1b1c71 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -119,7 +119,9 @@ namespace O3DE::ProjectManager void UpdateProjectCtrl::HandleNextButton() { - if (m_stack->currentIndex() == ScreenOrder::Settings) + bool shouldRebuild = false; + + if (m_stack->currentIndex() == ScreenOrder::Settings && m_updateSettingsScreen) { if (m_updateSettingsScreen) { @@ -155,11 +157,17 @@ namespace O3DE::ProjectManager m_projectInfo = newProjectSettings; } } - - if (m_stack->currentIndex() == ScreenOrder::Gems && m_gemCatalogScreen) + else if (m_stack->currentIndex() == ScreenOrder::Gems && m_gemCatalogScreen) { // Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project. m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path); + + shouldRebuild = true; + } + + if (shouldRebuild) + { + emit NotifyBuildProject(m_projectInfo); } emit ChangeScreenRequest(ProjectManagerScreen::Projects); diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index 40f450ab6f..eb9cd1145e 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -38,6 +38,8 @@ set(FILES Source/ProjectInfo.cpp Source/ProjectUtils.h Source/ProjectUtils.cpp + Source/ProjectBuilder.h + Source/ProjectBuilder.cpp Source/UpdateProjectSettingsScreen.h Source/UpdateProjectSettingsScreen.cpp Source/NewProjectSettingsScreen.h From 9df995dd264516dfd821ca5c51c84400f14f957c Mon Sep 17 00:00:00 2001 From: Ken Pruiksma Date: Fri, 4 Jun 2021 20:57:44 -0500 Subject: [PATCH 71/71] Temporal anti-aliasing and constrast adaptive sharpening (#1161) First version of temporal antialiasing and contrast adaptive sharpening for GA. Works well in most cases but still has a few issues that will need additional time. This is only the passes and shaders with no exposure to the editor. TAA and CAS can be turned on by enabling their respective passes in the pipeline. All of the code has been previously reviewed in smaller PRs into the taa_staging branch: aws-lumberyard-dev#29 aws-lumberyard-dev#53 aws-lumberyard-dev#73 aws-lumberyard-dev#79 aws-lumberyard-dev#84 Main issues: - Bloom doesn't play nice with TAA and seems to greatly amplify any flickering - AuxGeom jitters with the camera, so TAA doesn't currently work well in editor - Transparencies don't have correct motion vectors. History rectification keeps this from looking too bad, but could still be improved - There is still more that could be done to inhibit flickering, usually from specular aliasing - Motion vectors aren't correct on POM unless PDO is turned on, which can result in some blurring during motion. - SSAO can contribute to flickering in its default half res configuration. Changing this to full res mitigates the problem. Squashed merge of the following: * [ATOM-13987] Initial checkin of Taa pass. * TAA pass setup WIP. (does not work yet due to pass configuration issues). * Taa WIP - Camera motion vectors fixed and hooked up. TAA does simple reprojection and rejection based on depth. * Small update to use lerp and add some comments. * Fix issue with attachments not being set up on bindings at initialization. Fixing issue with half-pixel offsets in TAA shader * - Motion vector passes now use the same output with mesh motion vectors overwriting camera motion vectors. - Taa pass now works with multiple pipelines. - Cleaned up TAA shader a bit. * Fixes from PR review. * Adding check for multiple attachments of the same name with different resources in Pass::ImportAttachments(). * Adding camera jitter with configurable position count. Updated TAA to blend in tonemapped space. * Fixes from PR review. Fixing camera motion vectors for background (infinite distance) * Updates to taa shader from PR review * Adding a rcp input color size. * Fix comment on PassAttachment::Update() * Updates for PR review. * Fixing missing const on the FrameAttachment* in Pass's call to FindAttachment() * Taa WIP - Adding filtering to both the current pixel and history. Adding rectification based on variance clipping. Adding some basic anti-flickering. Removing rejection based on depth. * Updates from PR code review. Mostly better commenting and naming. * Adding contrast adaptive sharpening based on AMD FidelityFX CAS to help with the softness added by TAA. * Changing to using luminance for sharpening instead of just green. Added some comments. * Moving Taa's NaN check to a better location. Disabling TAA and sharpening in prep for check in. * Updates from PR feedback. --- .../Passes/ContrastAdaptiveSharpening.pass | 75 +++++ .../Common/Assets/Passes/MainPipeline.pass | 7 + .../Assets/Passes/MeshMotionVector.pass | 39 +-- .../Assets/Passes/MotionVectorParent.pass | 20 ++ .../Assets/Passes/PassTemplates.azasset | 8 + .../Assets/Passes/PostProcessParent.pass | 52 +++- .../Passes/SMAA1xApplyLinearHDRColor.pass | 6 + .../Feature/Common/Assets/Passes/Taa.pass | 113 ++++++++ .../MotionVector/CameraMotionVector.azsl | 21 +- .../MotionVector/MeshMotionVectorCommon.azsli | 4 + .../ContrastAdaptiveSharpening.azsl | 85 ++++++ .../ContrastAdaptiveSharpening.shader | 11 + .../Assets/Shaders/PostProcessing/Taa.azsl | 271 ++++++++++++++++++ .../Assets/Shaders/PostProcessing/Taa.shader | 11 + .../atom_feature_common_asset_files.cmake | 2 + .../Code/Source/CommonSystemComponent.cpp | 5 + .../Code/Source/PostProcessing/TaaPass.cpp | 247 ++++++++++++++++ .../Code/Source/PostProcessing/TaaPass.h | 105 +++++++ .../Code/atom_feature_common_files.cmake | 2 + .../Atom/RHI/FrameGraphAttachmentInterface.h | 6 + .../Code/Include/Atom/RPI.Public/Pass/Pass.h | 4 +- .../Atom/RPI.Public/Pass/PassAttachment.h | 3 +- .../RPI/Code/Include/Atom/RPI.Public/View.h | 16 +- .../RPI/Code/Source/RPI.Public/Pass/Pass.cpp | 29 +- .../Source/RPI.Public/Pass/PassAttachment.cpp | 4 +- Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp | 83 +++--- 26 files changed, 1138 insertions(+), 91 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Assets/Passes/ContrastAdaptiveSharpening.pass create mode 100644 Gems/Atom/Feature/Common/Assets/Passes/Taa.pass create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.azsl create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.shader create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.azsl create mode 100644 Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.shader create mode 100644 Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp create mode 100644 Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.h diff --git a/Gems/Atom/Feature/Common/Assets/Passes/ContrastAdaptiveSharpening.pass b/Gems/Atom/Feature/Common/Assets/Passes/ContrastAdaptiveSharpening.pass new file mode 100644 index 0000000000..44ab6f4a52 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/ContrastAdaptiveSharpening.pass @@ -0,0 +1,75 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "ContrastAdaptiveSharpeningTemplate", + "PassClass": "ComputePass", + "Slots": [ + { + "Name": "InputColor", + "SlotType": "Input", + "ShaderInputName": "m_inputColor", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "OutputColor", + "SlotType": "Output", + "ShaderInputName": "m_outputColor", + "ScopeAttachmentUsage": "Shader" + } + ], + "ImageAttachments": [ + { + "Name": "Output", + "FormatSource": { + "Pass": "This", + "Attachment": "InputColor" + }, + "SizeSource": { + "Source": { + "Pass": "This", + "Attachment": "InputColor" + } + }, + "ImageDescriptor": { + "Format": "R16G16B16A16_FLOAT", + "BindFlags": "3", + "SharedQueueMask": "1" + } + } + ], + "Connections": [ + { + "LocalSlot": "OutputColor", + "AttachmentRef": { + "Pass": "This", + "Attachment": "Output" + } + } + ], + "FallbackConnections": [ + { + "Input": "InputColor", + "Output": "OutputColor" + } + ], + "PassData": { + "$type": "ComputePassData", + "ShaderAsset": { + "FilePath": "Shaders/Postprocessing/ContrastAdaptiveSharpening.shader" + }, + "Make Fullscreen Pass": true, + "ShaderDataMappings": { + "FloatMappings": [ + { + "Name": "m_strength", + "Value": 0.25 + } + ] + } + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass index af7408b48c..b2e0cf088e 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass @@ -341,6 +341,13 @@ "Attachment": "Depth" } }, + { + "LocalSlot": "MotionVectors", + "AttachmentRef": { + "Pass": "MotionVectorPass", + "Attachment": "MotionVectorOutput" + } + }, { "LocalSlot": "SwapChainOutput", "AttachmentRef": { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/MeshMotionVector.pass b/Gems/Atom/Feature/Common/Assets/Passes/MeshMotionVector.pass index 57600440b4..4c14fd9b3f 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/MeshMotionVector.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/MeshMotionVector.pass @@ -13,22 +13,11 @@ "SlotType": "Input", "ScopeAttachmentUsage": "InputAssembly" }, - // Outputs... + // Input/Output... { - "Name": "Output", - "SlotType": "Output", - "ScopeAttachmentUsage": "RenderTarget", - "LoadStoreAction": { - "ClearValue": { - "Value": [ - 0.0, - 0.0, - 0.0, - {} - ] - }, - "LoadAction": "Clear" - } + "Name": "MotionInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" }, { "Name": "OutputDepthStencil", @@ -46,19 +35,6 @@ } ], "ImageAttachments": [ - { - "Name": "MotionBuffer", - "SizeSource": { - "Source": { - "Pass": "Parent", - "Attachment": "SwapChainOutput" - } - }, - "ImageDescriptor": { - "Format": "R16G16_FLOAT", - "SharedQueueMask": "Graphics" - } - }, { "Name": "DepthStencil", "SizeSource": { @@ -74,13 +50,6 @@ } ], "Connections": [ - { - "LocalSlot": "Output", - "AttachmentRef": { - "Pass": "This", - "Attachment": "MotionBuffer" - } - }, { "LocalSlot": "OutputDepthStencil", "AttachmentRef": { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/MotionVectorParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/MotionVectorParent.pass index a8369e4618..d7f4894706 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/MotionVectorParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/MotionVectorParent.pass @@ -20,6 +20,19 @@ { "Name": "SwapChainOutput", "SlotType": "InputOutput" + }, + { + "Name": "MotionVectorOutput", + "SlotType": "Output" + } + ], + "Connections": [ + { + "LocalSlot": "MotionVectorOutput", + "AttachmentRef": { + "Pass": "MeshMotionVectorPass", + "Attachment": "MotionInputOutput" + } } ], "PassRequests": [ @@ -50,6 +63,13 @@ "Pass": "Parent", "Attachment": "SkinnedMeshes" } + }, + { + "LocalSlot": "MotionInputOutput", + "AttachmentRef": { + "Pass": "CameraMotionVectorPass", + "Attachment": "Output" + } } ], "PassData": { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset index c56e8932b1..702ac8fe72 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset +++ b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset @@ -284,6 +284,14 @@ "Name": "SMAA1xApplyPerceptualColorTemplate", "Path": "Passes/SMAA1xApplyPerceptualColor.pass" }, + { + "Name": "TaaTemplate", + "Path": "Passes/Taa.pass" + }, + { + "Name": "ContrastAdaptiveSharpeningTemplate", + "Path": "Passes/ContrastAdaptiveSharpening.pass" + }, { "Name": "SsaoParentTemplate", "Path": "Passes/SsaoParent.pass" diff --git a/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass index 36f7f1e985..fb27770da3 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass @@ -16,6 +16,10 @@ "Name": "Depth", "SlotType": "Input" }, + { + "Name": "MotionVectors", + "SlotType": "Input" + }, // SwapChain here is only used to reference the frame height and format { "Name": "SwapChainOutput", @@ -40,8 +44,8 @@ { "LocalSlot": "Output", "AttachmentRef": { - "Pass": "LightAdaptation", - "Attachment": "Output" + "Pass": "ContrastAdaptiveSharpeningPass", + "Attachment": "OutputColor" } }, { @@ -80,6 +84,34 @@ } ] }, + { + "Name": "TaaPass", + "TemplateName": "TaaTemplate", + "Enabled": false, + "Connections": [ + { + "LocalSlot": "InputColor", + "AttachmentRef": { + "Pass": "SMAA1xApplyLinearHDRColorPass", + "Attachment": "OutputColor" + } + }, + { + "LocalSlot": "InputDepth", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "Depth" + } + }, + { + "LocalSlot": "MotionVectors", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "MotionVectors" + } + } + ] + }, { "Name": "DepthOfFieldPass", "TemplateName": "DepthOfFieldTemplate", @@ -88,7 +120,7 @@ { "LocalSlot": "DoFColorInput", "AttachmentRef": { - "Pass": "SMAA1xApplyLinearHDRColorPass", + "Pass": "TaaPass", "Attachment": "OutputColor" } }, @@ -134,6 +166,20 @@ } } ] + }, + { + "Name": "ContrastAdaptiveSharpeningPass", + "TemplateName": "ContrastAdaptiveSharpeningTemplate", + "Enabled": false, + "Connections": [ + { + "LocalSlot": "InputColor", + "AttachmentRef": { + "Pass": "LightAdaptation", + "Attachment": "Output" + } + } + ] } ] } diff --git a/Gems/Atom/Feature/Common/Assets/Passes/SMAA1xApplyLinearHDRColor.pass b/Gems/Atom/Feature/Common/Assets/Passes/SMAA1xApplyLinearHDRColor.pass index 98700d5f0c..70604fba25 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/SMAA1xApplyLinearHDRColor.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/SMAA1xApplyLinearHDRColor.pass @@ -40,6 +40,12 @@ } } ], + "FallbackConnections": [ + { + "Input": "InputColor", + "Output": "OutputColor" + } + ], "PassRequests": [ { "Name": "SMAAConvertToPerceptualColor", diff --git a/Gems/Atom/Feature/Common/Assets/Passes/Taa.pass b/Gems/Atom/Feature/Common/Assets/Passes/Taa.pass new file mode 100644 index 0000000000..f1ba156007 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/Taa.pass @@ -0,0 +1,113 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "TaaTemplate", + "PassClass": "TaaPass", + "Slots": [ + { + "Name": "InputColor", + "SlotType": "Input", + "ShaderInputName": "m_inputColor", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "InputDepth", + "SlotType": "Input", + "ShaderInputName": "m_inputDepth", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "MotionVectors", + "SlotType": "Input", + "ShaderInputName": "m_motionVectors", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "LastFrameAccumulation", + "SlotType": "Input", + "ShaderInputName": "m_lastFrameAccumulation", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "OutputColor", + "SlotType": "Output", + "ShaderInputName": "m_outputColor", + "ScopeAttachmentUsage": "Shader" + } + ], + "ImageAttachments": [ + { + "Name": "Accumulation1", + "Lifetime": "Imported", + "FormatSource": { + "Pass": "This", + "Attachment": "InputColor" + }, + "SizeSource": { + "Source": { + "Pass": "This", + "Attachment": "InputColor" + } + }, + "ImageDescriptor": { + "Format": "R16G16B16A16_FLOAT", + "BindFlags": "3", + "SharedQueueMask": "1" + } + }, + { + "Name": "Accumulation2", + "Lifetime": "Imported", + "FormatSource": { + "Pass": "This", + "Attachment": "InputColor" + }, + "SizeSource": { + "Source": { + "Pass": "This", + "Attachment": "InputColor" + } + }, + "ImageDescriptor": { + "Format": "R16G16B16A16_FLOAT", + "BindFlags": "3", + "SharedQueueMask": "1" + } + } + ], + "FallbackConnections": [ + { + "Input": "InputColor", + "Output": "OutputColor" + } + ], + "PassData": { + "$type": "TaaPassData", + "ShaderAsset": { + "FilePath": "Shaders/Postprocessing/Taa.shader" + }, + "Make Fullscreen Pass": true, + "ShaderDataMappings": { + "FloatMappings": [ + { + "Name": "m_currentFrameContribution", + "Value": 0.1 + }, + { + "Name": "m_clampGamma", + "Value": 1.0 + }, + { + "Name": "m_maxDeviationBeforeDampening", + "Value": 0.5 + } + ] + }, + "NumJitterPositions": 16 + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/CameraMotionVector.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/CameraMotionVector.azsl index a073f42f03..c83e5138e6 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/CameraMotionVector.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/CameraMotionVector.azsl @@ -39,10 +39,27 @@ PSOutput MainPS(VSOutput IN) PSOutput OUT; float depth = PassSrg::m_depthStencil.Sample(PassSrg::LinearSampler, IN.m_texCoord).r; + + // If depth is 0, that means depth is on the far plane. This should be treated as being infinitely far + // away, not actually on the far plane, because the infinitely far background shouldn't move as a result + // of camera translation. Tweaking the depth to -near/far distance makes that happen. Keep in mind near + // and far are inverted, so this normally a very small value. + if (depth == 0.0) + { + depth = -ViewSrg::GetFarZ() / ViewSrg::GetNearZ(); + } + float2 clipPos = float2(mad(IN.m_texCoord.x, 2.0, -1.0), mad(IN.m_texCoord.y, -2.0, 1.0)); float4 worldPos = mul(ViewSrg::m_viewProjectionInverseMatrix, float4(clipPos, depth, 1.0)); + float4 clipPosPrev = mul(ViewSrg::m_viewProjectionPrevMatrix, float4((worldPos / worldPos.w).xyz, 1.0)); - - OUT.m_motion = (clipPos - (clipPosPrev / clipPosPrev.w).xy) * 0.5; + clipPosPrev = (clipPosPrev / clipPosPrev.w); + + // Clip space is from -1.0 to 1.0, so the motion vectors are 2x as big as they should be + OUT.m_motion = (clipPos - clipPosPrev.xy) * 0.5; + + // Flip y to line up with uv coordinates + OUT.m_motion.y = -OUT.m_motion.y; + return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli index c11e9d9e0e..ff2758af87 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli @@ -41,5 +41,9 @@ PSOutput MainPS(VSOutput IN) float2 motion = (clipPos.xy / clipPos.w - clipPosPrev.xy / clipPosPrev.w) * 0.5; OUT.m_motion = motion; + + // Flip y to line up with uv coordinates + OUT.m_motion.y = -OUT.m_motion.y; + return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.azsl new file mode 100644 index 0000000000..14fa942a7d --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.azsl @@ -0,0 +1,85 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +#define TILE_DIM_X 16 +#define TILE_DIM_Y 16 + +ShaderResourceGroup PassSrg : SRG_PerPass +{ + Texture2D m_inputColor; + RWTexture2D m_outputColor; + + float m_strength; // Strength of the sharpening effect. Range from 0 to 1. +} + + +// Constrast Adaptive Sharpening, based on AMD FidelityFX CAS - https://gpuopen.com/fidelityfx-cas/ + +// This shader sharpens the input based on the contrast of the local neighborhood +// so that only areas that need sharpening are sharpened, while high constast areas +// are mostly left alone. + +[numthreads(TILE_DIM_X, TILE_DIM_Y, 1)] +void MainCS( + uint3 dispatchThreadID : SV_DispatchThreadID, + uint3 groupID : SV_GroupID, + uint groupIndex : SV_GroupIndex) +{ + uint2 pixelCoord = dispatchThreadID.xy; + + // Fetch local neighborhood to determin sharpening weight. + // a + // b c d + // e + + float3 sampleA = PassSrg::m_inputColor[pixelCoord + int2( 0, -1)].rgb; + float3 sampleB = PassSrg::m_inputColor[pixelCoord + int2(-1, 0)].rgb; + float3 sampleC = PassSrg::m_inputColor[pixelCoord + int2( 0, 0)].rgb; + float3 sampleD = PassSrg::m_inputColor[pixelCoord + int2( 1, 0)].rgb; + float3 sampleE = PassSrg::m_inputColor[pixelCoord + int2( 0, 1)].rgb; + + float lumA = GetLuminance(sampleA); + float lumB = GetLuminance(sampleB); + float lumC = GetLuminance(sampleC); + float lumD = GetLuminance(sampleD); + float lumE = GetLuminance(sampleE); + + // Get the min and max. Just use the green channel for luminance. + float minLum = min(min(lumA, lumB), min(lumC, min(lumD, lumE))); + float maxLum = max(max(lumA, lumB), max(lumC, max(lumD, lumE))); + + float dMinLum = minLum; // Distance from 0 to minimum + float dMaxLum = 1.0 - maxLum; // Distance from 1 to the maximum + + // baseSharpening is higher when local contrast is lower to avoid over-sharpening. + float baseSharpening = min(dMinLum, dMaxLum) / max(maxLum, 0.0001); + baseSharpening = sqrt(baseSharpening); // bias towards more sharpening + + // Negative weights for sharpening effect, center pixel is always weighted 1. + float developerMaximum = lerp(-0.125, -0.2, PassSrg::m_strength); + float weight = baseSharpening * developerMaximum; + float totalWeight = weight * 4 + 1.0; + + float3 output = + ( + sampleA * weight + + sampleB * weight + + sampleC + + sampleD * weight + + sampleE * weight + ) / totalWeight; + + PassSrg::m_outputColor[pixelCoord] = float4(output, 1.0); +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.shader new file mode 100644 index 0000000000..756ce0ec7a --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ContrastAdaptiveSharpening.shader @@ -0,0 +1,11 @@ +{ + "Source": "ContrastAdaptiveSharpening", + "ProgramSettings": { + "EntryPoints": [ + { + "name": "MainCS", + "type": "Compute" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.azsl new file mode 100644 index 0000000000..94944df9de --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.azsl @@ -0,0 +1,271 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +#define TILE_DIM_X 16 +#define TILE_DIM_Y 16 + +ShaderResourceGroup PassSrg : SRG_PerPass +{ + Texture2D m_inputColor; + Texture2D m_inputDepth; + Texture2D m_motionVectors; + Texture2D m_lastFrameAccumulation; + + RWTexture2D m_outputColor; + + Sampler LinearSampler + { + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; + + // Current frame's default contribution to the history. + float m_currentFrameContribution; + + // Increase this value for weaker clamping, decrease for stronger clamping, default 1.0. + float m_clampGamma; + + // Default 0.5, used for flicker reduction. Any sample further than this many standard deviations outside the neighborhood + // will have its weight decreased. The further outside the max deviation, the more its weight is reduced. + float m_maxDeviationBeforeDampening; + + struct Constants + { + uint2 m_inputColorSize; + float2 m_inputColorRcpSize; + + // 3x3 filter weights + // 8 2 6 + // 3 0 1 + // 7 4 5 + float4 m_weights1; // 0 1 2 3 + float4 m_weights2; // 4 5 6 7 + float4 m_weights3; // 8 x x x + }; + Constants m_constantData; +} + +static const int2 offsets[9] = +{ + // Center + int2(0, 0), + // Cross + int2( 1, 0), + int2( 0,-1), + int2(-1, 0), + int2( 0, 1), + // Diagonals + int2( 1,-1), + int2( 1, 1), + int2(-1,-1), + int2(-1, 1), +}; + +float3 RgbToYCoCg(float3 rgb) +{ + const float3x3 conversionMatrix = + { + 0.25, 0.50, 0.25, + 0.50, 0.00, -0.50, + -0.25, 0.50, -0.25 + }; + return mul(conversionMatrix, rgb); +} + +float3 YCoCgToRgb(float3 yCoCg) +{ + const float3x3 conversionMatrix = + { + 1.0, 1.0, -1.0, + 1.0, 0.0, 1.0, + 1.0, -1.0, -1.0 + }; + return mul(conversionMatrix, yCoCg); +} + +// Sample a texture with a 5 tap Catmull-Rom. Consider ripping this out and putting in a more general location. +// This function samples a 4x4 neighborhood around the uv. By taking advantage of bilinear filtering this can be +// done with only 9 taps on the edges between pixels. The cost is further reduced by dropping the 4 diagonal +// samples as their influence is negligible. +float4 SampleCatmullRom5Tap(Texture2D texture, SamplerState linearSampler, float2 uv, float2 textureSize, float2 rcpTextureSize, float sharpness) +{ + // Think of sample locations in the 4x4 neighborhood as having a top left coordinate of 0,0 and + // a bottom right coordinate of 3,3. + + // Find the position in texture space then round it to get the center of the 1,1 pixel (tc1) + float2 texelPos = uv * textureSize; + float2 tc1= floor(texelPos - 0.5) + 0.5; + + // Offset from center position to texel + float2 f = texelPos - tc1; + + // Compute Catmull-Rom weights based on the offset and sharpness + float c = sharpness; + float2 w0 = f * (-c + f * (2.0 * c - c * f)); + float2 w1 = 1.0 + f * f * (c -3.0 + (2.0 - c) * f); + float2 w2 = f * (c + f * ((3.0 - 2.0 * c) - (2.0 - c) * f)); + float2 w3 = f * f * (c * f - c); + + float2 w12 = w1 + w2; + + // Compute uv coordinates for sampling the texture + float2 tc0 = (tc1 - 1.0f) * rcpTextureSize; + float2 tc3 = (tc1 + 2.0f) * rcpTextureSize; + float2 tc12 = (tc1 + w2 / w12) * rcpTextureSize; + + // Compute sample weights + float sw0 = w12.x * w0.y; + float sw1 = w0.x * w12.y; + float sw2 = w12.x * w12.y; + float sw3 = w3.x * w12.y; + float sw4 = w12.x * w3.y; + + // total weight of samples to normalize result. + float totalWeight = sw0 + sw1 + sw2 + sw3 + sw4; + + float4 result = 0.0f; + result += texture.SampleLevel(linearSampler, float2(tc12.x, tc0.y), 0.0) * sw0; + result += texture.SampleLevel(linearSampler, float2( tc0.x, tc12.y), 0.0) * sw1; + result += texture.SampleLevel(linearSampler, float2(tc12.x, tc12.y), 0.0) * sw2; + result += texture.SampleLevel(linearSampler, float2( tc3.x, tc12.y), 0.0) * sw3; + result += texture.SampleLevel(linearSampler, float2(tc12.x, tc3.y), 0.0) * sw4; + + return result / totalWeight; +} + +[numthreads(TILE_DIM_X, TILE_DIM_Y, 1)] +void MainCS( + uint3 dispatchThreadID : SV_DispatchThreadID, + uint3 groupID : SV_GroupID, + uint groupIndex : SV_GroupIndex) +{ + uint2 pixelCoord = dispatchThreadID.xy; + + const float filterWeights[9] = + { + PassSrg::m_constantData.m_weights1.x, + PassSrg::m_constantData.m_weights1.y, + PassSrg::m_constantData.m_weights1.z, + PassSrg::m_constantData.m_weights1.w, + PassSrg::m_constantData.m_weights2.x, + PassSrg::m_constantData.m_weights2.y, + PassSrg::m_constantData.m_weights2.z, + PassSrg::m_constantData.m_weights2.w, + PassSrg::m_constantData.m_weights3.x, + }; + + float3 sum = 0.0; + float3 sumOfSquares = 0.0; + float nearestDepth = 1.0; + uint2 nearestDepthPixelCoord; + + float3 thisFrameColor = float3(0.0, 0.0, 0.0); + + // Sample the neighborhood to filter the current pixel, gather statistics about + // its neighbors, and find the closest neighbor to choose a motion vector. + [unroll] for (int i = 0; i < 9; ++i) + { + uint2 neighborhoodPixelCoord = pixelCoord + offsets[i]; + float3 neighborhoodColor = PassSrg::m_inputColor[neighborhoodPixelCoord].rgb; + + // Convert to YCoCg space for better clipping. + neighborhoodColor = RgbToYCoCg(neighborhoodColor); + + sum += neighborhoodColor; + sumOfSquares += neighborhoodColor * neighborhoodColor; + thisFrameColor += neighborhoodColor * filterWeights[i]; + + // Find the coordinate of the nearest depth + float neighborhoodDepth = PassSrg::m_inputDepth[neighborhoodPixelCoord].r; + if (neighborhoodDepth < nearestDepth) + { + nearestDepth = neighborhoodDepth; + nearestDepthPixelCoord = neighborhoodPixelCoord; + } + } + + // Variance clipping, see http://developer.download.nvidia.com/gameworks/events/GDC2016/msalvi_temporal_supersampling.pdf + float3 mean = sum / 9.0; + float3 standardDeviation = max(0.0, sqrt(sumOfSquares / 9.0 - mean * mean)); + standardDeviation *= PassSrg::m_clampGamma; + + // Grab the motion vector from the closest pixel in the 3x3 neighborhood. This is done so that motion vectors correctly + // track edges. For instance, if a pixel lies on the edge of a moving object, where the color is a blend of the + // forground and background, it's possible for the pixel center to hit the (not moving) background. However, the correct + // history for this pixel will be the location this edge was the previous frame. By choosing the motion of the nearest + // pixel in the neighborhood that edge will be correctly tracked. + + // Motion vectors store the direction of movement, so to look up where things were in the previous frame, it's negated. + float2 previousPositionOffset = -PassSrg::m_motionVectors[nearestDepthPixelCoord]; + + // Get the uv coordinate for the previous frame. + float2 rcpSize = PassSrg::m_constantData.m_inputColorRcpSize; + float2 uvCoord = (pixelCoord + 0.5f) * rcpSize; + float2 uvOld = uvCoord + previousPositionOffset; + float2 previousPositionOffsetInPixels = float2(PassSrg::m_constantData.m_inputColorSize) * previousPositionOffset; + + // Sample the last frame using a 5-tap Catmull-Rom + float3 lastFrameColor = SampleCatmullRom5Tap(PassSrg::m_lastFrameAccumulation, PassSrg::LinearSampler, uvOld, PassSrg::m_constantData.m_inputColorSize, PassSrg::m_constantData.m_inputColorRcpSize, 0.5).rgb; + lastFrameColor = RgbToYCoCg(lastFrameColor); + + // Last frame color relative to mean + float3 centerColorOffset = lastFrameColor - mean; + float3 colorOffsetStandardDeviationRatio = abs(standardDeviation / centerColorOffset); + + // Clamp the color by the aabb of the standardDeviation. Can never be greater than 1, so will always be inside or on the bounds of the aabb. + float clampedColorLength = min(min(min(1, colorOffsetStandardDeviationRatio.x), colorOffsetStandardDeviationRatio.y), colorOffsetStandardDeviationRatio.z); + + // Calculate the true clamped color by offsetting it back from the mean. + float3 lastFrameClampedColor = mean + centerColorOffset * clampedColorLength; + + // Anti-flickering - Reduce current frame weight the more it deviates from the history based on the standard deviation of the neighborhood. + // Start reducing weight at differences greater than m_maxDeviationBeforeDampening standard deviations in luminance. + float standardDeviationWeight = standardDeviation.r * PassSrg::m_maxDeviationBeforeDampening; + float3 sdFromLastFrame = standardDeviationWeight / abs(lastFrameClampedColor.r - thisFrameColor.r); + + float currentFrameWeight = PassSrg::m_currentFrameContribution; + currentFrameWeight *= saturate(sdFromLastFrame * sdFromLastFrame); + + // Back to Rgb space + thisFrameColor = YCoCgToRgb(thisFrameColor); + lastFrameClampedColor = YCoCgToRgb(lastFrameClampedColor); + + // Out of bounds protection. + if (any(uvOld > 1.0) || any(uvOld < 0.0)) + { + currentFrameWeight = 1.0f; + } + + // Blend should be in perceptual space, so tonemap first + float luminance = GetLuminance(thisFrameColor); + thisFrameColor = thisFrameColor / (1 + luminance); + lastFrameClampedColor = lastFrameClampedColor / (1 + luminance); + + // Blend color with history + float3 color = lerp(lastFrameClampedColor, thisFrameColor, currentFrameWeight); + + // Un-tonemap color + color = color * (1.0 + luminance); + + // NaN protection (without this NaNs could get in the history buffer and quickly consume the frame) + color = max(0.0, color); + + PassSrg::m_outputColor[pixelCoord].rgb = color; + +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.shader new file mode 100644 index 0000000000..f30ff92f20 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/Taa.shader @@ -0,0 +1,11 @@ +{ + "Source": "Taa", + "ProgramSettings": { + "EntryPoints": [ + { + "name": "MainCS", + "type": "Compute" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index a9ba765329..3dfabc586a 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -89,6 +89,7 @@ set(FILES Passes/CascadedShadowmaps.pass Passes/CheckerboardResolveColor.pass Passes/CheckerboardResolveDepth.pass + Passes/ContrastAdaptiveSharpening.pass Passes/ConvertToAcescg.pass Passes/DebugOverlayParent.pass Passes/DeferredFog.pass @@ -207,6 +208,7 @@ set(FILES Passes/SsaoHalfRes.pass Passes/SsaoParent.pass Passes/SubsurfaceScattering.pass + Passes/Taa.pass Passes/Transparent.pass Passes/TransparentParent.pass Passes/UI.pass diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index d38db1b08e..af28624357 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -133,6 +134,7 @@ namespace AZ PostProcessFeatureProcessor::Reflect(context); ImGuiPassData::Reflect(context); RayTracingPassData::Reflect(context); + TaaPassData::Reflect(context); LightingPreset::Reflect(context); ModelPreset::Reflect(context); @@ -230,6 +232,9 @@ namespace AZ // Add Depth Downsample/Upsample passes passSystem->AddPassCreator(Name("DepthUpsamplePass"), &DepthUpsamplePass::Create); + + // Add Taa Pass + passSystem->AddPassCreator(Name("TaaPass"), &TaaPass::Create); // Add DepthOfField pass passSystem->AddPassCreator(Name("DepthOfFieldCompositePass"), &DepthOfFieldCompositePass::Create); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp new file mode 100644 index 0000000000..9f885ede70 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp @@ -0,0 +1,247 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace AZ::Render +{ + + RPI::Ptr TaaPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew TaaPass(descriptor); + return pass; + } + + TaaPass::TaaPass(const RPI::PassDescriptor& descriptor) + : Base(descriptor) + { + uint32_t numJitterPositions = 8; + + const TaaPassData* taaPassData = RPI::PassUtils::GetPassData(descriptor); + if (taaPassData) + { + numJitterPositions = taaPassData->m_numJitterPositions; + } + + // The coprimes 2, 3 are commonly used for halton sequences because they have an even distribution even for + // few samples. With larger primes you need to offset by some amount between each prime to have the same + // effect. We could allow this to be configurable in the future. + SetupSubPixelOffsets(2, 3, numJitterPositions); + } + + void TaaPass::CompileResources(const RHI::FrameGraphCompileContext& context) + { + struct TaaConstants + { + AZStd::array m_size = { 1, 1 }; + AZStd::array m_rcpSize = { 0.0, 0.0 }; + + AZStd::array m_weights1 = { 0.0 }; + AZStd::array m_weights2 = { 0.0 }; + AZStd::array m_weights3 = { 0.0 }; + }; + + TaaConstants cb; + RHI::Size inputSize = m_lastFrameAccumulationBinding->m_attachment->m_descriptor.m_image.m_size; + cb.m_size[0] = inputSize.m_width; + cb.m_size[1] = inputSize.m_height; + cb.m_rcpSize[0] = 1.0f / inputSize.m_width; + cb.m_rcpSize[1] = 1.0f / inputSize.m_height; + + Offset jitterOffset = m_subPixelOffsets.at(m_offsetIndex); + GenerateFilterWeights(Vector2(jitterOffset.m_xOffset, jitterOffset.m_yOffset)); + cb.m_weights1 = { m_filterWeights[0], m_filterWeights[1], m_filterWeights[2], m_filterWeights[3] }; + cb.m_weights2 = { m_filterWeights[4], m_filterWeights[5], m_filterWeights[6], m_filterWeights[7] }; + cb.m_weights3 = { m_filterWeights[8], 0.0f, 0.0f, 0.0f }; + + m_shaderResourceGroup->SetConstant(m_constantDataIndex, cb); + + + Base::CompileResources(context); + } + + void TaaPass::FrameBeginInternal(FramePrepareParams params) + { + RHI::Size inputSize = m_inputColorBinding->m_attachment->m_descriptor.m_image.m_size; + Vector2 rcpInputSize = Vector2(1.0 / inputSize.m_width, 1.0 / inputSize.m_height); + + RPI::ViewPtr view = GetRenderPipeline()->GetDefaultView(); + m_offsetIndex = (m_offsetIndex + 1) % m_subPixelOffsets.size(); + Offset offset = m_subPixelOffsets.at(m_offsetIndex); + view->SetClipSpaceOffset(offset.m_xOffset * rcpInputSize.GetX(), offset.m_yOffset * rcpInputSize.GetY()); + + m_lastFrameAccumulationBinding->SetAttachment(m_accumulationAttachments[m_accumulationOuptutIndex]); + m_accumulationOuptutIndex ^= 1; // swap which attachment is the output and last frame + + UpdateAttachmentImage(m_accumulationAttachments[m_accumulationOuptutIndex]); + m_outputColorBinding->SetAttachment(m_accumulationAttachments[m_accumulationOuptutIndex]); + + Base::FrameBeginInternal(params); + } + + void TaaPass::ResetInternal() + { + m_accumulationAttachments[0].reset(); + m_accumulationAttachments[1].reset(); + + m_inputColorBinding = nullptr; + m_lastFrameAccumulationBinding = nullptr; + m_outputColorBinding = nullptr; + + Base::ResetInternal(); + } + + void TaaPass::BuildAttachmentsInternal() + { + m_accumulationAttachments[0] = FindAttachment(Name("Accumulation1")); + m_accumulationAttachments[1] = FindAttachment(Name("Accumulation2")); + + bool hasAttachments = m_accumulationAttachments[0] || m_accumulationAttachments[1]; + AZ_Error("TaaPass", hasAttachments, "TaaPass must have Accumulation1 and Accumulation2 ImageAttachments defined."); + + if (hasAttachments) + { + // Make sure the attachments have images when the pass first loads. + for (auto i : { 0, 1 }) + { + if (!m_accumulationAttachments[i]->m_importedResource) + { + UpdateAttachmentImage(m_accumulationAttachments[i]); + } + } + } + + m_inputColorBinding = FindAttachmentBinding(Name("InputColor")); + AZ_Error("TaaPass", m_inputColorBinding, "TaaPass requires a slot for InputColor."); + m_lastFrameAccumulationBinding = FindAttachmentBinding(Name("LastFrameAccumulation")); + AZ_Error("TaaPass", m_lastFrameAccumulationBinding, "TaaPass requires a slot for LastFrameAccumulation."); + m_outputColorBinding = FindAttachmentBinding(Name("OutputColor")); + AZ_Error("TaaPass", m_outputColorBinding, "TaaPass requires a slot for OutputColor."); + + // Set up the attachment for last frame accumulation and output color if it's never been done to + // ensure SRG indices are set up correctly by the pass system. + if (m_lastFrameAccumulationBinding->m_attachment == nullptr) + { + m_lastFrameAccumulationBinding->SetAttachment(m_accumulationAttachments[0]); + m_outputColorBinding->SetAttachment(m_accumulationAttachments[1]); + } + + Base::BuildAttachmentsInternal(); + } + + void TaaPass::UpdateAttachmentImage(RPI::Ptr& attachment) + { + if (!attachment) + { + return; + } + + // update the image attachment descriptor to sync up size and format + attachment->Update(true); + RHI::ImageDescriptor& imageDesc = attachment->m_descriptor.m_image; + RPI::AttachmentImage* currentImage = azrtti_cast(attachment->m_importedResource.get()); + + if (attachment->m_importedResource && imageDesc.m_size == currentImage->GetDescriptor().m_size) + { + // If there's a resource already and the size didn't change, just keep using the old AttachmentImage. + return; + } + + Data::Instance pool = RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool(); + + // set the bind flags + imageDesc.m_bindFlags |= RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderReadWrite; + + // The ImageViewDescriptor must be specified to make sure the frame graph compiler doesn't treat this as a transient image. + RHI::ImageViewDescriptor viewDesc = RHI::ImageViewDescriptor::Create(imageDesc.m_format, 0, 0); + viewDesc.m_aspectFlags = RHI::ImageAspectFlags::Color; + viewDesc.m_overrideBindFlags = RHI::ImageBindFlags::ShaderReadWrite; + + // The full path name is needed for the attachment image so it's not deduplicated from accumulation images in different pipelines. + AZStd::string imageName = RPI::ConcatPassString(GetPathName(), attachment->m_path); + auto attachmentImage = RPI::AttachmentImage::Create(*pool.get(), imageDesc, Name(imageName), nullptr, &viewDesc); + + attachment->m_path = attachmentImage->GetAttachmentId(); + attachment->m_importedResource = attachmentImage; + } + + void TaaPass::SetupSubPixelOffsets(uint32_t haltonX, uint32_t haltonY, uint32_t length) + { + m_subPixelOffsets.resize(length); + HaltonSequence<2> sequence = HaltonSequence<2>({haltonX, haltonY}); + sequence.FillHaltonSequence(m_subPixelOffsets.begin(), m_subPixelOffsets.end()); + + // Adjust to the -1.0 to 1.0 range. This is done because the view needs offsets in clip + // space and is one less calculation that would need to be done in FrameBeginInternal() + AZStd::for_each(m_subPixelOffsets.begin(), m_subPixelOffsets.end(), + [](Offset& offset) + { + offset.m_xOffset = 2.0f * offset.m_xOffset - 1.0f; + offset.m_yOffset = 2.0f * offset.m_yOffset - 1.0f; + } + ); + } + + // Approximation of a Blackman Harris window function of width 3.3. + // https://en.wikipedia.org/wiki/Window_function#Blackman%E2%80%93Harris_window + static float BlackmanHarris(AZ::Vector2 uv) + { + return expf(-2.29f * (uv.GetX() * uv.GetX() + uv.GetY() * uv.GetY())); + } + + // Generates filter weights for the 3x3 neighborhood of a pixel. Since jitter positions are the + // same for every pixel we can calculate this once here and upload to the SRG. + // Jitter weights are based on a window function centered at the pixel center (we use Blackman-Harris). + // As the jitter position moves around, some neighborhood locations decrease in weight, and others + // increase in weight based on their distance from the center of the pixel. + void TaaPass::GenerateFilterWeights(AZ::Vector2 jitterOffset) + { + static const AZStd::array pixelOffsets = + { + // Center + Vector2(0.0f, 0.0f), + // Cross + Vector2( 1.0f, 0.0f), + Vector2( 0.0f, 1.0f), + Vector2(-1.0f, 0.0f), + Vector2( 0.0f, -1.0f), + // Diagonals + Vector2( 1.0f, 1.0f), + Vector2( 1.0f, -1.0f), + Vector2(-1.0f, 1.0f), + Vector2(-1.0f, -1.0f), + }; + + float sum = 0.0f; + for (uint32_t i = 0; i < 9; ++i) + { + m_filterWeights[i] = BlackmanHarris(pixelOffsets[i] + jitterOffset); + sum += m_filterWeights[i]; + } + + // Normalize the weight so the sum of all weights is 1.0. + float normalization = 1.0f / sum; + for (uint32_t i = 0; i < 9; ++i) + { + m_filterWeights[i] *= normalization; + } + } + +} // namespace AZ::Render diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.h new file mode 100644 index 0000000000..6133720691 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.h @@ -0,0 +1,105 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include + +namespace AZ::Render +{ + //! Custom data for the Taa Pass. + struct TaaPassData + : public RPI::ComputePassData + { + AZ_RTTI(TaaPassData, "{BCDF5C7D-7A78-4C69-A460-FA6899C3B960}", ComputePassData); + AZ_CLASS_ALLOCATOR(TaaPassData, SystemAllocator, 0); + + TaaPassData() = default; + virtual ~TaaPassData() = default; + + static void Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("NumJitterPositions", &TaaPassData::m_numJitterPositions) + ; + } + } + + uint32_t m_numJitterPositions = 8; + }; + + class TaaPass : public RPI::ComputePass + { + using Base = RPI::ComputePass; + AZ_RPI_PASS(TaaPass); + + public: + AZ_RTTI(AZ::Render::TaaPass, "{AB3BD4EA-33D7-477F-82B4-21DDFB517499}", Base); + AZ_CLASS_ALLOCATOR(TaaPass, SystemAllocator, 0); + virtual ~TaaPass() = default; + + /// Creates a TaaPass + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + private: + + TaaPass(const RPI::PassDescriptor& descriptor); + + // Scope producer functions... + void CompileResources(const RHI::FrameGraphCompileContext& context) override; + + // Pass behavior overrides... + void FrameBeginInternal(FramePrepareParams params) override; + void ResetInternal() override; + void BuildAttachmentsInternal() override; + + void UpdateAttachmentImage(RPI::Ptr& attachment); + + void SetupSubPixelOffsets(uint32_t haltonX, uint32_t haltonY, uint32_t length); + void GenerateFilterWeights(AZ::Vector2 jitterOffset); + + RHI::ShaderInputNameIndex m_outputIndex = "m_output"; + RHI::ShaderInputNameIndex m_lastFrameAccumulationIndex = "m_lastFrameAccumulation"; + RHI::ShaderInputNameIndex m_constantDataIndex = "m_constantData"; + + Data::Instance m_accumulationAttachments[2]; + + RPI::PassAttachmentBinding* m_inputColorBinding = nullptr; + RPI::PassAttachmentBinding* m_lastFrameAccumulationBinding = nullptr; + RPI::PassAttachmentBinding* m_outputColorBinding = nullptr; + + struct Offset + { + Offset() = default; + + // Constructor for implicit conversion from array output by HaltonSequence. + Offset(AZStd::array offsets) + : m_xOffset(offsets[0]) + , m_yOffset(offsets[1]) + {}; + + float m_xOffset = 0.0f; + float m_yOffset = 0.0f; + }; + + AZStd::array m_filterWeights = { 0.0f }; + + AZStd::vector m_subPixelOffsets; + uint32_t m_offsetIndex = 0; + + uint8_t m_accumulationOuptutIndex = 0; + + }; +} // namespace AZ::Render diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index a108fc82f5..a759de77fa 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -252,6 +252,8 @@ set(FILES Source/PostProcessing/SsaoPasses.h Source/PostProcessing/SubsurfaceScatteringPass.cpp Source/PostProcessing/SubsurfaceScatteringPass.h + Source/PostProcessing/TaaPass.h + Source/PostProcessing/TaaPass.cpp Source/RayTracing/RayTracingFeatureProcessor.h Source/RayTracing/RayTracingFeatureProcessor.cpp Source/RayTracing/RayTracingAccelerationStructurePass.cpp diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameGraphAttachmentInterface.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameGraphAttachmentInterface.h index c3194efbb6..af29079a97 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameGraphAttachmentInterface.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameGraphAttachmentInterface.h @@ -89,6 +89,12 @@ namespace AZ return m_attachmentDatabase.IsAttachmentValid(attachmentId); } + //! Returns the FrameAttachment for a given AttachmentId, or nullptr if not found. + const FrameAttachment* FindAttachment(const AttachmentId& attachmentId) const + { + return m_attachmentDatabase.FindAttachment(attachmentId); + } + //! Resolves an attachment id to a buffer descriptor. This is useful when accessing buffer information for //! an attachment that was declared in a different scope. //! \param attachmentId The attachment id used to lookup the descriptors. diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h index 1cba71ae7e..3d0a8cd3a8 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h @@ -184,8 +184,8 @@ namespace AZ //! Collect all different view tags from this pass virtual void GetPipelineViewTags(SortedPipelineViewTags& outTags) const; - //! Adds this pass' DrawListTags to the outDrawListMask. - virtual void GetViewDrawListInfo(RHI::DrawListMask& outDrawListMask, PassesByDrawList& outPassesByDrawList, const PipelineViewTag& viewTag) const; + //! Adds this pass' DrawListTags to the outDrawListMask. + virtual void GetViewDrawListInfo(RHI::DrawListMask& outDrawListMask, PassesByDrawList& outPassesByDrawList, const PipelineViewTag& viewTag) const; //! Check if the pass has a DrawListTag. Pass' DrawListTag can be used to filter draw items. virtual RHI::DrawListTag GetDrawListTag() const; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassAttachment.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassAttachment.h index 5509398f55..fd2a49a941 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassAttachment.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassAttachment.h @@ -52,7 +52,8 @@ namespace AZ const RHI::TransientBufferDescriptor GetTransientBufferDescriptor() const; //! Updates the size and format of this attachment using the sources below if specified - void Update(); + //! @param updateImportedAttachments - Imported attchments will only update if this is true. + void Update(bool updateImportedAttachments = false); //! Sets all formats to nearest device supported formats and warns if changes where made void ValidateDeviceFormats(const AZStd::vector& formatFallbacks, RHI::FormatCapabilities capabilities = RHI::FormatCapabilities::None); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h index e611ecf0d6..fabec8d896 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h @@ -88,6 +88,9 @@ namespace AZ //! Sets the viewToClip matrix and recalculates the other matrices void SetViewToClipMatrix(const AZ::Matrix4x4& viewToClip); + //! Sets a pixel offset on the view, usually used for jittering the camera for anti-aliasing techniques. + void SetClipSpaceOffset(float xOffset, float yOffset); + const AZ::Matrix4x4& GetWorldToViewMatrix() const; //! Use GetViewToWorldMatrix().GetTranslation() to get the camera's position. const AZ::Matrix4x4& GetViewToWorldMatrix() const; @@ -173,7 +176,6 @@ namespace AZ Matrix4x4 m_worldToViewMatrix; Matrix4x4 m_viewToWorldMatrix; Matrix4x4 m_viewToClipMatrix; - Matrix4x4 m_clipToViewMatrix; Matrix4x4 m_clipToWorldMatrix; // View's position in world space @@ -188,17 +190,15 @@ namespace AZ // Cached matrix to transform from world space to clip space Matrix4x4 m_worldToClipMatrix; - Matrix4x4 m_worldToClipPrevMatrix; + Matrix4x4 m_worldToViewPrevMatrix; + Matrix4x4 m_viewToClipPrevMatrix; + + // Clip space offset for camera jitter with taa + Vector2 m_clipSpaceOffset = Vector2(0.0f, 0.0f); // Flags whether view matrices are dirty which requires rebuild srg bool m_needBuildSrg = true; - // Following two bools form a delay circuit to update history of next frame - // if vp matrix is changed during current frame, this is required because - // view class doesn't contain subroutines called at the end of each frame - bool m_worldToClipMatrixChanged = true; - bool m_worldToClipPrevMatrixNeedsUpdate = false; - MatrixChangedEvent m_onWorldToClipMatrixChange; MatrixChangedEvent m_onWorldToViewMatrixChange; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp index 8d613eb2bb..1e230a7fc0 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -914,23 +914,40 @@ namespace AZ { // make sure to only import the resource one time RHI::AttachmentId attachmentId = attachment->GetAttachmentId(); - if (!attachmentDatabase.IsAttachmentValid(attachmentId)) + const RHI::FrameAttachment* currentAttachment = attachmentDatabase.FindAttachment(attachmentId); + + if (azrtti_istypeof(attachment->m_importedResource.get())) { - if (azrtti_istypeof(attachment->m_importedResource.get())) + Image* image = static_cast(attachment->m_importedResource.get()); + if (currentAttachment == nullptr) { - Image* image = static_cast(attachment->m_importedResource.get()); attachmentDatabase.ImportImage(attachmentId, image->GetRHIImage()); } - else if (azrtti_istypeof(attachment->m_importedResource.get())) + else + { + AZ_Assert(currentAttachment->GetResource() == image->GetRHIImage(), + "Importing image attachment named \"%s\" but a different attachment with the " + "same name already exists in the database.\n", attachmentId.GetCStr()); + } + } + else if (azrtti_istypeof(attachment->m_importedResource.get())) + { + Buffer* buffer = static_cast(attachment->m_importedResource.get()); + if (currentAttachment == nullptr) { - Buffer* buffer = static_cast(attachment->m_importedResource.get()); attachmentDatabase.ImportBuffer(attachmentId, buffer->GetRHIBuffer()); } else { - AZ_RPI_PASS_ERROR(false, "Can't import unknown resource type"); + AZ_Assert(currentAttachment->GetResource() == buffer->GetRHIBuffer(), + "Importing buffer attachment named \"%s\" but a different attachment with the " + "same name already exists in the database.\n", attachmentId.GetCStr()); } } + else + { + AZ_RPI_PASS_ERROR(false, "Can't import unknown resource type"); + } } } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassAttachment.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassAttachment.cpp index a5082c5ee6..3bd26a5906 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassAttachment.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassAttachment.cpp @@ -114,9 +114,9 @@ namespace AZ return RHI::TransientBufferDescriptor(GetAttachmentId(), m_descriptor.m_buffer); } - void PassAttachment::Update() + void PassAttachment::Update(bool updateImportedAttachments) { - if (m_descriptor.m_type == RHI::AttachmentType::Image && m_lifetime == RHI::AttachmentLifetimeType::Transient) + if (m_descriptor.m_type == RHI::AttachmentType::Image && (m_lifetime == RHI::AttachmentLifetimeType::Transient || updateImportedAttachments == true)) { if (m_settingFlags.m_getFormatFromPipeline && m_renderPipelineSource) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp index bd0e15fb2e..24dfcb7097 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp @@ -127,7 +127,6 @@ namespace AZ m_worldToViewMatrix = worldToView; m_worldToClipMatrix = m_viewToClipMatrix * m_worldToViewMatrix; - m_worldToClipMatrixChanged = true; m_onWorldToViewMatrixChange.Signal(m_worldToViewMatrix); m_onWorldToClipMatrixChange.Signal(m_worldToClipMatrix); @@ -166,8 +165,6 @@ namespace AZ m_worldToViewMatrix = m_viewToWorldMatrix.GetInverseFast(); m_worldToClipMatrix = m_viewToClipMatrix * m_worldToViewMatrix; - m_clipToWorldMatrix = m_viewToWorldMatrix * m_clipToViewMatrix; - m_worldToClipMatrixChanged = true; m_onWorldToViewMatrixChange.Signal(m_worldToViewMatrix); m_onWorldToClipMatrixChange.Signal(m_worldToClipMatrix); @@ -178,12 +175,8 @@ namespace AZ void View::SetViewToClipMatrix(const AZ::Matrix4x4& viewToClip) { m_viewToClipMatrix = viewToClip; - m_clipToViewMatrix = viewToClip.GetInverseFull(); m_worldToClipMatrix = m_viewToClipMatrix * m_worldToViewMatrix; - m_worldToClipMatrixChanged = true; - - m_clipToWorldMatrix = m_viewToWorldMatrix * m_clipToViewMatrix; // Update z depth constant simultaneously // zNear -> n, zFar -> f @@ -210,6 +203,12 @@ namespace AZ InvalidateSrg(); } + + void View::SetClipSpaceOffset(float xOffset, float yOffset) + { + m_clipSpaceOffset.Set(xOffset, yOffset); + InvalidateSrg(); + } const AZ::Matrix4x4& View::GetWorldToViewMatrix() const { @@ -368,36 +367,56 @@ namespace AZ void View::UpdateSrg() { - if (m_worldToClipPrevMatrixNeedsUpdate) + if (m_needBuildSrg) { - m_shaderResourceGroup->SetConstant(m_worldToClipPrevMatrixConstantIndex, m_worldToClipPrevMatrix); - m_worldToClipPrevMatrixNeedsUpdate = false; - } + if (m_clipSpaceOffset.IsZero()) + { + Matrix4x4 worldToClipPrevMatrix = m_viewToClipPrevMatrix * m_worldToViewPrevMatrix; + m_shaderResourceGroup->SetConstant(m_worldToClipPrevMatrixConstantIndex, worldToClipPrevMatrix); + m_shaderResourceGroup->SetConstant(m_viewProjectionMatrixConstantIndex, m_worldToClipMatrix); + m_shaderResourceGroup->SetConstant(m_projectionMatrixConstantIndex, m_viewToClipMatrix); + m_shaderResourceGroup->SetConstant(m_clipToWorldMatrixConstantIndex, m_clipToWorldMatrix); + m_shaderResourceGroup->SetConstant(m_projectionMatrixInverseConstantIndex, m_viewToClipMatrix.GetInverseFull()); + } + else + { + // Offset the current and previous frame clip matricies + Matrix4x4 offsetViewToClipMatrix = m_viewToClipMatrix; + offsetViewToClipMatrix.SetElement(0, 2, m_clipSpaceOffset.GetX()); + offsetViewToClipMatrix.SetElement(1, 2, m_clipSpaceOffset.GetY()); + + Matrix4x4 offsetViewToClipPrevMatrix = m_viewToClipPrevMatrix; + offsetViewToClipPrevMatrix.SetElement(0, 2, m_clipSpaceOffset.GetX()); + offsetViewToClipPrevMatrix.SetElement(1, 2, m_clipSpaceOffset.GetY()); + + // Build other matricies dependent on the view to clip matricies + Matrix4x4 offsetWorldToClipMatrix = offsetViewToClipMatrix * m_worldToViewMatrix; + Matrix4x4 offsetWorldToClipPrevMatrix = offsetViewToClipPrevMatrix * m_worldToViewPrevMatrix; + + Matrix4x4 offsetClipToViewMatrix = offsetViewToClipMatrix.GetInverseFull(); + Matrix4x4 offsetClipToWorldMatrix = m_viewToWorldMatrix * offsetClipToViewMatrix; + + m_shaderResourceGroup->SetConstant(m_worldToClipPrevMatrixConstantIndex, offsetWorldToClipPrevMatrix); + m_shaderResourceGroup->SetConstant(m_viewProjectionMatrixConstantIndex, offsetWorldToClipMatrix); + m_shaderResourceGroup->SetConstant(m_projectionMatrixConstantIndex, offsetViewToClipMatrix); + m_shaderResourceGroup->SetConstant(m_clipToWorldMatrixConstantIndex, offsetClipToWorldMatrix); + m_shaderResourceGroup->SetConstant(m_projectionMatrixInverseConstantIndex, offsetViewToClipMatrix.GetInverseFull()); + } - if (m_worldToClipMatrixChanged) - { - m_worldToClipPrevMatrix = m_worldToClipMatrix; - m_worldToClipPrevMatrixNeedsUpdate = true; - m_worldToClipMatrixChanged = false; - } + m_shaderResourceGroup->SetConstant(m_worldPositionConstantIndex, m_position); + m_shaderResourceGroup->SetConstant(m_viewMatrixConstantIndex, m_worldToViewMatrix); + m_shaderResourceGroup->SetConstant(m_viewMatrixInverseConstantIndex, m_worldToViewMatrix.GetInverseFull()); + m_shaderResourceGroup->SetConstant(m_zConstantsConstantIndex, m_nearZ_farZ_farZTimesNearZ_farZMinusNearZ); + m_shaderResourceGroup->SetConstant(m_unprojectionConstantsIndex, m_unprojectionConstants); - if (!m_needBuildSrg) - { - return; + m_shaderResourceGroup->Compile(); + m_needBuildSrg = false; } - m_shaderResourceGroup->SetConstant(m_worldPositionConstantIndex, m_position); - m_shaderResourceGroup->SetConstant(m_viewProjectionMatrixConstantIndex, m_worldToClipMatrix); - m_shaderResourceGroup->SetConstant(m_viewMatrixConstantIndex, m_worldToViewMatrix); - m_shaderResourceGroup->SetConstant(m_viewMatrixInverseConstantIndex, m_worldToViewMatrix.GetInverseFull()); - m_shaderResourceGroup->SetConstant(m_projectionMatrixConstantIndex, m_viewToClipMatrix); - m_shaderResourceGroup->SetConstant(m_projectionMatrixInverseConstantIndex, m_viewToClipMatrix.GetInverseFull()); - m_shaderResourceGroup->SetConstant(m_zConstantsConstantIndex, m_nearZ_farZ_farZTimesNearZ_farZMinusNearZ); - m_shaderResourceGroup->SetConstant(m_clipToWorldMatrixConstantIndex, m_clipToWorldMatrix); - m_shaderResourceGroup->SetConstant(m_unprojectionConstantsIndex, m_unprojectionConstants); - - m_shaderResourceGroup->Compile(); - m_needBuildSrg = false; + m_viewToClipPrevMatrix = m_viewToClipMatrix; + m_worldToViewPrevMatrix = m_worldToViewMatrix; + + m_clipSpaceOffset.Set(0); } void View::BeginCulling()