ソースを参照

Stabilization/2305 (as of 4/4/2023) -> development (#15544)

Olex Lozitskiy 2 年 前
コミット
1e4df46562
44 ファイル変更626 行追加175 行削除
  1. 1 0
      Code/Editor/LevelFileDialog.cpp
  2. 3 2
      Code/Editor/ViewPane.cpp
  3. 3 0
      Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h
  4. 22 0
      Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h
  5. 2 0
      Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h
  6. 34 4
      Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp
  7. 11 5
      Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/BreadCrumbs.cpp
  8. 65 21
      Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/Action/ActionManager.cpp
  9. 6 0
      Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/Action/ActionManager.h
  10. 1 0
      Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/Action/EditorAction.cpp
  11. 4 0
      Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/HotKey/HotKeyManager.cpp
  12. 102 0
      Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/ToolBar/EditorToolBar.cpp
  13. 19 0
      Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/ToolBar/EditorToolBar.h
  14. 1 0
      Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/ToolBar/ToolBarManager.cpp
  15. 9 3
      Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorManager.cpp
  16. 13 4
      Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorManager.h
  17. 16 0
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp
  18. 46 1
      Code/Framework/AzToolsFramework/Tests/ManipulatorCoreTests.cpp
  19. 1 1
      Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp
  20. 5 2
      Code/Tools/AssetProcessor/native/resourcecompiler/rccontroller.cpp
  21. 4 5
      Code/Tools/AssetProcessor/native/resourcecompiler/rcjoblistmodel.cpp
  22. 20 5
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/AmbientOcclusion.preset
  23. 20 5
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Emissive.preset
  24. 3 0
      Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Image/ImageSystemDescriptor.h
  25. 58 3
      Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp
  26. 1 0
      Gems/Atom/RPI/Code/Source/RPI.Reflect/Image/ImageSystemDescriptor.cpp
  27. 4 2
      Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp
  28. 2 1
      Gems/Atom/RPI/Registry/atom_rpi.setreg
  29. 12 18
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/EntityPreviewViewport/EntityPreviewViewportScene.cpp
  30. 10 2
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/Graph/GraphCompiler.cpp
  31. 2 0
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/Graph/GraphDocument.cpp
  32. 2 2
      Gems/CameraFramework/Code/Source/CameraRigComponent.cpp
  33. 9 6
      Gems/GraphCanvas/Code/Source/Components/NodePropertyDisplays/StringNodePropertyDisplay.cpp
  34. 66 66
      Gems/GraphCanvas/Code/Source/Components/NodePropertyDisplays/VectorNodePropertyDisplay.cpp
  35. 1 0
      Gems/GraphModel/Code/Include/GraphModel/Integration/GraphController.h
  36. 18 7
      Gems/GraphModel/Code/Source/Integration/GraphController.cpp
  37. 1 1
      Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerEditorServerBus.h
  38. 3 2
      Gems/Multiplayer/Code/Source/Debug/MultiplayerConnectionViewportMessageSystemComponent.cpp
  39. 2 2
      Gems/Multiplayer/Code/Source/Debug/MultiplayerConnectionViewportMessageSystemComponent.h
  40. 2 2
      Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorAutomation.cpp
  41. 1 1
      Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorAutomation.h
  42. 18 2
      Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp
  43. 1 0
      Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp
  44. 2 0
      Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.cpp

+ 1 - 0
Code/Editor/LevelFileDialog.cpp

@@ -66,6 +66,7 @@ CLevelFileDialog::CLevelFileDialog(bool openDialog, QWidget* parent)
     if (m_bOpenDialog)
     {
         setWindowTitle(tr("Open Level"));
+        ui->treeView->expandToDepth(1);
         ui->newFolderButton->setVisible(false);
         ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Open"));
     }

+ 3 - 2
Code/Editor/ViewPane.cpp

@@ -322,7 +322,7 @@ void CLayoutViewPane::OnActionRegistrationHook()
     {
         constexpr AZStd::string_view actionIdentifier = "o3de.action.viewport.menuIcon";
         AzToolsFramework::ActionProperties actionProperties;
-        actionProperties.m_name = "Menu";
+        actionProperties.m_name = "Options";
         actionProperties.m_iconPath = ":/Menu/menu.svg";
 
         m_actionManagerInterface->RegisterAction(
@@ -331,7 +331,8 @@ void CLayoutViewPane::OnActionRegistrationHook()
             actionProperties,
             []
             {
-            });
+            }
+        );
     }
 
     // Viewport Debug Information

+ 3 - 0
Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h

