[development] Consolidated programmatic profiler capture API (#4969)
Merged overlapping profiler EBuses/Interfaces into AzCore Fixed ambiguous LogLevel type in some unity file scenarios Added cvar/console access for profiler capture Added utilities for reflecting AZ::Interfaces through the BehaviorContext Included profiler system sample python script Signed-off-by: AMZN-ScottR 24445312+AMZN-ScottR@users.noreply.github.commonroegm-disable-blank-issue-2
commit
797b8248e5
@ -0,0 +1,7 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||||
|
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
#
|
||||||
|
#
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||||
|
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
import azlmbr.debug as debug
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
def test_profiler_system():
|
||||||
|
if not debug.g_ProfilerSystem.IsValid():
|
||||||
|
print('g_ProfilerSystem is INVALID')
|
||||||
|
return
|
||||||
|
|
||||||
|
state = 'ACTIVE' if debug.g_ProfilerSystem.IsActive() else 'INACTIVE'
|
||||||
|
print(f'Profiler system is currently {state}')
|
||||||
|
|
||||||
|
capture_location = pathlib.Path(debug.g_ProfilerSystem.GetCaptureLocation())
|
||||||
|
print(f'Capture location set to {capture_location}')
|
||||||
|
|
||||||
|
print('Capturing single frame...' )
|
||||||
|
capture_file = str(capture_location / 'script_capture_frame.json')
|
||||||
|
debug.g_ProfilerSystem.CaptureFrame(capture_file)
|
||||||
|
|
||||||
|
# Invoke main function
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test_profiler_system()
|
||||||
@ -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
|
||||||
@ -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
|
||||||
@ -1,59 +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
|
|
||||||
|
|
||||||
#include <AzCore/EBus/EBus.h>
|
|
||||||
#include <AzCore/Interface/Interface.h>
|
|
||||||
#include <AzCore/std/string/string.h>
|
|
||||||
|
|
||||||
namespace Profiler
|
|
||||||
{
|
|
||||||
class ProfilerRequests
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AZ_RTTI(ProfilerRequests, "{3757c4e5-1941-457c-85ae-16305e17a4c6}");
|
|
||||||
virtual ~ProfilerRequests() = default;
|
|
||||||
|
|
||||||
//! Enable/Disable the CpuProfiler
|
|
||||||
virtual void SetProfilerEnabled(bool enabled) = 0;
|
|
||||||
|
|
||||||
//! Dump a single frame of Cpu profiling data
|
|
||||||
virtual bool CaptureCpuProfilingStatistics(const AZStd::string& outputFilePath) = 0;
|
|
||||||
|
|
||||||
//! Start a multiframe capture of CPU profiling data.
|
|
||||||
virtual bool BeginContinuousCpuProfilingCapture() = 0;
|
|
||||||
|
|
||||||
//! End and dump an in-progress continuous capture.
|
|
||||||
virtual bool EndContinuousCpuProfilingCapture(const AZStd::string& outputFilePath) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ProfilerBusTraits
|
|
||||||
: public AZ::EBusTraits
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// EBusTraits overrides
|
|
||||||
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
|
||||||
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ProfilerNotifications
|
|
||||||
: public AZ::EBusTraits
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~ProfilerNotifications() = default;
|
|
||||||
|
|
||||||
//! Notify when the current CpuProfilingStatistics 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 OnCaptureCpuProfilingStatisticsFinished(bool result, const AZStd::string& info) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
using ProfilerInterface = AZ::Interface<ProfilerRequests>;
|
|
||||||
using ProfilerRequestBus = AZ::EBus<ProfilerRequests, ProfilerBusTraits>;
|
|
||||||
using ProfilerNotificationBus = AZ::EBus<ProfilerNotifications>;
|
|
||||||
} // namespace Profiler
|
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"O3DE":
|
||||||
|
{
|
||||||
|
"AzCore":
|
||||||
|
{
|
||||||
|
"Debug":
|
||||||
|
{
|
||||||
|
"Profiler":
|
||||||
|
{
|
||||||
|
"CaptureLocation" : "@user@/Profiler"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue