Merge remote-tracking branch 'upstream/development' into hultonha_LYN-2349_tube_delete_crash

Signed-off-by: hultonha <hultonha@amazon.co.uk>
monroegm-disable-blank-issue-2
hultonha 5 years ago
commit a365ae1da1

@ -6,13 +6,13 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
import logging
import os
import tempfile
import psutil
import ly_test_tools.log.log_monitor
import ly_test_tools.environment.process_utils as process_utils
import ly_test_tools.environment.waiter as waiter
from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole
from ly_remote_console.remote_console_commands import send_command_and_expect_response as send_command_and_expect_response
logger = logging.getLogger(__name__)
@ -95,7 +95,7 @@ def launch_and_validate_results_launcher(launcher, level, remote_console_instanc
return port_listening
if null_renderer:
launcher.args.extend(["-NullRenderer"])
launcher.args.extend(["-rhi=Null"])
# Start the Launcher
with launcher.start():
@ -110,8 +110,8 @@ def launch_and_validate_results_launcher(launcher, level, remote_console_instanc
# Load the specified level in the launcher
send_command_and_expect_response(remote_console_instance,
f"map {level}",
"LEVEL_LOAD_COMPLETE", timeout=30)
f"LoadLevel {level}",
"LEVEL_LOAD_END", timeout=30)
# Monitor the console for expected lines
for line in expected_lines:

@ -36,8 +36,8 @@ class TestAltitudeFilterFilterStageToggle(EditorTestHelper):
:return: None
"""
PREPROCESS_INSTANCE_COUNT = 24
POSTPROCESS_INSTANCE_COUNT = 18
PREPROCESS_INSTANCE_COUNT = 44
POSTPROCESS_INSTANCE_COUNT = 34
# Create empty level
self.test_success = self.create_level(
@ -62,25 +62,7 @@ class TestAltitudeFilterFilterStageToggle(EditorTestHelper):
dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0)
# Add entity with Mesh to replicate creation of hills
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 40.0, 40.0, 40.0)
# Disable/Re-enable Mesh component due to ATOM-14299
general.idle_wait(1.0)
editor.EditorComponentAPIBus(bus.Broadcast, 'DisableComponents', [hill_entity.components[0]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', hill_entity.components[0])
if is_enabled:
print("Mesh component is still enabled")
else:
print("Mesh component was disabled")
editor.EditorComponentAPIBus(bus.Broadcast, 'EnableComponents', [hill_entity.components[0]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', hill_entity.components[0])
if is_enabled:
print("Mesh component is now enabled")
else:
print("Mesh component is still disabled")
# Increase Box Shape size to encompass the hills
vegetation.get_set_test(1, "Box Shape|Box Configuration|Dimensions", math.Vector3(100.0, 100.0, 100.0))
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 10.0)
# Set a Min Altitude of 38 and Max of 40 in Vegetation Altitude Filter
vegetation.get_set_test(3, "Configuration|Altitude Min", 38.0)

@ -9,9 +9,10 @@ import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.asset as asset
import azlmbr.components as components
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.entity as EntityId
import azlmbr.entity as entity
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.paths
@ -83,18 +84,12 @@ class TestDynamicSliceInstanceSpawnerEmbeddedEditor(EditorTestHelper):
self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances")
self.test_success = self.test_success and num_found == num_expected_instances
# 5) Create a new entity with a Camera component for testing in the launcher
# 5) Move the default Camera entity for testing in the launcher
cam_position = math.Vector3(512.0, 500.0, 35.0)
camera_component = ["Camera"]
new_entity_id2 = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", cam_position, EntityId.EntityId()
)
if new_entity_id2.IsValid():
self.log("Camera entity created")
camera_entity = hydra.Entity("Camera Entity", new_entity_id2)
camera_entity.components = []
for component in camera_component:
camera_entity.components.append(hydra.add_component(component, new_entity_id2))
search_filter = entity.SearchFilter()
search_filter.names = ["Camera"]
search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position)
# 6) Save and export to engine
general.save_level()

@ -11,7 +11,8 @@ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.entity as EntityId
import azlmbr.components as components
import azlmbr.entity as entity
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.paths
@ -68,7 +69,7 @@ class TestDynamicSliceInstanceSpawnerExternalEditor(EditorTestHelper):
veg_area_required_components = ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List",
"Script Canvas"]
new_entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, entity.EntityId()
)
if new_entity_id.IsValid():
self.log("Spawner entity created")
@ -106,18 +107,12 @@ class TestDynamicSliceInstanceSpawnerExternalEditor(EditorTestHelper):
self.log(f"Expected {num_expected_instances} instances - Found {num_found} instances")
self.test_success = self.test_success and num_found == num_expected_instances
# 5) Create a new entity with a Camera component for testing in the launcher
entity_position = math.Vector3(512.0, 500.0, 35.0)
camera_component = ["Camera"]
new_entity_id2 = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
if new_entity_id2.IsValid():
self.log("Camera entity created")
camera_entity = hydra.Entity("Camera Entity", new_entity_id2)
camera_entity.components = []
for component in camera_component:
camera_entity.components.append(hydra.add_component(component, new_entity_id2))
# 5) Move the default Camera entity for testing in the launcher
cam_position = math.Vector3(512.0, 500.0, 35.0)
search_filter = entity.SearchFilter()
search_filter.names = ["Camera"]
search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position)
# 6) Save and export to engine
general.save_level()

@ -18,9 +18,9 @@ import azlmbr.areasystem as areasystem
import azlmbr.legacy.general as general
import azlmbr
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.components as components
import azlmbr.math as math
import azlmbr.entity as EntityId
import azlmbr.entity as entity
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
@ -134,20 +134,14 @@ class TestVegLayerBlenderCreated(EditorTestHelper):
purple_count += 1
self.test_success = pink_count == purple_count and (pink_count + purple_count == num_expected) and self.test_success
# 5) Create a new entity with a Camera component for testing in the launcher
entity_position = math.Vector3(500.0, 500.0, 47.0)
rot_degrees_vector = math.Vector3(radians(-55.0), radians(28.5), radians(-17.0))
camera_component = ["Camera"]
camera_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
if camera_id.IsValid():
self.log("Camera entity created")
camera_entity = hydra.Entity("Camera Entity", camera_id)
camera_entity.components = []
for component in camera_component:
camera_entity.components.append(hydra.add_component(component, camera_id))
azlmbr.components.TransformBus(bus.Event, "SetLocalRotation", camera_id, rot_degrees_vector)
# 5) Move the default Camera entity for testing in the launcher
cam_position = math.Vector3(500.0, 500.0, 47.0)
cam_rot_degrees_vector = math.Vector3(radians(-55.0), radians(28.5), radians(-17.0))
search_filter = entity.SearchFilter()
search_filter.names = ["Camera"]
search_entity_ids = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
components.TransformBus(bus.Event, "MoveEntity", search_entity_ids[0], cam_position)
azlmbr.components.TransformBus(bus.Event, "SetLocalRotation", search_entity_ids[0], cam_rot_degrees_vector)
# 6) Save and export level
general.save_level()

@ -34,8 +34,8 @@ class TestLayerSpawnerFilterStageToggle(EditorTestHelper):
:return: None
"""
PREPROCESS_INSTANCE_COUNT = 425
POSTPROCESS_INSTANCE_COUNT = 430
PREPROCESS_INSTANCE_COUNT = 21
POSTPROCESS_INSTANCE_COUNT = 19
# Create empty level
self.test_success = self.create_level(
@ -56,7 +56,6 @@ class TestLayerSpawnerFilterStageToggle(EditorTestHelper):
vegetation_entity.add_component("Vegetation Altitude Filter")
vegetation_entity.add_component("Vegetation Position Modifier")
# Create a child entity under vegetation area
child_entity = hydra.Entity("child_entity")
components_to_add = ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"]
@ -66,29 +65,13 @@ class TestLayerSpawnerFilterStageToggle(EditorTestHelper):
vegetation_entity.get_set_test(4, "Configuration|Position X|Gradient|Gradient Entity Id", child_entity.id)
vegetation_entity.get_set_test(4, "Configuration|Position Y|Gradient|Gradient Entity Id", child_entity.id)
# Set the min and max values for Altitude Filter
vegetation_entity.get_set_test(3, "Configuration|Altitude Min", 32.0)
vegetation_entity.get_set_test(3, "Configuration|Altitude Max", 35.0)
vegetation_entity.get_set_test(3, "Configuration|Altitude Min", 34.0)
vegetation_entity.get_set_test(3, "Configuration|Altitude Max", 38.0)
# Add entity with Mesh to replicate creation of hills and a flat surface to plant on
dynveg.create_surface_entity("Flat Surface", position, 32.0, 32.0, 1.0)
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 4.0, 4.0, 4.0)
# Disable/Re-enable Mesh component due to ATOM-14299
general.idle_wait(1.0)
editor.EditorComponentAPIBus(bus.Broadcast, 'DisableComponents', [hill_entity.components[0]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', hill_entity.components[0])
if is_enabled:
print("Mesh component is still enabled")
else:
print("Mesh component was disabled")
editor.EditorComponentAPIBus(bus.Broadcast, 'EnableComponents', [hill_entity.components[0]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', hill_entity.components[0])
if is_enabled:
print("Mesh component is now enabled")
else:
print("Mesh component is still disabled")
hill_entity = dynveg.create_mesh_surface_entity_with_slopes("hill", position, 4.0)
# Set the filter stage to preprocess and postprocess respectively and verify instance count
vegetation_entity.get_set_test(0, "Configuration|Filter Stage", 1)

@ -13,7 +13,7 @@ import azlmbr.legacy.general as general
import azlmbr.math as math
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from automatedtesting_shared.editor_test_helper import EditorTestHelper
from editor_python_test_tools.editor_test_helper import EditorTestHelper
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg

@ -82,22 +82,7 @@ class test_MeshBlocker_InstancesBlockedByMesh(EditorTestHelper):
bus.Broadcast, "GetAssetIdByPath", os.path.join("objects", "_primitives", "_box_1x1.azmodel"), math.Uuid(),
False)
blocker_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", cubeId)
components.TransformBus(bus.Event, "SetLocalScale", blocker_entity.id, math.Vector3(2.0, 2.0, 2.0))
# Disable/Re-enable Mesh component due to ATOM-14299
general.idle_wait(1.0)
editor.EditorComponentAPIBus(bus.Broadcast, 'DisableComponents', [blocker_entity.components[1]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', blocker_entity.components[1])
if is_enabled:
print("Mesh component is still enabled")
else:
print("Mesh component was disabled")
editor.EditorComponentAPIBus(bus.Broadcast, 'EnableComponents', [blocker_entity.components[1]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', blocker_entity.components[1])
if is_enabled:
print("Mesh component is now enabled")
else:
print("Mesh component is still disabled")
components.TransformBus(bus.Event, "SetLocalUniformScale", blocker_entity.id, 2.0)
# Verify spawned instance counts are accurate after addition of Blocker Entity
num_expected = 160 # Number of "PurpleFlower"s that plant on a 10 x 10 surface minus 2m blocker cube

@ -88,24 +88,9 @@ class test_MeshBlocker_InstancesBlockedByMeshHeightTuning(EditorTestHelper):
bus.Broadcast, "GetAssetIdByPath", os.path.join("objects", "_primitives", "_box_1x1.azmodel"), math.Uuid(),
False)
blocker_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", sphere_id)
components.TransformBus(bus.Event, "SetLocalScale", blocker_entity.id, math.Vector3(5.0, 5.0, 5.0))
components.TransformBus(bus.Event, "SetLocalUniformScale", blocker_entity.id, 5.0)
components.TransformBus(bus.Event, "SetLocalRotation", blocker_entity.id, math.Vector3(0.0, y_rotation, 0.0))
# Disable/Re-enable Mesh component due to ATOM-14299
general.idle_wait(1.0)
editor.EditorComponentAPIBus(bus.Broadcast, 'DisableComponents', [blocker_entity.components[1]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', blocker_entity.components[1])
if is_enabled:
print("Mesh component is still enabled")
else:
print("Mesh component was disabled")
editor.EditorComponentAPIBus(bus.Broadcast, 'EnableComponents', [blocker_entity.components[1]])
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', blocker_entity.components[1])
if is_enabled:
print("Mesh component is now enabled")
else:
print("Mesh component is still disabled")
# 5) Adjust the height Max percentage values of blocker
blocker_entity.get_set_test(0, "Configuration|Mesh Height Percent Max", 0.8)

@ -90,7 +90,6 @@ class TestDynamicSliceInstanceSpawner(object):
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.parametrize("launcher_platform", ['windows'])
@pytest.mark.skip # ATOM-14703
def test_DynamicSliceInstanceSpawner_Embedded_E2E_Launcher(self, workspace, launcher, level,
remote_console_instance, project, launcher_platform):
@ -126,7 +125,6 @@ class TestDynamicSliceInstanceSpawner(object):
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.parametrize("launcher_platform", ['windows'])
@pytest.mark.skip # ATOM-14703
def test_DynamicSliceInstanceSpawner_External_E2E_Launcher(self, workspace, launcher, level,
remote_console_instance, project, launcher_platform):

@ -68,7 +68,6 @@ class TestLayerBlender(object):
"Entity has a Box Shape component",
"Blender Configuration|Vegetation Areas: SUCCESS",
"Blender Box Shape|Box Configuration|Dimensions: SUCCESS",
"Camera entity created",
"LayerBlender_E2E_Editor: result=SUCCESS"
]
@ -85,12 +84,11 @@ class TestLayerBlender(object):
@pytest.mark.BAT
@pytest.mark.SUITE_periodic
@pytest.mark.dynveg_area
@pytest.mark.xfail
@pytest.mark.parametrize("launcher_platform", ['windows'])
def test_LayerBlender_E2E_Launcher(self, workspace, project, launcher, level, remote_console_instance,
launcher_platform):
launcher.args.extend(["-NullRenderer"])
launcher.args.extend(["-rhi=Null"])
launcher.start()
assert launcher.is_alive(), "Launcher failed to start"

@ -121,7 +121,7 @@ class TestLayerSpawner(object):
@pytest.mark.test_case_id("C30000751")
@pytest.mark.SUITE_sandbox
@pytest.mark.dynveg_misc
@pytest.mark.skip # ATOM-14828
@pytest.mark.skip # https://github.com/o3de/o3de/issues/2038
def test_LayerSpawner_InstancesRefreshUsingCorrectViewportCamera(self, request, editor, level, launcher_platform):
expected_lines = [
@ -136,5 +136,6 @@ class TestLayerSpawner(object):
editor,
"LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py",
expected_lines,
cfg_args=[level]
cfg_args=[level],
null_renderer=False
)

@ -34,7 +34,7 @@ def create_surface_entity(name, center_point, box_size_x, box_size_y, box_size_z
return surface_entity
def create_mesh_surface_entity_with_slopes(name, center_point, scale_x, scale_y, scale_z):
def create_mesh_surface_entity_with_slopes(name, center_point, uniform_scale):
# Creates an entity with the assigned mesh_asset as the specified scale and sets up as a planting surface
mesh_asset_path = os.path.join("models", "sphere.azmodel")
mesh_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", mesh_asset_path, math.Uuid(),
@ -47,7 +47,7 @@ def create_mesh_surface_entity_with_slopes(name, center_point, scale_x, scale_y,
if surface_entity.id.IsValid():
print(f"'{surface_entity.name}' created")
hydra.get_set_test(surface_entity, 0, "Controller|Configuration|Mesh Asset", mesh_asset)
components.TransformBus(bus.Event, "SetLocalScale", surface_entity.id, math.Vector3(scale_x, scale_y, scale_z))
components.TransformBus(bus.Event, "SetLocalUniformScale", surface_entity.id, uniform_scale)
return surface_entity

@ -742,14 +742,14 @@
</Class>
<Class name="int" field="methodType" value="2" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
<Class name="AZStd::string" field="methodName" value="GetOnPostsimulateEvent" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="className" value="System Interface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="className" value="PhysicsSystemInterface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::vector" field="namespaces" type="{99DAD0BC-740E-5E82-826B-8FC7968CC02C}"/>
<Class name="AZStd::vector" field="resultSlotIDs" type="{D0B13803-101B-54D8-914C-0DA49FDFA268}">
<Class name="SlotId" field="element" version="2" type="{14C629F6-467B-46FE-8B63-48FDFCA42175}">
<Class name="AZ::Uuid" field="m_id" value="{00000000-0000-0000-0000-000000000000}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="AZStd::string" field="prettyClassName" value="System Interface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="prettyClassName" value="PhysicsSystemInterface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
</Class>
<Class name="bool" field="IsDependencyReady" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>

@ -1045,14 +1045,14 @@
</Class>
<Class name="int" field="methodType" value="2" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
<Class name="AZStd::string" field="methodName" value="GetOnPostsimulateEvent" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="className" value="System Interface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="className" value="PhysicsSystemInterface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::vector" field="namespaces" type="{99DAD0BC-740E-5E82-826B-8FC7968CC02C}"/>
<Class name="AZStd::vector" field="resultSlotIDs" type="{D0B13803-101B-54D8-914C-0DA49FDFA268}">
<Class name="SlotId" field="element" version="2" type="{14C629F6-467B-46FE-8B63-48FDFCA42175}">
<Class name="AZ::Uuid" field="m_id" value="{00000000-0000-0000-0000-000000000000}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="AZStd::string" field="prettyClassName" value="System Interface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="prettyClassName" value="PhysicsSystemInterface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
</Class>
<Class name="bool" field="IsDependencyReady" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>

@ -1085,14 +1085,14 @@
</Class>
<Class name="int" field="methodType" value="2" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
<Class name="AZStd::string" field="methodName" value="GetOnPresimulateEvent" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="className" value="System Interface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="className" value="PhysicsSystemInterface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::vector" field="namespaces" type="{99DAD0BC-740E-5E82-826B-8FC7968CC02C}"/>
<Class name="AZStd::vector" field="resultSlotIDs" type="{D0B13803-101B-54D8-914C-0DA49FDFA268}">
<Class name="SlotId" field="element" version="2" type="{14C629F6-467B-46FE-8B63-48FDFCA42175}">
<Class name="AZ::Uuid" field="m_id" value="{00000000-0000-0000-0000-000000000000}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="AZStd::string" field="prettyClassName" value="System Interface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::string" field="prettyClassName" value="PhysicsSystemInterface" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
</Class>
<Class name="bool" field="IsDependencyReady" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>

@ -1896,7 +1896,7 @@ void EditorViewportWidget::SetViewTM(const Matrix34& viewTM, bool bMoveOnly)
if (m_pressedKeyState != KeyPressedState::PressedInPreviousFrame)
{
CUndo undo("Move Camera");
AzToolsFramework::ScopedUndoBatch undo("Move Camera");
if (bMoveOnly)
{
// specify eObjectUpdateFlags_UserInput so that an undo command gets logged
@ -1932,7 +1932,7 @@ void EditorViewportWidget::SetViewTM(const Matrix34& viewTM, bool bMoveOnly)
if (m_pressedKeyState != KeyPressedState::PressedInPreviousFrame)
{
CUndo undo("Move Camera");
AzToolsFramework::ScopedUndoBatch undo("Move Camera");
if (bMoveOnly)
{
AZ::TransformBus::Event(
@ -1945,6 +1945,8 @@ void EditorViewportWidget::SetViewTM(const Matrix34& viewTM, bool bMoveOnly)
m_viewEntityId, &AZ::TransformInterface::SetWorldTM,
LYTransformToAZTransform(camMatrix));
}
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::AddDirtyEntity, m_viewEntityId);
}
else
{

@ -5,7 +5,7 @@
<key>CFBundleExecutable</key>
<string>Editor</string>
<key>CFBundleIdentifier</key>
<string>com.Amazon.Lumberyard.Editor</string>
<string>org.O3DE.Editor</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>

@ -46,4 +46,8 @@ ly_add_target(
AZ::AzCore
AZ::AzToolsFramework
AZ::AzQtComponents
RUNTIME_DEPENDENCIES
AZ::AzCore
AZ::AzToolsFramework
AZ::AzQtComponents
)

@ -20,12 +20,12 @@
#include "ValidationHandler.h"
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/IO/Path/Path.h>
#include "AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h"
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyManagerComponent.h>
#include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
#include <Util/FileUtil.h>
#include <QMessageBox>
#include <QCloseEvent>
@ -52,7 +52,7 @@ namespace ProjectSettingsTool
PlatformEnabled(PlatformId::Ios) ?
ProjectSettingsContainer::PlistInitVector({
ProjectSettingsContainer::PlatformAndPath
{ PlatformId::Ios, m_projectRoot + PlatformResourcesFolder(PlatformId::Ios) }
{ PlatformId::Ios, GetPlatformResource(PlatformId::Ios) }
})
:
ProjectSettingsContainer::PlistInitVector())
@ -647,33 +647,38 @@ namespace ProjectSettingsTool
// iOS can be disabled if the plist file is missing
if (platformId == PlatformId::Ios)
{
const AZStd::string filename = m_projectRoot + PlatformResourcesFolder(platformId);
return CFileUtil::FileExists(filename.c_str());
AZStd::string plistPath = GetPlatformResource(platformId);
return !plistPath.empty();
}
return true;
}
const char* ProjectSettingsToolWindow::PlatformResourcesFolder(PlatformId platformId)
AZStd::string ProjectSettingsToolWindow::GetPlatformResource(PlatformId platformId)
{
if (platformId == PlatformId::Ios)
{
const AZStd::string firstfilename = m_projectRoot + "/Gem/Resources/Platform/iOS/Info.plist";
if (CFileUtil::FileExists(firstfilename.c_str()))
{
return "/Gem/Resources/Platform/iOS/Info.plist";
}
else
const char* searchPaths[] = {
"Resources/Platform/iOS/Info.plist",
// legacy paths
"Gem/Resources/Platform/iOS/Info.plist",
"Gem/Resources/IOSLauncher/Info.plist",
};
for (auto relPath : searchPaths)
{
const AZStd::string filename = m_projectRoot + "/Gem/Resources/IOSLauncher/Info.plist";
if (CFileUtil::FileExists(filename.c_str()))
AZ::IO::FixedMaxPath projectPlist{ m_projectRoot };
projectPlist /= relPath;
if (AZ::IO::SystemFile::Exists(projectPlist.c_str()))
{
return "/Gem/Resources/IOSLauncher/Info.plist";
return projectPlist.LexicallyNormal().String();
}
}
}
return nullptr;
return AZStd::string();
}
#include <moc_ProjectSettingsToolWindow.cpp>

@ -137,8 +137,8 @@ namespace ProjectSettingsTool
// returns true if the platform is enabled
bool PlatformEnabled(PlatformId platformId);
// returns the resource folder
const char* PlatformResourcesFolder(PlatformId platformId);
// returns the main platform specific resource file e.g. for iOS it would be the Info.plist
AZStd::string GetPlatformResource(PlatformId platformId);
// The ui for the window
QScopedPointer<Ui::ProjectSettingsToolWidget> m_ui;

@ -116,7 +116,7 @@ namespace AZ
{
const char* uuidString = nullptr;
unsigned int uuidStringLength = 0;
if (dc.ReadArg(0, uuidString) && dc.ReadValue(1, uuidStringLength))
if (dc.ReadArg(0, uuidString) && dc.ReadArg(1, uuidStringLength))
{
dc.PushResult(Uuid(uuidString, uuidStringLength));
}

@ -205,6 +205,25 @@ namespace AzFramework
class InputDeviceImplementationRequest : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId so that they are only
//! handled by one input device that has connected to the bus using that unique id, or they
//! can be broadcast to all input devices that have connected to the bus, regardless of id.
//! Connected input devices are ordered by their local player index from lowest to highest.
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ByIdAndOrdered;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests should be handled by only one input device connected to each id
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId
using BusIdType = InputDeviceId;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests are handled by connected devices in the order of local player index
using BusIdOrderCompare = AZStd::less<BusIdType>;
////////////////////////////////////////////////////////////////////////////////////////////
//! Alias for the EBus implementation of this interface
using Bus = AZ::EBus<InputDeviceImplementationRequest<InputDeviceType>>;
@ -214,11 +233,12 @@ namespace AzFramework
using CreateFunctionType = typename InputDeviceType::Implementation*(*)(InputDeviceType&);
////////////////////////////////////////////////////////////////////////////////////////////
//! Create a custom implementation for all the existing instances of this input device type.
//! Set a custom implementation for this input device type, either for a specific instance
//! by addressing the call to an InputDeviceId, or for all existing instances by broadcast.
//! Passing InputDeviceType::Implementation::Create as the argument will create the default
//! device implementation, while passing nullptr will delete any existing implementation.
//! \param[in] createFunction Pointer to the function that will create the implementation.
virtual void CreateCustomImplementation(CreateFunctionType createFunction) = 0;
virtual void SetCustomImplementation(CreateFunctionType createFunction) = 0;
};
////////////////////////////////////////////////////////////////////////////////////////////////
@ -238,7 +258,7 @@ namespace AzFramework
AZ_INLINE InputDeviceImplementationRequestHandler(InputDeviceType& inputDevice)
: m_inputDevice(inputDevice)
{
InputDeviceImplementationRequest<InputDeviceType>::Bus::Handler::BusConnect();
InputDeviceImplementationRequest<InputDeviceType>::Bus::Handler::BusConnect(m_inputDevice.GetInputDeviceId());
}
////////////////////////////////////////////////////////////////////////////////////////////
@ -251,8 +271,8 @@ namespace AzFramework
using CreateFunctionType = typename InputDeviceType::Implementation*(*)(InputDeviceType&);
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref InputDeviceImplementationRequest<InputDeviceType>::CreateCustomImplementation
AZ_INLINE void CreateCustomImplementation(CreateFunctionType createFunction) override
//! \ref InputDeviceImplementationRequest<InputDeviceType>::SetCustomImplementation
AZ_INLINE void SetCustomImplementation(CreateFunctionType createFunction) override
{
AZStd::unique_ptr<typename InputDeviceType::Implementation> newImplementation;
if (createFunction)

@ -63,6 +63,9 @@ namespace AzToolsFramework
//! Signal the Python handler to stop
virtual bool StopPython(bool silenceWarnings = false) = 0;
//! Query to determine if the Python VM has been initialized indicating an active state
virtual bool IsPythonActive() = 0;
//! Determines if the caller needs to wait for the Python VM to initialize (non-main thread only)
virtual void WaitForInitialization() {}

@ -7,6 +7,8 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <AzCore/RTTI/BehaviorContext.h>
namespace AZ
{
@ -31,6 +33,12 @@ namespace AZ
{
serializeContext->Class<ExportingComponent, AZ::Component>()->Version(2);
}
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
Events::ExportProductList::Reflect(behaviorContext);
}
}
} // namespace SceneCore
} // namespace SceneAPI

@ -211,6 +211,7 @@ namespace AZ
AZ::SceneAPI::Containers::SceneGraph::Reflect(context);
AZ::SceneAPI::Containers::SceneManifest::Reflect(context);
AZ::SceneAPI::Containers::RuleContainer::Reflect(context);
AZ::SceneAPI::SceneCore::ExportingComponent::Reflect(context);
}
void Activate()

@ -6,6 +6,8 @@
*/
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/limits.h>
namespace AZ
{
@ -49,6 +51,45 @@ namespace AZ
return *this;
}
void ExportProductList::Reflect(ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ExportProduct>()->Version(1);
serializeContext->Class<ExportProductList>()->Version(1);
}
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<ExportProduct>("ExportProduct")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Property("filename", BehaviorValueProperty(&ExportProduct::m_filename))
->Property("sourceId", BehaviorValueProperty(&ExportProduct::m_id))
->Property("assetType", BehaviorValueProperty(&ExportProduct::m_assetType))
->Property("productDependencies", BehaviorValueProperty(&ExportProduct::m_productDependencies))
->Property("subId",
[](ExportProduct* self) { return self->m_subId.has_value() ? self->m_subId.value() : 0; },
[](ExportProduct* self, u32 subId) { self->m_subId = AZStd::optional<u32>(subId); });
behaviorContext->Class<ExportProductList>("ExportProductList")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Method("AddProduct", [](ExportProductList& self, ExportProduct& product)
{
self.AddProduct(
product.m_filename,
product.m_id,
product.m_assetType,
product.m_lod,
product.m_subId,
product.m_dependencyFlags);
})
->Method("GetProducts", &ExportProductList::GetProducts)
->Method("AddDependencyToProduct", &ExportProductList::AddDependencyToProduct);
}
}
ExportProduct& ExportProductList::AddProduct(const AZStd::string& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId,
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags)
{

@ -14,6 +14,8 @@
namespace AZ
{
class ReflectContext;
namespace SceneAPI
{
namespace Events
@ -24,6 +26,7 @@ namespace AZ
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags = Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad));
SCENE_CORE_API ExportProduct(AZStd::string&& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId,
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags = Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad));
ExportProduct() = default;
ExportProduct(const ExportProduct& rhs) = default;
SCENE_CORE_API ExportProduct(ExportProduct&& rhs);
@ -54,6 +57,8 @@ namespace AZ
class ExportProductList
{
public:
static void Reflect(ReflectContext* context);
SCENE_CORE_API ExportProduct& AddProduct(const AZStd::string& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId,
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags = Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad));
SCENE_CORE_API ExportProduct& AddProduct(AZStd::string&& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId,
@ -69,3 +74,9 @@ namespace AZ
} // namespace Events
} // namespace SceneAPI
} // namespace AZ
namespace AZ
{
AZ_TYPE_INFO_SPECIALIZE(SceneAPI::Events::ExportProduct, "{6054EDCB-4C04-4D96-BF26-704999FFB725}");
AZ_TYPE_INFO_SPECIALIZE(SceneAPI::Events::ExportProductList, "{1C76A51F-431B-4987-B653-CFCC940D0D0F}");
}

@ -10,6 +10,7 @@
#include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h>
@ -75,15 +76,16 @@ namespace AZ
filename += s_extension;
filename += s_generated;
AZStd::string altManifestPath = path;
AZStd::string altManifestFolder = path;
AzFramework::ApplicationRequests::Bus::Broadcast(
&AzFramework::ApplicationRequests::Bus::Events::MakePathRootRelative,
altManifestPath);
&AzFramework::ApplicationRequests::Bus::Events::MakePathRelative,
altManifestFolder,
AZ::Utils::GetProjectPath().c_str());
AZ::StringFunc::Path::GetFolderPath(altManifestPath.c_str(), altManifestPath);
AZ::StringFunc::Path::GetFolderPath(altManifestFolder.c_str(), altManifestFolder);
AZStd::string generatedAssetInfoPath;
AZ::StringFunc::Path::Join(assetCacheRoot.c_str(), altManifestPath.c_str(), generatedAssetInfoPath);
AZ::StringFunc::Path::Join(assetCacheRoot.c_str(), altManifestFolder.c_str(), generatedAssetInfoPath);
AZ::StringFunc::Path::ConstructFull(generatedAssetInfoPath.c_str(), filename.c_str(), generatedAssetInfoPath);
if (!AZ::IO::FileIOBase::GetInstance()->Exists(generatedAssetInfoPath.c_str()))

@ -25,213 +25,319 @@
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneData/Rules/ScriptProcessorRule.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
namespace AZ
namespace AZ::SceneAPI::Behaviors
{
namespace SceneAPI
class EditorPythonConsoleNotificationHandler final
: protected AzToolsFramework::EditorPythonConsoleNotificationBus::Handler
{
namespace Behaviors
public:
EditorPythonConsoleNotificationHandler()
{
class EditorPythonConsoleNotificationHandler final
: protected AzToolsFramework::EditorPythonConsoleNotificationBus::Handler
{
public:
EditorPythonConsoleNotificationHandler()
{
BusConnect();
}
BusConnect();
}
~EditorPythonConsoleNotificationHandler()
{
BusDisconnect();
}
~EditorPythonConsoleNotificationHandler()
{
BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////
// AzToolsFramework::EditorPythonConsoleNotifications
void OnTraceMessage([[maybe_unused]] AZStd::string_view message) override
{
using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(LogWindow, "%.*s \n", AZ_STRING_ARG(message));
}
////////////////////////////////////////////////////////////////////////////////////////////
// AzToolsFramework::EditorPythonConsoleNotifications
void OnTraceMessage([[maybe_unused]] AZStd::string_view message) override
{
using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(LogWindow, "%.*s \n", AZ_STRING_ARG(message));
}
void OnErrorMessage([[maybe_unused]] AZStd::string_view message) override
{
using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(ErrorWindow, "[ERROR] %.*s \n", AZ_STRING_ARG(message));
}
void OnErrorMessage([[maybe_unused]] AZStd::string_view message) override
{
using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(ErrorWindow, "[ERROR] %.*s \n", AZ_STRING_ARG(message));
}
void OnExceptionMessage([[maybe_unused]] AZStd::string_view message) override
{
using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(ErrorWindow, "[EXCEPTION] %.*s \n", AZ_STRING_ARG(message));
}
};
void OnExceptionMessage([[maybe_unused]] AZStd::string_view message) override
{
using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(ErrorWindow, "[EXCEPTION] %.*s \n", AZ_STRING_ARG(message));
}
};
// a event bus to signal during scene building
struct ScriptBuildingNotifications
: public AZ::EBusTraits
{
virtual AZStd::string OnUpdateManifest(Containers::Scene& scene) = 0;
};
using ScriptBuildingNotificationBus = AZ::EBus<ScriptBuildingNotifications>;
using ExportProductList = AZ::SceneAPI::Events::ExportProductList;
// a event bus to signal during scene building
struct ScriptBuildingNotifications
: public AZ::EBusTraits
{
virtual AZStd::string OnUpdateManifest(Containers::Scene& scene) = 0;
virtual ExportProductList OnPrepareForExport(
const Containers::Scene& scene,
AZStd::string_view outputDirectory,
AZStd::string_view platformIdentifier,
const ExportProductList& productList) = 0;
};
using ScriptBuildingNotificationBus = AZ::EBus<ScriptBuildingNotifications>;
// a back end to handle scene builder events for a script
struct ScriptBuildingNotificationBusHandler final
: public ScriptBuildingNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
AZ_EBUS_BEHAVIOR_BINDER(
ScriptBuildingNotificationBusHandler,
"{DF2B51DE-A4D0-4139-B5D0-DF185832380D}",
AZ::SystemAllocator,
OnUpdateManifest,
OnPrepareForExport);
// a back end to handle scene builder events for a script
struct ScriptBuildingNotificationBusHandler final
: public ScriptBuildingNotificationBus::Handler
, public AZ::BehaviorEBusHandler
virtual ~ScriptBuildingNotificationBusHandler() = default;
AZStd::string OnUpdateManifest(Containers::Scene& scene) override
{
AZStd::string result;
CallResult(result, FN_OnUpdateManifest, scene);
return result;
}
ExportProductList OnPrepareForExport(
const Containers::Scene& scene,
AZStd::string_view outputDirectory,
AZStd::string_view platformIdentifier,
const ExportProductList& productList) override
{
ExportProductList result;
CallResult(result, FN_OnPrepareForExport, scene, outputDirectory, platformIdentifier, productList);
return result;
}
static void Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
AZ_EBUS_BEHAVIOR_BINDER(
ScriptBuildingNotificationBusHandler,
"{DF2B51DE-A4D0-4139-B5D0-DF185832380D}",
AZ::SystemAllocator,
OnUpdateManifest);
behaviorContext->EBus<ScriptBuildingNotificationBus>("ScriptBuildingNotificationBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Handler<ScriptBuildingNotificationBusHandler>()
->Event("OnUpdateManifest", &ScriptBuildingNotificationBus::Events::OnUpdateManifest)
->Event("OnPrepareForExport", &ScriptBuildingNotificationBus::Events::OnPrepareForExport);
}
}
};
virtual ~ScriptBuildingNotificationBusHandler() = default;
struct ScriptProcessorRuleBehavior::ExportEventHandler final
: public AZ::SceneAPI::SceneCore::ExportingComponent
{
using PreExportEventContextFunction = AZStd::function<bool(Events::PreExportEventContext&)>;
PreExportEventContextFunction m_preExportEventContextFunction;
AZStd::string OnUpdateManifest(Containers::Scene& scene) override
{
AZStd::string result;
CallResult(result, FN_OnUpdateManifest, scene);
return result;
}
ExportEventHandler(PreExportEventContextFunction preExportEventContextFunction)
: m_preExportEventContextFunction(preExportEventContextFunction)
{
BindToCall(&ExportEventHandler::PrepareForExport);
AZ::SceneAPI::SceneCore::ExportingComponent::Activate();
}
static void Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<ScriptBuildingNotificationBus>("ScriptBuildingNotificationBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Handler<ScriptBuildingNotificationBusHandler>()
->Event("OnUpdateManifest", &ScriptBuildingNotificationBus::Events::OnUpdateManifest);
}
}
};
~ExportEventHandler()
{
AZ::SceneAPI::SceneCore::ExportingComponent::Deactivate();
}
// this allows a Python script to add product assets on "scene export"
Events::ProcessingResult PrepareForExport(Events::PreExportEventContext& context)
{
return m_preExportEventContextFunction(context) ? Events::ProcessingResult::Success : Events::ProcessingResult::Failure;
}
};
void ScriptProcessorRuleBehavior::Activate()
{
Events::AssetImportRequestBus::Handler::BusConnect();
m_exportEventHandler = AZStd::make_shared<ExportEventHandler>([this](Events::PreExportEventContext& context)
{
return this->DoPrepareForExport(context);
});
}
void ScriptProcessorRuleBehavior::Deactivate()
{
m_exportEventHandler.reset();
Events::AssetImportRequestBus::Handler::BusDisconnect();
UnloadPython();
}
bool ScriptProcessorRuleBehavior::LoadPython(const AZ::SceneAPI::Containers::Scene& scene)
{
if (m_editorPythonEventsInterface && !m_scriptFilename.empty())
{
return true;
}
// get project folder
auto settingsRegistry = AZ::SettingsRegistry::Get();
AZ::IO::FixedMaxPath projectPath;
if (!settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
{
return false;
}
void ScriptProcessorRuleBehavior::Activate()
const AZ::SceneAPI::Containers::SceneManifest& manifest = scene.GetManifest();
auto view = Containers::MakeDerivedFilterView<DataTypes::IScriptProcessorRule>(manifest.GetValueStorage());
for (const auto& scriptItem : view)
{
AZ::IO::FixedMaxPath scriptFilename(scriptItem.GetScriptFilename());
if (scriptFilename.empty())
{
Events::AssetImportRequestBus::Handler::BusConnect();
AZ_Warning("scene", false, "Skipping an empty script filename in (%s)", scene.GetManifestFilename().c_str());
continue;
}
void ScriptProcessorRuleBehavior::Deactivate()
// check for file exist via absolute path
if (!IO::FileIOBase::GetInstance()->Exists(scriptFilename.c_str()))
{
Events::AssetImportRequestBus::Handler::BusDisconnect();
if (m_editorPythonEventsInterface)
// check for script in the project folder
AZ::IO::FixedMaxPath projectScriptPath = projectPath / scriptFilename;
if (!IO::FileIOBase::GetInstance()->Exists(projectScriptPath.c_str()))
{
const bool silenceWarnings = true;
m_editorPythonEventsInterface->StopPython(silenceWarnings);
m_editorPythonEventsInterface = nullptr;
AZ_Warning("scene", false, "Skipping a missing script (%s) in manifest file (%s)",
scriptFilename.c_str(),
scene.GetManifestFilename().c_str());
continue;
}
scriptFilename = AZStd::move(projectScriptPath);
}
void ScriptProcessorRuleBehavior::Reflect(ReflectContext* context)
// lazy load the Python interface
auto editorPythonEventsInterface = AZ::Interface<AzToolsFramework::EditorPythonEventsInterface>::Get();
if (editorPythonEventsInterface->IsPythonActive() == false)
{
ScriptBuildingNotificationBusHandler::Reflect(context);
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
const bool silenceWarnings = false;
if (editorPythonEventsInterface->StartPython(silenceWarnings) == false)
{
serializeContext->Class<ScriptProcessorRuleBehavior, BehaviorComponent>()->Version(1);
editorPythonEventsInterface = nullptr;
}
}
Events::ProcessingResult ScriptProcessorRuleBehavior::UpdateManifest(
Containers::Scene& scene,
Events::AssetImportRequest::ManifestAction action,
[[maybe_unused]] Events::AssetImportRequest::RequestingApplication requester)
// both Python and the script need to be ready
if (editorPythonEventsInterface == nullptr || scriptFilename.empty())
{
using namespace AzToolsFramework;
AZ_Warning("scene", false,"The scene manifest (%s) attempted to use script(%s) but Python is not enabled;"
"please add the EditorPythonBinding gem & PythonAssetBuilder gem to your project.",
scene.GetManifestFilename().c_str(), scriptFilename.c_str());
if (action != ManifestAction::Update)
{
return Events::ProcessingResult::Ignored;
}
return false;
}
// get project folder
auto settingsRegistry = AZ::SettingsRegistry::Get();
AZ::IO::FixedMaxPath projectPath;
if (!settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
{
return Events::ProcessingResult::Ignored;
}
m_editorPythonEventsInterface = editorPythonEventsInterface;
m_scriptFilename = scriptFilename.c_str();
return true;
}
return false;
}
void ScriptProcessorRuleBehavior::UnloadPython()
{
if (m_editorPythonEventsInterface)
{
const bool silenceWarnings = true;
m_editorPythonEventsInterface->StopPython(silenceWarnings);
m_editorPythonEventsInterface = nullptr;
}
}
bool ScriptProcessorRuleBehavior::DoPrepareForExport(Events::PreExportEventContext& context)
{
using namespace AzToolsFramework;
auto executeCallback = [this, &context]()
{
// set up script's hook callback
EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilename,
m_scriptFilename.c_str());
// call script's callback to allow extra products
ExportProductList extraProducts;
ScriptBuildingNotificationBus::BroadcastResult(extraProducts, &ScriptBuildingNotificationBus::Events::OnPrepareForExport,
context.GetScene(),
context.GetOutputDirectory(),
context.GetPlatformIdentifier(),
context.GetProductList()
);
auto& sceneManifest = scene.GetManifest();
auto view = Containers::MakeDerivedFilterView<DataTypes::IScriptProcessorRule>(sceneManifest.GetValueStorage());
for (const auto& scriptItem : view)
// add new products
for (const auto& product : extraProducts.GetProducts())
{
context.GetProductList().AddProduct(
product.m_filename,
product.m_id,
product.m_assetType,
product.m_lod,
product.m_subId,
product.m_dependencyFlags);
}
};
if (LoadPython(context.GetScene()))
{
EditorPythonConsoleNotificationHandler logger;
m_editorPythonEventsInterface->ExecuteWithLock(executeCallback);
}
return true;
}
void ScriptProcessorRuleBehavior::Reflect(ReflectContext* context)
{
ScriptBuildingNotificationBusHandler::Reflect(context);
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<ScriptProcessorRuleBehavior, BehaviorComponent>()->Version(1);
}
}
Events::ProcessingResult ScriptProcessorRuleBehavior::UpdateManifest(
Containers::Scene& scene,
Events::AssetImportRequest::ManifestAction action,
[[maybe_unused]] Events::AssetImportRequest::RequestingApplication requester)
{
using namespace AzToolsFramework;
if (action != ManifestAction::Update)
{
return Events::ProcessingResult::Ignored;
}
if (LoadPython(scene))
{
AZStd::string manifestUpdate;
auto executeCallback = [this, &scene, &manifestUpdate]()
{
EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilename,
m_scriptFilename.c_str());
ScriptBuildingNotificationBus::BroadcastResult(manifestUpdate, &ScriptBuildingNotificationBus::Events::OnUpdateManifest,
scene);
};
EditorPythonConsoleNotificationHandler logger;
m_editorPythonEventsInterface->ExecuteWithLock(executeCallback);
// attempt to load the manifest string back to a JSON-scene-manifest
auto sceneManifestLoader = AZStd::make_unique<AZ::SceneAPI::Containers::SceneManifest>();
auto loadOutcome = sceneManifestLoader->LoadFromString(manifestUpdate);
if (loadOutcome.IsSuccess())
{
scene.GetManifest().Clear();
for (size_t entryIndex = 0; entryIndex < sceneManifestLoader->GetEntryCount(); ++entryIndex)
{
AZ::IO::FixedMaxPath scriptFilename(scriptItem.GetScriptFilename());
if (scriptFilename.empty())
{
AZ_Warning("scene", false, "Skipping an empty script filename in (%s)", scene.GetManifestFilename().c_str());
continue;
}
// check for file exist via absolute path
if (!IO::FileIOBase::GetInstance()->Exists(scriptFilename.c_str()))
{
// check for script in the project folder
AZ::IO::FixedMaxPath projectScriptPath = projectPath / scriptFilename;
if (!IO::FileIOBase::GetInstance()->Exists(projectScriptPath.c_str()))
{
AZ_Warning("scene", false, "Skipping a missing script (%s) in manifest file (%s)",
scriptFilename.c_str(),
scene.GetManifestFilename().c_str());
continue;
}
scriptFilename = AZStd::move(projectScriptPath);
}
// lazy load the Python interface
if (!m_editorPythonEventsInterface)
{
m_editorPythonEventsInterface = AZ::Interface<AzToolsFramework::EditorPythonEventsInterface>::Get();
const bool silenceWarnings = true;
m_editorPythonEventsInterface->StartPython(silenceWarnings);
}
if (!m_editorPythonEventsInterface && !scriptFilename.empty())
{
AZ_Warning("scene", false,
"The scene manifest (%s) attempted to use script(%s) but Python is not enabled;"
"please add the EditorPythonBinding gem & PythonAssetBuilder gem to your project.",
scene.GetManifestFilename().c_str(), scriptFilename.c_str());
return Events::ProcessingResult::Ignored;
}
AZStd::string manifestUpdate;
auto executeCallback = [&scene, &scriptFilename, &manifestUpdate]()
{
EditorPythonRunnerRequestBus::Broadcast(
&EditorPythonRunnerRequestBus::Events::ExecuteByFilename,
scriptFilename.c_str());
ScriptBuildingNotificationBus::BroadcastResult(
manifestUpdate,
&ScriptBuildingNotificationBus::Events::OnUpdateManifest,
scene);
};
EditorPythonConsoleNotificationHandler logger;
m_editorPythonEventsInterface->ExecuteWithLock(executeCallback);
// attempt to load the manifest string back to a JSON-scene-manifest
auto sceneManifestLoader = AZStd::make_unique<AZ::SceneAPI::Containers::SceneManifest>();
auto loadOutcome = sceneManifestLoader->LoadFromString(manifestUpdate);
if (loadOutcome.IsSuccess())
{
sceneManifest.Clear();
for (size_t entryIndex = 0; entryIndex < sceneManifestLoader->GetEntryCount(); ++entryIndex)
{
sceneManifest.AddEntry(sceneManifestLoader->GetValue(entryIndex));
}
return Events::ProcessingResult::Success;
}
scene.GetManifest().AddEntry(sceneManifestLoader->GetValue(entryIndex));
}
return Events::ProcessingResult::Ignored;
return Events::ProcessingResult::Success;
}
}
return Events::ProcessingResult::Ignored;
}
} // namespace Behaviors
} // namespace SceneAPI
} // namespace AZ

