Merge branch 'main' into sc_automation_balibhan

This commit is contained in:
balibhan
2021-05-19 09:42:08 +05:30
194 changed files with 5131 additions and 1392 deletions
-1
View File
@@ -115,7 +115,6 @@
*.wav filter=lfs diff=lfs merge=lfs -text
*.webm filter=lfs diff=lfs merge=lfs -text
*.wem filter=lfs diff=lfs merge=lfs -text
*.wxs filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.tbscene filter=lfs diff=lfs merge=lfs -text
*.spp filter=lfs diff=lfs merge=lfs -text
@@ -0,0 +1,25 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::BlastTests
TEST_SUITE main
TEST_SERIAL TRUE
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Active.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT Blast
)
endif()
+19 -379
View File
@@ -15,406 +15,46 @@
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
include(${pal_dir}/PAL_traits_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
## Asset pipeline ##
add_subdirectory(assetpipeline)
## Atom Renderer ##
add_subdirectory(atom_renderer)
## Physics ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::PhysicsTests_Main
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/physics/TestSuite_Main.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Physics
)
ly_add_pytest(
NAME AutomatedTesting::PhysicsTests_Periodic
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/physics/TestSuite_Periodic.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Physics
)
ly_add_pytest(
NAME AutomatedTesting::PhysicsTests_Sandbox
TEST_SUITE sandbox
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/physics/TestSuite_Sandbox.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Physics
)
endif()
add_subdirectory(physics)
## ScriptCanvas ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::ScriptCanvasTests
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/scripting/TestSuite_Active.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
ScriptCanvas
)
ly_add_pytest(
NAME AutomatedTesting::ScriptCanvasTests_Sandbox
TEST_SUITE sandbox
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/scripting/TestSuite_Sandbox.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
)
endif()
add_subdirectory(scripting)
## White Box ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::WhiteBoxTests
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/WhiteBox/TestSuite_Active.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
WhiteBox
)
endif()
add_subdirectory(WhiteBox)
## NvCloth ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::NvClothTests_Main
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/NvCloth/TestSuite_Active.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
NvCloth
)
endif()
add_subdirectory(NvCloth)
## Prefab ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::PrefabTests
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/prefab/TestSuite_Main.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
)
endif()
add_subdirectory(prefab)
## Editor Python Bindings ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::EditorPythonBindings
TEST_SUITE sandbox
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/EditorPythonBindings
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
Gem::EditorPythonBindings.Editor
COMPONENT TestTools
)
endif()
add_subdirectory(EditorPythonBindings)
## Python Asset Builder ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::PythonAssetBuilder
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/PythonAssetBuilder
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
Gem::EditorPythonBindings.Editor
Gem::PythonAssetBuilder.Editor
COMPONENT TestTools
)
endif()
add_subdirectory(PythonAssetBuilder)
## Blast ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::BlastTests
TEST_SUITE main
TEST_SERIAL TRUE
PATH ${CMAKE_CURRENT_LIST_DIR}/Blast/TestSuite_Active.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT Blast
)
endif()
#############
add_subdirectory(Blast)
## Large Worlds ##
include(${pal_dir}/PAL_traits_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_LARGE_WORLDS_TEST_SUPPORTED)
## DynVeg ##
# Temporarily moving all tests to periodic suite - SPEC-6553
#ly_add_pytest(
# NAME AutomatedTesting::DynamicVegetationTests_Main
# TEST_SERIAL
# TEST_SUITE main
# PATH ${CMAKE_CURRENT_LIST_DIR}/largeworlds/dyn_veg
# PYTEST_MARKS "not SUITE_sandbox and not SUITE_periodic and not SUITE_benchmark"
# TIMEOUT 1500
# RUNTIME_DEPENDENCIES
# AZ::AssetProcessor
# Legacy::Editor
# AutomatedTesting.GameLauncher
# AutomatedTesting.Assets
# COMPONENT
# LargeWorlds
#)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationTests_Sandbox
TEST_SERIAL
TEST_SUITE sandbox
PATH ${CMAKE_CURRENT_LIST_DIR}/largeworlds/dyn_veg
PYTEST_MARKS "SUITE_sandbox"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.GameLauncher
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationFilterTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/largeworlds/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_filter"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationModifierTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/largeworlds/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_modifier"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationRegressionTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/largeworlds/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_regression"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationAreaTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/largeworlds/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_area"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationMiscTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/largeworlds/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_misc"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationSurfaceTagTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/largeworlds/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_surfacetagemitter"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
## LandscapeCanvas ##
# Temporarily moving all tests to periodic suite - SPEC-6553
#ly_add_pytest(
# NAME AutomatedTesting::LandscapeCanvasTests_Main
# TEST_SERIAL
# TEST_SUITE main
# PATH ${CMAKE_CURRENT_LIST_DIR}/largeworlds/landscape_canvas
# PYTEST_MARKS "not SUITE_sandbox and not SUITE_periodic and not SUITE_benchmark"
# TIMEOUT 1500
# RUNTIME_DEPENDENCIES
# AZ::AssetProcessor
# Legacy::Editor
# AutomatedTesting.Assets
# COMPONENT
# LargeWorlds
#)
ly_add_pytest(
NAME AutomatedTesting::LandscapeCanvasTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/largeworlds/landscape_canvas
PYTEST_MARKS "SUITE_periodic"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
## GradientSignal ##
ly_add_pytest(
NAME AutomatedTesting::GradientSignalTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/largeworlds/gradient_signal
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
endif()
add_subdirectory(largeworlds)
## Editor ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_FOUNDATION_TEST_SUPPORTED)
ly_add_pytest(
NAME AutomatedTesting::EditorTests_Periodic
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/editor
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Editor
)
endif()
add_subdirectory(editor)
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
# Unstable, SPEC-3838 will restore
#ly_add_pytest(
# NAME AutomatedTesting::asset_load_benchmark_test
# TEST_SERIAL
# TEST_SUITE benchmark
# PATH ${CMAKE_CURRENT_LIST_DIR}/streaming/benchmark/asset_load_benchmark_test.py
# RUNTIME_DEPENDENCIES
# AZ::AssetProcessor
# AZ::AssetProcessorBatch
# AutomatedTesting.GameLauncher
#)
endif()
## Streaming ##
add_subdirectory(streaming)
## Smoke ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::SmokeTest
TEST_SUITE smoke
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/smoke
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
AZ::PythonBindingsExample
Legacy::Editor
AutomatedTesting.GameLauncher
AutomatedTesting.Assets
COMPONENT
Smoke
)
endif()
## Streaming ##
add_subdirectory(smoke)
@@ -0,0 +1,26 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::EditorPythonBindings
TEST_SUITE sandbox
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
Gem::EditorPythonBindings.Editor
COMPONENT TestTools
)
endif()
@@ -0,0 +1,26 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::NvClothTests_Main
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Active.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
NvCloth
)
endif()
@@ -0,0 +1,27 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::PythonAssetBuilder
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
Gem::EditorPythonBindings.Editor
Gem::PythonAssetBuilder.Editor
COMPONENT TestTools
)
endif()
@@ -0,0 +1,26 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::WhiteBoxTests
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Active.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
WhiteBox
)
endif()
@@ -13,8 +13,6 @@ add_subdirectory(asset_processor_tests)
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
## AP Python Tests ##
ly_add_pytest(
NAME AssetPipelineTests.AuxiliaryContent
PATH ${CMAKE_CURRENT_LIST_DIR}/auxiliary_content_tests/auxiliary_content_tests.py
@@ -22,7 +20,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
TEST_SUITE periodic
)
ly_add_pytest(
NAME AssetPipelineTests.BankInfoParser
PATH ${CMAKE_CURRENT_LIST_DIR}/wwise_bank_dependency_tests/bank_info_parser_tests.py
@@ -33,4 +30,3 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
)
endif()
@@ -141,4 +141,3 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
# )
endif()
@@ -0,0 +1,26 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_FOUNDATION_TEST_SUPPORTED)
ly_add_pytest(
NAME AutomatedTesting::EditorTests_Periodic
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Editor
)
endif()
@@ -0,0 +1,186 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_LARGE_WORLDS_TEST_SUPPORTED)
## DynVeg ##
# Temporarily moving all tests to periodic suite - SPEC-6553
#ly_add_pytest(
# NAME AutomatedTesting::DynamicVegetationTests_Main
# TEST_SERIAL
# TEST_SUITE main
# PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
# PYTEST_MARKS "not SUITE_sandbox and not SUITE_periodic and not SUITE_benchmark"
# TIMEOUT 1500
# RUNTIME_DEPENDENCIES
# AZ::AssetProcessor
# Legacy::Editor
# AutomatedTesting.GameLauncher
# AutomatedTesting.Assets
# COMPONENT
# LargeWorlds
#)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationTests_Sandbox
TEST_SERIAL
TEST_SUITE sandbox
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_sandbox"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.GameLauncher
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationFilterTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_filter"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationModifierTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_modifier"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationRegressionTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_regression"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationAreaTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_area"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationMiscTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_misc"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::DynamicVegetationSurfaceTagTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_surfacetagemitter"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
## LandscapeCanvas ##
# Temporarily moving all tests to periodic suite - SPEC-6553
#ly_add_pytest(
# NAME AutomatedTesting::LandscapeCanvasTests_Main
# TEST_SERIAL
# TEST_SUITE main
# PATH ${CMAKE_CURRENT_LIST_DIR}/largeworlds/landscape_canvas
# PYTEST_MARKS "not SUITE_sandbox and not SUITE_periodic and not SUITE_benchmark"
# TIMEOUT 1500
# RUNTIME_DEPENDENCIES
# AZ::AssetProcessor
# Legacy::Editor
# AutomatedTesting.Assets
# COMPONENT
# LargeWorlds
#)
ly_add_pytest(
NAME AutomatedTesting::LandscapeCanvasTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/landscape_canvas
PYTEST_MARKS "SUITE_periodic"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
## GradientSignal ##
ly_add_pytest(
NAME AutomatedTesting::GradientSignalTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/gradient_signal
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
endif()
@@ -0,0 +1,52 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::PhysicsTests_Main
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Physics
)
ly_add_pytest(
NAME AutomatedTesting::PhysicsTests_Periodic
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Periodic.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Physics
)
ly_add_pytest(
NAME AutomatedTesting::PhysicsTests_Sandbox
TEST_SUITE sandbox
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Sandbox.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Physics
)
endif()
@@ -0,0 +1,25 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
## Prefab ##
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::PrefabTests
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
)
endif()
@@ -0,0 +1,37 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::ScriptCanvasTests_Perodic
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Periodic.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
ScriptCanvas
)
ly_add_pytest(
NAME AutomatedTesting::ScriptCanvasTests_Sandbox
TEST_SUITE sandbox
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Sandbox.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
)
endif()
@@ -0,0 +1,27 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::SmokeTest
TEST_SUITE smoke
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
AZ::PythonBindingsExample
Legacy::Editor
AutomatedTesting.GameLauncher
AutomatedTesting.Assets
COMPONENT
Smoke
)
endif()
@@ -20,6 +20,7 @@ import subprocess
@pytest.mark.SUITE_smoke
class TestCLIToolAssetBuilderWorks(object):
@pytest.mark.xfail(reason="Ignoring failure temporarily - SPEC-6905")
def test_CLITool_AssetBuilder_Works(self, build_directory):
file_path = os.path.join(build_directory, "AssetBuilder")
help_message = "AssetBuilder is part of the Asset Processor"
@@ -0,0 +1,24 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
# Unstable, SPEC-3838 will restore
#ly_add_pytest(
# NAME AutomatedTesting::asset_load_benchmark_test
# TEST_SERIAL
# TEST_SUITE benchmark
# PATH ${CMAKE_CURRENT_LIST_DIR}/benchmark/asset_load_benchmark_test.py
# RUNTIME_DEPENDENCIES
# AZ::AssetProcessor
# AZ::AssetProcessorBatch
# AutomatedTesting.GameLauncher
#)
endif()
-1
View File
@@ -772,7 +772,6 @@ _MS_ALIGN(16) struct SSkinningData
void* pCharInstCB; // used if per char instance cbs are available in renderdll (d3d11+);
// members below are for Software Skinning
void* pCustomData; // client specific data, used for example for sw-skinning on animation side
SSkinningData** pMasterSkinningDataList; // used by the SkinningData for a Character Instance, contains a list of all Skin Instances which need SW-Skinning
SSkinningData* pNextSkinningData; // List to the next element which needs SW-Skinning
} _ALIGN(16);
+900
View File
@@ -0,0 +1,900 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "DebugCallStack.h"
#if defined(WIN32) || defined(WIN64)
#include <IConsole.h>
#include <CryPath.h>
#include "System.h"
#include <AzCore/Debug/StackTracer.h>
#include <AzCore/Debug/EventTraceDrillerBus.h>
#define VS_VERSION_INFO 1
#define IDD_CRITICAL_ERROR 101
#define IDB_CONFIRM_SAVE 102
#define IDB_DONT_SAVE 103
#define IDD_CONFIRM_SAVE_LEVEL 127
#define IDB_CRASH_FACE 128
#define IDD_EXCEPTION 245
#define IDC_CALLSTACK 1001
#define IDC_EXCEPTION_CODE 1002
#define IDC_EXCEPTION_ADDRESS 1003
#define IDC_EXCEPTION_MODULE 1004
#define IDC_EXCEPTION_DESC 1005
#define IDB_EXIT 1008
#define IDB_IGNORE 1010
__pragma(comment(lib, "version.lib"))
//! Needs one external of DLL handle.
extern HMODULE gDLLHandle;
#include <DbgHelp.h>
#define MAX_PATH_LENGTH 1024
#define MAX_SYMBOL_LENGTH 512
static HWND hwndException = 0;
static bool g_bUserDialog = true; // true=on crash show dialog box, false=supress user interaction
static int PrintException(EXCEPTION_POINTERS* pex);
static bool IsFloatingPointException(EXCEPTION_POINTERS* pex);
extern LONG WINAPI CryEngineExceptionFilterWER(struct _EXCEPTION_POINTERS* pExceptionPointers);
extern LONG WINAPI CryEngineExceptionFilterMiniDump(struct _EXCEPTION_POINTERS* pExceptionPointers, const char* szDumpPath, MINIDUMP_TYPE mdumpValue);
//=============================================================================
CONTEXT CaptureCurrentContext()
{
CONTEXT context;
memset(&context, 0, sizeof(context));
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
return context;
}
LONG __stdcall CryUnhandledExceptionHandler(EXCEPTION_POINTERS* pex)
{
return DebugCallStack::instance()->handleException(pex);
}
BOOL CALLBACK EnumModules(
PCSTR ModuleName,
DWORD64 BaseOfDll,
PVOID UserContext)
{
DebugCallStack::TModules& modules = *static_cast<DebugCallStack::TModules*>(UserContext);
modules[(void*)BaseOfDll] = ModuleName;
return TRUE;
}
//=============================================================================
// Class Statics
//=============================================================================
// Return single instance of class.
IDebugCallStack* IDebugCallStack::instance()
{
static DebugCallStack sInstance;
return &sInstance;
}
//------------------------------------------------------------------------------------------------------------------------
// Sets up the symbols for functions in the debug file.
//------------------------------------------------------------------------------------------------------------------------
DebugCallStack::DebugCallStack()
: prevExceptionHandler(0)
, m_pSystem(0)
, m_nSkipNumFunctions(0)
, m_bCrash(false)
, m_szBugMessage(NULL)
{
}
DebugCallStack::~DebugCallStack()
{
}
void DebugCallStack::RemoveOldFiles()
{
RemoveFile("error.log");
RemoveFile("error.bmp");
RemoveFile("error.dmp");
}
void DebugCallStack::RemoveFile(const char* szFileName)
{
FILE* pFile = nullptr;
azfopen(&pFile, szFileName, "r");
const bool bFileExists = (pFile != NULL);
if (bFileExists)
{
fclose(pFile);
WriteLineToLog("Removing file \"%s\"...", szFileName);
if (remove(szFileName) == 0)
{
WriteLineToLog("File successfully removed.");
}
else
{
WriteLineToLog("Couldn't remove file!");
}
}
}
void DebugCallStack::installErrorHandler(ISystem* pSystem)
{
m_pSystem = pSystem;
prevExceptionHandler = (void*)SetUnhandledExceptionFilter(CryUnhandledExceptionHandler);
}
//////////////////////////////////////////////////////////////////////////
void DebugCallStack::SetUserDialogEnable(const bool bUserDialogEnable)
{
g_bUserDialog = bUserDialogEnable;
}
DWORD g_idDebugThreads[10];
const char* g_nameDebugThreads[10];
int g_nDebugThreads = 0;
volatile int g_lockThreadDumpList = 0;
void MarkThisThreadForDebugging(const char* name)
{
EBUS_EVENT(AZ::Debug::EventTraceDrillerSetupBus, SetThreadName, AZStd::this_thread::get_id(), name);
WriteLock lock(g_lockThreadDumpList);
DWORD id = GetCurrentThreadId();
if (g_nDebugThreads == sizeof(g_idDebugThreads) / sizeof(g_idDebugThreads[0]))
{
return;
}
for (int i = 0; i < g_nDebugThreads; i++)
{
if (g_idDebugThreads[i] == id)
{
return;
}
}
g_nameDebugThreads[g_nDebugThreads] = name;
g_idDebugThreads[g_nDebugThreads++] = id;
((CSystem*)gEnv->pSystem)->EnableFloatExceptions(g_cvars.sys_float_exceptions);
}
void UnmarkThisThreadFromDebugging()
{
WriteLock lock(g_lockThreadDumpList);
DWORD id = GetCurrentThreadId();
for (int i = g_nDebugThreads - 1; i >= 0; i--)
{
if (g_idDebugThreads[i] == id)
{
memmove(g_idDebugThreads + i, g_idDebugThreads + i + 1, (g_nDebugThreads - 1 - i) * sizeof(g_idDebugThreads[0]));
memmove(g_nameDebugThreads + i, g_nameDebugThreads + i + 1, (g_nDebugThreads - 1 - i) * sizeof(g_nameDebugThreads[0]));
--g_nDebugThreads;
}
}
}
extern int prev_sys_float_exceptions;
void UpdateFPExceptionsMaskForThreads()
{
int mask = -iszero(g_cvars.sys_float_exceptions);
CONTEXT ctx;
for (int i = 0; i < g_nDebugThreads; i++)
{
if (g_idDebugThreads[i] != GetCurrentThreadId())
{
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, TRUE, g_idDebugThreads[i]);
ctx.ContextFlags = CONTEXT_ALL;
SuspendThread(hThread);
GetThreadContext(hThread, &ctx);
#ifndef WIN64
(ctx.FloatSave.ControlWord |= 7) &= ~5 | mask;
(*(WORD*)(ctx.ExtendedRegisters + 24) |= 0x280) &= ~0x280 | mask;
#else
(ctx.FltSave.ControlWord |= 7) &= ~5 | mask;
(ctx.FltSave.MxCsr |= 0x280) &= ~0x280 | mask;
#endif
SetThreadContext(hThread, &ctx);
ResumeThread(hThread);
}
}
}
//////////////////////////////////////////////////////////////////////////
int DebugCallStack::handleException(EXCEPTION_POINTERS* exception_pointer)
{
if (gEnv == NULL)
{
return EXCEPTION_EXECUTE_HANDLER;
}
ResetFPU(exception_pointer);
prev_sys_float_exceptions = 0;
const int cached_sys_float_exceptions = g_cvars.sys_float_exceptions;
((CSystem*)gEnv->pSystem)->EnableFloatExceptions(0);
if (g_cvars.sys_WER)
{
gEnv->pLog->FlushAndClose();
return CryEngineExceptionFilterWER(exception_pointer);
}
if (g_cvars.sys_no_crash_dialog)
{
DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);
}
m_bCrash = true;
if (g_cvars.sys_no_crash_dialog)
{
DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);
}
static bool firstTime = true;
if (g_cvars.sys_dump_aux_threads)
{
for (int i = 0; i < g_nDebugThreads; i++)
{
if (g_idDebugThreads[i] != GetCurrentThreadId())
{
SuspendThread(OpenThread(THREAD_ALL_ACCESS, TRUE, g_idDebugThreads[i]));
}
}
}
// uninstall our exception handler.
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)prevExceptionHandler);
if (!firstTime)
{
WriteLineToLog("Critical Exception! Called Multiple Times!");
gEnv->pLog->FlushAndClose();
// Exception called more then once.
return EXCEPTION_EXECUTE_HANDLER;
}
// Print exception info:
{
char excCode[80];
char excAddr[80];
WriteLineToLog("<CRITICAL EXCEPTION>");
sprintf_s(excAddr, "0x%04X:0x%p", exception_pointer->ContextRecord->SegCs, exception_pointer->ExceptionRecord->ExceptionAddress);
sprintf_s(excCode, "0x%08X", exception_pointer->ExceptionRecord->ExceptionCode);
WriteLineToLog("Exception: %s, at Address: %s", excCode, excAddr);
}
firstTime = false;
const int ret = SubmitBug(exception_pointer);
if (ret != IDB_IGNORE)
{
CryEngineExceptionFilterWER(exception_pointer);
}
gEnv->pLog->FlushAndClose();
if (exception_pointer->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{
// This is non continuable exception. abort application now.
exit(exception_pointer->ExceptionRecord->ExceptionCode);
}
//typedef long (__stdcall *ExceptionFunc)(EXCEPTION_POINTERS*);
//ExceptionFunc prevFunc = (ExceptionFunc)prevExceptionHandler;
//return prevFunc( (EXCEPTION_POINTERS*)exception_pointer );
if (ret == IDB_EXIT)
{
// Immediate exit.
// on windows, exit() and _exit() do all sorts of things, unfortuantely
// TerminateProcess is the only way to die.
TerminateProcess(GetCurrentProcess(), exception_pointer->ExceptionRecord->ExceptionCode); // we crashed, so don't return a zero exit code!
// on linux based systems, _exit will not call ATEXIT and other things, which makes it more suitable for termination in an emergency such
// as an unhandled exception.
// however, this function is a windows exception handler.
}
else if (ret == IDB_IGNORE)
{
#ifndef WIN64
exception_pointer->ContextRecord->FloatSave.StatusWord &= ~31;
exception_pointer->ContextRecord->FloatSave.ControlWord |= 7;
(*(WORD*)(exception_pointer->ContextRecord->ExtendedRegisters + 24) &= 31) |= 0x1F80;
#else
exception_pointer->ContextRecord->FltSave.StatusWord &= ~31;
exception_pointer->ContextRecord->FltSave.ControlWord |= 7;
(exception_pointer->ContextRecord->FltSave.MxCsr &= 31) |= 0x1F80;
#endif
firstTime = true;
prevExceptionHandler = (void*)SetUnhandledExceptionFilter(CryUnhandledExceptionHandler);
g_cvars.sys_float_exceptions = cached_sys_float_exceptions;
((CSystem*)gEnv->pSystem)->EnableFloatExceptions(g_cvars.sys_float_exceptions);
return EXCEPTION_CONTINUE_EXECUTION;
}
// Continue;
return EXCEPTION_EXECUTE_HANDLER;
}
void DebugCallStack::ReportBug(const char* szErrorMessage)
{
WriteLineToLog("Reporting bug: %s", szErrorMessage);
m_szBugMessage = szErrorMessage;
m_context = CaptureCurrentContext();
SubmitBug(NULL);
m_szBugMessage = NULL;
}
void DebugCallStack::dumpCallStack(std::vector<string>& funcs)
{
WriteLineToLog("=============================================================================");
int len = (int)funcs.size();
for (int i = 0; i < len; i++)
{
const char* str = funcs[i].c_str();
WriteLineToLog("%2d) %s", len - i, str);
}
WriteLineToLog("=============================================================================");
}
//////////////////////////////////////////////////////////////////////////
void DebugCallStack::LogExceptionInfo(EXCEPTION_POINTERS* pex)
{
string path("");
if ((gEnv) && (gEnv->pFileIO))
{
const char* logAlias = gEnv->pFileIO->GetAlias("@log@");
if (!logAlias)
{
logAlias = gEnv->pFileIO->GetAlias("@root@");
}
if (logAlias)
{
path = logAlias;
path += "/";
}
}
string fileName = path;
fileName += "error.log";
struct stat fileInfo;
string timeStamp;
string backupPath;
if (gEnv->IsDedicated())
{
backupPath = PathUtil::ToUnixPath(PathUtil::AddSlash(path + "DumpBackups"));
gEnv->pFileIO->CreatePath(backupPath.c_str());
if (stat(fileName.c_str(), &fileInfo) == 0)
{
// Backup log
tm creationTime;
localtime_s(&creationTime, &fileInfo.st_mtime);
char tempBuffer[32];
strftime(tempBuffer, sizeof(tempBuffer), "%d %b %Y (%H %M %S)", &creationTime);
timeStamp = tempBuffer;
string backupFileName = backupPath + timeStamp + " error.log";
CopyFile(fileName.c_str(), backupFileName.c_str(), true);
}
}
FILE* f = nullptr;
azfopen(&f, fileName.c_str(), "wt");
static char errorString[s_iCallStackSize];
errorString[0] = 0;
// Time and Version.
char versionbuf[1024];
azstrcpy(versionbuf, AZ_ARRAY_SIZE(versionbuf), "");
PutVersion(versionbuf, AZ_ARRAY_SIZE(versionbuf));
cry_strcat(errorString, versionbuf);
cry_strcat(errorString, "\n");
char excCode[MAX_WARNING_LENGTH];
char excAddr[80];
char desc[1024];
char excDesc[MAX_WARNING_LENGTH];
// make sure the mouse cursor is visible
ShowCursor(TRUE);
const char* excName;
if (m_bIsFatalError || !pex)
{
const char* const szMessage = m_bIsFatalError ? s_szFatalErrorCode : m_szBugMessage;
excName = szMessage;
cry_strcpy(excCode, szMessage);
cry_strcpy(excAddr, "");
cry_strcpy(desc, "");
cry_strcpy(m_excModule, "");
cry_strcpy(excDesc, szMessage);
}
else
{
sprintf_s(excAddr, "0x%04X:0x%p", pex->ContextRecord->SegCs, pex->ExceptionRecord->ExceptionAddress);
sprintf_s(excCode, "0x%08X", pex->ExceptionRecord->ExceptionCode);
excName = TranslateExceptionCode(pex->ExceptionRecord->ExceptionCode);
cry_strcpy(desc, "");
sprintf_s(excDesc, "%s\r\n%s", excName, desc);
if (pex->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
if (pex->ExceptionRecord->NumberParameters > 1)
{
ULONG_PTR iswrite = pex->ExceptionRecord->ExceptionInformation[0];
DWORD64 accessAddr = pex->ExceptionRecord->ExceptionInformation[1];
if (iswrite)
{
sprintf_s(desc, "Attempt to write data to address 0x%08llu\r\nThe memory could not be \"written\"", accessAddr);
}
else
{
sprintf_s(desc, "Attempt to read from address 0x%08llu\r\nThe memory could not be \"read\"", accessAddr);
}
}
}
}
WriteLineToLog("Exception Code: %s", excCode);
WriteLineToLog("Exception Addr: %s", excAddr);
WriteLineToLog("Exception Module: %s", m_excModule);
WriteLineToLog("Exception Name : %s", excName);
WriteLineToLog("Exception Description: %s", desc);
cry_strcpy(m_excDesc, excDesc);
cry_strcpy(m_excAddr, excAddr);
cry_strcpy(m_excCode, excCode);
char errs[32768];
sprintf_s(errs, "Exception Code: %s\nException Addr: %s\nException Module: %s\nException Description: %s, %s\n",
excCode, excAddr, m_excModule, excName, desc);
cry_strcat(errs, "\nCall Stack Trace:\n");
std::vector<string> funcs;
{
AZ::Debug::StackFrame frames[25];
AZ::Debug::SymbolStorage::StackLine lines[AZ_ARRAY_SIZE(frames)];
unsigned int numFrames = AZ::Debug::StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), 3);
if (numFrames)
{
AZ::Debug::SymbolStorage::DecodeFrames(frames, numFrames, lines);
for (unsigned int i = 0; i < numFrames; i++)
{
funcs.push_back(lines[i]);
}
}
dumpCallStack(funcs);
// Fill call stack.
char str[s_iCallStackSize];
cry_strcpy(str, "");
for (unsigned int i = 0; i < funcs.size(); i++)
{
char temp[s_iCallStackSize];
sprintf_s(temp, "%2zd) %s", funcs.size() - i, (const char*)funcs[i].c_str());
cry_strcat(str, temp);
cry_strcat(str, "\r\n");
cry_strcat(errs, temp);
cry_strcat(errs, "\n");
}
cry_strcpy(m_excCallstack, str);
}
cry_strcat(errorString, errs);
if (f)
{
fwrite(errorString, strlen(errorString), 1, f);
{
if (g_cvars.sys_dump_aux_threads)
{
for (int i = 0; i < g_nDebugThreads; i++)
{
if (g_idDebugThreads[i] != GetCurrentThreadId())
{
fprintf(f, "\n\nSuspended thread (%s):\n", g_nameDebugThreads[i]);
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, TRUE, g_idDebugThreads[i]);
// mirrors the AZ::Debug::Trace::PrintCallstack() functionality, but prints to a file
{
AZ::Debug::StackFrame frames[10];
// Without StackFrame explicit alignment frames array is aligned to 4 bytes
// which causes the stack tracing to fail.
AZ::Debug::SymbolStorage::StackLine lines[AZ_ARRAY_SIZE(frames)];
unsigned int numFrames = AZ::Debug::StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), 0, hThread);
if (numFrames)
{
AZ::Debug::SymbolStorage::DecodeFrames(frames, numFrames, lines);
for (unsigned int i2 = 0; i2 < numFrames; ++i2)
{
fprintf(f, "%2d) %s\n", numFrames - i2, lines[i2]);
}
}
}
ResumeThread(hThread);
}
}
}
}
fflush(f);
fclose(f);
}
if (pex)
{
MINIDUMP_TYPE mdumpValue;
bool bDump = true;
switch (g_cvars.sys_dump_type)
{
case 0:
bDump = false;
break;
case 1:
mdumpValue = MiniDumpNormal;
break;
case 2:
mdumpValue = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithDataSegs);
break;
case 3:
mdumpValue = MiniDumpWithFullMemory;
break;
default:
mdumpValue = (MINIDUMP_TYPE)g_cvars.sys_dump_type;
break;
}
if (bDump)
{
fileName = path + "error.dmp";
if (gEnv->IsDedicated() && stat(fileName.c_str(), &fileInfo) == 0)
{
// Backup dump (use timestamp from error.log if available)
if (timeStamp.empty())
{
tm creationTime;
localtime_s(&creationTime, &fileInfo.st_mtime);
char tempBuffer[32];
strftime(tempBuffer, sizeof(tempBuffer), "%d %b %Y (%H %M %S)", &creationTime);
timeStamp = tempBuffer;
}
string backupFileName = backupPath + timeStamp + " error.dmp";
CopyFile(fileName.c_str(), backupFileName.c_str(), true);
}
CryEngineExceptionFilterMiniDump(pex, fileName.c_str(), mdumpValue);
}
}
//if no crash dialog don't even submit the bug
if (m_postBackupProcess && g_cvars.sys_no_crash_dialog == 0 && g_bUserDialog)
{
m_postBackupProcess();
}
else
{
// lawsonn: Disabling the JIRA-based crash reporter for now
// we'll need to deal with it our own way, pending QA.
// if you're customizing the engine this is also your opportunity to deal with it.
if (g_cvars.sys_no_crash_dialog != 0 || !g_bUserDialog)
{
// ------------ place custom crash handler here ---------------------
// it should launch an executable!
/// by this time, error.bmp will be in the engine root folder
// error.log and error.dmp will also be present in the engine root folder
// if your error dumper wants those, it should zip them up and send them or offer to do so.
// ------------------------------------------------------------------
}
}
const bool bQuitting = !gEnv || !gEnv->pSystem || gEnv->pSystem->IsQuitting();
//[AlexMcC|16.04.10] When the engine is shutting down, MessageBox doesn't display a box
// and immediately returns IDYES. Avoid this by just not trying to save if we're quitting.
// Don't ask to save if this isn't a real crash (a real crash has exception pointers)
if (g_cvars.sys_no_crash_dialog == 0 && g_bUserDialog && gEnv->IsEditor() && !bQuitting && pex)
{
BackupCurrentLevel();
const INT_PTR res = DialogBoxParam(gDLLHandle, MAKEINTRESOURCE(IDD_CONFIRM_SAVE_LEVEL), NULL, DebugCallStack::ConfirmSaveDialogProc, NULL);
if (res == IDB_CONFIRM_SAVE)
{
if (SaveCurrentLevel())
{
MessageBox(NULL, "Level has been successfully saved!\r\nPress Ok to terminate Editor.", "Save", MB_OK);
}
else
{
MessageBox(NULL, "Error saving level.\r\nPress Ok to terminate Editor.", "Save", MB_OK | MB_ICONWARNING);
}
}
}
if (g_cvars.sys_no_crash_dialog != 0 || !g_bUserDialog)
{
// terminate immediately - since we're in a crash, there is no point unwinding stack, we've already done access violation or worse.
// calling exit will only cause further death down the line...
TerminateProcess(GetCurrentProcess(), pex->ExceptionRecord->ExceptionCode);
}
}
INT_PTR CALLBACK DebugCallStack::ExceptionDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static EXCEPTION_POINTERS* pex;
static char errorString[32768] = "";
switch (message)
{
case WM_INITDIALOG:
{
pex = (EXCEPTION_POINTERS*)lParam;
HWND h;
if (pex->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{
// Disable continue button for non continuable exceptions.
//h = GetDlgItem( hwndDlg,IDB_CONTINUE );
//if (h) EnableWindow( h,FALSE );
}
DebugCallStack* pDCS = static_cast<DebugCallStack*>(DebugCallStack::instance());
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_DESC);
if (h)
{
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excDesc);
}
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_CODE);
if (h)
{
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excCode);
}
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_MODULE);
if (h)
{
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excModule);
}
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_ADDRESS);
if (h)
{
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excAddr);
}
// Fill call stack.
HWND callStack = GetDlgItem(hwndDlg, IDC_CALLSTACK);
if (callStack)
{
SendMessage(callStack, WM_SETTEXT, FALSE, (LPARAM)pDCS->m_excCallstack);
}
if (hwndException)
{
DestroyWindow(hwndException);
hwndException = 0;
}
if (IsFloatingPointException(pex))
{
EnableWindow(GetDlgItem(hwndDlg, IDB_IGNORE), TRUE);
}
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDB_EXIT:
case IDB_IGNORE:
// Fall through.
EndDialog(hwndDlg, wParam);
return TRUE;
}
}
return FALSE;
}
INT_PTR CALLBACK DebugCallStack::ConfirmSaveDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, [[maybe_unused]] LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
{
// The user might be holding down the spacebar while the engine crashes.
// If we don't remove keyboard focus from this dialog, the keypress will
// press the default button before the dialog actually appears, even if
// the user has already released the key, which is bad.
SetFocus(NULL);
} break;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDB_CONFIRM_SAVE: // Fall through
case IDB_DONT_SAVE:
{
EndDialog(hwndDlg, wParam);
return TRUE;
}
}
} break;
}
return FALSE;
}
bool DebugCallStack::BackupCurrentLevel()
{
CSystem* pSystem = static_cast<CSystem*>(m_pSystem);
if (pSystem && pSystem->GetUserCallback())
{
return pSystem->GetUserCallback()->OnBackupDocument();
}
return false;
}
bool DebugCallStack::SaveCurrentLevel()
{
CSystem* pSystem = static_cast<CSystem*>(m_pSystem);
if (pSystem && pSystem->GetUserCallback())
{
return pSystem->GetUserCallback()->OnSaveDocument();
}
return false;
}
int DebugCallStack::SubmitBug(EXCEPTION_POINTERS* exception_pointer)
{
int ret = IDB_EXIT;
assert(!hwndException);
RemoveOldFiles();
AZ::Debug::Trace::PrintCallstack("", 2);
LogExceptionInfo(exception_pointer);
if (IsFloatingPointException(exception_pointer))
{
//! Print exception dialog.
ret = PrintException(exception_pointer);
}
return ret;
}
void DebugCallStack::ResetFPU(EXCEPTION_POINTERS* pex)
{
if (IsFloatingPointException(pex))
{
// How to reset FPU: http://www.experts-exchange.com/Programming/System/Windows__Programming/Q_10310953.html
_clearfp();
#ifndef WIN64
pex->ContextRecord->FloatSave.ControlWord |= 0x2F;
pex->ContextRecord->FloatSave.StatusWord &= ~0x8080;
#endif
}
}
string DebugCallStack::GetModuleNameForAddr(void* addr)
{
if (m_modules.empty())
{
return "[unknown]";
}
if (addr < m_modules.begin()->first)
{
return "[unknown]";
}
TModules::const_iterator it = m_modules.begin();
TModules::const_iterator end = m_modules.end();
for (; ++it != end; )
{
if (addr < it->first)
{
return (--it)->second;
}
}
//if address is higher than the last module, we simply assume it is in the last module.
return m_modules.rbegin()->second;
}
void DebugCallStack::GetProcNameForAddr(void* addr, string& procName, void*& baseAddr, string& filename, int& line)
{
AZ::Debug::SymbolStorage::StackLine func, file, module;
AZ::Debug::SymbolStorage::FindFunctionFromIP(addr, &func, &file, &module, line, baseAddr);
procName = func;
filename = file;
}
string DebugCallStack::GetCurrentFilename()
{
char fullpath[MAX_PATH_LENGTH + 1];
GetModuleFileName(NULL, fullpath, MAX_PATH_LENGTH);
return fullpath;
}
static bool IsFloatingPointException(EXCEPTION_POINTERS* pex)
{
if (!pex)
{
return false;
}
DWORD exceptionCode = pex->ExceptionRecord->ExceptionCode;
switch (exceptionCode)
{
case EXCEPTION_FLT_DENORMAL_OPERAND:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_UNDERFLOW:
case STATUS_FLOAT_MULTIPLE_FAULTS:
case STATUS_FLOAT_MULTIPLE_TRAPS:
return true;
default:
return false;
}
}
int DebugCallStack::PrintException(EXCEPTION_POINTERS* exception_pointer)
{
return (int)DialogBoxParam(gDLLHandle, MAKEINTRESOURCE(IDD_CRITICAL_ERROR), NULL, DebugCallStack::ExceptionDialogProc, (LPARAM)exception_pointer);
}
#else
void MarkThisThreadForDebugging(const char*) {}
void UnmarkThisThreadFromDebugging() {}
void UpdateFPExceptionsMaskForThreads() {}
#endif //WIN32
+95
View File
@@ -0,0 +1,95 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_DEBUGCALLSTACK_H
#define CRYINCLUDE_CRYSYSTEM_DEBUGCALLSTACK_H
#pragma once
#include "IDebugCallStack.h"
#if defined (WIN32) || defined (WIN64)
//! Limits the maximal number of functions in call stack.
const int MAX_DEBUG_STACK_ENTRIES_FILE_DUMP = 12;
struct ISystem;
//!============================================================================
//!
//! DebugCallStack class, capture call stack information from symbol files.
//!
//!============================================================================
class DebugCallStack
: public IDebugCallStack
{
public:
DebugCallStack();
virtual ~DebugCallStack();
ISystem* GetSystem() { return m_pSystem; };
virtual string GetModuleNameForAddr(void* addr);
virtual void GetProcNameForAddr(void* addr, string& procName, void*& baseAddr, string& filename, int& line);
virtual string GetCurrentFilename();
void installErrorHandler(ISystem* pSystem);
virtual int handleException(EXCEPTION_POINTERS* exception_pointer);
virtual void ReportBug(const char*);
void dumpCallStack(std::vector<string>& functions);
void SetUserDialogEnable(const bool bUserDialogEnable);
typedef std::map<void*, string> TModules;
protected:
static void RemoveOldFiles();
static void RemoveFile(const char* szFileName);
static int PrintException(EXCEPTION_POINTERS* exception_pointer);
static INT_PTR CALLBACK ExceptionDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK ConfirmSaveDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
void LogExceptionInfo(EXCEPTION_POINTERS* exception_pointer);
bool BackupCurrentLevel();
bool SaveCurrentLevel();
int SubmitBug(EXCEPTION_POINTERS* exception_pointer);
void ResetFPU(EXCEPTION_POINTERS* pex);
static const int s_iCallStackSize = 32768;
char m_excLine[256];
char m_excModule[128];
char m_excDesc[MAX_WARNING_LENGTH];
char m_excCode[MAX_WARNING_LENGTH];
char m_excAddr[80];
char m_excCallstack[s_iCallStackSize];
void* prevExceptionHandler;
bool m_bCrash;
const char* m_szBugMessage;
ISystem* m_pSystem;
int m_nSkipNumFunctions;
CONTEXT m_context;
TModules m_modules;
};
#endif //WIN32
#endif // CRYINCLUDE_CRYSYSTEM_DEBUGCALLSTACK_H
+11
View File
@@ -14,6 +14,7 @@
#include "CrySystem_precompiled.h"
#include "System.h"
#include <AZCrySystemInitLogSink.h>
#include "DebugCallStack.h"
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
@@ -87,6 +88,16 @@ CRYSYSTEM_API ISystem* CreateSystemInterface(const SSystemInitParams& startupPar
startupParams.pUserCallback->OnSystemConnect(pSystem);
}
#if defined(WIN32)
// Environment Variable to signal we don't want to override our exception handler - our crash report system will set this
auto envVar = AZ::Environment::FindVariable<bool>("ExceptionHandlerIsSet");
const bool handlerIsSet = (envVar && *envVar);
if (!handlerIsSet)
{
((DebugCallStack*)IDebugCallStack::instance())->installErrorHandler(pSystem);
}
#endif
bool retVal = false;
{
AZ::Debug::StartupLogSinkReporter<AZ::Debug::CrySystemInitLogSink> initLogSink;
@@ -0,0 +1,275 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : A multiplatform base class for handling errors and collecting call stacks
#include "CrySystem_precompiled.h"
#include "IDebugCallStack.h"
#include "System.h"
#include <AzFramework/IO/FileOperations.h>
#include <AzCore/NativeUI/NativeUIRequests.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
//#if !defined(LINUX)
#include <ISystem.h>
const char* const IDebugCallStack::s_szFatalErrorCode = "FATAL_ERROR";
IDebugCallStack::IDebugCallStack()
: m_bIsFatalError(false)
, m_postBackupProcess(0)
, m_memAllocFileHandle(AZ::IO::InvalidHandle)
{
}
IDebugCallStack::~IDebugCallStack()
{
StopMemLog();
}
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_SINGLETON
IDebugCallStack* IDebugCallStack::instance()
{
static IDebugCallStack sInstance;
return &sInstance;
}
#endif
void IDebugCallStack::FileCreationCallback(void (* postBackupProcess)())
{
m_postBackupProcess = postBackupProcess;
}
//////////////////////////////////////////////////////////////////////////
void IDebugCallStack::LogCallstack()
{
AZ::Debug::Trace::PrintCallstack("", 2);
}
const char* IDebugCallStack::TranslateExceptionCode(DWORD dwExcept)
{
switch (dwExcept)
{
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_TRANSLATE
case EXCEPTION_ACCESS_VIOLATION:
return "EXCEPTION_ACCESS_VIOLATION";
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
return "EXCEPTION_DATATYPE_MISALIGNMENT";
break;
case EXCEPTION_BREAKPOINT:
return "EXCEPTION_BREAKPOINT";
break;
case EXCEPTION_SINGLE_STEP:
return "EXCEPTION_SINGLE_STEP";
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
return "EXCEPTION_FLT_DENORMAL_OPERAND";
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
break;
case EXCEPTION_FLT_INEXACT_RESULT:
return "EXCEPTION_FLT_INEXACT_RESULT";
break;
case EXCEPTION_FLT_INVALID_OPERATION:
return "EXCEPTION_FLT_INVALID_OPERATION";
break;
case EXCEPTION_FLT_OVERFLOW:
return "EXCEPTION_FLT_OVERFLOW";
break;
case EXCEPTION_FLT_STACK_CHECK:
return "EXCEPTION_FLT_STACK_CHECK";
break;
case EXCEPTION_FLT_UNDERFLOW:
return "EXCEPTION_FLT_UNDERFLOW";
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
return "EXCEPTION_INT_DIVIDE_BY_ZERO";
break;
case EXCEPTION_INT_OVERFLOW:
return "EXCEPTION_INT_OVERFLOW";
break;
case EXCEPTION_PRIV_INSTRUCTION:
return "EXCEPTION_PRIV_INSTRUCTION";
break;
case EXCEPTION_IN_PAGE_ERROR:
return "EXCEPTION_IN_PAGE_ERROR";
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
return "EXCEPTION_ILLEGAL_INSTRUCTION";
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
break;
case EXCEPTION_STACK_OVERFLOW:
return "EXCEPTION_STACK_OVERFLOW";
break;
case EXCEPTION_INVALID_DISPOSITION:
return "EXCEPTION_INVALID_DISPOSITION";
break;
case EXCEPTION_GUARD_PAGE:
return "EXCEPTION_GUARD_PAGE";
break;
case EXCEPTION_INVALID_HANDLE:
return "EXCEPTION_INVALID_HANDLE";
break;
//case EXCEPTION_POSSIBLE_DEADLOCK: return "EXCEPTION_POSSIBLE_DEADLOCK"; break ;
case STATUS_FLOAT_MULTIPLE_FAULTS:
return "STATUS_FLOAT_MULTIPLE_FAULTS";
break;
case STATUS_FLOAT_MULTIPLE_TRAPS:
return "STATUS_FLOAT_MULTIPLE_TRAPS";
break;
#endif
default:
return "Unknown";
break;
}
}
void IDebugCallStack::PutVersion(char* str, size_t length)
{
AZ_PUSH_DISABLE_WARNING(4996, "-Wunknown-warning-option")
if (!gEnv || !gEnv->pSystem)
{
return;
}
char sFileVersion[128];
gEnv->pSystem->GetFileVersion().ToString(sFileVersion, sizeof(sFileVersion));
char sProductVersion[128];
gEnv->pSystem->GetProductVersion().ToString(sProductVersion, sizeof(sFileVersion));
//! Get time.
time_t ltime;
time(&ltime);
tm* today = localtime(&ltime);
char s[1024];
//! Use strftime to build a customized time string.
strftime(s, 128, "Logged at %#c\n", today);
azstrcat(str, length, s);
sprintf_s(s, "FileVersion: %s\n", sFileVersion);
azstrcat(str, length, s);
sprintf_s(s, "ProductVersion: %s\n", sProductVersion);
azstrcat(str, length, s);
if (gEnv->pLog)
{
const char* logfile = gEnv->pLog->GetFileName();
if (logfile)
{
sprintf (s, "LogFile: %s\n", logfile);
azstrcat(str, length, s);
}
}
AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
azstrcat(str, length, "ProjectDir: ");
azstrcat(str, length, projectPath.c_str());
azstrcat(str, length, "\n");
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_APPEND_MODULENAME
GetModuleFileNameA(NULL, s, sizeof(s));
// Log EXE filename only if possible (not full EXE path which could contain sensitive info)
AZStd::string exeName;
if (AZ::StringFunc::Path::GetFullFileName(s, exeName))
{
azstrcat(str, length, "Executable: ");
azstrcat(str, length, exeName.c_str());
# ifdef AZ_DEBUG_BUILD
azstrcat(str, length, " (debug: yes");
# else
azstrcat(str, length, " (debug: no");
# endif
}
#endif
AZ_POP_DISABLE_WARNING
}
//Crash the application, in this way the debug callstack routine will be called and it will create all the necessary files (error.log, dump, and eventually screenshot)
void IDebugCallStack::FatalError(const char* description)
{
m_bIsFatalError = true;
WriteLineToLog(description);
#ifndef _RELEASE
bool bShowDebugScreen = g_cvars.sys_no_crash_dialog == 0;
// showing the debug screen is not safe when not called from mainthread
// it normally leads to a infinity recursion followed by a stack overflow, preventing
// useful call stacks, thus they are disabled
bShowDebugScreen = bShowDebugScreen && gEnv->mMainThreadId == CryGetCurrentThreadId();
if (bShowDebugScreen)
{
EBUS_EVENT(AZ::NativeUI::NativeUIRequestBus, DisplayOkDialog, "Open 3D Engine Fatal Error", description, false);
}
#endif
#if defined(WIN32) || !defined(_RELEASE)
int* p = 0x0;
PREFAST_SUPPRESS_WARNING(6011) * p = 1; // we're intentionally crashing here
#endif
}
void IDebugCallStack::WriteLineToLog(const char* format, ...)
{
va_list ArgList;
char szBuffer[MAX_WARNING_LENGTH];
va_start(ArgList, format);
vsnprintf_s(szBuffer, sizeof(szBuffer), sizeof(szBuffer) - 1, format, ArgList);
cry_strcat(szBuffer, "\n");
szBuffer[sizeof(szBuffer) - 1] = '\0';
va_end(ArgList);
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle);
if (fileHandle != AZ::IO::InvalidHandle)
{
AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, szBuffer, strlen(szBuffer));
AZ::IO::FileIOBase::GetDirectInstance()->Flush(fileHandle);
AZ::IO::FileIOBase::GetDirectInstance()->Close(fileHandle);
}
}
//////////////////////////////////////////////////////////////////////////
void IDebugCallStack::StartMemLog()
{
AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle);
assert(m_memAllocFileHandle != AZ::IO::InvalidHandle);
}
//////////////////////////////////////////////////////////////////////////
void IDebugCallStack::StopMemLog()
{
if (m_memAllocFileHandle != AZ::IO::InvalidHandle)
{
AZ::IO::FileIOBase::GetDirectInstance()->Close(m_memAllocFileHandle);
m_memAllocFileHandle = AZ::IO::InvalidHandle;
}
}
//#endif //!defined(LINUX)
@@ -0,0 +1,90 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : A multiplatform base class for handling errors and collecting call stacks
#ifndef CRYINCLUDE_CRYSYSTEM_IDEBUGCALLSTACK_H
#define CRYINCLUDE_CRYSYSTEM_IDEBUGCALLSTACK_H
#pragma once
#include "System.h"
#if AZ_LEGACY_CRYSYSTEM_TRAIT_FORWARD_EXCEPTION_POINTERS
struct EXCEPTION_POINTERS;
#endif
//! Limits the maximal number of functions in call stack.
enum
{
MAX_DEBUG_STACK_ENTRIES = 80
};
class IDebugCallStack
{
public:
// Returns single instance of DebugStack
static IDebugCallStack* instance();
virtual int handleException([[maybe_unused]] EXCEPTION_POINTERS* exception_pointer){return 0; }
// returns the module name of a given address
virtual string GetModuleNameForAddr([[maybe_unused]] void* addr) { return "[unknown]"; }
// returns the function name of a given address together with source file and line number (if available) of a given address
virtual void GetProcNameForAddr(void* addr, string& procName, void*& baseAddr, string& filename, int& line)
{
filename = "[unknown]";
line = 0;
baseAddr = addr;
#if defined(PLATFORM_64BIT)
procName.Format("[%016llX]", addr);
#else
procName.Format("[%08X]", addr);
#endif
}
// returns current filename
virtual string GetCurrentFilename() { return "[unknown]"; }
//! Dumps Current Call Stack to log.
virtual void LogCallstack();
//triggers a fatal error, so the DebugCallstack can create the error.log and terminate the application
void FatalError(const char*);
//Reports a bug and continues execution
virtual void ReportBug(const char*) {}
virtual void FileCreationCallback(void (* postBackupProcess)());
static void WriteLineToLog(const char* format, ...);
virtual void StartMemLog();
virtual void StopMemLog();
protected:
IDebugCallStack();
virtual ~IDebugCallStack();
static const char* TranslateExceptionCode(DWORD dwExcept);
static void PutVersion(char* str, size_t length);
bool m_bIsFatalError;
static const char* const s_szFatalErrorCode;
void (* m_postBackupProcess)();
AZ::IO::HandleType m_memAllocFileHandle;
};
#endif // CRYINCLUDE_CRYSYSTEM_IDEBUGCALLSTACK_H
+1
View File
@@ -208,6 +208,7 @@ struct SSystemCVars
int sys_no_crash_dialog;
int sys_no_error_report_window;
int sys_dump_aux_threads;
int sys_WER;
int sys_dump_type;
int sys_ai;
int sys_entitysystem;
+19
View File
@@ -121,6 +121,10 @@
# include <AzFramework/Network/AssetProcessorConnection.h>
#endif
#ifdef WIN32
extern LONG WINAPI CryEngineExceptionFilterWER(struct _EXCEPTION_POINTERS* pExceptionPointers);
#endif
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEMINIT_CPP_SECTION_14
#include AZ_RESTRICTED_FILE(SystemInit_cpp)
@@ -1484,6 +1488,13 @@ AZ_POP_DISABLE_WARNING
InlineInitializationProcessing("CSystem::Init LoadConfigurations");
#ifdef WIN32
if (g_cvars.sys_WER)
{
SetUnhandledExceptionFilter(CryEngineExceptionFilterWER);
}
#endif
//////////////////////////////////////////////////////////////////////////
// Localization
//////////////////////////////////////////////////////////////////////////
@@ -2020,6 +2031,14 @@ void CSystem::CreateSystemVars()
REGISTER_CVAR2("sys_update_profile_time", &g_cvars.sys_update_profile_time, 1.0f, 0, "Time to keep updates timings history for.");
REGISTER_CVAR2("sys_no_crash_dialog", &g_cvars.sys_no_crash_dialog, m_bNoCrashDialog, VF_NULL, "Whether to disable the crash dialog window");
REGISTER_CVAR2("sys_no_error_report_window", &g_cvars.sys_no_error_report_window, m_bNoErrorReportWindow, VF_NULL, "Whether to disable the error report list");
#if defined(_RELEASE)
if (!gEnv->IsDedicated())
{
REGISTER_CVAR2("sys_WER", &g_cvars.sys_WER, 1, 0, "Enables Windows Error Reporting");
}
#else
REGISTER_CVAR2("sys_WER", &g_cvars.sys_WER, 0, 0, "Enables Windows Error Reporting");
#endif
#ifdef USE_HTTP_WEBSOCKETS
REGISTER_CVAR2("sys_simple_http_base_port", &g_cvars.sys_simple_http_base_port, 1880, VF_REQUIRE_APP_RESTART,
+5
View File
@@ -46,6 +46,8 @@
#include <shlobj.h>
#endif
#include "IDebugCallStack.h"
#if defined(APPLE) || defined(LINUX)
#include <pwd.h>
#endif
@@ -355,6 +357,7 @@ void CSystem::FatalError(const char* format, ...)
}
// Dump callstack.
IDebugCallStack::instance()->FatalError(szBuffer);
#endif
CryDebugBreak();
@@ -396,6 +399,8 @@ void CSystem::ReportBug([[maybe_unused]] const char* format, ...)
va_start(ArgList, format);
azvsnprintf(szBuffer + strlen(sPrefix), MAX_WARNING_LENGTH - strlen(sPrefix), format, ArgList);
va_end(ArgList);
IDebugCallStack::instance()->ReportBug(szBuffer);
#endif
}
@@ -0,0 +1,137 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Support for Windows Error Reporting (WER)
#include "CrySystem_precompiled.h"
#ifdef WIN32
#include "System.h"
#include <windows.h>
#include <tchar.h>
#include "errorrep.h"
#include "ISystem.h"
#include <DbgHelp.h>
static WCHAR szPath[MAX_PATH + 1];
static WCHAR szFR[] = L"\\System32\\FaultRep.dll";
WCHAR* GetFullPathToFaultrepDll(void)
{
UINT rc = GetSystemWindowsDirectoryW(szPath, ARRAYSIZE(szPath));
if (rc == 0 || rc > ARRAYSIZE(szPath) - ARRAYSIZE(szFR) - 1)
{
return NULL;
}
wcscat_s(szPath, szFR);
return szPath;
}
typedef BOOL (WINAPI * MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);
//////////////////////////////////////////////////////////////////////////
LONG WINAPI CryEngineExceptionFilterMiniDump(struct _EXCEPTION_POINTERS* pExceptionPointers, const char* szDumpPath, MINIDUMP_TYPE DumpType)
{
// note: In debug mode, this dll is loaded on startup anyway, so this should not incur an additional load unless it crashes
// very early during startup.
fflush(nullptr); // according to MSDN on fflush, calling fflush on null flushes all buffers.
HMODULE hndDBGHelpDLL = LoadLibraryA("DBGHELP.DLL");
if (!hndDBGHelpDLL)
{
CryLogAlways("Failed to record DMP file: Could not open DBGHELP.DLL");
return EXCEPTION_CONTINUE_SEARCH;
}
MINIDUMPWRITEDUMP dumpFnPtr = (MINIDUMPWRITEDUMP)::GetProcAddress(hndDBGHelpDLL, "MiniDumpWriteDump");
if (!dumpFnPtr)
{
CryLogAlways("Failed to record DMP file: Unable to find MiniDumpWriteDump in DBGHELP.DLL");
return EXCEPTION_CONTINUE_SEARCH;
}
HANDLE hFile = ::CreateFile(szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
CryLogAlways("Failed to record DMP file: could not open file '%s' for writing - error code: %d", szDumpPath, GetLastError());
return EXCEPTION_CONTINUE_SEARCH;
}
_MINIDUMP_EXCEPTION_INFORMATION ExInfo;
ExInfo.ThreadId = ::GetCurrentThreadId();
ExInfo.ExceptionPointers = pExceptionPointers;
ExInfo.ClientPointers = NULL;
BOOL bOK = dumpFnPtr(GetCurrentProcess(), GetCurrentProcessId(), hFile, DumpType, &ExInfo, NULL, NULL);
::CloseHandle(hFile);
if (bOK)
{
CryLogAlways("Successfully recorded DMP file: '%s'", szDumpPath);
return EXCEPTION_EXECUTE_HANDLER; // SUCCESS! you can execute your handlers now
}
else
{
CryLogAlways("Failed to record DMP file: '%s' - error code: %d", szDumpPath, GetLastError());
}
return EXCEPTION_CONTINUE_SEARCH;
}
//////////////////////////////////////////////////////////////////////////
LONG WINAPI CryEngineExceptionFilterWER(struct _EXCEPTION_POINTERS* pExceptionPointers)
{
if (g_cvars.sys_WER > 1)
{
char szScratch [_MAX_PATH];
const char* szDumpPath = gEnv->pCryPak->AdjustFileName("@log@/CE2Dump.dmp", szScratch, AZ_ARRAY_SIZE(szScratch), 0);
MINIDUMP_TYPE mdumpValue = (MINIDUMP_TYPE)(MiniDumpNormal);
if (g_cvars.sys_WER > 1)
{
mdumpValue = (MINIDUMP_TYPE)(g_cvars.sys_WER - 2);
}
return CryEngineExceptionFilterMiniDump(pExceptionPointers, szDumpPath, mdumpValue);
}
LONG lRet = EXCEPTION_CONTINUE_SEARCH;
WCHAR* psz = GetFullPathToFaultrepDll();
if (psz)
{
HMODULE hFaultRepDll = LoadLibraryW(psz);
if (hFaultRepDll)
{
pfn_REPORTFAULT pfn = (pfn_REPORTFAULT)GetProcAddress(hFaultRepDll, "ReportFault");
if (pfn)
{
pfn(pExceptionPointers, 0);
lRet = EXCEPTION_EXECUTE_HANDLER;
}
FreeLibrary(hFaultRepDll);
}
}
return lRet;
}
#endif // WIN32
@@ -15,6 +15,8 @@ set(FILES
CmdLineArg.cpp
ConsoleBatchFile.cpp
ConsoleHelpGen.cpp
DebugCallStack.cpp
IDebugCallStack.cpp
Log.cpp
System.cpp
SystemCFG.cpp
@@ -31,6 +33,8 @@ set(FILES
CmdLineArg.h
ConsoleBatchFile.h
ConsoleHelpGen.h
DebugCallStack.h
IDebugCallStack.h
Log.h
SimpleStringPool.h
CrySystem_precompiled.h
@@ -72,4 +76,5 @@ set(FILES
ViewSystem/ViewSystem.cpp
ViewSystem/ViewSystem.h
CrySystem_precompiled.cpp
WindowsErrorReporting.cpp
)
@@ -20,10 +20,12 @@
namespace AZStd
{
//! Simple class for verifying that no concurrent access is occuring.
//! Simple class for verifying that no concurrent access is occurring.
//! This is *not* a synchronization primitive, and is intended simply for checking that no concurrency issues exist.
//! It will be compiled out in release builds.
//! Use concurrency_checker like a mutex (i.e. call soft_lock() and soft_unlock() around all instances of your data access).
//! Use soft_lock_shared and soft_unlock_shared around places where multiple threads are allowed to have read access
//! at the same time as long as nothing else already has a soft lock
//! It will assert if there are multiple threads accessing the locked code/data at the same time.
//! Expected use case is for defensive programming: when you do not expect any concurrent access within a system,
//! but want to verify that it stays that way in the future, without incurring the overhead of a mutex.
@@ -34,7 +36,7 @@ namespace AZStd
{
#ifdef AZ_CONCURRENCY_CHECKER_ENABLED
uint32_t count = ++m_concurrencyCounter;
AZ_Assert(count == 1, "Concurrency check failed. Multiple threads are trying to access data at the same time, or there is a lock/unlock mismatch.");
AZ_Assert(count == 1 && m_sharedConcurrencyCounter == 0, "Concurrency check failed. Multiple threads are trying to access data at the same time, or there is a lock/unlock mismatch.");
#endif
}
@@ -46,9 +48,27 @@ namespace AZStd
#endif
}
AZ_FORCE_INLINE void soft_lock_shared()
{
#ifdef AZ_CONCURRENCY_CHECKER_ENABLED
AZ_Assert(m_concurrencyCounter == 0, "Concurrency check failed. A soft_lock_shared was attempted when there was already a soft_lock.");
++m_sharedConcurrencyCounter;
#endif
}
AZ_FORCE_INLINE void soft_unlock_shared()
{
#ifdef AZ_CONCURRENCY_CHECKER_ENABLED
AZ_Assert(m_sharedConcurrencyCounter != 0, "Concurrency check failed. There is a shared_lock/shared_unlock mismatch.");
--m_sharedConcurrencyCounter;
#endif
}
private:
#ifdef AZ_CONCURRENCY_CHECKER_ENABLED
AZStd::atomic_uint32_t m_concurrencyCounter = 0;
AZStd::atomic_uint32_t m_sharedConcurrencyCounter = 0;
#endif
};
+2 -5
View File
@@ -293,15 +293,12 @@ namespace UnitTest
{
array_view<int> view({ 1,2,3,4 });
UnitTest::TestRunner::Instance().StartAssertTests();
AZ_TEST_START_TRACE_SUPPRESSION;
EXPECT_EQ(0, UnitTest::TestRunner::Instance().m_numAssertsFailed);
view[4];
EXPECT_EQ(1, UnitTest::TestRunner::Instance().m_numAssertsFailed);
view[5];
EXPECT_EQ(2, UnitTest::TestRunner::Instance().m_numAssertsFailed);
UnitTest::TestRunner::Instance().StopAssertTests();
AZ_TEST_STOP_TRACE_SUPPRESSION(2);
}
}
@@ -0,0 +1,100 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AtomCore/std/parallel/concurrency_checker.h>
#include <AzCore/UnitTest/TestTypes.h>
using namespace AZStd;
namespace UnitTest
{
class ConcurrencyCheckerTestFixture
: public AllocatorsTestFixture
{
void SetUp() override
{
AllocatorsFixture::SetUp();
}
};
TEST_F(AllocatorsTestFixture, SoftLock_NoContention_NoAsserts)
{
concurrency_checker concurrencyChecker;
concurrencyChecker.soft_lock();
concurrencyChecker.soft_unlock();
concurrencyChecker.soft_lock();
concurrencyChecker.soft_unlock();
}
TEST_F(AllocatorsTestFixture, SoftLock_AlreadyLocked_Assert)
{
concurrency_checker concurrencyChecker;
concurrencyChecker.soft_lock();
AZ_TEST_START_TRACE_SUPPRESSION;
concurrencyChecker.soft_lock();
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(AllocatorsTestFixture, SoftUnlock_NotAlreadyLocked_Assert)
{
concurrency_checker concurrencyChecker;
concurrencyChecker.soft_lock();
concurrencyChecker.soft_unlock();
AZ_TEST_START_TRACE_SUPPRESSION;
concurrencyChecker.soft_unlock();
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(AllocatorsTestFixture, SoftLockShared_NoContention_NoAsserts)
{
concurrency_checker concurrencyChecker;
// Multiple shared locks can be made at once,
// as long as they are all unlocked before the next soft_lock
concurrencyChecker.soft_lock_shared();
concurrencyChecker.soft_lock_shared();
concurrencyChecker.soft_unlock_shared();
concurrencyChecker.soft_unlock_shared();
concurrencyChecker.soft_lock();
concurrencyChecker.soft_unlock();
concurrencyChecker.soft_lock_shared();
concurrencyChecker.soft_lock_shared();
concurrencyChecker.soft_unlock_shared();
concurrencyChecker.soft_unlock_shared();
concurrencyChecker.soft_lock();
concurrencyChecker.soft_unlock();
}
TEST_F(AllocatorsTestFixture, SoftLockShared_SharedLockAfterSoftLock_Assert)
{
concurrency_checker concurrencyChecker;
concurrencyChecker.soft_lock();
AZ_TEST_START_TRACE_SUPPRESSION;
concurrencyChecker.soft_lock_shared();
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(AllocatorsTestFixture, SoftUnlockShared_NotAlreadyLocked_Assert)
{
concurrency_checker concurrencyChecker;
concurrencyChecker.soft_lock_shared();
concurrencyChecker.soft_unlock_shared();
AZ_TEST_START_TRACE_SUPPRESSION;
concurrencyChecker.soft_unlock_shared();
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
}
@@ -11,6 +11,7 @@
set(FILES
ArrayView.cpp
ConcurrencyCheckerTests.cpp
InstanceDatabase.cpp
JsonSerializationUtilsTests.cpp
lru_cache.cpp
@@ -307,6 +307,8 @@ namespace AZ
Asset(AssetLoadBehavior loadBehavior = AssetLoadBehavior::Default);
/// Create an asset from a valid asset data (created asset), might not be loaded or currently loading.
Asset(AssetData* assetData, AssetLoadBehavior loadBehavior);
/// Create an asset from a valid asset data (created asset) and set the asset id for both, might not be loaded or currently loading.
Asset(const AZ::Data::AssetId& id, AssetData* assetData, AssetLoadBehavior loadBehavior);
/// Initialize asset pointer with id, type, and hint. No data construction will occur until QueueLoad is called.
Asset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type, const AZStd::string& hint = AZStd::string());
@@ -787,6 +789,18 @@ namespace AZ
SetData(assetData);
}
//=========================================================================
template<class T>
Asset<T>::Asset(const AssetId& id, AssetData* assetData, AssetLoadBehavior loadBehavior)
: m_assetId(id)
, m_assetType(azrtti_typeid<T>())
, m_loadBehavior(loadBehavior)
{
AZ_Assert(!assetData->m_assetId.IsValid(), "Asset data already has an ID set.");
assetData->m_assetId = id;
SetData(assetData);
}
//=========================================================================
template<class T>
Asset<T>::Asset(const AssetId& id, const AZ::Data::AssetType& type, const AZStd::string& hint)
@@ -103,7 +103,7 @@ namespace AZ
}
}
/// Returns a pointer to the beginning of master vector of SmallAllocationGroups.
/// Returns a pointer to the beginning of vector of SmallAllocationGroups.
SmallAllocationGroup* ArrayHead()
{
return this - m_index;
@@ -169,7 +169,7 @@ namespace AZ
return m_marker == MARKER;
}
/// Returns the master index of the SmallAllocationGroup containing this allocation
/// Returns the index of the SmallAllocationGroup containing this allocation
uint32_t GetSmallAllocationIndex() const
{
return (uint32_t)(m_data & 0xFFFFFFFF);
@@ -40,7 +40,7 @@ namespace AZ
// Afterwards use the AZ::IO::Path Append function append the filename as a child
// of the framework directory
AZ::IO::FixedMaxPathString fileName{ fullPath.Filename().Native() };
fullPath.ReplaceFilename(AZ::IO::PathView((fileName + ".framework").c_str()));
fullPath.ReplaceFilename(AZ::IO::PathView(AZStd::string_view(fileName + ".framework")));
fullPath /= fileName;
}
}
@@ -115,7 +115,7 @@ namespace AZ::IO
// If used, the source path will be treated as the destination path
// and no transformations will be done. Pass this flag when the path is to be the actual
// path on the disk/in the packs and doesn't need adjustment (or after it has come through adjustments already)
// if this is set, AdjustFileName will not map the input path into the master folder (Ex: Shaders will not be converted to Game\Shaders)
// if this is set, AdjustFileName will not map the input path into the folder (Ex: Shaders will not be converted to Game\Shaders)
FLAGS_PATH_REAL = 1 << 16,
// AdjustFileName will always copy the file path to the destination path:
@@ -318,7 +318,6 @@ namespace AZ::IO
virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, uint32_t nFlags = 0, bool bAllowUseFileSystem = false) = 0;
virtual ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator handle) = 0;
virtual bool FindClose(AZ::IO::ArchiveFileIterator handle) = 0;
// virtual bool IsOutOfDate(const char * szCompiledName, const char * szMasterFile)=0;
//returns file modification time
virtual IArchive::FileTime GetModificationTime(AZ::IO::HandleType fileHandle) = 0;
@@ -47,7 +47,7 @@ namespace AZ::IO
enum EPakFlags
{
// support for absolute and other complex path specifications -
// all paths will be treated relatively to the current directory (normally MasterCD)
// all paths will be treated relatively to the current directory
FLAGS_ABSOLUTE_PATHS = 1,
// if this is set, the object will only understand relative to the zip file paths,
@@ -18,6 +18,7 @@
#include <AzFramework/Physics/Collision/CollisionGroups.h>
#include <AzFramework/Physics/Collision/CollisionLayers.h>
#include <AzFramework/Physics/Common/PhysicsTypes.h>
#include <AzFramework/Physics/Common/PhysicsSimulatedBody.h>
#include <AzFramework/Physics/Configuration/SimulatedBodyConfiguration.h>
@@ -36,7 +37,7 @@ namespace Physics
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_name;
ShapeConfigurationList m_shapes;
AzPhysics::ShapeColliderPairList m_shapes;
};
class CharacterColliderConfiguration
@@ -57,7 +57,7 @@ namespace Physics
classElement.RemoveElement(shapesIndex);
// add a new vector in the new format
const int newShapesIndex = classElement.AddElement<ShapeConfigurationList>(context, "shapes");
const int newShapesIndex = classElement.AddElement<AzPhysics::ShapeColliderPairList>(context, "shapes");
if (newShapesIndex != -1)
{
AZ::SerializeContext::DataElementNode& newShapesElement = classElement.GetSubElement(newShapesIndex);
@@ -65,7 +65,9 @@ namespace Physics
// convert the old shapes into the new format and add to the vector
for (AZ::SerializeContext::DataElementNode shape : shapesCopy)
{
const int pairIndex = newShapesElement.AddElementWithData<ShapeConfigurationPair>(context, "element", ShapeConfigurationPair());
const int pairIndex = newShapesElement.AddElementWithData<AzPhysics::ShapeColliderPair>(
context, "element", AzPhysics::ShapeColliderPair());
AZ::SerializeContext::DataElementNode& pairElement = newShapesElement.GetSubElement(pairIndex);
ColliderConfiguration colliderConfig;
@@ -131,8 +133,8 @@ namespace Physics
AZ::SerializeContext::DataElementNode* baseBaseClass1 = baseClass1->FindSubElement(AZ_CRC("BaseClass1", 0xd4925735));
if (baseBaseClass1 && baseBaseClass1->FindSubElementAndGetData<AZStd::string>(AZ_CRC("name", 0x5e237e06), name))
{
ShapeConfigurationList shapes;
if (nodeElement.FindSubElementAndGetData<ShapeConfigurationList>(AZ_CRC("shapes", 0x93dba512), shapes))
AzPhysics::ShapeColliderPairList shapes;
if (nodeElement.FindSubElementAndGetData<AzPhysics::ShapeColliderPairList>(AZ_CRC("shapes", 0x93dba512), shapes))
{
CharacterColliderNodeConfiguration newColliderNodeConfig;
newColliderNodeConfig.m_name = name;
@@ -70,7 +70,10 @@ namespace AzPhysics
using SimulatedBodyHandleList = AZStd::vector<SimulatedBodyHandle>;
//! Helper used for pairing the ShapeConfiguration and ColliderConfiguration together which is used when creating a Simulated Body.
using ShapeColliderPair = AZStd::pair<Physics::ColliderConfiguration*, Physics::ShapeConfiguration*>;
using ShapeColliderPair = AZStd::pair<
AZStd::shared_ptr<Physics::ColliderConfiguration>,
AZStd::shared_ptr<Physics::ShapeConfiguration>>;
using ShapeColliderPairList = AZStd::vector<ShapeColliderPair>;
//! Flags used to specifying which properties of a body to compute.
enum class MassComputeFlags : AZ::u8
@@ -80,9 +80,6 @@ namespace Physics
void OnContactOffsetChanged();
};
using ShapeConfigurationPair = AZStd::pair<AZStd::shared_ptr<ColliderConfiguration>, AZStd::shared_ptr<ShapeConfiguration>>;
using ShapeConfigurationList = AZStd::vector<ShapeConfigurationPair>;
struct RayCastRequest;
class Shape
@@ -264,12 +264,12 @@ namespace AzFramework
// SampleRPC =
// {
// // Two callbacks can be registered to the NetRPC
// // A function to be invoked on the Master - OnMaster
// // A function to be invoked on the main server - OnServer
// // and a function to be invoked on the Proxy - OnProxy
// //
// // Every NetRPC needs to have a valid OnMaster function, while OnProxy is optional.
// OnMaster = function()
// Debug.Log("Function to be invoked on the Master.");
// // Every NetRPC needs to have a valid OnServer function, while OnProxy is optional.
// OnServer = function()
// Debug.Log("Function to be invoked on the server.");
// end
//
// OnProxy = function()
@@ -84,6 +84,7 @@ namespace AzFramework
};
using EntitySpawnCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableConstEntityContainerView)>;
using EntityPreInsertionCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableEntityContainerView)>;
using EntityDespawnCallback = AZStd::function<void(EntitySpawnTicket&)>;
using ReloadSpawnableCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableConstEntityContainerView)>;
using ListEntitiesCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableConstEntityContainerView)>;
@@ -110,7 +111,8 @@ namespace AzFramework
//! @param completionCallback Optional callback that's called when spawning entities has completed. This can be called from
//! a different thread than the one that made the function call. The returned list of entities contains all the newly
//! created entities.
virtual void SpawnAllEntities(EntitySpawnTicket& ticket, EntitySpawnCallback completionCallback = {}) = 0;
virtual void SpawnAllEntities(EntitySpawnTicket& ticket, EntityPreInsertionCallback preInsertionCallback = {},
EntitySpawnCallback completionCallback = {}) = 0;
//! Spawn instances of some entities in the spawnable.
//! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them.
//! @param entityIndices The indices into the template entities stored in the spawnable that will be used to spawn entities from.
@@ -118,7 +120,7 @@ namespace AzFramework
//! a different thread than the one that made this function call. The returned list of entities contains all the newly
//! created entities.
virtual void SpawnEntities(EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices,
EntitySpawnCallback completionCallback = {}) = 0;
EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) = 0;
//! Removes all entities in the provided list from the environment.
//! @param ticket The ticket previously used to spawn entities with.
//! @param completionCallback Optional callback that's called when despawning entities has completed. This can be called from
@@ -14,17 +14,20 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/parallel/scoped_lock.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Spawnable/Spawnable.h>
#include <AzFramework/Spawnable/SpawnableEntitiesManager.h>
namespace AzFramework
{
void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, EntitySpawnCallback completionCallback)
void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, EntityPreInsertionCallback preInsertionCallback,
EntitySpawnCallback completionCallback)
{
SpawnAllEntitiesCommand queueEntry;
queueEntry.m_ticket = &ticket;
queueEntry.m_completionCallback = AZStd::move(completionCallback);
queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback);
{
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
queueEntry.m_ticketId = GetTicketPayload<Ticket>(ticket).m_nextTicketId++;
@@ -32,13 +35,15 @@ namespace AzFramework
}
}
void SpawnableEntitiesManager::SpawnEntities(EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices,
EntitySpawnCallback completionCallback)
void SpawnableEntitiesManager::SpawnEntities(
EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices,
EntityPreInsertionCallback preInsertionCallback, EntitySpawnCallback completionCallback)
{
SpawnEntitiesCommand queueEntry;
queueEntry.m_ticket = &ticket;
queueEntry.m_entityIndices = AZStd::move(entityIndices);
queueEntry.m_completionCallback = AZStd::move(completionCallback);
queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback);
{
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
queueEntry.m_ticketId = GetTicketPayload<Ticket>(ticket).m_nextTicketId++;
@@ -205,6 +210,7 @@ namespace AzFramework
AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate);
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
clone->SetId(AZ::Entity::MakeId());
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, clone);
return clone;
}
@@ -214,23 +220,79 @@ namespace AzFramework
Ticket& ticket = GetTicketPayload<Ticket>(*request.m_ticket);
if (ticket.m_spawnable.IsReady() && request.m_ticketId == ticket.m_currentTicketId)
{
size_t spawnedEntitiesCount = ticket.m_spawnedEntities.size();
AZStd::vector<AZ::Entity*>& spawnedEntities = ticket.m_spawnedEntities;
AZStd::vector<size_t>& spawnedEntityIndices = ticket.m_spawnedEntityIndices;
const Spawnable::EntityList& entities = ticket.m_spawnable->GetEntities();
size_t entitiesSize = entities.size();
ticket.m_spawnedEntities.reserve(ticket.m_spawnedEntities.size() + entitiesSize);
ticket.m_spawnedEntityIndices.reserve(ticket.m_spawnedEntityIndices.size() + entitiesSize);
// Keep track how many entities there were in the array initially
size_t spawnedEntitiesInitialCount = spawnedEntities.size();
for(size_t i=0; i<entitiesSize; ++i)
// These are 'template' entities we'll be cloning from
const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities();
size_t entitiesToSpawnSize = entitiesToSpawn.size();
// Reserve buffers
spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize);
ticket.m_spawnedEntityIndices.reserve(ticket.m_spawnedEntityIndices.size() + entitiesToSpawnSize);
// TEMP: To be replaced by IdUtils::Remapper
using EntityIdMap = AZStd::unordered_map<AZ::EntityId, AZ::EntityId>;
EntityIdMap templateToCloneIdMap;
// \TEMP
// Clone the entities from Spawnable
for (size_t i = 0; i < entitiesToSpawnSize; ++i)
{
ticket.m_spawnedEntities.push_back(SpawnSingleEntity(*entities[i], serializeContext));
ticket.m_spawnedEntityIndices.push_back(i);
const AZ::Entity& entityTemplate = *entitiesToSpawn[i];
AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate);
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
clone->SetId(AZ::Entity::MakeId());
spawnedEntities.push_back(clone);
spawnedEntityIndices.push_back(i);
// TEMP: To be replaced by IdUtils::Remapper
templateToCloneIdMap[entityTemplate.GetId()] = clone->GetId();
// Update TransformComponent parent Id. It is guaranteed for the entities array to be sorted from parent->child here.
auto* transformComponent = clone->FindComponent<AzFramework::TransformComponent>();
AZ::EntityId parentId = transformComponent->GetParentId();
if (parentId.IsValid())
{
auto it = templateToCloneIdMap.find(parentId);
if (it != templateToCloneIdMap.end())
{
transformComponent->SetParentRelative(it->second);
}
else
{
AZ_Warning(
"SpawnableEntitiesManager", false, "Entity %s doesn't have the parent entity %s present in the spawnable",
clone->GetName().c_str(), parentId.ToString().data());
}
}
// \TEMP
}
// Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context.
if (request.m_preInsertionCallback)
{
request.m_preInsertionCallback(*request.m_ticket, SpawnableEntityContainerView(
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
}
// Add to the game context, now the entities are active
AZStd::for_each(ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end(),
[](AZ::Entity* entity)
{
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, entity);
});
// Let other systems know about newly spawned entities for any post-processing after adding to the scene/game context.
if (request.m_completionCallback)
{
request.m_completionCallback(*request.m_ticket, SpawnableConstEntityContainerView(
ticket.m_spawnedEntities.begin() + spawnedEntitiesCount, ticket.m_spawnedEntities.end()));
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
}
m_onSpawnedEvent.Signal(ticket.m_spawnable);
@@ -249,24 +311,56 @@ namespace AzFramework
Ticket& ticket = GetTicketPayload<Ticket>(*request.m_ticket);
if (ticket.m_spawnable.IsReady() && request.m_ticketId == ticket.m_currentTicketId)
{
size_t spawnedEntitiesCount = ticket.m_spawnedEntities.size();
AZStd::vector<AZ::Entity*>& spawnedEntities = ticket.m_spawnedEntities;
AZStd::vector<size_t>& spawnedEntityIndices = ticket.m_spawnedEntityIndices;
const Spawnable::EntityList& entities = ticket.m_spawnable->GetEntities();
size_t entitiesSize = entities.size();
ticket.m_spawnedEntities.reserve(ticket.m_spawnedEntities.size() + entitiesSize);
ticket.m_spawnedEntityIndices.reserve(ticket.m_spawnedEntityIndices.size() + entitiesSize);
// Keep track how many entities there were in the array initially
size_t spawnedEntitiesInitialCount = spawnedEntities.size();
// These are 'template' entities we'll be cloning from
const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities();
size_t entitiesToSpawnSize = request.m_entityIndices.size();
spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize);
spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize);
for (size_t index : request.m_entityIndices)
{
ticket.m_spawnedEntities.push_back(SpawnSingleEntity(*entities[index], serializeContext));
ticket.m_spawnedEntityIndices.push_back(index);
if (index < entitiesToSpawn.size())
{
const AZ::Entity& entityTemplate = *entitiesToSpawn[index];
AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate);
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
clone->SetId(AZ::Entity::MakeId());
spawnedEntities.push_back(clone);
spawnedEntityIndices.push_back(index);
}
}
ticket.m_loadAll = false;
// Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context.
if (request.m_preInsertionCallback)
{
request.m_preInsertionCallback(
*request.m_ticket,
SpawnableEntityContainerView(
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
}
// Add to the game context, now the entities are active
AZStd::for_each(ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end(),
[](AZ::Entity* entity)
{
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, entity);
});
if (request.m_completionCallback)
{
request.m_completionCallback(*request.m_ticket, SpawnableConstEntityContainerView(
ticket.m_spawnedEntities.begin() + spawnedEntitiesCount, ticket.m_spawnedEntities.end()));
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
}
m_onSpawnedEvent.Signal(ticket.m_spawnable);
@@ -47,8 +47,8 @@ namespace AzFramework
// The following functions are thread safe
//
void SpawnAllEntities(EntitySpawnTicket& ticket, EntitySpawnCallback completionCallback = {}) override;
void SpawnEntities(EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices,
void SpawnAllEntities(EntitySpawnTicket& ticket, EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) override;
void SpawnEntities(EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, EntityPreInsertionCallback preInsertionCallback = {},
EntitySpawnCallback completionCallback = {}) override;
void DespawnAllEntities(EntitySpawnTicket& ticket, EntityDespawnCallback completionCallback = {}) override;
@@ -90,6 +90,7 @@ namespace AzFramework
struct SpawnAllEntitiesCommand
{
EntitySpawnCallback m_completionCallback;
EntityPreInsertionCallback m_preInsertionCallback;
EntitySpawnTicket* m_ticket;
uint32_t m_ticketId;
};
@@ -97,6 +98,7 @@ namespace AzFramework
{
AZStd::vector<size_t> m_entityIndices;
EntitySpawnCallback m_completionCallback;
EntityPreInsertionCallback m_preInsertionCallback;
EntitySpawnTicket* m_ticket;
uint32_t m_ticketId;
};
@@ -41,7 +41,7 @@ namespace Neighborhood {
//---------------------------------------------------------------------
void NeighborReplica::OnReplicaActivate(const GridMate::ReplicaContext& /*rc*/)
{
// TODO: Should we send the message to ourselves as well (master)?
// TODO: Should we send the message to ourselves as well?
if (IsProxy())
{
AZ_Assert(m_persistentName.Get().c_str(), "Received NeighborReplica with missing persistent name!");
@@ -52,7 +52,7 @@ namespace Neighborhood {
//---------------------------------------------------------------------
void NeighborReplica::OnReplicaDeactivate(const GridMate::ReplicaContext& /*rc*/)
{
// TODO: Should we send the message to ourselves as well (master)?
// TODO: Should we send the message to ourselves as well?
if (IsProxy())
{
EBUS_EVENT(NeighborhoodBus, OnNodeLeft, *this);
@@ -30,7 +30,8 @@ namespace AzFramework
AZ_CVAR(float, ed_cameraSystemOrbitDollyScrollSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemOrbitDollyCursorSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemScrollTranslateSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemMaxOrbitDistance, 60.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemMinOrbitDistance, 6.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemMaxOrbitDistance, 50.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemLookSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemTranslateSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemRotateSpeed, 0.005f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
@@ -532,20 +533,39 @@ namespace AzFramework
if (Beginning())
{
float hit_distance = 0.0f;
AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3::CreateAxisZ(), AZ::Vector3::CreateAxisZ(ed_cameraSystemDefaultPlaneHeight))
.CastRay(targetCamera.Translation(), targetCamera.Rotation().GetBasisY(), hit_distance);
const auto hasLookAt = [&nextCamera, &targetCamera, lookAtFn = m_lookAtFn] {
if (lookAtFn)
{
if (const auto lookAt = lookAtFn())
{
auto transform = AZ::Transform::CreateLookAt(targetCamera.m_lookAt, *lookAt);
nextCamera.m_lookDist = -lookAt->GetDistance(targetCamera.m_lookAt);
UpdateCameraFromTransform(nextCamera, transform);
if (hit_distance > 0.0f)
return true;
}
}
return false;
}();
if (!hasLookAt)
{
hit_distance = AZStd::min<float>(hit_distance, ed_cameraSystemMaxOrbitDistance);
nextCamera.m_lookDist = -hit_distance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * hit_distance;
}
else
{
nextCamera.m_lookDist = -ed_cameraSystemMaxOrbitDistance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * ed_cameraSystemMaxOrbitDistance;
float hit_distance = 0.0f;
AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3::CreateAxisZ(), AZ::Vector3::CreateAxisZ(ed_cameraSystemDefaultPlaneHeight))
.CastRay(targetCamera.Translation(), targetCamera.Rotation().GetBasisY(), hit_distance);
if (hit_distance > 0.0f)
{
hit_distance = AZStd::min<float>(hit_distance, ed_cameraSystemMaxOrbitDistance);
nextCamera.m_lookDist = -hit_distance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * hit_distance;
}
else
{
nextCamera.m_lookDist = -ed_cameraSystemMinOrbitDistance;
nextCamera.m_lookAt =
targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * ed_cameraSystemMinOrbitDistance;
}
}
}
@@ -199,6 +199,7 @@ namespace AzFramework
public:
explicit RotateCameraInput(InputChannelId rotateChannelId);
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
@@ -239,6 +240,7 @@ namespace AzFramework
public:
PanCameraInput(InputChannelId panChannelId, PanAxesFn panAxesFn);
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
@@ -279,6 +281,7 @@ namespace AzFramework
public:
explicit TranslateCameraInput(TranslationAxesFn translationAxesFn);
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
void ResetImpl() override;
@@ -348,6 +351,7 @@ namespace AzFramework
class OrbitDollyScrollCameraInput : public CameraInput
{
public:
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
};
@@ -357,6 +361,7 @@ namespace AzFramework
public:
explicit OrbitDollyCursorMoveCameraInput(InputChannelId dollyChannelId);
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
@@ -367,6 +372,7 @@ namespace AzFramework
class ScrollTranslationCameraInput : public CameraInput
{
public:
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
};
@@ -374,16 +380,32 @@ namespace AzFramework
class OrbitCameraInput : public CameraInput
{
public:
using LookAtFn = AZStd::function<AZStd::optional<AZ::Vector3>()>;
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
bool Exclusive() const override
{
return true;
}
bool Exclusive() const override;
Cameras m_orbitCameras;
//! Override the default behavior for how a look-at point is calculated.
void SetLookAtFn(const LookAtFn& lookAtFn);
private:
LookAtFn m_lookAtFn;
};
inline void OrbitCameraInput::SetLookAtFn(const LookAtFn& lookAtFn)
{
m_lookAtFn = lookAtFn;
}
inline bool OrbitCameraInput::Exclusive() const
{
return true;
}
struct WindowSize;
//! Map from a generic InputChannel event to a camera specific InputEvent.
@@ -116,8 +116,8 @@ namespace AzNetworking
int32_t TcpSocket::Receive(uint8_t* outData, uint32_t size) const
{
AZ_Assert(size > 0, "Invalid data size for send");
AZ_Assert(outData != nullptr, "NULL data pointer passed to send");
AZ_Assert(size > 0, "Invalid data size for receive");
AZ_Assert(outData != nullptr, "NULL data pointer passed to receive");
if (!IsOpen())
{
return SocketOpResultErrorNotOpen;
@@ -176,7 +176,7 @@ namespace AzNetworking
if (::bind(aznumeric_cast<int32_t>(m_socketFd), (const sockaddr*)&hints, sizeof(hints)) != 0)
{
const int32_t error = GetLastNetworkError();
AZLOG_ERROR("Failed to bind socket (%d:%s)", error, GetNetworkErrorDesc(error));
AZLOG_ERROR("Failed to bind TCP socket to port %u (%d:%s)", uint32_t(port), error, GetNetworkErrorDesc(error));
return false;
}
@@ -162,7 +162,7 @@ namespace AzNetworking
const UdpReaderThread::ReceivedPackets* packets = m_readerThread.GetReceivedPackets(m_socket.get());
if (packets == nullptr)
{
AZ_Assert(false, "nullptr was retrieved for the received packet buffer, check that the socket has been registered with the reader thread");
// Socket is not yet registered with the reader thread and is likely still pending, try again later
return;
}
@@ -82,7 +82,7 @@ namespace AzNetworking
if (::bind(static_cast<int32_t>(m_socketFd), (const sockaddr *)&hints, sizeof(hints)) != 0)
{
const int32_t error = GetLastNetworkError();
AZLOG_ERROR("Failed to bind socket to port %u (%d:%s)", uint32_t(port), error, GetNetworkErrorDesc(error));
AZLOG_ERROR("Failed to bind UDP socket to port %u (%d:%s)", uint32_t(port), error, GetNetworkErrorDesc(error));
return false;
}
}
@@ -12,6 +12,7 @@
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
@@ -46,6 +47,10 @@ namespace AzToolsFramework
virtual Prefab::InstanceOptionalReference GetRootPrefabInstance() = 0;
//! Get all Assets generated by Prefab processing when entering Play-In Editor mode (Ctrl+G)
//! /return The vector of Assets generated by Prefab processing
virtual const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() = 0;
virtual bool LoadFromStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) = 0;
virtual bool SaveToStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) = 0;
@@ -314,6 +314,11 @@ namespace AzToolsFramework
return *m_rootInstance;
}
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData()
{
return m_playInEditorData.m_assets;
}
void PrefabEditorEntityOwnershipService::OnEntityRemoved(AZ::EntityId entityId)
{
AzFramework::SliceEntityRequestBus::MultiHandler::BusDisconnect(entityId);
@@ -195,6 +195,8 @@ namespace AzToolsFramework
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder) override;
Prefab::InstanceOptionalReference GetRootPrefabInstance() override;
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() override;
//////////////////////////////////////////////////////////////////////////
void OnEntityRemoved(AZ::EntityId entityId);
@@ -134,9 +134,13 @@ namespace AzToolsFramework
}
}
auto findInstancesResult = m_templateInstanceMapperInterface->FindInstancesOwnedByTemplate(instanceTemplateId)->get();
auto findInstancesResult = m_templateInstanceMapperInterface->FindInstancesOwnedByTemplate(instanceTemplateId);
AZ_Assert(
findInstancesResult.has_value(), "Prefab Instances corresponding to template with id %llu couldn't be found.",
instanceTemplateId);
if (findInstancesResult.find(instanceToUpdate) == findInstancesResult.end())
if (findInstancesResult == AZStd::nullopt ||
findInstancesResult->get().find(instanceToUpdate) == findInstancesResult->get().end())
{
// Since nested instances get reconstructed during propagation, remove any nested instance that no longer
// maps to a template.
@@ -182,16 +182,16 @@ namespace AzToolsFramework
else
{
AZ::JsonSerializationResult::ResultCode applyPatchResult = AZ::JsonSerialization::ApplyPatch(
linkedInstanceDom,
sourceTemplateDomCopy,
targetTemplatePrefabDom.GetAllocator(),
sourceTemplatePrefabDom,
patchesReference->get(),
AZ::JsonMergeApproach::JsonPatch);
linkedInstanceDom.CopyFrom(sourceTemplateDomCopy, targetTemplatePrefabDom.GetAllocator());
if (applyPatchResult.GetProcessing() != AZ::JsonSerializationResult::Processing::Completed)
{
AZ_Error("Prefab", false,
"Link::UpdateTarget - "
"ApplyPatches failed for Prefab DOM from source Template '%u' and target Template '%u'.",
AZ_Error(
"Prefab", false,
"Link::UpdateTarget - ApplyPatches failed for Prefab DOM from source Template '%u' and target Template '%u'.",
m_sourceTemplateId, m_targetTemplateId);
return false;
}
@@ -122,11 +122,14 @@ namespace AzToolsFramework
AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId();
// Parent the entities to the container entity. Parenting the container entities of the instances passed to createPrefab
// will be done during the creation of links below.
for (AZ::Entity* topLevelEntity : entities)
// Parent the non-container top level entities to the container entity.
// Parenting the top level container entities will be done during the creation of links.
for (AZ::Entity* topLevelEntity : topLevelEntities)
{
AZ::TransformBus::Event(topLevelEntity->GetId(), &AZ::TransformBus::Events::SetParent, containerEntityId);
if (!IsInstanceContainerEntity(topLevelEntity->GetId()))
{
AZ::TransformBus::Event(topLevelEntity->GetId(), &AZ::TransformBus::Events::SetParent, containerEntityId);
}
}
// Update the template of the instance since the entities are modified since the template creation.
@@ -142,11 +145,25 @@ namespace AzToolsFramework
AZ_Assert(
nestedInstanceContainerEntity, "Invalid container entity found for the nested instance used in prefab creation.");
AZ::EntityId parentId;
AZ::TransformBus::EventResult(
parentId, nestedInstanceContainerEntity->get().GetId(), &AZ::TransformBus::Events::GetParentId);
auto entityIterator = AZStd::find_if(
entities.begin(), entities.end(), [parentId](AZ::Entity* entity) { return entity->GetId() == parentId; });
// If the previous parent entity of the nested instance is not part of the entities of the newly created prefab,
// then set the parent of the nested prefab as the container entity of the newly created prefab.
if (entityIterator == entities.end())
{
parentId = containerEntityId;
}
// These link creations shouldn't be undone because that would put the template in a non-usable state if a user
// chooses to instantiate the template after undoing the creation.
CreateLink(
{&nestedInstanceContainerEntity->get()}, *nestedInstance, instanceToCreate->get().GetTemplateId(),
undoBatch.GetUndoBatch(), containerEntityId, false);
undoBatch.GetUndoBatch(), parentId, false);
});
// Create a link between the templates of the newly created instance and the instance it's being parented under.
@@ -296,7 +296,7 @@ DefaultTrafficControl::OnReceived(TrafficControlConnectionId id, DataGramControl
if (m_maxRecvPackets != 0)
{
--cd->m_recvPacketAllowance;
if (cd->m_recvPacketAllowance == 0) // hit the limit -> let's blacklist connection
if (cd->m_recvPacketAllowance == 0) // hit the limit
{
cd->m_canReceiveData = false;
}
@@ -49,6 +49,7 @@
#include <AzToolsFramework/API/ComponentEntityObjectBus.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
// AtomToolsFramework
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
@@ -1238,6 +1239,20 @@ void EditorViewportWidget::SetViewportId(int id)
auto firstPersonWheelCamera = AZStd::make_shared<AzFramework::ScrollTranslationCameraInput>();
auto orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>();
orbitCamera->SetLookAtFn([]() -> AZStd::optional<AZ::Vector3> {
AZStd::optional<AZ::Transform> manipulatorTransform;
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
if (manipulatorTransform)
{
return manipulatorTransform->GetTranslation();
}
return {};
});
auto orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::CameraOrbitLookButton);
auto orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::OrbitTranslation);
auto orbitDollyWheelCamera = AZStd::make_shared<AzFramework::OrbitDollyScrollCameraInput>();
@@ -200,11 +200,11 @@ namespace RedirectOutput
{
s_RedirectModule = module;
SetRedirection("stdout", g_redirect_stdout_saved, g_redirect_stdout, [](const char* msg) {
SetRedirection("stdout", g_redirect_stdout_saved, g_redirect_stdout, []([[maybe_unused]] const char* msg) {
AZ_TracePrintf("Python", msg);
});
SetRedirection("stderr", g_redirect_stderr_saved, g_redirect_stderr, [](const char* msg) {
SetRedirection("stderr", g_redirect_stderr_saved, g_redirect_stderr, []([[maybe_unused]] const char* msg) {
AZ_TracePrintf("Python", msg);
});
@@ -173,8 +173,7 @@ namespace AZ
return false;
}
const SliceComponent::SliceList& sliceList = sliceComponent->GetSlices();
AZ_Warning("Convert-Slice", sliceList.empty(), " Slice depends on other slices, this conversion will lose data.\n");
AZ_Warning("Convert-Slice", sliceComponent->GetSlices().empty(), " Slice depends on other slices, this conversion will lose data.\n");
// Create the Prefab with the entities from the slice
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> sourceInstance(
@@ -10,8 +10,7 @@
*
*/
#ifndef DRILLER_WORKSPACE_SETTINGS_MASTER_H
#define DRILLER_WORKSPACE_SETTINGS_MASTER_H
#pragma once
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/unordered_map.h>
@@ -77,7 +76,3 @@ namespace Driller
SavedWorkspaceMap m_WorkspaceSaveData;
};
}
#pragma once
#endif // DRILLER_WORKSPACE_SETTINGS_MASTER_H
@@ -81,12 +81,12 @@ namespace AssetMemoryAnalyzer
using AssetTree = AZ::Debug::AssetTree<Data::AssetData>;
using AssetTreeNode = typename AssetTree::NodeType;
using AllocationTable = AZ::Debug::AllocationTable<Data::AllocationData>;
using MasterCodePoints = AZStd::unordered_set<Data::CodePoint, AZStd::hash<Data::CodePoint>, AZStd::equal_to<Data::CodePoint>, AZ::Debug::AZStdAssetTrackingAllocator>;
using CodePoints = AZStd::unordered_set<Data::CodePoint, AZStd::hash<Data::CodePoint>, AZStd::equal_to<Data::CodePoint>, AZ::Debug::AZStdAssetTrackingAllocator>;
using mutex_type = AZStd::mutex;
using lock_type = AZStd::lock_guard<mutex_type>;
mutex_type m_mutex;
MasterCodePoints m_masterCodePoints;
CodePoints m_codePoints;
AssetTree m_assetTree;
AllocationTable m_allocationTable;
AZ::Debug::AssetTracking m_assetTracking;
@@ -195,7 +195,7 @@ namespace AssetMemoryAnalyzer
{
// Store a record for this allocation, at this code-point
lock_type lock(m_mutex);
auto insertResult = m_masterCodePoints.emplace(Data::CodePoint{ fileName ? fileName : "<unknown>", lineNum, category });
auto insertResult = m_codePoints.emplace(Data::CodePoint{ fileName ? fileName : "<unknown>", lineNum, category });
Data::CodePoint* cp = &*insertResult.first;
m_allocationTable.Get().emplace(address, AllocationTable::RecordType{ activeAsset, (uint32_t)byteSize, Data::AllocationData{ cp, categoryInfo } });
static_cast<typename AssetTree::NodeType*>(activeAsset)->m_data.m_totalAllocations[(int)category]++;
@@ -1199,6 +1199,14 @@
"file": "./StandardPBR_ForwardPass_EDS.shader",
"tag": "ForwardPass_EDS"
},
{
"file": "./StandardPBR_LowEndForward.shader",
"tag": "LowEndForward"
},
{
"file": "./StandardPBR_LowEndForward_EDS.shader",
"tag": "LowEndForward_EDS"
},
{
"file": "Shaders/Shadow/Shadowmap.shader",
"tag": "Shadowmap"
@@ -1289,10 +1297,6 @@
"textureProperty": "baseColor.textureMap",
"useTextureProperty": "baseColor.useTexture",
"dependentProperties": ["baseColor.textureMapUv", "baseColor.textureBlendMode"],
"shaderTags": [
"ForwardPass",
"ForwardPass_EDS"
],
"shaderOption": "o_baseColor_useTexture"
}
},
@@ -1302,10 +1306,6 @@
"textureProperty": "metallic.textureMap",
"useTextureProperty": "metallic.useTexture",
"dependentProperties": ["metallic.textureMapUv"],
"shaderTags": [
"ForwardPass",
"ForwardPass_EDS"
],
"shaderOption": "o_metallic_useTexture"
}
},
@@ -1315,10 +1315,6 @@
"textureProperty": "specularF0.textureMap",
"useTextureProperty": "specularF0.useTexture",
"dependentProperties": ["specularF0.textureMapUv"],
"shaderTags": [
"ForwardPass",
"ForwardPass_EDS"
],
"shaderOption": "o_specularF0_useTexture"
}
},
@@ -1328,10 +1324,6 @@
"textureProperty": "normal.textureMap",
"useTextureProperty": "normal.useTexture",
"dependentProperties": ["normal.textureMapUv", "normal.factor", "normal.flipX", "normal.flipY"],
"shaderTags": [
"ForwardPass",
"ForwardPass_EDS"
],
"shaderOption": "o_normal_useTexture"
}
},
@@ -10,6 +10,8 @@
*
*/
#include "Atom/Features/ShaderQualityOptions.azsli"
#include "StandardPBR_Common.azsli"
// SRGs
@@ -317,13 +319,18 @@ ForwardPassOutputWithDepth StandardPbr_ForwardPassPS(VSOutput IN, bool isFrontFa
PbrLightingOutput lightingOutput = ForwardPassPS_Common(IN, isFrontFace, depth);
#ifdef UNIFIED_FORWARD_OUTPUT
OUT.m_color.rgb = lightingOutput.m_diffuseColor.rgb + lightingOutput.m_specularColor.rgb;
OUT.m_color.a = lightingOutput.m_diffuseColor.a;
OUT.m_depth = depth;
#else
OUT.m_diffuseColor = lightingOutput.m_diffuseColor;
OUT.m_specularColor = lightingOutput.m_specularColor;
OUT.m_specularF0 = lightingOutput.m_specularF0;
OUT.m_albedo = lightingOutput.m_albedo;
OUT.m_normal = lightingOutput.m_normal;
OUT.m_depth = depth;
#endif
return OUT;
}
@@ -335,12 +342,16 @@ ForwardPassOutput StandardPbr_ForwardPassPS_EDS(VSOutput IN, bool isFrontFace :
PbrLightingOutput lightingOutput = ForwardPassPS_Common(IN, isFrontFace, depth);
#ifdef UNIFIED_FORWARD_OUTPUT
OUT.m_color.rgb = lightingOutput.m_diffuseColor.rgb + lightingOutput.m_specularColor.rgb;
OUT.m_color.a = lightingOutput.m_diffuseColor.a;
#else
OUT.m_diffuseColor = lightingOutput.m_diffuseColor;
OUT.m_specularColor = lightingOutput.m_specularColor;
OUT.m_specularF0 = lightingOutput.m_specularF0;
OUT.m_albedo = lightingOutput.m_albedo;
OUT.m_normal = lightingOutput.m_normal;
#endif
return OUT;
}
@@ -0,0 +1,17 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// NOTE: This file is a temporary workaround until .shader files can #define macros for their .azsl files
#define QUALITY_LOW_END 1
#include "StandardPBR_ForwardPass.azsl"
@@ -0,0 +1,59 @@
{
// Note: "LowEnd" shaders are for supporting the low end pipeline
// These shaders can be safely added to materials without incurring additional runtime draw
// items as draw items for shaders are only created if the scene has a pass with a matching
// DrawListTag. If your pipeline doesn't have a "lowEndForward" DrawListTag, no draw items
// for this shader will be created.
"Source" : "./StandardPBR_LowEndForward.azsl",
"DepthStencilState" :
{
"Depth" :
{
"Enable" : true,
"CompareFunc" : "GreaterEqual"
},
"Stencil" :
{
"Enable" : true,
"ReadMask" : "0x00",
"WriteMask" : "0xFF",
"FrontFace" :
{
"Func" : "Always",
"DepthFailOp" : "Keep",
"FailOp" : "Keep",
"PassOp" : "Replace"
},
"BackFace" :
{
"Func" : "Always",
"DepthFailOp" : "Keep",
"FailOp" : "Keep",
"PassOp" : "Replace"
}
}
},
"CompilerHints" : {
"DisableOptimizations" : false
},
"ProgramSettings":
{
"EntryPoints":
[
{
"name": "StandardPbr_ForwardPassVS",
"type": "Vertex"
},
{
"name": "StandardPbr_ForwardPassPS",
"type": "Fragment"
}
]
},
"DrawList" : "lowEndForward"
}
@@ -0,0 +1,59 @@
{
// Note: "LowEnd" shaders are for supporting the low end pipeline
// These shaders can be safely added to materials without incurring additional runtime draw
// items as draw items for shaders are only created if the scene has a pass with a matching
// DrawListTag. If your pipeline doesn't have a "lowEndForward" DrawListTag, no draw items
// for this shader will be created.
"Source" : "./StandardPBR_LowEndForward.azsl",
"DepthStencilState" :
{
"Depth" :
{
"Enable" : true,
"CompareFunc" : "GreaterEqual"
},
"Stencil" :
{
"Enable" : true,
"ReadMask" : "0x00",
"WriteMask" : "0xFF",
"FrontFace" :
{
"Func" : "Always",
"DepthFailOp" : "Keep",
"FailOp" : "Keep",
"PassOp" : "Replace"
},
"BackFace" :
{
"Func" : "Always",
"DepthFailOp" : "Keep",
"FailOp" : "Keep",
"PassOp" : "Replace"
}
}
},
"CompilerHints" : {
"DisableOptimizations" : false
},
"ProgramSettings":
{
"EntryPoints":
[
{
"name": "StandardPbr_ForwardPassVS",
"type": "Vertex"
},
{
"name": "StandardPbr_ForwardPassPS_EDS",
"type": "Fragment"
}
]
},
"DrawList" : "lowEndForward"
}
@@ -29,26 +29,33 @@ function Process(context)
local depthPass = context:GetShaderByTag("DepthPass")
local shadowMap = context:GetShaderByTag("Shadowmap")
local forwardPassEDS = context:GetShaderByTag("ForwardPass_EDS")
local lowEndForwardEDS = context:GetShaderByTag("LowEndForward_EDS")
local depthPassWithPS = context:GetShaderByTag("DepthPass_WithPS")
local shadowMapWitPS = context:GetShaderByTag("Shadowmap_WithPS")
local forwardPass = context:GetShaderByTag("ForwardPass")
local lowEndForward = context:GetShaderByTag("LowEndForward")
if parallaxEnabled and parallaxPdoEnabled then
depthPass:SetEnabled(false)
shadowMap:SetEnabled(false)
forwardPassEDS:SetEnabled(false)
lowEndForwardEDS:SetEnabled(false)
depthPassWithPS:SetEnabled(true)
shadowMapWitPS:SetEnabled(true)
forwardPass:SetEnabled(true)
lowEndForward:SetEnabled(true)
else
depthPass:SetEnabled(opacityMode == OpacityMode_Opaque)
shadowMap:SetEnabled(opacityMode == OpacityMode_Opaque)
forwardPassEDS:SetEnabled((opacityMode == OpacityMode_Opaque) or (opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent))
lowEndForwardEDS:SetEnabled((opacityMode == OpacityMode_Opaque) or (opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent))
depthPassWithPS:SetEnabled(opacityMode == OpacityMode_Cutout)
shadowMapWitPS:SetEnabled(opacityMode == OpacityMode_Cutout)
forwardPass:SetEnabled(opacityMode == OpacityMode_Cutout)
lowEndForward:SetEnabled(opacityMode == OpacityMode_Cutout)
end
context:GetShaderByTag("DepthPassTransparentMin"):SetEnabled((opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent))
@@ -148,22 +148,6 @@
},
"LoadAction": "Clear"
}
},
{
"Name": "ScatterDistanceOutput",
"SlotType": "Output",
"ScopeAttachmentUsage": "RenderTarget",
"LoadStoreAction": {
"ClearValue": {
"Value": [
0.0,
0.0,
0.0,
0.0
]
},
"LoadAction": "Clear"
}
}
],
"ImageAttachments": [
@@ -238,19 +222,6 @@
"AssetRef": {
"FilePath": "Textures/BRDFTexture.attimage"
}
},
{
"Name": "ScatterDistanceImage",
"SizeSource": {
"Source": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
},
"ImageDescriptor": {
"Format": "R11G11B10_FLOAT",
"SharedQueueMask": "Graphics"
}
}
],
"Connections": [
@@ -295,13 +266,6 @@
"Pass": "This",
"Attachment": "BRDFTexture"
}
},
{
"LocalSlot": "ScatterDistanceOutput",
"AttachmentRef": {
"Pass": "This",
"Attachment": "ScatterDistanceImage"
}
}
]
}
@@ -0,0 +1,146 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "PassAsset",
"ClassData": {
"PassTemplate": {
"Name": "LightAdaptationParentTemplate",
"PassClass": "ParentPass",
"Slots": [
// Inputs...
{
"Name": "LightingInput",
"SlotType": "Input"
},
// SwapChain here is only used to reference the frame height and format
{
"Name": "SwapChainOutput",
"SlotType": "InputOutput"
},
// Outputs...
{
"Name": "Output",
"SlotType": "Output"
},
// Debug Outputs...
{
"Name": "LuminanceMipChainOutput",
"SlotType": "Output"
}
],
"Connections": [
{
"LocalSlot": "Output",
"AttachmentRef": {
"Pass": "DisplayMapperPass",
"Attachment": "Output"
}
},
{
"LocalSlot": "LuminanceMipChainOutput",
"AttachmentRef": {
"Pass": "DownsampleLuminanceMipChain",
"Attachment": "MipChainInputOutput"
}
}
],
"PassRequests": [
{
"Name": "DownsampleLuminanceMinAvgMax",
"TemplateName": "DownsampleLuminanceMinAvgMaxCS",
"Connections": [
{
"LocalSlot": "Input",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "LightingInput"
}
}
]
},
{
"Name": "DownsampleLuminanceMipChain",
"TemplateName": "DownsampleMipChainTemplate",
"Connections": [
{
"LocalSlot": "MipChainInputOutput",
"AttachmentRef": {
"Pass": "DownsampleLuminanceMinAvgMax",
"Attachment": "Output"
}
}
],
"PassData": {
"$type": "DownsampleMipChainPassData",
"ShaderAsset": {
"FilePath": "Shaders/PostProcessing/DownsampleMinAvgMaxCS.shader"
}
}
},
{
"Name": "EyeAdaptationPass",
"TemplateName": "EyeAdaptationTemplate",
"Enabled": false,
"Connections": [
{
"LocalSlot": "SceneLuminanceInput",
"AttachmentRef": {
"Pass": "DownsampleLuminanceMipChain",
"Attachment": "MipChainInputOutput"
}
}
]
},
{
"Name": "LookModificationTransformPass",
"TemplateName": "LookModificationTransformTemplate",
"Enabled": true,
"Connections": [
{
"LocalSlot": "Input",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "LightingInput"
}
},
{
"LocalSlot": "EyeAdaptationDataInput",
"AttachmentRef": {
"Pass": "EyeAdaptationPass",
"Attachment": "EyeAdaptationDataInputOutput"
}
},
{
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
},
{
"Name": "DisplayMapperPass",
"TemplateName": "DisplayMapperTemplate",
"Enabled": true,
"Connections": [
{
"LocalSlot": "Input",
"AttachmentRef": {
"Pass": "LookModificationTransformPass",
"Attachment": "Output"
}
},
{
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
}
]
}
}
}
@@ -0,0 +1,133 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "PassAsset",
"ClassData": {
"PassTemplate": {
"Name": "LowEndForwardPassTemplate",
"PassClass": "RasterPass",
"Slots": [
// Inputs...
{
"Name": "BRDFTextureInput",
"ShaderInputName": "m_brdfMap",
"SlotType": "Input",
"ScopeAttachmentUsage": "Shader"
},
{
"Name": "DirectionalLightShadowmap",
"ShaderInputName": "m_directionalLightShadowmap",
"SlotType": "Input",
"ScopeAttachmentUsage": "Shader",
"ImageViewDesc": {
"IsArray": 1
}
},
{
"Name": "ExponentialShadowmapDirectional",
"ShaderInputName": "m_directionalLightExponentialShadowmap",
"SlotType": "Input",
"ScopeAttachmentUsage": "Shader",
"ImageViewDesc": {
"IsArray": 1
}
},
{
"Name": "ProjectedShadowmap",
"ShaderInputName": "m_projectedShadowmaps",
"SlotType": "Input",
"ScopeAttachmentUsage": "Shader",
"ImageViewDesc": {
"IsArray": 1
}
},
{
"Name": "ExponentialShadowmapProjected",
"ShaderInputName": "m_projectedExponentialShadowmap",
"SlotType": "Input",
"ScopeAttachmentUsage": "Shader",
"ImageViewDesc": {
"IsArray": 1
}
},
{
"Name": "TileLightData",
"SlotType": "Input",
"ShaderInputName": "m_tileLightData",
"ScopeAttachmentUsage": "Shader"
},
{
"Name": "LightListRemapped",
"SlotType": "Input",
"ShaderInputName": "m_lightListRemapped",
"ScopeAttachmentUsage": "Shader"
},
// Input/Outputs...
{
"Name": "DepthStencilInputOutput",
"SlotType": "InputOutput",
"ScopeAttachmentUsage": "DepthStencil"
},
// Outputs...
{
"Name": "LightingOutput",
"SlotType": "Output",
"ScopeAttachmentUsage": "RenderTarget",
"LoadStoreAction": {
"ClearValue": {
"Value": [
0.0,
0.0,
0.0,
0.0
]
},
"LoadAction": "Clear"
}
}
],
"ImageAttachments": [
{
"Name": "LightingAttachment",
"SizeSource": {
"Source": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
},
"MultisampleSource": {
"Pass": "This",
"Attachment": "DepthStencilInputOutput"
},
"ImageDescriptor": {
"Format": "R16G16B16A16_FLOAT",
"SharedQueueMask": "Graphics"
}
},
{
"Name": "BRDFTexture",
"Lifetime": "Imported",
"AssetRef": {
"FilePath": "Textures/BRDFTexture.attimage"
}
}
],
"Connections": [
{
"LocalSlot": "LightingOutput",
"AttachmentRef": {
"Pass": "This",
"Attachment": "LightingAttachment"
}
},
{
"LocalSlot": "BRDFTextureInput",
"AttachmentRef": {
"Pass": "This",
"Attachment": "BRDFTexture"
}
}
]
}
}
}
@@ -0,0 +1,344 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "PassAsset",
"ClassData": {
"PassTemplate": {
"Name": "LowEndPipelineTemplate",
"PassClass": "ParentPass",
"Slots": [
{
"Name": "SwapChainOutput",
"SlotType": "InputOutput",
"ScopeAttachmentUsage": "RenderTarget"
}
],
"PassRequests": [
{
"Name": "MorphTargetPass",
"TemplateName": "MorphTargetPassTemplate"
},
{
"Name": "SkinningPass",
"TemplateName": "SkinningPassTemplate",
"Connections": [
{
"LocalSlot": "SkinnedMeshOutputStream",
"AttachmentRef": {
"Pass": "MorphTargetPass",
"Attachment": "MorphTargetDeltaOutput"
}
}
]
},
{
"Name": "DepthPrePass",
"TemplateName": "DepthMSAAParentTemplate",
"Connections": [
{
"LocalSlot": "SkinnedMeshes",
"AttachmentRef": {
"Pass": "SkinningPass",
"Attachment": "SkinnedMeshOutputStream"
}
},
{
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
},
{
"Name": "LightCullingPass",
"TemplateName": "LightCullingParentTemplate",
"Connections": [
{
"LocalSlot": "SkinnedMeshes",
"AttachmentRef": {
"Pass": "SkinningPass",
"Attachment": "SkinnedMeshOutputStream"
}
},
{
"LocalSlot": "DepthMSAA",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "DepthMSAA"
}
},
{
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
},
{
"Name": "ShadowPass",
"TemplateName": "ShadowParentTemplate",
"Connections": [
{
"LocalSlot": "SkinnedMeshes",
"AttachmentRef": {
"Pass": "SkinningPass",
"Attachment": "SkinnedMeshOutputStream"
}
},
{
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
},
{
"Name": "ForwardPass",
"TemplateName": "LowEndForwardPassTemplate",
"Connections": [
// Inputs...
{
"LocalSlot": "DirectionalLightShadowmap",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "DirectionalShadowmap"
}
},
{
"LocalSlot": "ExponentialShadowmapDirectional",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "DirectionalESM"
}
},
{
"LocalSlot": "ProjectedShadowmap",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "ProjectedShadowmap"
}
},
{
"LocalSlot": "ExponentialShadowmapProjected",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "ProjectedESM"
}
},
{
"LocalSlot": "TileLightData",
"AttachmentRef": {
"Pass": "LightCullingPass",
"Attachment": "TileLightData"
}
},
{
"LocalSlot": "LightListRemapped",
"AttachmentRef": {
"Pass": "LightCullingPass",
"Attachment": "LightListRemapped"
}
},
// Input/Outputs...
{
"LocalSlot": "DepthStencilInputOutput",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "DepthMSAA"
}
}
],
"PassData": {
"$type": "RasterPassData",
"DrawListTag": "lowEndForward",
"PipelineViewTag": "MainCamera",
"PassSrgAsset": {
"FilePath": "shaderlib/atom/features/pbr/forwardpasssrg.azsli:PassSrg"
}
}
},
{
"Name": "SkyBoxPass",
"TemplateName": "SkyBoxTemplate",
"Enabled": true,
"Connections": [
{
"LocalSlot": "SpecularInputOutput",
"AttachmentRef": {
"Pass": "ForwardPass",
"Attachment": "LightingOutput"
}
},
{
"LocalSlot": "SkyBoxDepth",
"AttachmentRef": {
"Pass": "ForwardPass",
"Attachment": "DepthStencilInputOutput"
}
}
]
},
{
"Name": "MSAAResolvePass",
"TemplateName": "MSAAResolveColorTemplate",
"Connections": [
{
"LocalSlot": "Input",
"AttachmentRef": {
"Pass": "SkyBoxPass",
"Attachment": "SpecularInputOutput"
}
}
]
},
{
"Name": "TransparentPass",
"TemplateName": "TransparentParentTemplate",
"Connections": [
{
"LocalSlot": "DirectionalShadowmap",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "DirectionalShadowmap"
}
},
{
"LocalSlot": "DirectionalESM",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "DirectionalESM"
}
},
{
"LocalSlot": "ProjectedShadowmap",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "ProjectedShadowmap"
}
},
{
"LocalSlot": "ProjectedESM",
"AttachmentRef": {
"Pass": "ShadowPass",
"Attachment": "ProjectedESM"
}
},
{
"LocalSlot": "TileLightData",
"AttachmentRef": {
"Pass": "LightCullingPass",
"Attachment": "TileLightData"
}
},
{
"LocalSlot": "LightListRemapped",
"AttachmentRef": {
"Pass": "LightCullingPass",
"Attachment": "LightListRemapped"
}
},
{
"LocalSlot": "DepthStencil",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "Depth"
}
},
{
"LocalSlot": "InputOutput",
"AttachmentRef": {
"Pass": "MSAAResolvePass",
"Attachment": "Output"
}
}
]
},
{
"Name": "LightAdaptation",
"TemplateName": "LightAdaptationParentTemplate",
"Connections": [
{
"LocalSlot": "LightingInput",
"AttachmentRef": {
"Pass": "TransparentPass",
"Attachment": "InputOutput"
}
},
{
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
},
{
"Name": "AuxGeomPass",
"TemplateName": "AuxGeomPassTemplate",
"Enabled": true,
"Connections": [
{
"LocalSlot": "ColorInputOutput",
"AttachmentRef": {
"Pass": "LightAdaptation",
"Attachment": "Output"
}
},
{
"LocalSlot": "DepthInputOutput",
"AttachmentRef": {
"Pass": "DepthPrePass",
"Attachment": "Depth"
}
}
],
"PassData": {
"$type": "RasterPassData",
"DrawListTag": "auxgeom",
"PipelineViewTag": "MainCamera"
}
},
{
"Name": "UIPass",
"TemplateName": "UIParentTemplate",
"Connections": [
{
"LocalSlot": "InputOutput",
"AttachmentRef": {
"Pass": "AuxGeomPass",
"Attachment": "ColorInputOutput"
}
}
]
},
{
"Name": "CopyToSwapChain",
"TemplateName": "FullscreenCopyTemplate",
"Connections": [
{
"LocalSlot": "Input",
"AttachmentRef": {
"Pass": "UIPass",
"Attachment": "InputOutput"
}
},
{
"LocalSlot": "Output",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
}
]
}
}
}
@@ -305,7 +305,7 @@
},
{
"Name": "SkyBoxPass",
"TemplateName": "SkyBoxTemplate",
"TemplateName": "SkyBoxTwoOutputsTemplate",
"Enabled": true,
"Connections": [
{
@@ -92,6 +92,10 @@
"Name": "SkyBoxTemplate",
"Path": "Passes/SkyBox.pass"
},
{
"Name": "SkyBoxTwoOutputsTemplate",
"Path": "Passes/SkyBox_TwoOutputs.pass"
},
{
"Name": "UIPassTemplate",
"Path": "Passes/UI.pass"
@@ -483,6 +487,18 @@
{
"Name": "UIParentTemplate",
"Path": "Passes/UIParent.pass"
},
{
"Name": "LightAdaptationParentTemplate",
"Path": "Passes/LightAdaptationParent.pass"
},
{
"Name": "LowEndForwardPassTemplate",
"Path": "Passes/LowEndForward.pass"
},
{
"Name": "LowEndPipelineTemplate",
"Path": "Passes/LowEndPipeline.pass"
}
]
}
@@ -40,7 +40,7 @@
{
"LocalSlot": "Output",
"AttachmentRef": {
"Pass": "DisplayMapperPass",
"Pass": "LightAdaptation",
"Attachment": "Output"
}
},
@@ -54,8 +54,8 @@
{
"LocalSlot": "LuminanceMipChainOutput",
"AttachmentRef": {
"Pass": "DownsampleLuminanceMipChain",
"Attachment": "MipChainInputOutput"
"Pass": "LightAdaptation",
"Attachment": "LuminanceMipChainOutput"
}
}
],
@@ -115,94 +115,16 @@
}
]
},
// Everything before this point deals in raw lighting values
// ---------------------------------------------------------
// Everything after starts to map to values we see on screen
{
"Name": "DownsampleLuminanceMinAvgMax",
"TemplateName": "DownsampleLuminanceMinAvgMaxCS",
"Name": "LightAdaptation",
"TemplateName": "LightAdaptationParentTemplate",
"Connections": [
{
"LocalSlot": "Input",
"LocalSlot": "LightingInput",
"AttachmentRef": {
"Pass": "BloomPass",
"Attachment": "InputOutput"
}
}
]
},
{
"Name": "DownsampleLuminanceMipChain",
"TemplateName": "DownsampleMipChainTemplate",
"Connections": [
{
"LocalSlot": "MipChainInputOutput",
"AttachmentRef": {
"Pass": "DownsampleLuminanceMinAvgMax",
"Attachment": "Output"
}
}
],
"PassData": {
"$type": "DownsampleMipChainPassData",
"ShaderAsset": {
"FilePath": "Shaders/PostProcessing/DownsampleMinAvgMaxCS.shader"
}
}
},
{
"Name": "EyeAdaptationPass",
"TemplateName": "EyeAdaptationTemplate",
"Enabled": false,
"Connections": [
{
"LocalSlot": "SceneLuminanceInput",
"AttachmentRef": {
"Pass": "DownsampleLuminanceMipChain",
"Attachment": "MipChainInputOutput"
}
}
]
},
{
"Name": "LookModificationTransformPass",
"TemplateName": "LookModificationTransformTemplate",
"Enabled": true,
"Connections": [
{
"LocalSlot": "Input",
"AttachmentRef": {
"Pass": "BloomPass",
"Attachment": "InputOutput"
}
},
{
"LocalSlot": "EyeAdaptationDataInput",
"AttachmentRef": {
"Pass": "EyeAdaptationPass",
"Attachment": "EyeAdaptationDataInputOutput"
}
},
{
"LocalSlot": "SwapChainOutput",
"AttachmentRef": {
"Pass": "Parent",
"Attachment": "SwapChainOutput"
}
}
]
},
{
"Name": "DisplayMapperPass",
"TemplateName": "DisplayMapperTemplate",
"Enabled": true,
"Connections": [
{
"LocalSlot": "Input",
"AttachmentRef": {
"Pass": "LookModificationTransformPass",
"Attachment": "Output"
}
},
{
"LocalSlot": "SwapChainOutput",
@@ -12,11 +12,6 @@
"SlotType": "InputOutput",
"ScopeAttachmentUsage": "RenderTarget"
},
{
"Name": "ReflectionInputOutput",
"SlotType": "InputOutput",
"ScopeAttachmentUsage": "RenderTarget"
},
{
"Name": "SkyBoxDepth",
"SlotType": "InputOutput",
@@ -0,0 +1,43 @@
{
"Type": "JsonSerialization",
"Version": 1,
"ClassName": "PassAsset",
"ClassData": {
"PassTemplate": {
"Name": "SkyBoxTwoOutputsTemplate",
"PassClass": "FullScreenTriangle",
"Slots": [
{
"Name": "SpecularInputOutput",
"SlotType": "InputOutput",
"ScopeAttachmentUsage": "RenderTarget"
},
{
"Name": "ReflectionInputOutput",
"SlotType": "InputOutput",
"ScopeAttachmentUsage": "RenderTarget"
},
{
"Name": "SkyBoxDepth",
"SlotType": "InputOutput",
"ScopeAttachmentUsage": "DepthStencil"
}
],
"PassData": {
"$type": "FullscreenTrianglePassData",
"ShaderAsset": {
"FilePath": "shaders/skybox/skybox_twooutputs.shader"
},
"PipelineViewTag": "MainCamera",
"ShaderDataMappings": {
"FloatMappings": [
{
"Name": "m_sunIntensityMultiplier",
"Value": 1.0
}
]
}
}
}
}
}
@@ -26,7 +26,7 @@
//
//----------------------------------------------------------------------------------
#define Depth_to_Z(d, unprojectZ) (unprojectZ.x / (d + unprojectZ.y))
#define DepthBufferToViewSpace(d, unprojectZ) (unprojectZ.x / (d + unprojectZ.y))
#define NVLC_MAX_POSSIBLE_LIGHTS_PER_BIN 256
@@ -187,7 +187,7 @@ float4 RemapZToUnit(float4 z, float2 minmaxz)
uint DepthSamplesToBinMask2x(float2 d, float2 minmaxz, float2 unprojectZ)
{
float2 z = Depth_to_Z(d, unprojectZ);
float2 z = DepthBufferToViewSpace(d, unprojectZ);
// Tile_UnitValueToBit will convert that 0 to 1 value into 0.0 to 31.99999
float2 bit = Tile_UnitValueToBit(RemapZToUnit(z, minmaxz));
@@ -207,7 +207,7 @@ uint DepthSamplesToBinMask2x(float2 d, float2 minmaxz, float2 unprojectZ)
uint DepthSamplesToBinMask4x(float4 d, float2 minmaxz, float2 unprojectZ)
{
float4 z = Depth_to_Z(d, unprojectZ);
float4 z = DepthBufferToViewSpace(d, unprojectZ);
// Tile_UnitValueToBit will convert that 0 to 1 value into 0.0 to 31.99999
float4 bit = Tile_UnitValueToBit(RemapZToUnit(z, minmaxz));
@@ -10,6 +10,21 @@
*
*/
#ifdef UNIFIED_FORWARD_OUTPUT
struct ForwardPassOutput
{
float4 m_color : SV_Target0;
};
struct ForwardPassOutputWithDepth
{
float4 m_color : SV_Target0;
float m_depth : SV_Depth;
};
#else
struct ForwardPassOutput
{
float4 m_diffuseColor : SV_Target0; //!< RGB = Diffuse Lighting, A = Blend Alpha (for blended surfaces) OR A = special encoding of surfaceScatteringFactor, m_subsurfaceScatteringQuality, o_enableSubsurfaceScattering
@@ -30,3 +45,5 @@ struct ForwardPassOutputWithDepth
float4 m_normal : SV_Target4;
float m_depth : SV_Depth;
};
#endif
@@ -12,38 +12,39 @@
#pragma once
// --- Static Options Available ---
// FORCE_IBL_IN_FORWARD_PASS - forces IBL lighting to be run in the forward pass, used in pipelines that don't have a reflection pass
#include <Atom/Features/PBR/LightingOptions.azsli>
#include <Atom/RPI/Math.azsli>
#include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
#include <Atom/Features/PBR/LightingUtils.azsli>
void ApplyIblDiffuse(
float3 GetIblDiffuse(
float3 normal,
float3 albedo,
float3 diffuseResponse,
out float3 outDiffuse)
float3 diffuseResponse)
{
float3 irradianceDir = MultiplyVectorQuaternion(normal, SceneSrg::m_iblOrientation);
float3 diffuseSample = SceneSrg::m_diffuseEnvMap.Sample(SceneSrg::m_samplerEnv, GetCubemapCoords(irradianceDir)).rgb;
outDiffuse = diffuseResponse * albedo * diffuseSample;
return diffuseResponse * albedo * diffuseSample;
}
void ApplyIblSpecular(
float3 GetIblSpecular(
float3 position,
float3 normal,
float3 specularF0,
float roughnessLinear,
float3 dirToCamera,
float2 brdf,
out float3 outSpecular)
float2 brdf)
{
float3 reflectDir = reflect(-dirToCamera, normal);
reflectDir = MultiplyVectorQuaternion(reflectDir, SceneSrg::m_iblOrientation);
// global
outSpecular = SceneSrg::m_specularEnvMap.SampleLevel(SceneSrg::m_samplerEnv, GetCubemapCoords(reflectDir), GetRoughnessMip(roughnessLinear)).rgb;
float3 outSpecular = SceneSrg::m_specularEnvMap.SampleLevel(SceneSrg::m_samplerEnv, GetCubemapCoords(reflectDir), GetRoughnessMip(roughnessLinear)).rgb;
outSpecular *= (specularF0 * brdf.x + brdf.y);
// reflection probe
@@ -72,86 +73,55 @@ void ApplyIblSpecular(
outSpecular = lerp(outSpecular, probeSpecular, blendAmount);
}
return outSpecular;
}
void ApplyIBL(Surface surface, inout LightingData lightingData)
{
if (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent)
#ifdef FORCE_IBL_IN_FORWARD_PASS
bool useDiffuseIbl = true;
bool useSpecularIbl = true;
bool useIbl = o_enableIBL;
#else
bool isTransparent = (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent);
bool useDiffuseIbl = isTransparent;
bool useSpecularIbl = (isTransparent || o_meshUseForwardPassIBLSpecular || o_materialUseForwardPassIBLSpecular);
bool useIbl = o_enableIBL && (useDiffuseIbl || useSpecularIbl);
#endif
if(useIbl)
{
// transparencies currently require IBL in the forward pass
if (o_enableIBL)
float iblExposureFactor = pow(2.0, SceneSrg::m_iblExposure);
if(useDiffuseIbl)
{
float3 iblDiffuse = 0.0f;
ApplyIblDiffuse(
surface.normal,
surface.albedo,
lightingData.diffuseResponse,
iblDiffuse);
float3 iblSpecular = 0.0f;
ApplyIblSpecular(
surface.position,
surface.normal,
surface.specularF0,
surface.roughnessLinear,
lightingData.dirToCamera,
lightingData.brdf,
iblSpecular);
// Adjust IBL lighting by exposure.
float iblExposureFactor = pow(2.0, SceneSrg::m_iblExposure);
float3 iblDiffuse = GetIblDiffuse(surface.normal, surface.albedo, lightingData.diffuseResponse);
lightingData.diffuseLighting += (iblDiffuse * iblExposureFactor * lightingData.diffuseAmbientOcclusion);
lightingData.specularLighting += (iblSpecular * iblExposureFactor);
}
}
else if (o_meshUseForwardPassIBLSpecular || o_materialUseForwardPassIBLSpecular)
{
if (o_enableIBL)
{
float3 iblSpecular = 0.0f;
ApplyIblSpecular(
surface.position,
surface.normal,
surface.specularF0,
surface.roughnessLinear,
lightingData.dirToCamera,
lightingData.brdf,
iblSpecular);
if(useSpecularIbl)
{
float3 iblSpecular = GetIblSpecular(surface.position, surface.normal, surface.specularF0, surface.roughnessLinear, lightingData.dirToCamera, lightingData.brdf);
iblSpecular *= lightingData.multiScatterCompensation;
if (o_clearCoat_feature_enabled)
if (o_clearCoat_feature_enabled && surface.clearCoat.factor > 0.0f)
{
if (surface.clearCoat.factor > 0.0f)
{
float clearCoatNdotV = saturate(dot(surface.clearCoat.normal, lightingData.dirToCamera));
clearCoatNdotV = max(clearCoatNdotV, 0.01f); // [GFX TODO][ATOM-4466] This is a current band-aid for specular noise at grazing angles.
float2 clearCoatBrdf = PassSrg::m_brdfMap.Sample(PassSrg::LinearSampler, GetBRDFTexCoords(surface.clearCoat.roughness, clearCoatNdotV)).rg;
float clearCoatNdotV = saturate(dot(surface.clearCoat.normal, lightingData.dirToCamera));
clearCoatNdotV = max(clearCoatNdotV, 0.01f); // [GFX TODO][ATOM-4466] This is a current band-aid for specular noise at grazing angles.
float2 clearCoatBrdf = PassSrg::m_brdfMap.Sample(PassSrg::LinearSampler, GetBRDFTexCoords(surface.clearCoat.roughness, clearCoatNdotV)).rg;
// clear coat uses fixed IOR = 1.5 represents polyurethane which is the most common material for gloss clear coat
// coat layer assumed to be dielectric thus don't need multiple scattering compensation
float3 clearCoatSpecularF0 = float3(0.04f, 0.04f, 0.04f);
float3 clearCoatIblSpecular = 0.0f;
// clear coat uses fixed IOR = 1.5 represents polyurethane which is the most common material for gloss clear coat
// coat layer assumed to be dielectric thus don't need multiple scattering compensation
float3 clearCoatSpecularF0 = float3(0.04f, 0.04f, 0.04f);
float3 clearCoatIblSpecular = GetIblSpecular(surface.position, surface.clearCoat.normal, clearCoatSpecularF0, surface.clearCoat.roughness, lightingData.dirToCamera, clearCoatBrdf);
ApplyIblSpecular(
surface.position,
surface.clearCoat.normal,
clearCoatSpecularF0,
surface.clearCoat.roughness,
lightingData.dirToCamera,
clearCoatBrdf,
clearCoatIblSpecular);
clearCoatIblSpecular *= surface.clearCoat.factor;
clearCoatIblSpecular *= surface.clearCoat.factor;
// attenuate base layer energy
float3 clearCoatResponse = FresnelSchlickWithRoughness(clearCoatNdotV, clearCoatSpecularF0, surface.clearCoat.roughness) * surface.clearCoat.factor;
iblSpecular = iblSpecular * (1.0 - clearCoatResponse) * (1.0 - clearCoatResponse) + clearCoatIblSpecular;
}
// attenuate base layer energy
float3 clearCoatResponse = FresnelSchlickWithRoughness(clearCoatNdotV, clearCoatSpecularF0, surface.clearCoat.roughness) * surface.clearCoat.factor;
iblSpecular = iblSpecular * (1.0 - clearCoatResponse) * (1.0 - clearCoatResponse) + clearCoatIblSpecular;
}
float iblExposureFactor = pow(2.0f, SceneSrg::m_iblExposure);
lightingData.specularLighting += (iblSpecular * iblExposureFactor);
}
}
@@ -0,0 +1,26 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
// This file translates quality option macros like QUALITY_LOW_END to their relevant settings
#ifdef QUALITY_LOW_END
// Unifies the forward output into a single lighting buffer instead of splitting it into a GBuffer
#define UNIFIED_FORWARD_OUTPUT 1
// Forces IBL lighting to be executed in the forward pass instead of subsequent refleciton passes
#define FORCE_IBL_IN_FORWARD_PASS 1
#endif
@@ -150,7 +150,7 @@ uint ComputeTransparentBitMask(float2 minmaxZ)
return 0;
}
float2 minmaxZ_transparent = Depth_to_Z(minmaxDepth_transparent, PassSrg::m_constantData.m_unprojectZ);
float2 minmaxZ_transparent = DepthBufferToViewSpace(minmaxDepth_transparent, PassSrg::m_constantData.m_unprojectZ);
float2 minmaxUnit_transparent = RemapZToUnit(minmaxZ_transparent, minmaxZ);
@@ -295,7 +295,7 @@ void MainCS(
float2 minmaxDepth_opaque = ComputeDepthMinMaxFrom2Samples(opaqueDepthSamples);
minmaxDepth_both = ExpandMinMax(minmaxDepth_opaque, minmaxDepth_transparent);
UpdateMinMaxFromAllThreads(minmaxDepth_both, minmaxDepth_transparent, isPixelOnScreen);
minmaxDepth_both = Depth_to_Z(minmaxDepth_both, PassSrg::m_constantData.m_unprojectZ);
minmaxDepth_both = DepthBufferToViewSpace(minmaxDepth_both, PassSrg::m_constantData.m_unprojectZ);
// if zNear == zFar we want to map z == zNear to 0-bit, so we have to keep zNear without modifications
minmaxDepth_both.y = IncrementULP(minmaxDepth_both.y);
@@ -313,7 +313,7 @@ void MainCS(
float2 minmaxDepth_opaque = ComputeDepthMinMaxFrom4Samples(opaqueDepthSamples);
minmaxDepth_both = ExpandMinMax(minmaxDepth_opaque, minmaxDepth_transparent);
UpdateMinMaxFromAllThreads(minmaxDepth_both, minmaxDepth_transparent, isPixelOnScreen);
minmaxDepth_both = Depth_to_Z(minmaxDepth_both, PassSrg::m_constantData.m_unprojectZ);
minmaxDepth_both = DepthBufferToViewSpace(minmaxDepth_both, PassSrg::m_constantData.m_unprojectZ);
// if zNear == zFar we want to map z == zNear to 0-bit, so we have to keep zNear without modifications
minmaxDepth_both.y = IncrementULP(minmaxDepth_both.y);
@@ -10,6 +10,9 @@
*
*/
// --- Static Options Available ---
// SKYBOX_TWO_OUTPUTS - Skybox renders to two rendertargets instead of one (SkyBox_TwoOutputs.pass writes to specular and reflection targets)
#include <Atom/Features/ColorManagement/TransformColor.azsli>
#include <Atom/Features/PostProcessing/FullscreenVertexUtil.azsli>
#include <Atom/Features/MatrixUtility.azsli>
@@ -102,7 +105,9 @@ float3 GetCubemapCoords(float3 original)
struct PSOutput
{
float4 m_specular : SV_Target0;
#ifdef SKYBOX_TWO_OUTPUTS
float4 m_reflection : SV_Target1;
#endif
};
PSOutput MainPS(VSOutput input)
@@ -163,6 +168,8 @@ PSOutput MainPS(VSOutput input)
PSOutput OUT;
OUT.m_specular = float4(color, 1.0);
#ifdef SKYBOX_TWO_OUTPUTS
OUT.m_reflection = float4(color, 1.0);
#endif
return OUT;
}
@@ -0,0 +1,17 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// NOTE: This file is a temporary workaround until .shader files can #define macros for their .azsl files
#define SKYBOX_TWO_OUTPUTS
#include "SkyBox.azsl"
@@ -0,0 +1,22 @@
{
"Source" : "SkyBox_TwoOutputs",
"DepthStencilState" : {
"Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" }
},
"ProgramSettings":
{
"EntryPoints":
[
{
"name": "MainVS",
"type": "Vertex"
},
{
"name": "MainPS",
"type": "Fragment"
}
]
}
}
@@ -38,6 +38,7 @@ set(FILES
Materials/Types/StandardMultilayerPBR_ForwardPass_EDS.shader
Materials/Types/StandardMultilayerPBR_Parallax.lua
Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua
Materials/Types/StandardMultilayerPBR_ShaderEnable.lua
Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl
Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.shader
Materials/Types/StandardPBR.materialtype
@@ -52,6 +53,9 @@ set(FILES
Materials/Types/StandardPBR_ForwardPass_EDS.shader
Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua
Materials/Types/StandardPBR_HandleOpacityMode.lua
Materials/Types/StandardPBR_LowEndForward.azsl
Materials/Types/StandardPBR_LowEndForward.shader
Materials/Types/StandardPBR_LowEndForward_EDS.shader
Materials/Types/StandardPBR_ParallaxState.lua
Materials/Types/StandardPBR_Roughness.lua
Materials/Types/StandardPBR_ShaderEnable.lua
@@ -116,6 +120,7 @@ set(FILES
Passes/DiffuseProbeGridBlendDistance.pass
Passes/DiffuseProbeGridBlendIrradiance.pass
Passes/DiffuseProbeGridBorderUpdate.pass
Passes/DiffuseProbeGridClassification.pass
Passes/DiffuseProbeGridDownsample.pass
Passes/DiffuseProbeGridRayTracing.pass
Passes/DiffuseProbeGridRelocation.pass
@@ -144,6 +149,7 @@ set(FILES
Passes/FullscreenCopy.pass
Passes/FullscreenOutputOnly.pass
Passes/ImGui.pass
Passes/LightAdaptationParent.pass
Passes/LightCulling.pass
Passes/LightCullingHeatmap.pass
Passes/LightCullingParent.pass
@@ -152,6 +158,8 @@ set(FILES
Passes/LightCullingTilePrepareMSAA.pass
Passes/LookModificationComposite.pass
Passes/LookModificationTransform.pass
Passes/LowEndForward.pass
Passes/LowEndPipeline.pass
Passes/LuminanceHeatmap.pass
Passes/LuminanceHistogramGenerator.pass
Passes/MainPipeline.pass
@@ -179,13 +187,16 @@ set(FILES
Passes/ReflectionScreenSpace.pass
Passes/ReflectionScreenSpaceBlur.pass
Passes/ReflectionScreenSpaceBlurHorizontal.pass
Passes/ReflectionScreenSpaceBlurMobile.pass
Passes/ReflectionScreenSpaceBlurVertical.pass
Passes/ReflectionScreenSpaceComposite.pass
Passes/ReflectionScreenSpaceMobile.pass
Passes/ReflectionScreenSpaceTrace.pass
Passes/Reflections_nomsaa.pass
Passes/ShadowParent.pass
Passes/Skinning.pass
Passes/SkyBox.pass
Passes/SkyBox_TwoOutputs.pass
Passes/SMAA1xApplyLinearHDRColor.pass
Passes/SMAA1xApplyPerceptualColor.pass
Passes/SMAABlendingWeightCalculation.pass
@@ -205,6 +216,7 @@ set(FILES
ShaderLib/Atom/Features/IndirectRendering.azsli
ShaderLib/Atom/Features/MatrixUtility.azsli
ShaderLib/Atom/Features/ParallaxMapping.azsli
ShaderLib/Atom/Features/ShaderQualityOptions.azsli
ShaderLib/Atom/Features/SphericalHarmonicsUtility.azsli
ShaderLib/Atom/Features/SrgSemantics.azsli
ShaderLib/Atom/Features/ColorManagement/TransformColor.azsli
@@ -272,6 +284,7 @@ set(FILES
ShaderLib/Atom/Features/PostProcessing/GlyphData.azsli
ShaderLib/Atom/Features/PostProcessing/GlyphRender.azsli
ShaderLib/Atom/Features/PostProcessing/PostProcessUtil.azsli
ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli
ShaderLib/Atom/Features/ScreenSpace/ScreenSpaceUtil.azsli
ShaderLib/Atom/Features/Shadow/BicubicPcfFilters.azsli
ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli
@@ -471,4 +484,6 @@ set(FILES
Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli
Shaders/SkyBox/SkyBox.azsl
Shaders/SkyBox/SkyBox.shader
Shaders/SkyBox/SkyBox_TwoOutputs.azsl
Shaders/SkyBox/SkyBox_TwoOutputs.shader
)
@@ -53,6 +53,7 @@ namespace AZ
void SetBufferViewsOnShaderResourceGroup(const Data::Instance<RPI::ShaderResourceGroup>& perInstanceSRG);
private:
RHI::Ptr<RHI::BufferView> m_vertexDeltaBufferView;
Data::Instance<RPI::Buffer> m_vertexDeltaBuffer;
};
struct MorphTargetMetaData
@@ -12,6 +12,7 @@
#include <MorphTargets/MorphTargetComputePass.h>
#include <SkinnedMesh/SkinnedMeshFeatureProcessor.h>
#include <Atom/Feature/SkinnedMesh/SkinnedMeshOutputStreamManagerInterface.h>
#include <Atom/RPI.Public/Shader/Shader.h>
@@ -38,6 +39,11 @@ namespace AZ
return m_shader;
}
void MorphTargetComputePass::SetFeatureProcessor(SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor)
{
m_skinnedMeshFeatureProcessor = skinnedMeshFeatureProcessor;
}
void MorphTargetComputePass::BuildAttachmentsInternal()
{
// The same buffer that skinning writes to is used to manage the computed vertex deltas that are passed from the
@@ -45,30 +51,16 @@ namespace AZ
AttachBufferToSlot(Name{ "MorphTargetDeltaOutput" }, SkinnedMeshOutputStreamManagerInterface::Get()->GetBuffer());
}
void MorphTargetComputePass::AddDispatchItem(const RHI::DispatchItem* dispatchItem)
{
AZ_Assert(dispatchItem != nullptr, "invalid dispatchItem");
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
//using an unordered_set here to prevent redundantly adding the same dispatchItem to the submission queue
//(i.e. if the same morph target exists in multiple views, it can call AddDispatchItem multiple times with the same item)
m_dispatches.insert(dispatchItem);
}
void MorphTargetComputePass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context)
{
RHI::CommandList* commandList = context.GetCommandList();
SetSrgsForDispatch(commandList);
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
for (const RHI::DispatchItem* dispatchItem : m_dispatches)
if (m_skinnedMeshFeatureProcessor)
{
commandList->Submit(*dispatchItem);
}
RHI::CommandList* commandList = context.GetCommandList();
// Clear the dispatch items. They will need to be re-populated next frame
m_dispatches.clear();
SetSrgsForDispatch(commandList);
m_skinnedMeshFeatureProcessor->SubmitMorphTargetDispatchItems(commandList);
}
}
} // namespace Render
} // namespace AZ
@@ -18,6 +18,8 @@ namespace AZ
{
namespace Render
{
class SkinnedMeshFeatureProcessor;
//! The morph target compute pass submits dispatch items for morph targets. The dispatch items are cleared every frame, so it needs to be re-populated.
class MorphTargetComputePass
: public RPI::ComputePass
@@ -31,16 +33,14 @@ namespace AZ
static RPI::Ptr<MorphTargetComputePass> Create(const RPI::PassDescriptor& descriptor);
//! Thread-safe function for adding a dispatch item to the current frame.
void AddDispatchItem(const RHI::DispatchItem* dispatchItem);
Data::Instance<RPI::Shader> GetShader() const;
void SetFeatureProcessor(SkinnedMeshFeatureProcessor* m_skinnedMeshFeatureProcessor);
private:
void BuildAttachmentsInternal() override;
void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override;
AZStd::mutex m_mutex;
AZStd::unordered_set<const RHI::DispatchItem*> m_dispatches;
SkinnedMeshFeatureProcessor* m_skinnedMeshFeatureProcessor = nullptr;
};
}
}
@@ -11,7 +11,7 @@
*/
#include <MorphTargets/MorphTargetDispatchItem.h>
#include <MorphTargets/MorphTargetComputePass.h>
#include <SkinnedMesh/SkinnedMeshFeatureProcessor.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
#include <Atom/RPI.Public/Shader/Shader.h>
@@ -30,7 +30,7 @@ namespace AZ
MorphTargetDispatchItem::MorphTargetDispatchItem(
const AZStd::intrusive_ptr<MorphTargetInputBuffers> inputBuffers,
const MorphTargetMetaData& morphTargetMetaData,
RPI::Ptr<MorphTargetComputePass> morphTargetComputePass,
SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor,
MorphTargetInstanceMetaData morphInstanceMetaData,
float morphDeltaIntegerEncoding)
: m_inputBuffers(inputBuffers)
@@ -38,7 +38,7 @@ namespace AZ
, m_morphInstanceMetaData(morphInstanceMetaData)
, m_accumulatedDeltaIntegerEncoding(morphDeltaIntegerEncoding)
{
m_morphTargetShader = morphTargetComputePass->GetShader();
m_morphTargetShader = skinnedMeshFeatureProcessor->GetMorphTargetShader();
RPI::ShaderReloadNotificationBus::Handler::BusConnect(m_morphTargetShader->GetAssetId());
}
@@ -37,7 +37,7 @@ namespace AZ
namespace Render
{
class MorphTargetComputePass;
class SkinnedMeshFeatureProcessor;
//! Holds and manages an RHI DispatchItem for a specific morph target, and the resources that are needed to build and maintain it.
class MorphTargetDispatchItem
@@ -51,7 +51,7 @@ namespace AZ
explicit MorphTargetDispatchItem(
const AZStd::intrusive_ptr<MorphTargetInputBuffers> inputBuffers,
const MorphTargetMetaData& morphTargetMetaData,
RPI::Ptr<MorphTargetComputePass> morphTargetComputePass,
SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor,
MorphTargetInstanceMetaData morphInstanceMetaData,
float accumulatedDeltaRange
);
@@ -30,16 +30,18 @@ namespace AZ
{
MorphTargetInputBuffers::MorphTargetInputBuffers(const RPI::BufferAssetView& bufferAssetView, const AZStd::string& bufferNamePrefix)
{
auto buffer = RPI::Buffer::FindOrCreate(bufferAssetView.GetBufferAsset());
AZ::RHI::Ptr<AZ::RHI::BufferView> bufferView = RHI::Factory::Get().CreateBufferView();
m_vertexDeltaBuffer = RPI::Buffer::FindOrCreate(bufferAssetView.GetBufferAsset());
if (m_vertexDeltaBuffer)
{
bufferView->SetName(Name(bufferNamePrefix + "MorphTargetVertexDeltaView"));
[[maybe_unused]] RHI::ResultCode resultCode = bufferView->Init(*buffer->GetRHIBuffer(), bufferAssetView.GetBufferViewDescriptor());
AZ_Error("MorphTargetInputBuffers", resultCode == RHI::ResultCode::Success, "Failed to initialize buffer view for morph target.");
}
AZ::RHI::Ptr<AZ::RHI::BufferView> bufferView = RHI::Factory::Get().CreateBufferView();
{
bufferView->SetName(Name(bufferNamePrefix + "MorphTargetVertexDeltaView"));
[[maybe_unused]] RHI::ResultCode resultCode = bufferView->Init(*m_vertexDeltaBuffer->GetRHIBuffer(), bufferAssetView.GetBufferViewDescriptor());
AZ_Error("MorphTargetInputBuffers", resultCode == RHI::ResultCode::Success, "Failed to initialize buffer view for morph target.");
}
m_vertexDeltaBufferView = bufferView;
m_vertexDeltaBufferView = bufferView;
}
}
void MorphTargetInputBuffers::SetBufferViewsOnShaderResourceGroup(const Data::Instance<RPI::ShaderResourceGroup>& perInstanceSRG)
@@ -12,6 +12,7 @@
#include <SkinnedMesh/SkinnedMeshComputePass.h>
#include <SkinnedMesh/SkinnedMeshFeatureProcessor.h>
#include <Atom/Feature/SkinnedMesh/SkinnedMeshOutputStreamManagerInterface.h>
#include <Atom/RPI.Public/Shader/Shader.h>
@@ -22,11 +23,9 @@ namespace AZ
{
namespace Render
{
SkinnedMeshComputePass::SkinnedMeshComputePass(const RPI::PassDescriptor& descriptor)
: RPI::ComputePass(descriptor)
{
m_cachedShaderOptions.SetShader(m_shader);
}
RPI::Ptr<SkinnedMeshComputePass> SkinnedMeshComputePass::Create(const RPI::PassDescriptor& descriptor)
@@ -40,42 +39,30 @@ namespace AZ
return m_shader;
}
RPI::ShaderOptionGroup SkinnedMeshComputePass::CreateShaderOptionGroup(const SkinnedMeshShaderOptions shaderOptions, SkinnedMeshShaderOptionNotificationBus::Handler& shaderReinitializedHandler)
void SkinnedMeshComputePass::SetFeatureProcessor(SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor)
{
m_cachedShaderOptions.ConnectToShaderReinitializedEvent(shaderReinitializedHandler);
return m_cachedShaderOptions.CreateShaderOptionGroup(shaderOptions);
}
void SkinnedMeshComputePass::AddDispatchItem(const RHI::DispatchItem* dispatchItem)
{
AZ_Assert(dispatchItem != nullptr, "invalid dispatchItem");
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
//using an unordered_set here to prevent redundantly adding the same dispatchItem to the submission queue
//(i.e. if the same skinnedMesh exists in multiple views, it can call AddDispatchItem multiple times with the same item)
m_dispatches.insert(dispatchItem);
m_skinnedMeshFeatureProcessor = skinnedMeshFeatureProcessor;
}
void SkinnedMeshComputePass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context)
{
RHI::CommandList* commandList = context.GetCommandList();
SetSrgsForDispatch(commandList);
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
for (const RHI::DispatchItem* dispatchItem : m_dispatches)
if (m_skinnedMeshFeatureProcessor)
{
commandList->Submit(*dispatchItem);
}
RHI::CommandList* commandList = context.GetCommandList();
// Clear the dispatch items. They will need to be re-populated next frame
m_dispatches.clear();
SetSrgsForDispatch(commandList);
m_skinnedMeshFeatureProcessor->SubmitSkinningDispatchItems(commandList);
}
}
void SkinnedMeshComputePass::OnShaderReinitialized(const RPI::Shader& shader)
{
ComputePass::OnShaderReinitialized(shader);
m_cachedShaderOptions.SetShader(m_shader);
if (m_skinnedMeshFeatureProcessor)
{
m_skinnedMeshFeatureProcessor->OnSkinningShaderReinitialized(m_shader);
}
}
void SkinnedMeshComputePass::OnShaderVariantReinitialized(const RPI::Shader& shader, const RPI::ShaderVariantId&, RPI::ShaderVariantStableId)
@@ -20,6 +20,8 @@ namespace AZ
{
namespace Render
{
class SkinnedMeshFeatureProcessor;
//! The skinned mesh compute pass submits dispatch items for skinning. The dispatch items are cleared every frame, so it needs to be re-populated.
class SkinnedMeshComputePass
: public RPI::ComputePass
@@ -33,10 +35,9 @@ namespace AZ
static RPI::Ptr<SkinnedMeshComputePass> Create(const RPI::PassDescriptor& descriptor);
//! Thread-safe function for adding a dispatch item to the current frame.
void AddDispatchItem(const RHI::DispatchItem* dispatchItem);
Data::Instance<RPI::Shader> GetShader() const;
RPI::ShaderOptionGroup CreateShaderOptionGroup(const SkinnedMeshShaderOptions shaderOptions, SkinnedMeshShaderOptionNotificationBus::Handler& shaderReinitializedHandler);
void SetFeatureProcessor(SkinnedMeshFeatureProcessor* m_skinnedMeshFeatureProcessor);
private:
void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override;
@@ -45,9 +46,7 @@ namespace AZ
void OnShaderReinitialized(const RPI::Shader& shader) override;
void OnShaderVariantReinitialized(const RPI::Shader& shader, const RPI::ShaderVariantId& shaderVariantId, RPI::ShaderVariantStableId shaderVariantStableId) override;
AZStd::mutex m_mutex;
AZStd::unordered_set<const RHI::DispatchItem*> m_dispatches;
CachedSkinnedMeshShaderOptions m_cachedShaderOptions;
SkinnedMeshFeatureProcessor* m_skinnedMeshFeatureProcessor = nullptr;
};
}
}
@@ -12,7 +12,7 @@
#include <SkinnedMesh/SkinnedMeshDispatchItem.h>
#include <SkinnedMesh/SkinnedMeshOutputStreamManager.h>
#include <SkinnedMesh/SkinnedMeshComputePass.h>
#include <SkinnedMesh/SkinnedMeshFeatureProcessor.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
#include <Atom/RPI.Public/Shader/Shader.h>
@@ -34,7 +34,7 @@ namespace AZ
size_t lodIndex,
Data::Instance<RPI::Buffer> boneTransforms,
const SkinnedMeshShaderOptions& shaderOptions,
RPI::Ptr<SkinnedMeshComputePass> skinnedMeshComputePass,
SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor,
MorphTargetInstanceMetaData morphTargetInstanceMetaData,
float morphTargetDeltaIntegerEncoding)
: m_inputBuffers(inputBuffers)
@@ -45,7 +45,7 @@ namespace AZ
, m_morphTargetInstanceMetaData(morphTargetInstanceMetaData)
, m_morphTargetDeltaIntegerEncoding(morphTargetDeltaIntegerEncoding)
{
m_skinningShader = skinnedMeshComputePass->GetShader();
m_skinningShader = skinnedMeshFeatureProcessor->GetSkinningShader();
// Shader options are generally set per-skinned mesh instance, but morph targets may only exist on some lods. Override the option for applying morph targets here
if (m_morphTargetInstanceMetaData.m_accumulatedPositionDeltaOffsetInBytes != MorphTargetConstants::s_invalidDeltaOffset)
@@ -58,7 +58,7 @@ namespace AZ
}
// CreateShaderOptionGroup will also connect to the SkinnedMeshShaderOptionNotificationBus
m_shaderOptionGroup = skinnedMeshComputePass->CreateShaderOptionGroup(m_shaderOptions, *this);
m_shaderOptionGroup = skinnedMeshFeatureProcessor->CreateSkinningShaderOptionGroup(m_shaderOptions, *this);
}
SkinnedMeshDispatchItem::~SkinnedMeshDispatchItem()
@@ -38,7 +38,7 @@ namespace AZ
namespace Render
{
class SkinnedMeshComputePass;
class SkinnedMeshFeatureProcessor;
//! Holds and manages an RHI DispatchItem for a specific skinned mesh, and the resources that are needed to build and maintain it.
class SkinnedMeshDispatchItem
@@ -55,7 +55,7 @@ namespace AZ
size_t lodIndex,
Data::Instance<RPI::Buffer> skinningMatrices,
const SkinnedMeshShaderOptions& shaderOptions,
RPI::Ptr<SkinnedMeshComputePass> skinnedMeshComputePass,
SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor,
MorphTargetInstanceMetaData morphTargetInstanceMetaData,
float morphTargetDeltaIntegerEncoding
);
@@ -24,8 +24,10 @@
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Shader/Shader.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RHI/CpuProfiler.h>
#include <Atom/RHI/CommandList.h>
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/Jobs/JobCompletion.h>
@@ -84,11 +86,6 @@ namespace AZ
AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender);
AZ_ATOM_PROFILE_FUNCTION("SkinnedMesh", "SkinnedMeshFeatureProcessor: Render");
if (!m_skinningPass)
{
return;
}
#if 0 //[GFX_TODO][ATOM-13564] Temporarily disable skinning culling until we figure out how to hook up visibility & lod selection with skinning:
//Setup the culling workgroup (it will be re-used for each view)
{
@@ -132,7 +129,7 @@ namespace AZ
//Dispatch the workgroup to each view
for (const RPI::ViewPtr& viewPtr : packet.m_views)
{
Job *processWorkgroupJob = AZ::CreateJobFunction(
Job* processWorkgroupJob = AZ::CreateJobFunction(
[this, cullingSystem, viewPtr](AZ::Job& thisJob)
{
AZ_PROFILE_SCOPE_DYNAMIC(Debug::ProfileCategory::AzRender, "skinningMeshFP processWorkgroupJob - View: %s", viewPtr->GetName().GetCStr());
@@ -167,7 +164,16 @@ namespace AZ
float maxScreenPercentage(lod.m_range.m_max);
if (approxScreenPercentage >= minScreenPercentage && approxScreenPercentage <= maxScreenPercentage)
{
m_skinningPass->AddDispatchItem(&renderProxy->m_dispatchItemsByLod[lodIndex]->GetRHIDispatchItem());
AZStd::lock_guard lock(m_dispatchItemMutex);
m_skinningDispatches.insert(&renderProxy->m_dispatchItemsByLod[lodIndex]->GetRHIDispatchItem());
for (size_t morphTargetIndex = 0; morphTargetIndex < renderProxy->m_morphTargetDispatchItemsByLod[lodIndex].size(); morphTargetIndex++)
{
const MorphTargetDispatchItem* dispatchItem = renderProxy->m_morphTargetDispatchItemsByLod[lodIndex][morphTargetIndex].get();
if (dispatchItem && dispatchItem->GetWeight() > AZ::Constants::FloatEpsilon)
{
m_morphTargetDispatches.insert(&dispatchItem->GetRHIDispatchItem());
}
}
}
}
}
@@ -232,13 +238,14 @@ namespace AZ
//Note that this supports overlapping lod ranges (to support cross-fading lods, for example)
if (approxScreenPercentage >= lod.m_screenCoverageMin && approxScreenPercentage <= lod.m_screenCoverageMax)
{
m_skinningPass->AddDispatchItem(&renderProxy.m_dispatchItemsByLod[lodIndex]->GetRHIDispatchItem());
AZStd::lock_guard lock(m_dispatchItemMutex);
m_skinningDispatches.insert(&renderProxy.m_dispatchItemsByLod[lodIndex]->GetRHIDispatchItem());
for (size_t morphTargetIndex = 0; morphTargetIndex < renderProxy.m_morphTargetDispatchItemsByLod[lodIndex].size(); morphTargetIndex++)
{
const MorphTargetDispatchItem* dispatchItem = renderProxy.m_morphTargetDispatchItemsByLod[lodIndex][morphTargetIndex].get();
if (dispatchItem && dispatchItem->GetWeight() > AZ::Constants::FloatEpsilon)
{
m_morphTargetPass->AddDispatchItem(&dispatchItem->GetRHIDispatchItem());
m_morphTargetDispatches.insert(&dispatchItem->GetRHIDispatchItem());
}
}
}
@@ -248,19 +255,14 @@ namespace AZ
#endif
}
void SkinnedMeshFeatureProcessor::OnRenderPipelineAdded([[maybe_unused]] RPI::RenderPipelinePtr pipeline)
void SkinnedMeshFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline)
{
InitSkinningAndMorphPass();
InitSkinningAndMorphPass(pipeline->GetRootPass());
}
void SkinnedMeshFeatureProcessor::OnRenderPipelineRemoved([[maybe_unused]] RPI::RenderPipeline* pipeline)
void SkinnedMeshFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline)
{
InitSkinningAndMorphPass();
}
void SkinnedMeshFeatureProcessor::OnRenderPipelinePassesChanged([[maybe_unused]] RPI::RenderPipeline* renderPipeline)
{
InitSkinningAndMorphPass();
InitSkinningAndMorphPass(renderPipeline->GetRootPass());
}
void SkinnedMeshFeatureProcessor::OnBeginPrepareRender()
@@ -268,9 +270,15 @@ namespace AZ
m_renderProxiesChecker.soft_lock();
}
void SkinnedMeshFeatureProcessor::OnEndPrepareRender()
void SkinnedMeshFeatureProcessor::OnRenderEnd()
{
m_renderProxiesChecker.soft_unlock();
// Clear any dispatch items that were added but never submitted
// in case there were no passes that submitted this frame
// because they execute at a lower frequency
m_skinningDispatches.clear();
m_morphTargetDispatches.clear();
}
SkinnedMeshRenderProxyHandle SkinnedMeshFeatureProcessor::AcquireRenderProxy(const SkinnedMeshRenderProxyDesc& desc)
@@ -295,61 +303,73 @@ namespace AZ
return false;
}
void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass()
void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass(const RPI::Ptr<RPI::ParentPass> pipelineRootPass)
{
m_skinningPass = nullptr; //reset it to null, just in case it fails to load the assets properly
m_morphTargetPass = nullptr;
RPI::PassSystemInterface* passSystem = RPI::PassSystemInterface::Get();
if (passSystem->HasPassesForTemplateName(AZ::Name{ "SkinningPassTemplate" }))
RPI::Ptr<RPI::Pass> skinningPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "SkinningPass" });
if (skinningPass)
{
auto& skinningPasses = passSystem->GetPassesForTemplateName(AZ::Name{ "SkinningPassTemplate" });
SkinnedMeshComputePass* skinnedMeshComputePass = azdynamic_cast<SkinnedMeshComputePass*>(skinningPass.get());
skinnedMeshComputePass->SetFeatureProcessor(this);
// For now, assume one skinning pass
if (!skinningPasses.empty() && skinningPasses[0])
// There may be multiple skinning passes in the scene due to multiple pipelines, but there is only one skinning shader
m_skinningShader = skinnedMeshComputePass->GetShader();
if (!m_skinningShader)
{
m_skinningPass = static_cast<SkinnedMeshComputePass*>(skinningPasses[0]);
const Data::Instance<RPI::Shader> shader = m_skinningPass->GetShader();
if (!shader)
{
AZ_Error(s_featureProcessorName, false, "Failed to get skinning pass shader. It may need to finish processing.");
}
AZ_Error(s_featureProcessorName, false, "Failed to get skinning pass shader. It may need to finish processing.");
}
else
{
AZ_Error(s_featureProcessorName, false, "\"SkinningPassTemplate\" does not have any valid passes. Check your game project's .pass assets.");
m_cachedSkinningShaderOptions.SetShader(m_skinningShader);
}
}
else
{
AZ_Error(s_featureProcessorName, false, "Failed to find passes for \"SkinningPassTemplate\". Check your game project's .pass assets.");
}
if (passSystem->HasPassesForTemplateName(AZ::Name{ "MorphTargetPassTemplate" }))
RPI::Ptr<RPI::Pass> morphTargetPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "MorphTargetPass" });
if (morphTargetPass)
{
auto& morphTargetPasses = passSystem->GetPassesForTemplateName(AZ::Name{ "MorphTargetPassTemplate" });
MorphTargetComputePass* morphTargetComputePass = azdynamic_cast<MorphTargetComputePass*>(morphTargetPass.get());
morphTargetComputePass->SetFeatureProcessor(this);
// For now, assume one skinning pass
if (!morphTargetPasses.empty() && morphTargetPasses[0])
// There may be multiple morph target passes in the scene due to multiple pipelines, but there is only one morph target shader
m_morphTargetShader = morphTargetComputePass->GetShader();
if (!m_morphTargetShader)
{
m_morphTargetPass = static_cast<MorphTargetComputePass*>(morphTargetPasses[0]);
const Data::Instance<RPI::Shader> shader = m_morphTargetPass->GetShader();
AZ_Error(s_featureProcessorName, false, "Failed to get morph target pass shader. It may need to finish processing.");
}
}
}
if (!shader)
{
AZ_Error(s_featureProcessorName, false, "Failed to get morph target pass shader. It may need to finish processing.");
}
}
else
{
AZ_Error(s_featureProcessorName, false, "\"MorphTargetPassTemplate\" does not have any valid passes. Check your game project's .pass assets.");
}
}
else
RPI::ShaderOptionGroup SkinnedMeshFeatureProcessor::CreateSkinningShaderOptionGroup(const SkinnedMeshShaderOptions shaderOptions, SkinnedMeshShaderOptionNotificationBus::Handler& shaderReinitializedHandler)
{
m_cachedSkinningShaderOptions.ConnectToShaderReinitializedEvent(shaderReinitializedHandler);
return m_cachedSkinningShaderOptions.CreateShaderOptionGroup(shaderOptions);
}
void SkinnedMeshFeatureProcessor::OnSkinningShaderReinitialized(const Data::Instance<RPI::Shader> skinningShader)
{
m_skinningShader = skinningShader;
m_cachedSkinningShaderOptions.SetShader(m_skinningShader);
}
void SkinnedMeshFeatureProcessor::SubmitSkinningDispatchItems(RHI::CommandList* commandList)
{
AZStd::lock_guard lock(m_dispatchItemMutex);
for (const RHI::DispatchItem* dispatchItem : m_skinningDispatches)
{
AZ_Error(s_featureProcessorName, false, "Failed to find passes for \"MorphTargetPassTemplate\". Check your game project's .pass assets.");
commandList->Submit(*dispatchItem);
}
m_skinningDispatches.clear();
}
void SkinnedMeshFeatureProcessor::SubmitMorphTargetDispatchItems(RHI::CommandList* commandList)
{
AZStd::lock_guard lock(m_dispatchItemMutex);
for (const RHI::DispatchItem* dispatchItem : m_morphTargetDispatches)
{
commandList->Submit(*dispatchItem);
}
m_morphTargetDispatches.clear();
}
SkinnedMeshRenderProxyInterfaceHandle SkinnedMeshFeatureProcessor::AcquireRenderProxyInterface(const SkinnedMeshRenderProxyDesc& desc)
@@ -363,14 +383,14 @@ namespace AZ
return ReleaseRenderProxy(handle);
}
RPI::Ptr<SkinnedMeshComputePass> SkinnedMeshFeatureProcessor::GetSkinningPass() const
Data::Instance<RPI::Shader> SkinnedMeshFeatureProcessor::GetSkinningShader() const
{
return m_skinningPass;
return m_skinningShader;
}
RPI::Ptr<MorphTargetComputePass> SkinnedMeshFeatureProcessor::GetMorphTargetPass() const
Data::Instance<RPI::Shader> SkinnedMeshFeatureProcessor::GetMorphTargetShader() const
{
return m_morphTargetPass;
return m_morphTargetShader;
}
} // namespace Render
} // namespace AZ
@@ -51,35 +51,46 @@ namespace AZ
void Deactivate() override;
void Simulate(const FeatureProcessor::SimulatePacket& packet) override;
void Render(const FeatureProcessor::RenderPacket& packet) override;
void OnRenderEnd() override;
// RPI::SceneNotificationBus overrides ...
void OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) override;
void OnRenderPipelineRemoved(RPI::RenderPipeline* pipeline) override;
void OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) override;
void OnBeginPrepareRender() override;
void OnEndPrepareRender() override;
SkinnedMeshRenderProxyHandle AcquireRenderProxy(const SkinnedMeshRenderProxyDesc& desc);
bool ReleaseRenderProxy(SkinnedMeshRenderProxyHandle& handle);
RPI::Ptr<SkinnedMeshComputePass> GetSkinningPass() const;
RPI::Ptr<MorphTargetComputePass> GetMorphTargetPass() const;
Data::Instance<RPI::Shader> GetSkinningShader() const;
RPI::ShaderOptionGroup CreateSkinningShaderOptionGroup(const SkinnedMeshShaderOptions shaderOptions, SkinnedMeshShaderOptionNotificationBus::Handler& shaderReinitializedHandler);
void OnSkinningShaderReinitialized(const Data::Instance<RPI::Shader> skinningShader);
void SubmitSkinningDispatchItems(RHI::CommandList* commandList);
Data::Instance<RPI::Shader> GetMorphTargetShader() const;
void SubmitMorphTargetDispatchItems(RHI::CommandList* commandList);
private:
AZ_DISABLE_COPY_MOVE(SkinnedMeshFeatureProcessor);
void InitSkinningAndMorphPass();
void InitSkinningAndMorphPass(const RPI::Ptr<RPI::ParentPass> pipelineRootPass);
SkinnedMeshRenderProxyInterfaceHandle AcquireRenderProxyInterface(const SkinnedMeshRenderProxyDesc& desc) override;
bool ReleaseRenderProxyInterface(SkinnedMeshRenderProxyInterfaceHandle& handle) override;
static const char* s_featureProcessorName;
RPI::Ptr<SkinnedMeshComputePass> m_skinningPass;
RPI::Ptr<MorphTargetComputePass> m_morphTargetPass;
Data::Instance<RPI::Shader> m_skinningShader;
CachedSkinnedMeshShaderOptions m_cachedSkinningShaderOptions;
Data::Instance<RPI::Shader> m_morphTargetShader;
AZStd::concurrency_checker m_renderProxiesChecker;
StableDynamicArray<SkinnedMeshRenderProxy> m_renderProxies;
AZStd::unique_ptr<SkinnedMeshStatsCollector> m_statsCollector;
MeshFeatureProcessor* m_meshFeatureProcessor = nullptr;
AZStd::unordered_set<const RHI::DispatchItem*> m_skinningDispatches;
AZStd::unordered_set<const RHI::DispatchItem*> m_morphTargetDispatches;
AZStd::mutex m_dispatchItemMutex;
};
} // namespace Render

Some files were not shown because too many files have changed in this diff Show More