@@ -22,6 +22,9 @@ namespace AZ
 
 namespace AzFramework
 {
+    //! Provides an interface to query and set various view/camera properties.
+    //! These include a camera's view matrix, projection matrix and transform (inverse of view matrix).
+    //! @note The bus is addressed by ViewportId and should be preferred over existing global camera buses.
     class ViewportRequests : public AZ::EBusTraits
     {
     public:

+ 22 - 0
Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h

@@ -43,6 +43,10 @@ namespace AzManipulatorTestFramework
         DerivedDispatcherT* MouseMButtonDown();
         //! Set the middle mouse button up.
         DerivedDispatcherT* MouseMButtonUp();
+        //! Set the right mouse button down.
+        DerivedDispatcherT* MouseRButtonDown();
+        //! Set the right mouse button up.
+        DerivedDispatcherT* MouseRButtonUp();
         //! Send a double click event.
         DerivedDispatcherT* MouseLButtonDoubleClick();
         //! Set the keyboard modifier button down.
@@ -79,6 +83,8 @@ namespace AzManipulatorTestFramework
         virtual void MouseLButtonUpImpl() = 0;
         virtual void MouseMButtonDownImpl() = 0;
         virtual void MouseMButtonUpImpl() = 0;
+        virtual void MouseRButtonDownImpl() = 0;
+        virtual void MouseRButtonUpImpl() = 0;
         virtual void MouseLButtonDoubleClickImpl() = 0;
         virtual void MousePositionImpl(const AzFramework::ScreenPoint& position) = 0;
         virtual void KeyboardModifierDownImpl(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier) = 0;
@@ -205,6 +211,22 @@ namespace AzManipulatorTestFramework
         return static_cast<DerivedDispatcherT*>(this);
     }
 
+    template<typename DerivedDispatcherT>
+    DerivedDispatcherT* ActionDispatcher<DerivedDispatcherT>::MouseRButtonDown()
+    {
+        Log("Mouse right button down");
+        MouseRButtonDownImpl();
+        return static_cast<DerivedDispatcherT*>(this);
+    }
+
+    template<typename DerivedDispatcherT>
+    DerivedDispatcherT* ActionDispatcher<DerivedDispatcherT>::MouseRButtonUp()
+    {
+        Log("Mouse right button up");
+        MouseRButtonUpImpl();
+        return static_cast<DerivedDispatcherT*>(this);
+    }
+
     template<typename DerivedDispatcherT>
     DerivedDispatcherT* ActionDispatcher<DerivedDispatcherT>::MouseLButtonDoubleClick()
     {

+ 2 - 0
Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h

@@ -64,6 +64,8 @@ namespace AzManipulatorTestFramework
         void MouseLButtonUpImpl() override;
         void MouseMButtonDownImpl() override;
         void MouseMButtonUpImpl() override;
+        void MouseRButtonDownImpl() override;
+        void MouseRButtonUpImpl() override;
         void MouseLButtonDoubleClickImpl() override;
         void MousePositionImpl(const AzFramework::ScreenPoint& position) override;
         void KeyboardModifierDownImpl(KeyboardModifier keyModifier) override;

+ 34 - 4
Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp

@@ -75,7 +75,9 @@ namespace AzManipulatorTestFramework
     {
         ToggleOn(GetMouseInteractionEvent()->m_mouseInteraction.m_mouseButtons.m_mouseButtons, MouseButton::Left);
         GetMouseInteractionEvent()->m_mouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent::Down;
-        m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(*m_event);
+        auto mouseEvent = *GetMouseInteractionEvent();
+        mouseEvent.m_mouseInteraction.m_mouseButtons.m_mouseButtons = aznumeric_cast<AZ::u32>(MouseButton::Left);
+        m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(mouseEvent);
         // the mouse position will be the same as the previous event, thus the delta will be 0
         MouseMoveAfterButton();
     }
@@ -83,7 +85,9 @@ namespace AzManipulatorTestFramework
     void ImmediateModeActionDispatcher::MouseLButtonUpImpl()
     {
         GetMouseInteractionEvent()->m_mouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent::Up;
-        m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(*m_event);
+        auto mouseEvent = *GetMouseInteractionEvent();
+        mouseEvent.m_mouseInteraction.m_mouseButtons.m_mouseButtons = aznumeric_cast<AZ::u32>(MouseButton::Left);
+        m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(mouseEvent);
         ToggleOff(GetMouseInteractionEvent()->m_mouseInteraction.m_mouseButtons.m_mouseButtons, MouseButton::Left);
         // the mouse position will be the same as the previous event, thus the delta will be 0
         MouseMoveAfterButton();
@@ -93,7 +97,9 @@ namespace AzManipulatorTestFramework
     {
         ToggleOn(GetMouseInteractionEvent()->m_mouseInteraction.m_mouseButtons.m_mouseButtons, MouseButton::Middle);
         GetMouseInteractionEvent()->m_mouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent::Down;
-        m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(*m_event);
+        auto mouseEvent = *GetMouseInteractionEvent();
+        mouseEvent.m_mouseInteraction.m_mouseButtons.m_mouseButtons = aznumeric_cast<AZ::u32>(MouseButton::Middle);
+        m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(mouseEvent);
         // the mouse position will be the same as the previous event, thus the delta will be 0
         MouseMoveAfterButton();
     }
@@ -101,12 +107,36 @@ namespace AzManipulatorTestFramework
     void ImmediateModeActionDispatcher::MouseMButtonUpImpl()
     {
         GetMouseInteractionEvent()->m_mouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent::Up;
-        m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(*m_event);
+        auto mouseEvent = *GetMouseInteractionEvent();
+        mouseEvent.m_mouseInteraction.m_mouseButtons.m_mouseButtons = aznumeric_cast<AZ::u32>(MouseButton::Middle);
+        m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(mouseEvent);
         ToggleOff(GetMouseInteractionEvent()->m_mouseInteraction.m_mouseButtons.m_mouseButtons, MouseButton::Middle);
         // the mouse position will be the same as the previous event, thus the delta will be 0
         MouseMoveAfterButton();
     }
 
+    void ImmediateModeActionDispatcher::MouseRButtonDownImpl()
+    {
+        ToggleOn(GetMouseInteractionEvent()->m_mouseInteraction.m_mouseButtons.m_mouseButtons, MouseButton::Right);
+        GetMouseInteractionEvent()->m_mouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent::Down;
+        auto mouseEvent = *GetMouseInteractionEvent();
+        mouseEvent.m_mouseInteraction.m_mouseButtons.m_mouseButtons = aznumeric_cast<AZ::u32>(MouseButton::Right);
+        m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(mouseEvent);
+        // the mouse position will be the same as the previous event, thus the delta will be 0
+        MouseMoveAfterButton();
+    }
+
+    void ImmediateModeActionDispatcher::MouseRButtonUpImpl()
+    {
+        GetMouseInteractionEvent()->m_mouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent::Up;
+        auto mouseEvent = *GetMouseInteractionEvent();
+        mouseEvent.m_mouseInteraction.m_mouseButtons.m_mouseButtons = aznumeric_cast<AZ::u32>(MouseButton::Right);
+        m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(mouseEvent);
+        ToggleOff(GetMouseInteractionEvent()->m_mouseInteraction.m_mouseButtons.m_mouseButtons, MouseButton::Right);
+        // the mouse position will be the same as the previous event, thus the delta will be 0
+        MouseMoveAfterButton();
+    }
+
     void ImmediateModeActionDispatcher::MouseLButtonDoubleClickImpl()
     {
         GetMouseInteractionEvent()->m_mouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent::DoubleClick;

+ 11 - 5
Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/BreadCrumbs.cpp

@@ -531,7 +531,7 @@ namespace AzQtComponents
         AZ_PUSH_DISABLE_WARNING(4566, "-Wunknown-warning-option")//4566:character represented by universal-character-name 'u00a0' and 'u203a' cannot be represented in the current code page (ex.cp932)
         const qreal iconSpaceWidth = g_iconWidth + fm.horizontalAdvance(QStringLiteral("\u00a0\u00a0"));
         AZ_POP_DISABLE_WARNING
-        QString plainTextPath;
+
         QString linkColor = isEnabled() ? m_config.linkColor : m_config.disabledLinkColor;
         auto formatLink = [linkColor](const QString& fullPath, const QString& shortPath) -> QString
         {
@@ -552,8 +552,6 @@ namespace AzQtComponents
         }
 
         // last section is not clickable
-        plainTextPath = m_truncatedPaths.takeLast();
-
         int index = m_currentPathSize - 1;
 
         // to estimate how much the rendered html will take, we need to take icons into account
@@ -561,9 +559,17 @@ namespace AzQtComponents
 
         const QString firstIconHtml = generateIconHtml(index);
         totalIconsWidth += firstIconHtml.isEmpty() ? .0 : iconSpaceWidth;
-        htmlString.prepend(generateIconHtml(index) + plainTextPath);
+
+        if ((fm.horizontalAdvance(m_truncatedPaths.last()) + totalIconsWidth) > availableWidth)
+        {
+            m_label->clear();
+            return;
+        }
+        htmlString.prepend(firstIconHtml + m_truncatedPaths.takeLast());
         --index;
 
+        QString plainTextPath;
+
         if (!m_truncatedPaths.isEmpty())
         {
             prependSeparators();
@@ -698,7 +704,7 @@ namespace AzQtComponents
     void BreadCrumbs::showTruncatedPathsMenu()
     {
         QMenu hiddenPaths;
-        for (int i = m_truncatedPaths.size() - 1; i >= 0; i--)
+        for (int i = 0; i < m_truncatedPaths.size(); ++i)
         {
             hiddenPaths.addAction(m_truncatedPaths.at(i), [this, i]() {
                 onLinkActivated(buildPathFromList(m_truncatedPaths, i + 1));

+ 65 - 21
Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/Action/ActionManager.cpp

@@ -10,6 +10,8 @@
 #include <QtGui/private/qguiapplication_p.h>
 #include <QShortcutEvent>
 
+#include <AzQtComponents/Components/StyledDockWidget.h>
+
 #include <AzToolsFramework/ActionManager/Action/ActionManager.h>
 #include <AzToolsFramework/ActionManager/Menu/MenuManagerInterface.h>
 
@@ -29,6 +31,36 @@ namespace AzToolsFramework
         case QEvent::ShortcutOverride:
         {
             m_shortcutWasTriggered = false;
+
+            // Handle the case where the shortcut might've been passed directly to the dock widget that owns
+            // the actual widget/action context if the user had tried to focus part of the widget that
+            // doesn't accept focus.
+            auto* dockWidget = qobject_cast<AzQtComponents::StyledDockWidget*>(watched);
+            if (dockWidget)
+            {
+                auto watchedWidget = dockWidget->widget();
+                auto contextIdentifierVariant = watchedWidget->property(ActionManager::ActionContextWidgetIdentifier.data());
+
+                if (contextIdentifierVariant.isValid())
+                {
+                    QString contextIdentifier = contextIdentifierVariant.toString();
+                    auto actionManagerInternalInterface = AZ::Interface<ActionManagerInternalInterface>::Get();
+                    auto widgetWatcher = actionManagerInternalInterface->GetActionContextWidgetWatcher(contextIdentifier.toUtf8().constData());
+
+                    AZ_Assert(widgetWatcher, "Unable to find widget watcher for action context: %s", contextIdentifier.toUtf8().constData());
+
+                    // Check if the widget has any actions that could accept the shortcut event
+                    auto keyEvent = static_cast<QKeyEvent*>(event);
+                    if (widgetWatcher->TriggerActiveActionsForWidget(watchedWidget, keyEvent))
+                    {
+                        // We need to accept the event in addition to return true on this event filter to ensure the event doesn't get propagated
+                        // to any parent widgets. Signal the application eventFilter to eat the KeyPress that will be spawned by accepting the event.
+                        SetShortcutTriggeredFlag();
+                        event->accept();
+                        return true;
+                    }
+                }
+            }
             break;
         }
         case QEvent::KeyPress:
@@ -72,29 +104,9 @@ namespace AzToolsFramework
             }
 
             auto keyEvent = static_cast<QKeyEvent*>(event);
-            int keyCode = keyEvent->key();
-            Qt::KeyboardModifiers modifiers = keyEvent->modifiers();
-            if (modifiers & Qt::ShiftModifier)
-            {
-                keyCode += Qt::SHIFT;
-            }
-            if (modifiers & Qt::ControlModifier)
-            {
-                keyCode += Qt::CTRL;
-            }
-            if (modifiers & Qt::AltModifier)
-            {
-                keyCode += Qt::ALT;
-            }
-            if (modifiers & Qt::MetaModifier)
-            {
-                keyCode += Qt::META;
-            }
-
-            QKeySequence keySequence(keyCode);
             QWidget* watchedWidget = qobject_cast<QWidget*>(watched);
 
-            if (TriggerActiveActionsWithShortcut(m_editorActionContext->GetActions(), watchedWidget->actions(), keySequence))
+            if (TriggerActiveActionsWithShortcut(m_editorActionContext->GetActions(), watchedWidget->actions(), keyEvent))
             {
                 // We need to accept the event in addition to return true on this event filter to ensure the event doesn't get propagated
                 // to any parent widgets. Signal the application eventFilter to eat the KeyPress that will be spawned by accepting the event.
@@ -166,6 +178,38 @@ namespace AzToolsFramework
         return !matchingActions.isEmpty();
     }
 
+    bool ActionContextWidgetWatcher::TriggerActiveActionsWithShortcut(
+        const QList<QAction*>& contextActions, const QList<QAction*>& widgetActions, const QKeyEvent* shortcutKeyEvent)
+    {
+        int keyCode = shortcutKeyEvent->key();
+        Qt::KeyboardModifiers modifiers = shortcutKeyEvent->modifiers();
+        if (modifiers & Qt::ShiftModifier)
+        {
+            keyCode += Qt::SHIFT;
+        }
+        if (modifiers & Qt::ControlModifier)
+        {
+            keyCode += Qt::CTRL;
+        }
+        if (modifiers & Qt::AltModifier)
+        {
+            keyCode += Qt::ALT;
+        }
+        if (modifiers & Qt::MetaModifier)
+        {
+            keyCode += Qt::META;
+        }
+
+        QKeySequence keySequence(keyCode);
+
+        return TriggerActiveActionsWithShortcut(contextActions, widgetActions, keySequence);
+    }
+
+    bool ActionContextWidgetWatcher::TriggerActiveActionsForWidget(const QWidget* watchedWidget, const QKeyEvent* keyEvent)
+    {
+        return TriggerActiveActionsWithShortcut(m_editorActionContext->GetActions(), watchedWidget->actions(), keyEvent);
+    }
+
     ActionManager::ActionManager()
     {
         AZ::Interface<ActionManagerInterface>::Register(this);

+ 6 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/Action/ActionManager.h

@@ -38,9 +38,13 @@ namespace AzToolsFramework
 
         bool eventFilter(QObject* watched, QEvent* event) override;
 
+        bool TriggerActiveActionsForWidget(const QWidget* watchedWidget, const QKeyEvent* keyEvent);
+
     private:
         static bool TriggerActiveActionsWithShortcut(
             const QList<QAction*>& contextActions, const QList<QAction*>& widgetActions, const QKeySequence& shortcutKeySequence);
+        static bool TriggerActiveActionsWithShortcut(
+            const QList<QAction*>& contextActions, const QList<QAction*>& widgetActions, const QKeyEvent* shortcutKeyEvent);
 
         ApplicationWatcher* m_applicationWatcher = nullptr;
         EditorActionContext* m_editorActionContext = nullptr;
@@ -56,6 +60,8 @@ namespace AzToolsFramework
         ActionManager();
         ~ActionManager();
 
+        static constexpr AZStd::string_view ActionContextWidgetIdentifier = "ActionContextWidgetIdentifier";
+
     private:
         // ActionManagerInterface overrides ...
         ActionManagerOperationResult RegisterActionContext(

+ 1 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/Action/EditorAction.cpp

@@ -41,6 +41,7 @@ namespace AzToolsFramework
     {
         UpdateIconFromPath();
         m_action = new QAction(m_icon, m_name.c_str(), this);
+        m_action->setObjectName(m_identifier.c_str());
 
         QObject::connect(
             m_action, &QAction::triggered, this,

+ 4 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/HotKey/HotKeyManager.cpp

@@ -46,6 +46,9 @@ namespace AzToolsFramework
                 contextIdentifier.c_str()));
         }
 
+        // Set the context identifier as a property on the widget so that the watcher can be queried easily later
+        widget->setProperty(ActionManager::ActionContextWidgetIdentifier.data(), contextIdentifier.c_str());
+
         widget->installEventFilter(widgetWatcher);
         return AZ::Success();
     }
@@ -61,6 +64,7 @@ namespace AzToolsFramework
                 contextIdentifier.c_str()));
         }
 
+        widget->setProperty(ActionManager::ActionContextWidgetIdentifier.data(), QVariant());
         widget->removeEventFilter(widgetWatcher);
         return AZ::Success();
     }

+ 102 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/ToolBar/EditorToolBar.cpp

@@ -13,14 +13,109 @@
 #include <AzToolsFramework/ActionManager/Menu/MenuManagerInternalInterface.h>
 #include <AzToolsFramework/ActionManager/ToolBar/ToolBarManagerInterface.h>
 
+#include <AzQtComponents/Components/Style.h>
+#include <AzQtComponents/Components/Widgets/ToolBar.h>
+
 #include <AzCore/Serialization/SerializeContext.h>
 
 #include <QMenu>
 #include <QToolBar>
 #include <QToolButton>
+#include <QWidgetAction>
 
 namespace AzToolsFramework
 {
+    ToolBarExpanderWatcher::ToolBarExpanderWatcher(QObject* parent)
+        : QObject(parent)
+    {
+    }
+
+    void ToolBarExpanderWatcher::Initialize()
+    {
+        s_actionManagerInterface = AZ::Interface<ActionManagerInterface>::Get();
+        AZ_Assert(s_actionManagerInterface, "EditorToolBar - Could not retrieve instance of ActionManagerInterface");
+
+        s_actionManagerInternalInterface = AZ::Interface<ActionManagerInternalInterface>::Get();
+        AZ_Assert(s_actionManagerInternalInterface, "EditorToolBar - Could not retrieve instance of ActionManagerInternalInterface");
+
+        s_menuManagerInterface = AZ::Interface<MenuManagerInterface>::Get();
+        AZ_Assert(s_menuManagerInterface, "EditorToolBar - Could not retrieve instance of MenuManagerInterface");
+
+        s_menuManagerInternalInterface = AZ::Interface<MenuManagerInternalInterface>::Get();
+        AZ_Assert(s_menuManagerInternalInterface, "EditorToolBar - Could not retrieve instance of MenuManagerInternalInterface");
+    }
+
+    bool ToolBarExpanderWatcher::eventFilter(QObject* obj, QEvent* event)
+    {
+        switch (event->type())
+        {
+        case QEvent::MouseButtonPress:
+        case QEvent::MouseButtonRelease:
+        case QEvent::MouseButtonDblClick:
+            {
+                if (qobject_cast<QToolButton*>(obj))
+                {
+                    auto mouseEvent = static_cast<QMouseEvent*>(event);
+                    auto expander = qobject_cast<QToolButton*>(obj);
+                    expander->setPopupMode(QToolButton::InstantPopup);
+
+                    auto toolbar = qobject_cast<QToolBar*>(expander->parentWidget());
+                    auto menu = new QMenu(expander);
+
+                    // Create a parent widget to more easily delete widget action widgets.
+                    auto parentWidget = new QWidget();
+
+                    for (auto action : toolbar->actions())
+                    {
+                        // Only show actions that are not visible in the toolbar.
+                        if (toolbar->widgetForAction(action)->isVisible())
+                        {
+                            continue;
+                        }
+
+                        if (auto widgetAction = qobject_cast<QWidgetAction*>(action))
+                        {
+                            if (auto toolButton = qobject_cast<QToolButton*>(widgetAction->defaultWidget());
+                                toolButton && toolButton->menu())
+                            {
+                                menu->addMenu(s_menuManagerInternalInterface->GetMenu(action->objectName().toStdString().c_str()));
+                            }
+                            else if (QWidget* widget =
+                                s_actionManagerInternalInterface->GenerateWidgetFromWidgetAction(action->objectName().toStdString().c_str()))
+                            {
+                                widget->setParent(parentWidget);
+
+                                QWidgetAction* w = new QWidgetAction(parentWidget);
+                                w->setDefaultWidget(widget);
+
+                                menu->addAction(w);
+                            }
+                        }
+                        else if (action->isSeparator())
+                        {
+                            menu->addSeparator();
+                        }
+                        else
+                        {
+                            menu->addAction(s_actionManagerInternalInterface->GetAction(action->objectName().toStdString().c_str()));
+                        }
+                    }
+
+                    menu->exec(mouseEvent->globalPos());
+
+                    delete menu;
+                    delete parentWidget;
+
+                    return true;
+                }
+
+                break;
+            }
+        }
+
+        return QObject::eventFilter(obj, event);
+    }
+
     EditorToolBar::EditorToolBar()
     {
     }
@@ -122,6 +217,11 @@ namespace AzToolsFramework
         QToolBar* toolBar = new QToolBar(m_name.c_str(), s_defaultParentWidget);
         toolBar->setMovable(false);
 
+        if (QToolButton* expander = AzQtComponents::ToolBar::getToolBarExpansionButton(toolBar))
+        {
+            expander->installEventFilter(new ToolBarExpanderWatcher(toolBar));
+        }
+
         m_toolBars.insert(toolBar);
 
         s_defaultParentWidget->connect(
@@ -239,6 +339,7 @@ namespace AzToolsFramework
                     {
                         m_widgetAction = new QWidgetAction(nullptr);
                         m_widgetAction->setDefaultWidget(widget);
+                        m_widgetAction->setObjectName(m_identifier.c_str());
                     }
                 }
                 break;
@@ -259,6 +360,7 @@ namespace AzToolsFramework
 
                         m_widgetAction = new QWidgetAction(s_defaultParentWidget);
                         m_widgetAction->setDefaultWidget(toolButton);
+                        m_widgetAction->setObjectName(m_subMenuIdentifier.c_str());
                     }
                 }
                 break;

+ 19 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/ToolBar/EditorToolBar.h

@@ -17,6 +17,8 @@
 #include <AzCore/std/optional.h>
 #include <AzCore/std/string/string.h>
 
+#include <QEvent>
+#include <QMouseEvent>
 #include <QWidgetAction>
 
 class QAction;
@@ -31,6 +33,23 @@ namespace AzToolsFramework
     class MenuManagerInternalInterface;
     class ToolBarManagerInterface;
 
+    //! A watcher class to handle the expander menu for a toolbar.
+    class ToolBarExpanderWatcher : public QObject
+    {
+    public:
+        explicit ToolBarExpanderWatcher(QObject* parent);
+
+        static void Initialize();
+
+    private:
+        bool eventFilter(QObject* obj, QEvent* event) override;
+
+        inline static ActionManagerInterface* s_actionManagerInterface = nullptr;
+        inline static ActionManagerInternalInterface* s_actionManagerInternalInterface = nullptr;
+        inline static MenuManagerInterface* s_menuManagerInterface = nullptr;
+        inline static MenuManagerInternalInterface* s_menuManagerInternalInterface = nullptr;
+    };
+
     //! Editor ToolBar class definitions.
     //! Wraps a QToolBar and provides additional functionality to handle and sort its items.
     class EditorToolBar final

+ 1 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/ActionManager/ToolBar/ToolBarManager.cpp

@@ -33,6 +33,7 @@ namespace AzToolsFramework
         ActionManagerNotificationBus::Handler::BusConnect();
 
         EditorToolBar::Initialize(defaultParentWidget);
+        ToolBarExpanderWatcher::Initialize();
         EditorToolBarArea::Initialize();
     }
 

+ 9 - 3
Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorManager.cpp

@@ -196,6 +196,7 @@ namespace AzToolsFramework
             {
                 if (manipulator->OnLeftMouseDown(interaction, intersectionDistance))
                 {
+                    m_mouseDownButton = ManipulatorMouseDownButton::Left;
                     m_activeManipulator = manipulator;
                     return true;
                 }
@@ -205,13 +206,14 @@ namespace AzToolsFramework
             {
                 if (manipulator->OnRightMouseDown(interaction, intersectionDistance))
                 {
+                    m_mouseDownButton = ManipulatorMouseDownButton::Right;
                     m_activeManipulator = manipulator;
                     return true;
                 }
             }
         }
 
-        return false;
+        return m_activeManipulator != nullptr;
     }
 
     bool ManipulatorManager::ConsumeViewportMouseRelease(const ViewportInteraction::MouseInteraction& interaction)
@@ -220,17 +222,21 @@ namespace AzToolsFramework
         // active manipulator - only notify mouse up if this was the case
         if (m_activeManipulator)
         {
-            if (interaction.m_mouseButtons.Left())
+            if (interaction.m_mouseButtons.Left() && m_mouseDownButton.has_value() &&
+                *m_mouseDownButton == ManipulatorMouseDownButton::Left)
             {
                 m_activeManipulator->OnLeftMouseUp(interaction);
                 m_activeManipulator.reset();
+                m_mouseDownButton.reset();
                 return true;
             }
 
-            if (interaction.m_mouseButtons.Right())
+            if (interaction.m_mouseButtons.Right() && m_mouseDownButton.has_value() &&
+                *m_mouseDownButton == ManipulatorMouseDownButton::Right)
             {
                 m_activeManipulator->OnRightMouseUp(interaction);
                 m_activeManipulator.reset();
+                m_mouseDownButton.reset();
                 return true;
             }
         }

+ 13 - 4
Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorManager.h

@@ -42,6 +42,13 @@ namespace AzToolsFramework
         bool m_interacting;
     };
 