@ -11,40 +11,56 @@
#include <AzCore/std/string/string.h>
#include <SceneAPI/SceneCore/Components/BehaviorComponent.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
namespace AzToolsFramework
{
class EditorPythonEventsInterface;
}
namespace AZ
namespace AZ::SceneAPI::Events
{
namespace SceneAPI
class PreExportEventContext;
}
namespace AZ::SceneAPI::Containers
{
class Scene;
}
namespace AZ::SceneAPI::Behaviors
{
class SCENE_DATA_CLASS ScriptProcessorRuleBehavior
: public SceneCore::BehaviorComponent
, public Events::AssetImportRequestBus::Handler
{
namespace Behaviors
{
class SCENE_DATA_CLASS ScriptProcessorRuleBehavior
: public SceneCore::BehaviorComponent
, public Events::AssetImportRequestBus::Handler
{
public:
AZ_COMPONENT(ScriptProcessorRuleBehavior, "{24054E73-1B92-43B0-AC13-174B2F0E3F66}", SceneCore::BehaviorComponent);
~ScriptProcessorRuleBehavior() override = default;
SCENE_DATA_API void Activate() override;
SCENE_DATA_API void Deactivate() override;
static void Reflect(ReflectContext* context);
// AssetImportRequestBus::Handler
SCENE_DATA_API Events::ProcessingResult UpdateManifest(
Containers::Scene& scene,
ManifestAction action,
RequestingApplication requester) override;
private:
AzToolsFramework::EditorPythonEventsInterface* m_editorPythonEventsInterface = nullptr;
};
} // namespace SceneData
} // namespace SceneAPI
} // namespace AZ
public:
AZ_COMPONENT(ScriptProcessorRuleBehavior, "{24054E73-1B92-43B0-AC13-174B2F0E3F66}", SceneCore::BehaviorComponent);
~ScriptProcessorRuleBehavior() override = default;
SCENE_DATA_API void Activate() override;
SCENE_DATA_API void Deactivate() override;
static void Reflect(ReflectContext* context);
// AssetImportRequestBus::Handler
SCENE_DATA_API Events::ProcessingResult UpdateManifest(
Containers::Scene& scene,
ManifestAction action,
RequestingApplication requester) override;
protected:
bool LoadPython(const AZ::SceneAPI::Containers::Scene& scene);
void UnloadPython();
bool DoPrepareForExport(Events::PreExportEventContext& context);
private:
AzToolsFramework::EditorPythonEventsInterface* m_editorPythonEventsInterface = nullptr;
AZStd::string m_scriptFilename;
struct ExportEventHandler;
AZStd::shared_ptr<ExportEventHandler> m_exportEventHandler;
};
} // namespace AZ::SceneAPI::Behaviors

