diff --git a/AutomatedTesting/CMakeLists.txt b/AutomatedTesting/CMakeLists.txt index e239ba7674..9a870b1279 100644 --- a/AutomatedTesting/CMakeLists.txt +++ b/AutomatedTesting/CMakeLists.txt @@ -10,7 +10,7 @@ # if(NOT PROJECT_NAME) - cmake_minimum_required(VERSION 3.19) + cmake_minimum_required(VERSION 3.20) project(AutomatedTesting LANGUAGES C CXX VERSION 1.0.0.0 diff --git a/AutomatedTesting/Gem/PythonTests/smoke/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/smoke/CMakeLists.txt index a3b6e36250..18ffa1944d 100644 --- a/AutomatedTesting/Gem/PythonTests/smoke/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/smoke/CMakeLists.txt @@ -40,6 +40,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) AutomatedTesting.GameLauncher AutomatedTesting.Assets COMPONENT - Smoke + Sandbox ) endif() \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py b/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py index e6b072ba58..985740307f 100644 --- a/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py +++ b/AutomatedTesting/Gem/PythonTests/smoke/test_Editor_NewExistingLevels_Works.py @@ -15,7 +15,7 @@ from automatedtesting_shared.base import TestAutomationBase import ly_test_tools.environment.file_system as file_system -@pytest.mark.SUITE_sandbox +@pytest.mark.SUITE_smoke @pytest.mark.parametrize("launcher_platform", ["windows_editor"]) @pytest.mark.parametrize("project", ["AutomatedTesting"]) @pytest.mark.parametrize("level", ["temp_level"]) diff --git a/AutomatedTesting/Registry/C3510644_Collider_CollisionGroups.setreg_override b/AutomatedTesting/Registry/C3510644_Collider_CollisionGroups.setreg_override index 696a0a74da..9fa5e26768 100644 --- a/AutomatedTesting/Registry/C3510644_Collider_CollisionGroups.setreg_override +++ b/AutomatedTesting/Registry/C3510644_Collider_CollisionGroups.setreg_override @@ -119,9 +119,6 @@ ] } }, - "DefaultMaterial": { - "SurfaceType": "Default_1" - }, "MaterialLibrary": { "assetId": { "guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}" diff --git a/AutomatedTesting/Registry/C4044459_Material_DynamicFriction.setreg_override b/AutomatedTesting/Registry/C4044459_Material_DynamicFriction.setreg_override index c53b04e5c2..88a7b5c309 100644 --- a/AutomatedTesting/Registry/C4044459_Material_DynamicFriction.setreg_override +++ b/AutomatedTesting/Registry/C4044459_Material_DynamicFriction.setreg_override @@ -101,9 +101,6 @@ ] } }, - "DefaultMaterial": { - "SurfaceType": "Default_1" - }, "MaterialLibrary": { "assetId": { "guid": "{6AA79EE4-7EC3-5717-87AE-EDD7D886FD7F}" diff --git a/AutomatedTesting/Registry/C4976227_Collider_NewGroup.setreg_override b/AutomatedTesting/Registry/C4976227_Collider_NewGroup.setreg_override index 5e98e08ede..afbe6a9d38 100644 --- a/AutomatedTesting/Registry/C4976227_Collider_NewGroup.setreg_override +++ b/AutomatedTesting/Registry/C4976227_Collider_NewGroup.setreg_override @@ -107,9 +107,6 @@ ] } }, - "DefaultMaterial": { - "SurfaceType": "Default_1" - }, "MaterialLibrary": { "assetId": { "guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}" diff --git a/AutomatedTesting/Registry/C4976244_Collider_SameGroupSameLayerCollision.setreg_override b/AutomatedTesting/Registry/C4976244_Collider_SameGroupSameLayerCollision.setreg_override index 696a0a74da..9fa5e26768 100644 --- a/AutomatedTesting/Registry/C4976244_Collider_SameGroupSameLayerCollision.setreg_override +++ b/AutomatedTesting/Registry/C4976244_Collider_SameGroupSameLayerCollision.setreg_override @@ -119,9 +119,6 @@ ] } }, - "DefaultMaterial": { - "SurfaceType": "Default_1" - }, "MaterialLibrary": { "assetId": { "guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}" diff --git a/AutomatedTesting/Registry/C4976245_PhysXCollider_CollisionLayerTest.setreg_override b/AutomatedTesting/Registry/C4976245_PhysXCollider_CollisionLayerTest.setreg_override index 696a0a74da..9fa5e26768 100644 --- a/AutomatedTesting/Registry/C4976245_PhysXCollider_CollisionLayerTest.setreg_override +++ b/AutomatedTesting/Registry/C4976245_PhysXCollider_CollisionLayerTest.setreg_override @@ -119,9 +119,6 @@ ] } }, - "DefaultMaterial": { - "SurfaceType": "Default_1" - }, "MaterialLibrary": { "assetId": { "guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}" diff --git a/AutomatedTesting/Registry/C4982593_PhysXCollider_CollisionLayer.setreg_override b/AutomatedTesting/Registry/C4982593_PhysXCollider_CollisionLayer.setreg_override index 696a0a74da..9fa5e26768 100644 --- a/AutomatedTesting/Registry/C4982593_PhysXCollider_CollisionLayer.setreg_override +++ b/AutomatedTesting/Registry/C4982593_PhysXCollider_CollisionLayer.setreg_override @@ -119,9 +119,6 @@ ] } }, - "DefaultMaterial": { - "SurfaceType": "Default_1" - }, "MaterialLibrary": { "assetId": { "guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}" diff --git a/AutomatedTesting/Registry/physxsystemconfiguration.setreg b/AutomatedTesting/Registry/physxsystemconfiguration.setreg index 30e9dced44..02f65b685b 100644 --- a/AutomatedTesting/Registry/physxsystemconfiguration.setreg +++ b/AutomatedTesting/Registry/physxsystemconfiguration.setreg @@ -101,9 +101,6 @@ ] } }, - "DefaultMaterial": { - "SurfaceType": "Default_1" - }, "MaterialLibrary": { "assetId": { "guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}" diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ca9aeacb8..0585089325 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,17 +9,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# Cmake version 3.19 is the minimum version needed for all of Open 3D Engine's supported platforms -cmake_minimum_required(VERSION 3.19) - -# CMP0111 introduced in 3.19 has a bug that produces the policy to warn every time there is an -# INTERFACE IMPORTED library. We use this type of libraries for handling 3rdParty. The rest of -# the documentation states that INTERFACE IMPORTED libraries do not require to set locations, but -# the policy still warns about it. Issue: https://gitlab.kitware.com/cmake/cmake/-/issues/21470 -# The issue was fixed in 3.19.1 so we just disable the policy for 3.19 -if(CMAKE_VERSION VERSION_EQUAL 3.19) - cmake_policy(SET CMP0111 OLD) -endif() +# Cmake version 3.20 is the minimum version needed for all of Open 3D Engine's supported platforms +cmake_minimum_required(VERSION 3.20) include(cmake/LySet.cmake) include(cmake/Version.cmake) diff --git a/Code/CryEngine/CryCommon/Mocks/ICryPakMock.h b/Code/CryEngine/CryCommon/Mocks/ICryPakMock.h index 2d451793bc..072a798985 100644 --- a/Code/CryEngine/CryCommon/Mocks/ICryPakMock.h +++ b/Code/CryEngine/CryCommon/Mocks/ICryPakMock.h @@ -67,7 +67,7 @@ struct CryPakMock MOCK_METHOD1(PoolMalloc, void*(size_t size)); MOCK_METHOD1(PoolFree, void(void* p)); MOCK_METHOD3(PoolAllocMemoryBlock, AZStd::intrusive_ptr (size_t nSize, const char* sUsage, size_t nAlign)); - MOCK_METHOD3(FindFirst, AZ::IO::ArchiveFileIterator(AZStd::string_view pDir, uint32_t nFlags, bool bAllOwUseFileSystem)); + MOCK_METHOD2(FindFirst, AZ::IO::ArchiveFileIterator(AZStd::string_view pDir, AZ::IO::IArchive::EFileSearchType)); MOCK_METHOD1(FindNext, AZ::IO::ArchiveFileIterator(AZ::IO::ArchiveFileIterator handle)); MOCK_METHOD1(FindClose, bool(AZ::IO::ArchiveFileIterator)); MOCK_METHOD1(GetModificationTime, AZ::IO::IArchive::FileTime(AZ::IO::HandleType f)); diff --git a/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp b/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp index c36b9bcee7..08caeeeb70 100644 --- a/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp +++ b/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp @@ -306,8 +306,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder) AZStd::unordered_set pakList; - bool allowFileSystem = true; - AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(search.c_str(), 0, allowFileSystem); + AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(search.c_str(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskOnly); if (handle) { @@ -320,7 +319,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder) { if (AZ::StringFunc::Equal(handle.m_filename.data(), LevelPakName)) { - // level folder contain pak files like 'level.pak' + // level folder contain pak files like 'level.pak' // which we only want to load during level loading. continue; } @@ -351,7 +350,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder) PopulateLevels(search, folder, pPak, modFolder, false); // Load levels outside of the bundles to maintain backward compatibility. PopulateLevels(search, folder, pPak, modFolder, true); - + } void CLevelSystem::PopulateLevels( @@ -360,7 +359,7 @@ void CLevelSystem::PopulateLevels( { // allow this find first to actually touch the file system // (causes small overhead but with minimal amount of levels this should only be around 150ms on actual DVD Emu) - AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(searchPattern.c_str(), 0, fromFileSystemOnly); + AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(searchPattern.c_str(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskOnly); if (handle) { @@ -973,7 +972,7 @@ void CLevelSystem::UnloadLevel() m_lastLevelName.clear(); SAFE_RELEASE(m_pCurrentLevel); - + // Force Lua garbage collection (may no longer be needed now the legacy renderer has been removed). // Normally the GC step is triggered at the end of this method (by the ESYSTEM_EVENT_LEVEL_POST_UNLOAD event). EBUS_EVENT(AZ::ScriptSystemRequestBus, GarbageCollect); diff --git a/Code/Framework/AzAutoGen/CMakeLists.txt b/Code/Framework/AzAutoGen/CMakeLists.txt index 925e7aed29..4c79f268fe 100644 --- a/Code/Framework/AzAutoGen/CMakeLists.txt +++ b/Code/Framework/AzAutoGen/CMakeLists.txt @@ -9,8 +9,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -cmake_minimum_required(VERSION 3.0) - ly_add_target( NAME AzAutoGen HEADERONLY NAMESPACE AZ diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp index 6e3c8cec60..8fe1bf50e2 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp @@ -24,7 +24,12 @@ #include #include #include +#include #include +#include +#include +#include +#include #include #include #include @@ -33,12 +38,11 @@ #include #include #include +#include #include #include #include #include -#include -#include namespace AZ { @@ -98,6 +102,13 @@ namespace AZ jsonContext->Serializer() ->HandlesType(); + jsonContext->Serializer() + ->HandlesType(); + jsonContext->Serializer() + ->HandlesType(); + jsonContext->Serializer() + ->HandlesType(); + MathReflect(jsonContext); } else if (SerializeContext* serializeContext = azrtti_cast(reflectContext)) diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/RegistrationContext.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/RegistrationContext.cpp index 552b16a731..82db4df000 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/RegistrationContext.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/RegistrationContext.cpp @@ -33,34 +33,31 @@ namespace AZ , m_serializerIter(serializerMapIter) {} - JsonRegistrationContext::SerializerBuilder* JsonRegistrationContext::SerializerBuilder::HandlesTypeId(const Uuid& uuid) + JsonRegistrationContext::SerializerBuilder* JsonRegistrationContext::SerializerBuilder::HandlesTypeId( + const Uuid& uuid, bool overwriteExisting) { if (!m_context->IsRemovingReflection()) { auto serializer = m_serializerIter->second.get(); if (uuid.IsNull()) { - AZ_Error("Serialization", false, - "Could not register Json serializer %s. Its Uuid is null.", - serializer->RTTI_GetTypeName() - ); + AZ_Assert(false, "Could not register Json serializer %s. Its Uuid is null.", serializer->RTTI_GetTypeName()); return this; } - auto serializerIter = m_context->m_handledTypesMap.find(uuid); - - if (serializerIter == m_context->m_handledTypesMap.end()) + if (!overwriteExisting) { - m_context->m_handledTypesMap.emplace(uuid, serializer); - return this; + auto emplaceResult = m_context->m_handledTypesMap.try_emplace(uuid, serializer); + AZ_Assert( + emplaceResult.second, + "Couldn't register Json serializer %s. Another serializer (%s) has already been registered for the same Uuid (%s).", + serializer->RTTI_GetTypeName(), emplaceResult.first->second->RTTI_GetTypeName(), + uuid.ToString().c_str()); + } + else + { + m_context->m_handledTypesMap.insert_or_assign(uuid, serializer); } - - AZ_Error("Serialization", false, - "Couldn't register Json serializer %s. Another serializer (%s) has already been registered for the same Uuid (%s).", - serializer->RTTI_GetTypeName(), - serializerIter->second->RTTI_GetTypeName(), - serializerIter->first.ToString().c_str() - ); } else { diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/RegistrationContext.h b/Code/Framework/AzCore/AzCore/Serialization/Json/RegistrationContext.h index 89d69e223f..fa1575a916 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/RegistrationContext.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/RegistrationContext.h @@ -63,52 +63,50 @@ namespace AZ SerializerBuilder* operator->(); template - SerializerBuilder* HandlesType() + SerializerBuilder* HandlesType(bool overwriteExisting = false) { - return HandlesTypeId(azrtti_typeid()); + return HandlesTypeId(azrtti_typeid(), overwriteExisting); } template class T> - SerializerBuilder* HandlesType() + SerializerBuilder* HandlesType(bool overwriteExisting = false) { - return HandlesTypeId(azrtti_typeid()); + return HandlesTypeId(azrtti_typeid(), overwriteExisting); } template class T> - SerializerBuilder* HandlesType() + SerializerBuilder* HandlesType(bool overwriteExisting = false) { - return HandlesTypeId(azrtti_typeid()); + return HandlesTypeId(azrtti_typeid(), overwriteExisting); } template class T> - SerializerBuilder* HandlesType() + SerializerBuilder* HandlesType(bool overwriteExisting = false) { - return HandlesTypeId(azrtti_typeid()); + return HandlesTypeId(azrtti_typeid(), overwriteExisting); } template class T> - SerializerBuilder* HandlesType() + SerializerBuilder* HandlesType(bool overwriteExisting = false) { - return HandlesTypeId(azrtti_typeid()); + return HandlesTypeId(azrtti_typeid(), overwriteExisting); } template class T> - SerializerBuilder* HandlesType() + SerializerBuilder* HandlesType(bool overwriteExisting = false) { - return HandlesTypeId(azrtti_typeid()); + return HandlesTypeId(azrtti_typeid(), overwriteExisting); } template class T> - SerializerBuilder* HandlesType() + SerializerBuilder* HandlesType(bool overwriteExisting = false) { - return HandlesTypeId(azrtti_typeid()); + return HandlesTypeId(azrtti_typeid(), overwriteExisting); } protected: - struct Placeholder { AZ_TYPE_INFO(PlaceHolder, "{4425191C-F497-411A-A7C3-52928E720B0A}"); }; - SerializerBuilder(JsonRegistrationContext* context, SerializerMap::const_iterator serializerMapIter); - SerializerBuilder* HandlesTypeId(const AZ::Uuid& uuid); + SerializerBuilder* HandlesTypeId(const AZ::Uuid& uuid, bool overwriteExisting); JsonRegistrationContext* m_context = nullptr; SerializerMap::const_iterator m_serializerIter; diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/UnsupportedTypesSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/UnsupportedTypesSerializer.cpp new file mode 100644 index 0000000000..b03215dd75 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/UnsupportedTypesSerializer.cpp @@ -0,0 +1,56 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +namespace AZ +{ + AZ_CLASS_ALLOCATOR_IMPL(JsonUnsupportedTypesSerializer, SystemAllocator, 0); + AZ_CLASS_ALLOCATOR_IMPL(JsonAnySerializer, SystemAllocator, 0); + AZ_CLASS_ALLOCATOR_IMPL(JsonVariantSerializer, SystemAllocator, 0); + AZ_CLASS_ALLOCATOR_IMPL(JsonOptionalSerializer, SystemAllocator, 0); + + JsonSerializationResult::Result JsonUnsupportedTypesSerializer::Load(void*, const Uuid&, const rapidjson::Value&, + JsonDeserializerContext& context) + { + namespace JSR = JsonSerializationResult; + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Invalid, GetMessage()); + } + + JsonSerializationResult::Result JsonUnsupportedTypesSerializer::Store(rapidjson::Value&, const void*, const void*, const Uuid&, + JsonSerializerContext& context) + { + namespace JSR = JsonSerializationResult; + return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Invalid, GetMessage()); + } + + AZStd::string_view JsonAnySerializer::GetMessage() const + { + return "The Json Serialization doesn't support AZStd::any by design. The Json Serialization attempts to minimize the use of $type, " + "in particular the guid version, but no way has yet been found to use AZStd::any without explicitly and always requiring " + "one."; + } + + AZStd::string_view JsonVariantSerializer::GetMessage() const + { + return "The Json Serialization doesn't support AZStd::variant by design. The Json Serialization attempts to minimize the use of " + "$type, in particular the guid version. While combinations of AZStd::variant can be constructed that don't require a $type, " + "this cannot be guaranteed in general."; + } + + AZStd::string_view JsonOptionalSerializer::GetMessage() const + { + return "The Json Serialization doesn't support AZStd::optional by design. No JSON format has yet been found that wasn't deemed too " + "complex or overly verbose."; + } +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/UnsupportedTypesSerializer.h b/Code/Framework/AzCore/AzCore/Serialization/Json/UnsupportedTypesSerializer.h new file mode 100644 index 0000000000..882b479e5d --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/UnsupportedTypesSerializer.h @@ -0,0 +1,72 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include + +namespace AZ +{ + class JsonUnsupportedTypesSerializer : public BaseJsonSerializer + { + public: + AZ_RTTI(JsonUnsupportedTypesSerializer, "{AFCC76B9-1F28-429D-8B4E-020BFD95ADAC}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR_DECL; + + JsonSerializationResult::Result Load( + void* outputValue, + const Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, + JsonDeserializerContext& context) override; + JsonSerializationResult::Result Store( + rapidjson::Value& outputValue, + const void* inputValue, + const void* defaultValue, + const Uuid& valueTypeId, + JsonSerializerContext& context) override; + + protected: + virtual AZStd::string_view GetMessage() const = 0; + }; + + class JsonAnySerializer : public JsonUnsupportedTypesSerializer + { + public: + AZ_RTTI(JsonAnySerializer, "{699A0864-C4E2-4266-8141-99793C76870F}", JsonUnsupportedTypesSerializer); + AZ_CLASS_ALLOCATOR_DECL; + + protected: + AZStd::string_view GetMessage() const override; + }; + + class JsonVariantSerializer : public JsonUnsupportedTypesSerializer + { + public: + AZ_RTTI(JsonVariantSerializer, "{08F8E746-F8A4-4E83-8902-713E90F3F498}", JsonUnsupportedTypesSerializer); + AZ_CLASS_ALLOCATOR_DECL; + + protected: + AZStd::string_view GetMessage() const override; + }; + + class JsonOptionalSerializer : public JsonUnsupportedTypesSerializer + { + public: + AZ_RTTI(JsonOptionalSerializer, "{F8AF1C95-BD1B-44D2-9B4A-F5726133A104}", JsonUnsupportedTypesSerializer); + AZ_CLASS_ALLOCATOR_DECL; + + protected: + AZStd::string_view GetMessage() const override; + }; +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index dc0fb13f00..ca5b8cb7f2 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -544,6 +544,8 @@ set(FILES Serialization/Json/TupleSerializer.cpp Serialization/Json/UnorderedSetSerializer.h Serialization/Json/UnorderedSetSerializer.cpp + Serialization/Json/UnsupportedTypesSerializer.h + Serialization/Json/UnsupportedTypesSerializer.cpp Serialization/std/VariantReflection.inl Settings/CommandLine.cpp Settings/CommandLine.h diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/JsonRegistrationContextTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/JsonRegistrationContextTests.cpp index b10b569a42..7367fd47ff 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/JsonRegistrationContextTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/JsonRegistrationContextTests.cpp @@ -95,6 +95,20 @@ namespace JsonSerializationTests } }; + class SerializerWithOneDuplicatedTypeWithOverride + : public JsonSerializerTemplatedMock + { + public: + AZ_RTTI(SerializerWithOneDuplicatedTypeWithOverride, "{4218D591-E578-499B-B578-ACA70C9944AB}", BaseJsonSerializer); + ~SerializerWithOneDuplicatedTypeWithOverride() override = default; + + static void Reflect(AZ::JsonRegistrationContext* context) + { + context->Serializer() + ->HandlesType(true); + } + }; + // Attempts to register the same type twice class SerializerWithTwoSameTypes : public JsonSerializerTemplatedMock @@ -271,13 +285,29 @@ namespace JsonSerializationTests EXPECT_EQ(1, m_jsonRegistrationContext->GetRegisteredSerializers().size()); AZ::BaseJsonSerializer* mockSerializer = m_jsonRegistrationContext->GetSerializerForType(azrtti_typeid()); - EXPECT_NE(mockSerializer, nullptr); + ASSERT_NE(mockSerializer, nullptr); EXPECT_EQ(AZ::AzTypeInfo::Uuid(), mockSerializer->RTTI_GetType()); SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get()); SerializerWithOneDuplicatedType::Unreflect(m_jsonRegistrationContext.get()); } + TEST_F(JsonRegistrationContextTests, OverwriteRegisterSameUuidWithMultipleSerializers_Succeeds) + { + EXPECT_NE(AZ::AzTypeInfo::Uuid(), AZ::AzTypeInfo::Uuid()); + + SerializerWithOneType::Reflect(m_jsonRegistrationContext.get()); + SerializerWithOneDuplicatedTypeWithOverride::Reflect(m_jsonRegistrationContext.get()); + + EXPECT_EQ(1, m_jsonRegistrationContext->GetRegisteredSerializers().size()); + AZ::BaseJsonSerializer* mockSerializer = m_jsonRegistrationContext->GetSerializerForType(azrtti_typeid()); + ASSERT_NE(mockSerializer, nullptr); + EXPECT_EQ(AZ::AzTypeInfo::Uuid(), mockSerializer->RTTI_GetType()); + + SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get()); + SerializerWithOneDuplicatedTypeWithOverride::Unreflect(m_jsonRegistrationContext.get()); + } + TEST_F(JsonRegistrationContextTests, RegisterSameUuidWithSameSerializers_Fails) { AZ_TEST_START_ASSERTTEST; diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/UnsupportedTypesSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/UnsupportedTypesSerializerTests.cpp new file mode 100644 index 0000000000..b7ad6bceae --- /dev/null +++ b/Code/Framework/AzCore/Tests/Serialization/Json/UnsupportedTypesSerializerTests.cpp @@ -0,0 +1,142 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include +#include +#include + +namespace JsonSerializationTests +{ + struct AnyInfo + { + using Type = AZStd::any; + using Serializer = AZ::JsonAnySerializer; + }; + + struct VariantInfo + { + using Type = AZStd::variant; + using Serializer = AZ::JsonVariantSerializer; + }; + + struct OptionalInfo + { + using Type = AZStd::optional; + using Serializer = AZ::JsonVariantSerializer; + }; + + template + class JsonUnsupportedTypesSerializerTests : public BaseJsonSerializerFixture + { + public: + using Type = typename Info::Type; + using Serializer = typename Info::Serializer; + + void SetUp() override + { + BaseJsonSerializerFixture::SetUp(); + this->m_serializer = AZStd::make_unique(); + } + + void TearDown() override + { + this->m_serializer.reset(); + BaseJsonSerializerFixture::TearDown(); + } + + protected: + AZStd::unique_ptr m_serializer; + Type m_instance{}; + }; + + using UnsupportedTypesTestTypes = ::testing::Types; + TYPED_TEST_CASE(JsonUnsupportedTypesSerializerTests, UnsupportedTypesTestTypes); + + TYPED_TEST(JsonUnsupportedTypesSerializerTests, Load_CallDirectly_ReportsIssueAndHalts) + { + namespace JSR = AZ::JsonSerializationResult; + + bool hasMessage = false; + auto callback = [&hasMessage](AZStd::string_view message, JSR::ResultCode result, AZStd::string_view) -> JSR::ResultCode + { + hasMessage = !message.empty(); + return result; + }; + this->m_jsonDeserializationContext->PushReporter(AZStd::move(callback)); + + JSR::Result result = this->m_serializer->Load( + &this->m_instance, azrtti_typeid(this->m_instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext); + this->m_jsonDeserializationContext->PopReporter(); + + EXPECT_EQ(JSR::Processing::Halted, result.GetResultCode().GetProcessing()); + EXPECT_TRUE(hasMessage); + } + + TYPED_TEST(JsonUnsupportedTypesSerializerTests, Load_CallThroughFrontEnd_ReportsIssueAndHalts) + { + namespace JSR = AZ::JsonSerializationResult; + + bool hasMessage = false; + auto callback = [&hasMessage](AZStd::string_view message, JSR::ResultCode result, AZStd::string_view) -> JSR::ResultCode + { + hasMessage = !message.empty(); + return result; + }; + this->m_deserializationSettings->m_reporting = AZStd::move(callback); + + JSR::ResultCode result = AZ::JsonSerialization::Load(this->m_instance, *this->m_jsonDocument, *this->m_deserializationSettings); + + EXPECT_EQ(JSR::Processing::Halted, result.GetProcessing()); + EXPECT_TRUE(hasMessage); + } + + TYPED_TEST(JsonUnsupportedTypesSerializerTests, Save_CallDirectly_ReportsIssueAndHalts) + { + namespace JSR = AZ::JsonSerializationResult; + + bool hasMessage = false; + auto callback = [&hasMessage](AZStd::string_view message, JSR::ResultCode result, AZStd::string_view) -> JSR::ResultCode + { + hasMessage = !message.empty(); + return result; + }; + this->m_jsonSerializationContext->PushReporter(AZStd::move(callback)); + + JSR::Result result = this->m_serializer->Store( + *this->m_jsonDocument, &this->m_instance, nullptr, azrtti_typeid(this->m_instance), *this->m_jsonSerializationContext); + this->m_jsonSerializationContext->PopReporter(); + + EXPECT_EQ(JSR::Processing::Halted, result.GetResultCode().GetProcessing()); + EXPECT_TRUE(hasMessage); + } + + TYPED_TEST(JsonUnsupportedTypesSerializerTests, Save_CallThroughFrontEnd_ReportsIssueAndHalts) + { + namespace JSR = AZ::JsonSerializationResult; + + bool hasMessage = false; + auto callback = [&hasMessage](AZStd::string_view message, JSR::ResultCode result, AZStd::string_view) -> JSR::ResultCode + { + hasMessage = !message.empty(); + return result; + }; + this->m_serializationSettings->m_reporting = AZStd::move(callback); + + JSR::ResultCode result = AZ::JsonSerialization::Store( + *this->m_jsonDocument, this->m_jsonDocument->GetAllocator(), this->m_instance, *this->m_serializationSettings); + + EXPECT_EQ(JSR::Processing::Halted, result.GetProcessing()); + EXPECT_TRUE(hasMessage); + } +} // namespace JsonSerializationTests diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index 78b2701d92..ffbfb75f6b 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -128,6 +128,7 @@ set(FILES Serialization/Json/TransformSerializerTests.cpp Serialization/Json/TupleSerializerTests.cpp Serialization/Json/UnorderedSetSerializerTests.cpp + Serialization/Json/UnsupportedTypesSerializerTests.cpp Serialization/Json/UuidSerializerTests.cpp Math/AabbTests.cpp Math/ColorTests.cpp diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp index 04573eb2e5..5cb2006447 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp @@ -1290,7 +1290,7 @@ namespace AZ::IO ////////////////////////////////////////////////////////////////////////// - AZ::IO::ArchiveFileIterator Archive::FindFirst(AZStd::string_view pDir, [[maybe_unused]] uint32_t nPathFlags, bool bAllowUseFileSystem) + AZ::IO::ArchiveFileIterator Archive::FindFirst(AZStd::string_view pDir, EFileSearchType searchType) { auto szFullPath = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pDir); if (!szFullPath) @@ -1299,8 +1299,26 @@ namespace AZ::IO return {}; } + bool bScanZips{}; + bool bAllowUseFileSystem{}; + switch (searchType) + { + case IArchive::eFileSearchType_AllowInZipsOnly: + bAllowUseFileSystem = false; + bScanZips = true; + break; + case IArchive::eFileSearchType_AllowOnDiskAndInZips: + bAllowUseFileSystem = true; + bScanZips = true; + break; + case IArchive::eFileSearchType_AllowOnDiskOnly: + bAllowUseFileSystem = true; + bScanZips = false; + break; + } + AZStd::intrusive_ptr pFindData = new AZ::IO::FindData(); - pFindData->Scan(this, szFullPath->Native(), bAllowUseFileSystem); + pFindData->Scan(this, szFullPath->Native(), bAllowUseFileSystem, bScanZips); return pFindData->Fetch(); } @@ -1676,7 +1694,7 @@ namespace AZ::IO return true; } - if (AZ::IO::ArchiveFileIterator fileIterator = FindFirst(pWildcardIn, 0, true); fileIterator) + if (AZ::IO::ArchiveFileIterator fileIterator = FindFirst(pWildcardIn, IArchive::eFileSearchType_AllowOnDiskOnly); fileIterator) { AZStd::vector files; do diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h index 997b3e3d2c..329beb4291 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h @@ -234,7 +234,7 @@ namespace AZ::IO uint64_t FTell(AZ::IO::HandleType handle) override; int FFlush(AZ::IO::HandleType handle) override; int FClose(AZ::IO::HandleType handle) override; - AZ::IO::ArchiveFileIterator FindFirst(AZStd::string_view pDir, uint32_t nPathFlags = 0, bool bAllOwUseFileSystem = false) override; + AZ::IO::ArchiveFileIterator FindFirst(AZStd::string_view pDir, EFileSearchType searchType = eFileSearchType_AllowInZipsOnly) override; AZ::IO::ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator fileIterator) override; bool FindClose(AZ::IO::ArchiveFileIterator fileIterator) override; int FEof(AZ::IO::HandleType handle) override; diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp index 1794ae90e7..05da5f16eb 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp @@ -77,7 +77,7 @@ namespace AZ::IO return m_findData && m_lastFetchValid; } - void FindData::Scan(IArchive* archive, AZStd::string_view szDir, bool bAllowUseFS) + void FindData::Scan(IArchive* archive, AZStd::string_view szDir, bool bAllowUseFS, bool bScanZips) { // get the priority into local variable to avoid it changing in the course of // this function execution @@ -87,12 +87,18 @@ namespace AZ::IO { // first, find the file system files ScanFS(archive, szDir); - ScanZips(archive, szDir); + if (bScanZips) + { + ScanZips(archive, szDir); + } } else { // first, find the zip files - ScanZips(archive, szDir); + if (bScanZips) + { + ScanZips(archive, szDir); + } if (bAllowUseFS || nVarPakPriority != ArchiveLocationPriority::ePakPriorityPakOnly) { ScanFS(archive, szDir); @@ -111,30 +117,31 @@ namespace AZ::IO } AZ::IO::FileIOBase::GetDirectInstance()->FindFiles(searchDirectory.c_str(), pattern.c_str(), [&](const char* filePath) -> bool { - AZ::IO::FileDesc fileDesc; - AZStd::string filePathEntry{filePath}; + AZ::IO::ArchiveFileIterator fileIterator; + fileIterator.m_filename = AZ::IO::PathView(filePath).Filename().Native(); + fileIterator.m_fileDesc.nAttrib = {}; if (AZ::IO::FileIOBase::GetDirectInstance()->IsDirectory(filePath)) { - fileDesc.nAttrib = fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::Subdirectory; + fileIterator.m_fileDesc.nAttrib = fileIterator.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::Subdirectory; + m_fileStack.emplace_back(AZStd::move(fileIterator)); } else { if (AZ::IO::FileIOBase::GetDirectInstance()->IsReadOnly(filePath)) { - fileDesc.nAttrib = fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::ReadOnly; + fileIterator.m_fileDesc.nAttrib = fileIterator.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::ReadOnly; } AZ::u64 fileSize = 0; AZ::IO::FileIOBase::GetDirectInstance()->Size(filePath, fileSize); - fileDesc.nSize = fileSize; - fileDesc.tWrite = AZ::IO::FileIOBase::GetDirectInstance()->ModificationTime(filePath); + fileIterator.m_fileDesc.nSize = fileSize; + fileIterator.m_fileDesc.tWrite = AZ::IO::FileIOBase::GetDirectInstance()->ModificationTime(filePath); // These times are not supported by our file interface - fileDesc.tAccess = fileDesc.tWrite; - fileDesc.tCreate = fileDesc.tWrite; + fileIterator.m_fileDesc.tAccess = fileIterator.m_fileDesc.tWrite; + fileIterator.m_fileDesc.tCreate = fileIterator.m_fileDesc.tWrite; + m_fileStack.emplace_back(AZStd::move(fileIterator)); } - [[maybe_unused]] auto result = m_mapFiles.emplace(AZStd::move(filePathEntry), fileDesc); - AZ_Assert(result.second, "Failed to insert FindData entry for filePath %s", filePath); return true; }); } @@ -164,7 +171,7 @@ namespace AZ::IO fileDesc.nAttrib = AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive; fileDesc.nSize = fileEntry->desc.lSizeUncompressed; fileDesc.tWrite = fileEntry->GetModificationTime(); - m_mapFiles.emplace(fname, fileDesc); + m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ this, fname, fileDesc }); } ZipDir::FindDir findDirectoryEntry(zipCache); @@ -177,7 +184,7 @@ namespace AZ::IO } AZ::IO::FileDesc fileDesc; fileDesc.nAttrib = AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive | AZ::IO::FileDesc::Attribute::Subdirectory; - m_mapFiles.emplace(fname, fileDesc); + m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ this, fname, fileDesc }); } }; @@ -246,7 +253,7 @@ namespace AZ::IO if (!bindRootIter->empty() && AZStd::wildcard_match(sourcePathRemainder.Native(), bindRootIter->Native())) { AZ::IO::FileDesc fileDesc{ AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive | AZ::IO::FileDesc::Attribute::Subdirectory }; - m_mapFiles.emplace(bindRootIter->Native(), fileDesc); + m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ this, bindRootIter->Native(), fileDesc }); } } else @@ -262,22 +269,19 @@ namespace AZ::IO AZ::IO::ArchiveFileIterator FindData::Fetch() { - AZ::IO::ArchiveFileIterator fileIterator; - fileIterator.m_findData = this; - if (m_mapFiles.empty()) + if (m_fileStack.empty()) { - return fileIterator; + AZ::IO::ArchiveFileIterator emptyFileIterator; + emptyFileIterator.m_lastFetchValid = false; + emptyFileIterator.m_findData = this; + return emptyFileIterator; } - auto pakFileIter = m_mapFiles.begin(); - AZStd::string fullFilePath; - AZ::StringFunc::Path::GetFullFileName(pakFileIter->first.c_str(), fullFilePath); - fileIterator.m_filename = AZStd::move(fullFilePath); - fileIterator.m_fileDesc = pakFileIter->second; - fileIterator.m_lastFetchValid = true; - // Remove Fetched item from the FindData map so that the iteration continues - m_mapFiles.erase(pakFileIter); + AZ::IO::ArchiveFileIterator fileIterator{ m_fileStack.back() }; + fileIterator.m_lastFetchValid = true; + fileIterator.m_findData = this; + m_fileStack.pop_back(); return fileIterator; } } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.h b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.h index d5e23779fc..a07e98c81f 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.h @@ -15,7 +15,6 @@ #include #include - namespace AZ::IO { struct IArchive; @@ -74,13 +73,13 @@ namespace AZ::IO AZ_CLASS_ALLOCATOR(FindData, AZ::SystemAllocator, 0); FindData() = default; AZ::IO::ArchiveFileIterator Fetch(); - void Scan(IArchive* archive, AZStd::string_view path, bool bAllowUseFS = false); + void Scan(IArchive* archive, AZStd::string_view path, bool bAllowUseFS = false, bool bScanZips = true); protected: void ScanFS(IArchive* archive, AZStd::string_view path); void ScanZips(IArchive* archive, AZStd::string_view path); - using FileMap = AZStd::map; - FileMap m_mapFiles; + using FileStack = AZStd::vector; + FileStack m_fileStack; }; } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h b/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h index ce8403b033..08bd87c334 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/IArchive.h @@ -197,6 +197,13 @@ namespace AZ::IO eInMemoryPakLocale_PAK, }; + enum EFileSearchType + { + eFileSearchType_AllowInZipsOnly = 0, + eFileSearchType_AllowOnDiskAndInZips, + eFileSearchType_AllowOnDiskOnly + }; + using SignedFileSize = int64_t; virtual ~IArchive() = default; @@ -315,7 +322,7 @@ namespace AZ::IO // Arguments: // nFlags is a combination of EPathResolutionRules flags. - virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, uint32_t nFlags = 0, bool bAllowUseFileSystem = false) = 0; + virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, EFileSearchType searchType = eFileSearchType_AllowInZipsOnly) = 0; virtual ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator handle) = 0; virtual bool FindClose(AZ::IO::ArchiveFileIterator handle) = 0; //returns file modification time diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h index c15f75e3e7..f4ef1a1ca2 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h @@ -166,7 +166,7 @@ namespace AzFramework { public: AZ_CLASS_ALLOCATOR(RequestEscalateAsset, AZ::OSAllocator, 0); - AZ_RTTI(RequestAssetStatus, "{E95C5422-5F00-478B-A984-C041DE70484F}", BaseAssetProcessorMessage); + AZ_RTTI(RequestEscalateAsset, "{E95C5422-5F00-478B-A984-C041DE70484F}", BaseAssetProcessorMessage); static void Reflect(AZ::ReflectContext* context); static constexpr unsigned int MessageType = AZ_CRC("AssetSystem::RequestEscalateAsset", 0x1894d94e); diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.h index 56fe9a68c4..6119a6f630 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.h @@ -46,7 +46,7 @@ namespace AzPhysics //! Each Physics Scene uses this as a base and will override as needed. CollisionConfiguration m_collisionConfig; - Physics::MaterialConfiguration m_defaultMaterialConfiguration; //!< Default material parameters for the project. + Physics::DefaultMaterialConfiguration m_defaultMaterialConfiguration; //!< Default material parameters for the project. AZ::Data::Asset m_materialLibraryAsset = AZ::Data::AssetLoadBehavior::NoLoad; //!< Material Library exposed by the system component SystemBus API. //! Controls whether the Physics System will self register to the TickBus and call StartSimulation / FinishSimulation on each Scene. diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp index 78e0431753..d84d2739d0 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Material.cpp @@ -83,14 +83,12 @@ namespace Physics AZ::EditContext* editContext = serializeContext->GetEditContext(); if (editContext) { - AZStd::unordered_set forbiddenSurfaceTypeNames; - forbiddenSurfaceTypeNames.insert("Default"); editContext->Class("", "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "Physics Material") - ->DataElement(MaterialConfiguration::s_configLineEdit, &MaterialConfiguration::m_surfaceType, "Surface type", "Game surface type") // Uses ConfigStringLineEditCtrl in PhysX gem. + ->DataElement(MaterialConfiguration::s_configLineEdit, &MaterialConfiguration::m_surfaceType, "Name", "Name of the physics material") // Uses ConfigStringLineEditCtrl in PhysX gem. ->Attribute(AZ::Edit::Attributes::MaxLength, 64) + ->Attribute(AZ::Edit::Attributes::ReadOnly, &MaterialConfiguration::IsNameReadOnly) ->Attribute(MaterialConfiguration::s_stringGroup, AZ_CRC("LineEditGroupSurfaceType", 0x6670659e)) - ->Attribute(MaterialConfiguration::s_forbiddenStringSet, forbiddenSurfaceTypeNames) ->DataElement(AZ::Edit::UIHandlers::Default, &MaterialConfiguration::m_staticFriction, "Static friction", "Friction coefficient when object is still") ->Attribute(AZ::Edit::Attributes::Min, 0.f) ->DataElement(AZ::Edit::UIHandlers::Default, &MaterialConfiguration::m_dynamicFriction, "Dynamic friction", "Friction coefficient when object is moving") @@ -178,6 +176,23 @@ namespace Physics ////////////////////////////////////////////////////////////////////////// + DefaultMaterialConfiguration::DefaultMaterialConfiguration() + { + m_surfaceType = Physics::DefaultPhysicsMaterialLabel; + } + + void DefaultMaterialConfiguration::Reflect(AZ::ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ; + } + } + + ////////////////////////////////////////////////////////////////////////// + void MaterialLibraryAsset::Reflect(AZ::ReflectContext * context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); @@ -212,7 +227,7 @@ namespace Physics if (serializeContext) { serializeContext->Class() - ->Version(1) + ->Version(2) ->Field("DefaultMaterial", &MaterialInfoReflectionWrapper::m_defaultMaterialConfiguration) ->Field("Asset", &MaterialInfoReflectionWrapper::m_materialLibraryAsset) ; @@ -300,7 +315,7 @@ namespace Physics { auto foundMaterialConfiguration = AZStd::find_if(m_materialLibrary.begin(), m_materialLibrary.end(), [&materialName](const auto& data) { - return data.m_configuration.m_surfaceType == materialName; + return AZ::StringFunc::Equal(data.m_configuration.m_surfaceType, materialName, false/*bCaseSensitive*/); }); if (foundMaterialConfiguration != m_materialLibrary.end()) @@ -372,20 +387,28 @@ namespace Physics serializeContext->Class() ->Version(3, &ClassConverters::MaterialSelectionConverter) ->EventHandler() + ->Field("EditContextMaterialLibrary", &MaterialSelection::m_editContextMaterialLibrary) ->Field("MaterialIds", &MaterialSelection::m_materialIdsAssignedToSlots) ; if (auto editContext = serializeContext->GetEditContext()) { - editContext->Class("Physics Material", "Select physics material library and which materials to use for the object") + editContext->Class("Physics Materials", "Select which physics materials to use for each element of this object") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialIdsAssignedToSlots, "Mesh Surfaces", "Specify which Physics Material to use for each element of this object") + ->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_editContextMaterialLibrary, "Library", "Physics material library from PhysX Configuration") + ->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false) + ->Attribute(AZ_CRC_CE("BrowseButtonEnabled"), false) + ->Attribute(AZ_CRC_CE("EditButton"), "") + ->Attribute(AZ_CRC_CE("EditDescription"), "Open in Asset Editor") + ->Attribute(AZ::Edit::Attributes::DefaultAsset, &MaterialSelection::GetMaterialLibraryId) + ->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialIdsAssignedToSlots, "", "") ->ElementAttribute(Attributes::MaterialLibraryAssetId, &MaterialSelection::GetMaterialLibraryId) ->Attribute(AZ::Edit::Attributes::IndexedChildNameLabelOverride, &MaterialSelection::GetMaterialSlotLabel) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->ElementAttribute(AZ::Edit::Attributes::ReadOnly, &MaterialSelection::AreMaterialSlotsReadOnly) ->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false) + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; } } diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Material.h b/Code/Framework/AzFramework/AzFramework/Physics/Material.h index 69edf3ed25..1e12967945 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Material.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Material.h @@ -25,6 +25,8 @@ namespace AZ namespace Physics { + static constexpr AZStd::string_view DefaultPhysicsMaterialLabel = ""; + /// Physics material /// ========================= /// This is the interface to the wrapper around native material type (such as PxMaterial in PhysX gem) @@ -97,10 +99,13 @@ namespace Physics class MaterialConfiguration { public: - AZ_TYPE_INFO(Physics::MaterialConfiguration, "{8807CAA1-AD08-4238-8FDB-2154ADD084A1}"); + AZ_RTTI(Physics::MaterialConfiguration, "{8807CAA1-AD08-4238-8FDB-2154ADD084A1}"); static void Reflect(AZ::ReflectContext* context); + MaterialConfiguration() = default; + virtual ~MaterialConfiguration() = default; + const static AZ::Crc32 s_stringGroup; ///< Edit context data attribute. Identifies a string group instance. String values in the same group are unique. const static AZ::Crc32 s_forbiddenStringSet; ///< Edit context data attribute. A set of strings that are not acceptable as values to the data element. Can be AZStd::unordered_set, AZStd::set, AZStd::vector const static AZ::Crc32 s_configLineEdit; ///< Edit context data element handler. Creates custom line edit widget that allows string values to be unique in a group. @@ -121,11 +126,28 @@ namespace Physics bool operator==(const MaterialConfiguration& other) const; bool operator!=(const MaterialConfiguration& other) const; + protected: + virtual bool IsNameReadOnly() const { return false; } + private: static bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); static AZ::Color GenerateDebugColor(const char* materialName); }; + class DefaultMaterialConfiguration + : public MaterialConfiguration + { + public: + AZ_RTTI(Physics::DefaultMaterialConfiguration, "{A1F64C5C-D413-4757-9D42-51DD0EBFC270}", Physics::MaterialConfiguration); + + static void Reflect(AZ::ReflectContext* context); + + DefaultMaterialConfiguration(); + + protected: + bool IsNameReadOnly() const override { return true; } + }; + namespace Attributes { const static AZ::Crc32 MaterialLibraryAssetId = AZ_CRC("MaterialAssetId", 0x4a88a3f5); @@ -237,7 +259,7 @@ namespace Physics AZ_TYPE_INFO(Physics::MaterialInfoReflectionWrapper, "{02AB8CBC-D35B-4E0F-89BA-A96D94DAD4F9}"); static void Reflect(AZ::ReflectContext* context); - Physics::MaterialConfiguration m_defaultMaterialConfiguration; + Physics::DefaultMaterialConfiguration m_defaultMaterialConfiguration; AZ::Data::Asset m_materialLibraryAsset = AZ::Data::AssetLoadBehavior::NoLoad; }; @@ -296,6 +318,10 @@ namespace Physics // EditorContext callbacks AZStd::string GetMaterialSlotLabel(int index); + + // Only used for Edit Context as it requires to have an asset reflected. + // To get the material library use GetMaterialLibraryId() + AZ::Data::Asset m_editContextMaterialLibrary{ AZ::Data::AssetLoadBehavior::NoLoad }; }; /// Editor Bus used to assign material to terrain surface id. diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Shape.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Shape.cpp index 6325b50f4d..910184857b 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Shape.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Shape.cpp @@ -62,7 +62,7 @@ namespace Physics ->Attribute(AZ::Edit::Attributes::Step, 0.01f) ->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_rotation, "Rotation", "Local rotation relative to the rigid body") ->Attribute(AZ::Edit::Attributes::Visibility, &ColliderConfiguration::GetOffsetVisibility) - ->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_materialSelection, "Physics Material", "Select physics material library and which materials to use for the shape") + ->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_materialSelection, "Physics Materials", "Select which physics materials to use for each element of this shape") ->Attribute(AZ::Edit::Attributes::Visibility, &ColliderConfiguration::GetMaterialSelectionVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_tag, "Tag", "Tag used to identify colliders from one another") ->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_restOffset, "Rest offset", diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp index 275103bc28..2c3b113d65 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp @@ -17,21 +17,6 @@ namespace Physics { - namespace Internal - { - bool ShapeConfigurationVersionConverter( - [[maybe_unused]] AZ::SerializeContext& context, - AZ::SerializeContext::DataElementNode& classElement) - { - if (classElement.GetVersion() <= 1) - { - classElement.RemoveElementByName(AZ_CRC_CE("UseMaterialsFromAsset")); - } - - return true; - } - } - void ShapeConfiguration::Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context)) @@ -181,9 +166,10 @@ namespace Physics ->RegisterGenericType>(); serializeContext->Class() - ->Version(2, &Internal::ShapeConfigurationVersionConverter) + ->Version(3) ->Field("PhysicsAsset", &PhysicsAssetShapeConfiguration::m_asset) ->Field("AssetScale", &PhysicsAssetShapeConfiguration::m_assetScale) + ->Field("UseMaterialsFromAsset", &PhysicsAssetShapeConfiguration::m_useMaterialsFromAsset) ->Field("SubdivisionLevel", &PhysicsAssetShapeConfiguration::m_subdivisionLevel) ; @@ -196,6 +182,7 @@ namespace Physics ->DataElement(AZ::Edit::UIHandlers::Default, &PhysicsAssetShapeConfiguration::m_assetScale, "Asset Scale", "The scale of the asset shape") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Step, 0.01f) + ->DataElement(AZ::Edit::UIHandlers::Default, &PhysicsAssetShapeConfiguration::m_useMaterialsFromAsset, "Physics materials from asset", "Auto-set physics materials using asset's physics material names") ; } } diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h index 8234ef9173..b3d04a10c9 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.h @@ -140,7 +140,7 @@ namespace Physics AZ::Data::Asset m_asset{ AZ::Data::AssetLoadBehavior::PreLoad }; AZ::Vector3 m_assetScale = AZ::Vector3::CreateOne(); - bool m_useMaterialsFromAsset = false; // Not reflected or exposed to the user until there is a way to auto-match mesh's materials with physics materials + bool m_useMaterialsFromAsset = true; AZ::u8 m_subdivisionLevel = 4; ///< The level of subdivision if a primitive shape is replaced with a convex mesh due to scaling. }; diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp index 2c3b62bb88..17c09bb6ce 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp @@ -118,6 +118,7 @@ namespace Physics AzPhysics::TriggerEvent::Reflect(context); AzPhysics::SceneConfiguration::Reflect(context); MaterialConfiguration::Reflect(context); + DefaultMaterialConfiguration::Reflect(context); MaterialLibraryAsset::Reflect(context); MaterialInfoReflectionWrapper::Reflect(context); JointLimitConfiguration::Reflect(context); diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/asset_editor.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/asset_editor.svg new file mode 100644 index 0000000000..b96f27dda7 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/asset_editor.svg @@ -0,0 +1,7 @@ + + + Asset Editor icon + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/audio_editor.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/audio_editor.svg new file mode 100644 index 0000000000..1cc863ebcd --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/audio_editor.svg @@ -0,0 +1,7 @@ + + + audio edit + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/emfx_editor.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/emfx_editor.svg new file mode 100644 index 0000000000..72e67b5170 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/emfx_editor.svg @@ -0,0 +1,7 @@ + + + EMFX header icon + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/landscape_canvas_editor.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/landscape_canvas_editor.svg new file mode 100644 index 0000000000..ebf38d15c0 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/landscape_canvas_editor.svg @@ -0,0 +1,7 @@ + + + Veg editor icon + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/lua_editor.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/lua_editor.svg new file mode 100644 index 0000000000..76b4ed3da4 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/lua_editor.svg @@ -0,0 +1,13 @@ + + + Lua icon + + + + + + + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/material_editor.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/material_editor.svg new file mode 100644 index 0000000000..ed6f477367 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/material_editor.svg @@ -0,0 +1,10 @@ + + + Material editor icon + + + + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/script_canvas_editor.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/script_canvas_editor.svg new file mode 100644 index 0000000000..b2a303d237 --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/script_canvas_editor.svg @@ -0,0 +1,7 @@ + + + SC icon + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/trackview_editor.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/trackview_editor.svg new file mode 100644 index 0000000000..56da550f0e --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/trackview_editor.svg @@ -0,0 +1,7 @@ + + + trackview icon + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/ui_editor.svg b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/ui_editor.svg new file mode 100644 index 0000000000..8d27f071da --- /dev/null +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/Menu/ui_editor.svg @@ -0,0 +1,7 @@ + + + UI editor icon + + + + \ No newline at end of file diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc b/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc index 2487917f67..cc66558367 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc +++ b/Code/Framework/AzQtComponents/AzQtComponents/Images/resources.qrc @@ -19,5 +19,14 @@ Menu/resolution.svg Menu/debug.svg Menu/camera.svg + Menu/asset_editor.svg + Menu/audio_editor.svg + Menu/emfx_editor.svg + Menu/landscape_canvas_editor.svg + Menu/lua_editor.svg + Menu/material_editor.svg + Menu/script_canvas_editor.svg + Menu/trackview_editor.svg + Menu/ui_editor.svg diff --git a/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h b/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h index 855b4fe416..64816f75b2 100644 --- a/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h +++ b/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h @@ -22,7 +22,7 @@ #define AZ_TRAIT_DISABLE_FAILED_AP_CONNECTION_TESTS true #define AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS true -#define AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS true + #define AZ_TRAIT_DISABLE_FAILED_ATOM_RPI_TESTS true #define AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS true #define AZ_TRAIT_DISABLE_FAILED_ZERO_COLOR_CONVERSION_TEST true diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h index 72150b1b57..ab5c58c72a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h @@ -916,9 +916,6 @@ namespace AzToolsFramework eECMF_USE_VIEWPORT_CENTER = 0x2, }; - /// Populate global edit-time context menu. - virtual void PopulateEditorGlobalContextMenu(QMenu * /*menu*/, const AZ::Vector2& /*point*/, int /*flags*/) {} - /// Populate slice portion of edit-time context menu virtual void PopulateEditorGlobalContextMenu_SliceSection(QMenu * /*menu*/, const AZ::Vector2& /*point*/, int /*flags*/) {} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Editor/EditorContextMenuBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Editor/EditorContextMenuBus.h index 33710e68f3..e8f37be539 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Editor/EditorContextMenuBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Editor/EditorContextMenuBus.h @@ -79,7 +79,7 @@ namespace AzToolsFramework * This is the menu that appears when right clicking the main editor window, * including the Entity Outliner and the Viewport. */ - virtual void PopulateEditorGlobalContextMenu(QMenu* menu) const = 0; + virtual void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) = 0; }; using EditorContextMenuBus = AZ::EBus; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp index 766a293b4a..83a76aeb01 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp @@ -187,13 +187,14 @@ namespace AzToolsFramework return removedEntity; } - void Instance::DetachNestedEntities(const AZStd::function)>& callback) + void Instance::DetachAllEntitiesInHierarchy(const AZStd::function)>& callback) { + callback(AZStd::move(DetachContainerEntity())); DetachEntities(callback); for (const auto& [instanceAlias, instance] : m_nestedInstances) { - instance->DetachNestedEntities(callback); + instance->DetachAllEntitiesInHierarchy(callback); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h index d14203e2e5..5f36ac10dc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h @@ -87,7 +87,15 @@ namespace AzToolsFramework bool AddEntity(AZ::Entity& entity, EntityAlias entityAlias); AZStd::unique_ptr DetachEntity(const AZ::EntityId& entityId); void DetachEntities(const AZStd::function)>& callback); - void DetachNestedEntities(const AZStd::function)>& callback); + + /** + * Detaches all entities in the instance hierarchy. + * Includes all direct entities, all nested entities, and all container entities. + * Note that without container entities the hierarchy that remains cannot be used further without restoring new ones. + * @param callback A user provided callback that can be used to capture ownership and manipulate the detached entities. + */ + void DetachAllEntitiesInHierarchy(const AZStd::function)>& callback); + void RemoveNestedEntities(const AZStd::function&)>& filter); void Reset(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.cpp index 8a72b221dc..1b28320010 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.cpp @@ -154,7 +154,7 @@ namespace AzToolsFramework InstanceOptionalReference owningInstanceReference = m_storingInstance->m_instanceEntityMapper->FindOwningInstance(entityId); // Start with an empty alias to build out our reference path - // If we can't resolve this id we'll return a random new alias instead of a reference path + // If we can't resolve this id we'll return a new alias based on the entity ID instead of a reference path AliasPath relativeEntityAliasPath; if (!owningInstanceReference) { @@ -162,7 +162,7 @@ namespace AzToolsFramework "Prefab - EntityIdMapper: Entity with Id %s has no registered owning instance", entityId.ToString().c_str()); - return Instance::GenerateEntityAlias(); + return AZStd::string::format("Entity_%s", entityId.ToString().c_str()); } Instance* owningInstance = &(owningInstanceReference->get()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp index 3c91f99b05..241d2a3bd5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp @@ -38,11 +38,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils // going to be used to create clones of the entities. { AzFramework::Spawnable::EntityList& entities = spawnable.GetEntities(); - if (instance.HasContainerEntity()) - { - entities.emplace_back(AZStd::move(instance.DetachContainerEntity())); - } - instance.DetachNestedEntities( + instance.DetachAllEntitiesInHierarchy( [&entities](AZStd::unique_ptr entity) { entities.emplace_back(AZStd::move(entity)); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp index 3e13e6226b..bcbe98c3a7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp @@ -1206,7 +1206,7 @@ namespace AzToolsFramework Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushableOnSliceRoot)-> Attribute(AZ::Edit::Attributes::ReadOnly, &EditorTransform::m_locked)-> DataElement(AZ::Edit::UIHandlers::Default, &EditorTransform::m_rotate, "Rotate", "Local Rotation (Relative to parent) in degrees.")-> - Attribute(AZ::Edit::Attributes::Step, 0.1f)-> + Attribute(AZ::Edit::Attributes::Step, 1.0f)-> Attribute(AZ::Edit::Attributes::Suffix, " deg")-> Attribute(AZ::Edit::Attributes::ReadOnly, &EditorTransform::m_locked)-> Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushableOnSliceRoot)-> diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp index 8293614525..d569d266ab 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -544,8 +545,7 @@ namespace AzToolsFramework QMenu* contextMenu = new QMenu(this); // Populate global context menu. - EBUS_EVENT(EditorEvents::Bus, - PopulateEditorGlobalContextMenu, + AzToolsFramework::EditorContextMenuBus::Broadcast(&AzToolsFramework::EditorContextMenuEvents::PopulateEditorGlobalContextMenu, contextMenu, AZ::Vector2::CreateZero(), EditorEvents::eECMF_HIDE_ENTITY_CREATION | EditorEvents::eECMF_USE_VIEWPORT_CENTER); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 9d01911532..77918565e4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -119,7 +119,7 @@ namespace AzToolsFramework return "Prefabs"; } - void PrefabIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu) const + void PrefabIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags) { AzToolsFramework::EntityIdList selectedEntities; AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h index 69ec3013cc..a3a9bc3741 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h @@ -65,7 +65,7 @@ namespace AzToolsFramework // EditorContextMenuBus... int GetMenuPosition() const override; AZStd::string GetMenuIdentifier() const override; - void PopulateEditorGlobalContextMenu(QMenu* menu) const override; + void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override; // EntityOutlinerSourceDropHandlingBus... void HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp index a0b3762123..e8be4be0a2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp @@ -1130,6 +1130,16 @@ namespace AzToolsFramework m_browseEdit->setAttachedButtonIcon(icon); } + void PropertyAssetCtrl::SetBrowseButtonEnabled(bool enabled) + { + m_browseEdit->setEnabled(enabled); + } + + void PropertyAssetCtrl::SetBrowseButtonVisible(bool visible) + { + m_browseEdit->setVisible(visible); + } + const QModelIndex PropertyAssetCtrl::GetSourceIndex(const QModelIndex& index) { if (!index.isValid()) @@ -1360,6 +1370,22 @@ namespace AzToolsFramework GUI->SetBrowseButtonIcon(QIcon(iconPath.c_str())); } } + else if (attrib == AZ_CRC_CE("BrowseButtonEnabled")) + { + bool enabled = true; + if (attrValue->Read(enabled)) + { + GUI->SetBrowseButtonEnabled(enabled); + } + } + else if (attrib == AZ_CRC_CE("BrowseButtonVisible")) + { + bool visible = true; + if (attrValue->Read(visible)) + { + GUI->SetBrowseButtonVisible(visible); + } + } else if (attrib == AZ_CRC_CE("Thumbnail")) { bool showThumbnail = false; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx index 5a6310eb35..fc52a1a9c9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx @@ -208,6 +208,8 @@ namespace AzToolsFramework void SetEditButtonIcon(const QIcon& icon); void SetEditButtonTooltip(QString tooltip); void SetBrowseButtonIcon(const QIcon& icon); + void SetBrowseButtonEnabled(bool enabled); + void SetBrowseButtonVisible(bool visible); void SetClearButtonEnabled(bool enable); void SetClearButtonVisible(bool visible); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ActionBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ActionBus.h index 7eaf3da37c..fa43eb422d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ActionBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ActionBus.h @@ -188,7 +188,7 @@ namespace AzToolsFramework virtual void AddEditMenuAction(QAction* action) = 0; /// Add an action to the Editor menu. - virtual void AddMenuAction(AZStd::string_view categoryId, QAction* action) = 0; + virtual void AddMenuAction(AZStd::string_view categoryId, QAction* action, bool addToToolsToolbar) = 0; /// (Re)populate the default EditMenu. /// Restore the EditMenu to its default state (the options available when first opening a level). diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/EditorContextMenu.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/EditorContextMenu.cpp index 308f0f074f..4521f45783 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/EditorContextMenu.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/EditorContextMenu.cpp @@ -13,6 +13,7 @@ #include "EditorContextMenu.h" #include "AzToolsFramework/Viewport/ViewportMessages.h" +#include "Editor/EditorContextMenuBus.h" namespace AzToolsFramework { @@ -55,8 +56,7 @@ namespace AzToolsFramework // Populate global context menu. const int contextMenuFlag = 0; - EditorEvents::Bus::BroadcastReverse( - &EditorEvents::PopulateEditorGlobalContextMenu, contextMenu.m_menu.data(), + AzToolsFramework::EditorContextMenuBus::Broadcast(&AzToolsFramework::EditorContextMenuEvents::PopulateEditorGlobalContextMenu, contextMenu.m_menu.data(), AzFramework::Vector2FromScreenPoint(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates), contextMenuFlag); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index eadb870563..c503386ab5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -1018,6 +1018,7 @@ namespace AzToolsFramework EditorEntityVisibilityNotificationBus::Router::BusRouterConnect(); EditorEntityLockComponentNotificationBus::Router::BusRouterConnect(); EditorManipulatorCommandUndoRedoRequestBus::Handler::BusConnect(entityContextId); + EditorContextMenuBus::Handler::BusConnect(); CreateTransformModeSelectionCluster(); CreateSpaceSelectionCluster(); @@ -1038,6 +1039,7 @@ namespace AzToolsFramework m_pivotOverrideFrame.Reset(); + EditorContextMenuBus::Handler::BusConnect(); EditorManipulatorCommandUndoRedoRequestBus::Handler::BusDisconnect(); EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect(); EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect(); @@ -3097,15 +3099,25 @@ namespace AzToolsFramework } } - void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& /*point*/, const int /*flags*/) + int EditorTransformComponentSelection::GetMenuPosition() const { - QAction* action = menu->addAction(QObject::tr(s_togglePivotTitleRightClick)); - QObject::connect( - action, &QAction::triggered, action, - [this]() - { - ToggleCenterPivotSelection(); - }); + return aznumeric_cast(EditorContextMenuOrdering::BOTTOM); + } + + AZStd::string EditorTransformComponentSelection::GetMenuIdentifier() const + { + return "Transform Component"; + } + + void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu(QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags) + { + QAction* action = menu->addAction(QObject::tr(s_togglePivotTitleRightClick)); + QObject::connect( + action, &QAction::triggered, action, + [this]() + { + ToggleCenterPivotSelection(); + }); } void EditorTransformComponentSelection::BeforeEntitySelectionChanged() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h index 2ec7fa2489..1b52911978 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -126,6 +127,7 @@ namespace AzToolsFramework //! Provide a suite of functionality for manipulating entities, primarily through their TransformComponent. class EditorTransformComponentSelection : public ViewportInteraction::ViewportSelectionRequests + , public EditorContextMenuBus::Handler , private EditorEventsBus::Handler , private EditorTransformComponentSelectionRequestBus::Handler , private ToolsApplicationNotificationBus::Handler @@ -238,8 +240,12 @@ namespace AzToolsFramework void UndoRedoEntityManipulatorCommand( AZ::u8 pivotOverride, const AZ::Transform& transform, AZ::EntityId entityId) override; + // EditorContextMenuBus... + void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2 & point, int flags) override; + int GetMenuPosition() const override; + AZStd::string GetMenuIdentifier() const override; + // EditorEventsBus ... - void PopulateEditorGlobalContextMenu(QMenu *menu, const AZ::Vector2& point, int flags) override; void OnEscape() override; // ToolsApplicationNotificationBus ... diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/SpawnableCreateTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/SpawnableCreateTests.cpp index af178ec7a4..14bd977318 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/SpawnableCreateTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/SpawnableCreateTests.cpp @@ -71,24 +71,29 @@ namespace UnitTest m_prefabSystemComponent->CreatePrefab({ entitiesCreated[0] }, {}, "test/path1")); ASSERT_TRUE(firstInstance); + ASSERT_TRUE(firstInstance->HasContainerEntity()); + expectedEntityNameSet.insert(firstInstance->GetContainerEntity()->get().GetName()); + AZStd::unique_ptr secondInstance( m_prefabSystemComponent->CreatePrefab({ entitiesCreated[1] }, MakeInstanceList(AZStd::move(firstInstance)), "test/path2")); ASSERT_TRUE(secondInstance); + ASSERT_TRUE(secondInstance->HasContainerEntity()); + expectedEntityNameSet.insert(secondInstance->GetContainerEntity()->get().GetName()); + AZStd::unique_ptr thirdInstance( m_prefabSystemComponent->CreatePrefab({ entitiesCreated[2] }, MakeInstanceList(AZStd::move(secondInstance)), "test/path3")); ASSERT_TRUE(thirdInstance); ASSERT_TRUE(thirdInstance->HasContainerEntity()); - auto& containerEntity = thirdInstance->GetContainerEntity()->get(); - expectedEntityNameSet.insert(containerEntity.GetName()); + expectedEntityNameSet.insert(thirdInstance->GetContainerEntity()->get().GetName()); //Create Spawnable auto& prefabDom = m_prefabSystemComponent->FindTemplateDom(thirdInstance->GetTemplateId()); AzFramework::Spawnable spawnable; AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(spawnable, prefabDom); - EXPECT_EQ(spawnable.GetEntities().size() - 1, normalEntityCount); // 1 for container entity + EXPECT_EQ(spawnable.GetEntities().size(), normalEntityCount + 3); // +1 for each container entity const auto& spawnableEntities = spawnable.GetEntities(); AZStd::unordered_set actualEntityNameSet; @@ -97,6 +102,6 @@ namespace UnitTest actualEntityNameSet.insert(spawnableEntity->GetName()); } - EXPECT_EQ(expectedEntityNameSet, actualEntityNameSet); + EXPECT_EQ(actualEntityNameSet, expectedEntityNameSet); } } diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/SpawnableRemoveEditorInfoTestFixture.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/SpawnableRemoveEditorInfoTestFixture.cpp index 152d09e9b7..03ce52cfd0 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/SpawnableRemoveEditorInfoTestFixture.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/SpawnableRemoveEditorInfoTestFixture.cpp @@ -213,7 +213,7 @@ namespace UnitTest AZStd::unique_ptr convertedInstance(aznew Instance()); ASSERT_TRUE(AzToolsFramework::Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(*convertedInstance, m_prefabDom)); - convertedInstance->DetachNestedEntities( + convertedInstance->DetachAllEntitiesInHierarchy( [this](AZStd::unique_ptr entity) { m_runtimeEntities.emplace_back(entity.release()); diff --git a/Code/Sandbox/Editor/AssetEditor/AssetEditorWindow.cpp b/Code/Sandbox/Editor/AssetEditor/AssetEditorWindow.cpp index 53e2884943..9f14e471a3 100644 --- a/Code/Sandbox/Editor/AssetEditor/AssetEditorWindow.cpp +++ b/Code/Sandbox/Editor/AssetEditor/AssetEditorWindow.cpp @@ -117,6 +117,8 @@ void AssetEditorWindow::RegisterViewClass() { AzToolsFramework::ViewPaneOptions options; options.preferedDockingArea = Qt::LeftDockWidgetArea; + options.showOnToolsToolbar = true; + options.toolbarIcon = ":/Menu/asset_editor.svg"; AzToolsFramework::RegisterViewPane(LyViewPane::AssetEditor, LyViewPane::CategoryTools, options); } diff --git a/Code/Sandbox/Editor/CMakeLists.txt b/Code/Sandbox/Editor/CMakeLists.txt index 58d5e86a2f..e1fce97067 100644 --- a/Code/Sandbox/Editor/CMakeLists.txt +++ b/Code/Sandbox/Editor/CMakeLists.txt @@ -13,8 +13,6 @@ if(NOT PAL_TRAIT_BUILD_HOST_TOOLS) return() endif() -include(${CMAKE_CURRENT_SOURCE_DIR}/Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) - ly_add_target( NAME EditorCore SHARED NAMESPACE Legacy @@ -161,7 +159,7 @@ ly_add_source_properties( # Editor ################################################################################ ly_add_target( - NAME Editor ${PAL_TRAIT_BUILD_EDITOR_APPLICATION_TYPE} # Mac Editor is a bare executable on Atom until we can fix packaging of the shader compilers. DO NOT MERGE BACK TO MAINLINE + NAME Editor APPLICATION NAMESPACE Legacy AUTORCC FILES_CMAKE diff --git a/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp b/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp index 39c7ae43fd..ebc688f9da 100644 --- a/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp +++ b/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.cpp @@ -1292,7 +1292,7 @@ void LevelEditorMenuHandler::AddEditMenuAction(QAction* action) } } -void LevelEditorMenuHandler::AddMenuAction(AZStd::string_view categoryId, QAction* action) +void LevelEditorMenuHandler::AddMenuAction(AZStd::string_view categoryId, QAction* action, bool addToToolsToolbar) { auto menuWrapper = m_actionManager->FindMenu(categoryId.data()); if (menuWrapper.isNull()) @@ -1301,6 +1301,11 @@ void LevelEditorMenuHandler::AddMenuAction(AZStd::string_view categoryId, QActio return; } menuWrapper.Get()->addAction(action); + + if (addToToolsToolbar) + { + m_mainWindow->GetToolbarManager()->AddButtonToEditToolbar(action); + } } void LevelEditorMenuHandler::RestoreEditMenuToDefault() diff --git a/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.h b/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.h index bad689ae67..aaa2c08622 100644 --- a/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.h +++ b/Code/Sandbox/Editor/Core/LevelEditorMenuHandler.h @@ -98,7 +98,7 @@ private: // EditorMenuRequestBus void AddEditMenuAction(QAction* action) override; - void AddMenuAction(AZStd::string_view categoryId, QAction* action) override; + void AddMenuAction(AZStd::string_view categoryId, QAction* action, bool addToToolsToolbar) override; void RestoreEditMenuToDefault() override; MainWindow* m_mainWindow; diff --git a/Code/Sandbox/Editor/CryEditDoc.cpp b/Code/Sandbox/Editor/CryEditDoc.cpp index 5b2a4b9a83..77d5794ab3 100644 --- a/Code/Sandbox/Editor/CryEditDoc.cpp +++ b/Code/Sandbox/Editor/CryEditDoc.cpp @@ -1139,7 +1139,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename) const QString oldLevelPattern = QDir(oldLevelFolder).absoluteFilePath("*.*"); const QString oldLevelName = Path::GetFile(GetLevelPathName()); const QString oldLevelXml = Path::ReplaceExtension(oldLevelName, "xml"); - AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), 0, true); + AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskAndInZips); if (findHandle) { do diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index 0d3405f8f0..7e087d452b 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -2664,6 +2664,18 @@ bool EditorViewportWidget::GetActiveCameraPosition(AZ::Vector3& cameraPos) return false; } +bool EditorViewportWidget::GetActiveCameraState(AzFramework::CameraState& cameraState) +{ + if (m_pPrimaryViewport == this) + { + cameraState = GetCameraState(); + + return true; + } + + return false; +} + void EditorViewportWidget::OnStartPlayInEditor() { if (m_viewEntityId.IsValid()) diff --git a/Code/Sandbox/Editor/EditorViewportWidget.h b/Code/Sandbox/Editor/EditorViewportWidget.h index 4062a3c801..5d6f06c9ae 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.h +++ b/Code/Sandbox/Editor/EditorViewportWidget.h @@ -184,6 +184,7 @@ public: void SetViewAndMovementLockFromEntityPerspective(const AZ::EntityId& entityId, bool lockCameraMovement) override; AZ::EntityId GetCurrentViewEntityId() override { return m_viewEntityId; } bool GetActiveCameraPosition(AZ::Vector3& cameraPos) override; + bool GetActiveCameraState(AzFramework::CameraState& cameraState) override; // AzToolsFramework::EditorEntityContextNotificationBus (handler moved to cpp to resolve link issues in unity builds) virtual void OnStartPlayInEditor(); diff --git a/Code/Sandbox/Editor/GameEngine.cpp b/Code/Sandbox/Editor/GameEngine.cpp index c338a2e28d..6649de1107 100644 --- a/Code/Sandbox/Editor/GameEngine.cpp +++ b/Code/Sandbox/Editor/GameEngine.cpp @@ -627,6 +627,8 @@ void CGameEngine::SwitchToInGame() &AzFramework::InputSystemCursorRequests::SetSystemCursorState, AzFramework::SystemCursorState::ConstrainedAndHidden); } + + Log("Entered game mode"); } void CGameEngine::SwitchToInEditor() @@ -681,6 +683,8 @@ void CGameEngine::SwitchToInEditor() AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id, &AzFramework::InputSystemCursorRequests::SetSystemCursorState, AzFramework::SystemCursorState::UnconstrainedAndVisible); + + Log("Exited game mode"); } void CGameEngine::HandleQuitRequest(IConsoleCmdArgs* /*args*/) diff --git a/Code/Sandbox/Editor/MainWindow.cpp b/Code/Sandbox/Editor/MainWindow.cpp index 3753c29064..39a98236c7 100644 --- a/Code/Sandbox/Editor/MainWindow.cpp +++ b/Code/Sandbox/Editor/MainWindow.cpp @@ -833,13 +833,6 @@ void MainWindow::InitActions() .Connect(&QAction::triggered, []() { SandboxEditor::SetAngleSnapping(!SandboxEditor::AngleSnappingEnabled()); }); // Display actions - am->AddAction(ID_WIREFRAME, tr("&Wireframe")) - .SetShortcut(tr("F3")) - .SetToolTip(tr("Wireframe (F3)")) - .SetCheckable(true) - .SetStatusTip(tr("Render in Wireframe Mode.")) - .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateWireframe); - am->AddAction(ID_SWITCHCAMERA_DEFAULTCAMERA, tr("Default Camera")).SetCheckable(true) .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateSwitchToDefaultCamera); am->AddAction(ID_SWITCHCAMERA_SEQUENCECAMERA, tr("Sequence Camera")).SetCheckable(true) diff --git a/Code/Sandbox/Editor/Platform/Linux/PAL_linux.cmake b/Code/Sandbox/Editor/Platform/Linux/PAL_linux.cmake deleted file mode 100644 index 5181f4afb7..0000000000 --- a/Code/Sandbox/Editor/Platform/Linux/PAL_linux.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -set (PAL_TRAIT_BUILD_EDITOR_APPLICATION_TYPE APPLICATION) - diff --git a/Code/Sandbox/Editor/Platform/Mac/PAL_mac.cmake b/Code/Sandbox/Editor/Platform/Mac/PAL_mac.cmake deleted file mode 100644 index 5181f4afb7..0000000000 --- a/Code/Sandbox/Editor/Platform/Mac/PAL_mac.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -set (PAL_TRAIT_BUILD_EDITOR_APPLICATION_TYPE APPLICATION) - diff --git a/Code/Sandbox/Editor/Platform/Windows/PAL_windows.cmake b/Code/Sandbox/Editor/Platform/Windows/PAL_windows.cmake deleted file mode 100644 index 5181f4afb7..0000000000 --- a/Code/Sandbox/Editor/Platform/Windows/PAL_windows.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -set (PAL_TRAIT_BUILD_EDITOR_APPLICATION_TYPE APPLICATION) - diff --git a/Code/Sandbox/Editor/RenderViewport.cpp b/Code/Sandbox/Editor/RenderViewport.cpp index 8cef9e802c..bd30bc7c79 100644 --- a/Code/Sandbox/Editor/RenderViewport.cpp +++ b/Code/Sandbox/Editor/RenderViewport.cpp @@ -49,6 +49,7 @@ // AzToolsFramework #include #include +#include #include #include #include @@ -112,20 +113,20 @@ static const char TextCantCreateCameraNoLevel[] = "Cannot create camera when no class EditorEntityNotifications : public AzToolsFramework::EditorEntityContextNotificationBus::Handler - , public AzToolsFramework::EditorEvents::Bus::Handler + , public AzToolsFramework::EditorContextMenuBus::Handler { public: EditorEntityNotifications(CRenderViewport& renderViewport) : m_renderViewport(renderViewport) { AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect(); - AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + AzToolsFramework::EditorContextMenuBus::Handler::BusConnect(); } ~EditorEntityNotifications() override { AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect(); - AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + AzToolsFramework::EditorContextMenuBus::Handler::BusDisconnect(); } // AzToolsFramework::EditorEntityContextNotificationBus @@ -138,7 +139,7 @@ public: m_renderViewport.OnStopPlayInEditor(); } - // AzToolsFramework::EditorEvents::Bus + // AzToolsFramework::EditorContextMenu::Bus void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override { m_renderViewport.PopulateEditorGlobalContextMenu(menu, point, flags); diff --git a/Code/Sandbox/Editor/RenderViewport.h b/Code/Sandbox/Editor/RenderViewport.h index b45c92b1c8..d7d362fe27 100644 --- a/Code/Sandbox/Editor/RenderViewport.h +++ b/Code/Sandbox/Editor/RenderViewport.h @@ -179,7 +179,7 @@ public: virtual void OnStartPlayInEditor(); virtual void OnStopPlayInEditor(); - // AzToolsFramework::EditorEvents::Bus (handler moved to cpp to resolve link issues in unity builds) + // AzToolsFramework::EditorContextMenu::Bus (handler moved to cpp to resolve link issues in unity builds) // We use this to determine when the viewport context menu is being displayed so we can exit move mode void PopulateEditorGlobalContextMenu(QMenu* /*menu*/, const AZ::Vector2& /*point*/, int /*flags*/); diff --git a/Code/Sandbox/Editor/TrackView/TrackViewDialog.cpp b/Code/Sandbox/Editor/TrackView/TrackViewDialog.cpp index 0179b779e8..9176520b15 100644 --- a/Code/Sandbox/Editor/TrackView/TrackViewDialog.cpp +++ b/Code/Sandbox/Editor/TrackView/TrackViewDialog.cpp @@ -116,6 +116,8 @@ void CTrackViewDialog::RegisterViewClass() AzToolsFramework::ViewPaneOptions opts; opts.shortcut = QKeySequence(Qt::Key_T); opts.isDisabledInSimMode = true; + opts.showOnToolsToolbar = true; + opts.toolbarIcon = ":/Menu/trackview_editor.svg"; AzToolsFramework::RegisterViewPane(LyViewPane::TrackView, LyViewPane::CategoryTools, opts); GetIEditor()->GetSettingsManager()->AddToolName(s_kTrackViewLayoutSection, LyViewPane::TrackView); diff --git a/Code/Sandbox/Editor/ViewportTitleDlg.cpp b/Code/Sandbox/Editor/ViewportTitleDlg.cpp index 716568b5c7..dc4815eb6a 100644 --- a/Code/Sandbox/Editor/ViewportTitleDlg.cpp +++ b/Code/Sandbox/Editor/ViewportTitleDlg.cpp @@ -154,11 +154,6 @@ void CViewportTitleDlg::SetupCameraDropdownMenu() QAction* gotoPositionAction = new QAction("Go to position", cameraMenu); connect(gotoPositionAction, &QAction::triggered, this, &CViewportTitleDlg::OnBnClickedGotoPosition); cameraMenu->addAction(gotoPositionAction); - m_syncPlayerToCameraAction = new QAction("Sync camera to player", cameraMenu); - m_syncPlayerToCameraAction->setCheckable(true); - connect(m_syncPlayerToCameraAction, &QAction::triggered, this, &CViewportTitleDlg::OnBnClickedSyncplayer); - cameraMenu->addAction(m_syncPlayerToCameraAction); - cameraMenu->addSeparator(); auto cameraSpeedActionWidget = new QWidgetAction(cameraMenu); @@ -238,21 +233,15 @@ void CViewportTitleDlg::SetupOverflowMenu() overFlowMenu->addAction(m_enableGridSnappingAction); m_gridSizeActionWidget = new QWidgetAction(overFlowMenu); - auto gridSizeContainer = new QWidget(overFlowMenu); - auto gridSizeLabel = new QLabel(tr("Grid Size"), overFlowMenu); - m_gridSpinBox = new AzQtComponents::DoubleSpinBox(); m_gridSpinBox->setValue(SandboxEditor::GridSnappingSize()); m_gridSpinBox->setMinimum(1e-2f); + m_gridSpinBox->setToolTip(tr("Grid size")); QObject::connect( m_gridSpinBox, QOverload::of(&AzQtComponents::DoubleSpinBox::valueChanged), this, &CViewportTitleDlg::OnGridSpinBoxChanged); - QHBoxLayout* gridSizeLayout = new QHBoxLayout; - gridSizeLayout->addWidget(gridSizeLabel); - gridSizeLayout->addWidget(m_gridSpinBox); - gridSizeContainer->setLayout(gridSizeLayout); - m_gridSizeActionWidget->setDefaultWidget(gridSizeContainer); + m_gridSizeActionWidget->setDefaultWidget(m_gridSpinBox); overFlowMenu->addAction(m_gridSizeActionWidget); overFlowMenu->addSeparator(); @@ -263,22 +252,16 @@ void CViewportTitleDlg::SetupOverflowMenu() overFlowMenu->addAction(m_enableAngleSnappingAction); m_angleSizeActionWidget = new QWidgetAction(overFlowMenu); - auto angleSizeContainer = new QWidget(overFlowMenu); - auto angleSizeLabel = new QLabel(tr("Angle Snapping"), overFlowMenu); - m_angleSpinBox = new AzQtComponents::DoubleSpinBox(); m_angleSpinBox->setValue(SandboxEditor::AngleSnappingSize()); m_angleSpinBox->setMinimum(1e-2f); + m_angleSpinBox->setToolTip(tr("Angle Snapping")); QObject::connect( m_angleSpinBox, QOverload::of(&AzQtComponents::DoubleSpinBox::valueChanged), this, &CViewportTitleDlg::OnAngleSpinBoxChanged); - QHBoxLayout* angleSizeLayout = new QHBoxLayout; - angleSizeLayout->addWidget(angleSizeLabel); - angleSizeLayout->addWidget(m_angleSpinBox); - angleSizeContainer->setLayout(angleSizeLayout); - m_angleSizeActionWidget->setDefaultWidget(angleSizeContainer); + m_angleSizeActionWidget->setDefaultWidget(m_angleSpinBox); overFlowMenu->addAction(m_angleSizeActionWidget); m_ui->m_overflowBtn->setMenu(overFlowMenu); @@ -850,14 +833,6 @@ bool CViewportTitleDlg::eventFilter(QObject* object, QEvent* event) return QWidget::eventFilter(object, event) || consumeEvent; } -void CViewportTitleDlg::OnBnClickedSyncplayer() -{ - emit ActionTriggered(ID_GAME_SYNCPLAYER); - - bool bSyncPlayer = GetIEditor()->GetGameEngine()->IsSyncPlayerPosition(); - m_syncPlayerToCameraAction->setChecked(!bSyncPlayer); -} - void CViewportTitleDlg::OnBnClickedGotoPosition() { emit ActionTriggered(ID_DISPLAY_GOTOPOSITION); diff --git a/Code/Sandbox/Editor/ViewportTitleDlg.h b/Code/Sandbox/Editor/ViewportTitleDlg.h index dd0816082a..255354dcbb 100644 --- a/Code/Sandbox/Editor/ViewportTitleDlg.h +++ b/Code/Sandbox/Editor/ViewportTitleDlg.h @@ -115,10 +115,10 @@ protected: float m_prevMoveSpeed; // Speed combobox/lineEdit settings - double m_minSpeed = 0.1; + double m_minSpeed = 0.01; double m_maxSpeed = 100.0; - double m_speedStep = 0.1; - int m_numDecimals = 1; + double m_speedStep = 0.01; + int m_numDecimals = 3; // Speed presets float m_speedPresetValues[3] = { 0.1f, 1.0f, 10.0f }; @@ -143,7 +143,6 @@ protected: void SetFullViewportInfo(); void SetCompactViewportInfo(); - void OnBnClickedSyncplayer(); void OnBnClickedGotoPosition(); void OnBnClickedMuteAudio(); void OnBnClickedEnableVR(); @@ -174,7 +173,6 @@ protected: QAction* m_fullInformationAction = nullptr; QAction* m_compactInformationAction = nullptr; QAction* m_debugHelpersAction = nullptr; - QAction* m_syncPlayerToCameraAction = nullptr; QAction* m_audioMuteAction = nullptr; QAction* m_enableVRAction = nullptr; QAction* m_enableGridSnappingAction = nullptr; diff --git a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp index 694714cc6e..3b04fc70f8 100644 --- a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp +++ b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp @@ -167,7 +167,7 @@ void SandboxIntegrationManager::Setup() AzToolsFramework::ToolsApplicationEvents::Bus::Handler::BusConnect(); AzToolsFramework::EditorRequests::Bus::Handler::BusConnect(); AzToolsFramework::EditorWindowRequests::Bus::Handler::BusConnect(); - AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + AzToolsFramework::EditorContextMenuBus::Handler::BusConnect(); AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect(); AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusConnect(); @@ -384,7 +384,7 @@ void SandboxIntegrationManager::Teardown() AzFramework::DisplayContextRequestBus::Handler::BusDisconnect(); AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusDisconnect(); AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect(); - AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + AzToolsFramework::EditorContextMenuBus::Handler::BusDisconnect(); AzToolsFramework::EditorWindowRequests::Bus::Handler::BusDisconnect(); AzToolsFramework::EditorRequests::Bus::Handler::BusDisconnect(); AzToolsFramework::ToolsApplicationEvents::Bus::Handler::BusDisconnect(); @@ -589,6 +589,11 @@ void SandboxIntegrationManager::OnSaveLevel() m_unsavedEntities.clear(); } +int SandboxIntegrationManager::GetMenuPosition() const +{ + return aznumeric_cast(AzToolsFramework::EditorContextMenuOrdering::TOP); +} + void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) { if (!IsLevelDocumentOpen()) @@ -662,13 +667,6 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con SetupSliceContextMenu(menu); } - else - { - menu->addSeparator(); - - // Allow handlers to append menu items to the context menu - AzToolsFramework::EditorContextMenuBus::Broadcast(&AzToolsFramework::EditorContextMenuEvents::PopulateEditorGlobalContextMenu, menu); - } action = menu->addAction(QObject::tr("Duplicate")); QObject::connect(action, &QAction::triggered, action, [this] { ContextMenu_Duplicate(); }); diff --git a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h index 6d714b67df..5cb03cc99f 100644 --- a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h +++ b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -97,7 +98,7 @@ class SandboxIntegrationManager : private AzToolsFramework::ToolsApplicationEvents::Bus::Handler , private AzToolsFramework::EditorRequests::Bus::Handler , private AzToolsFramework::EditorPickModeNotificationBus::Handler - , private AzToolsFramework::EditorEvents::Bus::Handler + , private AzToolsFramework::EditorContextMenuBus::Handler , private AzToolsFramework::EditorWindowRequests::Bus::Handler , private AzFramework::AssetCatalogEventBus::Handler , private AzFramework::DisplayContextRequestBus::Handler @@ -184,8 +185,9 @@ private: void OnEntityPickModeStopped() override; ////////////////////////////////////////////////////////////////////////// - // AzToolsFramework::EditorEvents::Bus::Handler overrides + // AzToolsFramework::EditorContextMenu::Bus::Handler overrides void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override; + int GetMenuPosition() const; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.cpp b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.cpp index fb8084cb6f..33126c7f3e 100644 --- a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.cpp +++ b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerWidget.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -530,8 +531,8 @@ void OutlinerWidget::contextMenuEvent(QContextMenuEvent* event) QMenu* contextMenu = new QMenu(this); // Populate global context menu. - EBUS_EVENT(AzToolsFramework::EditorEvents::Bus, - PopulateEditorGlobalContextMenu, + + AzToolsFramework::EditorContextMenuBus::Broadcast(&AzToolsFramework::EditorContextMenuEvents::PopulateEditorGlobalContextMenu, contextMenu, AZ::Vector2::CreateZero(), AzToolsFramework::EditorEvents::eECMF_HIDE_ENTITY_CREATION | AzToolsFramework::EditorEvents::eECMF_USE_VIEWPORT_CENTER); diff --git a/Code/Tools/AssetProcessor/CMakeLists.txt b/Code/Tools/AssetProcessor/CMakeLists.txt index 2db888218b..5e5ede1397 100644 --- a/Code/Tools/AssetProcessor/CMakeLists.txt +++ b/Code/Tools/AssetProcessor/CMakeLists.txt @@ -259,8 +259,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_add_googletest( NAME AZ::AssetProcessor.Tests - TEST_COMMAND $ --unittest + TEST_COMMAND $ --unittest --gtest_filter=-*.SUITE_sandbox* ) - endif() diff --git a/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_linux.cpp b/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_linux.cpp index 5029db6603..6e71897ab7 100644 --- a/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_linux.cpp +++ b/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_linux.cpp @@ -11,9 +11,127 @@ */ #include +#include +#include +#include + +#include +#include + + +static constexpr int s_handleToFolderMapLockTimeout = 1000; // 1 sec timeout for obtaining the handle to folder map lock +static constexpr size_t s_iNotifyMaxEntries = 1024 * 16; // Control the maximum number of entries (from inotify) that can be read at one time +static constexpr size_t s_iNotifyEventSize = sizeof(struct inotify_event); +static constexpr size_t s_iNotifyReadBufferSize = s_iNotifyMaxEntries * s_iNotifyEventSize; + struct FolderRootWatch::PlatformImplementation { - PlatformImplementation() { } + PlatformImplementation() = default; + + int m_iNotifyHandle = -1; + QMutex m_handleToFolderMapLock; + QHash m_handleToFolderMap; + + bool Initialize() + { + if (m_iNotifyHandle < 0) + { + m_iNotifyHandle = inotify_init(); + } + return (m_iNotifyHandle >= 0); + } + + void Finalize() + { + if (m_iNotifyHandle >= 0) + { + if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout)) + { + AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread"); + return; + } + + QHashIterator iter(m_handleToFolderMap); + while (iter.hasNext()) + { + iter.next(); + int watchHandle = iter.key(); + inotify_rm_watch(m_iNotifyHandle, watchHandle); + } + m_handleToFolderMap.clear(); + m_handleToFolderMapLock.unlock(); + + ::close(m_iNotifyHandle); + m_iNotifyHandle = -1; + } + } + + void AddWatchFolder(QString folder) + { + if (m_iNotifyHandle >= 0) + { + // Clean up the path before accepting it as a watch folder + QString cleanPath = QDir::cleanPath(folder); + + // Add the folder to watch and track it + int watchHandle = inotify_add_watch(m_iNotifyHandle, + cleanPath.toUtf8().constData(), + IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY); + + if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout)) + { + AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread"); + return; + } + m_handleToFolderMap[watchHandle] = cleanPath; + m_handleToFolderMapLock.unlock(); + + // Add all the subfolders to watch and track them + QDirIterator dirIter(folder, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks); + + while (dirIter.hasNext()) + { + QString dirName = dirIter.next(); + if (dirName.endsWith("/.") || dirName.endsWith("/..")) + { + continue; + } + + int watchHandle = inotify_add_watch(m_iNotifyHandle, + dirName.toUtf8().constData(), + IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY); + + if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout)) + { + AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread"); + return; + } + m_handleToFolderMap[watchHandle] = dirName; + m_handleToFolderMapLock.unlock(); + } + } + } + + void RemoveWatchFolder(int watchHandle) + { + if (m_iNotifyHandle >= 0) + { + if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout)) + { + AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread"); + return; + } + + QHash::iterator handleToRemove = m_handleToFolderMap.find(watchHandle); + if (handleToRemove != m_handleToFolderMap.end()) + { + inotify_rm_watch(m_iNotifyHandle, watchHandle); + m_handleToFolderMap.erase(handleToRemove); + } + + m_handleToFolderMapLock.unlock(); + } + } }; ////////////////////////////////////////////////////////////////////////////// @@ -36,17 +154,86 @@ FolderRootWatch::~FolderRootWatch() bool FolderRootWatch::Start() { - // TODO: Implement for Linux - return false; + // inotify will be used by linux to monitor file changes within directories under the root folder + if (!m_platformImpl->Initialize()) + { + return false; + } + m_platformImpl->AddWatchFolder(m_root); + + m_shutdownThreadSignal = false; + m_thread = std::thread([this]() { WatchFolderLoop(); }); + return true; } void FolderRootWatch::Stop() { - // TODO: Implement for Linux + m_shutdownThreadSignal = true; + + m_platformImpl->Finalize(); + + if (m_thread.joinable()) + { + m_thread.join(); // wait for the thread to finish + m_thread = std::thread(); //destroy + } } + void FolderRootWatch::WatchFolderLoop() { - // TODO: Implement for Linux + char eventBuffer[s_iNotifyReadBufferSize]; + while (!m_shutdownThreadSignal) + { + ssize_t bytesRead = ::read(m_platformImpl->m_iNotifyHandle, eventBuffer, s_iNotifyReadBufferSize); + if (bytesRead < 0) + { + // Break out of the loop when the notify handle was closed (outside of this thread) + break; + } + else if (bytesRead > 0) + { + for (size_t index=0; indexname; + + if (event->mask & (IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVE )) + { + QString pathStr = QString("%1%2%3").arg(m_platformImpl->m_handleToFolderMap[event->wd], QDir::separator(), event->name); + + if (event->mask & (IN_CREATE | IN_MOVED_TO)) + { + if ( event->mask & IN_ISDIR ) + { + // New Directory, add it to the watch + m_platformImpl->AddWatchFolder(pathStr); + } + else + { + ProcessNewFileEvent(pathStr); + } + } + else if (event->mask & (IN_DELETE | IN_MOVED_FROM)) + { + if (event->mask & IN_ISDIR) + { + // Directory Deleted, remove it from the watch + m_platformImpl->RemoveWatchFolder(event->wd); + } + else + { + ProcessDeleteFileEvent(pathStr); + } + } + else if ((event->mask & IN_MODIFY) && ((event->mask & IN_ISDIR) != IN_ISDIR)) + { + ProcessModifyFileEvent(pathStr); + } + } + index += s_iNotifyEventSize + event->len; + } + } + } } diff --git a/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_win.cpp b/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_win.cpp deleted file mode 100644 index fbcfe140fd..0000000000 --- a/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_win.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#include - -#include - -struct FolderRootWatch::PlatformImplementation -{ - PlatformImplementation() : m_directoryHandle(nullptr), m_ioHandle(nullptr) { } - HANDLE m_directoryHandle; - HANDLE m_ioHandle; -}; - -////////////////////////////////////////////////////////////////////////////// -/// FolderWatchRoot -FolderRootWatch::FolderRootWatch(const QString rootFolder) - : m_root(rootFolder) - , m_shutdownThreadSignal(false) - , m_fileWatcher(nullptr) - , m_platformImpl(new PlatformImplementation()) -{ -} - -FolderRootWatch::~FolderRootWatch() -{ - // Destructor is required in here since this file contains the definition of struct PlatformImplementation - Stop(); - - delete m_platformImpl; -} - -bool FolderRootWatch::Start() -{ - m_platformImpl->m_directoryHandle = ::CreateFileW(m_root.toStdWString().data(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr); - - if (m_platformImpl->m_directoryHandle != INVALID_HANDLE_VALUE) - { - m_platformImpl->m_ioHandle = ::CreateIoCompletionPort(m_platformImpl->m_directoryHandle, nullptr, 1, 0); - if (m_platformImpl->m_ioHandle != INVALID_HANDLE_VALUE) - { - m_shutdownThreadSignal = false; - m_thread = std::thread(std::bind(&FolderRootWatch::WatchFolderLoop, this)); - return true; - } - } - return false; -} - -void FolderRootWatch::Stop() -{ - m_shutdownThreadSignal = true; - CloseHandle(m_platformImpl->m_ioHandle); - m_platformImpl->m_ioHandle = nullptr; - - if (m_thread.joinable()) - { - m_thread.join(); // wait for the thread to finish - m_thread = std::thread(); //destroy - } - CloseHandle(m_platformImpl->m_directoryHandle); - m_platformImpl->m_directoryHandle = nullptr; -} - -void FolderRootWatch::WatchFolderLoop() -{ - FILE_NOTIFY_INFORMATION aFileNotifyInformationList[50000]; - QString path; - OVERLAPPED aOverlapped; - LPOVERLAPPED pOverlapped; - DWORD dwByteCount; - ULONG_PTR ulKey; - - while (!m_shutdownThreadSignal) - { - ::memset(aFileNotifyInformationList, 0, sizeof(aFileNotifyInformationList)); - ::memset(&aOverlapped, 0, sizeof(aOverlapped)); - - if (::ReadDirectoryChangesW(m_platformImpl->m_directoryHandle, aFileNotifyInformationList, sizeof(aFileNotifyInformationList), true, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_FILE_NAME, nullptr, &aOverlapped, nullptr)) - { - //wait for up to a second for I/O to signal - dwByteCount = 0; - if (::GetQueuedCompletionStatus(m_platformImpl->m_ioHandle, &dwByteCount, &ulKey, &pOverlapped, INFINITE)) - { - //if we are signaled to shutdown bypass - if (!m_shutdownThreadSignal && ulKey) - { - if (dwByteCount) - { - int offset = 0; - FILE_NOTIFY_INFORMATION* pFileNotifyInformation = aFileNotifyInformationList; - do - { - pFileNotifyInformation = (FILE_NOTIFY_INFORMATION*)((char*)pFileNotifyInformation + offset); - - path.clear(); - path.append(m_root); - path.append(QString::fromWCharArray(pFileNotifyInformation->FileName, pFileNotifyInformation->FileNameLength / 2)); - - QString file = QDir::toNativeSeparators(QDir::cleanPath(path)); - - switch (pFileNotifyInformation->Action) - { - case FILE_ACTION_ADDED: - case FILE_ACTION_RENAMED_NEW_NAME: - ProcessNewFileEvent(file); - break; - case FILE_ACTION_REMOVED: - case FILE_ACTION_RENAMED_OLD_NAME: - ProcessDeleteFileEvent(file); - break; - case FILE_ACTION_MODIFIED: - ProcessModifyFileEvent(file); - break; - } - - offset = pFileNotifyInformation->NextEntryOffset; - } while (offset); - } - } - } - } - } -} - diff --git a/Code/Tools/AssetProcessor/native/AssetManager/PathDependencyManager.cpp b/Code/Tools/AssetProcessor/native/AssetManager/PathDependencyManager.cpp index de8bde9b05..2345c9e285 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/PathDependencyManager.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/PathDependencyManager.cpp @@ -445,10 +445,10 @@ namespace AssetProcessor bool isExcludedDependency = dependencyPathSearch.starts_with(ExcludedDependenciesSymbol); dependencyPathSearch = isExcludedDependency ? dependencyPathSearch.substr(1) : dependencyPathSearch; bool isExactDependency = !AzFramework::StringFunc::Replace(dependencyPathSearch, '*', '%'); - SanitizeForDatabase(dependencyPathSearch); if (cleanedupDependency.m_dependencyType == AssetBuilderSDK::ProductPathDependencyType::ProductFile) { + SanitizeForDatabase(dependencyPathSearch); AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer productInfoContainer; QString productNameWithPlatform = QString("%1%2%3").arg(platform.c_str(), AZ_CORRECT_DATABASE_SEPARATOR_STRING, dependencyPathSearch.c_str()); @@ -508,6 +508,10 @@ namespace AssetProcessor } else { + // For source assets, the casing of the input path must be maintained. Just fix up the path separators. + AZStd::replace(dependencyPathSearch.begin(), dependencyPathSearch.end(), AZ_WRONG_DATABASE_SEPARATOR, AZ_CORRECT_DATABASE_SEPARATOR); + AzFramework::StringFunc::Replace(dependencyPathSearch, AZ_DOUBLE_CORRECT_DATABASE_SEPARATOR, AZ_CORRECT_DATABASE_SEPARATOR_STRING); + // See if path matches any source files AzToolsFramework::AssetDatabase::SourceDatabaseEntryContainer sourceInfoContainer; diff --git a/Code/Tools/AssetProcessor/native/AssetManager/SourceFileRelocator.cpp b/Code/Tools/AssetProcessor/native/AssetManager/SourceFileRelocator.cpp index 49c7ef7f20..c0300b2b2a 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/SourceFileRelocator.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/SourceFileRelocator.cpp @@ -190,44 +190,55 @@ Please note that only those seed files will get updated that are active for your void SourceFileRelocator::HandleMetaDataFiles(QStringList pathMatches, QHash& sourceIndexMap, const ScanFolderInfo* scanFolderInfo, SourceFileRelocationContainer& metadataFiles, bool excludeMetaDataFiles) const { QSet metaDataFileEntries; - for (QString file : pathMatches) + + // Remove all the metadata files + if (excludeMetaDataFiles) { - for (int idx = 0; idx < m_platformConfig->MetaDataFileTypesCount(); idx++) - { - QPair metaInfo = m_platformConfig->GetMetaDataFileTypeAt(idx); - if (file.endsWith("." + metaInfo.first, Qt::CaseInsensitive)) + pathMatches.erase(AZStd::remove_if(pathMatches.begin(), pathMatches.end(), [this](const QString& file) { - //it is a metadata file - if (excludeMetaDataFiles) - { - AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Metadata file %s will be ignored because --excludeMetadataFiles was specified in the command line.\n", - file.toUtf8().constData()); - break; // don't check it against other metafile entries, we've already ascertained its a metafile. - } - else + for (int idx = 0; idx < m_platformConfig->MetaDataFileTypesCount(); idx++) { - QString normalizedFilePath = AssetUtilities::NormalizeFilePath(file); - if (metaDataFileEntries.find(normalizedFilePath) == metaDataFileEntries.end()) + const auto& [metadataType, extension] = m_platformConfig->GetMetaDataFileTypeAt(idx); + if (file.endsWith("." + metadataType, Qt::CaseInsensitive)) { - SourceFileRelocationInfo metaDataFile(file.toUtf8().data(), scanFolderInfo); - metaDataFile.m_isMetaDataFile = true; - metadataFiles.emplace_back(metaDataFile); - metaDataFileEntries.insert(normalizedFilePath); + AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Metadata file %s will be ignored because --excludeMetadataFiles was specified in the command line.\n", + file.toUtf8().constData()); + return true; } } + return false; + }), + pathMatches.end()); + } + + for (const QString& file : pathMatches) + { + for (int idx = 0; idx < m_platformConfig->MetaDataFileTypesCount(); idx++) + { + const auto& [metadataType, extension] = m_platformConfig->GetMetaDataFileTypeAt(idx); + if (file.endsWith("." + metadataType, Qt::CaseInsensitive)) + { + const QString normalizedFilePath = AssetUtilities::NormalizeFilePath(file); + if (!metaDataFileEntries.contains(normalizedFilePath)) + { + SourceFileRelocationInfo metaDataFile(file.toUtf8().data(), scanFolderInfo); + metaDataFile.m_isMetaDataFile = true; + metadataFiles.emplace_back(metaDataFile); + metaDataFileEntries.insert(normalizedFilePath); + } } - else if (!excludeMetaDataFiles && (file.endsWith("." + metaInfo.second, Qt::CaseInsensitive) || metaInfo.second.isEmpty())) + else if (!excludeMetaDataFiles && (file.endsWith("." + extension, Qt::CaseInsensitive) || extension.isEmpty())) { // if we are here it implies that a metadata file might exists for this source file, // add metadata file only if it exists and is not added already AZStd::string metadataFilePath(file.toUtf8().data()); - if (metaInfo.second.isEmpty()) + if (extension.isEmpty()) { - metadataFilePath.append(AZStd::string::format(".%s", metaInfo.first.toUtf8().data())); + metadataFilePath.append(AZStd::string::format(".%s", metadataType.toUtf8().data())); } else { - AZ::StringFunc::Path::ReplaceExtension(metadataFilePath, metaInfo.first.toUtf8().data()); + AZ::StringFunc::Path::ReplaceExtension(metadataFilePath, metadataType.toUtf8().data()); }; // The metadata file can have a different case than the source file, diff --git a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp index 47f79d9709..25ec36c888 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp @@ -1697,7 +1697,10 @@ namespace AssetProcessor if(productFileInfo.absoluteDir().entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot).empty()) { - productFileInfo.absoluteDir().rmdir("."); + const QDir productDir = productFileInfo.absoluteDir(); + QDir parentDir = productDir; + parentDir.cdUp(); + successfullyRemoved &= parentDir.rmdir(productDir.dirName()); } if (successfullyRemoved) diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp index 04949c82a9..8879697faa 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp @@ -28,7 +28,7 @@ namespace AssetProcessorMessagesTests using namespace AssetProcessor; using namespace AssetBuilderSDK; - static constexpr unsigned short AssetProcessorPort = static_cast(888u); + static constexpr unsigned short AssetProcessorPort{65535u}; class AssetProcessorMessages; @@ -85,7 +85,6 @@ namespace AssetProcessorMessagesTests public: void SetUp() override { -#if !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS AssetUtilities::ResetGameName(); m_temporarySourceDir = QDir(m_temporaryDir.path()); @@ -166,12 +165,10 @@ namespace AssetProcessorMessagesTests }); -#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS } void TearDown() override { -#if !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS QEventLoop eventLoop; QObject::connect(m_batchApplicationManager->m_connectionManager, &ConnectionManager::ReadyToQuit, &eventLoop, &QEventLoop::quit); @@ -182,7 +179,6 @@ namespace AssetProcessorMessagesTests m_assetSystemComponent->Deactivate(); m_batchApplicationManager->Destroy(); -#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS } void RunNetworkRequest(AZStd::function func) const @@ -207,6 +203,7 @@ namespace AssetProcessorMessagesTests thread.join(); } + protected: MockAssetRequestHandler* m_assetRequestHandler{}; // Not owned, AP will delete this pointer @@ -226,11 +223,7 @@ namespace AssetProcessorMessagesTests AZStd::unique_ptr m_response; }; -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS - TEST_F(AssetProcessorMessages, DISABLED_All) -#else TEST_F(AssetProcessorMessages, All) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS { // Test that we can successfully send network messages and have them arrive for processing // For messages that have a response, it also verifies the response comes back @@ -311,11 +304,7 @@ namespace AssetProcessorMessagesTests }); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS - TEST_F(AssetProcessorMessages, DISABLED_GetUnresolvedProductReferences_Succeeds) -#else TEST_F(AssetProcessorMessages, GetUnresolvedProductReferences_Succeeds) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS { using namespace AzToolsFramework::AssetDatabase; diff --git a/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp b/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp index 2731aa6c5a..e9f18a442a 100644 --- a/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp @@ -112,7 +112,6 @@ namespace UnitTests SourceDatabaseEntry sourceFile8 = { m_data->m_scanFolder1.m_scanFolderID, "test.txt", AZ::Uuid::CreateRandom(), "AnalysisFingerprint" }; SourceDatabaseEntry sourceFile9 = { m_data->m_scanFolder1.m_scanFolderID, "duplicate/folder/file1.tif", AZ::Uuid::CreateRandom(), "AnalysisFingerprint" }; SourceDatabaseEntry sourceFile10 = { m_data->m_scanFolder1.m_scanFolderID, "folder/file.foo", AZ::Uuid::CreateRandom(), "AnalysisFingerprint" }; - SourceDatabaseEntry sourceFile11 = { m_data->m_scanFolder1.m_scanFolderID, "testfolder/file.foo", AZ::Uuid::CreateRandom(), "AnalysisFingerprint" }; ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile1)); ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile2)); ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile3)); @@ -123,7 +122,6 @@ namespace UnitTests ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile8)); ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile9)); ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile10)); - ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile11)); SourceFileDependencyEntry dependency1 = { AZ::Uuid::CreateRandom(), "subfolder1/somefile.tif", "subfolder1/otherfile.tif", SourceFileDependencyEntry::TypeOfDependency::DEP_SourceToSource, false }; SourceFileDependencyEntry dependency2 = { AZ::Uuid::CreateRandom(), "subfolder1/otherfile.tif", "otherfile.tif", SourceFileDependencyEntry::TypeOfDependency::DEP_JobToJob, false }; @@ -176,8 +174,6 @@ namespace UnitTests ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/dummy/foo.metadataextension"))); ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/folder/file.foo"))); ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/folder/file.bar"))); - ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/testfolder/file.foo"))); - ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/testfolder/File.bar"))); if (AZ::IO::FileIOBase::GetInstance() == nullptr) { @@ -487,20 +483,12 @@ namespace UnitTests TestGetSourcesByPath("dev/", { }, false); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS - TEST_F(SourceFileRelocatorTest, DISABLED_GetSources_MultipleScanFolders_Fails) -#else TEST_F(SourceFileRelocatorTest, GetSources_MultipleScanFolders_Fails) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS { TestGetSourcesByPath("*", { }, false); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS - TEST_F(SourceFileRelocatorTest, DISABLED_GetSources_PartialPath_FailsWithNoResults) -#else TEST_F(SourceFileRelocatorTest, GetSources_PartialPath_FailsWithNoResults) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS { TestGetSourcesByPath("older/*", { }, false); } @@ -547,18 +535,6 @@ namespace UnitTests TestGetSourcesByPath(filePath.toUtf8().constData(), { "folder/file.foo" }, true, true); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS - TEST_F(SourceFileRelocatorTest, DISABLED_GetSources_HaveMetadataDifferentFileCase_AbsolutePath_Succeeds) -#else - TEST_F(SourceFileRelocatorTest, GetSources_HaveMetadataDifferentFileCase_AbsolutePath_Succeeds) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS - { - QDir tempPath(m_tempDir.path()); - - auto filePath = QDir(tempPath.absoluteFilePath(m_data->m_scanFolder1.m_scanFolder.c_str())).absoluteFilePath("testfolder/file.foo"); - TestGetSourcesByPath(filePath.toUtf8().constData(), { "testfolder/file.foo", "testfolder/File.bar" }, true, false); - } - TEST_F(SourceFileRelocatorTest, GetMetaDataFile_SingleFileWildcard_Succeeds) { QDir tempPath(m_tempDir.path()); @@ -921,32 +897,46 @@ namespace UnitTests ASSERT_FALSE(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.toUtf8().constData())); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS - TEST_F(SourceFileRelocatorTest, DISABLED_Delete_Real_Readonly_Fails) -#else TEST_F(SourceFileRelocatorTest, Delete_Real_Readonly_Fails) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS { + struct AutoResetDirectoryReadOnlyState + { + AutoResetDirectoryReadOnlyState(QString dirName) + : m_dirName(AZStd::move(dirName)) + { + AZ::IO::SystemFile::SetWritable(m_dirName.toUtf8().constData(), false); + } + ~AutoResetDirectoryReadOnlyState() + { + AZ::IO::SystemFile::SetWritable(m_dirName.toUtf8().constData(), true); + } + AZ_DISABLE_COPY_MOVE(AutoResetDirectoryReadOnlyState) + private: + QString m_dirName; + }; + QDir tempPath(m_tempDir.path()); auto filePath = QDir(tempPath.absoluteFilePath(m_data->m_scanFolder1.m_scanFolder.c_str())).absoluteFilePath("duplicate/file1.tif"); ASSERT_TRUE(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.toUtf8().constData())); + AutoResetDirectoryReadOnlyState readOnlyResetter(QFileInfo(filePath).absoluteDir().absolutePath()); + AZ::IO::SystemFile::SetWritable(filePath.toUtf8().constData(), false); auto result = m_data->m_reporter->Delete(filePath.toUtf8().constData(), false); - ASSERT_TRUE(result.IsSuccess()); + EXPECT_TRUE(result.IsSuccess()); RelocationSuccess successResult = result.TakeValue(); - ASSERT_EQ(successResult.m_moveSuccessCount, 0); - ASSERT_EQ(successResult.m_moveFailureCount, 1); - ASSERT_EQ(successResult.m_moveTotalCount, 1); - ASSERT_EQ(successResult.m_updateTotalCount, 0); + EXPECT_EQ(successResult.m_moveSuccessCount, 0); + EXPECT_EQ(successResult.m_moveFailureCount, 1); + EXPECT_EQ(successResult.m_moveTotalCount, 1); + EXPECT_EQ(successResult.m_updateTotalCount, 0); - ASSERT_TRUE(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.toUtf8().constData())); + EXPECT_TRUE(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.toUtf8().constData())); } TEST_F(SourceFileRelocatorTest, Delete_Real_WithDependencies_Fails) diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp index bd99cf7a94..6cd18d534b 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp @@ -30,11 +30,7 @@ public: friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies); friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies_DeferredResolution); -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS - friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, DISABLED_AssetProcessed_Impl_MultiplatformDependencies_SourcePath); -#else friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies_SourcePath); -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, DeleteFolder_SignalsDeleteOfContainedFiles); @@ -70,19 +66,11 @@ public: friend class GTEST_TEST_CLASS_NAME_(AbsolutePathProductDependencyTest, UnresolvedProductPathDependency_AssetProcessedTwice_ValidatePathDependenciesMap); friend class GTEST_TEST_CLASS_NAME_(AbsolutePathProductDependencyTest, UnresolvedSourceFileTypeProductPathDependency_DependencyHasNoProductOutput_ValidatePathDependenciesMap); -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS - friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, DISABLED_ModtimeSkipping_FileUnchanged_WithoutModtimeSkipping); -#else friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_FileUnchanged_WithoutModtimeSkipping); -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_FileUnchanged); -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS - friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, DISABLED_ModtimeSkipping_EnablePlatform_ShouldProcessFilesForPlatform); -#else friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_EnablePlatform_ShouldProcessFilesForPlatform); -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_ModifyFile); friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_ModifyFile_AndThenRevert_ProcessesAgain); @@ -2362,11 +2350,7 @@ TEST_F(PathDependencyTest, ChangeDependencies_Existing_ResolveCorrectly) ); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS -TEST_F(PathDependencyTest, DISABLED_MixedPathDependencies_Existing_ResolveCorrectly) -#else TEST_F(PathDependencyTest, MixedPathDependencies_Existing_ResolveCorrectly) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS { using namespace AssetProcessor; using namespace AssetBuilderSDK; @@ -2661,11 +2645,7 @@ TEST_F(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDepende ASSERT_NE(SearchDependencies(dependencyContainer, asset1.m_products[0]), SearchDependencies(dependencyContainer, asset1.m_products[1])); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS -TEST_F(MultiplatformPathDependencyTest, DISABLED_AssetProcessed_Impl_MultiplatformDependencies_SourcePath) -#else TEST_F(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies_SourcePath) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS { // One product will be pc, one will be console (order is non-deterministic) TestAsset asset1("testAsset1"); @@ -3894,7 +3874,7 @@ void ModtimeScanningTest::ProcessAssetJobs() for (const auto& processResult : m_data->m_processResults) { - auto file = QDir(processResult.m_destinationPath).absoluteFilePath(processResult.m_jobEntry.m_databaseSourceName + ".arc1"); + auto file = QDir(processResult.m_destinationPath).absoluteFilePath(processResult.m_jobEntry.m_databaseSourceName.toLower() + ".arc1"); m_data->m_productPaths.emplace( QDir(processResult.m_jobEntry.m_watchFolderPath) .absoluteFilePath(processResult.m_jobEntry.m_databaseSourceName) @@ -3943,11 +3923,11 @@ void ModtimeScanningTest::ExpectWork(int createJobs, int processJobs) { ASSERT_TRUE(BlockUntilIdle(5000)); - ASSERT_EQ(m_data->m_mockBuilderInfoHandler.m_createJobsCount, createJobs); - ASSERT_EQ(m_data->m_processResults.size(), processJobs); - ASSERT_FALSE(m_data->m_processResults[0].m_autoFail); - ASSERT_FALSE(m_data->m_processResults[1].m_autoFail); - ASSERT_EQ(m_data->m_deletedSources.size(), 0); + EXPECT_EQ(m_data->m_mockBuilderInfoHandler.m_createJobsCount, createJobs); + EXPECT_EQ(m_data->m_processResults.size(), processJobs); + EXPECT_FALSE(m_data->m_processResults[0].m_autoFail); + EXPECT_FALSE(m_data->m_processResults[1].m_autoFail); + EXPECT_EQ(m_data->m_deletedSources.size(), 0); m_isIdling = false; } @@ -3975,11 +3955,7 @@ void ModtimeScanningTest::SetFileContents(QString filePath, QString contents) file.close(); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS -TEST_F(ModtimeScanningTest, DISABLED_ModtimeSkipping_FileUnchanged_WithoutModtimeSkipping) -#else TEST_F(ModtimeScanningTest, ModtimeSkipping_FileUnchanged_WithoutModtimeSkipping) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS { using namespace AzToolsFramework::AssetSystem; @@ -4008,11 +3984,7 @@ TEST_F(ModtimeScanningTest, ModtimeSkipping_FileUnchanged) ExpectNoWork(); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS -TEST_F(ModtimeScanningTest, DISABLED_ModtimeSkipping_EnablePlatform_ShouldProcessFilesForPlatform) -#else TEST_F(ModtimeScanningTest, ModtimeSkipping_EnablePlatform_ShouldProcessFilesForPlatform) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS { using namespace AzToolsFramework::AssetSystem; @@ -4633,11 +4605,7 @@ TEST_F(AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_WildcardM dependList.clear(); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS -TEST_F(AssetProcessorManagerTest, DISABLED_RemoveSource_RemoveCacheFolderIfEmpty_Ok) -#else TEST_F(AssetProcessorManagerTest, RemoveSource_RemoveCacheFolderIfEmpty_Ok) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS { using namespace AssetProcessor; using namespace AssetBuilderSDK; diff --git a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp index 9c80b267eb..4a8e1a7e1c 100644 --- a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp @@ -14,6 +14,7 @@ #include "native/tests/platformconfiguration/platformconfigurationtests.h" #include +#include const char TestAppRoot[] = "@exefolder@/testdata"; const char EmptyDummyProjectName[] = "EmptyDummyProject"; @@ -238,33 +239,6 @@ TEST_F(PlatformConfigurationUnitTests_OnePCHostFixture, GetScanFolderForFile_Sub EXPECT_STREQ(info->GetDisplayName().toUtf8().constData(), "Editor ScanFolder"); } -// note that in the case of GetOverridingFile, this SHOULD return the correct case if an override is found -// because its possible to override a file with another file with different case in a different scan folder -// such a situation is supposed to be very rare, so the cost of correcting the case is mitigated. -TEST_F(PlatformConfigurationUnitTests_OnePCHostFixture, GetOverridingFile_Exists_ReturnsCorrectCase) -{ - using namespace AzToolsFramework::AssetSystem; - using namespace AssetProcessor; - - // create two scan folders, since its order dependent, the ScanFolder1 is the "winner" in tie breakers (when they both contain same file relpath) - QString scanfolder1Path = m_tempPath.filePath("scanfolder1"); - QString scanfolder2Path = m_tempPath.filePath("scanfolder2"); - QString caseSensitiveDummyFileName = m_tempPath.absoluteFilePath("scanfolder1/TestCase.tXt"); - QString differentCaseDummyFileName = m_tempPath.absoluteFilePath("scanfolder2/testcase.txt"); - UnitTestUtils::CreateDummyFile(caseSensitiveDummyFileName, QString("testcase1\n")); - UnitTestUtils::CreateDummyFile(differentCaseDummyFileName, QString("testcase2\n")); - m_config->AddScanFolder(ScanFolderInfo(scanfolder1Path, "ScanFolder1", "sf1", false, true, m_platforms), true); - m_config->AddScanFolder(ScanFolderInfo(scanfolder2Path, "ScanFolder2", "sf2", false, true, m_platforms), true); - - // Perform the test by asking it whether anyone overrides "testcase" (lowercase) in scanfolder 2. - QString overrider = m_config->GetOverridingFile("testcase.txt", scanfolder2Path); - - ASSERT_FALSE(overrider.isEmpty()); - // the result should be the real actual case of the file in scanfolder 1: - EXPECT_STREQ(overrider.toUtf8().constData(), caseSensitiveDummyFileName.toUtf8().constData()); -} - - TEST_F(PlatformConfigurationUnitTests_OnePCHostFixture, GetOverridingFile_ExistsButNotOverridden_ReturnsEmpty) { using namespace AzToolsFramework::AssetSystem; @@ -360,7 +334,7 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolder) ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0); ASSERT_EQ(config.GetScanFolderCount(), 3); // the two, and then the one that has the same data as prior but different identifier. - QString scanName = AssetUtilities::ComputeProjectPath() + " Scan Folder"; + QString scanName = AssetUtilities::ComputeProjectPath(true) + " Scan Folder"; ASSERT_EQ(config.GetScanFolderAt(0).GetDisplayName(), scanName); ASSERT_EQ(config.GetScanFolderAt(0).RecurseSubFolders(), true); ASSERT_EQ(config.GetScanFolderAt(0).GetOrder(), 0); @@ -445,15 +419,11 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularExcludes) ASSERT_FALSE(config.IsFileExcluded("blahblah/Levels/blahblahhold/whatever.test")); } -#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS -TEST_F(PlatformConfigurationUnitTests, DISABLED_TestFailReadConfigFile_Recognizers) -#else TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers) -#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS { using namespace AzToolsFramework::AssetSystem; using namespace AssetProcessor; -#if defined(AZ_PLATFORM_WINDOWS) +#if defined(AZ_PLATFORM_WINDOWS) || defined(AZ_PLATFORM_LINUX) const char* platformWhichIsNotCurrentPlatform = "mac"; #else const char* platformWhichIsNotCurrentPlatform = "pc"; @@ -502,31 +472,47 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers) // the "rend" test makes sure that even if you dont specify 'params' its still there by default for all enabled platforms. // (but platforms can override it) ASSERT_TRUE(recogs.contains("rend")); - ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); - ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains("android")); - ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains("server")); - ASSERT_FALSE(recogs["rend"].m_platformSpecs.contains(platformWhichIsNotCurrentPlatform)); // this is not an enabled platform and should not be there. - ASSERT_EQ(recogs["rend"].m_platformSpecs.size(), 3); - ASSERT_EQ(recogs["rend"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams"); - ASSERT_EQ(recogs["rend"].m_platformSpecs["android"].m_extraRCParams, "rendererparams"); - ASSERT_EQ(recogs["rend"].m_platformSpecs["server"].m_extraRCParams, ""); // default if not specified is empty string + EXPECT_THAT( + recogs["rend"].m_platformSpecs.keys(), + testing::AllOf( + testing::UnorderedElementsAre( + QString(AzToolsFramework::AssetSystem::GetHostAssetPlatform()), + QString("android"), + QString("server") + ), + testing::Not(testing::Contains(platformWhichIsNotCurrentPlatform)) // this is not an enabled platform and should not be there. + ) + ); + EXPECT_EQ(recogs["rend"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams"); + EXPECT_EQ(recogs["rend"].m_platformSpecs["android"].m_extraRCParams, "rendererparams"); + EXPECT_EQ(recogs["rend"].m_platformSpecs["server"].m_extraRCParams, ""); // default if not specified is empty string ASSERT_TRUE(recogs.contains("alldefault")); - ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); - ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains("android")); - ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains("server")); - ASSERT_FALSE(recogs["alldefault"].m_platformSpecs.contains(platformWhichIsNotCurrentPlatform)); // this is not an enabled platform and should not be there. - ASSERT_EQ(recogs["alldefault"].m_platformSpecs.size(), 3); - ASSERT_EQ(recogs["alldefault"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, ""); - ASSERT_EQ(recogs["alldefault"].m_platformSpecs["android"].m_extraRCParams, ""); - ASSERT_EQ(recogs["alldefault"].m_platformSpecs["server"].m_extraRCParams, ""); + EXPECT_THAT( + recogs["alldefault"].m_platformSpecs.keys(), + testing::AllOf( + testing::UnorderedElementsAre( + QString(AzToolsFramework::AssetSystem::GetHostAssetPlatform()), + QString("android"), + QString("server") + ), + testing::Not(testing::Contains(platformWhichIsNotCurrentPlatform)) // this is not an enabled platform and should not be there. + ) + ); + EXPECT_EQ(recogs["alldefault"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, ""); + EXPECT_EQ(recogs["alldefault"].m_platformSpecs["android"].m_extraRCParams, ""); + EXPECT_EQ(recogs["alldefault"].m_platformSpecs["server"].m_extraRCParams, ""); ASSERT_TRUE(recogs.contains("skipallbutone")); - ASSERT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); - ASSERT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains("android")); - ASSERT_TRUE(recogs["skipallbutone"].m_platformSpecs.contains("server")); // server is only one enabled (set to copy) - ASSERT_EQ(recogs["skipallbutone"].m_platformSpecs.size(), 1); - ASSERT_EQ(recogs["skipallbutone"].m_platformSpecs["server"].m_extraRCParams, "copy"); + EXPECT_THAT( + recogs["skipallbutone"].m_platformSpecs.keys(), + testing::UnorderedElementsAre( + QString("server") // server is only one enabled (set to copy) + ) + ); + EXPECT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform())); + EXPECT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains("android")); + EXPECT_EQ(recogs["skipallbutone"].m_platformSpecs["server"].m_extraRCParams, "copy"); } diff --git a/Code/Tools/AssetProcessor/native/tests/utilities/assetUtilsTest.cpp b/Code/Tools/AssetProcessor/native/tests/utilities/assetUtilsTest.cpp index 5ed392d12b..796bca2af2 100644 --- a/Code/Tools/AssetProcessor/native/tests/utilities/assetUtilsTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/utilities/assetUtilsTest.cpp @@ -137,10 +137,13 @@ TEST_F(AssetUtilitiesTest, UpdateToCorrectCase_ExistingFile_ReturnsTrue_Corrects thingsToTry << "SomeFile.TxT"; thingsToTry << "otherfile.txt"; thingsToTry << "subfolder1/otherfile.txt"; + + #if defined(AZ_PLATFORM_WINDOWS) thingsToTry << "subfolder2\\otherfile.txt"; thingsToTry << "subFolder3\\somefile.txt"; thingsToTry << "subFolder4\\subfolder6\\somefile.txt"; thingsToTry << "subFolder5\\subfolder7/someFile.txt"; + #endif // AZ_PLATFORM_WINDOWS thingsToTry << "specialFileName[.txt"; thingsToTry << "specialFileName].txt"; thingsToTry << "specialFileName!.txt"; diff --git a/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp index 079cdb7c66..efeec5d7c1 100644 --- a/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp @@ -175,6 +175,16 @@ namespace AssetProcessor UNIT_TEST_EXPECT_FALSE(gameName.isEmpty()); // should create cache folder in the root, and read everything from there. + // There is a sub-case of handling mixed cases, but is only supported on case-insensitive filesystems. +#if defined(AZ_PLATFORM_LINUX) + // Linux is case-sensitive, so 'basefile.txt' will stay the same case as the other subfolder versions + constexpr const char* subfolder3BaseFilePath = "subfolder3/basefile.txt"; + constexpr int expectedLegacyAssetIdCount = 1; +#else + constexpr const char* subfolder3BaseFilePath = "subfolder3/BaseFile.txt"; + constexpr int expectedLegacyAssetIdCount = 2; +#endif + QSet expectedFiles; // set up some interesting files: expectedFiles << tempPath.absoluteFilePath("rootfile2.txt"); @@ -185,7 +195,9 @@ namespace AssetProcessor expectedFiles << tempPath.absoluteFilePath("subfolder2/aaa/bbb/basefile.txt"); expectedFiles << tempPath.absoluteFilePath("subfolder2/aaa/bbb/ccc/basefile.txt"); expectedFiles << tempPath.absoluteFilePath("subfolder2/aaa/bbb/ccc/ddd/basefile.txt"); - expectedFiles << tempPath.absoluteFilePath("subfolder3/BaseFile.txt"); // note the case upper here + + expectedFiles << tempPath.absoluteFilePath(subfolder3BaseFilePath); + expectedFiles << tempPath.absoluteFilePath("subfolder8/a/b/c/test.txt"); // subfolder3 is not recursive so none of these should show up in any scan or override check @@ -1521,7 +1533,8 @@ namespace AssetProcessor // -------------- override test ----------------- // set up by letting it compile basefile.txt from 3: - absolutePath = AssetUtilities::NormalizeFilePath(tempPath.absoluteFilePath("subfolder3/BaseFile.txt")); + + absolutePath = AssetUtilities::NormalizeFilePath(tempPath.absoluteFilePath(subfolder3BaseFilePath)); QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absolutePath)); UNIT_TEST_EXPECT_TRUE(BlockUntil(idling, 5000)); @@ -1583,8 +1596,7 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(assetMessages.size() == 4); for (auto element : assetMessages) { - // because the source asset had UPPER CASE in it, we should have multiple legacy IDs - UNIT_TEST_EXPECT_TRUE(element.m_legacyAssetIds.size() == 2); + UNIT_TEST_EXPECT_TRUE(element.m_legacyAssetIds.size() == expectedLegacyAssetIdCount); } // ------------- setup complete, now do the test... @@ -1607,7 +1619,7 @@ namespace AssetProcessor // delete the highest priority override file and ensure that it generates tasks // for the next highest priority! Basically, deleting this file should "reveal" the file underneath it in the other subfolder - QString deletedFile = tempPath.absoluteFilePath("subfolder3/BaseFile.txt"); + QString deletedFile = tempPath.absoluteFilePath(subfolder3BaseFilePath); QString expectedReplacementInputFile = AssetUtilities::NormalizeFilePath(tempPath.absoluteFilePath("subfolder2/basefile.txt")); UNIT_TEST_EXPECT_TRUE(QFile::remove(deletedFile)); @@ -1621,6 +1633,11 @@ namespace AssetProcessor sortAssetToProcessResultList(processResults); +#if defined(AZ_PLATFORM_LINUX) + // On Linux, because of we cannot change the case of the source file, the job fingerprint is not updated due the case-switch so + // there will be actually nothing to process + UNIT_TEST_EXPECT_TRUE(processResults.size() == 0); +#else // --------- same result as above ---------- UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier); @@ -1641,7 +1658,7 @@ namespace AssetProcessor UNIT_TEST_EXPECT_TRUE(processFile1.startsWith(platformFolder)); UNIT_TEST_EXPECT_TRUE(processResults[checkIdx].m_jobEntry.m_computedFingerprint != 0); } - +#endif // defined(AZ_PLATFORM_LINUX) relativePathFromWatchFolder = "somefile.xxx"; watchFolderPath = tempPath.absoluteFilePath("subfolder3"); absolutePath = watchFolderPath + "/" + relativePathFromWatchFolder; @@ -2721,7 +2738,12 @@ namespace AssetProcessor { AssetBuilderSDK::JobDescriptor secondDescriptor = descriptor; secondDescriptor.m_jobKey = "yyy"; + #if defined(AZ_PLATFORM_WINDOWS) sourceFileDependency.m_sourceFileDependencyPath = "some\\random/Folders/FILEa.TxT"; + #else + sourceFileDependency.m_sourceFileDependencyPath = "some/random/folders/FileA.txt"; + #endif // defined(AZ_PLATFORM_WINDOWS) + // ... declare a job dependency on job A ('FileA.txt', 'xxx', platform) AssetBuilderSDK::JobDependency jobDependency("xxx", platformInfo.m_identifier.c_str(), AssetBuilderSDK::JobDependencyType::Fingerprint, sourceFileDependency); secondDescriptor.m_jobDependencyList.push_back(jobDependency); @@ -2805,11 +2827,11 @@ namespace AssetProcessor QDir cacheRoot; UNIT_TEST_EXPECT_TRUE(AssetUtilities::ComputeProjectCacheRoot(cacheRoot)); - QString productFileAPath = cacheRoot.filePath(QString("pc/FileAProduct.txt")); - QString productFileBPath = cacheRoot.filePath(QString("pc/FileBProduct1.txt")); - QString product2FileBPath = cacheRoot.filePath(QString("pc/FileBProduct2.txt")); - QString productFileCPath = cacheRoot.filePath(QString("pc/FileCProduct.txt")); - QString product2FileCPath = cacheRoot.filePath(QString("pc/FileCProduct2.txt")); + QString productFileAPath = cacheRoot.filePath(QString("pc/fileaproduct.txt")); + QString productFileBPath = cacheRoot.filePath(QString("pc/filebproduct1.txt")); + QString product2FileBPath = cacheRoot.filePath(QString("pc/filebproduct2.txt")); + QString productFileCPath = cacheRoot.filePath(QString("pc/filecproduct.txt")); + QString product2FileCPath = cacheRoot.filePath(QString("pc/filecproduct2.txt")); UNIT_TEST_EXPECT_TRUE(CreateDummyFile(sourceFileAPath, "")); UNIT_TEST_EXPECT_TRUE(CreateDummyFile(sourceFileBPath, "")); diff --git a/Code/Tools/AssetProcessor/native/unittests/FileWatcherUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/FileWatcherUnitTests.cpp index 785ea8efa6..f6fb21e4e7 100644 --- a/Code/Tools/AssetProcessor/native/unittests/FileWatcherUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/FileWatcherUnitTests.cpp @@ -106,7 +106,7 @@ void FileWatcherUnitTestRunner::StartTest() AZ_TracePrintf(AssetProcessor::DebugChannel, "Waiting for remaining notifications: %d \n", outstandingFiles.count()); } - if (outstandingFiles.count() > 0) + if (outstandingFiles.count() > 0) { #if defined(AZ_ENABLE_TRACING) AZ_TracePrintf(AssetProcessor::DebugChannel, "Timed out waiting for file changes: %d / %d missed\n", outstandingFiles.count(), maxFiles); @@ -226,7 +226,9 @@ void FileWatcherUnitTestRunner::StartTest() UNIT_TEST_EXPECT_TRUE(fileAddCalled); UNIT_TEST_EXPECT_TRUE(fileRemoveCalled); - UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in +#if defined(AZ_PLATFORM_WINDOWS) + UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in (Only on Windows) +#endif // AZ_PLATFORM_WINDOWS UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileRemoveName).toLower() == QDir::toNativeSeparators(originalName).toLower()); UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileAddName).toLower() == QDir::toNativeSeparators(newName1).toLower()); @@ -249,7 +251,10 @@ void FileWatcherUnitTestRunner::StartTest() // the new1 was "removed" and the new2 was "added" UNIT_TEST_EXPECT_TRUE(fileAddCalled); UNIT_TEST_EXPECT_TRUE(fileRemoveCalled); - UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in +#if defined(AZ_PLATFORM_WINDOWS) + UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in (Only on Windows) +#endif // AZ_PLATFORM_WINDOWS + UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileRemoveName).toLower() == QDir::toNativeSeparators(newName1).toLower()); UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileAddName).toLower() == QDir::toNativeSeparators(newName2).toLower()); @@ -270,11 +275,15 @@ void FileWatcherUnitTestRunner::StartTest() // the new1 was "removed" and the new2 was "added" UNIT_TEST_EXPECT_TRUE(fileAddCalled); UNIT_TEST_EXPECT_TRUE(fileRemoveCalled); - UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in +#if defined(AZ_PLATFORM_WINDOWS) + UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in (Only on Windows) +#endif // AZ_PLATFORM_WINDOWS UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileRemoveName).toLower() == QDir::toNativeSeparators(newName2).toLower()); UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileAddName).toLower() == QDir::toNativeSeparators(newName3).toLower()); - // final test... make sure that renaming a DIRECTORY works too +#if !defined(AZ_PLATFORM_LINUX) + // final test... make sure that renaming a DIRECTORY works too. + // Note that linux does not get any callbacks if just the directory is renamed (from inotify) QDir renamer; fileAddCalled = false; fileRemoveCalled = false; @@ -297,7 +306,7 @@ void FileWatcherUnitTestRunner::StartTest() UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileRemoveName).toLower() == QDir::toNativeSeparators(tempDirPath.absoluteFilePath("dir3")).toLower()); UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileAddName).toLower() == QDir::toNativeSeparators(tempDirPath.absoluteFilePath("dir4")).toLower()); - +#endif // AZ_PLATFORM_LINUX QObject::disconnect(connectionRemove); QObject::disconnect(connectionAdd); diff --git a/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp index 02b98e5e33..38c1377b46 100644 --- a/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp @@ -545,10 +545,13 @@ void RCcontrollerUnitTests::RunRCControllerTests() rcJob.SetCheckExclusiveLock(true); rcJob.Start(); + +#if defined(AZ_PLATFORM_WINDOWS) + // on windows, opening a file for reading locks it + // but on other platforms, this is not the case. // we only expect work to begin when we can gain an exclusive lock on this file. UNIT_TEST_EXPECT_FALSE(UnitTestUtils::BlockUntil(beginWork, 5000)); -#if defined(AZ_PLATFORM_WINDOWS) // Once we release the file, it should process normally lockFileTest.close(); #else diff --git a/Code/Tools/AssetProcessor/native/unittests/UtilitiesUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/UtilitiesUnitTests.cpp index 1eada874d9..24433b550c 100644 --- a/Code/Tools/AssetProcessor/native/unittests/UtilitiesUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/UtilitiesUnitTests.cpp @@ -227,8 +227,9 @@ void UtilitiesUnitTests::StartTest() #else int handle = open(lockTestFileName.toUtf8().constData(), O_RDONLY | O_EXLOCK | O_NONBLOCK); #endif // AZ_PLATFORM_WINDOWS - UNIT_TEST_EXPECT_FALSE(AssetUtilities::CheckCanLock(lockTestFileName)); + #if defined(AZ_PLATFORM_WINDOWS) + UNIT_TEST_EXPECT_FALSE(AssetUtilities::CheckCanLock(lockTestFileName)); lockTestFile.close(); #else if (handle != -1) diff --git a/Code/Tools/AssetProcessor/native/utilities/assetUtils.cpp b/Code/Tools/AssetProcessor/native/utilities/assetUtils.cpp index b152e5ed4f..19423358b5 100644 --- a/Code/Tools/AssetProcessor/native/utilities/assetUtils.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/assetUtils.cpp @@ -507,8 +507,13 @@ namespace AssetUtilities return QString::fromUtf8(s_projectName.c_str(), aznumeric_cast(s_projectName.size())); } - QString ComputeProjectPath() + QString ComputeProjectPath(bool resetCachedProjectPath/*=false*/) { + if (resetCachedProjectPath) + { + // Clear any cached value if reset was requested + s_projectPath.clear(); + } if (s_projectPath.empty()) { // Check command-line args first diff --git a/Code/Tools/AssetProcessor/native/utilities/assetUtils.h b/Code/Tools/AssetProcessor/native/utilities/assetUtils.h index 201911eb96..694498e423 100644 --- a/Code/Tools/AssetProcessor/native/utilities/assetUtils.h +++ b/Code/Tools/AssetProcessor/native/utilities/assetUtils.h @@ -104,7 +104,8 @@ namespace AssetUtilities QString ComputeProjectName(QString projectNameOverride = QString(), bool force = false); //! Determine the absolute path of the current project - QString ComputeProjectPath(); + //! The path computed path will be cached on subsequent calls unless resetCachedProjectPath=true + QString ComputeProjectPath(bool resetCachedProjectPath = false); //! Reads the allowed list directly from the bootstrap file QString ReadAllowedlistFromSettingsRegistry(QString initialFolder = QString()); diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 3f0af7fa49..bb6c05a472 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -246,7 +246,7 @@ namespace O3DE::ProjectManager AZStd::string pyBasePath = Platform::GetPythonHomePath(PY_PACKAGE, m_enginePath.c_str()); if (!AZ::IO::SystemFile::Exists(pyBasePath.c_str())) { - AZ_Warning("python", false, "Python home path must exist. path:%s", pyBasePath.c_str()); + AZ_Assert(false, "Python home path must exist. path:%s", pyBasePath.c_str()); return false; } @@ -277,11 +277,12 @@ namespace O3DE::ProjectManager AZStd::lock_guard lock(m_lock); pybind11::gil_scoped_acquire acquire; - // Setup sys.path - int result = PyRun_SimpleString("import sys"); - AZ_Warning("ProjectManagerWindow", result != -1, "Import sys failed"); - result = PyRun_SimpleString(AZStd::string::format("sys.path.append('%s')", m_enginePath.c_str()).c_str()); - AZ_Warning("ProjectManagerWindow", result != -1, "Append to sys path failed"); + // sanity import check + if (PyRun_SimpleString("import sys") != 0) + { + AZ_Assert(false, "Import sys failed"); + return false; + } // import required modules m_cmake = pybind11::module::import("o3de.cmake"); @@ -295,11 +296,11 @@ namespace O3DE::ProjectManager // make sure the engine is registered RegisterThisEngine(); - return result == 0 && !PyErr_Occurred(); + return !PyErr_Occurred(); } catch ([[maybe_unused]] const std::exception& e) { - AZ_Warning("ProjectManagerWindow", false, "Py_Initialize() failed with %s", e.what()); + AZ_Assert(false, "Py_Initialize() failed with %s", e.what()); return false; } } diff --git a/Code/Tools/SerializeContextTools/Application.cpp b/Code/Tools/SerializeContextTools/Application.cpp index 918afd731c..274d04e7ce 100644 --- a/Code/Tools/SerializeContextTools/Application.cpp +++ b/Code/Tools/SerializeContextTools/Application.cpp @@ -35,7 +35,7 @@ namespace AZ Application::Application(int argc, char** argv) : AzToolsFramework::ToolsApplication(&argc, &argv) { - // We need a specialized variant of EditorEntityContextCompnent for the SliceConverter, so we register the descriptor here. + // We need a specialized variant of EditorEntityContextComponent for the SliceConverter, so we register the descriptor here. RegisterComponentDescriptor(AzToolsFramework::SliceConverterEditorEntityContextComponent::CreateDescriptor()); AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath(); diff --git a/Code/Tools/SerializeContextTools/SliceConverter.cpp b/Code/Tools/SerializeContextTools/SliceConverter.cpp index 56b3689d6b..9fba060960 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.cpp +++ b/Code/Tools/SerializeContextTools/SliceConverter.cpp @@ -128,8 +128,7 @@ namespace AZ return result; } - bool SliceConverter::ConvertSliceFile( - AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun) + bool SliceConverter::ConvertSliceFile(AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun) { /* To convert a slice file, we read the input file in via ObjectStream, then use the "class ready" callback to convert * the data in memory to a Prefab. @@ -177,11 +176,22 @@ namespace AZ } AZ::Entity* rootEntity = reinterpret_cast(classPtr); - return ConvertSliceToPrefab(context, outputPath, isDryRun, rootEntity); + bool convertResult = ConvertSliceToPrefab(context, outputPath, isDryRun, rootEntity); + // Clear out the references to any nested slices so that the nested assets get unloaded correctly at the end of + // the conversion. + ClearSliceAssetReferences(rootEntity); + return convertResult; }; // Read in the slice file and call the callback on completion to convert the read-in slice to a prefab. - if (!Utilities::InspectSerializedFile(inputPath.c_str(), serializeContext, callback)) + // This will also load dependent slice assets, but no other dependent asset types. + // Since we're not actually initializing any of the entities, we don't need any of the non-slice assets to be loaded. + if (!Utilities::InspectSerializedFile( + inputPath.c_str(), serializeContext, callback, + [](const AZ::Data::AssetFilterInfo& filterInfo) + { + return (filterInfo.m_assetType == azrtti_typeid()); + })) { AZ_Warning("Convert-Slice", false, "Failed to load '%s'. File may not contain an object stream.", inputPath.c_str()); result = false; @@ -256,7 +266,7 @@ namespace AZ for (auto& alias : entityAliases) { auto id = sourceInstance->GetEntityId(alias); - auto result = m_aliasIdMapper.emplace(TemplateEntityIdPair(templateId, id), alias); + auto result = m_aliasIdMapper.emplace(id, SliceEntityMappingInfo(templateId, alias)); if (!result.second) { AZ_Printf("Convert-Slice", " Duplicate entity alias -> entity id entries found, conversion may not be successful.\n"); @@ -342,10 +352,9 @@ namespace AZ // For each nested slice, convert it. for (auto& slice : sliceList) { - // Get the nested slice asset + // Get the nested slice asset. These should already be preloaded due to loading the root asset. auto sliceAsset = slice.GetSliceAsset(); - sliceAsset.QueueLoad(); - sliceAsset.BlockUntilLoadComplete(); + AZ_Assert(sliceAsset.IsReady(), "slice asset hasn't been loaded yet!"); // The slice list gives us asset IDs, and we need to get to the source path. So first we get the asset path from the ID, // then we get the source path from the asset path. @@ -429,6 +438,28 @@ namespace AZ auto instanceToTemplateInterface = AZ::Interface::Get(); auto prefabSystemComponentInterface = AZ::Interface::Get(); + // When creating the new instance, we would like to have deterministic instance aliases. Prefabs that depend on this one + // will have patches that reference the alias, so if we reconvert this slice a second time, we would like it to produce + // the same results. To get a deterministic and unique alias, we rely on the slice instance. The slice instance contains + // a map of slice entity IDs to unique instance entity IDs. We'll just consistently use the first entry in the map as the + // unique instance ID. + AZStd::string instanceAlias; + auto entityIdMap = instance.GetEntityIdMap(); + if (!entityIdMap.empty()) + { + instanceAlias = AZStd::string::format("Instance_%s", entityIdMap.begin()->second.ToString().c_str()); + } + else + { + instanceAlias = AZStd::string::format("Instance_%s", AZ::Entity::MakeId().ToString().c_str()); + } + + // Before processing any further, save off all the known entity IDs from this instance and how they map back to the base + // nested prefab that they've come from (i.e. this one). As we proceed up the chain of nesting, this will build out a + // hierarchical list of owning instances for each entity that we can trace upwards to know where to add the entity into + // our nested prefab instance. + UpdateSliceEntityInstanceMappings(instance.GetEntityIdToBaseMap(), instanceAlias); + // Create a new unmodified prefab Instance for the nested slice instance. auto nestedInstance = AZStd::make_unique(); AzToolsFramework::Prefab::Instance::EntityList newEntities; @@ -465,62 +496,125 @@ namespace AZ auto instantiated = dataPatch.Apply(&sourceObjects, dependentSlice->GetSerializeContext(), filterDesc, sourceDataFlags, targetDataFlags); - // Run through all the instantiated entities and fix up their parent hierarchy: - // - Invalid parents need to get set to the container. - // - Valid parents into the top-level instance mean that the nested slice instance is also child-nested under an entity. - // Prefabs handle this type of nesting differently - we need to set the parent to the container, and the container's - // parent to that other instance. - auto containerEntity = nestedInstance->GetContainerEntity(); - auto containerEntityId = containerEntity->get().GetId(); - for (auto entity : instantiated->m_entities) - { - AzToolsFramework::Components::TransformComponent* transformComponent = - entity->FindComponent(); - if (transformComponent) - { - bool onlySetIfInvalid = true; - auto parentId = transformComponent->GetParentId(); - if (parentId.IsValid()) - { - auto parentAlias = m_aliasIdMapper.find(TemplateEntityIdPair(topLevelInstance->GetTemplateId(), parentId)); - if (parentAlias != m_aliasIdMapper.end()) - { - // Set the container's parent to this entity's parent, and set this entity's parent to the container - // (i.e. go from A->B to A->container->B) - auto newParentId = topLevelInstance->GetEntityId(parentAlias->second); - SetParentEntity(containerEntity->get(), newParentId, false); - onlySetIfInvalid = false; - } - } - - SetParentEntity(*entity, containerEntityId, onlySetIfInvalid); - } - } - - // Replace all the entities in the instance with the new patched ones. + // Replace all the entities in the instance with the new patched ones. To do this, we'll remove all existing entities + // throughout the entire nested hierarchy, then add the new patched entities back in at the appropriate place in the hierarchy. // (This is easier than trying to figure out what the patched data changes are - we can let the JSON patch handle it for us) + nestedInstance->RemoveNestedEntities( [](const AZStd::unique_ptr&) { return true; }); + + AZStd::vector> addedEntityList; + for (auto& entity : instantiated->m_entities) { - auto entityAlias = m_aliasIdMapper.find(TemplateEntityIdPair(nestedInstance->GetTemplateId(), entity->GetId())); - if (entityAlias != m_aliasIdMapper.end()) + auto entityEntry = m_aliasIdMapper.find(entity->GetId()); + if (entityEntry != m_aliasIdMapper.end()) { - nestedInstance->AddEntity(*entity, entityAlias->second); + auto& mappingStruct = entityEntry->second; + + // Starting with the current nested instance, walk downwards through the nesting hierarchy until we're at the + // correct level for this instanced entity ID, then add it. Because we're adding it with the non-instanced alias, + // it doesn't matter what the slice's instanced entity ID is, and the JSON patch will correctly pick up the changes + // we've made for this instance. + AzToolsFramework::Prefab::Instance* addingInstance = nestedInstance.get(); + for (auto it = mappingStruct.m_nestedInstanceAliases.rbegin(); it != mappingStruct.m_nestedInstanceAliases.rend(); it++) + { + auto foundInstance = addingInstance->FindNestedInstance(*it); + if (foundInstance.has_value()) + { + addingInstance = &(foundInstance->get()); + } + else + { + AZ_Assert(false, "Couldn't find nested instance %s", it->c_str()); + } + } + addingInstance->AddEntity(*entity, mappingStruct.m_entityAlias); + addedEntityList.emplace_back(entity, addingInstance); } else { AZ_Assert(false, "Failed to find entity alias."); nestedInstance->AddEntity(*entity); + addedEntityList.emplace_back(entity, nestedInstance.get()); + } + } + + for (auto& [entity, addingInstance] : addedEntityList) + { + // Fix up the parent hierarchy: + // - Invalid parents need to get set to the container. + // - Valid parents into the top-level instance mean that the nested slice instance is also child-nested under an entity. + // Prefabs handle this type of nesting differently - we need to set the parent to the container, and the container's + // parent to that other instance. + auto containerEntity = addingInstance->GetContainerEntity(); + auto containerEntityId = containerEntity->get().GetId(); + AzToolsFramework::Components::TransformComponent* transformComponent = + entity->FindComponent(); + if (transformComponent) + { + bool onlySetIfInvalid = true; + auto parentId = transformComponent->GetParentId(); + if (parentId.IsValid()) + { + // Look to see if the parent ID exists in the same instance (i.e. an entity in the nested slice is a + // child of an entity in the containing slice). If this case exists, we need to adjust the parents so that + // the child entity connects to the prefab container, and the *container* is the child of the entity in the + // containing slice. (i.e. go from A->B to A->container->B) + auto parentEntry = m_aliasIdMapper.find(parentId); + if (parentEntry != m_aliasIdMapper.end()) + { + auto& parentMappingInfo = parentEntry->second; + if (parentMappingInfo.m_templateId != addingInstance->GetTemplateId()) + { + if (topLevelInstance->GetTemplateId() == parentMappingInfo.m_templateId) + { + parentId = topLevelInstance->GetEntityId(parentMappingInfo.m_entityAlias); + } + else + { + AzToolsFramework::Prefab::Instance* parentInstance = addingInstance; + + while ((parentInstance->GetParentInstance().has_value()) && + (parentInstance->GetTemplateId() != parentMappingInfo.m_templateId)) + { + parentInstance = &(parentInstance->GetParentInstance()->get()); + } + + if (parentInstance->GetTemplateId() == parentMappingInfo.m_templateId) + { + parentId = parentInstance->GetEntityId(parentMappingInfo.m_entityAlias); + } + else + { + AZ_Assert(false, "Could not find parent instance"); + } + } + + // Set the container's parent to this entity's parent, and set this entity's parent to the container + // auto newParentId = topLevelInstance->GetEntityId(parentMappingInfo.m_entityAlias); + SetParentEntity(containerEntity->get(), parentId, false); + onlySetIfInvalid = false; + } + } + + // If the parent ID is valid, but NOT in the top-level instance, then it's just a nested hierarchy inside + // the slice and we don't need to adjust anything. "onlySetIfInvalid" will still be true, which means we + // won't change the parent ID below. + } + + SetParentEntity(*entity, containerEntityId, onlySetIfInvalid); } } + // Set the container entity of the nested prefab to have the top-level prefab as the parent if it hasn't already gotten // another entity as its parent. { + auto containerEntity = nestedInstance->GetContainerEntity(); constexpr bool onlySetIfInvalid = true; SetParentEntity(containerEntity->get(), topLevelInstance->GetContainerEntityId(), onlySetIfInvalid); } @@ -531,21 +625,7 @@ namespace AZ AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomBefore; instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomBefore, *topLevelInstance); - // When creating the new instance, we would like to have deterministic instance aliases. Prefabs that depend on this one - // will have patches that reference the alias, so if we reconvert this slice a second time, we would like it to produce - // the same results. To get a deterministic and unique alias, we rely on the slice instance. The slice instance contains - // a map of slice entity IDs to unique instance entity IDs. We'll just consistently use the first entry in the map as the - // unique instance ID. - AZStd::string instanceAlias; - auto entityIdMap = instance.GetEntityIdMap(); - if (!entityIdMap.empty()) - { - instanceAlias = AZStd::string::format("Instance_%s", entityIdMap.begin()->second.ToString().c_str()); - } - else - { - instanceAlias = AZStd::string::format("Instance_%s", AZ::Entity::MakeId().ToString().c_str()); - } + // Use the deterministic instance alias for this new instance AzToolsFramework::Prefab::Instance& addedInstance = topLevelInstance->AddInstance(AZStd::move(nestedInstance), instanceAlias); AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomAfter; @@ -670,5 +750,48 @@ namespace AZ AZ_Error("Convert-Slice", disconnected, "Asset Processor failed to disconnect successfully."); } + void SliceConverter::ClearSliceAssetReferences(AZ::Entity* rootEntity) + { + SliceComponent* sliceComponent = AZ::EntityUtils::FindFirstDerivedComponent(rootEntity); + // Make a copy of the slice list and remove all of them from the loaded component. + AZ::SliceComponent::SliceList slices = sliceComponent->GetSlices(); + for (auto& slice : slices) + { + sliceComponent->RemoveSlice(&slice); + } + } + + void SliceConverter::UpdateSliceEntityInstanceMappings( + const AZ::SliceComponent::EntityIdToEntityIdMap& sliceEntityIdMap, const AZStd::string& currentInstanceAlias) + { + // For each instanced entity, map its ID all the way back to the original prefab template and entity ID that it came from. + // This counts on being run recursively from the leaf nodes upwards, so we first get B->A, + // then C->B which becomes a C->A entry, then D->C which becomes D->A, etc. + for (auto& [newId, oldId] : sliceEntityIdMap) + { + // Try to find the conversion chain from the old ID. if it's there, copy it and use it for the new ID, plus add this + // instance's name to the end of the chain. If it's not there, skip it, since it's probably the slice metadata entity, + // which we didn't convert. + auto parentEntry = m_aliasIdMapper.find(oldId); + if (parentEntry != m_aliasIdMapper.end()) + { + // Only add this instance's name if we don't already have an entry for the new ID. + if (m_aliasIdMapper.find(newId) == m_aliasIdMapper.end()) + { + auto newMappingEntry = m_aliasIdMapper.emplace(newId, parentEntry->second).first; + newMappingEntry->second.m_nestedInstanceAliases.emplace_back(currentInstanceAlias); + } + else + { + // If we already had an entry for the new ID, it might be because the old and new ID are the same. This happens + // when nesting multiple prefabs directly underneath each other without a nesting entity in-between. + // If the IDs are different, it's an unexpected error condition. + AZ_Assert(oldId == newId, "The same entity instance ID has unexpectedly appeared twice in the same nested prefab."); + } + } + } + } + + } // namespace SerializeContextTools } // namespace AZ diff --git a/Code/Tools/SerializeContextTools/SliceConverter.h b/Code/Tools/SerializeContextTools/SliceConverter.h index 82dcf30383..ee0bb0a539 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.h +++ b/Code/Tools/SerializeContextTools/SliceConverter.h @@ -42,8 +42,6 @@ namespace AZ bool ConvertSliceFiles(Application& application); private: - using TemplateEntityIdPair = AZStd::pair; - bool ConnectToAssetProcessor(); void DisconnectFromAssetProcessor(); @@ -60,10 +58,32 @@ namespace AZ void SetParentEntity(const AZ::Entity& entity, const AZ::EntityId& parentId, bool onlySetIfInvalid); void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId); bool SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId); + void ClearSliceAssetReferences(AZ::Entity* rootEntity); + void UpdateSliceEntityInstanceMappings( + const AZ::SliceComponent::EntityIdToEntityIdMap& sliceEntityIdMap, + const AZStd::string& currentInstanceAlias); + + // When converting slice entities, especially for nested slices, we need to keep track of the original + // entity ID, the entity alias it uses in the prefab, and which template and nested instance path it maps to. + // As we encounter each instanced entity ID, we can look it up in this structure and use this to determine how to properly + // add it to the correct place in the hierarchy. + struct SliceEntityMappingInfo + { + SliceEntityMappingInfo(AzToolsFramework::Prefab::TemplateId templateId, AzToolsFramework::Prefab::EntityAlias entityAlias) + : m_templateId(templateId) + , m_entityAlias(entityAlias) + { + } + + AzToolsFramework::Prefab::TemplateId m_templateId; + AzToolsFramework::Prefab::EntityAlias m_entityAlias; + AZStd::vector m_nestedInstanceAliases; + }; - // Track all of the entity IDs created and the prefab entity aliases that map to them. This mapping is used - // with nested slice conversion to remap parent entity IDs to the correct prefab entity IDs. - AZStd::unordered_map m_aliasIdMapper; + // Track all of the entity IDs created and associate them with enough conversion information to know how to place the + // entities in the correct place in the prefab hierarchy and fix up parent entity ID mappings to work with the nested + // prefab schema. + AZStd::unordered_map m_aliasIdMapper; // Track all of the created prefab template IDs on a slice conversion so that they can get removed at the end of the // conversion for that file. diff --git a/Code/Tools/SerializeContextTools/SliceConverterEditorEntityContextComponent.h b/Code/Tools/SerializeContextTools/SliceConverterEditorEntityContextComponent.h index 166cb2abdf..07246b20a7 100644 --- a/Code/Tools/SerializeContextTools/SliceConverterEditorEntityContextComponent.h +++ b/Code/Tools/SerializeContextTools/SliceConverterEditorEntityContextComponent.h @@ -36,7 +36,7 @@ namespace AzToolsFramework SliceConverterEditorEntityContextComponent() : EditorEntityContextComponent() {} // Simple API to selectively disable this logic *only* when performing slice to prefab conversion. - static void DisableOnContextEntityLogic() + static inline void DisableOnContextEntityLogic() { m_enableOnContextEntityLogic = false; } diff --git a/Code/Tools/SerializeContextTools/Utilities.cpp b/Code/Tools/SerializeContextTools/Utilities.cpp index cfd75a44e7..d39a71dd24 100644 --- a/Code/Tools/SerializeContextTools/Utilities.cpp +++ b/Code/Tools/SerializeContextTools/Utilities.cpp @@ -209,7 +209,11 @@ namespace AZ::SerializeContextTools return result; } - bool Utilities::InspectSerializedFile(const char* filePath, SerializeContext* sc, const ObjectStream::ClassReadyCB& classCallback) + bool Utilities::InspectSerializedFile( + const char* filePath, + SerializeContext* sc, + const ObjectStream::ClassReadyCB& classCallback, + Data::AssetFilterCB assetFilterCallback) { if (!AZ::IO::FileIOBase::GetInstance()->Exists(filePath)) { @@ -248,9 +252,9 @@ namespace AZ::SerializeContextTools AZ::IO::MemoryStream stream(data.data(), fileLength); ObjectStream::FilterDescriptor filter; - // Never load dependencies. That's another file that would need to be processed + // By default, never load dependencies. That's another file that would need to be processed // separately from this one. - filter.m_assetCB = AZ::Data::AssetFilterNoAssetLoading; + filter.m_assetCB = assetFilterCallback; if (!ObjectStream::LoadBlocking(&stream, *sc, classCallback, filter)) { AZ_Printf("Verify", "Failed to deserialize '%s'\n", filePath); diff --git a/Code/Tools/SerializeContextTools/Utilities.h b/Code/Tools/SerializeContextTools/Utilities.h index f08bfc2f64..9c9cd28ac6 100644 --- a/Code/Tools/SerializeContextTools/Utilities.h +++ b/Code/Tools/SerializeContextTools/Utilities.h @@ -39,7 +39,11 @@ namespace AZ static AZStd::vector GetSystemComponents(const Application& application); - static bool InspectSerializedFile(const char* filePath, SerializeContext* sc, const ObjectStream::ClassReadyCB& classCallback); + static bool InspectSerializedFile( + const char* filePath, + SerializeContext* sc, + const ObjectStream::ClassReadyCB& classCallback, + Data::AssetFilterCB assetFilterCallback = AZ::Data::AssetFilterNoAssetLoading); private: Utilities() = delete; diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/controller/error_controller.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/controller/error_controller.py new file mode 100644 index 0000000000..244245ad57 --- /dev/null +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/controller/error_controller.py @@ -0,0 +1,33 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + +from PySide2.QtCore import (QCoreApplication, QObject) + +from manager.view_manager import ViewManager +from view.error_page import ErrorPage + + +class ErrorController(QObject): + """ + ErrorPage Controller + """ + def __init__(self) -> None: + super(ErrorController, self).__init__() + # Initialize manager references + self._view_manager: ViewManager = ViewManager.get_instance() + # Initialize view references + self._error_page: ErrorPage = self._view_manager.get_error_page() + + def _ok(self) -> None: + QCoreApplication.instance().quit() + + def setup(self) -> None: + self._error_page.ok_button.clicked.connect(self._ok) diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py index fc188582c7..7c5ceb6946 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py @@ -48,11 +48,15 @@ class ConfigurationManager(object): def configuration(self, new_configuration: ConfigurationManager) -> None: self._configuration = new_configuration - def setup(self, config_path: str) -> bool: + def setup(self, profile_name: str, config_path: str) -> bool: result: bool = True - logger.info("Setting up default configuration ...") + logger.debug("Setting up default configuration ...") try: - normalized_config_path: str = file_utils.normalize_file_path(config_path); + logger.debug("Setting up boto3 default session ...") + aws_utils.setup_default_session(profile_name) + + logger.debug("Setting up config directory and files ...") + normalized_config_path: str = file_utils.normalize_file_path(config_path) if normalized_config_path: self._configuration.config_directory = normalized_config_path else: @@ -61,6 +65,7 @@ class ConfigurationManager(object): file_utils.find_files_with_suffix_under_directory(self._configuration.config_directory, constants.RESOURCE_MAPPING_CONFIG_FILE_NAME_SUFFIX) + logger.debug("Setting up aws account id and region ...") self._configuration.account_id = aws_utils.get_default_account_id() self._configuration.region = aws_utils.get_default_region() except (RuntimeError, FileNotFoundError) as e: diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/controller_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/controller_manager.py index 48c45d0350..61eaaddea2 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/controller_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/controller_manager.py @@ -12,6 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. from __future__ import annotations import logging +from controller.error_controller import ErrorController from controller.import_resources_controller import ImportResourcesController from controller.view_edit_controller import ViewEditController from model import error_messages @@ -33,8 +34,9 @@ class ControllerManager(object): def __init__(self) -> None: if ControllerManager.__instance is None: - self._view_edit_controller: ViewEditController = ViewEditController() - self._import_resources_controller: ImportResourcesController = ImportResourcesController() + self._error_controller: ErrorController = None + self._view_edit_controller: ViewEditController = None + self._import_resources_controller: ImportResourcesController = None ControllerManager.__instance = self else: raise AssertionError(error_messages.SINGLETON_OBJECT_ERROR_MESSAGE.format("ControllerManager")) @@ -47,9 +49,17 @@ class ControllerManager(object): def view_edit_controller(self) -> ViewEditController: return self._view_edit_controller - def setup(self) -> None: - logger.info("Setting up ViewEdit and ImportResource controllers ...") - self._view_edit_controller.setup() - self._import_resources_controller.setup() - self._import_resources_controller.add_import_resources_sender.connect( - self._view_edit_controller.add_import_resources_receiver) + def setup(self, setup_error: bool) -> None: + if setup_error: + logger.debug("Setting up Error controllers ...") + self._error_controller = ErrorController() + self._error_controller.setup() + else: + logger.debug("Setting up ViewEdit and ImportResource controllers ...") + self._view_edit_controller = ViewEditController() + self._import_resources_controller = ImportResourcesController() + + self._view_edit_controller.setup() + self._import_resources_controller.setup() + self._import_resources_controller.add_import_resources_sender.connect( + self._view_edit_controller.add_import_resources_receiver) diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/thread_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/thread_manager.py index 516a085439..4de928bb1b 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/thread_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/thread_manager.py @@ -37,10 +37,13 @@ class ThreadManager(object): else: raise AssertionError(error_messages.SINGLETON_OBJECT_ERROR_MESSAGE.format("ThreadManager")) - def setup(self, thread_count: int = 1) -> None: - # Based on prototype use case, we just need 1 thread - logger.info(f"Setting up thread pool with MaxThreadCount={thread_count} ...") - self._thread_pool.setMaxThreadCount(thread_count) + def setup(self, setup_error: bool, thread_count: int = 1) -> None: + if setup_error: + logger.debug("Skip thread pool creation, as there is major setup error.") + else: + # Based on prototype use case, we just need 1 thread + logger.debug(f"Setting up thread pool with MaxThreadCount={thread_count} ...") + self._thread_pool.setMaxThreadCount(thread_count) """Reserves a thread and uses it to run runnable worker, unless this thread will make the current thread count exceed max thread count. In that case, runnable is added to a run queue instead.""" diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/view_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/view_manager.py index fff5c4b59a..46158afb39 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/view_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/view_manager.py @@ -16,6 +16,7 @@ from PySide2.QtGui import QIcon from PySide2.QtWidgets import (QMainWindow, QStackedWidget, QWidget) from model import (error_messages, view_size_constants) +from view.error_page import ErrorPage from view.import_resources_page import ImportResourcesPage from view.view_edit_page import ViewEditPage @@ -27,6 +28,10 @@ class ViewManagerConstants(object): IMPORT_RESOURCES_PAGE_INDEX: int = 1 +# Error page will be a single page +ERROR_PAGE_INDEX: int = 0 + + class ViewManager(object): """ View manager maintains the main stacked pages for this tool, which @@ -58,21 +63,32 @@ class ViewManager(object): ViewManager.__instance = self else: raise AssertionError(error_messages.SINGLETON_OBJECT_ERROR_MESSAGE.format("ViewManager")) - + + def get_error_page(self) -> QWidget: + return self._resource_mapping_stacked_pages.widget(ERROR_PAGE_INDEX) + def get_view_edit_page(self) -> QWidget: return self._resource_mapping_stacked_pages.widget(ViewManagerConstants.VIEW_AND_EDIT_PAGE_INDEX) def get_import_resources_page(self) -> QWidget: return self._resource_mapping_stacked_pages.widget(ViewManagerConstants.IMPORT_RESOURCES_PAGE_INDEX) - def setup(self) -> None: - logger.debug("Setting up ViewEdit and ImportResources view pages ...") - self._resource_mapping_stacked_pages.addWidget(ViewEditPage()) - self._resource_mapping_stacked_pages.addWidget(ImportResourcesPage()) + def setup(self, setup_error: bool) -> None: + if setup_error: + logger.debug("Setting up Error view pages ...") + self._resource_mapping_stacked_pages.addWidget(ErrorPage()) + self._main_window.adjustSize() # fit error page size + else: + logger.debug("Setting up ViewEdit and ImportResources view pages ...") + self._resource_mapping_stacked_pages.addWidget(ViewEditPage()) + self._resource_mapping_stacked_pages.addWidget(ImportResourcesPage()) - def show(self) -> None: + def show(self, setup_error: bool) -> None: """Show up the tool view by setting default page index and showing main widget""" - self._resource_mapping_stacked_pages.setCurrentIndex(ViewManagerConstants.VIEW_AND_EDIT_PAGE_INDEX) + if setup_error: + self._resource_mapping_stacked_pages.setCurrentIndex(ERROR_PAGE_INDEX) + else: + self._resource_mapping_stacked_pages.setCurrentIndex(ViewManagerConstants.VIEW_AND_EDIT_PAGE_INDEX) self._main_window.show() def switch_to_view_edit_page(self) -> None: diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/model/error_messages.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/model/error_messages.py index 08ab2a146d..7aef2068a2 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/model/error_messages.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/model/error_messages.py @@ -9,6 +9,12 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ +ERROR_PAGE_TOOL_SETUP_ERROR_MESSAGE: str = \ + "AWS credentials are missing or invalid. See " \ + ""\ + "documentation for details." \ + "
Check log file under Gems/AWSCore/Code/Tool/ResourceMappingTool for further information." + VIEW_EDIT_PAGE_SAVING_FAILED_WITH_INVALID_ROW_ERROR_MESSAGE: str = \ "Row {} have errors. Please correct errors or delete the row to proceed." VIEW_EDIT_PAGE_READ_FROM_JSON_FAILED_WITH_UNEXPECTED_FILE_ERROR_MESSAGE: str = \ diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/model/notification_label_text.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/model/notification_label_text.py index 1819e4c4ce..aecff69fb1 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/model/notification_label_text.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/model/notification_label_text.py @@ -11,6 +11,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. NOTIFICATION_LOADING_MESSAGE: str = "Loading..." +ERROR_PAGE_OK_TEXT: str = "OK" + VIEW_EDIT_PAGE_CONFIG_FILE_TEXT: str = "Config File" VIEW_EDIT_PAGE_CONFIG_LOCATION_TEXT: str = "Config Location:" VIEW_EDIT_PAGE_ADD_ROW_TEXT: str = "Add Row" diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/model/view_size_constants.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/model/view_size_constants.py index c01d81b75c..7c0eb55885 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/model/view_size_constants.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/model/view_size_constants.py @@ -17,6 +17,18 @@ MAIN_PAGE_LAYOUT_MARGIN_TOPBOTTOM: int = 15 INTERACTION_COMPONENT_HEIGHT: int = 25 +"""error page related constants""" +ERROR_PAGE_LAYOUT_MARGIN_LEFTRIGHT: int = 10 +ERROR_PAGE_LAYOUT_MARGIN_TOPBOTTOM: int = 10 + +ERROR_PAGE_MAIN_WINDOW_WIDTH: int = 600 +ERROR_PAGE_MAIN_WINDOW_HEIGHT: int = 145 + +ERROR_PAGE_NOTIFICATION_AREA_HEIGHT: int = 100 +ERROR_PAGE_FOOTER_AREA_HEIGHT: int = 45 + +OK_BUTTON_WIDTH: int = 90 + """view edit page related constants""" VIEW_EDIT_PAGE_HEADER_AREA_HEIGHT: int = 65 VIEW_EDIT_PAGE_CENTER_AREA_HEIGHT: int = 500 diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py index 6a56491b24..177dae349e 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py @@ -73,32 +73,22 @@ if __name__ == "__main__": except FileNotFoundError: logger.warning("Failed to load style sheet for resource mapping tool") - logger.info("Initializing boto3 default session ...") - try: - aws_utils.setup_default_session(arguments.profile) - except RuntimeError as error: - logger.error(error) - environment_utils.cleanup_qt_environment() - exit(-1) - logger.info("Initializing configuration manager ...") configuration_manager: ConfigurationManager = ConfigurationManager() - if not configuration_manager.setup(arguments.config_path): - environment_utils.cleanup_qt_environment() - exit(-1) + configuration_error: bool = not configuration_manager.setup(arguments.profile, arguments.config_path) logger.info("Initializing thread manager ...") thread_manager: ThreadManager = ThreadManager() - thread_manager.setup() + thread_manager.setup(configuration_error) logger.info("Initializing view manager ...") view_manager: ViewManager = ViewManager() - view_manager.setup() + view_manager.setup(configuration_error) logger.info("Initializing controller manager ...") controller_manager: ControllerManager = ControllerManager() - controller_manager.setup() + controller_manager.setup(configuration_error) - view_manager.show() + view_manager.show(configuration_error) sys.exit(app.exec_()) diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/base_style_sheet.qss b/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/base_style_sheet.qss index c23c3d90bd..2638052e2c 100644 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/base_style_sheet.qss +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/base_style_sheet.qss @@ -368,3 +368,18 @@ QFrame#NotificationFrame margin: 4px; padding: 4px; } + +QFrame#ErrorPage +{ + background-color: #2d2d2d; + border: 1px solid #4A90E2; + border-radius: 2px; + margin: 0px; + padding: 15px; +} + +QFrame#ErrorPage QLabel#NotificationIcon +{ + padding-left: 15px; + padding-right: 15px; +} diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_configuration_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_configuration_manager.py index 552f9fff01..7c6ee496a0 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_configuration_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_configuration_manager.py @@ -33,6 +33,7 @@ class TestConfigurationManager(TestCase): def test_get_instance_raise_exception(self) -> None: self.assertRaises(Exception, ConfigurationManager) + @patch("utils.aws_utils.setup_default_session") @patch("utils.aws_utils.get_default_region", return_value=_expected_region) @patch("utils.aws_utils.get_default_account_id", return_value=_expected_account_id) @patch("utils.file_utils.find_files_with_suffix_under_directory", return_value=_expected_config_files) @@ -42,8 +43,10 @@ class TestConfigurationManager(TestCase): mock_check_path_exists: MagicMock, mock_find_files_with_suffix_under_directory: MagicMock, mock_get_default_account_id: MagicMock, - mock_get_default_region: MagicMock) -> None: - TestConfigurationManager._expected_configuration_manager.setup("") + mock_get_default_region: MagicMock, + mock_setup_default_session: MagicMock) -> None: + TestConfigurationManager._expected_configuration_manager.setup("", "") + mock_setup_default_session.assert_called_once() mock_get_current_directory_path.assert_called_once() mock_check_path_exists.assert_called_once_with(TestConfigurationManager._expected_directory_path) mock_find_files_with_suffix_under_directory.assert_called_once_with( @@ -59,6 +62,7 @@ class TestConfigurationManager(TestCase): assert TestConfigurationManager._expected_configuration_manager.configuration.region == \ TestConfigurationManager._expected_region + @patch("utils.aws_utils.setup_default_session") @patch("utils.aws_utils.get_default_region", return_value=_expected_region) @patch("utils.aws_utils.get_default_account_id", return_value=_expected_account_id) @patch("utils.file_utils.find_files_with_suffix_under_directory", return_value=_expected_config_files) @@ -68,8 +72,11 @@ class TestConfigurationManager(TestCase): mock_check_path_exists: MagicMock, mock_find_files_with_suffix_under_directory: MagicMock, mock_get_default_account_id: MagicMock, - mock_get_default_region: MagicMock) -> None: - TestConfigurationManager._expected_configuration_manager.setup(TestConfigurationManager._expected_directory_path) + mock_get_default_region: MagicMock, + mock_setup_default_session: MagicMock) -> None: + TestConfigurationManager._expected_configuration_manager.setup( + "", TestConfigurationManager._expected_directory_path) + mock_setup_default_session.assert_called_once() mock_normalize_file_path.assert_called_once() mock_check_path_exists.assert_called_once_with(TestConfigurationManager._expected_directory_path) mock_find_files_with_suffix_under_directory.assert_called_once_with( diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_controller_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_controller_manager.py index 93e49415ca..7f2f1dd3f2 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_controller_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_controller_manager.py @@ -25,6 +25,9 @@ class TestControllerManager(TestCase): @classmethod def setUpClass(cls) -> None: + error_controller_patcher: patch = patch("manager.controller_manager.ErrorController") + cls._mock_error_controller = error_controller_patcher.start() + import_resources_controller_patcher: patch = patch("manager.controller_manager.ImportResourcesController") cls._mock_import_resources_controller = import_resources_controller_patcher.start() @@ -52,8 +55,14 @@ class TestControllerManager(TestCase): mocked_import_resources_controller: MagicMock = \ TestControllerManager._mock_import_resources_controller.return_value - TestControllerManager._expected_controller_manager.setup() + TestControllerManager._expected_controller_manager.setup(False) mocked_view_edit_controller.setup.assert_called_once() mocked_import_resources_controller.setup.assert_called_once() mocked_import_resources_controller.add_import_resources_sender.connect.assert_called_once_with( mocked_view_edit_controller.add_import_resources_receiver) + + def test_setup_error_controller_setup_gets_invoked(self) -> None: + mocked_error_controller: MagicMock = TestControllerManager._mock_error_controller.return_value + + TestControllerManager._expected_controller_manager.setup(True) + mocked_error_controller.setup.assert_called_once() diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_thread_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_thread_manager.py index 456a17efe2..06e2b4abbd 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_thread_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_thread_manager.py @@ -45,9 +45,15 @@ class TestThreadManager(TestCase): def test_setup_thread_pool_setup_with_expected_configuration(self) -> None: mocked_thread_pool: MagicMock = TestThreadManager._mock_thread_pool.return_value - TestThreadManager._expected_thread_manager.setup() + TestThreadManager._expected_thread_manager.setup(False) mocked_thread_pool.setMaxThreadCount.assert_called_once_with(1) + def test_setup_thread_pool_skip_setup(self) -> None: + mocked_thread_pool: MagicMock = TestThreadManager._mock_thread_pool.return_value + + TestThreadManager._expected_thread_manager.setup(True) + mocked_thread_pool.setMaxThreadCount.asset_not_called() + def test_start_thread_pool_start_expected_worker(self) -> None: mocked_thread_pool: MagicMock = TestThreadManager._mock_thread_pool.return_value expected_mocked_worker: MagicMock = MagicMock() diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_view_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_view_manager.py index e5c84c6579..57ec41ba0a 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_view_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/tests/unit/manager/test_view_manager.py @@ -13,13 +13,14 @@ from typing import List from unittest import TestCase from unittest.mock import (call, MagicMock, patch) -from manager.view_manager import (ViewManager, ViewManagerConstants) +from manager.view_manager import (ERROR_PAGE_INDEX, ViewManager, ViewManagerConstants) class TestViewManager(TestCase): """ ViewManager unit test cases """ + _mock_error_page: MagicMock _mock_import_resources_page: MagicMock _mock_view_edit_page: MagicMock _mock_main_window: MagicMock @@ -28,6 +29,9 @@ class TestViewManager(TestCase): @classmethod def setUpClass(cls) -> None: + error_page_patcher: patch = patch("manager.view_manager.ErrorPage") + cls._mock_error_page = error_page_patcher.start() + import_resources_page_patcher: patch = patch("manager.view_manager.ImportResourcesPage") cls._mock_import_resources_page = import_resources_page_patcher.start() @@ -60,6 +64,15 @@ class TestViewManager(TestCase): def test_get_instance_raise_exception(self) -> None: self.assertRaises(Exception, ViewManager) + def test_get_error_page_return_expected_page(self) -> None: + expected_page: MagicMock = TestViewManager._mock_error_page.return_value + mocked_stacked_pages: MagicMock = TestViewManager._mock_stacked_pages.return_value + mocked_stacked_pages.widget.return_value = expected_page + + actual_page: MagicMock = TestViewManager._expected_view_manager.get_error_page() + mocked_stacked_pages.widget.assert_called_once_with(ERROR_PAGE_INDEX) + assert actual_page == expected_page + def test_get_import_resources_page_return_expected_page(self) -> None: expected_page: MagicMock = TestViewManager._mock_import_resources_page.return_value mocked_stacked_pages: MagicMock = TestViewManager._mock_stacked_pages.return_value @@ -83,18 +96,34 @@ class TestViewManager(TestCase): mocked_import_resources_page: MagicMock = TestViewManager._mock_import_resources_page.return_value mocked_view_edit_page: MagicMock = TestViewManager._mock_view_edit_page.return_value - TestViewManager._expected_view_manager.setup() + TestViewManager._expected_view_manager.setup(False) mocked_calls: List[call] = [call(mocked_view_edit_page), call(mocked_import_resources_page)] mocked_stacked_pages.addWidget.assert_has_calls(mocked_calls) + def test_setup_error_page_only(self) -> None: + mocked_stacked_pages: MagicMock = TestViewManager._mock_stacked_pages.return_value + mocked_error_page: MagicMock = TestViewManager._mock_error_page.return_value + + TestViewManager._expected_view_manager.setup(True) + mocked_calls: List[call] = [call(mocked_error_page)] + mocked_stacked_pages.addWidget.assert_has_calls(mocked_calls) + def test_show_stacked_pages_show_with_expected_index(self) -> None: mocked_stacked_pages: MagicMock = TestViewManager._mock_stacked_pages.return_value mocked_main_window: MagicMock = TestViewManager._mock_main_window.return_value - TestViewManager._expected_view_manager.show() + TestViewManager._expected_view_manager.show(False) mocked_stacked_pages.setCurrentIndex.assert_called_once_with(ViewManagerConstants.VIEW_AND_EDIT_PAGE_INDEX) mocked_main_window.show.assert_called_once() + def test_show_stacked_pages_show_error_plage(self) -> None: + mocked_stacked_pages: MagicMock = TestViewManager._mock_stacked_pages.return_value + mocked_main_window: MagicMock = TestViewManager._mock_main_window.return_value + + TestViewManager._expected_view_manager.show(True) + mocked_stacked_pages.setCurrentIndex.assert_called_once_with(ERROR_PAGE_INDEX) + mocked_main_window.show.assert_called_once() + def test_switch_to_view_edit_page_stacked_pages_switch_to_expected_index(self) -> None: mocked_stacked_pages: MagicMock = TestViewManager._mock_stacked_pages.return_value diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/view/common_view_components.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/view/common_view_components.py index 8aad0a785c..39c94ccf77 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/view/common_view_components.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/view/common_view_components.py @@ -37,10 +37,12 @@ class NotificationFrame(QFrame): self.setFrameStyle(QFrame.StyledPanel | QFrame.Plain) icon_label: QLabel = QLabel(self) + icon_label.setObjectName("NotificationIcon") icon_label.setPixmap(pixmap) self._title_label: QLabel = QLabel(title, self) - self._title_label.setObjectName("Title") + self._title_label.setOpenExternalLinks(True) + self._title_label.setObjectName("NotificationTitle") self._title_label.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)) self._title_label.setWordWrap(True) diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/view/error_page.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/view/error_page.py new file mode 100644 index 0000000000..2b42d2bfc2 --- /dev/null +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/view/error_page.py @@ -0,0 +1,89 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +""" + +from PySide2.QtGui import QPixmap +from PySide2.QtWidgets import (QHBoxLayout, QLayout, QPushButton, QSizePolicy, QSpacerItem, QVBoxLayout, QWidget) + +from model import (error_messages, notification_label_text, view_size_constants) +from view.common_view_components import NotificationFrame + + +class ErrorPage(QWidget): + """ + Error Page + """ + def __init__(self) -> None: + super().__init__() + self.setGeometry(0, 0, + view_size_constants.ERROR_PAGE_MAIN_WINDOW_WIDTH, + view_size_constants.ERROR_PAGE_MAIN_WINDOW_HEIGHT) + + page_vertical_layout: QVBoxLayout = QVBoxLayout(self) + page_vertical_layout.setSizeConstraint(QLayout.SetMinimumSize) + page_vertical_layout.setMargin(0) + + self._setup_notification_area() + page_vertical_layout.addWidget(self._notification_area) + + self._setup_footer_area() + page_vertical_layout.addWidget(self._footer_area) + + def _setup_notification_area(self) -> None: + self._notification_area: QWidget = QWidget(self) + self._notification_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) + self._notification_area.setMinimumSize(view_size_constants.ERROR_PAGE_MAIN_WINDOW_WIDTH, + view_size_constants.ERROR_PAGE_NOTIFICATION_AREA_HEIGHT) + + notification_area_layout: QVBoxLayout = QVBoxLayout(self._notification_area) + notification_area_layout.setSizeConstraint(QLayout.SetMinimumSize) + notification_area_layout.setContentsMargins( + view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_LEFTRIGHT, + view_size_constants.MAIN_PAGE_LAYOUT_MARGIN_TOPBOTTOM, + view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_LEFTRIGHT, 0) + + notification_frame: NotificationFrame = \ + NotificationFrame(self, QPixmap(":/error_report_warning.svg"), + error_messages.ERROR_PAGE_TOOL_SETUP_ERROR_MESSAGE, False) + notification_frame.setObjectName("ErrorPage") + notification_frame.setMinimumSize(view_size_constants.ERROR_PAGE_MAIN_WINDOW_WIDTH, + view_size_constants.ERROR_PAGE_NOTIFICATION_AREA_HEIGHT) + notification_frame.setVisible(True) + notification_area_layout.addWidget(notification_frame) + + def _setup_footer_area(self) -> None: + self._footer_area: QWidget = QWidget(self) + self._footer_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) + self._footer_area.setMaximumSize(view_size_constants.TOOL_APPLICATION_MAIN_WINDOW_WIDTH, + view_size_constants.ERROR_PAGE_FOOTER_AREA_HEIGHT) + + footer_area_layout: QHBoxLayout = QHBoxLayout(self._footer_area) + footer_area_layout.setSizeConstraint(QLayout.SetMinimumSize) + footer_area_layout.setContentsMargins( + view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_LEFTRIGHT, + view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_TOPBOTTOM, + view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_LEFTRIGHT, + view_size_constants.ERROR_PAGE_LAYOUT_MARGIN_TOPBOTTOM) + + footer_area_spacer: QSpacerItem = QSpacerItem(view_size_constants.ERROR_PAGE_MAIN_WINDOW_WIDTH, + view_size_constants.INTERACTION_COMPONENT_HEIGHT, + QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) + footer_area_layout.addItem(footer_area_spacer) + + self._ok_button: QPushButton = QPushButton(self._footer_area) + self._ok_button.setObjectName("Secondary") + self._ok_button.setText(notification_label_text.ERROR_PAGE_OK_TEXT) + self._ok_button.setMinimumSize(view_size_constants.OK_BUTTON_WIDTH, + view_size_constants.INTERACTION_COMPONENT_HEIGHT) + footer_area_layout.addWidget(self._ok_button) + + @property + def ok_button(self) -> QPushButton: + return self._ok_button diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl index b221ee0a33..23e4ef0c10 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl @@ -295,6 +295,10 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float lightingOutput.m_diffuseColor.rgb += lightingOutput.m_specularColor.rgb; // add specular lightingOutput.m_specularColor.rgb = baseColor * (1.0 - lightingOutput.m_diffuseColor.w); } + else + { + lightingOutput.m_diffuseColor.w = -1; // Disable subsurface scattering + } return lightingOutput; } @@ -341,4 +345,3 @@ ForwardPassOutput StandardPbr_ForwardPassPS_EDS(VSOutput IN, bool isFrontFace : #endif return OUT; } - diff --git a/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass index 38a4524aa2..849094dd69 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass @@ -322,7 +322,14 @@ "Pass": "AuxGeomPass", "Attachment": "ColorInputOutput" } - } + }, + { + "LocalSlot": "DepthInputOutput", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "Depth" + } + } ] }, { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass index b2e0cf088e..314d999e4d 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/MainPipeline.pass @@ -427,6 +427,13 @@ "Pass": "DebugOverlayPass", "Attachment": "InputOutput" } + }, + { + "LocalSlot": "DepthInputOutput", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "Depth" + } } ] }, diff --git a/Gems/Atom/Feature/Common/Assets/Passes/UI.pass b/Gems/Atom/Feature/Common/Assets/Passes/UI.pass index 569fe1d722..ac43f17c11 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/UI.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/UI.pass @@ -7,6 +7,23 @@ "Name": "UIPassTemplate", "PassClass": "RasterPass", "Slots": [ + { + "Name": "DepthInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "DepthStencil", + "LoadStoreAction": { + "ClearValue": { + "Type": "DepthStencil", + "Value": [ + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "LoadActionStencil": "Clear" + } + }, { "Name": "InputOutput", "SlotType": "InputOutput", diff --git a/Gems/Atom/Feature/Common/Assets/Passes/UIParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/UIParent.pass index 0069e89401..4ae67b9b09 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/UIParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/UIParent.pass @@ -10,6 +10,11 @@ { "Name": "InputOutput", "SlotType": "InputOutput" + }, + { + "Name": "DepthInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "DepthStencil" } ], "PassRequests": [ @@ -24,6 +29,13 @@ "Pass": "Parent", "Attachment": "InputOutput" } + }, + { + "LocalSlot": "DepthInputOutput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "DepthInputOutput" + } } ], "PassData": { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader index 95ffc36a11..08b1e7c298 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetCS.shader @@ -10,7 +10,5 @@ "type": "Compute" } ] - }, - "DisabledRHIBackends": ["metal"] - + } } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli index 7ec5b43368..f4b1beeb3e 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MorphTargets/MorphTargetSRG.azsli @@ -16,7 +16,10 @@ ShaderResourceGroup MorphTargetPassSrg : SRG_PerPass { - RWBuffer m_accumulatedDeltas; + //Since we do Interlocked atomic operations on this buffer it can not be RWBuffer due to broken MetalSL generation. + //It stems from the fact that typed buffers gets converted to textures and that breaks with atomic operations. + //In future we can handle this under the hood via our metal shader pipeline + RWStructuredBuffer m_accumulatedDeltas; } // This class represents the data that is passed to the morph target compute shader of an individual delta diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl index df92e36f9a..4238392004 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHeatmap.azsl @@ -37,7 +37,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2D m_sceneLuminance; // This should be of size NUM_HISTOGRAM_BINS. - Buffer m_histogram; + StructuredBuffer m_histogram; Sampler LinearSampler { diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl index 05cc870eea..ee95515a2a 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.azsl @@ -20,7 +20,11 @@ ShaderResourceGroup PassSrg : SRG_PerPass { Texture2D m_inputTexture; - RWBuffer m_outputTexture; + + //Since we do Interlocked atomic operations on this buffer it can not be RWBuffer due to broken MetalSL generation. + //It stems from the fact that typed buffers gets converted to textures and that breaks with atomic operations. + //In future we can handle this under the hood via our metal shader pipeline + RWStructuredBuffer m_outputTexture; } groupshared uint shared_histogramBins[NUM_HISTOGRAM_BINS]; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader index 566144bab8..f3dd11e11a 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LuminanceHistogramGenerator.shader @@ -12,7 +12,5 @@ "type": "Compute" } ] - }, - "DisabledRHIBackends": ["metal"] - + } } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli index 5a9e44bade..e5407d2d9e 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli @@ -16,7 +16,7 @@ ShaderResourceGroup PassSrg : SRG_PerPass { - RWBuffer m_skinnedMeshOutputStream; + RWStructuredBuffer m_skinnedMeshOutputStream; } ShaderResourceGroup InstanceSrg : SRG_PerDraw diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/AcesOutputTransformLutPass.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/AcesOutputTransformLutPass.h index bc8673a64b..a6c7615574 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/AcesOutputTransformLutPass.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/AcesOutputTransformLutPass.h @@ -48,7 +48,9 @@ namespace AZ void SetShaperParams(const ShaperParams& shaperParams); private: explicit AcesOutputTransformLutPass(const RPI::PassDescriptor& descriptor); - void Init() override; + + // Pass behavior overrides... + void InitializeInternal() override; // Scope producer functions... void SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) override; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/AcesOutputTransformPass.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/AcesOutputTransformPass.h index 5a0ccdb32a..350b178cfa 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/AcesOutputTransformPass.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/AcesOutputTransformPass.h @@ -50,7 +50,9 @@ namespace AZ private: explicit AcesOutputTransformPass(const RPI::PassDescriptor& descriptor); - void Init() override; + + // Pass behavior overrides + void InitializeInternal() override; // Scope producer functions... void CompileResources(const RHI::FrameGraphCompileContext& context) override; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/ApplyShaperLookupTablePass.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/ApplyShaperLookupTablePass.h index 35d2fcf603..3b6d7f7c27 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/ApplyShaperLookupTablePass.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/ApplyShaperLookupTablePass.h @@ -45,7 +45,9 @@ namespace AZ protected: explicit ApplyShaperLookupTablePass(const RPI::PassDescriptor& descriptor); - void Init() override; + + // Pass behavior overrides... + void InitializeInternal() override; RHI::ShaderInputImageIndex m_shaderInputLutImageIndex; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/BakeAcesOutputTransformLutPass.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/BakeAcesOutputTransformLutPass.h index 60505653b7..0f6f1aa954 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/BakeAcesOutputTransformLutPass.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/BakeAcesOutputTransformLutPass.h @@ -57,15 +57,13 @@ namespace AZ explicit BakeAcesOutputTransformLutPass(const RPI::PassDescriptor& descriptor); // Pass behavior overrides... - void FrameBeginInternal(FramePrepareParams params) override; + void InitializeInternal() override; // RHI::ScopeProducer overrides... void SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) override; void CompileResources(const RHI::FrameGraphCompileContext& context) override; void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override; - void Init(); - void AcquireLutImage(); void ReleaseLutImage(); diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperFullScreenPass.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperFullScreenPass.h index 94e52d41b0..59c02accdd 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperFullScreenPass.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperFullScreenPass.h @@ -41,14 +41,11 @@ namespace AZ void SetInputReferencePassName(const Name& passName); void SetInputReferenceAttachmentName(const Name& attachmentName); - // Pass behavior overrides - virtual void BuildAttachmentsInternal() override; - protected: explicit DisplayMapperFullScreenPass(const RPI::PassDescriptor& descriptor); - // FullscreenTrianglePass behavior overrides - virtual void Init(); + // Pass behavior overrides... + virtual void BuildInternal() override; private: Name m_inputReferencePassName = Name{ "Parent" }; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperPass.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperPass.h index 67d9eae9ae..b741a10953 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperPass.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperPass.h @@ -76,7 +76,8 @@ namespace AZ DisplayMapperPass(const RPI::PassDescriptor& descriptor); // Pass behavior overrides - void BuildAttachmentsInternal() final; + void BuildInternal() final; + void InitializeInternal() final; void FrameBeginInternal(FramePrepareParams params) final; void FrameEndInternal() final; void CreateChildPassesInternal() final; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/OutputTransformPass.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/OutputTransformPass.h index b266eab14b..caee7ae3dd 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/OutputTransformPass.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/OutputTransformPass.h @@ -49,7 +49,9 @@ namespace AZ protected: explicit OutputTransformPass(const RPI::PassDescriptor& descriptor); - void Init() override; + + // Pass behavior overrides + void InitializeInternal() override; private: // Scope producer functions... diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/LuxCore/LuxCoreTexturePass.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/LuxCore/LuxCoreTexturePass.h index 23e9c220b2..2ad2af3efd 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/LuxCore/LuxCoreTexturePass.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/LuxCore/LuxCoreTexturePass.h @@ -42,7 +42,7 @@ namespace AZ protected: // Pass behavior overrides void CreateChildPassesInternal() final; - void BuildAttachmentsInternal() final; + void BuildInternal() final; void FrameBeginInternal(FramePrepareParams params) final; private: diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/LuxCore/RenderTexturePass.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/LuxCore/RenderTexturePass.h index 5ae5008a92..cd148d3452 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/LuxCore/RenderTexturePass.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/LuxCore/RenderTexturePass.h @@ -46,7 +46,7 @@ namespace AZ private: - void BuildAttachmentsInternal() override; + void BuildInternal() override; void FrameBeginInternal(FramePrepareParams params) override; void UpdataAttachment(); diff --git a/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardColorResolvePass.cpp b/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardColorResolvePass.cpp index 3ebb6b370c..a6c440cf39 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardColorResolvePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardColorResolvePass.cpp @@ -60,7 +60,7 @@ namespace AZ Base::FrameBeginInternal(params); } - void CheckerboardColorResolvePass::BuildAttachmentsInternal() + void CheckerboardColorResolvePass::BuildInternal() { // For each bound attachments they are the inputs from current frame. // We use them to get their owner CheckerboardPass then find the render targets from last frame @@ -99,7 +99,7 @@ namespace AZ // reset frame offset to 0 since attachments are rebuilt m_frameOffset = 0; - Base::BuildAttachmentsInternal(); + Base::BuildInternal(); } void CheckerboardColorResolvePass::CompileResources(const RHI::FrameGraphCompileContext& context) @@ -135,7 +135,7 @@ namespace AZ void CheckerboardColorResolvePass::FrameEndInternal() { // For the input slots for current frame, they always get updated when CheckerboardPass updates the render targets - // But for the input slots for previous frame, we need to manually update them since they were manually attached in BuildAttachmentsInternal() + // But for the input slots for previous frame, we need to manually update them since they were manually attached in BuildInternal() // // When pass attachment was built, CheckerboardPass creates two resources for each render target. // For example, diffuse_0 and diffuse_1 which diffuse_0 is for even frame and diffuse_1 is for odd frame. diff --git a/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardColorResolvePass.h b/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardColorResolvePass.h index 8cb15d5f49..93930e2bbc 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardColorResolvePass.h +++ b/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardColorResolvePass.h @@ -52,7 +52,7 @@ namespace AZ protected: // Pass overrides... void FrameBeginInternal(FramePrepareParams params) override; - void BuildAttachmentsInternal() override; + void BuildInternal() override; void FrameEndInternal() override; // Scope producer functions... diff --git a/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardPass.cpp b/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardPass.cpp index 09c69dfe18..a781c460f5 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardPass.cpp @@ -50,7 +50,7 @@ namespace AZ Base::FrameBeginInternal(params); } - void CheckerboardPass::BuildAttachmentsInternal() + void CheckerboardPass::BuildInternal() { Data::Instance pool = RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool(); @@ -101,7 +101,7 @@ namespace AZ // reset frame offset to 0 since attachments are rebuilt m_frameOffset = 0; - Base::BuildAttachmentsInternal(); + Base::BuildInternal(); } diff --git a/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardPass.h b/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardPass.h index 3b905cc3db..02429bb837 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/Checkerboard/CheckerboardPass.h @@ -40,7 +40,7 @@ namespace AZ protected: // Pass overrides... void FrameBeginInternal(FramePrepareParams params); - void BuildAttachmentsInternal() override; + void BuildInternal() override; void FrameEndInternal() override; private: diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/CascadedShadowmapsPass.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/CascadedShadowmapsPass.cpp index d83201533c..b9beac4285 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/CascadedShadowmapsPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/CascadedShadowmapsPass.cpp @@ -100,7 +100,7 @@ namespace AZ m_arraySize = arraySize; m_updateChildren = true; - QueueForBuildAttachments(); + QueueForBuildAndInitialization(); m_atlas.Initialize(); for (size_t cascadeIndex = 0; cascadeIndex < m_arraySize; ++cascadeIndex) @@ -149,7 +149,7 @@ namespace AZ return m_atlas; } - void CascadedShadowmapsPass::BuildAttachmentsInternal() + void CascadedShadowmapsPass::BuildInternal() { UpdateChildren(); @@ -159,7 +159,7 @@ namespace AZ } UpdateShadowmapImageSize(); - Base::BuildAttachmentsInternal(); + Base::BuildInternal(); } void CascadedShadowmapsPass::GetPipelineViewTags(RPI::SortedPipelineViewTags& outTags) const @@ -215,7 +215,7 @@ namespace AZ AZ_RPI_PASS_WARNING(child, "CascadedShadowmapsPass child Pass creation failed for %d", cascadeIndex); if (child) { - child->QueueForBuildAttachments(); + child->QueueForBuildAndInitialization(); AddChild(child); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/CascadedShadowmapsPass.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/CascadedShadowmapsPass.h index aa95e0af44..772be9466d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/CascadedShadowmapsPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/CascadedShadowmapsPass.h @@ -53,7 +53,7 @@ namespace AZ explicit CascadedShadowmapsPass(const RPI::PassDescriptor& descriptor); // RPI::Pass overrides... - void BuildAttachmentsInternal() override; + void BuildInternal() override; void GetPipelineViewTags(RPI::SortedPipelineViewTags& outTags) const override; void GetViewDrawListInfo(RHI::DrawListMask& outDrawListMask, RPI::PassesByDrawList& outPassesByDrawList, const RPI::PipelineViewTag& viewTag) const override; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index 9b40097716..c4f4bc54b3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -1106,7 +1106,7 @@ namespace AZ { for (EsmShadowmapsPass* pass : it.second) { - pass->QueueForBuildAttachments(); + pass->QueueForBuildAndInitialization(); } } } diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.cpp index a9b43ef1e7..fa293ac9fd 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.cpp @@ -163,7 +163,6 @@ namespace AZ void LightCullingPass::ResetInternal() { - m_initialized = false; m_tileDataIndex = -1; m_constantDataIndex.Reset(); @@ -260,7 +259,7 @@ namespace AZ return gridPixelSize; } - void LightCullingPass::BuildAttachmentsInternal() + void LightCullingPass::BuildInternal() { m_tileDataIndex = FindInputBinding(AZ::Name("TileLightData")); CreateLightList(); diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.h index d8699ea0c7..8080379716 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.h @@ -50,7 +50,7 @@ namespace AZ // Pass behavior overrides... void ResetInternal()override; - void BuildAttachmentsInternal() override; + void BuildInternal() override; // Scope producer functions... void CompileResources(const RHI::FrameGraphCompileContext& context) override; @@ -96,7 +96,6 @@ namespace AZ AZ::RHI::ShaderInputNameIndex m_constantDataIndex = "m_constantData"; - bool m_initialized = false; Data::Instance m_lightList; uint32_t m_tileDataIndex = -1; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.cpp index 26e4ed9f6e..d13e72815b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.cpp @@ -108,7 +108,7 @@ namespace AZ return -1; } - void LightCullingRemap::BuildAttachmentsInternal() + void LightCullingRemap::BuildInternal() { m_tileDataIndex = FindInputOutputBinding(AZ::Name("TileLightData")); m_tileDim = GetTileDataBufferResolution(); diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.h index 0738acd459..6d60f93a64 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.h @@ -55,7 +55,7 @@ namespace AZ // Pass behavior overrides... void ResetInternal()override; - void BuildAttachmentsInternal() override; + void BuildInternal() override; // RHI::ScopeProducer overrides... void SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) override; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingTilePreparePass.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingTilePreparePass.cpp index 816ef25bc6..4f2a4f0346 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingTilePreparePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingTilePreparePass.cpp @@ -167,37 +167,36 @@ namespace AZ AZ_Assert(setOk, "LightCullingTilePreparePass::SetConstantData() - could not set constant data"); } - void LightCullingTilePreparePass::BuildAttachmentsInternal() + void LightCullingTilePreparePass::BuildInternal() { ChooseShaderVariant(); } - void LightCullingTilePreparePass::OnShaderReinitialized(const AZ::RPI::Shader&) + void LightCullingTilePreparePass::OnShaderReloaded() { LoadShader(); - if (!m_flags.m_queuedForBuildAttachment && !m_flags.m_isBuildingAttachments) + AZ_Assert(GetPassState() != RPI::PassState::Rendering, "LightCullingTilePreparePass: Trying to reload shader during rendering"); + if (GetPassState() == RPI::PassState::Idle) { ChooseShaderVariant(); } } + + void LightCullingTilePreparePass::OnShaderReinitialized(const AZ::RPI::Shader&) + { + OnShaderReloaded(); + } + void LightCullingTilePreparePass::OnShaderAssetReinitialized(const Data::Asset&) { - LoadShader(); - if (!m_flags.m_queuedForBuildAttachment && !m_flags.m_isBuildingAttachments) - { - ChooseShaderVariant(); - } + OnShaderReloaded(); } void LightCullingTilePreparePass::OnShaderVariantReinitialized( const AZ::RPI::Shader&, const AZ::RPI::ShaderVariantId&, AZ::RPI::ShaderVariantStableId) { - LoadShader(); - if (!m_flags.m_queuedForBuildAttachment && !m_flags.m_isBuildingAttachments) - { - ChooseShaderVariant(); - } + OnShaderReloaded(); } } // namespace Render diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingTilePreparePass.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingTilePreparePass.h index efba105912..0febb66e3d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingTilePreparePass.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingTilePreparePass.h @@ -50,7 +50,7 @@ namespace AZ LightCullingTilePreparePass(const RPI::PassDescriptor& descriptor); // Pass behavior overrides... - void BuildAttachmentsInternal() override; + void BuildInternal() override; /////////////////////////////////////////////////////////////////// // ShaderReloadNotificationBus overrides... @@ -73,6 +73,7 @@ namespace AZ const AZ::RPI::ShaderVariant& CreateShaderVariant(); void CreatePipelineStateFromShaderVariant(const RPI::ShaderVariant& shaderVariant); void SetConstantData(); + void OnShaderReloaded(); AZ::RHI::ShaderInputNameIndex m_constantDataIndex = "m_constantData"; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/ProjectedShadowmapsPass.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/ProjectedShadowmapsPass.cpp index d77419fe8f..2f1badaae7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/ProjectedShadowmapsPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/ProjectedShadowmapsPass.cpp @@ -73,7 +73,7 @@ namespace AZ { m_sizes = sizes; m_updateChildren = true; - QueueForBuildAttachments(); + QueueForBuildAndInitialization(); m_atlas.Initialize(); for (const auto& it : m_sizes) @@ -156,7 +156,7 @@ namespace AZ return m_atlas; } - void ProjectedShadowmapsPass::BuildAttachmentsInternal() + void ProjectedShadowmapsPass::BuildInternal() { UpdateChildren(); @@ -177,7 +177,7 @@ namespace AZ imageDescriptor.m_size = RHI::Size(shadowmapWidth, shadowmapWidth, 1); imageDescriptor.m_arraySize = m_atlas.GetArraySliceCount(); - Base::BuildAttachmentsInternal(); + Base::BuildInternal(); } void ProjectedShadowmapsPass::GetPipelineViewTags(RPI::SortedPipelineViewTags& outTags) const diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/ProjectedShadowmapsPass.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/ProjectedShadowmapsPass.h index 63640d22c6..f1963cc885 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/ProjectedShadowmapsPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/ProjectedShadowmapsPass.h @@ -71,7 +71,7 @@ namespace AZ explicit ProjectedShadowmapsPass(const RPI::PassDescriptor& descriptor); // RPI::Pass overrides... - void BuildAttachmentsInternal() override; + void BuildInternal() override; void GetPipelineViewTags(RPI::SortedPipelineViewTags& outTags) const override; void GetViewDrawListInfo(RHI::DrawListMask& outDrawListMask, RPI::PassesByDrawList& outPassesByDrawList, const RPI::PipelineViewTag& viewTag) const override; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/ShadowmapPass.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/ShadowmapPass.cpp index 087a73a94d..d31947da5a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/ShadowmapPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/ShadowmapPass.cpp @@ -107,7 +107,7 @@ namespace AZ m_scissorState = scissor; } - void ShadowmapPass::BuildAttachmentsInternal() + void ShadowmapPass::BuildInternal() { RPI::Ptr parentPass = GetParent(); if (!parentPass) @@ -135,7 +135,7 @@ namespace AZ action.m_loadAction = m_clearEnabled ? RHI::AttachmentLoadAction::Clear : RHI::AttachmentLoadAction::DontCare; binding.m_unifiedScopeDesc = RHI::UnifiedScopeAttachmentDescriptor(attachmentId, imageViewDescriptor, action); - Base::BuildAttachmentsInternal(); + Base::BuildInternal(); } } // namespace Render diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/ShadowmapPass.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/ShadowmapPass.h index 9c308dc4df..2814761a69 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/ShadowmapPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/ShadowmapPass.h @@ -58,7 +58,7 @@ namespace AZ explicit ShadowmapPass(const RPI::PassDescriptor& descriptor); // RHI::Pass overrides... - void BuildAttachmentsInternal() override; + void BuildInternal() override; uint16_t m_arraySlice = 0; bool m_clearEnabled = true; diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformLutPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformLutPass.cpp index 104a91174d..5e3bde94e9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformLutPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformLutPass.cpp @@ -42,9 +42,9 @@ namespace AZ ReleaseLutImage(); } - void AcesOutputTransformLutPass::Init() + void AcesOutputTransformLutPass::InitializeInternal() { - DisplayMapperFullScreenPass::Init(); + DisplayMapperFullScreenPass::InitializeInternal(); AZ_Assert(m_shaderResourceGroup != nullptr, "AcesOutputTransformLutPass %s has a null shader resource group when calling Init.", GetPathName().GetCStr()); diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformPass.cpp index 6fe38ff032..ec34d40ea6 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformPass.cpp @@ -44,9 +44,9 @@ namespace AZ { } - void AcesOutputTransformPass::Init() + void AcesOutputTransformPass::InitializeInternal() { - DisplayMapperFullScreenPass::Init(); + DisplayMapperFullScreenPass::InitializeInternal(); AZ_Assert(m_shaderResourceGroup != nullptr, "AcesOutputTransformPass %s has a null shader resource group when calling Init.", GetPathName().GetCStr()); diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/ApplyShaperLookupTablePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/ApplyShaperLookupTablePass.cpp index 496b4dcc8f..ff910a4b81 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/ApplyShaperLookupTablePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/ApplyShaperLookupTablePass.cpp @@ -38,9 +38,9 @@ namespace AZ ReleaseLutImage(); } - void ApplyShaperLookupTablePass::Init() + void ApplyShaperLookupTablePass::InitializeInternal() { - DisplayMapperFullScreenPass::Init(); + DisplayMapperFullScreenPass::InitializeInternal(); AZ_Assert(m_shaderResourceGroup != nullptr, "ApplyShaperLookupTablePass %s has a null shader resource group when calling Init.", GetPathName().GetCStr()); diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/BakeAcesOutputTransformLutPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/BakeAcesOutputTransformLutPass.cpp index 3322b82ff7..7a32925e93 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/BakeAcesOutputTransformLutPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/BakeAcesOutputTransformLutPass.cpp @@ -38,17 +38,7 @@ namespace AZ ReleaseLutImage(); } - void BakeAcesOutputTransformLutPass::FrameBeginInternal(FramePrepareParams params) - { - if (!m_flags.m_initialized) - { - Init(); - } - - ComputePass::FrameBeginInternal(params); - } - - void BakeAcesOutputTransformLutPass::Init() + void BakeAcesOutputTransformLutPass::InitializeInternal() { AZ_Assert(m_shaderResourceGroup != nullptr, "BakeAcesOutputTransformLutPass %s has a null shader resource group when calling Init.", GetPathName().GetCStr()); @@ -67,7 +57,6 @@ namespace AZ m_shaderInputShaperBiasIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name{ "m_shaperBias" }); m_shaderInputShaperScaleIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name{ "m_shaperScale" }); } - m_flags.m_initialized = true; } void BakeAcesOutputTransformLutPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperFullScreenPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperFullScreenPass.cpp index ba83fabed3..03fcfbe71d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperFullScreenPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperFullScreenPass.cpp @@ -34,7 +34,7 @@ namespace AZ { } - void DisplayMapperFullScreenPass::BuildAttachmentsInternal() + void DisplayMapperFullScreenPass::BuildInternal() { RPI::PassConnection inConnection; inConnection.m_localSlot = InputAttachmentName; @@ -53,9 +53,5 @@ namespace AZ m_inputReferenceAttachmentName = attachmentName; } - void DisplayMapperFullScreenPass::Init() - { - FullscreenTrianglePass::Init(); - } } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp index 8ae790e12c..b37c218c21 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp @@ -46,8 +46,6 @@ namespace AZ DisplayMapperPass::DisplayMapperPass(const RPI::PassDescriptor& descriptor) : RPI::ParentPass(descriptor) { - m_flags.m_alreadyCreated = false; - AzFramework::NativeWindowHandle windowHandle = nullptr; AzFramework::WindowSystemRequestBus::BroadcastResult( windowHandle, @@ -61,8 +59,6 @@ namespace AZ { m_displayMapperConfigurationDescriptor = passData->m_config; } - - m_needToRebuildChildren = true; } DisplayMapperPass::~DisplayMapperPass() @@ -86,7 +82,7 @@ namespace AZ const Name& passName = fullscreenTrianglePass->GetName(); if (passName.GetStringView() == "CopyToSwapChain") { - fullscreenTrianglePass->Invalidate(); + fullscreenTrianglePass->QueueForInitialization(); } } } @@ -137,7 +133,7 @@ namespace AZ } } - void DisplayMapperPass::BuildAttachmentsInternal() + void DisplayMapperPass::BuildInternal() { const Name outputName = Name{ "Output" }; Name inputPass = Name{ "Parent" }; @@ -187,7 +183,21 @@ namespace AZ m_swapChainAttachmentBinding = FindAttachmentBinding(Name("SwapChainOutput")); - ParentPass::BuildAttachmentsInternal(); + ParentPass::BuildInternal(); + } + + void DisplayMapperPass::InitializeInternal() + { + // Force update on bindings because children of display mapper pass have their outputs connect to + // their parent's output, which is a non-conventional and non-standard workflow. Parent outputs are + // updated after child outputs, so post-build the child outputs do not yet point to the attachments on + // the parent bindings they are connected to. Forcing this refresh sets the attachment on the child output. + for (const RPI::Ptr& child : m_children) + { + child->UpdateConnectedBindings(); + } + + RPI::ParentPass::InitializeInternal(); } void DisplayMapperPass::FrameBeginInternal(FramePrepareParams params) @@ -199,24 +209,14 @@ namespace AZ void DisplayMapperPass::FrameEndInternal() { GetDisplayMapperConfiguration(); - if (m_needToRebuildChildren) - { - ClearChildren(); - BuildGradingLutTemplate(); - CreateGradingAndAcesPasses(); - } ParentPass::FrameEndInternal(); } void DisplayMapperPass::CreateChildPassesInternal() { - if (m_needToRebuildChildren) - { - ClearChildren(); - BuildGradingLutTemplate(); - CreateGradingAndAcesPasses(); - } - ParentPass::CreateChildPassesInternal(); + ClearChildren(); + BuildGradingLutTemplate(); + CreateGradingAndAcesPasses(); } AZStd::shared_ptr CreatePassTemplateHelper( @@ -485,7 +485,6 @@ namespace AZ { AddChild(m_ldrGradingLookupTablePass); } - m_needToRebuildChildren = false; } void DisplayMapperPass::GetDisplayMapperConfiguration() @@ -513,7 +512,8 @@ namespace AZ desc.m_ldrColorGradingLut != m_displayMapperConfigurationDescriptor.m_ldrColorGradingLut || desc.m_acesParameterOverrides.m_overrideDefaults != m_displayMapperConfigurationDescriptor.m_acesParameterOverrides.m_overrideDefaults) { - m_needToRebuildChildren = true; + m_flags.m_createChildren = true; + QueueForBuildAndInitialization(); } m_displayMapperConfigurationDescriptor = desc; } @@ -527,41 +527,15 @@ namespace AZ void DisplayMapperPass::ClearChildren() { - if (m_acesOutputTransformPass) - { - RemoveChild(m_acesOutputTransformPass); - m_acesOutputTransformPass = nullptr; - } - if (m_bakeAcesOutputTransformLutPass) - { - RemoveChild(m_bakeAcesOutputTransformLutPass); - m_bakeAcesOutputTransformLutPass = nullptr; - } - if (m_acesOutputTransformLutPass) - { - RemoveChild(m_acesOutputTransformLutPass); - m_acesOutputTransformLutPass = nullptr; - } - if (m_displayMapperPassthroughPass) - { - RemoveChild(m_displayMapperPassthroughPass); - m_displayMapperPassthroughPass = nullptr; - } - if (m_displayMapperOnlyGammaCorrectionPass) - { - RemoveChild(m_displayMapperOnlyGammaCorrectionPass); - m_displayMapperOnlyGammaCorrectionPass = nullptr; - } - if (m_ldrGradingLookupTablePass) - { - RemoveChild(m_ldrGradingLookupTablePass); - m_ldrGradingLookupTablePass = nullptr; - } - if (m_outputTransformPass) - { - RemoveChild(m_outputTransformPass); - m_outputTransformPass = nullptr; - } + RemoveChildren(); + + m_acesOutputTransformPass = nullptr; + m_bakeAcesOutputTransformLutPass = nullptr; + m_acesOutputTransformLutPass = nullptr; + m_displayMapperPassthroughPass = nullptr; + m_displayMapperOnlyGammaCorrectionPass = nullptr; + m_ldrGradingLookupTablePass = nullptr; + m_outputTransformPass = nullptr; } } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/OutputTransformPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/OutputTransformPass.cpp index a6b23171a5..5937f9ee49 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/OutputTransformPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/OutputTransformPass.cpp @@ -39,9 +39,9 @@ namespace AZ { } - void OutputTransformPass::Init() + void OutputTransformPass::InitializeInternal() { - DisplayMapperFullScreenPass::Init(); + DisplayMapperFullScreenPass::InitializeInternal(); AZ_Assert(m_shaderResourceGroup != nullptr, "OutputTransformPass %s has a null shader resource group when calling Init.", GetPathName().GetCStr()); diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp index a29354f723..750402ada8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp @@ -519,13 +519,13 @@ namespace AZ io.Fonts->TexID = reinterpret_cast(m_fontAtlas.get()); } - void ImGuiPass::OnBuildAttachmentsFinishedInternal() + void ImGuiPass::InitializeInternal() { // Set output format and finalize pipeline state m_pipelineState->SetOutputFromPass(this); m_pipelineState->Finalize(); - Base::OnBuildAttachmentsFinishedInternal(); + Base::InitializeInternal(); } void ImGuiPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.h b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.h index c8be0d7ee6..f63b515536 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.h @@ -94,7 +94,7 @@ namespace AZ explicit ImGuiPass(const RPI::PassDescriptor& descriptor); // Pass Behaviour Overrides... - void OnBuildAttachmentsFinishedInternal() override; + void InitializeInternal() override; void FrameBeginInternal(FramePrepareParams params) override; // Scope producer functions diff --git a/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexturePass.cpp b/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexturePass.cpp index 9a921205ab..724511b355 100644 --- a/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexturePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexturePass.cpp @@ -28,8 +28,6 @@ namespace AZ LuxCoreTexturePass::LuxCoreTexturePass(const RPI::PassDescriptor& descriptor) : ParentPass(descriptor) { - m_flags.m_alreadyCreated = false; - RPI::PassSystemInterface* passSystem = RPI::PassSystemInterface::Get(); // Create render target pass @@ -41,8 +39,6 @@ namespace AZ // Create readback m_readback = AZStd::make_shared(AZ::RHI::ScopeId{ Uuid::CreateRandom().ToString() }); - - CreateChildPasses(); } LuxCoreTexturePass::~LuxCoreTexturePass() @@ -61,9 +57,9 @@ namespace AZ AddChild(m_renderTargetPass); } - void LuxCoreTexturePass::BuildAttachmentsInternal() + void LuxCoreTexturePass::BuildInternal() { - ParentPass::BuildAttachmentsInternal(); + ParentPass::BuildInternal(); } void LuxCoreTexturePass::FrameBeginInternal(FramePrepareParams params) diff --git a/Gems/Atom/Feature/Common/Code/Source/LuxCore/RenderTexturePass.cpp b/Gems/Atom/Feature/Common/Code/Source/LuxCore/RenderTexturePass.cpp index 0782e12278..6e0444dca4 100644 --- a/Gems/Atom/Feature/Common/Code/Source/LuxCore/RenderTexturePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/LuxCore/RenderTexturePass.cpp @@ -38,13 +38,13 @@ namespace AZ m_attachmentSize = image->GetRHIImage()->GetDescriptor().m_size; m_attachmentFormat = format; m_shaderResourceGroup->SetImage(m_textureIndex, image); - QueueForBuildAttachments(); + QueueForBuildAndInitialization(); } - void RenderTexturePass::BuildAttachmentsInternal() + void RenderTexturePass::BuildInternal() { UpdataAttachment(); - FullscreenTrianglePass::BuildAttachmentsInternal(); + FullscreenTrianglePass::BuildInternal(); } void RenderTexturePass::FrameBeginInternal(FramePrepareParams params) diff --git a/Gems/Atom/Feature/Common/Code/Source/MorphTargets/MorphTargetComputePass.cpp b/Gems/Atom/Feature/Common/Code/Source/MorphTargets/MorphTargetComputePass.cpp index 1bb537ecef..42acd5c447 100644 --- a/Gems/Atom/Feature/Common/Code/Source/MorphTargets/MorphTargetComputePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/MorphTargets/MorphTargetComputePass.cpp @@ -44,7 +44,7 @@ namespace AZ m_skinnedMeshFeatureProcessor = skinnedMeshFeatureProcessor; } - void MorphTargetComputePass::BuildAttachmentsInternal() + void MorphTargetComputePass::BuildInternal() { // The same buffer that skinning writes to is used to manage the computed vertex deltas that are passed from the // morph target pass to the skinning pass. This simplifies things by only requiring one class to manage the memory diff --git a/Gems/Atom/Feature/Common/Code/Source/MorphTargets/MorphTargetComputePass.h b/Gems/Atom/Feature/Common/Code/Source/MorphTargets/MorphTargetComputePass.h index 61fa485fbc..d346edd908 100644 --- a/Gems/Atom/Feature/Common/Code/Source/MorphTargets/MorphTargetComputePass.h +++ b/Gems/Atom/Feature/Common/Code/Source/MorphTargets/MorphTargetComputePass.h @@ -37,7 +37,7 @@ namespace AZ void SetFeatureProcessor(SkinnedMeshFeatureProcessor* m_skinnedMeshFeatureProcessor); private: - void BuildAttachmentsInternal() override; + void BuildInternal() override; void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override; SkinnedMeshFeatureProcessor* m_skinnedMeshFeatureProcessor = nullptr; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp index e0ff253301..433f9c0276 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp @@ -43,14 +43,9 @@ namespace AZ ReleaseLutImage(); } - void BlendColorGradingLutsPass::FrameBeginInternal(FramePrepareParams params) + void BlendColorGradingLutsPass::InitializeInternal() { - if (!m_flags.m_initialized) - { - Init(); - } - - ComputePass::FrameBeginInternal(params); + InitializeShaderVariant(); } void BlendColorGradingLutsPass::InitializeShaderVariant() @@ -106,12 +101,6 @@ namespace AZ m_needToUpdateShaderVariant = false; } - void BlendColorGradingLutsPass::Init() - { - InitializeShaderVariant(); - m_flags.m_initialized = true; - } - void BlendColorGradingLutsPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) { ComputePass::SetupFrameGraphDependencies(frameGraph); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.h index c131a3cb38..048d0069f4 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.h @@ -56,7 +56,7 @@ namespace AZ explicit BlendColorGradingLutsPass(const RPI::PassDescriptor& descriptor); // Pass behavior overrides... - void FrameBeginInternal(FramePrepareParams params) override; + void InitializeInternal() override; // Scope producer functions... void SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) override; @@ -66,8 +66,6 @@ namespace AZ void InitializeShaderVariant(); void UpdateCurrentShaderVariant(); - void Init(); - void AcquireLutImage(); void ReleaseLutImage(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomBlurPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomBlurPass.cpp index 1427adeb08..af95a76718 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomBlurPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomBlurPass.cpp @@ -252,10 +252,10 @@ namespace AZ } } - void BloomBlurPass::BuildAttachmentsInternal() + void BloomBlurPass::BuildInternal() { BuildChildPasses(); - ParentPass::BuildAttachmentsInternal(); + ParentPass::BuildInternal(); } void BloomBlurPass::FrameBeginInternal(FramePrepareParams params) diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomBlurPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomBlurPass.h index e87d831382..4c65368506 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomBlurPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomBlurPass.h @@ -47,7 +47,7 @@ namespace AZ BloomBlurPass(const RPI::PassDescriptor& descriptor); // Pass behaviour overrides... - void BuildAttachmentsInternal() override; + void BuildInternal() override; void FrameBeginInternal(FramePrepareParams params) override; void GetInputInfo(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomCompositePass.cpp index fbb020cbf3..810353aab1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomCompositePass.cpp @@ -56,10 +56,10 @@ namespace AZ m_passData = *passData; } - void BloomCompositePass::BuildAttachmentsInternal() + void BloomCompositePass::BuildInternal() { BuildChildPasses(); - ParentPass::BuildAttachmentsInternal(); + ParentPass::BuildInternal(); } void BloomCompositePass::FrameBeginInternal(FramePrepareParams params) diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomCompositePass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomCompositePass.h index 86d897ae7d..a19947e1d0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomCompositePass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomCompositePass.h @@ -44,7 +44,7 @@ namespace AZ BloomCompositePass(const RPI::PassDescriptor& descriptor); // Pass behaviour overrides... - void BuildAttachmentsInternal() override; + void BuildInternal() override; void FrameBeginInternal(FramePrepareParams params) override; void GetAttachmentInfo(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomDownsamplePass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomDownsamplePass.cpp index 647177a1ce..63779beb16 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomDownsamplePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomDownsamplePass.cpp @@ -74,10 +74,10 @@ namespace AZ AddAttachmentBinding(outBinding); } - ComputePass::BuildAttachmentsInternal(); + ComputePass::BuildInternal(); } - void BloomDownsamplePass::BuildAttachmentsInternal() + void BloomDownsamplePass::BuildInternal() { BuildOutAttachmentBinding(); } diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomDownsamplePass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomDownsamplePass.h index 824f703417..3eb0ac8740 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomDownsamplePass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BloomDownsamplePass.h @@ -37,7 +37,7 @@ namespace AZ BloomDownsamplePass(const RPI::PassDescriptor& descriptor); // Pass Behaviour Overrides... - void BuildAttachmentsInternal() override; + void BuildInternal() override; void FrameBeginInternal(FramePrepareParams params) override; void BuildOutAttachmentBinding(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldBokehBlurPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldBokehBlurPass.cpp index 4a4a8a8b76..090d87e048 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldBokehBlurPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldBokehBlurPass.cpp @@ -43,9 +43,9 @@ namespace AZ { } - void DepthOfFieldBokehBlurPass::Init() + void DepthOfFieldBokehBlurPass::InitializeInternal() { - FullscreenTrianglePass::Init(); + FullscreenTrianglePass::InitializeInternal(); m_sampleNumberIndex.Reset(); m_radiusMinIndex.Reset(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldBokehBlurPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldBokehBlurPass.h index 688138fff0..c9b2dc6be2 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldBokehBlurPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldBokehBlurPass.h @@ -39,11 +39,11 @@ namespace AZ protected: // Behaviour functions override... + void InitializeInternal() override; void FrameBeginInternal(FramePrepareParams params) override; private: DepthOfFieldBokehBlurPass(const RPI::PassDescriptor& descriptor); - void Init() override; void InitializeShaderVariant(); void UpdateCurrentShaderVariant(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCompositePass.cpp index 30c92963d0..bf88f8f8fb 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCompositePass.cpp @@ -40,9 +40,9 @@ namespace AZ { } - void DepthOfFieldCompositePass::Init() + void DepthOfFieldCompositePass::InitializeInternal() { - FullscreenTrianglePass::Init(); + FullscreenTrianglePass::InitializeInternal(); m_backBlendFactorDivision2Index.Reset(); m_backBlendFactorDivision4Index.Reset(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCompositePass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCompositePass.h index fe4d60e679..00afdd0b0b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCompositePass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCompositePass.h @@ -37,11 +37,11 @@ namespace AZ protected: // Pass behavior overrides... + void InitializeInternal() override; void FrameBeginInternal(FramePrepareParams params) override; private: DepthOfFieldCompositePass(const RPI::PassDescriptor& descriptor); - void Init() override; void InitializeShaderVariant(); void UpdateCurrentShaderVariant(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCopyFocusDepthToCpuPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCopyFocusDepthToCpuPass.cpp index d64bba1957..00d5ad93c3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCopyFocusDepthToCpuPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCopyFocusDepthToCpuPass.cpp @@ -52,7 +52,7 @@ namespace AZ return depth; } - void DepthOfFieldCopyFocusDepthToCpuPass::BuildAttachmentsInternal() + void DepthOfFieldCopyFocusDepthToCpuPass::BuildInternal() { SetScopeId(RHI::ScopeId(GetPathName())); } diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCopyFocusDepthToCpuPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCopyFocusDepthToCpuPass.h index 64301b6e0c..7e8d041e96 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCopyFocusDepthToCpuPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldCopyFocusDepthToCpuPass.h @@ -48,7 +48,7 @@ namespace AZ void BuildCommandList(const RHI::FrameGraphExecuteContext& context) override; // Pass overrides - void BuildAttachmentsInternal() override; + void BuildInternal() override; void FrameBeginInternal(FramePrepareParams params) override; RPI::Ptr m_bufferRef; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldMaskPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldMaskPass.cpp index 3f9c09c88c..93c19e5484 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldMaskPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldMaskPass.cpp @@ -33,9 +33,9 @@ namespace AZ { } - void DepthOfFieldMaskPass::Init() + void DepthOfFieldMaskPass::InitializeInternal() { - FullscreenTrianglePass::Init(); + FullscreenTrianglePass::InitializeInternal(); m_blendFactorIndex.Reset(); m_inputResolutionInverseIndex.Reset(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldMaskPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldMaskPass.h index 09bb08a22c..7e21f87af7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldMaskPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldMaskPass.h @@ -37,11 +37,11 @@ namespace AZ protected: // Pass behavior overrides... + void InitializeInternal() override; void FrameBeginInternal(FramePrepareParams params) override; private: DepthOfFieldMaskPass(const RPI::PassDescriptor& descriptor); - virtual void Init() override; // SRG binding indices... RHI::ShaderInputNameIndex m_blendFactorIndex = "m_blendFactor"; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.cpp index c355b7c8a7..6050ac2b2e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.cpp @@ -34,18 +34,6 @@ namespace AZ DepthOfFieldReadBackFocusDepthPass::DepthOfFieldReadBackFocusDepthPass(const RPI::PassDescriptor& descriptor) : ParentPass(descriptor) { - RPI::PassSystemInterface* passSystem = RPI::PassSystemInterface::Get(); - - // Create read back pass - m_readbackPass = passSystem->CreatePass(AZ::Name("DepthOfFieldReadBackPass")); - AZ_Assert(m_readbackPass, "DepthOfFieldReadBackFocusDepthPass : read back pass is invalid"); - - AddChild(m_readbackPass); - - // Find GetDepth pass on template - auto pass = FindChildPass(Name("DepthOfFieldWriteFocusDepthFromGpu")); - m_getDepthPass = static_cast(pass.get()); - // Create buffer for read back focus depth. We append static counter to avoid name conflicts. RPI::CommonBufferDescriptor desc; desc.m_bufferName = "DepthOfFieldReadBackAutoFocusDepthBuffer"; @@ -55,9 +43,6 @@ namespace AZ desc.m_bufferData = nullptr; desc.m_elementFormat = RHI::Format::R32_FLOAT; m_buffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); - - m_getDepthPass->SetBufferRef(m_buffer); - m_readbackPass->SetBufferRef(m_buffer); } DepthOfFieldReadBackFocusDepthPass::~DepthOfFieldReadBackFocusDepthPass() @@ -85,6 +70,24 @@ namespace AZ } } + void DepthOfFieldReadBackFocusDepthPass::CreateChildPassesInternal() + { + RPI::PassSystemInterface* passSystem = RPI::PassSystemInterface::Get(); + + // Create read back pass + m_readbackPass = passSystem->CreatePass(AZ::Name("DepthOfFieldReadBackPass")); + AZ_Assert(m_readbackPass, "DepthOfFieldReadBackFocusDepthPass : read back pass is invalid"); + + AddChild(m_readbackPass); + + // Find GetDepth pass on template + auto pass = FindChildPass(Name("DepthOfFieldWriteFocusDepthFromGpu")); + m_getDepthPass = static_cast(pass.get()); + + m_getDepthPass->SetBufferRef(m_buffer); + m_readbackPass->SetBufferRef(m_buffer); + } + void DepthOfFieldReadBackFocusDepthPass::FrameBeginInternal(FramePrepareParams params) { RPI::Scene* scene = GetScene(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.h index 75842fb67a..db29576d22 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.h @@ -43,6 +43,7 @@ namespace AZ protected: // Pass behavior overrides... + void CreateChildPassesInternal() override; void FrameBeginInternal(FramePrepareParams params) override; private: diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldWriteFocusDepthFromGpuPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldWriteFocusDepthFromGpuPass.cpp index e7ac891198..4aaf936426 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldWriteFocusDepthFromGpuPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldWriteFocusDepthFromGpuPass.cpp @@ -62,9 +62,9 @@ namespace AZ m_bufferRef = bufferRef; } - void DepthOfFieldWriteFocusDepthFromGpuPass::BuildAttachmentsInternal() + void DepthOfFieldWriteFocusDepthFromGpuPass::BuildInternal() { - AZ_Assert(m_bufferRef != nullptr, "%s has a null buffer when calling BuildAttachmentsInternal.", GetPathName().GetCStr()); + AZ_Assert(m_bufferRef != nullptr, "%s has a null buffer when calling BuildInternal.", GetPathName().GetCStr()); AttachBufferToSlot(Name("DofDepthInputOutput"), m_bufferRef); } diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldWriteFocusDepthFromGpuPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldWriteFocusDepthFromGpuPass.h index 592ab64c01..881ef9df94 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldWriteFocusDepthFromGpuPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldWriteFocusDepthFromGpuPass.h @@ -51,7 +51,7 @@ namespace AZ void CompileResources(const RHI::FrameGraphCompileContext& context) override; // Pass overrides - void BuildAttachmentsInternal() override; + void BuildInternal() override; }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.cpp index bf293fd3d2..e131f7ad22 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.cpp @@ -60,7 +60,7 @@ namespace AZ m_buffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); } - void EyeAdaptationPass::BuildAttachmentsInternal() + void EyeAdaptationPass::BuildInternal() { if (!m_buffer) { diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.h index cef168a122..d53569a9f9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.h @@ -58,7 +58,7 @@ namespace AZ float m_exposureValue = 1.0f; }; - void BuildAttachmentsInternal() override; + void BuildInternal() override; void FrameBeginInternal(FramePrepareParams params) override; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp index aff01d5bf1..45d3e0c39a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp @@ -48,9 +48,9 @@ namespace AZ } } - void LookModificationCompositePass::Init() + void LookModificationCompositePass::InitializeInternal() { - FullscreenTrianglePass::Init(); + FullscreenTrianglePass::InitializeInternal(); m_shaderColorGradingLutImageIndex.Reset(); m_shaderColorGradingShaperTypeIndex.Reset(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.h index fb6c968436..8292330a2d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.h @@ -60,9 +60,9 @@ namespace AZ protected: LookModificationCompositePass(const RPI::PassDescriptor& descriptor); - void Init() override; //! Pass behavior overrides + void InitializeInternal() override; void FrameBeginInternal(FramePrepareParams params) final; private: diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationTransformPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationTransformPass.cpp index 98c842042b..9f792370fc 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationTransformPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationTransformPass.cpp @@ -36,10 +36,10 @@ namespace AZ &AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle); } - void LookModificationPass::BuildAttachmentsInternal() + void LookModificationPass::BuildInternal() { m_swapChainAttachmentBinding = FindAttachmentBinding(Name("SwapChainOutput")); - ParentPass::BuildAttachmentsInternal(); + ParentPass::BuildInternal(); } void LookModificationPass::FrameBeginInternal([[maybe_unused]] FramePrepareParams params) diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationTransformPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationTransformPass.h index 5102015e82..8203530918 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationTransformPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationTransformPass.h @@ -52,7 +52,7 @@ namespace AZ //! Pass overrides ... void FrameBeginInternal(FramePrepareParams params) override; - void BuildAttachmentsInternal() override; + void BuildInternal() override; private: const RPI::PassAttachmentBinding* m_swapChainAttachmentBinding = nullptr; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp index 715ebf2945..fb430ac3d3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp @@ -67,7 +67,7 @@ namespace AZ desc.m_bufferName = "LuminanceHistogramBuffer"; desc.m_elementSize = sizeof(uint32_t); desc.m_byteCount = NumHistogramBins * sizeof(uint32_t); - desc.m_elementFormat = RHI::Format::R32_UINT; + desc.m_elementFormat = RHI::Format::Unknown; m_histogram = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); AZ_Assert(m_histogram != nullptr, "Unable to allocate buffer"); } @@ -81,7 +81,7 @@ namespace AZ return colorBuffer->m_descriptor.m_image.m_size; } - void LuminanceHistogramGeneratorPass::BuildAttachmentsInternal() + void LuminanceHistogramGeneratorPass::BuildInternal() { CreateHistogramBuffer(); AttachHistogramBuffer(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.h index ee8a10fe16..9691daab6f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.h @@ -42,7 +42,7 @@ namespace AZ protected: LuminanceHistogramGeneratorPass(const RPI::PassDescriptor& descriptor); - virtual void BuildAttachmentsInternal() override; + virtual void BuildInternal() override; void CreateHistogramBuffer(); void AttachHistogramBuffer(); AZ::RHI::Size GetColorBufferResolution(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABasePass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABasePass.cpp index d2424e24fd..f95637e824 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABasePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABasePass.cpp @@ -43,9 +43,9 @@ namespace AZ { } - void SMAABasePass::Init() + void SMAABasePass::InitializeInternal() { - FullscreenTrianglePass::Init(); + FullscreenTrianglePass::InitializeInternal(); AZ_Assert(m_shaderResourceGroup != nullptr, "SMAABasePass %s has a null shader resource group when calling Init.", GetPathName().GetCStr()); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABasePass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABasePass.h index 0484b7d430..3a18d587b5 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABasePass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABasePass.h @@ -41,9 +41,12 @@ namespace AZ protected: SMAABasePass(const RPI::PassDescriptor& descriptor); - void Init() override; + // Pass behavior overrides... + void InitializeInternal() override; + // An interface to update pass srg. virtual void UpdateSRG() = 0; + // An interface to get current shader variation option. virtual void GetCurrentShaderOption(AZ::RPI::ShaderOptionGroup& shaderOption) const = 0; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABlendingWeightCalculationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABlendingWeightCalculationPass.cpp index 39d9e51c86..7776bc3904 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABlendingWeightCalculationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABlendingWeightCalculationPass.cpp @@ -94,9 +94,9 @@ namespace AZ } } - void SMAABlendingWeightCalculationPass::Init() + void SMAABlendingWeightCalculationPass::InitializeInternal() { - SMAABasePass::Init(); + SMAABasePass::InitializeInternal(); AZ_Assert(m_shaderResourceGroup != nullptr, "SMAABlendingWeightCalculationPass %s has a null shader resource group when calling Init.", GetPathName().GetCStr()); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABlendingWeightCalculationPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABlendingWeightCalculationPass.h index b968a9c2d9..2f589a7ae6 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABlendingWeightCalculationPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAABlendingWeightCalculationPass.h @@ -43,7 +43,9 @@ namespace AZ private: SMAABlendingWeightCalculationPass(const RPI::PassDescriptor& descriptor); - void Init() override; + + // Pass behavior overrides... + void InitializeInternal() override; // SMAABasePass functions... void UpdateSRG() override; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAEdgeDetectionPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAEdgeDetectionPass.cpp index 68485f3280..bb8069d0de 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAEdgeDetectionPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAEdgeDetectionPass.cpp @@ -48,9 +48,9 @@ namespace AZ { } - void SMAAEdgeDetectionPass::Init() + void SMAAEdgeDetectionPass::InitializeInternal() { - SMAABasePass::Init(); + SMAABasePass::InitializeInternal(); m_renderTargetMetricsShaderInputIndex.Reset(); m_chromaThresholdShaderInputIndex.Reset(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAEdgeDetectionPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAEdgeDetectionPass.h index f8302c55ca..f2c85297b6 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAEdgeDetectionPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAEdgeDetectionPass.h @@ -45,7 +45,9 @@ namespace AZ private: SMAAEdgeDetectionPass(const RPI::PassDescriptor& descriptor); - void Init() override; + + // Pass behavior overrides + void InitializeInternal() override; // SMAABasePass functions... void UpdateSRG() override; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAANeighborhoodBlendingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAANeighborhoodBlendingPass.cpp index 777e4d7d95..b2307de714 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAANeighborhoodBlendingPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAANeighborhoodBlendingPass.cpp @@ -47,9 +47,9 @@ namespace AZ { } - void SMAANeighborhoodBlendingPass::Init() + void SMAANeighborhoodBlendingPass::InitializeInternal() { - SMAABasePass::Init(); + SMAABasePass::InitializeInternal(); m_renderTargetMetricsShaderInputIndex.Reset(); } diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAANeighborhoodBlendingPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAANeighborhoodBlendingPass.h index 86386a0545..f54ff30575 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAANeighborhoodBlendingPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAANeighborhoodBlendingPass.h @@ -37,7 +37,9 @@ namespace AZ private: SMAANeighborhoodBlendingPass(const RPI::PassDescriptor& descriptor); - void Init() override; + + // Pass behavior overrides + void InitializeInternal() override; // SMAABasePass functions... void UpdateSRG() override; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SsaoPasses.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SsaoPasses.cpp index 68472115ce..8954584886 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SsaoPasses.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SsaoPasses.cpp @@ -41,9 +41,9 @@ namespace AZ return ParentPass::IsEnabled(); } - void SsaoParentPass::OnBuildAttachmentsFinishedInternal() + void SsaoParentPass::InitializeInternal() { - ParentPass::OnBuildAttachmentsFinishedInternal(); + ParentPass::InitializeInternal(); m_blurParentPass = FindChildPass(Name("SsaoBlur"))->AsParent(); AZ_Assert(m_blurParentPass, "[SsaoParentPass] Could not retrieve parent blur pass."); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SsaoPasses.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SsaoPasses.h index 9abd04e306..9fc5448f65 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SsaoPasses.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SsaoPasses.h @@ -36,7 +36,7 @@ namespace AZ protected: // Behavior functions override... - void OnBuildAttachmentsFinishedInternal() override; + void InitializeInternal() override; void FrameBeginInternal(FramePrepareParams params) override; private: diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp index 9f885ede70..38e03ab156 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.cpp @@ -108,7 +108,7 @@ namespace AZ::Render Base::ResetInternal(); } - void TaaPass::BuildAttachmentsInternal() + void TaaPass::BuildInternal() { m_accumulationAttachments[0] = FindAttachment(Name("Accumulation1")); m_accumulationAttachments[1] = FindAttachment(Name("Accumulation2")); @@ -143,7 +143,7 @@ namespace AZ::Render m_outputColorBinding->SetAttachment(m_accumulationAttachments[1]); } - Base::BuildAttachmentsInternal(); + Base::BuildInternal(); } void TaaPass::UpdateAttachmentImage(RPI::Ptr& attachment) diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.h index 6133720691..e8f4796e7b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/TaaPass.h @@ -63,7 +63,7 @@ namespace AZ::Render // Pass behavior overrides... void FrameBeginInternal(FramePrepareParams params) override; void ResetInternal() override; - void BuildAttachmentsInternal() override; + void BuildInternal() override; void UpdateAttachmentImage(RPI::Ptr& attachment); diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp index 92cd41b4e8..f1fcd2de35 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp @@ -42,7 +42,7 @@ namespace AZ } } - void RayTracingAccelerationStructurePass::BuildAttachmentsInternal() + void RayTracingAccelerationStructurePass::BuildInternal() { SetScopeId(RHI::ScopeId(GetPathName())); } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.h index 6d90d752e3..127669301e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.h +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.h @@ -44,7 +44,7 @@ namespace AZ void BuildCommandList(const RHI::FrameGraphExecuteContext& context) override; // Pass overrides - void BuildAttachmentsInternal() override; + void BuildInternal() override; void FrameBeginInternal(FramePrepareParams params) override; // buffer view descriptor for the TLAS diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp index 5137b66bf9..9a9064bdf7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp @@ -30,7 +30,7 @@ namespace AZ { } - void ReflectionCopyFrameBufferPass::BuildAttachmentsInternal() + void ReflectionCopyFrameBufferPass::BuildInternal() { RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass")); const AZStd::vector& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter); @@ -43,7 +43,7 @@ namespace AZ AttachImageToSlot(outputBinding.m_name, frameBufferAttachment); } - FullscreenTrianglePass::BuildAttachmentsInternal(); + FullscreenTrianglePass::BuildInternal(); } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.h index 2a2fecea40..f0b8c80d72 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.h @@ -37,7 +37,7 @@ namespace AZ explicit ReflectionCopyFrameBufferPass(const RPI::PassDescriptor& descriptor); // Pass Overrides... - void BuildAttachmentsInternal() override; + void BuildInternal() override; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp index 883686ac5b..631ff803dd 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp @@ -47,7 +47,7 @@ namespace AZ RemoveChildren(); } - void ReflectionScreenSpaceBlurPass::CreateChildPasses(uint32_t numBlurMips) + void ReflectionScreenSpaceBlurPass::CreateChildPassesInternal() { RPI::PassSystemInterface* passSystem = RPI::PassSystemInterface::Get(); @@ -83,7 +83,7 @@ namespace AZ horizontalBlurChildDesc.m_passTemplate = blurHorizontalPassTemplate; // add child passes to perform the vertical and horizontal Gaussian blur for each roughness mip level - for (uint32_t mip = 0; mip < numBlurMips; ++mip) + for (uint32_t mip = 0; mip < m_numBlurMips; ++mip) { // create Vertical blur child passes { @@ -113,9 +113,10 @@ namespace AZ } } - void ReflectionScreenSpaceBlurPass::BuildAttachmentsInternal() + void ReflectionScreenSpaceBlurPass::BuildInternal() { RemoveChildren(); + m_flags.m_createChildren = true; Data::Instance pool = RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool(); @@ -163,12 +164,11 @@ namespace AZ m_ownedAttachments.push_back(transientPassAttachment); } - // create child passes, one vertical and one horizontal blur per mip level - CreateChildPasses(mipLevels - 1); + m_numBlurMips = mipLevels - 1; - // call ParentPass::BuildAttachmentsInternal() first to configure the slots and auto-add the empty bindings, + // call ParentPass::BuildInternal() first to configure the slots and auto-add the empty bindings, // then we will assign attachments to the bindings - ParentPass::BuildAttachmentsInternal(); + ParentPass::BuildInternal(); // setup attachment bindings on vertical blur child passes uint32_t attachmentIndex = 0; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h index 53f4026aef..4a2ccce1d4 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h @@ -40,16 +40,17 @@ namespace AZ private: explicit ReflectionScreenSpaceBlurPass(const RPI::PassDescriptor& descriptor); - void CreateChildPasses(uint32_t numBlurMips); + void CreateChildPassesInternal() override; // Pass Overrides... void ResetInternal() override; - void BuildAttachmentsInternal() override; + void BuildInternal() override; AZStd::vector> m_verticalBlurChildPasses; AZStd::vector> m_horizontalBlurChildPasses; Data::Instance m_frameBufferImageAttachment; + uint32_t m_numBlurMips = 0; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/ScreenSpace/DeferredFogPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ScreenSpace/DeferredFogPass.cpp index 477040b74a..70e17a28d3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ScreenSpace/DeferredFogPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ScreenSpace/DeferredFogPass.cpp @@ -46,9 +46,9 @@ namespace AZ } - void DeferredFogPass::Init() + void DeferredFogPass::InitializeInternal() { - FullscreenTrianglePass::Init(); + FullscreenTrianglePass::InitializeInternal(); // The following will ensure that in the case of data driven pass, the settings will get // updated by the pass enable state. diff --git a/Gems/Atom/Feature/Common/Code/Source/ScreenSpace/DeferredFogPass.h b/Gems/Atom/Feature/Common/Code/Source/ScreenSpace/DeferredFogPass.h index e2740850de..78b7d25bff 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ScreenSpace/DeferredFogPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/ScreenSpace/DeferredFogPass.h @@ -43,13 +43,11 @@ namespace AZ { AZ_RPI_PASS(DeferredFogPass); - public: AZ_RTTI(DeferredFogPass, "{0406C8AB-E95D-43A7-AF53-BDEE22D36746}", RPI::FullscreenTrianglePass); AZ_CLASS_ALLOCATOR(DeferredFogPass, SystemAllocator, 0); ~DeferredFogPass() = default; - void Init() override; static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); @@ -57,12 +55,16 @@ namespace AZ virtual bool IsEnabled() const override; - void SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) override; - void CompileResources(const RHI::FrameGraphCompileContext& context) override; - protected: DeferredFogPass(const RPI::PassDescriptor& descriptor); + // Pass behavior overrides... + void InitializeInternal() override; + + // Scope producer functions... + void SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) override; + void CompileResources(const RHI::FrameGraphCompileContext& context) override; + //! Set the binding indices of all members of the SRG void SetSrgBindIndices(); diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp index 216a126953..da174ab0d5 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -526,7 +526,7 @@ namespace AZ::Render for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses) { - esmPass->QueueForBuildAttachments(); + esmPass->QueueForBuildAndInitialization(); } for (ProjectedShadowmapsPass* shadowPass : m_projectedShadowmapsPasses) diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp index 1cb782d8b1..1b14611e84 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshOutputStreamManager.cpp @@ -67,8 +67,8 @@ namespace AZ creator.SetBuffer(nullptr, 0, bufferDescriptor); RHI::BufferViewDescriptor viewDescriptor; - viewDescriptor.m_elementFormat = RHI::Format::R32_FLOAT; - viewDescriptor.m_elementSize = RHI::GetFormatSize(viewDescriptor.m_elementFormat); + viewDescriptor.m_elementFormat = RHI::Format::Unknown; + viewDescriptor.m_elementSize = sizeof(float); viewDescriptor.m_elementCount = aznumeric_cast(m_sizeInBytes) / viewDescriptor.m_elementSize; viewDescriptor.m_elementOffset = 0; creator.SetBufferViewDescriptor(viewDescriptor); diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h index 51b15c4bcf..651be829b5 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Reflect/RenderStates.h @@ -160,6 +160,16 @@ namespace AZ StencilState m_stencil; }; + enum class WriteChannelMask : uint8_t + { + ColorWriteMaskNone = 0, + ColorWriteMaskRed = AZ_BIT(0), + ColorWriteMaskGreen = AZ_BIT(1), + ColorWriteMaskBlue = AZ_BIT(2), + ColorWriteMaskAlpha = AZ_BIT(3), + ColorWriteMaskAll = ColorWriteMaskRed | ColorWriteMaskGreen | ColorWriteMaskBlue | ColorWriteMaskAlpha + }; + struct TargetBlendState { AZ_TYPE_INFO(TargetBlendState, "{2CDF00FE-614D-44FC-929F-E6B50C348578}"); diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h index ce2c6c77ec..afcab28a1d 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/ShaderResourceGroupData.h @@ -410,7 +410,6 @@ namespace AZ // For any other type the buffer view's element size should match the stride. if (shaderInputBuffer.m_strideSize != bufferViewDescriptor.m_elementSize) { - // [GFX TODO][ATOM-5735][AZSL] ByteAddressBuffer shader input is setting a stride of 16 instead of 4 AZ_Error("ShaderResourceGroupData", false, "Buffer Input '%s[%d]': Does not match expected stride size %d", shaderInputBuffer.m_name.GetCStr(), arrayIndex, bufferViewDescriptor.m_elementSize); return false; diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h index a6fe57f6d8..6f09ea1d5f 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandList.h @@ -271,6 +271,12 @@ namespace AZ ShaderResourceBindings& bindings = GetShaderResourceBindingsByPipelineType(pipelineType); const PipelineState* pipelineState = static_cast(item.m_pipelineState); + if(!pipelineState) + { + AZ_Assert(false, "Pipeline state not provided"); + return false; + } + bool updatePipelineState = m_state.m_pipelineState != pipelineState; // The pipeline state gets set first. if (updatePipelineState) diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp index a3d14e3803..a29c7ae402 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.cpp @@ -10,6 +10,7 @@ * */ #include "RHI/Atom_RHI_DX12_precompiled.h" +#include #include #include #include @@ -1268,7 +1269,7 @@ namespace AZ dst.BlendOpAlpha = ConvertBlendOp(src.m_blendAlphaOp); dst.DestBlend = ConvertBlendFactor(src.m_blendDest); dst.DestBlendAlpha = ConvertBlendFactor(src.m_blendAlphaDest); - dst.RenderTargetWriteMask = src.m_writeMask; + dst.RenderTargetWriteMask = ConvertColorWriteMask(src.m_writeMask); dst.SrcBlend = ConvertBlendFactor(src.m_blendSource); dst.SrcBlendAlpha = ConvertBlendFactor(src.m_blendAlphaSource); dst.LogicOp = D3D12_LOGIC_OP_CLEAR; @@ -1355,6 +1356,38 @@ namespace AZ }; return table[(uint32_t)mask]; } + + uint8_t ConvertColorWriteMask(uint8_t writeMask) + { + uint8_t dflags = 0; + if(writeMask == 0) + { + return dflags; + } + + if(RHI::CheckBitsAll(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) + { + return D3D12_COLOR_WRITE_ENABLE_ALL; + } + + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) + { + dflags |= D3D12_COLOR_WRITE_ENABLE_RED; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) + { + dflags |= D3D12_COLOR_WRITE_ENABLE_GREEN; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) + { + dflags |= D3D12_COLOR_WRITE_ENABLE_BLUE; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) + { + dflags |= D3D12_COLOR_WRITE_ENABLE_ALPHA; + } + return dflags; + } D3D12_DEPTH_STENCIL_DESC ConvertDepthStencilState(const RHI::DepthStencilState& depthStencil) { diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h index d8385203f8..640a5fef31 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/Conversions.h @@ -164,5 +164,7 @@ namespace AZ uint32_t shaderRegisterSpace, D3D12_SHADER_VISIBILITY shaderVisibility, D3D12_STATIC_SAMPLER_DESC& staticSamplerDesc); + + uint8_t ConvertColorWriteMask(uint8_t writeMask); } } diff --git a/Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp b/Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp index cc8c4c80c0..b81c51a57b 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp @@ -94,7 +94,7 @@ namespace Platform void ResizeInternal(RHIMetalView* metalView, CGSize viewSize) { - [metalView resizeSubviewsWithOldSize:viewSize]; + [metalView.metalLayer setDrawableSize: viewSize]; } RHIMetalView* GetMetalView(NativeWindowType* nativeWindow) diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp index 6ffd15e0bc..49ff4146fc 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.cpp @@ -386,35 +386,35 @@ namespace AZ void ArgumentBuffer::AddUntrackedResourcesToEncoder(id commandEncoder, const ShaderResourceGroupVisibility& srgResourcesVisInfo) const { + //Map to cache all the resources based on the usage as we can batch all the resources for a given usage + ComputeResourcesToMakeResidentMap resourcesToMakeResidentCompute; + //Map to cache all the resources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage + GraphicsResourcesToMakeResidentMap resourcesToMakeResidentGraphics; + + //Cache the constant buffer associated with a srg if (m_constantBufferSize) { uint8_t numBitsSet = RHI::CountBitsSet(static_cast(srgResourcesVisInfo.m_constantDataStageMask)); if( numBitsSet > 0) { + id mtlconstantBufferResource = m_constantBuffer.GetGpuAddress>(); if(RHI::CheckBitsAny(srgResourcesVisInfo.m_constantDataStageMask, RHI::ShaderStageMask::Compute)) { - [static_cast>(commandEncoder) useResource:m_constantBuffer.GetGpuAddress>() usage:MTLResourceUsageRead]; + uint16_t arrayIndex = resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArrayLen++; + resourcesToMakeResidentCompute[MTLResourceUsageRead].m_resourceArray[arrayIndex] = mtlconstantBufferResource; } else { MTLRenderStages mtlRenderStages = GetRenderStages(srgResourcesVisInfo.m_constantDataStageMask); - [static_cast>(commandEncoder) useResource:m_constantBuffer.GetGpuAddress>() - usage:MTLResourceUsageRead - stages:mtlRenderStages]; + AZStd::pair key = AZStd::make_pair(MTLResourceUsageRead, mtlRenderStages); + uint16_t arrayIndex = resourcesToMakeResidentGraphics[key].m_resourceArrayLen++; + resourcesToMakeResidentGraphics[key].m_resourceArray[arrayIndex] = mtlconstantBufferResource; } - } } - ApplyUseResource(commandEncoder, m_resourceBindings, srgResourcesVisInfo); - } - - void ArgumentBuffer::ApplyUseResource(id encoder, - const ResourceBindingsMap& resourceMap, - const ShaderResourceGroupVisibility& srgResourcesVisInfo) const - { - - CommandEncoderType encodeType = CommandEncoderType::Invalid; - for (const auto& it : resourceMap) + + //Cach all the resources within a srg that are used by the shader based on the visibility information + for (const auto& it : m_resourceBindings) { //Extract the visibility mask for the give resource auto visMaskIt = srgResourcesVisInfo.m_resourcesStageMask.find(it.first); @@ -426,40 +426,53 @@ namespace AZ { if(RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Compute)) { - //Call UseResource on all resources for Compute stage - ApplyUseResourceToCompute(encoder, it.second); - encodeType = CommandEncoderType::Compute; + CollectResourcesForCompute(commandEncoder, it.second, resourcesToMakeResidentCompute); } else { - //Call UseResource on all resources for Vertex and Fragment stages - AZ_Assert(RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Vertex) || RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Fragment), "The visibility mask %i is not set for Vertex or fragment stage", visMaskIt->second); - ApplyUseResourceToGraphic(encoder, visMaskIt->second, it.second); - encodeType = CommandEncoderType::Render; + bool isBoundToGraphics = RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Vertex) || RHI::CheckBitsAny(visMaskIt->second, RHI::ShaderStageMask::Fragment); + AZ_Assert(isBoundToGraphics, "The visibility mask %i is not set for Vertex or fragment stage", visMaskIt->second); + CollectResourcesForGraphics(commandEncoder, visMaskIt->second, it.second, resourcesToMakeResidentGraphics); } } } + + //Call UseResource on all resources for Compute stage + for (const auto& key : resourcesToMakeResidentCompute) + { + [static_cast>(commandEncoder) useResources: key.second.m_resourceArray.data() + count: key.second.m_resourceArrayLen + usage: key.first]; + } + + //Call UseResource on all resources for Vertex and Fragment stages + for (const auto& key : resourcesToMakeResidentGraphics) + { + [static_cast>(commandEncoder) useResources: key.second.m_resourceArray.data() + count: key.second.m_resourceArrayLen + usage: key.first.first + stages: key.first.second]; + } } - - void ArgumentBuffer::ApplyUseResourceToCompute(id encoder, const ResourceBindingsSet& resourceBindingDataSet) const + + void ArgumentBuffer::CollectResourcesForCompute(id encoder, + const ResourceBindingsSet& resourceBindingDataSet, + ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const { for (const auto& resourceBindingData : resourceBindingDataSet) { ResourceType rescType = resourceBindingData.m_resourcPtr->GetResourceType(); + MTLResourceUsage resourceUsage = MTLResourceUsageRead; switch(rescType) { case ResourceType::MtlTextureType: { - MTLResourceUsage resourceUsage = GetImageResourceUsage(resourceBindingData.m_imageAccess); - [static_cast>(encoder) useResource:resourceBindingData.m_resourcPtr->GetGpuAddress>() usage:resourceUsage]; - + resourceUsage |= GetImageResourceUsage(resourceBindingData.m_imageAccess); break; } case ResourceType::MtlBufferType: { - MTLResourceUsage resourceUsage = GetBufferResourceUsage(resourceBindingData.m_bufferAccess); - [static_cast>(encoder) useResource:resourceBindingData.m_resourcPtr->GetGpuAddress>() usage:resourceUsage]; - + resourceUsage |= GetBufferResourceUsage(resourceBindingData.m_bufferAccess); break; } default: @@ -467,13 +480,20 @@ namespace AZ AZ_Assert(false, "Undefined Resource type"); } } + uint16_t arrayIndex = resourcesToMakeResidentMap[resourceUsage].m_resourceArrayLen++; + id mtlResourceToBind = resourceBindingData.m_resourcPtr->GetGpuAddress>(); + resourcesToMakeResidentMap[resourceUsage].m_resourceArray[arrayIndex] = mtlResourceToBind; } } - void ArgumentBuffer::ApplyUseResourceToGraphic(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet) const + void ArgumentBuffer::CollectResourcesForGraphics(id encoder, + RHI::ShaderStageMask visShaderMask, + const ResourceBindingsSet& resourceBindingDataSet, + GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const { - + MTLRenderStages mtlRenderStages = GetRenderStages(visShaderMask); + MTLResourceUsage resourceUsage = MTLResourceUsageRead; for (const auto& resourceBindingData : resourceBindingDataSet) { ResourceType rescType = resourceBindingData.m_resourcPtr->GetResourceType(); @@ -481,20 +501,12 @@ namespace AZ { case ResourceType::MtlTextureType: { - MTLResourceUsage resourceUsage = GetImageResourceUsage(resourceBindingData.m_imageAccess); - [static_cast>(encoder) useResource:resourceBindingData.m_resourcPtr->GetGpuAddress>() - usage:resourceUsage - stages:mtlRenderStages]; - + resourceUsage |= GetImageResourceUsage(resourceBindingData.m_imageAccess); break; } case ResourceType::MtlBufferType: { - MTLResourceUsage resourceUsage = GetBufferResourceUsage(resourceBindingData.m_bufferAccess); - [static_cast>(encoder) useResource:resourceBindingData.m_resourcPtr->GetGpuAddress>() - usage:resourceUsage - stages:mtlRenderStages]; - + resourceUsage |= GetBufferResourceUsage(resourceBindingData.m_bufferAccess); break; } default: @@ -502,8 +514,12 @@ namespace AZ AZ_Assert(false, "Undefined Resource type"); } } + + AZStd::pair key = AZStd::make_pair(resourceUsage, mtlRenderStages); + uint16_t arrayIndex = resourcesToMakeResidentMap[key].m_resourceArrayLen++; + id mtlResourceToBind = resourceBindingData.m_resourcPtr->GetGpuAddress>(); + resourcesToMakeResidentMap[key].m_resourceArray[arrayIndex] = mtlResourceToBind; } } - } } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h index a5a8e00e69..c4cfd17390 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/ArgumentBuffer.h @@ -119,8 +119,24 @@ namespace AZ using ResourceBindingsMap = AZStd::unordered_map; ResourceBindingsMap m_resourceBindings; - void ApplyUseResourceToCompute(id encoder, const ResourceBindingsSet& resourceBindingData) const; - void ApplyUseResourceToGraphic(id encoder, RHI::ShaderStageMask visShaderMask, const ResourceBindingsSet& resourceBindingDataSet) const; + static const int MaxEntriesInArgTable = 31; + struct MetalResourceArray + { + AZStd::array, MaxEntriesInArgTable> m_resourceArray; + uint16_t m_resourceArrayLen = 0; + }; + //Map to cache all the resources based on the usage as we can batch all the resources for a given usage + using ComputeResourcesToMakeResidentMap = AZStd::unordered_map; + //Map to cache all the resources based on the usage and shader stage as we can batch all the resources for a given usage/shader usage + using GraphicsResourcesToMakeResidentMap = AZStd::unordered_map, MetalResourceArray>; + + void CollectResourcesForCompute(id encoder, + const ResourceBindingsSet& resourceBindingData, + ComputeResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; + void CollectResourcesForGraphics(id encoder, + RHI::ShaderStageMask visShaderMask, + const ResourceBindingsSet& resourceBindingDataSet, + GraphicsResourcesToMakeResidentMap& resourcesToMakeResidentMap) const; //! Use visibility information to call UseResource on all resources for this Argument Buffer void ApplyUseResource(id encoder, const ResourceBindingsMap& resourceMap, @@ -144,8 +160,6 @@ namespace AZ #endif ShaderResourceGroupPool* m_srgPool = nullptr; - - static const int MaxEntriesInArgTable = 31; NSCache* m_samplerCache; }; } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp index 3eca8dabd8..7f65c9ea47 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -249,66 +250,191 @@ namespace AZ ShaderResourceBindings& bindings = GetShaderResourceBindingsByPipelineType(stateType); const PipelineLayout& pipelineLayout = pipelineState->GetPipelineLayout(); - for (uint32_t srgIndex = 0; srgIndex < RHI::Limits::Pipeline::ShaderResourceGroupCountMax; ++srgIndex) + uint32_t bufferVertexRegisterIdMin = RHI::Limits::Pipeline::ShaderResourceGroupCountMax; + uint32_t bufferFragmentOrComputeRegisterIdMin = RHI::Limits::Pipeline::ShaderResourceGroupCountMax; + uint32_t bufferVertexRegisterIdMax = 0; + uint32_t bufferFragmentOrComputeRegisterIdMax = 0; + + //Arrays to cache all the buffers and offsets in order to make batch calls + MetalArgumentBufferArray mtlVertexArgBuffers; + MetalArgumentBufferArrayOffsets mtlVertexArgBufferOffsets; + MetalArgumentBufferArray mtlFragmentOrComputeArgBuffers; + MetalArgumentBufferArrayOffsets mtlFragmentOrComputeArgBufferOffsets; + + mtlVertexArgBuffers.fill(nil); + mtlFragmentOrComputeArgBuffers.fill(nil); + mtlVertexArgBufferOffsets.fill(0); + mtlFragmentOrComputeArgBufferOffsets.fill(0); + + for (uint32_t slot = 0; slot < RHI::Limits::Pipeline::ShaderResourceGroupCountMax; ++slot) { - const ShaderResourceGroup* shaderResourceGroup = bindings.m_srgsBySlot[srgIndex]; - uint32_t slotIndex = pipelineLayout.GetSlotByIndex(srgIndex); + const ShaderResourceGroup* shaderResourceGroup = bindings.m_srgsBySlot[slot]; + uint32_t slotIndex = pipelineLayout.GetIndexBySlot(slot); if(!shaderResourceGroup || slotIndex == RHI::Limits::Pipeline::ShaderResourceGroupCountMax) { continue; } - if (bindings.m_srgsByIndex[srgIndex] != shaderResourceGroup) + uint32_t srgVisIndex = pipelineLayout.GetIndexBySlot(shaderResourceGroup->GetBindingSlot()); + const RHI::ShaderStageMask& srgVisInfo = pipelineLayout.GetSrgVisibility(srgVisIndex); + + bool isSrgUpdatd = bindings.m_srgsByIndex[slot] != shaderResourceGroup; + if(isSrgUpdatd) { - bindings.m_srgsByIndex[srgIndex] = shaderResourceGroup; + bindings.m_srgsByIndex[slot] = shaderResourceGroup; auto& compiledArgBuffer = shaderResourceGroup->GetCompiledArgumentBuffer(); - id argBuffer = compiledArgBuffer.GetArgEncoderBuffer(); size_t argBufferOffset = compiledArgBuffer.GetOffset(); - - uint32_t srgVisIndex = pipelineLayout.GetSlotByIndex(shaderResourceGroup->GetBindingSlot()); - const RHI::ShaderStageMask& srgVisInfo = pipelineLayout.GetSrgVisibility(srgVisIndex); - + if(srgVisInfo != RHI::ShaderStageMask::None) { - const ShaderResourceGroupVisibility& srgResourcesVisInfo = pipelineLayout.GetSrgResourcesVisibility(srgVisIndex); - - //For graphics and compute encoder bind the argument buffer and - //make the resource resident for the duration of the work associated with the current scope - //and ensure that it's in a format compatible with the appropriate metal function. + //For graphics and compute shader stages, cache all the argument buffers, offsets and track the min/max indices if(m_commandEncoderType == CommandEncoderType::Render) { id renderEncoder = GetEncoder>(); uint8_t numBitsSet = RHI::CountBitsSet(static_cast(srgVisInfo)); if( numBitsSet > 1 || srgVisInfo == RHI::ShaderStageMask::Vertex) { - [renderEncoder setVertexBuffer:argBuffer - offset:argBufferOffset - atIndex:slotIndex]; + mtlVertexArgBuffers[slotIndex] = argBuffer; + mtlVertexArgBufferOffsets[slotIndex] = argBufferOffset; + bufferVertexRegisterIdMin = AZStd::min(slotIndex, bufferVertexRegisterIdMin); + bufferVertexRegisterIdMax = AZStd::max(slotIndex, bufferVertexRegisterIdMax); } if( numBitsSet > 1 || srgVisInfo == RHI::ShaderStageMask::Fragment) { - [renderEncoder setFragmentBuffer:argBuffer - offset:argBufferOffset - atIndex:slotIndex]; + mtlFragmentOrComputeArgBuffers[slotIndex] = argBuffer; + mtlFragmentOrComputeArgBufferOffsets[slotIndex] = argBufferOffset; + bufferFragmentOrComputeRegisterIdMin = AZStd::min(slotIndex, bufferFragmentOrComputeRegisterIdMin); + bufferFragmentOrComputeRegisterIdMax = AZStd::max(slotIndex, bufferFragmentOrComputeRegisterIdMax); } + } + else if(m_commandEncoderType == CommandEncoderType::Compute) + { + mtlFragmentOrComputeArgBuffers[slotIndex] = argBuffer; + mtlFragmentOrComputeArgBufferOffsets[slotIndex] = argBufferOffset; + bufferFragmentOrComputeRegisterIdMin = AZStd::min(slotIndex, bufferFragmentOrComputeRegisterIdMin); + bufferFragmentOrComputeRegisterIdMax = AZStd::max(slotIndex, bufferFragmentOrComputeRegisterIdMax); + } + } + } + + //Check if the srg has been updated or if the srg resources visibility hash has been updated + //as it is possible for draw items to have different PSOs in the same pass. + const AZ::HashValue64 srgResourcesVisHash = pipelineLayout.GetSrgResourcesVisibilityHash(srgVisIndex); + if(bindings.m_srgVisHashByIndex[slot] != srgResourcesVisHash || isSrgUpdatd) + { + bindings.m_srgVisHashByIndex[slot] = srgResourcesVisHash; + if(srgVisInfo != RHI::ShaderStageMask::None) + { + const ShaderResourceGroupVisibility& srgResourcesVisInfo = pipelineLayout.GetSrgResourcesVisibility(srgVisIndex); + + //For graphics and compute encoder make the resource resident (call UseResource) for the duration + //of the work associated with the current scope and ensure that it's in a + //format compatible with the appropriate metal function. + if(m_commandEncoderType == CommandEncoderType::Render) + { shaderResourceGroup->AddUntrackedResourcesToEncoder(m_encoder, srgResourcesVisInfo); } else if(m_commandEncoderType == CommandEncoderType::Compute) { - id computeEncoder = GetEncoder>(); - [computeEncoder setBuffer:argBuffer - offset:argBufferOffset - atIndex:pipelineLayout.GetSlotByIndex(srgIndex)]; shaderResourceGroup->AddUntrackedResourcesToEncoder(m_encoder, srgResourcesVisInfo); } } } } - + + //For graphics and compute encoder bind all the argument buffers + if(m_commandEncoderType == CommandEncoderType::Render) + { + BindArgumentBuffers(RHI::ShaderStage::Vertex, + bufferVertexRegisterIdMin, + bufferVertexRegisterIdMax, + mtlVertexArgBuffers, + mtlVertexArgBufferOffsets); + + BindArgumentBuffers(RHI::ShaderStage::Fragment, + bufferFragmentOrComputeRegisterIdMin, + bufferFragmentOrComputeRegisterIdMax, + mtlFragmentOrComputeArgBuffers, + mtlFragmentOrComputeArgBufferOffsets); + } + else if(m_commandEncoderType == CommandEncoderType::Compute) + { + BindArgumentBuffers(RHI::ShaderStage::Compute, + bufferFragmentOrComputeRegisterIdMin, + bufferFragmentOrComputeRegisterIdMax, + mtlFragmentOrComputeArgBuffers, + mtlFragmentOrComputeArgBufferOffsets); + } + return true; } + + void CommandList::BindArgumentBuffers(RHI::ShaderStage shaderStage, + uint16_t registerIdMin, + uint16_t registerIdMax, + MetalArgumentBufferArray& mtlArgBuffers, + MetalArgumentBufferArrayOffsets mtlArgBufferOffsets) + { + //Metal Api only lets you bind multiple argument buffers in an array as long as there are no gaps in the array + //In order to accomodate that we break up the calls when a gap is noticed in the array and reconfigure the NSRange. + uint16_t startingIndex = registerIdMin; + bool trackingRange = true; + for(int i = registerIdMin; i <= registerIdMax+1; i++) + { + if(trackingRange) + { + if(mtlArgBuffers[i] == nil) + { + NSRange range = { startingIndex, i-startingIndex }; + + switch(shaderStage) + { + case RHI::ShaderStage::Vertex: + { + id renderEncoder = GetEncoder>(); + [renderEncoder setVertexBuffers:&mtlArgBuffers[startingIndex] + offsets:&mtlArgBufferOffsets[startingIndex] + withRange:range]; + break; + } + case RHI::ShaderStage::Fragment: + { + id renderEncoder = GetEncoder>(); + [renderEncoder setFragmentBuffers:&mtlArgBuffers[startingIndex] + offsets:&mtlArgBufferOffsets[startingIndex] + withRange:range]; + break; + } + case RHI::ShaderStage::Compute: + { + id computeEncoder = GetEncoder>(); + [computeEncoder setBuffers:&mtlArgBuffers[startingIndex] + offsets:&mtlArgBufferOffsets[startingIndex] + withRange:range]; + break; + } + default: + { + AZ_Assert(false, "Not supported"); + } + } + + trackingRange = false; + + } + } + else + { + if(mtlArgBuffers[i] != nil) + { + startingIndex = i; + trackingRange = true; + } + } + } + } void CommandList::Submit(const RHI::DrawItem& drawItem) { @@ -447,6 +573,7 @@ namespace AZ for (size_t i = 0; i < bindings.m_srgsByIndex.size(); ++i) { bindings.m_srgsByIndex[i] = nullptr; + bindings.m_srgVisHashByIndex[i] = AZ::HashValue64{0}; } const PipelineLayout& pipelineLayout = pipelineState->GetPipelineLayout(); @@ -469,6 +596,10 @@ namespace AZ void CommandList::SetStreamBuffers(const RHI::StreamBufferView* streams, uint32_t count) { + uint16_t bufferArrayLen = 0; + AZStd::array, METAL_MAX_ENTRIES_BUFFER_ARG_TABLE> mtlStreamBuffers; + AZStd::array mtlStreamBufferOffsets; + AZ::HashValue64 streamsHash = AZ::HashValue64{0}; for (uint32_t i = 0; i < count; ++i) { @@ -479,18 +610,25 @@ namespace AZ { m_state.m_streamsHash = streamsHash; AZ_Assert(count <= METAL_MAX_ENTRIES_BUFFER_ARG_TABLE , "Slots needed cannot exceed METAL_MAX_ENTRIES_BUFFER_ARG_TABLE"); - for (uint32_t i = 0; i < count; ++i) + + NSRange range = {METAL_MAX_ENTRIES_BUFFER_ARG_TABLE - count, count}; + //The stream buffers are populated from bottom to top as the top slots are taken by argument buffers + for (int i = count-1; i >= 0; --i) { if (streams[i].GetBuffer()) { const Buffer * buff = static_cast(streams[i].GetBuffer()); id mtlBuff = buff->GetMemoryView().GetGpuAddress>(); - uint32_t VBIndex = (METAL_MAX_ENTRIES_BUFFER_ARG_TABLE - 1) - i; uint32_t offset = streams[i].GetByteOffset() + buff->GetMemoryView().GetOffset(); - id renderEncoder = GetEncoder>(); - [renderEncoder setVertexBuffer: mtlBuff offset: offset atIndex: VBIndex]; + mtlStreamBuffers[bufferArrayLen] = mtlBuff; + mtlStreamBufferOffsets[bufferArrayLen] = offset; + bufferArrayLen++; } } + id renderEncoder = GetEncoder>(); + [renderEncoder setVertexBuffers: mtlStreamBuffers.data() + offsets: mtlStreamBufferOffsets.data() + withRange: range]; } } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h index 6660beb2c4..8674124cc2 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandList.h @@ -99,8 +99,17 @@ namespace AZ { AZStd::array m_srgsByIndex; AZStd::array m_srgsBySlot; + AZStd::array m_srgVisHashByIndex; }; + using MetalArgumentBufferArray = AZStd::array, RHI::Limits::Pipeline::ShaderResourceGroupCountMax>; + using MetalArgumentBufferArrayOffsets = AZStd::array; + void BindArgumentBuffers(RHI::ShaderStage shaderStage, + uint16_t registerIdMin, + uint16_t registerIdMax, + MetalArgumentBufferArray& mtlArgBuffers, + MetalArgumentBufferArrayOffsets mtlArgBufferOffsets); + ShaderResourceBindings& GetShaderResourceBindingsByPipelineType(RHI::PipelineStateType pipelineType); //! This is kept as a separate struct so that we can robustly reset it. Every property diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp index f95e871d33..81e589e62e 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/Conversions.cpp @@ -12,6 +12,7 @@ #include "Atom_RHI_Metal_precompiled.h" #include +#include #include #include #include @@ -456,8 +457,35 @@ namespace AZ MTLColorWriteMask ConvertColorWriteMask(AZ::u8 writeMask) { - //todo::Based on the mask set the correct writemask - return MTLColorWriteMaskAll; + MTLColorWriteMask colorMask = MTLColorWriteMaskNone; + if(writeMask == 0) + { + return colorMask; + } + + if(RHI::CheckBitsAll(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) + { + return MTLColorWriteMaskAll; + } + + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) + { + colorMask |= MTLColorWriteMaskRed; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) + { + colorMask |= MTLColorWriteMaskGreen; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) + { + colorMask |= MTLColorWriteMaskBlue; + } + if (RHI::CheckBitsAny(writeMask, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) + { + colorMask |= MTLColorWriteMaskAlpha; + } + + return colorMask; } MTLVertexFormat ConvertVertexFormat(RHI::Format format) diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp index f237884aab..9a30a04deb 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.cpp @@ -70,6 +70,7 @@ namespace AZ m_srgVisibilities.resize(RHI::Limits::Pipeline::ShaderResourceGroupCountMax); m_srgResourcesVisibility.resize(RHI::Limits::Pipeline::ShaderResourceGroupCountMax); + m_srgResourcesVisibilityHash.resize(RHI::Limits::Pipeline::ShaderResourceGroupCountMax); for (uint32_t srgLayoutIdx = 0; srgLayoutIdx < groupLayoutCount; ++srgLayoutIdx) { const RHI::ShaderResourceGroupLayout& srgLayout = *descriptor.GetShaderResourceGroupLayout(srgLayoutIdx); @@ -111,6 +112,7 @@ namespace AZ m_srgVisibilities[srgIndex] = mask; m_srgResourcesVisibility[srgIndex] = srgVis; + m_srgResourcesVisibilityHash[srgIndex] = srgVis.GetHash(); } // Cache the inline constant size and slot index @@ -123,12 +125,12 @@ namespace AZ size_t PipelineLayout::GetSlotByIndex(size_t index) const { - return m_slotToIndexTable[index]; + return m_indexToSlotTable[index]; } size_t PipelineLayout::GetIndexBySlot(size_t slot) const { - return m_indexToSlotTable[slot]; + return m_slotToIndexTable[slot]; } const RHI::ShaderStageMask& PipelineLayout::GetSrgVisibility(uint32_t index) const @@ -141,6 +143,11 @@ namespace AZ return m_srgResourcesVisibility[index]; } + const AZ::HashValue64 PipelineLayout::GetSrgResourcesVisibilityHash(uint32_t index) const + { + return m_srgResourcesVisibilityHash[index]; + } + uint32_t PipelineLayout::GetRootConstantsSize() const { return m_rootConstantsSize; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.h index e8cd249393..6fd06ea29b 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/PipelineLayout.h @@ -57,6 +57,9 @@ namespace AZ /// Returns srgVisibility data const ShaderResourceGroupVisibility& GetSrgResourcesVisibility(uint32_t index) const; + /// Returns srgVisibility hash + const AZ::HashValue64 GetSrgResourcesVisibilityHash(uint32_t index) const; + /// Returns the root constant specific layout information uint32_t GetRootConstantsSize() const; uint32_t GetRootConstantsSlotIndex() const; @@ -84,6 +87,9 @@ namespace AZ /// Cache Visibility across all the resources within the SRG AZStd::fixed_vector m_srgResourcesVisibility; + /// Cache Visibility hash across all the resources within the SRG + AZStd::fixed_vector m_srgResourcesVisibilityHash; + uint32_t m_rootConstantSlotIndex = (uint32_t)-1; uint32_t m_rootConstantsSize = 0; }; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp index 1f870548cc..94599bc390 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp @@ -73,6 +73,10 @@ namespace AZ m_metalView.metalLayer.drawableSize = CGSizeMake(descriptor.m_dimensions.m_imageWidth, descriptor.m_dimensions.m_imageHeight); } + else + { + AddSubView(); + } m_drawables.resize(descriptor.m_dimensions.m_imageCount); @@ -83,6 +87,20 @@ namespace AZ return RHI::ResultCode::Success; } + void SwapChain::AddSubView() + { + NativeViewType* superView = reinterpret_cast(m_nativeWindow); + + CGFloat screenScale = Platform::GetScreenScale(); + CGRect screenBounds = [superView bounds]; + m_metalView = [[RHIMetalView alloc] initWithFrame: screenBounds + scale: screenScale + device: m_mtlDevice]; + + [m_metalView retain]; + [superView addSubview: m_metalView]; + } + void SwapChain::ShutdownInternal() { if (m_viewController) @@ -161,16 +179,7 @@ namespace AZ } else { - NativeViewType* superView = reinterpret_cast(m_nativeWindow); - - CGFloat screenScale = Platform::GetScreenScale(); - CGRect screenBounds = [superView bounds]; - m_metalView = [[RHIMetalView alloc] initWithFrame: screenBounds - scale: screenScale - device: m_mtlDevice]; - - [m_metalView retain]; - [superView addSubview: m_metalView]; + AddSubView(); } } return RHI::ResultCode::Success; diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h index d455f3dd0c..dfe8b3365d 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h @@ -49,6 +49,8 @@ namespace AZ RHI::ResultCode ResizeInternal(const RHI::SwapChainDimensions& dimensions, RHI::SwapChainDimensions* nativeDimensions) override; ////////////////////////////////////////////////////////////////////////// + void AddSubView(); + id m_mtlCommandBuffer; RHIMetalView* m_metalView = nullptr; NativeViewControllerType* m_viewController = nullptr; diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp index accc18b5ec..8cad9b1f56 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Conversion.cpp @@ -334,19 +334,30 @@ namespace AZ VkColorComponentFlags ConvertComponentFlags(uint8_t sflags) { VkColorComponentFlags dflags = 0; - if (RHI::CheckBitsAny(sflags, static_cast(1))) + + if(sflags == 0) + { + return dflags; + } + + if(RHI::CheckBitsAll(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskAll))) + { + return VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + } + + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskRed))) { dflags |= VK_COLOR_COMPONENT_R_BIT; } - if (RHI::CheckBitsAny(sflags, static_cast(2))) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskGreen))) { dflags |= VK_COLOR_COMPONENT_G_BIT; } - if (RHI::CheckBitsAny(sflags, static_cast(4))) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskBlue))) { dflags |= VK_COLOR_COMPONENT_B_BIT; } - if (RHI::CheckBitsAny(sflags, static_cast(8))) + if (RHI::CheckBitsAny(sflags, static_cast(RHI::WriteChannelMask::ColorWriteMaskAlpha))) { dflags |= VK_COLOR_COMPONENT_A_BIT; } diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h index 81b23068a1..94fd20f828 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h @@ -138,6 +138,12 @@ namespace AZ //! Without per draw viewport, the viewport setup in pass is usually used. void UnsetViewport(); + //! Set stencil reference for following draws which are added to this DynamicDrawContext + void SetStencilReference(uint8_t stencilRef); + + //! Get the current stencil reference. + uint8_t GetStencilReference() const; + //! Draw Indexed primitives with vertex and index data and per draw srg //! The per draw srg need to be provided if it's required by shader. void DrawIndexed(void* vertexData, uint32_t vertexCount, void* indexData, uint32_t indexCount, RHI::IndexFormat indexFormat, Data::Instance < ShaderResourceGroup> drawSrg = nullptr); @@ -204,10 +210,13 @@ namespace AZ bool m_useScissor = false; RHI::Scissor m_scissor; - // current scissor + // current viewport bool m_useViewport = false; RHI::Viewport m_viewport; + // Current stencil reference value + uint8_t m_stencilRef = 0; + // Cached RHI pipeline states for different combination of render states AZStd::unordered_map m_cachedRhiPipelineStates; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/AttachmentReadback.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/AttachmentReadback.h index 539e1a5398..3c00ce4e4c 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/AttachmentReadback.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/AttachmentReadback.h @@ -99,7 +99,7 @@ namespace AZ void DecomposeExecute(const RHI::FrameGraphExecuteContext& context); // copy data from the read back buffer (m_readbackBuffer) to the data buffer (m_dataBuffer) - void CopyBufferData(uint32_t readbackBufferIndex); + bool CopyBufferData(uint32_t readbackBufferIndex); // Get read back data in a structure ReadbackResult GetReadbackResult() const; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/CopyPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/CopyPass.h index ede14564cd..fd5cdabc83 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/CopyPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/CopyPass.h @@ -49,7 +49,7 @@ namespace AZ void CopyImageToBuffer(const RHI::FrameGraphCompileContext& context); // Pass behavior overrides - void BuildAttachmentsInternal() override; + void BuildInternal() override; // Scope producer functions... void SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) override; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/FullscreenTrianglePass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/FullscreenTrianglePass.h index fdb3241b44..616fc4639a 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/FullscreenTrianglePass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/FullscreenTrianglePass.h @@ -44,14 +44,11 @@ namespace AZ //! Creates a FullscreenTrianglePass static Ptr Create(const PassDescriptor& descriptor); - // Clears the initialized flag so that the next time PrepareFrameInternal is called, it will update the pipeline state - void Invalidate(); - protected: FullscreenTrianglePass(const PassDescriptor& descriptor); - virtual void Init(); // Pass behavior overrides... + void InitializeInternal() override; void FrameBeginInternal(FramePrepareParams params) override; RHI::Viewport m_viewportState; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/MSAAResolvePass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/MSAAResolvePass.h index 91ae1536c3..71776982b3 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/MSAAResolvePass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/MSAAResolvePass.h @@ -40,7 +40,7 @@ namespace AZ MSAAResolvePass(const PassDescriptor& descriptor); // Pass behavior overrides... - void BuildAttachmentsInternal() override; + void BuildInternal() override; void FrameBeginInternal(FramePrepareParams params) override; private: 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 b4f63a7099..727398040d 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 @@ -109,15 +109,16 @@ namespace AZ // --- Pass Behaviour Overrides --- void ResetInternal() override; - void BuildAttachmentsInternal() override; - void OnBuildAttachmentsFinishedInternal() override; + void BuildInternal() override; + void OnInitializationFinishedInternal() override; + void InitializeInternal() override; void FrameBeginInternal(FramePrepareParams params) override; void FrameEndInternal() override; // Finds the pass in m_children and removes it void RemoveChild(Ptr pass); - // Orphans all children from clearing m_children. + // Orphans all children by clearing m_children. void RemoveChildren(); private: 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 3d0a8cd3a8..9d0b36d126 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 @@ -40,6 +40,7 @@ friend class PassSystem; \ friend class PassFactory; \ friend class ParentPass; \ + friend class RenderPipeline; \ friend class UnitTest::PassTests; \ namespace UnitTest @@ -78,9 +79,9 @@ namespace AZ //! //! When authoring a new pass class, inherit from Pass and override any of the virtual functions //! ending with 'Internal' to define the behavior of your passes. These virtual are recursively - //! called in Preorder order throughout the pass tree. Only FramePrepare and FrameEnd are + //! called in preorder traversal throughout the pass tree. Only FrameBegin and FrameEnd are //! guaranteed to be called per frame. The other override-able functions are called as needed - //! when scheduled with the PassSystem. See QueueForBuildAttachments and QueueForRemoval. + //! when scheduled with the PassSystem. See QueueForBuildAndInitialization, QueueForRemoval and QueueForInitialization. //! //! Passes are created by the PassFactory. They can be created using either Pass Name, //! a PassTemplate, or a PassRequest. To register your pass class with the PassFactory, @@ -153,11 +154,14 @@ namespace AZ // --- Utility functions --- - //! Queues the pass to have BuildAttachments() called by the PassSystem on frame update - void QueueForBuildAttachments(); + //! Queues the pass to have Build() and Initilize() called by the PassSystem on frame update + void QueueForBuildAndInitialization(); //! Queues the pass to have RemoveFromParent() called by the PassSystem on frame update - void QueueForRemoval(bool needsDeletion = false); + void QueueForRemoval(); + + //! Queues the pass to have Initialize() called by the PassSystem on frame update + void QueueForInitialization(); //! Adds an attachment binding to the list of this Pass' attachment bindings void AddAttachmentBinding(PassAttachmentBinding attachmentBinding); @@ -173,8 +177,8 @@ namespace AZ //! Attach an external buffer resource as attachment to specified slot //! The buffer will be added as a pass attachment then attach to the pass slot - //! Note: the pass attachment and binding will be removed after the general BuildAttachments call. - //! you can add this call in pass' BuildAttachmentsInternal so it will be added whenever attachments get rebuilt + //! Note: the pass attachment and binding will be removed after the general Build call. + //! you can add this call in pass' BuildInternal so it will be added whenever attachments get rebuilt void AttachBufferToSlot(AZStd::string_view slot, Data::Instance buffer); void AttachBufferToSlot(const Name& slot, Data::Instance buffer); void AttachImageToSlot(const Name& slot, Data::Instance image); @@ -256,6 +260,12 @@ namespace AZ //! Returns pointer to the parent pass ParentPass* GetParent() const; + PassState GetPassState() const; + + // Update all bindings on this pass that are connected to bindings on other passes + void UpdateConnectedBindings(); + + protected: explicit Pass(const PassDescriptor& descriptor); @@ -309,18 +319,23 @@ namespace AZ // customize it's behavior, hence why these functions are called the pass behavior functions. // Resets everything in the pass (like Attachments). - // Called from PassSystem when pass is QueueForBuildAttachments. + // Called from PassSystem when pass is QueueForBuildAndInitialization. void Reset(); virtual void ResetInternal() { } // Builds and sets up any attachments and input/output connections the pass needs. - // Called from PassSystem when pass is QueueForBuildAttachments. - void BuildAttachments(); - virtual void BuildAttachmentsInternal() { } + // Called from PassSystem when pass is QueueForBuildAndInitialization. + void Build(bool calledFromPassSystem = false); + virtual void BuildInternal() { } - // Called after the pass build phase has finished. Allows passes to reset build flags. - void OnBuildAttachmentsFinished(); - virtual void OnBuildAttachmentsFinishedInternal() { }; + // Allows for additional pass initialization between building and rendering + // Can be queued independently of Build so as to only invoke Initialize() without Build() + void Initialize(); + virtual void InitializeInternal() { }; + + // Called after the pass initialization phase has finished. Allows passes to reset various states and flags. + void OnInitializationFinished(); + virtual void OnInitializationFinishedInternal() { }; // The Pass's 'Render' function. Called every frame, here the pass sets up it's rendering logic with // the FrameGraphBuilder. This is where your derived pass needs to call ImportScopeProducer on @@ -378,21 +393,36 @@ namespace AZ { struct { + // Whether this pass was created with a PassRequest (in which case m_request holds valid data) uint64_t m_createdByPassRequest : 1; - uint64_t m_initialized : 1; + + // Whether the pass is enabled (behavior can be customized by overriding IsEnabled() ) uint64_t m_enabled : 1; + + // False if parent or one of it's ancestors is disabled uint64_t m_parentEnabled : 1; - uint64_t m_alreadyCreated : 1; - uint64_t m_alreadyReset : 1; - uint64_t m_alreadyPrepared : 1; + + // If this is a parent pass, indicates if the pass has already created children this frame + // Prevents ParentPass::CreateChildPasses from executing multiple times in the same pass + uint64_t m_alreadyCreatedChildren : 1; + + // If this is a parent pass, indicates whether the pass needs to create child passes + uint64_t m_createChildren : 1; + + // Whether this pass belongs to the pass hierarchy, i.e. if you can trace it's parents up to the Root pass uint64_t m_partOfHierarchy : 1; + + // Whether this pass has a DrawListTag uint64_t m_hasDrawListTag : 1; + + // Whether this pass has a PipelineViewTag uint64_t m_hasPipelineViewTag : 1; - uint64_t m_queuedForBuildAttachment : 1; + + // Whether the pass should gather timestamp query metrics uint64_t m_timestampQueryEnabled : 1; + + // Whether the pass should gather pipeline statics uint64_t m_pipelineStatisticsQueryEnabled : 1; - uint64_t m_isBuildingAttachments : 1; - uint64_t m_isRendering : 1; }; uint64_t m_allFlags = 0; }; @@ -412,6 +442,7 @@ namespace AZ // fully custom sort implementations by overriding the SortDrawList() function. RHI::DrawListSortType m_drawListSortType = RHI::DrawListSortType::KeyThenDepth; + private: // Return the Timestamp result of this pass virtual TimestampResult GetTimestampResultInternal() const; @@ -423,6 +454,10 @@ namespace AZ // buffers and images don't get deleted during attachment build phase void StoreImportedAttachmentReferences(); + // Used by the RenderPipeline to create it's passes immediately instead of waiting on + // the next Pass System update. The function internally build and initializes the pass. + void ManualPipelineBuildAndInitialize(); + // --- Hierarchy related functions --- // Called when the pass gets a new spot in the pass hierarchy @@ -460,9 +495,6 @@ namespace AZ // This sets the binding's attachment pointer to the connected binding's attachment void UpdateConnectedBinding(PassAttachmentBinding& binding); - // Update all bindings on this pass that are connected to bindings on other passes - void UpdateConnectedBindings(); - // Process a PassFallbackConnection to connect an output to an input to act as a short-circuit for when Pass is disabled void ProcessFallbackConnection(const PassFallbackConnection& connection); @@ -502,6 +534,7 @@ namespace AZ // buffers and images don't get deleted during attachment build phase AZStd::vector> m_importedAttachmentStore; + // Name of the pass. Will be concatenated with parent names to form a unique path Name m_name; // Path of the pass in the hierarchy. Example: Root.Ssao.Downsample @@ -510,6 +543,12 @@ namespace AZ // Depth of the tree hierarchy this pass is at. // Example: Root would be depth 0, Root.Ssao.Downsample depth 2 uint32_t m_treeDepth = 0; + + // Used to track what phase of build/execution the pass is in + PassState m_state = PassState::Uninitialized; + + // Used to track what phases of build/initialization the pass is queued for + PassQueueState m_queueState = PassQueueState::NoQueue; }; //! Struct used to return results from Pass hierarchy validation diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassDefines.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassDefines.h index d536104768..a7513cc53c 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassDefines.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassDefines.h @@ -21,3 +21,100 @@ // Set this to 1 locally on your machine to facilitate pass debugging and get extra information // about passes in the output window. DO NOT SUBMIT with value set to 1 #define AZ_RPI_ENABLE_PASS_DEBUGGING 0 + +namespace AZ +{ + namespace RPI + { + // This enum tracks the state of passes across build, initialization and rendering + // + // Standard order of state progression: + // + // Uninitialized -> Queued -> Resetting -> Reset -> Building -> Built -> Initializing -> Initialized -> Idle -> Rendering -> Idle ... + // + // Addition state transitions: + // + // Queued -> Resetting + // -> Building + // -> Initializing + // + // Idle -> Queued + // -> Resetting + // -> Building + // -> Initializing + // -> Rendering + // + // Rendering -> Idle + // -> Queued (Rendering will transition to Queued if a pass was queued with the PassSystem during Rendering) + // + enum class PassState : u8 + { + // Default value, you should only ever see this in the Pass constructor + // Once the constructor is done, the Pass will set it's state to Reset + Uninitialized, + // | + // | + // V + // Pass is queued with the Pass System for an update (see PassQueueState below) + Queued, + // | + // | + // V + // Pass is currently in the process of resetting + Resetting, + // | + // | + // V + // Pass has been reset and is awaiting build + Reset, + // | + // | + // V + // Pass is currently building + Building, + // | + // | + // V + // Pass has been built and is awaiting initialization + Built, + // | + // | + // V + // Pass is currently being initialized + Initializing, + // | + // | + // V + // Pass has been initialized + Initialized, + // | + // | + // V + // Idle state, pass is awaiting rendering + Idle, + // | + // | + // V + // Pass is currently rendering. Pass must be in Idle state before entering this state + Rendering + }; + + // This enum keeps track of what actions the pass is queued for with the pass system + enum class PassQueueState : u8 + { + // The pass is currently not in any queued state and may therefore transition to any queued state + NoQueue, + + // The pass is queued for Removal at the start of the next frame. Has the highest priority and cannot be overridden by any other queue state + QueuedForRemoval, + + // The pass is queued for Build at the start of the frame. Note that any pass built at the start of the frame will also be Initialized. + // This state can be overridden by QueuedForRemoval, as we don't want to build a pass that has been removed. + QueuedForBuildAndInitialization, + + // The pass is queued for Initialization at the start of the frame. + // This state has the lowest priority and can therefore be overridden by QueueForBuildAndInitialization or QueueForRemoval. + QueuedForInitialization, + }; + } +} 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 fd30f4f406..534bf211dd 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 @@ -73,12 +73,12 @@ namespace AZ bool LoadPassTemplateMappings(const AZStd::string& templateMappingPath) override; void WriteTemplateToFile(const PassTemplate& passTemplate, AZStd::string_view assetFilePath) override; void DebugPrintPassHierarchy() override; - bool IsBuilding() const override; bool IsHotReloading() const override; void SetHotReloading(bool hotReloading) override; void SetTargetedPassDebuggingName(const AZ::Name& targetPassName) override; const AZ::Name& GetTargetedPassDebuggingName() const override; void ConnectEvent(OnReadyLoadTemplatesEvent::Handler& handler) override; + PassSystemState GetState() const override; // PassSystemInterface factory related functions... void AddPassCreator(Name className, PassCreator createFunction) override; @@ -103,8 +103,11 @@ namespace AZ // Returns the root of the pass tree hierarchy const Ptr& GetRootPass() override; - // Calls BuildAttachments() on passes queued in m_buildAttachmentsList - void BuildPassAttachments(); + // Calls Build() on passes queued in m_buildPassList + void BuildPasses(); + + // Calls Initialize() on passes queued in m_initializePassList + void InitializePasses(); // Validates Pass Hierarchy after building void Validate(); @@ -113,13 +116,15 @@ namespace AZ void RemovePasses(); // Functions for queuing passes in the lists below - void QueueForBuildAttachments(Pass* pass) override; + void QueueForBuild(Pass* pass) override; void QueueForRemoval(Pass* pass) override; + void QueueForInitialization(Pass* pass) override; // Lists for queuing passes for various function calls // Name of the list reflects the pass function it will call - AZStd::vector< Ptr > m_buildAttachmentsList; + AZStd::vector< Ptr > m_buildPassList; AZStd::vector< Ptr > m_removePassList; + AZStd::vector< Ptr > m_initializePassList; // Library of pass descriptors that can be instantiated through data driven pass requests PassLibrary m_passLibrary; @@ -133,9 +138,6 @@ namespace AZ // Whether the Pass Hierarchy changed bool m_passHierarchyChanged = true; - // Whether the Pass System is currently in it's building phase - bool m_isBuilding = false; - // Whether the Pass System is currently hot reloading passes bool m_isHotReloading = false; @@ -147,6 +149,9 @@ namespace AZ // Events OnReadyLoadTemplatesEvent m_loadTemplatesEvent; + + // Used to track what phase of execution the pass system is in + PassSystemState m_state = PassSystemState::Unitialized; }; } // namespace RPI } // namespace AZ 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 1224bf9545..72501fc61c 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 @@ -40,6 +40,37 @@ namespace AZ using PassCreator = AZStd::function(const PassDescriptor& descriptor)>; + // Enum to track the different execution phases of the Pass System + enum class PassSystemState : u32 + { + // Default state, + Unitialized, + + // Initial Pass System setup. Transitions to Idle + InitializingPassSystem, + + // Pass System is processing passes queued for Removal. Transitions to Idle + RemovingPasses, + + // Pass System is processing passes queued for Build (and their child passes). Transitions to Idle + BuildingPasses, + + // Pass System is processing passes queued for Initialization (and their child passes). Transitions to Idle + InitializingPasses, + + // Pass System is validating that the Pass hierarchy is in a valid state after Build and Initialization. Transitions to Idle + ValidatingPasses, + + // Pass System is idle and can transition to any other state (except FrameEnd) + Idle, + + // Pass System is currently rendering a frame. Transitions to FrameEnd + Rendering, + + // Pass System is finishing rendering a frame. Transitions to Idle + FrameEnd, + }; + class PassSystemInterface { friend class Pass; @@ -77,9 +108,6 @@ namespace AZ //! Prints the entire pass hierarchy from the root virtual void DebugPrintPassHierarchy() = 0; - //! Returns whether the Pass System is currently in it's build phase - virtual bool IsBuilding() const = 0; - //! Returns whether the Pass System is currently hot reloading virtual bool IsHotReloading() const = 0; @@ -157,15 +185,20 @@ namespace AZ //! The handler can add new pass templates or load pass template mappings from assets virtual void ConnectEvent(OnReadyLoadTemplatesEvent::Handler& handler) = 0; + virtual PassSystemState GetState() const = 0; + private: // These functions are only meant to be used by the Pass class - // Schedules a pass to have it's BuildAttachments() function called during frame update - virtual void QueueForBuildAttachments(Pass* pass) = 0; + // Schedules a pass to have it's Build() function called during frame update + virtual void QueueForBuild(Pass* pass) = 0; // Schedules a pass to be deleted during frame update virtual void QueueForRemoval(Pass* pass) = 0; + // Schedules a pass to be initialized during frame update + virtual void QueueForInitialization(Pass* pass) = 0; + //! Registers the pass with the pass library. Called in the Pass constructor. virtual void RegisterPass(Pass* pass) = 0; 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 5cd917e841..8f8267393e 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 @@ -96,7 +96,7 @@ namespace AZ void BindPassSrg(const RHI::FrameGraphCompileContext& context, Data::Instance& shaderResourceGroup); // Pass behavior overrides... - void OnBuildAttachmentsFinishedInternal() override; + void InitializeInternal() override; void FrameBeginInternal(FramePrepareParams params) override; void FrameEndInternal() override; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/DownsampleMipChainPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/DownsampleMipChainPass.h index 71c1fe4c49..eb4c4ed0aa 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/DownsampleMipChainPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/DownsampleMipChainPass.h @@ -46,7 +46,7 @@ namespace AZ // Pass Behaviour Overrides... void ResetInternal() override; - void BuildAttachmentsInternal() override; + void BuildInternal() override; void FrameBeginInternal(FramePrepareParams params) override; private: diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.h index 293a750f38..235871df83 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.h @@ -61,7 +61,7 @@ namespace AZ // Pass overrides void CreateChildPassesInternal() override; - void BuildAttachmentsInternal() override; + void BuildInternal() override; void FrameBeginInternal(FramePrepareParams params) override; void FrameEndInternal() override; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h index c8daab8ef8..c86203b79b 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h @@ -47,7 +47,7 @@ namespace AZ protected: // Pass behavior overrides - void BuildAttachmentsInternal() override; + void BuildInternal() override; void FrameBeginInternal(FramePrepareParams params) override; // Function to be called when output size changed diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SelectorPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SelectorPass.h index fe0ce231eb..2d876c6040 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SelectorPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SelectorPass.h @@ -49,7 +49,7 @@ namespace AZ SelectorPass(const PassDescriptor& descriptor); // Pass behavior overrides - void BuildAttachmentsInternal() final; + void BuildInternal() final; // the input slot index each output slot connect to AZStd::vector m_connections; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SwapChainPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SwapChainPass.h index 9aa00c99c2..7eac0d366d 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SwapChainPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Specific/SwapChainPass.h @@ -64,7 +64,7 @@ namespace AZ protected: // Pass behavior overrides void CreateChildPassesInternal() override final; - void BuildAttachmentsInternal() override final; + void BuildInternal() override final; void FrameBeginInternal(FramePrepareParams params) override final; // WindowNotificationBus::Handler overrides ... diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp index 00c2122825..09e3499820 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicDrawContext.cpp @@ -382,6 +382,16 @@ namespace AZ m_useViewport = false; } + void DynamicDrawContext::SetStencilReference(uint8_t stencilRef) + { + m_stencilRef = stencilRef; + } + + uint8_t DynamicDrawContext::GetStencilReference() const + { + return m_stencilRef; + } + void DynamicDrawContext::SetShaderVariant(ShaderVariantId shaderVariantId) { AZ_Assert( m_initialized && m_supportShaderVariants, "DynamicDrawContext is not initialized or unable to support shader variants. " @@ -475,6 +485,9 @@ namespace AZ drawItem.m_viewports = &m_viewport; } + // Set stencil reference. Used when stencil is enabled. + drawItem.m_stencilRef = m_stencilRef; + drawItemInfo.m_sortKey = m_sortKey++; m_cachedDrawItems.emplace_back(drawItemInfo); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp index 6435ae9570..88d3eb27fd 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp @@ -320,8 +320,14 @@ namespace AZ { if (m_state == ReadbackState::Reading) { - CopyBufferData(readbackBufferCurrentIndex); - m_state = ReadbackState::Success; + if (CopyBufferData(readbackBufferCurrentIndex)) + { + m_state = ReadbackState::Success; + } + else + { + m_state = ReadbackState::Failed; + } } if (m_callback) { @@ -498,13 +504,13 @@ namespace AZ return result; } - void AttachmentReadback::CopyBufferData(uint32_t readbackBufferIndex) + bool AttachmentReadback::CopyBufferData(uint32_t readbackBufferIndex) { Data::Instance readbackBufferCurrent = m_readbackBufferArray[readbackBufferIndex]; if (!readbackBufferCurrent) { - return; + return false; } auto bufferSize = readbackBufferCurrent->GetBufferSize(); @@ -537,6 +543,7 @@ namespace AZ } m_isReadbackComplete[readbackBufferIndex] = true; + return true; } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/CopyPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/CopyPass.cpp index 4a6c883584..9648a47fd6 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/CopyPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/CopyPass.cpp @@ -74,7 +74,7 @@ namespace AZ // --- Pass behavior overrides --- - void CopyPass::BuildAttachmentsInternal() + void CopyPass::BuildInternal() { AZ_Assert(GetInputCount() == 1 && GetOutputCount() == 1, "CopyPass has %d inputs and %d outputs. It should have exactly one of each.", diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/FullscreenTrianglePass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/FullscreenTrianglePass.cpp index 1f8f361b8b..a854867998 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/FullscreenTrianglePass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/FullscreenTrianglePass.cpp @@ -64,6 +64,8 @@ namespace AZ void FullscreenTrianglePass::LoadShader() { + AZ_Assert(GetPassState() != PassState::Rendering, "FullscreenTrianglePass - Reloading shader during Rendering phase!"); + // Load FullscreenTrianglePassData const FullscreenTrianglePassData* passData = PassUtils::GetPassData(m_passDescriptor); if (passData == nullptr) @@ -121,14 +123,16 @@ namespace AZ // Store stencil reference value for the draw call m_stencilRef = passData->m_stencilRef; - m_flags.m_initialized = false; + QueueForInitialization(); ShaderReloadNotificationBus::Handler::BusDisconnect(); ShaderReloadNotificationBus::Handler::BusConnect(shaderAsset.GetId()); } - void FullscreenTrianglePass::Init() + void FullscreenTrianglePass::InitializeInternal() { + RenderPass::InitializeInternal(); + // This draw item purposefully does not reference any geometry buffers. // Instead it's expected that the extended class uses a vertex shader // that generates a full-screen triangle completely from vertex ids. @@ -155,17 +159,10 @@ namespace AZ m_item.m_arguments = RHI::DrawArguments(draw); m_item.m_pipelineState = m_shader->AcquirePipelineState(pipelineStateDescriptor); m_item.m_stencilRef = m_stencilRef; - - m_flags.m_initialized = true; } void FullscreenTrianglePass::FrameBeginInternal(FramePrepareParams params) { - if (!m_flags.m_initialized) - { - Init(); - } - const PassAttachment* outputAttachment = nullptr; if (GetOutputCount() > 0) @@ -225,11 +222,6 @@ namespace AZ commandList->Submit(m_item); } - - void FullscreenTrianglePass::Invalidate() - { - m_flags.m_initialized = false; - } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/MSAAResolvePass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/MSAAResolvePass.cpp index c889f367b7..e457cb4992 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/MSAAResolvePass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/MSAAResolvePass.cpp @@ -38,7 +38,7 @@ namespace AZ { } - void MSAAResolvePass::BuildAttachmentsInternal() + void MSAAResolvePass::BuildInternal() { AZ_Assert(GetOutputCount() != 0, "MSAAResolvePass %s has no outputs to render to.", GetPathName().GetCStr()); } 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 106ef82bf1..cbae56e809 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp @@ -40,7 +40,7 @@ namespace AZ ParentPass::ParentPass(const PassDescriptor& descriptor) : Pass(descriptor) { - CreateChildPasses(); + m_flags.m_createChildren = true; } ParentPass::~ParentPass() @@ -59,7 +59,7 @@ namespace AZ child->m_parent = this; child->OnHierarchyChange(); - QueueForBuildAttachments(); + QueueForBuildAndInitialization(); // Notify pipeline if (m_pipeline) @@ -248,25 +248,18 @@ namespace AZ void ParentPass::CreateChildPasses() { - // Flag prevents the function from executing multiple times a frame. Can happen - // as pass system has a list of passes for which it needs to call this function. - if (m_flags.m_alreadyCreated) + // The already created flag prevents this function from executing multiple times a frame + if (!m_flags.m_createChildren || m_flags.m_alreadyCreatedChildren) { return; } - m_flags.m_alreadyCreated = true; + m_flags.m_alreadyCreatedChildren = true; + RemoveChildren(); CreatePassesFromTemplate(); CreateChildPassesInternal(); - for (Ptr& child : m_children) - { - ParentPass* asParent = child->AsParent(); - if (asParent != nullptr) - { - asParent->CreateChildPasses(); - } - } + m_flags.m_createChildren = false; } void ParentPass::ResetInternal() @@ -277,19 +270,29 @@ namespace AZ } } - void ParentPass::BuildAttachmentsInternal() + void ParentPass::BuildInternal() + { + CreateChildPasses(); + + for (const Ptr& child : m_children) + { + child->Build(); + } + } + + void ParentPass::OnInitializationFinishedInternal() { for (const Ptr& child : m_children) { - child->BuildAttachments(); + child->OnInitializationFinished(); } } - void ParentPass::OnBuildAttachmentsFinishedInternal() + void ParentPass::InitializeInternal() { for (const Ptr& child : m_children) { - child->OnBuildAttachmentsFinished(); + child->Initialize(); } } 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 1e230a7fc0..99a6e23914 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -37,10 +37,12 @@ #include #include + namespace AZ { namespace RPI { + // --- Constructors --- Pass::Pass(const PassDescriptor& descriptor) @@ -71,7 +73,10 @@ namespace AZ } PassSystemInterface::Get()->RegisterPass(this); - QueueForBuildAttachments(); + QueueForBuildAndInitialization(); + + // Skip reset since the pass just got created + m_state = PassState::Reset; } Pass::~Pass() @@ -151,6 +156,8 @@ namespace AZ { AZ_RPI_PASS_ASSERT(m_parent != nullptr, "Trying to remove pass from parent but pointer to the parent pass is null."); m_parent->RemoveChild(Ptr(this)); + m_queueState = PassQueueState::NoQueue; + m_state = PassState::Idle; } void Pass::OnOrphan() @@ -162,6 +169,11 @@ namespace AZ // --- Getters & Setters --- + PassState Pass::GetPassState() const + { + return m_state; + } + ParentPass* Pass::GetParent() const { return m_parent; @@ -348,32 +360,6 @@ namespace AZ return nullptr; } - // --- Queuing functions with PassSystem --- - - void Pass::QueueForBuildAttachments() - { - // Don't queue if we're in building phase - if (!PassSystemInterface::Get()->IsBuilding()) - { - // m_queuedForBuildAttachment makes sure the pass only be queue for once - if (!m_flags.m_queuedForBuildAttachment) - { - PassSystemInterface::Get()->QueueForBuildAttachments(this); - m_flags.m_queuedForBuildAttachment = true; - - // Set these two flags to false since when queue build attachments request, they should all be already be false except one use - // case that the pass system processed all queued requests when active a scene. - m_flags.m_alreadyPrepared = false; - m_flags.m_alreadyReset = false; - } - } - } - - void Pass::QueueForRemoval([[maybe_unused]] bool needsDeletion) - { - PassSystemInterface::Get()->QueueForRemoval(this); - } - // --- PassTemplate related functions --- void Pass::CreateBindingsFromTemplate() @@ -403,7 +389,7 @@ namespace AZ PassAttachmentBinding* localBinding = FindAttachmentBinding(slot); if (!localBinding) { - AZ_RPI_PASS_ERROR(false, "Pass::AttachBufferToSlot - Pass %s failed to find slot %s.", + AZ_RPI_PASS_ERROR(false, "Pass::AttachBufferToSlot - Pass [%s] failed to find slot [%s].", m_path.GetCStr(), slot.GetCStr()); return; } @@ -413,7 +399,7 @@ namespace AZ // handle the connected bindings if (localBinding->m_attachment) { - AZ_RPI_PASS_ERROR(false, "Pass::AttachBufferToSlot - Slot %s already has attachment %s.", + AZ_RPI_PASS_ERROR(false, "Pass::AttachBufferToSlot - Slot [%s] already has attachment [%s].", slot.GetCStr(), localBinding->m_attachment->m_name.GetCStr()); return; } @@ -435,7 +421,7 @@ namespace AZ PassAttachmentBinding* localBinding = FindAttachmentBinding(slot); if (!localBinding) { - AZ_RPI_PASS_ERROR(false, "Pass::AttachImageToSlot - Pass %s failed to find slot %s.", + AZ_RPI_PASS_ERROR(false, "Pass::AttachImageToSlot - Pass [%s] failed to find slot [%s].", m_path.GetCStr(), slot.GetCStr()); return; } @@ -445,7 +431,7 @@ namespace AZ // handle the connected bindings if (localBinding->m_attachment) { - AZ_RPI_PASS_ERROR(false, "Pass::AttachImageToSlot - Slot %s already has attachment %s.", + AZ_RPI_PASS_ERROR(false, "Pass::AttachImageToSlot - Slot [%s] already has attachment [%s].", slot.GetCStr(), localBinding->m_attachment->m_name.GetCStr()); return; } @@ -468,7 +454,7 @@ namespace AZ PassAttachmentBinding* localBinding = FindAttachmentBinding(connection.m_localSlot); if (!localBinding) { - AZ_RPI_PASS_ERROR(false, "Pass::ProcessConnection - Pass %s failed to find slot %s.", + AZ_RPI_PASS_ERROR(false, "Pass::ProcessConnection - Pass [%s] failed to find slot [%s].", m_path.GetCStr(), connection.m_localSlot.GetCStr()); return; @@ -490,7 +476,7 @@ namespace AZ { foundPass = true; const Ptr attachment = FindOwnedAttachment(connectedSlotName); - AZ_RPI_PASS_ERROR(attachment, "Pass::ProcessConnection - Pass %s doesn't own an attachment named %s.", + AZ_RPI_PASS_ERROR(attachment, "Pass::ProcessConnection - Pass [%s] doesn't own an attachment named [%s].", m_path.GetCStr(), connectedSlotName.GetCStr()); localBinding->SetAttachment(attachment); @@ -601,10 +587,10 @@ namespace AZ if (!outputBinding || !inputBinding) { - AZ_RPI_PASS_ERROR(inputBinding, "Pass::ProcessFallbackConnection - Pass %s failed to find input slot %s.", + AZ_RPI_PASS_ERROR(inputBinding, "Pass::ProcessFallbackConnection - Pass [%s] failed to find input slot [%s].", m_path.GetCStr(), connection.m_inputSlotName.GetCStr()); - AZ_RPI_PASS_ERROR(outputBinding, "Pass::ProcessFallbackConnection - Pass %s failed to find output slot %s.", + AZ_RPI_PASS_ERROR(outputBinding, "Pass::ProcessFallbackConnection - Pass [%s] failed to find output slot [%s].", m_path.GetCStr(), connection.m_outputSlotName.GetCStr()); return; @@ -614,10 +600,10 @@ namespace AZ if (!typesAreValid) { - AZ_RPI_PASS_ERROR(inputBinding->m_slotType == PassSlotType::Input, "Pass::ProcessFallbackConnection - Pass %s specifies fallback connection input %s, which is not an input.", + AZ_RPI_PASS_ERROR(inputBinding->m_slotType == PassSlotType::Input, "Pass::ProcessFallbackConnection - Pass [%s] specifies fallback connection input [%s], which is not an input.", m_path.GetCStr(), connection.m_inputSlotName.GetCStr()); - AZ_RPI_PASS_ERROR(outputBinding->m_slotType == PassSlotType::Output, "Pass::ProcessFallbackConnection - Pass %s specifies fallback connection output %s, which is not an output.", + AZ_RPI_PASS_ERROR(outputBinding->m_slotType == PassSlotType::Output, "Pass::ProcessFallbackConnection - Pass [%s] specifies fallback connection output [%s], which is not an output.", m_path.GetCStr(), connection.m_inputSlotName.GetCStr()); return; @@ -1007,7 +993,7 @@ namespace AZ { Ptr targetAttachment = nullptr; - if (!m_flags.m_isBuildingAttachments && !IsEnabled() && binding.m_slotType == PassSlotType::Output && binding.m_fallbackBinding) + if (m_state != PassState::Building && !IsEnabled() && binding.m_slotType == PassSlotType::Output && binding.m_fallbackBinding) { targetAttachment = binding.m_fallbackBinding->m_attachment; } @@ -1028,7 +1014,7 @@ namespace AZ // Check whether the template's slot allows this attachment if (m_template && !m_template->AttachmentFitsSlot(targetAttachment->m_descriptor, binding.m_name)) { - AZ_RPI_PASS_ERROR(false, "Pass::UpdateConnectedBinding - Attachment %s did not match the filters of input slot %s on pass %s.", + AZ_RPI_PASS_ERROR(false, "Pass::UpdateConnectedBinding - Attachment [%s] did not match the filters of input slot [%s] on pass [%s].", targetAttachment->m_name.GetCStr(), binding.m_name.GetCStr(), m_path.GetCStr()); @@ -1050,17 +1036,83 @@ namespace AZ } } + // --- Queuing functions with PassSystem --- + + void Pass::QueueForBuildAndInitialization() + { + // Don't queue if we're currently building. Don't queue if we're already queued for Build or Removal + if (m_state != PassState::Building && + m_queueState != PassQueueState::QueuedForBuildAndInitialization && + m_queueState != PassQueueState::QueuedForRemoval) + { + // NOTE: We only queue for Build here, the queue for Initialization happens at the end of Pass::Build + // (doing it this way is an optimization to minimize the number of passes queued for initialization, + // as many passes will be initialized by their parent passes and thus don't need to be queued) + PassSystemInterface::Get()->QueueForBuild(this); + + m_queueState = PassQueueState::QueuedForBuildAndInitialization; + + // Transition state + // If we are Rendering, the state will transition [Rendering -> Queued] in Pass::FrameEnd + if (m_state != PassState::Rendering) + { + m_state = PassState::Queued; + } + } + } + + void Pass::QueueForInitialization() + { + // Only queue if the pass is not in any queue. Don't queue if we're currently initializing. + if (m_queueState == PassQueueState::NoQueue && m_state != PassState::Initializing) + { + PassSystemInterface::Get()->QueueForInitialization(this); + m_queueState = PassQueueState::QueuedForInitialization; + + // Transition state + // If we are Rendering, the state will transition [Rendering -> Queued] in Pass::FrameEnd + // If the state is Built, preserve the state since [Built -> Initializing] is a valid transition + // Preserving PassState::Built lets the pass ignore subsequent build calls in the same frame + if (m_state != PassState::Rendering && m_state != PassState::Built) + { + m_state = PassState::Queued; + } + } + } + + void Pass::QueueForRemoval() + { + // Skip only if we're already queued for removal, otherwise proceed. + // QueuedForRemoval overrides QueuedForBuildAndInitialization and QueuedForInitialization. + if (m_queueState != PassQueueState::QueuedForRemoval) + { + PassSystemInterface::Get()->QueueForRemoval(this); + m_queueState = PassQueueState::QueuedForRemoval; + + // Transition state + // If we are Rendering, the state will transition [Rendering -> Queued] in Pass::FrameEnd + if (m_state != PassState::Rendering) + { + m_state = PassState::Queued; + } + } + } + // --- Pass behavior functions --- void Pass::Reset() { - // Flag prevents the function from executing multiple times a frame. Can happen - // as pass system has a list of passes for which it needs to call this function. - if (m_flags.m_alreadyReset) + // Ensure we're in a valid state to reset. This ensures the pass won't be reset multiple times in the same frame. + bool execute = (m_state == PassState::Idle); + execute = execute || (m_state == PassState::Queued && m_queueState == PassQueueState::QueuedForBuildAndInitialization); + execute = execute || (m_state == PassState::Queued && m_queueState == PassQueueState::QueuedForInitialization); + + if (!execute) { return; } - m_flags.m_alreadyReset = true; + + m_state = PassState::Resetting; // Store references to imported attachments to underlying images and buffers aren't deleted during attachment building StoreImportedAttachmentReferences(); @@ -1075,22 +1127,23 @@ namespace AZ m_executeBeforePasses.clear(); ResetInternal(); + + m_state = PassState::Reset; } - void Pass::BuildAttachments() + void Pass::Build(bool calledFromPassSystem) { - m_flags.m_queuedForBuildAttachment = false; + AZ_RPI_BREAK_ON_TARGET_PASS; + + // Ensure we're in a valid state to build. This ensures the pass won't be built multiple times in the same frame. + bool execute = (m_state == PassState::Reset); - // Flag prevents the function from executing multiple times a frame. Can happen - // as pass system has a list of passes for which it needs to call this function. - if (m_flags.m_alreadyPrepared) + if (!execute) { return; } - m_flags.m_alreadyPrepared = true; - m_flags.m_isBuildingAttachments = true; - AZ_RPI_BREAK_ON_TARGET_PASS; + m_state = PassState::Building; // Bindings, inputs and attachments CreateBindingsFromTemplate(); @@ -1101,7 +1154,7 @@ namespace AZ SetupInputsFromTemplate(); // Custom pass behavior - BuildAttachmentsInternal(); + BuildInternal(); // Outputs SetupOutputsFromTemplate(); @@ -1112,21 +1165,46 @@ namespace AZ UpdateOwnedAttachments(); UpdateAttachmentUsageIndices(); - m_flags.m_isBuildingAttachments = false; + m_state = PassState::Built; + m_queueState = PassQueueState::NoQueue; + + // If this pass's Build() wasn't called from the Pass System, then it was called by it's parent pass + // In which case we don't need to queue for initialization because the parent will already be queued + if (calledFromPassSystem) + { + // Queue for Initialization + QueueForInitialization(); + } } - void Pass::OnBuildAttachmentsFinished() + void Pass::Initialize() { AZ_RPI_BREAK_ON_TARGET_PASS; - // These flags are to prevent a pass from being built multiple times. - // We reset them after each build phase. - m_flags.m_alreadyCreated = false; - m_flags.m_alreadyPrepared = false; - m_flags.m_alreadyReset = false; - m_flags.m_queuedForBuildAttachment = false; + // Ensure we're in a valid state to initialize. This ensures the pass won't be initialized multiple times in the same frame. + bool execute = (m_state == PassState::Idle || m_state == PassState::Built); + execute = execute || (m_state == PassState::Queued && m_queueState == PassQueueState::QueuedForInitialization); + + if (!execute) + { + return; + } + + m_state = PassState::Initializing; + m_queueState = PassQueueState::NoQueue; + + InitializeInternal(); + + m_state = PassState::Initialized; + } + + void Pass::OnInitializationFinished() + { + m_flags.m_alreadyCreatedChildren = false; m_importedAttachmentStore.clear(); - OnBuildAttachmentsFinishedInternal(); + OnInitializationFinishedInternal(); + + m_state = PassState::Idle; } void Pass::Validate(PassValidationResults& validationResults) @@ -1182,7 +1260,9 @@ namespace AZ UpdateConnectedBindings(); return; } - m_flags.m_isRendering = true; + + AZ_Assert(m_state == PassState::Idle, "Pass::FrameBegin - Pass [%s] is attempting to render, but is not in the Idle state.", m_path.GetCStr()); + m_state = PassState::Rendering; UpdateConnectedBindings(); UpdateOwnedAttachments(); @@ -1197,10 +1277,10 @@ namespace AZ void Pass::FrameEnd() { - if (m_flags.m_isRendering) + if (m_state == PassState::Rendering) { FrameEndInternal(); - m_flags.m_isRendering = false; + m_state = (m_queueState == PassQueueState::NoQueue) ? PassState::Idle : PassState::Queued; } } @@ -1238,6 +1318,13 @@ namespace AZ return m_pipeline; } + void Pass::ManualPipelineBuildAndInitialize() + { + Build(); + Initialize(); + OnInitializationFinished(); + } + Scene* Pass::GetScene() const { if (m_pipeline) 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 448c28f202..cc51c8760a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp @@ -50,6 +50,7 @@ namespace AZ { namespace RPI { + PassSystemInterface* PassSystemInterface::Get() { return Interface::Get(); @@ -93,11 +94,21 @@ namespace AZ void PassSystem::Init() { + m_state = PassSystemState::InitializingPassSystem; + Interface::Register(this); m_passLibrary.Init(); m_passFactory.Init(&m_passLibrary); m_rootPass = CreatePass(Name{"Root"}); m_rootPass->m_flags.m_partOfHierarchy = true; + m_rootPass->m_flags.m_createChildren = false; + + // Here you can specify the name of a pass you would like to break into during execution + // If you enable AZ_RPI_ENABLE_PASS_DEBUGGING, then any pass matching the specified name will debug + // break on any instance of the AZ_RPI_BREAK_ON_TARGET_PASS macro. See Pass::Build for an example + // m_targetedPassDebugName = "MyPassName"; + + m_state = PassSystemState::Idle; } void PassSystem::InitPassTemplates() @@ -118,10 +129,10 @@ namespace AZ JsonSerializationUtils::SaveObjectToFile(&passAsset, assetFilePath); } - void PassSystem::QueueForBuildAttachments(Pass* pass) + void PassSystem::QueueForBuild(Pass* pass) { - AZ_Assert(pass != nullptr, "Queuing nullptr pass in PassSystem::QueueForBuildAttachments"); - m_buildAttachmentsList.push_back(pass); + AZ_Assert(pass != nullptr, "Queuing nullptr pass in PassSystem::QueueForBuild"); + m_buildPassList.push_back(pass); } void PassSystem::QueueForRemoval(Pass* pass) @@ -130,6 +141,12 @@ namespace AZ m_removePassList.push_back(pass); } + void PassSystem::QueueForInitialization(Pass* pass) + { + AZ_Assert(pass != nullptr, "Queuing nullptr pass in PassSystem::QueueForInitialization"); + m_initializePassList.push_back(pass); + } + // Sort so passes with less depth (closer to the root) are first. Used when changes // in the parent passes can affect the child passes, like with attachment building. void SortPassListAscending(AZStd::vector< Ptr >& passList) @@ -155,6 +172,7 @@ namespace AZ void PassSystem::RemovePasses() { + m_state = PassSystemState::RemovingPasses; AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: RemovePasses"); if (!m_removePassList.empty()) @@ -168,24 +186,25 @@ namespace AZ m_removePassList.clear(); } + + m_state = PassSystemState::Idle; } - void PassSystem::BuildPassAttachments() + void PassSystem::BuildPasses() { + m_state = PassSystemState::BuildingPasses; AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzRender); AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: BuildPassAttachments"); - m_isBuilding = true; + m_passHierarchyChanged = m_passHierarchyChanged || !m_buildPassList.empty(); - m_passHierarchyChanged = !m_buildAttachmentsList.empty(); - - // While loop is for the event in which passes being built add more pass to m_buildAttachmentsList - while(!m_buildAttachmentsList.empty()) + // While loop is for the event in which passes being built add more pass to m_buildPassList + while(!m_buildPassList.empty()) { AZ_Assert(m_removePassList.empty(), "Passes shouldn't be queued removal during build attachment process"); - AZStd::vector< Ptr > buildListCopy = m_buildAttachmentsList; - m_buildAttachmentsList.clear(); + AZStd::vector< Ptr > buildListCopy = m_buildPassList; + m_buildPassList.clear(); // Erase passes which were removed from pass tree already (which parent is empty) auto unused = AZStd::remove_if(buildListCopy.begin(), buildListCopy.end(), @@ -203,11 +222,8 @@ namespace AZ } for (const Ptr& pass : buildListCopy) { - pass->BuildAttachments(); + pass->Build(true); } - - // Signal all passes that we have finished building - m_rootPass->OnBuildAttachmentsFinished(); } if (m_passHierarchyChanged) @@ -221,11 +237,50 @@ namespace AZ #endif } - m_isBuilding = false; + m_state = PassSystemState::Idle; + } + + void PassSystem::InitializePasses() + { + m_state = PassSystemState::InitializingPasses; + AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzRender); + AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: BuildPassAttachments"); + + m_passHierarchyChanged = m_passHierarchyChanged || !m_initializePassList.empty(); + + while (!m_initializePassList.empty()) + { + AZStd::vector< Ptr > initListCopy = m_initializePassList; + m_initializePassList.clear(); + + // Erase passes which were removed from pass tree already (which parent is empty) + auto unused = AZStd::remove_if(initListCopy.begin(), initListCopy.end(), + [](const RHI::Ptr& currentPass) + { + return !currentPass->m_flags.m_partOfHierarchy; + }); + initListCopy.erase(unused, initListCopy.end()); + + SortPassListAscending(initListCopy); + + for (const Ptr& pass : initListCopy) + { + pass->Initialize(); + } + } + + if (m_passHierarchyChanged) + { + // Signal all passes that we have finished initialization + m_rootPass->OnInitializationFinished(); + } + + m_state = PassSystemState::Idle; } void PassSystem::Validate() { + m_state = PassSystemState::ValidatingPasses; AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: Validate"); if (PassValidation::IsEnabled()) @@ -241,12 +296,15 @@ namespace AZ m_rootPass->Validate(validationResults); validationResults.PrintValidationIfError(); } + + m_state = PassSystemState::Idle; } void PassSystem::ProcessQueuedChanges() { RemovePasses(); - BuildPassAttachments(); + BuildPasses(); + InitializePasses(); Validate(); } @@ -256,6 +314,8 @@ namespace AZ AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: FrameUpdate"); ProcessQueuedChanges(); + + m_state = PassSystemState::Rendering; Pass::FramePrepareParams params{ &frameGraphBuilder }; m_rootPass->FrameBegin(params); } @@ -264,6 +324,8 @@ namespace AZ { AZ_ATOM_PROFILE_FUNCTION("RHI", "PassSystem: FrameEnd"); + m_state = PassSystemState::FrameEnd; + m_rootPass->FrameEnd(); // remove any pipelines that are marked as ExecuteOnce @@ -278,12 +340,14 @@ namespace AZ } m_passHierarchyChanged = false; + + m_state = PassSystemState::Idle; } void PassSystem::Shutdown() { RemovePasses(); - m_buildAttachmentsList.clear(); + m_buildPassList.clear(); m_rootPass = nullptr; m_passFactory.Shutdown(); m_passLibrary.Shutdown(); @@ -296,9 +360,9 @@ namespace AZ return m_rootPass; } - bool PassSystem::IsBuilding() const + PassSystemState PassSystem::GetState() const { - return m_isBuilding; + return m_state; } bool PassSystem::IsHotReloading() const 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 3a2a556429..3e52130f6f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp @@ -128,7 +128,7 @@ namespace AZ } - void RenderPass::OnBuildAttachmentsFinishedInternal() + void RenderPass::InitializeInternal() { if (m_shaderResourceGroup != nullptr) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/DownsampleMipChainPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/DownsampleMipChainPass.cpp index 0e0ebf445e..329247d4eb 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/DownsampleMipChainPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/DownsampleMipChainPass.cpp @@ -181,12 +181,12 @@ namespace AZ // Pass behavior functions... - void DownsampleMipChainPass::BuildAttachmentsInternal() + void DownsampleMipChainPass::BuildInternal() { GetInputInfo(); BuildChildPasses(); UpdateChildren(); - ParentPass::BuildAttachmentsInternal(); + ParentPass::BuildInternal(); } void DownsampleMipChainPass::FrameBeginInternal(FramePrepareParams params) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.cpp index c72712bce5..28a03de297 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/EnvironmentCubeMapPass.cpp @@ -37,8 +37,6 @@ namespace AZ EnvironmentCubeMapPass::EnvironmentCubeMapPass(const PassDescriptor& passDescriptor) : ParentPass(passDescriptor) { - m_flags.m_alreadyCreated = false; - // load pass data const EnvironmentCubeMapPassData* passData = PassUtils::GetPassData(passDescriptor); if (passData == nullptr) @@ -88,8 +86,6 @@ namespace AZ AZ::Matrix4x4 viewToClipMatrix; MakePerspectiveFovMatrixRH(viewToClipMatrix, AZ::Constants::HalfPi, 1.0f, 0.1f, 100.0f, true); m_view->SetViewToClipMatrix(viewToClipMatrix); - - CreateChildPasses(); } EnvironmentCubeMapPass::~EnvironmentCubeMapPass() @@ -113,7 +109,7 @@ namespace AZ AddChild(m_childPass); } - void EnvironmentCubeMapPass::BuildAttachmentsInternal() + void EnvironmentCubeMapPass::BuildInternal() { // create output image descriptor m_outputImageDesc = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::Color | RHI::ImageBindFlags::CopyRead, CubeMapFaceSize, CubeMapFaceSize, RHI::Format::R16G16B16A16_FLOAT); @@ -135,7 +131,7 @@ namespace AZ m_attachmentBindings.push_back(outputAttachment); - ParentPass::BuildAttachmentsInternal(); + ParentPass::BuildInternal(); } void EnvironmentCubeMapPass::FrameBeginInternal(FramePrepareParams params) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/RenderToTexturePass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/RenderToTexturePass.cpp index 4356a56622..7306bf5b6a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/RenderToTexturePass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/RenderToTexturePass.cpp @@ -50,7 +50,7 @@ namespace AZ { } - void RenderToTexturePass::BuildAttachmentsInternal() + void RenderToTexturePass::BuildInternal() { m_outputAttachment = aznew PassAttachment(); m_outputAttachment->m_name = "RenderTarget"; @@ -73,7 +73,7 @@ namespace AZ m_attachmentBindings.push_back(outputBinding); - Base::BuildAttachmentsInternal(); + Base::BuildInternal(); } void RenderToTexturePass::FrameBeginInternal(FramePrepareParams params) @@ -100,7 +100,7 @@ namespace AZ m_passData.m_width = width; m_passData.m_height = height; OnUpdateOutputSize(); - QueueForBuildAttachments(); + QueueForBuildAndInitialization(); } void RenderToTexturePass::OnUpdateOutputSize() diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/SelectorPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/SelectorPass.cpp index 8e8c67393d..82cea31ca1 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/SelectorPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/SelectorPass.cpp @@ -44,7 +44,7 @@ namespace AZ } } - void SelectorPass::BuildAttachmentsInternal() + void SelectorPass::BuildInternal() { // Update output connections based on m_connections // This need to be done after BuildAttachment is finished @@ -72,7 +72,7 @@ namespace AZ m_connections[outputSlotIndex] = inputSlotIndex; // Queue to rebuild attachment connections - QueueForBuildAttachments(); + QueueForBuildAndInitialization(); } void SelectorPass::Connect(const AZ::Name& inputSlot, const AZ::Name& outputSlot) @@ -113,7 +113,7 @@ namespace AZ m_connections[outputIdx] = inputIdx; // Queue to rebuild attachment connections - QueueForBuildAttachments(); + QueueForBuildAndInitialization(); } } // namespace RPI diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/SwapChainPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/SwapChainPass.cpp index 5bcec87df0..7b79efbaec 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/SwapChainPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/SwapChainPass.cpp @@ -26,8 +26,6 @@ namespace AZ , m_windowContext(windowContext) , m_childTemplateName(childTemplateName) { - m_flags.m_alreadyCreated = false; - PassSystemInterface* passSystem = PassSystemInterface::Get(); // Create child pass @@ -44,8 +42,6 @@ namespace AZ m_childPass = passSystem->CreatePassFromRequest(&childRequest); AZ_Assert(m_childPass, "SwapChain child pass is invalid: check your passs pipeline, run configuration and your AssetProcessor set project (project_path)"); - - CreateChildPasses(); AzFramework::WindowNotificationBus::Handler::BusConnect(m_windowContext->GetWindowHandle()); } @@ -112,7 +108,7 @@ namespace AZ AddChild(m_childPass); } - void SwapChainPass::BuildAttachmentsInternal() + void SwapChainPass::BuildInternal() { if (m_windowContext->GetSwapChain() == nullptr) { @@ -124,7 +120,7 @@ namespace AZ SetupSwapChainAttachment(); - ParentPass::BuildAttachmentsInternal(); + ParentPass::BuildInternal(); } void SwapChainPass::FrameBeginInternal(FramePrepareParams params) @@ -154,7 +150,7 @@ namespace AZ void SwapChainPass::OnWindowResized([[maybe_unused]] uint32_t width, [[maybe_unused]] uint32_t height) { - QueueForBuildAttachments(); + QueueForBuildAndInitialization(); } void SwapChainPass::ReadbackSwapChain(AZStd::shared_ptr readback) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp index cc0cefd082..ad2fc4b19e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp @@ -107,7 +107,7 @@ namespace AZ pipeline->m_originalRenderSettings = desc.m_renderSettings; pipeline->m_activeRenderSettings = desc.m_renderSettings; pipeline->m_rootPass->SetRenderPipeline(pipeline); - pipeline->BuildPipelineViews(); + pipeline->m_rootPass->ManualPipelineBuildAndInitialize(); } RenderPipeline::~RenderPipeline() @@ -320,8 +320,8 @@ namespace AZ Ptr newRoot = m_rootPass->Recreate(); newRoot->SetRenderPipeline(this); - // Force processing of queued changes so we can validate the new pipeline - passSystem->ProcessQueuedChanges(); + // Manually build the pipeline + newRoot->ManualPipelineBuildAndInitialize(); // Validate the new root PassValidationResults validation; @@ -329,9 +329,8 @@ namespace AZ if (validation.IsValid()) { // Remove old pass - bool deletePass = true; m_rootPass->SetRenderPipeline(nullptr); - m_rootPass->QueueForRemoval(deletePass); + m_rootPass->QueueForRemoval(); // Set new root m_rootPass = newRoot; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 7e33750eb5..62efc3dccb 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -289,9 +289,10 @@ namespace AZ } pipeline->OnAddedToScene(this); + PassSystemInterface::Get()->ProcessQueuedChanges(); + pipeline->BuildPipelineViews(); // Force to update the lookup table since adding render pipeline would effect any pipeline states created before pass system tick - AZ::RPI::PassSystemInterface::Get()->ProcessQueuedChanges(); RebuildPipelineStatesLookup(); AZ_Assert(!m_id.IsNull(), "RPI::Scene needs to have a valid uuid."); diff --git a/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp b/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp index 96a21f8c9b..f9a2bc4493 100644 --- a/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp @@ -329,7 +329,7 @@ namespace UnitTest Ptr parentPass = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("ParentPass")); parentPass->Reset(); - parentPass->BuildAttachments(); + parentPass->Build(); PassValidationResults validationResults; parentPass->Validate(validationResults); @@ -351,7 +351,7 @@ namespace UnitTest Ptr parentPass = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("ParentPass")); parentPass->Reset(); - parentPass->BuildAttachments(); + parentPass->Build(); PassValidationResults validationResults; parentPass->Validate(validationResults); @@ -373,7 +373,7 @@ namespace UnitTest Ptr parentPass = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("ParentPass")); parentPass->Reset(); - parentPass->BuildAttachments(); + parentPass->Build(); PassValidationResults validationResults; parentPass->Validate(validationResults); @@ -397,7 +397,7 @@ namespace UnitTest parentPass->m_flags.m_partOfHierarchy = true; parentPass->OnHierarchyChange(); parentPass->Reset(); - parentPass->BuildAttachments(); + parentPass->Build(); PassValidationResults validationResults; parentPass->Validate(validationResults); @@ -421,7 +421,7 @@ namespace UnitTest parentPass->m_flags.m_partOfHierarchy = true; parentPass->OnHierarchyChange(); parentPass->Reset(); - parentPass->BuildAttachments(); + parentPass->Build(); PassValidationResults validationResults; parentPass->Validate(validationResults); @@ -445,7 +445,7 @@ namespace UnitTest parentPass->m_flags.m_partOfHierarchy = true; parentPass->OnHierarchyChange(); parentPass->Reset(); - parentPass->BuildAttachments(); + parentPass->Build(); PassValidationResults validationResults; parentPass->Validate(validationResults); @@ -469,7 +469,7 @@ namespace UnitTest parentPass->m_flags.m_partOfHierarchy = true; parentPass->OnHierarchyChange(); parentPass->Reset(); - parentPass->BuildAttachments(); + parentPass->Build(); PassValidationResults validationResults; parentPass->Validate(validationResults); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt b/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt index 134f605200..07449fe59f 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt @@ -113,6 +113,10 @@ ly_add_target( Gem::MaterialEditor.Document ) +# Add a 'builders' alias to allow the MaterialEditor root gem path to be added to the generated +# cmake_dependencies..assetprocessor.setreg to allow the asset scan folder for it to be added +ly_create_alias(NAME MaterialEditor.Builders NAMESPACE Gem) + # Add build dependency to Editor for the MaterialEditor application since # Editor opens up the MaterialEditor ly_add_dependencies(Editor Gem::MaterialEditor) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp new file mode 100644 index 0000000000..6a83c8c6de --- /dev/null +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp @@ -0,0 +1,42 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or +* a third party where indicated. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include + +namespace Platform +{ + void LoadPluginDependencies() + { + AZ_Warning("Material Editor", false, "LoadPluginDependencies() function is not implemented"); + } + + void ProcessInput(void* message) + { + AZ_Warning("Material Editor", false, "ProcessInput() function is not implemented"); + } + + AzFramework::NativeWindowHandle GetWindowHandle(WId winId) + { + AZ_Warning("Material Editor", false, "GetWindowHandle() function is not implemented"); + AZ_UNUSED(winId); + return nullptr; + } + + AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) + { + AZ_Warning("Material Editor", false, "GetClientAreaSize() function is not implemented"); + AZ_UNUSED(window); + return AzFramework::WindowSize{1,1}; + } +} diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Linux.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Linux.h new file mode 100644 index 0000000000..6f4eee610a --- /dev/null +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Linux.h @@ -0,0 +1,15 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MATERIALEDITOR_EXT "" + diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Platform.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Platform.h new file mode 100644 index 0000000000..9cd502877d --- /dev/null +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/PAL_linux.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/PAL_linux.cmake index 70d49fdb2c..77d41d4561 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/PAL_linux.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/PAL_linux.cmake @@ -9,4 +9,4 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -set(PAL_TRAIT_ATOM_MATERIAL_EDITOR_APPLICATION_SUPPORTED FALSE) +set(PAL_TRAIT_ATOM_MATERIAL_EDITOR_APPLICATION_SUPPORTED TRUE) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake index 5714be5dfb..038a605109 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake @@ -10,4 +10,7 @@ # set(FILES + MaterialEditor_Traits_Platform.h + MaterialEditor_Traits_Linux.h + MaterialEditor_Linux.cpp ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorBrowserInteractions.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorBrowserInteractions.cpp index 9b79bcb6d9..c4007616c5 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorBrowserInteractions.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorBrowserInteractions.cpp @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/PerformanceMonitor/PerformanceMonitorWidget.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/PerformanceMonitor/PerformanceMonitorWidget.cpp index 72c8daa63c..70a4361409 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/PerformanceMonitor/PerformanceMonitorWidget.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/PerformanceMonitor/PerformanceMonitorWidget.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include namespace MaterialEditor { diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/PerformanceMonitor/PerformanceMonitorWidget.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/PerformanceMonitor/PerformanceMonitorWidget.h index a75e856d83..dd31faef90 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/PerformanceMonitor/PerformanceMonitorWidget.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/PerformanceMonitor/PerformanceMonitorWidget.h @@ -13,7 +13,7 @@ #pragma once #include -#include +#include namespace Ui { diff --git a/Gems/Atom/Tools/MaterialEditor/gem.json b/Gems/Atom/Tools/MaterialEditor/gem.json new file mode 100644 index 0000000000..5113effc0a --- /dev/null +++ b/Gems/Atom/Tools/MaterialEditor/gem.json @@ -0,0 +1,10 @@ +{ + "gem_name": "MaterialEditor", + "display_name": "Atom Material Editor", + "summary": "Editor for creating, modifying, and previewing materials", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + ] +} diff --git a/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shadervariantlist b/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shadervariantlist index 79bb726c9f..c7eddb10f2 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shadervariantlist +++ b/Gems/AtomLyIntegration/AtomBridge/Assets/Shaders/LyShineUI.shadervariantlist @@ -4,7 +4,7 @@ { "StableId": 1, "Options": { - "o_preMultiplyAlpha": "true", + "o_preMultiplyAlpha": "false", "o_alphaTest": "false", "o_srgbWrite": "true", "o_modulate": "Modulate::None" @@ -14,7 +14,7 @@ "StableId": 2, "Options": { "o_preMultiplyAlpha": "false", - "o_alphaTest": "false", + "o_alphaTest": "true", "o_srgbWrite": "true", "o_modulate": "Modulate::None" } diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt b/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt index 4df40e3d13..abc4dfb2c6 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/AtomBridge/Code/CMakeLists.txt @@ -111,6 +111,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::AtomToolsFramework.Editor Gem::AtomViewportDisplayInfo Gem::AtomViewportDisplayIcons.Editor + Gem::MaterialEditor.Builders ) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp index e9a81f2e9f..06530b53b2 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialSystemComponent.cpp @@ -151,6 +151,7 @@ namespace AZ m_openMaterialEditorAction->setShortcut(QKeySequence(Qt::Key_M)); m_openMaterialEditorAction->setCheckable(false); m_openMaterialEditorAction->setChecked(false); + m_openMaterialEditorAction->setIcon(QIcon(":/Menu/material_editor.svg")); QObject::connect( m_openMaterialEditorAction, &QAction::triggered, m_openMaterialEditorAction, [this]() { @@ -158,7 +159,7 @@ namespace AZ } ); - AzToolsFramework::EditorMenuRequestBus::Broadcast(&AzToolsFramework::EditorMenuRequestBus::Handler::AddMenuAction, "ToolMenu", m_openMaterialEditorAction); + AzToolsFramework::EditorMenuRequestBus::Broadcast(&AzToolsFramework::EditorMenuRequestBus::Handler::AddMenuAction, "ToolMenu", m_openMaterialEditorAction, true); } } diff --git a/Gems/AudioSystem/Code/Tests/AudioSystemEditorTest.cpp b/Gems/AudioSystem/Code/Tests/AudioSystemEditorTest.cpp index e086246e29..d7f9b31e25 100644 --- a/Gems/AudioSystem/Code/Tests/AudioSystemEditorTest.cpp +++ b/Gems/AudioSystem/Code/Tests/AudioSystemEditorTest.cpp @@ -36,14 +36,14 @@ namespace CustomMocks : m_levelName(levelName) {} - AZ::IO::ArchiveFileIterator FindFirst([[maybe_unused]] AZStd::string_view dir, [[maybe_unused]] unsigned int flags, [[maybe_unused]] bool allowUseFileSystem) override + AZ::IO::ArchiveFileIterator FindFirst([[maybe_unused]] AZStd::string_view dir, AZ::IO::IArchive::EFileSearchType) override { AZ::IO::FileDesc fileDesc; fileDesc.nSize = sizeof(AZ::IO::FileDesc); // Add a filename and file description reference to the TestFindData map to make sure the file iterator is valid - AZStd::intrusive_ptr findData = new TestFindData{}; - findData->m_mapFiles.emplace(m_levelName, fileDesc); - return findData->Fetch(); + m_findData = new TestFindData(); + m_findData->m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ static_cast(m_findData.get()), m_levelName, fileDesc }); + return m_findData->Fetch(); } AZ::IO::ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator iter) override @@ -54,13 +54,14 @@ namespace CustomMocks // public: for easy resetting... AZStd::string m_levelName; - // Add an inherited FindData class to control the adding of a mapfile which indicates that a FileIterator is valid struct TestFindData : AZ::IO::FindData { - using AZ::IO::FindData::m_mapFiles; + using AZ::IO::FindData::m_fileStack; }; + + AZStd::intrusive_ptr m_findData; }; } // namespace CustomMocks diff --git a/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp b/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp index 9b6202b6c6..33290b9cfe 100644 --- a/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp +++ b/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp @@ -61,6 +61,7 @@ namespace Camera void CameraEditorSystemComponent::Activate() { + AzToolsFramework::EditorContextMenuBus::Handler::BusConnect(); AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); Camera::EditorCameraSystemRequestBus::Handler::BusConnect(); Camera::CameraViewRegistrationRequestsBus::Handler::BusConnect(); @@ -71,6 +72,7 @@ namespace Camera Camera::CameraViewRegistrationRequestsBus::Handler::BusDisconnect(); Camera::EditorCameraSystemRequestBus::Handler::BusDisconnect(); AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + AzToolsFramework::EditorContextMenuBus::Handler::BusDisconnect(); } void CameraEditorSystemComponent::PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2&, int flags) diff --git a/Gems/Camera/Code/Source/CameraEditorSystemComponent.h b/Gems/Camera/Code/Source/CameraEditorSystemComponent.h index ec361c1afd..44f23f38e6 100644 --- a/Gems/Camera/Code/Source/CameraEditorSystemComponent.h +++ b/Gems/Camera/Code/Source/CameraEditorSystemComponent.h @@ -15,6 +15,7 @@ #include #include +#include #include "CameraViewRegistrationBus.h" @@ -25,6 +26,7 @@ namespace Camera , private AzToolsFramework::EditorEvents::Bus::Handler , private EditorCameraSystemRequestBus::Handler , private CameraViewRegistrationRequestsBus::Handler + , private AzToolsFramework::EditorContextMenuBus::Handler { public: AZ_COMPONENT(CameraEditorSystemComponent, "{769802EB-722A-4F89-A475-DA396DA1FDCC}"); @@ -40,9 +42,13 @@ namespace Camera ////////////////////////////////////////////////////////////////////////// private: + ////////////////////////////////////////////////////////////////////////// + // AzToolsFramework::EditorContextMenuBus + void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override; + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// // AzToolsFramework::EditorEvents - void PopulateEditorGlobalContextMenu(QMenu *menu, const AZ::Vector2& point, int flags) override; void NotifyRegisterViews() override; ////////////////////////////////////////////////////////////////////////// diff --git a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp index 8e68c8cb44..61a5e89d80 100644 --- a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp @@ -891,6 +891,9 @@ namespace EMotionFX emotionFXWindowOptions.detachedWindow = true; #endif emotionFXWindowOptions.optionalMenuText = "Animation Editor"; + emotionFXWindowOptions.showOnToolsToolbar = true; + emotionFXWindowOptions.toolbarIcon = ":/Menu/emfx_editor.svg"; + EditorRequests::Bus::Broadcast(&EditorRequests::RegisterViewPane, EMStudio::MainWindow::GetEMotionFXPaneName(), LyViewPane::CategoryTools, emotionFXWindowOptions, windowCreationFunc); } diff --git a/Gems/LandscapeCanvas/Code/Source/LandscapeCanvasSystemComponent.cpp b/Gems/LandscapeCanvas/Code/Source/LandscapeCanvasSystemComponent.cpp index 72f7a65966..9e0129c6fe 100644 --- a/Gems/LandscapeCanvas/Code/Source/LandscapeCanvasSystemComponent.cpp +++ b/Gems/LandscapeCanvas/Code/Source/LandscapeCanvasSystemComponent.cpp @@ -146,6 +146,9 @@ namespace LandscapeCanvas { AzToolsFramework::ViewPaneOptions options; options.paneRect = QRect(100, 100, 1280, 1024); + options.showOnToolsToolbar = true; + options.toolbarIcon = ":/Menu/landscape_canvas_editor.svg"; + AzToolsFramework::RegisterViewPane(LyViewPane::LandscapeCanvas, LyViewPane::CategoryTools, options); } diff --git a/Gems/LyShine/Code/Editor/LyShineEditorSystemComponent.cpp b/Gems/LyShine/Code/Editor/LyShineEditorSystemComponent.cpp index d1c7ce96a4..19a47efdcd 100644 --- a/Gems/LyShine/Code/Editor/LyShineEditorSystemComponent.cpp +++ b/Gems/LyShine/Code/Editor/LyShineEditorSystemComponent.cpp @@ -153,6 +153,8 @@ namespace LyShineEditor opt.isPreview = true; opt.paneRect = QRect(x, y, (int)editorWidth, (int)editorHeight); opt.isDeletable = true; // we're in a plugin; make sure we can be deleted + opt.showOnToolsToolbar = true; + opt.toolbarIcon = ":/Menu/ui_editor.svg"; // opt.canHaveMultipleInstances = true; // uncomment this when CUiAnimViewSequenceManager::CanvasUnloading supports multiple canvases AzToolsFramework::RegisterViewPane(LyViewPane::UiEditor, LyViewPane::CategoryTools, opt); diff --git a/Gems/LyShine/Code/Source/RenderGraph.cpp b/Gems/LyShine/Code/Source/RenderGraph.cpp index d5a1df7b15..ad2c46f8e6 100644 --- a/Gems/LyShine/Code/Source/RenderGraph.cpp +++ b/Gems/LyShine/Code/Source/RenderGraph.cpp @@ -137,7 +137,11 @@ namespace LyShine AZ::RHI::Ptr dynamicDraw = uiRenderer->GetDynamicDrawContext(); const UiRenderer::UiShaderData& uiShaderData = uiRenderer->GetUiShaderData(); - dynamicDraw->SetShaderVariant(uiShaderData.m_shaderVariantDefault); + // Set render state + dynamicDraw->SetStencilState(uiRenderer->GetBaseState().m_stencilState); + dynamicDraw->SetTarget0BlendState(uiRenderer->GetBaseState().m_blendState); + + dynamicDraw->SetShaderVariant(uiRenderer->GetCurrentShaderVariant()); // Set up per draw SRG AZ::Data::Instance drawSrg = dynamicDraw->NewDrawSrg(); @@ -307,7 +311,7 @@ namespace LyShine //////////////////////////////////////////////////////////////////////////////////////////////////// void MaskRenderNode::Render(UiRenderer* uiRenderer) { - int priorBaseState = uiRenderer->GetBaseState(); + UiRenderer::BaseState priorBaseState = uiRenderer->GetBaseState(); if (m_isMaskingEnabled || m_drawBehind) { @@ -369,68 +373,61 @@ namespace LyShine #endif //////////////////////////////////////////////////////////////////////////////////////////////////// - void MaskRenderNode::SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, int priorBaseState) + void MaskRenderNode::SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState) { + UiRenderer::BaseState curBaseState = priorBaseState; + // If using alpha test for drawing the renderable components on this element then we turn on // alpha test as a pre-render step - int alphaTest = 0; - if (m_useAlphaTest) - { - alphaTest = GS_ALPHATEST_GREATER; - } + curBaseState.m_useAlphaTest = m_useAlphaTest; // if either of the draw flags are checked then we may want to draw the renderable component(s) // on this element, otherwise use the color mask to stop them rendering - int colorMask = GS_COLMASK_NONE; + curBaseState.m_blendState.m_enable = false; + curBaseState.m_blendState.m_writeMask = 0x0; if ((m_drawBehind && firstPass) || (m_drawInFront && !firstPass)) { - colorMask = 0; // mask everything, don't write color or alpha, we just write to stencil buffer + curBaseState.m_blendState.m_enable = true; + curBaseState.m_blendState.m_writeMask = 0xF; } - if (m_isMaskingEnabled) + if (m_isMaskingEnabled) { + AZ::RHI::StencilOpState stencilOpState; + stencilOpState.m_func = AZ::RHI::ComparisonFunc::Equal; + // masking is enabled so we want to setup to increment (first pass) or decrement (second pass) // the stencil buff when rendering the renderable component(s) on this element - int passOp = 0; if (firstPass) { - passOp = STENCOP_PASS(FSS_STENCOP_INCR); - gEnv->pRenderer->PushProfileMarker(s_maskIncrProfileMarker); + stencilOpState.m_passOp = AZ::RHI::StencilOp::Increment; } else { - passOp = STENCOP_PASS(FSS_STENCOP_DECR); - gEnv->pRenderer->PushProfileMarker(s_maskDecrProfileMarker); + stencilOpState.m_passOp = AZ::RHI::StencilOp::Decrement; } + curBaseState.m_stencilState.m_frontFace = stencilOpState; + curBaseState.m_stencilState.m_backFace = stencilOpState; + // set up for stencil write - const uint32 stencilRef = uiRenderer->GetStencilRef(); - const uint32 stencilMask = 0xFF; - const uint32 stencilWriteMask = 0xFF; - const int32 stencilState = STENC_FUNC(FSS_STENCFUNC_EQUAL) - | STENCOP_FAIL(FSS_STENCOP_KEEP) | STENCOP_ZFAIL(FSS_STENCOP_KEEP) | passOp; - gEnv->pRenderer->SetStencilState(stencilState, stencilRef, stencilMask, stencilWriteMask); - - // Set the base state that should be used when rendering the renderable component(s) on this - // element - uiRenderer->SetBaseState(priorBaseState | GS_STENCIL | alphaTest | colorMask); + AZ::RHI::Ptr dynamicDraw = uiRenderer->GetDynamicDrawContext(); + dynamicDraw->SetStencilReference(uiRenderer->GetStencilRef()); + curBaseState.m_stencilState.m_enable = true; + curBaseState.m_stencilState.m_writeMask = 0xFF; } else { // masking is not enabled - - // Even if not masking we still use alpha test (if checked). This is primarily to help the user to - // visualize what their alpha tested mask looks like. - if (colorMask || alphaTest) - { - uiRenderer->SetBaseState(priorBaseState | colorMask | alphaTest); - } + curBaseState.m_stencilState.m_enable = false; } + + uiRenderer->SetBaseState(curBaseState); } //////////////////////////////////////////////////////////////////////////////////////////////////// - void MaskRenderNode::SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, int priorBaseState) + void MaskRenderNode::SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState) { if (m_isMaskingEnabled) { @@ -442,26 +439,29 @@ namespace LyShine if (firstPass) { uiRenderer->IncrementStencilRef(); - gEnv->pRenderer->PopProfileMarker(s_maskIncrProfileMarker); } else { uiRenderer->DecrementStencilRef(); - gEnv->pRenderer->PopProfileMarker(s_maskDecrProfileMarker); } - // turn off stencil write and turn on stencil test - const uint32 stencilRef = uiRenderer->GetStencilRef(); - const uint32 stencilMask = 0xFF; - const uint32 stencilWriteMask = 0x00; - const int32 stencilState = STENC_FUNC(FSS_STENCFUNC_EQUAL) - | STENCOP_FAIL(FSS_STENCOP_KEEP) | STENCOP_ZFAIL(FSS_STENCOP_KEEP) | STENCOP_PASS(FSS_STENCOP_KEEP); - gEnv->pRenderer->SetStencilState(stencilState, stencilRef, stencilMask, stencilWriteMask); + AZ::RHI::Ptr dynamicDraw = uiRenderer->GetDynamicDrawContext(); + dynamicDraw->SetStencilReference(uiRenderer->GetStencilRef()); if (firstPass) { - // first pass, turn on stencil test for drawing children - uiRenderer->SetBaseState(priorBaseState | GS_STENCIL); + UiRenderer::BaseState curBaseState = priorBaseState; + + // turn off stencil write and turn on stencil test + curBaseState.m_stencilState.m_enable = true; + curBaseState.m_stencilState.m_writeMask = 0x00; + + AZ::RHI::StencilOpState stencilOpState; + stencilOpState.m_func = AZ::RHI::ComparisonFunc::Equal; + curBaseState.m_stencilState.m_frontFace = stencilOpState; + curBaseState.m_stencilState.m_backFace = stencilOpState; + + uiRenderer->SetBaseState(curBaseState); } else { @@ -475,7 +475,6 @@ namespace LyShine // remove any color mask or alpha test that we set in pre-render uiRenderer->SetBaseState(priorBaseState); } - } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -637,35 +636,24 @@ namespace LyShine //////////////////////////////////////////////////////////////////////////////////////////////////// void RenderGraph::BeginMask(bool isMaskingEnabled, bool useAlphaTest, bool drawBehind, bool drawInFront) { -#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (masks and render targets) - // this uses pool allocator MaskRenderNode* maskRenderNode = new MaskRenderNode(m_currentMask, isMaskingEnabled, useAlphaTest, drawBehind, drawInFront); m_currentMask = maskRenderNode; m_renderNodeListStack.push(&maskRenderNode->GetMaskRenderNodeList()); -#else - AZ_UNUSED(drawInFront); - AZ_UNUSED(drawBehind); - AZ_UNUSED(useAlphaTest); - AZ_UNUSED(isMaskingEnabled); -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// void RenderGraph::StartChildrenForMask() { -#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (masks and render targets) AZ_Assert(m_currentMask, "Calling StartChildrenForMask while not defining a mask"); m_renderNodeListStack.pop(); m_renderNodeListStack.push(&m_currentMask->GetContentRenderNodeList()); -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// void RenderGraph::EndMask() { -#ifdef LYSHINE_ATOM_TODO // keeping this code for future phase (masks and render targets) AZ_Assert(m_currentMask, "Calling EndMask while not defining a mask"); if (m_currentMask) { @@ -686,7 +674,6 @@ namespace LyShine m_renderNodeListStack.top()->push_back(newMaskRenderNode); } } -#endif } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -996,6 +983,12 @@ namespace LyShine // LYSHINE_ATOM_TODO - will probably need to support this when converting UI Editor to use Atom AZ_UNUSED(viewportSize); + AZ::RHI::Ptr dynamicDraw = uiRenderer->GetDynamicDrawContext(); + + // Disable stencil and enable blend/color write + dynamicDraw->SetStencilState(uiRenderer->GetBaseState().m_stencilState); + dynamicDraw->SetTarget0BlendState(uiRenderer->GetBaseState().m_blendState); + // First render the render targets, they are sorted so that more deeply nested ones are rendered first. #ifdef LYSHINE_ATOM_TODO // keeping this code for reference for future phase (render targets) diff --git a/Gems/LyShine/Code/Source/RenderGraph.h b/Gems/LyShine/Code/Source/RenderGraph.h index 2f1586e857..355616a29a 100644 --- a/Gems/LyShine/Code/Source/RenderGraph.h +++ b/Gems/LyShine/Code/Source/RenderGraph.h @@ -22,12 +22,11 @@ #include #include +#include "UiRenderer.h" #ifndef _RELEASE #include "LyShineDebug.h" #endif -class UiRenderer; - namespace LyShine { enum RenderNodeType @@ -157,8 +156,8 @@ namespace LyShine #endif private: // functions - void SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, int priorBaseState); - void SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, int priorBaseState); + void SetupBeforeRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState); + void SetupAfterRenderingMask(UiRenderer* uiRenderer, bool firstPass, UiRenderer::BaseState priorBaseState); private: // data AZStd::vector m_maskRenderNodes; //!< The render nodes used to render the mask shape diff --git a/Gems/LyShine/Code/Source/UiRenderer.cpp b/Gems/LyShine/Code/Source/UiRenderer.cpp index 57acbed5d5..e3ded694b5 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.cpp +++ b/Gems/LyShine/Code/Source/UiRenderer.cpp @@ -20,7 +20,6 @@ #include #include #include -#include // LYSHINE_ATOM_TODO - remove when GS_DEPTHFUNC_LEQUAL reference is removed with LyShine render target Atom conversion #include #include @@ -33,9 +32,7 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// UiRenderer::UiRenderer(AZ::RPI::ViewportContextPtr viewportContext) - : m_baseState(GS_DEPTHFUNC_LEQUAL) - , m_stencilRef(0) - , m_viewportContext(viewportContext) + : m_viewportContext(viewportContext) { // Use bootstrap scene event to indicate when the RPI has fully // initialized with all assets loaded and is ready to be used @@ -127,6 +124,8 @@ void UiRenderer::CreateDynamicDrawContext(AZ::RPI::ScenePtr scene, AZ::Data::Ins { "TEXCOORD", AZ::RHI::Format::R32G32_FLOAT }, { "BLENDINDICES", AZ::RHI::Format::R16G16_UINT } } ); + m_dynamicDraw->AddDrawStateOptions(AZ::RPI::DynamicDrawContext::DrawStateOptions::StencilState + | AZ::RPI::DynamicDrawContext::DrawStateOptions::BlendMode); m_dynamicDraw->EndInit(); } @@ -170,25 +169,24 @@ void UiRenderer::CacheShaderData(const AZ::RHI::Ptr shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("true"))); shaderOptionsDefault.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::None"))); m_uiShaderData.m_shaderVariantDefault = dynamicDraw->UseShaderVariant(shaderOptionsDefault); + AZ::RPI::ShaderOptionList shaderOptionsAlphaTest; + shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_preMultiplyAlpha"), AZ::Name("false"))); + shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_alphaTest"), AZ::Name("true"))); + shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_srgbWrite"), AZ::Name("true"))); + shaderOptionsAlphaTest.push_back(AZ::RPI::ShaderOption(AZ::Name("o_modulate"), AZ::Name("Modulate::None"))); + m_uiShaderData.m_shaderVariantAlphaTest = dynamicDraw->UseShaderVariant(shaderOptionsAlphaTest); } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiRenderer::BeginUiFrameRender() { -#ifdef LYSHINE_ATOM_TODO - m_renderer = gEnv->pRenderer; - - // we are rendering at the end of the frame, after tone mapping, so we should be writing sRGB values - m_renderer->SetSrgbWrite(true); - #ifndef _RELEASE if (m_debugTextureDataRecordLevel > 0) { m_texturesUsedInFrame.clear(); } #endif -#endif - + // Various platform drivers expect all texture slots used in the shader to be bound BindNullTexture(); } @@ -204,18 +202,10 @@ void UiRenderer::EndUiFrameRender() //////////////////////////////////////////////////////////////////////////////////////////////////// void UiRenderer::BeginCanvasRender() { -#ifdef LYSHINE_ATOM_TODO - m_baseState = GS_NODEPTHTEST; - m_stencilRef = 0; - // Set default starting state - IRenderer* renderer = gEnv->pRenderer; - - renderer->SetCullMode(R_CULL_DISABLE); - renderer->SetColorOp(eCO_MODULATE, eCO_MODULATE, DEF_TEXARG0, DEF_TEXARG0); - renderer->SetState(GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA | GS_NODEPTHTEST); -#endif + // Set base state + m_baseState.ResetToDefault(); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -229,11 +219,13 @@ AZ::RHI::Ptr UiRenderer::GetDynamicDrawContext() return m_dynamicDraw; } +//////////////////////////////////////////////////////////////////////////////////////////////////// const UiRenderer::UiShaderData& UiRenderer::GetUiShaderData() { return m_uiShaderData; } +//////////////////////////////////////////////////////////////////////////////////////////////////// AZ::Matrix4x4 UiRenderer::GetModelViewProjectionMatrix() { auto viewportContext = GetViewportContext(); @@ -253,6 +245,7 @@ AZ::Matrix4x4 UiRenderer::GetModelViewProjectionMatrix() return modelViewProjMat; } +//////////////////////////////////////////////////////////////////////////////////////////////////// AZ::Vector2 UiRenderer::GetViewportSize() { auto viewportContext = GetViewportContext(); @@ -267,17 +260,30 @@ AZ::Vector2 UiRenderer::GetViewportSize() } //////////////////////////////////////////////////////////////////////////////////////////////////// -int UiRenderer::GetBaseState() +UiRenderer::BaseState UiRenderer::GetBaseState() { return m_baseState; } //////////////////////////////////////////////////////////////////////////////////////////////////// -void UiRenderer::SetBaseState(int state) +void UiRenderer::SetBaseState(BaseState state) { m_baseState = state; } +//////////////////////////////////////////////////////////////////////////////////////////////////// +AZ::RPI::ShaderVariantId UiRenderer::GetCurrentShaderVariant() +{ + AZ::RPI::ShaderVariantId variantId = m_uiShaderData.m_shaderVariantDefault; + + if (m_baseState.m_useAlphaTest) + { + variantId = m_uiShaderData.m_shaderVariantAlphaTest; + } + + return variantId; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// uint32 UiRenderer::GetStencilRef() { @@ -354,6 +360,7 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) { if (recordingOption > 0) { +#ifdef LYSHINE_ATOM_TODO // Convert debug to use Atom images // compute the total area of all the textures, also create a vector that we can sort by area AZStd::vector textures; int totalArea = 0; @@ -431,6 +438,7 @@ void UiRenderer::DebugDisplayTextureData(int recordingOption) texture->GetWidth(), texture->GetHeight(), texture->GetDataSize(), texture->GetFormatName(), texture->GetName()); WriteLine(buffer, white); } +#endif } } diff --git a/Gems/LyShine/Code/Source/UiRenderer.h b/Gems/LyShine/Code/Source/UiRenderer.h index 888c88586a..413e8c45cf 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.h +++ b/Gems/LyShine/Code/Source/UiRenderer.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #ifndef _RELEASE @@ -38,6 +39,36 @@ public: // types AZ::RHI::ShaderInputConstantIndex m_isClampInputIndex; AZ::RPI::ShaderVariantId m_shaderVariantDefault; + AZ::RPI::ShaderVariantId m_shaderVariantAlphaTest; + }; + + // Base state + struct BaseState + { + BaseState() + { + ResetToDefault(); + } + + void ResetToDefault() + { + // Enable blend/color write + m_blendState.m_enable = true; + m_blendState.m_writeMask = 0xF; + m_blendState.m_blendSource = AZ::RHI::BlendFactor::AlphaSource; + m_blendState.m_blendDest = AZ::RHI::BlendFactor::AlphaSourceInverse; + m_blendState.m_blendOp = AZ::RHI::BlendOp::Add; + + // Disable stencil + m_stencilState = AZ::RHI::StencilState(); + m_stencilState.m_enable = 0; + + m_useAlphaTest = false; + } + + AZ::RHI::TargetBlendState m_blendState; + AZ::RHI::StencilState m_stencilState; + bool m_useAlphaTest = false; }; public: // member functions @@ -74,10 +105,13 @@ public: // member functions AZ::Vector2 GetViewportSize(); //! Get the current base state - int GetBaseState(); + BaseState GetBaseState(); //! Set the base state - void SetBaseState(int state); + void SetBaseState(BaseState state); + + //! Get the shader variant based on current render properties + AZ::RPI::ShaderVariantId GetCurrentShaderVariant(); //! Get the current stencil test reference value uint32 GetStencilRef(); @@ -126,8 +160,8 @@ protected: // attributes static constexpr char LogName[] = "UiRenderer"; - int m_baseState; - uint32 m_stencilRef; + BaseState m_baseState; + uint32 m_stencilRef = 0; UiShaderData m_uiShaderData; AZ::RHI::Ptr m_dynamicDraw; diff --git a/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleMask.tif b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleMask.tif new file mode 100644 index 0000000000..fd29516609 --- /dev/null +++ b/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/CircleMask.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8474b897fe02f70ed8d0e5c47cae8c816c832a7a5739b8c32317737fd275774f +size 1069752 diff --git a/Gems/PhysX/Code/Editor/ConfigStringLineEditCtrl.cpp b/Gems/PhysX/Code/Editor/ConfigStringLineEditCtrl.cpp index 6aaf45b184..a08d566d79 100644 --- a/Gems/PhysX/Code/Editor/ConfigStringLineEditCtrl.cpp +++ b/Gems/PhysX/Code/Editor/ConfigStringLineEditCtrl.cpp @@ -93,7 +93,7 @@ namespace PhysX } } - void ConfigStringLineEditCtrl::SetForbiddenStrings(const AZStd::unordered_set& forbiddenStrings) + void ConfigStringLineEditCtrl::SetForbiddenStrings(const UniqueStringContainer::StringSet& forbiddenStrings) { m_forbiddenStrings = forbiddenStrings; } @@ -122,7 +122,7 @@ namespace PhysX void ConfigStringLineEditValidator::OnEditStart(AZ::Crc32 stringGroupId , const AZStd::string& stringToEdit - , const AZStd::unordered_set& forbiddenStrings + , const UniqueStringContainer::StringSet& forbiddenStrings , int stringMaxLength , bool removeEditedString) { @@ -219,6 +219,14 @@ namespace PhysX GUI->setMaxLen(maxLen); } } + else if (attrib == AZ::Edit::Attributes::ReadOnly) + { + bool isReadOnly = false; + if (attrValue->Read(isReadOnly)) + { + GUI->setEnabled(!isReadOnly); + } + } else if (attrib == Physics::MaterialConfiguration::s_stringGroup) { AZ::Crc32 uniqueGroup; @@ -229,23 +237,23 @@ namespace PhysX } else if (attrib == Physics::MaterialConfiguration::s_forbiddenStringSet) { - AZStd::unordered_set forbiddenStringsUnorderedSet; + UniqueStringContainer::StringSet forbiddenStringsUnorderedSet; AZStd::set forbiddenStringsSet; AZStd::vector forbiddenStringsVector; - if (attrValue->Read>(forbiddenStringsUnorderedSet)) + if (attrValue->Read(forbiddenStringsUnorderedSet)) { GUI->SetForbiddenStrings(forbiddenStringsUnorderedSet); } else if (attrValue->Read>(forbiddenStringsSet)) { - forbiddenStringsUnorderedSet = AZStd::unordered_set(forbiddenStringsSet.begin() + forbiddenStringsUnorderedSet = UniqueStringContainer::StringSet(forbiddenStringsSet.begin() , forbiddenStringsSet.end()); GUI->SetForbiddenStrings(forbiddenStringsUnorderedSet); } else if (attrValue->Read>(forbiddenStringsVector)) { - forbiddenStringsUnorderedSet = AZStd::unordered_set(forbiddenStringsVector.begin() + forbiddenStringsUnorderedSet = UniqueStringContainer::StringSet(forbiddenStringsVector.begin() , forbiddenStringsVector.end()); GUI->SetForbiddenStrings(forbiddenStringsUnorderedSet); } diff --git a/Gems/PhysX/Code/Editor/ConfigStringLineEditCtrl.h b/Gems/PhysX/Code/Editor/ConfigStringLineEditCtrl.h index e672a9afd8..f9e7b752ec 100644 --- a/Gems/PhysX/Code/Editor/ConfigStringLineEditCtrl.h +++ b/Gems/PhysX/Code/Editor/ConfigStringLineEditCtrl.h @@ -47,7 +47,7 @@ namespace PhysX void OnEditStart(AZ::Crc32 stringGroupId , const AZStd::string& stringToEdit - , const AZStd::unordered_set& forbiddenStrings + , const UniqueStringContainer::StringSet& forbiddenStrings , int stringMaxLength , bool removeEditedString = true); @@ -62,7 +62,7 @@ namespace PhysX private: AZ::Crc32 m_currStringGroup = s_groupStringNotUnique; ///< Group of string field undergoing edit. int m_currStringMaxLen = s_qtLineEditMaxLen; ///< Max length of string field undergoing edit. - AZStd::unordered_set m_forbiddenStrings; ///< Value of string edit widget cannot be any of these strings. + UniqueStringContainer::StringSet m_forbiddenStrings; ///< Value of string edit widget cannot be any of these strings. UniqueStringContainer m_uniqueStringContainer; ///< Collection of groups of unique strings. Serves for validation and fixing of string input. }; @@ -79,7 +79,7 @@ namespace PhysX , ConfigStringLineEditValidator* validator = nullptr); virtual ~ConfigStringLineEditCtrl(); - void SetForbiddenStrings(const AZStd::unordered_set& forbiddenStrings); + void SetForbiddenStrings(const UniqueStringContainer::StringSet& forbiddenStrings); void SetUniqueGroup(AZ::Crc32 uniqueGroup); AZStd::string Value() const; @@ -92,7 +92,7 @@ namespace PhysX protected: void ConnectWidgets() override; - AZStd::unordered_set m_forbiddenStrings; ///< Value of this line edit ctrl cannot be any of these forbidden strings. + UniqueStringContainer::StringSet m_forbiddenStrings; ///< Value of this line edit ctrl cannot be any of these forbidden strings. ConfigStringLineEditValidator* m_pValidator = nullptr; ///< Validator for line edit widget. AZ::Crc32 m_uniqueGroup = ConfigStringLineEditValidator::s_groupStringNotUnique; ///< String group in which line edit value must remain unique. }; diff --git a/Gems/PhysX/Code/Editor/MaterialIdWidget.cpp b/Gems/PhysX/Code/Editor/MaterialIdWidget.cpp index 44787ddd20..30db8aa76d 100644 --- a/Gems/PhysX/Code/Editor/MaterialIdWidget.cpp +++ b/Gems/PhysX/Code/Editor/MaterialIdWidget.cpp @@ -15,12 +15,12 @@ #include +#include + namespace PhysX { namespace Editor { - static const char* const DefaultPhysicsMaterialLabel = ""; - AZ::u32 MaterialIdWidget::GetHandlerName() const { return Physics::Edit::MaterialIdSelector; @@ -74,7 +74,7 @@ namespace PhysX auto lockToDefault = [gui]() { - gui->addItem(DefaultPhysicsMaterialLabel); + gui->addItem(QLatin1String(Physics::DefaultPhysicsMaterialLabel.data(), Physics::DefaultPhysicsMaterialLabel.size())); gui->setCurrentIndex(0); return false; }; @@ -103,7 +103,7 @@ namespace PhysX // Add default physics material first m_libraryIds.push_back(Physics::MaterialId()); - gui->addItem(DefaultPhysicsMaterialLabel); + gui->addItem(QLatin1String(Physics::DefaultPhysicsMaterialLabel.data(), Physics::DefaultPhysicsMaterialLabel.size())); for (const auto& material : materials) { diff --git a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp index b28bf6ab4a..fff13d2f39 100644 --- a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp +++ b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.cpp @@ -134,6 +134,7 @@ namespace PhysX void EditorSystemComponent::Activate() { Physics::EditorWorldBus::Handler::BusConnect(); + AzToolsFramework::EditorContextMenuBus::Handler::BusConnect(); m_onMaterialLibraryLoadErrorEventHandler = AzPhysics::SystemEvents::OnMaterialLibraryLoadErrorEvent::Handler( [this]([[maybe_unused]] AzPhysics::SystemEvents::MaterialLibraryLoadErrorType error) @@ -168,6 +169,7 @@ namespace PhysX { AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect(); AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + AzToolsFramework::EditorContextMenuBus::Handler::BusDisconnect(); Physics::EditorWorldBus::Handler::BusDisconnect(); if (auto* physicsSystem = AZ::Interface::Get()) diff --git a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.h b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.h index 4bd11a951f..8105709244 100644 --- a/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.h +++ b/Gems/PhysX/Code/Editor/Source/Components/EditorSystemComponent.h @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace AzPhysics @@ -32,6 +33,7 @@ namespace PhysX , public Physics::EditorWorldBus::Handler , private AzToolsFramework::EditorEntityContextNotificationBus::Handler , private AzToolsFramework::EditorEvents::Bus::Handler + , private AzToolsFramework::EditorContextMenuBus::Handler { public: AZ_COMPONENT(EditorSystemComponent, "{560F08DC-94F5-4D29-9AD4-CDFB3B57C654}"); @@ -62,8 +64,10 @@ namespace PhysX void OnStartPlayInEditorBegin() override; void OnStopPlayInEditor() override; - // AztoolsFramework::EditorEvents::Bus::Handler + // AztoolsFramework::EditorContextMenuBus::Handler void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override; + + // AztoolsFramework::EditorEvents::Bus::Handler void NotifyRegisterViews() override; AZStd::optional> RetrieveDefaultMaterialLibrary(); diff --git a/Gems/PhysX/Code/Editor/UniqueStringContainer.cpp b/Gems/PhysX/Code/Editor/UniqueStringContainer.cpp index 6482624dc4..1d92471919 100644 --- a/Gems/PhysX/Code/Editor/UniqueStringContainer.cpp +++ b/Gems/PhysX/Code/Editor/UniqueStringContainer.cpp @@ -23,7 +23,7 @@ namespace PhysX StringGroups::iterator stringGroupsIter = m_stringGroups.find(stringGroupId); if (stringGroupsIter == m_stringGroups.end()) { - m_stringGroups.emplace(stringGroupId, AZStd::unordered_set()); + m_stringGroups.emplace(stringGroupId, StringSet()); } m_stringGroups[stringGroupId].insert(stringIn); } @@ -31,7 +31,7 @@ namespace PhysX AZStd::string UniqueStringContainer::GetUniqueString(AZ::Crc32 stringGroupId , const AZStd::string& stringIn , AZ::u64 maxStringLength - , const AZStd::unordered_set& forbiddenStrings) const + , const StringSet& forbiddenStrings) const { StringGroups::const_iterator stringGroupsIter = m_stringGroups.find(stringGroupId); @@ -45,7 +45,7 @@ namespace PhysX } AZStd::string stringOut; - const AZStd::unordered_set& stringGroup = (stringGroupsIter == m_stringGroups.end())? AZStd::unordered_set():stringGroupsIter->second; + const StringSet& stringGroup = (stringGroupsIter == m_stringGroups.end())? StringSet():stringGroupsIter->second; // Attempts to append a post-fix value, e.g. "_1" etc., to the original string so it is unique. // A unique post-fix index can be found by iterating total number of invalid string plus 1. @@ -86,8 +86,8 @@ namespace PhysX return true; } - const AZStd::unordered_set& stringGroup = stringGroupsIter->second; - return stringGroup.find(stringIn) == stringGroup.end(); + const StringSet& stringSet = stringGroupsIter->second; + return stringSet.find(stringIn) == stringSet.end(); } void UniqueStringContainer::RemoveString(AZ::Crc32 stringGroupId diff --git a/Gems/PhysX/Code/Editor/UniqueStringContainer.h b/Gems/PhysX/Code/Editor/UniqueStringContainer.h index 176063f341..7144137f8a 100644 --- a/Gems/PhysX/Code/Editor/UniqueStringContainer.h +++ b/Gems/PhysX/Code/Editor/UniqueStringContainer.h @@ -16,13 +16,38 @@ #include #include #include +#include namespace PhysX { - /// Class that keeps track of unique strings in groups. + /// Class that keeps track of unique strings (case insensitive) in groups. class UniqueStringContainer { public: + struct CaseInsensitiveStringHash + { + AZ_TYPE_INFO(UniqueStringContainer::CaseInsensitiveStringHash, "{EB80F2A1-2DEB-47CC-ABF7-592F492C20A9}"); + + size_t operator()(const AZStd::string& str) const + { + AZStd::string lowerStr = str; + AZStd::to_lower(lowerStr.begin(), lowerStr.end()); + return AZStd::hash{}(lowerStr); + } + }; + + struct CaseInsensitiveStringEqual + { + AZ_TYPE_INFO(UniqueStringContainer::CaseInsensitiveStringEqual, "{6ADEA1D9-27B8-4C7A-913D-EC8191F1B6A9}"); + + bool operator()(const AZStd::string& arg0, const AZStd::string& arg1) const + { + return AZ::StringFunc::Equal(arg0, arg1, false/*bCaseSensitive*/); + } + }; + + using StringSet = AZStd::unordered_set; + /// Add a unique string to a group of unique strings. void AddString(AZ::Crc32 stringGroupId, const AZStd::string& stringIn); @@ -30,7 +55,7 @@ namespace PhysX AZStd::string GetUniqueString(AZ::Crc32 stringGroupId , const AZStd::string& stringIn , AZ::u64 maxStringLength - , const AZStd::unordered_set& forbiddenStrings) const; + , const StringSet& forbiddenStrings) const; /// Checks if a string would be unique in the identified string group. bool IsStringUnique(AZ::Crc32 stringGroupId, const AZStd::string& stringIn) const; @@ -39,7 +64,7 @@ namespace PhysX void RemoveString(AZ::Crc32 stringGroupId, const AZStd::string& stringIn); private: - using StringGroups = AZStd::unordered_map>; + using StringGroups = AZStd::unordered_map; StringGroups m_stringGroups; ///< Collection of groups of unique strings, each group identified by an ID. }; -} +} // namespace PhysX diff --git a/Gems/PhysX/Code/Include/PhysX/MeshAsset.h b/Gems/PhysX/Code/Include/PhysX/MeshAsset.h index 8467c04359..fffa8b43b3 100644 --- a/Gems/PhysX/Code/Include/PhysX/MeshAsset.h +++ b/Gems/PhysX/Code/Include/PhysX/MeshAsset.h @@ -62,8 +62,8 @@ namespace PhysX using ShapeConfigurationList = AZStd::vector; ShapeConfigurationList m_colliderShapes; //!< Shapes data with optional collider configuration override. - AZStd::vector m_surfaceNames; //!< List of all surface names. - AZStd::vector m_materialNames; //!< List of all material names. + AZStd::vector m_materialNames; //!< List of material names of the mesh asset. + AZStd::vector m_physicsMaterialNames; //!< List of physics material names associated with each material. AZStd::vector m_materialIndexPerShape; //!< An index of the material in m_materialNames for each shape. }; @@ -75,6 +75,8 @@ namespace PhysX AZ_CLASS_ALLOCATOR(MeshAsset, AZ::SystemAllocator, 0); AZ_RTTI(MeshAsset, "{7A2871B9-5EAB-4DE0-A901-B0D2C6920DDB}", AZ::Data::AssetData); + static void Reflect(AZ::ReflectContext* context); + MeshAssetData m_assetData; }; } // namespace Pipeline diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index 4454429092..0671e3e77e 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -58,10 +58,12 @@ namespace PhysX { editContext->Class("EditorProxyShapeConfig", "PhysX Base shape collider") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_pxAsset, "PhysX Mesh", "PhysX mesh collider asset") + ->Attribute(AZ_CRC_CE("EditButton"), "") + ->Attribute(AZ_CRC_CE("EditDescription"), "Open in FBX Settings") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_configuration, "Configuration", "Configuration of asset shape") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly); + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly); } } } @@ -356,6 +358,7 @@ namespace PhysX [this](const AZ::Data::AssetId& defaultMaterialLibrary) { m_configuration.m_materialSelection.OnMaterialLibraryChanged(defaultMaterialLibrary); + UpdateMaterialSlotsFromMeshAsset(); AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues); @@ -698,10 +701,10 @@ namespace PhysX AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree); - ValidateMaterialSurfaces(); + ValidateAssetMaterials(); } - void EditorColliderComponent::ValidateMaterialSurfaces() + void EditorColliderComponent::ValidateAssetMaterials() { const AZ::Data::Asset& physicsAsset = m_shapeConfiguration.m_physicsAsset.m_pxAsset; @@ -712,7 +715,7 @@ namespace PhysX // Here we check the material indices assigned to every shape and validate that every index is used at least once. // It's not an error if the validation fails here but something we want to let the designers know about. - [[maybe_unused]] size_t surfacesNum = physicsAsset->m_assetData.m_surfaceNames.size(); + [[maybe_unused]] size_t materialsNum = physicsAsset->m_assetData.m_materialNames.size(); const AZStd::vector& indexPerShape = physicsAsset->m_assetData.m_materialIndexPerShape; AZStd::unordered_set usedIndices; @@ -728,10 +731,10 @@ namespace PhysX usedIndices.insert(index); } - AZ_Warning("PhysX", usedIndices.size() == surfacesNum, - "EditorColliderComponent::ValidateMaterialSurfaces. Entity: %s. Number of surfaces used by the shape (%d) does not match the " - "total number of surfaces in the asset (%d). Please check that there are no convex meshes with per-face materials. Asset: %s", - GetEntity()->GetName().c_str(), usedIndices.size(), surfacesNum, physicsAsset.GetHint().c_str()) + AZ_Warning("PhysX", usedIndices.size() == materialsNum, + "EditorColliderComponent::ValidateMaterialSurfaces. Entity: %s. Number of materials used by the shape (%d) does not match the " + "total number of materials in the asset (%d). Please check that there are no convex meshes with per-face materials. Asset: %s", + GetEntity()->GetName().c_str(), usedIndices.size(), materialsNum, physicsAsset.GetHint().c_str()) } void EditorColliderComponent::OnAssetReady(AZ::Data::Asset asset) diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.h b/Gems/PhysX/Code/Source/EditorColliderComponent.h index 07e1131ea9..603e0bf0b3 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.h @@ -244,7 +244,7 @@ namespace PhysX AZ::Data::AssetId FindMatchingPhysicsAsset(const AZ::Data::Asset& renderMeshAsset, const AZStd::vector& physicsAssets); - void ValidateMaterialSurfaces(); + void ValidateAssetMaterials(); void InitEventHandlers(); DebugDraw::Collider m_colliderDebugDraw; diff --git a/Gems/PhysX/Code/Source/Material.cpp b/Gems/PhysX/Code/Source/Material.cpp index 5e8e3bf759..ada01dad48 100644 --- a/Gems/PhysX/Code/Source/Material.cpp +++ b/Gems/PhysX/Code/Source/Material.cpp @@ -410,7 +410,7 @@ namespace PhysX } // Set the slots from the mesh asset - materialSelection.SetMaterialSlots(meshAsset->m_assetData.m_surfaceNames); + materialSelection.SetMaterialSlots(meshAsset->m_assetData.m_materialNames); if (!assetConfiguration.m_useMaterialsFromAsset) { @@ -419,12 +419,14 @@ namespace PhysX } // Update material IDs in the selection for each slot - const AZStd::vector& meshMaterialNames = meshAsset->m_assetData.m_materialNames; - for (size_t slotIndex = 0; slotIndex < meshMaterialNames.size(); ++slotIndex) + const AZStd::vector& physicsMaterialNames = meshAsset->m_assetData.m_physicsMaterialNames; + for (size_t slotIndex = 0; slotIndex < physicsMaterialNames.size(); ++slotIndex) { - const AZStd::string& physicsMaterialNameFromPhysicsAsset = meshMaterialNames[slotIndex]; - if (physicsMaterialNameFromPhysicsAsset == DefaultPhysicsMaterialNameFromPhysicsAsset) + const AZStd::string& physicsMaterialNameFromPhysicsAsset = physicsMaterialNames[slotIndex]; + if (physicsMaterialNameFromPhysicsAsset.empty() || + physicsMaterialNameFromPhysicsAsset == Physics::DefaultPhysicsMaterialLabel) { + materialSelection.SetMaterialId(Physics::MaterialId(), slotIndex); continue; } @@ -436,9 +438,10 @@ namespace PhysX else { AZ_Warning("PhysX", false, - "UpdateMaterialSelectionFromPhysicsAsset: Physics material '%s' not found in the material library. Mesh surface '%s' will use the default material.", + "UpdateMaterialSelectionFromPhysicsAsset: Physics material '%s' not found in the material library. Mesh material '%s' will use the default physics material.", physicsMaterialNameFromPhysicsAsset.c_str(), - meshAsset->m_assetData.m_surfaceNames[slotIndex].c_str()); + meshAsset->m_assetData.m_materialNames[slotIndex].c_str()); + materialSelection.SetMaterialId(Physics::MaterialId(), slotIndex); } } } @@ -516,7 +519,7 @@ namespace PhysX auto it = AZStd::find_if(m_materials.begin(), m_materials.end(), [&materialName](const auto& data) { - return data.second->GetSurfaceTypeName() == materialName; + return AZ::StringFunc::Equal(data.second->GetSurfaceTypeName(), materialName, false/*bCaseSensitive*/); }); if (it != m_materials.end()) { diff --git a/Gems/PhysX/Code/Source/Material.h b/Gems/PhysX/Code/Source/Material.h index 1541d156d8..694c48ea7c 100644 --- a/Gems/PhysX/Code/Source/Material.h +++ b/Gems/PhysX/Code/Source/Material.h @@ -21,12 +21,6 @@ namespace PhysX { - /// Name used by physx asset exporter to indicate that the default - /// physics material should be used for a mesh surface. The exporter - /// will use it as the fallback option when it's not possible to obtain - /// the surface information from the mesh material. - static const char* const DefaultPhysicsMaterialNameFromPhysicsAsset = ""; - /// PhysX implementation of Physics::Material interface /// =================================================== /// diff --git a/Gems/PhysX/Code/Source/Pipeline/MeshAssetHandler.cpp b/Gems/PhysX/Code/Source/Pipeline/MeshAssetHandler.cpp index 430ef7aef3..d909b31c47 100644 --- a/Gems/PhysX/Code/Source/Pipeline/MeshAssetHandler.cpp +++ b/Gems/PhysX/Code/Source/Pipeline/MeshAssetHandler.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -151,13 +152,34 @@ namespace PhysX serializeContext->Class() ->Field("ColliderShapes", &MeshAssetData::m_colliderShapes) - ->Field("SurfaceNames", &MeshAssetData::m_surfaceNames) - ->Field("MaterialNames", &MeshAssetData::m_materialNames) + ->Field("SurfaceNames", &MeshAssetData::m_materialNames) + ->Field("MaterialNames", &MeshAssetData::m_physicsMaterialNames) ->Field("MaterialIndexPerShape", &MeshAssetData::m_materialIndexPerShape) ; } } + void MeshAsset::Reflect(AZ::ReflectContext* context) + { + MeshAssetData::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Field("MeshAssetData", &MeshAsset::m_assetData) + ; + + // Note: This class needs to have edit context reflection so PropertyAssetCtrl::OnEditButtonClicked + // can open the asset with the preferred asset editor (FBX Settings). + if (auto* editContext = serializeContext->GetEditContext()) + { + editContext->Class("PhysX Mesh Asset", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ; + } + } + } + void AssetColliderConfiguration::Reflect(AZ::ReflectContext* context) { if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) diff --git a/Gems/PhysX/Code/Source/Pipeline/MeshBehavior.cpp b/Gems/PhysX/Code/Source/Pipeline/MeshBehavior.cpp index ce46d72fb4..8762349ad1 100644 --- a/Gems/PhysX/Code/Source/Pipeline/MeshBehavior.cpp +++ b/Gems/PhysX/Code/Source/Pipeline/MeshBehavior.cpp @@ -97,6 +97,10 @@ namespace PhysX nodeSelectionList.AddSelectedNode(graph.GetNodeName(nodeIndex).GetPath()); } } + + // Update list of materials slots after the group's node selection list has been gathered + group->SetSceneGraph(&graph); + group->UpdateMaterialSlots(); } AZ::SceneAPI::Events::ProcessingResult MeshBehavior::UpdateManifest(AZ::SceneAPI::Containers::Scene& scene, ManifestAction action, @@ -129,6 +133,8 @@ namespace PhysX // in the same way again. To guarantee the same uuid, generate a stable one instead. group->OverrideId(AZ::SceneAPI::DataTypes::Utilities::CreateStableUuid(scene, MeshGroup::TYPEINFO_Uuid())); + group->SetSceneGraph(&scene.GetGraph()); + EBUS_EVENT(AZ::SceneAPI::Events::ManifestMetaInfoBus, InitializeObject, scene, *group); scene.GetManifest().AddEntry(AZStd::move(group)); @@ -149,10 +155,15 @@ namespace PhysX } AZ::SceneAPI::Utilities::SceneGraphSelector::UpdateNodeSelection(scene.GetGraph(), group.GetSceneNodeSelectionList()); + + // Update list of materials slots after the group's node selection list has been updated + group.SetSceneGraph(&scene.GetGraph()); + group.UpdateMaterialSlots(); + updated = true; } return updated ? AZ::SceneAPI::Events::ProcessingResult::Success : AZ::SceneAPI::Events::ProcessingResult::Ignored; } - } // namespace SceneAPI -} // namespace AZ + } // namespace Pipeline +} // namespace PhysX diff --git a/Gems/PhysX/Code/Source/Pipeline/MeshExporter.cpp b/Gems/PhysX/Code/Source/Pipeline/MeshExporter.cpp index 24e532e2d1..de632c02b5 100644 --- a/Gems/PhysX/Code/Source/Pipeline/MeshExporter.cpp +++ b/Gems/PhysX/Code/Source/Pipeline/MeshExporter.cpp @@ -68,16 +68,6 @@ namespace PhysX } } pxDefaultErrorCallback; - // A struct to store the asset-wide material names shared by multiple shapes - struct AssetMaterialsData - { - // Material names coming from FBX, these will be Mesh Surfaces in the Collider Component - AZStd::vector m_fbxMaterialNames; - - // Look-up table for fbxMaterialNames - AZStd::unordered_map m_materialIndexByName; - }; - // A struct to store the geometry data per FBX node struct NodeCollisionGeomExportData { @@ -153,7 +143,7 @@ namespace PhysX AZ::SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(4); + serializeContext->Class()->Version(5); } } @@ -182,100 +172,49 @@ namespace PhysX return newIndex; } - // Building a map between FBX material name and the corresponding Cry surface type that is set in the .mtl file. - void BuildMaterialToSurfaceTypeMap(const AZStd::string& materialFilename, - AZStd::unordered_map& materialToSurfaceTypeMap) + bool UpdateAssetPhysicsMaterials( + const AZStd::vector& newMaterials, + AZStd::vector& materials, + AZStd::vector& physicsMaterials) { - AZ::IO::SystemFile mtlFile; - bool fileOpened = mtlFile.Open(materialFilename.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY); - if (fileOpened && mtlFile.Length() != 0) + if (materials.size() != physicsMaterials.size()) { - //Read material override file into a buffer - AZStd::vector buffer(mtlFile.Length()); - mtlFile.Read(mtlFile.Length(), buffer.data()); - mtlFile.Close(); - - //Apparently in rapidxml if 'parse_no_data_nodes' isn't set it creates both value and data nodes - //with the data nodes having precedence such that updating values doesn't work. - AZ::rapidxml::xml_document document; - document.parse(buffer.data()); - - //Parse MTL file for materials and/or submaterials. - AZ::rapidxml::xml_node* rootMaterialNode = document.first_node(AZ::GFxFramework::MaterialExport::g_materialString); - - AZ::rapidxml::xml_node* subMaterialNode = rootMaterialNode->first_node(AZ::GFxFramework::MaterialExport::g_subMaterialString); - - if (subMaterialNode) - { - for (AZ::rapidxml::xml_node* materialNode = subMaterialNode->first_node(AZ::GFxFramework::MaterialExport::g_materialString); - materialNode; - materialNode = materialNode->next_sibling(AZ::GFxFramework::MaterialExport::g_materialString)) - { - AZ::rapidxml::xml_attribute* nameAttribute = materialNode->first_attribute(AZ::GFxFramework::MaterialExport::g_nameString); - if (nameAttribute) - { - AZStd::string materialName = nameAttribute->value(); - AZStd::string surfaceTypeName = DefaultPhysicsMaterialNameFromPhysicsAsset; - - AZ::rapidxml::xml_attribute* surfaceTypeNode = materialNode->first_attribute("SurfaceType"); - if (surfaceTypeNode && surfaceTypeNode->value_size() != 0) - { - surfaceTypeName = surfaceTypeNode->value(); - } - - materialToSurfaceTypeMap[materialName] = surfaceTypeName; - } - else - { - AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "A SubMaterial without Name found in the .mtl file: %s", materialFilename.c_str()); - } - } - } - else - { - AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "No SubMaterial node in the .mtl file: %s", materialFilename.c_str()); - } + AZ_TracePrintf( + AZ::SceneAPI::Utilities::WarningWindow, + "Materials and Physics Materials have different number of elements. %d materials and %d physics materials.", + materials.size(), physicsMaterials.size()); + return false; } - } - - void UpdateAssetMaterialsFromCrySurfaceTypes(const AZStd::vector& fbxMaterialNames, - const AZStd::unordered_map& materialToSurfaceTypeMap, - MeshAssetData& assetData) - { - AZStd::vector& materialNames = assetData.m_materialNames; - AZ_Assert(materialNames.empty(), - "UpdateAssetMaterialsFromCrySurfaceTypes: Mesh Asset Data should not have materials already assigned."); - materialNames.clear(); - materialNames.reserve(fbxMaterialNames.size()); + AZStd::vector newPhysicsMaterials; + newPhysicsMaterials.reserve(newMaterials.size()); - for (const AZStd::string& fbxMaterial : fbxMaterialNames) + // In the new material list, the materials might have changed slots. + // Form the new list of physics materials by looking at the previous list + // and keeping the same physics materials association when found. + for (const auto& newMaterial : newMaterials) { - AZStd::string materialName; + AZStd::string physicsMaterialName = Physics::DefaultPhysicsMaterialLabel; - // Here we assign the actual engine surface type based on the material name - auto materialToSurfaceIt = materialToSurfaceTypeMap.find(fbxMaterial); - if (materialToSurfaceIt != materialToSurfaceTypeMap.end() - && !materialToSurfaceIt->second.empty()) + for (size_t slotId = 0; slotId < materials.size(); ++slotId) { - materialName = materialToSurfaceIt->second; - - // Remove the mat_ prefix since the material library generated from surface types doesn't have it. - if (materialName.find("mat_") == 0) + if (AZ::StringFunc::Equal(materials[slotId], newMaterial, false/*bCaseSensitive*/)) { - materialName = materialName.substr(4); + if (!physicsMaterials[slotId].empty()) + { + physicsMaterialName = physicsMaterials[slotId]; + } + break; } } - else - { - materialName = DefaultPhysicsMaterialNameFromPhysicsAsset; - } - materialNames.emplace_back(AZStd::move(materialName)); + newPhysicsMaterials.emplace_back(AZStd::move(physicsMaterialName)); } - // Asset mesh surfaces match FBX materials. These are the names that users see in the Collider Component in the Editor. - assetData.m_surfaceNames = fbxMaterialNames; + materials = newMaterials; + physicsMaterials = AZStd::move(newPhysicsMaterials); + + return true; } bool ValidateCookedTriangleMesh(void* assetData, AZ::u32 assetDataSize) @@ -320,6 +259,86 @@ namespace PhysX return materialNames; } + + AZStd::optional GatherMaterialsFromMeshGroup( + const MeshGroup& meshGroup, + const AZ::SceneAPI::Containers::SceneGraph& sceneGraph) + { + AssetMaterialsData assetMaterialData; + + const auto& sceneNodeSelectionList = meshGroup.GetSceneNodeSelectionList(); + size_t selectedNodeCount = sceneNodeSelectionList.GetSelectedNodeCount(); + + for (size_t index = 0; index < selectedNodeCount; index++) + { + AZ::SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex = sceneGraph.Find(sceneNodeSelectionList.GetSelectedNode(index)); + if (!nodeIndex.IsValid()) + { + AZ_TracePrintf( + AZ::SceneAPI::Utilities::WarningWindow, + "Node '%s' was not found in the scene graph.", + sceneNodeSelectionList.GetSelectedNode(index).c_str() + ); + continue; + } + auto nodeMesh = azrtti_cast(*sceneGraph.ConvertToStorageIterator(nodeIndex)); + if (!nodeMesh) + { + continue; + } + + AZStd::string_view nodeName = sceneGraph.GetNodeName(nodeIndex).GetName(); + + const AZStd::vector localFbxMaterialsList = GenerateLocalNodeMaterialMap(sceneGraph, nodeIndex); + if (localFbxMaterialsList.empty()) + { + AZ_TracePrintf( + AZ::SceneAPI::Utilities::WarningWindow, + "Node '%.*s' does not have any material assigned to it. Material '%s' will be used.", + nodeName.size(), nodeName, DefaultMaterialName + ); + } + + const AZ::u32 faceCount = nodeMesh->GetFaceCount(); + + assetMaterialData.m_nodesToPerFaceMaterialIndices.emplace(nodeName, AZStd::vector(faceCount)); + + // Convex and primitive methods can only have 1 material + const bool limitToOneMaterial = meshGroup.GetExportAsConvex() || meshGroup.GetExportAsPrimitive(); + + for (AZ::u32 faceIndex = 0; faceIndex < faceCount; ++faceIndex) + { + AZStd::string materialName = DefaultMaterialName; + if (!localFbxMaterialsList.empty()) + { + const int materialId = nodeMesh->GetFaceMaterialId(faceIndex); + if (materialId >= localFbxMaterialsList.size()) + { + AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, + "materialId %d for face %d is out of bound for localFbxMaterialsList (size %d).", + materialId, faceIndex, localFbxMaterialsList.size()); + + return AZStd::nullopt; + } + + materialName = localFbxMaterialsList[materialId]; + + // Keep using the first material when it has to be limited to one. + if (limitToOneMaterial && + assetMaterialData.m_fbxMaterialNames.size() == 1 && + assetMaterialData.m_fbxMaterialNames[0] != materialName) + { + materialName = assetMaterialData.m_fbxMaterialNames[0]; + } + } + + const AZ::u16 materialIndex = InsertMaterialIndexByName(materialName, assetMaterialData); + assetMaterialData.m_nodesToPerFaceMaterialIndices[nodeName][faceIndex] = materialIndex; + } + } + + return assetMaterialData; + } } static physx::PxMeshMidPhase::Enum GetMidPhaseStructureType(const AZStd::string& platformIdentifier) @@ -468,21 +487,11 @@ namespace PhysX return cookingSuccessful; } - // Utility function finding out the .mtl file for a given FBX (at the moment it's the same name as FBX but with .mtl extension) - static AZStd::string GetAssetMaterialFilename(const AZ::SceneAPI::Events::ExportEventContext& context) - { - const AZ::SceneAPI::Containers::Scene& scene = context.GetScene(); - - AZStd::string materialFilename = scene.GetSourceFilename(); - AzFramework::StringFunc::Path::ReplaceExtension(materialFilename, ".mtl"); - return materialFilename; - } - // Processes the collected data and writes into a file static AZ::SceneAPI::Events::ProcessingResult WritePxMeshAsset( AZ::SceneAPI::Events::ExportEventContext& context, const AZStd::vector& totalExportData, - const AssetMaterialsData &assetMaterialsData, + const Utils::AssetMaterialsData &assetMaterialsData, const MeshGroup& meshGroup) { SceneEvents::ProcessingResult result = SceneEvents::ProcessingResult::Ignored; @@ -492,14 +501,18 @@ namespace PhysX MeshAssetData assetData; - const AZStd::string& materialFilename = GetAssetMaterialFilename(context); - - // Read the information about surface type for each material from the .mtl file - AZStd::unordered_map fbxMaterialToCrySurfaceTypeMap; - Utils::BuildMaterialToSurfaceTypeMap(materialFilename, fbxMaterialToCrySurfaceTypeMap); - // Assign the materials into cooked data - Utils::UpdateAssetMaterialsFromCrySurfaceTypes(assetMaterialsData.m_fbxMaterialNames, fbxMaterialToCrySurfaceTypeMap, assetData); + assetData.m_materialNames = meshGroup.GetMaterialSlots(); + assetData.m_physicsMaterialNames = meshGroup.GetPhysicsMaterials(); + + // Updating materials lists from new materials gathered from fbx + // because this exporter runs when the FBX is being processed, which + // could have a different content from when the mesh group info was + // entered in FBX Settings Editor. + if (!Utils::UpdateAssetPhysicsMaterials(assetMaterialsData.m_fbxMaterialNames, assetData.m_materialNames, assetData.m_physicsMaterialNames)) + { + return SceneEvents::ProcessingResult::Failure; + } for (const NodeCollisionGeomExportData& subMesh : totalExportData) { @@ -714,9 +727,15 @@ namespace PhysX for (const MeshGroup& pxMeshGroup : view) { + // Gather material data from asset for the mesh group + AZStd::optional assetMaterialData = Utils::GatherMaterialsFromMeshGroup(pxMeshGroup, graph); + if (!assetMaterialData.has_value()) + { + return SceneEvents::ProcessingResult::Failure; + } + // Export data per node AZStd::vector totalExportData; - AssetMaterialsData assetMaterialData; const AZStd::string& groupName = pxMeshGroup.GetName(); @@ -764,7 +783,6 @@ namespace PhysX const AZ::SceneAPI::Containers::SceneGraph::Name& nodeName = graph.GetNodeName(nodeIndex); - const AZStd::vector localFbxMaterialsList = Utils::GenerateLocalNodeMaterialMap(graph, nodeIndex); const AZ::SceneAPI::DataTypes::MatrixType worldTransform = SceneUtil::BuildWorldTransform(graph, nodeIndex); NodeCollisionGeomExportData nodeExportData; @@ -783,53 +801,24 @@ namespace PhysX } nodeExportData.m_indices.resize(faceCount * 3); - nodeExportData.m_perFaceMaterialIndices.resize(faceCount); - if (localFbxMaterialsList.empty()) + nodeExportData.m_perFaceMaterialIndices = assetMaterialData->m_nodesToPerFaceMaterialIndices[nodeExportData.m_nodeName]; + if (nodeExportData.m_perFaceMaterialIndices.size() != faceCount) { AZ_TracePrintf( AZ::SceneAPI::Utilities::WarningWindow, - "Node '%s' does not have any material assigned to it. Material '%s' will be used.", - nodeExportData.m_nodeName.c_str(), DefaultMaterialName + "Node '%s' material information face count %d does not match the node's %d.", + nodeExportData.m_nodeName.c_str(), nodeExportData.m_perFaceMaterialIndices.size(), faceCount ); + return SceneEvents::ProcessingResult::Failure; } - // Convex and primitive methods can only have 1 material - const bool limitToOneMaterial = pxMeshGroup.GetExportAsConvex() || pxMeshGroup.GetExportAsPrimitive(); - for (AZ::u32 faceIndex = 0; faceIndex < faceCount; ++faceIndex) { - AZStd::string materialName = DefaultMaterialName; - if (!localFbxMaterialsList.empty()) - { - int materialId = nodeMesh->GetFaceMaterialId(faceIndex); - if (materialId >= localFbxMaterialsList.size()) - { - AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, - "materialId %d for face %d is out of bound for localFbxMaterialsList (size %d).", - materialId, faceIndex, localFbxMaterialsList.size()); - - return SceneEvents::ProcessingResult::Failure; - } - - materialName = localFbxMaterialsList[materialId]; - - // Keep using the first material when it has to be limited to one. - if (limitToOneMaterial && - assetMaterialData.m_fbxMaterialNames.size() == 1 && - assetMaterialData.m_fbxMaterialNames[0] != materialName) - { - materialName = assetMaterialData.m_fbxMaterialNames[0]; - } - } - const AZ::SceneAPI::DataTypes::IMeshData::Face& face = nodeMesh->GetFaceInfo(faceIndex); nodeExportData.m_indices[faceIndex * 3] = face.vertexIndex[0]; nodeExportData.m_indices[faceIndex * 3 + 1] = face.vertexIndex[1]; nodeExportData.m_indices[faceIndex * 3 + 2] = face.vertexIndex[2]; - - AZ::u16 materialIndex = Utils::InsertMaterialIndexByName(materialName, assetMaterialData); - nodeExportData.m_perFaceMaterialIndices[faceIndex] = materialIndex; } if (pxMeshGroup.GetDecomposeMeshes()) @@ -880,7 +869,7 @@ namespace PhysX if (!totalExportData.empty()) { - result += WritePxMeshAsset(context, totalExportData, assetMaterialData, pxMeshGroup); + result += WritePxMeshAsset(context, totalExportData, *assetMaterialData, pxMeshGroup); } } diff --git a/Gems/PhysX/Code/Source/Pipeline/MeshExporter.h b/Gems/PhysX/Code/Source/Pipeline/MeshExporter.h index ef0e58cecb..0593b27301 100644 --- a/Gems/PhysX/Code/Source/Pipeline/MeshExporter.h +++ b/Gems/PhysX/Code/Source/Pipeline/MeshExporter.h @@ -26,6 +26,7 @@ namespace AZ namespace Containers { class Scene; + class SceneGraph; } namespace DataTypes @@ -57,5 +58,34 @@ namespace PhysX private: AZ::SceneAPI::Events::ProcessingResult ExportMeshObject(AZ::SceneAPI::Events::ExportEventContext& context, const AZStd::shared_ptr& meshToExport, const AZStd::string& nodePath, const Pipeline::MeshGroup& pxMeshGroup) const; }; - } -} + + namespace Utils + { + //! A struct to store the materials of the mesh nodes selected in a mesh group. + struct AssetMaterialsData + { + //! Material names coming from FBX. + AZStd::vector m_fbxMaterialNames; + + //! Look-up table for fbxMaterialNames. + AZStd::unordered_map m_materialIndexByName; + + //! Map of mesh nodes to their list of material indices associated to each face. + AZStd::unordered_map> m_nodesToPerFaceMaterialIndices; + }; + + //! Returns the list of materials assigned to the triangles + //! of the mesh nodes selected in a mesh group. + AZStd::optional GatherMaterialsFromMeshGroup( + const MeshGroup& meshGroup, + const AZ::SceneAPI::Containers::SceneGraph& sceneGraph); + + //! Function to update a list of materials and physics materials from a new list. + //! All those new materials not found in the previous list will fallback to default physics material. + bool UpdateAssetPhysicsMaterials( + const AZStd::vector& newMaterials, + AZStd::vector& materials, + AZStd::vector& physicsMaterials); + } // namespace Utils + } // namespace Pipeline +} // namespace PhysX diff --git a/Gems/PhysX/Code/Source/Pipeline/MeshGroup.cpp b/Gems/PhysX/Code/Source/Pipeline/MeshGroup.cpp index db6e7e12e0..ffae8c449b 100644 --- a/Gems/PhysX/Code/Source/Pipeline/MeshGroup.cpp +++ b/Gems/PhysX/Code/Source/Pipeline/MeshGroup.cpp @@ -16,12 +16,17 @@ #include #include #include +#include +#include #include #include #include #include +#include #include +#include +#include #include @@ -577,7 +582,21 @@ namespace PhysX MeshGroup::MeshGroup() : m_id(AZ::Uuid::CreateRandom()) + , m_materialLibraryChangedHandler( + [this](const AZ::Data::AssetId& materialLibraryAssetId) + { + OnMaterialLibraryChanged(materialLibraryAssetId); + }) + { + if (auto* physicsSystem = AZ::Interface::Get()) + { + physicsSystem->RegisterOnMaterialLibraryChangedEventHandler(m_materialLibraryChangedHandler); + } + } + + MeshGroup::~MeshGroup() { + m_materialLibraryChangedHandler.Disconnect(); } void MeshGroup::Reflect(AZ::ReflectContext* context) @@ -602,6 +621,8 @@ namespace PhysX ->Field("PrimitiveAssetParams", &MeshGroup::m_primitiveAssetParams) ->Field("DecomposeMeshes", &MeshGroup::m_decomposeMeshes) ->Field("ConvexDecompositionParams", &MeshGroup::m_convexDecompositionParams) + ->Field("MaterialSlots", &MeshGroup::m_materialSlots) + ->Field("PhysicsMaterials", &MeshGroup::m_physicsMaterials) ->Field("rules", &MeshGroup::m_rules); if ( @@ -622,6 +643,7 @@ namespace PhysX "Select the meshes to be included in the mesh group.") ->Attribute("FilterName", "meshes") ->Attribute("FilterType", AZ::SceneAPI::DataTypes::IMeshData::TYPEINFO_Uuid()) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &MeshGroup::OnNodeSelectionChanged) ->DataElement(AZ::Edit::UIHandlers::ComboBox, &MeshGroup::m_exportMethod, "Export As", "The cooking method to be applied to this mesh group. For the asset to be usable as " @@ -629,14 +651,14 @@ namespace PhysX ->EnumAttribute(MeshExportMethod::TriMesh, "Triangle Mesh") ->EnumAttribute(MeshExportMethod::Convex, "Convex") ->EnumAttribute(MeshExportMethod::Primitive, "Primitive") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &MeshGroup::OnExportMethodChanged) ->DataElement(AZ_CRC("DecomposeMeshes", 0xe0e2ac1e), &MeshGroup::m_decomposeMeshes, "Decompose Meshes", "If enables, this option will apply the V-HACD algorithm to split each node " "into approximately convex parts. Each part will individually be exported as a convex " "collider using the parameters configured above.") ->Attribute(AZ::Edit::Attributes::Visibility, &MeshGroup::GetDecomposeMeshesVisibility) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &MeshGroup::OnDecomposeMeshesChanged) ->DataElement(AZ_CRC("TriangleMeshAssetParams", 0x1a408def), &MeshGroup::m_triangleMeshAssetParams, "Triangle Mesh Asset Parameters", "Configure the parameters controlling the exported triangle mesh asset.") @@ -658,6 +680,14 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Visibility, &MeshGroup::GetDecomposeMeshes) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &MeshGroup::m_physicsMaterials, "Physics Materials", + "Configure which physics materials to use for each element.") + ->Attribute(AZ::Edit::Attributes::IndexedChildNameLabelOverride, &MeshGroup::GetMaterialSlotLabel) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false) + ->ElementAttribute(AZ::Edit::UIHandlers::Handler, AZ::Edit::UIHandlers::ComboBox) + ->ElementAttribute(AZ::Edit::Attributes::StringList, &MeshGroup::GetPhysicsMaterialNames) + ->DataElement(AZ::Edit::UIHandlers::Default, &MeshGroup::m_rules, "", "Add or remove rules to fine-tune the export process.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20)); @@ -710,6 +740,37 @@ namespace PhysX return (GetExportAsConvex() || GetExportAsPrimitive()) && m_decomposeMeshes; } + const AZStd::vector& MeshGroup::GetPhysicsMaterials() const + { + return m_physicsMaterials; + } + + const AZStd::vector& MeshGroup::GetMaterialSlots() const + { + return m_materialSlots; + } + + void MeshGroup::SetSceneGraph(const AZ::SceneAPI::Containers::SceneGraph* graph) + { + m_graph = graph; + } + + void MeshGroup::UpdateMaterialSlots() + { + if (!m_graph) + { + return; + } + + AZStd::optional assetMaterialData = Utils::GatherMaterialsFromMeshGroup(*this, *m_graph); + if (!assetMaterialData) + { + return; + } + + Utils::UpdateAssetPhysicsMaterials(assetMaterialData->m_fbxMaterialNames, m_materialSlots, m_physicsMaterials); + } + AZ::SceneAPI::Containers::RuleContainer& MeshGroup::GetRuleContainer() { return m_rules; @@ -770,11 +831,79 @@ namespace PhysX return m_convexDecompositionParams; } + AZ::u32 MeshGroup::OnNodeSelectionChanged() + { + UpdateMaterialSlots(); + return AZ::Edit::PropertyRefreshLevels::EntireTree; + } + + AZ::u32 MeshGroup::OnExportMethodChanged() + { + UpdateMaterialSlots(); + return AZ::Edit::PropertyRefreshLevels::EntireTree; + } + + AZ::u32 MeshGroup::OnDecomposeMeshesChanged() + { + UpdateMaterialSlots(); + return AZ::Edit::PropertyRefreshLevels::EntireTree; + } + bool MeshGroup::GetDecomposeMeshesVisibility() const { return GetExportAsConvex() || GetExportAsPrimitive(); } + AZStd::string MeshGroup::GetMaterialSlotLabel(int index) const + { + if (index < m_materialSlots.size()) + { + // When limited to one material, clarify in the label the material + // will be used for the entire object. + if (index == 0 && (GetExportAsConvex() || GetExportAsPrimitive())) + { + return m_materialSlots[index] + " (entire object)"; + } + else + { + return m_materialSlots[index]; + } + } + else + { + return ""; + } + } + + AZStd::vector MeshGroup::GetPhysicsMaterialNames() const + { + if (auto* physicsSystem = AZ::Interface::Get()) + { + if (const auto* physicsConfiguration = physicsSystem->GetConfiguration()) + { + const auto& materials = physicsConfiguration->m_materialLibraryAsset->GetMaterialsData(); + + AZStd::vector physicsMaterialNames; + physicsMaterialNames.reserve(materials.size() + 1); + + physicsMaterialNames.emplace_back(Physics::DefaultPhysicsMaterialLabel); + for (const auto& material : materials) + { + physicsMaterialNames.emplace_back(material.m_configuration.m_surfaceType); + } + + return physicsMaterialNames; + } + } + return {Physics::DefaultPhysicsMaterialLabel}; + } + + void MeshGroup::OnMaterialLibraryChanged([[maybe_unused]] const AZ::Data::AssetId& materialLibraryAssetId) + { + AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, + AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues); + } + bool MeshGroup::VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) { // Remove the material rule. diff --git a/Gems/PhysX/Code/Source/Pipeline/MeshGroup.h b/Gems/PhysX/Code/Source/Pipeline/MeshGroup.h index 0095a3dce2..5b70ded7ea 100644 --- a/Gems/PhysX/Code/Source/Pipeline/MeshGroup.h +++ b/Gems/PhysX/Code/Source/Pipeline/MeshGroup.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,11 @@ namespace AZ { class ReflectContext; + + namespace SceneAPI::Containers + { + class SceneGraph; + } } namespace PhysX @@ -172,7 +178,7 @@ namespace PhysX AZ_CLASS_ALLOCATOR_DECL MeshGroup(); - ~MeshGroup() override = default; + ~MeshGroup() override; static void Reflect(AZ::ReflectContext* context); @@ -185,6 +191,11 @@ namespace PhysX bool GetExportAsTriMesh() const; bool GetExportAsPrimitive() const; bool GetDecomposeMeshes() const; + const AZStd::vector& GetPhysicsMaterials() const; + const AZStd::vector& GetMaterialSlots() const; + + void SetSceneGraph(const AZ::SceneAPI::Containers::SceneGraph* graph); + void UpdateMaterialSlots(); AZ::SceneAPI::Containers::RuleContainer& GetRuleContainer() override; const AZ::SceneAPI::Containers::RuleContainer& GetRuleContainerConst() const override; @@ -207,8 +218,17 @@ namespace PhysX protected: static bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); + AZ::u32 OnNodeSelectionChanged(); + AZ::u32 OnExportMethodChanged(); + AZ::u32 OnDecomposeMeshesChanged(); + bool GetDecomposeMeshesVisibility() const; + AZStd::string GetMaterialSlotLabel(int index) const; + AZStd::vector GetPhysicsMaterialNames() const; + + void OnMaterialLibraryChanged(const AZ::Data::AssetId& materialLibraryAssetId); + AZ::Uuid m_id{}; AZStd::string m_name{}; AZ::SceneAPI::SceneData::SceneNodeSelectionList m_nodeSelectionList{}; @@ -219,6 +239,12 @@ namespace PhysX PrimitiveAssetParams m_primitiveAssetParams{}; ConvexDecompositionParams m_convexDecompositionParams{}; AZ::SceneAPI::Containers::RuleContainer m_rules{}; + AZStd::vector m_materialSlots; + AZStd::vector m_physicsMaterials; + + const AZ::SceneAPI::Containers::SceneGraph* m_graph = nullptr; + + AzPhysics::SystemEvents::OnMaterialLibraryChangedEvent::Handler m_materialLibraryChangedHandler; }; } } diff --git a/Gems/PhysX/Code/Source/SystemComponent.cpp b/Gems/PhysX/Code/Source/SystemComponent.cpp index b4c17f672d..eae56967b2 100644 --- a/Gems/PhysX/Code/Source/SystemComponent.cpp +++ b/Gems/PhysX/Code/Source/SystemComponent.cpp @@ -94,7 +94,7 @@ namespace PhysX void SystemComponent::Reflect(AZ::ReflectContext* context) { D6JointLimitConfiguration::Reflect(context); - Pipeline::MeshAssetData::Reflect(context); + Pipeline::MeshAsset::Reflect(context); PhysX::ReflectionUtils::ReflectPhysXOnlyApi(context); diff --git a/Gems/PhysX/Code/Tests/PhysXSceneQueryTests.cpp b/Gems/PhysX/Code/Tests/PhysXSceneQueryTests.cpp index 313565caf2..90a2774ca4 100644 --- a/Gems/PhysX/Code/Tests/PhysXSceneQueryTests.cpp +++ b/Gems/PhysX/Code/Tests/PhysXSceneQueryTests.cpp @@ -172,7 +172,7 @@ namespace PhysX ASSERT_EQ(hit.m_material, rigidBody->GetShape(0).get()->GetMaterial().get()); const AZStd::string& typeName = hit.m_material->GetSurfaceTypeName(); - ASSERT_EQ(typeName, AZStd::string("Default")); + ASSERT_EQ(typeName, Physics::DefaultPhysicsMaterialLabel); } TEST_F(PhysXSceneQueryFixture, RayCast_AgainstStaticObject_ReturnsHit) diff --git a/Gems/ScriptCanvas/Code/Editor/SystemComponent.cpp b/Gems/ScriptCanvas/Code/Editor/SystemComponent.cpp index 60e8e5f23a..1ba9a64083 100644 --- a/Gems/ScriptCanvas/Code/Editor/SystemComponent.cpp +++ b/Gems/ScriptCanvas/Code/Editor/SystemComponent.cpp @@ -66,6 +66,7 @@ namespace ScriptCanvasEditor SystemComponent::~SystemComponent() { AzToolsFramework::UnregisterViewPane(LyViewPane::ScriptCanvas); + AzToolsFramework::EditorContextMenuBus::Handler::BusDisconnect(); AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); AzToolsFramework::AssetSeedManagerRequests::Bus::Handler::BusDisconnect(); @@ -119,6 +120,7 @@ namespace ScriptCanvasEditor void SystemComponent::Init() { AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + AzToolsFramework::EditorContextMenuBus::Handler::BusConnect(); } void SystemComponent::Activate() @@ -165,6 +167,8 @@ namespace ScriptCanvasEditor options.canHaveMultipleInstances = false; options.isPreview = true; options.showInMenu = true; + options.showOnToolsToolbar = true; + options.toolbarIcon = ":/Menu/script_canvas_editor.svg"; AzToolsFramework::RegisterViewPane(LyViewPane::ScriptCanvas, LyViewPane::CategoryTools, options); } diff --git a/Gems/ScriptCanvas/Code/Editor/SystemComponent.h b/Gems/ScriptCanvas/Code/Editor/SystemComponent.h index f2ebad38cd..ced5a67a4c 100644 --- a/Gems/ScriptCanvas/Code/Editor/SystemComponent.h +++ b/Gems/ScriptCanvas/Code/Editor/SystemComponent.h @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -42,6 +43,7 @@ namespace ScriptCanvasEditor , private AZ::Data::AssetBus::MultiHandler , private AZ::Interface::Registrar , private AzToolsFramework::AssetSeedManagerRequests::Bus::Handler + , private AzToolsFramework::EditorContextMenuBus::Handler { public: AZ_COMPONENT(SystemComponent, "{1DE7A120-4371-4009-82B5-8140CB1D7B31}"); @@ -71,8 +73,12 @@ namespace ScriptCanvasEditor //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// - // AztoolsFramework::EditorEvents::Bus::Handler... + // AztoolsFramework::EditorContextMenuBus::Handler... void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override; + //////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + // AztoolsFramework::EditorEvents::Bus::Handler... void NotifyRegisterViews() override; //////////////////////////////////////////////////////////////////////// diff --git a/Gems/ScriptCanvasPhysics/Code/Source/WorldNodes.h b/Gems/ScriptCanvasPhysics/Code/Source/WorldNodes.h index 5238c52e0e..14f03aa7c4 100644 --- a/Gems/ScriptCanvasPhysics/Code/Source/WorldNodes.h +++ b/Gems/ScriptCanvasPhysics/Code/Source/WorldNodes.h @@ -165,6 +165,7 @@ namespace ScriptCanvasPhysics request.m_start = worldSpaceTransform.GetTranslation(); request.m_direction = worldSpaceTransform.TransformVector(direction.GetNormalized()); request.m_distance = distance; + request.m_reportMultipleHits = true; request.m_filterCallback = [ignore](const AzPhysics::SimulatedBody* body, [[maybe_unused]] const Physics::Shape* shape) { return body->GetEntityId() != ignore ? AzPhysics::SceneQuery::QueryHitType::Touch : AzPhysics::SceneQuery::QueryHitType::None; diff --git a/README.md b/README.md index 0c59837a62..7a9751529f 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ If you have the Git credential manager core or other credential helpers installe * Game Development with C++ * MSVC v142 - VS 2019 C++ x64/x86 * C++ 2019 redistributable update -* CMake 3.19.1 minimum: [https://cmake.org/files/LatestRelease/cmake-3.19.1-win64-x64.msi](https://cmake.org/files/LatestRelease/cmake-3.19.1-win64-x64.msi) +* CMake 3.20 minimum: [https://cmake.org/download/](https://cmake.org/download/) #### Optional @@ -105,7 +105,7 @@ If you have the Git credential manager core or other credential helpers installe 1. Install the following redistributables to the following: - Visual Studio and VC++ redistributable can be installed to any location - - CMake can be installed to any location, as long as it's available in the system path, otherwise it can be installed to: `<3rdParty Path>\CMake\3.19.1` + - CMake can be installed to any location, as long as it's available in the system path - WWise can be installed anywhere, but you will need to set an environment variable for CMake to detect it: `set LY_WWISE_INSTALL_PATH=` 1. Navigate into the repo folder, then download the python runtime with this command diff --git a/Templates/DefaultGem/Template/Code/Include/${Name}/${Name}Bus.h b/Templates/DefaultGem/Template/Code/Include/${Name}/${Name}Bus.h index 0e8ab29cc4..32f34e309e 100644 --- a/Templates/DefaultGem/Template/Code/Include/${Name}/${Name}Bus.h +++ b/Templates/DefaultGem/Template/Code/Include/${Name}/${Name}Bus.h @@ -15,22 +15,30 @@ #pragma once #include +#include -namespace ${Name} +namespace ${SanitizedCppName} { - class ${Name}Requests + 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 const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; ////////////////////////////////////////////////////////////////////////// - - // Put your public methods here }; - using ${Name}RequestBus = AZ::EBus<${Name}Requests>; + using ${SanitizedCppName}RequestBus = AZ::EBus<${SanitizedCppName}Requests, ${SanitizedCppName}BusTraits>; + using ${SanitizedCppName}Interface = AZ::Interface<${SanitizedCppName}Requests>; -} // namespace ${Name} +} // namespace ${SanitizedCppName} diff --git a/Templates/DefaultGem/Template/Code/Source/${Name}EditorSystemComponent.cpp b/Templates/DefaultGem/Template/Code/Source/${Name}EditorSystemComponent.cpp index 5884489904..fedc37da6b 100644 --- a/Templates/DefaultGem/Template/Code/Source/${Name}EditorSystemComponent.cpp +++ b/Templates/DefaultGem/Template/Code/Source/${Name}EditorSystemComponent.cpp @@ -15,24 +15,40 @@ #include #include <${Name}EditorSystemComponent.h> -namespace ${Name} +namespace ${SanitizedCppName} { - void ${Name}EditorSystemComponent::Reflect(AZ::ReflectContext* context) + void ${SanitizedCppName}EditorSystemComponent::Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context)) { - serializeContext->Class<${Name}EditorSystemComponent, AZ::Component>()->Version(1); + serializeContext->Class<${SanitizedCppName}EditorSystemComponent, AZ::Component>()->Version(1); } } - void ${Name}EditorSystemComponent::Activate() + ${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::Activate() { AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); } - void ${Name}EditorSystemComponent::Deactivate() + void ${SanitizedCppName}EditorSystemComponent::Deactivate() { AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); } -} // namespace ${Name} +} // namespace ${SanitizedCppName} diff --git a/Templates/DefaultGem/Template/Code/Source/${Name}EditorSystemComponent.h b/Templates/DefaultGem/Template/Code/Source/${Name}EditorSystemComponent.h index 1f973651ee..ef7b0a2f2e 100644 --- a/Templates/DefaultGem/Template/Code/Source/${Name}EditorSystemComponent.h +++ b/Templates/DefaultGem/Template/Code/Source/${Name}EditorSystemComponent.h @@ -18,32 +18,33 @@ #include -namespace ${Name} +namespace ${SanitizedCppName} { - /// System component for ${Name} editor - class ${Name}EditorSystemComponent + /// System component for ${SanitizedCppName} editor + class ${SanitizedCppName}EditorSystemComponent : public AZ::Component , private AzToolsFramework::EditorEvents::Bus::Handler { public: - AZ_COMPONENT(${Name}EditorSystemComponent, "${EditorSysCompClassId}"); + AZ_COMPONENT(${SanitizedCppName}EditorSystemComponent, "${EditorSysCompClassId}"); static void Reflect(AZ::ReflectContext* context); - ${Name}EditorSystemComponent() = default; + ${SanitizedCppName}EditorSystemComponent(); + ~${SanitizedCppName}EditorSystemComponent(); private: static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { - provided.push_back(AZ_CRC("${Name}EditorService")); + provided.push_back(AZ_CRC("${SanitizedCppName}EditorService")); } static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) { - required.push_back(AZ_CRC("${Name}Service")); + required.push_back(AZ_CRC("${SanitizedCppName}Service")); } // AZ::Component void Activate() override; void Deactivate() override; }; -} // namespace ${Name} +} // namespace ${SanitizedCppName} diff --git a/Templates/DefaultGem/Template/Code/Source/${Name}Module.cpp b/Templates/DefaultGem/Template/Code/Source/${Name}Module.cpp index 3c6be88158..ca4a724f49 100644 --- a/Templates/DefaultGem/Template/Code/Source/${Name}Module.cpp +++ b/Templates/DefaultGem/Template/Code/Source/${Name}Module.cpp @@ -17,21 +17,21 @@ #include <${Name}SystemComponent.h> -namespace ${Name} +namespace ${SanitizedCppName} { - class ${Name}Module + class ${SanitizedCppName}Module : public AZ::Module { public: - AZ_RTTI(${Name}Module, "${ModuleClassId}", AZ::Module); - AZ_CLASS_ALLOCATOR(${Name}Module, AZ::SystemAllocator, 0); + AZ_RTTI(${SanitizedCppName}Module, "${ModuleClassId}", AZ::Module); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}Module, AZ::SystemAllocator, 0); - ${Name}Module() + ${SanitizedCppName}Module() : AZ::Module() { // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. m_descriptors.insert(m_descriptors.end(), { - ${Name}SystemComponent::CreateDescriptor(), + ${SanitizedCppName}SystemComponent::CreateDescriptor(), }); } @@ -41,10 +41,10 @@ namespace ${Name} AZ::ComponentTypeList GetRequiredSystemComponents() const override { return AZ::ComponentTypeList { - azrtti_typeid<${Name}SystemComponent>(), + azrtti_typeid<${SanitizedCppName}SystemComponent>(), }; } }; -}// namespace ${Name} +}// namespace ${SanitizedCppName} -AZ_DECLARE_MODULE_CLASS(Gem_${Name}, ${Name}::${Name}Module) +AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}Module) diff --git a/Templates/DefaultGem/Template/Code/Source/${Name}SystemComponent.cpp b/Templates/DefaultGem/Template/Code/Source/${Name}SystemComponent.cpp index 7f91197e4a..8f7eabf74e 100644 --- a/Templates/DefaultGem/Template/Code/Source/${Name}SystemComponent.cpp +++ b/Templates/DefaultGem/Template/Code/Source/${Name}SystemComponent.cpp @@ -18,19 +18,19 @@ #include #include -namespace ${Name} +namespace ${SanitizedCppName} { - void ${Name}SystemComponent::Reflect(AZ::ReflectContext* context) + void ${SanitizedCppName}SystemComponent::Reflect(AZ::ReflectContext* context) { if (AZ::SerializeContext* serialize = azrtti_cast(context)) { - serialize->Class<${Name}SystemComponent, AZ::Component>() + serialize->Class<${SanitizedCppName}SystemComponent, AZ::Component>() ->Version(0) ; if (AZ::EditContext* ec = serialize->GetEditContext()) { - ec->Class<${Name}SystemComponent>("${Name}", "[Description of functionality provided by this System Component]") + ec->Class<${SanitizedCppName}SystemComponent>("${SanitizedCppName}", "[Description of functionality provided by this System Component]") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System")) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) @@ -39,45 +39,61 @@ namespace ${Name} } } - void ${Name}SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + void ${SanitizedCppName}SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { - provided.push_back(AZ_CRC("${Name}Service")); + provided.push_back(AZ_CRC("${SanitizedCppName}Service")); } - void ${Name}SystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + void ${SanitizedCppName}SystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { - incompatible.push_back(AZ_CRC("${Name}Service")); + incompatible.push_back(AZ_CRC("${SanitizedCppName}Service")); } - void ${Name}SystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + void ${SanitizedCppName}SystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) { AZ_UNUSED(required); } - void ${Name}SystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + void ${SanitizedCppName}SystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) { AZ_UNUSED(dependent); } - void ${Name}SystemComponent::Init() + ${SanitizedCppName}SystemComponent::${SanitizedCppName}SystemComponent() + { + if (${SanitizedCppName}Interface::Get() == nullptr) + { + ${SanitizedCppName}Interface::Register(this); + } + } + + ${SanitizedCppName}SystemComponent::~${SanitizedCppName}SystemComponent() + { + if (${SanitizedCppName}Interface::Get() == this) + { + ${SanitizedCppName}Interface::Unregister(this); + } + } + + void ${SanitizedCppName}SystemComponent::Init() { } - void ${Name}SystemComponent::Activate() + void ${SanitizedCppName}SystemComponent::Activate() { - ${Name}RequestBus::Handler::BusConnect(); + ${SanitizedCppName}RequestBus::Handler::BusConnect(); AZ::TickBus::Handler::BusConnect(); } - void ${Name}SystemComponent::Deactivate() + void ${SanitizedCppName}SystemComponent::Deactivate() { AZ::TickBus::Handler::BusDisconnect(); - ${Name}RequestBus::Handler::BusDisconnect(); + ${SanitizedCppName}RequestBus::Handler::BusDisconnect(); } - void ${Name}SystemComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) + void ${SanitizedCppName}SystemComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) { } -} // namespace ${Name} +} // namespace ${SanitizedCppName} diff --git a/Templates/DefaultGem/Template/Code/Source/${Name}SystemComponent.h b/Templates/DefaultGem/Template/Code/Source/${Name}SystemComponent.h index 5071e7b9c7..8e91650c65 100644 --- a/Templates/DefaultGem/Template/Code/Source/${Name}SystemComponent.h +++ b/Templates/DefaultGem/Template/Code/Source/${Name}SystemComponent.h @@ -18,15 +18,15 @@ #include #include <${Name}/${Name}Bus.h> -namespace ${Name} +namespace ${SanitizedCppName} { - class ${Name}SystemComponent + class ${SanitizedCppName}SystemComponent : public AZ::Component - , protected ${Name}RequestBus::Handler + , protected ${SanitizedCppName}RequestBus::Handler , public AZ::TickBus::Handler { public: - AZ_COMPONENT(${Name}SystemComponent, "${SysCompClassId}"); + AZ_COMPONENT(${SanitizedCppName}SystemComponent, "${SysCompClassId}"); static void Reflect(AZ::ReflectContext* context); @@ -35,9 +35,12 @@ namespace ${Name} static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + ${SanitizedCppName}SystemComponent(); + ~${SanitizedCppName}SystemComponent(); + protected: //////////////////////////////////////////////////////////////////////// - // ${Name}RequestBus interface implementation + // ${SanitizedCppName}RequestBus interface implementation //////////////////////////////////////////////////////////////////////// @@ -54,4 +57,4 @@ namespace ${Name} //////////////////////////////////////////////////////////////////////// }; -} // namespace ${Name} +} // namespace ${SanitizedCppName} diff --git a/Templates/DefaultGem/Template/Code/Tests/${Name}EditorTest.cpp b/Templates/DefaultGem/Template/Code/Tests/${Name}EditorTest.cpp index 7fd3fa7cfa..9b36a3aadb 100644 --- a/Templates/DefaultGem/Template/Code/Tests/${Name}EditorTest.cpp +++ b/Templates/DefaultGem/Template/Code/Tests/${Name}EditorTest.cpp @@ -14,7 +14,7 @@ #include -class ${Name}EditorTest +class ${SanitizedCppName}EditorTest : public ::testing::Test { protected: @@ -29,7 +29,7 @@ protected: } }; -TEST_F(${Name}EditorTest, SanityTest) +TEST_F(${SanitizedCppName}EditorTest, SanityTest) { ASSERT_TRUE(true); } diff --git a/Templates/DefaultGem/Template/Code/Tests/${Name}Test.cpp b/Templates/DefaultGem/Template/Code/Tests/${Name}Test.cpp index 8021ca920e..bee38fa9d7 100644 --- a/Templates/DefaultGem/Template/Code/Tests/${Name}Test.cpp +++ b/Templates/DefaultGem/Template/Code/Tests/${Name}Test.cpp @@ -14,7 +14,7 @@ #include -class ${Name}Test +class ${SanitizedCppName}Test : public ::testing::Test { protected: @@ -29,7 +29,7 @@ protected: } }; -TEST_F(${Name}Test, SanityTest) +TEST_F(${SanitizedCppName}Test, SanityTest) { ASSERT_TRUE(true); } diff --git a/Templates/DefaultProject/Template/CMakeLists.txt b/Templates/DefaultProject/Template/CMakeLists.txt index 50e3a528e6..4dcfc5325b 100644 --- a/Templates/DefaultProject/Template/CMakeLists.txt +++ b/Templates/DefaultProject/Template/CMakeLists.txt @@ -12,7 +12,7 @@ # {END_LICENSE} if(NOT PROJECT_NAME) - cmake_minimum_required(VERSION 3.19) + cmake_minimum_required(VERSION 3.20) project(${Name} LANGUAGES C CXX VERSION 1.0.0.0 diff --git a/Templates/DefaultProject/Template/Code/Include/${Name}/${Name}Bus.h b/Templates/DefaultProject/Template/Code/Include/${Name}/${Name}Bus.h index faa0e8acf6..05e434ec03 100644 --- a/Templates/DefaultProject/Template/Code/Include/${Name}/${Name}Bus.h +++ b/Templates/DefaultProject/Template/Code/Include/${Name}/${Name}Bus.h @@ -15,20 +15,30 @@ #pragma once #include +#include -namespace ${Name} +namespace ${SanitizedCppName} { - class ${Name}Requests + 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 const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; ////////////////////////////////////////////////////////////////////////// - - // Put your public methods here }; - using ${Name}RequestBus = AZ::EBus<${Name}Requests>; -} // namespace ${Name} + + using ${SanitizedCppName}RequestBus = AZ::EBus<${SanitizedCppName}Requests, ${SanitizedCppName}BusTraits>; + using ${SanitizedCppName}Interface = AZ::Interface<${SanitizedCppName}Requests>; + +} // namespace ${SanitizedCppName} diff --git a/Templates/DefaultProject/Template/Code/Source/${Name}Module.cpp b/Templates/DefaultProject/Template/Code/Source/${Name}Module.cpp index 4f11828366..57b473b317 100644 --- a/Templates/DefaultProject/Template/Code/Source/${Name}Module.cpp +++ b/Templates/DefaultProject/Template/Code/Source/${Name}Module.cpp @@ -17,21 +17,21 @@ #include "${Name}SystemComponent.h" -namespace ${Name} +namespace ${SanitizedCppName} { - class ${Name}Module + class ${SanitizedCppName}Module : public AZ::Module { public: - AZ_RTTI(${Name}Module, "${ModuleClassId}", AZ::Module); - AZ_CLASS_ALLOCATOR(${Name}Module, AZ::SystemAllocator, 0); + AZ_RTTI(${SanitizedCppName}Module, "${ModuleClassId}", AZ::Module); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}Module, AZ::SystemAllocator, 0); - ${Name}Module() + ${SanitizedCppName}Module() : AZ::Module() { // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. m_descriptors.insert(m_descriptors.end(), { - ${Name}SystemComponent::CreateDescriptor(), + ${SanitizedCppName}SystemComponent::CreateDescriptor(), }); } @@ -41,10 +41,10 @@ namespace ${Name} AZ::ComponentTypeList GetRequiredSystemComponents() const override { return AZ::ComponentTypeList{ - azrtti_typeid<${Name}SystemComponent>(), + azrtti_typeid<${SanitizedCppName}SystemComponent>(), }; } }; -}// namespace ${Name} +}// namespace ${SanitizedCppName} -AZ_DECLARE_MODULE_CLASS(Gem_${Name}, ${Name}::${Name}Module) +AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}Module) diff --git a/Templates/DefaultProject/Template/Code/Source/${Name}SystemComponent.cpp b/Templates/DefaultProject/Template/Code/Source/${Name}SystemComponent.cpp index 1e505f743c..ed0a47d6db 100644 --- a/Templates/DefaultProject/Template/Code/Source/${Name}SystemComponent.cpp +++ b/Templates/DefaultProject/Template/Code/Source/${Name}SystemComponent.cpp @@ -18,19 +18,19 @@ #include "${Name}SystemComponent.h" -namespace ${Name} +namespace ${SanitizedCppName} { - void ${Name}SystemComponent::Reflect(AZ::ReflectContext* context) + void ${SanitizedCppName}SystemComponent::Reflect(AZ::ReflectContext* context) { if (AZ::SerializeContext* serialize = azrtti_cast(context)) { - serialize->Class<${Name}SystemComponent, AZ::Component>() + serialize->Class<${SanitizedCppName}SystemComponent, AZ::Component>() ->Version(0) ; if (AZ::EditContext* ec = serialize->GetEditContext()) { - ec->Class<${Name}SystemComponent>("${Name}", "[Description of functionality provided by this System Component]") + ec->Class<${SanitizedCppName}SystemComponent>("${SanitizedCppName}", "[Description of functionality provided by this System Component]") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System")) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) @@ -39,37 +39,53 @@ namespace ${Name} } } - void ${Name}SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + void ${SanitizedCppName}SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { - provided.push_back(AZ_CRC("${Name}Service")); + provided.push_back(AZ_CRC("${SanitizedCppName}Service")); } - void ${Name}SystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + void ${SanitizedCppName}SystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { - incompatible.push_back(AZ_CRC("${Name}Service")); + incompatible.push_back(AZ_CRC("${SanitizedCppName}Service")); } - void ${Name}SystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + void ${SanitizedCppName}SystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) { AZ_UNUSED(required); } - void ${Name}SystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + void ${SanitizedCppName}SystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) { AZ_UNUSED(dependent); } + + ${SanitizedCppName}SystemComponent::${SanitizedCppName}SystemComponent() + { + if (${SanitizedCppName}Interface::Get() == nullptr) + { + ${SanitizedCppName}Interface::Register(this); + } + } + + ${SanitizedCppName}SystemComponent::~${SanitizedCppName}SystemComponent() + { + if (${SanitizedCppName}Interface::Get() == this) + { + ${SanitizedCppName}Interface::Unregister(this); + } + } - void ${Name}SystemComponent::Init() + void ${SanitizedCppName}SystemComponent::Init() { } - void ${Name}SystemComponent::Activate() + void ${SanitizedCppName}SystemComponent::Activate() { - ${Name}RequestBus::Handler::BusConnect(); + ${SanitizedCppName}RequestBus::Handler::BusConnect(); } - void ${Name}SystemComponent::Deactivate() + void ${SanitizedCppName}SystemComponent::Deactivate() { - ${Name}RequestBus::Handler::BusDisconnect(); + ${SanitizedCppName}RequestBus::Handler::BusDisconnect(); } } diff --git a/Templates/DefaultProject/Template/Code/Source/${Name}SystemComponent.h b/Templates/DefaultProject/Template/Code/Source/${Name}SystemComponent.h index 39ccbda549..10a500aba2 100644 --- a/Templates/DefaultProject/Template/Code/Source/${Name}SystemComponent.h +++ b/Templates/DefaultProject/Template/Code/Source/${Name}SystemComponent.h @@ -18,14 +18,14 @@ #include <${Name}/${Name}Bus.h> -namespace ${Name} +namespace ${SanitizedCppName} { - class ${Name}SystemComponent + class ${SanitizedCppName}SystemComponent : public AZ::Component - , protected ${Name}RequestBus::Handler + , protected ${SanitizedCppName}RequestBus::Handler { public: - AZ_COMPONENT(${Name}SystemComponent, "${SysCompClassId}"); + AZ_COMPONENT(${SanitizedCppName}SystemComponent, "${SysCompClassId}"); static void Reflect(AZ::ReflectContext* context); @@ -34,9 +34,12 @@ namespace ${Name} static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + ${SanitizedCppName}SystemComponent(); + ~${SanitizedCppName}SystemComponent(); + protected: //////////////////////////////////////////////////////////////////////// - // ${Name}RequestBus interface implementation + // ${SanitizedCppName}RequestBus interface implementation //////////////////////////////////////////////////////////////////////// diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index a579e3128d..933d64149b 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -374,7 +374,7 @@ function(ly_setup_cmake_install) string(APPEND builtinpackages "ly_associate_package(PACKAGE_NAME ${package_name} TARGETS ${targets} PACKAGE_HASH ${package_hash})\n") endforeach() - ly_get_absolute_pal_filename(pal_builtin_file ${CMAKE_CURRENT_BINARY_DIR}/cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}/BuiltInPackages_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + set(pal_builtin_file ${CMAKE_CURRENT_BINARY_DIR}/cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}/BuiltInPackages_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) file(GENERATE OUTPUT ${pal_builtin_file} CONTENT ${builtinpackages} ) diff --git a/cmake/Tools/Platform/Android/android_deployment.py b/cmake/Tools/Platform/Android/android_deployment.py index eb8361ec18..82d14a2746 100755 --- a/cmake/Tools/Platform/Android/android_deployment.py +++ b/cmake/Tools/Platform/Android/android_deployment.py @@ -184,11 +184,15 @@ class AndroidDeployment(object): call_arguments.extend(arg_list) - output = subprocess.check_output(call_arguments, - shell=True, - stderr=subprocess.DEVNULL).decode(common.DEFAULT_TEXT_READ_ENCODING, - common.ENCODING_ERROR_HANDLINGS) - return output + try: + output = subprocess.check_output(call_arguments, + shell=True, + stderr=subprocess.PIPE).decode(common.DEFAULT_TEXT_READ_ENCODING, + common.ENCODING_ERROR_HANDLINGS) + return output + except subprocess.CalledProcessError as err: + raise common.LmbrCmdError(err.stderr.decode(common.DEFAULT_TEXT_READ_ENCODING, + common.ENCODING_ERROR_HANDLINGS)) def adb_shell(self, command, device_id): """ diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index d25b62dde8..5a52ac385e 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -48,7 +48,7 @@ def verify_gradle(override_gradle_path=None): CMAKE_ARGUMENT_NAME = '--cmake-install-path' -CMAKE_MIN_VERSION = LooseVersion('3.19.0') +CMAKE_MIN_VERSION = LooseVersion('3.20.0') CMAKE_VERSION_REGEX = re.compile(r'cmake version (\d+.\d+.?\d*)') CMAKE_EXECUTABLE = 'cmake' diff --git a/cmake/Tools/common.py b/cmake/Tools/common.py index 1990041c86..cf4631c227 100755 --- a/cmake/Tools/common.py +++ b/cmake/Tools/common.py @@ -274,6 +274,8 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too :return: Tuple of the resolved tool version and the resolved override tool path if provided """ + tool_source = tool_name + try: # Use either the provided gradle override or the gradle in the path environment if override_tool_path: @@ -306,18 +308,17 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too tool_desc = f"{tool_name} path provided in the command line argument '{argument_name}={override_tool_path}' " else: resolved_override_tool_path = None - tool_source = tool_name tool_desc = f"installed {tool_name} in the system path" # Extract the version and verify version_output = subprocess.check_output([tool_source, tool_version_argument], - shell=True).decode(DEFAULT_TEXT_READ_ENCODING, - ENCODING_ERROR_HANDLINGS) + shell=True, + stderr=subprocess.PIPE).decode(DEFAULT_TEXT_READ_ENCODING, + ENCODING_ERROR_HANDLINGS) version_match = tool_version_regex.search(version_output) if not version_match: raise RuntimeError() - # Since we are doing a compare, strip out any non-numeric and non . character from the version otherwise we will get a TypeError on the LooseVersion comparison result_version_str = re.sub(r"[^\.0-9]", "", str(version_match.group(1)).strip()) result_version = LooseVersion(result_version_str) @@ -331,7 +332,14 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too return result_version, resolved_override_tool_path - except (CalledProcessError, WindowsError, RuntimeError) as e: + except CalledProcessError as e: + error_msg = e.output.decode(DEFAULT_TEXT_READ_ENCODING, + ENCODING_ERROR_HANDLINGS) + raise LmbrCmdError(f"{tool_name} cannot be resolved or there was a problem determining its version number. " + f"Either make sure its in the system path environment or a valid path is passed in " + f"through the {argument_name} argument.\n{error_msg}", + ERROR_CODE_ERROR_NOT_SUPPORTED) + except (WindowsError, RuntimeError) as e: logging.error(f"Call to '{tool_source}' resulted in error: {e}") raise LmbrCmdError(f"{tool_name} cannot be resolved or there was a problem determining its version number. " f"Either make sure its in the system path environment or a valid path is passed in " diff --git a/python/get_python.bat b/python/get_python.bat index e11c4ab92f..b3c3bf9cfb 100644 --- a/python/get_python.bat +++ b/python/get_python.bat @@ -32,18 +32,15 @@ IF !ERRORLEVEL!==0 ( cd /D %CMD_DIR%\.. REM IF you update this logic, update it in scripts/build/Platform/Windows/env_windows.cmd -REM If cmake is not found on path, try a known location, using LY_CMAKE_PATH as the first fallback +REM If cmake is not found on path, try a known location at LY_CMAKE_PATH where /Q cmake IF NOT !ERRORLEVEL!==0 ( IF "%LY_CMAKE_PATH%"=="" ( - IF "%LY_3RDPARTY_PATH%"=="" ( - ECHO ERROR: CMake was not found on the PATH and LY_3RDPARTY_PATH is not defined. - ECHO Please ensure CMake is on the path or set LY_3RDPARTY_PATH or LY_CMAKE_PATH. - EXIT /b 1 - ) - SET LY_CMAKE_PATH=!LY_3RDPARTY_PATH!\CMake\3.19.1\Windows\bin - echo CMake was not found on the path, will use known location: !LY_CMAKE_PATH! + ECHO ERROR: CMake was not found on the PATH and LY_CMAKE_PATH is not defined. + ECHO Please ensure CMake is on the path or set LY_CMAKE_PATH. + EXIT /b 1 ) + PATH !LY_CMAKE_PATH!;!PATH! where /Q cmake if NOT !ERRORLEVEL!==0 ( diff --git a/python/get_python.cmake b/python/get_python.cmake index 5d6bf6fd96..198d3f0ba3 100644 --- a/python/get_python.cmake +++ b/python/get_python.cmake @@ -16,7 +16,7 @@ # example: # cmake -DPAL_PLATFORM_NAME:string=Windows -DLY_3RDPARTY_PATH:string=%CMD_DIR% -P get_python.cmake -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) if(LY_3RDPARTY_PATH) file(TO_CMAKE_PATH ${LY_3RDPARTY_PATH} LY_3RDPARTY_PATH) diff --git a/python/get_python.sh b/python/get_python.sh index 780f1bbdd8..9b774d91fc 100755 --- a/python/get_python.sh +++ b/python/get_python.sh @@ -42,31 +42,21 @@ then CMAKE_FOLDER_RELATIVE_TO_ROOT=CMake.app/Contents/bin else PAL=Linux - CMAKE_FOLDER_RELATIVE_TO_ROOT=bin + CMAKE_FOLDER_RELATIVE_TO_ROOT=bin fi if ! [ -x "$(command -v cmake)" ]; then - # Note that LY_3RDPARTY_PATH is only required here if you have no cmake in your PATH. if [ -z ${LY_CMAKE_PATH} ]; then - if [ -z ${LY_3RDPARTY_PATH} ]; then - echo "ERROR: Could not find cmake on the PATH and LY_3RDPARTY_PATH is not defined, cannot continue." - echo "Please add cmake to your PATH, or define $LY_3RDPARTY_PATH" - exit 1 - fi - LY_CMAKE_PATH=$LY_3RDPARTY_PATH/CMake/3.19.1/$PAL/$CMAKE_FOLDER_RELATIVE_TO_ROOT - # if you change the version number, change it also in: - # scripts/build/Platform/Mac/env_mac.sh - # and - # scripts/build/Platform/Linux/env_linux.sh + echo "ERROR: Could not find cmake on the PATH and LY_CMAKE_PATH is not defined, cannot continue." + echo "Please add cmake to your PATH, or define LY_CMAKE_PATH" + exit 1 fi - + export PATH=$LY_CMAKE_PATH:$PATH if ! [ -x "$(command -v cmake)" ]; then - echo "ERROR: Could not find cmake on the PATH or at the known location: $CMAKE_KNOWN_LOCATION" + echo "ERROR: Could not find cmake on the PATH or at the known location: $LY_CMAKE_PATH" echo "Please add cmake to the environment PATH or place it at the above known location." exit 1 - else - echo "CMake not found on path, but was found in the known 3rd Party location." fi fi diff --git a/scripts/build/Platform/Android/build_and_run_unit_tests.cmd b/scripts/build/Platform/Android/build_and_run_unit_tests.cmd index fbb31f95ec..5a51621f40 100644 --- a/scripts/build/Platform/Android/build_and_run_unit_tests.cmd +++ b/scripts/build/Platform/Android/build_and_run_unit_tests.cmd @@ -20,9 +20,6 @@ IF NOT EXIST "%LY_3RDPARTY_PATH%" ( GOTO :error ) -IF NOT EXIST "%LY_ANDROID_SDK%" ( - SET LY_ANDROID_SDK=!LY_3RDPARTY_PATH!/android-sdk/platform-29 -) IF NOT EXIST "%LY_ANDROID_SDK%" ( ECHO [ci_build] FAIL: LY_ANDROID_SDK=!LY_ANDROID_SDK! GOTO :error diff --git a/scripts/build/Platform/Linux/env_linux.sh b/scripts/build/Platform/Linux/env_linux.sh index 1d7324778c..85bdf0a745 100755 --- a/scripts/build/Platform/Linux/env_linux.sh +++ b/scripts/build/Platform/Linux/env_linux.sh @@ -13,27 +13,11 @@ set -o errexit # exit on the first failure encountered if ! command -v cmake &> /dev/null; then - if [[ -z $LY_CMAKE_PATH ]]; then LY_CMAKE_PATH=${LY_3RDPARTY_PATH}/CMake/3.19.1/Linux/bin; fi - if [[ ! -d $LY_CMAKE_PATH ]]; then - echo "[ci_build] CMake path not found" - exit 1 - fi - PATH=${LY_CMAKE_PATH}:${PATH} - if ! command -v cmake &> /dev/null; then - echo "[ci_build] CMake not found" - exit 1 - fi + echo "[ci_build] CMake not found" + exit 1 fi if ! command -v ninja &> /dev/null; then - if [[ -z $LY_NINJA_PATH ]]; then LY_NINJA_PATH=${LY_3RDPARTY_PATH}/ninja/1.10.1/Linux; fi - if [[ ! -d $LY_NINJA_PATH ]]; then - echo "[ci_build] Ninja path not found" - exit 1 - fi - PATH=${LY_NINJA_PATH}:${PATH} - if ! command -v ninja &> /dev/null; then - echo "[ci_build] Ninja not found" - exit 1 - fi + echo "[ci_build] Ninja not found" + exit 1 fi diff --git a/scripts/build/Platform/Mac/env_mac.sh b/scripts/build/Platform/Mac/env_mac.sh index 8f38d1d90b..917329b6af 100755 --- a/scripts/build/Platform/Mac/env_mac.sh +++ b/scripts/build/Platform/Mac/env_mac.sh @@ -13,14 +13,6 @@ set -o errexit # exit on the first failure encountered if ! command -v cmake &> /dev/null; then - if [[ -z $LY_CMAKE_PATH ]]; then LY_CMAKE_PATH=${LY_3RDPARTY_PATH}/CMake/3.19.1/Mac/CMake.app/Contents/bin; fi - if [[ ! -d $LY_CMAKE_PATH ]]; then - echo "[ci_build] CMake path not found" - exit 1 - fi - PATH=${LY_CMAKE_PATH}:${PATH} - if ! command -v cmake &> /dev/null; then - echo "[ci_build] CMake not found" - exit 1 - fi + echo "[ci_build] CMake not found" + exit 1 fi diff --git a/scripts/build/Platform/Windows/build_ninja_windows.cmd b/scripts/build/Platform/Windows/build_ninja_windows.cmd index b782cc2357..e1a802e254 100644 --- a/scripts/build/Platform/Windows/build_ninja_windows.cmd +++ b/scripts/build/Platform/Windows/build_ninja_windows.cmd @@ -12,19 +12,10 @@ REM SETLOCAL EnableDelayedExpansion -IF NOT EXIST "%LY_NINJA_PATH%" ( - SET LY_NINJA_PATH=%LY_3RDPARTY_PATH%/ninja/1.10.1/Windows -) -IF NOT EXIST "%LY_NINJA_PATH%" ( - ECHO [ci_build] FAIL: LY_NINJA_PATH=%LY_NINJA_PATH% - GOTO :error -) -PATH %LY_NINJA_PATH%;%PATH% - CALL "%~dp0build_windows.cmd" IF NOT %ERRORLEVEL%==0 GOTO :error EXIT /b 0 :error -EXIT /b 1 \ No newline at end of file +EXIT /b 1 diff --git a/scripts/build/Platform/Windows/env_windows.cmd b/scripts/build/Platform/Windows/env_windows.cmd index 592b2867cd..3ef161c2ef 100644 --- a/scripts/build/Platform/Windows/env_windows.cmd +++ b/scripts/build/Platform/Windows/env_windows.cmd @@ -12,17 +12,8 @@ REM where /Q cmake IF NOT %ERRORLEVEL%==0 ( - IF "%LY_CMAKE_PATH%"=="" (SET LY_CMAKE_PATH=%LY_3RDPARTY_PATH%/CMake/3.19.1/Windows/bin) - IF NOT EXIST !LY_CMAKE_PATH! ( - ECHO [ci_build] CMake path not found - GOTO :error - ) - PATH !LY_CMAKE_PATH!;!PATH! - where /Q cmake - IF NOT !ERRORLEVEL!==0 ( - ECHO [ci_build] CMake not found - GOTO :error - ) + ECHO [ci_build] CMake not found + GOTO :error ) EXIT /b 0 diff --git a/scripts/o3de/o3de/engine_template.py b/scripts/o3de/o3de/engine_template.py index 9bb62eff35..06e34cc449 100755 --- a/scripts/o3de/o3de/engine_template.py +++ b/scripts/o3de/o3de/engine_template.py @@ -1232,10 +1232,12 @@ def create_from_template(destination_path: str, with_this = replace.pop(0) replacements.append((replace_this, with_this)) + sanitized_cpp_name = utils.sanitize_identifier_for_cpp(destination_name) # dst name is Name replacements.append(("${Name}", destination_name)) replacements.append(("${NameUpper}", destination_name.upper())) replacements.append(("${NameLower}", destination_name.lower())) + replacements.append(("${SanitizedCppName}", sanitized_cpp_name)) if _instantiate_template(template_json_data, destination_name, @@ -1536,10 +1538,12 @@ def create_project(project_path: str, with_this = replace.pop(0) replacements.append((replace_this, with_this)) + sanitized_cpp_name = utils.sanitize_identifier_for_cpp(project_name) # project name replacements.append(("${Name}", project_name)) replacements.append(("${NameUpper}", project_name.upper())) replacements.append(("${NameLower}", project_name.lower())) + replacements.append(("${SanitizedCppName}", sanitized_cpp_name)) # module id is a uuid with { and - if module_id: @@ -1890,6 +1894,10 @@ def create_gem(gem_path: str, # gem name is now the last component of the gem_path gem_name = os.path.basename(gem_path) + if not utils.validate_identifier(gem_name): + logger.error(f'Gem name must be fewer than 64 characters, contain only alphanumeric, "_" or "-" characters, and start with a letter. {gem_name}') + return 1 + # gem name cannot be the same as a restricted platform name if gem_name in restricted_platforms: logger.error(f'Gem path cannot be a restricted name. {gem_name}') @@ -1927,10 +1935,13 @@ def create_gem(gem_path: str, with_this = replace.pop(0) replacements.append((replace_this, with_this)) + sanitized_cpp_name = utils.sanitize_identifier_for_cpp(gem_name) # gem name replacements.append(("${Name}", gem_name)) replacements.append(("${NameUpper}", gem_name.upper())) replacements.append(("${NameLower}", gem_name.lower())) + replacements.append(("${SanitizedCppName}", sanitized_cpp_name)) + # module id is a uuid with { and - if module_id: diff --git a/scripts/o3de/o3de/manifest.py b/scripts/o3de/o3de/manifest.py index 074e7f5a9e..8255946cfb 100644 --- a/scripts/o3de/o3de/manifest.py +++ b/scripts/o3de/o3de/manifest.py @@ -401,13 +401,6 @@ def get_templates_for_generic_creation(): # temporary until we have a better wa return list(filter(filter_project_and_gem_templates_out, get_all_templates())) -def get_all_restricted() -> list: - engine_restricted = get_engine_restricted() - restricted_data = get_restricted() - restricted_data.extend(engine_restricted) - return restricted_data - - def find_engine_data(json_data: dict, engine_path: str or pathlib.Path = None) -> dict or None: if not engine_path: diff --git a/scripts/o3de/o3de/utils.py b/scripts/o3de/o3de/utils.py index 4330de25b8..11c668a37b 100755 --- a/scripts/o3de/o3de/utils.py +++ b/scripts/o3de/o3de/utils.py @@ -36,6 +36,23 @@ def validate_identifier(identifier: str) -> bool: return True +def sanitize_identifier_for_cpp(identifier: str) -> str: + """ + Convert the provided identifier to a valid C++ identifier + :param identifier: the name which needs to to sanitized + :return: str: sanitized identifier + """ + if not identifier: + return '' + + sanitized_identifier = list(identifier) + for index, character in enumerate(sanitized_identifier): + if not (character.isalnum() or character == '_'): + sanitized_identifier[index] = '_' + + return "".join(sanitized_identifier) + + def validate_uuid4(uuid_string: str) -> bool: """ Determine if the uuid supplied is valid.