diff --git a/AutomatedTesting/Assets/Textures/image.png b/AutomatedTesting/Assets/Textures/image.png new file mode 100644 index 0000000000..2f558c634e --- /dev/null +++ b/AutomatedTesting/Assets/Textures/image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:011454252e40c927343cce16296412f02f45d1f345c75c036651bdcca473bda5 +size 2672 diff --git a/AutomatedTesting/Assets/Textures/normal.png b/AutomatedTesting/Assets/Textures/normal.png new file mode 100644 index 0000000000..d3355e6699 --- /dev/null +++ b/AutomatedTesting/Assets/Textures/normal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7bafcc4aefab827e1414e64bcdde235500b51392e52c9ccd588b2d7a24b865a0 +size 20214 diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py deleted file mode 100644 index 7f97c132bb..0000000000 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU_Optimized.py +++ /dev/null @@ -1,46 +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 EditorSharedTest, EditorTestSuite -from ly_test_tools.image.screenshot_compare_qssim import qssim as compare_screenshots -from .atom_utils.atom_component_helper import create_screenshots_archive, golden_images_directory - -DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots' - - -@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.") -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestAutomation(EditorTestSuite): - # Remove -autotest_mode from global_extra_cmdline_args since we need rendering for these tests. - global_extra_cmdline_args = ["-BatchMode"] # Default is ["-BatchMode", "-autotest_mode"] - - enable_prefab_system = False - - @pytest.mark.test_case_id("C34603773") - class AtomGPU_BasicLevelSetup_SetsUpLevel(EditorSharedTest): - use_null_renderer = False # Default is True - screenshot_name = "AtomBasicLevelSetup.ppm" - test_screenshots = [] # Gets set by setup() - screenshot_directory = "" # Gets set by setup() - - # Clear existing test screenshots before starting test. - def setup(self, workspace): - screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH) - test_screenshots = [os.path.join(screenshot_directory, self.screenshot_name)] - file_system.delete(test_screenshots, True, True) - - from Atom.tests import hydra_AtomGPU_BasicLevelSetup as test_module - - golden_images = [os.path.join(golden_images_directory(), screenshot_name)] - for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images): - compare_screenshots(test_screenshot, golden_screenshot) - create_screenshots_archive(screenshot_directory) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py deleted file mode 100644 index 1641b529ae..0000000000 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py +++ /dev/null @@ -1,200 +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 editor_python_test_tools.hydra_editor_utils as hydra -from editor_python_test_tools.editor_test_helper import EditorTestHelper -from Atom.atom_utils.screenshot_utils import ScreenshotHelper - -SCREEN_WIDTH = 1280 -SCREEN_HEIGHT = 720 -DEGREE_RADIAN_FACTOR = 0.0174533 - -helper = EditorTestHelper(log_prefix="Test_Atom_BasicLevelSetup") - - -def run(): - """ - 1. View -> Layouts -> Restore Default Layout, sets the viewport to ratio 16:9 @ 1280 x 720 - 2. Runs console command r_DisplayInfo = 0 - 3. Deletes all entities currently present in the level. - 4. Creates a "default_level" entity to hold all other entities, setting the translate values to x:0, y:0, z:0 - 5. Adds a Grid component to the "default_level" & updates its Grid Spacing to 1.0m - 6. Adds a "global_skylight" entity to "default_level", attaching an HDRi Skybox w/ a Cubemap Texture. - 7. Adds a Global Skylight (IBL) component w/ diffuse image and specular image to "global_skylight" entity. - 8. Adds a "ground_plane" entity to "default_level", attaching a Mesh component & Material component. - 9. Adds a "directional_light" entity to "default_level" & adds a Directional Light component. - 10. Adds a "sphere" entity to "default_level" & adds a Mesh component with a Material component to it. - 11. Adds a "camera" entity to "default_level" & adds a Camera component with 80 degree FOV and Transform values: - Translate - x:5.5m, y:-12.0m, z:9.0m - Rotate - x:-27.0, y:-12.0, z:25.0 - 12. Finally enters game mode, takes a screenshot, & exits game mode. - :return: None - """ - import azlmbr.asset as asset - import azlmbr.bus as bus - import azlmbr.camera as camera - import azlmbr.entity as entity - import azlmbr.legacy.general as general - import azlmbr.math as math - import azlmbr.paths - import azlmbr.editor as editor - - def initial_viewport_setup(screen_width, screen_height): - general.set_viewport_size(screen_width, screen_height) - general.update_viewport() - helper.wait_for_condition( - function=lambda: helper.isclose(a=general.get_viewport_size().x, b=SCREEN_WIDTH, rel_tol=0.1) - and helper.isclose(a=general.get_viewport_size().y, b=SCREEN_HEIGHT, rel_tol=0.1), - timeout_in_seconds=4.0 - ) - result = helper.isclose(a=general.get_viewport_size().x, b=SCREEN_WIDTH, rel_tol=0.1) and helper.isclose( - a=general.get_viewport_size().y, b=SCREEN_HEIGHT, rel_tol=0.1) - general.log(general.get_viewport_size().x) - general.log(general.get_viewport_size().y) - general.log(general.get_viewport_size().z) - general.log(f"Viewport is set to the expected size: {result}") - general.run_console("r_DisplayInfo = 0") - - def after_level_load(): - """Function to call after creating/opening a level to ensure it loads.""" - # Give everything a second to initialize. - general.idle_enable(True) - general.idle_wait(1.0) - general.update_viewport() - general.idle_wait(0.5) # half a second is more than enough for updating the viewport. - - # Close out problematic windows, FPS meters, and anti-aliasing. - if general.is_helpers_shown(): # Turn off the helper gizmos if visible - general.toggle_helpers() - general.idle_wait(1.0) - if general.is_pane_visible("Error Report"): # Close Error Report windows that block focus. - general.close_pane("Error Report") - if general.is_pane_visible("Error Log"): # Close Error Log windows that block focus. - general.close_pane("Error Log") - general.idle_wait(1.0) - general.run_console("r_displayInfo=0") - general.idle_wait(1.0) - - # Wait for Editor idle loop before executing Python hydra scripts. - general.idle_enable(True) - - # Basic setup for opened level. - helper.open_level(level_name="Base") - after_level_load() - initial_viewport_setup(SCREEN_WIDTH, SCREEN_HEIGHT) - - # Create default_level entity - search_filter = azlmbr.entity.SearchFilter() - all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter) - editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities) - - default_level = hydra.Entity("default_level") - position = math.Vector3(0.0, 0.0, 0.0) - default_level.create_entity(position, ["Grid"]) - default_level.get_set_test(0, "Controller|Configuration|Secondary Grid Spacing", 1.0) - - # Create global_skylight entity and set the properties - global_skylight = hydra.Entity("global_skylight") - global_skylight.create_entity( - entity_position=math.Vector3(0.0, 0.0, 0.0), - components=["HDRi Skybox", "Global Skylight (IBL)"], - parent_id=default_level.id - ) - global_skylight_image_asset_path = os.path.join("LightingPresets", "default_iblskyboxcm.exr.streamingimage") - global_skylight_image_asset = asset.AssetCatalogRequestBus( - bus.Broadcast, "GetAssetIdByPath", global_skylight_image_asset_path, math.Uuid(), False) - global_skylight.get_set_test(0, "Controller|Configuration|Cubemap Texture", global_skylight_image_asset) - hydra.get_set_test(global_skylight, 1, "Controller|Configuration|Diffuse Image", global_skylight_image_asset) - hydra.get_set_test(global_skylight, 1, "Controller|Configuration|Specular Image", global_skylight_image_asset) - - # Create ground_plane entity and set the properties - ground_plane = hydra.Entity("ground_plane") - ground_plane.create_entity( - entity_position=math.Vector3(0.0, 0.0, 0.0), - components=["Material"], - parent_id=default_level.id - ) - azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalUniformScale", ground_plane.id, 32.0) - - # Work around to add the correct Atom Mesh component and asset. - mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId - ground_plane.components.append( - editor.EditorComponentAPIBus( - bus.Broadcast, "AddComponentsOfType", ground_plane.id, [mesh_type_id] - ).GetValue()[0] - ) - ground_plane_mesh_asset_path = os.path.join("TestData", "Objects", "plane.azmodel") - ground_plane_mesh_asset = asset.AssetCatalogRequestBus( - bus.Broadcast, "GetAssetIdByPath", ground_plane_mesh_asset_path, math.Uuid(), False) - hydra.get_set_test(ground_plane, 1, "Controller|Configuration|Mesh Asset", ground_plane_mesh_asset) - - # Add Atom Material component and asset. - ground_plane_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_chrome.azmaterial") - ground_plane_material_asset = asset.AssetCatalogRequestBus( - bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False) - ground_plane.get_set_test(0, "Default Material|Material Asset", ground_plane_material_asset) - - # Create directional_light entity and set the properties - directional_light = hydra.Entity("directional_light") - directional_light.create_entity( - entity_position=math.Vector3(0.0, 0.0, 10.0), - components=["Directional Light"], - parent_id=default_level.id - ) - rotation = math.Vector3(DEGREE_RADIAN_FACTOR * -90.0, 0.0, 0.0) - azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", directional_light.id, rotation) - - # Create sphere entity and set the properties - sphere = hydra.Entity("sphere") - sphere.create_entity( - entity_position=math.Vector3(0.0, 0.0, 1.0), - components=["Material"], - parent_id=default_level.id - ) - - # Work around to add the correct Atom Mesh component and asset. - sphere.components.append( - editor.EditorComponentAPIBus( - bus.Broadcast, "AddComponentsOfType", sphere.id, [mesh_type_id] - ).GetValue()[0] - ) - sphere_mesh_asset_path = os.path.join("Models", "sphere.azmodel") - sphere_mesh_asset = asset.AssetCatalogRequestBus( - bus.Broadcast, "GetAssetIdByPath", sphere_mesh_asset_path, math.Uuid(), False) - hydra.get_set_test(sphere, 1, "Controller|Configuration|Mesh Asset", sphere_mesh_asset) - - # Add Atom Material component and asset. - sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial") - sphere_material_asset = asset.AssetCatalogRequestBus( - bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False) - sphere.get_set_test(0, "Default Material|Material Asset", sphere_material_asset) - - # Create camera component and set the properties - camera_entity = hydra.Entity("camera") - position = math.Vector3(5.5, -12.0, 9.0) - camera_entity.create_entity(components=["Camera"], entity_position=position, parent_id=default_level.id) - rotation = math.Vector3( - DEGREE_RADIAN_FACTOR * -27.0, DEGREE_RADIAN_FACTOR * -12.0, DEGREE_RADIAN_FACTOR * 25.0 - ) - azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", camera_entity.id, rotation) - camera_entity.get_set_test(0, "Controller|Configuration|Field of view", 60.0) - camera.EditorCameraViewRequestBus(azlmbr.bus.Event, "ToggleCameraAsActiveView", camera_entity.id) - - # Enter game mode, take screenshot, & exit game mode. - general.idle_wait(0.5) - general.enter_game_mode() - general.idle_wait(1.0) - helper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=2.0) - ScreenshotHelper(general.idle_wait_frames).capture_screenshot_blocking(f"{'AtomBasicLevelSetup'}.ppm") - general.exit_game_mode() - helper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=2.0) - - -if __name__ == "__main__": - run() diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py deleted file mode 100644 index f69ceb2120..0000000000 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py +++ /dev/null @@ -1,261 +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 sys - -import azlmbr.asset as asset -import azlmbr.bus as bus -import azlmbr.editor as editor -import azlmbr.math as math -import azlmbr.paths -import azlmbr.legacy.general as general - -sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests")) - -import editor_python_test_tools.hydra_editor_utils as hydra -from Atom.atom_utils import atom_component_helper, atom_constants, screenshot_utils -from editor_python_test_tools.editor_test_helper import EditorTestHelper - -helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper") - -LEVEL_NAME = "Base" -LIGHT_COMPONENT = "Light" -LIGHT_TYPE_PROPERTY = 'Controller|Configuration|Light type' -DEGREE_RADIAN_FACTOR = 0.0174533 - - -def run(): - """ - Sets up the tests by making sure the required level is created & setup correctly. - It then executes 2 test cases - see each associated test function's docstring for more info. - - Finally prints the string "Light component tests completed" after completion - - Tests will fail immediately if any of these log lines are found: - 1. Trace::Assert - 2. Trace::Error - 3. Traceback (most recent call last): - - :return: None - """ - atom_component_helper.create_basic_atom_level(level_name=LEVEL_NAME) - - # Run tests. - area_light_test() - spot_light_test() - general.log("Light component tests completed.") - - -def area_light_test(): - """ - Basic test for the "Light" component attached to an "area_light" entity. - - Test Case - Light Component: Capsule, Spot (disk), and Point (sphere): - 1. Creates "area_light" entity w/ a Light component that has a Capsule Light type w/ the color set to 255, 0, 0 - 2. Enters game mode to take a screenshot for comparison, then exits game mode. - 3. Sets the Light component Intensity Mode to Lumens (default). - 4. Ensures the Light component Mode is Automatic (default). - 5. Sets the Intensity value of the Light component to 0.0 - 6. Enters game mode again, takes another screenshot for comparison, then exits game mode. - 7. Updates the Intensity value of the Light component to 1000.0 - 8. Enters game mode again, takes another screenshot for comparison, then exits game mode. - 9. Swaps the Capsule light type option to Spot (disk) light type on the Light component - 10. Updates "area_light" entity Transform rotate value to x: 90.0, y:0.0, z:0.0 - 11. Enters game mode again, takes another screenshot for comparison, then exits game mode. - 12. Swaps the Spot (disk) light type for the Point (sphere) light type in the Light component. - 13. Enters game mode again, takes another screenshot for comparison, then exits game mode. - 14. Deletes the Light component from the "area_light" entity and verifies its successful. - """ - # Create an "area_light" entity with "Light" component using Light type of "Capsule" - area_light_entity_name = "area_light" - area_light = hydra.Entity(area_light_entity_name) - area_light.create_entity(math.Vector3(-1.0, -2.0, 3.0), [LIGHT_COMPONENT]) - general.log( - f"{area_light_entity_name}_test: Component added to the entity: " - f"{hydra.has_components(area_light.id, [LIGHT_COMPONENT])}") - light_component_id_pair = hydra.attach_component_to_entity(area_light.id, LIGHT_COMPONENT) - - # Select the "Capsule" light type option. - azlmbr.editor.EditorComponentAPIBus( - azlmbr.bus.Broadcast, - 'SetComponentProperty', - light_component_id_pair, - LIGHT_TYPE_PROPERTY, - atom_constants.LIGHT_TYPES['capsule'] - ) - - # Update color and take screenshot in game mode - color = math.Color(255.0, 0.0, 0.0, 0.0) - area_light.get_set_test(0, "Controller|Configuration|Color", color) - general.idle_wait(1.0) - screenshot_utils.take_screenshot_game_mode("AreaLight_1", area_light_entity_name) - - # Update intensity value to 0.0 and take screenshot in game mode - area_light.get_set_test(0, "Controller|Configuration|Attenuation Radius|Mode", 1) - area_light.get_set_test(0, "Controller|Configuration|Intensity", 0.0) - general.idle_wait(1.0) - screenshot_utils.take_screenshot_game_mode("AreaLight_2", area_light_entity_name) - - # Update intensity value to 1000.0 and take screenshot in game mode - area_light.get_set_test(0, "Controller|Configuration|Intensity", 1000.0) - general.idle_wait(1.0) - screenshot_utils.take_screenshot_game_mode("AreaLight_3", area_light_entity_name) - - # Swap the "Capsule" light type option to "Spot (disk)" light type - azlmbr.editor.EditorComponentAPIBus( - azlmbr.bus.Broadcast, - 'SetComponentProperty', - light_component_id_pair, - LIGHT_TYPE_PROPERTY, - atom_constants.LIGHT_TYPES['spot_disk'] - ) - area_light_rotation = math.Vector3(DEGREE_RADIAN_FACTOR * 90.0, 0.0, 0.0) - azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", area_light.id, area_light_rotation) - general.idle_wait(1.0) - screenshot_utils.take_screenshot_game_mode("AreaLight_4", area_light_entity_name) - - # Swap the "Spot (disk)" light type to the "Point (sphere)" light type and take screenshot. - azlmbr.editor.EditorComponentAPIBus( - azlmbr.bus.Broadcast, - 'SetComponentProperty', - light_component_id_pair, - LIGHT_TYPE_PROPERTY, - atom_constants.LIGHT_TYPES['sphere'] - ) - general.idle_wait(1.0) - screenshot_utils.take_screenshot_game_mode("AreaLight_5", area_light_entity_name) - - editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntityById", area_light.id) - - -def spot_light_test(): - """ - Basic test for the Light component attached to a "spot_light" entity. - - Test Case - Light Component: Spot (disk) with shadows & colors: - 1. Creates "spot_light" entity w/ a Light component attached to it. - 2. Selects the "directional_light" entity already present in the level and disables it. - 3. Selects the "global_skylight" entity already present in the level and disables the HDRi Skybox component, - as well as the Global Skylight (IBL) component. - 4. Enters game mode to take a screenshot for comparison, then exits game mode. - 5. Selects the "ground_plane" entity and changes updates the material to a new material. - 6. Enters game mode to take a screenshot for comparison, then exits game mode. - 7. Selects the "spot_light" entity and increases the Light component Intensity to 800 lm - 8. Enters game mode to take a screenshot for comparison, then exits game mode. - 9. Selects the "spot_light" entity and sets the Light component Color to 47, 75, 37 - 10. Enters game mode to take a screenshot for comparison, then exits game mode. - 11. Selects the "spot_light" entity and modifies the Shutter controls to the following values: - - Enable shutters: True - - Inner Angle: 60.0 - - Outer Angle: 75.0 - 12. Enters game mode to take a screenshot for comparison, then exits game mode. - 13. Selects the "spot_light" entity and modifies the Shadow controls to the following values: - - Enable Shadow: True - - ShadowmapSize: 256 - 14. Modifies the world translate position of the "spot_light" entity to 0.7, -2.0, 1.9 (for casting shadows better) - 15. Enters game mode to take a screenshot for comparison, then exits game mode. - """ - # Disable "Directional Light" component for the "directional_light" entity - # "directional_light" entity is created by the create_basic_atom_level() function by default. - directional_light_entity_id = hydra.find_entity_by_name("directional_light") - directional_light = hydra.Entity(name='directional_light', id=directional_light_entity_id) - directional_light_component_type = azlmbr.editor.EditorComponentAPIBus( - azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Directional Light"], 0)[0] - directional_light_component = azlmbr.editor.EditorComponentAPIBus( - azlmbr.bus.Broadcast, 'GetComponentOfType', directional_light.id, directional_light_component_type - ).GetValue() - editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [directional_light_component]) - general.idle_wait(0.5) - - # Disable "Global Skylight (IBL)" and "HDRi Skybox" components for the "global_skylight" entity - global_skylight_entity_id = hydra.find_entity_by_name("global_skylight") - global_skylight = hydra.Entity(name='global_skylight', id=global_skylight_entity_id) - global_skylight_component_type = azlmbr.editor.EditorComponentAPIBus( - azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Global Skylight (IBL)"], 0)[0] - global_skylight_component = azlmbr.editor.EditorComponentAPIBus( - azlmbr.bus.Broadcast, 'GetComponentOfType', global_skylight.id, global_skylight_component_type - ).GetValue() - editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [global_skylight_component]) - hdri_skybox_component_type = azlmbr.editor.EditorComponentAPIBus( - azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["HDRi Skybox"], 0)[0] - hdri_skybox_component = azlmbr.editor.EditorComponentAPIBus( - azlmbr.bus.Broadcast, 'GetComponentOfType', global_skylight.id, hdri_skybox_component_type - ).GetValue() - editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [hdri_skybox_component]) - general.idle_wait(0.5) - - # Create a "spot_light" entity with "Light" component using Light Type of "Spot (disk)" - spot_light_entity_name = "spot_light" - spot_light = hydra.Entity(spot_light_entity_name) - spot_light.create_entity(math.Vector3(0.7, -2.0, 1.0), [LIGHT_COMPONENT]) - general.log( - f"{spot_light_entity_name}_test: Component added to the entity: " - f"{hydra.has_components(spot_light.id, [LIGHT_COMPONENT])}") - rotation = math.Vector3(DEGREE_RADIAN_FACTOR * 300.0, 0.0, 0.0) - azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", spot_light.id, rotation) - light_component_type = hydra.attach_component_to_entity(spot_light.id, LIGHT_COMPONENT) - editor.EditorComponentAPIBus( - azlmbr.bus.Broadcast, - 'SetComponentProperty', - light_component_type, - LIGHT_TYPE_PROPERTY, - atom_constants.LIGHT_TYPES['spot_disk'] - ) - - general.idle_wait(1.0) - screenshot_utils.take_screenshot_game_mode("SpotLight_1", spot_light_entity_name) - - # Change default material of ground plane entity and take screenshot - ground_plane_entity_id = hydra.find_entity_by_name("ground_plane") - ground_plane = hydra.Entity(name='ground_plane', id=ground_plane_entity_id) - ground_plane_asset_path = os.path.join("Materials", "Presets", "MacBeth", "22_neutral_5-0_0-70d.azmaterial") - ground_plane_asset_value = asset.AssetCatalogRequestBus( - bus.Broadcast, "GetAssetIdByPath", ground_plane_asset_path, math.Uuid(), False) - material_property_path = "Default Material|Material Asset" - material_component_type = azlmbr.editor.EditorComponentAPIBus( - azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Material"], 0)[0] - material_component = azlmbr.editor.EditorComponentAPIBus( - azlmbr.bus.Broadcast, 'GetComponentOfType', ground_plane.id, material_component_type).GetValue() - editor.EditorComponentAPIBus( - azlmbr.bus.Broadcast, - 'SetComponentProperty', - material_component, - material_property_path, - ground_plane_asset_value - ) - general.idle_wait(1.0) - screenshot_utils.take_screenshot_game_mode("SpotLight_2", spot_light_entity_name) - - # Increase intensity value of the Spot light and take screenshot in game mode - spot_light.get_set_test(0, "Controller|Configuration|Intensity", 800.0) - general.idle_wait(1.0) - screenshot_utils.take_screenshot_game_mode("SpotLight_3", spot_light_entity_name) - - # Update the Spot light color and take screenshot in game mode - color_value = math.Color(47.0 / 255.0, 75.0 / 255.0, 37.0 / 255.0, 255.0 / 255.0) - spot_light.get_set_test(0, "Controller|Configuration|Color", color_value) - general.idle_wait(1.0) - screenshot_utils.take_screenshot_game_mode("SpotLight_4", spot_light_entity_name) - - # Update the Shutter controls of the Light component and take screenshot - spot_light.get_set_test(0, "Controller|Configuration|Shutters|Enable shutters", True) - spot_light.get_set_test(0, "Controller|Configuration|Shutters|Inner angle", 60.0) - spot_light.get_set_test(0, "Controller|Configuration|Shutters|Outer angle", 75.0) - general.idle_wait(1.0) - screenshot_utils.take_screenshot_game_mode("SpotLight_5", spot_light_entity_name) - - # Update the Shadow controls, move the spot_light entity world translate position and take screenshot - spot_light.get_set_test(0, "Controller|Configuration|Shadows|Enable shadow", True) - spot_light.get_set_test(0, "Controller|Configuration|Shadows|Shadowmap size", 256.0) - azlmbr.components.TransformBus( - azlmbr.bus.Event, "SetWorldTranslation", spot_light.id, math.Vector3(0.7, -2.0, 1.9)) - general.idle_wait(1.0) - screenshot_utils.take_screenshot_game_mode("SpotLight_6", spot_light_entity_name) - - -if __name__ == "__main__": - run() diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 71a87234a9..e8001f7215 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -113,8 +113,15 @@ set(FILES Debug/TraceMessageBus.h Debug/TraceReflection.cpp Debug/TraceReflection.h + DOM/DomBackend.cpp + DOM/DomBackend.h + DOM/DomUtils.cpp + DOM/DomUtils.h DOM/DomVisitor.cpp DOM/DomVisitor.h + DOM/Backends/JSON/JsonBackend.h + DOM/Backends/JSON/JsonSerializationUtils.cpp + DOM/Backends/JSON/JsonSerializationUtils.h EBus/BusImpl.h EBus/EBus.h EBus/EBusEnvironment.cpp diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index 82b427ae19..b7fe113bc6 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -212,6 +212,8 @@ set(FILES AZStd/Variant.cpp AZStd/VariantSerialization.cpp AZStd/VectorAndArray.cpp + DOM/DomJsonTests.cpp + DOM/DomJsonBenchmarks.cpp ) # Prevent the following files from being grouped in UNITY builds diff --git a/Code/Framework/AzToolsFramework/Tests/TransformComponent.cpp b/Code/Framework/AzToolsFramework/Tests/TransformComponent.cpp index bc50962dbd..71ce9e3b5f 100644 --- a/Code/Framework/AzToolsFramework/Tests/TransformComponent.cpp +++ b/Code/Framework/AzToolsFramework/Tests/TransformComponent.cpp @@ -18,7 +18,10 @@ #include #include +#include #include +#include +#include #include @@ -1062,4 +1065,67 @@ R"DELIMITER( } } } + + // Fixture provides a root prefab with Transform component and listens for TransformNotificationBus. + class TransformComponentActivationTest + : public PrefabTestFixture + , public TransformNotificationBus::Handler + { + protected: + void SetUpEditorFixtureImpl() override + { + PrefabTestFixture::SetUpEditorFixtureImpl(); + + CreateRootPrefab(); + } + + void TearDownEditorFixtureImpl() override + { + BusDisconnect(); + + PrefabTestFixture::TearDownEditorFixtureImpl(); + } + + void OnTransformChanged(const Transform& /*local*/, const Transform& /*world*/) override + { + m_transformUpdated = true; + } + + void MoveEntity(AZ::EntityId entityId) + { + AzToolsFramework::ScopedUndoBatch undoBatch("Move Entity"); + TransformBus::Event(entityId, &TransformInterface::SetWorldTranslation, Vector3(1.f, 0.f, 0.f)); + } + + bool m_transformUpdated = false; + }; + + TEST_F(TransformComponentActivationTest, TransformChangedEventIsSentWhenEntityIsActivatedViaUndoRedo) + { + AZ::EntityId entityId = CreateEntityUnderRootPrefab("Entity"); + MoveEntity(entityId); + BusConnect(entityId); + + // verify that undoing/redoing move operations fires TransformChanged event + Undo(); + EXPECT_TRUE(m_transformUpdated); + m_transformUpdated = false; + + Redo(); + EXPECT_TRUE(m_transformUpdated); + m_transformUpdated = false; + } + + TEST_F(TransformComponentActivationTest, TransformChangedEventIsNotSentWhenEntityIsDeactivatedAndActivated) + { + AZ::EntityId entityId = CreateEntityUnderRootPrefab("Entity"); + BusConnect(entityId); + + // verify that simply activating/deactivating an entity does not fire TransformChanged event + Entity* entity = nullptr; + ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, entityId); + entity->Deactivate(); + entity->Activate(); + EXPECT_FALSE(m_transformUpdated); + } } // namespace UnitTest diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Presets/MacBeth/15_red_sRGB.tif b/Gems/Atom/Feature/Common/Assets/Materials/Presets/MacBeth/15_red_sRGB.tif new file mode 100644 index 0000000000..4c150db62f --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/Presets/MacBeth/15_red_sRGB.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b53b8ca6b7062239398820aef6fb4145a0b6c8c7e3aae90c17b6b87f806d3579 +size 19426 diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp index 81ce82b70d..fedbf849eb 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp @@ -670,8 +670,6 @@ namespace AZ AZ::u8 width, int32_t viewProjOverrideIndex) { - AZ_PROFILE_SCOPE(AzRender, "AuxGeomDrawQueue: DrawPrimitiveWithSharedVerticesCommon"); - // grab a mutex lock for the rest of this function so that a commit cannot happen during it and // other threads can't add geometry during it AZStd::lock_guard lock(m_buffersWriteLock); @@ -742,8 +740,6 @@ namespace AZ AZ::u8 width, int32_t viewProjOverrideIndex) { - AZ_PROFILE_SCOPE(AzRender, "AuxGeomDrawQueue: DrawPrimitiveWithSharedVerticesCommon"); - AZ_Assert(indexCount >= verticesPerPrimitiveType && (indexCount % verticesPerPrimitiveType == 0), "Index count must be at least %d and must be a multiple of %d", verticesPerPrimitiveType, verticesPerPrimitiveType); diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 2ac44e17da..c11d223458 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -76,8 +76,8 @@ namespace AZ void MeshFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { AZ_PROFILE_SCOPE(RPI, "MeshFeatureProcessor: Simulate"); - AZ_UNUSED(packet); + AZ::Job* parentJob = packet.m_parentJob; AZStd::concurrency_check_scope scopeCheck(m_meshDataChecker); const auto iteratorRanges = m_modelData.GetParallelRanges(); @@ -86,6 +86,8 @@ namespace AZ { const auto jobLambda = [&]() -> void { + AZ_PROFILE_SCOPE(AzRender, "MeshFeatureProcessor: Simulate: Job"); + for (auto meshDataIter = iteratorRange.first; meshDataIter != iteratorRange.second; ++meshDataIter) { if (!meshDataIter->m_model) @@ -113,24 +115,22 @@ namespace AZ { meshDataIter->BuildCullable(); } + + if (meshDataIter->m_cullBoundsNeedsUpdate) + { + meshDataIter->UpdateCullBounds(m_transformService); + } } }; Job* executeGroupJob = aznew JobFunction(jobLambda, true, nullptr); // Auto-deletes - executeGroupJob->SetDependent(&jobCompletion); - executeGroupJob->Start(); + parentJob->StartAsChild(executeGroupJob); } - jobCompletion.StartAndWaitForCompletion(); - - m_forceRebuildDrawPackets = false; - - // CullingSystem::RegisterOrUpdateCullable() is not threadsafe, so need to do those updates in a single thread - for (ModelDataInstance& modelDataInstance : m_modelData) { - if (modelDataInstance.m_model && modelDataInstance.m_cullBoundsNeedsUpdate) - { - modelDataInstance.UpdateCullBounds(m_transformService); - } + AZ_PROFILE_SCOPE(AzRender, "MeshFeatureProcessor: Simulate: WaitForChildren"); + parentJob->WaitForChildren(); } + + m_forceRebuildDrawPackets = false; } void MeshFeatureProcessor::OnBeginPrepareRender() @@ -1037,7 +1037,6 @@ namespace AZ void ModelDataInstance::UpdateDrawPackets(bool forceUpdate /*= false*/) { - AZ_PROFILE_SCOPE(AzRender, "ModelDataInstance:: UpdateDrawPackets"); for (auto& drawPacketList : m_drawPacketListsByLod) { for (auto& drawPacket : drawPacketList) @@ -1052,7 +1051,6 @@ namespace AZ void ModelDataInstance::BuildCullable() { - AZ_PROFILE_SCOPE(AzRender, "ModelDataInstance: BuildCullable"); AZ_Assert(m_cullableNeedsRebuild, "This function only needs to be called if the cullable to be rebuilt"); AZ_Assert(m_model, "The model has not finished loading yet"); @@ -1129,7 +1127,6 @@ namespace AZ void ModelDataInstance::UpdateCullBounds(const TransformServiceFeatureProcessor* transformService) { - AZ_PROFILE_SCOPE(AzRender, "ModelDataInstance: UpdateCullBounds"); AZ_Assert(m_cullBoundsNeedsUpdate, "This function only needs to be called if the culling bounds need to be rebuilt"); AZ_Assert(m_model, "The model has not finished loading yet"); diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp index 482834a2ed..716582b3ab 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp @@ -232,9 +232,10 @@ namespace AZ for (ScopeProducer* scopeProducer : m_scopeProducers) { + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: PrepareProducers: Scope %s", scopeProducer->GetScopeId().GetCStr()); m_frameGraph->BeginScope(*scopeProducer->GetScope()); scopeProducer->SetupFrameGraphDependencies(*m_frameGraph); - + // All scopes depend on the root scope. if (scopeProducer->GetScopeId() != m_rootScopeId) { @@ -526,7 +527,10 @@ namespace AZ parentJob->StartAsChild(AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr)); } - parentJob->WaitForChildren(); + { + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: ExecuteGroupInternal: WaitForChildren"); + parentJob->WaitForChildren(); + } } m_frameGraphExecuter->EndGroup(groupIndex); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp index d2370ae4cb..e73978fccc 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp @@ -477,8 +477,7 @@ namespace AZ return; } - D3D12_RESOURCE_TRANSITION_BARRIER transition; - memset(&transition, 0, sizeof(D3D12_RESOURCE_TRANSITION_BARRIER)); // C4701 potentially unitialized local variable 'transition' used + D3D12_RESOURCE_TRANSITION_BARRIER transition = {0}; transition.pResource = image.GetMemoryView().GetMemory(); Scope& firstScope = static_cast(scopeAttachment->GetScope()); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Scope.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Scope.cpp index 4702c17ecd..95e88af77d 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Scope.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Scope.cpp @@ -92,13 +92,13 @@ namespace AZ const bool Scope::IsStateSupportedByQueue(D3D12_RESOURCE_STATES state) const { - const D3D12_RESOURCE_STATES VALID_COMPUTE_QUEUE_RESOURCE_STATES = + constexpr D3D12_RESOURCE_STATES VALID_COMPUTE_QUEUE_RESOURCE_STATES = (D3D12_RESOURCE_STATE_UNORDERED_ACCESS | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_COPY_DEST | D3D12_RESOURCE_STATE_COPY_SOURCE); - const D3D12_RESOURCE_STATES VALID_GRAPHICS_QUEUE_RESOURCE_STATES = + constexpr D3D12_RESOURCE_STATES VALID_GRAPHICS_QUEUE_RESOURCE_STATES = (D3D12_RESOURCE_STATES)DX12_RESOURCE_STATE_VALID_API_MASK; switch (GetHardwareQueueClass()) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index d8525a8811..18be5c8460 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -34,7 +34,7 @@ //Enables more detailed profiling descriptions within the culling system, but adds some performance overhead. //Enable this to more easily see which jobs are associated with which view. -//#define AZ_CULL_PROFILE_VERBOSE +//#define AZ_CULL_PROFILE_VERBOSE namespace AZ { @@ -43,6 +43,7 @@ namespace AZ AZ_CVAR(bool, r_CullInParallel, true, nullptr, ConsoleFunctorFlags::Null, ""); AZ_CVAR(uint32_t, r_CullWorkPerBatch, 500, nullptr, ConsoleFunctorFlags::Null, ""); +#ifdef AZ_CULL_DEBUG_ENABLED void DebugDrawWorldCoordinateAxes(AuxGeomDraw* auxGeom) { auxGeom->DrawCylinder(Vector3(.5, .0, .0), Vector3(1, 0, 0), 0.02f, 1.0f, Colors::Red, AuxGeomDraw::DrawStyle::Solid, AuxGeomDraw::DepthTest::Off); @@ -198,6 +199,7 @@ namespace AZ AZ_Assert(false, "invalid frustum, cannot draw"); } } +#endif //AZ_CULL_DEBUG_ENABLED CullingDebugContext::~CullingDebugContext() { @@ -282,7 +284,7 @@ namespace AZ const Scene& scene, View& view, Frustum& frustum, - [[maybe_unused]]void* maskedOcclusionCulling) + [[maybe_unused]] void* maskedOcclusionCulling) { AZStd::shared_ptr worklistData = AZStd::make_shared(); worklistData->m_debugCtx = &debugCtx; @@ -318,14 +320,16 @@ namespace AZ for (const AzFramework::IVisibilityScene::NodeData& nodeData : worklist) { //If a node is entirely contained within the frustum, then we can skip the fine grained culling. - bool nodeIsContainedInFrustum = ShapeIntersection::Contains(worklistData->m_frustum, nodeData.m_bounds); + bool nodeIsContainedInFrustum = + !worklistData->m_debugCtx->m_enableFrustumCulling || + ShapeIntersection::Contains(worklistData->m_frustum, nodeData.m_bounds); #ifdef AZ_CULL_PROFILE_VERBOSE - AZ_PROFILE_SCOPE(RPI, "process node (view: %s, skip fine cull: %d", - m_view->GetName().GetCStr(), nodeIsContainedInFrustum ? 1 : 0); + AZ_PROFILE_SCOPE(RPI, "process node (view: %s, skip fine cull: %ds", + worklistData->m_view->GetName().GetCStr(), nodeIsContainedInFrustum ? "true" : "false"); #endif - if (nodeIsContainedInFrustum || !worklistData->m_debugCtx->m_enableFrustumCulling) + if (nodeIsContainedInFrustum) { //Add all objects within this node to the view, without any extra culling for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries) @@ -391,7 +395,7 @@ namespace AZ } } } - +#ifdef AZ_CULL_DEBUG_ENABLED if (worklistData->m_debugCtx->m_debugDraw && (worklistData->m_view->GetName() == worklistData->m_debugCtx->m_currentViewSelectionName)) { AZ_PROFILE_SCOPE(RPI, "debug draw culling"); @@ -443,8 +447,10 @@ namespace AZ } } } +#endif } +#ifdef AZ_CULL_DEBUG_ENABLED if (worklistData->m_debugCtx->m_enableStats) { CullingDebugContext::CullStats& cullStats = worklistData->m_debugCtx->GetCullStatsForView(worklistData->m_view); @@ -454,6 +460,7 @@ namespace AZ cullStats.m_numVisibleCullables += numVisibleCullables; ++cullStats.m_numJobs; } +#endif //AZ_CULL_DEBUG_ENABLED } #if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED @@ -466,6 +473,10 @@ namespace AZ return MaskedOcclusionCulling::CullingResult::VISIBLE; } +#ifdef AZ_CULL_PROFILE_VERBOSE + AZ_PROFILE_SCOPE(RPI, "TestOcclusionCulling"); +#endif + if (visibleEntry->m_boundingVolume.Contains(worklistData->m_view->GetCameraTransform().GetTranslation())) { // camera is inside bounding volume @@ -515,10 +526,15 @@ namespace AZ } #endif - void CullingScene::ProcessCullablesCommon(const Scene& scene, View& view, AZ::Frustum& frustum, [[maybe_unused]]void*& maskedOcclusionCulling) + void CullingScene::ProcessCullablesCommon( + const Scene& scene [[maybe_unused]], + View& view, + AZ::Frustum& frustum [[maybe_unused]], + void*& maskedOcclusionCulling [[maybe_unused]]) { AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullablesCommon() - %s", view.GetName().GetCStr()); +#ifdef AZ_CULL_DEBUG_ENABLED if (m_debugCtx.m_freezeFrustums) { AZStd::lock_guard lock(m_debugCtx.m_frozenFrustumsMutex); @@ -543,7 +559,7 @@ namespace AZ CullingDebugContext::CullStats& cullStats = m_debugCtx.GetCullStatsForView(&view); cullStats.m_cameraViewToWorld = view.GetViewToWorldMatrix(); } - +#endif //AZ_CULL_DEBUG_ENABLED #if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED // setup occlusion culling, if necessary maskedOcclusionCulling = m_occlusionPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); @@ -805,6 +821,7 @@ namespace AZ beginCullingDescriptor, [&view]() { + AZ_PROFILE_SCOPE(RPI, "CullingScene: BeginCullingTaskGraph"); view->BeginCulling(); }); } @@ -822,6 +839,7 @@ namespace AZ { const auto cullingLambda = [&view]() { + AZ_PROFILE_SCOPE(RPI, "CullingScene: BeginCullingJob"); view->BeginCulling(); }; @@ -843,7 +861,11 @@ namespace AZ m_taskGraphActive = AZ::Interface::Get(); - if (m_taskGraphActive && m_taskGraphActive->IsTaskGraphActive()) + if(views.size() == 1) // avoid job overhead when only 1 job + { + views[0]->BeginCulling(); + } + else if (m_taskGraphActive && m_taskGraphActive->IsTaskGraphActive()) { BeginCullingTaskGraph(views); } @@ -852,6 +874,7 @@ namespace AZ BeginCullingJobs(views); } +#if AZ_CULL_DEBUG_ENABLED AuxGeomDrawPtr auxGeom; if (m_debugCtx.m_debugDraw) { @@ -884,6 +907,7 @@ namespace AZ m_debugCtx.m_frozenFrustums.clear(); } } +#endif } void CullingScene::EndCulling() diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp index da75ded4d6..cd64010680 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp @@ -311,7 +311,6 @@ namespace AZ Pass::FramePrepareParams params{ &frameGraphBuilder }; { - AZ_PROFILE_SCOPE(RPI, "Pass: FrameBegin"); m_rootPass->FrameBegin(params); } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index efece58e0d..a21b0bfecd 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -399,10 +399,11 @@ namespace AZ for (FeatureProcessorPtr& fp : m_featureProcessors) { FeatureProcessor* featureProcessor = fp.get(); - const auto jobLambda = [this, featureProcessor]() + const auto jobLambda = [this, featureProcessor](AZ::Job& owner) { - - featureProcessor->Simulate(m_simulatePacket); + FeatureProcessor::SimulatePacket jobPacket = m_simulatePacket; + jobPacket.m_parentJob = &owner; + featureProcessor->Simulate(jobPacket); }; AZ::Job* simulationJob = AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr); //auto-deletes @@ -515,7 +516,7 @@ namespace AZ collectDrawPacketsTG.Submit(&collectDrawPacketsTGEvent); // Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs if m_parallelOctreeTraversal) - bool parallelOctreeTraversal = m_cullingScene->GetDebugContext().m_parallelOctreeTraversal; + const bool parallelOctreeTraversal = m_cullingScene->GetDebugContext().m_parallelOctreeTraversal; m_cullingScene->BeginCulling(m_renderPacket.m_views); static const AZ::TaskDescriptor processCullablesDescriptor{"AZ::RPI::Scene::ProcessCullables", "Graphics"}; AZ::TaskGraphEvent processCullablesTGEvent; @@ -575,6 +576,7 @@ namespace AZ } // Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs) + const bool parallelOctreeTraversal = m_cullingScene->GetDebugContext().m_parallelOctreeTraversal; m_cullingScene->BeginCulling(m_renderPacket.m_views); for (ViewPtr& viewPtr : m_renderPacket.m_views) { @@ -583,7 +585,7 @@ namespace AZ m_cullingScene->ProcessCullablesJobs(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job }, true, nullptr); //auto-deletes - if (m_cullingScene->GetDebugContext().m_parallelOctreeTraversal) + if (parallelOctreeTraversal) { processCullablesJob->SetDependent(collectDrawPacketsCompletion); processCullablesJob->Start(); @@ -730,20 +732,19 @@ namespace AZ // Add dynamic draw data for all the views if (m_dynamicDrawSystem) { - AZ_PROFILE_SCOPE(RPI, "DynamicDraw SubmitDrawData"); m_dynamicDrawSystem->SubmitDrawData(this, m_renderPacket.m_views); } } { - AZ_PROFILE_BEGIN(RPI, "FinalizeDrawLists"); - if (jobPolicy == RHI::JobPolicy::Serial) + AZ_PROFILE_SCOPE(RPI, "FinalizeDrawLists"); + if (jobPolicy == RHI::JobPolicy::Serial || + m_renderPacket.m_views.size() <= 1) // FinalizeDrawListsX both immediately wait for the job to complete, skip job if only 1 job would be generated { for (auto& view : m_renderPacket.m_views) { view->FinalizeDrawLists(); } - AZ_PROFILE_END(RPI); } else { @@ -755,7 +756,6 @@ namespace AZ { FinalizeDrawListsJobs(); } - AZ_PROFILE_END(RPI); } } diff --git a/Gems/ScriptCanvas/Code/Editor/Framework/ScriptCanvasGraphUtilities.inl b/Gems/ScriptCanvas/Code/Editor/Framework/ScriptCanvasGraphUtilities.inl index f8da519be0..dabd06b0d1 100644 --- a/Gems/ScriptCanvas/Code/Editor/Framework/ScriptCanvasGraphUtilities.inl +++ b/Gems/ScriptCanvas/Code/Editor/Framework/ScriptCanvasGraphUtilities.inl @@ -74,7 +74,7 @@ namespace ScriptCanvasEditor AZ_Assert(loadResult.m_runtimeAsset, "failed to load dependent asset"); AZ::Outcome luaAssetOutcome = AZ::Failure(AZStd::string("lua asset creation for function failed")); - ScriptCanvasEditor::EditorAssetConversionBus::BroadcastResult(luaAssetOutcome, &ScriptCanvasEditor::EditorAssetConversionBusTraits::CreateLuaAsset, loadResult.m_editorAsset, loadResult.m_graphPath); + ScriptCanvasEditor::EditorAssetConversionBus::BroadcastResult(luaAssetOutcome, &ScriptCanvasEditor::EditorAssetConversionBusTraits::CreateLuaAsset, loadResult.m_editorAsset, loadResult.m_editorAsset.Path().c_str()); AZ_Assert(luaAssetOutcome.IsSuccess(), "failed to create Lua asset"); AZStd::string modulePath = namespacePath[0].data(); @@ -111,18 +111,19 @@ namespace ScriptCanvasEditor AZ_INLINE LoadTestGraphResult LoadTestGraph(AZStd::string_view graphPath) { - AZ::Data::Asset editorAsset; - ScriptCanvasEditor::EditorAssetConversionBus::BroadcastResult(editorAsset, &ScriptCanvasEditor::EditorAssetConversionBusTraits::LoadAsset, graphPath); - - if (editorAsset.GetData()) + if (auto loadFileOutcome = LoadFromFile(graphPath); loadFileOutcome.IsSuccess()) { - AZ::Outcome< AZ::Data::Asset, AZStd::string> assetOutcome = AZ::Failure(AZStd::string("asset creation failed")); - ScriptCanvasEditor::EditorAssetConversionBus::BroadcastResult(assetOutcome, &ScriptCanvasEditor::EditorAssetConversionBusTraits::CreateRuntimeAsset, editorAsset); + auto& source = loadFileOutcome.GetValue(); + auto testableSource = SourceHandle(source, AZ::Uuid::CreateRandom(), source.Path().c_str()); + + AZ::Outcome, AZStd::string> assetOutcome(AZ::Failure(AZStd::string("asset create failed"))); + ScriptCanvasEditor::EditorAssetConversionBus::BroadcastResult(assetOutcome + , &ScriptCanvasEditor::EditorAssetConversionBusTraits::CreateRuntimeAsset, testableSource); + if (assetOutcome.IsSuccess()) { LoadTestGraphResult result; - result.m_graphPath = graphPath; - result.m_editorAsset = editorAsset; + result.m_editorAsset = AZStd::move(testableSource); result.m_runtimeAsset = assetOutcome.GetValue(); result.m_entity = AZStd::make_unique("Loaded Graph"); return result; @@ -163,8 +164,7 @@ namespace ScriptCanvasEditor reporter.SetExecutionMode(mode); LoadTestGraphResult loadResult; - loadResult.m_graphPath = asset.GetHint().c_str(); - loadResult.m_editorAsset = asset; + loadResult.m_editorAsset = SourceHandle(nullptr, assetId.m_guid, asset.GetHint()); AZ::EntityId scriptCanvasId; loadResult.m_entity = AZStd::make_unique("Loaded test graph"); loadResult.m_runtimeAsset = runtimeAsset; @@ -205,7 +205,8 @@ namespace ScriptCanvasEditor { ScopedOutputSuppression outputSuppressor; AZ::Outcome luaAssetOutcome = AZ::Failure(AZStd::string("lua asset creation failed")); - ScriptCanvasEditor::EditorAssetConversionBus::BroadcastResult(luaAssetOutcome, &ScriptCanvasEditor::EditorAssetConversionBusTraits::CreateLuaAsset, loadResult.m_editorAsset, loadResult.m_graphPath); + ScriptCanvasEditor::EditorAssetConversionBus::BroadcastResult(luaAssetOutcome + , &ScriptCanvasEditor::EditorAssetConversionBusTraits::CreateLuaAsset, loadResult.m_editorAsset, loadResult.m_editorAsset.Path().c_str()); reporter.MarkParseAttemptMade(); if (luaAssetOutcome.IsSuccess()) @@ -219,6 +220,8 @@ namespace ScriptCanvasEditor { RuntimeDataOverrides runtimeDataOverrides; runtimeDataOverrides.m_runtimeAsset = loadResult.m_runtimeAsset; + runtimeDataOverrides.m_runtimeAsset.SetHint("original"); + runtimeDataOverrides.m_runtimeAsset.Get()->m_runtimeData.m_script.SetHint("original"); #if defined(LINUX) ////////////////////////////////////////////////////////////////////////// // Temporarily disable testing on the Linux build until the file name casing discrepancy @@ -264,6 +267,10 @@ namespace ScriptCanvasEditor RuntimeDataOverrides dependencyRuntimeDataOverrides; dependencyRuntimeDataOverrides.m_runtimeAsset = dependency.runtimeAsset; + AZStd::string dependencyHint = AZStd::string::format("dependency_%d", index); + dependencyRuntimeDataOverrides.m_runtimeAsset.SetHint(dependencyHint); + dependencyRuntimeDataOverrides.m_runtimeAsset.Get()->m_runtimeData.m_script.SetHint(dependencyHint); + runtimeDataOverrides.m_dependencies.push_back(dependencyRuntimeDataOverrides); RuntimeData& dependencyData = dependencyDataBuffer[index]; diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Controller.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Controller.cpp index 6209667910..5aa8598c80 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Controller.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/Controller.cpp @@ -52,7 +52,6 @@ namespace ScriptCanvasEditor m_view->textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); connect(m_view->scanButton, &QPushButton::pressed, this, &Controller::OnButtonPressScan); connect(m_view->closeButton, &QPushButton::pressed, this, &Controller::OnButtonPressClose); - m_view->upgradeAllButton->setVisible(false); connect(m_view->upgradeAllButton, &QPushButton::pressed, this, &Controller::OnButtonPressUpgrade); m_view->progressBar->setValue(0); m_view->progressBar->setVisible(false); @@ -103,9 +102,9 @@ namespace ScriptCanvasEditor } } - QList Controller::FindTableItems(const AZ::Data::AssetInfo& info) + QList Controller::FindTableItems(const SourceHandle& info) { - return m_view->tableWidget->findItems(info.m_relativePath.c_str(), Qt::MatchFlag::MatchExactly); + return m_view->tableWidget->findItems(info.Path().c_str(), Qt::MatchFlag::MatchExactly); } void Controller::OnButtonPressClose() @@ -116,30 +115,20 @@ namespace ScriptCanvasEditor void Controller::OnButtonPressScan() { // \todo move to another file - auto isUpToDate = [this](AZ::Data::Asset asset) + auto isUpToDate = [this](const SourceHandle& asset) { - AZ::Entity* scriptCanvasEntity = nullptr; + auto graphComponent = asset.Get(); - if (asset.GetType() == azrtti_typeid()) - { - ScriptCanvasAsset* scriptCanvasAsset = asset.GetAs(); - if (!scriptCanvasAsset) - { - AZ_Warning - (ScriptCanvas::k_VersionExplorerWindow.data() - , false - , "InspectAsset: %s, AsestData failed to return ScriptCanvasAsset" - , asset.GetHint().c_str()); - return true; - } - - scriptCanvasEntity = scriptCanvasAsset->GetScriptCanvasEntity(); - AZ_Assert(scriptCanvasEntity, "The Script Canvas asset must have a valid entity"); - } - - auto graphComponent = scriptCanvasEntity->FindComponent(); - AZ_Assert(graphComponent, "The Script Canvas entity must have a Graph component"); - return !m_view->forceUpgrade->isChecked() && graphComponent->GetVersion().IsLatest(); + AZ_Warning + ( ScriptCanvas::k_VersionExplorerWindow.data() + , asset.Get() != nullptr + , "InspectAsset: %s, failed to load valid graph" + , asset.Path().c_str()); + + return graphComponent + && (!graphComponent->GetVersion().IsLatest() || m_view->forceUpgrade->isChecked()) + ? ScanConfiguration::Filter::Include + : ScanConfiguration::Filter::Exclude; }; ScanConfiguration config; @@ -155,59 +144,19 @@ namespace ScriptCanvasEditor OnButtonPressUpgradeImplementation({}); } - void Controller::OnButtonPressUpgradeImplementation(const AZ::Data::AssetInfo& assetInfo) + void Controller::OnButtonPressUpgradeImplementation(const SourceHandle& assetInfo) { - auto simpleUpdate = [this](AZ::Data::Asset asset) + auto simpleUpdate = [this](SourceHandle& asset) { - if (asset.GetType() == azrtti_typeid()) - { - ScriptCanvasAsset* scriptCanvasAsset = asset.GetAs(); - AZ_Assert(scriptCanvasAsset, "Unable to get the asset of ScriptCanvasAsset, but received type: %s" - , azrtti_typeid().template ToString().c_str()); - if (!scriptCanvasAsset) - { - return; - } + AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), asset.Get() != nullptr + , "The Script Canvas asset must have a Graph component"); - AZ::Entity* scriptCanvasEntity = scriptCanvasAsset->GetScriptCanvasEntity(); - AZ_Assert(scriptCanvasEntity, "View::UpgradeGraph The Script Canvas asset must have a valid entity"); - if (!scriptCanvasEntity) - { - return; - } - - AZ::Entity* queryEntity = nullptr; - AZ::ComponentApplicationBus::BroadcastResult(queryEntity, &AZ::ComponentApplicationRequests::FindEntity, scriptCanvasEntity->GetId()); - if (queryEntity) - { - if (queryEntity->GetState() == AZ::Entity::State::Active) - { - queryEntity->Deactivate(); - } - - scriptCanvasEntity = queryEntity; - } - - if (scriptCanvasEntity->GetState() == AZ::Entity::State::Constructed) - { - scriptCanvasEntity->Init(); - } - - if (scriptCanvasEntity->GetState() == AZ::Entity::State::Init) - { - scriptCanvasEntity->Activate(); - } - - AZ_Assert(scriptCanvasEntity->GetState() == AZ::Entity::State::Active, "Graph entity is not active"); - auto graphComponent = scriptCanvasEntity->FindComponent(); - AZ_Assert(graphComponent, "The Script Canvas entity must have a Graph component"); - if (graphComponent) - { - graphComponent->UpgradeGraph - (asset - , m_view->forceUpgrade->isChecked() ? Graph::UpgradeRequest::Forced : Graph::UpgradeRequest::IfOutOfDate - , m_view->verbose->isChecked()); - } + if (asset.Get()) + { + asset.Mod()->UpgradeGraph + ( asset + , m_view->forceUpgrade->isChecked() ? Graph::UpgradeRequest::Forced : Graph::UpgradeRequest::IfOutOfDate + , m_view->verbose->isChecked()); } }; @@ -215,7 +164,7 @@ namespace ScriptCanvasEditor { int result = QMessageBox::No; QMessageBox mb - (QMessageBox::Warning + ( QMessageBox::Warning , QObject::tr("Failed to Save Upgraded File") , QObject::tr("The upgraded file could not be saved because the file is read only.\n" "Do you want to make it writeable and overwrite it?") @@ -234,12 +183,12 @@ namespace ScriptCanvasEditor ModelRequestsBus::Broadcast(&ModelRequestsTraits::Modify, config); } - void Controller::OnButtonPressUpgradeSingle(const AZ::Data::AssetInfo& assetInfo) + void Controller::OnButtonPressUpgradeSingle(const SourceHandle& info) { - OnButtonPressUpgradeImplementation(assetInfo); + OnButtonPressUpgradeImplementation(info); } - void Controller::OnUpgradeModificationBegin([[maybe_unused]] const ModifyConfiguration& config, const AZ::Data::AssetInfo& info) + void Controller::OnUpgradeModificationBegin([[maybe_unused]] const ModifyConfiguration& config, const SourceHandle& info) { for (auto* item : FindTableItems(info)) { @@ -251,16 +200,16 @@ namespace ScriptCanvasEditor void Controller::OnUpgradeModificationEnd ( [[maybe_unused]] const ModifyConfiguration& config - , const AZ::Data::AssetInfo& info + , const SourceHandle& info , ModificationResult result) { if (result.errorMessage.empty()) { - VE_LOG("Successfully modified %s", result.assetInfo.m_relativePath.c_str()); + VE_LOG("Successfully modified %s", result.asset.Path().c_str()); } else { - VE_LOG("Failed to modify %s: %s", result.assetInfo.m_relativePath.c_str(), result.errorMessage.data()); + VE_LOG("Failed to modify %s: %s", result.asset.Path().c_str(), result.errorMessage.data()); } for (auto* item : FindTableItems(info)) @@ -288,12 +237,10 @@ namespace ScriptCanvasEditor AddLogEntries(); } - void Controller::OnGraphUpgradeComplete(AZ::Data::Asset& asset, bool skipped) + void Controller::OnGraphUpgradeComplete(ScriptCanvasEditor::SourceHandle& asset, bool skipped) { ModificationResult result; result.asset = asset; - AZ::Data::AssetCatalogRequestBus::BroadcastResult - ( result.assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, asset.GetId()); if (skipped) { @@ -341,19 +288,19 @@ namespace ScriptCanvasEditor } } - void Controller::OnScanFilteredGraph(const AZ::Data::AssetInfo& info) + void Controller::OnScanFilteredGraph(const SourceHandle& info) { OnScannedGraph(info, Filtered::Yes); } - void Controller::OnScannedGraph(const AZ::Data::AssetInfo& assetInfo, [[maybe_unused]] Filtered filtered) + void Controller::OnScannedGraph(const SourceHandle& assetInfo, [[maybe_unused]] Filtered filtered) { const int rowIndex = m_view->tableWidget->rowCount(); if (filtered == Filtered::No || !m_view->onlyShowOutdated->isChecked()) { m_view->tableWidget->insertRow(rowIndex); - QTableWidgetItem* rowName = new QTableWidgetItem(tr(assetInfo.m_relativePath.c_str())); + QTableWidgetItem* rowName = new QTableWidgetItem(tr(assetInfo.Path().c_str())); m_view->tableWidget->setItem(rowIndex, static_cast(ColumnAsset), rowName); SetRowSucceeded(rowIndex); @@ -375,17 +322,11 @@ namespace ScriptCanvasEditor m_view->tableWidget->setCellWidget(rowIndex, static_cast(ColumnAction), upgradeButton); } - - char resolvedBuffer[AZ_MAX_PATH_LEN] = { 0 }; - AZStd::string path = AZStd::string::format("@devroot@/%s", assetInfo.m_relativePath.c_str()); - AZ::IO::FileIOBase::GetInstance()->ResolvePath(path.c_str(), resolvedBuffer, AZ_MAX_PATH_LEN); - AZ::StringFunc::Path::GetFullPath(resolvedBuffer, path); - AZ::StringFunc::Path::Normalize(path); - + bool result = false; AZ::Data::AssetInfo info; AZStd::string watchFolder; - QByteArray assetNameUtf8 = assetInfo.m_relativePath.c_str(); + QByteArray assetNameUtf8 = assetInfo.Path().c_str(); AzToolsFramework::AssetSystemRequestBus::BroadcastResult ( result , &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath @@ -412,41 +353,41 @@ namespace ScriptCanvasEditor OnScannedGraphResult(assetInfo); } - void Controller::OnScannedGraphResult([[maybe_unused]] const AZ::Data::AssetInfo& info) + void Controller::OnScannedGraphResult([[maybe_unused]] const SourceHandle& info) { m_view->progressBar->setValue(aznumeric_cast(m_handledAssetCount)); ++m_handledAssetCount; AddLogEntries(); } - void Controller::OnScanLoadFailure(const AZ::Data::AssetInfo& info) + void Controller::OnScanLoadFailure(const SourceHandle& info) { const int rowIndex = m_view->tableWidget->rowCount(); m_view->tableWidget->insertRow(rowIndex); QTableWidgetItem* rowName = new QTableWidgetItem - ( tr(AZStd::string::format("Load Error: %s", info.m_relativePath.c_str()).c_str())); + ( tr(AZStd::string::format("Load Error: %s", info.Path().c_str()).c_str())); m_view->tableWidget->setItem(rowIndex, static_cast(ColumnAsset), rowName); SetRowFailed(rowIndex, "Load failed"); OnScannedGraphResult(info); } - void Controller::OnScanUnFilteredGraph(const AZ::Data::AssetInfo& info) + void Controller::OnScanUnFilteredGraph(const SourceHandle& info) { OnScannedGraph(info, Filtered::No); } void Controller::OnUpgradeBegin ( const ModifyConfiguration& config - , [[maybe_unused]] const WorkingAssets& assets) + , [[maybe_unused]] const AZStd::vector& assets) { QString spinnerText = QStringLiteral("Upgrade in progress - "); - if (config.modifySingleAsset.m_assetId.IsValid()) + if (!config.modifySingleAsset.Path().empty()) { spinnerText.append(" single graph"); if (assets.size() == 1) { - for (auto* item : FindTableItems(assets.front().info)) + for (auto* item : FindTableItems(assets.front())) { int row = item->row(); SetRowBusy(row); @@ -497,7 +438,7 @@ namespace ScriptCanvasEditor m_view->scanButton->setEnabled(true); } - void Controller::OnUpgradeDependenciesGathered(const AZ::Data::AssetInfo& info, Result result) + void Controller::OnUpgradeDependenciesGathered(const SourceHandle& info, Result result) { for (auto* item : FindTableItems(info)) { @@ -526,7 +467,7 @@ namespace ScriptCanvasEditor void Controller::OnUpgradeDependencySortBegin ( [[maybe_unused]] const ModifyConfiguration& config - , const WorkingAssets& assets) + , const AZStd::vector& assets) { m_handledAssetCount = 0; m_view->progressBar->setVisible(true); @@ -552,7 +493,7 @@ namespace ScriptCanvasEditor void Controller::OnUpgradeDependencySortEnd ( [[maybe_unused]] const ModifyConfiguration& config - , const WorkingAssets& assets + , const AZStd::vector& assets , [[maybe_unused]] const AZStd::vector& sortedOrder) { m_handledAssetCount = 0; diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp index 3472f8d263..49a35eac8a 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/Tools/UpgradeTool/FileSaver.cpp @@ -15,6 +15,7 @@ #include #include #include +#include namespace ScriptCanvasEditor { @@ -27,6 +28,11 @@ namespace ScriptCanvasEditor , m_onComplete(onComplete) {} + const SourceHandle& FileSaver::GetSource() const + { + return m_source; + } + void FileSaver::PerformMove ( AZStd::string tmpFileName , AZStd::string target @@ -37,7 +43,7 @@ namespace ScriptCanvasEditor AZ::SystemTickBus::QueueFunction([this, tmpFileName]() { FileSaveResult result; - result.fileSaveError = "Failed to move updated file from temporary location to tmpFileName destination"; + result.fileSaveError = "Failed to move updated file from temporary location to original destination."; result.tempFileRemovalError = RemoveTempFile(tmpFileName); m_onComplete(result); }); @@ -65,7 +71,9 @@ namespace ScriptCanvasEditor auto streamer = AZ::Interface::Get(); AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(target.c_str()); // Bump the slice asset up in the asset processor's queue. - AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, target.c_str()); + AzFramework::AssetSystemRequestBus::Broadcast + (&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, target.c_str()); + AZ::SystemTickBus::QueueFunction([this, tmpFileName]() { FileSaveResult result; @@ -75,28 +83,29 @@ namespace ScriptCanvasEditor } else { - AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false, "moving converted file to tmpFileName destination failed: %s, trying again", target.c_str()); + AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false + , "moving converted file to tmpFileName destination failed: %s, trying again", target.c_str()); auto streamer = AZ::Interface::Get(); AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(target.c_str()); - streamer->SetRequestCompleteCallback(flushRequest, [this, tmpFileName, target, remainingAttempts]([[maybe_unused]] AZ::IO::FileRequestHandle request) + streamer->SetRequestCompleteCallback(flushRequest + , [this, tmpFileName, target, remainingAttempts]([[maybe_unused]] AZ::IO::FileRequestHandle request) { // Continue saving. - AZ::SystemTickBus::QueueFunction([this, tmpFileName, target, remainingAttempts]() { PerformMove(tmpFileName, target, remainingAttempts - 1); }); + AZ::SystemTickBus::QueueFunction( + [this, tmpFileName, target, remainingAttempts]() { PerformMove(tmpFileName, target, remainingAttempts - 1); }); }); streamer->QueueRequest(flushRequest); } } } - void FileSaver::OnSourceFileReleased(AZ::Data::Asset asset) + void FileSaver::OnSourceFileReleased(const SourceHandle& source) { - AZStd::string relativePath, fullPath; - AZ::Data::AssetCatalogRequestBus::BroadcastResult(relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, asset.GetId()); - bool fullPathFound = false; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(fullPathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, relativePath, fullPath); + AZStd::string fullPath = source.Path().c_str(); AZStd::string tmpFileName; // here we are saving the graph to a temp file instead of the original file and then copying the temp file to the original file. - // This ensures that AP will not a get a file change notification on an incomplete graph file causing it to fail processing. Temp files are ignored by AP. + // This ensures that AP will not a get a file change notification on an incomplete graph file causing it to fail processing. + // Temp files are ignored by AP. if (!AZ::IO::CreateTempFileName(fullPath.c_str(), tmpFileName)) { FileSaveResult result; @@ -105,23 +114,24 @@ namespace ScriptCanvasEditor return; } - bool tempSavedSucceeded = false; + AZStd::string saveError; + AZ::IO::FileIOStream fileStream(tmpFileName.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeText); if (fileStream.IsOpen()) { - if (asset.GetType() == azrtti_typeid()) + auto saveOutcome = ScriptCanvasEditor::SaveToStream(source, fileStream); + if (!saveOutcome.IsSuccess()) { - ScriptCanvasEditor::ScriptCanvasAssetHandler handler; - tempSavedSucceeded = handler.SaveAssetData(asset, &fileStream); + saveError = saveOutcome.TakeError(); } fileStream.Close(); } - if (!tempSavedSucceeded) + if (!saveError.empty()) { FileSaveResult result; - result.fileSaveError = "Save asset data to temporary file failed"; + result.fileSaveError = AZStd::string::format("Save asset data to temporary file failed: %s", saveError.c_str()); m_onComplete(result); return; } @@ -131,26 +141,26 @@ namespace ScriptCanvasEditor , fullPath.c_str() , true , [this, fullPath, tmpFileName]([[maybe_unused]] bool success, const AzToolsFramework::SourceControlFileInfo& info) - { - constexpr const size_t k_maxAttemps = 10; + { + constexpr const size_t k_maxAttemps = 10; - if (!info.IsReadOnly()) - { - PerformMove(tmpFileName, fullPath, k_maxAttemps); - } - else if (m_onReadOnlyFile && m_onReadOnlyFile()) - { - AZ::IO::SystemFile::SetWritable(info.m_filePath.c_str(), true); - PerformMove(tmpFileName, fullPath, k_maxAttemps); - } - else - { - FileSaveResult result; - result.fileSaveError = "Source file was and remained read-only"; - result.tempFileRemovalError = RemoveTempFile(tmpFileName); - m_onComplete(result); - } - }); + if (!info.IsReadOnly()) + { + PerformMove(tmpFileName, fullPath, k_maxAttemps); + } + else if (m_onReadOnlyFile && m_onReadOnlyFile()) + { + AZ::IO::SystemFile::SetWritable(info.m_filePath.c_str(), true); + PerformMove(tmpFileName, fullPath, k_maxAttemps); + } + else + { + FileSaveResult result; + result.fileSaveError = "Source file was and remained read-only"; + result.tempFileRemovalError = RemoveTempFile(tmpFileName); + m_onComplete(result); + } + }); } AZStd::string FileSaver::RemoveTempFile(AZStd::string_view tempFile) @@ -158,7 +168,7 @@ namespace ScriptCanvasEditor AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); if (!fileIO) { - return "GraphUpgradeComplete: No FileIO instance"; + return "No FileIO instance"; } if (fileIO->Exists(tempFile.data()) && !fileIO->Remove(tempFile.data())) @@ -169,30 +179,30 @@ namespace ScriptCanvasEditor return ""; } - void FileSaver::Save(AZ::Data::Asset asset) + void FileSaver::Save(const SourceHandle& source) { - AZStd::string relativePath, fullPath; - AZ::Data::AssetCatalogRequestBus::BroadcastResult(relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, asset.GetId()); - bool fullPathFound = false; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult - (fullPathFound - , &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath - , relativePath, fullPath); - - if (!fullPathFound) + m_source = source; + + if (source.Path().empty()) { FileSaveResult result; - result.fileSaveError = "Full source path not found"; + result.fileSaveError = "No save location specified"; m_onComplete(result); } else { auto streamer = AZ::Interface::Get(); - AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(fullPath); - streamer->SetRequestCompleteCallback(flushRequest, [this, asset]([[maybe_unused]] AZ::IO::FileRequestHandle request) + AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(source.Path().c_str()); + streamer->SetRequestCompleteCallback(flushRequest, [this]([[maybe_unused]] AZ::IO::FileRequestHandle request) { - this->OnSourceFileReleased(asset); + AZStd::lock_guard lock(m_mutex); + if (!m_sourceFileReleased) + { + m_sourceFileReleased = true; + AZ::SystemTickBus::QueueFunction([this]() { this->OnSourceFileReleased(m_source); }); + } }); + streamer->QueueRequest(flushRequest); } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.cpp index d7ff870b85..43d4e9496d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Graph.cpp @@ -51,6 +51,7 @@ namespace GraphCpp VariablePanelSymantics, AddVersionData, RemoveFunctionGraphMarker, + FixupVersionDataTypeId, // label your version above Current }; @@ -70,9 +71,17 @@ namespace ScriptCanvas componentElementNode.AddElementWithData(context, "m_assetType", azrtti_typeid()); } - if (componentElementNode.GetVersion() < GraphCpp::GraphVersion::RemoveFunctionGraphMarker) + if (auto subElement = componentElementNode.FindElement(AZ_CRC_CE("isFunctionGraph")); subElement > 0) { - componentElementNode.RemoveElementByName(AZ_CRC_CE("isFunctionGraph")); + componentElementNode.RemoveElement(subElement); + } + + if (auto subElement = componentElementNode.FindSubElement(AZ_CRC_CE("versionData"))) + { + if (subElement->GetId() == azrtti_typeid()) + { + componentElementNode.RemoveElementByName(AZ_CRC_CE("versionData")); + } } return true; @@ -1189,11 +1198,6 @@ namespace ScriptCanvas m_isObserved = isObserved; } - AZ::Data::AssetType Graph::GetAssetType() const - { - return m_assetType; - } - void Graph::VersioningRemoveSlot(ScriptCanvas::Node& scriptCanvasNode, const SlotId& slotId) { bool deletedSlot = true;