+    //! The button that was used to start the manipulator behavior.
+    enum class ManipulatorMouseDownButton
+    {
+        Left,
+        Right
+    };
+
     //! This class serves to manage all relevant mouse events and coordinate all registered manipulators to function properly.
     //! ManipulatorManager does not manage the life cycle of specific manipulators. The users of manipulators are responsible
     //! for creating and deleting them at right time, as well as registering and unregistering accordingly.
@@ -115,13 +122,15 @@ namespace AzToolsFramework
         ManipulatorManagerId m_manipulatorManagerId; //!< This manipulator manager's id.
         ManipulatorId m_nextManipulatorIdToGenerate; //!< Id to use for the next manipulator that is registered with this manager.
 
-        AZStd::unordered_map<ManipulatorId, AZStd::shared_ptr<BaseManipulator>>
-            m_manipulatorIdToPtrMap; //!< Mapping from a manipulatorId to the corresponding manipulator.
-        AZStd::unordered_map<Picking::RegisteredBoundId, ManipulatorId>
-            m_boundIdToManipulatorIdMap; //!< Mapping from a boundId to the corresponding manipulatorId.
+        //! Mapping from a manipulatorId to the corresponding manipulator.
+        AZStd::unordered_map<ManipulatorId, AZStd::shared_ptr<BaseManipulator>> m_manipulatorIdToPtrMap;
+        //! Mapping from a boundId to the corresponding manipulatorId.
+        AZStd::unordered_map<Picking::RegisteredBoundId, ManipulatorId> m_boundIdToManipulatorIdMap;
 
         AZStd::shared_ptr<BaseManipulator> m_activeManipulator; //!< The manipulator we are currently interacting with.
         Picking::ManipulatorBoundManager m_boundManager; //!< All active manipulator bounds that could be interacted with.
