diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg new file mode 100644 index 0000000000..f1d36d3e41 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg new file mode 100644 index 0000000000..9fc9fe52c2 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg new file mode 100644 index 0000000000..7a61db38e0 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg new file mode 100644 index 0000000000..c6fb977c52 --- /dev/null +++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Code/Editor/Core/QtEditorApplication_linux.cpp b/Code/Editor/Core/QtEditorApplication_linux.cpp index 175bef0238..2fd4ef629e 100644 --- a/Code/Editor/Core/QtEditorApplication_linux.cpp +++ b/Code/Editor/Core/QtEditorApplication_linux.cpp @@ -9,7 +9,7 @@ #include "QtEditorApplication.h" #ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB -#include +#include #endif namespace Editor @@ -19,7 +19,7 @@ namespace Editor if (GetIEditor()->IsInGameMode()) { #ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - AzFramework::LinuxXcbEventHandlerBus::Broadcast(&AzFramework::LinuxXcbEventHandler::HandleXcbEvent, static_cast(message)); + AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::HandleXcbEvent, static_cast(message)); #endif return true; } diff --git a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp index cc6154f20c..496d89d767 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp @@ -28,34 +28,10 @@ namespace AzFramework } } - //////////////////////////////////////////////////////////////////////////////////////////////// - InputChannelId::InputChannelId(const char* name) - : m_crc32(name) - { - memset(m_name, 0, AZ_ARRAY_SIZE(m_name)); - azstrncpy(m_name, NAME_BUFFER_SIZE, name, MAX_NAME_LENGTH); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - InputChannelId::InputChannelId(const InputChannelId& other) - : m_crc32(other.m_crc32) - { - memset(m_name, 0, AZ_ARRAY_SIZE(m_name)); - azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - InputChannelId& InputChannelId::operator=(const InputChannelId& other) - { - azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name); - m_crc32 = other.m_crc32; - return *this; - } - //////////////////////////////////////////////////////////////////////////////////////////////// const char* InputChannelId::GetName() const { - return m_name; + return m_name.c_str(); } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h index 5c1c58d6b1..a4d8ac83eb 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h @@ -11,6 +11,7 @@ #include #include #include +#include //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework @@ -22,8 +23,7 @@ namespace AzFramework public: //////////////////////////////////////////////////////////////////////////////////////////// // Constants - static const int NAME_BUFFER_SIZE = 64; - static const int MAX_NAME_LENGTH = NAME_BUFFER_SIZE - 1; + static constexpr int MAX_NAME_LENGTH = 64; //////////////////////////////////////////////////////////////////////////////////////////// // Allocator @@ -39,21 +39,28 @@ namespace AzFramework //////////////////////////////////////////////////////////////////////////////////////////// //! Constructor - //! \param[in] name Name of the input channel (will be truncated if exceeds MAX_NAME_LENGTH) - explicit InputChannelId(const char* name = ""); + //! \param[in] name Name of the input channel (will be ignored if exceeds MAX_NAME_LENGTH) + explicit constexpr InputChannelId(AZStd::string_view name = "") + : m_name(name) + , m_crc32(name) + { + } - //////////////////////////////////////////////////////////////////////////////////////////// - //! Copy constructor - //! \param[in] other Another instance of the class to copy from - InputChannelId(const InputChannelId& other); - - //////////////////////////////////////////////////////////////////////////////////////////// - //! Copy assignment operator - //! \param[in] other Another instance of the class to copy from - InputChannelId& operator=(const InputChannelId& other); - - //////////////////////////////////////////////////////////////////////////////////////////// - //! Default destructor + constexpr InputChannelId(const InputChannelId& other) = default; + constexpr InputChannelId(InputChannelId&& other) = default; + constexpr InputChannelId& operator=(const InputChannelId& other) + { + m_name = other.m_name; + m_crc32 = other.m_crc32; + return *this; + } + constexpr InputChannelId& operator=(InputChannelId&& other) + { + m_name = AZStd::move(other.m_name); + m_crc32 = AZStd::move(other.m_crc32); + other.m_crc32 = 0; + return *this; + } ~InputChannelId() = default; //////////////////////////////////////////////////////////////////////////////////////////// @@ -77,7 +84,7 @@ namespace AzFramework private: //////////////////////////////////////////////////////////////////////////////////////////// // Variables - char m_name[NAME_BUFFER_SIZE]; //!< Name of the input channel + AZStd::fixed_string m_name; //!< Name of the input channel AZ::Crc32 m_crc32; //!< Crc32 of the input channel }; } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp index 95886c6871..51aa008519 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp @@ -28,91 +28,6 @@ namespace AzFramework return (inputDeviceId.GetNameCrc32() == IdForIndex0.GetNameCrc32()); } - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceGamepad::Button::A("gamepad_button_a"); - const InputChannelId InputDeviceGamepad::Button::B("gamepad_button_b"); - const InputChannelId InputDeviceGamepad::Button::X("gamepad_button_x"); - const InputChannelId InputDeviceGamepad::Button::Y("gamepad_button_y"); - const InputChannelId InputDeviceGamepad::Button::L1("gamepad_button_l1"); - const InputChannelId InputDeviceGamepad::Button::R1("gamepad_button_r1"); - const InputChannelId InputDeviceGamepad::Button::L3("gamepad_button_l3"); - const InputChannelId InputDeviceGamepad::Button::R3("gamepad_button_r3"); - const InputChannelId InputDeviceGamepad::Button::DU("gamepad_button_d_up"); - const InputChannelId InputDeviceGamepad::Button::DD("gamepad_button_d_down"); - const InputChannelId InputDeviceGamepad::Button::DL("gamepad_button_d_left"); - const InputChannelId InputDeviceGamepad::Button::DR("gamepad_button_d_right"); - const InputChannelId InputDeviceGamepad::Button::Start("gamepad_button_start"); - const InputChannelId InputDeviceGamepad::Button::Select("gamepad_button_select"); - const AZStd::array InputDeviceGamepad::Button::All = - {{ - A, - B, - X, - Y, - L1, - R1, - L3, - R3, - DU, - DD, - DL, - DR, - Start, - Select - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceGamepad::Trigger::L2("gamepad_trigger_l2"); - const InputChannelId InputDeviceGamepad::Trigger::R2("gamepad_trigger_r2"); - const AZStd::array InputDeviceGamepad::Trigger::All = - {{ - L2, - R2 - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceGamepad::ThumbStickAxis2D::L("gamepad_thumbstick_l"); - const InputChannelId InputDeviceGamepad::ThumbStickAxis2D::R("gamepad_thumbstick_r"); - const AZStd::array InputDeviceGamepad::ThumbStickAxis2D::All = - {{ - L, - R - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::LX("gamepad_thumbstick_l_x"); - const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::LY("gamepad_thumbstick_l_y"); - const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::RX("gamepad_thumbstick_r_x"); - const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::RY("gamepad_thumbstick_r_y"); - const AZStd::array InputDeviceGamepad::ThumbStickAxis1D::All = - {{ - LX, - LY, - RX, - RY - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceGamepad::ThumbStickDirection::LU("gamepad_thumbstick_l_up"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::LD("gamepad_thumbstick_l_down"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::LL("gamepad_thumbstick_l_left"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::LR("gamepad_thumbstick_l_right"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::RU("gamepad_thumbstick_r_up"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::RD("gamepad_thumbstick_r_down"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::RL("gamepad_thumbstick_r_left"); - const InputChannelId InputDeviceGamepad::ThumbStickDirection::RR("gamepad_thumbstick_r_right"); - const AZStd::array InputDeviceGamepad::ThumbStickDirection::All = - {{ - LU, - LD, - LL, - LR, - RU, - RD, - RL, - RR - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceGamepad::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h index f4fddbb24e..286b4f0df3 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h @@ -59,75 +59,115 @@ namespace AzFramework //! All the input channel ids that identify game-pad digital button input struct Button { - static const InputChannelId A; //!< The bottom diamond face button - static const InputChannelId B; //!< The right diamond face button - static const InputChannelId X; //!< The left diamond face button - static const InputChannelId Y; //!< The top diamond face button - static const InputChannelId L1; //!< The top-left shoulder bumper button - static const InputChannelId R1; //!< The top-right shoulder bumper button - static const InputChannelId L3; //!< The left thumb-stick click button - static const InputChannelId R3; //!< The right thumb-stick click button - static const InputChannelId DU; //!< The up directional pad button - static const InputChannelId DD; //!< The down directional pad button - static const InputChannelId DL; //!< The left directional pad button - static const InputChannelId DR; //!< The right directional pad button - static const InputChannelId Start; //!< The start/pause/options button - static const InputChannelId Select; //!< The select/back button + static constexpr inline InputChannelId A{"gamepad_button_a"}; //!< The bottom diamond face button + static constexpr inline InputChannelId B{"gamepad_button_b"}; //!< The right diamond face button + static constexpr inline InputChannelId X{"gamepad_button_x"}; //!< The left diamond face button + static constexpr inline InputChannelId Y{"gamepad_button_y"}; //!< The top diamond face button + static constexpr inline InputChannelId L1{"gamepad_button_l1"}; //!< The top-left shoulder bumper button + static constexpr inline InputChannelId R1{"gamepad_button_r1"}; //!< The top-right shoulder bumper button + static constexpr inline InputChannelId L3{"gamepad_button_l3"}; //!< The left thumb-stick click button + static constexpr inline InputChannelId R3{"gamepad_button_r3"}; //!< The right thumb-stick click button + static constexpr inline InputChannelId DU{"gamepad_button_d_up"}; //!< The up directional pad button + static constexpr inline InputChannelId DD{"gamepad_button_d_down"}; //!< The down directional pad button + static constexpr inline InputChannelId DL{"gamepad_button_d_left"}; //!< The left directional pad button + static constexpr inline InputChannelId DR{"gamepad_button_d_right"}; //!< The right directional pad button + static constexpr inline InputChannelId Start{"gamepad_button_start"}; //!< The start/pause/options button + static constexpr inline InputChannelId Select{"gamepad_button_select"}; //!< The select/back button //!< All digital game-pad button ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + A, + B, + X, + Y, + L1, + R1, + L3, + R3, + DU, + DD, + DL, + DR, + Start, + Select + }; }; //////////////////////////////////////////////////////////////////////////////////////////// //! All the input channel ids that identify game-pad analog trigger input struct Trigger { - static const InputChannelId L2; //!< The bottom-left shoulder trigger - static const InputChannelId R2; //!< The bottom-right shoulder trigger + static constexpr inline InputChannelId L2{"gamepad_trigger_l2"}; //!< The bottom-left shoulder trigger + static constexpr inline InputChannelId R2{"gamepad_trigger_r2"}; //!< The bottom-right shoulder trigger //!< All analog game-pad trigger ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + L2, + R2 + }; }; //////////////////////////////////////////////////////////////////////////////////////////// //! All the input channel ids that identify game-pad thumb-stick 2D axis input struct ThumbStickAxis2D { - static const InputChannelId L; //!< The left-hand thumb-stick - static const InputChannelId R; //!< The right-hand thumb-stick + static constexpr inline InputChannelId L{"gamepad_thumbstick_l"}; //!< The left-hand thumb-stick + static constexpr inline InputChannelId R{"gamepad_thumbstick_r"}; //!< The right-hand thumb-stick //!< All game-pad thumb-stick 2D axis input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + L, + R + }; }; //////////////////////////////////////////////////////////////////////////////////////////// //! All the input channel ids that identify game-pad thumb-stick 1D axis input struct ThumbStickAxis1D { - static const InputChannelId LX; //!< X-axis of the left-hand thumb-stick - static const InputChannelId LY; //!< Y-axis of the left-hand thumb-stick - static const InputChannelId RX; //!< X-axis of the right-hand thumb-stick - static const InputChannelId RY; //!< Y-axis of the right-hand thumb-stick + static constexpr inline InputChannelId LX{"gamepad_thumbstick_l_x"}; //!< X-axis of the left-hand thumb-stick + static constexpr inline InputChannelId LY{"gamepad_thumbstick_l_y"}; //!< Y-axis of the left-hand thumb-stick + static constexpr inline InputChannelId RX{"gamepad_thumbstick_r_x"}; //!< X-axis of the right-hand thumb-stick + static constexpr inline InputChannelId RY{"gamepad_thumbstick_r_y"}; //!< Y-axis of the right-hand thumb-stick //!< All game-pad thumb-stick 1D axis input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + LX, + LY, + RX, + RY + }; }; //////////////////////////////////////////////////////////////////////////////////////////// //! All the input channel ids that identify game-pad thumb-stick directional input struct ThumbStickDirection { - static const InputChannelId LU; //!< Up on the left-hand thumb-stick - static const InputChannelId LD; //!< Down on the left-hand thumb-stick - static const InputChannelId LL; //!< Left on the left-hand thumb-stick - static const InputChannelId LR; //!< Right on the left-hand thumb-stick - static const InputChannelId RU; //!< Up on the left-hand thumb-stick - static const InputChannelId RD; //!< Down on the left-hand thumb-stick - static const InputChannelId RL; //!< Left on the left-hand thumb-stick - static const InputChannelId RR; //!< Right on the left-hand thumb-stick + static constexpr inline InputChannelId LU{"gamepad_thumbstick_l_up"}; //!< Up on the left-hand thumb-stick + static constexpr inline InputChannelId LD{"gamepad_thumbstick_l_down"}; //!< Down on the left-hand thumb-stick + static constexpr inline InputChannelId LL{"gamepad_thumbstick_l_left"}; //!< Left on the left-hand thumb-stick + static constexpr inline InputChannelId LR{"gamepad_thumbstick_l_right"}; //!< Right on the left-hand thumb-stick + static constexpr inline InputChannelId RU{"gamepad_thumbstick_r_up"}; //!< Up on the left-hand thumb-stick + static constexpr inline InputChannelId RD{"gamepad_thumbstick_r_down"}; //!< Down on the left-hand thumb-stick + static constexpr inline InputChannelId RL{"gamepad_thumbstick_r_left"}; //!< Left on the left-hand thumb-stick + static constexpr inline InputChannelId RR{"gamepad_thumbstick_r_right"}; //!< Right on the left-hand thumb-stick //!< All game-pad thumb-stick directional input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + LU, + LD, + LL, + LR, + RU, + RD, + RL, + RR + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp index dd84fd9633..d1117ea895 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp @@ -24,279 +24,6 @@ namespace AzFramework return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32()); } - //////////////////////////////////////////////////////////////////////////////////////////////// - // Alphanumeric Keys - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric0("keyboard_key_alphanumeric_0"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric1("keyboard_key_alphanumeric_1"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric2("keyboard_key_alphanumeric_2"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric3("keyboard_key_alphanumeric_3"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric4("keyboard_key_alphanumeric_4"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric5("keyboard_key_alphanumeric_5"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric6("keyboard_key_alphanumeric_6"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric7("keyboard_key_alphanumeric_7"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric8("keyboard_key_alphanumeric_8"); - const InputChannelId InputDeviceKeyboard::Key::Alphanumeric9("keyboard_key_alphanumeric_9"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericA("keyboard_key_alphanumeric_A"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericB("keyboard_key_alphanumeric_B"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericC("keyboard_key_alphanumeric_C"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericD("keyboard_key_alphanumeric_D"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericE("keyboard_key_alphanumeric_E"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericF("keyboard_key_alphanumeric_F"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericG("keyboard_key_alphanumeric_G"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericH("keyboard_key_alphanumeric_H"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericI("keyboard_key_alphanumeric_I"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericJ("keyboard_key_alphanumeric_J"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericK("keyboard_key_alphanumeric_K"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericL("keyboard_key_alphanumeric_L"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericM("keyboard_key_alphanumeric_M"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericN("keyboard_key_alphanumeric_N"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericO("keyboard_key_alphanumeric_O"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericP("keyboard_key_alphanumeric_P"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericQ("keyboard_key_alphanumeric_Q"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericR("keyboard_key_alphanumeric_R"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericS("keyboard_key_alphanumeric_S"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericT("keyboard_key_alphanumeric_T"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericU("keyboard_key_alphanumeric_U"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericV("keyboard_key_alphanumeric_V"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericW("keyboard_key_alphanumeric_W"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericX("keyboard_key_alphanumeric_X"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericY("keyboard_key_alphanumeric_Y"); - const InputChannelId InputDeviceKeyboard::Key::AlphanumericZ("keyboard_key_alphanumeric_Z"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Edit (and escape) Keys - const InputChannelId InputDeviceKeyboard::Key::EditBackspace("keyboard_key_edit_backspace"); - const InputChannelId InputDeviceKeyboard::Key::EditCapsLock("keyboard_key_edit_capslock"); - const InputChannelId InputDeviceKeyboard::Key::EditEnter("keyboard_key_edit_enter"); - const InputChannelId InputDeviceKeyboard::Key::EditSpace("keyboard_key_edit_space"); - const InputChannelId InputDeviceKeyboard::Key::EditTab("keyboard_key_edit_tab"); - const InputChannelId InputDeviceKeyboard::Key::Escape("keyboard_key_escape"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Function Keys - const InputChannelId InputDeviceKeyboard::Key::Function01("keyboard_key_function_F01"); - const InputChannelId InputDeviceKeyboard::Key::Function02("keyboard_key_function_F02"); - const InputChannelId InputDeviceKeyboard::Key::Function03("keyboard_key_function_F03"); - const InputChannelId InputDeviceKeyboard::Key::Function04("keyboard_key_function_F04"); - const InputChannelId InputDeviceKeyboard::Key::Function05("keyboard_key_function_F05"); - const InputChannelId InputDeviceKeyboard::Key::Function06("keyboard_key_function_F06"); - const InputChannelId InputDeviceKeyboard::Key::Function07("keyboard_key_function_F07"); - const InputChannelId InputDeviceKeyboard::Key::Function08("keyboard_key_function_F08"); - const InputChannelId InputDeviceKeyboard::Key::Function09("keyboard_key_function_F09"); - const InputChannelId InputDeviceKeyboard::Key::Function10("keyboard_key_function_F10"); - const InputChannelId InputDeviceKeyboard::Key::Function11("keyboard_key_function_F11"); - const InputChannelId InputDeviceKeyboard::Key::Function12("keyboard_key_function_F12"); - const InputChannelId InputDeviceKeyboard::Key::Function13("keyboard_key_function_F13"); - const InputChannelId InputDeviceKeyboard::Key::Function14("keyboard_key_function_F14"); - const InputChannelId InputDeviceKeyboard::Key::Function15("keyboard_key_function_F15"); - const InputChannelId InputDeviceKeyboard::Key::Function16("keyboard_key_function_F16"); - const InputChannelId InputDeviceKeyboard::Key::Function17("keyboard_key_function_F17"); - const InputChannelId InputDeviceKeyboard::Key::Function18("keyboard_key_function_F18"); - const InputChannelId InputDeviceKeyboard::Key::Function19("keyboard_key_function_F19"); - const InputChannelId InputDeviceKeyboard::Key::Function20("keyboard_key_function_F20"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Modifier Keys - const InputChannelId InputDeviceKeyboard::Key::ModifierAltL("keyboard_key_modifier_alt_l"); - const InputChannelId InputDeviceKeyboard::Key::ModifierAltR("keyboard_key_modifier_alt_r"); - const InputChannelId InputDeviceKeyboard::Key::ModifierCtrlL("keyboard_key_modifier_ctrl_l"); - const InputChannelId InputDeviceKeyboard::Key::ModifierCtrlR("keyboard_key_modifier_ctrl_r"); - const InputChannelId InputDeviceKeyboard::Key::ModifierShiftL("keyboard_key_modifier_shift_l"); - const InputChannelId InputDeviceKeyboard::Key::ModifierShiftR("keyboard_key_modifier_shift_r"); - const InputChannelId InputDeviceKeyboard::Key::ModifierSuperL("keyboard_key_modifier_super_l"); - const InputChannelId InputDeviceKeyboard::Key::ModifierSuperR("keyboard_key_modifier_super_r"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Navigation Keys - const InputChannelId InputDeviceKeyboard::Key::NavigationArrowDown("keyboard_key_navigation_arrow_down"); - const InputChannelId InputDeviceKeyboard::Key::NavigationArrowLeft("keyboard_key_navigation_arrow_left"); - const InputChannelId InputDeviceKeyboard::Key::NavigationArrowRight("keyboard_key_navigation_arrow_right"); - const InputChannelId InputDeviceKeyboard::Key::NavigationArrowUp("keyboard_key_navigation_arrow_up"); - const InputChannelId InputDeviceKeyboard::Key::NavigationDelete("keyboard_key_navigation_delete"); - const InputChannelId InputDeviceKeyboard::Key::NavigationEnd("keyboard_key_navigation_end"); - const InputChannelId InputDeviceKeyboard::Key::NavigationHome("keyboard_key_navigation_home"); - const InputChannelId InputDeviceKeyboard::Key::NavigationInsert("keyboard_key_navigation_insert"); - const InputChannelId InputDeviceKeyboard::Key::NavigationPageDown("keyboard_key_navigation_page_down"); - const InputChannelId InputDeviceKeyboard::Key::NavigationPageUp("keyboard_key_navigation_page_up"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Numpad Keys - const InputChannelId InputDeviceKeyboard::Key::NumLock("keyboard_key_num_lock"); - const InputChannelId InputDeviceKeyboard::Key::NumPad0("keyboard_key_numpad_0"); - const InputChannelId InputDeviceKeyboard::Key::NumPad1("keyboard_key_numpad_1"); - const InputChannelId InputDeviceKeyboard::Key::NumPad2("keyboard_key_numpad_2"); - const InputChannelId InputDeviceKeyboard::Key::NumPad3("keyboard_key_numpad_3"); - const InputChannelId InputDeviceKeyboard::Key::NumPad4("keyboard_key_numpad_4"); - const InputChannelId InputDeviceKeyboard::Key::NumPad5("keyboard_key_numpad_5"); - const InputChannelId InputDeviceKeyboard::Key::NumPad6("keyboard_key_numpad_6"); - const InputChannelId InputDeviceKeyboard::Key::NumPad7("keyboard_key_numpad_7"); - const InputChannelId InputDeviceKeyboard::Key::NumPad8("keyboard_key_numpad_8"); - const InputChannelId InputDeviceKeyboard::Key::NumPad9("keyboard_key_numpad_9"); - const InputChannelId InputDeviceKeyboard::Key::NumPadAdd("keyboard_key_numpad_add"); - const InputChannelId InputDeviceKeyboard::Key::NumPadDecimal("keyboard_key_numpad_decimal"); - const InputChannelId InputDeviceKeyboard::Key::NumPadDivide("keyboard_key_numpad_divide"); - const InputChannelId InputDeviceKeyboard::Key::NumPadEnter("keyboard_key_numpad_enter"); - const InputChannelId InputDeviceKeyboard::Key::NumPadMultiply("keyboard_key_numpad_multiply"); - const InputChannelId InputDeviceKeyboard::Key::NumPadSubtract("keyboard_key_numpad_subtract"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Punctuation Keys - const InputChannelId InputDeviceKeyboard::Key::PunctuationApostrophe("keyboard_key_punctuation_apostrophe"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationBackslash("keyboard_key_punctuation_backslash"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationBracketL("keyboard_key_punctuation_bracket_l"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationBracketR("keyboard_key_punctuation_bracket_r"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationComma("keyboard_key_punctuation_comma"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationEquals("keyboard_key_punctuation_equals"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationHyphen("keyboard_key_punctuation_hyphen"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationPeriod("keyboard_key_punctuation_period"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationSemicolon("keyboard_key_punctuation_semicolon"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationSlash("keyboard_key_punctuation_slash"); - const InputChannelId InputDeviceKeyboard::Key::PunctuationTilde("keyboard_key_punctuation_tilde"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Supplementary ISO Key - const InputChannelId InputDeviceKeyboard::Key::SupplementaryISO("keyboard_key_supplementary_iso"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - // Windows System Keys - const InputChannelId InputDeviceKeyboard::Key::WindowsSystemPause("keyboard_key_windows_system_pause"); - const InputChannelId InputDeviceKeyboard::Key::WindowsSystemPrint("keyboard_key_windows_system_print"); - const InputChannelId InputDeviceKeyboard::Key::WindowsSystemScrollLock("keyboard_key_windows_system_scroll_lock"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - const AZStd::array InputDeviceKeyboard::Key::All = - {{ - // Alphanumeric Keys - Alphanumeric0, - Alphanumeric1, - Alphanumeric2, - Alphanumeric3, - Alphanumeric4, - Alphanumeric5, - Alphanumeric6, - Alphanumeric7, - Alphanumeric8, - Alphanumeric9, - AlphanumericA, - AlphanumericB, - AlphanumericC, - AlphanumericD, - AlphanumericE, - AlphanumericF, - AlphanumericG, - AlphanumericH, - AlphanumericI, - AlphanumericJ, - AlphanumericK, - AlphanumericL, - AlphanumericM, - AlphanumericN, - AlphanumericO, - AlphanumericP, - AlphanumericQ, - AlphanumericR, - AlphanumericS, - AlphanumericT, - AlphanumericU, - AlphanumericV, - AlphanumericW, - AlphanumericX, - AlphanumericY, - AlphanumericZ, - - // Edit (and escape) Keys - EditBackspace, - EditCapsLock, - EditEnter, - EditSpace, - EditTab, - Escape, - - // Function Keys - Function01, - Function02, - Function03, - Function04, - Function05, - Function06, - Function07, - Function08, - Function09, - Function10, - Function11, - Function12, - Function13, - Function14, - Function15, - Function16, - Function17, - Function18, - Function19, - Function20, - - // Modifier Keys - ModifierAltL, - ModifierAltR, - ModifierCtrlL, - ModifierCtrlR, - ModifierShiftL, - ModifierShiftR, - ModifierSuperL, - ModifierSuperR, - - // Navigation Keys - NavigationArrowDown, - NavigationArrowLeft, - NavigationArrowRight, - NavigationArrowUp, - NavigationDelete, - NavigationEnd, - NavigationHome, - NavigationInsert, - NavigationPageDown, - NavigationPageUp, - - // Numpad Keys - NumLock, - NumPad0, - NumPad1, - NumPad2, - NumPad3, - NumPad4, - NumPad5, - NumPad6, - NumPad7, - NumPad8, - NumPad9, - NumPadAdd, - NumPadDecimal, - NumPadDivide, - NumPadEnter, - NumPadMultiply, - NumPadSubtract, - - // Punctuation Keys - PunctuationApostrophe, - PunctuationBackslash, - PunctuationBracketL, - PunctuationBracketR, - PunctuationComma, - PunctuationEquals, - PunctuationHyphen, - PunctuationPeriod, - PunctuationSemicolon, - PunctuationSlash, - PunctuationTilde, - - // Supplementary ISO Key - SupplementaryISO, - - // Windows System Keys - WindowsSystemPause, - WindowsSystemPrint, - WindowsSystemScrollLock - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// ModifierKeyMask GetCorrespondingModifierKeyMask(const InputChannelId& channelId) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h index 5588f94d69..e3c21ec326 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h @@ -94,137 +94,268 @@ namespace AzFramework struct Key { // Alphanumeric Keys - static const InputChannelId Alphanumeric0; //!< The 0 key - static const InputChannelId Alphanumeric1; //!< The 1 key - static const InputChannelId Alphanumeric2; //!< The 2 key - static const InputChannelId Alphanumeric3; //!< The 3 key - static const InputChannelId Alphanumeric4; //!< The 4 key - static const InputChannelId Alphanumeric5; //!< The 5 key - static const InputChannelId Alphanumeric6; //!< The 6 key - static const InputChannelId Alphanumeric7; //!< The 7 key - static const InputChannelId Alphanumeric8; //!< The 8 key - static const InputChannelId Alphanumeric9; //!< The 9 key - static const InputChannelId AlphanumericA; //!< The A key - static const InputChannelId AlphanumericB; //!< The B key - static const InputChannelId AlphanumericC; //!< The C key - static const InputChannelId AlphanumericD; //!< The D key - static const InputChannelId AlphanumericE; //!< The E key - static const InputChannelId AlphanumericF; //!< The F key - static const InputChannelId AlphanumericG; //!< The G key - static const InputChannelId AlphanumericH; //!< The H key - static const InputChannelId AlphanumericI; //!< The I key - static const InputChannelId AlphanumericJ; //!< The J key - static const InputChannelId AlphanumericK; //!< The K key - static const InputChannelId AlphanumericL; //!< The L key - static const InputChannelId AlphanumericM; //!< The M key - static const InputChannelId AlphanumericN; //!< The N key - static const InputChannelId AlphanumericO; //!< The O key - static const InputChannelId AlphanumericP; //!< The P key - static const InputChannelId AlphanumericQ; //!< The Q key - static const InputChannelId AlphanumericR; //!< The R key - static const InputChannelId AlphanumericS; //!< The S key - static const InputChannelId AlphanumericT; //!< The T key - static const InputChannelId AlphanumericU; //!< The U key - static const InputChannelId AlphanumericV; //!< The V key - static const InputChannelId AlphanumericW; //!< The W key - static const InputChannelId AlphanumericX; //!< The X key - static const InputChannelId AlphanumericY; //!< The Y key - static const InputChannelId AlphanumericZ; //!< The Z key + static constexpr inline InputChannelId Alphanumeric0{"keyboard_key_alphanumeric_0"}; //!< The 0 key + static constexpr inline InputChannelId Alphanumeric1{"keyboard_key_alphanumeric_1"}; //!< The 1 key + static constexpr inline InputChannelId Alphanumeric2{"keyboard_key_alphanumeric_2"}; //!< The 2 key + static constexpr inline InputChannelId Alphanumeric3{"keyboard_key_alphanumeric_3"}; //!< The 3 key + static constexpr inline InputChannelId Alphanumeric4{"keyboard_key_alphanumeric_4"}; //!< The 4 key + static constexpr inline InputChannelId Alphanumeric5{"keyboard_key_alphanumeric_5"}; //!< The 5 key + static constexpr inline InputChannelId Alphanumeric6{"keyboard_key_alphanumeric_6"}; //!< The 6 key + static constexpr inline InputChannelId Alphanumeric7{"keyboard_key_alphanumeric_7"}; //!< The 7 key + static constexpr inline InputChannelId Alphanumeric8{"keyboard_key_alphanumeric_8"}; //!< The 8 key + static constexpr inline InputChannelId Alphanumeric9{"keyboard_key_alphanumeric_9"}; //!< The 9 key + static constexpr inline InputChannelId AlphanumericA{"keyboard_key_alphanumeric_A"}; //!< The A key + static constexpr inline InputChannelId AlphanumericB{"keyboard_key_alphanumeric_B"}; //!< The B key + static constexpr inline InputChannelId AlphanumericC{"keyboard_key_alphanumeric_C"}; //!< The C key + static constexpr inline InputChannelId AlphanumericD{"keyboard_key_alphanumeric_D"}; //!< The D key + static constexpr inline InputChannelId AlphanumericE{"keyboard_key_alphanumeric_E"}; //!< The E key + static constexpr inline InputChannelId AlphanumericF{"keyboard_key_alphanumeric_F"}; //!< The F key + static constexpr inline InputChannelId AlphanumericG{"keyboard_key_alphanumeric_G"}; //!< The G key + static constexpr inline InputChannelId AlphanumericH{"keyboard_key_alphanumeric_H"}; //!< The H key + static constexpr inline InputChannelId AlphanumericI{"keyboard_key_alphanumeric_I"}; //!< The I key + static constexpr inline InputChannelId AlphanumericJ{"keyboard_key_alphanumeric_J"}; //!< The J key + static constexpr inline InputChannelId AlphanumericK{"keyboard_key_alphanumeric_K"}; //!< The K key + static constexpr inline InputChannelId AlphanumericL{"keyboard_key_alphanumeric_L"}; //!< The L key + static constexpr inline InputChannelId AlphanumericM{"keyboard_key_alphanumeric_M"}; //!< The M key + static constexpr inline InputChannelId AlphanumericN{"keyboard_key_alphanumeric_N"}; //!< The N key + static constexpr inline InputChannelId AlphanumericO{"keyboard_key_alphanumeric_O"}; //!< The O key + static constexpr inline InputChannelId AlphanumericP{"keyboard_key_alphanumeric_P"}; //!< The P key + static constexpr inline InputChannelId AlphanumericQ{"keyboard_key_alphanumeric_Q"}; //!< The Q key + static constexpr inline InputChannelId AlphanumericR{"keyboard_key_alphanumeric_R"}; //!< The R key + static constexpr inline InputChannelId AlphanumericS{"keyboard_key_alphanumeric_S"}; //!< The S key + static constexpr inline InputChannelId AlphanumericT{"keyboard_key_alphanumeric_T"}; //!< The T key + static constexpr inline InputChannelId AlphanumericU{"keyboard_key_alphanumeric_U"}; //!< The U key + static constexpr inline InputChannelId AlphanumericV{"keyboard_key_alphanumeric_V"}; //!< The V key + static constexpr inline InputChannelId AlphanumericW{"keyboard_key_alphanumeric_W"}; //!< The W key + static constexpr inline InputChannelId AlphanumericX{"keyboard_key_alphanumeric_X"}; //!< The X key + static constexpr inline InputChannelId AlphanumericY{"keyboard_key_alphanumeric_Y"}; //!< The Y key + static constexpr inline InputChannelId AlphanumericZ{"keyboard_key_alphanumeric_Z"}; //!< The Z key - // Edit (and escape) Keys - static const InputChannelId EditBackspace; //!< The backspace key - static const InputChannelId EditCapsLock; //!< The caps lock key - static const InputChannelId EditEnter; //!< The enter/return key - static const InputChannelId EditSpace; //!< The spacebar key - static const InputChannelId EditTab; //!< The tab key - static const InputChannelId Escape; //!< The escape key + // Edit {and escape} Keys + static constexpr inline InputChannelId EditBackspace{"keyboard_key_edit_backspace"}; //!< The backspace key + static constexpr inline InputChannelId EditCapsLock{"keyboard_key_edit_capslock"}; //!< The caps lock key + static constexpr inline InputChannelId EditEnter{"keyboard_key_edit_enter"}; //!< The enter/return key + static constexpr inline InputChannelId EditSpace{"keyboard_key_edit_space"}; //!< The spacebar key + static constexpr inline InputChannelId EditTab{"keyboard_key_edit_tab"}; //!< The tab key + static constexpr inline InputChannelId Escape{"keyboard_key_escape"}; //!< The escape key // Function Keys - static const InputChannelId Function01; //!< The F1 key - static const InputChannelId Function02; //!< The F2 key - static const InputChannelId Function03; //!< The F3 key - static const InputChannelId Function04; //!< The F4 key - static const InputChannelId Function05; //!< The F5 key - static const InputChannelId Function06; //!< The F6 key - static const InputChannelId Function07; //!< The F7 key - static const InputChannelId Function08; //!< The F8 key - static const InputChannelId Function09; //!< The F9 key - static const InputChannelId Function10; //!< The F10 key - static const InputChannelId Function11; //!< The F11 key - static const InputChannelId Function12; //!< The F12 key - static const InputChannelId Function13; //!< The F13 key - static const InputChannelId Function14; //!< The F14 key - static const InputChannelId Function15; //!< The F15 key - static const InputChannelId Function16; //!< The F16 key - static const InputChannelId Function17; //!< The F17 key - static const InputChannelId Function18; //!< The F18 key - static const InputChannelId Function19; //!< The F19 key - static const InputChannelId Function20; //!< The F20 key + static constexpr inline InputChannelId Function01{"keyboard_key_function_F01"}; //!< The F1 key + static constexpr inline InputChannelId Function02{"keyboard_key_function_F02"}; //!< The F2 key + static constexpr inline InputChannelId Function03{"keyboard_key_function_F03"}; //!< The F3 key + static constexpr inline InputChannelId Function04{"keyboard_key_function_F04"}; //!< The F4 key + static constexpr inline InputChannelId Function05{"keyboard_key_function_F05"}; //!< The F5 key + static constexpr inline InputChannelId Function06{"keyboard_key_function_F06"}; //!< The F6 key + static constexpr inline InputChannelId Function07{"keyboard_key_function_F07"}; //!< The F7 key + static constexpr inline InputChannelId Function08{"keyboard_key_function_F08"}; //!< The F8 key + static constexpr inline InputChannelId Function09{"keyboard_key_function_F09"}; //!< The F9 key + static constexpr inline InputChannelId Function10{"keyboard_key_function_F10"}; //!< The F10 key + static constexpr inline InputChannelId Function11{"keyboard_key_function_F11"}; //!< The F11 key + static constexpr inline InputChannelId Function12{"keyboard_key_function_F12"}; //!< The F12 key + static constexpr inline InputChannelId Function13{"keyboard_key_function_F13"}; //!< The F13 key + static constexpr inline InputChannelId Function14{"keyboard_key_function_F14"}; //!< The F14 key + static constexpr inline InputChannelId Function15{"keyboard_key_function_F15"}; //!< The F15 key + static constexpr inline InputChannelId Function16{"keyboard_key_function_F16"}; //!< The F16 key + static constexpr inline InputChannelId Function17{"keyboard_key_function_F17"}; //!< The F17 key + static constexpr inline InputChannelId Function18{"keyboard_key_function_F18"}; //!< The F18 key + static constexpr inline InputChannelId Function19{"keyboard_key_function_F19"}; //!< The F19 key + static constexpr inline InputChannelId Function20{"keyboard_key_function_F20"}; //!< The F20 key // Modifier Keys - static const InputChannelId ModifierAltL; //!< The left alt/option key - static const InputChannelId ModifierAltR; //!< The right alt/option key - static const InputChannelId ModifierCtrlL; //!< The left control key - static const InputChannelId ModifierCtrlR; //!< The right control key - static const InputChannelId ModifierShiftL; //!< The left shift key - static const InputChannelId ModifierShiftR; //!< The right shift key - static const InputChannelId ModifierSuperL; //!< The left super (windows or apple) key - static const InputChannelId ModifierSuperR; //!< The right super (windows or apple) key + static constexpr inline InputChannelId ModifierAltL{"keyboard_key_modifier_alt_l"}; //!< The left alt/option key + static constexpr inline InputChannelId ModifierAltR{"keyboard_key_modifier_alt_r"}; //!< The right alt/option key + static constexpr inline InputChannelId ModifierCtrlL{"keyboard_key_modifier_ctrl_l"}; //!< The left control key + static constexpr inline InputChannelId ModifierCtrlR{"keyboard_key_modifier_ctrl_r"}; //!< The right control key + static constexpr inline InputChannelId ModifierShiftL{"keyboard_key_modifier_shift_l"}; //!< The left shift key + static constexpr inline InputChannelId ModifierShiftR{"keyboard_key_modifier_shift_r"}; //!< The right shift key + static constexpr inline InputChannelId ModifierSuperL{"keyboard_key_modifier_super_l"}; //!< The left super {windows or apple} key + static constexpr inline InputChannelId ModifierSuperR{"keyboard_key_modifier_super_r"}; //!< The right super {windows or apple} key // Navigation Keys - static const InputChannelId NavigationArrowDown; //!< The down arrow key - static const InputChannelId NavigationArrowLeft; //!< The left arrow key - static const InputChannelId NavigationArrowRight; //!< The right arrow key - static const InputChannelId NavigationArrowUp; //!< The up arrow key - static const InputChannelId NavigationDelete; //!< The delete key - static const InputChannelId NavigationEnd; //!< The end key - static const InputChannelId NavigationHome; //!< The home key - static const InputChannelId NavigationInsert; //!< The insert key - static const InputChannelId NavigationPageDown; //!< The page down key - static const InputChannelId NavigationPageUp; //!< The page up key + static constexpr inline InputChannelId NavigationArrowDown{"keyboard_key_navigation_arrow_down"}; //!< The down arrow key + static constexpr inline InputChannelId NavigationArrowLeft{"keyboard_key_navigation_arrow_left"}; //!< The left arrow key + static constexpr inline InputChannelId NavigationArrowRight{"keyboard_key_navigation_arrow_right"}; //!< The right arrow key + static constexpr inline InputChannelId NavigationArrowUp{"keyboard_key_navigation_arrow_up"}; //!< The up arrow key + static constexpr inline InputChannelId NavigationDelete{"keyboard_key_navigation_delete"}; //!< The delete key + static constexpr inline InputChannelId NavigationEnd{"keyboard_key_navigation_end"}; //!< The end key + static constexpr inline InputChannelId NavigationHome{"keyboard_key_navigation_home"}; //!< The home key + static constexpr inline InputChannelId NavigationInsert{"keyboard_key_navigation_insert"}; //!< The insert key + static constexpr inline InputChannelId NavigationPageDown{"keyboard_key_navigation_page_down"}; //!< The page down key + static constexpr inline InputChannelId NavigationPageUp{"keyboard_key_navigation_page_up"}; //!< The page up key // Numpad Keys - static const InputChannelId NumLock; //!< The num lock key (the clear key on apple keyboards) - static const InputChannelId NumPad0; //!< The numpad 0 key - static const InputChannelId NumPad1; //!< The numpad 1 key - static const InputChannelId NumPad2; //!< The numpad 2 key - static const InputChannelId NumPad3; //!< The numpad 3 key - static const InputChannelId NumPad4; //!< The numpad 4 key - static const InputChannelId NumPad5; //!< The numpad 5 key - static const InputChannelId NumPad6; //!< The numpad 6 key - static const InputChannelId NumPad7; //!< The numpad 7 key - static const InputChannelId NumPad8; //!< The numpad 8 key - static const InputChannelId NumPad9; //!< The numpad 9 key - static const InputChannelId NumPadAdd; //!< The numpad add key - static const InputChannelId NumPadDecimal; //!< The numpad decimal key - static const InputChannelId NumPadDivide; //!< The numpad divide key - static const InputChannelId NumPadEnter; //!< The numpad enter key - static const InputChannelId NumPadMultiply; //!< The numpad multiply key - static const InputChannelId NumPadSubtract; //!< The numpad subtract key + static constexpr inline InputChannelId NumLock{"keyboard_key_num_lock"}; //!< The num lock key {the clear key on apple keyboards} + static constexpr inline InputChannelId NumPad0{"keyboard_key_numpad_0"}; //!< The numpad 0 key + static constexpr inline InputChannelId NumPad1{"keyboard_key_numpad_1"}; //!< The numpad 1 key + static constexpr inline InputChannelId NumPad2{"keyboard_key_numpad_2"}; //!< The numpad 2 key + static constexpr inline InputChannelId NumPad3{"keyboard_key_numpad_3"}; //!< The numpad 3 key + static constexpr inline InputChannelId NumPad4{"keyboard_key_numpad_4"}; //!< The numpad 4 key + static constexpr inline InputChannelId NumPad5{"keyboard_key_numpad_5"}; //!< The numpad 5 key + static constexpr inline InputChannelId NumPad6{"keyboard_key_numpad_6"}; //!< The numpad 6 key + static constexpr inline InputChannelId NumPad7{"keyboard_key_numpad_7"}; //!< The numpad 7 key + static constexpr inline InputChannelId NumPad8{"keyboard_key_numpad_8"}; //!< The numpad 8 key + static constexpr inline InputChannelId NumPad9{"keyboard_key_numpad_9"}; //!< The numpad 9 key + static constexpr inline InputChannelId NumPadAdd{"keyboard_key_numpad_add"}; //!< The numpad add key + static constexpr inline InputChannelId NumPadDecimal{"keyboard_key_numpad_decimal"}; //!< The numpad decimal key + static constexpr inline InputChannelId NumPadDivide{"keyboard_key_numpad_divide"}; //!< The numpad divide key + static constexpr inline InputChannelId NumPadEnter{"keyboard_key_numpad_enter"}; //!< The numpad enter key + static constexpr inline InputChannelId NumPadMultiply{"keyboard_key_numpad_multiply"}; //!< The numpad multiply key + static constexpr inline InputChannelId NumPadSubtract{"keyboard_key_numpad_subtract"}; //!< The numpad subtract key // Punctuation Keys - static const InputChannelId PunctuationApostrophe; //!< The apostrophe key - static const InputChannelId PunctuationBackslash; //!< The backslash key - static const InputChannelId PunctuationBracketL; //!< The left bracket key - static const InputChannelId PunctuationBracketR; //!< The right bracket key - static const InputChannelId PunctuationComma; //!< The comma key - static const InputChannelId PunctuationEquals; //!< The equals key - static const InputChannelId PunctuationHyphen; //!< The hyphen/underscore key - static const InputChannelId PunctuationPeriod; //!< The period key - static const InputChannelId PunctuationSemicolon; //!< The semicolon key - static const InputChannelId PunctuationSlash; //!< The (forward) slash key - static const InputChannelId PunctuationTilde; //!< The tilde/grave key + static constexpr inline InputChannelId PunctuationApostrophe{"keyboard_key_punctuation_apostrophe"}; //!< The apostrophe key + static constexpr inline InputChannelId PunctuationBackslash{"keyboard_key_punctuation_backslash"}; //!< The backslash key + static constexpr inline InputChannelId PunctuationBracketL{"keyboard_key_punctuation_bracket_l"}; //!< The left bracket key + static constexpr inline InputChannelId PunctuationBracketR{"keyboard_key_punctuation_bracket_r"}; //!< The right bracket key + static constexpr inline InputChannelId PunctuationComma{"keyboard_key_punctuation_comma"}; //!< The comma key + static constexpr inline InputChannelId PunctuationEquals{"keyboard_key_punctuation_equals"}; //!< The equals key + static constexpr inline InputChannelId PunctuationHyphen{"keyboard_key_punctuation_hyphen"}; //!< The hyphen/underscore key + static constexpr inline InputChannelId PunctuationPeriod{"keyboard_key_punctuation_period"}; //!< The period key + static constexpr inline InputChannelId PunctuationSemicolon{"keyboard_key_punctuation_semicolon"}; //!< The semicolon key + static constexpr inline InputChannelId PunctuationSlash{"keyboard_key_punctuation_slash"}; //!< The {forward} slash key + static constexpr inline InputChannelId PunctuationTilde{"keyboard_key_punctuation_tilde"}; //!< The tilde/grave key // Supplementary ISO Key - static const InputChannelId SupplementaryISO; //!< The supplementary ISO layout key + static constexpr inline InputChannelId SupplementaryISO{"keyboard_key_supplementary_iso"}; //!< The supplementary ISO layout key // Windows System Keys - static const InputChannelId WindowsSystemPause; //!< The windows pause key - static const InputChannelId WindowsSystemPrint; //!< The windows print key - static const InputChannelId WindowsSystemScrollLock; //!< The windows scroll lock key + static constexpr inline InputChannelId WindowsSystemPause{"keyboard_key_windows_system_pause"}; //!< The windows pause key + static constexpr inline InputChannelId WindowsSystemPrint{"keyboard_key_windows_system_print"}; //!< The windows print key + static constexpr inline InputChannelId WindowsSystemScrollLock{"keyboard_key_windows_system_scroll_lock"}; //!< The windows scroll lock key //!< All keyboard key ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + // Alphanumeric Keys + Alphanumeric0, + Alphanumeric1, + Alphanumeric2, + Alphanumeric3, + Alphanumeric4, + Alphanumeric5, + Alphanumeric6, + Alphanumeric7, + Alphanumeric8, + Alphanumeric9, + AlphanumericA, + AlphanumericB, + AlphanumericC, + AlphanumericD, + AlphanumericE, + AlphanumericF, + AlphanumericG, + AlphanumericH, + AlphanumericI, + AlphanumericJ, + AlphanumericK, + AlphanumericL, + AlphanumericM, + AlphanumericN, + AlphanumericO, + AlphanumericP, + AlphanumericQ, + AlphanumericR, + AlphanumericS, + AlphanumericT, + AlphanumericU, + AlphanumericV, + AlphanumericW, + AlphanumericX, + AlphanumericY, + AlphanumericZ, + + // Edit (and escape) Keys + EditBackspace, + EditCapsLock, + EditEnter, + EditSpace, + EditTab, + Escape, + + // Function Keys + Function01, + Function02, + Function03, + Function04, + Function05, + Function06, + Function07, + Function08, + Function09, + Function10, + Function11, + Function12, + Function13, + Function14, + Function15, + Function16, + Function17, + Function18, + Function19, + Function20, + + // Modifier Keys + ModifierAltL, + ModifierAltR, + ModifierCtrlL, + ModifierCtrlR, + ModifierShiftL, + ModifierShiftR, + ModifierSuperL, + ModifierSuperR, + + // Navigation Keys + NavigationArrowDown, + NavigationArrowLeft, + NavigationArrowRight, + NavigationArrowUp, + NavigationDelete, + NavigationEnd, + NavigationHome, + NavigationInsert, + NavigationPageDown, + NavigationPageUp, + + // Numpad Keys + NumLock, + NumPad0, + NumPad1, + NumPad2, + NumPad3, + NumPad4, + NumPad5, + NumPad6, + NumPad7, + NumPad8, + NumPad9, + NumPadAdd, + NumPadDecimal, + NumPadDivide, + NumPadEnter, + NumPadMultiply, + NumPadSubtract, + + // Punctuation Keys + PunctuationApostrophe, + PunctuationBackslash, + PunctuationBracketL, + PunctuationBracketR, + PunctuationComma, + PunctuationEquals, + PunctuationHyphen, + PunctuationPeriod, + PunctuationSemicolon, + PunctuationSlash, + PunctuationTilde, + + // Supplementary ISO Key + SupplementaryISO, + + // Windows System Keys + WindowsSystemPause, + WindowsSystemPrint, + WindowsSystemScrollLock + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp index 34c72f20d2..c144f63d2c 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp @@ -23,44 +23,6 @@ namespace AzFramework return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32()); } - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMotion::Acceleration::Gravity("motion_acceleration_gravity"); - const InputChannelId InputDeviceMotion::Acceleration::Raw("motion_acceleration_raw"); - const InputChannelId InputDeviceMotion::Acceleration::User("motion_acceleration_user"); - const AZStd::array InputDeviceMotion::Acceleration::All = - {{ - Gravity, - Raw, - User - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMotion::RotationRate::Raw("motion_rotation_rate_raw"); - const InputChannelId InputDeviceMotion::RotationRate::Unbiased("motion_rotation_rate_unbiased"); - const AZStd::array InputDeviceMotion::RotationRate::All = - {{ - Raw, - Unbiased - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMotion::MagneticField::North("motion_magnetic_field_north"); - const InputChannelId InputDeviceMotion::MagneticField::Raw("motion_magnetic_field_raw"); - const InputChannelId InputDeviceMotion::MagneticField::Unbiased("motion_magnetic_field_unbiased"); - const AZStd::array InputDeviceMotion::MagneticField::All = - {{ - North, - Raw, - Unbiased - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMotion::Orientation::Current("motion_orientation_current"); - const AZStd::array InputDeviceMotion::Orientation::All = - {{ - Current - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceMotion::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h index 1e020a6e02..872bd1cfa0 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h @@ -44,12 +44,17 @@ namespace AzFramework //! - InputMotionSensorRequests::SetInputChannelEnabled struct Acceleration { - static const InputChannelId Gravity; - static const InputChannelId Raw; - static const InputChannelId User; + static constexpr inline InputChannelId Gravity{"motion_acceleration_gravity"}; + static constexpr inline InputChannelId Raw{"motion_acceleration_raw"}; + static constexpr inline InputChannelId User{"motion_acceleration_user"}; //!< All acceleration input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + Gravity, + Raw, + User + }; }; //////////////////////////////////////////////////////////////////////////////////////////// @@ -60,11 +65,15 @@ namespace AzFramework //! - InputMotionSensorRequests::SetInputChannelEnabled struct RotationRate { - static const InputChannelId Raw; - static const InputChannelId Unbiased; + static constexpr inline InputChannelId Raw{"motion_rotation_rate_raw"}; + static constexpr inline InputChannelId Unbiased{"motion_rotation_rate_unbiased"}; //!< All rotation rate input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + Raw, + Unbiased + }; }; //////////////////////////////////////////////////////////////////////////////////////////// @@ -75,12 +84,17 @@ namespace AzFramework //! - InputMotionSensorRequests::SetInputChannelEnabled struct MagneticField { - static const InputChannelId North; - static const InputChannelId Raw; - static const InputChannelId Unbiased; + static constexpr inline InputChannelId North{"motion_magnetic_field_north"}; + static constexpr inline InputChannelId Raw{"motion_magnetic_field_raw"}; + static constexpr inline InputChannelId Unbiased{"motion_magnetic_field_unbiased"}; //!< All magnetic field input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + North, + Raw, + Unbiased + }; }; //////////////////////////////////////////////////////////////////////////////////////////// @@ -91,10 +105,13 @@ namespace AzFramework //! - InputMotionSensorRequests::SetInputChannelEnabled struct Orientation { - static const InputChannelId Current; + static constexpr inline InputChannelId Current{"motion_orientation_current"}; //!< All orientation input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + Current + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp index 2cc4573bce..e9af4cc4ce 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp @@ -33,35 +33,6 @@ namespace AzFramework return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32()); } - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMouse::Button::Left("mouse_button_left"); - const InputChannelId InputDeviceMouse::Button::Right("mouse_button_right"); - const InputChannelId InputDeviceMouse::Button::Middle("mouse_button_middle"); - const InputChannelId InputDeviceMouse::Button::Other1("mouse_button_other1"); - const InputChannelId InputDeviceMouse::Button::Other2("mouse_button_other2"); - const AZStd::array InputDeviceMouse::Button::All = - {{ - Left, - Right, - Middle, - Other1, - Other2 - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMouse::Movement::X("mouse_delta_x"); - const InputChannelId InputDeviceMouse::Movement::Y("mouse_delta_y"); - const InputChannelId InputDeviceMouse::Movement::Z("mouse_delta_z"); - const AZStd::array InputDeviceMouse::Movement::All = - {{ - X, - Y, - Z - }}; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceMouse::SystemCursorPosition("mouse_system_cursor_position"); - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceMouse::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h index 99fe5b820c..3b35e03f1b 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h @@ -66,14 +66,21 @@ namespace AzFramework //! been implemented for windows simply to provide for backwards compatibility with CryInput. struct Button { - static const InputChannelId Left; //!< The left mouse button - static const InputChannelId Right; //!< The right mouse button - static const InputChannelId Middle; //!< The middle mouse button - static const InputChannelId Other1; //!< DEPRECATED: the x1 mouse button - static const InputChannelId Other2; //!< DEPRECATED: the x2 mouse button + static constexpr inline InputChannelId Left{"mouse_button_left"}; //!< The left mouse button + static constexpr inline InputChannelId Right{"mouse_button_right"}; //!< The right mouse button + static constexpr inline InputChannelId Middle{"mouse_button_middle"}; //!< The middle mouse button + static constexpr inline InputChannelId Other1{"mouse_button_other1"}; //!< DEPRECATED: the x1 mouse button + static constexpr inline InputChannelId Other2{"mouse_button_other2"}; //!< DEPRECATED: the x2 mouse button //!< All mouse button ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + Left, + Right, + Middle, + Other1, + Other2 + }; }; //////////////////////////////////////////////////////////////////////////////////////////// @@ -82,12 +89,17 @@ namespace AzFramework //! directly correlate to the mouse position (which is queried directly from the system). struct Movement { - static const InputChannelId X; //!< Raw horizontal mouse movement over the last frame - static const InputChannelId Y; //!< Raw vertical mouse movement over the last frame - static const InputChannelId Z; //!< Raw mouse wheel movement over the last frame + static constexpr inline InputChannelId X{"mouse_delta_x"}; //!< Raw horizontal mouse movement over the last frame + static constexpr inline InputChannelId Y{"mouse_delta_y"}; //!< Raw vertical mouse movement over the last frame + static constexpr inline InputChannelId Z{"mouse_delta_z"}; //!< Raw mouse wheel movement over the last frame //!< All mouse movement ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + X, + Y, + Z + }; }; //////////////////////////////////////////////////////////////////////////////////////////// @@ -96,7 +108,7 @@ namespace AzFramework //! the system cursor is hidden or visible. When the system cursor has been constrained to //! the active window values will be in the [0.0, 1.0] range, but not when unconstrained. //! See also InputSystemCursorRequests::SetSystemCursorState and GetSystemCursorState. - static const InputChannelId SystemCursorPosition; + static constexpr inline InputChannelId SystemCursorPosition{"mouse_system_cursor_position"}; //////////////////////////////////////////////////////////////////////////////////////////// // Allocator diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp index 07d10090a3..be850e9289 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp @@ -24,31 +24,6 @@ namespace AzFramework return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32()); } - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceTouch::Touch::Index0("touch_index_0"); - const InputChannelId InputDeviceTouch::Touch::Index1("touch_index_1"); - const InputChannelId InputDeviceTouch::Touch::Index2("touch_index_2"); - const InputChannelId InputDeviceTouch::Touch::Index3("touch_index_3"); - const InputChannelId InputDeviceTouch::Touch::Index4("touch_index_4"); - const InputChannelId InputDeviceTouch::Touch::Index5("touch_index_5"); - const InputChannelId InputDeviceTouch::Touch::Index6("touch_index_6"); - const InputChannelId InputDeviceTouch::Touch::Index7("touch_index_7"); - const InputChannelId InputDeviceTouch::Touch::Index8("touch_index_8"); - const InputChannelId InputDeviceTouch::Touch::Index9("touch_index_9"); - const AZStd::array InputDeviceTouch::Touch::All = - {{ - Index0, - Index1, - Index2, - Index3, - Index4, - Index5, - Index6, - Index7, - Index8, - Index9 - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceTouch::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h index 6e069c44ba..d21834c22a 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h @@ -38,19 +38,31 @@ namespace AzFramework //! track is arbitrary, but ten seems to be more than sufficient for most game applications. struct Touch { - static const InputChannelId Index0; //!< Touch index 0 - static const InputChannelId Index1; //!< Touch index 1 - static const InputChannelId Index2; //!< Touch index 2 - static const InputChannelId Index3; //!< Touch index 3 - static const InputChannelId Index4; //!< Touch index 4 - static const InputChannelId Index5; //!< Touch index 5 - static const InputChannelId Index6; //!< Touch index 6 - static const InputChannelId Index7; //!< Touch index 7 - static const InputChannelId Index8; //!< Touch index 8 - static const InputChannelId Index9; //!< Touch index 9 + static constexpr inline InputChannelId Index0{"touch_index_0"}; //!< Touch index 0 + static constexpr inline InputChannelId Index1{"touch_index_1"}; //!< Touch index 1 + static constexpr inline InputChannelId Index2{"touch_index_2"}; //!< Touch index 2 + static constexpr inline InputChannelId Index3{"touch_index_3"}; //!< Touch index 3 + static constexpr inline InputChannelId Index4{"touch_index_4"}; //!< Touch index 4 + static constexpr inline InputChannelId Index5{"touch_index_5"}; //!< Touch index 5 + static constexpr inline InputChannelId Index6{"touch_index_6"}; //!< Touch index 6 + static constexpr inline InputChannelId Index7{"touch_index_7"}; //!< Touch index 7 + static constexpr inline InputChannelId Index8{"touch_index_8"}; //!< Touch index 8 + static constexpr inline InputChannelId Index9{"touch_index_9"}; //!< Touch index 9 //!< All touch input channel ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + Index0, + Index1, + Index2, + Index3, + Index4, + Index5, + Index6, + Index7, + Index8, + Index9 + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp index bc23826ebc..f3024f11ab 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp @@ -23,17 +23,6 @@ namespace AzFramework return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32()); } - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputChannelId InputDeviceVirtualKeyboard::Command::EditEnter("virtual_keyboard_edit_enter"); - const InputChannelId InputDeviceVirtualKeyboard::Command::EditClear("virtual_keyboard_edit_clear"); - const InputChannelId InputDeviceVirtualKeyboard::Command::NavigationBack("virtual_keyboard_navigation_back"); - const AZStd::array InputDeviceVirtualKeyboard::Command::All = - {{ - EditClear, - EditEnter, - NavigationBack - }}; - //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceVirtualKeyboard::Reflect(AZ::ReflectContext* context) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h index 43e2815875..6f62c3ec61 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h @@ -39,17 +39,22 @@ namespace AzFramework struct Command { //!< The clear command used to indicate the user wants to clear the active text field - static const InputChannelId EditClear; + static constexpr inline InputChannelId EditClear{"virtual_keyboard_edit_enter"}; //!< The enter/return/close command used to indicate the user has finished text editing - static const InputChannelId EditEnter; + static constexpr inline InputChannelId EditEnter{"virtual_keyboard_edit_clear"}; //!< The back command used to indicate the user wants to navigate 'backwards'. //!< This is specific to android devices, and does not have an ios equivalent. - static const InputChannelId NavigationBack; + static constexpr inline InputChannelId NavigationBack{"virtual_keyboard_navigation_back"}; //!< All virtual keyboard command ids - static const AZStd::array All; + static constexpr inline AZStd::array All + { + EditClear, + EditEnter, + NavigationBack + }; }; //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/CMakeLists.txt b/Code/Framework/AzFramework/CMakeLists.txt index 6f6e47855e..f6851f0ab8 100644 --- a/Code/Framework/AzFramework/CMakeLists.txt +++ b/Code/Framework/AzFramework/CMakeLists.txt @@ -95,4 +95,4 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) endif() -endif() \ No newline at end of file +endif() diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp similarity index 54% rename from Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.cpp rename to Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp index aaab67b2a1..7cbd9ae0e2 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp @@ -6,29 +6,26 @@ * */ -#include -#include -#include "Application_Linux_xcb.h" +#include +#include -//////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { -#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB //////////////////////////////////////////////////////////////////////////////////////////////// - class LinuxXcbConnectionManagerImpl - : public LinuxXcbConnectionManagerBus::Handler + class XcbConnectionManagerImpl + : public XcbConnectionManagerBus::Handler { public: - LinuxXcbConnectionManagerImpl() + XcbConnectionManagerImpl() { m_xcbConnection = xcb_connect(nullptr, nullptr); - AZ_Error("ApplicationLinux", m_xcbConnection != nullptr, "Unable to connect to X11 Server."); - LinuxXcbConnectionManagerBus::Handler::BusConnect(); + AZ_Error("Application", m_xcbConnection != nullptr, "Unable to connect to X11 Server."); + XcbConnectionManagerBus::Handler::BusConnect(); } - ~LinuxXcbConnectionManagerImpl() + ~XcbConnectionManagerImpl() override { - LinuxXcbConnectionManagerBus::Handler::BusDisconnect(); + XcbConnectionManagerBus::Handler::BusDisconnect(); xcb_disconnect(m_xcbConnection); } @@ -42,53 +39,51 @@ namespace AzFramework }; //////////////////////////////////////////////////////////////////////////////////////////////// - ApplicationLinux_xcb::ApplicationLinux_xcb() + XcbApplication::XcbApplication() { LinuxLifecycleEvents::Bus::Handler::BusConnect(); - m_xcbConnectionManager = AZStd::make_unique(); - if (LinuxXcbConnectionManagerInterface::Get() == nullptr) + m_xcbConnectionManager = AZStd::make_unique(); + if (XcbConnectionManagerInterface::Get() == nullptr) { - LinuxXcbConnectionManagerInterface::Register(m_xcbConnectionManager.get()); + XcbConnectionManagerInterface::Register(m_xcbConnectionManager.get()); } } //////////////////////////////////////////////////////////////////////////////////////////////// - ApplicationLinux_xcb::~ApplicationLinux_xcb() + XcbApplication::~XcbApplication() { - if (LinuxXcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get()) + if (XcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get()) { - LinuxXcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get()); + XcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get()); } m_xcbConnectionManager.reset(); LinuxLifecycleEvents::Bus::Handler::BusDisconnect(); } //////////////////////////////////////////////////////////////////////////////////////////////// - void ApplicationLinux_xcb::PumpSystemEventLoopOnce() + void XcbApplication::PumpSystemEventLoopOnce() { if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection()) { if (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection)) { - LinuxXcbEventHandlerBus::Broadcast(&LinuxXcbEventHandlerBus::Events::HandleXcbEvent, event); + XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event); free(event); } } } //////////////////////////////////////////////////////////////////////////////////////////////// - void ApplicationLinux_xcb::PumpSystemEventLoopUntilEmpty() + void XcbApplication::PumpSystemEventLoopUntilEmpty() { if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection()) { while (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection)) { - LinuxXcbEventHandlerBus::Broadcast(&LinuxXcbEventHandlerBus::Events::HandleXcbEvent, event); + XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event); free(event); } } } -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.h similarity index 61% rename from Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h rename to Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.h index fbd7f165d3..f27b5dd680 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux_xcb.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.h @@ -5,27 +5,24 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once #include #include +#include -//////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { - -#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - - //////////////////////////////////////////////////////////////////////////////////////////////// - class ApplicationLinux_xcb + class XcbApplication : public Application::Implementation , public LinuxLifecycleEvents::Bus::Handler { public: //////////////////////////////////////////////////////////////////////////////////////////// - AZ_CLASS_ALLOCATOR(ApplicationLinux_xcb, AZ::SystemAllocator, 0); - ApplicationLinux_xcb(); - ~ApplicationLinux_xcb() override; + AZ_CLASS_ALLOCATOR(XcbApplication, AZ::SystemAllocator, 0); + XcbApplication(); + ~XcbApplication() override; //////////////////////////////////////////////////////////////////////////////////////////// // Application::Implementation @@ -33,9 +30,6 @@ namespace AzFramework void PumpSystemEventLoopUntilEmpty() override; private: - AZStd::unique_ptr m_xcbConnectionManager; + AZStd::unique_ptr m_xcbConnectionManager; }; - -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h new file mode 100644 index 0000000000..daa5bf35af --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +#include + +namespace AzFramework +{ + class XcbConnectionManager + { + public: + AZ_RTTI(XcbConnectionManager, "{1F756E14-8D74-42FD-843C-4863307710DB}"); + + virtual ~XcbConnectionManager() = default; + + virtual xcb_connection_t* GetXcbConnection() const = 0; + }; + + class XcbConnectionManagerBusTraits + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + }; + + using XcbConnectionManagerBus = AZ::EBus; + using XcbConnectionManagerInterface = AZ::Interface; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h new file mode 100644 index 0000000000..c6307755a7 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h @@ -0,0 +1,40 @@ +/* + * 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 + +namespace AzFramework +{ + class XcbEventHandler + { + public: + AZ_RTTI(XcbEventHandler, "{3F756E14-8D74-42FD-843C-4863307710DB}"); + + virtual ~XcbEventHandler() = default; + + virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0; + }; + + class XcbEventHandlerBusTraits + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + }; + + using XcbEventHandlerBus = AZ::EBus; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp new file mode 100644 index 0000000000..c971c3b793 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.cpp @@ -0,0 +1,271 @@ +/* + * 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 + +#define explicit ExplicitIsACXXKeyword +#include +#undef explicit +#include +#include +#include + +namespace AzFramework +{ + XcbInputDeviceKeyboard::XcbInputDeviceKeyboard(InputDeviceKeyboard& inputDevice) + : InputDeviceKeyboard::Implementation(inputDevice) + { + XcbEventHandlerBus::Handler::BusConnect(); + + auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); + if (!interface) + { + AZ_Warning("ApplicationLinux", false, "XCB interface not available"); + return; + } + + auto* connection = interface->GetXcbConnection(); + if (!connection) + { + AZ_Warning("ApplicationLinux", false, "XCB connection not available"); + return; + } + + XcbStdFreePtr xkbUseExtensionReply{ + xcb_xkb_use_extension_reply(connection, xcb_xkb_use_extension(connection, 1, 0), nullptr) + }; + if (!xkbUseExtensionReply) + { + AZ_Warning("ApplicationLinux", false, "Failed to initialize the xkb extension"); + return; + } + if (!xkbUseExtensionReply->supported) + { + AZ_Warning("ApplicationLinux", false, "The X server does not support the xkb extension"); + return; + } + + m_coreDeviceId = xkb_x11_get_core_keyboard_device_id(connection); + + m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS)); + m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), connection, m_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS)); + m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), connection, m_coreDeviceId)); + + m_initialized = true; + } + + bool XcbInputDeviceKeyboard::IsConnected() const + { + auto* connection = AzFramework::XcbConnectionManagerInterface::Get()->GetXcbConnection(); + return connection && !xcb_connection_has_error(connection); + } + + bool XcbInputDeviceKeyboard::HasTextEntryStarted() const + { + return false; + } + + void XcbInputDeviceKeyboard::TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options) + { + } + + void XcbInputDeviceKeyboard::TextEntryStop() + { + } + + void XcbInputDeviceKeyboard::TickInputDevice() + { + ProcessRawEventQueues(); + } + + void XcbInputDeviceKeyboard::HandleXcbEvent(xcb_generic_event_t* event) + { + if (!m_initialized) + { + return; + } + + switch (event->response_type & ~0x80) + { + case XCB_KEY_PRESS: + { + auto* keyPress = reinterpret_cast(event); + + const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail); + if (key) + { + QueueRawKeyEvent(*key, true); + } + break; + } + case XCB_KEY_RELEASE: + { + auto* keyRelease = reinterpret_cast(event); + + const InputChannelId* key = InputChannelFromKeyEvent(keyRelease->detail); + if (key) + { + QueueRawKeyEvent(*key, false); + } + break; + } + } + } + + [[nodiscard]] const InputChannelId* XcbInputDeviceKeyboard::InputChannelFromKeyEvent(xcb_keycode_t code) const + { + const xcb_keysym_t keysym = xkb_state_key_get_one_sym(m_xkbState.get(), code); + + switch(keysym) + { + case XKB_KEY_0: return &InputDeviceKeyboard::Key::Alphanumeric0; + case XKB_KEY_1: return &InputDeviceKeyboard::Key::Alphanumeric1; + case XKB_KEY_2: return &InputDeviceKeyboard::Key::Alphanumeric2; + case XKB_KEY_3: return &InputDeviceKeyboard::Key::Alphanumeric3; + case XKB_KEY_4: return &InputDeviceKeyboard::Key::Alphanumeric4; + case XKB_KEY_5: return &InputDeviceKeyboard::Key::Alphanumeric5; + case XKB_KEY_6: return &InputDeviceKeyboard::Key::Alphanumeric6; + case XKB_KEY_7: return &InputDeviceKeyboard::Key::Alphanumeric7; + case XKB_KEY_8: return &InputDeviceKeyboard::Key::Alphanumeric8; + case XKB_KEY_9: return &InputDeviceKeyboard::Key::Alphanumeric9; + case XKB_KEY_A: + case XKB_KEY_a: return &InputDeviceKeyboard::Key::AlphanumericA; + case XKB_KEY_B: + case XKB_KEY_b: return &InputDeviceKeyboard::Key::AlphanumericB; + case XKB_KEY_C: + case XKB_KEY_c: return &InputDeviceKeyboard::Key::AlphanumericC; + case XKB_KEY_D: + case XKB_KEY_d: return &InputDeviceKeyboard::Key::AlphanumericD; + case XKB_KEY_E: + case XKB_KEY_e: return &InputDeviceKeyboard::Key::AlphanumericE; + case XKB_KEY_F: + case XKB_KEY_f: return &InputDeviceKeyboard::Key::AlphanumericF; + case XKB_KEY_G: + case XKB_KEY_g: return &InputDeviceKeyboard::Key::AlphanumericG; + case XKB_KEY_H: + case XKB_KEY_h: return &InputDeviceKeyboard::Key::AlphanumericH; + case XKB_KEY_I: + case XKB_KEY_i: return &InputDeviceKeyboard::Key::AlphanumericI; + case XKB_KEY_J: + case XKB_KEY_j: return &InputDeviceKeyboard::Key::AlphanumericJ; + case XKB_KEY_K: + case XKB_KEY_k: return &InputDeviceKeyboard::Key::AlphanumericK; + case XKB_KEY_L: + case XKB_KEY_l: return &InputDeviceKeyboard::Key::AlphanumericL; + case XKB_KEY_M: + case XKB_KEY_m: return &InputDeviceKeyboard::Key::AlphanumericM; + case XKB_KEY_N: + case XKB_KEY_n: return &InputDeviceKeyboard::Key::AlphanumericN; + case XKB_KEY_O: + case XKB_KEY_o: return &InputDeviceKeyboard::Key::AlphanumericO; + case XKB_KEY_P: + case XKB_KEY_p: return &InputDeviceKeyboard::Key::AlphanumericP; + case XKB_KEY_Q: + case XKB_KEY_q: return &InputDeviceKeyboard::Key::AlphanumericQ; + case XKB_KEY_R: + case XKB_KEY_r: return &InputDeviceKeyboard::Key::AlphanumericR; + case XKB_KEY_S: + case XKB_KEY_s: return &InputDeviceKeyboard::Key::AlphanumericS; + case XKB_KEY_T: + case XKB_KEY_t: return &InputDeviceKeyboard::Key::AlphanumericT; + case XKB_KEY_U: + case XKB_KEY_u: return &InputDeviceKeyboard::Key::AlphanumericU; + case XKB_KEY_V: + case XKB_KEY_v: return &InputDeviceKeyboard::Key::AlphanumericV; + case XKB_KEY_W: + case XKB_KEY_w: return &InputDeviceKeyboard::Key::AlphanumericW; + case XKB_KEY_X: + case XKB_KEY_x: return &InputDeviceKeyboard::Key::AlphanumericX; + case XKB_KEY_Y: + case XKB_KEY_y: return &InputDeviceKeyboard::Key::AlphanumericY; + case XKB_KEY_Z: + case XKB_KEY_z: return &InputDeviceKeyboard::Key::AlphanumericZ; + case XKB_KEY_BackSpace: return &InputDeviceKeyboard::Key::EditBackspace; + case XKB_KEY_Caps_Lock: return &InputDeviceKeyboard::Key::EditCapsLock; + case XKB_KEY_Return: return &InputDeviceKeyboard::Key::EditEnter; + case XKB_KEY_space: return &InputDeviceKeyboard::Key::EditSpace; + case XKB_KEY_Tab: return &InputDeviceKeyboard::Key::EditTab; + case XKB_KEY_Escape: return &InputDeviceKeyboard::Key::Escape; + case XKB_KEY_F1: return &InputDeviceKeyboard::Key::Function01; + case XKB_KEY_F2: return &InputDeviceKeyboard::Key::Function02; + case XKB_KEY_F3: return &InputDeviceKeyboard::Key::Function03; + case XKB_KEY_F4: return &InputDeviceKeyboard::Key::Function04; + case XKB_KEY_F5: return &InputDeviceKeyboard::Key::Function05; + case XKB_KEY_F6: return &InputDeviceKeyboard::Key::Function06; + case XKB_KEY_F7: return &InputDeviceKeyboard::Key::Function07; + case XKB_KEY_F8: return &InputDeviceKeyboard::Key::Function08; + case XKB_KEY_F9: return &InputDeviceKeyboard::Key::Function09; + case XKB_KEY_F10: return &InputDeviceKeyboard::Key::Function10; + case XKB_KEY_F11: return &InputDeviceKeyboard::Key::Function11; + case XKB_KEY_F12: return &InputDeviceKeyboard::Key::Function12; + case XKB_KEY_F13: return &InputDeviceKeyboard::Key::Function13; + case XKB_KEY_F14: return &InputDeviceKeyboard::Key::Function14; + case XKB_KEY_F15: return &InputDeviceKeyboard::Key::Function15; + case XKB_KEY_F16: return &InputDeviceKeyboard::Key::Function16; + case XKB_KEY_F17: return &InputDeviceKeyboard::Key::Function17; + case XKB_KEY_F18: return &InputDeviceKeyboard::Key::Function18; + case XKB_KEY_F19: return &InputDeviceKeyboard::Key::Function19; + case XKB_KEY_F20: return &InputDeviceKeyboard::Key::Function20; + case XKB_KEY_Alt_L: return &InputDeviceKeyboard::Key::ModifierAltL; + case XKB_KEY_Alt_R: return &InputDeviceKeyboard::Key::ModifierAltR; + case XKB_KEY_Control_L: return &InputDeviceKeyboard::Key::ModifierCtrlL; + case XKB_KEY_Control_R: return &InputDeviceKeyboard::Key::ModifierCtrlR; + case XKB_KEY_Shift_L: return &InputDeviceKeyboard::Key::ModifierShiftL; + case XKB_KEY_Shift_R: return &InputDeviceKeyboard::Key::ModifierShiftR; + case XKB_KEY_Super_L: return &InputDeviceKeyboard::Key::ModifierSuperL; + case XKB_KEY_Super_R: return &InputDeviceKeyboard::Key::ModifierSuperR; + case XKB_KEY_Down: return &InputDeviceKeyboard::Key::NavigationArrowDown; + case XKB_KEY_Left: return &InputDeviceKeyboard::Key::NavigationArrowLeft; + case XKB_KEY_Right: return &InputDeviceKeyboard::Key::NavigationArrowRight; + case XKB_KEY_Up: return &InputDeviceKeyboard::Key::NavigationArrowUp; + case XKB_KEY_Delete: return &InputDeviceKeyboard::Key::NavigationDelete; + case XKB_KEY_End: return &InputDeviceKeyboard::Key::NavigationEnd; + case XKB_KEY_Home: return &InputDeviceKeyboard::Key::NavigationHome; + case XKB_KEY_Insert: return &InputDeviceKeyboard::Key::NavigationInsert; + case XKB_KEY_Page_Down: return &InputDeviceKeyboard::Key::NavigationPageDown; + case XKB_KEY_Page_Up: return &InputDeviceKeyboard::Key::NavigationPageUp; + case XKB_KEY_Num_Lock: return &InputDeviceKeyboard::Key::NumLock; + case XKB_KEY_KP_0: return &InputDeviceKeyboard::Key::NumPad0; + case XKB_KEY_KP_1: return &InputDeviceKeyboard::Key::NumPad1; + case XKB_KEY_KP_2: return &InputDeviceKeyboard::Key::NumPad2; + case XKB_KEY_KP_3: return &InputDeviceKeyboard::Key::NumPad3; + case XKB_KEY_KP_4: return &InputDeviceKeyboard::Key::NumPad4; + case XKB_KEY_KP_5: return &InputDeviceKeyboard::Key::NumPad5; + case XKB_KEY_KP_6: return &InputDeviceKeyboard::Key::NumPad6; + case XKB_KEY_KP_7: return &InputDeviceKeyboard::Key::NumPad7; + case XKB_KEY_KP_8: return &InputDeviceKeyboard::Key::NumPad8; + case XKB_KEY_KP_9: return &InputDeviceKeyboard::Key::NumPad9; + case XKB_KEY_KP_Add: return &InputDeviceKeyboard::Key::NumPadAdd; + case XKB_KEY_KP_Decimal: return &InputDeviceKeyboard::Key::NumPadDecimal; + case XKB_KEY_KP_Divide: return &InputDeviceKeyboard::Key::NumPadDivide; + case XKB_KEY_KP_Enter: return &InputDeviceKeyboard::Key::NumPadEnter; + case XKB_KEY_KP_Multiply: return &InputDeviceKeyboard::Key::NumPadMultiply; + case XKB_KEY_KP_Subtract: return &InputDeviceKeyboard::Key::NumPadSubtract; + case XKB_KEY_apostrophe: return &InputDeviceKeyboard::Key::PunctuationApostrophe; + case XKB_KEY_backslash: return &InputDeviceKeyboard::Key::PunctuationBackslash; + case XKB_KEY_bracketleft: return &InputDeviceKeyboard::Key::PunctuationBracketL; + case XKB_KEY_bracketright: return &InputDeviceKeyboard::Key::PunctuationBracketR; + case XKB_KEY_comma: return &InputDeviceKeyboard::Key::PunctuationComma; + case XKB_KEY_equal: return &InputDeviceKeyboard::Key::PunctuationEquals; + case XKB_KEY_hyphen: return &InputDeviceKeyboard::Key::PunctuationHyphen; + case XKB_KEY_period: return &InputDeviceKeyboard::Key::PunctuationPeriod; + case XKB_KEY_semicolon: return &InputDeviceKeyboard::Key::PunctuationSemicolon; + case XKB_KEY_slash: return &InputDeviceKeyboard::Key::PunctuationSlash; + case XKB_KEY_grave: + case XKB_KEY_asciitilde: return &InputDeviceKeyboard::Key::PunctuationTilde; + case XKB_KEY_ISO_Group_Shift: return &InputDeviceKeyboard::Key::SupplementaryISO; + case XKB_KEY_Pause: return &InputDeviceKeyboard::Key::WindowsSystemPause; + case XKB_KEY_Print: return &InputDeviceKeyboard::Key::WindowsSystemPrint; + case XKB_KEY_Scroll_Lock: return &InputDeviceKeyboard::Key::WindowsSystemScrollLock; + default: return nullptr; + } + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h new file mode 100644 index 0000000000..de65941a67 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceKeyboard.h @@ -0,0 +1,46 @@ +/* + * 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 + +namespace AzFramework +{ + class XcbInputDeviceKeyboard + : public InputDeviceKeyboard::Implementation + , public XcbEventHandlerBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(XcbInputDeviceKeyboard, AZ::SystemAllocator, 0); + + using InputDeviceKeyboard::Implementation::Implementation; + XcbInputDeviceKeyboard(InputDeviceKeyboard& inputDevice); + + bool IsConnected() const override; + + bool HasTextEntryStarted() const override; + void TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options) override; + void TextEntryStop() override; + void TickInputDevice() override; + + void HandleXcbEvent(xcb_generic_event_t* event) override; + + private: + [[nodiscard]] const InputChannelId* InputChannelFromKeyEvent(xcb_keycode_t code) const; + + XcbUniquePtr m_xkbContext; + XcbUniquePtr m_xkbKeymap; + XcbUniquePtr m_xkbState; + int m_coreDeviceId{-1}; + bool m_initialized{false}; + }; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInterface.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInterface.h new file mode 100644 index 0000000000..838048b174 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInterface.h @@ -0,0 +1,38 @@ +/* + * 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 AzFramework +{ + // @brief Wrap a function pointer in a type + // This serves as a convenient way to wrap a function pointer in a given + // type. That type can then be used in a `unique_ptr` or `shared_ptr`. + // Using a type instead of a function pointer by value prevents the need to + // copy the pointer when copying the smart poiner. + template + struct XcbDeleterFreeFunctionWrapper + { + using value_type = decltype(Callable); + static constexpr value_type s_value = Callable; + constexpr operator value_type() const noexcept + { + return s_value; + } + }; + + template + using XcbUniquePtr = AZStd::unique_ptr>; + + template + using XcbStdFreePtr = XcbUniquePtr; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp similarity index 85% rename from Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.cpp rename to Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp index cfc0222214..6ab97fd616 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp @@ -6,41 +6,37 @@ * */ -#include #include #include -#include +#include +#include -#include "NativeWindow_Linux_xcb.h" +#include namespace AzFramework { -#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - - [[maybe_unused]] const char LinuxXcbErrorWindow[] = "NativeWindow_Linux_xcb"; + [[maybe_unused]] const char XcbErrorWindow[] = "XcbNativeWindow"; static constexpr uint8_t s_XcbFormatDataSize = 32; // Format indicator for xcb for client messages static constexpr uint16_t s_DefaultXcbWindowBorderWidth = 4; // The default border with in pixels if a border was specified static constexpr uint8_t s_XcbResponseTypeMask = 0x7f; // Mask to extract the specific event type from an xcb event //////////////////////////////////////////////////////////////////////////////////////////////// - NativeWindowImpl_Linux_xcb::NativeWindowImpl_Linux_xcb() + XcbNativeWindow::XcbNativeWindow() : NativeWindow::Implementation() { - if (auto xcbConnectionManager = AzFramework::LinuxXcbConnectionManagerInterface::Get(); + if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get(); xcbConnectionManager != nullptr) { m_xcbConnection = xcbConnectionManager->GetXcbConnection(); } - AZ_Error(LinuxXcbErrorWindow, m_xcbConnection != nullptr, "Unable to get XCB Connection"); + AZ_Error(XcbErrorWindow, m_xcbConnection != nullptr, "Unable to get XCB Connection"); } //////////////////////////////////////////////////////////////////////////////////////////////// - NativeWindowImpl_Linux_xcb::~NativeWindowImpl_Linux_xcb() - { - } + XcbNativeWindow::~XcbNativeWindow() = default; //////////////////////////////////////////////////////////////////////////////////////////////// - void NativeWindowImpl_Linux_xcb::InitWindow(const AZStd::string& title, + void XcbNativeWindow::InitWindow(const AZStd::string& title, const WindowGeometry& geometry, const WindowStyleMasks& styleMasks) { @@ -98,13 +94,13 @@ namespace AzFramework xcb_intern_atom_cookie_t cookieProtocol = xcb_intern_atom(m_xcbConnection, 1, strlen(wmProtocolString), wmProtocolString); xcb_intern_atom_reply_t* replyProtocol = xcb_intern_atom_reply(m_xcbConnection, cookieProtocol, nullptr); - AZ_Error(LinuxXcbErrorWindow, replyProtocol != nullptr, "Unable to query xcb '%s' atom", wmProtocolString); + AZ_Error(XcbErrorWindow, replyProtocol != nullptr, "Unable to query xcb '%s' atom", wmProtocolString); m_xcbAtomProtocols = replyProtocol->atom; const static char* wmDeleteWindowString = "WM_DELETE_WINDOW"; xcb_intern_atom_cookie_t cookieDeleteWindow = xcb_intern_atom(m_xcbConnection, 0, strlen(wmDeleteWindowString), wmDeleteWindowString); xcb_intern_atom_reply_t* replyDeleteWindow = xcb_intern_atom_reply(m_xcbConnection, cookieDeleteWindow, nullptr); - AZ_Error(LinuxXcbErrorWindow, replyDeleteWindow != nullptr, "Unable to query xcb '%s' atom", wmDeleteWindowString); + AZ_Error(XcbErrorWindow, replyDeleteWindow != nullptr, "Unable to query xcb '%s' atom", wmDeleteWindowString); m_xcbAtomDeleteWindow = replyDeleteWindow->atom; xcbCheckResult = xcb_change_property_checked(m_xcbConnection, @@ -123,9 +119,9 @@ namespace AzFramework } //////////////////////////////////////////////////////////////////////////////////////////////// - void NativeWindowImpl_Linux_xcb::Activate() + void XcbNativeWindow::Activate() { - LinuxXcbEventHandlerBus::Handler::BusConnect(); + XcbEventHandlerBus::Handler::BusConnect(); if (!m_activated) // nothing to do if window was already activated { @@ -137,7 +133,7 @@ namespace AzFramework } //////////////////////////////////////////////////////////////////////////////////////////////// - void NativeWindowImpl_Linux_xcb::Deactivate() + void XcbNativeWindow::Deactivate() { if (m_activated) // nothing to do if window was already deactivated { @@ -148,17 +144,17 @@ namespace AzFramework xcb_unmap_window(m_xcbConnection, m_xcbWindow); xcb_flush(m_xcbConnection); } - LinuxXcbEventHandlerBus::Handler::BusDisconnect(); + XcbEventHandlerBus::Handler::BusDisconnect(); } //////////////////////////////////////////////////////////////////////////////////////////////// - NativeWindowHandle NativeWindowImpl_Linux_xcb::GetWindowHandle() const + NativeWindowHandle XcbNativeWindow::GetWindowHandle() const { return reinterpret_cast(m_xcbWindow); } //////////////////////////////////////////////////////////////////////////////////////////////// - void NativeWindowImpl_Linux_xcb::SetWindowTitle(const AZStd::string& title) + void XcbNativeWindow::SetWindowTitle(const AZStd::string& title) { xcb_void_cookie_t xcbCheckResult; xcbCheckResult = xcb_change_property(m_xcbConnection, @@ -173,7 +169,7 @@ namespace AzFramework } //////////////////////////////////////////////////////////////////////////////////////////////// - void NativeWindowImpl_Linux_xcb::ResizeClientArea(WindowSize clientAreaSize) + void XcbNativeWindow::ResizeClientArea(WindowSize clientAreaSize) { const uint32_t values[] = { clientAreaSize.m_width, clientAreaSize.m_height }; @@ -184,7 +180,7 @@ namespace AzFramework } //////////////////////////////////////////////////////////////////////////////////////////////// - uint32_t NativeWindowImpl_Linux_xcb::GetDisplayRefreshRate() const + uint32_t XcbNativeWindow::GetDisplayRefreshRate() const { // [GFX TODO][GHI - 2678] // Using 60 for now until proper support is added @@ -192,7 +188,7 @@ namespace AzFramework } //////////////////////////////////////////////////////////////////////////////////////////////// - bool NativeWindowImpl_Linux_xcb::ValidateXcbResult(xcb_void_cookie_t cookie) + bool XcbNativeWindow::ValidateXcbResult(xcb_void_cookie_t cookie) { bool result = true; if (xcb_generic_error_t* error = xcb_request_check(m_xcbConnection, cookie)) @@ -204,7 +200,7 @@ namespace AzFramework } //////////////////////////////////////////////////////////////////////////////////////////////// - void NativeWindowImpl_Linux_xcb::HandleXcbEvent(xcb_generic_event_t* event) + void XcbNativeWindow::HandleXcbEvent(xcb_generic_event_t* event) { switch (event->response_type & s_XcbResponseTypeMask) { @@ -233,7 +229,7 @@ namespace AzFramework } //////////////////////////////////////////////////////////////////////////////////////////////// - void NativeWindowImpl_Linux_xcb::WindowSizeChanged(const uint32_t width, const uint32_t height) + void XcbNativeWindow::WindowSizeChanged(const uint32_t width, const uint32_t height) { if (m_width != width || m_height != height) { @@ -246,7 +242,4 @@ namespace AzFramework } } } - -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.h similarity index 78% rename from Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h rename to Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.h index 40181395e4..36737e24e8 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux_xcb.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.h @@ -5,24 +5,25 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once -#include #include #include +#include + #include namespace AzFramework { -#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - class NativeWindowImpl_Linux_xcb final + class XcbNativeWindow final : public NativeWindow::Implementation - , public LinuxXcbEventHandlerBus::Handler + , public XcbEventHandlerBus::Handler { public: - AZ_CLASS_ALLOCATOR(NativeWindowImpl_Linux_xcb, AZ::SystemAllocator, 0); - NativeWindowImpl_Linux_xcb(); - ~NativeWindowImpl_Linux_xcb() override; + AZ_CLASS_ALLOCATOR(XcbNativeWindow, AZ::SystemAllocator, 0); + XcbNativeWindow(); + ~XcbNativeWindow() override; //////////////////////////////////////////////////////////////////////////////////////////// // NativeWindow::Implementation @@ -37,7 +38,7 @@ namespace AzFramework uint32_t GetDisplayRefreshRate() const override; //////////////////////////////////////////////////////////////////////////////////////////// - // LinuxXcbEventHandlerBus::Handler + // XcbEventHandlerBus::Handler void HandleXcbEvent(xcb_generic_event_t* event) override; private: @@ -49,6 +50,4 @@ namespace AzFramework xcb_atom_t m_xcbAtomProtocols; xcb_atom_t m_xcbAtomDeleteWindow; }; -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/azframework_xcb_files.cmake b/Code/Framework/AzFramework/Platform/Common/Xcb/azframework_xcb_files.cmake new file mode 100644 index 0000000000..68de710fa1 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/azframework_xcb_files.cmake @@ -0,0 +1,18 @@ +# +# 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 +# +# + +set(FILES + AzFramework/XcbApplication.cpp + AzFramework/XcbApplication.h + AzFramework/XcbConnectionManager.h + AzFramework/XcbInputDeviceKeyboard.cpp + AzFramework/XcbInputDeviceKeyboard.h + AzFramework/XcbInterface.h + AzFramework/XcbNativeWindow.cpp + AzFramework/XcbNativeWindow.h +) diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h b/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h index 833f4019f8..adf3ee0bdd 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h @@ -12,10 +12,6 @@ #include #include -#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB -#include -#endif // LY_COMPILE_DEFINITIONS - namespace AzFramework { class LinuxLifecycleEvents @@ -30,54 +26,4 @@ namespace AzFramework using Bus = AZ::EBus; }; - -#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - class LinuxXcbConnectionManager - { - public: - AZ_RTTI(LinuxXcbConnectionManager, "{1F756E14-8D74-42FD-843C-4863307710DB}"); - - virtual ~LinuxXcbConnectionManager() = default; - - virtual xcb_connection_t* GetXcbConnection() const = 0; - }; - - class LinuxXcbConnectionManagerBusTraits - : public AZ::EBusTraits - { - public: - ////////////////////////////////////////////////////////////////////////// - // EBusTraits overrides - static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; - ////////////////////////////////////////////////////////////////////////// - }; - - using LinuxXcbConnectionManagerBus = AZ::EBus; - using LinuxXcbConnectionManagerInterface = AZ::Interface; - - class LinuxXcbEventHandler - { - public: - AZ_RTTI(LinuxXcbEventHandler, "{3F756E14-8D74-42FD-843C-4863307710DB}"); - - virtual ~LinuxXcbEventHandler() = default; - - virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0; - }; - - class LinuxXcbEventHandlerBusTraits - : public AZ::EBusTraits - { - public: - ////////////////////////////////////////////////////////////////////////// - // EBusTraits overrides - static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; - static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; - ////////////////////////////////////////////////////////////////////////// - }; - - using LinuxXcbEventHandlerBus = AZ::EBus; - -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp index 59602fe788..158240210e 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp @@ -8,7 +8,9 @@ #include -#include "Application_Linux_xcb.h" +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB +#include +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework @@ -17,7 +19,7 @@ namespace AzFramework Application::Implementation* Application::Implementation::Create() { #if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - return aznew ApplicationLinux_xcb(); + return aznew XcbApplication(); #elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND #error "Linux Window Manager Wayland not supported." return nullptr; diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp new file mode 100644 index 0000000000..d1f90c3052 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp @@ -0,0 +1,27 @@ +/* + * 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 + * + */ + +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB +#include +#endif + +namespace AzFramework +{ + InputDeviceKeyboard::Implementation* InputDeviceKeyboard::Implementation::Create(InputDeviceKeyboard& inputDevice) + { +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + return aznew XcbInputDeviceKeyboard(inputDevice); +#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND + #error "Linux Window Manager Wayland not supported." + return nullptr; +#else + #error "Linux Window Manager not recognized." + return nullptr; +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_xcb.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_xcb.cpp deleted file mode 100644 index 8ff4c72d42..0000000000 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_xcb.cpp +++ /dev/null @@ -1,293 +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 -#include - -#define explicit ExplicitIsACXXKeyword -#include -#undef explicit -#include -#include -#include - -namespace AzFramework -{ - class InputDeviceKeyboardXcb - : public InputDeviceKeyboard::Implementation - , public LinuxXcbEventHandlerBus::Handler - { - public: - AZ_CLASS_ALLOCATOR(InputDeviceKeyboardXcb, AZ::SystemAllocator, 0); - - using InputDeviceKeyboard::Implementation::Implementation; - InputDeviceKeyboardXcb(InputDeviceKeyboard& inputDevice) - : InputDeviceKeyboard::Implementation(inputDevice) - { - LinuxXcbEventHandlerBus::Handler::BusConnect(); - - auto* interface = AzFramework::LinuxXcbConnectionManagerInterface::Get(); - if (!interface) - { - AZ_Warning("ApplicationLinux", false, "XCB interface not available"); - return; - } - - auto* connection = AzFramework::LinuxXcbConnectionManagerInterface::Get()->GetXcbConnection(); - if (!connection) - { - AZ_Warning("ApplicationLinux", false, "XCB connection not available"); - return; - } - - AZStd::unique_ptr> xkbUseExtensionReply{ - xcb_xkb_use_extension_reply(connection, xcb_xkb_use_extension(connection, 1, 0), nullptr) - }; - if (!xkbUseExtensionReply) - { - AZ_Warning("ApplicationLinux", false, "Failed to initialize the xkb extension"); - return; - } - if (!xkbUseExtensionReply->supported) - { - AZ_Warning("ApplicationLinux", false, "The X server does not support the xkb extension"); - return; - } - - m_coreDeviceId = xkb_x11_get_core_keyboard_device_id(connection); - - m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS)); - m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), connection, m_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS)); - m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), connection, m_coreDeviceId)); - - m_initialized = true; - } - - bool IsConnected() const override - { - return m_initialized; - } - - bool HasTextEntryStarted() const override - { - return false; - } - - void TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options) override - { - } - - void TextEntryStop() override - { - } - - void TickInputDevice() override - { - ProcessRawEventQueues(); - } - - void HandleXcbEvent(xcb_generic_event_t* event) override - { - if (!IsConnected()) - { - return; - } - - switch (event->response_type & ~0x80) - { - case XCB_KEY_PRESS: - { - auto* keyPress = reinterpret_cast(event); - - const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail); - if (key) - { - QueueRawKeyEvent(*key, true); - } - break; - } - case XCB_KEY_RELEASE: - { - auto* keyRelease = reinterpret_cast(event); - - const InputChannelId* key = InputChannelFromKeyEvent(keyRelease->detail); - if (key) - { - QueueRawKeyEvent(*key, false); - } - break; - } - } - } - - private: - [[nodiscard]] const InputChannelId* InputChannelFromKeyEvent(xcb_keycode_t code) const - { - const xcb_keysym_t keysym = xkb_state_key_get_one_sym(m_xkbState.get(), code); - - switch(keysym) - { - case XKB_KEY_0: return &InputDeviceKeyboard::Key::Alphanumeric0; - case XKB_KEY_1: return &InputDeviceKeyboard::Key::Alphanumeric1; - case XKB_KEY_2: return &InputDeviceKeyboard::Key::Alphanumeric2; - case XKB_KEY_3: return &InputDeviceKeyboard::Key::Alphanumeric3; - case XKB_KEY_4: return &InputDeviceKeyboard::Key::Alphanumeric4; - case XKB_KEY_5: return &InputDeviceKeyboard::Key::Alphanumeric5; - case XKB_KEY_6: return &InputDeviceKeyboard::Key::Alphanumeric6; - case XKB_KEY_7: return &InputDeviceKeyboard::Key::Alphanumeric7; - case XKB_KEY_8: return &InputDeviceKeyboard::Key::Alphanumeric8; - case XKB_KEY_9: return &InputDeviceKeyboard::Key::Alphanumeric9; - case XKB_KEY_A: - case XKB_KEY_a: return &InputDeviceKeyboard::Key::AlphanumericA; - case XKB_KEY_B: - case XKB_KEY_b: return &InputDeviceKeyboard::Key::AlphanumericB; - case XKB_KEY_C: - case XKB_KEY_c: return &InputDeviceKeyboard::Key::AlphanumericC; - case XKB_KEY_D: - case XKB_KEY_d: return &InputDeviceKeyboard::Key::AlphanumericD; - case XKB_KEY_E: - case XKB_KEY_e: return &InputDeviceKeyboard::Key::AlphanumericE; - case XKB_KEY_F: - case XKB_KEY_f: return &InputDeviceKeyboard::Key::AlphanumericF; - case XKB_KEY_G: - case XKB_KEY_g: return &InputDeviceKeyboard::Key::AlphanumericG; - case XKB_KEY_H: - case XKB_KEY_h: return &InputDeviceKeyboard::Key::AlphanumericH; - case XKB_KEY_I: - case XKB_KEY_i: return &InputDeviceKeyboard::Key::AlphanumericI; - case XKB_KEY_J: - case XKB_KEY_j: return &InputDeviceKeyboard::Key::AlphanumericJ; - case XKB_KEY_K: - case XKB_KEY_k: return &InputDeviceKeyboard::Key::AlphanumericK; - case XKB_KEY_L: - case XKB_KEY_l: return &InputDeviceKeyboard::Key::AlphanumericL; - case XKB_KEY_M: - case XKB_KEY_m: return &InputDeviceKeyboard::Key::AlphanumericM; - case XKB_KEY_N: - case XKB_KEY_n: return &InputDeviceKeyboard::Key::AlphanumericN; - case XKB_KEY_O: - case XKB_KEY_o: return &InputDeviceKeyboard::Key::AlphanumericO; - case XKB_KEY_P: - case XKB_KEY_p: return &InputDeviceKeyboard::Key::AlphanumericP; - case XKB_KEY_Q: - case XKB_KEY_q: return &InputDeviceKeyboard::Key::AlphanumericQ; - case XKB_KEY_R: - case XKB_KEY_r: return &InputDeviceKeyboard::Key::AlphanumericR; - case XKB_KEY_S: - case XKB_KEY_s: return &InputDeviceKeyboard::Key::AlphanumericS; - case XKB_KEY_T: - case XKB_KEY_t: return &InputDeviceKeyboard::Key::AlphanumericT; - case XKB_KEY_U: - case XKB_KEY_u: return &InputDeviceKeyboard::Key::AlphanumericU; - case XKB_KEY_V: - case XKB_KEY_v: return &InputDeviceKeyboard::Key::AlphanumericV; - case XKB_KEY_W: - case XKB_KEY_w: return &InputDeviceKeyboard::Key::AlphanumericW; - case XKB_KEY_X: - case XKB_KEY_x: return &InputDeviceKeyboard::Key::AlphanumericX; - case XKB_KEY_Y: - case XKB_KEY_y: return &InputDeviceKeyboard::Key::AlphanumericY; - case XKB_KEY_Z: - case XKB_KEY_z: return &InputDeviceKeyboard::Key::AlphanumericZ; - case XKB_KEY_BackSpace: return &InputDeviceKeyboard::Key::EditBackspace; - case XKB_KEY_Caps_Lock: return &InputDeviceKeyboard::Key::EditCapsLock; - case XKB_KEY_Return: return &InputDeviceKeyboard::Key::EditEnter; - case XKB_KEY_space: return &InputDeviceKeyboard::Key::EditSpace; - case XKB_KEY_Tab: return &InputDeviceKeyboard::Key::EditTab; - case XKB_KEY_Escape: return &InputDeviceKeyboard::Key::Escape; - case XKB_KEY_F1: return &InputDeviceKeyboard::Key::Function01; - case XKB_KEY_F2: return &InputDeviceKeyboard::Key::Function02; - case XKB_KEY_F3: return &InputDeviceKeyboard::Key::Function03; - case XKB_KEY_F4: return &InputDeviceKeyboard::Key::Function04; - case XKB_KEY_F5: return &InputDeviceKeyboard::Key::Function05; - case XKB_KEY_F6: return &InputDeviceKeyboard::Key::Function06; - case XKB_KEY_F7: return &InputDeviceKeyboard::Key::Function07; - case XKB_KEY_F8: return &InputDeviceKeyboard::Key::Function08; - case XKB_KEY_F9: return &InputDeviceKeyboard::Key::Function09; - case XKB_KEY_F10: return &InputDeviceKeyboard::Key::Function10; - case XKB_KEY_F11: return &InputDeviceKeyboard::Key::Function11; - case XKB_KEY_F12: return &InputDeviceKeyboard::Key::Function12; - case XKB_KEY_F13: return &InputDeviceKeyboard::Key::Function13; - case XKB_KEY_F14: return &InputDeviceKeyboard::Key::Function14; - case XKB_KEY_F15: return &InputDeviceKeyboard::Key::Function15; - case XKB_KEY_F16: return &InputDeviceKeyboard::Key::Function16; - case XKB_KEY_F17: return &InputDeviceKeyboard::Key::Function17; - case XKB_KEY_F18: return &InputDeviceKeyboard::Key::Function18; - case XKB_KEY_F19: return &InputDeviceKeyboard::Key::Function19; - case XKB_KEY_F20: return &InputDeviceKeyboard::Key::Function20; - case XKB_KEY_Alt_L: return &InputDeviceKeyboard::Key::ModifierAltL; - case XKB_KEY_Alt_R: return &InputDeviceKeyboard::Key::ModifierAltR; - case XKB_KEY_Control_L: return &InputDeviceKeyboard::Key::ModifierCtrlL; - case XKB_KEY_Control_R: return &InputDeviceKeyboard::Key::ModifierCtrlR; - case XKB_KEY_Shift_L: return &InputDeviceKeyboard::Key::ModifierShiftL; - case XKB_KEY_Shift_R: return &InputDeviceKeyboard::Key::ModifierShiftR; - case XKB_KEY_Super_L: return &InputDeviceKeyboard::Key::ModifierSuperL; - case XKB_KEY_Super_R: return &InputDeviceKeyboard::Key::ModifierSuperR; - case XKB_KEY_Down: return &InputDeviceKeyboard::Key::NavigationArrowDown; - case XKB_KEY_Left: return &InputDeviceKeyboard::Key::NavigationArrowLeft; - case XKB_KEY_Right: return &InputDeviceKeyboard::Key::NavigationArrowRight; - case XKB_KEY_Up: return &InputDeviceKeyboard::Key::NavigationArrowUp; - case XKB_KEY_Delete: return &InputDeviceKeyboard::Key::NavigationDelete; - case XKB_KEY_End: return &InputDeviceKeyboard::Key::NavigationEnd; - case XKB_KEY_Home: return &InputDeviceKeyboard::Key::NavigationHome; - case XKB_KEY_Insert: return &InputDeviceKeyboard::Key::NavigationInsert; - case XKB_KEY_Page_Down: return &InputDeviceKeyboard::Key::NavigationPageDown; - case XKB_KEY_Page_Up: return &InputDeviceKeyboard::Key::NavigationPageUp; - case XKB_KEY_Num_Lock: return &InputDeviceKeyboard::Key::NumLock; - case XKB_KEY_KP_0: return &InputDeviceKeyboard::Key::NumPad0; - case XKB_KEY_KP_1: return &InputDeviceKeyboard::Key::NumPad1; - case XKB_KEY_KP_2: return &InputDeviceKeyboard::Key::NumPad2; - case XKB_KEY_KP_3: return &InputDeviceKeyboard::Key::NumPad3; - case XKB_KEY_KP_4: return &InputDeviceKeyboard::Key::NumPad4; - case XKB_KEY_KP_5: return &InputDeviceKeyboard::Key::NumPad5; - case XKB_KEY_KP_6: return &InputDeviceKeyboard::Key::NumPad6; - case XKB_KEY_KP_7: return &InputDeviceKeyboard::Key::NumPad7; - case XKB_KEY_KP_8: return &InputDeviceKeyboard::Key::NumPad8; - case XKB_KEY_KP_9: return &InputDeviceKeyboard::Key::NumPad9; - case XKB_KEY_KP_Add: return &InputDeviceKeyboard::Key::NumPadAdd; - case XKB_KEY_KP_Decimal: return &InputDeviceKeyboard::Key::NumPadDecimal; - case XKB_KEY_KP_Divide: return &InputDeviceKeyboard::Key::NumPadDivide; - case XKB_KEY_KP_Enter: return &InputDeviceKeyboard::Key::NumPadEnter; - case XKB_KEY_KP_Multiply: return &InputDeviceKeyboard::Key::NumPadMultiply; - case XKB_KEY_KP_Subtract: return &InputDeviceKeyboard::Key::NumPadSubtract; - case XKB_KEY_apostrophe: return &InputDeviceKeyboard::Key::PunctuationApostrophe; - case XKB_KEY_backslash: return &InputDeviceKeyboard::Key::PunctuationBackslash; - case XKB_KEY_bracketleft: return &InputDeviceKeyboard::Key::PunctuationBracketL; - case XKB_KEY_bracketright: return &InputDeviceKeyboard::Key::PunctuationBracketR; - case XKB_KEY_comma: return &InputDeviceKeyboard::Key::PunctuationComma; - case XKB_KEY_equal: return &InputDeviceKeyboard::Key::PunctuationEquals; - case XKB_KEY_hyphen: return &InputDeviceKeyboard::Key::PunctuationHyphen; - case XKB_KEY_period: return &InputDeviceKeyboard::Key::PunctuationPeriod; - case XKB_KEY_semicolon: return &InputDeviceKeyboard::Key::PunctuationSemicolon; - case XKB_KEY_slash: return &InputDeviceKeyboard::Key::PunctuationSlash; - case XKB_KEY_grave: - case XKB_KEY_asciitilde: return &InputDeviceKeyboard::Key::PunctuationTilde; - case XKB_KEY_ISO_Group_Shift: return &InputDeviceKeyboard::Key::SupplementaryISO; - case XKB_KEY_Pause: return &InputDeviceKeyboard::Key::WindowsSystemPause; - case XKB_KEY_Print: return &InputDeviceKeyboard::Key::WindowsSystemPrint; - case XKB_KEY_Scroll_Lock: return &InputDeviceKeyboard::Key::WindowsSystemScrollLock; - default: return nullptr; - } - } - - template - using DeleterForFreeFn = AZStd::integral_constant; - - AZStd::unique_ptr> m_xkbContext; - AZStd::unique_ptr> m_xkbKeymap; - AZStd::unique_ptr> m_xkbState; - int m_coreDeviceId{-1}; - bool m_initialized{false}; - }; - - InputDeviceKeyboard::Implementation* InputDeviceKeyboard::Implementation::Create(InputDeviceKeyboard& inputDevice) - { - return aznew InputDeviceKeyboardXcb(inputDevice); - } -} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp index 2950f8dcfc..ba3e761974 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Windowing/NativeWindow_Linux.cpp @@ -6,14 +6,16 @@ * */ -#include "NativeWindow_Linux_xcb.h" +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB +#include +#endif namespace AzFramework { NativeWindow::Implementation* NativeWindow::Implementation::Create() { #if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - return aznew NativeWindowImpl_Linux_xcb(); + return aznew XcbNativeWindow(); #elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND #error "Linux Window Manager Wayland not supported." return nullptr; @@ -22,5 +24,4 @@ namespace AzFramework return nullptr; #endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB } - } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake b/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake index 5c8ca8392f..b08c3b310d 100644 --- a/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake +++ b/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake @@ -10,6 +10,14 @@ # Only 'xcb' and 'wayland' are recognized if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb") + set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) + set(LY_INCLUDE_DIRECTORIES + PUBLIC + Platform/Common/Xcb + ) + set(LY_FILES_CMAKE + Platform/Common/Xcb/azframework_xcb_files.cmake + ) set(LY_BUILD_DEPENDENCIES PRIVATE 3rdParty::X11::xcb @@ -18,8 +26,6 @@ if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb") 3rdParty::X11::xkbcommon_X11 ) - set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) - elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "wayland") set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND) diff --git a/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake b/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake index 93767322b2..4f06aa5c57 100644 --- a/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake +++ b/Code/Framework/AzFramework/Platform/Linux/platform_linux_files.cmake @@ -12,8 +12,6 @@ set(FILES AzFramework/API/ApplicationAPI_Platform.h AzFramework/API/ApplicationAPI_Linux.h AzFramework/Application/Application_Linux.cpp - AzFramework/Application/Application_Linux_xcb.h - AzFramework/Application/Application_Linux_xcb.cpp AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp AzFramework/Process/ProcessWatcher_Linux.cpp AzFramework/Process/ProcessCommon.h @@ -22,10 +20,8 @@ set(FILES ../Common/Unimplemented/AzFramework/StreamingInstall/StreamingInstall_Unimplemented.cpp ../Common/Default/AzFramework/TargetManagement/TargetManagementComponent_Default.cpp AzFramework/Windowing/NativeWindow_Linux.cpp - AzFramework/Windowing/NativeWindow_Linux_xcb.h - AzFramework/Windowing/NativeWindow_Linux_xcb.cpp ../Common/Unimplemented/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad_Unimplemented.cpp - AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_xcb.cpp + AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp ../Common/Unimplemented/AzFramework/Input/Devices/Motion/InputDeviceMotion_Unimplemented.cpp ../Common/Unimplemented/AzFramework/Input/Devices/Mouse/InputDeviceMouse_Unimplemented.cpp ../Common/Unimplemented/AzFramework/Input/Devices/Touch/InputDeviceTouch_Unimplemented.cpp diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index 469a235b9f..ec709aef8a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -138,7 +138,13 @@ namespace AzToolsFramework m_indexMap[row] = index; m_rowMap[index] = row; ++row; - ++m_displayedItemsCounter; + + // We only want to increase the displayed counter if it is a parent (Source) + // so we don't cut children entries. + if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source) + { + ++m_displayedItemsCounter; + } } if (model->hasChildren(index)) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp index 37091c11e2..217e282a37 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp @@ -29,20 +29,20 @@ namespace AzToolsFramework { AssetBrowserTableView::AssetBrowserTableView(QWidget* parent) : AzQtComponents::TableView(parent) - , m_delegate(new EntryDelegate(this)) + , m_delegate(new SearchEntryDelegate(this)) { - setSortingEnabled(true); + setSortingEnabled(false); setItemDelegate(m_delegate); setRootIsDecorated(false); //Styling the header aligning text to the left and using a bold font. header()->setDefaultAlignment(Qt::AlignLeft); - header()->setStyleSheet("QHeaderView { font-weight: bold; }"); + header()->setStyleSheet("QHeaderView { font-weight: bold; };"); + setContextMenuPolicy(Qt::CustomContextMenu); setMouseTracking(true); - setSortingEnabled(false); setSelectionMode(QAbstractItemView::SingleSelection); connect(this, &AzQtComponents::TableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu); @@ -67,6 +67,8 @@ namespace AzToolsFramework header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch); header()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch); + header()->setSortIndicatorShown(false); + header()->setSectionsClickable(false); } void AssetBrowserTableView::SetName(const QString& name) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h index 94e9bdc4e4..b4eca59cef 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h @@ -26,7 +26,7 @@ namespace AzToolsFramework class AssetBrowserEntry; class AssetBrowserTableModel; class AssetBrowserFilterModel; - class EntryDelegate; + class SearchEntryDelegate; class AssetBrowserTableView //! Table view that displays the asset browser entries in a list. : public AzQtComponents::TableView @@ -67,9 +67,9 @@ namespace AzToolsFramework private: QString m_name; - QPointer m_tableModel = nullptr; - QPointer m_sourceFilterModel = nullptr; - EntryDelegate* m_delegate = nullptr; + QPointer m_tableModel; + QPointer m_sourceFilterModel; + SearchEntryDelegate* m_delegate = nullptr; private Q_SLOTS: void OnContextMenu(const QPoint& point); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index 29324c612c..755c59b55b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -11,12 +11,13 @@ #include #include #include - +#include #include #include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: class 'QScopedPointer' needs to have dll-interface to be used by clients of class 'QBrush' // 4800: 'uint': forcing value to bool 'true' or 'false' (performance warning) +#include #include AZ_POP_DISABLE_WARNING @@ -24,8 +25,13 @@ namespace AzToolsFramework { namespace AssetBrowser { - const int ENTRY_SPACING_LEFT_PIXELS = 8; - const int ENTRY_ICON_MARGIN_LEFT_PIXELS = 2; + static constexpr const char* TreeIconPathFirst = "Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg"; + static constexpr const char* TreeIconPathMiddle = "Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg"; + static constexpr const char* TreeIconPathLast = "Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg"; + static constexpr const char* TreeIconPathOneChild = "Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg"; + + const int EntrySpacingLeftPixels = 8; + const int EntryIconMarginLeftPixels = 2; EntryDelegate::EntryDelegate(QWidget* parent) : QStyledItemDelegate(parent) @@ -62,7 +68,7 @@ namespace AzToolsFramework // Draw main entry thumbnail. QRect remainingRect(option.rect); - remainingRect.adjust(ENTRY_ICON_MARGIN_LEFT_PIXELS, 0, 0, 0); // bump it rightwards to give some margin to the icon. + remainingRect.adjust(EntryIconMarginLeftPixels, 0, 0, 0); // bump it rightwards to give some margin to the icon. QSize iconSize(m_iconSize, m_iconSize); // Note that the thumbnail might actually be smaller than the row if theres a lot of padding or font size @@ -89,7 +95,7 @@ namespace AzToolsFramework } remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail - remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing. + remainingRect.adjust(EntrySpacingLeftPixels, 0, 0, 0); // bump it to the right by the spacing. } QString displayString = index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name) ? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name))) @@ -148,7 +154,162 @@ namespace AzToolsFramework return m_iconSize; } - } // namespace Thumbnailer + SearchEntryDelegate::SearchEntryDelegate(QWidget* parent) + : EntryDelegate(parent) + { + LoadBranchPixMaps(); + } + + void SearchEntryDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const + { + auto data = index.data(AssetBrowserModel::Roles::EntryRole); + if (data.canConvert()) + { + bool isEnabled = (option.state & QStyle::State_Enabled) != 0; + bool isSelected = (option.state & QStyle::State_Selected) != 0; + + QStyle* style = option.widget ? option.widget->style() : QApplication::style(); + + // draw the background + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, option.widget); + + // Draw main entry thumbnail. + QRect remainingRect(option.rect); + + QSize iconSize(m_iconSize, m_iconSize); + // Note that the thumbnail might actually be smaller than the row if theres a lot of padding or font size + // so it needs to center vertically with padding in that case: + QPoint iconTopLeft; + QPoint branchIconTopLeft = QPoint(); + + auto entry = qvariant_cast(data); + auto sourceEntry = azrtti_cast(entry); + + //If it is a SourceEntry or it is not the column name we don't want to add space for the branch Icon + if (sourceEntry || index.column() != aznumeric_cast(AssetBrowserEntry::Column::Name)) + { + remainingRect.adjust(EntryIconMarginLeftPixels, 0, 0, 0); // bump it rightwards to give some margin to the icon. + iconTopLeft = QPoint(remainingRect.x(), remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2)); + } + else + { + remainingRect.adjust(EntryIconMarginLeftPixels + m_iconSize, 0, 0, 0); // bump it rightwards to give some margin to the icon. + iconTopLeft = QPoint(remainingRect.x() / 2 + m_iconSize, remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2)); + branchIconTopLeft = QPoint((remainingRect.x() / 2) - 2, remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2)); + } + + QPalette actualPalette(option.palette); + + if (index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name)) + { + int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey()); + if (sourceEntry) + { + if (m_showSourceControl) + { + DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey()); + } + // sources with no children should be greyed out. + if (sourceEntry->GetChildCount() == 0) + { + isEnabled = false; // draw in disabled style. + actualPalette.setCurrentColorGroup(QPalette::Disabled); + } + } + else + { + //Get the indexes above and below our entry to see what type are they. + QAbstractItemView* view = qobject_cast(option.styleObject); + const QAbstractItemModel* viewModel = view->model(); + + const QModelIndex indexBelow = viewModel->index(index.row() + 1, index.column()); + const QModelIndex indexAbove = viewModel->index(index.row() - 1, index.column()); + + auto aboveEntry = qvariant_cast(indexBelow.data(AssetBrowserModel::Roles::EntryRole)); + auto belowEntry = qvariant_cast(indexAbove.data(AssetBrowserModel::Roles::EntryRole)); + + auto aboveSourceEntry = azrtti_cast(aboveEntry); + auto belowSourceEntry = azrtti_cast(belowEntry); + + // if current index is the last entry in the view + // or the index above it is a Source Entry and + // the index below is invalid or is valid but it is also a source entry + // then the current index is the only child. + if (index.row() == viewModel->rowCount() - 1 || + (indexBelow.isValid() && aboveSourceEntry && + (!indexAbove.isValid() || (indexAbove.isValid() && belowSourceEntry)))) + { + DrawBranchPixMap(EntryBranchType::OneChild, painter, branchIconTopLeft, iconSize); // Draw One Child Icon + } + else if (indexBelow.isValid() && aboveSourceEntry) // The index above is a source entry + { + DrawBranchPixMap(EntryBranchType::Last, painter, branchIconTopLeft, iconSize); // Draw First child Icon + } + else if (indexAbove.isValid() && belowSourceEntry) // The index below is a source entry + { + DrawBranchPixMap(EntryBranchType::First, painter, branchIconTopLeft, iconSize); // Draw Last Child Icon + } + else //the index above and below are also child entries + { + DrawBranchPixMap(EntryBranchType::Middle, painter, branchIconTopLeft, iconSize); // Draw Default child Icon. + } + } + + remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail + remainingRect.adjust(EntrySpacingLeftPixels, 0, 0, 0); // bump it to the right by the spacing. + } + QString displayString = index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name) + ? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name))) + : qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Path))); + + style->drawItemText( + painter, remainingRect, option.displayAlignment, actualPalette, isEnabled, displayString, + isSelected ? QPalette::HighlightedText : QPalette::Text); + } + } + + void SearchEntryDelegate::LoadBranchPixMaps() + { + AZ::IO::BasicPath absoluteIconPath; + for (int branchType = EntryBranchType::First; branchType != EntryBranchType::Count; ++branchType) + { + QPixmap pixmap; + switch (branchType) + { + case AzToolsFramework::AssetBrowser::EntryBranchType::First: + absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathFirst; + break; + case AzToolsFramework::AssetBrowser::EntryBranchType::Middle: + absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathMiddle; + break; + case AzToolsFramework::AssetBrowser::EntryBranchType::Last: + absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathLast; + break; + case AzToolsFramework::AssetBrowser::EntryBranchType::OneChild: + default: + absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathOneChild; + break; + } + bool pixmapLoadedSuccess = pixmap.load(absoluteIconPath.c_str()); + AZ_Assert(pixmapLoadedSuccess, "Error loading Branch Icons in SearchEntryDelegate"); + + m_branchIcons[static_cast(branchType)] = pixmap; + + } + } + + void SearchEntryDelegate::DrawBranchPixMap( + EntryBranchType branchType, QPainter* painter, const QPoint& point, const QSize& size) const + { + const QPixmap& pixmap = m_branchIcons[branchType]; + + pixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); + const QSize sizeDelta = size - pixmap.size(); + const QPoint pointDelta = QPoint(sizeDelta.width() / 2, sizeDelta.height() / 2); + painter->drawPixmap(point + pointDelta, pixmap); + } + + } // namespace AssetBrowser } // namespace AzToolsFramework #include "AssetBrowser/Views/moc_EntryDelegate.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h index 643bedec7c..ac68c19248 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h @@ -27,9 +27,19 @@ namespace AzToolsFramework { namespace AssetBrowser { + //! Type of branch icon the delegate should paint. + enum EntryBranchType + { + First, + Middle, + Last, + OneChild, + Count + }; + class AssetBrowserFilterModel; - //! EntryDelegate draws a single item in AssetBrowser + //! EntryDelegate draws a single item in AssetBrowser. class EntryDelegate : public QStyledItemDelegate { @@ -52,5 +62,23 @@ namespace AzToolsFramework //! Draw a thumbnail and return its width int DrawThumbnail(QPainter* painter, const QPoint& point, const QSize& size, Thumbnailer::SharedThumbnailKey thumbnailKey) const; }; + + //! SearchEntryDelegate draws a single item in AssetBrowserTableView. + class SearchEntryDelegate + : public EntryDelegate + { + Q_OBJECT + public: + explicit SearchEntryDelegate(QWidget* parent = nullptr); + + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + + private: + void LoadBranchPixMaps(); + void DrawBranchPixMap(EntryBranchType branchType, QPainter* painter, const QPoint& point, const QSize& size) const; + + private: + QMap m_branchIcons; + }; } // namespace AssetBrowser } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h index 6d3a7599de..a4b90f95b7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h @@ -33,7 +33,7 @@ namespace AzToolsFramework virtual AZ::EntityId GetFocusRoot() = 0; //! Returns whether the entity id provided is part of the focused sub-tree. - virtual bool IsInFocusSubTree(AZ::EntityId entityId) = 0; + virtual bool IsInFocusSubTree(AZ::EntityId entityId) const = 0; }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index 817006413d..f6afc3bff5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -86,7 +86,7 @@ namespace AzToolsFramework return m_focusRoot; } - bool FocusModeSystemComponent::IsInFocusSubTree(AZ::EntityId entityId) + bool FocusModeSystemComponent::IsInFocusSubTree(AZ::EntityId entityId) const { if (m_focusRoot == AZ::EntityId()) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h index 697a4ff998..27ddebe1ed 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h @@ -42,7 +42,7 @@ namespace AzToolsFramework void SetFocusRoot(AZ::EntityId entityId) override; void ClearFocusRoot() override; AZ::EntityId GetFocusRoot() override; - bool IsInFocusSubTree(AZ::EntityId entityId) override; + bool IsInFocusSubTree(AZ::EntityId entityId) const override; private: AZ::EntityId m_focusRoot; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 6135f35df5..c17b58486a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -39,7 +39,7 @@ namespace AzToolsFramework::Prefab { InstanceOptionalReference focusedInstance; - if (entityId == AZ::EntityId()) + if (!entityId.IsValid()) { PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); @@ -121,7 +121,7 @@ namespace AzToolsFramework::Prefab return false; } - if (entityId == AZ::EntityId()) + if (!entityId.IsValid()) { return false; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutliner.qss b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutliner.qss index 15ff8b1b67..b3c5882334 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutliner.qss +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutliner.qss @@ -18,18 +18,19 @@ AzToolsFramework--EntityOutlinerWidget QTreeView selection-background-color: transparent; } + +/* + * Entity Outliner handles hover and selected state of items via code, + * so we need to override the AzQtComponents::Treeview style. + */ AzToolsFramework--EntityOutlinerWidget QTreeView::branch:hover , AzToolsFramework--EntityOutlinerWidget QTreeView::item:hover -{ - background: rgba(255, 255, 255, 30); -} - -AzToolsFramework--EntityOutlinerWidget QTreeView::branch:selected +, AzToolsFramework--EntityOutlinerWidget QTreeView::branch:selected , AzToolsFramework--EntityOutlinerWidget QTreeView::item:selected , AzToolsFramework--EntityOutlinerWidget QTreeView::branch:selected:active , AzToolsFramework--EntityOutlinerWidget QTreeView::item:selected:active { - background: rgba(255, 255, 255, 45); + background: transparent; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp index d9f3ff8bb8..90f8e1d121 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -102,10 +103,14 @@ namespace AzToolsFramework EntityCompositionNotificationBus::Handler::BusConnect(); AZ::EntitySystemBus::Handler::BusConnect(); - m_editorEntityFrameworkInterface = AZ::Interface::Get(); + m_editorEntityUiInterface = AZ::Interface::Get(); + AZ_Assert(m_editorEntityUiInterface != nullptr, + "EntityOutlinerListModel requires a EditorEntityUiInterface instance on Initialize."); - AZ_Assert(m_editorEntityFrameworkInterface != nullptr, - "EntityOutlinerListModel requires a EditorEntityFrameworkInterface instance on Initialize."); + m_focusModeInterface = AZ::Interface::Get(); + AZ_Assert( + m_focusModeInterface != nullptr, + "EntityOutlinerListModel requires a FocusModeInterface instance on Initialize."); } int EntityOutlinerListModel::rowCount(const QModelIndex& parent) const @@ -279,7 +284,7 @@ namespace AzToolsFramework QVariant EntityOutlinerListModel::GetEntityIcon(const AZ::EntityId& id) const { - auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(id); + auto entityUiHandler = m_editorEntityUiInterface->GetHandler(id); QIcon icon; // Retrieve the icon from the handler @@ -316,7 +321,7 @@ namespace AzToolsFramework QVariant EntityOutlinerListModel::GetEntityTooltip(const AZ::EntityId& id) const { - auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(id); + auto entityUiHandler = m_editorEntityUiInterface->GetHandler(id); QString tooltip; // Retrieve the tooltip from the handler @@ -349,7 +354,7 @@ namespace AzToolsFramework QVariant EntityOutlinerListModel::dataForVisibility(const QModelIndex& index, int role) const { auto entityId = GetEntityFromIndex(index); - auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId); + auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId); if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId)) { @@ -377,7 +382,7 @@ namespace AzToolsFramework QVariant EntityOutlinerListModel::dataForLock(const QModelIndex& index, int role) const { auto entityId = GetEntityFromIndex(index); - auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId); + auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId); if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId)) { @@ -436,7 +441,7 @@ namespace AzToolsFramework if (value.canConvert()) { const auto entityId = GetEntityFromIndex(index); - auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId); + auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId); if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId)) { @@ -532,6 +537,11 @@ namespace AzToolsFramework break; } + if (AZ::EntityId entityId = GetEntityFromIndex(index); !m_focusModeInterface->IsInFocusSubTree(entityId)) + { + itemFlags &= !Qt::ItemIsEnabled; + } + return itemFlags; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx index 25e4276dd4..4b13aa6d27 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx @@ -36,6 +36,7 @@ namespace AzToolsFramework { class EditorEntityUiInterface; + class FocusModeInterface; namespace EntityOutliner { @@ -273,7 +274,8 @@ namespace AzToolsFramework QVariant GetEntityIcon(const AZ::EntityId& id) const; QVariant GetEntityTooltip(const AZ::EntityId& id) const; - EditorEntityUiInterface* m_editorEntityFrameworkInterface = nullptr; + EditorEntityUiInterface* m_editorEntityUiInterface = nullptr; + FocusModeInterface* m_focusModeInterface = nullptr; }; class EntityOutlinerCheckBox diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp index 73276973bb..6a40b3be3a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp @@ -40,6 +40,8 @@ namespace AzToolsFramework "EntityOutlinerTreeView requires a EditorEntityFrameworkInterface instance on Construction."); FocusModeNotificationBus::Handler::BusConnect(); + + viewport()->setMouseTracking(true); } EntityOutlinerTreeView::~EntityOutlinerTreeView() @@ -63,6 +65,11 @@ namespace AzToolsFramework } } + void EntityOutlinerTreeView::leaveEvent([[maybe_unused]] QEvent* event) + { + m_mousePosition = QPoint(); + } + void EntityOutlinerTreeView::mousePressEvent(QMouseEvent* event) { //postponing normal mouse pressed logic until mouse is released or dragged @@ -116,6 +123,8 @@ namespace AzToolsFramework setSelectionMode(selectionModeBefore); } + m_mousePosition = event->pos(); + //process mouse movement as normal, potentially triggering drag and drop QTreeView::mouseMoveEvent(event); } @@ -176,12 +185,45 @@ namespace AzToolsFramework void EntityOutlinerTreeView::drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const { + const bool isEnabled = (this->model()->flags(index) & Qt::ItemIsEnabled); + + const bool isSelected = selectionModel()->isSelected(index); + const bool isHovered = (index == indexAt(m_mousePosition)) && isEnabled; + + // Paint the branch Selection/Hover Rect + PaintBranchSelectionHoverRect(painter, rect, isSelected, isHovered); + // Paint the branch background as defined by the entity's handler, or its closes ancestor's. PaintBranchBackground(painter, rect, index); QTreeView::drawBranches(painter, rect, index); } + void EntityOutlinerTreeView::PaintBranchSelectionHoverRect( + QPainter* painter, const QRect& rect, bool isSelected, bool isHovered) const + { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, false); + + if (isSelected || isHovered) + { + QPainterPath backgroundPath; + QRect backgroundRect(rect); + + backgroundPath.addRect(backgroundRect); + + QColor backgroundColor = m_hoverColor; + if (isSelected) + { + backgroundColor = m_selectedColor; + } + + painter->fillPath(backgroundPath, backgroundColor); + } + + painter->restore(); + } + void EntityOutlinerTreeView::PaintBranchBackground(QPainter* painter, const QRect& rect, const QModelIndex& index) const { // Go through ancestors and add them to the stack diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx index 8d71b3ed31..2ddbaaafa9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx @@ -61,6 +61,7 @@ namespace AzToolsFramework void startDrag(Qt::DropActions supportedActions) override; void dragMoveEvent(QDragMoveEvent* event) override; void dropEvent(QDropEvent* event) override; + void leaveEvent(QEvent* event) override; // FocusModeNotificationBus overrides ... void OnEditorFocusChanged(AZ::EntityId entityId) override; @@ -77,8 +78,10 @@ namespace AzToolsFramework void StartCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions) override; void PaintBranchBackground(QPainter* painter, const QRect& rect, const QModelIndex& index) const; + void PaintBranchSelectionHoverRect(QPainter* painter, const QRect& rect, bool isSelected, bool isHovered) const; QMouseEvent* m_queuedMouseEvent; + QPoint m_mousePosition; bool m_draggingUnselectedItem; // This is set when an item is dragged outside its bounding box. int m_expandOnlyDelay = -1; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp index 25b10463dc..57e6c9ff7a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +113,17 @@ namespace AzToolsFramework } } + EditorHelpers::EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache) + : m_entityDataCache(entityDataCache) + { + m_focusModeInterface = AZ::Interface::Get(); + AZ_Assert( + m_focusModeInterface, + "EditorHelpers - " + "Focus Mode Interface could not be found. " + "Check that it is being correctly initialized."); + } + AZ::EntityId EditorHelpers::HandleMouseInteraction( const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteractionEvent& mouseInteraction) { @@ -173,6 +185,12 @@ namespace AzToolsFramework } } + // Verify if the entity Id corresponds to an entity that is focused; if not, halt selection. + if (!m_focusModeInterface->IsInFocusSubTree(entityIdUnderCursor)) + { + return AZ::EntityId(); + } + return entityIdUnderCursor; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h index 0dbb1d0f6c..a6a78a4e61 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h @@ -22,6 +22,7 @@ namespace AzFramework namespace AzToolsFramework { class EditorVisibleEntityDataCache; + class FocusModeInterface; namespace ViewportInteraction { @@ -38,10 +39,7 @@ namespace AzToolsFramework //! An EditorVisibleEntityDataCache must be passed to EditorHelpers to allow it to //! efficiently read entity data without resorting to EBus calls. - explicit EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache) - : m_entityDataCache(entityDataCache) - { - } + explicit EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache); EditorHelpers(const EditorHelpers&) = delete; EditorHelpers& operator=(const EditorHelpers&) = delete; ~EditorHelpers() = default; @@ -62,5 +60,6 @@ namespace AzToolsFramework private: const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers. + const FocusModeInterface* m_focusModeInterface = nullptr; }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp new file mode 100644 index 0000000000..5972da58a7 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp @@ -0,0 +1,128 @@ +/* + * 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 + +namespace AzToolsFramework +{ + class EditorFocusModeTests + : public ::testing::Test + { + protected: + void SetUp() override + { + m_app.Start(m_descriptor); + + // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // in the unit tests. + AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); + + GenerateTestHierarchy(); + } + + void GenerateTestHierarchy() + { + /* + * City + * |_ Street + * |_ Car + * | |_ Passenger + * |_ SportsCar + * |_ Passenger + */ + + m_entityMap["cityId"] = CreateEditorEntity("City", AZ::EntityId()); + m_entityMap["streetId"] = CreateEditorEntity("Street", m_entityMap["cityId"]); + m_entityMap["carId"] = CreateEditorEntity("Car", m_entityMap["streetId"]); + m_entityMap["passengerId1"] = CreateEditorEntity("Passenger", m_entityMap["carId"]); + m_entityMap["sportsCarId"] = CreateEditorEntity("SportsCar", m_entityMap["streetId"]); + m_entityMap["passengerId2"] = CreateEditorEntity("Passenger", m_entityMap["sportsCarId"]); + } + + AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId) + { + AZ::Entity* entity = nullptr; + UnitTest::CreateDefaultEditorEntity(name, &entity); + + // Parent + AZ::TransformBus::Event(entity->GetId(), &AZ::TransformInterface::SetParent, parentId); + + return entity->GetId(); + } + + void TearDown() override + { + m_app.Stop(); + } + + UnitTest::ToolsTestApplication m_app{ "EditorFocusModeTests" }; + AZ::ComponentApplication::Descriptor m_descriptor; + AZStd::unordered_map m_entityMap; + }; + + TEST_F(EditorFocusModeTests, EditorFocusModeTests_SetFocus) + { + FocusModeInterface* focusModeInterface = AZ::Interface::Get(); + EXPECT_TRUE(focusModeInterface != nullptr); + + focusModeInterface->SetFocusRoot(m_entityMap["carId"]); + EXPECT_EQ(focusModeInterface->GetFocusRoot(), m_entityMap["carId"]); + + focusModeInterface->ClearFocusRoot(); + EXPECT_EQ(focusModeInterface->GetFocusRoot(), AZ::EntityId()); + } + + TEST_F(EditorFocusModeTests, EditorFocusModeTests_IsInFocusSubTree) + { + FocusModeInterface* focusModeInterface = AZ::Interface::Get(); + EXPECT_TRUE(focusModeInterface != nullptr); + + focusModeInterface->ClearFocusRoot(); + + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true); + + focusModeInterface->SetFocusRoot(m_entityMap["streetId"]); + + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true); + + focusModeInterface->SetFocusRoot(m_entityMap["carId"]); + + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), false); + + focusModeInterface->SetFocusRoot(m_entityMap["passengerId2"]); + + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true); + + focusModeInterface->ClearFocusRoot(); + } +} diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp new file mode 100644 index 0000000000..ffd6cd2a44 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp @@ -0,0 +1,173 @@ +/* + * 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 + +namespace UnitTest +{ + class PrefabFocusTests + : public PrefabTestFixture + { + protected: + void GenerateTestHierarchy() + { + /* + * City (Prefab Container) + * |_ City + * |_ Street (Prefab Container) + * |_ Car (Prefab Container) + * | |_ Passenger + * |_ SportsCar (Prefab Container) + * |_ Passenger + */ + + m_entityMap["passenger1"] = CreateEntity("Passenger1"); + m_entityMap["passenger2"] = CreateEntity("Passenger2"); + m_entityMap["city"] = CreateEntity("City"); + + AzToolsFramework::EditorEntityContextRequestBus::Broadcast( + &AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, + AzToolsFramework::EntityList{ m_entityMap["passenger1"], m_entityMap["passenger2"], m_entityMap["city"] }); + + AZStd::unique_ptr carInstance = + m_prefabSystemComponent->CreatePrefab({ m_entityMap["passenger1"] }, {}, "test/car"); + ASSERT_TRUE(carInstance); + m_instanceMap["car"] = carInstance.get(); + + AZStd::unique_ptr sportsCarInstance = + m_prefabSystemComponent->CreatePrefab({ m_entityMap["passenger2"] }, {}, "test/sportsCar"); + ASSERT_TRUE(sportsCarInstance); + m_instanceMap["sportsCar"] = sportsCarInstance.get(); + + AZStd::unique_ptr streetInstance = + m_prefabSystemComponent->CreatePrefab({}, MakeInstanceList( AZStd::move(carInstance), AZStd::move(sportsCarInstance) ), "test/street"); + ASSERT_TRUE(streetInstance); + m_instanceMap["street"] = streetInstance.get(); + + m_rootInstance = + m_prefabSystemComponent->CreatePrefab({ m_entityMap["city"] }, MakeInstanceList(AZStd::move(streetInstance)), "test/city"); + ASSERT_TRUE(m_rootInstance); + m_instanceMap["city"] = m_rootInstance.get(); + } + + AZStd::unordered_map m_entityMap; + AZStd::unordered_map m_instanceMap; + + AZStd::unique_ptr m_rootInstance; + }; + + TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab) + { + GenerateTestHierarchy(); + + PrefabFocusInterface* prefabFocusInterface = AZ::Interface::Get(); + EXPECT_TRUE(prefabFocusInterface != nullptr); + + // Verify FocusOnOwningPrefab works when passing the container entity of the root prefab. + { + prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["city"]->GetContainerEntityId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["city"]->GetTemplateId()); + + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), m_instanceMap["city"]); + } + + // Verify FocusOnOwningPrefab works when passing a nested entity of the root prefab. + { + prefabFocusInterface->FocusOnOwningPrefab(m_entityMap["city"]->GetId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["city"]->GetTemplateId()); + + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), m_instanceMap["city"]); + } + + // Verify FocusOnOwningPrefab works when passing the container entity of a nested prefab. + { + prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["car"]->GetContainerEntityId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["car"]->GetTemplateId()); + + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), m_instanceMap["car"]); + } + + // Verify FocusOnOwningPrefab works when passing a nested entity of the a nested prefab. + { + prefabFocusInterface->FocusOnOwningPrefab(m_entityMap["passenger1"]->GetId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["car"]->GetTemplateId()); + + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), m_instanceMap["car"]); + } + + // Verify FocusOnOwningPrefab points to the root prefab when the focus is cleared. + { + AzToolsFramework::PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface = + AZ::Interface::Get(); + AzToolsFramework::Prefab::InstanceOptionalReference rootPrefabInstance = + prefabEditorEntityOwnershipInterface->GetRootPrefabInstance(); + EXPECT_TRUE(rootPrefabInstance.has_value()); + + prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), rootPrefabInstance->get().GetTemplateId()); + + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), &rootPrefabInstance->get()); + } + + m_rootInstance.release(); + } + + TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused) + { + GenerateTestHierarchy(); + + PrefabFocusInterface* prefabFocusInterface = AZ::Interface::Get(); + EXPECT_TRUE(prefabFocusInterface != nullptr); + + // Verify IsOwningPrefabBeingFocused returns true for all entities in a focused prefab (container/nested) + { + prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["city"]->GetContainerEntityId()); + + EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["city"]->GetContainerEntityId())); + EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["city"]->GetId())); + } + + // Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (ancestors/descendants) + { + prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["street"]->GetContainerEntityId()); + + EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["street"]->GetContainerEntityId())); + EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["city"]->GetContainerEntityId())); + EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["city"]->GetId())); + EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["car"]->GetContainerEntityId())); + EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger1"]->GetId())); + } + + // Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (siblings) + { + prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["sportsCar"]->GetContainerEntityId()); + + EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["sportsCar"]->GetContainerEntityId())); + EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger2"]->GetId())); + EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["car"]->GetContainerEntityId())); + EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger1"]->GetId())); + } + + m_rootInstance.release(); + } + +} diff --git a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake index 764afce266..ecc7d17ccc 100644 --- a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake +++ b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake @@ -34,6 +34,7 @@ set(FILES EntityTestbed.h FileFunc.cpp FingerprintingTests.cpp + FocusMode/EditorFocusModeTests.cpp GenericComponentWrapperTest.cpp InstanceDataHierarchy.cpp IntegerPrimtitiveTestConfig.h @@ -50,6 +51,7 @@ set(FILES Prefab/Benchmark/PrefabLoadBenchmarks.cpp Prefab/Benchmark/PrefabUpdateInstancesBenchmarks.cpp Prefab/Benchmark/SpawnableCreateBenchmarks.cpp + Prefab/PrefabFocus/PrefabFocusTests.cpp Prefab/MockPrefabFileIOActionValidator.cpp Prefab/MockPrefabFileIOActionValidator.h Prefab/PrefabDuplicateTests.cpp diff --git a/Gems/AWSMetrics/cdk/aws_metrics/batch_analytics.py b/Gems/AWSMetrics/cdk/aws_metrics/batch_analytics.py index eb36d8a5da..709335f9ce 100755 --- a/Gems/AWSMetrics/cdk/aws_metrics/batch_analytics.py +++ b/Gems/AWSMetrics/cdk/aws_metrics/batch_analytics.py @@ -122,6 +122,9 @@ class BatchAnalytics: ) ] + for named_query in self._named_queries: + named_query.node.add_dependency(self._athena_work_group) + @property def athena_work_group_name(self) -> athena.CfnWorkGroup.name: return self._athena_work_group.name diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 82568126df..3fcda71998 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -85,7 +85,6 @@ namespace AZ { const auto jobLambda = [&]() -> void { - AZ_PROFILE_SCOPE(AzRender, "MeshFP::Simulate() Lambda"); for (auto meshDataIter = iteratorRange.first; meshDataIter != iteratorRange.second; ++meshDataIter) { if (!meshDataIter->m_model) diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h index 36511ebccf..b6f6e0df11 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/MemoryUsage.h @@ -28,25 +28,19 @@ namespace AZ size_t m_accumulatedInBytes = 0; }; - /** - * Tracks memory usage for a specific heap in the system. The data is expected to adhere to the following constraints: - * - * 1) Reserved <= Budget (unless the budget is 0). - * 2) Resident <= Reserved. - */ + //! Tracks memory usage for a specific heap in the system. The data is expected to adhere to the following constraints: + //! 1) Reserved <= Budget (unless the budget is 0). + //! 2) Resident <= Reserved. struct HeapMemoryUsage { HeapMemoryUsage() = default; HeapMemoryUsage(const HeapMemoryUsage&); HeapMemoryUsage& operator=(const HeapMemoryUsage&); - /** - * This helper reserves memory in a thread-safe fashion. If the result exceeds the budget, the reservation is safely - * reverted and false is returned. otherwise, true is returned. Only m_reservedInBytes is affected. - * - * @param sizeInBytes The amount of bytes to reserve. - * @return Whether the reservation was successful. - */ + //! This helper reserves memory in a thread-safe fashion. If the result exceeds the budget, the reservation is safely + //! reverted and false is returned. otherwise, true is returned. Only m_reservedInBytes is affected. + //! @param sizeInBytes The amount of bytes to reserve. + //! @return Whether the reservation was successful. bool TryReserveMemory(size_t sizeInBytes) { const size_t reservationInBytes = (m_reservedInBytes += sizeInBytes); @@ -60,45 +54,41 @@ namespace AZ return true; } - /** - * Helper function to validate sizes - */ + //! Helper function to validate sizes void Validate() { if (Validation::IsEnabled()) { - AZ_Assert(m_budgetInBytes >= m_reservedInBytes, "Reserved memory is larger than memory budget"); - AZ_Assert(m_reservedInBytes >= m_residentInBytes, "Resident memory is larger than reserved memory"); + AZ_Assert( + m_budgetInBytes >= m_reservedInBytes, + "Reserved memory is larger than memory budget. Memory budget %zu Reserved %zu", m_budgetInBytes, m_reservedInBytes.load()); + AZ_Assert( + m_reservedInBytes >= m_residentInBytes, + "Resident memory is larger than reserved memory. Reserved Memory %zu Resident memory %zu", m_reservedInBytes.load(), + m_residentInBytes.load()); } } - /** - * The budget for the heap in bytes. A non-zero budget means the pool will reject reservation requests - * once the budget is exceeded. A zero budget effectively disables this check. On certain platforms, - * it may be unnecessary to budget certain heaps. Other platforms may require a non-zero budget for certain - * heaps. - */ + // The budget for the heap in bytes. A non-zero budget means the pool will reject reservation requests + // once the budget is exceeded. A zero budget effectively disables this check. On certain platforms, + // it may be unnecessary to budget certain heaps. Other platforms may require a non-zero budget for certain + // heaps. size_t m_budgetInBytes = 0; - /** - * Number of bytes reserved on the heap for allocations. This value represents the allocation capacity for - * the platform. It is validated against the budget and may not exceed it. - */ + // Number of bytes reserved on the heap for allocations. This value represents the allocation capacity for + // the platform. It is validated against the budget and may not exceed it. AZStd::atomic_size_t m_reservedInBytes{ 0 }; - /** - * Number of bytes physically allocated on the heap. This may not exceed the reservation. Certain platforms - * may choose to transfer memory down the heap level hierarchy in response to memory trim events from the driver. - */ + // Number of bytes physically allocated on the heap. This may not exceed the reservation. Certain platforms + // may choose to transfer memory down the heap level hierarchy in response to memory trim events from the driver. AZStd::atomic_size_t m_residentInBytes{ 0 }; }; - /** - * Describes memory usage metrics of a resource pool. Resource pools *can* associate with a single - * device memory heap (i.e. a single GPU) and the host memory heap. Certain pools on specific platforms - * may not require one or the other. In this case, the memory usage / budget will report empty values for - * that heap type. - */ + //! + //! Describes memory usage metrics of a resource pool. Resource pools *can* associate with a single + //! device memory heap (i.e. a single GPU) and the host memory heap. Certain pools on specific platforms + //! may not require one or the other. In this case, the memory usage / budget will report empty values for + //! that heap type. struct PoolMemoryUsage { PoolMemoryUsage() = default; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h index 0bd0056a0b..dc95078f80 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Allocator.h @@ -13,13 +13,12 @@ namespace AZ { namespace RHI { - /** - * A virtual address which may be relative to a base resource. This means - * 0 might be a valid address (dependent on the Allocator::Descriptor::m_addressBase value). - * To account for this, VirtualAddress::Null is used instead. Check validity of the address - * using IsValid or IsNull instead of checking for 0. VirtualAddress is initialized - * to Null, so returning the default constructor is sufficient to represent an invalid address. - */ + + //! A virtual address which may be relative to a base resource. This means + //! 0 might be a valid address (dependent on the Allocator::Descriptor::m_addressBase value). + //! To account for this, VirtualAddress::Null is used instead. Check validity of the address + //! using IsValid or IsNull instead of checking for 0. VirtualAddress is initialized + //! to Null, so returning the default constructor is sufficient to represent an invalid address. class VirtualAddress { static const VirtualAddress Null; @@ -29,13 +28,13 @@ namespace AZ static VirtualAddress CreateNull(); - /// Creates a valid address with a zero offset. + //! Creates a valid address with a zero offset. static VirtualAddress CreateZero(); - /// Creates an address from a pointer. + //! Creates an address from a pointer. static VirtualAddress CreateFromPointer(void* ptr); - /// Creates an address from an offset from a base pointer. + //! Creates an address from an offset from a base pointer. static VirtualAddress CreateFromOffset(uint64_t offset); inline bool IsValid() const @@ -51,15 +50,13 @@ namespace AZ uintptr_t m_ptr; }; - /** - * An allocator interface used for external GPU allocations. The allocator - * does not manage the host memory. Instead, the user specifies a base address - * (which may be 0, in order to allocate offsets from a base resource). The allocator - * interface also provides an API for garbage collection. If used to manage GPU resources, - * these are often deferred-released after N frames. The user may provide a garbage collection - * latency, which controls the number of GarbageCollect calls that must occur before an allocation - * is actually reclaimed. The intended use case is to garbage collect at the end of each frame. - */ + //! An allocator interface used for external GPU allocations. The allocator + //! does not manage the host memory. Instead, the user specifies a base address + //! (which may be 0, in order to allocate offsets from a base resource). The allocator + //! interface also provides an API for garbage collection. If used to manage GPU resources, + //! these are often deferred-released after N frames. The user may provide a garbage collection + //! latency, which controls the number of GarbageCollect calls that must occur before an allocation + //! is actually reclaimed. The intended use case is to garbage collect at the end of each frame. class Allocator { public: @@ -86,44 +83,42 @@ namespace AZ virtual void Shutdown() = 0; - /** - * Allocates a virtual address relative to the base address provided at initialization time. - * @param byteCount The number of bytes to allocate. - * @param byteAlignement The alignment used to align the allocation. - */ + //! Allocates a virtual address relative to the base address provided at initialization time. + //! @param byteCount The number of bytes to allocate. + //! @param byteAlignement The alignment used to align the allocation. virtual VirtualAddress Allocate(size_t byteCount, size_t byteAlignment) = 0; - /** - * Deallocates an allocation. The memory is not reclaimed until garbage collect is called. - * Depending on the garbage collection latency, it may take several garbage collection cycles - * before the memory is reclaimed. - */ + //! Deallocates an allocation. The memory is not reclaimed until garbage collect is called. + //! Depending on the garbage collection latency, it may take several garbage collection cycles + //! before the memory is reclaimed. virtual void DeAllocate(VirtualAddress offset) = 0; - /// Allocations are deferred-released until a specific number of GC cycles have occurred. This - /// is useful for allocations actively being consumed by the GPU. + //! Allocations are deferred-released until a specific number of GC cycles have occurred. This + //! is useful for allocations actively being consumed by the GPU. virtual void GarbageCollect() = 0; - /// Forces garbage collection of all allocations, regardless of the GC latency. + //! Forces garbage collection of all allocations, regardless of the GC latency. virtual void GarbageCollectForce() = 0; - /** - * Returns the number of allocations active for this allocator. This includes - * allocations that are pending garbage collection. - */ + //! Returns the number of allocations active for this allocator. This includes + //! allocations that are pending garbage collection. virtual size_t GetAllocationCount() const { return 0; } - /** - * Returns the number of bytes used by the allocator. This includes - * allocations that are pending garbage collection. - */ + //! Returns the number of bytes used by the allocator. This includes + //! allocations that are pending garbage collection. virtual size_t GetAllocatedByteCount() const { return 0; } - /// Returns the descriptor used to initialize the allocator. + //! Returns the descriptor used to initialize the allocator. virtual const Descriptor& GetDescriptor() const = 0; - /// Helper for converting agnostic VirtualAddress type to pointer type. Will convert - /// VirtualAddress::Null to nullptr. + //! Clone the current allocator to the new allocator passed in + virtual void Clone([[maybe_unused]] RHI::Allocator* newAllocator) + { + AZ_Assert(false, "Not Implemented"); + }; + + //! Helper for converting agnostic VirtualAddress type to pointer type. Will convert + //! VirtualAddress::Null to nullptr. template T* AllocateAs(size_t byteCount, size_t byteAlignment) { diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h index f9df29cf74..fb4082bb25 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Device.h @@ -139,6 +139,12 @@ namespace AZ //! Notifies after all objects currently in the platform release queue are released virtual void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) = 0; + //! Allows the back-ends to compact SRG related memory if applicable + virtual RHI::ResultCode CompactSRGMemory() + { + return RHI::ResultCode::Success; + }; + protected: DeviceFeatures m_features; DeviceLimits m_limits; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h index 992cc0e79a..71f9f1605b 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/Factory.h @@ -112,6 +112,9 @@ namespace AZ //! Returns true if Pix dll is loaded static bool IsPixModuleLoaded(); + //! Returns true if Warp is enabled + static bool UsingWarpDevice(); + //! Returns the name of the Factory. virtual Name GetName() = 0; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h index ee1cb7c7f4..ce20f9cbe5 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/FreeListAllocator.h @@ -55,6 +55,7 @@ namespace AZ size_t GetAllocationCount() const override; size_t GetAllocatedByteCount() const override; const Descriptor& GetDescriptor() const override; + void Clone(RHI::Allocator* newAllocator) override; ////////////////////////////////////////////////////////////////////////// private: diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp index ab45d2239b..ef330e7902 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp @@ -155,11 +155,6 @@ namespace AZ { arguments += " -Zpr"; } - if (m_dxcGenerateDebugInfo) - { - arguments += " -Zi"; // Generate debug information - arguments += " -Zss"; // Compute Shader Hash considering source information - } // strip spaces at both sides AZStd::string dxcAdditionalFreeArguments = m_dxcAdditionalFreeArguments; AzFramework::StringFunc::TrimWhiteSpace(dxcAdditionalFreeArguments, true, true); diff --git a/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp b/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp index 48b64c17c0..82b0a13c86 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/Factory.cpp @@ -8,12 +8,12 @@ #include #include +#include #include #include #if defined(USE_RENDERDOC) || defined(USE_PIX) #include -#include #include #endif @@ -28,6 +28,8 @@ static AZStd::unique_ptr s_pixModule; static bool s_isPixGpuCaptureDllLoaded = false; #endif +static bool s_usingWarpDevice = false; + namespace AZ { namespace RHI @@ -55,6 +57,8 @@ namespace AZ Factory::Factory() { + AZStd::string preferredUserAdapterName = RHI::GetCommandLineValue("forceAdapter"); + s_usingWarpDevice = preferredUserAdapterName == "Microsoft Basic Render Driver"; #if defined(USE_RENDERDOC) // If RenderDoc is requested, we need to load the library as early as possible (before device queries/factories are made) bool enableRenderDoc = RHI::QueryCommandLineOption("enableRenderDoc"); @@ -197,5 +201,10 @@ namespace AZ return false; #endif } + + bool Factory::UsingWarpDevice() + { + return s_usingWarpDevice; + } } } diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp index df887379fe..5d2feb1e34 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp @@ -314,6 +314,11 @@ namespace AZ resourcePoolDatabase.ForEachShaderResourceGroupPool(compileAllLambda); } + + //It is possible for certain back ends to run out of SRG memory (due to fragmentation) in which case + //we try to compact and re-compile SRGs. + RHI::ResultCode resultCode = m_device->CompactSRGMemory(); + AZ_Assert(resultCode == RHI::ResultCode::Success, "SRG compaction failed and this can lead to a gpu crash."); } void FrameScheduler::BuildRayTracingShaderTables() diff --git a/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp b/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp index c646700495..77877b0020 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FreeListAllocator.cpp @@ -344,5 +344,17 @@ namespace AZ handle = node.m_nextFree; } } + + void FreeListAllocator::Clone(RHI::Allocator* newAllocator) + { + FreeListAllocator* newFreeListAllocator = static_cast(newAllocator); + newFreeListAllocator->m_headHandle = m_headHandle; + newFreeListAllocator->m_nodeFreeList = m_nodeFreeList; + newFreeListAllocator->m_nodes = m_nodes; + newFreeListAllocator->m_allocations = m_allocations; + newFreeListAllocator->m_garbage = m_garbage; + newFreeListAllocator->m_garbageCollectCycle = m_garbageCollectCycle; + newFreeListAllocator->m_byteCountTotal = m_byteCountTotal; + } } } diff --git a/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h b/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h index 1f22599b38..63c2d69ea2 100644 --- a/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h +++ b/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h @@ -64,6 +64,12 @@ namespace AZ //! int array: Max count for descriptors AZStd::unordered_map> m_descriptorHeapLimits; + // Number of max static handles for shader visible srv/uav/cbv views + uint32_t m_numShaderVisibleCbvSrvUavStaticHandles = 2000; + + //Bool to indicate allowing compaction of shader visible srv/uav/cbv heap in case of fragmentation + bool m_allowDescriptorHeapCompaction = false; + FrameGraphExecuterData m_frameGraphExecuterData; void LoadPlatformLimitsDescriptor(const char* rhiName) override; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp index 89ae3ede46..f30fc72ace 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp @@ -255,6 +255,11 @@ namespace AZ // Compilation parameters AZStd::string params = shaderCompilerArguments.MakeAdditionalDxcCommandLineString(); + if (BuildHasDebugInfo(shaderCompilerArguments)) + { + params += " -Zi"; // Generate debug information + params += " -Zss"; // Compute Shader Hash considering source information + } // Enable half precision types when shader model >= 6.2 int shaderModelMajor = 0; @@ -281,12 +286,11 @@ namespace AZ AZStd::string symbolDatabaseFileCliArgument{" "}; // when not debug: still insert a space between 5.dxil and 7.hlsl-in if (BuildHasDebugInfo(shaderCompilerArguments)) { - // prepare .ldd filename: + // prepare .pdb filename: AZStd::string md5hex = RHI::ByteToHexString(md5); AZStd::string symbolDatabaseFilePath = dxcInputFile.c_str(); // mutate from source - AZStd::string lldFileName = md5hex // lld is like pdb but it's the default symbol database extension in dxc - + "-" + profileIt->second; // concatenate the shader profile to disambiguate vs/ps... - AzFramework::StringFunc::Path::ReplaceFullName(symbolDatabaseFilePath, lldFileName.c_str(), "lld"); + AZStd::string pdbFileName = md5hex + "-" + profileIt->second; // concatenate the shader profile to disambiguate vs/ps... + AzFramework::StringFunc::Path::ReplaceFullName(symbolDatabaseFilePath, pdbFileName.c_str(), "pdb"); // it is possible that another activated platform/profile, already exported that file. (since it's hashed on the source file) // dxc returns an error in such case. we get less surprising effets by just not mentionning an -Fd argument if (AZ::IO::SystemFile::Exists(symbolDatabaseFilePath.c_str())) diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp index 77b41d6ffc..fd9ad84511 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/PlatformLimitsDescriptor.cpp @@ -19,8 +19,10 @@ namespace AZ if (SerializeContext* serializeContext = azrtti_cast(context)) { serializeContext->Class() - ->Version(0) + ->Version(1) ->Field("DescriptorHeapLimits", &PlatformLimitsDescriptor::m_descriptorHeapLimits) + ->Field("NumShaderVisibleCbvSrvUavStaticHandles", &PlatformLimitsDescriptor::m_numShaderVisibleCbvSrvUavStaticHandles) + ->Field("AllowDescriptorHeapCompaction", &PlatformLimitsDescriptor::m_allowDescriptorHeapCompaction) ->Field("FrameGraphExecuterData", &PlatformLimitsDescriptor::m_frameGraphExecuterData) ; } @@ -54,7 +56,7 @@ namespace AZ // Map default value must be initialized after attempting to serialize (and result in failure). // Otherwise, serialization won't overwrite the default values. m_descriptorHeapLimits = AZStd::unordered_map>({ - { AZStd::string("DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV"), { 1000000, 1000000 } }, + { AZStd::string("DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV"), { 100000, 1000000 } }, { AZStd::string("DESCRIPTOR_HEAP_TYPE_SAMPLER"), { 2048, 2048 } }, { AZStd::string("DESCRIPTOR_HEAP_TYPE_RTV"), { 2048, 0 } }, { AZStd::string("DESCRIPTOR_HEAP_TYPE_DSV"), { 2048, 0 } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h index 443ab3e73a..1472cdc80e 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h @@ -438,7 +438,7 @@ namespace AZ switch (pipelineType) { case RHI::PipelineStateType::Draw: - if (binding.m_resourceTable.IsValid()) + if (binding.m_resourceTable.IsValid() && compiledData.m_gpuViewsDescriptorHandle.ptr) { GetCommandList()->SetGraphicsRootDescriptorTable(binding.m_resourceTable.GetIndex(), compiledData.m_gpuViewsDescriptorHandle); } @@ -448,14 +448,15 @@ namespace AZ GetCommandList()->SetGraphicsRootConstantBufferView(binding.m_constantBuffer.GetIndex(), compiledData.m_gpuConstantAddress); } - if (binding.m_samplerTable.IsValid()) + if (binding.m_samplerTable.IsValid() && compiledData.m_gpuSamplersDescriptorHandle.ptr) { GetCommandList()->SetGraphicsRootDescriptorTable(binding.m_samplerTable.GetIndex(), compiledData.m_gpuSamplersDescriptorHandle); } for (uint32_t unboundedArrayIndex = 0; unboundedArrayIndex < ShaderResourceGroupCompiledData::MaxUnboundedArrays; ++unboundedArrayIndex) { - if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid()) + if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid() && + compiledData.m_gpuUnboundedArraysDescriptorHandles[unboundedArrayIndex].ptr) { GetCommandList()->SetGraphicsRootDescriptorTable( binding.m_unboundedArrayResourceTables[unboundedArrayIndex].GetIndex(), @@ -465,7 +466,7 @@ namespace AZ break; case RHI::PipelineStateType::Dispatch: - if (binding.m_resourceTable.IsValid()) + if (binding.m_resourceTable.IsValid() && compiledData.m_gpuViewsDescriptorHandle.ptr) { GetCommandList()->SetComputeRootDescriptorTable(binding.m_resourceTable.GetIndex(), compiledData.m_gpuViewsDescriptorHandle); } @@ -475,14 +476,15 @@ namespace AZ GetCommandList()->SetComputeRootConstantBufferView(binding.m_constantBuffer.GetIndex(), compiledData.m_gpuConstantAddress); } - if (binding.m_samplerTable.IsValid()) + if (binding.m_samplerTable.IsValid() && compiledData.m_gpuSamplersDescriptorHandle.ptr) { GetCommandList()->SetComputeRootDescriptorTable(binding.m_samplerTable.GetIndex(), compiledData.m_gpuSamplersDescriptorHandle); } for (uint32_t unboundedArrayIndex = 0; unboundedArrayIndex < ShaderResourceGroupCompiledData::MaxUnboundedArrays; ++unboundedArrayIndex) { - if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid()) + if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid() && + compiledData.m_gpuUnboundedArraysDescriptorHandles[unboundedArrayIndex].ptr) { GetCommandList()->SetComputeRootDescriptorTable( binding.m_unboundedArrayResourceTables[unboundedArrayIndex].GetIndex(), diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp index efa9518ee1..9733045179 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp @@ -50,7 +50,7 @@ namespace AZ void CommandListBase::SetNameInternal(const AZStd::string_view& name) { - AZStd::wstring wname; + AZStd::fixed_wstring<256> wname; AZStd::to_wstring(wname, name.data()); GetCommandList()->SetName(wname.data()); } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp index 9bf25d8bc6..012784d3fc 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp @@ -39,7 +39,7 @@ namespace AZ { Device& device = static_cast(deviceBase); m_currentFrameIndex = 0; - m_frameFences.resize(RHI::Limits::Device::FrameCountMax - 1); + m_frameFences.resize(RHI::Limits::Device::FrameCountMax); for (FenceSet& fences : m_frameFences) { fences.Init(device.GetDevice(), RHI::FenceState::Signaled); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp index 635c66f1cd..1b0df5846c 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp @@ -10,7 +10,9 @@ #include #include #include +#include #include +#include namespace AZ { @@ -40,7 +42,7 @@ namespace AZ for (D3D12_SRV_DIMENSION dimension : validSRVDimensions) { - DescriptorHandle srvDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + DescriptorHandle srvDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); D3D12_SHADER_RESOURCE_VIEW_DESC desc = {}; desc.Format = DXGI_FORMAT_R32_UINT; @@ -62,7 +64,7 @@ namespace AZ for (D3D12_UAV_DIMENSION dimension : UAVDimensions) { - DescriptorHandle uavDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + DescriptorHandle uavDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); D3D12_UNORDERED_ACCESS_VIEW_DESC desc = {}; desc.Format = DXGI_FORMAT_R32_UINT; @@ -75,14 +77,14 @@ namespace AZ void DescriptorContext::CreateNullDescriptorsCBV() { D3D12_CONSTANT_BUFFER_VIEW_DESC constantBufferDesc = {}; - DescriptorHandle cbvDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + DescriptorHandle cbvDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); m_device->CreateConstantBufferView(&constantBufferDesc, GetCpuPlatformHandle(cbvDescriptorHandle)); m_nullDescriptorCBV = cbvDescriptorHandle; } void DescriptorContext::CreateNullDescriptorsSampler() { - m_nullSamplerDescriptor = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + m_nullSamplerDescriptor = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); D3D12_SAMPLER_DESC samplerDesc = {}; samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; @@ -102,7 +104,7 @@ namespace AZ AZ_Assert(platformLimitsDescriptor.get(), "Platform limits information is missing"); m_platformLimitsDescriptor = platformLimitsDescriptor; - + m_allowDescriptorHeapCompaction = m_platformLimitsDescriptor->m_allowDescriptorHeapCompaction; for (const auto& itr : platformLimitsDescriptor->m_descriptorHeapLimits) { for (uint32_t shaderVisibleIdx = 0; shaderVisibleIdx < PlatformLimitsDescriptor::NumHeapFlags; ++shaderVisibleIdx) @@ -114,11 +116,33 @@ namespace AZ if (descriptorCountMax) { - GetPool(static_cast(heapTypeIdx.value()), shaderVisibleIdx).Init(m_device.get(), type, flags, descriptorCountMax); + if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flags)) + { + //Init the two heaps to help support compaction after fragmentation + m_shaderVisibleCbvSrvUavPools[0].Init( + m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, + descriptorCountMax, platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles); + + m_shaderVisibleCbvSrvUavPools[1].Init( + m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, + descriptorCountMax, platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles); + } + else + { + GetPool(static_cast(heapTypeIdx.value()), shaderVisibleIdx).Init(m_device.get(), type, flags, descriptorCountMax, descriptorCountMax); + } } } } - + + if (m_allowDescriptorHeapCompaction) + { + m_backupStaticHandles.Init( + m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, + platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles, + platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles); + } + CreateNullDescriptors(); } @@ -129,7 +153,7 @@ namespace AZ { if (constantBufferView.IsNull()) { - constantBufferView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + constantBufferView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(constantBufferView); @@ -145,7 +169,7 @@ namespace AZ { if (shaderResourceView.IsNull()) { - shaderResourceView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + shaderResourceView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(shaderResourceView); @@ -165,7 +189,7 @@ namespace AZ { if (unorderedAccessView.IsNull()) { - unorderedAccessView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + unorderedAccessView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE unorderedAccessDescriptor = GetCpuPlatformHandle(unorderedAccessView); @@ -176,7 +200,24 @@ namespace AZ // Copy the UAV descriptor into the GPU-visible version for clearing. if (unorderedAccessViewClear.IsNull()) { - unorderedAccessViewClear = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1).GetOffset(); + unorderedAccessViewClear = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1); + + if (unorderedAccessViewClear.IsNull()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory for static handles. Please consider increasing the value of NumShaderVisibleCbvSrvUavStaticHandles" + "within platformlimits.azasset file for dx12."); + return; + } + + if (m_allowDescriptorHeapCompaction) + { + //We make a copy of static handles in case we need to compact and recreate the shader visible heap + m_device->CopyDescriptorsSimple( + 1, m_backupStaticHandles.GetCpuPlatformHandle(unorderedAccessViewClear), unorderedAccessDescriptor, + unorderedAccessViewClear.m_type); + } } CopyDescriptor(unorderedAccessViewClear, unorderedAccessView); } @@ -188,7 +229,7 @@ namespace AZ { if (shaderResourceView.IsNull()) { - shaderResourceView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + shaderResourceView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(shaderResourceView); @@ -205,7 +246,7 @@ namespace AZ { if (unorderedAccessView.IsNull()) { - unorderedAccessView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + unorderedAccessView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE unorderedAccessDescriptor = GetCpuPlatformHandle(unorderedAccessView); @@ -216,7 +257,24 @@ namespace AZ // Copy the UAV descriptor into the GPU-visible version for clearing. if (unorderedAccessViewClear.IsNull()) { - unorderedAccessViewClear = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1).GetOffset(); + unorderedAccessViewClear = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1); + + if (unorderedAccessViewClear.IsNull()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory for static handles. Please consider increasing the value of " + "NumShaderVisibleCbvSrvUavStaticHandles within platformlimits.azasset file for dx12."); + return; + } + + if (m_allowDescriptorHeapCompaction) + { + // We make a copy of static handles in case we need to compact and recreate the shader visible heap + m_device->CopyDescriptorsSimple( + 1, m_backupStaticHandles.GetCpuPlatformHandle(unorderedAccessViewClear), unorderedAccessDescriptor, + unorderedAccessViewClear.m_type); + } } CopyDescriptor(unorderedAccessViewClear, unorderedAccessView); } @@ -228,7 +286,7 @@ namespace AZ { if (renderTargetView.IsNull()) { - renderTargetView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + renderTargetView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor = GetCpuPlatformHandle(renderTargetView); @@ -245,13 +303,13 @@ namespace AZ { if (depthStencilView.IsNull()) { - depthStencilView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + depthStencilView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE depthStencilDescriptor = GetCpuPlatformHandle(depthStencilView); if (depthStencilReadView.IsNull()) { - depthStencilReadView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + depthStencilReadView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_CPU_DESCRIPTOR_HANDLE depthStencilReadDescriptor = GetCpuPlatformHandle(depthStencilReadView); @@ -274,7 +332,7 @@ namespace AZ { if (samplerHandle.IsNull()) { - samplerHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset(); + samplerHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1); } D3D12_SAMPLER_DESC samplerDesc; @@ -286,17 +344,49 @@ namespace AZ { if (!descriptorHandle.IsNull()) { - ReleaseDescriptorTable(DescriptorTable(descriptorHandle, 1)); + GetPool(descriptorHandle.m_type, descriptorHandle.m_flags).ReleaseHandle(descriptorHandle); } } DescriptorTable DescriptorContext::CreateDescriptorTable( - D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, - uint32_t descriptorCount) + D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, uint32_t descriptorCount, ShaderResourceGroup* srg) { - return Allocate(descriptorHeapType, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, descriptorCount); + if (m_allowDescriptorHeapCompaction && !m_compactionInProgress) + { + // Track active SRGs in case we need to compact the shader visible cbv_srv_uav heap + AZStd::scoped_lock lock{ m_srgMapMutex }; + auto iter = m_srgAllocations.find(srg); + if (iter == m_srgAllocations.end()) + { + m_srgAllocations.emplace(srg, 1); + } + else + { + m_srgAllocations[srg]++; + } + } + + return GetPool(descriptorHeapType, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE).AllocateTable(descriptorCount); } + void DescriptorContext::ReleaseDescriptorTable(DescriptorTable table, ShaderResourceGroup* srg) + { + if (m_allowDescriptorHeapCompaction && !m_compactionInProgress) + { + //Track active SRGs in case we need to compact the shader visible cbv_srv_uav heap + AZStd::scoped_lock lock{ m_srgMapMutex }; + auto iter = m_srgAllocations.find(srg); + AZ_Assert(iter != m_srgAllocations.end(), "Srg entry not found"); + m_srgAllocations[srg]--; + if (m_srgAllocations[srg] == 0) + { + m_srgAllocations.erase(srg); + } + } + + GetPool(table.GetType(), table.GetFlags()).ReleaseTable(table); + } + void DescriptorContext::UpdateDescriptorTableRange( DescriptorTable gpuDestinationTable, const DescriptorHandle* cpuSourceDescriptors, @@ -313,14 +403,12 @@ namespace AZ } // Resolve destination descriptor to platform handle. - D3D12_CPU_DESCRIPTOR_HANDLE gpuDestinationHandle = GetCpuPlatformHandle(gpuDestinationTable.GetOffset()); + D3D12_CPU_DESCRIPTOR_HANDLE gpuDestinationHandle = GetCpuPlatformHandleForTable(gpuDestinationTable); // An array of descriptor sizes for each range. We just want N ranges with 1 descriptor each. AZStd::vector rangeCounts(DescriptorCount, 1); - /** - * We are gathering N source descriptors into a contiguous destination table. - */ + //We are gathering N source descriptors into a contiguous destination table. m_device->CopyDescriptors( 1, // Number of destination ranges. &gpuDestinationHandle, // Destination range array. @@ -353,19 +441,24 @@ namespace AZ } } } + + if (m_allowDescriptorHeapCompaction) + { + m_backupStaticHandles.GarbageCollect(); + } } - DescriptorTable DescriptorContext::Allocate( + DescriptorTable DescriptorContext::AllocateTable( D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count) { - return GetPool(type, flags).Allocate(count); + return GetPool(type, flags).AllocateTable(count); } - void DescriptorContext::ReleaseDescriptorTable(DescriptorTable table) + DescriptorHandle DescriptorContext::AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count) { - GetPool(table.GetType(), table.GetFlags()).Release(table); + return GetPool(type, flags).AllocateHandle(count); } D3D12_CPU_DESCRIPTOR_HANDLE DescriptorContext::GetCpuPlatformHandle(DescriptorHandle handle) const @@ -378,6 +471,16 @@ namespace AZ return GetPool(handle.m_type, handle.m_flags).GetGpuPlatformHandle(handle); } + D3D12_CPU_DESCRIPTOR_HANDLE DescriptorContext::GetCpuPlatformHandleForTable(DescriptorTable descTable) const + { + return GetPool(descTable.GetOffset().m_type, descTable.GetOffset().m_flags).GetCpuPlatformHandleForTable(descTable); + } + + D3D12_GPU_DESCRIPTOR_HANDLE DescriptorContext::GetGpuPlatformHandleForTable(DescriptorTable descTable) const + { + return GetPool(descTable.GetOffset().m_type, descTable.GetOffset().m_flags).GetGpuPlatformHandleForTable(descTable); + } + DescriptorHandle DescriptorContext::GetNullHandleSRV(D3D12_SRV_DIMENSION dimension) const { auto iter = m_nullDescriptorsSRV.find(dimension); @@ -431,14 +534,88 @@ namespace AZ { AZ_Assert(type < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES, "Trying to get pool with invalid type: [%d]", type); AZ_Assert(flag < NumHeapFlags, "Trying to get pool with invalid flag: [%d]", flag); - return m_pools[type][flag]; + + if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flag)) + { + return m_shaderVisibleCbvSrvUavPools[m_currentHeapIndex]; + } + else + { + return m_pools[type][flag]; + } } const DescriptorPool& DescriptorContext::GetPool(uint32_t type, uint32_t flag) const { AZ_Assert(type < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES, "Trying to get pool with invalid type: [%d]", type); AZ_Assert(flag < NumHeapFlags, "Trying to get pool with invalid flag: [%d]", flag); - return m_pools[type][flag]; + if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flag)) + { + return m_shaderVisibleCbvSrvUavPools[m_currentHeapIndex]; + } + else + { + return m_pools[type][flag]; + } + } + + RHI::ResultCode DescriptorContext::CompactDescriptorHeap() + { + //Check if heap compaction is enabled by the user. Since there is an overhead associated with heap compaction it is not enabled by default + if(!m_allowDescriptorHeapCompaction) + { + AZ_Assert( + false, + "Descriptor heap Compaction not allowed. Please consider increasing number of handles allowed for the second value" + "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV or enabling AllowDescriptorHeapCompaction within platformlimits.azasset file for dx12."); + return RHI::ResultCode::OutOfMemory; + } + + //We need to ping-pong between two heaps as we cannot compact the active heap without updating it and that is not allowed as + //we need to keep that gpu memory untouched until GPU is finished consuming which can take up to 3 frames. + m_compactionInProgress = true; + DescriptorPool& srcPool = GetPool(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE); + + //Update the currently active heap index + m_currentHeapIndex = !m_currentHeapIndex; + DescriptorPool& destPool = GetPool(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE); + + //Copy over all the static handles first + for (size_t i = 0; i < m_platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles; i++) + { + DescriptorHandle srcHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, static_cast(i)); + DescriptorHandle destHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, static_cast(i)); + m_device->CopyDescriptorsSimple(1, destPool.GetCpuPlatformHandle(destHandle), m_backupStaticHandles.GetCpuPlatformHandle(srcHandle), destHandle.m_type); + } + + //Clone the allocator of the source pool into the destination pool + srcPool.CloneAllocator(destPool.GetAllocator()); + + { + //The mutex is here 'just in case' Compaction is called from more than one thread. + AZStd::scoped_lock lock{ m_srgMapMutex }; + //Re-update all the descriptor tables associated with active SRGs + for (const auto& [srg, numAllocations] : m_srgAllocations) + { + RHI::ResultCode resultCode = static_cast(srg->GetPool())->UpdateDescriptorTableAfterCompaction(*srg, srg->GetData()); + if (resultCode != RHI::ResultCode::Success) + { + return resultCode; + } + } + } + + //Clear the allocator of the source pool + srcPool.ClearAllocator(); + + m_compactionInProgress = false; + + return RHI::ResultCode::Success; + } + + bool DescriptorContext::IsShaderVisibleCbvSrvUavHeap(uint32_t type, uint32_t flag) const + { + return type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV && flag == D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; } } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h index d4b6479fb2..88d2781329 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.h @@ -15,6 +15,8 @@ #include #include #include +#include +#include namespace AZ { @@ -82,12 +84,14 @@ namespace AZ //! Creates a GPU-visible descriptor table. //! @param descriptorHeapType The descriptor heap to allocate from. //! @param descriptorCount The number of descriptors to allocate. + //! @param srg Shader resource group with which the descriptor table is associated with DescriptorTable CreateDescriptorTable( - D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, - uint32_t descriptorCount); - - void ReleaseDescriptorTable(DescriptorTable descriptorTable); + D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, uint32_t descriptorCount, ShaderResourceGroup* srg); + //! Releases a GPU-visible descriptor table. + //! @param descriptorHeapType The descriptor heap to allocate from. + //! @param srg Shader resource group with which the descriptor table is associated with + void ReleaseDescriptorTable(DescriptorTable descriptorTable, ShaderResourceGroup* srg); //! Performs a gather of disjoint CPU-side descriptors and copies to a contiguous GPU-side descriptor table. //! @param gpuDestinationTable The destination descriptor table that the descriptors will be uploaded to. @@ -110,6 +114,8 @@ namespace AZ D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandle(DescriptorHandle handle) const; D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandle(DescriptorHandle handle) const; + D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable descTable) const; + D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable descTable) const; void SetDescriptorHeaps(ID3D12GraphicsCommandList* commandList) const; @@ -117,6 +123,12 @@ namespace AZ ID3D12DeviceX* GetDevice(); + //! Since we are only allowed one shader visible CbvSrvUav heap of a limited size in certain hardware, it is possible that + //! it can get fragmented by constant alloc/de-alloc of descriptor tables related to direct views or unbounded resource views within a SRG. We use two + //! heaps to ping pong during compaction as fragmentation can occur many times. It copies static handles directly and for all the + //! dynamic handles we re-update the new heap by copying over the handles from the 'non-shader visible' heap. + RHI::ResultCode CompactDescriptorHeap(); + private: void CopyDescriptor(DescriptorHandle dst, DescriptorHandle src); @@ -129,10 +141,13 @@ namespace AZ DescriptorPool& GetPool(uint32_t type, uint32_t flag); const DescriptorPool& GetPool(uint32_t type, uint32_t flag) const; - DescriptorTable Allocate( - D3D12_DESCRIPTOR_HEAP_TYPE type, - D3D12_DESCRIPTOR_HEAP_FLAGS flags, - uint32_t count); + //! Allocates a Descriptor table which describes a contiguous range of descriptor handles + DescriptorTable AllocateTable(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count); + + //! Allocates a single descriptor handle + DescriptorHandle AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count); + + bool IsShaderVisibleCbvSrvUavHeap(uint32_t type, uint32_t flag) const; static const uint32_t NumHeapFlags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE + 1; static const uint32_t s_descriptorCountMax[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES][NumHeapFlags]; @@ -147,6 +162,25 @@ namespace AZ DescriptorHandle m_nullSamplerDescriptor; RHI::ConstPtr m_platformLimitsDescriptor; + + // Use 2 heaps below in order to ping-pong between shader visible CbvSrvUav heap when one of them fragments and run out of memory. + static const uint32_t MaxShaderVisibleCbvSrvUavHeaps = 2; + DescriptorPoolShaderVisibleCbvSrvUav m_shaderVisibleCbvSrvUavPools[MaxShaderVisibleCbvSrvUavHeaps]; + //This pool stores a copy of static handles that can later be used to recreate the compacted shader visible CbvSrvUav heap. + DescriptorPool m_backupStaticHandles; + + //Boolean to dictate when compaction was in progress + bool m_compactionInProgress = false; + + //Boolean to dictate if we should support compaction for shader visible CbvSrvUav heap + bool m_allowDescriptorHeapCompaction = false; + + //Map to store active SRGs and the number of associated descriptor tables. This is used to recreate the new compacted heap when we switch heaps + AZStd::unordered_map m_srgAllocations; + AZStd::mutex m_srgMapMutex; + + //Index that holds the currently active shader visible CbvSrvUav heap + uint32_t m_currentHeapIndex = 0; }; } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp index 7a6d96a519..96a3979f41 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.cpp @@ -18,34 +18,38 @@ namespace AZ ID3D12DeviceX* device, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, - uint32_t descriptorCount) + uint32_t descriptorCountForHeap, + uint32_t descriptorCountForAllocator) { - m_Desc.Type = type; - m_Desc.Flags = flags; - m_Desc.NumDescriptors = descriptorCount; - m_Desc.NodeMask = 1; + m_desc.Type = type; + m_desc.Flags = flags; + m_desc.NumDescriptors = descriptorCountForHeap; + m_desc.NodeMask = 1; ID3D12DescriptorHeap* heap; - DX12::AssertSuccess(device->CreateDescriptorHeap(&m_Desc, IID_GRAPHICS_PPV_ARGS(&heap))); + DX12::AssertSuccess(device->CreateDescriptorHeap(&m_desc, IID_GRAPHICS_PPV_ARGS(&heap))); heap->SetName(L"DescriptorHeap"); - m_DescriptorHeap.Attach(heap); - m_Stride = device->GetDescriptorHandleIncrementSize(m_Desc.Type); + m_descriptorHeap.Attach(heap); + m_stride = device->GetDescriptorHandleIncrementSize(m_desc.Type); - m_CpuStart = heap->GetCPUDescriptorHandleForHeapStart(); - m_GpuStart = {}; - - if (RHI::CheckBitsAny(flags, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)) - { - m_GpuStart = heap->GetGPUDescriptorHandleForHeapStart(); - } + m_cpuStart = heap->GetCPUDescriptorHandleForHeapStart(); + m_gpuStart = {}; const bool isGpuVisible = RHI::CheckBitsAll(flags, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE); + if (isGpuVisible) + { + m_gpuStart = heap->GetGPUDescriptorHandleForHeapStart(); + } + if (isGpuVisible) { RHI::FreeListAllocator::Descriptor descriptor; descriptor.m_alignmentInBytes = 1; - descriptor.m_capacityInBytes = descriptorCount; + + //It is possible for descriptorCountForAllocator to not match descriptorCountForHeap for DescriptorPoolShaderVisibleCbvSrvUav + //heaps in which case descriptorCountForAllocator defines the number of static handles + descriptor.m_capacityInBytes = aznumeric_cast(descriptorCountForAllocator); descriptor.m_garbageCollectLatency = RHI::Limits::Device::FrameCountMax; RHI::FreeListAllocator* allocator = aznew RHI::FreeListAllocator(); @@ -56,10 +60,11 @@ namespace AZ { // Non-shader-visible heaps don't require contiguous descriptors. Therefore, we can allocate // them using a block allocator. + RHI::PoolAllocator::Descriptor descriptor; descriptor.m_alignmentInBytes = 1; descriptor.m_elementSize = 1; - descriptor.m_capacityInBytes = descriptorCount; + descriptor.m_capacityInBytes = aznumeric_cast(descriptorCountForAllocator); descriptor.m_garbageCollectLatency = 0; RHI::PoolAllocator* allocator = aznew RHI::PoolAllocator(); @@ -68,7 +73,7 @@ namespace AZ } } - DescriptorTable DescriptorPool::Allocate(uint32_t count) + DescriptorHandle DescriptorPool::AllocateHandle(uint32_t count) { RHI::VirtualAddress address; { @@ -78,24 +83,34 @@ namespace AZ if (address.IsValid()) { - DescriptorHandle handle(m_Desc.Type, m_Desc.Flags, static_cast(address.m_ptr)); - return DescriptorTable(handle, static_cast(count)); + DescriptorHandle handle(m_desc.Type, m_desc.Flags, static_cast(address.m_ptr)); + return handle; } else { - return DescriptorTable{}; + return DescriptorHandle{}; } } - void DescriptorPool::Release(DescriptorTable table) + void DescriptorPool::ReleaseHandle(DescriptorHandle handle) { - if (table.IsNull()) + if (handle.IsNull()) { return; } AZStd::lock_guard lock(m_mutex); - m_allocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(table.GetOffset().m_index)); + m_allocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(handle.m_index)); + } + + DescriptorTable DescriptorPool::AllocateTable(uint32_t count) + { + return DescriptorTable(AllocateHandle(count), static_cast(count)); + } + + void DescriptorPool::ReleaseTable(DescriptorTable table) + { + ReleaseHandle(table.GetOffset()); } void DescriptorPool::GarbageCollect() @@ -106,20 +121,134 @@ namespace AZ ID3D12DescriptorHeap* DescriptorPool::GetPlatformHeap() const { - return m_DescriptorHeap.Get(); + return m_descriptorHeap.Get(); } D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPool::GetCpuPlatformHandle(DescriptorHandle handle) const { AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); - return D3D12_CPU_DESCRIPTOR_HANDLE{ m_CpuStart.ptr + handle.m_index * m_Stride }; + return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + handle.m_index * m_stride }; } D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPool::GetGpuPlatformHandle(DescriptorHandle handle) const { AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible"); AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); - return D3D12_GPU_DESCRIPTOR_HANDLE{ m_GpuStart.ptr + handle.m_index * m_Stride }; + return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (handle.m_index * m_stride) }; + } + + D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPool::GetCpuPlatformHandleForTable(DescriptorTable descTable) const + { + DescriptorHandle handle = descTable.GetOffset(); + AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); + return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + handle.m_index * m_stride }; + } + + D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPool::GetGpuPlatformHandleForTable(DescriptorTable descTable) const + { + DescriptorHandle handle = descTable.GetOffset(); + AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible"); + AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); + return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (handle.m_index * m_stride) }; + } + + void DescriptorPool::CloneAllocator(RHI::Allocator* newAllocator) + { + m_allocator->Clone(newAllocator); + } + + void DescriptorPool::ClearAllocator() + { + AZ_Assert(m_gpuStart.ptr, "Clearing the allocator is only supported for the gpu visible heap as only this heap can be compacted"); + static_cast(m_allocator.get()) + ->Init(static_cast(m_allocator.get())->GetDescriptor()); + } + + RHI::Allocator* DescriptorPool::GetAllocator() const + { + return m_allocator.get(); + } + + void DescriptorPoolShaderVisibleCbvSrvUav::Init( + ID3D12DeviceX* device, + D3D12_DESCRIPTOR_HEAP_TYPE type, + D3D12_DESCRIPTOR_HEAP_FLAGS flags, + uint32_t descriptorCount, + uint32_t staticHandlesCount) + { + //This pool manages two allocators. The allocator in the base class manages static handles + Base::Init(device, type, flags, descriptorCount, staticHandlesCount); + + //This allocator manages dynamic handles associated with descriptor tables. This allows us to + //reconstruct the full heap in a compact manner if it ever fragments. + RHI::FreeListAllocator::Descriptor descriptor; + descriptor.m_alignmentInBytes = 1; + descriptor.m_capacityInBytes = aznumeric_cast(descriptorCount - staticHandlesCount); + descriptor.m_garbageCollectLatency = RHI::Limits::Device::FrameCountMax; + + RHI::FreeListAllocator* allocator = aznew RHI::FreeListAllocator(); + allocator->Init(descriptor); + m_unboundedArrayAllocator.reset(allocator); + + //Cache the starting point of the dynamic section of the heap + m_startingHandleIndex = staticHandlesCount; + } + + DescriptorTable DescriptorPoolShaderVisibleCbvSrvUav::AllocateTable(uint32_t count) + { + RHI::VirtualAddress address; + { + AZStd::lock_guard lock(m_mutex); + address = m_unboundedArrayAllocator->Allocate(count, 1); + } + + if (address.IsValid()) + { + DescriptorHandle handle(m_desc.Type, m_desc.Flags, static_cast(address.m_ptr)); + return DescriptorTable(handle, static_cast(count)); + } + else + { + return DescriptorTable{}; + } + } + + void DescriptorPoolShaderVisibleCbvSrvUav::ReleaseTable(DescriptorTable table) + { + if (table.IsNull()) + { + return; + } + + AZStd::lock_guard lock(m_mutex); + m_unboundedArrayAllocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(table.GetOffset().m_index)); + } + + void DescriptorPoolShaderVisibleCbvSrvUav::GarbageCollect() + { + Base::GarbageCollect(); + m_unboundedArrayAllocator->GarbageCollect(); + } + + D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPoolShaderVisibleCbvSrvUav::GetCpuPlatformHandleForTable(DescriptorTable descTable) const + { + DescriptorHandle handle = descTable.GetOffset(); + AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); + return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + (m_startingHandleIndex * m_stride) + (handle.m_index * m_stride) }; + } + + D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPoolShaderVisibleCbvSrvUav::GetGpuPlatformHandleForTable(DescriptorTable descTable) const + { + DescriptorHandle handle = descTable.GetOffset(); + AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible"); + AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid"); + return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (m_startingHandleIndex * m_stride) + (handle.m_index * m_stride) }; + } + + void DescriptorPoolShaderVisibleCbvSrvUav::ClearAllocator() + { + Base::ClearAllocator(); + static_cast(m_unboundedArrayAllocator.get())->Init(static_cast(m_unboundedArrayAllocator.get())->GetDescriptor()); } } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h index cd90d1aba3..2237841ea7 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorPool.h @@ -18,37 +18,91 @@ namespace AZ { namespace DX12 { + //! This class defines a Descriptor pool which manages all the descriptors used for binding resources class DescriptorPool { public: DescriptorPool() = default; + virtual ~DescriptorPool() = default; - void Init( + //! Initialize the native heap as well as init the allocators tracking the memory for descriptor handles + virtual void Init( ID3D12DeviceX* device, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, - uint32_t descriptorCount); + uint32_t descriptorCountForHeap, + uint32_t descriptorCountForAllocator); ID3D12DescriptorHeap* GetPlatformHeap() const; - DescriptorTable Allocate(uint32_t count = 1); + //! Allocate a Descriptor handles + DescriptorHandle AllocateHandle(uint32_t count = 1); + //! Release a descriptor handle + void ReleaseHandle(DescriptorHandle table); + //! Allocate a range contiguous handles (i.e Descriptor table) + virtual DescriptorTable AllocateTable(uint32_t count = 1); + //! Release a range contiguous handles (i.e Descriptor table) + virtual void ReleaseTable(DescriptorTable table); + //! Garbage collection for freed handles or tables + virtual void GarbageCollect(); + //Get native pointers from the heap + virtual D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable handle) const; + virtual D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable handle) const; + //Clear the tracking allocator + virtual void ClearAllocator(); - void Release(DescriptorTable table); - - void GarbageCollect(); D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandle(DescriptorHandle handle) const; D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandle(DescriptorHandle handle) const; - private: - D3D12_CPU_DESCRIPTOR_HANDLE m_CpuStart = {}; - D3D12_GPU_DESCRIPTOR_HANDLE m_GpuStart = {}; - D3D12_CPU_DESCRIPTOR_HANDLE m_NullDescriptor = {}; - uint32_t m_Stride = 0; - D3D12_DESCRIPTOR_HEAP_DESC m_Desc; - Microsoft::WRL::ComPtr m_DescriptorHeap; + //Clone the tracking allocator + void CloneAllocator(RHI::Allocator* newAllocator); + RHI::Allocator* GetAllocator() const; + + protected: + D3D12_DESCRIPTOR_HEAP_DESC m_desc; AZStd::mutex m_mutex; + D3D12_CPU_DESCRIPTOR_HANDLE m_cpuStart = {}; + D3D12_GPU_DESCRIPTOR_HANDLE m_gpuStart = {}; + uint32_t m_stride = 0; + private: + + // Native heap + Microsoft::WRL::ComPtr m_descriptorHeap; + + // Allocator used to manage the whole native heap. In the case of DescriptorPoolShaderVisibleCbvSrvUav this allocator + // is used to manage the part of the heap that only manages static handles. AZStd::unique_ptr m_allocator; }; + + //! A specialized pool that specifically handles Descriptor tables for Cbv/Srv/Uav views and allows for Compaction + //! Specifically this pool handles the dynamic part of the heap + class DescriptorPoolShaderVisibleCbvSrvUav : public DescriptorPool + { + using Base = DescriptorPool; + + public: + void Init( + ID3D12DeviceX* device, + D3D12_DESCRIPTOR_HEAP_TYPE type, + D3D12_DESCRIPTOR_HEAP_FLAGS flags, + uint32_t descriptorCount, + uint32_t staticHandlesCount); + + DescriptorTable AllocateTable(uint32_t count = 1) override; + void ReleaseTable(DescriptorTable table) override; + void GarbageCollect() override; + + D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable handle) const override; + D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable handle) const override; + void ClearAllocator() override; + + private: + + // A separate allocator that handles descriptor tables which are dynamic in nature and may fragment and require compaction + AZStd::unique_ptr m_unboundedArrayAllocator; + //Starting index of the dynamic part of the heap + uint32_t m_startingHandleIndex = 0; + }; } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp index 82e4aba57c..6f614499d2 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp @@ -625,5 +625,20 @@ namespace AZ { return m_isAftermathInitialized; } + + RHI::ResultCode Device::CompactSRGMemory() + { + if (m_isDescriptorHeapCompactionNeeded) + { + m_isDescriptorHeapCompactionNeeded = false; + return m_descriptorContext->CompactDescriptorHeap(); + } + return RHI::ResultCode::Success; + } + + void Device::DescriptorHeapCompactionNeeded() + { + m_isDescriptorHeapCompactionNeeded = true; + } } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h index 7004900ec5..40e26db891 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Device.h @@ -98,39 +98,27 @@ namespace AZ D3D12_RESOURCE_STATES initialState, ImageTileLayout& imageTilingInfo); - /** - * Queues a DX12 COM object for release (by taking a reference) after the current frame has flushed - * through the GPU. - */ + //! Queues a DX12 COM object for release (by taking a reference) after the current frame has flushed + //! through the GPU. void QueueForRelease(RHI::Ptr dx12Object); - /** - * Queues the backing Memory instance of a MemoryView for release (by taking a reference) after the - * current frame has flushed through the GPU. The reference on the MemoryView itself is not released. - */ + //! Queues the backing Memory instance of a MemoryView for release (by taking a reference) after the + //! current frame has flushed through the GPU. The reference on the MemoryView itself is not released. void QueueForRelease(const MemoryView& memoryView); - /** - * Allocates host memory from the internal frame allocator that is suitable for staging - * uploads to the GPU for the current frame. The memory is valid for the lifetime of - * the frame and is automatically reclaimed after the frame has completed on the GPU. - */ + //! Allocates host memory from the internal frame allocator that is suitable for staging + //! uploads to the GPU for the current frame. The memory is valid for the lifetime of + //! the frame and is automatically reclaimed after the frame has completed on the GPU. MemoryView AcquireStagingMemory(size_t size, size_t alignment); - /** - * Acquires a pipeline layout from the internal cache. - */ + //! Acquires a pipeline layout from the internal cache. RHI::ConstPtr AcquirePipelineLayout(const RHI::PipelineLayoutDescriptor& descriptor); - /** - * Acquires a new command list for the frame given the hardware queue class. The command list is - * automatically reclaimed after the current frame has flushed through the GPU. - */ + //! Acquires a new command list for the frame given the hardware queue class. The command list is + //! automatically reclaimed after the current frame has flushed through the GPU. CommandList* AcquireCommandList(RHI::HardwareQueueClass hardwareQueueClass); - /** - * Acquires a sampler from the internal cache. - */ + //! Acquires a sampler from the internal cache. RHI::ConstPtr AcquireSampler(const RHI::SamplerState& state); const PhysicalDevice& GetPhysicalDevice() const; @@ -146,6 +134,10 @@ namespace AZ AsyncUploadQueue& GetAsyncUploadQueue(); bool IsAftermathInitialized() const; + + //! Indicate that we need to compact the shader visible srv/uav/cbv shader visible heap. + void DescriptorHeapCompactionNeeded(); + private: Device(); @@ -167,6 +159,7 @@ namespace AZ RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::ImageDescriptor & descriptor) override; RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::BufferDescriptor & descriptor) override; void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) override; + RHI::ResultCode CompactSRGMemory() override; ////////////////////////////////////////////////////////////////////////// RHI::ResultCode InitSubPlatform(RHI::PhysicalDevice& physicalDevice); @@ -198,6 +191,9 @@ namespace AZ AZStd::mutex m_samplerCacheMutex; bool m_isAftermathInitialized = false; + + // Boolean used to compact the view specific shader visible heap + bool m_isDescriptorHeapCompactionNeeded = false; }; } } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp index 66b3f9623a..63625fde72 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/PipelineLibrary.cpp @@ -49,9 +49,11 @@ namespace AZ AZStd::array_view bytes; bool shouldCreateLibFromSerializedData = true; - if (RHI::Factory::Get().IsRenderDocModuleLoaded() || RHI::Factory::Get().IsPixModuleLoaded()) + if (RHI::Factory::Get().IsRenderDocModuleLoaded() || + RHI::Factory::Get().IsPixModuleLoaded() || + RHI::Factory::Get().UsingWarpDevice()) { - // CreatePipelineLibrary api does not function properly if Renderdoc or Pix is enabled + // CreatePipelineLibrary api does not function properly if Renderdoc, Pix or Warp is enabled shouldCreateLibFromSerializedData = false; } @@ -215,9 +217,11 @@ namespace AZ RHI::ResultCode PipelineLibrary::MergeIntoInternal([[maybe_unused]] AZStd::array_view pipelineLibraries) { - if (RHI::Factory::Get().IsRenderDocModuleLoaded() || RHI::Factory::Get().IsPixModuleLoaded()) + if (RHI::Factory::Get().IsRenderDocModuleLoaded() || + RHI::Factory::Get().IsPixModuleLoaded() || + RHI::Factory::Get().UsingWarpDevice()) { - // StorePipeline api does not function properly if RenderDoc or Pix is enabled + // StorePipeline api does not function properly if RenderDoc, Pix or Warp is enabled return RHI::ResultCode::Fail; } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h index 4c686fe525..0a7185ccd5 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroup.h @@ -51,6 +51,7 @@ namespace AZ ShaderResourceGroup() = default; friend class ShaderResourceGroupPool; + friend class DescriptorContext; /// The current index into the compiled data array. uint32_t m_compiledDataIndex = 0; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp index d51fdd3495..bd648cf535 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.cpp @@ -132,33 +132,17 @@ namespace AZ compiledData.m_cpuConstantAddress = cpuAddress + m_constantBufferSize * i; } } - - if (m_viewsDescriptorTableSize) - { - group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize); - - if (!group.m_viewsDescriptorTable.IsValid()) - { - AZ_Error("ShaderResourceGroupPool", false, "Descriptor context failed to allocate view descriptor table. Try increasing the limits specified in platformlimits.azasset file for dx12"); - return RHI::ResultCode::OutOfMemory; - } - - for (uint32_t i = 0; i < copyCount; ++i) - { - const DescriptorHandle descriptorHandle = group.m_viewsDescriptorTable.GetOffset() + m_viewsDescriptorTableSize * i; - - ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i]; - compiledData.m_gpuViewsDescriptorHandle = m_descriptorContext->GetGpuPlatformHandle(descriptorHandle); - } - } - + if (m_samplersDescriptorTableSize) { - group.m_samplersDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, m_samplersDescriptorTableRingSize); + group.m_samplersDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, m_samplersDescriptorTableRingSize, &group); if (!group.m_samplersDescriptorTable.IsValid()) { - AZ_Error("ShaderResourceGroupPool", false, "Descriptor context failed to allocate sampler descriptor table. Try increasing the limits specified in platformlimits.azasset file for dx12."); + AZ_Error( + "ShaderResourceGroupPool", false, + "Descriptor context failed to allocate sampler descriptor table. Please consider increasing number of handles " + "allowed for the second value of DESCRIPTOR_HEAP_TYPE_SAMPLER within platformlimits.azasset file for dx12."); return RHI::ResultCode::OutOfMemory; } @@ -167,7 +151,7 @@ namespace AZ const DescriptorHandle descriptorHandle = group.m_samplersDescriptorTable.GetOffset() + m_samplersDescriptorTableSize * i; ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i]; - compiledData.m_gpuSamplersDescriptorHandle = m_descriptorContext->GetGpuPlatformHandle(descriptorHandle); + compiledData.m_gpuSamplersDescriptorHandle = m_descriptorContext->GetGpuPlatformHandleForTable(DescriptorTable(descriptorHandle, static_cast(m_samplersDescriptorTableSize))); } } @@ -186,19 +170,25 @@ namespace AZ if (m_viewsDescriptorTableSize) { - m_descriptorContext->ReleaseDescriptorTable(group.m_viewsDescriptorTable); + if (group.m_viewsDescriptorTable.IsValid()) + { + m_descriptorContext->ReleaseDescriptorTable(group.m_viewsDescriptorTable, &group); + } } if (m_samplersDescriptorTableSize) { - m_descriptorContext->ReleaseDescriptorTable(group.m_samplersDescriptorTable); + if (group.m_viewsDescriptorTable.IsValid()) + { + m_descriptorContext->ReleaseDescriptorTable(group.m_samplersDescriptorTable, &group); + } } for (uint32_t unboundedArrayindex = 0; unboundedArrayindex < (ShaderResourceGroupCompiledData::MaxUnboundedArrays * RHI::Limits::Device::FrameCountMax); ++unboundedArrayindex) { if (group.m_unboundedDescriptorTables[unboundedArrayindex].IsValid()) { - m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[unboundedArrayindex]); + m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[unboundedArrayindex], &group); } } @@ -213,6 +203,7 @@ namespace AZ const RHI::ShaderResourceGroupData& groupData) { ShaderResourceGroup& group = static_cast(groupBase); + auto& device = static_cast(GetDevice()); group.m_compiledDataIndex = (group.m_compiledDataIndex + 1) % RHI::Limits::Device::FrameCountMax; if (m_constantBufferSize) @@ -222,6 +213,22 @@ namespace AZ if (m_viewsDescriptorTableSize) { + //Lazy initialization for cbv/srv/uav Descriptor Tables + if (!group.m_viewsDescriptorTable.IsValid()) + { + group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize, &group); + + if (!group.m_viewsDescriptorTable.IsValid()) + { + //We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that. + device.DescriptorHeapCompactionNeeded(); + return RHI::ResultCode::Success; + } + + CacheGpuHandlesForViews(group); + } + const DescriptorTable descriptorTable( group.m_viewsDescriptorTable.GetOffset() + group.m_compiledDataIndex * m_viewsDescriptorTableSize, static_cast(m_viewsDescriptorTableSize)); @@ -246,6 +253,18 @@ namespace AZ return RHI::ResultCode::Success; } + void ShaderResourceGroupPool::CacheGpuHandlesForViews(ShaderResourceGroup& group) + { + for (uint32_t i = 0; i < RHI::Limits::Device::FrameCountMax; ++i) + { + const DescriptorHandle descriptorHandle = group.m_viewsDescriptorTable.GetOffset() + m_viewsDescriptorTableSize * i; + + ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i]; + compiledData.m_gpuViewsDescriptorHandle = m_descriptorContext->GetGpuPlatformHandleForTable( + DescriptorTable(descriptorHandle, static_cast(m_viewsDescriptorTableSize))); + } + } + void ShaderResourceGroupPool::UpdateViewsDescriptorTable(DescriptorTable descriptorTable, const RHI::ShaderResourceGroupData& groupData) { const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout(); @@ -261,27 +280,27 @@ namespace AZ AZStd::vector descriptorHandles; switch (descriptorRangeType) { - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: - { - descriptorHandles = GetSRVsFromImageViews< RHI::BufferView, BufferView> (bufferViews, D3D12_SRV_DIMENSION_BUFFER); - break; + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: + { + descriptorHandles = GetSRVsFromImageViews< RHI::BufferView, BufferView> (bufferViews, D3D12_SRV_DIMENSION_BUFFER); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + { + descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: + { + descriptorHandles = GetCBVsFromBufferViews(bufferViews); + break; + } + default: + AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); + break; } - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: - { - descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER); - break; - } - case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: - { - descriptorHandles = GetCBVsFromBufferViews(bufferViews); - break; - } - default: - AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); - break; - } - UpdateDescriptorTableRange(descriptorTable, descriptorHandles, bufferInputIndex); + UpdateDescriptorTableRange(descriptorTable, descriptorHandles, bufferInputIndex); ++shaderInputIndex; } @@ -297,23 +316,24 @@ namespace AZ AZStd::vector descriptorHandles; switch (descriptorRangeType) { - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: - { - descriptorHandles = GetSRVsFromImageViews(imageViews, ConvertSRVDimension(shaderInputImage.m_type)); - break; - } - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: - { - descriptorHandles = GetUAVsFromImageViews(imageViews, ConvertUAVDimension(shaderInputImage.m_type)); - break; - } - default: + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: + { + descriptorHandles = + GetSRVsFromImageViews(imageViews, ConvertSRVDimension(shaderInputImage.m_type)); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + { + descriptorHandles = + GetUAVsFromImageViews(imageViews, ConvertUAVDimension(shaderInputImage.m_type)); + break; + } + default: AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); break; } UpdateDescriptorTableRange(descriptorTable, descriptorHandles, imageInputIndex); - ++shaderInputIndex; } } @@ -334,7 +354,7 @@ namespace AZ void ShaderResourceGroupPool::UpdateUnboundedArrayDescriptorTables(ShaderResourceGroup& group, const RHI::ShaderResourceGroupData& groupData) { const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout(); - + auto& device = static_cast(GetDevice()); uint32_t shaderInputIndex = 0; // process buffer unbounded arrays @@ -350,50 +370,30 @@ namespace AZ { if (group.m_unboundedDescriptorTables[tableIndex].IsValid()) { - m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex]); + m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex], &group); group.m_unboundedDescriptorTables[tableIndex] = DescriptorTable{}; } if (!bufferViews.empty()) { - group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(bufferViews.size())); - AZ_Assert(group.m_unboundedDescriptorTables[tableIndex].IsValid(), "Descriptor context failed to allocate unbounded array descriptor table, most likely out of memory."); + group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(bufferViews.size()), &group); + + if (!group.m_unboundedDescriptorTables[tableIndex].IsValid()) + { + // We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that. + device.DescriptorHeapCompactionNeeded(); + return; + } ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex]; - compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandle(group.m_unboundedDescriptorTables[tableIndex].GetOffset()); + compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]); } } - - ++shaderInputIndex; - - if (bufferViews.empty()) - { - // we don't need to update descriptors since the buffer list is empty - continue; - } - - D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputBufferAccess(shaderInputBufferUnboundedArray.m_access); - - AZStd::vector descriptorHandles; - switch (descriptorRangeType) - { - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: - { - descriptorHandles = GetSRVsFromImageViews(bufferViews, D3D12_SRV_DIMENSION_BUFFER); - break; - } - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: - { - descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER); - break; - } - default: - AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); - break; - } - + const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(bufferViews.size())); - m_descriptorContext->UpdateDescriptorTableRange(descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + UpdateUnboundedBuffersDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputBufferUnboundedArray.m_access); + ++shaderInputIndex; } // process image unbounded arrays @@ -407,55 +407,223 @@ namespace AZ // resize the descriptor table allocation if necessary if (group.m_unboundedDescriptorTables[tableIndex].GetSize() != imageViews.size()) { + if (group.m_unboundedDescriptorTables[tableIndex].IsValid()) { - m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex]); + m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex], &group); group.m_unboundedDescriptorTables[tableIndex] = DescriptorTable{}; } if (!imageViews.empty()) { - group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(imageViews.size())); - AZ_Assert(group.m_unboundedDescriptorTables[tableIndex].IsValid(), "Descriptor context failed to allocate unbounded array descriptor table, most likely out of memory."); + group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(imageViews.size()), &group); + + if (!group.m_unboundedDescriptorTables[tableIndex].IsValid()) + { + // We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that + device.DescriptorHeapCompactionNeeded(); + return; + } ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex]; - compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandle(group.m_unboundedDescriptorTables[tableIndex].GetOffset()); + compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]); } } - ++shaderInputIndex; - - if (imageViews.empty()) - { - // we don't need to update descriptors since the image list is empty - continue; - } - - D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputImageAccess(shaderInputImageUnboundedArray.m_access); - - AZStd::vector descriptorHandles; - switch (descriptorRangeType) - { - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: - { - descriptorHandles = GetSRVsFromImageViews(imageViews, ConvertSRVDimension(shaderInputImageUnboundedArray.m_type)); - break; - } - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: - { - descriptorHandles = GetUAVsFromImageViews(imageViews, ConvertUAVDimension(shaderInputImageUnboundedArray.m_type)); - break; - } - default: - AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); - break; - } - const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(imageViews.size())); - m_descriptorContext->UpdateDescriptorTableRange(descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + UpdateUnboundedImagesDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputImageUnboundedArray.m_access, shaderInputImageUnboundedArray.m_type); + ++shaderInputIndex; } } + void ShaderResourceGroupPool::UpdateUnboundedBuffersDescTable( + DescriptorTable descriptorTable, + const RHI::ShaderResourceGroupData& groupData, + uint32_t shaderInputIndex, + RHI::ShaderInputBufferAccess bufferAccess) + { + const RHI::ShaderInputBufferUnboundedArrayIndex bufferUnboundedArrayInputIndex(shaderInputIndex); + AZStd::array_view> bufferViews = + groupData.GetBufferViewUnboundedArray(bufferUnboundedArrayInputIndex); + + if (bufferViews.empty()) + { + // we don't need to update descriptors since the buffer list is empty + return; + } + + D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputBufferAccess(bufferAccess); + + AZStd::vector descriptorHandles; + switch (descriptorRangeType) + { + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: + { + descriptorHandles = GetSRVsFromImageViews(bufferViews, D3D12_SRV_DIMENSION_BUFFER); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + { + descriptorHandles = GetUAVsFromImageViews(bufferViews, D3D12_UAV_DIMENSION_BUFFER); + break; + } + default: + AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); + break; + } + + m_descriptorContext->UpdateDescriptorTableRange( + descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + } + + void ShaderResourceGroupPool::UpdateUnboundedImagesDescTable( + DescriptorTable descriptorTable, + const RHI::ShaderResourceGroupData& groupData, + uint32_t shaderInputIndex, + RHI::ShaderInputImageAccess imageAccess, + RHI::ShaderInputImageType imageType) + { + const RHI::ShaderInputImageUnboundedArrayIndex imageUnboundedArrayInputIndex(shaderInputIndex); + AZStd::array_view> imageViews = + groupData.GetImageViewUnboundedArray(imageUnboundedArrayInputIndex); + + if (imageViews.empty()) + { + // we don't need to update descriptors since the image list is empty + return; + } + + D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputImageAccess(imageAccess); + + AZStd::vector descriptorHandles; + switch (descriptorRangeType) + { + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: + { + descriptorHandles = GetSRVsFromImageViews(imageViews, ConvertSRVDimension(imageType)); + break; + } + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + { + descriptorHandles = GetUAVsFromImageViews(imageViews, ConvertUAVDimension(imageType)); + break; + } + default: + AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration"); + break; + } + + m_descriptorContext->UpdateDescriptorTableRange( + descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + } + + RHI::ResultCode ShaderResourceGroupPool::UpdateDescriptorTableAfterCompaction( + RHI::ShaderResourceGroup& groupBase, const RHI::ShaderResourceGroupData& groupData) + { + // Since we are trying to compact we will re-create all the descriptor tables and re-update them all + ShaderResourceGroup& group = static_cast(groupBase); + + if (m_viewsDescriptorTableSize) + { + group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize, &group); + + if (!group.m_viewsDescriptorTable.IsValid()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value" + "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12."); + return RHI::ResultCode::OutOfMemory; + } + + CacheGpuHandlesForViews(group); + + const DescriptorTable descriptorTable( + group.m_viewsDescriptorTable.GetOffset() + group.m_compiledDataIndex * m_viewsDescriptorTableSize, + static_cast(m_viewsDescriptorTableSize)); + + UpdateViewsDescriptorTable(descriptorTable, groupData); + } + + if (m_unboundedArrayCount) + { + //Reset all the old descriptor tables as the previous heap is gone. + for (uint32_t unboundedArrayindex = 0; unboundedArrayindex < (ShaderResourceGroupCompiledData::MaxUnboundedArrays * RHI::Limits::Device::FrameCountMax); ++unboundedArrayindex) + { + group.m_unboundedDescriptorTables[unboundedArrayindex] = DescriptorTable{}; + } + + const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout(); + uint32_t shaderInputIndex = 0; + + // process buffer unbounded arrays + for (const RHI::ShaderInputBufferUnboundedArrayDescriptor& shaderInputBufferUnboundedArray : groupLayout.GetShaderInputListForBufferUnboundedArrays()) + { + const RHI::ShaderInputBufferUnboundedArrayIndex bufferUnboundedArrayInputIndex(shaderInputIndex); + AZStd::array_view> bufferViews = groupData.GetBufferViewUnboundedArray(bufferUnboundedArrayInputIndex); + + uint32_t tableIndex = shaderInputIndex * RHI::Limits::Device::FrameCountMax + group.m_compiledDataIndex; + if (!bufferViews.empty()) + { + group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(bufferViews.size()), &group); + + if (!group.m_unboundedDescriptorTables[tableIndex].IsValid()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value" + "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12."); + return RHI::ResultCode::OutOfMemory; + } + + ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex]; + compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]); + + const DescriptorTable descriptorTable( + group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(bufferViews.size())); + UpdateUnboundedBuffersDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputBufferUnboundedArray.m_access); + } + shaderInputIndex++; + } + + // process image unbounded arrays + for (const RHI::ShaderInputImageUnboundedArrayDescriptor& shaderInputImageUnboundedArray : + groupLayout.GetShaderInputListForImageUnboundedArrays()) + { + const RHI::ShaderInputImageUnboundedArrayIndex imageUnboundedArrayInputIndex(shaderInputIndex); + AZStd::array_view> imageViews = + groupData.GetImageViewUnboundedArray(imageUnboundedArrayInputIndex); + + uint32_t tableIndex = shaderInputIndex * RHI::Limits::Device::FrameCountMax + group.m_compiledDataIndex; + if (!imageViews.empty()) + { + group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast(imageViews.size()), &group); + + if (!group.m_unboundedDescriptorTables[tableIndex].IsValid()) + { + AZ_Assert( + false, + "Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value" + "of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12."); + return RHI::ResultCode::OutOfMemory; + } + + ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex]; + compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]); + + const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast(imageViews.size())); + UpdateUnboundedImagesDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputImageUnboundedArray.m_access, shaderInputImageUnboundedArray.m_type); + } + shaderInputIndex++; + } + } + return RHI::ResultCode::Success; + } + void ShaderResourceGroupPool::OnFrameEnd() { m_constantAllocator.GarbageCollect(); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h index bbe7e6596d..e1b2145097 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/ShaderResourceGroupPool.h @@ -30,6 +30,9 @@ namespace AZ static RHI::Ptr Create(); + //! Re-Update the descriptor tables for all the cbv/srv/uav views (direct and via unbounded array) + RHI::ResultCode UpdateDescriptorTableAfterCompaction(RHI::ShaderResourceGroup& groupBase, const RHI::ShaderResourceGroupData& groupData); + private: ShaderResourceGroupPool() = default; @@ -51,6 +54,21 @@ namespace AZ void UpdateSamplersDescriptorTable(DescriptorTable descriptorTable, const RHI::ShaderResourceGroupData& groupData); void UpdateUnboundedArrayDescriptorTables(ShaderResourceGroup& group, const RHI::ShaderResourceGroupData& groupData); + //! Update all the buffer views for the unbounded array + void UpdateUnboundedBuffersDescTable( + DescriptorTable descriptorTable, + const RHI::ShaderResourceGroupData& groupData, + uint32_t shaderInputIndex, + RHI::ShaderInputBufferAccess bufferAccess); + + //! Update all the image views for the unbounded array + void UpdateUnboundedImagesDescTable( + DescriptorTable descriptorTable, + const RHI::ShaderResourceGroupData& groupData, + uint32_t shaderInputIndex, + RHI::ShaderInputImageAccess imageAccess, + RHI::ShaderInputImageType imageType); + void UpdateDescriptorTableRange( DescriptorTable descriptorTable, const AZStd::vector& descriptors, @@ -66,6 +84,9 @@ namespace AZ RHI::ShaderInputSamplerIndex samplerIndex, AZStd::array_view samplerStates); + //Cache all the gpu handles for the Descriptor tables related to all the views + void CacheGpuHandlesForViews(ShaderResourceGroup& group); + DescriptorTable GetBufferTable(DescriptorTable descriptorTable, RHI::ShaderInputBufferIndex bufferIndex) const; DescriptorTable GetBufferTableUnbounded(DescriptorTable descriptorTable, RHI::ShaderInputBufferIndex bufferIndex) const; DescriptorTable GetImageTable(DescriptorTable descriptorTable, RHI::ShaderInputImageIndex imageIndex) const; @@ -79,7 +100,6 @@ namespace AZ AZStd::vector GetCBVsFromBufferViews(const AZStd::array_view>& bufferViews); - AZStd::mutex m_constantAllocatorMutex; MemoryPoolSubAllocator m_constantAllocator; DescriptorContext* m_descriptorContext = nullptr; uint32_t m_constantBufferSize = 0; diff --git a/Gems/Atom/RHI/Registry/Platform/Windows/PlatformLimits.setreg b/Gems/Atom/RHI/Registry/Platform/Windows/PlatformLimits.setreg index 240f8b041c..22b7d1bf29 100644 --- a/Gems/Atom/RHI/Registry/Platform/Windows/PlatformLimits.setreg +++ b/Gems/Atom/RHI/Registry/Platform/Windows/PlatformLimits.setreg @@ -21,11 +21,13 @@ "$type": "AZ::DX12::PlatformLimitsDescriptor", "DescriptorHeapLimits": { - "DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [1000000, 1000000], + "DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [100000, 1000000], "DESCRIPTOR_HEAP_TYPE_SAMPLER": [2048, 2048], "DESCRIPTOR_HEAP_TYPE_RTV": [2048, 0], "DESCRIPTOR_HEAP_TYPE_DSV": [2048, 0] - } + }, + "NumShaderVisibleCbvSrvUavStaticHandles": 2000, + "AllowDescriptorHeapCompaction": false }, "vulkan": { diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp index a818599b0d..7d70d0245f 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp @@ -10,6 +10,10 @@ #include #include +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB +#include +#endif + namespace AZ { namespace Vulkan @@ -21,7 +25,7 @@ namespace AZ #if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB xcb_connection_t* xcb_connection = nullptr; - if (auto xcbConnectionManager = AzFramework::LinuxXcbConnectionManagerInterface::Get(); + if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get(); xcbConnectionManager != nullptr) { xcb_connection = xcbConnectionManager->GetXcbConnection(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantAsset.h index 1462490884..66bbd7b188 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderVariantAsset.h @@ -40,7 +40,7 @@ namespace AZ //! by ShaderVariantAssetBuilder this is 1+. static uint32_t MakeAssetProductSubId( uint32_t rhiApiUniqueIndex, uint32_t supervariantIndex, ShaderVariantStableId variantStableId, - uint32_t subProductType = ShaderVariantAssetSubProductType); + uint32_t subProductType = 0); ShaderVariantAsset() = default; ~ShaderVariantAsset() = default; diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Header.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja similarity index 100% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Header.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Source.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja similarity index 100% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Source.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Common.jinja similarity index 100% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Common.jinja diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja similarity index 100% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja similarity index 100% rename from Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja rename to Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index 3516255c7d..7782444a6e 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -51,11 +51,11 @@ set(FILES Include/Multiplayer/NetworkTime/RewindableObject.inl Include/Multiplayer/Physics/PhysicsUtils.h Include/Multiplayer/ReplicationWindows/IReplicationWindow.h - Source/AutoGen/AutoComponentTypes_Header.jinja - Source/AutoGen/AutoComponentTypes_Source.jinja - Source/AutoGen/AutoComponent_Common.jinja - Source/AutoGen/AutoComponent_Header.jinja - Source/AutoGen/AutoComponent_Source.jinja + Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja + Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja + Include/Multiplayer/AutoGen/AutoComponent_Common.jinja + Include/Multiplayer/AutoGen/AutoComponent_Header.jinja + Include/Multiplayer/AutoGen/AutoComponent_Source.jinja Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml Source/AutoGen/Multiplayer.AutoPackets.xml Source/AutoGen/MultiplayerEditor.AutoPackets.xml diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp index 7e3f5f7d24..ca57824b78 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp @@ -652,8 +652,13 @@ namespace ScriptCanvas return {}; } - bool Method::GetBehaviorContextClassMethod(const AZStd::string&, const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const + bool Method::GetBehaviorContextClassMethod(const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const { + if (m_lookupName.empty() && m_className.empty()) + { + return false; + } + AZStd::string prettyClassName; AZStd::string methodName = m_lookupName; @@ -749,15 +754,14 @@ namespace ScriptCanvas AZStd::tuple Method::LookupMethod() const { using TupleType = AZStd::tuple; - AZStd::string methodName = m_lookupName; - + AZStd::string prettyClassName; const AZ::BehaviorClass* bcClass{}; const AZ::BehaviorMethod* method{}; EventType eventType; - if (GetBehaviorContextClassMethod(m_lookupName, bcClass, method, eventType)) + if (GetBehaviorContextClassMethod(bcClass, method, eventType)) { return TupleType{ method, m_methodType, eventType, bcClass }; } @@ -776,7 +780,7 @@ namespace ScriptCanvas const AZ::BehaviorMethod* method{}; EventType eventType; - if (GetBehaviorContextClassMethod(m_lookupName, bcClass, method, eventType)) + if (GetBehaviorContextClassMethod(bcClass, method, eventType)) { m_eventType = eventType; ConfigureMethod(*method, bcClass); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h index 1f1daddc71..8a8dc09856 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h @@ -168,7 +168,7 @@ namespace ScriptCanvas AZ_INLINE void SetWarnOnMissingFunction(bool enabled) { m_warnOnMissingFunction = enabled; } - bool GetBehaviorContextClassMethod(const AZStd::string& name, const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const; + bool GetBehaviorContextClassMethod(const AZ::BehaviorClass*& outClass, const AZ::BehaviorMethod*& outMethod, EventType& outType) const; private: friend struct ScriptCanvas::BehaviorContextMethodHelper; diff --git a/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp index afaf0470b4..5357ccce32 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp @@ -13,8 +13,6 @@ #include #include -#include -#include #include namespace Terrain @@ -34,8 +32,6 @@ namespace Terrain ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ; } - - Terrain::TerrainFeatureProcessor::Reflect(context); } } @@ -49,9 +45,8 @@ namespace Terrain incompatible.push_back(AZ_CRC_CE("TerrainService")); } - void TerrainSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + void TerrainSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) { - required.push_back(AZ_CRC_CE("RPISystem")); } void TerrainSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) @@ -68,14 +63,11 @@ namespace Terrain // every time an entity is added or removed to a level. If this ever changes, the Terrain System ownership could move into // the level component. m_terrainSystem = new TerrainSystem(); - AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); } void TerrainSystemComponent::Deactivate() { delete m_terrainSystem; m_terrainSystem = nullptr; - - AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); } } diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp index 7233d08281..e140129563 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp @@ -95,6 +95,8 @@ namespace Terrain AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId()); AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); + + RefreshCachedWireframeGrid(AZ::Aabb::CreateNull()); } void TerrainWorldDebuggerComponent::Deactivate() diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp new file mode 100644 index 0000000000..3bf34fa019 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp @@ -0,0 +1,213 @@ +/* + * 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 Terrain +{ + void TerrainWorldRendererConfig::Reflect(AZ::ReflectContext* context) + { + Terrain::TerrainFeatureProcessor::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class()->Version(1); + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("Terrain World Renderer Component", "Enables terrain rendering") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC_CE("Level") })) + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true); + } + } + } + + void TerrainWorldRendererComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainRendererService")); + } + + void TerrainWorldRendererComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainRendererService")); + } + + void TerrainWorldRendererComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainService")); + } + + void TerrainWorldRendererComponent::Reflect(AZ::ReflectContext* context) + { + TerrainWorldRendererConfig::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class()->Version(0)->Field( + "Configuration", &TerrainWorldRendererComponent::m_configuration); + } + } + + TerrainWorldRendererComponent::TerrainWorldRendererComponent(const TerrainWorldRendererConfig& configuration) + : m_configuration(configuration) + { + } + + TerrainWorldRendererComponent::~TerrainWorldRendererComponent() + { + if (m_terrainRendererActive) + { + Deactivate(); + } + } + + AZ::RPI::Scene* TerrainWorldRendererComponent::GetScene() const + { + // Find the entity context for the entity ID. + AzFramework::EntityContextId entityContextId = AzFramework::EntityContextId::CreateNull(); + AzFramework::EntityIdContextQueryBus::EventResult( + entityContextId, GetEntityId(), &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId); + + return AZ::RPI::Scene::GetSceneForEntityContextId(entityContextId); + } + + void TerrainWorldRendererComponent::Activate() + { + // On component activation, register the terrain feature processor with Atom and the scene related to this entity context. + + AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); + + if (AZ::RPI::Scene* scene = GetScene(); scene) + { + m_terrainFeatureProcessor = scene->EnableFeatureProcessor(); + } + + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); + m_terrainRendererActive = true; + } + + void TerrainWorldRendererComponent::Deactivate() + { + // On component deactivation, unregister the feature processor and remove it from the default scene. + + m_terrainRendererActive = false; + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); + + if (AZ::RPI::Scene* scene = GetScene(); scene) + { + if (scene->GetFeatureProcessor()) + { + scene->DisableFeatureProcessor(); + } + } + m_terrainFeatureProcessor = nullptr; + + AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); + } + + bool TerrainWorldRendererComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) + { + if (auto config = azrtti_cast(baseConfig)) + { + m_configuration = *config; + return true; + } + return false; + } + + bool TerrainWorldRendererComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + { + if (auto config = azrtti_cast(outBaseConfig)) + { + *config = m_configuration; + return true; + } + return false; + } + + void TerrainWorldRendererComponent::OnTerrainDataDestroyBegin() + { + // If the terrain is being destroyed, remove all existing terrain data from the feature processor. + + if (m_terrainFeatureProcessor) + { + m_terrainFeatureProcessor->RemoveTerrainData(); + } + } + + void TerrainWorldRendererComponent::OnTerrainDataChanged([[maybe_unused]] const AZ::Aabb& dirtyRegion, [[maybe_unused]] TerrainDataChangedMask dataChangedMask) + { + // Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus). + // We lock our surface data mutex *before* checking / setting "isRequestInProgress" so that we prevent race conditions + // that create false detection of cyclic dependencies when multiple requests occur on different threads simultaneously. + // (One case where this was previously able to occur was in rapid updating of the Preview widget on the + // GradientSurfaceDataComponent in the Editor when moving the threshold sliders back and forth rapidly) + auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false); + typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex); + + AZ::Vector2 queryResolution = AZ::Vector2(1.0f); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution); + + AZ::Aabb worldBounds = AZ::Aabb::CreateNull(); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + worldBounds, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb); + + + AZ::Transform transform = AZ::Transform::CreateTranslation(worldBounds.GetCenter()); + + uint32_t width = aznumeric_cast( + (float)worldBounds.GetXExtent() / queryResolution.GetX()); + uint32_t height = aznumeric_cast( + (float)worldBounds.GetYExtent() / queryResolution.GetY()); + AZStd::vector pixels; + pixels.resize_no_construct(width * height); + const uint32_t pixelDataSize = width * height * sizeof(float); + memset(pixels.data(), 0, pixelDataSize); + + for (uint32_t y = 0; y < height; y++) + { + for (uint32_t x = 0; x < width; x++) + { + bool terrainExists = true; + float terrainHeight = 0.0f; + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + terrainHeight, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, + (x * queryResolution.GetX()) + worldBounds.GetMin().GetX(), + (y * queryResolution.GetY()) + worldBounds.GetMin().GetY(), + AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, + &terrainExists); + + pixels[(y * width) + x] = + (terrainHeight - worldBounds.GetMin().GetZ()) / worldBounds.GetExtents().GetZ(); + } + } + + if (m_terrainFeatureProcessor) + { + m_terrainFeatureProcessor->UpdateTerrainData(transform, worldBounds, queryResolution.GetX(), width, height, pixels); + } + } + +} diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h new file mode 100644 index 0000000000..a1e9498be8 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h @@ -0,0 +1,75 @@ +/* + * 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 + +namespace LmbrCentral +{ + template + class EditorWrappedComponentBase; +} + +namespace AZ::RPI +{ + class Scene; +} + +namespace Terrain +{ + class TerrainFeatureProcessor; + + class TerrainWorldRendererConfig + : public AZ::ComponentConfig + { + public: + AZ_CLASS_ALLOCATOR(TerrainWorldRendererConfig, AZ::SystemAllocator, 0); + AZ_RTTI(TerrainWorldRendererConfig, "{08C5863C-092D-4A69-8226-4978E4F6E343}", AZ::ComponentConfig); + static void Reflect(AZ::ReflectContext* context); + }; + + + class TerrainWorldRendererComponent + : public AZ::Component + , public AzFramework::Terrain::TerrainDataNotificationBus::Handler + { + public: + template + friend class LmbrCentral::EditorWrappedComponentBase; + AZ_COMPONENT(TerrainWorldRendererComponent, "{3B0DB71E-5944-437C-8C88-70F8B405BFC7}"); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void Reflect(AZ::ReflectContext* context); + + TerrainWorldRendererComponent(const TerrainWorldRendererConfig& configuration); + TerrainWorldRendererComponent() = default; + ~TerrainWorldRendererComponent() override; + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override; + bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; + + protected: + void OnTerrainDataDestroyBegin() override; + void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; + + AZ::RPI::Scene* GetScene() const; + + private: + TerrainWorldRendererConfig m_configuration; + bool m_terrainRendererActive{ false }; + TerrainFeatureProcessor* m_terrainFeatureProcessor{ nullptr }; + }; +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.cpp b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.cpp new file mode 100644 index 0000000000..2d42585fa5 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.cpp @@ -0,0 +1,56 @@ +/* + * 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 + +namespace Terrain +{ + void EditorTerrainWorldRendererComponent::Reflect(AZ::ReflectContext* context) + { + BaseClassType::Reflect(context); + + AZ::SerializeContext* serializeContext = azrtti_cast(context); + + if (serializeContext) + { + serializeContext->Class() + ->Version(0) + ; + + if (auto editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Terrain World Renderer", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Terrain") + ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/TerrainWorldRenderer.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/TerrainWorldRenderer.svg") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC_CE("Level") })) + ; + } + } + } + + + void EditorTerrainWorldRendererComponent::Init() + { + BaseClassType::Init(); + } + + void EditorTerrainWorldRendererComponent::Activate() + { + BaseClassType::Activate(); + } + + AZ::u32 EditorTerrainWorldRendererComponent::ConfigurationChanged() + { + return BaseClassType::ConfigurationChanged(); + } +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.h b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.h new file mode 100644 index 0000000000..92ce648988 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldRendererComponent.h @@ -0,0 +1,38 @@ +/* + * 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 + +namespace Terrain +{ + class EditorTerrainWorldRendererComponent + : public LmbrCentral::EditorWrappedComponentBase + { + public: + using BaseClassType = LmbrCentral::EditorWrappedComponentBase; + AZ_EDITOR_COMPONENT(EditorTerrainWorldRendererComponent, "{7BEFF763-89A6-4EDA-B199-B049A8E757AF}", BaseClassType); + static void Reflect(AZ::ReflectContext* context); + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Init() override; + void Activate() override; + AZ::u32 ConfigurationChanged() override; + + protected: + using BaseClassType::m_configuration; + using BaseClassType::m_component; + using BaseClassType::m_visible; + + private: + }; +} diff --git a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp index 0ba416cbb7..5305087694 100644 --- a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp +++ b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace Terrain { @@ -25,6 +26,7 @@ namespace Terrain Terrain::EditorTerrainSystemComponent::CreateDescriptor(), Terrain::EditorTerrainWorldComponent::CreateDescriptor(), Terrain::EditorTerrainWorldDebuggerComponent::CreateDescriptor(), + Terrain::EditorTerrainWorldRendererComponent::CreateDescriptor(), }); } diff --git a/Gems/Terrain/Code/Source/TerrainModule.cpp b/Gems/Terrain/Code/Source/TerrainModule.cpp index 29b2542472..24b87b4ab3 100644 --- a/Gems/Terrain/Code/Source/TerrainModule.cpp +++ b/Gems/Terrain/Code/Source/TerrainModule.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ namespace Terrain TerrainSystemComponent::CreateDescriptor(), TerrainWorldComponent::CreateDescriptor(), TerrainWorldDebuggerComponent::CreateDescriptor(), + TerrainWorldRendererComponent::CreateDescriptor(), TerrainHeightGradientListComponent::CreateDescriptor(), TerrainLayerSpawnerComponent::CreateDescriptor(), TerrainSurfaceDataSystemComponent::CreateDescriptor(), diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp index 5ec6af6f37..46ac677dd1 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp @@ -12,10 +12,6 @@ #include #include -#include -#include -#include - using namespace Terrain; bool TerrainLayerPriorityComparator::operator()(const AZ::EntityId& layer1id, const AZ::EntityId& layer2id) const @@ -114,18 +110,6 @@ void TerrainSystem::Deactivate() m_terrainSettingsDirty = true; m_requestedSettings.m_systemActive = false; - if (auto rpi = AZ::RPI::RPISystemInterface::Get(); rpi) - { - if (auto defaultScene = rpi->GetDefaultScene(); defaultScene) - { - const AZ::RPI::Scene* scene = defaultScene.get(); - if (auto terrainFeatureProcessor = scene->GetFeatureProcessor(); terrainFeatureProcessor) - { - terrainFeatureProcessor->RemoveTerrainData(); - } - } - } - AzFramework::Terrain::TerrainDataNotificationBus::Broadcast( &AzFramework::Terrain::TerrainDataNotificationBus::Events::OnTerrainDataDestroyEnd); } @@ -507,60 +491,6 @@ void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) m_currentSettings = m_requestedSettings; } - if (m_currentSettings.m_systemActive && m_terrainHeightDirty) - { - AZStd::shared_lock lock(m_areaMutex); - - // Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus). - // We lock our surface data mutex *before* checking / setting "isRequestInProgress" so that we prevent race conditions - // that create false detection of cyclic dependencies when multiple requests occur on different threads simultaneously. - // (One case where this was previously able to occur was in rapid updating of the Preview widget on the - // GradientSurfaceDataComponent in the Editor when moving the threshold sliders back and forth rapidly) - auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false); - typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex); - - AZ::Transform transform = AZ::Transform::CreateTranslation(m_currentSettings.m_worldBounds.GetCenter()); - - uint32_t width = aznumeric_cast( - (float)m_currentSettings.m_worldBounds.GetXExtent() / m_currentSettings.m_heightQueryResolution.GetX()); - uint32_t height = aznumeric_cast( - (float)m_currentSettings.m_worldBounds.GetYExtent() / m_currentSettings.m_heightQueryResolution.GetY()); - AZStd::vector pixels; - pixels.resize_no_construct(width * height); - const uint32_t pixelDataSize = width * height * sizeof(float); - memset(pixels.data(), 0, pixelDataSize); - - for (uint32_t y = 0; y < height; y++) - { - for (uint32_t x = 0; x < width; x++) - { - bool terrainExists; - float terrainHeight = GetTerrainAreaHeight( - (x * m_currentSettings.m_heightQueryResolution.GetX()) + m_currentSettings.m_worldBounds.GetMin().GetX(), - (y * m_currentSettings.m_heightQueryResolution.GetY()) + m_currentSettings.m_worldBounds.GetMin().GetY(), - terrainExists); - - pixels[(y * width) + x] = - (terrainHeight - m_currentSettings.m_worldBounds.GetMin().GetZ()) / - m_currentSettings.m_worldBounds.GetExtents().GetZ(); - } - } - - if (auto rpi = AZ::RPI::RPISystemInterface::Get(); rpi) - { - if (auto defaultScene = rpi->GetDefaultScene(); defaultScene) - { - const AZ::RPI::Scene* scene = defaultScene.get(); - if (auto terrainFeatureProcessor = scene->GetFeatureProcessor(); terrainFeatureProcessor) - { - terrainFeatureProcessor->UpdateTerrainData( - transform, m_currentSettings.m_worldBounds, m_currentSettings.m_heightQueryResolution.GetX(), width, height, - pixels); - } - } - } - } - if (terrainSettingsChanged || m_terrainHeightDirty) { // Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus). diff --git a/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp b/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp index 91a9744f0b..e4fb0f348e 100644 --- a/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp +++ b/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp @@ -19,15 +19,31 @@ #include using ::testing::AtLeast; +using ::testing::FloatNear; +using ::testing::FloatEq; +using ::testing::IsFalse; +using ::testing::Ne; using ::testing::NiceMock; using ::testing::Return; class TerrainSystemTest : public ::testing::Test { protected: + // Defines a structure for defining both an XY position and the expected height for that position. + struct HeightTestPoint + { + AZ::Vector2 m_testLocation; + float m_expectedHeight; + }; + AZ::ComponentApplication m_app; AZStd::unique_ptr m_terrainSystem; + AZStd::unique_ptr> m_boxShapeRequests; + AZStd::unique_ptr> m_shapeRequests; + AZStd::unique_ptr> m_terrainAreaHeightRequests; + + void SetUp() override { AZ::ComponentApplication::Descriptor appDesc; @@ -41,6 +57,9 @@ protected: void TearDown() override { m_terrainSystem.reset(); + m_boxShapeRequests.reset(); + m_shapeRequests.reset(); + m_terrainAreaHeightRequests.reset(); m_app.Destroy(); } @@ -71,6 +90,53 @@ protected: m_app.RegisterComponentDescriptor(Component::CreateDescriptor()); return entity->CreateComponent(); } + + // Create a terrain system with reasonable defaults for testing, but with the ability to override the defaults + // on a test-by-test basis. + void CreateAndActivateTerrainSystem( + AZ::Vector2 queryResolution = AZ::Vector2(1.0f), + AZ::Aabb worldBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-128.0f), AZ::Vector3(128.0f))) + { + // Create the terrain system and give it one tick to fully initialize itself. + m_terrainSystem = AZStd::make_unique(); + m_terrainSystem->SetTerrainAabb(worldBounds); + m_terrainSystem->SetTerrainHeightQueryResolution(queryResolution); + m_terrainSystem->Activate(); + AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.f, AZ::ScriptTimePoint{}); + } + + + AZStd::unique_ptr CreateAndActivateMockTerrainLayerSpawner( + const AZ::Aabb& spawnerBox, + const AZStd::function& mockHeights) + { + // Create the base entity with a mock box shape, Terrain Layer Spawner, and height provider. + auto entity = CreateEntity(); + CreateComponent(entity.get()); + CreateComponent(entity.get()); + + m_boxShapeRequests = AZStd::make_unique>(entity->GetId()); + m_shapeRequests = AZStd::make_unique>(entity->GetId()); + + // Set up the box shape to return whatever spawnerBox was passed in. + ON_CALL(*m_shapeRequests, GetEncompassingAabb).WillByDefault(Return(spawnerBox)); + + // Set up a mock height provider to use the passed-in mock height function to generate a height. + m_terrainAreaHeightRequests = AZStd::make_unique>(entity->GetId()); + ON_CALL(*m_terrainAreaHeightRequests, GetHeight) + .WillByDefault( + [mockHeights](const AZ::Vector3& inPosition, AZ::Vector3& outPosition, bool& terrainExists) + { + // By default, set the outPosition to the input position and terrain to always exist. + outPosition = inPosition; + terrainExists = true; + // Let the test function modify these values based on the needs of the specific test. + mockHeights(outPosition, terrainExists); + }); + + ActivateEntity(entity.get()); + return entity; + } }; TEST_F(TerrainSystemTest, TrivialCreateDestroy) @@ -122,10 +188,8 @@ TEST_F(TerrainSystemTest, TerrainDoesNotExistWhenNoTerrainLayerSpawnersAreRegist // will return false for terrainExists, returns a height equal to the min world bounds of the terrain system, and returns // a normal facing up the Z axis. - // Create the terrain system and give it one tick to fully initialize itself. - m_terrainSystem = AZStd::make_unique(); - m_terrainSystem->Activate(); - AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.f, AZ::ScriptTimePoint{}); + // Create and activate the terrain system with our testing defaults for world bounds and query resolution. + CreateAndActivateTerrainSystem(); AZ::Aabb worldBounds = m_terrainSystem->GetTerrainAabb(); @@ -139,7 +203,7 @@ TEST_F(TerrainSystemTest, TerrainDoesNotExistWhenNoTerrainLayerSpawnersAreRegist bool terrainExists = true; float height = m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); EXPECT_FALSE(terrainExists); - EXPECT_EQ(height, worldBounds.GetMin().GetZ()); + EXPECT_FLOAT_EQ(height, worldBounds.GetMin().GetZ()); terrainExists = true; AZ::Vector3 normal = m_terrainSystem->GetNormal( @@ -162,37 +226,21 @@ TEST_F(TerrainSystemTest, TerrainExistsOnlyWithinTerrainLayerSpawnerBounds) // The terrain system should only query Heights from the TerrainAreaHeightRequest bus within the // TerrainLayerSpawner region, and so those values should only get returned from GetHeight for queries inside that region. - // Create the base entity with a mock Box Shape and a Terrain Layer Spawner. - auto entity = CreateEntity(); - CreateComponent(entity.get()); - CreateComponent(entity.get()); - - // Set up the box shape to return a box from (0,0,5) to (10, 10, 15) - AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(0.0f, 0.0f, 5.0f, 10.0f, 10.0f, 15.0f); - NiceMock boxShapeRequests(entity->GetId()); - NiceMock shapeRequests(entity->GetId()); - ON_CALL(shapeRequests, GetEncompassingAabb).WillByDefault(Return(spawnerBox)); - - // Set up a mock height provider that always returns 5.0 and a normal of Y-up. - const float spawnerHeight = 5.0f; - NiceMock terrainAreaHeightRequests(entity->GetId()); - ON_CALL(terrainAreaHeightRequests, GetHeight) - .WillByDefault( - [spawnerHeight](const AZ::Vector3& inPosition, AZ::Vector3& outPosition, bool& terrainExists) - { - outPosition = inPosition; - outPosition.SetZ(spawnerHeight); - terrainExists = true; - }); - - ActivateEntity(entity.get()); + // Create a mock terrain layer spawner that uses a box of (0,0,5) - (10,10,15) and always returns a height of 5. + constexpr float spawnerHeight = 5.0f; + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(0.0f, 0.0f, 5.0f, 10.0f, 10.0f, 15.0f); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [](AZ::Vector3& position, bool& terrainExists) + { + position.SetZ(spawnerHeight); + terrainExists = true; + }); // Verify that terrain exists within the layer spawner bounds, and doesn't exist outside of it. - // Create the terrain system and give it one tick to fully initialize itself. - m_terrainSystem = AZStd::make_unique(); - m_terrainSystem->Activate(); - AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.f, AZ::ScriptTimePoint{}); + // Create and activate the terrain system with our testing defaults for world bounds and query resolution. + CreateAndActivateTerrainSystem(); // Create a box that's twice as big as the layer spawner box. Loop through it and verify that points within the layer box contain // terrain and the expected height & normal values, and points outside the layer box don't contain terrain. @@ -215,7 +263,7 @@ TEST_F(TerrainSystemTest, TerrainExistsOnlyWithinTerrainLayerSpawnerBounds) { EXPECT_TRUE(heightQueryTerrainExists); EXPECT_FALSE(isHole); - EXPECT_EQ(height, spawnerHeight); + EXPECT_FLOAT_EQ(height, spawnerHeight); } else { @@ -226,3 +274,202 @@ TEST_F(TerrainSystemTest, TerrainExistsOnlyWithinTerrainLayerSpawnerBounds) } } +TEST_F(TerrainSystemTest, TerrainHeightQueriesWithExactSamplersIgnoreQueryGrid) +{ + // Verify that when using the "EXACT" height sampler, the returned heights come directly from the height provider at the exact + // requested location, instead of the position being quantized to the height query grid. + + // Create a mock terrain layer spawner that uses a box of (0,0,5) - (10,10,15) and generates a height based on a sine wave + // using a frequency of 1m and an amplitude of 10m. i.e. Heights will range between -10 to 10 meters, but will have a value of 0 + // every 0.5 meters. The sine wave value is based on the absolute X position only, for simplicity. + constexpr float amplitudeMeters = 10.0f; + constexpr float frequencyMeters = 1.0f; + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(0.0f, 0.0f, 5.0f, 10.0f, 10.0f, 15.0f); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [](AZ::Vector3& position, bool& terrainExists) + { + position.SetZ(amplitudeMeters * sin(AZ::Constants::TwoPi * (position.GetX() / frequencyMeters))); + terrainExists = true; + }); + + // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution that exactly matches + // the frequency of our sine wave. If our height queries rely on the query resolution, we should always get a value of 0. + const AZ::Vector2 queryResolution(frequencyMeters); + CreateAndActivateTerrainSystem(queryResolution); + + // Test an arbitrary set of points that should all produce non-zero heights with the EXACT sampler. They're not aligned with the + // query resolution, or with the 0 points on the sine wave. + const AZ::Vector2 nonZeroPoints[] = { AZ::Vector2(0.3f), AZ::Vector2(2.8f), AZ::Vector2(5.9f), AZ::Vector2(7.7f) }; + for (auto& nonZeroPoint : nonZeroPoints) + { + AZ::Vector3 position(nonZeroPoint.GetX(), nonZeroPoint.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &heightQueryTerrainExists); + + // We've chosen a bunch of places on the sine wave that should return a non-zero positive or negative value. + constexpr float epsilon = 0.0001f; + EXPECT_GT(fabsf(height), epsilon); + } + + // Test an arbitrary set of points that should all produce zero heights with the EXACT sampler, since they align with 0 points on the + // sine wave, regardless of whether or not they align to the query resolution. + const AZ::Vector2 zeroPoints[] = { AZ::Vector2(0.5f), AZ::Vector2(1.0f), AZ::Vector2(5.0f), AZ::Vector2(7.5f) }; + for (auto& zeroPoint : zeroPoints) + { + AZ::Vector3 position(zeroPoint.GetX(), zeroPoint.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &heightQueryTerrainExists); + + constexpr float epsilon = 0.0001f; + EXPECT_NEAR(height, 0.0f, epsilon); + } +} + +TEST_F(TerrainSystemTest, TerrainHeightQueriesWithClampSamplersUseQueryGrid) +{ + // Verify that when using the "CLAMP" height sampler, the requested location is quantized to the height query grid before fetching + // the height. + + // Create a mock terrain layer spawner that uses a box of (-10,-10,-5) - (10,10,15) and generates a height equal + // to the X + Y position, so if either one doesn't get clamped we'll get an unexpected result. + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [](AZ::Vector3& position, bool& terrainExists) + { + position.SetZ(position.GetX() + position.GetY()); + terrainExists = true; + }); + + // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 0.25 meter intervals. + const AZ::Vector2 queryResolution(0.25f); + CreateAndActivateTerrainSystem(queryResolution); + + // Test some points and verify that the results always go "downward", whether they're in positive or negative space. + // (Z contains the the expected result for convenience). + const HeightTestPoint testPoints[] = + { + { AZ::Vector2(0.0f, 0.0f), 0.0f }, // Should return a height of 0.00 + 0.00 + { AZ::Vector2(0.3f, 0.3f), 0.5f }, // Should return a height of 0.25 + 0.25 + { AZ::Vector2(2.8f, 2.8f), 5.5f }, // Should return a height of 2.75 + 2.75 + { AZ::Vector2(5.5f, 5.5f), 11.0f }, // Should return a height of 5.50 + 5.50 + { AZ::Vector2(7.7f, 7.7f), 15.0f }, // Should return a height of 7.50 + 7.50 + + { AZ::Vector2(-0.3f, -0.3f), -1.0f }, // Should return a height of -0.50 + -0.50 + { AZ::Vector2(-2.8f, -2.8f), -6.0f }, // Should return a height of -3.00 + -3.00 + { AZ::Vector2(-5.5f, -5.5f), -11.0f }, // Should return a height of -5.50 + -5.50 + { AZ::Vector2(-7.7f, -7.7f), -15.5f } // Should return a height of -7.75 + -7.75 + }; + for (auto& testPoint : testPoints) + { + const float expectedHeight = testPoint.m_expectedHeight; + + AZ::Vector3 position(testPoint.m_testLocation.GetX(), testPoint.m_testLocation.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP, &heightQueryTerrainExists); + + constexpr float epsilon = 0.0001f; + EXPECT_NEAR(height, expectedHeight, epsilon); + } +} + +TEST_F(TerrainSystemTest, TerrainHeightQueriesWithBilinearSamplersUseQueryGridToInterpolate) +{ + // Verify that when using the "BILINEAR" height sampler, the heights are interpolated from points sampled from the query grid. + + // Create a mock terrain layer spawner that uses a box of (-10,-10,-5) - (10,10,15) and generates a height equal + // to the X + Y position, so we'll have heights that look like this on our grid: + // 0 *---* 1 + // | | + // 1 *---* 2 + // However, everywhere inside the grid box, we'll generate heights much larger than X + Y. It will have no effect on exact grid + // points, but it will noticeably affect the expected height values if any points get sampled in-between grid points. + + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f); + const float amplitudeMeters = 10.0f; + const float frequencyMeters = 1.0f; + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [amplitudeMeters, frequencyMeters](AZ::Vector3& position, bool& terrainExists) + { + // Our generated height will be X + Y. + float expectedHeight = position.GetX() + position.GetY(); + + // If either X or Y aren't evenly divisible by the query frequency, add a scaled value to our generated height. + // This will show up as an unexpected height "spike" if it gets used in any bilinear filter queries. + float unexpectedVariance = amplitudeMeters * + (fmodf(position.GetX(), frequencyMeters) + fmodf(position.GetY(), frequencyMeters)); + position.SetZ(expectedHeight + unexpectedVariance); + terrainExists = true; + }); + + // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 1 meter intervals. + const AZ::Vector2 queryResolution(frequencyMeters); + CreateAndActivateTerrainSystem(queryResolution); + + // Test some points and verify that the results are the expected bilinear filtered result, + // whether they're in positive or negative space. + // (Z contains the the expected result for convenience). + const HeightTestPoint testPoints[] = { + + // Queries directly on grid points. These should return values of X + Y. + { AZ::Vector2(0.0f, 0.0f), 0.0f }, // Should return a height of 0 + 0 + { AZ::Vector2(1.0f, 0.0f), 1.0f }, // Should return a height of 1 + 0 + { AZ::Vector2(0.0f, 1.0f), 1.0f }, // Should return a height of 0 + 1 + { AZ::Vector2(1.0f, 1.0f), 2.0f }, // Should return a height of 1 + 1 + { AZ::Vector2(3.0f, 5.0f), 8.0f }, // Should return a height of 3 + 5 + + { AZ::Vector2(-1.0f, 0.0f), -1.0f }, // Should return a height of -1 + 0 + { AZ::Vector2(0.0f, -1.0f), -1.0f }, // Should return a height of 0 + -1 + { AZ::Vector2(-1.0f, -1.0f), -2.0f }, // Should return a height of -1 + -1 + { AZ::Vector2(-3.0f, -5.0f), -8.0f }, // Should return a height of -3 + -5 + + // Queries that are on a grid edge (one axis on the grid, the other somewhere in-between). + // These should just be a linear interpolation of the points, so it should still be X + Y. + + { AZ::Vector2(0.25f, 0.0f), 0.25f }, // Should return a height of -0.25 + 0 + { AZ::Vector2(3.75f, 0.0f), 3.75f }, // Should return a height of -3.75 + 0 + { AZ::Vector2(0.0f, 0.25f), 0.25f }, // Should return a height of 0 + -0.25 + { AZ::Vector2(0.0f, 3.75f), 3.75f }, // Should return a height of 0 + -3.75 + + { AZ::Vector2(2.0f, 3.75f), 5.75f }, // Should return a height of -2 + -3.75 + { AZ::Vector2(2.25f, 4.0f), 6.25f }, // Should return a height of -2.25 + -4 + + { AZ::Vector2(-0.25f, 0.0f), -0.25f }, // Should return a height of -0.25 + 0 + { AZ::Vector2(-3.75f, 0.0f), -3.75f }, // Should return a height of -3.75 + 0 + { AZ::Vector2(0.0f, -0.25f), -0.25f }, // Should return a height of 0 + -0.25 + { AZ::Vector2(0.0f, -3.75f), -3.75f }, // Should return a height of 0 + -3.75 + + { AZ::Vector2(-2.0f, -3.75f), -5.75f }, // Should return a height of -2 + -3.75 + { AZ::Vector2(-2.25f, -4.0f), -6.25f }, // Should return a height of -2.25 + -4 + + // Queries inside a grid square (both axes are in-between grid points) + // This is a full bilinear interpolation, but because we're using X + Y for our heights, the interpolated values + // should *still* be X + Y assuming the points were sampled correctly from the grid points. + + { AZ::Vector2(3.25f, 5.25f), 8.5f }, // Should return a height of 3.25 + 5.25 + { AZ::Vector2(7.71f, 9.74f), 17.45f }, // Should return a height of 7.71 + 9.74 + + { AZ::Vector2(-3.25f, -5.25f), -8.5f }, // Should return a height of -3.25 + -5.25 + { AZ::Vector2(-7.71f, -9.74f), -17.45f }, // Should return a height of -7.71 + -9.74 + }; + + // Loop through every test point and validate it. + for (auto& testPoint : testPoints) + { + const float expectedHeight = testPoint.m_expectedHeight; + + AZ::Vector3 position(testPoint.m_testLocation.GetX(), testPoint.m_testLocation.GetY(), 0.0f); + bool heightQueryTerrainExists = false; + float height = + m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, &heightQueryTerrainExists); + + // Verify that our height query returned the bilinear filtered result we expect. + constexpr float epsilon = 0.0001f; + EXPECT_NEAR(height, expectedHeight, epsilon); + } +} diff --git a/Gems/Terrain/Code/terrain_editor_shared_files.cmake b/Gems/Terrain/Code/terrain_editor_shared_files.cmake index 9868f7d310..d4719f46d5 100644 --- a/Gems/Terrain/Code/terrain_editor_shared_files.cmake +++ b/Gems/Terrain/Code/terrain_editor_shared_files.cmake @@ -15,6 +15,8 @@ set(FILES Source/EditorComponents/EditorTerrainWorldComponent.h Source/EditorComponents/EditorTerrainWorldDebuggerComponent.cpp Source/EditorComponents/EditorTerrainWorldDebuggerComponent.h + Source/EditorComponents/EditorTerrainWorldRendererComponent.cpp + Source/EditorComponents/EditorTerrainWorldRendererComponent.h Source/EditorComponents/EditorTerrainSystemComponent.cpp Source/EditorComponents/EditorTerrainSystemComponent.h Source/EditorTerrainModule.cpp diff --git a/Gems/Terrain/Code/terrain_files.cmake b/Gems/Terrain/Code/terrain_files.cmake index 6a76dd7141..2c013fa332 100644 --- a/Gems/Terrain/Code/terrain_files.cmake +++ b/Gems/Terrain/Code/terrain_files.cmake @@ -19,6 +19,8 @@ set(FILES Source/Components/TerrainWorldComponent.h Source/Components/TerrainWorldDebuggerComponent.cpp Source/Components/TerrainWorldDebuggerComponent.h + Source/Components/TerrainWorldRendererComponent.cpp + Source/Components/TerrainWorldRendererComponent.h Source/TerrainRenderer/TerrainFeatureProcessor.cpp Source/TerrainRenderer/TerrainFeatureProcessor.h Source/TerrainSystem/TerrainSystem.cpp diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index 816ef6db30..65e046c8ba 100644 --- a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake +++ b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake @@ -30,7 +30,7 @@ ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev6-linux ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-linux TARGETS Lua PACKAGE_HASH 1adc812abe3dd0dbb2ca9756f81d8f0e0ba45779ac85bf1d8455b25c531a38b0) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-linux TARGETS PhysX PACKAGE_HASH a110249cbef4f266b0002c4ee9a71f59f373040cefbe6b82f1e1510c811edde6) ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-linux TARGETS etc2comp PACKAGE_HASH 9283aa5db5bb7fb90a0ddb7a9f3895317c8ebe8044943124bbb3673a41407430) -ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-linux TARGETS mcpp PACKAGE_HASH 0aa713f3f2c156cb2f17d9b800aed8acf9df5ab167c48b679853ecb040da9a67) +ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.2-rev1-linux TARGETS mcpp PACKAGE_HASH df7a998d0bc3fedf44b5bdebaf69ddad6033355b71a590e8642445ec77bc6c41) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-linux TARGETS mikkelsen PACKAGE_HASH 5973b1e71a64633588eecdb5b5c06ca0081f7be97230f6ef64365cbda315b9c8) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-linux TARGETS googletest PACKAGE_HASH 7b7ad330f369450c316a4c4592d17fbb4c14c731c95bd8f37757203e8c2bbc1b) ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-linux TARGETS GoogleBenchmark PACKAGE_HASH 4038878f337fc7e0274f0230f71851b385b2e0327c495fc3dd3d1c18a807928d) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index 58cafdd4b8..acddb879d4 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -32,7 +32,7 @@ ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev5-mac ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev6-mac TARGETS Lua PACKAGE_HASH b9079fd35634774c9269028447562c6b712dbc83b9c64975c095fd423ff04c08) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-mac TARGETS PhysX PACKAGE_HASH 5e092a11d5c0a50c4dd99bb681a04b566a4f6f29aa08443d9bffc8dc12c27c8e) ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-mac TARGETS etc2comp PACKAGE_HASH 1966ab101c89db7ecf30984917e0a48c0d02ee0e4d65b798743842b9469c0818) -ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-mac TARGETS mcpp PACKAGE_HASH 48a9c5197bf72843fb9ac44825501ee16bbe3e72e086a32b8c9c05bf47db12ab) +ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.2-rev1-mac TARGETS mcpp PACKAGE_HASH be9558905c9c49179ef3d7d84f0a5472415acdf7fe2d76eb060d9431723ddf2e) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-mac TARGETS mikkelsen PACKAGE_HASH 83af99ca8bee123684ad254263add556f0cf49486c0b3e32e6d303535714e505) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-mac TARGETS googletest PACKAGE_HASH cbf020d5ef976c5db8b6e894c6c63151ade85ed98e7c502729dd20172acae5a8) ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-mac TARGETS GoogleBenchmark PACKAGE_HASH ad25de0146769c91e179953d845de2bec8ed4a691f973f47e3eb37639381f665) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index d02fefc0e9..d56a03f515 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -33,7 +33,7 @@ ly_associate_package(PACKAGE_NAME AWSNativeSDK-1.7.167-rev4-windows ly_associate_package(PACKAGE_NAME Lua-5.3.5-rev5-windows TARGETS Lua PACKAGE_HASH 136faccf1f73891e3fa3b95f908523187792e56f5b92c63c6a6d7e72d1158d40) ly_associate_package(PACKAGE_NAME PhysX-4.1.2.29882248-rev3-windows TARGETS PhysX PACKAGE_HASH 0c5ffbd9fa588e5cf7643721a7cfe74d0fe448bf82252d39b3a96d06dfca2298) ly_associate_package(PACKAGE_NAME etc2comp-9cd0f9cae0-rev1-windows TARGETS etc2comp PACKAGE_HASH fc9ae937b2ec0d42d5e7d0e9e8c80e5e4d257673fb33bc9b7d6db76002117123) -ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-windows TARGETS mcpp PACKAGE_HASH 511672598fa319bfb8db87f965b59abff1620bb7c1dcf7669e039a8acd8d3ff8) +ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.2-rev1-windows TARGETS mcpp PACKAGE_HASH 794789aba639bfe2f4e8fcb4424d679933dd6290e523084aa0a4e287ac44acb2) ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-windows TARGETS mikkelsen PACKAGE_HASH 872c4d245a1c86139aa929f2b465b63ea4ea55b04ced50309135dd4597457a4e) ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-windows TARGETS googletest PACKAGE_HASH 7e8f03ae8a01563124e3daa06386f25a2b311c10bb95bff05cae6c41eff83837) ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-windows TARGETS GoogleBenchmark PACKAGE_HASH 0c94ca69ae8e7e4aab8e90032b5c82c5964410429f3dd9dbb1f9bf4fe032b1d4) diff --git a/cmake/Platform/Mac/InstallUtils_mac.cmake.in b/cmake/Platform/Mac/InstallUtils_mac.cmake.in index de6d9ddf65..d73c4db459 100644 --- a/cmake/Platform/Mac/InstallUtils_mac.cmake.in +++ b/cmake/Platform/Mac/InstallUtils_mac.cmake.in @@ -155,7 +155,9 @@ function(ly_copy source_file target_directory) endfunction() function(ly_download_and_codesign_sdk_python) - execute_process(COMMAND ${CMAKE_COMMAND} -DPAL_PLATFORM_NAME=Mac -DLY_3RDPARTY_PATH=${CMAKE_INSTALL_PREFIX}/python -P ${CMAKE_INSTALL_PREFIX}/python/get_python.cmake) + execute_process(COMMAND ${CMAKE_COMMAND} -DPAL_PLATFORM_NAME=Mac -DLY_3RDPARTY_PATH=${CMAKE_INSTALL_PREFIX}/python -P ${CMAKE_INSTALL_PREFIX}/python/get_python.cmake + WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX} + ) fixup_python_framework(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework) codesign_python_framework_binaries(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework) codesign_file(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework @LY_ROOT_FOLDER@/python/Platform/Mac/PythonEntitlements.plist)