@ -1,42 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
namespace AZ
{
class BehaviorContext;
class EditContext;
class SerializeContext;
} // namespace AZ
// this just provides a convenient template to avoid the necessary boilerplate when you derive from AWSScriptBehaviorBase
#define AWS_SCRIPT_BEHAVIOR_DEFINITION(className, guidString) \
AZ_TYPE_INFO(className, guidString) \
AZ_CLASS_ALLOCATOR(className, AZ::SystemAllocator, 0) \
void ReflectSerialization(AZ::SerializeContext* serializeContext) override; \
void ReflectBehaviors(AZ::BehaviorContext* behaviorContext) override; \
void ReflectEditParameters(AZ::EditContext* editContext) override; \
className(); \
namespace AWSCore
{
//! An interface for AWS ScriptCanvas Behaviors to inherit from
class AWSScriptBehaviorBase
{
public:
virtual ~AWSScriptBehaviorBase() = default;
virtual void ReflectSerialization(AZ::SerializeContext* reflectContext) = 0;
virtual void ReflectBehaviors(AZ::BehaviorContext* behaviorContext) = 0;
virtual void ReflectEditParameters(AZ::EditContext* editContext) = 0;
virtual void Init() {}
virtual void Activate() {}
virtual void Deactivate() {}
};
} // namespace AWSCore

