diff --git a/Code/Editor/Platform/Mac/gui_info.plist b/Code/Editor/Platform/Mac/gui_info.plist
index 0ec7b59e98..5b5f94e977 100644
--- a/Code/Editor/Platform/Mac/gui_info.plist
+++ b/Code/Editor/Platform/Mac/gui_info.plist
@@ -5,7 +5,7 @@
CFBundleExecutable
Editor
CFBundleIdentifier
- com.Amazon.Lumberyard.Editor
+ org.O3DE.Editor
CFBundlePackageType
APPL
CFBundleSignature
diff --git a/Code/Editor/Plugins/EditorCommon/CMakeLists.txt b/Code/Editor/Plugins/EditorCommon/CMakeLists.txt
index 96d9cce5b7..048f77cd0c 100644
--- a/Code/Editor/Plugins/EditorCommon/CMakeLists.txt
+++ b/Code/Editor/Plugins/EditorCommon/CMakeLists.txt
@@ -46,4 +46,8 @@ ly_add_target(
AZ::AzCore
AZ::AzToolsFramework
AZ::AzQtComponents
+ RUNTIME_DEPENDENCIES
+ AZ::AzCore
+ AZ::AzToolsFramework
+ AZ::AzQtComponents
)
diff --git a/Code/Framework/AzCore/AzCore/Script/ScriptContextDebug.cpp b/Code/Framework/AzCore/AzCore/Script/ScriptContextDebug.cpp
index b68b19ddda..0444c4ce2a 100644
--- a/Code/Framework/AzCore/AzCore/Script/ScriptContextDebug.cpp
+++ b/Code/Framework/AzCore/AzCore/Script/ScriptContextDebug.cpp
@@ -157,8 +157,11 @@ ScriptContextDebug::EnumRegisteredClasses(EnumClass enumClass, EnumMethod enumMe
lua_pop(l, 2); // pop the Class name and behaviorClass
lua_pushnil(l);
+
+ // iterate over the key/value pairs
while (lua_next(l, -2) != 0)
{
+ // if key: string value: function
if (lua_isstring(l, -2) && lua_isfunction(l, -1))
{
const char* name = lua_tostring(l, -2);
@@ -167,6 +170,7 @@ ScriptContextDebug::EnumRegisteredClasses(EnumClass enumClass, EnumMethod enumMe
bool isRead = true;
bool isWrite = true;
+ // check if there is a getter provided
lua_getupvalue(l, -1, 1);
if (lua_isnil(l, -1))
{
@@ -174,6 +178,7 @@ ScriptContextDebug::EnumRegisteredClasses(EnumClass enumClass, EnumMethod enumMe
}
lua_pop(l, 1);
+ // check if there is a setter provided
lua_getupvalue(l, -1, 2);
if (lua_isnil(l, -1))
{
@@ -181,6 +186,7 @@ ScriptContextDebug::EnumRegisteredClasses(EnumClass enumClass, EnumMethod enumMe
}
lua_pop(l, 1);
+ // enumerate the remaining property
if (!enumProperty(&behaviorClass->m_typeId, name, isRead, isWrite, userData))
{
lua_pop(l, 5);
@@ -189,21 +195,30 @@ ScriptContextDebug::EnumRegisteredClasses(EnumClass enumClass, EnumMethod enumMe
}
else
{
+ // for any non-built in methods
if (strncmp(name, "__", 2) != 0)
{
const char* dbgParamInfo = NULL;
- lua_getupvalue(l, -1, 2);
+
+ // attempt to get the name
+ bool popDebugName = lua_getupvalue(l, -1, 2) != nullptr;
if (lua_isstring(l, -1))
{
dbgParamInfo = lua_tostring(l, -1);
}
+ // enumerate the method's parameters
if (!enumMethod(&behaviorClass->m_typeId, name, dbgParamInfo, userData))
{
lua_pop(l, 6);
return;
}
- lua_pop(l, 1); // pop the DBG name
+
+ // if we were able to get the name, pop it from the stack
+ if (popDebugName)
+ {
+ lua_pop(l, 1);
+ }
}
}
}
diff --git a/Code/Framework/AzFramework/AzFramework/Input/Buses/Requests/InputDeviceRequestBus.h b/Code/Framework/AzFramework/AzFramework/Input/Buses/Requests/InputDeviceRequestBus.h
index a1ec4a05f0..a122fe3133 100644
--- a/Code/Framework/AzFramework/AzFramework/Input/Buses/Requests/InputDeviceRequestBus.h
+++ b/Code/Framework/AzFramework/AzFramework/Input/Buses/Requests/InputDeviceRequestBus.h
@@ -205,6 +205,25 @@ namespace AzFramework
class InputDeviceImplementationRequest : public AZ::EBusTraits
{
public:
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ //! EBus Trait: requests can be addressed to a specific InputDeviceId so that they are only
+ //! handled by one input device that has connected to the bus using that unique id, or they
+ //! can be broadcast to all input devices that have connected to the bus, regardless of id.
+ //! Connected input devices are ordered by their local player index from lowest to highest.
+ static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ByIdAndOrdered;
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ //! EBus Trait: requests should be handled by only one input device connected to each id
+ static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ //! EBus Trait: requests can be addressed to a specific InputDeviceId
+ using BusIdType = InputDeviceId;
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ //! EBus Trait: requests are handled by connected devices in the order of local player index
+ using BusIdOrderCompare = AZStd::less;
+
////////////////////////////////////////////////////////////////////////////////////////////
//! Alias for the EBus implementation of this interface
using Bus = AZ::EBus>;
@@ -214,11 +233,12 @@ namespace AzFramework
using CreateFunctionType = typename InputDeviceType::Implementation*(*)(InputDeviceType&);
////////////////////////////////////////////////////////////////////////////////////////////
- //! Create a custom implementation for all the existing instances of this input device type.
+ //! Set a custom implementation for this input device type, either for a specific instance
+ //! by addressing the call to an InputDeviceId, or for all existing instances by broadcast.
//! Passing InputDeviceType::Implementation::Create as the argument will create the default
//! device implementation, while passing nullptr will delete any existing implementation.
//! \param[in] createFunction Pointer to the function that will create the implementation.
- virtual void CreateCustomImplementation(CreateFunctionType createFunction) = 0;
+ virtual void SetCustomImplementation(CreateFunctionType createFunction) = 0;
};
////////////////////////////////////////////////////////////////////////////////////////////////
@@ -238,7 +258,7 @@ namespace AzFramework
AZ_INLINE InputDeviceImplementationRequestHandler(InputDeviceType& inputDevice)
: m_inputDevice(inputDevice)
{
- InputDeviceImplementationRequest::Bus::Handler::BusConnect();
+ InputDeviceImplementationRequest::Bus::Handler::BusConnect(m_inputDevice.GetInputDeviceId());
}
////////////////////////////////////////////////////////////////////////////////////////////
@@ -251,8 +271,8 @@ namespace AzFramework
using CreateFunctionType = typename InputDeviceType::Implementation*(*)(InputDeviceType&);
////////////////////////////////////////////////////////////////////////////////////////////
- //! \ref InputDeviceImplementationRequest::CreateCustomImplementation
- AZ_INLINE void CreateCustomImplementation(CreateFunctionType createFunction) override
+ //! \ref InputDeviceImplementationRequest::SetCustomImplementation
+ AZ_INLINE void SetCustomImplementation(CreateFunctionType createFunction) override
{
AZStd::unique_ptr newImplementation;
if (createFunction)
diff --git a/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorBase.h b/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorBase.h
deleted file mode 100644
index 90163dd363..0000000000
--- a/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorBase.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
-
-#pragma once
-
-namespace AZ
-{
- class BehaviorContext;
- class EditContext;
- class SerializeContext;
-} // namespace AZ
-
-// this just provides a convenient template to avoid the necessary boilerplate when you derive from AWSScriptBehaviorBase
-#define AWS_SCRIPT_BEHAVIOR_DEFINITION(className, guidString) \
- AZ_TYPE_INFO(className, guidString) \
- AZ_CLASS_ALLOCATOR(className, AZ::SystemAllocator, 0) \
- void ReflectSerialization(AZ::SerializeContext* serializeContext) override; \
- void ReflectBehaviors(AZ::BehaviorContext* behaviorContext) override; \
- void ReflectEditParameters(AZ::EditContext* editContext) override; \
- className(); \
-
-namespace AWSCore
-{
- //! An interface for AWS ScriptCanvas Behaviors to inherit from
- class AWSScriptBehaviorBase
- {
- public:
- virtual ~AWSScriptBehaviorBase() = default;
-
- virtual void ReflectSerialization(AZ::SerializeContext* reflectContext) = 0;
- virtual void ReflectBehaviors(AZ::BehaviorContext* behaviorContext) = 0;
- virtual void ReflectEditParameters(AZ::EditContext* editContext) = 0;
-
- virtual void Init() {}
- virtual void Activate() {}
- virtual void Deactivate() {}
- };
-} // namespace AWSCore
diff --git a/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorDynamoDB.h b/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorDynamoDB.h
index 317d16dbe4..7244f57f6a 100644
--- a/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorDynamoDB.h
+++ b/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorDynamoDB.h
@@ -10,8 +10,6 @@
#include
#include
-#include
-
namespace AWSCore
{
using DynamoDBAttributeValueMap = AZStd::unordered_map;
@@ -55,10 +53,14 @@ namespace AWSCore
};
class AWSScriptBehaviorDynamoDB
- : public AWSScriptBehaviorBase
{
public:
- AWS_SCRIPT_BEHAVIOR_DEFINITION(AWSScriptBehaviorDynamoDB, "{569E74F6-1268-4199-9653-A3B603FC9F4F}");
+ AZ_RTTI(AWSScriptBehaviorDynamoDB, "{569E74F6-1268-4199-9653-A3B603FC9F4F}");
+
+ AWSScriptBehaviorDynamoDB() = default;
+ virtual ~AWSScriptBehaviorDynamoDB() = default;
+
+ static void Reflect(AZ::ReflectContext* context);
static void GetItem(const AZStd::string& tableResourceKey, const DynamoDBAttributeValueMap& keyMap);
static void GetItemRaw(const AZStd::string& table, const DynamoDBAttributeValueMap& keyMap, const AZStd::string& region);
diff --git a/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorLambda.h b/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorLambda.h
index a8a807e7fb..64601d8e59 100644
--- a/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorLambda.h
+++ b/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorLambda.h
@@ -10,8 +10,6 @@
#include
#include
-#include
-
namespace AWSCore
{
//! AWS Script Behavior notifications for ScriptCanvas behaviors that interact with AWS Lambda
@@ -53,10 +51,14 @@ namespace AWSCore
};
class AWSScriptBehaviorLambda
- : public AWSScriptBehaviorBase
{
public:
- AWS_SCRIPT_BEHAVIOR_DEFINITION(AWSScriptBehaviorLambda, "{9E71534D-34B3-4723-B180-2552513DDA3D}");
+ AZ_RTTI(AWSScriptBehaviorLambda, "{9E71534D-34B3-4723-B180-2552513DDA3D}");
+
+ AWSScriptBehaviorLambda() = default;
+ virtual ~AWSScriptBehaviorLambda() = default;
+
+ static void Reflect(AZ::ReflectContext* context);
static void Invoke(const AZStd::string& functionResourceKey, const AZStd::string& payload);
static void InvokeRaw(const AZStd::string& functionName, const AZStd::string& payload, const AZStd::string& region);
diff --git a/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorS3.h b/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorS3.h
index a75c3bce31..ac2df1c822 100644
--- a/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorS3.h
+++ b/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorS3.h
@@ -10,8 +10,6 @@
#include
#include
-#include
-
namespace AWSCore
{
//! AWS Script Behavior notifications for ScriptCanvas behaviors that interact with AWS S3
@@ -68,7 +66,6 @@ namespace AWSCore
};
class AWSScriptBehaviorS3
- : public AWSScriptBehaviorBase
{
static constexpr const char AWSScriptBehaviorS3Name[] = "AWSScriptBehaviorS3";
static constexpr const char OutputFileIsEmptyErrorMessage[] = "Request validation failed, output file is empty.";
@@ -81,7 +78,12 @@ namespace AWSCore
static constexpr const char RegionNameIsEmptyErrorMessage[] = "Request validation failed, region name is empty.";
public:
- AWS_SCRIPT_BEHAVIOR_DEFINITION(AWSScriptBehaviorS3, "{7F4E956C-7463-4236-B320-C992D36A9C6E}");
+ AZ_RTTI(AWSScriptBehaviorS3, "{7F4E956C-7463-4236-B320-C992D36A9C6E}");
+
+ AWSScriptBehaviorS3() = default;
+ virtual ~AWSScriptBehaviorS3() = default;
+
+ static void Reflect(AZ::ReflectContext* context);
static void GetObject(const AZStd::string& bucketResourceKey, const AZStd::string& objectKey, const AZStd::string& outFile);
static void GetObjectRaw(const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region, const AZStd::string& outFile);
diff --git a/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorsComponent.h b/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorsComponent.h
index 8958425615..43bd15d726 100644
--- a/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorsComponent.h
+++ b/Gems/AWSCore/Code/Include/Public/ScriptCanvas/AWSScriptBehaviorsComponent.h
@@ -10,7 +10,6 @@
#include
#include
#include
-#include
namespace AWSCore
{
@@ -30,23 +29,8 @@ namespace AWSCore
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
- static bool AddedBehaviours()
- {
- return m_alreadyAddedBehaviors;
- }
-
- protected:
-
- ////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
- void Init() override;
void Activate() override;
void Deactivate() override;
- ////////////////////////////////////////////////////////////////////////
-
- static void AddBehaviors(); // Add any behaviors you derived from AWSScriptBehaviorBase to the implementation of this function
-
- static AZStd::vector> m_behaviors;
- static bool m_alreadyAddedBehaviors;
};
} // namespace AWSCore
diff --git a/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorDynamoDB.cpp b/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorDynamoDB.cpp
index c25df555fc..41c1aff34d 100644
--- a/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorDynamoDB.cpp
+++ b/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorDynamoDB.cpp
@@ -20,39 +20,30 @@
namespace AWSCore
{
- AWSScriptBehaviorDynamoDB::AWSScriptBehaviorDynamoDB()
+ void AWSScriptBehaviorDynamoDB::Reflect(AZ::ReflectContext* context)
{
- }
-
- void AWSScriptBehaviorDynamoDB::ReflectSerialization(AZ::SerializeContext* serializeContext)
- {
- if (serializeContext)
+ if (AZ::SerializeContext* serializeContext = azrtti_cast(context))
{
serializeContext->Class()
->Version(0);
}
- }
-
- void AWSScriptBehaviorDynamoDB::ReflectBehaviors(AZ::BehaviorContext* behaviorContext)
- {
- behaviorContext->Class("AWSScriptBehaviorDynamoDB")
- ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
- ->Method("GetItem", &AWSScriptBehaviorDynamoDB::GetItem,
- {{{"Table Resource KeyName", "The name of the table containing the requested item."},
- {"Key Map", "A map of attribute names to AttributeValue objects, representing the primary key of the item to retrieve."}}})
- ->Method("GetItemRaw", &AWSScriptBehaviorDynamoDB::GetItemRaw,
- {{{"Table Name", "The name of the table containing the requested item."},
- {"Key Map", "A map of attribute names to AttributeValue objects, representing the primary key of the item to retrieve."},
- {"Region Name", "The region of the table located in."}}});
- behaviorContext->EBus("AWSDynamoDBBehaviorNotificationBus")
- ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
- ->Handler();
- }
+ if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context))
+ {
+ behaviorContext->Class("AWSScriptBehaviorDynamoDB")
+ ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
+ ->Method("GetItem", &AWSScriptBehaviorDynamoDB::GetItem,
+ {{{"Table Resource KeyName", "The name of the table containing the requested item."},
+ {"Key Map", "A map of attribute names to AttributeValue objects, representing the primary key of the item to retrieve."}}})
+ ->Method("GetItemRaw", &AWSScriptBehaviorDynamoDB::GetItemRaw,
+ {{{"Table Name", "The name of the table containing the requested item."},
+ {"Key Map", "A map of attribute names to AttributeValue objects, representing the primary key of the item to retrieve."},
+ {"Region Name", "The region of the table located in."}}});
- void AWSScriptBehaviorDynamoDB::ReflectEditParameters(AZ::EditContext* editContext)
- {
- AZ_UNUSED(editContext);
+ behaviorContext->EBus("AWSDynamoDBBehaviorNotificationBus")
+ ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
+ ->Handler();
+ }
}
void AWSScriptBehaviorDynamoDB::GetItem(const AZStd::string& tableResourceKey, const DynamoDBAttributeValueMap& keyMap)
diff --git a/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorLambda.cpp b/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorLambda.cpp
index a7420654de..d4137c313b 100644
--- a/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorLambda.cpp
+++ b/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorLambda.cpp
@@ -21,39 +21,30 @@
namespace AWSCore
{
- AWSScriptBehaviorLambda::AWSScriptBehaviorLambda()
+ void AWSScriptBehaviorLambda::Reflect(AZ::ReflectContext* context)
{
- }
-
- void AWSScriptBehaviorLambda::ReflectSerialization(AZ::SerializeContext* serializeContext)
- {
- if (serializeContext)
+ if (AZ::SerializeContext* serializeContext = azrtti_cast(context))
{
serializeContext->Class()
->Version(0);
}
- }
-
- void AWSScriptBehaviorLambda::ReflectBehaviors(AZ::BehaviorContext* behaviorContext)
- {
- behaviorContext->Class("AWSScriptBehaviorLambda")
- ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
- ->Method("Invoke", &AWSScriptBehaviorLambda::Invoke,
- {{{"Function Resource KeyName", "The resource key name of the lambda function in resource mapping config file."},
- {"Payload", "The JSON that you want to provide to your Lambda function as input."}}})
- ->Method("InvokeRaw", &AWSScriptBehaviorLambda::InvokeRaw,
- {{{"Function Name", "The name of the Lambda function, version, or alias."},
- {"Payload", "The JSON that you want to provide to your Lambda function as input."},
- {"Region Name", "The region of the lambda function located in."}}});
- behaviorContext->EBus("AWSLambdaBehaviorNotificationBus")
- ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
- ->Handler();
- }
+ if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context))
+ {
+ behaviorContext->Class("AWSScriptBehaviorLambda")
+ ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
+ ->Method("Invoke", &AWSScriptBehaviorLambda::Invoke,
+ {{{"Function Resource KeyName", "The resource key name of the lambda function in resource mapping config file."},
+ {"Payload", "The JSON that you want to provide to your Lambda function as input."}}})
+ ->Method("InvokeRaw", &AWSScriptBehaviorLambda::InvokeRaw,
+ {{{"Function Name", "The name of the Lambda function, version, or alias."},
+ {"Payload", "The JSON that you want to provide to your Lambda function as input."},
+ {"Region Name", "The region of the lambda function located in."}}});
- void AWSScriptBehaviorLambda::ReflectEditParameters(AZ::EditContext* editContext)
- {
- AZ_UNUSED(editContext);
+ behaviorContext->EBus("AWSLambdaBehaviorNotificationBus")
+ ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
+ ->Handler();
+ }
}
void AWSScriptBehaviorLambda::Invoke(const AZStd::string& functionResourceKey, const AZStd::string& payload)
diff --git a/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorS3.cpp b/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorS3.cpp
index f47dda8046..e82fff0c31 100644
--- a/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorS3.cpp
+++ b/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorS3.cpp
@@ -24,49 +24,39 @@
namespace AWSCore
{
- AWSScriptBehaviorS3::AWSScriptBehaviorS3()
+ void AWSScriptBehaviorS3::Reflect(AZ::ReflectContext* context)
{
- }
-
- void AWSScriptBehaviorS3::ReflectSerialization(AZ::SerializeContext* serializeContext)
- {
- if (serializeContext)
+ if (AZ::SerializeContext* serializeContext = azrtti_cast(context))
{
serializeContext->Class()
->Version(0);
}
- }
-
- void AWSScriptBehaviorS3::ReflectBehaviors(AZ::BehaviorContext* behaviorContext)
- {
- behaviorContext->Class(AWSScriptBehaviorS3Name)
- ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
- ->Method("GetObject", &AWSScriptBehaviorS3::GetObject,
- {{{"Bucket Resource KeyName", "The resource key name of the bucket in resource mapping config file."},
- {"Object KeyName", "The object key."},
- {"Outfile Name", "Filename where the content will be saved."}}})
- ->Method("GetObjectRaw", &AWSScriptBehaviorS3::GetObjectRaw,
- {{{"Bucket Name", "The name of the bucket containing the object."},
- {"Object KeyName", "The object key."},
- {"Region Name", "The region of the bucket located in."},
- {"Outfile Name", "Filename where the content will be saved."}}})
- ->Method("HeadObject", &AWSScriptBehaviorS3::HeadObject,
- {{{"Bucket Resource KeyName", "The resource key name of the bucket in resource mapping config file."},
- {"Object KeyName", "The object key."}}})
- ->Method("HeadObjectRaw", &AWSScriptBehaviorS3::HeadObjectRaw,
- {{{"Bucket Name", "The name of the bucket containing the object."},
- {"Object KeyName", "The object key."},
- {"Region Name", "The region of the bucket located in."}}})
- ;
-
- behaviorContext->EBus("AWSS3BehaviorNotificationBus")
- ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
- ->Handler();
- }
- void AWSScriptBehaviorS3::ReflectEditParameters(AZ::EditContext* editContext)
- {
- AZ_UNUSED(editContext);
+ if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context))
+ {
+ behaviorContext->Class(AWSScriptBehaviorS3Name)
+ ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
+ ->Method("GetObject", &AWSScriptBehaviorS3::GetObject,
+ {{{"Bucket Resource KeyName", "The resource key name of the bucket in resource mapping config file."},
+ {"Object KeyName", "The object key."},
+ {"Outfile Name", "Filename where the content will be saved."}}})
+ ->Method("GetObjectRaw", &AWSScriptBehaviorS3::GetObjectRaw,
+ {{{"Bucket Name", "The name of the bucket containing the object."},
+ {"Object KeyName", "The object key."},
+ {"Region Name", "The region of the bucket located in."},
+ {"Outfile Name", "Filename where the content will be saved."}}})
+ ->Method("HeadObject", &AWSScriptBehaviorS3::HeadObject,
+ {{{"Bucket Resource KeyName", "The resource key name of the bucket in resource mapping config file."},
+ {"Object KeyName", "The object key."}}})
+ ->Method("HeadObjectRaw", &AWSScriptBehaviorS3::HeadObjectRaw,
+ {{{"Bucket Name", "The name of the bucket containing the object."},
+ {"Object KeyName", "The object key."},
+ {"Region Name", "The region of the bucket located in."}}});
+
+ behaviorContext->EBus("AWSS3BehaviorNotificationBus")
+ ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
+ ->Handler();
+ }
}
void AWSScriptBehaviorS3::GetObject(
diff --git a/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorsComponent.cpp b/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorsComponent.cpp
index 530c35cde2..4c41dc3f0e 100644
--- a/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorsComponent.cpp
+++ b/Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorsComponent.cpp
@@ -12,35 +12,17 @@
namespace AWSCore
{
- AZStd::vector> AWSScriptBehaviorsComponent::m_behaviors;
- bool AWSScriptBehaviorsComponent::m_alreadyAddedBehaviors = false;
-
- void AWSScriptBehaviorsComponent::AddBehaviors()
- {
- if (!m_alreadyAddedBehaviors)
- {
- // Add new script behaviors here
- m_behaviors.push_back(AZStd::make_unique());
- m_behaviors.push_back(AZStd::make_unique());
- m_behaviors.push_back(AZStd::make_unique());
- m_alreadyAddedBehaviors = true;
- }
- }
-
void AWSScriptBehaviorsComponent::Reflect(AZ::ReflectContext* context)
{
- AddBehaviors();
+ AWSScriptBehaviorDynamoDB::Reflect(context);
+ AWSScriptBehaviorLambda::Reflect(context);
+ AWSScriptBehaviorS3::Reflect(context);
if (AZ::SerializeContext* serialize = azrtti_cast(context))
{
serialize->Class()
->Version(0);
- for (auto&& behavior : m_behaviors)
- {
- behavior->ReflectSerialization(serialize);
- }
-
if (AZ::EditContext* editContext = serialize->GetEditContext())
{
editContext->Class("AWSScriptBehaviors", "Provides ScriptCanvas functions for calling AWS")
@@ -49,19 +31,6 @@ namespace AWSCore
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("AWS"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
-
- for (auto&& behavior : m_behaviors)
- {
- behavior->ReflectEditParameters(editContext);
- }
- }
- }
-
- if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context))
- {
- for (auto&& behavior : m_behaviors)
- {
- behavior->ReflectBehaviors(behaviorContext);
}
}
}
@@ -86,31 +55,12 @@ namespace AWSCore
AZ_UNUSED(dependent);
}
- void AWSScriptBehaviorsComponent::Init()
- {
- for (auto&& behavior : m_behaviors)
- {
- behavior->Init();
- }
- }
-
void AWSScriptBehaviorsComponent::Activate()
{
- for (auto&& behavior : m_behaviors)
- {
- behavior->Activate();
- }
}
void AWSScriptBehaviorsComponent::Deactivate()
{
- for (auto&& behavior : m_behaviors)
- {
- behavior->Deactivate();
- }
-
- // this forces the vector to release its capacity, clear/shrink_to_fit is not
- m_behaviors.swap(AZStd::vector>());
}
}
diff --git a/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorsComponentTest.cpp b/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorsComponentTest.cpp
index 15ad297f9b..2283caa69e 100644
--- a/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorsComponentTest.cpp
+++ b/Gems/AWSCore/Code/Tests/ScriptCanvas/AWSScriptBehaviorsComponentTest.cpp
@@ -15,18 +15,6 @@
using namespace AWSCore;
-class AWSScriptBehaviorsComponentMock
- : public AWSScriptBehaviorsComponent
-{
-public:
- AZ_COMPONENT(AWSScriptBehaviorsComponentMock, "{78579706-E1B2-4788-A34D-A58D3F273FF9}");
-
- int GetBehaviorsNum()
- {
- return m_behaviors.size();
- }
-};
-
class AWSScriptBehaviorsComponentTest
: public UnitTest::ScopedAllocatorSetupFixture
{
@@ -38,7 +26,7 @@ public:
m_behaviorContext = AZStd::make_unique();
m_entity = AZStd::make_unique();
- m_scriptBehaviorsComponent.reset(m_entity->CreateComponent());
+ m_scriptBehaviorsComponent.reset(m_entity->CreateComponent());
}
void TearDown() override
@@ -56,20 +44,16 @@ protected:
AZStd::unique_ptr m_serializeContext;
AZStd::unique_ptr m_behaviorContext;
AZStd::unique_ptr m_componentDescriptor;
- AZStd::unique_ptr m_scriptBehaviorsComponent;
+ AZStd::unique_ptr m_scriptBehaviorsComponent;
AZStd::unique_ptr m_entity;
};
-TEST_F(AWSScriptBehaviorsComponentTest, InitActivateDeactivate_Call_GetExpectedNumOfAddedBehaviors)
+TEST_F(AWSScriptBehaviorsComponentTest, Reflect)
{
- m_componentDescriptor.reset(AWSScriptBehaviorsComponentMock::CreateDescriptor());
+ int oldEBusNum = m_behaviorContext->m_ebuses.size();
+ m_componentDescriptor.reset(AWSScriptBehaviorsComponent::CreateDescriptor());
m_componentDescriptor->Reflect(m_serializeContext.get());
m_componentDescriptor->Reflect(m_behaviorContext.get());
- EXPECT_TRUE(AWSScriptBehaviorsComponentMock::AddedBehaviours());
- EXPECT_TRUE(m_scriptBehaviorsComponent->GetBehaviorsNum() == 3);
- m_entity->Init();
- m_entity->Activate();
- m_entity->Deactivate();
- EXPECT_TRUE(m_scriptBehaviorsComponent->GetBehaviorsNum() == 0);
+ EXPECT_TRUE(m_behaviorContext->m_ebuses.size() - oldEBusNum == 3);
}
diff --git a/Gems/AWSCore/Code/awscore_files.cmake b/Gems/AWSCore/Code/awscore_files.cmake
index c1577ec1eb..8e8ed280f8 100644
--- a/Gems/AWSCore/Code/awscore_files.cmake
+++ b/Gems/AWSCore/Code/awscore_files.cmake
@@ -32,7 +32,6 @@ set(FILES
Include/Public/Framework/ServiceRequestJobConfig.h
Include/Public/Framework/Util.h
Include/Public/ResourceMapping/AWSResourceMappingBus.h
- Include/Public/ScriptCanvas/AWSScriptBehaviorBase.h
Include/Public/ScriptCanvas/AWSScriptBehaviorDynamoDB.h
Include/Public/ScriptCanvas/AWSScriptBehaviorLambda.h
Include/Public/ScriptCanvas/AWSScriptBehaviorS3.h
diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp
index afa11d8e16..f6b99cac12 100644
--- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp
+++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp
@@ -95,7 +95,7 @@ namespace AZ
shaderVariantAssetBuilderDescriptor.m_name = "Shader Variant Asset Builder";
// Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update
// ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder".
- shaderVariantAssetBuilderDescriptor.m_version = 23; // ATOM-15472
+ shaderVariantAssetBuilderDescriptor.m_version = 24; // ATOM-15978
shaderVariantAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
shaderVariantAssetBuilderDescriptor.m_busId = azrtti_typeid();
shaderVariantAssetBuilderDescriptor.m_createJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::CreateJobs, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp
index 7be98ab056..010778575e 100644
--- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp
+++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp
@@ -48,6 +48,7 @@
#include "ShaderAssetBuilder.h"
#include "ShaderBuilderUtility.h"
+#include "SrgLayoutUtility.h"
#include "AzslData.h"
#include "AzslCompiler.h"
#include
@@ -520,6 +521,96 @@ namespace AZ
return;
}
}
+
+ static bool LoadSrgLayoutListFromShaderAssetBuilder(
+ const RHI::ShaderPlatformInterface* shaderPlatformInterface,
+ const AssetBuilderSDK::PlatformInfo& platformInfo,
+ const AzslCompiler& azslCompiler, const AZStd::string& shaderSourceFileFullPath,
+ const RPI::SupervariantIndex supervariantIndex,
+ const bool platformUsesRegisterSpaces,
+ RPI::ShaderResourceGroupLayoutList& srgLayoutList,
+ RootConstantData& rootConstantData)
+ {
+ auto srgJsonPathOutcome = ShaderBuilderUtility::ObtainBuildArtifactPathFromShaderAssetBuilder(
+ shaderPlatformInterface->GetAPIUniqueIndex(), platformInfo.m_identifier, shaderSourceFileFullPath, supervariantIndex.GetIndex(), AZ::RPI::ShaderAssetSubId::SrgJson);
+ if (!srgJsonPathOutcome.IsSuccess())
+ {
+ AZ_Error(ShaderVariantAssetBuilderName, false, "%s", srgJsonPathOutcome.GetError().c_str());
+ return false;
+ }
+
+ auto srgJsonPath = srgJsonPathOutcome.TakeValue();
+ auto jsonOutcome = JsonSerializationUtils::ReadJsonFile(srgJsonPath);
+ if (!jsonOutcome.IsSuccess())
+ {
+ AZ_Error(ShaderVariantAssetBuilderName, false, "%s", jsonOutcome.GetError().c_str());
+ return false;
+ }
+ SrgDataContainer srgData;
+ if (!azslCompiler.ParseSrgPopulateSrgData(jsonOutcome.GetValue(), srgData))
+ {
+ AZ_Error(ShaderVariantAssetBuilderName, false, "Failed to parse srg data");
+ return false;
+ }
+ // Add all Shader Resource Group Assets that were defined in the shader code to the shader asset
+ if (!SrgLayoutUtility::LoadShaderResourceGroupLayouts(ShaderVariantAssetBuilderName, srgData, platformUsesRegisterSpaces, srgLayoutList))
+ {
+ AZ_Error(ShaderVariantAssetBuilderName, false, "Failed to load ShaderResourceGroupLayouts");
+ return false;
+ }
+
+ for (auto srgLayout : srgLayoutList)
+ {
+ if (!srgLayout->Finalize())
+ {
+ AZ_Error(ShaderVariantAssetBuilderName, false,
+ "Failed to finalize SrgLayout %s", srgLayout->GetName().GetCStr());
+ return false;
+ }
+ }
+
+ // Access the root constants reflection
+ if (!azslCompiler.ParseSrgPopulateRootConstantData(
+ jsonOutcome.GetValue(),
+ rootConstantData)) // consuming data from --srg ("InlineConstantBuffer" subjson section)
+ {
+ AZ_Error(ShaderVariantAssetBuilderName, false, "Failed to obtain root constant data reflection");
+ return false;
+ }
+
+ return true;
+ }
+
+ static bool LoadBindingDependenciesFromShaderAssetBuilder(
+ const RHI::ShaderPlatformInterface* shaderPlatformInterface,
+ const AssetBuilderSDK::PlatformInfo& platformInfo,
+ const AzslCompiler& azslCompiler, const AZStd::string& shaderSourceFileFullPath,
+ const RPI::SupervariantIndex supervariantIndex,
+ BindingDependencies& bindingDependencies)
+ {
+ auto bindingsJsonPathOutcome = ShaderBuilderUtility::ObtainBuildArtifactPathFromShaderAssetBuilder(
+ shaderPlatformInterface->GetAPIUniqueIndex(), platformInfo.m_identifier, shaderSourceFileFullPath, supervariantIndex.GetIndex(), AZ::RPI::ShaderAssetSubId::BindingdepJson);
+ if (!bindingsJsonPathOutcome.IsSuccess())
+ {
+ AZ_Error(ShaderVariantAssetBuilderName, false, "%s", bindingsJsonPathOutcome.GetError().c_str());
+ return false;
+ }
+
+ auto bindingsJsonPath = bindingsJsonPathOutcome.TakeValue();
+ auto jsonOutcome = JsonSerializationUtils::ReadJsonFile(bindingsJsonPath);
+ if (!jsonOutcome.IsSuccess())
+ {
+ AZ_Error(ShaderVariantAssetBuilderName, false, "%s", jsonOutcome.GetError().c_str());
+ return false;
+ }
+ if (!azslCompiler.ParseBindingdepPopulateBindingDependencies(jsonOutcome.GetValue(), bindingDependencies))
+ {
+ AZ_Error(ShaderVariantAssetBuilderName, false, "Failed to parse binding dependencies data");
+ return false;
+ }
+
+ return true;
+ }
// Returns the content of the hlsl file for the given supervariant as produced by ShaderAsssetBuilder.
@@ -773,6 +864,50 @@ namespace AZ
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return;
}
+
+ //! It is important to keep this refcounted pointer outside of the if block to prevent it from being destroyed.
+ RHI::Ptr pipelineLayoutDescriptor;
+ if (shaderPlatformInterface->VariantCompilationRequiresSrgLayoutData())
+ {
+ AZStd::string azslcCompilerParameters =
+ shaderPlatformInterface->GetAzslCompilerParameters(buildOptions.m_compilerArguments);
+ const bool platformUsesRegisterSpaces =
+ (AzFramework::StringFunc::Find(azslcCompilerParameters, "--use-spaces") != AZStd::string::npos);
+
+ RPI::ShaderResourceGroupLayoutList srgLayoutList;
+ RootConstantData rootConstantData;
+ if (!LoadSrgLayoutListFromShaderAssetBuilder(
+ shaderPlatformInterface, request.m_platformInfo, azslc, shaderSourceFileFullPath, supervariantIndex,
+ platformUsesRegisterSpaces,
+ srgLayoutList,
+ rootConstantData))
+ {
+ response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
+ return;
+ }
+
+ BindingDependencies bindingDependencies;
+ if (!LoadBindingDependenciesFromShaderAssetBuilder(
+ shaderPlatformInterface, request.m_platformInfo, azslc, shaderSourceFileFullPath, supervariantIndex,
+ bindingDependencies))
+ {
+ response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
+ return;
+ }
+
+ pipelineLayoutDescriptor =
+ ShaderBuilderUtility::BuildPipelineLayoutDescriptorForApi(
+ ShaderVariantAssetBuilderName, srgLayoutList, shaderEntryPoints, buildOptions.m_compilerArguments, rootConstantData,
+ shaderPlatformInterface, bindingDependencies);
+ if (!pipelineLayoutDescriptor)
+ {
+ AZ_Error(
+ ShaderVariantAssetBuilderName, false, "Failed to build pipeline layout descriptor for api=[%s]",
+ shaderPlatformInterface->GetAPIName().GetCStr());
+ response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
+ return;
+ }
+ }
// Setup the shader variant creation context:
ShaderVariantCreationContext shaderVariantCreationContext =
diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderPlatformInterface.h b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderPlatformInterface.h
index d83fbf2267..f7db5f865b 100644
--- a/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderPlatformInterface.h
+++ b/Gems/Atom/RHI/Code/Include/Atom/RHI.Edit/ShaderPlatformInterface.h
@@ -144,6 +144,12 @@ namespace AZ
const ShaderResourceGroupInfoList& srgInfoList,
const RootConstantsInfo& rootConstantsInfo,
const ShaderCompilerArguments& shaderCompilerArguments) = 0;
+
+ //! In general, shader compilation doesn't require SRG Layout data, but RHIs like
+ //! Metal don't do well if unused resources (descriptors) are not bound. If this function returns TRUE
+ //! the ShaderVariantAssetBuilder will invoke BuildPipelineLayoutDescriptor() so the RHI gets the chance to
+ //! build SRG Layout data which will be useful when compiling MetalISL to Metal byte code.
+ virtual bool VariantCompilationRequiresSrgLayoutData() const { return false; }
//! See AZ::RHI::Factory::GetAPIUniqueIndex() for details.
//! See AZ::RHI::Limits::APIType::PerPlatformApiUniqueIndexMax.
diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp
index 0801101178..2f648acc05 100644
--- a/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp
+++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/ShaderCompilerArguments.cpp
@@ -159,7 +159,13 @@ namespace AZ
arguments += " -Zi"; // Generate debug information
arguments += " -Zss"; // Compute Shader Hash considering source information
}
- arguments += " " + m_dxcAdditionalFreeArguments;
+ // strip spaces at both sides
+ AZStd::string dxcAdditionalFreeArguments = m_dxcAdditionalFreeArguments;
+ AzFramework::StringFunc::TrimWhiteSpace(dxcAdditionalFreeArguments, true, true);
+ if (!dxcAdditionalFreeArguments.empty())
+ {
+ arguments += " " + dxcAdditionalFreeArguments;
+ }
return arguments;
}
}
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 de3bc23a11..cb9e72118a 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
@@ -43,11 +43,36 @@ namespace Platform
}
return physicalDeviceList;
}
+
+ float GetRefreshRate()
+ {
+ CGDirectDisplayID display = CGMainDisplayID();
+ CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(display);
+ return CGDisplayModeGetRefreshRate(currentMode);
+ }
- void PresentInternal(id mtlCommandBuffer, id drawable, float syncInterval)
+ void PresentInternal(id mtlCommandBuffer, id drawable, float syncInterval, float refreshRate)
{
- AZ_UNUSED(syncInterval);
- [mtlCommandBuffer presentDrawable:drawable];
+ bool framePresented = false;
+
+ //seconds per frame (1/refreshrate) * num frames (sync interval)
+ float presentAfterMinimumDuration = syncInterval / refreshRate;
+
+#if defined(__MAC_10_15_4)
+ if(@available(macOS 10.15.4, *))
+ {
+ if(presentAfterMinimumDuration > 0.0f)
+ {
+ [mtlCommandBuffer presentDrawable:drawable afterMinimumDuration:presentAfterMinimumDuration];
+ framePresented = true;
+ }
+ }
+#endif
+
+ if(!framePresented)
+ {
+ [mtlCommandBuffer presentDrawable:drawable];
+ }
}
CGRect GetScreenBounds(NativeWindowType* nativeWindow)
diff --git a/Gems/Atom/RHI/Metal/Code/Source/Platform/iOS/RHI/Metal_RHI_iOS.cpp b/Gems/Atom/RHI/Metal/Code/Source/Platform/iOS/RHI/Metal_RHI_iOS.cpp
index b7c309fba2..007655a93e 100644
--- a/Gems/Atom/RHI/Metal/Code/Source/Platform/iOS/RHI/Metal_RHI_iOS.cpp
+++ b/Gems/Atom/RHI/Metal/Code/Source/Platform/iOS/RHI/Metal_RHI_iOS.cpp
@@ -29,13 +29,19 @@ namespace Platform
return physicalDeviceList;
}
- void PresentInternal(id mtlCommandBuffer, id drawable, float syncInterval)
+ float GetRefreshRate()
{
- bool hasPresentAfterMinimumDuration = [drawable respondsToSelector:@selector(presentAfterMinimumDuration:)];
-
- if (hasPresentAfterMinimumDuration && syncInterval > 0.0f)
+ NativeScreenType* nativeScreen = [NativeScreenType mainScreen];
+ return [nativeScreen maximumFramesPerSecond];
+ }
+
+ void PresentInternal(id mtlCommandBuffer, id drawable, float syncInterval, float refreshRate)
+ {
+ //seconds per frame (1/refreshrate) * num frames (sync interval)
+ float presentAfterMinimumDuration = syncInterval / refreshRate;
+ if (hasPresentAfterMinimumDuration > 0.0f)
{
- [mtlCommandBuffer presentDrawable:drawable afterMinimumDuration:syncInterval];
+ [mtlCommandBuffer presentDrawable:drawable afterMinimumDuration:presentAfterMinimumDuration];
}
else
{
diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.h b/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.h
index 935d2e6471..0be02053cd 100644
--- a/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.h
+++ b/Gems/Atom/RHI/Metal/Code/Source/RHI.Builders/ShaderPlatformInterface.h
@@ -40,6 +40,8 @@ namespace AZ
const ShaderResourceGroupInfoList& srgInfoList,
const RootConstantsInfo& rootConstantsInfo,
const RHI::ShaderCompilerArguments& shaderCompilerArguments) override;
+
+ bool VariantCompilationRequiresSrgLayoutData() const override { return true; }
bool CompilePlatformInternal(
const AssetBuilderSDK::PlatformInfo& platform,
diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandListBase.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandListBase.cpp
index 675a593bc8..9df5f3362d 100644
--- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandListBase.cpp
+++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandListBase.cpp
@@ -30,6 +30,7 @@ namespace AZ
{
AZ_UNUSED(device);
m_hardwareQueueClass = hardwareQueueClass;
+ m_supportsInterDrawTimestamps = AZ::RHI::QueryTypeFlags::Timestamp == (device->GetFeatures().m_queryTypesMask[static_cast(hardwareQueueClass)] & AZ::RHI::QueryTypeFlags::Timestamp);
}
void CommandListBase::Reset()
@@ -64,7 +65,10 @@ namespace AZ
[m_encoder endEncoding];
m_encoder = nil;
#if AZ_TRAIT_ATOM_METAL_COUNTER_SAMPLING
- m_timeStampQueue.clear();
+ if (m_supportsInterDrawTimestamps)
+ {
+ m_timeStampQueue.clear();
+ }
#endif
}
}
@@ -144,9 +148,12 @@ namespace AZ
m_isEncoded = true;
#if AZ_TRAIT_ATOM_METAL_COUNTER_SAMPLING
- for(auto& timeStamp: m_timeStampQueue)
+ if (m_supportsInterDrawTimestamps)
{
- SampleCounters(timeStamp.m_counterSampleBuffer, timeStamp.m_timeStampIndex);
+ for(auto& timeStamp: m_timeStampQueue)
+ {
+ SampleCounters(timeStamp.m_counterSampleBuffer, timeStamp.m_timeStampIndex);
+ }
}
#endif
}
@@ -195,6 +202,11 @@ namespace AZ
#if AZ_TRAIT_ATOM_METAL_COUNTER_SAMPLING
void CommandListBase::SampleCounters(id counterSampleBuffer, uint32_t sampleIndex)
{
+ if (!m_supportsInterDrawTimestamps)
+ {
+ return;
+ }
+
AZ_Assert(sampleIndex >= 0, "Invalid sample index");
//useBarrier - Inserting a barrier ensures that encoded work is complete before the GPU samples the hardware counters.
//If it is true there is a performance penalty but you will get consistent results
@@ -231,6 +243,11 @@ namespace AZ
void CommandListBase::SamplePassCounters(id counterSampleBuffer, uint32_t sampleIndex)
{
+ if (!m_supportsInterDrawTimestamps)
+ {
+ return;
+ }
+
if(m_encoder == nil)
{
//Queue the query to be activated upon encoder creation. Applies to timestamp queries
diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandListBase.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandListBase.h
index 70678aed01..a344222e63 100644
--- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandListBase.h
+++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandListBase.h
@@ -101,6 +101,8 @@ namespace AZ
const AZStd::set>* m_residentHeaps = nullptr;
+ bool m_supportsInterDrawTimestamps = AZ_TRAIT_ATOM_METAL_COUNTER_SAMPLING; // iOS/TVOS = false, MacOS = defaults to true
+
#if AZ_TRAIT_ATOM_METAL_COUNTER_SAMPLING
struct TimeStampData
{
diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.cpp
index 5e23353b65..0da2ea6aaa 100644
--- a/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.cpp
+++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/Device.cpp
@@ -328,14 +328,28 @@ namespace AZ
m_features.m_indirectDrawSupport = false;
RHI::QueryTypeFlags counterSamplingFlags = RHI::QueryTypeFlags::None;
-
-#if AZ_TRAIT_ATOM_METAL_COUNTER_SAMPLING
- counterSamplingFlags |= (RHI::QueryTypeFlags::Timestamp | RHI::QueryTypeFlags::PipelineStatistics);
- m_features.m_queryTypesMask[static_cast(RHI::HardwareQueueClass::Copy)] = RHI::QueryTypeFlags::Timestamp;
+
+ bool supportsInterDrawTimestamps = true;
+#if defined(__IPHONE_14_0) || defined(__MAC_11_0) || defined(__TVOS_14_0)
+ if (@available(macOS 11.0, iOS 14, tvOS 14, *))
+ {
+ supportsInterDrawTimestamps = [m_metalDevice supportsCounterSampling:MTLCounterSamplingPointAtDrawBoundary];
+ }
+ else
#endif
+ {
+ supportsInterDrawTimestamps = ![m_metalDevice.name containsString:@"Apple"]; // Apple GPU's don't support inter draw timestamps at the M1/A14 generation
+ }
+
+ if (supportsInterDrawTimestamps)
+ {
+ counterSamplingFlags |= (RHI::QueryTypeFlags::Timestamp | RHI::QueryTypeFlags::PipelineStatistics);
+ m_features.m_queryTypesMask[static_cast(RHI::HardwareQueueClass::Copy)] = RHI::QueryTypeFlags::Timestamp;
+ }
+
m_features.m_queryTypesMask[static_cast(RHI::HardwareQueueClass::Graphics)] = RHI::QueryTypeFlags::Occlusion | counterSamplingFlags;
//Compute queue can do gfx work
- m_features.m_queryTypesMask[static_cast(RHI::HardwareQueueClass::Compute)] = RHI::QueryTypeFlags::Occlusion |counterSamplingFlags;
+ m_features.m_queryTypesMask[static_cast(RHI::HardwareQueueClass::Compute)] = RHI::QueryTypeFlags::Occlusion | counterSamplingFlags;
m_features.m_occlusionQueryPrecise = true;
//Values taken from https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp
index 31d23e5100..08d5d2be87 100644
--- a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp
+++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp
@@ -19,8 +19,9 @@ namespace Platform
CGFloat GetScreenScale();
void AttachViewController(NativeWindowType* nativeWindow, NativeViewControllerType* viewController, RHIMetalView* metalView);
void UnAttachViewController(NativeWindowType* nativeWindow, NativeViewControllerType* viewController);
- void PresentInternal(id mtlCommandBuffer, id drawable, float syncInterval);
+ void PresentInternal(id mtlCommandBuffer, id drawable, float syncInterval, float refreshRate);
void ResizeInternal(RHIMetalView* metalView, CGSize viewSize);
+ float GetRefreshRate();
RHIMetalView* GetMetalView(NativeWindowType* nativeWindow);
}
@@ -73,6 +74,15 @@ namespace AZ
AddSubView();
}
+ m_refreshRate = Platform::GetRefreshRate();
+
+ //Assume 60hz if 0 is returned.
+ //Internal OSX displays have 'flexible' refresh rates, with a max of 60Hz - but report 0hz
+ if (m_refreshRate < 0.1f)
+ {
+ m_refreshRate = 60.0f;
+ }
+
m_drawables.resize(descriptor.m_dimensions.m_imageCount);
if (nativeDimensions)
@@ -148,10 +158,9 @@ namespace AZ
uint32_t SwapChain::PresentInternal()
{
const uint32_t currentImageIndex = GetCurrentImageIndex();
- //GFX TODO][ATOM-432] - Hardcoding to 30fps for now. Only used by ios. This needs to be driven by higher level code.
- float syncInterval = 1.0f/30.0f;
+
//Preset the drawable
- Platform::PresentInternal(m_mtlCommandBuffer, m_drawables[currentImageIndex], syncInterval);
+ Platform::PresentInternal(m_mtlCommandBuffer, m_drawables[currentImageIndex], GetDescriptor().m_verticalSyncInterval, m_refreshRate);
[m_drawables[currentImageIndex] release];
m_drawables[currentImageIndex] = nil;
diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h
index c86bd1f7a5..72014005ac 100644
--- a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h
+++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.h
@@ -52,6 +52,7 @@ namespace AZ
id m_mtlDevice = nil;
NativeWindowType* m_nativeWindow = nullptr;
AZStd::vector> m_drawables;
+ float m_refreshRate = 0.0f;
};
}
}
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderResourceGroupPool.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderResourceGroupPool.cpp
index d8c69cf8a0..4e858e4880 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderResourceGroupPool.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderResourceGroupPool.cpp
@@ -58,8 +58,8 @@ namespace AZ
RHI::ShaderResourceGroupPoolDescriptor poolDescriptor;
poolDescriptor.m_layout = shaderAsset.FindShaderResourceGroupLayout(srgName, supervariantIndex).get();
-
- m_pool->SetName(srgName);
+ m_pool->SetName(AZ::Name(AZStd::string::format("%s_%s",shaderAsset.GetName().GetCStr(),srgName.GetCStr())));
+
const RHI::ResultCode resultCode = m_pool->Init(*device, poolDescriptor);
return resultCode;
}
diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/WindowContext.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/WindowContext.cpp
index 16ad7a1155..4b8c061b68 100644
--- a/Gems/Atom/RPI/Code/Source/RPI.Public/WindowContext.cpp
+++ b/Gems/Atom/RPI/Code/Source/RPI.Public/WindowContext.cpp
@@ -27,7 +27,7 @@ void OnVsyncIntervalChanged(uint32_t const& interval)
// NOTE: On change, broadcasts the new requested vsync interval to all windows.
// The value of the vsync interval is constrained between 0 and 4
// Vsync intervals greater than 1 are not currently supported on the Vulkan RHI (see #2061 for discussion)
-AZ_CVAR(uint32_t, rpi_vsync_interval, 0, OnVsyncIntervalChanged, AZ::ConsoleFunctorFlags::Null, "Set swapchain vsync interval");
+AZ_CVAR(uint32_t, rpi_vsync_interval, 1, OnVsyncIntervalChanged, AZ::ConsoleFunctorFlags::Null, "Set swapchain vsync interval");
namespace AZ
{
diff --git a/Gems/Gestures/Code/Include/Gestures/IGestureRecognizer.h b/Gems/Gestures/Code/Include/Gestures/IGestureRecognizer.h
index 69e28df102..e606d68a3e 100644
--- a/Gems/Gestures/Code/Include/Gestures/IGestureRecognizer.h
+++ b/Gems/Gestures/Code/Include/Gestures/IGestureRecognizer.h
@@ -209,12 +209,17 @@ namespace Gestures
////////////////////////////////////////////////////////////////////////////////////////////////
inline uint32_t IRecognizer::GetGesturePointerIndex(const AzFramework::InputChannel& inputChannel)
{
- const auto& mouseButtonIt = AZStd::find(AzFramework::InputDeviceMouse::Button::All.cbegin(),
- AzFramework::InputDeviceMouse::Button::All.cend(),
- inputChannel.GetInputChannelId());
- if (mouseButtonIt != AzFramework::InputDeviceMouse::Button::All.cend())
+ // Only recognize gestures for the default mouse input device. The Editor may register synthetic
+ // mouse input devices with the same mouse input channels, which can confuse gesture recognition.
+ if (inputChannel.GetInputDevice().GetInputDeviceId() == AzFramework::InputDeviceMouse::Id)
{
- return static_cast(mouseButtonIt - AzFramework::InputDeviceMouse::Button::All.cbegin());
+ const auto& mouseButtonIt = AZStd::find(AzFramework::InputDeviceMouse::Button::All.cbegin(),
+ AzFramework::InputDeviceMouse::Button::All.cend(),
+ inputChannel.GetInputChannelId());
+ if (mouseButtonIt != AzFramework::InputDeviceMouse::Button::All.cend())
+ {
+ return static_cast(mouseButtonIt - AzFramework::InputDeviceMouse::Button::All.cbegin());
+ }
}
const auto& touchIndexIt = AZStd::find(AzFramework::InputDeviceTouch::Touch::All.cbegin(),
diff --git a/Gems/LyShine/Code/CMakeLists.txt b/Gems/LyShine/Code/CMakeLists.txt
index fe5e2e77df..905228e414 100644
--- a/Gems/LyShine/Code/CMakeLists.txt
+++ b/Gems/LyShine/Code/CMakeLists.txt
@@ -63,7 +63,6 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
AUTORCC
FILES_CMAKE
lyshine_uicanvaseditor_files.cmake
- lyshine_editor_builder_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
@@ -78,7 +77,6 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
3rdParty::Qt::Widgets
AZ::AzCore
AZ::AzToolsFramework
- AZ::AssetBuilderSDK
Legacy::EditorCommon
Legacy::EditorCore
Gem::LyShine.Static
@@ -98,7 +96,6 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME LyShine.Editor GEM_MODULE
-
NAMESPACE Gem
FILES_CMAKE
lyshine_common_module_files.cmake
@@ -115,17 +112,72 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
BUILD_DEPENDENCIES
PRIVATE
Legacy::CryCommon
- AZ::AssetBuilderSDK
+ AZ::AzToolsFramework
Gem::LyShine.Editor.Static
Gem::LmbrCentral.Editor
Gem::TextureAtlas.Editor
RUNTIME_DEPENDENCIES
Gem::LmbrCentral.Editor
Gem::TextureAtlas.Editor
-)
+ )
+
+ # by naming this target LyShine.Builders it ensures that it is loaded
+ # in any pipeline tools (Like Asset Processor, AssetBuilder, etc)
+ ly_add_target(
+ NAME LyShine.Builders.Static STATIC
+ NAMESPACE Gem
+ FILES_CMAKE
+ lyshine_editor_builder_files.cmake
+ INCLUDE_DIRECTORIES
+ PRIVATE
+ .
+ Source
+ PUBLIC
+ Include
+ BUILD_DEPENDENCIES
+ PRIVATE
+ AZ::AzCore
+ AZ::AzToolsFramework
+ AZ::AssetBuilderSDK
+ Gem::LyShine.Static
+ Legacy::CryCommon
+ Gem::LmbrCentral.Editor
+ Gem::TextureAtlas.Editor
+ Gem::AtomToolsFramework.Static
+ Gem::AtomToolsFramework.Editor
+ ${additional_dependencies}
+ PUBLIC
+ Gem::Atom_RPI.Public
+ Gem::Atom_Utils.Static
+ Gem::Atom_Bootstrap.Headers
+ )
+
+ ly_add_target(
+ NAME LyShine.Builders GEM_MODULE
+ NAMESPACE Gem
+ OUTPUT_NAME Gem.LyShine.Builders
+ FILES_CMAKE
+ lyshine_common_module_files.cmake
+ COMPILE_DEFINITIONS
+ PRIVATE
+ LYSHINE_BUILDER
+ INCLUDE_DIRECTORIES
+ PRIVATE
+ .
+ Source
+ Editor
+ PUBLIC
+ Include
+ BUILD_DEPENDENCIES
+ PRIVATE
+ Legacy::CryCommon
+ AZ::AssetBuilderSDK
+ Gem::LyShine.Builders.Static
+ Gem::LmbrCentral.Editor
+ Gem::TextureAtlas.Editor
+ )
# by default, load the above "Gem::LyShine.Editor" module in dev tools:
- ly_create_alias(NAME LyShine.Builders NAMESPACE Gem TARGETS Gem::LyShine.Editor)
ly_create_alias(NAME LyShine.Tools NAMESPACE Gem TARGETS Gem::LyShine.Editor)
endif()
@@ -169,6 +221,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
COMPILE_DEFINITIONS
PRIVATE
LYSHINE_EDITOR
+ LYSHINE_BUILDER
INCLUDE_DIRECTORIES
PRIVATE
Tests
@@ -182,6 +235,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
Legacy::CryCommon
AZ::AssetBuilderSDK
Gem::LyShine.Editor.Static
+ Gem::LyShine.Builders.Static
Gem::LmbrCentral.Editor
Gem::TextureAtlas
RUNTIME_DEPENDENCIES
diff --git a/Gems/LyShine/Code/Source/LyShineModule.cpp b/Gems/LyShine/Code/Source/LyShineModule.cpp
index 011bb9f203..64ce61b07a 100644
--- a/Gems/LyShine/Code/Source/LyShineModule.cpp
+++ b/Gems/LyShine/Code/Source/LyShineModule.cpp
@@ -52,9 +52,9 @@
#include "World/UiCanvasProxyRefComponent.h"
#include "World/UiCanvasOnMeshComponent.h"
-#if defined (LYSHINE_EDITOR)
-# include "Pipeline/LyShineBuilder/LyShineBuilderComponent.h"
-#endif // LYSHINE_EDITOR
+#if defined(LYSHINE_BUILDER)
+#include "Pipeline/LyShineBuilder/LyShineBuilderComponent.h"
+#endif // LYSHINE_BUILDER
namespace LyShine
{
@@ -64,7 +64,7 @@ namespace LyShine
// Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
m_descriptors.insert(m_descriptors.end(), {
LyShineSystemComponent::CreateDescriptor(),
-#if defined (LYSHINE_EDITOR)
+#if defined(LYSHINE_EDITOR)
LyShineEditor::LyShineEditorSystemComponent::CreateDescriptor(),
#endif
UiCanvasAssetRefComponent::CreateDescriptor(),
@@ -103,7 +103,7 @@ namespace LyShine
UiRadioButtonComponent::CreateDescriptor(),
UiRadioButtonGroupComponent::CreateDescriptor(),
UiParticleEmitterComponent::CreateDescriptor(),
- #if defined(LYSHINE_EDITOR)
+ #if defined(LYSHINE_BUILDER)
// Builder
LyShineBuilder::LyShineBuilderComponent::CreateDescriptor(),
#endif
@@ -123,7 +123,7 @@ namespace LyShine
{
return AZ::ComponentTypeList{
azrtti_typeid(),
- #if defined (LYSHINE_EDITOR)
+ #if defined(LYSHINE_EDITOR)
azrtti_typeid(),
#endif
#if AZ_LOADSCREENCOMPONENT_ENABLED
diff --git a/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp b/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp
index 7521c8b6c1..b420e551b5 100644
--- a/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp
+++ b/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp
@@ -148,7 +148,6 @@ namespace LyShine
{
LyShineAllocatorScope::ActivateAllocators();
- LyShineRequestBus::Handler::BusConnect();
UiSystemBus::Handler::BusConnect();
UiSystemToolsBus::Handler::BusConnect();
UiFrameworkBus::Handler::BusConnect();
@@ -196,7 +195,6 @@ namespace LyShine
UiSystemBus::Handler::BusDisconnect();
UiSystemToolsBus::Handler::BusDisconnect();
UiFrameworkBus::Handler::BusDisconnect();
- LyShineRequestBus::Handler::BusDisconnect();
CrySystemEventBus::Handler::BusDisconnect();
LyShineAllocatorScope::DeactivateAllocators();
diff --git a/Gems/LyShine/Code/Source/LyShineSystemComponent.h b/Gems/LyShine/Code/Source/LyShineSystemComponent.h
index 5b455715ee..70e7eb5fe5 100644
--- a/Gems/LyShine/Code/Source/LyShineSystemComponent.h
+++ b/Gems/LyShine/Code/Source/LyShineSystemComponent.h
@@ -13,7 +13,6 @@
#include
-#include
#include
#include
#include
@@ -28,7 +27,6 @@ namespace LyShine
class LyShineSystemComponent
: public AZ::Component
- , protected LyShineRequestBus::Handler
, protected UiSystemBus::Handler
, protected UiSystemToolsBus::Handler
, protected LyShineAllocatorScope
diff --git a/Gems/LyShine/Code/lyshine_common_module_files.cmake b/Gems/LyShine/Code/lyshine_common_module_files.cmake
index e725112e53..38d01eb181 100644
--- a/Gems/LyShine/Code/lyshine_common_module_files.cmake
+++ b/Gems/LyShine/Code/lyshine_common_module_files.cmake
@@ -8,4 +8,6 @@
set(FILES
Source/LyShineModule.cpp
Source/LyShineModule.h
+ Source/LyShineSystemComponent.cpp
+ Source/LyShineSystemComponent.h
)
diff --git a/Gems/LyShine/Code/lyshine_static_files.cmake b/Gems/LyShine/Code/lyshine_static_files.cmake
index 8c2275ab92..749ce06125 100644
--- a/Gems/LyShine/Code/lyshine_static_files.cmake
+++ b/Gems/LyShine/Code/lyshine_static_files.cmake
@@ -26,8 +26,6 @@ set(FILES
Source/EditorPropertyTypes.h
Source/LyShineLoadScreen.cpp
Source/LyShineLoadScreen.h
- Source/LyShineSystemComponent.cpp
- Source/LyShineSystemComponent.h
Source/RenderGraph.cpp
Source/RenderGraph.h
Source/TextMarkup.cpp
diff --git a/Gems/SceneProcessing/Code/CMakeLists.txt b/Gems/SceneProcessing/Code/CMakeLists.txt
index 9aa5bc6763..b33e157e51 100644
--- a/Gems/SceneProcessing/Code/CMakeLists.txt
+++ b/Gems/SceneProcessing/Code/CMakeLists.txt
@@ -61,6 +61,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
RUNTIME_DEPENDENCIES
AZ::SceneCore
AZ::SceneData
+ AZ::SceneUI
)
# the SceneProcessing.Editor module above is only used in Builders and Tools.
ly_create_alias(NAME SceneProcessing.Builders NAMESPACE Gem TARGETS Gem::SceneProcessing.Editor)
diff --git a/Tools/LyTestTools/tests/CMakeLists.txt b/Tools/LyTestTools/tests/CMakeLists.txt
index e5f270197c..5e57cbf123 100644
--- a/Tools/LyTestTools/tests/CMakeLists.txt
+++ b/Tools/LyTestTools/tests/CMakeLists.txt
@@ -52,18 +52,4 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT
AutomatedTesting.Assets
COMPONENT TestTools
)
-
- # Example tests.
- ly_add_pytest(
- NAME LyTestTools_ExampleTests_periodic_no_gpu
- PATH ${CMAKE_CURRENT_LIST_DIR}/example/test_system_example.py
- TEST_SERIAL
- TEST_SUITE periodic
- RUNTIME_DEPENDENCIES
- Legacy::Editor
- AssetProcessor
- AutomatedTesting.GameLauncher
- AutomatedTesting.Assets
- COMPONENT TestTools
- )
endif()
diff --git a/Tools/LyTestTools/tests/example/__init__.py b/Tools/LyTestTools/tests/example/__init__.py
deleted file mode 100755
index e200fa77d0..0000000000
--- a/Tools/LyTestTools/tests/example/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""
-Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
-
-SPDX-License-Identifier: Apache-2.0 OR MIT
-"""
diff --git a/Tools/LyTestTools/tests/example/test_system_example.py b/Tools/LyTestTools/tests/example/test_system_example.py
deleted file mode 100755
index 3cb6128802..0000000000
--- a/Tools/LyTestTools/tests/example/test_system_example.py
+++ /dev/null
@@ -1,129 +0,0 @@
-"""
-Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
-
-SPDX-License-Identifier: Apache-2.0 OR MIT
-
-Example test using LyTestTools to test Lumberyard.
-"""
-# Python built-in dependencies.
-import logging
-
-# Third party dependencies.
-import pytest
-
-# ly_test_tools dependencies.
-import ly_test_tools.log.log_monitor
-import ly_remote_console.remote_console_commands as remote_console_commands
-
-# Configuring the logging is done in ly_test_tools at the following location:
-# ~/dev/Tools/LyTestTools/ly_test_tools/_internal/log/py_logging_util.py
-
-# Use the following logging pattern to hook all test logging together:
-logger = logging.getLogger(__name__)
-
-
-@pytest.fixture
-def remote_console(request):
- """
- Creates a RemoteConsole() class instance to send console commands to the
- Lumberyard client console.
- :param request: _pytest.fixtures.SubRequest class that handles getting
- a pytest fixture from a pytest function/fixture.
- :return: ly_remote_console.remote_console_commands.RemoteConsole class instance
- representing the Lumberyard remote console executable.
- """
- # Initialize the RemoteConsole object to send commands to the Lumberyard client console.
- console = remote_console_commands.RemoteConsole()
-
- # Custom teardown method for this remote_console fixture.
- def teardown():
- console.stop()
-
- # Utilize request.addfinalizer() to add custom teardown() methods.
- request.addfinalizer(teardown) # This pattern must be used in pytest version
-
- return console
-
-# Shared parameters & fixtures for all test methods inside the TestSystemExample class.
-@pytest.mark.usefixtures("automatic_process_killer")
-@pytest.mark.parametrize('project', ['AutomatedTesting'])
-class TestSystemExample(object):
- """
- Example test case class to hold a set of test case methods.
-
- The amount of tests run is based on the parametrization stacking made in each test method or class.
- For this test, we placed unique test values in test methods and shared test values in the test class.
- We also assume building has already been done, but the test should error if the build is mis-configured.
- """
- # This test method needs specific parameters not shared by all other tests in the class.
- # For targeting specific launchers, use the 'launcher_platform' pytest param like below:
- # @pytest.mark.parametrize("launcher_platform", ['android'])
- # If you want to target different AssetProcessor platforms, use asset_processor_platform:
- # @pytest.mark.parametrize("asset_processor_platform", ['android'])
- @pytest.mark.parametrize('level', ['simple_jacklocomotion'])
- @pytest.mark.parametrize('load_wait', [120])
- @pytest.mark.test_case_id('C16806863')
- def test_SystemTestExample_AllSupportedPlatforms_LaunchAutomatedTesting(
- # launcher_platform, asset_processor_platform, # Re-add these here if you plan to use them.
- self, launcher, remote_console, level, load_wait):
- """
- Tests launching the AutomatedTesting then launches the Lumberyard client &
- loads the "simple_jacklocomotion" level using the remote console.
- Assumes the user already setup & built their machine for the test.
- """
- # Launch the Lumberyard client & remote console test case:
- with launcher.start():
- remote_console.start()
- launcher_load = remote_console.expect_log_line(
- match_string='Level system is loading "simple_jacklocomotion"',
- timeout=load_wait)
-
- # Assert loading was successful using remote console logs:
- assert launcher_load, (
- 'Launcher failed to load Lumberyard client with the '
- f'"{level}" level - waited "{load_wait}" seconds.')
-
- # This test method only needs pytest.mark report values and shared test class parameters.
- @pytest.mark.parametrize('processes_to_kill', ['Editor.exe'])
- @pytest.mark.parametrize("launcher_platform", ['windows_editor'])
- @pytest.mark.test_case_id('C16806864')
- def test_SystemTestExample_AllSupportedPlatforms_LaunchEditor(self, editor, processes_to_kill, launcher_platform):
- """
- Tests launching the Lumberyard Editor is successful with the current build.
- """
- # Launch the Lumberyard editor & verify load is successful:
- with editor.start():
- assert editor.is_alive(), (
- 'Editor failed to launch for the current Lumberyard build.')
-
- # Log monitoring example test.
- @pytest.mark.parametrize('level', ['simple_jacklocomotion'])
- @pytest.mark.parametrize('expected_lines', [['Log Monitoring test 1', 'Log Monitoring test 2']])
- @pytest.mark.parametrize('unexpected_lines', [['Unexpected test 1', 'Unexpected test 2']])
- @pytest.mark.test_case_id('C21202585')
- def test_SystemTestExample_AllSupportedPlatforms_LogMonitoring(self, level, launcher, expected_lines,
- unexpected_lines):
- """
- Tests that the logging paths created by LyTestTools can be monitored for results using the log monitor.
- """
- # Launch the Lumberyard client & initialize the log monitor.
- file_to_monitor = launcher.workspace.info_log_path
- log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher,
- log_file_path=file_to_monitor)
-
- # Generate log lines to the info log using logger.
- for expected_line in expected_lines:
- logger.info(expected_line)
-
- # Start the Lumberyard client & test that the lines we logged can be viewed by the log monitor.
- with launcher.start():
- log_test = log_monitor.monitor_log_for_lines(
- expected_lines=expected_lines, # Defaults to None.
- unexpected_lines=unexpected_lines, # Defaults to None.
- halt_on_unexpected=True, # Defaults to False.
- timeout=60) # Defaults to 30
-
- # Assert the log monitor detected expected lines and did not detect any unexpected lines.
- assert log_test, (
- f'Log monitoring failed. Used expected_lines values: {expected_lines} & '
- f'unexpected_lines values: {unexpected_lines}')
diff --git a/Tools/LyTestTools/tests/integ/sanity_tests.py b/Tools/LyTestTools/tests/integ/sanity_tests.py
index c6c5ce0c31..f68bc9ae57 100755
--- a/Tools/LyTestTools/tests/integ/sanity_tests.py
+++ b/Tools/LyTestTools/tests/integ/sanity_tests.py
@@ -6,49 +6,86 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
A sanity test for the built-in fixtures.
Launch the windows launcher attached to the currently installed instance.
"""
+# Import any dependencies for the test.
import logging
import pytest
+# Import any desired LTT modules from the package `ly_test_tools`. All LTT modules can be viewed at `Tools/LyTestTools/ly_test_tools`.
import ly_test_tools
+# The `launchers.launcher_helper` module helps create Launcher objects which control the Open 3D Engine (O3DE) Editor and game clients.
import ly_test_tools.launchers.launcher_helper as launcher_helper
+# The `builtin.helpers` module helps create the Workspace object, which controls the testing workspace in LTT.
import ly_test_tools.builtin.helpers as helpers
+# The `environment` module contains tools that involve the system's environment such as processes or timed waiters.
import ly_test_tools.environment.process_utils as process_utils
import ly_test_tools.environment.waiter as waiter
-pytestmark = pytest.mark.SUITE_smoke
-
+# Initialize a logger instance to hook all test logs together. The sub-logger pattern below makes it easy to track which file creates a log line.
logger = logging.getLogger(__name__)
# Note: For device testing, device ids must exist in ~/ly_test_tools/devices.ini, see README.txt for more info.
+# First define the class `TestAutomatedTestingProject` to group test functions together.
+# The example test contains two test functions: `test_StartGameLauncher_Sanity` and `test_StartEditor_Sanity`.
@pytest.mark.parametrize("project", ["AutomatedTesting"])
+# The example test utilizes Pytest parameterization. The following sets the `project` parameter to `AutomatedTesting`
+# for both test functions. Notice that the Pytest mark is defined at the class level to affect both test functions.
class TestAutomatedTestingProject(object):
+
def test_StartGameLauncher_Sanity(self, project):
+ """
+ The `test_StartGameLauncher_Sanity` test function verifies that the O3DE game client launches successfully.
+ Start the test by utilizing the `kill_processes_named` function to close any open O3DE processes that may
+ interfere with the test. The Workspace object emulates the O3DE package by locating the engine and project
+ directories. The Launcher object controls the O3DE game client and requires a Workspace object for
+ initialization. Add the `-rhi=Null` arg to the executable call to disable GPU rendering. This allows the
+ test to run on instances without a GPU. We launch the game client executable and wait for the process to exist.
+ A try/finally block ensures proper test cleanup if issues occur during the test.
+ """
+ # Kill processes that may interfere with the test
process_utils.kill_processes_named(names=process_utils.LY_PROCESS_KILL_LIST, ignore_extensions=True)
try:
+ # Create the Workspace object
workspace = helpers.create_builtin_workspace(project=project)
+ # Create the Launcher object and add args
launcher = launcher_helper.create_launcher(workspace)
- launcher.args.extend(['-NullRenderer', '-BatchMode'])
+ launcher.args.extend(['-rhi=Null'])
+ # Call the game client executable
with launcher.start():
+ # Wait for the process to exist
waiter.wait_for(lambda: process_utils.process_exists(f"{project}.GameLauncher.exe", ignore_extensions=True))
finally:
+ # Clean up processes after the test is finished
process_utils.kill_processes_named(names=process_utils.LY_PROCESS_KILL_LIST, ignore_extensions=True)
@pytest.mark.skipif(not ly_test_tools.WINDOWS, reason="Editor currently only functions on Windows")
def test_StartEditor_Sanity(self, project):
+ """
+ The `test_StartEditor_Sanity` test function is similar to the previous example with minor adjustments. A
+ PyTest mark skips the test if the operating system is not Windows. We use the `create_editor` function instead
+ of `create_launcher` to create an Editor type launcher instead of a game client type launcher. The additional
+ `-autotest_mode` arg supresses modal dialogs from interfering with our test. We launch the Editor executable and
+ wait for the process to exist.
+ """
+ # Kill processes that may interfere with the test
process_utils.kill_processes_named(names=process_utils.LY_PROCESS_KILL_LIST, ignore_extensions=True)
try:
+ # Create the Workspace object
workspace = helpers.create_builtin_workspace(project=project)
+ # Create the Launcher object and add args
editor = launcher_helper.create_editor(workspace)
- editor.args.extend(['-NullRenderer', '-autotest_mode'])
+ editor.args.extend(['-rhi=Null', '-autotest_mode'])
+ # Call the Editor executable
with editor.start():
+ # Wait for the process to exist
waiter.wait_for(lambda: process_utils.process_exists("Editor", ignore_extensions=True))
finally:
+ # Clean up processes after the test is finished
process_utils.kill_processes_named(names=process_utils.LY_PROCESS_KILL_LIST, ignore_extensions=True)
diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake
index 7ab6006220..20b7a7c81d 100644
--- a/cmake/Platform/Common/Install_common.cmake
+++ b/cmake/Platform/Common/Install_common.cmake
@@ -100,18 +100,29 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar
cmake_path(RELATIVE_PATH target_library_output_directory BASE_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} OUTPUT_VARIABLE target_library_output_subdirectory)
endif()
- install(
- TARGETS ${TARGET_NAME}
- ARCHIVE
- DESTINATION ${archive_output_directory}/${PAL_PLATFORM_NAME}/$
- COMPONENT ${install_component}
- LIBRARY
- DESTINATION ${library_output_directory}/${PAL_PLATFORM_NAME}/$/${target_library_output_subdirectory}
- COMPONENT ${install_component}
- RUNTIME
- DESTINATION ${runtime_output_directory}/${PAL_PLATFORM_NAME}/$/${target_runtime_output_subdirectory}
- COMPONENT ${install_component}
- )
+ if(COMMAND ly_install_target_override)
+ # Mac needs special handling because of a cmake issue
+ ly_install_target_override(TARGET ${TARGET_NAME}
+ ARCHIVE_DIR ${archive_output_directory}
+ LIBRARY_DIR ${library_output_directory}
+ RUNTIME_DIR ${runtime_output_directory}
+ LIBRARY_SUBDIR ${target_library_output_subdirectory}
+ RUNTIME_SUBDIR ${target_runtime_output_subdirectory}
+ )
+ else()
+ install(
+ TARGETS ${TARGET_NAME}
+ ARCHIVE
+ DESTINATION ${archive_output_directory}/${PAL_PLATFORM_NAME}/$
+ COMPONENT ${install_component}
+ LIBRARY
+ DESTINATION ${library_output_directory}/${PAL_PLATFORM_NAME}/$/${target_library_output_subdirectory}
+ COMPONENT ${install_component}
+ RUNTIME
+ DESTINATION ${runtime_output_directory}/${PAL_PLATFORM_NAME}/$/${target_runtime_output_subdirectory}
+ COMPONENT ${install_component}
+ )
+ endif()
# CMakeLists.txt file
string(REGEX MATCH "(.*)::(.*)$" match ${ALIAS_TARGET_NAME})
@@ -487,7 +498,7 @@ function(ly_setup_others)
# Scripts
file(GLOB o3de_scripts "${LY_ROOT_FOLDER}/scripts/o3de.*")
- install(FILES
+ install(PROGRAMS
${o3de_scripts}
DESTINATION ./scripts
)
@@ -505,6 +516,14 @@ function(ly_setup_others)
DESTINATION .
REGEX "downloaded_packages" EXCLUDE
REGEX "runtime" EXCLUDE
+ REGEX ".*$\.sh" EXCLUDE
+ )
+
+ # For Mac/Linux shell scripts need to be installed as PROGRAMS to have execute permission
+ file(GLOB python_scripts "${LY_ROOT_FOLDER}/python/*.sh")
+ install(PROGRAMS
+ ${python_scripts}
+ DESTINATION ./python
)
# Registry
diff --git a/cmake/Platform/Mac/Configurations_mac.cmake b/cmake/Platform/Mac/Configurations_mac.cmake
index a3551be3b9..bdb358e9d2 100644
--- a/cmake/Platform/Mac/Configurations_mac.cmake
+++ b/cmake/Platform/Mac/Configurations_mac.cmake
@@ -28,7 +28,9 @@ else()
endif()
# Signing
-ly_set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS --deep)
+# The "-o linker-signed" flag is required as a work-around for the following CMake issue:
+# https://gitlab.kitware.com/cmake/cmake/-/issues/21854
+ly_set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--deep -o linker-signed")
# Generate scheme files for Xcode
ly_set(CMAKE_XCODE_GENERATE_SCHEME TRUE)
diff --git a/cmake/Platform/Mac/Install_mac.cmake b/cmake/Platform/Mac/Install_mac.cmake
index 74ebd293ae..40f09a16b0 100644
--- a/cmake/Platform/Mac/Install_mac.cmake
+++ b/cmake/Platform/Mac/Install_mac.cmake
@@ -5,7 +5,47 @@
#
#
-# Empty implementations for untested platforms to fix build errors.
+#! ly_install_target_override: Mac specific target installation
+function(ly_install_target_override)
-function(ly_setup_o3de_install)
-endfunction()
\ No newline at end of file
+ set(options)
+ set(oneValueArgs TARGET ARCHIVE_DIR LIBRARY_DIR RUNTIME_DIR LIBRARY_SUBDIR RUNTIME_SUBDIR)
+ set(multiValueArgs)
+ cmake_parse_arguments(ly_platform_install_target "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ get_property(install_component TARGET ${ly_platform_install_target_TARGET} PROPERTY INSTALL_COMPONENT)
+
+ # For bundles on Mac, we set the icons by passing in a path to the Images.xcassets directory.
+ # However, the CMake install command expects paths to files for the the RESOURCE property.
+ # More details can be found in the CMake issue: https://gitlab.kitware.com/cmake/cmake/-/issues/22409
+ get_target_property(is_bundle ${ly_platform_install_target_TARGET} MACOSX_BUNDLE)
+ if (${is_bundle})
+ get_target_property(cached_resources_dir ${ly_platform_install_target_TARGET} RESOURCE)
+ set_property(TARGET ${ly_platform_install_target_TARGET} PROPERTY RESOURCE "")
+ endif()
+
+ install(
+ TARGETS ${ly_platform_install_target_TARGET}
+ ARCHIVE
+ DESTINATION ${ly_platform_install_target_ARCHIVE_DIR}/${PAL_PLATFORM_NAME}/$
+ COMPONENT ${install_component}
+ LIBRARY
+ DESTINATION ${ly_platform_install_target_LIBRARY_DIR}/${PAL_PLATFORM_NAME}/$/${ly_platform_install_target_LIBRARY_SUBDIR}
+ COMPONENT ${install_component}
+ RUNTIME
+ DESTINATION ${ly_platform_install_target_RUNTIME_DIR}/${PAL_PLATFORM_NAME}/$/${ly_platform_install_target_RUNTIME_SUBDIR}
+ COMPONENT ${install_component}
+ BUNDLE
+ DESTINATION ${ly_platform_install_target_RUNTIME_DIR}/${PAL_PLATFORM_NAME}/$/${ly_platform_install_target_RUNTIME_SUBDIR}
+ COMPONENT ${install_component}
+ RESOURCE
+ DESTINATION ${ly_platform_install_target_RUNTIME_DIR}/${PAL_PLATFORM_NAME}/$/${ly_platform_install_target_RUNTIME_SUBDIR}/
+ COMPONENT ${install_component}
+ )
+
+ if (${is_bundle})
+ set_property(TARGET ${ly_platform_install_target_TARGET} PROPERTY RESOURCE ${cached_resources_dir})
+ endif()
+endfunction()
+
+include(cmake/Platform/Common/Install_common.cmake)