+        //! The mouse button that is currently pressed (empty if no button is held).
+        AZStd::optional<ManipulatorMouseDownButton> m_mouseDownButton;
     };
 
     // The main/default ManipulatorManagerId to be used for

+ 16 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp

@@ -8,7 +8,9 @@
 
 #include <AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h>
 
+#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
 #include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
+#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
 
 #include <QTimer>
 
@@ -166,6 +168,8 @@ namespace AzToolsFramework::Prefab
 
         PrefabFocusNotificationBus::Handler::BusConnect(m_editorEntityContextId);
         ViewportEditorModeNotificationsBus::Handler::BusConnect(m_editorEntityContextId);
+
+        Refresh();
     }
 
     PrefabFocusPathWidget::~PrefabFocusPathWidget()
@@ -209,6 +213,18 @@ namespace AzToolsFramework::Prefab
 
     void PrefabFocusPathWidget::Refresh()
     {
+        auto prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
+
+        AZ::EntityId levelEntityId = prefabPublicInterface->GetLevelInstanceContainerEntityId();
+        AZStd::size_t childCount = 0;
+
+        EditorEntityInfoRequestBus::EventResult(childCount, levelEntityId, &EditorEntityInfoRequestBus::Events::GetChildCount);
+        // Ignore the refresh if there isn't a level loaded yet
+        if (childCount == 0)
+        {
+            return;
+        }
+        
         // Push new Path
         pushPath(m_prefabFocusPublicInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str());
 

+ 46 - 1
Code/Framework/AzToolsFramework/Tests/ManipulatorCoreTests.cpp

@@ -12,6 +12,7 @@
 #include <AzTest/AzTest.h>
 #include <AzToolsFramework/Application/ToolsApplication.h>
 #include <AzToolsFramework/Manipulators/LinearManipulator.h>
+#include <AzToolsFramework/Manipulators/ManipulatorBus.h>
 #include <AzToolsFramework/ToolsComponents/EditorLockComponent.h>
 #include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
 #include <AzToolsFramework/ToolsComponents/TransformComponent.h>
@@ -169,7 +170,8 @@ namespace UnitTest
         AzToolsFramework::ManipulatorViews views;
         views.emplace_back(AzToolsFramework::CreateManipulatorViewSphere(
             // note: use a small radius for the manipulator view/bounds to ensure precise mouse movement
-            AZ::Color{}, 0.001f,
+            AZ::Color{},
+            0.001f,
             [](const AzToolsFramework::ViewportInteraction::MouseInteraction&, bool, const AZ::Color&)
             {
                 return AZ::Color{};
@@ -210,4 +212,47 @@ namespace UnitTest
         // ensure final world positions match
         EXPECT_THAT(finalManipulatorTransform, IsCloseTolerance(finalTransformWorld, 0.01f));
     }
+
+    TEST_F(ManipulatorCoreInteractionFixture, MouseUpOfOtherMouseButtonDoesNotEndManipulatorInteraction)
+    {
+        // setup viewport/camera
+        m_cameraState.m_viewportSize = AzFramework::ScreenSize(1280, 720);
+        AzFramework::SetCameraTransform(m_cameraState, AZ::Transform::CreateIdentity());
+
+        AzToolsFramework::ManipulatorViews views;
+        views.emplace_back(AzToolsFramework::CreateManipulatorViewSphere(
+            // note: use a small radius for the manipulator view/bounds to ensure precise mouse movement
+            AZ::Color{},
+            0.001f,
+            [](const AzToolsFramework::ViewportInteraction::MouseInteraction&, bool, const AZ::Color&)
+            {
+                return AZ::Color{};
+            }));
+
+        m_linearManipulator->SetViews(views);
+        m_linearManipulator->Register(m_viewportManipulatorInteraction->GetManipulatorManagerId());
+
+        // the transform of the manipulator in world space
+        const auto transformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 10.0f, 0.0f));
+        // the position of the manipulator in screen space
+        const auto positionScreen = AzFramework::WorldToScreen(transformWorld.GetTranslation(), m_cameraState);
+
+        m_linearManipulator->SetSpace(AZ::Transform::CreateIdentity());
+        m_linearManipulator->SetLocalTransform(transformWorld);
+
+        // press and drag the mouse (starting where the surface manipulator is)
+        m_actionDispatcher->CameraState(m_cameraState)
+            ->MousePosition(positionScreen)
+            ->MouseLButtonDown()
+            ->MouseRButtonDown()
+            ->MouseRButtonUp();
+
+        bool interacting = false;
+        AzToolsFramework::ManipulatorManagerRequestBus::EventResult(
+            interacting,
+            m_viewportManipulatorInteraction->GetManipulatorManagerId(),
+            &AzToolsFramework::ManipulatorManagerRequestBus::Events::Interacting);
+
+        EXPECT_THAT(interacting, ::testing::IsTrue());
+    }
 } // namespace UnitTest

+ 1 - 1
Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp

@@ -5033,7 +5033,7 @@ namespace AssetProcessor
                     }
                     else
                     {
-                        AZ_Error("AssetProcessor", false, "%s", outcome.GetError().c_str());
+                        AZ_Error(AssetProcessor::ConsoleChannel, false, "%s", outcome.GetError().c_str());
                         return {};
                     }
                 }

+ 5 - 2
Code/Tools/AssetProcessor/native/resourcecompiler/rccontroller.cpp

@@ -172,6 +172,7 @@ namespace AssetProcessor
             details.m_jobEntry.m_platformInfo.m_identifier.c_str(),
             details.m_jobEntry.m_jobKey);
         bool cancelJob = false;
+        bool markCancelledJobAsFinished = false;
         RCJob* existingJob = nullptr;
         int existingJobIndex = -1;
 
@@ -192,6 +193,7 @@ namespace AssetProcessor
                         checkFile.GetPlatform().toUtf8().data(),
                         checkFile.GetJobDescriptor().toUtf8().data());
                     cancelJob = true;
+                    markCancelledJobAsFinished = existingJob->GetState() == RCJob::JobState::pending;
                 }
             }
 
@@ -219,6 +221,8 @@ namespace AssetProcessor
             {
                 existingJob = m_RCJobListModel.getItem(existingJobIndex);
 
+                // This does not set markCanceledJobAsFinished to true in either case the job is cancelled, because the job will
+                // have FinishJob called once through the callback in RCController::StartJob. FinishJob should not be called more than once.
                 if (existingJob->GetJobEntry().m_computedFingerprint != details.m_jobEntry.m_computedFingerprint)
                 {
                     AZ_TracePrintf(
@@ -262,11 +266,10 @@ namespace AssetProcessor
 
         if (cancelJob && existingJob && existingJobIndex != -1)
         {
-            bool markCanceledJobAsFinished = existingJob->GetState() == RCJob::JobState::pending;
             existingJob->SetState(RCJob::JobState::cancelled);
 
             // If the job was pending, mark it as finished, so asset processor can clean up the interface for this job and update tracking info.
-            if (markCanceledJobAsFinished)
+            if (markCancelledJobAsFinished)
             {
                 FinishJob(existingJob);
             }

+ 4 - 5
Code/Tools/AssetProcessor/native/resourcecompiler/rcjoblistmodel.cpp

@@ -292,14 +292,13 @@ namespace AssetProcessor
             }
         }
 
-        AZ_TracePrintf(
-            AssetProcessor::DebugChannel,
-            "JobTrace jobIndex == -1!!! (%i %s,%s,%s)\n",
-            rcJob,
+        AZ_Error(
+            AssetProcessor::ConsoleChannel,
+            false,
+            "Programmer Error: Could not mark job for file %s as completed, job was not tracked in the m_jobs container. It was either already finished, or never queued. (platform:%s, job key:%s)\n",
             rcJob->GetJobEntry().GetAbsoluteSourcePath().toUtf8().constData(),
             rcJob->GetPlatformInfo().m_identifier.c_str(),
             rcJob->GetJobKey().toUtf8().constData());
-        AZ_Assert(false, "Job not found!!!");
     }
 
     void RCJobListModel::markAsCataloged(const AssetProcessor::QueueElementID& check)

+ 20 - 5
Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/AmbientOcclusion.preset

@@ -8,7 +8,10 @@
             "Name": "AmbientOcclusion",
             "SourceColor": "Linear",
             "DestColor": "Linear",
-            "PixelFormat": "BC4"
+            "PixelFormat": "BC4",
+            "MipMapSetting": {
+                "MipGenType": "Box"
+            }
         },
         "PlatformsPresets": {
             "android": {
@@ -17,7 +20,10 @@
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "MaxTextureSize": 2048,
-                "PixelFormat": "ASTC_4x4"
+                "PixelFormat": "ASTC_4x4",
+                "MipMapSetting": {
+                  "MipGenType": "Box"
+                }
             },
             "ios": {
                 "UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}",
@@ -25,21 +31,30 @@
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "MaxTextureSize": 2048,
-                "PixelFormat": "ASTC_4x4"
+                "PixelFormat": "ASTC_4x4",
+                "MipMapSetting": {
+                    "MipGenType": "Box"
+                }
             },
             "mac": {
                 "UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}",
                 "Name": "AmbientOcclusion",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "PixelFormat": "BC4"
+                "PixelFormat": "BC4",
+                "MipMapSetting": {
+                    "MipGenType": "Box"
+                }
             },
             "provo": {
                 "UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}",
                 "Name": "AmbientOcclusion",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "PixelFormat": "BC4"
+                "PixelFormat": "BC4",
+                "MipMapSetting": {
+                  "MipGenType": "Box"
+                }
             }
         }
     }

