From 416f9aecf71faf3df82bfcabb23ba87f1b13d809 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Wed, 28 Jul 2021 15:09:38 +0200 Subject: [PATCH 01/38] Removed collision mesh aabb color from render option Signed-off-by: Benjamin Jillich --- .../Source/RenderPlugin/RenderOptions.cpp | 23 ------------------- .../Source/RenderPlugin/RenderOptions.h | 6 ----- 2 files changed, 29 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp index ef74cf466f..5e57d64167 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp @@ -55,7 +55,6 @@ namespace EMStudio const char* RenderOptions::s_nodeAABBColorOptionName = "nodeAABBColor"; const char* RenderOptions::s_staticAABBColorOptionName = "staticAABBColor"; const char* RenderOptions::s_meshAABBColorOptionName = "meshAABBColor"; - const char* RenderOptions::s_collisionMeshAABBColorOptionName = "collisionMeshAABBColor"; const char* RenderOptions::s_OBBsColorOptionName = "OBBsColor"; const char* RenderOptions::s_lineSkeletonColorOptionName = "lineSkeletonColor_v2"; const char* RenderOptions::s_skeletonColorOptionName = "skeletonColor"; @@ -108,7 +107,6 @@ namespace EMStudio , m_nodeAABBColor(1.0f, 0.0f, 0.0f, 1.0f) , m_staticAABBColor(0.0f, 0.7f, 0.7f, 1.0f) , m_meshAABBColor(0.0f, 0.0f, 0.7f, 1.0f) - , m_collisionMeshAABBColor(0.0f, 0.7f, 0.0f, 1.0f) , m_OBBsColor(1.0f, 1.0f, 0.0f, 1.0f) , m_lineSkeletonColor(0.33333f, 1.0f, 0.0f, 1.0f) , m_skeletonColor(0.19f, 0.58f, 0.19f, 1.0f) @@ -169,7 +167,6 @@ namespace EMStudio SetNodeAABBColor(other.GetNodeAABBColor()); SetStaticAABBColor(other.GetStaticAABBColor()); SetMeshAABBColor(other.GetMeshAABBColor()); - SetCollisionMeshAABBColor(other.GetCollisionMeshAABBColor()); SetOBBsColor(other.GetOBBsColor()); SetLineSkeletonColor(other.GetLineSkeletonColor()); SetSkeletonColor(other.GetSkeletonColor()); @@ -206,7 +203,6 @@ namespace EMStudio settings->setValue(s_nodeAABBColorOptionName, ColorToString(m_nodeAABBColor)); settings->setValue(s_staticAABBColorOptionName, ColorToString(m_staticAABBColor)); settings->setValue(s_meshAABBColorOptionName, ColorToString(m_meshAABBColor)); - settings->setValue(s_collisionMeshAABBColorOptionName, ColorToString(m_collisionMeshAABBColor)); settings->setValue(s_collisionMeshColorOptionName, ColorToString(m_collisionMeshColor)); settings->setValue(s_OBBsColorOptionName, ColorToString(m_OBBsColor)); settings->setValue(s_lineSkeletonColorOptionName, ColorToString(m_lineSkeletonColor)); @@ -275,7 +271,6 @@ namespace EMStudio options.m_nodeAABBColor = StringToColor(settings->value(s_nodeAABBColorOptionName, ColorToString(options.m_nodeAABBColor)).toString()); options.m_staticAABBColor = StringToColor(settings->value(s_staticAABBColorOptionName, ColorToString(options.m_staticAABBColor)).toString()); options.m_meshAABBColor = StringToColor(settings->value(s_meshAABBColorOptionName, ColorToString(options.m_meshAABBColor)).toString()); - options.m_collisionMeshAABBColor = StringToColor(settings->value(s_collisionMeshAABBColorOptionName, ColorToString(options.m_collisionMeshAABBColor)).toString()); options.m_collisionMeshColor = StringToColor(settings->value(s_collisionMeshColorOptionName, ColorToString(options.m_collisionMeshColor)).toString()); options.m_OBBsColor = StringToColor(settings->value(s_OBBsColorOptionName, ColorToString(options.m_OBBsColor)).toString()); options.m_lineSkeletonColor = StringToColor(settings->value(s_lineSkeletonColorOptionName, ColorToString(options.m_lineSkeletonColor)).toString()); @@ -393,7 +388,6 @@ namespace EMStudio ->Field(s_nodeAABBColorOptionName, &RenderOptions::m_nodeAABBColor) ->Field(s_staticAABBColorOptionName, &RenderOptions::m_staticAABBColor) ->Field(s_meshAABBColorOptionName, &RenderOptions::m_meshAABBColor) - ->Field(s_collisionMeshAABBColorOptionName, &RenderOptions::m_collisionMeshAABBColor) ->Field(s_OBBsColorOptionName, &RenderOptions::m_OBBsColor) ->Field(s_lineSkeletonColorOptionName, &RenderOptions::m_lineSkeletonColor) ->Field(s_skeletonColorOptionName, &RenderOptions::m_skeletonColor) @@ -552,9 +546,6 @@ namespace EMStudio ->DataElement(AZ::Edit::UIHandlers::Default, &RenderOptions::m_meshAABBColor, "Mesh based AABB color", "Color for the runtime-updated AABB calculated based on the deformed meshes.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &RenderOptions::OnMeshAABBColorChangedCallback) - ->DataElement(AZ::Edit::UIHandlers::Default, &RenderOptions::m_collisionMeshAABBColor, "CollisionMesh based AABB color", - "Color for the runtime-updated AABB calculated based on the deformed collision meshes.") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &RenderOptions::OnCollisionMeshAABBColorChangedCallback) ->DataElement(AZ::Edit::UIHandlers::Default, &RenderOptions::m_OBBsColor, "Joint OBB color", "Color used for the pre-calculated joint oriented bounding boxes.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &RenderOptions::OnOBBsColorChangedCallback) @@ -903,15 +894,6 @@ namespace EMStudio } } - void RenderOptions::SetCollisionMeshAABBColor(const AZ::Color& collisionMeshAABBColor) - { - if (!collisionMeshAABBColor.IsClose(m_collisionMeshAABBColor)) - { - m_collisionMeshAABBColor = collisionMeshAABBColor; - OnCollisionMeshAABBColorChangedCallback(); - } - } - void RenderOptions::SetOBBsColor(const AZ::Color& OBBsColor) { if (!OBBsColor.IsClose(m_OBBsColor)) @@ -1258,11 +1240,6 @@ namespace EMStudio PluginOptionsNotificationsBus::Event(s_meshAABBColorOptionName, &PluginOptionsNotificationsBus::Events::OnOptionChanged, s_meshAABBColorOptionName); } - void RenderOptions::OnCollisionMeshAABBColorChangedCallback() const - { - PluginOptionsNotificationsBus::Event(s_collisionMeshAABBColorOptionName, &PluginOptionsNotificationsBus::Events::OnOptionChanged, s_collisionMeshAABBColorOptionName); - } - void RenderOptions::OnOBBsColorChangedCallback() const { PluginOptionsNotificationsBus::Event(s_OBBsColorOptionName, &PluginOptionsNotificationsBus::Events::OnOptionChanged, s_OBBsColorOptionName); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h index 1df1a023c8..5e12cf935f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h @@ -59,7 +59,6 @@ namespace EMStudio static const char* s_nodeAABBColorOptionName; static const char* s_staticAABBColorOptionName; static const char* s_meshAABBColorOptionName; - static const char* s_collisionMeshAABBColorOptionName; static const char* s_OBBsColorOptionName; static const char* s_lineSkeletonColorOptionName; static const char* s_skeletonColorOptionName; @@ -191,9 +190,6 @@ namespace EMStudio AZ::Color GetMeshAABBColor() const { return m_meshAABBColor; } void SetMeshAABBColor(const AZ::Color& meshAABBColor); - AZ::Color GetCollisionMeshAABBColor() const { return m_collisionMeshAABBColor; } - void SetCollisionMeshAABBColor(const AZ::Color& collisionMeshAABBColor); - AZ::Color GetOBBsColor() const { return m_OBBsColor; } void SetOBBsColor(const AZ::Color& OBBsColor); @@ -303,7 +299,6 @@ namespace EMStudio void OnNodeAABBColorChangedCallback() const; void OnStaticAABBColorChangedCallback() const; void OnMeshAABBColorChangedCallback() const; - void OnCollisionMeshAABBColorChangedCallback() const; void OnOBBsColorChangedCallback() const; void OnLineSkeletonColorChangedCallback() const; void OnSkeletonColorChangedCallback() const; @@ -361,7 +356,6 @@ namespace EMStudio AZ::Color m_nodeAABBColor; AZ::Color m_staticAABBColor; AZ::Color m_meshAABBColor; - AZ::Color m_collisionMeshAABBColor; AZ::Color m_OBBsColor; AZ::Color m_lineSkeletonColor; AZ::Color m_skeletonColor; From e9718d9ce87ceb09c209777174ca46d37ef4a288 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Wed, 28 Jul 2021 15:30:34 +0200 Subject: [PATCH 02/38] Converted the EMFX Mesh to use AZ::Aabb instead of MCore::AABB Signed-off-by: Benjamin Jillich --- Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp | 9 +++------ Gems/EMotionFX/Code/EMotionFX/Source/Mesh.h | 3 ++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp index 51486c39b5..60ebd9a2dc 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.cpp @@ -1372,20 +1372,17 @@ namespace EMotionFX } - void Mesh::CalcAABB(MCore::AABB* outBoundingBox, const Transform& transform, uint32 vertexFrequency) + void Mesh::CalcAabb(AZ::Aabb* outBoundingBox, const Transform& transform, uint32 vertexFrequency) { MCORE_ASSERT(vertexFrequency >= 1); + *outBoundingBox = AZ::Aabb::CreateNull(); - // init the bounding box - outBoundingBox->Init(); - - // get the position data AZ::Vector3* positions = (AZ::Vector3*)FindVertexData(ATTRIB_POSITIONS); const uint32 numVerts = GetNumVertices(); for (uint32 i = 0; i < numVerts; i += vertexFrequency) { - outBoundingBox->Encapsulate(transform.TransformPoint(positions[i])); + outBoundingBox->AddPoint(transform.TransformPoint(positions[i])); } } diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.h b/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.h index f9a9f960ce..cb929dc7db 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Mesh.h @@ -9,6 +9,7 @@ #pragma once #include "EMotionFXConfig.h" +#include #include #include #include @@ -571,7 +572,7 @@ namespace EMotionFX * @param vertexFrequency This is the for loop increase counter value. A value of 1 means every vertex will be processed * while a value of 2 means every second vertex, etc. The value must be 1 or higher. */ - void CalcAABB(MCore::AABB* outBoundingBox, const Transform& transform, uint32 vertexFrequency = 1); + void CalcAabb(AZ::Aabb* outBoundingBox, const Transform& transform, uint32 vertexFrequency = 1); /** * The mesh type used to indicate if a mesh is either static, like a cube or building, cpu deformed, if it needs to be processed on the CPU, or GPU deformed if it can be processed fully on the GPU. From e80931185bc0aab051eca321f3b602dca1e5e0d6 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Wed, 28 Jul 2021 15:59:11 +0200 Subject: [PATCH 03/38] Ported RenderPlugin from MCore::AABB to AZ::AaBB * Containing functionality verified. * Zoom to joints * Normals scale multiplier * View closeup * Calculating the scene aabb * Rendering the actor instance aabbs * Selection aabb Signed-off-by: Benjamin Jillich --- .../Source/RenderPlugin/RenderPlugin.cpp | 80 +++++++++---------- .../Source/RenderPlugin/RenderPlugin.h | 2 +- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp index 182314f2a6..ab219b2b92 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp @@ -266,8 +266,7 @@ namespace EMStudio return; } - MCore::AABB aabb; - aabb.Init(); + AZ::Aabb aabb = AZ::Aabb::CreateNull(); const EMotionFX::Actor* actor = actorInstance->GetActor(); const EMotionFX::Skeleton* skeleton = actor->GetSkeleton(); @@ -276,21 +275,20 @@ namespace EMStudio for (const EMotionFX::Node* joint : joints) { const AZ::Vector3 jointPosition = pose->GetWorldSpaceTransform(joint->GetNodeIndex()).mPosition; - - aabb.Encapsulate(jointPosition); + aabb.AddPoint(jointPosition); const AZ::u32 childCount = joint->GetNumChildNodes(); for (AZ::u32 i = 0; i < childCount; ++i) { EMotionFX::Node* childJoint = skeleton->GetNode(joint->GetChildIndex(i)); const AZ::Vector3 childPosition = pose->GetWorldSpaceTransform(childJoint->GetNodeIndex()).mPosition; - aabb.Encapsulate(childPosition); + aabb.AddPoint(childPosition); } } - if (aabb.CheckIfIsValid()) + if (aabb.IsValid()) { - aabb.Widen(aabb.CalcRadius()); + aabb.Expand(AZ::Vector3(aabb.GetExtents().GetLength() * 0.5f)); bool isFollowModeActive = false; for (const RenderViewWidget* viewWidget : m_viewWidgets) @@ -619,35 +617,30 @@ namespace EMStudio EMotionFX::ActorInstance* actorInstance = EMotionFX::ActorInstance::Create(mActor); actorInstance->UpdateMeshDeformers(0.0f, true); - MCore::AABB aabb; - actorInstance->CalcMeshBasedAABB(0, &aabb); + AZ::Aabb aabb; + actorInstance->CalcMeshBasedAabb(0, &aabb); - if (aabb.CheckIfIsValid() == false) + if (!aabb.IsValid()) { - actorInstance->CalcNodeOBBBasedAABB(&aabb); + actorInstance->CalcNodeBasedAabb(&aabb); } - if (aabb.CheckIfIsValid() == false) - { - actorInstance->CalcNodeBasedAABB(&aabb); - } - - mCharacterHeight = aabb.CalcHeight(); + mCharacterHeight = aabb.GetExtents().GetZ(); mOffsetFromTrajectoryNode = aabb.GetMin().GetY() + (mCharacterHeight * 0.5f); actorInstance->Destroy(); // scale the normals down to 1% of the character size, that looks pretty nice on all models - mNormalsScaleMultiplier = aabb.CalcRadius() * 0.01f; + const float radius = AZ::Vector3(aabb.GetMax() - aabb.GetMin()).GetLength() * 0.5f; + mNormalsScaleMultiplier = radius * 0.01f; } // zoom to characters void RenderPlugin::ViewCloseup(bool selectedInstancesOnly, RenderWidget* renderWidget, float flightTime) { - const MCore::AABB sceneAABB = GetSceneAABB(selectedInstancesOnly); - - if (sceneAABB.CheckIfIsValid()) + const AZ::Aabb sceneAabb = GetSceneAabb(selectedInstancesOnly); + if (sceneAabb.IsValid()) { // in case the given view widget parameter is nullptr apply it on all view widgets if (!renderWidget) @@ -655,13 +648,13 @@ namespace EMStudio for (RenderViewWidget* viewWidget : m_viewWidgets) { RenderWidget* current = viewWidget->GetRenderWidget(); - current->ViewCloseup(sceneAABB, flightTime); + current->ViewCloseup(sceneAabb, flightTime); } } // only apply it to the given view widget else { - renderWidget->ViewCloseup(sceneAABB, flightTime); + renderWidget->ViewCloseup(sceneAabb, flightTime); } } } @@ -882,9 +875,9 @@ namespace EMStudio // get the AABB containing all actor instances in the scene - MCore::AABB RenderPlugin::GetSceneAABB(bool selectedInstancesOnly) + AZ::Aabb RenderPlugin::GetSceneAabb(bool selectedInstancesOnly) { - MCore::AABB finalAABB; + AZ::Aabb finalAabb = AZ::Aabb::CreateNull(); CommandSystem::SelectionList& selection = GetCommandManager()->GetCurrentSelection(); if (mUpdateCallback) @@ -922,20 +915,28 @@ namespace EMStudio } // get the mesh based AABB - MCore::AABB aabb; - actorInstance->CalcMeshBasedAABB(0, &aabb); + AZ::Aabb aabb; + actorInstance->CalcMeshBasedAabb(0, &aabb); // get the node based AABB - if (aabb.CheckIfIsValid() == false) + if (!aabb.IsValid()) { - actorInstance->CalcNodeBasedAABB(&aabb); + actorInstance->CalcNodeBasedAabb(&aabb); } // make sure the actor instance is covered in our global bounding box - finalAABB.Encapsulate(aabb); + if (aabb.IsValid()) + { + finalAabb.AddAabb(aabb); + } } - return finalAABB; + if (!finalAabb.IsValid()) + { + finalAabb.Set(AZ::Vector3(-1.0f, -1.0f, 0.0f), AZ::Vector3(1.0f, 1.0f, 0.0f)); + } + + return finalAabb; } @@ -1155,9 +1156,8 @@ namespace EMStudio settings.mNodeBasedColor = renderOptions->GetNodeAABBColor(); settings.mStaticBasedColor = renderOptions->GetStaticAABBColor(); settings.mMeshBasedColor = renderOptions->GetMeshAABBColor(); - settings.mCollisionMeshBasedColor = renderOptions->GetCollisionMeshAABBColor(); - renderUtil->RenderAABBs(actorInstance, settings); + renderUtil->RenderAabbs(actorInstance, settings); } if (widget->GetRenderFlag(RenderViewWidget::RENDER_OBB)) @@ -1169,10 +1169,10 @@ namespace EMStudio const MCommon::Camera* camera = widget->GetRenderWidget()->GetCamera(); const AZ::Vector3& cameraPos = camera->GetPosition(); - MCore::AABB aabb; - actorInstance->CalcNodeBasedAABB(&aabb); - const AZ::Vector3 aabbMid = aabb.CalcMiddle(); - const float aabbRadius = aabb.CalcRadius(); + AZ::Aabb aabb; + actorInstance->CalcNodeBasedAabb(&aabb); + const AZ::Vector3 aabbMid = aabb.GetCenter(); + const float aabbRadius = AZ::Vector3(aabb.GetMax() - aabb.GetMin()).GetLength() * 0.5f; const float camDistance = fabs((cameraPos - aabbMid).GetLength()); // Avoid rendering too big joint spheres when zooming in onto a joint. @@ -1185,7 +1185,7 @@ namespace EMStudio // Scale the joint spheres based on the character's extents, to avoid really large joint spheres // on small characters and too small spheres on large characters. static const float baseRadius = 0.005f; - const float jointSphereRadius = aabb.CalcRadius() * scaleMultiplier * baseRadius; + const float jointSphereRadius = aabbRadius * scaleMultiplier * baseRadius; renderUtil->RenderSimpleSkeleton(actorInstance, &visibleJointIndices, &selectedJointIndices, renderOptions->GetLineSkeletonColor(), renderOptions->GetSelectedObjectColor(), jointSphereRadius); @@ -1268,8 +1268,8 @@ namespace EMStudio // render the selection if (renderOptions->GetRenderSelectionBox() && EMotionFX::GetActorManager().GetNumActorInstances() != 1 && GetCurrentSelection()->CheckIfHasActorInstance(actorInstance)) { - MCore::AABB aabb = actorInstance->GetAABB(); - aabb.Widen(aabb.CalcRadius() * 0.005f); + AZ::Aabb aabb = actorInstance->GetAabb(); + aabb.Expand(AZ::Vector3(0.005f)); renderUtil->RenderSelection(aabb, renderOptions->GetSelectionColor()); } diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.h index 68cabbefa3..7fa0a880d0 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.h @@ -154,7 +154,7 @@ namespace EMStudio MCORE_INLINE CommandSystem::SelectionList* GetCurrentSelection() const { return mCurrentSelection; } MCORE_INLINE MCommon::RenderUtil* GetRenderUtil() const { return mRenderUtil; } - MCore::AABB GetSceneAABB(bool selectedInstancesOnly); + AZ::Aabb GetSceneAabb(bool selectedInstancesOnly); MCommon::RenderUtil::TrajectoryTracePath* FindTracePath(EMotionFX::ActorInstance* actorInstance); void ResetSelectedTrajectoryPaths(); From e651f255772b11d01f1210178e6f567981dba36c Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Mon, 2 Aug 2021 13:17:34 +0200 Subject: [PATCH 04/38] Ported the render plugin, render update callback and render widget Signed-off-by: Benjamin Jillich --- .../Rendering/Common/RotateManipulator.h | 2 +- .../Rendering/Common/TranslateManipulator.h | 2 +- .../Source/BlendTreeRotationMath2Node.cpp | 1 - .../Source/RenderPlugin/RenderPlugin.cpp | 2 +- .../RenderPlugin/RenderUpdateCallback.cpp | 7 +++---- .../Source/RenderPlugin/RenderWidget.cpp | 18 ++++++++---------- .../Source/RenderPlugin/RenderWidget.h | 12 ++++-------- 7 files changed, 18 insertions(+), 26 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RotateManipulator.h b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RotateManipulator.h index c6d719ae7c..5443bad219 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RotateManipulator.h +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RotateManipulator.h @@ -8,7 +8,7 @@ #pragma once -// include the Core system +#include #include #include #include diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/TranslateManipulator.h b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/TranslateManipulator.h index 362f6c19be..7bc0bacfe0 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/TranslateManipulator.h +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/TranslateManipulator.h @@ -9,7 +9,7 @@ #ifndef __MCOMMON_TRANSLATEMANIPULATOR_H #define __MCOMMON_TRANSLATEMANIPULATOR_H -// include the Core system +#include #include #include #include "MCommonConfig.h" diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeRotationMath2Node.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeRotationMath2Node.cpp index d01a6e5f0d..1f15333561 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeRotationMath2Node.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeRotationMath2Node.cpp @@ -105,7 +105,6 @@ namespace EMotionFX } // If both x and y inputs have connections - //MCore::Quaternion x = MCore::AzQuatToEmfxQuat(m_defaultValue); AZ::Quaternion x = m_defaultValue; AZ::Quaternion y = x; if (mConnections.size() == 2) diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp index ab219b2b92..e4663750ce 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp @@ -915,7 +915,7 @@ namespace EMStudio } // get the mesh based AABB - AZ::Aabb aabb; + AZ::Aabb aabb = AZ::Aabb::CreateNull(); actorInstance->CalcMeshBasedAabb(0, &aabb); // get the node based AABB diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderUpdateCallback.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderUpdateCallback.cpp index 4c032cb2ea..fed309d2e5 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderUpdateCallback.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderUpdateCallback.cpp @@ -170,9 +170,8 @@ namespace EMStudio settings.mNodeBasedColor = renderOptions->GetNodeAABBColor(); settings.mStaticBasedColor = renderOptions->GetStaticAABBColor(); settings.mMeshBasedColor = renderOptions->GetMeshAABBColor(); - settings.mCollisionMeshBasedColor = renderOptions->GetCollisionMeshAABBColor(); - renderUtil->RenderAABBs(actorInstance, settings); + renderUtil->RenderAabbs(actorInstance, settings); } if (widget->GetRenderFlag(RenderViewWidget::RENDER_OBB)) @@ -260,8 +259,8 @@ namespace EMStudio // render the selection if (renderOptions->GetRenderSelectionBox() && EMotionFX::GetActorManager().GetNumActorInstances() != 1 && mPlugin->GetCurrentSelection()->CheckIfHasActorInstance(actorInstance)) { - MCore::AABB aabb = actorInstance->GetAABB(); - aabb.Widen(aabb.CalcRadius() * 0.005f); + AZ::Aabb aabb = actorInstance->GetAabb(); + aabb.Expand(aabb.GetExtents() * 0.005f); renderUtil->RenderSelection(aabb, renderOptions->GetSelectionColor()); } diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.cpp index c74f693765..fb0ce4fdbf 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.cpp @@ -72,9 +72,8 @@ namespace EMStudio } // start view closeup flight - void RenderWidget::ViewCloseup(const MCore::AABB& aabb, float flightTime, uint32 viewCloseupWaiting) + void RenderWidget::ViewCloseup(const AZ::Aabb& aabb, float flightTime, uint32 viewCloseupWaiting) { - //LogError("ViewCloseup: AABB: Pos=(%.3f, %.3f, %.3f), Width=%.3f, Height=%.3f, Depth=%.3f", aabb.CalcMiddle().x, aabb.CalcMiddle().y, aabb.CalcMiddle().z, aabb.CalcWidth(), aabb.CalcHeight(), aabb.CalcDepth()); mViewCloseupWaiting = viewCloseupWaiting; mViewCloseupAABB = aabb; mViewCloseupFlightTime = flightTime; @@ -82,9 +81,8 @@ namespace EMStudio void RenderWidget::ViewCloseup(bool selectedInstancesOnly, float flightTime, uint32 viewCloseupWaiting) { - //LogError("ViewCloseup: AABB: Pos=(%.3f, %.3f, %.3f), Width=%.3f, Height=%.3f, Depth=%.3f", aabb.CalcMiddle().x, aabb.CalcMiddle().y, aabb.CalcMiddle().z, aabb.CalcWidth(), aabb.CalcHeight(), aabb.CalcDepth()); mViewCloseupWaiting = viewCloseupWaiting; - mViewCloseupAABB = mPlugin->GetSceneAABB(selectedInstancesOnly); + mViewCloseupAABB = mPlugin->GetSceneAabb(selectedInstancesOnly); mViewCloseupFlightTime = flightTime; } @@ -603,14 +601,15 @@ namespace EMStudio if (actor->CheckIfHasMeshes(actorInstance->GetLODLevel()) == false) { // calculate the node based AABB - MCore::AABB box; - actorInstance->CalcNodeBasedAABB(&box); + AZ::Aabb box; + actorInstance->CalcNodeBasedAabb(&box); // render the aabb - if (box.CheckIfIsValid()) + if (box.IsValid()) { + const MCore::AABB mcoreAabb(box.GetMin(), box.GetMax()); AZ::Vector3 ii, n; - if (ray.Intersects(box, &ii, &n)) + if (ray.Intersects(mcoreAabb, &ii, &n)) { selectedActorInstance = actorInstance; oldIntersectionPoint = ii; @@ -1169,8 +1168,7 @@ namespace EMStudio mViewCloseupWaiting--; if (mViewCloseupWaiting == 0) { - mCamera->ViewCloseup(mViewCloseupAABB, mViewCloseupFlightTime); - //mViewCloseupWaiting = 0; + mCamera->ViewCloseup(MCore::AABB(mViewCloseupAABB.GetMin(), mViewCloseupAABB.GetMax()), mViewCloseupFlightTime); } } diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.h index b6776d43a4..10722f744a 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.h @@ -6,11 +6,10 @@ * */ -#ifndef __EMSTUDIO_RENDERWIDGET_H -#define __EMSTUDIO_RENDERWIDGET_H +#pragma once -// #if !defined(Q_MOC_RUN) +#include #include #include "../EMStudioConfig.h" #include @@ -117,7 +116,7 @@ namespace EMStudio MCORE_INLINE MCommon::Camera* GetCamera() const { return mCamera; } MCORE_INLINE CameraMode GetCameraMode() const { return mCameraMode; } MCORE_INLINE void SetSkipFollowCalcs(bool skipFollowCalcs) { mSkipFollowCalcs = skipFollowCalcs; } - void ViewCloseup(const MCore::AABB& aabb, float flightTime, uint32 viewCloseupWaiting = 5); + void ViewCloseup(const AZ::Aabb& aabb, float flightTime, uint32 viewCloseupWaiting = 5); void ViewCloseup(bool selectedInstancesOnly, float flightTime, uint32 viewCloseupWaiting = 5); void SwitchCamera(CameraMode mode); @@ -161,7 +160,7 @@ namespace EMStudio // used for closeup camera flights uint32 mViewCloseupWaiting; - MCore::AABB mViewCloseupAABB; + AZ::Aabb mViewCloseupAABB; float mViewCloseupFlightTime; // manipulator helper data @@ -175,6 +174,3 @@ namespace EMStudio int32 mPixelsMovedSinceRightClick; }; } // namespace EMStudio - - -#endif From b840b24de2c018f0a50f4e580bad54dde07d88c2 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Mon, 2 Aug 2021 14:38:20 +0200 Subject: [PATCH 05/38] Ported the actor and a few other places * Fixed a bug with updating the static aabb for actors. It called that before the mesh was loaded resulting in an invalid aabb. * Ported a few more places to AZ::Aabb from MCore::AABB Signed-off-by: Benjamin Jillich --- .../Code/Source/AtomActorInstance.cpp | 9 ++--- .../CommandSystem/Source/ActorCommands.cpp | 9 ++--- .../ExporterLib/Exporter/NodeExport.cpp | 12 +++--- .../EMotionFX/Code/EMotionFX/Source/Actor.cpp | 38 +++++++++---------- Gems/EMotionFX/Code/EMotionFX/Source/Actor.h | 10 ++--- 5 files changed, 37 insertions(+), 41 deletions(-) diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index 706ed27a4d..f9c0242b77 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -83,10 +83,10 @@ namespace AZ // Update RenderActorInstance world bounding box // The bounding box is moving with the actor instance. // The entity and actor transforms are kept in sync already. - m_worldAABB = AZ::Aabb::CreateFromMinMax(m_actorInstance->GetAABB().GetMin(), m_actorInstance->GetAABB().GetMax()); + m_worldAABB = m_actorInstance->GetAabb(); // Update RenderActorInstance local bounding box - // NB: computing the local bbox from the world bbox makes the local bbox artifically larger than it should be + // NB: computing the local bbox from the world bbox makes the local bbox artificially larger than it should be // instead EMFX should support getting the local bbox from the actor instance directly m_localAABB = m_worldAABB.GetTransformedAabb(m_transformInterface->GetWorldTM().GetInverse()); @@ -107,9 +107,8 @@ namespace AZ { if (debugOptions.m_drawAABB) { - const MCore::AABB emfxAabb = m_actorInstance->GetAABB(); - const AZ::Aabb azAabb = AZ::Aabb::CreateFromMinMax(emfxAabb.GetMin(), emfxAabb.GetMax()); - auxGeom->DrawAabb(azAabb, AZ::Color(0.0f, 1.0f, 1.0f, 1.0f), RPI::AuxGeomDraw::DrawStyle::Line); + const AZ::Aabb& aabb = m_actorInstance->GetAabb(); + auxGeom->DrawAabb(aabb, AZ::Color(0.0f, 1.0f, 1.0f, 1.0f), RPI::AuxGeomDraw::DrawStyle::Line); } if (debugOptions.m_drawSkeleton) diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ActorCommands.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ActorCommands.cpp index a21b618b91..52a4ec6a0b 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ActorCommands.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ActorCommands.cpp @@ -1063,11 +1063,10 @@ namespace CommandSystem continue; } - MCore::AABB newAABB; - actorInstance->SetStaticBasedAABB(actor->GetStaticAABB()); // this is needed as the CalcStaticBasedAABB uses the current AABB as starting point - actorInstance->CalcStaticBasedAABB(&newAABB); - actorInstance->SetStaticBasedAABB(newAABB); - //actorInstance->UpdateVisualizeScale(); + actorInstance->SetStaticBasedAabb(actor->GetStaticAabb()); // this is needed as the CalcStaticBasedAabb uses the current AABB as starting point + AZ::Aabb newAabb; + actorInstance->CalcStaticBasedAabb(&newAabb); + actorInstance->SetStaticBasedAabb(newAabb); const float factor = (float)MCore::Distance::GetConversionFactor(beforeUnitType, targetUnitType); actorInstance->SetVisualizeScale(actorInstance->GetVisualizeScale() * factor); diff --git a/Gems/EMotionFX/Code/EMotionFX/Exporters/ExporterLib/Exporter/NodeExport.cpp b/Gems/EMotionFX/Code/EMotionFX/Exporters/ExporterLib/Exporter/NodeExport.cpp index a9010fb524..c66c3ca8ef 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Exporters/ExporterLib/Exporter/NodeExport.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Exporters/ExporterLib/Exporter/NodeExport.cpp @@ -184,12 +184,12 @@ namespace ExporterLib EMotionFX::FileFormat::Actor_Nodes nodesChunk; nodesChunk.mNumNodes = numNodes; nodesChunk.mNumRootNodes = actor->GetSkeleton()->GetNumRootNodes(); - nodesChunk.mStaticBoxMin.mX = actor->GetStaticAABB().GetMin().GetX(); - nodesChunk.mStaticBoxMin.mY = actor->GetStaticAABB().GetMin().GetY(); - nodesChunk.mStaticBoxMin.mZ = actor->GetStaticAABB().GetMin().GetZ(); - nodesChunk.mStaticBoxMax.mX = actor->GetStaticAABB().GetMax().GetX(); - nodesChunk.mStaticBoxMax.mY = actor->GetStaticAABB().GetMax().GetY(); - nodesChunk.mStaticBoxMax.mZ = actor->GetStaticAABB().GetMax().GetZ(); + nodesChunk.mStaticBoxMin.mX = actor->GetStaticAabb().GetMin().GetX(); + nodesChunk.mStaticBoxMin.mY = actor->GetStaticAabb().GetMin().GetY(); + nodesChunk.mStaticBoxMin.mZ = actor->GetStaticAabb().GetMin().GetZ(); + nodesChunk.mStaticBoxMax.mX = actor->GetStaticAabb().GetMax().GetX(); + nodesChunk.mStaticBoxMax.mY = actor->GetStaticAabb().GetMax().GetY(); + nodesChunk.mStaticBoxMax.mZ = actor->GetStaticAabb().GetMax().GetZ(); // endian conversion and write it ConvertUnsignedInt(&nodesChunk.mNumNodes, targetEndianType); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp index 973d8eb460..c55509e817 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp @@ -97,6 +97,7 @@ namespace EMotionFX mID = MCore::GetIDGenerator().GenerateID(); mUnitType = GetEMotionFX().GetUnitType(); mFileUnitType = mUnitType; + m_staticAabb = AZ::Aabb::CreateNull(); mUsedForVisualization = false; mDirtyFlag = false; @@ -148,7 +149,7 @@ namespace EMotionFX result->mMotionExtractionNode = mMotionExtractionNode; result->mUnitType = mUnitType; result->mFileUnitType = mFileUnitType; - result->mStaticAABB = mStaticAABB; + result->m_staticAabb = m_staticAabb; result->mRetargetRootNode = mRetargetRootNode; result->mInvBindPoseTransforms = mInvBindPoseTransforms; result->m_optimizeSkeleton = m_optimizeSkeleton; @@ -1405,10 +1406,6 @@ namespace EMotionFX m_simulatedObjectSetup->InitAfterLoad(this); - // build the static axis aligned bounding box by creating an actor instance (needed to perform cpu skinning mesh deforms and mesh scaling etc) - // then copy it over to the actor - UpdateStaticAABB(); - // rescale all content if needed if (convertUnitType) { @@ -1526,6 +1523,10 @@ namespace EMotionFX mMorphSetups[i] = nullptr; } } + + // build the static axis aligned bounding box by creating an actor instance (needed to perform cpu skinning mesh deforms and mesh scaling etc) + // then copy it over to the actor + UpdateStaticAabb(); } m_isReady = true; @@ -1534,16 +1535,13 @@ namespace EMotionFX } // update the static AABB (very heavy as it has to create an actor instance, update mesh deformers, calculate the mesh based bounds etc) - void Actor::UpdateStaticAABB() + void Actor::UpdateStaticAabb() { - if (!mStaticAABB.CheckIfIsValid()) - { - ActorInstance* actorInstance = ActorInstance::Create(this, nullptr, mThreadIndex); - //actorInstance->UpdateMeshDeformers(0.0f); - //actorInstance->UpdateStaticBasedAABBDimensions(); - actorInstance->GetStaticBasedAABB(&mStaticAABB); - actorInstance->Destroy(); - } + ActorInstance* actorInstance = ActorInstance::Create(this, nullptr, mThreadIndex); + actorInstance->UpdateMeshDeformers(0.0f); + actorInstance->UpdateStaticBasedAabbDimensions(); + actorInstance->GetStaticBasedAabb(&m_staticAabb); + actorInstance->Destroy(); } @@ -2206,14 +2204,14 @@ namespace EMotionFX #endif } - const MCore::AABB& Actor::GetStaticAABB() const + const AZ::Aabb& Actor::GetStaticAabb() const { - return mStaticAABB; + return m_staticAabb; } - void Actor::SetStaticAABB(const MCore::AABB& box) + void Actor::SetStaticAabb(const AZ::Aabb& aabb) { - mStaticAABB = box; + m_staticAabb = aabb; } //--------------------------------- @@ -2422,8 +2420,8 @@ namespace EMotionFX } // update static aabb - mStaticAABB.SetMin(mStaticAABB.GetMin() * scaleFactor); - mStaticAABB.SetMax(mStaticAABB.GetMax() * scaleFactor); + m_staticAabb.SetMin(m_staticAabb.GetMin() * scaleFactor); + m_staticAabb.SetMax(m_staticAabb.GetMax() * scaleFactor); // update mesh data for all LOD levels const uint32 numLODs = GetNumLODLevels(); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h index 6c3ab6bf29..53cbe7e05a 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h @@ -16,11 +16,11 @@ #include #include #include +#include #include #include // include MCore related files -#include #include #include #include @@ -781,9 +781,9 @@ namespace EMotionFX void ResizeTransformData(); void CopyTransformsFrom(const Actor* other); - const MCore::AABB& GetStaticAABB() const; - void SetStaticAABB(const MCore::AABB& box); - void UpdateStaticAABB(); // VERY heavy operation, you shouldn't call this ever (internally creates an actor instance, updates mesh deformers, calcs a mesh based aabb, destroys the actor instance again) + const AZ::Aabb& GetStaticAabb() const; + void SetStaticAabb(const AZ::Aabb& aabb); + void UpdateStaticAabb(); // VERY heavy operation, you shouldn't call this ever (internally creates an actor instance, updates mesh deformers, calcs a mesh based aabb, destroys the actor instance again) void SetThreadIndex(uint32 index) { mThreadIndex = index; } uint32 GetThreadIndex() const { return mThreadIndex; } @@ -985,7 +985,7 @@ namespace EMotionFX uint32 mRetargetRootNode; /**< The retarget root node, which controls the height displacement of the character. This is most likely the hip or pelvis node. */ uint32 mID; /**< The unique identification number for the actor. */ uint32 mThreadIndex; /**< The thread number we are running on, which is a value starting at 0, up to the number of threads in the job system. */ - MCore::AABB mStaticAABB; /**< The static AABB. */ + AZ::Aabb m_staticAabb; /**< The static AABB. */ bool mDirtyFlag; /**< The dirty flag which indicates whether the user has made changes to the actor since the last file save operation. */ bool mUsedForVisualization; /**< Indicates if the actor is used for visualization specific things and is not used as a normal in-game actor. */ bool m_optimizeSkeleton; /**< Indicates if we should perform/ */ From f9d5a4a93b661f77363e17b1b4b0b3625a317776 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Tue, 3 Aug 2021 16:06:51 +0200 Subject: [PATCH 06/38] Ported MCommon::RenderUtil * Removed collision mesh based AABB rendering. * Ported to AZ::Aabb * Reduced the number of triangles used for rendering default spheres for joints to improve rendering times. Signed-off-by: Benjamin Jillich --- .../EMotionFX/Rendering/Common/RenderUtil.cpp | 73 +++++++------------ .../EMotionFX/Rendering/Common/RenderUtil.h | 24 +++--- 2 files changed, 37 insertions(+), 60 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp index 6301b0d7e3..36bce97f88 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp @@ -198,7 +198,7 @@ namespace MCommon // render the current bounding box of the given actor instance - void RenderUtil::RenderAABB(const MCore::AABB& box, const MCore::RGBAColor& color, bool directlyRender) + void RenderUtil::RenderAabb(const AZ::Aabb& box, const MCore::RGBAColor& color, bool directlyRender) { AZ::Vector3 min = box.GetMin(); AZ::Vector3 max = box.GetMax(); @@ -238,12 +238,12 @@ namespace MCommon // render selection gizmo around the given AABB - void RenderUtil::RenderSelection(const MCore::AABB& box, const MCore::RGBAColor& color, bool directlyRender) + void RenderUtil::RenderSelection(const AZ::Aabb& box, const MCore::RGBAColor& color, bool directlyRender) { - //const Vector3 center = box.CalcMiddle(); - const AZ::Vector3 min = box.GetMin();// + (box.GetMin()-center).Normalized()*0.005f; - const AZ::Vector3 max = box.GetMax();// + (box.GetMax()-center).Normalized()*0.005f; - const float scale = box.CalcRadius() * 0.1f; + const AZ::Vector3 min = box.GetMin(); + const AZ::Vector3 max = box.GetMax(); + const float radius = AZ::Vector3(box.GetMax() - box.GetMin()).GetLength() * 0.5f; + const float scale = radius * 0.1f; const AZ::Vector3 up = AZ::Vector3(0.0f, 1.0f, 0.0f) * scale; const AZ::Vector3 right = AZ::Vector3(1.0f, 0.0f, 0.0f) * scale; const AZ::Vector3 front = AZ::Vector3(0.0f, 0.0f, 1.0f) * scale; @@ -304,46 +304,29 @@ namespace MCommon { mNodeBasedAABB = true; mMeshBasedAABB = true; - mCollisionMeshBasedAABB = true; mStaticBasedAABB = true; mStaticBasedColor = MCore::RGBAColor(0.0f, 0.7f, 0.7f); mNodeBasedColor = MCore::RGBAColor(1.0f, 0.0f, 0.0f); - mCollisionMeshBasedColor = MCore::RGBAColor(0.0f, 0.7f, 0.0f); mMeshBasedColor = MCore::RGBAColor(0.0f, 0.0f, 0.7f); } // render the given types of AABBs of a actor instance - void RenderUtil::RenderAABBs(EMotionFX::ActorInstance* actorInstance, const AABBRenderSettings& renderSettings, bool directlyRender) + void RenderUtil::RenderAabbs(EMotionFX::ActorInstance* actorInstance, const AABBRenderSettings& renderSettings, bool directlyRender) { - // get the current LOD level const uint32 lodLevel = actorInstance->GetLODLevel(); - // handle the collision mesh based AABB - if (renderSettings.mCollisionMeshBasedAABB) - { - // calculate the collision mesh based AABB - MCore::AABB box; - actorInstance->CalcCollisionMeshBasedAABB(lodLevel, &box); - - // render the aabb - if (box.CheckIfIsValid()) - { - RenderAABB(box, renderSettings.mCollisionMeshBasedColor); - } - } - // handle the node based AABB if (renderSettings.mNodeBasedAABB) { // calculate the node based AABB - MCore::AABB box; - actorInstance->CalcNodeBasedAABB(&box); + AZ::Aabb box; + actorInstance->CalcNodeBasedAabb(&box); // render the aabb - if (box.CheckIfIsValid()) + if (box.IsValid()) { - RenderAABB(box, renderSettings.mNodeBasedColor); + RenderAabb(box, renderSettings.mNodeBasedColor); } } @@ -351,26 +334,26 @@ namespace MCommon if (renderSettings.mMeshBasedAABB) { // calculate the mesh based AABB - MCore::AABB box; - actorInstance->CalcMeshBasedAABB(lodLevel, &box); + AZ::Aabb box; + actorInstance->CalcMeshBasedAabb(lodLevel, &box); // render the aabb - if (box.CheckIfIsValid()) + if (box.IsValid()) { - RenderAABB(box, renderSettings.mMeshBasedColor); + RenderAabb(box, renderSettings.mMeshBasedColor); } } if (renderSettings.mStaticBasedAABB) { // calculate the static based AABB - MCore::AABB box; - actorInstance->CalcStaticBasedAABB(&box); + AZ::Aabb box; + actorInstance->CalcStaticBasedAabb(&box); // render the aabb - if (box.CheckIfIsValid()) + if (box.IsValid()) { - RenderAABB(box, renderSettings.mStaticBasedColor); + RenderAabb(box, renderSettings.mStaticBasedColor); } } @@ -1639,7 +1622,7 @@ namespace MCommon // 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 - MCore::AABB aabb; + AZ::Aabb aabb = AZ::Aabb::CreateNull(); AZ::Vector3 intersectionPoint; const AZ::Plane groundPlane = AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3(0.0f, 0.0f, 1.0f), AZ::Vector3::CreateZero()); for (AZ::u32 i = 0; i < 4; ++i) @@ -1649,7 +1632,7 @@ namespace MCommon corners[i] = intersectionPoint; } - aabb.Encapsulate(corners[i]); + aabb.AddPoint(corners[i]); } // set the grid start and end values @@ -1665,9 +1648,9 @@ namespace MCommon // get aabb which includes all actor instances - MCore::AABB RenderUtil::CalcSceneAABB() + AZ::Aabb RenderUtil::CalcSceneAabb() { - MCore::AABB finalAABB; + AZ::Aabb finalAABB = AZ::Aabb::CreateNull(); // get the number of actor instances and iterate through them const uint32 numActorInstances = EMotionFX::GetActorManager().GetNumActorInstances(); @@ -1685,17 +1668,17 @@ namespace MCommon actorInstance->UpdateMeshDeformers(0.0f); // get the mesh based bounding box - MCore::AABB boundingBox; - actorInstance->CalcMeshBasedAABB(actorInstance->GetLODLevel(), &boundingBox); + AZ::Aabb boundingBox; + actorInstance->CalcMeshBasedAabb(actorInstance->GetLODLevel(), &boundingBox); // in case there aren't any meshes, use the node based bounding box - if (boundingBox.CheckIfIsValid() == false) + if (!boundingBox.IsValid()) { - actorInstance->CalcNodeBasedAABB(&boundingBox); + actorInstance->CalcNodeBasedAabb(&boundingBox); } // make sure the actor instance is covered in our world bounding box - finalAABB.Encapsulate(boundingBox); + finalAABB.AddAabb(boundingBox); } return finalAABB; diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h index 8b1cd30b6c..5c5d7bbef8 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h @@ -6,10 +6,9 @@ * */ -#ifndef __MCOMMON_RENDERUTIL_H -#define __MCOMMON_RENDERUTIL_H +#pragma once -// include required headers +#include #include #include #include @@ -111,7 +110,7 @@ namespace MCommon * Render tangents and bitangents of the mesh. * @param mesh A pointer to the mesh which will be rendered. * @param worldTM The world space transformation matrix of the node to which the given mesh belongs to. - * @param scale This parameter controls the length of the tangents and bitangentss. The default size of the tangents and bitangents is one unit. + * @param scale This parameter controls the length of the tangents and bitangents. The default size of the tangents and bitangents is one unit. * @param colorTangents The color of the tangents. * @param mirroredBitangentColor The color of the mirrored bitangents, so the ones that have a w value of -1. * @param colorBitangent The color of the face bitangents. @@ -127,7 +126,7 @@ namespace MCommon * @param directlyRender Will call the RenderLines() function internally in case it is set to true. If false * you have to make sure to call RenderLines() manually at the end of your custom render frame function. */ - void RenderAABB(const MCore::AABB& box, const MCore::RGBAColor& color, bool directlyRender = false); + void RenderAabb(const AZ::Aabb& box, const MCore::RGBAColor& color, bool directlyRender = false); /** * Render a selection gizmo around the given axis aligned bounding box. @@ -136,7 +135,7 @@ namespace MCommon * @param directlyRender Will call the RenderLines() function internally in case it is set to true. If false * you have to make sure to call RenderLines() manually at the end of your custom render frame function. */ - void RenderSelection(const MCore::AABB& box, const MCore::RGBAColor& color, bool directlyRender = false); + void RenderSelection(const AZ::Aabb& box, const MCore::RGBAColor& color, bool directlyRender = false); /** * The render settings used to enable the different AABB types of an actor instance. @@ -152,11 +151,9 @@ namespace MCommon bool mNodeBasedAABB; /**< Enable in case you want to render the node based AABB (default=true). */ bool mMeshBasedAABB; /**< Enable in case you want to render the mesh based AABB (default=true). */ - bool mCollisionMeshBasedAABB; /**< Enable in case you want to render the collision mesh based AABB (default=true). */ bool mStaticBasedAABB; /**< Enable in case you want to render the static based AABB (default=true). */ MCore::RGBAColor mNodeBasedColor; /**< The color of the node based AABB. */ MCore::RGBAColor mMeshBasedColor; /**< The color of the mesh based AABB. */ - MCore::RGBAColor mCollisionMeshBasedColor; /**< The color of the collision mesh based AABB. */ MCore::RGBAColor mStaticBasedColor; /**< The color of the static based AABB. */ }; @@ -168,7 +165,7 @@ namespace MCommon * @param directlyRender Will call the RenderLines() function internally in case it is set to true. If false * you have to make sure to call RenderLines() manually at the end of your custom render frame function. */ - void RenderAABBs(EMotionFX::ActorInstance* actorInstance, const AABBRenderSettings& renderSettings = AABBRenderSettings(), bool directlyRender = false); + void RenderAabbs(EMotionFX::ActorInstance* actorInstance, const AABBRenderSettings& renderSettings = AABBRenderSettings(), bool directlyRender = false); /** * Render OBB for all enabled nodes inside the actor instance. @@ -615,7 +612,7 @@ namespace MCommon * Calculate the aabb which includes all actor instances. * @return The aabb which includes all actor instances. */ - MCore::AABB CalcSceneAABB(); + AZ::Aabb CalcSceneAabb(); struct TrajectoryPathParticle { @@ -722,7 +719,7 @@ namespace MCommon /** * Change the shape of a given arrow head util mesh. This method can be used to adjust an already allocated arrow head util mesh. - * For example this can be usedful if you need to change the radius or the height of an arrow head. + * For example this can be useful if you need to change the radius or the height of an arrow head. * @param mesh A pointer to the arrow head util mesh. Note that this mesh has to be created using CreateArrowHead(). * @param height The height of the arrow head from the base to the head. * @param radius The radius of the base of the arrow head. @@ -735,7 +732,7 @@ namespace MCommon * @param radius The radius of the sphere. * @return A pointer to the newly created util sphere mesh. */ - static UtilMesh* CreateSphere(float radius, uint32 numSegments = 8); + static UtilMesh* CreateSphere(float radius, uint32 numSegments = 5); /** * Create an util mesh we can use to render cubes. @@ -831,6 +828,3 @@ namespace MCommon static uint32 mNumMaxTriangleVertices; /**< The maximum capacity of the triangle vertex buffer */ }; } // namespace MCommon - - -#endif From aab8ab97065b53d874ad9ebbc9251da5ae1f3b2a Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Tue, 3 Aug 2021 17:04:26 +0200 Subject: [PATCH 07/38] Removed OBBs from Actor including the node infos Signed-off-by: Benjamin Jillich --- .../Pipeline/RCExt/Actor/ActorBuilder.cpp | 2 +- .../EMotionFX/Code/EMotionFX/Source/Actor.cpp | 115 +----------------- Gems/EMotionFX/Code/EMotionFX/Source/Actor.h | 74 +---------- .../EMotionFX/Code/Tests/AnimGraphFixture.cpp | 2 +- .../Code/Tests/MorphTargetRuntimeTests.cpp | 2 +- .../Code/Tests/UI/CanMorphManyShapes.cpp | 2 +- 6 files changed, 6 insertions(+), 191 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorBuilder.cpp b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorBuilder.cpp index 35d38dd359..0c5916f684 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorBuilder.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorBuilder.cpp @@ -272,7 +272,7 @@ namespace EMotionFX // Post create actor actor->SetUnitType(MCore::Distance::UNITTYPE_METERS); actor->SetFileUnitType(MCore::Distance::UNITTYPE_METERS); - actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*generateOBBs=*/false, /*convertUnitType=*/false); + actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*convertUnitType=*/false); // Only enable joints that are used for skinning (and their parents). // On top of that, enable all joints marked as critical joints. diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp index c55509e817..62bd8b32cb 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp @@ -42,7 +42,6 @@ #include #include #include -#include #include @@ -50,11 +49,6 @@ namespace EMotionFX { AZ_CLASS_ALLOCATOR_IMPL(Actor, ActorAllocator, 0) - Actor::NodeInfo::NodeInfo() - { - mOBB.Init(); - } - Actor::LODLevel::LODLevel() { } @@ -188,7 +182,6 @@ namespace EMotionFX result->mSkeleton = mSkeleton->Clone(); // clone lod data - result->mNodeInfos = mNodeInfos; const uint32 numNodes = mSkeleton->GetNumNodes(); const size_t numLodLevels = m_meshLodData.m_lodLevels.size(); @@ -998,18 +991,6 @@ namespace EMotionFX } } - // update the bounding volumes - void Actor::UpdateNodeBindPoseOBBs(uint32 lodLevel) - { - // for all nodes - const uint32 numNodes = mSkeleton->GetNumNodes(); - for (uint32 i = 0; i < numNodes; ++i) - { - CalcOBBFromBindPose(lodLevel, i); - } - } - - // remove all node groups void Actor::RemoveAllNodeGroups() { @@ -1353,9 +1334,8 @@ namespace EMotionFX } } - // post init - void Actor::PostCreateInit(bool makeGeomLodsCompatibleWithSkeletalLODs, bool generateOBBs, bool convertUnitType) + void Actor::PostCreateInit(bool makeGeomLodsCompatibleWithSkeletalLODs, bool convertUnitType) { if (mThreadIndex == MCORE_INVALIDINDEX32) { @@ -1388,11 +1368,6 @@ namespace EMotionFX mSkeleton->GetBindPose()->ForceUpdateFullModelSpacePose(); mSkeleton->GetBindPose()->ZeroMorphWeights(); - if (generateOBBs) - { - UpdateNodeBindPoseOBBs(0); - } - if (!GetHasMirrorInfo()) { AllocateNodeMirrorInfos(); @@ -1883,7 +1858,6 @@ namespace EMotionFX void Actor::SetNumNodes(uint32 numNodes) { mSkeleton->SetNumNodes(numNodes); - mNodeInfos.resize(numNodes); AZStd::vector& lodLevels = m_meshLodData.m_lodLevels; for (LODLevel& lodLevel : lodLevels) @@ -1901,7 +1875,6 @@ namespace EMotionFX mSkeleton->GetBindPose()->LinkToActor(this, Pose::FLAG_LOCALTRANSFORMREADY, false); // initialize the LOD data - mNodeInfos.emplace_back(); AZStd::vector& lodLevels = m_meshLodData.m_lodLevels; for (LODLevel& lodLevel : lodLevels) { @@ -1932,7 +1905,6 @@ namespace EMotionFX void Actor::RemoveNode(uint32 nr, bool delMem) { mSkeleton->RemoveNode(nr, delMem); - mNodeInfos.erase(mNodeInfos.begin() + nr); AZStd::vector& lodLevels = m_meshLodData.m_lodLevels; for (LODLevel& lodLevel : lodLevels) @@ -1944,7 +1916,6 @@ namespace EMotionFX void Actor::DeleteAllNodes() { mSkeleton->RemoveAllNodes(); - mNodeInfos.clear(); AZStd::vector& lodLevels = m_meshLodData.m_lodLevels; for (LODLevel& lodLevel : lodLevels) @@ -2263,82 +2234,6 @@ namespace EMotionFX return (stack->CheckIfHasDeformerOfType(SoftSkinDeformer::TYPE_ID) || stack->CheckIfHasDeformerOfType(DualQuatSkinDeformer::TYPE_ID)); } - - // calculate the OBB for a given node - void Actor::CalcOBBFromBindPose(uint32 lodLevel, uint32 nodeIndex) - { - AZStd::vector points; - - // if there is a mesh - Mesh* mesh = GetMesh(lodLevel, nodeIndex); - if (mesh) - { - // if the mesh is not skinned - if (mesh->FindSharedVertexAttributeLayer(SkinningInfoVertexAttributeLayer::TYPE_ID) == nullptr) - { - mesh->ExtractOriginalVertexPositions(points); - } - } - else // there is no mesh, so maybe this is a bone - { - const Transform& invBindPoseTransform = GetInverseBindPoseTransform(nodeIndex); - - // for all nodes inside the actor where this node belongs to - const uint32 numNodes = mSkeleton->GetNumNodes(); - for (uint32 n = 0; n < numNodes; ++n) - { - Mesh* loopMesh = GetMesh(lodLevel, n); - if (loopMesh == nullptr) - { - continue; - } - - // get the vertex positions in bind pose - const uint32 numVerts = loopMesh->GetNumVertices(); - points.reserve(numVerts * 2); - AZ::Vector3* positions = (AZ::Vector3*)loopMesh->FindOriginalVertexData(Mesh::ATTRIB_POSITIONS); - - SkinningInfoVertexAttributeLayer* skinLayer = (SkinningInfoVertexAttributeLayer*)loopMesh->FindSharedVertexAttributeLayer(SkinningInfoVertexAttributeLayer::TYPE_ID); - if (skinLayer) - { - // iterate over all skinning influences and see if this node number is used - // if so, add it to the list of points - const uint32* orgVertices = (uint32*)loopMesh->FindVertexData(Mesh::ATTRIB_ORGVTXNUMBERS); - for (uint32 v = 0; v < numVerts; ++v) - { - // get the original vertex number - const uint32 orgVtx = orgVertices[v]; - - // for all skinning influences for this vertex - const size_t numInfluences = skinLayer->GetNumInfluences(orgVtx); - for (size_t i = 0; i < numInfluences; ++i) - { - // get the node used by this influence - const uint32 nodeNr = skinLayer->GetInfluence(orgVtx, i)->GetNodeNr(); - - // if this is the same node as we are updating the bounds for, add the vertex position to the list - if (nodeNr == nodeIndex) - { - const AZ::Vector3 tempPos(positions[v]); - points.emplace_back(invBindPoseTransform.TransformPoint(tempPos)); - } - } // for all influences - } // for all vertices - } // if there is skinning info - } // for all nodes - } - - // init from the set of points - if (!points.empty()) - { - GetNodeOBB(nodeIndex).InitFromPoints(&points[0], static_cast(points.size())); - } - else - { - GetNodeOBB(nodeIndex).Init(); - } - } - // remove the mesh for a given node in a given LOD void Actor::RemoveNodeMeshForLOD(uint32 lodLevel, uint32 nodeIndex, bool destroyMesh) { @@ -2411,14 +2306,6 @@ namespace EMotionFX mInvBindPoseTransforms[i] = bindPose->GetModelSpaceTransform(i).Inversed(); } - // update node obbs - for (uint32 i = 0; i < numNodes; ++i) - { - MCore::OBB& box = GetNodeOBB(i); - box.SetExtents(box.GetExtents() * scaleFactor); - box.SetCenter(box.GetCenter() * scaleFactor); - } - // update static aabb m_staticAabb.SetMin(m_staticAabb.GetMin() * scaleFactor); m_staticAabb.SetMax(m_staticAabb.GetMax() * scaleFactor); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h index 53cbe7e05a..1c1173a9a2 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h @@ -24,7 +24,6 @@ #include #include #include -#include #include // include required headers @@ -564,17 +563,6 @@ namespace EMotionFX */ void SetMorphSetup(uint32 lodLevel, MorphSetup* setup); - /** - * Update the oriented bounding volumes (OBB) of all the nodes inside this actor. - * This is a very heavy calculation and must NOT be performed on a per-frame basis but only as pre-process step. - * The OBBs of the nodes are already being calculated at export time, so you shouldn't really need to use this method. - * Only when the bind pose geometry has changed you can update the node OBBs by calling this method. - * For more information about how the bounds are calculated please see the Node::GetOBB() and Node::CalcOBBFromBindPose() methods. - * The calculations performed by this method are automatically spread over multiple threads to improve the performance. - * @param lodLevel The geometry LOD level to use while calculating the object oriented bounds per node. - */ - void UpdateNodeBindPoseOBBs(uint32 lodLevel); - /** * Get the number of node groups inside this actor object. * @result The number of node groups. @@ -758,7 +746,7 @@ namespace EMotionFX void MakeGeomLODsCompatibleWithSkeletalLODs(); void ReinitializeMeshDeformers(); - void PostCreateInit(bool makeGeomLodsCompatibleWithSkeletalLODs = true, bool generateOBBs = true, bool convertUnitType = true); + void PostCreateInit(bool makeGeomLodsCompatibleWithSkeletalLODs = true, bool convertUnitType = true); void AutoDetectMirrorAxes(); const MCore::Array& GetNodeMirrorInfos() const; @@ -808,57 +796,6 @@ namespace EMotionFX bool CheckIfHasMorphDeformer(uint32 lodLevel, uint32 nodeIndex) const; bool CheckIfHasSkinningDeformer(uint32 lodLevel, uint32 nodeIndex) const; - /** - * Calculate the object oriented box for a given LOD level. - * This will try to fit the tightest bounding box around the mesh of a node. - * If the node has no mesh and acts as bone inside skinning deformations the resulting box will contain - * all the vertices that are influenced by this given node/bone. - * Calculating this box is already done at export time. But you can use this to recalculate it if the mesh data changed. - * This method is relatively slow and not meant for per-frame calculations but only for preprocessing. - * You can use the GetOBB() method to retrieve the calculated box at any time. - * Nodes that do not have a mesh and not act as bone will have invalid OBB bounds, as they have no volume. You can check whether - * this is the case or not by using the MCore::OBB::IsValid() method. - * The box is stored in local space of the node. - * @param lodLevel The geometry LOD level to generate the OBBs from. - * @param nodeIndex The node to calculate the OBB for. - */ - void CalcOBBFromBindPose(uint32 lodLevel, uint32 nodeIndex); - - /** - * Get the object oriented bounding box for this node. - * The box is in local space. In order to convert it into world space you have to multiply the corner points of the box - * with the world space matrix of this node. - * Nodes that do not have a mesh and do not act as bone will have invalid bounds. You can use the MCore::OBB::CheckIfIsValid() method to check if - * the bounds are valid bounds or not. If it is not, then it means there was nothing to calculate the box from. - * Object Oriented Boxes for the nodes are calculated at export time by using the Actor::UpdateNodeBindPoseOBBs() and Node::CalcOBBFromBindPose() methods. - * @param nodeIndex The index of the node to get the OBB for. - * @result The object oriented bounding box that has been calculated before already. - */ - MCore::OBB& GetNodeOBB(uint32 nodeIndex) { return mNodeInfos[nodeIndex].mOBB; } - - /** - * Get the object oriented bounding box for this node. - * The box is in local space. In order to convert it into world space you have to multiply the corner points of the box - * with the world space matrix of this node. - * Nodes that do not have a mesh and do not act as bone will have invalid bounds. You can use the MCore::OBB::CheckIfIsValid() method to check if - * the bounds are valid bounds or not. If it is not, then it means there was nothing to calculate the box from. - * Object Oriented Boxes for the nodes are calculated at export time by using the Actor::UpdateNodeBindPoseOBBs() and Node::CalcOBBFromBindPose() methods. - * @param nodeIndex The index of the node to get the OBB for. - * @result The object oriented bounding box that has been calculated before already. - */ - const MCore::OBB& GetNodeOBB(uint32 nodeIndex) const { return mNodeInfos[nodeIndex].mOBB; } - - /** - * Set the object oriented bounding box for this node. - * The box is in local space. In order to convert it into world space you have to multiply the corner points of the box - * with the world space matrix of this node. - * Nodes that do not have a mesh and do not act as bone will have invalid bounds. You can use the MCore::OBB::CheckIfIsValid() method to check if - * the bounds are valid bounds or not. If it is not, then it means there was nothing to calculate the box from. - * @param nodeIndex The index of the node to set the OBB for. - * @param obb The object oriented bounding box that has been calculated before already. - */ - void SetNodeOBB(uint32 nodeIndex, const MCore::OBB& obb) { mNodeInfos[nodeIndex].mOBB = obb; } - void RemoveNodeMeshForLOD(uint32 lodLevel, uint32 nodeIndex, bool destroyMesh = true); void SetNumNodes(uint32 numNodes); @@ -917,14 +854,6 @@ namespace EMotionFX Node* FindJointByMeshName(const AZStd::string_view meshName) const; - // per node info (shared between lods) - struct EMFX_API NodeInfo - { - MCore::OBB mOBB; - - NodeInfo(); - }; - // data per node, per lod struct EMFX_API NodeLODInfo { @@ -968,7 +897,6 @@ namespace EMotionFX Skeleton* mSkeleton; /**< The skeleton, containing the nodes and bind pose. */ MCore::Array mDependencies; /**< The dependencies on other actors (shared meshes and transforms). */ - AZStd::vector mNodeInfos; /**< The per node info, shared between lods. */ AZStd::string mName; /**< The name of the actor. */ AZStd::string mFileName; /**< The filename of the actor. */ MCore::Array mNodeMirrorInfos; /**< The array of node mirror info. */ diff --git a/Gems/EMotionFX/Code/Tests/AnimGraphFixture.cpp b/Gems/EMotionFX/Code/Tests/AnimGraphFixture.cpp index 31c5455571..ab9d91046b 100644 --- a/Gems/EMotionFX/Code/Tests/AnimGraphFixture.cpp +++ b/Gems/EMotionFX/Code/Tests/AnimGraphFixture.cpp @@ -45,7 +45,7 @@ namespace EMotionFX ConstructActor(); ASSERT_TRUE(m_actor) << "Construct actor did not build a valid actor."; m_actor->ResizeTransformData(); - m_actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/ false, /*generateOBBs=*/ false, /*convertUnitType=*/ false); + m_actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/ false, /*convertUnitType=*/ false); } { m_motionSet = aznew MotionSet("testMotionSet"); diff --git a/Gems/EMotionFX/Code/Tests/MorphTargetRuntimeTests.cpp b/Gems/EMotionFX/Code/Tests/MorphTargetRuntimeTests.cpp index 0f022b72ef..24afc7af47 100644 --- a/Gems/EMotionFX/Code/Tests/MorphTargetRuntimeTests.cpp +++ b/Gems/EMotionFX/Code/Tests/MorphTargetRuntimeTests.cpp @@ -84,7 +84,7 @@ namespace EMotionFX // Without this call, the bind pose does not know about newly added // morph target (mMorphWeights.GetLength() == 0) m_actor->ResizeTransformData(); - m_actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*generateOBBs=*/false, /*convertUnitType=*/false); + m_actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*convertUnitType=*/false); m_animGraph = AZStd::make_unique(); diff --git a/Gems/EMotionFX/Code/Tests/UI/CanMorphManyShapes.cpp b/Gems/EMotionFX/Code/Tests/UI/CanMorphManyShapes.cpp index 978532505b..ba24c501ab 100644 --- a/Gems/EMotionFX/Code/Tests/UI/CanMorphManyShapes.cpp +++ b/Gems/EMotionFX/Code/Tests/UI/CanMorphManyShapes.cpp @@ -68,7 +68,7 @@ namespace EMotionFX // Without this call, the bind pose does not know about newly added morph target (mMorphWeights.GetLength() == 0) m_actor->ResizeTransformData(); - m_actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*generateOBBs=*/false, /*convertUnitType=*/false); + m_actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*convertUnitType=*/false); m_animGraph = AZStd::make_unique(); From b335285e19aae79fb0535211ab2e4713f739f30a Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Tue, 3 Aug 2021 17:13:19 +0200 Subject: [PATCH 08/38] Removed OBB rendering helpers and color options (was already hidden from the UI) Signed-off-by: Benjamin Jillich --- .../EMotionFX/Rendering/Common/RenderUtil.cpp | 71 ------------------- .../EMotionFX/Rendering/Common/RenderUtil.h | 12 ---- Gems/EMotionFX/Code/EMotionFX/Source/Node.cpp | 1 - .../Source/RenderPlugin/RenderOptions.cpp | 23 ------ .../Source/RenderPlugin/RenderOptions.h | 6 -- .../Source/RenderPlugin/RenderPlugin.cpp | 4 -- .../RenderPlugin/RenderUpdateCallback.cpp | 4 -- .../Source/RenderPlugin/RenderViewWidget.cpp | 3 - .../Source/RenderPlugin/RenderViewWidget.h | 1 - 9 files changed, 125 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp index 36bce97f88..8464cb2b2a 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp @@ -403,77 +403,6 @@ namespace MCommon } } - - // render object orientated bounding boxes for all enabled nodes inside the actor instance - void RenderUtil::RenderOBBs(EMotionFX::ActorInstance* actorInstance, const AZStd::unordered_set* visibleJointIndices, const AZStd::unordered_set* selectedJointIndices, const MCore::RGBAColor& color, const MCore::RGBAColor& selectedColor, bool directlyRender) - { - AZ::Vector3 p[8]; - - // get the actor it is an instance from - const EMotionFX::Actor* actor = actorInstance->GetActor(); - const EMotionFX::Skeleton* skeleton = actor->GetSkeleton(); - const EMotionFX::Pose* pose = actorInstance->GetTransformData()->GetCurrentPose(); - - // iterate through all enabled nodes - MCore::RGBAColor tempColor; - const uint32 numEnabled = actorInstance->GetNumEnabledNodes(); - for (uint32 i = 0; i < numEnabled; ++i) - { - const EMotionFX::Node* joint = skeleton->GetNode(actorInstance->GetEnabledNode(i)); - const AZ::u32 jointIndex = joint->GetNodeIndex(); - - if (!visibleJointIndices || visibleJointIndices->empty() || - (visibleJointIndices->find(jointIndex) != visibleJointIndices->end())) - { - const MCore::OBB& obb = actor->GetNodeOBB(jointIndex); - EMotionFX::Transform worldTransform = pose->GetWorldSpaceTransform(jointIndex); - - // skip the OBB if it isn't valid - if (obb.CheckIfIsValid() == false) - { - continue; - } - - // check if the current bone is selected and set the color according to it - if (selectedJointIndices && selectedJointIndices->find(jointIndex) != selectedJointIndices->end()) - { - tempColor = selectedColor; - } - else - { - tempColor = color; - } - - obb.CalcCornerPoints(p); - for (uint32 a = 0; a < 8; a++) - { - p[a] = worldTransform.TransformPoint(p[a]); - } - - // render - RenderLine(p[0], p[1], tempColor); - RenderLine(p[1], p[2], tempColor); - RenderLine(p[2], p[3], tempColor); - RenderLine(p[0], p[3], tempColor); - - RenderLine(p[1], p[5], tempColor); - RenderLine(p[3], p[7], tempColor); - RenderLine(p[2], p[6], tempColor); - RenderLine(p[0], p[4], tempColor); - - RenderLine(p[4], p[5], tempColor); - RenderLine(p[4], p[7], tempColor); - RenderLine(p[6], p[7], tempColor); - RenderLine(p[6], p[5], tempColor); - } - } - - if (directlyRender) - { - RenderLines(); - } - } - // render wireframe mesh void RenderUtil::RenderWireframe(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM, const MCore::RGBAColor& color, bool directlyRender, float offsetScale) { diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h index 5c5d7bbef8..f63d41e812 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.h @@ -167,18 +167,6 @@ namespace MCommon */ void RenderAabbs(EMotionFX::ActorInstance* actorInstance, const AABBRenderSettings& renderSettings = AABBRenderSettings(), bool directlyRender = false); - /** - * Render OBB for all enabled nodes inside the actor instance. - * @param actorInstance A pointer to the actor instance which will be rendered. - * @param[in] visibleJointIndices List of visible joint indices. nullptr in case all joints should be rendered. - * @param[in] selectedJointIndices List of selected joint indices. nullptr in case selection should not be considered. - * @param[in] color The color of the OBBs. - * @param[in] selectedColor The color of the selected OBBs. - * @param[in] directlyRender Will call the RenderLines() function internally in case it is set to true. If false - * you have to make sure to call RenderLines() manually at the end of your custom render frame function. - */ - void RenderOBBs(EMotionFX::ActorInstance* actorInstance, const AZStd::unordered_set* visibleJointIndices = nullptr, const AZStd::unordered_set* selectedJointIndices = nullptr, const MCore::RGBAColor& color = MCore::RGBAColor(1.0f, 1.0f, 0.0f, 1.0f), const MCore::RGBAColor& selectedColor = MCore::RGBAColor(1.0f, 0.647f, 0.0f), bool directlyRender = false); - /** * Render a simple line based skeleton for all enabled nodes of the actor instance. * @param[in] actorInstance A pointer to the actor instance which will be rendered. diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Node.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Node.cpp index 2aa6b796ab..8ab7fa540d 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Node.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Node.cpp @@ -98,7 +98,6 @@ namespace EMotionFX result->mChildIndices = mChildIndices; //result->mImportanceFactor = mImportanceFactor; result->mNodeFlags = mNodeFlags; - result->mOBB = mOBB; result->mSemanticNameID = mSemanticNameID; // copy the node attributes diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp index 5e57d64167..274edd36ce 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp @@ -55,7 +55,6 @@ namespace EMStudio const char* RenderOptions::s_nodeAABBColorOptionName = "nodeAABBColor"; const char* RenderOptions::s_staticAABBColorOptionName = "staticAABBColor"; const char* RenderOptions::s_meshAABBColorOptionName = "meshAABBColor"; - const char* RenderOptions::s_OBBsColorOptionName = "OBBsColor"; const char* RenderOptions::s_lineSkeletonColorOptionName = "lineSkeletonColor_v2"; const char* RenderOptions::s_skeletonColorOptionName = "skeletonColor"; const char* RenderOptions::s_selectionColorOptionName = "selectionColor"; @@ -107,7 +106,6 @@ namespace EMStudio , m_nodeAABBColor(1.0f, 0.0f, 0.0f, 1.0f) , m_staticAABBColor(0.0f, 0.7f, 0.7f, 1.0f) , m_meshAABBColor(0.0f, 0.0f, 0.7f, 1.0f) - , m_OBBsColor(1.0f, 1.0f, 0.0f, 1.0f) , m_lineSkeletonColor(0.33333f, 1.0f, 0.0f, 1.0f) , m_skeletonColor(0.19f, 0.58f, 0.19f, 1.0f) , m_selectionColor(1.0f, 1.0f, 1.0f, 1.0f) @@ -167,7 +165,6 @@ namespace EMStudio SetNodeAABBColor(other.GetNodeAABBColor()); SetStaticAABBColor(other.GetStaticAABBColor()); SetMeshAABBColor(other.GetMeshAABBColor()); - SetOBBsColor(other.GetOBBsColor()); SetLineSkeletonColor(other.GetLineSkeletonColor()); SetSkeletonColor(other.GetSkeletonColor()); SetSelectionColor(other.GetSelectionColor()); @@ -204,7 +201,6 @@ namespace EMStudio settings->setValue(s_staticAABBColorOptionName, ColorToString(m_staticAABBColor)); settings->setValue(s_meshAABBColorOptionName, ColorToString(m_meshAABBColor)); settings->setValue(s_collisionMeshColorOptionName, ColorToString(m_collisionMeshColor)); - settings->setValue(s_OBBsColorOptionName, ColorToString(m_OBBsColor)); settings->setValue(s_lineSkeletonColorOptionName, ColorToString(m_lineSkeletonColor)); settings->setValue(s_skeletonColorOptionName, ColorToString(m_skeletonColor)); settings->setValue(s_selectionColorOptionName, ColorToString(m_selectionColor)); @@ -272,7 +268,6 @@ namespace EMStudio options.m_staticAABBColor = StringToColor(settings->value(s_staticAABBColorOptionName, ColorToString(options.m_staticAABBColor)).toString()); options.m_meshAABBColor = StringToColor(settings->value(s_meshAABBColorOptionName, ColorToString(options.m_meshAABBColor)).toString()); options.m_collisionMeshColor = StringToColor(settings->value(s_collisionMeshColorOptionName, ColorToString(options.m_collisionMeshColor)).toString()); - options.m_OBBsColor = StringToColor(settings->value(s_OBBsColorOptionName, ColorToString(options.m_OBBsColor)).toString()); options.m_lineSkeletonColor = StringToColor(settings->value(s_lineSkeletonColorOptionName, ColorToString(options.m_lineSkeletonColor)).toString()); options.m_skeletonColor = StringToColor(settings->value(s_skeletonColorOptionName, ColorToString(options.m_skeletonColor)).toString()); options.m_selectionColor = StringToColor(settings->value(s_selectionColorOptionName, ColorToString(options.m_selectionColor)).toString()); @@ -388,7 +383,6 @@ namespace EMStudio ->Field(s_nodeAABBColorOptionName, &RenderOptions::m_nodeAABBColor) ->Field(s_staticAABBColorOptionName, &RenderOptions::m_staticAABBColor) ->Field(s_meshAABBColorOptionName, &RenderOptions::m_meshAABBColor) - ->Field(s_OBBsColorOptionName, &RenderOptions::m_OBBsColor) ->Field(s_lineSkeletonColorOptionName, &RenderOptions::m_lineSkeletonColor) ->Field(s_skeletonColorOptionName, &RenderOptions::m_skeletonColor) ->Field(s_selectionColorOptionName, &RenderOptions::m_selectionColor) @@ -546,9 +540,6 @@ namespace EMStudio ->DataElement(AZ::Edit::UIHandlers::Default, &RenderOptions::m_meshAABBColor, "Mesh based AABB color", "Color for the runtime-updated AABB calculated based on the deformed meshes.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &RenderOptions::OnMeshAABBColorChangedCallback) - ->DataElement(AZ::Edit::UIHandlers::Default, &RenderOptions::m_OBBsColor, "Joint OBB color", - "Color used for the pre-calculated joint oriented bounding boxes.") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &RenderOptions::OnOBBsColorChangedCallback) ->DataElement(AZ::Edit::UIHandlers::Default, &RenderOptions::m_lineSkeletonColor, "Line based skeleton color", "Line-based skeleton color.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &RenderOptions::OnLineSkeletonColorChangedCallback) @@ -894,15 +885,6 @@ namespace EMStudio } } - void RenderOptions::SetOBBsColor(const AZ::Color& OBBsColor) - { - if (!OBBsColor.IsClose(m_OBBsColor)) - { - m_OBBsColor = OBBsColor; - OnOBBsColorChangedCallback(); - } - } - void RenderOptions::SetLineSkeletonColor(const AZ::Color& lineSkeletonColor) { if (!lineSkeletonColor.IsClose(m_lineSkeletonColor)) @@ -1240,11 +1222,6 @@ namespace EMStudio PluginOptionsNotificationsBus::Event(s_meshAABBColorOptionName, &PluginOptionsNotificationsBus::Events::OnOptionChanged, s_meshAABBColorOptionName); } - void RenderOptions::OnOBBsColorChangedCallback() const - { - PluginOptionsNotificationsBus::Event(s_OBBsColorOptionName, &PluginOptionsNotificationsBus::Events::OnOptionChanged, s_OBBsColorOptionName); - } - void RenderOptions::OnLineSkeletonColorChangedCallback() const { PluginOptionsNotificationsBus::Event(s_lineSkeletonColorOptionName, &PluginOptionsNotificationsBus::Events::OnOptionChanged, s_lineSkeletonColorOptionName); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h index 5e12cf935f..60f7aa1291 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h @@ -59,7 +59,6 @@ namespace EMStudio static const char* s_nodeAABBColorOptionName; static const char* s_staticAABBColorOptionName; static const char* s_meshAABBColorOptionName; - static const char* s_OBBsColorOptionName; static const char* s_lineSkeletonColorOptionName; static const char* s_skeletonColorOptionName; static const char* s_selectionColorOptionName; @@ -190,9 +189,6 @@ namespace EMStudio AZ::Color GetMeshAABBColor() const { return m_meshAABBColor; } void SetMeshAABBColor(const AZ::Color& meshAABBColor); - AZ::Color GetOBBsColor() const { return m_OBBsColor; } - void SetOBBsColor(const AZ::Color& OBBsColor); - AZ::Color GetLineSkeletonColor() const { return m_lineSkeletonColor; } void SetLineSkeletonColor(const AZ::Color& lineSkeletonColor); @@ -299,7 +295,6 @@ namespace EMStudio void OnNodeAABBColorChangedCallback() const; void OnStaticAABBColorChangedCallback() const; void OnMeshAABBColorChangedCallback() const; - void OnOBBsColorChangedCallback() const; void OnLineSkeletonColorChangedCallback() const; void OnSkeletonColorChangedCallback() const; void OnSelectionColorChangedCallback() const; @@ -356,7 +351,6 @@ namespace EMStudio AZ::Color m_nodeAABBColor; AZ::Color m_staticAABBColor; AZ::Color m_meshAABBColor; - AZ::Color m_OBBsColor; AZ::Color m_lineSkeletonColor; AZ::Color m_skeletonColor; AZ::Color m_selectionColor; diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp index e4663750ce..ef281b8301 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp @@ -1160,10 +1160,6 @@ namespace EMStudio renderUtil->RenderAabbs(actorInstance, settings); } - if (widget->GetRenderFlag(RenderViewWidget::RENDER_OBB)) - { - renderUtil->RenderOBBs(actorInstance, &visibleJointIndices, &selectedJointIndices, renderOptions->GetOBBsColor(), renderOptions->GetSelectedObjectColor()); - } if (widget->GetRenderFlag(RenderViewWidget::RENDER_LINESKELETON)) { const MCommon::Camera* camera = widget->GetRenderWidget()->GetCamera(); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderUpdateCallback.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderUpdateCallback.cpp index fed309d2e5..6de228fa52 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderUpdateCallback.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderUpdateCallback.cpp @@ -174,10 +174,6 @@ namespace EMStudio renderUtil->RenderAabbs(actorInstance, settings); } - if (widget->GetRenderFlag(RenderViewWidget::RENDER_OBB)) - { - renderUtil->RenderOBBs(actorInstance, &visibleJointIndices, &selectedJointIndices, renderOptions->GetOBBsColor(), renderOptions->GetSelectedObjectColor()); - } if (widget->GetRenderFlag(RenderViewWidget::RENDER_LINESKELETON)) { renderUtil->RenderSimpleSkeleton(actorInstance, &visibleJointIndices, &selectedJointIndices, renderOptions->GetLineSkeletonColor(), renderOptions->GetSelectedObjectColor()); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderViewWidget.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderViewWidget.cpp index 76e1a1e7f3..55699c165f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderViewWidget.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderViewWidget.cpp @@ -108,7 +108,6 @@ namespace EMStudio CreateViewOptionEntry(contextMenu, "Face Normals", RENDER_FACENORMALS); CreateViewOptionEntry(contextMenu, "Tangents", RENDER_TANGENTS); CreateViewOptionEntry(contextMenu, "Actor Bounding Boxes", RENDER_AABB); - CreateViewOptionEntry(contextMenu, "Joint OBBs", RENDER_OBB, false); CreateViewOptionEntry(contextMenu, "Collision Meshes", RENDER_COLLISIONMESHES, false); contextMenu->addSeparator(); CreateViewOptionEntry(contextMenu, "Line Skeleton", RENDER_LINESKELETON); @@ -233,7 +232,6 @@ namespace EMStudio SetRenderFlag(RENDER_TANGENTS, false); SetRenderFlag(RENDER_AABB, false); - SetRenderFlag(RENDER_OBB, false); SetRenderFlag(RENDER_COLLISIONMESHES, false); SetRenderFlag(RENDER_RAGDOLL_COLLIDERS, true); SetRenderFlag(RENDER_RAGDOLL_JOINTLIMITS, true); @@ -410,7 +408,6 @@ namespace EMStudio } // Override some settings as we removed those from the menu. - SetRenderFlag(RENDER_OBB, false); SetRenderFlag(RENDER_COLLISIONMESHES, false); SetRenderFlag(RENDER_TEXTURING, false); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderViewWidget.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderViewWidget.h index 74e5e0ce72..defdb0d2a7 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderViewWidget.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderViewWidget.h @@ -52,7 +52,6 @@ namespace EMStudio RENDER_VERTEXNORMALS = 6, RENDER_TANGENTS = 7, RENDER_AABB = 8, - RENDER_OBB = 9, RENDER_COLLISIONMESHES = 10, RENDER_SKELETON = 11, RENDER_LINESKELETON = 12, From 8190d61e185ff31ff2d986f4447f5f2bd28d1cc4 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Tue, 3 Aug 2021 17:14:04 +0200 Subject: [PATCH 09/38] Removed MCore::OBB Signed-off-by: Benjamin Jillich --- Gems/EMotionFX/Code/MCore/Source/OBB.cpp | 638 -------------------- Gems/EMotionFX/Code/MCore/Source/OBB.h | 235 ------- Gems/EMotionFX/Code/MCore/Source/OBB.inl | 36 -- Gems/EMotionFX/Code/MCore/mcore_files.cmake | 3 - 4 files changed, 912 deletions(-) delete mode 100644 Gems/EMotionFX/Code/MCore/Source/OBB.cpp delete mode 100644 Gems/EMotionFX/Code/MCore/Source/OBB.h delete mode 100644 Gems/EMotionFX/Code/MCore/Source/OBB.inl diff --git a/Gems/EMotionFX/Code/MCore/Source/OBB.cpp b/Gems/EMotionFX/Code/MCore/Source/OBB.cpp deleted file mode 100644 index 66c47e9475..0000000000 --- a/Gems/EMotionFX/Code/MCore/Source/OBB.cpp +++ /dev/null @@ -1,638 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -// include required headers -#include "OBB.h" -#include "AABB.h" - -#include -#include -#include -#include - -namespace MCore -{ - // check if the box contains a given point - bool OBB::Contains(const AZ::Vector3& p) const - { - // translate to box space - AZ::Vector3 relPoint = p - mCenter; - - // convert the box into box space and test each axis - float f = mRotation.GetBasisX().Dot(relPoint); - if (f >= mExtents.GetX() || f <= -mExtents.GetX()) - { - return false; - } - - f = mRotation.GetBasisY().Dot(relPoint); - if (f >= mExtents.GetY() || f <= -mExtents.GetY()) - { - return false; - } - - f = mRotation.GetBasisZ().Dot(relPoint); - if (f >= mExtents.GetZ() || f <= -mExtents.GetZ()) - { - return false; - } - - return true; - } - - - void OBB::Create(const AABB& aabb, const AZ::Transform& mat) - { - // calculate the center and extents - mCenter = aabb.CalcMiddle(); - mExtents = aabb.CalcExtents(); - - // transform the center - mCenter = mat.TransformPoint(mCenter); - - // set the rotation - mRotation = mat; - } - - - void OBB::Transform(const AZ::Transform& transMatrix) - { - mCenter = transMatrix.TransformPoint(mCenter); - mRotation = transMatrix * mRotation; - } - - - void OBB::Transformed(const AZ::Transform& transMatrix, OBB* outOBB) const - { - outOBB->mExtents = mExtents; - outOBB->mCenter = transMatrix.TransformPoint(mCenter); - outOBB->mRotation = transMatrix * mRotation; - } - - - bool OBB::CheckIfIsInside(const OBB& box) const - { - // make a 4x4 from the box & inverse it - AZ::Transform M0 = box.mRotation; - M0.SetTranslation(box.mCenter); - AZ::Transform M0Inv = M0.GetInverse(); - - // with our inversed 4x4, create box1 in space of box0 - OBB _1in0; - Transformed(M0Inv, &_1in0); - - // this should cancel out box0's rotation, i.e. it's now an AABB - - // the two boxes are in the same space so now we can compare them - // create the AABB of (box1 in space of box0) - const AZ::Transform& mtx = _1in0.mRotation; - - AZ::Vector3 transformedAxisX = mtx.GetUniformScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisX())); - AZ::Vector3 transformedAxisY = mtx.GetUniformScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisY())); - AZ::Vector3 transformedAxisZ = mtx.GetUniformScale() * (mtx.GetRotation().GetConjugate().TransformVector(AZ::Vector3::CreateAxisZ())); - - float f = transformedAxisX.GetAbs().Dot(mExtents) - box.mExtents.GetX(); - if (f > _1in0.mCenter.GetX()) - { - return false; - } - if (-f < _1in0.mCenter.GetX()) - { - return false; - } - - f = transformedAxisY.GetAbs().Dot(mExtents) - box.mExtents.GetY(); - if (f > _1in0.mCenter.GetY()) - { - return false; - } - if (-f < _1in0.mCenter.GetY()) - { - return false; - } - - f = transformedAxisZ.GetAbs().Dot(mExtents) - box.mExtents.GetZ(); - if (f > _1in0.mCenter.GetZ()) - { - return false; - } - if (-f < _1in0.mCenter.GetZ()) - { - return false; - } - - return true; - } - - - // calculate the corner points for the OBB - void OBB::CalcCornerPoints(AZ::Vector3* outPoints) const - { - MCORE_ASSERT(outPoints); - MCORE_ASSERT(CheckIfIsValid()); - - AZ::Vector3 right = MCore::GetRight(mRotation); - AZ::Vector3 up = MCore::GetUp(mRotation); - AZ::Vector3 forward = MCore::GetForward(mRotation); - - right *= mExtents.GetX(); - up *= mExtents.GetZ(); - forward *= mExtents.GetY(); - - // 7+------+6 - // /| /| - // / | / | - // / 4+---/--+5 - // 3+------+2 / - // | / | / - // |/ |/ - // 0+------+1 - - outPoints[0] = mCenter - right - up - forward; - outPoints[1] = mCenter + right - up - forward; - outPoints[2] = mCenter + right + up - forward; - outPoints[3] = mCenter - right + up - forward; - outPoints[4] = mCenter - right - up + forward; - outPoints[5] = mCenter + right - up + forward; - outPoints[6] = mCenter + right + up + forward; - outPoints[7] = mCenter - right + up + forward; - } - - - //---------------------------------------------------------------------------------------------------------- - - // calculate the 3 eigen vectors - void OBB::GetRealSymmetricEigenvectors(const float A[6], AZ::Vector3& v1, AZ::Vector3& v2, AZ::Vector3& v3) - { - // compute coefficients for cubic equation - const float c2 = A[0] + A[3] + A[5]; - const float a12sq = A[1] * A[1]; - const float a13sq = A[2] * A[2]; - const float a23sq = A[4] * A[4]; - const float a11a22 = A[0] * A[3]; - const float c1 = a11a22 - a12sq + A[0] * A[5] - a13sq + A[3] * A[5] - a23sq; - const float c0 = a11a22 * A[5] + 2.0f * A[1] * A[2] * A[4] - A[0] * a23sq - A[3] * a13sq - A[5] * a12sq; - - // compute intermediate values for root solving - const float c2sq = c2 * c2; - const float a = (3.0f * c1 - c2sq) / 3.0f; - const float b = (9.0f * c1 * c2 - 2.0f * c2sq * c2 - 27.f * c0) / 27.0f; - const float halfb = b * 0.5f; - const float halfb2 = halfb * halfb; - const float Q = halfb2 + a * a * a / 27.0f; - - // determine type of eigenspaces - if (Q > 1.0e-6f) - { - // one eigenvalue, use standard basis - v1.Set(1.0f, 0.0f, 0.0f); - v2.Set(0.0f, 1.0f, 0.0f); - v3.Set(0.0f, 0.0f, 1.0f); - return; - } - else - if (Q < -1.0e-6f) - { - // three distinct eigenvalues - - // intermediate terms - const float theta_3 = Math::ATan2(Math::Sqrt(-Q), -halfb) / 3.0f; - float rho = Math::Sqrt(halfb2 - Q); - const float c2_3 = c2 / 3.0f; - float rho_13 = powf(Math::Abs(rho), 1.0f / 3.0f); - if (rho < 0.0f) - { - rho_13 = -rho_13; - } - float ct_3, st_3; - const float sqrt3 = Math::Sqrt(3.0f); - ct_3 = Math::Cos(theta_3); - st_3 = Math::Sin(theta_3); - - // compute each eigenvalue and eigenvector - // sort from largest to smallest - float lambda1 = c2_3 + 2.0f * rho_13 * ct_3; - CalcSymmetricEigenVector(A, lambda1, v1); - - float lambda2 = c2_3 - rho_13 * (ct_3 + sqrt3 * st_3); - if (lambda2 > lambda1) - { - v2 = v1; - float temp = lambda2; - lambda2 = lambda1; - lambda1 = temp; - CalcSymmetricEigenVector(A, lambda2, v1); - } - else - { - CalcSymmetricEigenVector(A, lambda2, v2); - } - - float lambda3 = c2_3 - rho_13 * (ct_3 - sqrt3 * st_3); - if (lambda3 > lambda1) - { - v3 = v2; - v2 = v1; - CalcSymmetricEigenVector(A, lambda3, v1); - } - else - if (lambda3 > lambda2) - { - v3 = v2; - CalcSymmetricEigenVector(A, lambda3, v2); - } - else - { - CalcSymmetricEigenVector(A, lambda3, v3); - } - } - else - { - // two distinct eigenvalues - - // intermediate terms - float c2_3 = c2 / 3.0f; - float halfb_13 = Math::Pow(Math::Abs(halfb), 1.0f / 3.0f); - if (halfb < 0.0f) - { - halfb_13 = -halfb_13; - } - - // compute each eigenvalue and eigenvector - // sort from largest to smallest - float lambda1 = c2_3 + halfb_13; - CalcSymmetricEigenPair(A, lambda1, v1, v2); - - float lambda2 = c2_3 - 2.0f * halfb_13; - if (lambda2 > lambda1) - { - v3 = v2; - v2 = v1; - CalcSymmetricEigenVector(A, lambda2, v1); - } - else - { - CalcSymmetricEigenVector(A, lambda2, v3); - } - } - - v1.Normalize(); - v2.Normalize(); - v3.Normalize(); - - if ((v1.Cross(v2)).Dot(v3) < 0.0f) - { - v3 = -v3; - } - } - - - // calculate the eigen vector from a symmetric matrix in combination with a given eigen value - void OBB::CalcSymmetricEigenVector(const float A[6], float eigenValue, AZ::Vector3& v1) - { - const float m11 = A[0] - eigenValue; - const float m12 = A[1]; - const float m13 = A[2]; - const float m22 = A[3] - eigenValue; - const float m23 = A[4]; - const float m33 = A[5] - eigenValue; - - // compute cross product matrix, and find column with maximal entry - const float u11 = m22 * m33 - m23 * m23; - float max = Math::Abs(u11); - int c = 1; - const float u12 = m13 * m23 - m12 * m33; - if (Math::Abs(u12) > max) - { - max = Math::Abs(u12); - c = 2; - } - - const float u13 = m12 * m23 - m13 * m22; - if (Math::Abs(u13) > max) - { - max = Math::Abs(u13); - c = 3; - } - - const float u22 = m11 * m33 - m13 * m13; - if (Math::Abs(u22) > max) - { - max = Math::Abs(u22); - c = 2; - } - - const float u23 = m12 * m13 - m23 * m11; - if (Math::Abs(u23) > max) - { - max = Math::Abs(u23); - c = 3; - } - - const float u33 = m11 * m22 - m12 * m12; - if (Math::Abs(u33) > max) - { - max = Math::Abs(u33); - c = 3; - } - - // return column with maximal entry - if (c == 1) - { - v1.Set(u11, u12, u13); - } - else - if (c == 2) - { - v1.Set(u12, u22, u23); - } - else - { - v1.Set(u13, u23, u33); - } - } - - - //------------------------------------------------------------------------------- - // Given symmetric matrix A and eigenvalue l, returns eigenvector pair - // Assumes that order of eigenvalue is 2 - //------------------------------------------------------------------------------- - void OBB::CalcSymmetricEigenPair(const float A[6], float eigenValue, AZ::Vector3& v1, AZ::Vector3& v2) - { - // find maximal entry in M - const float m11 = A[0] - eigenValue; - float max = Math::Abs(m11); - int r = 1, c = 1; - if (Math::Abs(A[1]) > max) - { - max = Math::Abs(A[1]); - r = 1; - c = 2; - } - - if (Math::Abs(A[2]) > max) - { - max = Math::Abs(A[2]); - r = 1; - c = 3; - } - - const float m22 = A[3] - eigenValue; - if (Math::Abs(m22) > max) - { - max = Math::Abs(m22); - r = 2; - c = 2; - } - - if (Math::Abs(A[4]) > max) - { - max = Math::Abs(A[4]); - r = 2; - c = 3; - } - - const float m33 = A[5] - eigenValue; - if (Math::Abs(m33) > max) - { - r = 3; - c = 3; - } - - // compute eigenvectors for each case - if (r == 1) - { - if (c == 3) - { - v1.Set(A[2], 0.0f, -m11); - v2.Set(-A[1] * m11, m11 * m11 + A[2] * A[2], -A[1] * A[2]); - } - else - { - v1.Set(-A[1], m11, 0.0f); - v2.Set(-A[2] * m11, -A[2] * A[1], m11 * m11 + A[1] * A[1]); - } - } - else - if (r == 2) - { - v1.Set(0.0f, -A[4], m22); - v2.Set(m22 * m22 + A[4] * A[4], -A[1] * m22, -A[1] * A[4]); - } - else - if (r == 3) - { - v1.Set(0.0f, -m33, A[4]); - v2.Set(A[4] * A[4] + m33 * m33, -A[2] * A[4], -A[2] * m33); - } - } - - //----------------------- - - //------------------------------------------------------------------------------- - // Compute covariance matrix for set of points - // Returns centroid and unique values of matrix - //------------------------------------------------------------------------------- - void OBB::CovarianceMatrix(const AZ::Vector3* points, uint32 numPoints, AZ::Vector3& mean, float C[6]) - { - uint32 i; - - // compute mean - mean = points[0]; - for (i = 1; i < numPoints; ++i) - { - mean += points[i]; - } - - mean *= 1.0f / numPoints; - - // compute each element of matrix - memset(C, 0, sizeof(float) * 6); - for (i = 0; i < numPoints; ++i) - { - const AZ::Vector3 diff = points[i] - mean; - C[0] += diff.GetX() * diff.GetX(); - C[1] += diff.GetX() * diff.GetY(); - C[2] += diff.GetX() * diff.GetZ(); - C[3] += diff.GetY() * diff.GetY(); - C[4] += diff.GetY() * diff.GetZ(); - C[5] += diff.GetZ() * diff.GetZ(); - } - - // normalize the matrix values - float maxC = 0.0f; - for (i = 0; i < 6; ++i) - { - if (Math::Abs(C[i]) > maxC) - { - maxC = Math::Abs(C[i]); - } - } - for (i = 0; i < 6; ++i) - { - C[i] /= maxC; - } - } - - - // calc the best fit for a given x rotation slice - void OBB::InitFromPointsRange(const AZ::Vector3* points, uint32 numPoints, float xDegrees, float* outMinArea, AABB* outMinBox, AZ::Transform* outMinMatrix) - { - // calculate the x rotation matrix - AZ::Transform rotMatrix = AZ::Transform::CreateRotationX(Math::DegreesToRadians(xDegrees)); - - // try the same over the z axis - for (float z = -180.0f; z < 180.0f; z += 5.0f) - { - // calculate the final rotation matrix - rotMatrix = AZ::Transform::CreateRotationZ(Math::DegreesToRadians(z)) * rotMatrix; - - // calculate the inverse so we can transform the point set into space of this current rotation - AZ::Transform invMatrix = rotMatrix.GetInverse(); - - // rotate the points into the space of the current rotation - AABB box; - box.Init(); - for (uint32 i = 0; i < numPoints; ++i) - { - box.Encapsulate(invMatrix.TransformPoint(points[i])); - } - - // check if the surface area of this box is smaller than the smallest one we have - const float area = box.CalcSurfaceArea(); - if (area < *outMinArea) - { - *outMinArea = area; - *outMinBox = box; - *outMinMatrix = rotMatrix; - } - } - } - - - // Compute bounding box for set of points - void OBB::InitFromPoints(const AZ::Vector3* points, uint32 numPoints) - { - // if we have no points, just init - if (numPoints == 0) - { - Init(); - return; - } - - // some values we need - const uint32 MAX_NUM = (360 / 5) + 1; - AABB minBoxes[MAX_NUM]; - AZ::Transform minRotMatrices[MAX_NUM]; - float minAreas[MAX_NUM]; - for (uint32 i = 0; i < MAX_NUM; ++i) - { - minAreas[i] = FLT_MAX; - } - - - // try all rotation on the x axis (multithreaded) - AZ::JobCompletion jobCompletion; - uint32 index = 0; - for (float x = -180.0f; x < 180.0f; x += 5.0f) - { - MCORE_ASSERT(index < MAX_NUM); - - // create the job and add it - AZ::JobContext* jobContext = nullptr; - AZ::Job* job = AZ::CreateJobFunction([this, &minAreas, &minBoxes, &minRotMatrices, &numPoints, &points, x, index]() - { - InitFromPointsRange(points, numPoints, x, &minAreas[index], &minBoxes[index], &minRotMatrices[index]); - }, true, jobContext); - - job->SetDependent(&jobCompletion); - job->Start(); - - index++; - } - - jobCompletion.StartAndWaitForCompletion(); - - // find the real minimum value (single threaded lookup) - float minimumArea = FLT_MAX; - uint32 minimumIndex = 0; - for (uint32 i = 0; i < MAX_NUM; ++i) - { - if (minAreas[i] < minimumArea) - { - minimumArea = minAreas[i]; - minimumIndex = i; - } - } - - // update - mRotation = minRotMatrices[minimumIndex]; - mCenter = mRotation.TransformPoint(minBoxes[minimumIndex].CalcMiddle()); - mExtents = minBoxes[minimumIndex].CalcExtents(); - - /* - // compute covariance matrix - float C[6]; - CovarianceMatrix( points, numPoints, mCenter, C ); - - // get principle axes - Vector3 basis[3]; - GetRealSymmetricEigenvectors( C, basis[0], basis[1], basis[2] ); - - // init the min and max vectors - Vector3 minVec; - Vector3 maxVec; - minVec.Set(FLT_MAX, FLT_MAX, FLT_MAX); - maxVec.Set(-FLT_MAX, -FLT_MAX, -FLT_MAX); - - // find the min and max - for (uint32 i=0; i maxVec[j]) - maxVec[j] = length; - else - if (length < minVec[j]) - minVec[j] = length; - } - } - - // build the matrix from the calculated basis vectors - mRotation.Identity(); - mRotation.SetRow(0, basis[0]); - mRotation.SetRow(1, basis[1]); - mRotation.SetRow(2, basis[2]); - - // calculate the extents - mExtents = (maxVec - minVec) * 0.5f; - */ - } - - - // calculate the minimum and maximum point - void OBB::CalcMinMaxPoints(AZ::Vector3* outMin, AZ::Vector3* outMax) const - { - AZ::Transform rotation = mRotation; - rotation.SetTranslation(AZ::Vector3::CreateZero()); - AZ::Vector3 rotatedExtents = rotation.TransformPoint(mExtents); - *outMax = mCenter + rotatedExtents; - *outMin = mCenter - rotatedExtents; - - // +------+MAX - // /| /| - // / | / | - // / +---/--+ - // +------+ / - // | / | / - // |/ |/ - //MIN+------+ - } -} // namespace MCore diff --git a/Gems/EMotionFX/Code/MCore/Source/OBB.h b/Gems/EMotionFX/Code/MCore/Source/OBB.h deleted file mode 100644 index 64cf61fb20..0000000000 --- a/Gems/EMotionFX/Code/MCore/Source/OBB.h +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include -#include "StandardHeaders.h" - - -namespace MCore -{ - // forward declarations - class AABB; - - - /** - * 3D Oriented Bounding Box (OBB) template. - * This is basically a AABB with an arbitrary rotation. - */ - class MCORE_API OBB - { - public: - /** - * The constructor. - * This automatically initializes the box. After initialization the box is invalid since it basically has no size yet. - * The IsValid() method will return false. - */ - MCORE_INLINE OBB() { Init(); } - - /** - * Construct the OBB from a given axis aligned bounding box and a transformation. - * @param aabb The axis aligned bounding box. - * @param transformation The transformation of the box. - */ - MCORE_INLINE OBB(const AABB& aabb, const AZ::Transform& transformation) { Create(aabb, transformation); } - - /** - * Construct the OBB from a center, extends and a rotation. - * @param center The center of the box. - * @param extents The extents of the box, which start at the center of the box. - * @param rot The matrix, representing the transformation of the box. - */ - MCORE_INLINE OBB(const AZ::Vector3& center, const AZ::Vector3& extents, const AZ::Transform& rot) - : mRotation(rot) - , mExtents(extents) - , mCenter(center) {} - - /** - * Reset the OBB with as center 0,0,0, infinite negative extents and no rotation. - * This makes the box an invalid box as well, because the extents have not been set. - */ - MCORE_INLINE void Init(); - - /** - * Initialize the box from a set of points. - * This uses the covariant matrix and eigen vectors to fit the box to the set of points. - * @param points The set of points to fit the box to. - * @param numPoints The number of points inside array specified as first parameter. - */ - void InitFromPoints(const AZ::Vector3* points, uint32 numPoints); - - /** - * Check if this box OBB contains a given point or not. - * @param p The point to check. - * @result Returns true when the point is inside this box, otherwise false is returned. - */ - bool Contains(const AZ::Vector3& p) const; - - /** - * Check if this OBB is inside another specified box. - * @param box The OBB to check. - * @result Returns true when this OBB is inside the box specified as parameter. - */ - bool CheckIfIsInside(const OBB& box) const; - - /** - * Create the OBB from a given AABB and a matrix. - * @param aabb The axis aligned bounding box. - * @param mat The matrix, which represents the orientation of the box. - */ - void Create(const AABB& aabb, const AZ::Transform& mat); - - /** - * Transform this OBB with a given matrix. - * This means the transformation specified as parameter will be applied to the current transformation of the OBB. - * So the transformation specified is NOT an absolute rotation, but a relative transformation. - * @param transMatrix The relative transformation matrix, to be applied to the current transformation. - */ - void Transform(const AZ::Transform& transMatrix); - - /** - * Calculate the transformed version of this OBB. - * @param rotMatrix The transformation matrix to be applied to the rotation of this OBB, so not an absolute rotation! - * @param outOBB A pointer to the OBB to fill with the rotated version of this OBB. - */ - void Transformed(const AZ::Transform& rotMatrix, OBB* outOBB) const; - - /** - * Check if this is a valid OBB or not. - * The box is only valid if the extents are non-negative. - * @result Returns true when the OBB is valid, otherwise false is returned. - */ - MCORE_INLINE bool CheckIfIsValid() const; - - /** - * Set the center of the box. - * @param center The new center of the box. - */ - MCORE_INLINE void SetCenter(const AZ::Vector3& center) { mCenter = center; } - - /** - * Set the extents of the box. - * @param extents The new extents of the box. - */ - MCORE_INLINE void SetExtents(const AZ::Vector3& extents) { mExtents = extents; } - - /** - * Set the transformation of the box. - * @param transform The new transformation of the box. - */ - MCORE_INLINE void SetTransformation(const AZ::Transform& transform) { mRotation = transform; } - - /** - * Get the center of the box. - * @result The center point of the box. - */ - MCORE_INLINE const AZ::Vector3& GetCenter() const { return mCenter; } - - /** - * Get the extents of the box. - * @result The extents of the box, which start at the center. - */ - MCORE_INLINE const AZ::Vector3& GetExtents() const { return mExtents; } - - /** - * Get the transformation of the box. - * @result The transformation of the box. - */ - MCORE_INLINE const AZ::Transform& GetTransformation() const { return mRotation; } - - /** - * Calculate the 8 corner points of the box. - * The layout is as follows: - *
-         *
-         *     7+------+6
-         *     /|     /|
-         *    / |    / |
-         *   / 4+---/--+5
-         * 3+------+2 /
-         *  | /    | /
-         *  |/     |/
-         * 0+------+1
-         *
-         * 
- * @param outPoints the array of at least 8 vectors to write the points in. - */ - void CalcCornerPoints(AZ::Vector3* outPoints) const; - - /** - * Calculate the rotated minimum and maximum points of the box. - * After rotation it is possible that the min point is not really the min anymore though. The same goes for max. - * But the main use for this method however is to quickly approximate an AABB from this OBB, without having to - * calculate all 8 corner points. - *
-         *
-         *        +------+MAX
-         *       /|     /|
-         *      / |    / |
-         *     /  +---/--+
-         *    +------+  /
-         *    | /    | /
-         *    |/     |/
-         * MIN+------+
-         *
-         * 
- * @param outMin The vector that we will write the minimum point to. - * @param outMax The vector that we will write the maximum point to. - */ - void CalcMinMaxPoints(AZ::Vector3* outMin, AZ::Vector3* outMax) const; - - private: - AZ::Transform mRotation; /**< The rotation of the box. */ // TODO: store the center inside the translation component and extents inside last column? - AZ::Vector3 mExtents; /**< The extents of the box. */ - AZ::Vector3 mCenter; /**< The center of the box. */ - - /** - * Calculate the three eigen vectors for a symmetric matrix. - * @param A The symmetric matrix values. - * @param v1 The first output eigen vector. - * @param v2 The second output eigen vector. - * @param v3 The third output eigen vector. - */ - void GetRealSymmetricEigenvectors(const float A[6], AZ::Vector3& v1, AZ::Vector3& v2, AZ::Vector3& v3); - - /** - * Calculate the eigen vector from a symmetric matrix. - * This assumes that the specified eigenvalue is of order 1. - * @param A The symmetric matrix values. - * @param eigenValue The eigen value. - * @param v1 The output eigen vector. - */ - void CalcSymmetricEigenVector(const float A[6], float eigenValue, AZ::Vector3& v1); - - /** - * Calculate the pair of eigen vectors from a symmetric matrix. - * This assumes that the specified eigen value is of order 2. - * @param A The symmetric matrix values. - * @param eigenValue The eigen value. - * @param v1 The first output eigen vector. - * @param v2 The second output eigen vector. - */ - void CalcSymmetricEigenPair(const float A[6], float eigenValue, AZ::Vector3& v1, AZ::Vector3& v2); - - /** - * Calculate the covariance matrix from a set of points. - * @param points The set of points to calculate the covariance matrix from. - * @param numPoints The number of points inside the specified set of points. - * @param mean The statistical mean will be output in this vector. - * @param C The covariance matrix values that will be written to. Since the matrix is symmetric we only output one triangle of the 3x3 matrix. - */ - void CovarianceMatrix(const AZ::Vector3 * points, uint32 numPoints, AZ::Vector3 & mean, float C[6]); - - void InitFromPointsRange(const AZ::Vector3* points, uint32 numPoints, float xDegrees, float* outMinArea, AABB* outMinBox, AZ::Transform* outMinMatrix); - }; - - // include the inline code -#include "OBB.inl" -} // namespace MCore diff --git a/Gems/EMotionFX/Code/MCore/Source/OBB.inl b/Gems/EMotionFX/Code/MCore/Source/OBB.inl deleted file mode 100644 index e8d82dd247..0000000000 --- a/Gems/EMotionFX/Code/MCore/Source/OBB.inl +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -// initialize the box -// this creates an invalid box (with negative extents) so the IsValid method will return false -MCORE_INLINE void OBB::Init() -{ - mCenter = AZ::Vector3::CreateZero(); - mExtents.Set(-FLT_MAX, -FLT_MAX, -FLT_MAX); - mRotation = AZ::Transform::CreateIdentity(); -} - - -// check if the OBB is valid -MCORE_INLINE bool OBB::CheckIfIsValid() const -{ - if (mExtents.GetX() < 0.0f) - { - return false; - } - if (mExtents.GetY() < 0.0f) - { - return false; - } - if (mExtents.GetZ() < 0.0f) - { - return false; - } - return true; -} - diff --git a/Gems/EMotionFX/Code/MCore/mcore_files.cmake b/Gems/EMotionFX/Code/MCore/mcore_files.cmake index b0d8a67ccd..43a41b4a11 100644 --- a/Gems/EMotionFX/Code/MCore/mcore_files.cmake +++ b/Gems/EMotionFX/Code/MCore/mcore_files.cmake @@ -101,9 +101,6 @@ set(FILES Source/MemoryTracker.cpp Source/MemoryTracker.h Source/MultiThreadManager.h - Source/OBB.cpp - Source/OBB.h - Source/OBB.inl Source/PlaneEq.cpp Source/PlaneEq.h Source/PlaneEq.inl From 1b002dcc82ca4fbb8e466c2d569d5e1c0fc9566a Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Wed, 4 Aug 2021 11:48:16 +0200 Subject: [PATCH 10/38] Saving node chunk v2 and adapting the chunk processor and importer to it Signed-off-by: Benjamin Jillich --- .../ExporterLib/Exporter/NodeExport.cpp | 50 +++--------------- .../Source/Importer/ActorFileFormat.h | 8 +-- .../Source/Importer/ChunkProcessors.cpp | 52 ++----------------- .../Source/Importer/ChunkProcessors.h | 3 +- .../EMotionFX/Source/Importer/Importer.cpp | 4 +- 5 files changed, 18 insertions(+), 99 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Exporters/ExporterLib/Exporter/NodeExport.cpp b/Gems/EMotionFX/Code/EMotionFX/Exporters/ExporterLib/Exporter/NodeExport.cpp index c66c3ca8ef..79861be4b8 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Exporters/ExporterLib/Exporter/NodeExport.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Exporters/ExporterLib/Exporter/NodeExport.cpp @@ -18,21 +18,6 @@ namespace ExporterLib { - void WriteObbToNodeChunk(EMotionFX::FileFormat::Actor_Node& nodeChunk, const MCore::OBB& obb) - { - AZ::Transform obbMatrix = obb.GetTransformation(); - obbMatrix.GetBasisX().StoreToFloat3(nodeChunk.mOBB); - nodeChunk.mOBB[3] = 0.0f; - obbMatrix.GetBasisY().StoreToFloat3(nodeChunk.mOBB + 4); - nodeChunk.mOBB[7] = 0.0f; - obbMatrix.GetBasisZ().StoreToFloat3(nodeChunk.mOBB + 8); - nodeChunk.mOBB[11] = 0.0f; - nodeChunk.mOBB[12] = 0.0f; - nodeChunk.mOBB[13] = 0.0f; - nodeChunk.mOBB[14] = 0.0f; - nodeChunk.mOBB[15] = 1.0f; - } - void SaveNode(MCore::Stream* file, EMotionFX::Actor* actor, EMotionFX::Node* node, MCore::Endian::EEndianType targetEndianType) { MCORE_ASSERT(file); @@ -47,7 +32,7 @@ namespace ExporterLib const uint32 numChilds = node->GetNumChildNodes(); const EMotionFX::Transform& transform = actor->GetBindPose()->GetLocalSpaceTransform(nodeIndex); AZ::PackedVector3f position = AZ::PackedVector3f(transform.mPosition); - AZ::Quaternion rotation = transform.mRotation.GetNormalized();; + AZ::Quaternion rotation = transform.mRotation.GetNormalized(); #ifndef EMFX_SCALE_DISABLED AZ::PackedVector3f scale = AZ::PackedVector3f(transform.mScale); @@ -56,14 +41,13 @@ namespace ExporterLib #endif // create the node chunk and copy over the information - EMotionFX::FileFormat::Actor_Node nodeChunk; - memset(&nodeChunk, 0, sizeof(EMotionFX::FileFormat::Actor_Node)); + EMotionFX::FileFormat::Actor_Node2 nodeChunk; + memset(&nodeChunk, 0, sizeof(EMotionFX::FileFormat::Actor_Node2)); CopyVector(nodeChunk.mLocalPos, position); CopyQuaternion(nodeChunk.mLocalQuat, rotation); CopyVector(nodeChunk.mLocalScale, scale); - //nodeChunk.mImportanceFactor = FLT_MAX;//importance; nodeChunk.mNumChilds = numChilds; nodeChunk.mParentIndex = parentIndex; @@ -98,10 +82,6 @@ namespace ExporterLib nodeChunk.mNodeFlags &= ~EMotionFX::Node::ENodeFlags::FLAG_CRITICAL; } - // OBB - WriteObbToNodeChunk(nodeChunk, actor->GetNodeOBB(node->GetNodeIndex())); - - // log the node chunk information MCore::LogDetailedInfo("- Node: name='%s' index=%i", actor->GetSkeleton()->GetNode(nodeIndex)->GetName(), nodeIndex); if (parentIndex == MCORE_INVALIDINDEX32) @@ -140,19 +120,13 @@ namespace ExporterLib ConvertUnsignedInt(&nodeChunk.mNumChilds, targetEndianType); ConvertUnsignedInt(&nodeChunk.mSkeletalLODs, targetEndianType); - for (uint32 j = 0; j < 16; ++j) - { - ConvertFloat(&nodeChunk.mOBB[j], targetEndianType); - } - // write it - file->Write(&nodeChunk, sizeof(EMotionFX::FileFormat::Actor_Node)); + file->Write(&nodeChunk, sizeof(EMotionFX::FileFormat::Actor_Node2)); // write the name of the node and parent SaveString(node->GetName(), file, targetEndianType); } - void SaveNodes(MCore::Stream* file, EMotionFX::Actor* actor, MCore::Endian::EEndianType targetEndianType) { uint32 i; @@ -167,10 +141,10 @@ namespace ExporterLib // chunk information EMotionFX::FileFormat::FileChunk chunkHeader; chunkHeader.mChunkID = EMotionFX::FileFormat::ACTOR_CHUNK_NODES; - chunkHeader.mVersion = 1; + chunkHeader.mVersion = 2; // get the nodes chunk size - chunkHeader.mSizeInBytes = sizeof(EMotionFX::FileFormat::Actor_Nodes) + numNodes * sizeof(EMotionFX::FileFormat::Actor_Node); + chunkHeader.mSizeInBytes = sizeof(EMotionFX::FileFormat::Actor_Nodes2) + numNodes * sizeof(EMotionFX::FileFormat::Actor_Node2); for (i = 0; i < numNodes; i++) { chunkHeader.mSizeInBytes += GetStringChunkSize(actor->GetSkeleton()->GetNode(i)->GetName()); @@ -181,23 +155,15 @@ namespace ExporterLib file->Write(&chunkHeader, sizeof(EMotionFX::FileFormat::FileChunk)); // nodes chunk - EMotionFX::FileFormat::Actor_Nodes nodesChunk; + EMotionFX::FileFormat::Actor_Nodes2 nodesChunk; nodesChunk.mNumNodes = numNodes; nodesChunk.mNumRootNodes = actor->GetSkeleton()->GetNumRootNodes(); - nodesChunk.mStaticBoxMin.mX = actor->GetStaticAabb().GetMin().GetX(); - nodesChunk.mStaticBoxMin.mY = actor->GetStaticAabb().GetMin().GetY(); - nodesChunk.mStaticBoxMin.mZ = actor->GetStaticAabb().GetMin().GetZ(); - nodesChunk.mStaticBoxMax.mX = actor->GetStaticAabb().GetMax().GetX(); - nodesChunk.mStaticBoxMax.mY = actor->GetStaticAabb().GetMax().GetY(); - nodesChunk.mStaticBoxMax.mZ = actor->GetStaticAabb().GetMax().GetZ(); // endian conversion and write it ConvertUnsignedInt(&nodesChunk.mNumNodes, targetEndianType); ConvertUnsignedInt(&nodesChunk.mNumRootNodes, targetEndianType); - ConvertFileVector3(&nodesChunk.mStaticBoxMin, targetEndianType); - ConvertFileVector3(&nodesChunk.mStaticBoxMax, targetEndianType); - file->Write(&nodesChunk, sizeof(EMotionFX::FileFormat::Actor_Nodes)); + file->Write(&nodesChunk, sizeof(EMotionFX::FileFormat::Actor_Nodes2)); // write the nodes for (uint32 n = 0; n < numNodes; n++) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ActorFileFormat.h b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ActorFileFormat.h index b8a5cc9616..402c70c8a1 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ActorFileFormat.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ActorFileFormat.h @@ -108,7 +108,7 @@ namespace EMotionFX // a node header // (not aligned) - struct Actor_Node + struct Actor_Node2 { FileQuaternion mLocalQuat; // the local rotation (before hierarchy) FileVector3 mLocalPos; // the local translation (before hierarchy) @@ -117,7 +117,6 @@ namespace EMotionFX uint32 mParentIndex;// parent node number, or 0xFFFFFFFF in case of a root node uint32 mNumChilds; // the number of child nodes uint8 mNodeFlags; // #1 bit boolean specifies whether we have to include this node in the bounds calculation or not - float mOBB[16]; // followed by: // string : node name (the unique name of the node) @@ -200,14 +199,11 @@ namespace EMotionFX // uint16 [mNumNodes] }; - // (aligned) - struct Actor_Nodes + struct Actor_Nodes2 { uint32 mNumNodes; uint32 mNumRootNodes; - FileVector3 mStaticBoxMin; - FileVector3 mStaticBoxMax; // followed by Actor_Node4[mNumNodes] or Actor_NODE5[mNumNodes] (for v2) }; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.cpp index 7e7a9ae1c2..ddb240841b 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.cpp @@ -355,12 +355,9 @@ namespace EMotionFX return mLoggingActive; } - //================================================================================================= - - // a chunk that contains all nodes in one chunk - bool ChunkProcessorActorNodes::Process(MCore::File* file, Importer::ImportParameters& importParams) + bool ChunkProcessorActorNodes2::Process(MCore::File* file, Importer::ImportParameters& importParams) { const MCore::Endian::EEndianType endianType = importParams.mEndianType; Actor* actor = importParams.mActor; @@ -369,28 +366,12 @@ namespace EMotionFX MCORE_ASSERT(actor); Skeleton* skeleton = actor->GetSkeleton(); - FileFormat::Actor_Nodes nodesHeader; - file->Read(&nodesHeader, sizeof(FileFormat::Actor_Nodes)); + FileFormat::Actor_Nodes2 nodesHeader; + file->Read(&nodesHeader, sizeof(FileFormat::Actor_Nodes2)); // convert endian MCore::Endian::ConvertUnsignedInt32(&nodesHeader.mNumNodes, endianType); MCore::Endian::ConvertUnsignedInt32(&nodesHeader.mNumRootNodes, endianType); - MCore::Endian::ConvertFloat(&nodesHeader.mStaticBoxMin.mX, endianType); - MCore::Endian::ConvertFloat(&nodesHeader.mStaticBoxMin.mY, endianType); - MCore::Endian::ConvertFloat(&nodesHeader.mStaticBoxMin.mZ, endianType); - MCore::Endian::ConvertFloat(&nodesHeader.mStaticBoxMax.mX, endianType); - MCore::Endian::ConvertFloat(&nodesHeader.mStaticBoxMax.mY, endianType); - MCore::Endian::ConvertFloat(&nodesHeader.mStaticBoxMax.mZ, endianType); - - // convert endian and coord system of the static box - AZ::Vector3 boxMin(nodesHeader.mStaticBoxMin.mX, nodesHeader.mStaticBoxMin.mY, nodesHeader.mStaticBoxMin.mZ); - AZ::Vector3 boxMax(nodesHeader.mStaticBoxMax.mX, nodesHeader.mStaticBoxMax.mY, nodesHeader.mStaticBoxMax.mZ); - - // build the box and set it - MCore::AABB staticBox; - staticBox.SetMin(boxMin); - staticBox.SetMax(boxMax); - actor->SetStaticAABB(staticBox); // pre-allocate space for the nodes actor->SetNumNodes(nodesHeader.mNumNodes); @@ -410,8 +391,8 @@ namespace EMotionFX for (uint32 n = 0; n < nodesHeader.mNumNodes; ++n) { // read the node header - FileFormat::Actor_Node nodeChunk; - file->Read(&nodeChunk, sizeof(FileFormat::Actor_Node)); + FileFormat::Actor_Node2 nodeChunk; + file->Read(&nodeChunk, sizeof(FileFormat::Actor_Node2)); // read the node name const char* nodeName = SharedHelperData::ReadString(file, importParams.mSharedData, endianType); @@ -420,7 +401,6 @@ namespace EMotionFX MCore::Endian::ConvertUnsignedInt32(&nodeChunk.mParentIndex, endianType); MCore::Endian::ConvertUnsignedInt32(&nodeChunk.mSkeletalLODs, endianType); MCore::Endian::ConvertUnsignedInt32(&nodeChunk.mNumChilds, endianType); - MCore::Endian::ConvertFloat(&nodeChunk.mOBB[0], endianType, 16); // show the name of the node, the parent and the number of children if (GetLogging()) @@ -453,11 +433,6 @@ namespace EMotionFX ConvertScale(&scale, endianType); ConvertQuaternion(&rot, endianType); - // make sure the input data is normalized - // TODO: this isn't really needed as we already normalized? - //rot.FastNormalize(); - //scaleRot.FastNormalize(); - // set the local transform Transform bindTransform; bindTransform.mPosition = pos; @@ -503,23 +478,6 @@ namespace EMotionFX skeleton->AddRootNode(nodeIndex); } - // OBB - AZ::Matrix4x4 obbMatrix4x4 = AZ::Matrix4x4::CreateFromRowMajorFloat16(nodeChunk.mOBB); - - const AZ::Vector3 obbCenter = obbMatrix4x4.GetTranslation(); - const AZ::Vector3 obbExtents = obbMatrix4x4.GetRowAsVector3(3); - - // initialize the OBB - MCore::OBB obb; - obb.SetCenter(obbCenter); - obb.SetExtents(obbExtents); - - // need to transpose to go from row major to column major - const AZ::Matrix3x3 obbMatrix3x3 = AZ::Matrix3x3::CreateFromMatrix4x4(obbMatrix4x4).GetTranspose(); - const AZ::Transform obbTransform = AZ::Transform::CreateFromMatrix3x3AndTranslation(obbMatrix3x3, obbExtents); - obb.SetTransformation(obbTransform); - actor->SetNodeOBB(nodeIndex, obb); - if (GetLogging()) { MCore::LogDetailedInfo(" - Position: x=%f, y=%f, z=%f", diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.h b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.h index 306e4af0c8..56822c6940 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.h @@ -256,7 +256,6 @@ namespace EMotionFX virtual ~ChunkProcessor(); }; - //------------------------------------------------------------------------------------------------- /** @@ -287,7 +286,7 @@ namespace EMotionFX EMFX_CHUNKPROCESSOR(ChunkProcessorActorInfo3, FileFormat::ACTOR_CHUNK_INFO, 3) EMFX_CHUNKPROCESSOR(ChunkProcessorActorProgMorphTarget, FileFormat::ACTOR_CHUNK_STDPROGMORPHTARGET, 1) EMFX_CHUNKPROCESSOR(ChunkProcessorActorNodeGroups, FileFormat::ACTOR_CHUNK_NODEGROUPS, 1) - EMFX_CHUNKPROCESSOR(ChunkProcessorActorNodes, FileFormat::ACTOR_CHUNK_NODES, 1) + EMFX_CHUNKPROCESSOR(ChunkProcessorActorNodes2, FileFormat::ACTOR_CHUNK_NODES, 2) EMFX_CHUNKPROCESSOR(ChunkProcessorActorProgMorphTargets, FileFormat::ACTOR_CHUNK_STDPMORPHTARGETS, 1) EMFX_CHUNKPROCESSOR(ChunkProcessorActorProgMorphTargets2, FileFormat::ACTOR_CHUNK_STDPMORPHTARGETS, 2) EMFX_CHUNKPROCESSOR(ChunkProcessorActorNodeMotionSources, FileFormat::ACTOR_CHUNK_NODEMOTIONSOURCES, 1) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/Importer.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/Importer.cpp index 67cbb91c06..82da9ccd86 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/Importer.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/Importer.cpp @@ -352,7 +352,7 @@ namespace EMotionFX } // post create init - actor->PostCreateInit(actorSettings.mMakeGeomLODsCompatibleWithSkeletalLODs, false, actorSettings.mUnitTypeConvert); + actor->PostCreateInit(actorSettings.mMakeGeomLODsCompatibleWithSkeletalLODs, actorSettings.mUnitTypeConvert); } // close the file and return a pointer to the actor we loaded @@ -846,7 +846,7 @@ namespace EMotionFX RegisterChunkProcessor(aznew ChunkProcessorActorInfo3()); RegisterChunkProcessor(aznew ChunkProcessorActorProgMorphTarget()); RegisterChunkProcessor(aznew ChunkProcessorActorNodeGroups()); - RegisterChunkProcessor(aznew ChunkProcessorActorNodes()); + RegisterChunkProcessor(aznew ChunkProcessorActorNodes2()); RegisterChunkProcessor(aznew ChunkProcessorActorProgMorphTargets()); RegisterChunkProcessor(aznew ChunkProcessorActorProgMorphTargets2()); RegisterChunkProcessor(aznew ChunkProcessorActorNodeMotionSources()); From d134deee1dccdfe4f4af64fcafe289377fe02316 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Thu, 5 Aug 2021 15:07:14 +0200 Subject: [PATCH 11/38] Add bounding volume expansion to the actor instance Signed-off-by: Benjamin Jillich --- .../Code/EMotionFX/Source/ActorInstance.cpp | 343 ++++-------------- .../Code/EMotionFX/Source/ActorInstance.h | 84 ++--- 2 files changed, 95 insertions(+), 332 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp index b2091f967f..162d826ca4 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp @@ -67,7 +67,7 @@ namespace EMotionFX mMotionSamplingTimer = 0.0f; mTrajectoryDelta.IdentityWithZeroScale(); - mStaticAABB.Init(); + m_staticAabb = AZ::Aabb::CreateNull(); mAnimGraphInstance = nullptr; @@ -137,15 +137,15 @@ namespace EMotionFX UpdateDependencies(); // update the static based AABB dimensions - mStaticAABB = mActor->GetStaticAABB(); - if (mStaticAABB.CheckIfIsValid() == false) + m_staticAabb = mActor->GetStaticAabb(); + if (!m_staticAabb.IsValid()) { UpdateMeshDeformers(0.0f, true); // TODO: not really thread safe because of shared meshes, although it probably will output correctly - UpdateStaticBasedAABBDimensions(); + UpdateStaticBasedAabbDimensions(); } // update the bounds - UpdateBounds(0, mBoundsUpdateType, 1); + UpdateBounds(/*lodLevel=*/0, mBoundsUpdateType); // register it GetActorManager().RegisterActorInstance(this); @@ -254,12 +254,12 @@ namespace EMotionFX UpdateAttachments(); // update the attachment parent matrices // update the bounds when needed - if (GetBoundsUpdateEnabled() && mBoundsUpdateType != BOUNDS_MESH_BASED) + if (GetBoundsUpdateEnabled()) { mBoundsUpdatePassedTime += timePassedInSeconds; if (mBoundsUpdatePassedTime >= mBoundsUpdateFrequency) { - UpdateBounds(mLODLevel, BOUNDS_NODE_BASED, mBoundsUpdateItemFreq); + UpdateBounds(mLODLevel, mBoundsUpdateType, mBoundsUpdateItemFreq); mBoundsUpdatePassedTime = 0.0f; } } @@ -354,7 +354,7 @@ namespace EMotionFX } // update the bounds when needed - if (GetBoundsUpdateEnabled() && mBoundsUpdateType != BOUNDS_MESH_BASED) + if (GetBoundsUpdateEnabled()) { mBoundsUpdatePassedTime += timePassedInSeconds; if (mBoundsUpdatePassedTime >= mBoundsUpdateFrequency) @@ -407,18 +407,6 @@ namespace EMotionFX stack->Update(this, node, timePassedInSeconds, processDisabledDeformers); } } - - // Update the bounds when we are set to use mesh based bounds. - if (GetBoundsUpdateEnabled() && - GetBoundsUpdateType() == BOUNDS_MESH_BASED) - { - mBoundsUpdatePassedTime += timePassedInSeconds; - if (mBoundsUpdatePassedTime >= mBoundsUpdateFrequency) - { - UpdateBounds(mLODLevel, mBoundsUpdateType, mBoundsUpdateItemFreq); - mBoundsUpdatePassedTime = 0.0f; - } - } } // Update the mesh morph deformers, which updates the vertex positions on the CPU, so performing CPU morphing. @@ -439,18 +427,6 @@ namespace EMotionFX stack->UpdateByModifierType(this, node, timePassedInSeconds, MorphMeshDeformer::TYPE_ID, true, processDisabledDeformers); } } - - // Update the bounds when we are set to use mesh based bounds. - if (GetBoundsUpdateEnabled() && - GetBoundsUpdateType() == BOUNDS_MESH_BASED) - { - mBoundsUpdatePassedTime += timePassedInSeconds; - if (mBoundsUpdatePassedTime >= mBoundsUpdateFrequency) - { - UpdateBounds(mLODLevel, mBoundsUpdateType, mBoundsUpdateItemFreq); - mBoundsUpdatePassedTime = 0.0f; - } - } } void ActorInstance::PostPhysicsUpdate(float timePassedInSeconds) @@ -639,118 +615,40 @@ namespace EMotionFX { // calculate the static based AABB case BOUNDS_STATIC_BASED: - CalcStaticBasedAABB(&mAABB); + CalcStaticBasedAabb(&m_aabb); break; // based on the world space positions of the nodes (least accurate, but fastest) case BOUNDS_NODE_BASED: - CalcNodeBasedAABB(&mAABB, itemFrequency); - break; - - // based on the world space positions of the vertices of the collision meshes (faster and more accurate than mesh based) - case BOUNDS_COLLISIONMESH_BASED: - CalcCollisionMeshBasedAABB(geomLODLevel, &mAABB, itemFrequency); + CalcNodeBasedAabb(&m_aabb, itemFrequency); break; // based on the world space positions of the vertices of the meshes (most accurate) case BOUNDS_MESH_BASED: - CalcMeshBasedAABB(geomLODLevel, &mAABB, itemFrequency); - break; - - // based on the world space positions of the vertices of the meshes (most accurate) - case BOUNDS_NODEOBB_BASED: - CalcNodeOBBBasedAABB(&mAABB, itemFrequency); - break; - - case BOUNDS_NODEOBBFAST_BASED: - CalcNodeOBBBasedAABBFast(&mAABB, itemFrequency); + UpdateMeshDeformers(0.0f); + CalcMeshBasedAabb(geomLODLevel, &m_aabb, itemFrequency); break; // when we're dealing with an unspecified bounding volume update method default: MCore::LogInfo("*** EMotionFX::ActorInstance::UpdateBounds() - Unknown boundsType specified! (%d) ***", (uint32)boundsType); } - } - // calculate the axis aligned bounding box that contains the object oriented boxes of all nodes - void ActorInstance::CalcNodeOBBBasedAABBFast(MCore::AABB* outResult, uint32 nodeFrequency) - { - // init the axis aligned bounding box - outResult->Init(); - - const Pose* pose = mTransformData->GetCurrentPose(); - const Skeleton* skeleton = mActor->GetSkeleton(); - - // for all nodes, encapsulate the world space positions - uint16 nodeNr; - const uint32 numNodes = GetNumEnabledNodes(); - for (uint32 i = 0; i < numNodes; i += nodeFrequency) + // Expand the bounding volume by a tolerance area in case set. + if (m_boundsExpandBy > 0.0f) { - nodeNr = GetEnabledNode(i); - Node* node = skeleton->GetNode(nodeNr); - if (node->GetIncludeInBoundsCalc()) - { - const MCore::OBB& obb = mActor->GetNodeOBB(nodeNr); - if (obb.CheckIfIsValid() == false) - { - continue; - } - - // calculate the corner points of the node in local space - AZ::Vector3 minPoint, maxPoint; - obb.CalcMinMaxPoints(&minPoint, &maxPoint); - - // encapsulate the results in the AABB box - const Transform worldTransform = pose->GetWorldSpaceTransform(nodeNr); - outResult->Encapsulate(worldTransform.TransformPoint(minPoint)); - outResult->Encapsulate(worldTransform.TransformPoint(maxPoint)); - } - } - } - - // more accurate node obb based method that uses the 8 corner points of the obb - void ActorInstance::CalcNodeOBBBasedAABB(MCore::AABB* outResult, uint32 nodeFrequency) - { - // init the axis aligned bounding box - outResult->Init(); - - const Pose* pose = mTransformData->GetCurrentPose(); - const Skeleton* skeleton = mActor->GetSkeleton(); - - // for all nodes, encapsulate the world space positions - AZ::Vector3 cornerPoints[8]; - uint16 nodeNr; - const uint32 numNodes = GetNumEnabledNodes(); - for (uint32 i = 0; i < numNodes; i += nodeFrequency) - { - nodeNr = GetEnabledNode(i); - Node* node = skeleton->GetNode(nodeNr); - if (node->GetIncludeInBoundsCalc()) - { - const MCore::OBB& obb = mActor->GetNodeOBB(nodeNr); - if (obb.CheckIfIsValid() == false) - { - continue; - } - - // calculate the 8 corner points - obb.CalcCornerPoints(cornerPoints); - - const Transform worldTransform = pose->GetWorldSpaceTransform(nodeNr); - - // encapsulate all OBB world space corner points inside the AABB - for (uint32 p = 0; p < 8; ++p) - { - outResult->Encapsulate(worldTransform.TransformPoint(cornerPoints[p])); - } - } + const AZ::Vector3 center = m_aabb.GetCenter(); + const AZ::Vector3 halfExtents = m_aabb.GetExtents() * 0.5f; + const AZ::Vector3 scaledHalfExtents = halfExtents * (1.0f + m_boundsExpandBy); + m_aabb.SetMin(center - scaledHalfExtents); + m_aabb.SetMax(center + scaledHalfExtents); } } // calculate the axis aligned bounding box based on the world space positions of the nodes - void ActorInstance::CalcNodeBasedAABB(MCore::AABB* outResult, uint32 nodeFrequency) + void ActorInstance::CalcNodeBasedAabb(AZ::Aabb* outResult, uint32 nodeFrequency) { - outResult->Init(); + *outResult = AZ::Aabb::CreateNull(); const Pose* pose = mTransformData->GetCurrentPose(); const Skeleton* skeleton = mActor->GetSkeleton(); @@ -763,16 +661,15 @@ namespace EMotionFX nodeNr = GetEnabledNode(i); if (skeleton->GetNode(nodeNr)->GetIncludeInBoundsCalc()) { - outResult->Encapsulate(pose->GetWorldSpaceTransform(nodeNr).mPosition); + outResult->AddPoint(pose->GetWorldSpaceTransform(nodeNr).mPosition); } } } // calculate the AABB that contains all world space vertices of all meshes - void ActorInstance::CalcMeshBasedAABB(uint32 geomLODLevel, MCore::AABB* outResult, uint32 vertexFrequency) + void ActorInstance::CalcMeshBasedAabb(uint32 geomLODLevel, AZ::Aabb* outResult, uint32 vertexFrequency) { - // init the axis aligned bounding box - outResult->Init(); + *outResult = AZ::Aabb::CreateNull(); const Pose* pose = mTransformData->GetCurrentPose(); const Skeleton* skeleton = mActor->GetSkeleton(); @@ -800,52 +697,9 @@ namespace EMotionFX const Transform worldTransform = pose->GetMeshNodeWorldSpaceTransform(geomLODLevel, nodeNr); // calculate and encapsulate the mesh bounds inside the total mesh box - MCore::AABB meshBox; - mesh->CalcAABB(&meshBox, worldTransform, vertexFrequency); - outResult->Encapsulate(meshBox); - } - } - - void ActorInstance::CalcCollisionMeshBasedAABB(uint32 geomLODLevel, MCore::AABB* outResult, uint32 vertexFrequency) - { - // init the axis aligned bounding box - outResult->Init(); - - const Pose* pose = mTransformData->GetCurrentPose(); - const Skeleton* skeleton = mActor->GetSkeleton(); - - // for all nodes, encapsulate the world space positions - uint16 nodeNr; - const uint32 numNodes = GetNumEnabledNodes(); - for (uint32 i = 0; i < numNodes; ++i) - { - nodeNr = GetEnabledNode(i); - Node* node = skeleton->GetNode(nodeNr); - - // skip nodes without collision meshes - Mesh* mesh = mActor->GetMesh(geomLODLevel, nodeNr); - if (mesh == nullptr) - { - continue; - } - - if (mesh->GetIsCollisionMesh() == false) - { - continue; - } - - // if this node should be excluded - if (node->GetIncludeInBoundsCalc() == false) - { - continue; - } - - const Transform worldTransform = pose->GetMeshNodeWorldSpaceTransform(geomLODLevel, nodeNr); - - // calculate and encapsulate the mesh bounds inside the total mesh box - MCore::AABB meshBox; - mesh->CalcAABB(&meshBox, worldTransform, vertexFrequency); - outResult->Encapsulate(meshBox); + AZ::Aabb meshBox; + mesh->CalcAabb(&meshBox, worldTransform, vertexFrequency); + outResult->AddAabb(meshBox); } } @@ -1567,111 +1421,45 @@ namespace EMotionFX } // update the static based aabb dimensions - void ActorInstance::UpdateStaticBasedAABBDimensions() + void ActorInstance::UpdateStaticBasedAabbDimensions() { - // backup the transform Transform orgTransform = GetLocalSpaceTransform(); - //------------------------------------- - - // reset position and scale SetLocalSpacePosition(AZ::Vector3::CreateZero()); + EMFX_SCALECODE(SetLocalSpaceScale(AZ::Vector3(1.0f, 1.0f, 1.0f));) - EMFX_SCALECODE( - SetLocalSpaceScale(AZ::Vector3(1.0f, 1.0f, 1.0f));) + UpdateTransformations(0.0f, true); + UpdateMeshDeformers(0.0f); - // rotate over x, y and z axis - AZ::Vector3 boxMin(FLT_MAX, FLT_MAX, FLT_MAX); - AZ::Vector3 boxMax(-FLT_MAX, -FLT_MAX, -FLT_MAX); - for (uint32 axis = 0; axis < 3; axis++) + // calculate the aabb of this + if (mActor->CheckIfHasMeshes(0)) { - for (uint32 i = 0; i < 360; i += 45) // steps of 45 degrees - { - // rotate a given amount of degrees over the axis we are currently testing - AZ::Vector3 axisVector(0.0f, 0.0f, 0.0f); - axisVector.SetElement(axis, 1.0f); - const float angle = static_cast(i); - SetLocalSpaceRotation(MCore::CreateFromAxisAndAngle(axisVector, MCore::Math::DegreesToRadians(angle))); - - UpdateTransformations(0.0f, true); - UpdateMeshDeformers(0.0f); - - // calculate the aabb of this - if (mActor->CheckIfHasMeshes(0)) - { - CalcMeshBasedAABB(0, &mStaticAABB); - } - else - { - CalcNodeBasedAABB(&mStaticAABB); - } - - // find the minimum and maximum - const AZ::Vector3& curMin = mStaticAABB.GetMin(); - const AZ::Vector3& curMax = mStaticAABB.GetMax(); - if (curMin.GetX() < boxMin.GetX()) - { - boxMin.SetX(curMin.GetX()); - } - if (curMin.GetY() < boxMin.GetY()) - { - boxMin.SetY(curMin.GetY()); - } - if (curMin.GetZ() < boxMin.GetZ()) - { - boxMin.SetZ(curMin.GetZ()); - } - if (curMax.GetX() > boxMax.GetX()) - { - boxMax.SetX(curMax.GetX()); - } - if (curMax.GetY() > boxMax.GetY()) - { - boxMax.SetY(curMax.GetY()); - } - if (curMax.GetZ() > boxMax.GetZ()) - { - boxMax.SetZ(curMax.GetZ()); - } - } + CalcMeshBasedAabb(0, &m_staticAabb); + } + else + { + CalcNodeBasedAabb(&m_staticAabb); } - mStaticAABB.SetMin(boxMin); - mStaticAABB.SetMax(boxMax); - - /* - // calculate the center point of the box - const AZ::Vector3 center = mStaticAABB.CalcMiddle(); - - // find the maximum of the width, height and depth - const float maxDim = MCore::Max3( mStaticAABB.CalcWidth(), mStaticAABB.CalcHeight(), mStaticAABB.CalcDepth() ) * 0.5f; - - // make width, height and depth the same as its maximum - mStaticAABB.SetMin( center + AZ::Vector3(-maxDim, -maxDim, -maxDim) ); - mStaticAABB.SetMax( center + AZ::Vector3( maxDim, maxDim, maxDim) ); - */ - //------------------------------------- - - // restore the transform mLocalTransform = orgTransform; } // calculate the moved static based aabb - void ActorInstance::CalcStaticBasedAABB(MCore::AABB* outResult) + void ActorInstance::CalcStaticBasedAabb(AZ::Aabb* outResult) { if (GetIsSkinAttachment()) { - mSelfAttachment->GetAttachToActorInstance()->CalcStaticBasedAABB(outResult); + mSelfAttachment->GetAttachToActorInstance()->CalcStaticBasedAabb(outResult); return; } - *outResult = mStaticAABB; + *outResult = m_staticAabb; EMFX_SCALECODE( - outResult->SetMin(mStaticAABB.GetMin() * mWorldTransform.mScale); - outResult->SetMax(mStaticAABB.GetMax() * mWorldTransform.mScale);) + outResult->SetMin(m_staticAabb.GetMin() * mWorldTransform.mScale); + outResult->SetMax(m_staticAabb.GetMax() * mWorldTransform.mScale);) outResult->Translate(mWorldTransform.mPosition); } - // adjust the animgraph instance + // adjust the anim graph instance void ActorInstance::SetAnimGraphInstance(AnimGraphInstance* instance) { mAnimGraphInstance = instance; @@ -1774,29 +1562,29 @@ namespace EMotionFX SetFlag(BOOL_BOUNDSUPDATEENABLED, enable); } - void ActorInstance::SetStaticBasedAABB(const MCore::AABB& aabb) + void ActorInstance::SetStaticBasedAabb(const AZ::Aabb& aabb) { - mStaticAABB = aabb; + m_staticAabb = aabb; } - void ActorInstance::GetStaticBasedAABB(MCore::AABB* outAABB) + void ActorInstance::GetStaticBasedAabb(AZ::Aabb* outAabb) { - *outAABB = mStaticAABB; + *outAabb = m_staticAabb; } - const MCore::AABB& ActorInstance::GetStaticBasedAABB() const + const AZ::Aabb& ActorInstance::GetStaticBasedAabb() const { - return mStaticAABB; + return m_staticAabb; } - const MCore::AABB& ActorInstance::GetAABB() const + const AZ::Aabb& ActorInstance::GetAabb() const { - return mAABB; + return m_aabb; } - void ActorInstance::SetAABB(const MCore::AABB& aabb) + void ActorInstance::SetAabb(const AZ::Aabb& aabb) { - mAABB = aabb; + m_aabb = aabb; } uint32 ActorInstance::GetNumAttachments() const @@ -2018,23 +1806,20 @@ namespace EMotionFX mVisualizeScale = 0.0f; UpdateMeshDeformers(0.0f); - MCore::AABB box; - CalcCollisionMeshBasedAABB(0, &box); - if (box.CheckIfIsValid()) + AZ::Aabb box = AZ::Aabb::CreateNull(); + + CalcNodeBasedAabb(&box); + if (box.IsValid()) { - mVisualizeScale = MCore::Max(mVisualizeScale, box.CalcRadius()); + const float boxRadius = AZ::Vector3(box.GetMax() - box.GetMin()).GetLength() * 0.5f; + mVisualizeScale = MCore::Max(mVisualizeScale, boxRadius); } - CalcNodeBasedAABB(&box); - if (box.CheckIfIsValid()) + CalcMeshBasedAabb(0, &box); + if (box.IsValid()) { - mVisualizeScale = MCore::Max(mVisualizeScale, box.CalcRadius()); - } - - CalcMeshBasedAABB(0, &box); - if (box.CheckIfIsValid()) - { - mVisualizeScale = MCore::Max(mVisualizeScale, box.CalcRadius()); + const float boxRadius = AZ::Vector3(box.GetMax() - box.GetMin()).GetLength() * 0.5f; + mVisualizeScale = MCore::Max(mVisualizeScale, boxRadius); } mVisualizeScale *= 0.01f; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.h b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.h index 3567175d6d..4488a1386f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.h @@ -60,9 +60,6 @@ namespace EMotionFX { BOUNDS_NODE_BASED = 0, /**< Calculate the bounding volumes based on the world space node positions. */ BOUNDS_MESH_BASED = 1, /**< Calculate the bounding volumes based on the world space vertex positions. */ - BOUNDS_COLLISIONMESH_BASED = 2, /**< Calculate the bounding volumes based on the world space collision mesh vertex positions. */ - BOUNDS_NODEOBB_BASED = 3, /**< Calculate the bounding volumes based on the oriented bounding boxes of the nodes. Uses all 8 corner points of the individual node OBB boxes. */ - BOUNDS_NODEOBBFAST_BASED = 4, /**< Calculate the bounding volumes based on the oriented bounding boxes of the nodes. Uses the min and max point of the individual node OBB boxes. This is less accurate but faster. */ BOUNDS_STATIC_BASED = 5 /**< Calculate the bounding volumes based on an approximate box, based on the mesh bounds, and move this box along with the actor instance position. */ }; @@ -348,6 +345,14 @@ namespace EMotionFX */ EBoundsType GetBoundsUpdateType() const; + /** + * Get the normalized percentage that the calculated bounding box is expanded with. + * This can be used to add a tolerance area to the calculated bounding box to avoid clipping the character too early. + * A static bounding box together with the expansion is the recommended way for maximum performance. + * @result A value of 1.0 means that the calculated bounding box won't be expanded at all, while 2.0 means it is twice the size. + */ + float GetExpandBoundsBy() const { return m_boundsExpandBy; } + /** * Get the bounding volume auto-update item frequency. * A value of 1 would mean every node or vertex will be taken into account in the bounds calculation. @@ -376,11 +381,19 @@ namespace EMotionFX /** * Set the bounding volume auto-update type. * This can be either based on the node's world space positions, the mesh vertex world space positions, or the - * collision mesh vertex world space postitions. + * collision mesh vertex world space positions. * @param bType The bounding volume update type. */ void SetBoundsUpdateType(EBoundsType bType); + /** + * Set the normalized percentage that the calculated bounding box should be expanded with. + * This can be used to add a tolerance area to the calculated bounding box to avoid clipping the character too early. + * A static bounding box together with the expansion is the recommended way for maximum performance. + * @param[in] expandBy A value of 1.0 means that the calculated bounding box won't be expanded at all, while 2.0 means it will be twice the size. + */ + void SetExpandBoundsBy(float expandBy) { m_boundsExpandBy = expandBy; } + /** * Set the bounding volume auto-update item frequency. * A value of 1 would mean every node or vertex will be taken into account in the bounds calculation. @@ -420,11 +433,11 @@ namespace EMotionFX * This function is generally only executed once, when creating the actor instance. * The CalcStaticBasedAABB function then simply translates this box along with the actor instance's position. */ - void UpdateStaticBasedAABBDimensions(); + void UpdateStaticBasedAabbDimensions(); - void SetStaticBasedAABB(const MCore::AABB& aabb); - void GetStaticBasedAABB(MCore::AABB* outAABB); - const MCore::AABB& GetStaticBasedAABB() const; + void SetStaticBasedAabb(const AZ::Aabb& aabb); + void GetStaticBasedAabb(AZ::Aabb* outAabb); + const AZ::Aabb& GetStaticBasedAabb() const; /** * Calculate an axis aligned bounding box that can be used as static AABB. It is static in the way that the volume does not change. It can however be translated as it will move @@ -434,7 +447,7 @@ namespace EMotionFX * If there are no meshes present, a widened node based box will be used instead as basis. * @param outResult The resulting bounding box, moved along with the actor instance's position. */ - void CalcStaticBasedAABB(MCore::AABB* outResult); + void CalcStaticBasedAabb(AZ::Aabb* outResult); /** * Calculate the axis aligned bounding box based on the world space positions of the nodes. @@ -442,7 +455,7 @@ namespace EMotionFX * @param nodeFrequency This will include every "nodeFrequency"-th node. So a value of 1 will include all nodes. A value of 2 would * process every second node, meaning that half of the nodes will be skipped. A value of 4 would process every 4th node, etc. */ - void CalcNodeBasedAABB(MCore::AABB* outResult, uint32 nodeFrequency = 1); + void CalcNodeBasedAabb(AZ::Aabb* outResult, uint32 nodeFrequency = 1); /** * Calculate the axis aligned bounding box based on the world space vertex coordinates of the meshes. @@ -452,43 +465,7 @@ namespace EMotionFX * @param vertexFrequency This includes every "vertexFrequency"-th vertex. So for example a value of 2 would skip every second vertex and * so will process half of the vertices. A value of 4 would process only each 4th vertex, etc. */ - void CalcMeshBasedAABB(uint32 geomLODLevel, MCore::AABB* outResult, uint32 vertexFrequency = 1); - - /** - * Calculate the axis aligned bounding box based on the world space vertex coordinates of the collision meshes. - * If the actor has no collision meshes, the created box will be invalid. - * @param geomLODLevel The geometry LOD level to calculate the box for. - * @param outResult The AABB where this method should store the resulting box in. - * @param vertexFrequency This includes every "vertexFrequency"-th vertex. So for example a value of 2 would skip every second vertex and - * so will process half of the vertices. A value of 4 would process only each 4th vertex, etc. - */ - void CalcCollisionMeshBasedAABB(uint32 geomLODLevel, MCore::AABB* outResult, uint32 vertexFrequency = 1); - - /** - * Calculate the axis aligned bounding box that contains the object oriented boxes of all nodes. - * The OBB (oriented bounding box) of each node is calculated by fitting an OBB to its mesh. - * The OBB of nodes that act as bones and have no meshes themselves are fit to the set of vertices that are influenced by the given bone. - * This method will give more accurate results than the CalcNodeBasedAABB method in trade for a bit lower performance. - * Also one big advantage of this method is that you can use these bounds for hit detection, without having artists setup collision meshes. - * @param outResult The AABB where this method should store the resulting box in. - * @param nodeFrequency This will include every "nodeFrequency"-th node. So a value of 1 will include all nodes. A value of 2 would - * process every second node, meaning that half of the nodes will be skipped. A value of 4 would process every 4th node, etc. - */ - void CalcNodeOBBBasedAABB(MCore::AABB* outResult, uint32 nodeFrequency = 1); - - /** - * Calculate the axis aligned bounding box that contains the object oriented boxes of all nodes. - * The OBB (oriented bounding box) of each node is calculated by fitting an OBB to its mesh. - * The OBB of nodes that act as bones and have no meshes themselves are fit to the set of vertices that are influenced by the given bone. - * This method will give more accurate results than the CalcNodeBasedAABB method in trade for a bit lower performance. - * Also one big advantage of this method is that you can use these bounds for hit detection, without having artists setup collision meshes. - * NOTE: this is a faster variant from the CalcNodeOBBBasedAABB method. The difference is that this method only transforms the min and max point of the box in local space. - * Therefore it is less accurate, but it might still be enough. The original CalcNodeOBBBasedAABB method calculates the 8 corner points of the node obb boxes. - * @param outResult The AABB where this method should store the resulting box in. - * @param nodeFrequency This will include every "nodeFrequency"-th node. So a value of 1 will include all nodes. A value of 2 would - * process every second node, meaning that half of the nodes will be skipped. A value of 4 would process every 4th node, etc. - */ - void CalcNodeOBBBasedAABBFast(MCore::AABB* outResult, uint32 nodeFrequency = 1); + void CalcMeshBasedAabb(uint32 geomLODLevel, AZ::Aabb* outResult, uint32 vertexFrequency = 1); /** * Get the axis aligned bounding box. @@ -496,14 +473,14 @@ namespace EMotionFX * That method is also called automatically when the bounds auto-update feature is enabled. * @result The axis aligned bounding box. */ - const MCore::AABB& GetAABB() const; + const AZ::Aabb& GetAabb() const; /** * Set the axis aligned bounding box. * Please beware that this box will get automatically overwritten when automatic bounds update is enabled. * @param aabb The axis aligned bounding box to store. */ - void SetAABB(const MCore::AABB& aabb); + void SetAabb(const AZ::Aabb& aabb); //------------------------------------------------------------------------------------------- @@ -887,8 +864,8 @@ namespace EMotionFX private: TransformData* mTransformData; /**< The transformation data for this instance. */ - MCore::AABB mAABB; /**< The axis aligned bounding box. */ - MCore::AABB mStaticAABB; /**< A static pre-calculated bounding box, which we can move along with the position of the actor instance, and use for visibility checks. */ + AZ::Aabb m_aabb; /**< The axis aligned bounding box. */ + AZ::Aabb m_staticAabb; /**< A static pre-calculated bounding box, which we can move along with the position of the actor instance, and use for visibility checks. */ Transform mLocalTransform = Transform::CreateIdentity(); Transform mWorldTransform = Transform::CreateIdentity(); @@ -907,7 +884,7 @@ namespace EMotionFX MotionSystem* mMotionSystem; /**< The motion system, that handles all motion playback and blending etc. */ AnimGraphInstance* mAnimGraphInstance; /**< A pointer to the anim graph instance, which can be nullptr when there is no anim graph instance. */ AZStd::unique_ptr m_ragdollInstance; - MCore::Mutex mLock; /**< The multithread lock. */ + MCore::Mutex mLock; /**< The multi-thread lock. */ void* mCustomData; /**< A pointer to custom data for this actor. This could be a pointer to your engine or game object for example. */ AZ::Entity* m_entity; /**< The entity to which the actor instance belongs to. */ float mBoundsUpdateFrequency; /**< The bounds update frequency. Which is a time value in seconds. */ @@ -920,7 +897,8 @@ namespace EMotionFX uint32 mBoundsUpdateItemFreq; /**< The bounds update item counter step size. A value of 1 means every vertex/node, a value of 2 means every second vertex/node, etc. */ uint32 mID; /**< The unique identification number for the actor instance. */ uint32 mThreadIndex; /**< The thread index. This specifies the thread number this actor instance is being processed in. */ - EBoundsType mBoundsUpdateType; /**< The bounds update type (node based, mesh based or colliison mesh based). */ + EBoundsType mBoundsUpdateType; /**< The bounds update type (node based, mesh based or collision mesh based). */ + float m_boundsExpandBy = 0.25f; /**< Expand bounding box by normalized percentage. (Default: 25% greater than the calculated bounding box) */ uint8 mNumAttachmentRefs; /**< Specifies how many actor instances use this actor instance as attachment. */ uint8 mBoolFlags; /**< Boolean flags. */ From 60fa18ec279ba9d39487692ff6a1d943eb920179 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Thu, 5 Aug 2021 15:09:00 +0200 Subject: [PATCH 12/38] Added box expansion percentage to the (editor)actor components Signed-off-by: Benjamin Jillich --- .../Integration/Components/ActorComponent.cpp | 39 ++++++++++--- .../Integration/Components/ActorComponent.h | 21 ++++--- .../Components/EditorActorComponent.cpp | 55 ++++++++++--------- 3 files changed, 71 insertions(+), 44 deletions(-) diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp index 74133997e4..b033e46a87 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp @@ -58,27 +58,32 @@ namespace EMotionFX }; ////////////////////////////////////////////////////////////////////////// - void ActorComponent::BoundingBoxConfiguration::Set(ActorInstance* actor) const + void ActorComponent::BoundingBoxConfiguration::Set(ActorInstance* actorInstance) const { + actorInstance->SetExpandBoundsBy(m_expandBy * 0.01f); // Normalize percentage for internal use. (1% == 0.01f) + if (m_autoUpdateBounds) { - actor->SetupAutoBoundsUpdate(m_updateTimeFrequency, m_boundsType, m_updateItemFrequency); + actorInstance->SetupAutoBoundsUpdate(m_updateTimeFrequency, m_boundsType, m_updateItemFrequency); } else { - actor->SetBoundsUpdateType(m_boundsType); - actor->SetBoundsUpdateEnabled(false); + actorInstance->SetBoundsUpdateType(m_boundsType); + actorInstance->SetBoundsUpdateEnabled(false); } } - void ActorComponent::BoundingBoxConfiguration::SetAndUpdate(ActorInstance* actor) const + void ActorComponent::BoundingBoxConfiguration::SetAndUpdate(ActorInstance* actorInstance) const { - Set(actor); - const AZ::u32 freq = actor->GetBoundsUpdateEnabled() ? actor->GetBoundsUpdateItemFrequency() : 1; - actor->UpdateBounds(0, actor->GetBoundsUpdateType(), freq); + Set(actorInstance); + + const AZ::u32 updateFrequency = actorInstance->GetBoundsUpdateEnabled() ? actorInstance->GetBoundsUpdateItemFrequency() : 1; + const ActorInstance::EBoundsType boundUpdateType = actorInstance->GetBoundsUpdateType(); + + actorInstance->UpdateBounds(actorInstance->GetLODLevel(), boundUpdateType, updateFrequency); } - void ActorComponent::BoundingBoxConfiguration::Reflect(AZ::ReflectContext * context) + void ActorComponent::BoundingBoxConfiguration::Reflect(AZ::ReflectContext* context) { if (auto* serializeContext = azrtti_cast(context)) { @@ -105,10 +110,26 @@ namespace EMotionFX ->Field("m_autoUpdateBounds", &BoundingBoxConfiguration::m_autoUpdateBounds) ->Field("m_updateTimeFrequency", &BoundingBoxConfiguration::m_updateTimeFrequency) ->Field("m_updateItemFrequency", &BoundingBoxConfiguration::m_updateItemFrequency) + ->Field("expandBy", &BoundingBoxConfiguration::m_expandBy) ; } } + AZ::Crc32 ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdate() const + { + return m_boundsType != EMotionFX::ActorInstance::BOUNDS_STATIC_BASED ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide; + } + + AZ::Crc32 ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdateSettings() const + { + if (m_boundsType == EMotionFX::ActorInstance::BOUNDS_STATIC_BASED || m_autoUpdateBounds == false) + { + return AZ::Edit::PropertyVisibility::Hide; + } + + return AZ::Edit::PropertyVisibility::Show; + } + ////////////////////////////////////////////////////////////////////////// void ActorComponent::Configuration::Reflect(AZ::ReflectContext* context) { diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h index 0f36846a50..2bf85692be 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h @@ -45,24 +45,29 @@ namespace EMotionFX AZ_COMPONENT(ActorComponent, "{BDC97E7F-A054-448B-A26F-EA2B5D78E377}"); friend class EditorActorComponent; - struct BoundingBoxConfiguration + class BoundingBoxConfiguration { + public: AZ_TYPE_INFO(BoundingBoxConfiguration, "{EBCFF975-00A5-4578-85C7-59909F52067C}"); BoundingBoxConfiguration() = default; - EMotionFX::ActorInstance::EBoundsType m_boundsType = EMotionFX::ActorInstance::BOUNDS_STATIC_BASED; - bool m_autoUpdateBounds = true; - float m_updateTimeFrequency = 0.f; - AZ::u32 m_updateItemFrequency = 1; + EMotionFX::ActorInstance::EBoundsType m_boundsType = EMotionFX::ActorInstance::BOUNDS_STATIC_BASED; + float m_expandBy = 25.0f; ///< Expand the bounding volume by the given percentage. + bool m_autoUpdateBounds = true; + float m_updateTimeFrequency = 0.0f; + AZ::u32 m_updateItemFrequency = 1; - // Set the bounding box configuration of the given actor instance to the parameters given by `this'. The actor instance must not be null (this is not checked). - void Set(ActorInstance* inst) const; + // Set the bounding box configuration of the given actor instance to the parameters given by 'this'. The actor instance must not be null (this is not checked). + void Set(ActorInstance* actorInstance) const; // Set the bounding box configuration, then update the bounds of the actor instance - void SetAndUpdate(ActorInstance* inst) const; + void SetAndUpdate(ActorInstance* actorInstance) const; static void Reflect(AZ::ReflectContext* context); + + AZ::Crc32 GetVisibilityAutoUpdate() const; + AZ::Crc32 GetVisibilityAutoUpdateSettings() const; }; /** diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index efc5cbd9a2..ed5333da9c 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -62,39 +62,40 @@ namespace EMotionFX { editContext->Class("Actor Bounding Box Config", "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &ActorComponent::BoundingBoxConfiguration::m_boundsType, "Bounds type", - "The method used to compute the Actor bounding box. NOTE: ordered by least expensive to compute to most expensive to compute." - ) - ->EnumAttribute(ActorInstance::BOUNDS_STATIC_BASED, "Static bounds (source-asset bounds)") - ->EnumAttribute(ActorInstance::BOUNDS_NODE_BASED, "Bone position-based") - ->EnumAttribute(ActorInstance::BOUNDS_NODEOBB_BASED, "Bone local bounding box-based") - ->EnumAttribute(ActorInstance::BOUNDS_MESH_BASED, "Render mesh vertex position-based (VERY EXPENSIVE)") - - ->DataElement(0, &ActorComponent::BoundingBoxConfiguration::m_autoUpdateBounds, + "The method used to compute the Actor bounding box. NOTE: ordered by least expensive to compute to most expensive to compute.") + ->EnumAttribute(ActorInstance::BOUNDS_STATIC_BASED, "Static (Recommended)") + ->EnumAttribute(ActorInstance::BOUNDS_NODE_BASED, "Bone position-based") + ->EnumAttribute(ActorInstance::BOUNDS_MESH_BASED, "Mesh vertex-based (Expensive)") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) + ->DataElement(AZ::Edit::UIHandlers::Default, &ActorComponent::BoundingBoxConfiguration::m_expandBy, + "Expand by", + "Percentage that the calculated bounding box should be automatically expanded with. " + "This can be used to add a tolerance area to the calculated bounding box to avoid clipping the character too early. " + "A static bounding box together with the expansion is the recommended way for maximum performance. (Default = 25%)") + ->Attribute(AZ::Edit::Attributes::Suffix, " %") + ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->DataElement(AZ::Edit::UIHandlers::Default, &ActorComponent::BoundingBoxConfiguration::m_autoUpdateBounds, "Automatically update bounds?", - "If true, bounds are automatically updated based on some frequency. Otherwise bounds are computed only at creation or when triggered manually" - ) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) - - ->DataElement(0, &ActorComponent::BoundingBoxConfiguration::m_updateTimeFrequency, + "If true, bounds are automatically updated based on some frequency. Otherwise bounds are computed only at creation or when triggered manually") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) + ->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdate) + ->DataElement(AZ::Edit::UIHandlers::Default, &ActorComponent::BoundingBoxConfiguration::m_updateTimeFrequency, "Update frequency", - "How often to update bounds automatically" - ) - ->Attribute(AZ::Edit::Attributes::Suffix, " Hz") - ->Attribute(AZ::Edit::Attributes::Min, 0.f) - ->Attribute(AZ::Edit::Attributes::Step, 0.001f) - ->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::m_autoUpdateBounds) - - ->DataElement(0, &ActorComponent::BoundingBoxConfiguration::m_updateItemFrequency, + "How often to update bounds automatically") + ->Attribute(AZ::Edit::Attributes::Suffix, " Hz") + ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->Attribute(AZ::Edit::Attributes::Max, FLT_MAX) + ->Attribute(AZ::Edit::Attributes::Step, 0.1f) + ->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdateSettings) + ->DataElement(AZ::Edit::UIHandlers::Default, &ActorComponent::BoundingBoxConfiguration::m_updateItemFrequency, "Update item skip factor", "How many items (bones or vertices) to skip when automatically updating bounds." - "
i.e. =1 uses every single item, =2 uses every 2nd item, =3 uses every 3rd item... " - ) - ->Attribute(AZ::Edit::Attributes::Suffix, " items") - ->Attribute(AZ::Edit::Attributes::Min, (AZ::u32)1) - ->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::m_autoUpdateBounds) + "
i.e. =1 uses every single item, =2 uses every 2nd item, =3 uses every 3rd item...") + ->Attribute(AZ::Edit::Attributes::Suffix, " items") + ->Attribute(AZ::Edit::Attributes::Min, (AZ::u32)1) + ->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdateSettings) ; editContext->Class("Actor", "The Actor component manages an instance of an Actor") From f65bf1a06bc67ca0f425eb15a277876ca56e114e Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Thu, 5 Aug 2021 15:10:16 +0200 Subject: [PATCH 13/38] Increased version in the actor group exporter to automatically reprocess all actors to use the new node chunks Signed-off-by: Benjamin Jillich --- .../EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp index bf79fbf05c..ba8168476c 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp @@ -48,7 +48,10 @@ namespace EMotionFX AZ::SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(2); + // Increasing the version number of the actor group exporter will make sure all actor products will be force re-generated. + // Version history: + // v3: Introduced Actor_Nodes2 (replaced Actor_Nodes) and Actor_Node2 (replaced Actor_Node) + serializeContext->Class()->Version(3); } } From c6760d8935c9afbebecca82c6237bfb7de304fe9 Mon Sep 17 00:00:00 2001 From: Nemerle Date: Thu, 5 Aug 2021 16:48:00 +0200 Subject: [PATCH 14/38] Editor code: tidy up BOOLs,NULLs and overrides pt3. A few 'typedefs' replaced by 'using's This shouldn't have any functional changes at all, just c++17 modernization It's a part 3 of a split #2847 Signed-off-by: Nemerle --- Code/Editor/QtUI/QCollapsibleGroupBox.cpp | 2 +- Code/Editor/RenderHelpers/AxisHelper.h | 2 +- .../TrackView/2DBezierKeyUIControls.cpp | 10 ++++---- .../TrackView/AssetBlendKeyUIControls.cpp | 10 ++++---- .../Editor/TrackView/CaptureKeyUIControls.cpp | 10 ++++---- .../TrackView/CharacterKeyUIControls.cpp | 10 ++++---- .../Editor/TrackView/CommentKeyUIControls.cpp | 14 +++++------ Code/Editor/TrackView/CommentNodeAnimator.cpp | 2 +- .../Editor/TrackView/ConsoleKeyUIControls.cpp | 10 ++++---- Code/Editor/TrackView/EventKeyUIControls.cpp | 14 +++++------ Code/Editor/TrackView/GotoKeyUIControls.cpp | 10 ++++---- .../TrackView/ScreenFaderKeyUIControls.cpp | 14 +++++------ Code/Editor/TrackView/SelectKeyUIControls.cpp | 18 +++++++------- .../TrackView/SequenceBatchRenderDialog.cpp | 15 ++++++------ .../TrackView/SequenceBatchRenderDialog.h | 6 ++--- .../TrackView/SequenceKeyUIControls.cpp | 14 +++++------ Code/Editor/TrackView/SoundKeyUIControls.cpp | 10 ++++---- .../TrackView/TVCustomizeTrackColorsDlg.cpp | 2 +- Code/Editor/TrackView/TVEventsDialog.cpp | 2 +- Code/Editor/TrackView/TVSequenceProps.cpp | 6 ++--- Code/Editor/TrackView/TVSequenceProps.h | 4 ++-- .../TrackView/TimeRangeKeyUIControls.cpp | 10 ++++---- .../TrackView/TrackEventKeyUIControls.cpp | 12 +++++----- Code/Editor/TrackView/TrackViewAnimNode.cpp | 4 ++-- Code/Editor/TrackView/TrackViewDialog.cpp | 18 +++++++------- Code/Editor/TrackView/TrackViewDialog.h | 4 ++-- .../TrackView/TrackViewDopeSheetBase.cpp | 24 +++++++++---------- Code/Editor/TrackView/TrackViewFindDlg.cpp | 4 ++-- Code/Editor/TrackView/TrackViewFindDlg.h | 2 +- .../TrackView/TrackViewKeyPropertiesDlg.cpp | 4 ++-- .../TrackView/TrackViewKeyPropertiesDlg.h | 2 +- Code/Editor/TrackView/TrackViewNodes.cpp | 16 ++++++------- Code/Editor/TrackView/TrackViewSplineCtrl.cpp | 22 ++++++++--------- 33 files changed, 153 insertions(+), 154 deletions(-) diff --git a/Code/Editor/QtUI/QCollapsibleGroupBox.cpp b/Code/Editor/QtUI/QCollapsibleGroupBox.cpp index e1d3f59510..7fe56285e6 100644 --- a/Code/Editor/QtUI/QCollapsibleGroupBox.cpp +++ b/Code/Editor/QtUI/QCollapsibleGroupBox.cpp @@ -13,7 +13,7 @@ QCollapsibleGroupBox::QCollapsibleGroupBox(QWidget* parent) : QGroupBox(parent) , m_collapsed(false) - , m_toggleButton(0) + , m_toggleButton(nullptr) { m_toggleButton = new QToolButton(this); m_toggleButton->setFixedSize(16, 16); diff --git a/Code/Editor/RenderHelpers/AxisHelper.h b/Code/Editor/RenderHelpers/AxisHelper.h index b7d246d48c..70dcd2dae2 100644 --- a/Code/Editor/RenderHelpers/AxisHelper.h +++ b/Code/Editor/RenderHelpers/AxisHelper.h @@ -60,7 +60,7 @@ public: void DrawDome(const Matrix34& worldTM, const SGizmoParameters& setup, DisplayContext& dc, AABB& objectBox); bool HitTest(const Matrix34& worldTM, const SGizmoParameters& setup, HitContext& hc); - bool HitTestForRotationCircle(const Matrix34& worldTM, IDisplayViewport* view, const QPoint& pos, float fHitWidth, Vec3* pOutHitPos = NULL, Vec3* pOutHitNormal = NULL); + bool HitTestForRotationCircle(const Matrix34& worldTM, IDisplayViewport* view, const QPoint& pos, float fHitWidth, Vec3* pOutHitPos = nullptr, Vec3* pOutHitNormal = nullptr); void SetHighlightAxis(int axis) { m_highlightAxis = axis; }; int GetHighlightAxis() const { return m_highlightAxis; }; diff --git a/Code/Editor/TrackView/2DBezierKeyUIControls.cpp b/Code/Editor/TrackView/2DBezierKeyUIControls.cpp index 1709ea2eac..81cd151373 100644 --- a/Code/Editor/TrackView/2DBezierKeyUIControls.cpp +++ b/Code/Editor/TrackView/2DBezierKeyUIControls.cpp @@ -26,19 +26,19 @@ public: CSmartVariableArray mv_table; CSmartVariable mv_value; - virtual void OnCreateVars() + void OnCreateVars() override { AddVariable(mv_table, "Key Properties"); AddVariable(mv_table, mv_value, "Value"); } - bool SupportTrackType([[maybe_unused]] const CAnimParamType& paramType, EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const + bool SupportTrackType([[maybe_unused]] const CAnimParamType& paramType, EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const override { return trackType == eAnimCurveType_BezierFloat; } - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys); - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys); + bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys) override; + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys) override; - virtual unsigned int GetPriority() const { return 0; } + unsigned int GetPriority() const override { return 0; } static const GUID& GetClassID() { diff --git a/Code/Editor/TrackView/AssetBlendKeyUIControls.cpp b/Code/Editor/TrackView/AssetBlendKeyUIControls.cpp index a8e24cf58d..038a9d9780 100644 --- a/Code/Editor/TrackView/AssetBlendKeyUIControls.cpp +++ b/Code/Editor/TrackView/AssetBlendKeyUIControls.cpp @@ -41,7 +41,7 @@ public: CSmartVariable mv_blendInTime; CSmartVariable mv_blendOutTime; - virtual void OnCreateVars() + void OnCreateVars() override { // Init to an invalid id AZ::Data::AssetId assetId; @@ -62,15 +62,15 @@ public: mv_timeScale->SetLimits(0.001f, 100.f); } - bool SupportTrackType([[maybe_unused]] const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, AnimValueType valueType) const + bool SupportTrackType([[maybe_unused]] const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, AnimValueType valueType) const override { return valueType == AnimValueType::AssetBlend; } - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys); - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys); + bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys) override; + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { diff --git a/Code/Editor/TrackView/CaptureKeyUIControls.cpp b/Code/Editor/TrackView/CaptureKeyUIControls.cpp index 821066d4a3..e6d88a6cd6 100644 --- a/Code/Editor/TrackView/CaptureKeyUIControls.cpp +++ b/Code/Editor/TrackView/CaptureKeyUIControls.cpp @@ -27,7 +27,7 @@ public: CSmartVariable mv_folder; CSmartVariable mv_once; - virtual void OnCreateVars() + void OnCreateVars() override { mv_duration.GetVar()->SetLimits(0, 100000.0f); mv_timeStep.GetVar()->SetLimits(0.001f, 1.0f); @@ -39,14 +39,14 @@ public: AddVariable(mv_table, mv_folder, "Output Folder"); AddVariable(mv_table, mv_once, "Just one frame?"); } - bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const + bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const override { return paramType == AnimParamType::Capture; } - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys); - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys); + bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys) override; + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { diff --git a/Code/Editor/TrackView/CharacterKeyUIControls.cpp b/Code/Editor/TrackView/CharacterKeyUIControls.cpp index 9ff41f1275..24bfd9f680 100644 --- a/Code/Editor/TrackView/CharacterKeyUIControls.cpp +++ b/Code/Editor/TrackView/CharacterKeyUIControls.cpp @@ -33,7 +33,7 @@ public: CSmartVariable mv_endTime; CSmartVariable mv_timeScale; - virtual void OnCreateVars() + void OnCreateVars() override { AddVariable(mv_table, "Key Properties"); AddVariable(mv_table, mv_animation, "Animation", IVariable::DT_ANIMATION); @@ -45,14 +45,14 @@ public: AddVariable(mv_table, mv_timeScale, "Time Scale"); mv_timeScale->SetLimits(0.001f, 100.f); } - bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, AnimValueType valueType) const + bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, AnimValueType valueType) const override { return paramType == AnimParamType::Animation || valueType == AnimValueType::CharacterAnim; } - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys); - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys); + bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys) override; + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { diff --git a/Code/Editor/TrackView/CommentKeyUIControls.cpp b/Code/Editor/TrackView/CommentKeyUIControls.cpp index 4c5dc10bfa..b5a706c843 100644 --- a/Code/Editor/TrackView/CommentKeyUIControls.cpp +++ b/Code/Editor/TrackView/CommentKeyUIControls.cpp @@ -30,7 +30,7 @@ public: CSmartVariableEnum mv_font; - virtual void OnCreateVars() + void OnCreateVars() override { AddVariable(mv_table, "Key Properties"); AddVariable(mv_table, mv_comment, "Comment"); @@ -41,13 +41,13 @@ public: AddVariable(mv_table, mv_color, "Color", IVariable::DT_COLOR); - mv_align->SetEnumList(NULL); + mv_align->SetEnumList(nullptr); mv_align->AddEnumItem("Left", ICommentKey::eTA_Left); mv_align->AddEnumItem("Center", ICommentKey::eTA_Center); mv_align->AddEnumItem("Right", ICommentKey::eTA_Right); AddVariable(mv_table, mv_align, "Align"); - mv_font->SetEnumList(NULL); + mv_font->SetEnumList(nullptr); IFileUtil::FileArray fa; CFileUtil::ScanDirectory((Path::GetEditingGameDataFolder() + "/Fonts/").c_str(), "*.xml", fa, true); for (size_t i = 0; i < fa.size(); ++i) @@ -58,14 +58,14 @@ public: } AddVariable(mv_table, mv_font, "Font"); } - bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const + bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const override { return paramType == AnimParamType::CommentText; } - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys); - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys); + bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys) override; + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { diff --git a/Code/Editor/TrackView/CommentNodeAnimator.cpp b/Code/Editor/TrackView/CommentNodeAnimator.cpp index 47f4c5d9ae..15dcce1566 100644 --- a/Code/Editor/TrackView/CommentNodeAnimator.cpp +++ b/Code/Editor/TrackView/CommentNodeAnimator.cpp @@ -26,7 +26,7 @@ CCommentNodeAnimator::CCommentNodeAnimator(CTrackViewAnimNode* pCommentNode) CCommentNodeAnimator::~CCommentNodeAnimator() { - m_pCommentNode = 0; + m_pCommentNode = nullptr; } void CCommentNodeAnimator::Animate(CTrackViewAnimNode* pNode, const SAnimContext& ac) diff --git a/Code/Editor/TrackView/ConsoleKeyUIControls.cpp b/Code/Editor/TrackView/ConsoleKeyUIControls.cpp index cbf22f63e6..7bf23e0b52 100644 --- a/Code/Editor/TrackView/ConsoleKeyUIControls.cpp +++ b/Code/Editor/TrackView/ConsoleKeyUIControls.cpp @@ -24,19 +24,19 @@ public: CSmartVariableArray mv_table; CSmartVariable mv_command; - virtual void OnCreateVars() + void OnCreateVars() override { AddVariable(mv_table, "Key Properties"); AddVariable(mv_table, mv_command, "Command"); } - bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const + bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const override { return paramType == AnimParamType::Console; } - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys); - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys); + bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys) override; + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { diff --git a/Code/Editor/TrackView/EventKeyUIControls.cpp b/Code/Editor/TrackView/EventKeyUIControls.cpp index a45e59dc1a..56b16d7c02 100644 --- a/Code/Editor/TrackView/EventKeyUIControls.cpp +++ b/Code/Editor/TrackView/EventKeyUIControls.cpp @@ -26,7 +26,7 @@ public: CSmartVariable mv_value; CSmartVariable mv_notrigger_in_scrubbing; - virtual void OnCreateVars() + void OnCreateVars() override { AddVariable(mv_table, "Key Properties"); AddVariable(mv_table, mv_event, "Event"); @@ -35,14 +35,14 @@ public: AddVariable(mv_deprecated, "Deprecated"); AddVariable(mv_deprecated, mv_animation, "Animation"); } - bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const + bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const override { return paramType == AnimParamType::Event; } - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys); - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys); + bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys) override; + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { @@ -73,8 +73,8 @@ bool CEventKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType(); if (paramType == AnimParamType::Event) { - mv_event.SetEnumList(NULL); - mv_animation.SetEnumList(NULL); + mv_event.SetEnumList(nullptr); + mv_animation.SetEnumList(nullptr); // Add for empty, unset event mv_event->AddEnumItem(QObject::tr(""), ""); diff --git a/Code/Editor/TrackView/GotoKeyUIControls.cpp b/Code/Editor/TrackView/GotoKeyUIControls.cpp index 6435745d2a..c48ce4c5ad 100644 --- a/Code/Editor/TrackView/GotoKeyUIControls.cpp +++ b/Code/Editor/TrackView/GotoKeyUIControls.cpp @@ -24,12 +24,12 @@ public: CSmartVariableArray mv_table; CSmartVariable mv_command; - virtual void OnCreateVars() + void OnCreateVars() override { AddVariable(mv_table, "Key Properties"); AddVariable(mv_table, mv_command, "Goto Time"); } - bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const + bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const override { if (paramType == AnimParamType::Goto) { @@ -40,10 +40,10 @@ public: return false; } } - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys); - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys); + bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys) override; + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { diff --git a/Code/Editor/TrackView/ScreenFaderKeyUIControls.cpp b/Code/Editor/TrackView/ScreenFaderKeyUIControls.cpp index 786016cb37..a62ab17cbf 100644 --- a/Code/Editor/TrackView/ScreenFaderKeyUIControls.cpp +++ b/Code/Editor/TrackView/ScreenFaderKeyUIControls.cpp @@ -33,23 +33,23 @@ class CScreenFaderKeyUIControls public: //----------------------------------------------------------------------------- //! - virtual bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const + bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const override { return paramType == AnimParamType::ScreenFader; } //----------------------------------------------------------------------------- //! - virtual void OnCreateVars() + void OnCreateVars() override { AddVariable(mv_table, "Key Properties"); - mv_fadeType->SetEnumList(NULL); + mv_fadeType->SetEnumList(nullptr); mv_fadeType->AddEnumItem("FadeIn", IScreenFaderKey::eFT_FadeIn); mv_fadeType->AddEnumItem("FadeOut", IScreenFaderKey::eFT_FadeOut); AddVariable(mv_table, mv_fadeType, "Type"); - mv_fadechangeType->SetEnumList(NULL); + mv_fadechangeType->SetEnumList(nullptr); mv_fadechangeType->AddEnumItem("Linear", IScreenFaderKey::eFCT_Linear); mv_fadechangeType->AddEnumItem("Square", IScreenFaderKey::eFCT_Square); mv_fadechangeType->AddEnumItem("Cubic Square", IScreenFaderKey::eFCT_CubicSquare); @@ -67,13 +67,13 @@ public: //----------------------------------------------------------------------------- //! - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& keys); + bool OnKeySelectionChange(CTrackViewKeyBundle& keys) override; //----------------------------------------------------------------------------- //! - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& keys); + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& keys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { diff --git a/Code/Editor/TrackView/SelectKeyUIControls.cpp b/Code/Editor/TrackView/SelectKeyUIControls.cpp index 37acf105f3..d12f449c73 100644 --- a/Code/Editor/TrackView/SelectKeyUIControls.cpp +++ b/Code/Editor/TrackView/SelectKeyUIControls.cpp @@ -22,7 +22,7 @@ class CSelectKeyUIControls , protected AZ::EntitySystemBus::Handler { public: - CSelectKeyUIControls() {} + CSelectKeyUIControls() = default; ~CSelectKeyUIControls() override; @@ -30,7 +30,7 @@ public: CSmartVariableEnum mv_camera; CSmartVariable mv_BlendTime; - virtual void OnCreateVars() + void OnCreateVars() override { AddVariable(mv_table, "Key Properties"); AddVariable(mv_table, mv_camera, "Camera"); @@ -39,14 +39,14 @@ public: Camera::CameraNotificationBus::Handler::BusConnect(); AZ::EntitySystemBus::Handler::BusConnect(); } - bool SupportTrackType([[maybe_unused]] const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, AnimValueType valueType) const + bool SupportTrackType([[maybe_unused]] const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, AnimValueType valueType) const override { return valueType == AnimValueType::Select; } - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys); - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys); + bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys) override; + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { @@ -98,7 +98,7 @@ bool CSelectKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKey ResetCameraEntries(); // Get All cameras. - mv_camera.SetEnumList(NULL); + mv_camera.SetEnumList(nullptr); mv_camera->AddEnumItem(QObject::tr(""), QString::number(static_cast(AZ::EntityId::InvalidEntityId))); @@ -217,7 +217,7 @@ void CSelectKeyUIControls::OnCameraRemoved(const AZ::EntityId & cameraId) // We can't iterate or remove an item from the enum list, and Camera::CameraRequests::GetCameras // still includes the deleted camera at this point. Reset the list anyway and filter out the // deleted camera. - mv_camera->SetEnumList(NULL); + mv_camera->SetEnumList(nullptr); mv_camera->AddEnumItem(QObject::tr(""), QString::number(static_cast(AZ::EntityId::InvalidEntityId))); AZ::EBusAggregateResults cameraComponentEntities; @@ -256,7 +256,7 @@ void CSelectKeyUIControls::OnEntityNameChanged(const AZ::EntityId & entityId, [[ void CSelectKeyUIControls::ResetCameraEntries() { - mv_camera.SetEnumList(NULL); + mv_camera.SetEnumList(nullptr); mv_camera->AddEnumItem(QObject::tr(""), QString::number(static_cast(AZ::EntityId::InvalidEntityId))); // Find all Component Entity Cameras diff --git a/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp b/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp index 7b08b3e614..4a56b2389b 100644 --- a/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp +++ b/Code/Editor/TrackView/SequenceBatchRenderDialog.cpp @@ -117,8 +117,7 @@ CSequenceBatchRenderDialog::CSequenceBatchRenderDialog(float fps, QWidget* pPare } CSequenceBatchRenderDialog::~CSequenceBatchRenderDialog() -{ -} += default; void CSequenceBatchRenderDialog::reject() { @@ -519,7 +518,7 @@ void CSequenceBatchRenderDialog::OnGo() InitializeContext(); // Trigger the first item. - OnMovieEvent(IMovieListener::eMovieEvent_Stopped, NULL); + OnMovieEvent(IMovieListener::eMovieEvent_Stopped, nullptr); } } @@ -728,7 +727,7 @@ bool CSequenceBatchRenderDialog::GetResolutionFromCustomResText(const char* cust bool CSequenceBatchRenderDialog::LoadOutputOptions(const QString& pathname) { XmlNodeRef batchRenderOptionsNode = XmlHelpers::LoadXmlFromFile(pathname.toStdString().c_str()); - if (batchRenderOptionsNode == NULL) + if (batchRenderOptionsNode == nullptr) { return true; } @@ -1391,7 +1390,7 @@ void CSequenceBatchRenderDialog::OnLoadBatch() Path::GetUserSandboxFolder(), loadPath)) { XmlNodeRef batchRenderListNode = XmlHelpers::LoadXmlFromFile(loadPath.toStdString().c_str()); - if (batchRenderListNode == NULL) + if (batchRenderListNode == nullptr) { return; } @@ -1414,7 +1413,7 @@ void CSequenceBatchRenderDialog::OnLoadBatch() // sequence const QString seqName = itemNode->getAttr("sequence"); item.pSequence = GetIEditor()->GetMovieSystem()->FindLegacySequenceByName(seqName.toUtf8().data()); - if (item.pSequence == NULL) + if (item.pSequence == nullptr) { QMessageBox::warning(this, tr("Sequence not found"), tr("A sequence of '%1' not found! This'll be skipped.").arg(seqName)); continue; @@ -1431,7 +1430,7 @@ void CSequenceBatchRenderDialog::OnLoadBatch() break; } } - if (item.pDirectorNode == NULL) + if (item.pDirectorNode == nullptr) { QMessageBox::warning(this, tr("Director node not found"), tr("A director node of '%1' not found in the sequence of '%2'! This'll be skipped.").arg(directorName).arg(seqName)); continue; @@ -1544,7 +1543,7 @@ bool CSequenceBatchRenderDialog::SetUpNewRenderItem(SRenderItem& item) break; } } - if (item.pDirectorNode == NULL) + if (item.pDirectorNode == nullptr) { return false; } diff --git a/Code/Editor/TrackView/SequenceBatchRenderDialog.h b/Code/Editor/TrackView/SequenceBatchRenderDialog.h index 2d2097fa7a..5d8934f783 100644 --- a/Code/Editor/TrackView/SequenceBatchRenderDialog.h +++ b/Code/Editor/TrackView/SequenceBatchRenderDialog.h @@ -81,8 +81,8 @@ protected: bool disableDebugInfo; bool bCreateVideo; SRenderItem() - : pSequence(NULL) - , pDirectorNode(NULL) + : pSequence(nullptr) + , pDirectorNode(nullptr) , disableDebugInfo(false) , bCreateVideo(false) {} bool operator==(const SRenderItem& item) @@ -155,7 +155,7 @@ protected: , expectedTotalTime(0) , spentTime(0) , flagBU(0) - , pActiveDirectorBU(NULL) + , pActiveDirectorBU(nullptr) , cvarCustomResWidthBU(0) , cvarCustomResHeightBU(0) , cvarDisplayInfoBU(0) diff --git a/Code/Editor/TrackView/SequenceKeyUIControls.cpp b/Code/Editor/TrackView/SequenceKeyUIControls.cpp index 5ef5a9c4ab..7889b30848 100644 --- a/Code/Editor/TrackView/SequenceKeyUIControls.cpp +++ b/Code/Editor/TrackView/SequenceKeyUIControls.cpp @@ -27,7 +27,7 @@ public: CSmartVariable mv_startTime; CSmartVariable mv_endTime; - virtual void OnCreateVars() + void OnCreateVars() override { AddVariable(mv_table, "Key Properties"); AddVariable(mv_table, mv_sequence, "Sequence"); @@ -35,14 +35,14 @@ public: AddVariable(mv_table, mv_startTime, "Start Time"); AddVariable(mv_table, mv_endTime, "End Time"); } - bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const + bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const override { return paramType == AnimParamType::Sequence; } - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys); - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys); + bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys) override; + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { @@ -78,7 +78,7 @@ bool CSequenceKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedK ///////////////////////////////////////////////////////////////////////////////// // fill sequence comboBox with available sequences - mv_sequence.SetEnumList(NULL); + mv_sequence.SetEnumList(nullptr); // Insert '' empty enum mv_sequence->AddEnumItem(QObject::tr(""), CTrackViewDialog::GetEntityIdAsString(AZ::EntityId(AZ::EntityId::InvalidEntityId))); @@ -193,7 +193,7 @@ void CSequenceKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& se IMovieSystem* pMovieSystem = GetIEditor()->GetSystem()->GetIMovieSystem(); - if (pMovieSystem != NULL) + if (pMovieSystem != nullptr) { pMovieSystem->SetStartEndTime(pSequence, sequenceKey.fStartTime, sequenceKey.fEndTime); } diff --git a/Code/Editor/TrackView/SoundKeyUIControls.cpp b/Code/Editor/TrackView/SoundKeyUIControls.cpp index ff6b558abf..8c18b58aed 100644 --- a/Code/Editor/TrackView/SoundKeyUIControls.cpp +++ b/Code/Editor/TrackView/SoundKeyUIControls.cpp @@ -29,7 +29,7 @@ public: CSmartVariable mv_duration; CSmartVariable mv_customColor; - virtual void OnCreateVars() + void OnCreateVars() override { AddVariable(mv_table, "Key Properties"); AddVariable(mv_table, mv_startTrigger, "StartTrigger", IVariable::DT_AUDIO_TRIGGER); @@ -38,14 +38,14 @@ public: AddVariable(mv_options, "Options"); AddVariable(mv_options, mv_customColor, "Custom Color", IVariable::DT_COLOR); } - bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const + bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const override { return paramType == AnimParamType::Sound; } - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys); - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys); + bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys) override; + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { diff --git a/Code/Editor/TrackView/TVCustomizeTrackColorsDlg.cpp b/Code/Editor/TrackView/TVCustomizeTrackColorsDlg.cpp index e03b013720..e2f8a4f7a2 100644 --- a/Code/Editor/TrackView/TVCustomizeTrackColorsDlg.cpp +++ b/Code/Editor/TrackView/TVCustomizeTrackColorsDlg.cpp @@ -344,7 +344,7 @@ void CTVCustomizeTrackColorsDlg::Export(const QString& fullPath) const bool CTVCustomizeTrackColorsDlg::Import(const QString& fullPath) { XmlNodeRef customTrackColorsNode = XmlHelpers::LoadXmlFromFile(fullPath.toStdString().c_str()); - if (customTrackColorsNode == NULL) + if (customTrackColorsNode == nullptr) { return false; } diff --git a/Code/Editor/TrackView/TVEventsDialog.cpp b/Code/Editor/TrackView/TVEventsDialog.cpp index a89c0aac2a..c8221b38cd 100644 --- a/Code/Editor/TrackView/TVEventsDialog.cpp +++ b/Code/Editor/TrackView/TVEventsDialog.cpp @@ -211,7 +211,7 @@ public: int GetNumberOfUsageAndFirstTimeUsed(const char* eventName, float& timeFirstUsed) const; }; -CTVEventsDialog::CTVEventsDialog(QWidget* pParent /*=NULL*/) +CTVEventsDialog::CTVEventsDialog(QWidget* pParent /*=nullptr*/) : QDialog(pParent) , m_ui(new Ui::TVEventsDialog) { diff --git a/Code/Editor/TrackView/TVSequenceProps.cpp b/Code/Editor/TrackView/TVSequenceProps.cpp index 3a0fab2f2c..1ac31d7e2b 100644 --- a/Code/Editor/TrackView/TVSequenceProps.cpp +++ b/Code/Editor/TrackView/TVSequenceProps.cpp @@ -51,7 +51,7 @@ CTVSequenceProps::~CTVSequenceProps() } // CTVSequenceProps message handlers -BOOL CTVSequenceProps::OnInitDialog() +bool CTVSequenceProps::OnInitDialog() { ui->NAME->setText(m_pSequence->GetName()); int seqFlags = m_pSequence->GetFlags(); @@ -97,7 +97,7 @@ BOOL CTVSequenceProps::OnInitDialog() ui->ORT_ONCE->setChecked(true); } - return TRUE; // return TRUE unless you set the focus to a control + return true; // return true unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } @@ -259,7 +259,7 @@ void CTVSequenceProps::OnOK() void CTVSequenceProps::ToggleCutsceneOptions(bool bActivated) { - if (bActivated == FALSE) + if (bActivated == false) { ui->NOABORT->setChecked(false); ui->DISABLEPLAYER->setChecked(false); diff --git a/Code/Editor/TrackView/TVSequenceProps.h b/Code/Editor/TrackView/TVSequenceProps.h index f12c245c57..b54208e429 100644 --- a/Code/Editor/TrackView/TVSequenceProps.h +++ b/Code/Editor/TrackView/TVSequenceProps.h @@ -28,7 +28,7 @@ class CTVSequenceProps { Q_OBJECT public: - CTVSequenceProps(CTrackViewSequence* pSequence, float fps, QWidget* pParent = NULL); // standard constructor + CTVSequenceProps(CTrackViewSequence* pSequence, float fps, QWidget* pParent = nullptr); // standard constructor ~CTVSequenceProps(); private: @@ -39,7 +39,7 @@ private: }; CTrackViewSequence* m_pSequence; - virtual BOOL OnInitDialog(); + virtual bool OnInitDialog(); virtual void OnOK(); void MoveScaleKeys(); diff --git a/Code/Editor/TrackView/TimeRangeKeyUIControls.cpp b/Code/Editor/TrackView/TimeRangeKeyUIControls.cpp index bd771e3849..e35c18329b 100644 --- a/Code/Editor/TrackView/TimeRangeKeyUIControls.cpp +++ b/Code/Editor/TrackView/TimeRangeKeyUIControls.cpp @@ -27,7 +27,7 @@ public: CSmartVariable mv_timeScale; CSmartVariable mv_bLoop; - virtual void OnCreateVars() + void OnCreateVars() override { AddVariable(mv_table, "Key Properties"); AddVariable(mv_table, mv_startTime, "Start Time"); @@ -36,14 +36,14 @@ public: AddVariable(mv_table, mv_bLoop, "Loop"); mv_timeScale->SetLimits(0.001f, 100.f); } - bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const + bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const override { return paramType == AnimParamType::TimeRanges; } - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys); - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys); + bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys) override; + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { diff --git a/Code/Editor/TrackView/TrackEventKeyUIControls.cpp b/Code/Editor/TrackView/TrackEventKeyUIControls.cpp index 934820ede1..035087733b 100644 --- a/Code/Editor/TrackView/TrackEventKeyUIControls.cpp +++ b/Code/Editor/TrackView/TrackEventKeyUIControls.cpp @@ -26,21 +26,21 @@ public: CSmartVariableEnum mv_event; CSmartVariable mv_value; - virtual void OnCreateVars() + void OnCreateVars() override { AddVariable(mv_table, "Key Properties"); AddVariable(mv_table, mv_event, "Track Event"); mv_event->SetFlags(mv_event->GetFlags() | IVariable::UI_UNSORTED); AddVariable(mv_table, mv_value, "Value"); } - bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const + bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const override { return paramType == AnimParamType::TrackEvent; } - virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys); - virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys); + bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys) override; + void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys) override; - virtual unsigned int GetPriority() const { return 1; } + unsigned int GetPriority() const override { return 1; } static const GUID& GetClassID() { @@ -182,7 +182,7 @@ void CTrackEventKeyUIControls::BuildEventDropDown(QString& curEvent, const QStri { bool curEventExists = false; bool addedEventExists = false; - mv_event.SetEnumList(NULL); + mv_event.SetEnumList(nullptr); const int eventCount = sequence->GetTrackEventsCount(); // Need to check if event exists before adding all events diff --git a/Code/Editor/TrackView/TrackViewAnimNode.cpp b/Code/Editor/TrackView/TrackViewAnimNode.cpp index c66ea5635c..9050808d3b 100644 --- a/Code/Editor/TrackView/TrackViewAnimNode.cpp +++ b/Code/Editor/TrackView/TrackViewAnimNode.cpp @@ -370,7 +370,7 @@ bool CTrackViewAnimNode::IsBoundToEditorObjects() const else { // check if bound to legacy entity - return (m_animNode->GetNodeOwner() != NULL); + return (m_animNode->GetNodeOwner() != nullptr); } } @@ -1468,7 +1468,7 @@ bool CTrackViewAnimNode::PasteNodesFromClipboard(QWidget* context) } XmlNodeRef animNodesRoot = clipboard.Get(); - if (animNodesRoot == NULL || strcmp(animNodesRoot->getTag(), "CopyAnimNodesRoot") != 0) + if (animNodesRoot == nullptr || strcmp(animNodesRoot->getTag(), "CopyAnimNodesRoot") != 0) { return false; } diff --git a/Code/Editor/TrackView/TrackViewDialog.cpp b/Code/Editor/TrackView/TrackViewDialog.cpp index 9a35f09911..331daf6730 100644 --- a/Code/Editor/TrackView/TrackViewDialog.cpp +++ b/Code/Editor/TrackView/TrackViewDialog.cpp @@ -130,10 +130,10 @@ const GUID& CTrackViewDialog::GetClassID() ////////////////////////////////////////////////////////////////////////// -CTrackViewDialog* CTrackViewDialog::s_pTrackViewDialog = NULL; +CTrackViewDialog* CTrackViewDialog::s_pTrackViewDialog = nullptr; ////////////////////////////////////////////////////////////////////////// -CTrackViewDialog::CTrackViewDialog(QWidget* pParent /*=NULL*/) +CTrackViewDialog::CTrackViewDialog(QWidget* pParent /*=nullptr*/) : QMainWindow(pParent) { s_pTrackViewDialog = this; @@ -152,7 +152,7 @@ CTrackViewDialog::CTrackViewDialog(QWidget* pParent /*=NULL*/) m_lazyInitDone = false; m_bEditLock = false; - m_pNodeForTracksToolBar = NULL; + m_pNodeForTracksToolBar = nullptr; m_currentToolBarParamTypeId = 0; @@ -181,7 +181,7 @@ CTrackViewDialog::~CTrackViewDialog() m_findDlg->deleteLater(); m_findDlg = nullptr; } - s_pTrackViewDialog = 0; + s_pTrackViewDialog = nullptr; const CTrackViewSequenceManager* pSequenceManager = GetIEditor()->GetSequenceManager(); CTrackViewSequence* sequence = pSequenceManager->GetSequenceByEntityId(m_currentSequenceEntityId); @@ -210,7 +210,7 @@ void CTrackViewDialog::OnAddEntityNodeMenu() } ////////////////////////////////////////////////////////////////////////// -BOOL CTrackViewDialog::OnInitDialog() +bool CTrackViewDialog::OnInitDialog() { InitToolbar(); InitMenu(); @@ -270,7 +270,7 @@ BOOL CTrackViewDialog::OnInitDialog() QString cursorPosText = QString("0.000(%1fps)").arg(FloatToIntRet(m_wndCurveEditor->GetFPS())); m_cursorPos->setText(cursorPosText); - return TRUE; // return TRUE unless you set the focus to a control + return true; // return true unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } @@ -621,7 +621,7 @@ void CTrackViewDialog::InitToolbar() { qaction2->setCheckable(true); } - + m_actions[ID_TV_SNAP_NONE]->setChecked(true); m_tracksToolBar = addToolBar("Tracks Toolbar"); @@ -883,7 +883,7 @@ void CTrackViewDialog::Update() // The active camera node means two conditions: // 1. Sequence camera is currently active. // 2. The camera which owns this node has been set as the current camera by the director node. - bool bSequenceCamInUse = gEnv->pMovieSystem->GetCallback() == NULL || + bool bSequenceCamInUse = gEnv->pMovieSystem->GetCallback() == nullptr || gEnv->pMovieSystem->GetCallback()->IsSequenceCamUsed(); AZ::EntityId camId = gEnv->pMovieSystem->GetCameraParams().cameraEntityId; if (camId.IsValid() && bSequenceCamInUse) @@ -2049,7 +2049,7 @@ void CTrackViewDialog::ClearTracksToolBar() m_tracksToolBar->clear(); m_tracksToolBar->addWidget(new QLabel("Tracks:")); - m_pNodeForTracksToolBar = NULL; + m_pNodeForTracksToolBar = nullptr; m_toolBarParamTypes.clear(); m_currentToolBarParamTypeId = 0; } diff --git a/Code/Editor/TrackView/TrackViewDialog.h b/Code/Editor/TrackView/TrackViewDialog.h index f30d9a414f..f6c1126713 100644 --- a/Code/Editor/TrackView/TrackViewDialog.h +++ b/Code/Editor/TrackView/TrackViewDialog.h @@ -52,7 +52,7 @@ class CTrackViewDialog public: friend CMovieCallback; - CTrackViewDialog(QWidget* pParent = NULL); + CTrackViewDialog(QWidget* pParent = nullptr); ~CTrackViewDialog(); static void RegisterViewClass(); @@ -183,7 +183,7 @@ private: void OnAddEntityNodeMenu(); void OnEditorNotifyEvent(EEditorNotifyEvent event) override; - BOOL OnInitDialog(); + bool OnInitDialog(); void SaveLayouts(); void SaveMiscSettings() const; diff --git a/Code/Editor/TrackView/TrackViewDopeSheetBase.cpp b/Code/Editor/TrackView/TrackViewDopeSheetBase.cpp index 34ca2f062f..accba34422 100644 --- a/Code/Editor/TrackView/TrackViewDopeSheetBase.cpp +++ b/Code/Editor/TrackView/TrackViewDopeSheetBase.cpp @@ -88,7 +88,7 @@ CTrackViewDopeSheetBase::CTrackViewDopeSheetBase(QWidget* parent) m_currentTime = 0.0f; m_storedTime = m_currentTime; m_rcSelect = QRect(0, 0, 0, 0); - m_rubberBand = 0; + m_rubberBand = nullptr; m_scrollBar = new QScrollBar(Qt::Horizontal, this); connect(m_scrollBar, &QScrollBar::valueChanged, this, &CTrackViewDopeSheetBase::OnHScroll); m_keyTimeOffset = 0; @@ -113,7 +113,7 @@ CTrackViewDopeSheetBase::CTrackViewDopeSheetBase(QWidget* parent) m_bFastRedraw = false; - m_pLastTrackSelectedOnSpot = NULL; + m_pLastTrackSelectedOnSpot = nullptr; m_wndPropsOnSpot = nullptr; @@ -544,7 +544,7 @@ void CTrackViewDopeSheetBase::OnLButtonUp(Qt::KeyboardModifiers modifiers, const SelectKeys(m_rcSelect, modifiers & Qt::ControlModifier); m_rcSelect = QRect(); m_rubberBand->deleteLater(); - m_rubberBand = 0; + m_rubberBand = nullptr; } else if (m_mouseMode == eTVMouseMode_SelectWithinTime) { @@ -552,7 +552,7 @@ void CTrackViewDopeSheetBase::OnLButtonUp(Qt::KeyboardModifiers modifiers, const SelectAllKeysWithinTimeFrame(m_rcSelect, modifiers & Qt::ControlModifier); m_rcSelect = QRect(); m_rubberBand->deleteLater(); - m_rubberBand = 0; + m_rubberBand = nullptr; } else if (m_mouseMode == eTVMouseMode_DragTime) { @@ -744,7 +744,7 @@ void CTrackViewDopeSheetBase::OnRButtonDown(Qt::KeyboardModifiers modifiers, con } else { - m_pLastTrackSelectedOnSpot = NULL; + m_pLastTrackSelectedOnSpot = nullptr; } ShowKeyPropertyCtrlOnSpot(p.x(), p.y(), selectedKeys.GetKeyCount() > 1, bKeyChangeInSameTrack); @@ -783,7 +783,7 @@ void CTrackViewDopeSheetBase::OnRButtonUp([[maybe_unused]] Qt::KeyboardModifiers if (!m_bCursorWasInKey) { - const bool bHasCopiedKey = (GetKeysInClickboard() != NULL); + const bool bHasCopiedKey = (GetKeysInClickboard() != nullptr); if (bHasCopiedKey && m_bMouseMovedAfterRButtonDown == false) // Once moved, it means the user wanted to scroll, so no paste pop-up. { @@ -1230,24 +1230,24 @@ XmlNodeRef CTrackViewDopeSheetBase::GetKeysInClickboard() CClipboard clip(this); if (clip.IsEmpty()) { - return NULL; + return nullptr; } if (clip.GetTitle() != "Track view keys") { - return NULL; + return nullptr; } XmlNodeRef copyNode = clip.Get(); - if (copyNode == NULL || strcmp(copyNode->getTag(), "CopyKeysNode")) + if (copyNode == nullptr || strcmp(copyNode->getTag(), "CopyKeysNode")) { - return NULL; + return nullptr; } int nNumTracksToPaste = copyNode->getChildCount(); if (nNumTracksToPaste == 0) { - return NULL; + return nullptr; } return copyNode; @@ -1789,7 +1789,7 @@ float CTrackViewDopeSheetBase::FrameSnap(float time) const ////////////////////////////////////////////////////////////////////////// void CTrackViewDopeSheetBase::ShowKeyPropertyCtrlOnSpot(int x, int y, [[maybe_unused]] bool bMultipleKeysSelected, bool bKeyChangeInSameTrack) { - if (m_keyPropertiesDlg == NULL) + if (m_keyPropertiesDlg == nullptr) { return; } diff --git a/Code/Editor/TrackView/TrackViewFindDlg.cpp b/Code/Editor/TrackView/TrackViewFindDlg.cpp index 3d6e93fb05..014618a62a 100644 --- a/Code/Editor/TrackView/TrackViewFindDlg.cpp +++ b/Code/Editor/TrackView/TrackViewFindDlg.cpp @@ -24,13 +24,13 @@ AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING // CTrackViewFindDlg dialog -CTrackViewFindDlg::CTrackViewFindDlg(const char* title, QWidget* pParent /*=NULL*/) +CTrackViewFindDlg::CTrackViewFindDlg(const char* title, QWidget* pParent /*=nullptr*/) : QDialog(pParent) , ui(new Ui::TrackViewFindDlg) { setWindowTitle(title); - m_tvDlg = 0; + m_tvDlg = nullptr; m_numSeqs = 0; ui->setupUi(this); diff --git a/Code/Editor/TrackView/TrackViewFindDlg.h b/Code/Editor/TrackView/TrackViewFindDlg.h index 65723634e4..8a3239087d 100644 --- a/Code/Editor/TrackView/TrackViewFindDlg.h +++ b/Code/Editor/TrackView/TrackViewFindDlg.h @@ -32,7 +32,7 @@ class CTrackViewFindDlg Q_OBJECT // Construction public: - CTrackViewFindDlg(const char* title = NULL, QWidget* pParent = NULL); // standard constructor + CTrackViewFindDlg(const char* title = nullptr, QWidget* pParent = nullptr); // standard constructor ~CTrackViewFindDlg(); //Functions diff --git a/Code/Editor/TrackView/TrackViewKeyPropertiesDlg.cpp b/Code/Editor/TrackView/TrackViewKeyPropertiesDlg.cpp index 158052eb2a..6a5c5b6427 100644 --- a/Code/Editor/TrackView/TrackViewKeyPropertiesDlg.cpp +++ b/Code/Editor/TrackView/TrackViewKeyPropertiesDlg.cpp @@ -328,8 +328,8 @@ bool CTrackViewTrackPropsDlg::OnKeySelectionChange(CTrackViewKeyBundle& selected } else { - ui->PREVNEXT->setEnabled(FALSE); - ui->TIME->setEnabled(FALSE); + ui->PREVNEXT->setEnabled(false); + ui->TIME->setEnabled(false); } return true; } diff --git a/Code/Editor/TrackView/TrackViewKeyPropertiesDlg.h b/Code/Editor/TrackView/TrackViewKeyPropertiesDlg.h index d4670a95c8..9b9a933b49 100644 --- a/Code/Editor/TrackView/TrackViewKeyPropertiesDlg.h +++ b/Code/Editor/TrackView/TrackViewKeyPropertiesDlg.h @@ -74,7 +74,7 @@ protected: // Helper functions. ////////////////////////////////////////////////////////////////////////// template - void SyncValue(CSmartVariable& var, T& value, bool bCopyToUI, IVariable* pSrcVar = NULL) + void SyncValue(CSmartVariable& var, T& value, bool bCopyToUI, IVariable* pSrcVar = nullptr) { if (bCopyToUI) { diff --git a/Code/Editor/TrackView/TrackViewNodes.cpp b/Code/Editor/TrackView/TrackViewNodes.cpp index d69c256049..daf99990a8 100644 --- a/Code/Editor/TrackView/TrackViewNodes.cpp +++ b/Code/Editor/TrackView/TrackViewNodes.cpp @@ -69,7 +69,7 @@ public: : QStyledItemDelegate(parent) {} - void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override { bool enabled = index.data(CTrackViewNodesCtrl::CRecord::EnableRole).toBool(); QStyleOptionViewItem opt = option; @@ -106,7 +106,7 @@ protected: return Qt::CopyAction | Qt::MoveAction; } - void dragMoveEvent(QDragMoveEvent* event) + void dragMoveEvent(QDragMoveEvent* event) override { CTrackViewNodesCtrl::CRecord* record = (CTrackViewNodesCtrl::CRecord*) itemAt(event->pos()); if (!record) @@ -144,7 +144,7 @@ protected: } } - void dropEvent(QDropEvent* event) + void dropEvent(QDropEvent* event) override { CTrackViewNodesCtrl::CRecord* record = (CTrackViewNodesCtrl::CRecord*) itemAt(event->pos()); if (!record) @@ -200,7 +200,7 @@ protected: } } - void keyPressEvent(QKeyEvent* event) + void keyPressEvent(QKeyEvent* event) override { // HAVE TO INCLUDE CASES FOR THESE IN THE ShortcutOverride handler in ::event() below switch (event->key()) @@ -242,7 +242,7 @@ protected: } - bool focusNextPrevChild([[maybe_unused]] bool next) + bool focusNextPrevChild([[maybe_unused]] bool next) override { return false; // so we get the tab key } @@ -361,7 +361,7 @@ CTrackViewNodesCtrl::CTrackViewNodesCtrl(QWidget* hParentWnd, CTrackViewDialog* , m_pTrackViewDialog(parent) { ui->setupUi(this); - m_pDopeSheet = 0; + m_pDopeSheet = nullptr; m_currentMatchIndex = 0; m_matchCount = 0; @@ -954,7 +954,7 @@ void CTrackViewNodesCtrl::OnSelectionChanged() ////////////////////////////////////////////////////////////////////////// void CTrackViewNodesCtrl::OnNMRclick(QPoint point) { - CRecord* record = 0; + CRecord* record = nullptr; bool isOnAzEntity = false; CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence(); if (!sequence) @@ -1605,7 +1605,7 @@ CTrackViewTrack* CTrackViewNodesCtrl::GetTrackViewTrack(const Export::EntityAnim } } - return 0; + return nullptr; } ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Editor/TrackView/TrackViewSplineCtrl.cpp b/Code/Editor/TrackView/TrackViewSplineCtrl.cpp index 19c8b2d4c2..c21403d1f5 100644 --- a/Code/Editor/TrackView/TrackViewSplineCtrl.cpp +++ b/Code/Editor/TrackView/TrackViewSplineCtrl.cpp @@ -56,10 +56,10 @@ protected: } } - virtual int GetSize() { return sizeof(*this); } - virtual QString GetDescription() { return "UndoTrackViewSplineCtrl"; }; + int GetSize() override { return sizeof(*this); } + QString GetDescription() override { return "UndoTrackViewSplineCtrl"; }; - virtual void Undo(bool bUndo) + void Undo(bool bUndo) override { CTrackViewSplineCtrl* pCtrl = FindControl(m_pCtrl); if (pCtrl) @@ -103,7 +103,7 @@ protected: } } - virtual void Redo() + void Redo() override { const CTrackViewSequenceManager* pSequenceManager = GetIEditor()->GetSequenceManager(); CTrackViewSequence* sequence = pSequenceManager->GetSequenceByEntityId(m_sequenceEntityId); @@ -136,7 +136,7 @@ protected: sequence->OnKeySelectionChanged(); } - virtual bool IsSelectionChanged() const + bool IsSelectionChanged() const override { const CTrackViewSequenceManager* sequenceManager = GetIEditor()->GetSequenceManager(); CTrackViewSequence* sequence = sequenceManager->GetSequenceByEntityId(m_sequenceEntityId); @@ -151,19 +151,19 @@ protected: } public: - typedef std::list CTrackViewSplineCtrls; + using CTrackViewSplineCtrls = std::list; static CTrackViewSplineCtrl* FindControl(CTrackViewSplineCtrl* pCtrl) { if (!pCtrl) { - return 0; + return nullptr; } auto iter = std::find(s_activeCtrls.begin(), s_activeCtrls.end(), pCtrl); if (iter == s_activeCtrls.end()) { - return 0; + return nullptr; } return *iter; @@ -449,7 +449,7 @@ void CTrackViewSplineCtrl::AddSpline(ISplineInterpolator* pSpline, CTrackViewTra } si.pSpline = pSpline; - si.pDetailSpline = NULL; + si.pDetailSpline = nullptr; m_splines.push_back(si); m_tracks.push_back(pTrack); m_bKeyTimesDirty = true; @@ -896,7 +896,7 @@ bool CTrackViewSplineCtrl::IsUnifiedKeyCurrentlySelected() const { ISplineInterpolator* pSpline = m_splines[splineIndex].pSpline; - if (pSpline == NULL) + if (pSpline == nullptr) { continue; } @@ -960,7 +960,7 @@ void CTrackViewSplineCtrl::mouseReleaseEvent(QMouseEvent* event) if (GetIEditor()->GetAnimation()->GetSequence()) { bool restoreRecordModeToTrue = (m_editMode == TimeMarkerMode && m_stashedRecordModeWhenDraggingTime); - + SplineWidget::mouseReleaseEvent(event); if (restoreRecordModeToTrue) From 1a80d313e58dbef0ac251ddeca40818050fdcbd6 Mon Sep 17 00:00:00 2001 From: Nemerle Date: Thu, 5 Aug 2021 16:49:02 +0200 Subject: [PATCH 15/38] Editor code: tidy up BOOLs,NULLs and overrides pt4. A few 'typedefs' replaced by 'using's This shouldn't have any functional changes at all, just c++17 modernization It's a part 4 of a split #2847 Signed-off-by: Nemerle --- Code/Editor/Undo/Undo.cpp | 24 +++++++------- Code/Editor/Undo/Undo.h | 4 +-- Code/Editor/Util/3DConnexionDriver.cpp | 10 +++--- Code/Editor/Util/DynamicArray2D.cpp | 2 +- Code/Editor/Util/EditorUtils.cpp | 6 ++-- Code/Editor/Util/EditorUtils.h | 12 +++---- Code/Editor/Util/FileChangeMonitor.cpp | 6 ++-- Code/Editor/Util/FileChangeMonitor.h | 2 +- Code/Editor/Util/FileEnum.cpp | 10 +++--- Code/Editor/Util/FileUtil.cpp | 34 ++++++++++---------- Code/Editor/Util/FileUtil.h | 2 +- Code/Editor/Util/FileUtil_impl.h | 4 +-- Code/Editor/Util/GdiUtil.h | 2 +- Code/Editor/Util/IXmlHistoryManager.h | 2 +- Code/Editor/Util/ImageASC.cpp | 28 ++++++++--------- Code/Editor/Util/ImageGif.cpp | 2 +- Code/Editor/Util/ImageTIF.cpp | 12 +++---- Code/Editor/Util/ImageUtil.cpp | 38 +++++++++++------------ Code/Editor/Util/IndexedFiles.cpp | 2 +- Code/Editor/Util/IndexedFiles.h | 2 +- Code/Editor/Util/KDTree.cpp | 24 +++++++------- Code/Editor/Util/Math.h | 2 +- Code/Editor/Util/MemoryBlock.cpp | 8 ++--- Code/Editor/Util/NamedData.cpp | 12 +++---- Code/Editor/Util/PakFile.cpp | 14 ++++----- Code/Editor/Util/PathUtil.cpp | 8 ++--- Code/Editor/Util/PathUtil.h | 12 +++---- Code/Editor/Util/StringHelpers.cpp | 20 ++++++------ Code/Editor/Util/UIEnumerations.cpp | 8 ++--- Code/Editor/Util/Variable.cpp | 10 +++--- Code/Editor/Util/Variable.h | 10 +++--- Code/Editor/Util/VariablePropertyType.cpp | 12 +++---- Code/Editor/Util/XmlHistoryManager.cpp | 34 ++++++++++---------- Code/Editor/Util/XmlHistoryManager.h | 20 ++++++------ Code/Editor/Util/XmlTemplate.cpp | 8 ++--- Code/Editor/Util/bitarray.h | 2 +- 36 files changed, 204 insertions(+), 204 deletions(-) diff --git a/Code/Editor/Undo/Undo.cpp b/Code/Editor/Undo/Undo.cpp index 5b3d2cd73a..6af141b9e7 100644 --- a/Code/Editor/Undo/Undo.cpp +++ b/Code/Editor/Undo/Undo.cpp @@ -34,7 +34,7 @@ public: { m_undoSteps.push_back(step); } - virtual int GetSize() const + int GetSize() const override { int size = 0; for (int i = 0; i < m_undoSteps.size(); i++) @@ -43,18 +43,18 @@ public: } return size; } - virtual bool IsEmpty() const + bool IsEmpty() const override { return m_undoSteps.empty(); } - virtual void Undo(bool bUndo) + void Undo(bool bUndo) override { for (int i = m_undoSteps.size() - 1; i >= 0; i--) { m_undoSteps[i]->Undo(bUndo); } } - virtual void Redo() + void Redo() override { for (int i = 0; i < m_undoSteps.size(); i++) { @@ -113,8 +113,8 @@ CUndoManager::CUndoManager() m_bRecording = false; m_bSuperRecording = false; - m_currentUndo = 0; - m_superUndo = 0; + m_currentUndo = nullptr; + m_superUndo = nullptr; m_assetManagerUndoInterruptor = new AssetManagerUndoInterruptor(); m_suspendCount = 0; @@ -270,7 +270,7 @@ void CUndoManager::Accept(const QString& name) } m_bRecording = false; - m_currentUndo = 0; + m_currentUndo = nullptr; SignalNumUndoRedoToListeners(); @@ -303,7 +303,7 @@ void CUndoManager::Cancel() } delete m_currentUndo; - m_currentUndo = 0; + m_currentUndo = nullptr; //CLogFile::WriteLine( " Cancel OK" ); } @@ -582,7 +582,7 @@ void CUndoManager::SuperAccept(const QString& name) //CLogFile::FormatLine( "Undo Object Accepted (Undo:%d,Redo:%d)",m_undoStack.size(),m_redoStack.size() ); m_bSuperRecording = false; - m_superUndo = 0; + m_superUndo = nullptr; //CLogFile::WriteLine( " SupperAccept OK" ); SignalNumUndoRedoToListeners(); @@ -616,7 +616,7 @@ void CUndoManager::SuperCancel() m_bSuperRecording = false; delete m_superUndo; - m_superUndo = 0; + m_superUndo = nullptr; //CLogFile::WriteLine( " SuperCancel OK" ); } @@ -708,8 +708,8 @@ void CUndoManager::Flush() delete m_superUndo; delete m_currentUndo; - m_superUndo = 0; - m_currentUndo = 0; + m_superUndo = nullptr; + m_currentUndo = nullptr; SignalUndoFlushedToListeners(); } diff --git a/Code/Editor/Undo/Undo.h b/Code/Editor/Undo/Undo.h index 3f943f7a82..e7b6c5b852 100644 --- a/Code/Editor/Undo/Undo.h +++ b/Code/Editor/Undo/Undo.h @@ -116,7 +116,7 @@ public: continue; } - if (m_undoObjects[i]->GetObjectName() == NULL) + if (m_undoObjects[i]->GetObjectName() == nullptr) { continue; } @@ -209,7 +209,7 @@ public: bool IsHaveUndo() const; bool IsHaveRedo() const; - + void SetMaxUndoStep(int steps); int GetMaxUndoStep() const; diff --git a/Code/Editor/Util/3DConnexionDriver.cpp b/Code/Editor/Util/3DConnexionDriver.cpp index 26f45a4912..c1c3b47c4b 100644 --- a/Code/Editor/Util/3DConnexionDriver.cpp +++ b/Code/Editor/Util/3DConnexionDriver.cpp @@ -34,12 +34,12 @@ bool C3DConnexionDriver::InitDevice() // Find the Raw Devices UINT nDevices; // Get Number of devices attached - if (GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0) + if (GetRawInputDeviceList(nullptr, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0) { return false; } // Create list large enough to hold all RAWINPUTDEVICE structs - if ((m_pRawInputDeviceList = (PRAWINPUTDEVICELIST)malloc(sizeof(RAWINPUTDEVICELIST) * nDevices)) == NULL) + if ((m_pRawInputDeviceList = (PRAWINPUTDEVICELIST)malloc(sizeof(RAWINPUTDEVICELIST) * nDevices)) == nullptr) { return false; } @@ -85,7 +85,7 @@ bool C3DConnexionDriver::InitDevice() m_pRawInputDevices[m_nUsagePage1Usage8Devices].usUsagePage = phidInfo->usUsagePage; m_pRawInputDevices[m_nUsagePage1Usage8Devices].usUsage = phidInfo->usUsage; m_pRawInputDevices[m_nUsagePage1Usage8Devices].dwFlags = 0; - m_pRawInputDevices[m_nUsagePage1Usage8Devices].hwndTarget = NULL; + m_pRawInputDevices[m_nUsagePage1Usage8Devices].hwndTarget = nullptr; m_nUsagePage1Usage8Devices++; } } @@ -126,8 +126,8 @@ bool C3DConnexionDriver::GetInputMessageData(LPARAM lParam, S3DConnexionMessage& { if (event->header.dwType == RIM_TYPEHID) { - static BOOL bGotTranslation = FALSE, - bGotRotation = FALSE; + static bool bGotTranslation = false, + bGotRotation = false; static int all6DOFs[6] = {0}; LPRAWHID pRawHid = &event->data.hid; diff --git a/Code/Editor/Util/DynamicArray2D.cpp b/Code/Editor/Util/DynamicArray2D.cpp index 6d9b1683f2..6ac3edbb6b 100644 --- a/Code/Editor/Util/DynamicArray2D.cpp +++ b/Code/Editor/Util/DynamicArray2D.cpp @@ -58,7 +58,7 @@ CDynamicArray2D::~CDynamicArray2D() } delete [] m_Array; - m_Array = 0; + m_Array = nullptr; } diff --git a/Code/Editor/Util/EditorUtils.cpp b/Code/Editor/Util/EditorUtils.cpp index 8f56e4d956..33c311ed76 100644 --- a/Code/Editor/Util/EditorUtils.cpp +++ b/Code/Editor/Util/EditorUtils.cpp @@ -42,14 +42,14 @@ void HeapCheck::Check([[maybe_unused]] const char* file, [[maybe_unused]] int li { CString str; str.Format( "Bad Start of Heap, at file %s line:%d",file,line ); - MessageBox( NULL,str,"Heap Check",MB_OK ); + MessageBox( nullptr,str,"Heap Check",MB_OK ); } break; case _HEAPBADNODE: { CString str; str.Format( "Bad Node in Heap, at file %s line:%d",file,line ); - MessageBox( NULL,str,"Heap Check",MB_OK ); + MessageBox( nullptr,str,"Heap Check",MB_OK ); } break; } @@ -258,7 +258,7 @@ namespace EditorUtils AzWarningAbsorber::AzWarningAbsorber(const char* window) : m_window(window) AZ_POP_DISABLE_WARNING - { + { BusConnect(); } diff --git a/Code/Editor/Util/EditorUtils.h b/Code/Editor/Util/EditorUtils.h index fb767c98b3..355a1939c2 100644 --- a/Code/Editor/Util/EditorUtils.h +++ b/Code/Editor/Util/EditorUtils.h @@ -359,7 +359,7 @@ inline QString TokenizeString(const QString& s, LPCSTR pszTokens, int& iStart) QByteArray str = s.toUtf8(); - if (pszTokens == NULL) + if (pszTokens == nullptr) { return str; } @@ -472,7 +472,7 @@ inline const char* strstri(const char* pString, const char* pSubstring) } } - return NULL; + return nullptr; } @@ -542,7 +542,7 @@ public: // There is a bug in QT with writing files larger than 32MB. It separates // the write into 32MB blocks, but doesn't write the last block correctly. // To deal with this, we'll separate into blocks here so QT doesn't have to. - + // QT bug in qfileengine_win.cpp line 434. Block size is calculated once and always // used as the amount of data to write, but for the last block, unless there is exactly // block size left to write, the actual remaining amount needs to be written, not the @@ -658,14 +658,14 @@ inline CArchive& operator>>(CArchive& ar, QString& str) str = QString::fromUtf16(reinterpret_cast(raw), aznumeric_cast(length)); } } - + return ar; } inline CArchive& operator<<(CArchive& ar, const QString& str) { // This is written to mimic how MFC archiving worked, which was to - // write markers to indicate the size of the length - + // write markers to indicate the size of the length - // so a length that will fit into 8 bits takes 8 bits. // A length that requires more than 8 bits, puts an 8 bit marker (0xff) // to indicate that the length is greater, then 16 bits for the length. @@ -693,7 +693,7 @@ inline CArchive& operator<<(CArchive& ar, const QString& str) ar << static_cast(0xffff); ar << static_cast(length); } - + ar.device()->write(data); return ar; diff --git a/Code/Editor/Util/FileChangeMonitor.cpp b/Code/Editor/Util/FileChangeMonitor.cpp index c2448e5dac..8a1f961bb0 100644 --- a/Code/Editor/Util/FileChangeMonitor.cpp +++ b/Code/Editor/Util/FileChangeMonitor.cpp @@ -16,7 +16,7 @@ #include -CFileChangeMonitor* CFileChangeMonitor::s_pFileMonitorInstance = NULL; +CFileChangeMonitor* CFileChangeMonitor::s_pFileMonitorInstance = nullptr; ////////////////////////////////////////////////////////////////////////// CFileChangeMonitor::CFileChangeMonitor(QObject* parent) @@ -34,7 +34,7 @@ CFileChangeMonitor::~CFileChangeMonitor() if (pListener) { - pListener->SetMonitor(NULL); + pListener->SetMonitor(nullptr); } } @@ -143,7 +143,7 @@ void CFileChangeMonitor::Unsubscribe(CFileChangeMonitorListener* pListener) { assert(pListener); m_listeners.erase(pListener); - pListener->SetMonitor(NULL); + pListener->SetMonitor(nullptr); } void CFileChangeMonitor::OnDirectoryChange(const QString &path) diff --git a/Code/Editor/Util/FileChangeMonitor.h b/Code/Editor/Util/FileChangeMonitor.h index c2746dc30a..b32686bec4 100644 --- a/Code/Editor/Util/FileChangeMonitor.h +++ b/Code/Editor/Util/FileChangeMonitor.h @@ -111,7 +111,7 @@ class CFileChangeMonitorListener { public: CFileChangeMonitorListener() - : m_pMonitor(NULL) + : m_pMonitor(nullptr) { } diff --git a/Code/Editor/Util/FileEnum.cpp b/Code/Editor/Util/FileEnum.cpp index 1d13929889..98ff0f7f4e 100644 --- a/Code/Editor/Util/FileEnum.cpp +++ b/Code/Editor/Util/FileEnum.cpp @@ -12,7 +12,7 @@ #include "FileEnum.h" CFileEnum::CFileEnum() - : m_hEnumFile(0) + : m_hEnumFile(nullptr) { } @@ -21,7 +21,7 @@ CFileEnum::~CFileEnum() if (m_hEnumFile) { delete m_hEnumFile; - m_hEnumFile = 0; + m_hEnumFile = nullptr; } } @@ -53,7 +53,7 @@ bool CFileEnum::StartEnumeration(const QString& szEnumPathAndPattern, QFileInfo* if (m_hEnumFile) { delete m_hEnumFile; - m_hEnumFile = 0; + m_hEnumFile = nullptr; } QStringList parts = szEnumPathAndPattern.split(QRegularExpression(R"([\\/])")); @@ -66,7 +66,7 @@ bool CFileEnum::StartEnumeration(const QString& szEnumPathAndPattern, QFileInfo* { // No files found delete m_hEnumFile; - m_hEnumFile = 0; + m_hEnumFile = nullptr; return false; } @@ -84,7 +84,7 @@ bool CFileEnum::GetNextFile(QFileInfo* pFile) { // No more files left delete m_hEnumFile; - m_hEnumFile = 0; + m_hEnumFile = nullptr; return false; } diff --git a/Code/Editor/Util/FileUtil.cpp b/Code/Editor/Util/FileUtil.cpp index 9e4f688f69..32154ec66d 100644 --- a/Code/Editor/Util/FileUtil.cpp +++ b/Code/Editor/Util/FileUtil.cpp @@ -279,7 +279,7 @@ void CFileUtil::EditTextureFile(const char* textureFile, [[maybe_unused]] bool b // Qt does. QString fullTexturePathFixedForWindows = QString(fullTexturePath.data()).replace('/', '\\'); QByteArray fullTexturePathFixedForWindowsUtf8 = fullTexturePathFixedForWindows.toUtf8(); - HINSTANCE hInst = ShellExecute(NULL, "open", textureEditorPath.data(), fullTexturePathFixedForWindowsUtf8.data(), NULL, SW_SHOWNORMAL); + HINSTANCE hInst = ShellExecute(nullptr, "open", textureEditorPath.data(), fullTexturePathFixedForWindowsUtf8.data(), nullptr, SW_SHOWNORMAL); failedToLaunch = ((DWORD_PTR)hInst <= 32); #elif defined(AZ_PLATFORM_MAC) failedToLaunch = QProcess::execute(QString("/usr/bin/open"), {"-a", gSettings.textureEditor, QString(fullTexturePath.data()) }) != 0; @@ -332,7 +332,7 @@ bool CFileUtil::EditMayaFile(const char* filepath, const bool bExtractFromPak, c CryMessageBox("Can't open the file. You can specify a source editor in Sandbox Preferences or create an association in Windows.", "Cannot open file!", MB_OK | MB_ICONERROR); } } - return TRUE; + return true; } ////////////////////////////////////////////////////////////////////////// @@ -348,10 +348,10 @@ bool CFileUtil::EditFile(const char* filePath, const bool bExtrackFromPak, const else if ((extension.compare(".bspace") == 0) || (extension.compare(".comb") == 0)) { EditTextFile(filePath, 0, IFileUtil::FILE_TYPE_BSPACE); - return TRUE; + return true; } - return FALSE; + return false; } ////////////////////////////////////////////////////////////////////////// @@ -374,7 +374,7 @@ bool CFileUtil::CalculateDccFilename(const QString& assetFilename, QString& dccF ////////////////////////////////////////////////////////////////////////// bool CFileUtil::ExtractDccFilenameFromAssetDatabase(const QString& assetFilename, QString& dccFilename) { - IAssetItemDatabase* pCurrentDatabaseInterface = NULL; + IAssetItemDatabase* pCurrentDatabaseInterface = nullptr; std::vector assetDatabasePlugins; IEditorClassFactory* pClassFactory = GetIEditor()->GetClassFactory(); pClassFactory->GetClassesByCategory("Asset Item DB", assetDatabasePlugins); @@ -592,7 +592,7 @@ inline bool ScanDirectoryFiles(const QString& root, const QString& path, const Q /* CFileFind finder; - BOOL bWorking = finder.FindFile( Path::Make(dir,fileSpec) ); + bool bWorking = finder.FindFile( Path::Make(dir,fileSpec) ); while (bWorking) { bWorking = finder.FindNextFile(); @@ -663,7 +663,7 @@ inline int ScanDirectoryRecursive(const QString& root, const QString& path, cons { /* CFileFind finder; - BOOL bWorking = finder.FindFile( Path::Make(dir,"*.*") ); + bool bWorking = finder.FindFile( Path::Make(dir,"*.*") ); while (bWorking) { bWorking = finder.FindNextFile(); @@ -847,12 +847,12 @@ void BlockAndWait(const bool& opComplete, QWidget* parent, const char* message) { // note that 16ms below is not the amount of time to wait, its the maximum time that // processEvents is allowed to keep processing them if they just keep being emitted. - // adding a maximum time here means that we get an opportunity to pump the TickBus + // adding a maximum time here means that we get an opportunity to pump the TickBus // periodically even during a flood of events. QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 16); AZ::TickBus::ExecuteQueuedEvents(); } - + // if we are not the main thread then the above will be done by the main thread, and we can just wait for it to happen. // its fairly important we don't sleep for really long because this legacy code is often invoked in a blocking loop // for many items, and in the worst case, any time we spend sleeping here will be added to each item. @@ -1206,10 +1206,10 @@ bool CFileUtil::CreatePath(const QString& strPath) QString strFilename; QString strExtension; QString strCurrentDirectoryPath; - QStringList cstrDirectoryQueue; + QStringList cstrDirectoryQueue; size_t nCurrentPathQueue(0); size_t nTotalPathQueueElements(0); - BOOL bnLastDirectoryWasCreated(FALSE); + bool bnLastDirectoryWasCreated(false); if (PathExists(strPath)) { @@ -1361,7 +1361,7 @@ IFileUtil::ECopyTreeResult CFileUtil::CopyTree(const QString& strSourceDirectory nTotal = cFiles.size(); for (nCurrent = 0; nCurrent < nTotal; ++nCurrent) { - BOOL bnLastFileWasCopied(FALSE); + bool bnLastFileWasCopied(false); if (eCopyResult == IFileUtil::ETREECOPYUSERCANCELED) @@ -1447,7 +1447,7 @@ IFileUtil::ECopyTreeResult CFileUtil::CopyTree(const QString& strSourceDirectory return eCopyResult; } - BOOL bnLastDirectoryWasCreated(FALSE); + bool bnLastDirectoryWasCreated(false); QString sourceName = sourceDir.absoluteFilePath(cDirectories[nCurrent]); QString targetName = targetDir.absoluteFilePath(cDirectories[nCurrent]); @@ -1529,7 +1529,7 @@ IFileUtil::ECopyTreeResult CFileUtil::CopyFile(const QString& strSourceFile, c CUserOptions oFileOptions; IFileUtil::ECopyTreeResult eCopyResult(IFileUtil::ETREECOPYOK); - BOOL bnLastFileWasCopied(FALSE); + bool bnLastFileWasCopied(false); QString name(strSourceFile); QString strQueryFilename; QString strFullStargetName; @@ -1658,7 +1658,7 @@ IFileUtil::ECopyTreeResult CFileUtil::CopyFile(const QString& strSourceFile, c } if (pfnProgress) { - pfnProgress(source.size(), totalRead, 0, 0, 0, 0, 0, 0, 0); + pfnProgress(source.size(), totalRead, 0, 0, 0, 0, nullptr, nullptr, nullptr); } } if (totalRead != source.size()) @@ -1742,7 +1742,7 @@ IFileUtil::ECopyTreeResult CFileUtil::MoveTree(const QString& strSourceDirecto return eCopyResult; } - BOOL bnLastFileWasCopied(FALSE); + bool bnLastFileWasCopied(false); QString sourceName(sourceDir.absoluteFilePath(cFiles[nCurrent])); QString targetName(targetDir.absoluteFilePath(cFiles[nCurrent])); @@ -1816,7 +1816,7 @@ IFileUtil::ECopyTreeResult CFileUtil::MoveTree(const QString& strSourceDirecto nTotal = cDirectories.size(); for (nCurrent = 0; nCurrent < nTotal; ++nCurrent) { - BOOL bnLastDirectoryWasCreated(FALSE); + bool bnLastDirectoryWasCreated(false); if (eCopyResult == IFileUtil::ETREECOPYUSERCANCELED) { diff --git a/Code/Editor/Util/FileUtil.h b/Code/Editor/Util/FileUtil.h index 000215e98d..210d702f71 100644 --- a/Code/Editor/Util/FileUtil.h +++ b/Code/Editor/Util/FileUtil.h @@ -123,7 +123,7 @@ public: ////////////////////////////////////////////////////////////////////////// // @param LPPROGRESS_ROUTINE pfnProgress - called by the system to notify of file copy progress - // @param LPBOOL pbCancel - when the contents of this BOOL are set to TRUE, the system cancels the copy operation + // @param LPBOOL pbCancel - when the contents of this BOOL are set to true, the system cancels the copy operation static IFileUtil::ECopyTreeResult CopyFile(const QString& strSourceFile, const QString& strTargetFile, bool boConfirmOverwrite = false, ProgressRoutine pfnProgress = nullptr, bool* pbCancel = nullptr); diff --git a/Code/Editor/Util/FileUtil_impl.h b/Code/Editor/Util/FileUtil_impl.h index 2f2872ff6f..04d9e829b9 100644 --- a/Code/Editor/Util/FileUtil_impl.h +++ b/Code/Editor/Util/FileUtil_impl.h @@ -110,8 +110,8 @@ public: ////////////////////////////////////////////////////////////////////////// // @param LPPROGRESS_ROUTINE pfnProgress - called by the system to notify of file copy progress - // @param LPBOOL pbCancel - when the contents of this BOOL are set to TRUE, the system cancels the copy operation - ECopyTreeResult CopyFile(const QString& strSourceFile, const QString& strTargetFile, bool boConfirmOverwrite = false, ProgressRoutine pfnProgress = NULL, bool* pbCancel = NULL) override; + // @param LPBOOL pbCancel - when the contents of this BOOL are set to true, the system cancels the copy operation + ECopyTreeResult CopyFile(const QString& strSourceFile, const QString& strTargetFile, bool boConfirmOverwrite = false, ProgressRoutine pfnProgress = nullptr, bool* pbCancel = nullptr) override; // As we don't have a FileUtil interface here, we have to duplicate some code :-( in order to keep // function calls clean. diff --git a/Code/Editor/Util/GdiUtil.h b/Code/Editor/Util/GdiUtil.h index f61781f7d4..55165b5799 100644 --- a/Code/Editor/Util/GdiUtil.h +++ b/Code/Editor/Util/GdiUtil.h @@ -35,7 +35,7 @@ public: ~CAlphaBitmap(); //! creates the bitmap from raw 32bpp data - //! \param pData the 32bpp raw image data, RGBA, can be NULL and it would create just an empty bitmap + //! \param pData the 32bpp raw image data, RGBA, can be nullptr and it would create just an empty bitmap //! \param aWidth the bitmap width //! \param aHeight the bitmap height bool Create(void* pData, UINT aWidth, UINT aHeight, bool bVerticalFlip = false, bool bPremultiplyAlpha = false); diff --git a/Code/Editor/Util/IXmlHistoryManager.h b/Code/Editor/Util/IXmlHistoryManager.h index 78d849df7d..20f9dd4573 100644 --- a/Code/Editor/Util/IXmlHistoryManager.h +++ b/Code/Editor/Util/IXmlHistoryManager.h @@ -37,7 +37,7 @@ struct IXmlHistoryEventListener eHET_HistoryGroupAdded, eHET_HistoryGroupRemoved, }; - virtual void OnEvent(EHistoryEventType event, void* pData = NULL) = 0; + virtual void OnEvent(EHistoryEventType event, void* pData = nullptr) = 0; }; struct IXmlHistoryView diff --git a/Code/Editor/Util/ImageASC.cpp b/Code/Editor/Util/ImageASC.cpp index 69a6892c65..c018018196 100644 --- a/Code/Editor/Util/ImageASC.cpp +++ b/Code/Editor/Util/ImageASC.cpp @@ -105,34 +105,34 @@ bool CImageASC::Load(const QString& fileName, CFloatImage& image) // ncols = grid width validData = validData && (azstricmp(token, "ncols") == 0); - token = azstrtok(NULL, 0, seps, &nextToken); + token = azstrtok(nullptr, 0, seps, &nextToken); width = atoi(token); // nrows = grid height - token = azstrtok(NULL, 0, seps, &nextToken); + token = azstrtok(nullptr, 0, seps, &nextToken); validData = validData && (azstricmp(token, "nrows") == 0); - token = azstrtok(NULL, 0, seps, &nextToken); + token = azstrtok(nullptr, 0, seps, &nextToken); height = atoi(token); // xllcorner = leftmost coordinate. (Skip, we don't care about it) - token = azstrtok(NULL, 0, seps, &nextToken); + token = azstrtok(nullptr, 0, seps, &nextToken); validData = validData && (azstricmp(token, "xllcorner") == 0); - token = azstrtok(NULL, 0, seps, &nextToken); + token = azstrtok(nullptr, 0, seps, &nextToken); // yllcorner = bottommost coordinate. (Skip, we don't care about it) - token = azstrtok(NULL, 0, seps, &nextToken); + token = azstrtok(nullptr, 0, seps, &nextToken); validData = validData && (azstricmp(token, "yllcorner") == 0); - token = azstrtok(NULL, 0, seps, &nextToken); + token = azstrtok(nullptr, 0, seps, &nextToken); // cellsize = size of each grid cell. (Skip, we don't care about it) - token = azstrtok(NULL, 0, seps, &nextToken); + token = azstrtok(nullptr, 0, seps, &nextToken); validData = validData && (azstricmp(token, "cellsize") == 0); - token = azstrtok(NULL, 0, seps, &nextToken); + token = azstrtok(nullptr, 0, seps, &nextToken); // nodata_value = the value used for missing data. We'll replace these with 0 height. - token = azstrtok(NULL, 0, seps, &nextToken); + token = azstrtok(nullptr, 0, seps, &nextToken); validData = validData && (azstricmp(token, "nodata_value") == 0); - token = azstrtok(NULL, 0, seps, &nextToken); + token = azstrtok(nullptr, 0, seps, &nextToken); nodataValue = atof(token); if (!validData) @@ -152,10 +152,10 @@ bool CImageASC::Load(const QString& fileName, CFloatImage& image) int i = 0; float pixelValue; float maxPixel = 0.0f; - while (token != NULL && i < size) + while (token != nullptr && i < size) { - token = azstrtok(NULL, 0, seps, &nextToken); - if (token != NULL) + token = azstrtok(nullptr, 0, seps, &nextToken); + if (token != nullptr) { // Negative heights aren't supported, clamp to 0. pixelValue = max(0.0, atof(token)); diff --git a/Code/Editor/Util/ImageGif.cpp b/Code/Editor/Util/ImageGif.cpp index a0cd6c5650..2c07fc9acb 100644 --- a/Code/Editor/Util/ImageGif.cpp +++ b/Code/Editor/Util/ImageGif.cpp @@ -223,7 +223,7 @@ bool CImageGif::Load(const QString& fileName, CImageEx& outImage) Pass = 0; OutCount = 0; - Palette = NULL; + Palette = nullptr; CHK (Raster = new uint8 [filesize]); if (strncmp((char*) ptr, id87, 6)) diff --git a/Code/Editor/Util/ImageTIF.cpp b/Code/Editor/Util/ImageTIF.cpp index 9040306a44..c4f21855a4 100644 --- a/Code/Editor/Util/ImageTIF.cpp +++ b/Code/Editor/Util/ImageTIF.cpp @@ -142,7 +142,7 @@ bool CImageTIF::Load(const QString& fileName, CImageEx& outImage) uint32 dwWidth, dwHeight; size_t npixels; uint32* raster; - char* dccfilename = NULL; + char* dccfilename = nullptr; TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &dwWidth); TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &dwHeight); @@ -232,7 +232,7 @@ bool CImageTIF::Load(const QString& fileName, CFloatImage& outImage) { uint32 width = 0, height = 0; uint16 spp = 0, bpp = 0, format = 0; - char* dccfilename = NULL; + char* dccfilename = nullptr; TIFFGetField(tif, TIFFTAG_IMAGEDESCRIPTION, &dccfilename); @@ -252,11 +252,11 @@ bool CImageTIF::Load(const QString& fileName, CFloatImage& outImage) // Check to see if it's a GeoTIFF, and if so, whether or not it has the ZScale parameter. uint32 tagCount = 0; - double *pixelScales = NULL; + double *pixelScales = nullptr; if (TIFFGetField(tif, GEOTIFF_MODELPIXELSCALE_TAG, &tagCount, &pixelScales) == 1) { // if there's an xyz scale, and the Z scale isn't 0, let's use it. - if ((tagCount == 3) && (pixelScales != NULL) && (pixelScales[2] != 0.0f)) + if ((tagCount == 3) && (pixelScales != nullptr) && (pixelScales[2] != 0.0f)) { pixelValueScale = static_cast(pixelScales[2]); } @@ -455,7 +455,7 @@ const char* CImageTIF::GetPreset(const QString& fileName) if (!file.Open(fileName.toUtf8().data(), "rb")) { CLogFile::FormatLine("File not found %s", fileName.toUtf8().data()); - return NULL; + return nullptr; } MemImage memImage; @@ -473,7 +473,7 @@ const char* CImageTIF::GetPreset(const QString& fileName) libtiffDummyCloseProc, libtiffDummySizeProc, libtiffDummyMapFileProc, libtiffDummyUnmapFileProc); string strReturn; - char* preset = NULL; + char* preset = nullptr; int size; if (tif) { diff --git a/Code/Editor/Util/ImageUtil.cpp b/Code/Editor/Util/ImageUtil.cpp index 1b985c82f9..3d121a1d47 100644 --- a/Code/Editor/Util/ImageUtil.cpp +++ b/Code/Editor/Util/ImageUtil.cpp @@ -149,13 +149,13 @@ bool CImageUtil::LoadPGM(const QString& fileName, CImageEx& image) char* nextToken = nullptr; token = azstrtok(str, 0, seps, &nextToken); - while (token != NULL && token[0] == '#') + while (token != nullptr && token[0] == '#') { - if (token != NULL && token[0] == '#') + if (token != nullptr && token[0] == '#') { - azstrtok(NULL, 0, "\n", &nextToken); + azstrtok(nullptr, 0, "\n", &nextToken); } - token = azstrtok(NULL, 0, seps, &nextToken); + token = azstrtok(nullptr, 0, seps, &nextToken); } if (azstricmp(token, "P2") != 0) { @@ -167,32 +167,32 @@ bool CImageUtil::LoadPGM(const QString& fileName, CImageEx& image) do { - token = azstrtok(NULL, 0, seps, &nextToken); - if (token != NULL && token[0] == '#') + token = azstrtok(nullptr, 0, seps, &nextToken); + if (token != nullptr && token[0] == '#') { - azstrtok(NULL, 0, "\n", &nextToken); + azstrtok(nullptr, 0, "\n", &nextToken); } - } while (token != NULL && token[0] == '#'); + } while (token != nullptr && token[0] == '#'); width = atoi(token); do { - token = azstrtok(NULL, 0, seps, &nextToken); - if (token != NULL && token[0] == '#') + token = azstrtok(nullptr, 0, seps, &nextToken); + if (token != nullptr && token[0] == '#') { - azstrtok(NULL, 0, "\n", &nextToken); + azstrtok(nullptr, 0, "\n", &nextToken); } - } while (token != NULL && token[0] == '#'); + } while (token != nullptr && token[0] == '#'); height = atoi(token); do { - token = azstrtok(NULL, 0, seps, &nextToken); - if (token != NULL && token[0] == '#') + token = azstrtok(nullptr, 0, seps, &nextToken); + if (token != nullptr && token[0] == '#') { - azstrtok(NULL, 0, "\n", &nextToken); + azstrtok(nullptr, 0, "\n", &nextToken); } - } while (token != NULL && token[0] == '#'); + } while (token != nullptr && token[0] == '#'); numColors = atoi(token); image.Allocate(width, height); @@ -200,12 +200,12 @@ bool CImageUtil::LoadPGM(const QString& fileName, CImageEx& image) uint32* p = image.GetData(); int size = width * height; int i = 0; - while (token != NULL && i < size) + while (token != nullptr && i < size) { do { - token = azstrtok(NULL, 0, seps, &nextToken); - } while (token != NULL && token[0] == '#'); + token = azstrtok(nullptr, 0, seps, &nextToken); + } while (token != nullptr && token[0] == '#'); *p++ = atoi(token); i++; } diff --git a/Code/Editor/Util/IndexedFiles.cpp b/Code/Editor/Util/IndexedFiles.cpp index c90b3c5f20..ae777abb49 100644 --- a/Code/Editor/Util/IndexedFiles.cpp +++ b/Code/Editor/Util/IndexedFiles.cpp @@ -14,7 +14,7 @@ #include "IndexedFiles.h" volatile TIntAtomic CIndexedFiles::s_bIndexingDone; -CIndexedFiles* CIndexedFiles::s_pIndexedFiles = NULL; +CIndexedFiles* CIndexedFiles::s_pIndexedFiles = nullptr; bool CIndexedFiles::m_startedFileIndexing = false; diff --git a/Code/Editor/Util/IndexedFiles.h b/Code/Editor/Util/IndexedFiles.h index 34554dbe37..e23c0ea827 100644 --- a/Code/Editor/Util/IndexedFiles.h +++ b/Code/Editor/Util/IndexedFiles.h @@ -88,7 +88,7 @@ public: } public: - void Initialize(const QString& path, IFileUtil::ScanDirectoryUpdateCallBack updateCB = NULL); + void Initialize(const QString& path, IFileUtil::ScanDirectoryUpdateCallBack updateCB = nullptr); // Adds a new file to the database. void AddFile(const IFileUtil::FileDesc& path); diff --git a/Code/Editor/Util/KDTree.cpp b/Code/Editor/Util/KDTree.cpp index c8fe742370..4547149e9b 100644 --- a/Code/Editor/Util/KDTree.cpp +++ b/Code/Editor/Util/KDTree.cpp @@ -17,9 +17,9 @@ class KDTreeNode public: KDTreeNode() { - pChildren[0] = NULL; - pChildren[1] = NULL; - pVertexIndices = NULL; + pChildren[0] = nullptr; + pChildren[1] = nullptr; + pVertexIndices = nullptr; } ~KDTreeNode() { @@ -76,13 +76,13 @@ public: } bool IsLeaf() const { - return pChildren[0] == NULL && pChildren[1] == NULL; + return pChildren[0] == nullptr && pChildren[1] == nullptr; } KDTreeNode* GetChild(uint32 nIndex) const { if (nIndex > 1) { - return NULL; + return nullptr; } return pChildren[nIndex]; } @@ -200,7 +200,7 @@ bool SearchForBestSplitPos(CKDTree::ESplitAxis axis, const std::vectorpStatObj->GetIndexedMesh(); - if (pMesh == NULL) + if (pMesh == nullptr) { continue; } @@ -256,7 +256,7 @@ bool SplitNode(const std::vector& statObjList, const AABB& bo const CKDTree::SStatObj* pObj = &statObjList[nObjIndex]; const IIndexedMesh* pMesh = pObj->pStatObj->GetIndexedMesh(); - if (pMesh == NULL) + if (pMesh == nullptr) { return false; } @@ -295,7 +295,7 @@ bool SplitNode(const std::vector& statObjList, const AABB& bo CKDTree::CKDTree() { - m_pRootNode = NULL; + m_pRootNode = nullptr; } CKDTree::~CKDTree() @@ -308,7 +308,7 @@ CKDTree::~CKDTree() bool CKDTree::Build(IStatObj* pStatObj) { - if (pStatObj == NULL) + if (pStatObj == nullptr) { return false; } @@ -332,7 +332,7 @@ bool CKDTree::Build(IStatObj* pStatObj) for (int i = 0, iStatObjSize(m_StatObjectList.size()); i < iStatObjSize; ++i) { IIndexedMesh* pMesh = m_StatObjectList[i].pStatObj->GetIndexedMesh(true); - if (pMesh == NULL) + if (pMesh == nullptr) { continue; } @@ -398,7 +398,7 @@ void CKDTree::BuildRecursively(KDTreeNode* pNode, const AABB& boundbox, std::vec void CKDTree::ConstructStatObjList(IStatObj* pStatObj, const Matrix34& matParent) { - if (pStatObj == NULL) + if (pStatObj == nullptr) { return; } @@ -472,7 +472,7 @@ bool CKDTree::FindNearestVertexRecursively(KDTreeNode* pNode, const Vec3& raySrc const SStatObj* pStatObjInfo = &(m_StatObjectList[nObjIndex]); IIndexedMesh* pMesh = m_StatObjectList[nObjIndex].pStatObj->GetIndexedMesh(); - if (pMesh == NULL) + if (pMesh == nullptr) { continue; } diff --git a/Code/Editor/Util/Math.h b/Code/Editor/Util/Math.h index 66bbf883e8..34fb77e434 100644 --- a/Code/Editor/Util/Math.h +++ b/Code/Editor/Util/Math.h @@ -128,7 +128,7 @@ inline float PointToLineDistance(const Vec3& p1, const Vec3& p2, const Vec3& p3, @param p2 Target point of first line. @param p3 Source point of second line. @param p4 Target point of second line. - @return FALSE if no solution exists. + @return false if no solution exists. */ inline bool LineLineIntersect(const Vec3& p1, const Vec3& p2, const Vec3& p3, const Vec3& p4, Vec3& pa, Vec3& pb, float& mua, float& mub) diff --git a/Code/Editor/Util/MemoryBlock.cpp b/Code/Editor/Util/MemoryBlock.cpp index 1368553511..03d432762d 100644 --- a/Code/Editor/Util/MemoryBlock.cpp +++ b/Code/Editor/Util/MemoryBlock.cpp @@ -18,7 +18,7 @@ ////////////////////////////////////////////////////////////////////////// CMemoryBlock::CMemoryBlock() - : m_buffer(0) + : m_buffer(nullptr) , m_size(0) , m_uncompressedSize(0) , m_owns(false) @@ -54,7 +54,7 @@ CMemoryBlock& CMemoryBlock::operator=(const CMemoryBlock& mem) } else { - m_buffer = 0; + m_buffer = nullptr; m_size = 0; m_owns = false; } @@ -104,7 +104,7 @@ bool CMemoryBlock::Allocate(int size, int uncompressedSize) m_size = size; m_uncompressedSize = uncompressedSize; // Check if allocation failed. - if (m_buffer == 0) + if (m_buffer == nullptr) { return false; } @@ -118,7 +118,7 @@ void CMemoryBlock::Free() { free(m_buffer); } - m_buffer = 0; + m_buffer = nullptr; m_owns = false; m_size = 0; m_uncompressedSize = 0; diff --git a/Code/Editor/Util/NamedData.cpp b/Code/Editor/Util/NamedData.cpp index 1f0d6c5a50..240d71f58d 100644 --- a/Code/Editor/Util/NamedData.cpp +++ b/Code/Editor/Util/NamedData.cpp @@ -36,7 +36,7 @@ void CNamedData::AddDataBlock(const QString& blockName, void* pData, int nSize assert(pData); assert(nSize > 0); - DataBlock* pBlock = stl::find_in_map(m_blocks, blockName, (DataBlock*)0); + DataBlock* pBlock = stl::find_in_map(m_blocks, blockName, (DataBlock*)nullptr); if (pBlock) { delete pBlock; @@ -66,7 +66,7 @@ void CNamedData::AddDataBlock(const QString& blockName, void* pData, int nSize void CNamedData::AddDataBlock(const QString& blockName, CMemoryBlock& mem) { - DataBlock* pBlock = stl::find_in_map(m_blocks, blockName, (DataBlock*)0); + DataBlock* pBlock = stl::find_in_map(m_blocks, blockName, (DataBlock*)nullptr); if (pBlock) { delete pBlock; @@ -102,7 +102,7 @@ void CNamedData::Clear() ////////////////////////////////////////////////////////////////////////// bool CNamedData::GetDataBlock(const QString& blockName, void*& pData, int& nSize) { - pData = 0; + pData = nullptr; nSize = 0; bool bUncompressed = false; @@ -119,10 +119,10 @@ bool CNamedData::GetDataBlock(const QString& blockName, void*& pData, int& nSize ////////////////////////////////////////////////////////////////////////// CMemoryBlock* CNamedData::GetDataBlock(const QString& blockName, bool& bCompressed) { - DataBlock* pBlock = stl::find_in_map(m_blocks, blockName, (DataBlock*)0); + DataBlock* pBlock = stl::find_in_map(m_blocks, blockName, (DataBlock*)nullptr); if (!pBlock) { - return 0; + return nullptr; } if (bCompressed) @@ -150,7 +150,7 @@ CMemoryBlock* CNamedData::GetDataBlock(const QString& blockName, bool& bCompress } } } - return 0; + return nullptr; } ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Editor/Util/PakFile.cpp b/Code/Editor/Util/PakFile.cpp index 5c26b9a7a2..88d5598495 100644 --- a/Code/Editor/Util/PakFile.cpp +++ b/Code/Editor/Util/PakFile.cpp @@ -21,14 +21,14 @@ ////////////////////////////////////////////////////////////////////////// CPakFile::CPakFile() - : m_pArchive(NULL) - , m_pCryPak(NULL) + : m_pArchive(nullptr) + , m_pCryPak(nullptr) { } ////////////////////////////////////////////////////////////////////////// CPakFile::CPakFile(AZ::IO::IArchive* pCryPak) - : m_pArchive(NULL) + : m_pArchive(nullptr) , m_pCryPak(pCryPak) { } @@ -42,14 +42,14 @@ CPakFile::~CPakFile() ////////////////////////////////////////////////////////////////////////// CPakFile::CPakFile(const char* filename) { - m_pArchive = NULL; + m_pArchive = nullptr; Open(filename); } ////////////////////////////////////////////////////////////////////////// void CPakFile::Close() { - m_pArchive = NULL; + m_pArchive = nullptr; } ////////////////////////////////////////////////////////////////////////// @@ -61,7 +61,7 @@ bool CPakFile::Open(const char* filename, bool bAbsolutePath) } auto pCryPak = m_pCryPak ? m_pCryPak : GetIEditor()->GetSystem()->GetIPak(); - if (pCryPak == NULL) + if (pCryPak == nullptr) { return false; } @@ -89,7 +89,7 @@ bool CPakFile::OpenForRead(const char* filename) Close(); } auto pCryPak = m_pCryPak ? m_pCryPak : GetIEditor()->GetSystem()->GetIPak(); - if (pCryPak == NULL) + if (pCryPak == nullptr) { return false; } diff --git a/Code/Editor/Util/PathUtil.cpp b/Code/Editor/Util/PathUtil.cpp index 4bb5c20ece..2be09da06f 100644 --- a/Code/Editor/Util/PathUtil.cpp +++ b/Code/Editor/Util/PathUtil.cpp @@ -42,7 +42,7 @@ namespace Path // Directory named filenames containing ":" are invalid, so we can assume if there is a : // it will be the drive name. pchCurrentPosition = strchr(pchLastPosition, ':'); - if (pchCurrentPosition == NULL) + if (pchCurrentPosition == nullptr) { rstrDriveLetter = ""; } @@ -54,7 +54,7 @@ namespace Path pchCurrentPosition = strrchr(pchLastPosition, '\\'); pchAuxPosition = strrchr(pchLastPosition, '/'); - if ((pchCurrentPosition == NULL) && (pchAuxPosition == NULL)) + if ((pchCurrentPosition == nullptr) && (pchAuxPosition == nullptr)) { rstrDirectory = ""; } @@ -70,7 +70,7 @@ namespace Path } pchCurrentPosition = strrchr(pchLastPosition, '.'); - if (pchCurrentPosition == NULL) + if (pchCurrentPosition == nullptr) { rstrExtension = ""; strFilename.assign(pchLastPosition); @@ -114,7 +114,7 @@ namespace Path do { pchCurrentPosition = strpbrk(pchLastPosition, "\\/"); - if (pchCurrentPosition == NULL) + if (pchCurrentPosition == nullptr) { break; } diff --git a/Code/Editor/Util/PathUtil.h b/Code/Editor/Util/PathUtil.h index 0f87df0d5c..868d9843ab 100644 --- a/Code/Editor/Util/PathUtil.h +++ b/Code/Editor/Util/PathUtil.h @@ -228,7 +228,7 @@ namespace Path { return (path.endsWith(QStringLiteral("\\")) || path.endsWith(QStringLiteral("/"))); } - + template inline bool EndsWithSlash(CryStackStringT* path) { @@ -236,15 +236,15 @@ namespace Path { return false; } - + if ( ((*path)[path->size() - 1] != '\\') || - ((*path)[path->size() - 1] != '/') + ((*path)[path->size() - 1] != '/') ) { return true; } - + return false; } @@ -336,9 +336,9 @@ namespace Path { char path_buffer[_MAX_PATH]; #ifdef AZ_COMPILER_MSVC - _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), NULL, dir.toUtf8().data(), filename.toUtf8().data(), ext.toUtf8().data()); + _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), nullptr, dir.toUtf8().data(), filename.toUtf8().data(), ext.toUtf8().data()); #else - _makepath(path_buffer, NULL, dir.toUtf8().data(), filename.toUtf8().data(), ext.toUtf8().data()); + _makepath(path_buffer, nullptr, dir.toUtf8().data(), filename.toUtf8().data(), ext.toUtf8().data()); #endif return CaselessPaths(path_buffer); } diff --git a/Code/Editor/Util/StringHelpers.cpp b/Code/Editor/Util/StringHelpers.cpp index 836090e923..5d37dfef77 100644 --- a/Code/Editor/Util/StringHelpers.cpp +++ b/Code/Editor/Util/StringHelpers.cpp @@ -48,7 +48,7 @@ static inline int Vscprintf(const char* format, va_list argList) int retval; va_list argcopy; va_copy(argcopy, argList); - retval = azvsnprintf(NULL, 0, format, argcopy); + retval = azvsnprintf(nullptr, 0, format, argcopy); va_end(argcopy); return retval; #else @@ -64,7 +64,7 @@ static inline int Vscprintf(const wchar_t* format, va_list argList) int retval; va_list argcopy; va_copy(argcopy, argList); - retval = azvsnwprintf(NULL, 0, format, argcopy); + retval = azvsnwprintf(nullptr, 0, format, argcopy); va_end(argcopy); return retval; #else @@ -408,9 +408,9 @@ bool StringHelpers::MatchesWildcardsIgnoreCase(const wstring& str, const wstring template static inline bool MatchesWildcardsIgnoreCaseExt_Tpl(const TS& str, const TS& wildcards, std::vector& wildcardMatches) { - const typename TS::value_type* savedStrBegin = 0; - const typename TS::value_type* savedStrEnd = 0; - const typename TS::value_type* savedWild = 0; + const typename TS::value_type* savedStrBegin = nullptr; + const typename TS::value_type* savedStrEnd = nullptr; + const typename TS::value_type* savedWild = nullptr; size_t savedWildCount = 0; const typename TS::value_type* pStr = str.c_str(); @@ -775,7 +775,7 @@ void StringHelpers::SplitByAnyOf(const wstring& str, const wstring& separators, template static inline TS FormatVA_Tpl(const typename TS::value_type* const format, va_list parg) { - if ((format == 0) || (format[0] == 0)) + if ((format == nullptr) || (format[0] == 0)) { return TS(); } @@ -935,8 +935,8 @@ static string ConvertUtf16ToMultibyte(const wchar_t* wstr, uint codePage, char b len, 0, 0, - ((badChar && codePage != CP_UTF8) ? &badChar : NULL), - NULL); + ((badChar && codePage != CP_UTF8) ? &badChar : nullptr), + nullptr); if (neededByteCount <= 0) { return string(); @@ -952,8 +952,8 @@ static string ConvertUtf16ToMultibyte(const wchar_t* wstr, uint codePage, char b len, &buffer[0], // output buffer neededByteCount - 1, // size of the output buffer in bytes - ((badChar && codePage != CP_UTF8) ? &badChar : NULL), - NULL); + ((badChar && codePage != CP_UTF8) ? &badChar : nullptr), + nullptr); if (byteCount != neededByteCount - 1) { return string(); diff --git a/Code/Editor/Util/UIEnumerations.cpp b/Code/Editor/Util/UIEnumerations.cpp index 8d9fc40377..2666a49f8e 100644 --- a/Code/Editor/Util/UIEnumerations.cpp +++ b/Code/Editor/Util/UIEnumerations.cpp @@ -55,15 +55,15 @@ CUIEnumerations::TDValuesContainer& CUIEnumerations::GetStandardNameContainer() { oEnumerationItem = oEnumaration->getChild(nCurrentEnumarationItem); - const char* szKey(NULL); - const char* szValue(NULL); + const char* szKey(nullptr); + const char* szValue(nullptr); oEnumerationItem->getAttributeByIndex(0, &szKey, &szValue); cValues.push_back(szValue); } - const char* szKey(NULL); - const char* szValue(NULL); + const char* szKey(nullptr); + const char* szValue(nullptr); oEnumaration->getAttributeByIndex(0, &szKey, &szValue); cValuesContainer.insert(TDValuesContainer::value_type(szValue, cValues)); diff --git a/Code/Editor/Util/Variable.cpp b/Code/Editor/Util/Variable.cpp index 5125032baf..ee5ccc9879 100644 --- a/Code/Editor/Util/Variable.cpp +++ b/Code/Editor/Util/Variable.cpp @@ -447,7 +447,7 @@ void CVarObject::AddVariable(CVariableArray& table, CVariableBase& var, const QS ////////////////////////////////////////////////////////////////////////// void CVarObject::RemoveVariable(IVariable* var) { - if (m_vars != NULL) + if (m_vars != nullptr) { m_vars->DeleteVariable(var); } @@ -455,7 +455,7 @@ void CVarObject::RemoveVariable(IVariable* var) ////////////////////////////////////////////////////////////////////////// void CVarObject::EnableUpdateCallbacks(bool boEnable) { - if (m_vars != NULL) + if (m_vars != nullptr) { m_vars->EnableUpdateCallbacks(boEnable); } @@ -463,7 +463,7 @@ void CVarObject::EnableUpdateCallbacks(bool boEnable) ////////////////////////////////////////////////////////////////////////// void CVarObject::OnSetValues() { - if (m_vars != NULL) + if (m_vars != nullptr) { m_vars->OnSetValues(); } @@ -471,7 +471,7 @@ void CVarObject::OnSetValues() ////////////////////////////////////////////////////////////////////////// void CVarObject::ReserveNumVariables(int numVars) { - if (m_vars != NULL) + if (m_vars != nullptr) { m_vars->ReserveNumVariables(numVars); } @@ -482,7 +482,7 @@ void CVarObject::CopyVariableValues(CVarObject* sourceObject) { // Check if compatible types. assert(metaObject() == sourceObject->metaObject()); - if (m_vars != NULL && sourceObject->m_vars != NULL) + if (m_vars != nullptr && sourceObject->m_vars != nullptr) { m_vars->CopyValues(sourceObject->m_vars); } diff --git a/Code/Editor/Util/Variable.h b/Code/Editor/Util/Variable.h index 0d1e7a9ef1..83afee0924 100644 --- a/Code/Editor/Util/Variable.h +++ b/Code/Editor/Util/Variable.h @@ -1411,7 +1411,7 @@ protected: struct IVarEnumList : public CRefCountBase { - //! Get the name of specified value in enumeration, or NULL if out of range. + //! Get the name of specified value in enumeration, or empty string if out of range. virtual QString GetItemName(uint index) = 0; }; typedef _smart_ptr IVarEnumListPtr; @@ -1498,7 +1498,7 @@ public: { if (index >= m_items.size()) { - return NULL; + return QString(); } return m_items[index].name; }; @@ -1869,9 +1869,9 @@ public: void Serialize(XmlNodeRef node, bool load); CVarBlock* GetVarBlock() const { return m_vars; }; - void AddVariable(CVariableBase& var, const QString& varName, VarOnSetCallback* cb = NULL, unsigned char dataType = IVariable::DT_SIMPLE); - void AddVariable(CVariableBase& var, const QString& varName, const QString& varHumanName, VarOnSetCallback* cb = NULL, unsigned char dataType = IVariable::DT_SIMPLE); - void AddVariable(CVariableArray& table, CVariableBase& var, const QString& varName, const QString& varHumanName, VarOnSetCallback* cb = NULL, unsigned char dataType = IVariable::DT_SIMPLE); + void AddVariable(CVariableBase& var, const QString& varName, VarOnSetCallback* cb = nullptr, unsigned char dataType = IVariable::DT_SIMPLE); + void AddVariable(CVariableBase& var, const QString& varName, const QString& varHumanName, VarOnSetCallback* cb = nullptr, unsigned char dataType = IVariable::DT_SIMPLE); + void AddVariable(CVariableArray& table, CVariableBase& var, const QString& varName, const QString& varHumanName, VarOnSetCallback* cb = nullptr, unsigned char dataType = IVariable::DT_SIMPLE); void ReserveNumVariables(int numVars); void RemoveVariable(IVariable* var); diff --git a/Code/Editor/Util/VariablePropertyType.cpp b/Code/Editor/Util/VariablePropertyType.cpp index c6d26b3470..014995467b 100644 --- a/Code/Editor/Util/VariablePropertyType.cpp +++ b/Code/Editor/Util/VariablePropertyType.cpp @@ -71,28 +71,28 @@ namespace Prop Description::Description() : m_type(ePropertyInvalid) , m_numImages(-1) - , m_enumList(NULL) + , m_enumList(nullptr) , m_rangeMin(0) , m_rangeMax(100) , m_step(0) , m_bHardMin(false) , m_bHardMax(false) , m_valueMultiplier(1) - , m_pEnumDBItem(NULL) + , m_pEnumDBItem(nullptr) { } Description::Description(IVariable* pVar) : m_type(ePropertyInvalid) , m_numImages(-1) - , m_enumList(NULL) + , m_enumList(nullptr) , m_rangeMin(0) , m_rangeMax(100) , m_step(0) , m_bHardMin(false) , m_bHardMax(false) , m_valueMultiplier(1) - , m_pEnumDBItem(NULL) + , m_pEnumDBItem(nullptr) { if (!pVar) { @@ -110,7 +110,7 @@ namespace Prop m_name = pVar->GetHumanName(); m_enumList = pVar->GetEnumList(); - if (m_enumList != NULL) + if (m_enumList != nullptr) { m_type = ePropertySelection; } @@ -325,7 +325,7 @@ namespace Prop case ePropertyAudioPreloadRequest: return "AudioPreloadRequest"; default: - return 0; + return nullptr; } } } diff --git a/Code/Editor/Util/XmlHistoryManager.cpp b/Code/Editor/Util/XmlHistoryManager.cpp index 3aea041d29..ed92700705 100644 --- a/Code/Editor/Util/XmlHistoryManager.cpp +++ b/Code/Editor/Util/XmlHistoryManager.cpp @@ -76,7 +76,7 @@ const XmlNodeRef& SXmlHistory::GetCurrentVersion(bool* bVersionExist, int* iVers bool SXmlHistory::IsModified() const { int currVersion; - GetCurrentVersion(NULL, &currVersion); + GetCurrentVersion(nullptr, &currVersion); return m_SavedVersion != currVersion; } @@ -94,7 +94,7 @@ void SXmlHistory::FlagAsSaved() if (Exist()) { int currVersion; - GetCurrentVersion(NULL, &currVersion); + GetCurrentVersion(nullptr, &currVersion); m_SavedVersion = currVersion; } } @@ -167,7 +167,7 @@ SXmlHistory* SXmlHistoryGroup::GetHistory(int index) const --index; } } - return it != m_List.end() ? *it : NULL; + return it != m_List.end() ? *it : nullptr; } //////////////////////////////////////////////////////////////////////////// @@ -199,7 +199,7 @@ SXmlHistory* SXmlHistoryGroup::GetHistoryByTypeId(uint32 typeId, int index /*= 0 return pHistory; } } - return NULL; + return nullptr; } //////////////////////////////////////////////////////////////////////////// @@ -246,9 +246,9 @@ int SXmlHistoryGroup::GetHistoryIndex(const SXmlHistory* pHistory) const CXmlHistoryManager::CXmlHistoryManager() : m_CurrentVersion(0) , m_LatestVersion(0) - , m_pExclusiveListener(NULL) + , m_pExclusiveListener(nullptr) , m_RecordNextVersion(false) - , m_pExActiveGroup(NULL) + , m_pExActiveGroup(nullptr) , m_bIsActiveGroupEx(false) { m_pNullGroup = new SXmlHistoryGroup(this, (uint32) - 1); @@ -376,7 +376,7 @@ void CXmlHistoryManager::PrepareForNextVersion() } ///////////////////////////////////////////////////////////////////////////// -void CXmlHistoryManager::RecordNextVersion(SXmlHistory* pHistory, XmlNodeRef newData, const char* undoDesc /*= NULL*/) +void CXmlHistoryManager::RecordNextVersion(SXmlHistory* pHistory, XmlNodeRef newData, const char* undoDesc /*= nullptr*/) { assert(m_RecordNextVersion); RegisterUndoEventHandler(this, pHistory); @@ -405,7 +405,7 @@ void CXmlHistoryManager::ClearHistory(bool flagAsSaved) it->ClearHistory(flagAsSaved); } - SetActiveGroup(NULL); + SetActiveGroup(nullptr); m_CurrentVersion = 0; m_LatestVersion = 0; @@ -451,14 +451,14 @@ SXmlHistoryGroup* CXmlHistoryManager::CreateXmlGroup(uint32 typeId) } ///////////////////////////////////////////////////////////////////////////// -void CXmlHistoryManager::AddXmlGroup(const SXmlHistoryGroup* pGroup, const char* undoDesc /*= NULL*/) +void CXmlHistoryManager::AddXmlGroup(const SXmlHistoryGroup* pGroup, const char* undoDesc /*= nullptr*/) { RecordUndoInternal(undoDesc ? undoDesc : "New XML Group added"); m_HistoryInfoMap[ m_CurrentVersion ].ActiveGroups.push_back(pGroup); NotifyUndoEventListener(IXmlHistoryEventListener::eHET_HistoryGroupAdded, (void*)pGroup); } ///////////////////////////////////////////////////////////////////////////// -void CXmlHistoryManager::RemoveXmlGroup(const SXmlHistoryGroup* pGroup, const char* undoDesc /*= NULL*/) +void CXmlHistoryManager::RemoveXmlGroup(const SXmlHistoryGroup* pGroup, const char* undoDesc /*= nullptr*/) { bool unload = m_HistoryInfoMap[ m_CurrentVersion ].CurrGroup == pGroup; RecordUndoInternal(undoDesc ? undoDesc : "XML Group deleted"); @@ -466,14 +466,14 @@ void CXmlHistoryManager::RemoveXmlGroup(const SXmlHistoryGroup* pGroup, const ch stl::find_and_erase(list, pGroup); if (unload) { - SetActiveGroupInt(NULL); + SetActiveGroupInt(nullptr); } m_HistoryInfoMap[ m_CurrentVersion ].CurrGroup = m_pNullGroup; NotifyUndoEventListener(IXmlHistoryEventListener::eHET_HistoryGroupRemoved, (void*)pGroup); } ///////////////////////////////////////////////////////////////////////////// -void CXmlHistoryManager::SetActiveGroup(const SXmlHistoryGroup* pGroup, const char* displayName /*= NULL*/, const TGroupIndexMap& groupIndex /*= TGroupIndexMap()*/, bool setExternal /*= false*/) +void CXmlHistoryManager::SetActiveGroup(const SXmlHistoryGroup* pGroup, const char* displayName /*= nullptr*/, const TGroupIndexMap& groupIndex /*= TGroupIndexMap()*/, bool setExternal /*= false*/) { TGroupIndexMap userIndex; const SXmlHistoryGroup* pActiveGroup = GetActiveGroup(userIndex); @@ -488,7 +488,7 @@ void CXmlHistoryManager::SetActiveGroup(const SXmlHistoryGroup* pGroup, const ch } } -void CXmlHistoryManager::SetActiveGroupInt(const SXmlHistoryGroup* pGroup, const char* displayName /*= NULL*/, bool bRecordNullUndo /*= false*/, const TGroupIndexMap& groupIndex /*= TGroupIndexMap()*/) +void CXmlHistoryManager::SetActiveGroupInt(const SXmlHistoryGroup* pGroup, const char* displayName /*= nullptr*/, bool bRecordNullUndo /*= false*/, const TGroupIndexMap& groupIndex /*= TGroupIndexMap()*/) { UnloadInt(); @@ -511,7 +511,7 @@ void CXmlHistoryManager::SetActiveGroupInt(const SXmlHistoryGroup* pGroup, const userIndexCount[ (*history)->GetTypeId() ] = 0; } uint32 userindex = userIndexCount[ (*history)->GetTypeId() ]; - IXmlUndoEventHandler* pEventHandler = NULL; + IXmlUndoEventHandler* pEventHandler = nullptr; TGroupIndexMap::const_iterator indexIter = groupIndex.find((*history)->GetTypeId()); if (indexIter == groupIndex.end() || indexIter->second == userindex) { @@ -581,12 +581,12 @@ const SXmlHistoryGroup* CXmlHistoryManager::GetActiveGroup(TGroupIndexMap& currU if (it != m_HistoryInfoMap.end() && pGroup) { currUserIndex = it->second.CurrUserIndex; - return pGroup == m_pNullGroup ? NULL : pGroup; + return pGroup == m_pNullGroup ? nullptr : pGroup; } } currVersion--; } while (currVersion >= 0); - return NULL; + return nullptr; } ///////////////////////////////////////////////////////////////////////////// @@ -796,7 +796,7 @@ SXmlHistory* CXmlHistoryManager::GetLatestHistory(SUndoEventHandlerData& eventHa } currVersion--; } while (currVersion >= 0); - return NULL; + return nullptr; } diff --git a/Code/Editor/Util/XmlHistoryManager.h b/Code/Editor/Util/XmlHistoryManager.h index d894bc7f00..d7aae71939 100644 --- a/Code/Editor/Util/XmlHistoryManager.h +++ b/Code/Editor/Util/XmlHistoryManager.h @@ -23,9 +23,9 @@ public: void AddToHistory(const XmlNodeRef& newXmlVersion); - const XmlNodeRef& Undo(bool* bVersionExist = NULL); + const XmlNodeRef& Undo(bool* bVersionExist = nullptr); const XmlNodeRef& Redo(); - const XmlNodeRef& GetCurrentVersion(bool* bVersionExist = NULL, int* iVersionNumber = NULL) const; + const XmlNodeRef& GetCurrentVersion(bool* bVersionExist = nullptr, int* iVersionNumber = nullptr) const; bool IsModified() const; uint32 GetTypeId() const {return m_typeId; } void FlagAsDeleted(); @@ -93,7 +93,7 @@ public: void RestoreUndoEventHandler(IXmlUndoEventHandler* pEventHandler, uint32 typeId); void PrepareForNextVersion(); - void RecordNextVersion(SXmlHistory* pHistory, XmlNodeRef newData, const char* undoDesc = NULL); + void RecordNextVersion(SXmlHistory* pHistory, XmlNodeRef newData, const char* undoDesc = nullptr); bool IsPreparedForNextVersion() const {return m_RecordNextVersion; } void RegisterEventListener(IXmlHistoryEventListener* pEventListener); @@ -112,11 +112,11 @@ public: // Xml History Groups SXmlHistoryGroup* CreateXmlGroup(uint32 typeId); - void SetActiveGroup(const SXmlHistoryGroup* pGroup, const char* displayName = NULL, const TGroupIndexMap& groupIndex = TGroupIndexMap(), bool setExternal = false); + void SetActiveGroup(const SXmlHistoryGroup* pGroup, const char* displayName = nullptr, const TGroupIndexMap& groupIndex = TGroupIndexMap(), bool setExternal = false); const SXmlHistoryGroup* GetActiveGroup() const; const SXmlHistoryGroup* GetActiveGroup(TGroupIndexMap& currUserIndex /*out*/) const; - void AddXmlGroup(const SXmlHistoryGroup* pGroup, const char* undoDesc = NULL); - void RemoveXmlGroup(const SXmlHistoryGroup* pGroup, const char* undoDesc = NULL); + void AddXmlGroup(const SXmlHistoryGroup* pGroup, const char* undoDesc = nullptr); + void RemoveXmlGroup(const SXmlHistoryGroup* pGroup, const char* undoDesc = nullptr); void DeleteAll(); @@ -156,7 +156,7 @@ private: { SHistoryInfo() : IsNullUndo(false) - , CurrGroup(NULL) + , CurrGroup(nullptr) , HistoryInvalidated(false) {} const SXmlHistoryGroup* CurrGroup; @@ -174,7 +174,7 @@ private: struct SUndoEventHandlerData { SUndoEventHandlerData() - : CurrentData(NULL) {} + : CurrentData(nullptr) {} SXmlHistory* CurrentData; THistoryVersionMap HistoryData; @@ -203,9 +203,9 @@ private: void RecordNullUndo(const TEventHandlerList& eventHandler, const char* desc, bool isNull = true); void ReloadCurrentVersion(const SXmlHistoryGroup* pPrevGroup, int prevVersion); SXmlHistory* GetLatestHistory(SUndoEventHandlerData& eventHandlerData); - void NotifyUndoEventListener(IXmlHistoryEventListener::EHistoryEventType event, void* pData = NULL); + void NotifyUndoEventListener(IXmlHistoryEventListener::EHistoryEventType event, void* pData = nullptr); - void SetActiveGroupInt(const SXmlHistoryGroup* pGroup, const char* displayName = NULL, bool bRecordNullUndo = false, const TGroupIndexMap& groupIndex = TGroupIndexMap()); + void SetActiveGroupInt(const SXmlHistoryGroup* pGroup, const char* displayName = nullptr, bool bRecordNullUndo = false, const TGroupIndexMap& groupIndex = TGroupIndexMap()); void UnloadInt(); void ClearRedo(); diff --git a/Code/Editor/Util/XmlTemplate.cpp b/Code/Editor/Util/XmlTemplate.cpp index 1b0cc6ff6f..8acdd4973f 100644 --- a/Code/Editor/Util/XmlTemplate.cpp +++ b/Code/Editor/Util/XmlTemplate.cpp @@ -89,7 +89,7 @@ void CXmlTemplate::SetValues(const XmlNodeRef& node, XmlNodeRef& toNode) } else { - assert(!"NULL returned from node->GetChild()"); + assert(!"nullptr returned from node->GetChild()"); } } } @@ -125,7 +125,7 @@ bool CXmlTemplate::SetValues(const XmlNodeRef& node, XmlNodeRef& toNode, const X } else { - assert(!"NULL returned from node->GetChild()"); + assert(!"nullptr returned from node->GetChild()"); } } return false; @@ -193,7 +193,7 @@ void CXmlTemplateRegistry::LoadTemplates(const QString& path) XmlNodeRef child; // Construct the full filepath of the current file XmlNodeRef node = XmlHelpers::LoadXmlFromFile((dir + files[k].filename).toUtf8().data()); - if (node != 0 && node->isTag("Templates")) + if (node != nullptr && node->isTag("Templates")) { QString name; for (int i = 0; i < node->getChildCount(); i++) @@ -220,5 +220,5 @@ XmlNodeRef CXmlTemplateRegistry::FindTemplate(const QString& name) { return node; } - return 0; + return nullptr; } diff --git a/Code/Editor/Util/bitarray.h b/Code/Editor/Util/bitarray.h index c7d7f2012a..f55d195fd1 100644 --- a/Code/Editor/Util/bitarray.h +++ b/Code/Editor/Util/bitarray.h @@ -74,7 +74,7 @@ public: void flip() {* p ^= mask; } }; - CBitArray() { m_base = NULL; m_bits = NULL; m_size = 0; m_numBits = 0; }; + CBitArray() { m_base = nullptr; m_bits = nullptr; m_size = 0; m_numBits = 0; }; CBitArray(int numBits) { resize(numBits); }; ~CBitArray() { From 769fd7818911a80b654984072386acb366a031fb Mon Sep 17 00:00:00 2001 From: Nemerle Date: Thu, 5 Aug 2021 16:52:43 +0200 Subject: [PATCH 16/38] Editor code: tidy up BOOLs,NULLs and overrides pt6. A few 'typedefs' replaced by 'using's This shouldn't have any functional changes at all, just c++17 modernization It's a part 6 of a split #2847 Signed-off-by: Nemerle --- Code/Editor/LayoutWnd.h | 2 +- Code/Editor/LevelInfo.cpp | 2 +- Code/Editor/LogFile.cpp | 2 +- Code/Editor/MainWindow.cpp | 38 +++++++------- Code/Editor/NewLevelDialog.cpp | 8 +-- Code/Editor/NewTerrainDialog.cpp | 2 +- Code/Editor/Plugin.cpp | 4 +- Code/Editor/PluginManager.cpp | 18 +++---- Code/Editor/PythonEditorFuncs.cpp | 2 +- Code/Editor/ResizeResolutionDialog.cpp | 2 +- Code/Editor/ResourceSelectorHost.cpp | 2 +- Code/Editor/SelectEAXPresetDlg.cpp | 2 +- Code/Editor/Settings.cpp | 8 +-- Code/Editor/SettingsManager.cpp | 72 +++++++++++++------------- Code/Editor/SettingsManagerDialog.cpp | 2 +- Code/Editor/StartupLogoDialog.cpp | 8 +-- Code/Editor/StringDlg.h | 2 +- Code/Editor/ToolBox.cpp | 14 ++--- Code/Editor/ToolBox.h | 2 +- Code/Editor/ToolsConfigPage.cpp | 6 +-- Code/Editor/UIEnumsDatabase.cpp | 4 +- Code/Editor/UndoDropDown.cpp | 4 +- Code/Editor/ViewManager.cpp | 12 ++--- Code/Editor/ViewManager.h | 2 +- Code/Editor/ViewPane.cpp | 8 +-- Code/Editor/Viewport.cpp | 4 +- Code/Editor/Viewport.h | 10 ++-- Code/Editor/ViewportTitleDlg.cpp | 2 +- Code/Editor/WipFeatureManager.cpp | 20 +++---- Code/Editor/WipFeatureManager.h | 4 +- Code/Editor/WipFeaturesDlg.cpp | 2 +- Code/Editor/WipFeaturesDlg.h | 2 +- 32 files changed, 136 insertions(+), 136 deletions(-) diff --git a/Code/Editor/LayoutWnd.h b/Code/Editor/LayoutWnd.h index 106c32d0a0..918734ebb9 100644 --- a/Code/Editor/LayoutWnd.h +++ b/Code/Editor/LayoutWnd.h @@ -103,7 +103,7 @@ public: static const char* GetConfigGroupName(); CLayoutViewPane* FindViewByClass(const QString& viewClassName); - void BindViewport(CLayoutViewPane* vp, const QString& viewClassName, QWidget* pViewport = NULL); + void BindViewport(CLayoutViewPane* vp, const QString& viewClassName, QWidget* pViewport = nullptr); QString ViewportTypeToClassName(EViewportType viewType); //! Switch 2D viewports. diff --git a/Code/Editor/LevelInfo.cpp b/Code/Editor/LevelInfo.cpp index 5821f9c933..5ad7a6593d 100644 --- a/Code/Editor/LevelInfo.cpp +++ b/Code/Editor/LevelInfo.cpp @@ -93,7 +93,7 @@ void CLevelInfo::ValidateObjects() pObject->Validate(m_pReport); - m_pReport->SetCurrentValidatorObject(NULL); + m_pReport->SetCurrentValidatorObject(nullptr); } CLogFile::WriteLine("Validating Duplicate Objects..."); diff --git a/Code/Editor/LogFile.cpp b/Code/Editor/LogFile.cpp index 8c04eab58c..c768f5013b 100644 --- a/Code/Editor/LogFile.cpp +++ b/Code/Editor/LogFile.cpp @@ -380,7 +380,7 @@ AZ_POP_DISABLE_WARNING ////////////////////////////////////////////////////////////////////// #if defined(AZ_PLATFORM_WINDOWS) - EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DisplayConfig); + EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &DisplayConfig); GetPrivateProfileString("boot.description", "display.drv", "(Unknown graphics card)", szProfileBuffer, sizeof(szProfileBuffer), "system.ini"); diff --git a/Code/Editor/MainWindow.cpp b/Code/Editor/MainWindow.cpp index ff322b79ac..3fba3fea6b 100644 --- a/Code/Editor/MainWindow.cpp +++ b/Code/Editor/MainWindow.cpp @@ -160,45 +160,45 @@ public: } } - ~EngineConnectionListener() + ~EngineConnectionListener() override { AzFramework::AssetSystemInfoBus::Handler::BusDisconnect(); AzFramework::EngineConnectionEvents::Bus::Handler::BusDisconnect(); } public: - virtual void Connected([[maybe_unused]] AzFramework::SocketConnection* connection) + void Connected([[maybe_unused]] AzFramework::SocketConnection* connection) override { m_state = EConnectionState::Connected; } - virtual void Connecting([[maybe_unused]] AzFramework::SocketConnection* connection) + void Connecting([[maybe_unused]] AzFramework::SocketConnection* connection) override { m_state = EConnectionState::Connecting; } - virtual void Listening([[maybe_unused]] AzFramework::SocketConnection* connection) + void Listening([[maybe_unused]] AzFramework::SocketConnection* connection) override { m_state = EConnectionState::Listening; } - virtual void Disconnecting([[maybe_unused]] AzFramework::SocketConnection* connection) + void Disconnecting([[maybe_unused]] AzFramework::SocketConnection* connection) override { m_state = EConnectionState::Disconnecting; } - virtual void Disconnected([[maybe_unused]] AzFramework::SocketConnection* connection) + void Disconnected([[maybe_unused]] AzFramework::SocketConnection* connection) override { m_state = EConnectionState::Disconnected; } - virtual void AssetCompilationSuccess(const AZStd::string& assetPath) override + void AssetCompilationSuccess(const AZStd::string& assetPath) override { m_lastAssetProcessorTask = assetPath; } - virtual void AssetCompilationFailed(const AZStd::string& assetPath) override + void AssetCompilationFailed(const AZStd::string& assetPath) override { m_failedJobs.insert(assetPath); } - virtual void CountOfAssetsInQueue(const int& count) override + void CountOfAssetsInQueue(const int& count) override { m_pendingJobsCount = count; } @@ -298,7 +298,7 @@ MainWindow::MainWindow(QWidget* parent) , m_undoStateAdapter(new UndoStackStateAdapter(this)) , m_keyboardCustomization(nullptr) , m_activeView(nullptr) - , m_settings("O3DE", "O3DE") + , m_settings("O3DE", "O3DE") , m_toolbarManager(new ToolbarManager(m_actionManager, this)) , m_assetImporterManager(new AssetImporterManager(this)) , m_levelEditorMenuHandler(new LevelEditorMenuHandler(this, m_viewPaneManager, m_settings)) @@ -573,7 +573,7 @@ void MainWindow::closeEvent(QCloseEvent* event) if (GetIEditor()->GetDocument()) { - GetIEditor()->GetDocument()->SetModifiedFlag(FALSE); + GetIEditor()->GetDocument()->SetModifiedFlag(false); GetIEditor()->GetDocument()->SetModifiedModules(eModifiedNothing); } // Close all edit panels. @@ -581,7 +581,7 @@ void MainWindow::closeEvent(QCloseEvent* event) GetIEditor()->GetObjectManager()->EndEditParams(); // force clean up of all deferred deletes, so that we don't have any issues with windows from plugins not being deleted yet - qApp->sendPostedEvents(0, QEvent::DeferredDelete); + qApp->sendPostedEvents(nullptr, QEvent::DeferredDelete); QMainWindow::closeEvent(event); } @@ -1243,7 +1243,7 @@ void MainWindow::OnEditorNotifyEvent(EEditorNotifyEvent ev) auto cryEdit = CCryEditApp::instance(); if (cryEdit) { - cryEdit->SetEditorWindowTitle(0, AZ::Utils::GetProjectName().c_str(), GetIEditor()->GetGameEngine()->GetLevelName()); + cryEdit->SetEditorWindowTitle(nullptr, AZ::Utils::GetProjectName().c_str(), GetIEditor()->GetGameEngine()->GetLevelName()); } } break; @@ -1252,7 +1252,7 @@ void MainWindow::OnEditorNotifyEvent(EEditorNotifyEvent ev) auto cryEdit = CCryEditApp::instance(); if (cryEdit) { - cryEdit->SetEditorWindowTitle(0, AZ::Utils::GetProjectName().c_str(), 0); + cryEdit->SetEditorWindowTitle(nullptr, AZ::Utils::GetProjectName().c_str(), nullptr); } } break; @@ -1351,8 +1351,8 @@ void MainWindow::ResetAutoSaveTimers(bool bForceInit) { delete m_autoRemindTimer; } - m_autoSaveTimer = 0; - m_autoRemindTimer = 0; + m_autoSaveTimer = nullptr; + m_autoRemindTimer = nullptr; if (bForceInit) { @@ -1389,7 +1389,7 @@ void MainWindow::ResetBackgroundUpdateTimer() if (m_backgroundUpdateTimer) { delete m_backgroundUpdateTimer; - m_backgroundUpdateTimer = 0; + m_backgroundUpdateTimer = nullptr; } ICVar* pBackgroundUpdatePeriod = gEnv->pConsole->GetCVar("ed_backgroundUpdatePeriod"); @@ -1435,7 +1435,7 @@ void MainWindow::OnRefreshAudioSystem() if (QString::compare(sLevelName, "Untitled", Qt::CaseInsensitive) == 0) { - // Rather pass NULL to indicate that no level is loaded! + // Rather pass nullptr to indicate that no level is loaded! sLevelName = QString(); } @@ -1868,7 +1868,7 @@ QWidget* MainWindow::CreateToolbarWidget(int actionId) break; case ID_TOOLBAR_WIDGET_SPACER_RIGHT: w = CreateSpacerRightWidget(); - break; + break; default: qWarning() << Q_FUNC_INFO << "Unknown id " << actionId; return nullptr; diff --git a/Code/Editor/NewLevelDialog.cpp b/Code/Editor/NewLevelDialog.cpp index 7db795333b..29445cef2b 100644 --- a/Code/Editor/NewLevelDialog.cpp +++ b/Code/Editor/NewLevelDialog.cpp @@ -19,7 +19,7 @@ #include // Editor -#include "NewTerrainDialog.h" +#include "NewTerrainDialog.h" AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING #include @@ -54,7 +54,7 @@ private: // CNewLevelDialog dialog -CNewLevelDialog::CNewLevelDialog(QWidget* pParent /*=NULL*/) +CNewLevelDialog::CNewLevelDialog(QWidget* pParent /*=nullptr*/) : QDialog(pParent) , m_bUpdate(false) , ui(new Ui::CNewLevelDialog) @@ -69,7 +69,7 @@ CNewLevelDialog::CNewLevelDialog(QWidget* pParent /*=NULL*/) m_bIsResize = false; - + ui->TITLE->setText(tr("Assign a name and location to the new level.")); ui->STATIC1->setText(tr("Location:")); ui->STATIC2->setText(tr("Name:")); @@ -98,7 +98,7 @@ CNewLevelDialog::CNewLevelDialog(QWidget* pParent /*=NULL*/) m_levelFolders = GetLevelsFolder(); m_level = ""; - // First of all, keyboard focus is related to widget tab order, and the default tab order is based on the order in which + // First of all, keyboard focus is related to widget tab order, and the default tab order is based on the order in which // widgets are constructed. Therefore, creating more widgets changes the keyboard focus. That is why setFocus() is called last. // Secondly, using singleShot() allows setFocus() slot of the QLineEdit instance to be invoked right after the event system // is ready to do so. Therefore, it is better to use singleShot() than directly call setFocus(). diff --git a/Code/Editor/NewTerrainDialog.cpp b/Code/Editor/NewTerrainDialog.cpp index 16641ee275..0e66482eb4 100644 --- a/Code/Editor/NewTerrainDialog.cpp +++ b/Code/Editor/NewTerrainDialog.cpp @@ -19,7 +19,7 @@ AZ_POP_DISABLE_WARNING -CNewTerrainDialog::CNewTerrainDialog(QWidget* pParent /*=NULL*/) +CNewTerrainDialog::CNewTerrainDialog(QWidget* pParent /*=nullptr*/) : QDialog(pParent) , m_terrainResolutionIndex(0) , m_terrainUnitsIndex(0) diff --git a/Code/Editor/Plugin.cpp b/Code/Editor/Plugin.cpp index 0fb37c96c7..e73b9daa2c 100644 --- a/Code/Editor/Plugin.cpp +++ b/Code/Editor/Plugin.cpp @@ -133,7 +133,7 @@ IClassDesc* CClassFactory::FindClass(const char* pClassName) const if (!pSubClassName) { - return NULL; + return nullptr; } QString name = QString(pClassName).left(pSubClassName - pClassName); @@ -169,7 +169,7 @@ void CClassFactory::UnregisterClass(const char* pClassName) { IClassDesc* pClassDesc = FindClass(pClassName); - if (pClassDesc == NULL) + if (pClassDesc == nullptr) { return; } diff --git a/Code/Editor/PluginManager.cpp b/Code/Editor/PluginManager.cpp index f03cb54fce..5efd52a97e 100644 --- a/Code/Editor/PluginManager.cpp +++ b/Code/Editor/PluginManager.cpp @@ -18,8 +18,8 @@ #include "Include/IPlugin.h" -typedef IPlugin* (* TPfnCreatePluginInstance)(PLUGIN_INIT_PARAM* pInitParam); -typedef void (* TPfnQueryPluginSettings)(SPluginSettings&); +using TPfnCreatePluginInstance = IPlugin *(*)(PLUGIN_INIT_PARAM *pInitParam); +using TPfnQueryPluginSettings = void (*)(SPluginSettings &); CPluginManager::CPluginManager() { @@ -210,7 +210,7 @@ bool CPluginManager::LoadPlugins(const char* pPathWithMask) continue; } - IPlugin* pPlugin = NULL; + IPlugin* pPlugin = nullptr; PLUGIN_INIT_PARAM sInitParam = { GetIEditor(), @@ -279,7 +279,7 @@ IPlugin* CPluginManager::GetPluginByGUID(const char* pGUID) } } - return NULL; + return nullptr; } IPlugin* CPluginManager::GetPluginByUIID(uint8 iUserInterfaceID) @@ -290,7 +290,7 @@ IPlugin* CPluginManager::GetPluginByUIID(uint8 iUserInterfaceID) if (it == m_uuidPluginMap.end()) { - return NULL; + return nullptr; } return (*it).second; @@ -302,7 +302,7 @@ IUIEvent* CPluginManager::GetEventByIDAndPluginID(uint8 aPluginID, uint8 aEventI // specified by its ID and the user interface ID of the plugin which // created the UI element - IPlugin* pPlugin = NULL; + IPlugin* pPlugin = nullptr; TEventHandlerIt eventIt; TPluginEventIt pluginIt; @@ -310,21 +310,21 @@ IUIEvent* CPluginManager::GetEventByIDAndPluginID(uint8 aPluginID, uint8 aEventI if (!pPlugin) { - return NULL; + return nullptr; } pluginIt = m_pluginEventMap.find(pPlugin); if (pluginIt == m_pluginEventMap.end()) { - return NULL; + return nullptr; } eventIt = (*pluginIt).second.find(aEventID); if (eventIt == (*pluginIt).second.end()) { - return NULL; + return nullptr; } return (*eventIt).second; diff --git a/Code/Editor/PythonEditorFuncs.cpp b/Code/Editor/PythonEditorFuncs.cpp index 93f5f8f10f..2b7bca3b0b 100644 --- a/Code/Editor/PythonEditorFuncs.cpp +++ b/Code/Editor/PythonEditorFuncs.cpp @@ -323,7 +323,7 @@ namespace ////////////////////////////////////////////////////////////////////////// void GetPythonArgumentsVector(const char* pArguments, QStringList& inputArguments) { - if (pArguments == NULL) + if (pArguments == nullptr) { return; } diff --git a/Code/Editor/ResizeResolutionDialog.cpp b/Code/Editor/ResizeResolutionDialog.cpp index ad57650afa..f2a03fb1b2 100644 --- a/Code/Editor/ResizeResolutionDialog.cpp +++ b/Code/Editor/ResizeResolutionDialog.cpp @@ -89,7 +89,7 @@ int ResizeResolutionModel::SizeRow(uint32 dwSize) const // CResizeResolutionDialog dialog -CResizeResolutionDialog::CResizeResolutionDialog(QWidget* pParent /*=NULL*/) +CResizeResolutionDialog::CResizeResolutionDialog(QWidget* pParent /*=nullptr*/) : QDialog(pParent) , m_model(new ResizeResolutionModel(this)) , ui(new Ui::CResizeResolutionDialog) diff --git a/Code/Editor/ResourceSelectorHost.cpp b/Code/Editor/ResourceSelectorHost.cpp index d265b3ffbe..af6c398fe7 100644 --- a/Code/Editor/ResourceSelectorHost.cpp +++ b/Code/Editor/ResourceSelectorHost.cpp @@ -101,7 +101,7 @@ public: } private: - typedef std::map > TTypeMap; + using TTypeMap = std::map>; TTypeMap m_typeMap; std::map m_globallySelectedResources; diff --git a/Code/Editor/SelectEAXPresetDlg.cpp b/Code/Editor/SelectEAXPresetDlg.cpp index 4a5ea9b66c..733de8b9cf 100644 --- a/Code/Editor/SelectEAXPresetDlg.cpp +++ b/Code/Editor/SelectEAXPresetDlg.cpp @@ -49,7 +49,7 @@ QString CSelectEAXPresetDlg::GetCurrPreset() const { return m_ui->listView->currentIndex().data().toString(); } - // EXCEPTION: OCX Property Pages should return FALSE + // EXCEPTION: OCX Property Pages should return false return QString(); } diff --git a/Code/Editor/Settings.cpp b/Code/Editor/Settings.cpp index 8f86c74b25..40d53a340c 100644 --- a/Code/Editor/Settings.cpp +++ b/Code/Editor/Settings.cpp @@ -243,7 +243,7 @@ SEditorSettings::SEditorSettings() gui.nToolbarIconSize = static_cast(AzQtComponents::ToolBar::ToolBarIconSize::Default); - int lfHeight = 8;// -MulDiv(8, GetDeviceCaps(GetDC(NULL), LOGPIXELSY), 72); + int lfHeight = 8;// -MulDiv(8, GetDeviceCaps(GetDC(nullptr), LOGPIXELSY), 72); gui.nDefaultFontHieght = lfHeight; gui.hSystemFont = QFont("Ms Shell Dlg 2", lfHeight, QFont::Normal); gui.hSystemFontBold = QFont("Ms Shell Dlg 2", lfHeight, QFont::Bold); @@ -530,7 +530,7 @@ void SEditorSettings::Save() SaveValue("Settings", "ShowTimeInConsole", bShowTimeInConsole); SaveValue("Settings", "EnableSceneInspector", enableSceneInspector); - + ////////////////////////////////////////////////////////////////////////// // Viewport settings. ////////////////////////////////////////////////////////////////////////// @@ -623,7 +623,7 @@ void SEditorSettings::Save() SaveValue("Settings\\AssetBrowser", "AutoFilterFromViewportSelection", sAssetBrowserSettings.bAutoFilterFromViewportSelection); SaveValue("Settings\\AssetBrowser", "VisibleColumnNames", sAssetBrowserSettings.sVisibleColumnNames); SaveValue("Settings\\AssetBrowser", "ColumnNames", sAssetBrowserSettings.sColumnNames); - + ////////////////////////////////////////////////////////////////////////// // Deep Selection Settings ////////////////////////////////////////////////////////////////////////// @@ -702,7 +702,7 @@ void SEditorSettings::Load() QString strPlaceholderString; // Load settings from registry. LoadValue("Settings", "UndoLevels", undoLevels); - LoadValue("Settings", "UndoSliceOverrideSaveValue", m_undoSliceOverrideSaveValue); + LoadValue("Settings", "UndoSliceOverrideSaveValue", m_undoSliceOverrideSaveValue); LoadValue("Settings", "ShowWelcomeScreenAtStartup", bShowDashboardAtStartup); LoadValue("Settings", "ShowCircularDependencyError", m_showCircularDependencyError); LoadValue("Settings", "LoadLastLevelAtStartup", bAutoloadLastLevelAtStartup); diff --git a/Code/Editor/SettingsManager.cpp b/Code/Editor/SettingsManager.cpp index 82e3eaf1e6..27031ca43e 100644 --- a/Code/Editor/SettingsManager.cpp +++ b/Code/Editor/SettingsManager.cpp @@ -105,7 +105,7 @@ bool CSettingsManager::CreateDefaultLayoutSettingsFile() AZStd::vector CSettingsManager::BuildSettingsList() { - XmlNodeRef root = NULL; + XmlNodeRef root = nullptr; root = m_pSettingsManagerMemoryNode; @@ -132,8 +132,8 @@ void CSettingsManager::BuildSettingsList_Helper(const XmlNodeRef& node, const AZ { for (int i = 0; i < node->getNumAttributes(); ++i) { - const char* key = NULL; - const char* value = NULL; + const char* key = nullptr; + const char* value = nullptr; node->getAttributeByIndex(i, &key, &value); if (!pathToNode.empty()) { @@ -163,7 +163,7 @@ void CSettingsManager::BuildSettingsList_Helper(const XmlNodeRef& node, const AZ result ); } - + } } } @@ -190,7 +190,7 @@ void CSettingsManager::SaveSetting(const QString& path, const QString& attr, con // Spaces in node names not allowed writeAttr.replace(" ", ""); - XmlNodeRef root = NULL; + XmlNodeRef root = nullptr; root = m_pSettingsManagerMemoryNode; @@ -276,11 +276,11 @@ XmlNodeRef CSettingsManager::LoadSetting(const QString& path, const QString& att // Spaces in node names not allowed readAttr.replace(" ", ""); - XmlNodeRef root = NULL; + XmlNodeRef root = nullptr; root = m_pSettingsManagerMemoryNode; - XmlNodeRef tmpNode = NULL; + XmlNodeRef tmpNode = nullptr; if (NeedSettingsNode(path)) { @@ -293,7 +293,7 @@ XmlNodeRef CSettingsManager::LoadSetting(const QString& path, const QString& att if (!tmpNode) { - return 0; + return nullptr; } for (int i = 0; i < strNodes.size(); ++i) @@ -304,13 +304,13 @@ XmlNodeRef CSettingsManager::LoadSetting(const QString& path, const QString& att } else { - return 0; + return nullptr; } } if (!tmpNode->findChild(readAttr.toUtf8().data())) { - return 0; + return nullptr; } else { @@ -360,7 +360,7 @@ void CSettingsManager::AddToolVersion(const QString& toolName, const QString& to return; } - if (stl::find_in_map(m_toolNames, toolName, NULL) == "") + if (stl::find_in_map(m_toolNames, toolName, nullptr) == "") { if (!toolVersion.isEmpty()) { @@ -380,7 +380,7 @@ void CSettingsManager::AddToolName(const QString& toolName, const QString& human return; } - if (stl::find_in_map(m_toolNames, toolName, NULL) == "") + if (stl::find_in_map(m_toolNames, toolName, nullptr) == "") { if (!humanReadableName.isEmpty()) { @@ -499,7 +499,7 @@ void CSettingsManager::GetMatchingLayoutNames(TToolNamesMap& foundTools, XmlNode return; } - TToolNamesMap* toolNames = NULL; + TToolNamesMap* toolNames = nullptr; if (!foundTools.empty()) { @@ -593,11 +593,11 @@ bool CSettingsManager::NeedSettingsNode(const QString& path) { if ((path != EDITOR_LAYOUT_ROOT_NODE) && (path != TOOLBOX_NODE) && (path != TOOLBOXMACROS_NODE)) { - return TRUE; + return true; } else { - return FALSE; + return false; } } @@ -605,13 +605,13 @@ void CSettingsManager::SerializeCVars(XmlNodeRef& node, bool bLoad) { int nNumberOfVariables(0); int nCurrentVariable(0); - IConsole* piConsole(NULL); - ICVar* piVariable(NULL); + IConsole* piConsole(nullptr); + ICVar* piVariable(nullptr); std::vector cszVariableNames; - char* szKey(NULL); - char* szValue(NULL); - ICVar* piCVar(NULL); + char* szKey(nullptr); + char* szValue(nullptr); + ICVar* piCVar(nullptr); piConsole = gEnv->pConsole; @@ -622,7 +622,7 @@ void CSettingsManager::SerializeCVars(XmlNodeRef& node, bool bLoad) if (bLoad) { - XmlNodeRef readNode = NULL; + XmlNodeRef readNode = nullptr; XmlNodeRef inputCVarsNode = node->findChild(CVARS_NODE); if (!inputCVarsNode) @@ -649,7 +649,7 @@ void CSettingsManager::SerializeCVars(XmlNodeRef& node, bool bLoad) } else { - XmlNodeRef newCVarNode = NULL; + XmlNodeRef newCVarNode = nullptr; XmlNodeRef oldCVarsNode = node->findChild(CVARS_NODE); if (oldCVarsNode) @@ -660,9 +660,9 @@ void CSettingsManager::SerializeCVars(XmlNodeRef& node, bool bLoad) XmlNodeRef cvarsNode = XmlHelpers::CreateXmlNode(CVARS_NODE); nNumberOfVariables = piConsole->GetNumVisibleVars(); - cszVariableNames.resize(nNumberOfVariables, NULL); + cszVariableNames.resize(nNumberOfVariables, nullptr); - if (piConsole->GetSortedVars((const char**)&cszVariableNames.front(), nNumberOfVariables, NULL) != nNumberOfVariables) + if (piConsole->GetSortedVars((const char**)&cszVariableNames.front(), nNumberOfVariables, nullptr) != nNumberOfVariables) { assert(false); return; @@ -711,8 +711,8 @@ void CSettingsManager::ReadValueStr(XmlNodeRef& sourceNode, const QString& path, // Spaces in node names not allowed readAttr.replace(" ", ""); - XmlNodeRef root = NULL; - XmlNodeRef tmpNode = NULL; + XmlNodeRef root = nullptr; + XmlNodeRef tmpNode = nullptr; if (NeedSettingsNode(path)) { @@ -809,7 +809,7 @@ bool CSettingsManager::IsEventSafe(const SEventLog& event) if (!root) { - return TRUE; + return true; } QString eventName = event.m_eventName; @@ -823,7 +823,7 @@ bool CSettingsManager::IsEventSafe(const SEventLog& event) // Log entry not found, so it is safe to start if (!resNode) { - return TRUE; + return true; } XmlNodeRef callerVersion = resNode->findChild(EVENT_LOG_CALLER_VERSION); @@ -841,15 +841,15 @@ bool CSettingsManager::IsEventSafe(const SEventLog& event) { if (callerVersionStr != GetToolVersion(eventName)) { - return TRUE; + return true; } } // The same version of tool/level found - return FALSE; + return false; } - return TRUE; + return true; } ////////////////////////////////////////////////////////////////////////// @@ -947,15 +947,15 @@ XmlNodeRef CSettingsManager::LoadLogEventSetting(const QString& path, const QStr if (!root) { - return 0; + return nullptr; } - XmlNodeRef tmpNode = NULL; + XmlNodeRef tmpNode = nullptr; tmpNode = root; if (!tmpNode) { - return 0; + return nullptr; } for (int i = 0; i < strNodes.size(); ++i) @@ -966,7 +966,7 @@ XmlNodeRef CSettingsManager::LoadLogEventSetting(const QString& path, const QStr } else { - return 0; + return nullptr; } } @@ -975,7 +975,7 @@ XmlNodeRef CSettingsManager::LoadLogEventSetting(const QString& path, const QStr return tmpNode; } - return 0; + return nullptr; } QString CSettingsManager::GenerateContentHash(XmlNodeRef& node, QString sourceName) diff --git a/Code/Editor/SettingsManagerDialog.cpp b/Code/Editor/SettingsManagerDialog.cpp index 3f1c718b38..f8b720ae55 100644 --- a/Code/Editor/SettingsManagerDialog.cpp +++ b/Code/Editor/SettingsManagerDialog.cpp @@ -84,7 +84,7 @@ void CSettingsManagerDialog::OnReadBtnClick() ui->m_layoutListBox->clear(); TToolNamesMap toolNames; - XmlNodeRef dummyNode = NULL; + XmlNodeRef dummyNode = nullptr; GetIEditor()->GetSettingsManager()->GetMatchingLayoutNames(toolNames, dummyNode, m_importFileStr); diff --git a/Code/Editor/StartupLogoDialog.cpp b/Code/Editor/StartupLogoDialog.cpp index 38d733f8df..d9625aff1a 100644 --- a/Code/Editor/StartupLogoDialog.cpp +++ b/Code/Editor/StartupLogoDialog.cpp @@ -27,14 +27,14 @@ AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING ///////////////////////////////////////////////////////////////////////////// // CStartupLogoDialog dialog -CStartupLogoDialog* CStartupLogoDialog::s_pLogoWindow = 0; +CStartupLogoDialog* CStartupLogoDialog::s_pLogoWindow = nullptr; -CStartupLogoDialog::CStartupLogoDialog(QString versionText, QString richTextCopyrightNotice, QWidget* pParent /*=NULL*/) +CStartupLogoDialog::CStartupLogoDialog(QString versionText, QString richTextCopyrightNotice, QWidget* pParent /*=nullptr*/) : QWidget(pParent, Qt::Dialog | Qt::FramelessWindowHint) , m_ui(new Ui::StartupLogoDialog) { m_ui->setupUi(this); - + s_pLogoWindow = this; m_backgroundImage = QPixmap(QStringLiteral(":/StartupLogoDialog/splashscreen_background_developer_preview.jpg")); @@ -61,7 +61,7 @@ CStartupLogoDialog::CStartupLogoDialog(QString versionText, QString richTextCopy CStartupLogoDialog::~CStartupLogoDialog() { - s_pLogoWindow = 0; + s_pLogoWindow = nullptr; } void CStartupLogoDialog::SetText(const char* text) diff --git a/Code/Editor/StringDlg.h b/Code/Editor/StringDlg.h index a1ee3908c7..e52cd207d1 100644 --- a/Code/Editor/StringDlg.h +++ b/Code/Editor/StringDlg.h @@ -25,7 +25,7 @@ typedef bool (StringDlgPredicate)(QString input); class StringDlg : public QInputDialog { public: - StringDlg(const QString &title, QWidget* pParent = NULL, bool bFileNameLimitation = false); + StringDlg(const QString &title, QWidget* pParent = nullptr, bool bFileNameLimitation = false); void SetCheckCallback(const std::function& Check) { m_Check = Check; diff --git a/Code/Editor/ToolBox.cpp b/Code/Editor/ToolBox.cpp index 82817e1ff7..bac02d7460 100644 --- a/Code/Editor/ToolBox.cpp +++ b/Code/Editor/ToolBox.cpp @@ -186,7 +186,7 @@ const CToolBoxMacro* CToolBoxManager::GetMacro(int iIndex, bool bToolbox) const assert(0 <= iIndex && iIndex < m_shelveMacros.size()); return m_shelveMacros[iIndex]; } - return NULL; + return nullptr; } ////////////////////////////////////////////////////////////////////////// @@ -202,7 +202,7 @@ CToolBoxMacro* CToolBoxManager::GetMacro(int iIndex, bool bToolbox) assert(0 <= iIndex && iIndex < m_shelveMacros.size()); return m_shelveMacros[iIndex]; } - return NULL; + return nullptr; } ////////////////////////////////////////////////////////////////////////// @@ -240,14 +240,14 @@ CToolBoxMacro* CToolBoxManager::NewMacro(const QString& title, bool bToolbox, in const int macroCount = m_macros.size(); if (macroCount > ID_TOOL_LAST - ID_TOOL_FIRST + 1) { - return NULL; + return nullptr; } for (size_t i = 0; i < macroCount; ++i) { if (QString::compare(m_macros[i]->GetTitle(), title, Qt::CaseInsensitive) == 0) { - return NULL; + return nullptr; } } @@ -264,7 +264,7 @@ CToolBoxMacro* CToolBoxManager::NewMacro(const QString& title, bool bToolbox, in const int shelveMacroCount = m_shelveMacros.size(); if (shelveMacroCount > ID_TOOL_SHELVE_LAST - ID_TOOL_SHELVE_FIRST + 1) { - return NULL; + return nullptr; } CToolBoxMacro* pNewTool = new CToolBoxMacro(title); @@ -275,7 +275,7 @@ CToolBoxMacro* CToolBoxManager::NewMacro(const QString& title, bool bToolbox, in m_shelveMacros.push_back(pNewTool); return pNewTool; } - return NULL; + return nullptr; } ////////////////////////////////////////////////////////////////////////// @@ -333,7 +333,7 @@ void CToolBoxManager::Load([[maybe_unused]] ActionManager* actionManager) void CToolBoxManager::Load(QString xmlpath, AmazonToolbar* pToolbar, bool bToolbox, ActionManager* actionManager) { XmlNodeRef toolBoxNode = XmlHelpers::LoadXmlFromFile(xmlpath.toUtf8().data()); - if (toolBoxNode == NULL) + if (toolBoxNode == nullptr) { return; } diff --git a/Code/Editor/ToolBox.h b/Code/Editor/ToolBox.h index 0c91305c79..eefc74f791 100644 --- a/Code/Editor/ToolBox.h +++ b/Code/Editor/ToolBox.h @@ -137,7 +137,7 @@ public: CToolBoxMacro* GetMacro(int iIndex, bool bToolbox); //! Get the index of a macro from its title. int GetMacroIndex(const QString& title, bool bToolbox) const; - //! Creates a new macro in the manager. If the title is duplicate, this returns NULL. + //! Creates a new macro in the manager. If the title is duplicate, this returns nullptr. CToolBoxMacro* NewMacro(const QString& title, bool bToolbox, int* newIdx); //! Try to change the title of a macro. If the title is duplicate, the change is aborted and this returns false. bool SetMacroTitle(int index, const QString& title, bool bToolbox); diff --git a/Code/Editor/ToolsConfigPage.cpp b/Code/Editor/ToolsConfigPage.cpp index a2328550e0..bae70f0cfe 100644 --- a/Code/Editor/ToolsConfigPage.cpp +++ b/Code/Editor/ToolsConfigPage.cpp @@ -109,7 +109,7 @@ private: QStringList m_iconFiles; }; -CIconListDialog::CIconListDialog(QWidget* pParent /* = NULL */) +CIconListDialog::CIconListDialog(QWidget* pParent /* = nullptr */) : QDialog(pParent) , m_ui(new Ui::IconListDialog) { @@ -498,7 +498,7 @@ CToolsConfigPage::CToolsConfigPage(QWidget* parent) QKeySequence shortcut(value); m_ui->m_macroShortcutKey->setKeySequence(shortcut); } - + if (m_ui->m_macroShortcutKey->keySequence().count() >= 1) { m_ui->m_assignShortcut->setEnabled(true); @@ -703,7 +703,7 @@ void CToolsConfigPage::OnAssignMacroShortcut() { auto pShortcutMgr = MainWindow::instance()->GetShortcutManager(); - if (pShortcutMgr == NULL) + if (pShortcutMgr == nullptr) { return; } diff --git a/Code/Editor/UIEnumsDatabase.cpp b/Code/Editor/UIEnumsDatabase.cpp index f0496f64e3..b859f54ffa 100644 --- a/Code/Editor/UIEnumsDatabase.cpp +++ b/Code/Editor/UIEnumsDatabase.cpp @@ -59,7 +59,7 @@ void CUIEnumsDatabase::SetEnumStrings(const QString& enumName, const QStringList { int nStringCount = sStringsArray.size(); - CUIEnumsDatabase_SEnum* pEnum = stl::find_in_map(m_enums, enumName, 0); + CUIEnumsDatabase_SEnum* pEnum = stl::find_in_map(m_enums, enumName, nullptr); if (!pEnum) { pEnum = new CUIEnumsDatabase_SEnum; @@ -86,6 +86,6 @@ void CUIEnumsDatabase::SetEnumStrings(const QString& enumName, const QStringList ////////////////////////////////////////////////////////////////////////// CUIEnumsDatabase_SEnum* CUIEnumsDatabase::FindEnum(const QString& enumName) const { - CUIEnumsDatabase_SEnum* pEnum = stl::find_in_map(m_enums, enumName, 0); + CUIEnumsDatabase_SEnum* pEnum = stl::find_in_map(m_enums, enumName, nullptr); return pEnum; } diff --git a/Code/Editor/UndoDropDown.cpp b/Code/Editor/UndoDropDown.cpp index 669c05ecaa..fbe18d5d52 100644 --- a/Code/Editor/UndoDropDown.cpp +++ b/Code/Editor/UndoDropDown.cpp @@ -56,7 +56,7 @@ public: m_manager.AddListener(this); } - virtual ~UndoDropDownListModel() + ~UndoDropDownListModel() override { m_manager.RemoveListener(this); } @@ -81,7 +81,7 @@ public: return m_stackNames[index.row()]; } - void SignalNumUndoRedo(const unsigned int& numUndo, const unsigned int& numRedo) + void SignalNumUndoRedo(const unsigned int& numUndo, const unsigned int& numRedo) override { std::vector fresh; if (UndoRedoDirection::Undo == m_direction && m_stackNames.size() != numUndo) diff --git a/Code/Editor/ViewManager.cpp b/Code/Editor/ViewManager.cpp index 5a3e92a525..d5d932dae1 100644 --- a/Code/Editor/ViewManager.cpp +++ b/Code/Editor/ViewManager.cpp @@ -55,7 +55,7 @@ CViewManager::CViewManager() m_updateRegion.min = Vec3(-100000, -100000, -100000); m_updateRegion.max = Vec3(100000, 100000, 100000); - m_pSelectedView = NULL; + m_pSelectedView = nullptr; m_nGameViewports = 0; m_bGameViewportsUpdated = false; @@ -117,7 +117,7 @@ void CViewManager::UnregisterViewport(CViewport* pViewport) { if (m_pSelectedView == pViewport) { - m_pSelectedView = NULL; + m_pSelectedView = nullptr; } stl::find_and_erase(m_viewports, pViewport); m_bGameViewportsUpdated = false; @@ -137,7 +137,7 @@ CViewport* CViewManager::GetViewport(EViewportType type) const return m_viewports[i]; } } - return 0; + return nullptr; } ////////////////////////////////////////////////////////////////////////// @@ -150,7 +150,7 @@ CViewport* CViewManager::GetViewport(const QString& name) const return m_viewports[i]; } } - return 0; + return nullptr; } ////////////////////////////////////////////////////////////////////////// @@ -234,7 +234,7 @@ void CViewManager::SelectViewport(CViewport* pViewport) { // Audio: Handle viewport change for listeners - if (m_pSelectedView != NULL && m_pSelectedView != pViewport) + if (m_pSelectedView != nullptr && m_pSelectedView != pViewport) { m_pSelectedView->SetSelected(false); @@ -242,7 +242,7 @@ void CViewManager::SelectViewport(CViewport* pViewport) m_pSelectedView = pViewport; - if (m_pSelectedView != NULL) + if (m_pSelectedView != nullptr) { m_pSelectedView->SetSelected(true); } diff --git a/Code/Editor/ViewManager.h b/Code/Editor/ViewManager.h index 22f9f6804f..b62ece89ac 100644 --- a/Code/Editor/ViewManager.h +++ b/Code/Editor/ViewManager.h @@ -93,7 +93,7 @@ public: ////////////////////////////////////////////////////////////////////////// //! Get current layout window. - //! @return Pointer to the layout window, can be NULL. + //! @return Pointer to the layout window, can be nullptr. virtual CLayoutWnd* GetLayout() const; //! Cycle between different 2D viewports type on same view pane. diff --git a/Code/Editor/ViewPane.cpp b/Code/Editor/ViewPane.cpp index 19b653b865..1d530ece48 100644 --- a/Code/Editor/ViewPane.cpp +++ b/Code/Editor/ViewPane.cpp @@ -159,8 +159,8 @@ CLayoutViewPane::CLayoutViewPane(QWidget* parent) , m_viewportTitleDlg(this) , m_expanderWatcher(new ViewportTitleExpanderWatcher(this, &m_viewportTitleDlg)) { - m_viewport = 0; - m_active = 0; + m_viewport = nullptr; + m_active = false; m_nBorder = VIEW_BORDER; m_bFullscreen = false; @@ -338,7 +338,7 @@ void CLayoutViewPane::DetachViewport() { DisconnectRenderViewportInteractionRequestBus(); OnFOVChanged(gSettings.viewports.fDefaultFov); - m_viewport = 0; + m_viewport = nullptr; } ////////////////////////////////////////////////////////////////////////// @@ -348,7 +348,7 @@ void CLayoutViewPane::ReleaseViewport() { DisconnectRenderViewportInteractionRequestBus(); m_viewport->deleteLater(); - m_viewport = 0; + m_viewport = nullptr; } } diff --git a/Code/Editor/Viewport.cpp b/Code/Editor/Viewport.cpp index 906a60ac47..2cc9c78e4b 100644 --- a/Code/Editor/Viewport.cpp +++ b/Code/Editor/Viewport.cpp @@ -192,7 +192,7 @@ QtViewport::QtViewport(QWidget* parent) m_viewTM.SetIdentity(); m_screenTM.SetIdentity(); - m_pMouseOverObject = 0; + m_pMouseOverObject = nullptr; m_bAdvancedSelectMode = false; @@ -398,7 +398,7 @@ void QtViewport::OnDeactivate() ////////////////////////////////////////////////////////////////////////// void QtViewport::ResetContent() { - m_pMouseOverObject = 0; + m_pMouseOverObject = nullptr; // Need to clear visual object cache. // Right after loading new level, some code(e.g. OnMouseMove) access invalid diff --git a/Code/Editor/Viewport.h b/Code/Editor/Viewport.h index c94a77b06e..0a23d93d0d 100644 --- a/Code/Editor/Viewport.h +++ b/Code/Editor/Viewport.h @@ -113,7 +113,7 @@ public: virtual void AddPostRenderer(IPostRenderer* pPostRenderer) = 0; virtual bool RemovePostRenderer(IPostRenderer* pPostRenderer) = 0; - virtual BOOL DestroyWindow() { return FALSE; } + virtual bool DestroyWindow() { return false; } /** Get type of this viewport. */ @@ -252,7 +252,7 @@ public: virtual void SetCursorString(const QString& str) = 0; virtual void SetFocus() = 0; - virtual void Invalidate(BOOL bErase = 1) = 0; + virtual void Invalidate(bool bErase = 1) = 0; // Is overridden by RenderViewport virtual void SetFOV([[maybe_unused]] float fov) {} @@ -266,8 +266,8 @@ public: void SetViewPane(CLayoutViewPane* viewPane) { m_viewPane = viewPane; } //Child classes can override these to provide extra logic that wraps - //widget rendering. Needed by the RenderViewport to handle raycasts - //from screen-space to world-space. + //widget rendering. Needed by the RenderViewport to handle raycasts + //from screen-space to world-space. virtual void PreWidgetRendering() {} virtual void PostWidgetRendering() {} @@ -346,7 +346,7 @@ public: QString GetName() const; virtual void SetFocus() { setFocus(); } - virtual void Invalidate([[maybe_unused]] BOOL bErase = 1) { update(); } + virtual void Invalidate([[maybe_unused]] bool bErase = 1) { update(); } // Is overridden by RenderViewport virtual void SetFOV([[maybe_unused]] float fov) {} diff --git a/Code/Editor/ViewportTitleDlg.cpp b/Code/Editor/ViewportTitleDlg.cpp index 4d27506929..acc6771e9f 100644 --- a/Code/Editor/ViewportTitleDlg.cpp +++ b/Code/Editor/ViewportTitleDlg.cpp @@ -91,7 +91,7 @@ namespace void ViewportInfoStatusUpdated(int newIndex); private: - void OnViewportInfoDisplayStateChanged(AZ::AtomBridge::ViewportInfoDisplayState state) + void OnViewportInfoDisplayStateChanged(AZ::AtomBridge::ViewportInfoDisplayState state) override { emit ViewportInfoStatusUpdated(static_cast(state)); } diff --git a/Code/Editor/WipFeatureManager.cpp b/Code/Editor/WipFeatureManager.cpp index 6777065c1c..f6fbc7ac93 100644 --- a/Code/Editor/WipFeatureManager.cpp +++ b/Code/Editor/WipFeatureManager.cpp @@ -19,7 +19,7 @@ const char* CWipFeatureManager::kWipFeaturesFilename = "@user@\\Editor\\UI\\WipF #else const char* CWipFeatureManager::kWipFeaturesFilename = "@user@/Editor/UI/WipFeatures.xml"; #endif -CWipFeatureManager* CWipFeatureManager::s_pInstance = NULL; +CWipFeatureManager* CWipFeatureManager::s_pInstance = nullptr; static void WipFeatureVarChange(ICVar* pVar) { @@ -162,7 +162,7 @@ void CWipFeatureManager::Shutdown() { CWipFeatureManager::Instance()->Save(); delete s_pInstance; - s_pInstance = NULL; + s_pInstance = nullptr; } bool CWipFeatureManager::Load(const char* pFilename, bool bClearExisting) @@ -350,7 +350,7 @@ void CWipFeatureManager::ShowFeature(int aFeatureId, bool bShow) if (m_features[aFeatureId].m_pfnUpdateFeature) { - m_features[aFeatureId].m_pfnUpdateFeature(aFeatureId, &bShow, NULL, NULL, NULL); + m_features[aFeatureId].m_pfnUpdateFeature(aFeatureId, &bShow, nullptr, nullptr, nullptr); } } @@ -360,7 +360,7 @@ void CWipFeatureManager::EnableFeature(int aFeatureId, bool bEnable) if (m_features[aFeatureId].m_pfnUpdateFeature) { - m_features[aFeatureId].m_pfnUpdateFeature(aFeatureId, NULL, &bEnable, NULL, NULL); + m_features[aFeatureId].m_pfnUpdateFeature(aFeatureId, nullptr, &bEnable, nullptr, nullptr); } } @@ -370,7 +370,7 @@ void CWipFeatureManager::SetFeatureSafeMode(int aFeatureId, bool bSafeMode) if (m_features[aFeatureId].m_pfnUpdateFeature) { - m_features[aFeatureId].m_pfnUpdateFeature(aFeatureId, NULL, NULL, &bSafeMode, NULL); + m_features[aFeatureId].m_pfnUpdateFeature(aFeatureId, nullptr, nullptr, &bSafeMode, nullptr); } } @@ -380,7 +380,7 @@ void CWipFeatureManager::SetFeatureParams(int aFeatureId, const char* pParams) if (m_features[aFeatureId].m_pfnUpdateFeature) { - m_features[aFeatureId].m_pfnUpdateFeature(aFeatureId, NULL, NULL, NULL, pParams); + m_features[aFeatureId].m_pfnUpdateFeature(aFeatureId, nullptr, nullptr, nullptr, pParams); } } @@ -392,7 +392,7 @@ void CWipFeatureManager::ShowAllFeatures(bool bShow) if (iter->second.m_pfnUpdateFeature) { - iter->second.m_pfnUpdateFeature(iter->first, &bShow, NULL, NULL, NULL); + iter->second.m_pfnUpdateFeature(iter->first, &bShow, nullptr, nullptr, nullptr); } } } @@ -405,7 +405,7 @@ void CWipFeatureManager::EnableAllFeatures(bool bEnable) if (iter->second.m_pfnUpdateFeature) { - iter->second.m_pfnUpdateFeature(iter->first, NULL, &bEnable, NULL, NULL); + iter->second.m_pfnUpdateFeature(iter->first, nullptr, &bEnable, nullptr, nullptr); } } } @@ -418,7 +418,7 @@ void CWipFeatureManager::SetAllFeaturesSafeMode(bool bSafeMode) if (iter->second.m_pfnUpdateFeature) { - iter->second.m_pfnUpdateFeature(iter->first, NULL, NULL, &bSafeMode, NULL); + iter->second.m_pfnUpdateFeature(iter->first, nullptr, nullptr, &bSafeMode, nullptr); } } } @@ -431,7 +431,7 @@ void CWipFeatureManager::SetAllFeaturesParams(const char* pParams) if (iter->second.m_pfnUpdateFeature) { - iter->second.m_pfnUpdateFeature(iter->first, NULL, NULL, NULL, pParams); + iter->second.m_pfnUpdateFeature(iter->first, nullptr, nullptr, nullptr, pParams); } } } diff --git a/Code/Editor/WipFeatureManager.h b/Code/Editor/WipFeatureManager.h index 52ed364524..cd20f6d591 100644 --- a/Code/Editor/WipFeatureManager.h +++ b/Code/Editor/WipFeatureManager.h @@ -48,7 +48,7 @@ public: static const char* kWipFeaturesFilename; // Used to register a callback function to update the state of features whitin the editor - // pbVisible, pbEnabled, pbSafeMode, pParams - if the pointer is NULL, then that attribute was not changed + // pbVisible, pbEnabled, pbSafeMode, pParams - if the pointer is nullptr, then that attribute was not changed typedef void (* TWipFeatureUpdateCallback)(int aFeatureId, const bool* const pbVisible, const bool* const pbEnabled, const bool* const pbSafeMode, const char* pParams); // wip feature registerer auto create object, used for static auto feature creation with the REGISTER_WIP_FEATURE macro @@ -71,7 +71,7 @@ public: , m_bVisible(true) , m_bEnabled(true) , m_bSafeMode(false) - , m_pfnUpdateFeature(NULL) + , m_pfnUpdateFeature(nullptr) , m_bLoadedFromXml(false) {} diff --git a/Code/Editor/WipFeaturesDlg.cpp b/Code/Editor/WipFeaturesDlg.cpp index 8a5c323fa5..0d0a179539 100644 --- a/Code/Editor/WipFeaturesDlg.cpp +++ b/Code/Editor/WipFeaturesDlg.cpp @@ -152,7 +152,7 @@ public: } }; -CWipFeaturesDlg::CWipFeaturesDlg(QWidget* pParent /*=NULL*/) +CWipFeaturesDlg::CWipFeaturesDlg(QWidget* pParent /*=nullptr*/) : QDialog(pParent) , m_ui(new Ui::WipFeaturesDlg) { diff --git a/Code/Editor/WipFeaturesDlg.h b/Code/Editor/WipFeaturesDlg.h index 03b3654a27..28f4f43958 100644 --- a/Code/Editor/WipFeaturesDlg.h +++ b/Code/Editor/WipFeaturesDlg.h @@ -29,7 +29,7 @@ class CWipFeaturesDlg { Q_OBJECT public: - CWipFeaturesDlg(QWidget* pParent = NULL); // standard constructor + CWipFeaturesDlg(QWidget* pParent = nullptr); // standard constructor virtual ~CWipFeaturesDlg(); private: From 2f57d725611b559fe968d4f730ff4d7bd46edfe7 Mon Sep 17 00:00:00 2001 From: Jeremy Ong Date: Mon, 2 Aug 2021 23:50:44 -0600 Subject: [PATCH 17/38] Add initial JobGraph prototype Signed-off-by: Jeremy Ong --- .../AzCore/Jobs/Internal/JobTypeEraser.cpp | 76 ++ .../AzCore/Jobs/Internal/JobTypeEraser.h | 229 +++++ .../AzCore/AzCore/Jobs/JobDescriptor.h | 49 + .../AzCore/AzCore/Jobs/JobExecutor.cpp | 361 ++++++++ .../AzCore/AzCore/Jobs/JobExecutor.h | 86 ++ .../Framework/AzCore/AzCore/Jobs/JobGraph.cpp | 60 ++ Code/Framework/AzCore/AzCore/Jobs/JobGraph.h | 139 +++ .../Framework/AzCore/AzCore/Jobs/JobGraph.inl | 62 ++ .../AzCore/AzCore/azcore_files.cmake | 8 + .../AzCore/AzCore/std/parallel/thread.h | 3 +- Code/Framework/AzCore/Tests/JobGraphTests.cpp | 848 ++++++++++++++++++ .../AzCore/Tests/azcoretests_files.cmake | 1 + 12 files changed, 1920 insertions(+), 2 deletions(-) create mode 100644 Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.cpp create mode 100644 Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h create mode 100644 Code/Framework/AzCore/AzCore/Jobs/JobDescriptor.h create mode 100644 Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp create mode 100644 Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h create mode 100644 Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp create mode 100644 Code/Framework/AzCore/AzCore/Jobs/JobGraph.h create mode 100644 Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl create mode 100644 Code/Framework/AzCore/Tests/JobGraphTests.cpp diff --git a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.cpp b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.cpp new file mode 100644 index 0000000000..043f232e32 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace AZ::Internal +{ + TypeErasedJob::TypeErasedJob(TypeErasedJob&& other) noexcept + { + if (!other.m_relocator || other.m_lambda != other.m_buffer) + { + // The type-erased lambda is trivially relocatable OR, the lambda is heap allocated + memcpy(this, &other, sizeof(TypeErasedJob)); + + if (other.m_lambda == other.m_buffer) + { + m_lambda = m_buffer; + } + + // Prevent deletion in the event the lambda had spilled to the heap + other.m_lambda = nullptr; + return; + } + + // At this point, we know the lambda was inlined + m_lambda = m_buffer; + + m_invoker = other.m_invoker; + m_relocator = other.m_relocator; + m_destroyer = other.m_destroyer; + + // We now own the lambda, so clear the moved-from job's destroyer + other.m_destroyer = nullptr; + other.m_invoker = nullptr; + + m_relocator(m_buffer, other.m_buffer); + } + + TypeErasedJob& TypeErasedJob::operator=(TypeErasedJob&& other) noexcept + { + if (this == &other) + { + return *this; + } + + this->~TypeErasedJob(); + + new (this) TypeErasedJob{ AZStd::move(other) }; + + return *this; + } + + TypeErasedJob::~TypeErasedJob() + { + if (m_lambda) + { + if (m_destroyer) + { + // The presence of m_destroyer indicates that the lambda is not trivially destructible + m_destroyer(m_lambda); + } + + if (m_lambda != m_buffer) + { + // We've spilled the lambda into the heap, free its memory + azfree(m_lambda); + } + } + } + +} // namespace AZ::Internal diff --git a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h new file mode 100644 index 0000000000..1455f1311a --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h @@ -0,0 +1,229 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include +#include +#include + +namespace AZ::Internal +{ + using JobInvoke_t = void (*)(void* lambda); + using JobRelocate_t = void (*)(void* dst, void* src); + using JobDestroy_t = void (*)(void* obj); + + class CompiledJobGraph; + + // Lambdas are opaque types and we cannot extract any member function pointers. In order to store lambdas in a + // type erased fashion, we instead use a single function call indirection, invoking the lambda function in a + // static class function which has a stable address in memory. The Erased* methods return addresses to the + // indirect callers of the lambda copy/move assignment operators, call operator, and destructor. + // + // For lambdas that are trivially relocatable, both the returned move and copy assignment function pointers + // will be nullptr. + // + // Lambdas that are trivially destructible will result in a nullptr returned JobDestroy_t pointer. + // + // The class will check that the lambda is copy assignable or movable. + template + class JobTypeEraser final + { + public: + constexpr JobInvoke_t ErasedInvoker() + { + return reinterpret_cast(Invoker); + } + + constexpr JobRelocate_t ErasedRelocator() + { + if constexpr (AZStd::is_trivially_move_constructible_v) + { + return nullptr; + } + else if constexpr (AZStd::is_move_constructible_v) + { + return reinterpret_cast(Mover); + } + else if constexpr (AZStd::is_copy_constructible_v) + { + return reinterpret_cast(Copyer); + } + else + { + static_assert( + false, + "Job lambdas must be either move or copy constructible. Please verify that all captured data is move or copy " + "constructible."); + } + } + + constexpr JobDestroy_t ErasedDestroyer() + { + if constexpr (AZStd::is_trivially_destructible_v) + { + return nullptr; + } + else + { + return reinterpret_cast(Destroyer); + } + } + + private: + constexpr static void Invoker(Lambda* lambda) + { + lambda->operator()(); + } + + constexpr static void Mover(Lambda* dst, Lambda* src) + { + new (dst) Lambda{ AZStd::move(*src) }; + } + + constexpr static void Copyer(Lambda* dst, Lambda* src) + { + new (dst) Lambda{ *src }; + } + + constexpr static void Destroyer(Lambda* lambda) + { + lambda->~Lambda(); + } + }; + + // The TypeErasedJob encapsulates member function pointers to store in a homogeneously-typed container + // The function signature of all lambdas encoded in a TypeErasedJob is void(*)(). The lambdas can capture + // data, in which case the data is inlined in this structure if the payload is less than or equal to the + // buffer size. Otherwise, the data is heap allocated. + class alignas(alignof(max_align_t)) TypeErasedJob final + { + public: + // The inline buffer allows the TypeErasedJob to span two cache lines. Lambdas can capture 56 + // bytes of data (7 pointers/references on a 64-bit machine) before spilling to the heap. + constexpr static size_t BufferSize = 128 - sizeof(size_t) * 6 - sizeof(uint32_t) - sizeof(JobDescriptor); + + TypeErasedJob() = default; + + template + TypeErasedJob(JobDescriptor const& desc, Lambda&& lambda) noexcept + : m_descriptor{desc} + { + JobTypeEraser eraser; + m_invoker = eraser.ErasedInvoker(); + m_relocator = eraser.ErasedRelocator(); + m_destroyer = eraser.ErasedDestroyer(); + + // NOTE: This code is conservative in that extended alignment requirements result in a heap + // spill, even if the lambda could have occupied a portion of the inline buffer with a base + // pointer adjustment. + if constexpr (sizeof(Lambda) <= BufferSize && alignof(Lambda) <= alignof(max_align_t)) + { + TypedRelocate(AZStd::forward(lambda), m_buffer); + m_lambda = m_buffer; + } + else + { + // Lambda has spilled to the heap (or requires extended alignment) + m_lambda = reinterpret_cast(azmalloc(sizeof(Lambda), alignof(Lambda))); + TypedRelocate(AZStd::forward(lambda), m_lambda); + } + } + + TypeErasedJob(TypeErasedJob&& other) noexcept; + + TypeErasedJob& operator=(TypeErasedJob&& other) noexcept; + + ~TypeErasedJob(); + + void Link(TypeErasedJob& other); + + // Indicates if this job is a root of the graph (with no dependencies) + bool IsRoot(); + + void AttachToJobGraph(CompiledJobGraph& graph) noexcept + { + m_graph = &graph; + } + + void Invoke() + { + m_invoker(m_lambda); + } + + uint8_t GetPriorityNumber() const + { + return static_cast(m_descriptor.priority); + } + + private: + friend class CompiledJobGraph; + friend class JobWorker; + + // This relocation avoids branches needed if the lambda type is unknown + template + void TypedRelocate(Lambda&& lambda, char* destination) + { + if constexpr (AZStd::is_trivially_move_constructible_v) + { + memcpy(destination, reinterpret_cast(&lambda), sizeof(Lambda)); + } + else if constexpr (AZStd::is_move_constructible_v) + { + new (destination) Lambda{ AZStd::move(lambda) }; + } + else if constexpr (AZStd::is_copy_constructible_v) + { + new (destination) Lambda{ lambda }; + } + else + { + static_assert( + false, + "Job lambdas must be either move or copy constructible. Please verify that all captured data is move or copy " + "constructible."); + } + } + + // Small buffer optimization for lambdas. We cover our bases here by enforcing alignment on the + // class to equal the alignment of the largest scalar type available on the system (generally + // 16 bytes). + char m_buffer[BufferSize]; + + // This value is an offset in a buffer that stores dependency tracking information. + uint32_t m_successorOffset = 0; + uint32_t m_inboundLinkCount = 0; + uint32_t m_outboundLinkCount = 0; + + // May point to the inlined payload buffer, or heap + char* m_lambda = nullptr; + + CompiledJobGraph* m_graph = nullptr; + + JobInvoke_t m_invoker; + + // If nullptr, the lambda is trivially relocatable (via memcpy). Otherwise, it must be invoked + // when instances of this class are moved. + JobRelocate_t m_relocator; + JobDestroy_t m_destroyer; + + JobDescriptor m_descriptor; + }; + + inline void TypeErasedJob::Link(TypeErasedJob& other) + { + ++m_outboundLinkCount; + ++other.m_inboundLinkCount; + } + + inline bool TypeErasedJob::IsRoot() + { + return m_inboundLinkCount == 0; + } +} // namespace AZ::Internal diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobDescriptor.h b/Code/Framework/AzCore/AzCore/Jobs/JobDescriptor.h new file mode 100644 index 0000000000..82a9603bd5 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Jobs/JobDescriptor.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AZ +{ + // Job priorities MAY be used judiciously to fine tune runtime execution, with the understanding + // that profiling is needed to understand what the critical path per frame is. Modifying + // job priorities is an EXPERT setting that should succeed a healthy dose of measurement. + enum class JobPriority : uint8_t + { + CRITICAL = 0, + HIGH = 1, + MEDIUM = 2, // Default + LOW = 3, + PRIORITY_COUNT = 4, + }; + + // All submitted jobs are associated with a JobDescriptor which defines the priority, affinitization, + // and tracking of the job resource utilization. + // + // TODO: Define various job kinds and provide a mechanism for cpuMask computation on different systems. + struct JobDescriptor + { + // Unique job kind label (e.g. "frustum culling") + // Job names *must* be provided + const char* jobName = nullptr; + + // Associates a set of job kinds together for budget tracking (e.g. "graphics") + const char* jobGroup = nullptr; + + // EXPERTS ONLY. Jobs of higher priority are executed ahead of any lower priority jobs + // that were queued before it provided they had not yet started + JobPriority priority = JobPriority::MEDIUM; + + // EXPERTS ONLY. A bitmask that restricts jobs of this kind to run only on cores + // corresponding to a set bit. 0 is synonymous with all bits set + uint32_t cpuMask = 0; + }; +} diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp new file mode 100644 index 0000000000..16e4983752 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp @@ -0,0 +1,361 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace AZ +{ + constexpr static size_t PRIORITY_COUNT = static_cast(JobPriority::PRIORITY_COUNT); + + namespace Internal + { + CompiledJobGraph::CompiledJobGraph( + AZStd::vector&& jobs, + AZStd::unordered_map>& links, + size_t linkCount, + bool retained) + : m_remaining{ jobs.size() } + , m_retained{ retained } + { + m_jobs = AZStd::move(jobs); + m_dependencyCounts = reinterpret_cast*>(azcalloc(sizeof(AZStd::atomic) * m_jobs.size())); + m_successors.resize(linkCount); + + uint32_t* cursor = m_successors.data(); + + for (size_t i = 0; i != m_jobs.size(); ++i) + { + TypeErasedJob& job = m_jobs[i]; + job.m_successorOffset = cursor - m_successors.data(); + cursor += job.m_outboundLinkCount; + + AZ_Assert(job.m_outboundLinkCount == links[i].size(), "Job outbound link information mismatch"); + + for (uint32_t j = 0; j != job.m_outboundLinkCount; ++j) + { + m_successors[static_cast(job.m_successorOffset) + j] = links[i][j]; + } + + if (job.m_inboundLinkCount > 0) + { + m_dependencyCounts[i].store(job.m_inboundLinkCount, AZStd::memory_order_release); + } + } + + // TODO: Check for dependency cycles + } + + CompiledJobGraph::~CompiledJobGraph() + { + if (m_dependencyCounts) + { + azfree(m_dependencyCounts); + } + } + + void CompiledJobGraph::Release() + { + if (--m_remaining == 0) + { + if (m_retained) + { + m_remaining = m_jobs.size(); + for (size_t i = 0; i != m_jobs.size(); ++i) + { + TypeErasedJob& job = m_jobs[i]; + if (job.m_inboundLinkCount > 0) + { + m_dependencyCounts[i].store(job.m_inboundLinkCount, AZStd::memory_order_release); + } + } + } + + if (m_waitEvent) + { + m_waitEvent->m_submitted = false; + m_waitEvent->Signal(); + } + + if (!m_retained) + { + azdestroy(this); + } + } + } + + struct QueueStatus + { + AZStd::atomic head; + AZStd::atomic tail; + AZStd::atomic reserve; + }; + + // The Job Queue is a lock free 4-priority queue. Its basic operation is as follows: + // Each priority level is associated with a different queue, corresponding to the maximum size of a uint16_t. + // Each queue is implemented as a ring buffer, and a 64 bit atomic maintains the following state per queue: + // - offset to the "head" of the ring, from where we acquire elements + // - offset to the "tail" of the ring, which tracks where new elements should be enqueued + // - offset to a tail reservation index, which is used to reserve a slot to enqueue elements + class JobQueue final + { + public: + // Preallocating upfront allows us to reserve slots to insert jobs without locks. + // Each thread allocated by the job manager consumes ~2 MB. + constexpr static uint16_t MaxQueueSize = 0xffff; + constexpr static uint8_t PriorityLevelCount = static_cast(JobPriority::PRIORITY_COUNT); + + JobQueue() = default; + JobQueue(const JobQueue&) = delete; + JobQueue& operator=(const JobQueue&) = delete; + + bool Enqueue(TypeErasedJob* job); + TypeErasedJob* TryDequeue(); + + private: + QueueStatus m_status[PriorityLevelCount] = {}; + TypeErasedJob* m_queues[PriorityLevelCount][MaxQueueSize] = {}; + }; + + bool JobQueue::Enqueue(TypeErasedJob* job) + { + uint8_t priority = job->GetPriorityNumber(); + QueueStatus& status = m_status[priority]; + + while (true) + { + uint16_t reserve = status.reserve.load(); + uint16_t head = status.head.load(); + + // Enqueuing is done in two phases because we cannot atomically write the job to the slot we reserve + // and simulataneously publish the fact that the slot is now available. + if (reserve != head - 1) + { + // Try to reserve a slot + if (status.reserve.compare_exchange_weak(reserve, reserve + 1)) + { + m_queues[priority][reserve] = job; + + uint16_t expectedReserve = reserve; + + // Increment the tail to advertise the new job + while (!status.tail.compare_exchange_weak(expectedReserve, reserve + 1)) + { + expectedReserve = reserve; + } + + return status.head == status.tail - 1; + } + + // We failed to reserve a slot, try again + } + else + { + // TODO need exponential backup here + AZStd::this_thread::sleep_for(AZStd::chrono::microseconds{ 100 }); + } + } + } + + TypeErasedJob* JobQueue::TryDequeue() + { + for (size_t priority = 0; priority != PriorityLevelCount; ++priority) + { + QueueStatus& status = m_status[priority]; + while (true) + { + uint16_t head = status.head.load(); + uint16_t tail = status.tail.load(); + if (head == tail) + { + // Queue empty + break; + } + else + { + TypeErasedJob* job = m_queues[priority][status.head]; + if (status.head.compare_exchange_weak(head, head + 1)) + { + return job; + } + } + } + } + + return nullptr; + } + + class JobWorker + { + public: + void Spawn(::AZ::JobExecutor& executor, size_t id, AZStd::semaphore& initSemaphore, bool affinitize) + { + m_executor = &executor; + + AZStd::string threadName = AZStd::string::format("JobWorker %zu", id); + AZStd::thread_desc desc = {}; + desc.m_name = threadName.c_str(); + if (affinitize) + { + desc.m_cpuId = 1 << id; + } + m_active.store(true, AZStd::memory_order_release); + + m_thread = AZStd::thread{ [this, &initSemaphore] + { + initSemaphore.release(); + Run(); + }, + &desc }; + } + + void Join() + { + m_active.store(false, AZStd::memory_order_release); + m_semaphore.release(); + m_thread.join(); + } + + void Enqueue(TypeErasedJob* job) + { + if (m_queue.Enqueue(job)) + { + // The queue was empty prior to enqueueing the job, release the semaphore + m_semaphore.release(); + } + } + + private: + void Run() + { + while (m_active) + { + m_semaphore.acquire(); + // m_semaphore.try_acquire_for(AZStd::chrono::microseconds{ 10 }); + + if (!m_active) + { + return; + } + + TypeErasedJob* job = m_queue.TryDequeue(); + while (job) + { + job->Invoke(); + // Decrement counts for all job successors + for (size_t j = 0; j != job->m_outboundLinkCount; ++j) + { + uint32_t successorIndex = job->m_graph->m_successors[job->m_successorOffset + j]; + if (--job->m_graph->m_dependencyCounts[successorIndex] == 0) + { + m_executor->Submit(job->m_graph->m_jobs[successorIndex]); + } + } + + job->m_graph->Release(); + --m_executor->m_remaining; + + job = m_queue.TryDequeue(); + } + } + } + + AZStd::thread m_thread; + AZStd::atomic m_active; + AZStd::binary_semaphore m_semaphore; + + ::AZ::JobExecutor* m_executor; + JobQueue m_queue; + }; + } // namespace Internal + + JobExecutor& JobExecutor::Instance() + { + // TODO: Create the default executor as part of a component (as in JobManagerComponent) + static JobExecutor executor; + return executor; + } + + JobExecutor::JobExecutor(uint32_t threadCount) + { + // TODO: Configure thread count + affinity based on configuration + m_threadCount = threadCount == 0 ? AZStd::thread::hardware_concurrency() : threadCount; + + m_workers = reinterpret_cast(azmalloc(m_threadCount * sizeof(Internal::JobWorker))); + + bool affinitize = m_threadCount == AZStd::thread::hardware_concurrency(); + + AZStd::semaphore initSemaphore; + + for (size_t i = 0; i != m_threadCount; ++i) + { + new (m_workers + i) Internal::JobWorker{}; + m_workers[i].Spawn(*this, i, initSemaphore, affinitize); + } + + for (size_t i = 0; i != m_threadCount; ++i) + { + initSemaphore.acquire(); + } + } + + JobExecutor::~JobExecutor() + { + for (size_t i = 0; i != m_threadCount; ++i) + { + m_workers[i].Join(); + m_workers[i].~JobWorker(); + } + + azfree(m_workers); + } + + void JobExecutor::Submit(Internal::CompiledJobGraph& graph) + { + for (Internal::TypeErasedJob& job : graph.Jobs()) + { + job.AttachToJobGraph(graph); + } + + // Submit all jobs that have no inbound edges + for (Internal::TypeErasedJob& job : graph.Jobs()) + { + if (job.IsRoot()) + { + Submit(job); + } + } + } + + void JobExecutor::Submit(Internal::TypeErasedJob& job) + { + // TODO: Something more sophisticated is likely needed here. + // First, we are completely ignoring affinity. + // Second, some heuristics on core availability will help distribute work more effectively + ++m_remaining; + m_workers[++m_lastSubmission % m_threadCount].Enqueue(&job); + } + + void JobExecutor::Drain() + { + while (m_remaining > 0) + { + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds{ 100 }); + } + } +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h new file mode 100644 index 0000000000..9418126678 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + class JobGraphEvent; + + namespace Internal + { + class CompiledJobGraph final + { + public: + AZ_CLASS_ALLOCATOR(CompiledJobGraph, SystemAllocator, 0) + + CompiledJobGraph( + AZStd::vector&& jobs, + AZStd::unordered_map>& links, + size_t linkCount, + bool retained); + + ~CompiledJobGraph(); + + AZStd::vector& Jobs() noexcept + { + return m_jobs; + } + + // Indicate that a constituent job has finished and decrement a counter to determine if the + // graph should be freed + void Release(); + + private: + friend class JobGraph; + friend class JobWorker; + + AZStd::vector m_jobs; + AZStd::vector m_successors; + AZStd::atomic* m_dependencyCounts = nullptr; + JobGraphEvent* m_waitEvent = nullptr; + AZStd::atomic m_remaining; + bool m_retained; + }; + + class JobWorker; + } // namespace Internal + + class JobExecutor + { + public: + AZ_CLASS_ALLOCATOR(JobExecutor, SystemAllocator, 0); + + static JobExecutor& Instance(); + + // Passing 0 for the threadCount requests for the thread count to match the hardware concurrency + JobExecutor(uint32_t threadCount = 0); + ~JobExecutor(); + + void Submit(Internal::CompiledJobGraph& graph); + + void Submit(Internal::TypeErasedJob& job); + + // Busy wait until jobs are cleared from the executor (note, does not prevent future jobs from being submitted) + void Drain(); + private: + friend class Internal::JobWorker; + + Internal::JobWorker* m_workers; + uint32_t m_threadCount = 0; + AZStd::atomic m_lastSubmission; + AZStd::atomic m_remaining; + }; +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp new file mode 100644 index 0000000000..b715e0ada7 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include + +namespace AZ +{ + using Internal::CompiledJobGraph; + + void JobToken::PrecedesInternal(JobToken& comesAfter) + { + AZ_Assert(!m_parent.m_submitted, "Cannot mutate a JobGraph that was previously submitted."); + + // Increment inbound/outbound edge counts + m_parent.m_jobs[m_index].Link(m_parent.m_jobs[comesAfter.m_index]); + + m_parent.m_links[m_index].emplace_back(comesAfter.m_index); + + ++m_parent.m_linkCount; + } + + JobGraph::~JobGraph() + { + if (m_retained && m_compiledJobGraph) + { + azdestroy(m_compiledJobGraph); + } + } + + void JobGraph::Submit(JobGraphEvent* waitEvent) + { + SubmitOnExecutor(JobExecutor::Instance(), waitEvent); + } + + void JobGraph::SubmitOnExecutor(JobExecutor& executor, JobGraphEvent* waitEvent) + { + m_submitted = true; + + if (!m_compiledJobGraph) + { + m_compiledJobGraph = aznew CompiledJobGraph(AZStd::move(m_jobs), m_links, m_linkCount, m_retained); + } + + m_compiledJobGraph->m_waitEvent = waitEvent; + + executor.Submit(*m_compiledJobGraph); + + if (waitEvent) + { + waitEvent->m_submitted = true; + } + } +} diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h new file mode 100644 index 0000000000..62565ea78c --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +// NOTE: If adding additional header/symbol dependencies, consider if such additions are better +// suited in the private CompiledJobGraph implementation instead to keep this header lean. +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Internal + { + class CompiledJobGraph; + } + class JobExecutor; + + // A JobToken is returned each time a Job is added to the JobGraph. JobTokens are used to + // express dependencies between jobs within the graph. + class JobToken final + { + public: + // Indicate that this job must finish before the job passed as the argument + template + void Precedes(JT&... tokens); + + private: + friend class JobGraph; + + void PrecedesInternal(JobToken& comesAfter); + + // Only the JobGraph should be creating JobToken + JobToken(JobGraph& parent, size_t index); + + JobGraph& m_parent; + size_t m_index; + }; + + // A JobGraphEvent may be used to block until a job graph has finished executing. Usage + // is NOT recommended for the majority of tasks (prefer to simply containing expanding/contracting + // the graph without synchronization over the course of the frame). However, the event + // is useful for the edges of the computation graph. + // + // You are responsible for ensuring the event object lifetime exceeds the job graph lifetime. + // + // After the JobGraphEvent is signaled, you are allowed to reuse the same JobGraphEvent + // for a future submission. + class JobGraphEvent + { + public: + bool IsSignaled(); + void Wait(); + + private: + friend class ::AZ::Internal::CompiledJobGraph; + friend class JobGraph; + void Signal(); + + AZStd::binary_semaphore m_semaphore; + bool m_submitted = false; + }; + + // The JobGraph encapsulates a set of jobs and their interdependencies. After adding + // jobs, and marking dependencies as necessary, the entire graph is submitted via + // the JobGraph::Submit method. + // + // The JobGraph MAY be retained across multiple frames and resubmitted, provided the + // user provides some guarantees (see comments associated with JobGraph::Retain). + class JobGraph final + { + public: + ~JobGraph(); + + // Add a job to the graph, retrieiving a token that can be used to express dependencies + // between jobs. The first argument specifies the JobKind, used for tracking the job. + template + JobToken AddJob(JobDescriptor const& descriptor, Lambda&& lambda); + + template + AZStd::fixed_vector AddJobs(JobDescriptor const& descriptor, Lambdas&&... lambdas); + + // By default, you are responsible for retaining the JobGraph, indicating you promise that + // this JobGraph will live as long as it takes for all constituent jobs to complete. + // Once retained, this job graph can be resubmitted after completion without any + // modifications. JobTokens that were created as a result of adding jobs used to + // mark dependencies DO NOT need to outlive the job graph. + // + // Invoking Detach PRIOR to submission indicates you wish the jobs associated with this + // JobGraph to deallocate upon completion. After invoking Detach, you may let this JobGraph + // go out of scope or deallocate after submission. + // + // NOTE: The JobGraph has no concept of resources used by design. Resubmission + // of the job graph is expected to rely on either indirection, or safe overwriting + // of previously used memory to supply new data (this can even be done as the first + // job in the graph). + void Detach(); + + // Invoke the job graph, asserting if there are dependency violations. Note that + // submitting the same graph multiple times to process simultaneously is VALID + // behavior. This is, for example, a mechanism that allows a job graph to loop + // in perpetuity (in fact, the entire frame could be modeled as a single job graph, + // where the final job resubmits the job graph again). + // + // This API is not designed to protect against memory safety violations (nothing + // can prevent a user from incorrectly aliasing memory unsafely even without repeated + // submission). To catch memory safety violations, it is ENCOURAGED that you access + // data through JobResource handles. + void Submit(JobGraphEvent* waitEvent = nullptr); + + // Same as submit but run on a different executor than the default system executor + void SubmitOnExecutor(JobExecutor& executor, JobGraphEvent* waitEvent = nullptr); + + private: + friend class JobToken; + + Internal::CompiledJobGraph* m_compiledJobGraph = nullptr; + + AZStd::vector m_jobs; + + // Job index |-> Dependent job indices + AZStd::unordered_map> m_links; + + uint32_t m_linkCount = 0; + bool m_retained = true; + bool m_submitted = false; + }; +} // namespace AZ + +#include diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl new file mode 100644 index 0000000000..f541d9923f --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl @@ -0,0 +1,62 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +namespace AZ +{ + inline JobToken::JobToken(JobGraph& parent, size_t index) + : m_parent{ parent } + , m_index{ index } + { + } + + template + inline void JobToken::Precedes(JT&... tokens) + { + (PrecedesInternal(tokens), ...); + } + + inline bool JobGraphEvent::IsSignaled() + { + AZ_Assert(m_submitted, "Querying the status of a job graph event that was never submitted along with the jobgraph"); + return m_semaphore.try_acquire_for(AZStd::chrono::milliseconds{ 0 }); + } + + inline void JobGraphEvent::Wait() + { + AZ_Assert(m_submitted, "Waiting on a job graph event that was never submitted along with the jobgraph"); + m_semaphore.acquire(); + } + + inline void JobGraphEvent::Signal() + { + m_semaphore.release(); + } + + template + inline JobToken JobGraph::AddJob(JobDescriptor const& desc, Lambda&& lambda) + { + AZ_Assert(!m_submitted, "Cannot mutate a JobGraph that was previously submitted."); + + m_jobs.emplace_back(desc, AZStd::forward(lambda)); + + return { *this, m_jobs.size() - 1 }; + } + + template + inline AZStd::fixed_vector AddJobs(JobDescriptor const& descriptor, Lambdas&&... lambdas) + { + return { AddJob(descriptor, lambdas)... }; + } + + inline void JobGraph::Detach() + { + m_retained = false; + } +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index e3d2987a3c..a23ec9ab82 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -221,6 +221,8 @@ set(FILES Jobs/Internal/JobManagerWorkStealing.cpp Jobs/Internal/JobManagerWorkStealing.h Jobs/Internal/JobNotify.h + Jobs/Internal/JobTypeEraser.cpp + Jobs/Internal/JobTypeEraser.h Jobs/Job.cpp Jobs/Job.h Jobs/JobCancelGroup.h @@ -228,8 +230,14 @@ set(FILES Jobs/JobCompletionSpin.h Jobs/JobContext.cpp Jobs/JobContext.h + Jobs/JobDescriptor.h Jobs/JobEmpty.h + Jobs/JobExecutor.cpp + Jobs/JobExecutor.h Jobs/JobFunction.h + Jobs/JobGraph.cpp + Jobs/JobGraph.h + Jobs/JobGraph.inl Jobs/JobManager.cpp Jobs/JobManager.h Jobs/JobManagerBus.h diff --git a/Code/Framework/AzCore/AzCore/std/parallel/thread.h b/Code/Framework/AzCore/AzCore/std/parallel/thread.h index a46671a4cd..9f7830c91b 100644 --- a/Code/Framework/AzCore/AzCore/std/parallel/thread.h +++ b/Code/Framework/AzCore/AzCore/std/parallel/thread.h @@ -69,8 +69,7 @@ namespace AZStd int m_priority{ -100000 }; //! The CPU ids (as a bitfield) that this thread will be running on, see \ref AZStd::thread_desc::m_cpuId. - //! Windows: This parameter is ignored. - //! On other platforms, each bit maps directly to the core numbers [0-n], default is 0 + //! Each bit maps directly to the core numbers [0-n], default is 0 int m_cpuId{ AFFINITY_MASK_ALL }; //! If we can join the thread. diff --git a/Code/Framework/AzCore/Tests/JobGraphTests.cpp b/Code/Framework/AzCore/Tests/JobGraphTests.cpp new file mode 100644 index 0000000000..175881b89a --- /dev/null +++ b/Code/Framework/AzCore/Tests/JobGraphTests.cpp @@ -0,0 +1,848 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +#include + +#include + +using AZ::JobDescriptor; +using AZ::JobGraph; +using AZ::JobGraphEvent; +using AZ::JobExecutor; +using AZ::Internal::TypeErasedJob; +using AZ::JobPriority; + +static JobDescriptor defaultJD{ "JobGraphTestJob", "JobGraphTests" }; + +namespace UnitTest +{ + class JobGraphTestFixture : public AllocatorsTestFixture + { + public: + void SetUp() override + { + AllocatorsTestFixture::SetUp(); + AZ::AllocatorInstance::Create(); + AZ::AllocatorInstance::Create(); + + m_executor = aznew JobExecutor(4); + } + + void TearDown() override + { + azdestroy(m_executor); + AZ::AllocatorInstance::Destroy(); + AZ::AllocatorInstance::Destroy(); + AllocatorsTestFixture::TearDown(); + } + + protected: + JobExecutor* m_executor; + }; + + TEST(JobGraphTests, TrivialJobLambda) + { + int x = 0; + + TypeErasedJob job( + defaultJD, + [&x]() + { + ++x; + }); + job.Invoke(); + + EXPECT_EQ(1, x); + } + + TEST(JobGraphTests, TrivialJobLambdaMove) + { + int x = 0; + + TypeErasedJob job( + defaultJD, + [&x]() + { + ++x; + }); + + TypeErasedJob job2 = AZStd::move(job); + + job2.Invoke(); + + EXPECT_EQ(1, x); + } + + struct TrackMoves + { + TrackMoves() = default; + + TrackMoves(const TrackMoves&) = delete; + + TrackMoves(TrackMoves&& other) + : moveCount{other.moveCount + 1} + { + } + + int moveCount = 0; + }; + + struct TrackCopies + { + TrackCopies() = default; + + TrackCopies(TrackCopies&&) = delete; + + TrackCopies(const TrackCopies& other) + : copyCount{other.copyCount + 1} + { + } + + int copyCount = 0; + }; + + TEST(JobGraphTests, MoveOnlyJobLambda) + { + TrackMoves tm; + int moveCount = 0; + + TypeErasedJob job( + defaultJD, + [tm = AZStd::move(tm), &moveCount] + { + moveCount = tm.moveCount; + }); + job.Invoke(); + + // Two moves are expected. Once into the capture body of the lambda, once to construct + // the type erased job + EXPECT_EQ(2, moveCount); + } + + TEST(JobGraphTests, MoveOnlyJobLambdaMove) + { + TrackMoves tm; + int moveCount = 0; + + TypeErasedJob job( + defaultJD, + [tm = AZStd::move(tm), &moveCount] + { + moveCount = tm.moveCount; + }); + + TypeErasedJob job2 = AZStd::move(job); + job2.Invoke(); + + EXPECT_EQ(3, moveCount); + } + + TEST(JobGraphTests, CopyOnlyJobLambda) + { + TrackCopies tc; + int copyCount = 0; + + TypeErasedJob job( + defaultJD, + [tc, ©Count] + { + copyCount = tc.copyCount; + }); + job.Invoke(); + + // Two copies are expected. Once into the capture body of the lambda, once to construct + // the type erased job + EXPECT_EQ(2, copyCount); + } + + TEST(JobGraphTests, CopyOnlyJobLambdaMove) + { + TrackCopies tc; + int copyCount = 0; + + TypeErasedJob job( + defaultJD, + [tc, ©Count] + { + copyCount = tc.copyCount; + }); + TypeErasedJob job2 = AZStd::move(job); + job2.Invoke(); + + EXPECT_EQ(3, copyCount); + } + + TEST(JobGraphTests, DestroyLambda) + { + // This test ensures that for a lambda with a destructor, the destructor is invoked + // exactly once on a non-moved-from object. + int x = 0; + struct TrackDestroy + { + TrackDestroy(int* px) + : count{ px } + { + } + TrackDestroy(TrackDestroy&& other) + : count{ other.count } + { + other.count = nullptr; + } + ~TrackDestroy() + { + if (count) + { + ++*count; + } + } + int* count = nullptr; + }; + + { + TrackDestroy td{ &x }; + TypeErasedJob job( + defaultJD, + [td = AZStd::move(td)] + { + }); + job.Invoke(); + // Destructor should not have run yet (except on moved-from instances) + EXPECT_EQ(x, 0); + } + + // Destructor should have run now + EXPECT_EQ(x, 1); + } + + TEST_F(JobGraphTestFixture, SerialGraph) + { + int x = 0; + + JobGraph graph; + auto a = graph.AddJob( + defaultJD, + [&] + { + x += 3; + }); + auto b = graph.AddJob( + defaultJD, + [&] + { + x = 4 * x; + }); + auto c = graph.AddJob( + defaultJD, + [&] + { + x -= 1; + }); + + a.Precedes(b); + b.Precedes(c); + + JobGraphEvent ev; + graph.SubmitOnExecutor(*m_executor, &ev); + ev.Wait(); + + EXPECT_EQ(11, x); + } + + TEST_F(JobGraphTestFixture, DetachedGraph) + { + int x = 0; + + JobGraphEvent ev; + + { + JobGraph graph; + auto a = graph.AddJob( + defaultJD, + [&] + { + x += 3; + }); + auto b = graph.AddJob( + defaultJD, + [&] + { + x = 4 * x; + }); + auto c = graph.AddJob( + defaultJD, + [&] + { + x -= 1; + }); + + a.Precedes(b); + b.Precedes(c); + graph.Detach(); + graph.SubmitOnExecutor(*m_executor, &ev); + } + + ev.Wait(); + + EXPECT_EQ(11, x); + } + + TEST_F(JobGraphTestFixture, ForkJoin) + { + AZStd::atomic x = 0; + + // Job a initializes x to 3 + // Job b and c toggles the lowest two bits atomically + // Job d decrements x + + JobGraph graph; + auto a = graph.AddJob( + defaultJD, + [&] + { + x = 0b111; + }); + auto b = graph.AddJob( + defaultJD, + [&] + { + x ^= 1; + }); + auto c = graph.AddJob( + defaultJD, + [&] + { + x ^= 2; + }); + auto d = graph.AddJob( + defaultJD, + [&] + { + x -= 1; + }); + + // a <-- Root + // / \ + // b c + // \ / + // d + + a.Precedes(b, c); + b.Precedes(d); + c.Precedes(d); + + JobGraphEvent ev; + graph.SubmitOnExecutor(*m_executor, &ev); + ev.Wait(); + + EXPECT_EQ(3, x); + } + + TEST_F(JobGraphTestFixture, SpawnSubgraph) + { + AZStd::atomic x = 0; + + JobGraph graph; + auto a = graph.AddJob( + defaultJD, + [&] + { + x = 0b111; + }); + auto b = graph.AddJob( + defaultJD, + [&] + { + x ^= 1; + }); + auto c = graph.AddJob( + defaultJD, + [&] + { + x ^= 2; + + JobGraph subgraph; + auto e = subgraph.AddJob( + defaultJD, + [&] + { + x ^= 0b1000; + }); + auto f = subgraph.AddJob( + defaultJD, + [&] + { + x ^= 0b10000; + }); + auto g = subgraph.AddJob( + defaultJD, + [&] + { + x += 0b1000; + }); + e.Precedes(g); + f.Precedes(g); + JobGraphEvent ev; + subgraph.SubmitOnExecutor(*m_executor, &ev); + ev.Wait(); + }); + auto d = graph.AddJob( + defaultJD, + [&] + { + x -= 1; + }); + + // NOTE: The ideal way to express this topology is without the wait on the subgraph + // at task g, but this is more an illustrative test. Better is to express the entire + // graph in a single larger graph. + // a <-- Root + // / \ + // b c - f + // \ \ \ + // \ e - g + // \ / + // \ / + // \ / + // d + + a.Precedes(b); + a.Precedes(c); + b.Precedes(d); + c.Precedes(d); + + JobGraphEvent ev; + graph.SubmitOnExecutor(*m_executor, &ev); + ev.Wait(); + + EXPECT_EQ(3 | 0b100000, x); + } + + TEST_F(JobGraphTestFixture, RetainedGraph) + { + AZStd::atomic x = 0; + + JobGraph graph; + auto a = graph.AddJob( + defaultJD, + [&] + { + x = 0b111; + }); + auto b = graph.AddJob( + defaultJD, + [&] + { + x ^= 1; + }); + auto c = graph.AddJob( + defaultJD, + [&] + { + x ^= 2; + }); + auto d = graph.AddJob( + defaultJD, + [&] + { + x -= 1; + }); + auto e = graph.AddJob( + defaultJD, + [&] + { + x ^= 0b1000; + }); + auto f = graph.AddJob( + defaultJD, + [&] + { + x ^= 0b10000; + }); + auto g = graph.AddJob( + defaultJD, + [&] + { + x += 0b1000; + }); + + // a <-- Root + // / \ + // b c - f + // \ \ \ + // \ e - g + // \ / + // \ / + // \ / + // d + + a.Precedes(b, c); + b.Precedes(d); + c.Precedes(e, f); + e.Precedes(g); + f.Precedes(g); + g.Precedes(d); + + JobGraphEvent ev; + graph.SubmitOnExecutor(*m_executor, &ev); + ev.Wait(); + + EXPECT_EQ(3 | 0b100000, x); + x = 0; + + graph.SubmitOnExecutor(*m_executor, &ev); + ev.Wait(); + + EXPECT_EQ(3 | 0b100000, x); + } +} // namespace UnitTest + +#if defined(HAVE_BENCHMARK) +namespace Benchmark +{ + class JobGraphBenchmarkFixture : public ::benchmark::Fixture + { + public: + static const int32_t LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH = 1; + static const int32_t MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH = 1024; + static const int32_t HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH = 1048576; + + static const int32_t SMALL_NUMBER_OF_JOBS = 10; + static const int32_t MEDIUM_NUMBER_OF_JOBS = 1024; + static const int32_t LARGE_NUMBER_OF_JOBS = 16384; + static AZStd::atomic s_numIncompleteJobs; + + int m_depth = 1; + JobGraph* graphs; + + void SetUp(benchmark::State&) override + { + s_numIncompleteJobs = 0; + + m_executor = aznew JobExecutor(0); + graphs = new JobGraph[4]; + + // Generate some random priorities + m_randomPriorities.resize(LARGE_NUMBER_OF_JOBS); + std::mt19937_64 randomPriorityGenerator(1); // Always use the same seed + std::uniform_int_distribution<> randomPriorityDistribution(0, static_cast(AZ::JobPriority::PRIORITY_COUNT)); + std::generate( + m_randomPriorities.begin(), m_randomPriorities.end(), + [&randomPriorityDistribution, &randomPriorityGenerator]() + { + return randomPriorityDistribution(randomPriorityGenerator); + }); + + // Generate some random depths + m_randomDepths.resize(LARGE_NUMBER_OF_JOBS); + std::mt19937_64 randomDepthGenerator(1); // Always use the same seed + std::uniform_int_distribution<> randomDepthDistribution( + LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); + std::generate( + m_randomDepths.begin(), m_randomDepths.end(), + [&randomDepthDistribution, &randomDepthGenerator]() + { + return randomDepthDistribution(randomDepthGenerator); + }); + + for (size_t i = 0; i != 4; ++i) + { + graphs[i].AddJob( + descriptors[i], + [this] + { + benchmark::DoNotOptimize(CalculatePi(m_depth)); + --s_numIncompleteJobs; + }); + } + } + + void TearDown(benchmark::State&) override + { + delete[] graphs; + azdestroy(m_executor); + m_randomDepths = {}; + m_randomPriorities = {}; + } + + JobDescriptor descriptors[4] = { { "critical", "benchmark", JobPriority::CRITICAL }, + { "high", "benchmark", JobPriority::HIGH }, + { "mediium", "benchmark", JobPriority::MEDIUM }, + { "low", "benchmark", JobPriority::LOW } }; + + static inline double CalculatePi(AZ::u32 depth) + { + double pi = 0.0; + for (AZ::u32 i = 0; i < depth; ++i) + { + const double numerator = static_cast(((i % 2) * 2) - 1); + const double denominator = static_cast((2 * i) - 1); + pi += numerator / denominator; + } + return (pi - 1.0) * 4; + } + + void RunCalculatePiJob(int32_t depth, int8_t priority) + { + m_depth = depth; + ++s_numIncompleteJobs; + + graphs[priority].SubmitOnExecutor(*m_executor); + } + + void RunMultipleCalculatePiJobsWithDefaultPriority(uint32_t numberOfJobs, int32_t depth) + { + for (size_t i = 0; i != numberOfJobs; ++i) + { + RunCalculatePiJob(depth, 2); + } + + while (s_numIncompleteJobs > 0) + { + } + } + + void RunMultipleCalculatePiJobsWithRandomPriority(uint32_t numberOfJobs, int32_t depth) + { + for (size_t i = 0; i != numberOfJobs; ++i) + { + RunCalculatePiJob(depth, m_randomPriorities[i]); + } + + while (s_numIncompleteJobs > 0) + { + } + } + + void RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(uint32_t numberOfJobs) + { + for (size_t i = 0; i != numberOfJobs; ++i) + { + RunCalculatePiJob(m_randomDepths[i], 0); + } + + while (s_numIncompleteJobs > 0) + { + } + } + + void RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(uint32_t numberOfJobs) + { + for (size_t i = 0; i != numberOfJobs; ++i) + { + RunCalculatePiJob(m_randomDepths[i], m_randomPriorities[i]); + } + + while (s_numIncompleteJobs > 0) + { + } + } + + JobExecutor* m_executor; + AZStd::vector m_randomDepths; + AZStd::vector m_randomPriorities; + }; + + AZStd::atomic JobGraphBenchmarkFixture::s_numIncompleteJobs = 0; + + BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfLightWeightJobsWithDefaultPriority)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithDefaultPriority(SMALL_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfLightWeightJobsWithDefaultPriority)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithDefaultPriority(MEDIUM_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfLightWeightJobsWithDefaultPriority)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithDefaultPriority(LARGE_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfMediumWeightJobsWithDefaultPriority)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithDefaultPriority(SMALL_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfMediumWeightJobsWithDefaultPriority)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithDefaultPriority(MEDIUM_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfMediumWeightJobsWithDefaultPriority)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithDefaultPriority(LARGE_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfHeavyWeightJobsWithDefaultPriority)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithDefaultPriority(SMALL_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfHeavyWeightJobsWithDefaultPriority)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithDefaultPriority(MEDIUM_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfHeavyWeightJobsWithDefaultPriority)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithDefaultPriority(LARGE_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfRandomWeightJobsWithDefaultPriority)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(SMALL_NUMBER_OF_JOBS); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfRandomWeightJobsWithDefaultPriority)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(MEDIUM_NUMBER_OF_JOBS); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfRandomWeightJobsWithDefaultPriority)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(LARGE_NUMBER_OF_JOBS); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfLightWeightJobsWithRandomPriorities)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomPriority(SMALL_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfLightWeightJobsWithRandomPriorities)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomPriority(MEDIUM_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfLightWeightJobsWithRandomPriorities)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomPriority(LARGE_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfMediumWeightJobsWithRandomPriorities)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomPriority(SMALL_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfMediumWeightJobsWithRandomPriorities)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomPriority(MEDIUM_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfMediumWeightJobsWithRandomPriorities)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomPriority(LARGE_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfHeavyWeightJobsWithRandomPriorities)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomPriority(SMALL_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfHeavyWeightJobsWithRandomPriorities)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomPriority(MEDIUM_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfHeavyWeightJobsWithRandomPriorities)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomPriority(LARGE_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfRandomWeightJobsWithRandomPriorities)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(SMALL_NUMBER_OF_JOBS); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfRandomWeightJobsWithRandomPriorities)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(MEDIUM_NUMBER_OF_JOBS); + } + } + + BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfRandomWeightJobsWithRandomPriorities)(benchmark::State& state) + { + for (auto _ : state) + { + RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(LARGE_NUMBER_OF_JOBS); + } + } +} // namespace Benchmark +#endif diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index d340173c87..480baabe7a 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -40,6 +40,7 @@ set(FILES Interface.cpp IO/Path/PathTests.cpp IPC.cpp + JobGraphTests.cpp Jobs.cpp JSON.cpp FixedWidthIntegers.cpp From d1c06e9c804bbaee2e098fcf4f5610679a9f12a9 Mon Sep 17 00:00:00 2001 From: Jeremy Ong Date: Tue, 3 Aug 2021 23:37:56 -0600 Subject: [PATCH 18/38] Add JobGraph::Reset, streamline execution, address feedback Also, came up with more useful benchmarks that actually measure the enqueue/dequeue operations for various simple workflows. For retained graphs, time-of-flight from submission to execution is ~1us per job, indicating job granularity should be >20us for retained jobs. For dynamic jobs, where we need to pay the cost of allocation, a granularity of ~100+ us may be advised. Signed-off-by: Jeremy Ong --- .../AzCore/Jobs/Internal/JobTypeEraser.h | 19 +- .../AzCore/AzCore/Jobs/JobExecutor.cpp | 87 ++--- .../AzCore/AzCore/Jobs/JobExecutor.h | 15 +- .../Framework/AzCore/AzCore/Jobs/JobGraph.cpp | 37 +- Code/Framework/AzCore/AzCore/Jobs/JobGraph.h | 20 +- .../Framework/AzCore/AzCore/Jobs/JobGraph.inl | 14 +- Code/Framework/AzCore/Tests/JobGraphTests.cpp | 362 +++--------------- 7 files changed, 168 insertions(+), 386 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h index 1455f1311a..11d7f955b4 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h +++ b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -105,15 +106,16 @@ namespace AZ::Internal class alignas(alignof(max_align_t)) TypeErasedJob final { public: - // The inline buffer allows the TypeErasedJob to span two cache lines. Lambdas can capture 56 - // bytes of data (7 pointers/references on a 64-bit machine) before spilling to the heap. - constexpr static size_t BufferSize = 128 - sizeof(size_t) * 6 - sizeof(uint32_t) - sizeof(JobDescriptor); + // The inline buffer allows the TypeErasedJob to span two cache lines. Lambdas can capture 48 + // bytes of data (6 pointers/references on a 64-bit machine) before spilling to the heap. + constexpr static size_t BufferSize = + 128 - sizeof(size_t) * 6 - sizeof(uint32_t) - sizeof(JobDescriptor) - sizeof(AZStd::atomic); TypeErasedJob() = default; - template + template TypeErasedJob(JobDescriptor const& desc, Lambda&& lambda) noexcept - : m_descriptor{desc} + : m_descriptor{ desc } { JobTypeEraser eraser; m_invoker = eraser.ErasedInvoker(); @@ -147,9 +149,9 @@ namespace AZ::Internal // Indicates if this job is a root of the graph (with no dependencies) bool IsRoot(); - void AttachToJobGraph(CompiledJobGraph& graph) noexcept + void Init() noexcept { - m_graph = &graph; + m_dependencyCount = m_inboundLinkCount; } void Invoke() @@ -167,7 +169,7 @@ namespace AZ::Internal friend class JobWorker; // This relocation avoids branches needed if the lambda type is unknown - template + template void TypedRelocate(Lambda&& lambda, char* destination) { if constexpr (AZStd::is_trivially_move_constructible_v) @@ -195,6 +197,7 @@ namespace AZ::Internal // class to equal the alignment of the largest scalar type available on the system (generally // 16 bytes). char m_buffer[BufferSize]; + AZStd::atomic m_dependencyCount; // This value is an offset in a buffer that stores dependency tracking information. uint32_t m_successorOffset = 0; diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp index 16e4983752..aa7f696942 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp +++ b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp @@ -29,19 +29,18 @@ namespace AZ AZStd::vector&& jobs, AZStd::unordered_map>& links, size_t linkCount, - bool retained) - : m_remaining{ jobs.size() } - , m_retained{ retained } + JobGraph* parent) + : m_parent{ parent } { m_jobs = AZStd::move(jobs); - m_dependencyCounts = reinterpret_cast*>(azcalloc(sizeof(AZStd::atomic) * m_jobs.size())); m_successors.resize(linkCount); - uint32_t* cursor = m_successors.data(); + TypeErasedJob** cursor = m_successors.data(); for (size_t i = 0; i != m_jobs.size(); ++i) { TypeErasedJob& job = m_jobs[i]; + job.m_graph = this; job.m_successorOffset = cursor - m_successors.data(); cursor += job.m_outboundLinkCount; @@ -49,54 +48,42 @@ namespace AZ for (uint32_t j = 0; j != job.m_outboundLinkCount; ++j) { - m_successors[static_cast(job.m_successorOffset) + j] = links[i][j]; - } - - if (job.m_inboundLinkCount > 0) - { - m_dependencyCounts[i].store(job.m_inboundLinkCount, AZStd::memory_order_release); + m_successors[static_cast(job.m_successorOffset) + j] = &m_jobs[links[i][j]]; } } // TODO: Check for dependency cycles } - CompiledJobGraph::~CompiledJobGraph() + uint32_t CompiledJobGraph::Release() { - if (m_dependencyCounts) - { - azfree(m_dependencyCounts); - } - } + uint32_t remaining = --m_remaining; - void CompiledJobGraph::Release() - { - if (--m_remaining == 0) + if (m_parent) { - if (m_retained) + if (remaining == 1) { - m_remaining = m_jobs.size(); - for (size_t i = 0; i != m_jobs.size(); ++i) - { - TypeErasedJob& job = m_jobs[i]; - if (job.m_inboundLinkCount > 0) - { - m_dependencyCounts[i].store(job.m_inboundLinkCount, AZStd::memory_order_release); - } - } + // Allow the parent graph to be submitted again + m_parent->m_submitted = false; } - + } + else if (remaining == 0) + { if (m_waitEvent) { - m_waitEvent->m_submitted = false; m_waitEvent->Signal(); } - if (!m_retained) - { - azdestroy(this); - } + azdestroy(this); + return remaining; } + + if (m_waitEvent && remaining == (m_parent ? 1 : 0)) + { + m_waitEvent->Signal(); + } + + return remaining; } struct QueueStatus @@ -124,7 +111,7 @@ namespace AZ JobQueue(const JobQueue&) = delete; JobQueue& operator=(const JobQueue&) = delete; - bool Enqueue(TypeErasedJob* job); + void Enqueue(TypeErasedJob* job); TypeErasedJob* TryDequeue(); private: @@ -132,7 +119,7 @@ namespace AZ TypeErasedJob* m_queues[PriorityLevelCount][MaxQueueSize] = {}; }; - bool JobQueue::Enqueue(TypeErasedJob* job) + void JobQueue::Enqueue(TypeErasedJob* job) { uint8_t priority = job->GetPriorityNumber(); QueueStatus& status = m_status[priority]; @@ -159,7 +146,7 @@ namespace AZ expectedReserve = reserve; } - return status.head == status.tail - 1; + return; } // We failed to reserve a slot, try again @@ -233,9 +220,11 @@ namespace AZ void Enqueue(TypeErasedJob* job) { - if (m_queue.Enqueue(job)) + m_queue.Enqueue(job); + + if (!m_busy.exchange(true)) { - // The queue was empty prior to enqueueing the job, release the semaphore + // The worker was idle prior to enqueueing the job, release the semaphore m_semaphore.release(); } } @@ -245,14 +234,16 @@ namespace AZ { while (m_active) { + m_busy = false; m_semaphore.acquire(); - // m_semaphore.try_acquire_for(AZStd::chrono::microseconds{ 10 }); if (!m_active) { return; } + m_busy = true; + TypeErasedJob* job = m_queue.TryDequeue(); while (job) { @@ -260,10 +251,10 @@ namespace AZ // Decrement counts for all job successors for (size_t j = 0; j != job->m_outboundLinkCount; ++j) { - uint32_t successorIndex = job->m_graph->m_successors[job->m_successorOffset + j]; - if (--job->m_graph->m_dependencyCounts[successorIndex] == 0) + TypeErasedJob* successor = job->m_graph->m_successors[job->m_successorOffset + j]; + if (--successor->m_dependencyCount == 0) { - m_executor->Submit(job->m_graph->m_jobs[successorIndex]); + m_executor->Submit(*successor); } } @@ -277,6 +268,7 @@ namespace AZ AZStd::thread m_thread; AZStd::atomic m_active; + AZStd::atomic m_busy; AZStd::binary_semaphore m_semaphore; ::AZ::JobExecutor* m_executor; @@ -327,11 +319,6 @@ namespace AZ void JobExecutor::Submit(Internal::CompiledJobGraph& graph) { - for (Internal::TypeErasedJob& job : graph.Jobs()) - { - job.AttachToJobGraph(graph); - } - // Submit all jobs that have no inbound edges for (Internal::TypeErasedJob& job : graph.Jobs()) { diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h index 9418126678..60176f0717 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h +++ b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h @@ -18,6 +18,7 @@ namespace AZ { class JobGraphEvent; + class JobGraph; namespace Internal { @@ -30,9 +31,7 @@ namespace AZ AZStd::vector&& jobs, AZStd::unordered_map>& links, size_t linkCount, - bool retained); - - ~CompiledJobGraph(); + JobGraph* parent); AZStd::vector& Jobs() noexcept { @@ -40,19 +39,19 @@ namespace AZ } // Indicate that a constituent job has finished and decrement a counter to determine if the - // graph should be freed - void Release(); + // graph should be freed (returns the value after atomic decrement) + uint32_t Release(); private: friend class JobGraph; friend class JobWorker; AZStd::vector m_jobs; - AZStd::vector m_successors; - AZStd::atomic* m_dependencyCounts = nullptr; + AZStd::vector m_successors; JobGraphEvent* m_waitEvent = nullptr; + // The pointer to the parent graph is set only if it is retained + JobGraph* m_parent = nullptr; AZStd::atomic m_remaining; - bool m_retained; }; class JobWorker; diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp index b715e0ada7..6b34f1273a 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp +++ b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp @@ -30,10 +30,27 @@ namespace AZ { if (m_retained && m_compiledJobGraph) { - azdestroy(m_compiledJobGraph); + // This job graph has already finished and we are potentially responsible for its destruction + if (m_compiledJobGraph->Release() == 0) + { + azdestroy(m_compiledJobGraph); + } } } + void JobGraph::Reset() + { + AZ_Assert(!m_submitted, "Cannot reset a job graph while it is in flight"); + if (m_compiledJobGraph) + { + azdestroy(m_compiledJobGraph); + m_compiledJobGraph = nullptr; + } + m_jobs.clear(); + m_links.clear(); + m_linkCount = 0; + } + void JobGraph::Submit(JobGraphEvent* waitEvent) { SubmitOnExecutor(JobExecutor::Instance(), waitEvent); @@ -41,20 +58,28 @@ namespace AZ void JobGraph::SubmitOnExecutor(JobExecutor& executor, JobGraphEvent* waitEvent) { - m_submitted = true; - if (!m_compiledJobGraph) { - m_compiledJobGraph = aznew CompiledJobGraph(AZStd::move(m_jobs), m_links, m_linkCount, m_retained); + m_compiledJobGraph = aznew CompiledJobGraph(AZStd::move(m_jobs), m_links, m_linkCount, m_retained ? this : nullptr); } m_compiledJobGraph->m_waitEvent = waitEvent; + m_compiledJobGraph->m_remaining = m_compiledJobGraph->m_jobs.size() + (m_retained ? 1 : 0); + for (size_t i = 0; i != m_compiledJobGraph->m_jobs.size(); ++i) + { + m_compiledJobGraph->m_jobs[i].Init(); + } executor.Submit(*m_compiledJobGraph); - if (waitEvent) + if (m_retained) { - waitEvent->m_submitted = true; + m_submitted = true; + } + else + { + m_compiledJobGraph = nullptr; + Reset(); } } } diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h index 62565ea78c..070236b0b5 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h +++ b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h @@ -12,7 +12,7 @@ // suited in the private CompiledJobGraph implementation instead to keep this header lean. #include #include -#include +#include #include #include #include @@ -30,10 +30,14 @@ namespace AZ class JobToken final { public: - // Indicate that this job must finish before the job passed as the argument + // Indicate that this job must finish before the job token(s) passed as the argument template void Precedes(JT&... tokens); + // Indicate that this job must finish after the job token(s) passed as the argument + template + void Succeeds(JT&... tokens); + private: friend class JobGraph; @@ -67,7 +71,6 @@ namespace AZ void Signal(); AZStd::binary_semaphore m_semaphore; - bool m_submitted = false; }; // The JobGraph encapsulates a set of jobs and their interdependencies. After adding @@ -81,13 +84,18 @@ namespace AZ public: ~JobGraph(); + // Reset the state of the job graph to begin recording jobs and edges again + // NOTE: Graph must be in a "settled" state (cannot be in-flight) + void Reset(); + // Add a job to the graph, retrieiving a token that can be used to express dependencies // between jobs. The first argument specifies the JobKind, used for tracking the job. + // NOTE: This operation is invalid if the graph is in-flight template JobToken AddJob(JobDescriptor const& descriptor, Lambda&& lambda); template - AZStd::fixed_vector AddJobs(JobDescriptor const& descriptor, Lambdas&&... lambdas); + AZStd::array AddJobs(JobDescriptor const& descriptor, Lambdas&&... lambdas); // By default, you are responsible for retaining the JobGraph, indicating you promise that // this JobGraph will live as long as it takes for all constituent jobs to complete. @@ -103,6 +111,7 @@ namespace AZ // of the job graph is expected to rely on either indirection, or safe overwriting // of previously used memory to supply new data (this can even be done as the first // job in the graph). + // NOTE: This operation is invalid if the graph is in-flight void Detach(); // Invoke the job graph, asserting if there are dependency violations. Note that @@ -122,6 +131,7 @@ namespace AZ private: friend class JobToken; + friend class Internal::CompiledJobGraph; Internal::CompiledJobGraph* m_compiledJobGraph = nullptr; @@ -132,7 +142,7 @@ namespace AZ uint32_t m_linkCount = 0; bool m_retained = true; - bool m_submitted = false; + AZStd::atomic m_submitted = false; }; } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl index f541d9923f..ad1fb3505c 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl +++ b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl @@ -22,15 +22,19 @@ namespace AZ (PrecedesInternal(tokens), ...); } + template + inline void JobToken::Succeeds(JT&... tokens) + { + (tokens.PrecedesInternal(*this), ...); + } + inline bool JobGraphEvent::IsSignaled() { - AZ_Assert(m_submitted, "Querying the status of a job graph event that was never submitted along with the jobgraph"); return m_semaphore.try_acquire_for(AZStd::chrono::milliseconds{ 0 }); } inline void JobGraphEvent::Wait() { - AZ_Assert(m_submitted, "Waiting on a job graph event that was never submitted along with the jobgraph"); m_semaphore.acquire(); } @@ -42,7 +46,7 @@ namespace AZ template inline JobToken JobGraph::AddJob(JobDescriptor const& desc, Lambda&& lambda) { - AZ_Assert(!m_submitted, "Cannot mutate a JobGraph that was previously submitted."); + AZ_Assert(!m_submitted, "Cannot mutate a JobGraph that was previously submitted or in flight."); m_jobs.emplace_back(desc, AZStd::forward(lambda)); @@ -50,9 +54,9 @@ namespace AZ } template - inline AZStd::fixed_vector AddJobs(JobDescriptor const& descriptor, Lambdas&&... lambdas) + inline AZStd::array JobGraph::AddJobs(JobDescriptor const& descriptor, Lambdas&&... lambdas) { - return { AddJob(descriptor, lambdas)... }; + return { AddJob(descriptor, AZStd::forward(lambdas))... }; } inline void JobGraph::Detach() diff --git a/Code/Framework/AzCore/Tests/JobGraphTests.cpp b/Code/Framework/AzCore/Tests/JobGraphTests.cpp index 175881b89a..b080e5b679 100644 --- a/Code/Framework/AzCore/Tests/JobGraphTests.cpp +++ b/Code/Framework/AzCore/Tests/JobGraphTests.cpp @@ -336,8 +336,7 @@ namespace UnitTest // d a.Precedes(b, c); - b.Precedes(d); - c.Precedes(d); + d.Succeeds(b, c); JobGraphEvent ev; graph.SubmitOnExecutor(*m_executor, &ev); @@ -487,8 +486,7 @@ namespace UnitTest a.Precedes(b, c); b.Precedes(d); c.Precedes(e, f); - e.Precedes(g); - f.Precedes(g); + g.Succeeds(e, f); g.Precedes(d); JobGraphEvent ev; @@ -511,338 +509,94 @@ namespace Benchmark class JobGraphBenchmarkFixture : public ::benchmark::Fixture { public: - static const int32_t LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH = 1; - static const int32_t MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH = 1024; - static const int32_t HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH = 1048576; - - static const int32_t SMALL_NUMBER_OF_JOBS = 10; - static const int32_t MEDIUM_NUMBER_OF_JOBS = 1024; - static const int32_t LARGE_NUMBER_OF_JOBS = 16384; - static AZStd::atomic s_numIncompleteJobs; - - int m_depth = 1; - JobGraph* graphs; - void SetUp(benchmark::State&) override { - s_numIncompleteJobs = 0; - - m_executor = aznew JobExecutor(0); - graphs = new JobGraph[4]; - - // Generate some random priorities - m_randomPriorities.resize(LARGE_NUMBER_OF_JOBS); - std::mt19937_64 randomPriorityGenerator(1); // Always use the same seed - std::uniform_int_distribution<> randomPriorityDistribution(0, static_cast(AZ::JobPriority::PRIORITY_COUNT)); - std::generate( - m_randomPriorities.begin(), m_randomPriorities.end(), - [&randomPriorityDistribution, &randomPriorityGenerator]() - { - return randomPriorityDistribution(randomPriorityGenerator); - }); - - // Generate some random depths - m_randomDepths.resize(LARGE_NUMBER_OF_JOBS); - std::mt19937_64 randomDepthGenerator(1); // Always use the same seed - std::uniform_int_distribution<> randomDepthDistribution( - LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - std::generate( - m_randomDepths.begin(), m_randomDepths.end(), - [&randomDepthDistribution, &randomDepthGenerator]() - { - return randomDepthDistribution(randomDepthGenerator); - }); - - for (size_t i = 0; i != 4; ++i) - { - graphs[i].AddJob( - descriptors[i], - [this] - { - benchmark::DoNotOptimize(CalculatePi(m_depth)); - --s_numIncompleteJobs; - }); - } + executor = new JobExecutor; + graph = new JobGraph; } void TearDown(benchmark::State&) override { - delete[] graphs; - azdestroy(m_executor); - m_randomDepths = {}; - m_randomPriorities = {}; + delete graph; + delete executor; } JobDescriptor descriptors[4] = { { "critical", "benchmark", JobPriority::CRITICAL }, { "high", "benchmark", JobPriority::HIGH }, - { "mediium", "benchmark", JobPriority::MEDIUM }, + { "medium", "benchmark", JobPriority::MEDIUM }, { "low", "benchmark", JobPriority::LOW } }; - static inline double CalculatePi(AZ::u32 depth) - { - double pi = 0.0; - for (AZ::u32 i = 0; i < depth; ++i) - { - const double numerator = static_cast(((i % 2) * 2) - 1); - const double denominator = static_cast((2 * i) - 1); - pi += numerator / denominator; - } - return (pi - 1.0) * 4; - } - - void RunCalculatePiJob(int32_t depth, int8_t priority) - { - m_depth = depth; - ++s_numIncompleteJobs; - - graphs[priority].SubmitOnExecutor(*m_executor); - } - - void RunMultipleCalculatePiJobsWithDefaultPriority(uint32_t numberOfJobs, int32_t depth) - { - for (size_t i = 0; i != numberOfJobs; ++i) - { - RunCalculatePiJob(depth, 2); - } - - while (s_numIncompleteJobs > 0) - { - } - } - - void RunMultipleCalculatePiJobsWithRandomPriority(uint32_t numberOfJobs, int32_t depth) - { - for (size_t i = 0; i != numberOfJobs; ++i) - { - RunCalculatePiJob(depth, m_randomPriorities[i]); - } - - while (s_numIncompleteJobs > 0) - { - } - } - - void RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(uint32_t numberOfJobs) - { - for (size_t i = 0; i != numberOfJobs; ++i) - { - RunCalculatePiJob(m_randomDepths[i], 0); - } - - while (s_numIncompleteJobs > 0) - { - } - } - - void RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(uint32_t numberOfJobs) - { - for (size_t i = 0; i != numberOfJobs; ++i) - { - RunCalculatePiJob(m_randomDepths[i], m_randomPriorities[i]); - } - - while (s_numIncompleteJobs > 0) - { - } - } - - JobExecutor* m_executor; - AZStd::vector m_randomDepths; - AZStd::vector m_randomPriorities; + JobGraph* graph; + JobExecutor* executor; }; - AZStd::atomic JobGraphBenchmarkFixture::s_numIncompleteJobs = 0; - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfLightWeightJobsWithDefaultPriority)(benchmark::State& state) + BENCHMARK_F(JobGraphBenchmarkFixture, QueueToDequeue)(benchmark::State& state) { + graph->AddJob( + descriptors[2], + [] + { + }); for (auto _ : state) { - RunMultipleCalculatePiJobsWithDefaultPriority(SMALL_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); + JobGraphEvent ev; + graph->SubmitOnExecutor(*executor, &ev); + ev.Wait(); } } - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfLightWeightJobsWithDefaultPriority)(benchmark::State& state) + BENCHMARK_F(JobGraphBenchmarkFixture, OneAfterAnother)(benchmark::State& state) { + auto a = graph->AddJob( + descriptors[2], + [] + { + }); + auto b = graph->AddJob( + descriptors[2], + [] + { + }); + a.Precedes(b); + for (auto _ : state) { - RunMultipleCalculatePiJobsWithDefaultPriority(MEDIUM_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); + JobGraphEvent ev; + graph->SubmitOnExecutor(*executor, &ev); + ev.Wait(); } + executor->Drain(); } - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfLightWeightJobsWithDefaultPriority)(benchmark::State& state) + BENCHMARK_F(JobGraphBenchmarkFixture, FourToOneJoin)(benchmark::State& state) { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithDefaultPriority(LARGE_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } + auto [a, b, c, d, e] = graph->AddJobs( + descriptors[2], + [] + { + }, + [] + { + }, + [] + { + }, + [] + { + }, + [] + { + }); - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfMediumWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithDefaultPriority(SMALL_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } + e.Succeeds(a, b, c, d); - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfMediumWeightJobsWithDefaultPriority)(benchmark::State& state) - { for (auto _ : state) { - RunMultipleCalculatePiJobsWithDefaultPriority(MEDIUM_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfMediumWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithDefaultPriority(LARGE_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfHeavyWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithDefaultPriority(SMALL_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfHeavyWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithDefaultPriority(MEDIUM_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfHeavyWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithDefaultPriority(LARGE_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfRandomWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(SMALL_NUMBER_OF_JOBS); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfRandomWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(MEDIUM_NUMBER_OF_JOBS); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfRandomWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(LARGE_NUMBER_OF_JOBS); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfLightWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(SMALL_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfLightWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(MEDIUM_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfLightWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(LARGE_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfMediumWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(SMALL_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfMediumWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(MEDIUM_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfMediumWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(LARGE_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfHeavyWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(SMALL_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfHeavyWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(MEDIUM_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfHeavyWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(LARGE_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfRandomWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(SMALL_NUMBER_OF_JOBS); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfRandomWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(MEDIUM_NUMBER_OF_JOBS); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfRandomWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(LARGE_NUMBER_OF_JOBS); + JobGraphEvent ev; + graph->SubmitOnExecutor(*executor, &ev); + ev.Wait(); } + executor->Drain(); } } // namespace Benchmark #endif From 6ac74ad41e20a0ec31b372a039ff080338b475d4 Mon Sep 17 00:00:00 2001 From: Jeremy Ong Date: Wed, 4 Aug 2021 03:12:17 -0600 Subject: [PATCH 19/38] Resolve clang compiler error "If constexpr" branches are evaluated at template instantiation time, but static assertions receiving false are triggered even earlier. Signed-off-by: Jeremy Ong --- Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h index 11d7f955b4..e57ba85176 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h +++ b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h @@ -59,7 +59,7 @@ namespace AZ::Internal else { static_assert( - false, + AZStd::is_move_constructible_v || AZStd::is_copy_constructible_v, "Job lambdas must be either move or copy constructible. Please verify that all captured data is move or copy " "constructible."); } @@ -187,7 +187,7 @@ namespace AZ::Internal else { static_assert( - false, + AZStd::is_move_constructible_v || AZStd::is_copy_constructible_v, "Job lambdas must be either move or copy constructible. Please verify that all captured data is move or copy " "constructible."); } From 4d058f329b0eb742adc81040a12c9cae85357c3a Mon Sep 17 00:00:00 2001 From: Jeremy Ong Date: Wed, 4 Aug 2021 03:23:16 -0600 Subject: [PATCH 20/38] Use exponential backoff during job submission when ring buffers are full Signed-off-by: Jeremy Ong --- Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp | 9 +++++---- Code/Framework/AzCore/Tests/JobGraphTests.cpp | 2 -- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp index aa7f696942..83823515c1 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp +++ b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp @@ -9,13 +9,14 @@ #include #include -#include #include #include -#include +#include #include #include +#include #include +#include #include @@ -124,6 +125,7 @@ namespace AZ uint8_t priority = job->GetPriorityNumber(); QueueStatus& status = m_status[priority]; + AZStd::exponential_backoff backoff; while (true) { uint16_t reserve = status.reserve.load(); @@ -153,8 +155,7 @@ namespace AZ } else { - // TODO need exponential backup here - AZStd::this_thread::sleep_for(AZStd::chrono::microseconds{ 100 }); + backoff.wait(); } } } diff --git a/Code/Framework/AzCore/Tests/JobGraphTests.cpp b/Code/Framework/AzCore/Tests/JobGraphTests.cpp index b080e5b679..469773f471 100644 --- a/Code/Framework/AzCore/Tests/JobGraphTests.cpp +++ b/Code/Framework/AzCore/Tests/JobGraphTests.cpp @@ -565,7 +565,6 @@ namespace Benchmark graph->SubmitOnExecutor(*executor, &ev); ev.Wait(); } - executor->Drain(); } BENCHMARK_F(JobGraphBenchmarkFixture, FourToOneJoin)(benchmark::State& state) @@ -596,7 +595,6 @@ namespace Benchmark graph->SubmitOnExecutor(*executor, &ev); ev.Wait(); } - executor->Drain(); } } // namespace Benchmark #endif From d2f2a186cb124a34ed9811f6ae1baa3243241b0c Mon Sep 17 00:00:00 2001 From: Jeremy Ong Date: Wed, 4 Aug 2021 03:32:49 -0600 Subject: [PATCH 21/38] Add forward declaration needed on clang Signed-off-by: Jeremy Ong --- Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h | 2 +- Code/Framework/AzCore/AzCore/Jobs/JobGraph.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h index 60176f0717..746edf00a1 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h +++ b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h @@ -43,7 +43,7 @@ namespace AZ uint32_t Release(); private: - friend class JobGraph; + friend class ::AZ::JobGraph; friend class JobWorker; AZStd::vector m_jobs; diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h index 070236b0b5..872a9a4e5c 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h +++ b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h @@ -24,6 +24,7 @@ namespace AZ class CompiledJobGraph; } class JobExecutor; + class JobGraph; // A JobToken is returned each time a Job is added to the JobGraph. JobTokens are used to // express dependencies between jobs within the graph. From eaa6e087cf7602e4fc57a087d13e80b24ba7941b Mon Sep 17 00:00:00 2001 From: Jeremy Ong Date: Thu, 5 Aug 2021 14:37:59 -0600 Subject: [PATCH 22/38] JobGraph -> TaskGraph (and associated classes/files) This commit also addresses all PR feedback Signed-off-by: Jeremy Ong --- .../AzCore/Jobs/Internal/JobTypeEraser.cpp | 76 ---- .../AzCore/Jobs/Internal/JobTypeEraser.h | 232 ---------- .../AzCore/AzCore/Jobs/JobDescriptor.h | 49 --- .../AzCore/AzCore/Jobs/JobExecutor.h | 85 ---- .../Framework/AzCore/AzCore/Jobs/JobGraph.cpp | 85 ---- Code/Framework/AzCore/AzCore/Jobs/JobGraph.h | 150 ------- .../AzCore/AzCore/Task/Internal/Task.cpp | 58 +++ .../AzCore/AzCore/Task/Internal/Task.h | 180 ++++++++ .../AzCore/AzCore/Task/Internal/Task.inl | 84 ++++ .../AzCore/AzCore/Task/Internal/TaskConfig.h | 14 + .../AzCore/AzCore/Task/TaskDescriptor.h | 49 +++ .../JobExecutor.cpp => Task/TaskExecutor.cpp} | 176 ++++---- .../AzCore/AzCore/Task/TaskExecutor.h | 94 ++++ .../AzCore/AzCore/Task/TaskGraph.cpp | 85 ++++ Code/Framework/AzCore/AzCore/Task/TaskGraph.h | 151 +++++++ .../{Jobs/JobGraph.inl => Task/TaskGraph.inl} | 26 +- .../AzCore/AzCore/azcore_files.cmake | 18 +- .../{JobGraphTests.cpp => TaskTests.cpp} | 405 ++++++++++++------ .../AzCore/Tests/azcoretests_files.cmake | 2 +- 19 files changed, 1109 insertions(+), 910 deletions(-) delete mode 100644 Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.cpp delete mode 100644 Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h delete mode 100644 Code/Framework/AzCore/AzCore/Jobs/JobDescriptor.h delete mode 100644 Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h delete mode 100644 Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp delete mode 100644 Code/Framework/AzCore/AzCore/Jobs/JobGraph.h create mode 100644 Code/Framework/AzCore/AzCore/Task/Internal/Task.cpp create mode 100644 Code/Framework/AzCore/AzCore/Task/Internal/Task.h create mode 100644 Code/Framework/AzCore/AzCore/Task/Internal/Task.inl create mode 100644 Code/Framework/AzCore/AzCore/Task/Internal/TaskConfig.h create mode 100644 Code/Framework/AzCore/AzCore/Task/TaskDescriptor.h rename Code/Framework/AzCore/AzCore/{Jobs/JobExecutor.cpp => Task/TaskExecutor.cpp} (62%) create mode 100644 Code/Framework/AzCore/AzCore/Task/TaskExecutor.h create mode 100644 Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp create mode 100644 Code/Framework/AzCore/AzCore/Task/TaskGraph.h rename Code/Framework/AzCore/AzCore/{Jobs/JobGraph.inl => Task/TaskGraph.inl} (50%) rename Code/Framework/AzCore/Tests/{JobGraphTests.cpp => TaskTests.cpp} (53%) diff --git a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.cpp b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.cpp deleted file mode 100644 index 043f232e32..0000000000 --- a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include - -namespace AZ::Internal -{ - TypeErasedJob::TypeErasedJob(TypeErasedJob&& other) noexcept - { - if (!other.m_relocator || other.m_lambda != other.m_buffer) - { - // The type-erased lambda is trivially relocatable OR, the lambda is heap allocated - memcpy(this, &other, sizeof(TypeErasedJob)); - - if (other.m_lambda == other.m_buffer) - { - m_lambda = m_buffer; - } - - // Prevent deletion in the event the lambda had spilled to the heap - other.m_lambda = nullptr; - return; - } - - // At this point, we know the lambda was inlined - m_lambda = m_buffer; - - m_invoker = other.m_invoker; - m_relocator = other.m_relocator; - m_destroyer = other.m_destroyer; - - // We now own the lambda, so clear the moved-from job's destroyer - other.m_destroyer = nullptr; - other.m_invoker = nullptr; - - m_relocator(m_buffer, other.m_buffer); - } - - TypeErasedJob& TypeErasedJob::operator=(TypeErasedJob&& other) noexcept - { - if (this == &other) - { - return *this; - } - - this->~TypeErasedJob(); - - new (this) TypeErasedJob{ AZStd::move(other) }; - - return *this; - } - - TypeErasedJob::~TypeErasedJob() - { - if (m_lambda) - { - if (m_destroyer) - { - // The presence of m_destroyer indicates that the lambda is not trivially destructible - m_destroyer(m_lambda); - } - - if (m_lambda != m_buffer) - { - // We've spilled the lambda into the heap, free its memory - azfree(m_lambda); - } - } - } - -} // namespace AZ::Internal diff --git a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h deleted file mode 100644 index e57ba85176..0000000000 --- a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace AZ::Internal -{ - using JobInvoke_t = void (*)(void* lambda); - using JobRelocate_t = void (*)(void* dst, void* src); - using JobDestroy_t = void (*)(void* obj); - - class CompiledJobGraph; - - // Lambdas are opaque types and we cannot extract any member function pointers. In order to store lambdas in a - // type erased fashion, we instead use a single function call indirection, invoking the lambda function in a - // static class function which has a stable address in memory. The Erased* methods return addresses to the - // indirect callers of the lambda copy/move assignment operators, call operator, and destructor. - // - // For lambdas that are trivially relocatable, both the returned move and copy assignment function pointers - // will be nullptr. - // - // Lambdas that are trivially destructible will result in a nullptr returned JobDestroy_t pointer. - // - // The class will check that the lambda is copy assignable or movable. - template - class JobTypeEraser final - { - public: - constexpr JobInvoke_t ErasedInvoker() - { - return reinterpret_cast(Invoker); - } - - constexpr JobRelocate_t ErasedRelocator() - { - if constexpr (AZStd::is_trivially_move_constructible_v) - { - return nullptr; - } - else if constexpr (AZStd::is_move_constructible_v) - { - return reinterpret_cast(Mover); - } - else if constexpr (AZStd::is_copy_constructible_v) - { - return reinterpret_cast(Copyer); - } - else - { - static_assert( - AZStd::is_move_constructible_v || AZStd::is_copy_constructible_v, - "Job lambdas must be either move or copy constructible. Please verify that all captured data is move or copy " - "constructible."); - } - } - - constexpr JobDestroy_t ErasedDestroyer() - { - if constexpr (AZStd::is_trivially_destructible_v) - { - return nullptr; - } - else - { - return reinterpret_cast(Destroyer); - } - } - - private: - constexpr static void Invoker(Lambda* lambda) - { - lambda->operator()(); - } - - constexpr static void Mover(Lambda* dst, Lambda* src) - { - new (dst) Lambda{ AZStd::move(*src) }; - } - - constexpr static void Copyer(Lambda* dst, Lambda* src) - { - new (dst) Lambda{ *src }; - } - - constexpr static void Destroyer(Lambda* lambda) - { - lambda->~Lambda(); - } - }; - - // The TypeErasedJob encapsulates member function pointers to store in a homogeneously-typed container - // The function signature of all lambdas encoded in a TypeErasedJob is void(*)(). The lambdas can capture - // data, in which case the data is inlined in this structure if the payload is less than or equal to the - // buffer size. Otherwise, the data is heap allocated. - class alignas(alignof(max_align_t)) TypeErasedJob final - { - public: - // The inline buffer allows the TypeErasedJob to span two cache lines. Lambdas can capture 48 - // bytes of data (6 pointers/references on a 64-bit machine) before spilling to the heap. - constexpr static size_t BufferSize = - 128 - sizeof(size_t) * 6 - sizeof(uint32_t) - sizeof(JobDescriptor) - sizeof(AZStd::atomic); - - TypeErasedJob() = default; - - template - TypeErasedJob(JobDescriptor const& desc, Lambda&& lambda) noexcept - : m_descriptor{ desc } - { - JobTypeEraser eraser; - m_invoker = eraser.ErasedInvoker(); - m_relocator = eraser.ErasedRelocator(); - m_destroyer = eraser.ErasedDestroyer(); - - // NOTE: This code is conservative in that extended alignment requirements result in a heap - // spill, even if the lambda could have occupied a portion of the inline buffer with a base - // pointer adjustment. - if constexpr (sizeof(Lambda) <= BufferSize && alignof(Lambda) <= alignof(max_align_t)) - { - TypedRelocate(AZStd::forward(lambda), m_buffer); - m_lambda = m_buffer; - } - else - { - // Lambda has spilled to the heap (or requires extended alignment) - m_lambda = reinterpret_cast(azmalloc(sizeof(Lambda), alignof(Lambda))); - TypedRelocate(AZStd::forward(lambda), m_lambda); - } - } - - TypeErasedJob(TypeErasedJob&& other) noexcept; - - TypeErasedJob& operator=(TypeErasedJob&& other) noexcept; - - ~TypeErasedJob(); - - void Link(TypeErasedJob& other); - - // Indicates if this job is a root of the graph (with no dependencies) - bool IsRoot(); - - void Init() noexcept - { - m_dependencyCount = m_inboundLinkCount; - } - - void Invoke() - { - m_invoker(m_lambda); - } - - uint8_t GetPriorityNumber() const - { - return static_cast(m_descriptor.priority); - } - - private: - friend class CompiledJobGraph; - friend class JobWorker; - - // This relocation avoids branches needed if the lambda type is unknown - template - void TypedRelocate(Lambda&& lambda, char* destination) - { - if constexpr (AZStd::is_trivially_move_constructible_v) - { - memcpy(destination, reinterpret_cast(&lambda), sizeof(Lambda)); - } - else if constexpr (AZStd::is_move_constructible_v) - { - new (destination) Lambda{ AZStd::move(lambda) }; - } - else if constexpr (AZStd::is_copy_constructible_v) - { - new (destination) Lambda{ lambda }; - } - else - { - static_assert( - AZStd::is_move_constructible_v || AZStd::is_copy_constructible_v, - "Job lambdas must be either move or copy constructible. Please verify that all captured data is move or copy " - "constructible."); - } - } - - // Small buffer optimization for lambdas. We cover our bases here by enforcing alignment on the - // class to equal the alignment of the largest scalar type available on the system (generally - // 16 bytes). - char m_buffer[BufferSize]; - AZStd::atomic m_dependencyCount; - - // This value is an offset in a buffer that stores dependency tracking information. - uint32_t m_successorOffset = 0; - uint32_t m_inboundLinkCount = 0; - uint32_t m_outboundLinkCount = 0; - - // May point to the inlined payload buffer, or heap - char* m_lambda = nullptr; - - CompiledJobGraph* m_graph = nullptr; - - JobInvoke_t m_invoker; - - // If nullptr, the lambda is trivially relocatable (via memcpy). Otherwise, it must be invoked - // when instances of this class are moved. - JobRelocate_t m_relocator; - JobDestroy_t m_destroyer; - - JobDescriptor m_descriptor; - }; - - inline void TypeErasedJob::Link(TypeErasedJob& other) - { - ++m_outboundLinkCount; - ++other.m_inboundLinkCount; - } - - inline bool TypeErasedJob::IsRoot() - { - return m_inboundLinkCount == 0; - } -} // namespace AZ::Internal diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobDescriptor.h b/Code/Framework/AzCore/AzCore/Jobs/JobDescriptor.h deleted file mode 100644 index 82a9603bd5..0000000000 --- a/Code/Framework/AzCore/AzCore/Jobs/JobDescriptor.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include - -namespace AZ -{ - // Job priorities MAY be used judiciously to fine tune runtime execution, with the understanding - // that profiling is needed to understand what the critical path per frame is. Modifying - // job priorities is an EXPERT setting that should succeed a healthy dose of measurement. - enum class JobPriority : uint8_t - { - CRITICAL = 0, - HIGH = 1, - MEDIUM = 2, // Default - LOW = 3, - PRIORITY_COUNT = 4, - }; - - // All submitted jobs are associated with a JobDescriptor which defines the priority, affinitization, - // and tracking of the job resource utilization. - // - // TODO: Define various job kinds and provide a mechanism for cpuMask computation on different systems. - struct JobDescriptor - { - // Unique job kind label (e.g. "frustum culling") - // Job names *must* be provided - const char* jobName = nullptr; - - // Associates a set of job kinds together for budget tracking (e.g. "graphics") - const char* jobGroup = nullptr; - - // EXPERTS ONLY. Jobs of higher priority are executed ahead of any lower priority jobs - // that were queued before it provided they had not yet started - JobPriority priority = JobPriority::MEDIUM; - - // EXPERTS ONLY. A bitmask that restricts jobs of this kind to run only on cores - // corresponding to a set bit. 0 is synonymous with all bits set - uint32_t cpuMask = 0; - }; -} diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h deleted file mode 100644 index 746edf00a1..0000000000 --- a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace AZ -{ - class JobGraphEvent; - class JobGraph; - - namespace Internal - { - class CompiledJobGraph final - { - public: - AZ_CLASS_ALLOCATOR(CompiledJobGraph, SystemAllocator, 0) - - CompiledJobGraph( - AZStd::vector&& jobs, - AZStd::unordered_map>& links, - size_t linkCount, - JobGraph* parent); - - AZStd::vector& Jobs() noexcept - { - return m_jobs; - } - - // Indicate that a constituent job has finished and decrement a counter to determine if the - // graph should be freed (returns the value after atomic decrement) - uint32_t Release(); - - private: - friend class ::AZ::JobGraph; - friend class JobWorker; - - AZStd::vector m_jobs; - AZStd::vector m_successors; - JobGraphEvent* m_waitEvent = nullptr; - // The pointer to the parent graph is set only if it is retained - JobGraph* m_parent = nullptr; - AZStd::atomic m_remaining; - }; - - class JobWorker; - } // namespace Internal - - class JobExecutor - { - public: - AZ_CLASS_ALLOCATOR(JobExecutor, SystemAllocator, 0); - - static JobExecutor& Instance(); - - // Passing 0 for the threadCount requests for the thread count to match the hardware concurrency - JobExecutor(uint32_t threadCount = 0); - ~JobExecutor(); - - void Submit(Internal::CompiledJobGraph& graph); - - void Submit(Internal::TypeErasedJob& job); - - // Busy wait until jobs are cleared from the executor (note, does not prevent future jobs from being submitted) - void Drain(); - private: - friend class Internal::JobWorker; - - Internal::JobWorker* m_workers; - uint32_t m_threadCount = 0; - AZStd::atomic m_lastSubmission; - AZStd::atomic m_remaining; - }; -} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp deleted file mode 100644 index 6b34f1273a..0000000000 --- a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include - -#include - -namespace AZ -{ - using Internal::CompiledJobGraph; - - void JobToken::PrecedesInternal(JobToken& comesAfter) - { - AZ_Assert(!m_parent.m_submitted, "Cannot mutate a JobGraph that was previously submitted."); - - // Increment inbound/outbound edge counts - m_parent.m_jobs[m_index].Link(m_parent.m_jobs[comesAfter.m_index]); - - m_parent.m_links[m_index].emplace_back(comesAfter.m_index); - - ++m_parent.m_linkCount; - } - - JobGraph::~JobGraph() - { - if (m_retained && m_compiledJobGraph) - { - // This job graph has already finished and we are potentially responsible for its destruction - if (m_compiledJobGraph->Release() == 0) - { - azdestroy(m_compiledJobGraph); - } - } - } - - void JobGraph::Reset() - { - AZ_Assert(!m_submitted, "Cannot reset a job graph while it is in flight"); - if (m_compiledJobGraph) - { - azdestroy(m_compiledJobGraph); - m_compiledJobGraph = nullptr; - } - m_jobs.clear(); - m_links.clear(); - m_linkCount = 0; - } - - void JobGraph::Submit(JobGraphEvent* waitEvent) - { - SubmitOnExecutor(JobExecutor::Instance(), waitEvent); - } - - void JobGraph::SubmitOnExecutor(JobExecutor& executor, JobGraphEvent* waitEvent) - { - if (!m_compiledJobGraph) - { - m_compiledJobGraph = aznew CompiledJobGraph(AZStd::move(m_jobs), m_links, m_linkCount, m_retained ? this : nullptr); - } - - m_compiledJobGraph->m_waitEvent = waitEvent; - m_compiledJobGraph->m_remaining = m_compiledJobGraph->m_jobs.size() + (m_retained ? 1 : 0); - for (size_t i = 0; i != m_compiledJobGraph->m_jobs.size(); ++i) - { - m_compiledJobGraph->m_jobs[i].Init(); - } - - executor.Submit(*m_compiledJobGraph); - - if (m_retained) - { - m_submitted = true; - } - else - { - m_compiledJobGraph = nullptr; - Reset(); - } - } -} diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h deleted file mode 100644 index 872a9a4e5c..0000000000 --- a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -// NOTE: If adding additional header/symbol dependencies, consider if such additions are better -// suited in the private CompiledJobGraph implementation instead to keep this header lean. -#include -#include -#include -#include -#include -#include - -namespace AZ -{ - namespace Internal - { - class CompiledJobGraph; - } - class JobExecutor; - class JobGraph; - - // A JobToken is returned each time a Job is added to the JobGraph. JobTokens are used to - // express dependencies between jobs within the graph. - class JobToken final - { - public: - // Indicate that this job must finish before the job token(s) passed as the argument - template - void Precedes(JT&... tokens); - - // Indicate that this job must finish after the job token(s) passed as the argument - template - void Succeeds(JT&... tokens); - - private: - friend class JobGraph; - - void PrecedesInternal(JobToken& comesAfter); - - // Only the JobGraph should be creating JobToken - JobToken(JobGraph& parent, size_t index); - - JobGraph& m_parent; - size_t m_index; - }; - - // A JobGraphEvent may be used to block until a job graph has finished executing. Usage - // is NOT recommended for the majority of tasks (prefer to simply containing expanding/contracting - // the graph without synchronization over the course of the frame). However, the event - // is useful for the edges of the computation graph. - // - // You are responsible for ensuring the event object lifetime exceeds the job graph lifetime. - // - // After the JobGraphEvent is signaled, you are allowed to reuse the same JobGraphEvent - // for a future submission. - class JobGraphEvent - { - public: - bool IsSignaled(); - void Wait(); - - private: - friend class ::AZ::Internal::CompiledJobGraph; - friend class JobGraph; - void Signal(); - - AZStd::binary_semaphore m_semaphore; - }; - - // The JobGraph encapsulates a set of jobs and their interdependencies. After adding - // jobs, and marking dependencies as necessary, the entire graph is submitted via - // the JobGraph::Submit method. - // - // The JobGraph MAY be retained across multiple frames and resubmitted, provided the - // user provides some guarantees (see comments associated with JobGraph::Retain). - class JobGraph final - { - public: - ~JobGraph(); - - // Reset the state of the job graph to begin recording jobs and edges again - // NOTE: Graph must be in a "settled" state (cannot be in-flight) - void Reset(); - - // Add a job to the graph, retrieiving a token that can be used to express dependencies - // between jobs. The first argument specifies the JobKind, used for tracking the job. - // NOTE: This operation is invalid if the graph is in-flight - template - JobToken AddJob(JobDescriptor const& descriptor, Lambda&& lambda); - - template - AZStd::array AddJobs(JobDescriptor const& descriptor, Lambdas&&... lambdas); - - // By default, you are responsible for retaining the JobGraph, indicating you promise that - // this JobGraph will live as long as it takes for all constituent jobs to complete. - // Once retained, this job graph can be resubmitted after completion without any - // modifications. JobTokens that were created as a result of adding jobs used to - // mark dependencies DO NOT need to outlive the job graph. - // - // Invoking Detach PRIOR to submission indicates you wish the jobs associated with this - // JobGraph to deallocate upon completion. After invoking Detach, you may let this JobGraph - // go out of scope or deallocate after submission. - // - // NOTE: The JobGraph has no concept of resources used by design. Resubmission - // of the job graph is expected to rely on either indirection, or safe overwriting - // of previously used memory to supply new data (this can even be done as the first - // job in the graph). - // NOTE: This operation is invalid if the graph is in-flight - void Detach(); - - // Invoke the job graph, asserting if there are dependency violations. Note that - // submitting the same graph multiple times to process simultaneously is VALID - // behavior. This is, for example, a mechanism that allows a job graph to loop - // in perpetuity (in fact, the entire frame could be modeled as a single job graph, - // where the final job resubmits the job graph again). - // - // This API is not designed to protect against memory safety violations (nothing - // can prevent a user from incorrectly aliasing memory unsafely even without repeated - // submission). To catch memory safety violations, it is ENCOURAGED that you access - // data through JobResource handles. - void Submit(JobGraphEvent* waitEvent = nullptr); - - // Same as submit but run on a different executor than the default system executor - void SubmitOnExecutor(JobExecutor& executor, JobGraphEvent* waitEvent = nullptr); - - private: - friend class JobToken; - friend class Internal::CompiledJobGraph; - - Internal::CompiledJobGraph* m_compiledJobGraph = nullptr; - - AZStd::vector m_jobs; - - // Job index |-> Dependent job indices - AZStd::unordered_map> m_links; - - uint32_t m_linkCount = 0; - bool m_retained = true; - AZStd::atomic m_submitted = false; - }; -} // namespace AZ - -#include diff --git a/Code/Framework/AzCore/AzCore/Task/Internal/Task.cpp b/Code/Framework/AzCore/AzCore/Task/Internal/Task.cpp new file mode 100644 index 0000000000..b0e5da8fc0 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Task/Internal/Task.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace AZ::Internal +{ + Task::Task(Task&& other) noexcept + { + if (!other.m_relocator) + { + // The type-erased lambda is trivially relocatable OR, the lambda is heap allocated + memcpy(this, &other, sizeof(Task)); + + // Prevent deletion in the event the lambda had spilled to the heap + other.m_destroyer = nullptr; + return; + } + + m_invoker = other.m_invoker; + m_relocator = other.m_relocator; + m_destroyer = other.m_destroyer; + + // We now own the lambda, so clear the moved-from task's destroyer + other.m_destroyer = nullptr; + + m_relocator(m_lambda, other.m_lambda); + } + + Task& Task::operator=(Task&& other) noexcept + { + if (this == &other) + { + return *this; + } + + this->~Task(); + + new (this) Task{ AZStd::move(other) }; + + return *this; + } + + Task::~Task() + { + if (m_destroyer) + { + // The presence of m_destroyer indicates that the lambda is not trivially destructible + m_destroyer(m_lambda); + } + } + +} // namespace AZ::Internal diff --git a/Code/Framework/AzCore/AzCore/Task/Internal/Task.h b/Code/Framework/AzCore/AzCore/Task/Internal/Task.h new file mode 100644 index 0000000000..e5d3736f2a --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Task/Internal/Task.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace AZ::Internal +{ + using TaskInvoke_t = void (*)(void* lambda); + using TaskRelocate_t = void (*)(void* dst, void* src); + using TaskDestroy_t = void (*)(void* obj); + + class CompiledTaskGraph; + + // Lambdas are opaque types and we cannot extract any member function pointers. In order to store lambdas in a + // type erased fashion, we instead use a single function call indirection, invoking the lambda function in a + // static class function which has a stable address in memory. The Erased* methods return addresses to the + // indirect callers of the lambda copy/move assignment operators, call operator, and destructor. + // + // For lambdas that are trivially relocatable, both the returned move and copy assignment function pointers + // will be nullptr. + // + // Lambdas that are trivially destructible will result in a nullptr returned TaskDestroy_t pointer. + // + // The class will check that the lambda is copy assignable or movable. + template + class TaskTypeEraser final + { + public: + constexpr TaskInvoke_t ErasedInvoker() + { + return reinterpret_cast(Invoker); + } + + constexpr TaskRelocate_t ErasedRelocator() + { + if constexpr (AZStd::is_trivially_move_constructible_v) + { + return nullptr; + } + else if constexpr (AZStd::is_move_constructible_v) + { + return reinterpret_cast(Mover); + } + else if constexpr (AZStd::is_copy_constructible_v) + { + return reinterpret_cast(Copier); + } + else + { + static_assert( + AZStd::is_move_constructible_v || AZStd::is_copy_constructible_v, + "Task lambdas must be either move or copy constructible. Please verify that all captured data is move or copy " + "constructible."); + } + } + + constexpr TaskDestroy_t ErasedDestroyer() + { + if constexpr (AZStd::is_trivially_destructible_v) + { + return nullptr; + } + else + { + return reinterpret_cast(Destroyer); + } + } + + private: + constexpr static void Invoker(Lambda* lambda) + { + lambda->operator()(); + } + + constexpr static void Mover(Lambda* dst, Lambda* src) + { + new (dst) Lambda{ AZStd::move(*src) }; + } + + constexpr static void Copier(Lambda* dst, Lambda* src) + { + new (dst) Lambda{ *src }; + } + + constexpr static void Destroyer(Lambda* lambda) + { + lambda->~Lambda(); + } + }; + + // The Task encapsulates member function pointers to store in a homogeneously-typed container + // The function signature of all lambdas encoded in a Task is void(*)(). The lambdas can capture + // data, in which case the data is inlined in this structure. Attempting to capture more data + // will result in a compile failure, so use indirection and capture a pointer/reference to your + // data if you run into this. + class alignas(alignof(max_align_t)) Task final + { + public: + AZ_CLASS_ALLOCATOR(Task, ThreadPoolAllocator, 0); + + // The inline buffer allows the Task to span two cache lines. Lambdas can capture 56 + // bytes of data (7 pointers/references on a 64-bit machine). + constexpr static size_t BufferSize = + AZ_TRAIT_TASK_BYTE_SIZE - sizeof(size_t) * 5 - sizeof(uint32_t) - sizeof(TaskDescriptor) - sizeof(AZStd::atomic); + + Task() = default; + + // Prevent binding lvalue references to lambdas + // If you are encountering a compiler error here, please either move the lambda into the AddJob function with AZStd::move + // or simply define the lambda directly as a parameter of AddJob + template + Task(TaskDescriptor const& desc, Lambda& lambda) = delete; + + template + Task(TaskDescriptor const& desc, Lambda&& lambda) noexcept; + + Task(Task&& other) noexcept; + + Task& operator=(Task&& other) noexcept; + + ~Task(); + + void Link(Task& other); + + // Indicates if this task is a root of the graph (with no dependencies) + bool IsRoot() const noexcept; + + // Prepare for dispatch (reset the dependency counter to the number of inbound edges) + void Init() noexcept; + + // Invoke the embedded lambda function + void Invoke(); + + uint8_t GetPriorityNumber() const noexcept; + + private: + friend class CompiledTaskGraph; + friend class TaskWorker; + + // This relocation avoids branches needed if the lambda type is unknown + template + void TypedRelocate(Lambda&& lambda, char* destination); + + // Small buffer optimization for lambdas. We cover our bases here by enforcing alignment on the + // class to equal the alignment of the largest scalar type available on the system (generally + // 16 bytes). + char m_lambda[BufferSize]; + AZStd::atomic m_dependencyCount; + + // This value is an offset in a buffer that stores dependency tracking information. + uint32_t m_successorOffset = 0; + uint32_t m_inboundLinkCount = 0; + uint32_t m_outboundLinkCount = 0; + + CompiledTaskGraph* m_graph = nullptr; + + TaskInvoke_t m_invoker; + + // If nullptr, the lambda is trivially relocatable (via memcpy). Otherwise, it must be invoked + // when instances of this class are moved. + TaskRelocate_t m_relocator; + TaskDestroy_t m_destroyer; + + TaskDescriptor m_descriptor; + }; +} // namespace AZ::Internal + +#include diff --git a/Code/Framework/AzCore/AzCore/Task/Internal/Task.inl b/Code/Framework/AzCore/AzCore/Task/Internal/Task.inl new file mode 100644 index 0000000000..83cd311f56 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Task/Internal/Task.inl @@ -0,0 +1,84 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +namespace AZ::Internal +{ + template + Task::Task(TaskDescriptor const& desc, Lambda&& lambda) noexcept + : m_descriptor{ desc } + { + static_assert( + sizeof(Lambda) <= BufferSize, + "Task lambda has too much captured data, please capture no" + "more than 56 bytes of data (likely by capturing a single reference/pointer to a container of data)"); + static_assert( + alignof(Lambda) <= alignof(max_align_t), + "Task lambda has extended alignment which isn't supported." + "Please capture a reference/pointer to the data requiring an extended alignment instead"); + + TaskTypeEraser eraser; + m_invoker = eraser.ErasedInvoker(); + m_relocator = eraser.ErasedRelocator(); + m_destroyer = eraser.ErasedDestroyer(); + + // NOTE: This code is conservative in that extended alignment requirements result in a heap + // spill, even if the lambda could have occupied a portion of the inline buffer with a base + // pointer adjustment. + TypedRelocate(AZStd::forward(lambda), m_lambda); + } + + template + void Task::TypedRelocate(Lambda&& lambda, char* destination) + { + if constexpr (AZStd::is_trivially_move_constructible_v) + { + memcpy(destination, reinterpret_cast(&lambda), sizeof(Lambda)); + } + else if constexpr (AZStd::is_move_constructible_v) + { + new (destination) Lambda{ AZStd::move(lambda) }; + } + else if constexpr (AZStd::is_copy_constructible_v) + { + new (destination) Lambda{ lambda }; + } + else + { + static_assert( + AZStd::is_move_constructible_v || AZStd::is_copy_constructible_v, + "Task lambdas must be either move or copy constructible. Please verify that all captured data is move or copy " + "constructible."); + } + } + + inline void Task::Init() noexcept + { + m_dependencyCount = m_inboundLinkCount; + } + + inline void Task::Invoke() + { + m_invoker(m_lambda); + } + + inline uint8_t Task::GetPriorityNumber() const noexcept + { + return static_cast(m_descriptor.priority); + } + + inline void Task::Link(Task& other) + { + ++m_outboundLinkCount; + ++other.m_inboundLinkCount; + } + + inline bool Task::IsRoot() const noexcept + { + return m_inboundLinkCount == 0; + } +} // namespace AZ::Internal diff --git a/Code/Framework/AzCore/AzCore/Task/Internal/TaskConfig.h b/Code/Framework/AzCore/AzCore/Task/Internal/TaskConfig.h new file mode 100644 index 0000000000..d5550b40d4 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Task/Internal/TaskConfig.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include + +#if !defined(AZ_TRAIT_TASK_BYTE_SIZE) +#define AZ_TRAIT_TASK_BYTE_SIZE 128 +#endif diff --git a/Code/Framework/AzCore/AzCore/Task/TaskDescriptor.h b/Code/Framework/AzCore/AzCore/Task/TaskDescriptor.h new file mode 100644 index 0000000000..8342fed925 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Task/TaskDescriptor.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AZ +{ + // Task priorities MAY be used judiciously to fine tune runtime execution, with the understanding + // that profiling is needed to understand what the critical path per frame is. Modifying + // task priorities is an EXPERT setting that should succeed a healthy dose of measurement. + enum class TaskPriority : uint8_t + { + CRITICAL = 0, + HIGH = 1, + MEDIUM = 2, // Default + LOW = 3, + PRIORITY_COUNT = 4, + }; + + // All submitted tasks are associated with a TaskDescriptor which defines the priority, affinitization, + // and tracking of the task resource utilization. + // + // TODO: Define various task kinds and provide a mechanism for cpuMask computation on different systems. + struct TaskDescriptor + { + // Unique task kind label (e.g. "frustum culling") + // Task names *must* be provided + const char* taskName = nullptr; + + // Associates a set of task kinds together for budget tracking (e.g. "graphics") + const char* taskGroup = nullptr; + + // EXPERTS ONLY. Tasks of higher priority are executed ahead of any lower priority tasks + // that were queued before it provided they had not yet started + TaskPriority priority = TaskPriority::MEDIUM; + + // EXPERTS ONLY. A bitmask that restricts tasks of this kind to run only on cores + // corresponding to a set bit. 0 is synonymous with all bits set + uint32_t cpuMask = 0; + }; +} diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp similarity index 62% rename from Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp rename to Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp index 83823515c1..c69763f289 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp +++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp @@ -6,8 +6,8 @@ * */ -#include -#include +#include +#include #include #include @@ -17,46 +17,45 @@ #include #include #include +#include #include namespace AZ { - constexpr static size_t PRIORITY_COUNT = static_cast(JobPriority::PRIORITY_COUNT); - namespace Internal { - CompiledJobGraph::CompiledJobGraph( - AZStd::vector&& jobs, + CompiledTaskGraph::CompiledTaskGraph( + AZStd::vector&& tasks, AZStd::unordered_map>& links, size_t linkCount, - JobGraph* parent) + TaskGraph* parent) : m_parent{ parent } { - m_jobs = AZStd::move(jobs); + m_tasks = AZStd::move(tasks); m_successors.resize(linkCount); - TypeErasedJob** cursor = m_successors.data(); + Task** cursor = m_successors.data(); - for (size_t i = 0; i != m_jobs.size(); ++i) + for (size_t i = 0; i != m_tasks.size(); ++i) { - TypeErasedJob& job = m_jobs[i]; - job.m_graph = this; - job.m_successorOffset = cursor - m_successors.data(); - cursor += job.m_outboundLinkCount; + Task& task = m_tasks[i]; + task.m_graph = this; + task.m_successorOffset = cursor - m_successors.data(); + cursor += task.m_outboundLinkCount; - AZ_Assert(job.m_outboundLinkCount == links[i].size(), "Job outbound link information mismatch"); + AZ_Assert(task.m_outboundLinkCount == links[i].size(), "Task outbound link information mismatch"); - for (uint32_t j = 0; j != job.m_outboundLinkCount; ++j) + for (uint32_t j = 0; j != task.m_outboundLinkCount; ++j) { - m_successors[static_cast(job.m_successorOffset) + j] = &m_jobs[links[i][j]]; + m_successors[static_cast(task.m_successorOffset) + j] = &m_tasks[links[i][j]]; } } // TODO: Check for dependency cycles } - uint32_t CompiledJobGraph::Release() + uint32_t CompiledTaskGraph::Release() { uint32_t remaining = --m_remaining; @@ -94,35 +93,35 @@ namespace AZ AZStd::atomic reserve; }; - // The Job Queue is a lock free 4-priority queue. Its basic operation is as follows: + // The Task Queue is a lock free 4-priority queue. Its basic operation is as follows: // Each priority level is associated with a different queue, corresponding to the maximum size of a uint16_t. // Each queue is implemented as a ring buffer, and a 64 bit atomic maintains the following state per queue: // - offset to the "head" of the ring, from where we acquire elements // - offset to the "tail" of the ring, which tracks where new elements should be enqueued // - offset to a tail reservation index, which is used to reserve a slot to enqueue elements - class JobQueue final + class TaskQueue final { public: - // Preallocating upfront allows us to reserve slots to insert jobs without locks. - // Each thread allocated by the job manager consumes ~2 MB. + // Preallocating upfront allows us to reserve slots to insert tasks without locks. + // Each thread allocated by the task manager consumes ~2 MB. constexpr static uint16_t MaxQueueSize = 0xffff; - constexpr static uint8_t PriorityLevelCount = static_cast(JobPriority::PRIORITY_COUNT); + constexpr static uint8_t PriorityLevelCount = static_cast(TaskPriority::PRIORITY_COUNT); - JobQueue() = default; - JobQueue(const JobQueue&) = delete; - JobQueue& operator=(const JobQueue&) = delete; + TaskQueue() = default; + TaskQueue(const TaskQueue&) = delete; + TaskQueue& operator=(const TaskQueue&) = delete; - void Enqueue(TypeErasedJob* job); - TypeErasedJob* TryDequeue(); + void Enqueue(Task* task); + Task* TryDequeue(); private: QueueStatus m_status[PriorityLevelCount] = {}; - TypeErasedJob* m_queues[PriorityLevelCount][MaxQueueSize] = {}; + Task* m_queues[PriorityLevelCount][MaxQueueSize] = {}; }; - void JobQueue::Enqueue(TypeErasedJob* job) + void TaskQueue::Enqueue(Task* task) { - uint8_t priority = job->GetPriorityNumber(); + uint8_t priority = task->GetPriorityNumber(); QueueStatus& status = m_status[priority]; AZStd::exponential_backoff backoff; @@ -131,18 +130,18 @@ namespace AZ uint16_t reserve = status.reserve.load(); uint16_t head = status.head.load(); - // Enqueuing is done in two phases because we cannot atomically write the job to the slot we reserve + // Enqueuing is done in two phases because we cannot atomically write the task to the slot we reserve // and simulataneously publish the fact that the slot is now available. if (reserve != head - 1) { // Try to reserve a slot if (status.reserve.compare_exchange_weak(reserve, reserve + 1)) { - m_queues[priority][reserve] = job; + m_queues[priority][reserve] = task; uint16_t expectedReserve = reserve; - // Increment the tail to advertise the new job + // Increment the tail to advertise the new task while (!status.tail.compare_exchange_weak(expectedReserve, reserve + 1)) { expectedReserve = reserve; @@ -160,7 +159,7 @@ namespace AZ } } - TypeErasedJob* JobQueue::TryDequeue() + Task* TaskQueue::TryDequeue() { for (size_t priority = 0; priority != PriorityLevelCount; ++priority) { @@ -176,10 +175,10 @@ namespace AZ } else { - TypeErasedJob* job = m_queues[priority][status.head]; + Task* task = m_queues[priority][status.head]; if (status.head.compare_exchange_weak(head, head + 1)) { - return job; + return task; } } } @@ -188,14 +187,14 @@ namespace AZ return nullptr; } - class JobWorker + class TaskWorker { public: - void Spawn(::AZ::JobExecutor& executor, size_t id, AZStd::semaphore& initSemaphore, bool affinitize) + void Spawn(::AZ::TaskExecutor& executor, size_t id, AZStd::semaphore& initSemaphore, bool affinitize) { m_executor = &executor; - AZStd::string threadName = AZStd::string::format("JobWorker %zu", id); + AZStd::string threadName = AZStd::string::format("TaskWorker %zu", id); AZStd::thread_desc desc = {}; desc.m_name = threadName.c_str(); if (affinitize) @@ -219,13 +218,13 @@ namespace AZ m_thread.join(); } - void Enqueue(TypeErasedJob* job) + void Enqueue(Task* task) { - m_queue.Enqueue(job); + m_queue.Enqueue(task); if (!m_busy.exchange(true)) { - // The worker was idle prior to enqueueing the job, release the semaphore + // The worker was idle prior to enqueueing the task, release the semaphore m_semaphore.release(); } } @@ -245,24 +244,26 @@ namespace AZ m_busy = true; - TypeErasedJob* job = m_queue.TryDequeue(); - while (job) + Task* task = m_queue.TryDequeue(); + while (task) { - job->Invoke(); - // Decrement counts for all job successors - for (size_t j = 0; j != job->m_outboundLinkCount; ++j) + task->Invoke(); + // Decrement counts for all task successors + for (size_t j = 0; j != task->m_outboundLinkCount; ++j) { - TypeErasedJob* successor = job->m_graph->m_successors[job->m_successorOffset + j]; + Task* successor = task->m_graph->m_successors[task->m_successorOffset + j]; if (--successor->m_dependencyCount == 0) { m_executor->Submit(*successor); } } - job->m_graph->Release(); - --m_executor->m_remaining; + if (task->m_graph->Release() == (task->m_graph->m_parent ? 1 : 0)) + { + m_executor->ReleaseGraph(); + } - job = m_queue.TryDequeue(); + task = m_queue.TryDequeue(); } } } @@ -272,24 +273,38 @@ namespace AZ AZStd::atomic m_busy; AZStd::binary_semaphore m_semaphore; - ::AZ::JobExecutor* m_executor; - JobQueue m_queue; + ::AZ::TaskExecutor* m_executor; + TaskQueue m_queue; }; } // namespace Internal - JobExecutor& JobExecutor::Instance() + static EnvironmentVariable s_executor; + constexpr static const char* s_executorName = "GlobalTaskExecutor"; + TaskExecutor& TaskExecutor::Instance() { - // TODO: Create the default executor as part of a component (as in JobManagerComponent) - static JobExecutor executor; - return executor; + if (!s_executor) + { + s_executor = AZ::Environment::FindVariable(s_executorName); + } + + return **s_executor; } - JobExecutor::JobExecutor(uint32_t threadCount) + // TODO: Create the default executor as part of a component (as in TaskManagerComponent) + void TaskExecutor::SetInstance(TaskExecutor* executor) + { + AZ_Assert(!s_executor, "Attempting to set the global task executor more than once"); + + s_executor = AZ::Environment::CreateVariable("GlobalTaskExecutor"); + s_executor.Set(executor); + } + + TaskExecutor::TaskExecutor(uint32_t threadCount) { // TODO: Configure thread count + affinity based on configuration m_threadCount = threadCount == 0 ? AZStd::thread::hardware_concurrency() : threadCount; - m_workers = reinterpret_cast(azmalloc(m_threadCount * sizeof(Internal::JobWorker))); + m_workers = reinterpret_cast(azmalloc(m_threadCount * sizeof(Internal::TaskWorker))); bool affinitize = m_threadCount == AZStd::thread::hardware_concurrency(); @@ -297,7 +312,7 @@ namespace AZ for (size_t i = 0; i != m_threadCount; ++i) { - new (m_workers + i) Internal::JobWorker{}; + new (m_workers + i) Internal::TaskWorker{}; m_workers[i].Spawn(*this, i, initSemaphore, affinitize); } @@ -307,43 +322,56 @@ namespace AZ } } - JobExecutor::~JobExecutor() + TaskExecutor::~TaskExecutor() { for (size_t i = 0; i != m_threadCount; ++i) { m_workers[i].Join(); - m_workers[i].~JobWorker(); + m_workers[i].~TaskWorker(); } azfree(m_workers); } - void JobExecutor::Submit(Internal::CompiledJobGraph& graph) + void TaskExecutor::Submit(Internal::CompiledTaskGraph& graph) { - // Submit all jobs that have no inbound edges - for (Internal::TypeErasedJob& job : graph.Jobs()) + ++m_graphsRemaining; + // Submit all tasks that have no inbound edges + for (Internal::Task& task : graph.Tasks()) { - if (job.IsRoot()) + if (task.IsRoot()) { - Submit(job); + Submit(task); } } } - void JobExecutor::Submit(Internal::TypeErasedJob& job) + void TaskExecutor::Submit(Internal::Task& task) { // TODO: Something more sophisticated is likely needed here. // First, we are completely ignoring affinity. // Second, some heuristics on core availability will help distribute work more effectively - ++m_remaining; - m_workers[++m_lastSubmission % m_threadCount].Enqueue(&job); + m_workers[++m_lastSubmission % m_threadCount].Enqueue(&task); } - void JobExecutor::Drain() + void TaskExecutor::Drain() { - while (m_remaining > 0) + m_isDraining = true; + if (m_graphsRemaining == 0) { - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds{ 100 }); + return; + } + m_drainSemaphore.acquire(); + } + + void TaskExecutor::ReleaseGraph() + { + uint64_t graphsRemaining = --m_graphsRemaining; + + if (graphsRemaining == 0 && m_isDraining) + { + m_drainSemaphore.release(); + m_isDraining = false; } } } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h new file mode 100644 index 0000000000..ad4c4b81c2 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + class TaskGraphEvent; + class TaskGraph; + + namespace Internal + { + class CompiledTaskGraph final + { + public: + AZ_CLASS_ALLOCATOR(CompiledTaskGraph, SystemAllocator, 0) + + CompiledTaskGraph( + AZStd::vector&& tasks, + AZStd::unordered_map>& links, + size_t linkCount, + TaskGraph* parent); + + AZStd::vector& Tasks() noexcept + { + return m_tasks; + } + + // Indicate that a constituent task has finished and decrement a counter to determine if the + // graph should be freed (returns the value after atomic decrement) + uint32_t Release(); + + private: + friend class ::AZ::TaskGraph; + friend class TaskWorker; + + AZStd::vector m_tasks; + AZStd::vector m_successors; + TaskGraphEvent* m_waitEvent = nullptr; + // The pointer to the parent graph is set only if it is retained + TaskGraph* m_parent = nullptr; + AZStd::atomic m_remaining; + }; + + class TaskWorker; + } // namespace Internal + + class TaskExecutor final + { + public: + AZ_CLASS_ALLOCATOR(TaskExecutor, SystemAllocator, 0); + + static TaskExecutor& Instance(); + + // Invoked by a system component on program launch + static void SetInstance(TaskExecutor* executor); + + // Passing 0 for the threadCount requests for the thread count to match the hardware concurrency + explicit TaskExecutor(uint32_t threadCount = 0); + ~TaskExecutor(); + + void Submit(Internal::CompiledTaskGraph& graph); + + void Submit(Internal::Task& task); + + // Wait until tasks are cleared from the executor (note, does not prevent future tasks from being submitted) + // If this is used, it's expected to be used between frames to shutdown the engine + void Drain(); + private: + friend class Internal::TaskWorker; + + void ReleaseGraph(); + + Internal::TaskWorker* m_workers; + uint32_t m_threadCount = 0; + AZStd::atomic m_lastSubmission; + AZStd::atomic m_graphsRemaining; + AZStd::atomic m_isDraining; + AZStd::binary_semaphore m_drainSemaphore; + }; +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp b/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp new file mode 100644 index 0000000000..86e4f846d5 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include + +namespace AZ +{ + using Internal::CompiledTaskGraph; + + void TaskToken::PrecedesInternal(TaskToken& comesAfter) + { + AZ_Assert(!m_parent.m_submitted, "Cannot mutate a TaskGraph that was previously submitted."); + + // Increment inbound/outbound edge counts + m_parent.m_tasks[m_index].Link(m_parent.m_tasks[comesAfter.m_index]); + + m_parent.m_links[m_index].emplace_back(comesAfter.m_index); + + ++m_parent.m_linkCount; + } + + TaskGraph::~TaskGraph() + { + if (m_retained && m_compiledTaskGraph) + { + // This job graph has already finished and we are potentially responsible for its destruction + if (m_compiledTaskGraph->Release() == 0) + { + azdestroy(m_compiledTaskGraph); + } + } + } + + void TaskGraph::Reset() + { + AZ_Assert(!m_submitted, "Cannot reset a job graph while it is in flight"); + if (m_compiledTaskGraph) + { + azdestroy(m_compiledTaskGraph); + m_compiledTaskGraph = nullptr; + } + m_tasks.clear(); + m_links.clear(); + m_linkCount = 0; + } + + void TaskGraph::Submit(TaskGraphEvent* waitEvent) + { + SubmitOnExecutor(TaskExecutor::Instance(), waitEvent); + } + + void TaskGraph::SubmitOnExecutor(TaskExecutor& executor, TaskGraphEvent* waitEvent) + { + if (!m_compiledTaskGraph) + { + m_compiledTaskGraph = aznew CompiledTaskGraph(AZStd::move(m_tasks), m_links, m_linkCount, m_retained ? this : nullptr); + } + + m_compiledTaskGraph->m_waitEvent = waitEvent; + m_compiledTaskGraph->m_remaining = m_compiledTaskGraph->m_tasks.size() + (m_retained ? 1 : 0); + for (size_t i = 0; i != m_compiledTaskGraph->m_tasks.size(); ++i) + { + m_compiledTaskGraph->m_tasks[i].Init(); + } + + executor.Submit(*m_compiledTaskGraph); + + if (m_retained) + { + m_submitted = true; + } + else + { + m_compiledTaskGraph = nullptr; + Reset(); + } + } +} diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.h b/Code/Framework/AzCore/AzCore/Task/TaskGraph.h new file mode 100644 index 0000000000..d133593508 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +// NOTE: If adding additional header/symbol dependencies, consider if such additions are better +// suited in the private CompiledTaskGraph implementation instead to keep this header lean. +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Internal + { + class CompiledTaskGraph; + } + class TaskExecutor; + class TaskGraph; + + // A TaskToken is returned each time a Task is added to the TaskGraph. TaskTokens are used to + // express dependencies between tasks within the graph, and have no purpose after the graph + // is submitted (simply let them go out of scope) + class TaskToken final + { + public: + // Indicate that this task must finish before the task token(s) passed as the argument + template + void Precedes(JT&... tokens); + + // Indicate that this task must finish after the task token(s) passed as the argument + template + void Follows(JT&... tokens); + + private: + friend class TaskGraph; + + void PrecedesInternal(TaskToken& comesAfter); + + // Only the TaskGraph should be creating TaskToken + TaskToken(TaskGraph& parent, size_t index); + + TaskGraph& m_parent; + size_t m_index; + }; + + // A TaskGraphEvent may be used to block until a task graph has finished executing. Usage + // is NOT recommended for the majority of tasks (prefer to simply containing expanding/contracting + // the graph without synchronization over the course of the frame). However, the event + // is useful for the edges of the computation graph. + // + // You are responsible for ensuring the event object lifetime exceeds the task graph lifetime. + // + // After the TaskGraphEvent is signaled, you are allowed to reuse the same TaskGraphEvent + // for a future submission. + class TaskGraphEvent + { + public: + bool IsSignaled(); + void Wait(); + + private: + friend class ::AZ::Internal::CompiledTaskGraph; + friend class TaskGraph; + void Signal(); + + AZStd::binary_semaphore m_semaphore; + }; + + // The TaskGraph encapsulates a set of tasks and their interdependencies. After adding + // tasks, and marking dependencies as necessary, the entire graph is submitted via + // the TaskGraph::Submit method. + // + // The TaskGraph MAY be retained across multiple frames and resubmitted, provided the + // user provides some guarantees (see comments associated with TaskGraph::Retain). + class TaskGraph final + { + public: + ~TaskGraph(); + + // Reset the state of the task graph to begin recording tasks and edges again + // NOTE: Graph must be in a "settled" state (cannot be in-flight) + void Reset(); + + // Add a task to the graph, retrieiving a token that can be used to express dependencies + // between tasks. The first argument specifies the TaskKind, used for tracking the task. + // NOTE: This operation is invalid if the graph is in-flight + template + TaskToken AddTask(TaskDescriptor const& descriptor, Lambda&& lambda); + + template + AZStd::array AddTasks(TaskDescriptor const& descriptor, Lambdas&&... lambdas); + + // By default, you are responsible for retaining the TaskGraph, indicating you promise that + // this TaskGraph will live as long as it takes for all constituent tasks to complete. + // Once retained, this task graph can be resubmitted after completion without any + // modifications. TaskTokens that were created as a result of adding tasks used to + // mark dependencies DO NOT need to outlive the task graph. + // + // Invoking Detach PRIOR to submission indicates you wish the tasks associated with this + // TaskGraph to deallocate upon completion. After invoking Detach, you may let this TaskGraph + // go out of scope or deallocate after submission. + // + // NOTE: The TaskGraph has no concept of resources used by design. Resubmission + // of the task graph is expected to rely on either indirection, or safe overwriting + // of previously used memory to supply new data (this can even be done as the first + // task in the graph). + // NOTE: This operation is invalid if the graph is in-flight + void Detach(); + + // Invoke the task graph, asserting if there are dependency violations. Note that + // submitting the same graph multiple times to process simultaneously is VALID + // behavior. This is, for example, a mechanism that allows a task graph to loop + // in perpetuity (in fact, the entire frame could be modeled as a single task graph, + // where the final task resubmits the task graph again). + // + // This API is not designed to protect against memory safety violations (nothing + // can prevent a user from incorrectly aliasing memory unsafely even without repeated + // submission). To catch memory safety violations, it is ENCOURAGED that you access + // data through TaskResource handles. + void Submit(TaskGraphEvent* waitEvent = nullptr); + + // Same as submit but run on a different executor than the default system executor + void SubmitOnExecutor(TaskExecutor& executor, TaskGraphEvent* waitEvent = nullptr); + + private: + friend class TaskToken; + friend class Internal::CompiledTaskGraph; + + Internal::CompiledTaskGraph* m_compiledTaskGraph = nullptr; + + AZStd::vector m_tasks; + + // Task index |-> Dependent task indices + AZStd::unordered_map> m_links; + + uint32_t m_linkCount = 0; + bool m_retained = true; + AZStd::atomic m_submitted = false; + }; +} // namespace AZ + +#include diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl b/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl similarity index 50% rename from Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl rename to Code/Framework/AzCore/AzCore/Task/TaskGraph.inl index ad1fb3505c..1971ddbbca 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl @@ -10,56 +10,56 @@ namespace AZ { - inline JobToken::JobToken(JobGraph& parent, size_t index) + inline TaskToken::TaskToken(TaskGraph& parent, size_t index) : m_parent{ parent } , m_index{ index } { } template - inline void JobToken::Precedes(JT&... tokens) + void TaskToken::Precedes(JT&... tokens) { (PrecedesInternal(tokens), ...); } template - inline void JobToken::Succeeds(JT&... tokens) + void TaskToken::Follows(JT&... tokens) { (tokens.PrecedesInternal(*this), ...); } - inline bool JobGraphEvent::IsSignaled() + inline bool TaskGraphEvent::IsSignaled() { return m_semaphore.try_acquire_for(AZStd::chrono::milliseconds{ 0 }); } - inline void JobGraphEvent::Wait() + inline void TaskGraphEvent::Wait() { m_semaphore.acquire(); } - inline void JobGraphEvent::Signal() + inline void TaskGraphEvent::Signal() { m_semaphore.release(); } template - inline JobToken JobGraph::AddJob(JobDescriptor const& desc, Lambda&& lambda) + TaskToken TaskGraph::AddTask(TaskDescriptor const& desc, Lambda&& lambda) { - AZ_Assert(!m_submitted, "Cannot mutate a JobGraph that was previously submitted or in flight."); + AZ_Assert(!m_submitted, "Cannot mutate a TaskGraph that was previously submitted or in flight."); - m_jobs.emplace_back(desc, AZStd::forward(lambda)); + m_tasks.emplace_back(desc, AZStd::forward(lambda)); - return { *this, m_jobs.size() - 1 }; + return { *this, m_tasks.size() - 1 }; } template - inline AZStd::array JobGraph::AddJobs(JobDescriptor const& descriptor, Lambdas&&... lambdas) + AZStd::array TaskGraph::AddTasks(TaskDescriptor const& descriptor, Lambdas&&... lambdas) { - return { AddJob(descriptor, AZStd::forward(lambdas))... }; + return { AddTask(descriptor, AZStd::forward(lambdas))... }; } - inline void JobGraph::Detach() + inline void TaskGraph::Detach() { m_retained = false; } diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index a23ec9ab82..1e2a0b98a0 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -221,8 +221,6 @@ set(FILES Jobs/Internal/JobManagerWorkStealing.cpp Jobs/Internal/JobManagerWorkStealing.h Jobs/Internal/JobNotify.h - Jobs/Internal/JobTypeEraser.cpp - Jobs/Internal/JobTypeEraser.h Jobs/Job.cpp Jobs/Job.h Jobs/JobCancelGroup.h @@ -230,14 +228,8 @@ set(FILES Jobs/JobCompletionSpin.h Jobs/JobContext.cpp Jobs/JobContext.h - Jobs/JobDescriptor.h Jobs/JobEmpty.h - Jobs/JobExecutor.cpp - Jobs/JobExecutor.h Jobs/JobFunction.h - Jobs/JobGraph.cpp - Jobs/JobGraph.h - Jobs/JobGraph.inl Jobs/JobManager.cpp Jobs/JobManager.h Jobs/JobManagerBus.h @@ -624,6 +616,16 @@ set(FILES Socket/AzSocket_fwd.h Socket/AzSocket.cpp Socket/AzSocket.h + Task/Internal/Task.cpp + Task/Internal/Task.inl + Task/Internal/Task.h + Task/Internal/TaskConfig.h + Task/TaskDescriptor.h + Task/TaskExecutor.cpp + Task/TaskExecutor.h + Task/TaskGraph.cpp + Task/TaskGraph.h + Task/TaskGraph.inl Threading/ThreadSafeDeque.h Threading/ThreadSafeDeque.inl Threading/ThreadSafeObject.h diff --git a/Code/Framework/AzCore/Tests/JobGraphTests.cpp b/Code/Framework/AzCore/Tests/TaskTests.cpp similarity index 53% rename from Code/Framework/AzCore/Tests/JobGraphTests.cpp rename to Code/Framework/AzCore/Tests/TaskTests.cpp index 469773f471..eeb523ae26 100644 --- a/Code/Framework/AzCore/Tests/JobGraphTests.cpp +++ b/Code/Framework/AzCore/Tests/TaskTests.cpp @@ -6,26 +6,26 @@ * */ -#include -#include +#include +#include #include #include #include -using AZ::JobDescriptor; -using AZ::JobGraph; -using AZ::JobGraphEvent; -using AZ::JobExecutor; -using AZ::Internal::TypeErasedJob; -using AZ::JobPriority; +using AZ::TaskDescriptor; +using AZ::TaskGraph; +using AZ::TaskGraphEvent; +using AZ::TaskExecutor; +using AZ::Internal::Task; +using AZ::TaskPriority; -static JobDescriptor defaultJD{ "JobGraphTestJob", "JobGraphTests" }; +static TaskDescriptor defaultTD{ "TaskGraphTestTask", "TaskGraphTests" }; namespace UnitTest { - class JobGraphTestFixture : public AllocatorsTestFixture + class TaskGraphTestFixture : public AllocatorsTestFixture { public: void SetUp() override @@ -34,7 +34,7 @@ namespace UnitTest AZ::AllocatorInstance::Create(); AZ::AllocatorInstance::Create(); - m_executor = aznew JobExecutor(4); + m_executor = aznew TaskExecutor(4); } void TearDown() override @@ -46,38 +46,38 @@ namespace UnitTest } protected: - JobExecutor* m_executor; + TaskExecutor* m_executor; }; - TEST(JobGraphTests, TrivialJobLambda) + TEST(TaskGraphTests, TrivialTaskLambda) { int x = 0; - TypeErasedJob job( - defaultJD, + Task task( + defaultTD, [&x]() { ++x; }); - job.Invoke(); + task.Invoke(); EXPECT_EQ(1, x); } - TEST(JobGraphTests, TrivialJobLambdaMove) + TEST(TaskGraphTests, TrivialTaskLambdaMove) { int x = 0; - TypeErasedJob job( - defaultJD, + Task task( + defaultTD, [&x]() { ++x; }); - TypeErasedJob job2 = AZStd::move(job); + Task task2 = AZStd::move(task); - job2.Invoke(); + task2.Invoke(); EXPECT_EQ(1, x); } @@ -110,78 +110,90 @@ namespace UnitTest int copyCount = 0; }; - TEST(JobGraphTests, MoveOnlyJobLambda) + /* + TEST(TaskGraphTests, ThisShouldNotCompile) + { + auto lambda = [] + { + }; + + Task task(defaultTD, lambda); + task.Invoke(); + } + */ + + TEST(TaskGraphTests, MoveOnlyTaskLambda) { TrackMoves tm; int moveCount = 0; - TypeErasedJob job( - defaultJD, + Task task( + defaultTD, [tm = AZStd::move(tm), &moveCount] { moveCount = tm.moveCount; }); - job.Invoke(); + task.Invoke(); // Two moves are expected. Once into the capture body of the lambda, once to construct - // the type erased job + // the type erased task EXPECT_EQ(2, moveCount); } - TEST(JobGraphTests, MoveOnlyJobLambdaMove) + TEST(TaskGraphTests, MoveOnlyTaskLambdaMove) { TrackMoves tm; int moveCount = 0; - TypeErasedJob job( - defaultJD, + Task task( + defaultTD, [tm = AZStd::move(tm), &moveCount] { moveCount = tm.moveCount; }); - TypeErasedJob job2 = AZStd::move(job); - job2.Invoke(); + Task task2 = AZStd::move(task); + task2.Invoke(); EXPECT_EQ(3, moveCount); } - TEST(JobGraphTests, CopyOnlyJobLambda) + TEST(TaskGraphTests, CopyOnlyTaskLambda) { TrackCopies tc; int copyCount = 0; - TypeErasedJob job( - defaultJD, + Task task( + defaultTD, [tc, ©Count] { copyCount = tc.copyCount; }); - job.Invoke(); + task.Invoke(); // Two copies are expected. Once into the capture body of the lambda, once to construct - // the type erased job + // the type erased task EXPECT_EQ(2, copyCount); } - TEST(JobGraphTests, CopyOnlyJobLambdaMove) + TEST(TaskGraphTests, CopyOnlyTaskLambdaMove) { TrackCopies tc; int copyCount = 0; - TypeErasedJob job( - defaultJD, + Task task( + defaultTD, [tc, ©Count] { copyCount = tc.copyCount; }); - TypeErasedJob job2 = AZStd::move(job); - job2.Invoke(); + Task task2 = AZStd::move(task); + task2.Invoke(); EXPECT_EQ(3, copyCount); } - TEST(JobGraphTests, DestroyLambda) + TEST(TaskGraphTests, DestroyLambda) { // This test ensures that for a lambda with a destructor, the destructor is invoked // exactly once on a non-moved-from object. @@ -209,12 +221,12 @@ namespace UnitTest { TrackDestroy td{ &x }; - TypeErasedJob job( - defaultJD, + Task task( + defaultTD, [td = AZStd::move(td)] { }); - job.Invoke(); + task.Invoke(); // Destructor should not have run yet (except on moved-from instances) EXPECT_EQ(x, 0); } @@ -223,25 +235,21 @@ namespace UnitTest EXPECT_EQ(x, 1); } - TEST_F(JobGraphTestFixture, SerialGraph) + TEST_F(TaskGraphTestFixture, VariadicInterface) { int x = 0; - JobGraph graph; - auto a = graph.AddJob( - defaultJD, + TaskGraph graph; + auto [a, b, c] = graph.AddTasks( + defaultTD, [&] { x += 3; - }); - auto b = graph.AddJob( - defaultJD, + }, [&] { x = 4 * x; - }); - auto c = graph.AddJob( - defaultJD, + }, [&] { x -= 1; @@ -250,35 +258,69 @@ namespace UnitTest a.Precedes(b); b.Precedes(c); - JobGraphEvent ev; + TaskGraphEvent ev; graph.SubmitOnExecutor(*m_executor, &ev); ev.Wait(); EXPECT_EQ(11, x); } - TEST_F(JobGraphTestFixture, DetachedGraph) + TEST_F(TaskGraphTestFixture, SerialGraph) { int x = 0; - JobGraphEvent ev; + TaskGraph graph; + auto a = graph.AddTask( + defaultTD, + [&] + { + x += 3; + }); + auto b = graph.AddTask( + defaultTD, + [&] + { + x = 4 * x; + }); + auto c = graph.AddTask( + defaultTD, + [&] + { + x -= 1; + }); + + a.Precedes(b); + b.Precedes(c); + + TaskGraphEvent ev; + graph.SubmitOnExecutor(*m_executor, &ev); + ev.Wait(); + + EXPECT_EQ(11, x); + } + + TEST_F(TaskGraphTestFixture, DetachedGraph) + { + int x = 0; + + TaskGraphEvent ev; { - JobGraph graph; - auto a = graph.AddJob( - defaultJD, + TaskGraph graph; + auto a = graph.AddTask( + defaultTD, [&] { x += 3; }); - auto b = graph.AddJob( - defaultJD, + auto b = graph.AddTask( + defaultTD, [&] { x = 4 * x; }); - auto c = graph.AddJob( - defaultJD, + auto c = graph.AddTask( + defaultTD, [&] { x -= 1; @@ -295,35 +337,35 @@ namespace UnitTest EXPECT_EQ(11, x); } - TEST_F(JobGraphTestFixture, ForkJoin) + TEST_F(TaskGraphTestFixture, ForkJoin) { AZStd::atomic x = 0; - // Job a initializes x to 3 - // Job b and c toggles the lowest two bits atomically - // Job d decrements x + // Task a initializes x to 3 + // Task b and c toggles the lowest two bits atomically + // Task d decrements x - JobGraph graph; - auto a = graph.AddJob( - defaultJD, + TaskGraph graph; + auto a = graph.AddTask( + defaultTD, [&] { x = 0b111; }); - auto b = graph.AddJob( - defaultJD, + auto b = graph.AddTask( + defaultTD, [&] { x ^= 1; }); - auto c = graph.AddJob( - defaultJD, + auto c = graph.AddTask( + defaultTD, [&] { x ^= 2; }); - auto d = graph.AddJob( - defaultJD, + auto d = graph.AddTask( + defaultTD, [&] { x -= 1; @@ -336,65 +378,65 @@ namespace UnitTest // d a.Precedes(b, c); - d.Succeeds(b, c); + d.Follows(b, c); - JobGraphEvent ev; + TaskGraphEvent ev; graph.SubmitOnExecutor(*m_executor, &ev); ev.Wait(); EXPECT_EQ(3, x); } - TEST_F(JobGraphTestFixture, SpawnSubgraph) + TEST_F(TaskGraphTestFixture, SpawnSubgraph) { AZStd::atomic x = 0; - JobGraph graph; - auto a = graph.AddJob( - defaultJD, + TaskGraph graph; + auto a = graph.AddTask( + defaultTD, [&] { x = 0b111; }); - auto b = graph.AddJob( - defaultJD, + auto b = graph.AddTask( + defaultTD, [&] { x ^= 1; }); - auto c = graph.AddJob( - defaultJD, + auto c = graph.AddTask( + defaultTD, [&] { x ^= 2; - JobGraph subgraph; - auto e = subgraph.AddJob( - defaultJD, + TaskGraph subgraph; + auto e = subgraph.AddTask( + defaultTD, [&] { x ^= 0b1000; }); - auto f = subgraph.AddJob( - defaultJD, + auto f = subgraph.AddTask( + defaultTD, [&] { x ^= 0b10000; }); - auto g = subgraph.AddJob( - defaultJD, + auto g = subgraph.AddTask( + defaultTD, [&] { x += 0b1000; }); e.Precedes(g); f.Precedes(g); - JobGraphEvent ev; + TaskGraphEvent ev; subgraph.SubmitOnExecutor(*m_executor, &ev); ev.Wait(); }); - auto d = graph.AddJob( - defaultJD, + auto d = graph.AddTask( + defaultTD, [&] { x -= 1; @@ -418,56 +460,56 @@ namespace UnitTest b.Precedes(d); c.Precedes(d); - JobGraphEvent ev; + TaskGraphEvent ev; graph.SubmitOnExecutor(*m_executor, &ev); ev.Wait(); EXPECT_EQ(3 | 0b100000, x); } - TEST_F(JobGraphTestFixture, RetainedGraph) + TEST_F(TaskGraphTestFixture, RetainedGraph) { AZStd::atomic x = 0; - JobGraph graph; - auto a = graph.AddJob( - defaultJD, + TaskGraph graph; + auto a = graph.AddTask( + defaultTD, [&] { x = 0b111; }); - auto b = graph.AddJob( - defaultJD, + auto b = graph.AddTask( + defaultTD, [&] { x ^= 1; }); - auto c = graph.AddJob( - defaultJD, + auto c = graph.AddTask( + defaultTD, [&] { x ^= 2; }); - auto d = graph.AddJob( - defaultJD, + auto d = graph.AddTask( + defaultTD, [&] { x -= 1; }); - auto e = graph.AddJob( - defaultJD, + auto e = graph.AddTask( + defaultTD, [&] { x ^= 0b1000; }); - auto f = graph.AddJob( - defaultJD, + auto f = graph.AddTask( + defaultTD, [&] { x ^= 0b10000; }); - auto g = graph.AddJob( - defaultJD, + auto g = graph.AddTask( + defaultTD, [&] { x += 0b1000; @@ -486,10 +528,10 @@ namespace UnitTest a.Precedes(b, c); b.Precedes(d); c.Precedes(e, f); - g.Succeeds(e, f); + g.Follows(e, f); g.Precedes(d); - JobGraphEvent ev; + TaskGraphEvent ev; graph.SubmitOnExecutor(*m_executor, &ev); ev.Wait(); @@ -501,18 +543,107 @@ namespace UnitTest EXPECT_EQ(3 | 0b100000, x); } + + TEST_F(TaskGraphTestFixture, ExecutorDrainRetained) + { + bool drainDone = false; + AZStd::binary_semaphore taskStart; + AZStd::binary_semaphore threadLaunched; + AZStd::binary_semaphore threadFinished; + + TaskGraph graph; + auto a = graph.AddTask( + defaultTD, + [&] + { + taskStart.acquire(); + }); + + graph.SubmitOnExecutor(*m_executor); + + AZStd::thread drainThread{ [this, &drainDone, &threadLaunched, &threadFinished] + { + threadLaunched.release(); + m_executor->Drain(); + drainDone = true; + threadFinished.release(); + } }; + + + // Wait until our drain thread has launched + threadLaunched.acquire(); + + // The task itself hasn't started, so the drain should still be blocking + EXPECT_EQ(false, drainDone); + + // Allow the task to finish + taskStart.release(); + + // Wait for the drain thread to wrap up + threadFinished.acquire(); + + // We successfully drained the executor + EXPECT_EQ(true, drainDone); + + drainThread.join(); + } + + TEST_F(TaskGraphTestFixture, ExecutorDrainDetached) + { + bool drainDone = false; + AZStd::binary_semaphore taskStart; + AZStd::binary_semaphore threadLaunched; + AZStd::binary_semaphore threadFinished; + + TaskGraph graph; + auto a = graph.AddTask( + defaultTD, + [&] + { + taskStart.acquire(); + }); + graph.Detach(); + + graph.SubmitOnExecutor(*m_executor); + + AZStd::thread drainThread{ [this, &drainDone, &threadLaunched, &threadFinished] + { + threadLaunched.release(); + m_executor->Drain(); + drainDone = true; + threadFinished.release(); + } }; + + + // Wait until our drain thread has launched + threadLaunched.acquire(); + + // The task itself hasn't started, so the drain should still be blocking + EXPECT_EQ(false, drainDone); + + // Allow the task to finish + taskStart.release(); + + // Wait for the drain thread to wrap up + threadFinished.acquire(); + + // We successfully drained the executor + EXPECT_EQ(true, drainDone); + + drainThread.join(); + } } // namespace UnitTest #if defined(HAVE_BENCHMARK) namespace Benchmark { - class JobGraphBenchmarkFixture : public ::benchmark::Fixture + class TaskGraphBenchmarkFixture : public ::benchmark::Fixture { public: void SetUp(benchmark::State&) override { - executor = new JobExecutor; - graph = new JobGraph; + executor = new TaskExecutor; + graph = new TaskGraph; } void TearDown(benchmark::State&) override @@ -521,38 +652,38 @@ namespace Benchmark delete executor; } - JobDescriptor descriptors[4] = { { "critical", "benchmark", JobPriority::CRITICAL }, - { "high", "benchmark", JobPriority::HIGH }, - { "medium", "benchmark", JobPriority::MEDIUM }, - { "low", "benchmark", JobPriority::LOW } }; + TaskDescriptor descriptors[4] = { { "critical", "benchmark", TaskPriority::CRITICAL }, + { "high", "benchmark", TaskPriority::HIGH }, + { "medium", "benchmark", TaskPriority::MEDIUM }, + { "low", "benchmark", TaskPriority::LOW } }; - JobGraph* graph; - JobExecutor* executor; + TaskGraph* graph; + TaskExecutor* executor; }; - BENCHMARK_F(JobGraphBenchmarkFixture, QueueToDequeue)(benchmark::State& state) + BENCHMARK_F(TaskGraphBenchmarkFixture, QueueToDequeue)(benchmark::State& state) { - graph->AddJob( + graph->AddTask( descriptors[2], [] { }); for (auto _ : state) { - JobGraphEvent ev; + TaskGraphEvent ev; graph->SubmitOnExecutor(*executor, &ev); ev.Wait(); } } - BENCHMARK_F(JobGraphBenchmarkFixture, OneAfterAnother)(benchmark::State& state) + BENCHMARK_F(TaskGraphBenchmarkFixture, OneAfterAnother)(benchmark::State& state) { - auto a = graph->AddJob( + auto a = graph->AddTask( descriptors[2], [] { }); - auto b = graph->AddJob( + auto b = graph->AddTask( descriptors[2], [] { @@ -561,15 +692,15 @@ namespace Benchmark for (auto _ : state) { - JobGraphEvent ev; + TaskGraphEvent ev; graph->SubmitOnExecutor(*executor, &ev); ev.Wait(); } } - BENCHMARK_F(JobGraphBenchmarkFixture, FourToOneJoin)(benchmark::State& state) + BENCHMARK_F(TaskGraphBenchmarkFixture, FourToOneJoin)(benchmark::State& state) { - auto [a, b, c, d, e] = graph->AddJobs( + auto [a, b, c, d, e] = graph->AddTasks( descriptors[2], [] { @@ -587,11 +718,11 @@ namespace Benchmark { }); - e.Succeeds(a, b, c, d); + e.Follows(a, b, c, d); for (auto _ : state) { - JobGraphEvent ev; + TaskGraphEvent ev; graph->SubmitOnExecutor(*executor, &ev); ev.Wait(); } diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index 480baabe7a..ca0e2862fc 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -40,7 +40,6 @@ set(FILES Interface.cpp IO/Path/PathTests.cpp IPC.cpp - JobGraphTests.cpp Jobs.cpp JSON.cpp FixedWidthIntegers.cpp @@ -66,6 +65,7 @@ set(FILES StreamerTests.cpp StringFunc.cpp SystemFile.cpp + TaskTests.cpp TickBusTest.cpp TimeDataStatistics.cpp UUIDTests.cpp From 4743ca8bc1d24e24d7d0f6171dc8f5baf858804e Mon Sep 17 00:00:00 2001 From: Jeremy Ong Date: Thu, 5 Aug 2021 17:32:57 -0600 Subject: [PATCH 23/38] Fix segfault when checking detached graph completion status Signed-off-by: Jeremy Ong --- Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp index c69763f289..e8b4735243 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp +++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp @@ -258,7 +258,8 @@ namespace AZ } } - if (task->m_graph->Release() == (task->m_graph->m_parent ? 1 : 0)) + bool isRetained = task->m_graph->m_parent != nullptr; + if (task->m_graph->Release() == (isRetained ? 1 : 0)) { m_executor->ReleaseGraph(); } From 4f9c2cf693924ec7732eed591d53937ef869bd50 Mon Sep 17 00:00:00 2001 From: Jeremy Ong Date: Fri, 6 Aug 2021 03:02:41 -0600 Subject: [PATCH 24/38] Remove TaskGraph::Drain which was only added initially for testing The drain function was used only before the API gained the ability to wait on the completion of a graph. This is the correct way to "drain" the task executor of work. Signed-off-by: Jeremy Ong --- .../AzCore/AzCore/Task/TaskExecutor.cpp | 18 +--- .../AzCore/AzCore/Task/TaskExecutor.h | 5 -- Code/Framework/AzCore/Tests/TaskTests.cpp | 89 ------------------- 3 files changed, 1 insertion(+), 111 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp index e8b4735243..293b88b2e6 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp +++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp @@ -355,24 +355,8 @@ namespace AZ m_workers[++m_lastSubmission % m_threadCount].Enqueue(&task); } - void TaskExecutor::Drain() - { - m_isDraining = true; - if (m_graphsRemaining == 0) - { - return; - } - m_drainSemaphore.acquire(); - } - void TaskExecutor::ReleaseGraph() { - uint64_t graphsRemaining = --m_graphsRemaining; - - if (graphsRemaining == 0 && m_isDraining) - { - m_drainSemaphore.release(); - m_isDraining = false; - } + --m_graphsRemaining; } } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h index ad4c4b81c2..dc2fa5a4c8 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h +++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h @@ -76,9 +76,6 @@ namespace AZ void Submit(Internal::Task& task); - // Wait until tasks are cleared from the executor (note, does not prevent future tasks from being submitted) - // If this is used, it's expected to be used between frames to shutdown the engine - void Drain(); private: friend class Internal::TaskWorker; @@ -88,7 +85,5 @@ namespace AZ uint32_t m_threadCount = 0; AZStd::atomic m_lastSubmission; AZStd::atomic m_graphsRemaining; - AZStd::atomic m_isDraining; - AZStd::binary_semaphore m_drainSemaphore; }; } // namespace AZ diff --git a/Code/Framework/AzCore/Tests/TaskTests.cpp b/Code/Framework/AzCore/Tests/TaskTests.cpp index eeb523ae26..f2ca484df3 100644 --- a/Code/Framework/AzCore/Tests/TaskTests.cpp +++ b/Code/Framework/AzCore/Tests/TaskTests.cpp @@ -543,95 +543,6 @@ namespace UnitTest EXPECT_EQ(3 | 0b100000, x); } - - TEST_F(TaskGraphTestFixture, ExecutorDrainRetained) - { - bool drainDone = false; - AZStd::binary_semaphore taskStart; - AZStd::binary_semaphore threadLaunched; - AZStd::binary_semaphore threadFinished; - - TaskGraph graph; - auto a = graph.AddTask( - defaultTD, - [&] - { - taskStart.acquire(); - }); - - graph.SubmitOnExecutor(*m_executor); - - AZStd::thread drainThread{ [this, &drainDone, &threadLaunched, &threadFinished] - { - threadLaunched.release(); - m_executor->Drain(); - drainDone = true; - threadFinished.release(); - } }; - - - // Wait until our drain thread has launched - threadLaunched.acquire(); - - // The task itself hasn't started, so the drain should still be blocking - EXPECT_EQ(false, drainDone); - - // Allow the task to finish - taskStart.release(); - - // Wait for the drain thread to wrap up - threadFinished.acquire(); - - // We successfully drained the executor - EXPECT_EQ(true, drainDone); - - drainThread.join(); - } - - TEST_F(TaskGraphTestFixture, ExecutorDrainDetached) - { - bool drainDone = false; - AZStd::binary_semaphore taskStart; - AZStd::binary_semaphore threadLaunched; - AZStd::binary_semaphore threadFinished; - - TaskGraph graph; - auto a = graph.AddTask( - defaultTD, - [&] - { - taskStart.acquire(); - }); - graph.Detach(); - - graph.SubmitOnExecutor(*m_executor); - - AZStd::thread drainThread{ [this, &drainDone, &threadLaunched, &threadFinished] - { - threadLaunched.release(); - m_executor->Drain(); - drainDone = true; - threadFinished.release(); - } }; - - - // Wait until our drain thread has launched - threadLaunched.acquire(); - - // The task itself hasn't started, so the drain should still be blocking - EXPECT_EQ(false, drainDone); - - // Allow the task to finish - taskStart.release(); - - // Wait for the drain thread to wrap up - threadFinished.acquire(); - - // We successfully drained the executor - EXPECT_EQ(true, drainDone); - - drainThread.join(); - } } // namespace UnitTest #if defined(HAVE_BENCHMARK) From 0f58d7394eaf8402b322663c2993062b9c23b393 Mon Sep 17 00:00:00 2001 From: greerdv Date: Fri, 6 Aug 2021 11:05:31 +0100 Subject: [PATCH 25/38] fixes #2544, fixes all doc links for physx, cloth, blast and white box Signed-off-by: greerdv --- Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp | 2 +- Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp | 2 +- Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp | 2 +- Gems/PhysX/Code/Editor/CollisionFilteringWidget.cpp | 2 +- Gems/PhysX/Code/Editor/PvdWidget.cpp | 2 +- Gems/PhysX/Code/Editor/SettingsWidget.cpp | 2 +- Gems/PhysX/Code/Source/EditorBallJointComponent.cpp | 1 + Gems/PhysX/Code/Source/EditorColliderComponent.cpp | 2 +- Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp | 1 + Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp | 2 +- Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp | 1 + Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp | 2 +- Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp | 1 + Gems/PhysX/Code/Source/NameConstants.cpp | 2 +- .../Components/EditorCharacterControllerComponent.cpp | 1 + .../Components/EditorCharacterGameplayComponent.cpp | 1 + .../Code/Source/PhysXCharacters/Components/RagdollComponent.cpp | 1 + .../Code/Source/Components/EditorWhiteBoxColliderComponent.cpp | 2 +- Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp | 2 +- 19 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp index 76a40db66e..58f28ed32a 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp @@ -38,7 +38,7 @@ namespace Blast ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute( AZ::Edit::Attributes::HelpPageURL, - "https://o3de.org/docs/user-guide/components/reference/blast-family/") + "https://o3de.org/docs/user-guide/components/reference/destruction/blast-family/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement( AZ::Edit::UIHandlers::Default, &EditorBlastFamilyComponent::m_blastAsset, "Blast asset", diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp index 27e57a569e..23ec5ae524 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp @@ -61,7 +61,7 @@ namespace Blast ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute( AZ::Edit::Attributes::HelpPageURL, - "https://o3de.org/docs/user-guide/components/reference/blast-family-mesh-data/") + "https://o3de.org/docs/user-guide/components/reference/destruction/blast-family-mesh-data/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement( AZ::Edit::UIHandlers::CheckBox, &EditorBlastMeshDataComponent::m_showMeshAssets, diff --git a/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp b/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp index f57cc0d171..113d660b32 100644 --- a/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp +++ b/Gems/NvCloth/Code/Source/Components/EditorClothComponent.cpp @@ -48,7 +48,7 @@ namespace NvCloth ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Cloth.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Cloth.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/cloth/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/cloth/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->UIElement(AZ::Edit::UIHandlers::CheckBox, "Simulate in editor", diff --git a/Gems/PhysX/Code/Editor/CollisionFilteringWidget.cpp b/Gems/PhysX/Code/Editor/CollisionFilteringWidget.cpp index f9de067007..4d581622ed 100644 --- a/Gems/PhysX/Code/Editor/CollisionFilteringWidget.cpp +++ b/Gems/PhysX/Code/Editor/CollisionFilteringWidget.cpp @@ -18,7 +18,7 @@ namespace PhysX namespace Editor { static const char* const s_collisionFilteringLink = "Learn more about configuring collision filtering."; - static const char* const s_collisionFilteringAddress = "configuration/collision"; + static const char* const s_collisionFilteringAddress = "configuring/configuration-collision-layers"; CollisionFilteringWidget::CollisionFilteringWidget(QWidget* parent) : QWidget(parent) diff --git a/Gems/PhysX/Code/Editor/PvdWidget.cpp b/Gems/PhysX/Code/Editor/PvdWidget.cpp index 41af8f08d8..27f1410ce5 100644 --- a/Gems/PhysX/Code/Editor/PvdWidget.cpp +++ b/Gems/PhysX/Code/Editor/PvdWidget.cpp @@ -19,7 +19,7 @@ namespace PhysX namespace Editor { static const char* const s_pvdDocumentationLink = "Learn more about the PhysX Visual Debugger (PVD)."; - static const char* const s_pvdDocumentationAddress = "configuration/debugger"; + static const char* const s_pvdDocumentationAddress = "configuring/configuration-debugger"; PvdWidget::PvdWidget(QWidget* parent) : QWidget(parent) diff --git a/Gems/PhysX/Code/Editor/SettingsWidget.cpp b/Gems/PhysX/Code/Editor/SettingsWidget.cpp index 01ee273260..39eb6ae9d8 100644 --- a/Gems/PhysX/Code/Editor/SettingsWidget.cpp +++ b/Gems/PhysX/Code/Editor/SettingsWidget.cpp @@ -19,7 +19,7 @@ namespace PhysX namespace Editor { static const char* const s_settingsDocumentationLink = "Learn more about configuring PhysX"; - static const char* const s_settingsDocumentationAddress = "configuration/global"; + static const char* const s_settingsDocumentationAddress = "configuring/configuration-global"; SettingsWidget::SettingsWidget(QWidget* parent) : QWidget(parent) diff --git a/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp b/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp index 737f06a62b..d196b4644d 100644 --- a/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp @@ -36,6 +36,7 @@ namespace PhysX ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/ball-joint/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(0, &EditorBallJointComponent::m_swingLimit, "Swing Limit", "Limitations for the swing (Y and Z axis) about joint") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorBallJointComponent::m_componentModeDelegate, "Component Mode", "Ball Joint Component Mode") diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index 1409760742..41db7d71ec 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -188,7 +188,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCollider.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/PhysXCollider.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx-collider/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/collider/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_configuration, "Collider Configuration", "Configuration of the collider") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) diff --git a/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp b/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp index 24ae6dbf6a..bf5af6b78d 100644 --- a/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp @@ -34,6 +34,7 @@ namespace PhysX ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/fixed-joint/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorFixedJointComponent::m_componentModeDelegate, "Component Mode", "Fixed Joint Component Mode") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) diff --git a/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp b/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp index d2c6e64633..610a0a9b2e 100644 --- a/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp @@ -170,7 +170,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/ForceVolume.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/ForceVolume.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx-force-region/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/force-region/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::RequiredService, AZ_CRC("PhysXTriggerService", 0x3a117d7b)) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_visibleInEditor, "Visible", "Always show the component in viewport") diff --git a/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp b/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp index f82cb8bb47..6676b9865e 100644 --- a/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp @@ -36,6 +36,7 @@ namespace PhysX ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/hinge-joint/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(0, &EditorHingeJointComponent::m_angularLimit, "Angular Limit", "Limitations for the rotation about hinge axis") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorHingeJointComponent::m_componentModeDelegate, "Component Mode", "Hinge Joint Component Mode") diff --git a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp index bd7c49a0a7..ce812a9f31 100644 --- a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp @@ -330,7 +330,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/PhysXRigidBody.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx-rigid-body-physics/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/rigid-body-physics/") ->DataElement(0, &EditorRigidBodyComponent::m_config, "Configuration", "Configuration for rigid body physics.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorRigidBodyComponent::CreateEditorWorldRigidBody) diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index 0e9a1ab8ee..7ff62e0fc6 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -85,6 +85,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCollider.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/PhysXCollider.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/shape-collider/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_colliderConfig, "Collider configuration", "Configuration of the collider") diff --git a/Gems/PhysX/Code/Source/NameConstants.cpp b/Gems/PhysX/Code/Source/NameConstants.cpp index 285271edf7..5f311769b8 100644 --- a/Gems/PhysX/Code/Source/NameConstants.cpp +++ b/Gems/PhysX/Code/Source/NameConstants.cpp @@ -14,7 +14,7 @@ namespace PhysX { const AZStd::string& GetPhysXDocsRoot() { - static const AZStd::string val = "https://o3de.org/docs/user-guide/interactivity/physics/"; + static const AZStd::string val = "https://o3de.org/docs/user-guide/interactivity/physics/nvidia-physx/"; return val; } } // namespace UXNameConstants diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterControllerComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterControllerComponent.cpp index d58952858d..4e24067dbf 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterControllerComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterControllerComponent.cpp @@ -99,6 +99,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCharacter.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/PhysXCharacter.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/character-controller/") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerComponent::m_configuration, "Configuration", "Configuration for the character controller") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterGameplayComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterGameplayComponent.cpp index 3bc2b39ebf..81d158d81c 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterGameplayComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterGameplayComponent.cpp @@ -49,6 +49,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCharacter.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/PhysXCharacter.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/character-gameplay/") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterGameplayComponent::m_gameplayConfig, "Gameplay Configuration", "Gameplay Configuration") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp index 844cf65707..6fa3bdbffd 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp @@ -88,6 +88,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXRagdoll.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/PhysXRagdoll.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/ragdoll/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_positionIterations, "Position Iteration Count", "A higher iteration count generally improves fidelity at the cost of performance, but note that very high " diff --git a/Gems/WhiteBox/Code/Source/Components/EditorWhiteBoxColliderComponent.cpp b/Gems/WhiteBox/Code/Source/Components/EditorWhiteBoxColliderComponent.cpp index d502194126..c22be6f072 100644 --- a/Gems/WhiteBox/Code/Source/Components/EditorWhiteBoxColliderComponent.cpp +++ b/Gems/WhiteBox/Code/Source/Components/EditorWhiteBoxColliderComponent.cpp @@ -44,7 +44,7 @@ namespace WhiteBox ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute( AZ::Edit::Attributes::HelpPageURL, - "https://o3de.org/docs/user-guide/components/reference/white-box-collider/") + "https://o3de.org/docs/user-guide/components/reference/shape/white-box-collider/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement( AZ::Edit::UIHandlers::Default, &EditorWhiteBoxColliderComponent::m_physicsColliderConfiguration, diff --git a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp index 5448c5c123..ad59da63d5 100644 --- a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp +++ b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp @@ -196,7 +196,7 @@ namespace WhiteBox ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/WhiteBox.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute( - AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/white-box/") + AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/shape/white-box/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement( AZ::Edit::UIHandlers::ComboBox, &EditorWhiteBoxComponent::m_defaultShape, "Default Shape", From ff8c4dce00f77d78875380ceeeb037d45bbb3b76 Mon Sep 17 00:00:00 2001 From: hultonha <82228511+hultonha@users.noreply.github.com> Date: Fri, 6 Aug 2021 12:50:31 +0100 Subject: [PATCH 26/38] Ensure we disconnect from EditorInteractionSystemViewportSelectionRequestBus while recreating m_interactionRequests (#2884) Fixes a crash while selecting an entity in the viewport while in 'pick' mode. --- .../EditorInteractionSystemComponent.cpp | 16 ++++++++++++---- .../ViewportUi/ViewportUiDisplay.cpp | 2 -- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.cpp index 3af1672ecb..696cf6b184 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.cpp @@ -50,11 +50,19 @@ namespace AzToolsFramework AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(GetEntityContextId()); } - m_entityDataCache = AZStd::make_unique(); + // temporarily disconnect from EditorInteractionSystemViewportSelectionRequestBus in case during the creation of + // m_interactionRequests (see interactionRequestsBuilder below) an event is propagated to the handler, if this happens then + // m_interactionRequests will be null as it will not have finished being created yet so we ensure no events are forwarded to it + EditorInteractionSystemViewportSelectionRequestBus::Handler::BusDisconnect(); - m_interactionRequests.reset(); // BusConnect/Disconnect in constructor/destructor, - // so have to reset before assigning the new one - m_interactionRequests = interactionRequestsBuilder(m_entityDataCache.get()); + { + m_entityDataCache = AZStd::make_unique(); + m_interactionRequests.reset(); // BusConnect/Disconnect in constructor/destructor, + // so have to reset before assigning the new one + m_interactionRequests = interactionRequestsBuilder(m_entityDataCache.get()); + } + + EditorInteractionSystemViewportSelectionRequestBus::Handler::BusConnect(GetEntityContextId()); } void EditorInteractionSystemComponent::SetDefaultHandler() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp index 3b2c26114d..80cc7941b0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp @@ -380,7 +380,6 @@ namespace AzToolsFramework::ViewportUi::Internal } PrepareWidgetForViewportUi(widget); - m_renderOverlay->setFocus(); } void ViewportUiDisplay::SetUiOverlayContentsAnchored(QPointer widget, const Qt::Alignment alignment) @@ -392,7 +391,6 @@ namespace AzToolsFramework::ViewportUi::Internal PrepareWidgetForViewportUi(widget); m_uiOverlayLayout.AddAnchoredWidget(widget, alignment); - m_renderOverlay->setFocus(); } void ViewportUiDisplay::UpdateUiOverlayGeometry() From 3986a1139646d47a0ea9cdebe38eed66662dc976 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Fri, 6 Aug 2021 06:52:11 -0500 Subject: [PATCH 27/38] Fixed Asset Browser path related context menu options. Signed-off-by: Chris Galvan --- .../AzAssetBrowserRequestHandler.cpp | 14 ++-- Code/Editor/Util/FileUtil.cpp | 81 +++---------------- Code/Editor/Util/FileUtil.h | 15 +--- 3 files changed, 18 insertions(+), 92 deletions(-) diff --git a/Code/Editor/AzAssetBrowser/AzAssetBrowserRequestHandler.cpp b/Code/Editor/AzAssetBrowser/AzAssetBrowserRequestHandler.cpp index 50f8aff144..cb97632e07 100644 --- a/Code/Editor/AzAssetBrowser/AzAssetBrowserRequestHandler.cpp +++ b/Code/Editor/AzAssetBrowser/AzAssetBrowserRequestHandler.cpp @@ -260,9 +260,7 @@ void AzAssetBrowserRequestHandler::AddContextMenuActions(QWidget* caller, QMenu* return; } - AZStd::string fullFileDirectory; AZStd::string fullFilePath; - AZStd::string fileName; AZStd::string extension; switch (entry->GetEntryType()) @@ -281,8 +279,6 @@ void AzAssetBrowserRequestHandler::AddContextMenuActions(QWidget* caller, QMenu* { AZ::Uuid sourceID = azrtti_cast(entry)->GetSourceUuid(); fullFilePath = entry->GetFullPath(); - fullFileDirectory = fullFilePath.substr(0, fullFilePath.find_last_of(AZ_CORRECT_DATABASE_SEPARATOR)); - fileName = entry->GetName(); AzFramework::StringFunc::Path::GetExtension(fullFilePath.c_str(), extension); // Add the "Open" menu item. @@ -369,19 +365,19 @@ void AzAssetBrowserRequestHandler::AddContextMenuActions(QWidget* caller, QMenu* { if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source) { - CFileUtil::PopulateQMenu(caller, menu, fileName.c_str(), fullFileDirectory.c_str()); + CFileUtil::PopulateQMenu(caller, menu, fullFilePath); } return; } - CFileUtil::PopulateQMenu(caller, menu, fileName.c_str(), fullFileDirectory.c_str()); + CFileUtil::PopulateQMenu(caller, menu, fullFilePath); } break; case AssetBrowserEntry::AssetEntryType::Folder: { - fullFileDirectory = entry->GetFullPath(); - // we are sending an empty filename to indicate that it is a folder and not a file - CFileUtil::PopulateQMenu(caller, menu, fileName.c_str(), fullFileDirectory.c_str()); + fullFilePath = entry->GetFullPath(); + + CFileUtil::PopulateQMenu(caller, menu, fullFilePath); } break; default: diff --git a/Code/Editor/Util/FileUtil.cpp b/Code/Editor/Util/FileUtil.cpp index 9e4f688f69..a270a830f2 100644 --- a/Code/Editor/Util/FileUtil.cpp +++ b/Code/Editor/Util/FileUtil.cpp @@ -1900,76 +1900,25 @@ IFileUtil::ECopyTreeResult CFileUtil::MoveTree(const QString& strSourceDirecto return eCopyResult; } -QString CFileUtil::PopupQMenu(const QString& filename, const QString& fullGamePath, QWidget* parent) +void CFileUtil::PopulateQMenu(QWidget* caller, QMenu* menu, const AZStd::string& fullGamePath) { - QStringList extraItemsFront; - return PopupQMenu(filename, fullGamePath, parent, nullptr, extraItemsFront); + PopulateQMenu(caller, menu, fullGamePath, nullptr); } -QString CFileUtil::PopupQMenu(const QString& filename, const QString& fullGamePath, QWidget* parent, [[maybe_unused]] bool* pIsSelected, const QStringList& extraItemsFront) +void CFileUtil::PopulateQMenu(QWidget* caller, QMenu* menu, const AZStd::string& fullGamePath, bool* isSelected) { - QStringList extraItemsBack; - return PopupQMenu(filename, fullGamePath, parent, nullptr, extraItemsFront, extraItemsBack); -} + // Normalize the full path so we get consistent separators + AZStd::string fullFilePath(fullGamePath); + AzFramework::StringFunc::Path::Normalize(fullFilePath); -QString CFileUtil::PopupQMenu(const QString& filename, const QString& fullGamePath, QWidget* parent, bool* pIsSelected, const QStringList& extraItemsFront, const QStringList& extraItemsBack) -{ - QMenu menu; - - foreach(QString text, extraItemsFront) - { - if (!text.isEmpty()) - { - menu.addAction(text); - } - } - if (extraItemsFront.count()) - { - menu.addSeparator(); - } - - PopulateQMenu(parent, &menu, filename, fullGamePath, pIsSelected); - if (extraItemsBack.count()) - { - menu.addSeparator(); - } - foreach(QString text, extraItemsBack) - { - if (!text.isEmpty()) - { - menu.addAction(text); - } - } - - QAction* result = menu.exec(QCursor::pos()); - return result ? result->text() : QString(); -} - -void CFileUtil::PopulateQMenu(QWidget* caller, QMenu* menu, const QString& filename, const QString& fullGamePath) -{ - PopulateQMenu(caller, menu, filename, fullGamePath, nullptr); -} - -void CFileUtil::PopulateQMenu(QWidget* caller, QMenu* menu, const QString& filename, const QString& fullGamePath, bool* isSelected) -{ - QString fullPath; + QString fullPath(fullFilePath.c_str()); + QFileInfo fileInfo(fullPath); if (isSelected) { *isSelected = false; } - if (!filename.isEmpty()) - { - QString path = Path::MakeGamePath(fullGamePath); - path = Path::AddSlash(path) + filename; - fullPath = Path::GamePathToFullPath(path); - } - else - { - fullPath = fullGamePath; - } - uint32 nFileAttr = CFileUtil::GetAttributes(fullPath.toUtf8().data()); QAction* action; @@ -2005,21 +1954,13 @@ void CFileUtil::PopulateQMenu(QWidget* caller, QMenu* menu, const QString& filen action = menu->addAction(QObject::tr("Copy Name To Clipboard"), [=]() { - if (filename.isEmpty()) - { - QFileInfo fi(fullGamePath); - QString file = fi.completeBaseName(); - QApplication::clipboard()->setText(file); - } - else - { - QApplication::clipboard()->setText(filename); - } + QString fileName = fileInfo.completeBaseName(); + QApplication::clipboard()->setText(fileName); }); action = menu->addAction(QObject::tr("Copy Path To Clipboard"), [fullPath]() { QApplication::clipboard()->setText(fullPath); }); - if (!filename.isEmpty() && GetIEditor()->IsSourceControlAvailable() && nFileAttr != SCC_FILE_ATTRIBUTE_INVALID) + if (fileInfo.isFile() && GetIEditor()->IsSourceControlAvailable() && nFileAttr != SCC_FILE_ATTRIBUTE_INVALID) { bool isEnableSC = nFileAttr & SCC_FILE_ATTRIBUTE_MANAGED; bool isInPak = nFileAttr & SCC_FILE_ATTRIBUTE_INPAK; diff --git a/Code/Editor/Util/FileUtil.h b/Code/Editor/Util/FileUtil.h index 000215e98d..599e0f0b2a 100644 --- a/Code/Editor/Util/FileUtil.h +++ b/Code/Editor/Util/FileUtil.h @@ -134,18 +134,7 @@ public: // THIS FUNCTION IS NOT DESIGNED FOR MULTI-THREADED USAGE static IFileUtil::ECopyTreeResult MoveTree(const QString& strSourceDirectory, const QString& strTargetDirectory, bool boRecurse = true, bool boConfirmOverwrite = false); - // Show Popup Menu with file commands include Source Control commands - // filename: a name of file without path - // fullGamePath: a game path to folder like "/Game/Objects" without filename - // wnd: pointer to window class, can be nullptr - // isSelected: output value indicated if Select menu item was chosen, if pointer is 0 - no Select menu item. - // pItems: you can specify additional menu items and get the result of selection using this parameter. - // return false if source control operation failed - static QString PopupQMenu(const QString& filename, const QString& fullGamePath, QWidget* parent); - static QString PopupQMenu(const QString& filename, const QString& fullGamePath, QWidget* parent, bool* pIsSelected, const QStringList& extraItemsFront); - static QString PopupQMenu(const QString& filename, const QString& fullGamePath, QWidget* parent, bool* pIsSelected, const QStringList& extraItemsFront, const QStringList& extraItemsBack); - - static void PopulateQMenu(QWidget* caller, QMenu* menu, const QString& filename, const QString& fullGamePath); + static void PopulateQMenu(QWidget* caller, QMenu* menu, const AZStd::string& fullGamePath); static void GatherAssetFilenamesFromLevel(std::set& rOutFilenames, bool bMakeLowerCase = false, bool bMakeUnixPath = false); @@ -172,7 +161,7 @@ private: static bool s_multiFileDlgPref[IFileUtil::EFILE_TYPE_LAST]; // Keep this variant of this method private! pIsSelected is captured in a lambda, and so requires menu use exec() and never use show() - static void PopulateQMenu(QWidget* caller, QMenu* menu, const QString& filename, const QString& fullGamePath, bool* pIsSelected); + static void PopulateQMenu(QWidget* caller, QMenu* menu, const AZStd::string& fullGamePath, bool* pIsSelected); static bool ExtractDccFilenameFromAssetDatabase(const QString& assetFilename, QString& dccFilename); static bool ExtractDccFilenameUsingNamingConventions(const QString& assetFilename, QString& dccFilename); From eeb1b68a725112dc0ac0598e14ed32be404eed66 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Fri, 6 Aug 2021 09:23:33 -0500 Subject: [PATCH 28/38] Updated string to string_view per PR feedback. Signed-off-by: Chris Galvan --- Code/Editor/Util/FileUtil.cpp | 4 ++-- Code/Editor/Util/FileUtil.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Code/Editor/Util/FileUtil.cpp b/Code/Editor/Util/FileUtil.cpp index a270a830f2..5356e4abc2 100644 --- a/Code/Editor/Util/FileUtil.cpp +++ b/Code/Editor/Util/FileUtil.cpp @@ -1900,12 +1900,12 @@ IFileUtil::ECopyTreeResult CFileUtil::MoveTree(const QString& strSourceDirecto return eCopyResult; } -void CFileUtil::PopulateQMenu(QWidget* caller, QMenu* menu, const AZStd::string& fullGamePath) +void CFileUtil::PopulateQMenu(QWidget* caller, QMenu* menu, AZStd::string_view fullGamePath) { PopulateQMenu(caller, menu, fullGamePath, nullptr); } -void CFileUtil::PopulateQMenu(QWidget* caller, QMenu* menu, const AZStd::string& fullGamePath, bool* isSelected) +void CFileUtil::PopulateQMenu(QWidget* caller, QMenu* menu, AZStd::string_view fullGamePath, bool* isSelected) { // Normalize the full path so we get consistent separators AZStd::string fullFilePath(fullGamePath); diff --git a/Code/Editor/Util/FileUtil.h b/Code/Editor/Util/FileUtil.h index 599e0f0b2a..1d907e5baf 100644 --- a/Code/Editor/Util/FileUtil.h +++ b/Code/Editor/Util/FileUtil.h @@ -134,7 +134,7 @@ public: // THIS FUNCTION IS NOT DESIGNED FOR MULTI-THREADED USAGE static IFileUtil::ECopyTreeResult MoveTree(const QString& strSourceDirectory, const QString& strTargetDirectory, bool boRecurse = true, bool boConfirmOverwrite = false); - static void PopulateQMenu(QWidget* caller, QMenu* menu, const AZStd::string& fullGamePath); + static void PopulateQMenu(QWidget* caller, QMenu* menu, AZStd::string_view fullGamePath); static void GatherAssetFilenamesFromLevel(std::set& rOutFilenames, bool bMakeLowerCase = false, bool bMakeUnixPath = false); @@ -161,7 +161,7 @@ private: static bool s_multiFileDlgPref[IFileUtil::EFILE_TYPE_LAST]; // Keep this variant of this method private! pIsSelected is captured in a lambda, and so requires menu use exec() and never use show() - static void PopulateQMenu(QWidget* caller, QMenu* menu, const AZStd::string& fullGamePath, bool* pIsSelected); + static void PopulateQMenu(QWidget* caller, QMenu* menu, AZStd::string_view fullGamePath, bool* pIsSelected); static bool ExtractDccFilenameFromAssetDatabase(const QString& assetFilename, QString& dccFilename); static bool ExtractDccFilenameUsingNamingConventions(const QString& assetFilename, QString& dccFilename); From 2c7f6f9742f705129db68445f8ed6dc4c5681440 Mon Sep 17 00:00:00 2001 From: AMZN-nggieber <52797929+AMZN-nggieber@users.noreply.github.com> Date: Fri, 6 Aug 2021 08:19:35 -0700 Subject: [PATCH 29/38] Adds Links to Gem Directory and Documentation for Gems (#2922) * Changed blue text to white that is not meant as a link, made 'View in Director' link work for gems in the inspector, added parsing for gem documentation link Signed-off-by: nggieber * Added documentation links for gems, changed markup for urls in summaries and requirements so they are clickable Signed-off-by: nggieber * Fixed a couple of the documentation links Signed-off-by: nggieber * Added documentation url to edit gem properties script and updated unit tests Signed-off-by: nggieber --- .../Source/GemCatalog/GemInspector.cpp | 6 ++++- .../Source/GemCatalog/GemInspector.h | 1 - .../Source/GemCatalog/GemItemDelegate.cpp | 1 - .../ProjectManager/Source/PythonBindings.cpp | 2 ++ Gems/AWSClientAuth/gem.json | 3 ++- Gems/AWSCore/gem.json | 3 ++- Gems/AWSGameLift/gem.json | 3 ++- Gems/AWSMetrics/gem.json | 3 ++- Gems/Achievements/gem.json | 3 ++- Gems/AssetMemoryAnalyzer/gem.json | 3 ++- Gems/AssetValidation/gem.json | 3 ++- Gems/Atom/gem.json | 3 ++- Gems/AtomContent/gem.json | 5 ++-- Gems/AtomLyIntegration/gem.json | 3 ++- Gems/AtomTressFX/gem.json | 3 ++- Gems/AudioEngineWwise/gem.json | 3 ++- Gems/AudioSystem/gem.json | 3 ++- Gems/Blast/gem.json | 3 ++- Gems/Camera/gem.json | 3 ++- Gems/CameraFramework/gem.json | 3 ++- Gems/CertificateManager/gem.json | 3 ++- Gems/CrashReporting/gem.json | 3 ++- Gems/CustomAssetExample/gem.json | 3 ++- Gems/DebugDraw/gem.json | 3 ++- Gems/DevTextures/gem.json | 3 ++- Gems/EMotionFX/gem.json | 3 ++- Gems/EditorPythonBindings/gem.json | 21 +++++++++-------- Gems/ExpressionEvaluation/gem.json | 3 ++- Gems/FastNoise/gem.json | 3 ++- Gems/GameState/gem.json | 3 ++- Gems/GameStateSamples/gem.json | 3 ++- Gems/Gestures/gem.json | 3 ++- Gems/GradientSignal/gem.json | 3 ++- Gems/GraphCanvas/gem.json | 3 ++- Gems/GraphModel/gem.json | 3 ++- Gems/HttpRequestor/gem.json | 3 ++- Gems/ImGui/gem.json | 3 ++- Gems/InAppPurchases/gem.json | 3 ++- Gems/LandscapeCanvas/gem.json | 3 ++- Gems/LmbrCentral/gem.json | 3 ++- Gems/LocalUser/gem.json | 3 ++- Gems/LyShine/gem.json | 3 ++- Gems/LyShineExamples/gem.json | 3 ++- Gems/Maestro/gem.json | 3 ++- Gems/MessagePopup/gem.json | 3 ++- Gems/Metastream/gem.json | 3 ++- Gems/Microphone/gem.json | 3 ++- Gems/MultiplayerCompression/gem.json | 3 ++- Gems/NvCloth/gem.json | 3 ++- Gems/PBSreferenceMaterials/gem.json | 3 ++- Gems/PhysX/gem.json | 3 ++- Gems/PhysXDebug/gem.json | 3 ++- Gems/PhysXSamples/gem.json | 3 ++- Gems/Prefab/PrefabBuilder/gem.json | 3 ++- Gems/Presence/gem.json | 3 ++- Gems/PrimitiveAssets/gem.json | 3 ++- Gems/PythonAssetBuilder/gem.json | 21 +++++++++-------- Gems/QtForPython/gem.json | 5 ++-- Gems/RADTelemetry/gem.json | 3 ++- Gems/SaveData/gem.json | 5 ++-- Gems/SceneLoggingExample/gem.json | 5 ++-- Gems/SceneProcessing/gem.json | 5 ++-- Gems/ScriptCanvas/gem.json | 21 +++++++++-------- Gems/ScriptCanvasDeveloper/gem.json | 5 ++-- Gems/ScriptCanvasPhysics/gem.json | 5 ++-- Gems/ScriptCanvasTesting/gem.json | 21 +++++++++-------- Gems/ScriptEvents/gem.json | 21 +++++++++-------- Gems/ScriptedEntityTweener/gem.json | 5 ++-- Gems/StartingPointCamera/gem.json | 3 ++- Gems/StartingPointInput/gem.json | 3 ++- Gems/StartingPointMovement/gem.json | 3 ++- Gems/SurfaceData/gem.json | 3 ++- Gems/TestAssetBuilder/gem.json | 3 ++- Gems/TextureAtlas/gem.json | 3 ++- Gems/TickBusOrderViewer/gem.json | 3 ++- Gems/Twitch/gem.json | 3 ++- Gems/UiBasics/gem.json | 3 ++- Gems/Vegetation/gem.json | 3 ++- Gems/VideoPlaybackFramework/gem.json | 3 ++- Gems/VirtualGamepad/gem.json | 3 ++- Gems/WhiteBox/gem.json | 3 ++- scripts/o3de/o3de/gem_properties.py | 18 ++++++++++----- .../o3de/tests/unit_test_gem_properties.py | 23 +++++++++++-------- 83 files changed, 240 insertions(+), 148 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp index ec4a80e175..6c1f5f6fec 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp @@ -108,7 +108,7 @@ namespace O3DE::ProjectManager { // Gem name, creator and summary m_nameLabel = CreateStyledLabel(m_mainLayout, 18, s_headerColor); - m_creatorLabel = CreateStyledLabel(m_mainLayout, 12, s_creatorColor); + m_creatorLabel = CreateStyledLabel(m_mainLayout, 12, s_headerColor); m_mainLayout->addSpacing(5); // TODO: QLabel seems to have issues determining the right sizeHint() for our font with the given font size. @@ -116,6 +116,8 @@ namespace O3DE::ProjectManager m_summaryLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); m_mainLayout->addWidget(m_summaryLabel); m_summaryLabel->setWordWrap(true); + m_summaryLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + m_summaryLabel->setOpenExternalLinks(true); m_mainLayout->addSpacing(5); // Directory and documentation links @@ -161,6 +163,8 @@ namespace O3DE::ProjectManager m_reqirementsTextLabel = GemInspector::CreateStyledLabel(requrementsLayout, 10, s_textColor); m_reqirementsTextLabel->setWordWrap(true); + m_reqirementsTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + m_reqirementsTextLabel->setOpenExternalLinks(true); QSpacerItem* reqirementsSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding); requrementsLayout->addSpacerItem(reqirementsSpacer); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h index 7d80cb0905..97c23f7df2 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h @@ -38,7 +38,6 @@ namespace O3DE::ProjectManager // Colors inline constexpr static const char* s_headerColor = "#FFFFFF"; inline constexpr static const char* s_textColor = "#DDDDDD"; - inline constexpr static const char* s_creatorColor = "#94D2FF"; private slots: void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp index 8f5cfd24aa..d5a213e80f 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -95,7 +95,6 @@ namespace O3DE::ProjectManager gemCreatorRect.moveTo(contentRect.left(), contentRect.top() + gemNameRect.height()); painter->setFont(standardFont); - painter->setPen(m_linkColor); gemCreatorRect = painter->boundingRect(gemCreatorRect, Qt::TextSingleLine, gemCreator); painter->drawText(gemCreatorRect, Qt::TextSingleLine, gemCreator); diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 5d118654bc..8bdfb0f152 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -646,6 +646,7 @@ namespace O3DE::ProjectManager { GemInfo gemInfo; gemInfo.m_path = Py_To_String(path); + gemInfo.m_directoryLink = gemInfo.m_path; auto data = m_manifest.attr("get_gem_json_data")(pybind11::none(), path, pyProjectPath); if (pybind11::isinstance(data)) @@ -661,6 +662,7 @@ namespace O3DE::ProjectManager gemInfo.m_version = ""; gemInfo.m_requirement = Py_To_String_Optional(data, "requirements", ""); gemInfo.m_creator = Py_To_String_Optional(data, "origin", ""); + gemInfo.m_documentationLink = Py_To_String_Optional(data, "documentation_url", ""); if (gemInfo.m_creator.contains("Open 3D Engine")) { diff --git a/Gems/AWSClientAuth/gem.json b/Gems/AWSClientAuth/gem.json index 76183cd785..42996e7f4f 100644 --- a/Gems/AWSClientAuth/gem.json +++ b/Gems/AWSClientAuth/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["AWS", "Network", "SDK"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/" } diff --git a/Gems/AWSCore/gem.json b/Gems/AWSCore/gem.json index bbaa4eb155..4c7889ace4 100644 --- a/Gems/AWSCore/gem.json +++ b/Gems/AWSCore/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["AWS", "Network", "SDK"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-core/" } diff --git a/Gems/AWSGameLift/gem.json b/Gems/AWSGameLift/gem.json index 1a19c82f0b..7711495ee7 100644 --- a/Gems/AWSGameLift/gem.json +++ b/Gems/AWSGameLift/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["AWS", "Framework", "Network"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/" } diff --git a/Gems/AWSMetrics/gem.json b/Gems/AWSMetrics/gem.json index 480200c565..5faeb66787 100644 --- a/Gems/AWSMetrics/gem.json +++ b/Gems/AWSMetrics/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["AWS", "Network", "SDK"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/" } diff --git a/Gems/Achievements/gem.json b/Gems/Achievements/gem.json index 8a30390295..ca2bc7f3e9 100644 --- a/Gems/Achievements/gem.json +++ b/Gems/Achievements/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Gameplay", "Achievements"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/achievements/" } diff --git a/Gems/AssetMemoryAnalyzer/gem.json b/Gems/AssetMemoryAnalyzer/gem.json index 45610e417c..544e0f7948 100644 --- a/Gems/AssetMemoryAnalyzer/gem.json +++ b/Gems/AssetMemoryAnalyzer/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Debug", "Utility", "Tools"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/asset-memory-analyzer/" } diff --git a/Gems/AssetValidation/gem.json b/Gems/AssetValidation/gem.json index 8a00dc27ca..83e37ddaf5 100644 --- a/Gems/AssetValidation/gem.json +++ b/Gems/AssetValidation/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Assets", "Utility", "Scripting"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/asset-validation/" } diff --git a/Gems/Atom/gem.json b/Gems/Atom/gem.json index c2aa033bf7..f927e42e7e 100644 --- a/Gems/Atom/gem.json +++ b/Gems/Atom/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Rendering", "Core"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom/" } diff --git a/Gems/AtomContent/gem.json b/Gems/AtomContent/gem.json index b1bc35b77b..8161635f43 100644 --- a/Gems/AtomContent/gem.json +++ b/Gems/AtomContent/gem.json @@ -4,9 +4,10 @@ "license": "Apache-2.0 Or MIT", "origin": "Open 3D Engine - o3de.org", "type": "Asset", - "summary": "The Atom Content Gem provides assets for Atom Renderer and a modified version of the Pixar Look Development Studio (https://renderman.pixar.com/look-development-studio).", + "summary": "The Atom Content Gem provides assets for Atom Renderer and a modified version of the Pixar Look Development Studio.", "canonical_tags": ["Gem"], "user_tags": ["Rendering", "Assets", "Tools"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-content/" } diff --git a/Gems/AtomLyIntegration/gem.json b/Gems/AtomLyIntegration/gem.json index 72be2f9151..28faf364db 100644 --- a/Gems/AtomLyIntegration/gem.json +++ b/Gems/AtomLyIntegration/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Rendering", "Core", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-ly-integration/" } diff --git a/Gems/AtomTressFX/gem.json b/Gems/AtomTressFX/gem.json index 0099906dd1..5f2e25b8a9 100644 --- a/Gems/AtomTressFX/gem.json +++ b/Gems/AtomTressFX/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Rendering", "Physics", "Animation"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/amd/atom-tressfx/" } diff --git a/Gems/AudioEngineWwise/gem.json b/Gems/AudioEngineWwise/gem.json index 699ed8419a..084c924977 100644 --- a/Gems/AudioEngineWwise/gem.json +++ b/Gems/AudioEngineWwise/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Audio", "Utility", "Tools"], "icon_path": "preview.png", - "requirements": "Users will need to download Wwise from the Audiokinetic web site: https://www.audiokinetic.com/download/" + "requirements": "Users will need to download Wwise from the Audiokinetic Web Site.", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/wwise/audio-engine-wwise/" } diff --git a/Gems/AudioSystem/gem.json b/Gems/AudioSystem/gem.json index a9058cb42e..f618103803 100644 --- a/Gems/AudioSystem/gem.json +++ b/Gems/AudioSystem/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Audio", "Utility", "Tools"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/audio-system/" } diff --git a/Gems/Blast/gem.json b/Gems/Blast/gem.json index a4eb9d6b31..3b1205d6fe 100644 --- a/Gems/Blast/gem.json +++ b/Gems/Blast/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Physics", "Simulation", "Animation"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-blast/" } diff --git a/Gems/Camera/gem.json b/Gems/Camera/gem.json index f59bb8cc0d..2fc2ea8355 100644 --- a/Gems/Camera/gem.json +++ b/Gems/Camera/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Rendering", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera/" } diff --git a/Gems/CameraFramework/gem.json b/Gems/CameraFramework/gem.json index 6a6cbaf1f0..2ab25a84b4 100644 --- a/Gems/CameraFramework/gem.json +++ b/Gems/CameraFramework/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Rendering", "Framework", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera-framework/" } diff --git a/Gems/CertificateManager/gem.json b/Gems/CertificateManager/gem.json index 38005a7cd1..cb49abc6b6 100644 --- a/Gems/CertificateManager/gem.json +++ b/Gems/CertificateManager/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Network", "Framework"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/certificate-manager/" } diff --git a/Gems/CrashReporting/gem.json b/Gems/CrashReporting/gem.json index e1e06f0ac1..bff54dcf36 100644 --- a/Gems/CrashReporting/gem.json +++ b/Gems/CrashReporting/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Debug", "Framework"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/crash-reporting/" } diff --git a/Gems/CustomAssetExample/gem.json b/Gems/CustomAssetExample/gem.json index 62bdcb1ff6..ac7c2788d7 100644 --- a/Gems/CustomAssetExample/gem.json +++ b/Gems/CustomAssetExample/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Assets", "Tools"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/custom-asset-example/" } diff --git a/Gems/DebugDraw/gem.json b/Gems/DebugDraw/gem.json index 0dd2e179f3..a7939f4077 100644 --- a/Gems/DebugDraw/gem.json +++ b/Gems/DebugDraw/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Debug", "Tools", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/debug-draw/" } diff --git a/Gems/DevTextures/gem.json b/Gems/DevTextures/gem.json index 7a27d7c776..81d14e78cb 100644 --- a/Gems/DevTextures/gem.json +++ b/Gems/DevTextures/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Assets", "Debug", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/dev-textures/" } diff --git a/Gems/EMotionFX/gem.json b/Gems/EMotionFX/gem.json index 74a060ed52..f00803ec2c 100644 --- a/Gems/EMotionFX/gem.json +++ b/Gems/EMotionFX/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Animation", "Tools", "Simulation"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/emotionfx/" } diff --git a/Gems/EditorPythonBindings/gem.json b/Gems/EditorPythonBindings/gem.json index a8ae59b22e..fae091a63f 100644 --- a/Gems/EditorPythonBindings/gem.json +++ b/Gems/EditorPythonBindings/gem.json @@ -1,12 +1,13 @@ { - "gem_name": "EditorPythonBindings", - "display_name": "Editor Python Bindings", - "license": "Apache-2.0 Or MIT", - "origin": "Open 3D Engine - o3de.org", - "type": "Tool", - "summary": "The Editor Python Bindings Gem provides Python commands for Open 3D Engine Editor functions.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Utility"], - "icon_path": "preview.png", - "requirements": "" + "gem_name": "EditorPythonBindings", + "display_name": "Editor Python Bindings", + "license": "Apache-2.0 Or MIT", + "origin": "Open 3D Engine - o3de.org", + "type": "Tool", + "summary": "The Editor Python Bindings Gem provides Python commands for Open 3D Engine Editor functions.", + "canonical_tags": ["Gem"], + "user_tags": ["Scripting", "Utility"], + "icon_path": "preview.png", + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/editor-python-bindings/" } diff --git a/Gems/ExpressionEvaluation/gem.json b/Gems/ExpressionEvaluation/gem.json index 92d5963891..2f555916a5 100644 --- a/Gems/ExpressionEvaluation/gem.json +++ b/Gems/ExpressionEvaluation/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Scripting", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/expression-evaluation/" } diff --git a/Gems/FastNoise/gem.json b/Gems/FastNoise/gem.json index f49db2aa02..5ee5bbaf89 100644 --- a/Gems/FastNoise/gem.json +++ b/Gems/FastNoise/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Utility", "Tools", "Design"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/fast-noise/" } diff --git a/Gems/GameState/gem.json b/Gems/GameState/gem.json index fe9569eb75..a1f272c290 100644 --- a/Gems/GameState/gem.json +++ b/Gems/GameState/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Gameplay", "Framework", "Scripting"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state/" } diff --git a/Gems/GameStateSamples/gem.json b/Gems/GameStateSamples/gem.json index 0241a8a1b7..f0dd82c5fa 100644 --- a/Gems/GameStateSamples/gem.json +++ b/Gems/GameStateSamples/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Gameplay", "Sample", "Assets"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state-samples/" } diff --git a/Gems/Gestures/gem.json b/Gems/Gestures/gem.json index 4c2f5fe67a..4412e58c6d 100644 --- a/Gems/Gestures/gem.json +++ b/Gems/Gestures/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Input", "Gameplay", "Scripting"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/gestures/" } diff --git a/Gems/GradientSignal/gem.json b/Gems/GradientSignal/gem.json index cae1a272b1..67afb1f819 100644 --- a/Gems/GradientSignal/gem.json +++ b/Gems/GradientSignal/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Utility", "Tools", "Design"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/gradient-signal/" } diff --git a/Gems/GraphCanvas/gem.json b/Gems/GraphCanvas/gem.json index 6884f6b9cb..ab1e7ac77a 100644 --- a/Gems/GraphCanvas/gem.json +++ b/Gems/GraphCanvas/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Framework", "Tools", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-canvas/" } diff --git a/Gems/GraphModel/gem.json b/Gems/GraphModel/gem.json index 52c914357d..62f1effb7c 100644 --- a/Gems/GraphModel/gem.json +++ b/Gems/GraphModel/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Framework", "Tools", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-model/" } diff --git a/Gems/HttpRequestor/gem.json b/Gems/HttpRequestor/gem.json index 8beb77d839..e5eb8d6f44 100644 --- a/Gems/HttpRequestor/gem.json +++ b/Gems/HttpRequestor/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Network", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/http-requestor/" } diff --git a/Gems/ImGui/gem.json b/Gems/ImGui/gem.json index 327e417bcf..deaac9925a 100644 --- a/Gems/ImGui/gem.json +++ b/Gems/ImGui/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Debug", "Rendering", "Framework"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/imgui/" } diff --git a/Gems/InAppPurchases/gem.json b/Gems/InAppPurchases/gem.json index 96d0e7fa0a..ab43075896 100644 --- a/Gems/InAppPurchases/gem.json +++ b/Gems/InAppPurchases/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["SDK", "Network"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/sdk/in-app-purchases/" } diff --git a/Gems/LandscapeCanvas/gem.json b/Gems/LandscapeCanvas/gem.json index 338845ebb1..63a80ec57e 100644 --- a/Gems/LandscapeCanvas/gem.json +++ b/Gems/LandscapeCanvas/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Environment", "Design", "Tools"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/landscape-canvas/" } diff --git a/Gems/LmbrCentral/gem.json b/Gems/LmbrCentral/gem.json index 6f0530c35d..b6442bfd06 100644 --- a/Gems/LmbrCentral/gem.json +++ b/Gems/LmbrCentral/gem.json @@ -8,6 +8,7 @@ "canonical_tags": ["Gem"], "user_tags": ["Core", "Framework", "Assets"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/core/lmbr-central/" } diff --git a/Gems/LocalUser/gem.json b/Gems/LocalUser/gem.json index 47c1601240..dcf4d44abb 100644 --- a/Gems/LocalUser/gem.json +++ b/Gems/LocalUser/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Input", "Gameplay", "Scripting"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/local-user/" } diff --git a/Gems/LyShine/gem.json b/Gems/LyShine/gem.json index c61ca0cce5..e0fdb860e5 100644 --- a/Gems/LyShine/gem.json +++ b/Gems/LyShine/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["UI", "Tools", "Framework"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine/" } diff --git a/Gems/LyShineExamples/gem.json b/Gems/LyShineExamples/gem.json index e8c7110466..a411bfb363 100644 --- a/Gems/LyShineExamples/gem.json +++ b/Gems/LyShineExamples/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["UI", "Sample", "Assets"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine-examples/" } diff --git a/Gems/Maestro/gem.json b/Gems/Maestro/gem.json index 4f638b63f1..6ee989aa55 100644 --- a/Gems/Maestro/gem.json +++ b/Gems/Maestro/gem.json @@ -8,6 +8,7 @@ "canonical_tags": ["Gem"], "user_tags": ["Animation", "Tools", "Scripting"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/maestro/" } diff --git a/Gems/MessagePopup/gem.json b/Gems/MessagePopup/gem.json index c75d28f50d..2560b8869a 100644 --- a/Gems/MessagePopup/gem.json +++ b/Gems/MessagePopup/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Gameplay", "Sample"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/message-popup/" } diff --git a/Gems/Metastream/gem.json b/Gems/Metastream/gem.json index 911c6be665..429d174cf5 100644 --- a/Gems/Metastream/gem.json +++ b/Gems/Metastream/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Gameplay", "Network", "Framework"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/metastream/" } diff --git a/Gems/Microphone/gem.json b/Gems/Microphone/gem.json index 31b3d46963..cc47cbcd7e 100644 --- a/Gems/Microphone/gem.json +++ b/Gems/Microphone/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Audio", "Input"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/microphone/" } diff --git a/Gems/MultiplayerCompression/gem.json b/Gems/MultiplayerCompression/gem.json index aeea2282cc..178cca0ffe 100644 --- a/Gems/MultiplayerCompression/gem.json +++ b/Gems/MultiplayerCompression/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Multiplayer", "Network", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/multiplayer/multiplayer-compression/" } diff --git a/Gems/NvCloth/gem.json b/Gems/NvCloth/gem.json index 95ea8168d6..49ccbf32f1 100644 --- a/Gems/NvCloth/gem.json +++ b/Gems/NvCloth/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Physics", "Simulation", "SDK"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-cloth/" } diff --git a/Gems/PBSreferenceMaterials/gem.json b/Gems/PBSreferenceMaterials/gem.json index b015d89888..563dd31a38 100644 --- a/Gems/PBSreferenceMaterials/gem.json +++ b/Gems/PBSreferenceMaterials/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Rendering", "Sample", "Assets"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/pbs-reference-materials/" } diff --git a/Gems/PhysX/gem.json b/Gems/PhysX/gem.json index 0b6b635c71..0fbd3e44f9 100644 --- a/Gems/PhysX/gem.json +++ b/Gems/PhysX/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Physics", "Simulation", "SDK"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx/" } diff --git a/Gems/PhysXDebug/gem.json b/Gems/PhysXDebug/gem.json index 7dbfc1a096..f7877cabdd 100644 --- a/Gems/PhysXDebug/gem.json +++ b/Gems/PhysXDebug/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Physics", "Simulation", "Debug"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-debug/" } diff --git a/Gems/PhysXSamples/gem.json b/Gems/PhysXSamples/gem.json index b5239344f6..e48dc6991b 100644 --- a/Gems/PhysXSamples/gem.json +++ b/Gems/PhysXSamples/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Physics", "Simulation", "Sample"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-samples/" } diff --git a/Gems/Prefab/PrefabBuilder/gem.json b/Gems/Prefab/PrefabBuilder/gem.json index 1a815738ca..ad6062ae3b 100644 --- a/Gems/Prefab/PrefabBuilder/gem.json +++ b/Gems/Prefab/PrefabBuilder/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Assets", "Utility", "Core"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/prefab/" } diff --git a/Gems/Presence/gem.json b/Gems/Presence/gem.json index 5df5fbdd6a..49d937946d 100644 --- a/Gems/Presence/gem.json +++ b/Gems/Presence/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Network", "Gameplay", "Framework"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/presence/" } diff --git a/Gems/PrimitiveAssets/gem.json b/Gems/PrimitiveAssets/gem.json index 1c1fd54c16..d1789d46fe 100644 --- a/Gems/PrimitiveAssets/gem.json +++ b/Gems/PrimitiveAssets/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Assets", "Sample", "Debug"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/primitive-assets/" } diff --git a/Gems/PythonAssetBuilder/gem.json b/Gems/PythonAssetBuilder/gem.json index dcf3b59e23..1a76d7d2c2 100644 --- a/Gems/PythonAssetBuilder/gem.json +++ b/Gems/PythonAssetBuilder/gem.json @@ -1,12 +1,13 @@ { - "gem_name": "PythonAssetBuilder", - "display_name": "Python Asset Builder", - "license": "Apache-2.0 Or MIT", - "origin": "Open 3D Engine - o3de.org", - "type": "Code", - "summary": "The Python Asset Builder Gem provides functionality to implement custom asset builders in Python for Asset Processor.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Assets", "Utility"], - "icon_path": "preview.png", - "requirements": "" + "gem_name": "PythonAssetBuilder", + "display_name": "Python Asset Builder", + "license": "Apache-2.0 Or MIT", + "origin": "Open 3D Engine - o3de.org", + "type": "Code", + "summary": "The Python Asset Builder Gem provides functionality to implement custom asset builders in Python for Asset Processor.", + "canonical_tags": ["Gem"], + "user_tags": ["Scripting", "Assets", "Utility"], + "icon_path": "preview.png", + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/python-asset-builder/" } diff --git a/Gems/QtForPython/gem.json b/Gems/QtForPython/gem.json index dea5fc2e16..1519d46c52 100644 --- a/Gems/QtForPython/gem.json +++ b/Gems/QtForPython/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Scripting", "UI", "Framework"], "icon_path": "preview.png", - "requirements": "" - } + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/qt-for-python/" +} diff --git a/Gems/RADTelemetry/gem.json b/Gems/RADTelemetry/gem.json index 43515976da..932093b4a9 100644 --- a/Gems/RADTelemetry/gem.json +++ b/Gems/RADTelemetry/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Debug", "SDK"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/rad/rad-telemetry/" } diff --git a/Gems/SaveData/gem.json b/Gems/SaveData/gem.json index 57de27624f..3892cc80c2 100644 --- a/Gems/SaveData/gem.json +++ b/Gems/SaveData/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Utility", "Gameplay"], "icon_path": "preview.png", - "requirements": "" - } + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/save-data/" +} diff --git a/Gems/SceneLoggingExample/gem.json b/Gems/SceneLoggingExample/gem.json index faa2860d13..ff7def1b32 100644 --- a/Gems/SceneLoggingExample/gem.json +++ b/Gems/SceneLoggingExample/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Debug", "Sample"], "icon_path": "preview.png", - "requirements": "" - } + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/scene-logging-example/" +} diff --git a/Gems/SceneProcessing/gem.json b/Gems/SceneProcessing/gem.json index d9814f6a7d..a6c2cefbd6 100644 --- a/Gems/SceneProcessing/gem.json +++ b/Gems/SceneProcessing/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Assets", "Tools", "Core"], "icon_path": "preview.png", - "requirements": "" - } + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/scene-processing/" +} diff --git a/Gems/ScriptCanvas/gem.json b/Gems/ScriptCanvas/gem.json index 413e58ef7e..b6b5522d7d 100644 --- a/Gems/ScriptCanvas/gem.json +++ b/Gems/ScriptCanvas/gem.json @@ -1,12 +1,13 @@ { - "gem_name": "ScriptCanvas", - "display_name": "Script Canvas", - "license": "Apache-2.0 Or MIT", - "origin": "Open 3D Engine - o3de.org", - "type": "Tool", - "summary": "The Script Canvas Gem provides Open 3D Engine's visual scripting environment, Script Canvas.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Tools", "Utility"], - "icon_path": "preview.png", - "requirements": "" + "gem_name": "ScriptCanvas", + "display_name": "Script Canvas", + "license": "Apache-2.0 Or MIT", + "origin": "Open 3D Engine - o3de.org", + "type": "Tool", + "summary": "The Script Canvas Gem provides Open 3D Engine's visual scripting environment, Script Canvas.", + "canonical_tags": ["Gem"], + "user_tags": ["Scripting", "Tools", "Utility"], + "icon_path": "preview.png", + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas/" } diff --git a/Gems/ScriptCanvasDeveloper/gem.json b/Gems/ScriptCanvasDeveloper/gem.json index 7cc2e7d03a..d7b0aefad3 100644 --- a/Gems/ScriptCanvasDeveloper/gem.json +++ b/Gems/ScriptCanvasDeveloper/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Scripting", "Utility", "Debug"], "icon_path": "preview.png", - "requirements": "" - } + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-developer/" +} diff --git a/Gems/ScriptCanvasPhysics/gem.json b/Gems/ScriptCanvasPhysics/gem.json index 04cfb86980..46e2ef1194 100644 --- a/Gems/ScriptCanvasPhysics/gem.json +++ b/Gems/ScriptCanvasPhysics/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Scripting", "Physics", "Simulation"], "icon_path": "preview.png", - "requirements": "" - } + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-physics/" +} diff --git a/Gems/ScriptCanvasTesting/gem.json b/Gems/ScriptCanvasTesting/gem.json index d82e841236..303eab8c7e 100644 --- a/Gems/ScriptCanvasTesting/gem.json +++ b/Gems/ScriptCanvasTesting/gem.json @@ -1,12 +1,13 @@ { - "gem_name": "ScriptCanvasTesting", - "display_name": "Script Canvas Testing", - "license": "Apache-2.0 Or MIT", - "origin": "Open 3D Engine - o3de.org", - "type": "Tool", - "summary": "The Script Canvas Testing Gem provides a framework for testing for and with Script Canvas.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Debug", "Framework"], - "icon_path": "preview.png", - "requirements": "" + "gem_name": "ScriptCanvasTesting", + "display_name": "Script Canvas Testing", + "license": "Apache-2.0 Or MIT", + "origin": "Open 3D Engine - o3de.org", + "type": "Tool", + "summary": "The Script Canvas Testing Gem provides a framework for testing for and with Script Canvas.", + "canonical_tags": ["Gem"], + "user_tags": ["Scripting", "Debug", "Framework"], + "icon_path": "preview.png", + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-testing/" } diff --git a/Gems/ScriptEvents/gem.json b/Gems/ScriptEvents/gem.json index fba99c6af9..2b7d74ef3f 100644 --- a/Gems/ScriptEvents/gem.json +++ b/Gems/ScriptEvents/gem.json @@ -1,12 +1,13 @@ { - "gem_name": "ScriptEvents", - "display_name": "Script Events", - "license": "Apache-2.0 Or MIT", - "origin": "Open 3D Engine - o3de.org", - "type": "Code", - "summary": "The Script Events Gem provides a framework for creating event assets usable from any scripting solution in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Framework", "Gameplay"], - "icon_path": "preview.png", - "requirements": "" + "gem_name": "ScriptEvents", + "display_name": "Script Events", + "license": "Apache-2.0 Or MIT", + "origin": "Open 3D Engine - o3de.org", + "type": "Code", + "summary": "The Script Events Gem provides a framework for creating event assets usable from any scripting solution in Open 3D Engine.", + "canonical_tags": ["Gem"], + "user_tags": ["Scripting", "Framework", "Gameplay"], + "icon_path": "preview.png", + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-events/" } diff --git a/Gems/ScriptedEntityTweener/gem.json b/Gems/ScriptedEntityTweener/gem.json index c2e1975f76..a0f558356b 100644 --- a/Gems/ScriptedEntityTweener/gem.json +++ b/Gems/ScriptedEntityTweener/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Scripting", "UI", "Animation"], "icon_path": "preview.png", - "requirements": "" - } + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/scripted-entity-tweener/" +} diff --git a/Gems/StartingPointCamera/gem.json b/Gems/StartingPointCamera/gem.json index 2c28f346a9..5a9aa0df9b 100644 --- a/Gems/StartingPointCamera/gem.json +++ b/Gems/StartingPointCamera/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Rendering", "Gameplay", "Scripting"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/starting-point-camera/" } diff --git a/Gems/StartingPointInput/gem.json b/Gems/StartingPointInput/gem.json index 2d5b6aee15..e65f271fe5 100644 --- a/Gems/StartingPointInput/gem.json +++ b/Gems/StartingPointInput/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Input", "Gameplay", "Scripting"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-input/" } diff --git a/Gems/StartingPointMovement/gem.json b/Gems/StartingPointMovement/gem.json index cb4e8d0e6a..70e1d105c1 100644 --- a/Gems/StartingPointMovement/gem.json +++ b/Gems/StartingPointMovement/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Input", "Gameplay", "Scripting"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-movement/" } diff --git a/Gems/SurfaceData/gem.json b/Gems/SurfaceData/gem.json index 24f7f1cb21..57e4d8fb70 100644 --- a/Gems/SurfaceData/gem.json +++ b/Gems/SurfaceData/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Environment", "Utility", "Design"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/surface-data/" } diff --git a/Gems/TestAssetBuilder/gem.json b/Gems/TestAssetBuilder/gem.json index 4c75343ebb..1e0b84894a 100644 --- a/Gems/TestAssetBuilder/gem.json +++ b/Gems/TestAssetBuilder/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Assets", "Debug", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/test-asset-builder/" } diff --git a/Gems/TextureAtlas/gem.json b/Gems/TextureAtlas/gem.json index 1991ad387e..832dee0e62 100644 --- a/Gems/TextureAtlas/gem.json +++ b/Gems/TextureAtlas/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Rendering", "Assets", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/texture-atlas/" } diff --git a/Gems/TickBusOrderViewer/gem.json b/Gems/TickBusOrderViewer/gem.json index 787fd24abb..5ea936960a 100644 --- a/Gems/TickBusOrderViewer/gem.json +++ b/Gems/TickBusOrderViewer/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Gameplay", "Simulation", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/tick-bus-order-viewer/" } diff --git a/Gems/Twitch/gem.json b/Gems/Twitch/gem.json index b5e2f3a3b2..6f875e9171 100644 --- a/Gems/Twitch/gem.json +++ b/Gems/Twitch/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Network", "SDK", "Multiplayer"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/twitch/" } diff --git a/Gems/UiBasics/gem.json b/Gems/UiBasics/gem.json index 9cd1548f57..d1e06eb15a 100644 --- a/Gems/UiBasics/gem.json +++ b/Gems/UiBasics/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["UI", "Assets", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/ui-basics/" } diff --git a/Gems/Vegetation/gem.json b/Gems/Vegetation/gem.json index 1796123014..3119d67560 100644 --- a/Gems/Vegetation/gem.json +++ b/Gems/Vegetation/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Environment", "Tools", "Design"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/vegetation/" } diff --git a/Gems/VideoPlaybackFramework/gem.json b/Gems/VideoPlaybackFramework/gem.json index 05598a8e5e..15b200c8db 100644 --- a/Gems/VideoPlaybackFramework/gem.json +++ b/Gems/VideoPlaybackFramework/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Rendering", "Framework"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/video-playback-framework/" } diff --git a/Gems/VirtualGamepad/gem.json b/Gems/VirtualGamepad/gem.json index 610f203f7c..472e8b93fe 100644 --- a/Gems/VirtualGamepad/gem.json +++ b/Gems/VirtualGamepad/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Input", "Gameplay"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/virtual-gamepad/" } diff --git a/Gems/WhiteBox/gem.json b/Gems/WhiteBox/gem.json index a46434dd65..0e44e2fe5e 100644 --- a/Gems/WhiteBox/gem.json +++ b/Gems/WhiteBox/gem.json @@ -8,5 +8,6 @@ "canonical_tags": ["Gem"], "user_tags": ["Design", "Tools", "Utility"], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/design/white-box/" } diff --git a/scripts/o3de/o3de/gem_properties.py b/scripts/o3de/o3de/gem_properties.py index c97e5db89d..39011a213a 100644 --- a/scripts/o3de/o3de/gem_properties.py +++ b/scripts/o3de/o3de/gem_properties.py @@ -53,6 +53,7 @@ def edit_gem_props(gem_path: pathlib.Path = None, new_summary: str = None, new_icon: str = None, new_requirements: str = None, + new_documentation_url: str = None, new_tags: list or str = None, remove_tags: list or str = None, replace_tags: list or str = None, @@ -90,7 +91,9 @@ def edit_gem_props(gem_path: pathlib.Path = None, if new_icon: update_key_dict['icon_path'] = new_icon if new_requirements: - update_key_dict['icon_requirements'] = new_requirements + update_key_dict['requirements'] = new_requirements + if new_documentation_url: + update_key_dict['documentation_url'] = new_documentation_url update_key_dict['user_tags'] = update_values_in_key_list(gem_json_data.get('user_tags', []), new_tags, remove_tags, replace_tags) @@ -110,6 +113,7 @@ def _edit_gem_props(args: argparse) -> int: args.gem_summary, args.gem_icon, args.gem_requirements, + args.gem_documentation_url, args.add_tags, args.remove_tags, args.replace_tags) @@ -129,20 +133,22 @@ def add_parser_args(parser): group.add_argument('-go', '--gem-origin', type=str, required=False, help='Sets description for gem origin.') group.add_argument('-gt', '--gem-type', type=str, required=False, choices=['Code', 'Tool', 'Asset'], - help='Sets the gem type. Can only be one of the selected choices') + help='Sets the gem type. Can only be one of the selected choices.') group.add_argument('-gs', '--gem-summary', type=str, required=False, help='Sets the summary description of the gem.') group.add_argument('-gi', '--gem-icon', type=str, required=False, help='Sets the path to the projects icon resource.') group.add_argument('-gr', '--gem-requirements', type=str, required=False, - help='Sets the description of the requirements needed to use the gem') + help='Sets the description of the requirements needed to use the gem.') + group.add_argument('-gdu', '--gem-documentation-url', type=str, required=False, + help='Sets the url for documentation of the gem.') group = parser.add_mutually_exclusive_group(required=False) group.add_argument('-at', '--add-tags', type=str, nargs='*', required=False, - help='Adds tag(s) to user_tags property. Can be specified multiple times') + help='Adds tag(s) to user_tags property. Can be specified multiple times.') group.add_argument('-dt', '--remove-tags', type=str, nargs='*', required=False, - help='Removes tag(s) from the user_tags property. Can be specified multiple times') + help='Removes tag(s) from the user_tags property. Can be specified multiple times.') group.add_argument('-rt', '--replace-tags', type=str, nargs='*', required=False, - help='Replace tag(s) in user_tags property. Can be specified multiple times') + help='Replace tag(s) in user_tags property. Can be specified multiple times.') parser.set_defaults(func=_edit_gem_props) diff --git a/scripts/o3de/tests/unit_test_gem_properties.py b/scripts/o3de/tests/unit_test_gem_properties.py index 29d53fbee6..5bfbe5573a 100644 --- a/scripts/o3de/tests/unit_test_gem_properties.py +++ b/scripts/o3de/tests/unit_test_gem_properties.py @@ -29,7 +29,8 @@ TEST_GEM_JSON_PAYLOAD = ''' "TestGem" ], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "documentation_url": "https://o3de.org/docs/" } ''' @@ -44,24 +45,26 @@ def init_gem_json_data(request): @pytest.mark.usefixtures('init_gem_json_data') class TestEditGemProperties: @pytest.mark.parametrize("gem_path, gem_name, gem_new_name, gem_display, gem_origin,\ - gem_type, gem_summary, gem_icon, gem_requirements,\ + gem_type, gem_summary, gem_icon, gem_requirements, gem_documentation_url,\ add_tags, remove_tags, replace_tags, expected_tags, expected_result", [ pytest.param(pathlib.PurePath('D:/TestProject'), None, 'TestGem2', 'New Gem Name', 'O3DE', 'Code', 'Gem that exercises Default Gem Template', - 'preview.png', '', + 'new_preview.png', 'Do this extra thing', 'https://o3de.org/docs/user-guide/gems/', ['Physics', 'Rendering', 'Scripting'], None, None, ['TestGem', 'Physics', 'Rendering', 'Scripting'], 0), pytest.param(None, 'TestGem2', None, 'New Gem Name', 'O3DE', 'Asset', 'Gem that exercises Default Gem Template', - 'preview.png', '', None, ['Physics'], None, ['TestGem', 'Rendering', 'Scripting'], 0), + 'new_preview.png', 'Do this extra thing', 'https://o3de.org/docs/user-guide/gems/', None, + ['Physics'], None, ['TestGem', 'Rendering', 'Scripting'], 0), pytest.param(None, 'TestGem2', None, 'New Gem Name', 'O3DE', 'Tool', 'Gem that exercises Default Gem Template', - 'preview.png', '', None, None, ['Animation', 'TestGem'], ['Animation', 'TestGem'], 0) + 'new_preview.png', 'Do this extra thing', 'https://o3de.org/docs/user-guide/gems/', None, + None, ['Animation', 'TestGem'], ['Animation', 'TestGem'], 0) ] ) def test_edit_gem_properties(self, gem_path, gem_name, gem_new_name, gem_display, gem_origin, - gem_type, gem_summary, gem_icon, gem_requirements, - add_tags, remove_tags, replace_tags, + gem_type, gem_summary, gem_icon, gem_requirements, + gem_documentation_url, add_tags, remove_tags, replace_tags, expected_tags, expected_result): def get_gem_json_data(gem_path: pathlib.Path) -> dict: @@ -79,7 +82,7 @@ class TestEditGemProperties: patch('o3de.manifest.get_registered', side_effect=get_gem_path) as get_registered_patch: result = gem_properties.edit_gem_props(gem_path, gem_name, gem_new_name, gem_display, gem_origin, gem_type, gem_summary, gem_icon, gem_requirements, - add_tags, remove_tags, replace_tags) + gem_documentation_url, add_tags, remove_tags, replace_tags) assert result == expected_result if gem_new_name: assert self.gem_json.data.get('gem_name', '') == gem_new_name @@ -94,6 +97,8 @@ class TestEditGemProperties: if gem_icon: assert self.gem_json.data.get('icon_path', '') == gem_icon if gem_requirements: - assert self.gem_json.data.get('requirments', '') == gem_requirements + assert self.gem_json.data.get('requirements', '') == gem_requirements + if gem_documentation_url: + assert self.gem_json.data.get('documentation_url', '') == gem_documentation_url assert set(self.gem_json.data.get('user_tags', [])) == set(expected_tags) From 9e0b8c564d4bc07d9479691bca0251a9ac194c01 Mon Sep 17 00:00:00 2001 From: moraaar Date: Fri, 6 Aug 2021 17:07:15 +0100 Subject: [PATCH 30/38] Fixed AzToolsFramework tests (#2887) * Fixed AzToolsFramework unit tests. Signed-off-by: moraaar * Include missing header. Signed-off-by: moraaar * Using util's class to generate temp directory, instead of qt. Signed-off-by: moraaar * Added empty line Signed-off-by: moraaar * Fixed warning in MessageTest fixture that CacheProjectRootFolder was not set Signed-off-by: moraaar * Additional checks in CreateDefaultEditorEntity helper function. Signed-off-by: moraaar * Updated the AzToolsFrameworkTest logic to set the project cache path The Project Cache Path and Project Path is set through the CommandLine functionality of the ComponentApplication. This allows those Project Cache Path and Project Path to be set within the Settings Registry during the ComponentApplication constructor Removed the explicitly calls to delete the temporary directory and fixed the ScopedTemporaryDirectory class to recursively delete the temporary directory Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Setup correctly @assets@ alias for PlatformAddressedAssetCatalogManagerTest and AssetSeedManagerTest fixtures. - These 2 test fixtures need to manually set the @asset@ alias to not include the platform at the end (which it does by default), because they are looping over platforms in their setup. - Also initializing pointers to nullptr, so if setup fail in the future the teardown doesn't crash trying to delete garbage. Signed-off-by: moraaar Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- .../AzFramework/Tests/Utils/Utils.cpp | 124 ++++++++++------- .../Framework/AzFramework/Tests/Utils/Utils.h | 3 + .../UnitTest/AzToolsFrameworkTestHelpers.cpp | 12 ++ .../UnitTest/ToolsTestApplication.cpp | 7 +- .../UnitTest/ToolsTestApplication.h | 1 + .../Tests/AssetFileInfoListComparison.cpp | 62 +++++---- .../Tests/AssetSeedManager.cpp | 121 ++++++----------- .../Tests/InstanceDataHierarchy.cpp | 24 ++-- .../PlatformAddressedAssetCatalogTests.cpp | 127 +++++++----------- 9 files changed, 236 insertions(+), 245 deletions(-) diff --git a/Code/Framework/AzFramework/Tests/Utils/Utils.cpp b/Code/Framework/AzFramework/Tests/Utils/Utils.cpp index cdc53a7e17..4f811a98ed 100644 --- a/Code/Framework/AzFramework/Tests/Utils/Utils.cpp +++ b/Code/Framework/AzFramework/Tests/Utils/Utils.cpp @@ -8,72 +8,100 @@ #include "Utils.h" #include +#include #include -UnitTest::ScopedTemporaryDirectory::ScopedTemporaryDirectory() +namespace UnitTest { - constexpr int MaxAttempts = 255; + void DeleteFolderRecursive(const AZ::IO::PathView& path) + { + auto callback = [&path](AZStd::string_view filename, bool isFile) -> bool + { + if (isFile) + { + auto filePath = AZ::IO::FixedMaxPath(path) / filename; + AZ::IO::SystemFile::Delete(filePath.c_str()); + } + else + { + if (filename != "." && filename != "..") + { + auto folderPath = AZ::IO::FixedMaxPath(path) / filename; + DeleteFolderRecursive(folderPath); + } + } + return true; + }; + auto searchPath = AZ::IO::FixedMaxPath(path) / "*"; + AZ::IO::SystemFile::FindFiles(searchPath.c_str(), callback); + AZ::IO::SystemFile::DeleteDir(AZ::IO::FixedMaxPathString(path.Native()).c_str()); + } + + + ScopedTemporaryDirectory::ScopedTemporaryDirectory() + { + constexpr int MaxAttempts = 255; #if !AZ_TRAIT_USE_POSIX_TEMP_FOLDER - const auto userTempFolder = std::filesystem::temp_directory_path(); + const auto userTempFolder = std::filesystem::temp_directory_path(); #else - AZ::IO::Path userTempFolder("/tmp"); + AZ::IO::Path userTempFolder("/tmp"); #endif - for (int i = 0; i < MaxAttempts; ++i) - { - auto randomFolder = AZ::Uuid::CreateRandom().ToString>(false, false); - AZ::IO::FixedMaxPath testPath; -#if !AZ_TRAIT_USE_POSIX_TEMP_FOLDER - auto path = userTempFolder / ("UnitTest-" + randomFolder).c_str(); - testPath = path.string().c_str(); -#else - userTempFolder /= ("UnitTest-" + randomFolder).c_str(); - testPath = userTempFolder.c_str(); -#endif - if (!AZ::IO::SystemFile::Exists(testPath.c_str())) + for (int i = 0; i < MaxAttempts; ++i) { -#if !AZ_TRAIT_USE_POSIX_TEMP_FOLDER - m_path = path; - m_tempDirectory = m_path.string().c_str(); + auto randomFolder = AZ::Uuid::CreateRandom().ToString>(false, false); + AZ::IO::FixedMaxPath testPath; +#if !AZ_TRAIT_USE_POSIX_TEMP_FOLDER + auto path = userTempFolder / ("UnitTest-" + randomFolder).c_str(); + testPath = path.string().c_str(); #else - m_tempDirectory = testPath; + userTempFolder /= ("UnitTest-" + randomFolder).c_str(); + testPath = userTempFolder.c_str(); #endif - m_directoryExists = AZ::IO::SystemFile::CreateDir(m_tempDirectory.c_str()); - break; + if (!AZ::IO::SystemFile::Exists(testPath.c_str())) + { +#if !AZ_TRAIT_USE_POSIX_TEMP_FOLDER + m_path = path; + m_tempDirectory = m_path.string().c_str(); +#else + m_tempDirectory = testPath; +#endif + m_directoryExists = AZ::IO::SystemFile::CreateDir(m_tempDirectory.c_str()); + break; + } + } + + AZ_Error("ScopedTemporaryDirectory", !m_tempDirectory.empty(), "Failed to create unique temporary directory after attempting %d random folder names", MaxAttempts); + } + + ScopedTemporaryDirectory::~ScopedTemporaryDirectory() + { + if (m_directoryExists) + { + DeleteFolderRecursive(m_tempDirectory); } } - AZ_Error("ScopedTemporaryDirectory", !m_tempDirectory.empty(), "Failed to create unique temporary directory after attempting %d random folder names", MaxAttempts); -} - -UnitTest::ScopedTemporaryDirectory::~ScopedTemporaryDirectory() -{ - if (m_directoryExists) + bool ScopedTemporaryDirectory::IsValid() const { - AZ::IO::SystemFile::DeleteDir(m_tempDirectory.c_str()); + return m_directoryExists; } -} -bool UnitTest::ScopedTemporaryDirectory::IsValid() const -{ - return m_directoryExists; -} - -const char* UnitTest::ScopedTemporaryDirectory::GetDirectory() const -{ - return m_tempDirectory.c_str(); -} + const char* ScopedTemporaryDirectory::GetDirectory() const + { + return m_tempDirectory.c_str(); + } #if !AZ_TRAIT_USE_POSIX_TEMP_FOLDER -const std::filesystem::path& UnitTest::ScopedTemporaryDirectory::GetPath() const -{ - return m_path; -} - -std::filesystem::path UnitTest::ScopedTemporaryDirectory::operator/(const std::filesystem::path& rhs) const -{ - return m_path / rhs; -} + const std::filesystem::path& ScopedTemporaryDirectory::GetPath() const + { + return m_path; + } + std::filesystem::path ScopedTemporaryDirectory::operator/(const std::filesystem::path& rhs) const + { + return m_path / rhs; + } #endif // !AZ_TRAIT_USE_POSIX_TEMP_FOLDER +} diff --git a/Code/Framework/AzFramework/Tests/Utils/Utils.h b/Code/Framework/AzFramework/Tests/Utils/Utils.h index 83a3a9dd42..b5fb5e8387 100644 --- a/Code/Framework/AzFramework/Tests/Utils/Utils.h +++ b/Code/Framework/AzFramework/Tests/Utils/Utils.h @@ -18,6 +18,9 @@ namespace UnitTest { + //! Deletes a folder hierarchy from the supplied path + void DeleteFolderRecursive(const AZ::IO::PathView& path); + // Creates a randomly named folder inside the user's temporary directory. // The folder and all contents will be destroyed when the object goes out of scope struct ScopedTemporaryDirectory diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.cpp index 3f2cb24d69..5ffbe1b4f3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.cpp @@ -352,8 +352,20 @@ namespace UnitTest AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( entityId, &AzToolsFramework::EditorEntityContextRequestBus::Events::CreateNewEditorEntity, name); + if (!entityId.IsValid()) + { + AZ_Error("CreateDefaultEditorEntity", false, "Failed to create editor entity '%s'", name); + return AZ::EntityId(); + } + AZ::Entity* entity = GetEntityById(entityId); + if (!entity) + { + AZ_Error("CreateDefaultEditorEntity", false, "Invalid entity obtained from Id %s", entityId.ToString().c_str()); + return AZ::EntityId(); + } + entity->Deactivate(); // add required components for the Editor entity diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/ToolsTestApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/ToolsTestApplication.cpp index be9b332982..3ef1210233 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/ToolsTestApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/ToolsTestApplication.cpp @@ -11,7 +11,12 @@ namespace UnitTest { ToolsTestApplication::ToolsTestApplication(AZStd::string applicationName) - : ToolsApplication() + :ToolsTestApplication(AZStd::move(applicationName), 0, nullptr) + { + } + + ToolsTestApplication::ToolsTestApplication(AZStd::string applicationName, int argc, char** argv) + : AzToolsFramework::ToolsApplication(&argc, &argv) , m_applicationName(AZStd::move(applicationName)) { } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/ToolsTestApplication.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/ToolsTestApplication.h index 22de71b2d8..2bf920cc4a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/ToolsTestApplication.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UnitTest/ToolsTestApplication.h @@ -18,6 +18,7 @@ namespace UnitTest { public: explicit ToolsTestApplication(AZStd::string applicationName); + ToolsTestApplication(AZStd::string applicationName, int argc, char** argv); void SetSettingsRegistrySpecializations(AZ::SettingsRegistryInterface::Specializations& specializations) override; protected: diff --git a/Code/Framework/AzToolsFramework/Tests/AssetFileInfoListComparison.cpp b/Code/Framework/AzToolsFramework/Tests/AssetFileInfoListComparison.cpp index 4aa9dbe2bb..5ad68a2c1c 100644 --- a/Code/Framework/AzToolsFramework/Tests/AssetFileInfoListComparison.cpp +++ b/Code/Framework/AzToolsFramework/Tests/AssetFileInfoListComparison.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -49,19 +50,22 @@ namespace UnitTest void SetUp() override { using namespace AZ::Data; - m_application = new ToolsTestApplication("AssetFileInfoListComparisonTest"); + constexpr size_t MaxCommandArgsCount = 128; + using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; + using ArgumentContainer = AZStd::fixed_vector; + // The first command line argument is assumed to be the executable name so add a blank entry for it + ArgumentContainer argContainer{ {} }; + + // Append Command Line override for the Project Cache Path + auto projectCachePathOverride = FixedValueString::format(R"(--project-cache-path="%s")", m_tempDir.GetDirectory()); + auto projectPathOverride = FixedValueString{ R"(--project-path=AutomatedTesting)" }; + argContainer.push_back(projectCachePathOverride.data()); + argContainer.push_back(projectPathOverride.data()); + m_application = new ToolsTestApplication("AssetFileInfoListComparisonTest", aznumeric_caster(argContainer.size()), argContainer.data()); AzToolsFramework::AssetSeedManager assetSeedManager; AzFramework::AssetRegistry assetRegistry; - m_localFileIO = aznew AZ::IO::LocalFileIO(); - - m_priorFileIO = AZ::IO::FileIOBase::GetInstance(); - AZ::IO::FileIOBase::SetInstance(nullptr); - AZ::IO::FileIOBase::SetInstance(m_localFileIO); - - AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", m_tempDir.GetDirectory()); - - AZStd::string assetRoot = AzToolsFramework::PlatformAddressedAssetCatalog::GetAssetRootForPlatform(AzFramework::PlatformId::PC); + const AZStd::string assetRoot = AzToolsFramework::PlatformAddressedAssetCatalog::GetAssetRootForPlatform(AzFramework::PlatformId::PC); for (int idx = 0; idx < TotalAssets; idx++) { @@ -75,7 +79,8 @@ namespace UnitTest AZ_TEST_START_TRACE_SUPPRESSION; if (m_fileStreams[idx].Open(m_assetsPath[idx].c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary | AZ::IO::OpenMode::ModeCreatePath)) { - m_fileStreams[idx].Write(info.m_relativePath.size(), info.m_relativePath.data()); + AZ::IO::SizeType bytesWritten = m_fileStreams[idx].Write(info.m_relativePath.size(), info.m_relativePath.data()); + EXPECT_EQ(bytesWritten, info.m_relativePath.size()); AZ_TEST_STOP_TRACE_SUPPRESSION(1); // writing to asset cache folder } else @@ -92,6 +97,7 @@ namespace UnitTest assetRegistry.RegisterAssetDependency(m_assets[3], AZ::Data::ProductDependency(m_assets[4], 0)); m_application->Start(AzFramework::Application::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. @@ -109,14 +115,16 @@ namespace UnitTest AZStd::string pcCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::PC); - ASSERT_TRUE(AzFramework::AssetCatalog::SaveCatalog(pcCatalogFile.c_str(), &assetRegistry)) << "Unable to save the asset catalog file.\n"; + bool catalogSaved = AzFramework::AssetCatalog::SaveCatalog(pcCatalogFile.c_str(), &assetRegistry); + EXPECT_TRUE(catalogSaved) << "Unable to save the asset catalog file.\n"; m_pcCatalog = new AzToolsFramework::PlatformAddressedAssetCatalog(AzFramework::PlatformId::PC); assetSeedManager.AddSeedAsset(m_assets[0], AzFramework::PlatformFlags::Platform_PC); assetSeedManager.AddSeedAsset(m_assets[1], AzFramework::PlatformFlags::Platform_PC); - assetSeedManager.SaveAssetFileInfo(TempFiles[FileIndex::FirstAssetFileInfoList], AzFramework::PlatformFlags::Platform_PC, {}); + bool firstAssetFileInfoListSaved = assetSeedManager.SaveAssetFileInfo(TempFiles[FileIndex::FirstAssetFileInfoList], AzFramework::PlatformFlags::Platform_PC, {}); + EXPECT_TRUE(firstAssetFileInfoListSaved); // Modify contents of asset2 int fileIndex = 2; @@ -124,7 +132,8 @@ namespace UnitTest if (m_fileStreams[fileIndex].Open(m_assetsPath[fileIndex].c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary | AZ::IO::OpenMode::ModeCreatePath)) { AZStd::string fileContent = AZStd::string::format("new Asset%d.txt", fileIndex);// changing file content - m_fileStreams[fileIndex].Write(fileContent.size(), fileContent.c_str()); + AZ::IO::SizeType bytesWritten = m_fileStreams[fileIndex].Write(fileContent.size(), fileContent.c_str()); + EXPECT_EQ(bytesWritten, fileContent.size()); AZ_TEST_STOP_TRACE_SUPPRESSION(1); // writing to asset cache folder } else @@ -138,7 +147,8 @@ namespace UnitTest if (m_fileStreams[fileIndex].Open(m_assetsPath[fileIndex].c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary | AZ::IO::OpenMode::ModeCreatePath)) { AZStd::string fileContent = AZStd::string::format("new Asset%d.txt", fileIndex);// changing file content - m_fileStreams[fileIndex].Write(fileContent.size(), fileContent.c_str()); + AZ::IO::SizeType bytesWritten = m_fileStreams[fileIndex].Write(fileContent.size(), fileContent.c_str()); + EXPECT_EQ(bytesWritten, fileContent.size()); AZ_TEST_STOP_TRACE_SUPPRESSION(1); // writing to asset cache folder } else @@ -149,7 +159,8 @@ namespace UnitTest assetSeedManager.RemoveSeedAsset(m_assets[0], AzFramework::PlatformFlags::Platform_PC); assetSeedManager.AddSeedAsset(m_assets[5], AzFramework::PlatformFlags::Platform_PC); - assetSeedManager.SaveAssetFileInfo(TempFiles[FileIndex::SecondAssetFileInfoList], AzFramework::PlatformFlags::Platform_PC, {}); + bool secondAssetFileInfoListSaved = assetSeedManager.SaveAssetFileInfo(TempFiles[FileIndex::SecondAssetFileInfoList], AzFramework::PlatformFlags::Platform_PC, {}); + EXPECT_TRUE(secondAssetFileInfoListSaved); } void TearDown() override @@ -162,7 +173,8 @@ namespace UnitTest if (fileIO->Exists(TempFiles[idx])) { AZ_TEST_START_TRACE_SUPPRESSION; - fileIO->Remove(TempFiles[idx]); + AZ::IO::Result result = fileIO->Remove(TempFiles[idx]); + EXPECT_EQ(result.GetResultCode(), AZ::IO::ResultCode::Success); AZ_TEST_STOP_TRACE_SUPPRESSION(1); // deleting from asset cache folder } } @@ -175,7 +187,8 @@ namespace UnitTest if (fileIO->Exists(m_assetsPath[idx].c_str())) { AZ_TEST_START_TRACE_SUPPRESSION; - fileIO->Remove(m_assetsPath[idx].c_str()); + AZ::IO::Result result = fileIO->Remove(m_assetsPath[idx].c_str()); + EXPECT_EQ(result.GetResultCode(), AZ::IO::ResultCode::Success); AZ_TEST_STOP_TRACE_SUPPRESSION(1); // deleting from asset cache folder } } @@ -184,15 +197,12 @@ namespace UnitTest if (fileIO->Exists(pcCatalogFile.c_str())) { AZ_TEST_START_TRACE_SUPPRESSION; - fileIO->Remove(pcCatalogFile.c_str()); + AZ::IO::Result result = fileIO->Remove(pcCatalogFile.c_str()); + EXPECT_EQ(result.GetResultCode(), AZ::IO::ResultCode::Success); AZ_TEST_STOP_TRACE_SUPPRESSION(1); // deleting from asset cache folder } delete m_pcCatalog; - delete m_localFileIO; - m_localFileIO = nullptr; - AZ::IO::FileIOBase::SetInstance(nullptr); - AZ::IO::FileIOBase::SetInstance(m_priorFileIO); m_application->Stop(); delete m_application; @@ -742,11 +752,9 @@ namespace UnitTest } - ToolsTestApplication* m_application; + ToolsTestApplication* m_application = nullptr; UnitTest::ScopedTemporaryDirectory m_tempDir; - AzToolsFramework::PlatformAddressedAssetCatalog* m_pcCatalog; - AZ::IO::FileIOBase* m_priorFileIO = nullptr; - AZ::IO::FileIOBase* m_localFileIO = nullptr; + AzToolsFramework::PlatformAddressedAssetCatalog* m_pcCatalog = nullptr; AZ::IO::FileIOStream m_fileStreams[TotalAssets]; AZ::Data::AssetId m_assets[TotalAssets]; AZStd::string m_assetsPath[TotalAssets]; diff --git a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp index 0b5c37ccc2..4083608370 100644 --- a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp +++ b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp @@ -23,11 +23,12 @@ #include #include #include +#include + namespace // anonymous { static const int s_totalAssets = 12; static const int s_totalTestPlatforms = 2; - const char* s_catalogFile = "AssetCatalog.xml"; AZ::Data::AssetId assets[s_totalAssets]; const char TestSliceAssetPath[] = "test.slice"; @@ -55,18 +56,30 @@ namespace UnitTest void SetUp() override { using namespace AZ::Data; - m_application = new ToolsTestApplication("AssetSeedManagerTest"); + constexpr size_t MaxCommandArgsCount = 128; + using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; + using ArgumentContainer = AZStd::fixed_vector; + // The first command line argument is assumed to be the executable name so add a blank entry for it + ArgumentContainer argContainer{ {} }; + + // Append Command Line override for the Project Cache Path + AZ::IO::Path cacheProjectRootFolder{ m_tempDir.GetDirectory() }; + auto projectCachePathOverride = FixedValueString::format(R"(--project-cache-path="%s")", cacheProjectRootFolder.c_str()); + auto projectPathOverride = FixedValueString{ R"(--project-path=AutomatedTesting)" }; + argContainer.push_back(projectCachePathOverride.data()); + argContainer.push_back(projectPathOverride.data()); + m_application = new ToolsTestApplication("AssetSeedManagerTest", aznumeric_caster(argContainer.size()), argContainer.data()); m_assetSeedManager = new AzToolsFramework::AssetSeedManager(); m_assetRegistry = new AzFramework::AssetRegistry(); - AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); - auto projectPathKey = - AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); - m_application->Start(AzFramework::Application::Descriptor()); + // By default @assets@ is setup to include the platform at the end. But this test is going to + // loop over platforms and it will be included as part of the relative path of the file. + // So the asset folder for these tests have to point to the cache project root folder, which + // doesn't include the platform. + AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", cacheProjectRootFolder.c_str()); + for (int idx = 0; idx < s_totalAssets; idx++) { assets[idx] = AssetId(AZ::Uuid::CreateRandom(), 0); @@ -83,17 +96,18 @@ namespace UnitTest int platformCount = 0; for(auto thisPlatform : m_testPlatforms) { - AZStd::string assetRoot = AzToolsFramework::PlatformAddressedAssetCatalog::GetAssetRootForPlatform(thisPlatform); + AZ::IO::Path assetRoot = AzToolsFramework::PlatformAddressedAssetCatalog::GetAssetRootForPlatform(thisPlatform); for (int idx = 0; idx < s_totalAssets; idx++) { - AzFramework::StringFunc::Path::Join(assetRoot.c_str(), m_assetsPath[idx].c_str(), m_assetsPathFull[platformCount][idx]); + m_assetsPathFull[platformCount][idx] = (assetRoot / m_assetsPath[idx]).Native(); AZ_TEST_START_TRACE_SUPPRESSION; if (m_fileStreams[platformCount][idx].Open(m_assetsPathFull[platformCount][idx].c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary | AZ::IO::OpenMode::ModeCreatePath)) { - m_fileStreams[platformCount][idx].Write(m_assetsPath[idx].size(), m_assetsPath[idx].data()); + AZ::IO::SizeType bytesWritten = m_fileStreams[platformCount][idx].Write(m_assetsPath[idx].size(), m_assetsPath[idx].data()); + EXPECT_EQ(bytesWritten, m_assetsPath[idx].size()); m_fileStreams[platformCount][idx].Close(); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // writing to asset cache folder, only invalid for PC, not invalid in Jenkins + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // writing to asset cache folder } else { @@ -117,7 +131,7 @@ namespace UnitTest AZ_TEST_START_TRACE_SUPPRESSION; AZ::IO::FileIOStream dynamicSliceFileIOStream(TestDynamicSliceAssetPath, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeText); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // writing to asset cache folder, not invalid in Jenkins + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // writing to asset cache folder AZ::Data::AssetInfo sliceAssetInfo; sliceAssetInfo.m_relativePath = TestSliceAssetPath; @@ -131,7 +145,7 @@ namespace UnitTest AZ_TEST_START_TRACE_SUPPRESSION; AZ::IO::FileIOStream sliceFileIOStream(TestSliceAssetPath, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeText); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // writing to asset cache folder, not invalid in Jenkins + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // writing to asset cache folder // asset0 -> asset1 -> asset2 -> asset4 // --> asset3 @@ -197,58 +211,6 @@ namespace UnitTest void TearDown() override { - AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); - - if (fileIO->Exists(s_catalogFile)) - { - AZ_TEST_START_TRACE_SUPPRESSION; - fileIO->Remove(s_catalogFile); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // deleting from asset cache folder, not invalid in Jenkins - } - - for (size_t platformCount = 0; platformCount < s_totalTestPlatforms; ++platformCount) - { - // Deleting all the temporary files - for (int idx = 0; idx < s_totalAssets; idx++) - { - // we need to close the handle before we try to remove the file - if (fileIO->Exists(m_assetsPathFull[platformCount][idx].c_str())) - { - AZ_TEST_START_TRACE_SUPPRESSION; - fileIO->Remove(m_assetsPathFull[platformCount][idx].c_str()); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // deleting from asset cache folder, not invalid in Jenkins - } - } - } - - if (fileIO->Exists(TestSliceAssetPath)) - { - AZ_TEST_START_TRACE_SUPPRESSION; - fileIO->Remove(TestSliceAssetPath); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // deleting from asset cache folder, not invalid in Jenkins - } - - if (fileIO->Exists(TestDynamicSliceAssetPath)) - { - AZ_TEST_START_TRACE_SUPPRESSION; - fileIO->Remove(TestDynamicSliceAssetPath); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // deleting from asset cache folder, not invalid in Jenkins - } - - auto pcCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::PC); - auto androidCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ANDROID_ID); - if (fileIO->Exists(pcCatalogFile.c_str())) - { - AZ_TEST_START_TRACE_SUPPRESSION; - fileIO->Remove(pcCatalogFile.c_str()); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // deleting from asset cache folder, not invalid in Jenkins - } - - if (fileIO->Exists(androidCatalogFile.c_str())) - { - fileIO->Remove(androidCatalogFile.c_str()); - } - delete m_assetSeedManager; delete m_assetRegistry; delete m_pcCatalog; @@ -284,7 +246,7 @@ namespace UnitTest // Attempt to save to the same file. Should not be allowed. AZ_TEST_START_TRACE_SUPPRESSION; EXPECT_FALSE(m_assetSeedManager->Save(filePath)); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // writing to asset cache folder, not invalid in Jenkins + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // One error expected // Clean up the test environment AZ::IO::SystemFile::SetWritable(filePath.c_str(), true); @@ -310,7 +272,7 @@ namespace UnitTest // Attempt to save to the same file. Should not be allowed. AZ_TEST_START_TRACE_SUPPRESSION; EXPECT_FALSE(m_assetSeedManager->SaveAssetFileInfo(filePath, AzFramework::PlatformFlags::Platform_PC, {})); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // writing to asset cache folder, not invalid in Jenkins + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // One error expected // Clean up the test environment AZ::IO::SystemFile::SetWritable(filePath.c_str(), true); @@ -379,7 +341,7 @@ namespace UnitTest // Step we are testing AZ_TEST_START_TRACE_SUPPRESSION; m_assetSeedManager->AddPlatformToAllSeeds(AzFramework::PlatformId::ANDROID_ID); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // writing to asset cache folder, not invalid in Jenkins + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // One error expected // Verification AzFramework::PlatformFlags expectedPlatformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID; @@ -649,9 +611,10 @@ namespace UnitTest if (m_fileStreams[0][fileIndex].Open(m_assetsPathFull[0][fileIndex].c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary | AZ::IO::OpenMode::ModeCreatePath)) { AZStd::string fileContent = AZStd::string::format("asset%d.txt", fileIndex); - m_fileStreams[0][fileIndex].Write(fileContent.size(), fileContent.c_str()); + AZ::IO::SizeType bytesWritten = m_fileStreams[0][fileIndex].Write(fileContent.size(), fileContent.c_str()); + EXPECT_EQ(bytesWritten, fileContent.size()); m_fileStreams[0][fileIndex].Close(); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // writing to asset cache folder, not invalid in Jenkins + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // writing to asset cache folder } AzToolsFramework::AssetFileInfoList assetList2 = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC); @@ -682,9 +645,10 @@ namespace UnitTest if (m_fileStreams[0][fileIndex].Open(m_assetsPathFull[0][fileIndex].c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary | AZ::IO::OpenMode::ModeCreatePath)) { AZStd::string fileContent = AZStd::string::format("asset%d.txt", fileIndex + 1);// changing file content - m_fileStreams[0][fileIndex].Write(fileContent.size(), fileContent.c_str()); + AZ::IO::SizeType bytesWritten = m_fileStreams[0][fileIndex].Write(fileContent.size(), fileContent.c_str()); + EXPECT_EQ(bytesWritten, fileContent.size()); m_fileStreams[0][fileIndex].Close(); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // writing to asset cache folder, not invalid in Jenkins + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // writing to asset cache folder } AzToolsFramework::AssetFileInfoList assetList2 = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC); @@ -790,16 +754,17 @@ namespace UnitTest } - AzToolsFramework::AssetSeedManager* m_assetSeedManager; - AzFramework::AssetRegistry* m_assetRegistry; - ToolsTestApplication* m_application; - AzToolsFramework::PlatformAddressedAssetCatalog* m_pcCatalog; - AzToolsFramework::PlatformAddressedAssetCatalog* m_androidCatalog; + AzToolsFramework::AssetSeedManager* m_assetSeedManager = nullptr; + AzFramework::AssetRegistry* m_assetRegistry = nullptr; + ToolsTestApplication* m_application = nullptr; + AzToolsFramework::PlatformAddressedAssetCatalog* m_pcCatalog = nullptr; + AzToolsFramework::PlatformAddressedAssetCatalog* m_androidCatalog = nullptr; AZ::IO::FileIOStream m_fileStreams[s_totalTestPlatforms][s_totalAssets]; AzFramework::PlatformId m_testPlatforms[s_totalTestPlatforms]; AZStd::string m_assetsPath[s_totalAssets]; AZStd::string m_assetsPathFull[s_totalTestPlatforms][s_totalAssets]; AZ::Data::AssetId m_testDynamicSliceAssetId; + UnitTest::ScopedTemporaryDirectory m_tempDir; }; TEST_F(AssetSeedManagerTest, AssetSeedManager_SaveSeedListFile_FileIsReadOnly) diff --git a/Code/Framework/AzToolsFramework/Tests/InstanceDataHierarchy.cpp b/Code/Framework/AzToolsFramework/Tests/InstanceDataHierarchy.cpp index 1b11479bff..806a3bbab2 100644 --- a/Code/Framework/AzToolsFramework/Tests/InstanceDataHierarchy.cpp +++ b/Code/Framework/AzToolsFramework/Tests/InstanceDataHierarchy.cpp @@ -1285,7 +1285,7 @@ namespace UnitTest Crc32 uiHandler = 0; EXPECT_EQ(it->ReadAttribute(AZ::Edit::UIHandlers::Handler, uiHandler), true); EXPECT_EQ(uiHandler, AZ_CRC("TestHandler")); - EXPECT_EQ(it->GetElementMetadata()->m_name, "UIElement"); + EXPECT_STREQ(it->GetElementMetadata()->m_name, "UIElement"); EXPECT_EQ(it->GetElementMetadata()->m_nameCrc, AZ_CRC("UIElement")); uiHandler = 0; @@ -1293,7 +1293,7 @@ namespace UnitTest ++it; EXPECT_EQ(it->ReadAttribute(AZ::Edit::UIHandlers::Handler, uiHandler), true); EXPECT_EQ(uiHandler, AZ_CRC("TestHandler2")); - EXPECT_EQ(it->GetElementMetadata()->m_name, "UIElement2"); + EXPECT_STREQ(it->GetElementMetadata()->m_name, "UIElement2"); EXPECT_EQ(it->GetElementMetadata()->m_nameCrc, AZ_CRC("UIElement2")); } }; @@ -1356,21 +1356,21 @@ namespace UnitTest auto it = children.begin(); - EXPECT_EQ(it->GetElementMetadata()->m_name, "aggregatedDataElement"); + EXPECT_STREQ(it->GetElementMetadata()->m_name, "aggregatedDataElement"); ++it; if (i == 0) { - EXPECT_EQ(it->GetElementMetadata()->m_name, "notAggregatedDataElement"); + EXPECT_STREQ(it->GetElementMetadata()->m_name, "notAggregatedDataElement"); ++it; } - EXPECT_EQ(it->GetElementMetadata()->m_name, "aggregatedUIElement"); + EXPECT_STREQ(it->GetElementMetadata()->m_name, "aggregatedUIElement"); ++it; if (i == 0) { - EXPECT_EQ(it->GetElementMetadata()->m_name, "notAggregatedUIElement"); + EXPECT_STREQ(it->GetElementMetadata()->m_name, "notAggregatedUIElement"); ++it; } } @@ -1505,11 +1505,11 @@ namespace UnitTest AZStd::string childName(child.GetElementMetadata()->m_name); if (childName.compare("GroupFloat") == 0) { - EXPECT_EQ(child.GetGroupElementMetadata()->m_description, "Normal Group"); + EXPECT_STREQ(child.GetGroupElementMetadata()->m_description, "Normal Group"); } if (childName.compare("ToggleGroupInt") == 0) { - EXPECT_EQ(child.GetGroupElementMetadata()->m_description, "Group Toggle"); + EXPECT_STREQ(child.GetGroupElementMetadata()->m_description, "Group Toggle"); } if ((childName.compare("SubDataNormal") == 0) || (childName.compare("SubDataToggle") == 0)) { @@ -1518,11 +1518,11 @@ namespace UnitTest childName = subChild.GetElementMetadata()->m_name; if (childName.compare("SubInt") == 0) { - EXPECT_EQ(subChild.GetGroupElementMetadata()->m_description, "Normal SubGroup"); + EXPECT_STREQ(subChild.GetGroupElementMetadata()->m_description, "Normal SubGroup"); } if (childName.compare("SubFloat") == 0) { - EXPECT_EQ(subChild.GetGroupElementMetadata()->m_description, "SubGroup Toggle"); + EXPECT_STREQ(subChild.GetGroupElementMetadata()->m_description, "SubGroup Toggle"); } } } @@ -1552,7 +1552,7 @@ namespace UnitTest AZStd::string childName(child.GetElementMetadata()->m_name); if (childName.compare(paramName) == 0) { - EXPECT_EQ(child.GetParent()->GetClassMetadata()->m_name, "GroupTestComponent"); + EXPECT_STREQ(child.GetParent()->GetClassMetadata()->m_name, "GroupTestComponent"); } if ((childName.compare("SubDataNormal") == 0) || (childName.compare("SubDataToggle") == 0)) { @@ -1561,7 +1561,7 @@ namespace UnitTest childName = subChild.GetElementMetadata()->m_name; if (childName.compare(paramName) == 0) { - EXPECT_EQ(subChild.GetParent()->GetClassMetadata()->m_name, "SubData"); + EXPECT_STREQ(subChild.GetParent()->GetClassMetadata()->m_name, "SubData"); } } } diff --git a/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp b/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp index 00a7b1973c..67d40be376 100644 --- a/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp @@ -19,9 +19,8 @@ #include #include #include -#include -#include #include +#include namespace { @@ -35,25 +34,22 @@ namespace UnitTest { public: - AZStd::string GetTempFolder() - { - QTemporaryDir dir; - QDir tempPath(dir.path()); - return tempPath.absolutePath().toUtf8().data(); - } - void SetUp() override { using namespace AZ::Data; - m_application = new ToolsTestApplication("AddressedAssetCatalogManager"); // Shorter name because Setting Registry - // specialization are 32 characters max. + constexpr size_t MaxCommandArgsCount = 128; + using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; + using ArgumentContainer = AZStd::fixed_vector; + // The first command line argument is assumed to be the executable name so add a blank entry for it + ArgumentContainer argContainer{ {} }; - AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); - - auto projectPathKey = - AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); + // Append Command Line override for the Project Cache Path + AZ::IO::Path cacheProjectRootFolder{ m_tempDir.GetDirectory() }; + auto projectCachePathOverride = FixedValueString::format(R"(--project-cache-path="%s")", cacheProjectRootFolder.c_str()); + auto projectPathOverride = FixedValueString{ R"(--project-path=AutomatedTesting)" }; + argContainer.push_back(projectCachePathOverride.data()); + argContainer.push_back(projectPathOverride.data()); + m_application = new ToolsTestApplication("AddressedAssetCatalogManager", aznumeric_caster(argContainer.size()), argContainer.data()); m_application->Start(AzFramework::Application::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is @@ -61,33 +57,36 @@ namespace UnitTest // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); - AZStd::string cacheFolder; - AzFramework::StringFunc::Path::Join(GetTempFolder().c_str(), "testplatform", cacheFolder); - AzFramework::StringFunc::Path::Join(cacheFolder.c_str(), "testproject", cacheFolder); - - AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", cacheFolder.c_str()); + // By default @assets@ is setup to include the platform at the end. But this test is going to + // loop over all platforms and it will be included as part of the relative path of the file. + // So the asset folder for these tests have to point to the cache project root folder, which + // doesn't include the platform. + AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", cacheProjectRootFolder.c_str()); for (int platformNum = AzFramework::PlatformId::PC; platformNum < AzFramework::PlatformId::NumPlatformIds; ++platformNum) { - AZStd::string platformName{ AzFramework::PlatformHelper::GetPlatformName(static_cast(platformNum)) }; + const AZStd::string platformName{ AzFramework::PlatformHelper::GetPlatformName(static_cast(platformNum)) }; if (!platformName.length()) { // Do not test disabled platforms continue; } + AZStd::unique_ptr assetRegistry = AZStd::make_unique(); for (int idx = 0; idx < s_totalAssets; idx++) { m_assets[platformNum][idx] = AssetId(AZ::Uuid::CreateRandom(), 0); AZ::Data::AssetInfo info; - info.m_relativePath = AZStd::string::format("%s%sAsset%d_%s.txt", cacheFolder.c_str(), AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING, idx, platformName.c_str()); + info.m_relativePath = AZStd::move((AZ::IO::Path(platformName) / AZStd::string::format("Asset%d.txt", idx)).Native()); info.m_assetId = m_assets[platformNum][idx]; assetRegistry->RegisterAsset(m_assets[platformNum][idx], info); - m_assetsPath[platformNum][idx] = info.m_relativePath; + m_assetsPath[platformNum][idx] = AZStd::move((cacheProjectRootFolder / info.m_relativePath).Native()); AZ_TEST_START_TRACE_SUPPRESSION; if (m_fileStreams[platformNum][idx].Open(m_assetsPath[platformNum][idx].c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary | AZ::IO::OpenMode::ModeCreatePath)) { - m_fileStreams[platformNum][idx].Write(info.m_relativePath.size(), info.m_relativePath.data()); + AZ::IO::SizeType bytesWritten = m_fileStreams[platformNum][idx].Write(info.m_relativePath.size(), info.m_relativePath.data()); + EXPECT_EQ(bytesWritten, info.m_relativePath.size()); + m_fileStreams[platformNum][idx].Close(); AZ_TEST_STOP_TRACE_SUPPRESSION(1); // writing to asset cache folder } else @@ -112,48 +111,15 @@ namespace UnitTest void TearDown() override { - AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); - for (int platformNum = AzFramework::PlatformId::PC; platformNum < AzFramework::PlatformId::NumPlatformIds; ++platformNum) - { - AZStd::string platformName{ AzFramework::PlatformHelper::GetPlatformName(static_cast(platformNum)) }; - if (!platformName.length()) - { - // Do not test disabled platforms - continue; - } - AZStd::string catalogPath = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(static_cast(platformNum)); - - if (fileIO->Exists(catalogPath.c_str())) - { - fileIO->Remove(catalogPath.c_str()); - } - // Deleting all the temporary files - for (int idx = 0; idx < s_totalAssets; idx++) - { - // we need to close the handle before we try to remove the file - m_fileStreams[platformNum][idx].Close(); - if (fileIO->Exists(m_assetsPath[platformNum][idx].c_str())) - { - AZ_TEST_START_TRACE_SUPPRESSION; - fileIO->Remove(m_assetsPath[platformNum][idx].c_str()); - AZ_TEST_STOP_TRACE_SUPPRESSION(1); // removing from asset cache folder - } - } - } - - delete m_localFileIO; - m_localFileIO = nullptr; - AZ::IO::FileIOBase::SetInstance(m_priorFileIO); delete m_PlatformAddressedAssetCatalogManager; m_application->Stop(); delete m_application; } - AzToolsFramework::PlatformAddressedAssetCatalogManager* m_PlatformAddressedAssetCatalogManager; - ToolsTestApplication* m_application; - AZ::IO::FileIOBase* m_priorFileIO = nullptr; - AZ::IO::FileIOBase* m_localFileIO = nullptr; + AzToolsFramework::PlatformAddressedAssetCatalogManager* m_PlatformAddressedAssetCatalogManager = nullptr; + ToolsTestApplication* m_application = nullptr; + UnitTest::ScopedTemporaryDirectory m_tempDir; AZ::IO::FileIOStream m_fileStreams[AzFramework::PlatformId::NumPlatformIds][s_totalAssets]; AZ::Data::AssetId m_assets[AzFramework::PlatformId::NumPlatformIds][s_totalAssets]; @@ -183,12 +149,14 @@ namespace UnitTest TEST_F(PlatformAddressedAssetCatalogManagerTest, PlatformAddressedAssetCatalogManager_CatalogExistsChecks_Success) { - EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ANDROID_ID), true); AZStd::string androidCatalogPath = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ANDROID_ID); if (AZ::IO::FileIOBase::GetInstance()->Exists(androidCatalogPath.c_str())) { - AZ::IO::FileIOBase::GetInstance()->Remove(androidCatalogPath.c_str()); + AZ_TEST_START_TRACE_SUPPRESSION; + AZ::IO::Result result = AZ::IO::FileIOBase::GetInstance()->Remove(androidCatalogPath.c_str()); + EXPECT_EQ(result.GetResultCode(), AZ::IO::ResultCode::Success); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // removing from asset cache folder } EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ANDROID_ID), false); } @@ -218,31 +186,32 @@ namespace UnitTest : public AllocatorsFixture { public: - AZStd::string GetTempFolder() - { - QTemporaryDir dir; - QDir tempPath(dir.path()); - return tempPath.absolutePath().toUtf8().data(); - } - void SetUp() override { - AZ::IO::FileIOBase::SetInstance(nullptr); // The API requires the old instance to be destroyed first - AZ::IO::FileIOBase::SetInstance(new AZ::IO::LocalFileIO()); + constexpr size_t MaxCommandArgsCount = 128; + using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; + using ArgumentContainer = AZStd::fixed_vector; + // The first command line argument is assumed to be the executable name so add a blank entry for it + ArgumentContainer argContainer{ {} }; - AZStd::string cacheFolder; - AzFramework::StringFunc::Path::Join(GetTempFolder().c_str(), "testplatform", cacheFolder); - AzFramework::StringFunc::Path::Join(cacheFolder.c_str(), "testproject", cacheFolder); - - AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", cacheFolder.c_str()); + // Append Command Line override for the Project Cache Path + AZ::IO::Path cacheProjectRootFolder{ m_tempDir.GetDirectory() }; + auto projectCachePathOverride = FixedValueString::format(R"(--project-cache-path="%s")", cacheProjectRootFolder.c_str()); + auto projectPathOverride = FixedValueString{ R"(--project-path=AutomatedTesting)" }; + argContainer.push_back(projectCachePathOverride.data()); + argContainer.push_back(projectPathOverride.data()); + m_application = new ToolsTestApplication("MessageTest", aznumeric_caster(argContainer.size()), argContainer.data()); m_platformAddressedAssetCatalogManager = AZStd::make_unique(AzFramework::PlatformId::Invalid); } void TearDown() override { m_platformAddressedAssetCatalogManager.reset(); + delete m_application; } + ToolsTestApplication* m_application = nullptr; AZStd::unique_ptr m_platformAddressedAssetCatalogManager; + UnitTest::ScopedTemporaryDirectory m_tempDir; }; TEST_F(MessageTest, PlatformAddressedAssetCatalogManagerMessageTest_MessagesForwarded_CountsMatch) @@ -253,7 +222,7 @@ namespace UnitTest AZ_TEST_START_TRACE_SUPPRESSION; auto* mockCatalog = new ::testing::NiceMock(AzFramework::PlatformId::ANDROID_ID); - AZ_TEST_STOP_TRACE_SUPPRESSION(1); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); // Expected error not finding catalog AZStd::unique_ptr< ::testing::NiceMock> catalogHolder; catalogHolder.reset(mockCatalog); From 4ab7aa551d23093e10e1cf5efac7a522936f4592 Mon Sep 17 00:00:00 2001 From: Jeremy Ong Date: Fri, 6 Aug 2021 10:44:08 -0600 Subject: [PATCH 31/38] Resolve size_t conversion werrors Signed-off-by: Jeremy Ong --- Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp | 2 +- Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp | 5 +++-- Code/Framework/AzCore/AzCore/Task/TaskGraph.h | 4 ++-- Code/Framework/AzCore/AzCore/Task/TaskGraph.inl | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp index 293b88b2e6..13db590291 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp +++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp @@ -37,7 +37,7 @@ namespace AZ Task** cursor = m_successors.data(); - for (size_t i = 0; i != m_tasks.size(); ++i) + for (uint32_t i = 0; i != m_tasks.size(); ++i) { Task& task = m_tasks[i]; task.m_graph = this; diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp b/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp index 86e4f846d5..3fb93903c9 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp @@ -64,8 +64,9 @@ namespace AZ } m_compiledTaskGraph->m_waitEvent = waitEvent; - m_compiledTaskGraph->m_remaining = m_compiledTaskGraph->m_tasks.size() + (m_retained ? 1 : 0); - for (size_t i = 0; i != m_compiledTaskGraph->m_tasks.size(); ++i) + uint32_t taskCount = aznumeric_cast(m_compiledTaskGraph->m_tasks.size()); + m_compiledTaskGraph->m_remaining = taskCount + (m_retained ? 1 : 0); + for (uint32_t i = 0; i != taskCount; ++i) { m_compiledTaskGraph->m_tasks[i].Init(); } diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.h b/Code/Framework/AzCore/AzCore/Task/TaskGraph.h index d133593508..4b454c63de 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.h +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.h @@ -46,10 +46,10 @@ namespace AZ void PrecedesInternal(TaskToken& comesAfter); // Only the TaskGraph should be creating TaskToken - TaskToken(TaskGraph& parent, size_t index); + TaskToken(TaskGraph& parent, uint32_t index); TaskGraph& m_parent; - size_t m_index; + uint32_t m_index; }; // A TaskGraphEvent may be used to block until a task graph has finished executing. Usage diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl b/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl index 1971ddbbca..e0ac74ba9d 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl @@ -10,7 +10,7 @@ namespace AZ { - inline TaskToken::TaskToken(TaskGraph& parent, size_t index) + inline TaskToken::TaskToken(TaskGraph& parent, uint32_t index) : m_parent{ parent } , m_index{ index } { @@ -50,7 +50,7 @@ namespace AZ m_tasks.emplace_back(desc, AZStd::forward(lambda)); - return { *this, m_tasks.size() - 1 }; + return { *this, aznumeric_cast(m_tasks.size() - 1) }; } template From 8e3b25e60683375100e463850fe83b23bea515fe Mon Sep 17 00:00:00 2001 From: Vincent Liu <5900509+onecent1101@users.noreply.github.com> Date: Fri, 6 Aug 2021 12:03:36 -0700 Subject: [PATCH 33/38] [LYN-5268] Copy resource mapping tool to install target and add argument for log path (#2819) Updates to make resource mapping tool work with the installer. Ensure correct log path. --- Gems/AWSCore/Code/CMakeLists.txt | 6 + .../Code/Include/Private/AWSCoreInternalBus.h | 5 - .../Configuration/AWSCoreConfiguration.h | 1 - .../UI/AWSCoreResourceMappingToolAction.h | 22 +-- .../Configuration/AWSCoreConfiguration.cpp | 13 -- .../Source/Editor/UI/AWSCoreEditorMenu.cpp | 4 +- .../UI/AWSCoreResourceMappingToolAction.cpp | 134 ++++++------------ .../AWSDefaultCredentialHandlerTest.cpp | 1 - .../Tests/Editor/AWSCoreEditorManagerTest.cpp | 2 - .../AWSCoreEditorSystemComponentTest.cpp | 2 - .../Tests/Editor/UI/AWSCoreEditorMenuTest.cpp | 11 -- .../AWSCoreResourceMappingToolActionTest.cpp | 16 +-- .../AWSResourceMappingManagerTest.cpp | 1 - .../Code/Tools/ResourceMappingTool/README.md | 10 ++ .../resource_mapping_tool.py | 15 +- .../tests/unit/utils/test_file_utils.py | 17 +++ .../ResourceMappingTool/utils/file_utils.py | 13 +- 17 files changed, 119 insertions(+), 154 deletions(-) diff --git a/Gems/AWSCore/Code/CMakeLists.txt b/Gems/AWSCore/Code/CMakeLists.txt index 71271eb7ee..8489e38550 100644 --- a/Gems/AWSCore/Code/CMakeLists.txt +++ b/Gems/AWSCore/Code/CMakeLists.txt @@ -166,3 +166,9 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) ) endif() endif() + +install(DIRECTORY "Tools/ResourceMappingTool" + DESTINATION "Gems/AWSCore/Code/Tools" + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} + PATTERN "__pycache__" EXCLUDE +) diff --git a/Gems/AWSCore/Code/Include/Private/AWSCoreInternalBus.h b/Gems/AWSCore/Code/Include/Private/AWSCoreInternalBus.h index 243d40c974..b24fe8f898 100644 --- a/Gems/AWSCore/Code/Include/Private/AWSCoreInternalBus.h +++ b/Gems/AWSCore/Code/Include/Private/AWSCoreInternalBus.h @@ -34,11 +34,6 @@ namespace AWSCore //! @return The path of AWS resource mapping config file virtual AZStd::string GetResourceMappingConfigFilePath() const = 0; - //! GetResourceMappingConfigFolderPath - //! Get the path of AWS resource mapping config folder - //! @return The path of AWS resource mapping config folder - virtual AZStd::string GetResourceMappingConfigFolderPath() const = 0; - //! ReloadConfiguration //! Reload AWSCore configuration without restarting application virtual void ReloadConfiguration() = 0; diff --git a/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h b/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h index 1c4bb84cad..9834f9ca38 100644 --- a/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h +++ b/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h @@ -50,7 +50,6 @@ namespace AWSCore // AWSCoreInternalRequestBus interface implementation AZStd::string GetResourceMappingConfigFilePath() const override; - AZStd::string GetResourceMappingConfigFolderPath() const override; AZStd::string GetProfileName() const override; void ReloadConfiguration() override; diff --git a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreResourceMappingToolAction.h b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreResourceMappingToolAction.h index d462e2d435..1a4c428e68 100644 --- a/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreResourceMappingToolAction.h +++ b/Gems/AWSCore/Code/Include/Private/Editor/UI/AWSCoreResourceMappingToolAction.h @@ -7,9 +7,11 @@ */ #pragma once +#include #include #include +#include namespace AWSCore { @@ -17,22 +19,26 @@ namespace AWSCore : public QAction { public: + static constexpr const char AWSCoreResourceMappingToolActionName[] = "AWSCoreResourceMappingToolAction"; static constexpr const char ResourceMappingToolDirectoryPath[] = "Gems/AWSCore/Code/Tools/ResourceMappingTool"; + static constexpr const char ResourceMappingToolLogDirectoryPath[] = "user/log/"; static constexpr const char EngineWindowsPythonEntryScriptPath[] = "python/python.cmd"; - AWSCoreResourceMappingToolAction(const QString& text); + AWSCoreResourceMappingToolAction(const QString& text, QObject* parent = nullptr); + + void InitAWSCoreResourceMappingToolAction(); AZStd::string GetToolLaunchCommand() const; - AZStd::string GetToolLogPath() const; + AZStd::string GetToolLogFilePath() const; AZStd::string GetToolReadMePath() const; private: bool m_isDebug; - AZStd::string m_enginePythonEntryPath; - AZStd::string m_toolScriptPath; - AZStd::string m_toolQtBinDirectoryPath; - - AZStd::string m_toolLogPath; - AZStd::string m_toolReadMePath; + AZ::IO::Path m_enginePythonEntryPath; + AZ::IO::Path m_toolScriptPath; + AZ::IO::Path m_toolQtBinDirectoryPath; + AZ::IO::Path m_toolLogDirectoryPath; + AZ::IO::Path m_toolConfigDirectoryPath; + AZ::IO::Path m_toolReadMePath; }; } // namespace AWSCore diff --git a/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp b/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp index 46bce001e1..4653975a52 100644 --- a/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp +++ b/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp @@ -56,19 +56,6 @@ namespace AWSCore return configFilePath; } - AZStd::string AWSCoreConfiguration::GetResourceMappingConfigFolderPath() const - { - if (m_sourceProjectFolder.empty()) - { - AZ_Warning(AWSCoreConfigurationName, false, ProjectSourceFolderNotFoundErrorMessage); - return ""; - } - AZStd::string configFolderPath = AZStd::string::format( - "%s/%s", m_sourceProjectFolder.c_str(), AWSCoreResourceMappingConfigFolderName); - AzFramework::StringFunc::Path::Normalize(configFolderPath); - return configFolderPath; - } - void AWSCoreConfiguration::InitConfig() { InitSourceProjectFolderPath(); diff --git a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp index 02dc30d887..a2c89a5af3 100644 --- a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp +++ b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreEditorMenu.cpp @@ -80,7 +80,7 @@ namespace AWSCore { #ifdef AWSCORE_EDITOR_RESOURCE_MAPPING_TOOL_ENABLED AWSCoreResourceMappingToolAction* resourceMappingTool = - new AWSCoreResourceMappingToolAction(QObject::tr(AWSResourceMappingToolActionText)); + new AWSCoreResourceMappingToolAction(QObject::tr(AWSResourceMappingToolActionText), this); QObject::connect(resourceMappingTool, &QAction::triggered, this, [resourceMappingTool, this]() { AZStd::string launchCommand = resourceMappingTool->GetToolLaunchCommand(); @@ -109,7 +109,7 @@ namespace AWSCore if (!m_resourceMappingToolWatcher || !m_resourceMappingToolWatcher->IsProcessRunning()) { - AZStd::string resourceMappingToolLogPath = resourceMappingTool->GetToolLogPath(); + AZStd::string resourceMappingToolLogPath = resourceMappingTool->GetToolLogFilePath(); AZStd::string message = AZStd::string::format(AWSResourceMappingToolLogWarningText, resourceMappingToolLogPath.c_str()); QMessageBox::warning(QApplication::activeWindow(), "Warning", message.c_str(), QMessageBox::Ok); } diff --git a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreResourceMappingToolAction.cpp b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreResourceMappingToolAction.cpp index 2519ab87c3..858d30fa40 100644 --- a/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreResourceMappingToolAction.cpp +++ b/Gems/AWSCore/Code/Source/Editor/UI/AWSCoreResourceMappingToolAction.cpp @@ -6,133 +6,89 @@ * */ -#include #include -#include #include +#include #include namespace AWSCore { - AWSCoreResourceMappingToolAction::AWSCoreResourceMappingToolAction(const QString& text) - : QAction(text) + AWSCoreResourceMappingToolAction::AWSCoreResourceMappingToolAction(const QString& text, QObject* parent) + : QAction(text, parent) , m_isDebug(false) - , m_enginePythonEntryPath("") - , m_toolScriptPath("") - , m_toolQtBinDirectoryPath("") - , m_toolLogPath("") - , m_toolReadMePath("") { - auto engineRootPath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@engroot@"); - if (!engineRootPath) - { - AZ_Error("AWSCoreEditor", false, "Failed to determine engine root path."); - } - else - { - m_enginePythonEntryPath = AZStd::string::format("%s/%s", engineRootPath, EngineWindowsPythonEntryScriptPath); - AzFramework::StringFunc::Path::Normalize(m_enginePythonEntryPath); - if (!AZ::IO::SystemFile::Exists(m_enginePythonEntryPath.c_str())) - { - AZ_Error("AWSCoreEditor", false, "Failed to find engine python entry at %s.", m_enginePythonEntryPath.c_str()); - m_enginePythonEntryPath.clear(); - } + InitAWSCoreResourceMappingToolAction(); + } - m_toolScriptPath = AZStd::string::format("%s/%s/resource_mapping_tool.py", engineRootPath, ResourceMappingToolDirectoryPath); - AzFramework::StringFunc::Path::Normalize(m_toolScriptPath); - if (!AZ::IO::SystemFile::Exists(m_toolScriptPath.c_str())) - { - AZ_Error("AWSCoreEditor", false, "Failed to find ResourceMappingTool python script at %s.", m_toolScriptPath.c_str()); - m_toolScriptPath.clear(); - } + void AWSCoreResourceMappingToolAction::InitAWSCoreResourceMappingToolAction() + { + AZ::IO::Path engineRootPath = AZ::IO::PathView(AZ::Utils::GetEnginePath()); + m_enginePythonEntryPath = (engineRootPath / EngineWindowsPythonEntryScriptPath).LexicallyNormal(); + m_toolScriptPath = (engineRootPath / ResourceMappingToolDirectoryPath / "resource_mapping_tool.py").LexicallyNormal(); + m_toolReadMePath = (engineRootPath / ResourceMappingToolDirectoryPath / "README.md").LexicallyNormal(); - m_toolLogPath = AZStd::string::format("%s/%s/resource_mapping_tool.log", engineRootPath, ResourceMappingToolDirectoryPath); - AzFramework::StringFunc::Path::Normalize(m_toolLogPath); - if (!AZ::IO::SystemFile::Exists(m_toolLogPath.c_str())) - { - AZ_Error("AWSCoreEditor", false, "Failed to find ResourceMappingTool log file at %s.", m_toolLogPath.c_str()); - m_toolLogPath.clear(); - } + AZ::IO::Path projectPath = AZ::IO::PathView(AZ::Utils::GetProjectPath()); + m_toolLogDirectoryPath = (projectPath / ResourceMappingToolLogDirectoryPath).LexicallyNormal(); + m_toolConfigDirectoryPath = (projectPath / AWSCoreConfiguration::AWSCoreResourceMappingConfigFolderName).LexicallyNormal(); - m_toolReadMePath = AZStd::string::format("%s/%s/README.md", engineRootPath, ResourceMappingToolDirectoryPath); - AzFramework::StringFunc::Path::Normalize(m_toolReadMePath); - if (!AZ::IO::SystemFile::Exists(m_toolReadMePath.c_str())) - { - AZ_Error("AWSCoreEditor", false, "Failed to find ResourceMappingTool README file at %s.", m_toolReadMePath.c_str()); - m_toolReadMePath.clear(); - } + AZ::IO::Path executablePath = AZ::IO::PathView(AZ::Utils::GetExecutableDirectory()); + m_toolQtBinDirectoryPath = (executablePath / "AWSCoreEditorQtBin").LexicallyNormal(); - char executablePath[AZ_MAX_PATH_LEN]; - auto result = AZ::Utils::GetExecutablePath(executablePath, AZ_MAX_PATH_LEN); - if (result.m_pathStored != AZ::Utils::ExecutablePathResult::Success) - { - AZ_Error("AWSCoreEditor", false, "Failed to find engine executable path."); - } - else - { - if (result.m_pathIncludesFilename) - { - // Remove the file name if it exists, and keep the parent folder only - char* lastSeparatorAddress = strrchr(executablePath, AZ_CORRECT_FILESYSTEM_SEPARATOR); - if (lastSeparatorAddress) - { - *lastSeparatorAddress = '\0'; - } - } - } - - AZStd::string binDirectoryPath(executablePath); - auto lastSeparator = binDirectoryPath.find_last_of(AZ_CORRECT_FILESYSTEM_SEPARATOR); - if (lastSeparator != AZStd::string::npos) - { - m_isDebug = binDirectoryPath.substr(lastSeparator).contains("debug"); - } - - m_toolQtBinDirectoryPath = AZStd::string::format("%s/%s", binDirectoryPath.c_str(), "AWSCoreEditorQtBin"); - AzFramework::StringFunc::Path::Normalize(m_toolQtBinDirectoryPath); - if (!AZ::IO::SystemFile::Exists(m_toolQtBinDirectoryPath.c_str())) - { - AZ_Error("AWSCoreEditor", false, "Failed to find ResourceMappingTool Qt binaries at %s.", m_toolQtBinDirectoryPath.c_str()); - m_toolQtBinDirectoryPath.clear(); - } - } + m_isDebug = AZStd::string_view(AZ_BUILD_CONFIGURATION_TYPE) == "debug"; } AZStd::string AWSCoreResourceMappingToolAction::GetToolLaunchCommand() const { - if (m_enginePythonEntryPath.empty() || m_toolScriptPath.empty() || m_toolQtBinDirectoryPath.empty()) + if (!AZ::IO::SystemFile::Exists(m_enginePythonEntryPath.c_str()) || + !AZ::IO::SystemFile::Exists(m_toolScriptPath.c_str()) || + !AZ::IO::SystemFile::Exists(m_toolQtBinDirectoryPath.c_str()) || + !AZ::IO::SystemFile::Exists(m_toolConfigDirectoryPath.c_str()) || + !AZ::IO::SystemFile::Exists(m_toolLogDirectoryPath.c_str())) { + AZ_Error(AWSCoreResourceMappingToolActionName, false, + "Expected parameter for tool launch command is invalid, engine python path: %s, tool script path: %s, tool qt binaries path: %s, tool config path: %s, tool log path: %s", + m_enginePythonEntryPath.c_str(), m_toolScriptPath.c_str(), m_toolQtBinDirectoryPath.c_str(), m_toolConfigDirectoryPath.c_str(), m_toolLogDirectoryPath.c_str()); return ""; } AZStd::string profileName = "default"; AWSCoreInternalRequestBus::BroadcastResult(profileName, &AWSCoreInternalRequests::GetProfileName); - AZStd::string configPath = ""; - AWSCoreInternalRequestBus::BroadcastResult(configPath, &AWSCoreInternalRequests::GetResourceMappingConfigFolderPath); - if (m_isDebug) { return AZStd::string::format( - "%s debug %s --binaries_path %s --debug --profile %s --config_path %s", m_enginePythonEntryPath.c_str(), - m_toolScriptPath.c_str(), m_toolQtBinDirectoryPath.c_str(), profileName.c_str(), configPath.c_str()); + "\"%s\" debug -B \"%s\" --binaries-path \"%s\" --debug --profile \"%s\" --config-path \"%s\" --log-path \"%s\"", + m_enginePythonEntryPath.c_str(), m_toolScriptPath.c_str(), m_toolQtBinDirectoryPath.c_str(), + profileName.c_str(), m_toolConfigDirectoryPath.c_str(), m_toolLogDirectoryPath.c_str()); } else { return AZStd::string::format( - "%s %s --binaries_path %s --profile %s --config_path %s", m_enginePythonEntryPath.c_str(), - m_toolScriptPath.c_str(), m_toolQtBinDirectoryPath.c_str(), profileName.c_str(), configPath.c_str()); + "\"%s\" -B \"%s\" --binaries-path \"%s\" --profile \"%s\" --config-path \"%s\" --log-path \"%s\"", + m_enginePythonEntryPath.c_str(), m_toolScriptPath.c_str(), m_toolQtBinDirectoryPath.c_str(), + profileName.c_str(), m_toolConfigDirectoryPath.c_str(), m_toolLogDirectoryPath.c_str()); } } - AZStd::string AWSCoreResourceMappingToolAction::GetToolLogPath() const + AZStd::string AWSCoreResourceMappingToolAction::GetToolLogFilePath() const { - return m_toolLogPath; + AZ::IO::Path toolLogFilePath = (m_toolLogDirectoryPath / "resource_mapping_tool.log").LexicallyNormal(); + if (!AZ::IO::SystemFile::Exists(toolLogFilePath.c_str())) + { + AZ_Error(AWSCoreResourceMappingToolActionName, false, "Invalid tool log file path: %s", toolLogFilePath.c_str()); + return ""; + } + return toolLogFilePath.Native(); } AZStd::string AWSCoreResourceMappingToolAction::GetToolReadMePath() const { - return m_toolReadMePath; + if (!AZ::IO::SystemFile::Exists(m_toolReadMePath.c_str())) + { + AZ_Error(AWSCoreResourceMappingToolActionName, false, "Invalid tool readme path: %s", m_toolReadMePath.c_str()); + return ""; + } + return m_toolReadMePath.Native(); } } // namespace AWSCore diff --git a/Gems/AWSCore/Code/Tests/Credential/AWSDefaultCredentialHandlerTest.cpp b/Gems/AWSCore/Code/Tests/Credential/AWSDefaultCredentialHandlerTest.cpp index e1acd3e3d9..b3e2ec5738 100644 --- a/Gems/AWSCore/Code/Tests/Credential/AWSDefaultCredentialHandlerTest.cpp +++ b/Gems/AWSCore/Code/Tests/Credential/AWSDefaultCredentialHandlerTest.cpp @@ -73,7 +73,6 @@ public: // AWSCoreInternalRequestBus interface implementation AZStd::string GetProfileName() const override { return m_profileName; } AZStd::string GetResourceMappingConfigFilePath() const override { return ""; } - AZStd::string GetResourceMappingConfigFolderPath() const override { return ""; } void ReloadConfiguration() override {} std::shared_ptr m_environmentCredentialsProviderMock; diff --git a/Gems/AWSCore/Code/Tests/Editor/AWSCoreEditorManagerTest.cpp b/Gems/AWSCore/Code/Tests/Editor/AWSCoreEditorManagerTest.cpp index e2ace4212f..9a0063e5a9 100644 --- a/Gems/AWSCore/Code/Tests/Editor/AWSCoreEditorManagerTest.cpp +++ b/Gems/AWSCore/Code/Tests/Editor/AWSCoreEditorManagerTest.cpp @@ -34,8 +34,6 @@ class AWSCoreEditorManagerTest TEST_F(AWSCoreEditorManagerTest, AWSCoreEditorManager_Constructor_HaveExpectedUIResourcesCreated) { - AZ_TEST_START_TRACE_SUPPRESSION; AWSCoreEditorManager testManager; - AZ_TEST_STOP_TRACE_SUPPRESSION(1); // expect the above have thrown an AZ_Error EXPECT_TRUE(testManager.GetAWSCoreEditorMenu()); } diff --git a/Gems/AWSCore/Code/Tests/Editor/AWSCoreEditorSystemComponentTest.cpp b/Gems/AWSCore/Code/Tests/Editor/AWSCoreEditorSystemComponentTest.cpp index b722c83950..07a6c8ae08 100644 --- a/Gems/AWSCore/Code/Tests/Editor/AWSCoreEditorSystemComponentTest.cpp +++ b/Gems/AWSCore/Code/Tests/Editor/AWSCoreEditorSystemComponentTest.cpp @@ -41,9 +41,7 @@ class AWSCoreEditorSystemComponentTest m_entity = aznew AZ::Entity(); m_coreEditorSystemsComponent.reset(m_entity->CreateComponent()); - AZ_TEST_START_TRACE_SUPPRESSION; m_entity->Init(); - AZ_TEST_STOP_TRACE_SUPPRESSION(1); // expect the above have thrown an AZ_Error m_entity->Activate(); } diff --git a/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreEditorMenuTest.cpp b/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreEditorMenuTest.cpp index 295818c44c..40ed5b53b5 100644 --- a/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreEditorMenuTest.cpp +++ b/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreEditorMenuTest.cpp @@ -42,18 +42,9 @@ class AWSCoreEditorMenuTest } }; -TEST_F(AWSCoreEditorMenuTest, AWSCoreEditorMenu_NoEngineRootFolder_ExpectOneError) -{ - AZ_TEST_START_TRACE_SUPPRESSION; - AWSCoreEditorMenu testMenu("dummy title"); - AZ_TEST_STOP_TRACE_SUPPRESSION(1); // expect the above have thrown an AZ_Error -} - TEST_F(AWSCoreEditorMenuTest, AWSCoreEditorMenu_GetAllActions_GetExpectedNumberOfActions) { - AZ_TEST_START_TRACE_SUPPRESSION; AWSCoreEditorMenu testMenu("dummy title"); - AZ_TEST_STOP_TRACE_SUPPRESSION(1); // expect the above have thrown an AZ_Error QList actualActions = testMenu.actions(); #ifdef AWSCORE_EDITOR_RESOURCE_MAPPING_TOOL_ENABLED @@ -65,9 +56,7 @@ TEST_F(AWSCoreEditorMenuTest, AWSCoreEditorMenu_GetAllActions_GetExpectedNumberO TEST_F(AWSCoreEditorMenuTest, AWSCoreEditorMenu_BroadcastFeatureGemsAreEnabled_CorrespondingActionsAreEnabled) { - AZ_TEST_START_TRACE_SUPPRESSION; AWSCoreEditorMenu testMenu("dummy title"); - AZ_TEST_STOP_TRACE_SUPPRESSION(1); // expect the above have thrown an AZ_Error AWSCoreEditorRequestBus::Broadcast(&AWSCoreEditorRequests::SetAWSClientAuthEnabled); AWSCoreEditorRequestBus::Broadcast(&AWSCoreEditorRequests::SetAWSMetricsEnabled); diff --git a/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreResourceMappingToolActionTest.cpp b/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreResourceMappingToolActionTest.cpp index 4a027ec920..81e7eda21e 100644 --- a/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreResourceMappingToolActionTest.cpp +++ b/Gems/AWSCore/Code/Tests/Editor/UI/AWSCoreResourceMappingToolActionTest.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include @@ -24,7 +25,6 @@ class AWSCoreResourceMappingToolActionTest { AWSCoreEditorUIFixture::SetUp(); AWSCoreFixture::SetUp(); - m_localFileIO->SetAlias("@engroot@", "dummy engine root"); } void TearDown() override @@ -34,20 +34,12 @@ class AWSCoreResourceMappingToolActionTest } }; -TEST_F(AWSCoreResourceMappingToolActionTest, AWSCoreResourceMappingToolAction_NoEngineRootFolder_ExpectOneError) +TEST_F(AWSCoreResourceMappingToolActionTest, AWSCoreResourceMappingToolAction_NoEngineRootPath_ExpectErrorsAndResult) { - m_localFileIO->ClearAlias("@engroot@"); - AZ_TEST_START_TRACE_SUPPRESSION; AWSCoreResourceMappingToolAction testAction("dummy title"); - AZ_TEST_STOP_TRACE_SUPPRESSION(1); // expect the above have thrown an AZ_Error -} - -TEST_F(AWSCoreResourceMappingToolActionTest, AWSCoreResourceMappingToolAction_UnableToFindExpectedFileOrFolder_ExpectFiveErrorsAndEmptyResult) -{ AZ_TEST_START_TRACE_SUPPRESSION; - AWSCoreResourceMappingToolAction testAction("dummy title"); - AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; EXPECT_TRUE(testAction.GetToolLaunchCommand() == ""); - EXPECT_TRUE(testAction.GetToolLogPath() == ""); + EXPECT_TRUE(testAction.GetToolLogFilePath() == ""); EXPECT_TRUE(testAction.GetToolReadMePath() == ""); + AZ_TEST_STOP_TRACE_SUPPRESSION(3); } diff --git a/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp b/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp index ebd13fbcb8..557fbc820c 100644 --- a/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp +++ b/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp @@ -115,7 +115,6 @@ public: // AWSCoreInternalRequestBus interface implementation AZStd::string GetProfileName() const override { return ""; } AZStd::string GetResourceMappingConfigFilePath() const override { return m_normalizedConfigFilePath; } - AZStd::string GetResourceMappingConfigFolderPath() const override { return m_normalizedConfigFolderPath; } void ReloadConfiguration() override { m_reloadConfigurationCounter++; } AZStd::unique_ptr m_resourceMappingManager; diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/README.md b/Gems/AWSCore/Code/Tools/ResourceMappingTool/README.md index 78a8856288..e09ecc281f 100644 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/README.md +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/README.md @@ -89,3 +89,13 @@ you can create the virtualenv manually. ``` $ python3 resource_mapping_tool.py ``` +## Tool Arguments +* `--binaries-path` **[Optional]** Path to QT Binaries necessary for PySide, + required if launching tool with engine python environment. +* `--config-path` **[Optional]** Path to resource mapping config directory, + if not provided tool will use current directory. +* `--debug` **[Optional]** Execute on debug mode to enable DEBUG logging level. +* `--log-path` **[Optional]** Path to resource mapping tool logging directory, + if not provided tool will store logging under tool source code directory. +* `--profile` **[Optional]** Named AWS profile to use for querying AWS resources, + if not provided tool will use `default` aws profile. \ No newline at end of file diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py index ec27fb3cf7..2351fd001b 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py @@ -9,15 +9,16 @@ from argparse import (ArgumentParser, Namespace) import logging import sys -from utils import aws_utils from utils import environment_utils from utils import file_utils # arguments setup argument_parser: ArgumentParser = ArgumentParser() -argument_parser.add_argument('--binaries_path', help='Path to QT Binaries necessary for PySide.') -argument_parser.add_argument('--config_path', help='Path to resource mapping config directory.') +argument_parser.add_argument('--binaries-path', help='Path to QT Binaries necessary for PySide.') +argument_parser.add_argument('--config-path', help='Path to resource mapping config directory.') argument_parser.add_argument('--debug', action='store_true', help='Execute on debug mode to enable DEBUG logging level') +argument_parser.add_argument('--log-path', help='Path to resource mapping tool logging directory ' + '(if not provided, logging file will be located at tool directory)') argument_parser.add_argument('--profile', default='default', help='Named AWS profile to use for querying AWS resources') arguments: Namespace = argument_parser.parse_args() @@ -25,8 +26,11 @@ arguments: Namespace = argument_parser.parse_args() logging_level: int = logging.INFO if arguments.debug: logging_level = logging.DEBUG -logging_path: str = file_utils.join_path(file_utils.get_parent_directory_path(__file__), - 'resource_mapping_tool.log') +logging_path: str = file_utils.join_path(file_utils.get_parent_directory_path(__file__), 'resource_mapping_tool.log') +if arguments.log_path: + normalized_logging_path: str = file_utils.normalize_file_path(arguments.log_path, False) + if normalized_logging_path and file_utils.create_directory(normalized_logging_path): + logging_path = file_utils.join_path(normalized_logging_path, 'resource_mapping_tool.log') logging.basicConfig(filename=logging_path, filemode='w', level=logging_level, format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s', datefmt='%H:%M:%S') logging.getLogger('boto3').setLevel(logging.CRITICAL) @@ -34,6 +38,7 @@ logging.getLogger('botocore').setLevel(logging.CRITICAL) logging.getLogger('s3transfer').setLevel(logging.CRITICAL) logging.getLogger('urllib3').setLevel(logging.CRITICAL) logger = logging.getLogger(__name__) +logger.info(f"Using {logging_path} for logging.") if __name__ == "__main__": if arguments.binaries_path and not environment_utils.is_qt_linked(): diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/utils/test_file_utils.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/utils/test_file_utils.py index ea0630af49..fe0f1754f3 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/utils/test_file_utils.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/utils/test_file_utils.py @@ -44,6 +44,23 @@ class TestFileUtils(TestCase): mocked_path.exists.assert_called_once() assert not actual_result + def test_create_directory_return_true(self) -> None: + mocked_path: MagicMock = self._mock_path.return_value + + actual_result: bool = file_utils.create_directory("dummy") + self._mock_path.assert_called_once() + mocked_path.mkdir.assert_called_once() + assert actual_result + + def test_create_directory_return_false_when_exception_raised(self) -> None: + mocked_path: MagicMock = self._mock_path.return_value + mocked_path.mkdir.side_effect = FileExistsError() + + actual_result: bool = file_utils.create_directory("dummy") + self._mock_path.assert_called_once() + mocked_path.mkdir.assert_called_once() + assert not actual_result + def test_get_current_directory_path_return_expected_path_name(self) -> None: self._mock_path.cwd.return_value = TestFileUtils._expected_path_name diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/file_utils.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/file_utils.py index 024c620948..9e5dd9617b 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/file_utils.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/file_utils.py @@ -20,6 +20,15 @@ def check_path_exists(file_path: str) -> bool: return pathlib.Path(file_path).exists() +def create_directory(dir_path: str) -> bool: + try: + pathlib.Path(dir_path).mkdir(parents=True, exist_ok=True) + return True + except FileExistsError: + logger.warning(f"Failed to create directory at {dir_path}") + return False + + def get_current_directory_path() -> str: return str(pathlib.Path.cwd()) @@ -40,10 +49,10 @@ def find_files_with_suffix_under_directory(dir_path: str, suffix: str) -> List[s return results -def normalize_file_path(file_path: str) -> str: +def normalize_file_path(file_path: str, strict: bool = True) -> str: if file_path: try: - return str(pathlib.Path(file_path).resolve(True)) + return str(pathlib.Path(file_path).resolve(strict)) except (FileNotFoundError, RuntimeError): logger.warning(f"Failed to normalize file path {file_path}, return empty string instead") return "" From 4f9382e8c63538760d6a5d026590401a68137861 Mon Sep 17 00:00:00 2001 From: Shirang Jia Date: Fri, 6 Aug 2021 12:52:04 -0700 Subject: [PATCH 34/38] Include build failure root cause in email notification (#2491) (#2888) Signed-off-by: shiranj --- scripts/build/Jenkins/Jenkinsfile | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 9aa09e136e..60326eb0d2 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -695,13 +695,19 @@ finally { ) } node('controller') { - step([ - $class: 'Mailer', - notifyEveryUnstableBuild: true, - recipients: emailextrecipients([ + if("${currentBuild.currentResult}" == "SUCCESS") { + emailBody = "${BUILD_URL}\nSuccess!" + } else { + buildFailure = tm('${BUILD_FAILURE_ANALYZER}') + emailBody = "${BUILD_URL}\n${buildFailure}!" + } + emailext ( + body: "${emailBody}", + subject: "${currentBuild.currentResult}: ${JOB_NAME} - Build # ${BUILD_NUMBER}", + recipientProviders: [ [$class: 'RequesterRecipientProvider'] - ]) - ]) + ] + ) } } catch(Exception e) { } From 9a8a411a0ba5f68c93f35b9c82138c12db299f94 Mon Sep 17 00:00:00 2001 From: Scott Romero <24445312+AMZN-ScottR@users.noreply.github.com> Date: Fri, 6 Aug 2021 13:23:14 -0700 Subject: [PATCH 35/38] [development] removal of unused and low stakes code related to Cry-threading (#2896) Removal highlights include: - File indexer (used CryThread<>) linked to long gone asset browser - Producer/consumer queues from CryMT - set/vector/CLocklessPointerQueue containers also from CryMT - Cry interlocked linked list and _InterlockedCompareExchange128 - CryThread type - SAtomicVar types - CryAutoSet type - Various unused lock types -- AutoLockModify -- AutoLockRead -- CryOptionalAutoLock -- CryReadModifyLock -- CryRWLock -- ReadLock -- ReadLockCond -- WriteAfterReadLock - Misc. unused functions -- CryInterLockedAdd (not to be confused with CryInterlockedAdd, using a lower case "locked") -- CryInterlockedExchange64 (which was only defined for unix platforms) -- SpinLock -- JobSpinLock -- AtomicAdd -- JobAtomicAdd Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- Code/Editor/CryEdit.cpp | 19 - .../PerforcePlugin/PerforceSourceControl.cpp | 2 +- Code/Editor/Util/IndexedFiles.cpp | 202 ------ Code/Editor/Util/IndexedFiles.h | 176 ----- Code/Editor/editor_lib_files.cmake | 2 - Code/Legacy/CryCommon/AndroidSpecific.h | 11 - Code/Legacy/CryCommon/CryThread.h | 638 ------------------ .../Legacy/CryCommon/CryThreadImpl_pthreads.h | 191 ------ Code/Legacy/CryCommon/CryThreadImpl_windows.h | 250 ------- Code/Legacy/CryCommon/CryThread_pthreads.h | 233 ------- Code/Legacy/CryCommon/CryThread_windows.h | 78 --- Code/Legacy/CryCommon/Linux_Win32Wrapper.h | 3 - Code/Legacy/CryCommon/MultiThread.h | 318 --------- .../Legacy/CryCommon/MultiThread_Containers.h | 315 --------- Code/Legacy/CryCommon/WinBase.cpp | 19 - Code/Legacy/CryCommon/iOSSpecific.h | 4 - 16 files changed, 1 insertion(+), 2460 deletions(-) delete mode 100644 Code/Editor/Util/IndexedFiles.cpp delete mode 100644 Code/Editor/Util/IndexedFiles.h diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index efeb7cdd19..cd1dd4fe47 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -127,7 +127,6 @@ AZ_POP_DISABLE_WARNING #include "Util/AutoDirectoryRestoreFileDialog.h" #include "Util/EditorAutoLevelLoadTest.h" -#include "Util/IndexedFiles.h" #include "AboutDialog.h" #include @@ -1715,18 +1714,6 @@ BOOL CCryEditApp::InitInstance() if (IsInRegularEditorMode()) { - CIndexedFiles::Create(); - - if (gEnv->pConsole->GetCVar("ed_indexfiles")->GetIVal()) - { - Log("Started game resource files indexing..."); - CIndexedFiles::StartFileIndexing(); - } - else - { - Log("Game resource files indexing is disabled."); - } - // QuickAccessBar creation should be before m_pMainWnd->SetFocus(), // since it receives the focus at creation time. It brakes MainFrame key accelerators. m_pQuickAccessBar = new CQuickAccessBar; @@ -2163,12 +2150,6 @@ int CCryEditApp::ExitInstance(int exitCode) } } - if (IsInRegularEditorMode()) - { - CIndexedFiles::AbortFileIndexing(); - CIndexedFiles::Destroy(); - } - if (GetIEditor() && !GetIEditor()->IsInMatEditMode()) { //Nobody seems to know in what case that kind of exit can happen so instrumented to see if it happens at all diff --git a/Code/Editor/Plugins/PerforcePlugin/PerforceSourceControl.cpp b/Code/Editor/Plugins/PerforcePlugin/PerforceSourceControl.cpp index aec613b8df..581f9a576d 100644 --- a/Code/Editor/Plugins/PerforcePlugin/PerforceSourceControl.cpp +++ b/Code/Editor/Plugins/PerforcePlugin/PerforceSourceControl.cpp @@ -56,7 +56,7 @@ void CPerforceSourceControl::ShowSettings() void CPerforceSourceControl::SetSourceControlState(SourceControlState state) { - AUTO_LOCK(g_cPerforceValues); + CryAutoLock lock(g_cPerforceValues); switch (state) { diff --git a/Code/Editor/Util/IndexedFiles.cpp b/Code/Editor/Util/IndexedFiles.cpp deleted file mode 100644 index ae777abb49..0000000000 --- a/Code/Editor/Util/IndexedFiles.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -// Description : Tagged files database for 'SmartFileOpen' dialog - -#include "EditorDefs.h" - -#include "IndexedFiles.h" - -volatile TIntAtomic CIndexedFiles::s_bIndexingDone; -CIndexedFiles* CIndexedFiles::s_pIndexedFiles = nullptr; - -bool CIndexedFiles::m_startedFileIndexing = false; - -void CIndexedFiles::Initialize(const QString& path, IFileUtil::ScanDirectoryUpdateCallBack updateCB) -{ - m_files.clear(); - m_pathToIndex.clear(); - m_tags.clear(); - m_rootPath = path; - - bool anyFiles = CFileUtil::ScanDirectory(path, "*.*", m_files, true, true, updateCB); - - if (anyFiles == false) - { - m_files.clear(); - return; - } - - if (updateCB) - { - updateCB("Parsing & tagging..."); - } - - for (int i = 0; i < m_files.size(); ++i) - { - m_pathToIndex[m_files[i].filename] = i; - } - - PrepareTagTable(); - - InvokeUpdateCallbacks(); -} - -void CIndexedFiles::AddFile(const IFileUtil::FileDesc& path) -{ - assert(m_pathToIndex.find(path.filename) == m_pathToIndex.end()); - m_files.push_back(path); - m_pathToIndex[path.filename] = m_files.size() - 1; - QStringList tags; - GetTags(tags, path.filename); - for (int k = 0; k < tags.size(); ++k) - { - m_tags[tags[k]].insert(m_files.size() - 1); - } -} - -void CIndexedFiles::RemoveFile(const QString& path) -{ - if (m_pathToIndex.find(path) == m_pathToIndex.end()) - { - return; - } - std::map::iterator itr = m_pathToIndex.find(path); - int index = itr->second; - m_pathToIndex.erase(itr); - m_files.erase(m_files.begin() + index); - QStringList tags; - GetTags(tags, path); - for (int k = 0; k < tags.size(); ++k) - { - m_tags[tags[k]].erase(index); - } -} - -void CIndexedFiles::Refresh(const QString& path, bool recursive) -{ - IFileUtil::FileArray files; - bool anyFiles = CFileUtil::ScanDirectory(m_rootPath, Path::Make(path, "*.*"), files, recursive, recursive ? true : false); - - if (anyFiles == false) - { - return; - } - - for (int i = 0; i < files.size(); ++i) - { - if (m_pathToIndex.find(files[i].filename) == m_pathToIndex.end()) - { - AddFile(files[i]); - } - } - - InvokeUpdateCallbacks(); -} - -void CIndexedFiles::GetFilesWithTags(IFileUtil::FileArray& files, const QStringList& tags) const -{ - files.clear(); - if (tags.empty()) - { - return; - } - int_set candidates; - TagTable::const_iterator i; - // Gets candidate files from the first tag. - for (i = m_tags.begin(); i != m_tags.end(); ++i) - { - if (i->first.startsWith(tags[0])) - { - candidates.insert(i->second.begin(), i->second.end()); - } - } - // Reduces the candidates further using additional tags, if any. - for (int k = 1; k < tags.size(); ++k) - { - // Gathers the filter set. - int_set filter; - for (i = m_tags.begin(); i != m_tags.end(); ++i) - { - if (i->first.startsWith(tags[k])) - { - filter.insert(i->second.begin(), i->second.end()); - } - } - - // Filters the candidates using it. - for (int_set::iterator m = candidates.begin(); m != candidates.end(); ) - { - if (filter.find(*m) == filter.end()) - { - int_set::iterator target = m; - ++m; - candidates.erase(target); - } - else - { - ++m; - } - } - } - // Outputs the result. - files.reserve(candidates.size()); - for (int_set::const_iterator m = candidates.begin(); m != candidates.end(); ++m) - { - files.push_back(m_files[*m]); - } -} - -void CIndexedFiles::GetTags(QStringList& tags, const QString& path) const -{ - tags = path.split(QRegularExpression(QStringLiteral(R"([\\/.])")), Qt::SkipEmptyParts); -} - -void CIndexedFiles::GetTagsOfPrefix(QStringList& tags, const QString& prefix) const -{ - tags.clear(); - TagTable::const_iterator i; - for (i = m_tags.begin(); i != m_tags.end(); ++i) - { - if (i->first.startsWith(prefix)) - { - tags.push_back(i->first); - } - } -} - -void CIndexedFiles::PrepareTagTable() -{ - QStringList tags; - for (int i = 0; i < m_files.size(); ++i) - { - GetTags(tags, m_files[i].filename); - for (int k = 0; k < tags.size(); ++k) - { - m_tags[tags[k]].insert(i); - } - } -} - -void CIndexedFiles::AddUpdateCallback(std::function updateCallback) -{ - CryAutoLock lock(m_updateCallbackMutex); - - m_updateCallbacks.push_back(updateCallback); -} - -void CIndexedFiles::InvokeUpdateCallbacks() -{ - CryAutoLock lock(m_updateCallbackMutex); - - for (auto updateCallback : m_updateCallbacks) - { - updateCallback(); - } -} diff --git a/Code/Editor/Util/IndexedFiles.h b/Code/Editor/Util/IndexedFiles.h deleted file mode 100644 index e23c0ea827..0000000000 --- a/Code/Editor/Util/IndexedFiles.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -// Description : Tagged files database for 'SmartFileOpen' dialog -// -// Notice : Refer SmartFileOpenDialog h - - -#ifndef CRYINCLUDE_EDITOR_UTIL_INDEXEDFILES_H -#define CRYINCLUDE_EDITOR_UTIL_INDEXEDFILES_H -#pragma once - - -#include "FileUtil.h" -#include - -class CIndexedFiles -{ - friend class CFileIndexingThread; -public: - static CIndexedFiles& GetDB() - { - if (!s_pIndexedFiles) - { - assert(!"CIndexedFiles not created! Make sure you use CIndexedFiles::GetDB() after CIndexedFiles::StartFileIndexing() is called."); - } - assert(s_pIndexedFiles); - return *s_pIndexedFiles; - } - - static bool HasFileIndexingDone() - { return s_bIndexingDone > 0; } - - static void Create() - { - assert(!s_pIndexedFiles); - s_pIndexedFiles = new CIndexedFiles; - } - - static void Destroy() - { - SAFE_DELETE(s_pIndexedFiles); - } - - static void StartFileIndexing() - { - assert(s_bIndexingDone == 0); - assert(s_pIndexedFiles); - - if (!s_pIndexedFiles) - { - return; - } - - GetFileIndexingThread().Start(-1, "FileIndexing"); - m_startedFileIndexing = true; - } - - static void AbortFileIndexing() - { - if (!m_startedFileIndexing) - { - return; - } - - if (HasFileIndexingDone() == false) - { - GetFileIndexingThread().Abort(); - } - m_startedFileIndexing = false; - } - - static void RegisterCallback(std::function callback) - { - assert(s_pIndexedFiles); - if (!s_pIndexedFiles) - { - return; - } - - s_pIndexedFiles->AddUpdateCallback(callback); - } - -public: - void Initialize(const QString& path, IFileUtil::ScanDirectoryUpdateCallBack updateCB = nullptr); - - // Adds a new file to the database. - void AddFile(const IFileUtil::FileDesc& path); - // Removes a no-longer-existing file from the database. - void RemoveFile(const QString& path); - // Refreshes this database for the subdirectory. - void Refresh(const QString& path, bool recursive = true); - - void GetFilesWithTags(IFileUtil::FileArray& files, const QStringList& tags) const; - - //! This method returns all the tags which start with a given prefix. - //! It is useful for the tag auto-completion. - void GetTagsOfPrefix(QStringList& tags, const QString& prefix) const; - - uint32 GetTotalCount() const - { return (uint32)m_files.size(); } - -private: - static bool m_startedFileIndexing; - - std::vector > m_updateCallbacks; - IFileUtil::FileArray m_files; - std::map m_pathToIndex; - typedef std::set > int_set; - typedef std::map > TagTable; - TagTable m_tags; - QString m_rootPath; - - void GetTags(QStringList& tags, const QString& path) const; - void PrepareTagTable(); - - CryMutex m_updateCallbackMutex; - - void AddUpdateCallback(std::function updateCallback); - void InvokeUpdateCallbacks(); - - // A done flag for the background file indexing - static volatile TIntAtomic s_bIndexingDone; - // A thread for the background file indexing - class CFileIndexingThread - : public CryThread - { - public: - virtual void Run() - { - CIndexedFiles::GetDB().Initialize("@assets@", CallBack); - CryInterlockedAdd(CIndexedFiles::s_bIndexingDone.Addr(), 1); - } - - CFileIndexingThread() - : m_abort(false) {} - - void Abort() - { - m_abort = true; - WaitForThread(); - } - - virtual ~CFileIndexingThread() - { - Abort(); - } - private: - bool m_abort; - static bool CallBack([[maybe_unused]] const QString& msg) - { - if (CIndexedFiles::GetFileIndexingThread().m_abort) - { - return false; - } - return true; - } - }; - - static CFileIndexingThread& GetFileIndexingThread() - { - static CFileIndexingThread s_fileIndexingThread; - - return s_fileIndexingThread; - } - - // A global database for tagged files - static CIndexedFiles* s_pIndexedFiles; -}; -#endif // CRYINCLUDE_EDITOR_UTIL_INDEXEDFILES_H diff --git a/Code/Editor/editor_lib_files.cmake b/Code/Editor/editor_lib_files.cmake index 386b495faa..d59edfe6a8 100644 --- a/Code/Editor/editor_lib_files.cmake +++ b/Code/Editor/editor_lib_files.cmake @@ -724,8 +724,6 @@ set(FILES Util/GuidUtil.cpp Util/GuidUtil.h Util/IObservable.h - Util/IndexedFiles.cpp - Util/IndexedFiles.h Util/KDTree.cpp Util/Mailer.h Util/NamedData.cpp diff --git a/Code/Legacy/CryCommon/AndroidSpecific.h b/Code/Legacy/CryCommon/AndroidSpecific.h index d4832172fc..0cb4a6786c 100644 --- a/Code/Legacy/CryCommon/AndroidSpecific.h +++ b/Code/Legacy/CryCommon/AndroidSpecific.h @@ -30,17 +30,6 @@ #define MOBILE #endif -#if (defined(__clang__) && NDK_REV_MAJOR >= 14) || (defined(_CPU_ARM) && defined(PLATFORM_64BIT)) - // The version of clang that NDK r14+ ships with is seemingly generating different (for better or worse) code for the atomic operations - // used in the LocklessLinkedList. In either case, this is causing deadlocks in the job system and crashes from memory stomps in - // the bucket allocator. By defining INTERLOCKED_COMPARE_EXCHANGE_128_NOT_SUPPORTED it will disable the Cry job system as well as - // change the implementation of the LocklessLinkedList to use a mutex in it's operations instead, essentially use the same behaviour - // as iOS. While not ideal to use this as a band-aid on the problem, it does fix it with a negligible performance impact. - // - // Additionally, arm64 processors do not provide a cmpxchg16b (or equivalent) instruction required for _InterlockedCompareExchange128 - #define INTERLOCKED_COMPARE_EXCHANGE_128_NOT_SUPPORTED -#endif - // Force all allocations to be aligned to TARGET_DEFAULT_ALIGN. // This is because malloc on Android 32 bit returns memory that is not aligned // to what some structs/classes need. diff --git a/Code/Legacy/CryCommon/CryThread.h b/Code/Legacy/CryCommon/CryThread.h index 691616651a..5929bed352 100644 --- a/Code/Legacy/CryCommon/CryThread.h +++ b/Code/Legacy/CryCommon/CryThread.h @@ -78,77 +78,6 @@ public: ~CryAutoLock() { m_pLock->Unlock(); } }; -////////////////////////////////////////////////////////////////////////// -// -// CryOptionalAutoLock implements a helper class to automatically -// lock critical section (if needed) in constructor and release on destructor. -// -////////////////////////////////////////////////////////////////////////// -template -class CryOptionalAutoLock -{ -private: - LockClass* m_Lock; - bool m_bLockAcquired; - - CryOptionalAutoLock(); - CryOptionalAutoLock(const CryOptionalAutoLock&); - CryOptionalAutoLock& operator = (const CryOptionalAutoLock&); - -public: - CryOptionalAutoLock(LockClass& Lock, bool acquireLock) - : m_Lock(&Lock) - , m_bLockAcquired(false) - { - if (acquireLock) - { - Acquire(); - } - } - ~CryOptionalAutoLock() - { - Release(); - } - void Release() - { - if (m_bLockAcquired) - { - m_Lock->Unlock(); - m_bLockAcquired = false; - } - } - void Acquire() - { - if (!m_bLockAcquired) - { - m_Lock->Lock(); - m_bLockAcquired = true; - } - } -}; - -////////////////////////////////////////////////////////////////////////// -// -// CryAutoSet implements a helper class to automatically -// set and reset value in constructor and release on destructor. -// -////////////////////////////////////////////////////////////////////////// -template -class CryAutoSet -{ -private: - ValueClass* m_pValue; - - CryAutoSet(); - CryAutoSet(const CryAutoSet&); - CryAutoSet& operator = (const CryAutoSet&); - -public: - CryAutoSet(ValueClass& value) - : m_pValue(&value) { *m_pValue = (ValueClass)1; } - ~CryAutoSet() { *m_pValue = (ValueClass)0; } -}; - ////////////////////////////////////////////////////////////////////////// // // Auto critical section is the most commonly used type of auto lock. @@ -156,10 +85,6 @@ public: ////////////////////////////////////////////////////////////////////////// typedef CryAutoLock CryAutoCriticalSection; -#define AUTO_LOCK_T(Type, lock) PREFAST_SUPPRESS_WARNING(6246); CryAutoLock __AutoLock(lock) -#define AUTO_LOCK(lock) AUTO_LOCK_T(CryCriticalSection, lock) -#define AUTO_LOCK_CS(csLock) CryAutoCriticalSection __AL__##csLock(csLock) - ///////////////////////////////////////////////////////////////////////////// // // Threads. @@ -235,14 +160,6 @@ struct CryThreadInfo template class CrySimpleThread; -// Standard thread class. -// -// The class provides a lock (mutex) and an associated condition variable. If -// you don't need the lock, then you should used CrySimpleThread instead of -// CryThread. -template -class CryThread; - /////////////////////////////////////////////////////////////////////////////// // Include architecture specific code. #if AZ_LEGACY_CRYCOMMON_TRAIT_USE_PTHREADS @@ -265,560 +182,5 @@ class CryThread; typedef CryLockT CryMutex; #endif // !_CRYTHREAD_CONDLOCK_GLITCH -// The the architecture specific code does not define a class CryRWLock, then -// a default implementation is provided here. -#if !defined _CRYTHREAD_HAVE_RWLOCK && !defined _CRYTHREAD_CONDLOCK_GLITCH -class CryRWLock -{ - CryCriticalSection m_lockExclusiveAccess; - CryCriticalSection m_lockSharedAccessComplete; - CryConditionVariable m_condSharedAccessComplete; - - int m_nSharedAccessCount; - int m_nCompletedSharedAccessCount; - bool m_bExclusiveAccess; - - CryRWLock(const CryRWLock&); - CryRWLock& operator= (const CryRWLock&); - - void AdjustSharedAccessCount() - { - m_nSharedAccessCount -= m_nCompletedSharedAccessCount; - m_nCompletedSharedAccessCount = 0; - } - -public: - CryRWLock() - : m_nSharedAccessCount(0) - , m_nCompletedSharedAccessCount(0) - , m_bExclusiveAccess(false) - { } - - void RLock() - { - m_lockExclusiveAccess.Lock(); - if (++m_nSharedAccessCount == INT_MAX) - { - m_lockSharedAccessComplete.Lock(); - AdjustSharedAccessCount(); - m_lockSharedAccessComplete.Unlock(); - } - m_lockExclusiveAccess.Unlock(); - } - - bool TryRLock() - { - if (!m_lockExclusiveAccess.TryLock()) - { - return false; - } - if (++m_nSharedAccessCount == INT_MAX) - { - m_lockSharedAccessComplete.Lock(); - AdjustSharedAccessCount(); - m_lockSharedAccessComplete.Unlock(); - } - m_lockExclusiveAccess.Unlock(); - return true; - } - - void RUnlock() - { - Unlock(); - } - - void WLock() - { - m_lockExclusiveAccess.Lock(); - m_lockSharedAccessComplete.Lock(); - assert(!m_bExclusiveAccess); - AdjustSharedAccessCount(); - if (m_nSharedAccessCount > 0) - { - m_nCompletedSharedAccessCount -= m_nSharedAccessCount; - do - { - m_condSharedAccessComplete.Wait(m_lockSharedAccessComplete); - } - while (m_nCompletedSharedAccessCount < 0); - m_nSharedAccessCount = 0; - } - m_bExclusiveAccess = true; - } - - bool TryWLock() - { - if (!m_lockExclusiveAccess.TryLock()) - { - return false; - } - if (!m_lockSharedAccessComplete.TryLock()) - { - m_lockExclusiveAccess.Unlock(); - return false; - } - assert(!m_bExclusiveAccess); - AdjustSharedAccessCount(); - if (m_nSharedAccessCount > 0) - { - m_lockSharedAccessComplete.Unlock(); - m_lockExclusiveAccess.Unlock(); - return false; - } - else - { - m_bExclusiveAccess = true; - } - return true; - } - - void WUnlock() - { - Unlock(); - } - - void Unlock() - { - if (!m_bExclusiveAccess) - { - m_lockSharedAccessComplete.Lock(); - if (++m_nCompletedSharedAccessCount == 0) - { - m_condSharedAccessComplete.NotifySingle(); - } - m_lockSharedAccessComplete.Unlock(); - } - else - { - m_bExclusiveAccess = false; - m_lockSharedAccessComplete.Unlock(); - m_lockExclusiveAccess.Unlock(); - } - } -}; -#endif // !defined _CRYTHREAD_HAVE_RWLOCK - -// Thread class. -// -// CryThread is an extension of CrySimpleThread providing a lock (mutex) and a -// condition variable per instance. -template -class CryThread - : public CrySimpleThread -{ - CryMutex m_Lock; - CryConditionVariable m_Cond; - - CryThread(const CryThread&); - void operator = (const CryThread&); - -public: - CryThread() { } - void Lock() { m_Lock.Lock(); } - bool TryLock() { return m_Lock.TryLock(); } - void Unlock() { m_Lock.Unlock(); } - void Wait() { m_Cond.Wait(m_Lock); } - // Timed wait on the associated condition. - // - // The 'milliseconds' parameter specifies the relative timeout in - // milliseconds. The method returns true if a notification was received and - // false if the specified timeout expired without receiving a notification. - // - // UNIX note: the method will _not_ return if the calling thread receives a - // signal. Instead the call is re-started with the _original_ timeout - // value. This misfeature may be fixed in the future. - bool TimedWait(uint32 milliseconds) - { - return m_Cond.TimedWait(m_Lock, milliseconds); - } - void Notify() { m_Cond.Notify(); } - void NotifySingle() { m_Cond.NotifySingle(); } - CryMutex& GetLock() { return m_Lock; } -}; - -////////////////////////////////////////////////////////////////////////// -// -// Sync primitive for multiple reads and exclusive locking change access -// -// Desc: -// Useful in case if you have rarely modified object that needs -// to be read quite often from different threads but still -// need to be exclusively modified sometimes -// Debug functionality: -// Can be used for debug-only lock calls, which verify that no -// simultaneous access is attempted. -// Use the bDebug argument of LockRead or LockModify, -// or use the DEBUG_READLOCK or DEBUG_MODIFYLOCK macros. -// There is no overhead in release builds, if you use the macros, -// and the lock definition is inside #ifdef _DEBUG. -////////////////////////////////////////////////////////////////////////// - -class CryReadModifyLock -{ -public: - CryReadModifyLock() - : m_modifyCount(0) - , m_readCount(0) - { - SetDebugLocked(false); - } - - bool LockRead(bool bTry = false, cstr strDebug = 0, bool bDebug = false) const - { - if (!WriteLock(bTry, bDebug, strDebug)) // wait until write unlocked - { - return false; - } - CryInterlockedIncrement(&m_readCount); // increment read counter - m_writeLock.Unlock(); - return true; - } - void UnlockRead() const - { - SetDebugLocked(false); - const int counter = CryInterlockedDecrement(&m_readCount); // release read - assert(counter >= 0); - if (m_writeLock.TryLock()) - { - m_writeLock.Unlock(); - } - else - if (counter == 0 && m_modifyCount) - { - m_ReadReleased.Set(); // signal the final read released - } - } - bool LockModify(bool bTry = false, cstr strDebug = 0, bool bDebug = false) const - { - if (!WriteLock(bTry, bDebug, strDebug)) - { - return false; - } - CryInterlockedIncrement(&m_modifyCount); // increment write counter (counter is for nested cases) - while (m_readCount) - { - m_ReadReleased.Wait(); // wait for all threads finish read operation - } - return true; - } - void UnlockModify() const - { - SetDebugLocked(false); -#if !defined(NDEBUG) - int counter = -#endif - CryInterlockedDecrement(&m_modifyCount); // decrement write counter - assert(counter >= 0); - m_writeLock.Unlock(); // release exclusive lock - } - -protected: - mutable volatile int m_readCount; - mutable volatile int m_modifyCount; - mutable CryEvent m_ReadReleased; - mutable CryCriticalSection m_writeLock; - mutable bool m_debugLocked; - mutable const char* m_debugLockStr; - - void SetDebugLocked([[maybe_unused]] bool b, [[maybe_unused]] const char* str = 0) const - { -#ifdef _DEBUG - m_debugLocked = b; - m_debugLockStr = str; -#endif - } - - bool WriteLock(bool bTry, [[maybe_unused]] bool bDebug, [[maybe_unused]] const char* strDebug) const - { - if (!m_writeLock.TryLock()) - { -#ifdef _DEBUG - assert(!m_debugLocked); - assert(!bDebug); -#endif - if (bTry) - { - return false; - } - m_writeLock.Lock(); - } -#ifdef _DEBUG - if (!m_readCount && !m_modifyCount) // not yet locked - { - SetDebugLocked(bDebug, strDebug); - } -#endif - return true; - } -}; - -// Auto-locking classes. -template -class AutoLockRead -{ -protected: - const T& m_lock; -public: - AutoLockRead(const T& lock, cstr strDebug = 0) - : m_lock(lock) { m_lock.LockRead(bDEBUG, strDebug, bDEBUG); } - ~AutoLockRead() - { m_lock.UnlockRead(); } -}; - -template -class AutoLockModify -{ -protected: - const T& m_lock; -public: - AutoLockModify(const T& lock, cstr strDebug = 0) - : m_lock(lock) { m_lock.LockModify(bDEBUG, strDebug, bDEBUG); } - ~AutoLockModify() - { m_lock.UnlockModify(); } -}; - -#define AUTO_READLOCK(p) PREFAST_SUPPRESS_WARNING(6246) AutoLockRead AZ_JOIN(__readlock, __LINE__)(p, __FUNC__) -#define AUTO_READLOCK_PROT(p) PREFAST_SUPPRESS_WARNING(6246) AutoLockRead AZ_JOIN(__readlock_prot, __LINE__)(p, __FUNC__) -#define AUTO_MODIFYLOCK(p) PREFAST_SUPPRESS_WARNING(6246) AutoLockModify AZ_JOIN(__modifylock, __LINE__)(p, __FUNC__) - -#if defined(_DEBUG) - #define DEBUG_READLOCK(p) AutoLockRead AZ_JOIN(__readlock, __LINE__)(p, __FUNC__) - #define DEBUG_MODIFYLOCK(p) AutoLockModify AZ_JOIN(__modifylock, __LINE__)(p, __FUNC__) -#else - #define DEBUG_READLOCK(p) - #define DEBUG_MODIFYLOCK(p) -#endif - -// producer consumer queue implementations, but here instead of MultiThread_Container.h -// since they requiere platform specific code, and including windows.h in a very common -// header file leads to all kinds of problems -namespace CryMT -{ - ////////////////////////////////////////////////////////////////////////// - // Producer/Consumer Queue for 1 to 1 thread communication - // Realized with only volatile variables and memory barriers - // *warning* this producer/consumer queue is only thread safe in a 1 to 1 situation - // and doesn't provide any yields or similar to prevent spinning - ////////////////////////////////////////////////////////////////////////// - template - class SingleProducerSingleConsumerQueue - : public CryMT::detail::SingleProducerSingleConsumerQueueBase - { - public: - SingleProducerSingleConsumerQueue(); - ~SingleProducerSingleConsumerQueue(); - - void Init(size_t nSize); - - void Push(const T& rObj); - void Pop(T* pResult); - uint32 Size() { return (m_nProducerIndex - m_nComsumerIndex); } - uint32 BufferSize() { return m_nBufferSize; } - uint32 FreeCount() { return (m_nBufferSize - (m_nProducerIndex - m_nComsumerIndex)); } - - private: - T* m_arrBuffer; - uint32 m_nBufferSize; - - volatile uint32 m_nProducerIndex _ALIGN(16); - volatile uint32 m_nComsumerIndex _ALIGN(16); - } _ALIGN(128); - - /////////////////////////////////////////////////////////////////////////////// - template - inline SingleProducerSingleConsumerQueue::SingleProducerSingleConsumerQueue() - : m_arrBuffer(NULL) - , m_nBufferSize(0) - , m_nProducerIndex(0) - , m_nComsumerIndex(0) - {} - - /////////////////////////////////////////////////////////////////////////////// - template - inline SingleProducerSingleConsumerQueue::~SingleProducerSingleConsumerQueue() - { - CryModuleMemalignFree(m_arrBuffer); - m_nBufferSize = 0; - } - - /////////////////////////////////////////////////////////////////////////////// - template - inline void SingleProducerSingleConsumerQueue::Init(size_t nSize) - { - assert(m_arrBuffer == NULL); - assert(m_nBufferSize == 0); - assert((nSize & (nSize - 1)) == 0); - - m_arrBuffer = alias_cast(CryModuleMemalign(nSize * sizeof(T), 16)); - m_nBufferSize = nSize; - } - - /////////////////////////////////////////////////////////////////////////////// - template - inline void SingleProducerSingleConsumerQueue::Push(const T& rObj) - { - assert(m_arrBuffer != NULL); - assert(m_nBufferSize != 0); - SingleProducerSingleConsumerQueueBase::Push((void*)&rObj, m_nProducerIndex, m_nComsumerIndex, m_nBufferSize, m_arrBuffer, sizeof(T)); - } - - /////////////////////////////////////////////////////////////////////////////// - template - inline void SingleProducerSingleConsumerQueue::Pop(T* pResult) - { - assert(m_arrBuffer != NULL); - assert(m_nBufferSize != 0); - SingleProducerSingleConsumerQueueBase::Pop(pResult, m_nProducerIndex, m_nComsumerIndex, m_nBufferSize, m_arrBuffer, sizeof(T)); - } - - ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - // Producer/Consumer Queue for N to 1 thread communication - // lockfree implemenation, to copy with multiple producers, - // a internal producer refcount is managed, the queue is empty - // as soon as there are no more producers and no new elements - ////////////////////////////////////////////////////////////////////////// - template - class N_ProducerSingleConsumerQueue - : public CryMT::detail::N_ProducerSingleConsumerQueueBase - { - public: - N_ProducerSingleConsumerQueue(); - ~N_ProducerSingleConsumerQueue(); - - void Init(size_t nSize); - - void Push(const T& rObj); - bool Pop(T* pResult); - - // needs to be called before using, assumes that there is at least one producer - // so the first one doesn't need to call AddProducer, but he has to deregister itself - void SetRunningState(); - - // to correctly track when the queue is empty(and no new jobs will be added), refcount the producer - void AddProducer(); - void RemoveProducer(); - - uint32 Size() { return (m_nProducerIndex - m_nComsumerIndex); } - uint32 BufferSize() { return m_nBufferSize; } - uint32 FreeCount() { return (m_nBufferSize - (m_nProducerIndex - m_nComsumerIndex)); } - - private: - T* m_arrBuffer; - volatile uint32* m_arrStates; - uint32 m_nBufferSize; - - volatile uint32 m_nProducerIndex; - volatile uint32 m_nComsumerIndex; - volatile uint32 m_nRunning; - volatile uint32 m_nProducerCount; - } _ALIGN(128); - - /////////////////////////////////////////////////////////////////////////////// - template - inline N_ProducerSingleConsumerQueue::N_ProducerSingleConsumerQueue() - : m_arrBuffer(NULL) - , m_arrStates(NULL) - , m_nBufferSize(0) - , m_nProducerIndex(0) - , m_nComsumerIndex(0) - , m_nRunning(0) - , m_nProducerCount(0) - {} - - /////////////////////////////////////////////////////////////////////////////// - template - inline N_ProducerSingleConsumerQueue::~N_ProducerSingleConsumerQueue() - { - CryModuleMemalignFree(m_arrBuffer); - CryModuleMemalignFree((void*)m_arrStates); - m_nBufferSize = 0; - } - - /////////////////////////////////////////////////////////////////////////////// - template - inline void N_ProducerSingleConsumerQueue::Init(size_t nSize) - { - assert(m_arrBuffer == NULL); - assert(m_arrStates == NULL); - assert(m_nBufferSize == 0); - assert((nSize & (nSize - 1)) == 0); - - m_arrBuffer = alias_cast(CryModuleMemalign(nSize * sizeof(T), 16)); - m_arrStates = alias_cast(CryModuleMemalign(nSize * sizeof(uint32), 16)); - memset((void*)m_arrStates, 0, sizeof(uint32) * nSize); - m_nBufferSize = nSize; - } - - /////////////////////////////////////////////////////////////////////////////// - template - inline void N_ProducerSingleConsumerQueue::SetRunningState() - { -#if !defined(_RELEASE) - if (m_nRunning == 1) - { - __debugbreak(); - } -#endif - m_nRunning = 1; - m_nProducerCount = 1; - } - - /////////////////////////////////////////////////////////////////////////////// - template - inline void N_ProducerSingleConsumerQueue::AddProducer() - { - assert(m_arrBuffer != NULL); - assert(m_arrStates != NULL); - assert(m_nBufferSize != 0); -#if !defined(_RELEASE) - if (m_nRunning == 0) - { - __debugbreak(); - } -#endif - CryInterlockedIncrement((volatile int*)&m_nProducerCount); - } - - /////////////////////////////////////////////////////////////////////////////// - template - inline void N_ProducerSingleConsumerQueue::RemoveProducer() - { - assert(m_arrBuffer != NULL); - assert(m_arrStates != NULL); - assert(m_nBufferSize != 0); -#if !defined(_RELEASE) - if (m_nRunning == 0) - { - __debugbreak(); - } -#endif - if (CryInterlockedDecrement((volatile int*)&m_nProducerCount) == 0) - { - m_nRunning = 0; - } - } - - /////////////////////////////////////////////////////////////////////////////// - template - inline void N_ProducerSingleConsumerQueue::Push(const T& rObj) - { - assert(m_arrBuffer != NULL); - assert(m_arrStates != NULL); - assert(m_nBufferSize != 0); - CryMT::detail::N_ProducerSingleConsumerQueueBase::Push((void*)&rObj, m_nProducerIndex, m_nComsumerIndex, m_nRunning, m_arrBuffer, m_nBufferSize, sizeof(T), m_arrStates); - } - - /////////////////////////////////////////////////////////////////////////////// - template - inline bool N_ProducerSingleConsumerQueue::Pop(T* pResult) - { - assert(m_arrBuffer != NULL); - assert(m_arrStates != NULL); - assert(m_nBufferSize != 0); - return CryMT::detail::N_ProducerSingleConsumerQueueBase::Pop(pResult, m_nProducerIndex, m_nComsumerIndex, m_nRunning, m_arrBuffer, m_nBufferSize, sizeof(T), m_arrStates); - } -} //namespace CryMT - // Include all multithreading containers. #include "MultiThread_Containers.h" diff --git a/Code/Legacy/CryCommon/CryThreadImpl_pthreads.h b/Code/Legacy/CryCommon/CryThreadImpl_pthreads.h index 895cbde524..c94d4fca90 100644 --- a/Code/Legacy/CryCommon/CryThreadImpl_pthreads.h +++ b/Code/Legacy/CryCommon/CryThreadImpl_pthreads.h @@ -107,195 +107,4 @@ void* CryCreateCriticalSection() return (void*) new TCritSecType; } -#if AZ_TRAIT_SKIP_CRYINTERLOCKED -#elif defined(INTERLOCKED_COMPARE_EXCHANGE_128_NOT_SUPPORTED) -////////////////////////////////////////////////////////////////////////// -void CryInterlockedPushEntrySList(SLockFreeSingleLinkedListHeader& list, SLockFreeSingleLinkedListEntry& element) -{ - AZStd::lock_guard lock(list.mutex); - - element.pNext = list.pNext; - list.pNext = &element; -} - -////////////////////////////////////////////////////////////////////////// -void* CryInterlockedPopEntrySList(SLockFreeSingleLinkedListHeader& list) -{ - AZStd::lock_guard lock(list.mutex); - - SLockFreeSingleLinkedListEntry* returnValue = list.pNext; - if (list.pNext) - { - list.pNext = list.pNext->pNext; - } - return returnValue; -} - -////////////////////////////////////////////////////////////////////////// -void CryInitializeSListHead(SLockFreeSingleLinkedListHeader& list) -{ - AZStd::lock_guard lock(list.mutex); - - list.pNext = NULL; -} - -////////////////////////////////////////////////////////////////////////// -void* CryInterlockedFlushSList(SLockFreeSingleLinkedListHeader& list) -{ - AZStd::lock_guard lock(list.mutex); - - SLockFreeSingleLinkedListEntry* returnValue = list.pNext; - list.pNext = nullptr; - return returnValue; -} - -#elif defined(LINUX32) -////////////////////////////////////////////////////////////////////////// -// Implementation for Linux32 with gcc using uint64 -////////////////////////////////////////////////////////////////////////// -void CryInterlockedPushEntrySList(SLockFreeSingleLinkedListHeader& list, SLockFreeSingleLinkedListEntry& element) -{ - uint32 curSetting[2]; - uint32 newSetting[2]; - uint32 newPointer = (uint32) & element; - do - { - curSetting[0] = (uint32)list.pNext; - curSetting[1] = list.salt; - element.pNext = (SLockFreeSingleLinkedListEntry*)curSetting[0]; - newSetting[0] = newPointer; // new pointer - newSetting[1] = curSetting[1] + 1; // new salt - } - while (false == __sync_bool_compare_and_swap((volatile uint64*)&list.pNext, *(uint64*)&curSetting[0], *(uint64*)&newSetting[0])); -} - -////////////////////////////////////////////////////////////////////////// -void* CryInterlockedPopEntrySList(SLockFreeSingleLinkedListHeader& list) -{ - uint32 curSetting[2]; - uint32 newSetting[2]; - do - { - curSetting[1] = list.salt; - curSetting[0] = (uint32)list.pNext; - if (curSetting[0] == 0) - { - return NULL; - } - newSetting[0] = *(uint32*)curSetting[0]; // new pointer - newSetting[1] = curSetting[1] + 1; // new salt - } - while (false == __sync_bool_compare_and_swap((volatile uint64*)&list.pNext, *(uint64*)&curSetting[0], *(uint64*)&newSetting[0])); - return (void*)curSetting[0]; -} - -////////////////////////////////////////////////////////////////////////// -void CryInitializeSListHead(SLockFreeSingleLinkedListHeader& list) -{ - list.salt = 0; - list.pNext = NULL; -} - -////////////////////////////////////////////////////////////////////////// -void* CryInterlockedFlushSList(SLockFreeSingleLinkedListHeader& list) -{ - uint32 curSetting[2]; - uint32 newSetting[2]; - uint32 newSalt; - uint32 newPointer; - do - { - curSetting[1] = list.salt; - curSetting[0] = (uint32)list.pNext; - if (curSetting[0] == 0) - { - return NULL; - } - newSetting[0] = 0; - newSetting[1] = curSetting[1] + 1; - } - while (false == __sync_bool_compare_and_swap((volatile uint64*)&list.pNext, *(uint64*)&curSetting[0], *(uint64*)&newSetting[0])); - return (void*)curSetting[0]; -} -#else -// This implementation get's used on multiple platforms that support uint128 compare and swap. - -////////////////////////////////////////////////////////////////////////// -// LINUX64 Implementation of Lockless Single Linked List -////////////////////////////////////////////////////////////////////////// -typedef __uint128_t uint128; - -////////////////////////////////////////////////////////////////////////// -// Implementation for Linux64 with gcc using __int128_t -////////////////////////////////////////////////////////////////////////// -void CryInterlockedPushEntrySList(SLockFreeSingleLinkedListHeader& list, SLockFreeSingleLinkedListEntry& element) -{ - uint64 curSetting[2]; - uint64 newSetting[2]; - uint64 newPointer = (uint64) & element; - do - { - curSetting[0] = (uint64)list.pNext; - curSetting[1] = list.salt; - element.pNext = (SLockFreeSingleLinkedListEntry*)curSetting[0]; - newSetting[0] = newPointer; // new pointer - newSetting[1] = curSetting[1] + 1; // new salt - } - // while (false == __sync_bool_compare_and_swap( (volatile uint128*)&list.pNext,*(uint128*)&curSetting[0],*(uint128*)&newSetting[0] )); - while (0 == _InterlockedCompareExchange128((volatile int64*)&list.pNext, (int64)newSetting[1], (int64)newSetting[0], (int64*)&curSetting[0])); -} - -////////////////////////////////////////////////////////////////////////// -void* CryInterlockedPopEntrySList(SLockFreeSingleLinkedListHeader& list) -{ - uint64 curSetting[2]; - uint64 newSetting[2]; - do - { - curSetting[1] = list.salt; - curSetting[0] = (uint64)list.pNext; - if (curSetting[0] == 0) - { - return NULL; - } - newSetting[0] = *(uint64*)curSetting[0]; // new pointer - newSetting[1] = curSetting[1] + 1; // new salt - } - //while (false == __sync_bool_compare_and_swap( (volatile uint128*)&list.pNext,*(uint128*)&curSetting[0],*(uint128*)&newSetting[0] )); - while (0 == _InterlockedCompareExchange128((volatile int64*)&list.pNext, (int64)newSetting[1], (int64)newSetting[0], (int64*)&curSetting[0])); - return (void*)curSetting[0]; -} - -////////////////////////////////////////////////////////////////////////// -void CryInitializeSListHead(SLockFreeSingleLinkedListHeader& list) -{ - list.salt = 0; - list.pNext = NULL; -} - -////////////////////////////////////////////////////////////////////////// -void* CryInterlockedFlushSList(SLockFreeSingleLinkedListHeader& list) -{ - uint64 curSetting[2]; - uint64 newSetting[2]; - uint64 newSalt; - uint64 newPointer; - do - { - curSetting[1] = list.salt; - curSetting[0] = (uint64)list.pNext; - if (curSetting[0] == 0) - { - return NULL; - } - newSetting[0] = 0; - newSetting[1] = curSetting[1] + 1; - } - // while (false == __sync_bool_compare_and_swap( (volatile uint128*)&list.pNext,*(uint128*)&curSetting[0],*(uint128*)&newSetting[0] )); - while (0 == _InterlockedCompareExchange128((volatile int64*)&list.pNext, (int64)newSetting[1], (int64)newSetting[0], (int64*)&curSetting[0])); - return (void*)curSetting[0]; -} -////////////////////////////////////////////////////////////////////////// -#endif - #endif // CRYINCLUDE_CRYCOMMON_CRYTHREADIMPL_PTHREADS_H diff --git a/Code/Legacy/CryCommon/CryThreadImpl_windows.h b/Code/Legacy/CryCommon/CryThreadImpl_windows.h index 0451258aac..5cf970414d 100644 --- a/Code/Legacy/CryCommon/CryThreadImpl_windows.h +++ b/Code/Legacy/CryCommon/CryThreadImpl_windows.h @@ -303,78 +303,6 @@ void CryFastSemaphore::Release() } } -////////////////////////////////////////////////////////////////////////// -CryRWLock::CryRWLock() -{ - STATIC_ASSERT(sizeof(m_Lock) == sizeof(PSRWLOCK), "RWLock-pointer has invalid size"); - InitializeSRWLock(reinterpret_cast(&m_Lock)); -} - -////////////////////////////////////////////////////////////////////////// -CryRWLock::~CryRWLock() -{ -} - -////////////////////////////////////////////////////////////////////////// -void CryRWLock::RLock() -{ - AcquireSRWLockShared(reinterpret_cast(&m_Lock)); -} - -////////////////////////////////////////////////////////////////////////// -#if defined(_CRYTHREAD_WANT_TRY_RWLOCK) -bool CryRWLock::TryRLock() -{ - return TryAcquireSRWLockShared(reinterpret_cast(&m_Lock)) != 0; -} -#endif - -////////////////////////////////////////////////////////////////////////// -void CryRWLock::RUnlock() -{ - ReleaseSRWLockShared(reinterpret_cast(&m_Lock)); -} - -////////////////////////////////////////////////////////////////////////// -void CryRWLock::WLock() -{ - AcquireSRWLockExclusive(reinterpret_cast(&m_Lock)); -} - -////////////////////////////////////////////////////////////////////////// -#if defined(_CRYTHREAD_WANT_TRY_RWLOCK) -bool CryRWLock::TryWLock() -{ - return TryAcquireSRWLockExclusive(reinterpret_cast(&m_Lock)) != 0; -} -#endif - -////////////////////////////////////////////////////////////////////////// -void CryRWLock::WUnlock() -{ - ReleaseSRWLockExclusive(reinterpret_cast(&m_Lock)); -} - -////////////////////////////////////////////////////////////////////////// -void CryRWLock::Lock() -{ - WLock(); -} - -////////////////////////////////////////////////////////////////////////// -#if defined(_CRYTHREAD_WANT_TRY_RWLOCK) -bool CryRWLock::TryLock() -{ - return TryWLock(); -} -#endif - -////////////////////////////////////////////////////////////////////////// -void CryRWLock::Unlock() -{ - WUnlock(); -} - ////////////////////////////////////////////////////////////////////////// CrySimpleThreadSelf::CrySimpleThreadSelf() : m_thread(NULL) @@ -415,181 +343,3 @@ void CrySimpleThreadSelf::StartThread(unsigned (__stdcall * func)(void*), void* PREFAST_ASSUME(m_thread); ResumeThread((HANDLE)m_thread); } - - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -void CryInterlockedPushEntrySList(SLockFreeSingleLinkedListHeader& list, SLockFreeSingleLinkedListEntry& element) -{ - STATIC_CHECK(sizeof(SLockFreeSingleLinkedListHeader) == sizeof(SLIST_HEADER), CRY_INTERLOCKED_SLIST_HEADER_HAS_WRONG_SIZE); - STATIC_CHECK(sizeof(SLockFreeSingleLinkedListEntry) >= sizeof(SLIST_ENTRY), CRY_INTERLOCKED_SLIST_ENTRY_HAS_WRONG_SIZE); - - assert(IsAligned(&list, MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Header has wrong Alignment"); - assert(IsAligned(&element, MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Entry has wrong Alignment"); - InterlockedPushEntrySList(alias_cast(&list), alias_cast(&element)); -} - -////////////////////////////////////////////////////////////////////////// -void* CryInterlockedPopEntrySList(SLockFreeSingleLinkedListHeader& list) -{ - STATIC_CHECK(sizeof(SLockFreeSingleLinkedListHeader) == sizeof(SLIST_HEADER), CRY_INTERLOCKED_SLIST_HEADER_HAS_WRONG_SIZE); - - assert(IsAligned(&list, MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Header has wrong Alignment"); - return reinterpret_cast(InterlockedPopEntrySList(alias_cast(&list))); -} - -////////////////////////////////////////////////////////////////////////// -void CryInitializeSListHead(SLockFreeSingleLinkedListHeader& list) -{ - assert(IsAligned(&list, MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Header has wrong Alignment"); - - STATIC_CHECK(sizeof(SLockFreeSingleLinkedListHeader) == sizeof(SLIST_HEADER), CRY_INTERLOCKED_SLIST_HEADER_HAS_WRONG_SIZE); - InitializeSListHead(alias_cast(&list)); -} - -////////////////////////////////////////////////////////////////////////// -void* CryInterlockedFlushSList(SLockFreeSingleLinkedListHeader& list) -{ - assert(IsAligned(&list, MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Header has wrong Alignment"); - - STATIC_CHECK(sizeof(SLockFreeSingleLinkedListHeader) == sizeof(SLIST_HEADER), CRY_INTERLOCKED_SLIST_HEADER_HAS_WRONG_SIZE); - return InterlockedFlushSList(alias_cast(&list)); -} - -/////////////////////////////////////////////////////////////////////////////// -// base class for lock less Producer/Consumer queue, due platforms specific they -// are implemeted in CryThead_platform.h -namespace CryMT { - namespace detail { - /////////////////////////////////////////////////////////////////////////////// - void SingleProducerSingleConsumerQueueBase::Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize) - { - // spin if queue is full - int iter = 0; - while (rProducerIndex - rComsumerIndex == nBufferSize) - { - CryLowLatencySleep(iter++ > 10 ? 1 : 0); - } - - MemoryBarrier(); - char* pBuffer = alias_cast(arrBuffer); - uint32 nIndex = rProducerIndex % nBufferSize; - - memcpy(pBuffer + (nIndex * nObjectSize), pObj, nObjectSize); - MemoryBarrier(); - rProducerIndex += 1; - MemoryBarrier(); - } - - /////////////////////////////////////////////////////////////////////////////// - void SingleProducerSingleConsumerQueueBase::Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize) - { - MemoryBarrier(); - // busy-loop if queue is empty - int iter = 0; - while (rProducerIndex - rComsumerIndex == 0) - { - CryLowLatencySleep(iter++ > 10 ? 1 : 0); - } - - char* pBuffer = alias_cast(arrBuffer); - uint32 nIndex = rComsumerIndex % nBufferSize; - - memcpy(pObj, pBuffer + (nIndex * nObjectSize), nObjectSize); - MemoryBarrier(); - rComsumerIndex += 1; - MemoryBarrier(); - } - - /////////////////////////////////////////////////////////////////////////////// - void N_ProducerSingleConsumerQueueBase::Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, [[maybe_unused]] volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates) - { - MemoryBarrier(); - uint32 nProducerIndex; - uint32 nComsumerIndex; - - int iter = 0; - do - { - nProducerIndex = rProducerIndex; - nComsumerIndex = rComsumerIndex; - - if (nProducerIndex - nComsumerIndex == nBufferSize) - { - CryLowLatencySleep(iter++ > 10 ? 1 : 0); - if (iter > 20) // 10 spins + 10 ms wait - { - uint32 nSizeToAlloc = sizeof(SFallbackList) + nObjectSize - 1; - SFallbackList* pFallbackEntry = (SFallbackList*)CryModuleMemalign(nSizeToAlloc, 128); - memcpy(pFallbackEntry->object, pObj, nObjectSize); - MemoryBarrier(); - CryInterlockedPushEntrySList(fallbackList, pFallbackEntry->nextEntry); - return; - } - continue; - } - - if (CryInterlockedCompareExchange(alias_cast(&rProducerIndex), nProducerIndex + 1, nProducerIndex) == nProducerIndex) - { - break; - } - } while (true); - - MemoryBarrier(); - char* pBuffer = alias_cast(arrBuffer); - uint32 nIndex = nProducerIndex % nBufferSize; - - memcpy(pBuffer + (nIndex * nObjectSize), pObj, nObjectSize); - MemoryBarrier(); - arrStates[nIndex] = 1; - MemoryBarrier(); - } - - /////////////////////////////////////////////////////////////////////////////// - bool N_ProducerSingleConsumerQueueBase::Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates) - { - MemoryBarrier(); - - // busy-loop if queue is empty - int iter = 0; - if (rRunning && rProducerIndex - rComsumerIndex == 0) - { - while (rRunning && rProducerIndex - rComsumerIndex == 0) - { - CryLowLatencySleep(iter++ > 10 ? 1 : 0); - } - } - - if (rRunning == 0 && rProducerIndex - rComsumerIndex == 0) - { - SFallbackList* pFallback = (SFallbackList*)CryInterlockedPopEntrySList(fallbackList); - IF (pFallback, 0) - { - memcpy(pObj, pFallback->object, nObjectSize); - CryModuleMemalignFree(pFallback); - return true; - } - // if the queue was empty, make sure we really are empty - return false; - } - - iter = 0; - while (arrStates[rComsumerIndex % nBufferSize] == 0) - { - CryLowLatencySleep(iter++ > 10 ? 1 : 0); - } - - char* pBuffer = alias_cast(arrBuffer); - uint32 nIndex = rComsumerIndex % nBufferSize; - - memcpy(pObj, pBuffer + (nIndex * nObjectSize), nObjectSize); - MemoryBarrier(); - arrStates[nIndex] = 0; - MemoryBarrier(); - rComsumerIndex += 1; - MemoryBarrier(); - - return true; - } - } // namespace detail -} // namespace CryMT diff --git a/Code/Legacy/CryCommon/CryThread_pthreads.h b/Code/Legacy/CryCommon/CryThread_pthreads.h index 4ebc4e9637..72b29344ec 100644 --- a/Code/Legacy/CryCommon/CryThread_pthreads.h +++ b/Code/Legacy/CryCommon/CryThread_pthreads.h @@ -524,57 +524,6 @@ inline void CryFastSemaphore::Release() } } -////////////////////////////////////////////////////////////////////////// -#if !defined _CRYTHREAD_HAVE_RWLOCK -class CryRWLock -{ - pthread_rwlock_t m_Lock; - - CryRWLock(const CryRWLock&); - CryRWLock& operator= (const CryRWLock&); - -public: - CryRWLock() { pthread_rwlock_init(&m_Lock, NULL); } - ~CryRWLock() { pthread_rwlock_destroy(&m_Lock); } - void RLock() { pthread_rwlock_rdlock(&m_Lock); } - bool TryRLock() - { -#if defined(AZ_RESTRICTED_PLATFORM) - #define AZ_RESTRICTED_SECTION CRYTHREAD_PTHREADS_H_SECTION_TRY_RLOCK - #include AZ_RESTRICTED_FILE(CryThread_pthreads_h) -#endif -#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED) - #undef AZ_RESTRICTED_SECTION_IMPLEMENTED -#else - return pthread_rwlock_tryrdlock(&m_Lock) != EBUSY; -#endif - } - void RUnlock() { Unlock(); } - void WLock() { pthread_rwlock_wrlock(&m_Lock); } - bool TryWLock() - { -#if defined(AZ_RESTRICTED_PLATFORM) - #define AZ_RESTRICTED_SECTION CRYTHREAD_PTHREADS_H_SECTION_TRY_RLOCK - #include AZ_RESTRICTED_FILE(CryThread_pthreads_h) -#endif -#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED) - #undef AZ_RESTRICTED_SECTION_IMPLEMENTED -#else - return pthread_rwlock_trywrlock(&m_Lock) != EBUSY; -#endif - } - void WUnlock() { Unlock(); } - void Lock() { WLock(); } - bool TryLock() { return TryWLock(); } - void Unlock() { pthread_rwlock_unlock(&m_Lock); } -}; - -// Indicate that this implementation header provides an implementation for -// CryRWLock. -#define _CRYTHREAD_HAVE_RWLOCK 1 -#endif // !defined _CRYTHREAD_HAVE_RWLOCK - - //////////////////////////////////////////////////////////////////////////////// // Provide TLS implementation using pthreads for those platforms without __thread //////////////////////////////////////////////////////////////////////////////// @@ -1145,185 +1094,3 @@ public: }; #include "MemoryAccess.h" - - /////////////////////////////////////////////////////////////////////////////// - // base class for lock less Producer/Consumer queue, due platforms specific they - // are implemeted in CryThead_platform.h - namespace CryMT { - namespace detail { - /////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////// - class SingleProducerSingleConsumerQueueBase - { - public: - SingleProducerSingleConsumerQueueBase() - {} - - void Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize); - void Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize); - }; - - /////////////////////////////////////////////////////////////////////////////// - inline void SingleProducerSingleConsumerQueueBase::Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize) - { - MemoryBarrier(); - // spin if queue is full - int iter = 0; - while (rProducerIndex - rComsumerIndex == nBufferSize) - { - Sleep(iter++ > 10 ? 1 : 0); - } - - char* pBuffer = alias_cast(arrBuffer); - uint32 nIndex = rProducerIndex % nBufferSize; - memcpy(pBuffer + (nIndex * nObjectSize), pObj, nObjectSize); - - MemoryBarrier(); - rProducerIndex += 1; - MemoryBarrier(); - } - - /////////////////////////////////////////////////////////////////////////////// - inline void SingleProducerSingleConsumerQueueBase::Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize) - { - MemoryBarrier(); - // busy-loop if queue is empty - int iter = 0; - while (rProducerIndex - rComsumerIndex == 0) - { - Sleep(iter++ > 10 ? 1 : 0); - } - - char* pBuffer = alias_cast(arrBuffer); - uint32 nIndex = rComsumerIndex % nBufferSize; - - memcpy(pObj, pBuffer + (nIndex * nObjectSize), nObjectSize); - - MemoryBarrier(); - rComsumerIndex += 1; - MemoryBarrier(); - } - - - /////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////// - class N_ProducerSingleConsumerQueueBase - { - public: - N_ProducerSingleConsumerQueueBase() - { - CryInitializeSListHead(fallbackList); - } - - void Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates); - bool Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates); - - SLockFreeSingleLinkedListHeader fallbackList; - struct SFallbackList - { - SLockFreeSingleLinkedListEntry nextEntry; - char alignment_padding[128 - sizeof(SLockFreeSingleLinkedListEntry)]; - char object[1]; // struct will be overallocated with enough memory for the object - }; - }; - - /////////////////////////////////////////////////////////////////////////////// - inline void N_ProducerSingleConsumerQueueBase::Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates) - { - MemoryBarrier(); - uint32 nProducerIndex; - uint32 nComsumerIndex; - - int iter = 0; - do - { - nProducerIndex = rProducerIndex; - nComsumerIndex = rComsumerIndex; - - if (nProducerIndex - nComsumerIndex == nBufferSize) - { - Sleep(iter++ > 10 ? 1 : 0); - if (iter > 20) // 10 spins + 10 ms wait - { - uint32 nSizeToAlloc = sizeof(SFallbackList) + nObjectSize - 1; - SFallbackList* pFallbackEntry = (SFallbackList*)CryModuleMemalign(nSizeToAlloc, 128); - memcpy(pFallbackEntry->object, pObj, nObjectSize); - CryInterlockedPushEntrySList(fallbackList, pFallbackEntry->nextEntry); - return; - } - continue; - } - - if (CryInterlockedCompareExchange(alias_cast(&rProducerIndex), nProducerIndex + 1, nProducerIndex) == nProducerIndex) - { - break; - } - } while (true); - - char* pBuffer = alias_cast(arrBuffer); - uint32 nIndex = nProducerIndex % nBufferSize; - - memcpy(pBuffer + (nIndex * nObjectSize), pObj, nObjectSize); - - MemoryBarrier(); - arrStates[nIndex] = 1; - MemoryBarrier(); - } - - /////////////////////////////////////////////////////////////////////////////// - inline bool N_ProducerSingleConsumerQueueBase::Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates) - { - MemoryBarrier(); - // busy-loop if queue is empty - int iter = 0; - do - { - SFallbackList* pFallback = (SFallbackList*)CryInterlockedPopEntrySList(fallbackList); - IF (pFallback, 0) - { - memcpy(pObj, pFallback->object, nObjectSize); - CryModuleMemalignFree(pFallback); - return true; - } - - if (iter > 10) - { - Sleep(iter > 100 ? 1 : 0); - } - iter++; - } while (rRunning && rProducerIndex - rComsumerIndex == 0); - - if (rRunning == 0 && rProducerIndex - rComsumerIndex == 0) - { - // if the queue was empty, make sure we really are empty - SFallbackList* pFallback = (SFallbackList*)CryInterlockedPopEntrySList(fallbackList); - IF (pFallback, 0) - { - memcpy(pObj, pFallback->object, nObjectSize); - CryModuleMemalignFree(pFallback); - return true; - } - return false; - } - - iter = 0; - while (arrStates[rComsumerIndex % nBufferSize] == 0) - { - Sleep(iter++ > 10 ? 1 : 0); - } - - char* pBuffer = alias_cast(arrBuffer); - uint32 nIndex = rComsumerIndex % nBufferSize; - - memcpy(pObj, pBuffer + (nIndex * nObjectSize), nObjectSize); - - MemoryBarrier(); - arrStates[nIndex] = 0; - MemoryBarrier(); - rComsumerIndex += 1; - MemoryBarrier(); - - return true; - } - } // namespace detail - } // namespace CryMT diff --git a/Code/Legacy/CryCommon/CryThread_windows.h b/Code/Legacy/CryCommon/CryThread_windows.h index b80666ce73..cc09cc530b 100644 --- a/Code/Legacy/CryCommon/CryThread_windows.h +++ b/Code/Legacy/CryCommon/CryThread_windows.h @@ -180,41 +180,6 @@ private: volatile int32 m_nCounter; }; -////////////////////////////////////////////////////////////////////////// -#if !defined(_CRYTHREAD_HAVE_RWLOCK) -class CryRWLock -{ - void* /*SRWLOCK*/ m_Lock; - - CryRWLock(const CryRWLock&); - CryRWLock& operator= (const CryRWLock&); - -public: - CryRWLock(); - ~CryRWLock(); - - void RLock(); - void RUnlock(); - - void WLock(); - void WUnlock(); - - void Lock(); - void Unlock(); - -#if defined(_CRYTHREAD_WANT_TRY_RWLOCK) - // Enabling TryXXX requires Windows 7 or newer - bool TryRLock(); - bool TryWLock(); - bool TryLock(); -#endif -}; - -// Indicate that this implementation header provides an implementation for -// CryRWLock. -#define _CRYTHREAD_HAVE_RWLOCK 1 -#endif - ////////////////////////////////////////////////////////////////////////// class CrySimpleThreadSelf { @@ -420,46 +385,3 @@ public: bool IsStarted() const { return m_bIsStarted; } bool IsRunning() const { return m_bIsRunning; } }; - -/////////////////////////////////////////////////////////////////////////////// -// base class for lock less Producer/Consumer queue, due platforms specific they -// are implemented in CryThead_platform.h -namespace CryMT { - namespace detail { - /////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////// - class SingleProducerSingleConsumerQueueBase - { - public: - SingleProducerSingleConsumerQueueBase() - {} - - void Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize); - void Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize); - }; - - - /////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////// - class N_ProducerSingleConsumerQueueBase - { - public: - N_ProducerSingleConsumerQueueBase() - { - CryInitializeSListHead(fallbackList); - } - - void Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates); - bool Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates); - - private: - SLockFreeSingleLinkedListHeader fallbackList; - struct SFallbackList - { - SLockFreeSingleLinkedListEntry nextEntry; - char alignment_padding[128 - sizeof(SLockFreeSingleLinkedListEntry)]; - char object[1]; // struct will be overallocated with enough memory for the object - }; - }; - } // namespace detail -} // namespace CryMT diff --git a/Code/Legacy/CryCommon/Linux_Win32Wrapper.h b/Code/Legacy/CryCommon/Linux_Win32Wrapper.h index 2dea83925d..23cd7faf7f 100644 --- a/Code/Legacy/CryCommon/Linux_Win32Wrapper.h +++ b/Code/Legacy/CryCommon/Linux_Win32Wrapper.h @@ -145,9 +145,6 @@ inline void MemoryBarrier() { typedef int64 __m128; #endif -#if defined(LINUX64) || defined(APPLE) -unsigned char _InterlockedCompareExchange128(int64 volatile* dst, int64 exchangehigh, int64 exchangelow, int64* comperand); -#endif ////////////////////////////////////////////////////////////////////////// // io.h stuff #if !defined(ANDROID) diff --git a/Code/Legacy/CryCommon/MultiThread.h b/Code/Legacy/CryCommon/MultiThread.h index 2454ade6ab..2b224c4743 100644 --- a/Code/Legacy/CryCommon/MultiThread.h +++ b/Code/Legacy/CryCommon/MultiThread.h @@ -29,78 +29,13 @@ #define MULTITHREAD_H_SECTION_IMPLEMENT_CRYINTERLOCKEDCOMPAREEXCHANGE64 8 #endif -#define THREAD_NAME_LENGTH_MAX 64 - #define WRITE_LOCK_VAL (1 << 16) // Traits #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION MULTITHREAD_H_SECTION_TRAITS #include AZ_RESTRICTED_FILE(MultiThread_h) -#else -#define MULTITHREAD_H_TRAIT_SLOCKFREESINGLELINKEDLISTENTRY_ATTRIBUTE_ALIGN_16 0 -#if defined(WIN64) -#define MULTITHREAD_H_TRAIT_SLOCKFREESINGLELINKEDLISTENTRY_MSALIGN_16 1 #endif -#if defined(APPLE) || defined(LINUX) -#define MULTITHREAD_H_TRAIT_USE_SALTED_LINKEDLISTHEADER 1 -#endif -#endif - -//as PowerPC operates via cache line reservation, lock variables should reside ion their own cache line -template -struct SAtomicVar -{ - T val; - - inline operator T() const{return val; } - inline operator T() volatile const{return val; } - inline SAtomicVar& operator =(const T& rV){val = rV; return *this; } - inline void Assign(const T& rV){val = rV; } - inline void Assign(const T& rV) volatile{val = rV; } - inline T* Addr() {return &val; } - inline volatile T* Addr() volatile {return &val; } - - inline bool operator<(const T& v) const{return val < v; } - inline bool operator<(const SAtomicVar& v) const{return val < v.val; } - inline bool operator>(const T& v) const{return val > v; } - inline bool operator>(const SAtomicVar& v) const{return val > v.val; } - inline bool operator<=(const T& v) const{return val <= v; } - inline bool operator<=(const SAtomicVar& v) const{return val <= v.val; } - inline bool operator>=(const T& v) const{return val >= v; } - inline bool operator>=(const SAtomicVar& v) const{return val >= v.val; } - inline bool operator==(const T& v) const{return val == v; } - inline bool operator==(const SAtomicVar& v) const{return val == v.val; } - inline bool operator!=(const T& v) const{return val != v; } - inline bool operator!=(const SAtomicVar& v) const{return val != v.val; } - inline T operator*(const T& v) const{return val * v; } - inline T operator/(const T& v) const{return val / v; } - inline T operator+(const T& v) const{return val + v; } - inline T operator-(const T& v) const{return val - v; } - - inline bool operator<(const T& v) volatile const{return val < v; } - inline bool operator<(const SAtomicVar& v) volatile const{return val < v.val; } - inline bool operator>(const T& v) volatile const{return val > v; } - inline bool operator>(const SAtomicVar& v) volatile const{return val > v.val; } - inline bool operator<=(const T& v) volatile const{return val <= v; } - inline bool operator<=(const SAtomicVar& v) volatile const{return val <= v.val; } - inline bool operator>=(const T& v) volatile const{return val >= v; } - inline bool operator>=(const SAtomicVar& v) volatile const{return val >= v.val; } - inline bool operator==(const T& v) volatile const{return val == v; } - inline bool operator==(const SAtomicVar& v) volatile const{return val == v.val; } - inline bool operator!=(const T& v) volatile const{return val != v; } - inline bool operator!=(const SAtomicVar& v) volatile const{return val != v.val; } - inline T operator*(const T& v) volatile const{return val * v; } - inline T operator/(const T& v) volatile const{return val / v; } - inline T operator+(const T& v) volatile const{return val + v; } - inline T operator-(const T& v) volatile const{return val - v; } -}; - -typedef SAtomicVar TIntAtomic; -typedef SAtomicVar TUIntAtomic; -typedef SAtomicVar TFloatAtomic; - -#define __add_db16cycl__ NIntrinsics::YieldFor16Cycles(); void CrySpinLock(volatile int* pLock, int checkVal, int setVal); void CryReleaseSpinLock (volatile int*, int); @@ -208,37 +143,6 @@ ILINE void CryReleaseSpinLock(volatile int* pLock, int setVal) } ////////////////////////////////////////////////////////////////////////// -#if defined(APPLE) || defined(LINUX64) -// Fixes undefined reference to CryInterlockedAdd(unsigned long volatile*, long) on -// Mac and linux. -ILINE void CryInterLockedAdd(volatile LONG* pVal, LONG iAdd) -{ - (void) CryInterlockedExchangeAdd(pVal, iAdd); -} - - -/* -ILINE void CryInterLockedAdd(volatile unsigned long *pVal, long iAdd) -{ - long r; - __asm__ __volatile__ ( - #if defined(LINUX64) || defined(MAC) // long is 64 bits on amd64. - "lock ; xaddq %0, (%1) \n\t" - #else - "lock ; xaddl %0, (%1) \n\t" - #endif - : "=r" (r) - : "r" (pVal), "0" (iAdd) - : "memory" - ); - (void) r; -}*/ -/*ILINE void CryInterlockedAdd(volatile size_t *pVal, ptrdiff_t iAdd) { - //(void)CryInterlockedExchangeAdd((volatile long*)pVal,(long)iAdd); - (void) __sync_fetch_and_add(pVal,iAdd); -}*/ - -#endif ILINE void CryInterlockedAdd(volatile int* pVal, int iAdd) { #ifdef _CPU_X86 @@ -311,123 +215,6 @@ ILINE void CryInterlockedAddSize(volatile size_t* pVal, ptrdiff_t iAdd) ////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -// CryInterlocked*SList Function, these are specialized C-A-S -// functions for single-linked lists which prevent the A-B-A problem there -// there are implemented in the platform specific CryThread_*.h files -// TODO clean up the interlocked function the same was the CryThread_* header are - -//TODO somehow get their real size on WIN (without including windows.h...) -//NOTE: The sizes are verifyed at compile-time in the implementation functions, but this is still ugly -#if MULTITHREAD_H_TRAIT_SLOCKFREESINGLELINKEDLISTENTRY_MSALIGN_16 -_MS_ALIGN(16) -#elif defined(WIN32) -_MS_ALIGN(8) -#endif -struct SLockFreeSingleLinkedListEntry -{ - SLockFreeSingleLinkedListEntry* volatile pNext; -} -#if MULTITHREAD_H_TRAIT_SLOCKFREESINGLELINKEDLISTENTRY_ATTRIBUTE_ALIGN_16 -__attribute__ ((aligned(16))) -#elif defined(LINUX32) -_ALIGN(8) -#elif defined(APPLE) || defined(LINUX64) -_ALIGN(16) -#endif -; - -#if MULTITHREAD_H_TRAIT_SLOCKFREESINGLELINKEDLISTENTRY_MSALIGN_16 -_MS_ALIGN(16) -#elif defined(WIN32) -_MS_ALIGN(8) -#endif -struct SLockFreeSingleLinkedListHeader -{ - SLockFreeSingleLinkedListEntry* volatile pNext; -#if defined(INTERLOCKED_COMPARE_EXCHANGE_128_NOT_SUPPORTED) - // arm64 processors do not provide a cmpxchg16b (or equivalent) instruction, - // so _InterlockedCompareExchange128 is not implemented on arm64 platforms, - // and we have to use a mutex to ensure thread safety. - AZStd::mutex mutex; -#elif MULTITHREAD_H_TRAIT_USE_SALTED_LINKEDLISTHEADER - // If pointers 32bit, salt should be as well. Otherwise we get 4 bytes of padding between pNext and salt and CAS operations fail -#if defined(PLATFORM_64BIT) - volatile uint64 salt; -#else - volatile uint32 salt; -#endif -#endif -} -#if MULTITHREAD_H_TRAIT_SLOCKFREESINGLELINKEDLISTENTRY_ATTRIBUTE_ALIGN_16 -__attribute__ ((aligned(16))) -#elif defined(LINUX32) -_ALIGN(8) -#elif defined(APPLE) || defined(LINUX64) -_ALIGN(16) -#endif -; - - -// push a element atomically onto a single linked list -void CryInterlockedPushEntrySList(SLockFreeSingleLinkedListHeader& list, SLockFreeSingleLinkedListEntry& element); - -// push a element atomically from a single linked list -void* CryInterlockedPopEntrySList(SLockFreeSingleLinkedListHeader& list); - -// initialzied the lock-free single linked list -void CryInitializeSListHead(SLockFreeSingleLinkedListHeader& list); - -// flush the whole list -void* CryInterlockedFlushSList(SLockFreeSingleLinkedListHeader& list); - -ILINE void CryReadLock(volatile int* rw, bool yield) -{ - CryInterlockedAdd(rw, 1); -#ifdef NEED_ENDIAN_SWAP - volatile char* pw = (volatile char*)rw + 1; -#else - volatile char* pw = (volatile char*)rw + 2; -#endif - - uint64 loops = 0; - for (; *pw; ) - { - if (yield) - { -# if !defined(ANDROID) && !defined(IOS) && !defined(MULTITHREAD_H_TRAIT_NO_MM_PAUSE) - _mm_pause(); -# endif - - if (!(++loops & 0x7F)) - { - // give other threads with other prio right to run -#if defined(AZ_RESTRICTED_PLATFORM) - #define AZ_RESTRICTED_SECTION MULTITHREAD_H_SECTION_CRYINTERLOCKEDFLUSHSLIST_PT1 - #include AZ_RESTRICTED_FILE(MultiThread_h) -#elif defined (LINUX) - usleep(1); -#endif - } - else if (!(loops & 0x3F)) - { - // give threads with same prio chance to run -#if defined(AZ_RESTRICTED_PLATFORM) - #define AZ_RESTRICTED_SECTION MULTITHREAD_H_SECTION_CRYINTERLOCKEDFLUSHSLIST_PT2 - #include AZ_RESTRICTED_FILE(MultiThread_h) -#elif defined (LINUX) - sched_yield(); -#endif - } - } - } -} - -ILINE void CryReleaseReadLock(volatile int* rw) -{ - CryInterlockedAdd(rw, -1); -} - ILINE void CryWriteLock(volatile int* rw) { CrySpinLock(rw, 0, WRITE_LOCK_VAL); @@ -438,78 +225,6 @@ ILINE void CryReleaseWriteLock(volatile int* rw) CryInterlockedAdd(rw, -WRITE_LOCK_VAL); } -////////////////////////////////////////////////////////////////////////// -struct ReadLock -{ - ILINE ReadLock(volatile int& rw) - { - CryInterlockedAdd(prw = &rw, 1); -#ifdef NEED_ENDIAN_SWAP - volatile char* pw = (volatile char*)&rw + 1; - for (; * pw; ) - { - ; - } -#else - volatile char* pw = (volatile char*)&rw + 2; - for (; * pw; ) - { - ; - } -#endif - } - ILINE ReadLock(volatile int& rw, bool yield) - { - CryReadLock(prw = &rw, yield); - } - ~ReadLock() - { - CryReleaseReadLock(prw); - } -private: - volatile int* prw; -}; - -struct ReadLockCond -{ - ILINE ReadLockCond(volatile int& rw, int bActive) - { - if (bActive) - { - CryInterlockedAdd(&rw, 1); - bActivated = 1; -#ifdef NEED_ENDIAN_SWAP - volatile char* pw = (volatile char*)&rw + 1; - for (; * pw; ) - { - ; - } -#else - volatile char* pw = (volatile char*)&rw + 2; - for (; * pw; ) - { - ; - } -#endif - } - else - { - bActivated = 0; - } - prw = &rw; - } - void SetActive(int bActive = 1) { bActivated = bActive; } - void Release() { CryInterlockedAdd(prw, -bActivated); } - ~ReadLockCond() - { - CryInterlockedAdd(prw, -bActivated); - } - -private: - volatile int* prw; - int bActivated; -}; - ////////////////////////////////////////////////////////////////////////// struct WriteLock { @@ -519,15 +234,6 @@ private: volatile int* prw; }; -////////////////////////////////////////////////////////////////////////// -struct WriteAfterReadLock -{ - ILINE WriteAfterReadLock(volatile int& rw) { CrySpinLock(&rw, 1, WRITE_LOCK_VAL + 1); prw = &rw; } - ~WriteAfterReadLock() { CryInterlockedAdd(prw, -WRITE_LOCK_VAL); } -private: - volatile int* prw; -}; - ////////////////////////////////////////////////////////////////////////// struct WriteLockCond { @@ -562,12 +268,6 @@ ILINE int64 CryInterlockedCompareExchange64(volatile int64* addr, int64 exchange // This is OK, because long is signed int64 on Linux x86_64 //return CryInterlockedCompareExchange((volatile long*)addr, (long)exchange, (long)comperand); } - -ILINE int64 CryInterlockedExchange64(volatile int64* addr, int64 exchange) -{ - __sync_synchronize(); - return __sync_lock_test_and_set(addr, exchange); -} #else ILINE int64 CryInterlockedCompareExchange64(volatile int64* addr, int64 exchange, int64 compare) { @@ -583,21 +283,3 @@ ILINE int64 CryInterlockedCompareExchange64(volatile int64* addr, int64 exchange #endif } #endif - -////////////////////////////////////////////////////////////////////////// -#if defined(EXCLUDE_PHYSICS_THREAD) -ILINE void SpinLock(volatile int* pLock, int checkVal, int setVal) { *(int*)pLock = setVal; } -ILINE void AtomicAdd(volatile int* pVal, int iAdd) { *(int*)pVal += iAdd; } -ILINE void AtomicAdd(volatile unsigned int* pVal, int iAdd) { *(unsigned int*)pVal += iAdd; } - -ILINE void JobSpinLock(volatile int* pLock, int checkVal, int setVal) { CrySpinLock(pLock, checkVal, setVal); } -#else -ILINE void SpinLock(volatile int* pLock, int checkVal, int setVal) { CrySpinLock(pLock, checkVal, setVal); } -ILINE void AtomicAdd(volatile int* pVal, int iAdd) { CryInterlockedAdd(pVal, iAdd); } -ILINE void AtomicAdd(volatile unsigned int* pVal, int iAdd) { CryInterlockedAdd((volatile int*)pVal, iAdd); } - -ILINE void JobSpinLock(volatile int* pLock, int checkVal, int setVal) { SpinLock(pLock, checkVal, setVal); } -#endif - -ILINE void JobAtomicAdd(volatile int* pVal, int iAdd) { CryInterlockedAdd(pVal, iAdd); } -ILINE void JobAtomicAdd(volatile unsigned int* pVal, int iAdd) { CryInterlockedAdd((volatile int*)pVal, iAdd); } diff --git a/Code/Legacy/CryCommon/MultiThread_Containers.h b/Code/Legacy/CryCommon/MultiThread_Containers.h index 7c5af2a5d0..23f7cd1e0c 100644 --- a/Code/Legacy/CryCommon/MultiThread_Containers.h +++ b/Code/Legacy/CryCommon/MultiThread_Containers.h @@ -94,325 +94,10 @@ namespace CryMT container_type v; mutable CryCriticalSection m_cs; }; - - ////////////////////////////////////////////////////////////////////////// - // Multi-Thread safe vector container, can be used instead of std::vector. - ////////////////////////////////////////////////////////////////////////// - template - class vector - { - public: - typedef T value_type; - typedef CryAutoCriticalSection AutoLock; - - CryCriticalSection& get_lock() const { return m_cs; } - - void free_memory() { AutoLock lock(m_cs); stl::free_container(v); } - - ////////////////////////////////////////////////////////////////////////// - // std::vector interface - ////////////////////////////////////////////////////////////////////////// - bool empty() const { AutoLock lock(m_cs); return v.empty(); } - int size() const { AutoLock lock(m_cs); return v.size(); } - void resize(int sz) { AutoLock lock(m_cs); v.resize(sz); } - void reserve(int sz) { AutoLock lock(m_cs); v.reserve(sz); } - size_t capacity() const { AutoLock lock(m_cs); return v.size(); } - void clear() { AutoLock lock(m_cs); v.clear(); } - T& operator[](size_t pos) { AutoLock lock(m_cs); return v[pos]; } - const T& operator[](size_t pos) const { AutoLock lock(m_cs); return v[pos]; } - const T& front() const { AutoLock lock(m_cs); return v.front(); } - const T& back() const { AutoLock lock(m_cs); return v.back(); } - T& back() { AutoLock lock(m_cs); return v.back(); } - - void push_back(const T& x) { AutoLock lock(m_cs); return v.push_back(x); } - void pop_back() { AutoLock lock(m_cs); return v.pop_back(); } - ////////////////////////////////////////////////////////////////////////// - - template - void sort(const Func& compare_less) { AutoLock lock(m_cs); std::sort(v.begin(), v.end(), compare_less); } - - template - void append(const Iter& startRange, const Iter& endRange) { AutoLock lock(m_cs); v.insert(v.end(), startRange, endRange); } - - void swap(std::vector& vec) { AutoLock lock(m_cs); v.swap(vec); } - - ////////////////////////////////////////////////////////////////////////// - bool try_pop_front(T& returnValue) - { - AutoLock lock(m_cs); - if (!v.empty()) - { - returnValue = v.front(); - v.erase(v.begin()); - return true; - } - return false; - }; - bool try_pop_back(T& returnValue) - { - AutoLock lock(m_cs); - if (!v.empty()) - { - returnValue = v.back(); - v.pop_back(); - return true; - } - return false; - }; - - ////////////////////////////////////////////////////////////////////////// - template - bool find_and_copy(FindFunction findFunc, const KeyType& key, T& foundValue) const - { - AutoLock lock(m_cs); - if (!v.empty()) - { - typename std::vector::const_iterator it; - for (it = v.begin(); it != v.end(); ++it) - { - if (findFunc(key, *it)) - { - foundValue = *it; - return true; - } - } - } - return false; - } - - ////////////////////////////////////////////////////////////////////////// - bool try_remove(const T& value) - { - AutoLock lock(m_cs); - if (!v.empty()) - { - typename std::vector::iterator it = std::find(v.begin(), v.end(), value); - if (it != v.end()) - { - v.erase(it); - return true; - } - } - return false; - }; - - ////////////////////////////////////////////////////////////////////////// - template - bool try_remove_and_erase_if(Predicate predicateFunction) - { - AutoLock lock(m_cs); - if (!v.empty()) - { - typename std::vector::iterator it = std::remove_if(v.begin(), v.end(), predicateFunction); - if (it != v.end()) - { - v.erase(it, v.end()); - return true; - } - } - return false; - }; - - - ////////////////////////////////////////////////////////////////////////// - bool try_remove_at(size_t idx) - { - AutoLock lock(m_cs); - if (idx < v.size()) - { - v.erase(v.begin() + idx); - return true; - } - return false; - } - - - ////////////////////////////////////////////////////////////////////////// - //Fast remove - just move last elem over deleted element - order is not preserved - bool try_remove_unordered(const T& value) - { - AutoLock lock(m_cs); - if (!v.empty()) - { - typename std::vector::iterator it = std::find(v.begin(), v.end(), value); - if (it != v.end()) - { - if (v.size() > 1) - { - typename std::vector::iterator it_back = v.end() - 1; - - if (it != it_back) - { - *it = *it_back; - } - - v.erase(it_back); - } - else - { - v.erase(it); - } - return true; - } - } - return false; - }; - - vector() {} - - vector(const vector& rOther) - { - AutoLock lock1(m_cs); - AutoLock lock2(rOther.m_cs); - - v = rOther.v; - } - - vector& operator=(const vector& rOther) - { - if (this == &rOther) - { - return *this; - } - - AutoLock lock1(m_cs); - AutoLock lock2(rOther.m_cs); - - v = rOther.v; - - return *this; - } - private: - std::vector v; - mutable CryCriticalSection m_cs; - }; - - - ////////////////////////////////////////////////////////////////////////// - // Multi-Thread safe set container, can be used instead of std::set. - // It has limited functionality, but most of it is there. - ////////////////////////////////////////////////////////////////////////// - template - class set - { - public: - typedef T value_type; - typedef T Key; - typedef typename std::set::size_type size_type; - typedef CryAutoCriticalSection AutoLock; - - ////////////////////////////////////////////////////////////////////////// - // Methods - ////////////////////////////////////////////////////////////////////////// - void clear() { AutoLock lock(m_cs); s.clear(); } - size_type count(const Key& _Key) const { AutoLock lock(m_cs); return s.count(_Key); } - bool empty() const { AutoLock lock(m_cs); return s.empty(); } - size_type erase(const Key& _Key) { AutoLock lock(m_cs); return s.erase(_Key); } - - bool find(const Key& _Key) { AutoLock lock(m_cs); return (s.find(_Key) != s.end()); } - - bool pop_front(value_type& rFrontElement) - { - AutoLock lock(m_cs); - if (s.empty()) - { - return false; - } - rFrontElement = *s.begin(); - s.erase(s.begin()); - return true; - } - bool pop_front() - { - AutoLock lock(m_cs); - if (s.empty()) - { - return false; - } - s.erase(s.begin()); - return true; - } - - bool front(value_type& rFrontElement) - { - AutoLock lock(m_cs); - if (s.empty()) - { - return false; - } - rFrontElement = *s.begin(); - return true; - } - - bool insert(const value_type& _Val) { AutoLock lock(m_cs); return s.insert(_Val).second; } - size_type max_size() const { AutoLock lock(m_cs); return s.max_size(); } - size_type size() const { AutoLock lock(m_cs); return s.size(); } - void swap(set& _Right) { AutoLock lock(m_cs); s.swap(_Right); } - - CryCriticalSection& get_lock() { return m_cs; } - - private: - std::set s; - mutable CryCriticalSection m_cs; - }; - - - /////////////////////////////////////////////////////////////////////////////// - // - // Multi-thread safe lock-less FIFO queue container for passing pointers between threads. - // The queue only stores pointers to T, it does not copy the contents of T. - // - ////////////////////////////////////////////////////////////////////////// - template > - class CLocklessPointerQueue - { - public: - explicit CLocklessPointerQueue(size_t reserve = 32) { m_lockFreeQueue.reserve(reserve); }; - ~CLocklessPointerQueue() {}; - - // Check's if queue is empty. - bool empty() const; - - // Pushes item to the queue, only pointer is stored, T contents are not copied. - void push(T* ptr); - // pop can return NULL, always check for it before use. - T* pop(); - - private: - queue::template rebind_alloc> m_lockFreeQueue; - }; - - ////////////////////////////////////////////////////////////////////////// - template - inline bool CLocklessPointerQueue::empty() const - { - return m_lockFreeQueue.empty(); - } - - ////////////////////////////////////////////////////////////////////////// - template - inline void CLocklessPointerQueue::push(T* ptr) - { - m_lockFreeQueue.push(ptr); - } - - ////////////////////////////////////////////////////////////////////////// - template - inline T* CLocklessPointerQueue::pop() - { - T* val = NULL; - m_lockFreeQueue.try_pop(val); - return val; - } }; // namespace CryMT namespace stl { - template - void free_container(CryMT::vector& v) - { - v.free_memory(); - } template void free_container(CryMT::queue& v) { diff --git a/Code/Legacy/CryCommon/WinBase.cpp b/Code/Legacy/CryCommon/WinBase.cpp index 3208da9d68..15146fbac1 100644 --- a/Code/Legacy/CryCommon/WinBase.cpp +++ b/Code/Legacy/CryCommon/WinBase.cpp @@ -1377,25 +1377,6 @@ DLL_EXPORT void* CryInterlockedExchangePointer(void* volatile* dst, void* ex //return (void*)CryInterlockedCompareExchange((long volatile*)dst, (long)exchange, (long)comperand); } -#if (defined(LINUX64) && !defined(ANDROID)) || defined(MAC) || defined(IOS_SIMULATOR) -DLL_EXPORT unsigned char _InterlockedCompareExchange128(int64 volatile* dst, int64 exchangehigh, int64 exchangelow, int64* comperand) -{ - bool bEquals; - __asm__ __volatile__ - ( - "lock cmpxchg16b %1\n\t" - "setz %0" - : "=q" (bEquals), "+m" (*dst), "+d" (comperand[1]), "+a" (comperand[0]) - : "c" (exchangehigh), "b" (exchangelow) - : "cc" - ); - return (char)bEquals; -} -#elif defined(INTERLOCKED_COMPARE_EXCHANGE_128_NOT_SUPPORTED) - // arm64 processors do not provide a cmpxchg16b (or equivalent) instruction, - // so _InterlockedCompareExchange128 is not implemented on arm64 platforms. -#endif - threadID CryGetCurrentThreadId() { return GetCurrentThreadId(); diff --git a/Code/Legacy/CryCommon/iOSSpecific.h b/Code/Legacy/CryCommon/iOSSpecific.h index 36b50f0e3e..d2503a7441 100644 --- a/Code/Legacy/CryCommon/iOSSpecific.h +++ b/Code/Legacy/CryCommon/iOSSpecific.h @@ -34,10 +34,6 @@ #define PLATFORM_64BIT #endif -#if defined(_CPU_ARM) && defined(PLATFORM_64BIT) -# define INTERLOCKED_COMPARE_EXCHANGE_128_NOT_SUPPORTED -#endif // defined(_CPU_ARM) && defined(PLATFORM_64BIT) - #ifndef MOBILE #define MOBILE #endif From 6b2c9cbede6ade42081ca222aff56968b3a1b724 Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Fri, 6 Aug 2021 16:23:01 -0500 Subject: [PATCH 36/38] Fix a crash when reloading AudioControlEditor controls (#2729) * Fix a crash when reloading ACE controls data The crash was due to destruction of xml_node that was held in a unique_ptr. Rapidxml has a very rudimentary memory allocation design, so in most cases dynamic allocations aren't even made. The memory_pool does all the cleanup in its destructor, so having a unique_ptr run its default_delete was causing the crash. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Fix numerical conversion warnings Wwise source files needed a few fixes for the numerical conversion warning changes that went in recently. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- .../Source/Engine/FileIOHandler_wwise.cpp | 8 +-- .../Code/Source/Editor/AudioControl.cpp | 2 +- .../Code/Source/Editor/AudioControl.h | 50 ++++++++++--------- .../Source/Editor/AudioControlsLoader.cpp | 8 +-- .../Source/Editor/AudioControlsWriter.cpp | 6 +-- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/Gems/AudioEngineWwise/Code/Source/Engine/FileIOHandler_wwise.cpp b/Gems/AudioEngineWwise/Code/Source/Engine/FileIOHandler_wwise.cpp index 9571dd86a9..30084565d6 100644 --- a/Gems/AudioEngineWwise/Code/Source/Engine/FileIOHandler_wwise.cpp +++ b/Gems/AudioEngineWwise/Code/Source/Engine/FileIOHandler_wwise.cpp @@ -69,7 +69,7 @@ namespace Audio AkDeviceSettings deviceSettings; AK::StreamMgr::GetDefaultDeviceSettings(deviceSettings); - deviceSettings.uIOMemorySize = poolSize; + deviceSettings.uIOMemorySize = aznumeric_cast(poolSize); deviceSettings.uSchedulerTypeFlags = AK_SCHEDULER_BLOCKING; Platform::SetThreadProperties(deviceSettings.threadProperties); @@ -198,7 +198,7 @@ namespace Audio deviceDesc.bCanWrite = true; deviceDesc.deviceID = m_deviceID; AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "IO::IArchive", AZ_ARRAY_SIZE(deviceDesc.szDeviceName)); - deviceDesc.uStringSize = AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName); + deviceDesc.uStringSize = aznumeric_cast(AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName)); } AkUInt32 CBlockingDevice_wwise::GetDeviceData() @@ -219,7 +219,7 @@ namespace Audio AkDeviceSettings deviceSettings; AK::StreamMgr::GetDefaultDeviceSettings(deviceSettings); - deviceSettings.uIOMemorySize = poolSize; + deviceSettings.uIOMemorySize = aznumeric_cast(poolSize); deviceSettings.uSchedulerTypeFlags = AK_SCHEDULER_DEFERRED_LINED_UP; Platform::SetThreadProperties(deviceSettings.threadProperties); @@ -336,7 +336,7 @@ namespace Audio deviceDesc.bCanWrite = false; deviceDesc.deviceID = m_deviceID; AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "IO::IStreamer", AZ_ARRAY_SIZE(deviceDesc.szDeviceName)); - deviceDesc.uStringSize = AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName); + deviceDesc.uStringSize = aznumeric_cast(AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName)); } AkUInt32 CStreamingDevice_wwise::GetDeviceData() diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControl.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControl.cpp index ce268022a8..c0a8fb8dde 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControl.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControl.cpp @@ -345,7 +345,7 @@ namespace AudioControls { for (auto& connectionNode : m_connectionNodes) { - if (TConnectionPtr connection = audioSystemImpl->CreateConnectionFromXMLNode(connectionNode.m_xmlNode.get(), m_type)) + if (TConnectionPtr connection = audioSystemImpl->CreateConnectionFromXMLNode(connectionNode.m_xmlNode, m_type)) { AddConnection(connection); connectionNode.m_isValid = true; diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControl.h b/Gems/AudioSystem/Code/Source/Editor/AudioControl.h index 37e67c815c..024e8eb6df 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControl.h +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControl.h @@ -25,46 +25,48 @@ namespace AudioControls { SRawConnectionData(AZ::rapidxml::xml_node* node, bool isValid) { - m_xmlNode = AZStd::move(DeepCopyNode(node)); + m_xmlNode = DeepCopyNode(node); m_isValid = isValid; } - AZStd::unique_ptr> m_xmlNode{}; + AZ::rapidxml::xml_node* m_xmlNode = nullptr; // indicates if the connection is valid for the currently loaded middleware bool m_isValid{ false }; + private: // Rapid XML provides a 'clone_node' utility that will copy an entire node tree, // but it only copies pointers of any strings in the node names and values. - // This causes problems with storing raw xml nodes as this class does because strings - // will be pointing into the memory pool of an xml document that has gone out of scope. + // This causes problems with storage of xml trees, as this class does, because strings + // will be pointing into an xml document's file buffer that has gone out of scope. // This function is a rewritten version of 'clone_node' that does the deep copy of strings // into the new destination tree. - [[nodiscard]] static AZStd::unique_ptr> DeepCopyNode(AZ::rapidxml::xml_node* srcNode) + [[nodiscard]] AZ::rapidxml::xml_node* DeepCopyNode(AZ::rapidxml::xml_node* srcNode) { - AZStd::unique_ptr> destNode; - if (srcNode) + if (!srcNode) { - XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); - destNode.reset(xmlAlloc.allocate_node(srcNode->type())); + return nullptr; + } - destNode->name(xmlAlloc.allocate_string(srcNode->name(), srcNode->name_size()), srcNode->name_size()); - destNode->value(xmlAlloc.allocate_string(srcNode->value(), srcNode->value_size()), srcNode->value_size()); + XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); + AZ::rapidxml::xml_node* destNode = xmlAlloc.allocate_node(srcNode->type()); - for (AZ::rapidxml::xml_node* child = srcNode->first_node(); child != nullptr; child = child->next_sibling()) - { - destNode->append_node(DeepCopyNode(child).release()); - } + destNode->name(xmlAlloc.allocate_string(srcNode->name(), srcNode->name_size()), srcNode->name_size()); + destNode->value(xmlAlloc.allocate_string(srcNode->value(), srcNode->value_size()), srcNode->value_size()); - for (AZ::rapidxml::xml_attribute* attr = srcNode->first_attribute(); attr != nullptr; attr = attr->next_attribute()) - { - destNode->append_attribute(xmlAlloc.allocate_attribute( - xmlAlloc.allocate_string(attr->name(), attr->name_size()), - xmlAlloc.allocate_string(attr->value(), attr->value_size()), - attr->name_size(), - attr->value_size() - )); - } + for (AZ::rapidxml::xml_node* child = srcNode->first_node(); child != nullptr; child = child->next_sibling()) + { + destNode->append_node(DeepCopyNode(child)); + } + + for (AZ::rapidxml::xml_attribute* attr = srcNode->first_attribute(); attr != nullptr; attr = attr->next_attribute()) + { + destNode->append_attribute(xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(attr->name(), attr->name_size()), + xmlAlloc.allocate_string(attr->value(), attr->value_size()), + attr->name_size(), + attr->value_size() + )); } return destNode; diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp index 220cd32b5c..30889e33f0 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp @@ -475,7 +475,7 @@ namespace AudioControls control->AddConnection(connection); } - control->m_connectionNodes.push_back(SRawConnectionData(childNode, connection != nullptr)); + control->m_connectionNodes.emplace_back(childNode, connection != nullptr); childNode = childNode->next_sibling(); } @@ -517,7 +517,7 @@ namespace AudioControls { control->AddConnection(connection); } - control->m_connectionNodes.push_back(SRawConnectionData(connectionNode, connection != nullptr)); + control->m_connectionNodes.emplace_back(connectionNode, connection != nullptr); connectionNode = connectionNode->next_sibling(); } configGroupNode = configGroupNode->next_sibling(); @@ -534,7 +534,7 @@ namespace AudioControls { control->AddConnection(connection); } - control->m_connectionNodes.push_back(SRawConnectionData(connectionNode, connection != nullptr)); + control->m_connectionNodes.emplace_back(connectionNode, connection != nullptr); connectionNode = connectionNode->next_sibling(); } } @@ -576,7 +576,7 @@ namespace AudioControls requestNode->append_node(valueNode); - childControl->m_connectionNodes.push_back(SRawConnectionData(requestNode, false)); + childControl->m_connectionNodes.emplace_back(requestNode, false); return childControl; } diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp index 91f9aa5138..eb022b706f 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp @@ -356,8 +356,8 @@ namespace AudioControls { if (!connectionNode.m_isValid) { - auto nodeCopy = SRawConnectionData::DeepCopyNode(connectionNode.m_xmlNode.get()); - node->append_node(nodeCopy.release()); + XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); + node->append_node(xmlAlloc.clone_node(connectionNode.m_xmlNode)); } } @@ -371,7 +371,7 @@ namespace AudioControls childNode != nullptr) { node->append_node(childNode); - control->m_connectionNodes.push_back(SRawConnectionData(childNode, true)); + control->m_connectionNodes.emplace_back(childNode, true); } } } From dcfeae1cc9ad9327552fbf48d78ca3bd594601f9 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 6 Aug 2021 16:24:06 -0700 Subject: [PATCH 37/38] warnings not previously detected (#2954) Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- Gems/LyShine/Code/Editor/Animation/UiAnimUndoManager.cpp | 2 +- Gems/LyShine/Code/Editor/AssetTreeEntry.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/LyShine/Code/Editor/Animation/UiAnimUndoManager.cpp b/Gems/LyShine/Code/Editor/Animation/UiAnimUndoManager.cpp index 82eb6091e6..025b8c94d8 100644 --- a/Gems/LyShine/Code/Editor/Animation/UiAnimUndoManager.cpp +++ b/Gems/LyShine/Code/Editor/Animation/UiAnimUndoManager.cpp @@ -59,7 +59,7 @@ public: virtual bool IsEmpty() const { return m_undoObjects.empty(); }; virtual void Undo(bool bUndo) { - for (int i = m_undoObjects.size() - 1; i >= 0; i--) + for (int i = aznumeric_cast(m_undoObjects.size()) - 1; i >= 0; i--) { m_undoObjects[i]->Undo(bUndo); } diff --git a/Gems/LyShine/Code/Editor/AssetTreeEntry.cpp b/Gems/LyShine/Code/Editor/AssetTreeEntry.cpp index caff5d2da0..5a281ba312 100644 --- a/Gems/LyShine/Code/Editor/AssetTreeEntry.cpp +++ b/Gems/LyShine/Code/Editor/AssetTreeEntry.cpp @@ -136,7 +136,7 @@ AssetTreeEntry* AssetTreeEntry::BuildAssetTree(const AZ::Data::AssetType& assetT // product name stored in db is in all lower case, but we want to preserve case here AzFramework::StringFunc::Path::Split(product->GetParent()->GetRelativePath().c_str(), nullptr, &path, &name); // find next character position after default slice path in order to generate hierarchical sub-menus matching the subfolders - int pos = AzFramework::StringFunc::Find(path.c_str(), pathToSearch.c_str()) + pathToSearch.length(); + const size_t pos = AzFramework::StringFunc::Find(path.c_str(), pathToSearch.c_str()) + pathToSearch.length(); assetTree->Insert(path.substr(pos), name, product->GetAssetId()); } return assetTree; From 733dc31518f106972b5d2e902c8ce49c08a84cac Mon Sep 17 00:00:00 2001 From: Benjamin Jillich Date: Thu, 5 Aug 2021 16:37:47 +0200 Subject: [PATCH 38/38] Fixed emfx unit tests Signed-off-by: Benjamin Jillich --- .../RCExt/Actor/ActorGroupExporter.cpp | 2 -- .../Code/EMotionFX/Rendering/Common/Camera.h | 1 + .../EMotionFX/Rendering/Common/RenderUtil.cpp | 24 ++++++++++--------- .../Rendering/Common/ScaleManipulator.h | 2 +- Gems/EMotionFX/Code/EMotionFX/Source/Actor.h | 2 +- .../Code/EMotionFX/Source/ActorInstance.cpp | 2 +- .../Source/RenderPlugin/RenderWidget.cpp | 2 +- .../Components/EditorActorComponent.cpp | 4 ++-- .../Code/Tests/Integration/CanAddActor.cpp | 2 +- .../Code/Tests/TestAssetCode/ActorFactory.h | 2 +- .../Code/Tests/TestAssets/Rin/rin.actor | 4 ++-- 11 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp index ba8168476c..a498ed2cb7 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp @@ -49,8 +49,6 @@ namespace EMotionFX if (serializeContext) { // Increasing the version number of the actor group exporter will make sure all actor products will be force re-generated. - // Version history: - // v3: Introduced Actor_Nodes2 (replaced Actor_Nodes) and Actor_Node2 (replaced Actor_Node) serializeContext->Class()->Version(3); } } diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/Camera.h b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/Camera.h index 0c7b19384e..6a95621b0d 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/Camera.h +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/Camera.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include "MCommonConfig.h" diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp index 8464cb2b2a..9c0a4af895 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp @@ -240,8 +240,8 @@ namespace MCommon // render selection gizmo around the given AABB void RenderUtil::RenderSelection(const AZ::Aabb& box, const MCore::RGBAColor& color, bool directlyRender) { - const AZ::Vector3 min = box.GetMin(); - const AZ::Vector3 max = box.GetMax(); + const AZ::Vector3& min = box.GetMin(); + const AZ::Vector3& max = box.GetMax(); const float radius = AZ::Vector3(box.GetMax() - box.GetMin()).GetLength() * 0.5f; const float scale = radius * 0.1f; const AZ::Vector3 up = AZ::Vector3(0.0f, 1.0f, 0.0f) * scale; @@ -249,15 +249,17 @@ namespace MCommon const AZ::Vector3 front = AZ::Vector3(0.0f, 0.0f, 1.0f) * scale; // generate our vertices - AZ::Vector3 p[8]; - p[0].Set(min.GetX(), min.GetY(), min.GetZ()); - p[1].Set(max.GetX(), min.GetY(), min.GetZ()); - p[2].Set(max.GetX(), min.GetY(), max.GetZ()); - p[3].Set(min.GetX(), min.GetY(), max.GetZ()); - p[4].Set(min.GetX(), max.GetY(), min.GetZ()); - p[5].Set(max.GetX(), max.GetY(), min.GetZ()); - p[6].Set(max.GetX(), max.GetY(), max.GetZ()); - p[7].Set(min.GetX(), max.GetY(), max.GetZ()); + const AZStd::array p + { + AZ::Vector3{min.GetX(), min.GetY(), min.GetZ()}, + AZ::Vector3{max.GetX(), min.GetY(), min.GetZ()}, + AZ::Vector3{max.GetX(), min.GetY(), max.GetZ()}, + AZ::Vector3{min.GetX(), min.GetY(), max.GetZ()}, + AZ::Vector3{min.GetX(), max.GetY(), min.GetZ()}, + AZ::Vector3{max.GetX(), max.GetY(), min.GetZ()}, + AZ::Vector3{max.GetX(), max.GetY(), max.GetZ()}, + AZ::Vector3{min.GetX(), max.GetY(), max.GetZ()}, + }; // render the box RenderLine(p[0], p[0] + up, color); diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/ScaleManipulator.h b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/ScaleManipulator.h index 8cfd3ec541..6d2f167893 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/ScaleManipulator.h +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/ScaleManipulator.h @@ -8,7 +8,7 @@ #pragma once -// include the Core system +#include #include #include #include "MCommonConfig.h" diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h index 1c1173a9a2..faf5b6b94d 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp index 162d826ca4..b80aab3c50 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp @@ -635,7 +635,7 @@ namespace EMotionFX } // Expand the bounding volume by a tolerance area in case set. - if (m_boundsExpandBy > 0.0f) + if (!AZ::IsClose(m_boundsExpandBy, 0.0f)) { const AZ::Vector3 center = m_aabb.GetCenter(); const AZ::Vector3 halfExtents = m_aabb.GetExtents() * 0.5f; diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.cpp index fb0ce4fdbf..25ae2ba2d4 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.cpp @@ -6,7 +6,6 @@ * */ -// include the required headers #include "RenderWidget.h" #include "RenderPlugin.h" #include @@ -21,6 +20,7 @@ #include "../EMStudioManager.h" #include "../MainWindow.h" #include +#include namespace EMStudio diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index ed5333da9c..160cbe8ee4 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -67,7 +67,7 @@ namespace EMotionFX "The method used to compute the Actor bounding box. NOTE: ordered by least expensive to compute to most expensive to compute.") ->EnumAttribute(ActorInstance::BOUNDS_STATIC_BASED, "Static (Recommended)") ->EnumAttribute(ActorInstance::BOUNDS_NODE_BASED, "Bone position-based") - ->EnumAttribute(ActorInstance::BOUNDS_MESH_BASED, "Mesh vertex-based (Expensive)") + ->EnumAttribute(ActorInstance::BOUNDS_MESH_BASED, "Mesh vertex-based (VERY EXPENSIVE)") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(AZ::Edit::UIHandlers::Default, &ActorComponent::BoundingBoxConfiguration::m_expandBy, "Expand by", @@ -75,7 +75,7 @@ namespace EMotionFX "This can be used to add a tolerance area to the calculated bounding box to avoid clipping the character too early. " "A static bounding box together with the expansion is the recommended way for maximum performance. (Default = 25%)") ->Attribute(AZ::Edit::Attributes::Suffix, " %") - ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->Attribute(AZ::Edit::Attributes::Min, -100.0f + AZ::Constants::Tolerance) ->DataElement(AZ::Edit::UIHandlers::Default, &ActorComponent::BoundingBoxConfiguration::m_autoUpdateBounds, "Automatically update bounds?", "If true, bounds are automatically updated based on some frequency. Otherwise bounds are computed only at creation or when triggered manually") diff --git a/Gems/EMotionFX/Code/Tests/Integration/CanAddActor.cpp b/Gems/EMotionFX/Code/Tests/Integration/CanAddActor.cpp index 3c529c93dd..9b18ca5862 100644 --- a/Gems/EMotionFX/Code/Tests/Integration/CanAddActor.cpp +++ b/Gems/EMotionFX/Code/Tests/Integration/CanAddActor.cpp @@ -40,7 +40,7 @@ namespace EMotionFX } // Ensure the Actor is correct - ASSERT_TRUE(GetActorManager().FindActorByName("rinactor")); + ASSERT_TRUE(GetActorManager().FindActorByName("rinActor")); EXPECT_EQ(GetActorManager().GetNumActors(), 1); } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Tests/TestAssetCode/ActorFactory.h b/Gems/EMotionFX/Code/Tests/TestAssetCode/ActorFactory.h index 73519e079e..198fe4ce49 100644 --- a/Gems/EMotionFX/Code/Tests/TestAssetCode/ActorFactory.h +++ b/Gems/EMotionFX/Code/Tests/TestAssetCode/ActorFactory.h @@ -22,7 +22,7 @@ namespace EMotionFX actor->SetID(0); actor->GetSkeleton()->UpdateNodeIndexValues(0); actor->ResizeTransformData(); - actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*generateOBBs=*/false, /*convertUnitType=*/false); + actor->PostCreateInit(/*makeGeomLodsCompatibleWithSkeletalLODs=*/false, /*convertUnitType=*/false); return actor; } }; diff --git a/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor b/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor index 5a79fd5f48..20d4ffef85 100644 --- a/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor +++ b/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7db74f39a261bb70e1fdbdd546c337107809cdbdd1fc52568a0d30358b0f83d7 -size 30005 +oid sha256:55ecbc78a913c808cd007326b51dfc98b943b196b77fd6dc16aff0892b112e74 +size 16948