@ -10,8 +10,6 @@
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
#include <ScriptCanvas/AWSScriptBehaviorBase.h>
namespace AWSCore
{
using DynamoDBAttributeValueMap = AZStd::unordered_map<AZStd::string, AZStd::string>;
@ -55,10 +53,14 @@ namespace AWSCore
};
class AWSScriptBehaviorDynamoDB
: public AWSScriptBehaviorBase
{
public:
AWS_SCRIPT_BEHAVIOR_DEFINITION(AWSScriptBehaviorDynamoDB, "{569E74F6-1268-4199-9653-A3B603FC9F4F}");
AZ_RTTI(AWSScriptBehaviorDynamoDB, "{569E74F6-1268-4199-9653-A3B603FC9F4F}");
AWSScriptBehaviorDynamoDB() = default;
virtual ~AWSScriptBehaviorDynamoDB() = default;
static void Reflect(AZ::ReflectContext* context);
static void GetItem(const AZStd::string& tableResourceKey, const DynamoDBAttributeValueMap& keyMap);
static void GetItemRaw(const AZStd::string& table, const DynamoDBAttributeValueMap& keyMap, const AZStd::string& region);

@ -10,8 +10,6 @@
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
#include <ScriptCanvas/AWSScriptBehaviorBase.h>
namespace AWSCore
{
//! AWS Script Behavior notifications for ScriptCanvas behaviors that interact with AWS Lambda
@ -53,10 +51,14 @@ namespace AWSCore
};
class AWSScriptBehaviorLambda
: public AWSScriptBehaviorBase
{
public:
AWS_SCRIPT_BEHAVIOR_DEFINITION(AWSScriptBehaviorLambda, "{9E71534D-34B3-4723-B180-2552513DDA3D}");
AZ_RTTI(AWSScriptBehaviorLambda, "{9E71534D-34B3-4723-B180-2552513DDA3D}");
AWSScriptBehaviorLambda() = default;
virtual ~AWSScriptBehaviorLambda() = default;
static void Reflect(AZ::ReflectContext* context);
static void Invoke(const AZStd::string& functionResourceKey, const AZStd::string& payload);
static void InvokeRaw(const AZStd::string& functionName, const AZStd::string& payload, const AZStd::string& region);

@ -10,8 +10,6 @@
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
#include <ScriptCanvas/AWSScriptBehaviorBase.h>
namespace AWSCore
{
//! AWS Script Behavior notifications for ScriptCanvas behaviors that interact with AWS S3
@ -68,7 +66,6 @@ namespace AWSCore
};
class AWSScriptBehaviorS3
: public AWSScriptBehaviorBase
{
static constexpr const char AWSScriptBehaviorS3Name[] = "AWSScriptBehaviorS3";
static constexpr const char OutputFileIsEmptyErrorMessage[] = "Request validation failed, output file is empty.";
@ -81,7 +78,12 @@ namespace AWSCore
static constexpr const char RegionNameIsEmptyErrorMessage[] = "Request validation failed, region name is empty.";
public:
AWS_SCRIPT_BEHAVIOR_DEFINITION(AWSScriptBehaviorS3, "{7F4E956C-7463-4236-B320-C992D36A9C6E}");
AZ_RTTI(AWSScriptBehaviorS3, "{7F4E956C-7463-4236-B320-C992D36A9C6E}");
AWSScriptBehaviorS3() = default;
virtual ~AWSScriptBehaviorS3() = default;
static void Reflect(AZ::ReflectContext* context);
static void GetObject(const AZStd::string& bucketResourceKey, const AZStd::string& objectKey, const AZStd::string& outFile);
static void GetObjectRaw(const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region, const AZStd::string& outFile);

@ -10,7 +10,6 @@
#include <AzCore/Component/Component.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/std/containers/vector.h>
namespace AWSCore
{
@ -30,23 +29,8 @@ namespace AWSCore
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
static bool AddedBehaviours()
{
return m_alreadyAddedBehaviors;
}
protected:
////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Init() override;
void Activate() override;
void Deactivate() override;
////////////////////////////////////////////////////////////////////////
static void AddBehaviors(); // Add any behaviors you derived from AWSScriptBehaviorBase to the implementation of this function
static AZStd::vector<AZStd::unique_ptr<AWSScriptBehaviorBase>> m_behaviors;
static bool m_alreadyAddedBehaviors;
};
} // namespace AWSCore

@ -20,39 +20,30 @@
namespace AWSCore
{
AWSScriptBehaviorDynamoDB::AWSScriptBehaviorDynamoDB()
void AWSScriptBehaviorDynamoDB::Reflect(AZ::ReflectContext* context)
{
}
void AWSScriptBehaviorDynamoDB::ReflectSerialization(AZ::SerializeContext* serializeContext)
{
if (serializeContext)
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AWSScriptBehaviorDynamoDB>()
->Version(0);
}
}
void AWSScriptBehaviorDynamoDB::ReflectBehaviors(AZ::BehaviorContext* behaviorContext)
{
behaviorContext->Class<AWSScriptBehaviorDynamoDB>("AWSScriptBehaviorDynamoDB")
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Method("GetItem", &AWSScriptBehaviorDynamoDB::GetItem,
{{{"Table Resource KeyName", "The name of the table containing the requested item."},
{"Key Map", "A map of attribute names to AttributeValue objects, representing the primary key of the item to retrieve."}}})
->Method("GetItemRaw", &AWSScriptBehaviorDynamoDB::GetItemRaw,
{{{"Table Name", "The name of the table containing the requested item."},
{"Key Map", "A map of attribute names to AttributeValue objects, representing the primary key of the item to retrieve."},
{"Region Name", "The region of the table located in."}}});
behaviorContext->EBus<AWSScriptBehaviorDynamoDBNotificationBus>("AWSDynamoDBBehaviorNotificationBus")
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Handler<AWSScriptBehaviorDynamoDBNotificationBusHandler>();
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<AWSScriptBehaviorDynamoDB>("AWSScriptBehaviorDynamoDB")
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Method("GetItem", &AWSScriptBehaviorDynamoDB::GetItem,
{{{"Table Resource KeyName", "The name of the table containing the requested item."},
{"Key Map", "A map of attribute names to AttributeValue objects, representing the primary key of the item to retrieve."}}})
->Method("GetItemRaw", &AWSScriptBehaviorDynamoDB::GetItemRaw,
{{{"Table Name", "The name of the table containing the requested item."},
{"Key Map", "A map of attribute names to AttributeValue objects, representing the primary key of the item to retrieve."},
{"Region Name", "The region of the table located in."}}});
void AWSScriptBehaviorDynamoDB::ReflectEditParameters(AZ::EditContext* editContext)
{
AZ_UNUSED(editContext);
behaviorContext->EBus<AWSScriptBehaviorDynamoDBNotificationBus>("AWSDynamoDBBehaviorNotificationBus")
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Handler<AWSScriptBehaviorDynamoDBNotificationBusHandler>();
}
}
void AWSScriptBehaviorDynamoDB::GetItem(const AZStd::string& tableResourceKey, const DynamoDBAttributeValueMap& keyMap)

@ -21,39 +21,30 @@
namespace AWSCore
{
AWSScriptBehaviorLambda::AWSScriptBehaviorLambda()
void AWSScriptBehaviorLambda::Reflect(AZ::ReflectContext* context)
{
}
void AWSScriptBehaviorLambda::ReflectSerialization(AZ::SerializeContext* serializeContext)
{
if (serializeContext)
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AWSScriptBehaviorLambda>()
->Version(0);
}
}
void AWSScriptBehaviorLambda::ReflectBehaviors(AZ::BehaviorContext* behaviorContext)
{
behaviorContext->Class<AWSScriptBehaviorLambda>("AWSScriptBehaviorLambda")
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Method("Invoke", &AWSScriptBehaviorLambda::Invoke,
{{{"Function Resource KeyName", "The resource key name of the lambda function in resource mapping config file."},
{"Payload", "The JSON that you want to provide to your Lambda function as input."}}})
->Method("InvokeRaw", &AWSScriptBehaviorLambda::InvokeRaw,
{{{"Function Name", "The name of the Lambda function, version, or alias."},
{"Payload", "The JSON that you want to provide to your Lambda function as input."},
{"Region Name", "The region of the lambda function located in."}}});
behaviorContext->EBus<AWSScriptBehaviorLambdaNotificationBus>("AWSLambdaBehaviorNotificationBus")
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Handler<AWSScriptBehaviorLambdaNotificationBusHandler>();
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<AWSScriptBehaviorLambda>("AWSScriptBehaviorLambda")
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Method("Invoke", &AWSScriptBehaviorLambda::Invoke,
{{{"Function Resource KeyName", "The resource key name of the lambda function in resource mapping config file."},
{"Payload", "The JSON that you want to provide to your Lambda function as input."}}})
->Method("InvokeRaw", &AWSScriptBehaviorLambda::InvokeRaw,
{{{"Function Name", "The name of the Lambda function, version, or alias."},
{"Payload", "The JSON that you want to provide to your Lambda function as input."},
{"Region Name", "The region of the lambda function located in."}}});
void AWSScriptBehaviorLambda::ReflectEditParameters(AZ::EditContext* editContext)
{
AZ_UNUSED(editContext);
behaviorContext->EBus<AWSScriptBehaviorLambdaNotificationBus>("AWSLambdaBehaviorNotificationBus")
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Handler<AWSScriptBehaviorLambdaNotificationBusHandler>();
}
}
void AWSScriptBehaviorLambda::Invoke(const AZStd::string& functionResourceKey, const AZStd::string& payload)

@ -24,49 +24,39 @@
namespace AWSCore
{
AWSScriptBehaviorS3::AWSScriptBehaviorS3()
void AWSScriptBehaviorS3::Reflect(AZ::ReflectContext* context)
{
}
void AWSScriptBehaviorS3::ReflectSerialization(AZ::SerializeContext* serializeContext)
{
if (serializeContext)
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AWSScriptBehaviorS3>()
->Version(0);
}
}
void AWSScriptBehaviorS3::ReflectBehaviors(AZ::BehaviorContext* behaviorContext)
{
behaviorContext->Class<AWSScriptBehaviorS3>(AWSScriptBehaviorS3Name)
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Method("GetObject", &AWSScriptBehaviorS3::GetObject,
{{{"Bucket Resource KeyName", "The resource key name of the bucket in resource mapping config file."},
{"Object KeyName", "The object key."},
{"Outfile Name", "Filename where the content will be saved."}}})
->Method("GetObjectRaw", &AWSScriptBehaviorS3::GetObjectRaw,
{{{"Bucket Name", "The name of the bucket containing the object."},
{"Object KeyName", "The object key."},
{"Region Name", "The region of the bucket located in."},
{"Outfile Name", "Filename where the content will be saved."}}})
->Method("HeadObject", &AWSScriptBehaviorS3::HeadObject,
{{{"Bucket Resource KeyName", "The resource key name of the bucket in resource mapping config file."},
{"Object KeyName", "The object key."}}})
->Method("HeadObjectRaw", &AWSScriptBehaviorS3::HeadObjectRaw,
{{{"Bucket Name", "The name of the bucket containing the object."},
{"Object KeyName", "The object key."},
{"Region Name", "The region of the bucket located in."}}})
;
behaviorContext->EBus<AWSScriptBehaviorS3NotificationBus>("AWSS3BehaviorNotificationBus")
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Handler<AWSScriptBehaviorS3NotificationBusHandler>();
}
void AWSScriptBehaviorS3::ReflectEditParameters(AZ::EditContext* editContext)
{
AZ_UNUSED(editContext);
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<AWSScriptBehaviorS3>(AWSScriptBehaviorS3Name)
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Method("GetObject", &AWSScriptBehaviorS3::GetObject,
{{{"Bucket Resource KeyName", "The resource key name of the bucket in resource mapping config file."},
{"Object KeyName", "The object key."},
{"Outfile Name", "Filename where the content will be saved."}}})
->Method("GetObjectRaw", &AWSScriptBehaviorS3::GetObjectRaw,
{{{"Bucket Name", "The name of the bucket containing the object."},
{"Object KeyName", "The object key."},
{"Region Name", "The region of the bucket located in."},
{"Outfile Name", "Filename where the content will be saved."}}})
->Method("HeadObject", &AWSScriptBehaviorS3::HeadObject,
{{{"Bucket Resource KeyName", "The resource key name of the bucket in resource mapping config file."},
{"Object KeyName", "The object key."}}})
->Method("HeadObjectRaw", &AWSScriptBehaviorS3::HeadObjectRaw,
{{{"Bucket Name", "The name of the bucket containing the object."},
{"Object KeyName", "The object key."},
{"Region Name", "The region of the bucket located in."}}});
behaviorContext->EBus<AWSScriptBehaviorS3NotificationBus>("AWSS3BehaviorNotificationBus")
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Handler<AWSScriptBehaviorS3NotificationBusHandler>();
}
}
void AWSScriptBehaviorS3::GetObject(

@ -12,35 +12,17 @@
namespace AWSCore
{
AZStd::vector<AZStd::unique_ptr<AWSScriptBehaviorBase>> AWSScriptBehaviorsComponent::m_behaviors;
bool AWSScriptBehaviorsComponent::m_alreadyAddedBehaviors = false;
void AWSScriptBehaviorsComponent::AddBehaviors()
{
if (!m_alreadyAddedBehaviors)
{
// Add new script behaviors here
m_behaviors.push_back(AZStd::make_unique<AWSScriptBehaviorDynamoDB>());
m_behaviors.push_back(AZStd::make_unique<AWSScriptBehaviorLambda>());
m_behaviors.push_back(AZStd::make_unique<AWSScriptBehaviorS3>());
m_alreadyAddedBehaviors = true;
}
}
void AWSScriptBehaviorsComponent::Reflect(AZ::ReflectContext* context)
{
AddBehaviors();
AWSScriptBehaviorDynamoDB::Reflect(context);
AWSScriptBehaviorLambda::Reflect(context);
AWSScriptBehaviorS3::Reflect(context);
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<AWSScriptBehaviorsComponent, AZ::Component>()
->Version(0);
for (auto&& behavior : m_behaviors)
{
behavior->ReflectSerialization(serialize);
}
if (AZ::EditContext* editContext = serialize->GetEditContext())
{
editContext->Class<AWSScriptBehaviorsComponent>("AWSScriptBehaviors", "Provides ScriptCanvas functions for calling AWS")
@ -49,19 +31,6 @@ namespace AWSCore
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("AWS"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
for (auto&& behavior : m_behaviors)
{
behavior->ReflectEditParameters(editContext);
}
}
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
for (auto&& behavior : m_behaviors)
{
behavior->ReflectBehaviors(behaviorContext);
}
}
}
@ -86,31 +55,12 @@ namespace AWSCore
AZ_UNUSED(dependent);
}
void AWSScriptBehaviorsComponent::Init()
{
for (auto&& behavior : m_behaviors)
{
behavior->Init();
}
}
void AWSScriptBehaviorsComponent::Activate()
{
for (auto&& behavior : m_behaviors)
{
behavior->Activate();
}
}
void AWSScriptBehaviorsComponent::Deactivate()
{
for (auto&& behavior : m_behaviors)
{
behavior->Deactivate();
}
// this forces the vector to release its capacity, clear/shrink_to_fit is not
m_behaviors.swap(AZStd::vector<AZStd::unique_ptr<AWSScriptBehaviorBase>>());
}
}

@ -15,18 +15,6 @@
using namespace AWSCore;
class AWSScriptBehaviorsComponentMock
: public AWSScriptBehaviorsComponent
{
public:
AZ_COMPONENT(AWSScriptBehaviorsComponentMock, "{78579706-E1B2-4788-A34D-A58D3F273FF9}");
int GetBehaviorsNum()
{
return m_behaviors.size();
}
};
class AWSScriptBehaviorsComponentTest
: public UnitTest::ScopedAllocatorSetupFixture
{
@ -38,7 +26,7 @@ public:
m_behaviorContext = AZStd::make_unique<AZ::BehaviorContext>();
m_entity = AZStd::make_unique<AZ::Entity>();
m_scriptBehaviorsComponent.reset(m_entity->CreateComponent<AWSScriptBehaviorsComponentMock>());
m_scriptBehaviorsComponent.reset(m_entity->CreateComponent<AWSScriptBehaviorsComponent>());
}
void TearDown() override
@ -56,20 +44,16 @@ protected:
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
AZStd::unique_ptr<AZ::BehaviorContext> m_behaviorContext;
AZStd::unique_ptr<AZ::ComponentDescriptor> m_componentDescriptor;
AZStd::unique_ptr<AWSScriptBehaviorsComponentMock> m_scriptBehaviorsComponent;
AZStd::unique_ptr<AWSScriptBehaviorsComponent> m_scriptBehaviorsComponent;
AZStd::unique_ptr<AZ::Entity> m_entity;
};
TEST_F(AWSScriptBehaviorsComponentTest, InitActivateDeactivate_Call_GetExpectedNumOfAddedBehaviors)
TEST_F(AWSScriptBehaviorsComponentTest, Reflect)
{
m_componentDescriptor.reset(AWSScriptBehaviorsComponentMock::CreateDescriptor());
int oldEBusNum = m_behaviorContext->m_ebuses.size();
m_componentDescriptor.reset(AWSScriptBehaviorsComponent::CreateDescriptor());
m_componentDescriptor->Reflect(m_serializeContext.get());
m_componentDescriptor->Reflect(m_behaviorContext.get());
EXPECT_TRUE(AWSScriptBehaviorsComponentMock::AddedBehaviours());
EXPECT_TRUE(m_scriptBehaviorsComponent->GetBehaviorsNum() == 3);
m_entity->Init();
m_entity->Activate();
m_entity->Deactivate();
EXPECT_TRUE(m_scriptBehaviorsComponent->GetBehaviorsNum() == 0);
EXPECT_TRUE(m_behaviorContext->m_ebuses.size() - oldEBusNum == 3);
}