+ 20 - 5
Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Emissive.preset

@@ -8,7 +8,10 @@
             "Name": "Emissive",
             "RGB_Weight": "CIEXYZ",
             "PixelFormat": "BC7",
-            "DiscardAlpha": true
+            "DiscardAlpha": true,
+            "MipMapSetting": {
+                "MipGenType": "Box"
+            }
         },
         "PlatformsPresets": {
             "android": {
@@ -17,7 +20,10 @@
                 "RGB_Weight": "CIEXYZ",
                 "PixelFormat": "ASTC_4x4",
                 "MaxTextureSize": 2048,
-                "DiscardAlpha": true
+                "DiscardAlpha": true,
+                "MipMapSetting": {
+                  "MipGenType": "Box"
+                }
             },
             "ios": {
                 "UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}",
@@ -25,21 +31,30 @@
                 "RGB_Weight": "CIEXYZ",
                 "PixelFormat": "ASTC_6x6",
                 "MaxTextureSize": 2048,
-                "DiscardAlpha": true
+                "DiscardAlpha": true,
+                "MipMapSetting": {
+                  "MipGenType": "Box"
+                }
             },
             "mac": {
                 "UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}",
                 "Name": "Emissive",
                 "RGB_Weight": "CIEXYZ",
                 "PixelFormat": "BC7",
-                "DiscardAlpha": true
+                "DiscardAlpha": true,
+                "MipMapSetting": {
+                  "MipGenType": "Box"
+                }
             },
             "provo": {
                 "UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}",
                 "Name": "Emissive",
                 "RGB_Weight": "CIEXYZ",
                 "PixelFormat": "BC7",
-                "DiscardAlpha": true
+                "DiscardAlpha": true,
+                "MipMapSetting": {
+                  "MipGenType": "Box"
+              }
             }
         }
     }

+ 3 - 0
Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Image/ImageSystemDescriptor.h

@@ -24,6 +24,9 @@ namespace AZ
             //! The maximum size of the image pool used for streaming images.
             //! Check ImageSystemInterface::GetSystemStreamingPool() for detail of this image pool
             uint64_t m_systemStreamingImagePoolSize = 0;
+            
+            //! The mipmap bias applied to streamable images created from the system streaming image pool
+            int16_t m_systemStreamingImagePoolMipBias = 0;
 
             //! The maximum size of the image pool used for system attachments images.
             //! Check ImageSystemInterface::GetSystemAttachmentPool() for detail of this image pool

+ 58 - 3
Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp

@@ -29,6 +29,7 @@
 #include <AzCore/Console/IConsole.h>
 #include <AzCore/Interface/Interface.h>
 #include <AzCore/Math/Color.h>
+#include <AzCore/Settings/SettingsRegistry.h>
 
 AZ_DECLARE_BUDGET(RPI);
 
@@ -37,6 +38,31 @@ namespace AZ
 
     namespace
     {
+        const char* MemoryBudgetSettingPath = "/O3DE/Atom/RPI/Initialization/ImageSystemDescriptor/SystemStreamingImagePoolSize";
+        const char* MipBiasSettingPath = "/O3DE/Atom/RPI/Initialization/ImageSystemDescriptor/SystemStreamingImagePoolMipBias";
+        
+        size_t cvar_r_streamingImagePoolBudgetMb_Init()
+        {
+            u64 value = 0;
+            auto settingsRegistry = AZ::SettingsRegistry::Get();
+            if (settingsRegistry)
+            {
+                settingsRegistry->Get(value, MemoryBudgetSettingPath);
+            }
+            return aznumeric_cast<size_t>(value);
+        }
+
+        int16_t cvar_r_streamingImageMipBias_Init()
+        {
+            s64 value = 0;
+            auto settingsRegistry = AZ::SettingsRegistry::Get();
+            if (settingsRegistry)
+            {
+                settingsRegistry->Get(value, MipBiasSettingPath);
+            }
+            return aznumeric_cast<int16_t>(value);
+        }
+
         void cvar_r_streamingImagePoolBudgetMb_Changed(const size_t& value)
         {
             if (auto* imageSystem = RPI::ImageSystemInterface::Get())
@@ -46,6 +72,14 @@ namespace AZ
                 [[maybe_unused]] bool success = pool->SetMemoryBudget(newBudget);
                 AZ_Warning("StreamingImagePool", success, "Can't update StreamingImagePool's memory budget to %uM", value);
             }
+            else
+            {
+                // Update setting registry value which is used for image system initialization
+                if (auto settingsRegistry = AZ::SettingsRegistry::Get())
+                {
+                    settingsRegistry->Set(MemoryBudgetSettingPath, aznumeric_cast<u64>(value));
+                }
+            }
         }
 
         void cvar_r_streamingImageMipBias_Changed(const int16_t& value)
@@ -55,12 +89,20 @@ namespace AZ
                 Data::Instance<RPI::StreamingImagePool> pool = imageSystem->GetSystemStreamingPool();
                 pool->SetMipBias(value);
             }
+            else
+            {
+                // Update setting registry value which is used for image system initialization
+                if (auto settingsRegistry = AZ::SettingsRegistry::Get())
+                {
+                    settingsRegistry->Set(MipBiasSettingPath, aznumeric_cast<s64>(value));
+                }
+            }
         }
     }
 
     // cvars for changing streaming image pool budget and setup mip bias of streaming controller
-    AZ_CVAR(size_t, r_streamingImagePoolBudgetMb, 0, cvar_r_streamingImagePoolBudgetMb_Changed, ConsoleFunctorFlags::Null, "Change gpu memory budget for the RPI system streaming image pool");
-    AZ_CVAR(int16_t, r_streamingImageMipBias, 0, cvar_r_streamingImageMipBias_Changed, ConsoleFunctorFlags::Null, "Set a mipmap bias for all streamable images created from the system streaming image pool");
+    AZ_CVAR(size_t, r_streamingImagePoolBudgetMb, cvar_r_streamingImagePoolBudgetMb_Init(), cvar_r_streamingImagePoolBudgetMb_Changed, ConsoleFunctorFlags::Null, "Change gpu memory budget for the RPI system streaming image pool");
+    AZ_CVAR(int16_t, r_streamingImageMipBias, cvar_r_streamingImageMipBias_Init(), cvar_r_streamingImageMipBias_Changed, ConsoleFunctorFlags::Null, "Set a mipmap bias for all streamable images created from the system streaming image pool");
 
     namespace RPI
     {
@@ -347,6 +389,19 @@ namespace AZ
                 Data::AssetId m_assetId;
             };
 
+            // Sync values from ImageSystemDescriptor back to the cvars
+            // Note 1: we need the sync here because one instance of the cvars might be initialized early than setting registry,
+            // so it can't be initialized properly. See cvar_r_streamingImagePoolBudgetMb_Init and cvar_r_streamingImageMipBias_Init
+            // Note 2: we need to use PerformCommand instead of assign value directly because of this issue https://github.com/o3de/o3de/issues/5537            
+            AZ::IConsole* console = AZ::Interface<AZ::IConsole>::Get();
+            if (console)
+            {
+                AZ::CVarFixedString commandString = AZ::CVarFixedString::format("r_streamingImagePoolBudgetMb %" PRIu64, desc.m_systemStreamingImagePoolSize);
+                console->PerformCommand(commandString.c_str());
+                commandString = AZ::CVarFixedString::format("r_streamingImageMipBias %" PRId16, desc.m_systemStreamingImagePoolMipBias);
+                console->PerformCommand(commandString.c_str());
+            }
+
             const SystemImagePoolDescriptor systemStreamingPoolDescriptor{ desc.m_systemStreamingImagePoolSize, "ImageSystem::SystemStreamingImagePool" };
             const SystemImagePoolDescriptor systemAttachmentPoolDescriptor{desc.m_systemAttachmentImagePoolSize, "ImageSystem::AttachmentImagePool" };
 
@@ -365,6 +420,7 @@ namespace AZ
                 AZ_Assert(created, "Failed to build streaming image pool");
 
                 m_systemStreamingPool = StreamingImagePool::FindOrCreate(poolAsset);
+                m_systemStreamingPool->SetMipBias(desc.m_systemStreamingImagePoolMipBias);
             }
 
             // Create the system attachment pool.
@@ -415,4 +471,3 @@ namespace AZ
         }
     } // namespace RPI
 }// namespace AZ
-

+ 1 - 0
Gems/Atom/RPI/Code/Source/RPI.Reflect/Image/ImageSystemDescriptor.cpp

@@ -22,6 +22,7 @@ namespace AZ
                 serializeContext->Class<ImageSystemDescriptor>()
                     ->Version(0)
                     ->Field("SystemStreamingImagePoolSize", &ImageSystemDescriptor::m_systemStreamingImagePoolSize)
+                    ->Field("SystemStreamingImagePoolMipBias", &ImageSystemDescriptor::m_systemStreamingImagePoolMipBias)
                     ->Field("SystemAttachmentImagePoolSize", &ImageSystemDescriptor::m_systemAttachmentImagePoolSize)
                     ;
             }

+ 4 - 2
Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp

