From 99a009bb770e363d2fb94419ea98a7a9cb7aaa21 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 17 Sep 2021 12:36:09 -0700 Subject: [PATCH 001/120] WIP checkpoint Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- Code/Legacy/CryCommon/ProjectDefines.h | 3 - Code/Legacy/CryCommon/platform_impl.cpp | 3 - cmake/Packaging.cmake | 89 ++++++++++++------- cmake/Platform/Common/Install_common.cmake | 51 +++++++---- .../Packaging/BootstrapperTheme.xml.in | 15 ++-- .../Platform/Windows/Packaging_windows.cmake | 5 +- cmake/Projects.cmake | 2 +- 7 files changed, 101 insertions(+), 67 deletions(-) diff --git a/Code/Legacy/CryCommon/ProjectDefines.h b/Code/Legacy/CryCommon/ProjectDefines.h index 203bd304c9..2c3df4e37f 100644 --- a/Code/Legacy/CryCommon/ProjectDefines.h +++ b/Code/Legacy/CryCommon/ProjectDefines.h @@ -81,9 +81,6 @@ #include AZ_RESTRICTED_FILE(ProjectDefines_h) #else #define PROJECTDEFINES_H_TRAIT_DISABLE_MONOLITHIC_PROFILING_MARKERS 1 - #if !defined(LINUX) && !defined(APPLE) - #define PROJECTDEFINES_H_TRAIT_ENABLE_SOFTCODE_SYSTEM 1 - #endif #if defined(WIN32) || defined(WIN64) || defined(LINUX) || defined(APPLE) #define PROJECTDEFINES_H_TRAIT_USE_GPU_PARTICLES 1 #endif diff --git a/Code/Legacy/CryCommon/platform_impl.cpp b/Code/Legacy/CryCommon/platform_impl.cpp index 323d3daf65..a68a5150db 100644 --- a/Code/Legacy/CryCommon/platform_impl.cpp +++ b/Code/Legacy/CryCommon/platform_impl.cpp @@ -71,7 +71,6 @@ void InitCRTHandlers() void InitCRTHandlers() {} #endif -#ifndef SOFTCODE ////////////////////////////////////////////////////////////////////////// // This is an entry to DLL initialization function that must be called for each loaded module ////////////////////////////////////////////////////////////////////////// @@ -136,8 +135,6 @@ void* GetDetachEnvironmentSymbol() return reinterpret_cast(&DetachEnvironment); } -#endif // !defined(SOFTCODE) - bool g_bProfilerEnabled = false; ////////////////////////////////////////////////////////////////////////// diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index 477a5f24ea..d13293db40 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -89,37 +89,42 @@ endif() set(_cmake_package_dest ${CPACK_BINARY_DIR}/${CPACK_CMAKE_PACKAGE_FILE}) -string(REPLACE "." ";" _version_componets "${CPACK_DESIRED_CMAKE_VERSION}") -list(GET _version_componets 0 _major_version) -list(GET _version_componets 1 _minor_version) - -set(_url_version_tag "v${_major_version}.${_minor_version}") -set(_package_url "https://cmake.org/files/${_url_version_tag}/${CPACK_CMAKE_PACKAGE_FILE}") - -message(STATUS "Downloading CMake ${CPACK_DESIRED_CMAKE_VERSION} for packaging...") -download_file( - URL ${_package_url} - TARGET_FILE ${_cmake_package_dest} - EXPECTED_HASH ${CPACK_CMAKE_PACKAGE_HASH} - RESULTS _results -) -list(GET _results 0 _status_code) - -if (${_status_code} EQUAL 0 AND EXISTS ${_cmake_package_dest}) - message(STATUS "Package found and verified!") +if(EXISTS ${_cmake_package_dest}) + message(STATUS "CMake ${CPACK_DESIRED_CMAKE_VERSION} found") else() - file(REMOVE ${_cmake_package_dest}) - list(REMOVE_AT _results 0) + # download it + string(REPLACE "." ";" _version_componets "${CPACK_DESIRED_CMAKE_VERSION}") + list(GET _version_componets 0 _major_version) + list(GET _version_componets 1 _minor_version) - set(_error_message "An error occurred, code ${_status_code}. URL ${_package_url} - ${_results}") + set(_url_version_tag "v${_major_version}.${_minor_version}") + set(_package_url "https://cmake.org/files/${_url_version_tag}/${CPACK_CMAKE_PACKAGE_FILE}") - if(${_status_code} EQUAL 1) - string(APPEND _error_message - " Please double check the CPACK_CMAKE_PACKAGE_FILE and " - "CPACK_CMAKE_PACKAGE_HASH properties before trying again.") + message(STATUS "Downloading CMake ${CPACK_DESIRED_CMAKE_VERSION} for packaging...") + download_file( + URL ${_package_url} + TARGET_FILE ${_cmake_package_dest} + EXPECTED_HASH ${CPACK_CMAKE_PACKAGE_HASH} + RESULTS _results + ) + list(GET _results 0 _status_code) + + if (${_status_code} EQUAL 0 AND EXISTS ${_cmake_package_dest}) + message(STATUS "CMake ${CPACK_DESIRED_CMAKE_VERSION} found") + else() + file(REMOVE ${_cmake_package_dest}) + list(REMOVE_AT _results 0) + + set(_error_message "An error occurred, code ${_status_code}. URL ${_package_url} - ${_results}") + + if(${_status_code} EQUAL 1) + string(APPEND _error_message + " Please double check the CPACK_CMAKE_PACKAGE_FILE and " + "CPACK_CMAKE_PACKAGE_HASH properties before trying again.") + endif() + + message(FATAL_ERROR ${_error_message}) endif() - - message(FATAL_ERROR ${_error_message}) endif() install(FILES ${_cmake_package_dest} @@ -192,17 +197,19 @@ include(CPack REQUIRED) function(ly_configure_cpack_component ly_configure_cpack_component_NAME) - set(options REQUIRED) - set(oneValueArgs DISPLAY_NAME DESCRIPTION LICENSE_NAME LICENSE_FILE) + set(options REQUIRED DISABLED) + set(oneValueArgs DISPLAY_NAME DESCRIPTION LICENSE_NAME LICENSE_FILE DEPENDS) set(multiValueArgs) cmake_parse_arguments(ly_configure_cpack_component "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - # default to optional - set(component_type DISABLED) + unset(component_type) + if(ly_configure_cpack_component_DISABLED) + list(APPEND component_type DISABLED) + endif() if(ly_configure_cpack_component_REQUIRED) - set(component_type REQUIRED) + list(APPEND component_type REQUIRED) endif() set(license_name ${DEFAULT_LICENSE_NAME}) @@ -225,9 +232,23 @@ endfunction() # configure ALL components here ly_configure_cpack_component( ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} REQUIRED - DISPLAY_NAME "${PROJECT_NAME} Core" - DESCRIPTION "${PROJECT_NAME} Headers, Libraries and Tools" + DISPLAY_NAME "${PROJECT_NAME}" + DESCRIPTION "${PROJECT_NAME} Headers, scripts and common files" ) +foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) + string(TOUPPER ${conf} UCONF) + unset(flags) + if(${conf} STREQUAL profile) + set(flags REQUIRED) + else() + set(flags DISABLED) + endif() + ly_configure_cpack_component( + ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF} ${flags} + DISPLAY_NAME "${PROJECT_NAME} (${conf})" + DESCRIPTION "${PROJECT_NAME} Libraries and Tools in ${conf}" + ) +endforeach() if(LY_INSTALLER_DOWNLOAD_URL) strip_trailing_slash(${LY_INSTALLER_DOWNLOAD_URL} LY_INSTALLER_DOWNLOAD_URL) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index fdd3256d78..ab34446922 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -105,18 +105,24 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar RUNTIME_SUBDIR ${target_runtime_output_subdirectory} ) else() - install( - TARGETS ${TARGET_NAME} - ARCHIVE - DESTINATION ${archive_output_directory} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - LIBRARY - DESTINATION ${library_output_directory}/${target_library_output_subdirectory} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - RUNTIME - DESTINATION ${runtime_output_directory}/${target_runtime_output_subdirectory} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - ) + foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) + string(TOUPPER ${conf} UCONF) + install( + TARGETS ${TARGET_NAME} + ARCHIVE + DESTINATION ${archive_output_directory} + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF} + CONFIGURATIONS ${conf} + LIBRARY + DESTINATION ${library_output_directory}/${target_library_output_subdirectory} + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF} + CONFIGURATIONS ${conf} + RUNTIME + DESTINATION ${runtime_output_directory}/${target_runtime_output_subdirectory} + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF} + CONFIGURATIONS ${conf} + ) + endforeach() endif() # CMakeLists.txt related files @@ -499,12 +505,15 @@ function(ly_setup_runtime_dependencies) if(COMMAND ly_install_code_function_override) ly_install_code_function_override() else() - install(CODE + foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) + string(TOUPPER ${conf} UCONF) + install(CODE "function(ly_copy source_file target_directory) file(COPY \"\${source_file}\" DESTINATION \"\${target_directory}\" FILE_PERMISSIONS ${LY_COPY_PERMISSIONS}) endfunction()" - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - ) + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF} + ) + endforeach() endif() unset(runtime_commands) @@ -543,9 +552,15 @@ endfunction()" list(REMOVE_DUPLICATES runtime_commands) list(JOIN runtime_commands " " runtime_commands_str) # the spaces are just to see the right identation in the cmake_install.cmake file - install(CODE "${runtime_commands_str}" - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - ) + foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) + string(TOUPPER ${conf} UCONF) + install(CODE +"if(\"\${CMAKE_INSTALL_CONFIG_NAME}\" MATCHES \"^(${conf})\$\") + ${runtime_commands_str} +endif()" + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF} + ) + endforeach() endfunction() diff --git a/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in b/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in index eea1adc11b..cafa6c765a 100644 --- a/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in +++ b/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in @@ -21,7 +21,7 @@ #(loc.InstallHeader) -@WIX_THEME_INSTALL_LICENSE_ELEMENTS@ + @@ -33,11 +33,14 @@ #(loc.OptionsHeader) - #(loc.OptionsLocationLabel) - - - #(loc.OptionsWarningTitle) - #(loc.OptionsWarning) + Include debug SDK + Include release monolithic SDK + + #(loc.OptionsLocationLabel) + + + #(loc.OptionsWarningTitle) + #(loc.OptionsWarning) diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 5bb9928b61..ddfaea00d1 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -23,6 +23,7 @@ set(CPACK_WIX_ROOT ${LY_INSTALLER_WIX_ROOT}) set(CPACK_GENERATOR WIX) +set(CPACK_THREADS 0) set(_cmake_package_name "cmake-${CPACK_DESIRED_CMAKE_VERSION}-windows-x86_64") set(CPACK_CMAKE_PACKAGE_FILE "${_cmake_package_name}.zip") set(CPACK_CMAKE_PACKAGE_HASH "15a49e2ab81c1822d75b1b1a92f7863f58e31f6d6aac1c4103eef2b071be3112") @@ -30,8 +31,8 @@ set(CPACK_CMAKE_PACKAGE_HASH "15a49e2ab81c1822d75b1b1a92f7863f58e31f6d6aac1c4103 # workaround for shortening the path cpack installs to by stripping the platform directory and forcing monolithic # mode to strip out component folders. this unfortunately is the closest we can get to changing the install location # as CPACK_PACKAGING_INSTALL_PREFIX/CPACK_SET_DESTDIR isn't supported for the WiX generator -set(CPACK_TOPLEVEL_TAG "") -set(CPACK_MONOLITHIC_INSTALL ON) +#set(CPACK_TOPLEVEL_TAG "") +#set(CPACK_MONOLITHIC_INSTALL ON) # CPack will generate the WiX product/upgrade GUIDs further down the chain if they weren't supplied # however, they are unique for each run. instead, let's do the auto generation here and add it to diff --git a/cmake/Projects.cmake b/cmake/Projects.cmake index 7f2ac6a4fd..85104e72f6 100644 --- a/cmake/Projects.cmake +++ b/cmake/Projects.cmake @@ -10,7 +10,7 @@ include_guard() -set(LY_PROJECTS "${LY_PROJECTS}" CACHE STRING "List of projects to enable, this can be a relative path to the engine root or an absolute path") +set(LY_PROJECTS "" CACHE STRING "List of projects to enable, this can be a relative path to the engine root or an absolute path") #! ly_add_target_dependencies: adds module load dependencies for this target. # From 642f2b37eeba821f3dacadd3a35d8efd6c02ce35 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Wed, 22 Sep 2021 18:48:26 -0700 Subject: [PATCH 002/120] able to put release monolithic into an installer Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/Packaging.cmake | 13 ++ .../Packaging/BootstrapperTheme.wxl.in | 2 + .../Packaging/BootstrapperTheme.xml.in | 121 +++++++++--------- .../Platform/Windows/Packaging_windows.cmake | 4 +- .../build/Platform/Windows/build_config.json | 2 +- .../Platform/Windows/installer_windows.cmd | 2 +- 6 files changed, 79 insertions(+), 65 deletions(-) diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index d13293db40..3c98c42d86 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -235,6 +235,8 @@ ly_configure_cpack_component( DISPLAY_NAME "${PROJECT_NAME}" DESCRIPTION "${PROJECT_NAME} Headers, scripts and common files" ) +#file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" "set(LY_CPACK_COMPONENTS_ALL ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME})\n") + foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) string(TOUPPER ${conf} UCONF) unset(flags) @@ -243,11 +245,20 @@ foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) else() set(flags DISABLED) endif() + + # Inject a check to not declare components that have not been built. We are using AzCore since that is a + # common target that will always be build, in every permutation and configuration + #file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" + # "if(EXISTS \"${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${conf}/${CMAKE_STATIC_LIBRARY_PREFIX}AzCore${CMAKE_STATIC_LIBRARY_SUFFIX}\")\n") ly_configure_cpack_component( ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF} ${flags} DISPLAY_NAME "${PROJECT_NAME} (${conf})" DESCRIPTION "${PROJECT_NAME} Libraries and Tools in ${conf}" ) + #file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" +#"list(APPEND LY_CPACK_COMPONENTS_ALL ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF}) +#endif()\n") + endforeach() if(LY_INSTALLER_DOWNLOAD_URL) @@ -260,3 +271,5 @@ if(LY_INSTALLER_DOWNLOAD_URL) ALL ) endif() + +#file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" "set(CPACK_COMPONENTS_ALL \${LY_CPACK_COMPONENTS_ALL})\n") diff --git a/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in b/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in index 7d221913d5..9e5f796b70 100644 --- a/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in +++ b/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in @@ -20,6 +20,8 @@ Setup will install [WixBundleName] on your computer. Click install to continue, Setup Options + Include debug SDK + Include release-monolithic SDK Install location: &Browse WARNING: diff --git a/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in b/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in index cafa6c765a..22e76ad2db 100644 --- a/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in +++ b/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in @@ -1,7 +1,7 @@ - #(loc.WindowTitle) + #(loc.WindowTitle) Segoe UI @@ -15,113 +15,112 @@ Segoe UI - + - #(loc.InstallHeader) + #(loc.InstallHeader) - - - - + + + + - #(loc.OptionsHeader) + #(loc.OptionsHeader) - Include debug SDK - Include release monolithic SDK + #(loc.IncludeDebugSDK) + #(loc.IncludeReleaseMonolithicSDK) - #(loc.OptionsLocationLabel) - - - #(loc.OptionsWarningTitle) - #(loc.OptionsWarning) + #(loc.OptionsLocationLabel) + + + #(loc.OptionsWarningTitle) + #(loc.OptionsWarning) - - - + + + - #(loc.ModifyHeader) + #(loc.ModifyHeader) - - - - + + + + - #(loc.ProgressHeader) + #(loc.ProgressHeader) - #(loc.CacheProgressLabel) - - + #(loc.CacheProgressLabel) + + 100% - #(loc.ExecuteProgressLabel) - - + #(loc.ExecuteProgressLabel) + + 100% - - + + - #(loc.FilesInUseHeader) + #(loc.FilesInUseHeader) - #(loc.FilesInUseLabel) - + #(loc.FilesInUseLabel) + + + - - - - - - + + + - #(loc.SuccessHeader) - #(loc.SuccessInstallHeader) - #(loc.SuccessRepairHeader) - #(loc.SuccessUninstallHeader) + #(loc.SuccessHeader) + #(loc.SuccessInstallHeader) + #(loc.SuccessRepairHeader) + #(loc.SuccessUninstallHeader) - - - + + + - - #(loc.FailureHeader) - #(loc.FailureInstallHeader) - #(loc.FailureUninstallHeader) - #(loc.FailureRepairHeader) + + #(loc.FailureHeader) + #(loc.FailureInstallHeader) + #(loc.FailureUninstallHeader) + #(loc.FailureRepairHeader) - #(loc.FailureHyperlinkLogText) - + #(loc.FailureHyperlinkLogText) + MessageText - - + + - #(loc.HelpHeader) + #(loc.HelpHeader) - #(loc.HelpText) + #(loc.HelpText) - - + + diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index ddfaea00d1..63f0abc6ae 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -83,8 +83,8 @@ endif() set(CPACK_WIX_PRODUCT_GUID ${LY_WIX_PRODUCT_GUID}) set(CPACK_WIX_UPGRADE_GUID ${LY_WIX_UPGRADE_GUID}) -set(CPACK_WIX_PRODUCT_LOGO ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/product_logo.png) -set(CPACK_WIX_PRODUCT_ICON ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/product_icon.ico) +set(CPACK_WIX_PRODUCT_LOGO ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/logo.png) +set(CPACK_WIX_PRODUCT_ICON ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/icon.ico) set(CPACK_WIX_TEMPLATE "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/Template.wxs.in") diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 3848e6f980..a90a99856b 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -359,7 +359,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"!WIX! \"", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"!WIX!\"", "EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=https://www.o3debinaries.org -DLY_INSTALLER_LICENSE_URL=https://www.o3debinaries.org/license", "CPACK_BUCKET": "spectra-prism-staging-us-west-2", "CMAKE_LY_PROJECTS": "", diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index 87f53adc7f..94fb8c4f42 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -52,7 +52,7 @@ IF NOT "%CPACK_BUCKET%"=="" ( ) ECHO [ci_build] "!CPACK_PATH!" -C %CONFIGURATION% %CPACK_OPTIONS% -"!CPACK_PATH!" -C %CONFIGURATION% %CPACK_OPTIONS% +REM "!CPACK_PATH!" -C %CONFIGURATION% %CPACK_OPTIONS% IF NOT %ERRORLEVEL%==0 ( REM dump the log file generated by cpack specifically for WIX ECHO **************************************************************** From a2e9b0cb6922f4276da0b547988ab48c4b74580e Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Wed, 22 Sep 2021 19:29:08 -0700 Subject: [PATCH 003/120] reverting some changes that affected the custom UI Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- .../Packaging/BootstrapperTheme.wxl.in | 2 - .../Packaging/BootstrapperTheme.xml.in | 122 +++++++++--------- .../Platform/Windows/Packaging_windows.cmake | 4 +- 3 files changed, 62 insertions(+), 66 deletions(-) diff --git a/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in b/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in index 9e5f796b70..7d221913d5 100644 --- a/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in +++ b/cmake/Platform/Windows/Packaging/BootstrapperTheme.wxl.in @@ -20,8 +20,6 @@ Setup will install [WixBundleName] on your computer. Click install to continue, Setup Options - Include debug SDK - Include release-monolithic SDK Install location: &Browse WARNING: diff --git a/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in b/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in index 22e76ad2db..eea1adc11b 100644 --- a/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in +++ b/cmake/Platform/Windows/Packaging/BootstrapperTheme.xml.in @@ -1,7 +1,7 @@ - #(loc.WindowTitle) + #(loc.WindowTitle) Segoe UI @@ -15,112 +15,110 @@ Segoe UI - + - #(loc.InstallHeader) + #(loc.InstallHeader) - +@WIX_THEME_INSTALL_LICENSE_ELEMENTS@ - - - - + + + + - #(loc.OptionsHeader) + #(loc.OptionsHeader) - #(loc.IncludeDebugSDK) - #(loc.IncludeReleaseMonolithicSDK) + #(loc.OptionsLocationLabel) + + + #(loc.OptionsWarningTitle) + #(loc.OptionsWarning) - #(loc.OptionsLocationLabel) - - - #(loc.OptionsWarningTitle) - #(loc.OptionsWarning) - - - - + + + - #(loc.ModifyHeader) + #(loc.ModifyHeader) - - - - + + + + - #(loc.ProgressHeader) + #(loc.ProgressHeader) - #(loc.CacheProgressLabel) - - 100% + #(loc.CacheProgressLabel) + + - #(loc.ExecuteProgressLabel) - - 100% + #(loc.ExecuteProgressLabel) + + - - + + - #(loc.FilesInUseHeader) + #(loc.FilesInUseHeader) - #(loc.FilesInUseLabel) - - - + #(loc.FilesInUseLabel) + - - - + + + + + + - #(loc.SuccessHeader) - #(loc.SuccessInstallHeader) - #(loc.SuccessRepairHeader) - #(loc.SuccessUninstallHeader) + #(loc.SuccessHeader) + #(loc.SuccessInstallHeader) + #(loc.SuccessRepairHeader) + #(loc.SuccessUninstallHeader) - - - + + + - - #(loc.FailureHeader) - #(loc.FailureInstallHeader) - #(loc.FailureUninstallHeader) - #(loc.FailureRepairHeader) + + #(loc.FailureHeader) + #(loc.FailureInstallHeader) + #(loc.FailureUninstallHeader) + #(loc.FailureRepairHeader) - #(loc.FailureHyperlinkLogText) - MessageText + #(loc.FailureHyperlinkLogText) + - - + + - #(loc.HelpHeader) + #(loc.HelpHeader) - #(loc.HelpText) + #(loc.HelpText) - - + + diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 63f0abc6ae..ddfaea00d1 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -83,8 +83,8 @@ endif() set(CPACK_WIX_PRODUCT_GUID ${LY_WIX_PRODUCT_GUID}) set(CPACK_WIX_UPGRADE_GUID ${LY_WIX_UPGRADE_GUID}) -set(CPACK_WIX_PRODUCT_LOGO ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/logo.png) -set(CPACK_WIX_PRODUCT_ICON ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/icon.ico) +set(CPACK_WIX_PRODUCT_LOGO ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/product_logo.png) +set(CPACK_WIX_PRODUCT_ICON ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/product_icon.ico) set(CPACK_WIX_TEMPLATE "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/Template.wxs.in") From f92daf060bdd2ccd37ef095b6d3d388e4b5eb379 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Thu, 23 Sep 2021 12:41:26 -0700 Subject: [PATCH 004/120] Working on exposing the doubles-sided flag outside the Opacity property group. Before, the only way to set the double-sided flag was to enable a non-opaque mode, because the flag was hidden. We are moving the double-sided flag to the general property group instead of the opacity property group, so it is always available. In this particular commit, we just add the general.doubleSided property so we don't break existing data. In an upcoming commit I will remove opacity.doubleSided, once we have the material backward compatibility system ready. I also added another "default" texture map to the Common/Feature gem that is directional, so better for understanding UV/tangent space. These were copied from the AtomLyIntegration gem. This is being used for a screenshot test in AtomSampleViewer with the new 009_Opacity_Opaque_DoubleSided.material. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../Materials/Types/EnhancedPBR.materialtype | 6 ++++++ .../Materials/Types/StandardPBR.materialtype | 6 ++++++ .../Types/StandardPBR_HandleOpacityDoubleSided.lua | 8 +++++--- .../Assets/Textures/Default/checker_basecolor.tif | 3 +++ .../Textures/Default/checker_uv_basecolor.png | 3 +++ .../009_Opacity_Opaque_DoubleSided.material | 14 ++++++++++++++ Gems/Atom/TestData/TestData/Objects/tube.fbx | 3 +++ 7 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Assets/Textures/Default/checker_basecolor.tif create mode 100644 Gems/Atom/Feature/Common/Assets/Textures/Default/checker_uv_basecolor.png create mode 100644 Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_Opaque_DoubleSided.material create mode 100644 Gems/Atom/TestData/TestData/Objects/tube.fbx diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype index 9b2c465352..66b88fddf9 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype @@ -92,6 +92,12 @@ ], "properties": { "general": [ + { + "id": "doubleSided", + "displayName": "Double-sided", + "description": "Whether to render back-faces or just front-faces.", + "type": "Bool" + }, { "id": "applySpecularAA", "displayName": "Apply Specular AA", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype index 8b7e4b1c7e..4038a2465d 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype @@ -72,6 +72,12 @@ ], "properties": { "general": [ + { + "id": "doubleSided", + "displayName": "Double-sided", + "description": "Whether to render back-faces or just front-faces.", + "type": "Bool" + }, { "id": "applySpecularAA", "displayName": "Apply Specular AA", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua index 2382f3f0f0..8b3bd2b91b 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua @@ -10,17 +10,19 @@ ---------------------------------------------------------------------------------------------------- function GetMaterialPropertyDependencies() - return {"opacity.doubleSided"} + return {"general.doubleSided", "opacity.doubleSided", "opacity.mode"} end ForwardPassIndex = 0 ForwardPassEdsIndex = 1 function Process(context) - local doubleSided = context:GetMaterialPropertyValue_bool("opacity.doubleSided") + local doubleSided = context:GetMaterialPropertyValue_bool("general.doubleSided") + local opacityDoubleSided = context:GetMaterialPropertyValue_bool("opacity.doubleSided") + local opacityMode = context:GetMaterialPropertyValue_enum("opacity.mode") local lastShader = context:GetShaderCount() - 1; - if(doubleSided) then + if(doubleSided or (opacityDoubleSided and opacityMode ~= 0)) then for i=0,lastShader do context:GetShader(i):GetRenderStatesOverride():SetCullMode(CullMode_None) end diff --git a/Gems/Atom/Feature/Common/Assets/Textures/Default/checker_basecolor.tif b/Gems/Atom/Feature/Common/Assets/Textures/Default/checker_basecolor.tif new file mode 100644 index 0000000000..5abe5bbd49 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Textures/Default/checker_basecolor.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:57d6744696768f9fb8a5fe5fee9aa36fee1eb87a9dbc1e60d4a35ed3c39d68e6 +size 810620 diff --git a/Gems/Atom/Feature/Common/Assets/Textures/Default/checker_uv_basecolor.png b/Gems/Atom/Feature/Common/Assets/Textures/Default/checker_uv_basecolor.png new file mode 100644 index 0000000000..07e240baf9 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Textures/Default/checker_uv_basecolor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:513f47f6fea5105f603170a8881b7e3b1cd2c4258636d64a6399c725032b500d +size 38689 diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_Opaque_DoubleSided.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_Opaque_DoubleSided.material new file mode 100644 index 0000000000..a26bf6e045 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_Opaque_DoubleSided.material @@ -0,0 +1,14 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardPBR.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 3, + "properties": { + "baseColor": { + "textureMap": "Textures/Default/checker_uv_basecolor.png" + }, + "general": { + "doubleSided": true + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Objects/tube.fbx b/Gems/Atom/TestData/TestData/Objects/tube.fbx new file mode 100644 index 0000000000..f9034e7641 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Objects/tube.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b2ecc32cd3052f3cb5836c8be7bf5cba54d98f46e6a0eeac95aaef00a123411a +size 27340 From 720495748ef91d4af3d0542288855b526680c6b4 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Thu, 23 Sep 2021 12:42:10 -0700 Subject: [PATCH 005/120] Since I was already working with _dev_shaderball_00_basecolor.png from AtomLyIntegration gem, I went ahead and updated the one in MaterialEditor to match, because I noticed that the one from AtomLyIntegration was a bit nicer, having colored arrays instead of low contrast gray ones. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../ViewportModels/_dev_shaderball_00_basecolor.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/Atom/Tools/MaterialEditor/Assets/MaterialEditor/ViewportModels/_dev_shaderball_00_basecolor.png b/Gems/Atom/Tools/MaterialEditor/Assets/MaterialEditor/ViewportModels/_dev_shaderball_00_basecolor.png index 415ca3e521..07e240baf9 100644 --- a/Gems/Atom/Tools/MaterialEditor/Assets/MaterialEditor/ViewportModels/_dev_shaderball_00_basecolor.png +++ b/Gems/Atom/Tools/MaterialEditor/Assets/MaterialEditor/ViewportModels/_dev_shaderball_00_basecolor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93a7e033d9fb0fcac221647322bde03716643d789390f79078c4fcc37ecfd005 -size 68327 +oid sha256:513f47f6fea5105f603170a8881b7e3b1cd2c4258636d64a6399c725032b500d +size 38689 From 8ab9f89b46866cc66735260416f35bdba51dfb1e Mon Sep 17 00:00:00 2001 From: evanchia Date: Wed, 13 Oct 2021 12:43:54 -0700 Subject: [PATCH 006/120] Adding unit tests and docstrings for editor test files Signed-off-by: evanchia --- .../_internal/pytest_plugin/editor_test.py | 31 +- .../pytest_plugin/test_tools_fixtures.py | 10 +- .../ly_test_tools/o3de/editor_test.py | 357 ++++-- .../ly_test_tools/o3de/editor_test_utils.py | 24 +- .../tests/unit/test_editor_test_utils.py | 158 +++ Tools/LyTestTools/tests/unit/test_fixtures.py | 11 + .../tests/unit/test_o3de_editor_test.py | 1017 +++++++++++++++++ .../unit/test_pytest_plugin_editor_test.py | 41 + 8 files changed, 1567 insertions(+), 82 deletions(-) create mode 100644 Tools/LyTestTools/tests/unit/test_editor_test_utils.py create mode 100644 Tools/LyTestTools/tests/unit/test_o3de_editor_test.py create mode 100644 Tools/LyTestTools/tests/unit/test_pytest_plugin_editor_test.py diff --git a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/editor_test.py b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/editor_test.py index 80a562977b..2df5810047 100644 --- a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/editor_test.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/editor_test.py @@ -3,9 +3,7 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. SPDX-License-Identifier: Apache-2.0 OR MIT -""" -""" Utility for specifying an Editor test, supports seamless parallelization and/or batching of tests. """ @@ -15,22 +13,41 @@ import inspect __test__ = False def pytest_addoption(parser): + # type (argparse.ArgumentParser) -> None + """ + Options when running tests in batches or parallel. + :param parser: The ArgumentParser object + :return: None + """ parser.addoption("--no-editor-batch", action="store_true", help="Don't batch multiple tests in single editor") parser.addoption("--no-editor-parallel", action="store_true", help="Don't run multiple editors in parallel") parser.addoption("--editors-parallel", type=int, action="store", help="Override the number editors to run at the same time") -# Create a custom custom item collection if the class defines pytest_custom_makeitem function -# This is used for automtically generating test functions with a custom collector def pytest_pycollect_makeitem(collector, name, obj): + # type (PyCollector, str, object) -> Collector + """ + Create a custom custom item collection if the class defines pytest_custom_makeitem function. This is used for + automtically generating test functions with a custom collector. + :param collector: The Python test collector + :param name: Name of the collector + :param obj: The custom collector, normally an EditorTestSuite.EditorTestClass object + :return: Returns the custom collector + """ if inspect.isclass(obj): for base in obj.__bases__: if hasattr(base, "pytest_custom_makeitem"): return base.pytest_custom_makeitem(collector, name, obj) -# Add custom modification of items. -# This is used for adding the runners into the item list @pytest.hookimpl(hookwrapper=True) def pytest_collection_modifyitems(session, items, config): + # type (Session, list, Config) -> None + """ + Add custom modification of items. This is used for adding the runners into the item list. + :param session: The Pytest Session + :param items: The test case functions + :param config: The Pytest Config object + :return: None + """ all_classes = set() for item in items: all_classes.add(item.instance.__class__) @@ -40,4 +57,4 @@ def pytest_collection_modifyitems(session, items, config): for cls in all_classes: if hasattr(cls, "pytest_custom_modify_items"): cls.pytest_custom_modify_items(session, items, config) - + diff --git a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py index f4bda9b293..3115c28406 100755 --- a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py @@ -55,8 +55,16 @@ def pytest_configure(config): ly_test_tools._internal.pytest_plugin.build_directory = _get_build_directory(config) ly_test_tools._internal.pytest_plugin.output_path = _get_output_path(config) - def pytest_pycollect_makeitem(collector, name, obj): + # type (PyCollector, str, object) -> Collector + """ + Create a custom custom item collection if the class defines pytest_custom_makeitem function. This is used for + automtically generating test functions with a custom collector. + :param collector: The Python test collector + :param name: Name of the collector + :param obj: The custom collector, normally an EditorTestSuite.EditorTestClass object + :return: Returns the custom collector + """ import inspect if inspect.isclass(obj): for base in obj.__bases__: diff --git a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py index 781977cf33..39413c22a3 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py @@ -25,16 +25,16 @@ import re import ly_test_tools.environment.file_system as file_system import ly_test_tools.environment.waiter as waiter import ly_test_tools.environment.process_utils as process_utils +import ly_test_tools.o3de.editor_test_utils as editor_utils from ly_test_tools.o3de.asset_processor import AssetProcessor from ly_test_tools.launchers.exceptions import WaitTimeoutError -from . import editor_test_utils as editor_utils # This file provides editor testing functionality to easily write automated editor tests for O3DE. -# For using these utilities, you can subclass your test suite from EditorTestSuite, this allows an easy way of specifying -# python test scripts that the editor will run without needing to write any boilerplace code. -# It supports out of the box parallelization(running multiple editor instances at once), batching(running multiple tests in the same editor instance) and -# crash detection. +# For using these utilities, you can subclass your test suite from EditorTestSuite, this allows an easy way of +# specifying python test scripts that the editor will run without needing to write any boilerplace code. +# It supports out of the box parallelization(running multiple editor instances at once), batching(running multiple tests +# in the same editor instance) and crash detection. # Usage example: # class MyTestSuite(EditorTestSuite): # @@ -48,7 +48,8 @@ from . import editor_test_utils as editor_utils # from . import yet_another_script_to_be_run_by_editor as test_module # # -# EditorTestSuite does introspection of the defined classes inside of it and automatically prepares the tests, parallelizing/batching as required +# EditorTestSuite does introspection of the defined classes inside of it and automatically prepares the tests, +# parallelizing/batching as required # This file contains no tests, but with this we make sure it won't be picked up by the runner since the file ends with _test __test__ = False @@ -109,12 +110,22 @@ class EditorBatchedTest(EditorSharedTest): class Result: class Base: def get_output_str(self): + # type () -> str + """ + Checks if the output attribute exists and returns it. + :return: Either the output string or a no output message + """ if hasattr(self, "output") and self.output is not None: return self.output else: return "-- No output --" def get_editor_log_str(self): + # type () -> str + """ + Checks if the editor_log attribute exists and returns it. + :return: Either the editor_log string or a no output message + """ if hasattr(self, "editor_log") and self.editor_log is not None: return self.editor_log else: @@ -122,7 +133,15 @@ class Result: class Pass(Base): @classmethod - def create(cls, test_spec : EditorTestBase, output : str, editor_log : str): + def create(cls, test_spec, output, editor_log): + # type (EditorTestBase, str, str) -> Pass + """ + Creates a Pass object with a given test spec, output string, and editor log string. + :test_spec: The type of EditorTestBase + :output: The test output + :editor_log: The editor log's output + :return: the Pass object + """ r = cls() r.test_spec = test_spec r.output = output @@ -141,7 +160,15 @@ class Result: class Fail(Base): @classmethod - def create(cls, test_spec : EditorTestBase, output, editor_log : str): + def create(cls, test_spec, output, editor_log): + # type (EditorTestBase, str, str) -> Fail + """ + Creates a Fail object with a given test spec, output string, and editor log string. + :test_spec: The type of EditorTestBase + :output: The test output + :editor_log: The editor log's output + :return: the Fail object + """ r = cls() r.test_spec = test_spec r.output = output @@ -164,7 +191,18 @@ class Result: class Crash(Base): @classmethod - def create(cls, test_spec : EditorTestBase, output : str, ret_code : int, stacktrace : str, editor_log : str): + def create(cls, test_spec, output, ret_code, stacktrace, editor_log): + # type (EditorTestBase, str, int, str, str) -> Crash + """ + Creates a Crash object with a given test spec, output string, and editor log string. This also includes the + return code and stacktrace. + :test_spec: The type of EditorTestBase + :output: The test output + :ret_code: The test's return code + :stacktrace: The test's stacktrace if available + :editor_log: The editor log's output + :return: The Crash object + """ r = cls() r.output = output r.test_spec = test_spec @@ -174,7 +212,7 @@ class Result: return r def __str__(self): - stacktrace_str = "-- No stacktrace data found --" if not self.stacktrace else self.stacktrace + stacktrace_str = "-- No stacktrace data found --\n" if not self.stacktrace else self.stacktrace output = ( f"Test CRASHED, return code {hex(self.ret_code)}\n" f"---------------\n" @@ -190,12 +228,21 @@ class Result: f"--------------\n" f"{self.get_editor_log_str()}\n" ) - crash_str = "-- No crash information found --" return output class Timeout(Base): @classmethod - def create(cls, test_spec : EditorTestBase, output : str, time_secs : float, editor_log : str): + def create(cls, test_spec, output, time_secs, editor_log): + # type (EditorTestBase, str, float, str) -> Timeout + """ + Creates a Timeout object with a given test spec, output string, and editor log string. The timeout time + should be provided in seconds + :test_spec: The type of EditorTestBase + :output: The test output + :time_secs: The timeout duration in seconds + :editor_log: The editor log's output + :return: The Timeout object + """ r = cls() r.output = output r.test_spec = test_spec @@ -219,14 +266,23 @@ class Result: class Unknown(Base): @classmethod - def create(cls, test_spec : EditorTestBase, output : str, extra_info : str, editor_log : str): + def create(cls, test_spec, output, extra_info, editor_log): + # type (EditorTestBase, str, str , str) -> Unknown + """ + Creates an Unknown test results object if something goes wrong. + :test_spec: The type of EditorTestBase + :output: The test output + :extra_info: Any extra information as a string + :editor_log: The editor log's output + :return: The Unknown object + """ r = cls() r.output = output r.test_spec = test_spec r.editor_log = editor_log r.extra_info = extra_info return r - + def __str__(self): output = ( f"Unknown test result, possible cause: {self.extra_info}\n" @@ -263,6 +319,18 @@ class EditorTestSuite(): @pytest.fixture(scope="class") def editor_test_data(self, request): + # type (request) -> TestData + """ + Yields a generator to capture the test results and an AssetProcessor object. + :request: The pytest request + :yield: The TestData object + """ + self._editor_test_data(request) + + def _editor_test_data(self, request): + """ + A wrapper function for unit testing to call directly + """ class TestData(): def __init__(self): self.results = {} # Dict of str(test_spec.__name__) -> Result @@ -445,6 +513,10 @@ class EditorTestSuite(): @classmethod def pytest_custom_modify_items(cls, session, items, config): + # type () -> None + """ + + """ # Add here the runners functions and filter the tests that will be run. # The runners will be added if they have any selected tests new_items = [] @@ -463,23 +535,53 @@ class EditorTestSuite(): @classmethod def get_single_tests(cls): + # type () -> list + """ + Grabs all of the EditorSingleTests subclassed tests from the EditorTestSuite class + Usage example: + class MyTestSuite(EditorTestSuite): + class MyFirstTest(EditorSingleTest): + from . import script_to_be_run_by_editor as test_module + :return: The list of single tests + """ single_tests = [c[1] for c in cls.__dict__.items() if inspect.isclass(c[1]) and issubclass(c[1], EditorSingleTest)] return single_tests @classmethod def get_shared_tests(cls): + # type () -> list + """ + Grabs all of the EditorSharedTests from the EditorTestSuite + Usage example: + class MyTestSuite(EditorTestSuite): + class MyFirstTest(EditorSharedTest): + from . import script_to_be_run_by_editor as test_module + :return: The list of shared tests + """ shared_tests = [c[1] for c in cls.__dict__.items() if inspect.isclass(c[1]) and issubclass(c[1], EditorSharedTest)] return shared_tests @classmethod def get_session_shared_tests(cls, session): + # type (Session) -> list[EditorTestBase] + """ + Filters and returns all of the shared tests in a given session. + :session: The test session + :return: The list of tests + """ shared_tests = cls.get_shared_tests() return cls.filter_session_shared_tests(session, shared_tests) @staticmethod def filter_session_shared_tests(session_items, shared_tests): - # Retrieve the test sub-set that was collected - # this can be less than the original set if were overriden via -k argument or similars + # type (list, list) -> list[EditorTestBase] + """ + Retrieve the test sub-set that was collected this can be less than the original set if were overriden via -k + argument or similars + :session_items: The tests in a session to run + :shared_tests: All of the shared tests + :return: The list of filtered tests + """ def will_run(item): try: skipping_pytest_runtest_setup(item) @@ -488,13 +590,20 @@ class EditorTestSuite(): return False session_items_by_name = { item.originalname:item for item in session_items } - selected_shared_tests = [test for test in shared_tests if test.__name__ in session_items_by_name.keys() and will_run(session_items_by_name[test.__name__])] + selected_shared_tests = [test for test in shared_tests if test.__name__ in session_items_by_name.keys() and + will_run(session_items_by_name[test.__name__])] return selected_shared_tests @staticmethod def filter_shared_tests(shared_tests, is_batchable=False, is_parallelizable=False): - # Retrieve the test sub-set that was collected - # this can be less than the original set if were overriden via -k argument or similars + # type (list, bool, bool) -> list + """ + Filters and returns all tests based off of if they are batchable and/or parallelizable + :shared_tests: All shared tests + :is_batchable: Filter to batchable tests + :is_parallelizable: Filter to parallelizable tests + :return: The list of filtered tests + """ return [ t for t in shared_tests if ( getattr(t, "is_batchable", None) is is_batchable @@ -504,9 +613,15 @@ class EditorTestSuite(): ] ### Utils ### - - # Prepares the asset processor for the test def _prepare_asset_processor(self, workspace, editor_test_data): + # type (AbstractWorkspace, TestData) -> None + """ + Prepares the asset processor for the test depending on whether or not the process is open and if the current + test owns it. + :workspace: The workspace object in case an AssetProcessor object needs to be created + :editor_test_data: The test data from calling editor_test_data() + :return: None + """ try: # Start-up an asset processor if we are not running one # If another AP process exist, don't kill it, as we don't own it @@ -525,14 +640,29 @@ class EditorTestSuite(): raise ex def _setup_editor_test(self, editor, workspace, editor_test_data): + # type(Editor, AbstractWorkspace, TestData) -> None + """ + Sets up an editor test by preparing the Asset Processor, killing all other O3DE processes, and configuring + :editor: The launcher Editor object + :workspace: The test Workspace object + :editor_test_data: The TestData from calling editor_test_data() + :return: None + """ self._prepare_asset_processor(workspace, editor_test_data) editor_utils.kill_all_ly_processes(include_asset_processor=False) editor.configure_settings() - # Utility function for parsing the output information from the editor. - # It deserializes the JSON content printed in the output for every test and returns that information. @staticmethod def _get_results_using_output(test_spec_list, output, editor_log_content): + # type(list, str, str) -> dict{str: Result} + """ + Utility function for parsing the output information from the editor. It deserializes the JSON content printed in + the output for every test and returns that information. + :test_spec_list: The list of EditorTests + :output: The Editor from Editor.get_output() + :editor_log_content: The contents of the editor log as a string + :return: A dict of the tests and their respective Result objects + """ results = {} pattern = re.compile(r"JSON_START\((.+?)\)JSON_END") out_matches = pattern.finditer(output) @@ -541,7 +671,8 @@ class EditorTestSuite(): try: elem = json.loads(m.groups()[0]) found_jsons[elem["name"]] = elem - except Exception: + except Exception as e: + raise e continue # Avoid to fail if the output data is corrupt # Try to find the element in the log, this is used for cutting the log contents later @@ -558,7 +689,9 @@ class EditorTestSuite(): for test_spec in test_spec_list: name = editor_utils.get_module_filename(test_spec.test_module) if name not in found_jsons.keys(): - results[test_spec.__name__] = Result.Unknown.create(test_spec, output, "Couldn't find any test run information on stdout", editor_log_content) + results[test_spec.__name__] = Result.Unknown.create(test_spec, output, + "Couldn't find any test run information on stdout", + editor_log_content) else: result = None json_result = found_jsons[name] @@ -573,7 +706,7 @@ class EditorTestSuite(): cur_log = editor_log_content[log_start : end] log_start = end - if json_result["success"]: + if "success" in json_result.keys(): result = Result.Pass.create(test_spec, json_output, cur_log) else: result = Result.Fail.create(test_spec, json_output, cur_log) @@ -581,9 +714,15 @@ class EditorTestSuite(): return results - # Fails the test if the test result is not a PASS, specifying the information @staticmethod - def _report_result(name : str, result : Result.Base): + def _report_result(name, result): + # type (str, Result) -> None + """ + Fails the test if the test result is not a PASS, specifying the information + :name: Name of the test + :result: The Result object which denotes if the test passed or not + :return: None + """ if isinstance(result, Result.Pass): output_str = f"Test {name}:\n{str(result)}" print(output_str) @@ -592,10 +731,19 @@ class EditorTestSuite(): pytest.fail(error_str) ### Running tests ### - # Starts the editor with the given test and retuns an result dict with a single element specifying the result - def _exec_editor_test(self, request, workspace, editor, run_id : int, log_name : str, - test_spec : EditorTestBase, cmdline_args : List[str] = []): - + def _exec_editor_test(self, request, workspace, editor, run_id, log_name, test_spec, cmdline_args = []): + # type (Request, AbstractWorkspace, Editor, int, str, EditorTestBase, list[str] -> dict{str: Result} + """ + Starts the editor with the given test and retuns an result dict with a single element specifying the result + :request: The pytest request + :workspace: The LyTestTools Workspace object + :editor: The LyTestTools Editor object + :run_id: The unique run id + :log_name: The name of the editor log to retrieve + :test_spec: The type of EditorTestBase + :cmdline_args: Any additional command line args + :return: a dictionary of Result objects + """ test_cmdline_args = self.global_extra_cmdline_args + cmdline_args test_spec_uses_null_renderer = getattr(test_spec, "use_null_renderer", None) if test_spec_uses_null_renderer or (test_spec_uses_null_renderer is None and self.use_null_renderer): @@ -629,12 +777,14 @@ class EditorTestSuite(): else: has_crashed = return_code != EditorTestSuite._TEST_FAIL_RETCODE if has_crashed: - test_result = Result.Crash.create(test_spec, output, return_code, editor_utils.retrieve_crash_output(run_id, workspace, self._TIMEOUT_CRASH_LOG), None) + test_result = Result.Crash.create(test_spec, output, return_code, editor_utils.retrieve_crash_output + (run_id, workspace, self._TIMEOUT_CRASH_LOG), None) editor_utils.cycle_crash_report(run_id, workspace) else: test_result = Result.Fail.create(test_spec, output, editor_log_content) except WaitTimeoutError: - editor.kill() + output = editor.get_output() + editor.kill() editor_log_content = editor_utils.retrieve_editor_log_content(run_id, log_name, workspace) test_result = Result.Timeout.create(test_spec, output, test_spec.timeout, editor_log_content) @@ -643,11 +793,21 @@ class EditorTestSuite(): results[test_spec.__name__] = test_result return results - # Starts an editor executable with a list of tests and returns a dict of the result of every test ran within that editor - # instance. In case of failure this function also parses the editor output to find out what specific tests failed - def _exec_editor_multitest(self, request, workspace, editor, run_id : int, log_name : str, - test_spec_list : List[EditorTestBase], cmdline_args=[]): - + def _exec_editor_multitest(self, request, workspace, editor, run_id, log_name, test_spec_list, cmdline_args=[]): + # type (Request, AbstractWorkspace, Editor, int, str, list[EditorTestBase], list[str]) -> dict{str: Result} + """ + Starts an editor executable with a list of tests and returns a dict of the result of every test ran within that + editor instance. In case of failure this function also parses the editor output to find out what specific tests + failed. + :request: The pytest request + :workspace: The LyTestTools Workspace object + :editor: The LyTestTools Editor object + :run_id: The unique run id + :log_name: The name of the editor log to retrieve + :test_spec_list: A list of EditorTestBase tests to run + :cmdline_args: Any additional command line args + :return: A dict of Result objects + """ test_cmdline_args = self.global_extra_cmdline_args + cmdline_args if self.use_null_renderer: test_cmdline_args += ["-rhi=null"] @@ -660,7 +820,8 @@ class EditorTestSuite(): editor_utils.cycle_crash_report(run_id, workspace) results = {} - test_filenames_str = ";".join(editor_utils.get_testcase_module_filepath(test_spec.test_module) for test_spec in test_spec_list) + test_filenames_str = ";".join(editor_utils.get_testcase_module_filepath(test_spec.test_module) for + test_spec in test_spec_list) cmdline = [ "--runpythontest", test_filenames_str, "-logfile", f"@log@/{log_name}", @@ -685,7 +846,8 @@ class EditorTestSuite(): # Scrap the output to attempt to find out which tests failed. # This function should always populate the result list, if it didn't find it, it will have "Unknown" type of result results = self._get_results_using_output(test_spec_list, output, editor_log_content) - assert len(results) == len(test_spec_list), "bug in _get_results_using_output(), the number of results don't match the tests ran" + assert len(results) == len(test_spec_list), "bug in _get_results_using_output(), the number of results" \ + "don't match the tests ran" # If the editor crashed, find out in which test it happened and update the results has_crashed = return_code != EditorTestSuite._TEST_FAIL_RETCODE @@ -695,50 +857,67 @@ class EditorTestSuite(): if isinstance(result, Result.Unknown): if not crashed_result: # The first test with "Unknown" result (no data in output) is likely the one that crashed - crash_error = editor_utils.retrieve_crash_output(run_id, workspace, self._TIMEOUT_CRASH_LOG) + crash_error = editor_utils.retrieve_crash_output(run_id, workspace, + self._TIMEOUT_CRASH_LOG) editor_utils.cycle_crash_report(run_id, workspace) - results[test_spec_name] = Result.Crash.create(result.test_spec, output, return_code, crash_error, result.editor_log) + results[test_spec_name] = Result.Crash.create(result.test_spec, output, return_code, + crash_error, result.editor_log) crashed_result = result else: - # If there are remaning "Unknown" results, these couldn't execute because of the crash, update with info about the offender - results[test_spec_name].extra_info = f"This test has unknown result, test '{crashed_result.test_spec.__name__}' crashed before this test could be executed" - + # If there are remaning "Unknown" results, these couldn't execute because of the crash, + # update with info about the offender + results[test_spec_name].extra_info = f"This test has unknown result, test " \ + f"'{crashed_result.test_spec.__name__}' crashed " \ + f"before this test could be executed" # if all the tests ran, the one that has caused the crash is the last test if not crashed_result: crash_error = editor_utils.retrieve_crash_output(run_id, workspace, self._TIMEOUT_CRASH_LOG) editor_utils.cycle_crash_report(run_id, workspace) - results[test_spec_name] = Result.Crash.create(crashed_result.test_spec, output, return_code, crash_error, crashed_result.editor_log) - - + results[test_spec_name] = Result.Crash.create(crashed_result.test_spec, output, return_code, + crash_error, crashed_result.editor_log) except WaitTimeoutError: editor.kill() - output = editor.get_output() editor_log_content = editor_utils.retrieve_editor_log_content(run_id, log_name, workspace) # The editor timed out when running the tests, get the data from the output to find out which ones ran results = self._get_results_using_output(test_spec_list, output, editor_log_content) - assert len(results) == len(test_spec_list), "bug in _get_results_using_output(), the number of results don't match the tests ran" - + assert len(results) == len(test_spec_list), "bug in _get_results_using_output(), the number of results " \ + "don't match the tests ran" # Similar logic here as crashes, the first test that has no result is the one that timed out timed_out_result = None for test_spec_name, result in results.items(): if isinstance(result, Result.Unknown): if not timed_out_result: - results[test_spec_name] = Result.Timeout.create(result.test_spec, result.output, self.timeout_editor_shared_test, result.editor_log) + results[test_spec_name] = Result.Timeout.create(result.test_spec, result.output, + self.timeout_editor_shared_test, + result.editor_log) timed_out_result = result else: - # If there are remaning "Unknown" results, these couldn't execute because of the timeout, update with info about the offender - results[test_spec_name].extra_info = f"This test has unknown result, test '{timed_out_result.test_spec.__name__}' timed out before this test could be executed" - + # If there are remaning "Unknown" results, these couldn't execute because of the timeout, + # update with info about the offender + results[test_spec_name].extra_info = f"This test has unknown result, test " \ + f"'{timed_out_result.test_spec.__name__}' timed out " \ + f"before this test could be executed" # if all the tests ran, the one that has caused the timeout is the last test, as it didn't close the editor if not timed_out_result: - results[test_spec_name] = Result.Timeout.create(timed_out_result.test_spec, results[test_spec_name].output, self.timeout_editor_shared_test, result.editor_log) + results[test_spec_name] = Result.Timeout.create(timed_out_result.test_spec, + results[test_spec_name].output, + self.timeout_editor_shared_test, result.editor_log) return results - # Runs a single test (one editor, one test) with the given specs - def _run_single_test(self, request, workspace, editor, editor_test_data, test_spec : EditorSingleTest): + def _run_single_test(self, request, workspace, editor, editor_test_data, test_spec): + # type (Request, AbstractWorkspace, Editor, TestData, EditorSingleTest) -> None + """ + Runs a single test (one editor, one test) with the given specs + :request: The Pytest Request + :workspace: The LyTestTools Workspace object + :editor: The LyTestTools Editor object + :editor_test_data: The TestData from calling editor_test_data() + :test_spec: The test class that should be a subclass of EditorSingleTest + :return: None + """ self._setup_editor_test(editor, workspace, editor_test_data) extra_cmdline_args = [] if hasattr(test_spec, "extra_cmdline_args"): @@ -749,18 +928,39 @@ class EditorTestSuite(): test_name, test_result = next(iter(results.items())) self._report_result(test_name, test_result) - # Runs a batch of tests in one single editor with the given spec list (one editor, multiple tests) - def _run_batched_tests(self, request, workspace, editor, editor_test_data, test_spec_list : List[EditorSharedTest], extra_cmdline_args=[]): + def _run_batched_tests(self, request, workspace, editor, editor_test_data, test_spec_list, extra_cmdline_args=[]): + # type (Request, AbstractWorkspace, Editor, TestData, list[EditorSharedTest], list[str]) -> None + """ + Runs a batch of tests in one single editor with the given spec list (one editor, multiple tests) + :request: The Pytest Request + :workspace: The LyTestTools Workspace object + :editor: The LyTestTools Editor object + :editor_test_data: The TestData from calling editor_test_data() + :test_spec_list: A list of EditorSharedTest tests to run + :extra_cmdline_args: Any extra command line args in a list + :return: None + """ if not test_spec_list: return self._setup_editor_test(editor, workspace, editor_test_data) - results = self._exec_editor_multitest(request, workspace, editor, 1, "editor_test.log", test_spec_list, extra_cmdline_args) + results = self._exec_editor_multitest(request, workspace, editor, 1, "editor_test.log", test_spec_list, + extra_cmdline_args) assert results is not None editor_test_data.results.update(results) - # Runs multiple editors with one test on each editor (multiple editor, one test each) - def _run_parallel_tests(self, request, workspace, editor, editor_test_data, test_spec_list : List[EditorSharedTest], extra_cmdline_args=[]): + def _run_parallel_tests(self, request, workspace, editor, editor_test_data, test_spec_list, extra_cmdline_args=[]): + # type(Request, AbstractWorkspace, Editor, TestData, list[EditorSharedTest], list[str]) -> None + """ + Runs multiple editors with one test on each editor (multiple editor, one test each) + :request: The Pytest Request + :workspace: The LyTestTools Workspace object + :editor: The LyTestTools Editor object + :editor_test_data: The TestData from calling editor_test_data() + :test_spec_list: A list of EditorSharedTest tests to run + :extra_cmdline_args: Any extra command line args in a list + :return: None + """ if not test_spec_list: return @@ -778,7 +978,8 @@ class EditorTestSuite(): for i in range(total_threads): def make_func(test_spec, index, my_editor): def run(request, workspace, extra_cmdline_args): - results = self._exec_editor_test(request, workspace, my_editor, index+1, f"editor_test.log", test_spec, extra_cmdline_args) + results = self._exec_editor_test(request, workspace, my_editor, index+1, f"editor_test.log", + test_spec, extra_cmdline_args) assert results is not None results_per_thread[index] = results return run @@ -796,8 +997,19 @@ class EditorTestSuite(): for result in results_per_thread: editor_test_data.results.update(result) - # Runs multiple editors with a batch of tests for each editor (multiple editor, multiple tests each) - def _run_parallel_batched_tests(self, request, workspace, editor, editor_test_data, test_spec_list : List[EditorSharedTest], extra_cmdline_args=[]): + def _run_parallel_batched_tests(self, request, workspace, editor, editor_test_data, test_spec_list, + extra_cmdline_args=[]): + # type(Request, AbstractWorkspace, Editor, TestData, list[EditorSharedTest], list[str] -> None + """ + Runs multiple editors with a batch of tests for each editor (multiple editor, multiple tests each) + :request: The Pytest Request + :workspace: The LyTestTools Workspace object + :editor: The LyTestTools Editor object + :editor_test_data: The TestData from calling editor_test_data() + :test_spec_list: A list of EditorSharedTest tests to run + :extra_cmdline_args: Any extra command line args in a list + :return: None + """ if not test_spec_list: return @@ -813,7 +1025,9 @@ class EditorTestSuite(): def run(request, workspace, extra_cmdline_args): results = None if len(test_spec_list_for_editor) > 0: - results = self._exec_editor_multitest(request, workspace, my_editor, index+1, f"editor_test.log", test_spec_list_for_editor, extra_cmdline_args) + results = self._exec_editor_multitest(request, workspace, my_editor, index+1, + f"editor_test.log", test_spec_list_for_editor, + extra_cmdline_args) assert results is not None else: results = {} @@ -833,8 +1047,13 @@ class EditorTestSuite(): for result in results_per_thread: editor_test_data.results.update(result) - # Retrieves the number of parallel preference cmdline overrides def _get_number_parallel_editors(self, request): + # type(Request) -> int + """ + Retrieves the number of parallel preference cmdline overrides + :request: The Pytest Request + :return: The number of parallel editors to use + """ parallel_editors_value = request.config.getoption("--editors-parallel", None) if parallel_editors_value: return int(parallel_editors_value) diff --git a/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py b/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py index feff78d866..838f929cfa 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py @@ -3,6 +3,8 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. SPDX-License-Identifier: Apache-2.0 OR MIT + +Utility functions for the editor_test module """ import os @@ -15,6 +17,12 @@ import ly_test_tools.environment.waiter as waiter logger = logging.getLogger(__name__) def kill_all_ly_processes(include_asset_processor=True): + # type (bool) -> None + """ + Kills all common O3DE processes such as the Editor, Game Launchers, and Asset Processor. + :param include_asset_processor: Boolean flag whether or not to kill the AP + :return: None + """ LY_PROCESSES = [ 'Editor', 'Profiler', 'RemoteConsole', ] @@ -47,7 +55,8 @@ def get_module_filename(testcase_module): """ return os.path.splitext(os.path.basename(testcase_module.__file__))[0] -def retrieve_log_path(run_id : int, workspace): +def retrieve_log_path(run_id, workspace): + # type (int, ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager) -> str """ return the log/ project path for this test run. :param run_id: editor id that will be used for differentiating paths @@ -56,7 +65,8 @@ def retrieve_log_path(run_id : int, workspace): """ return os.path.join(workspace.paths.project(), "user", f"log_test_{run_id}") -def retrieve_crash_output(run_id : int, workspace, timeout : float): +def retrieve_crash_output(run_id, workspace, timeout): + # type (int, ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager, float) -> str """ returns the crash output string for the given test run. :param run_id: editor id that will be used for differentiating paths @@ -79,7 +89,8 @@ def retrieve_crash_output(run_id : int, workspace, timeout : float): crash_info += f"\n{str(ex)}" return crash_info -def cycle_crash_report(run_id : int, workspace): +def cycle_crash_report(run_id, workspace): + # type (int, ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager) -> None """ Attempts to rename error.log and error.dmp(crash files) into new names with the timestamp on it. :param run_id: editor id that will be used for differentiating paths @@ -99,10 +110,12 @@ def cycle_crash_report(run_id : int, workspace): except Exception as ex: logger.warning(f"Couldn't cycle file {filepath}. Error: {str(ex)}") -def retrieve_editor_log_content(run_id : int, log_name : str, workspace, timeout=10): +def retrieve_editor_log_content(run_id, log_name, workspace, timeout=10): + # type (int , str, ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager, int) -> str """ Retrieves the contents of the given editor log file. :param run_id: editor id that will be used for differentiating paths + :log_name: The name of the editor log to retrieve :param workspace: Workspace fixture :timeout: Maximum time to wait for the log file to appear :return str: The contents of the log @@ -124,7 +137,8 @@ def retrieve_editor_log_content(run_id : int, log_name : str, workspace, timeout editor_info = f"-- Error reading editor.log: {str(ex)} --" return editor_info -def retrieve_last_run_test_index_from_output(test_spec_list, output : str): +def retrieve_last_run_test_index_from_output(test_spec_list, output): + # type (list, str) -> int """ Finds out what was the last test that was run by inspecting the input. This is used for determining what was the batched test has crashed the editor diff --git a/Tools/LyTestTools/tests/unit/test_editor_test_utils.py b/Tools/LyTestTools/tests/unit/test_editor_test_utils.py new file mode 100644 index 0000000000..134d1e1ad8 --- /dev/null +++ b/Tools/LyTestTools/tests/unit/test_editor_test_utils.py @@ -0,0 +1,158 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +import pytest +import os +import unittest.mock as mock +import unittest + +import ly_test_tools.o3de.editor_test_utils as editor_test_utils + +pytestmark = pytest.mark.SUITE_smoke + +class TestEditorTestUtils(unittest.TestCase): + + @mock.patch('ly_test_tools.environment.process_utils.kill_processes_named') + def test_KillAllLyProcesses_IncludeAP_CallsCorrectly(self, under_test): + process_list = ['Editor', 'Profiler', 'RemoteConsole', 'AssetProcessor', 'AssetProcessorBatch', 'AssetBuilder'] + + editor_test_utils.kill_all_ly_processes(include_asset_processor=True) + under_test.assert_called_once_with(process_list, ignore_extensions=True) + + @mock.patch('ly_test_tools.environment.process_utils.kill_processes_named') + def test_KillAllLyProcesses_NotIncludeAP_CallsCorrectly(self, under_test): + process_list = ['Editor', 'Profiler', 'RemoteConsole'] + + editor_test_utils.kill_all_ly_processes(include_asset_processor=False) + under_test.assert_called_once_with(process_list, ignore_extensions=True) + + def test_GetTestcaseModuleFilepath_NoExtension_ReturnsPYExtension(self): + mock_module = mock.MagicMock() + file_path = os.path.join('path', 'under_test') + mock_module.__file__ = file_path + + assert file_path + '.py' == editor_test_utils.get_testcase_module_filepath(mock_module) + + def test_GetTestcaseModuleFilepath_PYExtension_ReturnsPYExtension(self): + mock_module = mock.MagicMock() + file_path = os.path.join('path', 'under_test.py') + mock_module.__file__ = file_path + + assert file_path == editor_test_utils.get_testcase_module_filepath(mock_module) + + def test_GetModuleFilename_PythonModule_ReturnsFilename(self): + mock_module = mock.MagicMock() + file_path = os.path.join('path', 'under_test.py') + mock_module.__file__ = file_path + + assert 'under_test' == editor_test_utils.get_module_filename(mock_module) + + def test_RetrieveLogPath_NormalProject_ReturnsLogPath(self): + mock_workspace = mock.MagicMock() + mock_workspace.paths.project.return_value = 'mock_project_path' + expected = os.path.join('mock_project_path', 'user', 'log_test_0') + + assert expected == editor_test_utils.retrieve_log_path(0, mock_workspace) + + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock()) + def test_RetrieveCrashOutput_CrashLogExists_ReturnsLogInfo(self, mock_retrieve_log_path): + mock_retrieve_log_path.return_value = 'mock_log_path' + mock_workspace = mock.MagicMock() + mock_log = 'mock crash info' + + with mock.patch('builtins.open', mock.mock_open(read_data=mock_log)) as mock_file: + assert mock_log == editor_test_utils.retrieve_crash_output(0, mock_workspace, 0) + + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock()) + def test_RetrieveCrashOutput_CrashLogNotExists_ReturnsError(self, mock_retrieve_log_path): + mock_retrieve_log_path.return_value = 'mock_log_path' + mock_workspace = mock.MagicMock() + expected = "-- No crash log available --\n[Errno 2] No such file or directory: 'mock_log_path\\\\error.log'" + + assert expected == editor_test_utils.retrieve_crash_output(0, mock_workspace, 0) + + @mock.patch('os.rename') + @mock.patch('os.path.getmtime') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('os.path.exists') + def test_CycleCrashReport_LogExists_NamedCorrectly(self, mock_exists, mock_retrieve_log_path, mock_getmtime, + under_test): + mock_exists.side_effect = [True, False] + mock_retrieve_log_path.return_value = 'mock_log_path' + mock_workspace = mock.MagicMock() + mock_getmtime.return_value = 1 + + editor_test_utils.cycle_crash_report(0, mock_workspace) + under_test.assert_called_once_with(os.path.join('mock_log_path', 'error.log'), + os.path.join('mock_log_path', 'error_1969_12_31_16_00_01.log')) + + @mock.patch('os.rename') + @mock.patch('os.path.getmtime') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('os.path.exists') + def test_CycleCrashReport_DmpExists_NamedCorrectly(self, mock_exists, mock_retrieve_log_path, mock_getmtime, + under_test): + mock_exists.side_effect = [False, True] + mock_retrieve_log_path.return_value = 'mock_log_path' + mock_workspace = mock.MagicMock() + mock_getmtime.return_value = 1 + + editor_test_utils.cycle_crash_report(0, mock_workspace) + under_test.assert_called_once_with(os.path.join('mock_log_path', 'error.dmp'), + os.path.join('mock_log_path', 'error_1969_12_31_16_00_01.dmp')) + + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock()) + def test_RetrieveEditorLogContent_CrashLogExists_ReturnsLogInfo(self, mock_retrieve_log_path): + mock_retrieve_log_path.return_value = 'mock_log_path' + mock_logname = 'mock_log.log' + mock_workspace = mock.MagicMock() + mock_log = 'mock log info' + + with mock.patch('builtins.open', mock.mock_open(read_data=mock_log)) as mock_file: + assert f'[editor.log] {mock_log}' == editor_test_utils.retrieve_editor_log_content(0, mock_logname, mock_workspace) + + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock()) + def test_RetrieveEditorLogContent_CrashLogNotExists_ReturnsError(self, mock_retrieve_log_path): + mock_retrieve_log_path.return_value = 'mock_log_path' + mock_logname = 'mock_log.log' + mock_workspace = mock.MagicMock() + expected = f"-- Error reading editor.log: [Errno 2] No such file or directory: 'mock_log_path\\\\mock_log.log' --" + + assert expected == editor_test_utils.retrieve_editor_log_content(0, mock_logname, mock_workspace) + + def test_RetrieveLastRunTestIndexFromOutput_SecondTestFailed_Returns0(self): + mock_test = mock.MagicMock() + mock_test.__name__ = 'mock_test_name' + mock_test_list = [mock_test] + mock_editor_output = 'mock_test_name\n' \ + 'mock_test_name_1' + + assert 0 == editor_test_utils.retrieve_last_run_test_index_from_output(mock_test_list, mock_editor_output) + + def test_RetrieveLastRunTestIndexFromOutput_TenthTestFailed_Returns9(self): + mock_test_list = [] + mock_editor_output = '' + for x in range(10): + mock_test = mock.MagicMock() + mock_test.__name__ = f'mock_test_name_{x}' + mock_test_list.append(mock_test) + mock_editor_output += f'{mock_test.__name__}\n' + mock_editor_output += 'mock_test_name_x' + assert 9 == editor_test_utils.retrieve_last_run_test_index_from_output(mock_test_list, mock_editor_output) + + def test_RetrieveLastRunTestIndexFromOutput_FirstItemFailed_Returns0(self): + mock_test_list = [] + mock_editor_output = '' + for x in range(10): + mock_test = mock.MagicMock() + mock_test.__name__ = f'mock_test_name_{x}' + mock_test_list.append(mock_test) + + assert 0 == editor_test_utils.retrieve_last_run_test_index_from_output(mock_test_list, mock_editor_output) diff --git a/Tools/LyTestTools/tests/unit/test_fixtures.py b/Tools/LyTestTools/tests/unit/test_fixtures.py index 32dc204bd2..911e6fef91 100755 --- a/Tools/LyTestTools/tests/unit/test_fixtures.py +++ b/Tools/LyTestTools/tests/unit/test_fixtures.py @@ -369,3 +369,14 @@ class TestFixtures(object): mock_request.addfinalizer.call_args[0][0]() mock_stop.assert_called_once() + + @mock.patch('inspect.isclass', mock.MagicMock(return_value=True)) + def test_PytestPycollectMakeitem_ValidArgs_CallsCorrectly(self): + mock_collector = mock.MagicMock() + mock_name = mock.MagicMock() + mock_obj = mock.MagicMock() + mock_base = mock.MagicMock() + mock_obj.__bases__ = [mock_base] + + test_tools_fixtures.pytest_pycollect_makeitem(mock_collector, mock_name, mock_obj) + mock_base.pytest_custom_makeitem.assert_called_once_with(mock_collector, mock_name, mock_obj) diff --git a/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py b/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py new file mode 100644 index 0000000000..ef49934887 --- /dev/null +++ b/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py @@ -0,0 +1,1017 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +import unittest + +import pytest +import unittest.mock as mock + +import ly_test_tools +import ly_test_tools.o3de.editor_test as editor_test + +pytestmark = pytest.mark.SUITE_smoke + +class TestEditorTestBase(unittest.TestCase): + + def test_EditorSharedTest_Init_CorrectAttributes(self): + mock_editorsharedtest = editor_test.EditorSharedTest() + assert mock_editorsharedtest.is_batchable == True + assert mock_editorsharedtest.is_parallelizable == True + + def test_EditorParallelTest_Init_CorrectAttributes(self): + mock_editorsharedtest = editor_test.EditorParallelTest() + assert mock_editorsharedtest.is_batchable == False + assert mock_editorsharedtest.is_parallelizable == True + + def test_EditorBatchedTest_Init_CorrectAttributes(self): + mock_editorsharedtest = editor_test.EditorBatchedTest() + assert mock_editorsharedtest.is_batchable == True + assert mock_editorsharedtest.is_parallelizable == False + +class TestBase(unittest.TestCase): + + def setUp(self): + self.mock_result = editor_test.Result.Base() + + def test_GetOutputStr_HasOutput_ReturnsCorrectly(self): + self.mock_result.output = 'expected output' + assert self.mock_result.get_output_str() == 'expected output' + + def test_GetOutputStr_NoOutput_ReturnsCorrectly(self): + self.mock_result.output = None + assert self.mock_result.get_output_str() == '-- No output --' + + def test_GetEditorLogStr_HasOutput_ReturnsCorrectly(self): + self.mock_result.editor_log = 'expected log output' + assert self.mock_result.get_editor_log_str() == 'expected log output' + + def test_GetEditorLogStr_NoOutput_ReturnsCorrectly(self): + self.mock_result.editor_log = None + assert self.mock_result.get_editor_log_str() == '-- No editor log found --' + +class TestPass(unittest.TestCase): + + def test_Create_ValidArgs_CorrectAttributes(self): + mock_test_spec = mock.MagicMock() + mock_output = mock.MagicMock() + mock_editor_log = mock.MagicMock() + + mock_pass = editor_test.Result.Pass.create(mock_test_spec, mock_output, mock_editor_log) + assert mock_pass.test_spec == mock_test_spec + assert mock_pass.output == mock_output + assert mock_pass.editor_log == mock_editor_log + + def test_Str_ValidString_ReturnsOutput(self): + mock_test_spec = mock.MagicMock() + mock_output = 'mock_output' + mock_editor_log = mock.MagicMock() + expected = f"Test Passed\n"\ + f"------------\n"\ + f"| Output |\n"\ + f"------------\n"\ + f"{mock_output}\n" + + mock_pass = editor_test.Result.Pass.create(mock_test_spec, mock_output, mock_editor_log) + assert str(mock_pass) == expected + +class TestFail(unittest.TestCase): + + def test_Create_ValidArgs_CorrectAttributes(self): + mock_test_spec = mock.MagicMock() + mock_output = mock.MagicMock() + mock_editor_log = mock.MagicMock() + + mock_pass = editor_test.Result.Fail.create(mock_test_spec, mock_output, mock_editor_log) + assert mock_pass.test_spec == mock_test_spec + assert mock_pass.output == mock_output + assert mock_pass.editor_log == mock_editor_log + + def test_Str_ValidString_ReturnsOutput(self): + mock_test_spec = mock.MagicMock() + mock_output = 'mock_output' + mock_editor_log = 'mock_editor_log' + expected = f"Test FAILED\n"\ + f"------------\n"\ + f"| Output |\n"\ + f"------------\n"\ + f"{mock_output}\n"\ + f"--------------\n"\ + f"| Editor log |\n"\ + f"--------------\n"\ + f"{mock_editor_log}\n" + + mock_pass = editor_test.Result.Fail.create(mock_test_spec, mock_output, mock_editor_log) + assert str(mock_pass) == expected + +class TestCrash(unittest.TestCase): + + def test_Create_ValidArgs_CorrectAttributes(self): + mock_test_spec = mock.MagicMock() + mock_output = mock.MagicMock() + mock_editor_log = mock.MagicMock() + mock_ret_code = mock.MagicMock() + mock_stacktrace = mock.MagicMock() + + mock_pass = editor_test.Result.Crash.create(mock_test_spec, mock_output, mock_ret_code, mock_stacktrace, + mock_editor_log) + assert mock_pass.test_spec == mock_test_spec + assert mock_pass.output == mock_output + assert mock_pass.editor_log == mock_editor_log + assert mock_pass.ret_code == mock_ret_code + assert mock_pass.stacktrace == mock_stacktrace + + def test_Str_ValidString_ReturnsOutput(self): + mock_test_spec = mock.MagicMock() + mock_output = 'mock_output' + mock_editor_log = 'mock_editor_log' + mock_return_code = 0 + mock_stacktrace = 'mock stacktrace' + expected = f"Test CRASHED, return code {hex(mock_return_code)}\n"\ + f"---------------\n"\ + f"| Stacktrace |\n"\ + f"---------------\n"\ + f"{mock_stacktrace}"\ + f"------------\n" \ + f"| Output |\n" \ + f"------------\n" \ + f"{mock_output}\n" \ + f"--------------\n" \ + f"| Editor log |\n" \ + f"--------------\n" \ + f"{mock_editor_log}\n" + + mock_pass = editor_test.Result.Crash.create(mock_test_spec, mock_output, mock_return_code, mock_stacktrace, + mock_editor_log) + assert str(mock_pass) == expected + + def test_Str_MissingStackTrace_ReturnsCorrectly(self): + mock_test_spec = mock.MagicMock() + mock_output = 'mock_output' + mock_editor_log = 'mock_editor_log' + mock_return_code = 0 + mock_stacktrace = None + expected = f"Test CRASHED, return code {hex(mock_return_code)}\n"\ + f"---------------\n"\ + f"| Stacktrace |\n"\ + f"---------------\n"\ + f"-- No stacktrace data found --\n"\ + f"------------\n" \ + f"| Output |\n" \ + f"------------\n" \ + f"{mock_output}\n" \ + f"--------------\n" \ + f"| Editor log |\n" \ + f"--------------\n" \ + f"{mock_editor_log}\n" + + mock_pass = editor_test.Result.Crash.create(mock_test_spec, mock_output, mock_return_code, mock_stacktrace, + mock_editor_log) + assert str(mock_pass) == expected + +class Timeout(unittest.TestCase): + + def test_Create_ValidArgs_CorrectAttributes(self): + mock_test_spec = mock.MagicMock() + mock_output = mock.MagicMock() + mock_editor_log = mock.MagicMock() + mock_timeout = mock.MagicMock() + + mock_pass = editor_test.Result.Timeout.create(mock_test_spec, mock_output, mock_timeout, mock_editor_log) + assert mock_pass.test_spec == mock_test_spec + assert mock_pass.output == mock_output + assert mock_pass.editor_log == mock_editor_log + assert mock_pass.time_secs == mock_timeout + + def test_Str_ValidString_ReturnsOutput(self): + mock_test_spec = mock.MagicMock() + mock_output = 'mock_output' + mock_editor_log = 'mock_editor_log' + mock_timeout = 0 + expected = f"Test TIMED OUT after {mock_timeout} seconds\n"\ + f"------------\n" \ + f"| Output |\n" \ + f"------------\n" \ + f"{mock_output}\n" \ + f"--------------\n" \ + f"| Editor log |\n" \ + f"--------------\n" \ + f"{mock_editor_log}\n" + + mock_pass = editor_test.Result.Timeout.create(mock_test_spec, mock_output, mock_timeout, mock_editor_log) + assert str(mock_pass) == expected + +class Unknown(unittest.TestCase): + + def test_Create_ValidArgs_CorrectAttributes(self): + mock_test_spec = mock.MagicMock() + mock_output = mock.MagicMock() + mock_editor_log = mock.MagicMock() + mock_extra_info = mock.MagicMock() + + mock_pass = editor_test.Result.Unknown.create(mock_test_spec, mock_output, mock_extra_info, mock_editor_log) + assert mock_pass.test_spec == mock_test_spec + assert mock_pass.output == mock_output + assert mock_pass.editor_log == mock_editor_log + assert mock_pass.extra_info == mock_extra_info + + def test_Str_ValidString_ReturnsOutput(self): + mock_test_spec = mock.MagicMock() + mock_output = 'mock_output' + mock_editor_log = 'mock_editor_log' + mock_extra_info = 'mock extra info' + expected = f"Unknown test result, possible cause: {mock_extra_info}\n"\ + f"------------\n" \ + f"| Output |\n" \ + f"------------\n" \ + f"{mock_output}\n" \ + f"--------------\n" \ + f"| Editor log |\n" \ + f"--------------\n" \ + f"{mock_editor_log}\n" + + mock_pass = editor_test.Result.Unknown.create(mock_test_spec, mock_output, mock_extra_info, mock_editor_log) + assert str(mock_pass) == expected + +class TestEditorTestSuite(unittest.TestCase): + + @mock.patch('ly_test_tools.o3de.editor_test_utils.kill_all_ly_processes') + def test_EditorTestData_ValidAP_TeardownProperly(self, mock_kill_processes): + mock_editor_test_suite = editor_test.EditorTestSuite() + mock_test_data_generator = mock_editor_test_suite._editor_test_data(mock.MagicMock()) + mock_asset_processor = mock.MagicMock() + for test_data in mock_test_data_generator: + test_data.asset_processor = mock_asset_processor + mock_asset_processor.stop.assert_called_once_with(1) + mock_asset_processor.teardown.assert_called() + assert test_data.asset_processor is None + mock_kill_processes.assert_called_once_with(include_asset_processor=True) + + @mock.patch('ly_test_tools.o3de.editor_test_utils.kill_all_ly_processes') + def test_EditorTestData_NoAP_TeardownProperly(self, mock_kill_processes): + mock_editor_test_suite = editor_test.EditorTestSuite() + mock_test_data_generator = mock_editor_test_suite._editor_test_data(mock.MagicMock()) + for test_data in mock_test_data_generator: + test_data.asset_processor = None + mock_kill_processes.assert_called_once_with(include_asset_processor=False) + + def test_RunnerInit_ValidArgs_InitProperly(self): + mock_name = mock.MagicMock() + mock_func = mock.MagicMock() + mock_tests = mock.MagicMock() + + mock_runner = editor_test.EditorTestSuite.Runner(mock_name, mock_func, mock_tests) + mock_runner.name = mock_name + mock_runner.func = mock_func + mock_runner.tests = mock_tests + mock_runner.run_pytestfunc = None + mock_runner.result_pytestfuncs = [] + + def test_PytestCustomMakeitem_Called_ReturnsClass(self): + mock_test_class = editor_test.EditorTestSuite.pytest_custom_makeitem(mock.MagicMock(), mock.MagicMock(), + mock.MagicMock()) + assert isinstance(mock_test_class, editor_test.EditorTestSuite.EditorTestClass) + + def test_PytestCustomModifyItems(self): + pass + + def test_GetSingleTests_NoSingleTests_EmptyList(self): + class MockTestSuite(editor_test.EditorTestSuite): + pass + mock_test_suite = MockTestSuite() + tests = mock_test_suite.get_single_tests() + assert len(tests) == 0 + + def test_GetSingleTests_OneSingleTests_ReturnsOne(self): + class MockTestSuite(editor_test.EditorTestSuite): + class MockSingleTest(editor_test.EditorSingleTest): + pass + mock_test_suite = MockTestSuite() + tests = mock_test_suite.get_single_tests() + assert len(tests) == 1 + + + def test_GetSingleTests_AllTests_ReturnsOnlySingles(self): + class MockTestSuite(editor_test.EditorTestSuite): + class MockSingleTest(editor_test.EditorSingleTest): + pass + class MockAnotherSingleTest(editor_test.EditorSingleTest): + pass + class MockNotSingleTest(editor_test.EditorSharedTest): + pass + mock_test_suite = MockTestSuite() + tests = mock_test_suite.get_single_tests() + assert len(tests) == 2 + + def test_GetSharedTests_NoSharedTests_EmptyList(self): + class MockTestSuite(editor_test.EditorTestSuite): + pass + mock_test_suite = MockTestSuite() + tests = mock_test_suite.get_shared_tests() + assert len(tests) == 0 + + def test_GetSharedTests_OneSharedTests_ReturnsOne(self): + class MockTestSuite(editor_test.EditorTestSuite): + class MockSharedTest(editor_test.EditorSharedTest): + pass + mock_test_suite = MockTestSuite() + tests = mock_test_suite.get_shared_tests() + assert len(tests) == 1 + + def test_GetSharedTests_AllTests_ReturnsOnlyShared(self): + class MockTestSuite(editor_test.EditorTestSuite): + class MockSharedTest(editor_test.EditorSharedTest): + pass + class MockAnotherSharedTest(editor_test.EditorSharedTest): + pass + class MockNotSharedTest(editor_test.EditorSingleTest): + pass + mock_test_suite = MockTestSuite() + tests = mock_test_suite.get_shared_tests() + assert len(tests) == 2 + + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite.filter_session_shared_tests') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite.get_shared_tests') + def test_GetSessionSharedTests_Valid_CallsCorrectly(self, mock_get_shared_tests, mock_filter_session): + editor_test.EditorTestSuite.get_session_shared_tests(mock.MagicMock()) + assert mock_get_shared_tests.called + assert mock_filter_session.called + + @mock.patch('ly_test_tools.o3de.editor_test.skipping_pytest_runtest_setup', mock.MagicMock()) + def test_FilterSessionSharedTests_OneSharedTest_ReturnsOne(self): + def mock_test(): + pass + mock_session_items = mock.MagicMock() + mock_shared_tests = mock.MagicMock() + mock_test.originalname = 'mock_test' + mock_test.__name__ = mock_test.originalname + mock_session_items = [mock_test] + mock_shared_tests = [mock_test] + + selected_tests = editor_test.EditorTestSuite.filter_session_shared_tests(mock_session_items, mock_shared_tests) + assert selected_tests == mock_session_items + + @mock.patch('ly_test_tools.o3de.editor_test.skipping_pytest_runtest_setup', mock.MagicMock()) + def test_FilterSessionSharedTests_ManyTests_ReturnsCorrectTests(self): + def mock_test(): + pass + def mock_test_2(): + pass + def mock_test_3(): + pass + mock_session_items = mock.MagicMock() + mock_shared_tests = mock.MagicMock() + mock_test.originalname = 'mock_test' + mock_test.__name__ = mock_test.originalname + mock_test_2.originalname = 'mock_test_2' + mock_test_2.__name__ = mock_test_2.originalname + mock_test_3.originalname = 'mock_test_3' + mock_test_3.__name__ = mock_test_3.originalname + mock_session_items = [mock_test, mock_test_2] + mock_shared_tests = [mock_test, mock_test_2, mock_test_3] + + selected_tests = editor_test.EditorTestSuite.filter_session_shared_tests(mock_session_items, mock_shared_tests) + assert selected_tests == mock_session_items + + @mock.patch('ly_test_tools.o3de.editor_test.skipping_pytest_runtest_setup', mock.MagicMock(side_effect=Exception)) + def test_FilterSessionSharedTests_SkippingPytestRaises_SkipsAddingTest(self): + def mock_test(): + pass + mock_session_items = mock.MagicMock() + mock_shared_tests = mock.MagicMock() + mock_test.originalname = 'mock_test' + mock_test.__name__ = mock_test.originalname + mock_session_items = [mock_test] + mock_shared_tests = [mock_test] + + selected_tests = editor_test.EditorTestSuite.filter_session_shared_tests(mock_session_items, mock_shared_tests) + assert len(selected_tests) == 0 + + def test_FilterSharedTests_TrueParams_ReturnsTrueTests(self): + mock_test = mock.MagicMock() + mock_test.is_batchable = True + mock_test.is_parallelizable = True + mock_test_2 = mock.MagicMock() + mock_test_2.is_batchable = False + mock_test_2.is_parallelizable = False + mock_shared_tests = [mock_test, mock_test_2] + + filtered_tests = editor_test.EditorTestSuite.filter_shared_tests(mock_shared_tests, True, True) + assert filtered_tests == [mock_test] + + def test_FilterSharedTests_FalseParams_ReturnsFalseTests(self): + mock_test = mock.MagicMock() + mock_test.is_batchable = True + mock_test.is_parallelizable = True + mock_test_2 = mock.MagicMock() + mock_test_2.is_batchable = False + mock_test_2.is_parallelizable = False + mock_shared_tests = [mock_test, mock_test_2] + + filtered_tests = editor_test.EditorTestSuite.filter_shared_tests(mock_shared_tests, False, False) + assert filtered_tests == [mock_test_2] + +class TestUtils(unittest.TestCase): + + @mock.patch('ly_test_tools.o3de.editor_test_utils.kill_all_ly_processes') + def test_PrepareAssetProcessor_APExists_StartsAP(self, mock_kill_processes): + mock_test_suite = editor_test.EditorTestSuite() + mock_workspace = mock.MagicMock() + mock_editor_data = mock.MagicMock() + mock_ap = mock.MagicMock() + mock_editor_data.asset_processor = mock_ap + + mock_test_suite._prepare_asset_processor(mock_workspace, mock_editor_data) + assert mock_ap.start.called + assert not mock_kill_processes.called + + @mock.patch('ly_test_tools.o3de.asset_processor.AssetProcessor.start') + @mock.patch('ly_test_tools.environment.process_utils.process_exists') + @mock.patch('ly_test_tools.o3de.editor_test_utils.kill_all_ly_processes') + def test_PrepareAssetProcessor_NoAP_KillAndCreateAP(self, mock_kill_processes, mock_proc_exists, mock_start): + mock_test_suite = editor_test.EditorTestSuite() + mock_workspace = mock.MagicMock() + mock_editor_data = mock.MagicMock() + mock_editor_data.asset_processor = None + mock_proc_exists.return_value = False + + mock_test_suite._prepare_asset_processor(mock_workspace, mock_editor_data) + mock_kill_processes.assert_called_with(include_asset_processor=True) + assert isinstance(mock_editor_data.asset_processor, ly_test_tools.o3de.asset_processor.AssetProcessor) + assert mock_start.called + + @mock.patch('ly_test_tools.o3de.asset_processor.AssetProcessor.start') + @mock.patch('ly_test_tools.environment.process_utils.process_exists') + @mock.patch('ly_test_tools.o3de.editor_test_utils.kill_all_ly_processes') + def test_PrepareAssetProcessor_NoAPButProcExists_NoKill(self, mock_kill_processes, mock_proc_exists, mock_start): + mock_test_suite = editor_test.EditorTestSuite() + mock_workspace = mock.MagicMock() + mock_editor_data = mock.MagicMock() + mock_editor_data.asset_processor = None + mock_proc_exists.return_value = True + + mock_test_suite._prepare_asset_processor(mock_workspace, mock_editor_data) + mock_kill_processes.assert_called_with(include_asset_processor=False) + assert not mock_start.called + assert mock_editor_data.asset_processor is None + + + @mock.patch('ly_test_tools.o3de.asset_processor.AssetProcessor.start') + @mock.patch('ly_test_tools.environment.process_utils.process_exists') + @mock.patch('ly_test_tools.o3de.editor_test_utils.kill_all_ly_processes') + def test_PrepareAssetProcessor_NoAPButProcExists_NoKill(self, mock_kill_processes, mock_proc_exists, mock_start): + mock_test_suite = editor_test.EditorTestSuite() + mock_workspace = mock.MagicMock() + mock_editor_data = mock.MagicMock() + mock_editor_data.asset_processor = None + mock_proc_exists.return_value = True + + mock_test_suite._prepare_asset_processor(mock_workspace, mock_editor_data) + mock_kill_processes.assert_called_with(include_asset_processor=False) + assert not mock_start.called + assert mock_editor_data.asset_processor is None + + @mock.patch('ly_test_tools.o3de.editor_test_utils.kill_all_ly_processes') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._prepare_asset_processor') + def test_SetupEditorTest_ValidArgs_CallsCorrectly(self, mock_prepare_ap, mock_kill_processes): + mock_test_suite = editor_test.EditorTestSuite() + mock_editor = mock.MagicMock() + mock_test_suite._setup_editor_test(mock_editor, mock.MagicMock(), mock.MagicMock()) + + assert mock_editor.configure_settings.called + assert mock_prepare_ap.called + mock_kill_processes.assert_called_once_with(include_asset_processor=False) + + @mock.patch('ly_test_tools.o3de.editor_test.Result.Pass.create') + @mock.patch('ly_test_tools.o3de.editor_test_utils.get_module_filename') + def test_GetResultsUsingOutput_ValidJsonSuccess_CreatesPassResult(self, mock_get_module, mock_create): + mock_get_module.return_value = 'mock_module_name' + mock_test_suite = editor_test.EditorTestSuite() + mock_test = mock.MagicMock() + mock_test.__name__ = 'mock_test_name' + mock_test_list = [mock_test] + mock_output = 'JSON_START(' \ + '{"name": "mock_module_name", "output": "mock_std_out", "success": "mock_success_data"}' \ + ')JSON_END' + mock_editor_log = 'JSON_START(' \ + ')JSON_END' + + results = mock_test_suite._get_results_using_output(mock_test_list, mock_output, mock_editor_log) + assert mock_create.called + assert len(results) == 1 + + @mock.patch('ly_test_tools.o3de.editor_test.Result.Fail.create') + @mock.patch('ly_test_tools.o3de.editor_test_utils.get_module_filename') + def test_GetResultsUsingOutput_ValidJsonFail_CreatesFailResult(self, mock_get_module, mock_create): + mock_get_module.return_value = 'mock_module_name' + mock_test_suite = editor_test.EditorTestSuite() + mock_test = mock.MagicMock() + mock_test.__name__ = 'mock_test_name' + mock_test_list = [mock_test] + mock_output = 'JSON_START(' \ + '{"name": "mock_module_name", "output": "mock_std_out", "failed": "mock_fail_data"}' \ + ')JSON_END' + mock_editor_log = 'JSON_START(' \ + ')JSON_END' + + results = mock_test_suite._get_results_using_output(mock_test_list, mock_output, mock_editor_log) + assert mock_create.called + assert len(results) == 1 + + @mock.patch('ly_test_tools.o3de.editor_test.Result.Unknown.create') + @mock.patch('ly_test_tools.o3de.editor_test_utils.get_module_filename') + def test_GetResultsUsingOutput_ModuleNotInLog_CreatesUnknownResult(self, mock_get_module, mock_create): + mock_get_module.return_value = 'different_module_name' + mock_test_suite = editor_test.EditorTestSuite() + mock_test = mock.MagicMock() + mock_test.__name__ = 'mock_test_name' + mock_test_list = [mock_test] + mock_output = 'JSON_START(' \ + '{"name": "mock_module_name", "output": "mock_std_out", "failed": "mock_fail_data"}' \ + ')JSON_END' + mock_editor_log = 'JSON_START(' \ + ')JSON_END' + + results = mock_test_suite._get_results_using_output(mock_test_list, mock_output, mock_editor_log) + assert mock_create.called + assert len(results) == 1 + + @mock.patch('ly_test_tools.o3de.editor_test.Result.Pass.create') + @mock.patch('ly_test_tools.o3de.editor_test.Result.Fail.create') + @mock.patch('ly_test_tools.o3de.editor_test.Result.Unknown.create') + @mock.patch('ly_test_tools.o3de.editor_test_utils.get_module_filename') + def test_GetResultsUsingOutput_MultipleTests_CreatesCorrectResults(self, mock_get_module, mock_create_unknown, + mock_create_fail, mock_create_pass): + mock_get_module.side_effect = ['mock_module_name_pass', 'mock_module_name_fail', 'different_module_name'] + mock_test_suite = editor_test.EditorTestSuite() + mock_test_pass = mock.MagicMock() + mock_test_pass.__name__ = 'mock_test_name_pass' + mock_test_fail = mock.MagicMock() + mock_test_fail.__name__ = 'mock_test_name_fail' + mock_test_unknown = mock.MagicMock() + mock_test_unknown.__name__ = 'mock_test_name_unknown' + mock_test_list = [mock_test_pass, mock_test_fail, mock_test_unknown] + mock_output = 'JSON_START(' \ + '{"name": "mock_module_name_pass", "output": "mock_std_out", "success": "mock_success_data"}' \ + ')JSON_END' \ + 'JSON_START(' \ + '{"name": "mock_module_name_fail", "output": "mock_std_out", "failed": "mock_fail_data"}' \ + ')JSON_END' \ + 'JSON_START(' \ + '{"name": "mock_module_name_unknown", "output": "mock_std_out", "failed": "mock_fail_data"}' \ + ')JSON_END' + mock_editor_log = 'JSON_START(' \ + '{"name": "mock_module_name_pass"}' \ + ')JSON_END' \ + 'JSON_START(' \ + '{"name": "mock_module_name_fail"}' \ + ')JSON_END' \ + + results = mock_test_suite._get_results_using_output(mock_test_list, mock_output, mock_editor_log) + mock_create_pass.assert_called_with( + mock_test_pass, 'mock_std_out', 'JSON_START({"name": "mock_module_name_pass"})JSON_END') + mock_create_fail.assert_called_with( + mock_test_fail, 'mock_std_out', 'JSON_START({"name": "mock_module_name_fail"})JSON_END') + mock_create_unknown.assert_called_with( + mock_test_unknown, mock_output, "Couldn't find any test run information on stdout", mock_editor_log) + assert len(results) == 3 + + @mock.patch('builtins.print') + def test_ReportResult_TestPassed_ReportsCorrectly(self, mock_print): + mock_test_name = 'mock name' + mock_pass = ly_test_tools.o3de.editor_test.Result.Pass() + ly_test_tools.o3de.editor_test.EditorTestSuite._report_result(mock_test_name, mock_pass) + mock_print.assert_called_with(f'Test {mock_test_name}:\nTest Passed\n------------\n| Output |\n------------\n' + f'-- No output --\n') + + @mock.patch('pytest.fail') + def test_ReportResult_TestFailed_FailsCorrectly(self, mock_pytest_fail): + mock_fail = ly_test_tools.o3de.editor_test.Result.Fail() + + ly_test_tools.o3de.editor_test.EditorTestSuite._report_result('mock_test_name', mock_fail) + mock_pytest_fail.assert_called_with('Test mock_test_name:\nTest FAILED\n------------\n| Output |' + '\n------------\n-- No output --\n--------------\n| Editor log |' + '\n--------------\n-- No editor log found --\n') + +class TestRunningTests(unittest.TestCase): + + @mock.patch('ly_test_tools.o3de.editor_test.Result.Pass.create') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_results_using_output') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_editor_log_content') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('ly_test_tools.o3de.editor_test_utils.get_testcase_module_filepath') + @mock.patch('ly_test_tools.o3de.editor_test_utils.cycle_crash_report') + def test_ExecEditorTest_TestSucceeds_ReturnsPass(self, mock_cycle_crash, mock_get_testcase_filepath, + mock_retrieve_log, mock_retrieve_editor_log, + mock_get_output_results, mock_create): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_workspace = mock.MagicMock() + mock_editor = mock.MagicMock() + mock_test_spec = mock.MagicMock() + mock_test_spec.__name__ = 'mock_test_name' + mock_editor.get_returncode.return_value = 0 + mock_get_output_results.return_value = {} + mock_pass = mock.MagicMock() + mock_create.return_value = mock_pass + + results = mock_test_suite._exec_editor_test(mock.MagicMock(), mock_workspace, mock_editor, 0, + 'mock_log_name', mock_test_spec, []) + assert mock_cycle_crash.called + assert mock_editor.start.called + assert mock_create.called + assert results == {mock_test_spec.__name__: mock_pass} + + @mock.patch('ly_test_tools.o3de.editor_test.Result.Fail.create') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_results_using_output') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_editor_log_content') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('ly_test_tools.o3de.editor_test_utils.get_testcase_module_filepath') + @mock.patch('ly_test_tools.o3de.editor_test_utils.cycle_crash_report') + def test_ExecEditorTest_TestFails_ReturnsFail(self, mock_cycle_crash, mock_get_testcase_filepath, + mock_retrieve_log, mock_retrieve_editor_log, + mock_get_output_results, mock_create): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_workspace = mock.MagicMock() + mock_editor = mock.MagicMock() + mock_test_spec = mock.MagicMock() + mock_test_spec.__name__ = 'mock_test_name' + mock_editor.get_returncode.return_value = 15 + mock_get_output_results.return_value = {} + mock_fail = mock.MagicMock() + mock_create.return_value = mock_fail + + results = mock_test_suite._exec_editor_test(mock.MagicMock(), mock_workspace, mock_editor, 0, + 'mock_log_name', mock_test_spec, []) + assert mock_cycle_crash.called + assert mock_editor.start.called + assert mock_create.called + assert results == {mock_test_spec.__name__: mock_fail} + + @mock.patch('ly_test_tools.o3de.editor_test.Result.Crash.create') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_crash_output') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_results_using_output') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_editor_log_content') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('ly_test_tools.o3de.editor_test_utils.get_testcase_module_filepath') + @mock.patch('ly_test_tools.o3de.editor_test_utils.cycle_crash_report') + def test_ExecEditorTest_TestCrashes_ReturnsCrash(self, mock_cycle_crash, mock_get_testcase_filepath, + mock_retrieve_log, mock_retrieve_editor_log, + mock_get_output_results, mock_retrieve_crash, mock_create): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_workspace = mock.MagicMock() + mock_editor = mock.MagicMock() + mock_test_spec = mock.MagicMock() + mock_test_spec.__name__ = 'mock_test_name' + mock_editor.get_returncode.return_value = 1 + mock_get_output_results.return_value = {} + mock_crash = mock.MagicMock() + mock_create.return_value = mock_crash + + results = mock_test_suite._exec_editor_test(mock.MagicMock(), mock_workspace, mock_editor, 0, + 'mock_log_name', mock_test_spec, []) + assert mock_cycle_crash.call_count == 2 + assert mock_editor.start.called + assert mock_retrieve_crash.called + assert mock_create.called + assert results == {mock_test_spec.__name__: mock_crash} + + @mock.patch('ly_test_tools.o3de.editor_test.Result.Timeout.create') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_results_using_output') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_editor_log_content') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('ly_test_tools.o3de.editor_test_utils.get_testcase_module_filepath') + @mock.patch('ly_test_tools.o3de.editor_test_utils.cycle_crash_report') + def test_ExecEditorTest_TestTimeout_ReturnsTimeout(self, mock_cycle_crash, mock_get_testcase_filepath, + mock_retrieve_log, mock_retrieve_editor_log, + mock_get_output_results, mock_create): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_workspace = mock.MagicMock() + mock_editor = mock.MagicMock() + mock_test_spec = mock.MagicMock() + mock_test_spec.__name__ = 'mock_test_name' + mock_editor.wait.side_effect = ly_test_tools.launchers.exceptions.WaitTimeoutError() + mock_get_output_results.return_value = {} + mock_timeout = mock.MagicMock() + mock_create.return_value = mock_timeout + + results = mock_test_suite._exec_editor_test(mock.MagicMock(), mock_workspace, mock_editor, 0, + 'mock_log_name', mock_test_spec, []) + assert mock_cycle_crash.called + assert mock_editor.start.called + assert mock_editor.kill.called + assert mock_create.called + assert results == {mock_test_spec.__name__: mock_timeout} + + @mock.patch('ly_test_tools.o3de.editor_test.Result.Pass.create') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_editor_log_content') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('ly_test_tools.o3de.editor_test_utils.get_testcase_module_filepath') + @mock.patch('ly_test_tools.o3de.editor_test_utils.cycle_crash_report') + def test_ExecEditorMultitest_AllTestsPass_ReturnsPasses(self, mock_cycle_crash, mock_get_testcase_filepath, + mock_retrieve_log, mock_retrieve_editor_log, mock_create): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_workspace = mock.MagicMock() + mock_editor = mock.MagicMock() + mock_editor.get_returncode.return_value = 0 + mock_test_spec = mock.MagicMock() + mock_test_spec.__name__ = 'mock_test_name' + mock_test_spec_2 = mock.MagicMock() + mock_test_spec_2.__name__ = 'mock_test_name_2' + mock_test_spec_list = [mock_test_spec, mock_test_spec_2] + mock_get_testcase_filepath.side_effect = ['mock_path', 'mock_path_2'] + mock_pass = mock.MagicMock() + mock_pass_2 = mock.MagicMock() + mock_create.side_effect = [mock_pass, mock_pass_2] + + results = mock_test_suite._exec_editor_multitest(mock.MagicMock(), mock_workspace, mock_editor, 0, + 'mock_log_name', mock_test_spec_list, []) + assert results == {mock_test_spec.__name__: mock_pass, mock_test_spec_2.__name__: mock_pass_2} + assert mock_cycle_crash.called + assert mock_create.call_count == 2 + + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_results_using_output') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_editor_log_content') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('ly_test_tools.o3de.editor_test_utils.get_testcase_module_filepath') + @mock.patch('ly_test_tools.o3de.editor_test_utils.cycle_crash_report') + def test_ExecEditorMultitest_OneFailure_CallsCorrectFunc(self, mock_cycle_crash, mock_get_testcase_filepath, + mock_retrieve_log, mock_retrieve_editor_log, mock_get_results): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_workspace = mock.MagicMock() + mock_editor = mock.MagicMock() + mock_editor.get_returncode.return_value = 15 + mock_test_spec = mock.MagicMock() + mock_test_spec_2 = mock.MagicMock() + mock_test_spec_list = [mock_test_spec, mock_test_spec_2] + mock_get_testcase_filepath.side_effect = ['mock_path', 'mock_path_2'] + mock_get_results.return_value = {'mock_test_name': mock.MagicMock(), 'mock_test_name_2': mock.MagicMock()} + + results = mock_test_suite._exec_editor_multitest(mock.MagicMock(), mock_workspace, mock_editor, 0, + 'mock_log_name', mock_test_spec_list, []) + assert mock_cycle_crash.called + assert mock_get_results.called + + @mock.patch('ly_test_tools.o3de.editor_test.Result.Crash.create') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_crash_output') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_results_using_output') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_editor_log_content') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('ly_test_tools.o3de.editor_test_utils.get_testcase_module_filepath') + @mock.patch('ly_test_tools.o3de.editor_test_utils.cycle_crash_report') + def test_ExecEditorMultitest_OneCrash_ReportsOnUnknownResult(self, mock_cycle_crash, mock_get_testcase_filepath, + mock_retrieve_log, mock_retrieve_editor_log, + mock_get_results, mock_retrieve_crash, mock_create): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_workspace = mock.MagicMock() + mock_editor = mock.MagicMock() + mock_editor.get_returncode.return_value = 1 + mock_test_spec = mock.MagicMock() + mock_test_spec.__name__ = 'mock_test_name' + mock_test_spec_2 = mock.MagicMock() + mock_test_spec_2.__name__ = 'mock_test_name_2' + mock_test_spec_list = [mock_test_spec, mock_test_spec_2] + mock_unknown_result = ly_test_tools.o3de.editor_test.Result.Unknown() + mock_unknown_result.test_spec = mock.MagicMock() + mock_unknown_result.editor_log = mock.MagicMock() + mock_get_testcase_filepath.side_effect = ['mock_path', 'mock_path_2'] + mock_get_results.return_value = {mock_test_spec.__name__: mock_unknown_result, + mock_test_spec_2.__name__: mock.MagicMock()} + mock_crash = mock.MagicMock() + mock_create.return_value = mock_crash + + results = mock_test_suite._exec_editor_multitest(mock.MagicMock(), mock_workspace, mock_editor, 0, + 'mock_log_name', mock_test_spec_list, []) + assert mock_cycle_crash.call_count == 2 + assert mock_get_results.called + assert results[mock_test_spec.__name__] == mock_crash + + @mock.patch('ly_test_tools.o3de.editor_test.Result.Crash.create') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_crash_output') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_results_using_output') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_editor_log_content') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('ly_test_tools.o3de.editor_test_utils.get_testcase_module_filepath') + @mock.patch('ly_test_tools.o3de.editor_test_utils.cycle_crash_report') + def test_ExecEditorMultitest_ManyUnknown_ReportsUnknownResults(self, mock_cycle_crash, mock_get_testcase_filepath, + mock_retrieve_log, mock_retrieve_editor_log, + mock_get_results, mock_retrieve_crash, mock_create): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_workspace = mock.MagicMock() + mock_editor = mock.MagicMock() + mock_editor.get_returncode.return_value = 1 + mock_test_spec = mock.MagicMock() + mock_test_spec.__name__ = 'mock_test_name' + mock_test_spec_2 = mock.MagicMock() + mock_test_spec_2.__name__ = 'mock_test_name_2' + mock_test_spec_list = [mock_test_spec, mock_test_spec_2] + mock_unknown_result = ly_test_tools.o3de.editor_test.Result.Unknown() + mock_unknown_result.__name__ = 'mock_test_name' + mock_unknown_result.test_spec = mock.MagicMock() + mock_unknown_result.test_spec.__name__ = 'mock_test_spec_name' + mock_unknown_result.editor_log = mock.MagicMock() + mock_get_testcase_filepath.side_effect = ['mock_path', 'mock_path_2'] + mock_get_results.return_value = {mock_test_spec.__name__: mock_unknown_result, + mock_test_spec_2.__name__: mock_unknown_result} + mock_crash = mock.MagicMock() + mock_create.return_value = mock_crash + + results = mock_test_suite._exec_editor_multitest(mock.MagicMock(), mock_workspace, mock_editor, 0, + 'mock_log_name', mock_test_spec_list, []) + assert mock_cycle_crash.call_count == 2 + assert mock_get_results.called + assert results[mock_test_spec.__name__] == mock_crash + assert results[mock_test_spec_2.__name__].extra_info + + @mock.patch('ly_test_tools.o3de.editor_test.Result.Timeout.create') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_results_using_output') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_editor_log_content') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('ly_test_tools.o3de.editor_test_utils.get_testcase_module_filepath') + @mock.patch('ly_test_tools.o3de.editor_test_utils.cycle_crash_report') + def test_ExecEditorMultitest_EditorTimeout_ReportsCorrectly(self, mock_cycle_crash, mock_get_testcase_filepath, + mock_retrieve_log, mock_retrieve_editor_log, + mock_get_results, mock_create): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_workspace = mock.MagicMock() + mock_editor = mock.MagicMock() + mock_editor.wait.side_effect = ly_test_tools.launchers.exceptions.WaitTimeoutError() + mock_test_spec = mock.MagicMock() + mock_test_spec.__name__ = 'mock_test_name' + mock_test_spec_2 = mock.MagicMock() + mock_test_spec_2.__name__ = 'mock_test_name_2' + mock_test_spec_list = [mock_test_spec, mock_test_spec_2] + mock_unknown_result = ly_test_tools.o3de.editor_test.Result.Unknown() + mock_unknown_result.test_spec = mock.MagicMock() + mock_unknown_result.test_spec.__name__ = 'mock_test_spec_name' + mock_unknown_result.output = mock.MagicMock() + mock_unknown_result.editor_log = mock.MagicMock() + mock_get_testcase_filepath.side_effect = ['mock_path', 'mock_path_2'] + mock_get_results.return_value = {mock_test_spec.__name__: mock_unknown_result, + mock_test_spec_2.__name__: mock_unknown_result} + mock_timeout = mock.MagicMock() + mock_create.return_value = mock_timeout + + results = mock_test_suite._exec_editor_multitest(mock.MagicMock(), mock_workspace, mock_editor, 0, + 'mock_log_name', mock_test_spec_list, []) + assert mock_cycle_crash.called + assert mock_get_results.called + assert results[mock_test_spec_2.__name__].extra_info + assert results[mock_test_spec.__name__] == mock_timeout + + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._report_result') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._exec_editor_test') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test') + def test_RunSingleTest_ValidTest_ReportsResults(self, mock_setup_test, mock_exec_editor_test, mock_report_result): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_test_data = mock.MagicMock() + mock_test_spec = mock.MagicMock() + mock_result = mock.MagicMock() + mock_test_name = 'mock_test_result' + mock_exec_editor_test.return_value = {mock_test_name: mock_result} + + mock_test_suite._run_single_test(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), mock_test_data, + mock_test_spec) + + assert mock_setup_test.called + assert mock_exec_editor_test.called + assert mock_test_data.results.update.called + mock_report_result.assert_called_with(mock_test_name, mock_result) + + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._exec_editor_multitest') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test') + def test_RunBatchedTests_ValidTests_CallsCorrectly(self, mock_setup_test, mock_exec_multitest): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_test_data = mock.MagicMock() + + mock_test_suite._run_batched_tests(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), mock_test_data, + mock.MagicMock(), []) + + assert mock_setup_test.called + assert mock_exec_multitest.called + assert mock_test_data.results.update.called + + @mock.patch('threading.Thread') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test') + def test_RunParallelTests_TwoTestsAndEditors_TwoThreads(self, mock_setup_test, mock_get_num_editors, mock_thread): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_get_num_editors.return_value = 2 + mock_test_spec_list = [mock.MagicMock(), mock.MagicMock()] + mock_test_data = mock.MagicMock() + + mock_test_suite._run_parallel_tests(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), mock_test_data, + mock_test_spec_list, []) + + assert mock_setup_test.called + assert mock_test_data.results.update.call_count == len(mock_test_spec_list) + assert mock_thread.call_count == len(mock_test_spec_list) + + @mock.patch('threading.Thread') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test') + def test_RunParallelTests_TenTestsAndTwoEditors_TenThreads(self, mock_setup_test, mock_get_num_editors, mock_thread): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_get_num_editors.return_value = 2 + mock_test_spec_list = [] + for i in range(10): + mock_test_spec_list.append(mock.MagicMock()) + mock_test_data = mock.MagicMock() + + mock_test_suite._run_parallel_tests(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), mock_test_data, + mock_test_spec_list, []) + + assert mock_setup_test.called + assert mock_test_data.results.update.call_count == len(mock_test_spec_list) + assert mock_thread.call_count == len(mock_test_spec_list) + + @mock.patch('threading.Thread') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test') + def test_RunParallelTests_TenTestsAndThreeEditors_TenThreads(self, mock_setup_test, mock_get_num_editors, + mock_thread): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_get_num_editors.return_value = 3 + mock_test_spec_list = [] + for i in range(10): + mock_test_spec_list.append(mock.MagicMock()) + mock_test_data = mock.MagicMock() + + mock_test_suite._run_parallel_tests(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), mock_test_data, + mock_test_spec_list, []) + + assert mock_setup_test.called + assert mock_test_data.results.update.call_count == len(mock_test_spec_list) + assert mock_thread.call_count == len(mock_test_spec_list) + + @mock.patch('threading.Thread') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test') + def test_RunParallelBatchedTests_TwoTestsAndEditors_TwoThreads(self, mock_setup_test, mock_get_num_editors, + mock_thread): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_get_num_editors.return_value = 2 + mock_test_spec_list = [mock.MagicMock(), mock.MagicMock()] + mock_test_data = mock.MagicMock() + + mock_test_suite._run_parallel_tests(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), mock_test_data, + mock_test_spec_list, []) + + assert mock_setup_test.called + assert mock_test_data.results.update.call_count == len(mock_test_spec_list) + assert mock_thread.call_count == len(mock_test_spec_list) + + @mock.patch('threading.Thread') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test') + def test_RunParallelBatchedTests_TenTestsAndTwoEditors_TenThreads(self, mock_setup_test, mock_get_num_editors, + mock_thread): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_get_num_editors.return_value = 2 + mock_test_spec_list = [] + for i in range(10): + mock_test_spec_list.append(mock.MagicMock()) + mock_test_data = mock.MagicMock() + + mock_test_suite._run_parallel_tests(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), mock_test_data, + mock_test_spec_list, []) + + assert mock_setup_test.called + assert mock_test_data.results.update.call_count == len(mock_test_spec_list) + assert mock_thread.call_count == len(mock_test_spec_list) + + @mock.patch('threading.Thread') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors') + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test') + def test_RunParallelBatchedTests_TenTestsAndThreeEditors_TenThreads(self, mock_setup_test, mock_get_num_editors, + mock_thread): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_get_num_editors.return_value = 3 + mock_test_spec_list = [] + for i in range(10): + mock_test_spec_list.append(mock.MagicMock()) + mock_test_data = mock.MagicMock() + + mock_test_suite._run_parallel_tests(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), mock_test_data, + mock_test_spec_list, []) + + assert mock_setup_test.called + assert mock_test_data.results.update.call_count == len(mock_test_spec_list) + assert mock_thread.call_count == len(mock_test_spec_list) + + def test_GetNumberParallelEditors_ConfigExists_ReturnsConfig(self): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_request = mock.MagicMock() + mock_request.config.getoption.return_value = 1 + + num_of_editors = mock_test_suite._get_number_parallel_editors(mock_request) + assert num_of_editors == 1 + + def test_GetNumberParallelEditors_ConfigNotExists_ReturnsDefault(self): + mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() + mock_request = mock.MagicMock() + mock_request.config.getoption.return_value = None + + num_of_editors = mock_test_suite._get_number_parallel_editors(mock_request) + assert num_of_editors == mock_test_suite.get_number_parallel_editors() \ No newline at end of file diff --git a/Tools/LyTestTools/tests/unit/test_pytest_plugin_editor_test.py b/Tools/LyTestTools/tests/unit/test_pytest_plugin_editor_test.py new file mode 100644 index 0000000000..968c07aca6 --- /dev/null +++ b/Tools/LyTestTools/tests/unit/test_pytest_plugin_editor_test.py @@ -0,0 +1,41 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +import pytest +import os +import unittest.mock as mock +import unittest + +import ly_test_tools._internal.pytest_plugin.editor_test as editor_test + +pytestmark = pytest.mark.SUITE_smoke + +class TestEditorTest(unittest.TestCase): + + @mock.patch('inspect.isclass', mock.MagicMock(return_value=True)) + def test_PytestPycollectMakeitem_ValidArgs_CallsCorrectly(self): + mock_collector = mock.MagicMock() + mock_name = mock.MagicMock() + mock_obj = mock.MagicMock() + mock_base = mock.MagicMock() + mock_obj.__bases__ = [mock_base] + + editor_test.pytest_pycollect_makeitem(mock_collector, mock_name, mock_obj) + mock_base.pytest_custom_makeitem.assert_called_once_with(mock_collector, mock_name, mock_obj) + + def test_PytestCollectionModifyitem_OneValidClass_CallsOnce(self): + mock_item = mock.MagicMock() + mock_class = mock.MagicMock() + mock_class.pytest_custom_modify_items = mock.MagicMock() + mock_item.instance.__class__ = mock_class + mock_session = mock.MagicMock() + mock_items = [mock_item, mock.MagicMock()] + mock_config = mock.MagicMock() + + generator = editor_test.pytest_collection_modifyitems(mock_session, mock_items, mock_config) + for x in generator: + pass + assert mock_class.pytest_custom_modify_items.call_count == 1 \ No newline at end of file From f12d7626f841e69d2bbe7a4c0ee20f195f720b4f Mon Sep 17 00:00:00 2001 From: evanchia Date: Wed, 13 Oct 2021 16:22:26 -0700 Subject: [PATCH 007/120] Finishing up unit tests Signed-off-by: evanchia --- .../tests/unit/test_o3de_editor_test.py | 122 ++++++++++++++++-- 1 file changed, 109 insertions(+), 13 deletions(-) diff --git a/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py b/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py index ef49934887..05d1ebc305 100644 --- a/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py +++ b/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py @@ -955,8 +955,8 @@ class TestRunningTests(unittest.TestCase): mock_test_spec_list = [mock.MagicMock(), mock.MagicMock()] mock_test_data = mock.MagicMock() - mock_test_suite._run_parallel_tests(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), mock_test_data, - mock_test_spec_list, []) + mock_test_suite._run_parallel_batched_tests(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), + mock_test_data, mock_test_spec_list, []) assert mock_setup_test.called assert mock_test_data.results.update.call_count == len(mock_test_spec_list) @@ -965,7 +965,7 @@ class TestRunningTests(unittest.TestCase): @mock.patch('threading.Thread') @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors') @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test') - def test_RunParallelBatchedTests_TenTestsAndTwoEditors_TenThreads(self, mock_setup_test, mock_get_num_editors, + def test_RunParallelBatchedTests_TenTestsAndTwoEditors_2Threads(self, mock_setup_test, mock_get_num_editors, mock_thread): mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() mock_get_num_editors.return_value = 2 @@ -974,17 +974,17 @@ class TestRunningTests(unittest.TestCase): mock_test_spec_list.append(mock.MagicMock()) mock_test_data = mock.MagicMock() - mock_test_suite._run_parallel_tests(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), mock_test_data, - mock_test_spec_list, []) + mock_test_suite._run_parallel_batched_tests(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), + mock_test_data, mock_test_spec_list, []) assert mock_setup_test.called - assert mock_test_data.results.update.call_count == len(mock_test_spec_list) - assert mock_thread.call_count == len(mock_test_spec_list) + assert mock_test_data.results.update.call_count == 2 + assert mock_thread.call_count == 2 @mock.patch('threading.Thread') @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors') @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test') - def test_RunParallelBatchedTests_TenTestsAndThreeEditors_TenThreads(self, mock_setup_test, mock_get_num_editors, + def test_RunParallelBatchedTests_TenTestsAndThreeEditors_ThreeThreads(self, mock_setup_test, mock_get_num_editors, mock_thread): mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() mock_get_num_editors.return_value = 3 @@ -993,12 +993,12 @@ class TestRunningTests(unittest.TestCase): mock_test_spec_list.append(mock.MagicMock()) mock_test_data = mock.MagicMock() - mock_test_suite._run_parallel_tests(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), mock_test_data, - mock_test_spec_list, []) + mock_test_suite._run_parallel_batched_tests(mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), + mock_test_data, mock_test_spec_list, []) assert mock_setup_test.called - assert mock_test_data.results.update.call_count == len(mock_test_spec_list) - assert mock_thread.call_count == len(mock_test_spec_list) + assert mock_test_data.results.update.call_count == 3 + assert mock_thread.call_count == 3 def test_GetNumberParallelEditors_ConfigExists_ReturnsConfig(self): mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite() @@ -1014,4 +1014,100 @@ class TestRunningTests(unittest.TestCase): mock_request.config.getoption.return_value = None num_of_editors = mock_test_suite._get_number_parallel_editors(mock_request) - assert num_of_editors == mock_test_suite.get_number_parallel_editors() \ No newline at end of file + assert num_of_editors == mock_test_suite.get_number_parallel_editors() + +@mock.patch('_pytest.python.Class.collect') +class TestEditorTestClass(unittest.TestCase): + + def setUp(self): + mock_name = mock.MagicMock() + mock_collector = mock.MagicMock() + self.mock_test_class = ly_test_tools.o3de.editor_test.EditorTestSuite.EditorTestClass(mock_name, mock_collector) + self.mock_test_class.obj = mock.MagicMock() + single_1 = mock.MagicMock() + single_1.__name__ = 'single_1_name' + single_2 = mock.MagicMock() + single_2.__name__ = 'single_2_name' + self.mock_test_class.obj.get_single_tests.return_value = [single_1, single_2] + batch_1 = mock.MagicMock() + batch_1.__name__ = 'batch_1_name' + batch_2 = mock.MagicMock() + batch_2.__name__ = 'batch_2_name' + parallel_1 = mock.MagicMock() + parallel_1.__name__ = 'parallel_1_name' + parallel_2 = mock.MagicMock() + parallel_2.__name__ = 'parallel_2_name' + both_1 = mock.MagicMock() + both_1.__name__ = 'both_1_name' + both_2 = mock.MagicMock() + both_2.__name__ = 'both_2_name' + self.mock_test_class.obj.filter_shared_tests.side_effect = [ [batch_1, batch_2], + [parallel_1, parallel_2], + [both_1, both_2] ] + + def test_Collect_NoParallelNoBatched_RunsAsSingleTests(self, mock_collect): + self.mock_test_class.config.getoption.return_value = True + self.mock_test_class.collect() + assert self.mock_test_class.obj.single_1_name.__name__ == 'single_run' + assert self.mock_test_class.obj.single_2_name.__name__ == 'single_run' + assert self.mock_test_class.obj.batch_1_name.__name__ == 'single_run' + assert self.mock_test_class.obj.batch_2_name.__name__ == 'single_run' + assert self.mock_test_class.obj.parallel_1_name.__name__ == 'single_run' + assert self.mock_test_class.obj.parallel_2_name.__name__ == 'single_run' + assert self.mock_test_class.obj.both_1_name.__name__ == 'single_run' + assert self.mock_test_class.obj.both_2_name.__name__ == 'single_run' + + def test_Collect_AllValidTests_RunsAsInteded(self, mock_collect): + self.mock_test_class.config.getoption.return_value = False + self.mock_test_class.collect() + assert self.mock_test_class.obj.single_1_name.__name__ == 'single_run' + assert self.mock_test_class.obj.single_2_name.__name__ == 'single_run' + assert self.mock_test_class.obj.batch_1_name.__name__ == 'result' + assert self.mock_test_class.obj.batch_2_name.__name__ == 'result' + assert self.mock_test_class.obj.parallel_1_name.__name__ == 'result' + assert self.mock_test_class.obj.parallel_2_name.__name__ == 'result' + assert self.mock_test_class.obj.both_1_name.__name__ == 'result' + assert self.mock_test_class.obj.both_2_name.__name__ == 'result' + + def test_Collect_AllValidTests_CallsCollect(self, mock_collect): + self.mock_test_class.collect() + assert mock_collect.called + + def test_Collect_NormalCollection_ReturnsFilteredRuns(self, mock_collect): + mock_run = mock.MagicMock() + mock_run.obj.marks = {"run_type": 'run_shared'} + mock_run_2 = mock.MagicMock() + mock_run_2.obj.marks = {"run_type": 'result'} + mock_instance = mock.MagicMock() + mock_instance.collect.return_value = [mock_run, mock_run_2] + mock_collect.return_value = [mock_instance] + + collection = self.mock_test_class.collect() + assert collection == [mock_run_2] + + def test_Collect_NormalRun_ReturnsRunners(self, mock_collect): + self.mock_test_class.collect() + runners = self.mock_test_class.obj._runners + + assert runners[0].name == 'run_batched_tests' + assert runners[1].name == 'run_parallel_tests' + assert runners[2].name == 'run_parallel_batched_tests' + + def test_Collect_NormalCollection_StoresRunners(self, mock_collect): + mock_runner = mock.MagicMock() + mock_run = mock.MagicMock() + mock_run.obj.marks = {"run_type": 'run_shared'} + mock_run.function.marks = {"runner": mock_runner} + mock_runner_2 = mock.MagicMock() + mock_runner_2.result_pytestfuncs = [] + mock_run_2 = mock.MagicMock() + mock_run_2.obj.marks = {"run_type": 'result'} + mock_run_2.function.marks = {"runner": mock_runner_2} + mock_instance = mock.MagicMock() + mock_instance.collect.return_value = [mock_run, mock_run_2] + mock_collect.return_value = [mock_instance] + + self.mock_test_class.collect() + + assert mock_runner.run_pytestfunc == mock_run + assert mock_run_2 in mock_runner_2.result_pytestfuncs From 84493760caa0718c46789d6bcc4ec60fd5f5e385 Mon Sep 17 00:00:00 2001 From: evanchia Date: Mon, 18 Oct 2021 13:27:40 -0700 Subject: [PATCH 008/120] addressing PR feedback Signed-off-by: evanchia --- .../_internal/pytest_plugin/editor_test.py | 11 +- .../pytest_plugin/test_tools_fixtures.py | 16 -- .../ly_test_tools/o3de/editor_test.py | 55 +++---- .../ly_test_tools/o3de/editor_test_utils.py | 9 +- .../tests/unit/test_editor_test_utils.py | 26 +-- Tools/LyTestTools/tests/unit/test_fixtures.py | 11 -- .../tests/unit/test_o3de_editor_test.py | 149 ++++++++---------- .../unit/test_pytest_plugin_editor_test.py | 2 +- 8 files changed, 118 insertions(+), 161 deletions(-) diff --git a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/editor_test.py b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/editor_test.py index 2df5810047..19caf7e090 100644 --- a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/editor_test.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/editor_test.py @@ -4,7 +4,8 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT -Utility for specifying an Editor test, supports seamless parallelization and/or batching of tests. +Utility for specifying an Editor test, supports seamless parallelization and/or batching of tests. This is not a set of +tools to directly invoke, but a plugin with functions intended to be called by only the Pytest framework. """ import pytest @@ -15,7 +16,7 @@ __test__ = False def pytest_addoption(parser): # type (argparse.ArgumentParser) -> None """ - Options when running tests in batches or parallel. + Options when running editor tests in batches or parallel. :param parser: The ArgumentParser object :return: None """ @@ -27,8 +28,8 @@ def pytest_pycollect_makeitem(collector, name, obj): # type (PyCollector, str, object) -> Collector """ Create a custom custom item collection if the class defines pytest_custom_makeitem function. This is used for - automtically generating test functions with a custom collector. - :param collector: The Python test collector + automatically generating test functions with a custom collector. + :param collector: The Pytest collector :param name: Name of the collector :param obj: The custom collector, normally an EditorTestSuite.EditorTestClass object :return: Returns the custom collector @@ -40,7 +41,7 @@ def pytest_pycollect_makeitem(collector, name, obj): @pytest.hookimpl(hookwrapper=True) def pytest_collection_modifyitems(session, items, config): - # type (Session, list, Config) -> None + # type (Session, List[EditorTestBase], Config) -> None """ Add custom modification of items. This is used for adding the runners into the item list. :param session: The Pytest Session diff --git a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py index 3115c28406..af29064766 100755 --- a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py @@ -55,22 +55,6 @@ def pytest_configure(config): ly_test_tools._internal.pytest_plugin.build_directory = _get_build_directory(config) ly_test_tools._internal.pytest_plugin.output_path = _get_output_path(config) -def pytest_pycollect_makeitem(collector, name, obj): - # type (PyCollector, str, object) -> Collector - """ - Create a custom custom item collection if the class defines pytest_custom_makeitem function. This is used for - automtically generating test functions with a custom collector. - :param collector: The Python test collector - :param name: Name of the collector - :param obj: The custom collector, normally an EditorTestSuite.EditorTestClass object - :return: Returns the custom collector - """ - import inspect - if inspect.isclass(obj): - for base in obj.__bases__: - if hasattr(base, "pytest_custom_makeitem"): - return base.pytest_custom_makeitem(collector, name, obj) - def _get_build_directory(config): """ Fetch and verify the cmake build directory CLI arg, without creating an error when unset diff --git a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py index 39413c22a3..789d07f200 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py @@ -321,8 +321,9 @@ class EditorTestSuite(): def editor_test_data(self, request): # type (request) -> TestData """ - Yields a generator to capture the test results and an AssetProcessor object. - :request: The pytest request + Yields a per-testsuite structure to store the data of each test result and an AssetProcessor object that will be + re-used on the whole suite + :request: The Pytest request :yield: The TestData object """ self._editor_test_data(request) @@ -513,12 +514,15 @@ class EditorTestSuite(): @classmethod def pytest_custom_modify_items(cls, session, items, config): - # type () -> None + # type (Session, List[EditorTestBase], Config) -> None """ - + Adds the runners' functions and filters the tests that will run. The runners will be added if they have any + selected tests + :param session: The Pytest Session + :param items: The test case functions + :param config: The Pytest Config object + :return: None """ - # Add here the runners functions and filter the tests that will be run. - # The runners will be added if they have any selected tests new_items = [] for runner in cls._runners: runner.tests[:] = cls.filter_session_shared_tests(items, runner.tests) @@ -535,7 +539,7 @@ class EditorTestSuite(): @classmethod def get_single_tests(cls): - # type () -> list + # type () -> List """ Grabs all of the EditorSingleTests subclassed tests from the EditorTestSuite class Usage example: @@ -549,7 +553,7 @@ class EditorTestSuite(): @classmethod def get_shared_tests(cls): - # type () -> list + # type () -> List """ Grabs all of the EditorSharedTests from the EditorTestSuite Usage example: @@ -563,7 +567,7 @@ class EditorTestSuite(): @classmethod def get_session_shared_tests(cls, session): - # type (Session) -> list[EditorTestBase] + # type (Session) -> List[EditorTestBase] """ Filters and returns all of the shared tests in a given session. :session: The test session @@ -574,7 +578,7 @@ class EditorTestSuite(): @staticmethod def filter_session_shared_tests(session_items, shared_tests): - # type (list, list) -> list[EditorTestBase] + # type (List[EditorTestBase, List[EditorSharedTest]) -> List[EditorTestBase] """ Retrieve the test sub-set that was collected this can be less than the original set if were overriden via -k argument or similars @@ -596,7 +600,7 @@ class EditorTestSuite(): @staticmethod def filter_shared_tests(shared_tests, is_batchable=False, is_parallelizable=False): - # type (list, bool, bool) -> list + # type (List[EditorSharedTest], bool, bool) -> List[EditorSharedTest] """ Filters and returns all tests based off of if they are batchable and/or parallelizable :shared_tests: All shared tests @@ -654,7 +658,7 @@ class EditorTestSuite(): @staticmethod def _get_results_using_output(test_spec_list, output, editor_log_content): - # type(list, str, str) -> dict{str: Result} + # type(List[EditorTestBase], str, str) -> dict{str: Result} """ Utility function for parsing the output information from the editor. It deserializes the JSON content printed in the output for every test and returns that information. @@ -732,7 +736,7 @@ class EditorTestSuite(): ### Running tests ### def _exec_editor_test(self, request, workspace, editor, run_id, log_name, test_spec, cmdline_args = []): - # type (Request, AbstractWorkspace, Editor, int, str, EditorTestBase, list[str] -> dict{str: Result} + # type (Request, AbstractWorkspace, Editor, int, str, EditorTestBase, List[str] -> dict{str: Result} """ Starts the editor with the given test and retuns an result dict with a single element specifying the result :request: The pytest request @@ -794,7 +798,7 @@ class EditorTestSuite(): return results def _exec_editor_multitest(self, request, workspace, editor, run_id, log_name, test_spec_list, cmdline_args=[]): - # type (Request, AbstractWorkspace, Editor, int, str, list[EditorTestBase], list[str]) -> dict{str: Result} + # type (Request, AbstractWorkspace, Editor, int, str, List[EditorTestBase], List[str]) -> dict{str: Result} """ Starts an editor executable with a list of tests and returns a dict of the result of every test ran within that editor instance. In case of failure this function also parses the editor output to find out what specific tests @@ -804,7 +808,7 @@ class EditorTestSuite(): :editor: The LyTestTools Editor object :run_id: The unique run id :log_name: The name of the editor log to retrieve - :test_spec_list: A list of EditorTestBase tests to run + :test_spec_list: A list of EditorTestBase tests to run in the same editor instance :cmdline_args: Any additional command line args :return: A dict of Result objects """ @@ -820,8 +824,7 @@ class EditorTestSuite(): editor_utils.cycle_crash_report(run_id, workspace) results = {} - test_filenames_str = ";".join(editor_utils.get_testcase_module_filepath(test_spec.test_module) for - test_spec in test_spec_list) + test_filenames_str = ";".join(editor_utils.get_testcase_module_filepath(test_spec.test_module) for test_spec in test_spec_list) cmdline = [ "--runpythontest", test_filenames_str, "-logfile", f"@log@/{log_name}", @@ -846,8 +849,7 @@ class EditorTestSuite(): # Scrap the output to attempt to find out which tests failed. # This function should always populate the result list, if it didn't find it, it will have "Unknown" type of result results = self._get_results_using_output(test_spec_list, output, editor_log_content) - assert len(results) == len(test_spec_list), "bug in _get_results_using_output(), the number of results" \ - "don't match the tests ran" + assert len(results) == len(test_spec_list), "bug in _get_results_using_output(), the number of results don't match the tests ran" # If the editor crashed, find out in which test it happened and update the results has_crashed = return_code != EditorTestSuite._TEST_FAIL_RETCODE @@ -866,9 +868,9 @@ class EditorTestSuite(): else: # If there are remaning "Unknown" results, these couldn't execute because of the crash, # update with info about the offender - results[test_spec_name].extra_info = f"This test has unknown result, test " \ - f"'{crashed_result.test_spec.__name__}' crashed " \ - f"before this test could be executed" + results[test_spec_name].extra_info = f"This test has unknown result," \ + f"test '{crashed_result.test_spec.__name__}'" \ + f"crashed before this test could be executed" # if all the tests ran, the one that has caused the crash is the last test if not crashed_result: crash_error = editor_utils.retrieve_crash_output(run_id, workspace, self._TIMEOUT_CRASH_LOG) @@ -882,8 +884,7 @@ class EditorTestSuite(): # The editor timed out when running the tests, get the data from the output to find out which ones ran results = self._get_results_using_output(test_spec_list, output, editor_log_content) - assert len(results) == len(test_spec_list), "bug in _get_results_using_output(), the number of results " \ - "don't match the tests ran" + assert len(results) == len(test_spec_list), "bug in _get_results_using_output(), the number of results don't match the tests ran" # Similar logic here as crashes, the first test that has no result is the one that timed out timed_out_result = None for test_spec_name, result in results.items(): @@ -929,7 +930,7 @@ class EditorTestSuite(): self._report_result(test_name, test_result) def _run_batched_tests(self, request, workspace, editor, editor_test_data, test_spec_list, extra_cmdline_args=[]): - # type (Request, AbstractWorkspace, Editor, TestData, list[EditorSharedTest], list[str]) -> None + # type (Request, AbstractWorkspace, Editor, TestData, List[EditorSharedTest], List[str]) -> None """ Runs a batch of tests in one single editor with the given spec list (one editor, multiple tests) :request: The Pytest Request @@ -950,7 +951,7 @@ class EditorTestSuite(): editor_test_data.results.update(results) def _run_parallel_tests(self, request, workspace, editor, editor_test_data, test_spec_list, extra_cmdline_args=[]): - # type(Request, AbstractWorkspace, Editor, TestData, list[EditorSharedTest], list[str]) -> None + # type(Request, AbstractWorkspace, Editor, TestData, List[EditorSharedTest], List[str]) -> None """ Runs multiple editors with one test on each editor (multiple editor, one test each) :request: The Pytest Request @@ -999,7 +1000,7 @@ class EditorTestSuite(): def _run_parallel_batched_tests(self, request, workspace, editor, editor_test_data, test_spec_list, extra_cmdline_args=[]): - # type(Request, AbstractWorkspace, Editor, TestData, list[EditorSharedTest], list[str] -> None + # type(Request, AbstractWorkspace, Editor, TestData, List[EditorSharedTest], List[str] -> None """ Runs multiple editors with a batch of tests for each editor (multiple editor, multiple tests each) :request: The Pytest Request diff --git a/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py b/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py index 838f929cfa..6e319bb860 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py @@ -4,7 +4,7 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT -Utility functions for the editor_test module +Utility functions mostly for the editor_test module. They can also be used for assisting Editor tests. """ import os @@ -19,7 +19,8 @@ logger = logging.getLogger(__name__) def kill_all_ly_processes(include_asset_processor=True): # type (bool) -> None """ - Kills all common O3DE processes such as the Editor, Game Launchers, and Asset Processor. + Kills all common O3DE processes such as the Editor, Game Launchers, and optionally Asset Processor. Defaults to + killing the Asset Processor. :param include_asset_processor: Boolean flag whether or not to kill the AP :return: None """ @@ -65,7 +66,7 @@ def retrieve_log_path(run_id, workspace): """ return os.path.join(workspace.paths.project(), "user", f"log_test_{run_id}") -def retrieve_crash_output(run_id, workspace, timeout): +def retrieve_crash_output(run_id, workspace, timeout=10): # type (int, ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager, float) -> str """ returns the crash output string for the given test run. @@ -138,7 +139,7 @@ def retrieve_editor_log_content(run_id, log_name, workspace, timeout=10): return editor_info def retrieve_last_run_test_index_from_output(test_spec_list, output): - # type (list, str) -> int + # type (List[EditorTestBase], str) -> int """ Finds out what was the last test that was run by inspecting the input. This is used for determining what was the batched test has crashed the editor diff --git a/Tools/LyTestTools/tests/unit/test_editor_test_utils.py b/Tools/LyTestTools/tests/unit/test_editor_test_utils.py index 134d1e1ad8..b096f6d6b0 100644 --- a/Tools/LyTestTools/tests/unit/test_editor_test_utils.py +++ b/Tools/LyTestTools/tests/unit/test_editor_test_utils.py @@ -16,18 +16,20 @@ pytestmark = pytest.mark.SUITE_smoke class TestEditorTestUtils(unittest.TestCase): @mock.patch('ly_test_tools.environment.process_utils.kill_processes_named') - def test_KillAllLyProcesses_IncludeAP_CallsCorrectly(self, under_test): + def test_KillAllLyProcesses_IncludeAP_CallsCorrectly(self, mock_kill_processes_named): process_list = ['Editor', 'Profiler', 'RemoteConsole', 'AssetProcessor', 'AssetProcessorBatch', 'AssetBuilder'] editor_test_utils.kill_all_ly_processes(include_asset_processor=True) - under_test.assert_called_once_with(process_list, ignore_extensions=True) + mock_kill_processes_named.assert_called_once_with(process_list, ignore_extensions=True) @mock.patch('ly_test_tools.environment.process_utils.kill_processes_named') - def test_KillAllLyProcesses_NotIncludeAP_CallsCorrectly(self, under_test): + def test_KillAllLyProcesses_NotIncludeAP_CallsCorrectly(self, mock_kill_processes_named): process_list = ['Editor', 'Profiler', 'RemoteConsole'] + ap_process_list = ['AssetProcessor', 'AssetProcessorBatch', 'AssetBuilder'] editor_test_utils.kill_all_ly_processes(include_asset_processor=False) - under_test.assert_called_once_with(process_list, ignore_extensions=True) + mock_kill_processes_named.assert_called_once() + assert ap_process_list not in mock_kill_processes_named.call_args[0] def test_GetTestcaseModuleFilepath_NoExtension_ReturnsPYExtension(self): mock_module = mock.MagicMock() @@ -81,30 +83,30 @@ class TestEditorTestUtils(unittest.TestCase): @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') @mock.patch('os.path.exists') def test_CycleCrashReport_LogExists_NamedCorrectly(self, mock_exists, mock_retrieve_log_path, mock_getmtime, - under_test): + mock_rename): mock_exists.side_effect = [True, False] mock_retrieve_log_path.return_value = 'mock_log_path' mock_workspace = mock.MagicMock() mock_getmtime.return_value = 1 editor_test_utils.cycle_crash_report(0, mock_workspace) - under_test.assert_called_once_with(os.path.join('mock_log_path', 'error.log'), - os.path.join('mock_log_path', 'error_1969_12_31_16_00_01.log')) + mock_rename.assert_called_once_with(os.path.join('mock_log_path', 'error.log'), + os.path.join('mock_log_path', 'error_1969_12_31_16_00_01.log')) @mock.patch('os.rename') @mock.patch('os.path.getmtime') @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') @mock.patch('os.path.exists') def test_CycleCrashReport_DmpExists_NamedCorrectly(self, mock_exists, mock_retrieve_log_path, mock_getmtime, - under_test): + mock_rename): mock_exists.side_effect = [False, True] mock_retrieve_log_path.return_value = 'mock_log_path' mock_workspace = mock.MagicMock() mock_getmtime.return_value = 1 editor_test_utils.cycle_crash_report(0, mock_workspace) - under_test.assert_called_once_with(os.path.join('mock_log_path', 'error.dmp'), - os.path.join('mock_log_path', 'error_1969_12_31_16_00_01.dmp')) + mock_rename.assert_called_once_with(os.path.join('mock_log_path', 'error.dmp'), + os.path.join('mock_log_path', 'error_1969_12_31_16_00_01.dmp')) @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') @mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock()) @@ -123,9 +125,9 @@ class TestEditorTestUtils(unittest.TestCase): mock_retrieve_log_path.return_value = 'mock_log_path' mock_logname = 'mock_log.log' mock_workspace = mock.MagicMock() - expected = f"-- Error reading editor.log: [Errno 2] No such file or directory: 'mock_log_path\\\\mock_log.log' --" + expected = f"-- Error reading editor.log" - assert expected == editor_test_utils.retrieve_editor_log_content(0, mock_logname, mock_workspace) + assert expected in editor_test_utils.retrieve_editor_log_content(0, mock_logname, mock_workspace) def test_RetrieveLastRunTestIndexFromOutput_SecondTestFailed_Returns0(self): mock_test = mock.MagicMock() diff --git a/Tools/LyTestTools/tests/unit/test_fixtures.py b/Tools/LyTestTools/tests/unit/test_fixtures.py index 911e6fef91..32dc204bd2 100755 --- a/Tools/LyTestTools/tests/unit/test_fixtures.py +++ b/Tools/LyTestTools/tests/unit/test_fixtures.py @@ -369,14 +369,3 @@ class TestFixtures(object): mock_request.addfinalizer.call_args[0][0]() mock_stop.assert_called_once() - - @mock.patch('inspect.isclass', mock.MagicMock(return_value=True)) - def test_PytestPycollectMakeitem_ValidArgs_CallsCorrectly(self): - mock_collector = mock.MagicMock() - mock_name = mock.MagicMock() - mock_obj = mock.MagicMock() - mock_base = mock.MagicMock() - mock_obj.__bases__ = [mock_base] - - test_tools_fixtures.pytest_pycollect_makeitem(mock_collector, mock_name, mock_obj) - mock_base.pytest_custom_makeitem.assert_called_once_with(mock_collector, mock_name, mock_obj) diff --git a/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py b/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py index 05d1ebc305..2ba5a8ed6c 100644 --- a/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py +++ b/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py @@ -68,14 +68,9 @@ class TestPass(unittest.TestCase): mock_test_spec = mock.MagicMock() mock_output = 'mock_output' mock_editor_log = mock.MagicMock() - expected = f"Test Passed\n"\ - f"------------\n"\ - f"| Output |\n"\ - f"------------\n"\ - f"{mock_output}\n" mock_pass = editor_test.Result.Pass.create(mock_test_spec, mock_output, mock_editor_log) - assert str(mock_pass) == expected + assert mock_output in str(mock_pass) class TestFail(unittest.TestCase): @@ -93,18 +88,10 @@ class TestFail(unittest.TestCase): mock_test_spec = mock.MagicMock() mock_output = 'mock_output' mock_editor_log = 'mock_editor_log' - expected = f"Test FAILED\n"\ - f"------------\n"\ - f"| Output |\n"\ - f"------------\n"\ - f"{mock_output}\n"\ - f"--------------\n"\ - f"| Editor log |\n"\ - f"--------------\n"\ - f"{mock_editor_log}\n" mock_pass = editor_test.Result.Fail.create(mock_test_spec, mock_output, mock_editor_log) - assert str(mock_pass) == expected + assert mock_output in str(mock_pass) + assert mock_editor_log in str(mock_pass) class TestCrash(unittest.TestCase): @@ -129,23 +116,12 @@ class TestCrash(unittest.TestCase): mock_editor_log = 'mock_editor_log' mock_return_code = 0 mock_stacktrace = 'mock stacktrace' - expected = f"Test CRASHED, return code {hex(mock_return_code)}\n"\ - f"---------------\n"\ - f"| Stacktrace |\n"\ - f"---------------\n"\ - f"{mock_stacktrace}"\ - f"------------\n" \ - f"| Output |\n" \ - f"------------\n" \ - f"{mock_output}\n" \ - f"--------------\n" \ - f"| Editor log |\n" \ - f"--------------\n" \ - f"{mock_editor_log}\n" mock_pass = editor_test.Result.Crash.create(mock_test_spec, mock_output, mock_return_code, mock_stacktrace, mock_editor_log) - assert str(mock_pass) == expected + assert mock_stacktrace in str(mock_pass) + assert mock_output in str(mock_pass) + assert mock_editor_log in str(mock_pass) def test_Str_MissingStackTrace_ReturnsCorrectly(self): mock_test_spec = mock.MagicMock() @@ -153,23 +129,10 @@ class TestCrash(unittest.TestCase): mock_editor_log = 'mock_editor_log' mock_return_code = 0 mock_stacktrace = None - expected = f"Test CRASHED, return code {hex(mock_return_code)}\n"\ - f"---------------\n"\ - f"| Stacktrace |\n"\ - f"---------------\n"\ - f"-- No stacktrace data found --\n"\ - f"------------\n" \ - f"| Output |\n" \ - f"------------\n" \ - f"{mock_output}\n" \ - f"--------------\n" \ - f"| Editor log |\n" \ - f"--------------\n" \ - f"{mock_editor_log}\n" - mock_pass = editor_test.Result.Crash.create(mock_test_spec, mock_output, mock_return_code, mock_stacktrace, mock_editor_log) - assert str(mock_pass) == expected + assert mock_output in str(mock_pass) + assert mock_editor_log in str(mock_pass) class Timeout(unittest.TestCase): @@ -190,18 +153,10 @@ class Timeout(unittest.TestCase): mock_output = 'mock_output' mock_editor_log = 'mock_editor_log' mock_timeout = 0 - expected = f"Test TIMED OUT after {mock_timeout} seconds\n"\ - f"------------\n" \ - f"| Output |\n" \ - f"------------\n" \ - f"{mock_output}\n" \ - f"--------------\n" \ - f"| Editor log |\n" \ - f"--------------\n" \ - f"{mock_editor_log}\n" mock_pass = editor_test.Result.Timeout.create(mock_test_spec, mock_output, mock_timeout, mock_editor_log) - assert str(mock_pass) == expected + assert mock_output in str(mock_pass) + assert mock_editor_log in str(mock_pass) class Unknown(unittest.TestCase): @@ -222,18 +177,10 @@ class Unknown(unittest.TestCase): mock_output = 'mock_output' mock_editor_log = 'mock_editor_log' mock_extra_info = 'mock extra info' - expected = f"Unknown test result, possible cause: {mock_extra_info}\n"\ - f"------------\n" \ - f"| Output |\n" \ - f"------------\n" \ - f"{mock_output}\n" \ - f"--------------\n" \ - f"| Editor log |\n" \ - f"--------------\n" \ - f"{mock_editor_log}\n" mock_pass = editor_test.Result.Unknown.create(mock_test_spec, mock_output, mock_extra_info, mock_editor_log) - assert str(mock_pass) == expected + assert mock_output in str(mock_pass) + assert mock_editor_log in str(mock_pass) class TestEditorTestSuite(unittest.TestCase): @@ -274,12 +221,31 @@ class TestEditorTestSuite(unittest.TestCase): mock.MagicMock()) assert isinstance(mock_test_class, editor_test.EditorTestSuite.EditorTestClass) - def test_PytestCustomModifyItems(self): - pass + @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite.filter_session_shared_tests') + def test_PytestCustomModifyItems_FunctionsMatch_AddsRunners(self, mock_filter_tests): + class MockTestSuite(editor_test.EditorTestSuite): + pass + mock_func_1 = mock.MagicMock() + mock_test = mock.MagicMock() + runner_1 = editor_test.EditorTestSuite.Runner('mock_runner_1', mock_func_1, [mock_test]) + mock_run_pytest_func = mock.MagicMock() + runner_1.run_pytestfunc = mock_run_pytest_func + mock_result_pytestfuncs = [mock.MagicMock()] + runner_1.result_pytestfuncs = mock_result_pytestfuncs + mock_items = [] + mock_items.extend(mock_result_pytestfuncs) + + MockTestSuite._runners = [runner_1] + mock_test_1 = mock.MagicMock() + mock_test_2 = mock.MagicMock() + mock_filter_tests.return_value = [mock_test_1, mock_test_2] + + MockTestSuite.pytest_custom_modify_items(mock.MagicMock(), mock_items, mock.MagicMock()) + assert mock_items == [mock_run_pytest_func, mock_result_pytestfuncs[0]] def test_GetSingleTests_NoSingleTests_EmptyList(self): class MockTestSuite(editor_test.EditorTestSuite): - pass + pass mock_test_suite = MockTestSuite() tests = mock_test_suite.get_single_tests() assert len(tests) == 0 @@ -291,7 +257,8 @@ class TestEditorTestSuite(unittest.TestCase): mock_test_suite = MockTestSuite() tests = mock_test_suite.get_single_tests() assert len(tests) == 1 - + assert tests[0].__name__ == "MockSingleTest" + assert issubclass(tests[0], editor_test.EditorSingleTest) def test_GetSingleTests_AllTests_ReturnsOnlySingles(self): class MockTestSuite(editor_test.EditorTestSuite): @@ -304,6 +271,8 @@ class TestEditorTestSuite(unittest.TestCase): mock_test_suite = MockTestSuite() tests = mock_test_suite.get_single_tests() assert len(tests) == 2 + for test in tests: + assert issubclass(test, editor_test.EditorSingleTest) def test_GetSharedTests_NoSharedTests_EmptyList(self): class MockTestSuite(editor_test.EditorTestSuite): @@ -319,6 +288,8 @@ class TestEditorTestSuite(unittest.TestCase): mock_test_suite = MockTestSuite() tests = mock_test_suite.get_shared_tests() assert len(tests) == 1 + assert tests[0].__name__ == 'MockSharedTest' + assert issubclass(tests[0], editor_test.EditorSharedTest) def test_GetSharedTests_AllTests_ReturnsOnlyShared(self): class MockTestSuite(editor_test.EditorTestSuite): @@ -331,6 +302,8 @@ class TestEditorTestSuite(unittest.TestCase): mock_test_suite = MockTestSuite() tests = mock_test_suite.get_shared_tests() assert len(tests) == 2 + for test in tests: + assert issubclass(test, editor_test.EditorSharedTest) @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite.filter_session_shared_tests') @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite.get_shared_tests') @@ -343,8 +316,6 @@ class TestEditorTestSuite(unittest.TestCase): def test_FilterSessionSharedTests_OneSharedTest_ReturnsOne(self): def mock_test(): pass - mock_session_items = mock.MagicMock() - mock_shared_tests = mock.MagicMock() mock_test.originalname = 'mock_test' mock_test.__name__ = mock_test.originalname mock_session_items = [mock_test] @@ -361,8 +332,6 @@ class TestEditorTestSuite(unittest.TestCase): pass def mock_test_3(): pass - mock_session_items = mock.MagicMock() - mock_shared_tests = mock.MagicMock() mock_test.originalname = 'mock_test' mock_test.__name__ = mock_test.originalname mock_test_2.originalname = 'mock_test_2' @@ -379,8 +348,6 @@ class TestEditorTestSuite(unittest.TestCase): def test_FilterSessionSharedTests_SkippingPytestRaises_SkipsAddingTest(self): def mock_test(): pass - mock_session_items = mock.MagicMock() - mock_shared_tests = mock.MagicMock() mock_test.originalname = 'mock_test' mock_test.__name__ = mock_test.originalname mock_session_items = [mock_test] @@ -495,12 +462,13 @@ class TestUtils(unittest.TestCase): mock_output = 'JSON_START(' \ '{"name": "mock_module_name", "output": "mock_std_out", "success": "mock_success_data"}' \ ')JSON_END' - mock_editor_log = 'JSON_START(' \ - ')JSON_END' + mock_pass = mock.MagicMock() + mock_create.return_value = mock_pass - results = mock_test_suite._get_results_using_output(mock_test_list, mock_output, mock_editor_log) + results = mock_test_suite._get_results_using_output(mock_test_list, mock_output, '') assert mock_create.called assert len(results) == 1 + assert results[mock_test.__name__] == mock_pass @mock.patch('ly_test_tools.o3de.editor_test.Result.Fail.create') @mock.patch('ly_test_tools.o3de.editor_test_utils.get_module_filename') @@ -513,12 +481,13 @@ class TestUtils(unittest.TestCase): mock_output = 'JSON_START(' \ '{"name": "mock_module_name", "output": "mock_std_out", "failed": "mock_fail_data"}' \ ')JSON_END' - mock_editor_log = 'JSON_START(' \ - ')JSON_END' + mock_fail = mock.MagicMock() + mock_create.return_value = mock_fail - results = mock_test_suite._get_results_using_output(mock_test_list, mock_output, mock_editor_log) + results = mock_test_suite._get_results_using_output(mock_test_list, mock_output, '') assert mock_create.called assert len(results) == 1 + assert results[mock_test.__name__] == mock_fail @mock.patch('ly_test_tools.o3de.editor_test.Result.Unknown.create') @mock.patch('ly_test_tools.o3de.editor_test_utils.get_module_filename') @@ -531,12 +500,13 @@ class TestUtils(unittest.TestCase): mock_output = 'JSON_START(' \ '{"name": "mock_module_name", "output": "mock_std_out", "failed": "mock_fail_data"}' \ ')JSON_END' - mock_editor_log = 'JSON_START(' \ - ')JSON_END' + mock_unknown = mock.MagicMock() + mock_create.return_value = mock_unknown - results = mock_test_suite._get_results_using_output(mock_test_list, mock_output, mock_editor_log) + results = mock_test_suite._get_results_using_output(mock_test_list, mock_output, '') assert mock_create.called assert len(results) == 1 + assert results[mock_test.__name__] == mock_unknown @mock.patch('ly_test_tools.o3de.editor_test.Result.Pass.create') @mock.patch('ly_test_tools.o3de.editor_test.Result.Fail.create') @@ -567,7 +537,13 @@ class TestUtils(unittest.TestCase): ')JSON_END' \ 'JSON_START(' \ '{"name": "mock_module_name_fail"}' \ - ')JSON_END' \ + ')JSON_END' + mock_unknown = mock.MagicMock() + mock_pass = mock.MagicMock() + mock_fail = mock.MagicMock() + mock_create_unknown.return_value = mock_unknown + mock_create_pass.return_value = mock_pass + mock_create_fail.return_value = mock_fail results = mock_test_suite._get_results_using_output(mock_test_list, mock_output, mock_editor_log) mock_create_pass.assert_called_with( @@ -577,6 +553,9 @@ class TestUtils(unittest.TestCase): mock_create_unknown.assert_called_with( mock_test_unknown, mock_output, "Couldn't find any test run information on stdout", mock_editor_log) assert len(results) == 3 + assert results[mock_test_pass.__name__] == mock_pass + assert results[mock_test_fail.__name__] == mock_fail + assert results[mock_test_unknown.__name__] == mock_unknown @mock.patch('builtins.print') def test_ReportResult_TestPassed_ReportsCorrectly(self, mock_print): diff --git a/Tools/LyTestTools/tests/unit/test_pytest_plugin_editor_test.py b/Tools/LyTestTools/tests/unit/test_pytest_plugin_editor_test.py index 968c07aca6..10c779e546 100644 --- a/Tools/LyTestTools/tests/unit/test_pytest_plugin_editor_test.py +++ b/Tools/LyTestTools/tests/unit/test_pytest_plugin_editor_test.py @@ -38,4 +38,4 @@ class TestEditorTest(unittest.TestCase): generator = editor_test.pytest_collection_modifyitems(mock_session, mock_items, mock_config) for x in generator: pass - assert mock_class.pytest_custom_modify_items.call_count == 1 \ No newline at end of file + assert mock_class.pytest_custom_modify_items.call_count == 1 From 90bd09ffcaad68d9fa311b2dd6e6688c0d7450e2 Mon Sep 17 00:00:00 2001 From: evanchia Date: Mon, 18 Oct 2021 14:13:31 -0700 Subject: [PATCH 009/120] Fixing minor details in unit tests Signed-off-by: evanchia --- .../tests/unit/test_o3de_editor_test.py | 63 ++++++++++--------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py b/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py index 2ba5a8ed6c..f16f7533d4 100644 --- a/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py +++ b/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py @@ -31,7 +31,7 @@ class TestEditorTestBase(unittest.TestCase): assert mock_editorsharedtest.is_batchable == True assert mock_editorsharedtest.is_parallelizable == False -class TestBase(unittest.TestCase): +class TestResultBase(unittest.TestCase): def setUp(self): self.mock_result = editor_test.Result.Base() @@ -52,7 +52,7 @@ class TestBase(unittest.TestCase): self.mock_result.editor_log = None assert self.mock_result.get_editor_log_str() == '-- No editor log found --' -class TestPass(unittest.TestCase): +class TestResultPass(unittest.TestCase): def test_Create_ValidArgs_CorrectAttributes(self): mock_test_spec = mock.MagicMock() @@ -72,7 +72,7 @@ class TestPass(unittest.TestCase): mock_pass = editor_test.Result.Pass.create(mock_test_spec, mock_output, mock_editor_log) assert mock_output in str(mock_pass) -class TestFail(unittest.TestCase): +class TestResultFail(unittest.TestCase): def test_Create_ValidArgs_CorrectAttributes(self): mock_test_spec = mock.MagicMock() @@ -93,7 +93,7 @@ class TestFail(unittest.TestCase): assert mock_output in str(mock_pass) assert mock_editor_log in str(mock_pass) -class TestCrash(unittest.TestCase): +class TestResultCrash(unittest.TestCase): def test_Create_ValidArgs_CorrectAttributes(self): mock_test_spec = mock.MagicMock() @@ -134,7 +134,7 @@ class TestCrash(unittest.TestCase): assert mock_output in str(mock_pass) assert mock_editor_log in str(mock_pass) -class Timeout(unittest.TestCase): +class TestResultTimeout(unittest.TestCase): def test_Create_ValidArgs_CorrectAttributes(self): mock_test_spec = mock.MagicMock() @@ -158,7 +158,7 @@ class Timeout(unittest.TestCase): assert mock_output in str(mock_pass) assert mock_editor_log in str(mock_pass) -class Unknown(unittest.TestCase): +class TestResultUnknown(unittest.TestCase): def test_Create_ValidArgs_CorrectAttributes(self): mock_test_spec = mock.MagicMock() @@ -185,7 +185,7 @@ class Unknown(unittest.TestCase): class TestEditorTestSuite(unittest.TestCase): @mock.patch('ly_test_tools.o3de.editor_test_utils.kill_all_ly_processes') - def test_EditorTestData_ValidAP_TeardownProperly(self, mock_kill_processes): + def test_EditorTestData_ValidAP_TeardownAPOnce(self, mock_kill_processes): mock_editor_test_suite = editor_test.EditorTestSuite() mock_test_data_generator = mock_editor_test_suite._editor_test_data(mock.MagicMock()) mock_asset_processor = mock.MagicMock() @@ -197,30 +197,13 @@ class TestEditorTestSuite(unittest.TestCase): mock_kill_processes.assert_called_once_with(include_asset_processor=True) @mock.patch('ly_test_tools.o3de.editor_test_utils.kill_all_ly_processes') - def test_EditorTestData_NoAP_TeardownProperly(self, mock_kill_processes): + def test_EditorTestData_NoAP_NoTeardownAP(self, mock_kill_processes): mock_editor_test_suite = editor_test.EditorTestSuite() mock_test_data_generator = mock_editor_test_suite._editor_test_data(mock.MagicMock()) for test_data in mock_test_data_generator: test_data.asset_processor = None mock_kill_processes.assert_called_once_with(include_asset_processor=False) - def test_RunnerInit_ValidArgs_InitProperly(self): - mock_name = mock.MagicMock() - mock_func = mock.MagicMock() - mock_tests = mock.MagicMock() - - mock_runner = editor_test.EditorTestSuite.Runner(mock_name, mock_func, mock_tests) - mock_runner.name = mock_name - mock_runner.func = mock_func - mock_runner.tests = mock_tests - mock_runner.run_pytestfunc = None - mock_runner.result_pytestfuncs = [] - - def test_PytestCustomMakeitem_Called_ReturnsClass(self): - mock_test_class = editor_test.EditorTestSuite.pytest_custom_makeitem(mock.MagicMock(), mock.MagicMock(), - mock.MagicMock()) - assert isinstance(mock_test_class, editor_test.EditorTestSuite.EditorTestClass) - @mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite.filter_session_shared_tests') def test_PytestCustomModifyItems_FunctionsMatch_AddsRunners(self, mock_filter_tests): class MockTestSuite(editor_test.EditorTestSuite): @@ -323,6 +306,7 @@ class TestEditorTestSuite(unittest.TestCase): selected_tests = editor_test.EditorTestSuite.filter_session_shared_tests(mock_session_items, mock_shared_tests) assert selected_tests == mock_session_items + assert len(selected_tests) == 1 @mock.patch('ly_test_tools.o3de.editor_test.skipping_pytest_runtest_setup', mock.MagicMock()) def test_FilterSessionSharedTests_ManyTests_ReturnsCorrectTests(self): @@ -344,8 +328,29 @@ class TestEditorTestSuite(unittest.TestCase): selected_tests = editor_test.EditorTestSuite.filter_session_shared_tests(mock_session_items, mock_shared_tests) assert selected_tests == mock_session_items + @mock.patch('ly_test_tools.o3de.editor_test.skipping_pytest_runtest_setup') + def test_FilterSessionSharedTests_SkipOneTest_ReturnsCorrectTests(self, mock_skip): + def mock_test(): + pass + def mock_test_2(): + pass + def mock_test_3(): + pass + mock_skip.side_effect = [True, Exception] + mock_test.originalname = 'mock_test' + mock_test.__name__ = mock_test.originalname + mock_test_2.originalname = 'mock_test_2' + mock_test_2.__name__ = mock_test_2.originalname + mock_test_3.originalname = 'mock_test_3' + mock_test_3.__name__ = mock_test_3.originalname + mock_session_items = [mock_test, mock_test_2] + mock_shared_tests = [mock_test, mock_test_2, mock_test_3] + + selected_tests = editor_test.EditorTestSuite.filter_session_shared_tests(mock_session_items, mock_shared_tests) + assert selected_tests == [mock_test] + @mock.patch('ly_test_tools.o3de.editor_test.skipping_pytest_runtest_setup', mock.MagicMock(side_effect=Exception)) - def test_FilterSessionSharedTests_SkippingPytestRaises_SkipsAddingTest(self): + def test_FilterSessionSharedTests_ExceptionDuringSkipSetup_SkipsAddingTest(self): def mock_test(): pass mock_test.originalname = 'mock_test' @@ -365,7 +370,8 @@ class TestEditorTestSuite(unittest.TestCase): mock_test_2.is_parallelizable = False mock_shared_tests = [mock_test, mock_test_2] - filtered_tests = editor_test.EditorTestSuite.filter_shared_tests(mock_shared_tests, True, True) + filtered_tests = editor_test.EditorTestSuite.filter_shared_tests( + mock_shared_tests, is_batchable=True, is_parallelizable=True) assert filtered_tests == [mock_test] def test_FilterSharedTests_FalseParams_ReturnsFalseTests(self): @@ -377,7 +383,8 @@ class TestEditorTestSuite(unittest.TestCase): mock_test_2.is_parallelizable = False mock_shared_tests = [mock_test, mock_test_2] - filtered_tests = editor_test.EditorTestSuite.filter_shared_tests(mock_shared_tests, False, False) + filtered_tests = editor_test.EditorTestSuite.filter_shared_tests( + mock_shared_tests, is_batchable=False, is_parallelizable=False) assert filtered_tests == [mock_test_2] class TestUtils(unittest.TestCase): From f1a38ded86e02d436f061937b6f11585862739a3 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Mon, 18 Oct 2021 16:09:54 -0700 Subject: [PATCH 010/120] fixes warning in release Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- .../Code/Source/Material/EditorMaterialSystemComponent.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp index 5df17a5478..2e1c7f0bd6 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp @@ -192,6 +192,9 @@ namespace AZ AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultLightingPresetPath), propertyOverrides), [entityId, materialAssignmentId]() { + AZ_UNUSED(entityId); + AZ_UNUSED(materialAssignmentId); + AZ_Warning( "EditorMaterialSystemComponent", false, "RenderMaterialPreview capture failed for entity %s slot %s.", entityId.ToString().c_str(), materialAssignmentId.ToString().c_str()); From c842fce4bfcc7cba9bb4f7f734a1cd160239c5e3 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Mon, 18 Oct 2021 16:13:35 -0700 Subject: [PATCH 011/120] WIP, need to merge from development Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/Install.cmake | 2 +- cmake/Packaging.cmake | 35 ++++++++--- cmake/Platform/Common/Install_common.cmake | 73 +++++++++++++++------- cmake/Platform/Mac/Install_mac.cmake | 43 +++++++------ 4 files changed, 100 insertions(+), 53 deletions(-) diff --git a/cmake/Install.cmake b/cmake/Install.cmake index adcd28fa37..4816d475fc 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -168,4 +168,4 @@ function(ly_install_run_script SCRIPT) COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} # use the default for the time being ) -endfunction() \ No newline at end of file +endfunction() diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index 9f62b89e39..25f9b87cbc 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -136,6 +136,7 @@ endif() install(FILES ${_cmake_package_dest} DESTINATION ./Tools/Redistributables/CMake + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) # the version string and git tags are intended to be synchronized so it should be safe to use that instead @@ -159,6 +160,7 @@ if(${CPACK_PACKAGE_VERSION} VERSION_GREATER "0.0.0.0") if (${_status_code} EQUAL 0 AND EXISTS ${_3rd_party_license_dest}) install(FILES ${_3rd_party_license_dest} DESTINATION . + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) else() file(REMOVE ${_3rd_party_license_dest}) @@ -242,29 +244,35 @@ ly_configure_cpack_component( DISPLAY_NAME "${PROJECT_NAME}" DESCRIPTION "${PROJECT_NAME} Headers, scripts and common files" ) -#file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" "set(LY_CPACK_COMPONENTS_ALL ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME})\n") + +file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" "set(LY_CPACK_COMPONENTS_ALL ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME})\n") foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) string(TOUPPER ${conf} UCONF) unset(flags) - if(${conf} STREQUAL profile) + if(${conf} STREQUAL profile AND ${LY_BUILD_PERMUTATION} STREQUAL Default) set(flags REQUIRED) else() set(flags DISABLED) endif() + unset(permutation_description) + if(${LY_BUILD_PERMUTATION} STREQUAL Monolithic) + set(permutation_description " monolithic ") + endif() + # Inject a check to not declare components that have not been built. We are using AzCore since that is a # common target that will always be build, in every permutation and configuration - #file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" - # "if(EXISTS \"${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${conf}/${CMAKE_STATIC_LIBRARY_PREFIX}AzCore${CMAKE_STATIC_LIBRARY_SUFFIX}\")\n") + file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" + "if(EXISTS \"${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${conf}/${CMAKE_STATIC_LIBRARY_PREFIX}AzCore${CMAKE_STATIC_LIBRARY_SUFFIX}\")\n") ly_configure_cpack_component( - ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF} ${flags} + ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} ${flags} DISPLAY_NAME "${PROJECT_NAME} (${conf})" - DESCRIPTION "${PROJECT_NAME} Libraries and Tools in ${conf}" + DESCRIPTION "${PROJECT_NAME} Libraries and Tools in${permutation_description}${conf}" ) - #file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" -#"list(APPEND LY_CPACK_COMPONENTS_ALL ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF}) -#endif()\n") + file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" +"list(APPEND LY_CPACK_COMPONENTS_ALL ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF}) +endif()\n") endforeach() @@ -279,4 +287,11 @@ if(LY_INSTALLER_DOWNLOAD_URL) ) endif() -#file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" "set(CPACK_COMPONENTS_ALL \${LY_CPACK_COMPONENTS_ALL})\n") +# Inject other build directories +foreach(external_dir ${LY_INSTALL_EXTERNAL_BUILD_DIRS}) + file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" + "include(${external_dir}/CPackConfig.cmake)\n" + ) +endforeach() + +file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" "set(CPACK_COMPONENTS_ALL \${LY_CPACK_COMPONENTS_ALL})\n") diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 224d6ccffc..0adb63f08e 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -8,6 +8,8 @@ include(cmake/FileUtil.cmake) +set(LY_INSTALL_EXTERNAL_BUILD_DIRS "" CACHE PATH "External build directories to be included in the install process. This allows to package non-monolithic and monolithic.") + set(CMAKE_INSTALL_MESSAGE NEVER) # Simplify messages to reduce output noise define_property(TARGET PROPERTY LY_INSTALL_GENERATE_RUN_TARGET @@ -19,12 +21,25 @@ define_property(TARGET PROPERTY LY_INSTALL_GENERATE_RUN_TARGET ]] ) -ly_set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core) +# We can have elements being installed under the following components: +# - Core (required for all) (default) +# - Default +# - Default_$ +# - Monolithic +# - Monolithic_$ +# Debug/Monolithic are build permutations, so for a CMake run, it can only generate +# one of the permutations. Each build permutation can generate only one cmake_install.cmake. +# Each build permutation will generate the same elements in Core. +# CPack is able to put the two together by taking Core from one permutation and then taking +# each permutation. +ly_set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core) if(LY_MONOLITHIC_GAME) set(LY_BUILD_PERMUTATION Monolithic) + set(LY_INSTALL_PERMUTATION_COMPONENT Monolithic) else() set(LY_BUILD_PERMUTATION Default) + set(LY_INSTALL_PERMUTATION_COMPONENT Default) endif() cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE runtime_output_directory) @@ -106,19 +121,18 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar else() foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) string(TOUPPER ${conf} UCONF) - install( - TARGETS ${TARGET_NAME} + install(TARGETS ${TARGET_NAME} ARCHIVE DESTINATION ${archive_output_directory} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF} + COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} CONFIGURATIONS ${conf} LIBRARY DESTINATION ${library_output_directory}/${target_library_output_subdirectory} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF} + COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} CONFIGURATIONS ${conf} RUNTIME DESTINATION ${runtime_output_directory}/${target_runtime_output_subdirectory} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF} + COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} CONFIGURATIONS ${conf} ) endforeach() @@ -275,10 +289,15 @@ set_property(TARGET ${NAME_PLACEHOLDER} set(target_install_source_dir ${CMAKE_CURRENT_BINARY_DIR}/install/${relative_target_source_dir}) file(GENERATE OUTPUT "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/${NAME_PLACEHOLDER}_$.cmake" CONTENT "${target_file_contents}") - install(FILES "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/${NAME_PLACEHOLDER}_$.cmake" - DESTINATION ${relative_target_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - ) + + foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) + string(TOUPPER ${conf} UCONF) + install(FILES "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/${NAME_PLACEHOLDER}_${conf}.cmake" + DESTINATION ${relative_target_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} + COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} + CONFIGURATIONS ${conf} + ) + endforeach() # Since a CMakeLists.txt could contain multiple targets, we generate it in a folder per target ly_file_read(${LY_ROOT_FOLDER}/cmake/install/InstalledTarget.in target_cmakelists_template) @@ -353,9 +372,10 @@ endif() "${GEM_VARIANT_TO_LOAD_PLACEHOLDER}" "${ENABLE_GEMS_PLACEHOLDER}" ) + install(FILES "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/permutation.cmake" - DESTINATION ${relative_target_source_dir}//Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} + DESTINATION ${relative_target_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} + COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT} ) endfunction() @@ -377,6 +397,11 @@ function(ly_setup_o3de_install) COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + # Inject other build directories + foreach(external_dir ${LY_INSTALL_EXTERNAL_BUILD_DIRS}) + install(CODE "include(${external_dir}/cmake_install.cmake)") + endforeach() + if(COMMAND ly_post_install_steps) ly_post_install_steps() endif() @@ -399,18 +424,19 @@ function(ly_setup_cmake_install) DESTINATION cmake COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - # Inject code that will generate each ConfigurationType_.cmake file - set(install_configuration_type_template [=[ - configure_file(@LY_ROOT_FOLDER@/cmake/install/ConfigurationType_config.cmake.in - ${CMAKE_INSTALL_PREFIX}/cmake/Platform/@PAL_PLATFORM_NAME@/@LY_BUILD_PERMUTATION@/ConfigurationTypes_${CMAKE_INSTALL_CONFIG_NAME}.cmake + # generate each ConfigurationType_.cmake file and install it under that configuration + foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) + string(TOUPPER ${conf} UCONF) + configure_file("${LY_ROOT_FOLDER}/cmake/install/ConfigurationType_config.cmake.in" + "${CMAKE_BINARY_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/ConfigurationTypes_${conf}.cmake" @ONLY ) - message(STATUS "Generated ${CMAKE_INSTALL_PREFIX}/cmake/Platform/@PAL_PLATFORM_NAME@/@LY_BUILD_PERMUTATION@/ConfigurationTypes_${CMAKE_INSTALL_CONFIG_NAME}.cmake") - ]=]) - string(CONFIGURE "${install_configuration_type_template}" install_configuration_type @ONLY) - install(CODE "${install_configuration_type}" - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - ) + install(FILES "${CMAKE_BINARY_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/ConfigurationTypes_${conf}.cmake" + DESTINATION cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} + COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} + CONFIGURATIONS ${conf} + ) + endforeach() # Transform the LY_EXTERNAL_SUBDIRS list into a json array set(indent " ") @@ -429,8 +455,7 @@ function(ly_setup_cmake_install) configure_file(${LY_ROOT_FOLDER}/cmake/install/engine.json.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/engine.json @ONLY) - install( - FILES + install(FILES "${LY_ROOT_FOLDER}/CMakeLists.txt" "${CMAKE_CURRENT_BINARY_DIR}/cmake/engine.json" DESTINATION . diff --git a/cmake/Platform/Mac/Install_mac.cmake b/cmake/Platform/Mac/Install_mac.cmake index bdc2300131..472831425b 100644 --- a/cmake/Platform/Mac/Install_mac.cmake +++ b/cmake/Platform/Mac/Install_mac.cmake @@ -58,24 +58,31 @@ function(ly_install_target_override) set_property(TARGET ${ly_platform_install_target_TARGET} PROPERTY RESOURCE "") endif() - install( - TARGETS ${ly_platform_install_target_TARGET} - ARCHIVE - DESTINATION ${ly_platform_install_target_ARCHIVE_DIR} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - LIBRARY - DESTINATION ${ly_platform_install_target_LIBRARY_DIR}/${ly_platform_install_target_LIBRARY_SUBDIR} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - RUNTIME - DESTINATION ${ly_platform_install_target_RUNTIME_DIR}/${ly_platform_install_target_RUNTIME_SUBDIR} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - BUNDLE - DESTINATION ${ly_platform_install_target_RUNTIME_DIR}/${ly_platform_install_target_RUNTIME_SUBDIR} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - RESOURCE - DESTINATION ${ly_platform_install_target_RUNTIME_DIR}/${ly_platform_install_target_RUNTIME_SUBDIR} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - ) + foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) + string(TOUPPER ${conf} UCONF) + install(TARGETS ${TARGET_NAME} + ARCHIVE + DESTINATION ${ly_platform_install_target_ARCHIVE_DIR} + COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} + CONFIGURATIONS ${conf} + LIBRARY + DESTINATION ${ly_platform_install_target_LIBRARY_DIR}/${ly_platform_install_target_LIBRARY_SUBDIR} + COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} + CONFIGURATIONS ${conf} + RUNTIME + DESTINATION ${ly_platform_install_target_RUNTIME_DIR}/${ly_platform_install_target_RUNTIME_SUBDIR} + COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} + CONFIGURATIONS ${conf} + BUNDLE + DESTINATION ${ly_platform_install_target_RUNTIME_DIR}/${ly_platform_install_target_RUNTIME_SUBDIR} + COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} + CONFIGURATIONS ${conf} + RESOURCE + DESTINATION ${ly_platform_install_target_RUNTIME_DIR}/${ly_platform_install_target_RUNTIME_SUBDIR} + COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} + CONFIGURATIONS ${conf} + ) + endforeach() set(install_relative_binaries_path "${ly_platform_install_target_RUNTIME_DIR}/${ly_platform_install_target_RUNTIME_SUBDIR}") From 62a5aa18748cafb0aeab26a574666539c1a8c823 Mon Sep 17 00:00:00 2001 From: evanchia Date: Mon, 18 Oct 2021 18:15:46 -0700 Subject: [PATCH 012/120] fixing test breaking changes Signed-off-by: evanchia --- Tools/LyTestTools/ly_test_tools/o3de/editor_test.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py index 789d07f200..0bc341012c 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py @@ -326,7 +326,7 @@ class EditorTestSuite(): :request: The Pytest request :yield: The TestData object """ - self._editor_test_data(request) + yield from self._editor_test_data(request) def _editor_test_data(self, request): """ @@ -676,7 +676,6 @@ class EditorTestSuite(): elem = json.loads(m.groups()[0]) found_jsons[elem["name"]] = elem except Exception as e: - raise e continue # Avoid to fail if the output data is corrupt # Try to find the element in the log, this is used for cutting the log contents later @@ -710,7 +709,7 @@ class EditorTestSuite(): cur_log = editor_log_content[log_start : end] log_start = end - if "success" in json_result.keys(): + if json_result["success"]: result = Result.Pass.create(test_spec, json_output, cur_log) else: result = Result.Fail.create(test_spec, json_output, cur_log) From 64a20c45b56551d7f0841e2702cca7d05df61fc5 Mon Sep 17 00:00:00 2001 From: evanchia Date: Tue, 19 Oct 2021 15:51:44 -0700 Subject: [PATCH 013/120] adding editor integ tests to AR Signed-off-by: evanchia --- .../Gem/PythonTests/CMakeLists.txt | 3 + .../editor_test_testing/CMakeLists.txt | 38 +++ .../TestSuiteLinux_Main.py | 267 ++++++++++++++++++ ...Suite_Main.py => TestSuiteWindows_Main.py} | 7 +- .../launchers/launcher_helper.py | 2 +- 5 files changed, 315 insertions(+), 2 deletions(-) create mode 100644 AutomatedTesting/Gem/PythonTests/editor_test_testing/CMakeLists.txt create mode 100644 AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteLinux_Main.py rename AutomatedTesting/Gem/PythonTests/editor_test_testing/{TestSuite_Main.py => TestSuiteWindows_Main.py} (98%) diff --git a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt index 466a4b1679..7af10dba66 100644 --- a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt @@ -58,3 +58,6 @@ add_subdirectory(smoke) ## AWS ## add_subdirectory(AWS) + +## Test Tools ## +add_subdirectory(editor_test_testing) diff --git a/AutomatedTesting/Gem/PythonTests/editor_test_testing/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/editor_test_testing/CMakeLists.txt new file mode 100644 index 0000000000..fac6192673 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/editor_test_testing/CMakeLists.txt @@ -0,0 +1,38 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# + +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) + + ly_add_pytest( + NAME AutomatedTesting::ParallelEditorTestsWindows + TEST_SUITE main + TEST_SERIAL + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuiteWindows_Main.py + PYTEST_MARKS "SUITE_main" + RUNTIME_DEPENDENCIES + AZ::AssetProcessor + Legacy::Editor + AutomatedTesting.Assets + COMPONENT + TestTools + ) + + ly_add_pytest( + NAME AutomatedTesting::ParallelEditorTestsLinux + TEST_SUITE main + TEST_SERIAL + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuiteLinux_Main.py + PYTEST_MARKS "SUITE_main" + RUNTIME_DEPENDENCIES + AZ::AssetProcessor + Legacy::Editor + AutomatedTesting.Assets + COMPONENT + TestTools + ) + +endif() diff --git a/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteLinux_Main.py b/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteLinux_Main.py new file mode 100644 index 0000000000..8686c43357 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteLinux_Main.py @@ -0,0 +1,267 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + +""" +This suite contains the tests for editor_test utilities. +""" + +import pytest +import os +import sys +import importlib +import re + +import ly_test_tools +from ly_test_tools import LAUNCHERS + +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorTestSuite, Result +from ly_test_tools.o3de.asset_processor import AssetProcessor +import ly_test_tools.environment.process_utils as process_utils + +import argparse, sys + +if ly_test_tools.LINUX: + pytestmark = pytest.mark.SUITE_main +else: + pytestmark = pytest.mark.skipif(not ly_test_tools.LINUX, reason="Only runs on Linux") + +@pytest.mark.parametrize("launcher_platform", ['linux_editor']) +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +class TestEditorTest: + + args = None + path = None + @classmethod + def setup_class(cls): + TestEditorTest.args = sys.argv.copy() + build_dir_arg_index = TestEditorTest.args.index("--build-directory") + if build_dir_arg_index < 0: + print("Error: Must pass --build-directory argument in order to run this test") + sys.exit(-2) + + TestEditorTest.args[build_dir_arg_index+1] = os.path.abspath(TestEditorTest.args[build_dir_arg_index+1]) + TestEditorTest.args.append("-s") + TestEditorTest.path = os.path.dirname(os.path.abspath(__file__)) + cls._asset_processor = None + + def teardown_class(cls): + if cls._asset_processor: + cls._asset_processor.stop(1) + cls._asset_processor.teardown() + + # Test runs # + @classmethod + def _run_single_test(cls, testdir, workspace, module_name): + if cls._asset_processor is None: + if not process_utils.process_exists("AssetProcessor", ignore_extensions=True): + cls._asset_processor = AssetProcessor(workspace) + cls._asset_processor.start() + + testdir.makepyfile( + f""" + import pytest + import os + import sys + + from ly_test_tools import LAUNCHERS + from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorTestSuite + + @pytest.mark.SUITE_main + @pytest.mark.parametrize("launcher_platform", ['linux_editor']) + @pytest.mark.parametrize("project", ["AutomatedTesting"]) + class TestAutomation(EditorTestSuite): + class test_single(EditorSingleTest): + import {module_name} as test_module + + """) + result = testdir.runpytest(*TestEditorTest.args[2:]) + + def get_class(module_name): + class test_single(EditorSingleTest): + test_module = importlib.import_module(module_name) + return test_single + + output = "".join(result.outlines) + extracted_results = EditorTestSuite._get_results_using_output([get_class(module_name)], output, output) + extracted_result = next(iter(extracted_results.items())) + return (extracted_result[1], result) + + def test_single_passing_test(self, request, workspace, launcher_platform, testdir): + (extracted_result, result) = TestEditorTest._run_single_test(testdir, workspace, "EditorTest_That_Passes") + result.assert_outcomes(passed=1) + assert isinstance(extracted_result, Result.Pass) + + def test_single_failing_test(self, request, workspace, launcher_platform, testdir): + (extracted_result, result) = TestEditorTest._run_single_test(testdir, workspace, "EditorTest_That_Fails") + result.assert_outcomes(failed=1) + assert isinstance(extracted_result, Result.Fail) + + def test_single_crashing_test(self, request, workspace, launcher_platform, testdir): + (extracted_result, result) = TestEditorTest._run_single_test(testdir, workspace, "EditorTest_That_Crashes") + result.assert_outcomes(failed=1) + assert isinstance(extracted_result, Result.Unknown) + + @classmethod + def _run_shared_test(cls, testdir, module_class_code, extra_cmd_line=None): + if not extra_cmd_line: + extra_cmd_line = [] + + if cls._asset_processor is None: + if not process_utils.process_exists("AssetProcessor", ignore_extensions=True): + cls._asset_processor = AssetProcessor(workspace) + cls._asset_processor.start() + + testdir.makepyfile( + f""" + import pytest + import os + import sys + + from ly_test_tools import LAUNCHERS + from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorTestSuite + + @pytest.mark.SUITE_main + @pytest.mark.parametrize("launcher_platform", ['linux_editor']) + @pytest.mark.parametrize("project", ["AutomatedTesting"]) + class TestAutomation(EditorTestSuite): + {module_class_code} + """) + result = testdir.runpytest(*TestEditorTest.args[2:] + extra_cmd_line) + return result + + def test_batched_two_passing(self, request, workspace, launcher_platform, testdir): + result = self._run_shared_test(testdir, + """ + class test_pass(EditorSharedTest): + import EditorTest_That_Passes as test_module + is_parallelizable = False + + class test_2(EditorSharedTest): + import EditorTest_That_PassesToo as test_module + is_parallelizable = False + """ + ) + # 2 Passes +1(batch runner) + result.assert_outcomes(passed=3) + + def test_batched_one_pass_one_fail(self, request, workspace, launcher_platform, testdir): + result = self._run_shared_test(testdir, + """ + class test_pass(EditorSharedTest): + import EditorTest_That_Passes as test_module + is_parallelizable = False + + class test_fail(EditorSharedTest): + import EditorTest_That_Fails as test_module + is_parallelizable = False + """ + ) + # 1 Fail, 1 Passes +1(batch runner) + result.assert_outcomes(passed=2, failed=1) + + def test_batched_one_pass_one_fail_one_crash(self, request, workspace, launcher_platform, testdir): + result = self._run_shared_test(testdir, + """ + class test_pass(EditorSharedTest): + import EditorTest_That_Passes as test_module + is_parallelizable = False + + class test_fail(EditorSharedTest): + import EditorTest_That_Fails as test_module + is_parallelizable = False + + class test_crash(EditorSharedTest): + import EditorTest_That_Crashes as test_module + is_parallelizable = False + """ + ) + # 2 Fail, 1 Passes + 1(batch runner) + result.assert_outcomes(passed=2, failed=2) + + def test_parallel_two_passing(self, request, workspace, launcher_platform, testdir): + result = self._run_shared_test(testdir, + """ + class test_pass_1(EditorSharedTest): + import EditorTest_That_Passes as test_module + is_batchable = False + + class test_pass_2(EditorSharedTest): + import EditorTest_That_PassesToo as test_module + is_batchable = False + """ + ) + # 2 Passes +1(parallel runner) + result.assert_outcomes(passed=3) + + def test_parallel_one_passing_one_failing_one_crashing(self, request, workspace, launcher_platform, testdir): + result = self._run_shared_test(testdir, + """ + class test_pass(EditorSharedTest): + import EditorTest_That_Passes as test_module + is_batchable = False + + class test_fail(EditorSharedTest): + import EditorTest_That_Fails as test_module + is_batchable = False + + class test_crash(EditorSharedTest): + import EditorTest_That_Crashes as test_module + is_batchable = False + """ + ) + # 2 Fail, 1 Passes + 1(parallel runner) + result.assert_outcomes(passed=2, failed=2) + + def test_parallel_batched_two_passing(self, request, workspace, launcher_platform, testdir): + result = self._run_shared_test(testdir, + """ + class test_pass_1(EditorSharedTest): + import EditorTest_That_Passes as test_module + + class test_pass_2(EditorSharedTest): + import EditorTest_That_PassesToo as test_module + """ + ) + # 2 Passes +1(batched+parallel runner) + result.assert_outcomes(passed=3) + + def test_parallel_batched_one_passing_one_failing_one_crashing(self, request, workspace, launcher_platform, testdir): + result = self._run_shared_test(testdir, + """ + class test_pass(EditorSharedTest): + import EditorTest_That_Passes as test_module + + class test_fail(EditorSharedTest): + import EditorTest_That_Fails as test_module + + class test_crash(EditorSharedTest): + import EditorTest_That_Crashes as test_module + """ + ) + # 2 Fail, 1 Passes + 1(batched+parallel runner) + result.assert_outcomes(passed=2, failed=2) + + def test_selection_2_deselected_1_selected(self, request, workspace, launcher_platform, testdir): + result = self._run_shared_test(testdir, + """ + class test_pass(EditorSharedTest): + import EditorTest_That_Passes as test_module + + class test_fail(EditorSharedTest): + import EditorTest_That_Fails as test_module + + class test_crash(EditorSharedTest): + import EditorTest_That_Crashes as test_module + """, extra_cmd_line=["-k", "fail"] + ) + # 1 Fail + 1 Success(parallel runner) + result.assert_outcomes(failed=1, passed=1) + outcomes = result.parseoutcomes() + deselected = outcomes.get("deselected") + assert deselected == 2 diff --git a/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteWindows_Main.py similarity index 98% rename from AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py rename to AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteWindows_Main.py index 7c7e063951..3643ab8145 100644 --- a/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteWindows_Main.py @@ -15,6 +15,7 @@ import sys import importlib import re +import ly_test_tools from ly_test_tools import LAUNCHERS sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -25,7 +26,11 @@ import ly_test_tools.environment.process_utils as process_utils import argparse, sys -@pytest.mark.SUITE_main +if ly_test_tools.WINDOWS: + pytestmark = pytest.mark.SUITE_main +else: + pytestmark = pytest.mark.skipif(not ly_test_tools.WINDOWS, reason="Only runs on Windows") + @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) class TestEditorTest: diff --git a/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py b/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py index d37623f352..e4552ec119 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py @@ -51,7 +51,7 @@ def create_editor(workspace, launcher_platform=ly_test_tools.HOST_OS_EDITOR, arg Editor is only officially supported on the Windows Platform. :param workspace: lumberyard workspace to use - :param launcher_platform: the platform to target for a launcher (i.e. 'windows_dedicated' for DedicatedWinLauncher) + :param launcher_platform: the platform to target for a launcher (i.e. 'windows_dedicated' for DedicatedWinLauncher) :param args: List of arguments to pass to the launcher's 'args' argument during construction :return: Editor instance """ From 11c31cb5ca0ee09aa78d52bf889efb36d860decf Mon Sep 17 00:00:00 2001 From: evanchia Date: Thu, 21 Oct 2021 10:48:33 -0700 Subject: [PATCH 014/120] removed platform specific tests and consolodated Signed-off-by: evanchia --- .../editor_test_testing/CMakeLists.txt | 16 +- .../TestSuiteLinux_Main.py | 267 ------------------ ...SuiteWindows_Main.py => TestSuite_Main.py} | 12 +- 3 files changed, 10 insertions(+), 285 deletions(-) delete mode 100644 AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteLinux_Main.py rename AutomatedTesting/Gem/PythonTests/editor_test_testing/{TestSuiteWindows_Main.py => TestSuite_Main.py} (96%) diff --git a/AutomatedTesting/Gem/PythonTests/editor_test_testing/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/editor_test_testing/CMakeLists.txt index fac6192673..04a7c6de0b 100644 --- a/AutomatedTesting/Gem/PythonTests/editor_test_testing/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/editor_test_testing/CMakeLists.txt @@ -11,21 +11,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) NAME AutomatedTesting::ParallelEditorTestsWindows TEST_SUITE main TEST_SERIAL - PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuiteWindows_Main.py - PYTEST_MARKS "SUITE_main" - RUNTIME_DEPENDENCIES - AZ::AssetProcessor - Legacy::Editor - AutomatedTesting.Assets - COMPONENT - TestTools - ) - - ly_add_pytest( - NAME AutomatedTesting::ParallelEditorTestsLinux - TEST_SUITE main - TEST_SERIAL - PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuiteLinux_Main.py + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py PYTEST_MARKS "SUITE_main" RUNTIME_DEPENDENCIES AZ::AssetProcessor diff --git a/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteLinux_Main.py b/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteLinux_Main.py deleted file mode 100644 index 8686c43357..0000000000 --- a/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteLinux_Main.py +++ /dev/null @@ -1,267 +0,0 @@ -""" -Copyright (c) Contributors to the Open 3D Engine Project. -For complete copyright and license terms please see the LICENSE at the root of this distribution. - -SPDX-License-Identifier: Apache-2.0 OR MIT -""" - -""" -This suite contains the tests for editor_test utilities. -""" - -import pytest -import os -import sys -import importlib -import re - -import ly_test_tools -from ly_test_tools import LAUNCHERS - -sys.path.append(os.path.dirname(os.path.abspath(__file__))) - -from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorTestSuite, Result -from ly_test_tools.o3de.asset_processor import AssetProcessor -import ly_test_tools.environment.process_utils as process_utils - -import argparse, sys - -if ly_test_tools.LINUX: - pytestmark = pytest.mark.SUITE_main -else: - pytestmark = pytest.mark.skipif(not ly_test_tools.LINUX, reason="Only runs on Linux") - -@pytest.mark.parametrize("launcher_platform", ['linux_editor']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -class TestEditorTest: - - args = None - path = None - @classmethod - def setup_class(cls): - TestEditorTest.args = sys.argv.copy() - build_dir_arg_index = TestEditorTest.args.index("--build-directory") - if build_dir_arg_index < 0: - print("Error: Must pass --build-directory argument in order to run this test") - sys.exit(-2) - - TestEditorTest.args[build_dir_arg_index+1] = os.path.abspath(TestEditorTest.args[build_dir_arg_index+1]) - TestEditorTest.args.append("-s") - TestEditorTest.path = os.path.dirname(os.path.abspath(__file__)) - cls._asset_processor = None - - def teardown_class(cls): - if cls._asset_processor: - cls._asset_processor.stop(1) - cls._asset_processor.teardown() - - # Test runs # - @classmethod - def _run_single_test(cls, testdir, workspace, module_name): - if cls._asset_processor is None: - if not process_utils.process_exists("AssetProcessor", ignore_extensions=True): - cls._asset_processor = AssetProcessor(workspace) - cls._asset_processor.start() - - testdir.makepyfile( - f""" - import pytest - import os - import sys - - from ly_test_tools import LAUNCHERS - from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorTestSuite - - @pytest.mark.SUITE_main - @pytest.mark.parametrize("launcher_platform", ['linux_editor']) - @pytest.mark.parametrize("project", ["AutomatedTesting"]) - class TestAutomation(EditorTestSuite): - class test_single(EditorSingleTest): - import {module_name} as test_module - - """) - result = testdir.runpytest(*TestEditorTest.args[2:]) - - def get_class(module_name): - class test_single(EditorSingleTest): - test_module = importlib.import_module(module_name) - return test_single - - output = "".join(result.outlines) - extracted_results = EditorTestSuite._get_results_using_output([get_class(module_name)], output, output) - extracted_result = next(iter(extracted_results.items())) - return (extracted_result[1], result) - - def test_single_passing_test(self, request, workspace, launcher_platform, testdir): - (extracted_result, result) = TestEditorTest._run_single_test(testdir, workspace, "EditorTest_That_Passes") - result.assert_outcomes(passed=1) - assert isinstance(extracted_result, Result.Pass) - - def test_single_failing_test(self, request, workspace, launcher_platform, testdir): - (extracted_result, result) = TestEditorTest._run_single_test(testdir, workspace, "EditorTest_That_Fails") - result.assert_outcomes(failed=1) - assert isinstance(extracted_result, Result.Fail) - - def test_single_crashing_test(self, request, workspace, launcher_platform, testdir): - (extracted_result, result) = TestEditorTest._run_single_test(testdir, workspace, "EditorTest_That_Crashes") - result.assert_outcomes(failed=1) - assert isinstance(extracted_result, Result.Unknown) - - @classmethod - def _run_shared_test(cls, testdir, module_class_code, extra_cmd_line=None): - if not extra_cmd_line: - extra_cmd_line = [] - - if cls._asset_processor is None: - if not process_utils.process_exists("AssetProcessor", ignore_extensions=True): - cls._asset_processor = AssetProcessor(workspace) - cls._asset_processor.start() - - testdir.makepyfile( - f""" - import pytest - import os - import sys - - from ly_test_tools import LAUNCHERS - from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorTestSuite - - @pytest.mark.SUITE_main - @pytest.mark.parametrize("launcher_platform", ['linux_editor']) - @pytest.mark.parametrize("project", ["AutomatedTesting"]) - class TestAutomation(EditorTestSuite): - {module_class_code} - """) - result = testdir.runpytest(*TestEditorTest.args[2:] + extra_cmd_line) - return result - - def test_batched_two_passing(self, request, workspace, launcher_platform, testdir): - result = self._run_shared_test(testdir, - """ - class test_pass(EditorSharedTest): - import EditorTest_That_Passes as test_module - is_parallelizable = False - - class test_2(EditorSharedTest): - import EditorTest_That_PassesToo as test_module - is_parallelizable = False - """ - ) - # 2 Passes +1(batch runner) - result.assert_outcomes(passed=3) - - def test_batched_one_pass_one_fail(self, request, workspace, launcher_platform, testdir): - result = self._run_shared_test(testdir, - """ - class test_pass(EditorSharedTest): - import EditorTest_That_Passes as test_module - is_parallelizable = False - - class test_fail(EditorSharedTest): - import EditorTest_That_Fails as test_module - is_parallelizable = False - """ - ) - # 1 Fail, 1 Passes +1(batch runner) - result.assert_outcomes(passed=2, failed=1) - - def test_batched_one_pass_one_fail_one_crash(self, request, workspace, launcher_platform, testdir): - result = self._run_shared_test(testdir, - """ - class test_pass(EditorSharedTest): - import EditorTest_That_Passes as test_module - is_parallelizable = False - - class test_fail(EditorSharedTest): - import EditorTest_That_Fails as test_module - is_parallelizable = False - - class test_crash(EditorSharedTest): - import EditorTest_That_Crashes as test_module - is_parallelizable = False - """ - ) - # 2 Fail, 1 Passes + 1(batch runner) - result.assert_outcomes(passed=2, failed=2) - - def test_parallel_two_passing(self, request, workspace, launcher_platform, testdir): - result = self._run_shared_test(testdir, - """ - class test_pass_1(EditorSharedTest): - import EditorTest_That_Passes as test_module - is_batchable = False - - class test_pass_2(EditorSharedTest): - import EditorTest_That_PassesToo as test_module - is_batchable = False - """ - ) - # 2 Passes +1(parallel runner) - result.assert_outcomes(passed=3) - - def test_parallel_one_passing_one_failing_one_crashing(self, request, workspace, launcher_platform, testdir): - result = self._run_shared_test(testdir, - """ - class test_pass(EditorSharedTest): - import EditorTest_That_Passes as test_module - is_batchable = False - - class test_fail(EditorSharedTest): - import EditorTest_That_Fails as test_module - is_batchable = False - - class test_crash(EditorSharedTest): - import EditorTest_That_Crashes as test_module - is_batchable = False - """ - ) - # 2 Fail, 1 Passes + 1(parallel runner) - result.assert_outcomes(passed=2, failed=2) - - def test_parallel_batched_two_passing(self, request, workspace, launcher_platform, testdir): - result = self._run_shared_test(testdir, - """ - class test_pass_1(EditorSharedTest): - import EditorTest_That_Passes as test_module - - class test_pass_2(EditorSharedTest): - import EditorTest_That_PassesToo as test_module - """ - ) - # 2 Passes +1(batched+parallel runner) - result.assert_outcomes(passed=3) - - def test_parallel_batched_one_passing_one_failing_one_crashing(self, request, workspace, launcher_platform, testdir): - result = self._run_shared_test(testdir, - """ - class test_pass(EditorSharedTest): - import EditorTest_That_Passes as test_module - - class test_fail(EditorSharedTest): - import EditorTest_That_Fails as test_module - - class test_crash(EditorSharedTest): - import EditorTest_That_Crashes as test_module - """ - ) - # 2 Fail, 1 Passes + 1(batched+parallel runner) - result.assert_outcomes(passed=2, failed=2) - - def test_selection_2_deselected_1_selected(self, request, workspace, launcher_platform, testdir): - result = self._run_shared_test(testdir, - """ - class test_pass(EditorSharedTest): - import EditorTest_That_Passes as test_module - - class test_fail(EditorSharedTest): - import EditorTest_That_Fails as test_module - - class test_crash(EditorSharedTest): - import EditorTest_That_Crashes as test_module - """, extra_cmd_line=["-k", "fail"] - ) - # 1 Fail + 1 Success(parallel runner) - result.assert_outcomes(failed=1, passed=1) - outcomes = result.parseoutcomes() - deselected = outcomes.get("deselected") - assert deselected == 2 diff --git a/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteWindows_Main.py b/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py similarity index 96% rename from AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteWindows_Main.py rename to AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py index 3643ab8145..9a78afdeb7 100644 --- a/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuiteWindows_Main.py +++ b/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py @@ -31,7 +31,13 @@ if ly_test_tools.WINDOWS: else: pytestmark = pytest.mark.skipif(not ly_test_tools.WINDOWS, reason="Only runs on Windows") -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +def get_editor_launcher_platform(): + if ly_test_tools.WINDOWS: + return "windows_editor" + else: + return "linux_editor" + +@pytest.mark.parametrize("launcher_platform", [get_editor_launcher_platform()]) @pytest.mark.parametrize("project", ["AutomatedTesting"]) class TestEditorTest: @@ -73,7 +79,7 @@ class TestEditorTest: from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorTestSuite @pytest.mark.SUITE_main - @pytest.mark.parametrize("launcher_platform", ['windows_editor']) + @pytest.mark.parametrize("launcher_platform", [{get_editor_launcher_platform()}]) @pytest.mark.parametrize("project", ["AutomatedTesting"]) class TestAutomation(EditorTestSuite): class test_single(EditorSingleTest): @@ -127,7 +133,7 @@ class TestEditorTest: from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorTestSuite @pytest.mark.SUITE_main - @pytest.mark.parametrize("launcher_platform", ['windows_editor']) + @pytest.mark.parametrize("launcher_platform", [{get_editor_launcher_platform()}]) @pytest.mark.parametrize("project", ["AutomatedTesting"]) class TestAutomation(EditorTestSuite): {module_class_code} From 6168528964329f5eef84c029dc429f97d8113292 Mon Sep 17 00:00:00 2001 From: evanchia Date: Thu, 21 Oct 2021 14:44:43 -0700 Subject: [PATCH 015/120] fixing unit tests Signed-off-by: evanchia --- Tools/LyTestTools/tests/unit/test_o3de_editor_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py b/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py index f16f7533d4..3f6c23ea3d 100644 --- a/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py +++ b/Tools/LyTestTools/tests/unit/test_o3de_editor_test.py @@ -486,7 +486,7 @@ class TestUtils(unittest.TestCase): mock_test.__name__ = 'mock_test_name' mock_test_list = [mock_test] mock_output = 'JSON_START(' \ - '{"name": "mock_module_name", "output": "mock_std_out", "failed": "mock_fail_data"}' \ + '{"name": "mock_module_name", "output": "mock_std_out", "failed": "mock_fail_data", "success": ""}' \ ')JSON_END' mock_fail = mock.MagicMock() mock_create.return_value = mock_fail @@ -534,10 +534,10 @@ class TestUtils(unittest.TestCase): '{"name": "mock_module_name_pass", "output": "mock_std_out", "success": "mock_success_data"}' \ ')JSON_END' \ 'JSON_START(' \ - '{"name": "mock_module_name_fail", "output": "mock_std_out", "failed": "mock_fail_data"}' \ + '{"name": "mock_module_name_fail", "output": "mock_std_out", "failed": "mock_fail_data", "success": ""}' \ ')JSON_END' \ 'JSON_START(' \ - '{"name": "mock_module_name_unknown", "output": "mock_std_out", "failed": "mock_fail_data"}' \ + '{"name": "mock_module_name_unknown", "output": "mock_std_out", "failed": "mock_fail_data", "success": ""}' \ ')JSON_END' mock_editor_log = 'JSON_START(' \ '{"name": "mock_module_name_pass"}' \ From f61c37786f0733e31f722e1655cfcbb1d036b498 Mon Sep 17 00:00:00 2001 From: evanchia Date: Thu, 21 Oct 2021 16:40:48 -0700 Subject: [PATCH 016/120] fixing unit test with local time issue Signed-off-by: evanchia --- .../tests/unit/test_editor_test_utils.py | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Tools/LyTestTools/tests/unit/test_editor_test_utils.py b/Tools/LyTestTools/tests/unit/test_editor_test_utils.py index b096f6d6b0..56000364d1 100644 --- a/Tools/LyTestTools/tests/unit/test_editor_test_utils.py +++ b/Tools/LyTestTools/tests/unit/test_editor_test_utils.py @@ -78,6 +78,22 @@ class TestEditorTestUtils(unittest.TestCase): assert expected == editor_test_utils.retrieve_crash_output(0, mock_workspace, 0) + @mock.patch('os.path.getmtime', mock.MagicMock()) + @mock.patch('os.rename') + @mock.patch('time.strftime') + @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') + @mock.patch('os.path.exists') + def test_CycleCrashReport_DmpExists_NamedCorrectly(self, mock_exists, mock_retrieve_log_path, mock_strftime, + mock_rename): + mock_exists.side_effect = [False, True] + mock_retrieve_log_path.return_value = 'mock_log_path' + mock_workspace = mock.MagicMock() + mock_strftime.return_value = 'mock_strftime' + + editor_test_utils.cycle_crash_report(0, mock_workspace) + mock_rename.assert_called_once_with(os.path.join('mock_log_path', 'error.dmp'), + os.path.join('mock_log_path', 'error_mock_strftime.dmp')) + @mock.patch('os.rename') @mock.patch('os.path.getmtime') @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') @@ -93,21 +109,6 @@ class TestEditorTestUtils(unittest.TestCase): mock_rename.assert_called_once_with(os.path.join('mock_log_path', 'error.log'), os.path.join('mock_log_path', 'error_1969_12_31_16_00_01.log')) - @mock.patch('os.rename') - @mock.patch('os.path.getmtime') - @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') - @mock.patch('os.path.exists') - def test_CycleCrashReport_DmpExists_NamedCorrectly(self, mock_exists, mock_retrieve_log_path, mock_getmtime, - mock_rename): - mock_exists.side_effect = [False, True] - mock_retrieve_log_path.return_value = 'mock_log_path' - mock_workspace = mock.MagicMock() - mock_getmtime.return_value = 1 - - editor_test_utils.cycle_crash_report(0, mock_workspace) - mock_rename.assert_called_once_with(os.path.join('mock_log_path', 'error.dmp'), - os.path.join('mock_log_path', 'error_1969_12_31_16_00_01.dmp')) - @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') @mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock()) def test_RetrieveEditorLogContent_CrashLogExists_ReturnsLogInfo(self, mock_retrieve_log_path): From 2fe1e513468f7c39aa79d831de93be75c9af4760 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Thu, 21 Oct 2021 18:40:31 -0700 Subject: [PATCH 017/120] generated a package for debug/profile/release monolithic-profile/release Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- Registry/CMakeLists.txt | 11 +- cmake/Install.cmake | 29 ++++- cmake/Packaging.cmake | 122 ++++++++---------- cmake/Platform/Common/Install_common.cmake | 50 ++++++- cmake/Platform/Linux/Install_linux.cmake | 2 + .../Platform/Windows/Packaging_windows.cmake | 7 +- 6 files changed, 135 insertions(+), 86 deletions(-) diff --git a/Registry/CMakeLists.txt b/Registry/CMakeLists.txt index 773adac07f..df309ba657 100644 --- a/Registry/CMakeLists.txt +++ b/Registry/CMakeLists.txt @@ -12,6 +12,11 @@ endif() ly_install_directory(DIRECTORIES .) -ly_install_directory(DIRECTORIES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/Registry - DESTINATION ${runtime_output_directory} -) +foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) + string(TOUPPER ${conf} UCONF) + string(REPLACE "$" "${conf}" output ${runtime_output_directory}) + ly_install_directory(DIRECTORIES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${conf}/Registry + DESTINATION ${output} + COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} + ) +endforeach() diff --git a/cmake/Install.cmake b/cmake/Install.cmake index 9bc908f74a..26535d89be 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -18,6 +18,7 @@ endif() # \arg:DIRECTORIES directories to install # \arg:DESTINATION (optional) destination to install the directory to (relative to CMAKE_PREFIX_PATH) # \arg:EXCLUDE_PATTERNS (optional) patterns to exclude +# \arg:COMPONENT (optional) component to use (defaults to CMAKE_INSTALL_DEFAULT_COMPONENT_NAME) # \arg:VERBATIM (optional) copies the directories as they are, this excludes the default exclude patterns # # \notes: @@ -34,7 +35,7 @@ function(ly_install_directory) endif() set(options VERBATIM) - set(oneValueArgs DESTINATION) + set(oneValueArgs DESTINATION COMPONENT) set(multiValueArgs DIRECTORIES EXCLUDE_PATTERNS) cmake_parse_arguments(ly_install_directory "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -77,11 +78,21 @@ function(ly_install_directory) list(APPEND exclude_patterns PATTERN *.egg-info EXCLUDE) endif() - install(DIRECTORY ${directory} - DESTINATION ${ly_install_directory_DESTINATION} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} # use the deafult for the time being - ${exclude_patterns} - ) + if(ly_install_directory_COMPONENT) + install(DIRECTORY ${directory} + DESTINATION ${ly_install_directory_DESTINATION} + COMPONENT ${ly_install_directory_COMPONENT} + ${exclude_patterns} + ) + else() + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") + install(DIRECTORY ${directory} + DESTINATION ${ly_install_directory_DESTINATION} + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} + ${exclude_patterns} + ) + install(CODE "endif()") + endif() endforeach() endfunction() @@ -126,10 +137,12 @@ function(ly_install_files) set(install_type FILES) endif() + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(${install_type} ${files} DESTINATION ${ly_install_files_DESTINATION} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} # use the default for the time being ) + install(CODE "endif()") endfunction() @@ -144,9 +157,11 @@ function(ly_install_run_code CODE) return() endif() + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(CODE ${CODE} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} # use the default for the time being ) + install(CODE "endif()") endfunction() @@ -161,8 +176,10 @@ function(ly_install_run_script SCRIPT) return() endif() + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(SCRIPT ${SCRIPT} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} # use the default for the time being ) + install(CODE "endif()") endfunction() diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index 25f9b87cbc..a87b0c4361 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -134,10 +134,12 @@ if(NOT EXISTS ${_cmake_package_dest}) endif() endif() +install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(FILES ${_cmake_package_dest} DESTINATION ./Tools/Redistributables/CMake COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) +install(CODE "endif()") # the version string and git tags are intended to be synchronized so it should be safe to use that instead # of directly calling into git which could get messy in certain scenarios @@ -158,10 +160,12 @@ if(${CPACK_PACKAGE_VERSION} VERSION_GREATER "0.0.0.0") list(POP_FRONT _status _status_code) if (${_status_code} EQUAL 0 AND EXISTS ${_3rd_party_license_dest}) + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(FILES ${_3rd_party_license_dest} DESTINATION . COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + install(CODE "endif()") else() file(REMOVE ${_3rd_party_license_dest}) message(FATAL_ERROR "Failed to acquire the 3rd Party license manifest file at ${_3rd_party_license_url}. Error: ${_status}") @@ -204,76 +208,73 @@ endif() # IMPORTANT: required to be included AFTER setting all property overrides include(CPack REQUIRED) -function(ly_configure_cpack_component ly_configure_cpack_component_NAME) - - set(options REQUIRED DISABLED) - set(oneValueArgs DISPLAY_NAME DESCRIPTION LICENSE_NAME LICENSE_FILE DEPENDS) - set(multiValueArgs) - - cmake_parse_arguments(ly_configure_cpack_component "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - unset(component_type) - - if(ly_configure_cpack_component_DISABLED) - list(APPEND component_type DISABLED) - endif() - if(ly_configure_cpack_component_REQUIRED) - list(APPEND component_type REQUIRED) - endif() - - set(license_name ${DEFAULT_LICENSE_NAME}) - set(license_file ${DEFAULT_LICENSE_FILE}) - - if(ly_configure_cpack_component_LICENSE_NAME AND ly_configure_cpack_component_LICENSE_FILE) - set(license_name ${ly_configure_cpack_component_LICENSE_NAME}) - set(license_file ${ly_configure_cpack_component_LICENSE_FILE}) - elseif(ly_configure_cpack_component_LICENSE_NAME OR ly_configure_cpack_component_LICENSE_FILE) - message(FATAL_ERROR "Invalid argument configuration. Both LICENSE_NAME and LICENSE_FILE must be set for ly_configure_cpack_component") - endif() - - cpack_add_component( - ${ly_configure_cpack_component_NAME} ${component_type} - DISPLAY_NAME ${ly_configure_cpack_component_DISPLAY_NAME} - DESCRIPTION ${ly_configure_cpack_component_DESCRIPTION} - ) -endfunction() - # configure ALL components here -ly_configure_cpack_component( - ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} REQUIRED - DISPLAY_NAME "${PROJECT_NAME}" - DESCRIPTION "${PROJECT_NAME} Headers, scripts and common files" -) +file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" " +set(CPACK_COMPONENTS_ALL ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) +set(CPACK_COMPONENT_${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_DISPLAY_NAME \"${PROJECT_NAME}\") +set(CPACK_COMPONENT_${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_DESCRIPTION \"${PROJECT_NAME} Headers, scripts and common files\") +set(CPACK_COMPONENT_${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_REQUIRED TRUE) +set(CPACK_COMPONENT_${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_DISABLED FALSE) -file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" "set(LY_CPACK_COMPONENTS_ALL ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME})\n") +include(CPackComponents.cmake) +") + +# Generate a file (CPackComponents.config) that we will include that defines the components +# for this build permutation. This way we can get components for other permutations being passed +# through LY_INSTALL_EXTERNAL_BUILD_DIRS +unset(cpack_components_contents) + +set(required "FALSE") +set(disabled "FALSE") +if(${LY_INSTALL_PERMUTATION_COMPONENT} STREQUAL DEFAULT) + set(required "TRUE") +else() + set(disabled "TRUE") +endif() +string(APPEND cpack_components_contents " +list(APPEND CPACK_COMPONENTS_ALL ${LY_INSTALL_PERMUTATION_COMPONENT}) +set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_DISPLAY_NAME \"${PROJECT_NAME} (${LY_BUILD_PERMUTATION})\") +set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_DESCRIPTION \"${PROJECT_NAME} scripts and common files for ${LY_BUILD_PERMUTATION}\") +set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_DEPENDS ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) +set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_REQUIRED ${required}) +set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_DISABLED ${disabled}) +") foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) string(TOUPPER ${conf} UCONF) - unset(flags) - if(${conf} STREQUAL profile AND ${LY_BUILD_PERMUTATION} STREQUAL Default) - set(flags REQUIRED) + set(required "FALSE") + set(disabled "FALSE") + if(${conf} STREQUAL profile AND ${LY_INSTALL_PERMUTATION_COMPONENT} STREQUAL DEFAULT) + set(required "TRUE") else() - set(flags DISABLED) + set(disabled "TRUE") endif() unset(permutation_description) - if(${LY_BUILD_PERMUTATION} STREQUAL Monolithic) - set(permutation_description " monolithic ") + if(${LY_INSTALL_PERMUTATION_COMPONENT} STREQUAL MONOLITHIC) + set(permutation_description "monolithic ") endif() # Inject a check to not declare components that have not been built. We are using AzCore since that is a # common target that will always be build, in every permutation and configuration - file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" - "if(EXISTS \"${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${conf}/${CMAKE_STATIC_LIBRARY_PREFIX}AzCore${CMAKE_STATIC_LIBRARY_SUFFIX}\")\n") - ly_configure_cpack_component( - ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} ${flags} - DISPLAY_NAME "${PROJECT_NAME} (${conf})" - DESCRIPTION "${PROJECT_NAME} Libraries and Tools in${permutation_description}${conf}" - ) - file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" -"list(APPEND LY_CPACK_COMPONENTS_ALL ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF}) -endif()\n") + string(APPEND cpack_components_contents " +if(EXISTS \"${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${conf}/${CMAKE_STATIC_LIBRARY_PREFIX}AzCore${CMAKE_STATIC_LIBRARY_SUFFIX}\") + list(APPEND CPACK_COMPONENTS_ALL ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}) + set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DISPLAY_NAME \"${PROJECT_NAME} (${permutation_description}${conf})\") + set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DESCRIPTION \"${PROJECT_NAME} Libraries and Applications in ${permutation_description}${conf}\") + set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DEPENDS ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}) + set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_REQUIRED ${required}) + set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DISABLED ${disabled}) +endif() +") +endforeach() +file(WRITE "${CMAKE_BINARY_DIR}/CPackComponents.cmake" ${cpack_components_contents}) +# Inject other build directories +foreach(external_dir ${LY_INSTALL_EXTERNAL_BUILD_DIRS}) + file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" + "include(${external_dir}/CPackComponents.cmake)\n" + ) endforeach() if(LY_INSTALLER_DOWNLOAD_URL) @@ -286,12 +287,3 @@ if(LY_INSTALLER_DOWNLOAD_URL) ALL ) endif() - -# Inject other build directories -foreach(external_dir ${LY_INSTALL_EXTERNAL_BUILD_DIRS}) - file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" - "include(${external_dir}/CPackConfig.cmake)\n" - ) -endforeach() - -file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" "set(CPACK_COMPONENTS_ALL \${LY_CPACK_COMPONENTS_ALL})\n") diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index b1cd5095b8..71aacd5e31 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -33,14 +33,13 @@ define_property(TARGET PROPERTY LY_INSTALL_GENERATE_RUN_TARGET # CPack is able to put the two together by taking Core from one permutation and then taking # each permutation. -ly_set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core) +ly_set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME CORE) if(LY_MONOLITHIC_GAME) set(LY_BUILD_PERMUTATION Monolithic) - set(LY_INSTALL_PERMUTATION_COMPONENT Monolithic) else() set(LY_BUILD_PERMUTATION Default) - set(LY_INSTALL_PERMUTATION_COMPONENT Default) endif() +string(TOUPPER ${LY_BUILD_PERMUTATION} LY_INSTALL_PERMUTATION_COMPONENT) cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE runtime_output_directory) cmake_path(RELATIVE_PATH CMAKE_LIBRARY_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE library_output_directory) @@ -84,7 +83,8 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar cmake_path(RELATIVE_PATH include_directory BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE rel_include_dir) cmake_path(APPEND rel_include_dir "..") cmake_path(NORMAL_PATH rel_include_dir OUTPUT_VARIABLE destination_dir) - + + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(DIRECTORY ${include_directory} DESTINATION ${destination_dir} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} @@ -95,6 +95,7 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar PATTERN *.hxx PATTERN *.jinja # LyAutoGen files ) + install(CODE "endif()") endif() endforeach() endif() @@ -337,10 +338,13 @@ function(ly_setup_subdirectory absolute_target_source_dir) @cmake_copyright_comment@ include(Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) ]] @ONLY) + + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(FILES "${target_install_source_dir}/CMakeLists.txt" DESTINATION ${relative_target_source_dir} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + install(CODE "endif()") # 2. For this platform file, create a Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake file # that will include different configuration permutations (e.g. monolithic vs non-monolithic) @@ -352,10 +356,12 @@ else() include(Platform/${PAL_PLATFORM_NAME}/Default/permutation.cmake) endif() ]]) + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(FILES "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake" DESTINATION ${relative_target_source_dir}/Platform/${PAL_PLATFORM_NAME} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + install(CODE "endif()") # 3. For this configuration permutation, generate a Platform/${PAL_PLATFORM_NAME}/${permutation}/permutation.cmake # that will declare the target and configure it @@ -389,6 +395,7 @@ function(ly_setup_o3de_install) ly_setup_assets() # Misc + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(FILES ${LY_ROOT_FOLDER}/ctest_pytest.ini ${LY_ROOT_FOLDER}/LICENSE.txt @@ -396,10 +403,13 @@ function(ly_setup_o3de_install) DESTINATION . COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + install(CODE "endif()") # Inject other build directories foreach(external_dir ${LY_INSTALL_EXTERNAL_BUILD_DIRS}) - install(CODE "include(${external_dir}/cmake_install.cmake)") + install(CODE "set(LY_CORE_COMPONENT_ALREADY_INCLUDED TRUE) +include(${external_dir}/cmake_install.cmake)" +ALL_COMPONENTS) endforeach() if(COMMAND ly_post_install_steps) @@ -411,6 +421,7 @@ endfunction() #! ly_setup_cmake_install: install the "cmake" folder function(ly_setup_cmake_install) + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(DIRECTORY "${LY_ROOT_FOLDER}/cmake" DESTINATION . COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} @@ -418,11 +429,16 @@ function(ly_setup_cmake_install) PATTERN "Findo3de.cmake" EXCLUDE REGEX "3rdParty/Platform\/.*\/BuiltInPackages_.*\.cmake" EXCLUDE ) + install(CODE "endif()") + # Connect configuration types + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(FILES "${LY_ROOT_FOLDER}/cmake/install/ConfigurationTypes.cmake" DESTINATION cmake COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + install(CODE "endif()") + # generate each ConfigurationType_.cmake file and install it under that configuration foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) string(TOUPPER ${conf} UCONF) @@ -430,11 +446,13 @@ function(ly_setup_cmake_install) "${CMAKE_BINARY_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/ConfigurationTypes_${conf}.cmake" @ONLY ) + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(FILES "${CMAKE_BINARY_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/ConfigurationTypes_${conf}.cmake" DESTINATION cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} CONFIGURATIONS ${conf} ) + install(CODE "endif()") endforeach() # Transform the LY_EXTERNAL_SUBDIRS list into a json array @@ -454,12 +472,14 @@ function(ly_setup_cmake_install) configure_file(${LY_ROOT_FOLDER}/cmake/install/engine.json.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/engine.json @ONLY) + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(FILES "${LY_ROOT_FOLDER}/CMakeLists.txt" "${CMAKE_CURRENT_BINARY_DIR}/cmake/engine.json" DESTINATION . COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + install(CODE "endif()") # Collect all Find files that were added with ly_add_external_target_path unset(additional_find_files) @@ -477,6 +497,8 @@ function(ly_setup_cmake_install) list(APPEND additional_platform_files "${plat_files}") endforeach() endforeach() + + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(FILES ${additional_find_files} DESTINATION cmake/3rdParty COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} @@ -485,6 +507,7 @@ function(ly_setup_cmake_install) DESTINATION cmake/3rdParty/Platform/${PAL_PLATFORM_NAME} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + install(CODE "endif()") # Findo3de.cmake file: we generate a different Findo3de.camke file than the one we have in cmake. This one is going to expose all # targets that are pre-built @@ -498,10 +521,12 @@ function(ly_setup_cmake_install) endforeach() configure_file(${LY_ROOT_FOLDER}/cmake/install/Findo3de.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake @ONLY) + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake" DESTINATION cmake COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + install(CODE "endif()") # BuiltInPackage_.cmake: since associations could happen in any cmake file across the engine. We collect # all the associations in ly_associate_package and then generate them into BuiltInPackages_.cmake. This @@ -518,10 +543,12 @@ function(ly_setup_cmake_install) file(GENERATE OUTPUT ${pal_builtin_file} CONTENT ${builtinpackages} ) + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(FILES "${pal_builtin_file}" DESTINATION cmake/3rdParty/Platform/${PAL_PLATFORM_NAME} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + install(CODE "endif()") endfunction() @@ -536,9 +563,12 @@ function(ly_setup_runtime_dependencies) string(TOUPPER ${conf} UCONF) install(CODE "function(ly_copy source_file target_directory) - file(COPY \"\${source_file}\" DESTINATION \"\${target_directory}\" FILE_PERMISSIONS ${LY_COPY_PERMISSIONS}) + cmake_path(GET source_file FILENAME file_name) + if(NOT EXISTS ${target_directory}/${file_name}) + file(COPY \"\${source_file}\" DESTINATION \"\${target_directory}\" FILE_PERMISSIONS ${LY_COPY_PERMISSIONS}) + endif() endfunction()" - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_${UCONF} + COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} ) endforeach() endif() @@ -672,17 +702,23 @@ function(ly_setup_assets) if (NOT gem_install_dest_dir) cmake_path(SET gem_install_dest_dir .) endif() + if(IS_DIRECTORY ${gem_absolute_path}) + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(DIRECTORY "${gem_absolute_path}" DESTINATION ${gem_install_dest_dir} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + install(CODE "endif()") elseif (EXISTS ${gem_absolute_path}) + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(FILES ${gem_absolute_path} DESTINATION ${gem_install_dest_dir} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + install(CODE "endif()") endif() + endforeach() endforeach() diff --git a/cmake/Platform/Linux/Install_linux.cmake b/cmake/Platform/Linux/Install_linux.cmake index dea26e5872..68016872aa 100644 --- a/cmake/Platform/Linux/Install_linux.cmake +++ b/cmake/Platform/Linux/Install_linux.cmake @@ -22,9 +22,11 @@ endfunction()]]) function(ly_install_code_function_override) string(CONFIGURE "${ly_copy_template}" ly_copy_function_linux @ONLY) + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") install(CODE "${ly_copy_function_linux}" COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + install(CODE "endif()") endfunction() include(cmake/Platform/Common/Install_common.cmake) diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 65b5751ebe..5981372ed2 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -28,11 +28,8 @@ set(_cmake_package_name "cmake-${CPACK_DESIRED_CMAKE_VERSION}-windows-x86_64") set(CPACK_CMAKE_PACKAGE_FILE "${_cmake_package_name}.zip") set(CPACK_CMAKE_PACKAGE_HASH "15a49e2ab81c1822d75b1b1a92f7863f58e31f6d6aac1c4103eef2b071be3112") -# workaround for shortening the path cpack installs to by stripping the platform directory and forcing monolithic -# mode to strip out component folders. this unfortunately is the closest we can get to changing the install location -# as CPACK_PACKAGING_INSTALL_PREFIX/CPACK_SET_DESTDIR isn't supported for the WiX generator -#set(CPACK_TOPLEVEL_TAG "") -#set(CPACK_MONOLITHIC_INSTALL ON) +# workaround for shortening the path cpack installs to by stripping the platform directory +set(CPACK_TOPLEVEL_TAG "") # CPack will generate the WiX product/upgrade GUIDs further down the chain if they weren't supplied # however, they are unique for each run. instead, let's do the auto generation here and add it to From 29ecb0a246217455fdfd8ad7a0e2c2f9e6ec3803 Mon Sep 17 00:00:00 2001 From: evanchia Date: Thu, 21 Oct 2021 19:49:42 -0700 Subject: [PATCH 018/120] fixing more AR only unit test failures Signed-off-by: evanchia --- Tools/LyTestTools/tests/unit/test_editor_test_utils.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Tools/LyTestTools/tests/unit/test_editor_test_utils.py b/Tools/LyTestTools/tests/unit/test_editor_test_utils.py index 56000364d1..bf7fae7189 100644 --- a/Tools/LyTestTools/tests/unit/test_editor_test_utils.py +++ b/Tools/LyTestTools/tests/unit/test_editor_test_utils.py @@ -94,20 +94,21 @@ class TestEditorTestUtils(unittest.TestCase): mock_rename.assert_called_once_with(os.path.join('mock_log_path', 'error.dmp'), os.path.join('mock_log_path', 'error_mock_strftime.dmp')) + @mock.patch('os.path.getmtime', mock.MagicMock()) @mock.patch('os.rename') - @mock.patch('os.path.getmtime') + @mock.patch('time.strftime') @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') @mock.patch('os.path.exists') - def test_CycleCrashReport_LogExists_NamedCorrectly(self, mock_exists, mock_retrieve_log_path, mock_getmtime, + def test_CycleCrashReport_LogExists_NamedCorrectly(self, mock_exists, mock_retrieve_log_path, mock_strftime, mock_rename): mock_exists.side_effect = [True, False] mock_retrieve_log_path.return_value = 'mock_log_path' mock_workspace = mock.MagicMock() - mock_getmtime.return_value = 1 + mock_strftime.return_value = 'mock_strftime' editor_test_utils.cycle_crash_report(0, mock_workspace) mock_rename.assert_called_once_with(os.path.join('mock_log_path', 'error.log'), - os.path.join('mock_log_path', 'error_1969_12_31_16_00_01.log')) + os.path.join('mock_log_path', 'error_mock_strftime.log')) @mock.patch('ly_test_tools.o3de.editor_test_utils.retrieve_log_path') @mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock()) From c345733057bef4fe513c2c22af7dc6f37157d698 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 22 Oct 2021 15:30:32 -0700 Subject: [PATCH 019/120] improving some text messages Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/Packaging.cmake | 13 ++++--------- cmake/Platform/Windows/Packaging_windows.cmake | 3 +++ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index a87b0c4361..a810bf8b56 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -211,7 +211,7 @@ include(CPack REQUIRED) # configure ALL components here file(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" " set(CPACK_COMPONENTS_ALL ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) -set(CPACK_COMPONENT_${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_DISPLAY_NAME \"${PROJECT_NAME}\") +set(CPACK_COMPONENT_${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_DISPLAY_NAME \"Common files\") set(CPACK_COMPONENT_${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_DESCRIPTION \"${PROJECT_NAME} Headers, scripts and common files\") set(CPACK_COMPONENT_${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_REQUIRED TRUE) set(CPACK_COMPONENT_${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}_DISABLED FALSE) @@ -233,7 +233,7 @@ else() endif() string(APPEND cpack_components_contents " list(APPEND CPACK_COMPONENTS_ALL ${LY_INSTALL_PERMUTATION_COMPONENT}) -set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_DISPLAY_NAME \"${PROJECT_NAME} (${LY_BUILD_PERMUTATION})\") +set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_DISPLAY_NAME \"${LY_BUILD_PERMUTATION} common files\") set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_DESCRIPTION \"${PROJECT_NAME} scripts and common files for ${LY_BUILD_PERMUTATION}\") set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_DEPENDS ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_REQUIRED ${required}) @@ -250,18 +250,13 @@ foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) set(disabled "TRUE") endif() - unset(permutation_description) - if(${LY_INSTALL_PERMUTATION_COMPONENT} STREQUAL MONOLITHIC) - set(permutation_description "monolithic ") - endif() - # Inject a check to not declare components that have not been built. We are using AzCore since that is a # common target that will always be build, in every permutation and configuration string(APPEND cpack_components_contents " if(EXISTS \"${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${conf}/${CMAKE_STATIC_LIBRARY_PREFIX}AzCore${CMAKE_STATIC_LIBRARY_SUFFIX}\") list(APPEND CPACK_COMPONENTS_ALL ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}) - set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DISPLAY_NAME \"${PROJECT_NAME} (${permutation_description}${conf})\") - set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DESCRIPTION \"${PROJECT_NAME} Libraries and Applications in ${permutation_description}${conf}\") + set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DISPLAY_NAME \"Binaries for ${LY_BUILD_PERMUTATION} ${conf}\") + set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DESCRIPTION \"${PROJECT_NAME} libraries and applications for ${LY_BUILD_PERMUTATION} ${conf}\") set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DEPENDS ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}) set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_REQUIRED ${required}) set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DISABLED ${disabled}) diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 5981372ed2..630817370f 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -105,6 +105,9 @@ set(_raw_text_license [[ #(loc.InstallEulaAcceptance) ]]) +# if we are doing an offline installer, there is a limit in size the wix tooling can handle and produces +# issues for our current sizes. If the installer is offline, disable the curstom wix generator generating +# a msi instead. if(LY_INSTALLER_DOWNLOAD_URL) set(WIX_THEME_WARNING_IMAGE ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/warning.png) From ec78c1c00313f7f3f81658c4ea746c2939cab991 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 22 Oct 2021 15:31:46 -0700 Subject: [PATCH 020/120] Removes unity=true since its the default Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- .../Windows/ProjectBuilderWorker_windows.cpp | 3 +- .../build/Platform/Android/build_config.json | 10 +++--- .../build/Platform/Linux/build_config.json | 18 +++++------ scripts/build/Platform/Mac/build_config.json | 14 ++++---- .../build/Platform/Windows/build_config.json | 32 +++++++++---------- .../Platform/Windows/installer_windows.cmd | 4 +-- .../Windows/package_build_config.json | 2 +- scripts/build/Platform/iOS/build_config.json | 10 +++--- 8 files changed, 46 insertions(+), 47 deletions(-) diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp index 075c6de774..b6b37b222d 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp @@ -21,8 +21,7 @@ namespace O3DE::ProjectManager return AZ::Success(QStringList{ ProjectCMakeCommand, "-B", targetBuildPath, "-S", m_projectInfo.m_path, - QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath), - "-DLY_UNITY_BUILD=ON" } ); + QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath) } ); } AZ::Outcome ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const diff --git a/scripts/build/Platform/Android/build_config.json b/scripts/build/Platform/Android/build_config.json index c1fbb1dd87..5e2da44a3c 100644 --- a/scripts/build/Platform/Android/build_config.json +++ b/scripts/build/Platform/Android/build_config.json @@ -35,7 +35,7 @@ "PARAMETERS": { "CONFIGURATION":"debug", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\"", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -60,7 +60,7 @@ "PARAMETERS": { "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\"", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -93,7 +93,7 @@ "PARAMETERS": { "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"build\\windows_vs2019", - "CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"AssetProcessorBatch", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -112,7 +112,7 @@ "PARAMETERS": { "CONFIGURATION":"release", "OUTPUT_DIRECTORY":"build\\android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\"", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" @@ -128,7 +128,7 @@ "PARAMETERS": { "CONFIGURATION":"release", "OUTPUT_DIRECTORY":"build\\mono_android", - "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_MONOLITHIC_GAME=TRUE", "CMAKE_LY_PROJECTS":"AutomatedTesting", "CMAKE_TARGET":"all", "CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!" diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index ee4c27b0a9..c99d0d128d 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -37,7 +37,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -53,7 +53,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -80,7 +80,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest", @@ -110,7 +110,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "AssetProcessorBatch", "ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch", @@ -142,7 +142,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", "CTEST_OPTIONS": "-L (SUITE_periodic)", @@ -162,7 +162,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", "CTEST_OPTIONS": "-L (SUITE_sandbox)" @@ -178,7 +178,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_benchmark", "CTEST_OPTIONS": "-L (SUITE_benchmark)", @@ -195,7 +195,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -210,7 +210,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/mono_linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_MONOLITHIC_GAME=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } diff --git a/scripts/build/Platform/Mac/build_config.json b/scripts/build/Platform/Mac/build_config.json index 7eb3cb5699..b57cc522bc 100644 --- a/scripts/build/Platform/Mac/build_config.json +++ b/scripts/build/Platform/Mac/build_config.json @@ -37,7 +37,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G Xcode", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD" } @@ -51,7 +51,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G Xcode", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD" } @@ -81,7 +81,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G Xcode", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "AssetProcessorBatch", "ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch", @@ -99,7 +99,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G Xcode", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", "CTEST_OPTIONS": "-L \"(SUITE_periodic)\"", @@ -116,7 +116,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G Xcode", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_benchmark", "CTEST_OPTIONS": "-L \"(SUITE_benchmark)\"", @@ -133,7 +133,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G Xcode", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD" } @@ -148,7 +148,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/mono_mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DLY_MONOLITHIC_GAME=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD" } diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index ce1d55f877..9f779705a2 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -99,7 +99,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -113,7 +113,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -131,7 +131,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_TEST_IMPACT_INSTRUMENTATION_BIN=!TEST_IMPACT_WIN_BINARY!", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_TEST_IMPACT_INSTRUMENTATION_BIN=!TEST_IMPACT_WIN_BINARY!", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -162,7 +162,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -183,7 +183,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -203,7 +203,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "AssetProcessorBatch", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -234,7 +234,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_awsi", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -253,7 +253,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -275,7 +275,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_sandbox", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -294,7 +294,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_benchmark", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", @@ -314,7 +314,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -330,7 +330,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build\\mono_windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_MONOLITHIC_GAME=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -345,7 +345,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_DISABLE_TEST_MODULES=TRUE", "CMAKE_LY_PROJECTS": "", "CMAKE_TARGET": "INSTALL", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -363,7 +363,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"!WIX!\"", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"!WIX!\"", "EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=!INSTALLER_DOWNLOAD_URL! -DLY_INSTALLER_LICENSE_URL=!INSTALLER_DOWNLOAD_URL!/license", "CPACK_BUCKET": "!INSTALLER_BUCKET!", "CMAKE_LY_PROJECTS": "", @@ -382,7 +382,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DCMAKE_MODULE_PATH=!WORKSPACE!/o3de/cmake", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_MODULE_PATH=!WORKSPACE!/o3de/cmake", "CMAKE_LY_PROJECTS": "", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -397,7 +397,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DCMAKE_MODULE_PATH=!WORKSPACE!/o3de/install/cmake", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_MODULE_PATH=!WORKSPACE!/o3de/install/cmake", "CMAKE_LY_PROJECTS": "", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index 94fb8c4f42..6f793cab64 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -48,11 +48,11 @@ IF ERRORLEVEL 1 ( ) IF NOT "%CPACK_BUCKET%"=="" ( - SET "CPACK_OPTIONS=-D CPACK_UPLOAD_URL=s3://%CPACK_BUCKET% %CPACK_OPTIONS%" + SET "CPACK_OPTIONS=-DCPACK_UPLOAD_URL=s3://%CPACK_BUCKET% %CPACK_OPTIONS%" ) ECHO [ci_build] "!CPACK_PATH!" -C %CONFIGURATION% %CPACK_OPTIONS% -REM "!CPACK_PATH!" -C %CONFIGURATION% %CPACK_OPTIONS% +"!CPACK_PATH!" -C %CONFIGURATION% %CPACK_OPTIONS% IF NOT %ERRORLEVEL%==0 ( REM dump the log file generated by cpack specifically for WIX ECHO **************************************************************** diff --git a/scripts/build/Platform/Windows/package_build_config.json b/scripts/build/Platform/Windows/package_build_config.json index 89431ad96e..5eb96f1e38 100644 --- a/scripts/build/Platform/Windows/package_build_config.json +++ b/scripts/build/Platform/Windows/package_build_config.json @@ -4,7 +4,7 @@ "PARAMETERS": { "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"windows_vs2019", - "CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0", "CMAKE_LY_PROJECTS":"AtomTest;AtomSampleViewer", "CMAKE_TARGET":"ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" diff --git a/scripts/build/Platform/iOS/build_config.json b/scripts/build/Platform/iOS/build_config.json index 01f545ffb6..895f74daae 100644 --- a/scripts/build/Platform/iOS/build_config.json +++ b/scripts/build/Platform/iOS/build_config.json @@ -27,7 +27,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build/ios", - "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "-destination generic/platform=iOS" @@ -44,7 +44,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/ios", - "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "-destination generic/platform=iOS" @@ -76,7 +76,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/mac", - "CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "AssetProcessorBatch", "ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch", @@ -94,7 +94,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/ios", - "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "-destination generic/platform=iOS" @@ -112,7 +112,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/ios_test", - "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=FALSE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=TRUE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=TRUE -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=FALSE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=TRUE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "", "TARGET_DEVICE_NAME": "Lumberyard", From 595e0e1a833e6aa3bdd7b875541fa3ad2eea1f88 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 22 Oct 2021 20:38:52 -0700 Subject: [PATCH 021/120] better wrapping for install in core components Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/Install.cmake | 59 +++++---- cmake/Packaging.cmake | 10 +- cmake/Platform/Common/Install_common.cmake | 143 +++++++++------------ cmake/Platform/Linux/Install_linux.cmake | 8 +- cmake/Platform/Mac/Install_mac.cmake | 8 +- 5 files changed, 107 insertions(+), 121 deletions(-) diff --git a/cmake/Install.cmake b/cmake/Install.cmake index 26535d89be..a7340c29f3 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -13,6 +13,28 @@ if(LY_INSTALL_ENABLED) include(${pal_dir}/Install_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) endif() +#! ly_install: wrapper to install that handles common functionality +# +# \notes: +# - this wrapper handles the case where common installs are called multiple times from different +# build folders (when using LY_INSTALL_EXTERNAL_BUILD_DIRS) to generate install layouts that +# have multiple build permutations +# +function(ly_install) + + cmake_parse_arguments(ly_install "" "COMPONENT" "" ${ARGN}) + if (NOT ly_install_COMPONENT OR "${ly_install_COMPONENT}" STREQUAL "${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}") + # if it is installing under the default component, we need to de-duplicate since we can have + # cases coming from different build directories (when using LY_INSTALL_EXTERNAL_BUILD_DIRS) + install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)" ALL_COMPONENTS) + install(${ARGN}) + install(CODE "endif()\n" ALL_COMPONENTS) + else() + install(${ARGN}) + endif() + +endfunction() + #! ly_install_directory: specifies a directory to be copied to the install layout at install time # # \arg:DIRECTORIES directories to install @@ -43,6 +65,10 @@ function(ly_install_directory) if(NOT ly_install_directory_DIRECTORIES) message(FATAL_ERROR "You must provide at least a directory to install") endif() + + if(NOT ly_install_directory_COMPONENT) + set(ly_install_directory_COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) + endif() foreach(directory ${ly_install_directory_DIRECTORIES}) @@ -78,21 +104,12 @@ function(ly_install_directory) list(APPEND exclude_patterns PATTERN *.egg-info EXCLUDE) endif() - if(ly_install_directory_COMPONENT) - install(DIRECTORY ${directory} - DESTINATION ${ly_install_directory_DESTINATION} - COMPONENT ${ly_install_directory_COMPONENT} - ${exclude_patterns} - ) - else() - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(DIRECTORY ${directory} - DESTINATION ${ly_install_directory_DESTINATION} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - ${exclude_patterns} - ) - install(CODE "endif()") - endif() + ly_install(DIRECTORY ${directory} + DESTINATION ${ly_install_directory_DESTINATION} + COMPONENT ${ly_install_directory_COMPONENT} + ${exclude_patterns} + ) + endforeach() endfunction() @@ -137,12 +154,10 @@ function(ly_install_files) set(install_type FILES) endif() - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(${install_type} ${files} + ly_install(${install_type} ${files} DESTINATION ${ly_install_files_DESTINATION} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} # use the default for the time being ) - install(CODE "endif()") endfunction() @@ -157,11 +172,9 @@ function(ly_install_run_code CODE) return() endif() - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(CODE ${CODE} + ly_install(CODE ${CODE} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} # use the default for the time being ) - install(CODE "endif()") endfunction() @@ -176,10 +189,8 @@ function(ly_install_run_script SCRIPT) return() endif() - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(SCRIPT ${SCRIPT} + ly_install(SCRIPT ${SCRIPT} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} # use the default for the time being ) - install(CODE "endif()") endfunction() diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index a810bf8b56..6a1e2d8cef 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -134,12 +134,10 @@ if(NOT EXISTS ${_cmake_package_dest}) endif() endif() -install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") -install(FILES ${_cmake_package_dest} +ly_install(FILES ${_cmake_package_dest} DESTINATION ./Tools/Redistributables/CMake COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) -install(CODE "endif()") # the version string and git tags are intended to be synchronized so it should be safe to use that instead # of directly calling into git which could get messy in certain scenarios @@ -160,12 +158,10 @@ if(${CPACK_PACKAGE_VERSION} VERSION_GREATER "0.0.0.0") list(POP_FRONT _status _status_code) if (${_status_code} EQUAL 0 AND EXISTS ${_3rd_party_license_dest}) - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(FILES ${_3rd_party_license_dest} + ly_install(FILES ${_3rd_party_license_dest} DESTINATION . COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - install(CODE "endif()") else() file(REMOVE ${_3rd_party_license_dest}) message(FATAL_ERROR "Failed to acquire the 3rd Party license manifest file at ${_3rd_party_license_url}. Error: ${_status}") @@ -257,7 +253,7 @@ if(EXISTS \"${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${conf}/${CMAKE_STATIC_LIBRARY_PRE list(APPEND CPACK_COMPONENTS_ALL ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}) set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DISPLAY_NAME \"Binaries for ${LY_BUILD_PERMUTATION} ${conf}\") set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DESCRIPTION \"${PROJECT_NAME} libraries and applications for ${LY_BUILD_PERMUTATION} ${conf}\") - set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DEPENDS ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}) + set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DEPENDS ${LY_INSTALL_PERMUTATION_COMPONENT}) set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_REQUIRED ${required}) set(CPACK_COMPONENT_${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}_DISABLED ${disabled}) endif() diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 71aacd5e31..622100b4d8 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -9,6 +9,12 @@ include(cmake/FileUtil.cmake) set(LY_INSTALL_EXTERNAL_BUILD_DIRS "" CACHE PATH "External build directories to be included in the install process. This allows to package non-monolithic and monolithic.") +unset(normalized_external_build_dirs) +foreach(external_dir ${LY_INSTALL_EXTERNAL_BUILD_DIRS}) + cmake_path(ABSOLUTE_PATH external_dir BASE_DIRECTORY ${LY_ROOT_FOLDER} NORMALIZE) + list(APPEND normalized_external_build_dirs ${external_dir}) +endforeach() +set(LY_INSTALL_EXTERNAL_BUILD_DIRS ${normalized_external_build_dirs}) set(CMAKE_INSTALL_MESSAGE NEVER) # Simplify messages to reduce output noise @@ -84,8 +90,7 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar cmake_path(APPEND rel_include_dir "..") cmake_path(NORMAL_PATH rel_include_dir OUTPUT_VARIABLE destination_dir) - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(DIRECTORY ${include_directory} + ly_install(DIRECTORY ${include_directory} DESTINATION ${destination_dir} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} FILES_MATCHING @@ -95,7 +100,6 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar PATTERN *.hxx PATTERN *.jinja # LyAutoGen files ) - install(CODE "endif()") endif() endforeach() endif() @@ -122,7 +126,7 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar else() foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) string(TOUPPER ${conf} UCONF) - install(TARGETS ${TARGET_NAME} + ly_install(TARGETS ${TARGET_NAME} ARCHIVE DESTINATION ${archive_output_directory} COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} @@ -293,7 +297,7 @@ set_property(TARGET ${NAME_PLACEHOLDER} foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) string(TOUPPER ${conf} UCONF) - install(FILES "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/${NAME_PLACEHOLDER}_${conf}.cmake" + ly_install(FILES "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/${NAME_PLACEHOLDER}_${conf}.cmake" DESTINATION ${relative_target_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} CONFIGURATIONS ${conf} @@ -339,12 +343,10 @@ function(ly_setup_subdirectory absolute_target_source_dir) include(Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) ]] @ONLY) - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(FILES "${target_install_source_dir}/CMakeLists.txt" + ly_install(FILES "${target_install_source_dir}/CMakeLists.txt" DESTINATION ${relative_target_source_dir} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - install(CODE "endif()") # 2. For this platform file, create a Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake file # that will include different configuration permutations (e.g. monolithic vs non-monolithic) @@ -356,12 +358,10 @@ else() include(Platform/${PAL_PLATFORM_NAME}/Default/permutation.cmake) endif() ]]) - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(FILES "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake" + ly_install(FILES "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake" DESTINATION ${relative_target_source_dir}/Platform/${PAL_PLATFORM_NAME} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - install(CODE "endif()") # 3. For this configuration permutation, generate a Platform/${PAL_PLATFORM_NAME}/${permutation}/permutation.cmake # that will declare the target and configure it @@ -379,65 +379,29 @@ endif() "${ENABLE_GEMS_PLACEHOLDER}" ) - install(FILES "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/permutation.cmake" + ly_install(FILES "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/permutation.cmake" DESTINATION ${relative_target_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT} ) endfunction() -#! ly_setup_o3de_install: orchestrates the installation of the different parts. This is the entry point from the root CMakeLists.txt -function(ly_setup_o3de_install) - - ly_setup_subdirectories() - ly_setup_cmake_install() - ly_setup_runtime_dependencies() - ly_setup_assets() - - # Misc - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(FILES - ${LY_ROOT_FOLDER}/ctest_pytest.ini - ${LY_ROOT_FOLDER}/LICENSE.txt - ${LY_ROOT_FOLDER}/README.md - DESTINATION . - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - ) - install(CODE "endif()") - - # Inject other build directories - foreach(external_dir ${LY_INSTALL_EXTERNAL_BUILD_DIRS}) - install(CODE "set(LY_CORE_COMPONENT_ALREADY_INCLUDED TRUE) -include(${external_dir}/cmake_install.cmake)" -ALL_COMPONENTS) - endforeach() - - if(COMMAND ly_post_install_steps) - ly_post_install_steps() - endif() - -endfunction() - #! ly_setup_cmake_install: install the "cmake" folder function(ly_setup_cmake_install) - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(DIRECTORY "${LY_ROOT_FOLDER}/cmake" + ly_install(DIRECTORY "${LY_ROOT_FOLDER}/cmake" DESTINATION . COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} PATTERN "__pycache__" EXCLUDE PATTERN "Findo3de.cmake" EXCLUDE REGEX "3rdParty/Platform\/.*\/BuiltInPackages_.*\.cmake" EXCLUDE ) - install(CODE "endif()") # Connect configuration types - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(FILES "${LY_ROOT_FOLDER}/cmake/install/ConfigurationTypes.cmake" + ly_install(FILES "${LY_ROOT_FOLDER}/cmake/install/ConfigurationTypes.cmake" DESTINATION cmake COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - install(CODE "endif()") # generate each ConfigurationType_.cmake file and install it under that configuration foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) @@ -446,13 +410,11 @@ function(ly_setup_cmake_install) "${CMAKE_BINARY_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/ConfigurationTypes_${conf}.cmake" @ONLY ) - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(FILES "${CMAKE_BINARY_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/ConfigurationTypes_${conf}.cmake" + ly_install(FILES "${CMAKE_BINARY_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/ConfigurationTypes_${conf}.cmake" DESTINATION cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} CONFIGURATIONS ${conf} ) - install(CODE "endif()") endforeach() # Transform the LY_EXTERNAL_SUBDIRS list into a json array @@ -472,14 +434,12 @@ function(ly_setup_cmake_install) configure_file(${LY_ROOT_FOLDER}/cmake/install/engine.json.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/engine.json @ONLY) - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(FILES + ly_install(FILES "${LY_ROOT_FOLDER}/CMakeLists.txt" "${CMAKE_CURRENT_BINARY_DIR}/cmake/engine.json" DESTINATION . COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - install(CODE "endif()") # Collect all Find files that were added with ly_add_external_target_path unset(additional_find_files) @@ -498,16 +458,14 @@ function(ly_setup_cmake_install) endforeach() endforeach() - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(FILES ${additional_find_files} + ly_install(FILES ${additional_find_files} DESTINATION cmake/3rdParty COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - install(FILES ${additional_platform_files} + ly_install(FILES ${additional_platform_files} DESTINATION cmake/3rdParty/Platform/${PAL_PLATFORM_NAME} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - install(CODE "endif()") # Findo3de.cmake file: we generate a different Findo3de.camke file than the one we have in cmake. This one is going to expose all # targets that are pre-built @@ -521,21 +479,21 @@ function(ly_setup_cmake_install) endforeach() configure_file(${LY_ROOT_FOLDER}/cmake/install/Findo3de.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake @ONLY) - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake" + ly_install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake" DESTINATION cmake COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - install(CODE "endif()") # BuiltInPackage_.cmake: since associations could happen in any cmake file across the engine. We collect # all the associations in ly_associate_package and then generate them into BuiltInPackages_.cmake. This # will consolidate all associations in one file get_property(all_package_names GLOBAL PROPERTY LY_PACKAGE_NAMES) + list(REMOVE_DUPLICATES all_package_names) set(builtinpackages "# Generated by O3DE install\n\n") foreach(package_name IN LISTS all_package_names) get_property(package_hash GLOBAL PROPERTY LY_PACKAGE_HASH_${package_name}) get_property(targets GLOBAL PROPERTY LY_PACKAGE_TARGETS_${package_name}) + list(REMOVE_DUPLICATES targets) string(APPEND builtinpackages "ly_associate_package(PACKAGE_NAME ${package_name} TARGETS ${targets} PACKAGE_HASH ${package_hash})\n") endforeach() @@ -543,12 +501,10 @@ function(ly_setup_cmake_install) file(GENERATE OUTPUT ${pal_builtin_file} CONTENT ${builtinpackages} ) - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(FILES "${pal_builtin_file}" + ly_install(FILES "${pal_builtin_file}" DESTINATION cmake/3rdParty/Platform/${PAL_PLATFORM_NAME} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - install(CODE "endif()") endfunction() @@ -556,21 +512,18 @@ endfunction() function(ly_setup_runtime_dependencies) # Common functions used by the bellow code - if(COMMAND ly_install_code_function_override) - ly_install_code_function_override() + if(COMMAND ly_setup_runtime_dependencies_copy_function_override) + ly_setup_runtime_dependencies_copy_function_override() else() - foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) - string(TOUPPER ${conf} UCONF) - install(CODE + ly_install(CODE "function(ly_copy source_file target_directory) cmake_path(GET source_file FILENAME file_name) if(NOT EXISTS ${target_directory}/${file_name}) file(COPY \"\${source_file}\" DESTINATION \"\${target_directory}\" FILE_PERMISSIONS ${LY_COPY_PERMISSIONS}) endif() endfunction()" - COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} - ) - endforeach() + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} + ) endif() unset(runtime_commands) @@ -611,7 +564,7 @@ endfunction()" list(JOIN runtime_commands " " runtime_commands_str) # the spaces are just to see the right identation in the cmake_install.cmake file foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) string(TOUPPER ${conf} UCONF) - install(CODE + ly_install(CODE "if(\"\${CMAKE_INSTALL_CONFIG_NAME}\" MATCHES \"^(${conf})\$\") ${runtime_commands_str} endif()" @@ -704,19 +657,15 @@ function(ly_setup_assets) endif() if(IS_DIRECTORY ${gem_absolute_path}) - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(DIRECTORY "${gem_absolute_path}" + ly_install(DIRECTORY "${gem_absolute_path}" DESTINATION ${gem_install_dest_dir} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - install(CODE "endif()") elseif (EXISTS ${gem_absolute_path}) - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(FILES ${gem_absolute_path} + ly_install(FILES ${gem_absolute_path} DESTINATION ${gem_install_dest_dir} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - install(CODE "endif()") endif() endforeach() @@ -778,4 +727,36 @@ function(ly_setup_subdirectory_enable_gems absolute_target_source_dir output_scr string(APPEND enable_gems_calls ${enable_gems_command}) endforeach() set(${output_script} ${enable_gems_calls} PARENT_SCOPE) +endfunction() + +#! ly_setup_o3de_install: orchestrates the installation of the different parts. This is the entry point from the root CMakeLists.txt +function(ly_setup_o3de_install) + + ly_setup_subdirectories() + ly_setup_cmake_install() + ly_setup_runtime_dependencies() + ly_setup_assets() + + # Misc + ly_install(FILES + ${LY_ROOT_FOLDER}/ctest_pytest.ini + ${LY_ROOT_FOLDER}/LICENSE.txt + ${LY_ROOT_FOLDER}/README.md + DESTINATION . + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} + ) + + # Inject other build directories + foreach(external_dir ${LY_INSTALL_EXTERNAL_BUILD_DIRS}) + ly_install(CODE +"set(LY_CORE_COMPONENT_ALREADY_INCLUDED TRUE) +include(${external_dir}/cmake_install.cmake)" + ALL_COMPONENTS + ) + endforeach() + + if(COMMAND ly_post_install_steps) + ly_post_install_steps() + endif() + endfunction() \ No newline at end of file diff --git a/cmake/Platform/Linux/Install_linux.cmake b/cmake/Platform/Linux/Install_linux.cmake index 68016872aa..54d1e18837 100644 --- a/cmake/Platform/Linux/Install_linux.cmake +++ b/cmake/Platform/Linux/Install_linux.cmake @@ -6,7 +6,7 @@ # # -#! ly_install_code_function_override: Linux-specific copy function to handle RPATH fixes +#! ly_setup_runtime_dependencies_copy_function_override: Linux-specific copy function to handle RPATH fixes set(ly_copy_template [[ function(ly_copy source_file target_directory) file(COPY "${source_file}" DESTINATION "${target_directory}" FILE_PERMISSIONS @LY_COPY_PERMISSIONS@ FOLLOW_SYMLINK_CHAIN) @@ -20,13 +20,11 @@ function(ly_copy source_file target_directory) endif() endfunction()]]) -function(ly_install_code_function_override) +function(ly_setup_runtime_dependencies_copy_function_override) string(CONFIGURE "${ly_copy_template}" ly_copy_function_linux @ONLY) - install(CODE "if(NOT LY_CORE_COMPONENT_ALREADY_INCLUDED)") - install(CODE "${ly_copy_function_linux}" + ly_install(CODE "${ly_copy_function_linux}" COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - install(CODE "endif()") endfunction() include(cmake/Platform/Common/Install_common.cmake) diff --git a/cmake/Platform/Mac/Install_mac.cmake b/cmake/Platform/Mac/Install_mac.cmake index 472831425b..fe0424f86e 100644 --- a/cmake/Platform/Mac/Install_mac.cmake +++ b/cmake/Platform/Mac/Install_mac.cmake @@ -39,10 +39,10 @@ file(GENERATE # This needs to be done here because it needs to update the install prefix # before cmake does anything else in the install process. configure_file(${LY_ROOT_FOLDER}/cmake/Platform/Mac/PreInstallSteps_mac.cmake.in ${CMAKE_BINARY_DIR}/runtime_install/PreInstallSteps_mac.cmake @ONLY) -install(SCRIPT ${CMAKE_BINARY_DIR}/runtime_install/PreInstallSteps_mac.cmake COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) +ly_install(SCRIPT ${CMAKE_BINARY_DIR}/runtime_install/PreInstallSteps_mac.cmake COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) -#! ly_install_target_override: Mac specific target installation -function(ly_install_target_override) +#! ly_setup_runtime_dependencies_copy_function_override: Mac specific target installation +function(ly_setup_runtime_dependencies_copy_function_override) set(options) set(oneValueArgs TARGET ARCHIVE_DIR LIBRARY_DIR RUNTIME_DIR LIBRARY_SUBDIR RUNTIME_SUBDIR) @@ -60,7 +60,7 @@ function(ly_install_target_override) foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) string(TOUPPER ${conf} UCONF) - install(TARGETS ${TARGET_NAME} + ly_install(TARGETS ${TARGET_NAME} ARCHIVE DESTINATION ${ly_platform_install_target_ARCHIVE_DIR} COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} From 99b840652d22a67ce50c5f2312cd6d3c5cb9a76b Mon Sep 17 00:00:00 2001 From: John Date: Mon, 25 Oct 2021 13:40:12 +0100 Subject: [PATCH 022/120] Add Focus Mode integration tests. Signed-off-by: John --- .../Viewport/ViewportEditorModeTests.cpp | 130 +++++++++++++++++- 1 file changed, 125 insertions(+), 5 deletions(-) diff --git a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp index db6a0b8571..31d8bfdcb1 100644 --- a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -187,10 +188,13 @@ namespace UnitTest ASSERT_NE(m_viewportEditorModeTracker, nullptr); m_viewportEditorModes = m_viewportEditorModeTracker->GetViewportEditorModes({AzToolsFramework::GetEntityContextId()}); ASSERT_NE(m_viewportEditorModes, nullptr); + m_focusModeInterface = AZ::Interface::Get(); + ASSERT_NE(m_focusModeInterface, nullptr); } ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr; const ViewportEditorModesInterface* m_viewportEditorModes = nullptr; + AzToolsFramework::FocusModeInterface* m_focusModeInterface = nullptr; }; TEST_F(ViewportEditorModesTestsFixture, NumberOfEditorModesIsEqualTo4) @@ -522,7 +526,8 @@ namespace UnitTest } TEST_F( - ViewportEditorModeTrackerIntegrationTestFixture, EnteringComponentModeAfterInitialStateHasViewportEditorModesDefaultAndComponentModeActive) + ViewportEditorModeTrackerIntegrationTestFixture, + EnteringComponentModeAfterInitialStateHasViewportEditorModesDefaultAndComponentModeActive) { // When component mode is entered AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( @@ -539,15 +544,35 @@ namespace UnitTest // Expect the default and component viewport editor modes to be active EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default)); EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component)); - - // Do not expect the pick and focus viewport editor modes to be active EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick)); EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus)); } TEST_F( ViewportEditorModeTrackerIntegrationTestFixture, - EnteringEditorPickEntitySelectionAfterInitialStateHasOnlyViewportEditorModePickModeActive) + ExitingComponentModeAfterEnteringFrominitialStateHasViewportEditorModesDefaultActive) + { + // When component mode is entered and exited + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( + &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode, + AZStd::vector{}); + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( + &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::EndComponentMode); + + bool inComponentMode = false; + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::BroadcastResult( + inComponentMode, &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::InComponentMode); + + // Expect to not be in component mode + EXPECT_FALSE(inComponentMode); + + // Expect only the default viewport editor mode to be active + ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Default); + } + + TEST_F( + ViewportEditorModeTrackerIntegrationTestFixture, + EnteringEditorPickEntitySelectionAfterInitialStateHasOnlyViewportEditorModePickActive) { // When entering pick mode using AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus; @@ -563,6 +588,101 @@ namespace UnitTest ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Pick); } - // FocusMode integration tests will follow (LYN-6995) + TEST_F( + ViewportEditorModeTrackerIntegrationTestFixture, + EnteringEditorDefaultEntitySelectionFromEditorPickEntitySelectionHasOnlyViewportEditorModeDefaultActive) + { + // When pick mode is entered and exited + using AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus; + EditorInteractionSystemViewportSelectionRequestBus::Event( + AzToolsFramework::GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetHandler, + [](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache, + [[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker) + { + return AZStd::make_unique(entityDataCache, viewportEditorModeTracker); + }); + EditorInteractionSystemViewportSelectionRequestBus::Event( + AzToolsFramework::GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetHandler, + [](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache, + [[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker) + { + return AZStd::make_unique(entityDataCache, viewportEditorModeTracker); + }); + // Expect only the default viewport editor mode to be active + ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Default); + } + + TEST_F(ViewportEditorModeTrackerIntegrationTestFixture, EnteringFocusModeAfterInitialStateHasViewportEditorModeDefaultAndPickActive) + { + // When entering focus mode + m_focusModeInterface->SetFocusRoot(AZ::EntityId{ 1 }); + + // Expect the default and focus viewport editor modes to be active + EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default)); + EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus)); + EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick)); + EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component)); + } + + TEST_F( + ViewportEditorModeTrackerIntegrationTestFixture, + ExitingFocusModeAfterEnteringFromInitialStateHasOnlyViewportEditorModeDefaultActive) + { + // When entering and leaving focus mode + m_focusModeInterface->SetFocusRoot(AZ::EntityId(1)); + m_focusModeInterface->SetFocusRoot(AZ::EntityId()); + + // Expect only the default mode to be active + ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Default); + } + + TEST_F(ViewportEditorModeTrackerIntegrationTestFixture, EnteringComponentModeFromFocusModeStateHasViewportEditorModeDefaultAndFocusAndComponentActive) + { + // When entering component mode from focus mode + m_focusModeInterface->SetFocusRoot(AZ::EntityId{ 1 }); + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( + &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode, + AZStd::vector{}); + + bool inComponentMode = false; + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::BroadcastResult( + inComponentMode, &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::InComponentMode); + + // Expect to be in component mode + EXPECT_TRUE(inComponentMode); + + // Expect the default, focus and component viewport editor modes to be active + EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default)); + EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus)); + EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component)); + EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick)); + } + + TEST_F( + ViewportEditorModeTrackerIntegrationTestFixture, + ExitingComponentModeAfterEnteringFromFocusModeHasViewportEditorModeDefaultAndFocusActive) + { + // When entering and leaving component mode from focus mode + m_focusModeInterface->SetFocusRoot(AZ::EntityId{ 1 }); + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( + &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode, + AZStd::vector{}); + m_focusModeInterface->SetFocusRoot(AZ::EntityId(1)); + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( + &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::EndComponentMode); + + bool inComponentMode = false; + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::BroadcastResult( + inComponentMode, &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::InComponentMode); + + // Expect to not be in component mode + EXPECT_FALSE(inComponentMode); + + // Expect the default and focus viewport editor modes to be active + EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default)); + EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus)); + EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component)); + EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick)); + } } // namespace UnitTest From f98d2e55aad36165953ee0f3359135629dd172cd Mon Sep 17 00:00:00 2001 From: John Date: Mon, 25 Oct 2021 13:48:36 +0100 Subject: [PATCH 023/120] Refactor component mode query. Signed-off-by: John --- .../Viewport/ViewportEditorModeTests.cpp | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp index 31d8bfdcb1..05d7a0d37d 100644 --- a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp @@ -72,6 +72,14 @@ namespace UnitTest } } + bool IsComponentModeActive() + { + bool inComponentMode = false; + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::BroadcastResult( + inComponentMode, &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::InComponentMode); + return inComponentMode; + } + // Fixture for testing editor mode states class ViewportEditorModesTestsFixture : public ::testing::Test @@ -534,12 +542,8 @@ namespace UnitTest &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode, AZStd::vector{}); - bool inComponentMode = false; - AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::BroadcastResult( - inComponentMode, &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::InComponentMode); - // Expect to be in component mode - EXPECT_TRUE(inComponentMode); + EXPECT_TRUE(IsComponentModeActive()); // Expect the default and component viewport editor modes to be active EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default)); @@ -556,15 +560,14 @@ namespace UnitTest AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode, AZStd::vector{}); + + EXPECT_TRUE(IsComponentModeActive()); + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::EndComponentMode); - bool inComponentMode = false; - AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::BroadcastResult( - inComponentMode, &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::InComponentMode); - // Expect to not be in component mode - EXPECT_FALSE(inComponentMode); + EXPECT_FALSE(IsComponentModeActive()); // Expect only the default viewport editor mode to be active ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Default); @@ -601,6 +604,7 @@ namespace UnitTest { return AZStd::make_unique(entityDataCache, viewportEditorModeTracker); }); + EditorInteractionSystemViewportSelectionRequestBus::Event( AzToolsFramework::GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetHandler, [](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache, @@ -645,12 +649,8 @@ namespace UnitTest &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode, AZStd::vector{}); - bool inComponentMode = false; - AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::BroadcastResult( - inComponentMode, &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::InComponentMode); - // Expect to be in component mode - EXPECT_TRUE(inComponentMode); + EXPECT_TRUE(IsComponentModeActive()); // Expect the default, focus and component viewport editor modes to be active EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default)); @@ -668,16 +668,14 @@ namespace UnitTest AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode, AZStd::vector{}); - m_focusModeInterface->SetFocusRoot(AZ::EntityId(1)); + + EXPECT_TRUE(IsComponentModeActive()); + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::EndComponentMode); - bool inComponentMode = false; - AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::BroadcastResult( - inComponentMode, &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::InComponentMode); - // Expect to not be in component mode - EXPECT_FALSE(inComponentMode); + EXPECT_FALSE(IsComponentModeActive()); // Expect the default and focus viewport editor modes to be active EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default)); From 06d5711db83bf1e66e9c2c0b943131eb9638eb29 Mon Sep 17 00:00:00 2001 From: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com> Date: Mon, 25 Oct 2021 10:07:55 -0700 Subject: [PATCH 024/120] ATOM-16489 Add find passes functions for Scene or RenderPipeline in PassSystemInterface (#4739) * ATOM-16489 Add find passes functions for Scene or RenderPipeline in PassSystemInterface Introduced new PassSystemInterface::ForEachPass() funtion to replace PassSystemInterface::FindPasses(), PassSystemInterface::GetPassesByTemplateName and ParentPass::FindPassByNameRecursive() functions. Update all the places which were using those three functions. The new pass finding filter support any combination of pass name, pass template name, pass class type, pass hirechary, owner scene, owner render pipeline. Update unit tests. Signed-off-by: Qing Tao (cherry picked from commit fe8dac798977a2271a2a5775d947d7172949866e) --- .../Include/Atom/Feature/ImGui/ImGuiUtils.h | 6 +- .../Include/Atom/Feature/ImGui/SystemBus.h | 7 +- .../Atom/Feature/Utils/FrameCaptureBus.h | 2 +- .../DirectionalLightFeatureProcessor.cpp | 71 ++--- ...fuseGlobalIlluminationFeatureProcessor.cpp | 57 ++-- .../DiffuseProbeGridFeatureProcessor.cpp | 12 +- .../DisplayMapper/DisplayMapperPass.cpp | 25 +- .../Source/FrameCaptureSystemComponent.cpp | 37 +-- .../Source/ImGui/ImGuiSystemComponent.cpp | 48 +-- .../Code/Source/ImGui/ImGuiSystemComponent.h | 2 +- .../DepthOfField/DepthOfFieldSettings.cpp | 20 +- .../ExposureControlSettings.cpp | 27 +- .../LookModificationCompositePass.cpp | 14 +- .../PostProcessing/SMAAFeatureProcessor.cpp | 51 ++-- .../ProfilingCaptureSystemComponent.cpp | 31 +- .../Source/ProfilingCaptureSystemComponent.h | 2 - .../ReflectionCopyFrameBufferPass.cpp | 19 +- .../ReflectionScreenSpaceBlurPass.cpp | 2 +- .../ReflectionScreenSpaceCompositePass.cpp | 26 +- .../ProjectedShadowFeatureProcessor.cpp | 54 ++-- .../Shadows/ProjectedShadowFeatureProcessor.h | 4 +- .../SkinnedMeshFeatureProcessor.cpp | 13 +- .../SkinnedMesh/SkinnedMeshFeatureProcessor.h | 2 +- .../Include/Atom/RPI.Public/Pass/ParentPass.h | 3 - .../Code/Include/Atom/RPI.Public/Pass/Pass.h | 4 + .../Include/Atom/RPI.Public/Pass/PassFilter.h | 136 ++++----- .../Atom/RPI.Public/Pass/PassLibrary.h | 4 +- .../Include/Atom/RPI.Public/Pass/PassSystem.h | 4 +- .../RPI.Public/Pass/PassSystemInterface.h | 21 +- .../Source/RPI.Public/Pass/ParentPass.cpp | 23 -- .../RPI/Code/Source/RPI.Public/Pass/Pass.cpp | 5 + .../Source/RPI.Public/Pass/PassFilter.cpp | 285 ++++++++++++++---- .../Source/RPI.Public/Pass/PassLibrary.cpp | 90 ++++-- .../Source/RPI.Public/Pass/PassSystem.cpp | 22 +- Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp | 159 ++++++++-- .../Code/Rendering/HairFeatureProcessor.cpp | 44 ++- .../Code/Rendering/HairFeatureProcessor.h | 2 + 37 files changed, 800 insertions(+), 534 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h index a200f30c98..52e272a9c4 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h @@ -50,12 +50,12 @@ namespace AZ return scope; } - //! Sets the active context based on the provided PassHierarchyFilter. If the filter doesn't match exactly one pass, then do nothing. - static ImGuiActiveContextScope FromPass(const RPI::PassHierarchyFilter& passHierarchyFilter) + //! Sets the active context based on the provided pass hierarchy filter. If the filter doesn't match exactly one pass, then do nothing. + static ImGuiActiveContextScope FromPass(const AZStd::vector& passHierarchy) { ImGuiActiveContextScope scope; scope.ConnectToImguiNotificationBus(); - ImGuiSystemRequestBus::BroadcastResult(scope.m_isEnabled, &ImGuiSystemRequests::PushActiveContextFromPass, passHierarchyFilter); + ImGuiSystemRequestBus::BroadcastResult(scope.m_isEnabled, &ImGuiSystemRequests::PushActiveContextFromPass, passHierarchy); return scope; } diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h index 289cb178d4..78f71b39ba 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h @@ -15,11 +15,6 @@ namespace AZ { - namespace RPI - { - class PassHierarchyFilter; - } - namespace Render { class ImGuiPass; @@ -51,7 +46,7 @@ namespace AZ //! Pushes whichever ImGui pass is default on the top of the active context stack. Returns true/false for success/fail. virtual bool PushActiveContextFromDefaultPass() = 0; //! Pushes whichever ImGui pass is provided in passHierarchy on the top of the active context stack. Returns true/false for success/fail. - virtual bool PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchy) = 0; + virtual bool PushActiveContextFromPass(const AZStd::vector& passHierarchy) = 0; //! Pops the active context off the top of the active context stack. Returns true if there's a context to pop. virtual bool PopActiveContext() = 0; //! Gets the context at the top of the active context stack. Returns nullptr if the stack is emtpy. diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h index 93600ec08f..c80926700e 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h @@ -50,7 +50,7 @@ namespace AZ virtual bool CaptureScreenshotWithPreview(const AZStd::string& outputFilePath) = 0; //! Save a buffer attachment or a image attachment binded to a pass's slot to a data file. - //! @param passHierarchy For finding the pass by using PassHierarchyFilter + //! @param passHierarchy For finding the pass by using a pass hierarchy filter. Check PassFilter::CreateWithPassHierarchy() function for detail //! @param slotName Name of the pass's slot. The attachment bound to this slot will be captured. //! @param option Only valid for an InputOutput attachment. Use PassAttachmentReadbackOption::Input to capture the input state //! and use PassAttachmentReadbackOption::Output to capture the output state diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index 6c24b7d35b..b6c6910fd3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -647,54 +647,46 @@ namespace AZ UpdateViewsOfCascadeSegments(); } - void DirectionalLightFeatureProcessor::CacheCascadedShadowmapsPass() { - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->GetPassesForTemplateName(Name("CascadedShadowmapsTemplate")); + void DirectionalLightFeatureProcessor::CacheCascadedShadowmapsPass() + { m_cascadedShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - if (RPI::RenderPipeline* pipeline = pass->GetRenderPipeline()) + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("CascadedShadowmapsTemplate"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { + RPI::RenderPipeline* pipeline = pass->GetRenderPipeline(); const RPI::RenderPipelineId pipelineId = pipeline->GetId(); - // This function can be called when the pipeline is not attached to the scene. - // So we check it is attached to the scene. - if (GetParentScene()->GetRenderPipeline(pipelineId).get() == pipeline) + + CascadedShadowmapsPass* shadowPass = azrtti_cast(pass); + AZ_Assert(shadowPass, "It is not a CascadedShadowmapPass."); + if (pipeline->GetDefaultView()) { - CascadedShadowmapsPass* shadowPass = azrtti_cast(pass); - AZ_Assert(shadowPass, "It is not a CascadedShadowmapPass."); - if (pipeline->GetDefaultView()) - { - m_cascadedShadowmapsPasses[pipelineId].push_back(shadowPass); - } + m_cascadedShadowmapsPasses[pipelineId].push_back(shadowPass); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void DirectionalLightFeatureProcessor::CacheEsmShadowmapsPass() { - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->GetPassesForTemplateName(Name("EsmShadowmapsTemplate")); m_esmShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - if (RPI::RenderPipeline* pipeline = pass->GetRenderPipeline()) + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("EsmShadowmapsTemplate"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - const RPI::RenderPipelineId pipelineId = pipeline->GetId(); - // checking the render pipeline is just removed from the scene. - if (GetParentScene()->GetRenderPipeline(pipelineId).get() == pipeline) + const RPI::RenderPipelineId pipelineId = pass->GetRenderPipeline()->GetId(); + + if (m_cascadedShadowmapsPasses.find(pipelineId) != m_cascadedShadowmapsPasses.end()) { - if (m_cascadedShadowmapsPasses.find(pipelineId) != m_cascadedShadowmapsPasses.end()) + EsmShadowmapsPass* esmPass = azrtti_cast(pass); + AZ_Assert(esmPass, "It is not an EsmShadowmapPass."); + if (esmPass->GetLightTypeName() == m_lightTypeName) { - EsmShadowmapsPass* esmPass = azrtti_cast(pass); - AZ_Assert(esmPass, "It is not an EsmShadowmapPass."); - if (m_cascadedShadowmapsPasses.find(esmPass->GetRenderPipeline()->GetId()) != m_cascadedShadowmapsPasses.end() && - esmPass->GetLightTypeName() == m_lightTypeName) - { - m_esmShadowmapsPasses[pipelineId].push_back(esmPass); - } + m_esmShadowmapsPasses[pipelineId].push_back(esmPass); } } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void DirectionalLightFeatureProcessor::PrepareCameraViews() @@ -1063,12 +1055,13 @@ namespace AZ // if the shadow is rendering in an EnvironmentCubeMapPass it also needs to be a ReflectiveCubeMap view, // to filter out shadows from objects that are excluded from the cubemap - RPI::PassClassFilter passFilter; - AZStd::vector cubeMapPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (!cubeMapPasses.empty()) - { - usageFlags |= RPI::View::UsageReflectiveCubeMap; - } + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + passFilter.SetOwenrScene(GetParentScene()); // only handles passes for this scene + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [&usageFlags]([[maybe_unused]] RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + usageFlags |= RPI::View::UsageReflectiveCubeMap; + return RPI::PassFilterExecutionFlow::StopVisitingPasses; + }); segment.m_view = RPI::View::CreateView(viewName, usageFlags); } diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp index c085b26e31..453dbbc0ab 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp @@ -80,35 +80,48 @@ namespace AZ } // update the size multiplier on the DiffuseProbeGridDownsamplePass output - AZStd::vector downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") }; - RPI::PassHierarchyFilter downsamplePassFilter(downsamplePassHierarchy); - const AZStd::vector& downsamplePasses = RPI::PassSystemInterface::Get()->FindPasses(downsamplePassFilter); - for (RPI::Pass* pass : downsamplePasses) + // NOTE: The ownerScene wasn't added to both filters. This is because the passes from the non-owner scene may have invalid SRG values which could lead to + // GPU error if the scene doesn't have this feature processor enabled. + // For example, the ASV MultiScene sample may have TDR. { - for (uint32_t outputIndex = 0; outputIndex < pass->GetOutputCount(); ++outputIndex) - { - RPI::Ptr outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment; - RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers; + AZStd::vector downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") }; + RPI::PassFilter downsamplePassFilter = RPI::PassFilter::CreateWithPassHierarchy(downsamplePassHierarchy); + RPI::PassSystemInterface::Get()->ForEachPass( + downsamplePassFilter, + [sizeMultiplier](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + for (uint32_t outputIndex = 0; outputIndex < pass->GetOutputCount(); ++outputIndex) + { + RPI::Ptr outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment; + RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers; - sizeMultipliers.m_widthMultiplier = sizeMultiplier; - sizeMultipliers.m_heightMultiplier = sizeMultiplier; - } + sizeMultipliers.m_widthMultiplier = sizeMultiplier; + sizeMultipliers.m_heightMultiplier = sizeMultiplier; + } - // set the output scale on the PassSrg - RPI::FullscreenTrianglePass* downsamplePass = static_cast(pass); - auto constantIndex = downsamplePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_outputImageScale")); - downsamplePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + // set the output scale on the PassSrg + RPI::FullscreenTrianglePass* downsamplePass = static_cast(pass); + RHI::ShaderInputNameIndex outputImageScaleShaderInput = "m_outputImageScale"; + downsamplePass->GetShaderResourceGroup()->SetConstant( + outputImageScaleShaderInput, aznumeric_cast(1.0f / sizeMultiplier)); + + // handle all downsample passes + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } // update the image scale on the DiffuseComposite pass - AZStd::vector compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") }; - RPI::PassHierarchyFilter compositePassFilter(compositePassHierarchy); - const AZStd::vector& compositePasses = RPI::PassSystemInterface::Get()->FindPasses(compositePassFilter); - for (RPI::Pass* pass : compositePasses) { - RPI::FullscreenTrianglePass* compositePass = static_cast(pass); - auto constantIndex = compositePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_imageScale")); - compositePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + AZStd::vector compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") }; + RPI::PassFilter compositePassFilter = RPI::PassFilter::CreateWithPassHierarchy(compositePassHierarchy); + RPI::PassSystemInterface::Get()->ForEachPass(compositePassFilter, [sizeMultiplier](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + RPI::FullscreenTrianglePass* compositePass = static_cast(pass); + RHI::ShaderInputNameIndex imageScaleShaderInput = "m_imageScale"; + compositePass->GetShaderResourceGroup()->SetConstant(imageScaleShaderInput, aznumeric_cast(1.0f / sizeMultiplier)); + + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } } } // namespace Render diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp index 4329fd556c..5ea4748fc9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp @@ -603,12 +603,12 @@ namespace AZ RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); if (device->GetFeatures().m_rayTracing == false) { - RPI::PassHierarchyFilter updatePassFilter(AZ::Name("DiffuseProbeGridUpdatePass")); - const AZStd::vector& updatePasses = RPI::PassSystemInterface::Get()->FindPasses(updatePassFilter); - for (RPI::Pass* pass : updatePasses) - { - pass->SetEnabled(false); - } + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("DiffuseProbeGridUpdatePass"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + pass->SetEnabled(false); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp index 3ec8840a1c..5b5599383e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp @@ -10,9 +10,10 @@ #include #include #include -#include +#include #include #include +#include #include #include #include @@ -66,22 +67,14 @@ namespace AZ { // Need to invalidate the CopyToSwapChain pass so that it updates the pipeline state in the event that // the swapchain format changed (for example, moving from LDR to HDR display) - auto* passSystem = RPI::PassSystemInterface::Get(); - const Name fullscreenCopyTemplateName("FullscreenCopyTemplate"); - - if (passSystem->HasPassesForTemplateName(fullscreenCopyTemplateName)) - { - const AZStd::vector& passes = passSystem->GetPassesForTemplateName(fullscreenCopyTemplateName); - for (RPI::Pass* pass : passes) + const Name copyToSwapChainPassName("CopyToSwapChain"); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(copyToSwapChainPassName, GetRenderPipeline()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - RPI::FullscreenTrianglePass* fullscreenTrianglePass = azrtti_cast(pass); - const Name& passName = fullscreenTrianglePass->GetName(); - if (passName.GetStringView() == "CopyToSwapChain") - { - fullscreenTrianglePass->QueueForInitialization(); - } - } - } + pass->QueueForInitialization(); + return RPI::PassFilterExecutionFlow::StopVisitingPasses; + }); + ConfigureDisplayParameters(); } diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp index 8c0360dec2..ed2d8a1d9f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp @@ -372,29 +372,25 @@ namespace AZ } m_latestCaptureInfo.clear(); - // Find the pass first - RPI::PassClassFilter passFilter; - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); - - if (foundPasses.size() == 0) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + AZ::RPI::ImageAttachmentPreviewPass* previewPass = azrtti_cast(RPI::PassSystemInterface::Get()->FindFirstPass(passFilter)); + if (!previewPass) { - AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find an ImageAttachmentPreviewPass pass "); + AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find an ImageAttachmentPreviewPass"); return false; } - AZ::RPI::ImageAttachmentPreviewPass* previewPass = azrtti_cast(foundPasses[0]); bool result = previewPass->ReadbackOutput(m_readback); if (result) { m_state = State::Pending; m_result = FrameCaptureResult::None; SystemTickBus::Handler::BusConnect(); + return true; } - else - { - AZ_Warning("FrameCaptureSystemComponent", false, "CaptureScreenshotWithPreview. Failed to readback output from the ImageAttachmentPreviewPass");; - } - return result; + + AZ_Warning("FrameCaptureSystemComponent", false, "CaptureScreenshotWithPreview. Failed to readback output from the ImageAttachmentPreviewPass"); + return false; } bool FrameCaptureSystemComponent::CapturePassAttachment(const AZStd::vector& passHierarchy, const AZStd::string& slot, @@ -405,6 +401,12 @@ namespace AZ return false; } + if (passHierarchy.size() == 0) + { + AZ_Warning("FrameCaptureSystemComponent", false, "Empty data in passHierarchy"); + return false; + } + InitReadback(); if (m_state != State::Idle) @@ -426,17 +428,15 @@ namespace AZ } m_latestCaptureInfo.clear(); - // Find the pass first - AZ::RPI::PassHierarchyFilter passFilter(passHierarchy); - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassHierarchy(passHierarchy); + RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); - if (foundPasses.size() == 0) + if (!pass) { - AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find pass from %s", passFilter.ToString().c_str()); + AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find pass from %s", passHierarchy[0].c_str()); return false; } - AZ::RPI::Pass* pass = foundPasses[0]; if (pass->ReadbackAttachment(m_readback, Name(slot), option)) { m_state = State::Pending; @@ -444,6 +444,7 @@ namespace AZ SystemTickBus::Handler::BusConnect(); return true; } + AZ_Warning("FrameCaptureSystemComponent", false, "Failed to readback the attachment bound to pass [%s] slot [%s]", pass->GetName().GetCStr(), slot.c_str()); return false; } diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp index afca20c5cb..3783752e67 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp @@ -109,15 +109,15 @@ namespace AZ void ImGuiSystemComponent::ForAllImGuiPasses(PassFunction func) { ImGuiContext* contextToRestore = ImGui::GetCurrentContext(); - RPI::PassClassFilter filter; - auto imguiPasses = RPI::PassSystemInterface::Get()->FindPasses(filter); - - for (RPI::Pass* pass : imguiPasses) - { - ImGuiPass* imguiPass = azrtti_cast(pass); - ImGui::SetCurrentContext(imguiPass->GetContext()); - func(imguiPass); - } + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [func](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + ImGuiPass* imguiPass = azrtti_cast(pass); + ImGui::SetCurrentContext(imguiPass->GetContext()); + func(imguiPass); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); ImGui::SetCurrentContext(contextToRestore); } @@ -169,29 +169,37 @@ namespace AZ return false; } - bool ImGuiSystemComponent::PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchyFilter) + bool ImGuiSystemComponent::PushActiveContextFromPass(const AZStd::vector& passHierarchyFilter) { - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passHierarchyFilter); + if (passHierarchyFilter.size() == 0) + { + AZ_Warning("ImGuiSystemComponent", false, "passHierarchyFilter is empty"); + return false; + } + AZStd::vector foundImGuiPasses; - for (RPI::Pass* pass : foundPasses) - { - ImGuiPass* imGuiPass = azrtti_cast(pass); - if (imGuiPass) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassHierarchy(passHierarchyFilter); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [&foundImGuiPasses](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - foundImGuiPasses.push_back(imGuiPass); - } - } + ImGuiPass* imGuiPass = azrtti_cast(pass); + if (imGuiPass) + { + foundImGuiPasses.push_back(imGuiPass); + } + + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); if (foundImGuiPasses.size() == 0) { - AZ_Warning("ImGuiSystemComponent", false, "Failed to find ImGui pass to activate from %s", passHierarchyFilter.ToString().c_str()); + AZ_Warning("ImGuiSystemComponent", false, "Failed to find ImGui pass to activate from %s", passHierarchyFilter[0].c_str()); return false; } if (foundImGuiPasses.size() > 1) { - AZ_Warning("ImGuiSystemComponent", false, "Found more than one ImGui pass to activate from %s, only activating first one.", passHierarchyFilter.ToString().c_str()); + AZ_Warning("ImGuiSystemComponent", false, "Found more than one ImGui pass to activate from %s, only activating first one.", passHierarchyFilter[0].c_str()); } ImGuiContext* context = foundImGuiPasses.at(0)->GetContext(); diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h index 838cf0b3c2..d59124890f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h @@ -56,7 +56,7 @@ namespace AZ ImGuiPass* GetDefaultImGuiPass() override; bool PushActiveContextFromDefaultPass() override; - bool PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchy) override; + bool PushActiveContextFromPass(const AZStd::vector& passHierarchy) override; bool PopActiveContext() override; ImGuiContext* GetActiveContext() override; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp index fe1ce8a281..98b527868d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -259,24 +260,19 @@ namespace AZ // [GFX TODO][ATOM-3035]This function is temporary and will change with improvement to the draw list tag system void DepthOfFieldSettings::UpdateAutoFocusDepth(bool enabled) - { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); + { const Name TemplateNameReadBackFocusDepth = Name("DepthOfFieldReadBackFocusDepthTemplate"); - if (passSystem->HasPassesForTemplateName(TemplateNameReadBackFocusDepth)) - { - const AZStd::vector& dofPasses = passSystem->GetPassesForTemplateName(TemplateNameReadBackFocusDepth); - for (RPI::Pass* pass : dofPasses) + // [GFX TODO][ATOM-4908] multiple camera should be distingushed. + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(TemplateNameReadBackFocusDepth, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this, enabled](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { auto* dofPass = azrtti_cast(pass); - // Check this pass belongs to a render pipeline of the scene. - // [GFX TODO][ATOM-4908] multiple camera should be distingushed. - const RPI::RenderPipelineId pipelineId = dofPass->GetRenderPipeline()->GetId(); - if (enabled && GetParentScene()->GetRenderPipeline(pipelineId)) + if (enabled) { m_normalizedFocusDistanceForAutoFocus = dofPass->GetNormalizedFocusDistanceForAutoFocus(); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void DepthOfFieldSettings::SetCameraEntityId(EntityId cameraEntityId) diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp index 96ca424307..26c2a61d54 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -188,21 +189,21 @@ namespace AZ void ExposureControlSettings::UpdateLuminanceHeatmap() { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - // [GFX-TODO][ATOM-13194] Support multiple views for the luminance heatmap - // [GFX-TODO][ATOM-13224] Remove UpdateLuminanceHeatmap and UpdateEyeAdaptationPass - const RPI::Ptr luminanceHeatmap = passSystem->GetRootPass()->FindPassByNameRecursive(m_luminanceHeatmapNameId); - if (luminanceHeatmap) - { - luminanceHeatmap->SetEnabled(m_heatmapEnabled); - } + // [GFX-TODO][ATOM-13224] Remove UpdateLuminanceHeatmap and UpdateEyeAdaptationPass + RPI::PassFilter heatmapPassFilter = RPI::PassFilter::CreateWithPassName(m_luminanceHeatmapNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(heatmapPassFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + pass->SetEnabled(m_heatmapEnabled); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); - const RPI::Ptr histogramGenerator = passSystem->GetRootPass()->FindPassByNameRecursive(m_luminanceHistogramGeneratorNameId); - if (histogramGenerator) - { - histogramGenerator->SetEnabled(m_heatmapEnabled); - } + RPI::PassFilter histogramPassFilter = RPI::PassFilter::CreateWithPassName(m_luminanceHistogramGeneratorNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(histogramPassFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + pass->SetEnabled(m_heatmapEnabled); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void ExposureControlSettings::UpdateBuffer() diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp index 85c45de96b..aac3d1d941 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp @@ -31,12 +31,14 @@ namespace AZ 0, [](const uint8_t& value) { - auto passes = RPI::PassSystem::Get()->FindPasses(RPI::PassClassFilter()); - for (auto* pass : passes) - { - LookModificationCompositePass* lookModPass = azrtti_cast(pass); - lookModPass->SetSampleQuality(LookModificationCompositePass::SampleQuality(value)); - } + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [value](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + LookModificationCompositePass* lookModPass = azrtti_cast(pass); + lookModPass->SetSampleQuality(LookModificationCompositePass::SampleQuality(value)); + + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); }, ConsoleFunctorFlags::Null, "This can be increased to deal with particularly tricky luts. Range (0-2). 0 (default) - Standard linear sampling. 1 - 7 tap b-spline sampling. 2 - 19 tap b-spline sampling." diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp index eef0c51e95..ad059fbec0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -71,26 +72,18 @@ namespace AZ void SMAAFeatureProcessor::UpdateConvertToPerceptualPass() { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - - if (passSystem->HasPassesForTemplateName(m_convertToPerceptualColorPassTemplateNameId)) - { - const AZStd::vector& convertToPerceptualColorPasses = passSystem->GetPassesForTemplateName(m_convertToPerceptualColorPassTemplateNameId); - for (RPI::Pass* pass : convertToPerceptualColorPasses) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_convertToPerceptualColorPassTemplateNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { pass->SetEnabled(m_data.m_enable); - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void SMAAFeatureProcessor::UpdateEdgeDetectionPass() - { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - - if (passSystem->HasPassesForTemplateName(m_edgeDetectioPassTemplateNameId)) - { - const AZStd::vector& edgeDetectionPasses = passSystem->GetPassesForTemplateName(m_edgeDetectioPassTemplateNameId); - for (RPI::Pass* pass : edgeDetectionPasses) + { + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_edgeDetectioPassTemplateNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { auto* edgeDetectionPass = azrtti_cast(pass); @@ -106,18 +99,14 @@ namespace AZ edgeDetectionPass->SetPredicationScale(m_data.m_predicationScale); edgeDetectionPass->SetPredicationStrength(m_data.m_predicationStrength); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void SMAAFeatureProcessor::UpdateBlendingWeightCalculationPass() { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - - if (passSystem->HasPassesForTemplateName(m_blendingWeightCalculationPassTemplateNameId)) - { - const AZStd::vector& blendingWeightCalculationPasses = passSystem->GetPassesForTemplateName(m_blendingWeightCalculationPassTemplateNameId); - for (RPI::Pass* pass : blendingWeightCalculationPasses) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_blendingWeightCalculationPassTemplateNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { auto* blendingWeightCalculationPass = azrtti_cast(pass); @@ -130,18 +119,14 @@ namespace AZ blendingWeightCalculationPass->SetDiagonalDetectionEnable(m_data.m_enableDiagonalDetection); blendingWeightCalculationPass->SetCornerDetectionEnable(m_data.m_enableCornerDetection); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void SMAAFeatureProcessor::UpdateNeighborhoodBlendingPass() { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - - if (passSystem->HasPassesForTemplateName(m_neighborhoodBlendingPassTemplateNameId)) - { - const AZStd::vector& neighborhoodBlendingPasses = passSystem->GetPassesForTemplateName(m_neighborhoodBlendingPassTemplateNameId); - for (RPI::Pass* pass : neighborhoodBlendingPasses) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_neighborhoodBlendingPassTemplateNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { auto* neighborhoodBlendingPass = azrtti_cast(pass); @@ -153,8 +138,8 @@ namespace AZ { neighborhoodBlendingPass->SetOutputMode(SMAAOutputMode::PassThrough); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void SMAAFeatureProcessor::Render([[maybe_unused]] const SMAAFeatureProcessor::RenderPacket& packet) diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp index 66adfe9985..9bfb2b7e9e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp @@ -377,14 +377,7 @@ namespace AZ bool ProfilingCaptureSystemComponent::CapturePassTimestamp(const AZStd::string& outputFilePath) { - // Find the root pass. - AZStd::vector passes = FindPasses({ "Root" }); - if (passes.empty()) - { - return false; - } - - RPI::Pass* root = passes[0]; + RPI::Pass* root = AZ::RPI::PassSystemInterface::Get()->GetRootPass().get(); // Enable all the Timestamp queries in passes. root->SetTimestampQueryEnabled(true); @@ -465,14 +458,7 @@ namespace AZ bool ProfilingCaptureSystemComponent::CapturePassPipelineStatistics(const AZStd::string& outputFilePath) { - // Find the root pass. - AZStd::vector passes = FindPasses({ "Root" }); - if (passes.empty()) - { - return false; - } - - RPI::Pass* root = passes[0]; + RPI::Pass* root = AZ::RPI::PassSystemInterface::Get()->GetRootPass().get(); // Enable all the PipelineStatistics queries in passes. root->SetPipelineStatisticsQueryEnabled(true); @@ -572,19 +558,6 @@ namespace AZ return passes; } - AZStd::vector ProfilingCaptureSystemComponent::FindPasses(AZStd::vector&& passHierarchy) const - { - // Find the pass first. - RPI::PassHierarchyFilter passFilter(passHierarchy); - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (foundPasses.size() == 0) - { - AZ_Warning("ProfilingCaptureSystemComponent", false, "Failed to find pass from %s", passFilter.ToString().c_str()); - } - - return foundPasses; - } - void ProfilingCaptureSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time) { // Update the delayed captures diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h index a9bb8c585f..af1d2f5643 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h @@ -78,8 +78,6 @@ namespace AZ // Recursively collect all the passes from the root pass. AZStd::vector CollectPassesRecursively(const RPI::Pass* root) const; - AZStd::vector FindPasses(AZStd::vector&& passHierarchy) const; - DelayedQueryCaptureHelper m_timestampCapture; DelayedQueryCaptureHelper m_cpuFrameTimeStatisticsCapture; DelayedQueryCaptureHelper m_pipelineStatisticsCapture; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp index 37b1885ee3..a1858a7e9d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp @@ -28,16 +28,17 @@ namespace AZ void ReflectionCopyFrameBufferPass::BuildInternal() { - RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass")); - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (!passes.empty()) - { - Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(passes.front()); - Data::Instance& frameBufferAttachment = blurPass->GetFrameBufferImageAttachment(); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("ReflectionScreenSpaceBlurPass"), GetRenderPipeline()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(pass); + Data::Instance& frameBufferAttachment = blurPass->GetFrameBufferImageAttachment(); - RPI::PassAttachmentBinding& outputBinding = GetOutputBinding(0); - AttachImageToSlot(outputBinding.m_name, frameBufferAttachment); - } + RPI::PassAttachmentBinding& outputBinding = GetOutputBinding(0); + AttachImageToSlot(outputBinding.m_name, frameBufferAttachment); + + return RPI::PassFilterExecutionFlow::StopVisitingPasses; + }); FullscreenTrianglePass::BuildInternal(); } diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp index 394a6fd406..edd7ad1013 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp @@ -150,7 +150,7 @@ namespace AZ auto transientImageDesc = RHI::ImageDescriptor::Create2D(imageBindFlags, mipSize.m_width, mipSize.m_height, RHI::Format::R16G16B16A16_FLOAT); RPI::PassAttachment* transientPassAttachment = aznew RPI::PassAttachment(); - AZStd::string transientAttachmentName = AZStd::string::format("ReflectionScreenSpace_BlurImage%d", mip); + AZStd::string transientAttachmentName = AZStd::string::format("%s.ReflectionScreenSpace_BlurImage%d", GetPathName().GetCStr(), mip); transientPassAttachment->m_name = transientAttachmentName; transientPassAttachment->m_path = transientAttachmentName; transientPassAttachment->m_lifetime = RHI::AttachmentLifetimeType::Transient; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp index 1cf227650c..1362191691 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp @@ -33,20 +33,22 @@ namespace AZ return; } - RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass")); - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (!passes.empty()) - { - Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(passes.front()); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("ReflectionScreenSpaceBlurPass"), GetRenderPipeline()); - // compute the max mip level based on the available mips in the previous frame image, and capping it - // to stay within a range that has reasonable data - const uint32_t MaxNumRoughnessMips = 8; - uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1; + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(pass); - auto constantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_maxMipLevel")); - m_shaderResourceGroup->SetConstant(constantIndex, maxMipLevel); - } + // compute the max mip level based on the available mips in the previous frame image, and capping it + // to stay within a range that has reasonable data + const uint32_t MaxNumRoughnessMips = 8; + uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1; + + auto constantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_maxMipLevel")); + m_shaderResourceGroup->SetConstant(constantIndex, maxMipLevel); + + return RPI::PassFilterExecutionFlow::StopVisitingPasses; + }); FullscreenTrianglePass::CompileResources(context); } diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp index 7c0b3563c7..70ccaa5702 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -313,52 +313,38 @@ namespace AZ::Render void ProjectedShadowFeatureProcessor::CachePasses() { - const AZStd::vector validPipelineIds = CacheProjectedShadowmapsPass(); - CacheEsmShadowmapsPass(validPipelineIds); + CacheProjectedShadowmapsPass(); + CacheEsmShadowmapsPass(); m_shadowmapPassNeedsUpdate = true; } - AZStd::vector ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass() + void ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass() { - const AZStd::vector& renderPipelines = GetParentScene()->GetRenderPipelines(); - const auto* passSystem = RPI::PassSystemInterface::Get();; - const AZStd::vector& passes = passSystem->GetPassesForTemplateName(Name("ProjectedShadowmapsTemplate")); - - AZStd::vector validPipelineIds; m_projectedShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - ProjectedShadowmapsPass* shadowPass = static_cast(pass); - for (const RPI::RenderPipelinePtr& pipeline : renderPipelines) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("ProjectedShadowmapsTemplate"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - if (pipeline.get() == shadowPass->GetRenderPipeline()) - { - m_projectedShadowmapsPasses.emplace_back(shadowPass); - validPipelineIds.push_back(shadowPass->GetRenderPipeline()->GetId()); - } - } - } - return validPipelineIds; + ProjectedShadowmapsPass* shadowPass = static_cast(pass); + m_projectedShadowmapsPasses.emplace_back(shadowPass); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } - void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass(const AZStd::vector& validPipelineIds) + void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass() { const Name LightTypeName = Name("projected"); - - const auto* passSystem = RPI::PassSystemInterface::Get(); - const AZStd::vector passes = passSystem->GetPassesForTemplateName(Name("EsmShadowmapsTemplate")); - + m_esmShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - EsmShadowmapsPass* esmPass = static_cast(pass); - if (esmPass->GetRenderPipeline() && - AZStd::find(validPipelineIds.begin(), validPipelineIds.end(), esmPass->GetRenderPipeline()->GetId()) != validPipelineIds.end() && - esmPass->GetLightTypeName() == LightTypeName) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("EsmShadowmapsTemplate"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this, LightTypeName](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - m_esmShadowmapsPasses.emplace_back(esmPass); - } - } + EsmShadowmapsPass* esmPass = static_cast(pass); + if (esmPass->GetLightTypeName() == LightTypeName) + { + m_esmShadowmapsPasses.emplace_back(esmPass); + } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void ProjectedShadowFeatureProcessor::UpdateFilterParameters() diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h index fafcb25a08..8939f1845d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h @@ -97,8 +97,8 @@ namespace AZ::Render // Functions for caching the ProjectedShadowmapsPass and EsmShadowmapsPass. void CachePasses(); - AZStd::vector CacheProjectedShadowmapsPass(); - void CacheEsmShadowmapsPass(const AZStd::vector& validPipelineIds); + void CacheProjectedShadowmapsPass(); + void CacheEsmShadowmapsPass(); //! Functions to update the parameter of Gaussian filter used in ESM. void UpdateFilterParameters(); diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp index e0209702dc..4c379c4239 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -241,12 +242,12 @@ namespace AZ void SkinnedMeshFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) { - InitSkinningAndMorphPass(pipeline->GetRootPass()); + InitSkinningAndMorphPass(pipeline.get()); } void SkinnedMeshFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) { - InitSkinningAndMorphPass(renderPipeline->GetRootPass()); + InitSkinningAndMorphPass(renderPipeline); } void SkinnedMeshFeatureProcessor::OnBeginPrepareRender() @@ -289,9 +290,10 @@ namespace AZ return false; } - void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass(const RPI::Ptr pipelineRootPass) + void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass(RPI::RenderPipeline* renderPipeline) { - RPI::Ptr skinningPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "SkinningPass" }); + RPI::PassFilter skinPassFilter = RPI::PassFilter::CreateWithPassName(AZ::Name{ "SkinningPass" }, renderPipeline); + RPI::Ptr skinningPass = RPI::PassSystemInterface::Get()->FindFirstPass(skinPassFilter); if (skinningPass) { SkinnedMeshComputePass* skinnedMeshComputePass = azdynamic_cast(skinningPass.get()); @@ -310,7 +312,8 @@ namespace AZ } } - RPI::Ptr morphTargetPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "MorphTargetPass" }); + RPI::PassFilter morphPassFilter = RPI::PassFilter::CreateWithPassName(AZ::Name{ "MorphTargetPass" }, renderPipeline); + RPI::Ptr morphTargetPass = RPI::PassSystemInterface::Get()->FindFirstPass(morphPassFilter); if (morphTargetPass) { MorphTargetComputePass* morphTargetComputePass = azdynamic_cast(morphTargetPass.get()); diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h index 2e93acf2cd..5b7ab943e1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h @@ -66,7 +66,7 @@ namespace AZ private: AZ_DISABLE_COPY_MOVE(SkinnedMeshFeatureProcessor); - void InitSkinningAndMorphPass(const RPI::Ptr pipelineRootPass); + void InitSkinningAndMorphPass(RPI::RenderPipeline* renderPipeline); SkinnedMeshRenderProxyInterfaceHandle AcquireRenderProxyInterface(const SkinnedMeshRenderProxyDesc& desc) override; bool ReleaseRenderProxyInterface(SkinnedMeshRenderProxyInterfaceHandle& handle) override; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h index 36994ec03b..6523f0a6d8 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h @@ -68,9 +68,6 @@ namespace AZ template Ptr FindChildPass() const; - //! Searches the tree for the first pass that has same pass name (Depth-first search). Return nullptr if none found. - Ptr FindPassByNameRecursive(const Name& passName) const; - //! Gets the list of children. Useful for validating hierarchies AZStd::array_view> GetChildren() const; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h index 34eaae4495..e7b9825ecb 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h @@ -139,6 +139,10 @@ namespace AZ //! Returns the number of output attachment bindings uint32_t GetOutputCount() const; + //! Returns the pass template which was used for create this pass. + //! It may return nullptr if the pass wasn't create from a template + const PassTemplate* GetPassTemplate() const; + //! Enable/disable this pass //! If the pass is disabled, it (and any children if it's a ParentPass) won't be rendered. void SetEnabled(bool enabled); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h index c31f353adb..c42991725e 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h @@ -16,95 +16,85 @@ namespace AZ { namespace RPI { - // A base class for a filter which can be used to filter passes + class Scene; + class RenderPipeline; + class PassFilter { public: - //! Whether the input pass matches with the filter - virtual bool Matches(const Pass* pass) const = 0; + static PassFilter CreateWithPassName(Name passName, const Scene* scene); + static PassFilter CreateWithPassName(Name passName, const RenderPipeline* renderPipeline); - //! Return the pass' name if a pass name is used for the filter. - //! Return nullptr if the filter doesn't have pass name used for matching - virtual const Name* GetPassName() const = 0; + //! Create a PassFilter with pass hierarchy information + //! Filter for passes which have a matching name and also with ordered parents. + //! For example, if the filter is initialized with + //! pass name: "ShadowPass1" + //! pass parents names: "MainPipeline", "Shadow" + //! Passes with these names match the filter: + //! "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1" + //! or "Root.MainPipeline.Shadow.ShadowPass1" + //! or "MainPipeline.Shadow.Group1.ShadowPass1" + //! + //! Passes with these names wont match: + //! "MainPipeline.ShadowPass1" + //! or "Shadow.MainPipeline.ShadowPass1" + static PassFilter CreateWithPassHierarchy(const AZStd::vector& passHierarchy); + static PassFilter CreateWithPassHierarchy(const AZStd::vector& passHierarchy); + static PassFilter CreateWithTemplateName(Name templateName, const Scene* scene); + static PassFilter CreateWithTemplateName(Name templateName, const RenderPipeline* renderPipeline); + template + static PassFilter CreateWithPassClass(); - //! Return this filter's info as a string - virtual AZStd::string ToString() const = 0; - }; + enum FilterOptions : uint32_t + { + Empty = 0, + PassName = AZ_BIT(0), + PassTemplateName = AZ_BIT(1), + PassClass = AZ_BIT(2), + PassHierarchy = AZ_BIT(3), + OwnerScene = AZ_BIT(4), + OwnerRenderPipeline = AZ_BIT(5) + }; - //! Filter for passes which have a matching name and also with ordered parents. - //! For example, if the filter is initialized with - //! pass name: "ShadowPass1" - //! pass parents names: "MainPipeline", "Shadow" - //! Passes with these names match the filter: - //! "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1" - //! or "Root.MainPipeline.Shadow.ShadowPass1" - //! or "MainPipeline.Shadow.Group1.ShadowPass1" - //! - //! Passes with these names wont match: - //! "MainPipeline.ShadowPass1" - //! or "Shadow.MainPipeline.ShadowPass1" - class PassHierarchyFilter - : public PassFilter - { - public: - AZ_RTTI(PassHierarchyFilter, "{478F169F-BA97-4321-AC34-EDE823997159}", PassFilter); - AZ_CLASS_ALLOCATOR(PassHierarchyFilter, SystemAllocator, 0); + void SetOwenrScene(const Scene* scene); + void SetOwenrRenderPipeline(const RenderPipeline* renderPipeline); + void SetPassName(Name passName); + void SetTemplateName(Name passTemplateName); + void SetPassClass(TypeId passClassTypeId); - //! Construct filter with only pass name. - PassHierarchyFilter(const Name& passName); + const Name& GetPassName() const; + const Name& GetPassTemplateName() const; - virtual ~PassHierarchyFilter() = default; + uint32_t GetEnabledFilterOptions() const; - //! Construct filter with pass name and its parents' names in the order of the hierarchy - //! This means k-th element is always an ancestor of the (k-1)-th element. - //! And the last element is the pass name. - PassHierarchyFilter(const AZStd::vector& passHierarchy); - PassHierarchyFilter(const AZStd::vector& passHierarchy); + //! Return true if the input pass matches the filter + bool Matches(const Pass* pass) const; - // PassFilter overrides... - bool Matches(const Pass* pass) const override; - const Name* GetPassName() const override; - AZStd::string ToString() const override; + //! Return true if the input pass matches the filter with selected filter options + //! The input filter options should be a subset of options returned by GetEnabledFilterOptions() + //! This function is used to avoid extra checks for passes which was already filtered. + //! Check PassLibrary::ForEachPass() function's implementation for more details + bool Matches(const Pass* pass, uint32_t options) const; private: - PassHierarchyFilter() = delete; + void UpdateFilterOptions(); - AZStd::vector m_parentNames; Name m_passName; + Name m_templateName; + TypeId m_passClassTypeId = TypeId::CreateNull(); + AZStd::vector m_parentNames; + const RenderPipeline* m_ownerRenderPipeline = nullptr; + const Scene* m_ownerScene = nullptr; + uint32_t m_filterOptions = 0; }; - //! Filter for passes based on their class. - template - class PassClassFilter - : public PassFilter - { - public: - AZ_RTTI(PassClassFilter, "{AF6E3AD5-433A-462A-997A-F36D8A551D02}", PassFilter); - AZ_CLASS_ALLOCATOR(PassHierarchyFilter, SystemAllocator, 0); - PassClassFilter() = default; - - // PassFilter overrides... - bool Matches(const Pass* pass) const override; - const Name* GetPassName() const override; - AZStd::string ToString() const override; - }; - - template - bool PassClassFilter::Matches(const Pass* pass) const - { - return pass->RTTI_IsTypeOf(PassClass::RTTI_Type()); - } - - template - const Name* PassClassFilter::GetPassName() const - { - return nullptr; - } - - template - AZStd::string PassClassFilter::ToString() const - { - return AZStd::string::format("PassClassFilter<%s>", PassClass::RTTI_TypeName()); + template + PassFilter PassFilter::CreateWithPassClass() + { + PassFilter filter; + filter.m_passClassTypeId = PassClass::RTTI_Type(); + filter.UpdateFilterOptions(); + return filter; } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h index 0a4b1c4399..66c3c205ab 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h @@ -84,8 +84,8 @@ namespace AZ bool LoadPassTemplateMappings(const AZStd::string& templateMappingPath); bool LoadPassTemplateMappings(Data::Asset mappingAsset); - //! Returns a list of passes found in the pass name mapping using the provided pass filter - AZStd::vector FindPasses(const PassFilter& passFilter) const; + //! Visit each pass which matches the filter + void ForEachPass(const PassFilter& passFilter, AZStd::function passFunction); private: diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h index 30fa27e64b..8390b0f7e2 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h @@ -92,13 +92,13 @@ namespace AZ // PassSystemInterface library related functions... bool HasPassesForTemplateName(const Name& templateName) const override; - const AZStd::vector& GetPassesForTemplateName(const Name& templateName) const override; bool AddPassTemplate(const Name& name, const AZStd::shared_ptr& passTemplate) override; const AZStd::shared_ptr GetPassTemplate(const Name& name) const override; void RemovePassFromLibrary(Pass* pass) override; void RegisterPass(Pass* pass) override; void UnregisterPass(Pass* pass) override; - AZStd::vector FindPasses(const PassFilter& passFilter) const override; + void ForEachPass(const PassFilter& filter, AZStd::function passFunction) override; + Pass* FindFirstPass(const PassFilter& filter) override; private: // Returns the root of the pass tree hierarchy diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h index 0e9386f3bb..7f944df88f 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h @@ -75,6 +75,13 @@ namespace AZ u32 m_maxDrawItemsRenderedInAPass = 0; }; + + enum PassFilterExecutionFlow : uint8_t + { + StopVisitingPasses, + ContinueVisitingPasses, + }; + class PassSystemInterface { friend class Pass; @@ -186,9 +193,6 @@ namespace AZ //! Returns true if the pass factory contains passes created with the given template name virtual bool HasPassesForTemplateName(const Name& templateName) const = 0; - //! Get the passes created with the given template name. - virtual const AZStd::vector& GetPassesForTemplateName(const Name& templateName) const = 0; - //! Adds a PassTemplate to the library virtual bool AddPassTemplate(const Name& name, const AZStd::shared_ptr& passTemplate) = 0; @@ -197,9 +201,16 @@ namespace AZ //! Removes all references to the given pass from the pass library virtual void RemovePassFromLibrary(Pass* pass) = 0; + + //! Visit the matching passes from registered passes with specified filter + //! The return value of the passFunction decides if the search continues or not + //! Note: this function will find all the passes which match the pass filter even they are for render pipelines which are not added to a scene + //! This function is fast if a pass name or a pass template name is specified. + virtual void ForEachPass(const PassFilter& filter, AZStd::function passFunction) = 0; - //! Find matching passes from registered passes with specified filter - virtual AZStd::vector FindPasses(const PassFilter& passFilter) const = 0; + //! Find the first matching pass from registered passes with specified filter + //! Note: this function SHOULD ONLY be used when you are certain you only need to handle the first pass found + virtual Pass* FindFirstPass(const PassFilter& filter) = 0; private: // These functions are only meant to be used by the Pass class diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp index 36c28877ea..dccf5dbc2e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp @@ -149,29 +149,6 @@ namespace AZ return index.IsValid() ? m_children[index.GetIndex()] : Ptr(nullptr); } - Ptr ParentPass::FindPassByNameRecursive(const Name& passName) const - { - for (const Ptr& child : m_children) - { - if (child->GetName() == passName) - { - return child.get(); - } - - ParentPass* asParent = child->AsParent(); - if (asParent) - { - auto pass = asParent->FindPassByNameRecursive(passName); - if (pass) - { - return pass; - } - } - } - - return nullptr; - } - const Pass* ParentPass::FindPass(RHI::DrawListTag drawListTag) const { if (HasDrawListTag() && GetDrawListTag() == drawListTag) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp index a8d94e9a91..3c1de28d6a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -238,6 +238,11 @@ namespace AZ return m_attachmentBindings[bindingIndex]; } + const PassTemplate* Pass::GetPassTemplate() const + { + return m_template.get(); + } + void Pass::AddAttachmentBinding(PassAttachmentBinding attachmentBinding) { // Add the index of the binding to the input, output or input/output list based on the slot type diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp index 7bd1abc0a7..d9e458c615 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp @@ -8,101 +8,264 @@ #include #include +#include namespace AZ { namespace RPI { - PassHierarchyFilter::PassHierarchyFilter(const Name& passName) + PassFilter PassFilter::CreateWithPassName(Name passName, const Scene* scene) + { + PassFilter filter; + filter.m_passName = passName; + filter.m_ownerScene = scene; + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithPassName(Name passName, const RenderPipeline* renderPipeline) + { + PassFilter filter; + filter.m_passName = passName; + filter.m_ownerRenderPipeline = renderPipeline; + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithTemplateName(Name templateName, const Scene* scene) + { + PassFilter filter; + filter.m_templateName = templateName; + filter.m_ownerScene = scene; + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithTemplateName(Name templateName, const RenderPipeline* renderPipeline) + { + PassFilter filter; + filter.m_templateName = templateName; + filter.m_ownerRenderPipeline = renderPipeline; + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithPassHierarchy(const AZStd::vector& passHierarchy) + { + PassFilter filter; + if (passHierarchy.size() == 0) + { + AZ_Assert(false, "passHierarchy should have at least one element"); + return filter; + } + + filter.m_passName = passHierarchy.back(); + + filter.m_parentNames.resize(passHierarchy.size() - 1); + for (uint32_t index = 0; index < filter.m_parentNames.size(); index++) + { + filter.m_parentNames[index] = passHierarchy[index]; + } + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithPassHierarchy(const AZStd::vector& passHierarchy) + { + PassFilter filter; + if (passHierarchy.size() == 0) + { + AZ_Assert(false, "passHierarchy should have at least one element"); + return filter; + } + + filter.m_passName = Name(passHierarchy.back()); + + filter.m_parentNames.resize(passHierarchy.size() - 1); + for (uint32_t index = 0; index < filter.m_parentNames.size(); index++) + { + filter.m_parentNames[index] = Name(passHierarchy[index]); + } + filter.UpdateFilterOptions(); + return filter; + } + + void PassFilter::SetOwenrScene(const Scene* scene) + { + m_ownerScene = scene; + UpdateFilterOptions(); + } + + void PassFilter::SetOwenrRenderPipeline(const RenderPipeline* renderPipeline) + { + m_ownerRenderPipeline = renderPipeline; + UpdateFilterOptions(); + } + + void PassFilter::SetPassName(Name passName) { m_passName = passName; + UpdateFilterOptions(); } - PassHierarchyFilter::PassHierarchyFilter(const AZStd::vector& passHierarchy) + void PassFilter::SetTemplateName(Name passTemplateName) { - if (passHierarchy.size() == 0) - { - AZ_Assert(false, "passHierarchy should have at least one element"); - return; - } - - m_passName = Name(passHierarchy.back()); - - m_parentNames.resize(passHierarchy.size() - 1); - for (uint32_t index = 0; index < m_parentNames.size(); index++) - { - m_parentNames[index] = Name(passHierarchy[index]); - } + m_templateName = passTemplateName; + UpdateFilterOptions(); } - PassHierarchyFilter::PassHierarchyFilter(const AZStd::vector& passHierarchy) + void PassFilter::SetPassClass(TypeId passClassTypeId) { - if (passHierarchy.size() == 0) - { - AZ_Assert(false, "passHierarchy should have at least one element"); - return; - } - - m_passName = passHierarchy.back(); - - m_parentNames.resize(passHierarchy.size() - 1); - for (uint32_t index = 0; index < m_parentNames.size(); index++) - { - m_parentNames[index] = passHierarchy[index]; - } + m_passClassTypeId = passClassTypeId; + UpdateFilterOptions(); } - bool PassHierarchyFilter::Matches(const Pass* pass) const + const Name& PassFilter::GetPassName() const { - if (pass->GetName() != m_passName) + return m_passName; + } + + const Name& PassFilter::GetPassTemplateName() const + { + return m_templateName; + } + + uint32_t PassFilter::GetEnabledFilterOptions() const + { + return m_filterOptions; + } + + bool PassFilter::Matches(const Pass* pass) const + { + return Matches(pass, m_filterOptions); + } + + bool PassFilter::Matches(const Pass* pass, uint32_t options) const + { + AZ_Assert( (options&m_filterOptions) == options, "options should be a subset of m_filterOptions"); + + // return false if the pass doesn't have a pass template or the template's name is not matching + if (options & FilterOptions::PassTemplateName && (!pass->GetPassTemplate() || pass->GetPassTemplate()->m_name != m_templateName)) { return false; } - ParentPass* parent = pass->GetParent(); - - // search from the back of the array with the most close parent - for (int32_t index = static_cast(m_parentNames.size() - 1); index >= 0; index--) + if ((options & FilterOptions::PassName) && pass->GetName() != m_passName) { - const Name& parentName = m_parentNames[index]; - while (parent) - { - if (parent->GetName() == parentName) - { - break; - } - parent = parent->GetParent(); - } + return false; + } - // if parent is nullptr the it didn't find a parent has matching current parentName - if (!parent) + if ((options & FilterOptions::PassClass) && pass->RTTI_GetType() != m_passClassTypeId) + { + return false; + } + + if ((options & FilterOptions::OwnerRenderPipeline) && m_ownerRenderPipeline != pass->GetRenderPipeline()) + { + return false; + } + + // If the owner render pipeline was checked, the owner scene check can be skipped + if (options & FilterOptions::OwnerScene) + { + if (pass->GetRenderPipeline()) { + // return false if the owner scene doesn't match + if (m_ownerScene != pass->GetRenderPipeline()->GetScene()) + { + return false; + } + } + else + { + // return false if the pass doesn't have an owner scene return false; } + } - // move to next parent - parent = parent->GetParent(); + if ((options & FilterOptions::PassHierarchy)) + { + // Filter for passes which have a matching name and also with ordered parents. + // For example, if the filter is initialized with + // pass name: "ShadowPass1" + // pass parents names: "MainPipeline", "Shadow" + // Passes with these names match the filter: + // "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1" + // or "Root.MainPipeline.Shadow.ShadowPass1" + // or "MainPipeline.Shadow.Group1.ShadowPass1" + // + // Passes with these names wont match: + // "MainPipeline.ShadowPass1" + // or "Shadow.MainPipeline.ShadowPass1" + + ParentPass* parent = pass->GetParent(); + + // search from the back of the array with the most close parent + for (int32_t index = static_cast(m_parentNames.size() - 1); index >= 0; index--) + { + const Name& parentName = m_parentNames[index]; + while (parent) + { + if (parent->GetName() == parentName) + { + break; + } + parent = parent->GetParent(); + } + + // if parent is nullptr the it didn't find a parent has matching current parentName + if (!parent) + { + return false; + } + + // move to next parent + parent = parent->GetParent(); + } } return true; } - const Name* PassHierarchyFilter::GetPassName() const + void PassFilter::UpdateFilterOptions() { - return &m_passName; - } - - AZStd::string PassHierarchyFilter::ToString() const - { - AZStd::string result = "PassHierarchyFilter"; - for (uint32_t index = 0; index < m_parentNames.size(); index++) + m_filterOptions = FilterOptions::Empty; + if (!m_passName.IsEmpty()) { - result += AZStd::string::format(" [%s]", m_parentNames[index].GetCStr()); + m_filterOptions |= FilterOptions::PassName; + } + if (!m_templateName.IsEmpty()) + { + m_filterOptions |= FilterOptions::PassTemplateName; + } + if (m_parentNames.size() > 0) + { + m_filterOptions |= FilterOptions::PassHierarchy; + } + if (m_ownerRenderPipeline) + { + m_filterOptions |= FilterOptions::OwnerRenderPipeline; + } + if (m_ownerScene) + { + // If the OwnerRenderPipeline exists, we shouldn't need to filter owner scene + // Validate the owner render pipeline belongs to the owner scene + if (m_filterOptions & FilterOptions::OwnerRenderPipeline) + { + if (m_ownerRenderPipeline->GetScene() != m_ownerScene) + { + AZ_Warning("RPI", false, "The owner scene filter doesn't match owner render pipeline. It will be skipped."); + } + } + else + { + m_filterOptions |= FilterOptions::OwnerScene; + } + } + if (!m_passClassTypeId.IsNull()) + { + m_filterOptions |= FilterOptions::PassClass; } - - result += AZStd::string::format(" [%s]", m_passName.GetCStr()); - return result; } - } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp index c43edafd6b..6a6f5f3ff9 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp @@ -85,47 +85,80 @@ namespace AZ return (GetPassesForTemplate(templateName).size() > 0); } - AZStd::vector PassLibrary::FindPasses(const PassFilter& passFilter) const + void PassLibrary::ForEachPass(const PassFilter& passFilter, AZStd::function passFunction) { - const Name* passName = passFilter.GetPassName(); + uint32_t filterOptions = passFilter.GetEnabledFilterOptions(); - AZStd::vector result; - - if (passName) + // A lambda function which visits each pass in a pass list, if the pass matches the pass filter, then call the pass function + auto visitList = [passFilter, passFunction](const AZStd::vector& passList, uint32_t options) -> PassFilterExecutionFlow { - // If the pass' name is known, find passes with matching names first - const auto constItr = m_passNameMapping.find(*passName); - if (constItr == m_passNameMapping.end()) + if (passList.size() == 0) { - return result; + return PassFilterExecutionFlow::ContinueVisitingPasses; } - - const AZStd::vector& passes = constItr->second; - - for (Pass* pass : passes) + // if there is not other filter options enabled, skip the filter and call pass functions directly + if (options == PassFilter::FilterOptions::Empty) { - if (passFilter.Matches(pass)) + for (Pass* pass : passList) { - result.push_back(pass); - } - } - } - else - { - // If the filter doesn't know matching pass' name, need to go through all registered passes - for (auto& namePasses : m_passNameMapping) - { - for (Pass* pass : namePasses.second) - { - if (passFilter.Matches(pass)) + // If user want to skip processing, return directly. + if (passFunction(pass) == PassFilterExecutionFlow::StopVisitingPasses) { - result.push_back(pass); + return PassFilterExecutionFlow::StopVisitingPasses; + } + } + return PassFilterExecutionFlow::ContinueVisitingPasses; + } + + // Check with the pass filter and call pass functions + for (Pass* pass : passList) + { + if (passFilter.Matches(pass, options)) + { + if (passFunction(pass) == PassFilterExecutionFlow::StopVisitingPasses) + { + return PassFilterExecutionFlow::StopVisitingPasses; } } } + return PassFilterExecutionFlow::ContinueVisitingPasses; + }; + + // Check pass template name first + if (filterOptions & PassFilter::FilterOptions::PassTemplateName) + { + auto entry = GetEntry(passFilter.GetPassTemplateName()); + if (!entry) + { + return; + } + + filterOptions &= ~(PassFilter::FilterOptions::PassTemplateName); + visitList(entry->m_passes, filterOptions); + return; + } + else if (filterOptions & PassFilter::FilterOptions::PassName) + { + const auto constItr = m_passNameMapping.find(passFilter.GetPassName()); + if (constItr == m_passNameMapping.end()) + { + return; + } + + filterOptions &= ~(PassFilter::FilterOptions::PassName); + visitList(constItr->second, filterOptions); + return; } - return result; + // check againest every passes. This might be slow + AZ_PROFILE_SCOPE(RPI, "PassLibrary::ForEachPass"); + for (auto& namePasses : m_passNameMapping) + { + if (visitList(namePasses.second, filterOptions) == PassFilterExecutionFlow::StopVisitingPasses) + { + return; + } + } } // Add Functions... @@ -419,3 +452,4 @@ namespace AZ } // namespace RPI } // namespace AZ + diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp index f4f51f97b7..7f2948c13a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp @@ -456,11 +456,6 @@ namespace AZ return m_passLibrary.HasPassesForTemplate(templateName); } - const AZStd::vector& PassSystem::GetPassesForTemplateName(const Name& templateName) const - { - return m_passLibrary.GetPassesForTemplate(templateName); - } - bool PassSystem::AddPassTemplate(const Name& name, const AZStd::shared_ptr& passTemplate) { return m_passLibrary.AddPassTemplate(name, passTemplate); @@ -487,10 +482,21 @@ namespace AZ RemovePassFromLibrary(pass); --m_passCounter; } - - AZStd::vector PassSystem::FindPasses(const PassFilter& passFilter) const + + void PassSystem::ForEachPass(const PassFilter& filter, AZStd::function passFunction) { - return m_passLibrary.FindPasses(passFilter); + return m_passLibrary.ForEachPass(filter, passFunction); + } + + Pass* PassSystem::FindFirstPass(const PassFilter& filter) + { + Pass* foundPass = nullptr; + m_passLibrary.ForEachPass(filter, [&foundPass](RPI::Pass* pass) ->PassFilterExecutionFlow + { + foundPass = pass; + return PassFilterExecutionFlow::StopVisitingPasses; + }); + return foundPass; } SwapChainPass* PassSystem::FindSwapChainPass(AzFramework::NativeWindowHandle windowHandle) const diff --git a/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp b/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp index 420ab1798a..690f212ec7 100644 --- a/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp @@ -19,6 +19,8 @@ #include #include +#include + #include #include @@ -573,7 +575,7 @@ namespace UnitTest EXPECT_TRUE(pass != nullptr); } - TEST_F(PassTests, PassHierarchyFilter) + TEST_F(PassTests, PassFilter_PassHierarchy) { m_data->AddPassTemplatesToLibrary(); @@ -587,62 +589,55 @@ namespace UnitTest parent2->AsParent()->AddChild(parent1); parent1->AsParent()->AddChild(pass); - { - // Filter with only pass name - PassHierarchyFilter filter(Name("pass1")); - EXPECT_TRUE(filter.Matches(pass.get())); - } - { // Filter with pass hierarchy which has only one element - PassHierarchyFilter filter({ Name("pass1") }); + PassFilter filter = PassFilter::CreateWithPassHierarchy({Name("pass1")}); EXPECT_TRUE(filter.Matches(pass.get())); } { - // Filter with empty pass hierarchy. Result one assert + // Filter with empty pass hierarchy, triggers one assert AZ_TEST_START_TRACE_SUPPRESSION; - PassHierarchyFilter filter(AZStd::vector{}); + PassFilter filter = PassFilter::CreateWithPassHierarchy(AZStd::vector{}); AZ_TEST_STOP_TRACE_SUPPRESSION(1); - EXPECT_FALSE(filter.Matches(pass.get())); } { // Filters with partial hierarchy by using string vector AZStd::vector passHierarchy1 = { "parent1", "pass1" }; - PassHierarchyFilter filter1(passHierarchy1); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(passHierarchy1); EXPECT_TRUE(filter1.Matches(pass.get())); AZStd::vector passHierarchy2 = { "parent2", "pass1" }; - PassHierarchyFilter filter2(passHierarchy2); + PassFilter filter2 = PassFilter::CreateWithPassHierarchy(passHierarchy2); EXPECT_TRUE(filter2.Matches(pass.get())); AZStd::vector passHierarchy3 = { "parent3", "parent2", "pass1" }; - PassHierarchyFilter filter3(passHierarchy3); + PassFilter filter3 = PassFilter::CreateWithPassHierarchy(passHierarchy3); EXPECT_TRUE(filter3.Matches(pass.get())); } { // Filters with partial hierarchy by using Name vector AZStd::vector passHierarchy1 = { Name("parent1"), Name("pass1") }; - PassHierarchyFilter filter1(passHierarchy1); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(passHierarchy1); EXPECT_TRUE(filter1.Matches(pass.get())); AZStd::vector passHierarchy2 = { Name("parent2"), Name("pass1")}; - PassHierarchyFilter filter2(passHierarchy2); + PassFilter filter2 = PassFilter::CreateWithPassHierarchy(passHierarchy2); EXPECT_TRUE(filter2.Matches(pass.get())); AZStd::vector passHierarchy3 = { Name("parent3"), Name("parent2"), Name("pass1") }; - PassHierarchyFilter filter3(passHierarchy3); + PassFilter filter3 = PassFilter::CreateWithPassHierarchy(passHierarchy3); EXPECT_TRUE(filter3.Matches(pass.get())); } { // Find non-leaf pass - PassHierarchyFilter filter1(AZStd::vector{"parent3", "parent1"}); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(AZStd::vector{"parent3", "parent1"}); EXPECT_TRUE(filter1.Matches(parent1.get())); - - PassHierarchyFilter filter2(Name("parent1")); + + PassFilter filter2 = PassFilter::CreateWithPassHierarchy({ Name("parent1") }); EXPECT_TRUE(filter2.Matches(parent1.get())); EXPECT_FALSE(filter2.Matches(pass.get())); } @@ -650,11 +645,131 @@ namespace UnitTest { // Failed to find pass // Mis-matching hierarchy - PassHierarchyFilter filter1(AZStd::vector{"Parent1", "Parent3", "pass1"}); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(AZStd::vector{"Parent1", "Parent3", "pass1"}); EXPECT_FALSE(filter1.Matches(pass.get())); // Mis-matching name - PassHierarchyFilter filter2(AZStd::vector{"Parent1", "pass1"}); + PassFilter filter2 = PassFilter::CreateWithPassHierarchy(AZStd::vector{"Parent1", "pass1"}); EXPECT_FALSE(filter2.Matches(parent1.get())); } } + + TEST_F(PassTests, PassFilter_Empty_Success) + { + m_data->AddPassTemplatesToLibrary(); + + // create a pass tree + Ptr pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + Ptr parent2 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent2")); + Ptr parent3 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent3")); + + parent3->AsParent()->AddChild(parent2); + parent2->AsParent()->AddChild(parent1); + parent1->AsParent()->AddChild(pass); + + PassFilter filter; + + // Any pass can match an empty filter + EXPECT_TRUE(filter.Matches(pass.get())); + EXPECT_TRUE(filter.Matches(parent1.get())); + EXPECT_TRUE(filter.Matches(parent2.get())); + EXPECT_TRUE(filter.Matches(parent3.get())); + } + + TEST_F(PassTests, PassFilter_PassClass_Success) + { + m_data->AddPassTemplatesToLibrary(); + + // create a pass tree + Ptr pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr depthPass = m_passSystem->CreatePassFromTemplate(Name("DepthPrePass"), Name("depthPass")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + + parent1->AsParent()->AddChild(pass); + parent1->AsParent()->AddChild(depthPass); + + PassFilter filter1 = PassFilter::CreateWithPassClass(); + + EXPECT_TRUE(filter1.Matches(pass.get())); + EXPECT_FALSE(filter1.Matches(parent1.get())); + + PassFilter filter2 = PassFilter::CreateWithPassClass(); + EXPECT_FALSE(filter2.Matches(pass.get())); + EXPECT_TRUE(filter2.Matches(parent1.get())); + } + + TEST_F(PassTests, PassFilter_PassTemplate_Success) + { + m_data->AddPassTemplatesToLibrary(); + + // create a pass tree + Ptr childPass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + + PassFilter filter1 = PassFilter::CreateWithTemplateName(Name("Pass"), (Scene*) nullptr); + // childPass doesn't have a template + EXPECT_FALSE(filter1.Matches(childPass.get())); + + PassFilter filter2 = PassFilter::CreateWithTemplateName(Name("ParentPass"), (Scene*) nullptr); + EXPECT_TRUE(filter2.Matches(parent1.get())); + } + + TEST_F(PassTests, ForEachPass_PassTemplateFilter_Success) + { + m_data->AddPassTemplatesToLibrary(); + + // create a pass tree + Ptr pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + Ptr parent2 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent2")); + Ptr parent3 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent3")); + + parent3->AsParent()->AddChild(parent2); + parent2->AsParent()->AddChild(parent1); + parent1->AsParent()->AddChild(pass); + + // Create render pipeline + const RPI::PipelineViewTag viewTag{ "viewTag1" }; + RPI::RenderPipelineDescriptor desc; + desc.m_mainViewTagName = viewTag.GetStringView(); + desc.m_name = "TestPipeline"; + RPI::RenderPipelinePtr pipeline = RPI::RenderPipeline::CreateRenderPipeline(desc); + Ptr parent4 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent4")); + pipeline->GetRootPass()->AddChild(parent4); + + Name templateName = Name("ParentPass"); + PassFilter filter1 = PassFilter::CreateWithTemplateName(templateName, (RenderPipeline*)nullptr); + + int count = 0; + m_passSystem->ForEachPass(filter1, [&count, templateName](RPI::Pass* pass) -> PassFilterExecutionFlow + { + EXPECT_TRUE(pass->GetPassTemplate()->m_name == templateName); + count++; + return PassFilterExecutionFlow::ContinueVisitingPasses; + }); + + // three from CreatePassFromTemplate() calls and one from Render Pipeline. + EXPECT_TRUE(count == 4); + + count = 0; + m_passSystem->ForEachPass(filter1, [&count, templateName](RPI::Pass* pass) -> PassFilterExecutionFlow + { + EXPECT_TRUE(pass->GetPassTemplate()->m_name == templateName); + count++; + return PassFilterExecutionFlow::StopVisitingPasses; + }); + EXPECT_TRUE(count == 1); + + PassFilter filter2 = PassFilter::CreateWithTemplateName(templateName, pipeline.get()); + count = 0; + m_passSystem->ForEachPass(filter2, [&count]([[maybe_unused]] RPI::Pass* pass) -> PassFilterExecutionFlow + { + count++; + return PassFilterExecutionFlow::ContinueVisitingPasses; + }); + + // only the ParentPass in the render pipeline was found + EXPECT_TRUE(count == 1); + + } } diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp index a0be18f0e2..161160e16f 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -142,12 +143,13 @@ namespace AZ EnablePasses(true); } - void HairFeatureProcessor::EnablePasses([[maybe_unused]] bool enable) + void HairFeatureProcessor::EnablePasses(bool enable) { - RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName); - if (desiredPass) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, GetParentScene()); + RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); + if (pass) { - desiredPass->SetEnabled(enable); + pass->SetEnabled(enable); } } @@ -309,10 +311,17 @@ namespace AZ m_forceClearRenderData = true; } + bool HairFeatureProcessor::HasHairParentPass() + { + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, GetParentScene()); + RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); + return pass; + } + void HairFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!renderPipeline.get()->GetRootPass()->FindPassByNameRecursive(HairParentPassName)) + if (!HasHairParentPass()) { return; } @@ -323,10 +332,10 @@ namespace AZ m_forceRebuildRenderData = true; } - void HairFeatureProcessor::OnRenderPipelineRemoved(RPI::RenderPipeline* renderPipeline) + void HairFeatureProcessor::OnRenderPipelineRemoved([[maybe_unused]] RPI::RenderPipeline* renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName)) + if (!HasHairParentPass()) { return; } @@ -338,7 +347,7 @@ namespace AZ void HairFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName)) + if (!HasHairParentPass()) { return; } @@ -457,7 +466,8 @@ namespace AZ { m_computePasses[passName] = nullptr; - RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(passName); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(passName, m_renderPipeline); + RPI::Ptr desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); if (desiredPass) { m_computePasses[passName] = static_cast(desiredPass.get()); @@ -478,8 +488,9 @@ namespace AZ bool HairFeatureProcessor::InitPPLLFillPass() { m_hairPPLLRasterPass = nullptr; // reset it to null, just in case it fails to load the assets properly - - RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLRasterPassName); + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairPPLLRasterPassName, m_renderPipeline); + RPI::Ptr desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); if (desiredPass) { m_hairPPLLRasterPass = static_cast(desiredPass.get()); @@ -497,7 +508,8 @@ namespace AZ { m_hairPPLLResolvePass = nullptr; // reset it to null, just in case it fails to load the assets properly - RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLResolvePassName); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairPPLLResolvePassName, m_renderPipeline); + RPI::Ptr desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); if (desiredPass) { m_hairPPLLResolvePass = static_cast(desiredPass.get()); @@ -518,8 +530,8 @@ namespace AZ m_hairShortCutGeometryDepthAlphaPass = nullptr; m_hairShortCutGeometryShadingPass = nullptr; - m_hairShortCutGeometryDepthAlphaPass = static_cast( - m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryDepthAlphaPassName).get()); + RPI::PassFilter depthAlphaPassFilter = RPI::PassFilter::CreateWithPassName(HairShortCutGeometryDepthAlphaPassName, m_renderPipeline); + m_hairShortCutGeometryDepthAlphaPass = static_cast(RPI::PassSystemInterface::Get()->FindFirstPass(depthAlphaPassFilter)); if (m_hairShortCutGeometryDepthAlphaPass) { m_hairShortCutGeometryDepthAlphaPass->SetFeatureProcessor(this); @@ -530,8 +542,8 @@ namespace AZ return false; } - m_hairShortCutGeometryShadingPass = static_cast( - m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryShadingPassName).get()); + RPI::PassFilter shaderingPassFilter = RPI::PassFilter::CreateWithPassName(HairShortCutGeometryShadingPassName, m_renderPipeline); + m_hairShortCutGeometryShadingPass = static_cast(RPI::PassSystemInterface::Get()->FindFirstPass(shaderingPassFilter)); if (m_hairShortCutGeometryShadingPass) { m_hairShortCutGeometryShadingPass->SetFeatureProcessor(this); diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h index 46660a6623..f810967824 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h @@ -165,6 +165,8 @@ namespace AZ void EnablePasses(bool enable); + bool HasHairParentPass(); + //! The following will serve to register the FP in the Thumbnail system AZStd::vector m_hairFeatureProcessorRegistryName; From a5694a5ac65093dcb7b713b97f7382d987a7feed Mon Sep 17 00:00:00 2001 From: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com> Date: Mon, 25 Oct 2021 12:49:57 -0700 Subject: [PATCH 025/120] ATOM-16489 Add find passes functions for Scene or RenderPipeline in PassSystemInterface (#4739) (#4963) * ATOM-16489 Add find passes functions for Scene or RenderPipeline in PassSystemInterface Introduced new PassSystemInterface::ForEachPass() funtion to replace PassSystemInterface::FindPasses(), PassSystemInterface::GetPassesByTemplateName and ParentPass::FindPassByNameRecursive() functions. Update all the places which were using those three functions. The new pass finding filter support any combination of pass name, pass template name, pass class type, pass hirechary, owner scene, owner render pipeline. Update unit tests. Signed-off-by: Qing Tao (cherry picked from commit fe8dac798977a2271a2a5775d947d7172949866e) --- .../Include/Atom/Feature/ImGui/ImGuiUtils.h | 6 +- .../Include/Atom/Feature/ImGui/SystemBus.h | 7 +- .../Atom/Feature/Utils/FrameCaptureBus.h | 2 +- .../DirectionalLightFeatureProcessor.cpp | 71 ++--- ...fuseGlobalIlluminationFeatureProcessor.cpp | 57 ++-- .../DiffuseProbeGridFeatureProcessor.cpp | 12 +- .../DisplayMapper/DisplayMapperPass.cpp | 25 +- .../Source/FrameCaptureSystemComponent.cpp | 37 +-- .../Source/ImGui/ImGuiSystemComponent.cpp | 48 +-- .../Code/Source/ImGui/ImGuiSystemComponent.h | 2 +- .../DepthOfField/DepthOfFieldSettings.cpp | 20 +- .../ExposureControlSettings.cpp | 27 +- .../LookModificationCompositePass.cpp | 14 +- .../PostProcessing/SMAAFeatureProcessor.cpp | 51 ++-- .../ProfilingCaptureSystemComponent.cpp | 31 +- .../Source/ProfilingCaptureSystemComponent.h | 2 - .../ReflectionCopyFrameBufferPass.cpp | 19 +- .../ReflectionScreenSpaceBlurPass.cpp | 2 +- .../ReflectionScreenSpaceCompositePass.cpp | 26 +- .../ProjectedShadowFeatureProcessor.cpp | 54 ++-- .../Shadows/ProjectedShadowFeatureProcessor.h | 4 +- .../SkinnedMeshFeatureProcessor.cpp | 13 +- .../SkinnedMesh/SkinnedMeshFeatureProcessor.h | 2 +- .../Include/Atom/RPI.Public/Pass/ParentPass.h | 3 - .../Code/Include/Atom/RPI.Public/Pass/Pass.h | 4 + .../Include/Atom/RPI.Public/Pass/PassFilter.h | 136 ++++----- .../Atom/RPI.Public/Pass/PassLibrary.h | 4 +- .../Include/Atom/RPI.Public/Pass/PassSystem.h | 4 +- .../RPI.Public/Pass/PassSystemInterface.h | 21 +- .../Source/RPI.Public/Pass/ParentPass.cpp | 23 -- .../RPI/Code/Source/RPI.Public/Pass/Pass.cpp | 5 + .../Source/RPI.Public/Pass/PassFilter.cpp | 285 ++++++++++++++---- .../Source/RPI.Public/Pass/PassLibrary.cpp | 90 ++++-- .../Source/RPI.Public/Pass/PassSystem.cpp | 22 +- Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp | 159 ++++++++-- .../Code/Rendering/HairFeatureProcessor.cpp | 44 ++- .../Code/Rendering/HairFeatureProcessor.h | 2 + 37 files changed, 800 insertions(+), 534 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h index a200f30c98..52e272a9c4 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h @@ -50,12 +50,12 @@ namespace AZ return scope; } - //! Sets the active context based on the provided PassHierarchyFilter. If the filter doesn't match exactly one pass, then do nothing. - static ImGuiActiveContextScope FromPass(const RPI::PassHierarchyFilter& passHierarchyFilter) + //! Sets the active context based on the provided pass hierarchy filter. If the filter doesn't match exactly one pass, then do nothing. + static ImGuiActiveContextScope FromPass(const AZStd::vector& passHierarchy) { ImGuiActiveContextScope scope; scope.ConnectToImguiNotificationBus(); - ImGuiSystemRequestBus::BroadcastResult(scope.m_isEnabled, &ImGuiSystemRequests::PushActiveContextFromPass, passHierarchyFilter); + ImGuiSystemRequestBus::BroadcastResult(scope.m_isEnabled, &ImGuiSystemRequests::PushActiveContextFromPass, passHierarchy); return scope; } diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h index 289cb178d4..78f71b39ba 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h @@ -15,11 +15,6 @@ namespace AZ { - namespace RPI - { - class PassHierarchyFilter; - } - namespace Render { class ImGuiPass; @@ -51,7 +46,7 @@ namespace AZ //! Pushes whichever ImGui pass is default on the top of the active context stack. Returns true/false for success/fail. virtual bool PushActiveContextFromDefaultPass() = 0; //! Pushes whichever ImGui pass is provided in passHierarchy on the top of the active context stack. Returns true/false for success/fail. - virtual bool PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchy) = 0; + virtual bool PushActiveContextFromPass(const AZStd::vector& passHierarchy) = 0; //! Pops the active context off the top of the active context stack. Returns true if there's a context to pop. virtual bool PopActiveContext() = 0; //! Gets the context at the top of the active context stack. Returns nullptr if the stack is emtpy. diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h index 93600ec08f..c80926700e 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h @@ -50,7 +50,7 @@ namespace AZ virtual bool CaptureScreenshotWithPreview(const AZStd::string& outputFilePath) = 0; //! Save a buffer attachment or a image attachment binded to a pass's slot to a data file. - //! @param passHierarchy For finding the pass by using PassHierarchyFilter + //! @param passHierarchy For finding the pass by using a pass hierarchy filter. Check PassFilter::CreateWithPassHierarchy() function for detail //! @param slotName Name of the pass's slot. The attachment bound to this slot will be captured. //! @param option Only valid for an InputOutput attachment. Use PassAttachmentReadbackOption::Input to capture the input state //! and use PassAttachmentReadbackOption::Output to capture the output state diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index 6c24b7d35b..b6c6910fd3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -647,54 +647,46 @@ namespace AZ UpdateViewsOfCascadeSegments(); } - void DirectionalLightFeatureProcessor::CacheCascadedShadowmapsPass() { - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->GetPassesForTemplateName(Name("CascadedShadowmapsTemplate")); + void DirectionalLightFeatureProcessor::CacheCascadedShadowmapsPass() + { m_cascadedShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - if (RPI::RenderPipeline* pipeline = pass->GetRenderPipeline()) + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("CascadedShadowmapsTemplate"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { + RPI::RenderPipeline* pipeline = pass->GetRenderPipeline(); const RPI::RenderPipelineId pipelineId = pipeline->GetId(); - // This function can be called when the pipeline is not attached to the scene. - // So we check it is attached to the scene. - if (GetParentScene()->GetRenderPipeline(pipelineId).get() == pipeline) + + CascadedShadowmapsPass* shadowPass = azrtti_cast(pass); + AZ_Assert(shadowPass, "It is not a CascadedShadowmapPass."); + if (pipeline->GetDefaultView()) { - CascadedShadowmapsPass* shadowPass = azrtti_cast(pass); - AZ_Assert(shadowPass, "It is not a CascadedShadowmapPass."); - if (pipeline->GetDefaultView()) - { - m_cascadedShadowmapsPasses[pipelineId].push_back(shadowPass); - } + m_cascadedShadowmapsPasses[pipelineId].push_back(shadowPass); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void DirectionalLightFeatureProcessor::CacheEsmShadowmapsPass() { - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->GetPassesForTemplateName(Name("EsmShadowmapsTemplate")); m_esmShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - if (RPI::RenderPipeline* pipeline = pass->GetRenderPipeline()) + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("EsmShadowmapsTemplate"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - const RPI::RenderPipelineId pipelineId = pipeline->GetId(); - // checking the render pipeline is just removed from the scene. - if (GetParentScene()->GetRenderPipeline(pipelineId).get() == pipeline) + const RPI::RenderPipelineId pipelineId = pass->GetRenderPipeline()->GetId(); + + if (m_cascadedShadowmapsPasses.find(pipelineId) != m_cascadedShadowmapsPasses.end()) { - if (m_cascadedShadowmapsPasses.find(pipelineId) != m_cascadedShadowmapsPasses.end()) + EsmShadowmapsPass* esmPass = azrtti_cast(pass); + AZ_Assert(esmPass, "It is not an EsmShadowmapPass."); + if (esmPass->GetLightTypeName() == m_lightTypeName) { - EsmShadowmapsPass* esmPass = azrtti_cast(pass); - AZ_Assert(esmPass, "It is not an EsmShadowmapPass."); - if (m_cascadedShadowmapsPasses.find(esmPass->GetRenderPipeline()->GetId()) != m_cascadedShadowmapsPasses.end() && - esmPass->GetLightTypeName() == m_lightTypeName) - { - m_esmShadowmapsPasses[pipelineId].push_back(esmPass); - } + m_esmShadowmapsPasses[pipelineId].push_back(esmPass); } } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void DirectionalLightFeatureProcessor::PrepareCameraViews() @@ -1063,12 +1055,13 @@ namespace AZ // if the shadow is rendering in an EnvironmentCubeMapPass it also needs to be a ReflectiveCubeMap view, // to filter out shadows from objects that are excluded from the cubemap - RPI::PassClassFilter passFilter; - AZStd::vector cubeMapPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (!cubeMapPasses.empty()) - { - usageFlags |= RPI::View::UsageReflectiveCubeMap; - } + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + passFilter.SetOwenrScene(GetParentScene()); // only handles passes for this scene + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [&usageFlags]([[maybe_unused]] RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + usageFlags |= RPI::View::UsageReflectiveCubeMap; + return RPI::PassFilterExecutionFlow::StopVisitingPasses; + }); segment.m_view = RPI::View::CreateView(viewName, usageFlags); } diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp index c085b26e31..453dbbc0ab 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp @@ -80,35 +80,48 @@ namespace AZ } // update the size multiplier on the DiffuseProbeGridDownsamplePass output - AZStd::vector downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") }; - RPI::PassHierarchyFilter downsamplePassFilter(downsamplePassHierarchy); - const AZStd::vector& downsamplePasses = RPI::PassSystemInterface::Get()->FindPasses(downsamplePassFilter); - for (RPI::Pass* pass : downsamplePasses) + // NOTE: The ownerScene wasn't added to both filters. This is because the passes from the non-owner scene may have invalid SRG values which could lead to + // GPU error if the scene doesn't have this feature processor enabled. + // For example, the ASV MultiScene sample may have TDR. { - for (uint32_t outputIndex = 0; outputIndex < pass->GetOutputCount(); ++outputIndex) - { - RPI::Ptr outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment; - RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers; + AZStd::vector downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") }; + RPI::PassFilter downsamplePassFilter = RPI::PassFilter::CreateWithPassHierarchy(downsamplePassHierarchy); + RPI::PassSystemInterface::Get()->ForEachPass( + downsamplePassFilter, + [sizeMultiplier](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + for (uint32_t outputIndex = 0; outputIndex < pass->GetOutputCount(); ++outputIndex) + { + RPI::Ptr outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment; + RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers; - sizeMultipliers.m_widthMultiplier = sizeMultiplier; - sizeMultipliers.m_heightMultiplier = sizeMultiplier; - } + sizeMultipliers.m_widthMultiplier = sizeMultiplier; + sizeMultipliers.m_heightMultiplier = sizeMultiplier; + } - // set the output scale on the PassSrg - RPI::FullscreenTrianglePass* downsamplePass = static_cast(pass); - auto constantIndex = downsamplePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_outputImageScale")); - downsamplePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + // set the output scale on the PassSrg + RPI::FullscreenTrianglePass* downsamplePass = static_cast(pass); + RHI::ShaderInputNameIndex outputImageScaleShaderInput = "m_outputImageScale"; + downsamplePass->GetShaderResourceGroup()->SetConstant( + outputImageScaleShaderInput, aznumeric_cast(1.0f / sizeMultiplier)); + + // handle all downsample passes + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } // update the image scale on the DiffuseComposite pass - AZStd::vector compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") }; - RPI::PassHierarchyFilter compositePassFilter(compositePassHierarchy); - const AZStd::vector& compositePasses = RPI::PassSystemInterface::Get()->FindPasses(compositePassFilter); - for (RPI::Pass* pass : compositePasses) { - RPI::FullscreenTrianglePass* compositePass = static_cast(pass); - auto constantIndex = compositePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_imageScale")); - compositePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + AZStd::vector compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") }; + RPI::PassFilter compositePassFilter = RPI::PassFilter::CreateWithPassHierarchy(compositePassHierarchy); + RPI::PassSystemInterface::Get()->ForEachPass(compositePassFilter, [sizeMultiplier](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + RPI::FullscreenTrianglePass* compositePass = static_cast(pass); + RHI::ShaderInputNameIndex imageScaleShaderInput = "m_imageScale"; + compositePass->GetShaderResourceGroup()->SetConstant(imageScaleShaderInput, aznumeric_cast(1.0f / sizeMultiplier)); + + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } } } // namespace Render diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp index 4329fd556c..5ea4748fc9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp @@ -603,12 +603,12 @@ namespace AZ RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); if (device->GetFeatures().m_rayTracing == false) { - RPI::PassHierarchyFilter updatePassFilter(AZ::Name("DiffuseProbeGridUpdatePass")); - const AZStd::vector& updatePasses = RPI::PassSystemInterface::Get()->FindPasses(updatePassFilter); - for (RPI::Pass* pass : updatePasses) - { - pass->SetEnabled(false); - } + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("DiffuseProbeGridUpdatePass"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + pass->SetEnabled(false); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp index 3ec8840a1c..5b5599383e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp @@ -10,9 +10,10 @@ #include #include #include -#include +#include #include #include +#include #include #include #include @@ -66,22 +67,14 @@ namespace AZ { // Need to invalidate the CopyToSwapChain pass so that it updates the pipeline state in the event that // the swapchain format changed (for example, moving from LDR to HDR display) - auto* passSystem = RPI::PassSystemInterface::Get(); - const Name fullscreenCopyTemplateName("FullscreenCopyTemplate"); - - if (passSystem->HasPassesForTemplateName(fullscreenCopyTemplateName)) - { - const AZStd::vector& passes = passSystem->GetPassesForTemplateName(fullscreenCopyTemplateName); - for (RPI::Pass* pass : passes) + const Name copyToSwapChainPassName("CopyToSwapChain"); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(copyToSwapChainPassName, GetRenderPipeline()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - RPI::FullscreenTrianglePass* fullscreenTrianglePass = azrtti_cast(pass); - const Name& passName = fullscreenTrianglePass->GetName(); - if (passName.GetStringView() == "CopyToSwapChain") - { - fullscreenTrianglePass->QueueForInitialization(); - } - } - } + pass->QueueForInitialization(); + return RPI::PassFilterExecutionFlow::StopVisitingPasses; + }); + ConfigureDisplayParameters(); } diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp index 8c0360dec2..ed2d8a1d9f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp @@ -372,29 +372,25 @@ namespace AZ } m_latestCaptureInfo.clear(); - // Find the pass first - RPI::PassClassFilter passFilter; - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); - - if (foundPasses.size() == 0) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + AZ::RPI::ImageAttachmentPreviewPass* previewPass = azrtti_cast(RPI::PassSystemInterface::Get()->FindFirstPass(passFilter)); + if (!previewPass) { - AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find an ImageAttachmentPreviewPass pass "); + AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find an ImageAttachmentPreviewPass"); return false; } - AZ::RPI::ImageAttachmentPreviewPass* previewPass = azrtti_cast(foundPasses[0]); bool result = previewPass->ReadbackOutput(m_readback); if (result) { m_state = State::Pending; m_result = FrameCaptureResult::None; SystemTickBus::Handler::BusConnect(); + return true; } - else - { - AZ_Warning("FrameCaptureSystemComponent", false, "CaptureScreenshotWithPreview. Failed to readback output from the ImageAttachmentPreviewPass");; - } - return result; + + AZ_Warning("FrameCaptureSystemComponent", false, "CaptureScreenshotWithPreview. Failed to readback output from the ImageAttachmentPreviewPass"); + return false; } bool FrameCaptureSystemComponent::CapturePassAttachment(const AZStd::vector& passHierarchy, const AZStd::string& slot, @@ -405,6 +401,12 @@ namespace AZ return false; } + if (passHierarchy.size() == 0) + { + AZ_Warning("FrameCaptureSystemComponent", false, "Empty data in passHierarchy"); + return false; + } + InitReadback(); if (m_state != State::Idle) @@ -426,17 +428,15 @@ namespace AZ } m_latestCaptureInfo.clear(); - // Find the pass first - AZ::RPI::PassHierarchyFilter passFilter(passHierarchy); - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassHierarchy(passHierarchy); + RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); - if (foundPasses.size() == 0) + if (!pass) { - AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find pass from %s", passFilter.ToString().c_str()); + AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find pass from %s", passHierarchy[0].c_str()); return false; } - AZ::RPI::Pass* pass = foundPasses[0]; if (pass->ReadbackAttachment(m_readback, Name(slot), option)) { m_state = State::Pending; @@ -444,6 +444,7 @@ namespace AZ SystemTickBus::Handler::BusConnect(); return true; } + AZ_Warning("FrameCaptureSystemComponent", false, "Failed to readback the attachment bound to pass [%s] slot [%s]", pass->GetName().GetCStr(), slot.c_str()); return false; } diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp index afca20c5cb..3783752e67 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp @@ -109,15 +109,15 @@ namespace AZ void ImGuiSystemComponent::ForAllImGuiPasses(PassFunction func) { ImGuiContext* contextToRestore = ImGui::GetCurrentContext(); - RPI::PassClassFilter filter; - auto imguiPasses = RPI::PassSystemInterface::Get()->FindPasses(filter); - - for (RPI::Pass* pass : imguiPasses) - { - ImGuiPass* imguiPass = azrtti_cast(pass); - ImGui::SetCurrentContext(imguiPass->GetContext()); - func(imguiPass); - } + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [func](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + ImGuiPass* imguiPass = azrtti_cast(pass); + ImGui::SetCurrentContext(imguiPass->GetContext()); + func(imguiPass); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); ImGui::SetCurrentContext(contextToRestore); } @@ -169,29 +169,37 @@ namespace AZ return false; } - bool ImGuiSystemComponent::PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchyFilter) + bool ImGuiSystemComponent::PushActiveContextFromPass(const AZStd::vector& passHierarchyFilter) { - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passHierarchyFilter); + if (passHierarchyFilter.size() == 0) + { + AZ_Warning("ImGuiSystemComponent", false, "passHierarchyFilter is empty"); + return false; + } + AZStd::vector foundImGuiPasses; - for (RPI::Pass* pass : foundPasses) - { - ImGuiPass* imGuiPass = azrtti_cast(pass); - if (imGuiPass) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassHierarchy(passHierarchyFilter); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [&foundImGuiPasses](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - foundImGuiPasses.push_back(imGuiPass); - } - } + ImGuiPass* imGuiPass = azrtti_cast(pass); + if (imGuiPass) + { + foundImGuiPasses.push_back(imGuiPass); + } + + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); if (foundImGuiPasses.size() == 0) { - AZ_Warning("ImGuiSystemComponent", false, "Failed to find ImGui pass to activate from %s", passHierarchyFilter.ToString().c_str()); + AZ_Warning("ImGuiSystemComponent", false, "Failed to find ImGui pass to activate from %s", passHierarchyFilter[0].c_str()); return false; } if (foundImGuiPasses.size() > 1) { - AZ_Warning("ImGuiSystemComponent", false, "Found more than one ImGui pass to activate from %s, only activating first one.", passHierarchyFilter.ToString().c_str()); + AZ_Warning("ImGuiSystemComponent", false, "Found more than one ImGui pass to activate from %s, only activating first one.", passHierarchyFilter[0].c_str()); } ImGuiContext* context = foundImGuiPasses.at(0)->GetContext(); diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h index 838cf0b3c2..d59124890f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h @@ -56,7 +56,7 @@ namespace AZ ImGuiPass* GetDefaultImGuiPass() override; bool PushActiveContextFromDefaultPass() override; - bool PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchy) override; + bool PushActiveContextFromPass(const AZStd::vector& passHierarchy) override; bool PopActiveContext() override; ImGuiContext* GetActiveContext() override; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp index fe1ce8a281..98b527868d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -259,24 +260,19 @@ namespace AZ // [GFX TODO][ATOM-3035]This function is temporary and will change with improvement to the draw list tag system void DepthOfFieldSettings::UpdateAutoFocusDepth(bool enabled) - { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); + { const Name TemplateNameReadBackFocusDepth = Name("DepthOfFieldReadBackFocusDepthTemplate"); - if (passSystem->HasPassesForTemplateName(TemplateNameReadBackFocusDepth)) - { - const AZStd::vector& dofPasses = passSystem->GetPassesForTemplateName(TemplateNameReadBackFocusDepth); - for (RPI::Pass* pass : dofPasses) + // [GFX TODO][ATOM-4908] multiple camera should be distingushed. + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(TemplateNameReadBackFocusDepth, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this, enabled](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { auto* dofPass = azrtti_cast(pass); - // Check this pass belongs to a render pipeline of the scene. - // [GFX TODO][ATOM-4908] multiple camera should be distingushed. - const RPI::RenderPipelineId pipelineId = dofPass->GetRenderPipeline()->GetId(); - if (enabled && GetParentScene()->GetRenderPipeline(pipelineId)) + if (enabled) { m_normalizedFocusDistanceForAutoFocus = dofPass->GetNormalizedFocusDistanceForAutoFocus(); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void DepthOfFieldSettings::SetCameraEntityId(EntityId cameraEntityId) diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp index 96ca424307..26c2a61d54 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -188,21 +189,21 @@ namespace AZ void ExposureControlSettings::UpdateLuminanceHeatmap() { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - // [GFX-TODO][ATOM-13194] Support multiple views for the luminance heatmap - // [GFX-TODO][ATOM-13224] Remove UpdateLuminanceHeatmap and UpdateEyeAdaptationPass - const RPI::Ptr luminanceHeatmap = passSystem->GetRootPass()->FindPassByNameRecursive(m_luminanceHeatmapNameId); - if (luminanceHeatmap) - { - luminanceHeatmap->SetEnabled(m_heatmapEnabled); - } + // [GFX-TODO][ATOM-13224] Remove UpdateLuminanceHeatmap and UpdateEyeAdaptationPass + RPI::PassFilter heatmapPassFilter = RPI::PassFilter::CreateWithPassName(m_luminanceHeatmapNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(heatmapPassFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + pass->SetEnabled(m_heatmapEnabled); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); - const RPI::Ptr histogramGenerator = passSystem->GetRootPass()->FindPassByNameRecursive(m_luminanceHistogramGeneratorNameId); - if (histogramGenerator) - { - histogramGenerator->SetEnabled(m_heatmapEnabled); - } + RPI::PassFilter histogramPassFilter = RPI::PassFilter::CreateWithPassName(m_luminanceHistogramGeneratorNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(histogramPassFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + pass->SetEnabled(m_heatmapEnabled); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void ExposureControlSettings::UpdateBuffer() diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp index 85c45de96b..aac3d1d941 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp @@ -31,12 +31,14 @@ namespace AZ 0, [](const uint8_t& value) { - auto passes = RPI::PassSystem::Get()->FindPasses(RPI::PassClassFilter()); - for (auto* pass : passes) - { - LookModificationCompositePass* lookModPass = azrtti_cast(pass); - lookModPass->SetSampleQuality(LookModificationCompositePass::SampleQuality(value)); - } + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [value](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + LookModificationCompositePass* lookModPass = azrtti_cast(pass); + lookModPass->SetSampleQuality(LookModificationCompositePass::SampleQuality(value)); + + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); }, ConsoleFunctorFlags::Null, "This can be increased to deal with particularly tricky luts. Range (0-2). 0 (default) - Standard linear sampling. 1 - 7 tap b-spline sampling. 2 - 19 tap b-spline sampling." diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp index eef0c51e95..ad059fbec0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -71,26 +72,18 @@ namespace AZ void SMAAFeatureProcessor::UpdateConvertToPerceptualPass() { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - - if (passSystem->HasPassesForTemplateName(m_convertToPerceptualColorPassTemplateNameId)) - { - const AZStd::vector& convertToPerceptualColorPasses = passSystem->GetPassesForTemplateName(m_convertToPerceptualColorPassTemplateNameId); - for (RPI::Pass* pass : convertToPerceptualColorPasses) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_convertToPerceptualColorPassTemplateNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { pass->SetEnabled(m_data.m_enable); - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void SMAAFeatureProcessor::UpdateEdgeDetectionPass() - { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - - if (passSystem->HasPassesForTemplateName(m_edgeDetectioPassTemplateNameId)) - { - const AZStd::vector& edgeDetectionPasses = passSystem->GetPassesForTemplateName(m_edgeDetectioPassTemplateNameId); - for (RPI::Pass* pass : edgeDetectionPasses) + { + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_edgeDetectioPassTemplateNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { auto* edgeDetectionPass = azrtti_cast(pass); @@ -106,18 +99,14 @@ namespace AZ edgeDetectionPass->SetPredicationScale(m_data.m_predicationScale); edgeDetectionPass->SetPredicationStrength(m_data.m_predicationStrength); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void SMAAFeatureProcessor::UpdateBlendingWeightCalculationPass() { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - - if (passSystem->HasPassesForTemplateName(m_blendingWeightCalculationPassTemplateNameId)) - { - const AZStd::vector& blendingWeightCalculationPasses = passSystem->GetPassesForTemplateName(m_blendingWeightCalculationPassTemplateNameId); - for (RPI::Pass* pass : blendingWeightCalculationPasses) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_blendingWeightCalculationPassTemplateNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { auto* blendingWeightCalculationPass = azrtti_cast(pass); @@ -130,18 +119,14 @@ namespace AZ blendingWeightCalculationPass->SetDiagonalDetectionEnable(m_data.m_enableDiagonalDetection); blendingWeightCalculationPass->SetCornerDetectionEnable(m_data.m_enableCornerDetection); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void SMAAFeatureProcessor::UpdateNeighborhoodBlendingPass() { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - - if (passSystem->HasPassesForTemplateName(m_neighborhoodBlendingPassTemplateNameId)) - { - const AZStd::vector& neighborhoodBlendingPasses = passSystem->GetPassesForTemplateName(m_neighborhoodBlendingPassTemplateNameId); - for (RPI::Pass* pass : neighborhoodBlendingPasses) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_neighborhoodBlendingPassTemplateNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { auto* neighborhoodBlendingPass = azrtti_cast(pass); @@ -153,8 +138,8 @@ namespace AZ { neighborhoodBlendingPass->SetOutputMode(SMAAOutputMode::PassThrough); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void SMAAFeatureProcessor::Render([[maybe_unused]] const SMAAFeatureProcessor::RenderPacket& packet) diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp index 66adfe9985..9bfb2b7e9e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp @@ -377,14 +377,7 @@ namespace AZ bool ProfilingCaptureSystemComponent::CapturePassTimestamp(const AZStd::string& outputFilePath) { - // Find the root pass. - AZStd::vector passes = FindPasses({ "Root" }); - if (passes.empty()) - { - return false; - } - - RPI::Pass* root = passes[0]; + RPI::Pass* root = AZ::RPI::PassSystemInterface::Get()->GetRootPass().get(); // Enable all the Timestamp queries in passes. root->SetTimestampQueryEnabled(true); @@ -465,14 +458,7 @@ namespace AZ bool ProfilingCaptureSystemComponent::CapturePassPipelineStatistics(const AZStd::string& outputFilePath) { - // Find the root pass. - AZStd::vector passes = FindPasses({ "Root" }); - if (passes.empty()) - { - return false; - } - - RPI::Pass* root = passes[0]; + RPI::Pass* root = AZ::RPI::PassSystemInterface::Get()->GetRootPass().get(); // Enable all the PipelineStatistics queries in passes. root->SetPipelineStatisticsQueryEnabled(true); @@ -572,19 +558,6 @@ namespace AZ return passes; } - AZStd::vector ProfilingCaptureSystemComponent::FindPasses(AZStd::vector&& passHierarchy) const - { - // Find the pass first. - RPI::PassHierarchyFilter passFilter(passHierarchy); - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (foundPasses.size() == 0) - { - AZ_Warning("ProfilingCaptureSystemComponent", false, "Failed to find pass from %s", passFilter.ToString().c_str()); - } - - return foundPasses; - } - void ProfilingCaptureSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time) { // Update the delayed captures diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h index a9bb8c585f..af1d2f5643 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h @@ -78,8 +78,6 @@ namespace AZ // Recursively collect all the passes from the root pass. AZStd::vector CollectPassesRecursively(const RPI::Pass* root) const; - AZStd::vector FindPasses(AZStd::vector&& passHierarchy) const; - DelayedQueryCaptureHelper m_timestampCapture; DelayedQueryCaptureHelper m_cpuFrameTimeStatisticsCapture; DelayedQueryCaptureHelper m_pipelineStatisticsCapture; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp index 37b1885ee3..a1858a7e9d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp @@ -28,16 +28,17 @@ namespace AZ void ReflectionCopyFrameBufferPass::BuildInternal() { - RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass")); - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (!passes.empty()) - { - Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(passes.front()); - Data::Instance& frameBufferAttachment = blurPass->GetFrameBufferImageAttachment(); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("ReflectionScreenSpaceBlurPass"), GetRenderPipeline()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(pass); + Data::Instance& frameBufferAttachment = blurPass->GetFrameBufferImageAttachment(); - RPI::PassAttachmentBinding& outputBinding = GetOutputBinding(0); - AttachImageToSlot(outputBinding.m_name, frameBufferAttachment); - } + RPI::PassAttachmentBinding& outputBinding = GetOutputBinding(0); + AttachImageToSlot(outputBinding.m_name, frameBufferAttachment); + + return RPI::PassFilterExecutionFlow::StopVisitingPasses; + }); FullscreenTrianglePass::BuildInternal(); } diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp index 394a6fd406..edd7ad1013 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp @@ -150,7 +150,7 @@ namespace AZ auto transientImageDesc = RHI::ImageDescriptor::Create2D(imageBindFlags, mipSize.m_width, mipSize.m_height, RHI::Format::R16G16B16A16_FLOAT); RPI::PassAttachment* transientPassAttachment = aznew RPI::PassAttachment(); - AZStd::string transientAttachmentName = AZStd::string::format("ReflectionScreenSpace_BlurImage%d", mip); + AZStd::string transientAttachmentName = AZStd::string::format("%s.ReflectionScreenSpace_BlurImage%d", GetPathName().GetCStr(), mip); transientPassAttachment->m_name = transientAttachmentName; transientPassAttachment->m_path = transientAttachmentName; transientPassAttachment->m_lifetime = RHI::AttachmentLifetimeType::Transient; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp index 1cf227650c..1362191691 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp @@ -33,20 +33,22 @@ namespace AZ return; } - RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass")); - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (!passes.empty()) - { - Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(passes.front()); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("ReflectionScreenSpaceBlurPass"), GetRenderPipeline()); - // compute the max mip level based on the available mips in the previous frame image, and capping it - // to stay within a range that has reasonable data - const uint32_t MaxNumRoughnessMips = 8; - uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1; + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(pass); - auto constantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_maxMipLevel")); - m_shaderResourceGroup->SetConstant(constantIndex, maxMipLevel); - } + // compute the max mip level based on the available mips in the previous frame image, and capping it + // to stay within a range that has reasonable data + const uint32_t MaxNumRoughnessMips = 8; + uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1; + + auto constantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_maxMipLevel")); + m_shaderResourceGroup->SetConstant(constantIndex, maxMipLevel); + + return RPI::PassFilterExecutionFlow::StopVisitingPasses; + }); FullscreenTrianglePass::CompileResources(context); } diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp index 7c0b3563c7..70ccaa5702 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -313,52 +313,38 @@ namespace AZ::Render void ProjectedShadowFeatureProcessor::CachePasses() { - const AZStd::vector validPipelineIds = CacheProjectedShadowmapsPass(); - CacheEsmShadowmapsPass(validPipelineIds); + CacheProjectedShadowmapsPass(); + CacheEsmShadowmapsPass(); m_shadowmapPassNeedsUpdate = true; } - AZStd::vector ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass() + void ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass() { - const AZStd::vector& renderPipelines = GetParentScene()->GetRenderPipelines(); - const auto* passSystem = RPI::PassSystemInterface::Get();; - const AZStd::vector& passes = passSystem->GetPassesForTemplateName(Name("ProjectedShadowmapsTemplate")); - - AZStd::vector validPipelineIds; m_projectedShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - ProjectedShadowmapsPass* shadowPass = static_cast(pass); - for (const RPI::RenderPipelinePtr& pipeline : renderPipelines) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("ProjectedShadowmapsTemplate"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - if (pipeline.get() == shadowPass->GetRenderPipeline()) - { - m_projectedShadowmapsPasses.emplace_back(shadowPass); - validPipelineIds.push_back(shadowPass->GetRenderPipeline()->GetId()); - } - } - } - return validPipelineIds; + ProjectedShadowmapsPass* shadowPass = static_cast(pass); + m_projectedShadowmapsPasses.emplace_back(shadowPass); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } - void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass(const AZStd::vector& validPipelineIds) + void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass() { const Name LightTypeName = Name("projected"); - - const auto* passSystem = RPI::PassSystemInterface::Get(); - const AZStd::vector passes = passSystem->GetPassesForTemplateName(Name("EsmShadowmapsTemplate")); - + m_esmShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - EsmShadowmapsPass* esmPass = static_cast(pass); - if (esmPass->GetRenderPipeline() && - AZStd::find(validPipelineIds.begin(), validPipelineIds.end(), esmPass->GetRenderPipeline()->GetId()) != validPipelineIds.end() && - esmPass->GetLightTypeName() == LightTypeName) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("EsmShadowmapsTemplate"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this, LightTypeName](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - m_esmShadowmapsPasses.emplace_back(esmPass); - } - } + EsmShadowmapsPass* esmPass = static_cast(pass); + if (esmPass->GetLightTypeName() == LightTypeName) + { + m_esmShadowmapsPasses.emplace_back(esmPass); + } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void ProjectedShadowFeatureProcessor::UpdateFilterParameters() diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h index fafcb25a08..8939f1845d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h @@ -97,8 +97,8 @@ namespace AZ::Render // Functions for caching the ProjectedShadowmapsPass and EsmShadowmapsPass. void CachePasses(); - AZStd::vector CacheProjectedShadowmapsPass(); - void CacheEsmShadowmapsPass(const AZStd::vector& validPipelineIds); + void CacheProjectedShadowmapsPass(); + void CacheEsmShadowmapsPass(); //! Functions to update the parameter of Gaussian filter used in ESM. void UpdateFilterParameters(); diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp index e0209702dc..4c379c4239 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -241,12 +242,12 @@ namespace AZ void SkinnedMeshFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) { - InitSkinningAndMorphPass(pipeline->GetRootPass()); + InitSkinningAndMorphPass(pipeline.get()); } void SkinnedMeshFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) { - InitSkinningAndMorphPass(renderPipeline->GetRootPass()); + InitSkinningAndMorphPass(renderPipeline); } void SkinnedMeshFeatureProcessor::OnBeginPrepareRender() @@ -289,9 +290,10 @@ namespace AZ return false; } - void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass(const RPI::Ptr pipelineRootPass) + void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass(RPI::RenderPipeline* renderPipeline) { - RPI::Ptr skinningPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "SkinningPass" }); + RPI::PassFilter skinPassFilter = RPI::PassFilter::CreateWithPassName(AZ::Name{ "SkinningPass" }, renderPipeline); + RPI::Ptr skinningPass = RPI::PassSystemInterface::Get()->FindFirstPass(skinPassFilter); if (skinningPass) { SkinnedMeshComputePass* skinnedMeshComputePass = azdynamic_cast(skinningPass.get()); @@ -310,7 +312,8 @@ namespace AZ } } - RPI::Ptr morphTargetPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "MorphTargetPass" }); + RPI::PassFilter morphPassFilter = RPI::PassFilter::CreateWithPassName(AZ::Name{ "MorphTargetPass" }, renderPipeline); + RPI::Ptr morphTargetPass = RPI::PassSystemInterface::Get()->FindFirstPass(morphPassFilter); if (morphTargetPass) { MorphTargetComputePass* morphTargetComputePass = azdynamic_cast(morphTargetPass.get()); diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h index 2e93acf2cd..5b7ab943e1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h @@ -66,7 +66,7 @@ namespace AZ private: AZ_DISABLE_COPY_MOVE(SkinnedMeshFeatureProcessor); - void InitSkinningAndMorphPass(const RPI::Ptr pipelineRootPass); + void InitSkinningAndMorphPass(RPI::RenderPipeline* renderPipeline); SkinnedMeshRenderProxyInterfaceHandle AcquireRenderProxyInterface(const SkinnedMeshRenderProxyDesc& desc) override; bool ReleaseRenderProxyInterface(SkinnedMeshRenderProxyInterfaceHandle& handle) override; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h index 36994ec03b..6523f0a6d8 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h @@ -68,9 +68,6 @@ namespace AZ template Ptr FindChildPass() const; - //! Searches the tree for the first pass that has same pass name (Depth-first search). Return nullptr if none found. - Ptr FindPassByNameRecursive(const Name& passName) const; - //! Gets the list of children. Useful for validating hierarchies AZStd::array_view> GetChildren() const; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h index 34eaae4495..e7b9825ecb 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h @@ -139,6 +139,10 @@ namespace AZ //! Returns the number of output attachment bindings uint32_t GetOutputCount() const; + //! Returns the pass template which was used for create this pass. + //! It may return nullptr if the pass wasn't create from a template + const PassTemplate* GetPassTemplate() const; + //! Enable/disable this pass //! If the pass is disabled, it (and any children if it's a ParentPass) won't be rendered. void SetEnabled(bool enabled); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h index c31f353adb..c42991725e 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h @@ -16,95 +16,85 @@ namespace AZ { namespace RPI { - // A base class for a filter which can be used to filter passes + class Scene; + class RenderPipeline; + class PassFilter { public: - //! Whether the input pass matches with the filter - virtual bool Matches(const Pass* pass) const = 0; + static PassFilter CreateWithPassName(Name passName, const Scene* scene); + static PassFilter CreateWithPassName(Name passName, const RenderPipeline* renderPipeline); - //! Return the pass' name if a pass name is used for the filter. - //! Return nullptr if the filter doesn't have pass name used for matching - virtual const Name* GetPassName() const = 0; + //! Create a PassFilter with pass hierarchy information + //! Filter for passes which have a matching name and also with ordered parents. + //! For example, if the filter is initialized with + //! pass name: "ShadowPass1" + //! pass parents names: "MainPipeline", "Shadow" + //! Passes with these names match the filter: + //! "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1" + //! or "Root.MainPipeline.Shadow.ShadowPass1" + //! or "MainPipeline.Shadow.Group1.ShadowPass1" + //! + //! Passes with these names wont match: + //! "MainPipeline.ShadowPass1" + //! or "Shadow.MainPipeline.ShadowPass1" + static PassFilter CreateWithPassHierarchy(const AZStd::vector& passHierarchy); + static PassFilter CreateWithPassHierarchy(const AZStd::vector& passHierarchy); + static PassFilter CreateWithTemplateName(Name templateName, const Scene* scene); + static PassFilter CreateWithTemplateName(Name templateName, const RenderPipeline* renderPipeline); + template + static PassFilter CreateWithPassClass(); - //! Return this filter's info as a string - virtual AZStd::string ToString() const = 0; - }; + enum FilterOptions : uint32_t + { + Empty = 0, + PassName = AZ_BIT(0), + PassTemplateName = AZ_BIT(1), + PassClass = AZ_BIT(2), + PassHierarchy = AZ_BIT(3), + OwnerScene = AZ_BIT(4), + OwnerRenderPipeline = AZ_BIT(5) + }; - //! Filter for passes which have a matching name and also with ordered parents. - //! For example, if the filter is initialized with - //! pass name: "ShadowPass1" - //! pass parents names: "MainPipeline", "Shadow" - //! Passes with these names match the filter: - //! "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1" - //! or "Root.MainPipeline.Shadow.ShadowPass1" - //! or "MainPipeline.Shadow.Group1.ShadowPass1" - //! - //! Passes with these names wont match: - //! "MainPipeline.ShadowPass1" - //! or "Shadow.MainPipeline.ShadowPass1" - class PassHierarchyFilter - : public PassFilter - { - public: - AZ_RTTI(PassHierarchyFilter, "{478F169F-BA97-4321-AC34-EDE823997159}", PassFilter); - AZ_CLASS_ALLOCATOR(PassHierarchyFilter, SystemAllocator, 0); + void SetOwenrScene(const Scene* scene); + void SetOwenrRenderPipeline(const RenderPipeline* renderPipeline); + void SetPassName(Name passName); + void SetTemplateName(Name passTemplateName); + void SetPassClass(TypeId passClassTypeId); - //! Construct filter with only pass name. - PassHierarchyFilter(const Name& passName); + const Name& GetPassName() const; + const Name& GetPassTemplateName() const; - virtual ~PassHierarchyFilter() = default; + uint32_t GetEnabledFilterOptions() const; - //! Construct filter with pass name and its parents' names in the order of the hierarchy - //! This means k-th element is always an ancestor of the (k-1)-th element. - //! And the last element is the pass name. - PassHierarchyFilter(const AZStd::vector& passHierarchy); - PassHierarchyFilter(const AZStd::vector& passHierarchy); + //! Return true if the input pass matches the filter + bool Matches(const Pass* pass) const; - // PassFilter overrides... - bool Matches(const Pass* pass) const override; - const Name* GetPassName() const override; - AZStd::string ToString() const override; + //! Return true if the input pass matches the filter with selected filter options + //! The input filter options should be a subset of options returned by GetEnabledFilterOptions() + //! This function is used to avoid extra checks for passes which was already filtered. + //! Check PassLibrary::ForEachPass() function's implementation for more details + bool Matches(const Pass* pass, uint32_t options) const; private: - PassHierarchyFilter() = delete; + void UpdateFilterOptions(); - AZStd::vector m_parentNames; Name m_passName; + Name m_templateName; + TypeId m_passClassTypeId = TypeId::CreateNull(); + AZStd::vector m_parentNames; + const RenderPipeline* m_ownerRenderPipeline = nullptr; + const Scene* m_ownerScene = nullptr; + uint32_t m_filterOptions = 0; }; - //! Filter for passes based on their class. - template - class PassClassFilter - : public PassFilter - { - public: - AZ_RTTI(PassClassFilter, "{AF6E3AD5-433A-462A-997A-F36D8A551D02}", PassFilter); - AZ_CLASS_ALLOCATOR(PassHierarchyFilter, SystemAllocator, 0); - PassClassFilter() = default; - - // PassFilter overrides... - bool Matches(const Pass* pass) const override; - const Name* GetPassName() const override; - AZStd::string ToString() const override; - }; - - template - bool PassClassFilter::Matches(const Pass* pass) const - { - return pass->RTTI_IsTypeOf(PassClass::RTTI_Type()); - } - - template - const Name* PassClassFilter::GetPassName() const - { - return nullptr; - } - - template - AZStd::string PassClassFilter::ToString() const - { - return AZStd::string::format("PassClassFilter<%s>", PassClass::RTTI_TypeName()); + template + PassFilter PassFilter::CreateWithPassClass() + { + PassFilter filter; + filter.m_passClassTypeId = PassClass::RTTI_Type(); + filter.UpdateFilterOptions(); + return filter; } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h index 0a4b1c4399..66c3c205ab 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h @@ -84,8 +84,8 @@ namespace AZ bool LoadPassTemplateMappings(const AZStd::string& templateMappingPath); bool LoadPassTemplateMappings(Data::Asset mappingAsset); - //! Returns a list of passes found in the pass name mapping using the provided pass filter - AZStd::vector FindPasses(const PassFilter& passFilter) const; + //! Visit each pass which matches the filter + void ForEachPass(const PassFilter& passFilter, AZStd::function passFunction); private: diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h index 30fa27e64b..8390b0f7e2 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h @@ -92,13 +92,13 @@ namespace AZ // PassSystemInterface library related functions... bool HasPassesForTemplateName(const Name& templateName) const override; - const AZStd::vector& GetPassesForTemplateName(const Name& templateName) const override; bool AddPassTemplate(const Name& name, const AZStd::shared_ptr& passTemplate) override; const AZStd::shared_ptr GetPassTemplate(const Name& name) const override; void RemovePassFromLibrary(Pass* pass) override; void RegisterPass(Pass* pass) override; void UnregisterPass(Pass* pass) override; - AZStd::vector FindPasses(const PassFilter& passFilter) const override; + void ForEachPass(const PassFilter& filter, AZStd::function passFunction) override; + Pass* FindFirstPass(const PassFilter& filter) override; private: // Returns the root of the pass tree hierarchy diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h index 0e9386f3bb..7f944df88f 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h @@ -75,6 +75,13 @@ namespace AZ u32 m_maxDrawItemsRenderedInAPass = 0; }; + + enum PassFilterExecutionFlow : uint8_t + { + StopVisitingPasses, + ContinueVisitingPasses, + }; + class PassSystemInterface { friend class Pass; @@ -186,9 +193,6 @@ namespace AZ //! Returns true if the pass factory contains passes created with the given template name virtual bool HasPassesForTemplateName(const Name& templateName) const = 0; - //! Get the passes created with the given template name. - virtual const AZStd::vector& GetPassesForTemplateName(const Name& templateName) const = 0; - //! Adds a PassTemplate to the library virtual bool AddPassTemplate(const Name& name, const AZStd::shared_ptr& passTemplate) = 0; @@ -197,9 +201,16 @@ namespace AZ //! Removes all references to the given pass from the pass library virtual void RemovePassFromLibrary(Pass* pass) = 0; + + //! Visit the matching passes from registered passes with specified filter + //! The return value of the passFunction decides if the search continues or not + //! Note: this function will find all the passes which match the pass filter even they are for render pipelines which are not added to a scene + //! This function is fast if a pass name or a pass template name is specified. + virtual void ForEachPass(const PassFilter& filter, AZStd::function passFunction) = 0; - //! Find matching passes from registered passes with specified filter - virtual AZStd::vector FindPasses(const PassFilter& passFilter) const = 0; + //! Find the first matching pass from registered passes with specified filter + //! Note: this function SHOULD ONLY be used when you are certain you only need to handle the first pass found + virtual Pass* FindFirstPass(const PassFilter& filter) = 0; private: // These functions are only meant to be used by the Pass class diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp index 36c28877ea..dccf5dbc2e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp @@ -149,29 +149,6 @@ namespace AZ return index.IsValid() ? m_children[index.GetIndex()] : Ptr(nullptr); } - Ptr ParentPass::FindPassByNameRecursive(const Name& passName) const - { - for (const Ptr& child : m_children) - { - if (child->GetName() == passName) - { - return child.get(); - } - - ParentPass* asParent = child->AsParent(); - if (asParent) - { - auto pass = asParent->FindPassByNameRecursive(passName); - if (pass) - { - return pass; - } - } - } - - return nullptr; - } - const Pass* ParentPass::FindPass(RHI::DrawListTag drawListTag) const { if (HasDrawListTag() && GetDrawListTag() == drawListTag) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp index a8d94e9a91..3c1de28d6a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -238,6 +238,11 @@ namespace AZ return m_attachmentBindings[bindingIndex]; } + const PassTemplate* Pass::GetPassTemplate() const + { + return m_template.get(); + } + void Pass::AddAttachmentBinding(PassAttachmentBinding attachmentBinding) { // Add the index of the binding to the input, output or input/output list based on the slot type diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp index 7bd1abc0a7..d9e458c615 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp @@ -8,101 +8,264 @@ #include #include +#include namespace AZ { namespace RPI { - PassHierarchyFilter::PassHierarchyFilter(const Name& passName) + PassFilter PassFilter::CreateWithPassName(Name passName, const Scene* scene) + { + PassFilter filter; + filter.m_passName = passName; + filter.m_ownerScene = scene; + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithPassName(Name passName, const RenderPipeline* renderPipeline) + { + PassFilter filter; + filter.m_passName = passName; + filter.m_ownerRenderPipeline = renderPipeline; + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithTemplateName(Name templateName, const Scene* scene) + { + PassFilter filter; + filter.m_templateName = templateName; + filter.m_ownerScene = scene; + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithTemplateName(Name templateName, const RenderPipeline* renderPipeline) + { + PassFilter filter; + filter.m_templateName = templateName; + filter.m_ownerRenderPipeline = renderPipeline; + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithPassHierarchy(const AZStd::vector& passHierarchy) + { + PassFilter filter; + if (passHierarchy.size() == 0) + { + AZ_Assert(false, "passHierarchy should have at least one element"); + return filter; + } + + filter.m_passName = passHierarchy.back(); + + filter.m_parentNames.resize(passHierarchy.size() - 1); + for (uint32_t index = 0; index < filter.m_parentNames.size(); index++) + { + filter.m_parentNames[index] = passHierarchy[index]; + } + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithPassHierarchy(const AZStd::vector& passHierarchy) + { + PassFilter filter; + if (passHierarchy.size() == 0) + { + AZ_Assert(false, "passHierarchy should have at least one element"); + return filter; + } + + filter.m_passName = Name(passHierarchy.back()); + + filter.m_parentNames.resize(passHierarchy.size() - 1); + for (uint32_t index = 0; index < filter.m_parentNames.size(); index++) + { + filter.m_parentNames[index] = Name(passHierarchy[index]); + } + filter.UpdateFilterOptions(); + return filter; + } + + void PassFilter::SetOwenrScene(const Scene* scene) + { + m_ownerScene = scene; + UpdateFilterOptions(); + } + + void PassFilter::SetOwenrRenderPipeline(const RenderPipeline* renderPipeline) + { + m_ownerRenderPipeline = renderPipeline; + UpdateFilterOptions(); + } + + void PassFilter::SetPassName(Name passName) { m_passName = passName; + UpdateFilterOptions(); } - PassHierarchyFilter::PassHierarchyFilter(const AZStd::vector& passHierarchy) + void PassFilter::SetTemplateName(Name passTemplateName) { - if (passHierarchy.size() == 0) - { - AZ_Assert(false, "passHierarchy should have at least one element"); - return; - } - - m_passName = Name(passHierarchy.back()); - - m_parentNames.resize(passHierarchy.size() - 1); - for (uint32_t index = 0; index < m_parentNames.size(); index++) - { - m_parentNames[index] = Name(passHierarchy[index]); - } + m_templateName = passTemplateName; + UpdateFilterOptions(); } - PassHierarchyFilter::PassHierarchyFilter(const AZStd::vector& passHierarchy) + void PassFilter::SetPassClass(TypeId passClassTypeId) { - if (passHierarchy.size() == 0) - { - AZ_Assert(false, "passHierarchy should have at least one element"); - return; - } - - m_passName = passHierarchy.back(); - - m_parentNames.resize(passHierarchy.size() - 1); - for (uint32_t index = 0; index < m_parentNames.size(); index++) - { - m_parentNames[index] = passHierarchy[index]; - } + m_passClassTypeId = passClassTypeId; + UpdateFilterOptions(); } - bool PassHierarchyFilter::Matches(const Pass* pass) const + const Name& PassFilter::GetPassName() const { - if (pass->GetName() != m_passName) + return m_passName; + } + + const Name& PassFilter::GetPassTemplateName() const + { + return m_templateName; + } + + uint32_t PassFilter::GetEnabledFilterOptions() const + { + return m_filterOptions; + } + + bool PassFilter::Matches(const Pass* pass) const + { + return Matches(pass, m_filterOptions); + } + + bool PassFilter::Matches(const Pass* pass, uint32_t options) const + { + AZ_Assert( (options&m_filterOptions) == options, "options should be a subset of m_filterOptions"); + + // return false if the pass doesn't have a pass template or the template's name is not matching + if (options & FilterOptions::PassTemplateName && (!pass->GetPassTemplate() || pass->GetPassTemplate()->m_name != m_templateName)) { return false; } - ParentPass* parent = pass->GetParent(); - - // search from the back of the array with the most close parent - for (int32_t index = static_cast(m_parentNames.size() - 1); index >= 0; index--) + if ((options & FilterOptions::PassName) && pass->GetName() != m_passName) { - const Name& parentName = m_parentNames[index]; - while (parent) - { - if (parent->GetName() == parentName) - { - break; - } - parent = parent->GetParent(); - } + return false; + } - // if parent is nullptr the it didn't find a parent has matching current parentName - if (!parent) + if ((options & FilterOptions::PassClass) && pass->RTTI_GetType() != m_passClassTypeId) + { + return false; + } + + if ((options & FilterOptions::OwnerRenderPipeline) && m_ownerRenderPipeline != pass->GetRenderPipeline()) + { + return false; + } + + // If the owner render pipeline was checked, the owner scene check can be skipped + if (options & FilterOptions::OwnerScene) + { + if (pass->GetRenderPipeline()) { + // return false if the owner scene doesn't match + if (m_ownerScene != pass->GetRenderPipeline()->GetScene()) + { + return false; + } + } + else + { + // return false if the pass doesn't have an owner scene return false; } + } - // move to next parent - parent = parent->GetParent(); + if ((options & FilterOptions::PassHierarchy)) + { + // Filter for passes which have a matching name and also with ordered parents. + // For example, if the filter is initialized with + // pass name: "ShadowPass1" + // pass parents names: "MainPipeline", "Shadow" + // Passes with these names match the filter: + // "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1" + // or "Root.MainPipeline.Shadow.ShadowPass1" + // or "MainPipeline.Shadow.Group1.ShadowPass1" + // + // Passes with these names wont match: + // "MainPipeline.ShadowPass1" + // or "Shadow.MainPipeline.ShadowPass1" + + ParentPass* parent = pass->GetParent(); + + // search from the back of the array with the most close parent + for (int32_t index = static_cast(m_parentNames.size() - 1); index >= 0; index--) + { + const Name& parentName = m_parentNames[index]; + while (parent) + { + if (parent->GetName() == parentName) + { + break; + } + parent = parent->GetParent(); + } + + // if parent is nullptr the it didn't find a parent has matching current parentName + if (!parent) + { + return false; + } + + // move to next parent + parent = parent->GetParent(); + } } return true; } - const Name* PassHierarchyFilter::GetPassName() const + void PassFilter::UpdateFilterOptions() { - return &m_passName; - } - - AZStd::string PassHierarchyFilter::ToString() const - { - AZStd::string result = "PassHierarchyFilter"; - for (uint32_t index = 0; index < m_parentNames.size(); index++) + m_filterOptions = FilterOptions::Empty; + if (!m_passName.IsEmpty()) { - result += AZStd::string::format(" [%s]", m_parentNames[index].GetCStr()); + m_filterOptions |= FilterOptions::PassName; + } + if (!m_templateName.IsEmpty()) + { + m_filterOptions |= FilterOptions::PassTemplateName; + } + if (m_parentNames.size() > 0) + { + m_filterOptions |= FilterOptions::PassHierarchy; + } + if (m_ownerRenderPipeline) + { + m_filterOptions |= FilterOptions::OwnerRenderPipeline; + } + if (m_ownerScene) + { + // If the OwnerRenderPipeline exists, we shouldn't need to filter owner scene + // Validate the owner render pipeline belongs to the owner scene + if (m_filterOptions & FilterOptions::OwnerRenderPipeline) + { + if (m_ownerRenderPipeline->GetScene() != m_ownerScene) + { + AZ_Warning("RPI", false, "The owner scene filter doesn't match owner render pipeline. It will be skipped."); + } + } + else + { + m_filterOptions |= FilterOptions::OwnerScene; + } + } + if (!m_passClassTypeId.IsNull()) + { + m_filterOptions |= FilterOptions::PassClass; } - - result += AZStd::string::format(" [%s]", m_passName.GetCStr()); - return result; } - } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp index c43edafd6b..6a6f5f3ff9 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp @@ -85,47 +85,80 @@ namespace AZ return (GetPassesForTemplate(templateName).size() > 0); } - AZStd::vector PassLibrary::FindPasses(const PassFilter& passFilter) const + void PassLibrary::ForEachPass(const PassFilter& passFilter, AZStd::function passFunction) { - const Name* passName = passFilter.GetPassName(); + uint32_t filterOptions = passFilter.GetEnabledFilterOptions(); - AZStd::vector result; - - if (passName) + // A lambda function which visits each pass in a pass list, if the pass matches the pass filter, then call the pass function + auto visitList = [passFilter, passFunction](const AZStd::vector& passList, uint32_t options) -> PassFilterExecutionFlow { - // If the pass' name is known, find passes with matching names first - const auto constItr = m_passNameMapping.find(*passName); - if (constItr == m_passNameMapping.end()) + if (passList.size() == 0) { - return result; + return PassFilterExecutionFlow::ContinueVisitingPasses; } - - const AZStd::vector& passes = constItr->second; - - for (Pass* pass : passes) + // if there is not other filter options enabled, skip the filter and call pass functions directly + if (options == PassFilter::FilterOptions::Empty) { - if (passFilter.Matches(pass)) + for (Pass* pass : passList) { - result.push_back(pass); - } - } - } - else - { - // If the filter doesn't know matching pass' name, need to go through all registered passes - for (auto& namePasses : m_passNameMapping) - { - for (Pass* pass : namePasses.second) - { - if (passFilter.Matches(pass)) + // If user want to skip processing, return directly. + if (passFunction(pass) == PassFilterExecutionFlow::StopVisitingPasses) { - result.push_back(pass); + return PassFilterExecutionFlow::StopVisitingPasses; + } + } + return PassFilterExecutionFlow::ContinueVisitingPasses; + } + + // Check with the pass filter and call pass functions + for (Pass* pass : passList) + { + if (passFilter.Matches(pass, options)) + { + if (passFunction(pass) == PassFilterExecutionFlow::StopVisitingPasses) + { + return PassFilterExecutionFlow::StopVisitingPasses; } } } + return PassFilterExecutionFlow::ContinueVisitingPasses; + }; + + // Check pass template name first + if (filterOptions & PassFilter::FilterOptions::PassTemplateName) + { + auto entry = GetEntry(passFilter.GetPassTemplateName()); + if (!entry) + { + return; + } + + filterOptions &= ~(PassFilter::FilterOptions::PassTemplateName); + visitList(entry->m_passes, filterOptions); + return; + } + else if (filterOptions & PassFilter::FilterOptions::PassName) + { + const auto constItr = m_passNameMapping.find(passFilter.GetPassName()); + if (constItr == m_passNameMapping.end()) + { + return; + } + + filterOptions &= ~(PassFilter::FilterOptions::PassName); + visitList(constItr->second, filterOptions); + return; } - return result; + // check againest every passes. This might be slow + AZ_PROFILE_SCOPE(RPI, "PassLibrary::ForEachPass"); + for (auto& namePasses : m_passNameMapping) + { + if (visitList(namePasses.second, filterOptions) == PassFilterExecutionFlow::StopVisitingPasses) + { + return; + } + } } // Add Functions... @@ -419,3 +452,4 @@ namespace AZ } // namespace RPI } // namespace AZ + diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp index f4f51f97b7..7f2948c13a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp @@ -456,11 +456,6 @@ namespace AZ return m_passLibrary.HasPassesForTemplate(templateName); } - const AZStd::vector& PassSystem::GetPassesForTemplateName(const Name& templateName) const - { - return m_passLibrary.GetPassesForTemplate(templateName); - } - bool PassSystem::AddPassTemplate(const Name& name, const AZStd::shared_ptr& passTemplate) { return m_passLibrary.AddPassTemplate(name, passTemplate); @@ -487,10 +482,21 @@ namespace AZ RemovePassFromLibrary(pass); --m_passCounter; } - - AZStd::vector PassSystem::FindPasses(const PassFilter& passFilter) const + + void PassSystem::ForEachPass(const PassFilter& filter, AZStd::function passFunction) { - return m_passLibrary.FindPasses(passFilter); + return m_passLibrary.ForEachPass(filter, passFunction); + } + + Pass* PassSystem::FindFirstPass(const PassFilter& filter) + { + Pass* foundPass = nullptr; + m_passLibrary.ForEachPass(filter, [&foundPass](RPI::Pass* pass) ->PassFilterExecutionFlow + { + foundPass = pass; + return PassFilterExecutionFlow::StopVisitingPasses; + }); + return foundPass; } SwapChainPass* PassSystem::FindSwapChainPass(AzFramework::NativeWindowHandle windowHandle) const diff --git a/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp b/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp index 420ab1798a..690f212ec7 100644 --- a/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp @@ -19,6 +19,8 @@ #include #include +#include + #include #include @@ -573,7 +575,7 @@ namespace UnitTest EXPECT_TRUE(pass != nullptr); } - TEST_F(PassTests, PassHierarchyFilter) + TEST_F(PassTests, PassFilter_PassHierarchy) { m_data->AddPassTemplatesToLibrary(); @@ -587,62 +589,55 @@ namespace UnitTest parent2->AsParent()->AddChild(parent1); parent1->AsParent()->AddChild(pass); - { - // Filter with only pass name - PassHierarchyFilter filter(Name("pass1")); - EXPECT_TRUE(filter.Matches(pass.get())); - } - { // Filter with pass hierarchy which has only one element - PassHierarchyFilter filter({ Name("pass1") }); + PassFilter filter = PassFilter::CreateWithPassHierarchy({Name("pass1")}); EXPECT_TRUE(filter.Matches(pass.get())); } { - // Filter with empty pass hierarchy. Result one assert + // Filter with empty pass hierarchy, triggers one assert AZ_TEST_START_TRACE_SUPPRESSION; - PassHierarchyFilter filter(AZStd::vector{}); + PassFilter filter = PassFilter::CreateWithPassHierarchy(AZStd::vector{}); AZ_TEST_STOP_TRACE_SUPPRESSION(1); - EXPECT_FALSE(filter.Matches(pass.get())); } { // Filters with partial hierarchy by using string vector AZStd::vector passHierarchy1 = { "parent1", "pass1" }; - PassHierarchyFilter filter1(passHierarchy1); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(passHierarchy1); EXPECT_TRUE(filter1.Matches(pass.get())); AZStd::vector passHierarchy2 = { "parent2", "pass1" }; - PassHierarchyFilter filter2(passHierarchy2); + PassFilter filter2 = PassFilter::CreateWithPassHierarchy(passHierarchy2); EXPECT_TRUE(filter2.Matches(pass.get())); AZStd::vector passHierarchy3 = { "parent3", "parent2", "pass1" }; - PassHierarchyFilter filter3(passHierarchy3); + PassFilter filter3 = PassFilter::CreateWithPassHierarchy(passHierarchy3); EXPECT_TRUE(filter3.Matches(pass.get())); } { // Filters with partial hierarchy by using Name vector AZStd::vector passHierarchy1 = { Name("parent1"), Name("pass1") }; - PassHierarchyFilter filter1(passHierarchy1); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(passHierarchy1); EXPECT_TRUE(filter1.Matches(pass.get())); AZStd::vector passHierarchy2 = { Name("parent2"), Name("pass1")}; - PassHierarchyFilter filter2(passHierarchy2); + PassFilter filter2 = PassFilter::CreateWithPassHierarchy(passHierarchy2); EXPECT_TRUE(filter2.Matches(pass.get())); AZStd::vector passHierarchy3 = { Name("parent3"), Name("parent2"), Name("pass1") }; - PassHierarchyFilter filter3(passHierarchy3); + PassFilter filter3 = PassFilter::CreateWithPassHierarchy(passHierarchy3); EXPECT_TRUE(filter3.Matches(pass.get())); } { // Find non-leaf pass - PassHierarchyFilter filter1(AZStd::vector{"parent3", "parent1"}); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(AZStd::vector{"parent3", "parent1"}); EXPECT_TRUE(filter1.Matches(parent1.get())); - - PassHierarchyFilter filter2(Name("parent1")); + + PassFilter filter2 = PassFilter::CreateWithPassHierarchy({ Name("parent1") }); EXPECT_TRUE(filter2.Matches(parent1.get())); EXPECT_FALSE(filter2.Matches(pass.get())); } @@ -650,11 +645,131 @@ namespace UnitTest { // Failed to find pass // Mis-matching hierarchy - PassHierarchyFilter filter1(AZStd::vector{"Parent1", "Parent3", "pass1"}); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(AZStd::vector{"Parent1", "Parent3", "pass1"}); EXPECT_FALSE(filter1.Matches(pass.get())); // Mis-matching name - PassHierarchyFilter filter2(AZStd::vector{"Parent1", "pass1"}); + PassFilter filter2 = PassFilter::CreateWithPassHierarchy(AZStd::vector{"Parent1", "pass1"}); EXPECT_FALSE(filter2.Matches(parent1.get())); } } + + TEST_F(PassTests, PassFilter_Empty_Success) + { + m_data->AddPassTemplatesToLibrary(); + + // create a pass tree + Ptr pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + Ptr parent2 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent2")); + Ptr parent3 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent3")); + + parent3->AsParent()->AddChild(parent2); + parent2->AsParent()->AddChild(parent1); + parent1->AsParent()->AddChild(pass); + + PassFilter filter; + + // Any pass can match an empty filter + EXPECT_TRUE(filter.Matches(pass.get())); + EXPECT_TRUE(filter.Matches(parent1.get())); + EXPECT_TRUE(filter.Matches(parent2.get())); + EXPECT_TRUE(filter.Matches(parent3.get())); + } + + TEST_F(PassTests, PassFilter_PassClass_Success) + { + m_data->AddPassTemplatesToLibrary(); + + // create a pass tree + Ptr pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr depthPass = m_passSystem->CreatePassFromTemplate(Name("DepthPrePass"), Name("depthPass")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + + parent1->AsParent()->AddChild(pass); + parent1->AsParent()->AddChild(depthPass); + + PassFilter filter1 = PassFilter::CreateWithPassClass(); + + EXPECT_TRUE(filter1.Matches(pass.get())); + EXPECT_FALSE(filter1.Matches(parent1.get())); + + PassFilter filter2 = PassFilter::CreateWithPassClass(); + EXPECT_FALSE(filter2.Matches(pass.get())); + EXPECT_TRUE(filter2.Matches(parent1.get())); + } + + TEST_F(PassTests, PassFilter_PassTemplate_Success) + { + m_data->AddPassTemplatesToLibrary(); + + // create a pass tree + Ptr childPass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + + PassFilter filter1 = PassFilter::CreateWithTemplateName(Name("Pass"), (Scene*) nullptr); + // childPass doesn't have a template + EXPECT_FALSE(filter1.Matches(childPass.get())); + + PassFilter filter2 = PassFilter::CreateWithTemplateName(Name("ParentPass"), (Scene*) nullptr); + EXPECT_TRUE(filter2.Matches(parent1.get())); + } + + TEST_F(PassTests, ForEachPass_PassTemplateFilter_Success) + { + m_data->AddPassTemplatesToLibrary(); + + // create a pass tree + Ptr pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + Ptr parent2 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent2")); + Ptr parent3 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent3")); + + parent3->AsParent()->AddChild(parent2); + parent2->AsParent()->AddChild(parent1); + parent1->AsParent()->AddChild(pass); + + // Create render pipeline + const RPI::PipelineViewTag viewTag{ "viewTag1" }; + RPI::RenderPipelineDescriptor desc; + desc.m_mainViewTagName = viewTag.GetStringView(); + desc.m_name = "TestPipeline"; + RPI::RenderPipelinePtr pipeline = RPI::RenderPipeline::CreateRenderPipeline(desc); + Ptr parent4 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent4")); + pipeline->GetRootPass()->AddChild(parent4); + + Name templateName = Name("ParentPass"); + PassFilter filter1 = PassFilter::CreateWithTemplateName(templateName, (RenderPipeline*)nullptr); + + int count = 0; + m_passSystem->ForEachPass(filter1, [&count, templateName](RPI::Pass* pass) -> PassFilterExecutionFlow + { + EXPECT_TRUE(pass->GetPassTemplate()->m_name == templateName); + count++; + return PassFilterExecutionFlow::ContinueVisitingPasses; + }); + + // three from CreatePassFromTemplate() calls and one from Render Pipeline. + EXPECT_TRUE(count == 4); + + count = 0; + m_passSystem->ForEachPass(filter1, [&count, templateName](RPI::Pass* pass) -> PassFilterExecutionFlow + { + EXPECT_TRUE(pass->GetPassTemplate()->m_name == templateName); + count++; + return PassFilterExecutionFlow::StopVisitingPasses; + }); + EXPECT_TRUE(count == 1); + + PassFilter filter2 = PassFilter::CreateWithTemplateName(templateName, pipeline.get()); + count = 0; + m_passSystem->ForEachPass(filter2, [&count]([[maybe_unused]] RPI::Pass* pass) -> PassFilterExecutionFlow + { + count++; + return PassFilterExecutionFlow::ContinueVisitingPasses; + }); + + // only the ParentPass in the render pipeline was found + EXPECT_TRUE(count == 1); + + } } diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp index a0be18f0e2..161160e16f 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -142,12 +143,13 @@ namespace AZ EnablePasses(true); } - void HairFeatureProcessor::EnablePasses([[maybe_unused]] bool enable) + void HairFeatureProcessor::EnablePasses(bool enable) { - RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName); - if (desiredPass) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, GetParentScene()); + RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); + if (pass) { - desiredPass->SetEnabled(enable); + pass->SetEnabled(enable); } } @@ -309,10 +311,17 @@ namespace AZ m_forceClearRenderData = true; } + bool HairFeatureProcessor::HasHairParentPass() + { + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, GetParentScene()); + RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); + return pass; + } + void HairFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!renderPipeline.get()->GetRootPass()->FindPassByNameRecursive(HairParentPassName)) + if (!HasHairParentPass()) { return; } @@ -323,10 +332,10 @@ namespace AZ m_forceRebuildRenderData = true; } - void HairFeatureProcessor::OnRenderPipelineRemoved(RPI::RenderPipeline* renderPipeline) + void HairFeatureProcessor::OnRenderPipelineRemoved([[maybe_unused]] RPI::RenderPipeline* renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName)) + if (!HasHairParentPass()) { return; } @@ -338,7 +347,7 @@ namespace AZ void HairFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName)) + if (!HasHairParentPass()) { return; } @@ -457,7 +466,8 @@ namespace AZ { m_computePasses[passName] = nullptr; - RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(passName); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(passName, m_renderPipeline); + RPI::Ptr desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); if (desiredPass) { m_computePasses[passName] = static_cast(desiredPass.get()); @@ -478,8 +488,9 @@ namespace AZ bool HairFeatureProcessor::InitPPLLFillPass() { m_hairPPLLRasterPass = nullptr; // reset it to null, just in case it fails to load the assets properly - - RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLRasterPassName); + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairPPLLRasterPassName, m_renderPipeline); + RPI::Ptr desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); if (desiredPass) { m_hairPPLLRasterPass = static_cast(desiredPass.get()); @@ -497,7 +508,8 @@ namespace AZ { m_hairPPLLResolvePass = nullptr; // reset it to null, just in case it fails to load the assets properly - RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLResolvePassName); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairPPLLResolvePassName, m_renderPipeline); + RPI::Ptr desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); if (desiredPass) { m_hairPPLLResolvePass = static_cast(desiredPass.get()); @@ -518,8 +530,8 @@ namespace AZ m_hairShortCutGeometryDepthAlphaPass = nullptr; m_hairShortCutGeometryShadingPass = nullptr; - m_hairShortCutGeometryDepthAlphaPass = static_cast( - m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryDepthAlphaPassName).get()); + RPI::PassFilter depthAlphaPassFilter = RPI::PassFilter::CreateWithPassName(HairShortCutGeometryDepthAlphaPassName, m_renderPipeline); + m_hairShortCutGeometryDepthAlphaPass = static_cast(RPI::PassSystemInterface::Get()->FindFirstPass(depthAlphaPassFilter)); if (m_hairShortCutGeometryDepthAlphaPass) { m_hairShortCutGeometryDepthAlphaPass->SetFeatureProcessor(this); @@ -530,8 +542,8 @@ namespace AZ return false; } - m_hairShortCutGeometryShadingPass = static_cast( - m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryShadingPassName).get()); + RPI::PassFilter shaderingPassFilter = RPI::PassFilter::CreateWithPassName(HairShortCutGeometryShadingPassName, m_renderPipeline); + m_hairShortCutGeometryShadingPass = static_cast(RPI::PassSystemInterface::Get()->FindFirstPass(shaderingPassFilter)); if (m_hairShortCutGeometryShadingPass) { m_hairShortCutGeometryShadingPass->SetFeatureProcessor(this); diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h index 46660a6623..f810967824 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h @@ -165,6 +165,8 @@ namespace AZ void EnablePasses(bool enable); + bool HasHairParentPass(); + //! The following will serve to register the FP in the Thumbnail system AZStd::vector m_hairFeatureProcessorRegistryName; From 0f60d37fec8a8378fe1ca698706d099e1ab2fe2c Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Mon, 25 Oct 2021 13:32:32 -0700 Subject: [PATCH 026/120] Fixed a bug where material version updates didn't support moving a property from one group to another. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../RPI.Edit/Material/MaterialSourceData.cpp | 15 ++-- .../Material/MaterialTypeSourceData.cpp | 28 +++---- .../Material/MaterialSourceDataTests.cpp | 73 +++++++++++++++++++ .../Material/MaterialTypeSourceDataTests.cpp | 13 +++- 4 files changed, 103 insertions(+), 26 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp index 5aed6b2993..1467b017d5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -93,31 +93,28 @@ namespace AZ // Note that the only kind of property update currently supported is rename... + PropertyGroupMap newPropertyGroups; for (auto& groupPair : m_properties) { PropertyMap& propertyMap = groupPair.second; - PropertyMap newPropertyMap; - for (auto& propertyPair : propertyMap) { MaterialPropertyId propertyId{groupPair.first, propertyPair.first}; + if (materialTypeSourceData.ApplyPropertyRenames(propertyId, m_materialTypeVersion)) { - newPropertyMap[propertyId.GetPropertyName().GetStringView()] = propertyPair.second; changesWereApplied = true; } - else - { - newPropertyMap[propertyPair.first] = propertyPair.second; - } + + newPropertyGroups[propertyId.GetGroupName().GetStringView()][propertyId.GetPropertyName().GetStringView()] = propertyPair.second; } - - propertyMap = newPropertyMap; } if (changesWereApplied) { + m_properties = AZStd::move(newPropertyGroups); + AZ_Warning("MaterialSourceData", false, "This material is based on version '%u' of '%s', but the material type is now at version '%u'. " "Automatic updates are available. Consider updating the .material source file.", diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index 74250647c3..08f57c7cd3 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -164,16 +164,14 @@ namespace AZ const MaterialTypeSourceData::PropertyDefinition* MaterialTypeSourceData::FindProperty(AZStd::string_view groupName, AZStd::string_view propertyName, uint32_t materialTypeVersion) const { auto groupIter = m_propertyLayout.m_properties.find(groupName); - if (groupIter == m_propertyLayout.m_properties.end()) + if (groupIter != m_propertyLayout.m_properties.end()) { - return nullptr; - } - - for (const PropertyDefinition& property : groupIter->second) - { - if (property.m_name == propertyName) + for (const PropertyDefinition& property : groupIter->second) { - return &property; + if (property.m_name == propertyName) + { + return &property; + } } } @@ -185,16 +183,14 @@ namespace AZ // Do the search again with the new names groupIter = m_propertyLayout.m_properties.find(propertyId.GetGroupName().GetStringView()); - if (groupIter == m_propertyLayout.m_properties.end()) + if (groupIter != m_propertyLayout.m_properties.end()) { - return nullptr; - } - - for (const PropertyDefinition& property : groupIter->second) - { - if (property.m_name == propertyId.GetPropertyName().GetStringView()) + for (const PropertyDefinition& property : groupIter->second) { - return &property; + if (property.m_name == propertyId.GetPropertyName().GetStringView()) + { + return &property; + } } } diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp index acce52ae8e..fa8eed35de 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialSourceDataTests.cpp @@ -105,6 +105,13 @@ namespace UnitTest {"op": "rename", "from": "general.testColorNameB", "to": "general.testColorNameC"} ] }, + { + "toVersion": 6, + "actions": [ + {"op": "rename", "from": "oldGroup.MyFloat", "to": "general.MyFloat"}, + {"op": "rename", "from": "oldGroup.MyIntOldName", "to": "general.MyInt"} + ] + }, { "toVersion": 10, "actions": [ @@ -751,6 +758,72 @@ namespace UnitTest material.ApplyVersionUpdates(); } + TEST_F(MaterialSourceDataTests, Load_MaterialTypeVersionUpdate_MovePropertiesToAnotherGroup) + { + const AZStd::string inputJson = R"( + { + "materialType": "@exefolder@/Temp/test.materialtype", + "materialTypeVersion": 3, + "properties": { + "oldGroup": { + "MyFloat": 1.2, + "MyIntOldName": 5 + } + } + } + )"; + + MaterialSourceData material; + JsonTestResult loadResult = LoadTestDataFromJson(material, inputJson); + + EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask()); + EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing()); + + // Initially, the loaded material data will match the .material file exactly. This gives us the accurate representation of + // what's actually saved on disk. + + EXPECT_NE(material.m_properties["oldGroup"].find("MyFloat"), material.m_properties["oldGroup"].end()); + EXPECT_NE(material.m_properties["oldGroup"].find("MyIntOldName"), material.m_properties["oldGroup"].end()); + EXPECT_EQ(material.m_properties["general"].find("MyFloat"), material.m_properties["general"].end()); + EXPECT_EQ(material.m_properties["general"].find("MyInt"), material.m_properties["general"].end()); + + float myFloat = material.m_properties["oldGroup"]["MyFloat"].m_value.GetValue(); + EXPECT_EQ(myFloat, 1.2f); + + int32_t myInt = material.m_properties["oldGroup"]["MyIntOldName"].m_value.GetValue(); + EXPECT_EQ(myInt, 5); + + EXPECT_EQ(3, material.m_materialTypeVersion); + + // Then we force the material data to update to the latest material type version specification + ErrorMessageFinder warningFinder; // Note this finds errors and warnings, and we're looking for a warning. + warningFinder.AddExpectedErrorMessage("Automatic updates are available. Consider updating the .material source file"); + warningFinder.AddExpectedErrorMessage("This material is based on version '3'"); + warningFinder.AddExpectedErrorMessage("material type is now at version '10'"); + material.ApplyVersionUpdates(); + warningFinder.CheckExpectedErrorsFound(); + + // Now the material data should match the latest material type. + // Look for the property under the latest name in the material type, not the name used in the .material file. + + EXPECT_EQ(material.m_properties["oldGroup"].find("MyFloat"), material.m_properties["oldGroup"].end()); + EXPECT_EQ(material.m_properties["oldGroup"].find("MyIntOldName"), material.m_properties["oldGroup"].end()); + EXPECT_NE(material.m_properties["general"].find("MyFloat"), material.m_properties["general"].end()); + EXPECT_NE(material.m_properties["general"].find("MyInt"), material.m_properties["general"].end()); + + myFloat = material.m_properties["general"]["MyFloat"].m_value.GetValue(); + EXPECT_EQ(myFloat, 1.2f); + + myInt = material.m_properties["general"]["MyInt"].m_value.GetValue(); + EXPECT_EQ(myInt, 5); + + EXPECT_EQ(10, material.m_materialTypeVersion); + + // Calling ApplyVersionUpdates() again should not report the warning again, since the material has already been updated. + warningFinder.Reset(); + material.ApplyVersionUpdates(); + } + TEST_F(MaterialSourceDataTests, Load_MaterialTypeVersionPartialUpdate) { // This case is similar to Load_MaterialTypeVersionUpdate but we start at a later diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp index b811e630d0..2fe7b1dbe5 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp @@ -1346,7 +1346,8 @@ namespace UnitTest { "toVersion": 7, "actions": [ - { "op": "rename", "from": "general.bazA", "to": "otherGroup.bazB" } + { "op": "rename", "from": "general.bazA", "to": "otherGroup.bazB" }, + { "op": "rename", "from": "onlyOneProperty.bopA", "to": "otherGroup.bopB" } // This tests a group 'onlyOneProperty' that no longer exists in the material type ] } ], @@ -1370,6 +1371,10 @@ namespace UnitTest { "name": "bazB", "type": "Float" + }, + { + "name": "bopB", + "type": "Float" } ] } @@ -1386,13 +1391,16 @@ namespace UnitTest const MaterialTypeSourceData::PropertyDefinition* foo = materialType.FindProperty("general", "fooC"); const MaterialTypeSourceData::PropertyDefinition* bar = materialType.FindProperty("general", "barC"); const MaterialTypeSourceData::PropertyDefinition* baz = materialType.FindProperty("otherGroup", "bazB"); + const MaterialTypeSourceData::PropertyDefinition* bop = materialType.FindProperty("otherGroup", "bopB"); EXPECT_TRUE(foo); EXPECT_TRUE(bar); EXPECT_TRUE(baz); + EXPECT_TRUE(bop); EXPECT_EQ(foo->m_name, "fooC"); EXPECT_EQ(bar->m_name, "barC"); EXPECT_EQ(baz->m_name, "bazB"); + EXPECT_EQ(bop->m_name, "bopB"); // Now try doing the property lookup using old versions of the name and make sure the same property can be found @@ -1401,12 +1409,15 @@ namespace UnitTest EXPECT_EQ(bar, materialType.FindProperty("general", "barA")); EXPECT_EQ(bar, materialType.FindProperty("general", "barB")); EXPECT_EQ(baz, materialType.FindProperty("general", "bazA")); + EXPECT_EQ(bop, materialType.FindProperty("onlyOneProperty", "bopA")); EXPECT_EQ(nullptr, materialType.FindProperty("general", "fooX")); EXPECT_EQ(nullptr, materialType.FindProperty("general", "barX")); EXPECT_EQ(nullptr, materialType.FindProperty("general", "bazX")); EXPECT_EQ(nullptr, materialType.FindProperty("general", "bazB")); EXPECT_EQ(nullptr, materialType.FindProperty("otherGroup", "bazA")); + EXPECT_EQ(nullptr, materialType.FindProperty("onlyOneProperty", "bopB")); + EXPECT_EQ(nullptr, materialType.FindProperty("otherGroup", "bopA")); } TEST_F(MaterialTypeSourceDataTests, FindPropertyUsingOldName_Error_UnsupportedVersionUpdate) From 59da09c68b6a2476d72e839bf70a85a745b5b12c Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Mon, 25 Oct 2021 13:35:00 -0700 Subject: [PATCH 027/120] Now that we have material version auto update support, I remove the old opacity.doubleSided property and added a rename versionUpdate step to rename it to general.doubleSided. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../Materials/Types/EnhancedPBR.materialtype | 16 +++++++++------- .../Materials/Types/StandardPBR.materialtype | 16 +++++++++------- .../StandardPBR_HandleOpacityDoubleSided.lua | 6 ++---- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype index 384ad75260..d36213694b 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype @@ -1,6 +1,14 @@ { "description": "Material Type with properties used to define Enhanced PBR, a metallic-roughness Physically-Based Rendering (PBR) material shading model, with advanced features like subsurface scattering, transmission, and anisotropy.", - "version": 3, + "version": 4, + "versionUpdates": [ + { + "toVersion": 4, + "actions": [ + {"op": "rename", "from": "opacity.doubleSided", "to": "general.doubleSided"} + ] + } + ], "propertyLayout": { "groups": [ { @@ -715,12 +723,6 @@ "name": "m_opacityFactor" } }, - { - "name": "doubleSided", - "displayName": "Double-sided", - "description": "Whether to render back-faces or just front-faces.", - "type": "Bool" - }, { "name": "alphaAffectsSpecular", "displayName": "Alpha affects specular", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype index a64e97516d..e0b1949058 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype @@ -1,6 +1,14 @@ { "description": "Material Type with properties used to define Standard PBR, a metallic-roughness Physically-Based Rendering (PBR) material shading model.", - "version": 3, + "version": 4, + "versionUpdates": [ + { + "toVersion": 4, + "actions": [ + {"op": "rename", "from": "opacity.doubleSided", "to": "general.doubleSided"} + ] + } + ], "propertyLayout": { "groups": [ { @@ -656,12 +664,6 @@ "name": "m_opacityFactor" } }, - { - "name": "doubleSided", - "displayName": "Double-sided", - "description": "Whether to render back-faces or just front-faces.", - "type": "Bool" - }, { "name": "alphaAffectsSpecular", "displayName": "Alpha affects specular", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua index 8b3bd2b91b..9584698532 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua @@ -10,7 +10,7 @@ ---------------------------------------------------------------------------------------------------- function GetMaterialPropertyDependencies() - return {"general.doubleSided", "opacity.doubleSided", "opacity.mode"} + return {"general.doubleSided"} end ForwardPassIndex = 0 @@ -18,11 +18,9 @@ ForwardPassEdsIndex = 1 function Process(context) local doubleSided = context:GetMaterialPropertyValue_bool("general.doubleSided") - local opacityDoubleSided = context:GetMaterialPropertyValue_bool("opacity.doubleSided") - local opacityMode = context:GetMaterialPropertyValue_enum("opacity.mode") local lastShader = context:GetShaderCount() - 1; - if(doubleSided or (opacityDoubleSided and opacityMode ~= 0)) then + if(doubleSided) then for i=0,lastShader do context:GetShader(i):GetRenderStatesOverride():SetCullMode(CullMode_None) end From 3b4b8c354903f6c0fb9b579b13ae5d336c761382 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Mon, 25 Oct 2021 15:12:34 -0700 Subject: [PATCH 028/120] Move the initialization of m_editorEntityUiInterface higher so that it's initialized when the interface is set up. (#4972) Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../UI/Outliner/EntityOutlinerWidget.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp index 3e97f967fc..d6e9cac754 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp @@ -153,6 +153,9 @@ namespace AzToolsFramework { initEntityOutlinerWidgetResources(); + m_editorEntityUiInterface = AZ::Interface::Get(); + AZ_Assert(m_editorEntityUiInterface != nullptr, "EntityOutlinerWidget requires a EditorEntityUiInterface instance on Initialize."); + m_gui = new Ui::EntityOutlinerWidgetUI(); m_gui->setupUi(this); @@ -282,12 +285,6 @@ namespace AzToolsFramework m_listModel->Initialize(); - m_editorEntityUiInterface = AZ::Interface::Get(); - - AZ_Assert( - m_editorEntityUiInterface != nullptr, - "EntityOutlinerWidget requires a EditorEntityUiInterface instance on Initialize."); - EditorPickModeNotificationBus::Handler::BusConnect(GetEntityContextId()); EntityHighlightMessages::Bus::Handler::BusConnect(); EntityOutlinerModelNotificationBus::Handler::BusConnect(); From bec24a85bf4ca837fb7af22182856eae6409b3bd Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Mon, 25 Oct 2021 15:59:30 -0700 Subject: [PATCH 029/120] Fix old references to gem_list Signed-off-by: AMZN-Phil --- scripts/o3de/o3de/repo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/o3de/o3de/repo.py b/scripts/o3de/o3de/repo.py index 9c26658a30..32c2cba428 100644 --- a/scripts/o3de/o3de/repo.py +++ b/scripts/o3de/o3de/repo.py @@ -115,14 +115,14 @@ def get_gem_json_paths_from_cached_repo(repo_uri: str) -> set: file_name = pathlib.Path(cache_filename).resolve() if not file_name.is_file(): logger.error(f'Could not find cached repo json file for {repo_uri}') - return gem_list + return gem_set with file_name.open('r') as f: try: repo_data = json.load(f) except json.JSONDecodeError as e: logger.error(f'{file_name} failed to load: {str(e)}') - return gem_list + return gem_set # Get list of gems, then add all json paths to the list if they exist in the cache repo_gems = [] From 4e6f1981b92ff2ea8801915b528590503dbe1429 Mon Sep 17 00:00:00 2001 From: chiyenteng <82238204+chiyenteng@users.noreply.github.com> Date: Mon, 25 Oct 2021 15:59:31 -0700 Subject: [PATCH 030/120] Hold arrow keys to move selected element in UI editor (#4968) * Hold arrow keys to move selected element in UI editor Signed-off-by: chiyteng * Fix nits Signed-off-by: chiyteng * Hold arrow keys to move selected element in UI editor Signed-off-by: chiyteng * undo enum changes Signed-off-by: chiyteng * remove extra spaces Signed-off-by: chiyteng * fix comments Signed-off-by: chiyteng * refactor key event code Signed-off-by: chiyteng --- .../Code/Editor/ViewportInteraction.cpp | 22 ++- Gems/LyShine/Code/Editor/ViewportWidget.cpp | 159 ++++-------------- Gems/LyShine/Code/Editor/ViewportWidget.h | 7 +- 3 files changed, 55 insertions(+), 133 deletions(-) diff --git a/Gems/LyShine/Code/Editor/ViewportInteraction.cpp b/Gems/LyShine/Code/Editor/ViewportInteraction.cpp index e6bdb4d8d5..a39a7b9cac 100644 --- a/Gems/LyShine/Code/Editor/ViewportInteraction.cpp +++ b/Gems/LyShine/Code/Editor/ViewportInteraction.cpp @@ -739,14 +739,32 @@ void ViewportInteraction::MouseWheelEvent(QWheelEvent* ev) bool ViewportInteraction::KeyPressEvent(QKeyEvent* ev) { - if (ev->key() == Qt::Key_Space) + switch (ev->key()) { + case Qt::Key_Space: if (!ev->isAutoRepeat()) { ActivateSpaceBar(); } - return true; + case Qt::Key_Up: + Nudge(ViewportInteraction::NudgeDirection::Up, + (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow); + return true; + case Qt::Key_Down: + Nudge(ViewportInteraction::NudgeDirection::Down, + (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow); + return true; + case Qt::Key_Left: + Nudge(ViewportInteraction::NudgeDirection::Left, + (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow); + return true; + case Qt::Key_Right: + Nudge(ViewportInteraction::NudgeDirection::Right, + (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow); + return true; + default: + break; } return false; diff --git a/Gems/LyShine/Code/Editor/ViewportWidget.cpp b/Gems/LyShine/Code/Editor/ViewportWidget.cpp index e172d43b60..bf7447585c 100644 --- a/Gems/LyShine/Code/Editor/ViewportWidget.cpp +++ b/Gems/LyShine/Code/Editor/ViewportWidget.cpp @@ -220,6 +220,7 @@ ViewportWidget::ViewportWidget(EditorWindow* parent) InitUiRenderer(); SetupShortcuts(); + installEventFilter(m_editorWindow); // Setup a timer for the maximum refresh rate we want. // Refresh is actually triggered by interaction events and by the IdleUpdate. This avoids the UI @@ -258,6 +259,8 @@ ViewportWidget::~ViewportWidget() LyShinePassDataRequestBus::Handler::BusDisconnect(); AZ::RPI::ViewportContextNotificationBus::Handler::BusDisconnect(); + removeEventFilter(m_editorWindow); + m_uiRenderer.reset(); // Notify LyShine that this is no longer a valid UiRenderer. @@ -688,9 +691,9 @@ void ViewportWidget::wheelEvent(QWheelEvent* ev) Refresh(); } -bool ViewportWidget::event(QEvent* ev) +bool ViewportWidget::eventFilter([[maybe_unused]] QObject* watched, QEvent* event) { - if (ev->type() == QEvent::ShortcutOverride) + if (event->type() == QEvent::ShortcutOverride) { // When a shortcut is matched, Qt's event processing sends out a shortcut override event // to allow other systems to override it. If it's not overridden, then the key events @@ -698,40 +701,48 @@ bool ViewportWidget::event(QEvent* ev) // handler. In our case this causes a problem in preview mode for the Key_Delete event. // So, if we are preview mode avoid treating Key_Delete as a shortcut. - QKeyEvent* keyEvent = static_cast(ev); + QKeyEvent* keyEvent = static_cast(event); int key = keyEvent->key(); // Override the space bar shortcut so that the key gets handled by the viewport's KeyPress/KeyRelease // events when the viewport has the focus. The space bar is set up as a shortcut in order to give the // viewport the focus and activate the space bar when another widget has the focus. Once the shortcut // is pressed and focus is given to the viewport, the viewport takes over handling the space bar via - // the KeyPress/KeyRelease events - if (key == Qt::Key_Space) + // the KeyPress/KeyRelease events. + // Also ignore nudge shortcuts in edit/preview mode so that the KeyPressEvent will be sent. + switch (key) { - ev->accept(); + case Qt::Key_Space: + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Left: + case Qt::Key_Right: + { + event->accept(); return true; } + default: + { + break; + } + } UiEditorMode editorMode = m_editorWindow->GetEditorMode(); if (editorMode == UiEditorMode::Preview) { - switch (key) + if (key == Qt::Key_Delete) { - case Qt::Key_Delete: - // Ignore nudge shortcuts in preview mode so that the KeyPressEvent will be sent - case Qt::Key_Up: - case Qt::Key_Down: - case Qt::Key_Left: - case Qt::Key_Right: - { - ev->accept(); + event->accept(); return true; } - break; - }; } } - + + return false; +} + +bool ViewportWidget::event(QEvent* ev) +{ bool result = RenderViewportWidget::event(ev); return result; } @@ -742,8 +753,7 @@ void ViewportWidget::keyPressEvent(QKeyEvent* event) if (editorMode == UiEditorMode::Edit) { // in Edit mode just send input to ViewportInteraction - bool handled = m_viewportInteraction->KeyPressEvent(event); - if (!handled) + if (!m_viewportInteraction->KeyPressEvent(event)) { RenderViewportWidget::keyPressEvent(event); } @@ -1246,115 +1256,6 @@ void ViewportWidget::SetupShortcuts() { // Actions with shortcuts are created instead of direct shortcuts because the shortcut dispatcher only looks for matching actions - // Create nudge shortcuts that are active across the entire UI Editor window. Any widgets (such as the spin box widget) that - // handle the same keys and want the shortcut to be ignored need to handle that with a shortcut override event. - // In preview mode, the nudge shortcuts are ignored via the shortcut override event. KeyPressEvents are sent instead, - // and passed along to the canvas - - // Nudge up - { - QAction* action = new QAction("Up", this); - action->setShortcut(QKeySequence(Qt::Key_Up)); - QObject::connect(action, - &QAction::triggered, - [this]() - { - m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Up, ViewportInteraction::NudgeSpeed::Slow); - }); - addAction(action); - } - - // Nudge up fast - { - QAction* action = new QAction("Up Fast", this); - action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Up)); - QObject::connect(action, - &QAction::triggered, - [this]() - { - m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Up, ViewportInteraction::NudgeSpeed::Fast); - }); - addAction(action); - } - - // Nudge down - { - QAction* action = new QAction("Down", this); - action->setShortcut(QKeySequence(Qt::Key_Down)); - QObject::connect(action, - &QAction::triggered, - [this]() - { - m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Down, ViewportInteraction::NudgeSpeed::Slow); - }); - addAction(action); - } - - // Nudge down fast - { - QAction* action = new QAction("Down Fast", this); - action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Down)); - QObject::connect(action, - &QAction::triggered, - [this]() - { - m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Down, ViewportInteraction::NudgeSpeed::Fast); - }); - addAction(action); - } - - // Nudge left - { - QAction* action = new QAction("Left", this); - action->setShortcut(QKeySequence(Qt::Key_Left)); - QObject::connect(action, - &QAction::triggered, - [this]() - { - m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Left, ViewportInteraction::NudgeSpeed::Slow); - }); - addAction(action); - } - - // Nudge left fast - { - QAction* action = new QAction("Left Fast", this); - action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Left)); - QObject::connect(action, - &QAction::triggered, - [this]() - { - m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Left, ViewportInteraction::NudgeSpeed::Fast); - }); - addAction(action); - } - - // Nudge right - { - QAction* action = new QAction("Right", this); - action->setShortcut(QKeySequence(Qt::Key_Right)); - QObject::connect(action, - &QAction::triggered, - [this]() - { - m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Right, ViewportInteraction::NudgeSpeed::Slow); - }); - addAction(action); - } - - // Nudge right fast - { - QAction* action = new QAction("Right Fast", this); - action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Right)); - QObject::connect(action, - &QAction::triggered, - [this]() - { - m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Right, ViewportInteraction::NudgeSpeed::Fast); - }); - addAction(action); - } - // Give the viewport focus and activate the space bar { QAction* action = new QAction("Viewport Focus", this); diff --git a/Gems/LyShine/Code/Editor/ViewportWidget.h b/Gems/LyShine/Code/Editor/ViewportWidget.h index 620cb8fb35..722335aa93 100644 --- a/Gems/LyShine/Code/Editor/ViewportWidget.h +++ b/Gems/LyShine/Code/Editor/ViewportWidget.h @@ -122,12 +122,15 @@ protected: void wheelEvent(QWheelEvent* ev) override; //! Prevents shortcuts from interfering with preview mode. + bool eventFilter(QObject* watched, QEvent* event) override; + + //! Handle events from Qt. bool event(QEvent* ev) override; - //! Key press event from Qt + //! Key press event from Qt. void keyPressEvent(QKeyEvent* event) override; - //! Key release event from Qt + //! Key release event from Qt. void keyReleaseEvent(QKeyEvent* event) override; void focusOutEvent(QFocusEvent* ev) override; From 144af200bf561ae0b16da97d4fdbd7fe11b5d2e8 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Mon, 25 Oct 2021 16:02:55 -0700 Subject: [PATCH 031/120] Fixed potential unused variable 'originalVersion' with 'maybe_unused' attribute. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index e9d8a42641..36f4947e3d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -208,7 +208,7 @@ namespace AZ return; } - const uint32_t originalVersion = m_materialTypeVersion; + [[maybe_unused]] const uint32_t originalVersion = m_materialTypeVersion; bool changesWereApplied = false; From 423693d16b13a6589f8cda4ec0127ce83226cefc Mon Sep 17 00:00:00 2001 From: AMZN-Phil Date: Mon, 25 Oct 2021 16:05:19 -0700 Subject: [PATCH 032/120] Re-add call used to initiate gem download Signed-off-by: AMZN-Phil --- .../ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index bc667db4b4..b3d0ab83ed 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -166,6 +166,10 @@ namespace O3DE::ProjectManager { notification += " " + tr("and") + " "; } + if (added && GemModel::GetDownloadStatus(modelIndex) == GemInfo::DownloadStatus::NotDownloaded) + { + m_downloadController->AddGemDownload(GemModel::GetName(modelIndex)); + } } if (numChangedDependencies == 1 ) From 1bc2968330c7f700759d68b4b4a8e59d81007027 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Mon, 25 Oct 2021 17:47:54 -0700 Subject: [PATCH 033/120] Resolve minor hover state bugs on the Entity Outlier (branches detect hover state separately from the rest of the columns) (#4977) Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp index d94ced392c..0609f8113d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp @@ -72,7 +72,7 @@ namespace AzToolsFramework void EntityOutlinerTreeView::leaveEvent([[maybe_unused]] QEvent* event) { - m_mousePosition = QPoint(); + m_mousePosition = QPoint(-1, -1); m_currentHoveredIndex = QModelIndex(); update(); } @@ -200,7 +200,7 @@ namespace AzToolsFramework const bool isEnabled = (this->model()->flags(index) & Qt::ItemIsEnabled); const bool isSelected = selectionModel()->isSelected(index); - const bool isHovered = (index == indexAt(m_mousePosition)) && isEnabled; + const bool isHovered = (index == indexAt(m_mousePosition).siblingAtColumn(0)) && isEnabled; // Paint the branch Selection/Hover Rect PaintBranchSelectionHoverRect(painter, rect, isSelected, isHovered); From fa6b1d1d65979e55d4309e9b6065368414285095 Mon Sep 17 00:00:00 2001 From: sweeneys Date: Mon, 25 Oct 2021 18:50:34 -0700 Subject: [PATCH 034/120] Fix AzTestRunner smoke test on Linux Signed-off-by: sweeneys --- .../smoke/test_CLITool_AzTestRunner_Works.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AzTestRunner_Works.py b/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AzTestRunner_Works.py index b269c9e131..529716aaf3 100644 --- a/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AzTestRunner_Works.py +++ b/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AzTestRunner_Works.py @@ -13,12 +13,20 @@ import os import pytest import subprocess +import ly_test_tools + @pytest.mark.SUITE_smoke class TestCLIToolAzTestRunnerWorks(object): - def test_CLITool_AzTestRunner_Works(self, build_directory): + def test_CLITool_AzTestRunner_ListSelfTests(self, build_directory): file_path = os.path.join(build_directory, "AzTestRunner") help_message = "OKAY Symbol found: AzRunUnitTests" + + if ly_test_tools.WINDOWS: + target_lib = "AzTestRunner.Tests" + else: + target_lib = "libAzTestRunner.Tests" + # Launch AzTestRunner output = subprocess.run( [file_path, "AzTestRunner.Tests", "AzRunUnitTests", "--gtest_list_tests"], capture_output=True, timeout=10 From dd49798596d34adcce8632172dac93cb6e3fadd8 Mon Sep 17 00:00:00 2001 From: sweeneys Date: Mon, 25 Oct 2021 18:54:01 -0700 Subject: [PATCH 035/120] Fix AzTestRunner with correct lib Signed-off-by: sweeneys --- .../PythonTests/smoke/test_CLITool_AzTestRunner_Works.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AzTestRunner_Works.py b/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AzTestRunner_Works.py index 529716aaf3..df755d5d11 100644 --- a/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AzTestRunner_Works.py +++ b/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AzTestRunner_Works.py @@ -27,12 +27,12 @@ class TestCLIToolAzTestRunnerWorks(object): else: target_lib = "libAzTestRunner.Tests" - # Launch AzTestRunner + # Launch AzTestRunner, load self-tests, print test names output = subprocess.run( - [file_path, "AzTestRunner.Tests", "AzRunUnitTests", "--gtest_list_tests"], capture_output=True, timeout=10 + [file_path, target_lib, "AzRunUnitTests", "--gtest_list_tests"], capture_output=True, timeout=10 ) assert ( len(output.stderr) == 0 and output.returncode == 0 ), f"Error occurred while launching {file_path}: {output.stderr}" # Verify help message - assert help_message in str(output.stdout), f"Help Message: {help_message} is not present" + assert help_message in str(output.stdout), f"Help Message: '{help_message}' unexpectedly not present" From 057c8e0d4e77c6ede25359a86324791ac313b548 Mon Sep 17 00:00:00 2001 From: moraaar Date: Tue, 26 Oct 2021 13:58:21 +0100 Subject: [PATCH 036/120] Fixed error: unused variable 'physxMaximumMaterialIndex' (#4989) Signed-off-by: moraaar --- Gems/PhysX/Code/Source/Utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp index b4e1e768c3..3e77ef257a 100644 --- a/Gems/PhysX/Code/Source/Utils.cpp +++ b/Gems/PhysX/Code/Source/Utils.cpp @@ -102,7 +102,7 @@ namespace PhysX const float scaleFactor = (maxHeightBounds <= minHeightBounds) ? 1.0f : AZStd::numeric_limits::max() / halfBounds; const float heightScale{ 1.0f / scaleFactor }; - const uint8_t physxMaximumMaterialIndex = 0x7f; + [[maybe_unused]] const uint8_t physxMaximumMaterialIndex = 0x7f; // Delete the cached heightfield object if it is there, and create a new one and save in the shape configuration heightfieldConfig.SetCachedNativeHeightfield(nullptr); From 75ebf77b590e5a987f36924f584105ee3b06da7c Mon Sep 17 00:00:00 2001 From: nggieber Date: Tue, 26 Oct 2021 07:22:47 -0700 Subject: [PATCH 037/120] Added warning message when adding repositories Signed-off-by: nggieber --- .../ProjectManager/Resources/ProjectManager.qss | 6 ++++++ .../Source/GemRepo/GemRepoAddDialog.cpp | 12 +++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 6694168f2b..e755964a7f 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -691,6 +691,12 @@ QProgressBar::chunk { #gemRepoAddDialogInstructionTitleLabel { font-size:14px; + font-weight:bold; +} + +#gemRepoAddDialogWarningLabel { + font-size:12px; + font-style:italic; } #addGemRepoDialog #formFrame { diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.cpp index 601c62d6e1..05a14b48d3 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoAddDialog.cpp @@ -27,6 +27,7 @@ namespace O3DE::ProjectManager QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setContentsMargins(30, 30, 25, 10); vLayout->setSpacing(0); + vLayout->setAlignment(Qt::AlignTop); setLayout(vLayout); QLabel* instructionTitleLabel = new QLabel(tr("Enter a valid path to add a new user repository")); @@ -41,9 +42,18 @@ namespace O3DE::ProjectManager vLayout->addWidget(instructionContextLabel); m_repoPath = new FormFolderBrowseEditWidget(tr("Repository Path"), "", this); - m_repoPath->setFixedWidth(600); + m_repoPath->setFixedSize(QSize(600, 100)); vLayout->addWidget(m_repoPath); + vLayout->addSpacing(10); + + QLabel* warningLabel = new QLabel(tr("Online repositories may contain files that could potentially harm your computer," + " please ensure you understand the risks before downloading Gems from third-party sources.")); + warningLabel->setObjectName("gemRepoAddDialogWarningLabel"); + warningLabel->setWordWrap(true); + warningLabel->setAlignment(Qt::AlignLeft); + vLayout->addWidget(warningLabel); + vLayout->addSpacing(40); QDialogButtonBox* dialogButtons = new QDialogButtonBox(); From c2105b0631e6dbc1bd8962a210e08da6f5d68258 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 26 Oct 2021 15:30:08 +0100 Subject: [PATCH 038/120] Address PR comments. Signed-off-by: John --- ...ViewportEditorModeTrackerNotificationBus.h | 2 ++ .../Viewport/ViewportEditorModeTests.cpp | 22 ++++++------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h index 966b9f8478..2d25dbbafc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h @@ -43,6 +43,8 @@ namespace AzToolsFramework }; //! Provides a bus to notify when the different editor modes are entered/exit. + //! @note The editor modes are not discrete states but rather each progression of mode retain the active the parent + //! mode that the new mode progressed from. class ViewportEditorModeNotifications : public AZ::EBusTraits { public: diff --git a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp index 05d7a0d37d..85b65f3edf 100644 --- a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportEditorModeTests.cpp @@ -72,14 +72,6 @@ namespace UnitTest } } - bool IsComponentModeActive() - { - bool inComponentMode = false; - AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::BroadcastResult( - inComponentMode, &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::InComponentMode); - return inComponentMode; - } - // Fixture for testing editor mode states class ViewportEditorModesTestsFixture : public ::testing::Test @@ -543,7 +535,7 @@ namespace UnitTest AZStd::vector{}); // Expect to be in component mode - EXPECT_TRUE(IsComponentModeActive()); + EXPECT_TRUE(AzToolsFramework::ComponentModeFramework::InComponentMode()); // Expect the default and component viewport editor modes to be active EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default)); @@ -561,13 +553,13 @@ namespace UnitTest &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode, AZStd::vector{}); - EXPECT_TRUE(IsComponentModeActive()); + EXPECT_TRUE(AzToolsFramework::ComponentModeFramework::InComponentMode()); AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::EndComponentMode); // Expect to not be in component mode - EXPECT_FALSE(IsComponentModeActive()); + EXPECT_FALSE(AzToolsFramework::ComponentModeFramework::InComponentMode()); // Expect only the default viewport editor mode to be active ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Default); @@ -634,7 +626,7 @@ namespace UnitTest ExitingFocusModeAfterEnteringFromInitialStateHasOnlyViewportEditorModeDefaultActive) { // When entering and leaving focus mode - m_focusModeInterface->SetFocusRoot(AZ::EntityId(1)); + m_focusModeInterface->SetFocusRoot(AZ::EntityId{ 1 }); m_focusModeInterface->SetFocusRoot(AZ::EntityId()); // Expect only the default mode to be active @@ -650,7 +642,7 @@ namespace UnitTest AZStd::vector{}); // Expect to be in component mode - EXPECT_TRUE(IsComponentModeActive()); + EXPECT_TRUE(AzToolsFramework::ComponentModeFramework::InComponentMode()); // Expect the default, focus and component viewport editor modes to be active EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default)); @@ -669,13 +661,13 @@ namespace UnitTest &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode, AZStd::vector{}); - EXPECT_TRUE(IsComponentModeActive()); + EXPECT_TRUE(AzToolsFramework::ComponentModeFramework::InComponentMode()); AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::EndComponentMode); // Expect to not be in component mode - EXPECT_FALSE(IsComponentModeActive()); + EXPECT_FALSE(AzToolsFramework::ComponentModeFramework::InComponentMode()); // Expect the default and focus viewport editor modes to be active EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default)); From f3499011ac52f801ab5bf5f27791f56039e0a7d1 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Tue, 26 Oct 2021 08:30:51 -0700 Subject: [PATCH 039/120] [Mac] Fix QtEditorApplication_mac include (#4978) Commit 8e03d6f3065105f53a171345a2cf4a661cec4eb0 missed updating the platform-specific mac QApplication implementation file to include the class declaration from the new header. Signed-off-by: Chris Burel --- Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.mm b/Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.mm index a7f59b7ac8..c664d13c79 100644 --- a/Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.mm +++ b/Code/Editor/Platform/Mac/Editor/Core/QtEditorApplication_mac.mm @@ -9,7 +9,7 @@ #import #include "EditorDefs.h" -#include "QtEditorApplication.h" +#include "QtEditorApplication_mac.h" // AzFramework #include From 3ff469c55e4d8be1b45e343731324b78014252d6 Mon Sep 17 00:00:00 2001 From: nggieber Date: Tue, 26 Oct 2021 08:42:22 -0700 Subject: [PATCH 040/120] Fix issue with project still displaying when last project is removed Signed-off-by: nggieber --- Code/Tools/ProjectManager/Source/ScreensCtrl.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp index 314765def0..fd8fbc970a 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp @@ -133,6 +133,11 @@ namespace O3DE::ProjectManager return true; } + else + { + // If we are already on this screen still notify we are on this screen to refresh it + newScreen->NotifyCurrentScreen(); + } } return false; From 22a287d046c606ccb79ff19c1c15075d8cd71dbc Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Tue, 26 Oct 2021 08:56:37 -0700 Subject: [PATCH 041/120] Fix to set the Taskbar name and Game Launcher window title to the name of the Project (#4986) Signed-off-by: Steve Pham --- .../Common/Xcb/AzFramework/XcbNativeWindow.cpp | 11 +++++++++-- .../Code/Source/BootstrapSystemComponent.cpp | 5 ++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp index 79a6333612..c7663dc1ab 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbNativeWindow.cpp @@ -258,10 +258,17 @@ namespace AzFramework //////////////////////////////////////////////////////////////////////////////////////////////// void XcbNativeWindow::SetWindowTitle(const AZStd::string& title) { + // Set the title of both the window and the task bar by using + // a buffer to hold the title twice, separated by a null-terminator + auto doubleTitleSize = (title.size() + 1) * 2; + AZStd::string doubleTitle(doubleTitleSize, '\0'); + azstrncpy(doubleTitle.data(), doubleTitleSize, title.c_str(), title.size()); + azstrncpy(&doubleTitle.data()[title.size() + 1], title.size(), title.c_str(), title.size()); + xcb_void_cookie_t xcbCheckResult; xcbCheckResult = xcb_change_property( - m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, static_cast(title.size()), - title.c_str()); + m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, static_cast(doubleTitle.size()), + doubleTitle.c_str()); AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set window title."); } diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp index 8f2ff264e5..3f5761270a 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -115,7 +116,9 @@ namespace AZ { // GFX TODO - investigate window creation being part of the GameApplication. - m_nativeWindow = AZStd::make_unique("O3DELauncher", AzFramework::WindowGeometry(0, 0, 1920, 1080)); + auto projectTitle = AZ::Utils::GetProjectName(); + + m_nativeWindow = AZStd::make_unique(projectTitle.c_str(), AzFramework::WindowGeometry(0, 0, 1920, 1080)); AZ_Assert(m_nativeWindow, "Failed to create the game window\n"); m_nativeWindow->Activate(); From 866fd8a420e98ba871a49da8dec59b2a5971e36a Mon Sep 17 00:00:00 2001 From: nggieber Date: Tue, 26 Oct 2021 09:46:54 -0700 Subject: [PATCH 042/120] Fix selected gem filtering Signed-off-by: nggieber --- .../Source/GemCatalog/GemFilterWidget.cpp | 58 +++++++++++-------- .../GemCatalog/GemSortFilterProxyModel.cpp | 23 ++++++-- .../GemCatalog/GemSortFilterProxyModel.h | 3 +- 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp index b425c15dee..4f737d8629 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp @@ -225,21 +225,22 @@ namespace O3DE::ProjectManager QVector elementNames; QVector elementCounts; const int totalGems = m_gemModel->rowCount(); - const int selectedGemTotal = m_gemModel->TotalAddedGems(); + const int selectedGemTotal = m_gemModel->GatherGemsToBeAdded(/*includeDependencies=*/true).size(); + const int unselectedGemTotal = m_gemModel->GatherGemsToBeRemoved(/*includeDependencies=*/true).size(); const int enabledGemTotal = m_gemModel->TotalAddedGems(/*includeDependencies=*/true); - elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Unselected)); - elementCounts.push_back(totalGems - selectedGemTotal); - elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Selected)); elementCounts.push_back(selectedGemTotal); - elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Inactive)); - elementCounts.push_back(totalGems - enabledGemTotal); + elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Unselected)); + elementCounts.push_back(unselectedGemTotal); elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Active)); elementCounts.push_back(enabledGemTotal); + elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Inactive)); + elementCounts.push_back(totalGems - enabledGemTotal); + bool wasCollapsed = false; if (m_statusFilter) { @@ -262,44 +263,51 @@ namespace O3DE::ProjectManager const QList buttons = m_statusFilter->GetButtonGroup()->buttons(); - QAbstractButton* unselectedButton = buttons[0]; - QAbstractButton* selectedButton = buttons[1]; - unselectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Unselected); - selectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Selected); + QAbstractButton* selectedButton = buttons[0]; + QAbstractButton* unselectedButton = buttons[1]; + selectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Selected); + unselectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Unselected); auto updateGemSelection = [=]([[maybe_unused]] bool checked) { - if (unselectedButton->isChecked() && !selectedButton->isChecked()) - { - m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Unselected); - } - else if (!unselectedButton->isChecked() && selectedButton->isChecked()) + if (!unselectedButton->isChecked() && selectedButton->isChecked()) { m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Selected); } + else if (unselectedButton->isChecked() && !selectedButton->isChecked()) + { + m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Unselected); + } else { - m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::NoFilter); + if (unselectedButton->isChecked() && selectedButton->isChecked()) + { + m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Both); + } + else + { + m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::NoFilter); + } } }; connect(unselectedButton, &QAbstractButton::toggled, this, updateGemSelection); connect(selectedButton, &QAbstractButton::toggled, this, updateGemSelection); - QAbstractButton* inactiveButton = buttons[2]; - QAbstractButton* activeButton = buttons[3]; - inactiveButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Inactive); - activeButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Active); + QAbstractButton* activeButton = buttons[2]; + QAbstractButton* inactiveButton = buttons[3]; + activeButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Active); + inactiveButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Inactive); auto updateGemActive = [=]([[maybe_unused]] bool checked) { - if (inactiveButton->isChecked() && !activeButton->isChecked()) - { - m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Inactive); - } - else if (!inactiveButton->isChecked() && activeButton->isChecked()) + if (!inactiveButton->isChecked() && activeButton->isChecked()) { m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Active); } + else if (inactiveButton->isChecked() && !activeButton->isChecked()) + { + m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Inactive); + } else { m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::NoFilter); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp index 199692f200..7ec45ac721 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp @@ -50,11 +50,26 @@ namespace O3DE::ProjectManager } } - // Gem selected - if (m_gemSelectedFilter != GemSelected::NoFilter) + // Gem selected + if (m_gemSelectedFilter == GemSelected::Selected) { - const GemSelected sourceGemStatus = static_cast(GemModel::IsAdded(sourceIndex)); - if (m_gemSelectedFilter != sourceGemStatus) + if (!GemModel::NeedsToBeAdded(sourceIndex, true)) + { + return false; + } + } + // Gem unselected + else if (m_gemSelectedFilter == GemSelected::Unselected) + { + if (!GemModel::NeedsToBeRemoved(sourceIndex, true)) + { + return false; + } + } + // Gem selected or unselected + else if (m_gemSelectedFilter == GemSelected::Both) + { + if (!GemModel::NeedsToBeAdded(sourceIndex, true) && !GemModel::NeedsToBeRemoved(sourceIndex, true)) { return false; } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h index 74b1e915eb..ab739e62f9 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h @@ -29,7 +29,8 @@ namespace O3DE::ProjectManager { NoFilter = -1, Unselected, - Selected + Selected, + Both }; enum class GemActive { From b541d69efc1b1476eaa929a7cb81e09a3cf4185a Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Tue, 26 Oct 2021 12:19:12 -0500 Subject: [PATCH 043/120] DXC Validation Error Difficult to See in AP Window (#4982) * DXC Validation Error Difficult to See in AP Window Renamed ReportErrorMessages() as ReportMessages() All the message will be printed as a single AZ_Error() or AZ_Warning() instead of mingled AZ_Error/AZ_Warning/AZ_TRacePrintf which was making the output hard to read. Signed-off-by: garrieta --- .../Code/Source/Editor/ShaderAssetBuilder.cpp | 4 +-- .../RHI/Code/Include/Atom/RHI.Edit/Utils.h | 11 +++--- Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp | 36 +++++++------------ 3 files changed, 20 insertions(+), 31 deletions(-) diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp index a0205efa72..89e202a4bc 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp @@ -477,8 +477,8 @@ namespace AZ preprocessorOptions.m_predefinedMacros.end(), macroDefinitionsToAdd.begin(), macroDefinitionsToAdd.end()); // Run the preprocessor. PreprocessorData output; - PreprocessFile(prependedAzslFilePath, output, preprocessorOptions, true, true); - RHI::ReportErrorMessages(ShaderAssetBuilderName, output.diagnostics); + const bool preprocessorSuccess = PreprocessFile(prependedAzslFilePath, output, preprocessorOptions, true, true); + RHI::ReportMessages(ShaderAssetBuilderName, output.diagnostics, !preprocessorSuccess); // Dump the preprocessed string as a flat AZSL file with extension .azslin, which will be given to AZSLc to generate the HLSL file. AZStd::string superVariantAzslinStemName = shaderFileName; if (!supervariantInfo.m_name.IsEmpty()) diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h index 0624dce5f8..d648f05b46 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/Utils.h @@ -83,11 +83,12 @@ namespace AZ const AZStd::string& shaderSourcePathForDebug, const char* toolNameForLog); - //! Reports error messages to AZ_Error and/or AZ_Warning, given a text blob that potentially contains many lines of errors and warnings. - //! @param window Debug window name used for AZ Trace functions - //! @param errorMessages String that may contain many lines of errors and warnings - //! @param return true if Errors were detected and reported (Warnings don't count) - bool ReportErrorMessages(AZStd::string_view window, AZStd::string_view errorMessages); + //! Reports messages with AZ_Error or AZ_Warning (See @reportAsErrors). + //! @param window Debug window name used for AZ Trace functions. + //! @param errorMessages Message string. + //! @param reportAsErrors If true, messages are traced with AZ_Error, otherwise AZ_Warning is used. + //! @returns true If the input text blob contains at least one line with the "error" string. + bool ReportMessages(AZStd::string_view window, AZStd::string_view errorMessages, bool reportAsErrors); //! Converts from a RHI::ShaderHardwareStage to an RHI::ShaderStage ShaderStage ToRHIShaderStage(ShaderHardwareStage stageType); diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp index b4c68b3f75..dc203ef731 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp @@ -330,7 +330,7 @@ namespace AZ // Pump one last time to make sure the streams have been flushed pumpOuputStreams(); - const bool reportedErrors = ReportErrorMessages(toolNameForLog, errorMessages); + const bool reportedErrors = ReportMessages(toolNameForLog, errorMessages, exitCode != 0); if (timedOut) { @@ -367,32 +367,20 @@ namespace AZ return true; } - bool ReportErrorMessages([[maybe_unused]] AZStd::string_view window, AZStd::string_view errorMessages) + bool ReportMessages([[maybe_unused]] AZStd::string_view window, AZStd::string_view errorMessages, bool reportAsErrors) { - // There are more efficient ways to do this, but this approach is simple and gets us moving for now. - AZStd::vector lines; - AzFramework::StringFunc::Tokenize(errorMessages.data(), lines, "\n\r"); - - bool foundErrors = false; - - for (auto& line : lines) + if (reportAsErrors) { - if (AZStd::string::npos != AzFramework::StringFunc::Find(line, "error")) - { - AZ_Error(window.data(), false, "%s", line.data()); - foundErrors = true; - } - else if (AZStd::string::npos != AzFramework::StringFunc::Find(line, "warning")) - { - AZ_Warning(window.data(), false, "%s", line.data()); - } - else - { - AZ_TracePrintf(window.data(), "%s", line.data()); - } + AZ_Error(window.data(), false, "%.*s", aznumeric_cast(errorMessages.size()), errorMessages.data()); } - - return foundErrors; + else + { + // Using AZ_Warning instead of AZ_TracePrintf because this function is commonly + // used to report messages from stderr when executing applications. Applications + // when ran successfully, only output to stderr for errors or warnings. + AZ_Warning(window.data(), false, "%.*s", aznumeric_cast(errorMessages.size()), errorMessages.data()); + } + return AZStd::string::npos != AzFramework::StringFunc::Find(errorMessages, "error"); } ShaderStage ToRHIShaderStage(ShaderHardwareStage stageType) From a945fd9f1bc885af48bd7c3172f1e46d24588eb5 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Tue, 26 Oct 2021 12:27:40 -0500 Subject: [PATCH 044/120] Removed ShaderAsset related unncessary warning (#5008) Removed ShaderAsset related unncessary warning that pollutes the logs. Signed-off-by: garrieta --- Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp index 692087d93b..84757d58ab 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp @@ -264,7 +264,6 @@ namespace AZ { // When rebuilding shaders we may be in a state where the ShaderAsset and root ShaderVariantAsset have been rebuilt and reloaded, but some (or all) // shader variants haven't been built yet. Since we want to use the latest version of the shader code, ignore the old variants and fall back to the newer root variant instead. - AZ_Warning("ShaderAsset", false, "ShaderAsset and ShaderVariantAsset are out of sync; defaulting to root shader variant. (This is common while reloading shaders)."); return GetRootVariant(supervariantIndex); } } From 7c25cb6d5a0642b348c92c089dae0bb763b251e8 Mon Sep 17 00:00:00 2001 From: evanchia Date: Tue, 26 Oct 2021 10:37:39 -0700 Subject: [PATCH 045/120] addressing minor details in feedback Signed-off-by: evanchia --- .../editor_test_testing/TestSuite_Main.py | 9 ++-- .../ly_test_tools/o3de/editor_test.py | 46 +++++++++---------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py index 8832a16d99..50b89ab138 100644 --- a/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/editor_test_testing/TestSuite_Main.py @@ -26,16 +26,13 @@ import ly_test_tools.environment.process_utils as process_utils import argparse, sys -if ly_test_tools.WINDOWS: - pytestmark = pytest.mark.SUITE_main -else: - pytestmark = pytest.mark.skipif(not ly_test_tools.WINDOWS, reason="Only runs on Windows") - def get_editor_launcher_platform(): if ly_test_tools.WINDOWS: return "windows_editor" - else: + elif ly_test_tools.LINUX: return "linux_editor" + else: + return None @pytest.mark.parametrize("launcher_platform", [get_editor_launcher_platform()]) @pytest.mark.parametrize("project", ["AutomatedTesting"]) diff --git a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py index 0bc341012c..cf3682e080 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py @@ -3,6 +3,27 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. SPDX-License-Identifier: Apache-2.0 OR MIT + +This file provides editor testing functionality to easily write automated editor tests for O3DE. +For using these utilities, you can subclass your test suite from EditorTestSuite, this allows an easy way of +specifying python test scripts that the editor will run without needing to write any boilerplace code. +It supports out of the box parallelization(running multiple editor instances at once), batching(running multiple tests +in the same editor instance) and crash detection. +Usage example: + class MyTestSuite(EditorTestSuite): + + class MyFirstTest(EditorSingleTest): + from . import script_to_be_run_by_editor as test_module + + class MyTestInParallel_1(EditorParallelTest): + from . import another_script_to_be_run_by_editor as test_module + + class MyTestInParallel_2(EditorParallelTest): + from . import yet_another_script_to_be_run_by_editor as test_module + + +EditorTestSuite does introspection of the defined classes inside of it and automatically prepares the tests, +parallelizing/batching as required """ import pytest @@ -30,27 +51,6 @@ import ly_test_tools.o3de.editor_test_utils as editor_utils from ly_test_tools.o3de.asset_processor import AssetProcessor from ly_test_tools.launchers.exceptions import WaitTimeoutError -# This file provides editor testing functionality to easily write automated editor tests for O3DE. -# For using these utilities, you can subclass your test suite from EditorTestSuite, this allows an easy way of -# specifying python test scripts that the editor will run without needing to write any boilerplace code. -# It supports out of the box parallelization(running multiple editor instances at once), batching(running multiple tests -# in the same editor instance) and crash detection. -# Usage example: -# class MyTestSuite(EditorTestSuite): -# -# class MyFirstTest(EditorSingleTest): -# from . import script_to_be_run_by_editor as test_module -# -# class MyTestInParallel_1(EditorParallelTest): -# from . import another_script_to_be_run_by_editor as test_module -# -# class MyTestInParallel_2(EditorParallelTest): -# from . import yet_another_script_to_be_run_by_editor as test_module -# -# -# EditorTestSuite does introspection of the defined classes inside of it and automatically prepares the tests, -# parallelizing/batching as required - # This file contains no tests, but with this we make sure it won't be picked up by the runner since the file ends with _test __test__ = False @@ -212,7 +212,7 @@ class Result: return r def __str__(self): - stacktrace_str = "-- No stacktrace data found --\n" if not self.stacktrace else self.stacktrace + stacktrace_str = "-- No stacktrace data found --" if not self.stacktrace else self.stacktrace output = ( f"Test CRASHED, return code {hex(self.ret_code)}\n" f"---------------\n" @@ -675,7 +675,7 @@ class EditorTestSuite(): try: elem = json.loads(m.groups()[0]) found_jsons[elem["name"]] = elem - except Exception as e: + except Exception: continue # Avoid to fail if the output data is corrupt # Try to find the element in the log, this is used for cutting the log contents later From e9b5a51d9fec3126660bdd9a8626ab09c8807072 Mon Sep 17 00:00:00 2001 From: sphrose <82213493+sphrose@users.noreply.github.com> Date: Tue, 26 Oct 2021 18:39:39 +0100 Subject: [PATCH 046/120] Fix console warning when adding TerrainWorldRendererComponent (#4964) * Fix console warning when adding TerrainWorldRendererComponent Signed-off-by: sphrose <82213493+sphrose@users.noreply.github.com> * Change default value Signed-off-by: sphrose <82213493+sphrose@users.noreply.github.com> --- .../Code/Source/Components/TerrainWorldRendererComponent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h index 354b2fde34..140b830da1 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.h @@ -46,7 +46,7 @@ namespace Terrain WorldSizeCount, }; - WorldSize m_worldSize; + WorldSize m_worldSize = WorldSize::_1024Meters; }; From e22235ec5b850541d6a186a80b44050ebfd24d89 Mon Sep 17 00:00:00 2001 From: hershey5045 <43485729+hershey5045@users.noreply.github.com> Date: Tue, 26 Oct 2021 10:40:30 -0700 Subject: [PATCH 047/120] Add OpenImageIO as runtime dependency in AtomLyIntegration. (#4987) * Add OpenImageIO as runtime dependency in AtomLyIntegration. Signed-off-by: rbarrand * Place 3rdparty import inside if block. Signed-off-by: rbarrand * Add platform cmake files for other platforms to prevent compile errors. Signed-off-by: rbarrand Co-authored-by: rbarrand --- .../Source/Platform/Windows/platform_windows.cmake | 9 --------- .../CommonFeatures/Code/CMakeLists.txt | 2 ++ .../Source/Platform/Android/platform_android.cmake | 7 +++++++ .../Source/Platform/AppleTV/platform_appletv.cmake | 8 ++++++++ .../Code/Source/Platform/Linux/platform_linux.cmake | 7 +++++++ .../Code/Source/Platform/Mac/platform_mac.cmake | 7 +++++++ .../Source/Platform/Windows/platform_windows.cmake | 13 +++++++++++++ .../Code/Source/Platform/iOS/platform_ios.cmake | 7 +++++++ 8 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Android/platform_android.cmake create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/AppleTV/platform_appletv.cmake create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Linux/platform_linux.cmake create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Mac/platform_mac.cmake create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Windows/platform_windows.cmake create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/iOS/platform_ios.cmake diff --git a/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake b/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake index 7c594fc945..7a325ca97e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake +++ b/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake @@ -5,12 +5,3 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT # # - -if(LY_MONOLITHIC_GAME) # Do not use OpenImageIO in monolithic game - return() -endif() - -set(LY_BUILD_DEPENDENCIES - PRIVATE - 3rdParty::ilmbase -) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt index e68681315e..95331a2f3f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt @@ -86,6 +86,8 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) FILES_CMAKE atomlyintegration_commonfeatures_editor_files.cmake ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + PLATFORM_INCLUDE_FILES + ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake INCLUDE_DIRECTORIES PRIVATE . diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Android/platform_android.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Android/platform_android.cmake new file mode 100644 index 0000000000..7a325ca97e --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Android/platform_android.cmake @@ -0,0 +1,7 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/AppleTV/platform_appletv.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/AppleTV/platform_appletv.cmake new file mode 100644 index 0000000000..5cd1fb5a22 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/AppleTV/platform_appletv.cmake @@ -0,0 +1,8 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Linux/platform_linux.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Linux/platform_linux.cmake new file mode 100644 index 0000000000..7a325ca97e --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Linux/platform_linux.cmake @@ -0,0 +1,7 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Mac/platform_mac.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Mac/platform_mac.cmake new file mode 100644 index 0000000000..7a325ca97e --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Mac/platform_mac.cmake @@ -0,0 +1,7 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Windows/platform_windows.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Windows/platform_windows.cmake new file mode 100644 index 0000000000..3beda63de7 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/Windows/platform_windows.cmake @@ -0,0 +1,13 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +if(NOT LY_MONOLITHIC_GAME) # Do not use OpenImageIO in monolithic game + set(LY_RUNTIME_DEPENDENCIES + 3rdParty::OpenImageIO + ) +endif() diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/iOS/platform_ios.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/iOS/platform_ios.cmake new file mode 100644 index 0000000000..7a325ca97e --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Platform/iOS/platform_ios.cmake @@ -0,0 +1,7 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# From 707730ebbc1cedeea3bcb785251474401a0e76dd Mon Sep 17 00:00:00 2001 From: Adi Bar-Lev <82479970+Adi-Amazon@users.noreply.github.com> Date: Tue, 26 Oct 2021 15:26:54 -0400 Subject: [PATCH 048/120] Hair - bug fix resulted from change in pass fetch using the new pipeline filter (#5013) Signed-off-by: Adi-Amazon Co-authored-by: Adi-Amazon --- .../Code/Rendering/HairFeatureProcessor.cpp | 13 +++++++------ .../Code/Rendering/HairFeatureProcessor.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp index 161160e16f..74ba99c26c 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp @@ -311,17 +311,17 @@ namespace AZ m_forceClearRenderData = true; } - bool HairFeatureProcessor::HasHairParentPass() + bool HairFeatureProcessor::HasHairParentPass(RPI::RenderPipeline* renderPipeline) { - RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, GetParentScene()); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, renderPipeline); RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); - return pass; + return pass ? true : false; } void HairFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!HasHairParentPass()) + if (!HasHairParentPass(renderPipeline.get())) { return; } @@ -335,7 +335,7 @@ namespace AZ void HairFeatureProcessor::OnRenderPipelineRemoved([[maybe_unused]] RPI::RenderPipeline* renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!HasHairParentPass()) + if (!HasHairParentPass(renderPipeline)) { return; } @@ -347,7 +347,7 @@ namespace AZ void HairFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!HasHairParentPass()) + if (!HasHairParentPass(renderPipeline)) { return; } @@ -623,3 +623,4 @@ namespace AZ } // namespace Hair } // namespace Render } // namespace AZ + diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h index f810967824..70e37a7863 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h @@ -165,7 +165,7 @@ namespace AZ void EnablePasses(bool enable); - bool HasHairParentPass(); + bool HasHairParentPass(RPI::RenderPipeline* renderPipeline); //! The following will serve to register the FP in the Thumbnail system AZStd::vector m_hairFeatureProcessorRegistryName; From 78b0683313fe37f87c0a2f9990d4b32f04456111 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Tue, 26 Oct 2021 15:47:17 -0500 Subject: [PATCH 049/120] Added GetComponentTypeEditorIcon API and replaced old macro style ebus calls. Signed-off-by: Chris Galvan --- .../ComponentEntityEditorPlugin/SandboxIntegration.cpp | 5 +++++ .../ComponentEntityEditorPlugin/SandboxIntegration.h | 1 + .../UI/ComponentPalette/ComponentDataModel.cpp | 2 +- Code/Editor/TrackView/TrackViewNodes.cpp | 2 +- .../AzToolsFramework/API/ToolsApplicationAPI.h | 4 ++++ .../AzToolsFramework/Application/ToolsApplication.cpp | 9 ++++++++- .../UI/ComponentPalette/ComponentPaletteUtil.cpp | 2 +- .../UI/PropertyEditor/ComponentEditor.cpp | 2 +- 8 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp index 41e950a058..5b849dcbe7 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp @@ -1788,6 +1788,11 @@ AZStd::string SandboxIntegrationManager::GetComponentEditorIcon(const AZ::Uuid& return iconPath; } +AZStd::string SandboxIntegrationManager::GetComponentTypeEditorIcon(const AZ::Uuid& componentType) +{ + return GetComponentEditorIcon(componentType, nullptr); +} + AZStd::string SandboxIntegrationManager::GetComponentIconPath(const AZ::Uuid& componentType, AZ::Crc32 componentIconAttrib, AZ::Component* component) { diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h index 40962409a3..9afa944438 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h @@ -233,6 +233,7 @@ private: } AZStd::string GetComponentEditorIcon(const AZ::Uuid& componentType, AZ::Component* component) override; + AZStd::string GetComponentTypeEditorIcon(const AZ::Uuid& componentType) override; AZStd::string GetComponentIconPath(const AZ::Uuid& componentType, AZ::Crc32 componentIconAttrib, AZ::Component* component) override; ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/ComponentPalette/ComponentDataModel.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/ComponentPalette/ComponentDataModel.cpp index cd0fba350f..dcae6abfeb 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/ComponentPalette/ComponentDataModel.cpp +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/ComponentPalette/ComponentDataModel.cpp @@ -139,7 +139,7 @@ ComponentDataModel::ComponentDataModel(QObject* parent) if (element.m_elementId == AZ::Edit::ClassElements::EditorData) { AZStd::string iconPath; - EBUS_EVENT_RESULT(iconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, classData->m_typeId, nullptr); + AzToolsFramework::EditorRequestBus::BroadcastResult(iconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, classData->m_typeId); if (!iconPath.empty()) { m_componentIcons[classData->m_typeId] = QIcon(iconPath.c_str()); diff --git a/Code/Editor/TrackView/TrackViewNodes.cpp b/Code/Editor/TrackView/TrackViewNodes.cpp index 2a2d584e46..db65abf9d2 100644 --- a/Code/Editor/TrackView/TrackViewNodes.cpp +++ b/Code/Editor/TrackView/TrackViewNodes.cpp @@ -408,7 +408,7 @@ CTrackViewNodesCtrl::CTrackViewNodesCtrl(QWidget* hParentWnd, CTrackViewDialog* serializeContext->EnumerateDerived([this](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&) -> bool { AZStd::string iconPath; - EBUS_EVENT_RESULT(iconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, classData->m_typeId, nullptr); + AzToolsFramework::EditorRequestBus::BroadcastResult(iconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, classData->m_typeId); if (!iconPath.empty()) { m_componentTypeToIconMap[classData->m_typeId] = QIcon(iconPath.c_str()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h index 9e29b9813c..fd4196a296 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h @@ -826,6 +826,10 @@ namespace AzToolsFramework /// Path will be empty if component should have no icon. virtual AZStd::string GetComponentEditorIcon(const AZ::Uuid& /*componentType*/, AZ::Component* /*component*/) { return AZStd::string(); } + //! Return path to icon for component type. + //! Path will be empty if component type should have no icon. + virtual AZStd::string GetComponentTypeEditorIcon(const AZ::Uuid& /*componentType*/) { return AZStd::string(); } + /** * Return the icon image path based on the component type and where it is used. * \param componentType component type diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index fbd066ec6e..cdafa63eba 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -175,7 +175,7 @@ namespace AzToolsFramework , public AZ::BehaviorEBusHandler { AZ_EBUS_BEHAVIOR_BINDER(ToolsApplicationNotificationBusHandler, "{7EB67956-FF86-461A-91E2-7B08279CFACF}", AZ::SystemAllocator, - EntityRegistered, EntityDeregistered); + EntityRegistered, EntityDeregistered, AfterEntitySelectionChanged); void EntityRegistered(AZ::EntityId entityId) override { @@ -186,6 +186,11 @@ namespace AzToolsFramework { Call(FN_EntityDeregistered, entityId); } + + void AfterEntitySelectionChanged(const EntityIdList& newlySelectedEntities, const EntityIdList& newlyDeselectedEntities) override + { + Call(FN_AfterEntitySelectionChanged, newlySelectedEntities, newlyDeselectedEntities); + } }; struct ViewPaneCallbackBusHandler final @@ -408,6 +413,7 @@ namespace AzToolsFramework ->Handler() ->Event("EntityRegistered", &ToolsApplicationEvents::EntityRegistered) ->Event("EntityDeregistered", &ToolsApplicationEvents::EntityDeregistered) + ->Event("AfterEntitySelectionChanged", &ToolsApplicationEvents::AfterEntitySelectionChanged) ; behaviorContext->Class() @@ -426,6 +432,7 @@ namespace AzToolsFramework ->Attribute(AZ::Script::Attributes::Module, "editor") ->Event("RegisterCustomViewPane", &EditorRequests::RegisterCustomViewPane) ->Event("UnregisterViewPane", &EditorRequests::UnregisterViewPane) + ->Event("GetComponentTypeEditorIcon", &EditorRequests::GetComponentTypeEditorIcon) ; behaviorContext->EBus("EditorEventBus") diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp index b6f5b41b65..888554d065 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.cpp @@ -90,7 +90,7 @@ namespace AzToolsFramework } AZStd::string componentIconPath; - EBUS_EVENT_RESULT(componentIconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, componentClass->m_typeId, nullptr); + AzToolsFramework::EditorRequestBus::BroadcastResult(componentIconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, componentClass->m_typeId); componentIconTable[componentClass] = QString::fromUtf8(componentIconPath.c_str()); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.cpp index 91d2359ca6..48dec6f225 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ComponentEditor.cpp @@ -583,7 +583,7 @@ namespace AzToolsFramework } AZStd::string iconPath; - EBUS_EVENT_RESULT(iconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, componentType, const_cast(&componentInstance)); + AzToolsFramework::EditorRequestBus::BroadcastResult(iconPath, &AzToolsFramework::EditorRequests::GetComponentEditorIcon, componentType, const_cast(&componentInstance)); GetHeader()->SetIcon(QIcon(iconPath.c_str())); bool isExpanded = true; From 8988800a435879d8b910acdac8255f315e2a4979 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Mon, 25 Oct 2021 16:02:55 -0700 Subject: [PATCH 050/120] Fixed potential unused variable 'originalVersion' with 'maybe_unused' attribute. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index e9d8a42641..36f4947e3d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -208,7 +208,7 @@ namespace AZ return; } - const uint32_t originalVersion = m_materialTypeVersion; + [[maybe_unused]] const uint32_t originalVersion = m_materialTypeVersion; bool changesWereApplied = false; From edb50480f496586009971ac457003c482e3c5943 Mon Sep 17 00:00:00 2001 From: rhhong Date: Tue, 26 Oct 2021 14:58:34 -0700 Subject: [PATCH 051/120] Calculate camera view projection each frame so we can have a fixed size viewport. Signed-off-by: rhhong --- .../Tools/EMStudio/AnimViewportRenderer.cpp | 4 ++-- .../Tools/EMStudio/AnimViewportWidget.cpp | 24 ++++++++++++++++++- .../Code/Tools/EMStudio/AnimViewportWidget.h | 3 +++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp index f6186be235..facf7a7b12 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp @@ -128,10 +128,10 @@ namespace EMStudio AZ_Assert(m_gridEntity != nullptr, "Failed to create grid entity."); AZ::Render::GridComponentConfig gridConfig; - gridConfig.m_gridSize = 4.0f; + gridConfig.m_gridSize = 20.0f; gridConfig.m_axisColor = AZ::Color(0.5f, 0.5f, 0.5f, 1.0f); gridConfig.m_primaryColor = AZ::Color(0.3f, 0.3f, 0.3f, 1.0f); - gridConfig.m_secondaryColor = AZ::Color(0.5f, 0.1f, 0.1f, 1.0f); + gridConfig.m_secondaryColor = AZ::Color(0.5f, 0.5f, 0.5f, 1.0f); auto gridComponent = m_gridEntity->CreateComponent(AZ::Render::GridComponentTypeId); gridComponent->SetConfiguration(gridConfig); diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp index 7af5c1607a..42bc154fdf 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp @@ -6,10 +6,11 @@ * */ -#include +#include #include #include #include +#include #include #include @@ -19,6 +20,9 @@ namespace EMStudio { + static constexpr float DepthNear = 0.01f; + static constexpr float DepthFar = 100.0f; + AnimViewportWidget::AnimViewportWidget(QWidget* parent) : AtomToolsFramework::RenderViewportWidget(parent) { @@ -166,6 +170,24 @@ namespace EMStudio GetViewportContext()->SetCameraTransform(AZ::Transform::CreateLookAt(cameraPosition, targetPosition)); } + void AnimViewportWidget::OnTick(float deltaTime, AZ::ScriptTimePoint time) + { + RenderViewportWidget::OnTick(deltaTime, time); + CalculateCameraProjection(); + } + + void AnimViewportWidget::CalculateCameraProjection() + { + auto viewportContext = GetViewportContext(); + auto windowSize = viewportContext->GetViewportSize(); + const float aspectRatio = aznumeric_cast(windowSize.m_width) / aznumeric_cast(windowSize.m_height); + + AZ::Matrix4x4 viewToClipMatrix; + AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, AZ::Constants::HalfPi, aspectRatio, DepthNear, DepthFar, true); + + viewportContext->GetDefaultView()->SetViewToClipMatrix(viewToClipMatrix); + } + void AnimViewportWidget::ToggleRenderFlag(EMotionFX::ActorRenderFlag flag) { m_renderFlags[flag] = !m_renderFlags[flag]; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h index 8aa316a8ba..5a193d31f1 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h @@ -30,6 +30,9 @@ namespace EMStudio EMotionFX::ActorRenderFlagBitset GetRenderFlags() const; private: + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + + void CalculateCameraProjection(); void SetupCameras(); void SetupCameraController(); From 8fd34618636b922033441044b7937286e1ac2745 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Tue, 26 Oct 2021 15:38:09 -0700 Subject: [PATCH 052/120] Removed reference to opacity.doubleSided property that no longer exists. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua index c86fbfe0b7..9315131e44 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua @@ -81,7 +81,6 @@ function ProcessEditor(context) context:SetMaterialPropertyVisibility("opacity.textureMap", mainVisibility) context:SetMaterialPropertyVisibility("opacity.textureMapUv", mainVisibility) context:SetMaterialPropertyVisibility("opacity.factor", mainVisibility) - context:SetMaterialPropertyVisibility("opacity.doubleSided", mainVisibility) if(opacityMode == OpacityMode_Blended or opacityMode == OpacityMode_TintedTransparent) then context:SetMaterialPropertyVisibility("opacity.alphaAffectsSpecular", MaterialPropertyVisibility_Enabled) From 42a14079f2dd41f01048e9169daac282802b98c1 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Tue, 26 Oct 2021 17:48:28 -0500 Subject: [PATCH 053/120] Fix naming for DisableOptimizations vs DxcDisableOptimizations (#5016) Signed-off-by: garrieta --- .../Shaders/ScreenSpace/DeferredFog.shader | 4 +-- .../Atom/RHI.Edit/ShaderCompilerArguments.h | 19 ++++++---- .../RHI.Edit/ShaderCompilerArguments.cpp | 36 +++++++++---------- .../RHI.Builders/ShaderPlatformInterface.cpp | 4 +-- .../RHI.Builders/ShaderPlatformInterface.cpp | 2 +- .../RHI.Builders/ShaderPlatformInterface.cpp | 2 +- .../Types/AutoBrick_ForwardPass.shader | 2 +- .../Types/MinimalPBR_ForwardPass.shader | 2 +- 8 files changed, 39 insertions(+), 32 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/ScreenSpace/DeferredFog.shader b/Gems/Atom/Feature/Common/Assets/Shaders/ScreenSpace/DeferredFog.shader index 5e2ca9bff5..06c991400e 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/ScreenSpace/DeferredFog.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/ScreenSpace/DeferredFog.shader @@ -23,8 +23,8 @@ "DrawList" : "forward", "CompilerHints" : { - "DxcDisableOptimizations" : false, - "DxcGenerateDebugInfo" : false + "DisableOptimizations" : false, + "GenerateDebugInfo" : false }, "ProgramSettings": diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h index d0960a7fde..83a868ab65 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderCompilerArguments.h @@ -57,12 +57,19 @@ namespace AZ AZStd::string m_azslcAdditionalFreeArguments; // note: if you add new sort of arguments here, don't forget to update HasDifferentAzslcArguments() - //! DXC - bool m_dxcDisableWarnings = false; - bool m_dxcWarningAsError = false; - bool m_dxcDisableOptimizations = false; - bool m_dxcGenerateDebugInfo = false; - uint8_t m_dxcOptimizationLevel = LevelUnset; + //! Remark: To the user, the following parameters are exposed without the + //! "Dxc" prefix because these are common options for the "main" compiler + //! for the given RHI. At the moment the only "main" compiler is Dxc, but in + //! the future AZSLc may transpile from AZSL to some other proprietary language + //! and in that case the "main" compiler won't be DXC + bool m_disableWarnings = false; + bool m_warningAsError = false; + bool m_disableOptimizations = false; + bool m_generateDebugInfo = false; + uint8_t m_optimizationLevel = LevelUnset; + //! "DxcAdditionalFreeArguments" keeps the "Dxc" prefix because these arguments + //! are specific to DXC, and it will be relevant only if DXC is the "main" compiler + //! for a given RHI, otherwise this parameter won't matter. AZStd::string m_dxcAdditionalFreeArguments; //! both diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp index ef330e7902..5411e6655e 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp @@ -33,17 +33,17 @@ namespace AZ RegisterEnumerators(serializeContext); serializeContext->Class() - ->Version(2) + ->Version(3) ->Field("AzslcWarningLevel", &ShaderCompilerArguments::m_azslcWarningLevel) ->Field("AzslcWarningAsError", &ShaderCompilerArguments::m_azslcWarningAsError) ->Field("AzslcAdditionalFreeArguments", &ShaderCompilerArguments::m_azslcAdditionalFreeArguments) - ->Field("DxcDisableWarnings", &ShaderCompilerArguments::m_dxcDisableWarnings) - ->Field("DxcWarningAsError", &ShaderCompilerArguments::m_dxcWarningAsError) - ->Field("DxcDisableOptimizations", &ShaderCompilerArguments::m_dxcDisableOptimizations) - ->Field("DxcGenerateDebugInfo", &ShaderCompilerArguments::m_dxcGenerateDebugInfo) - ->Field("DxcOptimizationLevel", &ShaderCompilerArguments::m_dxcOptimizationLevel) - ->Field("DxcAdditionalFreeArguments", &ShaderCompilerArguments::m_dxcAdditionalFreeArguments) + ->Field("DisableWarnings", &ShaderCompilerArguments::m_disableWarnings) + ->Field("WarningAsError", &ShaderCompilerArguments::m_warningAsError) + ->Field("DisableOptimizations", &ShaderCompilerArguments::m_disableOptimizations) + ->Field("GenerateDebugInfo", &ShaderCompilerArguments::m_generateDebugInfo) + ->Field("OptimizationLevel", &ShaderCompilerArguments::m_optimizationLevel) ->Field("DefaultMatrixOrder", &ShaderCompilerArguments::m_defaultMatrixOrder) + ->Field("DxcAdditionalFreeArguments", &ShaderCompilerArguments::m_dxcAdditionalFreeArguments) ; } } @@ -62,13 +62,13 @@ namespace AZ } m_azslcWarningAsError = m_azslcWarningAsError || right.m_azslcWarningAsError; m_azslcAdditionalFreeArguments = CommandLineArgumentUtils::MergeCommandLineArguments(m_azslcAdditionalFreeArguments, right.m_azslcAdditionalFreeArguments); - m_dxcDisableWarnings = m_dxcDisableWarnings || right.m_dxcDisableWarnings; - m_dxcWarningAsError = m_dxcWarningAsError || right.m_dxcWarningAsError; - m_dxcDisableOptimizations = m_dxcDisableOptimizations || right.m_dxcDisableOptimizations; - m_dxcGenerateDebugInfo = m_dxcGenerateDebugInfo || right.m_dxcGenerateDebugInfo; - if (right.m_dxcOptimizationLevel != LevelUnset) + m_disableWarnings = m_disableWarnings || right.m_disableWarnings; + m_warningAsError = m_warningAsError || right.m_warningAsError; + m_disableOptimizations = m_disableOptimizations || right.m_disableOptimizations; + m_generateDebugInfo = m_generateDebugInfo || right.m_generateDebugInfo; + if (right.m_optimizationLevel != LevelUnset) { - m_dxcOptimizationLevel = right.m_dxcOptimizationLevel; + m_optimizationLevel = right.m_optimizationLevel; } m_dxcAdditionalFreeArguments = CommandLineArgumentUtils::MergeCommandLineArguments(m_dxcAdditionalFreeArguments, right.m_dxcAdditionalFreeArguments); if (right.m_defaultMatrixOrder != MatrixOrder::Default) @@ -131,21 +131,21 @@ namespace AZ AZStd::string ShaderCompilerArguments::MakeAdditionalDxcCommandLineString() const { AZStd::string arguments; - if (m_dxcDisableWarnings) + if (m_disableWarnings) { arguments += " -no-warnings"; } - else if (m_dxcWarningAsError) + else if (m_warningAsError) { arguments += " -WX"; } - if (m_dxcDisableOptimizations) + if (m_disableOptimizations) { arguments += " -Od"; } - else if (m_dxcOptimizationLevel <= 3) + else if (m_optimizationLevel <= 3) { - arguments = " -O" + AZStd::to_string(m_dxcOptimizationLevel); + arguments = " -O" + AZStd::to_string(m_optimizationLevel); } if (m_defaultMatrixOrder == MatrixOrder::Column) { diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp index f30fc72ace..ee293292e1 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp @@ -114,7 +114,7 @@ namespace AZ } } - if (shaderCompilerArguments.m_dxcDisableOptimizations) + if (shaderCompilerArguments.m_disableOptimizations) { // When optimizations are disabled (-Od), all resources declared in the source file are available to all stages // (when enabled only the resources which are referenced in a stage are bound to the stage) @@ -195,7 +195,7 @@ namespace AZ bool ShaderPlatformInterface::BuildHasDebugInfo(const RHI::ShaderCompilerArguments& shaderCompilerArguments) const { - return shaderCompilerArguments.m_dxcGenerateDebugInfo; + return shaderCompilerArguments.m_generateDebugInfo; } const char* ShaderPlatformInterface::GetAzslHeader(const AssetBuilderSDK::PlatformInfo& platform) const diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp index 7e41e50892..d43d88a7e1 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp @@ -167,7 +167,7 @@ namespace AZ bool ShaderPlatformInterface::BuildHasDebugInfo(const RHI::ShaderCompilerArguments& shaderCompilerArguments) const { - return shaderCompilerArguments.m_dxcGenerateDebugInfo; + return shaderCompilerArguments.m_generateDebugInfo; } const char* ShaderPlatformInterface::GetAzslHeader(const AssetBuilderSDK::PlatformInfo& platform) const diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp index 8b99b7b510..c5f1060ca3 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI.Builders/ShaderPlatformInterface.cpp @@ -109,7 +109,7 @@ namespace AZ bool ShaderPlatformInterface::BuildHasDebugInfo(const RHI::ShaderCompilerArguments& shaderCompilerArguments) const { - return shaderCompilerArguments.m_dxcGenerateDebugInfo; + return shaderCompilerArguments.m_generateDebugInfo; } const char* ShaderPlatformInterface::GetAzslHeader(const AssetBuilderSDK::PlatformInfo& platform) const diff --git a/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.shader b/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.shader index 4f1c45d235..6418cc392e 100644 --- a/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.shader +++ b/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.shader @@ -24,7 +24,7 @@ }, "CompilerHints" : { - "DxcDisableOptimizations" : false + "DisableOptimizations" : false }, "ProgramSettings": diff --git a/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR_ForwardPass.shader b/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR_ForwardPass.shader index 13ba0ce547..f87a56daa2 100644 --- a/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR_ForwardPass.shader +++ b/Gems/Atom/TestData/TestData/Materials/Types/MinimalPBR_ForwardPass.shader @@ -24,7 +24,7 @@ }, "CompilerHints" : { - "DxcDisableOptimizations" : false + "DisableOptimizations" : false }, "ProgramSettings": From 250a91dd96d399b10069559a55975afa18468da4 Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Tue, 26 Oct 2021 19:10:46 -0400 Subject: [PATCH 054/120] Network Hierarchy Root and Child components can now act as MultiplayerInputDriver for components with NetworkInputs Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> --- .../Code/Source/Components/NetworkHierarchyChildComponent.cpp | 1 + .../Code/Source/Components/NetworkHierarchyRootComponent.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp index 59782b1f4d..aa90b1b17b 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -45,6 +45,7 @@ namespace Multiplayer void NetworkHierarchyChildComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { provided.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent")); + provided.push_back(AZ_CRC_CE("MultiplayerInputDriver")); } void NetworkHierarchyChildComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 76f4bddb1a..1404484d5c 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -53,6 +53,7 @@ namespace Multiplayer void NetworkHierarchyRootComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { provided.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent")); + provided.push_back(AZ_CRC_CE("MultiplayerInputDriver")); } void NetworkHierarchyRootComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) From e4c69a29fa83e0a2dfff2581f5fe0e542cf0e7e1 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Tue, 26 Oct 2021 16:45:33 -0700 Subject: [PATCH 055/120] [Linux] Update Qt package to include xcb GL integration plugin (#4976) Fixes #3132. Signed-off-by: Chris Burel --- cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index 6210b9cc18..af7afff5dc 100644 --- a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake +++ b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake @@ -33,7 +33,7 @@ ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-linux ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-linux TARGETS googletest PACKAGE_HASH 7b7ad330f369450c316a4c4592d17fbb4c14c731c95bd8f37757203e8c2bbc1b) ly_associate_package(PACKAGE_NAME googlebenchmark-1.5.0-rev2-linux TARGETS GoogleBenchmark PACKAGE_HASH 4038878f337fc7e0274f0230f71851b385b2e0327c495fc3dd3d1c18a807928d) ly_associate_package(PACKAGE_NAME unwind-1.2.1-linux TARGETS unwind PACKAGE_HASH 3453265fb056e25432f611a61546a25f60388e315515ad39007b5925dd054a77) -ly_associate_package(PACKAGE_NAME qt-5.15.2-rev5-linux TARGETS Qt PACKAGE_HASH 76b395897b941a173002845c7219a5f8a799e44b269ffefe8091acc048130f28) +ly_associate_package(PACKAGE_NAME qt-5.15.2-rev6-linux TARGETS Qt PACKAGE_HASH a37bd9989f1e8fe57d94b98cbf9bd5c3caaea740e2f314e5162fa77300551531) ly_associate_package(PACKAGE_NAME libpng-1.6.37-rev1-linux TARGETS libpng PACKAGE_HASH 896451999f1de76375599aec4b34ae0573d8d34620d9ab29cc30b8739c265ba6) ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-linux TARGETS libsamplerate PACKAGE_HASH 41643c31bc6b7d037f895f89d8d8d6369e906b92eff42b0fe05ee6a100f06261) ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-linux TARGETS OpenSSL PACKAGE_HASH b779426d1e9c5ddf71160d5ae2e639c3b956e0fb5e9fcaf9ce97c4526024e3bc) From cd65e686ffa1cf76951e7bf32db0c99ec20bb1a2 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Wed, 27 Oct 2021 00:53:24 -0700 Subject: [PATCH 056/120] Use `aznew` where appropriate for EMotionFX Command subclasses (#4912) Signed-off-by: Chris Burel --- .../CommandSystem/Source/AnimGraphNodeGroupCommands.h | 2 +- .../Code/EMotionFX/CommandSystem/Source/CommandManager.cpp | 4 ++-- .../Code/EMotionFX/CommandSystem/Source/NodeGroupCommands.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphNodeGroupCommands.h b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphNodeGroupCommands.h index 014d3b0398..3623bd5cd5 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphNodeGroupCommands.h +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphNodeGroupCommands.h @@ -60,7 +60,7 @@ namespace CommandSystem const char* GetDescription() const override; MCore::Command* Create() override { - return new CommandAnimGraphAdjustNodeGroup(this); + return aznew CommandAnimGraphAdjustNodeGroup(this); } static AZStd::vector GenerateNodeNameVector(EMotionFX::AnimGraph* animGraph, const AZStd::vector& nodeIDs); diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/CommandManager.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/CommandManager.cpp index be4626f592..2c646b3504 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/CommandManager.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/CommandManager.cpp @@ -104,7 +104,7 @@ namespace CommandSystem RegisterCommand(new CommandMotionSetAdjustMotion()); // register node group commands - RegisterCommand(new CommandAdjustNodeGroup()); + RegisterCommand(aznew CommandAdjustNodeGroup()); RegisterCommand(new CommandAddNodeGroup()); RegisterCommand(new CommandRemoveNodeGroup()); @@ -134,7 +134,7 @@ namespace CommandSystem RegisterCommand(aznew CommandAdjustTransitionCondition()); RegisterCommand(new CommandAnimGraphAddNodeGroup()); RegisterCommand(new CommandAnimGraphRemoveNodeGroup()); - RegisterCommand(new CommandAnimGraphAdjustNodeGroup()); + RegisterCommand(aznew CommandAnimGraphAdjustNodeGroup()); RegisterCommand(new CommandAnimGraphAddGroupParameter()); RegisterCommand(new CommandAnimGraphRemoveGroupParameter()); RegisterCommand(new CommandAnimGraphAdjustGroupParameter()); diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/NodeGroupCommands.h b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/NodeGroupCommands.h index 6c57877bd5..640a670e44 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/NodeGroupCommands.h +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/NodeGroupCommands.h @@ -63,7 +63,7 @@ namespace CommandSystem const char* GetDescription() const override; MCore::Command* Create() override { - return new CommandAdjustNodeGroup(this); + return aznew CommandAdjustNodeGroup(this); } private: From 3e3f27e65c3d82cc262476901cc20b44d01098a1 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Wed, 27 Oct 2021 02:28:22 -0700 Subject: [PATCH 057/120] bugfix: improve viewport overlay (#4939) * bugfix: improve viewport overlay - disable animation for window - fix problem where vieport is offset from main window Signed-off-by: Michael Pollind * update geometry of m_uiOverlay Signed-off-by: Michael Pollind --- .../AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp index 43e2a6793f..5155de3087 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp @@ -427,8 +427,8 @@ namespace AzToolsFramework::ViewportUi::Internal void ViewportUiDisplay::PositionUiOverlayOverRenderViewport() { QPoint offset = m_renderOverlay->mapToGlobal(QPoint()); - m_uiMainWindow.move(offset); - m_uiOverlay.setFixedSize(m_renderOverlay->width(), m_renderOverlay->height()); + m_uiMainWindow.setGeometry(offset.x(), offset.y(), m_renderOverlay->width(), m_renderOverlay->height()); + m_uiOverlay.setGeometry(m_uiMainWindow.rect()); UpdateUiOverlayGeometry(); } From b9d51e53eb0feb399b02967643a4f4728bff61c5 Mon Sep 17 00:00:00 2001 From: jiaweig Date: Wed, 27 Oct 2021 03:53:32 -0700 Subject: [PATCH 058/120] Combine stencil face bits Signed-off-by: jiaweig --- Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandList.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandList.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandList.cpp index 2620aa5f7b..ffa02ac3c4 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandList.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandList.cpp @@ -729,8 +729,7 @@ namespace AZ void CommandList::SetStencilRef(uint8_t stencilRef) { - vkCmdSetStencilReference(m_nativeCommandBuffer, VK_STENCIL_FACE_FRONT_BIT, static_cast(stencilRef)); - vkCmdSetStencilReference(m_nativeCommandBuffer, VK_STENCIL_FACE_BACK_BIT, static_cast(stencilRef)); + vkCmdSetStencilReference(m_nativeCommandBuffer, VK_STENCIL_FACE_FRONT_AND_BACK, aznumeric_cast(stencilRef)); } void CommandList::BindPipeline(const PipelineState* pipelineState) From a29623e5f0d5d1bd83bcc7fa043f70e4c84e1123 Mon Sep 17 00:00:00 2001 From: moraaar Date: Wed, 27 Oct 2021 13:34:43 +0100 Subject: [PATCH 059/120] Fixed editor crash using cylinder shape component (#5037) Fixed cylinder shape by considering shape config list to be empty. The crash came from clearing shape config list when cylinder height is zero, but when the height is restored to zero it was expecting an element in the list. The code to set the shape configuration was the same for many shape types, so it has been refactored to a helper function. Fixes #4999 --- .../Source/EditorShapeColliderComponent.cpp | 56 ++----------------- .../Source/EditorShapeColliderComponent.h | 32 ++++++++++- .../Tests/ShapeColliderComponentTests.cpp | 40 ++++++++++++- 3 files changed, 73 insertions(+), 55 deletions(-) diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index 832145d016..59b659a0bf 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -348,19 +348,7 @@ namespace PhysX LmbrCentral::BoxShapeComponentRequestsBus::EventResult(boxDimensions, GetEntityId(), &LmbrCentral::BoxShapeComponentRequests::GetBoxDimensions); - if (m_shapeType != ShapeType::Box) - { - m_shapeConfigs.clear(); - m_shapeConfigs.emplace_back(AZStd::make_shared(boxDimensions)); - - m_shapeType = ShapeType::Box; - } - else - { - Physics::BoxShapeConfiguration& configuration = - static_cast(*m_shapeConfigs.back()); - configuration = Physics::BoxShapeConfiguration(boxDimensions); - } + SetShapeConfig(ShapeType::Box, Physics::BoxShapeConfiguration(boxDimensions)); m_shapeConfigs.back()->m_scale = scale; m_geometryCache.m_boxDimensions = scale * boxDimensions; @@ -374,19 +362,7 @@ namespace PhysX const Physics::CapsuleShapeConfiguration& capsuleShapeConfig = Utils::ConvertFromLmbrCentralCapsuleConfig(lmbrCentralCapsuleShapeConfig); - if (m_shapeType != ShapeType::Capsule) - { - m_shapeConfigs.clear(); - m_shapeConfigs.emplace_back(AZStd::make_shared(capsuleShapeConfig)); - - m_shapeType = ShapeType::Capsule; - } - else - { - Physics::CapsuleShapeConfiguration& configuration = - static_cast(*m_shapeConfigs.back()); - configuration = capsuleShapeConfig; - } + SetShapeConfig(ShapeType::Capsule, capsuleShapeConfig); m_shapeConfigs.back()->m_scale = scale; const float scalarScale = scale.GetMaxElement(); @@ -400,19 +376,7 @@ namespace PhysX LmbrCentral::SphereShapeComponentRequestsBus::EventResult(radius, GetEntityId(), &LmbrCentral::SphereShapeComponentRequests::GetRadius); - if (m_shapeType != ShapeType::Sphere) - { - m_shapeConfigs.clear(); - m_shapeConfigs.emplace_back(AZStd::make_shared(radius)); - - m_shapeType = ShapeType::Sphere; - } - else - { - Physics::SphereShapeConfiguration& configuration = - static_cast(*m_shapeConfigs.back()); - configuration = Physics::SphereShapeConfiguration(radius); - } + SetShapeConfig(ShapeType::Sphere, Physics::SphereShapeConfiguration(radius)); m_shapeConfigs.back()->m_scale = scale; m_geometryCache.m_radius = scale.GetMaxElement() * radius; @@ -455,19 +419,7 @@ namespace PhysX if (shapeConfig.has_value()) { - if (m_shapeType != ShapeType::Cylinder) - { - m_shapeConfigs.clear(); - m_shapeConfigs.push_back(AZStd::make_shared(shapeConfig.value())); - - m_shapeType = ShapeType::Cylinder; - } - else - { - Physics::CookedMeshShapeConfiguration& configuration = - static_cast(*m_shapeConfigs.back()); - configuration = Physics::CookedMeshShapeConfiguration(shapeConfig.value()); - } + SetShapeConfig(ShapeType::Cylinder, shapeConfig.value()); CreateStaticEditorCollider(); } diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h index 431b34b1ae..3422d4959f 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -90,12 +91,15 @@ namespace PhysX void UpdateBoxConfig(const AZ::Vector3& scale); void UpdateCapsuleConfig(const AZ::Vector3& scale); void UpdateSphereConfig(const AZ::Vector3& scale); + void UpdateCylinderConfig(const AZ::Vector3& scale); void UpdatePolygonPrismDecomposition(); void UpdatePolygonPrismDecomposition(const AZ::PolygonPrismPtr polygonPrismPtr); - void RefreshUiProperties(); + // Helper function to set a specific shape configuration + template + void SetShapeConfig(ShapeType shapeType, const ConfigType& shapeConfig); - void UpdateCylinderConfig(const AZ::Vector3& scale); + void RefreshUiProperties(); AZ::u32 OnSubdivisionCountChange(); AZ::Crc32 SubdivisionCountVisibility(); @@ -154,4 +158,28 @@ namespace PhysX AZ::NonUniformScaleChangedEvent::Handler m_nonUniformScaleChangedHandler; //!< Responds to changes in non-uniform scale. AZ::Vector3 m_currentNonUniformScale = AZ::Vector3::CreateOne(); //!< Caches the current non-uniform scale. }; + + template + void EditorShapeColliderComponent::SetShapeConfig(ShapeType shapeType, const ConfigType& shapeConfig) + { + if (m_shapeType != shapeType) + { + m_shapeConfigs.clear(); + m_shapeType = shapeType; + } + + if (m_shapeConfigs.empty()) + { + m_shapeConfigs.emplace_back(AZStd::make_shared(shapeConfig)); + } + else + { + AZ_Assert(m_shapeConfigs.back()->GetShapeType() == shapeConfig.GetShapeType(), + "Expected Physics shape configuration with shape type %d but found one with shape type %d.", + static_cast(shapeConfig.GetShapeType()), static_cast(m_shapeConfigs.back()->GetShapeType())); + ConfigType& configuration = + static_cast(*m_shapeConfigs.back()); + configuration = shapeConfig; + } + } } // namespace PhysX diff --git a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp index fce427d9d3..06ba4f3e9c 100644 --- a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp @@ -320,7 +320,7 @@ namespace PhysXEditorTests TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithCylinderWithNullHeight_HandledGracefully) { - ValidateInvalidEditorShapeColliderComponentParams(0.f, 1.f); + ValidateInvalidEditorShapeColliderComponentParams(1.f, 0.f); } TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithCylinderWithNullRadiusAndNullHeight_HandledGracefully) @@ -338,6 +338,44 @@ namespace PhysXEditorTests ValidateInvalidEditorShapeColliderComponentParams(0.f, -1.f); } + TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithCylinderSwitchingFromNullHeightToValidHeight_HandledGracefully) + { + // create an editor entity with a shape collider component and a cylinder shape component + EntityPtr editorEntity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity"); + editorEntity->CreateComponent(); + editorEntity->CreateComponent(LmbrCentral::EditorCylinderShapeComponentTypeId); + editorEntity->Activate(); + + const float validRadius = 1.0f; + const float nullHeight = 0.0f; + const float validHeight = 1.0f; + + LmbrCentral::CylinderShapeComponentRequestsBus::Event(editorEntity->GetId(), + &LmbrCentral::CylinderShapeComponentRequests::SetRadius, validRadius); + + { + UnitTest::ErrorHandler dimensionWarningHandler("Negative or zero cylinder dimensions are invalid"); + UnitTest::ErrorHandler colliderWarningHandler("No Collider or Shape information found when creating Rigid body"); + + LmbrCentral::CylinderShapeComponentRequestsBus::Event(editorEntity->GetId(), + &LmbrCentral::CylinderShapeComponentRequests::SetHeight, nullHeight); + + EXPECT_EQ(dimensionWarningHandler.GetExpectedWarningCount(), 1); + EXPECT_EQ(colliderWarningHandler.GetExpectedWarningCount(), 1); + } + + { + UnitTest::ErrorHandler dimensionWarningHandler("Negative or zero cylinder dimensions are invalid"); + UnitTest::ErrorHandler colliderWarningHandler("No Collider or Shape information found when creating Rigid body"); + + LmbrCentral::CylinderShapeComponentRequestsBus::Event(editorEntity->GetId(), + &LmbrCentral::CylinderShapeComponentRequests::SetHeight, validHeight); + + EXPECT_EQ(dimensionWarningHandler.GetExpectedWarningCount(), 0); + EXPECT_EQ(colliderWarningHandler.GetExpectedWarningCount(), 0); + } + } + TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithBoxAndRigidBody_CorrectRuntimeComponents) { // create an editor entity with a shape collider component and a box shape component From af7bb2332f0a0e26bd6eb8dc9aff097e6375e6a3 Mon Sep 17 00:00:00 2001 From: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed, 27 Oct 2021 08:15:35 -0500 Subject: [PATCH 060/120] {lyn7677} updated test modules to pass AssetPipelineTests on Linux (#5017) * {lyn7677} updated test modules to pass AssetPipelineTests on Linux Fixes for Python AssetPipelineTests modules fail on Linux Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> * Separating the Linux and Mac concerns Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> --- .../ap_fixtures/ap_fast_scan_setting_backup_fixture.py | 3 +++ .../asset_processor_batch_dependency_tests.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_fast_scan_setting_backup_fixture.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_fast_scan_setting_backup_fixture.py index 4a096ca3fe..c224289cf4 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_fast_scan_setting_backup_fixture.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/ap_fast_scan_setting_backup_fixture.py @@ -29,6 +29,9 @@ def ap_fast_scan_setting_backup_fixture(request, workspace) -> PlatformSetting: if workspace.asset_processor_platform == 'mac': pytest.skip("Mac plist file editing not implemented yet") + if workspace.asset_processor_platform == 'linux': + pytest.skip("Linux system settings not implemented yet") + key = fast_scan_key subkey = fast_scan_subkey diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py index ee8e177bbb..264f690534 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py @@ -79,7 +79,7 @@ class TestsAssetProcessorBatch_DependenycyTests(object): env = ap_setup_fixture BATCH_LOG_PATH = env["ap_batch_log_file"] asset_processor.create_temp_asset_root() - asset_processor.add_relative_source_asset(os.path.join("Assets", "Engine", "engine_dependencies.xml")) + asset_processor.add_relative_source_asset(os.path.join("Assets", "Engine", "Engine_Dependencies.xml")) asset_processor.add_scan_folder(os.path.join("Assets", "Engine")) asset_processor.add_relative_source_asset(os.path.join("Assets", "Engine", "Libs", "MaterialEffects", "surfacetypes.xml")) From 0e4e84eb73d7cfae7f4a0e7e6eab0dd0fb804871 Mon Sep 17 00:00:00 2001 From: Adi Bar-Lev <82479970+Adi-Amazon@users.noreply.github.com> Date: Wed, 27 Oct 2021 09:30:24 -0400 Subject: [PATCH 061/120] Hair - bug fix of changing the method by which passes are acquired (#5015) Signed-off-by: Adi-Amazon Signed-off-by: Adi-Amazon <82479970+Adi-Amazon@users.noreply.github.com> Co-authored-by: Adi-Amazon --- .../Code/Rendering/HairFeatureProcessor.cpp | 13 +++++++------ .../Code/Rendering/HairFeatureProcessor.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp index 161160e16f..74ba99c26c 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp @@ -311,17 +311,17 @@ namespace AZ m_forceClearRenderData = true; } - bool HairFeatureProcessor::HasHairParentPass() + bool HairFeatureProcessor::HasHairParentPass(RPI::RenderPipeline* renderPipeline) { - RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, GetParentScene()); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, renderPipeline); RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); - return pass; + return pass ? true : false; } void HairFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!HasHairParentPass()) + if (!HasHairParentPass(renderPipeline.get())) { return; } @@ -335,7 +335,7 @@ namespace AZ void HairFeatureProcessor::OnRenderPipelineRemoved([[maybe_unused]] RPI::RenderPipeline* renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!HasHairParentPass()) + if (!HasHairParentPass(renderPipeline)) { return; } @@ -347,7 +347,7 @@ namespace AZ void HairFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!HasHairParentPass()) + if (!HasHairParentPass(renderPipeline)) { return; } @@ -623,3 +623,4 @@ namespace AZ } // namespace Hair } // namespace Render } // namespace AZ + diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h index f810967824..70e37a7863 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h @@ -165,7 +165,7 @@ namespace AZ void EnablePasses(bool enable); - bool HasHairParentPass(); + bool HasHairParentPass(RPI::RenderPipeline* renderPipeline); //! The following will serve to register the FP in the Thumbnail system AZStd::vector m_hairFeatureProcessorRegistryName; From cf90d7a59466295ada9223751d4f0020a1d6336d Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Wed, 27 Oct 2021 10:05:26 -0500 Subject: [PATCH 062/120] ShaderVariantAssetBuilder: Provide registry property to disable (#5029) The registry property name is: "/O3DE/Atom/Shaders/BuildVariants" Default value is . Signed-off-by: garrieta --- .../AzslShaderBuilderSystemComponent.cpp | 42 +++++++++++++------ .../Editor/AzslShaderBuilderSystemComponent.h | 10 +++++ .../Asset/Shader/Registry/atom_shaders.setreg | 10 +++++ 3 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 Gems/Atom/Asset/Shader/Registry/atom_shaders.setreg diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp index 56f2fdec62..e41c04a0be 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -91,19 +92,31 @@ namespace AZ m_shaderAssetBuilder.BusConnect(shaderAssetBuilderDescriptor.m_busId); AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, shaderAssetBuilderDescriptor); - // Register Shader Variant Asset Builder - AssetBuilderSDK::AssetBuilderDesc shaderVariantAssetBuilderDescriptor; - shaderVariantAssetBuilderDescriptor.m_name = "Shader Variant Asset Builder"; - // Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update - // ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder". - shaderVariantAssetBuilderDescriptor.m_version = 26; // [AZSL] Changing inlineConstant to rootConstant keyword work. - shaderVariantAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); - shaderVariantAssetBuilderDescriptor.m_busId = azrtti_typeid(); - shaderVariantAssetBuilderDescriptor.m_createJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::CreateJobs, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); - shaderVariantAssetBuilderDescriptor.m_processJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::ProcessJob, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); + // If, either the SettingsRegistry doesn't exist, or the property @EnableShaderVariantAssetBuilderRegistryKey is not found, + // the default is to enable the ShaderVariantAssetBuilder. + m_enableShaderVariantAssetBuilder = true; + auto settingsRegistry = AZ::SettingsRegistry::Get(); + if (settingsRegistry) + { + settingsRegistry->Get(m_enableShaderVariantAssetBuilder, EnableShaderVariantAssetBuilderRegistryKey); + } - m_shaderVariantAssetBuilder.BusConnect(shaderVariantAssetBuilderDescriptor.m_busId); - AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, shaderVariantAssetBuilderDescriptor); + if (m_enableShaderVariantAssetBuilder) + { + // Register Shader Variant Asset Builder + AssetBuilderSDK::AssetBuilderDesc shaderVariantAssetBuilderDescriptor; + shaderVariantAssetBuilderDescriptor.m_name = "Shader Variant Asset Builder"; + // Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update + // ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder". + shaderVariantAssetBuilderDescriptor.m_version = 26; // [AZSL] Changing inlineConstant to rootConstant keyword work. + shaderVariantAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); + shaderVariantAssetBuilderDescriptor.m_busId = azrtti_typeid(); + shaderVariantAssetBuilderDescriptor.m_createJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::CreateJobs, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); + shaderVariantAssetBuilderDescriptor.m_processJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::ProcessJob, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); + + m_shaderVariantAssetBuilder.BusConnect(shaderVariantAssetBuilderDescriptor.m_busId); + AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, shaderVariantAssetBuilderDescriptor); + } // Register Precompiled Shader Builder AssetBuilderSDK::AssetBuilderDesc precompiledShaderBuilderDescriptor; @@ -121,7 +134,10 @@ namespace AZ void AzslShaderBuilderSystemComponent::Deactivate() { m_shaderAssetBuilder.BusDisconnect(); - m_shaderVariantAssetBuilder.BusDisconnect(); + if (m_enableShaderVariantAssetBuilder) + { + m_shaderVariantAssetBuilder.BusDisconnect(); + } m_precompiledShaderBuilder.BusDisconnect(); RHI::ShaderPlatformInterfaceRegisterBus::Handler::BusDisconnect(); diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.h b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.h index 9ff5bd8282..f502e4c329 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.h +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.h @@ -61,7 +61,17 @@ namespace AZ private: ShaderAssetBuilder m_shaderAssetBuilder; + + // The ShaderVariantAssetBuilder can be disabled with this registry key. + // By default it is enabled. A user might want to disable it when doing look development + // work with shaders or doing lots of iterative changes to shaders. In these cases + // GPU performance doesn't matter at all so it is important to not waste time + // building ShaderVariantAssets (Other than the Root ShaderVariantAsset, of course.). + static constexpr char EnableShaderVariantAssetBuilderRegistryKey[] = "/O3DE/Atom/Shaders/BuildVariants"; + bool m_enableShaderVariantAssetBuilder = true; + ShaderVariantAssetBuilder m_shaderVariantAssetBuilder; + PrecompiledShaderBuilder m_precompiledShaderBuilder; /// Contains the ShaderPlatformInterface for all registered RHIs diff --git a/Gems/Atom/Asset/Shader/Registry/atom_shaders.setreg b/Gems/Atom/Asset/Shader/Registry/atom_shaders.setreg new file mode 100644 index 0000000000..31d108f47a --- /dev/null +++ b/Gems/Atom/Asset/Shader/Registry/atom_shaders.setreg @@ -0,0 +1,10 @@ +{ + "O3DE": { + "Atom": { + "Shaders": { + "BuildVariants": true + } + } + } + } +} From ee57885d640e2d7ea9e23756a2ced8c12d9cdbc0 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Wed, 27 Oct 2021 10:10:59 -0500 Subject: [PATCH 063/120] Cherry picked PythonGem template to stabilization. Signed-off-by: Chris Galvan --- Templates/CMakeLists.txt | 2 + Templates/PythonGem/Template/CMakeLists.txt | 14 ++ .../Code/${NameLower}_editor_files.cmake | 14 ++ .../${NameLower}_editor_shared_files.cmake | 11 + .../${NameLower}_editor_tests_files.cmake | 11 + .../PythonGem/Template/Code/CMakeLists.txt | 76 ++++++ .../Code/Include/${Name}/${Name}Bus.h | 40 ++++ .../Linux/${NameLower}_linux_files.cmake | 15 ++ .../${NameLower}_shared_linux_files.cmake | 15 ++ .../Code/Platform/Linux/PAL_linux.cmake | 11 + .../Platform/Mac/${NameLower}_mac_files.cmake | 15 ++ .../Mac/${NameLower}_shared_mac_files.cmake | 15 ++ .../Template/Code/Platform/Mac/PAL_mac.cmake | 11 + .../${NameLower}_shared_windows_files.cmake | 15 ++ .../Windows/${NameLower}_windows_files.cmake | 15 ++ .../Code/Platform/Windows/PAL_windows.cmake | 11 + .../Code/Source/${Name}EditorModule.cpp | 47 ++++ .../Source/${Name}EditorSystemComponent.cpp | 70 ++++++ .../Source/${Name}EditorSystemComponent.h | 42 ++++ .../Code/Source/${Name}ModuleInterface.h | 36 +++ .../Template/Code/Tests/${Name}EditorTest.cpp | 13 ++ .../Editor/Scripts/${NameLower}_dialog.py | 46 ++++ .../Template/Editor/Scripts/__init__.py | 9 + .../Template/Editor/Scripts/bootstrap.py | 117 ++++++++++ Templates/PythonGem/Template/gem.json | 16 ++ Templates/PythonGem/Template/preview.png | 3 + Templates/PythonGem/template.json | 216 ++++++++++++++++++ 27 files changed, 906 insertions(+) create mode 100644 Templates/PythonGem/Template/CMakeLists.txt create mode 100644 Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake create mode 100644 Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake create mode 100644 Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake create mode 100644 Templates/PythonGem/Template/Code/CMakeLists.txt create mode 100644 Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h create mode 100644 Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake create mode 100644 Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake create mode 100644 Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp create mode 100644 Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp create mode 100644 Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h create mode 100644 Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h create mode 100644 Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp create mode 100644 Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py create mode 100644 Templates/PythonGem/Template/Editor/Scripts/__init__.py create mode 100644 Templates/PythonGem/Template/Editor/Scripts/bootstrap.py create mode 100644 Templates/PythonGem/Template/gem.json create mode 100644 Templates/PythonGem/Template/preview.png create mode 100644 Templates/PythonGem/template.json diff --git a/Templates/CMakeLists.txt b/Templates/CMakeLists.txt index 1a3a45b5ec..84a708989a 100644 --- a/Templates/CMakeLists.txt +++ b/Templates/CMakeLists.txt @@ -9,6 +9,8 @@ ly_install_directory( DIRECTORIES AssetGem + CustomTool + PythonGem DefaultGem DefaultProject MinimalProject diff --git a/Templates/PythonGem/Template/CMakeLists.txt b/Templates/PythonGem/Template/CMakeLists.txt new file mode 100644 index 0000000000..d61bbd9e7d --- /dev/null +++ b/Templates/PythonGem/Template/CMakeLists.txt @@ -0,0 +1,14 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +set(o3de_gem_path ${CMAKE_CURRENT_LIST_DIR}) +set(o3de_gem_json ${o3de_gem_path}/gem.json) +o3de_read_json_key(o3de_gem_name ${o3de_gem_json} "gem_name") +o3de_restricted_path(${o3de_gem_json} o3de_gem_restricted_path) + +add_subdirectory(Code) diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake b/Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake new file mode 100644 index 0000000000..8362d37f52 --- /dev/null +++ b/Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake @@ -0,0 +1,14 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +set(FILES + Include/${Name}/${Name}Bus.h + Source/${Name}ModuleInterface.h + Source/${Name}EditorSystemComponent.cpp + Source/${Name}EditorSystemComponent.h +) diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake b/Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake new file mode 100644 index 0000000000..2d4ceae97d --- /dev/null +++ b/Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +set(FILES + Source/${Name}EditorModule.cpp +) diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake b/Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake new file mode 100644 index 0000000000..ff45c2fc1c --- /dev/null +++ b/Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +set(FILES + Tests/${Name}EditorTest.cpp +) diff --git a/Templates/PythonGem/Template/Code/CMakeLists.txt b/Templates/PythonGem/Template/Code/CMakeLists.txt new file mode 100644 index 0000000000..b7a5ac89a9 --- /dev/null +++ b/Templates/PythonGem/Template/Code/CMakeLists.txt @@ -0,0 +1,76 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +# Currently we are in the Code folder: ${CMAKE_CURRENT_LIST_DIR} +# Get the platform specific folder ${pal_dir} for the current folder: ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} +# Note: ly_get_list_relative_pal_filename will take care of the details for us, as this may be a restricted platform +# in which case it will see if that platform is present here or in the restricted folder. +# i.e. It could here in our gem : Gems/${Name}/Code/Platform/ or +# //Gems/${Name}/Code +ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${o3de_gem_restricted_path} ${o3de_gem_path} ${o3de_gem_name}) + +# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the +# traits for this platform. Traits for a platform are defines for things like whether or not something in this gem +# is supported by this platform. +include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + + +# If we are on a host platform, we want to add the host tools targets like the ${Name}.Editor target which +# will also depend on ${Name}.Static +if(PAL_TRAIT_BUILD_HOST_TOOLS) + ly_add_target( + NAME ${Name}.Editor.Static STATIC + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_editor_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PUBLIC + AZ::AzToolsFramework + ) + + ly_add_target( + NAME ${Name}.Editor GEM_MODULE + NAMESPACE Gem + AUTOMOC + FILES_CMAKE + ${NameLower}_editor_shared_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PUBLIC + Gem::${Name}.Editor.Static + ) + + # By default, we will specify that the above target ${Name} would be used by + # Tool and Builder type targets when this gem is enabled. If you don't want it + # active in Tools or Builders by default, delete one of both of the following lines: + ly_create_alias(NAME ${Name}.Tools NAMESPACE Gem TARGETS Gem::${Name}.Editor) + ly_create_alias(NAME ${Name}.Builders NAMESPACE Gem TARGETS Gem::${Name}.Editor) + + +endif() + +################################################################################ +# Tests +################################################################################ +# See if globally, tests are supported +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + # We globally support tests, see if we support tests on this platform for ${Name}.Static + + # If we are a host platform we want to add tools test like editor tests here + if(PAL_TRAIT_BUILD_HOST_TOOLS) + endif() +endif() diff --git a/Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h b/Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h new file mode 100644 index 0000000000..d09bb2b009 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h @@ -0,0 +1,40 @@ +// {BEGIN_LICENSE} +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +// {END_LICENSE} + +#pragma once + +#include +#include + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}Requests + { + public: + AZ_RTTI(${SanitizedCppName}Requests, "{${Random_Uuid}}"); + virtual ~${SanitizedCppName}Requests() = default; + // Put your public methods here + }; + + class ${SanitizedCppName}BusTraits + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + }; + + using ${SanitizedCppName}RequestBus = AZ::EBus<${SanitizedCppName}Requests, ${SanitizedCppName}BusTraits>; + using ${SanitizedCppName}Interface = AZ::Interface<${SanitizedCppName}Requests>; + +} // namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +# Platform specific files for Linux +# i.e. ../Source/Linux/${Name}Linux.cpp +# ../Source/Linux/${Name}Linux.h +# ../Include/Linux/${Name}Linux.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +# Platform specific files for Linux +# i.e. ../Source/Linux/${Name}Linux.cpp +# ../Source/Linux/${Name}Linux.h +# ../Include/Linux/${Name}Linux.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake b/Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +# Platform specific files for Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +# Platform specific files for Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake b/Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +# Platform specific files for Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +# Platform specific files for Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake b/Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp b/Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp new file mode 100644 index 0000000000..644c513747 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp @@ -0,0 +1,47 @@ +// {BEGIN_LICENSE} +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +// {END_LICENSE} + +#include <${Name}ModuleInterface.h> +#include <${Name}EditorSystemComponent.h> + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}EditorModule + : public ${SanitizedCppName}ModuleInterface + { + public: + AZ_RTTI(${SanitizedCppName}EditorModule, "${ModuleClassId}", ${SanitizedCppName}ModuleInterface); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}EditorModule, AZ::SystemAllocator, 0); + + ${SanitizedCppName}EditorModule() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. + // This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert(m_descriptors.end(), { + ${SanitizedCppName}EditorSystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + * Non-SystemComponents should not be added here + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList { + azrtti_typeid<${SanitizedCppName}EditorSystemComponent>(), + }; + } + }; +}// namespace ${SanitizedCppName} + +AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}EditorModule) diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp new file mode 100644 index 0000000000..1493c98e68 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp @@ -0,0 +1,70 @@ +// {BEGIN_LICENSE} +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + // {END_LICENSE} + +#include +#include <${Name}EditorSystemComponent.h> + +namespace ${SanitizedCppName} +{ + void ${SanitizedCppName}EditorSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class<${SanitizedCppName}EditorSystemComponent, AZ::Component>(); + } + } + + ${SanitizedCppName}EditorSystemComponent::${SanitizedCppName}EditorSystemComponent() + { + if (${SanitizedCppName}Interface::Get() == nullptr) + { + ${SanitizedCppName}Interface::Register(this); + } + } + + ${SanitizedCppName}EditorSystemComponent::~${SanitizedCppName}EditorSystemComponent() + { + if (${SanitizedCppName}Interface::Get() == this) + { + ${SanitizedCppName}Interface::Unregister(this); + } + } + + void ${SanitizedCppName}EditorSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + } + + void ${SanitizedCppName}EditorSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + } + + void ${SanitizedCppName}EditorSystemComponent::Activate() + { + ${SanitizedCppName}RequestBus::Handler::BusConnect(); + AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + } + + void ${SanitizedCppName}EditorSystemComponent::Deactivate() + { + AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + ${SanitizedCppName}RequestBus::Handler::BusDisconnect(); + } + +} // namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h new file mode 100644 index 0000000000..1db8725a9e --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h @@ -0,0 +1,42 @@ +// {BEGIN_LICENSE} +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + // {END_LICENSE} + +#pragma once +#include +#include <${Name}/${Name}Bus.h> + +#include + +namespace ${SanitizedCppName} +{ + /// System component for ${SanitizedCppName} editor + class ${SanitizedCppName}EditorSystemComponent + : public ${SanitizedCppName}RequestBus::Handler + , private AzToolsFramework::EditorEvents::Bus::Handler + , public AZ::Component + { + public: + AZ_COMPONENT(${SanitizedCppName}EditorSystemComponent, "${EditorSysCompClassId}"); + static void Reflect(AZ::ReflectContext* context); + + ${SanitizedCppName}EditorSystemComponent(); + ~${SanitizedCppName}EditorSystemComponent(); + + private: + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + // AZ::Component + void Activate(); + void Deactivate(); + }; +} // namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h b/Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h new file mode 100644 index 0000000000..4ddfc9c007 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h @@ -0,0 +1,36 @@ +// {BEGIN_LICENSE} +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +// {END_LICENSE} + +#include +#include + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}ModuleInterface + : public AZ::Module + { + public: + AZ_RTTI(${SanitizedCppName}ModuleInterface, "{${Random_Uuid}}", AZ::Module); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}ModuleInterface, AZ::SystemAllocator, 0); + + ${SanitizedCppName}ModuleInterface() + { + } + + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + }; + } + }; +}// namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp b/Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp new file mode 100644 index 0000000000..9b84575fa0 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp @@ -0,0 +1,13 @@ +// {BEGIN_LICENSE} +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +// {END_LICENSE} + +#include + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py b/Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py new file mode 100644 index 0000000000..39515711ae --- /dev/null +++ b/Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py @@ -0,0 +1,46 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- +"""${SanitizedCppName}\\editor\\scripts\\${SanitizedCppName}_dialog.py +Generated from O3DE PythonGem Template""" + +import azlmbr +from shiboken2 import wrapInstance, getCppPointer +from PySide2 import QtCore, QtWidgets, QtGui +from PySide2.QtCore import QEvent, Qt +from PySide2.QtWidgets import QVBoxLayout, QAction, QDialog, QHeaderView, QLabel, QLineEdit, QPushButton, QSplitter, QTreeWidget, QTreeWidgetItem, QWidget, QAbstractButton + +# Once PySide2 has been bootstrapped, register our ${SanitizedCppName}Dialog with the Editor + +class ${SanitizedCppName}Dialog(QDialog): + def __init__(self, parent=None): + super(${SanitizedCppName}Dialog, self).__init__(parent) + + self.setObjectName("${SanitizedCppName}Dialog") + + self.setWindowTitle("HelloWorld, ${SanitizedCppName} Dialog") + + self.mainLayout = QVBoxLayout(self) + + self.introLabel = QLabel("Put your cool stuff here!") + + self.mainLayout.addWidget(self.introLabel, 0, Qt.AlignCenter) + + self.helpText = str("For help getting started," + "visit the UI Development documentation
" + "or come ask a question in the sig-ui-ux channel on Discord") + + self.helpLabel = QLabel() + self.helpLabel.setTextFormat(Qt.RichText) + self.helpLabel.setText(self.helpText) + self.helpLabel.setOpenExternalLinks(True) + + self.mainLayout.addWidget(self.helpLabel, 0, Qt.AlignCenter) + + self.setLayout(self.mainLayout) + + return \ No newline at end of file diff --git a/Templates/PythonGem/Template/Editor/Scripts/__init__.py b/Templates/PythonGem/Template/Editor/Scripts/__init__.py new file mode 100644 index 0000000000..b5da0c7ff0 --- /dev/null +++ b/Templates/PythonGem/Template/Editor/Scripts/__init__.py @@ -0,0 +1,9 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- + +__ALL__ = ['bootstrap','${NameLower}_dialog'] \ No newline at end of file diff --git a/Templates/PythonGem/Template/Editor/Scripts/bootstrap.py b/Templates/PythonGem/Template/Editor/Scripts/bootstrap.py new file mode 100644 index 0000000000..060116d36c --- /dev/null +++ b/Templates/PythonGem/Template/Editor/Scripts/bootstrap.py @@ -0,0 +1,117 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" +# ------------------------------------------------------------------------- +"""${SanitizedCppName}\\editor\\scripts\\boostrap.py +Generated from O3DE PythonGem Template""" + +import azlmbr +import az_qt_helpers +from PySide2 import QtCore, QtWidgets, QtGui +from PySide2.QtCore import QEvent, Qt +from PySide2.QtWidgets import QMainWindow, QAction, QDialog, QHeaderView, QLabel, QLineEdit, QPushButton, QSplitter, QTreeWidget, QTreeWidgetItem, QWidget, QAbstractButton +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +class SampleUI(QtWidgets.QDialog): + """Lightweight UI Test Class created a button""" + def __init__(self, parent, title='Not Set'): + super(SampleUI, self).__init__(parent) + self.setWindowTitle(title) + self.initUI() + + def initUI(self): + mainLayout = QtWidgets.QHBoxLayout() + testBtn = QtWidgets.QPushButton("I am just a Button man!") + mainLayout.addWidget(testBtn) + self.setLayout(mainLayout) +# ------------------------------------------------------------------------- + +if __name__ == "__main__": + print("${SanitizedCppName}.boostrap, Generated from O3DE PythonGem Template") + + # --------------------------------------------------------------------- + # validate pyside before continuing + try: + azlmbr.qt.QtForPythonRequestBus(azlmbr.bus.Broadcast, 'IsActive') + params = azlmbr.qt.QtForPythonRequestBus(azlmbr.bus.Broadcast, 'GetQtBootstrapParameters') + params is not None and params.mainWindowId is not 0 + from PySide2 import QtWidgets + except Exception as e: + _LOGGER.error(f'Pyside not available, exception: {e}') + raise e + + # keep going, import the other PySide2 bits we will use + from PySide2 import QtGui + from PySide2.QtCore import Slot + from shiboken2 import wrapInstance, getCppPointer + + # Get our Editor main window + _widget_main_window = None + try: + _widget_main_window = az_qt_helpers.get_editor_main_window() + except: + pass # may be booting in the AP? + # --------------------------------------------------------------------- + + + # --------------------------------------------------------------------- + if _widget_main_window: + # creat a custom menu + _tag_str = '${SanitizedCppName}' + + # create our own menuBar + ${SanitizedCppName}_menu = _widget_main_window.menuBar().addMenu(f"&{_tag_str}") + + # nest a menu for util/tool launching + ${SanitizedCppName}_launch_menu = ${SanitizedCppName}_menu.addMenu("examples") + else: + print('No O3DE MainWindow') + # --------------------------------------------------------------------- + + + # --------------------------------------------------------------------- + if _widget_main_window: + # (1) add the first SampleUI + action_launch_sample_ui = ${SanitizedCppName}_launch_menu.addAction("O3DE:SampleUI") + + @Slot() + def clicked_sample_ui(): + while 1: # simple PySide2 test, set to 0 to disable + ui = SampleUI(parent=_widget_main_window, title='O3DE:SampleUI') + ui.show() + break + return + # Add click event to menu bar + action_launch_sample_ui.triggered.connect(clicked_sample_ui) + # --------------------------------------------------------------------- + + + # --------------------------------------------------------------------- + if _widget_main_window: + # (1) and custom external module Qwidget + action_launch_${SanitizedCppName}_dialog = ${SanitizedCppName}_launch_menu.addAction("O3DE:${SanitizedCppName}_dialog") + + @Slot() + def clicked_${SanitizedCppName}_dialog(): + while 1: # simple PySide2 test, set to 0 to disable + try: + import az_qt_helpers + from ${NameLower}_dialog import ${SanitizedCppName}Dialog + az_qt_helpers.register_view_pane('${SanitizedCppName} Popup', ${SanitizedCppName}Dialog) + except Exception as e: + print(f'Error: {e}') + print('Skipping register our ${SanitizedCppName}Dialog with the Editor.') + ${SanitizedCppName}_dialog = ${SanitizedCppName}Dialog(parent=_widget_main_window) + ${SanitizedCppName}_dialog.show() + break + return + # Add click event to menu bar + action_launch_${SanitizedCppName}_dialog.triggered.connect(clicked_${SanitizedCppName}_dialog) + # --------------------------------------------------------------------- + + # end \ No newline at end of file diff --git a/Templates/PythonGem/Template/gem.json b/Templates/PythonGem/Template/gem.json new file mode 100644 index 0000000000..353ad6bf8d --- /dev/null +++ b/Templates/PythonGem/Template/gem.json @@ -0,0 +1,16 @@ +{ + "gem_name": "${Name}", + "display_name": "${Name}", + "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", + "type": "Code", + "summary": "A short description of ${Name}.", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "${Name}" + ], + "icon_path": "preview.png", + "requirements": "" +} diff --git a/Templates/PythonGem/Template/preview.png b/Templates/PythonGem/Template/preview.png new file mode 100644 index 0000000000..0f393ac886 --- /dev/null +++ b/Templates/PythonGem/Template/preview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ac9dd09bde78f389e3725ac49d61eff109857e004840bc0bc3881739df9618d +size 2217 diff --git a/Templates/PythonGem/template.json b/Templates/PythonGem/template.json new file mode 100644 index 0000000000..75be757abb --- /dev/null +++ b/Templates/PythonGem/template.json @@ -0,0 +1,216 @@ +{ + "template_name": "PythonGem", + "restricted_name": "o3de", + "restricted_platform_relative_path": "Templates", + "origin": "The primary repo for PythonGem goes here: i.e. http://www.mydomain.com", + "license": "What license PythonGem uses goes here: i.e. https://opensource.org/licenses/MIT", + "display_name": "PythonGem", + "summary": "A short description of PythonGem.", + "canonical_tags": [], + "user_tags": [ + "PythonGem" + ], + "icon_path": "preview.png", + "copyFiles": [ + { + "file": "CMakeLists.txt", + "origin": "CMakeLists.txt", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_files.cmake", + "origin": "Code/${NameLower}_editor_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_shared_files.cmake", + "origin": "Code/${NameLower}_editor_shared_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_tests_files.cmake", + "origin": "Code/${NameLower}_editor_tests_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/CMakeLists.txt", + "origin": "Code/CMakeLists.txt", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Include/${Name}/${Name}Bus.h", + "origin": "Code/Include/${Name}/${Name}Bus.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/PAL_linux.cmake", + "origin": "Code/Platform/Linux/PAL_linux.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/PAL_mac.cmake", + "origin": "Code/Platform/Mac/PAL_mac.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/PAL_windows.cmake", + "origin": "Code/Platform/Windows/PAL_windows.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorModule.cpp", + "origin": "Code/Source/${Name}EditorModule.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.cpp", + "origin": "Code/Source/${Name}EditorSystemComponent.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.h", + "origin": "Code/Source/${Name}EditorSystemComponent.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}ModuleInterface.h", + "origin": "Code/Source/${Name}ModuleInterface.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Tests/${Name}EditorTest.cpp", + "origin": "Code/Tests/${Name}EditorTest.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Editor/Scripts/__init__.py", + "origin": "Editor/Scripts/__init__.py", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Editor/Scripts/bootstrap.py", + "origin": "Editor/Scripts/bootstrap.py", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Editor/Scripts/${NameLower}_dialog.py", + "origin": "Editor/Scripts/${NameLower}_dialog.py", + "isTemplated": true, + "isOptional": false + }, + { + "file": "gem.json", + "origin": "gem.json", + "isTemplated": true, + "isOptional": false + }, + { + "file": "preview.png", + "origin": "preview.png", + "isTemplated": false, + "isOptional": false + } + ], + "createDirectories": [ + { + "dir": "Assets", + "origin": "Assets" + }, + { + "dir": "Code", + "origin": "Code" + }, + { + "dir": "Editor", + "origin": "Editor" + }, + { + "dir": "Editor/Scripts", + "origin": "Editor/Scripts" + }, + { + "dir": "Code/Include", + "origin": "Code/Include" + }, + { + "dir": "Code/Include/${Name}", + "origin": "Code/Include/${Name}" + }, + { + "dir": "Code/Platform", + "origin": "Code/Platform" + }, + { + "dir": "Code/Platform/Linux", + "origin": "Code/Platform/Linux" + }, + { + "dir": "Code/Platform/Mac", + "origin": "Code/Platform/Mac" + }, + { + "dir": "Code/Platform/Windows", + "origin": "Code/Platform/Windows" + }, + { + "dir": "Code/Source", + "origin": "Code/Source" + }, + { + "dir": "Code/Tests", + "origin": "Code/Tests" + } + ] +} From 97920feaf16ec8dfa3d2cd911555a914c67b0c80 Mon Sep 17 00:00:00 2001 From: Ken Pruiksma Date: Wed, 27 Oct 2021 10:12:13 -0500 Subject: [PATCH 064/120] Detail material Id texture created from surface weights. (#4984) * Added some structs for detail materials Signed-off-by: Ken Pruiksma * Added some template functions for looking up materials. Added lookups for all the relevant detail material fields in StandardPBR. Signed-off-by: Ken Pruiksma * Added some structs for detail materials Signed-off-by: Ken Pruiksma * Added some template functions for looking up materials. Added lookups for all the relevant detail material fields in StandardPBR. Signed-off-by: Ken Pruiksma * Added support for generating a detail material texture with IDs populated from surface weights. Signed-off-by: Ken Pruiksma * Updated TerrainAreaMaterailRequestBus to have separate calls for region vs materials instead of the awkward out parameter Update MaterialPropertyDescriptor so that you can retrieve enum names by ID Several bug fixes / updates to the terrain feature processor dealing with detail materials. Signed-off-by: Ken Pruiksma * Updating detail material texture based on offsets. Not quite working yet but close. Added visualization for detail material in shader (currently on, will be turned off before final commit) Signed-off-by: Ken Pruiksma * Small bugfixes * Fix compile error in non-unity builds * Fixed backwards x/y loops causing the wrong pixels to update * Fixed selection of surface type with multiple surface weights Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Adding seam to detail texture debug display. Offseting edges by a half-pixel to avoid bleed. Disabling debugging detail textures by default. Signed-off-by: Ken Pruiksma * Missing file from last commit for detail material change. Signed-off-by: Ken Pruiksma * Cleanups Signed-off-by: Ken Pruiksma * bug fix Signed-off-by: Ken Pruiksma * Bug fix in the terrain fp for TerrainAreaMaterialRequestBus returning incomplete materials on GetSurfaceMaterialMappings Signed-off-by: Ken Pruiksma * Some PR updates. Exposing detail material id debugging through a cvar. Signed-off-by: Ken Pruiksma * Various updates from review. Signed-off-by: Ken Pruiksma * PR updates dealing with debug texture boundary line. Signed-off-by: Ken Pruiksma * Hiding some fields from the terrain material Signed-off-by: Ken Pruiksma * Fixing type in generic lambda for linux / android Signed-off-by: Ken Pruiksma Co-authored-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../Material/MaterialPropertyDescriptor.h | 3 + .../Material/MaterialPropertyDescriptor.cpp | 10 + .../Materials/Terrain/PbrTerrain.materialtype | 47 +- .../Shaders/Terrain/TerrainCommon.azsli | 14 + .../Terrain/TerrainPBR_ForwardPass.azsl | 45 ++ .../TerrainSurfaceMaterialsListComponent.cpp | 19 +- .../TerrainSurfaceMaterialsListComponent.h | 5 +- .../TerrainAreaMaterialRequestBus.h | 9 +- .../TerrainFeatureProcessor.cpp | 734 +++++++++++++++++- .../TerrainRenderer/TerrainFeatureProcessor.h | 155 +++- 10 files changed, 992 insertions(+), 49 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h index 085256f6d8..76bb2a6113 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h @@ -106,6 +106,9 @@ namespace AZ static constexpr uint32_t InvalidEnumValue = std::numeric_limits::max(); uint32_t GetEnumValue(const AZ::Name& enumName) const; + //! Returns the name of the enum from its index. An empty name is returned for an invalid id. + const AZ::Name& GetEnumName(uint32_t enumValue) const; + //! Returns the unique name ID of this property const Name& GetName() const; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp index 700ac41003..5d0a88a6d3 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp @@ -203,6 +203,16 @@ namespace AZ return InvalidEnumValue; } + + const AZ::Name& MaterialPropertyDescriptor::GetEnumName(uint32_t enumValue) const + { + if (enumValue < m_enumNames.size()) + { + return m_enumNames.at(enumValue); + } + static AZ::Name EmptyName = AZ::Name(); + return EmptyName; + } } // namespace RPI } // namespace AZ diff --git a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype index 01b862c4f8..235079d95e 100644 --- a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype +++ b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype @@ -92,13 +92,58 @@ { "id": "heightmapImage", "displayName": "Heightmap Image", - "description": "Heightmap of the terrain, controlled by the runtime.", + "description": "Heightmap of the terrain. Controlled by the runtime.", + "visibility": "Hidden", "type": "Image", "connection": { "type": "ShaderInput", "id": "m_heightmapImage" } }, + { + "id": "detailMaterialIdImage", + "displayName": "Detail Material Id Image", + "description": "Texture containing detail material Ids and weights. Controlled by the runtime.", + "visibility": "Hidden", + "type": "Image", + "connection": { + "type": "ShaderInput", + "id": "m_detailMaterialIdImage" + } + }, + { + "id": "detailMaterialIdCenter", + "displayName": "Detail Material Id Image Center", + "description": "The center position of the detail material Id image. Controlled by the runtime.", + "visibility": "Hidden", + "type": "Vector2", + "connection": { + "type": "ShaderInput", + "id": "m_detailMaterialIdImageCenter" + } + }, + { + "id": "detailAabb", + "displayName": "Detail material bounds in 2d", + "description": "The 2d world space bounds of the detail id material. Controlled by the runtime.", + "visibility": "Hidden", + "type": "Vector4", + "connection": { + "type": "ShaderInput", + "id": "m_detailAabb" + } + }, + { + "id": "detailHalfPixelUv", + "displayName": "Detail texture half pixel uv size", + "description": "Uv size of a half pixel in the detail material id texture. Controlled by the runtime.", + "visibility": "Hidden", + "type": "float", + "connection": { + "type": "ShaderInput", + "id": "m_detailHalfPixelUv" + } + }, { "id": "detailTextureMultiplier", "displayName": "Detail Texture UV Multiplier", diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli index 72c1af953c..dc6f65207b 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli @@ -93,10 +93,15 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial { Texture2D m_heightmapImage; + Texture2D m_detailMaterialIdImage; + float2 m_detailMaterialIdImageCenter; float m_detailTextureMultiplier; float m_detailFadeDistance; float m_detailFadeLength; + float4 m_detailAabb; + float m_detailHalfPixelUv; + Sampler HeightmapSampler { MinFilter = Linear; @@ -117,6 +122,15 @@ ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial MaxAnisotropy = 16; }; + Sampler m_detailSampler + { + AddressU = Wrap; + AddressV = Wrap; + MinFilter = Point; + MagFilter = Point; + MipFilter = Point; + }; + // Base Color float3 m_baseColor; float m_baseColorFactor; diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl index 750cd2fb29..a15ad931f6 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl @@ -17,6 +17,7 @@ #include #include #include +#include struct VSOutput { @@ -29,6 +30,8 @@ struct VSOutput float2 m_uv : UV1; }; +option bool o_debugDetailMaterialIds = false; + VSOutput TerrainPBR_MainPassVS(VertexInput IN) { VSOutput OUT; @@ -121,6 +124,48 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN) float3 detailColor = GetBaseColorInput(TerrainMaterialSrg::m_baseColorMap, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_baseColor.rgb, o_baseColor_useTexture); float3 blendedColor = BlendBaseColor(lerp(detailColor, TerrainMaterialSrg::m_baseColor.rgb, detailFactor), macroColor, TerrainMaterialSrg::m_baseColorFactor, o_baseColorTextureBlendMode, o_baseColor_useTexture); + // ------- Debug detail materials using random colors ------- + // This assigns a random color to each material, turns off any kind of distance fading, and draws a black line at the texture edges. + if (o_debugDetailMaterialIds) + { + float2 detailRegionMin = TerrainMaterialSrg::m_detailAabb.xy; + float2 detailRegionMax = TerrainMaterialSrg::m_detailAabb.zw; + float2 detailRegionUv = (surface.position.xy - detailRegionMin) / (detailRegionMax - detailRegionMin); + if (all(detailRegionUv > TerrainMaterialSrg::m_detailHalfPixelUv) && all(detailRegionUv < 1.0 - TerrainMaterialSrg::m_detailHalfPixelUv)) + { + detailRegionUv += TerrainMaterialSrg::m_detailMaterialIdImageCenter - (0.5); + + uint material1 = TerrainMaterialSrg::m_detailMaterialIdImage.GatherRed(TerrainMaterialSrg::m_detailSampler, detailRegionUv, 0).r; + uint material2 = TerrainMaterialSrg::m_detailMaterialIdImage.GatherGreen(TerrainMaterialSrg::m_detailSampler, detailRegionUv, 0).r; + float blend = float(TerrainMaterialSrg::m_detailMaterialIdImage.GatherBlue(TerrainMaterialSrg::m_detailSampler, detailRegionUv, 0).r) / 0xFF; + + float3 material1Color = float3(0.1, 0.1, 0.1); + float3 material2Color = float3(0.1, 0.1, 0.1); + + // Get a reasonably random hue for the material id + if (material1 != 255) + { + float hue1 = (material1 * 25043 % 256) / 256.0; + material1Color = HsvToRgb(float3(hue1, 1.0, 1.0)); + } + if (material2 != 255) + { + float hue2 = (material2 * 25043 % 256) / 256.0; + material2Color = HsvToRgb(float3(hue2, 1.0, 1.0)); + } + + blendedColor = lerp(material1Color, material2Color, blend); + float seamBlend = 0.0; + const float halfLineWidth = 1.0 / 2048.0; + if (any(abs(detailRegionUv) % 1.0 < halfLineWidth) || any(abs(detailRegionUv) % 1.0 > 1.0 - halfLineWidth)) + { + seamBlend = 1.0; + } + blendedColor = lerp(blendedColor, float3(0.0, 0.0, 0.0), seamBlend); // draw texture seams + blendedColor = pow(blendedColor , 2.2); + } + } + // ------- Specular ------- float specularF0Factor = GetSpecularInput(TerrainMaterialSrg::m_specularF0Map, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_specularF0Factor, o_specularF0_useTexture); specularF0Factor = lerp(specularF0Factor, 0.5, detailFactor); diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp index 271919070c..3c6a213b02 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp @@ -123,20 +123,23 @@ namespace Terrain { surfaceMaterialMapping.m_active = false; surfaceMaterialMapping.m_materialAsset.QueueLoad(); - AZ::Data::AssetBus::Handler::BusConnect(surfaceMaterialMapping.m_materialAsset.GetId()); + AZ::Data::AssetBus::MultiHandler::BusConnect(surfaceMaterialMapping.m_materialAsset.GetId()); } } + + // Announce initial shape using OnShapeChanged + OnShapeChanged(LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged); } void TerrainSurfaceMaterialsListComponent::Deactivate() { TerrainAreaMaterialRequestBus::Handler::BusDisconnect(); + AZ::Data::AssetBus::MultiHandler::BusDisconnect(); for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials) { if (surfaceMaterialMapping.m_materialAsset.GetId().IsValid()) { - AZ::Data::AssetBus::Handler::BusDisconnect(surfaceMaterialMapping.m_materialAsset.GetId()); surfaceMaterialMapping.m_materialAsset.Release(); surfaceMaterialMapping.m_materialInstance.reset(); surfaceMaterialMapping.m_activeMaterialAssetId = AZ::Data::AssetId(); @@ -202,7 +205,7 @@ namespace Terrain // Don't disconnect from the AssetBus if this material is mapped more than once. if (CountMaterialIDInstances(surfaceMaterialMapping.m_activeMaterialAssetId) == 1) { - AZ::Data::AssetBus::Handler::BusDisconnect(surfaceMaterialMapping.m_activeMaterialAssetId); + AZ::Data::AssetBus::MultiHandler::BusDisconnect(surfaceMaterialMapping.m_activeMaterialAssetId); } surfaceMaterialMapping.m_activeMaterialAssetId = AZ::Data::AssetId(); @@ -273,12 +276,14 @@ namespace Terrain &TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingRegionChanged, GetEntityId(), oldAabb, m_cachedAabb); } - - const AZStd::vector& TerrainSurfaceMaterialsListComponent::GetSurfaceMaterialMappings( - AZ::Aabb& region) const + + const AZ::Aabb& TerrainSurfaceMaterialsListComponent::GetTerrainSurfaceMaterialRegion() const { - region = m_cachedAabb; + return m_cachedAabb; + } + const AZStd::vector& TerrainSurfaceMaterialsListComponent::GetSurfaceMaterialMappings() const + { return m_configuration.m_surfaceMaterials; } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h index 7c36033c41..0e32cb12c0 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h @@ -53,7 +53,7 @@ namespace Terrain class TerrainSurfaceMaterialsListComponent : public AZ::Component , private TerrainAreaMaterialRequestBus::Handler - , private AZ::Data::AssetBus::Handler + , private AZ::Data::AssetBus::MultiHandler , private LmbrCentral::ShapeComponentNotificationsBus::Handler { public: @@ -86,7 +86,8 @@ namespace Terrain ////////////////////////////////////////////////////////////////////////// // TerrainAreaMaterialRequestBus - const AZStd::vector& GetSurfaceMaterialMappings(AZ::Aabb& region) const override; + const AZ::Aabb& GetTerrainSurfaceMaterialRegion() const override; + const AZStd::vector& GetSurfaceMaterialMappings() const override; ////////////////////////////////////////////////////////////////////////// // AZ::Data::AssetBus::Handler diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainAreaMaterialRequestBus.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainAreaMaterialRequestBus.h index dfedf0b34a..ae36a2639e 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainAreaMaterialRequestBus.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainAreaMaterialRequestBus.h @@ -9,13 +9,9 @@ #pragma once #include - #include - #include -#include - namespace Terrain { //! This bus provides retrieval of information from Terrain Surfaces. @@ -30,8 +26,11 @@ namespace Terrain virtual ~TerrainAreaMaterialRequests() = default; + //! Get the Aabb for the region where a TerrainSurfaceMaterialMapping exists + virtual const AZ::Aabb& GetTerrainSurfaceMaterialRegion() const = 0; + //! Get the Material asset assigned to a particular surface tag. - virtual const AZStd::vector& GetSurfaceMaterialMappings(AZ::Aabb& region) const = 0; + virtual const AZStd::vector& GetSurfaceMaterialMappings() const = 0; }; using TerrainAreaMaterialRequestBus = AZ::EBus; diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index e6aed28897..571d109fd8 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -7,11 +7,13 @@ */ #include +#include +#include +#include #include #include #include -#include #include @@ -45,12 +47,49 @@ namespace Terrain { [[maybe_unused]] const char* TerrainFPName = "TerrainFeatureProcessor"; const char* TerrainHeightmapChars = "TerrainHeightmap"; + const char* TerrainDetailChars = "TerrainDetail"; } namespace MaterialInputs { // Terrain material static const char* const HeightmapImage("settings.heightmapImage"); + static const char* const DetailMaterialIdImage("settings.detailMaterialIdImage"); + static const char* const DetailCenter("settings.detailMaterialIdCenter"); + static const char* const DetailAabb("settings.detailAabb"); + static const char* const DetailHalfPixelUv("settings.detailHalfPixelUv"); + } + + namespace DetailMaterialInputs + { + static const char* const BaseColorMap("baseColor.textureMap"); + static const char* const BaseColorUseTexture("baseColor.useTexture"); + static const char* const BaseColorFactor("baseColor.factor"); + static const char* const BaseColorBlendMode("baseColor.textureBlendMode"); + static const char* const MetallicMap("metallic.textureMap"); + static const char* const MetallicUseTexture("metallic.useTexture"); + static const char* const MetallicFactor("metallic.factor"); + static const char* const RoughnessMap("roughness.textureMap"); + static const char* const RoughnessUseTexture("roughness.useTexture"); + static const char* const RoughnessFactor("roughness.factor"); + static const char* const RoughnessUpperBound("roughness.lowerBound"); + static const char* const RoughnessLowerBound("roughness.upperBound"); + static const char* const SpecularF0Map("specularF0.textureMap"); + static const char* const SpecularF0UseTexture("specularF0.useTexture"); + static const char* const SpecularF0Factor("specularF0.factor"); + static const char* const NormalMap("normal.textureMap"); + static const char* const NormalUseTexture("normal.useTexture"); + static const char* const NormalFactor("normal.factor"); + static const char* const NormalFlipX("normal.flipX"); + static const char* const NormalFlipY("normal.flipY"); + static const char* const DiffuseOcclusionMap("occlusion.diffuseTextureMap"); + static const char* const DiffuseOcclusionUseTexture("occlusion.diffuseUseTexture"); + static const char* const DiffuseOcclusionFactor("occlusion.diffuseFactor"); + static const char* const HeightMap("parallax.textureMap"); + static const char* const HeightUseTexture("parallax.useTexture"); + static const char* const HeightFactor("parallax.factor"); + static const char* const HeightOffset("parallax.offset"); + static const char* const HeightBlendFactor("parallax.blendFactor"); } namespace ShaderInputs @@ -62,6 +101,17 @@ namespace Terrain static const char* const MacroColorMap("m_macroColorMap"); static const char* const MacroNormalMap("m_macroNormalMap"); } + + AZ_CVAR(bool, + r_terrainDebugDetailMaterials, + false, + [](const bool& value) + { + AZ::RPI::ShaderSystemInterface::Get()->SetGlobalShaderOption(AZ::Name{ "o_debugDetailMaterialIds" }, AZ::RPI::ShaderOptionValue{ value }); + }, + AZ::ConsoleFunctorFlags::Null, + "Turns on debugging for detail material ids for terrain." + ); void TerrainFeatureProcessor::Reflect(AZ::ReflectContext* context) @@ -78,6 +128,12 @@ namespace Terrain { Initialize(); AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); + + m_handleGlobalShaderOptionUpdate = AZ::RPI::ShaderSystemInterface::GlobalShaderOptionUpdatedEvent::Handler + { + [this](const AZ::Name&, AZ::RPI::ShaderOptionValue) { m_forceRebuildDrawPackets = true; } + }; + AZ::RPI::ShaderSystemInterface::Get()->Connect(m_handleGlobalShaderOptionUpdate); } void TerrainFeatureProcessor::Initialize() @@ -139,11 +195,18 @@ namespace Terrain void TerrainFeatureProcessor::OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) { - if ((dataChangedMask & (TerrainDataChangedMask::HeightData | TerrainDataChangedMask::Settings)) == 0) + if ((dataChangedMask & (TerrainDataChangedMask::HeightData | TerrainDataChangedMask::Settings)) != 0) { - return; + TerrainHeightOrSettingsUpdated(dirtyRegion); } + if ((dataChangedMask & TerrainDataChangedMask::SurfaceData) != 0) + { + TerrainSurfaceDataUpdated(dirtyRegion); + } + } + void TerrainFeatureProcessor::TerrainHeightOrSettingsUpdated(const AZ::Aabb& dirtyRegion) + { AZ::Aabb worldBounds = AZ::Aabb::CreateNull(); AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( worldBounds, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb); @@ -177,10 +240,15 @@ namespace Terrain m_areaData.m_sampleSpacing = queryResolution.GetX(); m_areaData.m_heightmapUpdated = true; } - + + void TerrainFeatureProcessor::TerrainSurfaceDataUpdated(const AZ::Aabb& dirtyRegion) + { + m_dirtyDetailRegion.AddAabb(dirtyRegion); + } + void TerrainFeatureProcessor::OnTerrainMacroMaterialCreated(AZ::EntityId entityId, const MacroMaterialData& newMaterialData) { - MacroMaterialData& materialData = FindOrCreateMacroMaterial(entityId); + MacroMaterialData& materialData = FindOrCreateByEntityId(entityId, m_macroMaterials); UpdateMacroMaterialData(materialData, newMaterialData); @@ -197,14 +265,14 @@ namespace Terrain void TerrainFeatureProcessor::OnTerrainMacroMaterialChanged(AZ::EntityId entityId, const MacroMaterialData& newMaterialData) { - MacroMaterialData& data = FindOrCreateMacroMaterial(entityId); + MacroMaterialData& data = FindOrCreateByEntityId(entityId, m_macroMaterials); UpdateMacroMaterialData(data, newMaterialData); } void TerrainFeatureProcessor::OnTerrainMacroMaterialRegionChanged( AZ::EntityId entityId, [[maybe_unused]] const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) { - MacroMaterialData& materialData = FindOrCreateMacroMaterial(entityId); + MacroMaterialData& materialData = FindOrCreateByEntityId(entityId, m_macroMaterials); for (SectorData& sectorData : m_sectorData) { bool overlapsOld = sectorData.m_aabb.Overlaps(materialData.m_bounds); @@ -236,7 +304,7 @@ namespace Terrain void TerrainFeatureProcessor::OnTerrainMacroMaterialDestroyed(AZ::EntityId entityId) { - MacroMaterialData* materialData = FindMacroMaterial(entityId); + const MacroMaterialData* materialData = FindByEntityId(entityId, m_macroMaterials); if (materialData) { @@ -255,13 +323,461 @@ namespace Terrain } m_areaData.m_macroMaterialsUpdated = true; - RemoveMacroMaterial(entityId); + RemoveByEntityId(entityId, m_macroMaterials); + } + + void TerrainFeatureProcessor::OnTerrainSurfaceMaterialMappingCreated(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) + { + DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); + + // Validate that the surface tag is new + for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces) + { + if (surface.m_surfaceTag == surfaceTag) + { + AZ_Error(TerrainFPName, false, "Already have a surface material mapping for this surface tag."); + return; + } + } + + uint16_t detailMaterialId = CreateOrUpdateDetailMaterial(material); + materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, detailMaterialId }); + m_dirtyDetailRegion.AddAabb(materialRegion.m_region); + } + + void TerrainFeatureProcessor::OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag) + { + DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); + + for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces) + { + if (surface.m_surfaceTag == surfaceTag) + { + if (surface.m_surfaceTag != materialRegion.m_materialsForSurfaces.back().m_surfaceTag) + { + AZStd::swap(surface, materialRegion.m_materialsForSurfaces.back()); + } + materialRegion.m_materialsForSurfaces.pop_back(); + m_dirtyDetailRegion.AddAabb(materialRegion.m_region); + return; + } + } + AZ_Error(TerrainFPName, false, "Could not find surface tag to destroy for OnTerrainSurfaceMaterialMappingDestroyed()."); + } + + void TerrainFeatureProcessor::OnTerrainSurfaceMaterialMappingChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) + { + DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); + + bool found = false; + uint16_t materialId = CreateOrUpdateDetailMaterial(material); + for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces) + { + if (surface.m_surfaceTag == surfaceTag) + { + found = true; + surface.m_detailMaterialId = materialId; + break; + } + } + + if (!found) + { + materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, materialId }); + } + m_dirtyDetailRegion.AddAabb(materialRegion.m_region); + } + + void TerrainFeatureProcessor::OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) + { + DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); + materialRegion.m_region = newRegion; + m_dirtyDetailRegion.AddAabb(oldRegion); + m_dirtyDetailRegion.AddAabb(newRegion); + } + + uint16_t TerrainFeatureProcessor::CreateOrUpdateDetailMaterial(MaterialInstance material) + { + static constexpr uint16_t InvalidDetailMaterial = 0xFFFF; + uint16_t detailMaterialId = InvalidDetailMaterial; + + for (DetailMaterialData& detailMaterial : m_detailMaterials.GetDataVector()) + { + if (detailMaterial.m_assetId == material->GetAssetId()) + { + UpdateDetailMaterialData(detailMaterial, material); + detailMaterialId = m_detailMaterials.GetIndexForData(&detailMaterial); + break; + } + } + + if (detailMaterialId == InvalidDetailMaterial) + { + detailMaterialId = m_detailMaterials.GetFreeSlotIndex(); + UpdateDetailMaterialData(m_detailMaterials.GetData(detailMaterialId), material); + } + return detailMaterialId; + } + + void TerrainFeatureProcessor::UpdateDetailMaterialData(DetailMaterialData& materialData, MaterialInstance material) + { + if (materialData.m_materialChangeId != material->GetCurrentChangeId()) + { + materialData = DetailMaterialData(); + DetailTextureFlags& flags = materialData.m_properties.m_flags; + materialData.m_materialChangeId = material->GetCurrentChangeId(); + materialData.m_assetId = material->GetAssetId(); + + auto getIndex = [&](const char* const indexName) -> AZ::RPI::MaterialPropertyIndex + { + const AZ::RPI::MaterialPropertyIndex index = material->FindPropertyIndex(AZ::Name(indexName)); + AZ_Warning(TerrainFPName, index.IsValid(), "Failed to find shader input constant %s.", indexName); + return index; + }; + + auto applyProperty = [&](const char* const indexName, auto& ref) -> void + { + const auto index = getIndex(indexName); + if (index.IsValid()) + { + using TypeRefRemoved = AZStd::remove_cvref_t; + ref = material->GetPropertyValue(index).GetValue(); + } + }; + + auto applyFlag = [&](const char* const indexName, DetailTextureFlags flagToSet) -> void + { + const auto index = getIndex(indexName); + if (index.IsValid()) + { + bool flagValue = material->GetPropertyValue(index).GetValue(); + flags = DetailTextureFlags(flagValue ? flags | flagToSet : flags); + } + }; + + auto getEnumName = [&](const char* const indexName) -> const AZStd::string_view + { + const auto index = getIndex(indexName); + if (index.IsValid()) + { + uint32_t enumIndex = material->GetPropertyValue(index).GetValue(); + const AZ::Name& enumName = material->GetMaterialPropertiesLayout()->GetPropertyDescriptor(index)->GetEnumName(enumIndex); + return enumName.GetStringView(); + } + return ""; + }; + + using namespace DetailMaterialInputs; + applyProperty(BaseColorMap, materialData.m_colorImage); + applyFlag(BaseColorUseTexture, DetailTextureFlags::UseTextureBaseColor); + applyProperty(BaseColorFactor, materialData.m_properties.m_baseColorFactor); + + const AZStd::string_view& blendModeString = getEnumName(BaseColorBlendMode); + if (blendModeString == "Multiply") + { + flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeMultiply); + } + else if (blendModeString == "LinearLight") + { + flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeLinearLight); + } + else if (blendModeString == "Lerp") + { + flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeLerp); + } + else if (blendModeString == "Overlay") + { + flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeOverlay); + } + + applyProperty(MetallicMap, materialData.m_metalnessImage); + applyFlag(MetallicUseTexture, DetailTextureFlags::UseTextureMetallic); + applyProperty(MetallicFactor, materialData.m_properties.m_metalFactor); + + applyProperty(RoughnessMap, materialData.m_roughnessImage); + applyFlag(RoughnessUseTexture, DetailTextureFlags::UseTextureRoughness); + + if ((flags & DetailTextureFlags::UseTextureRoughness) > 0) + { + float lowerBound = 0.0; + float upperBound = 1.0; + applyProperty(RoughnessLowerBound, lowerBound); + applyProperty(RoughnessUpperBound, upperBound); + materialData.m_properties.m_roughnessBias = lowerBound; + materialData.m_properties.m_roughnessScale = upperBound - lowerBound; + } + else + { + materialData.m_properties.m_roughnessBias = 0.0; + applyProperty(RoughnessFactor, materialData.m_properties.m_roughnessScale); + } + + applyProperty(SpecularF0Map, materialData.m_specularF0Image); + applyFlag(SpecularF0UseTexture, DetailTextureFlags::UseTextureSpecularF0); + applyProperty(SpecularF0Factor, materialData.m_properties.m_specularF0Factor); + + applyProperty(NormalMap, materialData.m_normalImage); + applyFlag(NormalUseTexture, DetailTextureFlags::UseTextureNormal); + applyProperty(NormalFactor, materialData.m_properties.m_normalFactor); + applyFlag(NormalFlipX, DetailTextureFlags::FlipNormalX); + applyFlag(NormalFlipY, DetailTextureFlags::FlipNormalY); + + applyProperty(DiffuseOcclusionMap, materialData.m_occlusionImage); + applyFlag(DiffuseOcclusionUseTexture, DetailTextureFlags::UseTextureOcclusion); + applyProperty(DiffuseOcclusionFactor, materialData.m_properties.m_occlusionFactor); + + applyProperty(HeightMap, materialData.m_heightImage); + applyFlag(HeightUseTexture, DetailTextureFlags::UseTextureHeight); + applyProperty(HeightFactor, materialData.m_properties.m_heightFactor); + applyProperty(HeightOffset, materialData.m_properties.m_heightOffset); + applyProperty(HeightBlendFactor, materialData.m_properties.m_heightBlendFactor); + + } + } + + void TerrainFeatureProcessor::CheckUpdateDetailTexture(const Aabb2i& newBounds, const Vector2i& newCenter) + { + if (!m_detailTextureImage) + { + // If the m_detailTextureImage doesn't exist, create it and populate the entire texture + + const AZ::Data::Instance imagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool(); + AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D( + AZ::RHI::ImageBindFlags::ShaderRead, DetailTextureSize, DetailTextureSize, AZ::RHI::Format::R8G8B8A8_UINT + ); + const AZ::Name TerrainDetailName = AZ::Name(TerrainDetailChars); + m_detailTextureImage = AZ::RPI::AttachmentImage::Create(*imagePool.get(), imageDescriptor, TerrainDetailName, nullptr, nullptr); + AZ_Error(TerrainFPName, m_detailTextureImage, "Failed to initialize the detail texture image."); + + UpdateDetailTexture(newBounds, newBounds, newCenter); + } + else + { + // If the new bounds of the detail texture are different than the old bounds, then the edges of the texture need to be updated. + + int32_t offsetX = m_detailTextureBounds.m_min.m_x - newBounds.m_min.m_x; + + // Horizontal edge update + if (newBounds.m_min.m_x != m_detailTextureBounds.m_min.m_x) + { + Aabb2i updateBounds; + if (newBounds.m_min.m_x < m_detailTextureBounds.m_min.m_x) + { + updateBounds.m_min.m_x = newBounds.m_min.m_x; + updateBounds.m_max.m_x = m_detailTextureBounds.m_min.m_x; + } + else + { + updateBounds.m_min.m_x = m_detailTextureBounds.m_max.m_x; + updateBounds.m_max.m_x = newBounds.m_max.m_x; + } + updateBounds.m_min.m_y = newBounds.m_min.m_y; + updateBounds.m_max.m_y = newBounds.m_max.m_y; + UpdateDetailTexture(updateBounds, newBounds, newCenter); + } + + // Vertical edge update + if (newBounds.m_min.m_y != m_detailTextureBounds.m_min.m_y) + { + Aabb2i updateBounds; + // Don't update areas that have already been updated in the horizontal update. + updateBounds.m_min.m_x = newBounds.m_min.m_x + AZ::GetMax(0, offsetX); + updateBounds.m_max.m_x = newBounds.m_max.m_x + AZ::GetMin(0, offsetX); + if (newBounds.m_min.m_y < m_detailTextureBounds.m_min.m_y) + { + updateBounds.m_min.m_y = newBounds.m_min.m_y; + updateBounds.m_max.m_y = m_detailTextureBounds.m_min.m_y; + } + else + { + updateBounds.m_min.m_y = m_detailTextureBounds.m_max.m_y; + updateBounds.m_max.m_y = newBounds.m_max.m_y; + } + UpdateDetailTexture(updateBounds, newBounds, newCenter); + } + + if (m_dirtyDetailRegion.IsValid()) + { + // If any regions are marked as dirty, then they should be updated. + + AZ::Vector3 currentMin = AZ::Vector3(newBounds.m_min.m_x * DetailTextureScale, newBounds.m_min.m_y * DetailTextureScale, -0.5f); + AZ::Vector3 currentMax = AZ::Vector3(newBounds.m_max.m_x * DetailTextureScale, newBounds.m_max.m_y * DetailTextureScale, 0.5f); + AZ::Aabb detailTextureCoverage = AZ::Aabb::CreateFromMinMax(currentMin, currentMax); + AZ::Vector3 previousMin = AZ::Vector3(m_detailTextureBounds.m_min.m_x * DetailTextureScale, m_detailTextureBounds.m_min.m_y * DetailTextureScale, -0.5f); + AZ::Vector3 previousMax = AZ::Vector3(m_detailTextureBounds.m_max.m_x * DetailTextureScale, m_detailTextureBounds.m_max.m_y * DetailTextureScale, 0.5f); + AZ::Aabb previousCoverage = AZ::Aabb::CreateFromMinMax(previousMin, previousMax); + + // Area of texture not already updated by camera movement above. + AZ::Aabb clampedCoverage = previousCoverage.GetClamped(detailTextureCoverage); + + // Clamp the dirty region to the area of the detail texture that is visible and not already updated. + clampedCoverage.Clamp(m_dirtyDetailRegion); + + if (clampedCoverage.IsValid()) + { + Aabb2i updateBounds; + updateBounds.m_min.m_x = aznumeric_cast(AZStd::roundf(clampedCoverage.GetMin().GetX() / DetailTextureScale)); + updateBounds.m_min.m_y = aznumeric_cast(AZStd::roundf(clampedCoverage.GetMin().GetY() / DetailTextureScale)); + updateBounds.m_max.m_x = aznumeric_cast(AZStd::roundf(clampedCoverage.GetMax().GetX() / DetailTextureScale)); + updateBounds.m_max.m_y = aznumeric_cast(AZStd::roundf(clampedCoverage.GetMax().GetY() / DetailTextureScale)); + if (updateBounds.m_min.m_x < updateBounds.m_max.m_x && updateBounds.m_min.m_y < updateBounds.m_max.m_y) + { + UpdateDetailTexture(updateBounds, newBounds, newCenter); + } + } + } + } + + } + + uint8_t TerrainFeatureProcessor::CalculateUpdateRegions(const Aabb2i& updateArea, const Aabb2i& textureBounds, const Vector2i& centerPixel, + AZStd::array& textureSpaceAreas, AZStd::array& scaledWorldSpaceAreas) + { + Vector2i centerOffset = { centerPixel.m_x - DetailTextureSizeHalf, centerPixel.m_y - DetailTextureSizeHalf }; + + int32_t quadrantXOffset = centerPixel.m_x < DetailTextureSizeHalf ? DetailTextureSize : -DetailTextureSize; + int32_t quadrantYOffset = centerPixel.m_y < DetailTextureSizeHalf ? DetailTextureSize : -DetailTextureSize; + + uint8_t numQuadrants = 0; + + // For each of the 4 quadrants: + auto calculateQuadrant = [&](Vector2i quadrantOffset) + { + Aabb2i offsetUpdateArea = updateArea + centerOffset + quadrantOffset; + Aabb2i updateSectionBounds = textureBounds.GetClamped(offsetUpdateArea); + if (updateSectionBounds.IsValid()) + { + textureSpaceAreas[numQuadrants] = updateSectionBounds - textureBounds.m_min; + scaledWorldSpaceAreas[numQuadrants] = updateSectionBounds - centerOffset - quadrantOffset; + ++numQuadrants; + } + }; + + calculateQuadrant({ 0, 0 }); + calculateQuadrant({ quadrantXOffset, 0 }); + calculateQuadrant({ 0, quadrantYOffset }); + calculateQuadrant({ quadrantXOffset, quadrantYOffset }); + + return numQuadrants; + } + + void TerrainFeatureProcessor::UpdateDetailTexture(const Aabb2i& updateArea, const Aabb2i& textureBounds, const Vector2i& centerPixel) + { + if (!m_detailTextureImage) + { + return; + } + + struct DetailMaterialPixel + { + uint8_t m_material1{ 255 }; + uint8_t m_material2{ 255 }; + uint8_t m_blend{ 0 }; // 0 = full weight on material1, 255 = full weight on material2 + uint8_t m_padding{ 0 }; + }; + + // Because the center of the detail texture may be offset, each update area may actually need to be split into + // up to 4 separate update areas in each sector of the quadrant. + AZStd::array textureSpaceAreas; + AZStd::array scaledWorldSpaceAreas; + uint8_t updateAreaCount = CalculateUpdateRegions(updateArea, textureBounds, centerPixel, textureSpaceAreas, scaledWorldSpaceAreas); + + // Pull the data for each area updated and use it to construct an update for the detail material id texture. + for (uint8_t i = 0; i < updateAreaCount; ++i) + { + const Aabb2i& quadrantTextureArea = textureSpaceAreas[i]; + const Aabb2i& quadrantWorldArea = scaledWorldSpaceAreas[i]; + + AZStd::vector pixels; + pixels.resize((quadrantWorldArea.m_max.m_x - quadrantWorldArea.m_min.m_x) * (quadrantWorldArea.m_max.m_y - quadrantWorldArea.m_min.m_y)); + uint32_t index = 0; + + for (int yPos = quadrantWorldArea.m_min.m_y; yPos < quadrantWorldArea.m_max.m_y; ++yPos) + { + for (int xPos = quadrantWorldArea.m_min.m_x; xPos < quadrantWorldArea.m_max.m_x; ++xPos) + { + AZ::Vector2 position = AZ::Vector2(xPos * DetailTextureScale, yPos * DetailTextureScale); + AzFramework::SurfaceData::SurfaceTagWeightList surfaceWeights; + AzFramework::Terrain::TerrainDataRequestBus::Broadcast(&AzFramework::Terrain::TerrainDataRequests::GetSurfaceWeightsFromVector2, position, surfaceWeights, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, nullptr); + + // Store the top two surface weights in the texture with m_blend storing the relative weight. + bool isFirstMaterial = true; + float firstWeight = 0.0f; + for (const auto& surfaceTagWeight : surfaceWeights) + { + if (surfaceTagWeight.m_weight > 0.0f) + { + AZ::Crc32 surfaceType = surfaceTagWeight.m_surfaceType; + uint16_t materialId = GetDetailMaterialForSurfaceTypeAndPosition(surfaceType, position); + if (materialId != m_detailMaterials.NoFreeSlot && materialId < 255) + { + if (isFirstMaterial) + { + pixels.at(index).m_material1 = aznumeric_cast(materialId); + firstWeight = surfaceTagWeight.m_weight; + // m_blend only needs to be calculated is material 2 is found, otherwise the initial value of 0 is correct. + isFirstMaterial = false; + } + else + { + pixels.at(index).m_material2 = aznumeric_cast(materialId); + float totalWeight = firstWeight + surfaceTagWeight.m_weight; + float blendWeight = 1.0f - (firstWeight / totalWeight); + pixels.at(index).m_blend = aznumeric_cast(AZStd::round(blendWeight * 255.0f)); + break; + } + } + } + else + { + break; // since the list is ordered, no other materials are in the list with positive weights. + } + } + ++index; + } + } + + const int32_t left = quadrantTextureArea.m_min.m_x; + const int32_t top = quadrantTextureArea.m_min.m_y; + const int32_t width = quadrantTextureArea.m_max.m_x - quadrantTextureArea.m_min.m_x; + const int32_t height = quadrantTextureArea.m_max.m_y - quadrantTextureArea.m_min.m_y; + + AZ::RHI::ImageUpdateRequest imageUpdateRequest; + imageUpdateRequest.m_imageSubresourcePixelOffset.m_left = aznumeric_cast(left); + imageUpdateRequest.m_imageSubresourcePixelOffset.m_top = aznumeric_cast(top); + imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerRow = width * sizeof(DetailMaterialPixel); + imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerImage = width * height * sizeof(DetailMaterialPixel); + imageUpdateRequest.m_sourceSubresourceLayout.m_rowCount = height; + imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_width = width; + imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_height = height; + imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_depth = 1; + imageUpdateRequest.m_sourceData = pixels.data(); + imageUpdateRequest.m_image = m_detailTextureImage->GetRHIImage(); + + m_detailTextureImage->UpdateImageContents(imageUpdateRequest); + } + } + + uint16_t TerrainFeatureProcessor::GetDetailMaterialForSurfaceTypeAndPosition(AZ::Crc32 surfaceType, const AZ::Vector2& position) + { + for (const auto& materialRegion : m_detailMaterialRegions.GetDataVector()) + { + if (materialRegion.m_region.Contains(AZ::Vector3(position.GetX(), position.GetY(), 0.0f))) + { + for (const auto& materialSurface : materialRegion.m_materialsForSurfaces) + { + if (materialSurface.m_surfaceTag == surfaceType) + { + return materialSurface.m_detailMaterialId; + } + } + } + } + return m_detailMaterials.NoFreeSlot; } void TerrainFeatureProcessor::UpdateTerrainData() { - static const AZ::Name TerrainHeightmapName = AZ::Name(TerrainHeightmapChars); - uint32_t width = m_areaData.m_updateWidth; uint32_t height = m_areaData.m_updateHeight; const AZ::Aabb& worldBounds = m_areaData.m_terrainBounds; @@ -280,8 +796,10 @@ namespace Terrain AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D( AZ::RHI::ImageBindFlags::ShaderRead, width, height, AZ::RHI::Format::R16_UNORM ); + + const AZ::Name TerrainHeightmapName = AZ::Name(TerrainHeightmapChars); m_areaData.m_heightmapImage = AZ::RPI::AttachmentImage::Create(*imagePool.get(), imageDescriptor, TerrainHeightmapName, nullptr, nullptr); - AZ_Error(TerrainFPName, m_areaData.m_heightmapImage, "Failed to initialize the heightmap image!"); + AZ_Error(TerrainFPName, m_areaData.m_heightmapImage, "Failed to initialize the heightmap image."); } AZStd::vector pixels; @@ -366,6 +884,19 @@ namespace Terrain m_heightmapPropertyIndex = m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::HeightmapImage)); AZ_Error(TerrainFPName, m_heightmapPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::HeightmapImage); + m_detailMaterialIdPropertyIndex = m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::DetailMaterialIdImage)); + AZ_Error(TerrainFPName, m_detailMaterialIdPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::DetailMaterialIdImage); + + m_detailCenterPropertyIndex = m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::DetailCenter)); + AZ_Error(TerrainFPName, m_detailCenterPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::DetailCenter); + + m_detailAabbPropertyIndex = m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::DetailAabb)); + AZ_Error(TerrainFPName, m_detailAabbPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::DetailAabb); + + m_detailHalfPixelUvPropertyIndex = m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::DetailHalfPixelUv)); + AZ_Error(TerrainFPName, m_detailHalfPixelUvPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::DetailHalfPixelUv); + + // Find any macro materials that have already been created. TerrainMacroMaterialRequestBus::EnumerateHandlers( [&](TerrainMacroMaterialRequests* handler) { @@ -376,6 +907,30 @@ namespace Terrain } ); TerrainMacroMaterialNotificationBus::Handler::BusConnect(); + + // Find any detail material areas that have already been created. + TerrainAreaMaterialRequestBus::EnumerateHandlers( + [&](TerrainAreaMaterialRequests* handler) + { + const AZ::Aabb& bounds = handler->GetTerrainSurfaceMaterialRegion(); + const AZStd::vector materialMappings = handler->GetSurfaceMaterialMappings(); + AZ::EntityId entityId = *(Terrain::TerrainAreaMaterialRequestBus::GetCurrentBusId()); + + DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); + materialRegion.m_region = bounds; + + for (const auto& materialMapping : materialMappings) + { + if (materialMapping.m_materialInstance) + { + OnTerrainSurfaceMaterialMappingCreated(entityId, materialMapping.m_surfaceTag, materialMapping.m_materialInstance); + } + } + return true; + } + ); + TerrainAreaMaterialNotificationBus::Handler::BusConnect(); + } void TerrainFeatureProcessor::UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, const MacroMaterialData& newMaterialData) @@ -474,14 +1029,73 @@ namespace Terrain } } } + else if (m_forceRebuildDrawPackets) + { + for (auto& sectorData : m_sectorData) + { + for (auto& drawPacket : sectorData.m_drawPackets) + { + drawPacket.Update(*GetParentScene(), true); + } + } + } + m_forceRebuildDrawPackets = false; if (m_areaData.m_heightmapUpdated) { UpdateTerrainData(); - - const AZ::Data::Instance heightmapImage = m_areaData.m_heightmapImage; + + const AZ::Data::Instance heightmapImage = m_areaData.m_heightmapImage; // cast StreamingImage to Image m_materialInstance->SetPropertyValue(m_heightmapPropertyIndex, heightmapImage); - m_materialInstance->Compile(); + } + + AZ::Vector3 cameraPosition = AZ::Vector3::CreateZero(); + for (auto& view : process.m_views) + { + if ((view->GetUsageFlags() & AZ::RPI::View::UsageFlags::UsageCamera) > 0) + { + cameraPosition = view->GetCameraTransform().GetTranslation(); + break; + } + } + + if (m_dirtyDetailRegion.IsValid() || !cameraPosition.IsClose(m_previousCameraPosition)) + { + int32_t newDetailTexturePosX = aznumeric_cast(AZStd::roundf(cameraPosition.GetX() / DetailTextureScale)); + int32_t newDetailTexturePosY = aznumeric_cast(AZStd::roundf(cameraPosition.GetY() / DetailTextureScale)); + + Aabb2i newBounds; + newBounds.m_min.m_x = newDetailTexturePosX - DetailTextureSizeHalf; + newBounds.m_min.m_y = newDetailTexturePosY - DetailTextureSizeHalf; + newBounds.m_max.m_x = newDetailTexturePosX + DetailTextureSizeHalf; + newBounds.m_max.m_y = newDetailTexturePosY + DetailTextureSizeHalf; + + // Use modulo to find the center point in texture space. Care must be taken so negative values are + // handled appropriately (ie, we want -1 % 1024 to equal 1023, not -1) + Vector2i newCenter; + newCenter.m_x = (DetailTextureSize + (newDetailTexturePosX % DetailTextureSize)) % DetailTextureSize; + newCenter.m_y = (DetailTextureSize + (newDetailTexturePosY % DetailTextureSize)) % DetailTextureSize; + + CheckUpdateDetailTexture(newBounds, newCenter); + + m_detailTextureBounds = newBounds; + m_dirtyDetailRegion = AZ::Aabb::CreateNull(); + + m_previousCameraPosition = cameraPosition; + const AZ::Data::Instance detailTextureImage = m_detailTextureImage; // cast StreamingImage to Image + m_materialInstance->SetPropertyValue(m_detailMaterialIdPropertyIndex, detailTextureImage); + + AZ::Vector4 detailAabb = AZ::Vector4( + m_detailTextureBounds.m_min.m_x * DetailTextureScale, + m_detailTextureBounds.m_min.m_y * DetailTextureScale, + m_detailTextureBounds.m_max.m_x * DetailTextureScale, + m_detailTextureBounds.m_max.m_y * DetailTextureScale + ); + m_materialInstance->SetPropertyValue(m_detailAabbPropertyIndex, detailAabb); + m_materialInstance->SetPropertyValue(m_detailHalfPixelUvPropertyIndex, 0.5f / DetailTextureSize); + + AZ::Vector2 detailUvOffset = AZ::Vector2(float(newCenter.m_x) / DetailTextureSize, float(newCenter.m_y) / DetailTextureSize); + m_materialInstance->SetPropertyValue(m_detailCenterPropertyIndex, detailUvOffset); } if (m_areaData.m_heightmapUpdated || m_areaData.m_macroMaterialsUpdated) @@ -603,6 +1217,11 @@ namespace Terrain } } } + + if (m_materialInstance) + { + m_materialInstance->Compile(); + } } void TerrainFeatureProcessor::InitializeTerrainPatch(uint16_t gridSize, float gridSpacing, PatchData& patchdata) @@ -746,9 +1365,10 @@ namespace Terrain // larger but this will limit how much is rendered. } - MacroMaterialData* TerrainFeatureProcessor::FindMacroMaterial(AZ::EntityId entityId) + template + T* TerrainFeatureProcessor::FindByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container) { - for (MacroMaterialData& data : m_macroMaterials.GetDataVector()) + for (T& data : container.GetDataVector()) { if (data.m_entityId == entityId) { @@ -757,34 +1377,36 @@ namespace Terrain } return nullptr; } - - MacroMaterialData& TerrainFeatureProcessor::FindOrCreateMacroMaterial(AZ::EntityId entityId) + + template + T& TerrainFeatureProcessor::FindOrCreateByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container) { - MacroMaterialData* dataPtr = FindMacroMaterial(entityId); + T* dataPtr = FindByEntityId(entityId, container); if (dataPtr != nullptr) { return *dataPtr; } - const uint16_t slotId = m_macroMaterials.GetFreeSlotIndex(); - AZ_Assert(slotId != m_macroMaterials.NoFreeSlot, "Ran out of indices for macro materials"); + const uint16_t slotId = container.GetFreeSlotIndex(); + AZ_Assert(slotId != AZ::Render::IndexedDataVector::NoFreeSlot, "Ran out of indices"); - MacroMaterialData& data = m_macroMaterials.GetData(slotId); + T& data = container.GetData(slotId); data.m_entityId = entityId; return data; } - - void TerrainFeatureProcessor::RemoveMacroMaterial(AZ::EntityId entityId) + + template + void TerrainFeatureProcessor::RemoveByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container) { - for (MacroMaterialData& data : m_macroMaterials.GetDataVector()) + for (T& data : container.GetDataVector()) { if (data.m_entityId == entityId) { - m_macroMaterials.RemoveData(&data); + container.RemoveData(&data); return; } } - AZ_Assert(false, "Entity Id not found in m_macroMaterials.") + AZ_Assert(false, "Entity Id not found in container.") } template @@ -798,4 +1420,60 @@ namespace Terrain } } } + + auto TerrainFeatureProcessor::Vector2i::operator+(const Vector2i& rhs) const -> Vector2i + { + Vector2i offsetPoint = *this; + offsetPoint += rhs; + return offsetPoint; + } + + auto TerrainFeatureProcessor::Vector2i::operator+=(const Vector2i& rhs) -> Vector2i& + { + m_x += rhs.m_x; + m_y += rhs.m_y; + return *this; + } + + auto TerrainFeatureProcessor::Vector2i::operator-(const Vector2i& rhs) const -> Vector2i + { + return *this + -rhs; + } + + auto TerrainFeatureProcessor::Vector2i::operator-=(const Vector2i& rhs) -> Vector2i& + { + return *this += -rhs; + } + + auto TerrainFeatureProcessor::Vector2i::operator-() const -> Vector2i + { + return {-m_x, -m_y}; + } + + auto TerrainFeatureProcessor::Aabb2i::operator+(const Vector2i& rhs) const -> Aabb2i + { + return { m_min + rhs, m_max + rhs }; + } + + auto TerrainFeatureProcessor::Aabb2i::operator-(const Vector2i& rhs) const -> Aabb2i + { + return *this + -rhs; + } + + auto TerrainFeatureProcessor::Aabb2i::GetClamped(Aabb2i rhs) const -> Aabb2i + { + Aabb2i ret; + ret.m_min.m_x = AZ::GetMax(m_min.m_x, rhs.m_min.m_x); + ret.m_min.m_y = AZ::GetMax(m_min.m_y, rhs.m_min.m_y); + ret.m_max.m_x = AZ::GetMin(m_max.m_x, rhs.m_max.m_x); + ret.m_max.m_y = AZ::GetMin(m_max.m_y, rhs.m_max.m_y); + return ret; + } + + bool TerrainFeatureProcessor::Aabb2i::IsValid() const + { + // Intentionally strict, equal min/max not valid. + return m_min.m_x < m_max.m_x && m_min.m_y < m_max.m_y; + } + } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h index f82fd8ecb0..962ffe13bf 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h @@ -12,11 +12,13 @@ #include #include +#include #include #include #include #include +#include #include namespace AZ::RPI @@ -37,6 +39,7 @@ namespace Terrain , private AZ::RPI::MaterialReloadNotificationBus::Handler , private AzFramework::Terrain::TerrainDataNotificationBus::Handler , private TerrainMacroMaterialNotificationBus::Handler + , private TerrainAreaMaterialNotificationBus::Handler { public: AZ_RTTI(TerrainFeatureProcessor, "{D7DAC1F9-4A9F-4D3C-80AE-99579BF8AB1C}", AZ::RPI::FeatureProcessor); @@ -112,6 +115,109 @@ namespace Terrain AZStd::fixed_vector m_macroMaterials; }; + enum DetailTextureFlags : uint32_t + { + UseTextureBaseColor = 0b0000'0000'0000'0000'0000'0000'0000'0001, + UseTextureNormal = 0b0000'0000'0000'0000'0000'0000'0000'0010, + UseTextureMetallic = 0b0000'0000'0000'0000'0000'0000'0000'0100, + UseTextureRoughness = 0b0000'0000'0000'0000'0000'0000'0000'1000, + UseTextureOcclusion = 0b0000'0000'0000'0000'0000'0000'0001'0000, + UseTextureHeight = 0b0000'0000'0000'0000'0000'0000'0010'0000, + UseTextureSpecularF0 = 0b0000'0000'0000'0000'0000'0000'0100'0000, + + FlipNormalX = 0b0000'0000'0000'0000'0000'0000'1000'0000, + FlipNormalY = 0b0000'0000'0000'0000'0000'0001'0000'0000, + + BlendModeMask = 0b0000'0000'0000'0000'0000'0110'0000'0000, + BlendModeLerp = 0b0000'0000'0000'0000'0000'0000'0000'0000, + BlendModeLinearLight = 0b0000'0000'0000'0000'0000'0010'0000'0000, + BlendModeMultiply = 0b0000'0000'0000'0000'0000'0100'0000'0000, + BlendModeOverlay = 0b0000'0000'0000'0000'0000'0110'0000'0000, + }; + + struct DetailMaterialShaderProperties + { + // Uv + AZStd::array m_uvTransform + { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + }; + + // Factor / Scale / Bias for input textures + float m_baseColorFactor{ 1.0f }; + float m_normalFactor{ 1.0f }; + float m_metalFactor{ 1.0f }; + float m_roughnessScale{ 1.0f }; + + float m_roughnessBias{ 0.0f }; + float m_specularF0Factor{ 1.0f }; + float m_occlusionFactor{ 1.0f }; + float m_heightFactor{ 1.0f }; + + float m_heightOffset{ 0.0f }; + float m_heightBlendFactor{ 0.5f }; + + // Flags + DetailTextureFlags m_flags{ 0 }; + + float m_padding; // 16 byte aligned + }; + + struct DetailMaterialData + { + AZ::Data::AssetId m_assetId; + AZ::RPI::Material::ChangeId m_materialChangeId{AZ::RPI::Material::DEFAULT_CHANGE_ID}; + + AZ::Data::Instance m_colorImage; + AZ::Data::Instance m_normalImage; + AZ::Data::Instance m_roughnessImage; + AZ::Data::Instance m_metalnessImage; + AZ::Data::Instance m_specularF0Image; + AZ::Data::Instance m_occlusionImage; + AZ::Data::Instance m_heightImage; + + DetailMaterialShaderProperties m_properties; // maps directly to shader + }; + + struct DetailMaterialSurface + { + AZ::Crc32 m_surfaceTag; + uint16_t m_detailMaterialId; + }; + + struct DetailMaterialListRegion + { + AZ::EntityId m_entityId; + AZ::Aabb m_region{AZ::Aabb::CreateNull()}; + AZStd::vector m_materialsForSurfaces; + }; + + struct Vector2i + { + int32_t m_x{ 0 }; + int32_t m_y{ 0 }; + + Vector2i operator+(const Vector2i& rhs) const; + Vector2i& operator+=(const Vector2i& rhs); + Vector2i operator-(const Vector2i& rhs) const; + Vector2i& operator-=(const Vector2i& rhs); + Vector2i operator-() const; + }; + + struct Aabb2i + { + Vector2i m_min; + Vector2i m_max; + + Aabb2i operator+(const Vector2i& offset) const; + Aabb2i operator-(const Vector2i& offset) const; + + Aabb2i GetClamped(Aabb2i rhs) const; + bool IsValid() const; + }; + // AZ::RPI::MaterialReloadNotificationBus::Handler overrides... void OnMaterialReinitialized(const MaterialInstance& material) override; @@ -124,6 +230,12 @@ namespace Terrain void OnTerrainMacroMaterialChanged(AZ::EntityId entityId, const MacroMaterialData& material) override; void OnTerrainMacroMaterialRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override; void OnTerrainMacroMaterialDestroyed(AZ::EntityId entityId) override; + + // TerrainAreaMaterialNotificationBus overrides... + void OnTerrainSurfaceMaterialMappingCreated(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override; + void OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag) override; + void OnTerrainSurfaceMaterialMappingChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override; + void OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override; void Initialize(); void InitializeTerrainPatch(uint16_t gridSize, float gridSpacing, PatchData& patchdata); @@ -132,12 +244,26 @@ namespace Terrain void UpdateTerrainData(); void PrepareMaterialData(); void UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, const MacroMaterialData& newMaterialData); + + void TerrainHeightOrSettingsUpdated(const AZ::Aabb& dirtyRegion); + void TerrainSurfaceDataUpdated(const AZ::Aabb& dirtyRegion); + + uint16_t CreateOrUpdateDetailMaterial(MaterialInstance material); + void UpdateDetailMaterialData(DetailMaterialData& materialData, MaterialInstance material); + void CheckUpdateDetailTexture(const Aabb2i& newBounds, const Vector2i& newCenter); + void UpdateDetailTexture(const Aabb2i& updateArea, const Aabb2i& textureBounds, const Vector2i& centerPixel); + uint16_t GetDetailMaterialForSurfaceTypeAndPosition(AZ::Crc32 surfaceType, const AZ::Vector2& position); + uint8_t CalculateUpdateRegions(const Aabb2i& updateArea, const Aabb2i& textureBounds, const Vector2i& centerPixel, + AZStd::array& textureSpaceAreas, AZStd::array& scaledWorldSpaceAreas); void ProcessSurfaces(const FeatureProcessor::RenderPacket& process); - - MacroMaterialData* FindMacroMaterial(AZ::EntityId entityId); - MacroMaterialData& FindOrCreateMacroMaterial(AZ::EntityId entityId); - void RemoveMacroMaterial(AZ::EntityId entityId); + + template + T* FindByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container); + template + T& FindOrCreateByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container); + template + void RemoveByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container); template void ForOverlappingSectors(const AZ::Aabb& bounds, Callback callback); @@ -147,8 +273,11 @@ namespace Terrain // System-level parameters static constexpr float GridSpacing{ 1.0f }; - static constexpr uint32_t GridSize{ 64 }; // number of terrain quads (vertices are m_gridSize + 1) + static constexpr int32_t GridSize{ 64 }; // number of terrain quads (vertices are m_gridSize + 1) static constexpr float GridMeters{ GridSpacing * GridSize }; + static constexpr int32_t DetailTextureSize{ 1024 }; + static constexpr int32_t DetailTextureSizeHalf{ DetailTextureSize / 2 }; + static constexpr float DetailTextureScale{ 0.5f }; AZStd::unique_ptr m_materialAssetLoader; MaterialInstance m_materialInstance; @@ -160,8 +289,13 @@ namespace Terrain AZ::RHI::ShaderInputImageIndex m_macroColorMapIndex; AZ::RHI::ShaderInputImageIndex m_macroNormalMapIndex; AZ::RPI::MaterialPropertyIndex m_heightmapPropertyIndex; + AZ::RPI::MaterialPropertyIndex m_detailMaterialIdPropertyIndex; + AZ::RPI::MaterialPropertyIndex m_detailCenterPropertyIndex; + AZ::RPI::MaterialPropertyIndex m_detailAabbPropertyIndex; + AZ::RPI::MaterialPropertyIndex m_detailHalfPixelUvPropertyIndex; AZ::Data::Instance m_patchModel; + AZ::Vector3 m_previousCameraPosition = AZ::Vector3(AZStd::numeric_limits::max(), 0.0, 0.0); // Per-area data struct TerrainAreaData @@ -178,12 +312,21 @@ namespace Terrain bool m_macroMaterialsUpdated{ true }; bool m_rebuildSectors{ true }; }; - + TerrainAreaData m_areaData; AZ::Aabb m_dirtyRegion{ AZ::Aabb::CreateNull() }; + AZ::Aabb m_dirtyDetailRegion{ AZ::Aabb::CreateNull() }; + + Aabb2i m_detailTextureBounds; + Vector2i m_detailTextureCenter; + AZ::Data::Instance m_detailTextureImage; + AZ::RPI::ShaderSystemInterface::GlobalShaderOptionUpdatedEvent::Handler m_handleGlobalShaderOptionUpdate; + bool m_forceRebuildDrawPackets = false; AZStd::vector m_sectorData; AZ::Render::IndexedDataVector m_macroMaterials; + AZ::Render::IndexedDataVector m_detailMaterials; + AZ::Render::IndexedDataVector m_detailMaterialRegions; }; } From c5c043ecc5ee577e3fb194052cfa4b60313057ac Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Wed, 27 Oct 2021 08:46:31 -0700 Subject: [PATCH 065/120] Add Generic DOM visitor interface (#4852) * Add Generic DOM visitor interface Just the visitor interface from the [Generic DOM RFC](https://github.com/o3de/sig-content/blob/main/rfcs/rfc-10-generic-dom.md) with a few hardening changes so that we can align on it early: - Clarified Lifetimes with an enum, extended it to cover the by-ref opaque values as well - Added an explicit error type so that serializers can provide logging friendly rejections - Did a first pass on documentation - Added Visitor capabilities introspection and support for raw strings --- .../AzCore/AzCore/DOM/DomVisitor.cpp | 239 ++++++++++++++++++ Code/Framework/AzCore/AzCore/DOM/DomVisitor.h | 237 +++++++++++++++++ .../AzCore/AzCore/azcore_files.cmake | 2 + 3 files changed, 478 insertions(+) create mode 100644 Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp create mode 100644 Code/Framework/AzCore/AzCore/DOM/DomVisitor.h diff --git a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp new file mode 100644 index 0000000000..5d66bb6ac5 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace AZ::DOM +{ + const char* VisitorError::CodeToString(VisitorErrorCode code) + { + switch (code) + { + case VisitorErrorCode::UnsupportedOperation: + return "operation not supported"; + case VisitorErrorCode::InvalidData: + return "invalid data specified"; + case VisitorErrorCode::InternalError: + return "internal error"; + default: + return "unknown error"; + } + } + + VisitorError::VisitorError(VisitorErrorCode code) + : m_code(code) + { + } + + VisitorError::VisitorError(VisitorErrorCode code, AZStd::string additionalInfo) + : m_code(code) + , m_additionalInfo(AZStd::move(additionalInfo)) + { + } + + VisitorErrorCode VisitorError::GetCode() const + { + return m_code; + } + + const AZStd::string& VisitorError::GetAdditionalInfo() const + { + return m_additionalInfo; + } + + AZStd::string VisitorError::FormatVisitorErrorMessage() const + { + if (m_additionalInfo.empty()) + { + return AZStd::string::format("VisitorError: %s.", CodeToString(m_code)); + } + return AZStd::string::format("VisitorError: %s. %s.", CodeToString(m_code), m_additionalInfo.c_str()); + } + + Visitor::Result Visitor::VisitorFailure(VisitorErrorCode code) + { + return AZ::Failure(VisitorError(code)); + } + + Visitor::Result Visitor::VisitorFailure(VisitorErrorCode code, AZStd::string additionalInfo) + { + return AZ::Failure(VisitorError(code, AZStd::move(additionalInfo))); + } + + Visitor::Result Visitor::VisitorFailure(VisitorError error) + { + return AZ::Failure(error); + } + + Visitor::Result Visitor::VisitorSuccess() + { + return AZ::Success(); + } + + Visitor::Result Visitor::Null() + { + return VisitorSuccess(); + } + + Visitor::Result Visitor::Bool([[maybe_unused]] bool value) + { + return VisitorSuccess(); + } + + Visitor::Result Visitor::Int64([[maybe_unused]] AZ::s64 value) + { + return VisitorSuccess(); + } + + Visitor::Result Visitor::Uint64([[maybe_unused]] AZ::u64 value) + { + return VisitorSuccess(); + } + + Visitor::Result Visitor::Double([[maybe_unused]] double value) + { + return VisitorSuccess(); + } + + Visitor::Result Visitor::String([[maybe_unused]] AZStd::string_view value, [[maybe_unused]] Lifetime lifetime) + { + return VisitorSuccess(); + } + + Visitor::Result Visitor::OpaqueValue([[maybe_unused]] const OpaqueType& value, [[maybe_unused]] Lifetime lifetime) + { + if (!SupportsOpaqueValues()) + { + return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Opaque values are not supported by this visitor"); + } + return VisitorSuccess(); + } + + Visitor::Result Visitor::RawValue([[maybe_unused]] AZStd::string_view value, [[maybe_unused]] Lifetime lifetime) + { + if (!SupportsRawValues()) + { + return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Raw values are not supported by this visitor"); + } + return VisitorSuccess(); + } + + Visitor::Result Visitor::StartObject() + { + if (!SupportsObjects()) + { + return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Objects are not supported by this visitor"); + } + return VisitorSuccess(); + } + + Visitor::Result Visitor::EndObject([[maybe_unused]] AZ::u64 attributeCount) + { + if (!SupportsObjects()) + { + return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Objects are not supported by this visitor"); + } + return VisitorSuccess(); + } + + Visitor::Result Visitor::Key([[maybe_unused]] AZ::Name key) + { + if (!SupportsObjects() && !SupportsNodes()) + { + return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Keys are not supported by this visitor"); + } + return VisitorSuccess(); + } + + Visitor::Result Visitor::RawKey(AZStd::string_view key, [[maybe_unused]] Lifetime lifetime) + { + if (!SupportsRawKeys()) + { + return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Raw keys are not supported by this visitor"); + } + return Key(AZ::Name(key)); + } + + Visitor::Result Visitor::StartArray() + { + if (!SupportsArrays()) + { + return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Arrays are not supported by this visitor"); + } + return VisitorSuccess(); + } + + Visitor::Result Visitor::EndArray([[maybe_unused]] AZ::u64 elementCount) + { + if (!SupportsArrays()) + { + return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Arrays are not supported by this visitor"); + } + return VisitorSuccess(); + } + + Visitor::Result Visitor::StartNode([[maybe_unused]] AZ::Name name) + { + if (!SupportsNodes()) + { + return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Nodes are not supported by this visitor"); + } + return VisitorSuccess(); + } + + Visitor::Result Visitor::RawStartNode(AZStd::string_view name, [[maybe_unused]] Lifetime lifetime) + { + return StartNode(AZ::Name(name)); + } + + Visitor::Result Visitor::EndNode([[maybe_unused]] AZ::u64 attributeCount, [[maybe_unused]] AZ::u64 elementCount) + { + if (!SupportsNodes()) + { + return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Nodes are not supported by this visitor"); + } + return VisitorSuccess(); + } + + VisitorFlags Visitor::GetVisitorFlags() const + { + // By default support raw keys (promoting them to AZ::Name) and support Array / Object / Node + // We leave Opaque type support and Raw Values to more specialized, implementation-specific cases + return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects | VisitorFlags::SupportsNodes; + } + + bool Visitor::SupportsRawValues() const + { + return (GetVisitorFlags() & VisitorFlags::SupportsRawValues) != VisitorFlags::Null; + } + + bool Visitor::SupportsRawKeys() const + { + return (GetVisitorFlags() & VisitorFlags::SupportsRawKeys) != VisitorFlags::Null; + } + + bool Visitor::SupportsObjects() const + { + return (GetVisitorFlags() & VisitorFlags::SupportsObjects) != VisitorFlags::Null; + } + + bool Visitor::SupportsArrays() const + { + return (GetVisitorFlags() & VisitorFlags::SupportsArrays) != VisitorFlags::Null; + } + + bool Visitor::SupportsNodes() const + { + return (GetVisitorFlags() & VisitorFlags::SupportsNodes) != VisitorFlags::Null; + } + + bool Visitor::SupportsOpaqueValues() const + { + return (GetVisitorFlags() & VisitorFlags::SupportsOpaqueValues) != VisitorFlags::Null; + } +} // namespace AZ::DOM diff --git a/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h new file mode 100644 index 0000000000..584cfce4ed --- /dev/null +++ b/Code/Framework/AzCore/AzCore/DOM/DomVisitor.h @@ -0,0 +1,237 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace AZ::DOM +{ + // + // Lifetime enum + // + //! Specifies the period in which a reference value will still be alive and safe to read. + enum class Lifetime + { + //! Specifies that the value is safe to read and will remain so indefinitely. + //! This implies that the value will not be mutated for the duration of this storage. + Persistent, + //! Specifies that the value may change or be deallocated, and must be copied to be safely stored. + Temporary, + }; + + // + // VisitorErrorCode enum + // + //! Error code specifying the reason a Visitor operation failed. + enum class VisitorErrorCode + { + //! Set when a Visitor doesn't have an implementation for a given attribute type. + //! A pure-JSON serializer might reject a Node attribute, for example, and serialization visitors + //! can forbid non-serializable Opaque types. + UnsupportedOperation, + //! Set when a Visitor has received malformed or invalid data. + //! Potential sources include mismatching Begin/End call pairs or invalid attribute or element counts + //! being sent to End methods. + InvalidData, + //! The Visitor failed for some other reason not caused by invalid input. + //! If returning a custom error with this code, it's preferrable to also provide supplemental info + //! in the form of an explanatory string. + InternalError + }; + + // + // VisitorError class + // + //! Details of the reason for failure within a VisitorInterface operation. + class VisitorError final + { + public: + explicit VisitorError(VisitorErrorCode code); + VisitorError(VisitorErrorCode code, AZStd::string additionalInfo); + + //! Gets the error code associated with this error. + VisitorErrorCode GetCode() const; + //! Gets a supplemental error info string from the error. + //! Returns an empty string if no additional information was provided to the error. + const AZStd::string& GetAdditionalInfo() const; + //! Provides a formatted, human-readable error description that can be used for logging purposes. + AZStd::string FormatVisitorErrorMessage() const; + + //! Helper method, translates a VisitorErrorCode to a human readable string. + static const char* CodeToString(VisitorErrorCode code); + + private: + VisitorErrorCode m_code; + AZStd::string m_additionalInfo; + }; + + //! A type alias for opaque DOM types that aren't meant to be serializable. + //! /see VisitorInterface::OpaqueValue + using OpaqueType = AZStd::any; + + // + // VisitorFlags enum + // + //! Flags representning capabilities of a \ref Visitor. + enum class VisitorFlags : AZ::u16 + { + //! No flags are set. This can be used in conjunction with bitwise operators to check a flag. + Null = 0, + //! If set, this Visitor interface supports raw strings in place of specific value types. + //! Visitors with this flag accept RawValue calls in lieu of more specific value calls such as Int64 or String. + SupportsRawValues = (1 << 1), + //! If set, this Visitor interface supports raw strings in place of Name types for keys and Node names. + //! Visitors with this flag accept RawKey and RawStartNode in lieu of Key and StartNode calls. + SupportsRawKeys = (1 << 2), + //! If set, this Visitor interface supports Object types described via BeginObject and EndObject. + SupportsObjects = (1 << 3), + //! If set, this Visitor interface supports Array types described via BeginArray and EndArray. + SupportsArrays = (1 << 4), + //! If set, this Visitor interface supports Node types described BeginNode and EndNode. + SupportsNodes = (1 << 4), + //! If set, this Visitor interface supports opaque values described via OpaqueValue. + SupportsOpaqueValues = (1 << 5), + }; + + AZ_DEFINE_ENUM_BITWISE_OPERATORS(VisitorFlags); + + // + // Visitor class + // + //! An interface for performing operations on elements of a generic DOM (Document Object Model). + //! A Document Object Model is defined here as a tree structure comprised of one of the following values: + //! - Primitives: plain data types, including + //! - \ref Int64: 64 bit signed integer + //! - \ref Uint64: 64 bit unsigned integer + //! - \ref Bool: boolean value + //! - \ref Double: 64 bit double precision float + //! - \ref Null: sentinel "empty" type with no value representation + //! - \ref String: UTF8 encoded string + //! - \ref Object: an ordered container of key/value pairs where keys are AZ::Names and values may be any DOM type + //! (including Object) + //! - \ref Array: an ordered container of values, in which values are any DOM value type (including Array) + //! - \ref Node: a container + //! - \ref OpaqueValue: An arbitrary value stored in an AZStd::any. This is a non-serializable representation of an + //! entry useful for in-memory options. This is intended to be used as an intermediate value over the course of DOM + //! transformation and as a proxy to pass through types of which the DOM has no knowledge to other systems. + //! + //! Opaque values are rejected by the default VisitorInterface implementation. + //! + //! Care should be ensured that DOMs representing opaque types are only visited by consumers that understand them. + class Visitor + { + public: + virtual ~Visitor() = default; + + //! The result of a Visitor operation. + //! A failure indicates a non-recoverable issue and signals that no further visit calls may be made in the + //! current state. + using Result = AZ::Outcome; + + //! Returns a set of flags representing the operations this Visitor supports. + //! The base implementation supports raw keys (\see VisitorFlags::SupportsRawKeys) and + //! arrays (\see VisitorFlags::SupportsArrays), objects (\see VisitorFlags::SupportsObjects), and + //! nodes (\see VisitorFlags::SupportsNodes). + //! Raw (\see VisitorFlags::SupportsRawValues) and opaque values (\see VisitorFlags::SupportsOpaqueValues) + //! are disallowed by default, as their handling is intended to be implementation-specific. + virtual VisitorFlags GetVisitorFlags() const; + //! /see VisitorFlags::SupportsRawValues + bool SupportsRawValues() const; + //! /see VisitorFlags::SupportsRawKeys + bool SupportsRawKeys() const; + //! /see VisitorFlags::SupportsObjects + bool SupportsObjects() const; + //! /see VisitorFlags::SupportsArrays + bool SupportsArrays() const; + //! /see VisitorFlags::SupportsNodes + bool SupportsNodes() const; + //! /see VisitorFlags::SupportsOpaqueValues + bool SupportsOpaqueValues() const; + + //! Operates on an empty null value. + virtual Result Null(); + //! Operates on a bool value. + virtual Result Bool(bool value); + //! Operates on a signed, 64 bit integer value. + virtual Result Int64(AZ::s64 value); + //! Operates on an unsigned, 64 bit integer value. + virtual Result Uint64(AZ::u64 value); + //! Operates on a double precision, 64 bit floating point value. + virtual Result Double(double value); + //! Operates on a string value. As strings are a reference type. + //! Storage semantics are provided to indicate where the value may be stored persistently or requires a copy. + virtual Result String(AZStd::string_view value, Lifetime lifetime); + //! Operates on an opaque value. As opaque values are a reference type, storage semantics are provided to + //! indicate where the value may be stored persistently or requires a copy. + //! The base implementation of OpaqueValue rejects the operation, as opaque values are meant for special + //! cases with specific implementations, not generic usage. + //! Storage semantics are provided to indicate where the value may be stored persistently or requires a copy. + virtual Result OpaqueValue(const OpaqueType& value, Lifetime lifetime); + //! Operates on a raw value encoded as a UTF-8 string that hasn't had its type deduced. + //! Visitors that support raw values (\see VisitorFlags::SupportsRawValues) may parse the raw value and + //! forward it to the corresponding value call or calls of their choice. + //! The base implementation of RawValue rejects the operation, as raw values are meant to be handled on + //! a per-implementation basis. + virtual Result RawValue(AZStd::string_view value, Lifetime lifetime); + + //! Operates on an Object. + //! Callers may make any number of Key calls, followed by calls representing a value (including a nested + //! StartObject call) and then must call EndObject. + virtual Result StartObject(); + //! Finishes operating on an Object. + //! Callers must provide the number of attributes that were provided to the object, i.e. the number of key + //! and value calls made within the direct context of this object (but not any nested objects / nodes). + virtual Result EndObject(AZ::u64 attributeCount); + + //! Specifies a key for a key/value pair. + //! Key must be called subsequent to a call to \ref StartObject or \ref StartNode and immediately followed by + //! calls representing the key's associated value. + virtual Result Key(AZ::Name key); + //! Specifies a key for a key/value pair using a raw string instead of \ref AZ::Name. + //! \see Key + virtual Result RawKey(AZStd::string_view key, Lifetime lifetime); + + //! Operates on an Array. + //! Callers may make any number of subsequent value calls to represent the elements of the array, and then must + //! call EndArray. + virtual Result StartArray(); + //! Finishes operating on an Array. + //! Callers must provide the number of elements that were provided to the array, i.e. the number of value calls + //! made within the direct context of this array (but not any nested arrays / nodes). + virtual Result EndArray(AZ::u64 elementCount); + + //! Operates on a Node. + //! Callers may make any number of Key calls followed by value calls or value calls not prefixed with a Key + //! call, and then must call EndNode. See \ref StartObject and \ref StartArray as Node types combine the + //! functionality of both structures into a named Node structure. + virtual Result StartNode(AZ::Name name); + //! Operates on a Node using a raw string instead of \ref AZ::Name. + //! \see StartNode + virtual Result RawStartNode(AZStd::string_view name, Lifetime lifetime); + //! Finishes operating on a Node. + //! Callers must provide both the number of attributes the were provided and the number of elements that were + //! provided to the node, attributes being values prefaced by a call to Key. + virtual Result EndNode(AZ::u64 attributeCount, AZ::u64 elementCount); + + protected: + Visitor() = default; + + //! Helper method, constructs a failure \ref Result with the specified code. + static Result VisitorFailure(VisitorErrorCode code); + //! Helper method, constructs a failure \ref Result with the specified code and supplemental info. + static Result VisitorFailure(VisitorErrorCode code, AZStd::string additionalInfo); + //! Helper method, constructs a failure \ref Result with the specified error. + static Result VisitorFailure(VisitorError error); + //! Helper method, constructs a success \ref Result. + static Result VisitorSuccess(); + }; +} // namespace AZ::DOM diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 0c5a360844..6a9e5a29d6 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -123,6 +123,8 @@ set(FILES Debug/TraceMessagesDrillerBus.h Debug/TraceReflection.cpp Debug/TraceReflection.h + DOM/DomVisitor.cpp + DOM/DomVisitor.h Driller/DefaultStringPool.h Driller/Driller.cpp Driller/Driller.h From 30c366366ed9892fa79a47ec6b7a44dc81cb4dc3 Mon Sep 17 00:00:00 2001 From: Vincent Liu <5900509+onecent1101@users.noreply.github.com> Date: Wed, 27 Oct 2021 09:56:30 -0700 Subject: [PATCH 066/120] Improve gamelift unit test by checking handler invocation (#5030) Signed-off-by: onecent1101 --- .../Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp index be72de555e..bc147fe7f6 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp @@ -108,7 +108,7 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallWithoutClientSe MatchmakingNotificationsHandlerMock matchmakingHandlerMock; AZ_TEST_START_TRACE_SUPPRESSION; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish([](){ return ::UnitTest::TestRunner::Instance().m_numAssertsFailed == 1; }); + WaitForProcessFinish([&](){ return matchmakingHandlerMock.m_numMatchError == 1; }); AZ_TEST_STOP_TRACE_SUPPRESSION(1); ASSERT_TRUE(matchmakingHandlerMock.m_numMatchError == 1); ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); @@ -122,7 +122,7 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_MultipleCallsWithou AZ_TEST_START_TRACE_SUPPRESSION; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish([](){ return ::UnitTest::TestRunner::Instance().m_numAssertsFailed == 1; }); + WaitForProcessFinish([&](){ return matchmakingHandlerMock.m_numMatchError == 1; }); AZ_TEST_STOP_TRACE_SUPPRESSION(1); ASSERT_TRUE(matchmakingHandlerMock.m_numMatchError == 1); ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); @@ -140,7 +140,7 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButWithFailedOu MatchmakingNotificationsHandlerMock matchmakingHandlerMock; AZ_TEST_START_TRACE_SUPPRESSION; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish([](){ return ::UnitTest::TestRunner::Instance().m_numAssertsFailed == 1; }); + WaitForProcessFinish([&](){ return matchmakingHandlerMock.m_numMatchError == 1; }); AZ_TEST_STOP_TRACE_SUPPRESSION(1); ASSERT_TRUE(matchmakingHandlerMock.m_numMatchError == 1); ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); @@ -160,7 +160,7 @@ TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallWithMoreThanOne MatchmakingNotificationsHandlerMock matchmakingHandlerMock; AZ_TEST_START_TRACE_SUPPRESSION; m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); - WaitForProcessFinish([](){ return ::UnitTest::TestRunner::Instance().m_numAssertsFailed == 1; }); + WaitForProcessFinish([&](){ return matchmakingHandlerMock.m_numMatchError == 1; }); AZ_TEST_STOP_TRACE_SUPPRESSION(1); ASSERT_TRUE(matchmakingHandlerMock.m_numMatchError == 1); ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); From b7b79efd6938c0c7aa9622acb9ca481e46f4da99 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Wed, 27 Oct 2021 10:17:27 -0700 Subject: [PATCH 067/120] fixes monolithic vs non-monolithic installation (generates, monolithic doesnt build) Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- .../RPI.Reflect/Material/MaterialAsset.cpp | 2 +- cmake/LYWrappers.cmake | 7 ++-- cmake/Platform/Common/Install_common.cmake | 39 ++++++++++--------- .../install/ConfigurationType_config.cmake.in | 2 +- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index e9d8a42641..36f4947e3d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -208,7 +208,7 @@ namespace AZ return; } - const uint32_t originalVersion = m_materialTypeVersion; + [[maybe_unused]] const uint32_t originalVersion = m_materialTypeVersion; bool changesWereApplied = false; diff --git a/cmake/LYWrappers.cmake b/cmake/LYWrappers.cmake index fb3d420c26..c617603fa3 100644 --- a/cmake/LYWrappers.cmake +++ b/cmake/LYWrappers.cmake @@ -403,7 +403,7 @@ function(ly_target_link_libraries TARGET) message(FATAL_ERROR "You must provide a target") endif() - set_property(GLOBAL APPEND PROPERTY LY_DELAYED_LINK_${TARGET} ${ARGN}) + set_property(TARGET ${TARGET} APPEND PROPERTY LY_DELAYED_LINK ${ARGN}) set_property(GLOBAL APPEND PROPERTY LY_DELAYED_LINK_TARGETS ${TARGET}) # to walk them at the end endfunction() @@ -430,7 +430,7 @@ function(ly_delayed_target_link_libraries) get_property(delayed_targets GLOBAL PROPERTY LY_DELAYED_LINK_TARGETS) foreach(target ${delayed_targets}) - get_property(delayed_link GLOBAL PROPERTY LY_DELAYED_LINK_${target}) + get_property(delayed_link TARGET ${target} PROPERTY LY_DELAYED_LINK) if(delayed_link) cmake_parse_arguments(ly_delayed_target_link_libraries "" "" "${visibilities}" ${delayed_link}) @@ -458,9 +458,8 @@ function(ly_delayed_target_link_libraries) endforeach() endforeach() - set_property(GLOBAL PROPERTY LY_DELAYED_LINK_${target}) - endif() + endif() endforeach() set_property(GLOBAL PROPERTY LY_DELAYED_LINK_TARGETS) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 622100b4d8..8a06d1b584 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -85,6 +85,16 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar continue() endif() + # For some cases (e.g. codegen) we generate headers that end up in the BUILD_DIR. Since the BUILD_DIR + # is per-permutation, we need to install such headers per permutation. For the other cases, we can install + # under the default component since they are shared across permutations/configs. + cmake_path(IS_PREFIX CMAKE_BINARY_DIR ${include_directory} NORMALIZE include_directory_child_of_build) + if(NOT include_directory_child_of_build) + set(include_directory_component ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) + else() + set(include_directory_component ${LY_INSTALL_PERMUTATION_COMPONENT}) + endif() + unset(rel_include_dir) cmake_path(RELATIVE_PATH include_directory BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE rel_include_dir) cmake_path(APPEND rel_include_dir "..") @@ -92,7 +102,7 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar ly_install(DIRECTORY ${include_directory} DESTINATION ${destination_dir} - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} + COMPONENT ${include_directory_component} FILES_MATCHING PATTERN *.h PATTERN *.hpp @@ -203,24 +213,13 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar endif() string(REPEAT " " 12 PLACEHOLDER_INDENT) - get_target_property(inteface_build_dependencies_props ${TARGET_NAME} INTERFACE_LINK_LIBRARIES) + get_property(inteface_build_dependencies_props TARGET ${TARGET_NAME} PROPERTY LY_DELAYED_LINK) unset(INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER) if(inteface_build_dependencies_props) - foreach(build_dependency ${inteface_build_dependencies_props}) + cmake_parse_arguments(build_deps "" "" "PRIVATE;PUBLIC;INTERFACE" ${inteface_build_dependencies_props}) + foreach(build_dependency IN LISTS build_deps_INTERFACE build_deps_PUBLIC) # Skip wrapping produced when targets are not created in the same directory - if(NOT ${build_dependency} MATCHES "^::@") - list(APPEND INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${PLACEHOLDER_INDENT}${build_dependency}") - endif() - endforeach() - endif() - # We also need to pass the private link libraries since we will use that to generate the runtime dependencies - get_target_property(private_build_dependencies_props ${TARGET_NAME} LINK_LIBRARIES) - if(private_build_dependencies_props) - foreach(build_dependency ${private_build_dependencies_props}) - # Skip wrapping produced when targets are not created in the same directory - if(NOT ${build_dependency} MATCHES "^::@") - list(APPEND INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${PLACEHOLDER_INDENT}${build_dependency}") - endif() + list(APPEND INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${PLACEHOLDER_INDENT}${build_dependency}") endforeach() endif() list(REMOVE_DUPLICATES INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER) @@ -353,7 +352,7 @@ include(Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cm file(CONFIGURE OUTPUT "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake" CONTENT [[ @cmake_copyright_comment@ if(LY_MONOLITHIC_GAME) - include(Platform/${PAL_PLATFORM_NAME}/Monolithic/permutation.cmake) + include(Platform/${PAL_PLATFORM_NAME}/Monolithic/permutation.cmake OPTIONAL) else() include(Platform/${PAL_PLATFORM_NAME}/Default/permutation.cmake) endif() @@ -394,6 +393,7 @@ function(ly_setup_cmake_install) COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} PATTERN "__pycache__" EXCLUDE PATTERN "Findo3de.cmake" EXCLUDE + PATTERN "cmake/ConfigurationTypes.cmake" EXCLUDE REGEX "3rdParty/Platform\/.*\/BuiltInPackages_.*\.cmake" EXCLUDE ) @@ -739,7 +739,7 @@ function(ly_setup_o3de_install) # Misc ly_install(FILES - ${LY_ROOT_FOLDER}/ctest_pytest.ini + ${LY_ROOT_FOLDER}/pytest.ini ${LY_ROOT_FOLDER}/LICENSE.txt ${LY_ROOT_FOLDER}/README.md DESTINATION . @@ -750,7 +750,8 @@ function(ly_setup_o3de_install) foreach(external_dir ${LY_INSTALL_EXTERNAL_BUILD_DIRS}) ly_install(CODE "set(LY_CORE_COMPONENT_ALREADY_INCLUDED TRUE) -include(${external_dir}/cmake_install.cmake)" +include(${external_dir}/cmake_install.cmake) +set(LY_CORE_COMPONENT_ALREADY_INCLUDED FALSE)" ALL_COMPONENTS ) endforeach() diff --git a/cmake/install/ConfigurationType_config.cmake.in b/cmake/install/ConfigurationType_config.cmake.in index 074e034899..0a6940d7ff 100644 --- a/cmake/install/ConfigurationType_config.cmake.in +++ b/cmake/install/ConfigurationType_config.cmake.in @@ -8,4 +8,4 @@ include_guard(GLOBAL) -list(APPEND CMAKE_CONFIGURATION_TYPES @CMAKE_INSTALL_CONFIG_NAME@) +list(APPEND CMAKE_CONFIGURATION_TYPES @conf@) From faa87f56c9879f7190d10b6608d129af52eb6977 Mon Sep 17 00:00:00 2001 From: rhhong Date: Wed, 27 Oct 2021 10:18:24 -0700 Subject: [PATCH 068/120] Fix build error Signed-off-by: rhhong --- .../Code/Tools/EMStudio/AnimViewportWidget.cpp | 7 +++---- .../EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp index 42bc154fdf..c6e349236f 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp @@ -20,9 +20,6 @@ namespace EMStudio { - static constexpr float DepthNear = 0.01f; - static constexpr float DepthFar = 100.0f; - AnimViewportWidget::AnimViewportWidget(QWidget* parent) : AtomToolsFramework::RenderViewportWidget(parent) { @@ -180,7 +177,9 @@ namespace EMStudio { auto viewportContext = GetViewportContext(); auto windowSize = viewportContext->GetViewportSize(); - const float aspectRatio = aznumeric_cast(windowSize.m_width) / aznumeric_cast(windowSize.m_height); + // Prevent devided by zero + const float height = AZStd::max(aznumeric_cast(windowSize.m_height), 1.0f); + const float aspectRatio = aznumeric_cast(windowSize.m_width) / height; AZ::Matrix4x4 viewToClipMatrix; AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, AZ::Constants::HalfPi, aspectRatio, DepthNear, DepthFar, true); diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h index 5a193d31f1..e2099ea2ab 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h @@ -45,6 +45,8 @@ namespace EMStudio void ToggleRenderFlag(EMotionFX::ActorRenderFlag flag); static constexpr float CameraDistance = 2.0f; + static constexpr float DepthNear = 0.01f; + static constexpr float DepthFar = 100.0f; AZStd::unique_ptr m_renderer; AZStd::shared_ptr m_rotateCamera; From e6650f1ff4723c236f4693e37488e12fdf046db2 Mon Sep 17 00:00:00 2001 From: Gene Walters <32776221+AMZN-Gene@users.noreply.github.com> Date: Wed, 27 Oct 2021 10:38:09 -0700 Subject: [PATCH 069/120] LYN-7655 Fix Race Condition When Launching at Editor-Server (#4946) * Fix a race condition where the editor tries to connect to the editor-server before the editor-server is ready (originally discovered on lower-spec Jenkin machines). Change editor-server so that editor waits to receive a EditorServerReadyForInit before trying to send all the level data. * The editor might not be the connector so make sure to connect to the actual MP simulation even if the editor isn't the editor-server connect (if editorsv_launch=true then the editor-server will connect to the editor) * Adding warnings if MPEditorConnection cannot find certain cvars Signed-off-by: Gene Walters --- .../Multiplayer/MultiplayerEditorServerBus.h | 27 ++++ .../AutoGen/MultiplayerEditor.AutoPackets.xml | 8 +- .../Editor/MultiplayerEditorConnection.cpp | 112 +++++++------- .../Editor/MultiplayerEditorConnection.h | 9 +- .../MultiplayerEditorSystemComponent.cpp | 140 +++++++++++++----- .../Editor/MultiplayerEditorSystemComponent.h | 8 +- Gems/Multiplayer/Code/multiplayer_files.cmake | 1 + 7 files changed, 205 insertions(+), 100 deletions(-) create mode 100644 Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerEditorServerBus.h diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerEditorServerBus.h b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerEditorServerBus.h new file mode 100644 index 0000000000..1c6d2152bb --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerEditorServerBus.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +namespace Multiplayer +{ + class MultiplayerEditorServerRequests : public AZ::EBusTraits + { + public: + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + + //! Sends a packet that initializes a local server launched from the editor. + //! The editor will package the data required for loading the current editor level on the editor-server; data includes entities and asset data. + //! @param connection The connection to the editor-server + virtual void SendEditorServerLevelDataPacket(AzNetworking::IConnection* connection) = 0; + }; + using MultiplayerEditorServerRequestBus = AZ::EBus; +} // namespace Multiplayer diff --git a/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml index dd553a2413..b8b880d03c 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml @@ -4,13 +4,15 @@ - - + + + + - + diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index a30ab207b4..d2b0ca7095 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -17,7 +18,6 @@ #include #include #include -#include #include #include @@ -35,13 +35,28 @@ namespace Multiplayer m_networkEditorInterface->SetTimeoutMs(AZ::TimeMs{ 0 }); // Disable timeouts on this network interface if (editorsv_isDedicated) { - uint16_t editorServerPort = DefaultServerEditorPort; - if (auto console = AZ::Interface::Get(); console) + uint16_t editorsv_port = DefaultServerEditorPort; + const auto console = AZ::Interface::Get(); + if (console->GetCvarValue("editorsv_port", editorsv_port) != AZ::GetValueResult::Success) { - console->GetCvarValue("editorsv_port", editorServerPort); + AZ_Assert( false, + "MultiplayerEditorConnection failed! Could not find the editorsv_port cvar; we may not be able to connect to the editor's port! Please update this code to use a valid cvar!") + } + + AZ_Assert(m_networkEditorInterface, "MP Editor Network Interface was unregistered before Editor Server could start listening.") + + // Check if there's already an Editor out there waiting to connect + const ConnectionId editorServerToEditorConnectionId = m_networkEditorInterface->Connect(IpAddress(LocalHost.data(), editorsv_port, ProtocolType::Tcp)); + + // If there wasn't an Editor waiting for this server to start, then assume this is an editor-server launched by hand... listen and wait for the editor to request a connection + if (editorServerToEditorConnectionId == InvalidConnectionId) + { + m_networkEditorInterface->Listen(editorsv_port); + } + else + { + m_networkEditorInterface->SendReliablePacket(editorServerToEditorConnectionId, MultiplayerEditorPackets::EditorServerReadyForLevelData()); } - AZ_Assert(m_networkEditorInterface, "MP Editor Network Interface was unregistered before Editor Server could start listening."); - m_networkEditorInterface->Listen(editorServerPort); } } @@ -49,7 +64,7 @@ namespace Multiplayer ( [[maybe_unused]] AzNetworking::IConnection* connection, [[maybe_unused]] const IPacketHeader& packetHeader, - [[maybe_unused]] MultiplayerEditorPackets::EditorServerInit& packet + [[maybe_unused]] MultiplayerEditorPackets::EditorServerLevelData& packet ) { // Editor Server Init is intended for non-release targets @@ -76,7 +91,7 @@ namespace Multiplayer AZ::Data::AssetData* assetDatum = AZ::Utils::LoadObjectFromStream(m_byteStream, nullptr); if (!assetDatum) { - AZLOG_ERROR("EditorServerInit packet contains no asset data. Asset: %s", assetHint.c_str()); + AZLOG_ERROR("EditorServerLevelData packet contains no asset data. Asset: %s", assetHint.c_str()) return false; } assetSize = m_byteStream.GetCurPos() - assetSize; @@ -105,18 +120,21 @@ namespace Multiplayer // Load the level via the root spawnable that was registered const AZ::CVarFixedString loadLevelString = "LoadLevel Root.spawnable"; - AZ::Interface::Get()->PerformCommand(loadLevelString.c_str()); + const auto console = AZ::Interface::Get(); + console->PerformCommand(loadLevelString.c_str()); // Setup the normal multiplayer connection AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::DedicatedServer); INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpNetworkInterfaceName)); - uint16_t serverPort = DefaultServerPort; - if (auto console = AZ::Interface::Get(); console) + uint16_t sv_port = DefaultServerPort; + if (console->GetCvarValue("sv_port", sv_port) != AZ::GetValueResult::Success) { - console->GetCvarValue("sv_port", serverPort); + AZ_Assert(false, + "MultiplayerEditorConnection::HandleRequest for EditorServerLevelData failed! Could not find the sv_port cvar; we won't be able to listen on the correct port for incoming network messages! Please update this code to use a valid cvar!") } - networkInterface->Listen(serverPort); + + networkInterface->Listen(sv_port); AZLOG_INFO("Editor Server completed asset receive, responding to Editor..."); return connection->SendReliablePacket(MultiplayerEditorPackets::EditorServerReady()); @@ -125,6 +143,15 @@ namespace Multiplayer return true; } + bool MultiplayerEditorConnection::HandleRequest( + [[maybe_unused]] AzNetworking::IConnection* connection, + [[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader, + [[maybe_unused]] MultiplayerEditorPackets::EditorServerReadyForLevelData& packet) + { + MultiplayerEditorServerRequestBus::Broadcast(&MultiplayerEditorServerRequestBus::Events::SendEditorServerLevelDataPacket, connection); + return true; + } + bool MultiplayerEditorConnection::HandleRequest ( [[maybe_unused]] AzNetworking::IConnection* connection, @@ -132,23 +159,29 @@ namespace Multiplayer [[maybe_unused]] MultiplayerEditorPackets::EditorServerReady& packet ) { - if (connection->GetConnectionRole() == ConnectionRole::Connector) - { - // Receiving this packet means Editor sync is done, disconnect - connection->Disconnect(AzNetworking::DisconnectReason::TerminatedByClient, AzNetworking::TerminationEndpoint::Local); + // Receiving this packet means Editor sync is done, disconnect + connection->Disconnect(AzNetworking::DisconnectReason::TerminatedByClient, AzNetworking::TerminationEndpoint::Local); + const auto console = AZ::Interface::Get(); + AZ::CVarFixedString editorsv_serveraddr = AZ::CVarFixedString(LocalHost); + uint16_t sv_port = DefaultServerEditorPort; - if (auto console = AZ::Interface::Get(); console) - { - AZ::CVarFixedString remoteAddress; - uint16_t remotePort; - if (console->GetCvarValue("editorsv_serveraddr", remoteAddress) != AZ::GetValueResult::ConsoleVarNotFound && - console->GetCvarValue("sv_port", remotePort) != AZ::GetValueResult::ConsoleVarNotFound) - { - // Connect the Editor to the editor server for Multiplayer simulation - AZ::Interface::Get()->Connect(remoteAddress.c_str(), remotePort); - } - } + if (console->GetCvarValue("sv_port", sv_port) != AZ::GetValueResult::Success) + { + AZ_Assert(false, + "MultiplayerEditorConnection::HandleRequest for EditorServerReady failed! Could not find the sv_port cvar; we may not be able to " + "connect to the correct port for incoming network messages! Please update this code to use a valid cvar!") } + + if (console->GetCvarValue("editorsv_serveraddr", editorsv_serveraddr) != AZ::GetValueResult::Success) + { + AZ_Assert(false, + "MultiplayerEditorConnection::HandleRequest for EditorServerReady failed! Could not find the editorsv_serveraddr cvar; we may not be able to " + "connect to the correct port for incoming network messages! Please update this code to use a valid cvar!") + } + + // Connect the Editor to the editor server for Multiplayer simulation + AZ::Interface::Get()->Connect(editorsv_serveraddr.c_str(), sv_port); + return true; } @@ -171,26 +204,5 @@ namespace Multiplayer { return MultiplayerEditorPackets::DispatchPacket(connection, packetHeader, serializer, *this); } - - void MultiplayerEditorConnection::OnPacketLost([[maybe_unused]] IConnection* connection, [[maybe_unused]] PacketId packetId) - { - ; - } - - void MultiplayerEditorConnection::OnDisconnect([[maybe_unused]] AzNetworking::IConnection* connection, [[maybe_unused]] DisconnectReason reason, [[maybe_unused]] TerminationEndpoint endpoint) - { - bool editorLaunch = false; - if (auto console = AZ::Interface::Get(); console) - { - console->GetCvarValue("editorsv_launch", editorLaunch); - } - - if (editorsv_isDedicated && editorLaunch && m_networkEditorInterface->GetConnectionSet().GetConnectionCount() == 1) - { - if (m_networkEditorInterface->GetPort() != 0) - { - m_networkEditorInterface->StopListening(); - } - } - } + } diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h index b93c830cd0..3828d751f0 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h @@ -10,8 +10,6 @@ #include -#include -#include #include #include #include @@ -33,7 +31,8 @@ namespace Multiplayer MultiplayerEditorConnection(); ~MultiplayerEditorConnection() = default; - bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerInit& packet); + bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerReadyForLevelData& packet); + bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerLevelData& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerReady& packet); //! IConnectionListener interface @@ -41,8 +40,8 @@ namespace Multiplayer AzNetworking::ConnectResult ValidateConnect(const AzNetworking::IpAddress& remoteAddress, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override; void OnConnect(AzNetworking::IConnection* connection) override; AzNetworking::PacketDispatchResult OnPacketReceived(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override; - void OnPacketLost(AzNetworking::IConnection* connection, AzNetworking::PacketId packetId) override; - void OnDisconnect(AzNetworking::IConnection* connection, AzNetworking::DisconnectReason reason, AzNetworking::TerminationEndpoint endpoint) override; + void OnPacketLost([[maybe_unused]]AzNetworking::IConnection* connection, [[maybe_unused]]AzNetworking::PacketId packetId) override {} + void OnDisconnect([[maybe_unused]]AzNetworking::IConnection* connection, [[maybe_unused]]AzNetworking::DisconnectReason reason, [[maybe_unused]]AzNetworking::TerminationEndpoint endpoint) override {} //! @} private: diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index 55a05e33a7..2651981bce 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -71,6 +71,7 @@ namespace Multiplayer { AzFramework::GameEntityContextEventBus::Handler::BusConnect(); AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + MultiplayerEditorServerRequestBus::Handler::BusConnect(); AZ::Interface::Get()->AddServerAcceptanceReceivedHandler(m_serverAcceptanceReceivedHandler); } @@ -78,6 +79,7 @@ namespace Multiplayer { AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); AzFramework::GameEntityContextEventBus::Handler::BusDisconnect(); + MultiplayerEditorServerRequestBus::Handler::BusDisconnect(); } void MultiplayerEditorSystemComponent::NotifyRegisterViews() @@ -107,8 +109,8 @@ namespace Multiplayer m_serverProcess->TerminateProcess(0); m_serverProcess = nullptr; } - INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName)); - if (editorNetworkInterface) + + if (INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName))) { editorNetworkInterface->Disconnect(m_editorConnId, AzNetworking::DisconnectReason::TerminatedByClient); } @@ -191,53 +193,49 @@ namespace Multiplayer } const AZ::CVarFixedString remoteAddress = editorsv_serveraddr; - if (editorsv_launch && LocalHost == remoteAddress) + if (editorsv_launch) { + if (LocalHost != remoteAddress) + { + AZ_Warning( + "MultiplayerEditor", false, + "Launching EditorServer skipped because incompatible cvars. editorsv_launch=true, meaning you want to launch an editor-server on this machine, but the editorsv_serveraddr is %s instead of the local address (127.0.0.1). " + "Please either set editorsv_launch=false and keep the remote editor-server, or set editorsv_launch=true and editorsv_serveraddr=127.0.0.1.", + remoteAddress.c_str()) + return; + } + + // Begin listening for MPEditor packets before we launch the editor-server. + // The editor-server will send us (the editor) an "EditorServerReadyForLevelData" packet to let us know it's ready to receive data. + INetworkInterface* editorNetworkInterface = + AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName)); + AZ_Assert(editorNetworkInterface, "MP Editor Network Interface was unregistered before Editor could connect."); + editorNetworkInterface->Listen(editorsv_port); + + // Launch the editor-server m_serverProcess = LaunchEditorServer(); } - - // Spawnable library needs to be rebuilt since now we have newly registered in-memory spawnable assets - AZ::Interface::Get()->BuildSpawnablesList(); - - // Now that the server has launched, attempt to connect the NetworkInterface - INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName)); - AZ_Assert(editorNetworkInterface, "MP Editor Network Interface was unregistered before Editor could connect."); - m_editorConnId = editorNetworkInterface->Connect( - AzNetworking::IpAddress(remoteAddress.c_str(), editorsv_port, AzNetworking::ProtocolType::Tcp)); - - if (m_editorConnId == AzNetworking::InvalidConnectionId) + else { - AZ_Warning( - "MultiplayerEditor", false, - "Could not connect to server targeted by Editor. If using a local server, check that it's built and editorsv_launch is true."); - return; - } + // Editorsv_launch=false, so we're expecting an editor-server already exists. + // Connect to the editor-server and then send the EditorServerLevelData packet. + INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName)); + AZ_Assert(editorNetworkInterface, "MP Editor Network Interface was unregistered before Editor could connect.") + + m_editorConnId = editorNetworkInterface->Connect(AzNetworking::IpAddress(remoteAddress.c_str(), editorsv_port, AzNetworking::ProtocolType::Tcp)); - // Read the buffer into EditorServerInit packets until we've flushed the whole thing - byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); - - while (byteStream.GetCurPos() < byteStream.GetLength()) - { - MultiplayerEditorPackets::EditorServerInit packet; - auto& outBuffer = packet.ModifyAssetData(); - - // Size the packet's buffer appropriately - size_t readSize = outBuffer.GetCapacity(); - size_t byteStreamSize = byteStream.GetLength() - byteStream.GetCurPos(); - if (byteStreamSize < readSize) + if (m_editorConnId == AzNetworking::InvalidConnectionId) { - readSize = byteStreamSize; + AZ_Warning( + "MultiplayerEditor", false, + "Editor multiplayer game-mode failed! Could not connect to an editor-server. editorsv_launch is false so we're assuming you're running your own editor-server at editorsv_serveraddr(%s) on editorsv_port(%i). " + "Either set editorsv_launch=true so the editor launches an editor-server for you, or launch your own editor-server by hand before entering game-mode. Remember editor-servers must use editorsv_isDedicated=true.", + remoteAddress.c_str(), + static_cast < uint16_t>(editorsv_port)) + return; } - outBuffer.Resize(readSize); - byteStream.Read(readSize, outBuffer.GetBuffer()); - - // If we've run out of buffer, mark that we're done - if (byteStream.GetCurPos() == byteStream.GetLength()) - { - packet.SetLastUpdate(true); - } - editorNetworkInterface->SendReliablePacket(m_editorConnId, packet); + SendEditorServerLevelDataPacket(editorNetworkInterface->GetConnectionSet().GetConnection(m_editorConnId)); } } } @@ -253,4 +251,64 @@ namespace Multiplayer // but since we're in Editor, we're already in the level. AZ::Interface::Get()->SendReadyForEntityUpdates(true); } + + void MultiplayerEditorSystemComponent::SendEditorServerLevelDataPacket(AzNetworking::IConnection* connection) + { + const auto prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); + if (!prefabEditorEntityOwnershipInterface) + { + AZ_Error("MultiplayerEditor", prefabEditorEntityOwnershipInterface != nullptr, "PrefabEditorEntityOwnershipInterface unavailable") + return; + } + + const AZStd::vector>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData(); + + AZStd::vector buffer; + AZ::IO::ByteContainerStream byteStream(&buffer); + + // Serialize Asset information and AssetData into a potentially large buffer + for (const auto& asset : assetData) + { + AZ::Data::AssetId assetId = asset.GetId(); + AZStd::string assetHint = asset.GetHint(); + auto hintSize = aznumeric_cast(assetHint.size()); + + byteStream.Write(sizeof(AZ::Data::AssetId), reinterpret_cast(&assetId)); + byteStream.Write(sizeof(uint32_t), reinterpret_cast(&hintSize)); + byteStream.Write(assetHint.size(), assetHint.data()); + AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType()); + } + + // Spawnable library needs to be rebuilt since now we have newly registered in-memory spawnable assets + AZ::Interface::Get()->BuildSpawnablesList(); + + // Read the buffer into EditorServerLevelData packets until we've flushed the whole thing + byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); + + while (byteStream.GetCurPos() < byteStream.GetLength()) + { + MultiplayerEditorPackets::EditorServerLevelData editorServerLevelDataPacket; + auto& outBuffer = editorServerLevelDataPacket.ModifyAssetData(); + + // Size the packet's buffer appropriately + size_t readSize = outBuffer.GetCapacity(); + const size_t byteStreamSize = byteStream.GetLength() - byteStream.GetCurPos(); + if (byteStreamSize < readSize) + { + readSize = byteStreamSize; + } + + outBuffer.Resize(readSize); + byteStream.Read(readSize, outBuffer.GetBuffer()); + + // If we've run out of buffer, mark that we're done + if (byteStream.GetCurPos() == byteStream.GetLength()) + { + editorServerLevelDataPacket.SetLastUpdate(true); + } + + connection->SendReliablePacket(editorServerLevelDataPacket); + } + } + } diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h index 4e4c6b677f..a008afc873 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h @@ -9,7 +9,7 @@ #pragma once #include - +#include #include #include @@ -35,6 +35,7 @@ namespace Multiplayer , private AzFramework::GameEntityContextEventBus::Handler , private AzToolsFramework::EditorEvents::Bus::Handler , private IEditorNotifyListener + , private MultiplayerEditorServerRequestBus::Handler { public: AZ_COMPONENT(MultiplayerEditorSystemComponent, "{9F335CC0-5574-4AD3-A2D8-2FAEF356946C}"); @@ -73,6 +74,11 @@ namespace Multiplayer void OnGameEntitiesReset() override; //! @} + //! MultiplayerEditorServerRequestBus::Handler + //! @{ + void SendEditorServerLevelDataPacket(AzNetworking::IConnection* connection) override; + //! @} + IEditor* m_editor = nullptr; AzFramework::ProcessWatcher* m_serverProcess = nullptr; AzNetworking::ConnectionId m_editorConnId; diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index a799278203..1376083443 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -13,6 +13,7 @@ set(FILES Include/Multiplayer/MultiplayerConstants.h Include/Multiplayer/MultiplayerStats.h Include/Multiplayer/MultiplayerTypes.h + Include/Multiplayer/MultiplayerEditorServerBus.h Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h Include/Multiplayer/Components/MultiplayerComponent.h Include/Multiplayer/Components/MultiplayerComponentRegistry.h From 9e0756f3c11ae5218246fec732e1ceb4da14caeb Mon Sep 17 00:00:00 2001 From: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com> Date: Wed, 27 Oct 2021 11:28:21 -0700 Subject: [PATCH 070/120] ATOM-16656 PassTree tool: ParentPass image attachment preview doesn't work (#5032) Move imageAttachmentCopy instance from RenderPass to Pass so it can support preview image for all passes but not only for RenderPass. Fixed an issue with image attachment preview when switching render pipeline with attachment preview on. Signed-off-by: Qing Tao --- .../Source/FrameCaptureSystemComponent.cpp | 2 +- .../Code/Include/Atom/RPI.Public/Pass/Pass.h | 10 ++ .../Include/Atom/RPI.Public/Pass/RenderPass.h | 7 - .../Specific/ImageAttachmentPreviewPass.h | 2 +- .../RPI/Code/Source/RPI.Public/Pass/Pass.cpp | 18 +++ .../Source/RPI.Public/Pass/RenderPass.cpp | 12 +- .../Specific/ImageAttachmentPreviewPass.cpp | 4 +- .../Code/Include/Atom/Utils/ImGuiPassTree.h | 2 + .../Code/Include/Atom/Utils/ImGuiPassTree.inl | 138 +++++++++++------- 9 files changed, 118 insertions(+), 77 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp index ed2d8a1d9f..a9bb7271ab 100644 --- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h index e7b9825ecb..be7ac9e7a4 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ namespace AZ struct PassRequest; struct PassValidationResults; class AttachmentReadback; + class ImageAttachmentCopy; using SortedPipelineViewTags = AZStd::set; using PassesByDrawList = AZStd::map; @@ -94,6 +96,8 @@ namespace AZ { AZ_RPI_PASS(Pass); + friend class ImageAttachmentPreviewPass; + public: using ChildPassIndex = RHI::Handle; @@ -369,6 +373,9 @@ namespace AZ void UpdateReadbackAttachment(FramePrepareParams params, bool beforeAddScopes); + // Setup ImageAttachmentCopy + void UpdateAttachmentCopy(FramePrepareParams params); + // --- Protected Members --- const Name PassNameThis{"This"}; @@ -466,6 +473,9 @@ namespace AZ AZStd::shared_ptr m_attachmentReadback; PassAttachmentReadbackOption m_readbackOption; + // For image attachment preview + AZStd::weak_ptr m_attachmentCopy; + private: // Return the Timestamp result of this pass virtual TimestampResult GetTimestampResultInternal() const; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h index 5fb90d044e..ec51e34897 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h @@ -13,9 +13,7 @@ #include #include -#include #include -#include #include namespace AZ @@ -29,7 +27,6 @@ namespace AZ namespace RPI { - class ImageAttachmentCopy; class RenderPass; class Query; @@ -41,8 +38,6 @@ namespace AZ { AZ_RPI_PASS(RenderPass); - friend class ImageAttachmentPreviewPass; - using ScopeQuery = AZStd::array, static_cast(ScopeQueryType::Count)>; public: @@ -143,8 +138,6 @@ namespace AZ // Readback the results from the ScopeQueries void ReadbackScopeQueryResults(); - AZStd::weak_ptr m_attachmentCopy; - // Readback results from the Timestamp queries TimestampResult m_timestampResult; // Readback results from the PipelineStatistics queries diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h index 2e2b14a699..8ee7a44b6f 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h @@ -78,7 +78,7 @@ namespace AZ ~ImageAttachmentPreviewPass(); //! Preview the PassAttachment of a pass' PassAttachmentBinding - void PreviewImageAttachmentForPass(RenderPass* pass, const PassAttachment* passAttachment); + void PreviewImageAttachmentForPass(Pass* pass, const PassAttachment* passAttachment); //! Set the output color attachment for this pass void SetOutputColorAttachment(RHI::Ptr outputImageAttachment); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp index 3c1de28d6a..d04a35a10b 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -1215,6 +1216,12 @@ namespace AZ m_queueState = PassQueueState::NoQueue; InitializeInternal(); + + // Need to recreate the dest attachment because the source attachment might be changed + if (!m_attachmentCopy.expired()) + { + m_attachmentCopy.lock()->InvalidateDestImage(); + } m_state = PassState::Initialized; } @@ -1301,6 +1308,9 @@ namespace AZ // readback attachment with output state UpdateReadbackAttachment(params, false); + // update attachment copy for preview + UpdateAttachmentCopy(params); + UpdateConnectedOutputBindings(); } @@ -1489,6 +1499,14 @@ namespace AZ } } + void Pass::UpdateAttachmentCopy(FramePrepareParams params) + { + if (!m_attachmentCopy.expired()) + { + m_attachmentCopy.lock()->FrameBegin(params); + } + } + bool Pass::IsTimestampQueryEnabled() const { return m_flags.m_timestampQueryEnabled; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp index b8115dff10..8353762c0f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp @@ -177,12 +177,6 @@ namespace AZ } } } - - // Need to recreate the dest attachment because the source attachment might be changed - if (!m_attachmentCopy.expired()) - { - m_attachmentCopy.lock()->InvalidateDestImage(); - } } void RenderPass::FrameBeginInternal(FramePrepareParams params) @@ -196,11 +190,7 @@ namespace AZ // Read back the ScopeQueries submitted from previous frames ReadbackScopeQueryResults(); - - if (!m_attachmentCopy.expired()) - { - m_attachmentCopy.lock()->FrameBegin(params); - } + CollectSrgs(); PassSystemInterface::Get()->IncrementFrameRenderPassCount(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp index 105936f64d..8f83e4efe1 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -131,7 +131,7 @@ namespace AZ Data::AssetBus::Handler::BusDisconnect(); } - void ImageAttachmentPreviewPass::PreviewImageAttachmentForPass(RenderPass* pass, const PassAttachment* passAttachment) + void ImageAttachmentPreviewPass::PreviewImageAttachmentForPass(Pass* pass, const PassAttachment* passAttachment) { if (passAttachment->GetAttachmentType() != RHI::AttachmentType::Image) { diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.h index 12e9776ae2..0e942bca58 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.h +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.h @@ -39,6 +39,8 @@ namespace AZ bool m_showAttachments = false; AZ::RPI::Pass* m_selectedPass = nullptr; + AZ::RPI::Pass* m_lastSelectedPass = nullptr; + AZ::Name m_selectedPassPath; AZ::RHI::AttachmentId m_attachmentId; AZ::Name m_slotName; bool m_selectedChanged = false; diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.inl b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.inl index fa649ed47b..55e457926a 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.inl +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiPassTree.inl @@ -31,7 +31,7 @@ namespace AZ::Render { - inline AZ::RPI::PassAttachment* FindPassAttachment(AZ::RPI::RenderPass* pass, AZ::RHI::AttachmentId attachmentId) + inline AZ::RPI::PassAttachment* FindPassAttachment(AZ::RPI::Pass* pass, AZ::RHI::AttachmentId attachmentId) { for (auto& binding : pass->GetAttachmentBindings()) { @@ -47,6 +47,10 @@ namespace AZ::Render { using namespace AZ; + // always set m_selectedPass to empty and use m_selectedPassPath to find it when render the pass tree + m_selectedPass = nullptr; + bool needSaveAttachment = false; + ImGui::SetNextWindowSize(ImVec2(200.f, 200.f), ImGuiCond_FirstUseEver); if (ImGui::Begin("PassTree View", &draw, ImGuiWindowFlags_None)) { @@ -83,60 +87,16 @@ namespace AZ::Render if (Scriptable_ImGui::Button("Save Attachment")) { - m_attachmentReadbackInfo = ""; - if (!m_readback) - { - m_readback = AZStd::make_shared(AZ::RHI::ScopeId{ "AttachmentReadback" }); - m_readback->SetCallback(AZStd::bind(&ImGuiPassTree::ReadbackCallback, this, AZStd::placeholders::_1)); - } - - if (m_selectedPass && !m_slotName.IsEmpty()) - { - bool readbackResult = m_selectedPass->ReadbackAttachment(m_readback, m_slotName); - if (!readbackResult) - { - AZ_Error("ImGuiPassTree", false, "Failed to readback attachment from pass [%s] slot [%s]", m_selectedPass->GetName().GetCStr(), m_slotName.GetCStr()); - } - } + needSaveAttachment = true; } ImGui::TextWrapped("%s", m_attachmentReadbackInfo.c_str()); } - if (m_previewAttachment && m_selectedChanged) - { - m_selectedChanged = false; - if (!m_attachmentId.IsEmpty() && m_selectedPass) - { - AZ::RPI::RenderPass* renderPass = azrtti_cast(m_selectedPass); - if (renderPass) - { - if (!m_previewPass->GetParent()) - { - RPI::PassSystemInterface::Get()->GetRootPass()->AddChild(m_previewPass); - } - AZ::RPI::PassAttachment* attachment = FindPassAttachment(renderPass, m_attachmentId); - if (attachment) - { - // Reset output attachment to empty so the preview will use pass's owner render pipeline's output - m_previewPass->SetOutputColorAttachment(nullptr); - m_previewPass->PreviewImageAttachmentForPass(renderPass, attachment); - } - } - else - { - m_previewPass->ClearPreviewAttachment(); - if (m_previewPass->GetParent()) - { - m_previewPass->QueueForRemoval(); - } - } - } - } - ImGui::End(); // Draw the hierarchical view + // It will assign m_seletedPass if there is a pass matches m_seletedPassPath ImGui::SetNextWindowPos(ImVec2(300, 60), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(300, 500), ImGuiCond_FirstUseEver); if (ImGui::Begin("PassTree", nullptr, ImGuiWindowFlags_None)) @@ -144,6 +104,63 @@ namespace AZ::Render DrawTreeView(rootPass); } ImGui::End(); + + // It's possible that the pass pointer changed but selected pass path wasn't changed + if (m_selectedPass != m_lastSelectedPass) + { + m_selectedChanged = true; + if (m_selectedPass == nullptr) + { + m_selectedPassPath = AZ::Name{}; + } + } + m_lastSelectedPass = m_selectedPass; + + if (m_previewAttachment && m_selectedChanged) + { + m_selectedChanged = false; + if (!m_attachmentId.IsEmpty() && m_selectedPass) + { + if (!m_previewPass->GetParent()) + { + RPI::PassSystemInterface::Get()->GetRootPass()->AddChild(m_previewPass); + } + AZ::RPI::PassAttachment* attachment = FindPassAttachment(m_selectedPass, m_attachmentId); + if (attachment) + { + // Reset output attachment to empty so the preview will use pass's owner render pipeline's output + m_previewPass->SetOutputColorAttachment(nullptr); + m_previewPass->PreviewImageAttachmentForPass(m_selectedPass, attachment); + } + } + else + { + m_previewPass->ClearPreviewAttachment(); + if (m_previewPass->GetParent()) + { + m_previewPass->QueueForRemoval(); + } + } + } + + if (needSaveAttachment) + { + m_attachmentReadbackInfo = ""; + if (!m_readback) + { + m_readback = AZStd::make_shared(AZ::RHI::ScopeId{ "AttachmentReadback" }); + m_readback->SetCallback(AZStd::bind(&ImGuiPassTree::ReadbackCallback, this, AZStd::placeholders::_1)); + } + + if (m_selectedPass && !m_slotName.IsEmpty()) + { + bool readbackResult = m_selectedPass->ReadbackAttachment(m_readback, m_slotName); + if (!readbackResult) + { + AZ_Error("ImGuiPassTree", false, "Failed to readback attachment from pass [%s] slot [%s]", m_selectedPass->GetName().GetCStr(), m_slotName.GetCStr()); + } + } + } } inline void ImGuiPassTree::DrawPassAttachments(AZ::RPI::Pass* pass) @@ -202,6 +219,7 @@ namespace AZ::Render if (Scriptable_ImGui::Selectable(label.c_str(), m_attachmentId == binding.m_attachment->GetAttachmentId())) { + m_selectedPassPath = pass->GetPathName(); m_selectedPass = pass; m_attachmentId = binding.m_attachment->GetAttachmentId(); m_slotName = binding.m_name; @@ -232,9 +250,9 @@ namespace AZ::Render if (!m_showAttachments) { // Only draw the leaf pass as selectable if we are not showing attachments as its children - if (Scriptable_ImGui::Selectable(pass->GetName().GetCStr(), m_selectedPass == pass)) + if (Scriptable_ImGui::Selectable(pass->GetName().GetCStr(), m_selectedPassPath == pass->GetPathName())) { - m_selectedPass = pass; + m_selectedPassPath = pass->GetPathName(); m_attachmentId = AZ::RHI::AttachmentId{}; m_slotName = AZ::Name{}; m_selectedChanged = true; @@ -244,13 +262,13 @@ namespace AZ::Render { // Draw the pass as a tree node which has attachments as its children ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_DefaultOpen - | ((m_selectedPass == pass) ? ImGuiTreeNodeFlags_Selected : 0); + | ((m_selectedPassPath == pass->GetPathName()) ? ImGuiTreeNodeFlags_Selected : 0); bool nodeOpen = Scriptable_ImGui::TreeNodeEx(pass->GetName().GetCStr(), flags); if (ImGui::IsItemClicked()) { - m_selectedPass = pass; + m_selectedPassPath = pass->GetPathName(); m_attachmentId = AZ::RHI::AttachmentId{}; m_slotName = AZ::Name{}; m_selectedChanged = true; @@ -259,7 +277,6 @@ namespace AZ::Render if (nodeOpen) { DrawPassAttachments(pass); - Scriptable_ImGui::TreePop(); } } @@ -268,13 +285,13 @@ namespace AZ::Render { // For a ParentPasse, draw it as a tree node ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_DefaultOpen - | ((m_selectedPass == pass) ? ImGuiTreeNodeFlags_Selected : 0); + | ((m_selectedPassPath == pass->GetPathName()) ? ImGuiTreeNodeFlags_Selected : 0); bool nodeOpen = ImGui::TreeNodeEx(pass->GetName().GetCStr(), flags); if (ImGui::IsItemClicked()) { - m_selectedPass = pass; + m_selectedPassPath = pass->GetPathName(); m_attachmentId = AZ::RHI::AttachmentId{}; m_slotName = AZ::Name{}; m_selectedChanged = true; @@ -282,7 +299,10 @@ namespace AZ::Render if (nodeOpen) { - DrawPassAttachments(pass); + if (m_showAttachments) + { + DrawPassAttachments(pass); + } for (const auto& child : asParent->GetChildren()) { DrawTreeView(child.get()); @@ -296,6 +316,12 @@ namespace AZ::Render { ImGui::PopStyleColor(); } + + // set m_selectedPass if pass path matches + if (pass->GetPathName() == m_selectedPassPath) + { + m_selectedPass = pass; + } } inline void ImGuiPassTree::ReadbackCallback(const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) @@ -364,7 +390,9 @@ namespace AZ::Render m_previewAttachment = false; m_showAttachments = false; + m_selectedPassPath = AZ::Name{}; m_selectedPass = nullptr; + m_lastSelectedPass = nullptr; m_attachmentId = AZ::RHI::AttachmentId{}; m_slotName = AZ::Name{}; m_selectedChanged = false; From 35467b63d9964f8c308c7a4c07b9373181c48b6c Mon Sep 17 00:00:00 2001 From: John Jones-Steele <82226755+jjjoness@users.noreply.github.com> Date: Wed, 27 Oct 2021 19:31:30 +0100 Subject: [PATCH 071/120] Unit tests for Heightfield collider (#5042) Signed-off-by: John Jones-Steele --- Gems/PhysX/Code/CMakeLists.txt | 13 ++ .../MockPhysXHeightfieldProviderComponent.h | 74 ++++++ ...ditorHeightfieldColliderComponentTests.cpp | 215 ++++++++++++++++++ .../PhysX/Code/physx_editor_tests_files.cmake | 1 + Gems/PhysX/Code/physx_mocks_files.cmake | 11 + 5 files changed, 314 insertions(+) create mode 100644 Gems/PhysX/Code/Mocks/PhysX/MockPhysXHeightfieldProviderComponent.h create mode 100644 Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp create mode 100644 Gems/PhysX/Code/physx_mocks_files.cmake diff --git a/Gems/PhysX/Code/CMakeLists.txt b/Gems/PhysX/Code/CMakeLists.txt index 1acfae3bfd..c59db45aa7 100644 --- a/Gems/PhysX/Code/CMakeLists.txt +++ b/Gems/PhysX/Code/CMakeLists.txt @@ -17,6 +17,7 @@ if(PAL_TRAIT_PHYSX_SUPPORTED) set(physx_dependency 3rdParty::PhysX) set(physx_files physx_files.cmake) set(physx_shared_files physx_shared_files.cmake) + set(physx_mock_files physx_mocks_files.cmake) set(physx_editor_files physx_editor_files.cmake) else() set(physx_files physx_unsupported_files.cmake) @@ -151,6 +152,17 @@ endif() # Tests ################################################################################ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + ly_add_target( + NAME PhysX.Mocks HEADERONLY + NAMESPACE Gem + OUTPUT_NAME PhysX.Mocks.Gem + FILES_CMAKE + physx_mocks_files.cmake + INCLUDE_DIRECTORIES + INTERFACE + Mocks + ) + ly_add_target( NAME PhysX.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} NAMESPACE Gem @@ -213,6 +225,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest AZ::AzToolsFrameworkTestCommon Gem::PhysX.Static + Gem::PhysX.Mocks Gem::PhysX.Editor.Static RUNTIME_DEPENDENCIES Gem::LmbrCentral.Editor diff --git a/Gems/PhysX/Code/Mocks/PhysX/MockPhysXHeightfieldProviderComponent.h b/Gems/PhysX/Code/Mocks/PhysX/MockPhysXHeightfieldProviderComponent.h new file mode 100644 index 0000000000..7e500881c0 --- /dev/null +++ b/Gems/PhysX/Code/Mocks/PhysX/MockPhysXHeightfieldProviderComponent.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class MockPhysXHeightfieldProviderComponent + : public AZ::Component + { + public: + AZ_COMPONENT(MockPhysXHeightfieldProviderComponent, "{C5F7CCCF-FDB2-40DF-992D-CF028F4A1B59}"); + + static void Reflect([[maybe_unused]] AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1); + } + } + + void Activate() override + { + } + + void Deactivate() override + { + } + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("PhysicsHeightfieldProviderService")); + } + + }; + + class MockPhysXHeightfieldProvider + : protected Physics::HeightfieldProviderRequestsBus::Handler + { + public: + MockPhysXHeightfieldProvider(AZ::EntityId entityId) + { + Physics::HeightfieldProviderRequestsBus::Handler::BusConnect(entityId); + } + + ~MockPhysXHeightfieldProvider() + { + Physics::HeightfieldProviderRequestsBus::Handler::BusDisconnect(); + } + + MOCK_CONST_METHOD0(GetHeightsAndMaterials, AZStd::vector()); + MOCK_CONST_METHOD0(GetHeightfieldGridSpacing, AZ::Vector2()); + MOCK_CONST_METHOD2(GetHeightfieldGridSize, void(int32_t&, int32_t&)); + MOCK_CONST_METHOD2(GetHeightfieldHeightBounds, void(float&, float&)); + MOCK_CONST_METHOD0(GetHeightfieldTransform, AZ::Transform()); + MOCK_CONST_METHOD0(GetMaterialList, AZStd::vector()); + MOCK_CONST_METHOD0(GetHeights, AZStd::vector()); + MOCK_CONST_METHOD1(UpdateHeights, AZStd::vector(const AZ::Aabb& dirtyRegion)); + MOCK_CONST_METHOD1(UpdateHeightsAndMaterials, AZStd::vector(const AZ::Aabb& dirtyRegion)); + MOCK_CONST_METHOD0(GetHeightfieldAabb, AZ::Aabb()); + }; + +} // namespace UnitTest diff --git a/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp new file mode 100644 index 0000000000..ef112d8623 --- /dev/null +++ b/Gems/PhysX/Code/Tests/EditorHeightfieldColliderComponentTests.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ::testing::NiceMock; +using ::testing::Return; + +namespace PhysXEditorTests +{ + AZStd::vector GetSamples() + { + AZStd::vector samples{ { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, + { 2.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, + { 1.5f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, + { 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, + { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, + { 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, + { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, + { 0.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight }, + { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight } }; + return samples; + } + + EntityPtr SetupHeightfieldComponent() + { + // create an editor entity with a shape collider component and a box shape component + EntityPtr editorEntity = CreateInactiveEditorEntity("HeightfieldColliderComponentEditorEntity"); + editorEntity->CreateComponent(); + editorEntity->CreateComponent(LmbrCentral::EditorAxisAlignedBoxShapeComponentTypeId); + editorEntity->CreateComponent(); + AZ::ComponentApplicationBus::Broadcast( + &AZ::ComponentApplicationRequests::RegisterComponentDescriptor, + UnitTest::MockPhysXHeightfieldProviderComponent::CreateDescriptor()); + return editorEntity; + } + + void CleanupHeightfieldComponent() + { + AZ::ComponentApplicationBus::Broadcast( + &AZ::ComponentApplicationRequests::UnregisterComponentDescriptor, + UnitTest::MockPhysXHeightfieldProviderComponent::CreateDescriptor()); + } + + void SetupMockMethods(NiceMock& mockShapeRequests) + { + ON_CALL(mockShapeRequests, GetHeightfieldTransform).WillByDefault(Return(AZ::Transform::CreateTranslation({ 1, 2, 0 }))); + ON_CALL(mockShapeRequests, GetHeightfieldGridSpacing).WillByDefault(Return(AZ::Vector2(1, 1))); + ON_CALL(mockShapeRequests, GetHeightsAndMaterials).WillByDefault(Return(GetSamples())); + ON_CALL(mockShapeRequests, GetHeightfieldGridSize) + .WillByDefault( + [](int32_t& numColumns, int32_t& numRows) + { + numColumns = 3; + numRows = 3; + }); + ON_CALL(mockShapeRequests, GetHeightfieldHeightBounds) + .WillByDefault( + [](float& x, float& y) + { + x = -3.0f; + y = 3.0f; + }); + } + + EntityPtr TestCreateActiveGameEntityFromEditorEntity(AZ::Entity* editorEntity) + { + EntityPtr gameEntity = AZStd::make_unique(); + AzToolsFramework::ToolsApplicationRequestBus::Broadcast( + &AzToolsFramework::ToolsApplicationRequests::PreExportEntity, *editorEntity, *gameEntity); + gameEntity->Init(); + return gameEntity; + } + + + TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentDependenciesSatisfiedEntityIsValid) + { + EntityPtr entity = CreateInactiveEditorEntity("HeightfieldColliderComponentEditorEntity"); + entity->CreateComponent(); + entity->CreateComponent(LmbrCentral::EditorAxisAlignedBoxShapeComponentTypeId); + entity->CreateComponent()->CreateDescriptor(); + + // the entity should be in a valid state because the shape component and + // the Terrain Physics Collider Component requirement is satisfied. + AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails(); + EXPECT_TRUE(sortOutcome.IsSuccess()); + } + + TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentDependenciesMissingEntityIsInvalid) + { + EntityPtr entity = CreateInactiveEditorEntity("HeightfieldColliderComponentEditorEntity"); + entity->CreateComponent(); + + // the entity should not be in a valid state because the heightfield collider component requires + // a shape component and the Terrain Physics Collider Component + AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails(); + EXPECT_FALSE(sortOutcome.IsSuccess()); + EXPECT_TRUE(sortOutcome.GetError().m_code == AZ::Entity::DependencySortResult::MissingRequiredService); + } + + TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentMultipleHeightfieldColliderComponentsEntityIsInvalid) + { + EntityPtr entity = CreateInactiveEditorEntity("HeightfieldColliderComponentEditorEntity"); + entity->CreateComponent(); + entity->CreateComponent(LmbrCentral::EditorAxisAlignedBoxShapeComponentTypeId); + + // adding a second heightfield collider component should make the entity invalid + entity->CreateComponent(); + + AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails(); + EXPECT_FALSE(sortOutcome.IsSuccess()); + EXPECT_TRUE(sortOutcome.GetError().m_code == AZ::Entity::DependencySortResult::HasIncompatibleServices); + } + + TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentHeightfieldColliderWithCorrectComponentsCorrectRuntimeComponents) + { + EntityPtr editorEntity = SetupHeightfieldComponent(); + NiceMock mockShapeRequests(editorEntity->GetId()); + SetupMockMethods(mockShapeRequests); + editorEntity->Activate(); + + EntityPtr gameEntity = TestCreateActiveGameEntityFromEditorEntity(editorEntity.get()); + NiceMock mockShapeRequests2(gameEntity->GetId()); + SetupMockMethods(mockShapeRequests2); + gameEntity->Activate(); + + // check that the runtime entity has the expected components + EXPECT_TRUE(gameEntity->FindComponent() != nullptr); + EXPECT_TRUE(gameEntity->FindComponent() != nullptr); + EXPECT_TRUE(gameEntity->FindComponent(LmbrCentral::AxisAlignedBoxShapeComponentTypeId) != nullptr); + + CleanupHeightfieldComponent(); + } + + TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentHeightfieldColliderWithAABoxCorrectRuntimeGeometry) + { + EntityPtr editorEntity = SetupHeightfieldComponent(); + NiceMock mockShapeRequests(editorEntity->GetId()); + SetupMockMethods(mockShapeRequests); + editorEntity->Activate(); + + EntityPtr gameEntity = TestCreateActiveGameEntityFromEditorEntity(editorEntity.get()); + NiceMock mockShapeRequests2(gameEntity->GetId()); + SetupMockMethods(mockShapeRequests2); + gameEntity->Activate(); + + AzPhysics::SimulatedBody* staticBody = nullptr; + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult( + staticBody, gameEntity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetSimulatedBody); + const auto* pxRigidStatic = static_cast(staticBody->GetNativePointer()); + + PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene()); + + // there should be a single shape on the rigid body and it should be a heightfield + EXPECT_EQ(pxRigidStatic->getNbShapes(), 1); + + physx::PxShape* shape = nullptr; + pxRigidStatic->getShapes(&shape, 1, 0); + EXPECT_EQ(shape->getGeometryType(), physx::PxGeometryType::eHEIGHTFIELD); + + physx::PxHeightFieldGeometry heightfieldGeometry; + shape->getHeightFieldGeometry(heightfieldGeometry); + + physx::PxHeightField* heightfield = heightfieldGeometry.heightField; + + int32_t numRows{ 0 }; + int32_t numColumns{ 0 }; + Physics::HeightfieldProviderRequestsBus::Event( + gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, numColumns, numRows); + EXPECT_EQ(numColumns, heightfield->getNbColumns()); + EXPECT_EQ(numRows, heightfield->getNbRows()); + + for (int sampleRow = 0; sampleRow < numRows; ++sampleRow) + { + for (int sampleColumn = 0; sampleColumn < numColumns; ++sampleColumn) + { + float minHeightBounds{ 0.0f }; + float maxHeightBounds{ 0.0f }; + Physics::HeightfieldProviderRequestsBus::Event( + gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldHeightBounds, minHeightBounds, + maxHeightBounds); + + AZStd::vector samples; + Physics::HeightfieldProviderRequestsBus::EventResult( + samples, gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials); + const float halfBounds{ (maxHeightBounds - minHeightBounds) / 2.0f }; + const float scaleFactor = (maxHeightBounds <= minHeightBounds) ? 1.0f : AZStd::numeric_limits::max() / halfBounds; + + physx::PxHeightFieldSample samplePhysX = heightfield->getSample(sampleRow, sampleColumn); + Physics::HeightMaterialPoint samplePhysics = samples[sampleRow * numColumns + sampleColumn]; + EXPECT_EQ(samplePhysX.height, azlossy_cast(samplePhysics.m_height * scaleFactor)); + } + } + CleanupHeightfieldComponent(); + } + +} // namespace PhysXEditorTests + diff --git a/Gems/PhysX/Code/physx_editor_tests_files.cmake b/Gems/PhysX/Code/physx_editor_tests_files.cmake index 36fb139514..953e2a167e 100644 --- a/Gems/PhysX/Code/physx_editor_tests_files.cmake +++ b/Gems/PhysX/Code/physx_editor_tests_files.cmake @@ -18,6 +18,7 @@ set(FILES Tests/PolygonPrismMeshUtilsTest.cpp Tests/PhysXColliderComponentModeTests.cpp Tests/ShapeColliderComponentTests.cpp + Tests/EditorHeightfieldColliderComponentTests.cpp Tests/TestColliderComponent.h Tests/SystemComponentTest.cpp Tests/RigidBodyComponentTests.cpp diff --git a/Gems/PhysX/Code/physx_mocks_files.cmake b/Gems/PhysX/Code/physx_mocks_files.cmake new file mode 100644 index 0000000000..49843eeb6f --- /dev/null +++ b/Gems/PhysX/Code/physx_mocks_files.cmake @@ -0,0 +1,11 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + Mocks/PhysX/MockPhysXHeightfieldProviderComponent.h +) From 5ceb9ee2144e91fe09d1514c7ed110e7f6f122fa Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 27 Oct 2021 14:24:16 -0500 Subject: [PATCH 072/120] Material component property inspector indicator changes Shows indicator icon for differences between the overrides and the active material asset, not the material type. So, the indicator will only be shown for property changes that have an effect and will be stored in the component. Signed-off-by: Guthrie Adams --- .../EditorMaterialComponentInspector.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index 9af3b41b12..bfa9f1d36f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -314,12 +314,21 @@ namespace AZ AtomToolsFramework::ConvertToPropertyConfig(propertyConfig, propertyDefinition); + const auto& propertyIndex = + m_editData.m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyConfig.m_id); + propertyConfig.m_groupName = groupDisplayName; - const auto& propertyIndex = m_editData.m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyConfig.m_id); propertyConfig.m_showThumbnail = true; - propertyConfig.m_defaultValue = AtomToolsFramework::ConvertToEditableType(m_editData.m_materialTypeAsset->GetDefaultPropertyValues()[propertyIndex.GetIndex()]); - propertyConfig.m_parentValue = AtomToolsFramework::ConvertToEditableType(m_editData.m_materialTypeAsset->GetDefaultPropertyValues()[propertyIndex.GetIndex()]); - propertyConfig.m_originalValue = AtomToolsFramework::ConvertToEditableType(m_editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]); + + propertyConfig.m_defaultValue = AtomToolsFramework::ConvertToEditableType( + m_editData.m_materialTypeAsset->GetDefaultPropertyValues()[propertyIndex.GetIndex()]); + + // There is no explicit parent material here. Material instance property overrides replace the values from the + // assigned material asset. Its values should be treated as parent, for comparison, in this case. + propertyConfig.m_parentValue = AtomToolsFramework::ConvertToEditableType( + m_editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]); + propertyConfig.m_originalValue = AtomToolsFramework::ConvertToEditableType( + m_editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]); group.m_properties.emplace_back(propertyConfig); } } From 6f8890c2ef3f1bbee0f355dfa1fefe04dac0f3b7 Mon Sep 17 00:00:00 2001 From: srikappa-amzn <82230713+srikappa-amzn@users.noreply.github.com> Date: Thu, 28 Oct 2021 01:16:06 +0530 Subject: [PATCH 073/120] Improve error messaging when duplicating entities before they are created (#4922) * Improved error messaging when user tries to duplicate before entities are created Signed-off-by: srikappa-amzn --- .../Application/EditorEntityManager.cpp | 29 +++++++++++++++++-- .../Application/EditorEntityManager.h | 1 - .../Prefab/PrefabPublicHandler.cpp | 3 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.cpp index ce50fc7e00..6a926b89fd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.cpp @@ -9,9 +9,27 @@ #include #include +#include namespace AzToolsFramework { + static bool AreEntitiesValidForDuplication(const EntityIdList& entityIds) + { + for (AZ::EntityId entityId : entityIds) + { + if (GetEntityById(entityId) == nullptr) + { + AZ_Error( + "Entity", false, + "Entity with id '%llu' is not found. This can happen when you try to duplicate the entity before it is created. Please " + "ensure entities are created before trying to duplicate them.", + static_cast(entityId)); + return false; + } + } + return true; + } + void EditorEntityManager::Start() { m_prefabPublicInterface = AZ::Interface::Get(); @@ -62,7 +80,11 @@ namespace AzToolsFramework EntityIdList selectedEntities; ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities); - m_prefabPublicInterface->DuplicateEntitiesInInstance(selectedEntities); + if (AreEntitiesValidForDuplication(selectedEntities)) + { + m_prefabPublicInterface->DuplicateEntitiesInInstance(selectedEntities); + } + } void EditorEntityManager::DuplicateEntityById(AZ::EntityId entityId) @@ -72,7 +94,10 @@ namespace AzToolsFramework void EditorEntityManager::DuplicateEntities(const EntityIdList& entities) { - m_prefabPublicInterface->DuplicateEntitiesInInstance(entities); + if (AreEntitiesValidForDuplication(entities)) + { + m_prefabPublicInterface->DuplicateEntitiesInInstance(entities); + } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.h index 5786cc3fbc..c4311de672 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/EditorEntityManager.h @@ -34,5 +34,4 @@ namespace AzToolsFramework private: Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr; }; - } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index d976c91c3e..a6a80e58d3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -1110,7 +1110,8 @@ namespace AzToolsFramework // Select the duplicated entities/instances auto selectionUndo = aznew SelectionCommand(duplicatedEntityAndInstanceIds, "Select Duplicated Entities/Instances"); selectionUndo->SetParent(undoBatch.GetUndoBatch()); - ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, duplicatedEntityAndInstanceIds); + ToolsApplicationRequestBus::Broadcast( + &ToolsApplicationRequestBus::Events::SetSelectedEntities, duplicatedEntityAndInstanceIds); } return AZ::Success(AZStd::move(duplicatedEntityAndInstanceIds)); From 46fc1a720c120455d4eaf5f212905f2e3c30095a Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Wed, 27 Oct 2021 13:04:10 -0700 Subject: [PATCH 074/120] Fixes monolithic and non-monolithic installed builds (test project builds against debug/profile non-monolithic and release monolithic) Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- Gems/Atom/RHI/DX12/Code/CMakeLists.txt | 3 +- .../Code/inapppurchases_files.cmake | 1 + cmake/Platform/Common/Install_common.cmake | 36 ++++++++++++------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/Gems/Atom/RHI/DX12/Code/CMakeLists.txt b/Gems/Atom/RHI/DX12/Code/CMakeLists.txt index b913ad58bf..975b838ffa 100644 --- a/Gems/Atom/RHI/DX12/Code/CMakeLists.txt +++ b/Gems/Atom/RHI/DX12/Code/CMakeLists.txt @@ -107,7 +107,7 @@ ly_add_target( Gem::Atom_RHI.Reflect Gem::Atom_RHI_DX12.Reflect 3rdParty::d3dx12 - ${AFTERMATH_BUILD_DEPENDENCY} + ${AFTERMATH_BUILD_DEPENDENCY} COMPILE_DEFINITIONS PRIVATE ${USE_NSIGHT_AFTERMATH_DEFINE} @@ -128,7 +128,6 @@ ly_add_target( BUILD_DEPENDENCIES PRIVATE AZ::AzCore - Gem::Atom_RHI.Reflect Gem::Atom_RHI.Public Gem::Atom_RHI_DX12.Reflect diff --git a/Gems/InAppPurchases/Code/inapppurchases_files.cmake b/Gems/InAppPurchases/Code/inapppurchases_files.cmake index 5eab792206..2669cbf930 100644 --- a/Gems/InAppPurchases/Code/inapppurchases_files.cmake +++ b/Gems/InAppPurchases/Code/inapppurchases_files.cmake @@ -10,6 +10,7 @@ set(FILES Include/InAppPurchases/InAppPurchasesBus.h Include/InAppPurchases/InAppPurchasesInterface.h Include/InAppPurchases/InAppPurchasesResponseBus.h + Source/InAppPurchasesSystemComponent.h Source/InAppPurchasesSystemComponent.cpp Source/InAppPurchasesInterface.cpp ) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 8a06d1b584..ceab70162e 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -192,16 +192,15 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar endif() # Includes need additional processing to add the install root - if(include_directories) - foreach(include ${include_directories}) - string(GENEX_STRIP ${include} include_genex_expr) - if(include_genex_expr STREQUAL include) # only for cases where there are no generation expressions - # Make the include path relative to the source dir where the target will be declared - cmake_path(RELATIVE_PATH include BASE_DIRECTORY ${absolute_target_source_dir} OUTPUT_VARIABLE target_include) - string(APPEND INCLUDE_DIRECTORIES_PLACEHOLDER "${PLACEHOLDER_INDENT}${target_include}\n") - endif() - endforeach() - endif() + foreach(include IN LISTS include_directories) + string(GENEX_STRIP ${include} include_genex_expr) + if(include_genex_expr STREQUAL include) # only for cases where there are no generation expressions + # Make the include path relative to the source dir where the target will be declared + cmake_path(RELATIVE_PATH include BASE_DIRECTORY ${absolute_target_source_dir} OUTPUT_VARIABLE target_include) + list(APPEND INCLUDE_DIRECTORIES_PLACEHOLDER "${PLACEHOLDER_INDENT}${target_include}") + endif() + endforeach() + list(JOIN INCLUDE_DIRECTORIES_PLACEHOLDER "\n" INCLUDE_DIRECTORIES_PLACEHOLDER) string(REPEAT " " 8 PLACEHOLDER_INDENT) get_target_property(RUNTIME_DEPENDENCIES_PLACEHOLDER ${TARGET_NAME} MANUALLY_ADDED_DEPENDENCIES) @@ -217,12 +216,23 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar unset(INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER) if(inteface_build_dependencies_props) cmake_parse_arguments(build_deps "" "" "PRIVATE;PUBLIC;INTERFACE" ${inteface_build_dependencies_props}) - foreach(build_dependency IN LISTS build_deps_INTERFACE build_deps_PUBLIC) + # Interface and public dependencies should always be exposed + set(build_deps_target ${build_deps_INTERFACE}) + if(build_deps_PUBLIC) + set(build_deps_target "${build_deps_target};${build_deps_PUBLIC}") + endif() + # Private dependencies should only be exposed if it is a static library, since in those cases, link + # dependencies are transfered to the downstream dependencies + if("${target_type}" STREQUAL "STATIC_LIBRARY") + set(build_deps_target "${build_deps_target};${build_deps_PRIVATE}") + endif() + foreach(build_dependency IN LISTS build_deps_target) # Skip wrapping produced when targets are not created in the same directory - list(APPEND INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${PLACEHOLDER_INDENT}${build_dependency}") + if(build_dependency) + list(APPEND INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${PLACEHOLDER_INDENT}${build_dependency}") + endif() endforeach() endif() - list(REMOVE_DUPLICATES INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER) list(JOIN INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "\n" INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER) string(REPEAT " " 8 PLACEHOLDER_INDENT) From 59c898fc4853f898fc01ff6145a71bebad3caac6 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Wed, 27 Oct 2021 13:55:32 -0700 Subject: [PATCH 075/120] Fix notification queue and add gem action (#4985) (#5024) Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com> --- .../Components/ToastNotification.cpp | 9 +- .../Components/ToastNotification.h | 4 +- .../Notifications/ToastNotificationsView.cpp | 34 ++++++++ .../UI/Notifications/ToastNotificationsView.h | 5 ++ .../Source/GemCatalog/GemCatalogScreen.cpp | 87 ++++++++++--------- .../Source/GemCatalog/GemCatalogScreen.h | 1 + 6 files changed, 95 insertions(+), 45 deletions(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp index f79f355ccb..8831bef89c 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp @@ -22,6 +22,7 @@ namespace AzQtComponents , m_closeOnClick(true) , m_ui(new Ui::ToastNotification()) , m_fadeAnimation(nullptr) + , m_configuration(toastConfiguration) { setProperty("HasNoWindowDecorations", true); @@ -80,7 +81,13 @@ namespace AzQtComponents } ToastNotification::~ToastNotification() - { + { + } + + bool ToastNotification::IsDuplicate(const ToastConfiguration& toastConfiguration) + { + return toastConfiguration.m_title == m_configuration.m_title + && toastConfiguration.m_description == m_configuration.m_description; } void ToastNotification::paintEvent(QPaintEvent* event) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h index 7f2701a803..4343f37df4 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h @@ -45,6 +45,8 @@ namespace AzQtComponents void ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint); void UpdatePosition(const QPoint& screenPosition, const QPointF& anchorPoint); + + bool IsDuplicate(const ToastConfiguration& toastConfiguration); // QDialog void showEvent(QShowEvent* showEvent) override; @@ -64,7 +66,7 @@ namespace AzQtComponents private: QPropertyAnimation* m_fadeAnimation; - + ToastConfiguration m_configuration; bool m_closeOnClick; QTimer m_lifeSpan; uint32_t m_borderRadius = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp index e039230783..a88711a9ed 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp @@ -63,6 +63,12 @@ namespace AzToolsFramework ToastId ToastNotificationsView::ShowToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration) { + // reject duplicate messages + if (m_rejectDuplicates && DuplicateNotificationInQueue(toastConfiguration)) + { + return ToastId(); + } + ToastId toastId = CreateToastNotification(toastConfiguration); m_queuedNotifications.emplace_back(toastId); @@ -70,10 +76,28 @@ namespace AzToolsFramework { DisplayQueuedNotification(); } + else if (m_queuedNotifications.size() >= m_maxQueuedNotifications) + { + // hiding the active toast will cause the next toast to be displayed + HideToastNotification(m_activeNotification); + } return toastId; } + bool ToastNotificationsView::DuplicateNotificationInQueue(const AzQtComponents::ToastConfiguration& toastConfiguration) + { + for (auto iter : m_notifications) + { + if (iter.second && iter.second->IsDuplicate(toastConfiguration)) + { + return true; + } + } + + return false; + } + ToastId ToastNotificationsView::ShowToastAtCursor(const AzQtComponents::ToastConfiguration& toastConfiguration) { ToastId toastId = CreateToastNotification(toastConfiguration); @@ -187,4 +211,14 @@ namespace AzToolsFramework { m_anchorPoint = anchorPoint; } + + void ToastNotificationsView::SetMaxQueuedNotifications(AZ::u32 maxQueuedNotifications) + { + m_maxQueuedNotifications = maxQueuedNotifications; + } + + void ToastNotificationsView::SetRejectDuplicates(bool rejectDuplicates) + { + m_rejectDuplicates = rejectDuplicates; + } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h index e13f129467..c64ce00f4c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.h @@ -52,10 +52,13 @@ namespace AzToolsFramework void SetOffset(const QPoint& offset); void SetAnchorPoint(const QPointF& anchorPoint); + void SetMaxQueuedNotifications(AZ::u32 maxQueuedNotifications); + void SetRejectDuplicates(bool rejectDuplicates); private: ToastId CreateToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration); void DisplayQueuedNotification(); + bool DuplicateNotificationInQueue(const AzQtComponents::ToastConfiguration& toastConfiguration); QPoint GetGlobalPoint(); ToastId m_activeNotification; @@ -64,5 +67,7 @@ namespace AzToolsFramework QPoint m_offset = QPoint(10, 10); QPointF m_anchorPoint = QPointF(1, 0); + AZ::u32 m_maxQueuedNotifications = 5; + bool m_rejectDuplicates = true; }; } // AzToolsFramework diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index b3d0ab83ed..a22f41d054 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -42,7 +42,9 @@ namespace O3DE::ProjectManager m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel, m_downloadController); vLayout->addWidget(m_headerWidget); + connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged); connect(m_headerWidget, &GemCatalogHeaderWidget::OpenGemsRepo, this, &GemCatalogScreen::HandleOpenGemRepo); + connect(m_headerWidget, &GemCatalogHeaderWidget::AddGem, this, &GemCatalogScreen::OnAddGemClicked); QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setMargin(0); @@ -73,6 +75,7 @@ namespace O3DE::ProjectManager m_notificationsView = AZStd::make_unique(this, AZ_CRC("GemCatalogNotificationsView")); m_notificationsView->SetOffset(QPoint(10, 70)); + m_notificationsView->SetMaxQueuedNotifications(1); } void GemCatalogScreen::ReinitForProject(const QString& projectPath) @@ -94,48 +97,6 @@ namespace O3DE::ProjectManager m_headerWidget->ReinitForProject(); connect(m_gemModel, &GemModel::dataChanged, m_filterWidget, &GemFilterWidget::ResetGemStatusFilter); - connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged); - connect( - m_headerWidget, &GemCatalogHeaderWidget::AddGem, - [&]() - { - EngineInfo engineInfo; - QString defaultPath; - - AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); - if (engineInfoResult.IsSuccess()) - { - engineInfo = engineInfoResult.GetValue(); - defaultPath = engineInfo.m_defaultGemsFolder; - } - - if (defaultPath.isEmpty()) - { - defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - } - - QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath)); - if (!directory.isEmpty()) - { - // register the gem to the o3de_manifest.json and to the project after the user confirms - // project creation/update - auto registerResult = PythonBindingsInterface::Get()->RegisterGem(directory); - if(!registerResult) - { - QMessageBox::critical(this, tr("Failed to add gem"), registerResult.GetError().c_str()); - } - else - { - m_gemsToRegisterWithProject.insert(directory); - AZ::Outcome gemInfoResult = PythonBindingsInterface::Get()->GetGemInfo(directory); - if (gemInfoResult) - { - m_gemModel->AddGem(gemInfoResult.GetValue()); - m_gemModel->UpdateGemDependencies(); - } - } - } - }); // Select the first entry after everything got correctly sized QTimer::singleShot(200, [=]{ @@ -144,6 +105,46 @@ namespace O3DE::ProjectManager }); } + void GemCatalogScreen::OnAddGemClicked() + { + EngineInfo engineInfo; + QString defaultPath; + + AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); + if (engineInfoResult.IsSuccess()) + { + engineInfo = engineInfoResult.GetValue(); + defaultPath = engineInfo.m_defaultGemsFolder; + } + + if (defaultPath.isEmpty()) + { + defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + } + + QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath)); + if (!directory.isEmpty()) + { + // register the gem to the o3de_manifest.json and to the project after the user confirms + // project creation/update + auto registerResult = PythonBindingsInterface::Get()->RegisterGem(directory); + if(!registerResult) + { + QMessageBox::critical(this, tr("Failed to add gem"), registerResult.GetError().c_str()); + } + else + { + m_gemsToRegisterWithProject.insert(directory); + AZ::Outcome gemInfoResult = PythonBindingsInterface::Get()->GetGemInfo(directory); + if (gemInfoResult) + { + m_gemModel->AddGem(gemInfoResult.GetValue()); + m_gemModel->UpdateGemDependencies(); + } + } + } + } + void GemCatalogScreen::OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies) { if (m_notificationsEnabled) @@ -178,7 +179,7 @@ namespace O3DE::ProjectManager } else if (numChangedDependencies > 1) { - notification += QString("%d Gem ").arg(numChangedDependencies) + tr("dependencies"); + notification += QString("%1 Gem ").arg(numChangedDependencies) + tr("dependencies"); } notification += " " + (added ? tr("activated") : tr("deactivated")); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 8e9f31c710..1ade87af0c 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -47,6 +47,7 @@ namespace O3DE::ProjectManager public slots: void OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies); + void OnAddGemClicked(); protected: void hideEvent(QHideEvent* event) override; From 8bbd8f9807f53130d271f59373f8b2c0b725967d Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Wed, 27 Oct 2021 16:01:02 -0500 Subject: [PATCH 076/120] Renamed the C++ and Python tool gem templates after review. Signed-off-by: Chris Galvan --- Templates/CMakeLists.txt | 4 ++-- .../Template/CMakeLists.txt | 0 .../Template/Code/${NameLower}_editor_files.cmake | 0 .../Code/${NameLower}_editor_shared_files.cmake | 0 .../Code/${NameLower}_editor_tests_files.cmake | 0 .../Template/Code/${NameLower}_files.cmake | 0 .../Template/Code/${NameLower}_shared_files.cmake | 0 .../Template/Code/${NameLower}_tests_files.cmake | 0 .../Template/Code/CMakeLists.txt | 0 .../Template/Code/Include/${Name}/${Name}Bus.h | 0 .../Android/${NameLower}_android_files.cmake | 0 .../Android/${NameLower}_shared_android_files.cmake | 0 .../Template/Code/Platform/Android/PAL_android.cmake | 0 .../Platform/Linux/${NameLower}_linux_files.cmake | 0 .../Linux/${NameLower}_shared_linux_files.cmake | 0 .../Template/Code/Platform/Linux/PAL_linux.cmake | 0 .../Code/Platform/Mac/${NameLower}_mac_files.cmake | 0 .../Platform/Mac/${NameLower}_shared_mac_files.cmake | 0 .../Template/Code/Platform/Mac/PAL_mac.cmake | 0 .../Windows/${NameLower}_shared_windows_files.cmake | 0 .../Windows/${NameLower}_windows_files.cmake | 0 .../Template/Code/Platform/Windows/PAL_windows.cmake | 0 .../Code/Platform/iOS/${NameLower}_ios_files.cmake | 0 .../Platform/iOS/${NameLower}_shared_ios_files.cmake | 0 .../Template/Code/Platform/iOS/PAL_ios.cmake | 0 .../Template/Code/Source/${Name}.qrc | 0 .../Template/Code/Source/${Name}EditorModule.cpp | 0 .../Code/Source/${Name}EditorSystemComponent.cpp | 0 .../Code/Source/${Name}EditorSystemComponent.h | 0 .../Template/Code/Source/${Name}Module.cpp | 0 .../Template/Code/Source/${Name}ModuleInterface.h | 0 .../Template/Code/Source/${Name}SystemComponent.cpp | 0 .../Template/Code/Source/${Name}SystemComponent.h | 0 .../Template/Code/Source/${Name}Widget.cpp | 0 .../Template/Code/Source/${Name}Widget.h | 0 .../Template/Code/Source/toolbar_icon.svg | 0 .../Template/Code/Tests/${Name}EditorTest.cpp | 0 .../Template/Code/Tests/${Name}Test.cpp | 0 .../Template/Platform/Android/android_gem.cmake | 0 .../Template/Platform/Android/android_gem.json | 0 .../Template/Platform/Linux/linux_gem.cmake | 0 .../Template/Platform/Linux/linux_gem.json | 0 .../Template/Platform/Mac/mac_gem.cmake | 0 .../Template/Platform/Mac/mac_gem.json | 0 .../Template/Platform/Windows/windows_gem.cmake | 0 .../Template/Platform/Windows/windows_gem.json | 0 .../Template/Platform/iOS/ios_gem.cmake | 0 .../Template/Platform/iOS/ios_gem.json | 0 .../{CustomTool => CppToolGem}/Template/gem.json | 0 .../{CustomTool => CppToolGem}/Template/preview.png | 0 Templates/{CustomTool => CppToolGem}/template.json | 10 +++++----- .../Template/CMakeLists.txt | 0 .../Template/Code/${NameLower}_editor_files.cmake | 0 .../Code/${NameLower}_editor_shared_files.cmake | 0 .../Code/${NameLower}_editor_tests_files.cmake | 0 .../Template/Code/CMakeLists.txt | 0 .../Template/Code/Include/${Name}/${Name}Bus.h | 0 .../Platform/Linux/${NameLower}_linux_files.cmake | 0 .../Linux/${NameLower}_shared_linux_files.cmake | 0 .../Template/Code/Platform/Linux/PAL_linux.cmake | 0 .../Code/Platform/Mac/${NameLower}_mac_files.cmake | 0 .../Platform/Mac/${NameLower}_shared_mac_files.cmake | 0 .../Template/Code/Platform/Mac/PAL_mac.cmake | 0 .../Windows/${NameLower}_shared_windows_files.cmake | 0 .../Windows/${NameLower}_windows_files.cmake | 0 .../Template/Code/Platform/Windows/PAL_windows.cmake | 0 .../Template/Code/Source/${Name}EditorModule.cpp | 0 .../Code/Source/${Name}EditorSystemComponent.cpp | 0 .../Code/Source/${Name}EditorSystemComponent.h | 0 .../Template/Code/Source/${Name}ModuleInterface.h | 0 .../Template/Code/Tests/${Name}EditorTest.cpp | 0 .../Template/Editor/Scripts/${NameLower}_dialog.py | 0 .../Template/Editor/Scripts/__init__.py | 0 .../Template/Editor/Scripts/bootstrap.py | 0 .../{PythonGem => PythonToolGem}/Template/gem.json | 0 .../Template/preview.png | 0 Templates/{PythonGem => PythonToolGem}/template.json | 12 ++++++------ engine.json | 5 +++-- 78 files changed, 16 insertions(+), 15 deletions(-) rename Templates/{CustomTool => CppToolGem}/Template/CMakeLists.txt (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/${NameLower}_editor_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/${NameLower}_editor_shared_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/${NameLower}_editor_tests_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/${NameLower}_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/${NameLower}_shared_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/${NameLower}_tests_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/CMakeLists.txt (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Include/${Name}/${Name}Bus.h (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/Android/${NameLower}_android_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/Android/PAL_android.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/Linux/PAL_linux.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/Mac/PAL_mac.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/Windows/PAL_windows.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Platform/iOS/PAL_ios.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Source/${Name}.qrc (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Source/${Name}EditorModule.cpp (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Source/${Name}EditorSystemComponent.cpp (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Source/${Name}EditorSystemComponent.h (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Source/${Name}Module.cpp (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Source/${Name}ModuleInterface.h (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Source/${Name}SystemComponent.cpp (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Source/${Name}SystemComponent.h (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Source/${Name}Widget.cpp (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Source/${Name}Widget.h (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Source/toolbar_icon.svg (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Tests/${Name}EditorTest.cpp (100%) rename Templates/{CustomTool => CppToolGem}/Template/Code/Tests/${Name}Test.cpp (100%) rename Templates/{CustomTool => CppToolGem}/Template/Platform/Android/android_gem.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Platform/Android/android_gem.json (100%) rename Templates/{CustomTool => CppToolGem}/Template/Platform/Linux/linux_gem.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Platform/Linux/linux_gem.json (100%) rename Templates/{CustomTool => CppToolGem}/Template/Platform/Mac/mac_gem.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Platform/Mac/mac_gem.json (100%) rename Templates/{CustomTool => CppToolGem}/Template/Platform/Windows/windows_gem.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Platform/Windows/windows_gem.json (100%) rename Templates/{CustomTool => CppToolGem}/Template/Platform/iOS/ios_gem.cmake (100%) rename Templates/{CustomTool => CppToolGem}/Template/Platform/iOS/ios_gem.json (100%) rename Templates/{CustomTool => CppToolGem}/Template/gem.json (100%) rename Templates/{CustomTool => CppToolGem}/Template/preview.png (100%) rename Templates/{CustomTool => CppToolGem}/template.json (98%) rename Templates/{PythonGem => PythonToolGem}/Template/CMakeLists.txt (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/${NameLower}_editor_files.cmake (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/${NameLower}_editor_shared_files.cmake (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/${NameLower}_editor_tests_files.cmake (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/CMakeLists.txt (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Include/${Name}/${Name}Bus.h (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Platform/Linux/PAL_linux.cmake (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Platform/Mac/PAL_mac.cmake (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Platform/Windows/PAL_windows.cmake (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Source/${Name}EditorModule.cpp (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Source/${Name}EditorSystemComponent.cpp (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Source/${Name}EditorSystemComponent.h (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Source/${Name}ModuleInterface.h (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Code/Tests/${Name}EditorTest.cpp (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Editor/Scripts/${NameLower}_dialog.py (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Editor/Scripts/__init__.py (100%) rename Templates/{PythonGem => PythonToolGem}/Template/Editor/Scripts/bootstrap.py (100%) rename Templates/{PythonGem => PythonToolGem}/Template/gem.json (100%) rename Templates/{PythonGem => PythonToolGem}/Template/preview.png (100%) rename Templates/{PythonGem => PythonToolGem}/template.json (94%) diff --git a/Templates/CMakeLists.txt b/Templates/CMakeLists.txt index 84a708989a..9735907a6a 100644 --- a/Templates/CMakeLists.txt +++ b/Templates/CMakeLists.txt @@ -9,8 +9,8 @@ ly_install_directory( DIRECTORIES AssetGem - CustomTool - PythonGem + CppToolGem + PythonToolGem DefaultGem DefaultProject MinimalProject diff --git a/Templates/CustomTool/Template/CMakeLists.txt b/Templates/CppToolGem/Template/CMakeLists.txt similarity index 100% rename from Templates/CustomTool/Template/CMakeLists.txt rename to Templates/CppToolGem/Template/CMakeLists.txt diff --git a/Templates/CustomTool/Template/Code/${NameLower}_editor_files.cmake b/Templates/CppToolGem/Template/Code/${NameLower}_editor_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/${NameLower}_editor_files.cmake rename to Templates/CppToolGem/Template/Code/${NameLower}_editor_files.cmake diff --git a/Templates/CustomTool/Template/Code/${NameLower}_editor_shared_files.cmake b/Templates/CppToolGem/Template/Code/${NameLower}_editor_shared_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/${NameLower}_editor_shared_files.cmake rename to Templates/CppToolGem/Template/Code/${NameLower}_editor_shared_files.cmake diff --git a/Templates/CustomTool/Template/Code/${NameLower}_editor_tests_files.cmake b/Templates/CppToolGem/Template/Code/${NameLower}_editor_tests_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/${NameLower}_editor_tests_files.cmake rename to Templates/CppToolGem/Template/Code/${NameLower}_editor_tests_files.cmake diff --git a/Templates/CustomTool/Template/Code/${NameLower}_files.cmake b/Templates/CppToolGem/Template/Code/${NameLower}_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/${NameLower}_files.cmake rename to Templates/CppToolGem/Template/Code/${NameLower}_files.cmake diff --git a/Templates/CustomTool/Template/Code/${NameLower}_shared_files.cmake b/Templates/CppToolGem/Template/Code/${NameLower}_shared_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/${NameLower}_shared_files.cmake rename to Templates/CppToolGem/Template/Code/${NameLower}_shared_files.cmake diff --git a/Templates/CustomTool/Template/Code/${NameLower}_tests_files.cmake b/Templates/CppToolGem/Template/Code/${NameLower}_tests_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/${NameLower}_tests_files.cmake rename to Templates/CppToolGem/Template/Code/${NameLower}_tests_files.cmake diff --git a/Templates/CustomTool/Template/Code/CMakeLists.txt b/Templates/CppToolGem/Template/Code/CMakeLists.txt similarity index 100% rename from Templates/CustomTool/Template/Code/CMakeLists.txt rename to Templates/CppToolGem/Template/Code/CMakeLists.txt diff --git a/Templates/CustomTool/Template/Code/Include/${Name}/${Name}Bus.h b/Templates/CppToolGem/Template/Code/Include/${Name}/${Name}Bus.h similarity index 100% rename from Templates/CustomTool/Template/Code/Include/${Name}/${Name}Bus.h rename to Templates/CppToolGem/Template/Code/Include/${Name}/${Name}Bus.h diff --git a/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_android_files.cmake b/Templates/CppToolGem/Template/Code/Platform/Android/${NameLower}_android_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_android_files.cmake rename to Templates/CppToolGem/Template/Code/Platform/Android/${NameLower}_android_files.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake b/Templates/CppToolGem/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake rename to Templates/CppToolGem/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake b/Templates/CppToolGem/Template/Code/Platform/Android/PAL_android.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake rename to Templates/CppToolGem/Template/Code/Platform/Android/PAL_android.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake b/Templates/CppToolGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake rename to Templates/CppToolGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake b/Templates/CppToolGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake rename to Templates/CppToolGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/Linux/PAL_linux.cmake b/Templates/CppToolGem/Template/Code/Platform/Linux/PAL_linux.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/Linux/PAL_linux.cmake rename to Templates/CppToolGem/Template/Code/Platform/Linux/PAL_linux.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake b/Templates/CppToolGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake rename to Templates/CppToolGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake b/Templates/CppToolGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake rename to Templates/CppToolGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/Mac/PAL_mac.cmake b/Templates/CppToolGem/Template/Code/Platform/Mac/PAL_mac.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/Mac/PAL_mac.cmake rename to Templates/CppToolGem/Template/Code/Platform/Mac/PAL_mac.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake b/Templates/CppToolGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake rename to Templates/CppToolGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake b/Templates/CppToolGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake rename to Templates/CppToolGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/Windows/PAL_windows.cmake b/Templates/CppToolGem/Template/Code/Platform/Windows/PAL_windows.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/Windows/PAL_windows.cmake rename to Templates/CppToolGem/Template/Code/Platform/Windows/PAL_windows.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake b/Templates/CppToolGem/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake rename to Templates/CppToolGem/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake b/Templates/CppToolGem/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake rename to Templates/CppToolGem/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake diff --git a/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake b/Templates/CppToolGem/Template/Code/Platform/iOS/PAL_ios.cmake similarity index 100% rename from Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake rename to Templates/CppToolGem/Template/Code/Platform/iOS/PAL_ios.cmake diff --git a/Templates/CustomTool/Template/Code/Source/${Name}.qrc b/Templates/CppToolGem/Template/Code/Source/${Name}.qrc similarity index 100% rename from Templates/CustomTool/Template/Code/Source/${Name}.qrc rename to Templates/CppToolGem/Template/Code/Source/${Name}.qrc diff --git a/Templates/CustomTool/Template/Code/Source/${Name}EditorModule.cpp b/Templates/CppToolGem/Template/Code/Source/${Name}EditorModule.cpp similarity index 100% rename from Templates/CustomTool/Template/Code/Source/${Name}EditorModule.cpp rename to Templates/CppToolGem/Template/Code/Source/${Name}EditorModule.cpp diff --git a/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.cpp b/Templates/CppToolGem/Template/Code/Source/${Name}EditorSystemComponent.cpp similarity index 100% rename from Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.cpp rename to Templates/CppToolGem/Template/Code/Source/${Name}EditorSystemComponent.cpp diff --git a/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.h b/Templates/CppToolGem/Template/Code/Source/${Name}EditorSystemComponent.h similarity index 100% rename from Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.h rename to Templates/CppToolGem/Template/Code/Source/${Name}EditorSystemComponent.h diff --git a/Templates/CustomTool/Template/Code/Source/${Name}Module.cpp b/Templates/CppToolGem/Template/Code/Source/${Name}Module.cpp similarity index 100% rename from Templates/CustomTool/Template/Code/Source/${Name}Module.cpp rename to Templates/CppToolGem/Template/Code/Source/${Name}Module.cpp diff --git a/Templates/CustomTool/Template/Code/Source/${Name}ModuleInterface.h b/Templates/CppToolGem/Template/Code/Source/${Name}ModuleInterface.h similarity index 100% rename from Templates/CustomTool/Template/Code/Source/${Name}ModuleInterface.h rename to Templates/CppToolGem/Template/Code/Source/${Name}ModuleInterface.h diff --git a/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.cpp b/Templates/CppToolGem/Template/Code/Source/${Name}SystemComponent.cpp similarity index 100% rename from Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.cpp rename to Templates/CppToolGem/Template/Code/Source/${Name}SystemComponent.cpp diff --git a/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.h b/Templates/CppToolGem/Template/Code/Source/${Name}SystemComponent.h similarity index 100% rename from Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.h rename to Templates/CppToolGem/Template/Code/Source/${Name}SystemComponent.h diff --git a/Templates/CustomTool/Template/Code/Source/${Name}Widget.cpp b/Templates/CppToolGem/Template/Code/Source/${Name}Widget.cpp similarity index 100% rename from Templates/CustomTool/Template/Code/Source/${Name}Widget.cpp rename to Templates/CppToolGem/Template/Code/Source/${Name}Widget.cpp diff --git a/Templates/CustomTool/Template/Code/Source/${Name}Widget.h b/Templates/CppToolGem/Template/Code/Source/${Name}Widget.h similarity index 100% rename from Templates/CustomTool/Template/Code/Source/${Name}Widget.h rename to Templates/CppToolGem/Template/Code/Source/${Name}Widget.h diff --git a/Templates/CustomTool/Template/Code/Source/toolbar_icon.svg b/Templates/CppToolGem/Template/Code/Source/toolbar_icon.svg similarity index 100% rename from Templates/CustomTool/Template/Code/Source/toolbar_icon.svg rename to Templates/CppToolGem/Template/Code/Source/toolbar_icon.svg diff --git a/Templates/CustomTool/Template/Code/Tests/${Name}EditorTest.cpp b/Templates/CppToolGem/Template/Code/Tests/${Name}EditorTest.cpp similarity index 100% rename from Templates/CustomTool/Template/Code/Tests/${Name}EditorTest.cpp rename to Templates/CppToolGem/Template/Code/Tests/${Name}EditorTest.cpp diff --git a/Templates/CustomTool/Template/Code/Tests/${Name}Test.cpp b/Templates/CppToolGem/Template/Code/Tests/${Name}Test.cpp similarity index 100% rename from Templates/CustomTool/Template/Code/Tests/${Name}Test.cpp rename to Templates/CppToolGem/Template/Code/Tests/${Name}Test.cpp diff --git a/Templates/CustomTool/Template/Platform/Android/android_gem.cmake b/Templates/CppToolGem/Template/Platform/Android/android_gem.cmake similarity index 100% rename from Templates/CustomTool/Template/Platform/Android/android_gem.cmake rename to Templates/CppToolGem/Template/Platform/Android/android_gem.cmake diff --git a/Templates/CustomTool/Template/Platform/Android/android_gem.json b/Templates/CppToolGem/Template/Platform/Android/android_gem.json similarity index 100% rename from Templates/CustomTool/Template/Platform/Android/android_gem.json rename to Templates/CppToolGem/Template/Platform/Android/android_gem.json diff --git a/Templates/CustomTool/Template/Platform/Linux/linux_gem.cmake b/Templates/CppToolGem/Template/Platform/Linux/linux_gem.cmake similarity index 100% rename from Templates/CustomTool/Template/Platform/Linux/linux_gem.cmake rename to Templates/CppToolGem/Template/Platform/Linux/linux_gem.cmake diff --git a/Templates/CustomTool/Template/Platform/Linux/linux_gem.json b/Templates/CppToolGem/Template/Platform/Linux/linux_gem.json similarity index 100% rename from Templates/CustomTool/Template/Platform/Linux/linux_gem.json rename to Templates/CppToolGem/Template/Platform/Linux/linux_gem.json diff --git a/Templates/CustomTool/Template/Platform/Mac/mac_gem.cmake b/Templates/CppToolGem/Template/Platform/Mac/mac_gem.cmake similarity index 100% rename from Templates/CustomTool/Template/Platform/Mac/mac_gem.cmake rename to Templates/CppToolGem/Template/Platform/Mac/mac_gem.cmake diff --git a/Templates/CustomTool/Template/Platform/Mac/mac_gem.json b/Templates/CppToolGem/Template/Platform/Mac/mac_gem.json similarity index 100% rename from Templates/CustomTool/Template/Platform/Mac/mac_gem.json rename to Templates/CppToolGem/Template/Platform/Mac/mac_gem.json diff --git a/Templates/CustomTool/Template/Platform/Windows/windows_gem.cmake b/Templates/CppToolGem/Template/Platform/Windows/windows_gem.cmake similarity index 100% rename from Templates/CustomTool/Template/Platform/Windows/windows_gem.cmake rename to Templates/CppToolGem/Template/Platform/Windows/windows_gem.cmake diff --git a/Templates/CustomTool/Template/Platform/Windows/windows_gem.json b/Templates/CppToolGem/Template/Platform/Windows/windows_gem.json similarity index 100% rename from Templates/CustomTool/Template/Platform/Windows/windows_gem.json rename to Templates/CppToolGem/Template/Platform/Windows/windows_gem.json diff --git a/Templates/CustomTool/Template/Platform/iOS/ios_gem.cmake b/Templates/CppToolGem/Template/Platform/iOS/ios_gem.cmake similarity index 100% rename from Templates/CustomTool/Template/Platform/iOS/ios_gem.cmake rename to Templates/CppToolGem/Template/Platform/iOS/ios_gem.cmake diff --git a/Templates/CustomTool/Template/Platform/iOS/ios_gem.json b/Templates/CppToolGem/Template/Platform/iOS/ios_gem.json similarity index 100% rename from Templates/CustomTool/Template/Platform/iOS/ios_gem.json rename to Templates/CppToolGem/Template/Platform/iOS/ios_gem.json diff --git a/Templates/CustomTool/Template/gem.json b/Templates/CppToolGem/Template/gem.json similarity index 100% rename from Templates/CustomTool/Template/gem.json rename to Templates/CppToolGem/Template/gem.json diff --git a/Templates/CustomTool/Template/preview.png b/Templates/CppToolGem/Template/preview.png similarity index 100% rename from Templates/CustomTool/Template/preview.png rename to Templates/CppToolGem/Template/preview.png diff --git a/Templates/CustomTool/template.json b/Templates/CppToolGem/template.json similarity index 98% rename from Templates/CustomTool/template.json rename to Templates/CppToolGem/template.json index e3221db106..b516dbaec3 100644 --- a/Templates/CustomTool/template.json +++ b/Templates/CppToolGem/template.json @@ -1,12 +1,12 @@ { - "template_name": "CustomTool", - "origin": "The primary repo for CustomTool goes here: i.e. http://www.mydomain.com", - "license": "What license CustomTool uses goes here: i.e. https://opensource.org/licenses/MIT", - "display_name": "CustomTool", + "template_name": "CppToolGem", + "origin": "The primary repo for CppToolGem goes here: i.e. http://www.mydomain.com", + "license": "What license CppToolGem uses goes here: i.e. https://opensource.org/licenses/MIT", + "display_name": "CppToolGem", "summary": "A gem template for a custom tool in C++ that gets registered with the Editor.", "canonical_tags": [], "user_tags": [ - "CustomTool" + "CppToolGem" ], "icon_path": "preview.png", "copyFiles": [ diff --git a/Templates/PythonGem/Template/CMakeLists.txt b/Templates/PythonToolGem/Template/CMakeLists.txt similarity index 100% rename from Templates/PythonGem/Template/CMakeLists.txt rename to Templates/PythonToolGem/Template/CMakeLists.txt diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake b/Templates/PythonToolGem/Template/Code/${NameLower}_editor_files.cmake similarity index 100% rename from Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake rename to Templates/PythonToolGem/Template/Code/${NameLower}_editor_files.cmake diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake b/Templates/PythonToolGem/Template/Code/${NameLower}_editor_shared_files.cmake similarity index 100% rename from Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake rename to Templates/PythonToolGem/Template/Code/${NameLower}_editor_shared_files.cmake diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake b/Templates/PythonToolGem/Template/Code/${NameLower}_editor_tests_files.cmake similarity index 100% rename from Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake rename to Templates/PythonToolGem/Template/Code/${NameLower}_editor_tests_files.cmake diff --git a/Templates/PythonGem/Template/Code/CMakeLists.txt b/Templates/PythonToolGem/Template/Code/CMakeLists.txt similarity index 100% rename from Templates/PythonGem/Template/Code/CMakeLists.txt rename to Templates/PythonToolGem/Template/Code/CMakeLists.txt diff --git a/Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h b/Templates/PythonToolGem/Template/Code/Include/${Name}/${Name}Bus.h similarity index 100% rename from Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h rename to Templates/PythonToolGem/Template/Code/Include/${Name}/${Name}Bus.h diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake b/Templates/PythonToolGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake similarity index 100% rename from Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake rename to Templates/PythonToolGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake b/Templates/PythonToolGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake similarity index 100% rename from Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake rename to Templates/PythonToolGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake b/Templates/PythonToolGem/Template/Code/Platform/Linux/PAL_linux.cmake similarity index 100% rename from Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake rename to Templates/PythonToolGem/Template/Code/Platform/Linux/PAL_linux.cmake diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake b/Templates/PythonToolGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake similarity index 100% rename from Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake rename to Templates/PythonToolGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake b/Templates/PythonToolGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake similarity index 100% rename from Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake rename to Templates/PythonToolGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake b/Templates/PythonToolGem/Template/Code/Platform/Mac/PAL_mac.cmake similarity index 100% rename from Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake rename to Templates/PythonToolGem/Template/Code/Platform/Mac/PAL_mac.cmake diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake b/Templates/PythonToolGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake similarity index 100% rename from Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake rename to Templates/PythonToolGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake b/Templates/PythonToolGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake similarity index 100% rename from Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake rename to Templates/PythonToolGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake b/Templates/PythonToolGem/Template/Code/Platform/Windows/PAL_windows.cmake similarity index 100% rename from Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake rename to Templates/PythonToolGem/Template/Code/Platform/Windows/PAL_windows.cmake diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp b/Templates/PythonToolGem/Template/Code/Source/${Name}EditorModule.cpp similarity index 100% rename from Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp rename to Templates/PythonToolGem/Template/Code/Source/${Name}EditorModule.cpp diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp b/Templates/PythonToolGem/Template/Code/Source/${Name}EditorSystemComponent.cpp similarity index 100% rename from Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp rename to Templates/PythonToolGem/Template/Code/Source/${Name}EditorSystemComponent.cpp diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h b/Templates/PythonToolGem/Template/Code/Source/${Name}EditorSystemComponent.h similarity index 100% rename from Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h rename to Templates/PythonToolGem/Template/Code/Source/${Name}EditorSystemComponent.h diff --git a/Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h b/Templates/PythonToolGem/Template/Code/Source/${Name}ModuleInterface.h similarity index 100% rename from Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h rename to Templates/PythonToolGem/Template/Code/Source/${Name}ModuleInterface.h diff --git a/Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp b/Templates/PythonToolGem/Template/Code/Tests/${Name}EditorTest.cpp similarity index 100% rename from Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp rename to Templates/PythonToolGem/Template/Code/Tests/${Name}EditorTest.cpp diff --git a/Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py b/Templates/PythonToolGem/Template/Editor/Scripts/${NameLower}_dialog.py similarity index 100% rename from Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py rename to Templates/PythonToolGem/Template/Editor/Scripts/${NameLower}_dialog.py diff --git a/Templates/PythonGem/Template/Editor/Scripts/__init__.py b/Templates/PythonToolGem/Template/Editor/Scripts/__init__.py similarity index 100% rename from Templates/PythonGem/Template/Editor/Scripts/__init__.py rename to Templates/PythonToolGem/Template/Editor/Scripts/__init__.py diff --git a/Templates/PythonGem/Template/Editor/Scripts/bootstrap.py b/Templates/PythonToolGem/Template/Editor/Scripts/bootstrap.py similarity index 100% rename from Templates/PythonGem/Template/Editor/Scripts/bootstrap.py rename to Templates/PythonToolGem/Template/Editor/Scripts/bootstrap.py diff --git a/Templates/PythonGem/Template/gem.json b/Templates/PythonToolGem/Template/gem.json similarity index 100% rename from Templates/PythonGem/Template/gem.json rename to Templates/PythonToolGem/Template/gem.json diff --git a/Templates/PythonGem/Template/preview.png b/Templates/PythonToolGem/Template/preview.png similarity index 100% rename from Templates/PythonGem/Template/preview.png rename to Templates/PythonToolGem/Template/preview.png diff --git a/Templates/PythonGem/template.json b/Templates/PythonToolGem/template.json similarity index 94% rename from Templates/PythonGem/template.json rename to Templates/PythonToolGem/template.json index 75be757abb..4d85373ead 100644 --- a/Templates/PythonGem/template.json +++ b/Templates/PythonToolGem/template.json @@ -1,14 +1,14 @@ { - "template_name": "PythonGem", + "template_name": "PythonToolGem", "restricted_name": "o3de", "restricted_platform_relative_path": "Templates", - "origin": "The primary repo for PythonGem goes here: i.e. http://www.mydomain.com", - "license": "What license PythonGem uses goes here: i.e. https://opensource.org/licenses/MIT", - "display_name": "PythonGem", - "summary": "A short description of PythonGem.", + "origin": "The primary repo for PythonToolGem goes here: i.e. http://www.mydomain.com", + "license": "What license PythonToolGem uses goes here: i.e. https://opensource.org/licenses/MIT", + "display_name": "PythonToolGem", + "summary": "A gem template for a custom tool in Python that gets registered with the Editor.", "canonical_tags": [], "user_tags": [ - "PythonGem" + "PythonToolGem" ], "icon_path": "preview.png", "copyFiles": [ diff --git a/engine.json b/engine.json index 14deffecb8..05ccd0abfd 100644 --- a/engine.json +++ b/engine.json @@ -92,8 +92,9 @@ "templates": [ "Templates/AssetGem", "Templates/DefaultGem", - "Templates/CustomTool", "Templates/DefaultProject", - "Templates/MinimalProject" + "Templates/CppToolGem", + "Templates/MinimalProject", + "Templates/PythonToolGem" ] } From b48073b1712c737fe3627fb9f5713859b8e5326e Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Wed, 27 Oct 2021 14:41:00 -0700 Subject: [PATCH 077/120] fixes PR comments Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/LYWrappers.cmake | 2 +- cmake/Platform/Common/Install_common.cmake | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmake/LYWrappers.cmake b/cmake/LYWrappers.cmake index c617603fa3..0416d41ece 100644 --- a/cmake/LYWrappers.cmake +++ b/cmake/LYWrappers.cmake @@ -459,7 +459,7 @@ function(ly_delayed_target_link_libraries) endforeach() endforeach() - endif() + endif() endforeach() set_property(GLOBAL PROPERTY LY_DELAYED_LINK_TARGETS) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index ceab70162e..6d43661b99 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -212,10 +212,10 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar endif() string(REPEAT " " 12 PLACEHOLDER_INDENT) - get_property(inteface_build_dependencies_props TARGET ${TARGET_NAME} PROPERTY LY_DELAYED_LINK) + get_property(interface_build_dependencies_props TARGET ${TARGET_NAME} PROPERTY LY_DELAYED_LINK) unset(INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER) - if(inteface_build_dependencies_props) - cmake_parse_arguments(build_deps "" "" "PRIVATE;PUBLIC;INTERFACE" ${inteface_build_dependencies_props}) + if(interface_build_dependencies_props) + cmake_parse_arguments(build_deps "" "" "PRIVATE;PUBLIC;INTERFACE" ${interface_build_dependencies_props}) # Interface and public dependencies should always be exposed set(build_deps_target ${build_deps_INTERFACE}) if(build_deps_PUBLIC) @@ -309,7 +309,7 @@ set_property(TARGET ${NAME_PLACEHOLDER} ly_install(FILES "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/${NAME_PLACEHOLDER}_${conf}.cmake" DESTINATION ${relative_target_source_dir}/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF} - CONFIGURATIONS ${conf} + CONFIGURATIONS ${conf} ) endforeach() From 308fcd8bf220b3098965deaf7e59cd7d4c11844b Mon Sep 17 00:00:00 2001 From: Neil Widmaier Date: Wed, 27 Oct 2021 14:53:20 -0700 Subject: [PATCH 078/120] Adding the Hydra P0 Grid component test, and including it in the Test_Suite_Main_Optomized. Signed-off-by: Neil Widmaier --- .../Atom/TestSuite_Main_Optimized.py | 4 + .../hydra_AtomEditorComponents_GridAdded.py | 158 ++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py index 69a0e9c85d..45298ed563 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py @@ -37,6 +37,10 @@ class TestAutomation(EditorTestSuite): @pytest.mark.test_case_id("C32078115") class AtomEditorComponents_GlobalSkylightIBLAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_GlobalSkylightIBLAdded as test_module + + @pytest.mark.test_case_id("C32078122") + class AtomEditorComponents_GridAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_GridAdded as test_module @pytest.mark.test_case_id("C32078117") class AtomEditorComponents_LightAdded(EditorSharedTest): diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py new file mode 100644 index 0000000000..a77a1f50a4 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GridAdded.py @@ -0,0 +1,158 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + +class Tests: + creation_undo = ( + "UNDO Entity creation success", + "UNDO Entity creation failed") + creation_redo = ( + "REDO Entity creation success", + "REDO Entity creation failed") + grid_entity_creation = ( + "Grid Entity successfully created", + "Grid Entity failed to be created") + grid_component_added = ( + "Entity has a Grid component", + "Entity failed to find Grid component") + enter_game_mode = ( + "Entered game mode", + "Failed to enter game mode") + exit_game_mode = ( + "Exited game mode", + "Couldn't exit game mode") + is_visible = ( + "Entity is visible", + "Entity was not visible") + is_hidden = ( + "Entity is hidden", + "Entity was not hidden") + entity_deleted = ( + "Entity deleted", + "Entity was not deleted") + deletion_undo = ( + "UNDO deletion success", + "UNDO deletion failed") + deletion_redo = ( + "REDO deletion success", + "REDO deletion failed") + + +def AtomEditorComponents_Grid_AddedToEntity(): + """ + Summary: + Tests the Grid component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a Grid entity with no components. + 2) Add a Grid component to Grid entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Enter/Exit game mode. + 6) Test IsHidden. + 7) Test IsVisible. + 8) Delete Grid entity. + 9) UNDO deletion. + 10) REDO deletion. + 11) Look for errors. + + :return: None + """ + + import os + + import azlmbr.legacy.general as general + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper + from Atom.atom_utils.atom_constants import AtomComponentProperties + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + TestHelper.init_idle() + TestHelper.open_level("", "Base") + + # Test steps begin. + # 1. Create a Grid entity with no components. + grid_entity = EditorEntity.create_editor_entity(AtomComponentProperties.grid()) + Report.critical_result(Tests.grid_entity_creation, grid_entity.exists()) + + # 2. Add a Grid component to Grid entity. + grid_component = grid_entity.add_component(AtomComponentProperties.grid()) + Report.critical_result( + Tests.grid_component_added, + grid_entity.has_component(AtomComponentProperties.grid())) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not grid_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, grid_entity.exists()) + + # 5. Enter/Exit game mode. + TestHelper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + TestHelper.exit_game_mode(Tests.exit_game_mode) + + # 6. Test IsHidden. + grid_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, grid_entity.is_hidden() is True) + + # 7. Test IsVisible. + grid_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, grid_entity.is_visible() is True) + + # 8. Delete Grid entity. + grid_entity.delete() + Report.result(Tests.entity_deleted, not grid_entity.exists()) + + # 9. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, grid_entity.exists()) + + # 10. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not grid_entity.exists()) + + # 11. Look for errors or asserts. + TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_tracer.asserts: + Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}") + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_Grid_AddedToEntity) From c82f97c03ccdbb32036dcbfe71b622af34e3ed69 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Wed, 27 Oct 2021 16:59:39 -0700 Subject: [PATCH 079/120] Moves the inclusion of the platform-specific install files to the end of cmake/Install.cmake so the install wrapper functions are available Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/Install.cmake | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cmake/Install.cmake b/cmake/Install.cmake index a7340c29f3..b558590b9e 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -8,11 +8,6 @@ set(LY_INSTALL_ENABLED TRUE CACHE BOOL "Indicates if the install process is enabled") -if(LY_INSTALL_ENABLED) - ly_get_absolute_pal_filename(pal_dir ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}) - include(${pal_dir}/Install_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) -endif() - #! ly_install: wrapper to install that handles common functionality # # \notes: @@ -22,6 +17,10 @@ endif() # function(ly_install) + if(NOT LY_INSTALL_ENABLED) + return() + endif() + cmake_parse_arguments(ly_install "" "COMPONENT" "" ${ARGN}) if (NOT ly_install_COMPONENT OR "${ly_install_COMPONENT}" STREQUAL "${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}") # if it is installing under the default component, we need to de-duplicate since we can have @@ -194,3 +193,8 @@ function(ly_install_run_script SCRIPT) ) endfunction() + +if(LY_INSTALL_ENABLED) + ly_get_absolute_pal_filename(pal_dir ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}) + include(${pal_dir}/Install_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) +endif() From dd0780f6ec93abc5092beda86752f5a27264ba7a Mon Sep 17 00:00:00 2001 From: greerdv Date: Thu, 28 Oct 2021 12:26:05 +0100 Subject: [PATCH 080/120] make sure joint frame rotations are editable for ragdoll setup Signed-off-by: greerdv --- .../Configuration/JointConfiguration.cpp | 68 ++++++++++++++++++- .../Configuration/JointConfiguration.h | 24 ++++++- .../CommandSystem/Source/RagdollCommands.cpp | 2 + 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp index d2f74510e5..bcc71e0cf6 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp @@ -10,6 +10,7 @@ #include #include +#include namespace AzPhysics { @@ -28,6 +29,71 @@ namespace AzPhysics ->Field("ChildLocalPosition", &JointConfiguration::m_childLocalPosition) ->Field("StartSimulationEnabled", &JointConfiguration::m_startSimulationEnabled) ; + + if (auto* editContext = serializeContext->GetEditContext()) + { + editContext->Class("Joint Configuration", "Joint configuration.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_parentLocalRotation, + "Parent local rotation", "Parent joint frame relative to parent body.") + ->Attribute(AZ::Edit::Attributes::Visibility, &GetParentLocalRotationVisibility) + ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_parentLocalPosition, + "Parent local position", "Joint position relative to parent body.") + ->Attribute(AZ::Edit::Attributes::Visibility, &GetParentLocalPositionVisibility) + ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_childLocalRotation, + "Child local rotation", "Child joint frame relative to child body.") + ->Attribute(AZ::Edit::Attributes::Visibility, &GetChildLocalRotationVisibility) + ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_childLocalPosition, + "Child local position", "Joint position relative to child body.") + ->Attribute(AZ::Edit::Attributes::Visibility, &GetChildLocalPositionVisibility) + ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_startSimulationEnabled, + "Start simulation enabled", "When active, the joint will be enabled when the simulation begins.") + ->Attribute(AZ::Edit::Attributes::Visibility, &GetStartSimulationEnabledVisibility) + ; + } } } -} + + AZ::Crc32 JointConfiguration::GetPropertyVisibility(JointConfiguration::PropertyVisibility property) const + { + return (m_propertyVisibilityFlags & property) != 0 ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide; + } + + void JointConfiguration::SetPropertyVisibility(JointConfiguration::PropertyVisibility property, bool isVisible) + { + if (isVisible) + { + m_propertyVisibilityFlags |= property; + } + else + { + m_propertyVisibilityFlags &= ~property; + } + } + + AZ::Crc32 JointConfiguration::GetParentLocalRotationVisibility() const + { + return GetPropertyVisibility(JointConfiguration::PropertyVisibility::ParentLocalRotation); + } + + AZ::Crc32 JointConfiguration::GetParentLocalPositionVisibility() const + { + return GetPropertyVisibility(JointConfiguration::PropertyVisibility::ParentLocalPosition); + } + + AZ::Crc32 JointConfiguration::GetChildLocalRotationVisibility() const + { + return GetPropertyVisibility(JointConfiguration::PropertyVisibility::ChildLocalRotation); + } + + AZ::Crc32 JointConfiguration::GetChildLocalPositionVisibility() const + { + return GetPropertyVisibility(JointConfiguration::PropertyVisibility::ChildLocalPosition); + } + + AZ::Crc32 JointConfiguration::GetStartSimulationEnabledVisibility() const + { + return GetPropertyVisibility(JointConfiguration::PropertyVisibility::StartSimulationEnabled); + } +} // namespace AzPhysics diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.h index ff9bfb5bea..2a246692d7 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.h @@ -31,6 +31,25 @@ namespace AzPhysics JointConfiguration() = default; virtual ~JointConfiguration() = default; + // Visibility helpers for use in the Editor when reflected. + enum PropertyVisibility : AZ::u8 + { + ParentLocalRotation = 1 << 0, //!< Whether the parent local rotation is visible. + ParentLocalPosition = 1 << 1, //!< Whether the parent local position is visible. + ChildLocalRotation = 1 << 2, //!< Whether the child local rotation is visible. + ChildLocalPosition = 1 << 3, //!< Whether the child local position is visible. + StartSimulationEnabled = 1 << 4 //!< Whether the start simulation enabled setting is visible. + }; + + AZ::Crc32 GetPropertyVisibility(PropertyVisibility property) const; + void SetPropertyVisibility(PropertyVisibility property, bool isVisible); + + AZ::Crc32 GetParentLocalRotationVisibility() const; + AZ::Crc32 GetParentLocalPositionVisibility() const; + AZ::Crc32 GetChildLocalRotationVisibility() const; + AZ::Crc32 GetChildLocalPositionVisibility() const; + AZ::Crc32 GetStartSimulationEnabledVisibility() const; + // Entity/object association. void* m_customUserData = nullptr; @@ -40,8 +59,11 @@ namespace AzPhysics AZ::Quaternion m_childLocalRotation = AZ::Quaternion::CreateIdentity(); ///< Child joint frame relative to child body. AZ::Vector3 m_childLocalPosition = AZ::Vector3::CreateZero(); ///< Joint position relative to child body. bool m_startSimulationEnabled = true; - + // For debugging/tracking purposes only. AZStd::string m_debugName; + + // Default all visibility settings to invisible, since most joint configurations don't need to display these. + AZ::u8 m_propertyVisibilityFlags = 0; }; } diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/RagdollCommands.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/RagdollCommands.cpp index e51c9ece0b..d7c7bf69e5 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/RagdollCommands.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/RagdollCommands.cpp @@ -105,6 +105,8 @@ namespace EMotionFX *jointTypeId, parentBindRotationWorld, nodeBindRotationWorld, boneDirection, exampleRotationsLocal); AZ_Assert(jointLimitConfig, "Could not create joint limit configuration."); + jointLimitConfig->SetPropertyVisibility(AzPhysics::JointConfiguration::PropertyVisibility::ParentLocalRotation, true); + jointLimitConfig->SetPropertyVisibility(AzPhysics::JointConfiguration::PropertyVisibility::ChildLocalRotation, true); return jointLimitConfig; } } From 9b2afbc39b9fb619ba10604823027304851e4c1e Mon Sep 17 00:00:00 2001 From: greerdv Date: Thu, 28 Oct 2021 12:51:33 +0100 Subject: [PATCH 081/120] fix explicit qualification of member function addresses Signed-off-by: greerdv --- .../Physics/Configuration/JointConfiguration.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp index bcc71e0cf6..0a0e1bb8e3 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/JointConfiguration.cpp @@ -37,19 +37,19 @@ namespace AzPhysics ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_parentLocalRotation, "Parent local rotation", "Parent joint frame relative to parent body.") - ->Attribute(AZ::Edit::Attributes::Visibility, &GetParentLocalRotationVisibility) + ->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetParentLocalRotationVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_parentLocalPosition, "Parent local position", "Joint position relative to parent body.") - ->Attribute(AZ::Edit::Attributes::Visibility, &GetParentLocalPositionVisibility) + ->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetParentLocalPositionVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_childLocalRotation, "Child local rotation", "Child joint frame relative to child body.") - ->Attribute(AZ::Edit::Attributes::Visibility, &GetChildLocalRotationVisibility) + ->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetChildLocalRotationVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_childLocalPosition, "Child local position", "Joint position relative to child body.") - ->Attribute(AZ::Edit::Attributes::Visibility, &GetChildLocalPositionVisibility) + ->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetChildLocalPositionVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_startSimulationEnabled, "Start simulation enabled", "When active, the joint will be enabled when the simulation begins.") - ->Attribute(AZ::Edit::Attributes::Visibility, &GetStartSimulationEnabledVisibility) + ->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetStartSimulationEnabledVisibility) ; } } From a661189ea9d9318939f5ea2daca9ec54bfed2f4a Mon Sep 17 00:00:00 2001 From: Ken Pruiksma Date: Thu, 28 Oct 2021 10:53:57 -0500 Subject: [PATCH 082/120] Fix for rendering artifacts on height map update. (#5066) * Fix for rendering artifacts on height map update. This was being caused by not always lining up update aabbs with the query resolution correctly. In the future the float -> integer aabb calculations should be abstracted away. Some of this is done in the detail material ID work, but doesn't exist in the stabilization branch so we can circle around to it later. Signed-off-by: Ken Pruiksma * PR review updates - fixing cast, making constexpr for bytes per pixel. Signed-off-by: Ken Pruiksma --- .../TerrainFeatureProcessor.cpp | 91 +++++++++++-------- .../TerrainRenderer/TerrainFeatureProcessor.h | 4 - 2 files changed, 51 insertions(+), 44 deletions(-) diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index e6aed28897..1b13d3bb98 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -155,9 +155,11 @@ namespace Terrain const AZ::Transform transform = AZ::Transform::CreateTranslation(worldBounds.GetCenter()); - AZ::Vector2 queryResolution = AZ::Vector2(1.0f); + AZ::Vector2 queryResolution2D = AZ::Vector2(1.0f); AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( - queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution); + queryResolution2D, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution); + // Currently query resolution is multidimensional but the rendering system only supports this changing in one dimension. + float queryResolution = queryResolution2D.GetX(); // Sectors need to be rebuilt if the world bounds change in the x/y, or the sample spacing changes. m_areaData.m_rebuildSectors = m_areaData.m_rebuildSectors || @@ -165,16 +167,11 @@ namespace Terrain m_areaData.m_terrainBounds.GetMin().GetY() != worldBounds.GetMin().GetY() || m_areaData.m_terrainBounds.GetMax().GetX() != worldBounds.GetMax().GetX() || m_areaData.m_terrainBounds.GetMax().GetY() != worldBounds.GetMax().GetY() || - m_areaData.m_sampleSpacing != queryResolution.GetX(); + m_areaData.m_sampleSpacing != queryResolution; m_areaData.m_transform = transform; m_areaData.m_terrainBounds = worldBounds; - m_areaData.m_heightmapImageWidth = aznumeric_cast(worldBounds.GetXExtent() / queryResolution.GetX()); - m_areaData.m_heightmapImageHeight = aznumeric_cast(worldBounds.GetYExtent() / queryResolution.GetY()); - m_areaData.m_updateWidth = aznumeric_cast(m_dirtyRegion.GetXExtent() / queryResolution.GetX()); - m_areaData.m_updateHeight = aznumeric_cast(m_dirtyRegion.GetYExtent() / queryResolution.GetY()); - // Currently query resolution is multidimensional but the rendering system only supports this changing in one dimension. - m_areaData.m_sampleSpacing = queryResolution.GetX(); + m_areaData.m_sampleSpacing = queryResolution; m_areaData.m_heightmapUpdated = true; } @@ -261,31 +258,42 @@ namespace Terrain void TerrainFeatureProcessor::UpdateTerrainData() { static const AZ::Name TerrainHeightmapName = AZ::Name(TerrainHeightmapChars); - - uint32_t width = m_areaData.m_updateWidth; - uint32_t height = m_areaData.m_updateHeight; - const AZ::Aabb& worldBounds = m_areaData.m_terrainBounds; + const float queryResolution = m_areaData.m_sampleSpacing; + const AZ::Aabb& worldBounds = m_areaData.m_terrainBounds; - const AZ::RHI::Size worldSize = AZ::RHI::Size(m_areaData.m_heightmapImageWidth, m_areaData.m_heightmapImageHeight, 1); + int32_t heightmapImageXStart = aznumeric_cast(AZStd::ceilf(worldBounds.GetMin().GetX() / queryResolution)); + int32_t heightmapImageXEnd = aznumeric_cast(AZStd::floorf(worldBounds.GetMax().GetX() / queryResolution)) + 1; + int32_t heightmapImageYStart = aznumeric_cast(AZStd::ceilf(worldBounds.GetMin().GetY() / queryResolution)); + int32_t heightmapImageYEnd = aznumeric_cast(AZStd::floorf(worldBounds.GetMax().GetY() / queryResolution)) + 1; + uint32_t heightmapImageWidth = heightmapImageXEnd - heightmapImageXStart; + uint32_t heightmapImageHeight = heightmapImageYEnd - heightmapImageYStart; - if (!m_areaData.m_heightmapImage || m_areaData.m_heightmapImage->GetDescriptor().m_size != worldSize) + const AZ::RHI::Size heightmapSize = AZ::RHI::Size(heightmapImageWidth, heightmapImageHeight, 1); + + if (!m_areaData.m_heightmapImage || m_areaData.m_heightmapImage->GetDescriptor().m_size != heightmapSize) { - // World size changed, so the whole world needs updating. - width = worldSize.m_width; - height = worldSize.m_height; - m_dirtyRegion = worldBounds; - const AZ::Data::Instance imagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool(); AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D( - AZ::RHI::ImageBindFlags::ShaderRead, width, height, AZ::RHI::Format::R16_UNORM + AZ::RHI::ImageBindFlags::ShaderRead, heightmapSize.m_width, heightmapSize.m_height, AZ::RHI::Format::R16_UNORM ); + m_areaData.m_heightmapImage = AZ::RPI::AttachmentImage::Create(*imagePool.get(), imageDescriptor, TerrainHeightmapName, nullptr, nullptr); AZ_Error(TerrainFPName, m_areaData.m_heightmapImage, "Failed to initialize the heightmap image!"); + + // World size changed, so the whole height map needs updating. + m_dirtyRegion = worldBounds; } + + int32_t xStart = aznumeric_cast(AZStd::ceilf(m_dirtyRegion.GetMin().GetX() / queryResolution)); + int32_t xEnd = aznumeric_cast(AZStd::floorf(m_dirtyRegion.GetMax().GetX() / queryResolution)) + 1; + int32_t yStart = aznumeric_cast(AZStd::ceilf(m_dirtyRegion.GetMin().GetY() / queryResolution)); + int32_t yEnd = aznumeric_cast(AZStd::floorf(m_dirtyRegion.GetMax().GetY() / queryResolution)) + 1; + uint32_t updateWidth = xEnd - xStart; + uint32_t updateHeight = yEnd - yStart; AZStd::vector pixels; - pixels.reserve(width * height); + pixels.reserve(updateWidth * updateHeight); { // Block other threads from accessing the surface data bus while we are in GetHeightFromFloats (which may call into the SurfaceData bus). @@ -297,18 +305,17 @@ namespace Terrain auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false); typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex); - for (uint32_t y = 0; y < height; y++) + for (int32_t y = yStart; y < yEnd; y++) { - for (uint32_t x = 0; x < width; x++) + for (int32_t x = xStart; x < xEnd; x++) { bool terrainExists = true; float terrainHeight = 0.0f; + float xPos = x * queryResolution; + float yPos = y * queryResolution; AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( terrainHeight, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, - (x * queryResolution) + m_dirtyRegion.GetMin().GetX(), - (y * queryResolution) + m_dirtyRegion.GetMin().GetY(), - AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, - &terrainExists); + xPos, yPos, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); const float clampedHeight = AZ::GetClamp((terrainHeight - worldBounds.GetMin().GetZ()) / worldBounds.GetExtents().GetZ(), 0.0f, 1.0f); const float expandedHeight = AZStd::roundf(clampedHeight * AZStd::numeric_limits::max()); @@ -321,16 +328,18 @@ namespace Terrain if (m_areaData.m_heightmapImage) { - const float left = (m_dirtyRegion.GetMin().GetX() - worldBounds.GetMin().GetX()) / queryResolution; - const float top = (m_dirtyRegion.GetMin().GetY() - worldBounds.GetMin().GetY()) / queryResolution; + constexpr uint32_t BytesPerPixel = sizeof(uint16_t); + const float left = xStart - (worldBounds.GetMin().GetX() / queryResolution); + const float top = yStart - (worldBounds.GetMin().GetY() / queryResolution); + AZ::RHI::ImageUpdateRequest imageUpdateRequest; imageUpdateRequest.m_imageSubresourcePixelOffset.m_left = aznumeric_cast(left); imageUpdateRequest.m_imageSubresourcePixelOffset.m_top = aznumeric_cast(top); - imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerRow = width * sizeof(uint16_t); - imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerImage = width * height * sizeof(uint16_t); - imageUpdateRequest.m_sourceSubresourceLayout.m_rowCount = height; - imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_width = width; - imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_height = height; + imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerRow = updateWidth * BytesPerPixel; + imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerImage = updateWidth * updateHeight * BytesPerPixel; + imageUpdateRequest.m_sourceSubresourceLayout.m_rowCount = updateHeight; + imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_width = updateWidth; + imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_height = updateHeight; imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_depth = 1; imageUpdateRequest.m_sourceData = pixels.data(); imageUpdateRequest.m_image = m_areaData.m_heightmapImage->GetRHIImage(); @@ -492,6 +501,12 @@ namespace Terrain m_areaData.m_heightmapUpdated = false; m_areaData.m_macroMaterialsUpdated = false; + AZStd::array uvStep = + { + 1.0f / aznumeric_cast(m_areaData.m_terrainBounds.GetXExtent() / m_areaData.m_sampleSpacing), + 1.0f / aznumeric_cast(m_areaData.m_terrainBounds.GetYExtent() / m_areaData.m_sampleSpacing), + }; + for (SectorData& sectorData : m_sectorData) { ShaderTerrainData terrainDataForSrg; @@ -509,11 +524,7 @@ namespace Terrain ((yPatch + GridMeters) - terrainBounds.GetMin().GetY()) / terrainBounds.GetYExtent() }; - terrainDataForSrg.m_uvStep = - { - 1.0f / m_areaData.m_heightmapImageWidth, - 1.0f / m_areaData.m_heightmapImageHeight, - }; + terrainDataForSrg.m_uvStep = uvStep; AZ::Transform transform = m_areaData.m_transform; transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ()); diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h index f82fd8ecb0..91e3ce9a5c 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h @@ -169,10 +169,6 @@ namespace Terrain AZ::Transform m_transform{ AZ::Transform::CreateIdentity() }; AZ::Aabb m_terrainBounds{ AZ::Aabb::CreateNull() }; AZ::Data::Instance m_heightmapImage; - uint32_t m_heightmapImageWidth{ 0 }; - uint32_t m_heightmapImageHeight{ 0 }; - uint32_t m_updateWidth{ 0 }; - uint32_t m_updateHeight{ 0 }; float m_sampleSpacing{ 0.0f }; bool m_heightmapUpdated{ true }; bool m_macroMaterialsUpdated{ true }; From 67f90a9b37d9a402ed896a4acd7704dcc7a9f64b Mon Sep 17 00:00:00 2001 From: Tommy Walton <82672795+amzn-tommy@users.noreply.github.com> Date: Thu, 28 Oct 2021 10:22:20 -0700 Subject: [PATCH 083/120] Add missing dependencies to pass builder (#4884) * Adding shaders and attimage files as runtime depenencies for pass files, so that they are included in asset bundles. Also using the correct job key for attimage files. Signed-off-by: Tommy Walton * Use a reference to avoid a copy Signed-off-by: Tommy Walton * Bumping the AnyAsset builder version Signed-off-by: Tommy Walton * Revert "Bumping the AnyAsset builder version" This reverts commit 778798ae9cdd93ebe93248b3113e4cfb7609020d. Signed-off-by: Tommy Walton --- .../Source/RPI.Builders/Pass/PassBuilder.cpp | 58 ++++++++++++++++--- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Pass/PassBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Pass/PassBuilder.cpp index d5e243c687..6a0b10633e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Pass/PassBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Pass/PassBuilder.cpp @@ -10,8 +10,8 @@ #include #include - #include +#include #include #include @@ -33,11 +33,27 @@ namespace AZ static const char* PassAssetExtension = "pass"; } + namespace PassBuilderNamespace + { + enum PassDependencies + { + Shader, + AttachmentImage, + Count + }; + + static const AZStd::tuple DependencyExtensionJobKeyTable[PassDependencies::Count] = + { + {".shader", "Shader Asset"}, + {".attimage", "Any Asset Builder"} + }; + } + void PassBuilder::RegisterBuilder() { AssetBuilderSDK::AssetBuilderDesc builder; builder.m_name = PassBuilderJobKey; - builder.m_version = 13; // antonmic: making .pass files declare dependency on shaders they reference + builder.m_version = 14; // making .pass files emit product dependencies for the shaders they reference so they are picked up by the asset bundler builder.m_busId = azrtti_typeid(); builder.m_createJobFunction = AZStd::bind(&PassBuilder::CreateJobs, this, AZStd::placeholders::_1, AZStd::placeholders::_2); builder.m_processJobFunction = AZStd::bind(&PassBuilder::ProcessJob, this, AZStd::placeholders::_1, AZStd::placeholders::_2); @@ -104,8 +120,27 @@ namespace AZ } } + bool SetJobKeyForExtension(const AZStd::string& filePath, FindPassReferenceAssetParams& params) + { + AZStd::string extension; + StringFunc::Path::GetExtension(filePath.c_str(), extension); + for (const auto& [dependencyExtension, jobKey] : PassBuilderNamespace::DependencyExtensionJobKeyTable) + { + if (extension == dependencyExtension) + { + params.jobKey = jobKey; + return true; + } + } + + AZ_Error(PassBuilderName, false, "PassBuilder found a dependency with extension '%s', but does not know the corresponding job key. Add the job key for that extension to SetJobKeyForExtension in PassBuilder.cpp", extension.c_str()); + params.jobKey = "Unknown"; + return false; + } + // Helper function to find all assetId's and object references - bool FindReferencedAssets(FindPassReferenceAssetParams& params, AssetBuilderSDK::JobDescriptor* job) + bool FindReferencedAssets( + FindPassReferenceAssetParams& params, AssetBuilderSDK::JobDescriptor* job, AZStd::vector* productDependencies) { SerializeContext::ErrorHandler errorLogger; errorLogger.Reset(); @@ -129,8 +164,8 @@ namespace AZ if (job != nullptr) // Create Job Phase { params.dependencySourceFile = path; - bool dependencyAddedSuccessfully = AddDependency(params, job); - success = dependencyAddedSuccessfully && success; + success &= SetJobKeyForExtension(path, params); + success &= AddDependency(params, job); } else // Process Job Phase { @@ -139,6 +174,9 @@ namespace AZ if (assetIdOutcome) { assetReference->m_assetId = assetIdOutcome.GetValue(); + productDependencies->push_back( + AssetBuilderSDK::ProductDependency{assetReference->m_assetId, AZ::Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad)} + ); } else { @@ -223,9 +261,9 @@ namespace AZ params.passAssetSourceFile = request.m_sourceFile; params.passAssetUuid = passAssetUuid; params.serializeContext = serializeContext; - params.jobKey = "Shader Asset"; + params.jobKey = "Unknown"; - if (!FindReferencedAssets(params, &job)) + if (!FindReferencedAssets(params, &job, nullptr)) { return; } @@ -287,9 +325,10 @@ namespace AZ params.passAssetSourceFile = request.m_sourceFile; params.passAssetUuid = passAssetUuid; params.serializeContext = serializeContext; - params.jobKey = "Shader Asset"; + params.jobKey = "Unknown"; - if (!FindReferencedAssets(params, nullptr)) + AZStd::vector productDependencies; + if (!FindReferencedAssets(params, nullptr, &productDependencies)) { return; } @@ -313,6 +352,7 @@ namespace AZ // --- Save output product(s) to response --- AssetBuilderSDK::JobProduct jobProduct(destPath, PassAsset::RTTI_Type(), 0); + jobProduct.m_dependencies = productDependencies; jobProduct.m_dependenciesHandled = true; response.m_outputProducts.push_back(jobProduct); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; From e288ae47b479882ca89969b514c3a0ef3f3c33ac Mon Sep 17 00:00:00 2001 From: John Jones-Steele <82226755+jjjoness@users.noreply.github.com> Date: Thu, 28 Oct 2021 19:36:39 +0100 Subject: [PATCH 084/120] Fix for using too large values on Terrain World (#5091) Signed-off-by: John Jones-Steele --- .../Code/Source/Components/TerrainWorldComponent.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp index d8b7308ef7..bd65cf6abc 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp @@ -32,16 +32,22 @@ namespace Terrain AZ::EditContext* edit = serialize->GetEditContext(); if (edit) { - edit->Class( - "Terrain World Component", "Data required for the terrain system to run") + edit->Class("Terrain World Component", "Data required for the terrain system to run") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC_CE("Level") })) ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_worldMin, "World Bounds (Min)", "") + // Temporary constraint until the rest of the Terrain system is updated to support larger worlds. + ->Attribute(AZ::Edit::Attributes::Min, -2048.0f) + ->Attribute(AZ::Edit::Attributes::Max, 2048.0f) ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_worldMax, "World Bounds (Max)", "") - ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_heightQueryResolution, "Height Query Resolution (m)", "") + // Temporary constraint until the rest of the Terrain system is updated to support larger worlds. + ->Attribute(AZ::Edit::Attributes::Min, -2048.0f) + ->Attribute(AZ::Edit::Attributes::Max, 2048.0f) + ->DataElement( + AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_heightQueryResolution, "Height Query Resolution (m)", "") ; } } From b9147c60a063ce3c4fda5b6807899b2994605d65 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Thu, 28 Oct 2021 13:45:19 -0500 Subject: [PATCH 085/120] Added the generated cmake_dependencies.*.setreg files to engine.pak (#5073) * Copied the generated cmake_dependencies.*.setreg file to the Cache directory Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed the platform name from the bootstrap.game.*.setreg Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- .../AzCore/Component/ComponentApplication.cpp | 5 ---- .../Settings/SettingsRegistryMergeUtils.cpp | 14 ++++++++--- .../Application/GameApplication.cpp | 2 +- .../SettingsRegistryBuilder.cpp | 14 +++++++---- cmake/Projects.cmake | 25 +++++++++++++++++-- 5 files changed, 43 insertions(+), 17 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp index dabf02e260..df8db79db0 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp @@ -215,11 +215,6 @@ namespace AZ m_oldProjectPath = newProjectPath; // Merge the project.json file into settings registry under ProjectSettingsRootKey path. - AZ::IO::FixedMaxPath projectMetadataFile{ AZ::SettingsRegistryMergeUtils::FindEngineRoot(m_registry) / newProjectPath }; - projectMetadataFile /= "project.json"; - m_registry.MergeSettingsFile(projectMetadataFile.Native(), - AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey); - // Update all the runtime file paths based on the new "project_path" value. AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry); } diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp index 36f66312d8..5458a3fadf 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp @@ -634,12 +634,18 @@ namespace AZ::SettingsRegistryMergeUtils } // Project name - if it was set via merging project.json use that value, otherwise use the project path's folder name. - auto projectNameKey = - AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey) + constexpr auto projectNameKey = + FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey) + "/project_name"; - AZ::SettingsRegistryInterface::FixedValueString projectName; - if (!registry.Get(projectName, projectNameKey)) + // Read the project name from the project.json file if it exists + if (AZ::IO::FixedMaxPath projectJsonPath = normalizedProjectPath / "project.json"; + AZ::IO::SystemFile::Exists(projectJsonPath.c_str())) + { + registry.MergeSettingsFile(projectJsonPath.Native(), + AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey); + } + if (FixedValueString projectName; !registry.Get(projectName, projectNameKey)) { projectName = path.Filename().Native(); registry.Set(projectNameKey, projectName); diff --git a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp index 0cce93d751..6957844452 100644 --- a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp +++ b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp @@ -82,7 +82,7 @@ namespace AzGameFramework // Used the lowercase the platform name since the bootstrap.game...setreg is being loaded // from the asset cache root where all the files are in lowercased from regardless of the filesystem case-sensitivity - static constexpr char filename[] = "bootstrap.game." AZ_BUILD_CONFIGURATION_TYPE "." AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER ".setreg"; + static constexpr char filename[] = "bootstrap.game." AZ_BUILD_CONFIGURATION_TYPE ".setreg"; AZ::IO::FixedMaxPath cacheRootPath; if (registry.Get(cacheRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder)) diff --git a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp index c65ab24aed..6f15fa5ea2 100644 --- a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp +++ b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp @@ -259,6 +259,11 @@ namespace AssetProcessor scratchBuffer.reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer; AZStd::fixed_vector platformCodes; AzFramework::PlatformHelper::AppendPlatformCodeNames(platformCodes, request.m_platformInfo.m_identifier); + AZ_Assert(platformCodes.size() <= 1, "A one-to-one mapping of asset type platform identifier" + " to platform codename is required in the SettingsRegistryBuilder." + " The bootstrap.game is now only produced per build configuration and doesn't take into account" + " different platforms names"); + const AZStd::string& assetPlatformIdentifier = request.m_jobDescription.GetPlatformIdentifier(); // Determines the suffix that will be used for the launcher based on processing server vs non-server assets const char* launcherType = assetPlatformIdentifier != AzFramework::PlatformHelper::GetPlatformName(AzFramework::PlatformId::SERVER) @@ -293,9 +298,10 @@ namespace AssetProcessor outputBuffer.Reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer; SettingsExporter exporter(outputBuffer, excludes); - for (AZStd::string_view platform : platformCodes) + if (!platformCodes.empty()) { - AZ::u32 productSubID = static_cast(AZStd::hash{}(platform)); // Deliberately ignoring half the bits. + AZStd::string_view platform = platformCodes.front(); + constexpr AZ::u32 productSubID = 0; for (size_t i = 0; i < AZStd::size(specializations); ++i) { const AZ::SettingsRegistryInterface::Specializations& specialization = specializations[i]; @@ -337,7 +343,7 @@ namespace AssetProcessor // The purpose of this section is to copy the Gem's SourcePaths from the Global Settings Registry // the local SettingsRegistry. The reason this is needed is so that the call to // `MergeSettingsToRegistry_GemRegistries` below is able to locate each gem's "/Registry" folder - // that will be merged into the bootstrap.game...setreg file + // that will be merged into the bootstrap.game..setreg file // This is used by the GameLauncher applications to read from a single merged .setreg file // containing the settings needed to run a game/simulation without have access to the source code base registry AZStd::vector gemInfos; @@ -408,8 +414,6 @@ namespace AssetProcessor } outputPath += specialization.GetSpecialization(0); // Append configuration - outputPath += '.'; - outputPath += platform; outputPath += ".setreg"; AZ::IO::SystemFile file; diff --git a/cmake/Projects.cmake b/cmake/Projects.cmake index c09fe0fc6f..34ef3efd9b 100644 --- a/cmake/Projects.cmake +++ b/cmake/Projects.cmake @@ -150,22 +150,43 @@ foreach(project ${LY_PROJECTS}) # Get project name o3de_read_json_key(project_name ${full_directory_path}/project.json "project_name") + # The cmake tar command has a bit of a flaw + # Any paths within the archive files it creates are relative to the current working directory. + # That means with the setup of: + # cwd = "/Cache/pc" + # project product assets = "/Cache/pc/*" + # cmake dependency registry files = "/build/bin/Release/Registry/*" + # Running the tar command would result in the assets being placed in the to layout + # correctly, but the registry files + # engine.pak/ + # ../...build/bin/Release/Registry/cmake_dependencies.*.setreg -> Not correct + # project.json -> Correct + # Generate pak for project in release installs cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE install_base_runtime_output_directory) set(install_engine_pak_template [=[ if("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$") set(install_output_folder "${CMAKE_INSTALL_PREFIX}/@install_base_runtime_output_directory@/@PAL_PLATFORM_NAME@/${CMAKE_INSTALL_CONFIG_NAME}/@LY_BUILD_PERMUTATION@") set(install_pak_output_folder "${install_output_folder}/Cache/@LY_ASSET_DEPLOY_ASSET_TYPE@") + set(runtime_output_directory_RELEASE @CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE@) if(NOT DEFINED LY_ASSET_DEPLOY_ASSET_TYPE) set(LY_ASSET_DEPLOY_ASSET_TYPE @LY_ASSET_DEPLOY_ASSET_TYPE@) endif() message(STATUS "Generating ${install_pak_output_folder}/engine.pak from @full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") file(MAKE_DIRECTORY "${install_pak_output_folder}") cmake_path(SET cache_product_path "@full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") + # Copy the generated cmake_dependencies.*.setreg files for loading gems in non-monolithic to the cache + file(GLOB gem_source_paths_setreg "${runtime_output_directory_RELEASE}/Registry/*.setreg") + # The MergeSettingsToRegistry_TargetBuildDependencyRegistry function looks for lowercase "registry" + # So make sure the to copy it to a lowercase path, so that it works on non-case sensitive filesystems + file(MAKE_DIRECTORY "${cache_product_path}/registry") + file(COPY ${gem_source_paths_setreg} DESTINATION "${cache_product_path}/registry") + file(GLOB product_assets "${cache_product_path}/*") - if(product_assets) + list(APPEND pak_artifacts ${product_assets}) + if(pak_artifacts) execute_process( - COMMAND ${CMAKE_COMMAND} -E tar "cf" "${install_pak_output_folder}/engine.pak" --format=zip -- ${product_assets} + COMMAND ${CMAKE_COMMAND} -E tar "cf" "${install_pak_output_folder}/engine.pak" --format=zip -- ${pak_artifacts} WORKING_DIRECTORY "${cache_product_path}" RESULT_VARIABLE archive_creation_result ) From b71e307de57724260e014c100a71ee437ca9d0aa Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Thu, 28 Oct 2021 00:15:11 -0500 Subject: [PATCH 086/120] Fix issue setting enum values on material component from script Replaced get and set functions with explicit types with templates Added special case handling for setting enum values as strings or numbers from script Signed-off-by: Guthrie Adams --- .../Source/Material/MaterialAssignment.cpp | 53 +++-- .../Material/MaterialComponentBus.h | 59 ++---- .../Material/MaterialComponentController.cpp | 191 ++---------------- .../Material/MaterialComponentController.h | 25 --- 4 files changed, 77 insertions(+), 251 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp index c79ceb39ba..e81e46a749 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp @@ -139,32 +139,59 @@ namespace AZ { for (const auto& propertyPair : m_propertyOverrides) { - if (!propertyPair.second.empty()) + auto value = propertyPair.second; + if (!value.empty()) { bool wasRenamed = false; Name newName; - RPI::MaterialPropertyIndex materialPropertyIndex = m_materialInstance->FindPropertyIndex(propertyPair.first, &wasRenamed, &newName); + RPI::MaterialPropertyIndex materialPropertyIndex = + m_materialInstance->FindPropertyIndex(propertyPair.first, &wasRenamed, &newName); - // FindPropertyIndex will have already reported a message about what the old and new names are. Here we just add some extra info to help the user resolve it. - AZ_Warning("MaterialAssignment", !wasRenamed, + // FindPropertyIndex will have already reported a message about what the old and new names are. Here we just add + // some extra info to help the user resolve it. + AZ_Warning( + "MaterialAssignment", !wasRenamed, "Consider running \"Apply Automatic Property Updates\" to use the latest property names.", - propertyPair.first.GetCStr(), - newName.GetCStr()); + propertyPair.first.GetCStr(), newName.GetCStr()); if (wasRenamed && m_propertyOverrides.find(newName) != m_propertyOverrides.end()) { materialPropertyIndex.Reset(); - - AZ_Warning("MaterialAssignment", false, - "Material property '%s' has been renamed to '%s', and a property override exists for both. The one with the old name will be ignored.", - propertyPair.first.GetCStr(), - newName.GetCStr()); + + AZ_Warning( + "MaterialAssignment", false, + "Material property '%s' has been renamed to '%s', and a property override exists for both. The one with " + "the old name will be ignored.", + propertyPair.first.GetCStr(), newName.GetCStr()); } if (!materialPropertyIndex.IsNull()) { - m_materialInstance->SetPropertyValue( - materialPropertyIndex, AZ::RPI::MaterialPropertyValue::FromAny(propertyPair.second)); + const auto propertyDescriptor = + m_materialInstance->GetMaterialPropertiesLayout()->GetPropertyDescriptor(materialPropertyIndex); + + // Special case handling for enum values that need to be converted from numbers or strings + if (propertyDescriptor->GetDataType() == AZ::RPI::MaterialPropertyDataType::Enum) + { + if (value.is()) + { + value = propertyDescriptor->GetEnumValue(AZStd::any_cast(value)); + } + else if (value.is()) + { + value = propertyDescriptor->GetEnumValue(AZ::Name(AZStd::any_cast(value))); + } + else if (value.is()) + { + value = aznumeric_cast(AZStd::any_cast(value)); + } + else if (value.is()) + { + value = aznumeric_cast(AZStd::any_cast(value)); + } + } + + m_materialInstance->SetPropertyValue(materialPropertyIndex, AZ::RPI::MaterialPropertyValue::FromAny(value)); } } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h index 293080367d..d8d0c2dbc3 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h @@ -60,52 +60,8 @@ namespace AZ virtual void ClearMaterialOverride(const MaterialAssignmentId& materialAssignmentId) = 0; //! Set a material property override value wrapped by an AZStd::any virtual void SetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::any& value) = 0; - //! Set a material property override value to a bool - virtual void SetPropertyOverrideBool(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const bool& value) = 0; - //! Set a material property override value to a integer - virtual void SetPropertyOverrideInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const int32_t& value) = 0; - //! Set a material property override value to a unsigned integer - virtual void SetPropertyOverrideUInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const uint32_t& value) = 0; - //! Set a material property override value to a float - virtual void SetPropertyOverrideFloat(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const float& value) = 0; - //! Set a material property override value to a Vector2 - virtual void SetPropertyOverrideVector2(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector2& value) = 0; - //! Set a material property override value to a Vector3 - virtual void SetPropertyOverrideVector3(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector3& value) = 0; - //! Set a material property override value to a Vector4 - virtual void SetPropertyOverrideVector4(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector4& value) = 0; - //! Set a material property override value to a color - virtual void SetPropertyOverrideColor(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Color& value) = 0; - //! Set a material property override value to an image asset - virtual void SetPropertyOverrideImageAsset(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Data::Asset& value) = 0; - //! Set a material property override value to an image instance - virtual void SetPropertyOverrideImageInstance(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Data::Instance& value) = 0; - //! Set a material property override value to a string - virtual void SetPropertyOverrideString(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::string& value) = 0; //! Get a material property override value wrapped by an AZStd::any virtual AZStd::any GetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0; - //! Get a material property override value as a bool - virtual bool GetPropertyOverrideBool(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0; - //! Get a material property override value as an integer - virtual int32_t GetPropertyOverrideInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0; - //! Get a material property override value as an unsigned integer - virtual uint32_t GetPropertyOverrideUInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0; - //! Get a material property override value as a float - virtual float GetPropertyOverrideFloat(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0; - //! Get a material property override value as a Vector2 - virtual AZ::Vector2 GetPropertyOverrideVector2(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0; - //! Get a material property override value as a Vector3 - virtual AZ::Vector3 GetPropertyOverrideVector3(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0; - //! Get a material property override value as a Vector4 - virtual AZ::Vector4 GetPropertyOverrideVector4(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0; - //! Get a material property override value as a Color - virtual AZ::Color GetPropertyOverrideColor(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0; - //! Get a material property override value as an image asset - virtual AZ::Data::Asset GetPropertyOverrideImageAsset(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0; - //! Get a material property override value as an image instance - virtual AZ::Data::Instance GetPropertyOverrideImageInstance(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0; - //! Get a material property override value as a string - virtual AZStd::string GetPropertyOverrideString(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0; //! Clear property override for a specific material assignment virtual void ClearPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) = 0; //! Clear property overrides for a specific material assignment @@ -122,6 +78,21 @@ namespace AZ const MaterialAssignmentId& materialAssignmentId, const AZ::RPI::MaterialModelUvOverrideMap& modelUvOverrides) = 0; //! Get Model UV overrides for a specific material assignment virtual AZ::RPI::MaterialModelUvOverrideMap GetModelUvOverrides(const MaterialAssignmentId& materialAssignmentId) const = 0; + + //! Set material property override value with a specific type + template + void SetPropertyOverrideT(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const T& value) + { + SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value)); + } + + //! Get material property override value with a specific type + template + T GetPropertyOverrideT(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const + { + const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName); + return !value.empty() && value.is() ? AZStd::any_cast(value) : T{}; + } }; using MaterialComponentRequestBus = EBus; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp index 07082a87d5..2e13b13b09 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp @@ -54,29 +54,29 @@ namespace AZ ->Event("GetMaterialOverride", &MaterialComponentRequestBus::Events::GetMaterialOverride) ->Event("ClearMaterialOverride", &MaterialComponentRequestBus::Events::ClearMaterialOverride) ->Event("SetPropertyOverride", &MaterialComponentRequestBus::Events::SetPropertyOverride) - ->Event("SetPropertyOverrideBool", &MaterialComponentRequestBus::Events::SetPropertyOverrideBool) - ->Event("SetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideInt32) - ->Event("SetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideUInt32) - ->Event("SetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::SetPropertyOverrideFloat) - ->Event("SetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::SetPropertyOverrideVector2) - ->Event("SetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::SetPropertyOverrideVector3) - ->Event("SetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::SetPropertyOverrideVector4) - ->Event("SetPropertyOverrideColor", &MaterialComponentRequestBus::Events::SetPropertyOverrideColor) - ->Event("SetPropertyOverrideImageAsset", &MaterialComponentRequestBus::Events::SetPropertyOverrideImageAsset) - ->Event("SetPropertyOverrideImageInstance", &MaterialComponentRequestBus::Events::SetPropertyOverrideImageInstance) - ->Event("SetPropertyOverrideString", &MaterialComponentRequestBus::Events::SetPropertyOverrideString) + ->Event("SetPropertyOverrideBool", &MaterialComponentRequestBus::Events::SetPropertyOverrideT) + ->Event("SetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideT) + ->Event("SetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideT) + ->Event("SetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::SetPropertyOverrideT) + ->Event("SetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::SetPropertyOverrideT) + ->Event("SetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::SetPropertyOverrideT) + ->Event("SetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::SetPropertyOverrideT) + ->Event("SetPropertyOverrideColor", &MaterialComponentRequestBus::Events::SetPropertyOverrideT) + ->Event("SetPropertyOverrideImage", &MaterialComponentRequestBus::Events::SetPropertyOverrideT) + ->Event("SetPropertyOverrideString", &MaterialComponentRequestBus::Events::SetPropertyOverrideT) + ->Event("SetPropertyOverrideEnum", &MaterialComponentRequestBus::Events::SetPropertyOverrideT) ->Event("GetPropertyOverride", &MaterialComponentRequestBus::Events::GetPropertyOverride) - ->Event("GetPropertyOverrideBool", &MaterialComponentRequestBus::Events::GetPropertyOverrideBool) - ->Event("GetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideInt32) - ->Event("GetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideUInt32) - ->Event("GetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::GetPropertyOverrideFloat) - ->Event("GetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::GetPropertyOverrideVector2) - ->Event("GetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::GetPropertyOverrideVector3) - ->Event("GetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::GetPropertyOverrideVector4) - ->Event("GetPropertyOverrideColor", &MaterialComponentRequestBus::Events::GetPropertyOverrideColor) - ->Event("GetPropertyOverrideImageAsset", &MaterialComponentRequestBus::Events::GetPropertyOverrideImageAsset) - ->Event("GetPropertyOverrideImageInstance", &MaterialComponentRequestBus::Events::GetPropertyOverrideImageInstance) - ->Event("GetPropertyOverrideString", &MaterialComponentRequestBus::Events::GetPropertyOverrideString) + ->Event("GetPropertyOverrideBool", &MaterialComponentRequestBus::Events::GetPropertyOverrideT) + ->Event("GetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideT) + ->Event("GetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideT) + ->Event("GetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::GetPropertyOverrideT) + ->Event("GetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::GetPropertyOverrideT) + ->Event("GetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::GetPropertyOverrideT) + ->Event("GetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::GetPropertyOverrideT) + ->Event("GetPropertyOverrideColor", &MaterialComponentRequestBus::Events::GetPropertyOverrideT) + ->Event("GetPropertyOverrideImage", &MaterialComponentRequestBus::Events::GetPropertyOverrideT) + ->Event("GetPropertyOverrideString", &MaterialComponentRequestBus::Events::GetPropertyOverrideT) + ->Event("GetPropertyOverrideEnum", &MaterialComponentRequestBus::Events::GetPropertyOverrideT) ->Event("ClearPropertyOverride", &MaterialComponentRequestBus::Events::ClearPropertyOverride) ->Event("ClearPropertyOverrides", &MaterialComponentRequestBus::Events::ClearPropertyOverrides) ->Event("ClearAllPropertyOverrides", &MaterialComponentRequestBus::Events::ClearAllPropertyOverrides) @@ -499,76 +499,6 @@ namespace AZ QueuePropertyChanges(materialAssignmentId); } - void MaterialComponentController::SetPropertyOverrideBool( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const bool& value) - { - SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value)); - } - - void MaterialComponentController::SetPropertyOverrideInt32( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const int32_t& value) - { - SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value)); - } - - void MaterialComponentController::SetPropertyOverrideUInt32( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const uint32_t& value) - { - SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value)); - } - - void MaterialComponentController::SetPropertyOverrideFloat( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const float& value) - { - SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value)); - } - - void MaterialComponentController::SetPropertyOverrideVector2( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector2& value) - { - SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value)); - } - - void MaterialComponentController::SetPropertyOverrideVector3( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector3& value) - { - SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value)); - } - - void MaterialComponentController::SetPropertyOverrideVector4( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector4& value) - { - SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value)); - } - - void MaterialComponentController::SetPropertyOverrideColor( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Color& value) - { - SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value)); - } - - void MaterialComponentController::SetPropertyOverrideImageAsset( - const MaterialAssignmentId& materialAssignmentId, - const AZStd::string& propertyName, - const AZ::Data::Asset& value) - { - SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value)); - } - - void MaterialComponentController::SetPropertyOverrideImageInstance( - const MaterialAssignmentId& materialAssignmentId, - const AZStd::string& propertyName, - const AZ::Data::Instance& value) - { - SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value)); - } - - void MaterialComponentController::SetPropertyOverrideString( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::string& value) - { - SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value)); - } - AZStd::any MaterialComponentController::GetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const { const auto materialIt = m_configuration.m_materials.find(materialAssignmentId); @@ -586,83 +516,6 @@ namespace AZ return propertyIt->second; } - bool MaterialComponentController::GetPropertyOverrideBool( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const - { - const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName); - return !value.empty() && value.is() ? AZStd::any_cast(value) : false; - } - - int32_t MaterialComponentController::GetPropertyOverrideInt32( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const - { - const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName); - return !value.empty() && value.is() ? AZStd::any_cast(value) : 0; - } - - uint32_t MaterialComponentController::GetPropertyOverrideUInt32( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const - { - const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName); - return !value.empty() && value.is() ? AZStd::any_cast(value) : 0; - } - - float MaterialComponentController::GetPropertyOverrideFloat( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const - { - const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName); - return !value.empty() && value.is() ? AZStd::any_cast(value) : 0.0f; - } - - AZ::Vector2 MaterialComponentController::GetPropertyOverrideVector2( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const - { - const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName); - return !value.empty() && value.is() ? AZStd::any_cast(value) : AZ::Vector2::CreateZero(); - } - - AZ::Vector3 MaterialComponentController::GetPropertyOverrideVector3( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const - { - const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName); - return !value.empty() && value.is() ? AZStd::any_cast(value) : AZ::Vector3::CreateZero(); - } - - AZ::Vector4 MaterialComponentController::GetPropertyOverrideVector4( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const - { - const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName); - return !value.empty() && value.is() ? AZStd::any_cast(value) : AZ::Vector4::CreateZero(); - } - - AZ::Color MaterialComponentController::GetPropertyOverrideColor( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const - { - const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName); - return !value.empty() && value.is() ? AZStd::any_cast(value) : AZ::Color::CreateZero(); - } - - AZ::Data::Asset MaterialComponentController::GetPropertyOverrideImageAsset( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const - { - const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName); - return !value.empty() && value.is>() ? AZStd::any_cast>(value) : AZ::Data::Asset(); - } - - AZ::Data::Instance MaterialComponentController::GetPropertyOverrideImageInstance( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const - { - const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName); - return !value.empty() && value.is>() ? AZStd::any_cast>(value) : AZ::Data::Instance(); - } - - AZStd::string MaterialComponentController::GetPropertyOverrideString( - const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const - { - const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName); - return !value.empty() && value.is() ? AZStd::any_cast(value) : AZStd::string(); - } - void MaterialComponentController::ClearPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) { auto materialIt = m_configuration.m_materials.find(materialAssignmentId); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h index ec542c377c..74b1cfda4d 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h @@ -65,33 +65,8 @@ namespace AZ void SetMaterialOverride(const MaterialAssignmentId& materialAssignmentId, const AZ::Data::AssetId& materialAssetId) override; AZ::Data::AssetId GetMaterialOverride(const MaterialAssignmentId& materialAssignmentId) const override; void ClearMaterialOverride(const MaterialAssignmentId& materialAssignmentId) override; - void SetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::any& value) override; - void SetPropertyOverrideBool(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const bool& value) override; - void SetPropertyOverrideInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const int32_t& value) override; - void SetPropertyOverrideUInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const uint32_t& value) override; - void SetPropertyOverrideFloat(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const float& value) override; - void SetPropertyOverrideVector2(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector2& value) override; - void SetPropertyOverrideVector3(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector3& value) override; - void SetPropertyOverrideVector4(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector4& value) override; - void SetPropertyOverrideColor(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Color& value) override; - void SetPropertyOverrideImageAsset(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Data::Asset& value) override; - void SetPropertyOverrideImageInstance(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Data::Instance& value) override; - void SetPropertyOverrideString(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::string& value) override; - AZStd::any GetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override; - bool GetPropertyOverrideBool(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override; - int32_t GetPropertyOverrideInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override; - uint32_t GetPropertyOverrideUInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override; - float GetPropertyOverrideFloat(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override; - AZ::Vector2 GetPropertyOverrideVector2(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override; - AZ::Vector3 GetPropertyOverrideVector3(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override; - AZ::Vector4 GetPropertyOverrideVector4(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override; - AZ::Color GetPropertyOverrideColor(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override; - AZ::Data::Asset GetPropertyOverrideImageAsset(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override; - AZ::Data::Instance GetPropertyOverrideImageInstance(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override; - AZStd::string GetPropertyOverrideString(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override; - void ClearPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) override; void ClearPropertyOverrides(const MaterialAssignmentId& materialAssignmentId) override; void ClearAllPropertyOverrides() override; From 1f4967b1682f538e4d5bfe503cab53afa816fbcc Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Thu, 28 Oct 2021 11:30:58 -0500 Subject: [PATCH 087/120] extending conversions from script to other numeric types Signed-off-by: Guthrie Adams --- .../Feature/Material/MaterialAssignment.h | 5 ++ .../Source/Material/MaterialAssignment.cpp | 80 +++++++++++++------ 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h index 40555bae00..2a094dc0c9 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h @@ -78,5 +78,10 @@ namespace AZ //! Find an assignment id corresponding to the lod and label substring filters MaterialAssignmentId FindMaterialAssignmentIdInModel( const Data::Instance& model, const MaterialAssignmentLodIndex lodFilter, const AZStd::string& labelFilter); + + // Special case handling to convert script values to suported types + AZ::RPI::MaterialPropertyValue ConvertMaterialPropertyValueFromScript( + const AZ::RPI::MaterialPropertyDescriptor* propertyDescriptor, const AZStd::any& value); + } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp index e81e46a749..d99ce211bf 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp @@ -139,8 +139,7 @@ namespace AZ { for (const auto& propertyPair : m_propertyOverrides) { - auto value = propertyPair.second; - if (!value.empty()) + if (!propertyPair.second.empty()) { bool wasRenamed = false; Name newName; @@ -170,28 +169,8 @@ namespace AZ const auto propertyDescriptor = m_materialInstance->GetMaterialPropertiesLayout()->GetPropertyDescriptor(materialPropertyIndex); - // Special case handling for enum values that need to be converted from numbers or strings - if (propertyDescriptor->GetDataType() == AZ::RPI::MaterialPropertyDataType::Enum) - { - if (value.is()) - { - value = propertyDescriptor->GetEnumValue(AZStd::any_cast(value)); - } - else if (value.is()) - { - value = propertyDescriptor->GetEnumValue(AZ::Name(AZStd::any_cast(value))); - } - else if (value.is()) - { - value = aznumeric_cast(AZStd::any_cast(value)); - } - else if (value.is()) - { - value = aznumeric_cast(AZStd::any_cast(value)); - } - } - - m_materialInstance->SetPropertyValue(materialPropertyIndex, AZ::RPI::MaterialPropertyValue::FromAny(value)); + m_materialInstance->SetPropertyValue( + materialPropertyIndex, ConvertMaterialPropertyValueFromScript(propertyDescriptor, propertyPair.second)); } } } @@ -311,5 +290,58 @@ namespace AZ return MaterialAssignmentId(); } + + template + AZ::RPI::MaterialPropertyValue ConvertMaterialPropertyValueNumericType(const AZStd::any& value) + { + if (value.is()) + { + return aznumeric_cast(AZStd::any_cast(value)); + } + if (value.is()) + { + return aznumeric_cast(AZStd::any_cast(value)); + } + if (value.is()) + { + return aznumeric_cast(AZStd::any_cast(value)); + } + if (value.is()) + { + return aznumeric_cast(AZStd::any_cast(value)); + } + + return AZ::RPI::MaterialPropertyValue::FromAny(value); + } + + AZ::RPI::MaterialPropertyValue ConvertMaterialPropertyValueFromScript( + const AZ::RPI::MaterialPropertyDescriptor* propertyDescriptor, const AZStd::any& value) + { + switch (propertyDescriptor->GetDataType()) + { + case AZ::RPI::MaterialPropertyDataType::Enum: + if (value.is()) + { + return propertyDescriptor->GetEnumValue(AZStd::any_cast(value)); + } + if (value.is()) + { + return propertyDescriptor->GetEnumValue(AZ::Name(AZStd::any_cast(value))); + } + return ConvertMaterialPropertyValueNumericType(value); + case AZ::RPI::MaterialPropertyDataType::Int: + return ConvertMaterialPropertyValueNumericType(value); + case AZ::RPI::MaterialPropertyDataType::UInt: + return ConvertMaterialPropertyValueNumericType(value); + case AZ::RPI::MaterialPropertyDataType::Float: + return ConvertMaterialPropertyValueNumericType(value); + case AZ::RPI::MaterialPropertyDataType::Bool: + return ConvertMaterialPropertyValueNumericType(value); + default: + break; + } + + return AZ::RPI::MaterialPropertyValue::FromAny(value); + } } // namespace Render } // namespace AZ From 5de24437abf441318ec7ca55e51db9ca4129e861 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Thu, 28 Oct 2021 13:51:48 -0500 Subject: [PATCH 088/120] fixed comment Signed-off-by: Guthrie Adams --- .../Code/Include/Atom/Feature/Material/MaterialAssignment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h index 2a094dc0c9..21d9ec1bba 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h @@ -79,7 +79,7 @@ namespace AZ MaterialAssignmentId FindMaterialAssignmentIdInModel( const Data::Instance& model, const MaterialAssignmentLodIndex lodFilter, const AZStd::string& labelFilter); - // Special case handling to convert script values to suported types + //! Special case handling to convert script values to supported types AZ::RPI::MaterialPropertyValue ConvertMaterialPropertyValueFromScript( const AZ::RPI::MaterialPropertyDescriptor* propertyDescriptor, const AZStd::any& value); From e5729fbefe03287f6a968427b90e400d8a9e8697 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 27 Oct 2021 17:32:43 -0500 Subject: [PATCH 089/120] =?UTF-8?q?Fix=20clearing=20material=20component?= =?UTF-8?q?=20default=20material=20not=20clearing=20materials=20or=20updat?= =?UTF-8?q?ing=20preview=20=E2=80=A2=20Changed=20thumbnail=20property=20co?= =?UTF-8?q?ntrol=20to=20track=20asset=20key=20even=20if=20image=20is=20ove?= =?UTF-8?q?rridden=20so=20that=20it=20will=20be=20restored=20if=20the=20im?= =?UTF-8?q?age=20is=20cleared.=20=E2=80=A2=20Changed=20property=20asset=20?= =?UTF-8?q?control=20to=20disable=20the=20thumbnail=20image=20by=20default?= =?UTF-8?q?=20whenever=20the=20attribute=20is=20applied.=20It=20will=20onl?= =?UTF-8?q?y=20enable=20the=20thumbnail=20image=20if=20the=20pixmap=20is?= =?UTF-8?q?=20valid.=20=E2=80=A2=20Changed=20the=20material=20component=20?= =?UTF-8?q?controller=20to=20always=20use=20an=20empty=20material=20assign?= =?UTF-8?q?ment=20map=20on=20deactivation=20so=20that=20no=20persistent=20?= =?UTF-8?q?materials=20are=20reapplied.=20=E2=80=A2=20Changed=20the=20mate?= =?UTF-8?q?rial=20component=20controller=20to=20immediately=20send=20a=20n?= =?UTF-8?q?otification=20that=20materials=20have=20updated=20if=20no=20mat?= =?UTF-8?q?erials=20were=20queued=20for=20load=20but=20the=20configuration?= =?UTF-8?q?=20contained=20pre=20created=20or=20persistent=20material=20ins?= =?UTF-8?q?tances.=20This=20mainly=20affects=20the=20material=20editor=20b?= =?UTF-8?q?ecause=20it=20manages=20its=20own=20material=20instances.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Guthrie Adams --- .../UI/PropertyEditor/PropertyAssetCtrl.cpp | 14 +++++++++++--- .../UI/PropertyEditor/ThumbnailPropertyCtrl.cpp | 13 +++---------- .../Material/MaterialComponentController.cpp | 12 ++++++++++-- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp index 7f3b9e61fd..24b2c7466e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp @@ -1387,7 +1387,10 @@ namespace AzToolsFramework QDataStream stream(&pixmapBytes, QIODevice::ReadOnly); QPixmap pixmap; stream >> pixmap; - GUI->SetBrowseButtonIcon(pixmap); + if (!pixmap.isNull()) + { + GUI->SetBrowseButtonIcon(pixmap); + } } } } @@ -1417,6 +1420,8 @@ namespace AzToolsFramework } else if (attrib == AZ_CRC_CE("ThumbnailIcon")) { + GUI->SetCustomThumbnailEnabled(false); + AZStd::string iconPath; if (attrValue->Read(iconPath) && !iconPath.empty()) { @@ -1434,8 +1439,11 @@ namespace AzToolsFramework QDataStream stream(&pixmapBytes, QIODevice::ReadOnly); QPixmap pixmap; stream >> pixmap; - GUI->SetCustomThumbnailEnabled(true); - GUI->SetCustomThumbnailPixmap(pixmap); + if (!pixmap.isNull()) + { + GUI->SetCustomThumbnailEnabled(true); + GUI->SetCustomThumbnailPixmap(pixmap); + } } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp index d8ddee6b76..0bbb898196 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp @@ -67,16 +67,9 @@ namespace AzToolsFramework void ThumbnailPropertyCtrl::SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName) { - if (m_customThumbnailEnabled) - { - ClearThumbnail(); - } - else - { - m_key = key; - m_thumbnail->SetThumbnailKey(m_key, contextName); - m_thumbnailEnlarged->SetThumbnailKey(m_key, contextName); - } + m_key = key; + m_thumbnail->SetThumbnailKey(m_key, contextName); + m_thumbnailEnlarged->SetThumbnailKey(m_key, contextName); UpdateVisibility(); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp index 07082a87d5..ed6904b4ef 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp @@ -121,8 +121,13 @@ namespace AZ MaterialComponentRequestBus::Handler::BusDisconnect(); MaterialReceiverNotificationBus::Handler::BusDisconnect(); TickBus::Handler::BusDisconnect(); + ReleaseMaterials(); + // Sending notification to wipe any previously assigned material overrides + MaterialComponentNotificationBus::Event( + m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, MaterialAssignmentMap()); + m_queuedMaterialUpdateNotification = false; m_entityId = AZ::EntityId(AZ::EntityId::InvalidEntityId); } @@ -221,6 +226,11 @@ namespace AZ if (!anyQueued) { ReleaseMaterials(); + + // If no other materials were loaded, the notification must still be sent in case there are externally managed material + // instances in the configuration + MaterialComponentNotificationBus::Event( + m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, m_configuration.m_materials); } } @@ -268,8 +278,6 @@ namespace AZ { materialPair.second.Release(); } - - MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, m_configuration.m_materials); } MaterialAssignmentMap MaterialComponentController::GetOriginalMaterialAssignments() const From b6d634c3ed8a431bf5a7be45e9b13a1a6e84f446 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu, 28 Oct 2021 14:37:51 -0500 Subject: [PATCH 090/120] Print all captured messages in Assert Absorber used by FingerprintTest to help debug crash (#5096) Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> --- .../Tools/AssetProcessor/native/unittests/UnitTestRunner.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h b/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h index c281b3050a..0c4357f84a 100644 --- a/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h +++ b/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h @@ -21,6 +21,7 @@ #endif #include +#include //! These macros can be used for checking your unit tests, //! you can check AssetScannerUnitTest.cpp for usage @@ -155,6 +156,7 @@ namespace UnitTestUtils bool OnPreWarning([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override { + UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message); ++m_numWarningsAbsorbed; if (m_debugMessages) { @@ -165,6 +167,7 @@ namespace UnitTestUtils bool OnPreAssert([[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override { + UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message); ++m_numAssertsAbsorbed; if (m_debugMessages) { @@ -175,6 +178,7 @@ namespace UnitTestUtils bool OnPreError([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override { + UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message); ++m_numErrorsAbsorbed; if (m_debugMessages) { @@ -183,8 +187,9 @@ namespace UnitTestUtils return true; // I handled this, do not forward it } - bool OnPrintf(const char* /*window*/, const char* /*message*/) override + bool OnPrintf(const char* /*window*/, const char* message) override { + UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message); ++m_numMessagesAbsorbed; return true; } From f350ba3042b216369748935800628118b835ca81 Mon Sep 17 00:00:00 2001 From: Tommy Walton <82672795+amzn-tommy@users.noreply.github.com> Date: Thu, 28 Oct 2021 12:49:33 -0700 Subject: [PATCH 091/120] Modify the AssetBundler to correctly identify the Gems that are enabled in the current active project (#5072) * Modify the AssetBundler to correctly identify the Gems that are enabled in the current active project Signed-off-by: Tommy Walton * Removed unnecessary if() statement and updated the comment. Signed-off-by: Tommy Walton * Disabling gem loading in the asset bundler tests, just like the asset bundler itself. Signed-off-by: Tommy Walton --- Code/Tools/AssetBundler/CMakeLists.txt | 8 ++++++++ .../AssetBundler/source/utils/applicationManager.cpp | 7 ++++++- Code/Tools/AssetBundler/tests/applicationManagerTests.cpp | 6 +++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Code/Tools/AssetBundler/CMakeLists.txt b/Code/Tools/AssetBundler/CMakeLists.txt index d613b35ce5..dcc62595c9 100644 --- a/Code/Tools/AssetBundler/CMakeLists.txt +++ b/Code/Tools/AssetBundler/CMakeLists.txt @@ -47,6 +47,10 @@ ly_add_target( AZ::AssetBundlerBatch.Static ) +# Adds a specialized .setreg to identify gems enabled in the active project. +# This associates the AssetBundlerBatch target with the .Builders gem variants. +ly_set_gem_variant_to_load(TARGETS AssetBundlerBatch VARIANTS Builders) + # AssetBundler - Qt GUI Application ly_add_target( NAME AssetBundler ${PAL_TRAIT_BUILD_ASSETBUNDLER_APPLICATION_TYPE} @@ -73,6 +77,10 @@ ly_add_target( ${additional_dependencies} ) +# Adds a specialized .setreg to identify gems enabled in the active project. +# This associates the AssetBundler target with the .Builders gem variants. +ly_set_gem_variant_to_load(TARGETS AssetBundler VARIANTS Builders) + if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_add_target( diff --git a/Code/Tools/AssetBundler/source/utils/applicationManager.cpp b/Code/Tools/AssetBundler/source/utils/applicationManager.cpp index 0742035c1a..0388570fdd 100644 --- a/Code/Tools/AssetBundler/source/utils/applicationManager.cpp +++ b/Code/Tools/AssetBundler/source/utils/applicationManager.cpp @@ -54,7 +54,12 @@ namespace AssetBundler bool ApplicationManager::Init() { AZ::Debug::TraceMessageBus::Handler::BusConnect(); - Start(AzFramework::Application::Descriptor()); + + ComponentApplication::StartupParameters startupParameters; + // The AssetBundler does not need to load gems + startupParameters.m_loadDynamicModules = false; + Start(AzFramework::Application::Descriptor(), startupParameters); + AZ::SerializeContext* context; EBUS_EVENT_RESULT(context, AZ::ComponentApplicationBus, GetSerializeContext); AZ_Assert(context, "No serialize context"); diff --git a/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp b/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp index 35d3a5da1f..0d915dcc49 100644 --- a/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp +++ b/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp @@ -71,7 +71,11 @@ namespace AssetBundler m_data->m_applicationManager.reset(aznew MockApplicationManagerTest(0, 0)); - m_data->m_applicationManager->Start(AzFramework::Application::Descriptor()); + + AZ::ComponentApplication::StartupParameters startupParameters; + // The AssetBundler does not need to load gems + startupParameters.m_loadDynamicModules = false; + m_data->m_applicationManager->Start(AzFramework::Application::Descriptor(), startupParameters); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash From 00a49fa251121eeb13d28a5ff3cc466392ed3ad9 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Thu, 28 Oct 2021 12:53:54 -0700 Subject: [PATCH 092/120] Use source model data instead of filtered (#5071) Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com> --- .../Source/GemCatalog/GemCatalogScreen.cpp | 12 +++++++++--- .../Source/GemCatalog/GemCatalogScreen.h | 2 +- .../ProjectManager/Source/GemCatalog/GemModel.cpp | 14 +++++++++----- .../ProjectManager/Source/GemCatalog/GemModel.h | 4 ++-- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index a22f41d054..35ea67bdff 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -145,10 +145,11 @@ namespace O3DE::ProjectManager } } - void GemCatalogScreen::OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies) + void GemCatalogScreen::OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies) { if (m_notificationsEnabled) { + QModelIndex modelIndex = m_gemModel->FindIndexByNameString(gemName); bool added = GemModel::IsAdded(modelIndex); bool dependency = GemModel::IsAddedDependency(modelIndex); @@ -233,7 +234,11 @@ namespace O3DE::ProjectManager const QVector allRepoGemInfos = allRepoGemInfosResult.GetValue(); for (const GemInfo& gemInfo : allRepoGemInfos) { - m_gemModel->AddGem(gemInfo); + // do not add gems that have already been downloaded + if (!m_gemModel->FindIndexByNameString(gemInfo.m_name).isValid()) + { + m_gemModel->AddGem(gemInfo); + } } } else @@ -257,7 +262,8 @@ namespace O3DE::ProjectManager GemModel::SetWasPreviouslyAdded(*m_gemModel, modelIndex, true); GemModel::SetIsAdded(*m_gemModel, modelIndex, true); } - else + // ${Name} is a special name used in templates and is not really an error + else if (enabledGemName != "${Name}") { AZ_Warning("ProjectManager::GemCatalog", false, "Cannot find entry for gem with name '%s'. The CMake target name probably does not match the specified name in the gem.json.", diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 1ade87af0c..1b34019d1a 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -46,7 +46,7 @@ namespace O3DE::ProjectManager DownloadController* GetDownloadController() const { return m_downloadController; } public slots: - void OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies); + void OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies); void OnAddGemClicked(); protected: diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index acdef483ae..90eaaf0628 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -276,9 +276,11 @@ namespace O3DE::ProjectManager void GemModel::SetIsAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded) { + // get the gemName first, because the modelIndex data change after adding because of filters + QString gemName = modelIndex.data(RoleName).toString(); model.setData(modelIndex, isAdded, RoleIsAdded); - UpdateDependencies(model, modelIndex); + UpdateDependencies(model, gemName, isAdded); } bool GemModel::HasDependentGems(const QModelIndex& modelIndex) const @@ -294,15 +296,17 @@ namespace O3DE::ProjectManager return false; } - void GemModel::UpdateDependencies(QAbstractItemModel& model, const QModelIndex& modelIndex) + void GemModel::UpdateDependencies(QAbstractItemModel& model, const QString& gemName, bool isAdded) { GemModel* gemModel = GetSourceModel(&model); AZ_Assert(gemModel, "Failed to obtain GemModel"); + QModelIndex modelIndex = gemModel->FindIndexByNameString(gemName); + QVector dependencies = gemModel->GatherGemDependencies(modelIndex); uint32_t numChangedDependencies = 0; - if (IsAdded(modelIndex)) + if (isAdded) { for (const QModelIndex& dependency : dependencies) { @@ -324,7 +328,7 @@ namespace O3DE::ProjectManager bool hasDependentGems = gemModel->HasDependentGems(modelIndex); if (IsAddedDependency(modelIndex) != hasDependentGems) { - SetIsAddedDependency(model, modelIndex, hasDependentGems); + SetIsAddedDependency(*gemModel, modelIndex, hasDependentGems); } for (const QModelIndex& dependency : dependencies) @@ -343,7 +347,7 @@ namespace O3DE::ProjectManager } } - gemModel->emit gemStatusChanged(modelIndex, numChangedDependencies); + gemModel->emit gemStatusChanged(gemName, numChangedDependencies); } void GemModel::SetIsAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index 938543eb39..35231cc105 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -64,7 +64,7 @@ namespace O3DE::ProjectManager static bool NeedsToBeAdded(const QModelIndex& modelIndex, bool includeDependencies = false); static bool NeedsToBeRemoved(const QModelIndex& modelIndex, bool includeDependencies = false); static bool HasRequirement(const QModelIndex& modelIndex); - static void UpdateDependencies(QAbstractItemModel& model, const QModelIndex& modelIndex); + static void UpdateDependencies(QAbstractItemModel& model, const QString& gemName, bool isAdded); static void SetDownloadStatus(QAbstractItemModel& model, const QModelIndex& modelIndex, GemInfo::DownloadStatus status); bool DoGemsToBeAddedHaveRequirements() const; @@ -78,7 +78,7 @@ namespace O3DE::ProjectManager int TotalAddedGems(bool includeDependencies = false) const; signals: - void gemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies); + void gemStatusChanged(const QString& gemName, uint32_t numChangedDependencies); private: void FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames); From b8ced0d461f07eb34bba1f543bbea81e51d5d7b9 Mon Sep 17 00:00:00 2001 From: Shirang Jia Date: Thu, 28 Oct 2021 13:31:33 -0700 Subject: [PATCH 093/120] Fix AP log path on S3 (#5068) Signed-off-by: shiranj --- scripts/build/Jenkins/Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index f5e5edcf66..35f6f9a81c 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -449,7 +449,7 @@ def UploadAPLogs(Map options, String branchName, String jobName, String workspac } def command = "${pythonPath} -u ${s3UploadScriptPath} --base_dir ${apLogsPath} " + "--file_regex \".*\" --bucket ${env.AP_LOGS_S3_BUCKET} " + - "--search_subdirectories True --key_prefix ${env.JOB_NAME}/${branchName}/${env.BUILD_NUMBER}/${jobName}" + + "--search_subdirectories True --key_prefix ${env.JENKINS_JOB_NAME}/${branchName}/${env.BUILD_NUMBER}/${jobName} " + "--extra-args {\"ACL\": \"bucket-owner-full-control\"}" palSh(command, "Uploading AP logs for job ${jobName} for branch ${branchName}", false) } @@ -806,6 +806,7 @@ try { platform.value.build_types.each { build_job -> if (IsJobEnabled(branchName, build_job, pipelineName, platform.key)) { // User can filter jobs, jobs are tagged by pipeline def envVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, build_job.value.PIPELINE_ENV ?: EMPTY_JSON, pipelineName) + envVars['JENKINS_JOB_NAME'] = env.JOB_NAME // Save original Jenkins job name to JENKINS_JOB_NAME envVars['JOB_NAME'] = "${branchName}_${platform.key}_${build_job.key}" // backwards compatibility, some scripts rely on this someBuildHappened = true From 7536073df0b362c785aa4459f087f88636c28c60 Mon Sep 17 00:00:00 2001 From: Scott Murray Date: Thu, 28 Oct 2021 13:36:48 -0700 Subject: [PATCH 094/120] remove and unused import Signed-off-by: Scott Murray --- .../Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py index a81864f379..efef4c0a43 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXLayerAdded.py @@ -70,8 +70,6 @@ def AtomEditorComponents_postfx_layer_AddedToEntity(): :return: None """ - import os - import azlmbr.legacy.general as general from editor_python_test_tools.editor_entity_utils import EditorEntity From 86270339d8967de8c983cd4ab96982f347652d0f Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Thu, 28 Oct 2021 16:17:38 -0500 Subject: [PATCH 095/120] Added terrain surface data notifications (#5067) * Fix notifications for surface data changes. Separated the notifications from the surface component and the height component to add a reason to a RefreshArea request. This makes it possible to distinguish between surface changes and height changes and provide the appropriate OnTerrainDataChanged flags. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Reworked to use a changeMask instead of separate calls. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * PR Feedback Judicious use of "using" to reduce a bunch of bulky namespaces. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h | 3 +- .../TerrainHeightGradientListComponent.cpp | 8 +++-- .../TerrainLayerSpawnerComponent.cpp | 8 ++++- .../TerrainSurfaceGradientListComponent.cpp | 4 ++- .../Source/TerrainSystem/TerrainSystem.cpp | 36 ++++++++++++++----- .../Code/Source/TerrainSystem/TerrainSystem.h | 4 ++- .../Source/TerrainSystem/TerrainSystemBus.h | 2 +- Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp | 4 +-- .../Tests/TerrainHeightGradientListTests.cpp | 2 +- 9 files changed, 52 insertions(+), 19 deletions(-) diff --git a/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h b/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h index 53d93be8ea..f9607fdafb 100644 --- a/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h +++ b/Gems/Terrain/Code/Mocks/Terrain/MockTerrain.h @@ -34,7 +34,8 @@ namespace UnitTest MOCK_METHOD1(RegisterArea, void(AZ::EntityId areaId)); MOCK_METHOD1(UnregisterArea, void(AZ::EntityId areaId)); - MOCK_METHOD1(RefreshArea, void(AZ::EntityId areaId)); + MOCK_METHOD2(RefreshArea, + void(AZ::EntityId areaId, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask)); }; class MockTerrainDataNotificationListener : public AzFramework::Terrain::TerrainDataNotificationBus::Handler diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp index 01ad0bb77f..231d5abc28 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp @@ -119,7 +119,9 @@ namespace Terrain LmbrCentral::DependencyNotificationBus::Handler::BusDisconnect(); // Since this height data will no longer exist, notify the terrain system to refresh the area. - TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId()); + TerrainSystemServiceRequestBus::Broadcast( + &TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId(), + AzFramework::Terrain::TerrainDataNotifications::HeightData); } bool TerrainHeightGradientListComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) @@ -176,7 +178,9 @@ namespace Terrain void TerrainHeightGradientListComponent::OnCompositionChanged() { RefreshMinMaxHeights(); - TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId()); + TerrainSystemServiceRequestBus::Broadcast( + &TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId(), + AzFramework::Terrain::TerrainDataNotifications::HeightData); } void TerrainHeightGradientListComponent::RefreshMinMaxHeights() diff --git a/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.cpp index 00ed9c004f..c3803c25e8 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.cpp @@ -157,6 +157,12 @@ namespace Terrain void TerrainLayerSpawnerComponent::RefreshArea() { - TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId()); + using Terrain = AzFramework::Terrain::TerrainDataNotifications; + + // Notify the terrain system that the entire layer has changed, so both height and surface data can be affected. + TerrainSystemServiceRequestBus::Broadcast( + &TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId(), + static_cast(Terrain::HeightData | Terrain::SurfaceData) + ); } } diff --git a/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp index 5b76f15d74..748221d69e 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp @@ -184,7 +184,9 @@ namespace Terrain void TerrainSurfaceGradientListComponent::OnCompositionChanged() { - TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId()); + TerrainSystemServiceRequestBus::Broadcast( + &TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId(), + AzFramework::Terrain::TerrainDataNotifications::SurfaceData); } } // namespace Terrain diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp index fa1d483a5d..8d39340b06 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp @@ -76,6 +76,7 @@ void TerrainSystem::Activate() m_dirtyRegion = AZ::Aabb::CreateNull(); m_terrainHeightDirty = true; m_terrainSettingsDirty = true; + m_terrainSurfacesDirty = true; m_requestedSettings.m_systemActive = true; { @@ -115,6 +116,7 @@ void TerrainSystem::Deactivate() m_dirtyRegion = AZ::Aabb::CreateNull(); m_terrainHeightDirty = true; m_terrainSettingsDirty = true; + m_terrainSurfacesDirty = true; m_requestedSettings.m_systemActive = false; AzFramework::Terrain::TerrainDataNotificationBus::Broadcast( @@ -549,6 +551,7 @@ void TerrainSystem::RegisterArea(AZ::EntityId areaId) m_registeredAreas[areaId] = aabb; m_dirtyRegion.AddAabb(aabb); m_terrainHeightDirty = true; + m_terrainSurfacesDirty = true; } void TerrainSystem::UnregisterArea(AZ::EntityId areaId) @@ -567,14 +570,17 @@ void TerrainSystem::UnregisterArea(AZ::EntityId areaId) { m_dirtyRegion.AddAabb(aabb); m_terrainHeightDirty = true; + m_terrainSurfacesDirty = true; return true; } return false; }); } -void TerrainSystem::RefreshArea(AZ::EntityId areaId) +void TerrainSystem::RefreshArea(AZ::EntityId areaId, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask) { + using Terrain = AzFramework::Terrain::TerrainDataNotifications; + AZStd::unique_lock lock(m_areaMutex); auto areaAabb = m_registeredAreas.find(areaId); @@ -588,11 +594,18 @@ void TerrainSystem::RefreshArea(AZ::EntityId areaId) expandedAabb.AddAabb(newAabb); m_dirtyRegion.AddAabb(expandedAabb); - m_terrainHeightDirty = true; + + // Keep track of which types of data have changed so that we can send out the appropriate notifications later. + + m_terrainHeightDirty = m_terrainHeightDirty || ((changeMask & Terrain::HeightData) == Terrain::HeightData); + + m_terrainSurfacesDirty = m_terrainSurfacesDirty || ((changeMask & Terrain::SurfaceData) == Terrain::SurfaceData); } void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) { + using Terrain = AzFramework::Terrain::TerrainDataNotifications; + bool terrainSettingsChanged = false; if (m_terrainSettingsDirty) @@ -607,6 +620,7 @@ void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) m_dirtyRegion = m_currentSettings.m_worldBounds; m_dirtyRegion.AddAabb(m_requestedSettings.m_worldBounds); m_terrainHeightDirty = true; + m_terrainSurfacesDirty = true; m_currentSettings.m_worldBounds = m_requestedSettings.m_worldBounds; } @@ -614,12 +628,13 @@ void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) { m_dirtyRegion = AZ::Aabb::CreateNull(); m_terrainHeightDirty = true; + m_terrainSurfacesDirty = true; } m_currentSettings = m_requestedSettings; } - if (terrainSettingsChanged || m_terrainHeightDirty) + if (terrainSettingsChanged || m_terrainHeightDirty || m_terrainSurfacesDirty) { // Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus). // We lock our surface data mutex *before* checking / setting "isRequestInProgress" so that we prevent race conditions @@ -629,24 +644,27 @@ void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false); typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex); - AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask = - AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask::None; + Terrain::TerrainDataChangedMask changeMask = Terrain::TerrainDataChangedMask::None; if (terrainSettingsChanged) { - changeMask = static_cast( - changeMask | AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask::Settings); + changeMask = static_cast(changeMask | Terrain::TerrainDataChangedMask::Settings); } if (m_terrainHeightDirty) { - changeMask = static_cast( - changeMask | AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask::HeightData); + changeMask = static_cast(changeMask | Terrain::TerrainDataChangedMask::HeightData); + } + + if (m_terrainSurfacesDirty) + { + changeMask = static_cast(changeMask | Terrain::TerrainDataChangedMask::SurfaceData); } // Make sure to set these *before* calling OnTerrainDataChanged, since it's possible that subsystems reacting to that call will // cause the data to become dirty again. AZ::Aabb dirtyRegion = m_dirtyRegion; m_terrainHeightDirty = false; + m_terrainSurfacesDirty = false; m_dirtyRegion = AZ::Aabb::CreateNull(); AzFramework::Terrain::TerrainDataNotificationBus::Broadcast( diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h index 956424f048..022cd218cc 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h @@ -47,7 +47,8 @@ namespace Terrain void RegisterArea(AZ::EntityId areaId) override; void UnregisterArea(AZ::EntityId areaId) override; - void RefreshArea(AZ::EntityId areaId) override; + void RefreshArea( + AZ::EntityId areaId, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask) override; /////////////////////////////////////////// // TerrainDataRequestBus::Handler Impl @@ -164,6 +165,7 @@ namespace Terrain bool m_terrainSettingsDirty = true; bool m_terrainHeightDirty = false; + bool m_terrainSurfacesDirty = false; AZ::Aabb m_dirtyRegion; mutable AZStd::shared_mutex m_areaMutex; diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystemBus.h b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystemBus.h index cda6d65a1e..013d82d94d 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystemBus.h +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystemBus.h @@ -44,7 +44,7 @@ namespace Terrain // register an area to override terrain virtual void RegisterArea(AZ::EntityId areaId) = 0; virtual void UnregisterArea(AZ::EntityId areaId) = 0; - virtual void RefreshArea(AZ::EntityId areaId) = 0; + virtual void RefreshArea(AZ::EntityId areaId, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask) = 0; }; using TerrainSystemServiceRequestBus = AZ::EBus; diff --git a/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp b/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp index 0ca5e7d99a..3778ba860f 100644 --- a/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp +++ b/Gems/Terrain/Code/Tests/LayerSpawnerTests.cpp @@ -190,7 +190,7 @@ TEST_F(LayerSpawnerComponentTest, LayerSpawnerTransformChangedUpdatesTerrainSyst CreateMockTerrainSystem(); // The TransformChanged call should refresh the area. - EXPECT_CALL(*m_terrainSystem, RefreshArea(_)).Times(1); + EXPECT_CALL(*m_terrainSystem, RefreshArea(_, _)).Times(1); AddLayerSpawnerAndShapeComponentToEntity(); @@ -211,7 +211,7 @@ TEST_F(LayerSpawnerComponentTest, LayerSpawnerShapeChangedUpdatesTerrainSystem) CreateMockTerrainSystem(); // The ShapeChanged call should refresh the area. - EXPECT_CALL(*m_terrainSystem, RefreshArea(_)).Times(1); + EXPECT_CALL(*m_terrainSystem, RefreshArea(_, _)).Times(1); AddLayerSpawnerAndShapeComponentToEntity(); diff --git a/Gems/Terrain/Code/Tests/TerrainHeightGradientListTests.cpp b/Gems/Terrain/Code/Tests/TerrainHeightGradientListTests.cpp index c314e4e968..ec500d6ada 100644 --- a/Gems/Terrain/Code/Tests/TerrainHeightGradientListTests.cpp +++ b/Gems/Terrain/Code/Tests/TerrainHeightGradientListTests.cpp @@ -93,7 +93,7 @@ TEST_F(TerrainHeightGradientListComponentTest, TerrainHeightGradientRefreshesTer // As the TerrainHeightGradientListComponent subscribes to the dependency monitor, RefreshArea will be called twice: // once due to OnCompositionChanged being picked up by the the dependency monitor and resending the notification, // and once when the HeightGradientListComponent gets the OnCompositionChanged directly through the DependencyNotificationBus. - EXPECT_CALL(terrainSystem, RefreshArea(_)).Times(2); + EXPECT_CALL(terrainSystem, RefreshArea(_, _)).Times(2); LmbrCentral::DependencyNotificationBus::Event(m_entity->GetId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged); From 6cce184340dbce9796234a67a0452d1088474945 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Thu, 28 Oct 2021 14:33:24 -0700 Subject: [PATCH 096/120] Enforce unique gem names in catalog (#5063) Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com> --- Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 90eaaf0628..81598f5a6a 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -27,6 +27,14 @@ namespace O3DE::ProjectManager void GemModel::AddGem(const GemInfo& gemInfo) { + if (FindIndexByNameString(gemInfo.m_name).isValid()) + { + // do not add gems with duplicate names + // this can happen by mistake or when a gem repo has a gem with the same name as a local gem + AZ_TracePrintf("GemModel", "Ignoring duplicate gem: %s", gemInfo.m_name.toUtf8().constData()); + return; + } + QStandardItem* item = new QStandardItem(); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); From 29f5fb1271f30a51e236e101f57fe2d6a7c760c0 Mon Sep 17 00:00:00 2001 From: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Thu, 28 Oct 2021 16:38:27 -0500 Subject: [PATCH 097/120] {lyn7352} adding more logging around mock_asset_builder.py (#5103) o3de\AutomatedTesting\Gem\PythonTests\PythonAssetBuilder\mock_asset_builder.py - adding more logging - updated keys for platforms (pc, server) Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> --- .../Gem/PythonTests/PythonAssetBuilder/mock_asset_builder.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/mock_asset_builder.py b/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/mock_asset_builder.py index 443a420b61..a6e8b97b63 100644 --- a/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/mock_asset_builder.py +++ b/AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/mock_asset_builder.py @@ -23,7 +23,7 @@ def create_jobs(request): jobDescriptorList = [] for platformInfo in request.enabledPlatforms: jobDesc = azlmbr.asset.builder.JobDescriptor() - jobDesc.jobKey = jobKeyName + jobDesc.jobKey = f'{jobKeyName}-{platformInfo.identifier}' jobDesc.set_platform_identifier(platformInfo.identifier) jobDescriptorList.append(jobDesc) @@ -38,7 +38,7 @@ def on_create_jobs(args): return create_jobs(request) except: log_exception_traceback() - # returing back a default CreateJobsResponse() records an asset error + # returning back a default CreateJobsResponse() records an asset error return azlmbr.asset.builder.CreateJobsResponse() def process_file(request): @@ -58,6 +58,7 @@ def process_file(request): fileOutput = open(tempFilename, "w") fileOutput.write('{}') fileOutput.close() + print(f'Wrote mock asset file: {tempFilename}') # generate a product asset file entry subId = binascii.crc32(mockFilename.encode()) From 899bda4631208958cfc46e30a9ed7dd5dcf01c0e Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu, 28 Oct 2021 17:17:18 -0500 Subject: [PATCH 098/120] Add additional logging for Fingerprint test. (#5104) This logging is to help track down a Jenkins only automated test failure. Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> --- .../tests/assetmanager/AssetProcessorManagerTest.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp index 6c19213414..0b02b168dc 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp @@ -4447,7 +4447,9 @@ AssetBuilderSDK::AssetBuilderDesc MockBuilderInfoHandler::CreateBuilderDesc(cons void FingerprintTest::SetUp() { + AZ_Printf("FingerprintTest", "SetUp start"); AssetProcessorManagerTest::SetUp(); + AZ_Printf("FingerprintTest", "SetUp self"); // We don't want the mock application manager to provide builder descriptors, mockBuilderInfoHandler will provide our own m_mockApplicationManager->BusDisconnect(); @@ -4466,18 +4468,23 @@ void FingerprintTest::SetUp() }); ASSERT_TRUE(UnitTestUtils::CreateDummyFile(m_absolutePath, "")); + AZ_Printf("FingerprintTest", "SetUp end"); } void FingerprintTest::TearDown() { + AZ_Printf("FingerprintTest", "TearDown start"); m_jobResults = AZStd::vector{}; m_mockBuilderInfoHandler = {}; + AZ_Printf("FingerprintTest", "TearDown parent"); AssetProcessorManagerTest::TearDown(); + AZ_Printf("FingerprintTest", "TearDown end"); } void FingerprintTest::RunFingerprintTest(QString builderFingerprint, QString jobFingerprint, bool expectedResult) { + AZ_Printf("FingerprintTest", "Fingerprint Test Start"); m_mockBuilderInfoHandler.m_builderDesc.m_analysisFingerprint = builderFingerprint.toUtf8().data(); m_mockBuilderInfoHandler.m_jobFingerprint = jobFingerprint; QMetaObject::invokeMethod(m_assetProcessorManager.get(), "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, m_absolutePath)); @@ -4486,6 +4493,7 @@ void FingerprintTest::RunFingerprintTest(QString builderFingerprint, QString job ASSERT_EQ(m_mockBuilderInfoHandler.m_createJobsCount, 1); ASSERT_EQ(m_jobResults.size(), 1); ASSERT_EQ(m_jobResults[0].m_autoFail, expectedResult); + AZ_Printf("FingerprintTest", "Fingerprint Test End"); } TEST_F(FingerprintTest, FingerprintChecking_JobFingerprint_NoBuilderFingerprint) From ecf70daca8bf2b4a562832bdfb48db2dcb7adc95 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Thu, 28 Oct 2021 17:44:16 -0700 Subject: [PATCH 099/120] does this space affects? Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- scripts/build/Platform/Windows/installer_windows.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index 6f793cab64..87f53adc7f 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -48,7 +48,7 @@ IF ERRORLEVEL 1 ( ) IF NOT "%CPACK_BUCKET%"=="" ( - SET "CPACK_OPTIONS=-DCPACK_UPLOAD_URL=s3://%CPACK_BUCKET% %CPACK_OPTIONS%" + SET "CPACK_OPTIONS=-D CPACK_UPLOAD_URL=s3://%CPACK_BUCKET% %CPACK_OPTIONS%" ) ECHO [ci_build] "!CPACK_PATH!" -C %CONFIGURATION% %CPACK_OPTIONS% From 4dcd60ba086e2216841b5dff4e13292986d6742c Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Thu, 28 Oct 2021 17:49:31 -0700 Subject: [PATCH 100/120] testing changes to fix Jenkins Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/Platform/Windows/Packaging_windows.cmake | 1 + scripts/build/Platform/Windows/build_config.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 630817370f..e5aeef6209 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -8,6 +8,7 @@ set(LY_INSTALLER_WIX_ROOT "" CACHE PATH "Path to the WiX install path") +message("LY_INSTALLER_WIX_ROOT ${LY_INSTALLER_WIX_ROOT}") if(LY_INSTALLER_WIX_ROOT) if(NOT EXISTS ${LY_INSTALLER_WIX_ROOT}) message(FATAL_ERROR "Invalid path supplied for LY_INSTALLER_WIX_ROOT argument") diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index bebc91cb4d..b185a845ec 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -363,7 +363,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"!WIX!\"", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"!WIX! \"", "EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=!INSTALLER_DOWNLOAD_URL! -DLY_INSTALLER_LICENSE_URL=!INSTALLER_DOWNLOAD_URL!/license", "CPACK_BUCKET": "!INSTALLER_BUCKET!", "CMAKE_LY_PROJECTS": "", From 60e14d3a4ca82b9f183a46195d57e555f4937ee9 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Thu, 28 Oct 2021 18:17:26 -0700 Subject: [PATCH 101/120] Fixes PR comments Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/Platform/Windows/Packaging_windows.cmake | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index e5aeef6209..7b8f5a6c19 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -8,7 +8,6 @@ set(LY_INSTALLER_WIX_ROOT "" CACHE PATH "Path to the WiX install path") -message("LY_INSTALLER_WIX_ROOT ${LY_INSTALLER_WIX_ROOT}") if(LY_INSTALLER_WIX_ROOT) if(NOT EXISTS ${LY_INSTALLER_WIX_ROOT}) message(FATAL_ERROR "Invalid path supplied for LY_INSTALLER_WIX_ROOT argument") @@ -106,9 +105,8 @@ set(_raw_text_license [[ #(loc.InstallEulaAcceptance) ]]) -# if we are doing an offline installer, there is a limit in size the wix tooling can handle and produces -# issues for our current sizes. If the installer is offline, disable the curstom wix generator generating -# a msi instead. +# The offline installer generation will be a single monolithic MSI. The WIX burn tool for the bootstrapper EXE has a size limitation. +# So we will exclude the generation of the boostrapper EXE in the offline case. if(LY_INSTALLER_DOWNLOAD_URL) set(WIX_THEME_WARNING_IMAGE ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/warning.png) From 4e2de0170c345d1dc69b2589f0492b4bde95fb72 Mon Sep 17 00:00:00 2001 From: evanchia Date: Thu, 28 Oct 2021 19:33:59 -0700 Subject: [PATCH 102/120] reverting typehints Signed-off-by: evanchia --- .../_internal/pytest_plugin/editor_test.py | 11 +-- .../ly_test_tools/o3de/editor_test.py | 82 ++++++++----------- .../ly_test_tools/o3de/editor_test_utils.py | 26 ++---- 3 files changed, 46 insertions(+), 73 deletions(-) diff --git a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/editor_test.py b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/editor_test.py index 19caf7e090..1e9d4c3654 100644 --- a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/editor_test.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/editor_test.py @@ -7,14 +7,13 @@ SPDX-License-Identifier: Apache-2.0 OR MIT Utility for specifying an Editor test, supports seamless parallelization and/or batching of tests. This is not a set of tools to directly invoke, but a plugin with functions intended to be called by only the Pytest framework. """ - +from __future__ import annotations import pytest import inspect __test__ = False -def pytest_addoption(parser): - # type (argparse.ArgumentParser) -> None +def pytest_addoption(parser: argparse.ArgumentParser) -> None: """ Options when running editor tests in batches or parallel. :param parser: The ArgumentParser object @@ -24,8 +23,7 @@ def pytest_addoption(parser): parser.addoption("--no-editor-parallel", action="store_true", help="Don't run multiple editors in parallel") parser.addoption("--editors-parallel", type=int, action="store", help="Override the number editors to run at the same time") -def pytest_pycollect_makeitem(collector, name, obj): - # type (PyCollector, str, object) -> Collector +def pytest_pycollect_makeitem(collector: PyCollector, name: str, obj: object) -> PyCollector: """ Create a custom custom item collection if the class defines pytest_custom_makeitem function. This is used for automatically generating test functions with a custom collector. @@ -40,8 +38,7 @@ def pytest_pycollect_makeitem(collector, name, obj): return base.pytest_custom_makeitem(collector, name, obj) @pytest.hookimpl(hookwrapper=True) -def pytest_collection_modifyitems(session, items, config): - # type (Session, List[EditorTestBase], Config) -> None +def pytest_collection_modifyitems(session: Session, items: list[EditorTestBase], config: Config) -> None: """ Add custom modification of items. This is used for adding the runners into the item list. :param session: The Pytest Session diff --git a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py index cf3682e080..ae6ed16321 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/editor_test.py @@ -25,7 +25,7 @@ Usage example: EditorTestSuite does introspection of the defined classes inside of it and automatically prepares the tests, parallelizing/batching as required """ - +from __future__ import annotations import pytest from _pytest.skipping import pytest_runtest_setup as skipping_pytest_runtest_setup @@ -46,6 +46,7 @@ import re import ly_test_tools.environment.file_system as file_system import ly_test_tools.environment.waiter as waiter import ly_test_tools.environment.process_utils as process_utils +import ly_test_tools.o3de.editor_test import ly_test_tools.o3de.editor_test_utils as editor_utils from ly_test_tools.o3de.asset_processor import AssetProcessor @@ -133,8 +134,7 @@ class Result: class Pass(Base): @classmethod - def create(cls, test_spec, output, editor_log): - # type (EditorTestBase, str, str) -> Pass + def create(cls, test_spec: EditorTestBase, output: str, editor_log: str) -> Pass: """ Creates a Pass object with a given test spec, output string, and editor log string. :test_spec: The type of EditorTestBase @@ -160,8 +160,7 @@ class Result: class Fail(Base): @classmethod - def create(cls, test_spec, output, editor_log): - # type (EditorTestBase, str, str) -> Fail + def create(cls, test_spec: EditorTestBase, output: str, editor_log: str) -> Fail: """ Creates a Fail object with a given test spec, output string, and editor log string. :test_spec: The type of EditorTestBase @@ -191,8 +190,7 @@ class Result: class Crash(Base): @classmethod - def create(cls, test_spec, output, ret_code, stacktrace, editor_log): - # type (EditorTestBase, str, int, str, str) -> Crash + def create(cls, test_spec: EditorTestBase, output: str, ret_code: int, stacktrace: str, editor_log: str) -> Crash: """ Creates a Crash object with a given test spec, output string, and editor log string. This also includes the return code and stacktrace. @@ -232,8 +230,7 @@ class Result: class Timeout(Base): @classmethod - def create(cls, test_spec, output, time_secs, editor_log): - # type (EditorTestBase, str, float, str) -> Timeout + def create(cls, test_spec: EditorTestBase, output: str, time_secs: float, editor_log: str) -> Timeout: """ Creates a Timeout object with a given test spec, output string, and editor log string. The timeout time should be provided in seconds @@ -266,8 +263,7 @@ class Result: class Unknown(Base): @classmethod - def create(cls, test_spec, output, extra_info, editor_log): - # type (EditorTestBase, str, str , str) -> Unknown + def create(cls, test_spec: EditorTestBase, output: str, extra_info: str, editor_log: str) -> Unknown: """ Creates an Unknown test results object if something goes wrong. :test_spec: The type of EditorTestBase @@ -318,8 +314,7 @@ class EditorTestSuite(): _TEST_FAIL_RETCODE = 0xF # Return code for test failure @pytest.fixture(scope="class") - def editor_test_data(self, request): - # type (request) -> TestData + def editor_test_data(self, request: Request) -> TestData: """ Yields a per-testsuite structure to store the data of each test result and an AssetProcessor object that will be re-used on the whole suite @@ -328,7 +323,7 @@ class EditorTestSuite(): """ yield from self._editor_test_data(request) - def _editor_test_data(self, request): + def _editor_test_data(self, request: Request) -> TestData: """ A wrapper function for unit testing to call directly """ @@ -513,8 +508,7 @@ class EditorTestSuite(): return EditorTestSuite.EditorTestClass(name, collector) @classmethod - def pytest_custom_modify_items(cls, session, items, config): - # type (Session, List[EditorTestBase], Config) -> None + def pytest_custom_modify_items(cls, session: Session, items: list[EditorTestBase], config: Config) -> None: """ Adds the runners' functions and filters the tests that will run. The runners will be added if they have any selected tests @@ -538,8 +532,7 @@ class EditorTestSuite(): items[:] = items + new_items @classmethod - def get_single_tests(cls): - # type () -> List + def get_single_tests(cls) -> list[EditorSingleTest]: """ Grabs all of the EditorSingleTests subclassed tests from the EditorTestSuite class Usage example: @@ -552,8 +545,7 @@ class EditorTestSuite(): return single_tests @classmethod - def get_shared_tests(cls): - # type () -> List + def get_shared_tests(cls) -> list[EditorSharedTest]: """ Grabs all of the EditorSharedTests from the EditorTestSuite Usage example: @@ -566,8 +558,7 @@ class EditorTestSuite(): return shared_tests @classmethod - def get_session_shared_tests(cls, session): - # type (Session) -> List[EditorTestBase] + def get_session_shared_tests(cls, session: Session) -> list[EditorTestBase]: """ Filters and returns all of the shared tests in a given session. :session: The test session @@ -577,8 +568,7 @@ class EditorTestSuite(): return cls.filter_session_shared_tests(session, shared_tests) @staticmethod - def filter_session_shared_tests(session_items, shared_tests): - # type (List[EditorTestBase, List[EditorSharedTest]) -> List[EditorTestBase] + def filter_session_shared_tests(session_items: list[EditorTestBase], shared_tests: list[EditorSharedTest]) -> list[EditorTestBase]: """ Retrieve the test sub-set that was collected this can be less than the original set if were overriden via -k argument or similars @@ -599,8 +589,8 @@ class EditorTestSuite(): return selected_shared_tests @staticmethod - def filter_shared_tests(shared_tests, is_batchable=False, is_parallelizable=False): - # type (List[EditorSharedTest], bool, bool) -> List[EditorSharedTest] + def filter_shared_tests(shared_tests: list[EditorSharedTest], is_batchable: bool = False, + is_parallelizable: bool = False) -> list[EditorSharedTest]: """ Filters and returns all tests based off of if they are batchable and/or parallelizable :shared_tests: All shared tests @@ -617,8 +607,7 @@ class EditorTestSuite(): ] ### Utils ### - def _prepare_asset_processor(self, workspace, editor_test_data): - # type (AbstractWorkspace, TestData) -> None + def _prepare_asset_processor(self, workspace: AbstractWorkspace, editor_test_data: TestData) -> None: """ Prepares the asset processor for the test depending on whether or not the process is open and if the current test owns it. @@ -643,8 +632,7 @@ class EditorTestSuite(): editor_test_data.asset_processor = None raise ex - def _setup_editor_test(self, editor, workspace, editor_test_data): - # type(Editor, AbstractWorkspace, TestData) -> None + def _setup_editor_test(self, editor: Editor, workspace: AbstractWorkspace, editor_test_data: TestData) -> None: """ Sets up an editor test by preparing the Asset Processor, killing all other O3DE processes, and configuring :editor: The launcher Editor object @@ -657,8 +645,7 @@ class EditorTestSuite(): editor.configure_settings() @staticmethod - def _get_results_using_output(test_spec_list, output, editor_log_content): - # type(List[EditorTestBase], str, str) -> dict{str: Result} + def _get_results_using_output(test_spec_list: list[EditorTestBase], output: str, editor_log_content: str) -> dict[str, Result]: """ Utility function for parsing the output information from the editor. It deserializes the JSON content printed in the output for every test and returns that information. @@ -718,8 +705,7 @@ class EditorTestSuite(): return results @staticmethod - def _report_result(name, result): - # type (str, Result) -> None + def _report_result(name: str, result: Result) -> None: """ Fails the test if the test result is not a PASS, specifying the information :name: Name of the test @@ -734,8 +720,8 @@ class EditorTestSuite(): pytest.fail(error_str) ### Running tests ### - def _exec_editor_test(self, request, workspace, editor, run_id, log_name, test_spec, cmdline_args = []): - # type (Request, AbstractWorkspace, Editor, int, str, EditorTestBase, List[str] -> dict{str: Result} + def _exec_editor_test(self, request: Request, workspace: AbstractWorkspace, editor: Editor, run_id: int, + log_name: str, test_spec: EditorTestBase, cmdline_args: list[str] = []) -> dict[str, Result]: """ Starts the editor with the given test and retuns an result dict with a single element specifying the result :request: The pytest request @@ -796,8 +782,8 @@ class EditorTestSuite(): results[test_spec.__name__] = test_result return results - def _exec_editor_multitest(self, request, workspace, editor, run_id, log_name, test_spec_list, cmdline_args=[]): - # type (Request, AbstractWorkspace, Editor, int, str, List[EditorTestBase], List[str]) -> dict{str: Result} + def _exec_editor_multitest(self, request: Request, workspace: AbstractWorkspace, editor: Editor, run_id: int, log_name: str, + test_spec_list: list[EditorTestBase], cmdline_args: list[str] = []) -> dict[str, Result]: """ Starts an editor executable with a list of tests and returns a dict of the result of every test ran within that editor instance. In case of failure this function also parses the editor output to find out what specific tests @@ -907,8 +893,8 @@ class EditorTestSuite(): return results - def _run_single_test(self, request, workspace, editor, editor_test_data, test_spec): - # type (Request, AbstractWorkspace, Editor, TestData, EditorSingleTest) -> None + def _run_single_test(self, request: Request, workspace: AbstractWorkspace, editor: Editor, + editor_test_data: TestData, test_spec: EditorSingleTest) -> None: """ Runs a single test (one editor, one test) with the given specs :request: The Pytest Request @@ -928,8 +914,8 @@ class EditorTestSuite(): test_name, test_result = next(iter(results.items())) self._report_result(test_name, test_result) - def _run_batched_tests(self, request, workspace, editor, editor_test_data, test_spec_list, extra_cmdline_args=[]): - # type (Request, AbstractWorkspace, Editor, TestData, List[EditorSharedTest], List[str]) -> None + def _run_batched_tests(self, request: Request, workspace: AbstractWorkspace, editor: Editor, editor_test_data: TestData, + test_spec_list: list[EditorSharedTest], extra_cmdline_args: list[str] = []) -> None: """ Runs a batch of tests in one single editor with the given spec list (one editor, multiple tests) :request: The Pytest Request @@ -949,8 +935,8 @@ class EditorTestSuite(): assert results is not None editor_test_data.results.update(results) - def _run_parallel_tests(self, request, workspace, editor, editor_test_data, test_spec_list, extra_cmdline_args=[]): - # type(Request, AbstractWorkspace, Editor, TestData, List[EditorSharedTest], List[str]) -> None + def _run_parallel_tests(self, request: Request, workspace: AbstractWorkspace, editor: Editor, editor_test_data: TestData, + test_spec_list: list[EditorSharedTest], extra_cmdline_args: list[str] = []) -> None: """ Runs multiple editors with one test on each editor (multiple editor, one test each) :request: The Pytest Request @@ -997,9 +983,8 @@ class EditorTestSuite(): for result in results_per_thread: editor_test_data.results.update(result) - def _run_parallel_batched_tests(self, request, workspace, editor, editor_test_data, test_spec_list, - extra_cmdline_args=[]): - # type(Request, AbstractWorkspace, Editor, TestData, List[EditorSharedTest], List[str] -> None + def _run_parallel_batched_tests(self, request: Request, workspace: AbstractWorkspace, editor: Editor, editor_test_data: TestData, + test_spec_list: list[EditorSharedTest], extra_cmdline_args: list[str] = []) -> None: """ Runs multiple editors with a batch of tests for each editor (multiple editor, multiple tests each) :request: The Pytest Request @@ -1047,8 +1032,7 @@ class EditorTestSuite(): for result in results_per_thread: editor_test_data.results.update(result) - def _get_number_parallel_editors(self, request): - # type(Request) -> int + def _get_number_parallel_editors(self, request: Request) -> int: """ Retrieves the number of parallel preference cmdline overrides :request: The Pytest Request diff --git a/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py b/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py index 6e319bb860..35fbe93d37 100644 --- a/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py +++ b/Tools/LyTestTools/ly_test_tools/o3de/editor_test_utils.py @@ -6,7 +6,7 @@ SPDX-License-Identifier: Apache-2.0 OR MIT Utility functions mostly for the editor_test module. They can also be used for assisting Editor tests. """ - +from __future__ import annotations import os import time import logging @@ -16,8 +16,7 @@ import ly_test_tools.environment.waiter as waiter logger = logging.getLogger(__name__) -def kill_all_ly_processes(include_asset_processor=True): - # type (bool) -> None +def kill_all_ly_processes(include_asset_processor: bool = True) -> None: """ Kills all common O3DE processes such as the Editor, Game Launchers, and optionally Asset Processor. Defaults to killing the Asset Processor. @@ -36,8 +35,7 @@ def kill_all_ly_processes(include_asset_processor=True): else: process_utils.kill_processes_named(LY_PROCESSES, ignore_extensions=True) -def get_testcase_module_filepath(testcase_module): - # type: (Module) -> str +def get_testcase_module_filepath(testcase_module: Module) -> str: """ return the full path of the test module using always '.py' extension :param testcase_module: The testcase python module being tested @@ -45,8 +43,7 @@ def get_testcase_module_filepath(testcase_module): """ return os.path.splitext(testcase_module.__file__)[0] + ".py" -def get_module_filename(testcase_module): - # type: (Module) -> str +def get_module_filename(testcase_module: Module): """ return The filename of the module without path Note: This is differs from module.__name__ in the essence of not having the package directory. @@ -56,8 +53,7 @@ def get_module_filename(testcase_module): """ return os.path.splitext(os.path.basename(testcase_module.__file__))[0] -def retrieve_log_path(run_id, workspace): - # type (int, ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager) -> str +def retrieve_log_path(run_id: int, workspace: AbstractWorkspaceManager) -> str: """ return the log/ project path for this test run. :param run_id: editor id that will be used for differentiating paths @@ -66,8 +62,7 @@ def retrieve_log_path(run_id, workspace): """ return os.path.join(workspace.paths.project(), "user", f"log_test_{run_id}") -def retrieve_crash_output(run_id, workspace, timeout=10): - # type (int, ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager, float) -> str +def retrieve_crash_output(run_id: int, workspace: AbstractWorkspaceManager, timeout: float = 10) -> str: """ returns the crash output string for the given test run. :param run_id: editor id that will be used for differentiating paths @@ -90,8 +85,7 @@ def retrieve_crash_output(run_id, workspace, timeout=10): crash_info += f"\n{str(ex)}" return crash_info -def cycle_crash_report(run_id, workspace): - # type (int, ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager) -> None +def cycle_crash_report(run_id: int, workspace: AbstractWorkspaceManager) -> None: """ Attempts to rename error.log and error.dmp(crash files) into new names with the timestamp on it. :param run_id: editor id that will be used for differentiating paths @@ -111,8 +105,7 @@ def cycle_crash_report(run_id, workspace): except Exception as ex: logger.warning(f"Couldn't cycle file {filepath}. Error: {str(ex)}") -def retrieve_editor_log_content(run_id, log_name, workspace, timeout=10): - # type (int , str, ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager, int) -> str +def retrieve_editor_log_content(run_id: int, log_name: str, workspace: AbstractWorkspaceManager, timeout: int = 10) -> str: """ Retrieves the contents of the given editor log file. :param run_id: editor id that will be used for differentiating paths @@ -138,8 +131,7 @@ def retrieve_editor_log_content(run_id, log_name, workspace, timeout=10): editor_info = f"-- Error reading editor.log: {str(ex)} --" return editor_info -def retrieve_last_run_test_index_from_output(test_spec_list, output): - # type (List[EditorTestBase], str) -> int +def retrieve_last_run_test_index_from_output(test_spec_list: list[EditorTestBase], output: str) -> int: """ Finds out what was the last test that was run by inspecting the input. This is used for determining what was the batched test has crashed the editor From 37381489ee2bac06f7600db4fb27f4282eb4ad0d Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Fri, 29 Oct 2021 09:12:27 +0200 Subject: [PATCH 103/120] EMotion FX: Actor component: Skeleton rendering is not working (#5084) Enabling the "Draw skeleton" checkbox in the actor component does not show the skeleton. Signed-off-by: Benjamin Jillich --- .../Code/Source/Integration/Components/ActorComponent.cpp | 2 +- .../Integration/Editor/Components/EditorActorComponent.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp index 8520896dd2..e38e15d124 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp @@ -576,7 +576,7 @@ namespace EMotionFX // The configuration stores some debug option. When that is enabled, we override it on top of the render flags. m_debugRenderFlags[RENDER_AABB] = m_debugRenderFlags[RENDER_AABB] || m_configuration.m_renderBounds; - m_debugRenderFlags[RENDER_SKELETON] = m_debugRenderFlags[RENDER_SKELETON] || m_configuration.m_renderSkeleton; + m_debugRenderFlags[RENDER_LINESKELETON] = m_debugRenderFlags[RENDER_LINESKELETON] || m_configuration.m_renderSkeleton; m_debugRenderFlags[RENDER_EMFX_DEBUG] = true; m_renderActorInstance->DebugDraw(m_debugRenderFlags); } diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index d0274b3c68..78470a1fa5 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -606,7 +606,7 @@ namespace EMotionFX m_renderActorInstance->UpdateBounds(); m_debugRenderFlags[RENDER_AABB] = m_renderBounds; - m_debugRenderFlags[RENDER_SKELETON] = m_renderSkeleton; + m_debugRenderFlags[RENDER_LINESKELETON] = m_renderSkeleton; m_debugRenderFlags[RENDER_EMFX_DEBUG] = true; m_renderActorInstance->DebugDraw(m_debugRenderFlags); } From a322d9e2b851c7252370eba2c8aaa7306e727705 Mon Sep 17 00:00:00 2001 From: Artur K <96597+nemerle@users.noreply.github.com> Date: Fri, 29 Oct 2021 10:59:18 +0200 Subject: [PATCH 104/120] Small `Code/Editor` cleanup pass (#4909) * Clean-up in ConfigGroup Removed unused templated AddVar and related code. Replaced legacy types with `AZ::` ones. Cleaned up cpp file. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Add a few missing Q_OBJECT macros, remove some unused variables. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Apply some of clazy suggestions + simplifications * removed `emit` from non-signal function calls. * replaced `QStringLiteral("")` with a constexpr friendly `QLatin1String()` Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Fix a CNewLevelDialog focus bug Fixed an incorrect QTimer::singleShot invocation. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * match lambda to `messageChanged` signature Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * vs compilation fix + applied review Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * apply reviewer recommendation Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> --- Code/Editor/CVarMenu.h | 3 + Code/Editor/ConfigGroup.cpp | 182 +++++++++--------- Code/Editor/ConfigGroup.h | 90 ++------- .../PropertyMiscCtrl.h | 6 +- .../PropertyResourceCtrl.cpp | 12 +- .../PropertyResourceCtrl.h | 1 + .../ReflectedVarWrapper.cpp | 67 ++++--- Code/Editor/Controls/TimelineCtrl.cpp | 2 - Code/Editor/CryEdit.h | 1 + Code/Editor/CustomResolutionDlg.h | 5 +- Code/Editor/CustomizeKeyboardDialog.cpp | 6 +- Code/Editor/ErrorDialog.cpp | 4 +- Code/Editor/KeyboardCustomizationSettings.cpp | 2 +- Code/Editor/MainStatusBar.cpp | 2 +- Code/Editor/NewLevelDialog.cpp | 7 +- 15 files changed, 169 insertions(+), 221 deletions(-) diff --git a/Code/Editor/CVarMenu.h b/Code/Editor/CVarMenu.h index efcc8e8caf..5195bd99d7 100644 --- a/Code/Editor/CVarMenu.h +++ b/Code/Editor/CVarMenu.h @@ -16,9 +16,12 @@ #include #include +struct ICVar; + class CVarMenu : public QMenu { + Q_OBJECT public: // CVar that can be toggled on and off struct CVarToggle diff --git a/Code/Editor/ConfigGroup.cpp b/Code/Editor/ConfigGroup.cpp index 74fe6f7b5c..42236e43dd 100644 --- a/Code/Editor/ConfigGroup.cpp +++ b/Code/Editor/ConfigGroup.cpp @@ -19,10 +19,9 @@ namespace Config CConfigGroup::~CConfigGroup() { - for (TConfigVariables::const_iterator it = m_vars.begin(); - it != m_vars.end(); ++it) + for (IConfigVar* var : m_vars) { - delete (*it); + delete var; } } @@ -31,17 +30,15 @@ namespace Config m_vars.push_back(var); } - uint32 CConfigGroup::GetVarCount() + AZ::u32 CConfigGroup::GetVarCount() { - return static_cast(m_vars.size()); + return aznumeric_cast(m_vars.size()); } IConfigVar* CConfigGroup::GetVar(const char* szName) { - for (TConfigVariables::const_iterator it = m_vars.begin(); - it != m_vars.end(); ++it) + for (IConfigVar* var : m_vars) { - IConfigVar* var = (*it); if (0 == _stricmp(szName, var->GetName().c_str())) { return var; @@ -53,20 +50,19 @@ namespace Config const IConfigVar* CConfigGroup::GetVar(const char* szName) const { - for (TConfigVariables::const_iterator it = m_vars.begin(); - it != m_vars.end(); ++it) + for (const IConfigVar* var : m_vars) { - IConfigVar* var = (*it); if (0 == _stricmp(szName, var->GetName().c_str())) { return var; } + } return nullptr; } - IConfigVar* CConfigGroup::GetVar(uint index) + IConfigVar* CConfigGroup::GetVar(AZ::u32 index) { if (index < m_vars.size()) { @@ -76,7 +72,7 @@ namespace Config return nullptr; } - const IConfigVar* CConfigGroup::GetVar(uint index) const + const IConfigVar* CConfigGroup::GetVar(AZ::u32 index) const { if (index < m_vars.size()) { @@ -89,114 +85,110 @@ namespace Config void CConfigGroup::SaveToXML(XmlNodeRef node) { // save only values that don't have default values - for (TConfigVariables::const_iterator it = m_vars.begin(); - it != m_vars.end(); ++it) + for (const IConfigVar* var : m_vars) { - IConfigVar* var = (*it); - if (!var->IsFlagSet(IConfigVar::eFlag_DoNotSave)) + if (var->IsFlagSet(IConfigVar::eFlag_DoNotSave) || var->IsDefault()) { - if (!var->IsDefault()) - { - const char* szName = var->GetName().c_str(); + continue; + } - switch (var->GetType()) - { - case IConfigVar::eType_BOOL: - { - bool currentValue = false; - var->Get(¤tValue); - node->setAttr(szName, currentValue); - break; - } + const char* szName = var->GetName().c_str(); - case IConfigVar::eType_INT: - { - int currentValue = 0; - var->Get(¤tValue); - node->setAttr(szName, currentValue); - break; - } + switch (var->GetType()) + { + case IConfigVar::eType_BOOL: + { + bool currentValue = false; + var->Get(¤tValue); + node->setAttr(szName, currentValue); + break; + } - case IConfigVar::eType_FLOAT: - { - float currentValue = 0; - var->Get(¤tValue); - node->setAttr(szName, currentValue); - break; - } + case IConfigVar::eType_INT: + { + int currentValue = 0; + var->Get(¤tValue); + node->setAttr(szName, currentValue); + break; + } - case IConfigVar::eType_STRING: - { - AZStd::string currentValue; - var->Get(¤tValue); - node->setAttr(szName, currentValue.c_str()); - break; - } - } - } + case IConfigVar::eType_FLOAT: + { + float currentValue = 0; + var->Get(¤tValue); + node->setAttr(szName, currentValue); + break; + } + + case IConfigVar::eType_STRING: + { + AZStd::string currentValue; + var->Get(¤tValue); + node->setAttr(szName, currentValue.c_str()); + break; + } } } } void CConfigGroup::LoadFromXML(XmlNodeRef node) { - // save only values that don't have default values - for (TConfigVariables::const_iterator it = m_vars.begin(); - it != m_vars.end(); ++it) + // load values that are save-able + for (IConfigVar* var : m_vars) { - IConfigVar* var = (*it); - if (!var->IsFlagSet(IConfigVar::eFlag_DoNotSave)) + if (var->IsFlagSet(IConfigVar::eFlag_DoNotSave)) { - const char* szName = var->GetName().c_str(); + continue; + } + const char* szName = var->GetName().c_str(); - switch (var->GetType()) + switch (var->GetType()) + { + case IConfigVar::eType_BOOL: + { + bool currentValue = false; + var->GetDefault(¤tValue); + if (node->getAttr(szName, currentValue)) { - case IConfigVar::eType_BOOL: - { - bool currentValue = false; - var->GetDefault(¤tValue); - if (node->getAttr(szName, currentValue)) - { - var->Set(¤tValue); - } - break; + var->Set(¤tValue); } + break; + } - case IConfigVar::eType_INT: + case IConfigVar::eType_INT: + { + int currentValue = 0; + var->GetDefault(¤tValue); + if (node->getAttr(szName, currentValue)) { - int currentValue = 0; - var->GetDefault(¤tValue); - if (node->getAttr(szName, currentValue)) - { - var->Set(¤tValue); - } - break; + var->Set(¤tValue); } + break; + } - case IConfigVar::eType_FLOAT: + case IConfigVar::eType_FLOAT: + { + float currentValue = 0; + var->GetDefault(¤tValue); + if (node->getAttr(szName, currentValue)) { - float currentValue = 0; - var->GetDefault(¤tValue); - if (node->getAttr(szName, currentValue)) - { - var->Set(¤tValue); - } - break; + var->Set(¤tValue); } + break; + } - case IConfigVar::eType_STRING: + case IConfigVar::eType_STRING: + { + AZStd::string currentValue; + var->GetDefault(¤tValue); + QString readValue(currentValue.c_str()); + if (node->getAttr(szName, readValue)) { - AZStd::string currentValue; - var->GetDefault(¤tValue); - QString readValue(currentValue.c_str()); - if (node->getAttr(szName, readValue)) - { - currentValue = readValue.toUtf8().data(); - var->Set(¤tValue); - } - break; - } + currentValue = readValue.toUtf8().data(); + var->Set(¤tValue); } + break; + } } } } diff --git a/Code/Editor/ConfigGroup.h b/Code/Editor/ConfigGroup.h index 769a29ba8a..004725e32c 100644 --- a/Code/Editor/ConfigGroup.h +++ b/Code/Editor/ConfigGroup.h @@ -8,8 +8,12 @@ #pragma once -#ifndef CRYINCLUDE_EDITOR_CONFIGGROUP_H -#define CRYINCLUDE_EDITOR_CONFIGGROUP_H +#include +#include +#include + +struct ICVar; +class XmlNodeRef; namespace Config { @@ -32,7 +36,7 @@ namespace Config eFlag_DoNotSave = 1 << 2, }; - IConfigVar(const char* szName, const char* szDescription, EType varType, uint8 flags) + IConfigVar(const char* szName, const char* szDescription, EType varType, AZ::u8 flags) : m_name(szName) , m_description(szDescription) , m_type(varType) @@ -42,22 +46,22 @@ namespace Config virtual ~IConfigVar() = default; - ILINE EType GetType() const + AZ_FORCE_INLINE EType GetType() const { return m_type; } - ILINE const AZStd::string& GetName() const + AZ_FORCE_INLINE const AZStd::string& GetName() const { return m_name; } - ILINE const AZStd::string& GetDescription() const + AZ_FORCE_INLINE const AZStd::string& GetDescription() const { return m_description; } - ILINE bool IsFlagSet(EFlags flag) const + AZ_FORCE_INLINE bool IsFlagSet(EFlags flag) const { return 0 != (m_flags & flag); } @@ -68,73 +72,28 @@ namespace Config virtual void GetDefault(void* outPtr) const = 0; virtual void Reset() = 0; - static EType TranslateType(const bool&) { return eType_BOOL; } - static EType TranslateType(const int&) { return eType_INT; } - static EType TranslateType(const float&) { return eType_FLOAT; } - static EType TranslateType(const AZStd::string&) { return eType_STRING; } + static constexpr EType TranslateType(const bool&) { return eType_BOOL; } + static constexpr EType TranslateType(const int&) { return eType_INT; } + static constexpr EType TranslateType(const float&) { return eType_FLOAT; } + static constexpr EType TranslateType(const AZStd::string&) { return eType_STRING; } protected: EType m_type; - uint8 m_flags; + AZ::u8 m_flags; AZStd::string m_name; AZStd::string m_description; void* m_ptr; ICVar* m_pCVar; }; - // Typed wrapper for config variable - template - class TConfigVar - : public IConfigVar - { - private: - T m_default; - - public: - TConfigVar(const char* szName, const char* szDescription, uint8 flags, T& ptr, const T& defaultValue) - : IConfigVar(szName, szDescription, IConfigVar::TranslateType(ptr), flags) - , m_default(defaultValue) - { - m_ptr = &ptr; - - // reset to default value on initializations - ptr = defaultValue; - } - - virtual void Get(void* outPtr) const - { - *reinterpret_cast(outPtr) = *reinterpret_cast(m_ptr); - } - - virtual void Set(const void* ptr) - { - *reinterpret_cast(m_ptr) = *reinterpret_cast(ptr); - } - - virtual void Reset() - { - *reinterpret_cast(m_ptr) = m_default; - } - - virtual void GetDefault(void* outPtr) const - { - *reinterpret_cast(outPtr) = m_default; - } - - virtual bool IsDefault() const - { - return *reinterpret_cast(m_ptr) == m_default; - } - }; - // Group of configuration variables with optional mapping to CVars class CConfigGroup { private: - typedef std::vector TConfigVariables; + using TConfigVariables = AZStd::vector ; TConfigVariables m_vars; - typedef std::vector TConsoleVariables; + using TConsoleVariables = AZStd::vector; TConsoleVariables m_consoleVars; public: @@ -142,20 +101,13 @@ namespace Config virtual ~CConfigGroup(); void AddVar(IConfigVar* var); - uint32 GetVarCount(); + AZ::u32 GetVarCount(); IConfigVar* GetVar(const char* szName); - IConfigVar* GetVar(uint index); + IConfigVar* GetVar(AZ::u32 index); const IConfigVar* GetVar(const char* szName) const; - const IConfigVar* GetVar(uint index) const; + const IConfigVar* GetVar(AZ::u32 index) const; void SaveToXML(XmlNodeRef node); void LoadFromXML(XmlNodeRef node); - - template - void AddVar(const char* szName, const char* szDescription, T& var, const T& defaultValue, uint8 flags = 0) - { - AddVar(new TConfigVar(szName, szDescription, flags, var, defaultValue)); - } }; }; -#endif // CRYINCLUDE_EDITOR_CONFIGGROUP_H diff --git a/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.h b/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.h index 5ec24b679d..849e44cedd 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.h +++ b/Code/Editor/Controls/ReflectedPropertyControl/PropertyMiscCtrl.h @@ -6,8 +6,6 @@ * */ -#ifndef CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H -#define CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H #pragma once #if !defined(Q_MOC_RUN) @@ -53,6 +51,7 @@ private: class UserPopupWidgetHandler : public QObject, public AzToolsFramework::PropertyHandler < CReflectedVarUser, UserPropertyEditor> { + Q_OBJECT public: AZ_CLASS_ALLOCATOR(UserPopupWidgetHandler, AZ::SystemAllocator, 0); bool IsDefaultHandler() const override { return false; } @@ -67,6 +66,7 @@ public: class FloatCurveHandler : public QObject, public AzToolsFramework::PropertyHandler < CReflectedVarSpline, CSplineCtrl> { + Q_OBJECT public: AZ_CLASS_ALLOCATOR(FloatCurveHandler, AZ::SystemAllocator, 0); bool IsDefaultHandler() const override { return false; } @@ -80,5 +80,3 @@ public: void OnSplineChange(CSplineCtrl*); }; - -#endif // CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H diff --git a/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.cpp b/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.cpp index c5ccc599d6..d26e978ae8 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.cpp +++ b/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.cpp @@ -58,17 +58,9 @@ private: void OnClicked() override { QString tempValue(""); - QString ext(""); - if (m_path.isEmpty() == false) + if (!m_path.isEmpty() && !Path::GetExt(m_path).isEmpty()) { - if (Path::GetExt(m_path) == "") - { - tempValue = ""; - } - else - { - tempValue = m_path; - } + tempValue = m_path; } AssetSelectionModel selection; diff --git a/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.h b/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.h index fa30eba034..087ee9f1db 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.h +++ b/Code/Editor/Controls/ReflectedPropertyControl/PropertyResourceCtrl.h @@ -99,6 +99,7 @@ class FileResourceSelectorWidgetHandler : QObject , public AzToolsFramework::PropertyHandler < CReflectedVarResource, FileResourceSelectorWidget > { + Q_OBJECT public: AZ_CLASS_ALLOCATOR(FileResourceSelectorWidgetHandler, AZ::SystemAllocator, 0); diff --git a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.cpp b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.cpp index b3f1b35461..2320938f08 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.cpp +++ b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.cpp @@ -446,41 +446,54 @@ void ReflectedVarUserAdapter::SetVariable(IVariable *pVariable) m_reflectedVar.reset(new CReflectedVarUser( pVariable->GetHumanName().toUtf8().data())); } -void ReflectedVarUserAdapter::SyncReflectedVarToIVar(IVariable *pVariable) +void ReflectedVarUserAdapter::SyncReflectedVarToIVar(IVariable* pVariable) { QString value; pVariable->Get(value); m_reflectedVar->m_value = value.toUtf8().data(); - //extract the list of custom items from the IVariable user data - IVariable::IGetCustomItems* pGetCustomItems = static_cast (pVariable->GetUserData().value()); - if (pGetCustomItems != nullptr) - { - std::vector items; - QString dlgTitle; - // call the user supplied callback to fill-in items and get dialog title - bool bShowIt = pGetCustomItems->GetItems(pVariable, items, dlgTitle); - if (bShowIt) // if func didn't veto, show the dialog - { - m_reflectedVar->m_enableEdit = true; - m_reflectedVar->m_useTree = pGetCustomItems->UseTree(); - m_reflectedVar->m_treeSeparator = pGetCustomItems->GetTreeSeparator(); - m_reflectedVar->m_dialogTitle = dlgTitle.toUtf8().data(); - m_reflectedVar->m_itemNames.resize(items.size()); - m_reflectedVar->m_itemDescriptions.resize(items.size()); - - QByteArray ba; - int i = -1; - std::generate(m_reflectedVar->m_itemNames.begin(), m_reflectedVar->m_itemNames.end(), [&items, &i, &ba]() { ++i; ba = items[i].name.toUtf8(); return ba.data(); }); - i = -1; - std::generate(m_reflectedVar->m_itemDescriptions.begin(), m_reflectedVar->m_itemDescriptions.end(), [&items, &i, &ba]() { ++i; ba = items[i].desc.toUtf8(); return ba.data(); }); - - } - } - else + // extract the list of custom items from the IVariable user data + IVariable::IGetCustomItems* pGetCustomItems = static_cast(pVariable->GetUserData().value()); + if (pGetCustomItems == nullptr) { m_reflectedVar->m_enableEdit = false; + return; } + + std::vector items; + QString dlgTitle; + // call the user supplied callback to fill-in items and get dialog title + bool bShowIt = pGetCustomItems->GetItems(pVariable, items, dlgTitle); + if (!bShowIt) // if func vetoed it, don't show the dialog + { + return; + } + m_reflectedVar->m_enableEdit = true; + m_reflectedVar->m_useTree = pGetCustomItems->UseTree(); + m_reflectedVar->m_treeSeparator = pGetCustomItems->GetTreeSeparator(); + m_reflectedVar->m_dialogTitle = dlgTitle.toUtf8().data(); + m_reflectedVar->m_itemNames.resize(items.size()); + m_reflectedVar->m_itemDescriptions.resize(items.size()); + + QByteArray ba; + int i = -1; + AZStd::generate( + m_reflectedVar->m_itemNames.begin(), m_reflectedVar->m_itemNames.end(), + [&items, &i, &ba]() + { + ++i; + ba = items[i].name.toUtf8(); + return ba.data(); + }); + i = -1; + AZStd::generate( + m_reflectedVar->m_itemDescriptions.begin(), m_reflectedVar->m_itemDescriptions.end(), + [&items, &i, &ba]() + { + ++i; + ba = items[i].desc.toUtf8(); + return ba.data(); + }); } void ReflectedVarUserAdapter::SyncIVarToReflectedVar(IVariable *pVariable) diff --git a/Code/Editor/Controls/TimelineCtrl.cpp b/Code/Editor/Controls/TimelineCtrl.cpp index aa30c326ac..3890b46b2b 100644 --- a/Code/Editor/Controls/TimelineCtrl.cpp +++ b/Code/Editor/Controls/TimelineCtrl.cpp @@ -126,7 +126,6 @@ void TimelineWidget::DrawTicks(QPainter* painter) const QPen pOldPen = painter->pen(); const QPen ltgray(QColor(110, 110, 110)); - const QPen black(palette().color(QPalette::Normal, QPalette::Text)); const QPen redpen(QColor(255, 0, 255)); // Draw time ticks every tick step seconds. @@ -598,7 +597,6 @@ void TimelineWidget::DrawSecondTicks(QPainter* painter) { const QPen ltgray(QColor(110, 110, 110)); const QPen black(palette().color(QPalette::Normal, QPalette::Text)); - const QPen redpen(QColor(255, 0, 255)); for (int gx = m_grid.firstGridLine.x(); gx < m_grid.firstGridLine.x() + m_grid.numGridLines.x() + 1; gx++) { diff --git a/Code/Editor/CryEdit.h b/Code/Editor/CryEdit.h index f68cfdc33d..53ee8f1905 100644 --- a/Code/Editor/CryEdit.h +++ b/Code/Editor/CryEdit.h @@ -431,6 +431,7 @@ public: class CCrySingleDocTemplate : public QObject { + Q_OBJECT private: explicit CCrySingleDocTemplate(const QMetaObject* pDocClass) : QObject() diff --git a/Code/Editor/CustomResolutionDlg.h b/Code/Editor/CustomResolutionDlg.h index 5dd9acaae8..e1b8035c65 100644 --- a/Code/Editor/CustomResolutionDlg.h +++ b/Code/Editor/CustomResolutionDlg.h @@ -12,8 +12,6 @@ // Notice : Refer to ViewportTitleDlg.cpp for a use case. -#ifndef CRYINCLUDE_EDITOR_CUSTOMRESOLUTIONDLG_H -#define CRYINCLUDE_EDITOR_CUSTOMRESOLUTIONDLG_H #pragma once #if !defined(Q_MOC_RUN) @@ -28,6 +26,7 @@ namespace Ui class CCustomResolutionDlg : public QDialog { + Q_OBJECT public: CCustomResolutionDlg(int w, int h, QWidget* pParent = nullptr); ~CCustomResolutionDlg(); @@ -42,5 +41,3 @@ protected: QScopedPointer m_ui; }; - -#endif // CRYINCLUDE_EDITOR_CUSTOMRESOLUTIONDLG_H diff --git a/Code/Editor/CustomizeKeyboardDialog.cpp b/Code/Editor/CustomizeKeyboardDialog.cpp index ce09f8d878..280d6c5323 100644 --- a/Code/Editor/CustomizeKeyboardDialog.cpp +++ b/Code/Editor/CustomizeKeyboardDialog.cpp @@ -211,9 +211,9 @@ public: void Reset(QAction& action) { - emit beginResetModel(); + beginResetModel(); m_action = &action; - emit endResetModel(); + endResetModel(); } private: @@ -266,7 +266,7 @@ QStringList CustomizeKeyboardDialog::BuildModels(QWidget* parent) categories.append(category); QMenu* menu = menuAction->menu(); - m_menuActions[category] = GetAllActionsForMenu(menu, QStringLiteral("")); + m_menuActions[category] = GetAllActionsForMenu(menu, QString()); } return categories; diff --git a/Code/Editor/ErrorDialog.cpp b/Code/Editor/ErrorDialog.cpp index 0a45b2bf89..e43df75947 100644 --- a/Code/Editor/ErrorDialog.cpp +++ b/Code/Editor/ErrorDialog.cpp @@ -25,9 +25,9 @@ namespace SandboxEditor connect(m_ui->okButton, &QPushButton::clicked, this, &ErrorDialog::OnOK); connect( m_ui->messages, - SIGNAL(itemSelectionChanged()), + &QTreeWidget::itemSelectionChanged, this, - SLOT(MessageSelectionChanged())); + &ErrorDialog::MessageSelectionChanged); } ErrorDialog::~ErrorDialog() diff --git a/Code/Editor/KeyboardCustomizationSettings.cpp b/Code/Editor/KeyboardCustomizationSettings.cpp index c4f9133f66..81d9375850 100644 --- a/Code/Editor/KeyboardCustomizationSettings.cpp +++ b/Code/Editor/KeyboardCustomizationSettings.cpp @@ -240,7 +240,7 @@ QJsonObject KeyboardCustomizationSettings::ExportGroup() void KeyboardCustomizationSettings::ImportFromFile(QWidget* parent) { - QString fileName = QFileDialog::getOpenFileName(parent, QObject::tr("Export Keyboard Shortcuts"), QStringLiteral(""), QObject::tr("Keyboard Settings (*.keys)")); + QString fileName = QFileDialog::getOpenFileName(parent, QObject::tr("Export Keyboard Shortcuts"), QString(), QObject::tr("Keyboard Settings (*.keys)")); if (fileName.isEmpty()) { return; diff --git a/Code/Editor/MainStatusBar.cpp b/Code/Editor/MainStatusBar.cpp index f955035bd8..acc7f664df 100644 --- a/Code/Editor/MainStatusBar.cpp +++ b/Code/Editor/MainStatusBar.cpp @@ -419,7 +419,7 @@ void MemoryStatusItem::updateStatus() GeneralStatusItem::GeneralStatusItem(QString name, MainStatusBar* parent) : StatusBarItem(name, parent) { - connect(parent, SIGNAL(messageChanged(QString)), this, SLOT(update())); + connect(parent, &MainStatusBar::messageChanged, this, [this](const QString&) { update(); }); } QString GeneralStatusItem::CurrentText() const diff --git a/Code/Editor/NewLevelDialog.cpp b/Code/Editor/NewLevelDialog.cpp index 29445cef2b..c773acdb6f 100644 --- a/Code/Editor/NewLevelDialog.cpp +++ b/Code/Editor/NewLevelDialog.cpp @@ -100,9 +100,10 @@ CNewLevelDialog::CNewLevelDialog(QWidget* pParent /*=nullptr*/) m_level = ""; // First of all, keyboard focus is related to widget tab order, and the default tab order is based on the order in which // widgets are constructed. Therefore, creating more widgets changes the keyboard focus. That is why setFocus() is called last. - // Secondly, using singleShot() allows setFocus() slot of the QLineEdit instance to be invoked right after the event system - // is ready to do so. Therefore, it is better to use singleShot() than directly call setFocus(). - QTimer::singleShot(0, ui->LEVEL, SLOT(OnStartup())); + // in OnStartup() + // Secondly, using singleShot() allows OnStartup() slot of the QLineEdit instance to be invoked right after the event system + // is ready to do so. Therefore, it is better to use singleShot() than directly call OnStartup(). + QTimer::singleShot(0, this, &CNewLevelDialog::OnStartup); ReloadLevelFolder(); } From f40191dd8c2dd626a76d203f24dbe5437f64dec3 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Fri, 29 Oct 2021 02:15:22 -0700 Subject: [PATCH 105/120] bugfix: resolve broken focus for viewport (#5059) Signed-off-by: Michael Pollind --- .../AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp index 5155de3087..0f79e3535d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp @@ -301,6 +301,7 @@ namespace AzToolsFramework::ViewportUi::Internal HighlightBorderSize + ViewportUiOverlayMargin, HighlightBorderSize + ViewportUiOverlayMargin); m_componentModeBorderText.setVisible(true); m_componentModeBorderText.setText(borderTitle.c_str()); + UpdateUiOverlayGeometry(); } void ViewportUiDisplay::RemoveViewportBorder() From ca9093c2028e5e48ebc1e940488dc3585b18e2cb Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Fri, 29 Oct 2021 09:20:52 -0700 Subject: [PATCH 106/120] Add engine name, folder and fix refresh crash (#5112) Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> --- .../Resources/ProjectManager.qss | 8 +++-- .../Source/EngineSettingsScreen.cpp | 32 ++++++++++++++++--- .../Source/EngineSettingsScreen.h | 1 - .../Source/FormBrowseEditWidget.cpp | 5 +-- .../Source/FormBrowseEditWidget.h | 5 ++- .../Source/GemCatalog/GemCatalogScreen.cpp | 2 +- .../Source/GemCatalog/GemModel.cpp | 1 + .../Source/UpdateProjectSettingsScreen.cpp | 1 + 8 files changed, 43 insertions(+), 12 deletions(-) diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index fecdc67920..d3ec066be7 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -242,11 +242,15 @@ QTabBar::tab:focus { /************** Project Settings **************/ #projectSettings { - margin-top:42px; + margin-top:30px; +} + +#projectPreviewLabel { + margin: 10px 0 5px 0; } #projectTemplate { - margin: 55px 0 0 50px; + margin: 25px 0 0 50px; } #projectTemplateLabel { font-size:16px; diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp index dec1c9a257..c7df00f423 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp @@ -11,19 +11,28 @@ #include #include #include +#include #include #include #include #include +#include namespace O3DE::ProjectManager { EngineSettingsScreen::EngineSettingsScreen(QWidget* parent) : ScreenWidget(parent) { - auto* layout = new QVBoxLayout(); + QScrollArea* scrollArea = new QScrollArea(this); + scrollArea->setWidgetResizable(true); + + QWidget* scrollWidget = new QWidget(this); + scrollArea->setWidget(scrollWidget); + + QVBoxLayout* layout = new QVBoxLayout(scrollWidget); layout->setAlignment(Qt::AlignTop); + scrollWidget->setLayout(layout); setObjectName("engineSettingsScreen"); @@ -39,9 +48,18 @@ namespace O3DE::ProjectManager formTitleLabel->setObjectName("formTitleLabel"); layout->addWidget(formTitleLabel); - m_engineVersion = new FormLineEditWidget(tr("Engine Version"), engineInfo.m_version, this); - m_engineVersion->lineEdit()->setReadOnly(true); - layout->addWidget(m_engineVersion); + FormLineEditWidget* engineName = new FormLineEditWidget(tr("Engine Name"), engineInfo.m_name, this); + engineName->lineEdit()->setReadOnly(true); + layout->addWidget(engineName); + + FormLineEditWidget* engineVersion = new FormLineEditWidget(tr("Engine Version"), engineInfo.m_version, this); + engineVersion->lineEdit()->setReadOnly(true); + layout->addWidget(engineVersion); + + FormBrowseEditWidget* engineFolder = new FormBrowseEditWidget(tr("Engine Folder"), engineInfo.m_path, this); + engineFolder->lineEdit()->setReadOnly(true); + connect( engineFolder, &FormBrowseEditWidget::OnBrowse, [engineInfo]{ AzQtComponents::ShowFileOnDesktop(engineInfo.m_path); }); + layout->addWidget(engineFolder); m_thirdParty = new FormFolderBrowseEditWidget(tr("3rd Party Software Folder"), engineInfo.m_thirdPartyPath, this); m_thirdParty->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this)); @@ -71,7 +89,11 @@ namespace O3DE::ProjectManager connect(m_defaultProjectTemplates->lineEdit(), &QLineEdit::textChanged, this, &EngineSettingsScreen::OnTextChanged); layout->addWidget(m_defaultProjectTemplates); - setLayout(layout); + QVBoxLayout* mainLayout = new QVBoxLayout(); + mainLayout->setAlignment(Qt::AlignTop); + mainLayout->setMargin(0); + mainLayout->addWidget(scrollArea); + setLayout(mainLayout); } ProjectManagerScreen EngineSettingsScreen::GetScreenEnum() diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h index 2f16400405..1efabd4b5e 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h @@ -29,7 +29,6 @@ namespace O3DE::ProjectManager void OnTextChanged(); private: - FormLineEditWidget* m_engineVersion; FormBrowseEditWidget* m_thirdParty; FormBrowseEditWidget* m_defaultProjects; FormBrowseEditWidget* m_defaultGems; diff --git a/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.cpp b/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.cpp index 8cd28fcbb4..fe101cf37a 100644 --- a/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.cpp +++ b/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.cpp @@ -20,7 +20,8 @@ namespace O3DE::ProjectManager setObjectName("formBrowseEditWidget"); QPushButton* browseButton = new QPushButton(this); - connect(browseButton, &QPushButton::pressed, this, &FormBrowseEditWidget::HandleBrowseButton); + connect( browseButton, &QPushButton::pressed, [this]{ emit OnBrowse(); }); + connect( this, &FormBrowseEditWidget::OnBrowse, this, &FormBrowseEditWidget::HandleBrowseButton); m_frameLayout->addWidget(browseButton); } @@ -34,7 +35,7 @@ namespace O3DE::ProjectManager int key = event->key(); if (key == Qt::Key_Return || key == Qt::Key_Enter) { - HandleBrowseButton(); + emit OnBrowse(); } } diff --git a/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.h b/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.h index a1f6948ce9..179fe03253 100644 --- a/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.h +++ b/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.h @@ -24,10 +24,13 @@ namespace O3DE::ProjectManager explicit FormBrowseEditWidget(const QString& labelText = "", QWidget* parent = nullptr); ~FormBrowseEditWidget() = default; + signals: + void OnBrowse(); + protected: void keyPressEvent(QKeyEvent* event) override; protected slots: - virtual void HandleBrowseButton() = 0; + virtual void HandleBrowseButton() {}; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 35ea67bdff..98121d7cd2 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -80,7 +80,7 @@ namespace O3DE::ProjectManager void GemCatalogScreen::ReinitForProject(const QString& projectPath) { - m_gemModel->clear(); + m_gemModel->Clear(); m_gemsToRegisterWithProject.clear(); FillModel(projectPath); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 81598f5a6a..fb228c0b4a 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -68,6 +68,7 @@ namespace O3DE::ProjectManager void GemModel::Clear() { clear(); + m_nameToIndexMap.clear(); } void GemModel::UpdateGemDependencies() diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectSettingsScreen.cpp index 6f7f7e1bed..3bfc07c5b0 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectSettingsScreen.cpp @@ -36,6 +36,7 @@ namespace O3DE::ProjectManager QLabel* projectPreviewLabel = new QLabel(tr("Select an image (PNG). Minimum %1 x %2 pixels.") .arg(QString::number(ProjectPreviewImageWidth), QString::number(ProjectPreviewImageHeight))); + projectPreviewLabel->setObjectName("projectPreviewLabel"); previewExtrasLayout->addWidget(projectPreviewLabel); m_projectPreviewImage = new QLabel(this); From 971e24285fd11af8a53bce40c40aca0d17318404 Mon Sep 17 00:00:00 2001 From: ffarahmand-DPS Date: Fri, 29 Oct 2021 11:28:42 -0700 Subject: [PATCH 107/120] Fixes debug console's "quit" issues (#4975) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixes a crash caused when attempting to ‘quit’ the launcher from the debug console. The change simply ensures that the underlying font data the OnRenderTick() function relies on has not yet been released from memory. Signed-off-by: ffarahmand-DPS * Fixes a crash caused when attempting to ‘quit’ the launcher from the debug console. An object created on the heap was never deleted, causing a chain reaction. Signed-off-by: ffarahmand-DPS * Some quick clean-up for safety. Signed-off-by: ffarahmand-DPS * Changes !defined(DEDICATED_SERVER) to a runtime check since macro is no longer defined. Signed-off-by: ffarahmand-DPS --- .../CrySystem/ViewSystem/ViewSystem.cpp | 36 ++++++++++++++----- ...AtomViewportDisplayInfoSystemComponent.cpp | 3 +- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/Code/Legacy/CrySystem/ViewSystem/ViewSystem.cpp b/Code/Legacy/CrySystem/ViewSystem/ViewSystem.cpp index e83676827f..d7e5c081c0 100644 --- a/Code/Legacy/CrySystem/ViewSystem/ViewSystem.cpp +++ b/Code/Legacy/CrySystem/ViewSystem/ViewSystem.cpp @@ -115,17 +115,20 @@ CViewSystem::CViewSystem(ISystem* pSystem) , m_useDeferredViewSystemUpdate(false) , m_bControlsAudioListeners(true) { -#if !defined(_RELEASE) && !defined(DEDICATED_SERVER) - if (!s_debugCamera) +#if !defined(_RELEASE) + if (!gEnv->IsDedicated()) { - s_debugCamera = new DebugCamera; - } + if (!s_debugCamera) + { + s_debugCamera = new DebugCamera; + } - REGISTER_COMMAND("debugCameraToggle", ToggleDebugCamera, VF_DEV_ONLY, "Toggle the debug camera.\n"); - REGISTER_COMMAND("debugCameraInvertY", ToggleDebugCameraInvertY, VF_DEV_ONLY, "Toggle debug camera Y-axis inversion.\n"); - REGISTER_COMMAND("debugCameraMove", DebugCameraMove, VF_DEV_ONLY, "Move the debug camera the specified distance (x y z).\n"); - gEnv->pConsole->CreateKeyBind("ctrl_keyboard_key_punctuation_backslash", "debugCameraToggle"); - gEnv->pConsole->CreateKeyBind("alt_keyboard_key_punctuation_backslash", "debugCameraInvertY"); + REGISTER_COMMAND("debugCameraToggle", ToggleDebugCamera, VF_DEV_ONLY, "Toggle the debug camera.\n"); + REGISTER_COMMAND("debugCameraInvertY", ToggleDebugCameraInvertY, VF_DEV_ONLY, "Toggle debug camera Y-axis inversion.\n"); + REGISTER_COMMAND("debugCameraMove", DebugCameraMove, VF_DEV_ONLY, "Move the debug camera the specified distance (x y z).\n"); + gEnv->pConsole->CreateKeyBind("ctrl_keyboard_key_punctuation_backslash", "debugCameraToggle"); + gEnv->pConsole->CreateKeyBind("alt_keyboard_key_punctuation_backslash", "debugCameraInvertY"); + } #endif REGISTER_CVAR2("cl_camera_noise", &m_fCameraNoise, -1, 0, @@ -167,6 +170,21 @@ CViewSystem::~CViewSystem() { m_pSystem->GetILevelSystem()->RemoveListener(this); } + +#if !defined(_RELEASE) + if (!gEnv->IsDedicated()) + { + UNREGISTER_COMMAND("debugCameraToggle"); + UNREGISTER_COMMAND("debugCameraInvertY"); + UNREGISTER_COMMAND("debugCameraMove"); + + if (s_debugCamera) + { + delete s_debugCamera; + s_debugCamera = nullptr; + } + } +#endif } //------------------------------------------------------------------------ diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp index 7d659ebb7a..91844c9b2f 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/Code/Source/AtomViewportDisplayInfoSystemComponent.cpp @@ -130,7 +130,8 @@ namespace AZ::Render } AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext(); - if (!m_fontDrawInterface || !viewportContext || !viewportContext->GetRenderScene()) + if (!m_fontDrawInterface || !viewportContext || !viewportContext->GetRenderScene() || + !AZ::Interface::Get()) { return; } From 0807cb7f3e3c5148f17f60ed602fcd7e2e1ec2bd Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Fri, 29 Oct 2021 15:41:17 -0500 Subject: [PATCH 108/120] Added "RemovePassTemplate" method. (#5039) * Added "RemovePassTemplate" method. Signed-off-by: garrieta * RemovePassTemplate only allows to remove PassTemplates created with the Runtime API. It now asserts if there are Passes referencing the PassTemplate that should be removed. Signed-off-by: garrieta --- .../Code/Include/Atom/RPI.Public/Pass/PassLibrary.h | 10 ++++++++++ .../Code/Include/Atom/RPI.Public/Pass/PassSystem.h | 1 + .../Atom/RPI.Public/Pass/PassSystemInterface.h | 3 +++ .../RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp | 13 +++++++++++++ .../RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp | 5 +++++ 5 files changed, 32 insertions(+) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h index 66c3c205ab..f79da54636 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h @@ -77,6 +77,16 @@ namespace AZ const AZStd::shared_ptr GetPassTemplate(const Name& name) const; const AZStd::vector& GetPassesForTemplate(const Name& templateName) const; + //! Removes a PassTemplate by name, only if the following two conditions are met: + //! 1- The template was NOT created from an Asset. This means the template will be erasable + //! only if it was created at runtime with C++. + //! 2- The are no instantiated Passes referencing such template. + //! If the template exists but both conditions are not met then the function will assert. + //! If a template with the given name doesn't exist the function does nothing. + //! This function should be used judiciously, and under rare circumstances. For example, + //! Applications that iteratively create and need to delete templates at runtime. + void RemovePassTemplate(const Name& name); + //! Removes a pass from both it's associated template (if it has one) and from the pass name mapping void RemovePassFromLibrary(Pass* pass); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h index 8390b0f7e2..6412bf6166 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h @@ -94,6 +94,7 @@ namespace AZ bool HasPassesForTemplateName(const Name& templateName) const override; bool AddPassTemplate(const Name& name, const AZStd::shared_ptr& passTemplate) override; const AZStd::shared_ptr GetPassTemplate(const Name& name) const override; + void RemovePassTemplate(const Name& name) override; void RemovePassFromLibrary(Pass* pass) override; void RegisterPass(Pass* pass) override; void UnregisterPass(Pass* pass) override; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h index 7f944df88f..b91a31cfd5 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h @@ -199,6 +199,9 @@ namespace AZ //! Retrieves a PassTemplate from the library virtual const AZStd::shared_ptr GetPassTemplate(const Name& name) const = 0; + //! See remarks in PassLibrary.h for the function with this name. + virtual void RemovePassTemplate(const Name& name) = 0; + //! Removes all references to the given pass from the pass library virtual void RemovePassFromLibrary(Pass* pass) = 0; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp index 6a6f5f3ff9..13b2fa391f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp @@ -236,6 +236,19 @@ namespace AZ return true; } + void PassLibrary::RemovePassTemplate(const Name& name) + { + auto itr = m_templateEntries.find(name); + if (itr != m_templateEntries.end()) + { + AZ_Assert(itr->second.m_passes.empty(), "Can not delete PassTemplate '%s' because there are %zu Passes referencing it", + name.GetCStr(), itr->second.m_passes.size()); + AZ_Assert(!itr->second.m_mappingAssetId.IsValid(), "Can not delete PassTemplate '%s' because it was created from an asset", + name.GetCStr()); + m_templateEntries.erase(itr); + } + } + void PassLibrary::RemovePassFromLibrary(Pass* pass) { if (m_isShuttingDown) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp index 7f2948c13a..39d0e879e1 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp @@ -466,6 +466,11 @@ namespace AZ return m_passLibrary.GetPassTemplate(name); } + void PassSystem::RemovePassTemplate(const Name& name) + { + m_passLibrary.RemovePassTemplate(name); + } + void PassSystem::RemovePassFromLibrary(Pass* pass) { m_passLibrary.RemovePassFromLibrary(pass); From de4658b16ccfd285fe7ad3a334a5e4b53166e2f5 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 29 Oct 2021 14:25:13 -0700 Subject: [PATCH 109/120] Corrects mistake naming the override commands (#5140) * corrects mistake naming the override commands Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * fixes PR comments Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/Platform/Common/Install_common.cmake | 4 ++-- cmake/Platform/Mac/Install_mac.cmake | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 6d43661b99..df19ad0e78 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -124,9 +124,9 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar cmake_path(RELATIVE_PATH target_library_output_directory BASE_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} OUTPUT_VARIABLE target_library_output_subdirectory) endif() - if(COMMAND ly_install_target_override) + if(COMMAND ly_setup_target_install_targets_override) # Mac needs special handling because of a cmake issue - ly_install_target_override(TARGET ${TARGET_NAME} + ly_setup_target_install_targets_override(TARGET ${TARGET_NAME} ARCHIVE_DIR ${archive_output_directory} LIBRARY_DIR ${library_output_directory} RUNTIME_DIR ${runtime_output_directory} diff --git a/cmake/Platform/Mac/Install_mac.cmake b/cmake/Platform/Mac/Install_mac.cmake index fe0424f86e..76c381138d 100644 --- a/cmake/Platform/Mac/Install_mac.cmake +++ b/cmake/Platform/Mac/Install_mac.cmake @@ -41,8 +41,8 @@ file(GENERATE configure_file(${LY_ROOT_FOLDER}/cmake/Platform/Mac/PreInstallSteps_mac.cmake.in ${CMAKE_BINARY_DIR}/runtime_install/PreInstallSteps_mac.cmake @ONLY) ly_install(SCRIPT ${CMAKE_BINARY_DIR}/runtime_install/PreInstallSteps_mac.cmake COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) -#! ly_setup_runtime_dependencies_copy_function_override: Mac specific target installation -function(ly_setup_runtime_dependencies_copy_function_override) +#! ly_setup_target_install_targets_override: Mac specific target installation +function(ly_setup_target_install_targets_override) set(options) set(oneValueArgs TARGET ARCHIVE_DIR LIBRARY_DIR RUNTIME_DIR LIBRARY_SUBDIR RUNTIME_SUBDIR) @@ -109,8 +109,8 @@ function(ly_setup_runtime_dependencies_copy_function_override) endif() endfunction() -#! ly_install_code_function_override: Mac specific copy function to handle frameworks -function(ly_install_code_function_override) +#! ly_setup_runtime_dependencies_copy_function_override: Mac specific copy function to handle frameworks +function(ly_setup_runtime_dependencies_copy_function_override) configure_file(${LY_ROOT_FOLDER}/cmake/Platform/Mac/InstallUtils_mac.cmake.in ${CMAKE_BINARY_DIR}/runtime_install/InstallUtils_mac.cmake @ONLY) ly_install_run_script(${CMAKE_BINARY_DIR}/runtime_install/InstallUtils_mac.cmake) From e871dff70ecde0576c4b06ec9b85a51ca8a81e93 Mon Sep 17 00:00:00 2001 From: AMZN-AlexOteiza <82234181+AMZN-AlexOteiza@users.noreply.github.com> Date: Fri, 29 Oct 2021 22:32:33 +0100 Subject: [PATCH 110/120] Added two complex prefab tests (#5089) * Added two complex prefab tests * Fix compile error * Added extra methods, fixed test failure * Addressed PR commments * More PR comments * Fix space * Fix ar error --- .../editor_entity_utils.py | 91 ++++++++++++++++--- .../editor_python_test_tools/prefab_utils.py | 8 ++ .../Gem/PythonTests/Prefab/TestSuite_Main.py | 8 ++ ...ComplexWorflow_CreatePrefabInsidePrefab.py | 57 ++++++++++++ ...omplexWorflow_CreatePrefabOfChildEntity.py | 52 +++++++++++ Code/Editor/CryEdit.cpp | 10 +- .../AzCore/AzCore/Component/TransformBus.h | 5 + .../Components/TransformComponent.cpp | 7 ++ .../Components/TransformComponent.h | 1 + 9 files changed, 223 insertions(+), 16 deletions(-) create mode 100644 AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabInsidePrefab.py create mode 100644 AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabOfChildEntity.py diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py index 844f8de903..9e857ed8bc 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py @@ -107,6 +107,19 @@ class EditorComponent: return type_ids + +def convert_to_azvector3(xyz) -> azlmbr.math.Vector3: + """ + Converts a vector3-like element into a azlmbr.math.Vector3 + """ + if isinstance(xyz, Tuple) or isinstance(xyz, List): + assert len(xyz) == 3, ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3") + return math.Vector3(float(xyz[0]), float(xyz[1]), float(xyz[2])) + elif isinstance(xyz, type(math.Vector3())): + return xyz + else: + raise ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3") + class EditorEntity: """ Entity class is used to create and interact with Editor Entities. @@ -183,15 +196,6 @@ class EditorEntity: :return: EditorEntity class object """ - def convert_to_azvector3(xyz) -> math.Vector3: - if isinstance(xyz, Tuple) or isinstance(xyz, List): - assert len(xyz) == 3, ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3") - return math.Vector3(*xyz) - elif isinstance(xyz, type(math.Vector3())): - return xyz - else: - raise ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3") - if parent_id is None: parent_id = azlmbr.entity.EntityId() @@ -206,7 +210,7 @@ class EditorEntity: return entity # Methods - def set_name(self, entity_name: str): + def set_name(self, entity_name: str) -> None: """ Given entity_name, sets name to Entity :param: entity_name: Name of the entity to set @@ -324,7 +328,7 @@ class EditorEntity: self.start_status = status return status - def set_start_status(self, desired_start_status: str): + def set_start_status(self, desired_start_status: str) -> None: """ Set an entity as active/inactive at beginning of runtime or it is editor-only, given its entity id and the start status then return set success @@ -382,18 +386,75 @@ class EditorEntity: """ return editor.EditorEntityInfoRequestBus(bus.Event, "IsVisible", self.id) + # World Transform Functions + def get_world_translation(self) -> azlmbr.math.Vector3: + """ + Gets the world translation of the entity + """ + return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id) + + def set_world_translation(self, new_translation) -> None: + """ + Sets the new world translation of the current entity + """ + new_translation = convert_to_azvector3(new_translation) + azlmbr.components.TransformBus(azlmbr.bus.Event, "SetWorldTranslation", self.id, new_translation) + + def get_world_rotation(self) -> azlmbr.math.Quaternion: + """ + Gets the world rotation of the entity + """ + return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", self.id) + + def set_world_rotation(self, new_rotation): + """ + Sets the new world rotation of the current entity + """ + new_rotation = convert_to_azvector3(new_rotation) + azlmbr.components.TransformBus(azlmbr.bus.Event, "SetWorldRotation", self.id, new_rotation) + + # Local Transform Functions + def get_local_uniform_scale(self) -> float: + """ + Gets the local uniform scale of the entity + """ + return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetLocalUniformScale", self.id) + def set_local_uniform_scale(self, scale_float) -> None: """ - Sets the "SetLocalUniformScale" value on the entity. + Sets the local uniform scale value(relative to the parent) on the entity. :param scale_float: value for "SetLocalUniformScale" to set to. :return: None """ azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalUniformScale", self.id, scale_float) - def set_local_rotation(self, vector3_rotation) -> None: + def get_local_rotation(self) -> azlmbr.math.Quaternion: """ - Sets the "SetLocalRotation" value on the entity. + Gets the local rotation of the entity + """ + return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetLocalRotation", self.id) + + def set_local_rotation(self, new_rotation) -> None: + """ + Sets the set the local rotation(relative to the parent) of the current entity. :param vector3_rotation: The math.Vector3 value to use for rotation on the entity (uses radians). :return: None """ - azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", self.id, vector3_rotation) + new_rotation = convert_to_azvector3(new_rotation) + azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", self.id, new_rotation) + + def get_local_translation(self) -> azlmbr.math.Vector3: + """ + Gets the local translation of the current entity. + :return: The math.Vector3 value of the local translation. + """ + return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetLocalTranslation", self.id) + + def set_local_translation(self, new_translation) -> None: + """ + Sets the local translation(relative to the parent) of the current entity. + :param vector3_translation: The math.Vector3 value to use for translation on the entity. + :return: None + """ + new_translation = convert_to_azvector3(new_translation) + azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalTranslation", self.id, new_translation) diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py index 10a6ab1ef4..76c2a42c0a 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py @@ -138,6 +138,14 @@ class PrefabInstance: self.container_entity = reparented_container_entity current_instance_prefab.instances.add(self) + def get_direct_child_entities(self): + """ + Returns the entities only contained in the current prefab instance. + This function does not return entities contained in other child instances + """ + return self.container_entity.get_children() + + # This is a helper class which contains some of the useful information about a prefab template. class Prefab: diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py index 5337f0669c..479915752f 100644 --- a/AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py @@ -55,3 +55,11 @@ class TestAutomation(TestAutomationBase): def test_PrefabBasicWorkflow_CreateAndDuplicatePrefab(self, request, workspace, editor, launcher_platform): from .tests import PrefabBasicWorkflow_CreateAndDuplicatePrefab as test_module self._run_prefab_test(request, workspace, editor, test_module) + + def test_PrefabComplexWorflow_CreatePrefabOfChildEntity(self, request, workspace, editor, launcher_platform): + from .tests import PrefabComplexWorflow_CreatePrefabOfChildEntity as test_module + self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False) + + def test_PrefabComplexWorflow_CreatePrefabInsidePrefab(self, request, workspace, editor, launcher_platform): + from .tests import PrefabComplexWorflow_CreatePrefabInsidePrefab as test_module + self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False) diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabInsidePrefab.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabInsidePrefab.py new file mode 100644 index 0000000000..e14fc96449 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabInsidePrefab.py @@ -0,0 +1,57 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + +def PrefabComplexWorflow_CreatePrefabInsidePrefab(): + """ + Test description: + - Creates an entity with a physx collider + - Creates a prefab "Outer_prefab" and an instance based of that entity + - Creates a prefab "Inner_prefab" inside "Outer_prefab" based the entity contained inside of it + Checks that the entity is correctly handlded by the prefab system checking the name and that it contains the physx collider + """ + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.prefab_utils import Prefab + + import PrefabTestUtils as prefab_test_utils + + prefab_test_utils.open_base_tests_level() + + # Creates a new Entity at the root level + # Asserts if creation didn't succeed + entity = EditorEntity.create_editor_entity_at((100.0, 100.0, 100.0), name = "TestEntity") + assert entity.id.IsValid(), "Couldn't create entity" + entity.add_component("PhysX Collider") + assert entity.has_component("PhysX Collider"), "Attempted to add a PhysX Collider but no physx collider collider was found afterwards" + + # Create a prefab based on that entity + outer_prefab, outer_instance = Prefab.create_prefab([entity], "Outer_prefab") + # The test should be now inside the outer prefab instance. + entity = outer_instance.get_direct_child_entities()[0] + # We track if that is the same entity by checking the name and if it still contains the component that we created before + assert entity.get_name() == "TestEntity", f"Entity name inside outer_prefab doesn't match the original name, original:'TestEntity' current:'{entity.get_name()}'" + assert entity.has_component("PhysX Collider"), "Entity name inside outer_prefab doesn't have the collider component it should" + + # Now, create another prefab, based on the entity that is inside outer_prefab + inner_prefab, inner_instance = Prefab.create_prefab([entity], "Inner_prefab") + # The test entity should now be inside the inner prefab instance + entity = inner_instance.get_direct_child_entities()[0] + # We track if that is the same entity by checking the name and if it still contains the component that we created before + assert entity.get_name() == "TestEntity", f"Entity name inside inner_prefab doesn't match the original name, original:'TestEntity' current:'{entity.get_name()}'" + assert entity.has_component("PhysX Collider"), "Entity name inside inner_prefab doesn't have the collider component it should" + + # Verify hierarchy of entities: + # Outer_prefab + # |- Inner_prefab + # | |- TestEntity + assert entity.get_parent_id() == inner_instance.container_entity.id + assert inner_instance.container_entity.get_parent_id() == outer_instance.container_entity.id + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(PrefabComplexWorflow_CreatePrefabInsidePrefab) diff --git a/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabOfChildEntity.py b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabOfChildEntity.py new file mode 100644 index 0000000000..dec44d52be --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Prefab/tests/PrefabComplexWorflow_CreatePrefabOfChildEntity.py @@ -0,0 +1,52 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + +def PrefabComplexWorflow_CreatePrefabOfChildEntity(): + """ + Test description: + - Creates two entities, parent and child. Child entity has Parent entity as its parent. + - Creates a prefab of the child entity. + Test is successful if the new instanced prefab of the child has the parent entity id + """ + + CAR_PREFAB_FILE_NAME = 'car_prefab' + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.prefab_utils import Prefab + + import PrefabTestUtils as prefab_test_utils + + prefab_test_utils.open_base_tests_level() + + # Creates a new Entity at the root level + # Asserts if creation didn't succeed + parent_entity = EditorEntity.create_editor_entity_at((100.0, 100.0, 100.0)) + assert parent_entity.id.IsValid(), "Couldn't create parent entity" + + child_entity = EditorEntity.create_editor_entity(parent_id=parent_entity.id) + assert child_entity.id.IsValid(), "Couldn't create child entity" + assert child_entity.get_world_translation().IsClose(parent_entity.get_world_translation()), f"Child entity position{child_entity.get_world_translation().ToString()}" \ + f" is not located at the same position as the parent{parent_entity.get_world_translation().ToString()}" + + # Asserts if prefab creation doesn't succeed + child_prefab, child_instance = Prefab.create_prefab([child_entity], CAR_PREFAB_FILE_NAME) + child_entity_on_child_instance = child_instance.get_direct_child_entities()[0] + assert child_instance.container_entity.get_parent_id().IsValid(), "Newly instanced entity has no parent" + assert child_instance.container_entity.get_parent_id() == parent_entity.id, "Newly instanced entity parent does not match the expected parent" + assert child_instance.container_entity.get_world_translation().IsClose(parent_entity.get_world_translation()), "Newly instanced entity position is not located at the same position as the parent" + # Move the parent position, it should update the child position + parent_entity.set_world_translation((200.0, 200.0, 200.0)) + child_instance_translation = child_instance.container_entity.get_world_translation() + assert child_instance_translation.IsClose(azlmbr.math.Vector3(200.0, 200.0, 200.0)), f"Instance position position{child_instance_translation.ToString()} didn't get updated" \ + f" to the same position as the parent{parent_entity.get_world_translation().ToString()}" + child_translation = child_entity_on_child_instance.get_world_translation() + assert child_translation.IsClose(azlmbr.math.Vector3(200.0, 200.0, 200.0)), f"Entity position{child_translation.ToString()} of the instance didn't get updated" \ + f" to the same position as the parent{parent_entity.get_world_translation().ToString()}" + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(PrefabComplexWorflow_CreatePrefabOfChildEntity) diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index e4138da932..0277abe60d 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -4137,7 +4137,15 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[]) AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware); Editor::EditorQtApplication* app = Editor::EditorQtApplication::newInstance(argc, argv); - if (app->arguments().contains("-autotest_mode")) + QStringList qArgs = app->arguments(); + const bool is_automated_test = AZStd::any_of(qArgs.begin(), qArgs.end(), + [](const QString& elem) + { + return elem.endsWith("autotest_mode") || elem.endsWith("runpythontest"); + } + ); + + if (is_automated_test) { // Nullroute all stdout to null for automated tests, this way we make sure // that the test result output is not polluted with unrelated output data. diff --git a/Code/Framework/AzCore/AzCore/Component/TransformBus.h b/Code/Framework/AzCore/AzCore/Component/TransformBus.h index 1af77da51b..f8e30147da 100644 --- a/Code/Framework/AzCore/AzCore/Component/TransformBus.h +++ b/Code/Framework/AzCore/AzCore/Component/TransformBus.h @@ -168,6 +168,11 @@ namespace AZ //! Rotation modifiers //! @{ + //! Set the world rotation matrix using the composition of rotations around + //! the principle axes in the order of z-axis first and y-axis and then x-axis. + //! @param eulerRadianAngles A Vector3 denoting radian angles of the rotations around each principle axis. + virtual void SetWorldRotation([[maybe_unused]] const AZ::Vector3& eulerAnglesRadian) {} + //! Sets the entity's rotation in the world in quaternion notation. //! The origin of the axes is the entity's position in world space. //! @param quaternion A quaternion that represents the rotation to use for the entity. diff --git a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp index 68c4b00243..bc109b73c2 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp @@ -323,6 +323,13 @@ namespace AzFramework return localZ; } + void TransformComponent::SetWorldRotation(const AZ::Vector3& eulerAnglesRadian) + { + AZ::Transform newWorldTransform = m_worldTM; + newWorldTransform.SetRotation(AZ::Quaternion::CreateFromEulerAnglesRadians(eulerAnglesRadian)); + SetWorldTM(newWorldTransform); + } + void TransformComponent::SetWorldRotationQuaternion(const AZ::Quaternion& quaternion) { AZ::Transform newWorldTransform = m_worldTM; diff --git a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h index ac282b4d49..2375f28ed1 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.h @@ -108,6 +108,7 @@ namespace AzFramework float GetLocalZ() override; // Rotation modifiers + void SetWorldRotation(const AZ::Vector3& eulerAnglesRadian) override; void SetWorldRotationQuaternion(const AZ::Quaternion& quaternion) override; AZ::Vector3 GetWorldRotation() override; From 22820b6a90ec11063b1308888972698d260b3c59 Mon Sep 17 00:00:00 2001 From: Mike Chang Date: Fri, 29 Oct 2021 15:13:20 -0700 Subject: [PATCH 111/120] Update snapshots list to stablization/2110 Signed-off-by: Mike Chang --- scripts/build/Jenkins/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 35f6f9a81c..04e249255f 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -16,7 +16,7 @@ EMPTY_JSON = readJSON text: '{}' ENGINE_REPOSITORY_NAME = 'o3de' // Branches with build snapshots -BUILD_SNAPSHOTS = ['development', 'stabilization/2106'] +BUILD_SNAPSHOTS = ['development', 'stabilization/2110'] // Build snapshots with empty snapshot (for use with 'SNAPSHOT' pipeline paramater) BUILD_SNAPSHOTS_WITH_EMPTY = BUILD_SNAPSHOTS + '' From 1ec34f6123e029cd2a27c3c8003e03db0ac52145 Mon Sep 17 00:00:00 2001 From: Shirang Jia Date: Fri, 29 Oct 2021 21:11:53 -0700 Subject: [PATCH 112/120] Fix typo when calling upload_to_s3.py (#5139) Signed-off-by: shiranj --- scripts/build/Jenkins/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 35f6f9a81c..e00530d0b7 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -450,7 +450,7 @@ def UploadAPLogs(Map options, String branchName, String jobName, String workspac def command = "${pythonPath} -u ${s3UploadScriptPath} --base_dir ${apLogsPath} " + "--file_regex \".*\" --bucket ${env.AP_LOGS_S3_BUCKET} " + "--search_subdirectories True --key_prefix ${env.JENKINS_JOB_NAME}/${branchName}/${env.BUILD_NUMBER}/${jobName} " + - "--extra-args {\"ACL\": \"bucket-owner-full-control\"}" + "--extra_args {\"ACL\": \"bucket-owner-full-control\"}" palSh(command, "Uploading AP logs for job ${jobName} for branch ${branchName}", false) } } From 8c0dbe4b33ed958fad24835fb34c81efaae65335 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Sat, 30 Oct 2021 12:39:40 -0700 Subject: [PATCH 113/120] missed escaping these variables and breaks runtime dependencines in the install layout (#5149) Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/Platform/Common/Install_common.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index df19ad0e78..803edb7b1d 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -528,7 +528,7 @@ function(ly_setup_runtime_dependencies) ly_install(CODE "function(ly_copy source_file target_directory) cmake_path(GET source_file FILENAME file_name) - if(NOT EXISTS ${target_directory}/${file_name}) + if(NOT EXISTS \${target_directory}/\${file_name}) file(COPY \"\${source_file}\" DESTINATION \"\${target_directory}\" FILE_PERMISSIONS ${LY_COPY_PERMISSIONS}) endif() endfunction()" From db01d8dddae6bc7aebcb9b8a091dbea557f9619d Mon Sep 17 00:00:00 2001 From: Santi Paprika <44426596+santipaprika@users.noreply.github.com> Date: Mon, 1 Nov 2021 14:47:45 +0000 Subject: [PATCH 114/120] Fix bug timestamp view 'Once per Second' option (#5080) Signed-off-by: Santi Paprika --- Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.h | 2 +- Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.h index 1bed2b5715..7413390357 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.h +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.h @@ -249,7 +249,7 @@ namespace AZ // Controls how often the timestamp data is refreshed RefreshType m_refreshType = RefreshType::Realtime; - AZStd::sys_time_t m_lastUpdateTimeMicroSecond; + AZStd::sys_time_t m_lastUpdateTimeMicroSecond = 0; }; diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl index 80dcace2df..405848aa35 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl @@ -651,7 +651,7 @@ namespace AZ if (m_refreshType == RefreshType::OncePerSecond) { auto now = AZStd::GetTimeNowMicroSecond(); - if (m_lastUpdateTimeMicroSecond == 0 || now - m_lastUpdateTimeMicroSecond > 1000000) + if (now - m_lastUpdateTimeMicroSecond > 1000000) { needEnable = true; m_lastUpdateTimeMicroSecond = now; From 2dff26ddb56e5073bafebf451cbe2de1be336cd7 Mon Sep 17 00:00:00 2001 From: Artur K <96597+nemerle@users.noreply.github.com> Date: Mon, 1 Nov 2021 15:51:15 +0100 Subject: [PATCH 115/120] Fix asset type retrieval in AssetCatalogModel::GetAssetType (#4995) * Fix asset type retrieval in AssetCatalogModel::GetAssetType Previous logic would visit the next entry in m_extensionToAssetType map, if the previous entry had multiple types was only exiting the inner loop. The main change is that now the first found matching asset type is returned. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> * Apply reviewer's suggestions + reduce allocations. Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com> --- .../UI/AssetCatalogModel.cpp | 64 +++++++++---------- .../UI/AssetCatalogModel.h | 2 +- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.cpp index d0091f968e..df8d6db4a8 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.cpp +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.cpp @@ -197,48 +197,46 @@ AssetCatalogModel::~AssetCatalogModel() AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); } -AZ::Data::AssetType AssetCatalogModel::GetAssetType(QString filename) const +AZ::Data::AssetType AssetCatalogModel::GetAssetType(const QString &filename) const { - AZ::Data::AssetType returnType = AZ::Uuid::CreateNull(); // Compare file extensions with the map created from the asset database. int dotIndex = filename.lastIndexOf('.'); - if (dotIndex >= 0) + if (dotIndex < 0) { - QString extension = filename.mid(dotIndex); - for (auto pair : m_extensionToAssetType) - { - QString qExtensions = pair.first.c_str(); - if (qExtensions.indexOf(extension) >= 0) - { - if (pair.second.size() > 1) - { - // There are multiple types with this extension. Check each handler to see if they can handle this data type. - AZStd::string azFilename = filename.toStdString().c_str(); - EBUS_EVENT(AzFramework::ApplicationRequests::Bus, MakePathAssetRootRelative, azFilename); - AZ::Data::AssetId assetId; - EBUS_EVENT_RESULT(assetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, azFilename.c_str(), AZ::Data::s_invalidAssetType, false); + return AZ::Uuid::CreateNull(); + } - for (AZ::Uuid type : pair.second) - { - const AZ::Data::AssetHandler* handler = AZ::Data::AssetManager::Instance().GetHandler(type); - if (handler && handler->CanHandleAsset(assetId)) - { - returnType = type; - break; - } - } - } - else - { - returnType = pair.second[0]; - break; - } + QStringRef extension = filename.midRef(dotIndex); + for (const auto& pair : m_extensionToAssetType) + { + QString qExtensions = pair.first.c_str(); + if (qExtensions.indexOf(extension) < 0 || pair.second.empty()) + { + continue; + } + if (pair.second.size() == 1) + { + return pair.second[0]; + } + + // There are multiple types with this extension. Search for a handler that can handle this data type. + AZStd::string azFilename = filename.toStdString().c_str(); + EBUS_EVENT(AzFramework::ApplicationRequests::Bus, MakePathAssetRootRelative, azFilename); + AZ::Data::AssetId assetId; + EBUS_EVENT_RESULT(assetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, azFilename.c_str(), AZ::Data::s_invalidAssetType, false); + + for (const AZ::Uuid& type : pair.second) + { + const AZ::Data::AssetHandler* handler = AZ::Data::AssetManager::Instance().GetHandler(type); + if (handler && handler->CanHandleAsset(assetId)) + { + return type; } } } - return returnType; + return AZ::Uuid::CreateNull(); } QStandardItem* AssetCatalogModel::GetPath(QString& path, bool createIfNeeded, QStandardItem* parent) @@ -419,7 +417,7 @@ AssetCatalogEntry* AssetCatalogModel::AddAsset(QString assetPath, AZ::Data::Asse // icons' memory being reclaimed and crashing the Editor. QSize size = fileIcon.actualSize(QSize(16, 16)); QIcon deepCopy = fileIcon.pixmap(size).copy(0, 0, size.width(), size.height()); - + if (!fileIcon.isNull()) { m_assetTypeToIcon[assetType] = deepCopy; diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.h b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.h index c9143bb259..1dffa81d67 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.h +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/UI/AssetCatalogModel.h @@ -110,7 +110,7 @@ protected: void SetFilterRegExp(const AZStd::string& filterType, const QRegExp& regExp); void ClearFilterRegExp(const AZStd::string& filterType = AZStd::string()); - AZ::Data::AssetType GetAssetType(QString filename) const; + AZ::Data::AssetType GetAssetType(const QString &filename) const; QStandardItem* GetPath(QString& path, bool createIfNeeded, QStandardItem* parent = nullptr); void ApplyFilter(QStandardItem* parent); From 43563060bc44689f4bee16aad315d276eee1bb95 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Mon, 1 Nov 2021 08:44:22 -0700 Subject: [PATCH 116/120] Making trait variable consistent and fixing warning (#5118) Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- Gems/PhysX/Code/CMakeLists.txt | 4 ++-- Gems/PhysX/Code/Source/Utils.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gems/PhysX/Code/CMakeLists.txt b/Gems/PhysX/Code/CMakeLists.txt index c59db45aa7..bb3d7c08a2 100644 --- a/Gems/PhysX/Code/CMakeLists.txt +++ b/Gems/PhysX/Code/CMakeLists.txt @@ -11,7 +11,7 @@ add_subdirectory(NumericalMethods) ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME}) include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) # for PAL_TRAIT_PHYSX_SUPPORTED -set(PHYSX_ENABLE_RUNNING_BENCHMARKS OFF CACHE BOOL "Adds a target to allow running of the physx benchmarks.") +set(LY_PHYSX_ENABLE_RUNNING_BENCHMARKS OFF CACHE BOOL "Adds a target to allow running of the physx benchmarks.") if(PAL_TRAIT_PHYSX_SUPPORTED) set(physx_dependency 3rdParty::PhysX) @@ -197,7 +197,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) # Only add the physx benchmarks if this flag is set. The benchmark code is still built, as it is part of the PhysX.Tests project. # Currently jenkins has a 1500sec(25min) timeout, our benchmarks can sometimes take over 1500sec and cause a build failure for timeout. # Jenkins currently doesn't upload the results of the benchmarks, so this is ok. - if(PHYSX_ENABLE_RUNNING_BENCHMARKS) + if(LY_PHYSX_ENABLE_RUNNING_BENCHMARKS) ly_add_googlebenchmark( NAME Gem::PhysX.Benchmarks TARGET Gem::PhysX.Tests diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp index 3e77ef257a..fecea57d9c 100644 --- a/Gems/PhysX/Code/Source/Utils.cpp +++ b/Gems/PhysX/Code/Source/Utils.cpp @@ -102,7 +102,7 @@ namespace PhysX const float scaleFactor = (maxHeightBounds <= minHeightBounds) ? 1.0f : AZStd::numeric_limits::max() / halfBounds; const float heightScale{ 1.0f / scaleFactor }; - [[maybe_unused]] const uint8_t physxMaximumMaterialIndex = 0x7f; + [[maybe_unused]] constexpr uint8_t physxMaximumMaterialIndex = 0x7f; // Delete the cached heightfield object if it is there, and create a new one and save in the shape configuration heightfieldConfig.SetCachedNativeHeightfield(nullptr); From 8e420dad3d4efdc2c20fe4b893b2a2a3cd357567 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Mon, 1 Nov 2021 08:46:26 -0700 Subject: [PATCH 117/120] Removes some usage of DEDICATED_SERVER (#5119) Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- Code/Legacy/CrySystem/CrySystem_precompiled.h | 7 ------- .../World/UiCanvasAssetRefComponent.cpp | 19 ++++++++++--------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/Code/Legacy/CrySystem/CrySystem_precompiled.h b/Code/Legacy/CrySystem/CrySystem_precompiled.h index faa924931f..3ececa6514 100644 --- a/Code/Legacy/CrySystem/CrySystem_precompiled.h +++ b/Code/Legacy/CrySystem/CrySystem_precompiled.h @@ -60,13 +60,6 @@ #include -#if defined(WIN32) || defined(WIN64) || defined(APPLE) || defined(LINUX) -#if defined(DEDICATED_SERVER) -// enable/disable map load slicing functionality from the build -#define MAP_LOADING_SLICING -#endif -#endif - #ifdef WIN32 #include #include diff --git a/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp b/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp index d2a307ecaf..59d764f297 100644 --- a/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp +++ b/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp @@ -239,15 +239,16 @@ void UiCanvasAssetRefComponent::Activate() //////////////////////////////////////////////////////////////////////////////////////////////////// void UiCanvasAssetRefComponent::Deactivate() { -#if !defined(DEDICATED_SERVER) - if (m_canvasEntityId.IsValid()) + if (!gEnv->IsDedicated()) { - gEnv->pLyShine->ReleaseCanvasDeferred(m_canvasEntityId); - m_canvasEntityId.SetInvalid(); - } + if (m_canvasEntityId.IsValid()) + { + gEnv->pLyShine->ReleaseCanvasDeferred(m_canvasEntityId); + m_canvasEntityId.SetInvalid(); + } - UiCanvasAssetRefBus::Handler::BusDisconnect(); - UiCanvasRefBus::Handler::BusDisconnect(); - UiCanvasManagerNotificationBus::Handler::BusDisconnect(); -#endif + UiCanvasAssetRefBus::Handler::BusDisconnect(); + UiCanvasRefBus::Handler::BusDisconnect(); + UiCanvasManagerNotificationBus::Handler::BusDisconnect(); + } } From 2cc4f322b7313feba612572f5fa3297ef270c711 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Mon, 1 Nov 2021 11:06:04 -0700 Subject: [PATCH 118/120] Skips signing when there is no upload URL (#5120) * Skips signing when there is no upload URL so we can run the scripts locally Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * Fixes prebuild command and improves post build command to not depend on psiexec Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- .../Platform/Windows/PackagingPostBuild.cmake | 78 +++++++++++-------- .../Platform/Windows/PackagingPreBuild.cmake | 38 ++++++--- 2 files changed, 74 insertions(+), 42 deletions(-) diff --git a/cmake/Platform/Windows/PackagingPostBuild.cmake b/cmake/Platform/Windows/PackagingPostBuild.cmake index 377a9fb221..ac457bea87 100644 --- a/cmake/Platform/Windows/PackagingPostBuild.cmake +++ b/cmake/Platform/Windows/PackagingPostBuild.cmake @@ -32,9 +32,6 @@ set(_addtional_defines -dCPACK_RESOURCE_PATH=${CPACK_SOURCE_DIR}/Platform/Windows/Packaging ) -file(REAL_PATH "${CPACK_SOURCE_DIR}/.." _root_path) -file(TO_NATIVE_PATH "${_root_path}/scripts/signer/Platform/Windows/signer.ps1" _sign_script) - if(CPACK_LICENSE_URL) list(APPEND _addtional_defines -dCPACK_LICENSE_URL=${CPACK_LICENSE_URL}) endif() @@ -58,28 +55,41 @@ set(_light_command -o "${_bootstrap_output_file}" ) -set(_signing_command - psexec.exe - -accepteula - -nobanner - -s - powershell.exe - -NoLogo - -ExecutionPolicy Bypass - -File ${_sign_script} -) +if(CPACK_UPLOAD_URL) # Skip signing if we are not uploading the package + file(REAL_PATH "${CPACK_SOURCE_DIR}/.." _root_path) + file(TO_NATIVE_PATH "${_root_path}/scripts/signer/Platform/Windows/signer.ps1" _sign_script) -message(STATUS "Signing package files in ${_cpack_wix_out_dir}") -execute_process( - COMMAND ${_signing_command} -packagePath ${_cpack_wix_out_dir} - RESULT_VARIABLE _signing_result - ERROR_VARIABLE _signing_errors - OUTPUT_VARIABLE _signing_output - ECHO_OUTPUT_VARIABLE -) + unset(_signing_command) + find_program(_psiexec_path psexec.exe) + if(_psiexec_path) + list(APPEND _signing_command + ${_psiexec_path} + -accepteula + -nobanner + -s + ) + endif() -if(NOT ${_signing_result} EQUAL 0) - message(FATAL_ERROR "An error occurred during signing package files. ${_signing_errors}") + find_program(_powershell_path powershell.exe REQUIRED) + list(APPEND _signing_command + ${_powershell_path} + -NoLogo + -ExecutionPolicy Bypass + -File ${_sign_script} + ) + + message(STATUS "Signing package files in ${_cpack_wix_out_dir}") + execute_process( + COMMAND ${_signing_command} -packagePath ${_cpack_wix_out_dir} + RESULT_VARIABLE _signing_result + ERROR_VARIABLE _signing_errors + OUTPUT_VARIABLE _signing_output + ECHO_OUTPUT_VARIABLE + ) + + if(NOT ${_signing_result} EQUAL 0) + message(FATAL_ERROR "An error occurred during signing package files. ${_signing_errors}") + endif() endif() message(STATUS "Creating Bootstrap Installer...") @@ -107,17 +117,19 @@ file(COPY ${_bootstrap_output_file} message(STATUS "Bootstrap installer generated to ${CPACK_PACKAGE_DIRECTORY}/${_bootstrap_filename}") -message(STATUS "Signing bootstrap installer in ${CPACK_PACKAGE_DIRECTORY}") -execute_process( - COMMAND ${_signing_command} -bootstrapPath ${CPACK_PACKAGE_DIRECTORY}/${_bootstrap_filename} - RESULT_VARIABLE _signing_result - ERROR_VARIABLE _signing_errors - OUTPUT_VARIABLE _signing_output - ECHO_OUTPUT_VARIABLE -) +if(CPACK_UPLOAD_URL) # Skip signing if we are not uploading the package + message(STATUS "Signing bootstrap installer in ${CPACK_PACKAGE_DIRECTORY}") + execute_process( + COMMAND ${_signing_command} -bootstrapPath ${CPACK_PACKAGE_DIRECTORY}/${_bootstrap_filename} + RESULT_VARIABLE _signing_result + ERROR_VARIABLE _signing_errors + OUTPUT_VARIABLE _signing_output + ECHO_OUTPUT_VARIABLE + ) -if(NOT ${_signing_result} EQUAL 0) - message(FATAL_ERROR "An error occurred during signing bootstrap installer. ${_signing_errors}") + if(NOT ${_signing_result} EQUAL 0) + message(FATAL_ERROR "An error occurred during signing bootstrap installer. ${_signing_errors}") + endif() endif() # use the internal default path if somehow not specified from cpack_configure_downloads diff --git a/cmake/Platform/Windows/PackagingPreBuild.cmake b/cmake/Platform/Windows/PackagingPreBuild.cmake index d3924c7a02..7f2eedf352 100644 --- a/cmake/Platform/Windows/PackagingPreBuild.cmake +++ b/cmake/Platform/Windows/PackagingPreBuild.cmake @@ -6,21 +6,41 @@ # # +if(NOT CPACK_UPLOAD_URL) # Skip signing if we are not uploading the package + return() +endif() + file(REAL_PATH "${CPACK_SOURCE_DIR}/.." _root_path) set(_cpack_wix_out_dir ${CPACK_TOPLEVEL_DIRECTORY}) file(TO_NATIVE_PATH "${_root_path}/scripts/signer/Platform/Windows/signer.ps1" _sign_script) -set(_signing_command - psexec.exe - -accepteula - -nobanner - -s - powershell.exe +unset(_signing_command) +find_program(_psiexec_path psexec.exe) +if(_psiexec_path) + list(APPEND _signing_command + ${_psiexec_path} + -accepteula + -nobanner + -s + ) +endif() + +find_program(_powershell_path powershell.exe REQUIRED) +list(APPEND _signing_command + ${_powershell_path} -NoLogo - -ExecutionPolicy Bypass + -ExecutionPolicy Bypass -File ${_sign_script} ) +# This requires to have a valid local certificate. In continuous integration, these certificates are stored +# in the machine directly. +# You can generate a test certificate to be able to run this in a PowerShell elevated promp with: +# New-SelfSignedCertificate -DnsName foo.o3de.com -Type CodeSigning -CertStoreLocation Cert:\CurrentUser\My +# Export-Certificate -Cert (Get-ChildItem Cert:\CurrentUser\My\) -Filepath "c:\selfsigned.crt" +# Import-Certificate -FilePath "c:\selfsigned.crt" -Cert Cert:\CurrentUser\TrustedPublisher +# Import-Certificate -FilePath "c:\selfsigned.crt" -Cert Cert:\CurrentUser\Root + message(STATUS "Signing executable files in ${_cpack_wix_out_dir}") execute_process( COMMAND ${_signing_command} -exePath ${_cpack_wix_out_dir} @@ -32,6 +52,6 @@ execute_process( if(NOT ${_signing_result} EQUAL 0) message(FATAL_ERROR "An error occurred during signing executable files. ${_signing_errors}") +else() + message(STATUS "Signing exes complete!") endif() - -message(STATUS "Signing exes complete!") From edf5e7a242fd2b3b9853f2d24847ab30465c342f Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Mon, 1 Nov 2021 11:06:37 -0700 Subject: [PATCH 119/120] Cleanup of validation script exclusions (#5134) * Validation lists cleanup Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * more cleanup Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- .../commit_validation/commit_validation.py | 12 ++++-------- .../commit_validation/pal_allowedlist.txt | 3 --- .../validator_data_LEGAL_REVIEW_REQUIRED.py | 14 ++------------ 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/scripts/commit_validation/commit_validation/commit_validation.py b/scripts/commit_validation/commit_validation/commit_validation.py index b9d992fc9c..513244a735 100755 --- a/scripts/commit_validation/commit_validation/commit_validation.py +++ b/scripts/commit_validation/commit_validation/commit_validation.py @@ -173,16 +173,12 @@ EXCLUDED_VALIDATION_PATTERNS = [ '*/3rdParty/*', '*/__pycache__/*', '*/External/*', - 'build', - 'Cache', - '*/Code/Framework/AzCore/azgnmx/azgnmx/*', - 'Code/Tools/CryFXC', - 'Code/Tools/HLSLCrossCompiler', - 'Code/Tools/HLSLCrossCompilerMETAL', - 'Docs', + 'build', # build artifacts + '*/Cache/*', # Asset processing artifacts + 'install', # install layout artifacts 'python/runtime', + 'restricted/*/Code/Framework/AzCore/azgnmx/azgnmx/*', 'restricted/*/Tools/*RemoteControl', - 'Tools/3dsmax', '*/user/Cache/*', '*/user/log/*', ] diff --git a/scripts/commit_validation/commit_validation/pal_allowedlist.txt b/scripts/commit_validation/commit_validation/pal_allowedlist.txt index 4c90bcb6b4..6fd5aab62e 100644 --- a/scripts/commit_validation/commit_validation/pal_allowedlist.txt +++ b/scripts/commit_validation/commit_validation/pal_allowedlist.txt @@ -23,7 +23,6 @@ */Code/Framework/AzCore/AzCore/std/parallel/binary_semaphore.h */Code/Framework/AzCore/AzCore/std/parallel/semaphore.h */Code/Framework/AzCore/Platform/Android/AzCore/AzCore_Traits_Android.h -*/Code/Framework/AzCore/Platform/AppleTV/AzCore/AzCore_Traits_AppleTV.h */Code/Framework/AzCore/Platform/iOS/AzCore/AzCore_Traits_iOS.h */Code/Framework/AzCore/Platform/Jasper/AzCore/AzCore_Traits_Jasper.h */Code/Framework/AzCore/Platform/Linux/AzCore/AzCore_Traits_Linux.h @@ -49,7 +48,6 @@ */Code/Tools/* */Gems/*/3rdParty/* */Gems/*/External/* -*/Gems/CryLegacy* */Gems/EMotionFX/Code/EMotionFX/Rendering/OpenGL2/Source/GLInclude.h */Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp */Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/PluginManager.cpp @@ -61,4 +59,3 @@ */Gems/SaveData/Code/Tests/SaveDataTest.cpp */Gems/WhiteBox/Code/Source/Rendering/Legacy/WhiteBoxLegacyRenderMesh.cpp */restricted/*/Code/Framework/AzCore/AzCore/AzCore_Traits_*.h -*/Tools/CryDeprecation/precompile_check_defines.h diff --git a/scripts/scrubbing/validator_data_LEGAL_REVIEW_REQUIRED.py b/scripts/scrubbing/validator_data_LEGAL_REVIEW_REQUIRED.py index 666e04a2b0..0c008cbe65 100755 --- a/scripts/scrubbing/validator_data_LEGAL_REVIEW_REQUIRED.py +++ b/scripts/scrubbing/validator_data_LEGAL_REVIEW_REQUIRED.py @@ -100,14 +100,10 @@ def get_prohibited_platforms_for_package(package): def get_bypassed_directories(is_all): # Temporarily exempt folders to not fail validation while people is fixing validation errors, they will be removed once the errors are fixed. temp_bypass_directories = [ - 'commit_validation', - 'LauncherTestTools', - 'AutomatedTesting', - 'Atom' + 'commit_validation' ] bypassed_directories = [ - 'python', - 'AWSPythonSDK' + 'python' ] if not is_all: bypassed_directories.extend([ @@ -116,13 +112,7 @@ def get_bypassed_directories(is_all): 'Cache', 'logs', 'AssetProcessorTemp', - 'JenkinsScripts', - 'BuildLambdaFunctions', - 'layouts', - '.idea', 'user/log', - 'DirectXShaderCompiler', - 'v-hacd', 'External' ]) bypassed_directories.extend(temp_bypass_directories) From c87670bbfb1d294ff9856781d9da39d897adce42 Mon Sep 17 00:00:00 2001 From: Tommy Walton Date: Mon, 1 Nov 2021 11:38:45 -0700 Subject: [PATCH 120/120] Fix innocuous release build error in RPIUtils LoadStreamingTexture (#5021) Signed-off-by: Tommy Walton --- Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp index e70885daa1..fda8f967da 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp @@ -119,7 +119,12 @@ namespace AZ AzFramework::AssetSystem::AssetStatus status = AzFramework::AssetSystem::AssetStatus_Unknown; AzFramework::AssetSystemRequestBus::BroadcastResult( status, &AzFramework::AssetSystemRequestBus::Events::CompileAssetSync, path); - AZ_Error("RPIUtils", status == AzFramework::AssetSystem::AssetStatus_Compiled, "Could not compile image at '%s'", path.data()); + + // When running with no Asset Processor (for example in release), CompileAssetSync will return AssetStatus_Unknown. + AZ_Error( + "RPIUtils", + status == AzFramework::AssetSystem::AssetStatus_Compiled || status == AzFramework::AssetSystem::AssetStatus_Unknown, + "Could not compile image at '%s'", path.data()); Data::AssetId streamingImageAssetId; Data::AssetCatalogRequestBus::BroadcastResult(