Merge branch 'development' of https://github.com/o3de/o3de into sc-editor-asset-redux

This commit is contained in:
chcurran
2021-11-04 15:58:44 -07:00
337 changed files with 8195 additions and 4099 deletions
@@ -56,15 +56,21 @@ namespace AZ::ComponentApplicationLifecycle
}
bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler,
AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName)
AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName, bool autoRegisterEvent)
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
using Type = AZ::SettingsRegistryInterface::Type;
using NotifyEventHandler = AZ::SettingsRegistryInterface::NotifyEventHandler;
if (!ValidateEvent(settingsRegistry, eventName))
// Some systems may attempt to register a handler before the settings registry has been loaded
// If so, this flag lets them automatically register an event if it hasn't yet been registered.
// RegisterEvent calls validate event.
if ((!autoRegisterEvent && !ValidateEvent(settingsRegistry, eventName)) ||
(autoRegisterEvent && !RegisterEvent(settingsRegistry, eventName)))
{
AZ_Warning("ComponentApplicationLifecycle", false, R"(Cannot register event %.*s. Name does is not a field of object "%.*s".)"
AZ_Warning(
"ComponentApplicationLifecycle", false,
R"(Cannot register event %.*s. Name is not a field of object "%.*s".)"
R"( Please make sure the entry exists in the '<engine-root>/Registry/application_lifecycle_events.setreg")"
" or in *.setreg within the project", AZ_STRING_ARG(eventName), AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey));
return false;
@@ -14,7 +14,7 @@
namespace AZ::ComponentApplicationLifecycle
{
//! Root Key where lifecycle events should be registered under
inline constexpr AZStd::string_view ApplicationLifecycleEventRegistrationKey = "/O3DE/Runtime/Application/LifecycleEvents";
inline constexpr AZStd::string_view ApplicationLifecycleEventRegistrationKey = "/O3DE/Application/LifecycleEvents";
//! Validates that the event @eventName is stored in the array at ApplicationLifecycleEventRegistrationKey
@@ -48,7 +48,9 @@ namespace AZ::ComponentApplicationLifecycle
//! if the specified @eventName passes validation
//! @param callback will be moved into the handler if the specified @eventName is valid
//! @param eventName name of key underneath the ApplicationLifecycleEventRegistrationKey to register
//! @param autoRegisterEvent automatically register this event if it hasn't been registered yet. This is useful
//! when registering a handler before the settings registry has been loaded.
//! @return true if the handler was registered with the SettingsRegistry NotifyEvent
bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler,
AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName);
AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName, bool autoRegisterEvent = false);
}
@@ -7,4 +7,63 @@
*/
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Debug/ProfilerBus.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Settings/SettingsRegistry.h>
namespace AZ::Debug
{
AZStd::string GenerateOutputFile(const char* nameHint)
{
AZ::IO::FixedMaxPathString captureOutput = GetProfilerCaptureLocation();
return AZStd::string::format("%s/capture_%s_%lld.json", captureOutput.c_str(), nameHint, AZStd::GetTimeNowSecond());
}
void ProfilerCaptureFrame([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
if (auto profilerSystem = ProfilerSystemInterface::Get(); profilerSystem)
{
AZStd::string captureFile = GenerateOutputFile("single");
AZLOG_INFO("Setting capture file to %s", captureFile.c_str());
profilerSystem->CaptureFrame(captureFile);
}
}
AZ_CONSOLEFREEFUNC(ProfilerCaptureFrame, AZ::ConsoleFunctorFlags::DontReplicate, "Capture a single frame of profiling data");
void ProfilerStartCapture([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
if (auto profilerSystem = ProfilerSystemInterface::Get(); profilerSystem)
{
AZStd::string captureFile = GenerateOutputFile("multi");
AZLOG_INFO("Setting capture file to %s", captureFile.c_str());
profilerSystem->StartCapture(AZStd::move(captureFile));
}
}
AZ_CONSOLEFREEFUNC(ProfilerStartCapture, AZ::ConsoleFunctorFlags::DontReplicate, "Start a multi-frame capture of profiling data");
void ProfilerEndCapture([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
if (auto profilerSystem = ProfilerSystemInterface::Get(); profilerSystem)
{
profilerSystem->EndCapture();
}
}
AZ_CONSOLEFREEFUNC(ProfilerEndCapture, AZ::ConsoleFunctorFlags::DontReplicate, "End and dump an in-progress continuous capture");
AZ::IO::FixedMaxPathString GetProfilerCaptureLocation()
{
AZ::IO::FixedMaxPathString captureOutput;
if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry)
{
settingsRegistry->Get(captureOutput, RegistryKey_ProfilerCaptureLocation);
}
if (captureOutput.empty())
{
captureOutput = ProfilerCaptureLocationFallback;
}
return captureOutput;
}
} // namespace AZ::Debug
@@ -9,11 +9,20 @@
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
namespace Debug
{
//! settings registry entry for specifying where to output profiler captures
static constexpr const char* RegistryKey_ProfilerCaptureLocation = "/O3DE/AzCore/Debug/Profiler/CaptureLocation";
//! fallback value in the event the settings registry isn't ready or doesn't contain the key
static constexpr const char* ProfilerCaptureLocationFallback = "@user@/Profiler";
/**
* ProfilerNotifications provides a profiler event interface that can be used to update listeners on profiler status
*/
@@ -23,32 +32,38 @@ namespace AZ
public:
virtual ~ProfilerNotifications() = default;
virtual void OnProfileSystemInitialized() = 0;
//! Notify when the current profiler capture is finished
//! @param result Set to true if it's finished successfully
//! @param info The output file path or error information which depends on the return.
virtual void OnCaptureFinished(bool result, const AZStd::string& info) = 0;
};
using ProfilerNotificationBus = AZ::EBus<ProfilerNotifications>;
enum class ProfileFrameAdvanceType
{
Game,
Render,
Default = Game
};
/**
* ProfilerRequests provides an interface for making profiling system requests
*/
class ProfilerRequests
: public AZ::EBusTraits
{
public:
// Allow multiple threads to concurrently make requests
using MutexType = AZStd::mutex;
AZ_RTTI(ProfilerRequests, "{90AEC117-14C1-4BAE-9704-F916E49EF13F}");
virtual ~ProfilerRequests() = default;
virtual bool IsActive() = 0;
virtual void FrameAdvance(ProfileFrameAdvanceType type) = 0;
//! Getter/setter for the profiler active state
virtual bool IsActive() const = 0;
virtual void SetActive(bool active) = 0;
//! Capture a single frame of profiling data
virtual bool CaptureFrame(const AZStd::string& outputFilePath) = 0;
//! Starting/ending a multi-frame capture of profiling data
virtual bool StartCapture(AZStd::string outputFilePath) = 0;
virtual bool EndCapture() = 0;
};
using ProfilerRequestBus = AZ::EBus<ProfilerRequests>;
}
}
using ProfilerSystemInterface = AZ::Interface<ProfilerRequests>;
//! helper function for getting the profiler capture location from the settings registry that
//! includes fallback handing in the event the registry value can't be determined
AZ::IO::FixedMaxPathString GetProfilerCaptureLocation();
} // namespace Debug
} // namespace AZ
@@ -0,0 +1,92 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Debug/ProfilerBus.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/RTTI/BehaviorInterfaceProxy.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
namespace AZ::Debug
{
static constexpr const char* ProfilerScriptCategory = "Profiler";
static constexpr const char* ProfilerScriptModule = "debug";
static constexpr AZ::Script::Attributes::ScopeFlags ProfilerScriptScope = AZ::Script::Attributes::ScopeFlags::Automation;
class ProfilerNotificationBusHandler final
: public ProfilerNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
public:
AZ_EBUS_BEHAVIOR_BINDER(ProfilerNotificationBusHandler, "{44161459-B816-4876-95A4-BA16DEC767D6}", AZ::SystemAllocator,
OnCaptureFinished
);
void OnCaptureFinished(bool result, const AZStd::string& info) override
{
Call(FN_OnCaptureFinished, result, info);
}
static void Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<ProfilerNotificationBus>("ProfilerNotificationBus")
->Attribute(AZ::Script::Attributes::Category, ProfilerScriptCategory)
->Attribute(AZ::Script::Attributes::Module, ProfilerScriptModule)
->Attribute(AZ::Script::Attributes::Scope, ProfilerScriptScope)
->Handler<ProfilerNotificationBusHandler>();
}
}
};
class ProfilerSystemScriptProxy
: public BehaviorInterfaceProxy<ProfilerRequests>
{
public:
AZ_RTTI(ProfilerSystemScriptProxy, "{D671FB70-8B09-4C3A-96CD-06A339F3138E}", BehaviorInterfaceProxy<ProfilerRequests>);
AZ_BEHAVIOR_INTERFACE(ProfilerSystemScriptProxy, ProfilerRequests);
};
void ProfilerReflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->ConstantProperty("g_ProfilerSystem", ProfilerSystemScriptProxy::GetProxy)
->Attribute(AZ::Script::Attributes::Category, ProfilerScriptCategory)
->Attribute(AZ::Script::Attributes::Module, ProfilerScriptModule)
->Attribute(AZ::Script::Attributes::Scope, ProfilerScriptScope);
behaviorContext->Class<ProfilerSystemScriptProxy>("ProfilerSystemInterface")
->Attribute(AZ::Script::Attributes::Category, ProfilerScriptCategory)
->Attribute(AZ::Script::Attributes::Module, ProfilerScriptModule)
->Attribute(AZ::Script::Attributes::Scope, ProfilerScriptScope)
->Method("IsValid", &ProfilerSystemScriptProxy::IsValid)
->Method("GetCaptureLocation",
[](ProfilerSystemScriptProxy*) -> AZStd::string
{
AZ::IO::FixedMaxPathString captureOutput = GetProfilerCaptureLocation();
return AZStd::string(captureOutput.c_str(), captureOutput.length());
})
->Method("IsActive", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::IsActive>())
->Method("SetActive", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::SetActive>())
->Method("CaptureFrame", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::CaptureFrame>())
->Method("StartCapture", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::StartCapture>())
->Method("EndCapture", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::EndCapture>());
}
ProfilerNotificationBusHandler::Reflect(context);
}
} // namespace AZ::Debug
@@ -0,0 +1,19 @@
/*
* 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 ReflectContext;
namespace Debug
{
//! Reflects the profiler bus script bindings
void ProfilerReflect(AZ::ReflectContext* context);
} // namespace Debug
} // namespace AZ
+11 -16
View File
@@ -27,26 +27,21 @@
#include <AzCore/Console/IConsole.h>
#include <AzCore/std/chrono/chrono.h>
namespace AZ
namespace AZ::Debug
{
namespace Debug
struct StackFrame;
namespace Platform
{
struct StackFrame;
namespace Platform
{
#if defined(AZ_ENABLE_DEBUG_TOOLS)
bool AttachDebugger();
bool IsDebuggerPresent();
void HandleExceptions(bool isEnabled);
void DebugBreak();
bool AttachDebugger();
bool IsDebuggerPresent();
void HandleExceptions(bool isEnabled);
void DebugBreak();
#endif
void Terminate(int exitCode);
}
void Terminate(int exitCode);
}
using namespace AZ::Debug;
namespace DebugInternal
{
// other threads can trigger fatals and errors, but the same thread should not, to avoid stack overflow.
@@ -60,7 +55,7 @@ namespace AZ
// Globals
const int g_maxMessageLength = 4096;
static const char* g_dbgSystemWnd = "System";
Trace Debug::g_tracer;
Trace g_tracer;
void* g_exceptionInfo = nullptr;
// Environment var needed to track ignored asserts across systems and disable native UI under certain conditions
@@ -616,4 +611,4 @@ namespace AZ
val.Set(level);
}
}
} // namspace AZ
} // namspace AZ::Debug
@@ -156,7 +156,10 @@ namespace AZ
void StreamerComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
bool isEnabled = false;
AZ::Debug::ProfilerRequestBus::BroadcastResult(isEnabled, &AZ::Debug::ProfilerRequests::IsActive);
if (auto profilerSystem = AZ::Debug::ProfilerSystemInterface::Get(); profilerSystem)
{
isEnabled = profilerSystem->IsActive();
}
if (isEnabled)
{
@@ -383,36 +383,36 @@ namespace AZ
AZ_MATH_INLINE bool CmpAllEq(__m128 arg1, __m128 arg2, int32_t mask)
{
const __m128i compare = CastToInt(CmpNeq(arg1, arg2));
return (_mm_movemask_epi8(compare) & mask) == 0;
const __m128 compare = CmpEq(arg1, arg2);
return (_mm_movemask_ps(compare) & mask) == mask;
}
AZ_MATH_INLINE bool CmpAllLt(__m128 arg1, __m128 arg2, int32_t mask)
{
const __m128i compare = CastToInt(CmpGtEq(arg1, arg2));
return (_mm_movemask_epi8(compare) & mask) == 0;
const __m128 compare = CmpLt(arg1, arg2);
return (_mm_movemask_ps(compare) & mask) == mask;
}
AZ_MATH_INLINE bool CmpAllLtEq(__m128 arg1, __m128 arg2, int32_t mask)
{
const __m128i compare = CastToInt(CmpGt(arg1, arg2));
return (_mm_movemask_epi8(compare) & mask) == 0;
const __m128 compare = CmpLtEq(arg1, arg2);
return (_mm_movemask_ps(compare) & mask) == mask;
}
AZ_MATH_INLINE bool CmpAllGt(__m128 arg1, __m128 arg2, int32_t mask)
{
const __m128i compare = CastToInt(CmpLtEq(arg1, arg2));
return (_mm_movemask_epi8(compare) & mask) == 0;
const __m128 compare = CmpGt(arg1, arg2);
return (_mm_movemask_ps(compare) & mask) == mask;
}
AZ_MATH_INLINE bool CmpAllGtEq(__m128 arg1, __m128 arg2, int32_t mask)
{
const __m128i compare = CastToInt(CmpLt(arg1, arg2));
return (_mm_movemask_epi8(compare) & mask) == 0;
const __m128 compare = CmpGtEq(arg1, arg2);
return (_mm_movemask_ps(compare) & mask) == mask;
}
@@ -331,31 +331,32 @@ namespace AZ
AZ_MATH_INLINE bool Vec1::CmpAllEq(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllEq(arg1, arg2, 0x000F);
// Only check the first bit for Vector1
return Sse::CmpAllEq(arg1, arg2, 0b0001);
}
AZ_MATH_INLINE bool Vec1::CmpAllLt(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllLt(arg1, arg2, 0x000F);
return Sse::CmpAllLt(arg1, arg2, 0b0001);
}
AZ_MATH_INLINE bool Vec1::CmpAllLtEq(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllLtEq(arg1, arg2, 0x000F);
return Sse::CmpAllLtEq(arg1, arg2, 0b0001);
}
AZ_MATH_INLINE bool Vec1::CmpAllGt(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllGt(arg1, arg2, 0x000F);
return Sse::CmpAllGt(arg1, arg2, 0b0001);
}
AZ_MATH_INLINE bool Vec1::CmpAllGtEq(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllGtEq(arg1, arg2, 0x000F);
return Sse::CmpAllGtEq(arg1, arg2, 0b0001);
}
@@ -397,7 +398,7 @@ namespace AZ
AZ_MATH_INLINE bool Vec1::CmpAllEq(Int32ArgType arg1, Int32ArgType arg2)
{
return Sse::CmpAllEq(arg1, arg2, 0x000F);
return Sse::CmpAllEq(arg1, arg2, 0b0001);
}
@@ -383,31 +383,32 @@ namespace AZ
AZ_MATH_INLINE bool Vec2::CmpAllEq(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllEq(arg1, arg2, 0x00FF);
// Only check the first two bits for Vector2
return Sse::CmpAllEq(arg1, arg2, 0b0011);
}
AZ_MATH_INLINE bool Vec2::CmpAllLt(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllLt(arg1, arg2, 0x00FF);
return Sse::CmpAllLt(arg1, arg2, 0b0011);
}
AZ_MATH_INLINE bool Vec2::CmpAllLtEq(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllLtEq(arg1, arg2, 0x00FF);
return Sse::CmpAllLtEq(arg1, arg2, 0b0011);
}
AZ_MATH_INLINE bool Vec2::CmpAllGt(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllGt(arg1, arg2, 0x00FF);
return Sse::CmpAllGt(arg1, arg2, 0b0011);
}
AZ_MATH_INLINE bool Vec2::CmpAllGtEq(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllGtEq(arg1, arg2, 0x00FF);
return Sse::CmpAllGtEq(arg1, arg2, 0b0011);
}
@@ -419,31 +419,32 @@ namespace AZ
AZ_MATH_INLINE bool Vec3::CmpAllEq(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllEq(arg1, arg2, 0x0FFF);
// Only check the first three bits for Vector3
return Sse::CmpAllEq(arg1, arg2, 0b0111);
}
AZ_MATH_INLINE bool Vec3::CmpAllLt(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllLt(arg1, arg2, 0x0FFF);
return Sse::CmpAllLt(arg1, arg2, 0b0111);
}
AZ_MATH_INLINE bool Vec3::CmpAllLtEq(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllLtEq(arg1, arg2, 0x0FFF);
return Sse::CmpAllLtEq(arg1, arg2, 0b0111);
}
AZ_MATH_INLINE bool Vec3::CmpAllGt(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllGt(arg1, arg2, 0x0FFF);
return Sse::CmpAllGt(arg1, arg2, 0b0111);
}
AZ_MATH_INLINE bool Vec3::CmpAllGtEq(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllGtEq(arg1, arg2, 0x0FFF);
return Sse::CmpAllGtEq(arg1, arg2, 0b0111);
}
@@ -485,7 +486,7 @@ namespace AZ
AZ_MATH_INLINE bool Vec3::CmpAllEq(Int32ArgType arg1, Int32ArgType arg2)
{
return Sse::CmpAllEq(arg1, arg2, 0x0FFF);
return Sse::CmpAllEq(arg1, arg2, 0b0111);
}
@@ -455,31 +455,32 @@ namespace AZ
AZ_MATH_INLINE bool Vec4::CmpAllEq(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllEq(arg1, arg2, 0xFFFF);
// Check the first four bits for Vector4
return Sse::CmpAllEq(arg1, arg2, 0b1111);
}
AZ_MATH_INLINE bool Vec4::CmpAllLt(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllLt(arg1, arg2, 0xFFFF);
return Sse::CmpAllLt(arg1, arg2, 0b1111);
}
AZ_MATH_INLINE bool Vec4::CmpAllLtEq(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllLtEq(arg1, arg2, 0xFFFF);
return Sse::CmpAllLtEq(arg1, arg2, 0b1111);
}
AZ_MATH_INLINE bool Vec4::CmpAllGt(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllGt(arg1, arg2, 0xFFFF);
return Sse::CmpAllGt(arg1, arg2, 0b1111);
}
AZ_MATH_INLINE bool Vec4::CmpAllGtEq(FloatArgType arg1, FloatArgType arg2)
{
return Sse::CmpAllGtEq(arg1, arg2, 0xFFFF);
return Sse::CmpAllGtEq(arg1, arg2, 0b1111);
}
@@ -521,7 +522,7 @@ namespace AZ
AZ_MATH_INLINE bool Vec4::CmpAllEq(Int32ArgType arg1, Int32ArgType arg2)
{
return Sse::CmpAllEq(arg1, arg2, 0xFFFF);
return Sse::CmpAllEq(arg1, arg2, 0b1111);
}
@@ -0,0 +1,126 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Interface/Interface.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/typetraits/function_traits.h>
namespace AZ
{
/**
* Utility class for reflecting an AZ::Interface through the BehaviorContext
*
* Example:
*
* class MyInterface
* {
* public:
* AZ_RTTI(MyInterface, "{BADDF000D-CDCD-CDCD-CDCD-BAAAADF0000D}");
* virtual ~MyInterface() = default;
*
* virtual AZStd::string Foo() = 0;
* virtual void Bar(float x, float y) = 0;
* };
*
* class MySystemProxy
* : public BehaviorInterfaceProxy<MyInterface>
* {
* public:
* AZ_RTTI(MySystemProxy, "{CDCDCDCD-BAAD-BADD-F00D-CDCDCDCDCDCD}", BehaviorInterfaceProxy<MyInterface>);
* AZ_BEHAVIOR_INTERFACE(MySystemProxy, MyInterface);
* };
*
* void Reflect(AZ::ReflectContext* context)
* {
* if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
* {
* behaviorContext->ConstantProperty("g_MySystem", MySystemProxy::GetProxy)
* ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
* ->Attribute(AZ::Script::Attributes::Module, "MyModule");
*
* behaviorContext->Class<MySystemProxy>("MySystemInterface")
* ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
* ->Attribute(AZ::Script::Attributes::Module, "MyModule")
*
* ->Method("Foo", MySystemProxy::WrapMethod<&MyInterface::Foo>())
* ->Method("Bar", MySystemProxy::WrapMethod<&MyInterface::Bar>());
* }
* }
*/
template<typename T>
class BehaviorInterfaceProxy
{
public:
AZ_CLASS_ALLOCATOR(BehaviorInterfaceProxy, AZ::SystemAllocator, 0);
AZ_RTTI(BehaviorInterfaceProxy<T>, "{E7CC8D27-4499-454E-A7DF-3F72FBECD30D}");
BehaviorInterfaceProxy() = default;
virtual ~BehaviorInterfaceProxy() = default;
//! Stores the instance which will use the provided shared_ptr deleter when the reference count hits zero
BehaviorInterfaceProxy(AZStd::shared_ptr<T> sharedInstance)
: m_instance(AZStd::move(sharedInstance))
{
}
//! Stores the instance which will perform a no-op deleter when the reference count hits zero
BehaviorInterfaceProxy(T* rawIntance)
: m_instance(rawIntance, [](T*) {})
{
}
//! Returns if the m_instance shared pointer is non-nullptr
bool IsValid() const { return m_instance; }
protected:
//! Internal access for use in the derived GetProxy function
static T* GetInstance()
{
T* interfacePtr = AZ::Interface<T>::Get();
AZ_Warning("BehaviorInterfaceProxy", interfacePtr,
"There is currently no global %s registered with an AZ Interface<T>",
AzTypeInfo<T>::Name()
);
// Don't delete the global instance, it is not owned by the behavior context
return interfacePtr;
}
template<typename... Args>
struct MethodWrapper
{
template<typename Proxy, auto Method>
static auto WrapMethod()
{
using ReturnType = AZStd::function_traits_get_result_t<AZStd::remove_cvref_t<decltype(Method)>>;
return [](Proxy* proxy, Args... params) -> ReturnType
{
if (proxy && proxy->IsValid())
{
return AZStd::invoke(Method, proxy->m_instance, AZStd::forward<Args>(params)...);
}
return ReturnType();
};
}
};
AZStd::shared_ptr<T> m_instance;
};
#define AZ_BEHAVIOR_INTERFACE(ProxyType, InterfaceType) \
static ProxyType GetProxy() { return GetInstance(); } \
template<auto Method> \
static auto WrapMethod() { \
using FuncTraits = AZStd::function_traits<AZStd::remove_cvref_t<decltype(Method)>>; \
return FuncTraits::template expand_args<MethodWrapper>::template WrapMethod<ProxyType, Method>(); \
} \
ProxyType() = default; \
ProxyType(AZStd::shared_ptr<InterfaceType> sharedInstance) : BehaviorInterfaceProxy(sharedInstance) {} \
ProxyType(InterfaceType* rawIntance) : BehaviorInterfaceProxy(rawIntance) {}
} // namespace AZ
@@ -14,6 +14,7 @@
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Debug/ProfilerReflection.h>
#include <AzCore/Debug/TraceReflection.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/Math/MathReflection.h>
@@ -87,6 +88,8 @@ void ScriptSystemComponent::Activate()
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::AddExtension, "lua");
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::AddExtension, "luac");
AZ::Data::AssetCatalogRequestBus::Broadcast(
&AZ::Data::AssetCatalogRequests::EnableCatalogForAsset, AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid());
if (Data::AssetManager::Instance().IsReady())
{
@@ -925,6 +928,7 @@ void ScriptSystemComponent::Reflect(ReflectContext* reflection)
// reflect default entity
MathReflect(behaviorContext);
ScriptDebug::Reflect(behaviorContext);
Debug::ProfilerReflect(behaviorContext);
Debug::TraceReflect(behaviorContext);
behaviorContext->Class<PlatformID>("Platform")
@@ -363,7 +363,11 @@ namespace AZ
{
++m_graphsRemaining;
event->m_executor = this; // Used to validate event is not waited for inside a job
if (event)
{
event->IncWaitCount();
event->m_executor = this; // Used to validate event is not waited for inside a job
}
// Submit all tasks that have no inbound edges
for (Internal::Task& task : graph.Tasks())
@@ -20,6 +20,46 @@ namespace AZ
m_semaphore.acquire();
}
void TaskGraphEvent::IncWaitCount()
{
// guess zero to optimize for single task graph using an event, if multiple are using it then this will take 2+ comp_exch calls
int expectedValue = 0;
while(!m_waitCount.compare_exchange_weak(expectedValue, expectedValue + 1))
{
// value will be negative once event is ready to signal or has been signaled. Shouldn't happen.
AZ_Assert(expectedValue >= 0, "Called TaskGraphEvent::IncWaitCount on a signalled event");
if (expectedValue < 0) // event already signaled, skip
{
return;
}
};
}
void TaskGraphEvent::Signal()
{
// guess one to optimize for single task graph using an event, if multiple are using it then this will take 2+ comp_exch calls
int expectedValue = 1;
while(!m_waitCount.compare_exchange_weak(expectedValue, expectedValue - 1))
{
// It's an error for Signal to be called if no one is waiting, or the event has already been signaled
AZ_Assert(expectedValue > 0, "Called TaskGraphEvent::Signal when event is either signaled or unused");
if (expectedValue < 0) // return if already signaled
{
return;
}
};
if (expectedValue == 1) // This call to Signal decremented the value to 0.
{
expectedValue = 0;
// validate no one incremented the wait count and mark signalling state
if (m_waitCount.compare_exchange_strong(expectedValue, -1))
{
m_semaphore.release();
}
}
}
void TaskToken::PrecedesInternal(TaskToken& comesAfter)
{
AZ_Assert(!m_parent.m_submitted, "Cannot mutate a TaskGraph that was previously submitted.");
@@ -61,14 +61,14 @@ namespace AZ
uint32_t m_index;
};
// A TaskGraphEvent may be used to block until a task graph has finished executing. Usage
// A TaskGraphEvent may be used to block until one or more task graphs has finished executing. Usage
// is NOT recommended for the majority of tasks (prefer to simply containing expanding/contracting
// the graph without synchronization over the course of the frame). However, the event
// is useful for the edges of the computation graph.
//
// You are responsible for ensuring the event object lifetime exceeds the task graph lifetime.
//
// After the TaskGraphEvent is signaled, you are allowed to reuse the same TaskGraphEvent
// After the TaskGraphEvent is signaled, you are NOT allowed to reuse the same TaskGraphEvent
// for a future submission.
class TaskGraphEvent
{
@@ -81,10 +81,12 @@ namespace AZ
friend class TaskGraph;
friend class TaskExecutor;
void IncWaitCount();
void Signal();
AZStd::binary_semaphore m_semaphore;
TaskExecutor* m_executor = nullptr;
AZStd::atomic_int m_waitCount = 0;
TaskExecutor* m_executor = nullptr;
};
// The TaskGraph encapsulates a set of tasks and their interdependencies. After adding
@@ -33,11 +33,6 @@ namespace AZ
return m_semaphore.try_acquire_for(AZStd::chrono::milliseconds{ 0 });
}
inline void TaskGraphEvent::Signal()
{
m_semaphore.release();
}
template<typename Lambda>
TaskToken TaskGraph::AddTask(TaskDescriptor const& desc, Lambda&& lambda)
{
@@ -108,6 +108,8 @@ set(FILES
Debug/Profiler.inl
Debug/Profiler.h
Debug/ProfilerBus.h
Debug/ProfilerReflection.cpp
Debug/ProfilerReflection.h
Debug/StackTracer.h
Debug/EventTrace.h
Debug/EventTrace.cpp
@@ -456,6 +458,7 @@ set(FILES
RTTI/BehaviorContext.h
RTTI/BehaviorContextUtilities.h
RTTI/BehaviorContextUtilities.cpp
RTTI/BehaviorInterfaceProxy.h
RTTI/BehaviorObjectSignals.h
RTTI/TypeSafeIntegral.h
Script/ScriptAsset.cpp
@@ -17,7 +17,7 @@
#include <stdio.h>
namespace AZ
namespace AZ::Debug
{
#if defined(AZ_ENABLE_DEBUG_TOOLS)
LONG WINAPI ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo);
@@ -26,94 +26,91 @@ namespace AZ
constexpr int g_maxMessageLength = 4096;
namespace Debug
namespace Platform
{
namespace Platform
{
#if defined(AZ_ENABLE_DEBUG_TOOLS)
bool IsDebuggerPresent()
bool IsDebuggerPresent()
{
return ::IsDebuggerPresent() ? true : false;
}
void HandleExceptions(bool isEnabled)
{
if (isEnabled)
{
return ::IsDebuggerPresent() ? true : false;
g_previousExceptionHandler = ::SetUnhandledExceptionFilter(&ExceptionHandler);
}
void HandleExceptions(bool isEnabled)
else
{
if (isEnabled)
{
g_previousExceptionHandler = ::SetUnhandledExceptionFilter(&ExceptionHandler);
}
else
{
::SetUnhandledExceptionFilter(g_previousExceptionHandler);
g_previousExceptionHandler = NULL;
}
}
bool AttachDebugger()
{
if (IsDebuggerPresent())
{
return true;
}
// Launch vsjitdebugger.exe, this app is always present in System32 folder
// with an installation of any version of visual studio.
// It will open a debugging dialog asking the user what debugger to use
STARTUPINFOW startupInfo = {0};
startupInfo.cb = sizeof(startupInfo);
PROCESS_INFORMATION processInfo = {0};
wchar_t cmdline[MAX_PATH];
swprintf_s(cmdline, L"vsjitdebugger.exe -p %li", ::GetCurrentProcessId());
bool success = ::CreateProcessW(
NULL, // No module name (use command line)
cmdline, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // No handle inheritance
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&startupInfo, // Pointer to STARTUPINFO structure
&processInfo); // Pointer to PROCESS_INFORMATION structure
if (success)
{
::WaitForSingleObject(processInfo.hProcess, INFINITE);
::CloseHandle(processInfo.hProcess);
::CloseHandle(processInfo.hThread);
return true;
}
return false;
}
void DebugBreak()
{
__debugbreak();
}
#endif // AZ_ENABLE_DEBUG_TOOLS
void Terminate(int exitCode)
{
TerminateProcess(GetCurrentProcess(), exitCode);
}
void OutputToDebugger([[maybe_unused]] const char* window, const char* message)
{
AZStd::fixed_wstring<g_maxMessageLength> tmpW;
if(window)
{
AZStd::to_wstring(tmpW, window);
tmpW += L": ";
OutputDebugStringW(tmpW.c_str());
tmpW.clear();
}
AZStd::to_wstring(tmpW, message);
OutputDebugStringW(tmpW.c_str());
::SetUnhandledExceptionFilter(g_previousExceptionHandler);
g_previousExceptionHandler = NULL;
}
}
}
bool AttachDebugger()
{
if (IsDebuggerPresent())
{
return true;
}
// Launch vsjitdebugger.exe, this app is always present in System32 folder
// with an installation of any version of visual studio.
// It will open a debugging dialog asking the user what debugger to use
STARTUPINFOW startupInfo = {0};
startupInfo.cb = sizeof(startupInfo);
PROCESS_INFORMATION processInfo = {0};
wchar_t cmdline[MAX_PATH];
swprintf_s(cmdline, L"vsjitdebugger.exe -p %li", ::GetCurrentProcessId());
bool success = ::CreateProcessW(
NULL, // No module name (use command line)
cmdline, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // No handle inheritance
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&startupInfo, // Pointer to STARTUPINFO structure
&processInfo); // Pointer to PROCESS_INFORMATION structure
if (success)
{
::WaitForSingleObject(processInfo.hProcess, INFINITE);
::CloseHandle(processInfo.hProcess);
::CloseHandle(processInfo.hThread);
return true;
}
return false;
}
void DebugBreak()
{
__debugbreak();
}
#endif // AZ_ENABLE_DEBUG_TOOLS
void Terminate(int exitCode)
{
TerminateProcess(GetCurrentProcess(), exitCode);
}
void OutputToDebugger([[maybe_unused]] const char* window, const char* message)
{
AZStd::fixed_wstring<g_maxMessageLength> tmpW;
if(window)
{
AZStd::to_wstring(tmpW, window);
tmpW += L": ";
OutputDebugStringW(tmpW.c_str());
tmpW.clear();
}
AZStd::to_wstring(tmpW, message);
OutputDebugStringW(tmpW.c_str());
}
} // namespace Platform
#if defined(AZ_ENABLE_DEBUG_TOOLS)
@@ -187,6 +184,8 @@ namespace AZ
azsnprintf(message, g_maxMessageLength, "Exception : 0x%lX - '%s' [%p]\n", ExceptionInfo->ExceptionRecord->ExceptionCode, GetExeptionName(ExceptionInfo->ExceptionRecord->ExceptionCode), ExceptionInfo->ExceptionRecord->ExceptionAddress);
Debug::Trace::Instance().Output(nullptr, message);
Debug::Trace::Instance().PrintCallstack(nullptr, 0, ExceptionInfo->ContextRecord);
EBUS_EVENT(Debug::TraceMessageDrillerBus, OnException, message);
bool result = false;
@@ -198,7 +197,7 @@ namespace AZ
// if someone ever returns TRUE we assume that they somehow handled this exception and continue.
return EXCEPTION_CONTINUE_EXECUTION;
}
Debug::Trace::Instance().PrintCallstack(nullptr, 0, ExceptionInfo->ContextRecord);
Debug::Trace::Instance().Output(nullptr, "==================================================================\n");
// allowing continue of execution is not valid here. This handler gets called for serious exceptions.
@@ -211,4 +210,4 @@ namespace AZ
}
#endif
}
} // namspace AZ::Debug
+6 -5
View File
@@ -610,15 +610,16 @@ namespace UnitTest
g.Follows(e, f);
g.Precedes(d);
TaskGraphEvent ev;
graph.SubmitOnExecutor(*m_executor, &ev);
ev.Wait();
TaskGraphEvent ev1;
graph.SubmitOnExecutor(*m_executor, &ev1);
ev1.Wait();
EXPECT_EQ(3 | 0b100000, x);
x = 0;
graph.SubmitOnExecutor(*m_executor, &ev);
ev.Wait();
TaskGraphEvent ev2;
graph.SubmitOnExecutor(*m_executor, &ev2);
ev2.Wait();
EXPECT_EQ(3 | 0b100000, x);
}