@ -32,7 +32,6 @@ set(FILES
Include/Public/Framework/ServiceRequestJobConfig.h
Include/Public/Framework/Util.h
Include/Public/ResourceMapping/AWSResourceMappingBus.h
Include/Public/ScriptCanvas/AWSScriptBehaviorBase.h
Include/Public/ScriptCanvas/AWSScriptBehaviorDynamoDB.h
Include/Public/ScriptCanvas/AWSScriptBehaviorLambda.h
Include/Public/ScriptCanvas/AWSScriptBehaviorS3.h

@ -95,7 +95,7 @@ namespace AZ
shaderVariantAssetBuilderDescriptor.m_name = "Shader Variant Asset Builder";
// Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update
// ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder".
shaderVariantAssetBuilderDescriptor.m_version = 23; // ATOM-15472
shaderVariantAssetBuilderDescriptor.m_version = 24; // ATOM-15978
shaderVariantAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
shaderVariantAssetBuilderDescriptor.m_busId = azrtti_typeid<ShaderVariantAssetBuilder>();
shaderVariantAssetBuilderDescriptor.m_createJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::CreateJobs, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);

@ -48,6 +48,7 @@
#include "ShaderAssetBuilder.h"
#include "ShaderBuilderUtility.h"
#include "SrgLayoutUtility.h"
#include "AzslData.h"
#include "AzslCompiler.h"
#include <CommonFiles/Preprocessor.h>
@ -520,6 +521,96 @@ namespace AZ
return;
}
}
static bool LoadSrgLayoutListFromShaderAssetBuilder(
const RHI::ShaderPlatformInterface* shaderPlatformInterface,
const AssetBuilderSDK::PlatformInfo& platformInfo,
const AzslCompiler& azslCompiler, const AZStd::string& shaderSourceFileFullPath,
const RPI::SupervariantIndex supervariantIndex,
const bool platformUsesRegisterSpaces,
RPI::ShaderResourceGroupLayoutList& srgLayoutList,
RootConstantData& rootConstantData)
{
auto srgJsonPathOutcome = ShaderBuilderUtility::ObtainBuildArtifactPathFromShaderAssetBuilder(
shaderPlatformInterface->GetAPIUniqueIndex(), platformInfo.m_identifier, shaderSourceFileFullPath, supervariantIndex.GetIndex(), AZ::RPI::ShaderAssetSubId::SrgJson);
if (!srgJsonPathOutcome.IsSuccess())
{
AZ_Error(ShaderVariantAssetBuilderName, false, "%s", srgJsonPathOutcome.GetError().c_str());
return false;
}
auto srgJsonPath = srgJsonPathOutcome.TakeValue();
auto jsonOutcome = JsonSerializationUtils::ReadJsonFile(srgJsonPath);
if (!jsonOutcome.IsSuccess())
{
AZ_Error(ShaderVariantAssetBuilderName, false, "%s", jsonOutcome.GetError().c_str());
return false;
}
SrgDataContainer srgData;
if (!azslCompiler.ParseSrgPopulateSrgData(jsonOutcome.GetValue(), srgData))
{
AZ_Error(ShaderVariantAssetBuilderName, false, "Failed to parse srg data");
return false;
}
// Add all Shader Resource Group Assets that were defined in the shader code to the shader asset
if (!SrgLayoutUtility::LoadShaderResourceGroupLayouts(ShaderVariantAssetBuilderName, srgData, platformUsesRegisterSpaces, srgLayoutList))
{
AZ_Error(ShaderVariantAssetBuilderName, false, "Failed to load ShaderResourceGroupLayouts");
return false;
}
for (auto srgLayout : srgLayoutList)
{
if (!srgLayout->Finalize())
{
AZ_Error(ShaderVariantAssetBuilderName, false,
"Failed to finalize SrgLayout %s", srgLayout->GetName().GetCStr());
return false;
}
}
// Access the root constants reflection
if (!azslCompiler.ParseSrgPopulateRootConstantData(
jsonOutcome.GetValue(),
rootConstantData)) // consuming data from --srg ("InlineConstantBuffer" subjson section)
{
AZ_Error(ShaderVariantAssetBuilderName, false, "Failed to obtain root constant data reflection");
return false;
}
return true;
}
static bool LoadBindingDependenciesFromShaderAssetBuilder(
const RHI::ShaderPlatformInterface* shaderPlatformInterface,
const AssetBuilderSDK::PlatformInfo& platformInfo,
const AzslCompiler& azslCompiler, const AZStd::string& shaderSourceFileFullPath,
const RPI::SupervariantIndex supervariantIndex,
BindingDependencies& bindingDependencies)
{
auto bindingsJsonPathOutcome = ShaderBuilderUtility::ObtainBuildArtifactPathFromShaderAssetBuilder(
shaderPlatformInterface->GetAPIUniqueIndex(), platformInfo.m_identifier, shaderSourceFileFullPath, supervariantIndex.GetIndex(), AZ::RPI::ShaderAssetSubId::BindingdepJson);
if (!bindingsJsonPathOutcome.IsSuccess())
{
AZ_Error(ShaderVariantAssetBuilderName, false, "%s", bindingsJsonPathOutcome.GetError().c_str());
return false;
}
auto bindingsJsonPath = bindingsJsonPathOutcome.TakeValue();
auto jsonOutcome = JsonSerializationUtils::ReadJsonFile(bindingsJsonPath);
if (!jsonOutcome.IsSuccess())
{
AZ_Error(ShaderVariantAssetBuilderName, false, "%s", jsonOutcome.GetError().c_str());
return false;
}
if (!azslCompiler.ParseBindingdepPopulateBindingDependencies(jsonOutcome.GetValue(), bindingDependencies))
{
AZ_Error(ShaderVariantAssetBuilderName, false, "Failed to parse binding dependencies data");
return false;
}
return true;
}
// Returns the content of the hlsl file for the given supervariant as produced by ShaderAsssetBuilder.
@ -773,6 +864,50 @@ namespace AZ
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return;
}
//! It is important to keep this refcounted pointer outside of the if block to prevent it from being destroyed.
RHI::Ptr<RHI::PipelineLayoutDescriptor> pipelineLayoutDescriptor;
if (shaderPlatformInterface->VariantCompilationRequiresSrgLayoutData())
{
AZStd::string azslcCompilerParameters =
shaderPlatformInterface->GetAzslCompilerParameters(buildOptions.m_compilerArguments);
const bool platformUsesRegisterSpaces =
(AzFramework::StringFunc::Find(azslcCompilerParameters, "--use-spaces") != AZStd::string::npos);
RPI::ShaderResourceGroupLayoutList srgLayoutList;
RootConstantData rootConstantData;
if (!LoadSrgLayoutListFromShaderAssetBuilder(
shaderPlatformInterface, request.m_platformInfo, azslc, shaderSourceFileFullPath, supervariantIndex,
platformUsesRegisterSpaces,
srgLayoutList,
rootConstantData))
{
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return;
}
BindingDependencies bindingDependencies;
if (!LoadBindingDependenciesFromShaderAssetBuilder(
shaderPlatformInterface, request.m_platformInfo, azslc, shaderSourceFileFullPath, supervariantIndex,
bindingDependencies))
{
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return;
}
pipelineLayoutDescriptor =
ShaderBuilderUtility::BuildPipelineLayoutDescriptorForApi(
ShaderVariantAssetBuilderName, srgLayoutList, shaderEntryPoints, buildOptions.m_compilerArguments, rootConstantData,
shaderPlatformInterface, bindingDependencies);
if (!pipelineLayoutDescriptor)
{
AZ_Error(
ShaderVariantAssetBuilderName, false, "Failed to build pipeline layout descriptor for api=[%s]",
shaderPlatformInterface->GetAPIName().GetCStr());
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return;
}
}
// Setup the shader variant creation context:
ShaderVariantCreationContext shaderVariantCreationContext =