@@ -231,8 +231,10 @@ namespace AZ
 
         bool MaterialTypeAsset::PostLoadInit()
         {
-            [[maybe_unused]] bool success = InitializeNonSerializedData();
-            AZ_Assert(success, "Failed to InitializeNonSerializedData");
+            // Attempt to initialize non-serialized data. The referenced shader assets in the ShaderCollection
+            // may not be ready right now, but in the future the system will retry when said assets
+            // are ready.
+            InitializeNonSerializedData();
 
             for (const auto& shaderItem : m_generalShaderCollection)
             {

+ 2 - 1
Gems/Atom/RPI/Registry/atom_rpi.setreg

@@ -6,7 +6,8 @@
                     "CommonSrgsShaderAssetPath": "shaders/sceneandviewsrgs.azshader",
                     "ImageSystemDescriptor": {
                         "SystemStreamingImagePoolSize": 0,
-                        "SystemAttachmentImagePoolSize": 0 
+                        "SystemStreamingImagePoolMipBias": 0,
+                        "SystemAttachmentImagePoolSize": 0
                     },
                     "GpuQuerySystemDescriptor": {
                         "OcclusionQueryCount": 128,

+ 12 - 18
Gems/Atom/Tools/AtomToolsFramework/Code/Source/EntityPreviewViewport/EntityPreviewViewportScene.cpp

@@ -108,25 +108,26 @@ namespace AtomToolsFramework
         {
             return m_renderPipelines.emplace(pipelineAssetId, renderPipeline).first;
         }
-        else
-        {
-            return m_renderPipelines.end();
-        }
+
+        return m_renderPipelines.end();
     }
 
     bool EntityPreviewViewportScene::ActivateRenderPipeline(const AZ::Data::AssetId& pipelineAssetId)
     {
-        auto iter = m_renderPipelines.find(pipelineAssetId);
-
-        if (iter == m_renderPipelines.end())
+        if (!pipelineAssetId.IsValid())
         {
-            iter = AddRenderPipeline(pipelineAssetId);
+            return false;
         }
 
+        auto iter = m_renderPipelines.find(pipelineAssetId);
         if (iter == m_renderPipelines.end())
         {
-            // The pipeline was not found and could not be loaded
-            return false;
+            iter = AddRenderPipeline(pipelineAssetId);
+            if (iter == m_renderPipelines.end())
+            {
+                // The pipeline was not found and could not be loaded
+                return false;
+            }
         }
 
         if (iter->first != m_activeRenderPipelineId)
@@ -155,14 +156,7 @@ namespace AtomToolsFramework
     {
         using namespace AZ::RPI;
         AZ::Data::AssetId assetId = AssetUtils::GetAssetIdForProductPath(pipelineAssetPath.c_str(), AssetUtils::TraceLevel::Error);
-        if (assetId.IsValid())
-        {
-            return ActivateRenderPipeline(assetId);
-        }
-        else
-        {
-            return false;
-        }
+        return ActivateRenderPipeline(assetId);
     }
 
     AZ::RPI::ScenePtr EntityPreviewViewportScene::GetScene() const

+ 10 - 2
Gems/Atom/Tools/AtomToolsFramework/Code/Source/Graph/GraphCompiler.cpp

@@ -65,21 +65,29 @@ namespace AtomToolsFramework
         {
         case State::Idle:
             ReportStatus(AZStd::string::format("%s (Idle)", GetGraphPath().c_str()));
+            m_graph.reset();
+            m_graphName.clear();
+            m_graphPath.clear();
+            m_generatedFiles.clear();
             break;
         case State::Compiling:
-            m_generatedFiles.clear();
             ReportStatus(AZStd::string::format("%s (Compiling)", GetGraphPath().c_str()));
+            m_generatedFiles.clear();
             break;
         case State::Processing:
+            ReportStatus(AZStd::string::format("%s (Processing)", GetGraphPath().c_str()));
             break;
         case State::Complete:
             ReportStatus(AZStd::string::format("%s (Complete)", GetGraphPath().c_str()));
+            m_graph.reset();
             break;
         case State::Failed:
             ReportStatus(AZStd::string::format("%s (Failed)", GetGraphPath().c_str()));
+            m_graph.reset();
             break;
         case State::Canceled:
             ReportStatus(AZStd::string::format("%s (Cancelled)", GetGraphPath().c_str()));
+            m_graph.reset();
             break;
         }
 
@@ -170,7 +178,7 @@ namespace AtomToolsFramework
                     }
 
                     ReportStatus(AZStd::string::format(
-                        "Processing %s (%s)", generatedFile.c_str(), AzToolsFramework::AssetSystem::JobStatusString(job.m_status)));
+                        "%s (Processing: %s)", generatedFile.c_str(), AzToolsFramework::AssetSystem::JobStatusString(job.m_status)));
 
                     switch (job.m_status)
                     {

+ 2 - 0
Gems/Atom/Tools/AtomToolsFramework/Code/Source/Graph/GraphDocument.cpp

@@ -383,6 +383,8 @@ namespace AtomToolsFramework
             });
 
             graphCompiler->CompileGraph(graph, graphName, graphPath);
+            graphCompiler->SetStateChangeHandler({});
+            graph.reset();
         };
 
         auto job = AZ::CreateJobFunction(compileJobFn, true);

+ 2 - 2
Gems/CameraFramework/Code/Source/CameraRigComponent.cpp

@@ -149,11 +149,11 @@ namespace Camera
     void CameraRigComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
     {
         required.push_back(AZ_CRC("TransformService", 0x8ee22c50));
+        required.push_back(AZ_CRC("CameraService", 0x1dd1caa4));
     }
 
-    void CameraRigComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
+    void CameraRigComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
     {
-        dependent.push_back(AZ_CRC("CameraService", 0x1dd1caa4));
     }
 
     void CameraRigComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)

+ 9 - 6
Gems/GraphCanvas/Code/Source/Components/NodePropertyDisplays/StringNodePropertyDisplay.cpp

@@ -84,11 +84,6 @@ namespace GraphCanvas
         }
 
         ResizeToContents();
-        
-        if (m_proxyWidget)
-        {
-            m_proxyWidget->update();
-        }
     }
 
     QGraphicsLayoutItem* StringNodePropertyDisplay::GetDisabledGraphicsLayoutItem()
@@ -180,7 +175,10 @@ namespace GraphCanvas
             QObject::connect(m_lineEdit, &QLineEdit::textChanged, [this]() { ResizeToContents(); });
             QObject::connect(m_lineEdit, &Internal::FocusableLineEdit::OnFocusIn, [this]() { EditStart(); });
             QObject::connect(m_lineEdit, &Internal::FocusableLineEdit::OnFocusOut, [this]() { EditFinished(); });
-            QObject::connect(m_lineEdit, &QLineEdit::editingFinished, [this]() { SubmitValue(); });
+            QObject::connect(m_lineEdit, &QLineEdit::editingFinished, [this]() {
+                SubmitValue();
+                UpdateDisplay();
+            });
 
             m_proxyWidget->setWidget(m_lineEdit);
             UpdateDisplay();
@@ -232,6 +230,11 @@ namespace GraphCanvas
                 }
             }
         }
+        
+        if (m_proxyWidget)
+        {
+            m_proxyWidget->update();
+        }
     }
 
 #include <Source/Components/NodePropertyDisplays/moc_StringNodePropertyDisplay.cpp>

+ 66 - 66
Gems/GraphCanvas/Code/Source/Components/NodePropertyDisplays/VectorNodePropertyDisplay.cpp

@@ -56,48 +56,45 @@ namespace GraphCanvas
     //////////////////////////
     // ReadOnlyVectorControl
     //////////////////////////
-    
+
     ReadOnlyVectorControl::ReadOnlyVectorControl(int index, const VectorDataInterface& dataInterface)
         : m_index(index)
         , m_dataInterface(dataInterface)
     {
-        m_layout = new QGraphicsLinearLayout(Qt::Orientation::Horizontal);
-        m_layout->setSpacing(0);
-        m_layout->setContentsMargins(0, 0, 0, 0);
-        
         m_textLabel = aznew GraphCanvasLabel();
         m_textLabel->SetRoundedCornersMode(GraphCanvasLabel::RoundedCornersMode::LeftCorners);
+        m_textLabel->SetLabel(dataInterface.GetLabel(index));
+
         m_valueLabel = aznew GraphCanvasLabel();
         m_valueLabel->SetRoundedCornersMode(GraphCanvasLabel::RoundedCornersMode::RightCorners);
-        
+
+        m_layout = new QGraphicsLinearLayout(Qt::Orientation::Horizontal);
+        m_layout->setSpacing(0);
+        m_layout->setContentsMargins(0, 0, 0, 0);
         m_layout->addItem(m_textLabel);
         m_layout->addItem(m_valueLabel);
-
-        m_textLabel->SetLabel(dataInterface.GetLabel(index));
-        
-        setContentsMargins(0, 0, 0, 0);
-    
         setLayout(m_layout);
+        setContentsMargins(0, 0, 0, 0);
     }
-    
+
     ReadOnlyVectorControl::~ReadOnlyVectorControl()
     {
     }
-    
+
     void ReadOnlyVectorControl::RefreshStyle(const AZ::EntityId& sceneId)
     {
-        AZStd::string styleName = m_dataInterface.GetElementStyle(m_index);
+        const AZStd::string styleName = m_dataInterface.GetElementStyle(m_index);
         m_textLabel->SetSceneStyle(sceneId, NodePropertyDisplay::CreateDisplayLabelStyle(styleName + "_text").c_str());
         m_valueLabel->SetSceneStyle(sceneId, NodePropertyDisplay::CreateDisplayLabelStyle(styleName + "_value").c_str());
     }
-    
+
     void ReadOnlyVectorControl::UpdateDisplay()
     {
-        double value = m_dataInterface.GetValue(m_index);
-
-        AZStd::string displayValue = AZStd::string::format("%.*g%s", m_dataInterface.GetDisplayDecimalPlaces(m_index), value, m_dataInterface.GetSuffix(m_index));
-
-        m_valueLabel->SetLabel(displayValue);
+        m_valueLabel->SetLabel(AZStd::string::format(
+            "%.*g%s",
+            m_dataInterface.GetDisplayDecimalPlaces(m_index),
+            m_dataInterface.GetValue(m_index),
+            m_dataInterface.GetSuffix(m_index)));
     }
 
     int ReadOnlyVectorControl::GetIndex() const
@@ -129,11 +126,14 @@ namespace GraphCanvas
         : QGraphicsWidget(parent)
         , m_pixmap(new QGraphicsPixmapItem(this))
     {
+        m_pixmap->setVisible(false);
         setGraphicsItem(m_pixmap);
+        setContentsMargins(0, 0, 0, 0);
     }
 
     void IconLayoutItem::setIcon(const QPixmap& pixmap)
     {
+        m_pixmap->setVisible(!pixmap.isNull());
         m_pixmap->setPixmap(pixmap);
     }
 
@@ -162,23 +162,23 @@ namespace GraphCanvas
         displayLayout->setSpacing(5);
         displayLayout->setContentsMargins(0, 0, 0, 0);
         displayLayout->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-        
+
         m_iconDisplay = new IconLayoutItem();
         m_iconDisplay->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
 
         displayLayout->addItem(m_iconDisplay);
         displayLayout->setAlignment(m_iconDisplay, Qt::AlignBottom);
- 
-        int elementCount = dataInterface->GetElementCount();
+
+        const int elementCount = dataInterface->GetElementCount();
         m_vectorDisplays.reserve(elementCount);
