From 955a6db374f851e31c920b1d2e017fc315565330 Mon Sep 17 00:00:00 2001 From: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> Date: Thu, 30 Dec 2021 15:56:27 -0600 Subject: [PATCH 01/26] Converting Editor automated tests to utilize prefab system Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> --- .../hydra_editor_utils.py | 13 +++++-- .../AssetBrowser_SearchFiltering.py | 5 ++- .../AssetBrowser_TreeNavigation.py | 4 +-- .../editor/EditorScripts/AssetPicker_UI_UX.py | 3 +- ...flows_ExistingLevel_EntityComponentCRUD.py | 6 ++-- ...ditorWorkflows_LevelEntityComponentCRUD.py | 30 +++++++--------- .../ComponentCRUD_Add_Delete_Components.py | 6 ++-- .../EditorScripts/Docking_BasicDockedTools.py | 13 ++++--- .../EntityOutliner_EntityOrdering.py | 11 +++--- .../InputBindings_Add_Remove_Input_Events.py | 5 ++- .../EditorScripts/Menus_EditMenuOptions.py | 5 ++- .../EditorScripts/Menus_FileMenuOptions.py | 12 +++---- .../EditorScripts/Menus_ViewMenuOptions.py | 5 ++- .../Gem/PythonTests/editor/TestSuite_Main.py | 20 +++++------ .../editor/TestSuite_Main_Optimized.py | 33 +++++++++-------- .../PythonTests/editor/TestSuite_Periodic.py | 35 +++++++++++-------- .../PythonTests/editor/TestSuite_Sandbox.py | 4 +-- .../editor/TestSuite_Sandbox_Optimized.py | 2 -- 18 files changed, 105 insertions(+), 107 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_editor_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_editor_utils.py index 5e3828ad02..36fc6003f7 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_editor_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_editor_utils.py @@ -5,15 +5,22 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ +from typing import List +from math import isclose +import collections.abc + import azlmbr.bus as bus import azlmbr.editor as editor import azlmbr.entity as entity import azlmbr.legacy.general as general import azlmbr.object -from typing import List -from math import isclose -import collections.abc +from editor_python_test_tools.utils import TestHelper as helper + + +def open_base_level(): + helper.init_idle() + helper.open_level("Prefab", "Base") def find_entity_by_name(entity_name): diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetBrowser_SearchFiltering.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetBrowser_SearchFiltering.py index 7366faafdc..254224a2f8 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetBrowser_SearchFiltering.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetBrowser_SearchFiltering.py @@ -59,8 +59,8 @@ def AssetBrowser_SearchFiltering(): import azlmbr.legacy.general as general + import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper def verify_files_appeared(model, allowed_asset_extensions, parent_index=QtCore.QModelIndex()): indexes = [parent_index] @@ -80,8 +80,7 @@ def AssetBrowser_SearchFiltering(): return True # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Open Asset Browser (if not opened already) editor_window = pyside_utils.get_editor_main_window() diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetBrowser_TreeNavigation.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetBrowser_TreeNavigation.py index ecc77778cc..52072205b5 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetBrowser_TreeNavigation.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetBrowser_TreeNavigation.py @@ -53,6 +53,7 @@ def AssetBrowser_TreeNavigation(): import azlmbr.legacy.general as general import editor_python_test_tools.pyside_utils as pyside_utils + import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper @@ -69,8 +70,7 @@ def AssetBrowser_TreeNavigation(): file_path = ("AutomatedTesting", "Assets", "ImageGradients", "image_grad_test_gsi.png") # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Open Asset Browser (if not opened already) editor_window = pyside_utils.get_editor_main_window() diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetPicker_UI_UX.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetPicker_UI_UX.py index 59a78c9e5d..047d6edf41 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetPicker_UI_UX.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetPicker_UI_UX.py @@ -215,8 +215,7 @@ def AssetPicker_UI_UX(): QtTest.QTest.keyClick(tree, Qt.Key_Enter, Qt.NoModifier) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create entity and add Mesh component entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py index 39cacf9af5..48b7d2b176 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD.py @@ -56,19 +56,17 @@ def BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(): 06. delete parent entity """ + import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.utils import Report from editor_python_test_tools.editor_entity_utils import EditorEntity import azlmbr.bus as bus import azlmbr.editor as editor import azlmbr.entity as entity - import azlmbr.legacy.general as general import azlmbr.object # 01. load an existing level - test_level = 'Simple' - general.open_level_no_prompt(test_level) - Report.result(Tests.load_level, general.get_current_level_name() == test_level) + hydra.open_base_level() # 02. create parent entity and set name # Delete any exiting entity and Create a new Entity at the root level diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_LevelEntityComponentCRUD.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_LevelEntityComponentCRUD.py index 9c5880ab1e..f13b924e30 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_LevelEntityComponentCRUD.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/BasicEditorWorkflows_LevelEntityComponentCRUD.py @@ -31,7 +31,7 @@ class Tests: "Component removed from entity successfully", "Failed to remove component from entity" ) - level_saved_and_exported = ( + saved_and_exported = ( "Level saved and exported successfully", "Failed to save/export level" ) @@ -52,8 +52,7 @@ def BasicEditorWorkflows_LevelEntityComponentCRUD(): - A new entity can be created - Entity hierarchy can be adjusted - Components can be added/removed/updated - - Level can be saved - - Level can be exported + - Level can be saved/exported Note: - This test file must be called from the O3DE Editor command terminal @@ -70,7 +69,7 @@ def BasicEditorWorkflows_LevelEntityComponentCRUD(): import azlmbr.editor as editor import azlmbr.entity as entity import azlmbr.math as math - import azlmbr.paths + import azlmbr.paths as paths import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.utils import Report @@ -84,7 +83,7 @@ def BasicEditorWorkflows_LevelEntityComponentCRUD(): return None # 1) Create a new level - level = "tmp_level" + lvl_name = "tmp_level" editor_window = pyside_utils.get_editor_main_window() new_level_action = pyside_utils.get_action_for_menu_path(editor_window, "File", "New Level") pyside_utils.trigger_action_async(new_level_action) @@ -95,23 +94,24 @@ def BasicEditorWorkflows_LevelEntityComponentCRUD(): Report.info("New Level dialog opened") grp_box = new_level_dlg.findChild(QtWidgets.QGroupBox, "STATIC_GROUP1") level_name = grp_box.findChild(QtWidgets.QLineEdit, "LEVEL") - level_name.setText(level) + level_name.setText(lvl_name) button_box = new_level_dlg.findChild(QtWidgets.QDialogButtonBox, "buttonBox") button_box.button(QtWidgets.QDialogButtonBox.Ok).click() # Verify new level was created successfully level_create_success = await pyside_utils.wait_for_condition(lambda: editor.EditorToolsApplicationRequestBus( - bus.Broadcast, "GetCurrentLevelName") == level, 5.0) + bus.Broadcast, "GetCurrentLevelName") == lvl_name, 5.0) Report.critical_result(Tests.level_created, level_create_success) # 2) Delete existing entities, and create and manipulate new entities via Entity Inspector search_filter = azlmbr.entity.SearchFilter() all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter) editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities) - entity_outliner_widget = editor_window.findChild(QtWidgets.QWidget, "OutlinerWidgetUI") + entity_outliner_widget = editor_window.findChild(QtWidgets.QWidget, "EntityOutlinerWidgetUI") outliner_object_list = entity_outliner_widget.findChild(QtWidgets.QWidget, "m_objectList_Contents") outliner_tree = outliner_object_list.findChild(QtWidgets.QWidget, "m_objectTree") - await pyside_utils.trigger_context_menu_entry(outliner_tree, "Create entity") + outliner_viewport = outliner_tree.findChild(QtWidgets.QWidget, "qt_scrollarea_viewport") + await pyside_utils.trigger_context_menu_entry(outliner_viewport, "Create entity") # Find the new entity parent_entity_id = find_entity_by_name("Entity1") @@ -153,14 +153,10 @@ def BasicEditorWorkflows_LevelEntityComponentCRUD(): save_level_action = pyside_utils.get_action_for_menu_path(editor_window, "File", "Save") pyside_utils.trigger_action_async(save_level_action) - # 5) Export the level - export_action = pyside_utils.get_action_for_menu_path(editor_window, "Game", "Export to Engine") - pyside_utils.trigger_action_async(export_action) - level_pak_file = os.path.join( - "AutomatedTesting", "Levels", level, "level.pak" - ) - export_success = await pyside_utils.wait_for_condition(lambda: os.path.exists(level_pak_file), 5.0) - Report.result(Tests.level_saved_and_exported, export_success) + # 5) Verify the save/export of the level + level_prefab_path = os.path.join(paths.products, "levels", lvl_name, f"{lvl_name}.spawnable") + success = await pyside_utils.wait_for_condition(lambda: os.path.exists(level_prefab_path), 5.0) + Report.result(Tests.saved_and_exported, success) run_test() diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/ComponentCRUD_Add_Delete_Components.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/ComponentCRUD_Add_Delete_Components.py index 779f1ef953..6772450405 100755 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/ComponentCRUD_Add_Delete_Components.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/ComponentCRUD_Add_Delete_Components.py @@ -63,7 +63,7 @@ def ComponentCRUD_Add_Delete_Components(): :return: None """ - from PySide2 import QtWidgets, QtTest, QtCore + from PySide2 import QtWidgets, QtTest from PySide2.QtCore import Qt import azlmbr.legacy.general as general @@ -74,7 +74,6 @@ def ComponentCRUD_Add_Delete_Components(): import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper async def add_component(component_name): pyside_utils.click_button_async(add_comp_btn) @@ -88,8 +87,7 @@ def ComponentCRUD_Add_Delete_Components(): QtTest.QTest.keyClick(tree, Qt.Key_Enter, Qt.NoModifier) # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Create entity entity_position = math.Vector3(125.0, 136.0, 32.0) diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Docking_BasicDockedTools.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Docking_BasicDockedTools.py index 6683fc952a..d83db7d90c 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Docking_BasicDockedTools.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Docking_BasicDockedTools.py @@ -62,12 +62,11 @@ def Docking_BasicDockedTools(): import azlmbr.editor as editor import azlmbr.entity as entity + import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper # Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # Make sure the Entity Outliner, Entity Inspector and Console tools are open general.open_pane("Entity Outliner (PREVIEW)") @@ -80,7 +79,7 @@ def Docking_BasicDockedTools(): editor.EditorEntityAPIBus(bus.Event, 'SetName', entity_id, entity_original_name) editor_window = pyside_utils.get_editor_main_window() - entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)") + entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner") # 1) Open the tools and dock them together in a floating tabbed widget. # We drag/drop it over the viewport since it doesn't allow docking, so this will undock it @@ -89,7 +88,7 @@ def Docking_BasicDockedTools(): # We need to grab a new reference to the Entity Outliner QDockWidget because when it gets moved # to the floating window, its parent changes so the wrapped intance we had becomes invalid - entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)") + entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner") # Dock the Entity Inspector tabbed with the floating Entity Outliner entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector") @@ -106,7 +105,7 @@ def Docking_BasicDockedTools(): # Check to ensure all the tools are parented to the same QStackedWidget def check_all_panes_tabbed(): entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector") - entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)") + entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner") console = editor_window.findChild(QtWidgets.QDockWidget, "Console") entity_inspector_parent = entity_inspector.parentWidget() entity_outliner_parent = entity_outliner.parentWidget() @@ -122,7 +121,7 @@ def Docking_BasicDockedTools(): # 2.1,2) Select an Entity in the Entity Outliner. entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector") - entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)") + entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner") console = editor_window.findChild(QtWidgets.QDockWidget, "Console") object_tree = entity_outliner.findChild(QtWidgets.QTreeView, "m_objectTree") test_entity_index = pyside_utils.find_child_by_pattern(object_tree, entity_original_name) diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/EntityOutliner_EntityOrdering.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/EntityOutliner_EntityOrdering.py index 5fa8130302..fab7984df9 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/EntityOutliner_EntityOrdering.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/EntityOutliner_EntityOrdering.py @@ -30,11 +30,13 @@ def EntityOutliner_EntityOrdering(): 5) Add another new entity, ensure the rest of the order is unchanged """ - import editor_python_test_tools.pyside_utils as pyside_utils + from PySide2 import QtCore + import azlmbr.legacy.general as general + + import editor_python_test_tools.hydra_editor_utils as hydra + import editor_python_test_tools.pyside_utils as pyside_utils from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper - from PySide2 import QtCore, QtWidgets, QtGui, QtTest # Grab the Editor, Entity Outliner, and Outliner Model editor_window = pyside_utils.get_editor_main_window() @@ -110,8 +112,7 @@ def EntityOutliner_EntityOrdering(): expected_order = [] # 1) Open the empty Prefab Base level - helper.init_idle() - helper.open_level("Prefab", "Base") + hydra.open_base_level() # 2) Add 5 entities to the outliner ENTITIES_TO_ADD = 5 diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/InputBindings_Add_Remove_Input_Events.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/InputBindings_Add_Remove_Input_Events.py index f4769dab4d..07c89ff110 100755 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/InputBindings_Add_Remove_Input_Events.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/InputBindings_Add_Remove_Input_Events.py @@ -69,8 +69,8 @@ def InputBindings_Add_Remove_Input_Events(): import azlmbr.legacy.general as general + import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper def open_asset_editor(): general.open_pane("Asset Editor") @@ -81,8 +81,7 @@ def InputBindings_Add_Remove_Input_Events(): return not general.is_pane_visible("Asset Editor") # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Open Asset Editor Report.result(Tests.asset_editor_opened, open_asset_editor()) diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py index bd213be293..7d72d76776 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py @@ -26,9 +26,9 @@ def Menus_EditMenuOptions_Work(): :return: None """ + import editor_python_test_tools.hydra_editor_utils as hydra import editor_python_test_tools.pyside_utils as pyside_utils from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper edit_menu_options = [ ("Undo",), @@ -57,8 +57,7 @@ def Menus_EditMenuOptions_Work(): ] # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Interact with Edit Menu options editor_window = pyside_utils.get_editor_main_window() diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py index a3e7611b5e..cade2125e2 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py @@ -26,29 +26,27 @@ def Menus_FileMenuOptions_Work(): :return: None """ + import editor_python_test_tools.hydra_editor_utils as hydra import editor_python_test_tools.pyside_utils as pyside_utils from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper file_menu_options = [ ("New Level",), - ("Open Level",), + #("Open Level",), Temporarily disabled due to https://github.com/o3de/o3de/issues/6605 ("Import",), ("Save",), - ("Save As",), + #("Save As",), Temporarily disabled due to https://github.com/o3de/o3de/issues/6605 ("Save Level Statistics",), ("Edit Project Settings",), - ("Edit Platform Settings",), + #("Edit Platform Settings",), Temporarily disabled due to https://github.com/o3de/o3de/issues/6604 ("New Project",), ("Open Project",), ("Show Log File",), - ("Resave All Slices",), ("Exit",), ] # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Interact with File Menu options editor_window = pyside_utils.get_editor_main_window() diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py index deff2855a0..2d92fdb97c 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py @@ -26,9 +26,9 @@ def Menus_ViewMenuOptions_Work(): :return: None """ + import editor_python_test_tools.hydra_editor_utils as hydra import editor_python_test_tools.pyside_utils as pyside_utils from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper view_menu_options = [ ("Center on Selection",), @@ -45,8 +45,7 @@ def Menus_ViewMenuOptions_Work(): ] # 1) Open an existing simple level - helper.init_idle() - helper.open_level("Physics", "Base") + hydra.open_base_level() # 2) Interact with View Menu options editor_window = pyside_utils.get_editor_main_window() diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py index c9e91687e0..949aab140d 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py @@ -33,22 +33,20 @@ class TestAutomation(TestAutomationBase): def test_BasicEditorWorkflows_LevelEntityComponentCRUD(self, request, workspace, editor, launcher_platform, remove_test_level): from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False, enable_prefab_system=False) + self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False) @pytest.mark.REQUIRES_gpu def test_BasicEditorWorkflows_GPU_LevelEntityComponentCRUD(self, request, workspace, editor, launcher_platform, remove_test_level): from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False, - use_null_renderer=False, enable_prefab_system=False) + use_null_renderer=False) - def test_EntityOutlienr_EntityOrdering(self, request, workspace, editor, launcher_platform): + def test_BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(self, request, workspace, editor, + launcher_platform): + from .EditorScripts import BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD as test_module + self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False) + + def test_EntityOutliner_EntityOrdering(self, request, workspace, editor, launcher_platform): from .EditorScripts import EntityOutliner_EntityOrdering as test_module - self._run_test( - request, - workspace, - editor, - test_module, - batch_mode=False, - autotest_mode=True, - ) + self._run_test(request, workspace, editor, test_module, batch_mode=False) diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py index d87fd8625b..7364cd8bc9 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py @@ -12,7 +12,6 @@ import ly_test_tools.environment.file_system as file_system from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite -@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.") @pytest.mark.SUITE_main @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) @@ -21,8 +20,12 @@ class TestAutomationNoAutoTestMode(EditorTestSuite): # Disable -autotest_mode and -BatchMode. Tests cannot run in -BatchMode due to UI interactions, and these tests # interact with modal dialogs global_extra_cmdline_args = [] - - enable_prefab_system = False + + class test_AssetPicker_UI_UX(EditorSharedTest): + from .EditorScripts import AssetPicker_UI_UX as test_module + + class test_BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(EditorSingleTest): + from .EditorScripts import BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD as test_module class test_BasicEditorWorkflows_LevelEntityComponentCRUD(EditorSingleTest): # Custom teardown to remove slice asset created during test @@ -45,9 +48,6 @@ class TestAutomationNoAutoTestMode(EditorTestSuite): class test_InputBindings_Add_Remove_Input_Events(EditorSharedTest): from .EditorScripts import InputBindings_Add_Remove_Input_Events as test_module - class test_AssetPicker_UI_UX(EditorSharedTest): - from .EditorScripts import AssetPicker_UI_UX as test_module - @pytest.mark.SUITE_main @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @@ -57,23 +57,26 @@ class TestAutomationAutoTestMode(EditorTestSuite): # Enable only -autotest_mode for these tests. Tests cannot run in -BatchMode due to UI interactions global_extra_cmdline_args = ["-autotest_mode"] - enable_prefab_system = False + class test_AssetBrowser_SearchFiltering(EditorSharedTest): + from .EditorScripts import AssetBrowser_SearchFiltering as test_module class test_AssetBrowser_TreeNavigation(EditorSharedTest): from .EditorScripts import AssetBrowser_TreeNavigation as test_module - class test_AssetBrowser_SearchFiltering(EditorSharedTest): - from .EditorScripts import AssetBrowser_SearchFiltering as test_module - class test_ComponentCRUD_Add_Delete_Components(EditorSharedTest): from .EditorScripts import ComponentCRUD_Add_Delete_Components as test_module - class test_Menus_ViewMenuOptions_Work(EditorSharedTest): - from .EditorScripts import Menus_ViewMenuOptions as test_module + class test_Docking_BasicDockedTools(EditorSharedTest): + from .EditorScripts import Docking_BasicDockedTools as test_module + + class test_EntityOutliner_EntityOrdering(EditorSharedTest): + from .EditorScripts import EntityOutliner_EntityOrdering as test_module + + class test_Menus_EditMenuOptions_Work(EditorSharedTest): + from .EditorScripts import Menus_EditMenuOptions as test_module - @pytest.mark.skip(reason="Times out due to dialogs failing to dismiss: LYN-4208") class test_Menus_FileMenuOptions_Work(EditorSharedTest): from .EditorScripts import Menus_FileMenuOptions as test_module - class test_BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(EditorSharedTest): - from .EditorScripts import BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD as test_module + class test_Menus_ViewMenuOptions_Work(EditorSharedTest): + from .EditorScripts import Menus_ViewMenuOptions as test_module \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py index 1bd1d7f987..f8a054d517 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py @@ -10,6 +10,7 @@ import pytest import sys import ly_test_tools.environment.file_system as file_system +import ly_test_tools.environment.process_utils as process_utils sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared') from base import TestAutomationBase @@ -25,36 +26,42 @@ def remove_test_level(request, workspace, project): request.addfinalizer(teardown) +@pytest.fixture +def kill_external_tools(request): + def teardown(): + process_utils.kill_processes_named("o3de.exe") + request.addfinalizer(teardown) + + @pytest.mark.SUITE_periodic @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) class TestAutomation(TestAutomationBase): - def test_AssetBrowser_TreeNavigation(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AssetBrowser_TreeNavigation as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False) - def test_AssetBrowser_SearchFiltering(self, request, workspace, editor, launcher_platform): from .EditorScripts import AssetBrowser_SearchFiltering as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False) + self._run_test(request, workspace, editor, test_module, batch_mode=False) + + def test_AssetBrowser_TreeNavigation(self, request, workspace, editor, launcher_platform): + from .EditorScripts import AssetBrowser_TreeNavigation as test_module + self._run_test(request, workspace, editor, test_module, batch_mode=False) def test_AssetPicker_UI_UX(self, request, workspace, editor, launcher_platform): from .EditorScripts import AssetPicker_UI_UX as test_module - self._run_test(request, workspace, editor, test_module, autotest_mode=False, batch_mode=False, enable_prefab_system=False) + self._run_test(request, workspace, editor, test_module, autotest_mode=False, batch_mode=False) def test_ComponentCRUD_Add_Delete_Components(self, request, workspace, editor, launcher_platform): from .EditorScripts import ComponentCRUD_Add_Delete_Components as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False) + self._run_test(request, workspace, editor, test_module, batch_mode=False) def test_InputBindings_Add_Remove_Input_Events(self, request, workspace, editor, launcher_platform): from .EditorScripts import InputBindings_Add_Remove_Input_Events as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False, enable_prefab_system=False) + self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False) + + def test_Menus_FileMenuOptions_Work(self, request, workspace, editor, launcher_platform, kill_external_tools): + from .EditorScripts import Menus_FileMenuOptions as test_module + self._run_test(request, workspace, editor, test_module, batch_mode=False) def test_Menus_ViewMenuOptions_Work(self, request, workspace, editor, launcher_platform): from .EditorScripts import Menus_ViewMenuOptions as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False) - - @pytest.mark.skip(reason="Times out due to dialogs failing to dismiss: LYN-4208") - def test_Menus_FileMenuOptions_Work(self, request, workspace, editor, launcher_platform): - from .EditorScripts import Menus_FileMenuOptions as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False) + self._run_test(request, workspace, editor, test_module, batch_mode=False) diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox.py index 8a56a2dbfd..98a6620d9c 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox.py @@ -20,8 +20,8 @@ class TestAutomation(TestAutomationBase): def test_Menus_EditMenuOptions_Work(self, request, workspace, editor, launcher_platform): from .EditorScripts import Menus_EditMenuOptions as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False) + self._run_test(request, workspace, editor, test_module, batch_mode=False) def test_Docking_BasicDockedTools(self, request, workspace, editor, launcher_platform): from .EditorScripts import Docking_BasicDockedTools as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, enable_prefab_system=False) + self._run_test(request, workspace, editor, test_module, batch_mode=False) diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox_Optimized.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox_Optimized.py index ce0d5e43e9..4a472095ae 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox_Optimized.py @@ -19,8 +19,6 @@ class TestAutomationAutoTestMode(EditorTestSuite): # Enable only -autotest_mode for these tests. Tests cannot run in -BatchMode due to UI interactions global_extra_cmdline_args = ["-autotest_mode"] - enable_prefab_system = False - class test_Docking_BasicDockedTools(EditorSharedTest): from .EditorScripts import Docking_BasicDockedTools as test_module From 4426d15d32c76d924e6870c4006cada0704295ef Mon Sep 17 00:00:00 2001 From: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> Date: Tue, 4 Jan 2022 11:25:13 -0600 Subject: [PATCH 02/26] Adding explicit wait to avoid teardown issues with external tools Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> --- .../editor/EditorScripts/Menus_FileMenuOptions.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py index cade2125e2..c319535c05 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py @@ -26,6 +26,8 @@ def Menus_FileMenuOptions_Work(): :return: None """ + import azlmbr.legacy.general as general + import editor_python_test_tools.hydra_editor_utils as hydra import editor_python_test_tools.pyside_utils as pyside_utils from editor_python_test_tools.utils import Report @@ -64,6 +66,9 @@ def Menus_FileMenuOptions_Work(): ) Report.result(menu_action_triggered, action_triggered) + # Wait a few seconds for Project Settings dialogs to load so teardown can properly close them + general.idle_wait(2.0) + if __name__ == "__main__": From 2296cf228c42158c0c01a6a6ea5d45f9ebe2d257 Mon Sep 17 00:00:00 2001 From: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> Date: Wed, 5 Jan 2022 13:00:14 -0600 Subject: [PATCH 03/26] Removing custom teardown for closing O3DE applications, and adding to list of LY_PROCESSES to close with test Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> --- .../Gem/PythonTests/automatedtesting_shared/base.py | 2 +- .../editor/EditorScripts/Menus_FileMenuOptions.py | 5 ----- .../PythonTests/editor/TestSuite_Main_Optimized.py | 6 +++--- .../Gem/PythonTests/editor/TestSuite_Periodic.py | 11 ++--------- .../ly_test_tools/o3de/editor_test_utils.py | 2 +- 5 files changed, 7 insertions(+), 19 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/base.py b/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/base.py index 0441f8a1dc..4db959b23e 100755 --- a/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/base.py +++ b/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/base.py @@ -182,7 +182,7 @@ class TestAutomationBase: @staticmethod def _kill_ly_processes(include_asset_processor=True): LY_PROCESSES = [ - 'Editor', 'Profiler', 'RemoteConsole', 'AutomatedTesting.ServerLauncher' + 'Editor', 'Profiler', 'RemoteConsole', 'AutomatedTesting.ServerLauncher', 'o3de' ] AP_PROCESSES = [ 'AssetProcessor', 'AssetProcessorBatch', 'AssetBuilder', 'CrySCompileServer', diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py index c319535c05..cade2125e2 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py @@ -26,8 +26,6 @@ def Menus_FileMenuOptions_Work(): :return: None """ - import azlmbr.legacy.general as general - import editor_python_test_tools.hydra_editor_utils as hydra import editor_python_test_tools.pyside_utils as pyside_utils from editor_python_test_tools.utils import Report @@ -66,9 +64,6 @@ def Menus_FileMenuOptions_Work(): ) Report.result(menu_action_triggered, action_triggered) - # Wait a few seconds for Project Settings dialogs to load so teardown can properly close them - general.idle_wait(2.0) - if __name__ == "__main__": diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py index 7364cd8bc9..058c309652 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py @@ -24,11 +24,11 @@ class TestAutomationNoAutoTestMode(EditorTestSuite): class test_AssetPicker_UI_UX(EditorSharedTest): from .EditorScripts import AssetPicker_UI_UX as test_module - class test_BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(EditorSingleTest): + class test_BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(EditorSharedTest): from .EditorScripts import BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD as test_module class test_BasicEditorWorkflows_LevelEntityComponentCRUD(EditorSingleTest): - # Custom teardown to remove slice asset created during test + # Custom teardown to remove level created during test def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], True, True) @@ -39,7 +39,7 @@ class TestAutomationNoAutoTestMode(EditorTestSuite): # Disable null renderer use_null_renderer = False - # Custom teardown to remove slice asset created during test + # Custom teardown to remove level created during test def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], True, True) diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py index f8a054d517..282276250f 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py @@ -26,13 +26,6 @@ def remove_test_level(request, workspace, project): request.addfinalizer(teardown) -@pytest.fixture -def kill_external_tools(request): - def teardown(): - process_utils.kill_processes_named("o3de.exe") - request.addfinalizer(teardown) - - @pytest.mark.SUITE_periodic @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) @@ -40,7 +33,7 @@ class TestAutomation(TestAutomationBase): def test_AssetBrowser_SearchFiltering(self, request, workspace, editor, launcher_platform): from .EditorScripts import AssetBrowser_SearchFiltering as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False) + self._run_test(request, workspace, editor, test_module, batch_mode=False, use_null_renderer=False) def test_AssetBrowser_TreeNavigation(self, request, workspace, editor, launcher_platform): from .EditorScripts import AssetBrowser_TreeNavigation as test_module @@ -58,7 +51,7 @@ class TestAutomation(TestAutomationBase): from .EditorScripts import InputBindings_Add_Remove_Input_Events as test_module self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False) - def test_Menus_FileMenuOptions_Work(self, request, workspace, editor, launcher_platform, kill_external_tools): + def test_Menus_FileMenuOptions_Work(self, request, workspace, editor, launcher_platform): from .EditorScripts import Menus_FileMenuOptions as test_module self._run_test(request, workspace, editor, test_module, batch_mode=False) diff --git a/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py b/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py index 9f1e01342c..dd0bede4b8 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py @@ -24,7 +24,7 @@ def kill_all_ly_processes(include_asset_processor: bool = True) -> None: :return: None """ LY_PROCESSES = [ - 'Editor', 'Profiler', 'RemoteConsole', + 'Editor', 'Profiler', 'RemoteConsole', 'o3de' ] AP_PROCESSES = [ 'AssetProcessor', 'AssetProcessorBatch', 'AssetBuilder' From f71efa8b9bde2b48e04f8e88e57db45cceca4d27 Mon Sep 17 00:00:00 2001 From: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> Date: Wed, 5 Jan 2022 14:16:35 -0600 Subject: [PATCH 04/26] Updating KillAllLyProcesses tests to expect o3de.exe process Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> --- Tools/LyTestTools/tests/unit/test_editor_test_utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tools/LyTestTools/tests/unit/test_editor_test_utils.py b/Tools/LyTestTools/tests/unit/test_editor_test_utils.py index cd361d9e8a..849fa5c391 100644 --- a/Tools/LyTestTools/tests/unit/test_editor_test_utils.py +++ b/Tools/LyTestTools/tests/unit/test_editor_test_utils.py @@ -17,14 +17,15 @@ class TestEditorTestUtils(unittest.TestCase): @mock.patch('ly_test_tools.environment.process_utils.kill_processes_named') def test_KillAllLyProcesses_IncludeAP_CallsCorrectly(self, mock_kill_processes_named): - process_list = ['Editor', 'Profiler', 'RemoteConsole', 'AssetProcessor', 'AssetProcessorBatch', 'AssetBuilder'] + process_list = ['Editor', 'Profiler', 'RemoteConsole', 'o3de', 'AssetProcessor', 'AssetProcessorBatch', + 'AssetBuilder'] editor_test_utils.kill_all_ly_processes(include_asset_processor=True) mock_kill_processes_named.assert_called_once_with(process_list, ignore_extensions=True) @mock.patch('ly_test_tools.environment.process_utils.kill_processes_named') def test_KillAllLyProcesses_NotIncludeAP_CallsCorrectly(self, mock_kill_processes_named): - process_list = ['Editor', 'Profiler', 'RemoteConsole'] + process_list = ['Editor', 'Profiler', 'RemoteConsole', 'o3de'] ap_process_list = ['AssetProcessor', 'AssetProcessorBatch', 'AssetBuilder'] editor_test_utils.kill_all_ly_processes(include_asset_processor=False) From 9c85cd19a77fde4da716ad7a97ccd4f92ed0f5dc Mon Sep 17 00:00:00 2001 From: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> Date: Wed, 5 Jan 2022 14:17:01 -0600 Subject: [PATCH 05/26] Skipping Docking test due to unknown Jenkins failure Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> --- .../Gem/PythonTests/editor/TestSuite_Main_Optimized.py | 1 + 1 file changed, 1 insertion(+) diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py index 058c309652..7e07d125fd 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py @@ -66,6 +66,7 @@ class TestAutomationAutoTestMode(EditorTestSuite): class test_ComponentCRUD_Add_Delete_Components(EditorSharedTest): from .EditorScripts import ComponentCRUD_Add_Delete_Components as test_module + @pytest.mark.skip("Passes locally, fails on Jenkins") class test_Docking_BasicDockedTools(EditorSharedTest): from .EditorScripts import Docking_BasicDockedTools as test_module From 6ae8c6343194e58712eb620958ee9021555bdcab Mon Sep 17 00:00:00 2001 From: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> Date: Wed, 5 Jan 2022 16:27:21 -0600 Subject: [PATCH 06/26] Re-enabling Periodic suite for testing on Jenkins Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> --- .../Gem/PythonTests/editor/CMakeLists.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt index 1fc71da972..a43b3647f9 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt @@ -50,4 +50,17 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ Editor ) + ly_add_pytest( + NAME AutomatedTesting::EditorTests_Periodic + TEST_SUITE periodic + TEST_SERIAL + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Periodic.py + RUNTIME_DEPENDENCIES + Legacy::Editor + AZ::AssetProcessor + AutomatedTesting.Assets + COMPONENT + Editor + ) + endif() From 60b8292abffb39f0969c9c324e8a3c173b09d72e Mon Sep 17 00:00:00 2001 From: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> Date: Thu, 6 Jan 2022 10:12:15 -0600 Subject: [PATCH 07/26] Adding Docking and EditMenu tests to Periodic suite for Jenkins testing Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> --- .../Gem/PythonTests/editor/TestSuite_Periodic.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py index 282276250f..6e7bc413d3 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py @@ -47,10 +47,18 @@ class TestAutomation(TestAutomationBase): from .EditorScripts import ComponentCRUD_Add_Delete_Components as test_module self._run_test(request, workspace, editor, test_module, batch_mode=False) + def test_Docking_BasicDockedTools(self, request, workspace, editor, launcher_platform): + from .EditorScripts import Docking_BasicDockedTools as test_module + self._run_test(request, workspace, editor, test_module, batch_mode=False) + def test_InputBindings_Add_Remove_Input_Events(self, request, workspace, editor, launcher_platform): from .EditorScripts import InputBindings_Add_Remove_Input_Events as test_module self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False) + def test_Menus_EditMenuOptions_Work(self, request, workspace, editor, launcher_platform): + from .EditorScripts import Menus_EditMenuOptions as test_module + self._run_test(request, workspace, editor, test_module, batch_mode=False) + def test_Menus_FileMenuOptions_Work(self, request, workspace, editor, launcher_platform): from .EditorScripts import Menus_FileMenuOptions as test_module self._run_test(request, workspace, editor, test_module, batch_mode=False) From 7b172667cfe321982b7594bc70fe530bf60604a9 Mon Sep 17 00:00:00 2001 From: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> Date: Thu, 6 Jan 2022 13:13:00 -0600 Subject: [PATCH 08/26] Adding info lines for debugging File Menu tests and waits during drag and drop operations in Docking test Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> --- .../editor/EditorScripts/Docking_BasicDockedTools.py | 9 ++++++--- .../editor/EditorScripts/Menus_EditMenuOptions.py | 1 + .../editor/EditorScripts/Menus_FileMenuOptions.py | 1 + .../editor/EditorScripts/Menus_ViewMenuOptions.py | 1 + 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Docking_BasicDockedTools.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Docking_BasicDockedTools.py index d83db7d90c..8734769225 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Docking_BasicDockedTools.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Docking_BasicDockedTools.py @@ -85,22 +85,25 @@ def Docking_BasicDockedTools(): # We drag/drop it over the viewport since it doesn't allow docking, so this will undock it render_overlay = editor_window.findChild(QtWidgets.QWidget, "renderOverlay") pyside_utils.drag_and_drop(entity_outliner, render_overlay) - + general.idle_wait(0.5) + # We need to grab a new reference to the Entity Outliner QDockWidget because when it gets moved - # to the floating window, its parent changes so the wrapped intance we had becomes invalid + # to the floating window, its parent changes so the wrapped instance we had becomes invalid entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner") # Dock the Entity Inspector tabbed with the floating Entity Outliner entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector") pyside_utils.drag_and_drop(entity_inspector, entity_outliner) + general.idle_wait(0.5) # We need to grab a new reference to the Entity Inspector QDockWidget because when it gets moved - # to the floating window, its parent changes so the wrapped intance we had becomes invalid + # to the floating window, its parent changes so the wrapped instance we had becomes invalid entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector") # Dock the Console tabbed with the floating Entity Inspector console = editor_window.findChild(QtWidgets.QDockWidget, "Console") pyside_utils.drag_and_drop(console, entity_inspector) + general.idle_wait(0.5) # Check to ensure all the tools are parented to the same QStackedWidget def check_all_panes_tabbed(): diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py index 7d72d76776..0536575893 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py @@ -64,6 +64,7 @@ def Menus_EditMenuOptions_Work(): for option in edit_menu_options: try: action = pyside_utils.get_action_for_menu_path(editor_window, "Edit", *option) + Report.info(f"Triggering {action.iconText()}") action.trigger() action_triggered = True except Exception as e: diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py index cade2125e2..a4e702cbd1 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py @@ -53,6 +53,7 @@ def Menus_FileMenuOptions_Work(): for option in file_menu_options: try: action = pyside_utils.get_action_for_menu_path(editor_window, "File", *option) + Report.info(f"Triggering {action.iconText()}") action.trigger() action_triggered = True except Exception as e: diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py index 2d92fdb97c..e2ee3e2a55 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py @@ -52,6 +52,7 @@ def Menus_ViewMenuOptions_Work(): for option in view_menu_options: try: action = pyside_utils.get_action_for_menu_path(editor_window, "View", *option) + Report.info(f"Triggering {action.iconText()}") action.trigger() action_triggered = True except Exception as e: From f8734d7d067e12ca0755e4fd4f50798964ca58f5 Mon Sep 17 00:00:00 2001 From: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> Date: Fri, 7 Jan 2022 09:44:43 -0600 Subject: [PATCH 09/26] Finalizing update of Editor tests to utilize prefab system Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> --- .../Gem/PythonTests/editor/CMakeLists.txt | 32 +------ .../EditorScripts/Docking_BasicDockedTools.py | 5 +- .../EditorScripts/Menus_EditMenuOptions.py | 5 +- .../EditorScripts/Menus_FileMenuOptions.py | 2 +- .../EditorScripts/Menus_ViewMenuOptions.py | 2 + .../Gem/PythonTests/editor/TestSuite_Main.py | 87 +++++++++++++------ .../editor/TestSuite_Main_Optimized.py | 83 ------------------ .../PythonTests/editor/TestSuite_Periodic.py | 68 --------------- .../PythonTests/editor/TestSuite_Sandbox.py | 27 ------ .../editor/TestSuite_Sandbox_Optimized.py | 26 ------ 10 files changed, 69 insertions(+), 268 deletions(-) delete mode 100644 AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py delete mode 100644 AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py delete mode 100644 AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox.py delete mode 100644 AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox_Optimized.py diff --git a/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt index a43b3647f9..b3f0d2da8e 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/editor/CMakeLists.txt @@ -9,10 +9,10 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_FOUNDATION_TEST_SUPPORTED) ly_add_pytest( - NAME AutomatedTesting::EditorTests_Main_Optimized + NAME AutomatedTesting::EditorTests_Main TEST_SUITE main TEST_SERIAL - PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_Optimized.py + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py PYTEST_MARKS "not REQUIRES_gpu" RUNTIME_DEPENDENCIES Legacy::Editor @@ -27,7 +27,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ TEST_SUITE main TEST_SERIAL TEST_REQUIRES gpu - PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_Optimized.py + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py PYTEST_MARKS "REQUIRES_gpu" RUNTIME_DEPENDENCIES Legacy::Editor @@ -37,30 +37,4 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ Editor ) - ly_add_pytest( - NAME AutomatedTesting::EditorTests_Sandbox_Optimized - TEST_SUITE sandbox - TEST_SERIAL - PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Sandbox_Optimized.py - RUNTIME_DEPENDENCIES - Legacy::Editor - AZ::AssetProcessor - AutomatedTesting.Assets - COMPONENT - Editor - ) - - ly_add_pytest( - NAME AutomatedTesting::EditorTests_Periodic - TEST_SUITE periodic - TEST_SERIAL - PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Periodic.py - RUNTIME_DEPENDENCIES - Legacy::Editor - AZ::AssetProcessor - AutomatedTesting.Assets - COMPONENT - Editor - ) - endif() diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Docking_BasicDockedTools.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Docking_BasicDockedTools.py index 8734769225..a5a94f06a5 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Docking_BasicDockedTools.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Docking_BasicDockedTools.py @@ -85,8 +85,7 @@ def Docking_BasicDockedTools(): # We drag/drop it over the viewport since it doesn't allow docking, so this will undock it render_overlay = editor_window.findChild(QtWidgets.QWidget, "renderOverlay") pyside_utils.drag_and_drop(entity_outliner, render_overlay) - general.idle_wait(0.5) - + # We need to grab a new reference to the Entity Outliner QDockWidget because when it gets moved # to the floating window, its parent changes so the wrapped instance we had becomes invalid entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner") @@ -94,7 +93,6 @@ def Docking_BasicDockedTools(): # Dock the Entity Inspector tabbed with the floating Entity Outliner entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector") pyside_utils.drag_and_drop(entity_inspector, entity_outliner) - general.idle_wait(0.5) # We need to grab a new reference to the Entity Inspector QDockWidget because when it gets moved # to the floating window, its parent changes so the wrapped instance we had becomes invalid @@ -103,7 +101,6 @@ def Docking_BasicDockedTools(): # Dock the Console tabbed with the floating Entity Inspector console = editor_window.findChild(QtWidgets.QDockWidget, "Console") pyside_utils.drag_and_drop(console, entity_inspector) - general.idle_wait(0.5) # Check to ensure all the tools are parented to the same QStackedWidget def check_all_panes_tabbed(): diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py index 0536575893..ce85cf223f 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py @@ -52,8 +52,9 @@ def Menus_EditMenuOptions_Work(): ("Editor Settings", "Global Preferences"), ("Editor Settings", "Editor Settings Manager"), ("Editor Settings", "Keyboard Customization", "Customize Keyboard"), - ("Editor Settings", "Keyboard Customization", "Export Keyboard Settings"), - ("Editor Settings", "Keyboard Customization", "Import Keyboard Settings"), + # The following menu options are temporarily disabled due to https://github.com/o3de/o3de/issues/6746 + #("Editor Settings", "Keyboard Customization", "Export Keyboard Settings"), + #("Editor Settings", "Keyboard Customization", "Import Keyboard Settings"), ] # 1) Open an existing simple level diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py index a4e702cbd1..4fcdc371e7 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py @@ -33,7 +33,7 @@ def Menus_FileMenuOptions_Work(): file_menu_options = [ ("New Level",), #("Open Level",), Temporarily disabled due to https://github.com/o3de/o3de/issues/6605 - ("Import",), + #("Import",), Temporarily disabled due to https://github.com/o3de/o3de/issues/6746 ("Save",), #("Save As",), Temporarily disabled due to https://github.com/o3de/o3de/issues/6605 ("Save Level Statistics",), diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py index e2ee3e2a55..bb9ff15082 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py @@ -33,6 +33,8 @@ def Menus_ViewMenuOptions_Work(): view_menu_options = [ ("Center on Selection",), ("Show Quick Access Bar",), + ("Layouts", "Component Entity Layout",), + ("Layouts", "Save Layout",), ("Viewport", "Configure Layout"), ("Viewport", "Go to Position"), ("Viewport", "Center on Selection"), diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py index 949aab140d..3805ef15dd 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py @@ -7,46 +7,77 @@ SPDX-License-Identifier: Apache-2.0 OR MIT import os import pytest -import sys import ly_test_tools.environment.file_system as file_system - -sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared') -from base import TestAutomationBase - - -@pytest.fixture -def remove_test_level(request, workspace, project): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", "tmp_level")], True, True) - - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", "tmp_level")], True, True) - - request.addfinalizer(teardown) +from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite @pytest.mark.SUITE_main @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(TestAutomationBase): +class TestAutomationNoAutoTestMode(EditorTestSuite): - def test_BasicEditorWorkflows_LevelEntityComponentCRUD(self, request, workspace, editor, launcher_platform, - remove_test_level): + # Disable -autotest_mode and -BatchMode. Tests cannot run in -BatchMode due to UI interactions, and these tests + # interact with modal dialogs + global_extra_cmdline_args = [] + + class test_AssetPicker_UI_UX(EditorSharedTest): + from .EditorScripts import AssetPicker_UI_UX as test_module + + class test_BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(EditorSharedTest): + from .EditorScripts import BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD as test_module + + class test_BasicEditorWorkflows_LevelEntityComponentCRUD(EditorSingleTest): + # Custom teardown to remove level created during test + def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): + file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], + True, True) from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False) @pytest.mark.REQUIRES_gpu - def test_BasicEditorWorkflows_GPU_LevelEntityComponentCRUD(self, request, workspace, editor, launcher_platform, - remove_test_level): + class test_BasicEditorWorkflows_GPU_LevelEntityComponentCRUD(EditorSingleTest): + # Disable null renderer + use_null_renderer = False + + # Custom teardown to remove level created during test + def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): + file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], + True, True) from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False, - use_null_renderer=False) - def test_BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(self, request, workspace, editor, - launcher_platform): - from .EditorScripts import BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False) + class test_InputBindings_Add_Remove_Input_Events(EditorSharedTest): + from .EditorScripts import InputBindings_Add_Remove_Input_Events as test_module - def test_EntityOutliner_EntityOrdering(self, request, workspace, editor, launcher_platform): + +@pytest.mark.SUITE_main +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +class TestAutomationAutoTestMode(EditorTestSuite): + + # Enable only -autotest_mode for these tests. Tests cannot run in -BatchMode due to UI interactions + global_extra_cmdline_args = ["-autotest_mode"] + + class test_AssetBrowser_SearchFiltering(EditorSharedTest): + from .EditorScripts import AssetBrowser_SearchFiltering as test_module + + class test_AssetBrowser_TreeNavigation(EditorSharedTest): + from .EditorScripts import AssetBrowser_TreeNavigation as test_module + + class test_ComponentCRUD_Add_Delete_Components(EditorSharedTest): + from .EditorScripts import ComponentCRUD_Add_Delete_Components as test_module + + @pytest.mark.skip("Passes locally/fails on Jenkins. https://github.com/o3de/o3de/issues/6747") + class test_Docking_BasicDockedTools(EditorSharedTest): + from .EditorScripts import Docking_BasicDockedTools as test_module + + class test_EntityOutliner_EntityOrdering(EditorSharedTest): from .EditorScripts import EntityOutliner_EntityOrdering as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False) + + class test_Menus_EditMenuOptions_Work(EditorSharedTest): + from .EditorScripts import Menus_EditMenuOptions as test_module + + class test_Menus_FileMenuOptions_Work(EditorSharedTest): + from .EditorScripts import Menus_FileMenuOptions as test_module + + class test_Menus_ViewMenuOptions_Work(EditorSharedTest): + from .EditorScripts import Menus_ViewMenuOptions as test_module diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py deleted file mode 100644 index 7e07d125fd..0000000000 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main_Optimized.py +++ /dev/null @@ -1,83 +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 -""" - -import os -import pytest - -import ly_test_tools.environment.file_system as file_system -from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite - - -@pytest.mark.SUITE_main -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomationNoAutoTestMode(EditorTestSuite): - - # Disable -autotest_mode and -BatchMode. Tests cannot run in -BatchMode due to UI interactions, and these tests - # interact with modal dialogs - global_extra_cmdline_args = [] - - class test_AssetPicker_UI_UX(EditorSharedTest): - from .EditorScripts import AssetPicker_UI_UX as test_module - - class test_BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD(EditorSharedTest): - from .EditorScripts import BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD as test_module - - class test_BasicEditorWorkflows_LevelEntityComponentCRUD(EditorSingleTest): - # Custom teardown to remove level created during test - def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): - file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], - True, True) - from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module - - @pytest.mark.REQUIRES_gpu - class test_BasicEditorWorkflows_GPU_LevelEntityComponentCRUD(EditorSingleTest): - # Disable null renderer - use_null_renderer = False - - # Custom teardown to remove level created during test - def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): - file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], - True, True) - from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module - - class test_InputBindings_Add_Remove_Input_Events(EditorSharedTest): - from .EditorScripts import InputBindings_Add_Remove_Input_Events as test_module - - -@pytest.mark.SUITE_main -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomationAutoTestMode(EditorTestSuite): - - # Enable only -autotest_mode for these tests. Tests cannot run in -BatchMode due to UI interactions - global_extra_cmdline_args = ["-autotest_mode"] - - class test_AssetBrowser_SearchFiltering(EditorSharedTest): - from .EditorScripts import AssetBrowser_SearchFiltering as test_module - - class test_AssetBrowser_TreeNavigation(EditorSharedTest): - from .EditorScripts import AssetBrowser_TreeNavigation as test_module - - class test_ComponentCRUD_Add_Delete_Components(EditorSharedTest): - from .EditorScripts import ComponentCRUD_Add_Delete_Components as test_module - - @pytest.mark.skip("Passes locally, fails on Jenkins") - class test_Docking_BasicDockedTools(EditorSharedTest): - from .EditorScripts import Docking_BasicDockedTools as test_module - - class test_EntityOutliner_EntityOrdering(EditorSharedTest): - from .EditorScripts import EntityOutliner_EntityOrdering as test_module - - class test_Menus_EditMenuOptions_Work(EditorSharedTest): - from .EditorScripts import Menus_EditMenuOptions as test_module - - class test_Menus_FileMenuOptions_Work(EditorSharedTest): - from .EditorScripts import Menus_FileMenuOptions as test_module - - class test_Menus_ViewMenuOptions_Work(EditorSharedTest): - from .EditorScripts import Menus_ViewMenuOptions as test_module \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py deleted file mode 100644 index 6e7bc413d3..0000000000 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Periodic.py +++ /dev/null @@ -1,68 +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 -""" - -import os -import pytest -import sys - -import ly_test_tools.environment.file_system as file_system -import ly_test_tools.environment.process_utils as process_utils - -sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared') -from base import TestAutomationBase - - -@pytest.fixture -def remove_test_level(request, workspace, project): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", "tmp_level")], True, True) - - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", "tmp_level")], True, True) - - request.addfinalizer(teardown) - - -@pytest.mark.SUITE_periodic -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(TestAutomationBase): - - def test_AssetBrowser_SearchFiltering(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AssetBrowser_SearchFiltering as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, use_null_renderer=False) - - def test_AssetBrowser_TreeNavigation(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AssetBrowser_TreeNavigation as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False) - - def test_AssetPicker_UI_UX(self, request, workspace, editor, launcher_platform): - from .EditorScripts import AssetPicker_UI_UX as test_module - self._run_test(request, workspace, editor, test_module, autotest_mode=False, batch_mode=False) - - def test_ComponentCRUD_Add_Delete_Components(self, request, workspace, editor, launcher_platform): - from .EditorScripts import ComponentCRUD_Add_Delete_Components as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False) - - def test_Docking_BasicDockedTools(self, request, workspace, editor, launcher_platform): - from .EditorScripts import Docking_BasicDockedTools as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False) - - def test_InputBindings_Add_Remove_Input_Events(self, request, workspace, editor, launcher_platform): - from .EditorScripts import InputBindings_Add_Remove_Input_Events as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False) - - def test_Menus_EditMenuOptions_Work(self, request, workspace, editor, launcher_platform): - from .EditorScripts import Menus_EditMenuOptions as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False) - - def test_Menus_FileMenuOptions_Work(self, request, workspace, editor, launcher_platform): - from .EditorScripts import Menus_FileMenuOptions as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False) - - def test_Menus_ViewMenuOptions_Work(self, request, workspace, editor, launcher_platform): - from .EditorScripts import Menus_ViewMenuOptions as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False) diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox.py deleted file mode 100644 index 98a6620d9c..0000000000 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox.py +++ /dev/null @@ -1,27 +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 -""" - -import os -import pytest -import sys - -sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared') -from base import TestAutomationBase - - -@pytest.mark.SUITE_sandbox -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomation(TestAutomationBase): - - def test_Menus_EditMenuOptions_Work(self, request, workspace, editor, launcher_platform): - from .EditorScripts import Menus_EditMenuOptions as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False) - - def test_Docking_BasicDockedTools(self, request, workspace, editor, launcher_platform): - from .EditorScripts import Docking_BasicDockedTools as test_module - self._run_test(request, workspace, editor, test_module, batch_mode=False) diff --git a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox_Optimized.py b/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox_Optimized.py deleted file mode 100644 index 4a472095ae..0000000000 --- a/AutomatedTesting/Gem/PythonTests/editor/TestSuite_Sandbox_Optimized.py +++ /dev/null @@ -1,26 +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 -""" - -import os -import pytest - -from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite - - -@pytest.mark.SUITE_sandbox -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestAutomationAutoTestMode(EditorTestSuite): - - # Enable only -autotest_mode for these tests. Tests cannot run in -BatchMode due to UI interactions - global_extra_cmdline_args = ["-autotest_mode"] - - class test_Docking_BasicDockedTools(EditorSharedTest): - from .EditorScripts import Docking_BasicDockedTools as test_module - - class test_Menus_EditMenuOptions_Work(EditorSharedTest): - from .EditorScripts import Menus_EditMenuOptions as test_module From 641f7be041b0671a12340ae678608cbb757ff7ec Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Fri, 7 Jan 2022 14:24:53 -0600 Subject: [PATCH 10/26] Removed more unused Editor code and images Signed-off-by: Chris Galvan --- Code/Editor/EditorViewportWidget.cpp | 3 - Code/Editor/IEditor.h | 1 - Code/Editor/Include/IConsoleConnectivity.h | 85 ------------------- Code/Editor/Include/IFacialEditor.h | 43 ---------- Code/Editor/Include/IRenderListener.h | 29 ------- Code/Editor/Include/ITextureDatabaseUpdater.h | 38 --------- Code/Editor/MainWindow.qrc | 1 - Code/Editor/Resource.h | 2 - Code/Editor/TimeOfDay/main-00.png | 3 - Code/Editor/TimeOfDay/main-01.png | 3 - Code/Editor/TimeOfDay/main-02.png | 3 - Code/Editor/TimeOfDay/main-03.png | 3 - Code/Editor/TimeOfDay/main-04.png | 3 - Code/Editor/TimeOfDay/main-05.png | 3 - Code/Editor/TimeOfDay/main-06.png | 3 - Code/Editor/TimeOfDay/main-07.png | 3 - Code/Editor/TimeOfDay/main-08.png | 3 - Code/Editor/TimeOfDay/main-09.png | 3 - Code/Editor/TimeOfDay/main-10.png | 3 - Code/Editor/TimeOfDay/main-11.png | 3 - Code/Editor/TimeOfDay/main-12.png | 3 - Code/Editor/Viewport.cpp | 68 --------------- Code/Editor/Viewport.h | 13 --- Code/Editor/editor_lib_files.cmake | 3 - Code/Editor/water.png | 3 - 25 files changed, 328 deletions(-) delete mode 100644 Code/Editor/Include/IConsoleConnectivity.h delete mode 100644 Code/Editor/Include/IFacialEditor.h delete mode 100644 Code/Editor/Include/IRenderListener.h delete mode 100644 Code/Editor/Include/ITextureDatabaseUpdater.h delete mode 100644 Code/Editor/TimeOfDay/main-00.png delete mode 100644 Code/Editor/TimeOfDay/main-01.png delete mode 100644 Code/Editor/TimeOfDay/main-02.png delete mode 100644 Code/Editor/TimeOfDay/main-03.png delete mode 100644 Code/Editor/TimeOfDay/main-04.png delete mode 100644 Code/Editor/TimeOfDay/main-05.png delete mode 100644 Code/Editor/TimeOfDay/main-06.png delete mode 100644 Code/Editor/TimeOfDay/main-07.png delete mode 100644 Code/Editor/TimeOfDay/main-08.png delete mode 100644 Code/Editor/TimeOfDay/main-09.png delete mode 100644 Code/Editor/TimeOfDay/main-10.png delete mode 100644 Code/Editor/TimeOfDay/main-11.png delete mode 100644 Code/Editor/TimeOfDay/main-12.png delete mode 100644 Code/Editor/water.png diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp index 754ea84544..828812fb7e 100644 --- a/Code/Editor/EditorViewportWidget.cpp +++ b/Code/Editor/EditorViewportWidget.cpp @@ -454,9 +454,6 @@ void EditorViewportWidget::Update() // Render { - // TODO: Move out this logic to a controller and refactor to work with Atom - ProcessRenderLisneters(m_displayContext); - m_displayContext.Flush2D(); // Post Render Callback diff --git a/Code/Editor/IEditor.h b/Code/Editor/IEditor.h index c91ca62c5e..90f1b63f55 100644 --- a/Code/Editor/IEditor.h +++ b/Code/Editor/IEditor.h @@ -52,7 +52,6 @@ class CUIEnumsDatabase; struct ISourceControl; struct IEditorClassFactory; struct ITransformManipulator; -class IFacialEditor; class CDialog; #if defined(AZ_PLATFORM_WINDOWS) class C3DConnexionDriver; diff --git a/Code/Editor/Include/IConsoleConnectivity.h b/Code/Editor/Include/IConsoleConnectivity.h deleted file mode 100644 index 0f5e9bf35c..0000000000 --- a/Code/Editor/Include/IConsoleConnectivity.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -// Description : Standard interface for console connectivity plugins. - - -#ifndef CRYINCLUDE_EDITOR_INCLUDE_ICONSOLECONNECTIVITY_H -#define CRYINCLUDE_EDITOR_INCLUDE_ICONSOLECONNECTIVITY_H -#pragma once - - -////////////////////////////////////////////////////////////////////////// -// Description -// This interface provide access to the console connectivity -// functionality. -////////////////////////////////////////////////////////////////////////// -struct IConsoleConnectivity - : public IUnknown -{ - DEFINE_UUID(0x4DAA85E1, 0x8498, 0x402f, 0x9B, 0x85, 0x7F, 0x62, 0x9D, 0x76, 0x79, 0x8A); - - ////////////////////////////////////////////////////////////////////////// - //TODO: Must add the useful interface here. - ////////////////////////////////////////////////////////////////////////// - - // Description: - // Checks if a development console is connected to the development PC. - // See Also: - // Arguments: - // Nothing - // Return: - // bool - true if it is connected, false otherwise. - virtual bool IsConnectedToConsole() = 0; - - // Description: - // Send a file from the specified local filename to the console platform creating the full path - // as required so it can copy to the remote filename. - // See Also: - // Nothing - // Arguments: - // szLocalFileName - is the local filename from which you want to copy the file. - // szRemoteFilename - is the full path and filename to where you want to copy the file. - // Return: - // bool - true if the copy succeeded, false otherwise. - virtual bool SendFile(const char* szLocalFileName, const char* szRemoteFilename) = 0; - - // Description: - // Notifies to the console that a file has been changed, typically uploaded. - // This will be usually called after a SendFile (see above) call, so that the - // system running on the console may decide what to do with this new file. - // Typically the system will have to load or reloads this new file. - // See Also: - // SendFile - // Arguments: - // szRemoteFilename - is the full path and filename in the console of the changed - // file. - // Return: - // bool - true if succeeded sending the notification, false otherwise. - virtual bool NotifyFileChange(const char* szRemoteFilename) = 0; - - - // Description: - // Gets the the title IP for the connected console . - // Arguments: - // dwConsoleAddressPlaceholder - is the pointer to the placeholder of the variable - // which will contain the title IP of the console. - // Return: - // bool - true if dwConsoleAddressPlaceholder now contains the IP address, else false. - virtual bool GetConsoleAddress(DWORD* dwConsoleAddressPlaceholder) = 0; - ////////////////////////////////////////////////////////////////////////// - // IUnknown - ////////////////////////////////////////////////////////////////////////// - virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) { return E_NOINTERFACE; }; - virtual ULONG STDMETHODCALLTYPE AddRef() { return 0; }; - virtual ULONG STDMETHODCALLTYPE Release() { return 0; }; - ////////////////////////////////////////////////////////////////////////// -}; - -#endif // CRYINCLUDE_EDITOR_INCLUDE_ICONSOLECONNECTIVITY_H diff --git a/Code/Editor/Include/IFacialEditor.h b/Code/Editor/Include/IFacialEditor.h deleted file mode 100644 index 5dfa9ab03f..0000000000 --- a/Code/Editor/Include/IFacialEditor.h +++ /dev/null @@ -1,43 +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 - * - */ - - -#ifndef CRYINCLUDE_EDITOR_INCLUDE_IFACIALEDITOR_H -#define CRYINCLUDE_EDITOR_INCLUDE_IFACIALEDITOR_H -#pragma once - - -class IFacialEditor -{ -public: - enum EyeType - { - EYE_LEFT, - EYE_RIGHT - }; - - virtual int GetNumMorphTargets() const = 0; - virtual const char* GetMorphTargetName(int index) const = 0; - virtual void PreviewEffector(int index, float value) = 0; - virtual void ClearAllPreviewEffectors() = 0; - virtual void SetForcedNeckRotation(const Quat& rotation) = 0; - virtual void SetForcedEyeRotation(const Quat& rotation, EyeType eye) = 0; - virtual int GetJoystickCount() const = 0; - virtual const char* GetJoystickName(int joystickIndex) const = 0; - virtual void SetJoystickPosition(int joystickIndex, float x, float y) = 0; - virtual void GetJoystickPosition(int joystickIndex, float& x, float& y) const = 0; - virtual void LoadJoystickFile(const char* filename) = 0; - virtual void LoadCharacter(const char* filename) = 0; - virtual void LoadSequence(const char* filename) = 0; - virtual void SetVideoFrameResolution(int width, int height, int bpp) = 0; - virtual int GetVideoFramePitch() = 0; - virtual void* GetVideoFrameBits() = 0; - virtual void ShowVideoFramePane() = 0; -}; - -#endif // CRYINCLUDE_EDITOR_INCLUDE_IFACIALEDITOR_H diff --git a/Code/Editor/Include/IRenderListener.h b/Code/Editor/Include/IRenderListener.h deleted file mode 100644 index 892a42b701..0000000000 --- a/Code/Editor/Include/IRenderListener.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -// Description : Interface for rendering custom 3D elements in the main -// render viewport. Particularly usefull for debug geometries. - - -#ifndef CRYINCLUDE_EDITOR_INCLUDE_IRENDERLISTENER_H -#define CRYINCLUDE_EDITOR_INCLUDE_IRENDERLISTENER_H -#pragma once - - -struct DisplayContext; - -struct IRenderListener - : public IUnknown -{ - DEFINE_UUID(0x8D52F857, 0x1027, 0x4346, 0xAC, 0x7B, 0xF6, 0x20, 0xDA, 0x7C, 0xCE, 0x42) - - virtual void Render(DisplayContext& rDisplayContext) = 0; -}; - -#endif // CRYINCLUDE_EDITOR_INCLUDE_IRENDERLISTENER_H diff --git a/Code/Editor/Include/ITextureDatabaseUpdater.h b/Code/Editor/Include/ITextureDatabaseUpdater.h deleted file mode 100644 index 4482135b47..0000000000 --- a/Code/Editor/Include/ITextureDatabaseUpdater.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -// Description : This file declares the interface used by the texture viewer -// and (implemented first implemented by the Texture Database Creator) to -// syncronize their threads. A thread interace could be useful there. - -#ifndef CRYINCLUDE_EDITOR_INCLUDE_ITEXTUREDATABASEUPDATER_H -#define CRYINCLUDE_EDITOR_INCLUDE_ITEXTUREDATABASEUPDATER_H -#pragma once - - -class CTextureDatabaseItem; - -struct ITextureDatabaseUpdater -{ -public: - ////////////////////////////////////////////////////////////////////////// - // Thread control - virtual void NotifyShutDown() = 0; - virtual void Lock() = 0; - virtual void Unlock() = 0; - virtual void WaitForThread() = 0; - ////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////// - // Data access - virtual CTextureDatabaseItem* GetItem(const char* szAddItem) = 0; - ////////////////////////////////////////////////////////////////////////// -}; - -#endif // CRYINCLUDE_EDITOR_INCLUDE_ITEXTUREDATABASEUPDATER_H diff --git a/Code/Editor/MainWindow.qrc b/Code/Editor/MainWindow.qrc index 4506a4a2a8..c68e05ef41 100644 --- a/Code/Editor/MainWindow.qrc +++ b/Code/Editor/MainWindow.qrc @@ -166,7 +166,6 @@ arhitype_tree_01.png arhitype_tree_02.png arhitype_tree_03.png - water.png bmp00005_00.png bmp00005_01.png bmp00005_02.png diff --git a/Code/Editor/Resource.h b/Code/Editor/Resource.h index 432f07a531..ef72bfc9be 100644 --- a/Code/Editor/Resource.h +++ b/Code/Editor/Resource.h @@ -196,7 +196,6 @@ #define ID_FILE_EXPORT_TERRAINAREA 33904 #define ID_FILE_EXPORT_TERRAINAREAWITHOBJECTS 33910 #define ID_FILE_EXPORT_SELECTEDOBJECTS 33911 -#define ID_TERRAIN_TIMEOFDAY 33912 #define ID_SPLINE_PREVIOUS_KEY 33916 #define ID_SPLINE_NEXT_KEY 33917 #define ID_SPLINE_FLATTEN_ALL 33918 @@ -290,7 +289,6 @@ #define ID_TV_TRACKS_TOOLBAR_LAST 35183 // for up to 100 "Add Tracks..." dynamically added Track View Track buttons #define ID_OPEN_TERRAIN_EDITOR 36007 #define ID_OPEN_UICANVASEDITOR 36010 -#define ID_TERRAIN_TIMEOFDAYBUTTON 36011 #define ID_OPEN_TERRAINTEXTURE_EDITOR 36012 #define ID_SKINS_REFRESH 36014 #define ID_FILE_GENERATETERRAIN 36016 diff --git a/Code/Editor/TimeOfDay/main-00.png b/Code/Editor/TimeOfDay/main-00.png deleted file mode 100644 index 2c44fec541..0000000000 --- a/Code/Editor/TimeOfDay/main-00.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5201dbba6c8114914ed680b04b72a5e18e22c0519a514bcccdc7ae8d32670b4e -size 993 diff --git a/Code/Editor/TimeOfDay/main-01.png b/Code/Editor/TimeOfDay/main-01.png deleted file mode 100644 index 5cc1bf33d8..0000000000 --- a/Code/Editor/TimeOfDay/main-01.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:48a7250ad41c5e298079ddd13910b58baca2ef592defcc162ccc9df542d28905 -size 981 diff --git a/Code/Editor/TimeOfDay/main-02.png b/Code/Editor/TimeOfDay/main-02.png deleted file mode 100644 index b7d90648a3..0000000000 --- a/Code/Editor/TimeOfDay/main-02.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:98b9e9abcc54b4f3e6903ad74bb50f364bfe4c8cd9fedc9653a6603d64a1ee0a -size 838 diff --git a/Code/Editor/TimeOfDay/main-03.png b/Code/Editor/TimeOfDay/main-03.png deleted file mode 100644 index e073dbf60b..0000000000 --- a/Code/Editor/TimeOfDay/main-03.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1199c834fc8de69f9d7c76e8c7fbd84e9b92713b9f07b513137085e5089432cb -size 857 diff --git a/Code/Editor/TimeOfDay/main-04.png b/Code/Editor/TimeOfDay/main-04.png deleted file mode 100644 index 6049dbfe18..0000000000 --- a/Code/Editor/TimeOfDay/main-04.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:79b44be1dbe5518e06dc8c8823d00462136d39ffe60a32ef4f64dc0712654d33 -size 646 diff --git a/Code/Editor/TimeOfDay/main-05.png b/Code/Editor/TimeOfDay/main-05.png deleted file mode 100644 index 054419f39a..0000000000 --- a/Code/Editor/TimeOfDay/main-05.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6ea7450c1a278570e2a1dba3a8b4d7d4e5f0d054e8371139ebdb5220c405d355 -size 537 diff --git a/Code/Editor/TimeOfDay/main-06.png b/Code/Editor/TimeOfDay/main-06.png deleted file mode 100644 index 35f8afdae2..0000000000 --- a/Code/Editor/TimeOfDay/main-06.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bc73f0720f2ff877aff5c6646938b7fc509a69d92e766fecb9fa010eecadee7b -size 606 diff --git a/Code/Editor/TimeOfDay/main-07.png b/Code/Editor/TimeOfDay/main-07.png deleted file mode 100644 index aca9597355..0000000000 --- a/Code/Editor/TimeOfDay/main-07.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e63519ed54fc19a4b4a2a38cb2c5f148b7404ac614d4aab039b71fee64cd7425 -size 569 diff --git a/Code/Editor/TimeOfDay/main-08.png b/Code/Editor/TimeOfDay/main-08.png deleted file mode 100644 index abeb114fc2..0000000000 --- a/Code/Editor/TimeOfDay/main-08.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8742cad4b8f8f5bba59abb9cc028db84de222ed8b393398f6837fea16bb4a1d8 -size 563 diff --git a/Code/Editor/TimeOfDay/main-09.png b/Code/Editor/TimeOfDay/main-09.png deleted file mode 100644 index cc77b8c9e2..0000000000 --- a/Code/Editor/TimeOfDay/main-09.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ef85628301b4edc4f858f0988e4f072772be3feb92d27a2ec33b72a27bcee7ff -size 583 diff --git a/Code/Editor/TimeOfDay/main-10.png b/Code/Editor/TimeOfDay/main-10.png deleted file mode 100644 index dc463c6497..0000000000 --- a/Code/Editor/TimeOfDay/main-10.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d17bfbdee6d37566b241adf64241ef47b62145aaac3e9e8f9691d1fb866b5fcb -size 717 diff --git a/Code/Editor/TimeOfDay/main-11.png b/Code/Editor/TimeOfDay/main-11.png deleted file mode 100644 index d686ab18c8..0000000000 --- a/Code/Editor/TimeOfDay/main-11.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:98b629abf927bcea41d8857b1761d12a37836471d7106b2f5210337c0ace0d9c -size 1103 diff --git a/Code/Editor/TimeOfDay/main-12.png b/Code/Editor/TimeOfDay/main-12.png deleted file mode 100644 index 069510ab24..0000000000 --- a/Code/Editor/TimeOfDay/main-12.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e949111b33e28834995807cab30ecb58d988a81a2d58fa166117962c85b8e149 -size 849 diff --git a/Code/Editor/Viewport.cpp b/Code/Editor/Viewport.cpp index 437dcead3b..dcf86abb02 100644 --- a/Code/Editor/Viewport.cpp +++ b/Code/Editor/Viewport.cpp @@ -30,7 +30,6 @@ #include "Objects/ObjectManager.h" #include "Util/3DConnexionDriver.h" #include "PluginManager.h" -#include "Include/IRenderListener.h" #include "GameEngine.h" #include "Settings.h" @@ -227,61 +226,6 @@ void QtViewport::GetDimensions(int* pWidth, int* pHeight) const } } -////////////////////////////////////////////////////////////////////////// -void QtViewport::RegisterRenderListener(IRenderListener* piListener) -{ -#ifdef _DEBUG - size_t nCount(0); - size_t nTotal(0); - - nTotal = m_cRenderListeners.size(); - for (nCount = 0; nCount < nTotal; ++nCount) - { - if (m_cRenderListeners[nCount] == piListener) - { - assert(!"Registered the same RenderListener multiple times."); - break; - } - } -#endif //_DEBUG - m_cRenderListeners.push_back(piListener); -} - -////////////////////////////////////////////////////////////////////////// -bool QtViewport::UnregisterRenderListener(IRenderListener* piListener) -{ - size_t nCount(0); - size_t nTotal(0); - - nTotal = m_cRenderListeners.size(); - for (nCount = 0; nCount < nTotal; ++nCount) - { - if (m_cRenderListeners[nCount] == piListener) - { - m_cRenderListeners.erase(m_cRenderListeners.begin() + nCount); - return true; - } - } - return false; -} - -////////////////////////////////////////////////////////////////////////// -bool QtViewport::IsRenderListenerRegistered(IRenderListener* piListener) -{ - size_t nCount(0); - size_t nTotal(0); - - nTotal = m_cRenderListeners.size(); - for (nCount = 0; nCount < nTotal; ++nCount) - { - if (m_cRenderListeners[nCount] == piListener) - { - return true; - } - } - return false; -} - ////////////////////////////////////////////////////////////////////////// void QtViewport::AddPostRenderer(IPostRenderer* pPostRenderer) { @@ -1164,18 +1108,6 @@ bool QtViewport::GetAdvancedSelectModeFlag() return m_bAdvancedSelectMode; } -////////////////////////////////////////////////////////////////////////// -void QtViewport::ProcessRenderLisneters(DisplayContext& rstDisplayContext) -{ - size_t nCount(0); - size_t nTotal(0); - - nTotal = m_cRenderListeners.size(); - for (nCount = 0; nCount < nTotal; ++nCount) - { - m_cRenderListeners[nCount]->Render(rstDisplayContext); - } -} ////////////////////////////////////////////////////////////////////////// #if defined(AZ_PLATFORM_WINDOWS) // Note: Both CreateAnglesYPR and CreateOrientationYPR were copied verbatim from Cry_Camera.h which has been removed. diff --git a/Code/Editor/Viewport.h b/Code/Editor/Viewport.h index 1b0e5a4d93..d992eb4cb0 100644 --- a/Code/Editor/Viewport.h +++ b/Code/Editor/Viewport.h @@ -43,7 +43,6 @@ class CLayoutViewPane; class CViewManager; class CBaseObjectsCache; struct HitContext; -struct IRenderListener; class CImageEx; class QMenu; @@ -104,10 +103,6 @@ public: //! Access to view manager. CViewManager* GetViewManager() const { return m_viewManager; }; - virtual void RegisterRenderListener(IRenderListener* piListener) = 0; - virtual bool UnregisterRenderListener(IRenderListener* piListener) = 0; - virtual bool IsRenderListenerRegistered(IRenderListener* piListener) = 0; - virtual void AddPostRenderer(IPostRenderer* pPostRenderer) = 0; virtual bool RemovePostRenderer(IPostRenderer* pPostRenderer) = 0; @@ -477,10 +472,6 @@ public: void ResetCursor() override; void SetSupplementaryCursorStr(const QString& str) override; - void RegisterRenderListener(IRenderListener* piListener) override; - bool UnregisterRenderListener(IRenderListener* piListener) override; - bool IsRenderListenerRegistered(IRenderListener* piListener) override; - void AddPostRenderer(IPostRenderer* pPostRenderer) override; bool RemovePostRenderer(IPostRenderer* pPostRenderer) override; @@ -508,8 +499,6 @@ protected: void setRenderOverlayVisible(bool); bool isRenderOverlayVisible() const; - void ProcessRenderLisneters(DisplayContext& rstDisplayContext); - void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void mouseDoubleClickEvent(QMouseEvent* event) override; @@ -597,8 +586,6 @@ protected: // Same construction matrix is shared by all viewports. Matrix34 m_constructionMatrix[LAST_COORD_SYSTEM]; - std::vector m_cRenderListeners; - typedef std::vector<_smart_ptr > PostRenderers; PostRenderers m_postRenderers; AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING diff --git a/Code/Editor/editor_lib_files.cmake b/Code/Editor/editor_lib_files.cmake index 59a1a91647..345a8e15e1 100644 --- a/Code/Editor/editor_lib_files.cmake +++ b/Code/Editor/editor_lib_files.cmake @@ -270,7 +270,6 @@ set(FILES Include/Command.h Include/HitContext.h Include/ICommandManager.h - Include/IConsoleConnectivity.h Include/IDisplayViewport.h Include/IEditorClassFactory.h Include/IEventLoopHook.h @@ -282,9 +281,7 @@ set(FILES Include/IObjectManager.h Include/IPlugin.h Include/IPreferencesPage.h - Include/IRenderListener.h Include/ISourceControl.h - Include/ITextureDatabaseUpdater.h Include/ITransformManipulator.h Include/IViewPane.h Include/ObjectEvent.h diff --git a/Code/Editor/water.png b/Code/Editor/water.png deleted file mode 100644 index 342dee81e3..0000000000 --- a/Code/Editor/water.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4abde33fa9c29e927e403e275979536e7defbb6476eb375e85f847396645953f -size 41419 From b04cecc34dd9ace183fab95b97e393c3212de272 Mon Sep 17 00:00:00 2001 From: michabr <82236305+michabr@users.noreply.github.com> Date: Fri, 7 Jan 2022 14:28:38 -0800 Subject: [PATCH 11/26] Move Draw2d interface back to IDraw2d (#6730) * Move Draw2d interface back to IDraw2d Signed-off-by: abrmich * Fix compile error for gems using LyShine Signed-off-by: abrmich --- Gems/LyShine/Code/CMakeLists.txt | 3 + Gems/LyShine/Code/Include/LyShine/Draw2d.h | 363 +------------- Gems/LyShine/Code/Include/LyShine/IDraw2d.h | 457 ++++++++++++++++++ Gems/LyShine/Code/Source/LyShineDebug.cpp | 26 +- .../LyShine/Code/Source/UiCanvasComponent.cpp | 4 +- Gems/LyShine/Code/Source/UiCanvasComponent.h | 4 +- Gems/LyShine/Code/Source/UiCanvasManager.cpp | 6 +- Gems/LyShine/Code/Source/UiRenderer.cpp | 2 +- 8 files changed, 502 insertions(+), 363 deletions(-) diff --git a/Gems/LyShine/Code/CMakeLists.txt b/Gems/LyShine/Code/CMakeLists.txt index 77aaa681e0..4f2c4e90a7 100644 --- a/Gems/LyShine/Code/CMakeLists.txt +++ b/Gems/LyShine/Code/CMakeLists.txt @@ -47,6 +47,9 @@ ly_add_target( Gem::LyShine.Static Legacy::CryCommon Gem::LmbrCentral + PUBLIC + Gem::Atom_RHI.Reflect + Gem::Atom_RPI.Public RUNTIME_DEPENDENCIES Gem::LmbrCentral Gem::TextureAtlas diff --git a/Gems/LyShine/Code/Include/LyShine/Draw2d.h b/Gems/LyShine/Code/Include/LyShine/Draw2d.h index f92c6ce2e1..d138f41b03 100644 --- a/Gems/LyShine/Code/Include/LyShine/Draw2d.h +++ b/Gems/LyShine/Code/Include/LyShine/Draw2d.h @@ -8,13 +8,10 @@ #pragma once #include -#include -#include #include #include #include -#include #include //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -23,69 +20,11 @@ //! The CDraw2d class implements the IDraw2d interface for drawing 2D images, shapes and text. //! Positions and sizes are specified in pixels in the associated 2D viewport. class CDraw2d - : public IDraw2d // [LYSHINE_ATOM_TODO][GHI #3573] Make Draw2d work better as an API + : public IDraw2d , public AZ::Render::Bootstrap::NotificationBus::Handler { public: // types - struct RenderState - { - RenderState() - { - m_blendState.m_enable = true; - m_blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource; - m_blendState.m_blendDest = AZ::RHI::BlendFactor::AlphaSourceInverse; - - m_depthState.m_enable = false; - } - - AZ::RHI::TargetBlendState m_blendState; - AZ::RHI::DepthState m_depthState; - }; - - //! Struct used to pass additional image options. - // - //! If this is not passed then the defaults are used - struct ImageOptions - { - AZ::Vector3 color = AZ::Vector3(1.0f, 1.0f, 1.0f); - Rounding pixelRounding = Rounding::Nearest; - bool m_clamp = false; - RenderState m_renderState; - }; - - //! Struct used to pass additional text options - mostly ones that do not change from call to call. - // - //! If this is not passed then the defaults below are used - struct TextOptions - { - AZStd::string fontName; //!< default is "default" - unsigned int effectIndex; //!< default is 0 - AZ::Vector3 color; //!< default is (1,1,1) - HAlign horizontalAlignment; //!< default is HAlign::Left - VAlign verticalAlignment; //!< default is VAlign::Top - AZ::Vector2 dropShadowOffset; //!< default is (0,0), zero offset means no drop shadow is drawn - AZ::Color dropShadowColor; //!< default is (0,0,0,0), zero alpha means no drop shadow is drawn - float rotation; //!< default is 0 - bool depthTestEnabled; //!< default is false - }; - - //! Used to pass in arrays of vertices (e.g. to DrawQuad) - struct VertexPosColUV - { - VertexPosColUV() {} - VertexPosColUV(const AZ::Vector2& inPos, const AZ::Color& inColor, const AZ::Vector2& inUV) - { - position = inPos; - color = inColor; - uv = inUV; - } - - AZ::Vector2 position; //!< 2D position of vertex - AZ::Color color; //!< Float color - AZ::Vector2 uv; //!< Texture coordinate - }; - public: // member functions //! Constructor, constructed by the LyShine class @@ -116,7 +55,7 @@ public: // member functions //! \param imageOptions Optional struct specifying options that tend to be the same from call to call void DrawImage(AZ::Data::Instance image, AZ::Vector2 position, AZ::Vector2 size, float opacity = 1.0f, float rotation = 0.0f, const AZ::Vector2* pivotPoint = nullptr, const AZ::Vector2* minMaxTexCoords = nullptr, - ImageOptions* imageOptions = nullptr); + ImageOptions* imageOptions = nullptr) override; //! Draw a textured quad where the position specifies the point specified by the alignment. // @@ -135,7 +74,7 @@ public: // member functions void DrawImageAligned(AZ::Data::Instance image, AZ::Vector2 position, AZ::Vector2 size, HAlign horizontalAlignment, VAlign verticalAlignment, float opacity = 1.0f, float rotation = 0.0f, const AZ::Vector2* minMaxTexCoords = nullptr, - ImageOptions* imageOptions = nullptr); + ImageOptions* imageOptions = nullptr) override; //! Draw a textured quad where the position, color and uv of each point is specified explicitly // @@ -143,11 +82,11 @@ public: // member functions //! \param verts An array of 4 vertices, in clockwise order (e.g. top left, top right, bottom right, bottom left) //! \param pixelRounding Whether and how to round pixel coordinates //! \param renderState Blend mode and depth state - virtual void DrawQuad(AZ::Data::Instance image, + void DrawQuad(AZ::Data::Instance image, VertexPosColUV* verts, Rounding pixelRounding = Rounding::Nearest, bool clamp = false, - const RenderState& renderState = RenderState{}); + const RenderState& renderState = RenderState{}) override; //! Draw a line // @@ -156,9 +95,9 @@ public: // member functions //! \param color The color of the line //! \param pixelRounding Whether and how to round pixel coordinates //! \param renderState Blend mode and depth state - virtual void DrawLine(AZ::Vector2 start, AZ::Vector2 end, AZ::Color color, + void DrawLine(AZ::Vector2 start, AZ::Vector2 end, AZ::Color color, IDraw2d::Rounding pixelRounding = IDraw2d::Rounding::Nearest, - const RenderState& renderState = RenderState{}); + const RenderState& renderState = RenderState{}) override; //! Draw a line with a texture so it can be dotted or dashed // @@ -166,10 +105,10 @@ public: // member functions //! \param verts An array of 2 vertices for the start and end points of the line //! \param pixelRounding Whether and how to round pixel coordinates //! \param renderState Blend mode and depth state - virtual void DrawLineTextured(AZ::Data::Instance image, + void DrawLineTextured(AZ::Data::Instance image, VertexPosColUV* verts, IDraw2d::Rounding pixelRounding = IDraw2d::Rounding::Nearest, - const RenderState& renderState = RenderState{}); + const RenderState& renderState = RenderState{}) override; //! Draw a text string. Only supports ASCII text. // //! The font and effect used to render the text are specified in the textOptions structure @@ -179,7 +118,7 @@ public: // member functions //! \param opacity The opacity (alpha value) to use to draw the text //! \param textOptions Pointer to an options struct. If null the default options are used void DrawText(const char* textString, AZ::Vector2 position, float pointSize, - float opacity = 1.0f, TextOptions* textOptions = nullptr); + float opacity = 1.0f, TextOptions* textOptions = nullptr) override; //! Draw a rectangular outline with a texture // @@ -194,43 +133,43 @@ public: // member functions AZ::Vector2 rightVec, AZ::Vector2 downVec, AZ::Color color, - uint32_t lineThickness = 0); + uint32_t lineThickness = 0) override; //! Get the width and height (in pixels) that would be used to draw the given text string. // //! Pass the same parameter values that would be used to draw the string - AZ::Vector2 GetTextSize(const char* textString, float pointSize, TextOptions* textOptions = nullptr); + AZ::Vector2 GetTextSize(const char* textString, float pointSize, TextOptions* textOptions = nullptr) override; //! Get the width of the rendering viewport (in pixels). - float GetViewportWidth() const; + float GetViewportWidth() const override; //! Get the height of the rendering viewport (in pixels). - float GetViewportHeight() const; + float GetViewportHeight() const override; //! Get dpi scale factor - float GetViewportDpiScalingFactor() const; + float GetViewportDpiScalingFactor() const override; //! Get the default values that would be used if no image options were passed in // //! This is a convenient way to initialize the imageOptions struct - virtual const ImageOptions& GetDefaultImageOptions() const; + const ImageOptions& GetDefaultImageOptions() const override; //! Get the default values that would be used if no text options were passed in // //! This is a convenient way to initialize the textOptions struct - virtual const TextOptions& GetDefaultTextOptions() const; + const TextOptions& GetDefaultTextOptions() const override; //! Render the primitives that have been deferred - void RenderDeferredPrimitives(); + void RenderDeferredPrimitives() override; //! Specify whether to defer future primitives or render them right away - void SetDeferPrimitives(bool deferPrimitives); + void SetDeferPrimitives(bool deferPrimitives) override; //! Return whether future primitives will be deferred or rendered right away - bool GetDeferPrimitives(); + bool GetDeferPrimitives() override; //! Set sort key offset for following draws. - void SetSortKey(int64_t key); + void SetSortKey(int64_t key) override; private: @@ -378,263 +317,3 @@ protected: // attributes AZ::RHI::Ptr m_dynamicDraw; Draw2dShaderData m_shaderData; }; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Helper class for using the IDraw2d interface -//! -//! The Draw2dHelper class is an inline wrapper that provides the convenience feature of -//! automatically setting member options structures to their defaults and providing set functions. -class Draw2dHelper -{ -public: // member functions - - //! Start a section of 2D drawing function calls that will render to the default viewport - Draw2dHelper(bool deferCalls = false) - { - InitCommon(nullptr, deferCalls); - } - - //! Start a section of 2D drawing function calls that will render to the viewport - //! associated with the specified Draw2d object - Draw2dHelper(CDraw2d* draw2d, bool deferCalls = false) - { - InitCommon(draw2d, deferCalls); - } - - void InitCommon(CDraw2d* draw2d, bool deferCalls) - { - m_draw2d = draw2d; - - if (!m_draw2d) - { - // Set to default which is the game's draw 2d object - m_draw2d = GetDefaultDraw2d(); - } - - if (m_draw2d) - { - m_previousDeferCalls = m_draw2d->GetDeferPrimitives(); - m_draw2d->SetDeferPrimitives(deferCalls); - m_imageOptions = m_draw2d->GetDefaultImageOptions(); - m_textOptions = m_draw2d->GetDefaultTextOptions(); - } - } - - //! End a section of 2D drawing function calls. - ~Draw2dHelper() - { - if (m_draw2d) - { - m_draw2d->SetDeferPrimitives(m_previousDeferCalls); - } - } - - //! Draw a textured quad, optional rotation is counter-clockwise in degrees. - // - //! See IDraw2d:DrawImage for parameter descriptions - void DrawImage(AZ::Data::Instance image, AZ::Vector2 position, AZ::Vector2 size, float opacity = 1.0f, - float rotation = 0.0f, const AZ::Vector2* pivotPoint = nullptr, const AZ::Vector2* minMaxTexCoords = nullptr) - { - if (m_draw2d) - { - m_draw2d->DrawImage(image, position, size, opacity, rotation, pivotPoint, minMaxTexCoords, &m_imageOptions); - } - } - - //! Draw a textured quad where the position specifies the point specified by the alignment. - // - //! See IDraw2d:DrawImageAligned for parameter descriptions - void DrawImageAligned(AZ::Data::Instance image, AZ::Vector2 position, AZ::Vector2 size, - IDraw2d::HAlign horizontalAlignment, IDraw2d::VAlign verticalAlignment, - float opacity = 1.0f, float rotation = 0.0f, const AZ::Vector2* minMaxTexCoords = nullptr) - { - if (m_draw2d) - { - m_draw2d->DrawImageAligned(image, position, size, horizontalAlignment, verticalAlignment, - opacity, rotation, minMaxTexCoords, &m_imageOptions); - } - } - - //! Draw a textured quad where the position, color and uv of each point is specified explicitly - // - //! See IDraw2d:DrawQuad for parameter descriptions - void DrawQuad(AZ::Data::Instance image, CDraw2d::VertexPosColUV* verts, - IDraw2d::Rounding pixelRounding = IDraw2d::Rounding::Nearest, - bool clamp = false, - const CDraw2d::RenderState& renderState = CDraw2d::RenderState{}) - { - if (m_draw2d) - { - m_draw2d->DrawQuad(image, verts, pixelRounding, clamp, renderState); - } - } - - //! Draw a line - // - //! See IDraw2d:DrawLine for parameter descriptions - void DrawLine(AZ::Vector2 start, AZ::Vector2 end, AZ::Color color, - IDraw2d::Rounding pixelRounding = IDraw2d::Rounding::Nearest, - const CDraw2d::RenderState& renderState = CDraw2d::RenderState{}) - { - if (m_draw2d) - { - m_draw2d->DrawLine(start, end, color, pixelRounding, renderState); - } - } - - //! Draw a line with a texture so it can be dotted or dashed - // - //! See IDraw2d:DrawLineTextured for parameter descriptions - void DrawLineTextured(AZ::Data::Instance image, CDraw2d::VertexPosColUV* verts, - IDraw2d::Rounding pixelRounding = IDraw2d::Rounding::Nearest, - const CDraw2d::RenderState& renderState = CDraw2d::RenderState{}) - { - if (m_draw2d) - { - m_draw2d->DrawLineTextured(image, verts, pixelRounding, renderState); - } - } - - //! Draw a rect outline with a texture - // - //! See IDraw2d:DrawRectOutlineTextured for parameter descriptions - void DrawRectOutlineTextured(AZ::Data::Instance image, - UiTransformInterface::RectPoints points, - AZ::Vector2 rightVec, - AZ::Vector2 downVec, - AZ::Color color, - uint32_t lineThickness = 0) - { - if (m_draw2d) - { - m_draw2d->DrawRectOutlineTextured(image, points, rightVec, downVec, color, lineThickness); - } - } - - //! Draw a text string. Only supports ASCII text. - // - //! See IDraw2d:DrawText for parameter descriptions - void DrawText(const char* textString, AZ::Vector2 position, float pointSize, float opacity = 1.0f) - { - if (m_draw2d) - { - m_draw2d->DrawText(textString, position, pointSize, opacity, &m_textOptions); - } - } - - //! Get the width and height (in pixels) that would be used to draw the given text string. - // - //! See IDraw2d:GetTextSize for parameter descriptions - AZ::Vector2 GetTextSize(const char* textString, float pointSize) - { - if (m_draw2d) - { - return m_draw2d->GetTextSize(textString, pointSize, &m_textOptions); - } - else - { - return AZ::Vector2(0, 0); - } - } - - // State management - - //! Set the blend mode used for images, default is GS_BLSRC_SRCALPHA|GS_BLDST_ONEMINUSSRCALPHA. - void SetImageBlendMode(const AZ::RHI::TargetBlendState& blendState) { m_imageOptions.m_renderState.m_blendState = blendState; } - - //! Set the color used for DrawImage and other image drawing. - void SetImageColor(AZ::Vector3 color) { m_imageOptions.color = color; } - - //! Set whether images are rounded to have the points on exact pixel boundaries. - void SetImagePixelRounding(IDraw2d::Rounding round) { m_imageOptions.pixelRounding = round; } - - //! Set the base state (that blend mode etc is combined with) used for images, default is GS_NODEPTHTEST. - void SetImageDepthState(const AZ::RHI::DepthState& depthState) { m_imageOptions.m_renderState.m_depthState = depthState; } - - //! Set image clamp mode - void SetImageClamp(bool clamp) { m_imageOptions.m_clamp = clamp; } - - //! Set the text font. - void SetTextFont(AZStd::string_view fontName) { m_textOptions.fontName = fontName; } - - //! Set the text font effect index. - void SetTextEffectIndex(unsigned int effectIndex) { m_textOptions.effectIndex = effectIndex; } - - //! Set the text color. - void SetTextColor(AZ::Vector3 color) { m_textOptions.color = color; } - - //! Set the text alignment. - void SetTextAlignment(IDraw2d::HAlign horizontalAlignment, IDraw2d::VAlign verticalAlignment) - { - m_textOptions.horizontalAlignment = horizontalAlignment; - m_textOptions.verticalAlignment = verticalAlignment; - } - - //! Set a drop shadow for text drawing. An alpha of zero disables drop shadow. - void SetTextDropShadow(AZ::Vector2 offset, AZ::Color color) - { - m_textOptions.dropShadowOffset = offset; - m_textOptions.dropShadowColor = color; - } - - //! Set a rotation for the text. The text rotates around its position (taking into account alignment). - void SetTextRotation(float rotation) - { - m_textOptions.rotation = rotation; - } - - //! Set wheter to enable depth test for the text - void SetTextDepthTestEnabled(bool enabled) - { - m_textOptions.depthTestEnabled = enabled; - } - -public: // static member functions - - //! Helper to get the default IDraw2d interface - static CDraw2d* GetDefaultDraw2d() - { - if (gEnv && gEnv->pLyShine) // [LYSHINE_ATOM_TODO][GHI #3569] Remove LyShine global interface pointer from legacy global environment - { - IDraw2d* draw2d = gEnv->pLyShine->GetDraw2d(); - return reinterpret_cast(draw2d); - } - - return nullptr; - } - - //! Round the X and Y coordinates of a point using the given rounding policy - template - static T RoundXY(T value, IDraw2d::Rounding roundingType) - { - T result = value; - - switch (roundingType) - { - case IDraw2d::Rounding::None: - // nothing to do - break; - case IDraw2d::Rounding::Nearest: - result.SetX(floor(value.GetX() + 0.5f)); - result.SetY(floor(value.GetY() + 0.5f)); - break; - case IDraw2d::Rounding::Down: - result.SetX(floor(value.GetX())); - result.SetY(floor(value.GetY())); - break; - case IDraw2d::Rounding::Up: - result.SetX(ceil(value.GetX())); - result.SetY(ceil(value.GetY())); - break; - } - - return result; - } - -protected: // attributes - - CDraw2d::ImageOptions m_imageOptions; //!< image options are stored locally and updated by member functions - CDraw2d::TextOptions m_textOptions; //!< text options are stored locally and updated by member functions - CDraw2d* m_draw2d; - bool m_previousDeferCalls; -}; diff --git a/Gems/LyShine/Code/Include/LyShine/IDraw2d.h b/Gems/LyShine/Code/Include/LyShine/IDraw2d.h index 3bfa3a1c14..620b8c23c9 100644 --- a/Gems/LyShine/Code/Include/LyShine/IDraw2d.h +++ b/Gems/LyShine/Code/Include/LyShine/IDraw2d.h @@ -11,6 +11,10 @@ #include #include #include +#include +#include +#include +#include //////////////////////////////////////////////////////////////////////////////////////////////////// //! Class for 2D drawing in screen space @@ -55,8 +59,461 @@ public: // types MAX_TEXT_STRING_LENGTH = 1024, }; + struct RenderState + { + RenderState() + { + m_blendState.m_enable = true; + m_blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource; + m_blendState.m_blendDest = AZ::RHI::BlendFactor::AlphaSourceInverse; + + m_depthState.m_enable = false; + } + + AZ::RHI::TargetBlendState m_blendState; + AZ::RHI::DepthState m_depthState; + }; + + //! Struct used to pass additional image options. + // + //! If this is not passed then the defaults are used + struct ImageOptions + { + AZ::Vector3 color = AZ::Vector3(1.0f, 1.0f, 1.0f); + Rounding pixelRounding = Rounding::Nearest; + bool m_clamp = false; + RenderState m_renderState; + }; + + //! Struct used to pass additional text options - mostly ones that do not change from call to call. + // + //! If this is not passed then the defaults below are used + struct TextOptions + { + AZStd::string fontName; //!< default is "default" + unsigned int effectIndex; //!< default is 0 + AZ::Vector3 color; //!< default is (1,1,1) + HAlign horizontalAlignment; //!< default is HAlign::Left + VAlign verticalAlignment; //!< default is VAlign::Top + AZ::Vector2 dropShadowOffset; //!< default is (0,0), zero offset means no drop shadow is drawn + AZ::Color dropShadowColor; //!< default is (0,0,0,0), zero alpha means no drop shadow is drawn + float rotation; //!< default is 0 + bool depthTestEnabled; //!< default is false + }; + + //! Used to pass in arrays of vertices (e.g. to DrawQuad) + struct VertexPosColUV + { + VertexPosColUV() {} + VertexPosColUV(const AZ::Vector2& inPos, const AZ::Color& inColor, const AZ::Vector2& inUV) + { + position = inPos; + color = inColor; + uv = inUV; + } + + AZ::Vector2 position; //!< 2D position of vertex + AZ::Color color; //!< Float color + AZ::Vector2 uv; //!< Texture coordinate + }; + public: // member functions //! Implement virtual destructor just for safety. virtual ~IDraw2d() {} + + //! Draw a textured quad with the top left corner at the given position. + // + //! The image is drawn with the color specified by SetShapeColor and the opacity + //! passed as an argument. + //! If rotation is non-zero then the quad is rotated. If the pivot point is + //! provided then the points of the quad are rotated about that point, otherwise + //! they are rotated about the top left corner of the quad. + //! \param texId The texture ID returned by ITexture::GetTextureID() + //! \param position Position of the top left corner of the quad (before rotation) in pixels + //! \param size The width and height of the quad. Use texture width and height to avoid minification, + //! magnification or stretching (assuming the minMaxTexCoords are left to the default) + //! \param opacity The alpha value used when blending + //! \param rotation Angle of rotation in degrees counter-clockwise + //! \param pivotPoint The point about which the quad is rotated + //! \param minMaxTexCoords An optional two component array. The first component is the UV coord for the top left + //! point of the quad and the second is the UV coord of the bottom right point of the quad + //! \param imageOptions Optional struct specifying options that tend to be the same from call to call + virtual void DrawImage(AZ::Data::Instance image, AZ::Vector2 position, AZ::Vector2 size, float opacity = 1.0f, + float rotation = 0.0f, const AZ::Vector2* pivotPoint = nullptr, const AZ::Vector2* minMaxTexCoords = nullptr, + ImageOptions* imageOptions = nullptr) = 0; + + //! Draw a textured quad where the position specifies the point specified by the alignment. + // + //! Rotation is always around the position. + //! \param texId The texture ID returned by ITexture::GetTextureID() + //! \param position Position align point of the quad (before rotation) in pixels + //! \param size The width and height of the quad. Use texture width and height to avoid minification, + //! magnification or stretching (assuming the minMaxTexCoords are left to the default) + //! \param horizontalAlignment Specifies how the quad is horizontally aligned to the given position + //! \param verticalAlignment Specifies how the quad is vertically aligned to the given position + //! \param opacity The alpha value used when blending + //! \param rotation Angle of rotation in degrees counter-clockwise + //! \param minMaxTexCoords An optional two component array. The first component is the UV coord for the top left + //! point of the quad and the second is the UV coord of the bottom right point of the quad + //! \param imageOptions Optional struct specifying options that tend to be the same from call to call + virtual void DrawImageAligned(AZ::Data::Instance image, AZ::Vector2 position, AZ::Vector2 size, + HAlign horizontalAlignment, VAlign verticalAlignment, + float opacity = 1.0f, float rotation = 0.0f, const AZ::Vector2* minMaxTexCoords = nullptr, + ImageOptions* imageOptions = nullptr) = 0; + + //! Draw a textured quad where the position, color and uv of each point is specified explicitly + // + //! \param texId The texture ID returned by ITexture::GetTextureID() + //! \param verts An array of 4 vertices, in clockwise order (e.g. top left, top right, bottom right, bottom left) + //! \param pixelRounding Whether and how to round pixel coordinates + //! \param renderState Blend mode and depth state + virtual void DrawQuad(AZ::Data::Instance image, + VertexPosColUV* verts, + Rounding pixelRounding = Rounding::Nearest, + bool clamp = false, + const RenderState& renderState = RenderState{}) = 0; + + //! Draw a line + // + //! \param start The start position + //! \param end The end position + //! \param color The color of the line + //! \param pixelRounding Whether and how to round pixel coordinates + //! \param renderState Blend mode and depth state + virtual void DrawLine(AZ::Vector2 start, AZ::Vector2 end, AZ::Color color, + IDraw2d::Rounding pixelRounding = IDraw2d::Rounding::Nearest, + const RenderState& renderState = RenderState{}) = 0; + + //! Draw a line with a texture so it can be dotted or dashed + // + //! \param texId The texture ID returned by ITexture::GetTextureID() + //! \param verts An array of 2 vertices for the start and end points of the line + //! \param pixelRounding Whether and how to round pixel coordinates + //! \param renderState Blend mode and depth state + virtual void DrawLineTextured(AZ::Data::Instance image, + VertexPosColUV* verts, + IDraw2d::Rounding pixelRounding = IDraw2d::Rounding::Nearest, + const RenderState& renderState = RenderState{}) = 0; + //! Draw a text string. Only supports ASCII text. + // + //! The font and effect used to render the text are specified in the textOptions structure + //! \param textString A null terminated ASCII text string. May contain \n characters + //! \param position Position of the text in pixels. Alignment values in textOptions affect actual position + //! \param pointSize The size of the font to use + //! \param opacity The opacity (alpha value) to use to draw the text + //! \param textOptions Pointer to an options struct. If null the default options are used + virtual void DrawText(const char* textString, AZ::Vector2 position, float pointSize, + float opacity = 1.0f, TextOptions* textOptions = nullptr) = 0; + + //! Draw a rectangular outline with a texture + // + //! \param image The texture to be used for drawing the outline + //! \param points The rect's vertices (top left, top right, bottom right, bottom left) + //! \param rightVec Right vector. Specified because the rect's width/height could be 0 + //! \param downVec Down vector. Specified because the rect's width/height could be 0 + //! \param color The color of the outline + //! \param lineThickness The thickness in pixels of the outline. If 0, it will be based on image height + virtual void DrawRectOutlineTextured(AZ::Data::Instance image, + UiTransformInterface::RectPoints points, + AZ::Vector2 rightVec, + AZ::Vector2 downVec, + AZ::Color color, + uint32_t lineThickness = 0) = 0; + + //! Get the width and height (in pixels) that would be used to draw the given text string. + // + //! Pass the same parameter values that would be used to draw the string + virtual AZ::Vector2 GetTextSize(const char* textString, float pointSize, TextOptions* textOptions = nullptr) = 0; + + //! Get the width of the rendering viewport (in pixels). + virtual float GetViewportWidth() const = 0; + + //! Get the height of the rendering viewport (in pixels). + virtual float GetViewportHeight() const = 0; + + //! Get dpi scale factor + virtual float GetViewportDpiScalingFactor() const = 0; + + //! Get the default values that would be used if no image options were passed in + // + //! This is a convenient way to initialize the imageOptions struct + virtual const ImageOptions& GetDefaultImageOptions() const = 0; + + //! Get the default values that would be used if no text options were passed in + // + //! This is a convenient way to initialize the textOptions struct + virtual const TextOptions& GetDefaultTextOptions() const = 0; + + //! Render the primitives that have been deferred + virtual void RenderDeferredPrimitives() = 0; + + //! Specify whether to defer future primitives or render them right away + virtual void SetDeferPrimitives(bool deferPrimitives) = 0; + + //! Return whether future primitives will be deferred or rendered right away + virtual bool GetDeferPrimitives() = 0; + + //! Set sort key offset for following draws. + virtual void SetSortKey(int64_t key) = 0; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Helper class for using the IDraw2d interface +//! +//! The Draw2dHelper class is an inline wrapper that provides the convenience feature of +//! automatically setting member options structures to their defaults and providing set functions. +class Draw2dHelper +{ +public: // member functions + + //! Start a section of 2D drawing function calls that will render to the default viewport + Draw2dHelper(bool deferCalls = false) + { + InitCommon(nullptr, deferCalls); + } + + //! Start a section of 2D drawing function calls that will render to the viewport + //! associated with the specified Draw2d object + Draw2dHelper(IDraw2d* draw2d, bool deferCalls = false) + { + InitCommon(draw2d, deferCalls); + } + + void InitCommon(IDraw2d* draw2d, bool deferCalls) + { + m_draw2d = draw2d; + + if (!m_draw2d) + { + // Set to default which is the game's draw 2d object + m_draw2d = GetDefaultDraw2d(); + } + + if (m_draw2d) + { + m_previousDeferCalls = m_draw2d->GetDeferPrimitives(); + m_draw2d->SetDeferPrimitives(deferCalls); + m_imageOptions = m_draw2d->GetDefaultImageOptions(); + m_textOptions = m_draw2d->GetDefaultTextOptions(); + } + } + + //! End a section of 2D drawing function calls. + ~Draw2dHelper() + { + if (m_draw2d) + { + m_draw2d->SetDeferPrimitives(m_previousDeferCalls); + } + } + + //! Draw a textured quad, optional rotation is counter-clockwise in degrees. + // + //! See IDraw2d:DrawImage for parameter descriptions + void DrawImage(AZ::Data::Instance image, AZ::Vector2 position, AZ::Vector2 size, float opacity = 1.0f, + float rotation = 0.0f, const AZ::Vector2* pivotPoint = nullptr, const AZ::Vector2* minMaxTexCoords = nullptr) + { + if (m_draw2d) + { + m_draw2d->DrawImage(image, position, size, opacity, rotation, pivotPoint, minMaxTexCoords, &m_imageOptions); + } + } + + //! Draw a textured quad where the position specifies the point specified by the alignment. + // + //! See IDraw2d:DrawImageAligned for parameter descriptions + void DrawImageAligned(AZ::Data::Instance image, AZ::Vector2 position, AZ::Vector2 size, + IDraw2d::HAlign horizontalAlignment, IDraw2d::VAlign verticalAlignment, + float opacity = 1.0f, float rotation = 0.0f, const AZ::Vector2* minMaxTexCoords = nullptr) + { + if (m_draw2d) + { + m_draw2d->DrawImageAligned(image, position, size, horizontalAlignment, verticalAlignment, + opacity, rotation, minMaxTexCoords, &m_imageOptions); + } + } + + //! Draw a textured quad where the position, color and uv of each point is specified explicitly + // + //! See IDraw2d:DrawQuad for parameter descriptions + void DrawQuad(AZ::Data::Instance image, IDraw2d::VertexPosColUV* verts, + IDraw2d::Rounding pixelRounding = IDraw2d::Rounding::Nearest, + bool clamp = false, + const IDraw2d::RenderState& renderState = IDraw2d::RenderState{}) + { + if (m_draw2d) + { + m_draw2d->DrawQuad(image, verts, pixelRounding, clamp, renderState); + } + } + + //! Draw a line + // + //! See IDraw2d:DrawLine for parameter descriptions + void DrawLine(AZ::Vector2 start, AZ::Vector2 end, AZ::Color color, + IDraw2d::Rounding pixelRounding = IDraw2d::Rounding::Nearest, + const IDraw2d::RenderState& renderState = IDraw2d::RenderState{}) + { + if (m_draw2d) + { + m_draw2d->DrawLine(start, end, color, pixelRounding, renderState); + } + } + + //! Draw a line with a texture so it can be dotted or dashed + // + //! See IDraw2d:DrawLineTextured for parameter descriptions + void DrawLineTextured(AZ::Data::Instance image, IDraw2d::VertexPosColUV* verts, + IDraw2d::Rounding pixelRounding = IDraw2d::Rounding::Nearest, + const IDraw2d::RenderState& renderState = IDraw2d::RenderState{}) + { + if (m_draw2d) + { + m_draw2d->DrawLineTextured(image, verts, pixelRounding, renderState); + } + } + + //! Draw a rect outline with a texture + // + //! See IDraw2d:DrawRectOutlineTextured for parameter descriptions + void DrawRectOutlineTextured(AZ::Data::Instance image, + UiTransformInterface::RectPoints points, + AZ::Vector2 rightVec, + AZ::Vector2 downVec, + AZ::Color color, + uint32_t lineThickness = 0) + { + if (m_draw2d) + { + m_draw2d->DrawRectOutlineTextured(image, points, rightVec, downVec, color, lineThickness); + } + } + + //! Draw a text string. Only supports ASCII text. + // + //! See IDraw2d:DrawText for parameter descriptions + void DrawText(const char* textString, AZ::Vector2 position, float pointSize, float opacity = 1.0f) + { + if (m_draw2d) + { + m_draw2d->DrawText(textString, position, pointSize, opacity, &m_textOptions); + } + } + + //! Get the width and height (in pixels) that would be used to draw the given text string. + // + //! See IDraw2d:GetTextSize for parameter descriptions + AZ::Vector2 GetTextSize(const char* textString, float pointSize) + { + if (m_draw2d) + { + return m_draw2d->GetTextSize(textString, pointSize, &m_textOptions); + } + else + { + return AZ::Vector2(0, 0); + } + } + + // State management + + //! Set the blend mode used for images, default is GS_BLSRC_SRCALPHA|GS_BLDST_ONEMINUSSRCALPHA. + void SetImageBlendMode(const AZ::RHI::TargetBlendState& blendState) { m_imageOptions.m_renderState.m_blendState = blendState; } + + //! Set the color used for DrawImage and other image drawing. + void SetImageColor(AZ::Vector3 color) { m_imageOptions.color = color; } + + //! Set whether images are rounded to have the points on exact pixel boundaries. + void SetImagePixelRounding(IDraw2d::Rounding round) { m_imageOptions.pixelRounding = round; } + + //! Set the base state (that blend mode etc is combined with) used for images, default is GS_NODEPTHTEST. + void SetImageDepthState(const AZ::RHI::DepthState& depthState) { m_imageOptions.m_renderState.m_depthState = depthState; } + + //! Set image clamp mode + void SetImageClamp(bool clamp) { m_imageOptions.m_clamp = clamp; } + + //! Set the text font. + void SetTextFont(AZStd::string_view fontName) { m_textOptions.fontName = fontName; } + + //! Set the text font effect index. + void SetTextEffectIndex(unsigned int effectIndex) { m_textOptions.effectIndex = effectIndex; } + + //! Set the text color. + void SetTextColor(AZ::Vector3 color) { m_textOptions.color = color; } + + //! Set the text alignment. + void SetTextAlignment(IDraw2d::HAlign horizontalAlignment, IDraw2d::VAlign verticalAlignment) + { + m_textOptions.horizontalAlignment = horizontalAlignment; + m_textOptions.verticalAlignment = verticalAlignment; + } + + //! Set a drop shadow for text drawing. An alpha of zero disables drop shadow. + void SetTextDropShadow(AZ::Vector2 offset, AZ::Color color) + { + m_textOptions.dropShadowOffset = offset; + m_textOptions.dropShadowColor = color; + } + + //! Set a rotation for the text. The text rotates around its position (taking into account alignment). + void SetTextRotation(float rotation) + { + m_textOptions.rotation = rotation; + } + + //! Set wheter to enable depth test for the text + void SetTextDepthTestEnabled(bool enabled) + { + m_textOptions.depthTestEnabled = enabled; + } + +public: // static member functions + + //! Helper to get the default IDraw2d interface + static IDraw2d* GetDefaultDraw2d() + { + if (gEnv && gEnv->pLyShine) // [LYSHINE_ATOM_TODO][GHI #3569] Remove LyShine global interface pointer from legacy global environment + { + IDraw2d* draw2d = gEnv->pLyShine->GetDraw2d(); + return reinterpret_cast(draw2d); + } + + return nullptr; + } + + //! Round the X and Y coordinates of a point using the given rounding policy + template + static T RoundXY(T value, IDraw2d::Rounding roundingType) + { + T result = value; + + switch (roundingType) + { + case IDraw2d::Rounding::None: + // nothing to do + break; + case IDraw2d::Rounding::Nearest: + result.SetX(floor(value.GetX() + 0.5f)); + result.SetY(floor(value.GetY() + 0.5f)); + break; + case IDraw2d::Rounding::Down: + result.SetX(floor(value.GetX())); + result.SetY(floor(value.GetY())); + break; + case IDraw2d::Rounding::Up: + result.SetX(ceil(value.GetX())); + result.SetY(ceil(value.GetY())); + break; + } + + return result; + } + +protected: // attributes + + IDraw2d::ImageOptions m_imageOptions; //!< image options are stored locally and updated by member functions + IDraw2d::TextOptions m_textOptions; //!< text options are stored locally and updated by member functions + IDraw2d* m_draw2d; + bool m_previousDeferCalls; }; diff --git a/Gems/LyShine/Code/Source/LyShineDebug.cpp b/Gems/LyShine/Code/Source/LyShineDebug.cpp index 7762b1403b..bb57e70857 100644 --- a/Gems/LyShine/Code/Source/LyShineDebug.cpp +++ b/Gems/LyShine/Code/Source/LyShineDebug.cpp @@ -375,7 +375,7 @@ static void DebugDrawColoredBox(AZ::Vector2 pos, AZ::Vector2 size, AZ::Color col IDraw2d::HAlign horizontalAlignment = IDraw2d::HAlign::Left, IDraw2d::VAlign verticalAlignment = IDraw2d::VAlign::Top) { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); CDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); imageOptions.color = color.GetAsVector3(); @@ -390,7 +390,7 @@ static void DebugDrawColoredBox(AZ::Vector2 pos, AZ::Vector2 size, AZ::Color col static void DebugDrawStringWithSizeBox(AZStd::string_view font, unsigned int effectIndex, const char* sizeString, const char* testString, AZ::Vector2 pos, float spacing, float size) { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); CDraw2d::TextOptions textOptions = draw2d->GetDefaultTextOptions(); if (!font.empty()) @@ -424,7 +424,7 @@ static void DebugDrawStringWithSizeBox(AZStd::string_view font, unsigned int eff #if !defined(_RELEASE) static void DebugDraw2dFontSizes(AZStd::string_view font, unsigned int effectIndex) { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); float xOffset = 20.0f; float yOffset = 20.0f; @@ -546,7 +546,7 @@ static void DebugDrawAlignedTextWithOriginBox(AZ::Vector2 pos, #if !defined(_RELEASE) static void DebugDraw2dFontAlignment() { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); float w = draw2d->GetViewportWidth(); float yPos = 20; @@ -613,7 +613,7 @@ static void DebugDraw2dFontAlignment() #if !defined(_RELEASE) static AZ::Vector2 DebugDrawFontColorTestBox(AZ::Vector2 pos, const char* string, AZ::Vector3 color, float opacity) { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); float pointSize = 32.0f; const float spacing = 6.0f; @@ -648,7 +648,7 @@ static AZ::Vector2 DebugDrawFontColorTestBox(AZ::Vector2 pos, const char* string #if !defined(_RELEASE) static void DebugDraw2dFontColorAndOpacity() { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); AZ::Vector2 size; AZ::Vector2 pos(20.0f, 20.0f); @@ -686,7 +686,7 @@ static void DebugDraw2dFontColorAndOpacity() #if !defined(_RELEASE) static void DebugDraw2dImageRotations() { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); AZ::Data::Instance texture = GetMonoTestTexture(); @@ -738,7 +738,7 @@ static void DebugDraw2dImageRotations() #if !defined(_RELEASE) static void DebugDraw2dImageColor() { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); AZ::Data::Instance texture = GetMonoAlphaTestTexture(); @@ -774,7 +774,7 @@ static void DebugDraw2dImageColor() #if !defined(_RELEASE) static void DebugDraw2dImageBlendMode() { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); auto whiteTexture = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::White); @@ -841,7 +841,7 @@ static void DebugDraw2dImageBlendMode() #if !defined(_RELEASE) static void DebugDraw2dImageUVs() { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); AZ::Data::Instance texture = GetColorTestTexture(); @@ -890,7 +890,7 @@ static void DebugDraw2dImageUVs() #if !defined(_RELEASE) static void DebugDraw2dImagePixelRounding() { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); AZ::Data::Instance texture = GetColorTestTexture(); @@ -931,7 +931,7 @@ static void DebugDraw2dImagePixelRounding() #if !defined(_RELEASE) static void DebugDraw2dLineBasic() { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); CDraw2d::ImageOptions imageOptions = draw2d->GetDefaultImageOptions(); @@ -1422,7 +1422,7 @@ void LyShineDebug::RenderDebug() #if !defined(_RELEASE) #ifndef EXCLUDE_DOCUMENTATION_PURPOSE - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); if (!draw2d) { return; diff --git a/Gems/LyShine/Code/Source/UiCanvasComponent.cpp b/Gems/LyShine/Code/Source/UiCanvasComponent.cpp index 78b9c71f8e..0a21f92119 100644 --- a/Gems/LyShine/Code/Source/UiCanvasComponent.cpp +++ b/Gems/LyShine/Code/Source/UiCanvasComponent.cpp @@ -2122,13 +2122,13 @@ void UiCanvasComponent::DebugReportDrawCalls(AZ::IO::HandleType fileHandle, LySh } //////////////////////////////////////////////////////////////////////////////////////////////////// -void UiCanvasComponent::DebugDisplayElemBounds(CDraw2d* draw2d) const +void UiCanvasComponent::DebugDisplayElemBounds(IDraw2d* draw2d) const { DebugDisplayChildElemBounds(draw2d, m_rootElement); } //////////////////////////////////////////////////////////////////////////////////////////////////// -void UiCanvasComponent::DebugDisplayChildElemBounds(CDraw2d* draw2d, const AZ::EntityId entity) const +void UiCanvasComponent::DebugDisplayChildElemBounds(IDraw2d* draw2d, const AZ::EntityId entity) const { AZ::u64 time = AZStd::GetTimeUTCMilliSecond(); uint32 fractionsOfOneSecond = time % 1000; diff --git a/Gems/LyShine/Code/Source/UiCanvasComponent.h b/Gems/LyShine/Code/Source/UiCanvasComponent.h index 79cb044af0..050773131f 100644 --- a/Gems/LyShine/Code/Source/UiCanvasComponent.h +++ b/Gems/LyShine/Code/Source/UiCanvasComponent.h @@ -292,8 +292,8 @@ public: // member functions void DebugReportDrawCalls(AZ::IO::HandleType fileHandle, LyShineDebug::DebugInfoDrawCallReport& reportInfo, void* context) const; - void DebugDisplayElemBounds(CDraw2d* draw2d) const; - void DebugDisplayChildElemBounds(CDraw2d* draw2d, const AZ::EntityId entity) const; + void DebugDisplayElemBounds(IDraw2d* draw2d) const; + void DebugDisplayChildElemBounds(IDraw2d* draw2d, const AZ::EntityId entity) const; #endif public: // static member functions diff --git a/Gems/LyShine/Code/Source/UiCanvasManager.cpp b/Gems/LyShine/Code/Source/UiCanvasManager.cpp index c6564f9b3a..70646246f0 100644 --- a/Gems/LyShine/Code/Source/UiCanvasManager.cpp +++ b/Gems/LyShine/Code/Source/UiCanvasManager.cpp @@ -997,7 +997,7 @@ void UiCanvasManager::DebugDisplayCanvasData(int setting) const { bool onlyShowEnabledCanvases = (setting == 2) ? true : false; - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); float dpiScale = draw2d->GetViewportDpiScalingFactor(); float xOffset = 20.0f * dpiScale; @@ -1152,7 +1152,7 @@ void UiCanvasManager::DebugDisplayCanvasData(int setting) const //////////////////////////////////////////////////////////////////////////////////////////////////// void UiCanvasManager::DebugDisplayDrawCallData() const { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); float dpiScale = draw2d->GetViewportDpiScalingFactor(); float xOffset = 20.0f * dpiScale; @@ -1486,7 +1486,7 @@ void UiCanvasManager::DebugReportDrawCalls(const AZStd::string& name) const //////////////////////////////////////////////////////////////////////////////////////////////////// void UiCanvasManager::DebugDisplayElemBounds(int canvasIndexFilter) const { - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); int canvasIndex = 0; for (auto canvas : m_loadedCanvases) diff --git a/Gems/LyShine/Code/Source/UiRenderer.cpp b/Gems/LyShine/Code/Source/UiRenderer.cpp index 64ec1bb485..acf17049ad 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.cpp +++ b/Gems/LyShine/Code/Source/UiRenderer.cpp @@ -414,7 +414,7 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) return lhs.second > rhs.second; }); - CDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); + IDraw2d* draw2d = Draw2dHelper::GetDefaultDraw2d(); // setup to render lines of text for the debug display From 57e4fb9b393a9f37a6c31e4d65a87cff49de2aa7 Mon Sep 17 00:00:00 2001 From: moraaar Date: Mon, 10 Jan 2022 09:05:35 +0000 Subject: [PATCH 12/26] Fixed script canvas component asset not being found (#6727) Script canvas component has the member m_sourceData (that points to the script canvas asset) that internally has data, id and path. Path is serialized as the absolute path of the pc that is saving the level. So when another pc loads the same level it cannot find the script canvas asset. The function CompleteDescription takes a look at the id and takes the path from the asset catalog, but at the moment it's doing an early return because id and path are not empty (but path is the value serialized from other user saving the level). By removing the early return condition then it it will resolve by using id, looking into the catalog and getting the real path. This fix makes several physics automated tests that relied on script canvas to work. Signed-off-by: moraaar moraaar@amazon.com --- Gems/ScriptCanvas/Code/Editor/Components/EditorUtils.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Gems/ScriptCanvas/Code/Editor/Components/EditorUtils.cpp b/Gems/ScriptCanvas/Code/Editor/Components/EditorUtils.cpp index 2cd2dda89e..363f477fbf 100644 --- a/Gems/ScriptCanvas/Code/Editor/Components/EditorUtils.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Components/EditorUtils.cpp @@ -33,11 +33,6 @@ namespace ScriptCanvasEditor { AZStd::optional CompleteDescription(const SourceHandle& source) { - if (source.IsDescriptionValid()) - { - return source; - } - AzToolsFramework::AssetSystemRequestBus::Events* assetSystem = AzToolsFramework::AssetSystemRequestBus::FindFirstHandler(); if (assetSystem) { From afc531d4c3ad1e7a413df68a7a22d7717a7b6a2a Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Mon, 10 Jan 2022 10:22:02 +0100 Subject: [PATCH 13/26] Fixes VS2022 error C5233: explicit lambda capture 'isSlash' is not used (#6745) Signed-off-by: Benjamin Jillich --- Code/Tools/AssetProcessor/native/FileWatcher/FileWatcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Tools/AssetProcessor/native/FileWatcher/FileWatcher.cpp b/Code/Tools/AssetProcessor/native/FileWatcher/FileWatcher.cpp index f802bdbada..993e7f2760 100644 --- a/Code/Tools/AssetProcessor/native/FileWatcher/FileWatcher.cpp +++ b/Code/Tools/AssetProcessor/native/FileWatcher/FileWatcher.cpp @@ -25,7 +25,7 @@ static bool IsSubfolder(const QString& folderA, const QString& folderB) using AZStd::begin; using AZStd::end; - constexpr auto isSlash = [](const QChar c) constexpr + auto isSlash = [](const QChar c) constexpr { return c == AZ::IO::WindowsPathSeparator || c == AZ::IO::PosixPathSeparator; }; From 7c88f20e1e6bf4b24ea6c0631cccc20867c024bb Mon Sep 17 00:00:00 2001 From: John Jones-Steele <82226755+jjjoness@users.noreply.github.com> Date: Mon, 10 Jan 2022 11:49:22 +0000 Subject: [PATCH 14/26] Spinboxes now correct when rounding. (#6748) * Spinboxes now correct when rounding. Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com> * Changes from PR Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com> * Fixed tests after change to rounding in spinbox Signed-off-by: John Jones-Steele <82226755+jjjoness@users.noreply.github.com> --- .../Components/Widgets/SpinBox.cpp | 2 +- .../AzQtComponents/Tests/AzQtComponentTests.cpp | 16 ++++++++++++++++ .../AzQtComponents/Utilities/Conversions.cpp | 16 ++++++++++++---- .../AzQtComponents/Utilities/Conversions.h | 2 +- .../AzToolsFramework/Tests/SpinBoxTests.cpp | 8 ++++---- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/SpinBox.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/SpinBox.cpp index 8ace8d0f40..e9f484ee11 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/SpinBox.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/SpinBox.cpp @@ -1460,7 +1460,7 @@ QString DoubleSpinBox::stringValue(double value, bool truncated) const numDecimals = 0; } - return toString(value, numDecimals, locale(), isGroupSeparatorShown()); + return toString(value, numDecimals, locale(), isGroupSeparatorShown(), true); } void DoubleSpinBox::updateToolTip(double value) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Tests/AzQtComponentTests.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Tests/AzQtComponentTests.cpp index 3c911c8acc..d87b238250 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Tests/AzQtComponentTests.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Tests/AzQtComponentTests.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include // Environments subclass from AZ::Test::ITestEnvironment class AzQtComponentsTestEnvironment : public AZ::Test::ITestEnvironment @@ -34,3 +36,17 @@ protected: }; AZ_UNIT_TEST_HOOK(new AzQtComponentsTestEnvironment); + +TEST(AzQtComponents, ToStringReturnsTruncatedString) +{ + double testVal = 1.2399999; + QString result = AzQtComponents::toString(testVal, 3, QLocale(), false, false); + EXPECT_TRUE(result == "1.239"); +} + +TEST(AzQtComponents, ToStringReturnsRoundedString) +{ + double testVal = 1.2399999; + QString result = AzQtComponents::toString(testVal, 3, QLocale(), false, true); + EXPECT_TRUE(result == "1.24"); +} diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/Conversions.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Utilities/Conversions.cpp index bef41bb487..542bfaf7c8 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/Conversions.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Utilities/Conversions.cpp @@ -39,15 +39,23 @@ namespace AzQtComponents return AZ::Color(static_cast(rgb.redF()), static_cast(rgb.greenF()), static_cast(rgb.blueF()), static_cast(rgb.alphaF())); } - QString toString(double value, int numDecimals, const QLocale& locale, bool showGroupSeparator) + QString toString(double value, int numDecimals, const QLocale& locale, bool showGroupSeparator, bool round) { const QChar decimalPoint = locale.decimalPoint(); const QChar zeroDigit = locale.zeroDigit(); const int numToStringDecimals = AZStd::max(numDecimals, 20); + QString retValue; - // We want to truncate, not round. toString will round, so we add extra decimal places to the formatting - // so we can remove the last values - QString retValue = locale.toString(value, 'f', (numDecimals > 0) ? numToStringDecimals : 0); + // If we want to truncate, not round, we add extra decimal places to the formatting + // so we can remove the last values otherwise we allow rounding + if (round) + { + retValue = locale.toString(value, 'f', (numDecimals > 0) ? numDecimals : 0); + } + else + { + retValue = locale.toString(value, 'f', (numDecimals > 0) ? numToStringDecimals : 0); + } // Handle special cases when we have decimals in our value if (numDecimals > 0) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/Conversions.h b/Code/Framework/AzQtComponents/AzQtComponents/Utilities/Conversions.h index 84e874fc5a..29988545f7 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/Conversions.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Utilities/Conversions.h @@ -22,7 +22,7 @@ namespace AzQtComponents AZ_QT_COMPONENTS_API AZ::Color fromQColor(const QColor& color); - AZ_QT_COMPONENTS_API QString toString(double value, int numDecimals, const QLocale& locale, bool showGroupSeparator = false); + AZ_QT_COMPONENTS_API QString toString(double value, int numDecimals, const QLocale& locale, bool showGroupSeparator = false, bool round = false); // Maintained for backwards compile compatibility inline QColor ToQColor(const AZ::Color& color) diff --git a/Code/Framework/AzToolsFramework/Tests/SpinBoxTests.cpp b/Code/Framework/AzToolsFramework/Tests/SpinBoxTests.cpp index 2d524cbcf4..f885240fde 100644 --- a/Code/Framework/AzToolsFramework/Tests/SpinBoxTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/SpinBoxTests.cpp @@ -275,7 +275,7 @@ namespace UnitTest QString testString = "0" + QString(testLocale.decimalPoint()) + "9999999"; QString value = setupTruncationTest(testString); - testString = "0" + QString(testLocale.decimalPoint()) + "999"; + testString = "1" + QString(testLocale.decimalPoint()) + "0"; EXPECT_TRUE(value == testString); } @@ -295,19 +295,19 @@ namespace UnitTest QString testString = "0" + QString(testLocale.decimalPoint()) + "12395"; QString value = setupTruncationTest(testString); - testString = "0" + QString(testLocale.decimalPoint()) + "123"; + testString = "0" + QString(testLocale.decimalPoint()) + "124"; EXPECT_TRUE(value == testString); testString = "0" + QString(testLocale.decimalPoint()) + "94496"; value = setupTruncationTest(testString); - testString = "0" + QString(testLocale.decimalPoint()) + "944"; + testString = "0" + QString(testLocale.decimalPoint()) + "945"; EXPECT_TRUE(value == testString); testString = "0" + QString(testLocale.decimalPoint()) + "0009999"; value = setupTruncationTest(testString); - testString = "0" + QString(testLocale.decimalPoint()) + "0"; + testString = "0" + QString(testLocale.decimalPoint()) + "001"; EXPECT_TRUE(value == testString); } From 098005afbce28facfcaf23673d07b8ba343ab366 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Mon, 10 Jan 2022 10:03:31 -0600 Subject: [PATCH 15/26] AZStd::basic_string improvements (#6438) * AZStd::basic_string improvements The AZStd::basic_string class has a better implementation of the Short String Optimization, which increases the amount of characters that can be stored in a `basic_string` from 15 characters to 22 characters(not-including null-terminating characters). For a `basic_string` on Windows the amount of characters that can be stored increases from 7 to 10. Using `basic_string` on Unix platforms SSO character amount from 3 to 4 characters. An additional benefit is that the size of the AZStd::basic_string class has been reduced from 40 bytes to 32 bytes when using the AZStd::allocator. When using a stateless allocator with no non static data members such as AZStd::stateless_allocator, the size of the AZStd::basic_string is 24 bytes. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Corrected comments and updated type alias to usings for AZStd::basic_string Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Added Benchmarks for the basic_string and basic_fixed_string class The benchmarks currently measure the speed of the `assign` overloads. A benchmark has also been added to compare the speed swapping two `basic_string` instances by 3 memcpy vs 3 pointer swap operations Speed up string operation when in the iterator overload cases of the `assign`, `append`, `insert` and `replace` function. The code was always performing the logic to copy over a string that is overlapping, without actually checking if the string was overlapping in the first place. Added an `az_builtin_is_constant_evaluated` macro that allows use of the C++20 `std::is_constant_evaluated` feature to determine if an operation is being performed at compile time vs run time. That macro is being used to speed up the char_trait operations at run time, by using the faster standard library functions. For example char_traits::move now uses "memmove" at runtime, instead of a for loop. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Simplified string logic in AWSMetricsServiceApiTest. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/PlatformDef.h | 76 + .../AzCore/AzCore/std/allocator_stateless.cpp | 10 +- .../AzCore/AzCore/std/allocator_stateless.h | 6 +- .../AzCore/std/containers/compressed_pair.h | 17 +- .../AzCore/std/containers/compressed_pair.inl | 4 +- .../AzCore/AzCore/std/string/fixed_string.h | 20 - .../AzCore/AzCore/std/string/fixed_string.inl | 415 +++-- .../AzCore/AzCore/std/string/string.h | 1494 +++++++++-------- .../AzCore/AzCore/std/string/string_view.h | 152 +- .../VisualStudio/AzCore/Natvis/azcore.natvis | 51 +- Code/Framework/AzCore/Tests/AZStd/String.cpp | 912 ++++++---- .../AWSAttributionServiceApiTest.cpp | 9 +- .../Code/Tests/AWSMetricsServiceApiTest.cpp | 7 +- .../Code/Tests/AnimGraphEventTests.cpp | 2 +- .../Code/Tests/AnimGraphRefCountTests.cpp | 2 +- 15 files changed, 1860 insertions(+), 1317 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/PlatformDef.h b/Code/Framework/AzCore/AzCore/PlatformDef.h index 8d28d27959..7f00f7e90e 100644 --- a/Code/Framework/AzCore/AzCore/PlatformDef.h +++ b/Code/Framework/AzCore/AzCore/PlatformDef.h @@ -149,3 +149,79 @@ #if !defined(AZ_COMMAND_LINE_LEN) # define AZ_COMMAND_LINE_LEN 2048 #endif + +#include +#include +#include +#include +#include + +// First check if the feature if is_constant_evaluated is available via the feature test macro +// https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros#C.2B.2B20 +#if __cpp_lib_is_constant_evaluated + #define az_builtin_is_constant_evaluated() std::is_constant_evaluated() +#endif + +// Next check if there is a __builtin_is_constant_evaluated that can be used +// This works on MSVC 19.28+ toolsets when using C++17, as well as +// clang 9.0.0+ when using C++17. +// Finally it works on gcc 9.0+ when using C++17 +#if !defined(az_builtin_is_constant_evaluated) + #if defined(__has_builtin) + #if __has_builtin(__builtin_is_constant_evaluated) + #define az_builtin_is_constant_evaluated() __builtin_is_constant_evaluated() + #define az_has_builtin_is_constant_evaluated() true + #endif + #elif AZ_COMPILER_MSVC >= 1928 + #define az_builtin_is_constant_evaluated() __builtin_is_constant_evaluated() + #define az_has_builtin_is_constant_evaluated() true + #elif AZ_COMPILER_GCC + #define az_builtin_is_constant_evaluated() __builtin_is_constant_evaluated() + #define az_has_builtin_is_constant_evaluated() true + #endif +#endif + +// In this case no support for the determining whether an operation is occuring +// at compile time is supported so assume that evaluation is always occuring at compile time +// in order to make sure the "safe" operation is being performed +#if !defined(az_builtin_is_constant_evaluated) + namespace AZ::Internal + { + constexpr bool builtin_is_constant_evaluated() + { + return true; + } + } + #define az_builtin_is_constant_evaluated() AZ::Internal::builtin_is_constant_evaluated() + #define az_has_builtin_is_constant_evaluated() false +#endif + +// define builtin functions used by char_traits class for efficient compile time and runtime +// operations +#if defined(__has_builtin) + #if __has_builtin(__builtin_memcpy) + #define az_has_builtin_memcpy true + #endif + #if __has_builtin(__builtin_wmemcpy) + #define az_has_builtin_wmemcpy true + #endif + #if __has_builtin(__builtin_memmove) + #define az_has_builtin_memmove true + #endif + #if __has_builtin(__builtin_wmemmove) + #define az_has_builtin_wmemmove true + #endif +#endif + +#if !defined(az_has_builtin_memcpy) + #define az_has_builtin_memcpy false +#endif +#if !defined(az_has_builtin_wmemcpy) + #define az_has_builtin_wmemcpy false +#endif +#if !defined(az_has_builtin_memmove) + #define az_has_builtin_memmove false +#endif +#if !defined(az_has_builtin_wmemmove) + #define az_has_builtin_wmemmove false +#endif diff --git a/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp b/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp index 5806cc485c..baf650a560 100644 --- a/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp +++ b/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp @@ -11,17 +11,17 @@ namespace AZStd { - stateless_allocator::stateless_allocator(const char* name) - : m_name(name) {} + stateless_allocator::stateless_allocator() = default; + stateless_allocator::stateless_allocator(const char*) + {} const char* stateless_allocator::get_name() const { - return m_name; + return "AZStd::stateless_allocator"; } - void stateless_allocator::set_name(const char* name) + void stateless_allocator::set_name(const char*) { - m_name = name; } auto stateless_allocator::allocate(size_type byteSize) -> pointer_type diff --git a/Code/Framework/AzCore/AzCore/std/allocator_stateless.h b/Code/Framework/AzCore/AzCore/std/allocator_stateless.h index b73c680c32..6b78aca53d 100644 --- a/Code/Framework/AzCore/AzCore/std/allocator_stateless.h +++ b/Code/Framework/AzCore/AzCore/std/allocator_stateless.h @@ -26,7 +26,8 @@ namespace AZStd using difference_type = ptrdiff_t; using allow_memory_leaks = AZStd::true_type; - stateless_allocator(const char* name = "AZStd::stateless_allocator"); + stateless_allocator(); + explicit stateless_allocator(const char*); // Stateless allocator does not store a name stateless_allocator(const stateless_allocator& rhs) = default; stateless_allocator& operator=(const stateless_allocator& rhs) = default; @@ -51,9 +52,6 @@ namespace AZStd bool is_lock_free(); bool is_stale_read_allowed(); bool is_delayed_recycling(); - - private: - const char* m_name; }; bool operator==(const stateless_allocator& left, const stateless_allocator& right); diff --git a/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.h b/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.h index 066ec7be5e..c79ddf11ee 100644 --- a/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.h +++ b/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include /* Microsoft C++ ABI puts 1 byte of padding between each empty base class when multiple inheritance is being used @@ -20,7 +21,7 @@ #if defined(AZ_COMPILER_MSVC) #define AZSTD_COMPRESSED_PAIR_EMPTY_BASE_OPTIMIZATION __declspec(empty_bases) #else -#define AZSTD_COMPRESSED_PAIR_EMPTY_BASE_OPTIMIZATION +#define AZSTD_COMPRESSED_PAIR_EMPTY_BASE_OPTIMIZATION #endif namespace AZStd @@ -97,16 +98,14 @@ namespace AZStd using second_base_value_type = typename second_base_type::value_type; public: - // First template argument is a placeholder argument of void as MSVC examines the types - // of a templated function to determine if they are the same template - // Due to the "template compressed_pair(skip_element_tag, T&&)" - // constructor below, the default constructor template types needs to be distinguished from it - template ::value - && AZStd::is_default_constructible::value>> + // First template argument is used to perform a substitution into AZStd::enable_if_t + // so that SFINAE can trigger + template + && AZStd::is_default_constructible_v, Unused>> constexpr compressed_pair(); - template , compressed_pair>::value, bool> = true> + template , compressed_pair>, bool> = true> constexpr explicit compressed_pair(T&& firstElement); template diff --git a/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.inl b/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.inl index 9fb5eb87fb..8e585a1467 100644 --- a/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.inl +++ b/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.inl @@ -75,7 +75,7 @@ namespace AZStd } template - template , compressed_pair>::value, bool>> + template , compressed_pair>, bool>> inline constexpr compressed_pair::compressed_pair(T&& firstElement) : first_base_type{ AZStd::forward(firstElement) } , second_base_type{} @@ -117,7 +117,7 @@ namespace AZStd { return static_cast(*this).get(); } - + template inline constexpr auto compressed_pair::second() -> second_base_value_type& { diff --git a/Code/Framework/AzCore/AzCore/std/string/fixed_string.h b/Code/Framework/AzCore/AzCore/std/string/fixed_string.h index a8e0f96988..ea841bc4ca 100644 --- a/Code/Framework/AzCore/AzCore/std/string/fixed_string.h +++ b/Code/Framework/AzCore/AzCore/std/string/fixed_string.h @@ -343,26 +343,6 @@ namespace AZStd static decltype(auto) format(const wchar_t* format, ...); protected: - template - constexpr auto append_iter(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, basic_fixed_string&>; - - template - constexpr auto construct_iter(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v>; - - template - constexpr auto assign_iter(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, basic_fixed_string&>; - - template - constexpr auto insert_iter(const_iterator insertPos, InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, iterator>; - - template - constexpr auto replace_iter(const_iterator first, const_iterator last, InputIt first2, InputIt last2) - -> enable_if_t && !is_convertible_v, basic_fixed_string&>; - constexpr auto fits_in_capacity(size_type newSize) -> bool; inline static constexpr size_type Capacity = MaxElementCount; // current storage reserved for string not including null-terminator diff --git a/Code/Framework/AzCore/AzCore/std/string/fixed_string.inl b/Code/Framework/AzCore/AzCore/std/string/fixed_string.inl index 8b973d3b2c..15d2acf7a1 100644 --- a/Code/Framework/AzCore/AzCore/std/string/fixed_string.inl +++ b/Code/Framework/AzCore/AzCore/std/string/fixed_string.inl @@ -62,14 +62,7 @@ namespace AZStd template inline constexpr basic_fixed_string::basic_fixed_string(InputIt first, InputIt last) { // construct from [first, last) - if (first == last) - { - Traits::assign(m_buffer[0], Element()); // terminate - } - else - { - construct_iter(first, last); - } + assign(first, last); } // #7 @@ -98,8 +91,7 @@ namespace AZStd template inline constexpr basic_fixed_string::basic_fixed_string(const T& convertibleToView) { - AZStd::basic_string_view view = convertibleToView; - assign(view.begin(), view.end()); + assign(convertibleToView); } // #11 @@ -313,15 +305,7 @@ namespace AZStd if (count > 0 && fits_in_capacity(num)) { pointer data = m_buffer; - // make room and append new stuff using assign - if (count == 1) - { - Traits::assign(*(data + m_size), ch); - } - else - { - Traits::assign(data + m_size, count, ch); - } + Traits::assign(data + m_size, count, ch); m_size = static_cast(num); Traits::assign(data[num], Element()); // terminate } @@ -332,13 +316,47 @@ namespace AZStd template inline constexpr auto basic_fixed_string::append(InputIt first, InputIt last) -> enable_if_t && !is_convertible_v, basic_fixed_string&> - { // append [first, last) - return append_iter(first, last); + { + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + return append(AZStd::to_address(first), AZStd::distance(first, last)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be appended one by one into the buffer + size_type newSize = m_size + AZStd::distance(first, last); + if (fits_in_capacity(newSize)) + { + for (size_t updateIndex = m_size; first != last; ++first, ++updateIndex) + { + Traits::assign(m_buffer[updateIndex], static_cast(*first)); + } + m_size = static_cast(newSize); + Traits::assign(m_buffer[newSize], Element()); // terminate + } + return *this; + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_fixed_string inputCopy; + for (; first != last; ++first) + { + inputCopy.push_back(static_cast(*first)); + } + + return append(inputCopy.c_str(), inputCopy.size()); + } } template inline constexpr auto basic_fixed_string::append(AZStd::initializer_list ilist) -> basic_fixed_string& - { // append [first, last) - return append_iter(ilist.begin(), ilist.end()); + { + return append(ilist.begin(), ilist.size()); } template @@ -420,18 +438,10 @@ namespace AZStd inline constexpr auto basic_fixed_string::assign(size_type count, Element ch) -> basic_fixed_string& { // assign count * ch - AZSTD_CONTAINER_ASSERT(count != npos, "result is too long!"); if (fits_in_capacity(count)) { // make room and assign new stuff pointer data = m_buffer; - if (count == 1) - { - Traits::assign(*(data), ch); - } - else - { - Traits::assign(data, count, ch); - } + Traits::assign(data, count, ch); m_size = static_cast(count); Traits::assign(data[count], Element()); // terminate } @@ -443,12 +453,46 @@ namespace AZStd inline constexpr auto basic_fixed_string::assign(InputIt first, InputIt last) -> enable_if_t && !is_convertible_v, basic_fixed_string&> { - return assign_iter(first, last); + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + return assign(AZStd::to_address(first), AZStd::distance(first, last)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be assigned one by one into the buffer + size_type newSize = AZStd::distance(first, last); + if (fits_in_capacity(newSize)) + { + for (size_t updateIndex = 0; first != last; ++first, ++updateIndex) + { + Traits::assign(m_buffer[updateIndex], static_cast(*first)); + } + m_size = static_cast(newSize); + Traits::assign(m_buffer[newSize], Element()); // terminate + } + return *this; + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_fixed_string inputCopy; + for (; first != last; ++first) + { + inputCopy.push_back(static_cast(*first)); + } + + return assign(inputCopy.c_str(), inputCopy.size()); + } } template inline constexpr auto basic_fixed_string::assign(AZStd::initializer_list ilist) -> basic_fixed_string& { - return assign_iter(ilist.begin(), ilist.end()); + return assign(ilist.begin(), ilist.size()); } template @@ -536,14 +580,7 @@ namespace AZStd pointer data = m_buffer; // make room and insert new stuff Traits::copy_backward(data + offset + count, data + offset, m_size - offset); // empty out hole - if (count == 1) - { - Traits::assign(*(data + offset), ch); - } - else - { - Traits::assign(data + offset, count, ch); - } + Traits::assign(data + offset, count, ch); m_size = static_cast(num); Traits::assign(data[num], Element()); // terminate } @@ -582,14 +619,51 @@ namespace AZStd inline constexpr auto basic_fixed_string::insert(const_iterator insertPos, InputIt first, InputIt last)-> enable_if_t && !is_convertible_v, iterator> { // insert [_First, _Last) at _Where - return insert_iter(insertPos, first, last); + size_type insertOffset = AZStd::distance(cbegin(), insertPos); + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + insert(insertOffset, AZStd::to_address(first), AZStd::distance(first, last)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be inserted one by one into the buffer + size_type count = AZStd::distance(first, last); + size_type newSize = m_size + count; + if (fits_in_capacity(newSize)) + { + Traits::copy_backward(m_buffer + insertOffset + count, m_buffer + insertOffset, m_size - insertOffset); // empty out hole + for (size_t updateIndex = insertOffset; first != last; ++first, ++updateIndex) + { + Traits::assign(m_buffer[updateIndex], static_cast(*first)); + } + m_size = static_cast(newSize); + Traits::assign(m_buffer[newSize], Element()); // terminate + } + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_fixed_string inputCopy; + for (; first != last; ++first) + { + inputCopy.push_back(static_cast(*first)); + } + + insert(insertOffset, inputCopy.c_str(), inputCopy.size()); + } + return begin() + insertOffset; } template inline constexpr auto basic_fixed_string::insert(const_iterator insertPos, AZStd::initializer_list ilist) -> iterator { // insert [_First, _Last) at _Where - return insert_iter(insertPos, ilist.begin(), ilist.end()); + return insert(insertPos, ilist.begin(), ilist.end()); } template @@ -604,7 +678,7 @@ namespace AZStd { // move elements down pointer data = m_buffer; - Traits::copy(data + offset, data + offset + count, m_size - offset - count); + Traits::move(data + offset, data + offset + count, m_size - offset - count); m_size = static_cast(m_size - count); Traits::assign(data[m_size], Element()); // terminate } @@ -643,7 +717,7 @@ namespace AZStd const basic_fixed_string& rhs) -> basic_fixed_string& { // replace [offset, offset + count) with rhs - return replace(offset, count, rhs, size_type(0), npos); + return replace(offset, count, rhs.c_str(), rhs.size()); } template @@ -651,56 +725,7 @@ namespace AZStd const basic_fixed_string& rhs, size_type rhsOffset, size_type rhsCount) -> basic_fixed_string& { // replace [offset, offset + count) with rhs [rhsOffset, rhsOffset + rhsCount) - AZSTD_CONTAINER_ASSERT(m_size >= offset && rhs.m_size >= rhsOffset, "Invalid offsets"); - if (m_size - offset < count) - { - count = m_size - offset; // trim count to size - } - size_type num = rhs.m_size - rhsOffset; - if (num < rhsCount) - { - rhsCount = num; // trim rhsCount to size - } - AZSTD_CONTAINER_ASSERT(npos - rhsCount > m_size - count, "Result is too long"); - - size_type nm = m_size - count - offset; // length of preserved tail - size_type newSize = m_size + rhsCount - count; - if (fits_in_capacity(newSize)) - { - pointer data = m_buffer; - const_pointer rhsData = rhs.m_buffer; - - if (this != &rhs) - { // no overlap, just move down and copy in new stuff - Traits::copy_backward(data + offset + rhsCount, data + offset + count, nm); // empty hole - Traits::copy(data + offset, rhsData + rhsOffset, rhsCount); // fill hole - } - else if (rhsCount <= count) - { // hole doesn't get larger, just copy in substring - Traits::copy(data + offset, data + rhsOffset, rhsCount); // fill hole - Traits::copy_backward(data + offset + rhsCount, data + offset + count, nm); // move tail down - } - else if (rhsOffset <= offset) - { // hole gets larger, substring begins before hole - Traits::copy_backward(data + offset + rhsCount, data + offset + count, nm); // move tail down - Traits::copy(data + offset, data + rhsOffset, rhsCount); // fill hole - } - else if (offset + count <= rhsOffset) - { // hole gets larger, substring begins after hole - Traits::copy_backward(data + offset + rhsCount, data + offset + count, nm); // move tail down - Traits::copy(data + offset, data + (rhsOffset + rhsCount - count), rhsCount); // fill hole - } - else - { // hole gets larger, substring begins in hole - Traits::copy(data + offset, data + rhsOffset, count); // fill old hole - Traits::copy_backward(data + offset + rhsCount, data + offset + count, nm); // move tail down - Traits::copy(data + offset + count, data + rhsOffset + rhsCount, rhsCount - count); // fill rest of new hole - } - - m_size = static_cast(newSize); - Traits::assign(data[newSize], Element()); // terminate - } - return *this; + return replace(offset, count, rhs.c_str() + rhsOffset, AZStd::min(rhsCount, rhs.size() - rhsOffset)); } template template @@ -720,35 +745,83 @@ namespace AZStd pointer data = m_buffer; // replace [offset, offset + count) with [ptr, ptr + ptrCount) AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - if (m_size - offset < count) - { - count = m_size - offset; // trim _N0 to size - } - AZSTD_CONTAINER_ASSERT(npos - ptrCount > m_size - count, "Result too long"); + // Make sure count is within is no larger than the distance from the offset + // to the end of this string + count = AZStd::min(count, m_size - offset); - size_type nm = m_size - count - offset; - if (ptrCount < count) + size_type newSize = m_size + ptrCount - count; + if (fits_in_capacity(newSize)) { - Traits::copy(data + offset + ptrCount, data + offset + count, nm); // smaller hole, move tail up - } - size_type num = m_size + ptrCount - count; - if ((0 != ptrCount || 0 != count) && fits_in_capacity(num)) - { - data = m_buffer; - // make room and rearrange - if (count < ptrCount) + // The code assumes that compile time evaluation will not need to deal with overlapping input + size_type charsAfterCountToMove = m_size - count - offset; + if (az_builtin_is_constant_evaluated() || !((ptr >= data + offset && ptr < data + offset + count) + || (ptr + ptrCount > data + offset && ptr + ptrCount <= data + offset + count))) { - Traits::copy_backward(data + offset + ptrCount, data + offset + count, nm); // move tail down + // Ex1. this = "ABCDEFG", offset = 1, count = 4 + // Input string is "CDE" + // First the text post offset + count is moved to right after the input string will be copied + // "ABCDFG" + // ^^^ + // Next the input string is copied into the buffer + // "ACDEFG" + // + // Ex2. this = "ABCDEFG", offset = 1, count = 2 + // Input string is "CDE" + // Performing the same two steps above, the string transform as follows + // "ABCDEFG" -> "ABCDDEFG" -> "ACDEDEFG" + // ^^^ + if (count != ptrCount) + { + Traits::move(data + offset + ptrCount, data + offset + count, charsAfterCountToMove); + } + if (ptrCount > 0) + { + // Copy bytes up to the minimum of this string count and input string count + Traits::copy(data + offset, ptr, ptrCount); + } } - - if (ptrCount > 0) + else { - Traits::copy(data + offset, ptr, ptrCount); // fill hole + // Overlap checks for fixed_string only needs to check between this string + // [offset, offset + count) due to fixed_string never moving memory + // + // Ex. this = "ABCDEFG", offset = 1, count=4 + // substring is "CDE" + // The text from offset 1 for 4 chars "BCDE": should be replaced with "CDE" + // making a whole for the bytes results in output = "ABCDFG" + // Afterwards output = "ACDEFG" + // The input string overlaps with this string in this case + // So the string is copied piecewise + if (ptrCount <= count) + { // hole doesn't get larger, just copy in substring + Traits::move(data + offset, ptr, ptrCount); // fill hole + Traits::copy(data + offset + ptrCount, data + offset + count, charsAfterCountToMove); // move tail down + } + else + { + if (ptr <= data + offset) + { // hole gets larger, substring begins before hole + Traits::copy_backward(data + offset + ptrCount, data + offset + count, charsAfterCountToMove); // move tail down + Traits::copy(data + offset, ptr, ptrCount); // fill hole + } + else if (data + offset + count <= ptr) + { // hole gets larger, substring begins after hole + Traits::copy_backward(data + offset + ptrCount, data + offset + count, charsAfterCountToMove); // move tail down + Traits::copy(data + offset, ptr + (ptrCount - count), ptrCount); // fill hole + } + else + { // hole gets larger, substring begins in hole + Traits::copy(data + offset, ptr, count); // fill old hole + Traits::copy_backward(data + offset + ptrCount, data + offset + count, charsAfterCountToMove); // move tail down + Traits::copy(data + offset + count, ptr + ptrCount, ptrCount - count); // fill rest of new hole + } + } } - - m_size = static_cast(num); - Traits::assign(data[num], Element()); // terminate } + + m_size = static_cast(newSize); + Traits::assign(data[newSize], Element()); // terminate + return *this; } @@ -793,14 +866,7 @@ namespace AZStd { Traits::copy_backward(data + offset + num, data + offset + count, nm); // move tail down } - if (count == 1) - { - Traits::assign(*(data + offset), ch); - } - else - { - Traits::assign(data + offset, num, ch); - } + Traits::assign(data + offset, num, ch); m_size = static_cast(numToGrow); Traits::assign(data[numToGrow], Element()); // terminate } @@ -851,15 +917,54 @@ namespace AZStd template template inline constexpr auto basic_fixed_string::replace(const_iterator first, const_iterator last, - InputIt first2, InputIt last2) -> enable_if_t && !is_convertible_v, basic_fixed_string&> - { // replace [first, last) with [first2,last2) - return replace_iter(first, last, first2, last2); + InputIt replaceFirst, InputIt replaceLast) -> enable_if_t && !is_convertible_v, basic_fixed_string&> + { // replace [first, last) with [replaceFirst,replaceLast) + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + return replace(first, last, AZStd::to_address(replaceFirst), AZStd::distance(replaceFirst, replaceLast)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be appended one by one into the buffer + + size_type insertOffset = AZStd::distance(cbegin(), first); + size_type postInsertOffset = AZStd::distance(cbegin(), last); + size_type count = AZStd::distance(replaceFirst, replaceLast); + size_type newSize = m_size + count - AZStd::distance(first, last); + if (fits_in_capacity(newSize)) + { + Traits::move(first + count, last, m_size - postInsertOffset); // empty out hole + for (size_t updateIndex = insertOffset; replaceFirst != replaceLast; ++replaceFirst, ++updateIndex) + { + Traits::assign(m_buffer[updateIndex], static_cast(*replaceFirst)); + } + m_size = static_cast(newSize); + Traits::assign(m_buffer[newSize], Element()); // terminate + } + return *this; + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_fixed_string inputCopy; + for (; replaceFirst != replaceLast; ++replaceFirst) + { + inputCopy.push_back(static_cast(*replaceFirst)); + } + + return replace(first, last, inputCopy.c_str(), inputCopy.size()); + } } template inline constexpr auto basic_fixed_string::replace(const_iterator first, const_iterator last, AZStd::initializer_list ilist) -> basic_fixed_string& - { // replace [first, last) with [first2,last2) - return replace_iter(first, last, ilist.begin(), ilist.end()); + { + return replace(first, last, ilist.begin(), ilist.end()); } template @@ -1411,54 +1516,6 @@ namespace AZStd return result; } - template - template - inline constexpr auto basic_fixed_string::construct_iter(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v> - { - // initialize from [first, last), input iterators - for (; first != last; ++first) - { - append((size_type)1, (Element)* first); - } - } - - template - template - inline constexpr auto basic_fixed_string::append_iter(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, basic_fixed_string&> - { // append [first, last), input iterators - return replace(end(), end(), first, last); - } - - template - template - inline constexpr auto basic_fixed_string::assign_iter(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, basic_fixed_string&> - { - return replace(begin(), end(), first, last); - } - - template - template - inline constexpr auto basic_fixed_string::insert_iter(const_iterator insertPos, InputIt first, - InputIt last) -> enable_if_t && !is_convertible_v, iterator> - { // insert [first, last) at insertPos, input iterators - difference_type offset = insertPos - cbegin(); - replace(insertPos, insertPos, first, last); - return iterator(m_buffer + offset); - } - - template - template - inline constexpr auto basic_fixed_string::replace_iter(const_iterator first, const_iterator last, - InputIt first2, InputIt last2) -> enable_if_t && !is_convertible_v, basic_fixed_string&> - { // replace [first, last) with [first2, last2), input iterators - basic_fixed_string rhs(first2, last2); - replace(first, last, rhs); - return *this; - } - template inline constexpr auto basic_fixed_string::fits_in_capacity(size_type newSize)-> bool { diff --git a/Code/Framework/AzCore/AzCore/std/string/string.h b/Code/Framework/AzCore/AzCore/std/string/string.h index e107c4657d..7dd6dd7065 100644 --- a/Code/Framework/AzCore/AzCore/std/string/string.h +++ b/Code/Framework/AzCore/AzCore/std/string/string.h @@ -5,24 +5,43 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#ifndef AZSTD_STRING_H -#define AZSTD_STRING_H +#pragma once #include #include #include #include +#include #include #include #include #include #include -#include #include #include +namespace AZStd::StringInternal +{ + template + struct Padding + { + AZ::u8 m_padding[ElementSize - 1]; + }; + + template + struct Padding + {}; +} + +#if defined(HAVE_BENCHMARK) +namespace Benchmark +{ + class StringBenchmarkFixture; +} +#endif + namespace AZStd { /** @@ -35,130 +54,95 @@ namespace AZStd : public Debug::checked_container_base #endif { - typedef basic_string this_type; + using this_type = basic_string; public: - typedef Element* pointer; - typedef const Element* const_pointer; + using pointer = Element*; + using const_pointer = const Element*; - typedef Element& reference; - typedef const Element& const_reference; - typedef typename Allocator::difference_type difference_type; - typedef typename Allocator::size_type size_type; + using reference = Element&; + using const_reference = const Element&; + using difference_type = typename Allocator::difference_type; + using size_type = typename Allocator::size_type; - typedef pointer iterator_impl; - typedef const_pointer const_iterator_impl; + using iterator_impl = pointer; + using const_iterator_impl = const_pointer; #ifdef AZSTD_HAS_CHECKED_ITERATORS - typedef Debug::checked_randomaccess_iterator iterator; - typedef Debug::checked_randomaccess_iterator const_iterator; + using iterator = Debug::checked_randomaccess_iterator; + using const_iterator = Debug::checked_randomaccess_iterator; #else - typedef iterator_impl iterator; - typedef const_iterator_impl const_iterator; + using iterator = iterator_impl; + using const_iterator = const_iterator_impl; #endif - typedef AZStd::reverse_iterator reverse_iterator; - typedef AZStd::reverse_iterator const_reverse_iterator; - typedef Element value_type; - typedef Traits traits_type; - typedef Allocator allocator_type; + using reverse_iterator = AZStd::reverse_iterator; + using const_reverse_iterator = AZStd::reverse_iterator; + using value_type = Element; + using traits_type = Traits; + using allocator_type = Allocator; // AZSTD extension. /** * \brief Allocation node type. Common for all AZStd containers. * In vectors case we allocate always "sizeof(node_type)*capacity" block. */ - typedef value_type node_type; + using node_type = value_type; - static const size_type npos = size_type(-1); + inline static constexpr size_type npos = size_type(-1); inline basic_string(const Allocator& alloc = Allocator()) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(alloc) + : m_storage{ skip_element_tag{}, alloc } { - Traits::assign(m_buffer[0], Element()); + Traits::assign(m_storage.first().GetData()[0], Element()); } inline basic_string(const_pointer ptr, size_type count, const Allocator& alloc = Allocator()) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(alloc) + : m_storage{ skip_element_tag{}, alloc } { // construct from [ptr, ptr + count) assign(ptr, count); } inline basic_string(const_pointer ptr, const Allocator& alloc = Allocator()) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(alloc) + : m_storage{ skip_element_tag{}, alloc } { // construct from [ptr, ) assign(ptr); } inline basic_string(size_type count, Element ch, const Allocator& alloc = Allocator()) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(alloc) + : m_storage{ skip_element_tag{}, alloc } { // construct from count * ch assign(count, ch); } - template - inline basic_string(InputIterator first, InputIterator last, const Allocator& alloc = Allocator()) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(alloc) + template && !is_convertible_v>> + inline basic_string(InputIt first, InputIt last, const Allocator& alloc = Allocator()) + : m_storage{ skip_element_tag{}, alloc } { // construct from [first, last) - if (first == last) - { - Traits::assign(m_buffer[0], Element()); // terminate - } - else - { - construct_iter(first, last, is_integral()); - } + assign(first, last); } inline basic_string(const_pointer first, const_pointer last) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) { // construct from [first, last), const pointers - assign(&*first, last - first); + assign(first, last - first); } - //inline basic_string(const_iterator _First, const_iterator _Last) - // : m_size(0) - // , m_capacity(SSO_BUF_SIZE-1) - //{ // construct from [_First, _Last), const_iterators - // if (first != last) - // assign(&*first, last - first); - //} - inline basic_string(const this_type& rhs) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(rhs.m_allocator) + : m_storage{ skip_element_tag{}, rhs.m_storage.second() } { assign(rhs, 0, npos); } inline basic_string(this_type&& rhs) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(AZStd::move(rhs.m_allocator)) + : m_storage{ skip_element_tag{}, AZStd::move(rhs.m_storage.second()) } { assign(AZStd::forward(rhs)); } inline basic_string(const this_type& rhs, size_type rhsOffset, size_type count = npos) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) { // construct from rhs [rhsOffset, rhsOffset + count) assign(rhs, rhsOffset, count); } inline basic_string(const this_type& rhs, size_type rhsOffset, size_type count, const Allocator& alloc) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(alloc) + : m_storage{ skip_element_tag{}, alloc } { // construct from rhs [rhsOffset, rhsOffset + count) with allocator assign(rhs, rhsOffset, count); } @@ -174,7 +158,7 @@ namespace AZStd inline ~basic_string() { // destroy the string - deallocate_memory(m_data, 0, typename allocator_type::allow_memory_leaks()); + deallocate_memory(m_storage.first().GetData(), 0, typename allocator_type::allow_memory_leaks()); } operator AZStd::basic_string_view() const @@ -182,12 +166,12 @@ namespace AZStd return AZStd::basic_string_view(data(), size()); } - inline iterator begin() { return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer)); } - inline const_iterator begin() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer)); } - inline const_iterator cbegin() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer)); } - inline iterator end() { return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, (SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer) + m_size)); } - inline const_iterator end() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, (SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer) + m_size)); } - inline const_iterator cend() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, (SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer) + m_size)); } + inline iterator begin() { return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, m_storage.first().GetData())); } + inline const_iterator begin() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, m_storage.first().GetData())); } + inline const_iterator cbegin() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, m_storage.first().GetData())); } + inline iterator end() { return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, (m_storage.first().GetData()) + m_storage.first().GetSize())); } + inline const_iterator end() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, (m_storage.first().GetData()) + m_storage.first().GetSize())); } + inline const_iterator cend() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, (m_storage.first().GetData()) + m_storage.first().GetSize())); } inline reverse_iterator rbegin() { return reverse_iterator(end()); } inline const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } inline const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); } @@ -196,7 +180,7 @@ namespace AZStd inline const_reverse_iterator crend() const { return const_reverse_iterator(begin()); } inline this_type& operator=(const this_type& rhs) { return assign(rhs); } - inline this_type& operator=(this_type&& rhs) { return assign(AZStd::forward(rhs)); } + inline this_type& operator=(this_type&& rhs) { return assign(AZStd::move(rhs)); } inline this_type& operator=(AZStd::basic_string_view view) { return assign(view); } inline this_type& operator=(const_pointer ptr) { return assign(ptr); } inline this_type& operator=(Element ch) { return assign(1, ch); } @@ -208,21 +192,18 @@ namespace AZStd this_type& append(const this_type& rhs, size_type rhsOffset, size_type count) { // append rhs [rhsOffset, rhsOffset + count) AZSTD_CONTAINER_ASSERT(rhs.size() >= rhsOffset, "Invalid offset!"); - size_type num = rhs.m_size - rhsOffset; - if (num < count) + count = AZStd::min(count, rhs.size() - rhsOffset); + + size_type oldSize = size(); + size_type newSize = oldSize + count; + if (count > 0 && grow(newSize)) { - count = num; // trim count to size - } - AZSTD_CONTAINER_ASSERT(npos - m_size > count && m_size + count >= m_size, "result is too long!"); - num = m_size + count; - if (count > 0 && grow(num)) - { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; + pointer data = m_storage.first().GetData(); + const_pointer rhsData = rhs.data(); // make room and append new stuff - Traits::copy(data + m_size /*, m_capacity - m_size*/, rhsData + rhsOffset, count); - m_size = num; - Traits::assign(data[num], Element()); // terminate + Traits::copy(data + oldSize, rhsData + rhsOffset, count); + m_storage.first().SetSize(newSize); + Traits::assign(data[newSize], Element()); // terminate } return *this; } @@ -230,20 +211,21 @@ namespace AZStd this_type& append(const_pointer ptr, size_type count) { // append [ptr, ptr + count) - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (ptr != 0 && ptr >= data && (data + m_size) > ptr) + pointer data = m_storage.first().GetData(); + if (ptr != nullptr && ptr >= data && (data + size()) > ptr) { return append(*this, ptr - data, count); // substring } - AZSTD_CONTAINER_ASSERT(npos - m_size > count && m_size + count >= m_size, "result is too long!"); - size_type num = m_size + count; - if (count > 0 && grow(num)) + AZSTD_CONTAINER_ASSERT(npos - size() > count && size() + count >= size(), "result is too long!"); + size_type oldSize = size(); + size_type newSize = oldSize + count; + if (count > 0 && grow(newSize)) { // make room and append new stuff - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - Traits::copy(data + m_size /*, m_capacity - m_size*/, ptr, count); - m_size = num; - Traits::assign(data[num], Element()); // terminate + data = m_storage.first().GetData(); + Traits::copy(data + oldSize , ptr, count); + m_storage.first().SetSize(newSize); + Traits::assign(data[newSize], Element()); // terminate } return *this; } @@ -252,30 +234,60 @@ namespace AZStd this_type& append(size_type count, Element ch) { // append count * ch - AZSTD_CONTAINER_ASSERT(npos - m_size > count, "result is too long"); - size_type num = m_size + count; + AZSTD_CONTAINER_ASSERT(npos - size() > count, "result is too long"); + size_type num = size() + count; if (count > 0 && grow(num)) { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); // make room and append new stuff using assign - if (count == 1) - { - Traits::assign(*(data + m_size), ch); - } - else - { - Traits::assign(data + m_size, count, ch); - } - m_size = num; + Traits::assign(data + size(), count, ch); + m_storage.first().SetSize(num); Traits::assign(data[num], Element()); // terminate } return *this; } - template - inline this_type& append(InputIterator first, InputIterator last) + template + inline auto append(InputIt first, InputIt last) + -> enable_if_t && !is_convertible_v, this_type&> { // append [first, last) - return append_iter(first, last, AZStd::is_integral()); + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + return append(AZStd::to_address(first), AZStd::distance(first, last)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be appended one by one into the buffer + size_type oldSize = size(); + size_type newSize = oldSize + AZStd::distance(first, last); + if (grow(newSize)) + { + pointer buffer = data(); + for (size_t updateIndex = oldSize; first != last; ++first, ++updateIndex) + { + Traits::assign(buffer[updateIndex], static_cast(*first)); + } + m_storage.first().SetSize(newSize); + Traits::assign(buffer[newSize], Element()); // terminate + } + return *this; + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_string inputCopy; + for (; first != last; ++first) + { + inputCopy.push_back(static_cast(*first)); + } + + return append(inputCopy.c_str(), inputCopy.size()); + } } inline this_type& append(const_pointer first, const_pointer last) @@ -283,11 +295,6 @@ namespace AZStd return replace(end(), end(), first, last); } - //inline this_type& append(const_iterator first, const_iterator last) - //{ // append [first, last), const_iterators - // return replace(end(), end(), first, last); - //} - inline this_type& assign(const this_type& rhs) { return assign(rhs, 0, npos); @@ -302,27 +309,34 @@ namespace AZStd { if (this != &rhs) { - if (SSO_BUF_SIZE <= m_capacity) + deallocate_memory(m_storage.first().GetData(), 0, typename allocator_type::allow_memory_leaks()); + + m_storage.first().SetCapacity(rhs.capacity()); + + pointer data = m_storage.first().GetData(); + pointer rhsData = rhs.data(); + // Memmove the right hand side string data if it is using the short string optimization + // Otherwise set the pointer to the right hand side + if (rhs.m_storage.first().ShortStringOptimizationActive()) { - deallocate_memory(m_data, 0, typename allocator_type::allow_memory_leaks()); + Traits::move(data, rhsData, rhs.size() + 1); // string + null-terminator } + else + { + m_storage.first().SetData(rhsData); + } + m_storage.first().SetSize(rhs.size()); + m_storage.second() = rhs.m_storage.second(); - Traits::move(m_buffer, rhs.m_buffer, sizeof(m_buffer)); - m_size = rhs.m_size; - m_capacity = rhs.m_capacity; - m_allocator = rhs.m_allocator; - - rhs.m_data = nullptr; - rhs.m_size = 0; - rhs.m_capacity = SSO_BUF_SIZE - 1; + rhs.leak_and_reset(); } return *this; } this_type& assign(const this_type& rhs, size_type rhsOffset, size_type count) { // assign rhs [rhsOffset, rhsOffset + count) - AZSTD_CONTAINER_ASSERT(rhs.m_size >= rhsOffset, "Invalid offset"); - size_type num = rhs.m_size - rhsOffset; + AZSTD_CONTAINER_ASSERT(rhs.size() >= rhsOffset, "Invalid offset"); + size_type num = rhs.size() - rhsOffset; if (count < num) { num = count; // trim num to size @@ -334,10 +348,10 @@ namespace AZStd } else if (grow(num)) { // make room and assign new stuff - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - Traits::copy(data /*, m_capacity*/, rhsData + rhsOffset, num); - m_size = num; + pointer data = m_storage.first().GetData(); + const_pointer rhsData = rhs.data(); + Traits::copy(data, rhsData + rhsOffset, num); + m_storage.first().SetSize(num); Traits::assign(data[num], Element()); // terminate } return *this; @@ -345,20 +359,20 @@ namespace AZStd this_type& assign(const_pointer ptr, size_type count) { // assign [ptr, ptr + count) - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (ptr != 0 && ptr >= data && (data + m_size) > ptr) + pointer data = m_storage.first().GetData(); + if (ptr != nullptr && ptr >= data && (data + size()) > ptr) { return assign(*this, ptr - data, count); // substring } if (grow(count)) { // make room and assign new stuff - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + data = m_storage.first().GetData(); if (count > 0) { Traits::copy(data, ptr, count); } - m_size = count; + m_storage.first().SetSize(count); Traits::assign(data[count], Element()); // terminate } return *this; @@ -367,109 +381,132 @@ namespace AZStd this_type& assign(size_type count, Element ch) { // assign count * ch - AZSTD_CONTAINER_ASSERT(count != npos, "result is too long!"); if (grow(count)) { // make room and assign new stuff - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (count == 1) - { - Traits::assign(*(data), ch); - } - else - { - Traits::assign(data, count, ch); - } - m_size = count; + pointer data = m_storage.first().GetData(); + Traits::assign(data, count, ch); + m_storage.first().SetSize(count); Traits::assign(data[count], Element()); // terminate } return *this; } - template - inline this_type& assign(InputIterator first, InputIterator last) { return assign_iter(first, last, AZStd::is_integral()); } - inline this_type& assign(const_pointer first, const_pointer last) { return replace(begin(), end(), first, last); } - inline this_type& insert(size_type offset, const this_type& rhs) { return insert(offset, rhs, 0, npos); } + template + auto assign(InputIt first, InputIt last) + -> enable_if_t && !is_convertible_v, this_type&> + { + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + return assign(AZStd::to_address(first), AZStd::distance(first, last)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // forward iterator pointer type doesn't match the const_pointer type + // So the elements need to be assigned one by one into the buffer + size_type newSize = AZStd::distance(first, last); + if (grow(newSize)) + { + pointer buffer = data(); + for (size_t updateIndex = 0; first != last; ++first, ++updateIndex) + { + Traits::assign(buffer[updateIndex], static_cast(*first)); + } + m_storage.first().SetSize(newSize); + Traits::assign(buffer[newSize], Element()); // terminate + } + return *this; + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_string inputCopy; + for (; first != last; ++first) + { + inputCopy.push_back(static_cast(*first)); + } + + return assign(inputCopy.c_str(), inputCopy.size()); + } + } + inline this_type& insert(size_type offset, const this_type& rhs) { return insert(offset, rhs, 0, npos); } this_type& insert(size_type offset, const this_type& rhs, size_type rhsOffset, size_type count) { // insert rhs [rhsOffset, rhsOffset + count) at offset - AZSTD_CONTAINER_ASSERT(m_size >= offset && rhs.m_size >= rhsOffset, "Invalid offset(s)"); - size_type num = rhs.m_size - rhsOffset; + AZSTD_CONTAINER_ASSERT(size() >= offset && rhs.size() >= rhsOffset, "Invalid offset(s)"); + size_type num = rhs.size() - rhsOffset; if (num < count) { count = num; // trim _Count to size } - AZSTD_CONTAINER_ASSERT(npos - m_size > count, "Result is too long"); - num = m_size + count; + AZSTD_CONTAINER_ASSERT(npos - size() > count, "Result is too long"); + num = size() + count; if (count > 0 && grow(num)) { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); // make room and insert new stuff - Traits::move(data + offset + count /*, m_capacity - offset - count*/, data + offset, m_size - offset); // empty out hole + Traits::move(data + offset + count, data + offset, size() - offset); // empty out hole if (this == &rhs) { - Traits::move(data + offset /*, m_capacity - offset*/, data + (offset < rhsOffset ? rhsOffset + count : rhsOffset), count); // substring + Traits::move(data + offset, data + (offset < rhsOffset ? rhsOffset + count : rhsOffset), count); // substring } else { - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - Traits::copy(data + offset /*, m_capacity - offset*/, rhsData + rhsOffset, count); // fill hole + const_pointer rhsData = rhs.data(); + Traits::copy(data + offset, rhsData + rhsOffset, count); // fill hole } - m_size = num; + m_storage.first().SetSize(num); Traits::assign(data[num], Element()); // terminate } return (*this); } - this_type& insert(size_type offset, const_pointer ptr, size_type count) + this_type& insert(size_type offset, const_pointer ptr, size_type count) { // insert [ptr, ptr + count) at offset - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (ptr != 0 && ptr >= data && (data + m_size) > ptr) + pointer data = m_storage.first().GetData(); + if (ptr != nullptr && ptr >= data && (data + size()) > ptr) { - return insert(offset, *this, ptr - data, count); // substring + return insert(offset, *this, ptr - data, count); // substring } - AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - AZSTD_CONTAINER_ASSERT(npos - m_size > count, "Result is too long"); - size_type num = m_size + count; + AZSTD_CONTAINER_ASSERT(size() >= offset, "Invalid offset"); + AZSTD_CONTAINER_ASSERT(npos - size() > count, "Result is too long"); + size_type num = size() + count; if (count > 0 && grow(num)) { // make room and insert new stuff - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - Traits::move(data + offset + count /*, m_capacity - offset - count*/, data + offset, m_size - offset); // empty out hole - Traits::copy(data + offset /*, m_capacity - offset*/, ptr, count); // fill hole - m_size = num; + data = m_storage.first().GetData(); + Traits::move(data + offset + count, data + offset, size() - offset); // empty out hole + Traits::copy(data + offset, ptr, count); // fill hole + m_storage.first().SetSize(num); Traits::assign(data[num], Element()); // terminate } return *this; } - inline this_type& insert(size_type offset, const_pointer ptr) { return insert(offset, ptr, Traits::length(ptr)); } + inline this_type& insert(size_type offset, const_pointer ptr) { return insert(offset, ptr, Traits::length(ptr)); } this_type& insert(size_type offset, size_type count, Element ch) { // insert count * ch at offset - AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - AZSTD_CONTAINER_ASSERT(npos - m_size > count, "Result is too long"); - size_type num = m_size + count; + AZSTD_CONTAINER_ASSERT(size() >= offset, "Invalid offset"); + AZSTD_CONTAINER_ASSERT(npos - size() > count, "Result is too long"); + size_type num = size() + count; if (count > 0 && grow(num)) { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); // make room and insert new stuff - Traits::move(data + offset + count /*, m_capacity - offset - count*/, data + offset, m_size - offset); // empty out hole - if (count == 1) - { - Traits::assign(*(data + offset), ch); - } - else - { - Traits::assign(data + offset, count, ch); - } - m_size = num; + Traits::move(data + offset + count, data + offset, size() - offset); // empty out hole + Traits::assign(data + offset, count, ch); + m_storage.first().SetSize(num); Traits::assign(data[num], Element()); // terminate } return *this; } - inline iterator insert(const_iterator insertPos) { return insert(insertPos, Element()); } + inline iterator insert(const_iterator insertPos) { return insert(insertPos, Element()); } iterator insert(const_iterator insertPos, Element ch) { @@ -479,54 +516,89 @@ namespace AZStd const_pointer insertPosPtr = insertPos; #endif - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + const_pointer data = m_storage.first().GetData(); size_type offset = insertPosPtr - data; insert(offset, 1, ch); return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, data + offset)); } - void insert(const_iterator insertPos, size_type count, Element ch) + iterator insert(const_iterator insertPos, size_type count, Element ch) { // insert count * elem at insertPos #ifdef AZSTD_HAS_CHECKED_ITERATORS const_pointer insertPosPtr = insertPos.get_iterator(); #else const_pointer insertPosPtr = insertPos; #endif - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); size_type offset = insertPosPtr - data; insert(offset, count, ch); + return begin() + offset; } - template - inline void insert(const_iterator insertPos, InputIterator first, InputIterator last) + template + auto insert(const_iterator insertPos, InputIt first, InputIt last) + -> enable_if_t && !is_convertible_v, iterator> { // insert [_First, _Last) at _Where - insert_iter(insertPos, first, last, is_integral()); - } + size_type insertOffset = AZStd::distance(cbegin(), insertPos); + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + insert(insertOffset, AZStd::to_address(first), AZStd::distance(first, last)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be inserted one by one into the buffer + size_type count = AZStd::distance(first, last); + size_type oldSize = size(); + size_type newSize = oldSize + count; + if (grow(newSize)) + { + pointer buffer = m_storage.first().GetData(); + Traits::copy_backward(buffer + insertOffset + count, buffer + insertOffset, oldSize - insertOffset); // empty out hole + for (size_t updateIndex = insertOffset; first != last; ++first, ++updateIndex) + { + Traits::assign(buffer[updateIndex], static_cast(*first)); + } + m_storage.first().SetSize(newSize); + Traits::assign(buffer[newSize], Element()); // terminate + } + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_string inputCopy; + for (; first != last; ++first) + { + inputCopy.push_back(static_cast(*first)); + } - inline void insert(const_iterator insertPos, const_pointer first, const_pointer last) - { // insert [first, last) at insertPos, const pointers - replace(insertPos, insertPos, first, last); + insert(insertOffset, inputCopy.c_str(), inputCopy.size()); + } + return begin() + insertOffset; } - this_type& erase(size_type offset = 0, size_type count = npos) { // erase elements [offset, offset + count) - AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - if (m_size - offset < count) + AZSTD_CONTAINER_ASSERT(size() >= offset, "Invalid offset"); + if (size() - offset < count) { - count = m_size - offset; // trim count + count = size() - offset; // trim count } if (count > 0) { // move elements down - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); #ifdef AZSTD_HAS_CHECKED_ITERATORS orphan_range(data + offset, data + offset + count); #endif - Traits::move(data + offset /*, m_capacity - offset*/, data + offset + count, m_size - offset - count); - m_size = m_size - count; - Traits::assign(data[m_size], Element()); // terminate - } + Traits::move(data + offset, data + offset + count, size() - offset - count); + m_storage.first().SetSize(size() - count); + Traits::assign(data[size()], Element()); // terminate + } return *this; } @@ -538,10 +610,10 @@ namespace AZStd const_pointer erasePtr = erasePos; #endif // erase element at insertPos - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + const_pointer data = m_storage.first().GetData(); size_type count = erasePtr - data; erase(count, 1); - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + data = m_storage.first().GetData(); return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, data + count)); } @@ -554,159 +626,152 @@ namespace AZStd const_pointer firstPtr = first; const_pointer lastPtr = last; #endif - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); size_type count = firstPtr - data; erase(count, lastPtr - firstPtr); - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + data = m_storage.first().GetData(); return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, data + count)); } - inline void clear() { erase(begin(), end()); } - inline this_type& replace(size_type offset, size_type count, const this_type& rhs) + inline void clear() { erase(begin(), end()); } + this_type& replace(size_type offset, size_type count, const this_type& rhs) { - // replace [offset, offset + count) with rhs - return replace(offset, count, rhs, 0, npos); + return replace(offset, count, rhs.c_str(), rhs.size()); } this_type& replace(size_type offset, size_type count, const this_type& rhs, size_type rhsOffset, size_type rhsCount) { - // replace [offset, offset + count) with rhs [rhsOffset, rhsOffset + rhsCount) - AZSTD_CONTAINER_ASSERT(m_size >= offset && rhs.m_size >= rhsOffset, "Invalid offsets"); - if (m_size - offset < count) - { - count = m_size - offset; // trim count to size - } - size_type num = rhs.m_size - rhsOffset; - if (num < rhsCount) - { - rhsCount = num; // trim rhsCount to size - } - AZSTD_CONTAINER_ASSERT(npos - rhsCount > m_size - count, "Result is too long"); - - size_type nm = m_size - count - offset; // length of preserved tail - size_type newSize = m_size + rhsCount - count; - if (m_size < newSize) - { - grow(newSize); - } - - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - -#ifdef AZSTD_HAS_CHECKED_ITERATORS - orphan_range(data + offset, data + offset + count); -#endif - if (this != &rhs) - { // no overlap, just move down and copy in new stuff - Traits::move(data + offset + rhsCount /*, m_capacity - offset - rhsCount*/, data + offset + count, nm); // empty hole - Traits::copy(data + offset /*, m_capacity - offset*/, rhsData + rhsOffset, rhsCount); // fill hole - } - else if (rhsCount <= count) - { // hole doesn't get larger, just copy in substring - Traits::move(data + offset /*, m_capacity - offset*/, data + rhsOffset, rhsCount); // fill hole - Traits::move(data + offset + rhsCount /*, m_capacity - offset - rhsCount*/, data + offset + count, nm); // move tail down - } - else if (rhsOffset <= offset) - { // hole gets larger, substring begins before hole - Traits::move(data + offset + rhsCount /*, m_capacity - offset - rhsCount*/, data + offset + count, nm); // move tail down - Traits::move(data + offset /*, m_capacity - offset*/, data + rhsOffset, rhsCount); // fill hole - } - else if (offset + count <= rhsOffset) - { // hole gets larger, substring begins after hole - Traits::move(data + offset + rhsCount /*, m_capacity - offset - rhsCount*/, data + offset + count, nm); // move tail down - Traits::move(data + offset /*, m_capacity - offset*/, data + (rhsOffset + rhsCount - count), rhsCount); // fill hole - } - else - { // hole gets larger, substring begins in hole - Traits::move(data + offset /*, m_capacity - offset*/, data + rhsOffset, count); // fill old hole - Traits::move(data + offset + rhsCount /*, m_capacity - offset - rhsCount*/, data + offset + count, nm); // move tail down - Traits::move(data + offset + count /*, m_capacity - offset - count*/, data + rhsOffset + rhsCount, rhsCount - count); // fill rest of new hole - } - - m_size = newSize; - Traits::assign(data[newSize], Element()); // terminate - return (*this); + return replace(offset, count, rhs.c_str() + rhsOffset, AZStd::min(rhsCount, rhs.size() - rhsOffset)); } this_type& replace(size_type offset, size_type count, const_pointer ptr, size_type ptrCount) { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; // replace [offset, offset + count) with [ptr, ptr + ptrCount) - if (ptr != 0 && ptr >= data && (data + m_size) > ptr) - { - return (replace(offset, count, *this, ptr - data, ptrCount)); // substring, replace carefully - } - AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - if (m_size - offset < count) - { - count = m_size - offset; // trim _N0 to size - } - AZSTD_CONTAINER_ASSERT(npos - ptrCount > m_size - count, "Result too long"); + AZSTD_CONTAINER_ASSERT(size() >= offset, "Invalid offset"); + // Make sure count is within is no larger than the distance from the offset + // to the end of this string + count = AZStd::min(count, size() - offset); -#ifdef AZSTD_HAS_CHECKED_ITERATORS - orphan_range(data + offset, data + offset + count); -#endif - size_type nm = m_size - count - offset; - if (ptrCount < count) + size_type newSize = size() + ptrCount - count; + size_type charsAfterCountToMove = size() - count - offset; + pointer inputStringCopy{}; + + if (pointer thisBuffer = m_storage.first().GetData(); + (ptr >= thisBuffer && ptr < thisBuffer + size()) + || (ptr + ptrCount > thisBuffer && ptr + ptrCount <= thisBuffer + size())) { - Traits::move(data + offset + ptrCount, data + offset + count, nm); // smaller hole, move tail up - } - size_type num = m_size + ptrCount - count; - if ((0 < ptrCount || 0 < count) && grow(num)) - { - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - // make room and rearrange - if (count < ptrCount) + // Overlap checks for tring needs if the input pointer is anywhere within the string + // even if it is outside of the range of [offset, offset + count) as a growing + // the string buffer could cause a realloc to occur + if (!fits_in_capacity(newSize)) { - Traits::move(data + offset + ptrCount /*, m_capacity - offset - ptrCount*/, data + offset + count, nm); // move tail down + // If the input string is a sub-string and it would cause + // this string to need to re-allocated as it doesn't fit in the capacity + // Then the input string is needs to be copied into a local buffer + inputStringCopy = reinterpret_cast(get_allocator().allocate(ptrCount * sizeof(value_type), alignof(value_type))); + Traits::copy(inputStringCopy, ptr, ptrCount); + // Updated the input string pointer to point to the local buffer + ptr = inputStringCopy; + // Now this string buffer can now be safely resized and the non-overlapping string logic below can be used + } + else + { + // overlapping string in-place logic + // Ex. this = "ABCDEFG", offset = 1, count=4 + // substring is "CDE" + // The text from offset 1 for 4 chars "BCDE": should be replaced with "CDE" + // making a whole for the bytes results in output = "ABCDFG" + // Afterwards output = "ACDEFG" + // The input string overlaps with this string in this case + // So the string is copied piecewise + if (ptrCount <= count) + { // hole doesn't get larger, just copy in substring + Traits::move(thisBuffer + offset, ptr, ptrCount); // fill hole + Traits::copy(thisBuffer + offset + ptrCount, thisBuffer + offset + count, charsAfterCountToMove); // move tail down + } + else + { + if (ptr <= thisBuffer + offset) + { // hole gets larger, substring begins before hole + Traits::copy_backward(thisBuffer + offset + ptrCount, thisBuffer + offset + count, charsAfterCountToMove); // move tail down + Traits::copy(thisBuffer + offset, ptr, ptrCount); // fill hole + } + else if (thisBuffer + offset + count <= ptr) + { // hole gets larger, substring begins after hole + Traits::copy_backward(thisBuffer + offset + ptrCount, thisBuffer + offset + count, charsAfterCountToMove); // move tail down + Traits::copy(thisBuffer + offset, ptr + (ptrCount - count), ptrCount); // fill hole + } + else + { // hole gets larger, substring begins in hole + Traits::copy(thisBuffer + offset, ptr, count); // fill old hole + Traits::copy_backward(thisBuffer + offset + ptrCount, thisBuffer + offset + count, charsAfterCountToMove); // move tail down + Traits::copy(thisBuffer + offset + count, ptr + ptrCount, ptrCount - count); // fill rest of new hole + } + } + m_storage.first().SetSize(newSize); + Traits::assign(thisBuffer[newSize], Element()); // terminate + return *this; + } + } + + // input string doesn't overlap, so this string can be re-allocated safely + if (grow(newSize)) + { + // Need to regrab the memory address for the storage buffer + // in case the grow re-allocated memory + pointer thisBuffer = m_storage.first().GetData(); + if (count != ptrCount) + { + Traits::move(thisBuffer + offset + ptrCount, thisBuffer + offset + count, charsAfterCountToMove); } if (ptrCount > 0) { - Traits::copy(data + offset /*, m_capacity - offset*/, ptr, ptrCount); // fill hole + // Copy bytes up to the minimum of this string count and input string count + Traits::copy(thisBuffer + offset, ptr, ptrCount); } - - m_size = num; - Traits::assign(data[num], Element()); // terminate + // input string doesn't overlap, so this string can be re-allocated safely + m_storage.first().SetSize(newSize); + Traits::assign(thisBuffer[newSize], Element()); // terminate } + + // If a local string was allocated, then de-allocate its memory + if (inputStringCopy != nullptr) + { + get_allocator().deallocate(inputStringCopy, 0, alignof(value_type)); + } + return *this; } inline this_type& replace(size_type offset, size_type count, const_pointer ptr) { return replace(offset, count, ptr, Traits::length(ptr)); } this_type& replace(size_type offset, size_type count, size_type num, Element ch) { // replace [offset, offset + count) with num * ch - AZSTD_CONTAINER_ASSERT(m_size > offset, "Invalid offset"); - if (m_size - offset < count) + AZSTD_CONTAINER_ASSERT(size() > offset, "Invalid offset"); + if (size() - offset < count) { - count = m_size - offset; // trim count to size + count = size() - offset; // trim count to size } - AZSTD_CONTAINER_ASSERT(npos - num > m_size - count, "Result is too long"); - size_type nm = m_size - count - offset; + AZSTD_CONTAINER_ASSERT(npos - num > size() - count, "Result is too long"); + size_type nm = size() - count - offset; - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); #ifdef AZSTD_HAS_CHECKED_ITERATORS orphan_range(data + offset, data + offset + count); #endif if (num < count) { - Traits::move(data + offset + num /*, m_capacity - offset - num*/, data + offset + count, nm); // smaller hole, move tail up + Traits::move(data + offset + num, data + offset + count, nm); // smaller hole, move tail up } - size_type numToGrow = m_size + num - count; + size_type numToGrow = size() + num - count; if ((0 < num || 0 < count) && grow(numToGrow)) { // make room and rearrange - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + data = m_storage.first().GetData(); if (count < num) { - Traits::move(data + offset + num /*, m_capacity - offset - num*/, data + offset + count, nm); // move tail down + Traits::move(data + offset + num, data + offset + count, nm); // move tail down } - if (count == 1) - { - Traits::assign(*(data + offset), ch); - } - else - { - Traits::assign(data + offset, num, ch); - } - m_size = numToGrow; + Traits::assign(data + offset, num, ch); + m_storage.first().SetSize(numToGrow); Traits::assign(data[numToGrow], Element()); // terminate } return *this; @@ -722,7 +787,7 @@ namespace AZStd const_pointer firstPtr = first; const_pointer lastPtr = last; #endif - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); return replace(firstPtr - data, lastPtr - firstPtr, rhs); } @@ -735,7 +800,7 @@ namespace AZStd const_pointer firstPtr = first; const_pointer lastPtr = last; #endif - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); return replace(firstPtr - data, lastPtr - firstPtr, ptr, count); } @@ -748,7 +813,7 @@ namespace AZStd const_pointer firstPtr = first; const_pointer lastPtr = last; #endif - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); return replace(firstPtr - data, lastPtr - firstPtr, ptr); } @@ -761,113 +826,133 @@ namespace AZStd const_pointer firstPtr = first; const_pointer lastPtr = last; #endif - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); return replace(firstPtr - data, lastPtr - firstPtr, count, ch); } - template - inline this_type& replace(const_iterator first, const_iterator last, InputIterator first2, InputIterator last2) - { // replace [first, last) with [first2,last2) - return replace_iter(first, last, first2, last2, is_integral()); - } - - this_type& replace(const_iterator first, const_iterator last, const_pointer first2, const_pointer last2) + template + inline auto replace(const_iterator first, const_iterator last, InputIt replaceFirst, InputIt replaceLast) + -> enable_if_t && !is_convertible_v, this_type&> { -#ifdef AZSTD_HAS_CHECKED_ITERATORS - const_pointer first1 = first.get_iterator(); - const_pointer last1 = last.get_iterator(); -#else - const_pointer first1 = first; - const_pointer last1 = last; -#endif - // replace [first, last) with [first2, last2), const pointers - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (first2 == last2) + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) { - erase(first1 - data, last1 - first1); + return replace(first, last, AZStd::to_address(replaceFirst), AZStd::distance(replaceFirst, replaceLast)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be appended one by one into the buffer + + size_type insertOffset = AZStd::distance(cbegin(), first); + size_type postInsertOffset = AZStd::distance(cbegin(), last); + size_type count = AZStd::distance(replaceFirst, replaceLast); + size_type oldSize = size(); + size_type newSize = oldSize + count - AZStd::distance(first, last); + if (grow(newSize)) + { + pointer buffer = data(); + Traits::move(first + count, last, oldSize - postInsertOffset); // empty out hole + for (size_t updateIndex = insertOffset; replaceFirst != replaceLast; ++replaceFirst, ++updateIndex) + { + Traits::assign(buffer[updateIndex], static_cast(*replaceFirst)); + } + m_storage.first().SetSize(newSize); + Traits::assign(buffer[newSize], Element()); // terminate + } + return *this; } else { - replace(first1 - data, last1 - first1, &*first2, last2 - first2); + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_string inputCopy; + for (; replaceFirst != replaceLast; ++replaceFirst) + { + inputCopy.push_back(static_cast(*replaceFirst)); + } + + return replace(first, last, inputCopy.c_str(), inputCopy.size()); } - return *this; } inline reference at(size_type offset) { // subscript mutable sequence with checking - AZSTD_CONTAINER_ASSERT(m_size > offset, "Invalid offset"); - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + AZSTD_CONTAINER_ASSERT(size() > offset, "Invalid offset"); + pointer data = m_storage.first().GetData(); return data[offset]; } inline const_reference at(size_type offset) const { // subscript nonmutable sequence with checking - AZSTD_CONTAINER_ASSERT(m_size > offset, "Invalid offset"); - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + AZSTD_CONTAINER_ASSERT(size() > offset, "Invalid offset"); + const_pointer data = m_storage.first().GetData(); return data[offset]; } inline reference operator[](size_type offset) { // subscript mutable sequence with checking - AZSTD_CONTAINER_ASSERT(m_size > offset, "Invalid offset"); - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + AZSTD_CONTAINER_ASSERT(size() > offset, "Invalid offset"); + pointer data = m_storage.first().GetData(); return data[offset]; } inline const_reference operator[](size_type offset) const { // subscript nonmutable sequence with checking - AZSTD_CONTAINER_ASSERT(m_size > offset, "Invalid offset"); - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + AZSTD_CONTAINER_ASSERT(size() > offset, "Invalid offset"); + const_pointer data = m_storage.first().GetData(); return data[offset]; } inline reference front() { - AZSTD_CONTAINER_ASSERT(m_size != 0, "AZStd::string::front - string is empty!"); - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + AZSTD_CONTAINER_ASSERT(size() != 0, "AZStd::string::front - string is empty!"); + pointer data = m_storage.first().GetData(); return data[0]; } inline const_reference front() const { - AZSTD_CONTAINER_ASSERT(m_size != 0, "AZStd::string::front - string is empty!"); - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + AZSTD_CONTAINER_ASSERT(size() != 0, "AZStd::string::front - string is empty!"); + const_pointer data = m_storage.first().GetData(); return data[0]; } inline reference back() { - AZSTD_CONTAINER_ASSERT(m_size != 0, "AZStd::string::back - string is empty!"); - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - return data[m_size - 1]; + AZSTD_CONTAINER_ASSERT(size() != 0, "AZStd::string::back - string is empty!"); + pointer data = m_storage.first().GetData(); + return data[size() - 1]; } inline const_reference back() const { - AZSTD_CONTAINER_ASSERT(m_size != 0, "AZStd::string::back - string is empty!"); - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - return data[m_size - 1]; + AZSTD_CONTAINER_ASSERT(size() != 0, "AZStd::string::back - string is empty!"); + const_pointer data = m_storage.first().GetData(); + return data[size() - 1]; } inline void push_back(Element ch) { - const_pointer end = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - end += m_size; + const_pointer end = data(); + end += size(); insert(end, ch); } - inline const_pointer c_str() const { return (SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer); } - inline size_type length() const { return m_size; } - inline size_type size() const { return m_size; } - inline size_type capacity() const { return m_capacity; } + inline const_pointer c_str() const { return (data()); } + inline size_type length() const { return m_storage.first().GetSize(); } + inline size_type size() const { return m_storage.first().GetSize(); } + inline size_type capacity() const { return m_storage.first().GetCapacity(); } inline size_type max_size() const { // return maximum possible length of sequence - return AZStd::allocator_traits::max_size(m_allocator) / sizeof(value_type); + return AZStd::allocator_traits::max_size(m_storage.second()) / sizeof(value_type); } inline void resize(size_type newSize) @@ -877,58 +962,58 @@ namespace AZStd inline void resize_no_construct(size_type newSize) { - if (newSize <= m_size) + if (newSize <= size()) { erase(newSize); } else { reserve(newSize); - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - m_size = newSize; - Traits::assign(data[m_size], Element()); // terminate + pointer data = m_storage.first().GetData(); + m_storage.first().SetSize(newSize); + Traits::assign(data[newSize], Element()); // terminate } } inline void resize(size_type newSize, Element ch) { // determine new length, padding with ch elements as needed - if (newSize <= m_size) + if (newSize <= size()) { erase(newSize); } else { - append(newSize - m_size, ch); + append(newSize - size(), ch); } } void reserve(size_type newCapacity = 0) { // determine new minimum length of allocated storage - if (m_size <= newCapacity && m_capacity != newCapacity) + if (size() <= newCapacity && capacity() != newCapacity) { // change reservation - size_type size = m_size; + size_type curSize = size(); if (grow(newCapacity)) { - m_size = size; - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - Traits::assign(data[size], Element()); // terminate + m_storage.first().SetSize(curSize); + pointer data = m_storage.first().GetData(); + Traits::assign(data[curSize], Element()); // terminate } } } - inline bool empty() const { return (m_size == 0); } - size_type copy(Element* dest /*, size_type destSize */, size_type count, size_type offset = 0) const + inline bool empty() const { return size() == 0; } + size_type copy(Element* dest, size_type count, size_type offset = 0) const { // copy [offset, offset + count) to [dest, dest + count) // assume there is enough space in _Ptr - AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - if (m_size - offset < count) + AZSTD_CONTAINER_ASSERT(size() >= offset, "Invalid offset"); + if (size() - offset < count) { - count = m_size - offset; + count = size() - offset; } - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - Traits::copy(dest /*, destSize*/, data + offset, count); + const_pointer data = m_storage.first().GetData(); + Traits::copy(dest, data + offset, count); return count; } @@ -939,19 +1024,10 @@ namespace AZStd return; } - if (m_allocator == rhs.m_allocator) + if (m_storage.second() == rhs.m_storage.second()) { - // same allocator, swap control information -#ifdef AZSTD_HAS_CHECKED_ITERATORS - swap_all(rhs); -#endif - Element temp[SSO_BUF_SIZE]; - ::memcpy(temp, rhs.m_buffer, sizeof(m_buffer)); - ::memcpy(rhs.m_buffer, m_buffer, sizeof(m_buffer)); - ::memcpy(m_buffer, temp, sizeof(m_buffer)); - - AZStd::swap(m_size, rhs.m_size); - AZStd::swap(m_capacity, rhs.m_capacity); + // same allocator, swap storage + m_storage.first().swap(rhs.m_storage.first()); } else { @@ -980,174 +1056,76 @@ namespace AZStd inline size_type find(const this_type& rhs, size_type offset = 0) const { - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return find(rhsData, offset, rhs.m_size); + const_pointer rhsData = rhs.data(); + return find(rhsData, offset, rhs.size()); } size_type find(const_pointer ptr, size_type offset, size_type count) const { - AZ_Assert(ptr != NULL, "Invalid input!"); - - // look for [ptr, ptr + count) beginning at or after offset - if (count == 0 && offset <= m_size) - { - return offset; // null string always matches (if inside string) - } - size_type nm; - if (offset < m_size && count <= (nm = m_size - offset)) - { // room for match, look for it - const_pointer uptr, vptr; - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - for (nm -= count - 1, vptr = data + offset; (uptr = Traits::find(vptr, nm, *ptr)) != 0; nm -= uptr - vptr + 1, vptr = uptr + 1) - { - if (Traits::compare(uptr, ptr, count) == 0) - { - return (uptr - data); // found a match - } - } - } - - return (npos); // no match + return StringInternal::find(data(), size(), ptr, offset, count, npos); } inline size_type find(const_pointer ptr, size_type offset = 0) const { return find(ptr, offset, Traits::length(ptr)); } inline size_type find(Element ch, size_type offset = 0) const { return find((const_pointer) & ch, offset, 1); } inline size_type rfind(const this_type& rhs, size_type offset = npos) const { - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return rfind(rhsData, offset, rhs.m_size); + const_pointer rhsData = rhs.data(); + return rfind(rhsData, offset, rhs.size()); } size_type rfind(const_pointer ptr, size_type offset, size_type count) const - { // look for [ptr, ptr + count) beginning before offset - if (count == 0) - { - return (offset < m_size ? offset : m_size); // null always matches - } - if (count <= m_size) - { // room for match, look for it - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - const_pointer uptr = data + (offset < m_size - count ? offset : m_size - count); - for (;; --uptr) - { - if (Traits::eq(*uptr, *ptr) && Traits::compare(uptr, ptr, count) == 0) - { - return (uptr - data); // found a match - } - else if (uptr == data) - { - break; // at beginning, no more chance for match - } - } - } - - return npos; // no match + { + return StringInternal::rfind(data(), size(), ptr, offset, count, npos); } inline size_type rfind(const_pointer ptr, size_type offset = npos) const { return rfind(ptr, offset, Traits::length(ptr)); } inline size_type rfind(Element ch, size_type offset = npos) const { return rfind((const_pointer) & ch, offset, 1); } inline size_type find_first_of(const this_type& rhs, size_type offset = 0) const { - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return find_first_of(rhsData, offset, rhs.m_size); + const_pointer rhsData = rhs.data(); + return find_first_of(rhsData, offset, rhs.size()); } size_type find_first_of(const_pointer ptr, size_type offset, size_type count) const - { // look for one of [ptr, ptr + count) at or after offset - if (0 < count && offset < m_size) - { // room for match, look for it - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - const Element* const vptr = data + m_size; - for (const_pointer uptr = data + offset; uptr < vptr; ++uptr) - { - if (Traits::find(ptr, count, *uptr) != 0) - { - return uptr - data; // found a match - } - } - } - return npos; // no match + { + return StringInternal::find_first_of(data(), size(), ptr, offset, count, npos); } inline size_type find_first_of(const_pointer ptr, size_type offset = 0) const { return find_first_of(ptr, offset, Traits::length(ptr)); } inline size_type find_first_of(Element ch, size_type offset = 0) const { return find((const_pointer) & ch, offset, 1); } inline size_type find_last_of(const this_type& rhs, size_type offset = npos) const { - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return find_last_of(rhsData, offset, rhs.m_size); + const_pointer rhsData = rhs.data(); + return find_last_of(rhsData, offset, rhs.size()); } size_type find_last_of(const_pointer ptr, size_type offset, size_type count) const - { // look for one of [ptr, ptr + count) before offset - if (0 < count && 0 < m_size) - { - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - for (const_pointer uptr = data + (offset < m_size ? offset : m_size - 1);; --uptr) - { - if (Traits::find(ptr, count, *uptr) != 0) - { - return uptr - data; // found a match - } - else if (uptr == data) - { - break; // at beginning, no more chance for match - } - } - } - - return npos; // no match + { + return StringInternal::find_last_of(data(), size(), ptr, offset, count, npos); } inline size_type find_last_of(const_pointer ptr, size_type offset = npos) const { return find_last_of(ptr, offset, Traits::length(ptr)); } inline size_type find_last_of(Element ch, size_type offset = npos) const { return rfind((const_pointer) & ch, offset, 1); } inline size_type find_first_not_of(const this_type& rhs, size_type offset = 0) const { // look for none of rhs at or after offset - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return find_first_not_of(rhsData, offset, rhs.m_size); + const_pointer rhsData = rhs.data(); + return find_first_not_of(rhsData, offset, rhs.size()); } size_type find_first_not_of(const_pointer ptr, size_type offset, size_type count) const { - // look for none of [ptr, ptr + count) at or after offset - if (offset < m_size) - { // room for match, look for it - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - const Element* const vptr = data + m_size; - for (const_pointer uptr = data + offset; uptr < vptr; ++uptr) - { - if (Traits::find(ptr, count, *uptr) == 0) - { - return uptr - data; - } - } - } - return npos; + return StringInternal::find_first_not_of(data(), size(), ptr, offset, count, npos); } inline size_type find_first_not_of(const_pointer ptr, size_type offset = 0) const { return find_first_not_of(ptr, offset, Traits::length(ptr)); } inline size_type find_first_not_of(Element ch, size_type offset = 0) const { return find_first_not_of((const_pointer) & ch, offset, 1); } inline size_type find_last_not_of(const this_type& rhs, size_type offset = npos) const { // look for none of rhs before offset - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return find_last_not_of(rhsData, offset, rhs.m_size); + const_pointer rhsData = rhs.data(); + return find_last_not_of(rhsData, offset, rhs.size()); } size_type find_last_not_of(const_pointer ptr, size_type offset, size_type count) const - { // look for none of [ptr, ptr + count) before offset - if (0 < m_size) - { - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - for (const_pointer uptr = data + (offset < m_size ? offset : m_size - 1);; --uptr) - { - if (Traits::find(ptr, count, *uptr) == 0) - { - return uptr - data; - } - else if (uptr == data) - { - break; - } - } - } - return npos; + { + return StringInternal::find_last_not_of(data(), size(), ptr, offset, count, npos); } inline size_type find_last_not_of(const_pointer ptr, size_type offset = npos) const { return find_last_not_of(ptr, offset, Traits::length(ptr)); } @@ -1161,8 +1139,8 @@ namespace AZStd inline int compare(const this_type& rhs) const { - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return compare(0, m_size, rhsData, rhs.m_size); + const_pointer rhsData = rhs.data(); + return compare(0, size(), rhsData, rhs.size()); } inline int compare(size_type offset, size_type count, const this_type& rhs) const @@ -1173,26 +1151,26 @@ namespace AZStd int compare(size_type offset, size_type count, const this_type& rhs, size_type rhsOffset, size_type rhsCount) const { // compare [offset, offset + count) with rhs [rhsOffset, rhsOffset + rhsCount) - AZSTD_CONTAINER_ASSERT(rhs.m_size >= rhsOffset, "Invalid offset"); - if (rhs.m_size - rhsOffset < rhsCount) + AZSTD_CONTAINER_ASSERT(rhs.size() >= rhsOffset, "Invalid offset"); + if (rhs.size() - rhsOffset < rhsCount) { - rhsCount = rhs.m_size - rhsOffset; // trim rhsCount to size + rhsCount = rhs.size() - rhsOffset; // trim rhsCount to size } - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; + const_pointer rhsData = rhs.data(); return compare(offset, count, rhsData + rhsOffset, rhsCount); } - inline int compare(const_pointer ptr) const { return compare(0, m_size, ptr, Traits::length(ptr)); } + inline int compare(const_pointer ptr) const { return compare(0, size(), ptr, Traits::length(ptr)); } inline int compare(size_type offset, size_type count, const_pointer ptr) const { return compare(offset, count, ptr, Traits::length(ptr)); } int compare(size_type offset, size_type count, const_pointer ptr, size_type ptrCount) const { // compare [offset, offset + _N0) with [_Ptr, _Ptr + _Count) - AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - if (m_size - offset < count) + AZSTD_CONTAINER_ASSERT(size() >= offset, "Invalid offset"); + if (size() - offset < count) { - count = m_size - offset; // trim count to size + count = size() - offset; // trim count to size } - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + const_pointer data = m_storage.first().GetData(); size_type ans = Traits::compare(data + offset, ptr, count < ptrCount ? count : ptrCount); return (ans != 0 ? (int)ans : count < ptrCount ? -1 : count == ptrCount ? 0 : +1); } @@ -1231,11 +1209,11 @@ namespace AZStd inline void pop_back() { - if (m_size > 0) + if (!empty()) { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - --m_size; - Traits::assign(data[m_size], Element()); // terminate + pointer data = m_storage.first().GetData(); + m_storage.first().SetSize(m_storage.first().GetSize() - 1); + Traits::assign(data[size()], Element()); // terminate } } @@ -1245,39 +1223,35 @@ namespace AZStd * @{ */ /// TR1 Extension. Return pointer to the vector data. The vector data is guaranteed to be stored as an array. - inline pointer data() { return (SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer); } - inline const_pointer data() const { return (SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer); } + inline pointer data() { return m_storage.first().GetData(); } + inline const_pointer data() const { return m_storage.first().GetData(); } /// /// The only difference from the standard is that we return the allocator instance, not a copy. - inline allocator_type& get_allocator() { return m_allocator; } - inline const allocator_type& get_allocator() const { return m_allocator; } + inline allocator_type& get_allocator() { return m_storage.second(); } + inline const allocator_type& get_allocator() const { return m_storage.second(); } /// Set the vector allocator. If different than then current all elements will be reallocated. void set_allocator(const allocator_type& allocator) { - if (m_allocator != allocator) + if (m_storage.second() != allocator) { - if (m_size > 0 && SSO_BUF_SIZE <= m_capacity) + if (!empty() && !m_storage.first().ShortStringOptimizationActive()) { allocator_type newAllocator = allocator; - pointer data = m_data; + pointer data = m_storage.first().GetData(); - pointer newData = reinterpret_cast(newAllocator.allocate(sizeof(node_type) * (m_capacity + 1), alignment_of::value)); + pointer newData = reinterpret_cast(newAllocator.allocate(sizeof(node_type) * (capacity() + 1), alignof(node_type))); - Traits::copy(newData, data, m_size + 1); // copy elements and terminator + Traits::copy(newData, data, size() + 1); // copy elements and terminator // Free memory (if needed). deallocate_memory(data, 0, typename allocator_type::allow_memory_leaks()); - m_allocator = newAllocator; - -#ifdef AZSTD_HAS_CHECKED_ITERATORS - orphan_all(); -#endif + m_storage.second() = newAllocator; } else { - m_allocator = allocator; + m_storage.second() = allocator; } } } @@ -1296,12 +1270,12 @@ namespace AZStd #else pointer iterPtr = iter; #endif - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (iterPtr < data || iterPtr > (data + m_size)) + const_pointer data = m_storage.first().GetData(); + if (iterPtr < data || iterPtr > (data + size())) { return isf_none; } - else if (iterPtr == (data + m_size)) + else if (iterPtr == (data + size())) { return isf_valid; } @@ -1316,12 +1290,12 @@ namespace AZStd #else const_pointer iterPtr = iter; #endif - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (iterPtr < data || iterPtr > (data + m_size)) + const_pointer data = m_storage.first().GetData(); + if (iterPtr < data || iterPtr > (data + size())) { return isf_none; } - else if (iterPtr == (data + m_size)) + else if (iterPtr == (data + size())) { return isf_valid; } @@ -1337,86 +1311,74 @@ namespace AZStd * \note This function is added to the vector for consistency. In the vector case we have only one allocation, and if the allocator allows memory leaks * it can just leave deallocate function empty, which performance wise will be the same. For more complex containers this will make big difference. */ - void leak_and_reset() + void leak_and_reset() { - m_size = 0; - m_capacity = SSO_BUF_SIZE - 1; - Traits::assign(m_buffer[0], Element()); - -#ifdef AZSTD_HAS_CHECKED_ITERATORS - orphan_all(); -#endif + m_storage.first() = {}; } /** * Set the capacity, if necessary it will erase elements at the end of the container to match the new capacity. */ - void set_capacity(size_type numElements) + void set_capacity(size_type numElements) { // sets the new capacity of the vector, can be smaller than size() - if (m_capacity != numElements) + if (capacity() != numElements) { - if (numElements < SSO_BUF_SIZE) + if (numElements < ShortStringData::Capacity) { - if (m_capacity >= SSO_BUF_SIZE) + if (!m_storage.first().ShortStringOptimizationActive()) { // copy any leftovers to small buffer and deallocate - pointer ptr = m_data; - numElements = numElements < m_size ? numElements : m_size; + pointer ptr = m_storage.first().GetData(); + numElements = numElements < size() ? numElements : size(); + m_storage.first().SetCapacity(ShortStringData::Capacity); if (0 < numElements) { - Traits::copy(m_buffer /*, SSO_BUF_SIZE*/, ptr, numElements); + Traits::copy(m_storage.first().GetData(), ptr, numElements); } - deallocate_memory(ptr, 0, typename allocator_type::allow_memory_leaks()); - m_capacity = SSO_BUF_SIZE - 1; + // deallocate_memory functione examines the current + // m_storage short string optimization state was changed to true + // by the SetCapacity call above. Therefore m_storage.second().deallocate + // is used directly + m_storage.second().deallocate(ptr, 0, alignof(node_type)); } - m_size = numElements; - Traits::assign(m_buffer[numElements], Element()); // terminate + m_storage.first().SetSize(numElements); + Traits::assign(m_storage.first().GetData()[numElements], Element()); // terminate } else { size_type expandedSize = 0; - if (m_capacity >= SSO_BUF_SIZE) + if (!m_storage.first().ShortStringOptimizationActive()) { - expandedSize = m_allocator.resize(m_data, sizeof(node_type) * (numElements + 1)); + expandedSize = m_storage.second().resize(m_storage.first().GetData(), sizeof(node_type) * (numElements + 1)); // our memory managers allocate on 8+ bytes boundary and our node type should be less than that in general, otherwise // we need to take care when we compute the size on deallocate. AZ_Assert(expandedSize % sizeof(node_type) == 0, "Expanded size not a multiply of node type. This should not happen"); size_type expandedCapacity = expandedSize / sizeof(node_type); if (expandedCapacity > numElements) { - m_capacity = expandedCapacity - 1; + m_storage.first().SetCapacity(expandedCapacity - 1); return; } } - pointer newData = reinterpret_cast(m_allocator.allocate(sizeof(node_type) * (numElements + 1), alignment_of::value)); - AZSTD_CONTAINER_ASSERT(newData != 0, "AZStd::string allocation failed!"); + pointer newData = reinterpret_cast(m_storage.second().allocate(sizeof(node_type) * (numElements + 1), alignof(node_type))); + AZSTD_CONTAINER_ASSERT(newData != nullptr, "AZStd::string allocation failed!"); - size_type newSize = numElements < m_size ? numElements : m_size; - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + size_type newSize = numElements < m_storage.first().GetSize() ? numElements : m_storage.first().GetSize(); + pointer data = m_storage.first().GetData(); if (newSize > 0) { - Traits::copy(newData /*, newSize + 1*/, data, newSize); // copy existing elements - } - if (m_capacity >= SSO_BUF_SIZE) - { - deallocate_memory(m_data, expandedSize, typename allocator_type::allow_memory_leaks()); + Traits::copy(newData, data, newSize); // copy existing elements } + deallocate_memory(data, expandedSize, typename allocator_type::allow_memory_leaks()); - m_data = newData; - m_capacity = numElements; - m_size = newSize; - Traits::assign(m_data[newSize], Element()); // terminate + Traits::assign(newData[newSize], Element()); // terminate + m_storage.first().SetCapacity(numElements); + m_storage.first().SetData(newData); + m_storage.first().SetSize(newSize); } - -#ifdef AZSTD_HAS_CHECKED_ITERATORS - // when we move data in the buffer we don't really need to make invalid all iterators, but it's - // very important that we are consistent, so people don't have different behavior when they have - // short strings - orphan_all(); -#endif } } @@ -1521,9 +1483,9 @@ namespace AZStd } }; -// Clang supports compile-time check for printf-like signatures -// On MSVC, *only* if /analyze flag is enabled(defines _PREFAST_) we can also do a compile-time check -// For not affecting final release binary size, we don't use the templated version on Release configuration either + // Clang supports compile-time check for printf-like signatures + // On MSVC, *only* if /analyze flag is enabled(defines _PREFAST_) we can also do a compile-time check + // For not affecting final release binary size, we don't use the templated version on Release configuration either #if AZ_COMPILER_CLANG || defined(_PREFAST_) || defined(_RELEASE) # if AZ_COMPILER_CLANG # define FORMAT_FUNC __attribute__((format(printf, 1, 2))) @@ -1597,137 +1559,70 @@ namespace AZStd template inline basic_string(const basic_string& rhs) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) { assign(rhs.c_str()); } template inline this_type& operator=(const basic_string& rhs) { return assign(rhs.c_str()); } template - inline this_type& append(const basic_string& rhs) { return append(rhs.c_str()); } + inline this_type& append(const basic_string& rhs) { return append(rhs.c_str()); } template inline this_type& insert(size_type offset, const basic_string& rhs) { return insert(offset, rhs.c_str()); } template inline this_type& replace(size_type offset, size_type count, const basic_string& rhs) { return replace(offset, count, rhs.c_str()); } template - inline int compare(const basic_string& rhs) { return compare(rhs.c_str()); } + inline int compare(const basic_string& rhs) { return compare(rhs.c_str()); } // @} protected: - enum - { // length of internal buffer, [1, 16] - SSO_BUF_SIZE = 16 / sizeof (Element) < 1 ? 1 : 16 / sizeof(Element) - }; enum { // roundup mask for allocated buffers, [0, 15] - _ALLOC_MASK = sizeof (Element) <= 1 ? 15 : sizeof (Element) <= 2 ? 7 : sizeof (Element) <= 4 ? 3 : sizeof (Element) <= 8 ? 1 : 0 + _ALLOC_MASK = sizeof(Element) <= 1 ? 15 + : sizeof(Element) <= 2 ? 7 + : sizeof(Element) <= 4 ? 3 + : sizeof(Element) <= 8 ? 1 : 0 }; - template - inline this_type& append_iter(InputIterator count, InputIterator ch, const true_type& /* is_integral */) - { // append count * ch - return append((size_type)count, (Element)ch); - } - - template - inline void construct_iter(InputIterator count, InputIterator ch, const true_type& /* is_integral */) - { // initialize from count * ch - assign((size_type)count, (Element)ch); - } - - template - inline void construct_iter(InputIterator first, InputIterator last, const false_type& /*, const input_iterator_tag&*/) - { - // initialize from [first, last), input iterators - // \todo use insert ? - for (; first != last; ++first) - { - append((size_type)1, (Element) * first); - } - } - - - template - inline this_type& append_iter(InputIterator first, InputIterator last, const false_type& /* !is_integral */) - { // append [first, last), input iterators - return replace(end(), end(), first, last); - } - - - template - inline this_type& assign_iter(InputIterator count, InputIterator ch, const true_type&) { return assign((size_type)count, (Element)ch); } - template - inline this_type& assign_iter(InputIterator first, InputIterator last, const false_type&){ return replace(begin(), end(), first, last); } - - template - inline void insert_iter(const_iterator insertPos, InputIterator count, InputIterator ch, const true_type& /* is_integral() */) - { // insert count * ch at insertPos - insert(insertPos, (size_type)count, (Element)ch); - } - - template - inline void insert_iter(const_iterator insertPos, InputIterator first, InputIterator last, const false_type& /* is_integral() */) - { // insert [first, last) at insertPos, input iterators - replace(insertPos, insertPos, first, last); - } - - - template - inline this_type& replace_iter(const_iterator first, const_iterator last, InputIterator count, InputIterator ch, const true_type& /* is_intergral */) - { // replace [first, last) with count * ch - return replace(first, last, (size_type)count, (Element)ch); - } - - template - inline this_type& replace_iter(const_iterator first, const_iterator last, InputIterator first2, InputIterator last2, const false_type& /* !is_intergral */) - { // replace [first, last) with [first2, last2), input iterators - this_type rhs(first2, last2); - replace(first, last, rhs); - return *this; - } - void copy(size_type newSize, size_type oldLength) { size_type newCapacity = newSize | _ALLOC_MASK; - if (newCapacity / 3 < m_capacity / 2) + size_type currentCapacity = capacity(); + if (newCapacity / 3 < currentCapacity / 2) { - newCapacity = m_capacity + m_capacity / 2; // grow exponentially if possible + newCapacity = currentCapacity + currentCapacity / 2; // grow exponentially if possible } - if (newCapacity >= SSO_BUF_SIZE) + if (newCapacity >= ShortStringData::Capacity) { size_type expandedSize = 0; - if (m_capacity >= SSO_BUF_SIZE) + if (!m_storage.first().ShortStringOptimizationActive()) { - expandedSize = m_allocator.resize(m_data, sizeof(node_type) * (newCapacity + 1)); + expandedSize = m_storage.second().resize(m_storage.first().GetData(), sizeof(node_type) * (newCapacity + 1)); // our memory managers allocate on 8+ bytes boundary and our node type should be less than that in general, otherwise // we need to take care when we compute the size on deallocate. - AZ_Assert(expandedSize % sizeof(node_type) == 0, "Expanded size not a multiply of node type. This should not happen"); + AZ_Assert(expandedSize % sizeof(node_type) == 0, "Expanded size not a multiple of node type. This should not happen"); size_type expandedCapacity = expandedSize / sizeof(node_type); if (expandedCapacity > newCapacity) { - m_capacity = expandedCapacity - 1; + m_storage.first().SetCapacity(expandedCapacity - 1); return; } } - pointer newData = reinterpret_cast(m_allocator.allocate(sizeof(node_type) * (newCapacity + 1), alignment_of::value)); - AZSTD_CONTAINER_ASSERT(newData != 0, "AZStd::string allocation failed!"); + pointer newData = reinterpret_cast(m_storage.second().allocate(sizeof(node_type) * (newCapacity + 1), alignof(node_type))); + AZSTD_CONTAINER_ASSERT(newData != nullptr, "AZStd::string allocation failed!"); if (newData) { - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); if (0 < oldLength) { - Traits::copy(newData /*, newSize + 1*/, data, oldLength); // copy existing elements - } - if (m_capacity >= SSO_BUF_SIZE) - { - deallocate_memory(m_data, expandedSize, typename allocator_type::allow_memory_leaks()); + Traits::copy(newData, data, oldLength); // copy existing elements } + deallocate_memory(data, expandedSize, typename allocator_type::allow_memory_leaks()); - m_data = newData; - m_capacity = newCapacity; - Traits::assign(m_data[newSize], Element()); // terminate + Traits::assign(newData[oldLength], Element()); // terminate + m_storage.first().SetCapacity(newCapacity); + m_storage.first().SetSize(oldLength); + m_storage.first().SetData(newData); } } } @@ -1735,40 +1630,209 @@ namespace AZStd bool grow(size_type newSize) { // ensure buffer is big enough, trim to size if _Trim is true - if (m_capacity < newSize) + if (capacity() < newSize) { - copy(newSize, m_size); // reallocate to grow + copy(newSize, size()); // reallocate to grow } else if (newSize == 0) { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - m_size = 0; + pointer data = m_storage.first().GetData(); + m_storage.first().SetSize(0); Traits::assign(data[0], Element()); // terminate } return (0 < newSize); // return true only if more work to do } + bool fits_in_capacity(size_type newSize) + { + return newSize <= capacity(); + } + inline void deallocate_memory(pointer, size_type, const true_type& /* allocator::allow_memory_leaks */) {} inline void deallocate_memory(pointer data, size_type expandedSize, const false_type& /* !allocator::allow_memory_leaks */) { - if (m_capacity >= SSO_BUF_SIZE) + if (!m_storage.first().ShortStringOptimizationActive()) { - size_type byteSize = (expandedSize == 0) ? (sizeof(node_type) * (m_capacity + 1)) : expandedSize; - m_allocator.deallocate(data, byteSize, alignment_of::value); + size_type byteSize = (expandedSize == 0) ? (sizeof(node_type) * (m_storage.first().GetCapacity() + 1)) : expandedSize; + m_storage.second().deallocate(data, byteSize, alignof(node_type)); } } - union //Storage + //! Assuming 64-bit for pointer and size_t size + //! The offset and sizes of each structure are marked below + + //! dynamically allocated data + struct AllocatedStringData { - Element m_buffer[SSO_BUF_SIZE]; //< small buffer used for small string optimization - pointer m_data; //< dynamically allocated data + AllocatedStringData() + { + m_capacity = 0; + m_ssoActive = false; + } + // bit offset: 0, bits: 64 + pointer m_data{}; + + // bit offset: 64, bit: 64 + size_type m_size{}; + + // Use all but the top bit of a size_t for the string capacity + // This allows the short string optimization to be used + // with no additional space at the cost of cutting the max_size in half + // to 2^63-1 + // offset: 128, bits: 63 + size_type m_capacity : AZStd::numeric_limits::digits - 1; + + // bit offset: 191, bits: 1 + size_type m_ssoActive : 1; + + // Total size 192 bits(24 bytes) }; - size_type m_size; // current length of string - size_type m_capacity; // current storage reserved for string - allocator_type m_allocator; + static_assert(sizeof(AllocatedStringData) <= 24, "The AllocatedStringData structure" + " should be an 8-byte pointer, 8 byte size, 63-bit capacity and 1-bit SSO flag for" + " a total of 24 bytes"); + + //! small buffer used for small string optimization + struct ShortStringData + { + //! The size can be stored within 7 bits since the buffer will be no larger + //! than 23 bytes(22 characters + 1 null-terminating character) + inline static constexpr size_type BufferMaxSize = sizeof(AllocatedStringData) - sizeof(AZ::u8); + static_assert(sizeof(Element) < BufferMaxSize, "The size of Element type must be less than the size of " + " the AllocatedStringData struct in order to use it with the basic_string class"); + inline static constexpr size_type BufferCapacityPlusNull = BufferMaxSize / sizeof(Element); + + inline static constexpr size_type Capacity = BufferCapacityPlusNull - 1; + + ShortStringData() + { + // Make sure the short string buffer is null-terminated + m_buffer[0] = Element{}; + m_size = 0; + m_ssoActive = true; + } + + // bit offset: 0, bits: 184 + Element m_buffer[BufferCapacityPlusNull]; + + // Padding to make sure for Element types with a size >1 + // such as wchar_t, that the `m_size` member starts at the bit 164 + // NOTE: Uses the anonymous struct extension + // supported by MSVC, Clang and GCC + // Takes advantage of the empty base optimization + // to have the StringInternal::Padding struct + // take 0 bytes when the Element type is 1-byte type like `char` + // When C++20 support is added, this can be changed to use [[no_unique_address]] + struct + : StringInternal::Padding + { + + // bit offset: 184, bits: 7 + AZ::u8 m_size : AZStd::numeric_limits::digits - 1; + + // bit offset: 191, bits: 1 + AZ::u8 m_ssoActive : 1; + }; + // Total size 192 bits(24 bytes) + }; + + struct PointerAlignedData + { + uintptr_t m_alignedValues[sizeof(ShortStringData) / sizeof(uintptr_t)]; + }; + + static_assert(sizeof(AllocatedStringData) == sizeof(ShortStringData) && "Short string struct must be the same size" + " as the regular allocated string struct"); + + static_assert(sizeof(PointerAlignedData) == sizeof(ShortStringData) && "Pointer aligned struct must be the same size" + " as the short string struct "); + + // The top-bit in the last byte of the AllocatedStringData and ShortStringData is used to determine if the short string optimization is being used + union Storage + { + Storage() {}; + + bool ShortStringOptimizationActive() const + { + return m_shortData.m_ssoActive; + } + const_pointer GetData() const + { + return ShortStringOptimizationActive() ? m_shortData.m_buffer + : reinterpret_cast(m_shortData).m_data; + } + pointer GetData() + { + return ShortStringOptimizationActive() ? m_shortData.m_buffer + : reinterpret_cast(m_shortData).m_data; + } + void SetData(pointer address) + { + if (!ShortStringOptimizationActive()) + { + reinterpret_cast(m_shortData).m_data = address; + } + else + { + AZSTD_CONTAINER_ASSERT(false, "Programming Error: string class is invoking SetData when the Short Optimization" + " is active. Make sure SetCapacity() is invoked" + " before calling this function."); + } + } + size_type GetSize() const + { + return ShortStringOptimizationActive() ? m_shortData.m_size + : reinterpret_cast(m_shortData).m_size; + } + void SetSize(size_type size) + { + if (ShortStringOptimizationActive()) + { + m_shortData.m_size = size; + } + else + { + reinterpret_cast(m_shortData).m_size = size; + } + } + size_type GetCapacity() const + { + return ShortStringOptimizationActive() ? m_shortData.Capacity + : reinterpret_cast(m_shortData).m_capacity; + } + void SetCapacity(size_type capacity) + { + if (capacity <= ShortStringData::Capacity) + { + m_shortData.m_ssoActive = true; + } + else + { + m_shortData.m_ssoActive = false; + reinterpret_cast(m_shortData).m_capacity = capacity; + } + } + void swap(Storage& rhs) + { + // Use pointer sized swaps to swap the string storage + AZStd::aligned_storage_for_t tempStorage; + ::memcpy(&tempStorage, this, sizeof(Storage)); + ::memcpy(this, &rhs, sizeof(Storage)); + ::memcpy(&rhs, &tempStorage, sizeof(Storage)); + } + private: + ShortStringData m_shortData{}; + AllocatedStringData m_allocatedData; + PointerAlignedData m_pointerData; + }; + + AZStd::compressed_pair m_storage; + +#if defined(HAVE_BENCHMARK) + friend class Benchmark::StringBenchmarkFixture; +#endif #ifdef AZSTD_HAS_CHECKED_ITERATORS void orphan_range(pointer first, pointer last) const @@ -1809,18 +1873,7 @@ namespace AZStd }; template - const typename basic_string::size_type basic_string::npos; - - // basic_string implements a performant swap - /*template - class move_operation_category > - { - public: - typedef swap_move_tag move_cat; - };*/ - - template - inline void swap(basic_string& left, basic_string& right) + inline void swap(basic_string& left, basic_string& right) { left.swap(right); } @@ -2027,6 +2080,3 @@ namespace AZStd }; } // namespace AZStd - -#endif // AZSTD_STRING_H -#pragma once diff --git a/Code/Framework/AzCore/AzCore/std/string/string_view.h b/Code/Framework/AzCore/AzCore/std/string/string_view.h index 30e61f95ce..841044dd16 100644 --- a/Code/Framework/AzCore/AzCore/std/string/string_view.h +++ b/Code/Framework/AzCore/AzCore/std/string/string_view.h @@ -280,18 +280,36 @@ namespace AZStd static constexpr char_type* assign(char_type* dest, size_t count, char_type ch) noexcept { AZ_Assert(dest, "Invalid input!"); - for (char_type* iter = dest; count; --count, ++iter) + + if constexpr (AZStd::is_same_v) { - assign(*iter, ch); + // Use builtin_memset if available for char type + if (az_builtin_is_constant_evaluated()) + { + for (char_type* iter = dest; count; --count, ++iter) + { + assign(*iter, ch); + } + } + else + { + ::memset(dest, ch, count); + } } + else + { + for (char_type* iter = dest; count; --count, ++iter) + { + assign(*iter, ch); + } + } + return dest; } static constexpr bool eq(char_type left, char_type right) noexcept { return left == right; } static constexpr bool lt(char_type left, char_type right) noexcept { return left < right; } static constexpr int compare(const char_type* s1, const char_type* s2, size_t count) noexcept { - // Regression in VS2017 15.8 and 15.9 where __builtin_memcmp fails in valid checks in constexpr evaluation -#if !defined(AZ_COMPILER_MSVC) || AZ_COMPILER_MSVC < 1915 || AZ_COMPILER_MSVC > 1916 if constexpr (AZStd::is_same_v) { return __builtin_memcmp(s1, s2, count); @@ -301,7 +319,6 @@ namespace AZStd return __builtin_wmemcmp(s1, s2, count); } else -#endif { for (; count; --count, ++s1, ++s2) { @@ -339,10 +356,6 @@ namespace AZStd } static constexpr const char_type* find(const char_type* s, size_t count, const char_type& ch) noexcept { - // There is a bug with the __builtin_char_memchr intrinsic in Visual Studio 2017 15.8.x and 15.9.x - // It reads in one more additional character than the value of count. - // This is probably due to assuming null-termination -#if !defined(AZ_COMPILER_MSVC) || AZ_COMPILER_MSVC < 1915 || AZ_COMPILER_MSVC > 1916 if constexpr (AZStd::is_same_v) { return __builtin_char_memchr(s, ch, count); @@ -353,7 +366,6 @@ namespace AZStd } else -#endif { for (; count; --count, ++s) { @@ -368,64 +380,112 @@ namespace AZStd static constexpr char_type* move(char_type* dest, const char_type* src, size_t count) noexcept { AZ_Assert(dest != nullptr && src != nullptr, "Invalid input!"); - if (count == 0) + if (count == 0 || src == dest) { return dest; } - char_type* result = dest; - // The less than(<), greater than(>) and other variants(<=, >=) - // Cannot be compare pointers within a constexpr due to the potential for undefined behavior - // per the bullet linked in the C++ standard at http://eel.is/c++draft/expr.compound#expr.rel-5 - // Now clang and gcc compilers allow the use of this relation operators in a constexpr, but - // msvc is not so forgiving - // So a workaround of iterating the src pointer, checking for equality with the dest pointer - // is used to check for overlap - auto should_copy_forward = [](const char_type* dest1, const char_type* src2, size_t count2) constexpr -> bool + + #if az_has_builtin_memmove + __builtin_memmove(dest, src, count * sizeof(char_type)); + #else + auto NonBuiltinMove = [](char_type* dest1, const char_type* src1, size_t count1) constexpr + -> char_type* { - bool dest_less_than_src{ true }; - for(const char_type* src_iter = src2; src_iter != src2 + count2; ++src_iter) + if (az_builtin_is_constant_evaluated()) { - if (src_iter == dest1) + // The less than(<), greater than(>) and other variants(<=, >=) + // Cannot be compare pointers within a constexpr due to the potential for undefined behavior + // per the bullet linked in the C++ standard at http://eel.is/c++draft/expr.compound#expr.rel-5 + // Now clang and gcc compilers allow the use of this relation operators in a constexpr, but + // msvc is not so forgiving + // So a workaround of iterating the src pointer, checking for equality with the dest pointer + // is used to check for overlap + auto should_copy_forward = [](const char_type* dest2, const char_type* src2, size_t count2) constexpr -> bool { - dest_less_than_src = false; - break; + bool dest_less_than_src{ true }; + for (const char_type* src_iter = src2; src_iter != src2 + count2; ++src_iter) + { + if (src_iter == dest2) + { + dest_less_than_src = false; + break; + } + } + return dest_less_than_src; + }; + + if (should_copy_forward(dest1, src1, count1)) + { + copy(dest1, src1, count1); + } + else + { + copy_backward(dest1, src1, count1); } } - return dest_less_than_src; + else + { + // Use the faster ::memmove operation at runtime + ::memmove(dest1, src1, count1 * sizeof(char_type)); + } + + return dest1; }; + NonBuiltinMove(dest, src, count); + #endif - if (should_copy_forward(dest, src, count)) - { - copy(dest, src, count); - } - else - { - copy_backward(dest, src, count); - } - - return result; + return dest; } static constexpr char_type* copy(char_type* dest, const char_type* src, size_t count) noexcept { AZ_Assert(dest != nullptr && src != nullptr, "Invalid input!"); - char_type* result = dest; - for(; count; --count, ++dest, ++src) + + #if az_has_builtin_memcpy + __builtin_memcpy(dest, src, count * sizeof(char_type)); + #else + auto NonBuiltinCopy = [](char_type* dest1, const char_type* src1, size_t count1) constexpr + -> char_type* { - assign(*dest, *src); - } - return result; + if (az_builtin_is_constant_evaluated()) + { + for (; count1; --count1, ++dest1, ++src1) + { + assign(*dest1, *src1); + } + } + else + { + ::memcpy(dest1, src1, count1 * sizeof(char_type)); + } + return dest1; + }; + NonBuiltinCopy(dest, src, count); + #endif + + return dest; } // Extension for constexpr workarounds: Addresses of a string literal cannot be compared at compile time and MSVC and clang will just refuse to compile the constexpr // Adding a copy_backwards overload that always copies backwards. - static constexpr char_type* copy_backward(char_type* dest, const char_type*src, size_t count) noexcept + static constexpr char_type* copy_backward(char_type* dest, const char_type* src, size_t count) noexcept { char_type* result = dest; - dest += count; - src += count; - for (; count; --count) + #if az_has_builtin_memmove + __builtin_memmove(dest, src, count * sizeof(char_type)); + #else + if (az_builtin_is_constant_evaluated()) { - assign(*--dest, *--src); + dest += count; + src += count; + for (; count; --count) + { + assign(*--dest, *--src); + } } + else + { + ::memmove(dest, src, count); + } + #endif return result; } diff --git a/Code/Framework/AzCore/Platform/Common/VisualStudio/AzCore/Natvis/azcore.natvis b/Code/Framework/AzCore/Platform/Common/VisualStudio/AzCore/Natvis/azcore.natvis index 3730b8a4f9..dca82e2439 100644 --- a/Code/Framework/AzCore/Platform/Common/VisualStudio/AzCore/Natvis/azcore.natvis +++ b/Code/Framework/AzCore/Platform/Common/VisualStudio/AzCore/Natvis/azcore.natvis @@ -10,6 +10,13 @@ + + {m_element} + + + {$T1} is empty + + reverse_iterator base() {m_current} @@ -388,35 +395,41 @@ - - {m_buffer,s} - {m_data,s} - m_buffer,s - m_data,s + + {((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer,s} + {((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data,s} + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer,s + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data,s - m_size - m_capacity + (size_t)((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_size + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_size + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.Capacity + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_capacity - m_size - m_buffer - m_data + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_size,u + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_size + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data - {m_buffer,su} - {m_data,su} - m_buffer,su - m_data,su + {((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer,su} + {((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data,su} + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer,su + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data,su - m_size - m_capacity + (size_t)((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_size + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_size + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.Capacity + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_capacity - m_size - m_buffer - m_data + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_size,u + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_size + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data diff --git a/Code/Framework/AzCore/Tests/AZStd/String.cpp b/Code/Framework/AzCore/Tests/AZStd/String.cpp index 356310a94d..0f84ad0970 100644 --- a/Code/Framework/AzCore/Tests/AZStd/String.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/String.cpp @@ -8,11 +8,11 @@ #include "UserTypes.h" #include -#include #include #include #include #include +#include #include #include #include @@ -21,13 +21,10 @@ #include // we need this for AZ_TEST_FLOAT compare -#include #include #include #include -using namespace AZStd; - // Because of the SSO (small string optimization) we always shoule have capacity != 0 and data != 0 #define AZ_TEST_VALIDATE_EMPTY_STRING(_String) \ EXPECT_TRUE(_String.validate()); \ @@ -81,8 +78,6 @@ namespace UnitTest va_end(mark); } -#if !AZ_UNIT_TEST_SKIP_STD_STRING_TESTS - TEST(StringC, VSNPrintf) { char buffer32[32]; @@ -168,75 +163,75 @@ namespace UnitTest { const char* sChar = "SSO string"; // 10 characters const char* sCharLong = "This is a long string test that will allocate"; // 45 characters - array aChar = { + AZStd::array aChar = { { 'a', 'b', 'c', 'd', 'e', 'f' } }; // short string (should use SSO) - string str1; + AZStd::string str1; AZ_TEST_VALIDATE_EMPTY_STRING(str1); // short char* - string str2(sChar); + AZStd::string str2(sChar); AZ_TEST_VALIDATE_STRING(str2, 10); - string str2_1(""); + AZStd::string str2_1(""); AZ_TEST_VALIDATE_EMPTY_STRING(str2_1); - string str3(sChar, 5); + AZStd::string str3(sChar, 5); AZ_TEST_VALIDATE_STRING(str3, 5); // long char* - string str4(sCharLong); + AZStd::string str4(sCharLong); AZ_TEST_VALIDATE_STRING(str4, 45); - string str5(sCharLong, 35); + AZStd::string str5(sCharLong, 35); AZ_TEST_VALIDATE_STRING(str5, 35); // element - string str6(13, 'a'); + AZStd::string str6(13, 'a'); AZ_TEST_VALIDATE_STRING(str6, 13); - string str6_1(0, 'a'); + AZStd::string str6_1(0, 'a'); AZ_TEST_VALIDATE_EMPTY_STRING(str6_1); - string str7(aChar.begin(), aChar.end()); + AZStd::string str7(aChar.begin(), aChar.end()); AZ_TEST_VALIDATE_STRING(str7, 6); - string str7_1(aChar.begin(), aChar.begin()); + AZStd::string str7_1(aChar.begin(), aChar.begin()); AZ_TEST_VALIDATE_EMPTY_STRING(str7_1); - string str8(sChar, sChar + 3); + AZStd::string str8(sChar, sChar + 3); AZ_TEST_VALIDATE_STRING(str8, 3); - string str8_1(sChar, sChar); + AZStd::string str8_1(sChar, sChar); AZ_TEST_VALIDATE_EMPTY_STRING(str8_1); // - string str9(str2); + AZStd::string str9(str2); AZ_TEST_VALIDATE_STRING(str9, 10); - string str9_1(str1); + AZStd::string str9_1(str1); AZ_TEST_VALIDATE_EMPTY_STRING(str9_1); - string str10(str2, 4); + AZStd::string str10(str2, 4); AZ_TEST_VALIDATE_STRING(str10, 6); - string str11(str2, 4, 3); + AZStd::string str11(str2, 4, 3); AZ_TEST_VALIDATE_STRING(str11, 3); - string str12(sChar); - string large = sCharLong; + AZStd::string str12(sChar); + AZStd::string large = sCharLong; // move ctor - string strSm = AZStd::move(str12); + AZStd::string strSm = AZStd::move(str12); AZ_TEST_VALIDATE_STRING(strSm, 10); AZ_TEST_VALIDATE_EMPTY_STRING(str12); - string strLg(AZStd::move(large)); + AZStd::string strLg(AZStd::move(large)); AZ_TEST_VALIDATE_STRING(strLg, 45); AZ_TEST_VALIDATE_EMPTY_STRING(large); - string strEmpty(AZStd::move(str1)); + AZStd::string strEmpty(AZStd::move(str1)); AZ_TEST_VALIDATE_EMPTY_STRING(strEmpty); AZ_TEST_VALIDATE_EMPTY_STRING(str1); @@ -369,7 +364,7 @@ namespace UnitTest AZ_TEST_VALIDATE_STRING(str2, 28); AZ_TEST_ASSERT(str2[0] == 'b'); - str2.erase(str2.begin(), next(str2.begin(), 4)); + str2.erase(str2.begin(), AZStd::next(str2.begin(), 4)); AZ_TEST_VALIDATE_STRING(str2, 24); AZ_TEST_ASSERT(str2[0] == 'f'); @@ -400,33 +395,33 @@ namespace UnitTest AZ_TEST_ASSERT(str2[3] == 'g'); AZ_TEST_ASSERT(str2[4] == 'g'); - str2.replace(str2.begin(), next(str2.begin(), str1.length()), str1); + str2.replace(str2.begin(), AZStd::next(str2.begin(), str1.length()), str1); AZ_TEST_VALIDATE_STRING(str2, 24); AZ_TEST_ASSERT(str2[0] == 'a'); AZ_TEST_ASSERT(str2[1] == 'b'); - str2.replace(str2.begin(), next(str2.begin(), 10), sChar); + str2.replace(str2.begin(), AZStd::next(str2.begin(), 10), sChar); AZ_TEST_VALIDATE_STRING(str2, 24); AZ_TEST_ASSERT(str2[0] == 'S'); AZ_TEST_ASSERT(str2[1] == 'S'); - str2.replace(str2.begin(), next(str2.begin(), 3), sChar, 3); + str2.replace(str2.begin(), AZStd::next(str2.begin(), 3), sChar, 3); AZ_TEST_VALIDATE_STRING(str2, 24); AZ_TEST_ASSERT(str2[0] == 'S'); AZ_TEST_ASSERT(str2[1] == 'S'); AZ_TEST_ASSERT(str2[2] == 'O'); - str2.replace(str2.begin(), next(str2.begin(), 2), 2, 'h'); + str2.replace(str2.begin(), AZStd::next(str2.begin(), 2), 2, 'h'); AZ_TEST_VALIDATE_STRING(str2, 24); AZ_TEST_ASSERT(str2[0] == 'h'); AZ_TEST_ASSERT(str2[1] == 'h'); - str2.replace(str2.begin(), next(str2.begin(), 2), aChar.begin(), next(aChar.begin(), 2)); + str2.replace(str2.begin(), AZStd::next(str2.begin(), 2), aChar.begin(), AZStd::next(aChar.begin(), 2)); AZ_TEST_VALIDATE_STRING(str2, 24); AZ_TEST_ASSERT(str2[0] == 'a'); AZ_TEST_ASSERT(str2[1] == 'b'); - str2.replace(str2.begin(), next(str2.begin(), 2), sChar, sChar + 5); + str2.replace(str2.begin(), AZStd::next(str2.begin(), 2), sChar, sChar + 5); AZ_TEST_VALIDATE_STRING(str2, 27); AZ_TEST_ASSERT(str2[0] == 'S'); AZ_TEST_ASSERT(str2[1] == 'S'); @@ -489,7 +484,7 @@ namespace UnitTest AZ_TEST_ASSERT(pos == 2); pos = str1.find('Z'); - AZ_TEST_ASSERT(pos == string::npos); + AZ_TEST_ASSERT(pos == AZStd::string::npos); pos = str1.rfind(str2); AZ_TEST_ASSERT(pos == 12); @@ -510,7 +505,7 @@ namespace UnitTest AZ_TEST_ASSERT(pos == 12); pos = str1.rfind('Z'); - AZ_TEST_ASSERT(pos == string::npos); + AZ_TEST_ASSERT(pos == AZStd::string::npos); pos = str1.find_first_of(str2); AZ_TEST_ASSERT(pos == 2); @@ -535,7 +530,7 @@ namespace UnitTest AZ_TEST_ASSERT(pos == 12); pos = str1.find_first_of('Z'); - AZ_TEST_ASSERT(pos == string::npos); + AZ_TEST_ASSERT(pos == AZStd::string::npos); pos = str1.find_last_of(str2); AZ_TEST_ASSERT(pos == 14); @@ -550,7 +545,7 @@ namespace UnitTest AZ_TEST_ASSERT(pos == 12); pos = str1.find_last_of('Z'); - AZ_TEST_ASSERT(pos == string::npos); + AZ_TEST_ASSERT(pos == AZStd::string::npos); pos = str1.find_first_not_of(str2, 3); AZ_TEST_ASSERT(pos == 5); @@ -559,13 +554,13 @@ namespace UnitTest AZ_TEST_ASSERT(pos == 0); pos = str1.find_last_not_of(sChar); - AZ_TEST_ASSERT(pos == string::npos); + AZ_TEST_ASSERT(pos == AZStd::string::npos); pos = str1.find_last_not_of('Z'); AZ_TEST_ASSERT(pos == 19); - string sub = str1.substr(0, 10); + AZStd::string sub = str1.substr(0, 10); AZ_TEST_VALIDATE_STRING(sub, 10); AZ_TEST_ASSERT(sub[0] == 'S'); AZ_TEST_ASSERT(sub[9] == 'g'); @@ -594,13 +589,13 @@ namespace UnitTest using iteratorType = char; auto testValue = str4; - reverse_iterator rend = testValue.rend(); - reverse_iterator crend1 = testValue.rend(); - reverse_iterator crend2 = testValue.crend(); + AZStd::reverse_iterator rend = testValue.rend(); + AZStd::reverse_iterator crend1 = testValue.rend(); + AZStd::reverse_iterator crend2 = testValue.crend(); - reverse_iterator rbegin = testValue.rbegin(); - reverse_iterator crbegin1 = testValue.rbegin(); - reverse_iterator crbegin2 = testValue.crbegin(); + AZStd::reverse_iterator rbegin = testValue.rbegin(); + AZStd::reverse_iterator crbegin1 = testValue.rbegin(); + AZStd::reverse_iterator crbegin2 = testValue.crbegin(); AZ_TEST_ASSERT(rend == crend1); AZ_TEST_ASSERT(crend1 == crend2); @@ -630,128 +625,128 @@ namespace UnitTest TEST_F(String, Algorithms) { - string str = string::format("%s %d", "BlaBla", 5); + AZStd::string str = AZStd::string::format("%s %d", "BlaBla", 5); AZ_TEST_VALIDATE_STRING(str, 8); - wstring wstr = wstring::format(L"%ls %d", L"BlaBla", 5); + AZStd::wstring wstr = AZStd::wstring::format(L"%ls %d", L"BlaBla", 5); AZ_TEST_VALIDATE_WSTRING(wstr, 8); - to_lower(str.begin(), str.end()); + AZStd::to_lower(str.begin(), str.end()); AZ_TEST_ASSERT(str[0] == 'b'); AZ_TEST_ASSERT(str[3] == 'b'); - to_upper(str.begin(), str.end()); + AZStd::to_upper(str.begin(), str.end()); AZ_TEST_ASSERT(str[1] == 'L'); AZ_TEST_ASSERT(str[2] == 'A'); - string intStr("10"); + AZStd::string intStr("10"); int ival = AZStd::stoi(intStr); AZ_TEST_ASSERT(ival == 10); - wstring wintStr(L"10"); + AZStd::wstring wintStr(L"10"); ival = AZStd::stoi(wintStr); AZ_TEST_ASSERT(ival == 10); - string floatStr("2.32"); + AZStd::string floatStr("2.32"); float fval = AZStd::stof(floatStr); AZ_TEST_ASSERT_FLOAT_CLOSE(fval, 2.32f); - wstring wfloatStr(L"2.32"); + AZStd::wstring wfloatStr(L"2.32"); fval = AZStd::stof(wfloatStr); AZ_TEST_ASSERT_FLOAT_CLOSE(fval, 2.32f); - to_string(intStr, 20); + AZStd::to_string(intStr, 20); AZ_TEST_ASSERT(intStr == "20"); - AZ_TEST_ASSERT(to_string(static_cast(20)) == "20"); - AZ_TEST_ASSERT(to_string(static_cast(20)) == "20"); - AZ_TEST_ASSERT(to_string(static_cast(20)) == "20"); - AZ_TEST_ASSERT(to_string(static_cast(20)) == "20"); - AZ_TEST_ASSERT(to_string(static_cast(20)) == "20"); - AZ_TEST_ASSERT(to_string(static_cast(20)) == "20"); + EXPECT_EQ("20", AZStd::to_string(static_cast(20))); + EXPECT_EQ("20", AZStd::to_string(static_cast(20))); + EXPECT_EQ("20", AZStd::to_string(static_cast(20))); + EXPECT_EQ("20", AZStd::to_string(static_cast(20))); + EXPECT_EQ("20", AZStd::to_string(static_cast(20))); + EXPECT_EQ("20", AZStd::to_string(static_cast(20))); // wstring to string - string str1; - to_string(str1, wstr); + AZStd::string str1; + AZStd::to_string(str1, wstr); AZ_TEST_ASSERT(str1 == "BlaBla 5"); EXPECT_EQ(8, to_string_length(wstr)); - str1 = string::format("%ls", wstr.c_str()); + str1 = AZStd::string::format("%ls", wstr.c_str()); AZ_TEST_ASSERT(str1 == "BlaBla 5"); // string to wstring - wstring wstr1; - to_wstring(wstr1, str); + AZStd::wstring wstr1; + AZStd::to_wstring(wstr1, str); AZ_TEST_ASSERT(wstr1 == L"BLABLA 5"); - wstr1 = wstring::format(L"%hs", str.c_str()); + wstr1 = AZStd::wstring::format(L"%hs", str.c_str()); AZ_TEST_ASSERT(wstr1 == L"BLABLA 5"); // wstring to char buffer char strBuffer[9]; - to_string(strBuffer, 9, wstr1.c_str()); + AZStd::to_string(strBuffer, 9, wstr1.c_str()); AZ_TEST_ASSERT(0 == azstricmp(strBuffer, "BLABLA 5")); EXPECT_EQ(8, to_string_length(wstr1)); // wstring to char with unicode - wstring ws1InfinityEscaped = L"Infinity: \u221E"; // escaped + AZStd::wstring ws1InfinityEscaped = L"Infinity: \u221E"; // escaped EXPECT_EQ(13, to_string_length(ws1InfinityEscaped)); // wchar_t buffer to char buffer wchar_t wstrBuffer[9] = L"BLABLA 5"; memset(strBuffer, 0, AZ_ARRAY_SIZE(strBuffer)); - to_string(strBuffer, 9, wstrBuffer); + AZStd::to_string(strBuffer, 9, wstrBuffer); AZ_TEST_ASSERT(0 == azstricmp(strBuffer, "BLABLA 5")); // string to wchar_t buffer memset(wstrBuffer, 0, AZ_ARRAY_SIZE(wstrBuffer)); - to_wstring(wstrBuffer, 9, str1.c_str()); + AZStd::to_wstring(wstrBuffer, 9, str1.c_str()); AZ_TEST_ASSERT(0 == azwcsicmp(wstrBuffer, L"BlaBla 5")); // char buffer to wchar_t buffer memset(wstrBuffer, L' ', AZ_ARRAY_SIZE(wstrBuffer)); // to check that the null terminator is properly placed - to_wstring(wstrBuffer, 9, strBuffer); + AZStd::to_wstring(wstrBuffer, 9, strBuffer); AZ_TEST_ASSERT(0 == azwcsicmp(wstrBuffer, L"BLABLA 5")); // wchar UTF16/UTF32 to/from Utf8 wstr1 = L"this is a \u20AC \u00A3 test"; // that's a euro and a pound sterling AZStd::to_string(str, wstr1); - wstring wstr2; + AZStd::wstring wstr2; AZStd::to_wstring(wstr2, str); AZ_TEST_ASSERT(wstr1 == wstr2); // tokenize - vector tokens; - tokenize(string("one, two, three"), string(", "), tokens); + AZStd::vector tokens; + AZStd::tokenize(AZStd::string("one, two, three"), AZStd::string(", "), tokens); AZ_TEST_ASSERT(tokens.size() == 3); AZ_TEST_ASSERT(tokens[0] == "one"); AZ_TEST_ASSERT(tokens[1] == "two"); AZ_TEST_ASSERT(tokens[2] == "three"); - tokenize(string("one, ,, two, ,, three"), string(", "), tokens); + AZStd::tokenize(AZStd::string("one, ,, two, ,, three"), AZStd::string(", "), tokens); AZ_TEST_ASSERT(tokens.size() == 3); AZ_TEST_ASSERT(tokens[0] == "one"); AZ_TEST_ASSERT(tokens[1] == "two"); AZ_TEST_ASSERT(tokens[2] == "three"); - tokenize(string("thequickbrownfox"), string("ABC"), tokens); + AZStd::tokenize(AZStd::string("thequickbrownfox"), AZStd::string("ABC"), tokens); AZ_TEST_ASSERT(tokens.size() == 1); AZ_TEST_ASSERT(tokens[0] == "thequickbrownfox"); - tokenize(string(""), string(""), tokens); + AZStd::tokenize(AZStd::string{}, AZStd::string{}, tokens); AZ_TEST_ASSERT(tokens.empty()); - tokenize(string("ABC"), string("ABC"), tokens); + AZStd::tokenize(AZStd::string("ABC"), AZStd::string("ABC"), tokens); AZ_TEST_ASSERT(tokens.empty()); - tokenize(string(" foo bar "), string(" "), tokens); + AZStd::tokenize(AZStd::string(" foo bar "), AZStd::string(" "), tokens); AZ_TEST_ASSERT(tokens.size() == 2); AZ_TEST_ASSERT(tokens[0] == "foo"); AZ_TEST_ASSERT(tokens[1] == "bar"); - tokenize_keep_empty(string(" foo , bar "), string(","), tokens); + AZStd::tokenize_keep_empty(AZStd::string(" foo , bar "), AZStd::string(","), tokens); AZ_TEST_ASSERT(tokens.size() == 2); AZ_TEST_ASSERT(tokens[0] == " foo "); AZ_TEST_ASSERT(tokens[1] == " bar "); // Sort - AZStd::vector toSort; + AZStd::vector toSort; toSort.push_back("z2"); toSort.push_back("z100"); toSort.push_back("z1"); @@ -761,39 +756,39 @@ namespace UnitTest AZ_TEST_ASSERT(toSort[2] == "z2"); // Natural sort - AZ_TEST_ASSERT(alphanum_comp("", "") == 0); - AZ_TEST_ASSERT(alphanum_comp("", "a") < 0); - AZ_TEST_ASSERT(alphanum_comp("a", "") > 0); - AZ_TEST_ASSERT(alphanum_comp("a", "a") == 0); - AZ_TEST_ASSERT(alphanum_comp("", "9") < 0); - AZ_TEST_ASSERT(alphanum_comp("9", "") > 0); - AZ_TEST_ASSERT(alphanum_comp("1", "1") == 0); - AZ_TEST_ASSERT(alphanum_comp("1", "2") < 0); - AZ_TEST_ASSERT(alphanum_comp("3", "2") > 0); - AZ_TEST_ASSERT(alphanum_comp("a1", "a1") == 0); - AZ_TEST_ASSERT(alphanum_comp("a1", "a2") < 0); - AZ_TEST_ASSERT(alphanum_comp("a2", "a1") > 0); - AZ_TEST_ASSERT(alphanum_comp("a1a2", "a1a3") < 0); - AZ_TEST_ASSERT(alphanum_comp("a1a2", "a1a0") > 0); - AZ_TEST_ASSERT(alphanum_comp("134", "122") > 0); - AZ_TEST_ASSERT(alphanum_comp("12a3", "12a3") == 0); - AZ_TEST_ASSERT(alphanum_comp("12a1", "12a0") > 0); - AZ_TEST_ASSERT(alphanum_comp("12a1", "12a2") < 0); - AZ_TEST_ASSERT(alphanum_comp("a", "aa") < 0); - AZ_TEST_ASSERT(alphanum_comp("aaa", "aa") > 0); - AZ_TEST_ASSERT(alphanum_comp("Alpha 2", "Alpha 2") == 0); - AZ_TEST_ASSERT(alphanum_comp("Alpha 2", "Alpha 2A") < 0); - AZ_TEST_ASSERT(alphanum_comp("Alpha 2 B", "Alpha 2") > 0); - string strA("Alpha 2"); - AZ_TEST_ASSERT(alphanum_comp(strA, "Alpha 2") == 0); - AZ_TEST_ASSERT(alphanum_comp(strA, "Alpha 2A") < 0); - AZ_TEST_ASSERT(alphanum_comp("Alpha 2 B", strA) > 0); - AZ_TEST_ASSERT(alphanum_comp(strA, strdup("Alpha 2")) == 0); - AZ_TEST_ASSERT(alphanum_comp(strA, strdup("Alpha 2A")) < 0); - AZ_TEST_ASSERT(alphanum_comp(strdup("Alpha 2 B"), strA) > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("", "") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("", "a") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a", "") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a", "a") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("", "9") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("9", "") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("1", "1") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("1", "2") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("3", "2") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a1", "a1") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a1", "a2") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a2", "a1") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a1a2", "a1a3") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a1a2", "a1a0") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("134", "122") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("12a3", "12a3") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("12a1", "12a0") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("12a1", "12a2") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a", "aa") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("aaa", "aa") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("Alpha 2", "Alpha 2") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("Alpha 2", "Alpha 2A") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("Alpha 2 B", "Alpha 2") > 0); + AZStd::string strA("Alpha 2"); + AZ_TEST_ASSERT(AZStd::alphanum_comp(strA, "Alpha 2") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp(strA, "Alpha 2A") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("Alpha 2 B", strA) > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp(strA, strdup("Alpha 2")) == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp(strA, strdup("Alpha 2A")) < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp(strdup("Alpha 2 B"), strA) > 0); // show usage of the comparison functor with a set - using StringSetType = set>; + using StringSetType = AZStd::set>; StringSetType s; s.insert("Xiph Xlater 58"); s.insert("Xiph Xlater 5000"); @@ -879,7 +874,7 @@ namespace UnitTest AZ_TEST_ASSERT(*setIt++ == "Xiph Xlater 10000"); // show usage of comparison functor with a map - using StringIntMapType = map>; + using StringIntMapType = AZStd::map>; StringIntMapType m; m["z1.doc"] = 1; m["z10.doc"] = 2; @@ -931,13 +926,13 @@ namespace UnitTest AZ_TEST_ASSERT((mapIt++)->second == 5); // show usage of comparison functor with an STL algorithm on a vector - vector v; + AZStd::vector v; // vector contents are reversed sorted contents of the old set - AZStd::copy(s.rbegin(), s.rend(), back_inserter(v)); + AZStd::copy(s.rbegin(), s.rend(), AZStd::back_inserter(v)); // now sort the vector with the algorithm - AZStd::sort(v.begin(), v.end(), alphanum_less()); + AZStd::sort(v.begin(), v.end(), AZStd::alphanum_less()); // check values - vector::const_iterator vecIt = v.begin(); + AZStd::vector::const_iterator vecIt = v.begin(); AZ_TEST_ASSERT(*vecIt++ == "10X Radonius"); AZ_TEST_ASSERT(*vecIt++ == "20X Radonius"); AZ_TEST_ASSERT(*vecIt++ == "20X Radonius Prime"); @@ -988,52 +983,52 @@ namespace UnitTest TEST_F(Regex, Regex_IPAddressSubnetPattern_Success) { // Error case for LY-43888 - regex txt_regex("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(/([0-9]|[1-2][0-9]|3[0-2]))?$"); - string sample_input("10.85.22.92/24"); - bool match = regex_match(sample_input, txt_regex); + AZStd::regex txt_regex("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(/([0-9]|[1-2][0-9]|3[0-2]))?$"); + AZStd::string sample_input("10.85.22.92/24"); + bool match = AZStd::regex_match(sample_input, txt_regex); AZ_TEST_ASSERT(match); } TEST_F(Regex, MatchConstChar) { //regex - AZ_TEST_ASSERT(regex_match("subject", regex("(sub)(.*)"))); + AZ_TEST_ASSERT(AZStd::regex_match("subject", AZStd::regex("(sub)(.*)"))); } TEST_F(Regex, MatchString) { - string reStr("subject"); - regex re("(sub)(.*)"); - AZ_TEST_ASSERT(regex_match(reStr, re)); - AZ_TEST_ASSERT(regex_match(reStr.begin(), reStr.end(), re)) + AZStd::string reStr("subject"); + AZStd::regex re("(sub)(.*)"); + AZ_TEST_ASSERT(AZStd::regex_match(reStr, re)); + AZ_TEST_ASSERT(AZStd::regex_match(reStr.begin(), reStr.end(), re)) } TEST_F(Regex, CMatch) { - regex re("(sub)(.*)"); - cmatch cm; // same as match_results cm; - regex_match("subject", cm, re); + AZStd::regex re("(sub)(.*)"); + AZStd::cmatch cm; // same as match_results cm; + AZStd::regex_match("subject", cm, re); AZ_TEST_ASSERT(cm.size() == 3); } TEST_F(Regex, SMatch) { - string reStr("subject"); - regex re("(sub)(.*)"); - smatch sm; // same as std::match_results sm; - regex_match(reStr, sm, re); + AZStd::string reStr("subject"); + AZStd::regex re("(sub)(.*)"); + AZStd::smatch sm; // same as std::match_results sm; + AZStd::regex_match(reStr, sm, re); AZ_TEST_ASSERT(sm.size() == 3); - regex_match(reStr.cbegin(), reStr.cend(), sm, re); + AZStd::regex_match(reStr.cbegin(), reStr.cend(), sm, re); AZ_TEST_ASSERT(sm.size() == 3); } TEST_F(Regex, CMatchWithFlags) { - regex re("(sub)(.*)"); - cmatch cm; // same as match_results cm; + AZStd::regex re("(sub)(.*)"); + AZStd::cmatch cm; // same as match_results cm; // using explicit flags: - regex_match("subject", cm, re, regex_constants::match_default); + AZStd::regex_match("subject", cm, re, AZStd::regex_constants::match_default); AZ_TEST_ASSERT(cm[0] == "subject"); AZ_TEST_ASSERT(cm[1] == "sub"); AZ_TEST_ASSERT(cm[2] == "ject"); @@ -1042,18 +1037,18 @@ namespace UnitTest TEST_F(Regex, PatternMatchFiles) { // Simple regular expression matching - string fnames[] = { "foo.txt", "bar.txt", "baz.dat", "zoidberg" }; - regex txt_regex("[a-z]+\\.txt"); + AZStd::string fnames[] = { "foo.txt", "bar.txt", "baz.dat", "zoidberg" }; + AZStd::regex txt_regex("[a-z]+\\.txt"); for (size_t i = 0; i < AZ_ARRAY_SIZE(fnames); ++i) { if (i < 2) { - AZ_TEST_ASSERT(regex_match(fnames[i], txt_regex) == true); + AZ_TEST_ASSERT(AZStd::regex_match(fnames[i], txt_regex) == true); } else { - AZ_TEST_ASSERT(regex_match(fnames[i], txt_regex) == false); + AZ_TEST_ASSERT(AZStd::regex_match(fnames[i], txt_regex) == false); } } } @@ -1061,13 +1056,13 @@ namespace UnitTest TEST_F(Regex, PatternWithSingleCaptureGroup) { // Extraction of a sub-match - string fnames[] = { "foo.txt", "bar.txt", "baz.dat", "zoidberg" }; - regex base_regex("([a-z]+)\\.txt"); - smatch base_match; + AZStd::string fnames[] = { "foo.txt", "bar.txt", "baz.dat", "zoidberg" }; + AZStd::regex base_regex("([a-z]+)\\.txt"); + AZStd::smatch base_match; for (size_t i = 0; i < AZ_ARRAY_SIZE(fnames); ++i) { - if (regex_match(fnames[i], base_match, base_regex)) + if (AZStd::regex_match(fnames[i], base_match, base_regex)) { AZ_TEST_ASSERT(base_match.size() == 2); AZ_TEST_ASSERT(base_match[1] == "foo" || base_match[1] == "bar") @@ -1078,12 +1073,12 @@ namespace UnitTest TEST_F(Regex, PatternWithMultipleCaptureGroups) { // Extraction of several sub-matches - string fnames[] = { "foo.txt", "bar.txt", "baz.dat", "zoidberg" }; - regex pieces_regex("([a-z]+)\\.([a-z]+)"); - smatch pieces_match; + AZStd::string fnames[] = { "foo.txt", "bar.txt", "baz.dat", "zoidberg" }; + AZStd::regex pieces_regex("([a-z]+)\\.([a-z]+)"); + AZStd::smatch pieces_match; for (size_t i = 0; i < AZ_ARRAY_SIZE(fnames); ++i) { - if (regex_match(fnames[i], pieces_match, pieces_regex)) + if (AZStd::regex_match(fnames[i], pieces_match, pieces_regex)) { AZ_TEST_ASSERT(pieces_match.size() == 3); AZ_TEST_ASSERT(pieces_match[0] == "foo.txt" || pieces_match[0] == "bar.txt" || pieces_match[0] == "baz.dat"); @@ -1096,40 +1091,40 @@ namespace UnitTest TEST_F(Regex, WideCharTests) { //wchar_t - AZ_TEST_ASSERT(regex_match(L"subject", wregex(L"(sub)(.*)"))); - wstring reWStr(L"subject"); - wregex reW(L"(sub)(.*)"); - AZ_TEST_ASSERT(regex_match(reWStr, reW)); - AZ_TEST_ASSERT(regex_match(reWStr.begin(), reWStr.end(), reW)) + AZ_TEST_ASSERT(AZStd::regex_match(L"subject", AZStd::wregex(L"(sub)(.*)"))); + AZStd::wstring reWStr(L"subject"); + AZStd::wregex reW(L"(sub)(.*)"); + AZ_TEST_ASSERT(AZStd::regex_match(reWStr, reW)); + AZ_TEST_ASSERT(AZStd::regex_match(reWStr.begin(), reWStr.end(), reW)) } TEST_F(Regex, LongPatterns) { // test construction and destruction of a regex with a pattern long enough to require reallocation of buffers - regex longerThan16(".*\\/Presets\\/GeomCache\\/.*", regex::flag_type::icase | regex::flag_type::ECMAScript); - regex longerThan32(".*\\/Presets\\/GeomCache\\/Whatever\\/Much\\/Test\\/Very\\/Memory\\/.*", regex::flag_type::icase); + AZStd::regex longerThan16(".*\\/Presets\\/GeomCache\\/.*", AZStd::regex::flag_type::icase | AZStd::regex::flag_type::ECMAScript); + AZStd::regex longerThan32(".*\\/Presets\\/GeomCache\\/Whatever\\/Much\\/Test\\/Very\\/Memory\\/.*", AZStd::regex::flag_type::icase); } TEST_F(Regex, SmileyFaceParseRegression) { - regex smiley(":)"); + AZStd::regex smiley(":)"); EXPECT_TRUE(smiley.Empty()); EXPECT_TRUE(smiley.GetError() != nullptr); - EXPECT_FALSE(regex_match("wut", smiley)); - EXPECT_FALSE(regex_match(":)", smiley)); + EXPECT_FALSE(AZStd::regex_match("wut", smiley)); + EXPECT_FALSE(AZStd::regex_match(":)", smiley)); } TEST_F(Regex, ParseFailure) { - regex failed(")))/?!\\$"); + AZStd::regex failed(")))/?!\\$"); EXPECT_FALSE(failed.Valid()); - regex other = AZStd::move(failed); + AZStd::regex other = AZStd::move(failed); EXPECT_FALSE(other.Valid()); - regex other2; + AZStd::regex other2; other2.swap(other); EXPECT_TRUE(other.Empty()); EXPECT_TRUE(other.GetError() == nullptr); @@ -1139,69 +1134,69 @@ namespace UnitTest TEST_F(String, ConstString) { - string_view cstr1; - AZ_TEST_ASSERT(cstr1.data()==nullptr); - AZ_TEST_ASSERT(cstr1.size() == 0); - AZ_TEST_ASSERT(cstr1.length() == 0); - AZ_TEST_ASSERT(cstr1.begin() == cstr1.end()); - AZ_TEST_ASSERT(cstr1 == string_view()); - AZ_TEST_ASSERT(cstr1.empty()); + AZStd::string_view cstr1; + EXPECT_EQ(nullptr, cstr1.data()); + EXPECT_EQ(0, cstr1.size()); + EXPECT_EQ(0, cstr1.length()); + EXPECT_EQ(cstr1.begin(), cstr1.end()); + EXPECT_EQ(cstr1, AZStd::string_view()); + EXPECT_TRUE(cstr1.empty()); - string_view cstr2("Test"); - AZ_TEST_ASSERT(cstr2.data() != nullptr); - AZ_TEST_ASSERT(cstr2.size() == 4); - AZ_TEST_ASSERT(cstr2.length() == 4); - AZ_TEST_ASSERT(cstr2.begin() != cstr2.end()); - AZ_TEST_ASSERT(cstr2 != cstr1); - AZ_TEST_ASSERT(cstr2 == string_view("Test")); - AZ_TEST_ASSERT(cstr2 == "Test"); - AZ_TEST_ASSERT(cstr2 != "test"); - AZ_TEST_ASSERT(cstr2[2] == 's'); - AZ_TEST_ASSERT(cstr2.at(2) == 's'); + AZStd::string_view cstr2("Test"); + EXPECT_NE(nullptr, cstr2.data()); + EXPECT_EQ(4, cstr2.size()); + EXPECT_EQ(4, cstr2.length()); + EXPECT_NE(cstr2.begin(), cstr2.end()); + EXPECT_NE(cstr2, cstr1); + EXPECT_EQ(cstr2, AZStd::string_view("Test")); + EXPECT_EQ(cstr2, "Test"); + EXPECT_NE(cstr2, "test"); + EXPECT_EQ(cstr2[2], 's'); + EXPECT_EQ(cstr2.at(2), 's'); AZ_TEST_START_TRACE_SUPPRESSION; - AZ_TEST_ASSERT(cstr2.at(7) == 0); + EXPECT_EQ(0, cstr2.at(7)); AZ_TEST_STOP_TRACE_SUPPRESSION(1); - AZ_TEST_ASSERT(!cstr2.empty()); - AZ_TEST_ASSERT(cstr2.data() == string("Test")); - AZ_TEST_ASSERT((string)cstr2 == string("Test")); + EXPECT_FALSE(cstr2.empty()); + EXPECT_EQ(cstr2.data(), AZStd::string("Test")); + EXPECT_EQ(cstr2, AZStd::string("Test")); - string_view cstr3 = cstr2; - AZ_TEST_ASSERT(cstr3 == cstr2); + AZStd::string_view cstr3 = cstr2; + EXPECT_EQ(cstr3, cstr2); cstr3.swap(cstr1); - AZ_TEST_ASSERT(cstr3 == string_view()); - AZ_TEST_ASSERT(cstr1 == cstr2); + EXPECT_EQ(cstr3, AZStd::string_view()); + EXPECT_EQ(cstr1, cstr2); cstr1 = {}; - AZ_TEST_ASSERT(cstr1 == string_view()); - AZ_TEST_ASSERT(cstr1.size() == 0); - AZ_TEST_ASSERT(cstr1.length() == 0); + EXPECT_EQ(cstr1, AZStd::string_view()); + EXPECT_EQ(0, cstr1.size()); + EXPECT_EQ(0, cstr1.length()); AZStd::string str1("Test"); - AZ_TEST_ASSERT(cstr2 == str1); + EXPECT_EQ(cstr2, str1); cstr1 = str1; - AZ_TEST_ASSERT(cstr1 == cstr2); + EXPECT_EQ(cstr1, cstr2); // check hashing - AZStd::hash h; + AZStd::hash h; AZStd::size_t value = h(cstr1); - AZ_TEST_ASSERT(value != 0); + EXPECT_NE(0, value); // testing empty string AZStd::string emptyString; - string_view cstr4; + AZStd::string_view cstr4; cstr4 = emptyString; - AZ_TEST_ASSERT(cstr4.data() != nullptr); - AZ_TEST_ASSERT(cstr4.size() == 0); - AZ_TEST_ASSERT(cstr4.length() == 0); - AZ_TEST_ASSERT(cstr4.begin() == cstr4.end()); - AZ_TEST_ASSERT(cstr4.empty()); + EXPECT_NE(nullptr, cstr4.data()); + EXPECT_EQ(0, cstr4.size()); + EXPECT_EQ(0, cstr4.length()); + EXPECT_EQ(cstr4.begin(), cstr4.end()); + EXPECT_TRUE(cstr4.empty()); } TEST_F(String, StringViewModifierTest) { - string_view emptyView1; - string_view view2("Needle in Haystack"); + AZStd::string_view emptyView1; + AZStd::string_view view2("Needle in Haystack"); // front EXPECT_EQ('N', view2.front()); @@ -1209,7 +1204,7 @@ namespace UnitTest EXPECT_EQ('k', view2.back()); AZStd::string findStr("Hay"); - string_view view3(findStr); + AZStd::string_view view3(findStr); // copy const size_t destBufferSize = 32; @@ -1223,17 +1218,17 @@ namespace UnitTest AZ_TEST_STOP_TRACE_SUPPRESSION(1); // substr - string_view subView2 = view2.substr(10); + AZStd::string_view subView2 = view2.substr(10); EXPECT_EQ("Haystack", subView2); AZ_TEST_START_TRACE_SUPPRESSION; - [[maybe_unused]] string_view assertSubView = view2.substr(view2.size() + 1); + [[maybe_unused]] AZStd::string_view assertSubView = view2.substr(view2.size() + 1); AZ_TEST_STOP_TRACE_SUPPRESSION(1); // compare AZStd::size_t compareResult = view2.compare(1, view2.size() - 1, dest, copyResult); EXPECT_EQ(0, compareResult); - string_view compareView = "Stackhay in Needle"; + AZStd::string_view compareView = "Stackhay in Needle"; compareResult = compareView.compare(view2); EXPECT_NE(0, compareResult); @@ -1252,7 +1247,7 @@ namespace UnitTest EXPECT_EQ(10, findResult); findResult = compareView.find("Random String"); - EXPECT_EQ(string_view::npos, findResult); + EXPECT_EQ(AZStd::string_view::npos, findResult); findResult = view3.find('y', 2); EXPECT_EQ(2, findResult); @@ -1262,13 +1257,13 @@ namespace UnitTest EXPECT_EQ(1, rfindResult); rfindResult = emptyView1.rfind(""); - EXPECT_EQ(string_view::npos, rfindResult); + EXPECT_EQ(AZStd::string_view::npos, rfindResult); rfindResult = view2.rfind("z"); - EXPECT_EQ(string_view::npos, rfindResult); + EXPECT_EQ(AZStd::string_view::npos, rfindResult); // find_first_of - string_view repeatString = "abcdefabcfedghiabcdef"; + AZStd::string_view repeatString = "abcdefabcfedghiabcdef"; AZStd::size_t findFirstOfResult = repeatString.find_first_of('f'); EXPECT_EQ(5, findFirstOfResult); @@ -1281,7 +1276,7 @@ namespace UnitTest AZStd::string notFoundStr = "zzz"; AZStd::string foundStr = "ghi"; findFirstOfResult = repeatString.find_first_of(notFoundStr); - EXPECT_EQ(string_view::npos, findFirstOfResult); + EXPECT_EQ(AZStd::string_view::npos, findFirstOfResult); findFirstOfResult = repeatString.find_first_of(foundStr); EXPECT_EQ(12, findFirstOfResult); @@ -1297,7 +1292,7 @@ namespace UnitTest EXPECT_EQ(3, findLastOfResult); findLastOfResult = repeatString.find_last_of(notFoundStr); - EXPECT_EQ(string_view::npos, findLastOfResult); + EXPECT_EQ(AZStd::string_view::npos, findLastOfResult); findLastOfResult = repeatString.find_last_of(foundStr); EXPECT_EQ(14, findLastOfResult); @@ -1335,12 +1330,12 @@ namespace UnitTest EXPECT_EQ(11, findLastNotOfResult); // remove_prefix - string_view prefixRemovalView = view2; + AZStd::string_view prefixRemovalView = view2; prefixRemovalView.remove_prefix(6); EXPECT_EQ(" in Haystack", prefixRemovalView); // remove_suffix - string_view suffixRemovalView = view2; + AZStd::string_view suffixRemovalView = view2; suffixRemovalView.remove_suffix(8); EXPECT_EQ("Needle in ", suffixRemovalView); @@ -1365,10 +1360,10 @@ namespace UnitTest TEST_F(String, StringViewCmpOperatorTest) { - string_view view1("The quick brown fox jumped over the lazy dog"); - string_view view2("Needle in Haystack"); - string_view emptyBeaverView; - string_view superEmptyBeaverView(""); + AZStd::string_view view1("The quick brown fox jumped over the lazy dog"); + AZStd::string_view view2("Needle in Haystack"); + AZStd::string_view emptyBeaverView; + AZStd::string_view superEmptyBeaverView(""); EXPECT_EQ("", emptyBeaverView); EXPECT_EQ("", superEmptyBeaverView); @@ -1378,13 +1373,13 @@ namespace UnitTest EXPECT_EQ(view2, "Needle in Haystack"); EXPECT_NE(view2, "Needle in Hayqueue"); - string_view compareView(view2); + AZStd::string_view compareView(view2); EXPECT_EQ(view2, compareView); EXPECT_NE(view2, view1); AZStd::string compareStr("Busy Beaver"); - string_view notBeaverView("Lumber Beaver"); - string_view beaverView("Busy Beaver"); + AZStd::string_view notBeaverView("Lumber Beaver"); + AZStd::string_view beaverView("Busy Beaver"); EXPECT_EQ(compareStr, beaverView); EXPECT_NE(compareStr, notBeaverView); @@ -1528,8 +1523,8 @@ namespace UnitTest TYPED_TEST_CASE(BasicStringViewConstexprFixture, StringViewElementTypes); TYPED_TEST(BasicStringViewConstexprFixture, StringView_DefaultConstructorsIsConstexpr) { - constexpr basic_string_view defaultView1; - constexpr basic_string_view defaultView2; + constexpr AZStd::basic_string_view defaultView1; + constexpr AZStd::basic_string_view defaultView2; static_assert(defaultView1 == defaultView2, "string_view constructor should be constexpr"); } @@ -1549,7 +1544,7 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view charTView1(compileTimeString); + constexpr AZStd::basic_string_view charTView1(compileTimeString); static_assert(charTView1.size() == 10, "string_view constructor should be constexpr"); // non-null terminated compile time string constexpr const TypeParam* compileTimeString2 = []() constexpr -> const TypeParam* @@ -1565,7 +1560,7 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view charTViewWithLength(compileTimeString2, 7); + constexpr AZStd::basic_string_view charTViewWithLength(compileTimeString2, 7); static_assert(charTViewWithLength.size() == 7, "string_view constructor should be constexpr"); } @@ -1585,8 +1580,8 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view copyView1(compileTimeString); - constexpr basic_string_view copyView2(copyView1); + constexpr AZStd::basic_string_view copyView1(compileTimeString); + constexpr AZStd::basic_string_view copyView2(copyView1); static_assert(copyView1 == copyView2, "string_view constructor should be constexpr"); } @@ -1606,8 +1601,8 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view assignView1(compileTimeString1); - auto assignment_test_func = [](basic_string_view sourceView) constexpr -> basic_string_view + constexpr AZStd::basic_string_view assignView1(compileTimeString1); + auto assignment_test_func = [](AZStd::basic_string_view sourceView) constexpr -> AZStd::basic_string_view { constexpr const TypeParam* const compileTimeString2 = []() constexpr-> const TypeParam* { @@ -1622,7 +1617,7 @@ namespace UnitTest return {}; }(); - basic_string_view assignView2(compileTimeString2); + AZStd::basic_string_view assignView2(compileTimeString2); assignView2 = sourceView; return assignView2; }; @@ -1646,15 +1641,15 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view iteratorView(compileTimeString1); - constexpr typename basic_string_view::iterator beginIt = iteratorView.begin(); - constexpr typename basic_string_view::const_iterator cbeginIt = iteratorView.cbegin(); - constexpr typename basic_string_view::iterator endIt = iteratorView.end(); - constexpr typename basic_string_view::const_iterator cendIt = iteratorView.cend(); - constexpr typename basic_string_view::reverse_iterator rbeginIt = iteratorView.rbegin(); - constexpr typename basic_string_view::const_reverse_iterator crbeginIt = iteratorView.crbegin(); - constexpr typename basic_string_view::reverse_iterator rendIt = iteratorView.rend(); - constexpr typename basic_string_view::const_reverse_iterator crendIt = iteratorView.crend(); + constexpr AZStd::basic_string_view iteratorView(compileTimeString1); + constexpr typename AZStd::basic_string_view::iterator beginIt = iteratorView.begin(); + constexpr typename AZStd::basic_string_view::const_iterator cbeginIt = iteratorView.cbegin(); + constexpr typename AZStd::basic_string_view::iterator endIt = iteratorView.end(); + constexpr typename AZStd::basic_string_view::const_iterator cendIt = iteratorView.cend(); + constexpr typename AZStd::basic_string_view::reverse_iterator rbeginIt = iteratorView.rbegin(); + constexpr typename AZStd::basic_string_view::const_reverse_iterator crbeginIt = iteratorView.crbegin(); + constexpr typename AZStd::basic_string_view::reverse_iterator rendIt = iteratorView.rend(); + constexpr typename AZStd::basic_string_view::const_reverse_iterator crendIt = iteratorView.crend(); static_assert(beginIt != endIt, "begin and iterators should be different"); static_assert(cbeginIt != cendIt, "begin and iterators should be different"); static_assert(rbeginIt != rendIt, "begin and iterators should be different"); @@ -1679,7 +1674,7 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view elementView1(compileTimeString1); + constexpr AZStd::basic_string_view elementView1(compileTimeString1); static_assert(elementView1[4] == 'o', "character at index 4 in string_view should be 'o'"); static_assert(elementView1.at(5) == 'W', "character at index 5 in string_view should be 'W'"); } @@ -1700,7 +1695,7 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view elementView1(compileTimeString1); + constexpr AZStd::basic_string_view elementView1(compileTimeString1); static_assert(elementView1.front() == 'H', "Fourth character in string_view should be 'H'"); static_assert(elementView1.back() == 'd', "Fifth character in string_view should be 'd'"); } @@ -1734,8 +1729,8 @@ namespace UnitTest return {}; }(); - static constexpr basic_string_view elementView1(compileTimeString1); - static constexpr basic_string_view elementView2(compileTimeString2); + static constexpr AZStd::basic_string_view elementView1(compileTimeString1); + static constexpr AZStd::basic_string_view elementView2(compileTimeString2); static_assert(elementView1.data(), "string_view.data() should be non-nullptr"); static_assert(elementView2.data(), "string_view.data() should be non-nullptr"); } @@ -1756,7 +1751,7 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view sizeView1(compileTimeString1); + constexpr AZStd::basic_string_view sizeView1(compileTimeString1); static_assert(sizeView1.size() == sizeView1.length(), "string_views size and length function should return the same value"); static_assert(!sizeView1.empty(), "string_views should not be empty"); static_assert(sizeView1.max_size() != 0, "string_views max_size should be greater than 0"); @@ -1770,21 +1765,21 @@ namespace UnitTest { return "HelloWorld"; }; - constexpr basic_string_view modifierView("HelloWorld"); + constexpr AZStd::basic_string_view modifierView("HelloWorld"); // A constexpr lambda is used to evaluate non constexpr string_view instances' member functions which // have been marked as constexpr at compile time // The google test function being run is not a constexpr function and therefore will evaulate // non-constexpr string_view variables at runtime. This would cause static_assert to state // that the expression is evaluated at runtime - auto remove_prefix_test_func = [](basic_string_view sourceView) constexpr -> basic_string_view + auto remove_prefix_test_func = [](AZStd::basic_string_view sourceView) constexpr -> AZStd::basic_string_view { - basic_string_view lstripView(sourceView); + AZStd::basic_string_view lstripView(sourceView); lstripView.remove_prefix(5); return lstripView; }; - auto remove_suffix_test_func = [](basic_string_view sourceView) constexpr -> basic_string_view + auto remove_suffix_test_func = [](AZStd::basic_string_view sourceView) constexpr -> AZStd::basic_string_view { - basic_string_view rstripView(sourceView); + AZStd::basic_string_view rstripView(sourceView); rstripView.remove_suffix(5); return rstripView; }; @@ -1801,8 +1796,8 @@ namespace UnitTest return "HelloWorld"; }; constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();; - constexpr basic_string_view fullView(compileTimeString1); - auto substr_test_func = [](basic_string_view sourceView) constexpr -> basic_string_view + constexpr AZStd::basic_string_view fullView(compileTimeString1); + auto substr_test_func = [](AZStd::basic_string_view sourceView) constexpr -> AZStd::basic_string_view { return sourceView.substr(3, 5); }; @@ -1818,7 +1813,7 @@ namespace UnitTest return "elloGovernor"; }; constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1(); - constexpr basic_string_view withView(compileTimeString1); + constexpr AZStd::basic_string_view withView(compileTimeString1); static_assert(withView.starts_with("ello"), "string_view should start with \"ello\""); // Regression in VS2017 15.8 and 15.9 where __builtin_memcmp fails in valid checks #if AZ_COMPILER_MSVC < 1915 && AZ_COMPILER_MSVC > 1916 @@ -1854,9 +1849,9 @@ namespace UnitTest TYPED_TEST(BasicStringViewConstexprFixture, StringView_FindOperationsAreConstexpr) { constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1; - constexpr basic_string_view quickFoxView(compileTimeString1); + constexpr AZStd::basic_string_view quickFoxView(compileTimeString1); constexpr const TypeParam* searchString = MakeSearchString; - constexpr basic_string_view searchView(searchString); + constexpr AZStd::basic_string_view searchView(searchString); constexpr const TypeParam* testString1 = MakeTestString1; constexpr const TypeParam* testString2 = MakeTestString2; @@ -1893,10 +1888,10 @@ namespace UnitTest static_assert(quickFoxView.find_last_of('o') == 42, "string_view find_last_of should result in index 42"); static_assert(quickFoxView.find_last_of(testString6) == 40, "string_view find_last_of should result in index 40"); static_assert(quickFoxView.find_last_of(testString7, 31) == 29, "string_view find_last_of should result in index 29"); - static_assert(quickFoxView.find_last_of(testString8, basic_string_view::npos, 1) == 7, "string_view find_last_of should result in index 7"); + static_assert(quickFoxView.find_last_of(testString8, AZStd::basic_string_view::npos, 1) == 7, "string_view find_last_of should result in index 7"); // find_first_not_of test - constexpr basic_string_view firstNotOfView(testString9); + constexpr AZStd::basic_string_view firstNotOfView(testString9); static_assert(quickFoxView.find_first_not_of(firstNotOfView) == 4, "string_view find_first_not_of should result in index 0"); static_assert(quickFoxView.find_first_not_of('t') == 1, "string_view find_first_not_of should result in index 1"); static_assert(quickFoxView.find_first_not_of(testString9) == 4, "string_view find_first_not_of should result in index 4"); @@ -1904,12 +1899,12 @@ namespace UnitTest static_assert(quickFoxView.find_first_not_of(testString9, 0, 1) == 1, "string_view find_first_not_of should result in index 1"); // find_last_not_of test - constexpr basic_string_view lastNotOfView(testString10); + constexpr AZStd::basic_string_view lastNotOfView(testString10); static_assert(quickFoxView.find_last_not_of(lastNotOfView) == 39, "string_view find_last_not_of should result in index 39"); static_assert(quickFoxView.find_last_not_of('g') == 42, "string_view find_last_not_of should result in index 42"); static_assert(quickFoxView.find_last_not_of(testString10) == 39, "string_view find_last_not_of should result in index 39"); static_assert(quickFoxView.find_last_not_of(testString10, 27) == 24, "string_view find_last_not_of should result in index 24"); - static_assert(quickFoxView.find_last_not_of(testString10, basic_string_view::npos, 1) == 43, "string_view find_last_not_of should result in index 43"); + static_assert(quickFoxView.find_last_not_of(testString10, AZStd::basic_string_view::npos, 1) == 43, "string_view find_last_not_of should result in index 43"); } TEST_F(String, StringView_CompareIsConstexpr) @@ -1925,8 +1920,8 @@ namespace UnitTest }; constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1(); constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2(); - constexpr basic_string_view lhsView(compileTimeString1); - constexpr basic_string_view rhsView(compileTimeString2); + constexpr AZStd::basic_string_view lhsView(compileTimeString1); + constexpr AZStd::basic_string_view rhsView(compileTimeString2); static_assert(lhsView.compare(rhsView) > 0, R"("HelloWorld" > "HelloPearl")"); static_assert(lhsView.compare(0, 5, rhsView) < 0, R"("Hello" < HelloPearl")"); static_assert(lhsView.compare(2, 3, rhsView, 2, 3) == 0, R"("llo" == llo")"); @@ -1943,7 +1938,7 @@ namespace UnitTest return "HelloWorld"; }; constexpr const TypeParam* compileTimeString1 = TestMakeCompileTimeString1(); - constexpr basic_string_view compareView(compileTimeString1); + constexpr AZStd::basic_string_view compareView(compileTimeString1); static_assert(compareView == "HelloWorld", "string_view operator== comparison has failed"); static_assert(compareView != "MadWorld", "string_view operator!= comparison has failed"); static_assert(compareView < "JelloWorld", "string_view operator< comparison has failed"); @@ -1954,7 +1949,7 @@ namespace UnitTest TYPED_TEST(BasicStringViewConstexprFixture, StringView_SwapIsConstexpr) { - auto swap_test_func = []() constexpr -> basic_string_view + auto swap_test_func = []() constexpr -> AZStd::basic_string_view { constexpr auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam* { @@ -1980,8 +1975,8 @@ namespace UnitTest }; constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1(); constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2(); - basic_string_view lhsView(compileTimeString1); - basic_string_view rhsView(compileTimeString2); + AZStd::basic_string_view lhsView(compileTimeString1); + AZStd::basic_string_view rhsView(compileTimeString2); lhsView.swap(rhsView); return lhsView; }; @@ -2014,13 +2009,14 @@ namespace UnitTest } }; constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1(); - constexpr basic_string_view hashView(compileTimeString1); - constexpr size_t compileHash = AZStd::hash>{}(hashView); + constexpr AZStd::basic_string_view hashView(compileTimeString1); + constexpr size_t compileHash = AZStd::hash>{}(hashView); static_assert(compileHash != 0, "Hash of \"HelloWorld\" should not be 0"); } TEST_F(String, StringView_UserLiteralsSucceed) { + using namespace AZStd::string_view_literals; constexpr auto charView{ "Test"_sv }; constexpr auto wcharView{ L"Super Test"_sv }; static_assert(charView == "Test", "char string literal should be \"Test\""); @@ -2291,21 +2287,21 @@ namespace UnitTest { AZStd::fixed_string<32> filter1; AZStd::string testValue{ "test" }; - EXPECT_FALSE(wildcard_match(filter1, testValue)); + EXPECT_FALSE(AZStd::wildcard_match(filter1, testValue)); } TEST_F(String, WildcardMatch_EmptyFilterWithEmptyValue_Succeeds) { AZStd::fixed_string<32> filter1; AZStd::fixed_string<32> emptyValue; - EXPECT_TRUE(wildcard_match(filter1, emptyValue)); + EXPECT_TRUE(AZStd::wildcard_match(filter1, emptyValue)); } TEST_F(String, WildcardMatch_AsteriskOnlyFilterWithEmptyValue_Succeeds) { const char* filter1{ "*" }; const char* filter2{ "**" }; const char* emptyValue{ "" }; - EXPECT_TRUE(wildcard_match(filter1, emptyValue)); - EXPECT_TRUE(wildcard_match(filter2, emptyValue)); + EXPECT_TRUE(AZStd::wildcard_match(filter1, emptyValue)); + EXPECT_TRUE(AZStd::wildcard_match(filter2, emptyValue)); } TEST_F(String, WildcardMatch_AsteriskQuestionMarkFilterWithEmptyValue_Failes) { @@ -2313,60 +2309,60 @@ namespace UnitTest const char* filter1{ "*?" }; const char* filter2{ "?*" }; const char* emptyValue{ "" }; - EXPECT_FALSE(wildcard_match(filter1, emptyValue)); - EXPECT_FALSE(wildcard_match(filter2, emptyValue)); + EXPECT_FALSE(AZStd::wildcard_match(filter1, emptyValue)); + EXPECT_FALSE(AZStd::wildcard_match(filter2, emptyValue)); } TEST_F(String, WildcardMatch_DotValue_Succeeds) { const char* filter1{ "?" }; const char* dotValue{ "." }; - EXPECT_TRUE(wildcard_match(filter1, dotValue)); + EXPECT_TRUE(AZStd::wildcard_match(filter1, dotValue)); } TEST_F(String, WildcardMatch_DoubleDotValue_Succeeds) { const char* filter1{ "??" }; const char* dotValue{ ".." }; - EXPECT_TRUE(wildcard_match(filter1, dotValue)); + EXPECT_TRUE(AZStd::wildcard_match(filter1, dotValue)); } TEST_F(String, WildcardMatch_GlobFilters_Succeeds) { const char* filter1{ "*" }; const char* filter2{ "*?" }; const char* filter3{ "?*" }; - EXPECT_TRUE(wildcard_match(filter1, "Hello")); - EXPECT_TRUE(wildcard_match(filter1, "?")); - EXPECT_TRUE(wildcard_match(filter1, "*")); - EXPECT_TRUE(wildcard_match(filter1, "Q")); + EXPECT_TRUE(AZStd::wildcard_match(filter1, "Hello")); + EXPECT_TRUE(AZStd::wildcard_match(filter1, "?")); + EXPECT_TRUE(AZStd::wildcard_match(filter1, "*")); + EXPECT_TRUE(AZStd::wildcard_match(filter1, "Q")); - EXPECT_TRUE(wildcard_match(filter2, "Hello")); - EXPECT_TRUE(wildcard_match(filter2, "?")); - EXPECT_TRUE(wildcard_match(filter2, "*")); - EXPECT_TRUE(wildcard_match(filter2, "Q")); + EXPECT_TRUE(AZStd::wildcard_match(filter2, "Hello")); + EXPECT_TRUE(AZStd::wildcard_match(filter2, "?")); + EXPECT_TRUE(AZStd::wildcard_match(filter2, "*")); + EXPECT_TRUE(AZStd::wildcard_match(filter2, "Q")); - EXPECT_TRUE(wildcard_match(filter3, "Hello")); - EXPECT_TRUE(wildcard_match(filter3, "?")); - EXPECT_TRUE(wildcard_match(filter3, "*")); - EXPECT_TRUE(wildcard_match(filter3, "Q")); + EXPECT_TRUE(AZStd::wildcard_match(filter3, "Hello")); + EXPECT_TRUE(AZStd::wildcard_match(filter3, "?")); + EXPECT_TRUE(AZStd::wildcard_match(filter3, "*")); + EXPECT_TRUE(AZStd::wildcard_match(filter3, "Q")); } TEST_F(String, WildcardMatch_NormalString_Succeeds) { constexpr AZStd::string_view jpgFilter{ "**/*.jpg" }; - EXPECT_FALSE(wildcard_match(jpgFilter, "Test.jpg")); - EXPECT_FALSE(wildcard_match(jpgFilter, "Test.jpfg")); - EXPECT_TRUE(wildcard_match(jpgFilter, "Images/Other.jpg")); - EXPECT_FALSE(wildcard_match(jpgFilter, "Pictures/Other.gif")); + EXPECT_FALSE(AZStd::wildcard_match(jpgFilter, "Test.jpg")); + EXPECT_FALSE(AZStd::wildcard_match(jpgFilter, "Test.jpfg")); + EXPECT_TRUE(AZStd::wildcard_match(jpgFilter, "Images/Other.jpg")); + EXPECT_FALSE(AZStd::wildcard_match(jpgFilter, "Pictures/Other.gif")); constexpr AZStd::string_view tempDirFilter{ "temp/*" }; - EXPECT_TRUE(wildcard_match(tempDirFilter, "temp/")); - EXPECT_TRUE(wildcard_match(tempDirFilter, "temp/f")); - EXPECT_FALSE(wildcard_match(tempDirFilter, "tem1/")); + EXPECT_TRUE(AZStd::wildcard_match(tempDirFilter, "temp/")); + EXPECT_TRUE(AZStd::wildcard_match(tempDirFilter, "temp/f")); + EXPECT_FALSE(AZStd::wildcard_match(tempDirFilter, "tem1/")); constexpr AZStd::string_view xmlFilter{ "test.xml" }; - EXPECT_TRUE(wildcard_match(xmlFilter, "Test.xml")); - EXPECT_TRUE(wildcard_match(xmlFilter, "test.xml")); - EXPECT_FALSE(wildcard_match(xmlFilter, "test.xmlschema")); - EXPECT_FALSE(wildcard_match(xmlFilter, "Xtest.xml")); + EXPECT_TRUE(AZStd::wildcard_match(xmlFilter, "Test.xml")); + EXPECT_TRUE(AZStd::wildcard_match(xmlFilter, "test.xml")); + EXPECT_FALSE(AZStd::wildcard_match(xmlFilter, "test.xmlschema")); + EXPECT_FALSE(AZStd::wildcard_match(xmlFilter, "Xtest.xml")); } TEST_F(String, WildcardMatchCase_CanBeCompileTimeEvaluated_Succeeds) @@ -2424,6 +2420,31 @@ namespace UnitTest EXPECT_EQ("oWord", eraseIfTest); } + TEST_F(String, StringWithStatelessAllocator_HasSizeOf_PointerPlus2IntTypes_Compiles) + { + // The expected size of a basic_string with a stateless allocator + // Is the size of the pointer (used for storing the memory address of the string) + // + the size of the string "size" member used to store the size of the string + // + the size of the string "capacity" member used to store the capacity of the string + size_t constexpr ExpectedBasicStringSize = sizeof(void*) + 2 * sizeof(size_t); + using StringStatelessAllocator = AZStd::basic_string, AZStd::stateless_allocator>; + static_assert(ExpectedBasicStringSize == sizeof(StringStatelessAllocator), + "Stateless allocator is counting against the size of the basic_string class" + " A change has made to break the empty base optimization of the basic_string class"); + } + + TEST_F(String, StringWithStatefulAllocator_HasSizeOf_PointerPlus2IntTypesPlusAllocator_Compiles) + { + // The expected size of a basic_string with a stateless allocator + // Is the size of the pointer (used for storing the memory address of the string) + // + the size of the string "size" member used to store the size of the string + // + the size of the string "capacity" member used to store the capacity of the string + size_t constexpr ExpectedBasicStringSize = sizeof(void*) + 2 * sizeof(size_t) + sizeof(AZStd::allocator); + static_assert(ExpectedBasicStringSize == sizeof(AZStd::string), + "Using Stateful allocator with basic_string class should result in a 32-byte string class" + " on 64-bit platforms "); + } + template class ImmutableStringFunctionsFixture : public ScopedAllocatorSetupFixture @@ -2473,5 +2494,296 @@ namespace UnitTest EXPECT_EQ(str, formatted); } -#endif // AZ_UNIT_TEST_SKIP_STD_STRING_TESTS } + +#if defined(HAVE_BENCHMARK) +namespace Benchmark +{ + class StringBenchmarkFixture + : public ::UnitTest::AllocatorsBenchmarkFixture + { + protected: + template + void SwapStringViaMemcpy(AZStd::basic_string& left, + AZStd::basic_string& right) + { + // Test Swapping the storage container for the string class + // Use aligned_storage to prevent constructors from slowing operation + AZStd::aligned_storage_for_t tempStorage; + ::memcpy(&tempStorage, &left.m_storage.first(), sizeof(left.m_storage.first())); + ::memcpy(&left.m_storage.first(), &right.m_storage.first(), sizeof(right.m_storage.first())); + ::memcpy(&right.m_storage.first(), &tempStorage, sizeof(tempStorage)); + } + + + template + void SwapStringViaPointerSizedSwaps(AZStd::basic_string& left, + AZStd::basic_string& right) + { + using String = AZStd::basic_string; + using PointerAlignedData = typename String::PointerAlignedData; + // Use pointer sized swaps to swap the string storage + auto& leftAlignedPointers = reinterpret_cast(left.m_storage.first()); + auto& rightAlignedPointers = reinterpret_cast(right.m_storage.first()); + constexpr size_t alignedPointerCount{ AZStd::size(PointerAlignedData{}.m_alignedValues) }; + for (size_t i = 0; i < alignedPointerCount; ++i) + { + AZStd::swap(leftAlignedPointers.m_alignedValues[i], rightAlignedPointers.m_alignedValues[i]); + } + } + }; + + BENCHMARK_F(StringBenchmarkFixture, BM_StringPointerSwapShortString)(benchmark::State& state) + { + AZStd::string test1{ "foo bar"}; + AZStd::string test2{ "bar foo" }; + for (auto _ : state) + { + SwapStringViaPointerSizedSwaps(test1, test2); + } + } + + BENCHMARK_F(StringBenchmarkFixture, BM_StringPointerSwapLongString)(benchmark::State& state) + { + AZStd::string test1{ "The brown quick wolf jumped over the hyperactive cat" }; + AZStd::string test2{ "The quick brown fox jumped over the lazy dog" }; + for (auto _ : state) + { + SwapStringViaPointerSizedSwaps(test1, test2); + } + } + + BENCHMARK_F(StringBenchmarkFixture, BM_StringMemcpySwapShortString)(benchmark::State& state) + { + AZStd::string test1{ "foo bar" }; + AZStd::string test2{ "bar foo" }; + for (auto _ : state) + { + SwapStringViaMemcpy(test1, test2); + } + } + + BENCHMARK_F(StringBenchmarkFixture, BM_StringMemcpySwapLongString)(benchmark::State& state) + { + AZStd::string test1{ "The brown quick wolf jumped over the hyperactive cat" }; + AZStd::string test2{ "The quick brown fox jumped over the lazy dog" }; + for (auto _ : state) + { + SwapStringViaMemcpy(test1, test2); + } + } + + template + class StringTemplateBenchmarkFixture + : public ::UnitTest::AllocatorsBenchmarkFixture + {}; + + // AZStd::string assign benchmarks + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignConstPointer_NullDelimited, AZStd::string)(benchmark::State& state) + { + AZStd::string sourceString(state.range(0), 'a'); + const char* sourceAddress = sourceString.c_str(); + + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(sourceAddress); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignConstPointer_NullDelimited) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignConstPointer_WithSize, AZStd::string)(benchmark::State& state) + { + AZStd::string sourceString(state.range(0), 'a'); + const char* sourceAddress = sourceString.c_str(); + const size_t sourceSize = sourceString.size(); + + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(sourceAddress, sourceSize); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignConstPointer_WithSize) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignFromIterators, AZStd::string)(benchmark::State& state) + { + AZStd::string sourceString(state.range(0), 'a'); + auto sourceBegin = sourceString.begin(); + auto sourceEnd = sourceString.end(); + + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(sourceBegin, sourceEnd); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignFromIterators) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignFromStringView, AZStd::string)(benchmark::State& state) + { + AZStd::string sourceString(state.range(0), 'a'); + AZStd::string_view sourceView(sourceString); + + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(sourceView); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignFromStringView) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignFromString_LValue, AZStd::string)(benchmark::State& state) + { + AZStd::string sourceString(state.range(0), 'a'); + + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(sourceString); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignFromString_LValue) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignFromString_RValue, AZStd::string)(benchmark::State& state) + { + AZStd::string sourceString(state.range(0), 'a'); + + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(AZStd::move(sourceString)); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignFromString_RValue) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignFromSingleCharacter, AZStd::string)(benchmark::State& state) + { + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(state.range(0), 'a'); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignFromSingleCharacter) + ->RangeMultiplier(2)->Range(8, 32); + + // AZStd::fixed_string assign benchmarks + // NOTE: This is a copy-and-paste of above because Google Benchmark doesn't support real templated benchmarks like Googletest + // https://github.com/google/benchmark/issues/541 + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignConstPointer_NullDelimited, AZStd::fixed_string<1024>)(benchmark::State& state) + { + AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); + const char* sourceAddress = sourceString.c_str(); + + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(sourceAddress); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignConstPointer_NullDelimited) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignConstPointer_WithSize, AZStd::fixed_string<1024>)(benchmark::State& state) + { + AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); + const char* sourceAddress = sourceString.c_str(); + const size_t sourceSize = sourceString.size(); + + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(sourceAddress, sourceSize); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignConstPointer_WithSize) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromIterators, AZStd::fixed_string<1024>)(benchmark::State& state) + { + AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); + auto sourceBegin = sourceString.begin(); + auto sourceEnd = sourceString.end(); + + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(sourceBegin, sourceEnd); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromIterators) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromStringView, AZStd::fixed_string<1024>)(benchmark::State& state) + { + AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); + AZStd::string_view sourceView(sourceString); + + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(sourceView); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromStringView) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromString_LValue, AZStd::fixed_string<1024>)(benchmark::State& state) + { + AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); + + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(sourceString); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromString_LValue) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromString_RValue, AZStd::fixed_string<1024>)(benchmark::State& state) + { + AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); + + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(AZStd::move(sourceString)); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromString_RValue) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromSingleCharacter, AZStd::fixed_string<1024>)(benchmark::State& state) + { + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(state.range(0), 'a'); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromSingleCharacter) + ->RangeMultiplier(2)->Range(8, 32); +} +#endif diff --git a/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp index 7ce0290ea3..e17d5c1fbf 100644 --- a/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp +++ b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp @@ -72,12 +72,11 @@ namespace AWSCoreUnitTest AWSCore::RequestBuilder requestBuilder{}; EXPECT_TRUE(request.parameters.BuildRequest(requestBuilder)); std::shared_ptr bodyContent = requestBuilder.GetBodyContent(); - EXPECT_TRUE(bodyContent != nullptr); + EXPECT_NE(nullptr, bodyContent); - AZStd::string bodyString; std::istreambuf_iterator eos; - bodyString = AZStd::string{ std::istreambuf_iterator(*bodyContent), eos }; - AZ_Printf("AWSAttributionServiceApiTest", bodyString.c_str()); - EXPECT_TRUE(bodyString.find(AZStd::string::format("{\"%s\":\"1.1\"", AwsAttributionAttributeKeyVersion)) != AZStd::string::npos); + AZStd::string bodyString{ std::istreambuf_iterator(*bodyContent), eos }; + AZ_Printf("AWSAttributionServiceApiTest", "%s", bodyString.c_str()); + EXPECT_TRUE(bodyString.contains(AZStd::string::format("{\"%s\":\"1.1\"", AwsAttributionAttributeKeyVersion))); } } diff --git a/Gems/AWSMetrics/Code/Tests/AWSMetricsServiceApiTest.cpp b/Gems/AWSMetrics/Code/Tests/AWSMetricsServiceApiTest.cpp index 8844e727b6..b7e7527b20 100644 --- a/Gems/AWSMetrics/Code/Tests/AWSMetricsServiceApiTest.cpp +++ b/Gems/AWSMetrics/Code/Tests/AWSMetricsServiceApiTest.cpp @@ -100,11 +100,10 @@ namespace AWSMetrics AWSCore::RequestBuilder requestBuilder{}; EXPECT_TRUE(request.parameters.BuildRequest(requestBuilder)); std::shared_ptr bodyContent = requestBuilder.GetBodyContent(); - EXPECT_TRUE(bodyContent != nullptr); + ASSERT_NE(nullptr, bodyContent); - AZStd::string bodyString; std::istreambuf_iterator eos; - bodyString = AZStd::string{ std::istreambuf_iterator(*bodyContent), eos }; - EXPECT_TRUE(bodyString.find(AZStd::string::format("{\"%s\":[{\"event_timestamp\":", AwsMetricsRequestParameterKeyEvents)) != AZStd::string::npos); + AZStd::string bodyString{ std::istreambuf_iterator(*bodyContent), eos }; + EXPECT_TRUE(bodyString.contains(AZStd::string::format("{\"%s\":[{\"event_timestamp\":", AwsMetricsRequestParameterKeyEvents))); } } diff --git a/Gems/EMotionFX/Code/Tests/AnimGraphEventTests.cpp b/Gems/EMotionFX/Code/Tests/AnimGraphEventTests.cpp index bd3fa10b24..11865674c8 100644 --- a/Gems/EMotionFX/Code/Tests/AnimGraphEventTests.cpp +++ b/Gems/EMotionFX/Code/Tests/AnimGraphEventTests.cpp @@ -49,7 +49,7 @@ namespace EMotionFX for (int i = 0; i < params.m_numStates; ++i) { AnimGraphNode* state = aznew AnimGraphMotionNode(); - state->SetName(AZStd::string(1, startChar + i).c_str()); + state->SetName(AZStd::string(1, static_cast(startChar + i)).c_str()); m_rootStateMachine->AddChildNode(state); AddTransitionWithTimeCondition(prevState, state, /*blendTime*/params.m_transitionBlendTime, /*countDownTime*/params.m_conditionCountDownTime); prevState = state; diff --git a/Gems/EMotionFX/Code/Tests/AnimGraphRefCountTests.cpp b/Gems/EMotionFX/Code/Tests/AnimGraphRefCountTests.cpp index 54f945d95f..c4e6462fff 100644 --- a/Gems/EMotionFX/Code/Tests/AnimGraphRefCountTests.cpp +++ b/Gems/EMotionFX/Code/Tests/AnimGraphRefCountTests.cpp @@ -90,7 +90,7 @@ namespace EMotionFX for (int i = 0; i < param.m_numStates; ++i) { AnimGraphBindPoseNode* state = aznew AnimGraphBindPoseNode(); - state->SetName(AZStd::string(1, startChar + i).c_str()); + state->SetName(AZStd::string(1, static_cast(startChar + i)).c_str()); m_rootStateMachine->AddChildNode(state); AddTransitionWithTimeCondition(prevState, state, /*blendTime*/param.m_blendTime, /*countDownTime*/param.m_countDownTime); prevState = state; From 93996bfb3fc97de17f9127686114ac4fece3fb55 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Mon, 10 Jan 2022 09:59:35 -0800 Subject: [PATCH 16/26] Moves LmbrCentral Test targets into a different folder to prevent MSB8028 (#6742) * Moves Test targets into a different folder to prevent MSB8028 Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * Simplifies an if that was affecting the whole file Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- Gems/LmbrCentral/Code/CMakeLists.txt | 73 +---------------- Gems/LmbrCentral/Code/Tests/CMakeLists.txt | 79 +++++++++++++++++++ .../lmbrcentral_editor_tests_files.cmake | 28 +++++++ .../{ => Tests}/lmbrcentral_mocks_files.cmake | 2 +- .../Code/Tests/lmbrcentral_tests_files.cmake | 32 ++++++++ .../Code/lmbrcentral_editor_tests_files.cmake | 28 ------- .../Code/lmbrcentral_tests_files.cmake | 29 ------- 7 files changed, 141 insertions(+), 130 deletions(-) create mode 100644 Gems/LmbrCentral/Code/Tests/CMakeLists.txt create mode 100644 Gems/LmbrCentral/Code/Tests/lmbrcentral_editor_tests_files.cmake rename Gems/LmbrCentral/Code/{ => Tests}/lmbrcentral_mocks_files.cmake (83%) create mode 100644 Gems/LmbrCentral/Code/Tests/lmbrcentral_tests_files.cmake delete mode 100644 Gems/LmbrCentral/Code/lmbrcentral_editor_tests_files.cmake delete mode 100644 Gems/LmbrCentral/Code/lmbrcentral_tests_files.cmake diff --git a/Gems/LmbrCentral/Code/CMakeLists.txt b/Gems/LmbrCentral/Code/CMakeLists.txt index 2df0bd0632..4401d3b752 100644 --- a/Gems/LmbrCentral/Code/CMakeLists.txt +++ b/Gems/LmbrCentral/Code/CMakeLists.txt @@ -121,75 +121,4 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) endif() -################################################################################ -# Tests -################################################################################ -if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) - ly_add_target( - NAME LmbrCentral.Mocks HEADERONLY - NAMESPACE Gem - FILES_CMAKE - lmbrcentral_mocks_files.cmake - INCLUDE_DIRECTORIES - INTERFACE - Mocks - ) - - ly_add_target( - NAME LmbrCentral.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} - NAMESPACE Gem - FILES_CMAKE - lmbrcentral_tests_files.cmake - lmbrcentral_shared_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - Source - Tests - BUILD_DEPENDENCIES - PRIVATE - AZ::AzTest - AZ::AzTestShared - Legacy::CryCommon - AZ::AzFramework - Gem::LmbrCentral.Static - Gem::LmbrCentral.Mocks - ) - ly_add_googletest( - NAME Gem::LmbrCentral.Tests - ) - - if (PAL_TRAIT_BUILD_HOST_TOOLS) - ly_add_target( - NAME LmbrCentral.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} - NAMESPACE Gem - FILES_CMAKE - lmbrcentral_editor_tests_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - . - Source - Tests - COMPILE_DEFINITIONS - PRIVATE - LMBR_CENTRAL_EDITOR - BUILD_DEPENDENCIES - PRIVATE - 3rdParty::Qt::Gui - 3rdParty::Qt::Widgets - Legacy::CryCommon - Legacy::Editor.Headers - AZ::AzTest - AZ::AzCore - AZ::AzTestShared - AZ::AzToolsFramework - AZ::AzToolsFrameworkTestCommon - AZ::AssetBuilderSDK - AZ::AzManipulatorTestFramework.Static - Gem::LmbrCentral.Static - Gem::LmbrCentral.Editor.Static - ) - ly_add_googletest( - NAME Gem::LmbrCentral.Editor.Tests - ) - endif() -endif() +add_subdirectory(Tests) diff --git a/Gems/LmbrCentral/Code/Tests/CMakeLists.txt b/Gems/LmbrCentral/Code/Tests/CMakeLists.txt new file mode 100644 index 0000000000..52928cc5db --- /dev/null +++ b/Gems/LmbrCentral/Code/Tests/CMakeLists.txt @@ -0,0 +1,79 @@ +# +# 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(NOT PAL_TRAIT_BUILD_TESTS_SUPPORTED) + return() +endif() + +ly_add_target( + NAME LmbrCentral.Mocks HEADERONLY + NAMESPACE Gem + FILES_CMAKE + lmbrcentral_mocks_files.cmake + INCLUDE_DIRECTORIES + INTERFACE + ../Mocks +) + +ly_add_target( + NAME LmbrCentral.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + lmbrcentral_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + ../Source + . + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTest + AZ::AzTestShared + Legacy::CryCommon + AZ::AzFramework + Gem::LmbrCentral.Static + Gem::LmbrCentral.Mocks +) +ly_add_googletest( + NAME Gem::LmbrCentral.Tests +) + +if (PAL_TRAIT_BUILD_HOST_TOOLS) + ly_add_target( + NAME LmbrCentral.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + lmbrcentral_editor_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + .. + ../Source + . + COMPILE_DEFINITIONS + PRIVATE + LMBR_CENTRAL_EDITOR + BUILD_DEPENDENCIES + PRIVATE + 3rdParty::Qt::Gui + 3rdParty::Qt::Widgets + Legacy::CryCommon + Legacy::Editor.Headers + AZ::AzTest + AZ::AzCore + AZ::AzTestShared + AZ::AzToolsFramework + AZ::AzToolsFrameworkTestCommon + AZ::AssetBuilderSDK + AZ::AzManipulatorTestFramework.Static + Gem::LmbrCentral.Static + Gem::LmbrCentral.Editor.Static + ) + ly_add_googletest( + NAME Gem::LmbrCentral.Editor.Tests + ) +endif() + diff --git a/Gems/LmbrCentral/Code/Tests/lmbrcentral_editor_tests_files.cmake b/Gems/LmbrCentral/Code/Tests/lmbrcentral_editor_tests_files.cmake new file mode 100644 index 0000000000..72a26c2884 --- /dev/null +++ b/Gems/LmbrCentral/Code/Tests/lmbrcentral_editor_tests_files.cmake @@ -0,0 +1,28 @@ +# +# 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 + LmbrCentralEditorTest.cpp + LmbrCentralReflectionTest.h + LmbrCentralReflectionTest.cpp + EditorBoxShapeComponentTests.cpp + EditorSphereShapeComponentTests.cpp + EditorCapsuleShapeComponentTests.cpp + EditorCompoundShapeComponentTests.cpp + EditorCylinderShapeComponentTests.cpp + EditorPolygonPrismShapeComponentTests.cpp + EditorTubeShapeComponentTests.cpp + SpawnerComponentTest.cpp + Builders/CopyDependencyBuilderTest.cpp + Builders/SliceBuilderTests.cpp + Builders/LevelBuilderTest.cpp + Builders/LuaBuilderTests.cpp + Builders/SeedBuilderTests.cpp + ../Source/LmbrCentral.cpp + ../Source/LmbrCentralEditor.cpp +) diff --git a/Gems/LmbrCentral/Code/lmbrcentral_mocks_files.cmake b/Gems/LmbrCentral/Code/Tests/lmbrcentral_mocks_files.cmake similarity index 83% rename from Gems/LmbrCentral/Code/lmbrcentral_mocks_files.cmake rename to Gems/LmbrCentral/Code/Tests/lmbrcentral_mocks_files.cmake index c3a5cca3f8..1e510747f2 100644 --- a/Gems/LmbrCentral/Code/lmbrcentral_mocks_files.cmake +++ b/Gems/LmbrCentral/Code/Tests/lmbrcentral_mocks_files.cmake @@ -7,5 +7,5 @@ # set(FILES - Mocks/LmbrCentral/Shape/MockShapes.h + ../Mocks/LmbrCentral/Shape/MockShapes.h ) diff --git a/Gems/LmbrCentral/Code/Tests/lmbrcentral_tests_files.cmake b/Gems/LmbrCentral/Code/Tests/lmbrcentral_tests_files.cmake new file mode 100644 index 0000000000..1555ff53d8 --- /dev/null +++ b/Gems/LmbrCentral/Code/Tests/lmbrcentral_tests_files.cmake @@ -0,0 +1,32 @@ +# +# 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 + AudioComponentTests.cpp + AxisAlignedBoxShapeTest.cpp + BoxShapeTest.cpp + BundlingSystemComponentTests.cpp + SphereShapeTest.cpp + CylinderShapeTest.cpp + CapsuleShapeTest.cpp + PolygonPrismShapeTest.cpp + QuadShapeTest.cpp + TubeShapeTest.cpp + LmbrCentralReflectionTest.h + LmbrCentralReflectionTest.cpp + LmbrCentralTest.cpp + ShapeGeometryUtilTest.cpp + SpawnerComponentTest.cpp + SplineComponentTests.cpp + DiskShapeTest.cpp + ReferenceShapeTests.cpp + ../Source/LmbrCentral.cpp + ../Source/Ai/NavigationComponent.cpp + ../Source/Scripting/SpawnerComponent.cpp + ../Source/Shape/TubeShape.cpp +) diff --git a/Gems/LmbrCentral/Code/lmbrcentral_editor_tests_files.cmake b/Gems/LmbrCentral/Code/lmbrcentral_editor_tests_files.cmake deleted file mode 100644 index afba79e566..0000000000 --- a/Gems/LmbrCentral/Code/lmbrcentral_editor_tests_files.cmake +++ /dev/null @@ -1,28 +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 -# -# - -set(FILES - Tests/LmbrCentralEditorTest.cpp - Tests/LmbrCentralReflectionTest.h - Tests/LmbrCentralReflectionTest.cpp - Tests/EditorBoxShapeComponentTests.cpp - Tests/EditorSphereShapeComponentTests.cpp - Tests/EditorCapsuleShapeComponentTests.cpp - Tests/EditorCompoundShapeComponentTests.cpp - Tests/EditorCylinderShapeComponentTests.cpp - Tests/EditorPolygonPrismShapeComponentTests.cpp - Tests/EditorTubeShapeComponentTests.cpp - Tests/SpawnerComponentTest.cpp - Tests/Builders/CopyDependencyBuilderTest.cpp - Tests/Builders/SliceBuilderTests.cpp - Tests/Builders/LevelBuilderTest.cpp - Tests/Builders/LuaBuilderTests.cpp - Tests/Builders/SeedBuilderTests.cpp - Source/LmbrCentral.cpp - Source/LmbrCentralEditor.cpp -) diff --git a/Gems/LmbrCentral/Code/lmbrcentral_tests_files.cmake b/Gems/LmbrCentral/Code/lmbrcentral_tests_files.cmake deleted file mode 100644 index 97e5d87829..0000000000 --- a/Gems/LmbrCentral/Code/lmbrcentral_tests_files.cmake +++ /dev/null @@ -1,29 +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 -# -# - -set(FILES - Tests/AudioComponentTests.cpp - Tests/AxisAlignedBoxShapeTest.cpp - Tests/BoxShapeTest.cpp - Tests/BundlingSystemComponentTests.cpp - Tests/SphereShapeTest.cpp - Tests/CylinderShapeTest.cpp - Tests/CapsuleShapeTest.cpp - Tests/PolygonPrismShapeTest.cpp - Tests/QuadShapeTest.cpp - Tests/TubeShapeTest.cpp - Tests/LmbrCentralReflectionTest.h - Tests/LmbrCentralReflectionTest.cpp - Tests/LmbrCentralTest.cpp - Tests/ShapeGeometryUtilTest.cpp - Tests/SpawnerComponentTest.cpp - Tests/SplineComponentTests.cpp - Tests/DiskShapeTest.cpp - Tests/ReferenceShapeTests.cpp - Source/LmbrCentral.cpp -) From 0f7e55cf59c633249a6e83a675a4334598856044 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Mon, 10 Jan 2022 10:00:29 -0800 Subject: [PATCH 17/26] Some fixes for paths with spaces (#6757) * Some fixes for paths with spaces Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * PR comments Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- .../tests/run_python_tests.bat | 59 ------------------- cmake/CommandExecution.cmake | 10 ++-- .../runtime_dependencies_common.cmake.in | 6 +- .../Linux/runtime_dependencies_linux.cmake.in | 4 +- cmake/Platform/Mac/InstallUtils_mac.cmake.in | 58 +++++++++--------- .../Mac/runtime_dependencies_mac.cmake.in | 50 ++++++++-------- python/python.cmd | 3 +- scripts/build/Platform/Linux/build_linux.sh | 2 +- scripts/build/Platform/Mac/build_mac.sh | 2 +- .../build/Platform/Windows/build_windows.cmd | 5 +- scripts/build/ci_build.py | 2 +- 11 files changed, 71 insertions(+), 130 deletions(-) delete mode 100644 Code/Tools/PythonBindingsExample/tests/run_python_tests.bat diff --git a/Code/Tools/PythonBindingsExample/tests/run_python_tests.bat b/Code/Tools/PythonBindingsExample/tests/run_python_tests.bat deleted file mode 100644 index 216f2dd0da..0000000000 --- a/Code/Tools/PythonBindingsExample/tests/run_python_tests.bat +++ /dev/null @@ -1,59 +0,0 @@ -@echo off - -REM -REM Copyright (c) Contributors to the Open 3D Engine Project. -REM For complete copyright and license terms please see the LICENSE at the root of this distribution. -REM -REM SPDX-License-Identifier: Apache-2.0 OR MIT -REM -REM - -PUSHD "%~dp0" - -SET CWD="%~dp0" -SET EXEPATH141="../../../../Bin64vc141/PythonBindingsExample.exe" -SET EXEPATH142="../../../../Bin64vc142/PythonBindingsExample.exe" -SET EXEPATH="" - -IF EXIST %EXEPATH141% ( - SET EXEPATH=%EXEPATH141% -) ELSE ( - IF EXIST %EXEPATH142% ( - SET EXEPATH=%EXEPATH142% - ) ELSE ( - ECHO PythonBindingsExample.exe not found. - ) -) -IF /I %EXEPATH% EQU "" ( - ECHO [FAILED] Could not run tests since a build of PythonBindingsExample.exe is missing - GOTO exit_app -) - -ECHO Testing basics of tool Python bindings in %CWD% - -%EXEPATH% --file test_hello_tool.py -IF %ERRORLEVEL% EQU 0 ( - ECHO [WORKED] test_hello_tool.py -) ELSE ( - ECHO [FAILED] test_hello_tool.py with %ERRORLEVEL% - GOTO exit_app -) - -%EXEPATH% --file test_framework.py --arg entity -IF %ERRORLEVEL% EQU 0 ( - ECHO [WORKED] test_framework.py --arg entity -) ELSE ( - ECHO [FAILED] test_framework.py --arg entity with %ERRORLEVEL% - GOTO exit_app -) - -%EXEPATH% --file test_framework.py --arg math -IF %ERRORLEVEL% EQU 0 ( - ECHO [WORKED] test_framework.py --arg math -) ELSE ( - ECHO [FAILED] test_framework.py --arg math with %ERRORLEVEL% - GOTO exit_app -) - -:exit_app -POPD diff --git a/cmake/CommandExecution.cmake b/cmake/CommandExecution.cmake index 91f043ee9f..a8c21a22a2 100644 --- a/cmake/CommandExecution.cmake +++ b/cmake/CommandExecution.cmake @@ -38,13 +38,13 @@ endif() # Check for timestamp if(LY_TIMESTAMP_REFERENCE) - if(NOT EXISTS ${LY_TIMESTAMP_REFERENCE}) + if(NOT EXISTS "${LY_TIMESTAMP_REFERENCE}") message(FATAL_ERROR "File LY_TIMESTAMP_REFERENCE=${LY_TIMESTAMP_REFERENCE} does not exists") endif() if(NOT LY_TIMESTAMP_FILE) - set(LY_TIMESTAMP_FILE ${LY_TIMESTAMP_REFERENCE}.stamp) + set(LY_TIMESTAMP_FILE "${LY_TIMESTAMP_REFERENCE}.stamp") endif() - if(EXISTS ${LY_TIMESTAMP_FILE} AND NOT ${LY_TIMESTAMP_REFERENCE} IS_NEWER_THAN ${LY_TIMESTAMP_FILE}) + if(EXISTS "${LY_TIMESTAMP_FILE}" AND NOT "${LY_TIMESTAMP_REFERENCE}" IS_NEWER_THAN "${LY_TIMESTAMP_FILE}") # Stamp newer, nothing to do return() endif() @@ -52,7 +52,7 @@ endif() if(LY_LOCK_FILE) # Lock the file - file(LOCK ${LY_LOCK_FILE} TIMEOUT 1200 RESULT_VARIABLE lock_result) + file(LOCK "${LY_LOCK_FILE}" TIMEOUT 1200 RESULT_VARIABLE lock_result) if(NOT ${lock_result} EQUAL 0) message(FATAL_ERROR "Lock failure ${lock_result}") endif() @@ -83,5 +83,5 @@ endif() if(LY_TIMESTAMP_REFERENCE) # Touch the timestamp file - file(TOUCH ${LY_TIMESTAMP_FILE}) + file(TOUCH "${LY_TIMESTAMP_FILE}") endif() diff --git a/cmake/Platform/Common/runtime_dependencies_common.cmake.in b/cmake/Platform/Common/runtime_dependencies_common.cmake.in index 8717710a3b..a2c3e26ed0 100644 --- a/cmake/Platform/Common/runtime_dependencies_common.cmake.in +++ b/cmake/Platform/Common/runtime_dependencies_common.cmake.in @@ -13,7 +13,7 @@ function(ly_copy source_file target_directory) cmake_path(APPEND target_file "${target_directory}" "${target_filename}") cmake_path(COMPARE "${source_file}" EQUAL "${target_file}" same_location) if(NOT ${same_location}) - file(LOCK ${target_file}.lock GUARD FUNCTION TIMEOUT 300) + file(LOCK "${target_file}.lock" GUARD FUNCTION TIMEOUT 300) file(SIZE "${source_file}" source_file_size) if(EXISTS "${target_file}") file(SIZE "${target_file}" target_file_size) @@ -24,11 +24,11 @@ function(ly_copy source_file target_directory) message(STATUS "Copying \"${source_file}\" to \"${target_directory}\"...") file(MAKE_DIRECTORY "${full_target_directory}") file(COPY "${source_file}" DESTINATION "${target_directory}" FILE_PERMISSIONS @LY_COPY_PERMISSIONS@ FOLLOW_SYMLINK_CHAIN) - file(TOUCH_NOCREATE ${target_file}) + file(TOUCH_NOCREATE "${target_file}") endif() endif() endfunction() @LY_COPY_COMMANDS@ -file(TOUCH @STAMP_OUTPUT_FILE@) +file(TOUCH "@STAMP_OUTPUT_FILE@") diff --git a/cmake/Platform/Linux/runtime_dependencies_linux.cmake.in b/cmake/Platform/Linux/runtime_dependencies_linux.cmake.in index 16445ef8d9..e5d56d1b56 100644 --- a/cmake/Platform/Linux/runtime_dependencies_linux.cmake.in +++ b/cmake/Platform/Linux/runtime_dependencies_linux.cmake.in @@ -14,7 +14,7 @@ function(ly_copy source_file target_directory) cmake_path(APPEND target_file "${target_directory}" "${target_filename}") cmake_path(COMPARE "${source_file}" EQUAL "${target_file}" same_location) if(NOT ${same_location}) - file(LOCK ${target_file}.lock GUARD FUNCTION TIMEOUT 300) + file(LOCK "${target_file}.lock" GUARD FUNCTION TIMEOUT 300) file(SIZE "${source_file}" source_file_size) if(EXISTS "${target_file}") file(SIZE "${target_file}" target_file_size) @@ -39,4 +39,4 @@ endfunction() @LY_COPY_COMMANDS@ -file(TOUCH @STAMP_OUTPUT_FILE@) +file(TOUCH "@STAMP_OUTPUT_FILE@") diff --git a/cmake/Platform/Mac/InstallUtils_mac.cmake.in b/cmake/Platform/Mac/InstallUtils_mac.cmake.in index 89ce4a59f2..3db9903e48 100644 --- a/cmake/Platform/Mac/InstallUtils_mac.cmake.in +++ b/cmake/Platform/Mac/InstallUtils_mac.cmake.in @@ -31,36 +31,36 @@ endfunction() function(fixup_python_framework framework_path) file(REMOVE_RECURSE - ${framework_path}/Versions/Current - ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/Headers - ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/Python - ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/python@LY_PYTHON_VERSION_MAJOR_MINOR@/test - ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/python@LY_PYTHON_VERSION_MAJOR_MINOR@/site-packages/scipy/io/tests - ${framework_path}/Python - ${framework_path}/Resources - ${framework_path}/Headers + "${framework_path}/Versions/Current" + "${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/Headers" + "${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/Python" + "${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/python@LY_PYTHON_VERSION_MAJOR_MINOR@/test" + "${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/python@LY_PYTHON_VERSION_MAJOR_MINOR@/site-packages/scipy/io/tests" + "${framework_path}/Python" + "${framework_path}/Resources" + "${framework_path}/Headers" ) file(GLOB_RECURSE exe_file_list "${framework_path}/**/*.exe") if(exe_file_list) - file(REMOVE_RECURSE ${exe_file_list}) + file(REMOVE_RECURSE "${exe_file_list}") endif() - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink include/python@LY_PYTHON_VERSION_MAJOR_MINOR@m Headers - WORKING_DIRECTORY ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@ + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink include/python@LY_PYTHON_VERSION_MAJOR_MINOR@m Headers + WORKING_DIRECTORY "${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@" ) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink @LY_PYTHON_VERSION_MAJOR_MINOR@ Current - WORKING_DIRECTORY ${framework_path}/Versions/ + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink @LY_PYTHON_VERSION_MAJOR_MINOR@ Current + WORKING_DIRECTORY "${framework_path}/Versions/" ) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Python Python - WORKING_DIRECTORY ${framework_path} + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink Versions/Current/Python Python + WORKING_DIRECTORY "${framework_path}" ) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Headers Headers - WORKING_DIRECTORY ${framework_path} + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink Versions/Current/Headers Headers + WORKING_DIRECTORY "${framework_path}" ) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Resources Resources - WORKING_DIRECTORY ${framework_path} + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink Versions/Current/Resources Resources + WORKING_DIRECTORY "${framework_path}" ) - file(CHMOD ${framework_path}/Versions/Current/Python + file(CHMOD "${framework_path}/Versions/Current/Python" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) @@ -72,7 +72,7 @@ function(codesign_file file entitlement_file) return() endif() - if(EXISTS ${entitlement_file}) + if(EXISTS "${entitlement_file}") execute_process(COMMAND "/usr/bin/codesign" "--force" "--sign" "@LY_CODE_SIGN_IDENTITY@" "--deep" "-o" "runtime" "--timestamp" "--entitlements" "${entitlement_file}" "${file}" TIMEOUT 300 @@ -108,8 +108,8 @@ function(codesign_python_framework_binaries framework_path) "${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/Resources/**") foreach(file ${files}) - if(NOT EXISTS ${file}) - file(REMOVE ${file}) + if(NOT EXISTS "${file}") + file(REMOVE "${file}") continue() endif() cmake_path(SET path_var "${file}") @@ -164,16 +164,16 @@ 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 - WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX} + 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) + 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") endfunction() function(ly_codesign_sdk) - codesign_file(${LY_INSTALL_PATH_ORIGINAL}/O3DE_SDK.app "none") + codesign_file("${LY_INSTALL_PATH_ORIGINAL}/O3DE_SDK.app" "none") endfunction() diff --git a/cmake/Platform/Mac/runtime_dependencies_mac.cmake.in b/cmake/Platform/Mac/runtime_dependencies_mac.cmake.in index 892a90640f..51e6e5830c 100644 --- a/cmake/Platform/Mac/runtime_dependencies_mac.cmake.in +++ b/cmake/Platform/Mac/runtime_dependencies_mac.cmake.in @@ -127,7 +127,7 @@ function(ly_copy source_file target_directory) if(NOT is_framework) # if it is a bundle, there is no contention about the files in the destination, each bundle target will copy everything # we dont want these files to invalidate the bundle and cause a new signature - file(LOCK ${target_file}.lock GUARD FUNCTION TIMEOUT 300) + file(LOCK "${target_file}.lock" GUARD FUNCTION TIMEOUT 300) file(SIZE "${source_file}" source_file_size) if(EXISTS "${target_file}") file(SIZE "${target_file}" target_file_size) @@ -176,35 +176,35 @@ if(@target_file_dir@ MATCHES ".app/Contents/MacOS") message(STATUS "Fixing ${bundle_path}/Contents/Frameworks/Python.framework...") list(APPEND fixup_bundle_ignore Python python3.7m python3.7) file(REMOVE_RECURSE - ${bundle_path}/Contents/Frameworks/Python.framework/Versions/Current - ${bundle_path}/Contents/Frameworks/Python.framework/Versions/3.7/Headers - ${bundle_path}/Contents/Frameworks/Python.framework/Versions/3.7/lib/Python - ${bundle_path}/Contents/Frameworks/Python.framework/Versions/3.7/lib/python3.7/test - ${bundle_path}/Contents/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/scipy/io/tests - ${bundle_path}/Contents/Frameworks/Python.framework/Python - ${bundle_path}/Contents/Frameworks/Python.framework/Resources - ${bundle_path}/Contents/Frameworks/Python.framework/Headers + "${bundle_path}/Contents/Frameworks/Python.framework/Versions/Current" + "${bundle_path}/Contents/Frameworks/Python.framework/Versions/3.7/Headers" + "${bundle_path}/Contents/Frameworks/Python.framework/Versions/3.7/lib/Python" + "${bundle_path}/Contents/Frameworks/Python.framework/Versions/3.7/lib/python3.7/test" + "${bundle_path}/Contents/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/scipy/io/tests" + "${bundle_path}/Contents/Frameworks/Python.framework/Python" + "${bundle_path}/Contents/Frameworks/Python.framework/Resources" + "${bundle_path}/Contents/Frameworks/Python.framework/Headers" ) file(GLOB_RECURSE exe_file_list "${bundle_path}/Contents/Frameworks/Python.framework/**/*.exe") if(exe_file_list) - file(REMOVE_RECURSE ${exe_file_list}) + file(REMOVE_RECURSE "${exe_file_list}") endif() - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink include/python3.7m Headers - WORKING_DIRECTORY ${bundle_path}/Contents/Frameworks/Python.framework/Versions/3.7 + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink include/python3.7m Headers + WORKING_DIRECTORY "${bundle_path}/Contents/Frameworks/Python.framework/Versions/3.7" ) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink 3.7 Current - WORKING_DIRECTORY ${bundle_path}/Contents/Frameworks/Python.framework/Versions/ + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink 3.7 Current + WORKING_DIRECTORY "${bundle_path}/Contents/Frameworks/Python.framework/Versions/" ) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Python Python - WORKING_DIRECTORY ${bundle_path}/Contents/Frameworks/Python.framework + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink Versions/Current/Python Python + WORKING_DIRECTORY "${bundle_path}/Contents/Frameworks/Python.framework" ) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Headers Headers - WORKING_DIRECTORY ${bundle_path}/Contents/Frameworks/Python.framework + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink Versions/Current/Headers Headers + WORKING_DIRECTORY "${bundle_path}/Contents/Frameworks/Python.framework" ) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Resources Resources - WORKING_DIRECTORY ${bundle_path}/Contents/Frameworks/Python.framework + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink Versions/Current/Resources Resources + WORKING_DIRECTORY "${bundle_path}/Contents/Frameworks/Python.framework" ) - file(CHMOD ${bundle_path}/Contents/Frameworks/Python.framework/Versions/Current/Python + file(CHMOD "${bundle_path}/Contents/Frameworks/Python.framework/Versions/Current/Python" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif() @@ -215,8 +215,8 @@ if(@target_file_dir@ MATCHES ".app/Contents/MacOS") file(TOUCH "${fixup_timestamp_file}") # fixup bundle ends up removing the rpath of dxc (despite we exclude it) - if(EXISTS ${bundle_path}/Contents/MacOS/Builders/DirectXShaderCompiler/bin/dxc-3.7) - execute_process(COMMAND ${LY_INSTALL_NAME_TOOL} -add_rpath @executable_path/../lib ${bundle_path}/Contents/MacOS/Builders/DirectXShaderCompiler/bin/dxc-3.7) + if(EXISTS "${bundle_path}/Contents/MacOS/Builders/DirectXShaderCompiler/bin/dxc-3.7") + execute_process(COMMAND $"{LY_INSTALL_NAME_TOOL}" -add_rpath @executable_path/../lib ${bundle_path}/Contents/MacOS/Builders/DirectXShaderCompiler/bin/dxc-3.7) endif() # misplaced .DS_Store files can cause signing to fail @@ -226,7 +226,7 @@ if(@target_file_dir@ MATCHES ".app/Contents/MacOS") "${bundle_path/}**/*.cstemp" ) if(remove_file_list) - file(REMOVE_RECURSE ${remove_file_list}) + file(REMOVE_RECURSE "${remove_file_list}") endif() endif() @@ -235,7 +235,7 @@ else() # Non-bundle case if(depends_on_python) # RPATH fix python - execute_process(COMMAND ${LY_INSTALL_NAME_TOOL} -change @rpath/Python @rpath/Python.framework/Versions/Current/Python @target_file@) + execute_process(COMMAND "${LY_INSTALL_NAME_TOOL}" -change @rpath/Python @rpath/Python.framework/Versions/Current/Python "@target_file@") endif() endif() diff --git a/python/python.cmd b/python/python.cmd index 70c79e6789..28138fd699 100644 --- a/python/python.cmd +++ b/python/python.cmd @@ -21,7 +21,8 @@ SET PYTHONHOME=%CMD_DIR%\runtime\python-3.7.10-rev2-windows\python IF EXIST "%PYTHONHOME%" GOTO PYTHONHOME_EXISTS -ECHO Could not find Python for Windows in %CMD_DIR%\.. +ECHO Python not found in %CMD_DIR% +ECHO Try running %CMD_DIR%\get_python.bat first. exit /B 1 :PYTHONHOME_EXISTS diff --git a/scripts/build/Platform/Linux/build_linux.sh b/scripts/build/Platform/Linux/build_linux.sh index 2f33827c5b..ce84ec1749 100755 --- a/scripts/build/Platform/Linux/build_linux.sh +++ b/scripts/build/Platform/Linux/build_linux.sh @@ -17,7 +17,7 @@ SOURCE_DIRECTORY=${PWD} pushd $OUTPUT_DIRECTORY LAST_CONFIGURE_CMD_FILE=ci_last_configure_cmd.txt -CONFIGURE_CMD="cmake ${SOURCE_DIRECTORY} ${CMAKE_OPTIONS} ${EXTRA_CMAKE_OPTIONS}" +CONFIGURE_CMD="cmake '${SOURCE_DIRECTORY}' ${CMAKE_OPTIONS} ${EXTRA_CMAKE_OPTIONS}" if [[ -n "$CMAKE_LY_PROJECTS" ]]; then CONFIGURE_CMD="${CONFIGURE_CMD} -DLY_PROJECTS='${CMAKE_LY_PROJECTS}'" fi diff --git a/scripts/build/Platform/Mac/build_mac.sh b/scripts/build/Platform/Mac/build_mac.sh index d6a66fc6c6..e0cb1ba889 100755 --- a/scripts/build/Platform/Mac/build_mac.sh +++ b/scripts/build/Platform/Mac/build_mac.sh @@ -17,7 +17,7 @@ SOURCE_DIRECTORY=${PWD} pushd $OUTPUT_DIRECTORY LAST_CONFIGURE_CMD_FILE=ci_last_configure_cmd.txt -CONFIGURE_CMD="cmake ${SOURCE_DIRECTORY} ${CMAKE_OPTIONS} ${EXTRA_CMAKE_OPTIONS}" +CONFIGURE_CMD="cmake '${SOURCE_DIRECTORY}' ${CMAKE_OPTIONS} ${EXTRA_CMAKE_OPTIONS}" if [[ -n "$CMAKE_LY_PROJECTS" ]]; then CONFIGURE_CMD="${CONFIGURE_CMD} -DLY_PROJECTS='${CMAKE_LY_PROJECTS}'" fi diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index 9e9b124adb..798550370f 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -8,8 +8,7 @@ REM REM SETLOCAL EnableDelayedExpansion - -CALL %~dp0env_windows.cmd +CALL "%~dp0env_windows.cmd" IF NOT EXIST "%OUTPUT_DIRECTORY%" ( MKDIR %OUTPUT_DIRECTORY%. @@ -29,7 +28,7 @@ REM Compute half the amount of processors so some jobs can run SET /a HALF_PROCESSORS = NUMBER_OF_PROCESSORS / 2 SET LAST_CONFIGURE_CMD_FILE=ci_last_configure_cmd.txt -SET CONFIGURE_CMD=cmake %SOURCE_DIRECTORY% %CMAKE_OPTIONS% %EXTRA_CMAKE_OPTIONS% +SET CONFIGURE_CMD=cmake "%SOURCE_DIRECTORY%" %CMAKE_OPTIONS% %EXTRA_CMAKE_OPTIONS% IF NOT "%CMAKE_LY_PROJECTS%"=="" ( SET CONFIGURE_CMD=!CONFIGURE_CMD! -DLY_PROJECTS="%CMAKE_LY_PROJECTS%" ) diff --git a/scripts/build/ci_build.py b/scripts/build/ci_build.py index c35a5e9dbf..78978349c3 100755 --- a/scripts/build/ci_build.py +++ b/scripts/build/ci_build.py @@ -88,7 +88,7 @@ def build(build_config_filename, build_platform, build_type): env_params[v] = build_params[v] print(' {} = {} {}'.format(v, env_params[v], '(environment override)' if existing_param else '')) print('--------------------------------------------------------------------------------', flush=True) - process_return = subprocess.run(build_cmd_path, cwd=cwd_dir, env=env_params) + process_return = subprocess.run([build_cmd_path], cwd=cwd_dir, env=env_params) print('--------------------------------------------------------------------------------') if process_return.returncode != 0: print('[ci_build] FAIL: Command {} returned {}'.format(build_cmd_path, process_return.returncode), flush=True) From 5c0ba0253d25a9a12f91d23e36a0dc10f95de0c7 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Mon, 10 Jan 2022 10:01:17 -0800 Subject: [PATCH 18/26] git.ignore cleanup (#6760) Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- .gitignore | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 9012ec7576..2820d1e1c1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,18 +2,14 @@ .vs/ .vscode/ __pycache__ -AssetProcessorTemp/** [Bb]uild/ [Oo]ut/** CMakeUserPresets.json [Cc]ache/ /[Ii]nstall/ -Editor/EditorEventLog.xml -Editor/EditorLayout.xml **/*egg-info/** **/*egg-link **/[Rr]estricted -UserSettings.xml [Uu]ser/ FrameCapture/** .DS_Store @@ -22,9 +18,6 @@ client*.cfg server*.cfg .mayaSwatches/ _savebackup/ -#Output folder for test results when running Automated Tests -TestResults/** *.swatches /imgui.ini -/scripts/project_manager/logs/ -/AutomatedTesting/Gem/PythonTests/scripting/TestResults + From e4c04c1915c05aad3e1305f2b6aeac015de9131e Mon Sep 17 00:00:00 2001 From: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> Date: Mon, 10 Jan 2022 12:03:32 -0600 Subject: [PATCH 19/26] Removing redundant Editor test Signed-off-by: jckand-amzn <82226555+jckand-amzn@users.noreply.github.com> --- .../Gem/PythonTests/smoke/CMakeLists.txt | 13 -- .../smoke/Editor_NewExistingLevels_Works.py | 143 ------------------ .../test_Editor_NewExistingLevels_Works.py | 34 ----- 3 files changed, 190 deletions(-) delete mode 100644 AutomatedTesting/Gem/PythonTests/smoke/Editor_NewExistingLevels_Works.py delete mode 100644 AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py diff --git a/AutomatedTesting/Gem/PythonTests/smoke/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/smoke/CMakeLists.txt index 5d0808adb6..69d411536f 100644 --- a/AutomatedTesting/Gem/PythonTests/smoke/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/smoke/CMakeLists.txt @@ -31,19 +31,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) Smoke ) - ly_add_pytest( - NAME AutomatedTesting::EditorTestWithGPU - TEST_REQUIRES gpu - PATH ${CMAKE_CURRENT_LIST_DIR}/test_Editor_NewExistingLevels_Works.py - TIMEOUT 100 - RUNTIME_DEPENDENCIES - AZ::AssetProcessor - AZ::PythonBindingsExample - Legacy::Editor - AutomatedTesting.GameLauncher - AutomatedTesting.Assets - ) - ly_add_pytest( NAME AutomatedTesting::GameLauncherWithGPU TEST_SUITE sandbox diff --git a/AutomatedTesting/Gem/PythonTests/smoke/Editor_NewExistingLevels_Works.py b/AutomatedTesting/Gem/PythonTests/smoke/Editor_NewExistingLevels_Works.py deleted file mode 100644 index 71956488fc..0000000000 --- a/AutomatedTesting/Gem/PythonTests/smoke/Editor_NewExistingLevels_Works.py +++ /dev/null @@ -1,143 +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 - - -Test Case Title: Create Test for UI apps- Editor -""" - - -class Tests(): - level_created = ("Level created", "Failed to create level") - entity_found = ("New Entity created in level", "Failed to create New Entity in level") - mesh_added = ("Mesh Component added", "Failed to add Mesh Component") - enter_game_mode = ("Game Mode successfully entered", "Failed to enter in Game Mode") - exit_game_mode = ("Game Mode successfully exited", "Failed to exit in Game Mode") - level_opened = ("Level opened successfully", "Failed to open level") - level_exported = ("Level exported successfully", "Failed to export level") - mesh_removed = ("Mesh Component removed", "Failed to remove Mesh Component") - entity_deleted = ("Entity deleted", "Failed to delete Entity") - level_edits_present = ("Level edits persist after saving", "Failed to save level edits after saving") - - -def Editor_NewExistingLevels_Works(): - """ - Summary: Perform the below operations on Editor - - 1) Launch & Close editor - 2) Create new level - 3) Saving and loading levels - 4) Level edits persist after saving - 5) Export Level - 6) Can switch to play mode (ctrl+g) and exit that - 7) Run editor python bindings test - 8) Create an Entity - 9) Delete an Entity - 10) Add a component to an Entity - - Expected Behavior: - All operations succeed and do not cause a crash - - Test Steps: - 1) Launch editor and Create a new level - 2) Create a new entity - 3) Add Mesh component - 4) Verify enter/exit game mode - 5) Save, Load and Export level - 6) Remove Mesh component - 7) Delete entity - 8) Open an existing level - 9) Create a new entity in an existing level - 10) Save, Load and Export an existing level and close editor - - Note: - - This test file must be called from the O3DE Editor command terminal - - Any passed and failed tests are written to the Editor.log file. - Parsing the file or running a log_monitor are required to observe the test results. - - :return: None - """ - - import os - import editor_python_test_tools.hydra_editor_utils as hydra - from editor_python_test_tools.utils import TestHelper as helper - from editor_python_test_tools.utils import Report - import azlmbr.bus as bus - import azlmbr.editor as editor - import azlmbr.legacy.general as general - import azlmbr.math as math - - # 1) Launch editor and Create a new level - helper.init_idle() - test_level_name = "temp_level" - general.create_level_no_prompt(test_level_name, 128, 1, 128, False) - helper.wait_for_condition(lambda: general.get_current_level_name() == test_level_name, 2.0) - Report.result(Tests.level_created, general.get_current_level_name() == test_level_name) - - # 2) Create a new entity - entity_position = math.Vector3(200.0, 200.0, 38.0) - new_entity = hydra.Entity("Entity1") - new_entity.create_entity(entity_position, []) - test_entity = hydra.find_entity_by_name("Entity1") - Report.result(Tests.entity_found, test_entity.IsValid()) - - # 3) Add Mesh component - new_entity.add_component("Mesh") - Report.result(Tests.mesh_added, hydra.has_components(new_entity.id, ["Mesh"])) - - # 4) Verify enter/exit game mode - helper.enter_game_mode(Tests.enter_game_mode) - helper.exit_game_mode(Tests.exit_game_mode) - - # 5) Save, Load and Export level - # Save Level - general.save_level() - # Open Level - general.open_level(test_level_name) - Report.result(Tests.level_opened, general.get_current_level_name() == test_level_name) - # Export Level - general.export_to_engine() - level_pak_file = os.path.join("AutomatedTesting", "Levels", test_level_name, "level.pak") - Report.result(Tests.level_exported, os.path.exists(level_pak_file)) - - # 6) Remove Mesh component - new_entity.remove_component("Mesh") - Report.result(Tests.mesh_removed, not hydra.has_components(new_entity.id, ["Mesh"])) - - # 7) Delete entity - editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntityById", new_entity.id) - test_entity = hydra.find_entity_by_name("Entity1") - Report.result(Tests.entity_deleted, len(test_entity) == 0) - - # 8) Open an existing level - general.open_level(test_level_name) - Report.result(Tests.level_opened, general.get_current_level_name() == test_level_name) - - # 9) Create a new entity in an existing level - entity_position = math.Vector3(200.0, 200.0, 38.0) - new_entity_2 = hydra.Entity("Entity2") - new_entity_2.create_entity(entity_position, []) - test_entity = hydra.find_entity_by_name("Entity2") - Report.result(Tests.entity_found, test_entity.IsValid()) - - # 10) Save, Load and Export an existing level - # Save Level - general.save_level() - # Open Level - general.open_level(test_level_name) - Report.result(Tests.level_opened, general.get_current_level_name() == test_level_name) - entity_id = hydra.find_entity_by_name(new_entity_2.name) - Report.result(Tests.level_edits_present, entity_id == new_entity_2.id) - # Export Level - general.export_to_engine() - level_pak_file = os.path.join("AutomatedTesting", "Levels", test_level_name, "level.pak") - Report.result(Tests.level_exported, os.path.exists(level_pak_file)) - - -if __name__ == "__main__": - - from editor_python_test_tools.utils import Report - - Report.start_test(Editor_NewExistingLevels_Works) diff --git a/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py b/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py deleted file mode 100644 index 5caf7744c4..0000000000 --- a/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py +++ /dev/null @@ -1,34 +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 - - -Test should run in both gpu and non gpu -""" - -import pytest -import os -from automatedtesting_shared.base import TestAutomationBase - -import ly_test_tools -import ly_test_tools.environment.file_system as file_system - - -@pytest.mark.SUITE_smoke -@pytest.mark.skipif(not ly_test_tools.WINDOWS, reason="Only succeeds on windows https://github.com/o3de/o3de/issues/5539") -@pytest.mark.parametrize("launcher_platform", ["windows_editor"]) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("level", ["temp_level"]) -class TestAutomation(TestAutomationBase): - def test_Editor_NewExistingLevels_Works(self, request, workspace, editor, level, project, launcher_platform): - def teardown(): - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - request.addfinalizer(teardown) - file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) - - from . import Editor_NewExistingLevels_Works as test_module - - self._run_test(request, workspace, editor, test_module, enable_prefab_system=False) From 5aa7d56f1a75a0f3bdc0fef569d0f421a868d1da Mon Sep 17 00:00:00 2001 From: LesaelR <89800757+LesaelR@users.noreply.github.com> Date: Mon, 10 Jan 2022 10:15:43 -0800 Subject: [PATCH 20/26] LYN-8935 Bundle Mode Test Update (#6606) * Updating Bundle_Mode_Tests to replace level.pak for .spawnable Signed-off-by: Rosario Cox * Removing old TestDependenciesLevel files and replacing for TestDepencenciesLevel.prefab Signed-off-by: Rosario Cox * Adding missing file Signed-off-by: Rosario Cox --- .../bundle_mode_tests.py | 5 +- .../LevelData/Environment.xml | 14 - .../LevelData/TerrainTexture.xml | 7 - .../LevelData/TimeOfDay.xml | 356 ----------- .../LevelData/VegetationMap.dat | 3 - .../TestDependenciesLevel/TerrainTexture.pak | 3 - .../TestDependenciesLevel.ly | 3 - .../TestDependenciesLevel.prefab | 555 ++++++++++++++++++ .../Levels/TestDependenciesLevel/filelist.xml | 6 - .../Levels/TestDependenciesLevel/level.pak | 3 - 10 files changed, 557 insertions(+), 398 deletions(-) delete mode 100644 AutomatedTesting/Levels/TestDependenciesLevel/LevelData/Environment.xml delete mode 100644 AutomatedTesting/Levels/TestDependenciesLevel/LevelData/TerrainTexture.xml delete mode 100644 AutomatedTesting/Levels/TestDependenciesLevel/LevelData/TimeOfDay.xml delete mode 100644 AutomatedTesting/Levels/TestDependenciesLevel/LevelData/VegetationMap.dat delete mode 100644 AutomatedTesting/Levels/TestDependenciesLevel/TerrainTexture.pak delete mode 100644 AutomatedTesting/Levels/TestDependenciesLevel/TestDependenciesLevel.ly create mode 100644 AutomatedTesting/Levels/TestDependenciesLevel/TestDependenciesLevel.prefab delete mode 100644 AutomatedTesting/Levels/TestDependenciesLevel/filelist.xml delete mode 100644 AutomatedTesting/Levels/TestDependenciesLevel/level.pak diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/bundle_mode_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/bundle_mode_tests.py index af92bb1773..b2252567b6 100644 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/bundle_mode_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/bundle_mode_tests.py @@ -23,12 +23,11 @@ from ..ap_fixtures.timeout_option_fixture import timeout_option_fixture as timeo @pytest.mark.SUITE_periodic @pytest.mark.parametrize('launcher_platform', ['windows_editor']) @pytest.mark.parametrize('project', ['AutomatedTesting']) -@pytest.mark.parametrize('level', ['auto_test']) +@pytest.mark.parametrize('level', ['TestDependenciesLevel']) class TestBundleMode(object): def test_bundle_mode_with_levels_mounts_bundles_correctly(self, request, editor, level, launcher_platform, asset_processor, workspace, bundler_batch_helper): - level_pak = os.path.join("levels", level, "level.pak") - + level_pak = os.path.join("levels", level, "TestDependenciesLevel.spawnable") bundles_folder = os.path.join(workspace.paths.project(), "Bundles") bundle_request_path = os.path.join(bundles_folder, "bundle.pak") bundle_result_path = os.path.join(bundles_folder, diff --git a/AutomatedTesting/Levels/TestDependenciesLevel/LevelData/Environment.xml b/AutomatedTesting/Levels/TestDependenciesLevel/LevelData/Environment.xml deleted file mode 100644 index 4ba36f66ae..0000000000 --- a/AutomatedTesting/Levels/TestDependenciesLevel/LevelData/Environment.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/AutomatedTesting/Levels/TestDependenciesLevel/LevelData/TerrainTexture.xml b/AutomatedTesting/Levels/TestDependenciesLevel/LevelData/TerrainTexture.xml deleted file mode 100644 index f43df05b22..0000000000 --- a/AutomatedTesting/Levels/TestDependenciesLevel/LevelData/TerrainTexture.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/AutomatedTesting/Levels/TestDependenciesLevel/LevelData/TimeOfDay.xml b/AutomatedTesting/Levels/TestDependenciesLevel/LevelData/TimeOfDay.xml deleted file mode 100644 index c5b404318e..0000000000 --- a/AutomatedTesting/Levels/TestDependenciesLevel/LevelData/TimeOfDay.xml +++ /dev/null @@ -1,356 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AutomatedTesting/Levels/TestDependenciesLevel/LevelData/VegetationMap.dat b/AutomatedTesting/Levels/TestDependenciesLevel/LevelData/VegetationMap.dat deleted file mode 100644 index dce5631cd0..0000000000 --- a/AutomatedTesting/Levels/TestDependenciesLevel/LevelData/VegetationMap.dat +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0e6a5435c928079b27796f6b202bbc2623e7e454244ddc099a3cadf33b7cb9e9 -size 63 diff --git a/AutomatedTesting/Levels/TestDependenciesLevel/TerrainTexture.pak b/AutomatedTesting/Levels/TestDependenciesLevel/TerrainTexture.pak deleted file mode 100644 index fe3604a050..0000000000 --- a/AutomatedTesting/Levels/TestDependenciesLevel/TerrainTexture.pak +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8739c76e681f900923b900c9df0ef75cf421d39cabb54650c4b9ad19b6a76d85 -size 22 diff --git a/AutomatedTesting/Levels/TestDependenciesLevel/TestDependenciesLevel.ly b/AutomatedTesting/Levels/TestDependenciesLevel/TestDependenciesLevel.ly deleted file mode 100644 index 95cc91cd6b..0000000000 --- a/AutomatedTesting/Levels/TestDependenciesLevel/TestDependenciesLevel.ly +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:825828fe7c183e765315f933a8b1eb25283739d34d62cb84c34e2dcb56591d6e -size 12415 diff --git a/AutomatedTesting/Levels/TestDependenciesLevel/TestDependenciesLevel.prefab b/AutomatedTesting/Levels/TestDependenciesLevel/TestDependenciesLevel.prefab new file mode 100644 index 0000000000..cf30cb178c --- /dev/null +++ b/AutomatedTesting/Levels/TestDependenciesLevel/TestDependenciesLevel.prefab @@ -0,0 +1,555 @@ +{ + "ContainerEntity": { + "Id": "Entity_[1146574390643]", + "Name": "Level", + "Components": { + "Component_[10641544592923449938]": { + "$type": "EditorInspectorComponent", + "Id": 10641544592923449938 + }, + "Component_[12039882709170782873]": { + "$type": "EditorOnlyEntityComponent", + "Id": 12039882709170782873 + }, + "Component_[12265484671603697631]": { + "$type": "EditorPendingCompositionComponent", + "Id": 12265484671603697631 + }, + "Component_[14126657869720434043]": { + "$type": "EditorEntitySortComponent", + "Id": 14126657869720434043, + "Child Entity Order": [ + "Entity_[1176639161715]" + ] + }, + "Component_[15230859088967841193]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 15230859088967841193, + "Parent Entity": "" + }, + "Component_[16239496886950819870]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 16239496886950819870 + }, + "Component_[5688118765544765547]": { + "$type": "EditorEntityIconComponent", + "Id": 5688118765544765547 + }, + "Component_[6545738857812235305]": { + "$type": "SelectionComponent", + "Id": 6545738857812235305 + }, + "Component_[7247035804068349658]": { + "$type": "EditorPrefabComponent", + "Id": 7247035804068349658 + }, + "Component_[9307224322037797205]": { + "$type": "EditorLockComponent", + "Id": 9307224322037797205 + }, + "Component_[9562516168917670048]": { + "$type": "EditorVisibilityComponent", + "Id": 9562516168917670048 + } + } + }, + "Entities": { + "Entity_[1155164325235]": { + "Id": "Entity_[1155164325235]", + "Name": "Sun", + "Components": { + "Component_[10440557478882592717]": { + "$type": "SelectionComponent", + "Id": 10440557478882592717 + }, + "Component_[13620450453324765907]": { + "$type": "EditorLockComponent", + "Id": 13620450453324765907 + }, + "Component_[2134313378593666258]": { + "$type": "EditorInspectorComponent", + "Id": 2134313378593666258 + }, + "Component_[234010807770404186]": { + "$type": "EditorVisibilityComponent", + "Id": 234010807770404186 + }, + "Component_[2970359110423865725]": { + "$type": "EditorEntityIconComponent", + "Id": 2970359110423865725 + }, + "Component_[3722854130373041803]": { + "$type": "EditorOnlyEntityComponent", + "Id": 3722854130373041803 + }, + "Component_[5992533738676323195]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 5992533738676323195 + }, + "Component_[7378860763541895402]": { + "$type": "AZ::Render::EditorDirectionalLightComponent", + "Id": 7378860763541895402, + "Controller": { + "Configuration": { + "Intensity": 1.0, + "CameraEntityId": "", + "ShadowFilterMethod": 1 + } + } + }, + "Component_[7892834440890947578]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 7892834440890947578, + "Parent Entity": "Entity_[1176639161715]", + "Transform Data": { + "Translate": [ + 0.0, + 0.0, + 13.487043380737305 + ], + "Rotate": [ + -76.13099670410156, + -0.847000002861023, + -15.8100004196167 + ] + } + }, + "Component_[8599729549570828259]": { + "$type": "EditorEntitySortComponent", + "Id": 8599729549570828259 + }, + "Component_[952797371922080273]": { + "$type": "EditorPendingCompositionComponent", + "Id": 952797371922080273 + } + } + }, + "Entity_[1159459292531]": { + "Id": "Entity_[1159459292531]", + "Name": "Ground", + "Components": { + "Component_[11701138785793981042]": { + "$type": "SelectionComponent", + "Id": 11701138785793981042 + }, + "Component_[12260880513256986252]": { + "$type": "EditorEntityIconComponent", + "Id": 12260880513256986252 + }, + "Component_[13711420870643673468]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 13711420870643673468 + }, + "Component_[138002849734991713]": { + "$type": "EditorOnlyEntityComponent", + "Id": 138002849734991713 + }, + "Component_[16578565737331764849]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 16578565737331764849, + "Parent Entity": "Entity_[1176639161715]" + }, + "Component_[16919232076966545697]": { + "$type": "EditorInspectorComponent", + "Id": 16919232076966545697 + }, + "Component_[5182430712893438093]": { + "$type": "EditorMaterialComponent", + "Id": 5182430712893438093 + }, + "Component_[5675108321710651991]": { + "$type": "AZ::Render::EditorMeshComponent", + "Id": 5675108321710651991, + "Controller": { + "Configuration": { + "ModelAsset": { + "assetId": { + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", + "subId": 277889906 + }, + "assetHint": "objects/groudplane/groundplane_512x512m.azmodel" + } + } + } + }, + "Component_[5681893399601237518]": { + "$type": "EditorEntitySortComponent", + "Id": 5681893399601237518 + }, + "Component_[592692962543397545]": { + "$type": "EditorPendingCompositionComponent", + "Id": 592692962543397545 + }, + "Component_[7090012899106946164]": { + "$type": "EditorLockComponent", + "Id": 7090012899106946164 + }, + "Component_[9410832619875640998]": { + "$type": "EditorVisibilityComponent", + "Id": 9410832619875640998 + } + } + }, + "Entity_[1163754259827]": { + "Id": "Entity_[1163754259827]", + "Name": "Camera", + "Components": { + "Component_[11895140916889160460]": { + "$type": "EditorEntityIconComponent", + "Id": 11895140916889160460 + }, + "Component_[16880285896855930892]": { + "$type": "{CA11DA46-29FF-4083-B5F6-E02C3A8C3A3D} EditorCameraComponent", + "Id": 16880285896855930892, + "Controller": { + "Configuration": { + "Field of View": 55.0, + "EditorEntityId": 3342481886060234850 + } + } + }, + "Component_[17187464423780271193]": { + "$type": "EditorLockComponent", + "Id": 17187464423780271193 + }, + "Component_[17495696818315413311]": { + "$type": "EditorEntitySortComponent", + "Id": 17495696818315413311 + }, + "Component_[18086214374043522055]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 18086214374043522055, + "Parent Entity": "Entity_[1176639161715]", + "Transform Data": { + "Translate": [ + -2.3000001907348633, + -3.9368600845336914, + 1.0 + ], + "Rotate": [ + -2.050307512283325, + 1.9552897214889526, + -43.623355865478516 + ] + } + }, + "Component_[18387556550380114975]": { + "$type": "SelectionComponent", + "Id": 18387556550380114975 + }, + "Component_[2654521436129313160]": { + "$type": "EditorVisibilityComponent", + "Id": 2654521436129313160 + }, + "Component_[5265045084611556958]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 5265045084611556958 + }, + "Component_[7169798125182238623]": { + "$type": "EditorPendingCompositionComponent", + "Id": 7169798125182238623 + }, + "Component_[7255796294953281766]": { + "$type": "GenericComponentWrapper", + "Id": 7255796294953281766, + "m_template": { + "$type": "FlyCameraInputComponent" + } + }, + "Component_[8866210352157164042]": { + "$type": "EditorInspectorComponent", + "Id": 8866210352157164042 + }, + "Component_[9129253381063760879]": { + "$type": "EditorOnlyEntityComponent", + "Id": 9129253381063760879 + } + } + }, + "Entity_[1168049227123]": { + "Id": "Entity_[1168049227123]", + "Name": "Grid", + "Components": { + "Component_[11443347433215807130]": { + "$type": "EditorEntityIconComponent", + "Id": 11443347433215807130 + }, + "Component_[11779275529534764488]": { + "$type": "SelectionComponent", + "Id": 11779275529534764488 + }, + "Component_[14249419413039427459]": { + "$type": "EditorInspectorComponent", + "Id": 14249419413039427459 + }, + "Component_[15448581635946161318]": { + "$type": "AZ::Render::EditorGridComponent", + "Id": 15448581635946161318, + "Controller": { + "Configuration": { + "primarySpacing": 4.0, + "primaryColor": [ + 0.501960813999176, + 0.501960813999176, + 0.501960813999176 + ], + "secondarySpacing": 0.5, + "secondaryColor": [ + 0.250980406999588, + 0.250980406999588, + 0.250980406999588 + ] + } + } + }, + "Component_[1843303322527297409]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 1843303322527297409 + }, + "Component_[380249072065273654]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 380249072065273654, + "Parent Entity": "Entity_[1176639161715]" + }, + "Component_[7476660583684339787]": { + "$type": "EditorPendingCompositionComponent", + "Id": 7476660583684339787 + }, + "Component_[7557626501215118375]": { + "$type": "EditorEntitySortComponent", + "Id": 7557626501215118375 + }, + "Component_[7984048488947365511]": { + "$type": "EditorVisibilityComponent", + "Id": 7984048488947365511 + }, + "Component_[8118181039276487398]": { + "$type": "EditorOnlyEntityComponent", + "Id": 8118181039276487398 + }, + "Component_[9189909764215270515]": { + "$type": "EditorLockComponent", + "Id": 9189909764215270515 + } + } + }, + "Entity_[1172344194419]": { + "Id": "Entity_[1172344194419]", + "Name": "Shader Ball", + "Components": { + "Component_[10789351944715265527]": { + "$type": "EditorOnlyEntityComponent", + "Id": 10789351944715265527 + }, + "Component_[12037033284781049225]": { + "$type": "EditorEntitySortComponent", + "Id": 12037033284781049225 + }, + "Component_[13759153306105970079]": { + "$type": "EditorPendingCompositionComponent", + "Id": 13759153306105970079 + }, + "Component_[14135560884830586279]": { + "$type": "EditorInspectorComponent", + "Id": 14135560884830586279 + }, + "Component_[16247165675903986673]": { + "$type": "EditorVisibilityComponent", + "Id": 16247165675903986673 + }, + "Component_[18082433625958885247]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 18082433625958885247 + }, + "Component_[6472623349872972660]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 6472623349872972660, + "Parent Entity": "Entity_[1176639161715]", + "Transform Data": { + "Rotate": [ + 0.0, + 0.10000000149011612, + 180.0 + ] + } + }, + "Component_[6495255223970673916]": { + "$type": "AZ::Render::EditorMeshComponent", + "Id": 6495255223970673916, + "Controller": { + "Configuration": { + "ModelAsset": { + "assetId": { + "guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}", + "subId": 281415304 + }, + "assetHint": "objects/shaderball/shaderball_default_1m.azmodel" + } + } + } + }, + "Component_[8056625192494070973]": { + "$type": "SelectionComponent", + "Id": 8056625192494070973 + }, + "Component_[8550141614185782969]": { + "$type": "EditorEntityIconComponent", + "Id": 8550141614185782969 + }, + "Component_[9439770997198325425]": { + "$type": "EditorLockComponent", + "Id": 9439770997198325425 + } + } + }, + "Entity_[1176639161715]": { + "Id": "Entity_[1176639161715]", + "Name": "Atom Default Environment", + "Components": { + "Component_[10757302973393310045]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 10757302973393310045, + "Parent Entity": "Entity_[1146574390643]" + }, + "Component_[14505817420424255464]": { + "$type": "EditorInspectorComponent", + "Id": 14505817420424255464, + "ComponentOrderEntryArray": [ + { + "ComponentId": 10757302973393310045 + } + ] + }, + "Component_[14988041764659020032]": { + "$type": "EditorLockComponent", + "Id": 14988041764659020032 + }, + "Component_[15808690248755038124]": { + "$type": "SelectionComponent", + "Id": 15808690248755038124 + }, + "Component_[15900837685796817138]": { + "$type": "EditorVisibilityComponent", + "Id": 15900837685796817138 + }, + "Component_[3298767348226484884]": { + "$type": "EditorOnlyEntityComponent", + "Id": 3298767348226484884 + }, + "Component_[4076975109609220594]": { + "$type": "EditorPendingCompositionComponent", + "Id": 4076975109609220594 + }, + "Component_[5679760548946028854]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 5679760548946028854 + }, + "Component_[5855590796136709437]": { + "$type": "EditorEntitySortComponent", + "Id": 5855590796136709437, + "Child Entity Order": [ + "Entity_[1155164325235]", + "Entity_[1180934129011]", + "Entity_[1172344194419]", + "Entity_[1168049227123]", + "Entity_[1163754259827]", + "Entity_[1159459292531]" + ] + }, + "Component_[9277695270015777859]": { + "$type": "EditorEntityIconComponent", + "Id": 9277695270015777859 + } + } + }, + "Entity_[1180934129011]": { + "Id": "Entity_[1180934129011]", + "Name": "Global Sky", + "Components": { + "Component_[11231930600558681245]": { + "$type": "AZ::Render::EditorHDRiSkyboxComponent", + "Id": 11231930600558681245, + "Controller": { + "Configuration": { + "CubemapAsset": { + "assetId": { + "guid": "{215E47FD-D181-5832-B1AB-91673ABF6399}", + "subId": 1000 + }, + "assetHint": "lightingpresets/highcontrast/goegap_4k_skyboxcm.exr.streamingimage" + } + } + } + }, + "Component_[11980494120202836095]": { + "$type": "SelectionComponent", + "Id": 11980494120202836095 + }, + "Component_[1428633914413949476]": { + "$type": "EditorLockComponent", + "Id": 1428633914413949476 + }, + "Component_[14936200426671614999]": { + "$type": "AZ::Render::EditorImageBasedLightComponent", + "Id": 14936200426671614999, + "Controller": { + "Configuration": { + "diffuseImageAsset": { + "assetId": { + "guid": "{3FD09945-D0F2-55C8-B9AF-B2FD421FE3BE}", + "subId": 3000 + }, + "assetHint": "lightingpresets/highcontrast/goegap_4k_iblglobalcm_ibldiffuse.exr.streamingimage" + }, + "specularImageAsset": { + "assetId": { + "guid": "{3FD09945-D0F2-55C8-B9AF-B2FD421FE3BE}", + "subId": 2000 + }, + "assetHint": "lightingpresets/highcontrast/goegap_4k_iblglobalcm_iblspecular.exr.streamingimage" + } + } + } + }, + "Component_[14994774102579326069]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 14994774102579326069 + }, + "Component_[15417479889044493340]": { + "$type": "EditorPendingCompositionComponent", + "Id": 15417479889044493340 + }, + "Component_[15826613364991382688]": { + "$type": "EditorEntitySortComponent", + "Id": 15826613364991382688 + }, + "Component_[1665003113283562343]": { + "$type": "EditorOnlyEntityComponent", + "Id": 1665003113283562343 + }, + "Component_[3704934735944502280]": { + "$type": "EditorEntityIconComponent", + "Id": 3704934735944502280 + }, + "Component_[5698542331457326479]": { + "$type": "EditorVisibilityComponent", + "Id": 5698542331457326479 + }, + "Component_[6644513399057217122]": { + "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent", + "Id": 6644513399057217122, + "Parent Entity": "Entity_[1176639161715]" + }, + "Component_[931091830724002070]": { + "$type": "EditorInspectorComponent", + "Id": 931091830724002070 + } + } + } + }, + "Instances": { + "Instance_[425258647110]": { + "Source": "assets/simple_pot_fbx.procprefab" + } + } +} \ No newline at end of file diff --git a/AutomatedTesting/Levels/TestDependenciesLevel/filelist.xml b/AutomatedTesting/Levels/TestDependenciesLevel/filelist.xml deleted file mode 100644 index b5164a4aee..0000000000 --- a/AutomatedTesting/Levels/TestDependenciesLevel/filelist.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/AutomatedTesting/Levels/TestDependenciesLevel/level.pak b/AutomatedTesting/Levels/TestDependenciesLevel/level.pak deleted file mode 100644 index dfba7fb4e3..0000000000 --- a/AutomatedTesting/Levels/TestDependenciesLevel/level.pak +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2611b691998640a0e802461f47b5b876f6832fbece62d34cc25da53e135e1c38 -size 44525 From 8e2e2d96c50bca2a9210f13df1fdd910193e80be Mon Sep 17 00:00:00 2001 From: LesaelR <89800757+LesaelR@users.noreply.github.com> Date: Mon, 10 Jan 2022 10:15:51 -0800 Subject: [PATCH 21/26] Updating asset_bundler_batch_tests to Prefab/Spawnables instead of Level.pak (#6679) * Replaced TestDependenciesLevel's level.pak for TestDependenciesLevel.prefab to fix asset_bundler_batch_tests failure Updated asset_bundler_batch_tests to reflect the update. Signed-off-by: Rosario Cox * Missed one of the .spawnables changes Signed-off-by: Rosario Cox --- .../asset_processor_tests/asset_bundler_batch_tests.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py index f5e5642573..f64427f6df 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py @@ -108,7 +108,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): """ helper = bundler_batch_helper seed_list = os.path.join(workspace.paths.engine_root(), "Assets", "Engine", "SeedAssetList.seed") # Engine seed list - asset = r"levels\testdependencieslevel\level.pak" + asset = r"levels\testdependencieslevel\testdependencieslevel.spawnable" # Create Asset list helper.call_assetLists( @@ -191,7 +191,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): """ helper = bundler_batch_helper seed_list = os.path.join(workspace.paths.engine_root(), "Assets", "Engine", "SeedAssetList.seed") # Engine seed list - asset = r"levels\testdependencieslevel\level.pak" + asset = r"levels\testdependencieslevel\testdependencieslevel.spawnable" # Useful bundle locations / names (2 for comparing contents) # fmt:off @@ -924,7 +924,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): # Create a seed file helper.call_seeds( seedListFile=helper["seed_list_file"], - addSeed=r"levels\testdependencieslevel\level.pak", + addSeed=r"levels\testdependencieslevel\testdependencieslevel.spawnable", platform="pc", ) @@ -947,9 +947,9 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): # Specifying platform but not "add" or "remove" should fail result, _ = helper.call_assetLists( assetListFile=helper["asset_info_file_request"], + allowOverwrites="", seedListFile=helper["seed_list_file"], platform="pc", - allowOverwrites="", ) assert result, "Overwriting with override threw an error" @@ -982,7 +982,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): request.addfinalizer(lambda: fs.delete([bundle_result_path], True, False)) bundles_folder = os.path.join(workspace.paths.project(), "Bundles") - level_pak = r"levels\testdependencieslevel\level.pak" + level_pak = r"levels\testdependencieslevel\testdependencieslevel.spawnable" bundle_request_path = os.path.join(bundles_folder, "bundle.pak") bundle_result_path = os.path.join(bundles_folder, helper.platform_file_name("bundle.pak", workspace.asset_processor_platform)) From df7a2fbd9d8e07f2d6786c1927eeddab2f05513e Mon Sep 17 00:00:00 2001 From: michabr <82236305+michabr@users.noreply.github.com> Date: Mon, 10 Jan 2022 12:11:15 -0800 Subject: [PATCH 22/26] Add better error handling for failed loading of the LyShine shader (#6761) Signed-off-by: abrmich --- .../DynamicDraw/DynamicDrawContext.cpp | 6 ++- Gems/LyShine/Code/Source/Draw2d.cpp | 47 +++++++++++-------- Gems/LyShine/Code/Source/UiRenderer.cpp | 4 +- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp index 3b3473a2da..47791d3002 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp @@ -141,6 +141,7 @@ namespace AZ void DynamicDrawContext::InitVertexFormat(const AZStd::vector& vertexChannels) { AZ_Assert(!m_initialized, "Can't call InitVertexFormat after context was initialized (EndInit was called)"); + AZ_Assert(m_pipelineState, "Can't call InitVertexFormat before InitShader is called with a valid shader"); m_perVertexDataSize = 0; RHI::InputStreamLayoutBuilder layoutBuilder; @@ -150,7 +151,10 @@ namespace AZ bufferBuilder->Channel(channel.m_channel, channel.m_format); m_perVertexDataSize += RHI::GetFormatSize(channel.m_format); } - m_pipelineState->InputStreamLayout() = layoutBuilder.End(); + if (m_pipelineState) + { + m_pipelineState->InputStreamLayout() = layoutBuilder.End(); + } } void DynamicDrawContext::InitDrawListTag(RHI::DrawListTag drawListTag) diff --git a/Gems/LyShine/Code/Source/Draw2d.cpp b/Gems/LyShine/Code/Source/Draw2d.cpp index 917c1bb168..2838ed4877 100644 --- a/Gems/LyShine/Code/Source/Draw2d.cpp +++ b/Gems/LyShine/Code/Source/Draw2d.cpp @@ -122,27 +122,34 @@ void CDraw2d::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) } m_dynamicDraw->EndInit(); - // Cache draw srg input indices for later use - static const char textureIndexName[] = "m_texture"; - static const char worldToProjIndexName[] = "m_worldToProj"; - AZ::Data::Instance drawSrg = m_dynamicDraw->NewDrawSrg(); - const AZ::RHI::ShaderResourceGroupLayout* layout = drawSrg->GetLayout(); - m_shaderData.m_imageInputIndex = layout->FindShaderInputImageIndex(AZ::Name(textureIndexName)); - AZ_Error("Draw2d", m_shaderData.m_imageInputIndex.IsValid(), "Failed to find shader input constant %s.", - textureIndexName); - m_shaderData.m_viewProjInputIndex = layout->FindShaderInputConstantIndex(AZ::Name(worldToProjIndexName)); - AZ_Error("Draw2d", m_shaderData.m_viewProjInputIndex.IsValid(), "Failed to find shader input constant %s.", - worldToProjIndexName); + // Check that the dynamic draw context has been initialized appropriately + if (m_dynamicDraw->IsReady()) + { + // Cache draw srg input indices for later use + static const char textureIndexName[] = "m_texture"; + static const char worldToProjIndexName[] = "m_worldToProj"; + AZ::Data::Instance drawSrg = m_dynamicDraw->NewDrawSrg(); + if (drawSrg) + { + const AZ::RHI::ShaderResourceGroupLayout* layout = drawSrg->GetLayout(); + m_shaderData.m_imageInputIndex = layout->FindShaderInputImageIndex(AZ::Name(textureIndexName)); + AZ_Error("Draw2d", m_shaderData.m_imageInputIndex.IsValid(), "Failed to find shader input constant %s.", + textureIndexName); + m_shaderData.m_viewProjInputIndex = layout->FindShaderInputConstantIndex(AZ::Name(worldToProjIndexName)); + AZ_Error("Draw2d", m_shaderData.m_viewProjInputIndex.IsValid(), "Failed to find shader input constant %s.", + worldToProjIndexName); + } - // Cache shader variants that will be used - AZ::RPI::ShaderOptionList shaderOptionsClamp; - shaderOptionsClamp.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true"))); - shaderOptionsClamp.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("true"))); - m_shaderData.m_shaderOptionsClamp = m_dynamicDraw->UseShaderVariant(shaderOptionsClamp); - AZ::RPI::ShaderOptionList shaderOptionsWrap; - shaderOptionsWrap.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("false"))); - shaderOptionsWrap.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("true"))); - m_shaderData.m_shaderOptionsWrap = m_dynamicDraw->UseShaderVariant(shaderOptionsWrap); + // Cache shader variants that will be used + AZ::RPI::ShaderOptionList shaderOptionsClamp; + shaderOptionsClamp.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true"))); + shaderOptionsClamp.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("true"))); + m_shaderData.m_shaderOptionsClamp = m_dynamicDraw->UseShaderVariant(shaderOptionsClamp); + AZ::RPI::ShaderOptionList shaderOptionsWrap; + shaderOptionsWrap.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("false"))); + shaderOptionsWrap.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("true"))); + m_shaderData.m_shaderOptionsWrap = m_dynamicDraw->UseShaderVariant(shaderOptionsWrap); + } } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/LyShine/Code/Source/UiRenderer.cpp b/Gems/LyShine/Code/Source/UiRenderer.cpp index acf17049ad..3b835dec92 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.cpp +++ b/Gems/LyShine/Code/Source/UiRenderer.cpp @@ -76,7 +76,7 @@ void UiRenderer::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) // Create a dynamic draw context for UI Canvas drawing for the scene m_dynamicDraw = CreateDynamicDrawContext(uiShader); - if (m_dynamicDraw) + if (m_dynamicDraw && m_dynamicDraw->IsReady()) { // Cache shader data such as input indices for later use CacheShaderData(m_dynamicDraw); @@ -85,7 +85,7 @@ void UiRenderer::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) } else { - AZ_Error(LogName, false, "Failed to create a dynamic draw context for LyShine. \ + AZ_Error(LogName, false, "Failed to create or initialize a dynamic draw context for LyShine. \ This can happen if the LyShine pass hasn't been added to the main render pipeline."); } } From 18ea4ba6a8c2646b074f236eb03feab3ec037777 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Mon, 10 Jan 2022 15:21:04 -0600 Subject: [PATCH 23/26] Added a CriticalAssetsCompiled Lifecycle event (#6469) The CriticalAssetsCompiled event can be handled to detect when the AssetProcessor has finished processing Critical Assets Also with the new event, an audit has been performed over all the locations where the AssetCatalogEventBus OnCatalogLoaded event was being handle to make sure it was the proper event to use. If the handler was actually examing the enumerating over the full catalog or querying all assets within the catalog, then it was a proper use. For handlers that were interested in a particular asset it was not Moreover added implementations of `OnCatalogAssetChanged` and `OnCatalogAssetAdded` to the FileTagComponent and the MaterialViewportComponent. Any applications which uses the AtomToolsApplication class(MaterialEditor, AtomSampleViewerStandalone, ShaderMangementConsole) now signals a "CriticalAssetsCompiled" lifecycle event as well as loads the "assetcatalog.xml" if it exists. The Launcher application signals the "CrticalAssetsCompiled" event and reloads the "assetcatalog.xml" for the ${project}.GameLauncher and ${project}.ServerLauncher in Launcher.cpp Finally the Editor signals the "CriticalAssetsCompiled" and reloads the "assetcatalog.xml" in CryEdit.cpp resolves #6093 Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- Code/Editor/CryEdit.cpp | 23 ++++- .../AzFramework/FileTag/FileTagComponent.cpp | 35 +++++-- .../AzFramework/FileTag/FileTagComponent.h | 2 + .../Spawnable/SpawnableSystemComponent.cpp | 38 ++++---- .../Spawnable/SpawnableSystemComponent.h | 10 +- Code/LauncherUnified/Launcher.cpp | 96 ++++++++++++------- .../SerializeContextTools/SliceConverter.cpp | 4 - .../Code/Source/BootstrapSystemComponent.cpp | 2 +- .../Atom/RPI.Public/RPISystemInterface.h | 3 +- .../Application/AtomToolsApplication.cpp | 19 +++- .../Viewport/MaterialViewportComponent.cpp | 53 +++++++++- .../Viewport/MaterialViewportComponent.h | 3 + .../EditorCommonFeaturesSystemComponent.cpp | 20 ++-- .../EditorCommonFeaturesSystemComponent.h | 5 +- .../Editor/MultiplayerEditorConnection.cpp | 2 +- .../NetworkEntity/NetworkSpawnableLibrary.cpp | 18 ++-- .../NetworkEntity/NetworkSpawnableLibrary.h | 6 +- Gems/PhysX/Code/Source/System/PhysXSystem.cpp | 4 +- Registry/application_lifecycle_events.setreg | 3 +- 19 files changed, 233 insertions(+), 113 deletions(-) diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index 77099761c1..1c4e22b99e 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -1352,8 +1352,27 @@ void CCryEditApp::CompileCriticalAssets() const } } assetsInQueueNotifcation.BusDisconnect(); - CCryEditApp::OutputStartupMessage(QString("Asset Processor is now ready.")); + // Signal the "CriticalAssetsCompiled" lifecycle event + // Also reload the "assetcatalog.xml" if it exists + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "CriticalAssetsCompiled", R"({})"); + // Reload the assetcatalog.xml at this point again + // Start Monitoring Asset changes over the network and load the AssetCatalog + auto LoadCatalog = [settingsRegistry](AZ::Data::AssetCatalogRequests* assetCatalogRequests) + { + if (AZ::IO::FixedMaxPath assetCatalogPath; + settingsRegistry->Get(assetCatalogPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder)) + { + assetCatalogPath /= "assetcatalog.xml"; + assetCatalogRequests->LoadCatalog(assetCatalogPath.c_str()); + } + }; + AZ::Data::AssetCatalogRequestBus::Broadcast(AZStd::move(LoadCatalog)); + } + + CCryEditApp::OutputStartupMessage(QString("Asset Processor is now ready.")); } bool CCryEditApp::ConnectToAssetProcessor() const @@ -1669,7 +1688,7 @@ bool CCryEditApp::InitInstance() return false; } - if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get()) + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "LegacySystemInterfaceCreated", R"({})"); } diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp b/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp index 1d09500415..f4831d1f8b 100644 --- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp @@ -97,19 +97,38 @@ namespace AzFramework AZStd::vector registeredAssetPaths; AZ::Data::AssetCatalogRequestBus::BroadcastResult(registeredAssetPaths, &AZ::Data::AssetCatalogRequests::GetRegisteredAssetPaths); - const char* dependencyXmlPattern = "*_dependencies.xml"; + constexpr const char* dependencyXmlPattern = "_dependencies.xml"; for (const AZStd::string& assetPath : registeredAssetPaths) { - if (!AZStd::wildcard_match(dependencyXmlPattern, assetPath.c_str())) + if (assetPath.ends_with(dependencyXmlPattern)) { - continue; - } - - if (!m_excludeFileQueryManager.get()->LoadEngineDependencies(assetPath)) - { - AZ_Error("ExcludeFileComponent", false, "Failed to add assets referenced from %s to the blocked list", assetPath.c_str()); + AZ_VerifyError("ExcludeFileComponent", m_excludeFileQueryManager.get()->LoadEngineDependencies(assetPath), + "Failed to add assets referenced from %s to the blocked list", assetPath.c_str()); } } } + + void ExcludeFileComponent::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) + { + // Reload any modified "_dependencies.xml" files + AZ::IO::Path assetPath; + auto GetAssetPath = [&assetId, &assetPath](AZ::Data::AssetCatalogRequests* assetCatalogRequests) + { + assetPath = assetCatalogRequests->GetAssetPathById(assetId); + }; + + AZ::Data::AssetCatalogRequestBus::Broadcast(AZStd::move(GetAssetPath)); + constexpr const char* dependencyXmlPattern = "_dependencies.xml"; + if (assetPath.Native().ends_with(dependencyXmlPattern)) + { + AZ_VerifyError("ExcludeFileComponent", m_excludeFileQueryManager.get()->LoadEngineDependencies(assetPath.Native()), + "Failed to add assets referenced from %s to the blocked list", assetPath.c_str()); + } + } + + void ExcludeFileComponent::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) + { + OnCatalogAssetChanged(assetId); + } } } diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.h b/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.h index d53e4e7a89..1bd0f46aa0 100644 --- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.h +++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.h @@ -65,6 +65,8 @@ namespace AzFramework void Deactivate() override; void OnCatalogLoaded(const char* catalogFile) override; + void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override; + void OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) override; static void Reflect(AZ::ReflectContext* context); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp index 6ca2f3a53a..4ba9c45a98 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -61,15 +62,6 @@ namespace AzFramework m_entitiesManager.ProcessQueue(SpawnableEntitiesManager::CommandQueuePriority::High); } - void SpawnableSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile) - { - if (!m_catalogAvailable) - { - m_catalogAvailable = true; - LoadRootSpawnableFromSettingsRegistry(); - } - } - uint64_t SpawnableSystemComponent::AssignRootSpawnable(AZ::Data::Asset rootSpawnable) { uint32_t generation = 0; @@ -157,20 +149,29 @@ namespace AzFramework // Register with AssetDatabase AZ_Assert(AZ::Data::AssetManager::IsReady(), "Spawnables can't be registered because the Asset Manager is not ready yet."); AZ::Data::AssetManager::Instance().RegisterHandler(&m_assetHandler, AZ::AzTypeInfo::Uuid()); - + // Register with AssetCatalog AZ::Data::AssetCatalogRequestBus::Broadcast( &AZ::Data::AssetCatalogRequestBus::Events::EnableCatalogForAsset, AZ::AzTypeInfo::Uuid()); AZ::Data::AssetCatalogRequestBus::Broadcast( &AZ::Data::AssetCatalogRequestBus::Events::AddExtension, Spawnable::FileExtension); - AssetCatalogEventBus::Handler::BusConnect(); + // Register for the CriticalAssetsCompiled lifecycle event to trigger the loading of the root spawnable + auto settingsRegistry = AZ::SettingsRegistry::Get(); + AZ_Assert(settingsRegistry, "Unable to change root spawnable callback because Settings Registry is not available."); + + auto LifecycleCallback = [this](AZStd::string_view, AZ::SettingsRegistryInterface::Type) + { + LoadRootSpawnableFromSettingsRegistry(); + }; + AZ::ComponentApplicationLifecycle::RegisterHandler(*settingsRegistry, m_criticalAssetsHandler, + AZStd::move(LifecycleCallback), "CriticalAssetsCompiled"); + + RootSpawnableNotificationBus::Handler::BusConnect(); AZ::TickBus::Handler::BusConnect(); - auto registry = AZ::SettingsRegistry::Get(); - AZ_Assert(registry, "Unable to change root spawnable callback because Settings Registry is not available."); - m_registryChangeHandler = registry->RegisterNotifier([this](AZStd::string_view path, AZ::SettingsRegistryInterface::Type /*type*/) + m_registryChangeHandler = settingsRegistry->RegisterNotifier([this](AZStd::string_view path, AZ::SettingsRegistryInterface::Type /*type*/) { if (path.starts_with(RootSpawnableRegistryKey)) { @@ -187,13 +188,14 @@ namespace AzFramework AZ::TickBus::Handler::BusDisconnect(); RootSpawnableNotificationBus::Handler::BusDisconnect(); - AssetCatalogEventBus::Handler::BusDisconnect(); + // Unregister Lifecycle event handler + m_criticalAssetsHandler = {}; - if (m_catalogAvailable) + if (m_rootSpawnableId.IsValid()) { ReleaseRootSpawnable(); - // The SpawnalbleSystemComponent needs to guarantee there's no more processing left to do by the + // The SpawnableSystemComponent needs to guarantee there's no more processing left to do by the // entity manager before it can safely destroy it on shutdown, but also to make sure that are no // more calls to the callback registered to the root spawnable as that accesses this component. m_rootSpawnableContainer.Clear(); @@ -210,8 +212,6 @@ namespace AzFramework void SpawnableSystemComponent::LoadRootSpawnableFromSettingsRegistry() { - AZ_Assert(m_catalogAvailable, "Attempting to load root spawnable while the catalog is not available yet."); - auto registry = AZ::SettingsRegistry::Get(); AZ_Assert(registry, "Unable to check for root spawnable because the Settings Registry is not available."); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h index ecd2a9b728..712cd1529d 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -25,7 +24,6 @@ namespace AzFramework : public AZ::Component , public AZ::TickBus::Handler , public AZ::SystemTickBus::Handler - , public AssetCatalogEventBus::Handler , public RootSpawnableInterface::Registrar , public RootSpawnableNotificationBus::Handler { @@ -63,12 +61,6 @@ namespace AzFramework void OnSystemTick() override; - // - // AssetCatalogEventBus - // - - void OnCatalogLoaded(const char* catalogFile) override; - // // RootSpawnableInterface // @@ -97,6 +89,6 @@ namespace AzFramework AZ::SettingsRegistryInterface::NotifyEventHandler m_registryChangeHandler; AZ::Data::AssetId m_rootSpawnableId; - bool m_catalogAvailable{ false }; + AZ::SettingsRegistryInterface::NotifyEventHandler m_criticalAssetsHandler; }; } // namespace AzFramework diff --git a/Code/LauncherUnified/Launcher.cpp b/Code/LauncherUnified/Launcher.cpp index ed01a67f39..9dfd6213c4 100644 --- a/Code/LauncherUnified/Launcher.cpp +++ b/Code/LauncherUnified/Launcher.cpp @@ -229,33 +229,65 @@ namespace O3DELauncher void CreateRemoteFileIO(); - bool ConnectToAssetProcessor() + // This function make sure the launcher has signaled the "CriticalAssetsCompiled" + // lifecycle event as well as to load the "assetcatalog.xml" file if it exists + void CompileCriticalAssets() { - bool connectedToAssetProcessor{}; - // When the AssetProcessor is already launched it should take less than a second to perform a connection - // but when the AssetProcessor needs to be launch it could take up to 15 seconds to have the AssetProcessor initialize - // and able to negotiate a connection when running a debug build - // and to negotiate a connection - // Setting the connectTimeout to 3 seconds if not set within the settings registry - - AzFramework::AssetSystem::ConnectionSettings connectionSettings; - AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings); - - connectionSettings.m_launchAssetProcessorOnFailedConnection = true; - connectionSettings.m_connectionIdentifier = AzFramework::AssetSystem::ConnectionIdentifiers::Game; - connectionSettings.m_loggingCallback = []([[maybe_unused]] AZStd::string_view logData) + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { - AZ_TracePrintf("Launcher", "%.*s", aznumeric_cast(logData.size()), logData.data()); - }; + AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "CriticalAssetsCompiled", R"({})"); + // Reload the assetcatalog.xml at this point again + // Start Monitoring Asset changes over the network and load the AssetCatalog + auto LoadCatalog = [settingsRegistry](AZ::Data::AssetCatalogRequests* assetCatalogRequests) + { + if (AZ::IO::FixedMaxPath assetCatalogPath; + settingsRegistry->Get(assetCatalogPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder)) + { + assetCatalogPath /= "assetcatalog.xml"; + assetCatalogRequests->LoadCatalog(assetCatalogPath.c_str()); + } + }; + AZ::Data::AssetCatalogRequestBus::Broadcast(AZStd::move(LoadCatalog)); + } + } - AzFramework::AssetSystemRequestBus::BroadcastResult(connectedToAssetProcessor, &AzFramework::AssetSystemRequestBus::Events::EstablishAssetProcessorConnection, connectionSettings); - - if (connectedToAssetProcessor) + // If the connect option is false, this function will return true + // to make sure the Launcher passes the connected to AP check + // If REMOTE_ASSET_PROCESSOR is not defined, then the launcher doesn't need + // to connect to the AssetProcessor and therefore this function returns true + bool ConnectToAssetProcessor([[maybe_unused]] bool connect) + { + bool connectedToAssetProcessor = true; +#if defined(REMOTE_ASSET_PROCESSOR) + if (connect) { - AZ_TracePrintf("Launcher", "Connected to Asset Processor\n"); - CreateRemoteFileIO(); + // When the AssetProcessor is already launched it should take less than a second to perform a connection + // but when the AssetProcessor needs to be launch it could take up to 15 seconds to have the AssetProcessor initialize + // and able to negotiate a connection when running a debug build + // and to negotiate a connection + // Setting the connectTimeout to 3 seconds if not set within the settings registry + + AzFramework::AssetSystem::ConnectionSettings connectionSettings; + AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings); + + connectionSettings.m_launchAssetProcessorOnFailedConnection = true; + connectionSettings.m_connectionIdentifier = AzFramework::AssetSystem::ConnectionIdentifiers::Game; + connectionSettings.m_loggingCallback = []([[maybe_unused]] AZStd::string_view logData) + { + AZ_TracePrintf("Launcher", "%.*s", aznumeric_cast(logData.size()), logData.data()); + }; + + AzFramework::AssetSystemRequestBus::BroadcastResult(connectedToAssetProcessor, &AzFramework::AssetSystemRequestBus::Events::EstablishAssetProcessorConnection, connectionSettings); + + if (connectedToAssetProcessor) + { + AZ_TracePrintf("Launcher", "Connected to Asset Processor\n"); + CreateRemoteFileIO(); + } } +#endif + CompileCriticalAssets(); return connectedToAssetProcessor; } @@ -403,25 +435,21 @@ namespace O3DELauncher gameApplication.Start({}, gameApplicationStartupParams); -#if defined(REMOTE_ASSET_PROCESSOR) - bool allowedEngineConnection = !systemInitParams.bToolMode && !systemInitParams.bTestMode && bg_ConnectToAssetProcessor; //connect to the asset processor using the bootstrap values - if (allowedEngineConnection) + const bool allowedEngineConnection = !systemInitParams.bToolMode && !systemInitParams.bTestMode && bg_ConnectToAssetProcessor; + if (!ConnectToAssetProcessor(allowedEngineConnection)) { - if (!ConnectToAssetProcessor()) + AZ::s64 waitForConnect{}; + AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, waitForConnect, + AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, "wait_for_connect"); + if (waitForConnect != 0) { - AZ::s64 waitForConnect{}; - AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, waitForConnect, - AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, "wait_for_connect"); - if (waitForConnect != 0) - { - AZ_Error("Launcher", false, "Failed to connect to AssetProcessor."); - return ReturnCode::ErrAssetProccessor; - } + AZ_Error("Launcher", false, "Failed to connect to AssetProcessor."); + return ReturnCode::ErrAssetProccessor; } } -#endif + AZ_Assert(AZ::AllocatorInstance::IsReady(), "System allocator was not created or creation failed."); //Initialize the Debug trace instance to create necessary environment variables AZ::Debug::Trace::Instance().Init(); diff --git a/Code/Tools/SerializeContextTools/SliceConverter.cpp b/Code/Tools/SerializeContextTools/SliceConverter.cpp index d0528a9cad..cfb1998f48 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.cpp +++ b/Code/Tools/SerializeContextTools/SliceConverter.cpp @@ -79,10 +79,6 @@ namespace AZ return false; } - // Load the asset catalog so that we can find any nested assets successfully. We also need to tick the tick bus - // so that the OnCatalogLoaded event gets processed now, instead of during application shutdown. - application.Tick(); - AZStd::string logggingScratchBuffer; SetupLogging(logggingScratchBuffer, convertSettings.m_reporting, *commandLine); diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp index 84fc58718c..ef03a839d7 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp @@ -155,7 +155,7 @@ namespace AZ { Initialize(); }, - "LegacySystemInterfaceCreated"); + "CriticalAssetsCompiled"); } } diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystemInterface.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystemInterface.h index 3f30d498cf..693185b6d2 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystemInterface.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystemInterface.h @@ -34,8 +34,7 @@ namespace AZ RPISystemInterface() = default; virtual ~RPISystemInterface() = default; - //! Pre-load some system assets. This should be called once the asset catalog is ready and before create any RPI instances. - //! Note: can't rely on the AzFramework::AssetCatalogEventBus's OnCatalogLoaded since the order of calling handlers is undefined. + //! Pre-load some system assets. This should be called once Critical Asset have compiled ready and before create any RPI instances. virtual void InitializeSystemAssets() = 0; //! Was the RPI system initialized properly diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index ec365d7a6a..02248fffd4 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -295,10 +296,24 @@ namespace AtomToolsFramework QMessageBox::critical( activeWindow(), QString("Failed to compile critical assets"), QString("Failed to compile the following critical assets:\n%1\n%2") - .arg(failedAssets.join(",\n")) - .arg("Make sure this is an Atom project.")); + .arg(failedAssets.join(",\n")) + .arg("Make sure this is an Atom project.")); ExitMainLoop(); } + + AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "CriticalAssetsCompiled", R"({})"); + // Reload the assetcatalog.xml at this point again + // Start Monitoring Asset changes over the network and load the AssetCatalog + auto LoadCatalog = [settingsRegistry = m_settingsRegistry.get()](AZ::Data::AssetCatalogRequests* assetCatalogRequests) + { + if (AZ::IO::FixedMaxPath assetCatalogPath; + settingsRegistry->Get(assetCatalogPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder)) + { + assetCatalogPath /= "assetcatalog.xml"; + assetCatalogRequests->LoadCatalog(assetCatalogPath.c_str()); + } + }; + AZ::Data::AssetCatalogRequestBus::Broadcast(AZStd::move(LoadCatalog)); } void AtomToolsApplication::SaveSettings() diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.cpp index 35f5067cd0..9cf714b40e 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -165,12 +165,12 @@ namespace MaterialEditor // AssetCatalogRequestBus::EnumerateAssets can lead to deadlocked) AZ::Data::AssetCatalogRequests::AssetEnumerationCB enumerateCB = [this]([[maybe_unused]] const AZ::Data::AssetId id, const AZ::Data::AssetInfo& info) { - if (AzFramework::StringFunc::EndsWith(info.m_relativePath.c_str(), ".lightingpreset.azasset")) + if (AZ::StringFunc::EndsWith(info.m_relativePath.c_str(), ".lightingpreset.azasset")) { m_lightingPresetAssets[info.m_assetId] = { info.m_assetId, info.m_assetType }; AZ::Data::AssetBus::MultiHandler::BusConnect(info.m_assetId); } - else if (AzFramework::StringFunc::EndsWith(info.m_relativePath.c_str(), ".modelpreset.azasset")) + else if (AZ::StringFunc::EndsWith(info.m_relativePath.c_str(), ".modelpreset.azasset")) { m_modelPresetAssets[info.m_assetId] = { info.m_assetId, info.m_assetType }; AZ::Data::AssetBus::MultiHandler::BusConnect(info.m_assetId); @@ -429,4 +429,51 @@ namespace MaterialEditor ReloadContent(); }); } + + void MaterialViewportComponent::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) + { + auto ReloadLightingAndModelPresets = [this, &assetId](AZ::Data::AssetCatalogRequests* assetCatalogRequests) + { + AZ::Data::AssetInfo assetInfo = assetCatalogRequests->GetAssetInfoById(assetId); + AZ::Data::Asset* modifiedPresetAsset{}; + if (AZ::StringFunc::EndsWith(assetInfo.m_relativePath.c_str(), ".lightingpreset.azasset")) + { + m_lightingPresetAssets[assetInfo.m_assetId] = { assetInfo.m_assetId, assetInfo.m_assetType }; + AZ::Data::AssetBus::MultiHandler::BusConnect(assetInfo.m_assetId); + modifiedPresetAsset = &m_lightingPresetAssets[assetInfo.m_assetId]; + } + else if (AzFramework::StringFunc::EndsWith(assetInfo.m_relativePath.c_str(), ".modelpreset.azasset")) + { + m_modelPresetAssets[assetInfo.m_assetId] = { assetInfo.m_assetId, assetInfo.m_assetType }; + AZ::Data::AssetBus::MultiHandler::BusConnect(assetInfo.m_assetId); + modifiedPresetAsset = &m_modelPresetAssets[assetInfo.m_assetId]; + } + + // Queue a load on the changed asset + if (modifiedPresetAsset != nullptr) + { + modifiedPresetAsset->QueueLoad(); + } + }; + AZ::Data::AssetCatalogRequestBus::Broadcast(AZStd::move(ReloadLightingAndModelPresets)); + } + + void MaterialViewportComponent::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) + { + OnCatalogAssetChanged(assetId); + } + + void MaterialViewportComponent::OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& assetInfo) + { + if (AZ::StringFunc::EndsWith(assetInfo.m_relativePath.c_str(), ".lightingpreset.azasset")) + { + AZ::Data::AssetBus::MultiHandler::BusDisconnect(assetInfo.m_assetId); + m_lightingPresetAssets.erase(assetId); + } + if (AZ::StringFunc::EndsWith(assetInfo.m_relativePath.c_str(), ".modelpreset.azasset")) + { + AZ::Data::AssetBus::MultiHandler::BusDisconnect(assetInfo.m_assetId); + m_modelPresetAssets.erase(assetId); + } + } } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.h index 07385d842d..68668bd804 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.h @@ -95,6 +95,9 @@ namespace MaterialEditor //////////////////////////////////////////////////////////////////////// // AzFramework::AssetCatalogEventBus::Handler overrides ... void OnCatalogLoaded(const char* catalogFile) override; + void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override; + void OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) override; + void OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& assetInfo) override; //////////////////////////////////////////////////////////////////////// AZStd::unordered_map> m_lightingPresetAssets; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.cpp index 2bf428bd2d..0250640d65 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include @@ -88,14 +89,22 @@ namespace AZ AzToolsFramework::EditorLevelNotificationBus::Handler::BusConnect(); AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler::BusConnect(); - AzFramework::AssetCatalogEventBus::Handler::BusConnect(); + if (auto settingsRegistry{ AZ::SettingsRegistry::Get() }; settingsRegistry != nullptr) + { + auto LifecycleCallback = [this](AZStd::string_view, AZ::SettingsRegistryInterface::Type) + { + SetupThumbnails(); + }; + AZ::ComponentApplicationLifecycle::RegisterHandler(*settingsRegistry, m_criticalAssetsHandler, + AZStd::move(LifecycleCallback), "CriticalAssetsCompiled"); + } AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect(); } void EditorCommonFeaturesSystemComponent::Deactivate() { AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect(); - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); + m_criticalAssetsHandler = {}; AzToolsFramework::EditorLevelNotificationBus::Handler::BusDisconnect(); AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler::BusDisconnect(); @@ -192,13 +201,6 @@ namespace AZ } } - void EditorCommonFeaturesSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile) - { - AZ::TickBus::QueueFunction([this](){ - SetupThumbnails(); - }); - } - const AzToolsFramework::AssetBrowser::PreviewerFactory* EditorCommonFeaturesSystemComponent::GetPreviewerFactory( const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.h index 82bb93a808..dff9e68814 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.h @@ -28,7 +28,6 @@ namespace AZ , public AzToolsFramework::EditorLevelNotificationBus::Handler , public AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler , public AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler - , public AzFramework::AssetCatalogEventBus::Handler , public AzFramework::ApplicationLifecycleEvents::Bus::Handler { public: @@ -58,9 +57,6 @@ namespace AZ const AZ::Data::AssetId&, AZ::SliceComponent::SliceInstanceAddress&, const AzFramework::SliceInstantiationTicket&) override; void OnSliceInstantiationFailed(const AZ::Data::AssetId&, const AzFramework::SliceInstantiationTicket&) override; - // AzFramework::AssetCatalogEventBus::Handler overrides ... - void OnCatalogLoaded(const char* catalogFile) override; - // AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler overrides... const AzToolsFramework::AssetBrowser::PreviewerFactory* GetPreviewerFactory( const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const override; @@ -80,6 +76,7 @@ namespace AZ AZStd::unique_ptr m_thumbnailRenderer; AZStd::unique_ptr m_previewerFactory; + AZ::SettingsRegistryInterface::NotifyEventHandler m_criticalAssetsHandler; }; } // namespace Render } // namespace AZ diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index f112098eae..c8c1ed15bd 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -58,7 +58,7 @@ namespace Multiplayer { ActivateDedicatedEditorServer(); }, - "LegacySystemInterfaceCreated"); + "CriticalAssetsCompiled"); } } } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkSpawnableLibrary.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkSpawnableLibrary.cpp index ba8836b6e6..f60778dbb4 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkSpawnableLibrary.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkSpawnableLibrary.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -17,12 +18,20 @@ namespace Multiplayer NetworkSpawnableLibrary::NetworkSpawnableLibrary() { AZ::Interface::Register(this); - AzFramework::AssetCatalogEventBus::Handler::BusConnect(); + if (auto settingsRegistry{ AZ::SettingsRegistry::Get() }; settingsRegistry != nullptr) + { + auto LifecycleCallback = [this](AZStd::string_view, AZ::SettingsRegistryInterface::Type) + { + BuildSpawnablesList(); + }; + AZ::ComponentApplicationLifecycle::RegisterHandler(*settingsRegistry, m_criticalAssetsHandler, + AZStd::move(LifecycleCallback), "CriticalAssetsCompiled"); + } } NetworkSpawnableLibrary::~NetworkSpawnableLibrary() { - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); + m_criticalAssetsHandler = {}; AZ::Interface::Unregister(this); } @@ -50,11 +59,6 @@ namespace Multiplayer m_spawnablesReverseLookup[id] = name; } - void NetworkSpawnableLibrary::OnCatalogLoaded([[maybe_unused]] const char* catalogFile) - { - BuildSpawnablesList(); - } - AZ::Name NetworkSpawnableLibrary::GetSpawnableNameFromAssetId(AZ::Data::AssetId assetId) { if (assetId.IsValid()) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkSpawnableLibrary.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkSpawnableLibrary.h index 1cec63f81d..0fc3ae07cc 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkSpawnableLibrary.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkSpawnableLibrary.h @@ -9,14 +9,13 @@ #pragma once #include -#include +#include namespace Multiplayer { /// Implementation of the network prefab library interface. class NetworkSpawnableLibrary final : public INetworkSpawnableLibrary - , private AzFramework::AssetCatalogEventBus::Handler { public: AZ_RTTI(NetworkSpawnableLibrary, "{65E15F33-E893-49C2-A8E2-B6A8A6EF31E0}", INetworkSpawnableLibrary); @@ -30,11 +29,10 @@ namespace Multiplayer AZ::Name GetSpawnableNameFromAssetId(AZ::Data::AssetId assetId) override; AZ::Data::AssetId GetAssetIdByName(AZ::Name name) override; - /// AssetCatalogEventBus overrides. - void OnCatalogLoaded(const char* catalogFile) override; private: AZStd::unordered_map m_spawnables; AZStd::unordered_map m_spawnablesReverseLookup; + AZ::SettingsRegistryInterface::NotifyEventHandler m_criticalAssetsHandler; }; } diff --git a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp index 168adc8910..39c0dfc686 100644 --- a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp +++ b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp @@ -103,15 +103,13 @@ namespace PhysX if (auto* settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { - // Automatically register the event if it's not registered, because - // this system is initialized before the settings registry has loaded the event list. AZ::ComponentApplicationLifecycle::RegisterHandler( *settingsRegistry, m_componentApplicationLifecycleHandler, [this]([[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZ::SettingsRegistryInterface::Type type) { InitializeMaterialLibrary(); }, - "LegacySystemInterfaceCreated"); // LegacySystemInterfaceCreated is signaled after critical assets have been processed + "CriticalAssetsCompiled"); } m_state = State::Initialized; diff --git a/Registry/application_lifecycle_events.setreg b/Registry/application_lifecycle_events.setreg index 0d9cd0f170..c52c93c0c0 100644 --- a/Registry/application_lifecycle_events.setreg +++ b/Registry/application_lifecycle_events.setreg @@ -23,7 +23,8 @@ "GemsUnloaded": {}, "FileIOAvailable": {}, "FileIOUnavailable": {}, - "LegacySystemInterfaceCreated": {} + "LegacySystemInterfaceCreated": {}, + "CriticalAssetsCompiled": {} } } } From d3a99235aaa997688baebf3a1432090714f16765 Mon Sep 17 00:00:00 2001 From: srikappa-amzn <82230713+srikappa-amzn@users.noreply.github.com> Date: Mon, 10 Jan 2022 17:01:50 -0800 Subject: [PATCH 24/26] Fix undo for create editor entity (#6785) * Avoid undoing twice when undo is hit for CreateNewEditorEntity Signed-off-by: srikappa-amzn <82230713+srikappa-amzn@users.noreply.github.com> * Moved an assert immediately after entity creation Signed-off-by: srikappa-amzn <82230713+srikappa-amzn@users.noreply.github.com> --- .../Entity/EditorEntityContextComponent.cpp | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.cpp index 90657132ca..470ca8b9ea 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.cpp @@ -226,7 +226,15 @@ namespace AzToolsFramework AZ::EntityId EditorEntityContextComponent::CreateNewEditorEntity(const char* name) { AZ::Entity* entity = CreateEntity(name); - FinalizeEditorEntity(entity); + AZ_Assert(entity != nullptr, "Entity with name %s couldn't be created.", name); + if (m_isLegacySliceService) + { + FinalizeEditorEntity(entity); + } + else + { + EditorEntityContextNotificationBus::Broadcast(&EditorEntityContextNotification::OnEditorEntityCreated, entity->GetId()); + } return entity->GetId(); } @@ -253,8 +261,16 @@ namespace AzToolsFramework return AZ::EntityId(); } entity = aznew AZ::Entity(entityId, name); + AZ_Assert(entity != nullptr, "Entity with name %s couldn't be created.", name); AddEntity(entity); - FinalizeEditorEntity(entity); + if (m_isLegacySliceService) + { + FinalizeEditorEntity(entity); + } + else + { + EditorEntityContextNotificationBus::Broadcast(&EditorEntityContextNotification::OnEditorEntityCreated, entity->GetId()); + } return entity->GetId(); } From f09055af42af18dc75f709753da33940ae9c21f5 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Mon, 10 Jan 2022 19:11:00 -0600 Subject: [PATCH 25/26] Fixed CMake use with Android NDK 23 (#6460) * Fixed CMake use with Android NDK 23 This was done by only setting the `ANDROID_ARM_MODE` if the ANDROID_ABI starts with `armeabi`. The Android arm mode setting can't be used using non-`armeabi` ABIs. In O3DE we default to `arm64-v8a` `ANDROID_ABI` So `ANDROID_ARM_MODE` must not be set. The CMake [Android-Determine.cmake](https://gitlab.kitware.com/cmake/cmake/-/blob/master/Modules/Platform/Android-Determine.cmake#L573-585) which is used to detect platform-wide information when the CMAKE_SYSTEM_NAME is set to android, enforces that if the `CMAKE_ANDROID_ARCH_ABI` doesn't start with `armeabi`, then it will fatal error if the CMAKE_ANDROID_ARM_MODE option is set. In Android NDK 21 the `ANDROID_ARM_MODE` variable is used to set the CMAKE_ANDROID_ARM_MODE variable if the [ANDROID_ABI](https://android.googlesource.com/platform/ndk/+/refs/tags/ndk-r21e/build/cmake/android.toolchain.cmake#700) starts with `armeabi`. This meant when using Android NDK 21, the CMake Android-Determine.cmake module would succeed, due to the CMAKE_ANDROID_ARM_MODE not being set. In Android NDK 23 the `ANDROID_ARM_MODE` now will set the `CMAKE_ANDROID_ARM_MODE` variable to `TRUE` if it isn't defined. Added an `--extra-cmake-configure-args` option to the `generate_android_project.py` script which can be used to append user specified CMake arguments to the cmake configure step (`cmake -B -S -DCMAKE_TOOLCHAIN_FILE= `) Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the O3DE Android toolchain wrapper to fatal error if the 64-bit arm ABI isn't used. Also removed the unneccessary setting of the ANDROID_ARM_MODE and ANDROID_ARM_NEON option option now that the armeabi cannot be specified as an ABI. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- cmake/Platform/Android/Toolchain_android.cmake | 9 ++++----- cmake/Tools/Platform/Android/android_support.py | 9 ++++++++- .../Tools/Platform/Android/generate_android_project.py | 6 +++++- scripts/build/Platform/Android/build_config.json | 10 +++++----- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/cmake/Platform/Android/Toolchain_android.cmake b/cmake/Platform/Android/Toolchain_android.cmake index b5c4a56fd6..75ff4554fd 100644 --- a/cmake/Platform/Android/Toolchain_android.cmake +++ b/cmake/Platform/Android/Toolchain_android.cmake @@ -31,11 +31,10 @@ endif() if(NOT ANDROID_ABI) set(ANDROID_ABI arm64-v8a) endif() -if(NOT ANDROID_ARM_MODE) - set(ANDROID_ARM_MODE arm) -endif() -if(NOT ANDROID_ARM_NEON) - set(ANDROID_ARM_NEON FALSE) + +# Only the 64-bit ANDROID ABIs arm supported +if(NOT ANDROID_ABI MATCHES "^arm64-") + message(FATAL_ERROR "Only the 64-bit ANDROID_ABI's are supported. arm64-v8a can be used if not set") endif() if(NOT ANDROID_NATIVE_API_LEVEL) set(ANDROID_NATIVE_API_LEVEL 21) diff --git a/cmake/Tools/Platform/Android/android_support.py b/cmake/Tools/Platform/Android/android_support.py index f52cc2736c..657ef3b3e4 100755 --- a/cmake/Tools/Platform/Android/android_support.py +++ b/cmake/Tools/Platform/Android/android_support.py @@ -471,7 +471,8 @@ class AndroidProjectGenerator(object): def __init__(self, engine_root, build_dir, android_sdk_path, build_tool, android_sdk_platform, android_native_api_level, android_ndk, project_path, third_party_path, cmake_version, override_cmake_path, override_gradle_path, gradle_version, gradle_plugin_version, - override_ninja_path, include_assets_in_apk, asset_mode, asset_type, signing_config, native_build_path, vulkan_validation_path, is_test_project=False, + override_ninja_path, include_assets_in_apk, asset_mode, asset_type, signing_config, native_build_path, vulkan_validation_path, + extra_cmake_configure_args, is_test_project=False, overwrite_existing=True, unity_build_enabled=False): """ Initialize the object with all the required parameters needed to create an Android Project. The parameters should be verified before initializing this object @@ -497,6 +498,7 @@ class AndroidProjectGenerator(object): :param signing_config: Optional signing configuration arguments :param native_build_path: Override the native build staging path in gradle :param vulkan_validation_path: Override the path to where the Vulkan Validation Layers libraries are (required when using NDK r23+) + :param extra_cmake_configure_args Additional arguments to supply cmake when configuring a project :param is_test_project: Flag to indicate if this is a unit test runner project. (If true, project_path, asset_mode, asset_type, and include_assets_in_apk are ignored) :param overwrite_existing: Flag to overwrite existing project files when being generated, or skip if they already exist. """ @@ -539,6 +541,8 @@ class AndroidProjectGenerator(object): self.vulkan_validation_path = vulkan_validation_path + self.extra_cmake_configure_args = extra_cmake_configure_args + self.asset_mode = asset_mode self.asset_type = asset_type @@ -844,6 +848,9 @@ class AndroidProjectGenerator(object): if self.override_ninja_path: cmake_argument_list.append(f'"-DCMAKE_MAKE_PROGRAM={common.normalize_path_for_settings(self.override_ninja_path)}"') + if self.extra_cmake_configure_args: + cmake_argument_list.extend(map(json.dumps, self.extra_cmake_configure_args)) + # Query the project_path from the project.json file project_name = common.read_project_name_from_project_json(self.project_path) # Prepare the config-specific section to place the cmake argument list in the build.gradle for the app diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index fe0d787d38..5e414e7738 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -227,6 +227,9 @@ def main(args): help='Override path to where the Vulkan Validation Layers libraries are. Required for use with NDK r23+', default=None, required=False) + parser.add_argument('--extra-cmake-configure-args', + help='Extra arguments to supply to the cmake configure step', + nargs='*') # Asset Options parser.add_argument(INCLUDE_APK_ASSETS_ARGUMENT_NAME, @@ -415,7 +418,8 @@ def main(args): overwrite_existing=parsed_args.overwrite_existing, unity_build_enabled=parsed_args.enable_unity_build, native_build_path=parsed_args.native_build_path, - vulkan_validation_path=parsed_args.vulkan_validation_path) + vulkan_validation_path=parsed_args.vulkan_validation_path, + extra_cmake_configure_args=parsed_args.extra_cmake_configure_args) generator.execute() diff --git a/scripts/build/Platform/Android/build_config.json b/scripts/build/Platform/Android/build_config.json index 7a7e90c0b0..c505d8e940 100644 --- a/scripts/build/Platform/Android/build_config.json +++ b/scripts/build/Platform/Android/build_config.json @@ -35,7 +35,7 @@ "PARAMETERS": { "CONFIGURATION":"debug", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -50,7 +50,7 @@ "PARAMETERS": { "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -66,7 +66,7 @@ "PARAMETERS": { "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\" -DLY_UNITY_BUILD=FALSE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\" -DLY_UNITY_BUILD=FALSE", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -102,7 +102,7 @@ "PARAMETERS": { "CONFIGURATION":"release", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\"", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -118,7 +118,7 @@ "PARAMETERS": { "CONFIGURATION":"release", "OUTPUT_DIRECTORY":"build\\mono_android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\" -DLY_MONOLITHIC_GAME=TRUE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_NDK_DIR!\" -DLY_MONOLITHIC_GAME=TRUE", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" From 751caf5f7ccdddcf16f3ba63cbf1d21349ce3191 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Tue, 11 Jan 2022 10:01:02 -0600 Subject: [PATCH 26/26] Updated the Windows ScopedAutoTempDirectory creation logic to use a Uuid. (#6789) Previously it was using `GetTickCount()` for the creation of the directory which can collide if0 there are multiple processes creating a temporary directory via teh ScopedAutoTempDirectory constructor. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- .../ScopedAutoTempDirectory_Windows.cpp | 70 +++++++------------ 1 file changed, 27 insertions(+), 43 deletions(-) diff --git a/Code/Framework/AzTest/AzTest/Platform/Windows/ScopedAutoTempDirectory_Windows.cpp b/Code/Framework/AzTest/AzTest/Platform/Windows/ScopedAutoTempDirectory_Windows.cpp index fcda5d351a..b5314eda14 100644 --- a/Code/Framework/AzTest/AzTest/Platform/Windows/ScopedAutoTempDirectory_Windows.cpp +++ b/Code/Framework/AzTest/AzTest/Platform/Windows/ScopedAutoTempDirectory_Windows.cpp @@ -8,57 +8,41 @@ #include +#include #include #include #include -#include +#include -namespace AZ +namespace AZ::Test { - namespace Test + ScopedAutoTempDirectory::ScopedAutoTempDirectory() { - ScopedAutoTempDirectory::ScopedAutoTempDirectory() + using UuidString = AZStd::fixed_string; + constexpr DWORD bufferSize = static_cast(AZ::IO::MaxPathLength); + + wchar_t tempDirW[AZ::IO::MaxPathLength]{}; + GetTempPathW(bufferSize, tempDirW); + + AZ::IO::FixedMaxPath tempDirectoryRoot; + AZStd::to_string(tempDirectoryRoot.Native(), tempDirW); + + constexpr int MaxAttempts = 255; + for (int i = 0; i < MaxAttempts; ++i) { - constexpr const DWORD bufferSize = static_cast(AZ::IO::MaxPathLength); - - char tempDir[bufferSize] = {0}; - GetTempPathA(bufferSize, tempDir); - - char workingTempPathBuffer[bufferSize] = {'\0'}; - - int maxAttempts = 2000; // Prevent an infinite loop by setting an arbitrary maximum attempts at finding an available temp folder name - while (maxAttempts > 0) + AZ::IO::FixedMaxPath testPath = tempDirectoryRoot / + AZ::IO::FixedMaxPathString::format("UnitTest-%s", + AZ::Uuid::CreateRandom().ToString().c_str()); + // Try to create the temp directory if it doesn't exist + if (!AZ::IO::SystemFile::Exists(testPath.c_str()) && AZ::IO::SystemFile::CreateDir(testPath.c_str())) { - // Use the system's tick count to base the folder name - ULONGLONG currentTick = GetTickCount64(); - azsnprintf(workingTempPathBuffer, bufferSize, "%sUnitTest-%X", tempDir, aznumeric_cast(currentTick)); - - // Check if the requested directory name is available and re-generate if it already exists - bool exists = AZ::IO::SystemFile::Exists(workingTempPathBuffer); - if (exists) - { - Sleep(1); - maxAttempts--; - continue; - } + azstrncpy(AZStd::data(m_tempDirectory), AZStd::size(m_tempDirectory), + testPath.c_str(), testPath.Native().size()); break; } - - AZ_Error("AzTest", maxAttempts > 0, "Unable to determine a temp directory"); - - if (maxAttempts > 0) - { - // Create the temp directory and track it for deletion - bool tempDirectoryCreated = AZ::IO::SystemFile::CreateDir(workingTempPathBuffer); - if (tempDirectoryCreated) - { - azstrncpy(m_tempDirectory, AZ::IO::MaxPathLength, workingTempPathBuffer, AZ::IO::MaxPathLength); - } - else - { - AZ_Error("AzTest", false, "Unable to create temp directory %s", workingTempPathBuffer); - } - } } - } // Test -} // AZ + + AZ_Error("AzTest", m_tempDirectory[0] != '\0', "Unable to create temp path within directory %s after %d attempts", + tempDirectoryRoot.c_str(), MaxAttempts); + } +} // AZ::Test