@ -144,6 +144,12 @@ namespace AZ
const ShaderResourceGroupInfoList& srgInfoList,
const RootConstantsInfo& rootConstantsInfo,
const ShaderCompilerArguments& shaderCompilerArguments) = 0;
//! In general, shader compilation doesn't require SRG Layout data, but RHIs like
//! Metal don't do well if unused resources (descriptors) are not bound. If this function returns TRUE
//! the ShaderVariantAssetBuilder will invoke BuildPipelineLayoutDescriptor() so the RHI gets the chance to
//! build SRG Layout data which will be useful when compiling MetalISL to Metal byte code.
virtual bool VariantCompilationRequiresSrgLayoutData() const { return false; }
//! See AZ::RHI::Factory::GetAPIUniqueIndex() for details.
//! See AZ::RHI::Limits::APIType::PerPlatformApiUniqueIndexMax.

@ -159,7 +159,13 @@ namespace AZ
arguments += " -Zi"; // Generate debug information
arguments += " -Zss"; // Compute Shader Hash considering source information
}
arguments += " " + m_dxcAdditionalFreeArguments;
// strip spaces at both sides
AZStd::string dxcAdditionalFreeArguments = m_dxcAdditionalFreeArguments;
AzFramework::StringFunc::TrimWhiteSpace(dxcAdditionalFreeArguments, true, true);
if (!dxcAdditionalFreeArguments.empty())
{
arguments += " " + dxcAdditionalFreeArguments;
}
return arguments;
}
}

