diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/CMakeLists.txt
index f05aff6db3..baf8e9733e 100644
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/CMakeLists.txt
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/CMakeLists.txt
@@ -103,6 +103,19 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
AZ::AssetBundlerBatch
)
+ ly_add_pytest(
+ NAME AssetPipelineTests.AssetBundler_SandBox
+ TEST_SUITE sandbox
+ PATH ${CMAKE_CURRENT_LIST_DIR}/asset_bundler_batch_tests.py
+ PYTEST_MARKS "SUITE_sandbox" # run only sandbox tests in this file
+ EXCLUDE_TEST_RUN_TARGET_FROM_IDE
+ TEST_SERIAL
+ TIMEOUT 1500
+ RUNTIME_DEPENDENCIES
+ AZ::AssetProcessor
+ AZ::AssetBundlerBatch
+ )
+
ly_add_pytest(
NAME AssetPipelineTests.AssetBuilder
PATH ${CMAKE_CURRENT_LIST_DIR}/asset_builder_tests.py
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py
index 926f1cc602..1a5ece92e1 100755
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py
@@ -730,6 +730,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object):
@pytest.mark.BAT
@pytest.mark.assetpipeline
+ @pytest.mark.SUITE_sandbox
@pytest.mark.test_case_id("C16877174")
@pytest.mark.test_case_id("C16877175")
@pytest.mark.test_case_id("C16877178")
diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py
index 7ed08867a9..3f3885a028 100644
--- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py
+++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py
@@ -49,7 +49,6 @@ class TestViewMenuOptions(EditorTestHelper):
("Viewport", "Center on Selection"),
("Viewport", "Go to Location"),
("Viewport", "Remember Location"),
- ("Viewport", "Change Move Speed"),
("Viewport", "Switch Camera"),
("Viewport", "Show/Hide Helpers"),
("Refresh Style",),
diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py
index b45a087c79..982f3617b5 100644
--- a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py
+++ b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py
@@ -89,7 +89,6 @@ class TestMenus(object):
"Center on Selection Action triggered",
"Go to Location Action triggered",
"Remember Location Action triggered",
- "Change Move Speed Action triggered",
"Switch Camera Action triggered",
"Show/Hide Helpers Action triggered",
"Refresh Style Action triggered",
diff --git a/AutomatedTesting/Gem/Resources/CryEngineLogoLauncher.bmp b/AutomatedTesting/Gem/Resources/CryEngineLogoLauncher.bmp
deleted file mode 100644
index fe0adc54a4..0000000000
--- a/AutomatedTesting/Gem/Resources/CryEngineLogoLauncher.bmp
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:cf6d56fe4c367d39bd78500dd34332fcad57ad41241768b52781dbdb60ddd972
-size 347568
diff --git a/AutomatedTesting/Gem/Resources/GameSDK.ico b/AutomatedTesting/Gem/Resources/GameSDK.ico
index cb935cd926..0be1f28b6c 100644
--- a/AutomatedTesting/Gem/Resources/GameSDK.ico
+++ b/AutomatedTesting/Gem/Resources/GameSDK.ico
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:61efd8df621780af995fc1250918df5e00364ff00f849bef67702cd4b0a152e1
-size 65537
+oid sha256:41239f8345fa91fe546442208461ad3cd17c7a7a7047af45018b97363bfea204
+size 109783
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadAppIcon152x152.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadAppIcon152x152.png
index b0dd493c11..ad18894411 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadAppIcon152x152.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadAppIcon152x152.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e4901093fa6190bf37291b0fb6de23fba1be8ebbd742775a8565a4106722fbb6
-size 31942
+oid sha256:ebfc95bd4c0cbcc53d0ef9d314d26e09a347a22dabbf210597f405d9ed8646bf
+size 7729
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadAppIcon76x76.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadAppIcon76x76.png
index 21aa62e96b..888d8cf785 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadAppIcon76x76.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadAppIcon76x76.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e4ae97c4f44910121a61686862c8342ce598db4cdf9d46b29e96d3cb9e43bd06
-size 22158
+oid sha256:99cb7da9282cfcfa64598455827f27ca6791d45ca0d2c3c2dc090d82468dac03
+size 4447
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadProAppIcon167x167.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadProAppIcon167x167.png
index 6b696a84b2..86aa72016a 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadProAppIcon167x167.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadProAppIcon167x167.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:061e2d0ce8dc852dd298c80f2aed5fee8ea4b87511c00662aa2d00922c0ba3c2
-size 30162
+oid sha256:101568e946f1d4cea86d666187bbf71116bbf62e6eaf6d80bc3c5e2e184bdb15
+size 7938
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSettingsIcon29x29.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSettingsIcon29x29.png
index f3dfa05839..79331c29b1 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSettingsIcon29x29.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSettingsIcon29x29.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0fb4b4b77620d99dae7473b7bd8affe14630419835bd5719167ed200e657fa4f
-size 17504
+oid sha256:cf930ffd4efb0b7b627e05aac6e0f56252ea206623e8b5d097d803aa315cdfb8
+size 1812
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSettingsIcon58x58.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSettingsIcon58x58.png
index 5325b805fd..27c4aaef2e 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSettingsIcon58x58.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSettingsIcon58x58.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8aa9b1194f3244025578225a6a87cbc2dd12c70955ff615c8af640ea7f1334f1
-size 19619
+oid sha256:ba5fea53b349e254b4625035a308d5731cb06f6d0adc278874d14db2627962cb
+size 3424
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSpotlightIcon40x40.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSpotlightIcon40x40.png
index 98d8455838..df1630a95a 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSpotlightIcon40x40.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSpotlightIcon40x40.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0c25ffb1af8160b3202977de8c32aaa235e22c643ffd8004e4546c96868ef3b9
-size 18317
+oid sha256:cf087f357cd439d14651073ac079542c60f0648a30dced2a8d19912124b3f8b6
+size 2310
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSpotlightIcon80x80.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSpotlightIcon80x80.png
index 7482f6c892..4b7f5d6318 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSpotlightIcon80x80.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPadSpotlightIcon80x80.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2db961b8f922a552d8ad374fdb56029efd4049a6cde10399b3d961242c82ce53
-size 22571
+oid sha256:421ad4db14c28ed18666158f9ec30747c5b8c757405c1efb32442978911b0c06
+size 4437
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneAppIcon120x120.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneAppIcon120x120.png
index da987b86f9..674c6da124 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneAppIcon120x120.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneAppIcon120x120.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f39d897a57d4da0a70ede7c91339660b28e9d8c57b3e7d749807b13baa4b85f3
-size 28559
+oid sha256:0d0044ebf7e0a5dd23ed64a1289c705d8f6c3c41a62d65e5a1371058855b8cec
+size 6546
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneAppIcon180x180.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneAppIcon180x180.png
index 205e025c36..c0c10c2390 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneAppIcon180x180.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneAppIcon180x180.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:263b75d58328499eef1f8fa2e64c30706f546badcc0c4464a043b231da93cd0d
-size 34969
+oid sha256:3b8717c5f2109dfce1bf7b017278059d4915b524a6eb7e83cfb1926e54ed6869
+size 7383
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSettingsIcon58x58.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSettingsIcon58x58.png
index 0deb4f4f35..27c4aaef2e 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSettingsIcon58x58.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSettingsIcon58x58.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:33522ad8a8e826b22dd9ad214f56e63e24bf55c00bd8c845925d848b855dfb48
-size 19619
+oid sha256:ba5fea53b349e254b4625035a308d5731cb06f6d0adc278874d14db2627962cb
+size 3424
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSettingsIcon87x87.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSettingsIcon87x87.png
index 78591751d7..9093e13867 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSettingsIcon87x87.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSettingsIcon87x87.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f405c9f3d908d038aea26049e533b0d10955adfac370c7b3b80209997ea706d0
-size 24407
+oid sha256:a32908a839a6cb0ca2a76d6aa60376ba8a14b4428f06c13149ec277514eb5676
+size 4533
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSpotlightIcon120x120.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSpotlightIcon120x120.png
index 034dcb9fed..674c6da124 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSpotlightIcon120x120.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSpotlightIcon120x120.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d110f6e151799a2327bcdf5ef94d6fc82b114783a8cc973a8915896679ba4a80
-size 28559
+oid sha256:0d0044ebf7e0a5dd23ed64a1289c705d8f6c3c41a62d65e5a1371058855b8cec
+size 6546
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSpotlightIcon80x80.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSpotlightIcon80x80.png
index f0fa89149c..4b7f5d6318 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSpotlightIcon80x80.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AppIcon.appiconset/iPhoneSpotlightIcon80x80.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:db8f00568fad4e49b05249aaa7a48c9fbf85c8b7a78489c83dc9b8161778bcef
-size 22571
+oid sha256:421ad4db14c28ed18666158f9ec30747c5b8c757405c1efb32442978911b0c06
+size 4437
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadAppIcon152x152.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadAppIcon152x152.png
index b0dd493c11..ad18894411 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadAppIcon152x152.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadAppIcon152x152.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e4901093fa6190bf37291b0fb6de23fba1be8ebbd742775a8565a4106722fbb6
-size 31942
+oid sha256:ebfc95bd4c0cbcc53d0ef9d314d26e09a347a22dabbf210597f405d9ed8646bf
+size 7729
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadAppIcon76x76.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadAppIcon76x76.png
index 21aa62e96b..888d8cf785 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadAppIcon76x76.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadAppIcon76x76.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e4ae97c4f44910121a61686862c8342ce598db4cdf9d46b29e96d3cb9e43bd06
-size 22158
+oid sha256:99cb7da9282cfcfa64598455827f27ca6791d45ca0d2c3c2dc090d82468dac03
+size 4447
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadProAppIcon167x167.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadProAppIcon167x167.png
index 6b696a84b2..86aa72016a 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadProAppIcon167x167.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadProAppIcon167x167.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:061e2d0ce8dc852dd298c80f2aed5fee8ea4b87511c00662aa2d00922c0ba3c2
-size 30162
+oid sha256:101568e946f1d4cea86d666187bbf71116bbf62e6eaf6d80bc3c5e2e184bdb15
+size 7938
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSettingsIcon29x29.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSettingsIcon29x29.png
index f3dfa05839..79331c29b1 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSettingsIcon29x29.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSettingsIcon29x29.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0fb4b4b77620d99dae7473b7bd8affe14630419835bd5719167ed200e657fa4f
-size 17504
+oid sha256:cf930ffd4efb0b7b627e05aac6e0f56252ea206623e8b5d097d803aa315cdfb8
+size 1812
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSettingsIcon58x58.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSettingsIcon58x58.png
index 5325b805fd..27c4aaef2e 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSettingsIcon58x58.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSettingsIcon58x58.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8aa9b1194f3244025578225a6a87cbc2dd12c70955ff615c8af640ea7f1334f1
-size 19619
+oid sha256:ba5fea53b349e254b4625035a308d5731cb06f6d0adc278874d14db2627962cb
+size 3424
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSpotlightIcon40x40.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSpotlightIcon40x40.png
index 98d8455838..df1630a95a 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSpotlightIcon40x40.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSpotlightIcon40x40.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0c25ffb1af8160b3202977de8c32aaa235e22c643ffd8004e4546c96868ef3b9
-size 18317
+oid sha256:cf087f357cd439d14651073ac079542c60f0648a30dced2a8d19912124b3f8b6
+size 2310
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSpotlightIcon80x80.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSpotlightIcon80x80.png
index 7482f6c892..4b7f5d6318 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSpotlightIcon80x80.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPadSpotlightIcon80x80.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2db961b8f922a552d8ad374fdb56029efd4049a6cde10399b3d961242c82ce53
-size 22571
+oid sha256:421ad4db14c28ed18666158f9ec30747c5b8c757405c1efb32442978911b0c06
+size 4437
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneAppIcon120x120.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneAppIcon120x120.png
index da987b86f9..674c6da124 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneAppIcon120x120.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneAppIcon120x120.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f39d897a57d4da0a70ede7c91339660b28e9d8c57b3e7d749807b13baa4b85f3
-size 28559
+oid sha256:0d0044ebf7e0a5dd23ed64a1289c705d8f6c3c41a62d65e5a1371058855b8cec
+size 6546
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneAppIcon180x180.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneAppIcon180x180.png
index 205e025c36..c0c10c2390 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneAppIcon180x180.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneAppIcon180x180.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:263b75d58328499eef1f8fa2e64c30706f546badcc0c4464a043b231da93cd0d
-size 34969
+oid sha256:3b8717c5f2109dfce1bf7b017278059d4915b524a6eb7e83cfb1926e54ed6869
+size 7383
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSettingsIcon58x58.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSettingsIcon58x58.png
index 0deb4f4f35..27c4aaef2e 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSettingsIcon58x58.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSettingsIcon58x58.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:33522ad8a8e826b22dd9ad214f56e63e24bf55c00bd8c845925d848b855dfb48
-size 19619
+oid sha256:ba5fea53b349e254b4625035a308d5731cb06f6d0adc278874d14db2627962cb
+size 3424
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSettingsIcon87x87.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSettingsIcon87x87.png
index 78591751d7..9093e13867 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSettingsIcon87x87.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSettingsIcon87x87.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f405c9f3d908d038aea26049e533b0d10955adfac370c7b3b80209997ea706d0
-size 24407
+oid sha256:a32908a839a6cb0ca2a76d6aa60376ba8a14b4428f06c13149ec277514eb5676
+size 4533
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSpotlightIcon120x120.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSpotlightIcon120x120.png
index 034dcb9fed..674c6da124 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSpotlightIcon120x120.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSpotlightIcon120x120.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d110f6e151799a2327bcdf5ef94d6fc82b114783a8cc973a8915896679ba4a80
-size 28559
+oid sha256:0d0044ebf7e0a5dd23ed64a1289c705d8f6c3c41a62d65e5a1371058855b8cec
+size 6546
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSpotlightIcon80x80.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSpotlightIcon80x80.png
index f0fa89149c..4b7f5d6318 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSpotlightIcon80x80.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/AutomatedTestingAppIcon.appiconset/iPhoneSpotlightIcon80x80.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:db8f00568fad4e49b05249aaa7a48c9fbf85c8b7a78489c83dc9b8161778bcef
-size 22571
+oid sha256:421ad4db14c28ed18666158f9ec30747c5b8c757405c1efb32442978911b0c06
+size 4437
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/Contents.json b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/Contents.json
index 5ea213954f..f836f07ee7 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/Contents.json
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/Contents.json
@@ -1,41 +1,5 @@
{
"images" : [
- {
- "extent" : "full-screen",
- "filename" : "iPhoneLaunchImage1242x2688.png",
- "idiom" : "iphone",
- "minimum-system-version" : "12.0",
- "orientation" : "portrait",
- "scale" : "3x",
- "subtype" : "2688h"
- },
- {
- "extent" : "full-screen",
- "filename" : "iPhoneLaunchImage2688x1242.png",
- "idiom" : "iphone",
- "minimum-system-version" : "12.0",
- "orientation" : "landscape",
- "scale" : "3x",
- "subtype" : "2688h"
- },
- {
- "extent" : "full-screen",
- "filename" : "iPhoneLaunchImage828x1792.png",
- "idiom" : "iphone",
- "minimum-system-version" : "12.0",
- "orientation" : "portrait",
- "scale" : "2x",
- "subtype" : "1792h"
- },
- {
- "extent" : "full-screen",
- "filename" : "iPhoneLaunchImage1792x828.png",
- "idiom" : "iphone",
- "minimum-system-version" : "12.0",
- "orientation" : "landscape",
- "scale" : "2x",
- "subtype" : "1792h"
- },
{
"extent" : "full-screen",
"idiom" : "iphone",
@@ -202,4 +166,4 @@
"version" : 1,
"author" : "xcode"
}
-}
+}
\ No newline at end of file
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage1024x768.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage1024x768.png
index 1249ef3703..9f586d6af3 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage1024x768.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage1024x768.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:31afa7ed44c5d9844c8d6ce08beccac482c3f43590869a3d190d06e2df377ccc
-size 137472
+oid sha256:a4018d9df45b4a04d4cf24a40fe01aa7e30e44a9fdd8ad9a41b0d87791786c12
+size 30442
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage1536x2048.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage1536x2048.png
index cdb6d5a82a..c978631c22 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage1536x2048.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage1536x2048.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0aac8ef9899442820bec0df8bf6434a46cc787d57c5d6d38a04727b8dc310048
-size 338281
+oid sha256:2eea06cb8ad05acefe9664551af5645d52d9763b82473b1fd4a2b2b6f62e96d3
+size 53550
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage2048x1536.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage2048x1536.png
index 954d3084c8..a52e832a42 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage2048x1536.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage2048x1536.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c07495891f15b138ba09f142777b0f43217bf8be05cbb74ba938319f3425980c
-size 321125
+oid sha256:90991aca91ab7222fdb85c03947cff38f549a6492551e7447e0c8f55022aae48
+size 52467
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage768x1024.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage768x1024.png
index 021319fbc3..3e441fab3b 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage768x1024.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPadLaunchImage768x1024.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d6bf6acb92421a453a36fc143ab6cefda14d631ea5e6dbf95c6e252a445fcbac
-size 144797
+oid sha256:6c8439a64d18dbff17dd67f6405bf49f99695e9b22fc2cc541dc72c6e3167307
+size 30564
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage1242x2688.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage1242x2688.png
deleted file mode 100644
index e190d2b585..0000000000
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage1242x2688.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fc79117e25cc7533ccf6724453e3f44a01b4eaaecded6fa826abe897456f36ee
-size 405896
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage1792x828.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage1792x828.png
deleted file mode 100644
index 5601f081de..0000000000
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage1792x828.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6c7191be3bdae09dc621012a26b0c1b9c15de1d567cf65ff1079e00f8636a32a
-size 220720
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage2688x1242.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage2688x1242.png
deleted file mode 100644
index 844b23fdd9..0000000000
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage2688x1242.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:dfbd362f9cb5f285c23807a032af98150cf5409c514445122683736a3c65008c
-size 364976
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage640x1136.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage640x1136.png
index a15fd777fa..e662e9675c 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage640x1136.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage640x1136.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e9ad650fda925b1c076a67d1ef70315fe4f14db888c9fd36ee4eba1d18c1e7d1
-size 166749
+oid sha256:f752615184160d7a78f28d9eef354c86e544f11eb1dde9f651d7acd315b3f2e6
+size 35934
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage640x960.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage640x960.png
index 2855f4069d..2753529fc2 100644
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage640x960.png
+++ b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage640x960.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:16f6e9d7bd15fc528d934c252213de8792812e708b1810191c5f1767f7165852
-size 142331
+oid sha256:1a43f1d893e85aa99d335a657ec0f6c13a741db976c033451ab9a2328b8a5970
+size 35559
diff --git a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage828x1792.png b/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage828x1792.png
deleted file mode 100644
index 9ebb93e39c..0000000000
--- a/AutomatedTesting/Gem/Resources/IOSLauncher/Images.xcassets/LaunchImage.launchimage/iPhoneLaunchImage828x1792.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b0252b068b232f521ac6eca4a708fad6eaf257d0a66aa03f4f865f6a0b219cfc
-size 236433
diff --git a/AutomatedTesting/Gem/Resources/LegacyLogoLauncher.bmp b/AutomatedTesting/Gem/Resources/LegacyLogoLauncher.bmp
new file mode 100644
index 0000000000..7a35cddd49
--- /dev/null
+++ b/AutomatedTesting/Gem/Resources/LegacyLogoLauncher.bmp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7c8433178baebafe984ca23d9325d3c71b5a177fc3b3b869afbb01a583542fbe
+size 462842
diff --git a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/Contents.json b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/Contents.json
index bfa8bcf478..2b63c0ee15 100644
--- a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/Contents.json
+++ b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -33,7 +33,7 @@
{
"size" : "128x128",
"idiom" : "mac",
- "filename" : "icon_128 _2x.png",
+ "filename" : "icon_128_2x.png",
"scale" : "2x"
},
{
@@ -45,7 +45,7 @@
{
"size" : "256x256",
"idiom" : "mac",
- "filename" : "icon_256 _2x.png",
+ "filename" : "icon_256_2x.png",
"scale" : "2x"
},
{
diff --git a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_128 _2x.png b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_128 _2x.png
deleted file mode 100644
index 5970ea34ba..0000000000
--- a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_128 _2x.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e38257b6917cdf5d73e90e6009f10c8736d62b20c4e785085305075c7e6320e2
-size 32037
diff --git a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_128.png b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_128.png
index 9e30e09547..5a73a4a54d 100644
--- a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_128.png
+++ b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_128.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9f41a37d2347a617e93bd97adaf6d4c161c471ca3ef7e04b98c65ddda52396dc
-size 27833
+oid sha256:f3c651ca45a83d0f68bdaa466826a29b2ca6f674e225d90e68b7dbadc2ba582d
+size 6620
diff --git a/Templates/DefaultProject/Template/Resources/Platform/Mac/Images.xcassets/TestDPAppIcon.appiconset/icon_128 _2x.png b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_128_2x.png
similarity index 100%
rename from Templates/DefaultProject/Template/Resources/Platform/Mac/Images.xcassets/TestDPAppIcon.appiconset/icon_128 _2x.png
rename to AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_128_2x.png
diff --git a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_16.png b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_16.png
index aeb29abd0a..a7ec66841f 100644
--- a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_16.png
+++ b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_16.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b07984494059bf827bc485cbea06d12e0283811face1a18799495f9ba7ae8af1
-size 20779
+oid sha256:f7d5b15add5104d91a03df7d86898f4bc415d095d11c23555b24440497371948
+size 1061
diff --git a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_16_2x.png b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_16_2x.png
index 445a389d61..474378c926 100644
--- a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_16_2x.png
+++ b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_16_2x.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e645142d284de40aafb7a4a858f3df92b6a5ba9b03fa5f1a2d3cb25211597926
-size 21857
+oid sha256:148fdae6493d7b7e1bb6cc6aae1861e0469838f54dcb3c15cc157a548c707fec
+size 1910
diff --git a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_256 _2x.png b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_256 _2x.png
deleted file mode 100644
index 0904cf7ce8..0000000000
--- a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_256 _2x.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:07631f41b8dea80713d2463f81a713a9a93798975b6fb50afbeeb13d26c57fa2
-size 48899
diff --git a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_256.png b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_256.png
index 5970ea34ba..d30f2d3686 100644
--- a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_256.png
+++ b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_256.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e38257b6917cdf5d73e90e6009f10c8736d62b20c4e785085305075c7e6320e2
-size 32037
+oid sha256:094620c172320b062f9a1f8cc758ef4bbee11bc0a6049f46ad6b42f9bf613c92
+size 9679
diff --git a/Templates/DefaultProject/Template/Resources/Platform/Mac/Images.xcassets/TestDPAppIcon.appiconset/icon_256 _2x.png b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_256_2x.png
similarity index 100%
rename from Templates/DefaultProject/Template/Resources/Platform/Mac/Images.xcassets/TestDPAppIcon.appiconset/icon_256 _2x.png
rename to AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_256_2x.png
diff --git a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_32.png b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_32.png
index 445a389d61..474378c926 100644
--- a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_32.png
+++ b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_32.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e645142d284de40aafb7a4a858f3df92b6a5ba9b03fa5f1a2d3cb25211597926
-size 21857
+oid sha256:148fdae6493d7b7e1bb6cc6aae1861e0469838f54dcb3c15cc157a548c707fec
+size 1910
diff --git a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_32_2x.png b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_32_2x.png
index 1fad9bda96..3359e99cd4 100644
--- a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_32_2x.png
+++ b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_32_2x.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ad83faf98b49f4e37112baedeae726f4f8d71bcdd1961d9cdad31f043f8ca666
-size 24003
+oid sha256:749bcd29d73e5ef2d1ef8b2d878626d0bca09c6b0d5f1c9dc6cefe7b9082c8cc
+size 3758
diff --git a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_512.png b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_512.png
index e1517dddb6..f9ff1c15e3 100644
--- a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_512.png
+++ b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_512.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:68529a6c11d5ffa7ecd9d5bbb11ceea28e6852bd45946b525af09602c9a1e1bf
-size 48899
+oid sha256:934502e242ff7a2e34e21eed1424b5e0953e701761d158520b3297944132328e
+size 18716
diff --git a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_512_2x.png b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_512_2x.png
index b425cb685f..a736c7f6b8 100644
--- a/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_512_2x.png
+++ b/AutomatedTesting/Gem/Resources/MacLauncher/Images.xcassets/AppIcon.appiconset/icon_512_2x.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8a70003840b418848b2ce6c18ed7cbbfcd6fcf76598a6601dca8b98d9b6c1a2f
-size 114706
+oid sha256:5719043940db268dccd2e20bd9d6aa13131890d43edf002a173714ae33890422
+size 29510
diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp
index 1cfc3b6dd0..1b461e1356 100644
--- a/Code/Editor/EditorViewportWidget.cpp
+++ b/Code/Editor/EditorViewportWidget.cpp
@@ -714,7 +714,7 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
if (m_renderViewport)
{
- m_renderViewport->GetControllerList()->SetEnabled(false);
+ m_renderViewport->SetInputProcessingEnabled(false);
}
}
break;
@@ -738,7 +738,7 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
if (m_renderViewport)
{
- m_renderViewport->GetControllerList()->SetEnabled(true);
+ m_renderViewport->SetInputProcessingEnabled(true);
}
break;
diff --git a/Code/Editor/LegacyViewportCameraController.cpp b/Code/Editor/LegacyViewportCameraController.cpp
index 0750db8d97..f437196548 100644
--- a/Code/Editor/LegacyViewportCameraController.cpp
+++ b/Code/Editor/LegacyViewportCameraController.cpp
@@ -404,7 +404,7 @@ bool LegacyViewportCameraControllerInstance::HandleInputChannelEvent(const AzFra
}
else if (auto key = GetKeyboardKey(event.m_inputChannel); key != Qt::Key_unknown)
{
- if (state == InputChannel::State::Ended)
+ if (!event.m_inputChannel.IsActive())
{
m_pressedKeys.erase(key);
}
diff --git a/Code/Editor/Plugins/EditorCommon/EditorCommon.def b/Code/Editor/Plugins/EditorCommon/EditorCommon.def
deleted file mode 100644
index a8d10ff297..0000000000
--- a/Code/Editor/Plugins/EditorCommon/EditorCommon.def
+++ /dev/null
@@ -1,6 +0,0 @@
-; EditorCommon.def : Declares the module parameters for the DLL.
-
-LIBRARY
-
-EXPORTS
- ; Explicit exports can go here
diff --git a/Code/Editor/Plugins/EditorCommon/res/EditorCommon.rc2 b/Code/Editor/Plugins/EditorCommon/res/EditorCommon.rc2
deleted file mode 100644
index 8ffc09417a..0000000000
Binary files a/Code/Editor/Plugins/EditorCommon/res/EditorCommon.rc2 and /dev/null differ
diff --git a/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp b/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp
index f1a2aafd1e..f4e4fed973 100644
--- a/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp
+++ b/Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp
@@ -585,31 +585,11 @@ namespace ProjectSettingsTool
ShowAllErrorsThenExitIfInvalid();
m_ui->reconfigureLog->setText("");
- int result = QMessageBox::question
- (
- this,
- tr("Reconfigure Project"),
- tr("For new settings to be applied the project must be reconfigured. Would you like run configure now?"),
- QMessageBox::Yes,
- QMessageBox::No
- );
+ QMessageBox::information(this, tr("Project Settings Saved"),
+ tr("The project may need to be manually reconfigured for the new settings to be applied."));
- if (QMessageBox::Yes == result)
- {
- m_ui->reconfigureLog->show();
- #if defined(AZ_PLATFORM_WINDOWS)
- m_reconfigureProcess.start("cmd.exe", { QString("/C %1").arg("lmbr_waf.bat configure") });
- #elif defined(AZ_PLATFORM_MAC) || defined(AZ_PLATFORM_LINUX)
- m_reconfigureProcess.start("/bin/sh", { QString("%1").arg("lmbr_waf.sh configure") });
- #else
- #error "Needs to be implemented"
- #endif
- }
- else
- {
- m_ui->reloadButton->setEnabled(true);
- m_ui->saveButton->setEnabled(true);
- }
+ m_ui->reloadButton->setEnabled(true);
+ m_ui->saveButton->setEnabled(true);
}
// Show a message box telling user settings failed to save
else
diff --git a/Code/Editor/Plugins/ProjectSettingsTool/Validators.cpp b/Code/Editor/Plugins/ProjectSettingsTool/Validators.cpp
index 8484a2937a..c17da8e40b 100644
--- a/Code/Editor/Plugins/ProjectSettingsTool/Validators.cpp
+++ b/Code/Editor/Plugins/ProjectSettingsTool/Validators.cpp
@@ -172,8 +172,9 @@ namespace ProjectSettingsTool
// Returns true if valid ios version number
RetType IOSVersionNumber(const QString& value)
{
+ // support up to 4-component numerical-only version strings
return RegularExpressionValidator
- ("(0|[1-9][0-9]{0,8}|[1-2][0-1][0-9]{0,8})(\\.(0|[1-9][0-9]{0,8}|[1-2][0-1][0-9]{0,8})){0,2}",
+ ("^(\\d+)(\\.\\d+){0,3}$",
value,
maxIosVersionLength);
}
diff --git a/Code/Editor/Plugins/QtMocRule.props b/Code/Editor/Plugins/QtMocRule.props
deleted file mode 100644
index af7c107071..0000000000
--- a/Code/Editor/Plugins/QtMocRule.props
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
- %(RootDir)%(Directory)%(FileName).moc
- $(QTDIR)\bin\moc.exe [AllOptions] [Inputs]
- Moc'ing %(Filename)%(Extension)...
-
-
-
diff --git a/Code/Editor/Plugins/QtMocRule.targets b/Code/Editor/Plugins/QtMocRule.targets
deleted file mode 100644
index fdcb95e6a3..0000000000
--- a/Code/Editor/Plugins/QtMocRule.targets
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
- _QtMOC
-
-
-
- $(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml
-
-
-
-
-
-
-
- @(QtMOC, '|')
-
-
-
-
-
-
-
diff --git a/Code/Editor/Plugins/QtMocRule.xml b/Code/Editor/Plugins/QtMocRule.xml
deleted file mode 100644
index 21850b34ab..0000000000
--- a/Code/Editor/Plugins/QtMocRule.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
- General
-
-
-
-
- Command Line
-
-
-
-
-
-
-
-
-
-
-
-
-
- Additional Options
-
-
- Additional Options
-
-
-
-
-
-
diff --git a/Code/Editor/Plugins/QtRccRule.props b/Code/Editor/Plugins/QtRccRule.props
deleted file mode 100644
index 9c06b5c082..0000000000
--- a/Code/Editor/Plugins/QtRccRule.props
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
- %(RootDir)%(Directory)rcc_%(FileName).h
- $(QTDIR)\bin\rcc.exe [AllOptions] [Inputs]
- Rcc'ing %(Filename)%(Extension)...
-
-
-
diff --git a/Code/Editor/Plugins/QtRccRule.targets b/Code/Editor/Plugins/QtRccRule.targets
deleted file mode 100644
index bd4e7ae90e..0000000000
--- a/Code/Editor/Plugins/QtRccRule.targets
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
- _QtRCC
-
-
-
- $(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml
-
-
-
-
-
-
-
- @(QtRCC, '|')
-
-
-
-
-
-
-
diff --git a/Code/Editor/Plugins/QtRccRule.xml b/Code/Editor/Plugins/QtRccRule.xml
deleted file mode 100644
index ce7b98c323..0000000000
--- a/Code/Editor/Plugins/QtRccRule.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
- General
-
-
-
-
- Command Line
-
-
-
-
-
-
-
-
-
-
-
-
-
- Additional Options
-
-
- Additional Options
-
-
-
-
-
-
diff --git a/Code/Editor/Plugins/QtUicRule.props b/Code/Editor/Plugins/QtUicRule.props
deleted file mode 100644
index ce08b40be7..0000000000
--- a/Code/Editor/Plugins/QtUicRule.props
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
- %(RootDir)%(Directory)ui_%(FileName).h
- $(QTDIR)\bin\uic.exe [AllOptions] [Inputs]
- Uic'ing %(Filename)%(Extension)...
-
-
-
diff --git a/Code/Editor/Plugins/QtUicRule.targets b/Code/Editor/Plugins/QtUicRule.targets
deleted file mode 100644
index a5ada15f79..0000000000
--- a/Code/Editor/Plugins/QtUicRule.targets
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
- _QtUIC
-
-
-
- $(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml
-
-
-
-
-
-
-
- @(QtUIC, '|')
-
-
-
-
-
-
-
-
diff --git a/Code/Editor/Plugins/QtUicRule.xml b/Code/Editor/Plugins/QtUicRule.xml
deleted file mode 100644
index ec7ce4b214..0000000000
--- a/Code/Editor/Plugins/QtUicRule.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
- General
-
-
-
-
- Command Line
-
-
-
-
-
-
-
-
-
-
-
-
-
- Additional Options
-
-
- Additional Options
-
-
-
-
-
-
diff --git a/Code/Editor/RenderViewport.h b/Code/Editor/RenderViewport.h
index 1764028dd6..2a67e1bbc6 100644
--- a/Code/Editor/RenderViewport.h
+++ b/Code/Editor/RenderViewport.h
@@ -219,6 +219,7 @@ public:
void SetFullScreenState(bool fullScreenState) override;
bool CanToggleFullScreenState() const override;
void ToggleFullScreenState() override;
+ float GetDpiScaleFactor() const override { return 1.0f; };
void ConnectViewportInteractionRequestBus();
void DisconnectViewportInteractionRequestBus();
diff --git a/Code/Editor/TrackView/AtomOutputFrameCapture.cpp b/Code/Editor/TrackView/AtomOutputFrameCapture.cpp
index d5ceeae9b2..cd5a977367 100644
--- a/Code/Editor/TrackView/AtomOutputFrameCapture.cpp
+++ b/Code/Editor/TrackView/AtomOutputFrameCapture.cpp
@@ -73,7 +73,7 @@ namespace TrackView
bool startedCapture = false;
AZ::Render::FrameCaptureRequestBus::BroadcastResult(
startedCapture, &AZ::Render::FrameCaptureRequestBus::Events::CapturePassAttachmentWithCallback, m_passHierarchy,
- AZStd::string("Output"), attachmentReadbackCallback);
+ AZStd::string("Output"), attachmentReadbackCallback, AZ::RPI::PassAttachmentReadbackOption::Output);
return startedCapture;
}
diff --git a/Code/Editor/ViewportManipulatorController.cpp b/Code/Editor/ViewportManipulatorController.cpp
index c7d2885cb2..fb696a61f3 100644
--- a/Code/Editor/ViewportManipulatorController.cpp
+++ b/Code/Editor/ViewportManipulatorController.cpp
@@ -216,7 +216,8 @@ namespace SandboxEditor
interactionHandled, AzToolsFramework::GetEntityContextId(), targetInteractionEvent, mouseInteractionEvent);
}
- return interactionHandled;
+ // Only filter button/key press events, not release events
+ return interactionHandled && event.m_inputChannel.IsActive();
}
void ViewportManipulatorControllerInstance::ResetInputChannels()
diff --git a/Code/Editor/editor_lib_files.cmake b/Code/Editor/editor_lib_files.cmake
index 562623dab2..fc1694240e 100644
--- a/Code/Editor/editor_lib_files.cmake
+++ b/Code/Editor/editor_lib_files.cmake
@@ -21,7 +21,6 @@ set(FILES
res/TreeView.bmp
res/VisualLog_PlayerButtons.bmp
res/ab_toolbar.bmp
- res/about_dark.bmp
res/anim.bmp
res/animatio.bmp
res/animations_tree_soundevent.bmp
@@ -137,7 +136,6 @@ set(FILES
res/litebulb.bmp
res/lock_sel.bmp
res/locksele.bmp
- res/logo.bmp
res/mainfram.bmp
res/mann_tagdef_toolbar.bmp
res/mann_tagdef_tree.bmp
@@ -184,8 +182,6 @@ set(FILES
res/rename.ico
res/replace.ico
res/ribbon_system_button.png
- res/sandbox_dark.bmp
- res/sb_welcome_dark.bmp
res/selectobj.bmp
res/seq_1_colour_keys.bmp
res/seq_2_colour_keys.bmp
diff --git a/Code/Editor/res/about_dark.bmp b/Code/Editor/res/about_dark.bmp
deleted file mode 100644
index 2bf31764a6..0000000000
--- a/Code/Editor/res/about_dark.bmp
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:887d184cf49cf78c62a1fe53eac3cb8e7b071bb67e09b801a4893445ac4c800f
-size 542456
diff --git a/Code/Editor/res/logo.bmp b/Code/Editor/res/logo.bmp
deleted file mode 100644
index c67ca02a07..0000000000
--- a/Code/Editor/res/logo.bmp
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8b65af2765042354ae4110dc7bcbde905e4a55a4995f66b626d15ec6c0fa18c1
-size 96056
diff --git a/Code/Editor/res/logo.gif b/Code/Editor/res/logo.gif
deleted file mode 100644
index 00534c61cc..0000000000
--- a/Code/Editor/res/logo.gif
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:79412e83b32bb6712d9701f78465878a2057a590698a4dc8d8c7aa11de2623ef
-size 4227
diff --git a/Code/Editor/res/sandbox_dark.bmp b/Code/Editor/res/sandbox_dark.bmp
deleted file mode 100644
index 2bf31764a6..0000000000
--- a/Code/Editor/res/sandbox_dark.bmp
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:887d184cf49cf78c62a1fe53eac3cb8e7b071bb67e09b801a4893445ac4c800f
-size 542456
diff --git a/Code/Editor/res/sb_welcome_dark.bmp b/Code/Editor/res/sb_welcome_dark.bmp
deleted file mode 100644
index 979c61e4af..0000000000
--- a/Code/Editor/res/sb_welcome_dark.bmp
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:704faeb96d930d3e6992a1449908aa6d7860b648e2feb38da8bf37cd7268a694
-size 184856
diff --git a/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp b/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp
index e654ef4aa3..b505bba5dd 100644
--- a/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp
+++ b/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp
@@ -1155,7 +1155,7 @@ namespace Benchmark
class StorageDriveWindowsFixture : public benchmark::Fixture
{
public:
- constexpr static char* TestFileName = "StreamerBenchmark.bin";
+ constexpr static const char* TestFileName = "StreamerBenchmark.bin";
constexpr static size_t FileSize = 64_mib;
void SetupStreamer(bool enableFileSharing)
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp
index 7625679d69..75346227ed 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp
@@ -454,8 +454,8 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
- InputDeviceKeyboard::InputDeviceKeyboard()
- : InputDevice(Id)
+ InputDeviceKeyboard::InputDeviceKeyboard(AzFramework::InputDeviceId id)
+ : InputDevice(id)
, m_modifierKeyStates(AZStd::make_shared())
, m_allChannelsById()
, m_keyChannelsById()
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h
index ea0aa1beec..9f61ba96f7 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h
@@ -240,7 +240,7 @@ namespace AzFramework
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
- InputDeviceKeyboard();
+ InputDeviceKeyboard(AzFramework::InputDeviceId id = Id);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp
index a7180d7ec1..5cd940e989 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp
@@ -95,8 +95,8 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
- InputDeviceMouse::InputDeviceMouse()
- : InputDevice(Id)
+ InputDeviceMouse::InputDeviceMouse(AzFramework::InputDeviceId id)
+ : InputDevice(id)
, m_allChannelsById()
, m_buttonChannelsById()
, m_movementChannelsById()
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h
index 7fc6a7a505..3feb79ac68 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h
+++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h
@@ -111,7 +111,7 @@ namespace AzFramework
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
- explicit InputDeviceMouse();
+ explicit InputDeviceMouse(AzFramework::InputDeviceId id = Id);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp
index cdcdbe4655..05f41cc8da 100644
--- a/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp
@@ -504,7 +504,7 @@ namespace Physics
}
}
- const AZ::Data::Asset& MaterialSelection::GetMaterialLibrary()
+ AZ::Data::Asset MaterialSelection::GetMaterialLibrary()
{
if (auto* physicsSystem = AZ::Interface::Get())
{
@@ -516,7 +516,7 @@ namespace Physics
return s_invalidMaterialLibrary;
}
- const AZ::Data::AssetId& MaterialSelection::GetMaterialLibraryId()
+ AZ::Data::AssetId MaterialSelection::GetMaterialLibraryId()
{
return GetMaterialLibrary().GetId();
}
diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Material.h b/Code/Framework/AzFramework/AzFramework/Physics/Material.h
index bd15dd338e..455da207d1 100644
--- a/Code/Framework/AzFramework/AzFramework/Physics/Material.h
+++ b/Code/Framework/AzFramework/AzFramework/Physics/Material.h
@@ -306,8 +306,8 @@ namespace Physics
void SyncSelectionToMaterialLibrary();
- static const AZ::Data::Asset& GetMaterialLibrary();
- static const AZ::Data::AssetId& GetMaterialLibraryId();
+ static AZ::Data::Asset GetMaterialLibrary();
+ static AZ::Data::AssetId GetMaterialLibraryId();
bool AreMaterialSlotsReadOnly() const;
diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h
index c7d32739f2..694dcb7b72 100644
--- a/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h
+++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h
@@ -27,7 +27,7 @@ namespace AzFramework
AZStd::string m_ipAddress;
// The port number for the session.
- uint16_t m_port;
+ uint16_t m_port = 0;
};
//! SessionConnectionConfig
@@ -35,7 +35,7 @@ namespace AzFramework
struct PlayerConnectionConfig
{
// A unique identifier for player connection.
- uint32_t m_playerConnectionId;
+ uint32_t m_playerConnectionId = 0;
// A unique identifier for registered player in session.
AZStd::string m_playerSessionId;
diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h
index ae2896c337..a3262d0eb9 100644
--- a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h
+++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h
@@ -37,7 +37,7 @@ namespace AzFramework
AZStd::string m_sessionName;
// The maximum number of players that can be connected simultaneously to the session.
- uint64_t m_maxPlayer;
+ uint64_t m_maxPlayer = 0;
};
//! SearchSessionsRequest
@@ -58,7 +58,7 @@ namespace AzFramework
AZStd::string m_sortExpression;
// The maximum number of results to return.
- uint8_t m_maxResult;
+ uint8_t m_maxResult = 0;
// A token that indicates the start of the next sequential page of results.
AZStd::string m_nextToken;
diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h
index 17f61317e6..5f7f736514 100644
--- a/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h
+++ b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h
@@ -24,10 +24,10 @@ namespace AzFramework
virtual ~SessionConfig() = default;
// A time stamp indicating when this session was created. Format is a number expressed in Unix time as milliseconds.
- uint64_t m_creationTime;
+ uint64_t m_creationTime = 0;
// A time stamp indicating when this data object was terminated. Same format as creation time.
- uint64_t m_terminationTime;
+ uint64_t m_terminationTime = 0;
// A unique identifier for a player or entity creating the session.
AZStd::string m_creatorId;
@@ -48,13 +48,13 @@ namespace AzFramework
AZStd::string m_ipAddress;
// The port number for the session.
- uint16_t m_port;
+ uint16_t m_port = 0;
// The maximum number of players that can be connected simultaneously to the session.
- uint64_t m_maxPlayer;
+ uint64_t m_maxPlayer = 0;
// Number of players currently in the session.
- uint64_t m_currentPlayer;
+ uint64_t m_currentPlayer = 0;
// Current status of the session.
AZStd::string m_status;
diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp
index 3658f9cd22..0516cdd591 100644
--- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp
@@ -747,19 +747,24 @@ namespace AzFramework
return button == inputChannelId;
});
- if (inputChannelId == InputDeviceMouse::Movement::X)
+ // accept active mouse channel updates, inactive movement channels will just have a 0 delta
+ if (inputChannel.IsActive())
{
- return HorizontalMotionEvent{ aznumeric_cast(inputChannel.GetValue()) };
+ if (inputChannelId == InputDeviceMouse::Movement::X)
+ {
+ return HorizontalMotionEvent{ aznumeric_cast(inputChannel.GetValue()) };
+ }
+ else if (inputChannelId == InputDeviceMouse::Movement::Y)
+ {
+ return VerticalMotionEvent{ aznumeric_cast(inputChannel.GetValue()) };
+ }
+ else if (inputChannelId == InputDeviceMouse::Movement::Z)
+ {
+ return ScrollEvent{ inputChannel.GetValue() };
+ }
}
- else if (inputChannelId == InputDeviceMouse::Movement::Y)
- {
- return VerticalMotionEvent{ aznumeric_cast(inputChannel.GetValue()) };
- }
- else if (inputChannelId == InputDeviceMouse::Movement::Z)
- {
- return ScrollEvent{ inputChannel.GetValue() };
- }
- else if (wasMouseButton || InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId))
+
+ if (wasMouseButton || InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId))
{
return DiscreteInputEvent{ inputChannelId, inputChannel.GetState() };
}
diff --git a/Code/Framework/AzFramework/AzFramework/Windowing/NativeWindow.cpp b/Code/Framework/AzFramework/AzFramework/Windowing/NativeWindow.cpp
index b92d8dc3bc..3e7d67888f 100644
--- a/Code/Framework/AzFramework/AzFramework/Windowing/NativeWindow.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Windowing/NativeWindow.cpp
@@ -116,6 +116,11 @@ namespace AzFramework
SetFullScreenState(!GetFullScreenState());
}
+ float NativeWindow::GetDpiScaleFactor() const
+ {
+ return m_pimpl->GetDpiScaleFactor();
+ }
+
/*static*/ bool NativeWindow::GetFullScreenStateOfDefaultWindow()
{
NativeWindowHandle defaultWindowHandle = nullptr;
@@ -228,4 +233,10 @@ namespace AzFramework
return false;
}
+ float NativeWindow::Implementation::GetDpiScaleFactor() const
+ {
+ // For platforms that aren't DPI-aware, we simply return a 1.0 ratio for no scaling
+ return 1.0f;
+ }
+
} // namespace AzFramework
diff --git a/Code/Framework/AzFramework/AzFramework/Windowing/NativeWindow.h b/Code/Framework/AzFramework/AzFramework/Windowing/NativeWindow.h
index 0b53b5b31c..2d5a897146 100644
--- a/Code/Framework/AzFramework/AzFramework/Windowing/NativeWindow.h
+++ b/Code/Framework/AzFramework/AzFramework/Windowing/NativeWindow.h
@@ -128,6 +128,7 @@ namespace AzFramework
void SetFullScreenState(bool fullScreenState) override;
bool CanToggleFullScreenState() const override;
void ToggleFullScreenState() override;
+ float GetDpiScaleFactor() const override;
//! Get the full screen state of the default window.
//! \return True if the default window is currently in full screen, false otherwise.
@@ -169,6 +170,7 @@ namespace AzFramework
virtual bool GetFullScreenState() const;
virtual void SetFullScreenState(bool fullScreenState);
virtual bool CanToggleFullScreenState() const;
+ virtual float GetDpiScaleFactor() const;
protected:
uint32_t m_width = 0;
diff --git a/Code/Framework/AzFramework/AzFramework/Windowing/WindowBus.h b/Code/Framework/AzFramework/AzFramework/Windowing/WindowBus.h
index 959d7cc07b..f21993b80a 100644
--- a/Code/Framework/AzFramework/AzFramework/Windowing/WindowBus.h
+++ b/Code/Framework/AzFramework/AzFramework/Windowing/WindowBus.h
@@ -68,6 +68,11 @@ namespace AzFramework
//! Toggle the full screen state of the window.
virtual void ToggleFullScreenState() = 0;
+
+ //! Returns a scalar multiplier representing how many dots-per-inch this window has, compared
+ //! to a "standard" value of 96, the default for Windows in a DPI unaware setting. This can
+ //! be used to scale user interface elements to ensure legibility on high density displays.
+ virtual float GetDpiScaleFactor() const = 0;
};
using WindowRequestBus = AZ::EBus;
@@ -87,6 +92,9 @@ namespace AzFramework
//! This is called once when the window is Activated and also called if the user resizes the window.
virtual void OnWindowResized(uint32_t width, uint32_t height) { AZ_UNUSED(width); AZ_UNUSED(height); };
+ //! This is called if the window's underyling DPI scaling factor changes.
+ virtual void OnDpiScaleFactorChanged(float dpiScaleFactor) { AZ_UNUSED(dpiScaleFactor); }
+
//! This is called when the window is deactivated from code or if the user closes the window.
virtual void OnWindowClosed() {};
};
diff --git a/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp b/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp
index 44a3757aea..470594853c 100644
--- a/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp
+++ b/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp
@@ -8,6 +8,7 @@
#include
#include
+#include
#include
namespace AzFramework
@@ -17,7 +18,7 @@ namespace AzFramework
{
public:
AZ_CLASS_ALLOCATOR(NativeWindowImpl_Win32, AZ::SystemAllocator, 0);
- NativeWindowImpl_Win32() = default;
+ NativeWindowImpl_Win32();
~NativeWindowImpl_Win32() override;
// NativeWindow::Implementation overrides...
@@ -33,6 +34,7 @@ namespace AzFramework
bool GetFullScreenState() const override;
void SetFullScreenState(bool fullScreenState) override;
bool CanToggleFullScreenState() const override { return true; }
+ float GetDpiScaleFactor() const override;
private:
static DWORD ConvertToWin32WindowStyleMask(const WindowStyleMasks& styleMasks);
@@ -49,6 +51,9 @@ namespace AzFramework
RECT m_windowRectToRestoreOnFullScreenExit; //!< The position and size of the window to restore when exiting full screen.
UINT m_windowStyleToRestoreOnFullScreenExit; //!< The style(s) of the window to restore when exiting full screen.
bool m_isInBorderlessWindowFullScreenState = false; //!< Was a borderless window used to enter full screen state?
+
+ using GetDpiForWindowType = UINT(HWND hwnd);
+ GetDpiForWindowType* m_getDpiFunction = nullptr;
};
const char* NativeWindowImpl_Win32::s_defaultClassName = "O3DEWin32Class";
@@ -58,6 +63,15 @@ namespace AzFramework
return aznew NativeWindowImpl_Win32();
}
+ NativeWindowImpl_Win32::NativeWindowImpl_Win32()
+ {
+ // Attempt to load GetDpiForWindow from user32 at runtime, available on Windows 10+ versions >= 1607
+ if (auto user32module = AZ::DynamicModuleHandle::Create("user32"); user32module->Load(false))
+ {
+ m_getDpiFunction = user32module->GetFunction("GetDpiForWindow");
+ }
+ }
+
NativeWindowImpl_Win32::~NativeWindowImpl_Win32()
{
DestroyWindow(m_win32Handle);
@@ -237,6 +251,12 @@ namespace AzFramework
// Send all other WM_SYSKEYDOWN messages to the default WndProc.
break;
}
+ case WM_DPICHANGED:
+ {
+ const float newScaleFactor = nativeWindowImpl->GetDpiScaleFactor();
+ WindowNotificationBus::Event(nativeWindowImpl->GetWindowHandle(), &WindowNotificationBus::Events::OnDpiScaleFactorChanged, newScaleFactor);
+ break;
+ }
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
@@ -330,6 +350,17 @@ namespace AzFramework
}
}
+ float NativeWindowImpl_Win32::GetDpiScaleFactor() const
+ {
+ constexpr UINT defaultDotsPerInch = 96;
+ UINT dotsPerInch = defaultDotsPerInch;
+ if (m_getDpiFunction)
+ {
+ dotsPerInch = m_getDpiFunction(m_win32Handle);
+ }
+ return aznumeric_cast(dotsPerInch) / aznumeric_cast(defaultDotsPerInch);
+ }
+
void NativeWindowImpl_Win32::EnterBorderlessWindowFullScreen()
{
if (m_isInBorderlessWindowFullScreenState)
diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp
index 4c5e2adabd..7417bed2a0 100644
--- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp
+++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp
@@ -3288,15 +3288,20 @@ namespace AzQtComponents
}
// Untab tabbed dock widgets before restoring, as the restore only works on dock widgets parented directly to the main window
- const QList dockWidgets = m_mainWindow->findChildren();
- for (QDockWidget* dockWidget : dockWidgets)
+ for (QDockWidget* dockWidget : m_mainWindow->findChildren(
+ QRegularExpression(QString("%1.*").arg(m_tabContainerIdentifierPrefix)), Qt::FindChildrenRecursively))
{
- if (QStackedWidget* stackedWidget = qobject_cast(dockWidget->parentWidget()))
+ DockTabWidget* tabWidget = qobject_cast(dockWidget->widget());
+ if (!tabWidget)
{
- if (AzQtComponents::DockTabWidget* tabWidget = qobject_cast(stackedWidget->parentWidget()))
- {
- tabWidget->removeTab(dockWidget);
- }
+ continue;
+ }
+
+ // Remove the tabs from the tab widget (we don't actually want to close them, which could delete them at this point)
+ int numTabs = tabWidget->count();
+ for (int i = 0; i < numTabs; ++i)
+ {
+ tabWidget->removeTab(0);
}
}
diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp
index 3356955ae7..05de144ff5 100644
--- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp
+++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp
@@ -1303,6 +1303,10 @@ namespace AzQtComponents
}
break;
+ case QStyle::SP_MessageBoxInformation:
+ return QIcon(QString::fromUtf8(":/stylesheet/img/UI20/Info.svg"));
+ break;
+
default:
break;
}
diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/BaseStyleSheet.qss b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/BaseStyleSheet.qss
index 894b2921d4..3499404572 100644
--- a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/BaseStyleSheet.qss
+++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/BaseStyleSheet.qss
@@ -123,6 +123,7 @@ QPlainTextEdit:focus
@import "LineEdit.qss";
@import "Menu.qss";
@import "MenuBar.qss";
+@import "MessageBox.qss";
@import "ProgressBar.qss";
@import "PushButton.qss";
@import "QDockWidget.qss";
diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/MessageBox.qss b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/MessageBox.qss
new file mode 100644
index 0000000000..81aa7fd8d8
--- /dev/null
+++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/Widgets/MessageBox.qss
@@ -0,0 +1,21 @@
+
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+ /* correct the padding around the two main labels to give space at the borders */
+QMessageBox QLabel#qt_msgbox_label
+{
+ padding-top: 20px;
+ padding-right: 20px;
+ padding-bottom: 20px;
+}
+
+QMessageBox QLabel#qt_msgboxex_icon_label
+{
+ padding-left: 20px;
+ padding-top: 20px;
+}
diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc b/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc
index 20a25f8fcb..909510d63b 100644
--- a/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc
+++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/resources.qrc
@@ -354,17 +354,17 @@
img/UI20/toolbar/Grid.svg
img/UI20/toolbar/Lighting.svg
img/UI20/toolbar/Load.svg
- img/UI20/toolbar/Local.svg
+ img/UI20/toolbar/Local.svg
img/UI20/toolbar/Locked.svg
img/UI20/toolbar/Locked_Status.svg
- img/UI20/toolbar/LUA.svg
+ img/UI20/toolbar/LUA.svg
img/UI20/toolbar/Material.svg
img/UI20/toolbar/Measure.svg
img/UI20/toolbar/Move.svg
img/UI20/toolbar/Object_follow_terrain.svg
img/UI20/toolbar/Object_height.svg
img/UI20/toolbar/Object_list.svg
- img/UI20/toolbar/Parent.svg
+ img/UI20/toolbar/Parent.svg
img/UI20/toolbar/particle.svg
img/UI20/toolbar/Play.svg
img/UI20/toolbar/Redo.svg
@@ -385,7 +385,7 @@
img/UI20/toolbar/undo.svg
img/UI20/toolbar/Unlocked.svg
img/UI20/toolbar/Vertex_snapping.svg
- img/UI20/toolbar/World.svg
+ img/UI20/toolbar/World.svg
img/UI20/toolbar/X_axis.svg
img/UI20/toolbar/Y_axis.svg
img/UI20/toolbar/Z_axis.svg
@@ -459,6 +459,7 @@
Widgets/ComboBoxConfig.ini
Widgets/Menu.qss
Widgets/MenuBar.qss
+ Widgets/MessageBox.qss
Widgets/ProgressBarConfig.ini
Widgets/ProgressBar.qss
Widgets/PushButton.qss
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp
index 9db1e2e170..f89bd285de 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp
@@ -222,6 +222,21 @@ namespace AzToolsFramework
void AssetBrowserTreeView::UpdateAfterFilter(bool hasFilter, bool selectFirstValidEntry)
{
+ const QModelIndexList& selectedIndexes = selectionModel()->selectedRows();
+
+ // If we've cleared the filter but had something selected, ensure it stays selected and visible.
+ if (!hasFilter && !selectedIndexes.isEmpty())
+ {
+ QModelIndex curIndex = selectedIndexes[0];
+ m_expandToEntriesByDefault = true;
+ m_treeStateSaver->ApplySnapshot();
+
+ setCurrentIndex(curIndex);
+ scrollTo(curIndex);
+
+ return;
+ }
+
// Flag our default expansion state so that we expand down to source entries after filtering
m_expandToEntriesByDefault = hasFilter;
// Then ask our state saver to apply its current snapshot again, falling back on asking us if entries should be expanded or not
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp
new file mode 100644
index 0000000000..eb5034fdd5
--- /dev/null
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.cpp
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace AzToolsFramework
+{
+ void QtEventToAzInputMapper::InitializeKeyMappings()
+ {
+ // This assumes modifier keys (ctrl/shift/alt) map to the left control/shift/alt keys as Qt provides no way to disambiguate
+ // in a platform agnostic manner. This could be expanded later with a PAL mapping from native scan codes acquired from
+ // QKeyEvents, if needed.
+ m_keyMappings = { {
+ { Qt::Key_0, AzFramework::InputDeviceKeyboard::Key::Alphanumeric0 },
+ { Qt::Key_1, AzFramework::InputDeviceKeyboard::Key::Alphanumeric1 },
+ { Qt::Key_2, AzFramework::InputDeviceKeyboard::Key::Alphanumeric2 },
+ { Qt::Key_3, AzFramework::InputDeviceKeyboard::Key::Alphanumeric3 },
+ { Qt::Key_4, AzFramework::InputDeviceKeyboard::Key::Alphanumeric4 },
+ { Qt::Key_5, AzFramework::InputDeviceKeyboard::Key::Alphanumeric5 },
+ { Qt::Key_6, AzFramework::InputDeviceKeyboard::Key::Alphanumeric6 },
+ { Qt::Key_7, AzFramework::InputDeviceKeyboard::Key::Alphanumeric7 },
+ { Qt::Key_8, AzFramework::InputDeviceKeyboard::Key::Alphanumeric8 },
+ { Qt::Key_9, AzFramework::InputDeviceKeyboard::Key::Alphanumeric9 },
+ { Qt::Key_A, AzFramework::InputDeviceKeyboard::Key::AlphanumericA },
+ { Qt::Key_B, AzFramework::InputDeviceKeyboard::Key::AlphanumericB },
+ { Qt::Key_C, AzFramework::InputDeviceKeyboard::Key::AlphanumericC },
+ { Qt::Key_D, AzFramework::InputDeviceKeyboard::Key::AlphanumericD },
+ { Qt::Key_E, AzFramework::InputDeviceKeyboard::Key::AlphanumericE },
+ { Qt::Key_F, AzFramework::InputDeviceKeyboard::Key::AlphanumericF },
+ { Qt::Key_G, AzFramework::InputDeviceKeyboard::Key::AlphanumericG },
+ { Qt::Key_H, AzFramework::InputDeviceKeyboard::Key::AlphanumericH },
+ { Qt::Key_I, AzFramework::InputDeviceKeyboard::Key::AlphanumericI },
+ { Qt::Key_J, AzFramework::InputDeviceKeyboard::Key::AlphanumericJ },
+ { Qt::Key_K, AzFramework::InputDeviceKeyboard::Key::AlphanumericK },
+ { Qt::Key_L, AzFramework::InputDeviceKeyboard::Key::AlphanumericL },
+ { Qt::Key_M, AzFramework::InputDeviceKeyboard::Key::AlphanumericM },
+ { Qt::Key_N, AzFramework::InputDeviceKeyboard::Key::AlphanumericN },
+ { Qt::Key_O, AzFramework::InputDeviceKeyboard::Key::AlphanumericO },
+ { Qt::Key_P, AzFramework::InputDeviceKeyboard::Key::AlphanumericP },
+ { Qt::Key_Q, AzFramework::InputDeviceKeyboard::Key::AlphanumericQ },
+ { Qt::Key_R, AzFramework::InputDeviceKeyboard::Key::AlphanumericR },
+ { Qt::Key_S, AzFramework::InputDeviceKeyboard::Key::AlphanumericS },
+ { Qt::Key_T, AzFramework::InputDeviceKeyboard::Key::AlphanumericT },
+ { Qt::Key_U, AzFramework::InputDeviceKeyboard::Key::AlphanumericU },
+ { Qt::Key_V, AzFramework::InputDeviceKeyboard::Key::AlphanumericV },
+ { Qt::Key_W, AzFramework::InputDeviceKeyboard::Key::AlphanumericW },
+ { Qt::Key_X, AzFramework::InputDeviceKeyboard::Key::AlphanumericX },
+ { Qt::Key_Y, AzFramework::InputDeviceKeyboard::Key::AlphanumericY },
+ { Qt::Key_Z, AzFramework::InputDeviceKeyboard::Key::AlphanumericZ },
+ { Qt::Key_Backspace, AzFramework::InputDeviceKeyboard::Key::EditBackspace },
+ { Qt::Key_CapsLock, AzFramework::InputDeviceKeyboard::Key::EditCapsLock },
+ { Qt::Key_Enter, AzFramework::InputDeviceKeyboard::Key::EditEnter },
+ { Qt::Key_Space, AzFramework::InputDeviceKeyboard::Key::EditSpace },
+ { Qt::Key_Tab, AzFramework::InputDeviceKeyboard::Key::EditTab },
+ { Qt::Key_Escape, AzFramework::InputDeviceKeyboard::Key::Escape },
+ { Qt::Key_F1, AzFramework::InputDeviceKeyboard::Key::Function01 },
+ { Qt::Key_F2, AzFramework::InputDeviceKeyboard::Key::Function02 },
+ { Qt::Key_F3, AzFramework::InputDeviceKeyboard::Key::Function03 },
+ { Qt::Key_F4, AzFramework::InputDeviceKeyboard::Key::Function04 },
+ { Qt::Key_F5, AzFramework::InputDeviceKeyboard::Key::Function05 },
+ { Qt::Key_F6, AzFramework::InputDeviceKeyboard::Key::Function06 },
+ { Qt::Key_F7, AzFramework::InputDeviceKeyboard::Key::Function07 },
+ { Qt::Key_F8, AzFramework::InputDeviceKeyboard::Key::Function08 },
+ { Qt::Key_F9, AzFramework::InputDeviceKeyboard::Key::Function09 },
+ { Qt::Key_F10, AzFramework::InputDeviceKeyboard::Key::Function10 },
+ { Qt::Key_F11, AzFramework::InputDeviceKeyboard::Key::Function11 },
+ { Qt::Key_F12, AzFramework::InputDeviceKeyboard::Key::Function12 },
+ { Qt::Key_F13, AzFramework::InputDeviceKeyboard::Key::Function13 },
+ { Qt::Key_F14, AzFramework::InputDeviceKeyboard::Key::Function14 },
+ { Qt::Key_F15, AzFramework::InputDeviceKeyboard::Key::Function15 },
+ { Qt::Key_F16, AzFramework::InputDeviceKeyboard::Key::Function16 },
+ { Qt::Key_F17, AzFramework::InputDeviceKeyboard::Key::Function17 },
+ { Qt::Key_F18, AzFramework::InputDeviceKeyboard::Key::Function18 },
+ { Qt::Key_F19, AzFramework::InputDeviceKeyboard::Key::Function19 },
+ { Qt::Key_F20, AzFramework::InputDeviceKeyboard::Key::Function20 },
+ { Qt::Key_Alt, AzFramework::InputDeviceKeyboard::Key::ModifierAltL },
+ { Qt::Key_Control, AzFramework::InputDeviceKeyboard::Key::ModifierCtrlL },
+ { Qt::Key_Shift, AzFramework::InputDeviceKeyboard::Key::ModifierShiftL },
+ { Qt::Key_Super_L, AzFramework::InputDeviceKeyboard::Key::ModifierSuperL },
+ { Qt::Key_Super_R, AzFramework::InputDeviceKeyboard::Key::ModifierSuperR },
+ { Qt::Key_Down, AzFramework::InputDeviceKeyboard::Key::NavigationArrowDown },
+ { Qt::Key_Left, AzFramework::InputDeviceKeyboard::Key::NavigationArrowLeft },
+ { Qt::Key_Right, AzFramework::InputDeviceKeyboard::Key::NavigationArrowRight },
+ { Qt::Key_Up, AzFramework::InputDeviceKeyboard::Key::NavigationArrowUp },
+ { Qt::Key_Delete, AzFramework::InputDeviceKeyboard::Key::NavigationDelete },
+ { Qt::Key_End, AzFramework::InputDeviceKeyboard::Key::NavigationEnd },
+ { Qt::Key_Home, AzFramework::InputDeviceKeyboard::Key::NavigationHome },
+ { Qt::Key_Insert, AzFramework::InputDeviceKeyboard::Key::NavigationInsert },
+ { Qt::Key_PageDown, AzFramework::InputDeviceKeyboard::Key::NavigationPageDown },
+ { Qt::Key_PageUp, AzFramework::InputDeviceKeyboard::Key::NavigationPageUp },
+ { Qt::Key_Apostrophe, AzFramework::InputDeviceKeyboard::Key::PunctuationApostrophe },
+ { Qt::Key_Backslash, AzFramework::InputDeviceKeyboard::Key::PunctuationBackslash },
+ { Qt::Key_BracketLeft, AzFramework::InputDeviceKeyboard::Key::PunctuationBracketL },
+ { Qt::Key_BracketRight, AzFramework::InputDeviceKeyboard::Key::PunctuationBracketR },
+ { Qt::Key_Comma, AzFramework::InputDeviceKeyboard::Key::PunctuationComma },
+ { Qt::Key_Equal, AzFramework::InputDeviceKeyboard::Key::PunctuationEquals },
+ { Qt::Key_hyphen, AzFramework::InputDeviceKeyboard::Key::PunctuationHyphen },
+ { Qt::Key_Period, AzFramework::InputDeviceKeyboard::Key::PunctuationPeriod },
+ { Qt::Key_Semicolon, AzFramework::InputDeviceKeyboard::Key::PunctuationSemicolon },
+ { Qt::Key_Slash, AzFramework::InputDeviceKeyboard::Key::PunctuationSlash },
+ { Qt::Key_QuoteLeft, AzFramework::InputDeviceKeyboard::Key::PunctuationTilde },
+ { Qt::Key_Pause, AzFramework::InputDeviceKeyboard::Key::WindowsSystemPause },
+ { Qt::Key_Print, AzFramework::InputDeviceKeyboard::Key::WindowsSystemPrint },
+ { Qt::Key_ScrollLock, AzFramework::InputDeviceKeyboard::Key::WindowsSystemScrollLock },
+ } };
+ }
+
+ void QtEventToAzInputMapper::InitializeMouseButtonMappings()
+ {
+ m_mouseButtonMappings = { {
+ { Qt::MouseButton::LeftButton, AzFramework::InputDeviceMouse::Button::Left },
+ { Qt::MouseButton::RightButton, AzFramework::InputDeviceMouse::Button::Right },
+ { Qt::MouseButton::MiddleButton, AzFramework::InputDeviceMouse::Button::Middle },
+ { Qt::MouseButton::ExtraButton1, AzFramework::InputDeviceMouse::Button::Other1 },
+ { Qt::MouseButton::ExtraButton2, AzFramework::InputDeviceMouse::Button::Other2 },
+ } };
+ }
+
+ // Currently this is only set for modifier keys.
+ // This should only be expanded sparingly, any keys handled here will not be bubbled up to the shortcut system.
+ // ex: If Key_S was here, the viewport would consume S key presses before the application could process a QAction with a Ctrl+S
+ // shortcut.
+ void QtEventToAzInputMapper::InitializeHighPriorityKeys()
+ {
+ m_highPriorityKeys = { Qt::Key_Alt, Qt::Key_Control, Qt::Key_Shift, Qt::Key_Super_L, Qt::Key_Super_R };
+ }
+
+ QtEventToAzInputMapper::EditorQtKeyboardDevice::EditorQtKeyboardDevice(AzFramework::InputDeviceId id)
+ : AzFramework::InputDeviceKeyboard(id)
+ {
+ // Disable all platform native processing in favor of our Qt event handling
+ SetImplementation(nullptr);
+ }
+
+ QtEventToAzInputMapper::EditorQtMouseDevice::EditorQtMouseDevice(AzFramework::InputDeviceId id)
+ : AzFramework::InputDeviceMouse(id)
+ {
+ // Disable all platform native processing in favor of our Qt event handling
+ SetImplementation(nullptr);
+ }
+
+ QtEventToAzInputMapper::QtEventToAzInputMapper(QWidget* sourceWidget, int syntheticDeviceId)
+ : QObject(sourceWidget)
+ , m_sourceWidget(sourceWidget)
+ , m_keyboardModifiers(AZStd::make_shared())
+ , m_cursorPosition(AZStd::make_shared())
+ {
+ InitializeKeyMappings();
+ InitializeMouseButtonMappings();
+ InitializeHighPriorityKeys();
+
+ // Add an arbitrary offset to our device index to avoid collision with real physical device index.
+ // We still have to use the keyboard and mouse device channel names because input channels are only addressed
+ // by their own name and their device index, so overlapping input channels between devices would conflict.
+ constexpr AZ::u32 syntheticDeviceOffset = 1000;
+ const AzFramework::InputDeviceId keyboardDeviceId(
+ AzFramework::InputDeviceKeyboard::Id.GetName(), syntheticDeviceId + syntheticDeviceOffset);
+ const AzFramework::InputDeviceId mouseDeviceId(
+ AzFramework::InputDeviceMouse::Id.GetName(), syntheticDeviceId + syntheticDeviceOffset);
+
+ m_keyboardDevice = AZStd::make_unique(keyboardDeviceId);
+ m_mouseDevice = AZStd::make_unique(mouseDeviceId);
+
+ AddChannels(m_keyboardDevice->m_allChannelsById);
+ AddChannels(m_mouseDevice->m_allChannelsById);
+
+ // Install a global event filter to ensure we don't miss mouse and key release events.
+ QApplication::instance()->installEventFilter(this);
+ }
+
+ bool QtEventToAzInputMapper::HandlesInputEvent(const AzFramework::InputChannel& channel) const
+ {
+ const AzFramework::InputChannelId& channelId = channel.GetInputChannelId();
+ if (channelId == AzFramework::InputDeviceMouse::Movement::X || channelId == AzFramework::InputDeviceMouse::Movement::Y)
+ {
+ return false;
+ }
+
+ // We map keyboard and mouse events from Qt, so flag all events coming from those devices
+ // as handled by our synthetic event system.
+ const AzFramework::InputDeviceId& deviceId = channel.GetInputDevice().GetInputDeviceId();
+ return deviceId.GetNameCrc32() == AzFramework::InputDeviceMouse::Id.GetNameCrc32() ||
+ deviceId.GetNameCrc32() == AzFramework::InputDeviceKeyboard::Id.GetNameCrc32();
+ }
+
+ void QtEventToAzInputMapper::SetEnabled(bool enabled)
+ {
+ m_enabled = enabled;
+ if (!enabled)
+ {
+ // Send an internal focus change event to reset our input state to fresh if we're disabled.
+ HandleFocusChange(nullptr);
+ }
+ }
+
+ bool QtEventToAzInputMapper::eventFilter(QObject* object, QEvent* event)
+ {
+ // Abort if processing isn't enabled.
+ if (!m_enabled)
+ {
+ return false;
+ }
+
+ // Because there's no "end" to mouse movement and wheel events, we reset mouse movement channels that have been opened
+ // during the next processed non-mouse event.
+ if (m_mouseChannelsNeedUpdate && event->type() != QEvent::Type::MouseMove && event->type() != QEvent::Type::Wheel)
+ {
+ m_cursorPosition->m_normalizedPositionDelta = AZ::Vector2::CreateZero();
+ ProcessPendingMouseEvents();
+ m_mouseChannelsNeedUpdate = false;
+ }
+
+ // Only accept mouse & key release events that originate from an object that is not our target widget,
+ // as we don't want to erroneously intercept user input meant for another component.
+ if (object != m_sourceWidget && event->type() != QEvent::Type::KeyRelease && event->type() != QEvent::Type::MouseButtonRelease)
+ {
+ return false;
+ }
+
+ // If our focus changes, go ahead and reset all input devices.
+ if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut)
+ {
+ HandleFocusChange(event);
+ }
+ // Map key events to input channels.
+ // ShortcutOverride is used in lieu of KeyPress for high priority input channels like Alt
+ // that need to be accepted and stopped before they bubble up and cause unintended behavior.
+ else if (
+ event->type() == QEvent::Type::KeyPress || event->type() == QEvent::Type::KeyRelease ||
+ event->type() == QEvent::Type::ShortcutOverride)
+ {
+ QKeyEvent* keyEvent = static_cast(event);
+ HandleKeyEvent(keyEvent);
+ }
+ // Map mouse events to input channels.
+ else if (event->type() == QEvent::Type::MouseButtonPress || event->type() == QEvent::Type::MouseButtonRelease || event->type() == QEvent::Type::MouseButtonDblClick)
+ {
+ QMouseEvent* mouseEvent = static_cast(event);
+ HandleMouseButtonEvent(mouseEvent);
+ }
+ // Map mouse movement to the movement input channels.
+ // This includes SystemCursorPosition alongside Movement::X and Movement::Y.
+ else if (event->type() == QEvent::Type::MouseMove)
+ {
+ QMouseEvent* mouseEvent = static_cast(event);
+ HandleMouseMoveEvent(mouseEvent);
+ }
+ // Map wheel events to the mouse Z movement channel.
+ else if (event->type() == QEvent::Type::Wheel)
+ {
+ QWheelEvent* wheelEvent = static_cast(event);
+ HandleWheelEvent(wheelEvent);
+ }
+
+ return false;
+ }
+
+ void QtEventToAzInputMapper::NotifyUpdateChannelIfNotIdle(const AzFramework::InputChannel* channel, QEvent* event)
+ {
+ if (channel->GetState() != AzFramework::InputChannel::State::Idle)
+ {
+ emit InputChannelUpdated(channel, event);
+ }
+ }
+
+ void QtEventToAzInputMapper::ProcessPendingMouseEvents()
+ {
+ auto systemCursorChannel =
+ GetInputChannel(AzFramework::InputDeviceMouse::SystemCursorPosition);
+ auto mouseWheelChannel =
+ GetInputChannel(AzFramework::InputDeviceMouse::Movement::Z);
+
+ systemCursorChannel->ProcessRawInputEvent(m_cursorPosition->m_normalizedPositionDelta.GetLength());
+ mouseWheelChannel->ProcessRawInputEvent(0.f);
+
+ NotifyUpdateChannelIfNotIdle(systemCursorChannel, nullptr);
+ NotifyUpdateChannelIfNotIdle(mouseWheelChannel, nullptr);
+ }
+
+ void QtEventToAzInputMapper::HandleMouseButtonEvent(QMouseEvent* mouseEvent)
+ {
+ const Qt::MouseButton button = mouseEvent->button();
+
+ if (auto buttonIt = m_mouseButtonMappings.find(button); buttonIt != m_mouseButtonMappings.end())
+ {
+ auto buttonChannel = GetInputChannel(buttonIt->second);
+
+ if (buttonChannel)
+ {
+ if (mouseEvent->type() != QEvent::Type::MouseButtonRelease)
+ {
+ buttonChannel->UpdateState(true);
+ }
+ else
+ {
+ buttonChannel->UpdateState(false);
+ }
+
+ NotifyUpdateChannelIfNotIdle(buttonChannel, mouseEvent);
+ }
+ }
+ }
+
+ void QtEventToAzInputMapper::HandleMouseMoveEvent(QMouseEvent* mouseEvent)
+ {
+ const QPoint mousePos = mouseEvent->pos();
+ const float normalizedX = aznumeric_cast(mousePos.x()) / aznumeric_cast(m_sourceWidget->width());
+ const float normalizedY = aznumeric_cast(mousePos.y()) / aznumeric_cast(m_sourceWidget->height());
+ const AZ::Vector2 normalizedPosition(normalizedX, normalizedY);
+ m_cursorPosition->m_normalizedPositionDelta = normalizedPosition - m_cursorPosition->m_normalizedPosition;
+ m_cursorPosition->m_normalizedPosition = normalizedPosition;
+ ProcessPendingMouseEvents();
+ m_mouseChannelsNeedUpdate = true;
+ }
+
+ void QtEventToAzInputMapper::HandleKeyEvent(QKeyEvent* keyEvent)
+ {
+ // Ignore key repeat events, they're unrelated to actual physical button presses.
+ if (keyEvent->isAutoRepeat())
+ {
+ return;
+ }
+
+ const Qt::Key key = static_cast(keyEvent->key());
+
+ // For ShortcutEvent, only continue processing if we're in the HighPriorityKeys set.
+ if (keyEvent->type() != QEvent::Type::ShortcutOverride || m_highPriorityKeys.find(key) != m_highPriorityKeys.end())
+ {
+ if (auto keyIt = m_keyMappings.find(key); keyIt != m_keyMappings.end())
+ {
+ auto keyChannel = GetInputChannel(keyIt->second);
+
+ if (keyChannel)
+ {
+ if (keyEvent->type() == QEvent::Type::KeyPress || keyEvent->type() == QEvent::Type::ShortcutOverride)
+ {
+ keyChannel->UpdateState(true);
+ }
+ else
+ {
+ keyChannel->UpdateState(false);
+ }
+
+ NotifyUpdateChannelIfNotIdle(keyChannel, keyEvent);
+ }
+ }
+ }
+ }
+
+ void QtEventToAzInputMapper::HandleWheelEvent(QWheelEvent* wheelEvent)
+ {
+ auto cursorZChannel =
+ GetInputChannel(AzFramework::InputDeviceMouse::Movement::Z);
+ const QPoint angleDelta = wheelEvent->angleDelta();
+ // Check both angles, as the alt modifier can change the wheel direction.
+ int wheelAngle = angleDelta.x();
+ if (wheelAngle == 0)
+ {
+ wheelAngle = angleDelta.y();
+ }
+ cursorZChannel->ProcessRawInputEvent(aznumeric_cast(wheelAngle));
+ NotifyUpdateChannelIfNotIdle(cursorZChannel, wheelEvent);
+ m_mouseChannelsNeedUpdate = true;
+ }
+
+ void QtEventToAzInputMapper::HandleFocusChange(QEvent* event)
+ {
+ for (auto& channelData : m_channels)
+ {
+ // If resetting the input device changed the channel state, submit it to the mapped channel list
+ // for processing.
+ if (channelData.second->IsActive())
+ {
+ channelData.second->UpdateState(false);
+ NotifyUpdateChannelIfNotIdle(channelData.second, event);
+ }
+ }
+ m_mouseChannelsNeedUpdate = false;
+ }
+} // namespace AzToolsFramework
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h
new file mode 100644
index 0000000000..6d95f3a4b0
--- /dev/null
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Input/QtEventToAzInputManager.h
@@ -0,0 +1,152 @@
+/*
+ * 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
+
+#if !defined(Q_MOC_RUN)
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#endif //! defined(Q_MOC_RUN)
+
+class QWidget;
+class QKeyEvent;
+class QMouseEvent;
+class QWheelEvent;
+
+namespace AzToolsFramework
+{
+ //! Maps events from the Qt input system to synthetic InputChannels in AzFramework
+ //! that can be used by AzFramework::ViewportControllers.
+ class QtEventToAzInputMapper final : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ QtEventToAzInputMapper(QWidget* sourceWidget, int syntheticDeviceId = 0);
+ ~QtEventToAzInputMapper() = default;
+
+ //! Queries whether a given input channel has a synthetic equivalent mapped
+ //! by this system.
+ //! \returns true if the channel is handled by MapQtEventToAzInput.
+ bool HandlesInputEvent(const AzFramework::InputChannel& channel) const;
+
+ //! Sets whether or not this input mapper should be updating its input channels from Qt events.
+ void SetEnabled(bool enabled);
+
+ // QObject overrides...
+ bool eventFilter(QObject* object, QEvent* event) override;
+
+ signals:
+ //! This signal fires whenever the state of the specified input channel changes.
+ //! This is determined by Qt events dispatched to the source widget.
+ //! \param channel The AZ input channel that has been updated.
+ //! \param event The underlying Qt event that triggered this change, if applicable.
+ void InputChannelUpdated(const AzFramework::InputChannel* channel, QEvent* event);
+
+ private:
+ // Gets an input channel of the specified type by ID.
+ template
+ TInputChannel* GetInputChannel(const AzFramework::InputChannelId& id)
+ {
+ auto channelIt = m_channels.find(id);
+ if (channelIt != m_channels.end())
+ {
+ return static_cast(channelIt->second);
+ }
+ return nullptr;
+ }
+
+ // Adds channels from the specified channel container to our input channel ID -> input channel lookup table.
+ // Used for rapid lookup.
+ template
+ void AddChannels(const TContainer& container)
+ {
+ for (const auto& channelData : container)
+ {
+ // Break const as we're taking these input channels from devices we own.
+ m_channels.emplace(channelData.first, const_cast(channelData.second));
+ }
+ }
+
+ // Our synthetic Keyboard device, does no internal keyboard handling and instead listens to this class for updates.
+ class EditorQtKeyboardDevice : public AzFramework::InputDeviceKeyboard
+ {
+ public:
+ EditorQtKeyboardDevice(AzFramework::InputDeviceId id);
+
+ friend class QtEventToAzInputMapper;
+ };
+
+ // Our synthetic Mouse device, does no internal keyboard handling and instead listens to this class for updates.
+ class EditorQtMouseDevice : public AzFramework::InputDeviceMouse
+ {
+ public:
+ EditorQtMouseDevice(AzFramework::InputDeviceId id);
+
+ friend class QtEventToAzInputMapper;
+ };
+
+ // Emits InputChannelUpdated if channel has transitioned in state (i.e. has gone from active to inactive or vice versa).
+ void NotifyUpdateChannelIfNotIdle(const AzFramework::InputChannel* channel, QEvent* event);
+
+ // Processes any pending mouse movement events, this allows mouse movement channels to close themselves.
+ void ProcessPendingMouseEvents();
+
+ // Handle mouse click events.
+ void HandleMouseButtonEvent(QMouseEvent* mouseEvent);
+ // Handle mouse move events.
+ void HandleMouseMoveEvent(QMouseEvent* mouseEvent);
+ // Handles key press / release events (or ShortcutOverride events for keys listed in m_highPriorityKeys).
+ void HandleKeyEvent(QKeyEvent* keyEvent);
+ // Handles mouse wheel events.
+ void HandleWheelEvent(QWheelEvent* wheelEvent);
+ // Handles focus change events.
+ void HandleFocusChange(QEvent* event);
+
+ // Populates m_keyMappings.
+ void InitializeKeyMappings();
+ // Populates m_mouseButtonMappings.
+ void InitializeMouseButtonMappings();
+ // Populates m_highPriorityKeys.
+ void InitializeHighPriorityKeys();
+
+ // The current keyboard modifier state used by our synthetic key input channels.
+ AZStd::shared_ptr m_keyboardModifiers;
+ // The current normalized cursor position used by our synthetic system cursor event.
+ AZStd::shared_ptr m_cursorPosition;
+ // A lookup table for Qt key -> AZ input channel.
+ AZStd::unordered_map m_keyMappings;
+ // A lookup table for Qt mouse button -> AZ input channel.
+ AZStd::unordered_map m_mouseButtonMappings;
+ // A set of high priority keys that need to be processed at the ShortcutOverride level instead of the
+ // KeyEvent level. This prevents e.g. the main menu bar from processing a press of the "alt" key when the
+ // viewport consumes the event.
+ AZStd::unordered_set m_highPriorityKeys;
+ // A lookup table for AZ input channel ID -> physical input channel on our mouse or keyboard device.
+ AZStd::unordered_map m_channels;
+ // The source widget to map events from, used to calculate the relative mouse position within the widget bounds.
+ QWidget* m_sourceWidget;
+ // Flags when mouse movement channels have been opened and may need to be closed (as there are no movement ended events).
+ bool m_mouseChannelsNeedUpdate = false;
+ // Flags whether or not Qt events should currently be processed.
+ bool m_enabled = true;
+
+ // Our viewport-specific AZ devices. We control their internal input channel states.
+ AZStd::unique_ptr m_mouseDevice;
+ AZStd::unique_ptr m_keyboardDevice;
+ };
+} // namespace AzToolsFramework
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.cpp
index ccc7ef1fd7..f342b1afab 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.cpp
@@ -151,6 +151,12 @@ namespace LegacyFramework
specializations.Append("tools");
}
+ void Application::CreateReflectionManager()
+ {
+ AZ::ComponentApplication::CreateReflectionManager();
+ GetSerializeContext()->CreateEditContext();
+ }
+
int Application::Run(const ApplicationDesc& desc)
{
if (!AZ::AllocatorInstance::IsReady())
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.h
index 42aacf3dbd..5cde088c2d 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.h
@@ -56,6 +56,8 @@ namespace LegacyFramework
virtual int Run(const ApplicationDesc& desc);
Application();
+ void CreateReflectionManager() override;
+
protected:
// ------------------------------------------------------------------
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake
index b5b2c028a5..7fbde67ce4 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake
@@ -724,6 +724,8 @@ set(FILES
PythonTerminal/ScriptTermDialog.cpp
PythonTerminal/ScriptTermDialog.h
PythonTerminal/ScriptTermDialog.ui
+ Input/QtEventToAzInputManager.h
+ Input/QtEventToAzInputManager.cpp
)
# Prevent the following files from being grouped in UNITY builds
diff --git a/Code/LauncherUnified/Platform/iOS/Launcher_iOS.mm b/Code/LauncherUnified/Platform/iOS/Launcher_iOS.mm
index 480a1b0be1..b5401b8d80 100644
--- a/Code/LauncherUnified/Platform/iOS/Launcher_iOS.mm
+++ b/Code/LauncherUnified/Platform/iOS/Launcher_iOS.mm
@@ -6,7 +6,7 @@
*/
#import
-
+#include
int main(int argc, char* argv[])
{
diff --git a/Code/LauncherUnified/launcher_generator.cmake b/Code/LauncherUnified/launcher_generator.cmake
index 85976e1cb9..628b5fffac 100644
--- a/Code/LauncherUnified/launcher_generator.cmake
+++ b/Code/LauncherUnified/launcher_generator.cmake
@@ -122,6 +122,8 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC
FOLDER ${project_name}
)
+ # After ensuring that we correctly support DPI scaling, this should be switched to "PerMonitor"
+ set_property(TARGET ${project_name}.GameLauncher APPEND PROPERTY VS_DPI_AWARE "OFF")
if(LY_DEFAULT_PROJECT_PATH)
set_property(TARGET ${project_name}.GameLauncher APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\"")
endif()
diff --git a/Code/Legacy/CrySystem/SystemInit.cpp b/Code/Legacy/CrySystem/SystemInit.cpp
index 7602ed05aa..3c1f1b4cda 100644
--- a/Code/Legacy/CrySystem/SystemInit.cpp
+++ b/Code/Legacy/CrySystem/SystemInit.cpp
@@ -1043,7 +1043,7 @@ string GetUniqueLogFileName(string logFileName)
logFileNamePrefix = logFileName.substr(0, extensionIndex);
}
- logFileName.Format("%s(%d)%s", logFileNamePrefix.c_str(), instance, logFileExtension.c_str());
+ logFileName = AZStd::string::format("%s(%d)%s", logFileNamePrefix.c_str(), instance, logFileExtension.c_str()).c_str();
return logFileName;
}
diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp
index 9959ccf5cd..28f8d9bb4e 100644
--- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp
+++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp
@@ -346,7 +346,8 @@ namespace TestImpact
return orphans;
}
- ChangeDependencyList DynamicDependencyMap::ApplyAndResoveChangeList(const ChangeList& changeList)
+ ChangeDependencyList DynamicDependencyMap::ApplyAndResoveChangeList(
+ const ChangeList& changeList, Policy::IntegrityFailure integrityFailurePolicy)
{
AZStd::vector createDependencies;
AZStd::vector updateDependencies;
@@ -364,11 +365,15 @@ namespace TestImpact
{
if (sourceDependency->GetNumCoveringTestTargets())
{
- const AZStd::string msg = AZStd::string::format("The newly-created file %s belongs to a build target yet "
+ const AZStd::string msg = AZStd::string::format("The newly-created file '%s' belongs to a build target yet "
"still has coverage data in the source covering test list implying that a delete CRUD operation has been "
- "missed, thus the integrity of the source covering test list has been compromised", createdFile.c_str());
+ "missed, thus the integrity of the source covering test list has been compromised.", createdFile.c_str());
AZ_Error("File Creation", false, msg.c_str());
- throw DependencyException(msg);
+
+ if (integrityFailurePolicy == Policy::IntegrityFailure::Abort)
+ {
+ throw DependencyException(msg);
+ }
}
if (sourceDependency->GetNumParentTargets())
@@ -418,18 +423,26 @@ namespace TestImpact
{
if (sourceDependency->GetNumCoveringTestTargets())
{
- const AZStd::string msg = AZStd::string::format("The deleted file %s still belongs to a build target and still "
+ const AZStd::string msg = AZStd::string::format("The deleted file '%s' still belongs to a build target and still "
"has coverage data in the source covering test list, implying that the integrity of both the source to target "
- "mappings and the source covering test list has been compromised", deletedFile.c_str());
+ "mappings and the source covering test list has been compromised.", deletedFile.c_str());
AZ_Error("File Delete", false, msg.c_str());
- throw DependencyException(msg);
+
+ if (integrityFailurePolicy == Policy::IntegrityFailure::Abort)
+ {
+ throw DependencyException(msg);
+ }
}
else
{
- const AZStd::string msg = AZStd::string::format("The deleted file %s still belongs to a build target implying "
- "that the integrity of the source to target mappings has been compromised", deletedFile.c_str());
+ const AZStd::string msg = AZStd::string::format("The deleted file '%s' still belongs to a build target implying "
+ "that the integrity of the source to target mappings has been compromised.", deletedFile.c_str());
AZ_Error("File Delete", false, msg.c_str());
- throw DependencyException(msg);
+
+ if (integrityFailurePolicy == Policy::IntegrityFailure::Abort)
+ {
+ throw DependencyException(msg);
+ }
}
}
else
diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h
index 35f90930f8..d021851c25 100644
--- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h
+++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h
@@ -8,6 +8,7 @@
#pragma once
#include
+#include
#include
#include
@@ -91,8 +92,10 @@ namespace TestImpact
//! Applies the specified change list to the dependency map and resolves the change list to a change dependency list
//! containing the updated source dependencies for each source file in the change list.
//! @param changeList The change list to apply and resolve.
+ //! @param integrityFailurePolicy The policy to use for handling integrity errors in the source dependency map.
//! @returns The change list as resolved to the appropriate source dependencies.
- [[nodiscard]] ChangeDependencyList ApplyAndResoveChangeList(const ChangeList& changeList);
+ [[nodiscard]] ChangeDependencyList ApplyAndResoveChangeList(
+ const ChangeList& changeList, Policy::IntegrityFailure integrityFailurePolicy);
//! Removes the specified test target from all source coverage.
void RemoveTestTargetFromSourceCoverage(const TestTarget* testTarget);
diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp
index af6f02fa20..e325463324 100644
--- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp
+++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactRuntime.cpp
@@ -205,7 +205,7 @@ namespace TestImpact
AZStd::vector discardedTestTargets;
// Select and prioritize the test targets pertinent to this change list
- const auto changeDependencyList = m_dynamicDependencyMap->ApplyAndResoveChangeList(changeList);
+ const auto changeDependencyList = m_dynamicDependencyMap->ApplyAndResoveChangeList(changeList, m_integrationFailurePolicy);
const auto selectedTestTargets = m_testSelectorAndPrioritizer->SelectTestTargets(changeDependencyList, testPrioritizationPolicy);
// Populate a set with the selected test targets so that we can infer the discarded test target not selected for this change list
diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.cpp
new file mode 100644
index 0000000000..c2183c5bf5
--- /dev/null
+++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+
+namespace AWSGameLift
+{
+ namespace AWSGameLiftActivityUtils
+ {
+ void GetGameProperties(
+ const AZStd::unordered_map& sessionProperties,
+ Aws::Vector& outGameProperties,
+ AZStd::string& outGamePropertiesOutput)
+ {
+ for (auto iter = sessionProperties.begin(); iter != sessionProperties.end(); iter++)
+ {
+ Aws::GameLift::Model::GameProperty sessionProperty;
+ sessionProperty.SetKey(iter->first.c_str());
+ sessionProperty.SetValue(iter->second.c_str());
+ outGameProperties.push_back(sessionProperty);
+ outGamePropertiesOutput += AZStd::string::format("{Key=%s,Value=%s},", iter->first.c_str(), iter->second.c_str());
+ }
+ if (!outGamePropertiesOutput.empty())
+ {
+ outGamePropertiesOutput =
+ outGamePropertiesOutput.substr(0, outGamePropertiesOutput.size() - 1); // Trim last comma to fit array format
+ }
+ }
+ } // namespace AWSGameLiftActivityUtils
+} // namespace AWSGameLift
diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.h
new file mode 100644
index 0000000000..37adb808dc
--- /dev/null
+++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftActivityUtils.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+namespace AWSGameLift
+{
+ namespace AWSGameLiftActivityUtils
+ {
+ void GetGameProperties(
+ const AZStd::unordered_map& sessionProperties,
+ Aws::Vector& outGameProperties,
+ AZStd::string& outGamePropertiesOutput);
+ } // namespace AWSGameLiftActivityUtils
+} // namespace AWSGameLift
diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftCreateSessionActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftCreateSessionActivity.cpp
index fdd429e0bb..5846b4be4d 100644
--- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftCreateSessionActivity.cpp
+++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftCreateSessionActivity.cpp
@@ -5,6 +5,7 @@
*
*/
+#include
#include
#include
@@ -29,13 +30,12 @@ namespace AWSGameLift
{
request.SetIdempotencyToken(createSessionRequest.m_idempotencyToken.c_str());
}
- for (auto iter = createSessionRequest.m_sessionProperties.begin();
- iter != createSessionRequest.m_sessionProperties.end(); iter++)
+ AZStd::string propertiesOutput = "";
+ Aws::Vector properties;
+ AWSGameLiftActivityUtils::GetGameProperties(createSessionRequest.m_sessionProperties, properties, propertiesOutput);
+ if (!properties.empty())
{
- Aws::GameLift::Model::GameProperty sessionProperty;
- sessionProperty.SetKey(iter->first.c_str());
- sessionProperty.SetValue(iter->second.c_str());
- request.AddGameProperties(sessionProperty);
+ request.SetGameProperties(properties);
}
// Required attributes
@@ -49,6 +49,16 @@ namespace AWSGameLift
}
request.SetMaximumPlayerSessionCount(createSessionRequest.m_maxPlayer);
+ AZ_TracePrintf(AWSGameLiftCreateSessionActivityName,
+ "Built CreateGameSessionRequest with CreatorId=%s, Name=%s, IdempotencyToken=%s, GameProperties=%s, AliasId=%s, FleetId=%s and MaximumPlayerSessionCount=%d",
+ request.GetCreatorId().c_str(),
+ request.GetName().c_str(),
+ request.GetIdempotencyToken().c_str(),
+ AZStd::string::format("[%s]", propertiesOutput.c_str()).c_str(),
+ request.GetAliasId().c_str(),
+ request.GetFleetId().c_str(),
+ request.GetMaximumPlayerSessionCount());
+
return request;
}
@@ -61,6 +71,7 @@ namespace AWSGameLift
AZStd::string result = "";
Aws::GameLift::Model::CreateGameSessionRequest request = BuildAWSGameLiftCreateGameSessionRequest(createSessionRequest);
auto createSessionOutcome = gameliftClient.CreateGameSession(request);
+ AZ_TracePrintf(AWSGameLiftCreateSessionActivityName, "CreateGameSession request against Amazon GameLift service is complete");
if (createSessionOutcome.IsSuccess())
{
diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftCreateSessionOnQueueActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftCreateSessionOnQueueActivity.cpp
index d4124cf726..bbaba97a6c 100644
--- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftCreateSessionOnQueueActivity.cpp
+++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftCreateSessionOnQueueActivity.cpp
@@ -6,6 +6,7 @@
*/
#include
+#include
#include
namespace AWSGameLift
@@ -21,13 +22,12 @@ namespace AWSGameLift
{
request.SetGameSessionName(createSessionOnQueueRequest.m_sessionName.c_str());
}
- for (auto iter = createSessionOnQueueRequest.m_sessionProperties.begin();
- iter != createSessionOnQueueRequest.m_sessionProperties.end(); iter++)
+ AZStd::string propertiesOutput = "";
+ Aws::Vector properties;
+ AWSGameLiftActivityUtils::GetGameProperties(createSessionOnQueueRequest.m_sessionProperties, properties, propertiesOutput);
+ if (!properties.empty())
{
- Aws::GameLift::Model::GameProperty sessionProperty;
- sessionProperty.SetKey(iter->first.c_str());
- sessionProperty.SetValue(iter->second.c_str());
- request.AddGameProperties(sessionProperty);
+ request.SetGameProperties(properties);
}
// Required attributes
@@ -35,6 +35,14 @@ namespace AWSGameLift
request.SetMaximumPlayerSessionCount(createSessionOnQueueRequest.m_maxPlayer);
request.SetPlacementId(createSessionOnQueueRequest.m_placementId.c_str());
+ AZ_TracePrintf(AWSGameLiftCreateSessionOnQueueActivityName,
+ "Built StartGameSessionPlacementRequest with GameSessionName=%s, GameProperties=%s, GameSessionQueueName=%s, MaximumPlayerSessionCount=%d and PlacementId=%s",
+ request.GetGameSessionName().c_str(),
+ AZStd::string::format("[%s]", propertiesOutput.c_str()).c_str(),
+ request.GetGameSessionQueueName().c_str(),
+ request.GetMaximumPlayerSessionCount(),
+ request.GetPlacementId().c_str());
+
return request;
}
@@ -49,6 +57,8 @@ namespace AWSGameLift
Aws::GameLift::Model::StartGameSessionPlacementRequest request =
BuildAWSGameLiftStartGameSessionPlacementRequest(createSessionOnQueueRequest);
auto createSessionOnQueueOutcome = gameliftClient.StartGameSessionPlacement(request);
+ AZ_TracePrintf(AWSGameLiftCreateSessionOnQueueActivityName,
+ "StartGameSessionPlacement request against Amazon GameLift service is complete.");
if (createSessionOnQueueOutcome.IsSuccess())
{
diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftJoinSessionActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftJoinSessionActivity.cpp
index d02691967d..9920bc4f61 100644
--- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftJoinSessionActivity.cpp
+++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftJoinSessionActivity.cpp
@@ -27,6 +27,13 @@ namespace AWSGameLift
// Required attributes
request.SetPlayerId(joinSessionRequest.m_playerId.c_str());
request.SetGameSessionId(joinSessionRequest.m_sessionId.c_str());
+
+ AZ_TracePrintf(AWSGameLiftJoinSessionActivityName,
+ "Built CreatePlayerSessionRequest with PlayerData=%s, PlayerId=%s and GameSessionId=%s",
+ request.GetPlayerData().c_str(),
+ request.GetPlayerId().c_str(),
+ request.GetGameSessionId().c_str());
+
return request;
}
@@ -40,6 +47,13 @@ namespace AWSGameLift
sessionConnectionConfig.m_ipAddress = createPlayerSessionResult.GetPlayerSession().GetIpAddress().c_str();
sessionConnectionConfig.m_playerSessionId = createPlayerSessionResult.GetPlayerSession().GetPlayerSessionId().c_str();
sessionConnectionConfig.m_port = createPlayerSessionResult.GetPlayerSession().GetPort();
+
+ AZ_TracePrintf(AWSGameLiftJoinSessionActivityName,
+ "Built SessionConnectionConfig with IpAddress=%s, PlayerSessionId=%s and Port=%d",
+ sessionConnectionConfig.m_ipAddress.c_str(),
+ sessionConnectionConfig.m_playerSessionId.c_str(),
+ sessionConnectionConfig.m_port);
+
return sessionConnectionConfig;
}
@@ -54,6 +68,8 @@ namespace AWSGameLift
Aws::GameLift::Model::CreatePlayerSessionRequest request =
BuildAWSGameLiftCreatePlayerSessionRequest(joinSessionRequest);
auto createPlayerSessionOutcome = gameliftClient.CreatePlayerSession(request);
+ AZ_TracePrintf(AWSGameLiftJoinSessionActivityName,
+ "CreatePlayerSession request for player %s against Amazon GameLift service is complete", joinSessionRequest.m_playerId.c_str());
if (!createPlayerSessionOutcome.IsSuccess())
{
@@ -72,11 +88,13 @@ namespace AWSGameLift
auto clientRequestHandler = AZ::Interface::Get();
if (clientRequestHandler)
{
- AZ_TracePrintf(AWSGameLiftJoinSessionActivityName, "Requesting player to connect to game session ...");
-
AzFramework::SessionConnectionConfig sessionConnectionConfig =
BuildSessionConnectionConfig(createPlayerSessionOutcome);
+
+ AZ_TracePrintf(AWSGameLiftJoinSessionActivityName,
+ "Requesting and validating player session %s to connect to game session ...", sessionConnectionConfig.m_playerSessionId.c_str());
result = clientRequestHandler->RequestPlayerJoinSession(sessionConnectionConfig);
+ AZ_TracePrintf(AWSGameLiftJoinSessionActivityName, "Started connection process, and connection validation is in process.");
}
else
{
diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftLeaveSessionActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftLeaveSessionActivity.cpp
index 241f2ac120..024faae1c4 100644
--- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftLeaveSessionActivity.cpp
+++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftLeaveSessionActivity.cpp
@@ -19,9 +19,9 @@ namespace AWSGameLift
auto clientRequestHandler = AZ::Interface::Get();
if (clientRequestHandler)
{
- AZ_TracePrintf(AWSGameLiftLeaveSessionActivityName, "Requesting to leave the current session...");
-
+ AZ_TracePrintf(AWSGameLiftLeaveSessionActivityName, "Requesting player to leave the current session ...");
clientRequestHandler->RequestPlayerLeaveSession();
+ AZ_TracePrintf(AWSGameLiftLeaveSessionActivityName, "Started disconnect process, and player clean up is in process.");
}
else
{
diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftSearchSessionsActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftSearchSessionsActivity.cpp
index b1b01e66f1..aa5f0e14d9 100644
--- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftSearchSessionsActivity.cpp
+++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftSearchSessionsActivity.cpp
@@ -35,6 +35,7 @@ namespace AWSGameLift
{
request.SetNextToken(searchSessionsRequest.m_nextToken.c_str());
}
+
// Required attributes
if (!searchSessionsRequest.m_aliasId.empty())
{
@@ -46,6 +47,16 @@ namespace AWSGameLift
}
// TODO: Update the AWS Native SDK to accept the new request parameter.
//request.SetLocation(searchSessionsRequest.m_location.c_str());
+
+ AZ_TracePrintf(AWSGameLiftSearchSessionsActivityName,
+ "Built SearchGameSessionsRequest with FilterExpression=%s, SortExpression=%s, Limit=%d, NextToken=%s, AliasId=%s and FleetId=%s",
+ request.GetFilterExpression().c_str(),
+ request.GetSortExpression().c_str(),
+ request.GetLimit(),
+ request.GetNextToken().c_str(),
+ request.GetAliasId().c_str(),
+ request.GetFleetId().c_str());
+
return request;
}
@@ -58,6 +69,7 @@ namespace AWSGameLift
AzFramework::SearchSessionsResponse response;
Aws::GameLift::Model::SearchGameSessionsRequest request = BuildAWSGameLiftSearchGameSessionsRequest(searchSessionsRequest);
Aws::GameLift::Model::SearchGameSessionsOutcome outcome = gameliftClient.SearchGameSessions(request);
+ AZ_TracePrintf(AWSGameLiftSearchSessionsActivityName, "SearchGameSessions request against Amazon GameLift service is complete");
if (outcome.IsSuccess())
{
diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake
index ce6c017e8e..5bce8387a4 100644
--- a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake
+++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake
@@ -11,6 +11,8 @@ set(FILES
Include/Request/AWSGameLiftJoinSessionRequest.h
Include/Request/AWSGameLiftSearchSessionsRequest.h
Include/Request/IAWSGameLiftRequests.h
+ Source/Activity/AWSGameLiftActivityUtils.cpp
+ Source/Activity/AWSGameLiftActivityUtils.h
Source/Activity/AWSGameLiftCreateSessionActivity.cpp
Source/Activity/AWSGameLiftCreateSessionActivity.h
Source/Activity/AWSGameLiftCreateSessionOnQueueActivity.cpp
diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp
index 3b2fd910bf..72f120d7dc 100644
--- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp
+++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp
@@ -106,8 +106,10 @@ namespace AWSGameLift
return false;
}
- AZ_TracePrintf(AWSGameLiftServerManagerName, "Initiating Amazon GameLift Server SDK...");
+ AZ_TracePrintf(AWSGameLiftServerManagerName, "Initiating Amazon GameLift Server SDK ...");
Aws::GameLift::Server::InitSDKOutcome initOutcome = m_gameLiftServerSDKWrapper->InitSDK();
+ AZ_TracePrintf(AWSGameLiftServerManagerName, "InitSDK request against Amazon GameLift service is complete.");
+
m_serverSDKInitialized = initOutcome.IsSuccess();
AZ_Error(AWSGameLiftServerManagerName, m_serverSDKInitialized,
@@ -138,8 +140,10 @@ namespace AWSGameLift
return;
}
- AZ_TracePrintf(AWSGameLiftServerManagerName, "Notifying GameLift server process is ending...");
+ AZ_TracePrintf(AWSGameLiftServerManagerName, "Notifying GameLift server process is ending ...");
Aws::GameLift::GenericOutcome processEndingOutcome = m_gameLiftServerSDKWrapper->ProcessEnding();
+ AZ_TracePrintf(AWSGameLiftServerManagerName, "ProcessEnding request against Amazon GameLift service is complete.");
+
bool processEndingIsSuccess = processEndingOutcome.IsSuccess();
AZ_Error(AWSGameLiftServerManagerName, processEndingIsSuccess, AWSGameLiftServerProcessEndingErrorMessage,
@@ -155,7 +159,12 @@ namespace AWSGameLift
return;
}
+ AZ_TracePrintf(AWSGameLiftServerManagerName,
+ "Removing player session %s from Amazon GameLift service ...", playerSessionId.c_str());
Aws::GameLift::GenericOutcome disconnectOutcome = m_gameLiftServerSDKWrapper->RemovePlayerSession(playerSessionId);
+ AZ_TracePrintf(AWSGameLiftServerManagerName,
+ "RemovePlayerSession request for player session %s against Amazon GameLift service is complete.", playerSessionId.c_str());
+
AZ_Error(AWSGameLiftServerManagerName, disconnectOutcome.IsSuccess(), AWSGameLiftServerRemovePlayerSessionErrorMessage,
playerSessionId.c_str(), disconnectOutcome.GetError().GetErrorMessage().c_str());
}
@@ -188,8 +197,9 @@ namespace AWSGameLift
AZStd::bind(&AWSGameLiftServerManager::OnHealthCheck, this), desc.m_port,
Aws::GameLift::Server::LogParameters(logPaths));
- AZ_TracePrintf(AWSGameLiftServerManagerName, "Notifying GameLift server process is ready...");
+ AZ_TracePrintf(AWSGameLiftServerManagerName, "Notifying GameLift server process is ready ...");
auto processReadyOutcome = m_gameLiftServerSDKWrapper->ProcessReady(processReadyParameter);
+ AZ_TracePrintf(AWSGameLiftServerManagerName, "ProcessReady request against Amazon GameLift service is complete.");
if (!processReadyOutcome.IsSuccess())
{
@@ -213,8 +223,9 @@ namespace AWSGameLift
if (createSessionResult)
{
- AZ_TracePrintf(AWSGameLiftServerManagerName, "Activating GameLift game session...");
+ AZ_TracePrintf(AWSGameLiftServerManagerName, "Activating GameLift game session ...");
Aws::GameLift::GenericOutcome activationOutcome = m_gameLiftServerSDKWrapper->ActivateGameSession();
+ AZ_TracePrintf(AWSGameLiftServerManagerName, "ActivateGameSession request against Amazon GameLift service is complete.");
if (activationOutcome.IsSuccess())
{
@@ -240,7 +251,7 @@ namespace AWSGameLift
void AWSGameLiftServerManager::OnProcessTerminate()
{
- AZ_TracePrintf(AWSGameLiftServerManagerName, "GameLift is shutting down server process...");
+ AZ_TracePrintf(AWSGameLiftServerManagerName, "GameLift is shutting down server process ...");
HandleDestroySession();
}
@@ -299,8 +310,11 @@ namespace AWSGameLift
return false;
}
- AZ_TracePrintf(AWSGameLiftServerManagerName, "Attempting to accept player session connection with Amazon GameLift service...");
+ AZ_TracePrintf(AWSGameLiftServerManagerName,
+ "Attempting to accept player session %s connection with Amazon GameLift service ...", playerSessionId.c_str());
auto acceptPlayerSessionOutcome = m_gameLiftServerSDKWrapper->AcceptPlayerSession(playerSessionId.c_str());
+ AZ_TracePrintf(AWSGameLiftServerManagerName,
+ "AcceptPlayerSession request for player session %s against Amazon GameLift service is complete.", playerSessionId.c_str());
if (!acceptPlayerSessionOutcome.IsSuccess())
{
diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ltc.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ltc.azsli
index 292f8a8a22..6e45cf30c8 100644
--- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ltc.azsli
+++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ltc.azsli
@@ -80,14 +80,44 @@ float3x3 BuildViewAlignedOrthonormalBasis(in float3 normal, in float3 dirToView)
// xy plane in positive z space.
float IntegrateEdge(float3 v1, float3 v2)
{
+ // This alternate version may work better for platforms where acos() precision is low.
+ /*
+ float x = dot(v1, v2);
+ float y = abs(x);
+
+ float a = 5.42031 + (3.12829 + 0.0902326 * y) * y;
+ float b = 3.45068 + (4.18814 + y) * y;
+ float theta_sinTheta = a / b;
+
+ if (x < 0.0)
+ {
+ theta_sinTheta = PI * rsqrt(saturate(1.0 - x * x)) - theta_sinTheta;
+ }
+
+ float3 u = cross(v1, v2);
+ return theta_sinTheta * u.z;
+ */
+
float cosTheta = dot(v1, v2);
- cosTheta = clamp(cosTheta, -0.9999, 0.9999);
+ float theta = acos(cosTheta);
// calculate 1.0 / sin(theta)
- float invSinTheta = rsqrt(1.0 - cosTheta * cosTheta);
+ float invSinTheta = rsqrt(saturate(1.0 - cosTheta * cosTheta));
- float theta = acos(cosTheta);
- return cross(v1, v2).z * theta * invSinTheta;
+ return cross(v1, v2).z * ((theta > 0.001) ? theta * invSinTheta : 1.0);
+}
+
+// Cheaper version of above which is good enough for diffuse
+float IntegrateEdgeDiffuse(float3 v1, float3 v2)
+{
+ float cosTheta = dot(v1, v2);
+ float absCosTheta = abs(cosTheta);
+ float theta_sinTheta = 1.5708 + (-0.879406 + 0.308609 * absCosTheta) * absCosTheta;
+ if (cosTheta < 0.0)
+ {
+ theta_sinTheta = PI * rsqrt(1.0 - cosTheta * cosTheta) - theta_sinTheta;
+ }
+ return theta_sinTheta * cross(v1, v2).z;
}
// Returns the unnormalized z plane intersection point between pointAboveHorizon and pointBelowHorizon.
@@ -189,42 +219,90 @@ void ClipQuadToHorizon(inout float3 p[5], out int vertexCount)
}
}
-// Takes 4 points (p) from a quad plus a 5th dummy point, rotates them into the space of the normal, then transforms
-// the points by the LTC matrix provided. The points are then clipped to the normal's hemisphere. The number of points
-// after the clip is returned in vertexCount. It's possible for the resulting clipped quad to be a triangle (when 3
-// points are below the horizon), or a pentagon (when one point is below the horizon), or be a regular 4 point quad.
-void LtcClipAndNormalizeQuad(in float3 normal, in float3 dirToView, float3x3 ltcMat, inout float3 p[5], out int vertexCount)
+// Applies the LTC matrix to the clipped points of a quad.
+void ApplyLtcMatrixToQuad(in float3x3 ltcMat, inout float3 p[5], in int vertexCount)
{
- // Rotate ltc matrix
- ltcMat = mul(ltcMat, BuildViewAlignedOrthonormalBasis(normal, dirToView));
-
// Transform points into ltc space
- p[0] = mul(ltcMat, p[0].xyz);
- p[1] = mul(ltcMat, p[1].xyz);
- p[2] = mul(ltcMat, p[2].xyz);
- p[3] = mul(ltcMat, p[3].xyz);
+ p[0] = mul(ltcMat, p[0]);
+ p[1] = mul(ltcMat, p[1]);
+ p[2] = mul(ltcMat, p[2]);
- ClipQuadToHorizon(p, vertexCount);
-
- // visibility check
- if (vertexCount == 0)
+ if (vertexCount > 3)
{
- return;
+ p[3] = mul(ltcMat, p[3]);
}
+ if (vertexCount > 4)
+ {
+ p[4] = mul(ltcMat, p[4]);
+ }
+}
- // project onto sphere
+// Projects the clipped points of a quad onto the sphere.
+void NormalizeQuadPoints(inout float3 p[5], in int vertexCount)
+{
+ // project quad points onto a sphere.
p[0] = normalize(p[0]);
p[1] = normalize(p[1]);
p[2] = normalize(p[2]);
- p[3] = normalize(p[3]);
- p[4] = normalize(p[4]);
+
+ if (vertexCount > 3)
+ {
+ p[3] = normalize(p[3]);
+ }
+ if (vertexCount > 4)
+ {
+ p[4] = normalize(p[4]);
+ }
}
-float IntegrateQuad(in float3 v[5], in float vertexCount, in bool doubleSided)
+// Transforms the 4 points of a quad into the hemisphere of the normal
+void TransformQuadToOrthonormalBasis(in float3 normal, in float3 dirToView, inout float3 p[4])
+{
+ float3x3 orthoNormalBasis = BuildViewAlignedOrthonormalBasis(normal, dirToView);
+
+ // Transform points into orthonormal space
+ p[0] = mul(orthoNormalBasis, p[0]);
+ p[1] = mul(orthoNormalBasis, p[1]);
+ p[2] = mul(orthoNormalBasis, p[2]);
+ p[3] = mul(orthoNormalBasis, p[3]);
+}
+
+// Integrates the edges of a quad for lambertian diffuse contribution.
+float IntegrateQuadDiffuse(in float3 v[5], in float vertexCount, in bool doubleSided)
{
- // Integrate
float sum = 0.0;
+ NormalizeQuadPoints(v, vertexCount);
+
+ // There must be at least 3 points so don't check for the first 2 edges.
+ sum += IntegrateEdgeDiffuse(v[0], v[1]);
+ sum += IntegrateEdgeDiffuse(v[1], v[2]);
+
+ if (vertexCount > 3)
+ {
+ sum += IntegrateEdgeDiffuse(v[2], v[3]);
+ if (vertexCount == 5)
+ {
+ sum += IntegrateEdgeDiffuse(v[3], v[4]);
+ }
+ }
+
+ // Close the polygon
+ sum += IntegrateEdgeDiffuse(v[vertexCount - 1], v[0]);
+
+ // Note: negated due to winding order
+ sum = doubleSided ? abs(sum) : max(0.0, -sum);
+
+ return sum;
+}
+
+// Integrates the edges of a quad for specular contribution.
+float IntegrateQuadSpecular(in float3 v[5], in float vertexCount, in bool doubleSided)
+{
+ float sum = 0.0;
+
+ NormalizeQuadPoints(v, vertexCount);
+
// There must be at least 3 points so don't check for the first 2 edges.
sum += IntegrateEdge(v[0], v[1]);
sum += IntegrateEdge(v[1], v[2]);
@@ -247,26 +325,62 @@ float IntegrateQuad(in float3 v[5], in float vertexCount, in bool doubleSided)
return sum;
}
-float LtcQuadEvaluate(in float3 normal, in float3 dirToView, in float3x3 ltcMat, in float3 p[4], in bool doubleSided)
+// Evaluate linear transform cosine lighting for a 4 point quad.
+// normal - The surface normal
+// dirToView - Normalized direction from the surface to the view
+// ltcMat - The LTC matrix for specular, or identity for diffuse.
+// p[4] - The 4 light positions relative to the surface position.
+// doubleSided - If the quad emits light from both sides
+// diffuse - The output diffuse response for the quad light
+// specular - The output specular response for the quad light
+void LtcQuadEvaluate(
+ in float3 normal,
+ in float3 dirToView,
+ in float3x3 ltcMat,
+ in float3 p[4],
+ in bool doubleSided,
+ out float diffuse,
+ out float specular)
{
+ // Transform the points of the light into the space of the normal's hemisphere.
+ TransformQuadToOrthonormalBasis(normal, dirToView, p);
+
// Initialize quad with dummy point at end in case one corner is clipped (resulting in 5 sided polygon)
float3 v[5] = {p[0], p[1], p[2], p[3], float3(0.0, 0.0, 0.0)};
- int vertexCount = 0;
- LtcClipAndNormalizeQuad(normal, dirToView, ltcMat, v, vertexCount);
+ // Clip the light polygon to the normal hemisphere. This is done before the LTC matrix is applied to prevent
+ // parts of the light below the horizon from impacting the surface. The number of points remaining after
+ // the clip is returned in vertexCount. It's possible for the vertexCount of the resulting clipped quad to be
+ // 0 - all points clipped (no work to do, so return)
+ // 3 - 3 points clipped, leaving only a triangular corner of the quad
+ // 4 - 2 or 0 points clipped, leaving a quad
+ // 5 - 1 point clipped leaving a pentagon.
- if (vertexCount > 0)
+ int vertexCount = 0;
+ ClipQuadToHorizon(v, vertexCount);
+
+ if (vertexCount == 0)
{
- return IntegrateQuad(v, vertexCount, doubleSided);
+ // Entire light is below the horizon.
+ return;
}
- return 0.0;
+
+ // IntegrateQuadDiffuse is a cheap approximation compared to specular.
+ diffuse = IntegrateQuadDiffuse(v, vertexCount, doubleSided);
+
+ ApplyLtcMatrixToQuad(ltcMat, v, vertexCount);
+
+ // IntegrateQuadSpecular uses more accurate integration to handle smooth surfaces correctly.
+ specular = IntegrateQuadSpecular(v, vertexCount, doubleSided);
}
// Checks an edge against the horizon and integrates it.
-// p0 - first point
-// p1 - second point
-// prevClipPoint - the clip point saved from the last time an edge went from above to below the horizon
-// sum - the sum total of all integrations to contribute to.
+// p0 - First point
+// p1 - Second point
+// prevClipPoint - The clip point saved from the last time an edge went from above to below the horizon
+// ltcMat - The ltc lookup matrix for specular
+// diffuse - The current sum total of diffuse contribution to apply additional contribution to
+// specular - The current sum total of specular contribution to apply additional contribution to
//
// Explanation:
// When evaluating edges of a polygon there are four possible states to deal with
@@ -283,28 +397,34 @@ float LtcQuadEvaluate(in float3 normal, in float3 dirToView, in float3x3 ltcMat,
// 4. Both points are below the horizon
// - Do nothing.
-void EvaluatePolyEdge(in float3 p0, in float3 p1, inout float3 prevClipPoint, inout float sum)
+void EvaluatePolyEdge(in float3 p0, in float3 p1, inout float3 prevClipPoint, in float3x3 ltcMat, inout float diffuse, inout float specular)
{
if (p0.z > 0.0)
{
if (p1.z > 0.0)
{
// Both above horizon
- sum += IntegrateEdge(normalize(p0), normalize(p1));
+ diffuse += IntegrateEdgeDiffuse(normalize(p0), normalize(p1));
+ specular += IntegrateEdge(normalize(mul(ltcMat, p0)), normalize(mul(ltcMat, p1)));
}
else
{
// Going from above to below horizon
- prevClipPoint = normalize(ClipEdge(p0, p1));
- sum += IntegrateEdge(normalize(p0), prevClipPoint);
+ prevClipPoint = ClipEdge(p0, p1);
+ diffuse += IntegrateEdgeDiffuse(normalize(p0), normalize(prevClipPoint));
+ specular += IntegrateEdge(normalize(mul(ltcMat, p0)), normalize(mul(ltcMat, prevClipPoint)));
}
}
else if (p1.z > 0.0)
{
// Going from below to above horizon
- float3 clipPoint = normalize(ClipEdge(p1, p0));
- sum += IntegrateEdge(prevClipPoint, clipPoint);
- sum += IntegrateEdge(clipPoint, normalize(p1));
+ float3 clipPoint = ClipEdge(p1, p0);
+ diffuse += IntegrateEdgeDiffuse(normalize(prevClipPoint), normalize(clipPoint));
+ diffuse += IntegrateEdgeDiffuse(normalize(clipPoint), normalize(p1));
+
+ clipPoint = mul(ltcMat, clipPoint);
+ specular += IntegrateEdge(normalize(mul(ltcMat, prevClipPoint)), normalize(clipPoint));
+ specular += IntegrateEdge(normalize(clipPoint), normalize(mul(ltcMat, p1)));
}
}
@@ -316,26 +436,38 @@ void EvaluatePolyEdge(in float3 p0, in float3 p1, inout float3 prevClipPoint, in
// positions - The buffer where the polygon positions are
// startIdx - The index of the first polygon position
// endIdx - The index of the point directly after the last polygon position
-//
+// diffuse - The output diffuse response for the polygon light
+// specular - The output specular response for the polygon light
// The most complicated aspect of this function is clipping the polygon against the horizon of the surface point. See
// EvaluatePolyEdge() above for details on the general concept. However, this function must deal with the case of the
// first point being below the horizon. In that case, it needs to search in reverse from the end for the first point
// above the horizon, and save the intersection point between the above and below points so it can be used in
// EvaluatePolyEdge() later. During this search it also adjusts the end point index as necessary to avoid processing
// those points that are below the horizon.
-float LtcPolygonEvaluate(in float3 pos, in float3 normal, in float3 dirToView, in float3x3 ltcMat, in StructuredBuffer positions, in uint startIdx, in uint endIdx)
+void LtcPolygonEvaluate(
+ in float3 pos,
+ in float3 normal,
+ in float3 dirToView,
+ in float3x3 ltcMat,
+ in StructuredBuffer positions,
+ in uint startIdx,
+ in uint endIdx,
+ out float diffuse,
+ out float specular
+)
{
if (endIdx - startIdx < 3)
{
- return 0.0; // Must have at least 3 points to form a polygon.
+ return; // Must have at least 3 points to form a polygon.
}
// Rotate ltc matrix
- ltcMat = mul(ltcMat, BuildViewAlignedOrthonormalBasis(normal, dirToView));
+ float3x3 orthonormalMat = BuildViewAlignedOrthonormalBasis(normal, dirToView);
// Prepare initial values
- float sum = 0.0; // sum of edge integation
- float3 p0 = mul(ltcMat, positions[startIdx].xyz - pos); // First point in polygon
+ float3 p0 = mul(orthonormalMat, positions[startIdx].xyz - pos); // First point in polygon
+ diffuse = 0.0;
+ specular = 0.0;
float3 prevClipPoint = float3(0.0, 0.0, 0.0); // Used to hold previous clip point when polygon dips below horizon.
float3 closePoint = p0;
@@ -349,10 +481,10 @@ float LtcPolygonEvaluate(in float3 pos, in float3 normal, in float3 dirToView, i
// searching backwards, updating the endIdx along the way to avoid reprocessing those points later
for ( ; endIdx > startIdx + 1; --endIdx)
{
- float3 prevPoint = mul(ltcMat, positions[endIdx - 1].xyz - pos);
- if (prevPoint.z > 0)
+ float3 prevPoint = mul(orthonormalMat, positions[endIdx - 1].xyz - pos);
+ if (prevPoint.z > 0.0)
{
- prevClipPoint = normalize(ClipEdge(prevPoint, p0));
+ prevClipPoint = ClipEdge(prevPoint, p0);
closePoint = prevClipPoint;
break;
}
@@ -362,7 +494,7 @@ float LtcPolygonEvaluate(in float3 pos, in float3 normal, in float3 dirToView, i
// Check if all points below horizon
if (endIdx == startIdx + 1)
{
- return 0.0;
+ return;
}
p0 = firstPoint; // Restore the original p0
@@ -371,13 +503,14 @@ float LtcPolygonEvaluate(in float3 pos, in float3 normal, in float3 dirToView, i
// Evaluate all the points
for (uint curIdx = startIdx + 1; curIdx < endIdx; ++curIdx)
{
- float3 p1 = mul(ltcMat, positions[curIdx].xyz - pos); // Current point in polygon
- EvaluatePolyEdge(p0, p1, prevClipPoint, sum);
+ float3 p1 = mul(orthonormalMat, positions[curIdx].xyz - pos); // Current point in polygon
+ EvaluatePolyEdge(p0, p1, prevClipPoint, ltcMat, diffuse, specular);
p0 = p1;
}
- EvaluatePolyEdge(p0, closePoint, prevClipPoint, sum);
+ EvaluatePolyEdge(p0, closePoint, prevClipPoint, ltcMat, diffuse, specular);
// Note: negated due to winding order
- return -sum;
+ diffuse = -diffuse;
+ specular = -specular;
}
diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PolygonLight.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PolygonLight.azsli
index bbd3c1c9e3..d7313556a2 100644
--- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PolygonLight.azsli
+++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PolygonLight.azsli
@@ -50,26 +50,25 @@ void ApplyPoylgonLight(ViewSrg::PolygonLight light, Surface surface, inout Light
float radiusAttenuation = 1.0 - (falloff * falloff);
radiusAttenuation = radiusAttenuation * radiusAttenuation;
- // Diffuse
- static const float3x3 identityMatrix = float3x3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);
- float diffuse = LtcPolygonEvaluate(surface.position, surface.normal, lightingData.dirToCamera, identityMatrix, ViewSrg::m_polygonLightPoints, startIndex, endIndex);
- diffuse = doubleSided ? abs(diffuse) : max(0.0, diffuse);
-
- // Specular
float2 ltcCoords = LtcCoords(dot(surface.normal, lightingData.dirToCamera), surface.roughnessLinear);
float3x3 ltcMat = LtcMatrix(SceneSrg::m_ltcMatrix, ltcCoords);
- float3 specular = LtcPolygonEvaluate(surface.position, surface.normal, lightingData.dirToCamera, ltcMat, ViewSrg::m_polygonLightPoints, startIndex, endIndex);
+
+ float diffuse = 0.0;
+ float specular = 0.0;
+
+ LtcPolygonEvaluate(surface.position, surface.normal, lightingData.dirToCamera, ltcMat, ViewSrg::m_polygonLightPoints, startIndex, endIndex, diffuse, specular);
+ diffuse = doubleSided ? abs(diffuse) : max(0.0, diffuse);
specular = doubleSided ? abs(specular) : max(0.0, specular);
// Apply BRDF scale terms (BRDF magnitude and Schlick Fresnel)
float2 schlick = SceneSrg::m_ltcAmplification.Sample(PassSrg::LinearSampler, ltcCoords).xy;
- specular *= schlick.x + (1.0 - surface.specularF0) * schlick.y;
+ float3 specularRGB = specular * (schlick.x + (1.0 - surface.specularF0) * schlick.y);
// Scale by inverse surface area of hemisphere (1/2pi), attenuation, and light intensity
float3 intensity = 0.5 * INV_PI * radiusAttenuation * abs(light.m_rgbIntensityNits);
lightingData.diffuseLighting += surface.albedo * diffuse * intensity;
- lightingData.specularLighting += surface.specularF0 * specular * intensity;
+ lightingData.specularLighting += surface.specularF0 * specularRGB * intensity;
}
void ApplyPolygonLights(Surface surface, inout LightingData lightingData)
diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/QuadLight.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/QuadLight.azsli
index 91981e4f4d..29ee97fd6f 100644
--- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/QuadLight.azsli
+++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/QuadLight.azsli
@@ -111,24 +111,22 @@ void ApplyQuadLight(ViewSrg::QuadLight light, Surface surface, inout LightingDat
{
float3 p[4] = {p0, p1, p2, p3};
- // Diffuse
- float3x3 identityMatrix = float3x3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);
- float diffuse = LtcQuadEvaluate(surface.normal, lightingData.dirToCamera, identityMatrix, p, doubleSided);
-
- // Specular
float2 ltcCoords = LtcCoords(dot(surface.normal, lightingData.dirToCamera), surface.roughnessLinear);
float3x3 ltcMat = LtcMatrix(SceneSrg::m_ltcMatrix, ltcCoords);
- float3 specular = LtcQuadEvaluate(surface.normal, lightingData.dirToCamera, ltcMat, p, doubleSided);
+
+ float diffuse = 0.0;
+ float specular = 0.0;
+ LtcQuadEvaluate(surface.normal, lightingData.dirToCamera, ltcMat, p, doubleSided, diffuse, specular);
// Apply BRDF scale terms (BRDF magnitude and Schlick Fresnel)
float2 schlick = SceneSrg::m_ltcAmplification.Sample(PassSrg::LinearSampler, ltcCoords).xy;
- specular *= schlick.x + (1.0 - surface.specularF0) * schlick.y;
+ float3 specularRGB = specular * (schlick.x + (1.0 - surface.specularF0) * schlick.y);
// Scale by inverse surface area of hemisphere (1/2pi), attenuation, and light intensity
float3 intensity = 0.5 * INV_PI * radiusAttenuation * light.m_rgbIntensityNits;
lightingData.diffuseLighting += surface.albedo * diffuse * intensity;
- lightingData.specularLighting += surface.specularF0 * specular * intensity;
+ lightingData.specularLighting += surface.specularF0 * specularRGB * intensity;
}
else
{
diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h
index e783a03071..53e7de9148 100644
--- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h
+++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h
@@ -46,14 +46,16 @@ namespace AZ
//! Save a buffer attachment or a image attachment binded to a pass's slot to a data file.
//! @param passHierarchy For finding the pass by using PassHierarchyFilter
//! @param slotName Name of the pass's slot. The attachment bound to this slot will be captured.
+ //! @param option Only valid for an InputOutput attachment. Use PassAttachmentReadbackOption::Input to capture the input state
+ //! and use PassAttachmentReadbackOption::Output to capture the output state
//! @param outputFilename The output file path.
virtual bool CapturePassAttachment(const AZStd::vector& passHierarchy, const AZStd::string& slotName
- , const AZStd::string& outputFilePath) = 0;
+ , const AZStd::string& outputFilePath, RPI::PassAttachmentReadbackOption option) = 0;
//! Similar to CapturePassAttachment. But instead of saving the read back result to a file, it will call the callback function provide
//! in the input when callback is finished
virtual bool CapturePassAttachmentWithCallback(const AZStd::vector& passHierarchy, const AZStd::string& slotName
- , RPI::AttachmentReadback::CallbackFunction callback) = 0;
+ , RPI::AttachmentReadback::CallbackFunction callback, RPI::PassAttachmentReadbackOption option) = 0;
};
using FrameCaptureRequestBus = EBus;
diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp
index a115bfa6e8..e8e4609ec4 100644
--- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp
+++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp
@@ -343,7 +343,7 @@ namespace AZ
}
bool FrameCaptureSystemComponent::CapturePassAttachment(const AZStd::vector& passHierarchy, const AZStd::string& slot,
- const AZStd::string& outputFilePath)
+ const AZStd::string& outputFilePath, RPI::PassAttachmentReadbackOption option)
{
InitReadback();
@@ -376,40 +376,22 @@ namespace AZ
return false;
}
- AZ::RPI::RenderPass* renderPass = azrtti_cast(foundPasses[0]);
- if (renderPass)
+ AZ::RPI::Pass* pass = foundPasses[0];
+ if (pass->ReadbackAttachment(m_readback, Name(slot), option))
{
- Name slotName = Name(slot);
- AZ::RPI::PassAttachment* attachment = nullptr;
- for (auto& binding : renderPass->GetAttachmentBindings())
- {
- if (binding.m_name == slotName)
- {
- attachment = binding.m_attachment.get();
- break;
- }
- }
- if (attachment)
- {
- m_state = State::Pending;
- m_result = FrameCaptureResult::None;
- SystemTickBus::Handler::BusConnect();
- renderPass->ReadbackAttachment(m_readback, attachment);
- }
- else
- {
- AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find attachment bound to pass [%s] slot [%s]",
- renderPass->GetName().GetCStr(), slotName.GetCStr());
- return false;
- }
+ m_state = State::Pending;
+ m_result = FrameCaptureResult::None;
+ SystemTickBus::Handler::BusConnect();
+ return true;
}
- return true;
+ AZ_Warning("FrameCaptureSystemComponent", false, "Failed to readback the attachment bound to pass [%s] slot [%s]", pass->GetName().GetCStr(), slot.c_str());
+ return false;
}
bool FrameCaptureSystemComponent::CapturePassAttachmentWithCallback(const AZStd::vector& passHierarchy, const AZStd::string& slotName
- , RPI::AttachmentReadback::CallbackFunction callback)
+ , RPI::AttachmentReadback::CallbackFunction callback, RPI::PassAttachmentReadbackOption option)
{
- bool result = CapturePassAttachment(passHierarchy, slotName, "");
+ bool result = CapturePassAttachment(passHierarchy, slotName, "", option);
// Append state change to user provided call back
AZ::RPI::AttachmentReadback::CallbackFunction callbackSetState = [&, callback](const AZ::RPI::AttachmentReadback::ReadbackResult& result)
diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.h b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.h
index 472dd5da74..eb08494699 100644
--- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.h
+++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.h
@@ -36,9 +36,10 @@ namespace AZ
bool CaptureScreenshot(const AZStd::string& filePath) override;
bool CaptureScreenshotForWindow(const AZStd::string& filePath, AzFramework::NativeWindowHandle windowHandle) override;
bool CaptureScreenshotWithPreview(const AZStd::string& outputFilePath) override;
- bool CapturePassAttachment(const AZStd::vector& passHierarchy, const AZStd::string& slotName, const AZStd::string& outputFilePath) override;
+ bool CapturePassAttachment(const AZStd::vector& passHierarchy, const AZStd::string& slotName, const AZStd::string& outputFilePath,
+ RPI::PassAttachmentReadbackOption option) override;
bool CapturePassAttachmentWithCallback(const AZStd::vector& passHierarchy, const AZStd::string& slotName
- , RPI::AttachmentReadback::CallbackFunction callback) override;
+ , RPI::AttachmentReadback::CallbackFunction callback, RPI::PassAttachmentReadbackOption option) override;
private:
void CaptureAttachmentCallback(const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult);
diff --git a/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexturePass.cpp b/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexturePass.cpp
index 5faaba136e..63437ebbad 100644
--- a/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexturePass.cpp
+++ b/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexturePass.cpp
@@ -64,23 +64,9 @@ namespace AZ
// Set up read back attachment before children prepare
if (m_readback->IsReady())
{
- AZ::RPI::RenderPass* renderPass = azrtti_cast(m_renderTargetPass.get());
- if (renderPass)
+ if (m_renderTargetPass)
{
- RPI::PassAttachment* attachment = nullptr;
- for (auto& binding : renderPass->GetAttachmentBindings())
- {
- if (binding.m_slotType == RPI::PassSlotType::Output)
- {
- attachment = binding.m_attachment.get();
- break;
- }
- }
- if (attachment)
- {
- renderPass->ReadbackAttachment(m_readback, attachment);
- m_attachmentReadbackComplete = true;
- }
+ m_attachmentReadbackComplete = m_renderTargetPass->ReadbackAttachment(m_readback, AZ::Name("RenderTargetOutput"));
}
}
}
diff --git a/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp b/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp
index a69ad3f3af..b31fbca6f2 100644
--- a/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp
+++ b/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp
@@ -229,6 +229,7 @@ namespace AZ
{
m_clearContainers = false;
+ m_stackLevel = 0;
m_cachedTimeRegionMap.clear();
m_timeRegionStack.clear();
m_cachedTimeRegions.clear();
diff --git a/Gems/Atom/RHI/Code/Source/RHI/RayTracingPipelineState.cpp b/Gems/Atom/RHI/Code/Source/RHI/RayTracingPipelineState.cpp
index 5e5932456e..480c11b7c9 100644
--- a/Gems/Atom/RHI/Code/Source/RHI/RayTracingPipelineState.cpp
+++ b/Gems/Atom/RHI/Code/Source/RHI/RayTracingPipelineState.cpp
@@ -142,6 +142,8 @@ namespace AZ
void RayTracingPipelineState::Shutdown()
{
+ ShutdownInternal();
+ DeviceObject::Shutdown();
}
}
}
diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/PhysicalDevice.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/PhysicalDevice.cpp
index 36560868af..52aabdec1d 100644
--- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/PhysicalDevice.cpp
+++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/PhysicalDevice.cpp
@@ -261,7 +261,7 @@ namespace AZ
VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME
} };
- uint32_t optionalExtensionCount = sizeof(optionalExtensions) / sizeof(VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME);
+ [[maybe_unused]] uint32_t optionalExtensionCount = sizeof(optionalExtensions) / sizeof(VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME);
AZ_Assert(optionalExtensionCount == static_cast(OptionalDeviceExtension::Count), "The order and size must match the enum OptionalDeviceExtensions.");
diff --git a/Gems/Atom/RPI/Code/CMakeLists.txt b/Gems/Atom/RPI/Code/CMakeLists.txt
index 71e5d82e48..d806b8c687 100644
--- a/Gems/Atom/RPI/Code/CMakeLists.txt
+++ b/Gems/Atom/RPI/Code/CMakeLists.txt
@@ -25,6 +25,8 @@ ly_add_target(
../Assets/atom_rpi_asset_files.cmake
${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
${MASKED_OCCLUSION_CULLING_FILES}
+ PLATFORM_INCLUDE_FILES
+ ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/Common/${PAL_TRAIT_COMPILER_ID}/atom_rpi_public_${PAL_TRAIT_COMPILER_ID_LOWERCASE}.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h
index c6667e660a..736252a8a2 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h
@@ -57,6 +57,7 @@ namespace AZ
class PassTemplate;
struct PassRequest;
struct PassValidationResults;
+ class AttachmentReadback;
using SortedPipelineViewTags = AZStd::set;
using PassesByDrawList = AZStd::map;
@@ -65,7 +66,12 @@ namespace AZ
const uint32_t PassInputBindingCountMax = 16;
const uint32_t PassInputOutputBindingCountMax = PassInputBindingCountMax;
const uint32_t PassOutputBindingCountMax = PassInputBindingCountMax;
-
+
+ enum class PassAttachmentReadbackOption : uint8_t
+ {
+ Input = 0,
+ Output
+ };
//! Atom's base pass class (every pass class in Atom must derive from this class).
//!
@@ -222,6 +228,14 @@ namespace AZ
//! Enables/Disables PipelineStatistics queries for this pass
virtual void SetPipelineStatisticsQueryEnabled(bool enable);
+ //! Readback an attachment attached to the specified slot name
+ //! @param readback The AttachmentReadback object which is used for readback. Its callback function will be called when readback is finished.
+ //! @param slotName The attachment bind to the slot with this slotName is to be readback
+ //! @param option The option is used for choosing input or output state when readback an InputOutput attachment.
+ //! It's ignored if the attachment isn't an InputOutput attachment.
+ //! Return true if the readback request was successful. User may expect the AttachmentReadback's callback function would be called.
+ bool ReadbackAttachment(AZStd::shared_ptr readback, const Name& slotName, PassAttachmentReadbackOption option = PassAttachmentReadbackOption::Output);
+
//! Returns whether the Timestamp queries is enabled/disabled for this pass
bool IsTimestampQueryEnabled() const;
@@ -266,7 +280,6 @@ namespace AZ
// Update output bindings on this pass that are connected to bindings on other passes
void UpdateConnectedOutputBindings();
-
protected:
explicit Pass(const PassDescriptor& descriptor);
@@ -349,6 +362,7 @@ namespace AZ
void FrameEnd();
virtual void FrameEndInternal() { }
+ void UpdateReadbackAttachment(FramePrepareParams params, bool beforeAddScopes);
// --- Protected Members ---
@@ -442,7 +456,10 @@ namespace AZ
// Sort type to be used by the default sort implementation. Passes can also provide
// fully custom sort implementations by overriding the SortDrawList() function.
RHI::DrawListSortType m_drawListSortType = RHI::DrawListSortType::KeyThenDepth;
-
+
+ // For read back attachment
+ AZStd::shared_ptr m_attachmentReadback;
+ PassAttachmentReadbackOption m_readbackOption;
private:
// Return the Timestamp result of this pass
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h
index b13091082b..3d4ab0a573 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h
@@ -55,10 +55,7 @@ namespace AZ
//! Get MultisampleState of this pass from its output attachments
RHI::MultisampleState GetMultisampleState() const;
-
- //! Capture pass's output and input/output attachments in following frames
- void ReadbackAttachment(AZStd::shared_ptr readback, const PassAttachment* attachment);
-
+
//! Returns a pointer to the Pass ShaderResourceGroup
Data::Instance GetShaderResourceGroup();
@@ -145,9 +142,6 @@ namespace AZ
// Readback the results from the ScopeQueries
void ReadbackScopeQueryResults();
- // For read back attachments
- AZStd::shared_ptr m_attachmentReadback;
-
AZStd::weak_ptr m_attachmentCopy;
// Readback results from the Timestamp queries
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h
index f855154975..fac9abfe28 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h
@@ -166,9 +166,6 @@ namespace AZ
AZ::Vector2 m_position = AZ::Vector2(0, 0.6f);
AZ::Vector2 m_size = AZ::Vector2(0.4f, 0.4f);
bool m_keepAspectRatio = true;
-
- // For readback the output image attachment
- AZStd::shared_ptr m_readback;
};
} // namespace RPI
} // namespace AZ
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h
index 0a689f2f74..f96fe92610 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h
@@ -60,9 +60,6 @@ namespace AZ
// Name of the template used to create the child pass. Needed for Recreate()
Name m_childTemplateName;
- // For read back output
- AZStd::shared_ptr m_readback;
-
Ptr m_outputAttachment;
// saved settings for this pass
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SelectorPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SelectorPass.h
index 6b9559e824..9d1376c840 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SelectorPass.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SelectorPass.h
@@ -15,7 +15,6 @@
#include
#include
-#include
#include
#include
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SwapChainPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SwapChainPass.h
index e7869d463c..e8aeddf7e7 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SwapChainPass.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SwapChainPass.h
@@ -15,7 +15,6 @@
#include
#include
-#include
#include
#include
@@ -88,9 +87,6 @@ namespace AZ
// Name of the template used to create the child pass. Needed for Recreate()
Name m_childTemplateName;
-
- // For read back swap chain
- AZStd::shared_ptr m_swapChainReadback;
};
} // namespace RPI
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h
index 982ef498a0..92e50be75e 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContext.h
@@ -65,9 +65,15 @@ namespace AZ
ConstViewPtr GetDefaultView() const;
//! Gets the current size of the viewport.
+ //! This value is cached and updated on-demand, so may be efficiently queried.
AzFramework::WindowSize GetViewportSize() const;
- // SceneNotificationBus interface
+ //! Gets the screen DPI scaling factor.
+ //! This value is cached and updated on-demand, so may be efficiently queried.
+ //! \see AzFramework::WindowRequests::GetDpiScaleFactor
+ float GetDpiScalingFactor() const;
+
+ // SceneNotificationBus interface overrides...
//! Ensures our default view remains set when our scene's render pipelines are modified.
void OnRenderPipelineAdded(RenderPipelinePtr pipeline) override;
//! Ensures our default view remains set when our scene's render pipelines are modified.
@@ -75,15 +81,22 @@ namespace AZ
//! OnBeginPrepareRender is forwarded to our RenderTick notification to allow subscribers to do rendering.
void OnBeginPrepareRender() override;
- //WindowNotificationBus interface
- //! Used to fire a notification when our window resizes
+ // WindowNotificationBus interface overrides...
+ //! Used to fire a notification when our window resizes.
void OnWindowResized(uint32_t width, uint32_t height) override;
+ //! Used to fire a notification when our window DPI changes.
+ void OnDpiScaleFactorChanged(float dpiScaleFactor) override;
using SizeChangedEvent = AZ::Event;
//! Notifies consumers when the viewport size has changed.
//! Alternatively, connect to ViewportContextNotificationsBus and listen to ViewportContextNotifications::OnViewportSizeChanged.
void ConnectSizeChangedHandler(SizeChangedEvent::Handler& handler);
+ using ScalarChangedEvent = AZ::Event;
+ //! Notifies consumers when the viewport DPI scaling ratio has changed.
+ //! Alternatively, connect to ViewportContextNotificationsBus and listen to ViewportContextNotifications::OnViewportDpiScalingChanged.
+ void ConnectDpiScalingFactorChangedHandler(ScalarChangedEvent::Handler& handler);
+
using MatrixChangedEvent = AZ::Event;
//! Notifies consumers when the view matrix has changed.
void ConnectViewMatrixChangedHandler(MatrixChangedEvent::Handler& handler);
@@ -106,7 +119,7 @@ namespace AZ
//! Notifies consumers when this ViewportContext is about to be destroyed.
void ConnectAboutToBeDestroyedHandler(ViewportIdEvent::Handler& handler);
- // ViewportRequestBus interface
+ // ViewportRequestBus interface overrides...
//! Gets the current camera's view matrix.
const AZ::Matrix4x4& GetCameraViewMatrix() const override;
//! Sets the current camera's view matrix.
@@ -130,8 +143,10 @@ namespace AZ
WindowContextSharedPtr m_windowContext;
ViewPtr m_defaultView;
AzFramework::WindowSize m_viewportSize;
+ float m_viewportDpiScaleFactor = 1.0f;
SizeChangedEvent m_sizeChangedEvent;
+ ScalarChangedEvent m_dpiScalingFactorChangedEvent;
MatrixChangedEvent m_viewMatrixChangedEvent;
MatrixChangedEvent::Handler m_onViewMatrixChangedHandler;
MatrixChangedEvent m_projectionMatrixChangedEvent;
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContextBus.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContextBus.h
index da0c850f3d..152c8cf2c1 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContextBus.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContextBus.h
@@ -105,8 +105,10 @@ namespace AZ
class ViewportContextNotifications
{
public:
- //! Called when the underlying native window size changes for a given viewport context name.
+ //! Called when the underlying native window size changes for a given viewport context.
virtual void OnViewportSizeChanged(AzFramework::WindowSize size){AZ_UNUSED(size);}
+ //! Called when the window DPI scaling changes for a given viewport context.
+ virtual void OnViewportDpiScalingChanged(float dpiScale){AZ_UNUSED(dpiScale);}
//! Called when the active view for a given viewport context name changes.
virtual void OnViewportDefaultViewChanged(AZ::RPI::ViewPtr view){AZ_UNUSED(view);}
//! Called when the viewport is to be rendered.
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContextManager.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContextManager.h
index 008565d178..e50525cff0 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContextManager.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/ViewportContextManager.h
@@ -51,6 +51,7 @@ namespace AZ
{
AZStd::weak_ptr context;
ViewportContext::SizeChangedEvent::Handler sizeChangedHandler;
+ ViewportContext::ScalarChangedEvent::Handler dpiScalingChangedHandler;
};
// ViewportContextManager is a singleton owned solely by RPISystem, which is tagged as a friend
diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Model/ModelKdTree.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Model/ModelKdTree.h
index cd3f1968bf..fc915bcd07 100644
--- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Model/ModelKdTree.h
+++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Model/ModelKdTree.h
@@ -67,7 +67,8 @@ namespace AZ
void ConstructMeshList(const ModelAsset* model, const AZ::Transform& matParent);
static const int s_MinimumVertexSizeInLeafNode = 3 * 10;
-
+ // Stop splitting the tree if more than 10% of the triangles are straddling the split axis
+ static constexpr float s_MaximumSplitAxisStraddlingTriangles = 1.1;
AZStd::unique_ptr m_pRootNode;
struct MeshData
diff --git a/Gems/Atom/RPI/Code/Source/Platform/Common/Clang/atom_rpi_public_clang.cmake b/Gems/Atom/RPI/Code/Source/Platform/Common/Clang/atom_rpi_public_clang.cmake
new file mode 100644
index 0000000000..88309e590c
--- /dev/null
+++ b/Gems/Atom/RPI/Code/Source/Platform/Common/Clang/atom_rpi_public_clang.cmake
@@ -0,0 +1,18 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+ly_add_source_properties(
+ SOURCES External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp
+ PROPERTY COMPILE_OPTIONS
+ VALUES -mavx2 -mfma -msse4.1
+)
+
+ly_add_source_properties(
+ SOURCES External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp
+ PROPERTY COMPILE_OPTIONS
+ VALUES -mno-avx
+)
diff --git a/Gems/Atom/RPI/Code/Source/Platform/Common/MSVC/atom_rpi_public_msvc.cmake b/Gems/Atom/RPI/Code/Source/Platform/Common/MSVC/atom_rpi_public_msvc.cmake
new file mode 100644
index 0000000000..50cfc8a179
--- /dev/null
+++ b/Gems/Atom/RPI/Code/Source/Platform/Common/MSVC/atom_rpi_public_msvc.cmake
@@ -0,0 +1,19 @@
+#
+# 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
+#
+#
+
+ly_add_source_properties(
+ SOURCES External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp
+ PROPERTY COMPILE_OPTIONS
+ VALUES /arch:AVX2 /W3
+)
+ly_add_source_properties(
+ SOURCES
+ External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp
+ External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp
+ PROPERTY COMPILE_OPTIONS
+ VALUES /W3
+)
diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake b/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake
index 896f505d05..a9e3cf10f9 100644
--- a/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake
+++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake
@@ -7,16 +7,3 @@
set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED TRUE)
set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED TRUE)
-
-ly_add_source_properties(
- SOURCES External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp
- PROPERTY COMPILE_OPTIONS
- VALUES /arch:AVX2 /W3
-)
-ly_add_source_properties(
- SOURCES
- External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp
- External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp
- PROPERTY COMPILE_OPTIONS
- VALUES /W3
-)
\ No newline at end of file
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp
index 9d3a776b80..dfe470c3f8 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp
@@ -19,6 +19,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -36,8 +37,7 @@
namespace AZ
{
namespace RPI
- {
-
+ {
// --- Constructors ---
Pass::Pass(const PassDescriptor& descriptor)
@@ -1285,9 +1285,15 @@ namespace AZ
CreateTransientAttachments(params.m_frameGraphBuilder->GetAttachmentDatabase());
ImportAttachments(params.m_frameGraphBuilder->GetAttachmentDatabase());
+ // readback attachment with input state
+ UpdateReadbackAttachment(params, true);
+
// FrameBeginInternal needs to be the last function be called in FrameBegin because its implementation expects
// all the attachments are imported to database (for example, ImageAttachmentPreview)
FrameBeginInternal(params);
+
+ // readback attachment with output state
+ UpdateReadbackAttachment(params, false);
UpdateConnectedOutputBindings();
}
@@ -1426,6 +1432,57 @@ namespace AZ
m_flags.m_pipelineStatisticsQueryEnabled = enable;
}
+ bool Pass::ReadbackAttachment(AZStd::shared_ptr readback, const Name& slotName, PassAttachmentReadbackOption option)
+ {
+ // Return false if it's already readback
+ if (m_attachmentReadback)
+ {
+ AZ_Warning("Pass", false, "ReadbackAttachment: skip readback pass [%s] slot [%s]because there is an another active readback", m_name.GetCStr(), slotName.GetCStr());
+ return false;
+ }
+ uint32_t bindingIndex = 0;
+ for (auto& binding : m_attachmentBindings)
+ {
+ if (slotName == binding.m_name)
+ {
+ RHI::AttachmentType type = binding.m_attachment->GetAttachmentType();
+ if (type == RHI::AttachmentType::Buffer || type == RHI::AttachmentType::Image)
+ {
+ RHI::AttachmentId attachmentId = binding.m_attachment->GetAttachmentId();
+
+ // Append slot index and pass name so the read back's name won't be same as the attachment used in other passes.
+ AZStd::string readbackName = AZStd::string::format("%s_%d_%s", attachmentId.GetCStr(),
+ bindingIndex, GetName().GetCStr());
+ if (readback->ReadPassAttachment(binding.m_attachment.get(), AZ::Name(readbackName)))
+ {
+ m_readbackOption = PassAttachmentReadbackOption::Output;
+ // The m_readbackOption is only meaningful if the attachment is used for InputOutput.
+ if (binding.m_slotType == PassSlotType::InputOutput)
+ {
+ m_readbackOption = option;
+ }
+ m_attachmentReadback = readback;
+ return true;
+ }
+ return false;
+ }
+ }
+ bindingIndex++;
+ }
+ AZ_Warning("Pass", false, "ReadbackAttachment: failed to find slot [%s] from pass [%s]", slotName.GetCStr(), m_name.GetCStr());
+ return false;
+ }
+
+ void Pass::UpdateReadbackAttachment(FramePrepareParams params, bool beforeAddScopes)
+ {
+ if (beforeAddScopes == (m_readbackOption == PassAttachmentReadbackOption::Input) && m_attachmentReadback)
+ {
+ // Read the attachment for one frame. The reference can be released afterwards
+ m_attachmentReadback->FrameBegin(params);
+ m_attachmentReadback = nullptr;
+ }
+ }
+
bool Pass::IsTimestampQueryEnabled() const
{
return m_flags.m_timestampQueryEnabled;
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp
index f82d4bd9ef..96649573b2 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp
@@ -189,14 +189,8 @@ namespace AZ
{
SetScopeId(RHI::ScopeId(GetPathName()));
}
- params.m_frameGraphBuilder->ImportScopeProducer(*this);
- // Read the attachment for one frame. The reference can be released afterwards
- if (m_attachmentReadback)
- {
- m_attachmentReadback->FrameBegin(params);
- m_attachmentReadback = nullptr;
- }
+ params.m_frameGraphBuilder->ImportScopeProducer(*this);
// Read back the ScopeQueries submitted from previous frames
ReadbackScopeQueryResults();
@@ -415,31 +409,6 @@ namespace AZ
m_flags.m_hasPipelineViewTag = !viewTag.IsEmpty();
}
- void RenderPass::ReadbackAttachment(AZStd::shared_ptr readback, const PassAttachment* attachment)
- {
- m_attachmentReadback = readback;
-
- uint32_t bindingIndex = 0;
- for (auto& binding : m_attachmentBindings)
- {
- if (attachment == binding.m_attachment)
- {
- RHI::AttachmentType type = binding.m_attachment->GetAttachmentType();
- if (type == RHI::AttachmentType::Buffer || type == RHI::AttachmentType::Image)
- {
- RHI::AttachmentId attachmentId = binding.m_attachment->GetAttachmentId();
-
- // Append slot index and pass name so the read back's name won't be same as the attachment used in other passes.
- AZStd::string readbackName = AZStd::string::format("%s_%d_%s", attachmentId.GetCStr(),
- bindingIndex, GetName().GetCStr());
- m_attachmentReadback->ReadPassAttachment(binding.m_attachment.get(), AZ::Name(readbackName));
- return;
- }
- }
- bindingIndex++;
- }
- }
-
TimestampResult RenderPass::GetTimestampResultInternal() const
{
return m_timestampResult;
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp
index 0241d6ec25..5ee2c0a440 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp
@@ -333,22 +333,16 @@ namespace AZ
m_passSrg->Compile();
m_passSrgChanged = false;
}
-
- // Read preview output
- if (m_readback)
- {
- m_readback->FrameBegin(params);
- m_readback = nullptr;
- }
}
bool ImageAttachmentPreviewPass::ReadbackOutput(AZStd::shared_ptr readback)
{
if (m_outputColorAttachment)
{
- m_readback = readback;
+ m_readbackOption = PassAttachmentReadbackOption::Output;
+ m_attachmentReadback = readback;
AZStd::string readbackName = AZStd::string::format("%s_%s", m_outputColorAttachment->GetAttachmentId().GetCStr(), GetName().GetCStr());
- return m_readback->ReadPassAttachment(m_outputColorAttachment.get(), AZ::Name(readbackName));
+ return m_attachmentReadback->ReadPassAttachment(m_outputColorAttachment.get(), AZ::Name(readbackName));
}
return false;
}
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/RenderToTexturePass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/RenderToTexturePass.cpp
index 8b4c9b8aab..c532a1324f 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/RenderToTexturePass.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/RenderToTexturePass.cpp
@@ -8,6 +8,7 @@
#include
#include
+#include
#include
#include
#include
@@ -77,17 +78,6 @@ namespace AZ
params.m_viewportState = m_viewport;
Base::FrameBeginInternal(params);
-
- // for read back output
- if (m_readback)
- {
- m_readback->FrameBegin(params);
- if (m_readback->IsFinished())
- {
- // Done reading. Remove the reference
- m_readback = nullptr;
- }
- }
}
void RenderToTexturePass::ResizeOutput(uint32_t width, uint32_t height)
@@ -118,9 +108,10 @@ namespace AZ
{
if (m_outputAttachment)
{
- m_readback = readback;
+ m_readbackOption = PassAttachmentReadbackOption::Output;
+ m_attachmentReadback = readback;
AZStd::string readbackName = AZStd::string::format("%s_%s", m_outputAttachment->GetAttachmentId().GetCStr(), GetName().GetCStr());
- m_readback->ReadPassAttachment(m_outputAttachment.get(), AZ::Name(readbackName));
+ m_attachmentReadback->ReadPassAttachment(m_outputAttachment.get(), AZ::Name(readbackName));
}
}
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/SwapChainPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/SwapChainPass.cpp
index 7a3bf3a011..62649cd0b2 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/SwapChainPass.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/SwapChainPass.cpp
@@ -8,6 +8,7 @@
#include
#include
+#include
#include
#include
#include
@@ -134,13 +135,6 @@ namespace AZ
attachmentDatabase.ImportSwapChain(m_windowContext->GetSwapChainAttachmentId(), m_windowContext->GetSwapChain());
ParentPass::FrameBeginInternal(params);
-
- // Read swap chain for one frame. The reference can be released afterwards
- if (m_swapChainReadback)
- {
- m_swapChainReadback->FrameBegin(params);
- m_swapChainReadback = nullptr;
- }
}
void SwapChainPass::OnWindowResized([[maybe_unused]] uint32_t width, [[maybe_unused]] uint32_t height)
@@ -152,9 +146,11 @@ namespace AZ
{
if (m_swapChainAttachment)
{
- m_swapChainReadback = readback;
+ m_readbackOption = PassAttachmentReadbackOption::Output;
+ m_attachmentReadback = readback;
+
AZStd::string readbackName = AZStd::string::format("%s_%s", m_swapChainAttachment->GetAttachmentId().GetCStr(), GetName().GetCStr());
- m_swapChainReadback->ReadPassAttachment(m_swapChainAttachment.get(), AZ::Name(readbackName));
+ m_attachmentReadback->ReadPassAttachment(m_swapChainAttachment.get(), AZ::Name(readbackName));
}
}
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp
index 23d5805c12..eacdbe0293 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContext.cpp
@@ -28,6 +28,10 @@ namespace AZ
m_viewportSize,
nativeWindow,
&AzFramework::WindowRequestBus::Events::GetClientAreaSize);
+ AzFramework::WindowRequestBus::EventResult(
+ m_viewportDpiScaleFactor,
+ nativeWindow,
+ &AzFramework::WindowRequestBus::Events::GetDpiScaleFactor);
AzFramework::WindowNotificationBus::Handler::BusConnect(nativeWindow);
AzFramework::ViewportRequestBus::Handler::BusConnect(id);
@@ -148,11 +152,21 @@ namespace AZ
return m_viewportSize;
}
+ float ViewportContext::GetDpiScalingFactor() const
+ {
+ return m_viewportDpiScaleFactor;
+ }
+
void ViewportContext::ConnectSizeChangedHandler(SizeChangedEvent::Handler& handler)
{
handler.Connect(m_sizeChangedEvent);
}
+ void ViewportContext::ConnectDpiScalingFactorChangedHandler(ScalarChangedEvent::Handler& handler)
+ {
+ handler.Connect(m_dpiScalingFactorChangedEvent);
+ }
+
void ViewportContext::ConnectViewMatrixChangedHandler(MatrixChangedEvent::Handler& handler)
{
handler.Connect(m_viewMatrixChangedEvent);
@@ -289,5 +303,11 @@ namespace AZ
m_sizeChangedEvent.Signal(m_viewportSize);
}
}
+
+ void ViewportContext::OnDpiScaleFactorChanged(float dpiScaleFactor)
+ {
+ m_viewportDpiScaleFactor = dpiScaleFactor;
+ m_dpiScalingFactorChangedEvent.Signal(dpiScaleFactor);
+ }
} // namespace RPI
} // namespace AZ
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContextManager.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContextManager.cpp
index 552304557f..606b5e5b8c 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContextManager.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Public/ViewportContextManager.cpp
@@ -55,16 +55,28 @@ namespace AZ
auto onSizeChanged = [this, viewportId](AzFramework::WindowSize size)
{
// Ensure we emit OnViewportSizeChanged with the correct name.
- auto viewportContext = this->GetViewportContextById(viewportId);
+ auto viewportContext = GetViewportContextById(viewportId);
if (viewportContext)
{
ViewportContextNotificationBus::Event(viewportContext->GetName(), &ViewportContextNotificationBus::Events::OnViewportSizeChanged, size);
}
ViewportContextIdNotificationBus::Event(viewportId, &ViewportContextIdNotificationBus::Events::OnViewportSizeChanged, size);
};
+ auto onDpiScalingChanged = [this, viewportId](float dpiScalingFactor)
+ {
+ // Ensure we emit OnViewportDpiScalingChanged with the correct name.
+ auto viewportContext = GetViewportContextById(viewportId);
+ if (viewportContext)
+ {
+ ViewportContextNotificationBus::Event(viewportContext->GetName(), &ViewportContextNotificationBus::Events::OnViewportDpiScalingChanged, dpiScalingFactor);
+ }
+ ViewportContextIdNotificationBus::Event(viewportId, &ViewportContextIdNotificationBus::Events::OnViewportDpiScalingChanged, dpiScalingFactor);
+ };
viewportContext->m_name = contextName;
viewportData.sizeChangedHandler = ViewportContext::SizeChangedEvent::Handler(onSizeChanged);
+ viewportData.dpiScalingChangedHandler = ViewportContext::ScalarChangedEvent::Handler(onDpiScalingChanged);
viewportContext->ConnectSizeChangedHandler(viewportData.sizeChangedHandler);
+ viewportContext->ConnectDpiScalingFactorChangedHandler(viewportData.dpiScalingChangedHandler);
ViewPtrStack& associatedViews = GetOrCreateViewStackForContext(contextName);
viewportContext->SetDefaultView(associatedViews.back());
onSizeChanged(viewportContext->GetViewportSize());
@@ -176,6 +188,7 @@ namespace AZ
UpdateViewForContext(newContextName);
// Ensure anyone listening on per-name viewport size updates gets notified.
ViewportContextNotificationBus::Event(newContextName, &ViewportContextNotificationBus::Events::OnViewportSizeChanged, viewportContext->GetViewportSize());
+ ViewportContextNotificationBus::Event(newContextName, &ViewportContextNotificationBus::Events::OnViewportDpiScalingChanged, viewportContext->GetDpiScalingFactor());
}
void ViewportContextManager::EnumerateViewportContexts(AZStd::function visitorFunction)
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelKdTree.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelKdTree.cpp
index 54ec002893..ace4df5c0b 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelKdTree.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelKdTree.cpp
@@ -84,7 +84,11 @@ namespace AZ
// If either the top or bottom contain all the input indices, the triangles are too close to cut any
// further and the split failed
- return indices.size() != outInfo.m_aboveIndices.size() && indices.size() != outInfo.m_belowIndices.size();
+ // Additionally, if too many triangles straddle the split-axis,
+ // the triangles are too close and the split failed
+ // [ATOM-15944] - Use a more sophisticated method to terminate KdTree generation
+ return indices.size() != outInfo.m_aboveIndices.size() && indices.size() != outInfo.m_belowIndices.size()
+ && aznumeric_cast(outInfo.m_aboveIndices.size() + outInfo.m_belowIndices.size()) / aznumeric_cast(indices.size()) < s_MaximumSplitAxisStraddlingTriangles;
}
bool ModelKdTree::Build(const ModelAsset* model)
diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h
index c649031280..d8171a83a1 100644
--- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h
+++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -81,6 +82,12 @@ namespace AtomToolsFramework
//! Gets the default camera that's been automatically registered to our ViewportContext.
AZ::RPI::ViewPtr GetDefaultCamera();
AZ::RPI::ConstViewPtr GetDefaultCamera() const;
+ //! Sets whether or not input processing is enabled for this RenderViewportWidget.
+ //! While input processing is enabled, synthetic input events may appear in OnInputChannelEventFiltered
+ //! due to internal viewport input mapping via QtEventToAzInputMapper, so it may be desirable to disable
+ //! camera controller input processing wholesale to avoid competing input messages.
+ //! Input processing is enabled by default.
+ void SetInputProcessingEnabled(bool enabled);
// AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler ...
AzFramework::CameraState GetCameraState() override;
@@ -113,6 +120,7 @@ namespace AtomToolsFramework
void SetFullScreenState(bool fullScreenState) override;
bool CanToggleFullScreenState() const override;
void ToggleFullScreenState() override;
+ float GetDpiScaleFactor() const override;
protected:
// AzFramework::InputChannelEventListener ...
@@ -130,7 +138,6 @@ namespace AtomToolsFramework
private:
void SendWindowResizeEvent();
- bool CanInputGrantFocus(const AzFramework::InputChannel& inputChannel) const;
// The underlying ViewportContext, our entry-point to the Atom RPI.
AZ::RPI::ViewportContextPtr m_viewportContext;
@@ -157,5 +164,7 @@ namespace AtomToolsFramework
AZStd::optional m_lastCursorPosition;
// The viewport settings (e.g. grid snapping, grid size) for this viewport.
const AzToolsFramework::ViewportInteraction::ViewportSettings* m_viewportSettings = nullptr;
+ // Maps our internal Qt events into AzFramework InputChannels for our ViewportControllerList.
+ AzToolsFramework::QtEventToAzInputMapper* m_inputChannelMapper = nullptr;
};
} //namespace AtomToolsFramework
diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp
index 529652e1a9..6ab96bbebb 100644
--- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp
+++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp
@@ -81,6 +81,23 @@ namespace AtomToolsFramework
AZ::TickBus::Handler::BusConnect();
AzFramework::WindowRequestBus::Handler::BusConnect(params.windowHandle);
+ m_inputChannelMapper = new AzToolsFramework::QtEventToAzInputMapper(this, id);
+
+ // Forward input events to our controller list.
+ QObject::connect(m_inputChannelMapper, &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, this,
+ [this](const AzFramework::InputChannel* inputChannel, QEvent* event)
+ {
+ AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId());
+ if (m_controllerList->HandleInputChannelEvent({GetId(), windowId, *inputChannel}))
+ {
+ // If the controller handled the input event, mark the event as accepted so it doesn't continue to propagate.
+ if (event)
+ {
+ event->setAccepted(true);
+ }
+ }
+ });
+
return true;
}
@@ -149,86 +166,24 @@ namespace AtomToolsFramework
return m_defaultCamera;
}
- static bool IsMouseButtonEvent(const AzFramework::InputChannel& inputChannel)
- {
- const auto& mouseButtons = AzFramework::InputDeviceMouse::Button::All;
- return AZStd::find(mouseButtons.begin(), mouseButtons.end(), inputChannel.GetInputChannelId()) != mouseButtons.end();
- }
-
- static bool IsMouseMoveEvent(const AzFramework::InputChannel& inputChannel)
- {
- return inputChannel.GetInputChannelId() == AzFramework::InputDeviceMouse::SystemCursorPosition;
- }
-
- static bool IsMouseButtonOrWheelEvent(const AzFramework::InputChannel& inputChannel)
- {
- return IsMouseButtonEvent(inputChannel) || inputChannel.GetInputChannelId() == AzFramework::InputDeviceMouse::Movement::Z;
- }
-
- bool RenderViewportWidget::CanInputGrantFocus(const AzFramework::InputChannel& inputChannel) const
- {
- // Only take focus from a mouse event if the cursor is currently within the viewport
- if (!m_mouseOver)
- {
- return false;
- }
-
- // Only mouse button down events (clicks) can grant focus
- if (inputChannel.GetState() != AzFramework::InputChannel::State::Began)
- {
- return false;
- }
-
- // Only mouse button events can grant focus
- return IsMouseButtonEvent(inputChannel);
- }
-
bool RenderViewportWidget::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
{
- bool shouldConsumeEvent = true;
-
- // Grab keyboard focus if we've been clicked on.
- // Qt normally handles this for us, but we're filtering native events before they get
- // synthesized into QMouseEvents.
- if (!hasFocus() && CanInputGrantFocus(inputChannel))
- {
- setFocus();
- }
-
- // Don't consume new input events if we don't currently have focus.
- // We do forward Ended events, as they may be relevant to our current state
- // (e.g. a key gets released after we lose focus, it shouldn't remain "stuck").
if (!hasFocus())
{
- if (inputChannel.GetState() == AzFramework::InputChannel::State::Ended)
- {
- // Forward the input ended event to our controllers, but don't prevent other viewports from receiving it.
- shouldConsumeEvent = false;
- }
- else
- {
- // Not an event we should listen to, abort.
- return false;
- }
+ return false;
}
- // If we receive a mouse button event from outside of our viewport, ignore it even if we have focus.
- if (!m_mouseOver
- && inputChannel.GetState() == AzFramework::InputChannel::State::Began
- && IsMouseButtonOrWheelEvent(inputChannel))
+ // Only forward channels that aren't covered by our Qt -> AZ event mapper
+ if (!m_inputChannelMapper || m_inputChannelMapper->HandlesInputEvent(inputChannel))
{
return false;
}
- // Don't forward system cursor position updates, we'll do that ourselves for in-window movements once the result of
- // ViewportCursorScreenPosition is guaranteed to be correct (see mouseMoveEvent).
- if (IsMouseMoveEvent(inputChannel))
- {
- return false;
- }
+ bool shouldConsumeEvent = true;
AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId());
const bool eventHandled = m_controllerList->HandleInputChannelEvent({GetId(), windowId, inputChannel});
+
// If our controllers handled the event and it's one we can safely consume (i.e. it's not an Ended event that other viewports might need), consume it.
return eventHandled && shouldConsumeEvent;
}
@@ -262,6 +217,7 @@ namespace AtomToolsFramework
{
SendWindowResizeEvent();
}
+
return QWidget::event(event);
}
@@ -279,23 +235,12 @@ namespace AtomToolsFramework
{
m_mousePosition = event->localPos();
- // Now that we've looked a viewport local mouse position,
- // we can go ahead and broadcast the system cursor input event to the controllers.
- // This allows any controllers not listening to pure mouse deltas to consistently
- // look up the mouse position in viewport screen coordinates.
- const AzFramework::InputDevice* mouseInputDevice = nullptr;
- if (AzFramework::InputDeviceRequestBus::EventResult(
- mouseInputDevice, AzFramework::InputDeviceMouse::Id, &AzFramework::InputDeviceRequests::GetInputDevice);
- mouseInputDevice != nullptr)
- {
- const AzFramework::NativeWindowHandle windowId = reinterpret_cast(winId());
- AzFramework::InputChannel syntheticInput(AzFramework::InputDeviceMouse::SystemCursorPosition, *mouseInputDevice);
- m_controllerList->HandleInputChannelEvent({GetId(), windowId, syntheticInput});
- }
-
if (m_capturingCursor && m_lastCursorPosition.has_value())
{
AzQtComponents::SetCursorPos(m_lastCursorPosition.value());
+ // Even though we just set the cursor position, there are edge cases such as remote desktop that will leave
+ // the cursor position unchanged. For safety, we re-cache our last cursor position for delta generation.
+ m_lastCursorPosition = QCursor::pos();
}
else
{
@@ -354,6 +299,12 @@ namespace AtomToolsFramework
}
}
+ void RenderViewportWidget::SetInputProcessingEnabled(bool enabled)
+ {
+ m_inputChannelMapper->SetEnabled(enabled);
+ m_controllerList->SetEnabled(enabled);
+ }
+
AzFramework::CameraState RenderViewportWidget::GetCameraState()
{
AZ::RPI::ViewPtr currentView = m_viewportContext->GetDefaultView();
@@ -540,4 +491,9 @@ namespace AtomToolsFramework
{
// The RenderViewportWidget does not currently support full screen.
}
+
+ float RenderViewportWidget::GetDpiScaleFactor() const
+ {
+ return aznumeric_cast(devicePixelRatioF());
+ }
} //namespace AtomToolsFramework
diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h
index 1dd2622603..0c019f19aa 100644
--- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h
+++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h
@@ -7,8 +7,12 @@
#pragma once
-#include
+#include
+#include
+
#include
+#include
+
namespace AZ
{
@@ -19,24 +23,41 @@ namespace AZ
namespace Render
{
- struct ThreadRegionEntry
+ struct ThreadRegionEntry
{
AZStd::thread_id m_threadId;
AZStd::sys_time_t m_startTick = 0;
AZStd::sys_time_t m_endTick = 0;
};
+ // Stores data about a region that is agreggated from all collected frames
+ // Data collection can be toggled on and off through m_record.
+ struct RegionStatistics
+ {
+ float CalcAverageTimeMs() const;
+ void RecordRegion(const AZ::RHI::CachedTimeRegion& region);
+
+ bool m_draw = false;
+ bool m_record = true;
+ u64 m_invocations = 0;
+ AZStd::sys_time_t m_totalTicks = 0;
+ };
+
//! Visual profiler for Cpu statistics.
//! It uses ImGui as the library for displaying the Attachments and Heaps.
//! It shows all heaps that are being used by the RHI and how the
//! resources are allocated in each heap.
class ImGuiCpuProfiler
+ : SystemTickBus::Handler
{
// Region Name -> Array of ThreadRegion entries
using RegionEntryMap = AZStd::map>;
// Group Name -> RegionEntryMap
using GroupRegionMap = AZStd::map;
+ using TimeRegion = AZ::RHI::CachedTimeRegion;
+ using GroupRegionName = AZ::RHI::CachedTimeRegion::GroupRegionName;
+
public:
ImGuiCpuProfiler() = default;
~ImGuiCpuProfiler() = default;
@@ -44,7 +65,13 @@ namespace AZ
//! Draws the provided Cpu statistics.
void Draw(bool& keepDrawing, const AZ::RHI::CpuTimingStatistics& cpuTimingStatistics);
+ //! Draws the CPU profiling visualizer in a new window.
+ void DrawVisualizer(bool& keepDrawing, const AZ::RHI::CpuTimingStatistics& currentCpuTimingStatistics);
+
private:
+ static constexpr float RowHeight = 50.0;
+ static constexpr int DefaultFramesToCollect = 50;
+
// Update the GroupRegionMap with the latest cached time regions
void UpdateGroupRegionMap();
@@ -62,8 +89,73 @@ namespace AZ
AZ::RHI::CpuTimingStatistics m_cpuTimingStatisticsWhenPause;
AZStd::string m_lastCapturedFilePath;
+
+ // Visualizer methods
+
+ // Get the profiling data from the last frame, only called when the profiler is not paused.
+ void CollectFrameData();
+
+ // Cull old data from internal storage, only called when profiler is not paused.
+ void CullFrameData(const AZ::RHI::CpuTimingStatistics& currentCpuTimingStatistics);
+
+ // Draws a single block onto the timeline
+ void DrawBlock(const TimeRegion& block, u64 targetRow);
+
+ // Draw horizontal lines between threads in the timeline
+ void DrawThreadSeparator(u64 threadBoundary, u64 maxDepth);
+
+ // Draw the "Thread XXXXX" label onto the viewport
+ void DrawThreadLabel(u64 baseRow, AZStd::thread_id threadId);
+
+ // Draws all active function statistics windows
+ void DrawRegionStatistics();
+
+ // Draw the vertical lines separating frames in the timeline
+ void DrawFrameBoundaries();
+
+ // Draw the ruler with frame time labels
+ void DrawRuler();
+
+ // Converts raw ticks to a pixel value suitable to give to ImDrawList, handles window scrolling
+ float ConvertTickToPixelSpace(AZStd::sys_time_t tick) const;
+
+ AZStd::sys_time_t GetViewportTickWidth() const;
+
+ // Gets the color for a block using the GroupRegionName as a key into the cache
+ // Generates a random ImU32 if the block does not yet have a color
+ ImU32 GetBlockColor(const TimeRegion& block);
+
+ // System tick bus overrides
+ virtual void OnSystemTick() override;
+
+ // Visualizer state
+
+ bool m_showVisualizer = false;
+
+ int m_framesToCollect = DefaultFramesToCollect;
+
+ // Tally of the number of saved profiling events so far
+ u64 m_savedRegionCount = 0;
+
+ // Viewport tick bounds, these are used to convert tick space -> screen space and cull so we only draw onscreen objects
+ AZStd::sys_time_t m_viewportStartTick;
+ AZStd::sys_time_t m_viewportEndTick;
+
+ // Map to store each thread's TimeRegions, individual vectors are sorted by start tick
+ AZStd::unordered_map> m_savedData;
+
+ // Region color cache
+ AZStd::unordered_map m_regionColorMap;
+
+ // Tracks the frame boundaries
+ AZStd::vector m_frameEndTicks = { INT64_MIN };
+
+ // Main data structure for storing function statistics to be shown in the popup windows.
+ // For now we default allocate for all regions on the first render frame and then use RegionStatistics.m_draw to determine
+ // if we should draw the window or not. FIXME(ATOM-15948) this should be changed once RegionStatistics gets heavier.
+ AZStd::unordered_map m_regionStatisticsMap;
};
} // namespace Render
-}
+} // namespace AZ
#include "ImGuiCpuProfiler.inl"
diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl
index 7551cf50dc..e349685fd3 100644
--- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl
+++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl
@@ -6,10 +6,16 @@
*/
#include
+#include
+#include
#include
+
#include
-#include
+#include
#include
+#include
+#include
+
namespace AZ
{
@@ -19,17 +25,15 @@ namespace AZ
{
// NOTE: Fix build error in case AZStd::thread_id is not of an arithmetic type, and instead a pointer
template::value>::type* = nullptr>
- void TextThreadId(ThreadId threadId)
+ AZStd::string TextThreadId(ThreadId threadId)
{
- const AZStd::string threadIdText = AZStd::string::format("Thread: %p", threadId);
- ImGui::Text(threadIdText.c_str());
+ return AZStd::string::format("Thread: %p", threadId);
}
template::value>::type* = nullptr>
- void TextThreadId(ThreadId threadId)
+ AZStd::string TextThreadId(ThreadId threadId)
{
- const AZStd::string threadIdText = AZStd::string::format("Thread: %zu", static_cast(threadId));
- ImGui::Text(threadIdText.c_str());
+ return AZStd::string::format("Thread: %zu", static_cast(threadId));
}
inline float TicksToMs(AZStd::sys_time_t ticks)
{
@@ -38,14 +42,14 @@ namespace AZ
AZ_Assert(ticksPerSecond >= 1000, "Error in converting ticks to ms, expected ticksPerSecond >= 1000");
return static_cast((ticks * 1000) / (ticksPerSecond / 1000)) / 1000.0f;
}
- }
+ } // namespace CpuProfilerImGuiHelper
inline void ImGuiCpuProfiler::Draw(bool& keepDrawing, const AZ::RHI::CpuTimingStatistics& currentCpuTimingStatistics)
{
// Cache the value to detect if it was changed by ImGui(user pressed 'x')
const bool cachedShowCpuProfiler = keepDrawing;
- const ImVec2 windowSize(640.0f, 480.0f);
+ const ImVec2 windowSize(900.0f, 600.0f);
ImGui::SetNextWindowSize(windowSize, ImGuiCond_Once);
bool captureToFile = false;
if (ImGui::Begin("Cpu Profiler", &keepDrawing, ImGuiWindowFlags_None))
@@ -102,7 +106,7 @@ namespace AZ
for (ThreadRegionEntry& entry : entries)
{
- CpuProfilerImGuiHelper::TextThreadId(entry.m_threadId.m_id);
+ ImGui::Text(CpuProfilerImGuiHelper::TextThreadId(entry.m_threadId.m_id).c_str());
const AZStd::sys_time_t elapsed = entry.m_endTick - entry.m_startTick;
ShowTimeInMs(elapsed);
@@ -114,9 +118,9 @@ namespace AZ
}
};
- const auto ShowRegionRow = [ticksPerSecond, &DrawRegionHoverMarker, &ShowTimeInMs](const char* regionLabel,
- AZStd::vector regions,
- AZStd::sys_time_t duration)
+ const auto ShowRegionRow =
+ [ticksPerSecond, &DrawRegionHoverMarker,
+ &ShowTimeInMs](const char* regionLabel, AZStd::vector regions, AZStd::sys_time_t duration)
{
// Draw the region label
ImGui::Text(regionLabel);
@@ -142,14 +146,15 @@ namespace AZ
ImGui::NextColumn();
// Draw the time labels (max and then total)
- const AZStd::string timeLabel =
- AZStd::string::format("%.2f ms max, %.2f ms total",
- CpuProfilerImGuiHelper::TicksToMs(duration),
+ const AZStd::string timeLabel = AZStd::string::format(
+ "%.2f ms max, %.2f ms total", CpuProfilerImGuiHelper::TicksToMs(duration),
CpuProfilerImGuiHelper::TicksToMs(totalTime));
ImGui::Text(timeLabel.c_str());
ImGui::NextColumn();
};
+ ImGui::Checkbox("Enable Visualizer", &m_showVisualizer);
+
// Set column settings.
ImGui::Columns(2, "view", false);
ImGui::SetColumnWidth(0, 660.0f);
@@ -216,8 +221,8 @@ namespace AZ
char resolvedPath[AZ::IO::MaxPathLength];
AZ::IO::FileIOBase::GetInstance()->ResolvePath(frameDataFilePath.c_str(), resolvedPath, AZ::IO::MaxPathLength);
m_lastCapturedFilePath = resolvedPath;
- AZ::Render::ProfilingCaptureRequestBus::Broadcast(&AZ::Render::ProfilingCaptureRequestBus::Events::CaptureCpuProfilingStatistics,
- frameDataFilePath);
+ AZ::Render::ProfilingCaptureRequestBus::Broadcast(
+ &AZ::Render::ProfilingCaptureRequestBus::Events::CaptureCpuProfilingStatistics, frameDataFilePath);
}
// Toggle if the bool isn't the same as the cached value
@@ -225,6 +230,11 @@ namespace AZ
{
AZ::RHI::CpuProfiler::Get()->SetProfilerEnabled(keepDrawing);
}
+
+ if (m_showVisualizer)
+ {
+ DrawVisualizer(m_showVisualizer, currentCpuTimingStatistics);
+ }
}
inline void ImGuiCpuProfiler::UpdateGroupRegionMap()
@@ -251,5 +261,519 @@ namespace AZ
}
}
}
- }
-}
+
+ // -- CPU Visualizer --
+ inline void ImGuiCpuProfiler::DrawVisualizer(bool& keepDrawing, const AZ::RHI::CpuTimingStatistics& currentCpuTimingStatistics)
+ {
+ ImGui::SetNextWindowSize({ 900, 600 }, ImGuiCond_Once);
+ if (ImGui::Begin("CPU Visualizer", &keepDrawing, ImGuiWindowFlags_None))
+ {
+ // Get the instrumentation data for the last frame if active
+ if (!m_paused && m_groupRegionMap.size() != 0)
+ {
+ CollectFrameData(); // Also updates viewport bounds
+
+ CullFrameData(currentCpuTimingStatistics); // Trim data if necessary
+
+ if (!SystemTickBus::Handler::BusIsConnected())
+ {
+ SystemTickBus::Handler::BusConnect();
+ }
+ }
+
+ // Options & Statistics
+ if (ImGui::BeginChild("Options and Statistics", { 0, 0 }, true))
+ {
+ ImGui::Columns(3, "Options", true);
+ ImGui::Text("Frames To Collect:");
+ ImGui::SliderInt("", &m_framesToCollect, 10, 100, "%d", ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_Logarithmic);
+
+ ImGui::NextColumn();
+
+ ImGui::Text("Viewport width: %.3f ms", CpuProfilerImGuiHelper::TicksToMs(GetViewportTickWidth()));
+ ImGui::Text("Ticks [%lld , %lld]", m_viewportStartTick, m_viewportEndTick);
+ ImGui::Text("Recording %ld threads", RHI::CpuProfiler::Get()->GetTimeRegionMap().size());
+ ImGui::Text("%llu profiling events saved", m_savedRegionCount);
+
+ ImGui::NextColumn();
+
+ ImGui::TextWrapped(
+ "Hold the right mouse button to move around. Zoom by scrolling the mouse wheel while holding .");
+ }
+
+
+ ImGui::Columns(1, "RulerColumn", true);
+
+ // Ruler
+ if (ImGui::BeginChild("Ruler", { 0, 30 }, true, ImGuiWindowFlags_NoNavFocus))
+ {
+ DrawRuler();
+ }
+ ImGui::EndChild();
+
+
+ ImGui::Columns(1, "TimelineColumn", true);
+
+ // Timeline
+ if (ImGui::BeginChild(
+ "Timeline", { 0, 0 }, true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoScrollWithMouse))
+ {
+ // Find the next frame boundary after the viewport's right bound and draw until that tick
+ auto nextFrameBoundaryItr = AZStd::lower_bound(m_frameEndTicks.begin(), m_frameEndTicks.end(), m_viewportEndTick);
+ if (nextFrameBoundaryItr == m_frameEndTicks.end() &&
+ m_frameEndTicks.size() != 0) // lower_bound returns end() if not found
+ {
+ nextFrameBoundaryItr--;
+ }
+ const AZStd::sys_time_t nextFrameBoundary = *nextFrameBoundaryItr;
+
+ // Find the start tick of the leftmost frame, which may be offscreen.
+ auto startTickItr = AZStd::lower_bound(m_frameEndTicks.begin(), m_frameEndTicks.end(), m_viewportStartTick);
+ if (startTickItr != m_frameEndTicks.begin())
+ {
+ startTickItr--;
+ }
+
+ // Main draw loop
+ u64 baseRow = 0;
+ for (const auto& [currentThreadId, singleThreadData] : m_savedData)
+ {
+ // Find the first TimeRegion that we should draw
+ auto regionItr = AZStd::lower_bound(
+ singleThreadData.begin(), singleThreadData.end(), *startTickItr,
+ [](const TimeRegion& wrapper, AZStd::sys_time_t target)
+ {
+ return wrapper.m_startTick < target;
+ });
+
+ // Draw all of the blocks for a given thread/row
+ u64 maxDepth = 0;
+ while (regionItr != singleThreadData.end())
+ {
+ const TimeRegion& region = *regionItr;
+
+ // Early out if we have drawn all the onscreen regions
+ if (region.m_startTick > nextFrameBoundary)
+ {
+ break;
+ }
+ u64 targetRow = region.m_stackDepth + baseRow;
+ maxDepth = AZStd::max(aznumeric_cast(region.m_stackDepth), maxDepth);
+
+ DrawBlock(region, targetRow);
+
+ regionItr++;
+ }
+
+ // Draw UI details
+ DrawThreadLabel(baseRow, currentThreadId);
+ DrawThreadSeparator(baseRow, maxDepth);
+
+ baseRow += maxDepth + 1; // Next draw loop should start one row down
+ }
+
+ DrawRegionStatistics();
+ DrawFrameBoundaries();
+
+ // Draw an invisible button to capture inputs
+ ImGui::InvisibleButton("Timeline Input", { ImGui::GetWindowContentRegionWidth(), baseRow * RowHeight });
+
+ // Controls
+ ImGuiIO& io = ImGui::GetIO();
+ if (ImGui::IsWindowFocused() && ImGui::IsItemHovered())
+ {
+ io.WantCaptureMouse = true;
+ if (ImGui::IsMouseDragging(ImGuiMouseButton_Right)) // Scrolling
+ {
+ auto [deltaX, deltaY] = io.MouseDelta;
+ if (deltaX != 0 || deltaY != 0)
+ {
+ // We want to maintain uniformity in scrolling (a click and drag should leave the cursor at the same spot
+ // relative to the objects on screen)
+ const float pixelDeltaNormalized = deltaX / ImGui::GetWindowWidth();
+ auto tickDelta = aznumeric_cast(-1 * pixelDeltaNormalized * GetViewportTickWidth());
+ m_viewportStartTick += tickDelta;
+ m_viewportEndTick += tickDelta;
+
+ ImGui::SetScrollY(ImGui::GetScrollY() + deltaY * -1);
+ }
+ }
+ else if (io.MouseWheel != 0 && io.KeyCtrl) // Zooming
+ {
+ // We want zooming to be relative to the mouse's current position
+ const float mouseVel = io.MouseWheel;
+ const float mouseX = ImGui::GetMousePos().x;
+
+ // Find the normalized position of the cursor relative to the window
+ const float percentWindow = (mouseX - ImGui::GetWindowPos().x) / ImGui::GetWindowWidth();
+
+ const auto overallTickDelta = aznumeric_cast(0.05 * io.MouseWheel * GetViewportTickWidth());
+
+ // Split the overall delta between the two bounds depending on mouse pos
+ const auto newStartTick = m_viewportStartTick + aznumeric_cast(percentWindow * overallTickDelta);
+ const auto newEndTick = m_viewportEndTick - aznumeric_cast((1-percentWindow) * overallTickDelta);
+
+ // Avoid zooming too much, start tick should always be less than end tick
+ if (newStartTick < newEndTick)
+ {
+ m_viewportStartTick = newStartTick;
+ m_viewportEndTick = newEndTick;
+ }
+ }
+ }
+ }
+ ImGui::EndChild();
+ }
+ ImGui::End();
+ }
+
+ inline void ImGuiCpuProfiler::CollectFrameData()
+ {
+ const RHI::CpuProfiler::TimeRegionMap& timeRegionMap = RHI::CpuProfiler::Get()->GetTimeRegionMap();
+
+ m_viewportStartTick = INT64_MAX;
+ m_viewportEndTick = INT64_MIN;
+
+ // Iterate through the entire TimeRegionMap and copy the data since it will get deleted on the next frame
+ for (const auto& [threadId, singleThreadRegionMap] : timeRegionMap)
+ {
+ // The profiler can sometime return threads without any profiling events when dropping threads, FIXME(ATOM-15949)
+ if (singleThreadRegionMap.size() == 0)
+ {
+ continue;
+ }
+
+ // Now focus on just the data for the current thread
+ AZStd::vector newData;
+ newData.reserve(singleThreadRegionMap.size()); // Avoids reallocation in the normal case when each region only has one invocation
+ for (const auto& [regionName, regionVec] : singleThreadRegionMap)
+ {
+ for (const TimeRegion& region : regionVec)
+ {
+ newData.push_back(region); // Copies
+
+ // Update running statistics if we want to record this region's data
+ if (m_regionStatisticsMap[region.m_groupRegionName].m_record)
+ {
+ m_regionStatisticsMap[region.m_groupRegionName].RecordRegion(region);
+ }
+ }
+ }
+
+ // Sorting by start tick allows us to speed up some other processes (ex. finding the first block to draw)
+ // since we can binary search by start tick.
+ AZStd::sort(
+ newData.begin(), newData.end(),
+ [](const TimeRegion& lhs, const TimeRegion& rhs)
+ {
+ return lhs.m_startTick < rhs.m_startTick;
+ });
+
+ // Use the latest frame's data as the new bounds of the viewport
+ m_viewportStartTick = AZStd::min(newData.front().m_startTick, m_viewportStartTick);
+ m_viewportEndTick = AZStd::max(newData.back().m_endTick, m_viewportEndTick);
+
+ m_savedRegionCount += newData.size();
+
+ // Move onto the end of the current thread's saved data, sorted order maintained
+ AZStd::vector& savedDataVec = m_savedData[threadId];
+ savedDataVec.insert(
+ savedDataVec.end(), AZStd::make_move_iterator(newData.begin()), AZStd::make_move_iterator(newData.end()));
+ }
+ }
+
+ inline void ImGuiCpuProfiler::CullFrameData(const AZ::RHI::CpuTimingStatistics& currentCpuTimingStatistics)
+ {
+ const AZStd::sys_time_t frameToFrameTime = currentCpuTimingStatistics.m_frameToFrameTime;
+ const AZStd::sys_time_t deleteBeforeTick = AZStd::GetTimeNowTicks() - frameToFrameTime * m_framesToCollect;
+
+ // Remove old frame boundary data
+ auto firstBoundaryToKeepItr = AZStd::upper_bound(m_frameEndTicks.begin(), m_frameEndTicks.end(), deleteBeforeTick);
+ m_frameEndTicks.erase(m_frameEndTicks.begin(), firstBoundaryToKeepItr);
+
+ // Remove old region data for each thread
+ for (auto& [threadId, savedRegions] : m_savedData)
+ {
+ AZStd::size_t sizeBeforeRemove = savedRegions.size();
+
+ auto firstRegionToKeep = AZStd::lower_bound(
+ savedRegions.begin(), savedRegions.end(), deleteBeforeTick,
+ [](const TimeRegion& region, AZStd::sys_time_t target)
+ {
+ return region.m_startTick < target;
+ });
+ savedRegions.erase(savedRegions.begin(), firstRegionToKeep);
+
+ m_savedRegionCount -= sizeBeforeRemove - savedRegions.size();
+ }
+ }
+
+ inline void ImGuiCpuProfiler::DrawBlock(const TimeRegion& block, u64 targetRow)
+ {
+ float wy = ImGui::GetWindowPos().y - ImGui::GetScrollY();
+
+ ImDrawList* drawList = ImGui::GetWindowDrawList();
+
+ const float startPixel = ConvertTickToPixelSpace(block.m_startTick);
+ const float endPixel = ConvertTickToPixelSpace(block.m_endTick);
+
+ const ImVec2 startPoint = { startPixel, wy + targetRow * RowHeight };
+ const ImVec2 endPoint = { endPixel, wy + targetRow * RowHeight + 40 };
+
+ const ImU32 blockColor = GetBlockColor(block);
+
+ drawList->AddRectFilled(startPoint, endPoint, blockColor, 0);
+
+ // Draw the region name if possible
+ // If the block's current width is too small, we skip drawing the label.
+ const float regionPixelWidth = endPixel - startPixel;
+ const float maxCharWidth = ImGui::CalcTextSize("M").x; // M is usually the largest character in most fonts (see CSS em)
+ if (regionPixelWidth > maxCharWidth) // We can draw at least one character
+ {
+ const AZStd::string label =
+ AZStd::string::format("%s/ %s", block.m_groupRegionName->m_groupName, block.m_groupRegionName->m_regionName);
+ const float textWidth = ImGui::CalcTextSize(label.c_str()).x;
+
+ if (regionPixelWidth < textWidth) // Not enough space in the block to draw the whole name, draw clipped text.
+ {
+ // clipRect appears to only clip when a character is fully outside of its bounds which can lead to overflow
+ // for now subtract the width of a character
+ const ImVec4 clipRect = { startPoint.x, startPoint.y, endPoint.x - maxCharWidth, endPoint.y };
+ const float fontSize = ImGui::GetFont()->FontSize;
+
+ ImGui::GetFont()->RenderText(drawList, fontSize, startPoint, IM_COL32_WHITE, clipRect, label.c_str(), 0);
+ }
+ else // We have enough space to draw the entire label, draw and center text.
+ {
+ const float remainingWidth = regionPixelWidth - textWidth;
+ const float offset = remainingWidth * .5;
+
+ drawList->AddText({ startPoint.x + offset, startPoint.y }, IM_COL32_WHITE, label.c_str());
+ }
+ }
+
+ // Tooltip and block highlighting
+ if (ImGui::IsMouseHoveringRect(startPoint, endPoint) && ImGui::IsWindowHovered())
+ {
+ // Open function statistics map on click
+ if (ImGui::IsMouseClicked(ImGuiMouseButton_Left))
+ {
+ const GroupRegionName* key = block.m_groupRegionName;
+ m_regionStatisticsMap[key].m_draw = true;
+ }
+
+ // Hovering outline
+ drawList->AddRect(startPoint, endPoint, ImGui::GetColorU32({ 1, 1, 1, 1 }), 0.0, 0, 1.5);
+
+ ImGui::BeginTooltip();
+ ImGui::Text("%s::%s", block.m_groupRegionName->m_groupName, block.m_groupRegionName->m_regionName);
+ ImGui::Text("Execution time: %.3f ms", CpuProfilerImGuiHelper::TicksToMs(block.m_endTick - block.m_startTick));
+ ImGui::Text("Ticks %lld => %lld", block.m_startTick, block.m_endTick);
+ ImGui::EndTooltip();
+ }
+ }
+
+ inline ImU32 ImGuiCpuProfiler::GetBlockColor(const TimeRegion& block)
+ {
+ // Use the GroupRegionName pointer a key into the cache, equal regions will have equal pointers
+ const GroupRegionName* key = block.m_groupRegionName;
+ if (m_regionColorMap.contains(key)) // Cache hit
+ {
+ return ImGui::GetColorU32(m_regionColorMap[key]);
+ }
+
+ // Cache miss, generate a new random color
+ AZ::SimpleLcgRandom rand(aznumeric_cast(AZStd::GetTimeNowTicks()));
+ const float r = AZStd::clamp(rand.GetRandomFloat(), .1f, .9f);
+ const float g = AZStd::clamp(rand.GetRandomFloat(), .1f, .9f);
+ const float b = AZStd::clamp(rand.GetRandomFloat(), .1f, .9f);
+ const ImVec4 randomColor = {r, g, b, .8};
+ m_regionColorMap.emplace(key, randomColor);
+ return ImGui::GetColorU32(randomColor);
+ }
+
+ inline void ImGuiCpuProfiler::DrawThreadSeparator(u64 baseRow, u64 maxDepth)
+ {
+ const ImU32 red = ImGui::GetColorU32({ 1, 0, 0, 1 });
+
+ auto [wx, wy] = ImGui::GetWindowPos();
+ wy -= ImGui::GetScrollY();
+ const float windowWidth = ImGui::GetWindowWidth();
+ const float boundaryY = wy + (baseRow + maxDepth + 1) * RowHeight - 5;
+
+ ImGui::GetWindowDrawList()->AddLine({ wx, boundaryY }, { wx + windowWidth, boundaryY }, red, 2.0f);
+ }
+
+ inline void ImGuiCpuProfiler::DrawThreadLabel(u64 baseRow, AZStd::thread_id threadId)
+ {
+ auto [wx, wy] = ImGui::GetWindowPos();
+ wy -= ImGui::GetScrollY();
+ const AZStd::string threadIdText = CpuProfilerImGuiHelper::TextThreadId(threadId.m_id);
+
+ ImGui::GetWindowDrawList()->AddText({ wx + 10, wy + baseRow * RowHeight + 5 }, IM_COL32_WHITE, threadIdText.c_str());
+ }
+
+ inline void ImGuiCpuProfiler::DrawRegionStatistics()
+ {
+ for (auto& [groupRegionName, stat] : m_regionStatisticsMap)
+ {
+ if (stat.m_draw)
+ {
+ ImGui::SetNextWindowSize({300, 340}, ImGuiCond_FirstUseEver);
+ ImGui::Begin(groupRegionName->m_regionName, &stat.m_draw, 0);
+
+ if (ImGui::Button(stat.m_record ? "Pause" : "Resume"))
+ {
+ stat.m_record = !stat.m_record;
+ }
+
+ ImGui::Text("Invocations: %llu", stat.m_invocations);
+ ImGui::Text("Average time: %.3f ms", stat.CalcAverageTimeMs());
+
+ ImGui::Separator();
+
+ ImGui::ColorPicker4("Region color", &m_regionColorMap[groupRegionName].x);
+ ImGui::End();
+ }
+ }
+ }
+
+ inline void ImGuiCpuProfiler::DrawFrameBoundaries()
+ {
+ ImDrawList* drawList = ImGui::GetWindowDrawList();
+
+ const float wy = ImGui::GetWindowPos().y;
+ const float windowHeight = ImGui::GetWindowHeight();
+ const ImU32 red = ImGui::GetColorU32({ 1, 0, 0, 1 });
+
+ // End ticks are sorted in increasing order, find the first frame bound to draw
+ auto endTickItr = AZStd::lower_bound(m_frameEndTicks.begin(), m_frameEndTicks.end(), m_viewportStartTick);
+
+ while (endTickItr != m_frameEndTicks.end() && *endTickItr < m_viewportEndTick)
+ {
+ const float horizontalPixel = ConvertTickToPixelSpace(*endTickItr);
+ drawList->AddLine({ horizontalPixel, wy }, { horizontalPixel, wy + windowHeight }, red);
+ endTickItr++;
+ }
+ }
+
+ inline void ImGuiCpuProfiler::DrawRuler()
+ {
+ // Use a pair of iterators to go through all saved frame boundaries and draw ruler lines
+ auto lastFrameBoundaryItr = AZStd::lower_bound(m_frameEndTicks.begin(), m_frameEndTicks.end(), m_viewportStartTick);
+ auto nextFrameBoundaryItr = lastFrameBoundaryItr;
+ if (lastFrameBoundaryItr != m_frameEndTicks.begin())
+ {
+ lastFrameBoundaryItr--;
+ }
+
+ const auto [wx, wy] = ImGui::GetWindowPos();
+ ImDrawList* drawList = ImGui::GetWindowDrawList();
+
+ while (nextFrameBoundaryItr != m_frameEndTicks.end())
+ {
+ const AZStd::sys_time_t lastFrameBoundaryTick = *lastFrameBoundaryItr;
+ const AZStd::sys_time_t nextFrameBoundaryTick = *nextFrameBoundaryItr;
+ if (lastFrameBoundaryTick > m_viewportEndTick)
+ {
+ break;
+ }
+
+ const float lastFrameBoundaryPixel = ConvertTickToPixelSpace(lastFrameBoundaryTick);
+ const float nextFrameBoundaryPixel = ConvertTickToPixelSpace(nextFrameBoundaryTick);
+
+ const AZStd::string label =
+ AZStd::string::format("%.2f ms", CpuProfilerImGuiHelper::TicksToMs(nextFrameBoundaryTick - lastFrameBoundaryTick));
+ const float labelWidth = ImGui::CalcTextSize(label.c_str()).x;
+
+ // The label can fit between the two boundaries, center it and draw
+ if (labelWidth <= nextFrameBoundaryPixel - lastFrameBoundaryPixel)
+ {
+ const float offset = (nextFrameBoundaryPixel - lastFrameBoundaryPixel - labelWidth) /2;
+ const float textBeginPixel = lastFrameBoundaryPixel + offset;
+ const float textEndPixel = textBeginPixel + labelWidth;
+
+ // Execution time label
+ drawList->AddText({ textBeginPixel, wy + ImGui::GetWindowHeight() / 4 }, IM_COL32_WHITE, label.c_str());
+
+ // Left side
+ drawList->AddLine(
+ { lastFrameBoundaryPixel, wy + ImGui::GetWindowHeight() / 2 },
+ { textBeginPixel - 5, wy + ImGui::GetWindowHeight() / 2},
+ IM_COL32_WHITE);
+
+ // Right side
+ drawList->AddLine(
+ { textEndPixel, wy + ImGui::GetWindowHeight()/2 },
+ { nextFrameBoundaryPixel, wy + ImGui::GetWindowHeight()/2 },
+ IM_COL32_WHITE);
+ }
+ else // Cannot fit inside, just draw a line between the two boundaries
+ {
+ drawList->AddLine(
+ { lastFrameBoundaryPixel, wy + ImGui::GetWindowHeight() / 2 },
+ { nextFrameBoundaryPixel, wy + ImGui::GetWindowHeight() / 2 },
+ IM_COL32_WHITE);
+ }
+
+ // Left bound
+ drawList->AddLine(
+ { lastFrameBoundaryPixel, wy },
+ { lastFrameBoundaryPixel, wy + ImGui::GetWindowHeight() },
+ IM_COL32_WHITE);
+
+ // Right bound
+ drawList->AddLine(
+ { nextFrameBoundaryPixel, wy },
+ { nextFrameBoundaryPixel, wy + ImGui::GetWindowHeight() },
+ IM_COL32_WHITE);
+
+ lastFrameBoundaryItr = nextFrameBoundaryItr;
+ nextFrameBoundaryItr++;
+ }
+ }
+
+ inline AZStd::sys_time_t ImGuiCpuProfiler::GetViewportTickWidth() const
+ {
+ return m_viewportEndTick - m_viewportStartTick;
+ }
+
+ inline float ImGuiCpuProfiler::ConvertTickToPixelSpace(AZStd::sys_time_t tick) const
+ {
+ const float wx = ImGui::GetWindowPos().x;
+ const float tickSpaceShifted = aznumeric_cast(tick - m_viewportStartTick); // This will be close to zero, so FP inaccuracy should not be too bad
+ const float tickSpaceNormalized = tickSpaceShifted / GetViewportTickWidth();
+ const float pixelSpace = tickSpaceNormalized * ImGui::GetWindowWidth() + wx;
+ return pixelSpace;
+ }
+
+ // System tick bus overrides
+ inline void ImGuiCpuProfiler::OnSystemTick()
+ {
+ m_frameEndTicks.push_back(AZStd::GetTimeNowTicks());
+
+ if (!m_showVisualizer || m_paused)
+ {
+ SystemTickBus::Handler::BusDisconnect();
+ }
+ }
+
+ // ----- RegionStatistics implementation -----
+
+ inline float RegionStatistics::CalcAverageTimeMs() const
+ {
+ if (m_invocations == 0)
+ {
+ return 0.0;
+ }
+ const double averageTicks = aznumeric_cast(m_totalTicks) / m_invocations;
+ return CpuProfilerImGuiHelper::TicksToMs(aznumeric_cast(averageTicks));
+ }
+
+ inline void RegionStatistics::RecordRegion(const AZ::RHI::CachedTimeRegion& region)
+ {
+ m_invocations++;
+ m_totalTicks += region.m_endTick - region.m_startTick;
+ }
+ } // namespace Render
+} // namespace AZ
diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.h
index e735852926..d907e7e7eb 100644
--- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.h
+++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.h
@@ -32,11 +32,14 @@ namespace AZ
void ReadbackCallback(const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult);
+ void DrawPassAttachments(AZ::RPI::Pass* pass);
+
bool m_previewAttachment = false;
bool m_showAttachments = false;
AZ::RPI::Pass* m_selectedPass = nullptr;
AZ::RHI::AttachmentId m_attachmentId;
+ AZ::Name m_slotName;
bool m_selectedChanged = false;
AZStd::shared_ptr m_readback;
diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.inl b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.inl
index cd53131933..d31674754e 100644
--- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.inl
+++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.inl
@@ -76,6 +76,7 @@ namespace AZ::Render
{
m_selectedChanged = true;
m_attachmentId = AZ::RHI::AttachmentId{};
+ m_slotName = AZ::Name{};
}
}
@@ -88,13 +89,12 @@ namespace AZ::Render
m_readback->SetCallback(AZStd::bind(&ImGuiPassTree::ReadbackCallback, this, AZStd::placeholders::_1));
}
- if (m_selectedPass && !m_attachmentId.IsEmpty())
+ if (m_selectedPass && !m_slotName.IsEmpty())
{
- AZ::RPI::RenderPass* renderPass = azrtti_cast(m_selectedPass);
- if (renderPass)
+ bool readbackResult = m_selectedPass->ReadbackAttachment(m_readback, m_slotName);
+ if (!readbackResult)
{
- AZ::RPI::PassAttachment* attachment = FindPassAttachment(renderPass, m_attachmentId);
- renderPass->ReadbackAttachment(m_readback, attachment);
+ AZ_Error("ImGuiPassTree", false, "Failed to readback attachment from pass [%s] slot [%s]", m_selectedPass->GetName().GetCStr(), m_slotName.GetCStr());
}
}
}
@@ -144,6 +144,77 @@ namespace AZ::Render
}
ImGui::End();
}
+
+ inline void ImGuiPassTree::DrawPassAttachments(AZ::RPI::Pass* pass)
+ {
+ for (const auto& binding : pass->GetAttachmentBindings())
+ {
+ // Binding info: [slot type] [slot name]
+ AZStd::string label = AZStd::string::format("[%s] [%s]", AZ::RPI::ToString(binding.m_slotType),
+ binding.m_name.GetCStr());
+
+ // Append attachment info if the attachment exists
+ if (binding.m_attachment)
+ {
+ AZ::RHI::AttachmentType type = binding.m_attachment->GetAttachmentType();
+
+ // Append attachment info: [attachment type] attachment name
+ label += AZStd::string::format(" [%s] %s",
+ AZ::RHI::ToString(type),
+ binding.m_attachment->m_name.GetCStr());
+
+ if (type == AZ::RHI::AttachmentType::Image)
+ {
+ // Append image info: [format] [size] [msaa]
+ AZ::RHI::ImageDescriptor descriptor;
+ if (binding.m_attachment->m_importedResource)
+ {
+ AZ::RPI::Image* image = static_cast(binding.m_attachment->m_importedResource.get());
+ descriptor = image->GetRHIImage()->GetDescriptor();
+ }
+ else
+ {
+ descriptor = binding.m_attachment->m_descriptor.m_image;
+ }
+ auto format = descriptor.m_format;
+ auto size = descriptor.m_size;
+ label += AZStd::string::format(" [%s] [%dx%d]", AZ::RHI::ToString(format), size.m_width, size.m_height);
+
+ if (descriptor.m_multisampleState.m_samples > 1)
+ {
+ if (descriptor.m_multisampleState.m_customPositionsCount > 0)
+ {
+ label += AZStd::string::format(" [MSAA_Custom_%dx]", descriptor.m_multisampleState.m_samples);
+ }
+ else
+ {
+ label += AZStd::string::format(" [MSAA_%dx]", descriptor.m_multisampleState.m_samples);
+ }
+ }
+ }
+ else if (type == AZ::RHI::AttachmentType::Buffer)
+ {
+ // Append buffer info: [size]
+ auto size = binding.m_attachment->m_descriptor.m_buffer.m_byteCount;
+ label += AZStd::string::format(" [%llu]", size);
+ }
+
+ if (Scriptable_ImGui::Selectable(label.c_str(), m_attachmentId == binding.m_attachment->GetAttachmentId()))
+ {
+ m_selectedPass = pass;
+ m_attachmentId = binding.m_attachment->GetAttachmentId();
+ m_slotName = binding.m_name;
+ m_selectedChanged = true;
+ }
+ }
+ else
+ {
+ // Only draw text (not selectable) if there is no attachment binded to the slot.
+ ImGui::Text(label.c_str());
+ }
+ }
+
+ }
inline void ImGuiPassTree::DrawTreeView(AZ::RPI::Pass* pass)
{
@@ -164,6 +235,7 @@ namespace AZ::Render
{
m_selectedPass = pass;
m_attachmentId = AZ::RHI::AttachmentId{};
+ m_slotName = AZ::Name{};
m_selectedChanged = true;
}
}
@@ -179,76 +251,13 @@ namespace AZ::Render
{
m_selectedPass = pass;
m_attachmentId = AZ::RHI::AttachmentId{};
+ m_slotName = AZ::Name{};
m_selectedChanged = true;
}
if (nodeOpen)
{
- for (const auto& binding : pass->GetAttachmentBindings())
- {
- // Binding info: [slot type] [slot name]
- AZStd::string label = AZStd::string::format("[%s] [%s]", AZ::RPI::ToString(binding.m_slotType),
- binding.m_name.GetCStr());
-
- // Append attachment info if the attachment exists
- if (binding.m_attachment)
- {
- AZ::RHI::AttachmentType type = binding.m_attachment->GetAttachmentType();
-
- // Append attachment info: [attachment type] attachment name
- label += AZStd::string::format(" [%s] %s",
- AZ::RHI::ToString(type),
- binding.m_attachment->m_name.GetCStr());
-
- if (type == AZ::RHI::AttachmentType::Image)
- {
- // Append image info: [format] [size] [msaa]
- AZ::RHI::ImageDescriptor descriptor;
- if (binding.m_attachment->m_importedResource)
- {
- AZ::RPI::Image* image = static_cast(binding.m_attachment->m_importedResource.get());
- descriptor = image->GetRHIImage()->GetDescriptor();
- }
- else
- {
- descriptor = binding.m_attachment->m_descriptor.m_image;
- }
- auto format = descriptor.m_format;
- auto size = descriptor.m_size;
- label += AZStd::string::format(" [%s] [%dx%d]", AZ::RHI::ToString(format), size.m_width, size.m_height);
-
- if (descriptor.m_multisampleState.m_samples > 1)
- {
- if (descriptor.m_multisampleState.m_customPositionsCount > 0)
- {
- label += AZStd::string::format(" [MSAA_Custom_%dx]", descriptor.m_multisampleState.m_samples);
- }
- else
- {
- label += AZStd::string::format(" [MSAA_%dx]", descriptor.m_multisampleState.m_samples);
- }
- }
- }
- else if (type == AZ::RHI::AttachmentType::Buffer)
- {
- // Append buffer info: [size]
- auto size = binding.m_attachment->m_descriptor.m_buffer.m_byteCount;
- label += AZStd::string::format(" [%llu]", size);
- }
-
- if (Scriptable_ImGui::Selectable(label.c_str(), m_attachmentId == binding.m_attachment->GetAttachmentId()))
- {
- m_selectedPass = pass;
- m_attachmentId = binding.m_attachment->GetAttachmentId();
- m_selectedChanged = true;
- }
- }
- else
- {
- // Only draw text (not selectable) if there is no attachment binded to the slot.
- ImGui::Text(label.c_str());
- }
- }
+ DrawPassAttachments(pass);
Scriptable_ImGui::TreePop();
}
@@ -266,11 +275,13 @@ namespace AZ::Render
{
m_selectedPass = pass;
m_attachmentId = AZ::RHI::AttachmentId{};
+ m_slotName = AZ::Name{};
m_selectedChanged = true;
}
if (nodeOpen)
{
+ DrawPassAttachments(pass);
for (const auto& child : asParent->GetChildren())
{
DrawTreeView(child.get());
@@ -354,6 +365,7 @@ namespace AZ::Render
m_selectedPass = nullptr;
m_attachmentId = AZ::RHI::AttachmentId{};
+ m_slotName = AZ::Name{};
m_selectedChanged = false;
m_readback = nullptr;
m_previewPass = nullptr;
diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h
index 9fe3f2c25b..9e6c38a939 100644
--- a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h
+++ b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h
@@ -16,6 +16,7 @@
#include
#include
#include
+#include
#include