Merge branch 'main' into sc_automation_balibhan
This commit is contained in:
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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(<ime);
|
||||
tm* today = localtime(<ime);
|
||||
|
||||
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
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -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;
|
||||
|
||||
|
||||
+5
@@ -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);
|
||||
|
||||
+2
@@ -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);
|
||||
|
||||
+6
-2
@@ -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
|
||||
)
|
||||
|
||||
+1
@@ -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
Reference in New Issue
Block a user