-        for (int i=0; i < elementCount; ++i)
+        for (int i = 0; i < elementCount; ++i)
         {
             m_vectorDisplays.push_back(aznew ReadOnlyVectorControl(i, (*dataInterface)));
             displayLayout->addItem(m_vectorDisplays.back());
         }
 
         m_displayWidget->setLayout(displayLayout);
-        
+
         m_disabledLabel = aznew GraphCanvasLabel();
     }
 
@@ -194,23 +194,21 @@ namespace GraphCanvas
     
     void VectorNodePropertyDisplay::RefreshStyle()
     {
-        AZ::EntityId sceneId = GetSceneId();
+        const AZ::EntityId sceneId = GetSceneId();
 
-        AZStd::string elementStyle = m_dataInterface->GetStyle();
+        const AZStd::string elementStyle = m_dataInterface->GetStyle();
 
         m_styleHelper.SetScene(sceneId);
         m_styleHelper.SetStyle(NodePropertyDisplay::CreateDisplayLabelStyle(elementStyle).c_str());
         m_disabledLabel->SetSceneStyle(GetSceneId(), NodePropertyDisplay::CreateDisabledLabelStyle(elementStyle).c_str());
 
-        QColor backgroundColor = m_styleHelper.GetAttribute(GraphCanvas::Styling::Attribute::BackgroundColor, QColor(0, 0, 0, 0));
-
-        m_displayWidget->setAutoFillBackground(true);
-
         QPalette palette = m_displayWidget->palette();
+        const QColor backgroundColor = m_styleHelper.GetAttribute(GraphCanvas::Styling::Attribute::BackgroundColor, QColor(0, 0, 0, 0));
         palette.setColor(QPalette::ColorRole::Window, backgroundColor);
         m_displayWidget->setPalette(palette);
+        m_displayWidget->setAutoFillBackground(true);
 
-        qreal spacing = static_cast<QGraphicsLinearLayout*>(m_displayWidget->layout())->spacing();
+        const qreal spacing = static_cast<QGraphicsLinearLayout*>(m_displayWidget->layout())->spacing();
 
         // Start off with - spacing to make the iteration logic cleaner.
         qreal elementWidth = -spacing;
@@ -222,7 +220,7 @@ namespace GraphCanvas
         {
             control->RefreshStyle(sceneId);
 
-            QSizeF maximumSize = control->maximumSize();
+            const QSizeF maximumSize = control->maximumSize();
 
             // Maximum size might be stupidly large, which will cause an error
             // from Qt as it tries to overly allocate space for something.
@@ -230,21 +228,28 @@ namespace GraphCanvas
             // As such we want to put an upper limit on this that is large but not unreasonable.
             // Can't really do this at the element level since it messes with the styling
             // when I set it. So instead we'll do it here.
-            elementWidth += AZ::GetMin(maximumSize.width(), k_sizingConstraint) + spacing;
-            elementHeight = AZStd::GetMax(elementHeight, AZ::GetMin(k_sizingConstraint, maximumSize.height()));
+            elementWidth += AZ::GetMin(k_sizingConstraint, maximumSize.width()) + spacing;
+            elementHeight = AZ::GetMax(elementHeight, AZ::GetMin(k_sizingConstraint, maximumSize.height()));
+        }
+
+        if (m_iconDisplay && m_iconDisplay->isVisible())
+        {
+            elementWidth += m_iconDisplay->preferredWidth();
         }
-        elementWidth += m_iconDisplay->preferredWidth();
 
         m_displayWidget->setMinimumSize(elementWidth, elementHeight);
         m_displayWidget->setPreferredSize(elementWidth, elementHeight);
         m_displayWidget->setMaximumSize(elementWidth, elementHeight);
         m_displayWidget->adjustSize();
 
-        if (m_propertyVectorCtrl)
+        if (m_widgetContainer)
         {
-            m_propertyVectorCtrl->setMinimumSize(aznumeric_cast<int>(elementWidth), aznumeric_cast<int>(elementHeight));
-            m_propertyVectorCtrl->setMaximumSize(aznumeric_cast<int>(elementWidth), aznumeric_cast<int>(elementHeight));
-            m_propertyVectorCtrl->adjustSize();
+            const QSizeF minimumSize = m_displayWidget->minimumSize();
+            const QSizeF maximumSize = m_displayWidget->maximumSize();
+
+            m_widgetContainer->setMinimumSize(aznumeric_cast<int>(minimumSize.width()), aznumeric_cast<int>(minimumSize.height()));
+            m_widgetContainer->setMaximumSize(aznumeric_cast<int>(maximumSize.width()), aznumeric_cast<int>(maximumSize.height()));
+            m_widgetContainer->adjustSize();
         }
     }
 
@@ -258,23 +263,17 @@ namespace GraphCanvas
         const auto buttonIcon = m_dataInterface->GetIcon();
         if (m_iconDisplay)
         {
-            if (!buttonIcon.isNull())
-            {
-                m_iconDisplay->setPreferredSize(buttonIcon.size());
-                m_iconDisplay->setIcon(buttonIcon);
-            }
+            m_iconDisplay->setIcon(buttonIcon);
+            m_iconDisplay->setPreferredSize(buttonIcon.size());
             m_iconDisplay->setVisible(!buttonIcon.isNull());
         }
 
         if (m_button)
         {
-            if (!buttonIcon.isNull())
-            {
-                QIcon newIcon(buttonIcon);
-                m_button->setFixedSize(buttonIcon.size());
-                m_button->setIconSize(buttonIcon.size());
-                m_button->setIcon(newIcon);
-            }
+            const QIcon newIcon(buttonIcon);
+            m_button->setIcon(newIcon);
+            m_button->setFixedSize(buttonIcon.size());
+            m_button->setIconSize(buttonIcon.size());
             m_button->setVisible(!buttonIcon.isNull());
         }
 
@@ -353,13 +352,15 @@ namespace GraphCanvas
     {
         if (!m_propertyVectorCtrl)
         {
-            m_proxyWidget = new QGraphicsProxyWidget();
-
             m_widgetContainer = new QWidget();
-            QHBoxLayout* layout = new QHBoxLayout(m_widgetContainer);
-            m_widgetContainer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+            m_widgetContainer->setContentsMargins(0, 0, 0, 0);
+            m_widgetContainer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+            m_widgetContainer->setProperty("HasNoWindowDecorations", true);
 
+            QHBoxLayout* layout = new QHBoxLayout(m_widgetContainer);
             layout->setAlignment(Qt::AlignLeft);
+            layout->setMargin(0);
+            layout->setSpacing(0);
             layout->setContentsMargins(0, 0, 0, 0);
 
             m_button = new QToolButton(m_widgetContainer);
@@ -373,12 +374,10 @@ namespace GraphCanvas
             });
             layout->addWidget(m_button);
 
-            m_proxyWidget->setFlag(QGraphicsItem::ItemIsFocusable, true);
-            m_proxyWidget->setFocusPolicy(Qt::StrongFocus);
-            m_proxyWidget->setAcceptDrops(false);
-
             const int elementCount = m_dataInterface->GetElementCount();
             m_propertyVectorCtrl = new AzQtComponents::VectorInput(m_widgetContainer, elementCount);
+            m_propertyVectorCtrl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+            QObject::connect(m_propertyVectorCtrl, &AzQtComponents::VectorInput::editingFinished, [this]() { SubmitValue(); });
 
             for (int i = 0; i < elementCount; ++i)
             {
@@ -390,11 +389,12 @@ namespace GraphCanvas
                 m_propertyVectorCtrl->setSuffix(m_dataInterface->GetSuffix(i));
             }
 
-            m_propertyVectorCtrl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-            QObject::connect(m_propertyVectorCtrl, &AzQtComponents::VectorInput::editingFinished, [this]() { SubmitValue(); });
-
             layout->addWidget(m_propertyVectorCtrl);
-            m_widgetContainer->setProperty("HasNoWindowDecorations", true);
+
+            m_proxyWidget = new QGraphicsProxyWidget();
+            m_proxyWidget->setFlag(QGraphicsItem::ItemIsFocusable, true);
+            m_proxyWidget->setFocusPolicy(Qt::StrongFocus);
+            m_proxyWidget->setAcceptDrops(false);
             m_proxyWidget->setWidget(m_widgetContainer);
             
             UpdateDisplay();
@@ -411,10 +411,10 @@ namespace GraphCanvas
             delete m_widgetContainer; // NB: this implicitly deletes m_proxy widget
             m_widgetContainer = nullptr;
             m_propertyVectorCtrl = nullptr;
-            m_button= nullptr;
+            m_button = nullptr;
             m_proxyWidget = nullptr;
         }
     }
 
 #include <Source/Components/NodePropertyDisplays/moc_VectorNodePropertyDisplay.cpp>
-}
+} // namespace GraphCanvas

+ 1 - 0
Gems/GraphModel/Code/Include/GraphModel/Integration/GraphController.h

@@ -148,6 +148,7 @@ namespace GraphModelIntegration
         // GraphCanvas::SceneNotificationBus, connections
         void OnNodeAdded(const AZ::EntityId& nodeUiId, bool isPaste) override;
         void OnNodeRemoved(const AZ::EntityId& nodeUiId) override;
+        void OnConnectionAdded(const AZ::EntityId& connectionUiId) override;
         void OnConnectionRemoved(const AZ::EntityId& connectionUiId) override;
         void OnEntitiesSerialized(GraphCanvas::GraphSerialization& serializationTarget) override;
         void OnEntitiesDeserialized(const GraphCanvas::GraphSerialization& serializationSource) override;

+ 18 - 7
Gems/GraphModel/Code/Source/Integration/GraphController.cpp

@@ -683,7 +683,6 @@ namespace GraphModelIntegration
         {
             CreateConnectionUi(newConnection);
         }
-
         return newConnection;
     }
 
@@ -932,15 +931,25 @@ namespace GraphModelIntegration
         }
     }
 
-    void GraphController::OnConnectionRemoved(const AZ::EntityId& connectionUiId)
+    void GraphController::OnConnectionAdded(const AZ::EntityId& connectionUiId)
     {
         if (const GraphModel::ConnectionPtr connection = m_elementMap.Find<GraphModel::Connection>(connectionUiId))
         {
-            m_graph->RemoveConnection(connection);
-            m_elementMap.Remove(connection);
+            GraphCanvas::NodeUIRequestBus::Event(m_elementMap.Find(connection->GetSourceNode()), &GraphCanvas::NodeUIRequests::AdjustSize);
+            GraphCanvas::NodeUIRequestBus::Event(m_elementMap.Find(connection->GetTargetNode()), &GraphCanvas::NodeUIRequests::AdjustSize);
+        }
+    }
 