@ -40,6 +40,8 @@ namespace AZ
const ShaderResourceGroupInfoList& srgInfoList,
const RootConstantsInfo& rootConstantsInfo,
const RHI::ShaderCompilerArguments& shaderCompilerArguments) override;
bool VariantCompilationRequiresSrgLayoutData() const override { return true; }
bool CompilePlatformInternal(
const AssetBuilderSDK::PlatformInfo& platform,

@ -30,6 +30,7 @@ namespace AZ
{
AZ_UNUSED(device);
m_hardwareQueueClass = hardwareQueueClass;
m_supportsInterDrawTimestamps = AZ::RHI::QueryTypeFlags::Timestamp == (device->GetFeatures().m_queryTypesMask[static_cast<uint32_t>(hardwareQueueClass)] & AZ::RHI::QueryTypeFlags::Timestamp);
}
void CommandListBase::Reset()
@ -64,7 +65,10 @@ namespace AZ
[m_encoder endEncoding];
m_encoder = nil;
#if AZ_TRAIT_ATOM_METAL_COUNTER_SAMPLING
m_timeStampQueue.clear();
if (m_supportsInterDrawTimestamps)
{
m_timeStampQueue.clear();
}
#endif
}
}
@ -144,9 +148,12 @@ namespace AZ
m_isEncoded = true;
#if AZ_TRAIT_ATOM_METAL_COUNTER_SAMPLING
for(auto& timeStamp: m_timeStampQueue)
if (m_supportsInterDrawTimestamps)
{
SampleCounters(timeStamp.m_counterSampleBuffer, timeStamp.m_timeStampIndex);
for(auto& timeStamp: m_timeStampQueue)
{
SampleCounters(timeStamp.m_counterSampleBuffer, timeStamp.m_timeStampIndex);
}
}
#endif
}
@ -195,6 +202,11 @@ namespace AZ
#if AZ_TRAIT_ATOM_METAL_COUNTER_SAMPLING
void CommandListBase::SampleCounters(id<MTLCounterSampleBuffer> counterSampleBuffer, uint32_t sampleIndex)
{
if (!m_supportsInterDrawTimestamps)
{
return;
}
AZ_Assert(sampleIndex >= 0, "Invalid sample index");
//useBarrier - Inserting a barrier ensures that encoded work is complete before the GPU samples the hardware counters.
//If it is true there is a performance penalty but you will get consistent results
@ -231,6 +243,11 @@ namespace AZ
void CommandListBase::SamplePassCounters(id<MTLCounterSampleBuffer> counterSampleBuffer, uint32_t sampleIndex)
{
if (!m_supportsInterDrawTimestamps)
{
return;
}
if(m_encoder == nil)
{
//Queue the query to be activated upon encoder creation. Applies to timestamp queries

@ -101,6 +101,8 @@ namespace AZ
const AZStd::set<id<MTLHeap>>* m_residentHeaps = nullptr;
bool m_supportsInterDrawTimestamps = AZ_TRAIT_ATOM_METAL_COUNTER_SAMPLING; // iOS/TVOS = false, MacOS = defaults to true
#if AZ_TRAIT_ATOM_METAL_COUNTER_SAMPLING
struct TimeStampData
{

@ -328,14 +328,28 @@ namespace AZ
m_features.m_indirectDrawSupport = false;
RHI::QueryTypeFlags counterSamplingFlags = RHI::QueryTypeFlags::None;
#if AZ_TRAIT_ATOM_METAL_COUNTER_SAMPLING
counterSamplingFlags |= (RHI::QueryTypeFlags::Timestamp | RHI::QueryTypeFlags::PipelineStatistics);
m_features.m_queryTypesMask[static_cast<uint32_t>(RHI::HardwareQueueClass::Copy)] = RHI::QueryTypeFlags::Timestamp;
bool supportsInterDrawTimestamps = true;
#if defined(__IPHONE_14_0) || defined(__MAC_11_0) || defined(__TVOS_14_0)
if (@available(macOS 11.0, iOS 14, tvOS 14, *))
{
supportsInterDrawTimestamps = [m_metalDevice supportsCounterSampling:MTLCounterSamplingPointAtDrawBoundary];
}
else
#endif
{
supportsInterDrawTimestamps = ![m_metalDevice.name containsString:@"Apple"]; // Apple GPU's don't support inter draw timestamps at the M1/A14 generation
}
if (supportsInterDrawTimestamps)
{
counterSamplingFlags |= (RHI::QueryTypeFlags::Timestamp | RHI::QueryTypeFlags::PipelineStatistics);
m_features.m_queryTypesMask[static_cast<uint32_t>(RHI::HardwareQueueClass::Copy)] = RHI::QueryTypeFlags::Timestamp;
}
m_features.m_queryTypesMask[static_cast<uint32_t>(RHI::HardwareQueueClass::Graphics)] = RHI::QueryTypeFlags::Occlusion | counterSamplingFlags;
//Compute queue can do gfx work
m_features.m_queryTypesMask[static_cast<uint32_t>(RHI::HardwareQueueClass::Compute)] = RHI::QueryTypeFlags::Occlusion |counterSamplingFlags;
m_features.m_queryTypesMask[static_cast<uint32_t>(RHI::HardwareQueueClass::Compute)] = RHI::QueryTypeFlags::Occlusion | counterSamplingFlags;
m_features.m_occlusionQueryPrecise = true;
//Values taken from https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf

@ -218,7 +218,8 @@ namespace AZ
createInfo.extent = extent;
createInfo.mipLevels = AZStd::min<uint32_t>(descriptor.m_mipLevels, formatProps.maxMipLevels);
createInfo.arrayLayers = AZStd::min<uint32_t>(descriptor.m_arraySize, formatProps.maxArrayLayers);
createInfo.samples = static_cast<VkSampleCountFlagBits>(RHI::FilterBits(static_cast<VkSampleCountFlags>(ConvertSampleCount(descriptor.m_multisampleState.m_samples)), formatProps.sampleCounts));
VkSampleCountFlagBits sampleCountFlagBits = static_cast<VkSampleCountFlagBits>(RHI::FilterBits(static_cast<VkSampleCountFlags>(ConvertSampleCount(descriptor.m_multisampleState.m_samples)), formatProps.sampleCounts));
createInfo.samples = (static_cast<uint32_t>(sampleCountFlagBits) > 0) ? sampleCountFlagBits : VK_SAMPLE_COUNT_1_BIT;
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
createInfo.usage = GetImageUsageFlags();
createInfo.sharingMode = exclusiveOwnership ? VK_SHARING_MODE_EXCLUSIVE : VK_SHARING_MODE_CONCURRENT;

@ -116,7 +116,7 @@ namespace AZ
{
AZ_Assert(IsQueryTypeValid(queryType), "Provided QueryType is invalid");
return static_cast<uint32_t>(m_queryTypeSupport) & static_cast<uint32_t>(queryType);
return static_cast<uint32_t>(m_queryTypeSupport) & AZ_BIT(static_cast<uint32_t>(queryType));
}
RPI::QueryPool* GpuQuerySystem::GetQueryPoolByType(RHI::QueryType queryType)

@ -237,8 +237,8 @@ namespace EditorPythonBindings
{
ec->Class<PythonSystemComponent>("PythonSystemComponent", "The Python interpreter")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
@ -284,10 +284,10 @@ namespace EditorPythonBindings
ReleaseFunction m_releaseFunction;
};
ReleaseInitalizeWaiterScope scope([this]()
{
m_initalizeWaiter.release(m_initalizeWaiterCount);
m_initalizeWaiterCount = 0;
});
{
m_initalizeWaiter.release(m_initalizeWaiterCount);
m_initalizeWaiterCount = 0;
});
if (Py_IsInitialized())
{
@ -327,6 +327,11 @@ namespace EditorPythonBindings
return result;
}
bool PythonSystemComponent::IsPythonActive()
{
return Py_IsInitialized() != 0;
}
void PythonSystemComponent::WaitForInitialization()
{
m_initalizeWaiterCount++;

@ -44,6 +44,7 @@ namespace EditorPythonBindings
// AzToolsFramework::EditorPythonEventsInterface
bool StartPython(bool silenceWarnings = false) override;
bool StopPython(bool silenceWarnings = false) override;
bool IsPythonActive() override;
void WaitForInitialization() override;
void ExecuteWithLock(AZStd::function<void()> executionCallback) override;
////////////////////////////////////////////////////////////////////////

@ -209,12 +209,17 @@ namespace Gestures
////////////////////////////////////////////////////////////////////////////////////////////////
inline uint32_t IRecognizer::GetGesturePointerIndex(const AzFramework::InputChannel& inputChannel)
{
const auto& mouseButtonIt = AZStd::find(AzFramework::InputDeviceMouse::Button::All.cbegin(),
AzFramework::InputDeviceMouse::Button::All.cend(),
inputChannel.GetInputChannelId());
if (mouseButtonIt != AzFramework::InputDeviceMouse::Button::All.cend())
// Only recognize gestures for the default mouse input device. The Editor may register synthetic
// mouse input devices with the same mouse input channels, which can confuse gesture recognition.
if (inputChannel.GetInputDevice().GetInputDeviceId() == AzFramework::InputDeviceMouse::Id)
{
return static_cast<uint32_t>(mouseButtonIt - AzFramework::InputDeviceMouse::Button::All.cbegin());
const auto& mouseButtonIt = AZStd::find(AzFramework::InputDeviceMouse::Button::All.cbegin(),
AzFramework::InputDeviceMouse::Button::All.cend(),
inputChannel.GetInputChannelId());
if (mouseButtonIt != AzFramework::InputDeviceMouse::Button::All.cend())
{
return static_cast<uint32_t>(mouseButtonIt - AzFramework::InputDeviceMouse::Button::All.cbegin());
}
}
const auto& touchIndexIt = AZStd::find(AzFramework::InputDeviceTouch::Touch::All.cbegin(),

@ -63,7 +63,6 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
AUTORCC
FILES_CMAKE
lyshine_uicanvaseditor_files.cmake
lyshine_editor_builder_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
@ -78,7 +77,6 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
3rdParty::Qt::Widgets
AZ::AzCore
AZ::AzToolsFramework
AZ::AssetBuilderSDK
Legacy::EditorCommon
Legacy::EditorCore
Gem::LyShine.Static
@ -98,7 +96,6 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME LyShine.Editor GEM_MODULE
NAMESPACE Gem
FILES_CMAKE
lyshine_common_module_files.cmake
@ -115,17 +112,72 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
BUILD_DEPENDENCIES
PRIVATE
Legacy::CryCommon
AZ::AssetBuilderSDK
AZ::AzToolsFramework
Gem::LyShine.Editor.Static
Gem::LmbrCentral.Editor
Gem::TextureAtlas.Editor
RUNTIME_DEPENDENCIES
Gem::LmbrCentral.Editor
Gem::TextureAtlas.Editor
)
)
# by naming this target LyShine.Builders it ensures that it is loaded
# in any pipeline tools (Like Asset Processor, AssetBuilder, etc)
ly_add_target(
NAME LyShine.Builders.Static STATIC
NAMESPACE Gem
FILES_CMAKE
lyshine_editor_builder_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
AZ::AzToolsFramework
AZ::AssetBuilderSDK
Gem::LyShine.Static
Legacy::CryCommon
Gem::LmbrCentral.Editor
Gem::TextureAtlas.Editor
Gem::AtomToolsFramework.Static
Gem::AtomToolsFramework.Editor
${additional_dependencies}
PUBLIC
Gem::Atom_RPI.Public
Gem::Atom_Utils.Static
Gem::Atom_Bootstrap.Headers
)
ly_add_target(
NAME LyShine.Builders GEM_MODULE
NAMESPACE Gem
OUTPUT_NAME Gem.LyShine.Builders
FILES_CMAKE
lyshine_common_module_files.cmake
COMPILE_DEFINITIONS
PRIVATE
LYSHINE_BUILDER
INCLUDE_DIRECTORIES
PRIVATE
.
Source
Editor
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
Legacy::CryCommon
AZ::AssetBuilderSDK
Gem::LyShine.Builders.Static
Gem::LmbrCentral.Editor
Gem::TextureAtlas.Editor
)
# by default, load the above "Gem::LyShine.Editor" module in dev tools:
ly_create_alias(NAME LyShine.Builders NAMESPACE Gem TARGETS Gem::LyShine.Editor)
ly_create_alias(NAME LyShine.Tools NAMESPACE Gem TARGETS Gem::LyShine.Editor)
endif()
@ -169,6 +221,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
COMPILE_DEFINITIONS
PRIVATE
LYSHINE_EDITOR
LYSHINE_BUILDER
INCLUDE_DIRECTORIES
PRIVATE
Tests
@ -182,6 +235,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
Legacy::CryCommon
AZ::AssetBuilderSDK
Gem::LyShine.Editor.Static
Gem::LyShine.Builders.Static
Gem::LmbrCentral.Editor
Gem::TextureAtlas
RUNTIME_DEPENDENCIES

@ -52,9 +52,9 @@
#include "World/UiCanvasProxyRefComponent.h"
#include "World/UiCanvasOnMeshComponent.h"
#if defined (LYSHINE_EDITOR)
# include "Pipeline/LyShineBuilder/LyShineBuilderComponent.h"
#endif // LYSHINE_EDITOR
#if defined(LYSHINE_BUILDER)
#include "Pipeline/LyShineBuilder/LyShineBuilderComponent.h"
#endif // LYSHINE_BUILDER
namespace LyShine
{
@ -64,7 +64,7 @@ namespace LyShine
// Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
m_descriptors.insert(m_descriptors.end(), {
LyShineSystemComponent::CreateDescriptor(),
#if defined (LYSHINE_EDITOR)
#if defined(LYSHINE_EDITOR)
LyShineEditor::LyShineEditorSystemComponent::CreateDescriptor(),
#endif
UiCanvasAssetRefComponent::CreateDescriptor(),
@ -103,7 +103,7 @@ namespace LyShine
UiRadioButtonComponent::CreateDescriptor(),
UiRadioButtonGroupComponent::CreateDescriptor(),
UiParticleEmitterComponent::CreateDescriptor(),
#if defined(LYSHINE_EDITOR)
#if defined(LYSHINE_BUILDER)
// Builder
LyShineBuilder::LyShineBuilderComponent::CreateDescriptor(),
#endif
@ -123,7 +123,7 @@ namespace LyShine
{
return AZ::ComponentTypeList{
azrtti_typeid<LyShineSystemComponent>(),
#if defined (LYSHINE_EDITOR)
#if defined(LYSHINE_EDITOR)
azrtti_typeid<LyShineEditor::LyShineEditorSystemComponent>(),
#endif
#if AZ_LOADSCREENCOMPONENT_ENABLED

@ -148,7 +148,6 @@ namespace LyShine
{
LyShineAllocatorScope::ActivateAllocators();
LyShineRequestBus::Handler::BusConnect();
UiSystemBus::Handler::BusConnect();
UiSystemToolsBus::Handler::BusConnect();
UiFrameworkBus::Handler::BusConnect();
@ -196,7 +195,6 @@ namespace LyShine
UiSystemBus::Handler::BusDisconnect();
UiSystemToolsBus::Handler::BusDisconnect();
UiFrameworkBus::Handler::BusDisconnect();
LyShineRequestBus::Handler::BusDisconnect();
CrySystemEventBus::Handler::BusDisconnect();
LyShineAllocatorScope::DeactivateAllocators();

@ -13,7 +13,6 @@
#include <LmbrCentral/Rendering/MaterialAsset.h>
#include <LyShine/LyShineBus.h>
#include <LyShine/Bus/UiSystemBus.h>
#include <LyShine/Bus/UiCanvasManagerBus.h>
#include <LyShine/Bus/Tools/UiSystemToolsBus.h>
@ -28,7 +27,6 @@ namespace LyShine
class LyShineSystemComponent
: public AZ::Component
, protected LyShineRequestBus::Handler
, protected UiSystemBus::Handler
, protected UiSystemToolsBus::Handler
, protected LyShineAllocatorScope

@ -8,4 +8,6 @@
set(FILES
Source/LyShineModule.cpp
Source/LyShineModule.h
Source/LyShineSystemComponent.cpp
Source/LyShineSystemComponent.h
)

@ -26,8 +26,6 @@ set(FILES
Source/EditorPropertyTypes.h
Source/LyShineLoadScreen.cpp
Source/LyShineLoadScreen.h
Source/LyShineSystemComponent.cpp
Source/LyShineSystemComponent.h
Source/RenderGraph.cpp
Source/RenderGraph.h
Source/TextMarkup.cpp

@ -61,6 +61,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
RUNTIME_DEPENDENCIES
AZ::SceneCore
AZ::SceneData
AZ::SceneUI
)
# the SceneProcessing.Editor module above is only used in Builders and Tools.
ly_create_alias(NAME SceneProcessing.Builders NAMESPACE Gem TARGETS Gem::SceneProcessing.Editor)

@ -52,18 +52,4 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT
AutomatedTesting.Assets
COMPONENT TestTools
)
# Example tests.
ly_add_pytest(
NAME LyTestTools_ExampleTests_periodic_no_gpu
PATH ${CMAKE_CURRENT_LIST_DIR}/example/test_system_example.py
TEST_SERIAL
TEST_SUITE periodic
RUNTIME_DEPENDENCIES
Legacy::Editor
AssetProcessor
AutomatedTesting.GameLauncher
AutomatedTesting.Assets
COMPONENT TestTools
)
endif()

@ -1,5 +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
"""

@ -1,129 +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
Example test using LyTestTools to test Lumberyard.
"""
# Python built-in dependencies.
import logging
# Third party dependencies.
import pytest
# ly_test_tools dependencies.
import ly_test_tools.log.log_monitor
import ly_remote_console.remote_console_commands as remote_console_commands
# Configuring the logging is done in ly_test_tools at the following location:
# ~/dev/Tools/LyTestTools/ly_test_tools/_internal/log/py_logging_util.py
# Use the following logging pattern to hook all test logging together:
logger = logging.getLogger(__name__)
@pytest.fixture
def remote_console(request):
"""
Creates a RemoteConsole() class instance to send console commands to the
Lumberyard client console.
:param request: _pytest.fixtures.SubRequest class that handles getting
a pytest fixture from a pytest function/fixture.
:return: ly_remote_console.remote_console_commands.RemoteConsole class instance
representing the Lumberyard remote console executable.
"""
# Initialize the RemoteConsole object to send commands to the Lumberyard client console.
console = remote_console_commands.RemoteConsole()
# Custom teardown method for this remote_console fixture.
def teardown():
console.stop()
# Utilize request.addfinalizer() to add custom teardown() methods.
request.addfinalizer(teardown) # This pattern must be used in pytest version
return console
# Shared parameters & fixtures for all test methods inside the TestSystemExample class.
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize('project', ['AutomatedTesting'])
class TestSystemExample(object):
"""
Example test case class to hold a set of test case methods.
The amount of tests run is based on the parametrization stacking made in each test method or class.
For this test, we placed unique test values in test methods and shared test values in the test class.
We also assume building has already been done, but the test should error if the build is mis-configured.
"""
# This test method needs specific parameters not shared by all other tests in the class.
# For targeting specific launchers, use the 'launcher_platform' pytest param like below:
# @pytest.mark.parametrize("launcher_platform", ['android'])
# If you want to target different AssetProcessor platforms, use asset_processor_platform:
# @pytest.mark.parametrize("asset_processor_platform", ['android'])
@pytest.mark.parametrize('level', ['simple_jacklocomotion'])
@pytest.mark.parametrize('load_wait', [120])
@pytest.mark.test_case_id('C16806863')
def test_SystemTestExample_AllSupportedPlatforms_LaunchAutomatedTesting(
# launcher_platform, asset_processor_platform, # Re-add these here if you plan to use them.
self, launcher, remote_console, level, load_wait):
"""
Tests launching the AutomatedTesting then launches the Lumberyard client &
loads the "simple_jacklocomotion" level using the remote console.
Assumes the user already setup & built their machine for the test.
"""
# Launch the Lumberyard client & remote console test case:
with launcher.start():
remote_console.start()
launcher_load = remote_console.expect_log_line(
match_string='Level system is loading "simple_jacklocomotion"',
timeout=load_wait)
# Assert loading was successful using remote console logs:
assert launcher_load, (
'Launcher failed to load Lumberyard client with the '
f'"{level}" level - waited "{load_wait}" seconds.')
# This test method only needs pytest.mark report values and shared test class parameters.
@pytest.mark.parametrize('processes_to_kill', ['Editor.exe'])
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.test_case_id('C16806864')
def test_SystemTestExample_AllSupportedPlatforms_LaunchEditor(self, editor, processes_to_kill, launcher_platform):
"""
Tests launching the Lumberyard Editor is successful with the current build.
"""
# Launch the Lumberyard editor & verify load is successful:
with editor.start():
assert editor.is_alive(), (
'Editor failed to launch for the current Lumberyard build.')
# Log monitoring example test.
@pytest.mark.parametrize('level', ['simple_jacklocomotion'])
@pytest.mark.parametrize('expected_lines', [['Log Monitoring test 1', 'Log Monitoring test 2']])
@pytest.mark.parametrize('unexpected_lines', [['Unexpected test 1', 'Unexpected test 2']])
@pytest.mark.test_case_id('C21202585')
def test_SystemTestExample_AllSupportedPlatforms_LogMonitoring(self, level, launcher, expected_lines,
unexpected_lines):
"""
Tests that the logging paths created by LyTestTools can be monitored for results using the log monitor.
"""
# Launch the Lumberyard client & initialize the log monitor.
file_to_monitor = launcher.workspace.info_log_path
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher,
log_file_path=file_to_monitor)
# Generate log lines to the info log using logger.
for expected_line in expected_lines:
logger.info(expected_line)
# Start the Lumberyard client & test that the lines we logged can be viewed by the log monitor.
with launcher.start():
log_test = log_monitor.monitor_log_for_lines(
expected_lines=expected_lines, # Defaults to None.
unexpected_lines=unexpected_lines, # Defaults to None.
halt_on_unexpected=True, # Defaults to False.
timeout=60) # Defaults to 30
# Assert the log monitor detected expected lines and did not detect any unexpected lines.
assert log_test, (
f'Log monitoring failed. Used expected_lines values: {expected_lines} & '
f'unexpected_lines values: {unexpected_lines}')

@ -6,49 +6,86 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
A sanity test for the built-in fixtures.
Launch the windows launcher attached to the currently installed instance.
"""
# Import any dependencies for the test.
import logging
import pytest
# Import any desired LTT modules from the package `ly_test_tools`. All LTT modules can be viewed at `Tools/LyTestTools/ly_test_tools`.
import ly_test_tools
# The `launchers.launcher_helper` module helps create Launcher objects which control the Open 3D Engine (O3DE) Editor and game clients.
import ly_test_tools.launchers.launcher_helper as launcher_helper
# The `builtin.helpers` module helps create the Workspace object, which controls the testing workspace in LTT.
import ly_test_tools.builtin.helpers as helpers
# The `environment` module contains tools that involve the system's environment such as processes or timed waiters.
import ly_test_tools.environment.process_utils as process_utils
import ly_test_tools.environment.waiter as waiter
pytestmark = pytest.mark.SUITE_smoke
# Initialize a logger instance to hook all test logs together. The sub-logger pattern below makes it easy to track which file creates a log line.
logger = logging.getLogger(__name__)
# Note: For device testing, device ids must exist in ~/ly_test_tools/devices.ini, see README.txt for more info.
# First define the class `TestAutomatedTestingProject` to group test functions together.
# The example test contains two test functions: `test_StartGameLauncher_Sanity` and `test_StartEditor_Sanity`.
@pytest.mark.parametrize("project", ["AutomatedTesting"])
# The example test utilizes Pytest parameterization. The following sets the `project` parameter to `AutomatedTesting`
# for both test functions. Notice that the Pytest mark is defined at the class level to affect both test functions.
class TestAutomatedTestingProject(object):
def test_StartGameLauncher_Sanity(self, project):
"""
The `test_StartGameLauncher_Sanity` test function verifies that the O3DE game client launches successfully.
Start the test by utilizing the `kill_processes_named` function to close any open O3DE processes that may
interfere with the test. The Workspace object emulates the O3DE package by locating the engine and project
directories. The Launcher object controls the O3DE game client and requires a Workspace object for
initialization. Add the `-rhi=Null` arg to the executable call to disable GPU rendering. This allows the
test to run on instances without a GPU. We launch the game client executable and wait for the process to exist.
A try/finally block ensures proper test cleanup if issues occur during the test.
"""
# Kill processes that may interfere with the test
process_utils.kill_processes_named(names=process_utils.LY_PROCESS_KILL_LIST, ignore_extensions=True)
try:
# Create the Workspace object
workspace = helpers.create_builtin_workspace(project=project)
# Create the Launcher object and add args
launcher = launcher_helper.create_launcher(workspace)
launcher.args.extend(['-NullRenderer', '-BatchMode'])
launcher.args.extend(['-rhi=Null'])
# Call the game client executable
with launcher.start():
# Wait for the process to exist
waiter.wait_for(lambda: process_utils.process_exists(f"{project}.GameLauncher.exe", ignore_extensions=True))
finally:
# Clean up processes after the test is finished
process_utils.kill_processes_named(names=process_utils.LY_PROCESS_KILL_LIST, ignore_extensions=True)
@pytest.mark.skipif(not ly_test_tools.WINDOWS, reason="Editor currently only functions on Windows")
def test_StartEditor_Sanity(self, project):
"""
The `test_StartEditor_Sanity` test function is similar to the previous example with minor adjustments. A
PyTest mark skips the test if the operating system is not Windows. We use the `create_editor` function instead
of `create_launcher` to create an Editor type launcher instead of a game client type launcher. The additional
`-autotest_mode` arg supresses modal dialogs from interfering with our test. We launch the Editor executable and
wait for the process to exist.
"""
# Kill processes that may interfere with the test
process_utils.kill_processes_named(names=process_utils.LY_PROCESS_KILL_LIST, ignore_extensions=True)
try:
# Create the Workspace object
workspace = helpers.create_builtin_workspace(project=project)
# Create the Launcher object and add args
editor = launcher_helper.create_editor(workspace)
editor.args.extend(['-NullRenderer', '-autotest_mode'])
editor.args.extend(['-rhi=Null', '-autotest_mode'])
# Call the Editor executable
with editor.start():
# Wait for the process to exist
waiter.wait_for(lambda: process_utils.process_exists("Editor", ignore_extensions=True))
finally:
# Clean up processes after the test is finished
process_utils.kill_processes_named(names=process_utils.LY_PROCESS_KILL_LIST, ignore_extensions=True)

@ -100,18 +100,29 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar
cmake_path(RELATIVE_PATH target_library_output_directory BASE_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} OUTPUT_VARIABLE target_library_output_subdirectory)
endif()
install(
TARGETS ${TARGET_NAME}
ARCHIVE
DESTINATION ${archive_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>
COMPONENT ${install_component}
LIBRARY
DESTINATION ${library_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/${target_library_output_subdirectory}
COMPONENT ${install_component}
RUNTIME
DESTINATION ${runtime_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/${target_runtime_output_subdirectory}
COMPONENT ${install_component}
)
if(COMMAND ly_install_target_override)
# Mac needs special handling because of a cmake issue
ly_install_target_override(TARGET ${TARGET_NAME}
ARCHIVE_DIR ${archive_output_directory}
LIBRARY_DIR ${library_output_directory}
RUNTIME_DIR ${runtime_output_directory}
LIBRARY_SUBDIR ${target_library_output_subdirectory}
RUNTIME_SUBDIR ${target_runtime_output_subdirectory}
)
else()
install(
TARGETS ${TARGET_NAME}
ARCHIVE
DESTINATION ${archive_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>
COMPONENT ${install_component}
LIBRARY
DESTINATION ${library_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/${target_library_output_subdirectory}
COMPONENT ${install_component}
RUNTIME
DESTINATION ${runtime_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>/${target_runtime_output_subdirectory}
COMPONENT ${install_component}
)
endif()
# CMakeLists.txt file
string(REGEX MATCH "(.*)::(.*)$" match ${ALIAS_TARGET_NAME})
@ -487,7 +498,7 @@ function(ly_setup_others)
# Scripts
file(GLOB o3de_scripts "${LY_ROOT_FOLDER}/scripts/o3de.*")
install(FILES
install(PROGRAMS
${o3de_scripts}
DESTINATION ./scripts
)
@ -505,6 +516,14 @@ function(ly_setup_others)
DESTINATION .
REGEX "downloaded_packages" EXCLUDE
REGEX "runtime" EXCLUDE
REGEX ".*$\.sh" EXCLUDE
)
# For Mac/Linux shell scripts need to be installed as PROGRAMS to have execute permission
file(GLOB python_scripts "${LY_ROOT_FOLDER}/python/*.sh")
install(PROGRAMS
${python_scripts}
DESTINATION ./python
)
# Registry

@ -28,7 +28,9 @@ else()
endif()
# Signing
ly_set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS --deep)
# The "-o linker-signed" flag is required as a work-around for the following CMake issue:
# https://gitlab.kitware.com/cmake/cmake/-/issues/21854
ly_set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--deep -o linker-signed")
# Generate scheme files for Xcode
ly_set(CMAKE_XCODE_GENERATE_SCHEME TRUE)

@ -5,7 +5,47 @@
#
#
# Empty implementations for untested platforms to fix build errors.
#! ly_install_target_override: Mac specific target installation
function(ly_install_target_override)
function(ly_setup_o3de_install)
endfunction()
set(options)
set(oneValueArgs TARGET ARCHIVE_DIR LIBRARY_DIR RUNTIME_DIR LIBRARY_SUBDIR RUNTIME_SUBDIR)
set(multiValueArgs)
cmake_parse_arguments(ly_platform_install_target "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
get_property(install_component TARGET ${ly_platform_install_target_TARGET} PROPERTY INSTALL_COMPONENT)
# For bundles on Mac, we set the icons by passing in a path to the Images.xcassets directory.
# However, the CMake install command expects paths to files for the the RESOURCE property.
# More details can be found in the CMake issue: https://gitlab.kitware.com/cmake/cmake/-/issues/22409
get_target_property(is_bundle ${ly_platform_install_target_TARGET} MACOSX_BUNDLE)
if (${is_bundle})
get_target_property(cached_resources_dir ${ly_platform_install_target_TARGET} RESOURCE)
set_property(TARGET ${ly_platform_install_target_TARGET} PROPERTY RESOURCE "")
endif()
install(
TARGETS ${ly_platform_install_target_TARGET}
ARCHIVE
DESTINATION ${ly_platform_install_target_ARCHIVE_DIR}/${PAL_PLATFORM_NAME}/$<CONFIG>
COMPONENT ${install_component}
LIBRARY
DESTINATION ${ly_platform_install_target_LIBRARY_DIR}/${PAL_PLATFORM_NAME}/$<CONFIG>/${ly_platform_install_target_LIBRARY_SUBDIR}
COMPONENT ${install_component}
RUNTIME
DESTINATION ${ly_platform_install_target_RUNTIME_DIR}/${PAL_PLATFORM_NAME}/$<CONFIG>/${ly_platform_install_target_RUNTIME_SUBDIR}
COMPONENT ${install_component}
BUNDLE
DESTINATION ${ly_platform_install_target_RUNTIME_DIR}/${PAL_PLATFORM_NAME}/$<CONFIG>/${ly_platform_install_target_RUNTIME_SUBDIR}
COMPONENT ${install_component}
RESOURCE
DESTINATION ${ly_platform_install_target_RUNTIME_DIR}/${PAL_PLATFORM_NAME}/$<CONFIG>/${ly_platform_install_target_RUNTIME_SUBDIR}/
COMPONENT ${install_component}
)
if (${is_bundle})
set_property(TARGET ${ly_platform_install_target_TARGET} PROPERTY RESOURCE ${cached_resources_dir})
endif()
endfunction()
include(cmake/Platform/Common/Install_common.cmake)

Loading…
Cancel
Save