+    void GraphController::OnConnectionRemoved(const AZ::EntityId& connectionUiId)
+    {
+        if (const GraphModel::ConnectionPtr connection = m_elementMap.Find<GraphModel::Connection>(connectionUiId))
+        {
             GraphControllerNotificationBus::Event(
                 m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelConnectionRemoved, connection);
+            GraphCanvas::NodeUIRequestBus::Event(m_elementMap.Find(connection->GetSourceNode()), &GraphCanvas::NodeUIRequests::AdjustSize);
+            GraphCanvas::NodeUIRequestBus::Event(m_elementMap.Find(connection->GetTargetNode()), &GraphCanvas::NodeUIRequests::AdjustSize);
+            m_graph->RemoveConnection(connection);
+            m_elementMap.Remove(connection);
         }
     }
 
@@ -1166,10 +1175,12 @@ namespace GraphModelIntegration
             // No need to clean up the maps here because the OnConnectionRemoved() callback will handle that
         }
 
-        GraphModel::ConnectionPtr newConnection = m_graph->AddConnection(sourceSlot, targetSlot);
+        GraphModel::ConnectionPtr connection = m_graph->AddConnection(sourceSlot, targetSlot);
         GraphControllerNotificationBus::Event(
-            m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelConnectionAdded, newConnection);
-        return newConnection;
+            m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelConnectionAdded, connection);
+        GraphCanvas::NodeUIRequestBus::Event(m_elementMap.Find(connection->GetSourceNode()), &GraphCanvas::NodeUIRequests::AdjustSize);
+        GraphCanvas::NodeUIRequestBus::Event(m_elementMap.Find(connection->GetTargetNode()), &GraphCanvas::NodeUIRequests::AdjustSize);
+        return connection;
     }
 
     bool GraphController::CreateConnection(

+ 1 - 1
Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerEditorServerBus.h

@@ -53,7 +53,7 @@ namespace Multiplayer
         virtual void OnEditorConnectionAttemptsFailed([[maybe_unused]]uint16_t failedAttempts) {}
 
         //! Notification when the Editor starts sending the current level data (spawnable) to the server.
-        virtual void OnEditorSendingLevelData() {}
+        virtual void OnEditorSendingLevelData([[maybe_unused]] uint32_t bytesSent, [[maybe_unused]] uint32_t bytesTotal) {}
 
         //! Notification when the Editor has sent all the level data successful and is now fully connected to the multiplayer simulation.
         virtual void OnConnectToSimulationSuccess() {}

+ 3 - 2
Gems/Multiplayer/Code/Source/Debug/MultiplayerConnectionViewportMessageSystemComponent.cpp

@@ -375,10 +375,11 @@ namespace Multiplayer
         m_centerViewportDebugText = OnServerLaunchFailMessage;
     }
 
-    void MultiplayerConnectionViewportMessageSystemComponent::OnEditorSendingLevelData()
+    void MultiplayerConnectionViewportMessageSystemComponent::OnEditorSendingLevelData(uint32_t bytesSent, uint32_t bytesTotal)
     {
         m_centerViewportDebugTextColor = AZ::Colors::Yellow;
-        m_centerViewportDebugText = OnEditorSendingLevelDataMessage;
+        m_centerViewportDebugText =
+            AZStd::fixed_string<MaxMessageLength>::format(OnEditorSendingLevelDataMessage, bytesSent, bytesTotal);
     }   
 
     void MultiplayerConnectionViewportMessageSystemComponent::OnEditorConnectionAttempt(uint16_t connectionAttempts, uint16_t maxAttempts)

+ 2 - 2
Gems/Multiplayer/Code/Source/Debug/MultiplayerConnectionViewportMessageSystemComponent.h

@@ -37,7 +37,7 @@ namespace Multiplayer
         static constexpr char OnServerLaunchFailMessage[] = "(1/3) Could not launch editor server.\nSee console for more info.";
         static constexpr char OnEditorConnectionAttemptMessage[] = "(2/3) Attempting to connect to server in order to send level data.\nAttempt %i of %i";
         static constexpr char OnEditorConnectionAttemptsFailedMessage[] = "(2/3) Failed to connect to server after %i attempts!\nPlease exit play mode and try again.";  
-        static constexpr char OnEditorSendingLevelDataMessage[] = "(3/3) Editor is sending the editor-server the level data packet.";
+        static constexpr char OnEditorSendingLevelDataMessage[] = "(3/3) Editor is sending the editor-server the level data packet.\n Bytes %u / %u sent.";
         static constexpr char OnConnectToSimulationFailMessage[] = "EditorServerReady packet was received, but connecting to the editor-server's network simulation failed! Is the editor and server using the same sv_port (%i)?";
         static constexpr char OnEditorServerStoppedUnexpectedly[] ="Editor server has unexpectedly stopped running!";
 
@@ -87,7 +87,7 @@ namespace Multiplayer
         void OnServerLaunchFail() override;
         void OnEditorConnectionAttempt(uint16_t connectionAttempts, uint16_t maxAttempts) override;
         void OnEditorConnectionAttemptsFailed(uint16_t failedAttempts) override;
-        void OnEditorSendingLevelData() override;
+        void OnEditorSendingLevelData(uint32_t bytesSent, uint32_t bytesTotal) override;
         void OnConnectToSimulationSuccess() override;
         void OnConnectToSimulationFail(uint16_t serverPort) override;
         void OnPlayModeEnd() override;

+ 2 - 2
Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorAutomation.cpp

@@ -47,9 +47,9 @@ namespace Multiplayer::Automation
         Call(FN_OnEditorConnectionAttemptsFailed, failedAttempts);
     }
 
-    void MultiplayerEditorAutomationHandler::OnEditorSendingLevelData()
+    void MultiplayerEditorAutomationHandler::OnEditorSendingLevelData(uint32_t bytesSent, uint32_t bytesTotal)
     {
-        Call(FN_OnEditorSendingLevelData);
+        Call(FN_OnEditorSendingLevelData, bytesSent, bytesTotal);
     }
 
     void MultiplayerEditorAutomationHandler::OnConnectToSimulationSuccess()

+ 1 - 1
Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorAutomation.h

@@ -43,7 +43,7 @@ namespace Multiplayer::Automation
         void OnServerLaunchFail() override;
         void OnEditorConnectionAttempt(uint16_t connectionAttempts, uint16_t maxAttempts) override;
         void OnEditorConnectionAttemptsFailed(uint16_t failedAttempts) override;
-        void OnEditorSendingLevelData() override;
+        void OnEditorSendingLevelData(uint32_t bytesSent, uint32_t bytesTotal) override;
         void OnConnectToSimulationSuccess() override;
         void OnConnectToSimulationFail(uint16_t serverPort) override;
         void OnPlayModeEnd() override;

+ 18 - 2
Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp

@@ -376,10 +376,8 @@ namespace Multiplayer
             return;
         }
         
-        MultiplayerEditorServerNotificationBus::Broadcast(&MultiplayerEditorServerNotificationBus::Events::OnEditorSendingLevelData);
         AZ_TracePrintf("MultiplayerEditor", "Editor is sending the editor-server the level data packet.")
 
-
         AZStd::vector<uint8_t> buffer;
         AZ::IO::ByteContainerStream byteStream(&buffer);
 
@@ -401,6 +399,11 @@ namespace Multiplayer
         // Read the buffer into EditorServerLevelData packets until we've flushed the whole thing
         byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN);
 
+        // Send an initial notification showing how much data will be sent.
+        MultiplayerEditorServerNotificationBus::Broadcast(
+            &MultiplayerEditorServerNotificationBus::Events::OnEditorSendingLevelData,
+            0, aznumeric_cast<uint32_t>(byteStream.GetLength()));
+
         while (byteStream.GetCurPos() < byteStream.GetLength())
         {
             MultiplayerEditorPackets::EditorServerLevelData editorServerLevelDataPacket;
@@ -441,9 +444,22 @@ namespace Multiplayer
 
                     // Force the networking buffers to try and flush before sending the packet again.
                     AZ::Interface<AzNetworking::INetworking>::Get()->ForceUpdate();
+
+                    // Also, process the trace printing communication from server->client. Without this, if the piped trace
+                    // buffer is completely full, the server process will be completely halted waiting to send more trace
+                    // information. All retries here would fail indefinitely waiting for the server process to become available
+                    // again. With the Pump(), the trace pipes can be kept flowing, and retries can be processed by the server
+                    // in a timely manner.
+                    m_serverProcessTracePrinter->Pump();
                 }
             }
             AZ_Assert(packetSent, "Failed to send level packet after %d tries. Server will fail to run the level correctly.", numRetries);
+
+            // Update our information to track the current amount of data sent.
+            MultiplayerEditorServerNotificationBus::Broadcast(
+                &MultiplayerEditorServerNotificationBus::Events::OnEditorSendingLevelData,
+                aznumeric_cast<uint32_t>(byteStream.GetCurPos()),
+                aznumeric_cast<uint32_t>(byteStream.GetLength()));
         }
     }
 

+ 1 - 0
Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp

@@ -50,6 +50,7 @@ namespace Multiplayer
         AZ_Assert(!IsTimeRewound(), "Incrementing the global application frameId is unsupported under a rewound time scope");
         ++m_unalteredFrameId;
         m_hostFrameId = m_unalteredFrameId;
+        m_hostTimeMs = AZ::GetElapsedTimeMs();
     }
 
     AZ::TimeMs NetworkTime::GetHostTimeMs() const

+ 2 - 0
Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.cpp

@@ -711,9 +711,11 @@ namespace ScriptCanvasEditor
         // File menu
         connect(ui->action_New_Script, &QAction::triggered, this, &MainWindow::OnFileNew);
         ui->action_New_Script->setShortcut(QKeySequence(QKeySequence::New));
+        addAction(ui->action_New_Script);
 
         connect(ui->action_Open, &QAction::triggered, this, &MainWindow::OnFileOpen);
         ui->action_Open->setShortcut(QKeySequence(QKeySequence::Open));
+        addAction(ui->action_Open);
 
         connect(ui->action_UpgradeTool, &QAction::triggered, this, &MainWindow::RunUpgradeTool);
         ui->action_UpgradeTool->setVisible(true);