Merge branch 'development' into memory/benchmarks

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2021-12-07 15:47:29 -08:00
1784 changed files with 25936 additions and 108873 deletions
@@ -10,6 +10,8 @@
#include "EditorPreferencesPageViewportManipulator.h"
#include <AzToolsFramework/Viewport/ViewportSettings.h>
// Editor
#include "EditorViewportSettings.h"
#include "Settings.h"
@@ -19,7 +21,17 @@ void CEditorPreferencesPage_ViewportManipulator::Reflect(AZ::SerializeContext& s
serialize.Class<Manipulators>()
->Version(1)
->Field("LineBoundWidth", &Manipulators::m_manipulatorLineBoundWidth)
->Field("CircleBoundWidth", &Manipulators::m_manipulatorCircleBoundWidth);
->Field("CircleBoundWidth", &Manipulators::m_manipulatorCircleBoundWidth)
->Field("LinearManipulatorAxisLength", &Manipulators::m_linearManipulatorAxisLength)
->Field("PlanarManipulatorAxisLength", &Manipulators::m_planarManipulatorAxisLength)
->Field("SurfaceManipulatorRadius", &Manipulators::m_surfaceManipulatorRadius)
->Field("SurfaceManipulatorOpacity", &Manipulators::m_surfaceManipulatorOpacity)
->Field("LinearManipulatorConeLength", &Manipulators::m_linearManipulatorConeLength)
->Field("LinearManipulatorConeRadius", &Manipulators::m_linearManipulatorConeRadius)
->Field("ScaleManipulatorBoxHalfExtent", &Manipulators::m_scaleManipulatorBoxHalfExtent)
->Field("RotationManipulatorRadius", &Manipulators::m_rotationManipulatorRadius)
->Field("ManipulatorViewBaseScale", &Manipulators::m_manipulatorViewBaseScale)
->Field("FlipManipulatorAxesTowardsView", &Manipulators::m_flipManipulatorAxesTowardsView);
serialize.Class<CEditorPreferencesPage_ViewportManipulator>()->Version(2)->Field(
"Manipulators", &CEditorPreferencesPage_ViewportManipulator::m_manipulators);
@@ -36,7 +48,55 @@ void CEditorPreferencesPage_ViewportManipulator::Reflect(AZ::SerializeContext& s
AZ::Edit::UIHandlers::SpinBox, &Manipulators::m_manipulatorCircleBoundWidth, "Circle Bound Width",
"Manipulator Circle Bound Width")
->Attribute(AZ::Edit::Attributes::Min, 0.001f)
->Attribute(AZ::Edit::Attributes::Max, 2.0f);
->Attribute(AZ::Edit::Attributes::Max, 2.0f)
->DataElement(
AZ::Edit::UIHandlers::SpinBox, &Manipulators::m_linearManipulatorAxisLength, "Linear Manipulator Axis Length",
"Length of default Linear Manipulator (for Translation and Scale Manipulators)")
->Attribute(AZ::Edit::Attributes::Min, 0.1f)
->Attribute(AZ::Edit::Attributes::Max, 5.0f)
->DataElement(
AZ::Edit::UIHandlers::SpinBox, &Manipulators::m_planarManipulatorAxisLength, "Planar Manipulator Axis Length",
"Length of default Planar Manipulator (for Translation Manipulators)")
->Attribute(AZ::Edit::Attributes::Min, 0.1f)
->Attribute(AZ::Edit::Attributes::Max, 5.0f)
->DataElement(
AZ::Edit::UIHandlers::SpinBox, &Manipulators::m_surfaceManipulatorRadius, "Surface Manipulator Radius",
"Radius of default Surface Manipulator (for Translation Manipulators)")
->Attribute(AZ::Edit::Attributes::Min, 0.05f)
->Attribute(AZ::Edit::Attributes::Max, 1.0f)
->DataElement(
AZ::Edit::UIHandlers::SpinBox, &Manipulators::m_surfaceManipulatorOpacity, "Surface Manipulator Opacity",
"Opacity of default Surface Manipulator (for Translation Manipulators)")
->Attribute(AZ::Edit::Attributes::Min, 0.01f)
->Attribute(AZ::Edit::Attributes::Max, 1.0f)
->DataElement(
AZ::Edit::UIHandlers::SpinBox, &Manipulators::m_linearManipulatorConeLength, "Linear Manipulator Cone Length",
"Length of cone for default Linear Manipulator (for Translation Manipulators)")
->Attribute(AZ::Edit::Attributes::Min, 0.05f)
->Attribute(AZ::Edit::Attributes::Max, 1.0f)
->DataElement(
AZ::Edit::UIHandlers::SpinBox, &Manipulators::m_linearManipulatorConeRadius, "Linear Manipulator Cone Radius",
"Radius of cone for default Linear Manipulator (for Translation Manipulators)")
->Attribute(AZ::Edit::Attributes::Min, 0.05f)
->Attribute(AZ::Edit::Attributes::Max, 0.5f)
->DataElement(
AZ::Edit::UIHandlers::SpinBox, &Manipulators::m_scaleManipulatorBoxHalfExtent, "Scale Manipulator Box Half Extent",
"Half extent of box for default Scale Manipulator")
->Attribute(AZ::Edit::Attributes::Min, 0.05f)
->Attribute(AZ::Edit::Attributes::Max, 1.0f)
->DataElement(
AZ::Edit::UIHandlers::SpinBox, &Manipulators::m_rotationManipulatorRadius, "Rotation Manipulator Radius",
"Radius of default Angular Manipulators (for Rotation Manipulators)")
->Attribute(AZ::Edit::Attributes::Min, 0.5f)
->Attribute(AZ::Edit::Attributes::Max, 5.0f)
->DataElement(
AZ::Edit::UIHandlers::SpinBox, &Manipulators::m_manipulatorViewBaseScale, "Manipulator View Base Scale",
"The base scale to apply to all Manipulator Views (default is 1.0)")
->Attribute(AZ::Edit::Attributes::Min, 0.5f)
->Attribute(AZ::Edit::Attributes::Max, 2.0f)
->DataElement(
AZ::Edit::UIHandlers::CheckBox, &Manipulators::m_flipManipulatorAxesTowardsView, "Flip Manipulator Axes Towards View",
"Determines whether Planar and Linear Manipulators should switch to face the view (camera) in the Editor");
editContext
->Class<CEditorPreferencesPage_ViewportManipulator>("Manipulator Viewport Preferences", "Manipulator Viewport Preferences")
@@ -82,10 +142,32 @@ void CEditorPreferencesPage_ViewportManipulator::OnApply()
{
SandboxEditor::SetManipulatorLineBoundWidth(m_manipulators.m_manipulatorLineBoundWidth);
SandboxEditor::SetManipulatorCircleBoundWidth(m_manipulators.m_manipulatorCircleBoundWidth);
AzToolsFramework::SetLinearManipulatorAxisLength(m_manipulators.m_linearManipulatorAxisLength);
AzToolsFramework::SetPlanarManipulatorAxisLength(m_manipulators.m_planarManipulatorAxisLength);
AzToolsFramework::SetSurfaceManipulatorRadius(m_manipulators.m_surfaceManipulatorRadius);
AzToolsFramework::SetSurfaceManipulatorOpacity(m_manipulators.m_surfaceManipulatorOpacity);
AzToolsFramework::SetLinearManipulatorConeLength(m_manipulators.m_linearManipulatorConeLength);
AzToolsFramework::SetLinearManipulatorConeRadius(m_manipulators.m_linearManipulatorConeRadius);
AzToolsFramework::SetScaleManipulatorBoxHalfExtent(m_manipulators.m_scaleManipulatorBoxHalfExtent);
AzToolsFramework::SetRotationManipulatorRadius(m_manipulators.m_rotationManipulatorRadius);
AzToolsFramework::SetFlipManipulatorAxesTowardsView(m_manipulators.m_flipManipulatorAxesTowardsView);
AzToolsFramework::SetManipulatorViewBaseScale(m_manipulators.m_manipulatorViewBaseScale);
}
void CEditorPreferencesPage_ViewportManipulator::InitializeSettings()
{
m_manipulators.m_manipulatorLineBoundWidth = SandboxEditor::ManipulatorLineBoundWidth();
m_manipulators.m_manipulatorCircleBoundWidth = SandboxEditor::ManipulatorCircleBoundWidth();
m_manipulators.m_linearManipulatorAxisLength = AzToolsFramework::LinearManipulatorAxisLength();
m_manipulators.m_planarManipulatorAxisLength = AzToolsFramework::PlanarManipulatorAxisLength();
m_manipulators.m_surfaceManipulatorRadius = AzToolsFramework::SurfaceManipulatorRadius();
m_manipulators.m_surfaceManipulatorOpacity = AzToolsFramework::SurfaceManipulatorOpacity();
m_manipulators.m_linearManipulatorConeLength = AzToolsFramework::LinearManipulatorConeLength();
m_manipulators.m_linearManipulatorConeRadius = AzToolsFramework::LinearManipulatorConeRadius();
m_manipulators.m_scaleManipulatorBoxHalfExtent = AzToolsFramework::ScaleManipulatorBoxHalfExtent();
m_manipulators.m_rotationManipulatorRadius = AzToolsFramework::RotationManipulatorRadius();
m_manipulators.m_flipManipulatorAxesTowardsView = AzToolsFramework::FlipManipulatorAxesTowardsView();
m_manipulators.m_manipulatorViewBaseScale = AzToolsFramework::ManipulatorViewBaseScale();
}
@@ -41,6 +41,16 @@ private:
float m_manipulatorLineBoundWidth = 0.0f;
float m_manipulatorCircleBoundWidth = 0.0f;
float m_linearManipulatorAxisLength = 0.0f;
float m_planarManipulatorAxisLength = 0.0f;
float m_surfaceManipulatorRadius = 0.0f;
float m_surfaceManipulatorOpacity = 0.0f;
float m_linearManipulatorConeLength = 0.0f;
float m_linearManipulatorConeRadius = 0.0f;
float m_scaleManipulatorBoxHalfExtent = 0.0f;
float m_rotationManipulatorRadius = 0.0f;
float m_manipulatorViewBaseScale = 0.0f;
bool m_flipManipulatorAxesTowardsView = false;
};
Manipulators m_manipulators;
+93 -107
View File
@@ -12,6 +12,7 @@
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/std/string/string_view.h>
#include <AzToolsFramework/Viewport/ViewportSettings.h>
namespace SandboxEditor
{
@@ -57,31 +58,6 @@ namespace SandboxEditor
constexpr AZStd::string_view CameraDefaultStartingPositionY = "/Amazon/Preferences/Editor/Camera/DefaultStartingPosition/y";
constexpr AZStd::string_view CameraDefaultStartingPositionZ = "/Amazon/Preferences/Editor/Camera/DefaultStartingPosition/z";
template<typename T>
void SetRegistry(const AZStd::string_view setting, T&& value)
{
if (auto* registry = AZ::SettingsRegistry::Get())
{
registry->Set(setting, AZStd::forward<T>(value));
}
}
template<typename T>
AZStd::remove_cvref_t<T> GetRegistry(const AZStd::string_view setting, T&& defaultValue)
{
AZStd::remove_cvref_t<T> value = AZStd::forward<T>(defaultValue);
if (const auto* registry = AZ::SettingsRegistry::Get())
{
T potentialValue;
if (registry->Get(potentialValue, setting))
{
value = AZStd::move(potentialValue);
}
}
return value;
}
struct EditorViewportSettingsCallbacksImpl : public EditorViewportSettingsCallbacks
{
EditorViewportSettingsCallbacksImpl()
@@ -118,399 +94,409 @@ namespace SandboxEditor
AZ::Vector3 CameraDefaultEditorPosition()
{
return AZ::Vector3(
aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionX, 0.0)),
aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionY, -10.0)),
aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionZ, 4.0)));
aznumeric_cast<float>(AzToolsFramework::GetRegistry(CameraDefaultStartingPositionX, 0.0)),
aznumeric_cast<float>(AzToolsFramework::GetRegistry(CameraDefaultStartingPositionY, -10.0)),
aznumeric_cast<float>(AzToolsFramework::GetRegistry(CameraDefaultStartingPositionZ, 4.0)));
}
void SetCameraDefaultEditorPosition(const AZ::Vector3& defaultCameraPosition)
{
SetRegistry(CameraDefaultStartingPositionX, defaultCameraPosition.GetX());
SetRegistry(CameraDefaultStartingPositionY, defaultCameraPosition.GetY());
SetRegistry(CameraDefaultStartingPositionZ, defaultCameraPosition.GetZ());
AzToolsFramework::SetRegistry(CameraDefaultStartingPositionX, defaultCameraPosition.GetX());
AzToolsFramework::SetRegistry(CameraDefaultStartingPositionY, defaultCameraPosition.GetY());
AzToolsFramework::SetRegistry(CameraDefaultStartingPositionZ, defaultCameraPosition.GetZ());
}
AZ::u64 MaxItemsShownInAssetBrowserSearch()
{
return GetRegistry(AssetBrowserMaxItemsShownInSearchSetting, aznumeric_cast<AZ::u64>(50));
return AzToolsFramework::GetRegistry(AssetBrowserMaxItemsShownInSearchSetting, aznumeric_cast<AZ::u64>(50));
}
void SetMaxItemsShownInAssetBrowserSearch(const AZ::u64 numberOfItemsShown)
{
SetRegistry(AssetBrowserMaxItemsShownInSearchSetting, numberOfItemsShown);
AzToolsFramework::SetRegistry(AssetBrowserMaxItemsShownInSearchSetting, numberOfItemsShown);
}
bool GridSnappingEnabled()
{
return GetRegistry(GridSnappingSetting, false);
return AzToolsFramework::GetRegistry(GridSnappingSetting, false);
}
void SetGridSnapping(const bool enabled)
{
SetRegistry(GridSnappingSetting, enabled);
AzToolsFramework::SetRegistry(GridSnappingSetting, enabled);
}
float GridSnappingSize()
{
return aznumeric_cast<float>(GetRegistry(GridSizeSetting, 0.1));
return aznumeric_cast<float>(AzToolsFramework::GetRegistry(GridSizeSetting, 0.1));
}
void SetGridSnappingSize(const float size)
{
SetRegistry(GridSizeSetting, size);
AzToolsFramework::SetRegistry(GridSizeSetting, size);
}
bool AngleSnappingEnabled()
{
return GetRegistry(AngleSnappingSetting, false);
return AzToolsFramework::GetRegistry(AngleSnappingSetting, false);
}
void SetAngleSnapping(const bool enabled)
{
SetRegistry(AngleSnappingSetting, enabled);
AzToolsFramework::SetRegistry(AngleSnappingSetting, enabled);
}
float AngleSnappingSize()
{
return aznumeric_cast<float>(GetRegistry(AngleSizeSetting, 5.0));
return aznumeric_cast<float>(AzToolsFramework::GetRegistry(AngleSizeSetting, 5.0));
}
void SetAngleSnappingSize(const float size)
{
SetRegistry(AngleSizeSetting, size);
AzToolsFramework::SetRegistry(AngleSizeSetting, size);
}
bool ShowingGrid()
{
return GetRegistry(ShowGridSetting, false);
return AzToolsFramework::GetRegistry(ShowGridSetting, false);
}
void SetShowingGrid(const bool showing)
{
SetRegistry(ShowGridSetting, showing);
AzToolsFramework::SetRegistry(ShowGridSetting, showing);
}
bool StickySelectEnabled()
{
return GetRegistry(StickySelectSetting, false);
return AzToolsFramework::GetRegistry(StickySelectSetting, false);
}
void SetStickySelectEnabled(const bool enabled)
{
SetRegistry(StickySelectSetting, enabled);
AzToolsFramework::SetRegistry(StickySelectSetting, enabled);
}
float ManipulatorLineBoundWidth()
{
return aznumeric_cast<float>(GetRegistry(ManipulatorLineBoundWidthSetting, 0.1));
return aznumeric_cast<float>(AzToolsFramework::GetRegistry(ManipulatorLineBoundWidthSetting, 0.1));
}
void SetManipulatorLineBoundWidth(const float lineBoundWidth)
{
SetRegistry(ManipulatorLineBoundWidthSetting, lineBoundWidth);
AzToolsFramework::SetRegistry(ManipulatorLineBoundWidthSetting, lineBoundWidth);
}
float ManipulatorCircleBoundWidth()
{
return aznumeric_cast<float>(GetRegistry(ManipulatorCircleBoundWidthSetting, 0.1));
return aznumeric_cast<float>(AzToolsFramework::GetRegistry(ManipulatorCircleBoundWidthSetting, 0.1));
}
void SetManipulatorCircleBoundWidth(const float circleBoundWidth)
{
SetRegistry(ManipulatorCircleBoundWidthSetting, circleBoundWidth);
AzToolsFramework::SetRegistry(ManipulatorCircleBoundWidthSetting, circleBoundWidth);
}
float CameraTranslateSpeed()
{
return aznumeric_cast<float>(GetRegistry(CameraTranslateSpeedSetting, 10.0));
return aznumeric_cast<float>(AzToolsFramework::GetRegistry(CameraTranslateSpeedSetting, 10.0));
}
void SetCameraTranslateSpeed(const float speed)
{
SetRegistry(CameraTranslateSpeedSetting, speed);
AzToolsFramework::SetRegistry(CameraTranslateSpeedSetting, speed);
}
float CameraBoostMultiplier()
{
return aznumeric_cast<float>(GetRegistry(CameraBoostMultiplierSetting, 3.0));
return aznumeric_cast<float>(AzToolsFramework::GetRegistry(CameraBoostMultiplierSetting, 3.0));
}
void SetCameraBoostMultiplier(const float multiplier)
{
SetRegistry(CameraBoostMultiplierSetting, multiplier);
AzToolsFramework::SetRegistry(CameraBoostMultiplierSetting, multiplier);
}
float CameraRotateSpeed()
{
return aznumeric_cast<float>(GetRegistry(CameraRotateSpeedSetting, 0.005));
return aznumeric_cast<float>(AzToolsFramework::GetRegistry(CameraRotateSpeedSetting, 0.005));
}
void SetCameraRotateSpeed(const float speed)
{
SetRegistry(CameraRotateSpeedSetting, speed);
AzToolsFramework::SetRegistry(CameraRotateSpeedSetting, speed);
}
float CameraScrollSpeed()
{
return aznumeric_cast<float>(GetRegistry(CameraScrollSpeedSetting, 0.02));
return aznumeric_cast<float>(AzToolsFramework::GetRegistry(CameraScrollSpeedSetting, 0.02));
}
void SetCameraScrollSpeed(const float speed)
{
SetRegistry(CameraScrollSpeedSetting, speed);
AzToolsFramework::SetRegistry(CameraScrollSpeedSetting, speed);
}
float CameraDollyMotionSpeed()
{
return aznumeric_cast<float>(GetRegistry(CameraDollyMotionSpeedSetting, 0.01));
return aznumeric_cast<float>(AzToolsFramework::GetRegistry(CameraDollyMotionSpeedSetting, 0.01));
}
void SetCameraDollyMotionSpeed(const float speed)
{
SetRegistry(CameraDollyMotionSpeedSetting, speed);
AzToolsFramework::SetRegistry(CameraDollyMotionSpeedSetting, speed);
}
bool CameraOrbitYawRotationInverted()
{
return GetRegistry(CameraOrbitYawRotationInvertedSetting, false);
return AzToolsFramework::GetRegistry(CameraOrbitYawRotationInvertedSetting, false);
}
void SetCameraOrbitYawRotationInverted(const bool inverted)
{
SetRegistry(CameraOrbitYawRotationInvertedSetting, inverted);
AzToolsFramework::SetRegistry(CameraOrbitYawRotationInvertedSetting, inverted);
}
bool CameraPanInvertedX()
{
return GetRegistry(CameraPanInvertedXSetting, true);
return AzToolsFramework::GetRegistry(CameraPanInvertedXSetting, true);
}
void SetCameraPanInvertedX(const bool inverted)
{
SetRegistry(CameraPanInvertedXSetting, inverted);
AzToolsFramework::SetRegistry(CameraPanInvertedXSetting, inverted);
}
bool CameraPanInvertedY()
{
return GetRegistry(CameraPanInvertedYSetting, true);
return AzToolsFramework::GetRegistry(CameraPanInvertedYSetting, true);
}
void SetCameraPanInvertedY(const bool inverted)
{
SetRegistry(CameraPanInvertedYSetting, inverted);
AzToolsFramework::SetRegistry(CameraPanInvertedYSetting, inverted);
}
float CameraPanSpeed()
{
return aznumeric_cast<float>(GetRegistry(CameraPanSpeedSetting, 0.01));
return aznumeric_cast<float>(AzToolsFramework::GetRegistry(CameraPanSpeedSetting, 0.01));
}
void SetCameraPanSpeed(float speed)
{
SetRegistry(CameraPanSpeedSetting, speed);
AzToolsFramework::SetRegistry(CameraPanSpeedSetting, speed);
}
float CameraRotateSmoothness()
{
return aznumeric_cast<float>(GetRegistry(CameraRotateSmoothnessSetting, 5.0));
return aznumeric_cast<float>(AzToolsFramework::GetRegistry(CameraRotateSmoothnessSetting, 5.0));
}
void SetCameraRotateSmoothness(const float smoothness)
{
SetRegistry(CameraRotateSmoothnessSetting, smoothness);
AzToolsFramework::SetRegistry(CameraRotateSmoothnessSetting, smoothness);
}
float CameraTranslateSmoothness()
{
return aznumeric_cast<float>(GetRegistry(CameraTranslateSmoothnessSetting, 5.0));
return aznumeric_cast<float>(AzToolsFramework::GetRegistry(CameraTranslateSmoothnessSetting, 5.0));
}
void SetCameraTranslateSmoothness(const float smoothness)
{
SetRegistry(CameraTranslateSmoothnessSetting, smoothness);
AzToolsFramework::SetRegistry(CameraTranslateSmoothnessSetting, smoothness);
}
bool CameraRotateSmoothingEnabled()
{
return GetRegistry(CameraRotateSmoothingSetting, true);
return AzToolsFramework::GetRegistry(CameraRotateSmoothingSetting, true);
}
void SetCameraRotateSmoothingEnabled(const bool enabled)
{
SetRegistry(CameraRotateSmoothingSetting, enabled);
AzToolsFramework::SetRegistry(CameraRotateSmoothingSetting, enabled);
}
bool CameraTranslateSmoothingEnabled()
{
return GetRegistry(CameraTranslateSmoothingSetting, true);
return AzToolsFramework::GetRegistry(CameraTranslateSmoothingSetting, true);
}
void SetCameraTranslateSmoothingEnabled(const bool enabled)
{
SetRegistry(CameraTranslateSmoothingSetting, enabled);
AzToolsFramework::SetRegistry(CameraTranslateSmoothingSetting, enabled);
}
bool CameraCaptureCursorForLook()
{
return GetRegistry(CameraCaptureCursorLookSetting, true);
return AzToolsFramework::GetRegistry(CameraCaptureCursorLookSetting, true);
}
void SetCameraCaptureCursorForLook(const bool capture)
{
SetRegistry(CameraCaptureCursorLookSetting, capture);
AzToolsFramework::SetRegistry(CameraCaptureCursorLookSetting, capture);
}
float CameraDefaultOrbitDistance()
{
return aznumeric_cast<float>(GetRegistry(CameraDefaultOrbitDistanceSetting, 20.0));
return aznumeric_cast<float>(AzToolsFramework::GetRegistry(CameraDefaultOrbitDistanceSetting, 20.0));
}
void SetCameraDefaultOrbitDistance(const float distance)
{
SetRegistry(CameraDefaultOrbitDistanceSetting, distance);
AzToolsFramework::SetRegistry(CameraDefaultOrbitDistanceSetting, distance);
}
AzFramework::InputChannelId CameraTranslateForwardChannelId()
{
return AzFramework::InputChannelId(
GetRegistry(CameraTranslateForwardIdSetting, AZStd::string("keyboard_key_alphanumeric_W")).c_str());
AzToolsFramework::GetRegistry(CameraTranslateForwardIdSetting, AZStd::string("keyboard_key_alphanumeric_W")).c_str());
}
void SetCameraTranslateForwardChannelId(AZStd::string_view cameraTranslateForwardId)
{
SetRegistry(CameraTranslateForwardIdSetting, cameraTranslateForwardId);
AzToolsFramework::SetRegistry(CameraTranslateForwardIdSetting, cameraTranslateForwardId);
}
AzFramework::InputChannelId CameraTranslateBackwardChannelId()
{
return AzFramework::InputChannelId(
GetRegistry(CameraTranslateBackwardIdSetting, AZStd::string("keyboard_key_alphanumeric_S")).c_str());
AzToolsFramework::GetRegistry(CameraTranslateBackwardIdSetting, AZStd::string("keyboard_key_alphanumeric_S")).c_str());
}
void SetCameraTranslateBackwardChannelId(AZStd::string_view cameraTranslateBackwardId)
{
SetRegistry(CameraTranslateBackwardIdSetting, cameraTranslateBackwardId);
AzToolsFramework::SetRegistry(CameraTranslateBackwardIdSetting, cameraTranslateBackwardId);
}
AzFramework::InputChannelId CameraTranslateLeftChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraTranslateLeftIdSetting, AZStd::string("keyboard_key_alphanumeric_A")).c_str());
return AzFramework::InputChannelId(
AzToolsFramework::GetRegistry(CameraTranslateLeftIdSetting, AZStd::string("keyboard_key_alphanumeric_A")).c_str());
}
void SetCameraTranslateLeftChannelId(AZStd::string_view cameraTranslateLeftId)
{
SetRegistry(CameraTranslateLeftIdSetting, cameraTranslateLeftId);
AzToolsFramework::SetRegistry(CameraTranslateLeftIdSetting, cameraTranslateLeftId);
}
AzFramework::InputChannelId CameraTranslateRightChannelId()
{
return AzFramework::InputChannelId(
GetRegistry(CameraTranslateRightIdSetting, AZStd::string("keyboard_key_alphanumeric_D")).c_str());
AzToolsFramework::GetRegistry(CameraTranslateRightIdSetting, AZStd::string("keyboard_key_alphanumeric_D")).c_str());
}
void SetCameraTranslateRightChannelId(AZStd::string_view cameraTranslateRightId)
{
SetRegistry(CameraTranslateRightIdSetting, cameraTranslateRightId);
AzToolsFramework::SetRegistry(CameraTranslateRightIdSetting, cameraTranslateRightId);
}
AzFramework::InputChannelId CameraTranslateUpChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraTranslateUpIdSetting, AZStd::string("keyboard_key_alphanumeric_E")).c_str());
return AzFramework::InputChannelId(
AzToolsFramework::GetRegistry(CameraTranslateUpIdSetting, AZStd::string("keyboard_key_alphanumeric_E")).c_str());
}
void SetCameraTranslateUpChannelId(AZStd::string_view cameraTranslateUpId)
{
SetRegistry(CameraTranslateUpIdSetting, cameraTranslateUpId);
AzToolsFramework::SetRegistry(CameraTranslateUpIdSetting, cameraTranslateUpId);
}
AzFramework::InputChannelId CameraTranslateDownChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraTranslateDownIdSetting, AZStd::string("keyboard_key_alphanumeric_Q")).c_str());
return AzFramework::InputChannelId(
AzToolsFramework::GetRegistry(CameraTranslateDownIdSetting, AZStd::string("keyboard_key_alphanumeric_Q")).c_str());
}
void SetCameraTranslateDownChannelId(AZStd::string_view cameraTranslateDownId)
{
SetRegistry(CameraTranslateDownIdSetting, cameraTranslateDownId);
AzToolsFramework::SetRegistry(CameraTranslateDownIdSetting, cameraTranslateDownId);
}
AzFramework::InputChannelId CameraTranslateBoostChannelId()
{
return AzFramework::InputChannelId(
GetRegistry(CameraTranslateBoostIdSetting, AZStd::string("keyboard_key_modifier_shift_l")).c_str());
AzToolsFramework::GetRegistry(CameraTranslateBoostIdSetting, AZStd::string("keyboard_key_modifier_shift_l")).c_str());
}
void SetCameraTranslateBoostChannelId(AZStd::string_view cameraTranslateBoostId)
{
SetRegistry(CameraTranslateBoostIdSetting, cameraTranslateBoostId);
AzToolsFramework::SetRegistry(CameraTranslateBoostIdSetting, cameraTranslateBoostId);
}
AzFramework::InputChannelId CameraOrbitChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraOrbitIdSetting, AZStd::string("keyboard_key_modifier_alt_l")).c_str());
return AzFramework::InputChannelId(
AzToolsFramework::GetRegistry(CameraOrbitIdSetting, AZStd::string("keyboard_key_modifier_alt_l")).c_str());
}
void SetCameraOrbitChannelId(AZStd::string_view cameraOrbitId)
{
SetRegistry(CameraOrbitIdSetting, cameraOrbitId);
AzToolsFramework::SetRegistry(CameraOrbitIdSetting, cameraOrbitId);
}
AzFramework::InputChannelId CameraFreeLookChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraFreeLookIdSetting, AZStd::string("mouse_button_right")).c_str());
return AzFramework::InputChannelId(
AzToolsFramework::GetRegistry(CameraFreeLookIdSetting, AZStd::string("mouse_button_right")).c_str());
}
void SetCameraFreeLookChannelId(AZStd::string_view cameraFreeLookId)
{
SetRegistry(CameraFreeLookIdSetting, cameraFreeLookId);
AzToolsFramework::SetRegistry(CameraFreeLookIdSetting, cameraFreeLookId);
}
AzFramework::InputChannelId CameraFreePanChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraFreePanIdSetting, AZStd::string("mouse_button_middle")).c_str());
return AzFramework::InputChannelId(
AzToolsFramework::GetRegistry(CameraFreePanIdSetting, AZStd::string("mouse_button_middle")).c_str());
}
void SetCameraFreePanChannelId(AZStd::string_view cameraFreePanId)
{
SetRegistry(CameraFreePanIdSetting, cameraFreePanId);
AzToolsFramework::SetRegistry(CameraFreePanIdSetting, cameraFreePanId);
}
AzFramework::InputChannelId CameraOrbitLookChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraOrbitLookIdSetting, AZStd::string("mouse_button_left")).c_str());
return AzFramework::InputChannelId(
AzToolsFramework::GetRegistry(CameraOrbitLookIdSetting, AZStd::string("mouse_button_left")).c_str());
}
void SetCameraOrbitLookChannelId(AZStd::string_view cameraOrbitLookId)
{
SetRegistry(CameraOrbitLookIdSetting, cameraOrbitLookId);
AzToolsFramework::SetRegistry(CameraOrbitLookIdSetting, cameraOrbitLookId);
}
AzFramework::InputChannelId CameraOrbitDollyChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraOrbitDollyIdSetting, AZStd::string("mouse_button_right")).c_str());
return AzFramework::InputChannelId(
AzToolsFramework::GetRegistry(CameraOrbitDollyIdSetting, AZStd::string("mouse_button_right")).c_str());
}
void SetCameraOrbitDollyChannelId(AZStd::string_view cameraOrbitDollyId)
{
SetRegistry(CameraOrbitDollyIdSetting, cameraOrbitDollyId);
AzToolsFramework::SetRegistry(CameraOrbitDollyIdSetting, cameraOrbitDollyId);
}
AzFramework::InputChannelId CameraOrbitPanChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraOrbitPanIdSetting, AZStd::string("mouse_button_middle")).c_str());
return AzFramework::InputChannelId(
AzToolsFramework::GetRegistry(CameraOrbitPanIdSetting, AZStd::string("mouse_button_middle")).c_str());
}
void SetCameraOrbitPanChannelId(AZStd::string_view cameraOrbitPanId)
{
SetRegistry(CameraOrbitPanIdSetting, cameraOrbitPanId);
AzToolsFramework::SetRegistry(CameraOrbitPanIdSetting, cameraOrbitPanId);
}
AzFramework::InputChannelId CameraFocusChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraFocusIdSetting, AZStd::string("keyboard_key_alphanumeric_X")).c_str());
return AzFramework::InputChannelId(
AzToolsFramework::GetRegistry(CameraFocusIdSetting, AZStd::string("keyboard_key_alphanumeric_X")).c_str());
}
void SetCameraFocusChannelId(AZStd::string_view cameraFocusId)
{
SetRegistry(CameraFocusIdSetting, cameraFocusId);
AzToolsFramework::SetRegistry(CameraFocusIdSetting, cameraFocusId);
}
} // namespace SandboxEditor
-8
View File
@@ -35,7 +35,6 @@
// CryCommon
#include <CryCommon/INavigationSystem.h>
#include <CryCommon/LyShine/ILyShine.h>
#include <CryCommon/MainThreadRenderRequestBus.h>
// Editor
@@ -595,13 +594,6 @@ void CGameEngine::SwitchToInEditor()
// Enable accelerators.
GetIEditor()->EnableAcceleratos(true);
// reset UI system
if (gEnv->pLyShine)
{
gEnv->pLyShine->Reset();
}
// [Anton] - order changed, see comments for CGameEngine::SetSimulationMode
//! Send event to switch out of game.
GetIEditor()->GetObjectManager()->SendEvent(EVENT_OUTOFGAME);
@@ -19,7 +19,7 @@ using namespace ::testing;
namespace UnitTest
{
class TestingClickableLabel
: public testing::Test
: public ScopedAllocatorSetupFixture
{
public:
ClickableLabel m_clickableLabel;
@@ -11,6 +11,7 @@
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
@@ -22,7 +23,7 @@ namespace CryEditDocPythonBindingsUnitTests
{
class CryEditDocPythonBindingsFixture
: public testing::Test
: public ::UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -30,7 +31,6 @@ namespace CryEditDocPythonBindingsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -11,6 +11,7 @@
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
@@ -24,7 +25,7 @@ namespace CryEditPythonBindingsUnitTests
{
class CryEditPythonBindingsFixture
: public testing::Test
: public ::UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -32,7 +33,6 @@ namespace CryEditPythonBindingsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -11,6 +11,7 @@
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
@@ -22,7 +23,7 @@ namespace DisplaySettingsPythonBindingsUnitTests
{
class DisplaySettingsPythonBindingsFixture
: public testing::Test
: public ::UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -30,7 +31,6 @@ namespace DisplaySettingsPythonBindingsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
m_app.RegisterComponentDescriptor(AzToolsFramework::DisplaySettingsPythonFuncsHandler::CreateDescriptor());
@@ -52,7 +52,7 @@ namespace DisplaySettingsPythonBindingsUnitTests
}
class DisplaySettingsComponentFixture
: public testing::Test
: public ::UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -60,7 +60,6 @@ namespace DisplaySettingsPythonBindingsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
m_app.RegisterComponentDescriptor(AzToolsFramework::DisplaySettingsComponent::CreateDescriptor());
@@ -12,6 +12,7 @@
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
@@ -79,7 +80,7 @@ namespace EditorPythonBindingsUnitTests
};
class EditorPythonBindingsFixture
: public testing::Test
: public ::UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -87,7 +88,6 @@ namespace EditorPythonBindingsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
+2 -1
View File
@@ -11,6 +11,7 @@
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace EditorUtilsTest
{
@@ -39,7 +40,7 @@ namespace EditorUtilsTest
class TestWarningAbsorber
: public testing::Test
: public ::UnitTest::ScopedAllocatorSetupFixture
{
};
@@ -12,6 +12,7 @@
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
@@ -22,7 +23,7 @@ namespace MainWindowPythonBindingsUnitTests
{
class MainWindowPythonBindingsFixture
: public testing::Test
: public ::UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -30,7 +31,6 @@ namespace MainWindowPythonBindingsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -12,6 +12,7 @@
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
@@ -22,7 +23,7 @@ namespace ObjectManagerPythonBindingsUnitTests
{
class ObjectManagerPythonBindingsFixture
: public testing::Test
: public ::UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -30,7 +31,6 @@ namespace ObjectManagerPythonBindingsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -23,7 +23,7 @@ namespace TerrainFuncsUnitTests
{
class TerrainHoleToolPythonBindingsFixture
: public testing::Test
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -31,7 +31,6 @@ namespace TerrainFuncsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -23,7 +23,7 @@ namespace TerrainFuncsUnitTests
{
class TerrainLayerPythonBindingsFixture
: public testing::Test
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -31,7 +31,6 @@ namespace TerrainFuncsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -22,7 +22,7 @@ namespace TerrainModifyPythonBindingsUnitTests
{
class TerrainModifyPythonBindingsFixture
: public testing::Test
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -30,7 +30,6 @@ namespace TerrainModifyPythonBindingsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -23,7 +23,7 @@ namespace TerrainFuncsUnitTests
{
class TerrainPainterPythonBindingsFixture
: public testing::Test
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -31,7 +31,6 @@ namespace TerrainFuncsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -23,7 +23,7 @@ namespace TerrainFuncsUnitTests
{
class TerrainPythonBindingsFixture
: public testing::Test
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -31,7 +31,6 @@ namespace TerrainFuncsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -23,7 +23,7 @@ namespace TerrainFuncsUnitTests
{
class TerrainTexturePythonBindingsFixture
: public testing::Test
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -31,7 +31,6 @@ namespace TerrainFuncsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -12,6 +12,7 @@
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
@@ -22,7 +23,7 @@ namespace TrackViewPythonBindingsUnitTests
{
class TrackViewPythonBindingsFixture
: public testing::Test
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -30,7 +31,6 @@ namespace TrackViewPythonBindingsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -82,7 +82,7 @@ namespace TrackViewPythonBindingsUnitTests
}
class TrackViewComponentFixture
: public testing::Test
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -90,7 +90,6 @@ namespace TrackViewPythonBindingsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
m_app.RegisterComponentDescriptor(AzToolsFramework::TrackViewComponent::CreateDescriptor());
@@ -11,6 +11,7 @@
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
@@ -22,7 +23,7 @@ namespace ViewPaneFuncsUnitTests
{
class ViewPanePythonBindingsFixture
: public testing::Test
: public ::UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -30,7 +31,6 @@ namespace ViewPaneFuncsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -12,6 +12,7 @@
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
@@ -22,7 +23,7 @@ namespace ViewportTitleDlgFuncsUnitTests
{
class ViewportTitleDlgPythonBindingsFixture
: public testing::Test
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
AzToolsFramework::ToolsApplication m_app;
@@ -30,7 +31,6 @@ namespace ViewportTitleDlgFuncsUnitTests
void SetUp() override
{
AzFramework::Application::Descriptor appDesc;
appDesc.m_enableDrilling = false;
m_app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -1,41 +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 <LyShine/UiBase.h>
class UndoStack;
////////////////////////////////////////////////////////////////////////////////////////////////////
//! Interface class that the UI Editor needs to implement
class UiEditorDLLInterface
: public AZ::EBusTraits
{
public: // member functions
virtual ~UiEditorDLLInterface(){}
//! Get the selected elements in the UiEditor
virtual LyShine::EntityArray GetSelectedElements() = 0;
//! Get the id of the active Canvas the UiEditor
virtual AZ::EntityId GetActiveCanvasId() = 0;
//! Get the active undo stack for the UI Editor
virtual UndoStack* GetActiveUndoStack() = 0;
//! Soft-switch to the given file. Note that this should prompt for unsaved changes, etc.
virtual void OpenSourceCanvasFile(QString absolutePathToFile) = 0;
public: // static member functions
static const char* GetUniqueName() { return "UiEditorDLLInterface"; }
};
typedef AZ::EBus<UiEditorDLLInterface> UiEditorDLLBus;
@@ -13,7 +13,6 @@ set(FILES
EditorCommonAPI.h
ActionOutput.h
ActionOutput.cpp
UiEditorDLLBus.h
DockTitleBarWidget.cpp
DockTitleBarWidget.h
SaveUtilities/AsyncSaveRunner.h
+5 -6
View File
@@ -75,17 +75,16 @@ void CImageEx::ReverseUpDown()
}
uint32* pPixData = GetData();
uint32* pReversePix = new uint32[GetWidth() * GetHeight()];
for (int i = GetHeight() - 1, i2 = 0; i >= 0; i--, i2++)
const int height = GetHeight();
const int width = GetWidth();
for (int i = 0; i < height / 2; i++)
{
for (int k = 0; k < GetWidth(); k++)
for (int j = 0; j < width; j++)
{
pReversePix[i2 * GetWidth() + k] = pPixData[i * GetWidth() + k];
AZStd::swap(pPixData[i * width + j], pPixData[(height - 1 - i) * width + j]);
}
}
Attach(pReversePix, GetWidth(), GetHeight());
}
void CImageEx::FillAlpha(unsigned char value)
+2 -2
View File
@@ -37,7 +37,7 @@ namespace AZ
using namespace AZ;
// Handle asserts
class TraceDrillerHook
class TestEnvironmentHook
: public AZ::Test::ITestEnvironment
, public UnitTest::TraceBusRedirector
{
@@ -57,5 +57,5 @@ public:
}
};
AZ_UNIT_TEST_HOOK(new TraceDrillerHook());
AZ_UNIT_TEST_HOOK(new TestEnvironmentHook());
@@ -19,7 +19,6 @@
#include <AzCore/std/string/string.h>
#include <AzCore/std/string/string_view.h>
#include <AzCore/std/typetraits/is_base_of.h>
#include <AzCore/Debug/AssetTracking.h>
#include <AzCore/IO/Streamer/FileRequest.h>
namespace AZ
@@ -325,13 +324,13 @@ namespace AZ
T& operator*() const
{
AZ_Assert(m_assetData, "Asset is not loaded");
AZ_Assert(m_assetData, "Asset %s (%s) is not loaded", m_assetId.ToString<AZStd::string>().c_str(), m_assetHint.c_str());
return *Get();
}
T* operator->() const
{
AZ_Assert(m_assetData, "Asset is not loaded");
AZ_Assert(m_assetData, "Asset %s (%s) is not loaded", m_assetId.ToString<AZStd::string>().c_str(), m_assetHint.c_str());
return Get();
}
@@ -581,7 +580,6 @@ namespace AZ
template<typename Bus>
using ConnectionPolicy = AssetConnectionPolicy<Bus>;
using EventProcessingPolicy = Debug::AssetTrackingEventProcessingPolicy<>;
//////////////////////////////////////////////////////////////////////////
virtual ~AssetEvents() {}
@@ -10,7 +10,6 @@
#include <AzCore/Asset/AssetManager_private.h>
#include <AzCore/Asset/AssetDataStream.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Debug/AssetTracking.h>
#include <AzCore/IO/IStreamer.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Math/MathUtils.h>
@@ -164,8 +163,6 @@ namespace AZ::Data
AZ_PROFILE_SCOPE(AzCore, "AZ::Data::LoadAssetJob::Process: %s",
asset.GetHint().c_str());
AZ_ASSET_ATTACH_TO_SCOPE(this);
if (m_owner->ValidateAndRegisterAssetLoading(asset))
{
LoadAndSignal(asset);
@@ -200,7 +197,6 @@ namespace AZ::Data
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(cl_assetLoadDelay));
}
AZ_ASSET_NAMED_SCOPE(asset.GetHint().c_str());
bool loadedSuccessfully = false;
if (!cl_assetLoadError && m_requestState == AZ::IO::IStreamerTypes::RequestStatus::Completed)
@@ -982,7 +978,6 @@ namespace AZ::Data
}
AZ_PROFILE_SCOPE(AzCore, "GetAsset: %s", assetInfo.m_relativePath.c_str());
AZ_ASSET_NAMED_SCOPE("GetAsset: %s", assetInfo.m_relativePath.c_str());
AZStd::shared_ptr<AssetDataStream> dataStream;
AssetStreamInfo loadInfo;
@@ -48,10 +48,6 @@
#include <AzCore/IO/Path/PathReflect.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Driller/Driller.h>
#include <AzCore/Memory/MemoryDriller.h>
#include <AzCore/Debug/TraceMessagesDriller.h>
#include <AzCore/Debug/EventTraceDriller.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Script/ScriptSystemBus.h>
@@ -156,7 +152,6 @@ namespace AZ
m_reservedDebug = 0;
m_recordingMode = Debug::AllocationRecords::RECORD_STACK_IF_NO_FILE_LINE;
m_stackRecordLevels = 5;
m_enableDrilling = false;
m_useOverrunDetection = false;
m_useMalloc = false;
}
@@ -328,7 +323,6 @@ namespace AZ
->Field("blockSize", &Descriptor::m_memoryBlocksByteSize)
->Field("reservedOS", &Descriptor::m_reservedOS)
->Field("reservedDebug", &Descriptor::m_reservedDebug)
->Field("enableDrilling", &Descriptor::m_enableDrilling)
->Field("useOverrunDetection", &Descriptor::m_useOverrunDetection)
->Field("useMalloc", &Descriptor::m_useMalloc)
->Field("allocatorRemappings", &Descriptor::m_allocatorRemappings)
@@ -367,7 +361,6 @@ namespace AZ
->Attribute(Edit::Attributes::Step, &Descriptor::m_pageSize)
->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_reservedOS, "OS reserved memory", "System memory reserved for OS (used only when 'Allocate all memory at startup' is true)")
->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_reservedDebug, "Memory reserved for debugger", "System memory reserved for Debug allocator, like memory tracking (used only when 'Allocate all memory at startup' is true)")
->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_enableDrilling, "Enable Driller", "Enable Drilling support for the application (ignored in Release builds)")
->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_useOverrunDetection, "Use Overrun Detection", "Use the overrun detection memory manager (only available on some platforms, ignored in Release builds)")
->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_useMalloc, "Use Malloc", "Use malloc for memory allocations (for memory debugging only, ignored in Release builds)")
;
@@ -486,9 +479,11 @@ namespace AZ
// Merge Command Line arguments
constexpr bool executeRegDumpCommands = false;
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
#if defined(AZ_DEBUG_BUILD) || defined(AZ_PROFILE_BUILD)
// Skip over merging the User Registry in non-debug and profile configurations
SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(*m_settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {});
#endif
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
@@ -41,7 +41,6 @@ namespace AZ
}
namespace AZ::Debug
{
class DrillerManager;
class LocalFileEventLogger;
}
@@ -143,7 +142,6 @@ namespace AZ
AZ::u64 m_reservedDebug; //!< Reserved memory for Debugging (allocation,etc.). Used only when m_grabAllMemory is set to true. (default: 0)
Debug::AllocationRecords::Mode m_recordingMode; //!< When to record stack traces (default: AZ::Debug::AllocationRecords::RECORD_STACK_IF_NO_FILE_LINE)
AZ::u64 m_stackRecordLevels; //!< If stack recording is enabled, how many stack levels to record. (default: 5)
bool m_enableDrilling; //!< True to enabled drilling support for the application. RegisterDrillers will be called. Ignored in release. (default: true)
bool m_useOverrunDetection; //!< True to use the overrun detection memory management scheme. Only available on some platforms; greatly increases memory consumption.
bool m_useMalloc; //!< True to use malloc instead of the internal memory manager. Intended for debugging purposes only.
@@ -37,11 +37,6 @@ namespace AZ
class ComponentFactoryInterface;
}
namespace Debug
{
class DrillerManager;
}
struct ApplicationTypeQuery
{
bool IsEditor() const;
@@ -16,7 +16,6 @@
#define AZCORE_COMPONENT_TICK_BUS_H
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Debug/AssetTracking.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/parallel/mutex.h> // For TickBus thread events.
#include <AzCore/Script/ScriptTimePoint.h>
@@ -112,10 +111,6 @@ namespace AZ
AZ_FORCE_INLINE bool operator()(TickEvents* left, TickEvents* right) const { return left->GetTickOrder() < right->GetTickOrder(); }
};
/**
* Enable tick bus to work with the AssetTracking
*/
using EventProcessingPolicy = Debug::AssetTrackingEventProcessingPolicy<>;
//////////////////////////////////////////////////////////////////////////
/**
@@ -217,10 +212,6 @@ namespace AZ
*/
typedef AZStd::mutex EventQueueMutexType;
/**
* Enable tick bus to work with the AssetTracking
*/
using EventProcessingPolicy = Debug::AssetTrackingEventProcessingPolicy<>;
//////////////////////////////////////////////////////////////////////////
/**
@@ -0,0 +1,43 @@
/*
* 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/DOM/Backends/JSON/JsonSerializationUtils.h>
#include <AzCore/DOM/DomBackend.h>
#include <AzCore/IO/ByteContainerStream.h>
namespace AZ::Dom
{
//! A DOM backend for serializing and deserializing JSON <=> UTF-8 text
//! \param ParseFlags Controls how deserialized JSON is parsed.
//! \param WriteFormat Controls how serialized JSON is formatted.
template<
Json::ParseFlags ParseFlags = Json::ParseFlags::ParseComments,
Json::OutputFormatting WriteFormat = Json::OutputFormatting::PrettyPrintedJson>
class JsonBackend final : public Backend
{
public:
Visitor::Result ReadFromBuffer(const char* buffer, size_t size, AZ::Dom::Lifetime lifetime, Visitor& visitor) override
{
return Json::VisitSerializedJson<ParseFlags>({ buffer, size }, lifetime, visitor);
}
Visitor::Result ReadFromBufferInPlace(char* buffer, [[maybe_unused]] AZStd::optional<size_t> size, Visitor& visitor) override
{
return Json::VisitSerializedJsonInPlace<ParseFlags>(buffer, visitor);
}
Visitor::Result WriteToBuffer(AZStd::string& buffer, WriteCallback callback)
{
AZ::IO::ByteContainerStream<AZStd::string> stream{ &buffer };
AZStd::unique_ptr<Visitor> visitor = Json::CreateJsonStreamWriter(stream, WriteFormat);
return callback(*visitor);
}
};
} // namespace AZ::Dom
@@ -0,0 +1,582 @@
/*
* 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/DOM/Backends/JSON/JsonSerializationUtils.h>
#include <AzCore/IO/ByteContainerStream.h>
#include <AzCore/IO/TextStreamWriters.h>
#include <AzCore/JSON/filewritestream.h>
#include <AzCore/JSON/memorystream.h>
#include <AzCore/JSON/prettywriter.h>
#include <AzCore/JSON/rapidjson.h>
#include <AzCore/JSON/reader.h>
#include <AzCore/JSON/writer.h>
#include <AzCore/std/containers/stack.h>
#include <AzCore/std/containers/variant.h>
#include <AzCore/std/optional.h>
namespace AZ::Dom::Json
{
//
// class RapidJsonValueWriter
//
RapidJsonValueWriter::RapidJsonValueWriter(rapidjson::Value& outputValue, rapidjson::Value::AllocatorType& allocator)
: m_result(outputValue)
, m_allocator(allocator)
{
}
VisitorFlags RapidJsonValueWriter::GetVisitorFlags() const
{
return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects;
}
Visitor::Result RapidJsonValueWriter::Null()
{
CurrentValue().SetNull();
return FinishWrite();
}
Visitor::Result RapidJsonValueWriter::Bool(bool value)
{
CurrentValue().SetBool(value);
return FinishWrite();
}
Visitor::Result RapidJsonValueWriter::Int64(AZ::s64 value)
{
CurrentValue().SetInt64(value);
return FinishWrite();
}
Visitor::Result RapidJsonValueWriter::Uint64(AZ::u64 value)
{
CurrentValue().SetUint64(value);
return FinishWrite();
}
Visitor::Result RapidJsonValueWriter::Double(double value)
{
CurrentValue().SetDouble(value);
return FinishWrite();
}
Visitor::Result RapidJsonValueWriter::String(AZStd::string_view value, Lifetime lifetime)
{
if (lifetime == Lifetime::Temporary)
{
CurrentValue().SetString(value.data(), aznumeric_cast<rapidjson::SizeType>(value.length()), m_allocator);
}
else
{
CurrentValue().SetString(value.data(), aznumeric_cast<rapidjson::SizeType>(value.length()));
}
return FinishWrite();
}
Visitor::Result RapidJsonValueWriter::StartObject()
{
CurrentValue().SetObject();
const bool isObject = true;
m_entryStack.emplace_front(isObject, CurrentValue());
return VisitorSuccess();
}
Visitor::Result RapidJsonValueWriter::EndObject(AZ::u64 attributeCount)
{
if (m_entryStack.empty())
{
return VisitorFailure(VisitorErrorCode::InternalError, "EndObject called without a matching BeginObject call");
}
const ValueInfo& frontEntry = m_entryStack.front();
if (!frontEntry.m_isObject)
{
return VisitorFailure(VisitorErrorCode::InternalError, "Expected EndArray and received EndObject instead");
}
if (frontEntry.m_entryCount != attributeCount)
{
return VisitorFailure(
VisitorErrorCode::InternalError,
AZStd::string::format(
"EndObject: Expected %llu attributes but received %llu attributes instead", attributeCount,
frontEntry.m_entryCount));
}
m_entryStack.pop_front();
return FinishWrite();
}
Visitor::Result RapidJsonValueWriter::Key(AZ::Name key)
{
return RawKey(key.GetStringView(), Lifetime::Persistent);
}
Visitor::Result RapidJsonValueWriter::RawKey(AZStd::string_view key, Lifetime lifetime)
{
AZ_Assert(!m_entryStack.empty(), "Attempmted to push a key with no object");
AZ_Assert(m_entryStack.front().m_isObject, "Attempted to push a key to an array");
if (lifetime == Lifetime::Persistent)
{
m_entryStack.front().m_key.SetString(key.data(), aznumeric_cast<rapidjson::SizeType>(key.size()));
}
else
{
m_entryStack.front().m_key.SetString(key.data(), aznumeric_cast<rapidjson::SizeType>(key.size()), m_allocator);
}
return VisitorSuccess();
}
Visitor::Result RapidJsonValueWriter::StartArray()
{
CurrentValue().SetArray();
const bool isObject = false;
m_entryStack.emplace_front(isObject, CurrentValue());
return VisitorSuccess();
}
Visitor::Result RapidJsonValueWriter::EndArray(AZ::u64 elementCount)
{
if (m_entryStack.empty())
{
return VisitorFailure(VisitorErrorCode::InternalError, "EndArray called without a matching BeginArray call");
}
const ValueInfo& frontEntry = m_entryStack.front();
if (frontEntry.m_isObject)
{
return VisitorFailure(VisitorErrorCode::InternalError, "Expected EndObject and received EndArray instead");
}
if (frontEntry.m_entryCount != elementCount)
{
return VisitorFailure(
VisitorErrorCode::InternalError,
AZStd::string::format(
"EndArray: Expected %llu elements but received %llu elements instead", elementCount, frontEntry.m_entryCount));
}
m_entryStack.pop_front();
return FinishWrite();
}
Visitor::Result RapidJsonValueWriter::FinishWrite()
{
if (m_entryStack.empty())
{
return VisitorSuccess();
}
// Retrieve the top value of the stack and replace it with a null value
rapidjson::Value value;
m_entryStack.front().m_value.Swap(value);
ValueInfo& newEntry = m_entryStack.front();
++newEntry.m_entryCount;
if (newEntry.m_key.IsString())
{
newEntry.m_container.AddMember(m_entryStack.front().m_key.Move(), AZStd::move(value), m_allocator);
newEntry.m_key.SetNull();
}
else
{
newEntry.m_container.PushBack(AZStd::move(value), m_allocator);
}
return VisitorSuccess();
}
rapidjson::Value& RapidJsonValueWriter::CurrentValue()
{
if (m_entryStack.empty())
{
return m_result;
}
return m_entryStack.front().m_value;
}
RapidJsonValueWriter::ValueInfo::ValueInfo(bool isObject, rapidjson::Value& container)
: m_isObject(isObject)
, m_container(container)
{
}
//
// class StreamWriter
//
// Visitor that writes to a rapidjson::Writer
template<class Writer>
class StreamWriter : public Visitor
{
public:
StreamWriter(AZ::IO::GenericStream* stream)
: m_streamWriter(stream)
, m_writer(Writer(m_streamWriter))
{
}
VisitorFlags GetVisitorFlags() const override
{
return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects;
}
Result Null() override
{
return CheckWrite(m_writer.Null());
}
Result Bool(bool value) override
{
return CheckWrite(m_writer.Bool(value));
}
Result Int64(AZ::s64 value) override
{
return CheckWrite(m_writer.Int64(value));
}
Result Uint64(AZ::u64 value) override
{
return CheckWrite(m_writer.Uint64(value));
}
Result Double(double value) override
{
return CheckWrite(m_writer.Double(value));
}
Result String(AZStd::string_view value, Lifetime lifetime) override
{
const bool shouldCopy = lifetime == Lifetime::Temporary;
return CheckWrite(m_writer.String(value.data(), aznumeric_cast<rapidjson::SizeType>(value.size()), shouldCopy));
}
Result StartObject() override
{
return CheckWrite(m_writer.StartObject());
}
Result EndObject(AZ::u64 attributeCount) override
{
return CheckWrite(m_writer.EndObject(aznumeric_cast<rapidjson::SizeType>(attributeCount)));
}
Result Key(AZ::Name key) override
{
return RawKey(key.GetStringView(), Lifetime::Persistent);
}
Result RawKey(AZStd::string_view key, Lifetime lifetime) override
{
const bool shouldCopy = lifetime == Lifetime::Temporary;
return CheckWrite(m_writer.Key(key.data(), aznumeric_cast<rapidjson::SizeType>(key.size()), shouldCopy));
}
Result StartArray() override
{
return CheckWrite(m_writer.StartArray());
}
Result EndArray(AZ::u64 elementCount) override
{
return CheckWrite(m_writer.EndArray(aznumeric_cast<rapidjson::SizeType>(elementCount)));
}
private:
Result CheckWrite(bool writeSucceeded)
{
if (writeSucceeded)
{
return VisitorSuccess();
}
else
{
return VisitorFailure(VisitorErrorCode::InternalError, "Failed to write JSON");
}
}
AZ::IO::RapidJSONStreamWriter m_streamWriter;
Writer m_writer;
};
//
// struct JsonReadHandler
//
RapidJsonReadHandler::RapidJsonReadHandler(Visitor* visitor, Lifetime stringLifetime)
: m_visitor(visitor)
, m_stringLifetime(stringLifetime)
, m_outcome(AZ::Success())
{
}
bool RapidJsonReadHandler::Null()
{
return CheckResult(m_visitor->Null());
}
bool RapidJsonReadHandler::Bool(bool b)
{
return CheckResult(m_visitor->Bool(b));
}
bool RapidJsonReadHandler::Int(int i)
{
return CheckResult(m_visitor->Int64(aznumeric_cast<AZ::s64>(i)));
}
bool RapidJsonReadHandler::Uint(unsigned i)
{
return CheckResult(m_visitor->Uint64(aznumeric_cast<AZ::u64>(i)));
}
bool RapidJsonReadHandler::Int64(int64_t i)
{
return CheckResult(m_visitor->Int64(i));
}
bool RapidJsonReadHandler::Uint64(uint64_t i)
{
return CheckResult(m_visitor->Uint64(i));
}
bool RapidJsonReadHandler::Double(double d)
{
return CheckResult(m_visitor->Double(d));
}
bool RapidJsonReadHandler::RawNumber(
[[maybe_unused]] const char* str, [[maybe_unused]] rapidjson::SizeType length, [[maybe_unused]] bool copy)
{
AZ_Assert(false, "Raw numbers are unsupported in the rapidjson DOM backend");
return false;
}
bool RapidJsonReadHandler::String(const char* str, rapidjson::SizeType length, bool copy)
{
const Lifetime lifetime = copy ? m_stringLifetime : Lifetime::Temporary;
return CheckResult(m_visitor->String(AZStd::string_view(str, length), lifetime));
}
bool RapidJsonReadHandler::StartObject()
{
return CheckResult(m_visitor->StartObject());
}
bool RapidJsonReadHandler::Key(const char* str, rapidjson::SizeType length, [[maybe_unused]] bool copy)
{
AZStd::string_view key = AZStd::string_view(str, length);
if (!m_visitor->SupportsRawKeys())
{
m_visitor->Key(AZ::Name(key));
}
const Lifetime lifetime = copy ? m_stringLifetime : Lifetime::Temporary;
return CheckResult(m_visitor->RawKey(key, lifetime));
}
bool RapidJsonReadHandler::EndObject([[maybe_unused]] rapidjson::SizeType memberCount)
{
return CheckResult(m_visitor->EndObject(memberCount));
}
bool RapidJsonReadHandler::StartArray()
{
return CheckResult(m_visitor->StartArray());
}
bool RapidJsonReadHandler::EndArray([[maybe_unused]] rapidjson::SizeType elementCount)
{
return CheckResult(m_visitor->EndArray(elementCount));
}
Visitor::Result&& RapidJsonReadHandler::TakeOutcome()
{
return AZStd::move(m_outcome);
}
bool RapidJsonReadHandler::CheckResult(Visitor::Result result)
{
if (result.IsSuccess())
{
return true;
}
else
{
m_outcome = AZStd::move(result);
return false;
}
}
//
// Serialized JSON util functions
//
AZStd::unique_ptr<Visitor> CreateJsonStreamWriter(AZ::IO::GenericStream& stream, OutputFormatting format)
{
if (format == OutputFormatting::MinifiedJson)
{
using WriterType = rapidjson::Writer<AZ::IO::RapidJSONStreamWriter>;
return AZStd::make_unique<StreamWriter<WriterType>>(&stream);
}
else
{
using WriterType = rapidjson::PrettyWriter<AZ::IO::RapidJSONStreamWriter>;
return AZStd::make_unique<StreamWriter<WriterType>>(&stream);
}
}
//
// In-memory rapidjson util functions
//
AZ::Outcome<rapidjson::Document, AZStd::string> WriteToRapidJsonDocument(Backend::WriteCallback writeCallback)
{
rapidjson::Document document;
RapidJsonValueWriter writer(document, document.GetAllocator());
auto result = writeCallback(writer);
if (!result.IsSuccess())
{
return AZ::Failure(result.TakeError().FormatVisitorErrorMessage());
}
return AZ::Success(AZStd::move(document));
}
Visitor::Result WriteToRapidJsonValue(
rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator, Backend::WriteCallback writeCallback)
{
RapidJsonValueWriter writer(value, allocator);
return writeCallback(writer);
}
Visitor::Result VisitRapidJsonValue(const rapidjson::Value& value, Visitor& visitor, Lifetime lifetime)
{
struct EndArrayMarker
{
};
struct EndObjectMarker
{
};
// Processing stack consists of values comprised of one of a:
// - rapidjson::Value to process
// - EndArrayMarker or EndObjectMarker denoting the end of an array or object
// - string denoting a key at the beginning of a key/value pair
using Entry = AZStd::variant<const rapidjson::Value*, EndArrayMarker, EndObjectMarker, AZStd::string_view>;
AZStd::stack<Entry> entryStack;
AZStd::stack<u64> entryCountStack;
entryStack.push(&value);
while (!entryStack.empty())
{
const Entry currentEntry = entryStack.top();
entryStack.pop();
Visitor::Result result = AZ::Success();
AZStd::visit(
[&visitor, &entryStack, &entryCountStack, &result, lifetime](auto&& arg)
{
using Alternative = AZStd::decay_t<decltype(arg)>;
if constexpr (AZStd::is_same_v<Alternative, const rapidjson::Value*>)
{
const rapidjson::Value& currentValue = *arg;
if (!entryCountStack.empty())
{
++entryCountStack.top();
}
switch (currentValue.GetType())
{
case rapidjson::kNullType:
result = visitor.Null();
break;
case rapidjson::kFalseType:
result = visitor.Bool(false);
break;
case rapidjson::kTrueType:
result = visitor.Bool(true);
break;
case rapidjson::kObjectType:
entryStack.push(EndObjectMarker{});
entryCountStack.push(0);
result = visitor.StartObject();
for (auto it = currentValue.MemberEnd(); it != currentValue.MemberBegin(); --it)
{
auto entry = (it - 1);
const AZStd::string_view key(
entry->name.GetString(), aznumeric_cast<size_t>(entry->name.GetStringLength()));
entryStack.push(&entry->value);
entryStack.push(key);
}
break;
case rapidjson::kArrayType:
entryStack.push(EndArrayMarker{});
entryCountStack.push(0);
result = visitor.StartArray();
for (auto it = currentValue.End(); it != currentValue.Begin(); --it)
{
auto entry = (it - 1);
entryStack.push(entry);
}
break;
case rapidjson::kStringType:
result = visitor.String(
AZStd::string_view(currentValue.GetString(), aznumeric_cast<size_t>(currentValue.GetStringLength())),
lifetime);
break;
case rapidjson::kNumberType:
if (currentValue.IsFloat() || currentValue.IsDouble())
{
result = visitor.Double(currentValue.GetDouble());
}
else if (currentValue.IsInt64() || currentValue.IsInt())
{
result = visitor.Int64(currentValue.GetInt64());
}
else
{
result = visitor.Uint64(currentValue.GetUint64());
}
break;
default:
result = AZ::Failure(VisitorError(VisitorErrorCode::InvalidData, "Value with invalid type specified"));
}
}
else if constexpr (AZStd::is_same_v<Alternative, EndArrayMarker>)
{
result = visitor.EndArray(entryCountStack.top());
entryCountStack.pop();
}
else if constexpr (AZStd::is_same_v<Alternative, EndObjectMarker>)
{
result = visitor.EndObject(entryCountStack.top());
entryCountStack.pop();
}
else if constexpr (AZStd::is_same_v<Alternative, AZStd::string_view>)
{
if (visitor.SupportsRawKeys())
{
visitor.RawKey(arg, lifetime);
}
else
{
visitor.Key(AZ::Name(arg));
}
}
},
currentEntry);
if (!result.IsSuccess())
{
return result;
}
}
return AZ::Success();
}
} // namespace AZ::Dom::Json
@@ -0,0 +1,256 @@
/*
* 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/DOM/DomBackend.h>
#include <AzCore/DOM/DomVisitor.h>
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/JSON/document.h>
#include <AzCore/std/containers/deque.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AZ::Dom::Json
{
//! Specifies how JSON should be formatted when serialized.
enum class OutputFormatting
{
MinifiedJson, //!< Formats JSON in compact minified form, focusing on minimizing output size.
PrettyPrintedJson, //!< Formats JSON in a pretty printed form, focusing on legibility to readers.
};
//! Specifies parsing behavior when deserializing JSON.
enum class ParseFlags : int
{
Null = 0,
StopWhenDone = rapidjson::kParseStopWhenDoneFlag,
FullFloatingPointPrecision = rapidjson::kParseFullPrecisionFlag,
ParseComments = rapidjson::kParseCommentsFlag,
ParseNumbersAsStrings = rapidjson::kParseNumbersAsStringsFlag,
ParseTrailingCommas = rapidjson::kParseTrailingCommasFlag,
ParseNanAndInfinity = rapidjson::kParseNanAndInfFlag,
ParseEscapedApostrophies = rapidjson::kParseEscapedApostropheFlag,
};
AZ_DEFINE_ENUM_BITWISE_OPERATORS(ParseFlags);
//! Visitor that feeds into a rapidjson::Value
class RapidJsonValueWriter final : public Visitor
{
public:
RapidJsonValueWriter(rapidjson::Value& outputValue, rapidjson::Value::AllocatorType& allocator);
VisitorFlags GetVisitorFlags() const override;
Result Null() override;
Result Bool(bool value) override;
Result Int64(AZ::s64 value) override;
Result Uint64(AZ::u64 value) override;
Result Double(double value) override;
Result String(AZStd::string_view value, Lifetime lifetime) override;
Result StartObject() override;
Result EndObject(AZ::u64 attributeCount) override;
Result Key(AZ::Name key) override;
Result RawKey(AZStd::string_view key, Lifetime lifetime) override;
Result StartArray() override;
Result EndArray(AZ::u64 elementCount) override;
private:
Result FinishWrite();
rapidjson::Value& CurrentValue();
struct ValueInfo
{
ValueInfo(bool isObject, rapidjson::Value& container);
rapidjson::Value m_key;
rapidjson::Value m_value;
rapidjson::Value& m_container;
AZ::u64 m_entryCount = 0;
bool m_isObject;
};
rapidjson::Value& m_result;
rapidjson::Value::AllocatorType& m_allocator;
AZStd::deque<ValueInfo> m_entryStack;
};
//! Handler for a rapidjson::Reader that translates reads into an AZ::Dom::Visitor
struct RapidJsonReadHandler
{
public:
RapidJsonReadHandler(Visitor* visitor, Lifetime stringLifetime);
bool Null();
bool Bool(bool b);
bool Int(int i);
bool Uint(unsigned i);
bool Int64(int64_t i);
bool Uint64(uint64_t i);
bool Double(double d);
bool RawNumber(const char* str, rapidjson::SizeType length, bool copy);
bool String(const char* str, rapidjson::SizeType length, bool copy);
bool StartObject();
bool Key(const char* str, rapidjson::SizeType length, bool copy);
bool EndObject(rapidjson::SizeType memberCount);
bool StartArray();
bool EndArray(rapidjson::SizeType elementCount);
Visitor::Result&& TakeOutcome();
private:
bool CheckResult(Visitor::Result result);
Visitor::Result m_outcome;
Visitor* m_visitor;
Lifetime m_stringLifetime;
};
//! rapidjson stream wrapper for AZStd::string suitable for in-situ parsing
//! Faster than rapidjson::MemoryStream for reading from AZStd::string / AZStd::string_view (because it requires a null terminator)
//! \note This needs to be inlined for performance reasons.
struct NullDelimitedStringStream
{
using Ch = char; //<! Denotes the string character storage type for rapidjson
AZ_FORCE_INLINE NullDelimitedStringStream(char* buffer)
{
m_cursor = buffer;
m_begin = m_cursor;
}
AZ_FORCE_INLINE NullDelimitedStringStream(AZStd::string_view buffer)
{
// rapidjson won't actually call PutBegin or Put unless kParseInSituFlag is set, so this is safe
m_cursor = const_cast<char*>(buffer.data());
m_begin = m_cursor;
}
AZ_FORCE_INLINE char Peek() const
{
return *m_cursor;
}
AZ_FORCE_INLINE char Take()
{
return *m_cursor++;
}
AZ_FORCE_INLINE size_t Tell() const
{
return static_cast<size_t>(m_cursor - m_begin);
}
AZ_FORCE_INLINE char* PutBegin()
{
m_write = m_cursor;
return m_cursor;
}
AZ_FORCE_INLINE void Put(char c)
{
(*m_write++) = c;
}
AZ_FORCE_INLINE void Flush()
{
}
AZ_FORCE_INLINE size_t PutEnd(char* begin)
{
return m_write - begin;
}
AZ_FORCE_INLINE const char* Peek4() const
{
AZ_Assert(false, "Not implemented, encoding is hard-coded to UTF-8");
return m_cursor;
}
char* m_cursor; //!< Current read position.
char* m_write; //!< Current write position.
const char* m_begin; //!< Head of string.
};
//! Creates a Visitor that will write serialized JSON to the specified stream.
//! \param stream The stream the visitor will write to.
//! \param format The format to write in.
//! \return A Visitor that will write to stream when visited.
AZStd::unique_ptr<Visitor> CreateJsonStreamWriter(
AZ::IO::GenericStream& stream, OutputFormatting format = OutputFormatting::PrettyPrintedJson);
//! Reads serialized JSON from a string and applies it to a visitor.
//! \param buffer The UTF-8 serialized JSON to read.
//! \param lifetime Specifies the lifetime of the specified buffer. If the string specified by buffer might be deallocated,
//! ensure Lifetime::Temporary is specified.
//! \param visitor The visitor to visit with the JSON buffer's contents.
//! \param parseFlags (template) Settings for adjusting parser behavior.
//! \return The aggregate result specifying whether the visitor operations were successful.
template<ParseFlags parseFlags = ParseFlags::ParseComments>
Visitor::Result VisitSerializedJson(AZStd::string_view buffer, Lifetime lifetime, Visitor& visitor);
//! Reads serialized JSON from a string in-place and applies it to a visitor.
//! \param buffer The UTF-8 serialized JSON to read. This buffer will be modified as part of the deserialization process to
//! apply null terminators.
//! \param visitor The visitor to visit with the JSON buffer's contents. The strings provided to the visitor will only
//! be valid for the lifetime of buffer.
//! \param parseFlags (template) Settings for adjusting parser behavior.
//! \return The aggregate result specifying whether the visitor operations were successful.
template<ParseFlags parseFlags = ParseFlags::ParseComments>
Visitor::Result VisitSerializedJsonInPlace(char* buffer, Visitor& visitor);
//! Takes a visitor specified by a callback and produces a rapidjson::Document.
//! \param writeCallback A callback specifying a visitor to accept to build the resulting document.
//! \return An outcome with either the rapidjson::Document or an error message.
AZ::Outcome<rapidjson::Document, AZStd::string> WriteToRapidJsonDocument(Backend::WriteCallback writeCallback);
//! Takes a visitor specified by a callback and reads them into a rapidjson::Value.
//! \param value The value to read into, its contents will be overridden.
//! \param allocator The allocator to use when performing rapidjson allocations (generally provded by the rapidjson::Document).
//! \param writeCallback A callback specifying a visitor to accept to build the resulting document.
//! \return An outcome with either the rapidjson::Document or an error message.
Visitor::Result WriteToRapidJsonValue(
rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator, Backend::WriteCallback writeCallback);
//! Accepts a visitor with the contents of a rapidjson::Value.
//! \param value The rapidjson::Value to apply to visitor.
//! \param visitor The visitor to receive the contents of value.
//! \param lifetime The lifetime to specify for visiting strings. If the rapidjson::Value might be destroyed or changed
//! before the visitor is finished using these values, Lifetime::Temporary should be specified.
//! \return The aggregate result specifying whether the visitor operations were successful.
Visitor::Result VisitRapidJsonValue(const rapidjson::Value& value, Visitor& visitor, Lifetime lifetime);
template<ParseFlags parseFlags>
Visitor::Result VisitSerializedJson(AZStd::string_view buffer, Lifetime lifetime, Visitor& visitor)
{
rapidjson::Reader reader;
RapidJsonReadHandler handler(&visitor, lifetime);
// If the string is null terminated, we can use the faster AzStringStream path - otherwise we fall back on rapidjson::MemoryStream
if (buffer.data()[buffer.size()] == '\0')
{
NullDelimitedStringStream stream(buffer);
reader.Parse<aznumeric_cast<unsigned>(parseFlags)>(stream, handler);
}
else
{
rapidjson::MemoryStream stream(buffer.data(), buffer.size());
reader.Parse<aznumeric_cast<unsigned>(parseFlags)>(stream, handler);
}
return handler.TakeOutcome();
}
template<ParseFlags parseFlags>
Visitor::Result VisitSerializedJsonInPlace(char* buffer, Visitor& visitor)
{
rapidjson::Reader reader;
NullDelimitedStringStream stream(buffer);
RapidJsonReadHandler handler(&visitor, Lifetime::Persistent);
reader.Parse<aznumeric_cast<unsigned>(parseFlags) | rapidjson::kParseInsituFlag>(stream, handler);
return handler.TakeOutcome();
}
} // namespace AZ::Dom::Json
@@ -0,0 +1,17 @@
/*
* 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/DOM/DomBackend.h>
namespace AZ::Dom
{
Visitor::Result Backend::ReadFromBufferInPlace(char* buffer, AZStd::optional<size_t> size, Visitor& visitor)
{
return ReadFromBuffer(buffer, size.value_or(strlen(buffer)), AZ::Dom::Lifetime::Persistent, visitor);
}
} // namespace AZ::Dom
@@ -0,0 +1,44 @@
/*
* 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/DOM/DomVisitor.h>
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AZ::Dom
{
//! Backends are registered centrally and used to transition DOM formats to and from a textual format.
class Backend
{
public:
virtual ~Backend() = default;
//! Attempt to read this format from the given buffer into the target Visitor.
virtual Visitor::Result ReadFromBuffer(
const char* buffer, size_t size, AZ::Dom::Lifetime lifetime, Visitor& visitor) = 0;
//! Attempt to read this format from a mutable string into the target Visitor. This enables some backends to
//! parse without making additional string allocations.
//! This string must be null terminated.
//! This string may be modified and read in place without being copied, so when calling this please ensure that:
//! - The string won't be deallocated until the visitor no longer needs the values and
//! - The string is safe to modify in place.
//! The base implementation simply calls ReadFromBuffer.
virtual Visitor::Result ReadFromBufferInPlace(char* buffer, AZStd::optional<size_t> size, Visitor& visitor);
//! A callback that accepts a Visitor, making DOM calls to inform the serializer, and returns an
//! aggregate error code to indicate whether or not the operation succeeded.
using WriteCallback = AZStd::function<Visitor::Result(Visitor&)>;
//! Attempt to write a value to the specified string using a write callback.
virtual Visitor::Result WriteToBuffer(AZStd::string& buffer, WriteCallback callback) = 0;
};
} // namespace AZ::Dom
@@ -0,0 +1,24 @@
/*
* 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/DOM/DomUtils.h>
#include <AzCore/IO/ByteContainerStream.h>
namespace AZ::Dom::Utils
{
Visitor::Result ReadFromString(Backend& backend, AZStd::string_view string, AZ::Dom::Lifetime lifetime, Visitor& visitor)
{
return backend.ReadFromBuffer(string.data(), string.length(), lifetime, visitor);
}
Visitor::Result ReadFromStringInPlace(Backend& backend, AZStd::string& string, Visitor& visitor)
{
return backend.ReadFromBufferInPlace(string.data(), string.size(), visitor);
}
}
@@ -0,0 +1,17 @@
/*
* 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/DOM/DomBackend.h>
namespace AZ::Dom::Utils
{
Visitor::Result ReadFromString(Backend& backend, AZStd::string_view string, AZ::Dom::Lifetime lifetime, Visitor& visitor);
Visitor::Result ReadFromStringInPlace(Backend& backend, AZStd::string& string, Visitor& visitor);
} // namespace AZ::Dom::Utils
@@ -8,7 +8,7 @@
#include <AzCore/DOM/DomVisitor.h>
namespace AZ::DOM
namespace AZ::Dom
{
const char* VisitorError::CodeToString(VisitorErrorCode code)
{
@@ -236,4 +236,4 @@ namespace AZ::DOM
{
return (GetVisitorFlags() & VisitorFlags::SupportsOpaqueValues) != VisitorFlags::Null;
}
} // namespace AZ::DOM
} // namespace AZ::Dom
+13 -12
View File
@@ -13,11 +13,11 @@
#include <AzCore/std/any.h>
#include <AzCore/std/string/string.h>
namespace AZ::DOM
namespace AZ::Dom
{
//
// Lifetime enum
//
//
//! Specifies the period in which a reference value will still be alive and safe to read.
enum class Lifetime
{
@@ -30,7 +30,7 @@ namespace AZ::DOM
//
// VisitorErrorCode enum
//
//
//! Error code specifying the reason a Visitor operation failed.
enum class VisitorErrorCode
{
@@ -75,7 +75,7 @@ namespace AZ::DOM
};
//! A type alias for opaque DOM types that aren't meant to be serializable.
//! /see VisitorInterface::OpaqueValue
//! \see VisitorInterface::OpaqueValue
using OpaqueType = AZStd::any;
//
@@ -116,7 +116,7 @@ namespace AZ::DOM
//! - \ref Double: 64 bit double precision float
//! - \ref Null: sentinel "empty" type with no value representation
//! - \ref String: UTF8 encoded string
//! - \ref Object: an ordered container of key/value pairs where keys are AZ::Names and values may be any DOM type
//! - \ref Object: an ordered container of key/value pairs where keys are \ref AZ::Name and values may be any DOM type
//! (including Object)
//! - \ref Array: an ordered container of values, in which values are any DOM value type (including Array)
//! - \ref Node: a container
@@ -144,17 +144,17 @@ namespace AZ::DOM
//! Raw (\see VisitorFlags::SupportsRawValues) and opaque values (\see VisitorFlags::SupportsOpaqueValues)
//! are disallowed by default, as their handling is intended to be implementation-specific.
virtual VisitorFlags GetVisitorFlags() const;
//! /see VisitorFlags::SupportsRawValues
//! \see VisitorFlags::SupportsRawValues
bool SupportsRawValues() const;
//! /see VisitorFlags::SupportsRawKeys
//! \see VisitorFlags::SupportsRawKeys
bool SupportsRawKeys() const;
//! /see VisitorFlags::SupportsObjects
//! \see VisitorFlags::SupportsObjects
bool SupportsObjects() const;
//! /see VisitorFlags::SupportsArrays
//! \see VisitorFlags::SupportsArrays
bool SupportsArrays() const;
//! /see VisitorFlags::SupportsNodes
//! \see VisitorFlags::SupportsNodes
bool SupportsNodes() const;
//! /see VisitorFlags::SupportsOpaqueValues
//! \see VisitorFlags::SupportsOpaqueValues
bool SupportsOpaqueValues() const;
//! Operates on an empty null value.
@@ -231,7 +231,8 @@ namespace AZ::DOM
static Result VisitorFailure(VisitorErrorCode code, AZStd::string additionalInfo);
//! Helper method, constructs a failure \ref Result with the specified error.
static Result VisitorFailure(VisitorError error);
//! Helper method, constructs a success \ref Result.
static Result VisitorSuccess();
};
} // namespace AZ::DOM
} // namespace AZ::Dom
@@ -1,326 +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
*
*/
#include "AssetTracking.h"
#include <AzCore/Debug/AssetTrackingTypes.h>
#include <AzCore/Memory/AllocatorManager.h>
#include <AzCore/Memory/HphaSchema.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/smart_ptr/make_shared.h>
namespace AZ::Debug
{
namespace
{
struct AssetTreeNode;
// Per-thread data that needs to be stored.
struct ThreadData
{
AZStd::vector<AssetTreeNodeBase*, AZStdAssetTrackingAllocator> m_currentAssetStack;
};
// Access thread data through a virtual function to ensure that the same thread-local data is being shared across DLLs.
// Otherwise, the thread_local variables are replicated across DLLs that link the AzCore library, and you'll get a
// different version in each module.
class ThreadDataProvider
{
public:
virtual ThreadData& GetThreadData() = 0;
};
}
class AssetTrackingImpl final :
public ThreadDataProvider
{
public:
AZ_TYPE_INFO(AssetTrackingImpl, "{01E2A099-3523-40BE-80E0-E0ADD861BEE1}");
AZ_CLASS_ALLOCATOR(AssetTrackingImpl, OSAllocator, 0);
AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable);
~AssetTrackingImpl();
void AssetBegin(const char* id, const char* file, int line);
void AssetAttach(void* otherAllocation, const char* file, int line);
void AssetEnd();
ThreadData& GetThreadData() override;
private:
static EnvironmentVariable<AssetTrackingImpl*>& GetEnvironmentVariable();
static AssetTrackingImpl* GetSharedInstance();
static ThreadData& GetSharedThreadData();
using PrimaryAssets = AZStd::unordered_map<AssetTrackingId, AssetPrimaryInfo, AZStd::hash<AssetTrackingId>, AZStd::equal_to<AssetTrackingId>, AZStdAssetTrackingAllocator>;
using ThreadData = ThreadData;
using mutex_type = AZStd::mutex;
using lock_type = AZStd::lock_guard<mutex_type>;
mutex_type m_mutex;
PrimaryAssets m_primaryAssets;
AssetTreeNodeBase* m_assetRoot = nullptr;
AssetAllocationTableBase* m_allocationTable = nullptr;
bool m_performingAnalysis = false;
friend class AssetTracking;
friend class AssetTracking::Scope;
};
///////////////////////////////////////////////////////////////////////////////
// AssetTrackingImpl methods
///////////////////////////////////////////////////////////////////////////////
AssetTrackingImpl::AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable) :
m_assetRoot(&assetTree->GetRoot()),
m_allocationTable(allocationTable)
{
AZ_Assert(!GetSharedInstance(), "Only one AssetTrackingImpl can exist!");
GetEnvironmentVariable().Set(this);
AllocatorManager::Instance().EnterProfilingMode();
}
AssetTrackingImpl::~AssetTrackingImpl()
{
AllocatorManager::Instance().ExitProfilingMode();
GetEnvironmentVariable().Reset();
}
void AssetTrackingImpl::AssetBegin(const char* id, const char* file, int line)
{
// In the future it may be desirable to organize assets based on where in code the asset was entered into.
// For now these are ignored.
AZ_UNUSED(file);
AZ_UNUSED(line);
using namespace Internal;
AssetTrackingId assetId(id);
auto& threadData = GetSharedThreadData();
AssetTreeNodeBase* parentAsset = threadData.m_currentAssetStack.empty() ? nullptr : threadData.m_currentAssetStack.back();
AssetTreeNodeBase* childAsset;
AssetPrimaryInfo* assetPrimaryInfo;
if (!parentAsset)
{
parentAsset = m_assetRoot;
}
{
lock_type lock(m_mutex);
// Locate or create the primary record for this asset
auto primaryItr = m_primaryAssets.find(assetId);
if (primaryItr != m_primaryAssets.end())
{
assetPrimaryInfo = &primaryItr->second;
}
else
{
auto insertResult = m_primaryAssets.emplace(assetId, AssetPrimaryInfo());
assetPrimaryInfo = &insertResult.first->second;
assetPrimaryInfo->m_id = &insertResult.first->first;
}
// Add this asset to the stack for this thread's context
childAsset = parentAsset->FindOrAddChild(assetId, assetPrimaryInfo);
}
threadData.m_currentAssetStack.push_back(childAsset);
}
void AssetTrackingImpl::AssetAttach(void* otherAllocation, const char* file, int line)
{
AZ_UNUSED(file);
AZ_UNUSED(line);
using namespace Internal;
AssetTreeNodeBase* assetInfo = m_allocationTable->FindAllocation(otherAllocation);
// We will push back a nullptr if there is no asset, this is necessary to balance the call to AssetEnd()
GetSharedThreadData().m_currentAssetStack.push_back(assetInfo);
}
void AssetTrackingImpl::AssetEnd()
{
AZ_Assert(!GetSharedThreadData().m_currentAssetStack.empty(), "AssetEnd() called without matching AssetBegin() or AssetAttach. Use the AZ_ASSET_NAMED_SCOPE and AZ_ASSET_ATTACH_TO_SCOPE macros to avoid this!");
GetSharedThreadData().m_currentAssetStack.pop_back();
}
AssetTrackingImpl* AssetTrackingImpl::GetSharedInstance()
{
auto environmentVariable = GetEnvironmentVariable();
if(environmentVariable)
{
return *environmentVariable;
}
return nullptr;
}
ThreadData& AssetTrackingImpl::GetSharedThreadData()
{
// Cast to the base type so our virtual call doesn't get optimized away. We require GetThreadData() to be executed in the same DLL every time.
return static_cast<ThreadDataProvider*>(GetSharedInstance())->GetThreadData();
}
AssetTrackingImpl::ThreadData& AssetTrackingImpl::GetThreadData()
{
static thread_local ThreadData* data = nullptr;
static thread_local typename AZStd::aligned_storage_t<sizeof(ThreadData), alignof(ThreadData)> storage;
if (!data)
{
data = new (&storage) ThreadData;
}
return *data;
}
EnvironmentVariable<AssetTrackingImpl*>& AssetTrackingImpl::GetEnvironmentVariable()
{
static EnvironmentVariable<AssetTrackingImpl*> assetTrackingImpl = Environment::CreateVariable<AssetTrackingImpl*>(AzTypeInfo<AssetTrackingImpl*>::Name());
return assetTrackingImpl;
}
///////////////////////////////////////////////////////////////////////////////
// AssetTracking::Scope functions
///////////////////////////////////////////////////////////////////////////////
AssetTracking::Scope AssetTracking::Scope::ScopeFromAssetId(const char* file, int line, const char* fmt, ...)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
static const int BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE];
va_list args;
va_start(args, fmt);
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
va_end(args);
impl->AssetBegin(buffer, file, line);
}
return Scope();
}
AssetTracking::Scope AssetTracking::Scope::ScopeFromAttachment(void* attachTo, const char* file, int line)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetAttach(attachTo, file, line);
}
return Scope();
}
AssetTracking::Scope::~Scope()
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetEnd();
}
}
AssetTracking::Scope::Scope()
{
}
///////////////////////////////////////////////////////////////////////////////
// AssetTracking functions
///////////////////////////////////////////////////////////////////////////////
void AssetTracking::EnterScopeByAssetId(const char* file, int line, const char* fmt, ...)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
static const int BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE];
va_list args;
va_start(args, fmt);
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
va_end(args);
impl->AssetBegin(buffer, file, line);
}
}
void AssetTracking::EnterScopeByAttachment(void* attachTo, const char* file, int line)
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetAttach(attachTo, file, line);
}
}
void AssetTracking::ExitScope()
{
if (auto impl = AssetTrackingImpl::GetSharedInstance())
{
impl->AssetEnd();
}
}
const char* AssetTracking::GetDebugScope()
{
// Output debug information about the current asset scope in the current thread.
// Do not use in production code.
#ifndef RELEASE
static const int BUFFER_SIZE = 1024;
static char buffer[BUFFER_SIZE];
const auto& assetStack = AssetTrackingImpl::GetSharedInstance()->GetThreadData().m_currentAssetStack;
if (assetStack.empty())
{
azsnprintf(buffer, BUFFER_SIZE, "<none>");
}
else
{
char* pos = buffer;
for (auto itr = assetStack.rbegin(); itr != assetStack.rend(); ++itr)
{
pos += azsnprintf(pos, BUFFER_SIZE - (pos - buffer), "%s\n", (*itr)->GetAssetPrimaryInfo()->m_id->m_id.c_str());
if (pos >= buffer + BUFFER_SIZE)
{
break;
}
}
}
return buffer;
#else
return "";
#endif
}
AssetTracking::AssetTracking(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable)
{
m_impl.reset(aznew AssetTrackingImpl(assetTree, allocationTable));
}
AssetTracking::~AssetTracking()
{
}
AssetTreeNodeBase* AssetTracking::GetCurrentThreadAsset() const
{
const auto& assetStack = m_impl->GetThreadData().m_currentAssetStack;
AssetTreeNodeBase* result = assetStack.empty() ? nullptr : assetStack.back();
return result;
}
} // namespace AzFramework
@@ -1,131 +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/Memory/OSAllocator.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/EBus/Policies.h>
#ifndef AZ_TRACK_ASSET_SCOPES
// You may manually uncomment this to enable asset tracking.
//# define AZ_TRACK_ASSET_SCOPES
#endif
#if !defined(AZ_TRACK_ASSET_SCOPES)
// Default to enabling asset tracking when memory tracking is enabled
# define AZ_TRACK_ASSET_SCOPES
#endif
#ifdef AZ_TRACK_ASSET_SCOPES
#define AZ_ASSET_SCOPE_VARIABLE_NAME(line) AZ_JOIN(_az_assettracking_scope_, line)
///////////////////////////////////////////////////////////////////////////////
// Preferred macros to use at the top of a scope you want to to track asset memory for.
///////////////////////////////////////////////////////////////////////////////
// Creates a new scope with a name, usually the name of an asset being loaded. (This may be a format-string, e.g. "Foo: %s", bar.c_str())
# define AZ_ASSET_NAMED_SCOPE(...) AZ::Debug::AssetTracking::Scope AZ_ASSET_SCOPE_VARIABLE_NAME(__LINE__) (AZ::Debug::AssetTracking::Scope::ScopeFromAssetId(__FILE__, __LINE__, __VA_ARGS__))
// Attempts to enter an existing scope that already owns some other allocation.
# define AZ_ASSET_ATTACH_TO_SCOPE(other) AZ::Debug::AssetTracking::Scope AZ_ASSET_SCOPE_VARIABLE_NAME(__LINE__) (AZ::Debug::AssetTracking::Scope::ScopeFromAttachment((other), __FILE__, __LINE__))
///////////////////////////////////////////////////////////////////////////////
// Optional macros to manually enter and exit a scope.
// It is the responsibility of the user to make sure every call to AZ_ASSET_ENTER_SCOPE_* is matched by a corresponding call to AZ_ASSET_EXIT_SCOPE.
///////////////////////////////////////////////////////////////////////////////
# define AZ_ASSET_ENTER_SCOPE_BY_ASSET_ID(...) AZ::Debug::AssetTracking::EnterScopeByAssetId(__FILE__, __LINE__, __VA_ARGS__)
# define AZ_ASSET_ENTER_SCOPE_BY_ATTACHMENT(other) AZ::Debug::AssetTracking::EnterScopeByAttachment((other), __FILE__, __LINE__)
# define AZ_ASSET_EXIT_SCOPE AZ::Debug::AssetTracking::ExitScope()
#else
# define AZ_ASSET_NAMED_SCOPE(...) (void)0
# define AZ_ASSET_ATTACH_TO_SCOPE(other) (void)0
# define AZ_ASSET_ENTER_SCOPE_BY_ASSET_ID(...) (void)0
# define AZ_ASSET_ENTER_SCOPE_BY_ATTACHMENT(other) (void)0
# define AZ_ASSET_EXIT_SCOPE (void)0
#endif
namespace AZ
{
class ReflectContext;
namespace Debug
{
class AssetTrackingImpl;
class AssetTreeBase;
class AssetTreeNodeBase;
class AssetAllocationTableBase;
class AssetTracking
{
public:
AZ_TYPE_INFO(AssetTracking, "{D4335180-09A2-415A-8B50-9B734E7CE1E6}");
AZ_CLASS_ALLOCATOR(AssetTracking, OSAllocator, 0);
// Provide RAII method for entering and exiting scopes.
// Generally you will want to use the macros at the top of this file rather than instantiating this object directly.
class Scope
{
public:
static Scope ScopeFromAssetId(const char* file, int line, const char* fmt, ...);
static Scope ScopeFromAttachment(void* attachTo, const char* file, int line);
Scope(Scope&&) = default;
~Scope();
private:
Scope();
};
// Generally you will want to use the macros at the top of this file rather than calling these functions directly.
static void EnterScopeByAssetId(const char* file, int line, const char* fmt, ...);
static void EnterScopeByAttachment(void* attachTo, const char* file, int line);
static void ExitScope();
static const char* GetDebugScope();
AssetTracking(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable);
~AssetTracking();
AssetTreeNodeBase* GetCurrentThreadAsset() const;
private:
AZStd::unique_ptr<AssetTrackingImpl> m_impl;
};
// An EBus processing policy that attempts to attach to an existing scope before calling a handler.
//
// Use this on EBuses where you want the callees to track asset memory during their event handlers.
// This will work so long as the callees were themselves allocated inside an existing asset scope.
//
// May be added to an existing EBus with the following code:
// using EventProcessingPolicy = Debug::AssetTrackingEventProcessingPolicy;
//
template<typename Parent = EBusEventProcessingPolicy>
struct AssetTrackingEventProcessingPolicy
{
template<class Results, class Function, class Interface, class... InputArgs>
static void CallResult(Results& results, Function&& func, Interface&& iface, InputArgs&&... args)
{
AZ_ASSET_ATTACH_TO_SCOPE(iface);
Parent::CallResult(results, AZStd::forward<Function>(func), AZStd::forward<Interface>(iface), AZStd::forward<InputArgs>(args)...);
}
template<class Function, class Interface, class... InputArgs>
static void Call(Function&& func, Interface&& iface, InputArgs&&... args)
{
AZ_ASSET_ATTACH_TO_SCOPE(iface);
Parent::Call(AZStd::forward<Function>(func), AZStd::forward<Interface>(iface), AZStd::forward<InputArgs>(args)...);
}
};
}
} // namespace AzFramework
@@ -1,120 +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/Memory/HphaSchema.h>
#include <AzCore/Memory/SimpleSchemaAllocator.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
namespace Debug
{
struct AssetTrackingId;
}
}
namespace AZStd
{
// Declare hash specializations for types that need them; implementations will have to come after the classes are fully defined
template<>
struct hash<AZ::Debug::AssetTrackingId>
{
size_t operator()(const AZ::Debug::AssetTrackingId& id) const;
};
}
namespace AZ
{
namespace Debug
{
class AssetTrackingImpl;
// Custom allocator for the Analyzer that doesn't go through profiling tools and cannot be overridden
class AssetTrackingAllocator : public AZ::SimpleSchemaAllocator<AZ::HphaSchema, AZ::HphaSchema::Descriptor, false>
{
public:
AZ_TYPE_INFO(AssetTrackingAllocator, "{F6C08E92-559C-4153-9620-6A8491F78F10}");
using Base = AZ::SimpleSchemaAllocator<AZ::HphaSchema, AZ::HphaSchema::Descriptor, false>;
using Descriptor = Base::Descriptor;
AssetTrackingAllocator()
: Base("AssetTrackingAllocator", "Allocator for the AssetTracking")
{
DisableOverriding();
}
};
using AZStdAssetTrackingAllocator = AZ::AZStdAlloc<AssetTrackingAllocator>;
using AssetTrackingString = AZStd::basic_string<char, AZStd::char_traits<char>, AZStdAssetTrackingAllocator>;
template<typename Key, typename MappedType>
using AssetTrackingMap = AZStd::unordered_map<Key, MappedType, AZStd::hash<Key>, AZStd::equal_to<Key>, AZStdAssetTrackingAllocator>;
// ID for an asset that is hashable.
// Currently only contains one string identifier, but we may want to store a more sophisticated ID in the future.
struct AssetTrackingId
{
AssetTrackingId(const char* id) : m_id(id)
{
}
bool operator==(const AssetTrackingId& other) const
{
return m_id == other.m_id;
}
AssetTrackingString m_id;
};
// Primary information about an asset.
// Currently just contains the ID of the asset, but in the future may carry additional information about that asset (such as where in code it was initialized).
struct AssetPrimaryInfo
{
const AssetTrackingId* m_id;
};
// Base class for a node in the asset tree. Implemented by the template AssetTreeNode<>.
class AssetTreeNodeBase
{
public:
virtual ~AssetTreeNodeBase() = default;
virtual const AssetPrimaryInfo* GetAssetPrimaryInfo() const = 0;
virtual AssetTreeNodeBase* FindOrAddChild(const AssetTrackingId& id, const AssetPrimaryInfo* info) = 0;
};
// Base class for an asset tree. Implemented by the template AssetTree<>.
class AssetTreeBase
{
public:
virtual ~AssetTreeBase() = default;
virtual AssetTreeNodeBase& GetRoot() = 0;
};
// Base class for an asset allocation table. Implemented by the template AssetAllocationTable<>.
class AssetAllocationTableBase
{
public:
virtual ~AssetAllocationTableBase() = default;
virtual AssetTreeNodeBase* FindAllocation(void* ptr) const = 0;
};
}
}
///////////////////////////////////////////////////////////////////////////////
// Hash functions for map support
///////////////////////////////////////////////////////////////////////////////
inline size_t AZStd::hash<AZ::Debug::AssetTrackingId>::operator()(const AZ::Debug::AssetTrackingId& info) const
{
return AZStd::hash<AZ::Debug::AssetTrackingString>()(info.m_id);
}
@@ -1,174 +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/Debug/AssetTrackingTypes.h>
#include <AzCore/std/containers/map.h>
namespace AZ
{
namespace Debug
{
// A node in the current asset state tree.
// Each thread maintains a stack of currently in-scope assets. As this stack changes the asset tree forms.
// The same asset may appear in multiple places in the tree, e.g. if asset A is a common asset loaded by both asset B and asset C, the tree may look like:
// Root -> B -> A
// \--> C -> A
template<typename AssetDataT>
class AssetTreeNode : public AssetTreeNodeBase
{
public:
AssetTreeNode(const AssetPrimaryInfo* primaryInfo = nullptr, AssetTreeNode* parent = nullptr) :
m_primaryinfo(primaryInfo),
m_parent(parent)
{
}
~AssetTreeNode() override = default;
const AssetPrimaryInfo* GetAssetPrimaryInfo() const override
{
return m_primaryinfo;
}
AssetTreeNodeBase* FindOrAddChild(const AssetTrackingId& id, const AssetPrimaryInfo* info) override
{
AssetTreeNodeBase* result = nullptr;
auto childItr = m_children.find(id);
if (childItr != m_children.end())
{
result = &childItr->second;
}
else
{
auto childResult = m_children.emplace(id, AssetTreeNode(info, this));
result = &childResult.first->second;
}
return result;
}
using AssetMap = AssetTrackingMap<AssetTrackingId, AssetTreeNode>;
const AssetPrimaryInfo* m_primaryinfo;
AssetTreeNode* m_parent;
AssetMap m_children;
AssetDataT m_data;
};
template<typename AssetDataT>
class AssetTree : public AssetTreeBase
{
public:
~AssetTree() override = default;
AssetTreeNodeBase& GetRoot() override
{
return m_rootAssets;
}
using NodeType = AssetTreeNode<AssetDataT>;
NodeType m_rootAssets;
};
template<typename AllocationDataT>
struct AllocationRecord
{
AssetTreeNodeBase* m_asset;
uint32_t m_size;
AllocationDataT m_data;
};
template<typename AllocationDataT>
class AllocationTable : public AssetAllocationTableBase
{
public:
using RecordType = AllocationRecord<AllocationDataT>;
using AllocationReverseMap = AZStd::map<void*, RecordType, AZStd::greater<void*>, AZStdAssetTrackingAllocator>;
using mutex_type = AZStd::mutex;
using lock_type = AZStd::lock_guard<mutex_type>;
AllocationTable(mutex_type& mutex) : m_mutex(mutex)
{
}
~AllocationTable() override = default;
AssetTreeNodeBase* FindAllocation(void* ptr) const override
{
// Note that ptr is not guaranteed to have an exact entry in the map. For instance, ptr may point to a member of the original object that was allocated, or
// ptr may be a different "this" pointer in the case of multiple inheritance.
//
// To solve this, we use lower_bound() and check to see if ptr falls in the range of the nearest allocation. Our map uses AZStd::greater instead of
// AZStd::less as its sorting function, and thus sorts largest-to-smallest instead of smallest-to-largest. This causes lower_bound() to return the first
// iterator that is not greater than otherAllocation, i.e. less than or equal to ptr.
lock_type lock(m_mutex);
auto itr = m_allocationTable.lower_bound(ptr);
AssetTreeNodeBase* result = nullptr;
if (itr != m_allocationTable.end())
{
// Check if otherAllocation is within the size range of the allocation we found
if (reinterpret_cast<uintptr_t>(ptr) <= reinterpret_cast<uintptr_t>(itr->first) + itr->second.m_size)
{
result = itr->second.m_asset;
}
}
return result;
}
void ReallocateAllocation(void* prevAddress, void* newAddress, size_t newByteSize)
{
lock_type lock(m_mutex);
auto itr = m_allocationTable.find(prevAddress);
if (itr != m_allocationTable.end())
{
RecordType newAllocation = itr->second;
newAllocation.m_size = (uint32_t)newByteSize;
m_allocationTable.erase(itr);
m_allocationTable.emplace(newAddress, AZStd::move(newAllocation));
}
}
void ResizeAllocation(void* address, size_t newSize)
{
// Resize an existing allocation if we can find it
lock_type lock(m_mutex);
auto itr = m_allocationTable.find(address);
if (itr != m_allocationTable.end())
{
itr->second.m_size = (uint32_t)newSize;
}
}
AllocationReverseMap& Get()
{
return m_allocationTable;
}
const AllocationReverseMap& Get() const
{
return m_allocationTable;
}
private:
AllocationReverseMap m_allocationTable;
mutex_type& m_mutex;
};
}
}
@@ -1,29 +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
*
*/
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/Debug/EventTraceDrillerBus.h>
#include <AzCore/std/time.h>
#include <AzCore/std/parallel/thread.h>
namespace AZ::Debug
{
EventTrace::ScopedSlice::ScopedSlice(const char* name, const char* category)
: m_Name(name)
, m_Category(category)
, m_Time(AZStd::GetTimeNowMicroSecond())
{
}
EventTrace::ScopedSlice::~ScopedSlice()
{
EventTraceDrillerBus::TryQueueBroadcast(
&EventTraceDrillerInterface::RecordSlice, m_Name, m_Category, AZStd::this_thread::get_id(), m_Time,
(uint32_t)(AZStd::GetTimeNowMicroSecond() - m_Time));
}
} // namespace AZ::Debug
@@ -1,43 +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/base.h>
#include <AzCore/PlatformDef.h>
#include <AzCore/Debug/Profiler.h>
namespace AZStd
{
struct thread_id;
}
namespace AZ
{
namespace Debug
{
namespace EventTrace
{
class ScopedSlice
{
public:
ScopedSlice(const char* name, const char* category);
~ScopedSlice();
private:
const char* m_Name;
const char* m_Category;
u64 m_Time;
};
}
}
}
#define AZ_TRACE_METHOD_NAME_CATEGORY(name, category)
#define AZ_TRACE_METHOD_NAME(name) AZ_TRACE_METHOD_NAME_CATEGORY(name, "")
#define AZ_TRACE_METHOD() AZ_TRACE_METHOD_NAME(AZ_FUNCTION_SIGNATURE)
@@ -1,159 +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
*
*/
#include <AzCore/Debug/EventTraceDriller.h>
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/std/containers/array.h>
#include <algorithm>
namespace AZ::Debug
{
namespace Crc
{
constexpr u32 EventTraceDriller = AZ_CRC_CE("EventTraceDriller");
constexpr u32 Slice = AZ_CRC_CE("Slice");
constexpr u32 ThreadInfo = AZ_CRC_CE("ThreadInfo");
constexpr u32 Name = AZ_CRC_CE("Name");
constexpr u32 Category = AZ_CRC_CE("Category");
constexpr u32 ThreadId = AZ_CRC_CE("ThreadId");
constexpr u32 Timestamp = AZ_CRC_CE("Timestamp");
constexpr u32 Duration = AZ_CRC_CE("Duration");
constexpr u32 Instant = AZ_CRC_CE("Instant");
}
EventTraceDriller::EventTraceDriller()
{
EventTraceDrillerSetupBus::Handler::BusConnect();
AZStd::ThreadDrillerEventBus::Handler::BusConnect();
}
EventTraceDriller::~EventTraceDriller()
{
AZStd::ThreadDrillerEventBus::Handler::BusDisconnect();
EventTraceDrillerSetupBus::Handler::BusDisconnect();
}
void EventTraceDriller::Start(const Param* params, int numParams)
{
(void)params;
(void)numParams;
EventTraceDrillerBus::Handler::BusConnect();
TickBus::Handler::BusConnect();
EventTraceDrillerBus::AllowFunctionQueuing(true);
}
void EventTraceDriller::Stop()
{
EventTraceDrillerBus::AllowFunctionQueuing(false);
EventTraceDrillerBus::ClearQueuedEvents();
EventTraceDrillerBus::Handler::BusDisconnect();
TickBus::Handler::BusDisconnect();
}
void EventTraceDriller::OnTick(float deltaTime, ScriptTimePoint time)
{
(void)deltaTime;
(void)time;
AZ_TRACE_METHOD();
RecordThreads();
EventTraceDrillerBus::ExecuteQueuedEvents();
}
void EventTraceDriller::SetThreadName(const AZStd::thread_id& id, const char* name)
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_ThreadMutex);
m_Threads[(size_t)id.m_id] = ThreadData{ name };
}
void EventTraceDriller::OnThreadEnter(const AZStd::thread::id& id, const AZStd::thread_desc* desc)
{
if (desc && desc->m_name)
{
SetThreadName(id, desc->m_name);
}
}
void EventTraceDriller::OnThreadExit(const AZStd::thread::id& id)
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_ThreadMutex);
m_Threads.erase((size_t)id.m_id);
}
void EventTraceDriller::RecordThreads()
{
if (!m_output || m_Threads.empty())
{
return;
}
// Main bus mutex guards m_output.
auto& context = EventTraceDrillerBus::GetOrCreateContext();
AZStd::scoped_lock<decltype(context.m_contextMutex), decltype(m_ThreadMutex)> lock(context.m_contextMutex, m_ThreadMutex);
for (const auto& keyValue : m_Threads)
{
m_output->BeginTag(Crc::EventTraceDriller);
m_output->BeginTag(Crc::ThreadInfo);
m_output->Write(Crc::ThreadId, keyValue.first);
m_output->Write(Crc::Name, keyValue.second.name);
m_output->EndTag(Crc::ThreadInfo);
m_output->EndTag(Crc::EventTraceDriller);
}
}
void EventTraceDriller::RecordSlice(
const char* name,
const char* category,
const AZStd::thread_id threadId,
AZ::u64 timestamp,
AZ::u32 duration)
{
m_output->BeginTag(Crc::EventTraceDriller);
m_output->BeginTag(Crc::Slice);
m_output->Write(Crc::Name, name);
m_output->Write(Crc::Category, category);
m_output->Write(Crc::ThreadId, (size_t)threadId.m_id);
m_output->Write(Crc::Timestamp, timestamp);
m_output->Write(Crc::Duration, std::max(duration, 1u));
m_output->EndTag(Crc::Slice);
m_output->EndTag(Crc::EventTraceDriller);
}
void EventTraceDriller::RecordInstantGlobal(
const char* name,
const char* category,
AZ::u64 timestamp)
{
m_output->BeginTag(Crc::EventTraceDriller);
m_output->BeginTag(Crc::Instant);
m_output->Write(Crc::Name, name);
m_output->Write(Crc::Category, category);
m_output->Write(Crc::Timestamp, timestamp);
m_output->EndTag(Crc::Instant);
m_output->EndTag(Crc::EventTraceDriller);
}
void EventTraceDriller::RecordInstantThread(
const char* name,
const char* category,
const AZStd::thread_id threadId,
AZ::u64 timestamp)
{
m_output->BeginTag(Crc::EventTraceDriller);
m_output->BeginTag(Crc::Instant);
m_output->Write(Crc::Name, name);
m_output->Write(Crc::Category, category);
m_output->Write(Crc::ThreadId, (size_t)threadId.m_id);
m_output->Write(Crc::Timestamp, timestamp);
m_output->EndTag(Crc::Instant);
m_output->EndTag(Crc::EventTraceDriller);
}
} // namespace AZ::Debug
@@ -1,87 +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/Driller/Driller.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/std/parallel/threadbus.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/Debug/EventTraceDrillerBus.h>
namespace AZ
{
namespace Debug
{
class EventTraceDriller
: public Driller
, public EventTraceDrillerBus::Handler
, public EventTraceDrillerSetupBus::Handler
, public AZStd::ThreadDrillerEventBus::Handler
, public AZ::TickBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(EventTraceDriller, OSAllocator, 0)
EventTraceDriller();
virtual ~EventTraceDriller();
private:
// Driller
//////////////////////////////////////////////////////////////////////////
const char* GroupName() const override { return "SystemDrillers"; }
const char* GetName() const override { return "EventTraceDriller"; }
const char* GetDescription() const override { return "Handles timed events for a Chrome Tracing."; }
void Start(const Param* params = NULL, int numParams = 0) override;
void Stop() override;
// ThreadBus
//////////////////////////////////////////////////////////////////////////
void OnThreadEnter(const AZStd::thread::id& id, const AZStd::thread_desc* desc) override;
void OnThreadExit(const AZStd::thread::id& id) override;
// TickBus
//////////////////////////////////////////////////////////////////////////
void OnTick(float deltaTime, ScriptTimePoint time) override;
// EventTraceDrillerSetupBus
//////////////////////////////////////////////////////////////////////////
void SetThreadName(const AZStd::thread_id& threadId, const char* name) override;
// EventTraceDrillerBus
//////////////////////////////////////////////////////////////////////////
void RecordSlice(
const char* name,
const char* category,
const AZStd::thread_id threadId,
AZ::u64 timestamp,
AZ::u32 duration) override;
void RecordInstantGlobal(
const char* name,
const char* category,
AZ::u64 timestamp) override;
void RecordInstantThread(
const char* name,
const char* category,
const AZStd::thread_id threadId,
AZ::u64 timestamp) override;
void RecordThreads();
struct ThreadData
{
AZStd::string name;
};
AZStd::recursive_mutex m_ThreadMutex;
AZStd::unordered_map<size_t, ThreadData, AZStd::hash<size_t>, AZStd::equal_to<size_t>, OSStdAllocator> m_Threads;
};
}
} // namespace AZ
@@ -1,81 +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/Driller/DrillerBus.h>
#include <AzCore/std/time.h>
#include <AzCore/std/string/string.h>
namespace AZStd
{
struct thread_id;
}
namespace AZ
{
namespace Debug
{
class EventTraceDrillerInterface
: public DrillerEBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const bool EnableEventQueue = true;
static const bool EventQueueingActiveByDefault = false;
//////////////////////////////////////////////////////////////////////////
virtual ~EventTraceDrillerInterface() {}
virtual void RecordSlice(
const char* name,
const char* category,
const AZStd::thread_id threadId,
AZ::u64 timestamp,
AZ::u32 duration) = 0;
virtual void RecordInstantThread(
const char* name,
const char* category,
const AZStd::thread_id threadId,
AZ::u64 timestamp) = 0;
virtual void RecordInstantGlobal(
const char* name,
const char* category,
AZ::u64 timestamp) = 0;
};
typedef AZ::EBus<EventTraceDrillerInterface> EventTraceDrillerBus;
class EventTraceDrillerSetupInterface
: public DrillerEBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
//////////////////////////////////////////////////////////////////////////
virtual ~EventTraceDrillerSetupInterface() {}
virtual void SetThreadName(const AZStd::thread_id& threadId, const char* name) = 0;
};
typedef AZ::EBus<EventTraceDrillerSetupInterface> EventTraceDrillerSetupBus;
}
}
#define AZ_TRACE_INSTANT_GLOBAL_CATEGORY(name, category) \
EBUS_QUEUE_EVENT(AZ::Debug::EventTraceDrillerBus, RecordInstantGlobal, name, category, AZStd::GetTimeNowMicroSecond())
#define AZ_TRACE_INSTANT_GLOBAL(name) AZ_TRACE_INSTANT_GLOBAL_CATEGORY(name, "")
#define AZ_TRACE_INSTANT_THREAD_CATEGORY(name, category) \
EBUS_QUEUE_EVENT(AZ::Debug::EventTraceDrillerBus, RecordInstantThread, name, category, AZStd::this_thread::get_id(), AZStd::GetTimeNowMicroSecond())
#define AZ_TRACE_INSTANT_THREAD(name) AZ_TRACE_INSTANT_THREAD_CATEGORY(name, "")
@@ -8,7 +8,7 @@
#pragma once
#ifndef AZ_PROFILE_MEMORY_ALLOC
// No other profiler has defined the performance markers AZ_PROFILE_MEMORY_ALLOC (and friends), fall back to a Driller implementation (currently empty)
// No other profiler has defined the performance markers AZ_PROFILE_MEMORY_ALLOC (and friends), fall back to current implementation (empty)
# define AZ_PROFILE_MEMORY_ALLOC(category, address, size, context)
# define AZ_PROFILE_MEMORY_ALLOC_EX(category, filename, lineNumber, address, size, context)
# define AZ_PROFILE_MEMORY_FREE(category, address)
@@ -12,7 +12,6 @@
#include <AzCore/Debug/StackTracer.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/Debug/TraceMessagesDrillerBus.h>
#include <AzCore/Debug/IEventLogger.h>
#include <AzCore/Interface/Interface.h>
@@ -276,8 +275,6 @@ namespace AZ::Debug
logger->Flush(); // Flush as an assert may indicate a crash is imminent.
}
EBUS_EVENT(TraceMessageDrillerBus, OnPreAssert, fileName, line, funcName, message);
TraceMessageResult result;
EBUS_EVENT_RESULT(result, TraceMessageBus, OnPreAssert, fileName, line, funcName, message);
@@ -302,7 +299,6 @@ namespace AZ::Debug
azstrcat(message, g_maxMessageLength, "\n");
Output(g_dbgSystemWnd, message);
EBUS_EVENT(TraceMessageDrillerBus, OnAssert, message);
EBUS_EVENT_RESULT(result, TraceMessageBus, OnAssert, message);
if (result.m_value)
{
@@ -405,8 +401,6 @@ namespace AZ::Debug
logger->RecordStringEvent(ErrorEventId, message);
}
EBUS_EVENT(TraceMessageDrillerBus, OnPreError, window, fileName, line, funcName, message);
TraceMessageResult result;
EBUS_EVENT_RESULT(result, TraceMessageBus, OnPreError, window, fileName, line, funcName, message);
if (result.m_value)
@@ -421,7 +415,6 @@ namespace AZ::Debug
azstrcat(message, g_maxMessageLength, "\n");
Output(window, message);
EBUS_EVENT(TraceMessageDrillerBus, OnError, window, message);
EBUS_EVENT_RESULT(result, TraceMessageBus, OnError, window, message);
Output(window, "==================================================================\n");
if (result.m_value)
@@ -457,8 +450,6 @@ namespace AZ::Debug
logger->RecordStringEvent(WarningEventId, message);
}
EBUS_EVENT(TraceMessageDrillerBus, OnPreWarning, window, fileName, line, funcName, message);
TraceMessageResult result;
EBUS_EVENT_RESULT(result, TraceMessageBus, OnPreWarning, window, fileName, line, funcName, message);
if (result.m_value)
@@ -472,7 +463,6 @@ namespace AZ::Debug
azstrcat(message, g_maxMessageLength, "\n");
Output(window, message);
EBUS_EVENT(TraceMessageDrillerBus, OnWarning, window, message);
EBUS_EVENT_RESULT(result, TraceMessageBus, OnWarning, window, message);
Output(window, "==================================================================\n");
}
@@ -501,8 +491,6 @@ namespace AZ::Debug
logger->RecordStringEvent(PrintfEventId, message);
}
EBUS_EVENT(TraceMessageDrillerBus, OnPrintf, window, message);
TraceMessageResult result;
EBUS_EVENT_RESULT(result, TraceMessageBus, OnPrintf, window, message);
if (result.m_value)
@@ -531,7 +519,6 @@ namespace AZ::Debug
// only call into Ebusses if we are not in a recursive-exception situation as that
// would likely just lead to even more exceptions.
EBUS_EVENT(TraceMessageDrillerBus, OnOutput, window, message);
TraceMessageResult result;
EBUS_EVENT_RESULT(result, TraceMessageBus, OnOutput, window, message);
if (result.m_value)
@@ -1,99 +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
*
*/
#include <AzCore/Debug/TraceMessagesDriller.h>
#include <AzCore/Math/Crc.h>
namespace AZ::Debug
{
//=========================================================================
// Start
// [2/6/2013]
//=========================================================================
void TraceMessagesDriller::Start(const Param* params, int numParams)
{
(void)params;
(void)numParams;
BusConnect();
}
//=========================================================================
// Stop
// [2/6/2013]
//=========================================================================
void TraceMessagesDriller::Stop()
{
BusDisconnect();
}
//=========================================================================
// OnAssert
// [2/6/2013]
//=========================================================================
void TraceMessagesDriller::OnAssert(const char* message)
{
// Not sure if we can really capture assert since the code will stop executing very soon.
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
m_output->Write(AZ_CRC_CE("OnAssert"), message);
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
}
//=========================================================================
// OnException
// [2/6/2013]
//=========================================================================
void TraceMessagesDriller::OnException(const char* message)
{
// Not sure if we can really capture exception since the code will stop executing very soon.
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
m_output->Write(AZ_CRC_CE("OnException"), message);
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
}
//=========================================================================
// OnError
// [2/6/2013]
//=========================================================================
void TraceMessagesDriller::OnError(const char* window, const char* message)
{
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
m_output->BeginTag(AZ_CRC_CE("OnError"));
m_output->Write(AZ_CRC_CE("Window"), window);
m_output->Write(AZ_CRC_CE("Message"), message);
m_output->EndTag(AZ_CRC_CE("OnError"));
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
}
//=========================================================================
// OnWarning
// [2/6/2013]
//=========================================================================
void TraceMessagesDriller::OnWarning(const char* window, const char* message)
{
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
m_output->BeginTag(AZ_CRC_CE("OnWarning"));
m_output->Write(AZ_CRC_CE("Window"), window);
m_output->Write(AZ_CRC_CE("Message"), message);
m_output->EndTag(AZ_CRC_CE("OnWarning"));
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
}
//=========================================================================
// OnPrintf
// [2/6/2013]
//=========================================================================
void TraceMessagesDriller::OnPrintf(const char* window, const char* message)
{
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
m_output->BeginTag(AZ_CRC_CE("OnPrintf"));
m_output->Write(AZ_CRC_CE("Window"), window);
m_output->Write(AZ_CRC_CE("Message"), message);
m_output->EndTag(AZ_CRC_CE("OnPrintf"));
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
}
} // namespace AZ
@@ -1,49 +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/Driller/Driller.h>
#include <AzCore/Debug/TraceMessagesDrillerBus.h>
namespace AZ
{
namespace Debug
{
/**
* Trace messages driller class
*/
class TraceMessagesDriller
: public Driller
, public TraceMessageDrillerBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(TraceMessagesDriller, OSAllocator, 0)
protected:
//////////////////////////////////////////////////////////////////////////
// Driller
const char* GroupName() const override { return "SystemDrillers"; }
const char* GetName() const override { return "TraceMessagesDriller"; }
const char* GetDescription() const override { return "Handles all system messages like Assert, Exception, Error, Warning, Printf, etc."; }
void Start(const Param* params = NULL, int numParams = 0) override;
void Stop() override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// TraceMessagesDrillerBus
/// Triggered when a AZ_Assert failed. This is terminating event! (the code will break, crash).
void OnAssert(const char* message) override;
void OnException(const char* message) override;
void OnError(const char* window, const char* message) override;
void OnWarning(const char* window, const char* message) override;
void OnPrintf(const char* window, const char* message) override;
//////////////////////////////////////////////////////////////////////////
};
} // namespace Debug
} // namespace AZ
@@ -1,53 +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/Driller/DrillerBus.h>
namespace AZ
{
namespace Debug
{
/**
* Trace messages event handle.
* All messages are optional (they have default implementation) and you can handle only one at a time.
* Driller messages are similar to TraceMessages, but do not provide a return value,
* as we only care about collecting driller messages, not operating on them.
*
* We use a driller bus so all messages are sending in exclusive matter no other driller messages
* can be triggered at that moment, so we already preserve the calling order. You can assume
* all access code in the driller framework in guarded. You can manually lock the driller mutex are you
* use by using \ref AZ::Debug::DrillerEBusMutex.
*/
class TraceMessageDrillerEvents
: public DrillerEBusTraits
{
public:
virtual ~TraceMessageDrillerEvents() {}
/// Triggered when a AZ_Assert failed. This is terminating event! (the code will break, crash).
virtual void OnPreAssert(const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) {}
virtual void OnAssert(const char* /*message*/) {}
virtual void OnException(const char* /*message*/) {}
virtual void OnPreError(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) {}
virtual void OnError(const char* /*window*/, const char* /*message*/) {}
virtual void OnPreWarning(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) {}
virtual void OnWarning(const char* /*window*/, const char* /*message*/) {}
virtual void OnPrintf(const char* /*window*/, const char* /*message*/) {}
/**
* All trace functions you output to anything. So if you want to handle all the output this is the place.
* You are not given the choice to disable the system output as if you listen at that level you can't make
* that decision. Otherwise we can trigger an assert without even one line of message send to the console/debugger.
*/
virtual void OnOutput(const char* /*window*/, const char* /*message*/) {}
};
typedef AZ::EBus<TraceMessageDrillerEvents> TraceMessageDrillerBus;
} // namespace Debug
} // namespace AZ
@@ -1,125 +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
*
*/
#ifndef AZCORE_DRILLER_DEFAULT_STRING_POOL_H
#define AZCORE_DRILLER_DEFAULT_STRING_POOL_H
#include <AzCore/Driller/Stream.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/unordered_set.h>
namespace AZ
{
namespace Debug
{
template<class Key, class Mapped>
struct unordered_map
{
typedef AZStd::unordered_map<Key, Mapped, AZStd::hash<Key>, AZStd::equal_to<Key>, OSStdAllocator> type;
};
template<class Key>
struct unordered_set
{
typedef AZStd::unordered_set<Key, AZStd::hash<Key>, AZStd::equal_to<Key>, OSStdAllocator> type;
};
/**
* Default implementation of a string pool.
*/
class DrillerDefaultStringPool
: public DrillerStringPool
{
public:
virtual ~DrillerDefaultStringPool()
{
Reset();
}
typedef unordered_map<AZ::u32, const char*>::type CrcToStringMapType;
typedef unordered_set<const char*>::type OwnedStringsMapType;
/**
* Add a copy of the string to the pool. If we return true the string was added otherwise it was already in the bool.
* In both cases the crc32 of the string and the pointer to the shared copy is returned (optional for poolStringAddress)!
*/
virtual bool InsertCopy(const char* string, unsigned int length, AZ::u32& crc32, const char** poolStringAddress = nullptr)
{
crc32 = AZ::Crc32(string, length);
CrcToStringMapType::pair_iter_bool insertIt = m_crcToStringMap.insert_key(crc32);
if (insertIt.second)
{
char* newString = reinterpret_cast<char*>(azmalloc(length + 1, 1, AZ::OSAllocator));
memcpy(newString, string, length);
newString[length] = '\0'; // terminate
m_ownedStrings.insert(newString);
insertIt.first->second = newString;
}
if (poolStringAddress)
{
*poolStringAddress = insertIt.first->second;
}
return insertIt.second;
}
/**
* Same as the InsertCopy above without actually coping the string into the pool. The pool assumes that
* none of the strings added to the pool will be deleted.
*/
virtual bool Insert(const char* string, unsigned int length, AZ::u32& crc32)
{
crc32 = AZ::Crc32(string, length);
return m_crcToStringMap.insert(AZStd::make_pair(crc32, string)).second;
}
/// Finds a string in the pool by crc32.
virtual const char* Find(AZ::u32 crc32)
{
CrcToStringMapType::iterator it = m_crcToStringMap.find(crc32);
if (it != m_crcToStringMap.end())
{
return it->second;
}
return NULL;
}
virtual void Erase(AZ::u32 crc32)
{
CrcToStringMapType::iterator it = m_crcToStringMap.find(crc32);
if (it != m_crcToStringMap.end())
{
OwnedStringsMapType::iterator ownerIt = m_ownedStrings.find(it->second);
if (ownerIt != m_ownedStrings.end())
{
azfree(const_cast<char*>(it->second), AZ::OSAllocator);
m_ownedStrings.erase(ownerIt);
}
m_crcToStringMap.erase(it);
}
}
virtual void Reset()
{
for (OwnedStringsMapType::iterator it = m_ownedStrings.begin(); it != m_ownedStrings.end(); ++it)
{
azfree(const_cast<char*>(*it), AZ::OSAllocator);
}
m_crcToStringMap.clear();
m_ownedStrings.clear();
}
protected:
CrcToStringMapType m_crcToStringMap;
OwnedStringsMapType m_ownedStrings;
};
} // namespace Debug
} // namespace AZ
#endif // AZCORE_DRILLER_DEFAULT_STRING_POOL_H
#pragma once
@@ -1,301 +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
*
*/
#include <AzCore/Driller/Driller.h>
#include <AzCore/Driller/DrillerBus.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/parallel/lock.h>
#include <AzCore/Math/Crc.h>
namespace AZ::Debug
{
class DrillerManagerImpl
: public DrillerManager
{
public:
AZ_CLASS_ALLOCATOR(DrillerManagerImpl, OSAllocator, 0);
using SessionListType = forward_list<DrillerSession>::type;
SessionListType m_sessions;
using DrillerArrayType = vector<Driller*>::type;
DrillerArrayType m_drillers;
~DrillerManagerImpl() override;
void Register(Driller* factory) override;
void Unregister(Driller* factory) override;
void FrameUpdate() override;
DrillerSession* Start(DrillerOutputStream& output, const DrillerListType& drillerList, int numFrames = -1) override;
void Stop(DrillerSession* session) override;
int GetNumDrillers() const override { return static_cast<int>(m_drillers.size()); }
Driller* GetDriller(int index) override { return m_drillers[index]; }
};
//////////////////////////////////////////////////////////////////////////
// Driller
//=========================================================================
// Register
// [3/17/2011]
//=========================================================================
AZ::u32 Driller::GetId() const
{
return AZ::Crc32(GetName());
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Driller Manager
//=========================================================================
// Register
// [3/17/2011]
//=========================================================================
DrillerManager* DrillerManager::Create(/*const Descriptor& desc*/)
{
const bool createAllocator = !AZ::AllocatorInstance<OSAllocator>::IsReady();
if (createAllocator)
{
AZ::AllocatorInstance<OSAllocator>::Create();
}
DrillerManagerImpl* impl = aznew DrillerManagerImpl;
impl->m_ownsOSAllocator = createAllocator;
return impl;
}
//=========================================================================
// Register
// [3/17/2011]
//=========================================================================
void DrillerManager::Destroy(DrillerManager* manager)
{
const bool allocatorCreated = manager->m_ownsOSAllocator;
delete manager;
if (allocatorCreated)
{
AZ::AllocatorInstance<OSAllocator>::Destroy();
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// DrillerManagerImpl
//=========================================================================
// ~DrillerManagerImpl
// [3/17/2011]
//=========================================================================
DrillerManagerImpl::~DrillerManagerImpl()
{
while (!m_sessions.empty())
{
Stop(&m_sessions.front());
}
while (!m_drillers.empty())
{
Driller* driller = m_drillers[0];
Unregister(driller);
delete driller;
}
}
//=========================================================================
// Register
// [3/17/2011]
//=========================================================================
void
DrillerManagerImpl::Register(Driller* driller)
{
AZ_Assert(driller, "You must provide a valid factory!");
for (size_t i = 0; i < m_drillers.size(); ++i)
{
if (m_drillers[i]->GetId() == driller->GetId())
{
AZ_Error("Debug", false, "Driller with id %08x has already been registered! You can't have two factory instances for the same driller type", driller->GetId());
return;
}
}
m_drillers.push_back(driller);
}
//=========================================================================
// Unregister
// [3/17/2011]
//=========================================================================
void
DrillerManagerImpl::Unregister(Driller* driller)
{
AZ_Assert(driller, "You must provide a valid factory!");
for (DrillerArrayType::iterator iter = m_drillers.begin(); iter != m_drillers.end(); ++iter)
{
if ((*iter)->GetId() == driller->GetId())
{
m_drillers.erase(iter);
return;
}
}
AZ_Error("Debug", false, "Failed to find driller factory with id %08x", driller->GetId());
}
//=========================================================================
// FrameUpdate
// [3/17/2011]
//=========================================================================
void
DrillerManagerImpl::FrameUpdate()
{
if (m_sessions.empty())
{
return;
}
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex()); ///< Make sure no driller is writing to the stream
for (SessionListType::iterator sessionIter = m_sessions.begin(); sessionIter != m_sessions.end(); )
{
DrillerSession& s = *sessionIter;
// tick the drillers directly if they care.
for (size_t i = 0; i < s.drillers.size(); ++i)
{
s.drillers[i]->Update();
}
s.output->EndTag(AZ_CRC("Frame", 0xb5f83ccd));
s.output->OnEndOfFrame();
s.curFrame++;
if (s.numFrames != -1)
{
if (s.curFrame == s.numFrames)
{
Stop(&s);
continue;
}
}
s.output->BeginTag(AZ_CRC("Frame", 0xb5f83ccd));
s.output->Write(AZ_CRC("FrameNum", 0x85a1a919), s.curFrame);
++sessionIter;
}
}
//=========================================================================
// Start
// [3/17/2011]
//=========================================================================
DrillerSession*
DrillerManagerImpl::Start(DrillerOutputStream& output, const DrillerListType& drillerList, int numFrames)
{
if (drillerList.empty())
{
return nullptr;
}
m_sessions.push_back();
DrillerSession& s = m_sessions.back();
s.curFrame = 0;
s.numFrames = numFrames;
s.output = &output;
s.output->WriteHeader(); // first write the header in the stream
s.output->BeginTag(AZ_CRC("StartData", 0xecf3f53f));
s.output->Write(AZ_CRC("Platform", 0x3952d0cb), (unsigned int)g_currentPlatform);
for (DrillerListType::const_iterator iDriller = drillerList.begin(); iDriller != drillerList.end(); ++iDriller)
{
const DrillerInfo& di = *iDriller;
s.output->BeginTag(AZ_CRC("Driller", 0xa6e1fb73));
s.output->Write(AZ_CRC("Name", 0x5e237e06), di.id);
for (int iParam = 0; iParam < (int)di.params.size(); ++iParam)
{
s.output->BeginTag(AZ_CRC("Param", 0xa4fa7c89));
s.output->Write(AZ_CRC("Name", 0x5e237e06), di.params[iParam].name);
s.output->Write(AZ_CRC("Description", 0x6de44026), di.params[iParam].desc);
s.output->Write(AZ_CRC("Type", 0x8cde5729), di.params[iParam].type);
s.output->Write(AZ_CRC("Value", 0x1d775834), di.params[iParam].value);
s.output->EndTag(AZ_CRC("Param", 0xa4fa7c89));
}
s.output->EndTag(AZ_CRC("Driller", 0xa6e1fb73));
}
s.output->EndTag(AZ_CRC("StartData", 0xecf3f53f));
s.output->BeginTag(AZ_CRC("Frame", 0xb5f83ccd));
s.output->Write(AZ_CRC("FrameNum", 0x85a1a919), s.curFrame);
{
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex()); ///< Make sure no driller is writing to the stream
for (DrillerListType::const_iterator iDriller = drillerList.begin(); iDriller != drillerList.end(); ++iDriller)
{
Driller* driller = nullptr;
const DrillerInfo& di = *iDriller;
for (size_t iDesc = 0; iDesc < m_drillers.size(); ++iDesc)
{
if (m_drillers[iDesc]->GetId() == di.id)
{
driller = m_drillers[iDesc];
AZ_Assert(driller->m_output == nullptr, "Driller with id %08x is already have an output stream %p (currently we support only 1 at a time)", di.id, driller->m_output);
driller->m_output = &output;
driller->Start(di.params.data(), static_cast<unsigned int>(di.params.size()));
s.drillers.push_back(driller);
break;
}
}
AZ_Warning("Driller", driller != nullptr, "We can't start a driller with id %d!", di.id);
}
}
return &s;
}
//=========================================================================
// Stop
// [3/17/2011]
//=========================================================================
void
DrillerManagerImpl::Stop(DrillerSession* session)
{
SessionListType::iterator iter;
for (iter = m_sessions.begin(); iter != m_sessions.end(); ++iter)
{
if (&*iter == session)
{
break;
}
}
AZ_Assert(iter != m_sessions.end(), "We did not find session ID 0x%08x in the list!", session);
if (iter != m_sessions.end())
{
DrillerSession& s = *session;
{
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex());
for (size_t i = 0; i < s.drillers.size(); ++i)
{
s.drillers[i]->Stop();
s.drillers[i]->m_output = nullptr;
}
}
s.output->EndTag(AZ_CRC("Frame", 0xb5f83ccd));
m_sessions.erase(iter);
}
}
} // namespace AZ::Debug
@@ -1,141 +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
*
*/
#ifndef AZCORE_DRILLER_H
#define AZCORE_DRILLER_H
#include <AzCore/Driller/Stream.h>
namespace AZStd
{
class mutex;
}
namespace AZ
{
namespace Debug
{
class DrillerOutputStream;
/**
* Driller base class. Every driller should inherit from this class.
* When a driller is need to start outputting data
* the DrillerManager will call Driller::Start() so the driller
* can output the initial state for all reported entities.
* The same applies for the Stop.
* Depending on the type of your driller you might choose to collect state
* even before the driller has started. This of course should be a fast as
* possible, as we don't want to burden engine systems and it's highly recommended
* that you use configuration parameters to change that behavior as not all drillers
* are used on a daily basis.
* All drillers should use DebugAllocators (AZ_CLASS_ALLOCATOR(Driller,OSAllocator,0))
* and they should use 'aznew' to create one, as by default if you don't unregister a
* a driller, the manager will use "delete" to delete them.
*
* IMPORTANT: Driller systems works OUTSIDE engine systems, you should NOT use SystemAllocator or any other engine systems
* as they might be drilled or not available at the moment.
*/
class Driller
{
friend class DrillerManagerImpl;
public:
struct Param
{
enum Type
{
PT_BOOL,
PT_INT,
PT_FLOAT
};
const char* desc;
u32 name;
int type;
int value;
};
Driller()
: m_output(NULL) {}
virtual ~Driller() {}
/// Returns the driller ID Crc32 of the name (Crc32(GetName())
AZ::u32 GetId() const;
/// Driller group name, used only for organizational purpose
virtual const char* GroupName() const = 0;
/// Unique name of the Driller, driller ID is the Crc of the name
virtual const char* GetName() const = 0;
virtual const char* GetDescription() const = 0;
// @{ Managing the list of supported driller parameters.
virtual int GetNumParams() const { return 0; }
virtual const Param* GetParam(int index) const { (void)index; return NULL; }
protected:
Driller& operator=(const Driller&);
/// Called by DrillerManager
virtual void Start(const Param* params = NULL, int numParams = 0) { (void)params; (void)numParams; }
/// Called by DrillerManager
virtual void Stop() {}
/// Called every frame by DrillerManger (while the driller is started)
virtual void Update() {}
DrillerOutputStream* m_output; ///< Session output stream.
};
/**
* Stores the information while an active
* driller(s) session is running.
*/
struct DrillerSession
{
int numFrames;
int curFrame;
typedef vector<Driller*>::type DrillerArrayType;
DrillerArrayType drillers;
DrillerOutputStream* output;
};
/**
* Driller manager will manage all active driller sessions and driller factories. Generally you will never
* need more than one driller manger.
* IMPORTANT: Driller systems works OUTSIDE engine systems, you should NOT use SystemAllocator or any other engine systems
* as they might be drilled or not available at the moment.
*/
class DrillerManager
{
friend class DrillerRemoteServer;
public:
struct DrillerInfo
{
AZ::u32 id;
vector<Driller::Param>::type params;
};
typedef forward_list<DrillerInfo>::type DrillerListType;
virtual ~DrillerManager() {}
static DrillerManager* Create(/*const Descriptor& desc*/);
static void Destroy(DrillerManager* manager);
virtual void Register(Driller* driller) = 0;
virtual void Unregister(Driller* driller) = 0;
virtual void FrameUpdate() = 0;
virtual DrillerSession* Start(DrillerOutputStream& output, const DrillerListType& drillerList, int numFrames = -1) = 0;
virtual void Stop(DrillerSession* session) = 0;
virtual int GetNumDrillers() const = 0;
virtual Driller* GetDriller(int index) = 0;
private:
// If the manager created the allocator, it should destroy it when it gets destroyed
bool m_ownsOSAllocator = false;
};
}
}
#endif // AZCORE_DRILLER_H
#pragma once
@@ -1,65 +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
*
*/
#include <AzCore/Driller/DrillerBus.h>
#include <AzCore/Driller/Driller.h>
#include <AzCore/Module/Environment.h>
#include <AzCore/std/parallel/mutex.h>
namespace AZ::Debug
{
//////////////////////////////////////////////////////////////////////////
// Globals
// We need to synchronize all driller evens, so we have proper order, and access to the data
// We use a global mutex which should be used for all driller operations.
// The mutex is held in an environment variable so it works across DLLs.
EnvironmentVariable<AZStd::recursive_mutex> s_drillerGlobalMutex;
//////////////////////////////////////////////////////////////////////////
//=========================================================================
// lock
// [4/11/2011]
//=========================================================================
void DrillerEBusMutex::lock()
{
GetMutex().lock();
}
//=========================================================================
// try_lock
// [4/11/2011]
//=========================================================================
bool DrillerEBusMutex::try_lock()
{
return GetMutex().try_lock();
}
//=========================================================================
// unlock
// [4/11/2011]
//=========================================================================
void DrillerEBusMutex::unlock()
{
GetMutex().unlock();
}
//=========================================================================
// unlock
// [4/11/2011]
//=========================================================================
AZStd::recursive_mutex& DrillerEBusMutex::GetMutex()
{
if (!s_drillerGlobalMutex)
{
s_drillerGlobalMutex = Environment::CreateVariable<AZStd::recursive_mutex>(AZ_FUNCTION_SIGNATURE);
}
return *s_drillerGlobalMutex;
}
} // namespace AZ::Debug
@@ -1,52 +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
*
*/
#ifndef AZCORE_DRILLER_BUS_H
#define AZCORE_DRILLER_BUS_H
#include <AzCore/base.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Memory/OSAllocator.h>
namespace AZStd
{
class mutex;
}
namespace AZ
{
namespace Debug
{
class DrillerEBusMutex
{
public:
typedef AZStd::recursive_mutex MutexType;
static MutexType& GetMutex();
void lock();
bool try_lock();
void unlock();
};
/**
* Specialization of the EBusTraits for a driller bus. We make sure
* all allocation are made using DebugAllocation (so no engine systems are involved).
* In addition we make sure all driller buses use the same Mutex to synchronize data across
* threads (so all events came in order all the time), they are still executed in the context of
* the thread.
*/
struct DrillerEBusTraits
: public AZ::EBusTraits
{
typedef DrillerEBusMutex MutexType;
typedef OSStdAllocator AllocatorType;
};
}
}
#endif // AZCORE_DRILLER_BUS_H
#pragma once
@@ -1,170 +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
*
*/
#ifndef AZCORE_DRILLER_ROOT_HANDLER_H
#define AZCORE_DRILLER_ROOT_HANDLER_H
#include <AzCore/Driller/Stream.h>
#include <AzCore/Math/Crc.h>
namespace AZ
{
namespace Debug
{
// Please check DrillerRootHandler class... this is the one for direct use.
/**
* Handler for the <Frame><StartData><Driller></Driller></StartData></Frame> tag.
*/
class DrillerDrillerdataHandler
: public DrillerHandlerParser
{
public:
class ParamHandler
: public DrillerHandlerParser
{
public:
virtual void OnData(const DrillerSAXParser::Data& dataNode)
{
if (dataNode.m_name == AZ_CRC("Name", 0x5e237e06))
{
dataNode.Read(m_param->name);
}
else if (dataNode.m_name == AZ_CRC("Description", 0x6de44026))
{
m_param->desc = NULL; // ignored
}
else if (dataNode.m_name == AZ_CRC("Type", 0x8cde5729))
{
dataNode.Read(m_param->type);
}
else if (dataNode.m_name == AZ_CRC("Value", 0x1d775834))
{
dataNode.Read(m_param->value);
}
}
Driller::Param* m_param;
};
virtual DrillerHandlerParser* OnEnterTag(u32 tagName)
{
if (tagName == AZ_CRC("Param", 0xa4fa7c89))
{
m_drillerInfo->params.push_back();
m_paramHandler.m_param = &m_drillerInfo->params.back();
return &m_paramHandler;
}
return NULL;
}
virtual void OnData(const DrillerSAXParser::Data& dataNode)
{
if (dataNode.m_name == AZ_CRC("Name", 0x5e237e06))
{
dataNode.Read(m_drillerInfo->id);
}
}
DrillerManager::DrillerInfo* m_drillerInfo;
ParamHandler m_paramHandler;
};
/**
* Handler for the <Frame><StartData></StartData></Frame> tag
*/
class DrillerStartdataHandler
: public DrillerHandlerParser
{
public:
virtual DrillerHandlerParser* OnEnterTag(u32 tagName)
{
if (tagName == AZ_CRC("Driller", 0xa6e1fb73))
{
m_drillers.push_back();
m_drillerDataHandler.m_drillerInfo = &m_drillers.back();
return &m_drillerDataHandler;
}
return NULL;
}
virtual void OnData(const DrillerSAXParser::Data& dataNode)
{
if (dataNode.m_name == AZ_CRC("Platform", 0x3952d0cb))
{
dataNode.Read(m_platform);
}
}
unsigned int m_platform;
DrillerManager::DrillerListType m_drillers;
DrillerDrillerdataHandler m_drillerDataHandler;
};
/**
* Handler for the <Frame></Frame> tag
*/
template<class DrillerContainer>
class FrameHandler
: public DrillerHandlerParser
{
public:
FrameHandler()
: DrillerHandlerParser(DrillerContainer::s_isWarnOnMissingDrillers)
, m_currentFrame(-1) {}
virtual DrillerHandlerParser* OnEnterTag(u32 tagName) { return m_drillersContainer.FindDrillerHandler(tagName); }
virtual void OnData(const DrillerSAXParser::Data& dataNode)
{
if (dataNode.m_name == AZ_CRC("FrameNum", 0x85a1a919))
{
dataNode.Read(m_currentFrame);
}
}
DrillerContainer m_drillersContainer;
int m_currentFrame;
};
/**
* Use this class a input parameter to DrillerSAXParserHandler::DrillerSAXParserHandler(). It will handle all root level
* tags for a standard driller input stream stream.
*
* DrillerContainer should comply to the following requirements:
* - default constructible
* - has a static const bool s_isWarnOnMissingDrillers member to indicate if you want to
* trigger a warning when a driller is not found in the class.
* - implement a function DrillerHandlerParser* DrillerContainer::FindDrillerHandler(u32 drillerName)
*
*/
template<class DrillerContainer>
class DrillerRootHandler
: public DrillerHandlerParser
{
public:
DrillerContainer* GetDrillerContainer() { return m_frameHandler.m_drillersContainer; }
virtual DrillerHandlerParser* OnEnterTag(u32 tagName)
{
if (tagName == AZ_CRC("StartData", 0xecf3f53f))
{
return &m_drillerSessionInfo;
}
if (tagName == AZ_CRC("Frame", 0xb5f83ccd))
{
return &m_frameHandler;
}
return NULL;
}
DrillerStartdataHandler m_drillerSessionInfo;
FrameHandler<DrillerContainer> m_frameHandler;
};
} // namespace Debug
} // namespace AZ
#endif // AZCORE_DRILLER_ROOT_HANDLER_H
#pragma once
@@ -1,893 +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
*
*/
#include <AzCore/Driller/Stream.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Obb.h>
#include <AzCore/Math/Plane.h>
#include <AzCore/std/time.h>
#if !defined(AZCORE_EXCLUDE_ZLIB)
# define AZ_FILE_STREAM_COMPRESSION
#endif // AZCORE_EXCLUDE_ZLIB
#if defined(AZ_FILE_STREAM_COMPRESSION)
# include <AzCore/Compression/Compression.h>
#endif // AZ_FILE_STREAM_COMPRESSION
namespace AZ::Debug
{
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Driller output stream
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void DrillerOutputStream::Write(u32 name, const AZ::Vector3& v)
{
float data[4];
unsigned int dataSize = 3 * sizeof(float);
v.StoreToFloat4(data);
StreamEntry de;
de.name = name;
de.sizeAndFlags = dataSize;
WriteBinary(de);
WriteBinary(data, dataSize);
}
void DrillerOutputStream::Write(u32 name, const AZ::Vector4& v)
{
float data[4];
unsigned int dataSize = 4 * sizeof(float);
v.StoreToFloat4(data);
StreamEntry de;
de.name = name;
de.sizeAndFlags = dataSize;
WriteBinary(de);
WriteBinary(data, dataSize);
}
void DrillerOutputStream::Write(u32 name, const AZ::Aabb& aabb)
{
float data[7];
unsigned int dataSize = 6 * sizeof(float);
aabb.GetMin().StoreToFloat4(data);
aabb.GetMax().StoreToFloat4(&data[3]);
StreamEntry de;
de.name = name;
de.sizeAndFlags = dataSize;
WriteBinary(de);
WriteBinary(data, dataSize);
}
void DrillerOutputStream::Write(u32 name, const AZ::Obb& obb)
{
float data[10];
unsigned int dataSize = 10 * sizeof(float); // position (Vector3), rotation (Quaternion) and halfLengths (Vector3)
obb.GetPosition().StoreToFloat3(data);
obb.GetRotation().StoreToFloat4(&data[3]);
obb.GetHalfLengths().StoreToFloat3(&data[7]);
StreamEntry de;
de.name = name;
de.sizeAndFlags = dataSize;
WriteBinary(de);
WriteBinary(data, dataSize);
}
void DrillerOutputStream::Write(u32 name, const AZ::Transform& tm)
{
float data[12];
unsigned int dataSize = 12 * sizeof(float);
const Matrix3x4 matrix3x4 = Matrix3x4::CreateFromTransform(tm);
matrix3x4.StoreToRowMajorFloat12(data);
StreamEntry de;
de.name = name;
de.sizeAndFlags = dataSize;
WriteBinary(de);
WriteBinary(data, dataSize);
}
void DrillerOutputStream::Write(u32 name, const AZ::Matrix3x3& tm)
{
float data[9];
unsigned int dataSize = 9 * sizeof(float);
tm.StoreToRowMajorFloat9(data);
StreamEntry de;
de.name = name;
de.sizeAndFlags = dataSize;
WriteBinary(de);
WriteBinary(data, dataSize);
}
void DrillerOutputStream::Write(u32 name, const AZ::Matrix4x4& tm)
{
float data[16];
unsigned int dataSize = 16 * sizeof(float);
tm.StoreToRowMajorFloat16(data);
StreamEntry de;
de.name = name;
de.sizeAndFlags = dataSize;
WriteBinary(de);
WriteBinary(data, dataSize);
}
void DrillerOutputStream::Write(u32 name, const AZ::Quaternion& tm)
{
float data[4];
unsigned int dataSize = 4 * sizeof(float);
tm.StoreToFloat4(data);
StreamEntry de;
de.name = name;
de.sizeAndFlags = dataSize;
WriteBinary(de);
WriteBinary(data, dataSize);
}
void DrillerOutputStream::Write(u32 name, const AZ::Plane& plane)
{
Write(name, plane.GetPlaneEquationCoefficients());
}
void DrillerOutputStream::WriteHeader()
{
StreamHeader sh; // StreamHeader should be endianess independent.
WriteBinary(&sh, sizeof(sh));
}
void DrillerOutputStream::WriteTimeUTC(u32 name)
{
AZStd::sys_time_t now = AZStd::GetTimeUTCMilliSecond();
Write(name, now);
}
void DrillerOutputStream::WriteTimeMicrosecond(u32 name)
{
AZStd::sys_time_t now = AZStd::GetTimeNowMicroSecond();
Write(name, now);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Driller Input Stream
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
bool DrillerInputStream::ReadHeader()
{
DrillerOutputStream::StreamHeader sh; // StreamHeader should be endianess independent.
unsigned int numRead = ReadBinary(&sh, sizeof(sh));
(void)numRead;
AZ_Error("IO", numRead == sizeof(sh), "We should have atleast %d bytes in the stream to read the header!", sizeof(sh));
if (numRead != sizeof(sh))
{
return false;
}
m_isEndianSwap = AZ::IsBigEndian(static_cast<AZ::PlatformID>(sh.platform)) != AZ::IsBigEndian(AZ::g_currentPlatform);
return true;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Driller file stream
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//=========================================================================
// DrillerOutputFileStream::DrillerOutputFileStream
// [3/23/2011]
//=========================================================================
DrillerOutputFileStream::DrillerOutputFileStream()
{
#if defined(AZ_FILE_STREAM_COMPRESSION)
m_zlib = azcreate(ZLib, (&AllocatorInstance<OSAllocator>::GetAllocator()), OSAllocator);
m_zlib->StartCompressor(2);
#endif
}
//=========================================================================
// DrillerOutputFileStream::~DrillerOutputFileStream
// [3/23/2011]
//=========================================================================
DrillerOutputFileStream::~DrillerOutputFileStream()
{
#if defined(AZ_FILE_STREAM_COMPRESSION)
azdestroy(m_zlib, OSAllocator);
#endif
}
//=========================================================================
// DrillerOutputFileStream::Open
// [3/23/2011]
//=========================================================================
bool DrillerOutputFileStream::Open(const char* fileName, int mode, int platformFlags)
{
if (IO::SystemFile::Open(fileName, mode, platformFlags))
{
m_dataBuffer.reserve(100 * 1024);
#if defined(AZ_FILE_STREAM_COMPRESSION)
// // Enable optional: encode the file in the same format as the streamer so they are interchangeable
// IO::CompressorHeader ch;
// ch.SetAZCS();
// ch.m_compressorId = IO::CompressorZLib::TypeId();
// ch.m_uncompressedSize = 0; // will be updated later
// AZStd::endian_swap(ch.m_compressorId);
// AZStd::endian_swap(ch.m_uncompressedSize);
// IO::SystemFile::Write(&ch,sizeof(ch));
// IO::CompressorZLibHeader zlibHdr;
// zlibHdr.m_numSeekPoints = 0;
// IO::SystemFile::Write(&zlibHdr,sizeof(zlibHdr));
#endif
return true;
}
return false;
}
//=========================================================================
// DrillerOutputFileStream::Close
// [3/23/2011]
//=========================================================================
void DrillerOutputFileStream::Close()
{
unsigned int dataSizeInBuffer = static_cast<unsigned int>(m_dataBuffer.size());
{
#if defined(AZ_FILE_STREAM_COMPRESSION)
unsigned int minCompressBufferSize = m_zlib->GetMinCompressedBufferSize(dataSizeInBuffer);
if (m_compressionBuffer.size() < minCompressBufferSize) // grow compression buffer if needed
{
m_compressionBuffer.clear();
m_compressionBuffer.resize(minCompressBufferSize);
}
unsigned int compressedSize;
do
{
compressedSize = m_zlib->Compress(m_dataBuffer.data(), dataSizeInBuffer, m_compressionBuffer.data(), (unsigned)m_compressionBuffer.size(), ZLib::FT_FINISH);
if (compressedSize)
{
IO::SystemFile::Write(m_compressionBuffer.data(), compressedSize);
}
} while (compressedSize > 0);
m_zlib->ResetCompressor();
#else
if (dataSizeInBuffer)
{
IO::SystemFile::Write(m_dataBuffer.data(), m_dataBuffer.size());
}
#endif
m_dataBuffer.clear();
}
IO::SystemFile::Close();
}
//=========================================================================
// DrillerOutputFileStream::WriteBinary
// [3/23/2011]
//=========================================================================
void DrillerOutputFileStream::WriteBinary(const void* data, unsigned int dataSize)
{
size_t dataSizeInBuffer = m_dataBuffer.size();
if (dataSizeInBuffer + dataSize > m_dataBuffer.capacity())
{
if (dataSizeInBuffer > 0)
{
#if defined(AZ_FILE_STREAM_COMPRESSION)
// we need to flush the data
unsigned int dataToCompress = static_cast<unsigned int>(dataSizeInBuffer);
unsigned int minCompressBufferSize = m_zlib->GetMinCompressedBufferSize(dataToCompress);
if (m_compressionBuffer.size() < minCompressBufferSize) // grow compression buffer if needed
{
m_compressionBuffer.clear();
m_compressionBuffer.resize(minCompressBufferSize);
}
while (dataToCompress > 0)
{
unsigned int compressedSize = m_zlib->Compress(m_dataBuffer.data(), dataToCompress, m_compressionBuffer.data(), (unsigned)m_compressionBuffer.size());
if (compressedSize)
{
IO::SystemFile::Write(m_compressionBuffer.data(), compressedSize);
}
}
#else
IO::SystemFile::Write(m_dataBuffer.data(), m_dataBuffer.size());
#endif
m_dataBuffer.clear();
}
}
m_dataBuffer.insert(m_dataBuffer.end(), reinterpret_cast<const unsigned char*>(data), reinterpret_cast<const unsigned char*>(data) + dataSize);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Driller file input stream
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//=========================================================================
// DrillerInputFileStream::DrillerInputFileStream
// [3/23/2011]
//=========================================================================
DrillerInputFileStream::DrillerInputFileStream()
{
#if defined(AZ_FILE_STREAM_COMPRESSION)
m_zlib = azcreate(ZLib, (&AllocatorInstance<OSAllocator>::GetAllocator()), OSAllocator);
m_zlib->StartDecompressor();
#endif
}
//=========================================================================
// DrillerInputFileStream::DrillerInputFileStream
// [3/23/2011]
//=========================================================================
DrillerInputFileStream::~DrillerInputFileStream()
{
#if defined(AZ_FILE_STREAM_COMPRESSION)
azdestroy(m_zlib, OSAllocator);
#endif
}
//=========================================================================
// DrillerInputFileStream::Open
// [3/23/2011]
//=========================================================================
bool DrillerInputFileStream::Open(const char* fileName, int mode, int platformFlags)
{
if (IO::SystemFile::Open(fileName, mode, platformFlags))
{
DrillerOutputStream::StreamHeader sh;
#if defined(AZ_FILE_STREAM_COMPRESSION)
// TODO: optional encode the file in the same format as the streamer so they are interchangeable
#endif
// first read the header of the stream file.
return ReadHeader();
}
return false;
}
//=========================================================================
// DrillerInputFileStream::ReadBinary
// [3/23/2011]
//=========================================================================
unsigned int DrillerInputFileStream::ReadBinary(void* data, unsigned int maxDataSize)
{
// make sure the compressed buffer if full enough...
size_t dataToLoad = maxDataSize * 2;
m_compressedData.reserve(dataToLoad);
while (m_compressedData.size() < dataToLoad)
{
unsigned char buffer[10 * 1024];
IO::SystemFile::SizeType bytesRead = Read(AZ_ARRAY_SIZE(buffer), buffer);
if (bytesRead > 0)
{
m_compressedData.insert(m_compressedData.end(), (unsigned char*)buffer, buffer + bytesRead);
}
if (bytesRead < AZ_ARRAY_SIZE(buffer))
{
break;
}
}
#if defined(AZ_FILE_STREAM_COMPRESSION)
unsigned int dataSize = maxDataSize;
unsigned int bytesProcessed = m_zlib->Decompress(m_compressedData.data(), (unsigned)m_compressedData.size(), data, dataSize);
unsigned int readSize = maxDataSize - dataSize; // Zlib::Decompress decrements the dataSize parameter by the amount uncompressed
#else
unsigned int bytesProcessed = AZStd::GetMin((unsigned int)m_compressedData.size(), maxDataSize);
unsigned int readSize = bytesProcessed;
memcpy(data, m_compressedData.data(), readSize);
#endif
m_compressedData.erase(m_compressedData.begin(), m_compressedData.begin() + bytesProcessed);
return readSize;
}
//=========================================================================
// DrillerInputFileStream::Close
// [3/23/2011]
//=========================================================================
void DrillerInputFileStream::Close()
{
#if defined(AZ_FILE_STREAM_COMPRESSION)
if (m_zlib)
{
m_zlib->ResetDecompressor();
}
#endif // AZ_FILE_STREAM_COMPRESSION
AZ::IO::SystemFile::Close();
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// DrillerSAXParser
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//=========================================================================
// DrillerSAXParser
// [3/23/2011]
//=========================================================================
DrillerSAXParser::DrillerSAXParser(const TagCallbackType& tcb, const DataCallbackType& dcb)
: m_tagCallback(tcb)
, m_dataCallback(dcb)
{
}
//=========================================================================
// ProcessStream
// [3/23/2011]
//=========================================================================
void
DrillerSAXParser::ProcessStream(DrillerInputStream& stream)
{
static const int processChunkSize = 15 * 1024;
char buffer[processChunkSize];
unsigned int dataSize;
bool isEndianSwap = stream.IsEndianSwap();
while ((dataSize = stream.ReadBinary(buffer, processChunkSize)) > 0)
{
char* dataStart = buffer;
char* dataEnd = dataStart + dataSize;
bool dataInBuffer = false;
if (!m_buffer.empty())
{
m_buffer.insert(m_buffer.end(), dataStart, dataEnd);
dataStart = m_buffer.data();
dataEnd = dataStart + m_buffer.size();
dataInBuffer = true;
}
const int entrySize = sizeof(DrillerOutputStream::StreamEntry);
while (dataStart != dataEnd)
{
if ((dataEnd - dataStart) < entrySize) // we need at least one entry to proceed
{
// not enough data to process, buffer it.
if (!dataInBuffer)
{
m_buffer.insert(m_buffer.end(), dataStart, dataEnd);
}
break;
}
DrillerOutputStream::StreamEntry* se = reinterpret_cast<DrillerOutputStream::StreamEntry*>(dataStart);
if (isEndianSwap)
{
// endian swap
AZStd::endian_swap(se->name);
AZStd::endian_swap(se->sizeAndFlags);
}
u32 dataType = (se->sizeAndFlags & DrillerOutputStream::StreamEntry::dataInternalMask) >> DrillerOutputStream::StreamEntry::dataInternalShift;
u32 value = se->sizeAndFlags & DrillerOutputStream::StreamEntry::dataSizeMask;
Data de;
de.m_name = se->name;
de.m_stringPool = stream.GetStringPool();
de.m_isPooledString = false;
de.m_isPooledStringCrc32 = false;
switch (dataType)
{
case DrillerOutputStream::StreamEntry::INT_TAG:
{
bool isStart = (value != 0);
m_tagCallback(se->name, isStart);
dataStart += entrySize;
} break;
case DrillerOutputStream::StreamEntry::INT_DATA_U8:
{
u8 value8 = static_cast<u8>(value);
de.m_data = &value8;
de.m_dataSize = 1;
de.m_isEndianSwap = false;
m_dataCallback(de);
dataStart += entrySize;
} break;
case DrillerOutputStream::StreamEntry::INT_DATA_U16:
{
u16 value16 = static_cast<u16>(value);
de.m_data = &value16;
de.m_dataSize = 2;
de.m_isEndianSwap = false;
m_dataCallback(de);
dataStart += entrySize;
} break;
case DrillerOutputStream::StreamEntry::INT_DATA_U29:
{
de.m_data = &value;
de.m_dataSize = 4;
de.m_isEndianSwap = false;
m_dataCallback(de);
dataStart += entrySize;
} break;
case DrillerOutputStream::StreamEntry::INT_POOLED_STRING:
{
unsigned int userDataSize = value;
if ((userDataSize + entrySize) <= (unsigned)(dataEnd - dataStart))
{
// Add string to the pool
AZ_Assert(de.m_stringPool != nullptr, "We require a string pool to parse this stream");
AZ::u32 crc32;
const char* stringPtr;
dataStart += entrySize;
de.m_stringPool->InsertCopy(reinterpret_cast<const char*>(dataStart), userDataSize, crc32, &stringPtr);
de.m_dataSize = userDataSize;
de.m_isEndianSwap = isEndianSwap;
de.m_isPooledString = true;
de.m_data = const_cast<void*>(static_cast<const void*>(stringPtr));
m_dataCallback(de);
dataStart += userDataSize;
}
else
{
// we can't process data right now add it to the buffer (if we have not done that already)
if (!dataInBuffer)
{
m_buffer.insert(m_buffer.end(), dataStart, dataEnd);
}
dataEnd = dataStart; // exit the loop
}
} break;
case DrillerOutputStream::StreamEntry::INT_POOLED_STRING_CRC32:
{
de.m_isPooledStringCrc32 = true;
AZ_Assert(value == 4, "The data size for a pooled string crc32 should be 4 bytes!");
} // continue to INT_SIZE
case DrillerOutputStream::StreamEntry::INT_SIZE:
{
unsigned int userDataSize = value;
if ((userDataSize + entrySize) <= (unsigned)(dataEnd - dataStart)) // do we have all the date we need to process...
{
dataStart += entrySize;
de.m_data = dataStart;
de.m_dataSize = userDataSize;
de.m_isEndianSwap = isEndianSwap;
m_dataCallback(de);
dataStart += userDataSize;
}
else
{
// we can't process data right now add it to the buffer (if we have not done that already)
if (!dataInBuffer)
{
m_buffer.insert(m_buffer.end(), dataStart, dataEnd);
}
dataEnd = dataStart; // exit the loop
}
} break;
default:
{
AZ_Error("DrillerSAXParser",false,"Encounted unknown symbol (%i) while processing stream (%s). Aborting stream.\n",dataType, stream.GetIdentifier());
// If we can't process anything, we want to just escape the loop, to avoid spinning infinitely
dataEnd = dataStart;
} break;
}
}
if (dataInBuffer) // if the data was in the buffer remove the processed data!
{
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + (dataStart - m_buffer.data()));
}
}
}
void DrillerSAXParser::Data::Read(AZ::Vector3& v) const
{
AZ_Assert(m_dataSize == sizeof(float) * 3, "We are expecting 3 floats for Vector3 element 0x%08x with size %d bytes", m_name, m_dataSize);
float* data = reinterpret_cast<float*>(m_data);
if (m_isEndianSwap)
{
AZStd::endian_swap(data, data + 3);
m_isEndianSwap = false;
}
v = Vector3::CreateFromFloat3(data);
}
void DrillerSAXParser::Data::Read(AZ::Vector4& v) const
{
AZ_Assert(m_dataSize == sizeof(float) * 4, "We are expecting 4 floats for Vector4 element 0x%08x with size %d bytes", m_name, m_dataSize);
float* data = reinterpret_cast<float*>(m_data);
if (m_isEndianSwap)
{
AZStd::endian_swap(data, data + 4);
m_isEndianSwap = false;
}
v = Vector4::CreateFromFloat4(data);
}
void DrillerSAXParser::Data::Read(AZ::Aabb& aabb) const
{
AZ_Assert(m_dataSize == sizeof(float) * 6, "We are expecting 6 floats for Aabb element 0x%08x with size %d bytes", m_name, m_dataSize);
float* data = reinterpret_cast<float*>(m_data);
if (m_isEndianSwap)
{
AZStd::endian_swap(data, data + 6);
m_isEndianSwap = false;
}
Vector3 min = Vector3::CreateFromFloat3(data);
Vector3 max = Vector3::CreateFromFloat3(&data[3]);
aabb = Aabb::CreateFromMinMax(min, max);
}
void DrillerSAXParser::Data::Read(AZ::Obb& obb) const
{
AZ_Assert(m_dataSize == sizeof(float) * 10, "We are expecting 10 floats for Obb element 0x%08x with size %d bytes", m_name, m_dataSize);
float* data = reinterpret_cast<float*>(m_data);
if (m_isEndianSwap)
{
AZStd::endian_swap(data, data + 10);
m_isEndianSwap = false;
}
Vector3 position = Vector3::CreateFromFloat3(data);
Quaternion rotation = Quaternion::CreateFromFloat4(&data[3]);
Vector3 halfLengths = Vector3::CreateFromFloat3(&data[7]);
obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
}
void DrillerSAXParser::Data::Read(AZ::Transform& tm) const
{
AZ_Assert(m_dataSize == sizeof(float) * 12, "We are expecting 12 floats for Transform element 0x%08x with size %d bytes", m_name, m_dataSize);
float* data = reinterpret_cast<float*>(m_data);
if (m_isEndianSwap)
{
AZStd::endian_swap(data, data + 12);
m_isEndianSwap = false;
}
const Matrix3x4 matrix3x4 = Matrix3x4::CreateFromRowMajorFloat12(data);
tm = Transform::CreateFromMatrix3x4(matrix3x4);
}
void DrillerSAXParser::Data::Read(AZ::Matrix3x3& tm) const
{
AZ_Assert(m_dataSize == sizeof(float) * 9, "We are expecting 9 floats for Matrix3x3 element 0x%08x with size %d bytes", m_name, m_dataSize);
float* data = reinterpret_cast<float*>(m_data);
if (m_isEndianSwap)
{
AZStd::endian_swap(data, data + 9);
m_isEndianSwap = false;
}
tm = Matrix3x3::CreateFromRowMajorFloat9(data);
}
void DrillerSAXParser::Data::Read(AZ::Matrix4x4& tm) const
{
AZ_Assert(m_dataSize == sizeof(float) * 16, "We are expecting 16 floats for Matrix4x4 element 0x%08x with size %d bytes", m_name, m_dataSize);
float* data = reinterpret_cast<float*>(m_data);
if (m_isEndianSwap)
{
AZStd::endian_swap(data, data + 16);
m_isEndianSwap = false;
}
tm = Matrix4x4::CreateFromRowMajorFloat16(data);
}
void DrillerSAXParser::Data::Read(AZ::Quaternion& tm) const
{
AZ_Assert(m_dataSize == sizeof(float) * 4, "We are expecting 4 floats for Quaternion element 0x%08x with size %d bytes", m_name, m_dataSize);
float* data = reinterpret_cast<float*>(m_data);
if (m_isEndianSwap)
{
AZStd::endian_swap(data, data + 4);
m_isEndianSwap = false;
}
tm = Quaternion::CreateFromFloat4(data);
}
void DrillerSAXParser::Data::Read(AZ::Plane& plane) const
{
AZ::Vector4 coeff;
Read(coeff);
plane = Plane::CreateFromCoefficients(coeff.GetX(), coeff.GetY(), coeff.GetZ(), coeff.GetW());
}
const char* DrillerSAXParser::Data::PrepareString(unsigned int& stringLength) const
{
const char* srcData = reinterpret_cast<const char*>(m_data);
stringLength = m_dataSize;
if (m_stringPool)
{
AZ::u32 crc32;
const char* stringPtr;
if (m_isPooledStringCrc32)
{
crc32 = *reinterpret_cast<AZ::u32*>(m_data);
if (m_isEndianSwap)
{
AZStd::endian_swap(crc32);
}
stringPtr = m_stringPool->Find(crc32);
AZ_Assert(stringPtr != nullptr, "Failed to find string with id 0x%08x in the string pool, proper stream read is impossible!", crc32);
stringLength = static_cast<unsigned int>(strlen(stringPtr));
}
else if (m_isPooledString)
{
stringPtr = srcData; // already stored in the pool just transfer the pointer
}
else
{
// Store copy of the string in the pool to save memory (keep only one reference of the string).
m_stringPool->InsertCopy(reinterpret_cast<const char*>(srcData), stringLength, crc32, &stringPtr);
}
srcData = stringPtr;
}
else
{
AZ_Assert(m_isPooledString == false && m_isPooledStringCrc32 == false, "This stream requires using of a string pool as the string is send only once and afterwards only the Crc32 is used!");
}
return srcData;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// DrillerDOMParser
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//=========================================================================
// Node::GetTag
// [1/23/2013]
//=========================================================================
const DrillerDOMParser::Node* DrillerDOMParser::Node::GetTag(u32 tagName) const
{
const Node* tagNode = nullptr;
for (Node::NodeListType::const_iterator i = m_tags.begin(); i != m_tags.end(); ++i)
{
if ((*i).m_name == tagName)
{
tagNode = &*i;
break;
}
}
return tagNode;
}
//=========================================================================
// Node::GetData
// [3/23/2011]
//=========================================================================
const DrillerDOMParser::Data* DrillerDOMParser::Node::GetData(u32 dataName) const
{
const Data* dataNode = nullptr;
for (Node::DataListType::const_iterator i = m_data.begin(); i != m_data.end(); ++i)
{
if (i->m_name == dataName)
{
dataNode = &*i;
break;
}
}
return dataNode;
}
//=========================================================================
// DrillerDOMParser
// [3/23/2011]
//=========================================================================
DrillerDOMParser::DrillerDOMParser(bool isPersistentInputData)
: DrillerSAXParser(TagCallbackType(this, &DrillerDOMParser::OnTag), DataCallbackType(this, &DrillerDOMParser::OnData))
, m_isPersistentInputData(isPersistentInputData)
{
m_root.m_name = 0;
m_root.m_parent = nullptr;
m_topNode = &m_root;
}
static int g_numFree = 0;
//=========================================================================
// ~DrillerDOMParser
// [3/23/2011]
//=========================================================================
DrillerDOMParser::~DrillerDOMParser()
{
DeleteNode(m_root);
}
//=========================================================================
// OnTag
// [3/23/2011]
//=========================================================================
void
DrillerDOMParser::OnTag(AZ::u32 name, bool isOpen)
{
if (isOpen)
{
m_topNode->m_tags.push_back();
Node& node = m_topNode->m_tags.back();
node.m_name = name;
node.m_parent = m_topNode;
m_topNode = &node;
}
else
{
AZ_Assert(m_topNode->m_name == name, "We have opened tag with name 0x%08x and closing with name 0x%08x", m_topNode->m_name, name);
m_topNode = m_topNode->m_parent;
}
}
//=========================================================================
// OnData
// [3/23/2011]
//=========================================================================
void
DrillerDOMParser::OnData(const Data& data)
{
Data de = data;
if (!m_isPersistentInputData)
{
de.m_data = azmalloc(data.m_dataSize, 1, OSAllocator);
memcpy(const_cast<void*>(de.m_data), data.m_data, data.m_dataSize);
}
m_topNode->m_data.push_back(de);
}
//=========================================================================
// DeleteNode
// [3/23/2011]
//=========================================================================
void
DrillerDOMParser::DeleteNode(Node& node)
{
if (!m_isPersistentInputData)
{
for (Node::DataListType::iterator iter = node.m_data.begin(); iter != node.m_data.end(); ++iter)
{
azfree(iter->m_data, OSAllocator, iter->m_dataSize);
++g_numFree;
}
node.m_data.clear();
}
for (Node::NodeListType::iterator iter = node.m_tags.begin(); iter != node.m_tags.end(); ++iter)
{
DeleteNode(*iter);
}
node.m_tags.clear();
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// DrillerSAXParserHandler
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//=========================================================================
// DrillerSAXParserHandler
// [3/14/2013]
//=========================================================================
DrillerSAXParserHandler::DrillerSAXParserHandler(DrillerHandlerParser* rootHandler)
: DrillerSAXParser(TagCallbackType(this, &DrillerSAXParserHandler::OnTag), DataCallbackType(this, &DrillerSAXParserHandler::OnData))
{
// Push the root element
m_stack.push_back(rootHandler);
}
//=========================================================================
// OnTag
// [3/14/2013]
//=========================================================================
void DrillerSAXParserHandler::OnTag(u32 name, bool isOpen)
{
if (m_stack.empty())
{
return;
}
DrillerHandlerParser* childHandler = nullptr;
DrillerHandlerParser* currentHandler = m_stack.back();
if (isOpen)
{
if (currentHandler != nullptr)
{
childHandler = currentHandler->OnEnterTag(name);
AZ_Warning("Driller", !currentHandler->IsWarnOnUnsupportedTags() || childHandler != nullptr, "Could not find handler for tag 0x%08x", name);
}
m_stack.push_back(childHandler);
}
else
{
m_stack.pop_back();
if (!m_stack.empty())
{
DrillerHandlerParser* parentHandler = m_stack.back();
if (parentHandler)
{
parentHandler->OnExitTag(currentHandler, name);
}
}
}
}
//=========================================================================
// OnData
// [3/14/2013]
//=========================================================================
void DrillerSAXParserHandler::OnData(const DrillerSAXParser::Data& data)
{
if (m_stack.empty())
{
return;
}
DrillerHandlerParser* currentHandler = m_stack.back();
if (currentHandler)
{
currentHandler->OnData(data);
}
}
} // namespace AZ::Debug
@@ -1,848 +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
*
*/
#ifndef AZCORE_DRILLER_STREAM_H
#define AZCORE_DRILLER_STREAM_H
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/std/delegate/delegate.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/forward_list.h>
#include <AzCore/std/typetraits/is_signed.h>
#include <AzCore/std/typetraits/is_pod.h>
#include <AzCore/IO/SystemFile.h> // for the Driller direct file stream
#include <AzCore/PlatformId/PlatformId.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
class Vector3;
class Vector4;
class Aabb;
class Obb;
class Transform;
class Matrix3x3;
class Matrix4x4;
class Quaternion;
class Plane;
class ZLib;
namespace IO
{
class Stream;
}
namespace Debug
{
template<class T>
struct vector
{
typedef AZStd::vector<T, OSStdAllocator> type;
};
template<class T>
struct forward_list
{
typedef AZStd::forward_list<T, OSStdAllocator> type;
};
/**
* Interface for a string pool which can be used by input/output streams to avoid storing multiple copies of the same
* string in the stream. Of course this comes at the bookkeeping cost of the table.
*/
class DrillerStringPool
{
public:
virtual ~DrillerStringPool() {}
/**
* Add a copy of the string to the pool. If we return true the string was added otherwise it was already in the bool.
* In both cases the crc32 of the string and the pointer to the shared copy is returned (optional for poolStringAddress)!
*/
virtual bool InsertCopy(const char* string, unsigned int length, AZ::u32& crc32, const char** poolStringAddress = NULL) = 0;
/**
* Same as the InsertCopy above without actually coping the string into the pool. The pool assumes that
* none of the strings added to the pool will be deleted.
*/
virtual bool Insert(const char* string, unsigned int length, AZ::u32& crc32) = 0;
/// Finds a string in the pool by crc32.
virtual const char* Find(AZ::u32 crc32) = 0;
virtual void Erase(AZ::u32 crc32) = 0;
/// Clears all the strings in the pool, make sure you don't reference any strings before you call that function.
virtual void Reset() = 0;
};
/**
*
*/
class DrillerOutputStream
{
protected:
friend class DrillerManagerImpl;
friend class DrillerSAXParser;
struct StreamEntry
{
enum InternalDataSize // max 8 values as we use 3 bit to store them
{
INT_SIZE = 0, ///< No internal data, we store the data size. IMPORTANT: INT_SIZE should be 0 the code makes assumptions based on that
INT_TAG, ///< True if this entry is tag
INT_DATA_U8, ///< Internal data u8 stored (1 byte)
INT_DATA_U16, ///< Internal data u16 stored (2 bytes)
INT_DATA_U29, ///< Internal data u32 stored (4 bytes) for which we use only the first 29 bits.
INT_POOLED_STRING_CRC32, ///< Data size should be 4 bytes crc32 that a string CRC and it require string pool.
INT_POOLED_STRING, ///< This data contains a string which should be inserted in the string pool.
};
static const u32 dataSizeMask = 0x1fffffff;
static const u32 dataInternalMask = 0xE0000000;
static const u32 dataInternalShift = 29;
u32 name; ///< data or tag name
u32 sizeAndFlags; ///<
};
template<class T, size_t Size, bool isIntegralType>
struct IntergralType;
template<class T>
struct IntergralType<T, 1, true>
{
static void Write(DrillerOutputStream& stream, u32 name, const T& data)
{
StreamEntry de;
de.name = name;
de.sizeAndFlags = (u32)(StreamEntry::INT_DATA_U8) << StreamEntry::dataInternalShift;
de.sizeAndFlags |= *reinterpret_cast<const u8*>(&data);
stream.WriteBinary(de);
}
};
template<class T>
struct IntergralType<T, 2, true>
{
static void Write(DrillerOutputStream& stream, u32 name, const T& data)
{
StreamEntry de;
de.name = name;
de.sizeAndFlags = (u32)(StreamEntry::INT_DATA_U16) << StreamEntry::dataInternalShift;
de.sizeAndFlags |= *reinterpret_cast<const u16*>(&data);
stream.WriteBinary(de);
}
};
template<class T>
struct IntergralType<T, 4, true>
{
static void Write(DrillerOutputStream& stream, u32 name, const T& data)
{
StreamEntry de;
de.name = name;
const u32* uintData = reinterpret_cast<const u32*>(&data);
if (((*uintData) & StreamEntry::dataSizeMask) == *uintData) // check if we can store it internally
{
de.sizeAndFlags = (u32)(StreamEntry::INT_DATA_U29) << StreamEntry::dataInternalShift;
de.sizeAndFlags |= *uintData;
stream.WriteBinary(de);
}
else
{
de.sizeAndFlags = 4;
stream.WriteBinary(de);
stream.WriteBinary(&data, de.sizeAndFlags);
}
}
};
template<class T>
struct IntergralType<T, 8, true>
{
static void Write(DrillerOutputStream& stream, u32 name, const T& data)
{
StreamEntry de;
de.name = name;
const u64* uintData = reinterpret_cast<const u64*>(&data);
if (((*uintData) & static_cast<u64>(StreamEntry::dataSizeMask)) == *uintData) // check if we can store it internally
{
de.sizeAndFlags = (u32)(StreamEntry::INT_DATA_U29) << StreamEntry::dataInternalShift;
de.sizeAndFlags |= static_cast<u32>(*uintData);
stream.WriteBinary(de);
}
else
{
de.sizeAndFlags = 8;
stream.WriteBinary(de);
stream.WriteBinary(&data, de.sizeAndFlags);
}
}
};
template<class T>
struct IntergralType<T*, sizeof(void*), false>
{
static void Write(DrillerOutputStream& stream, u32 name, const T* pointer)
{
size_t id = reinterpret_cast<size_t>(pointer);
IntergralType<size_t, sizeof(id), true>::Write(stream, name, id);
}
};
public:
/**
* Each stream with start with this header, before anything else.
*/
struct StreamHeader
{
StreamHeader()
: platform((u8)g_currentPlatform) {}
u8 platform;
};
DrillerOutputStream(DrillerStringPool* stringPool = NULL)
: m_stringPool(stringPool) { }
virtual ~DrillerOutputStream() {}
//////////////////////////////////////////////////////////////////////////
// Write
inline void BeginTag(u32 name)
{
StreamEntry de;
de.name = name;
de.sizeAndFlags = (u32)(StreamEntry::INT_TAG) << StreamEntry::dataInternalShift;
de.sizeAndFlags |= 1; // true - open tag
WriteBinary(de);
}
inline void EndTag(u32 name)
{
StreamEntry de;
de.name = name;
de.sizeAndFlags = (u32)(StreamEntry::INT_TAG) << StreamEntry::dataInternalShift;
WriteBinary(de);
}
//////////////////////////////////////////////////////////////////////////
// Generic
template<class T>
inline void Write(u32 name, const T& data)
{
// User should handle non specialized non integral types.
IntergralType<T, sizeof(T), AZStd::is_integral<T>::value || AZStd::is_enum<T>::value>::Write(*this, name, data);
}
//////////////////////////////////////////////////////////////////////////
// Binary and strings
inline void Write(u32 name, const void* data, unsigned int dataSize)
{
StreamEntry de;
de.name = name;
de.sizeAndFlags = dataSize;
WriteBinary(de);
WriteBinary(data, dataSize);
}
inline void Write(u32 name, const char* string, bool isCopyString = true)
{
StreamEntry de;
de.name = name;
de.sizeAndFlags = static_cast<unsigned int>(strlen(string));
AZ_Assert(de.sizeAndFlags <= StreamEntry::dataSizeMask, "Invalid string length! String is too long, length is limited to %u bytes!", StreamEntry::dataSizeMask);
;
if (m_stringPool)
{
AZ::u32 crc;
bool isInserted = isCopyString ? m_stringPool->InsertCopy(string, de.sizeAndFlags, crc) : m_stringPool->Insert(string, de.sizeAndFlags, crc);
if (!isInserted) // if already inserted, it means it's in the stream, so store the crc only.
{
de.sizeAndFlags = (u32)(StreamEntry::INT_POOLED_STRING_CRC32) << StreamEntry::dataInternalShift;
de.sizeAndFlags |= sizeof(crc);
WriteBinary(de);
WriteBinary(&crc, sizeof(crc));
}
else
{
AZ::u32 stringSize = de.sizeAndFlags;
de.sizeAndFlags |= (u32)(StreamEntry::INT_POOLED_STRING) << StreamEntry::dataInternalShift;
WriteBinary(de);
WriteBinary(string, stringSize);
}
}
else
{
WriteBinary(de);
WriteBinary(string, de.sizeAndFlags);
}
}
template<class Allocator>
inline void Write(u32 name, const AZStd::basic_string<AZStd::string::value_type, AZStd::string::traits_type, Allocator>& str, bool isCopyString = true)
{
StreamEntry de;
de.name = name;
de.sizeAndFlags = static_cast<AZ::u32>(str.size());
AZ_Assert(de.sizeAndFlags <= StreamEntry::dataSizeMask, "Invalid string length! String is too long, length is limited to %u bytes!", StreamEntry::dataSizeMask);
if (m_stringPool)
{
AZ::u32 crc;
bool isInserted = isCopyString ? m_stringPool->InsertCopy(str.c_str(), de.sizeAndFlags, crc) : m_stringPool->Insert(str.c_str(), de.sizeAndFlags, crc);
if (!isInserted) // if already inserted, it means it's in the stream, so store the crc only.
{
de.sizeAndFlags = (u32)(StreamEntry::INT_POOLED_STRING_CRC32) << StreamEntry::dataInternalShift;
de.sizeAndFlags |= sizeof(crc);
WriteBinary(de);
WriteBinary(&crc, sizeof(crc));
}
else
{
AZ::u32 stringSize = de.sizeAndFlags;
de.sizeAndFlags |= (u32)(StreamEntry::INT_POOLED_STRING) << StreamEntry::dataInternalShift;
WriteBinary(de);
WriteBinary(str.data(), stringSize);
}
}
else
{
WriteBinary(de);
WriteBinary(str.data(), de.sizeAndFlags);
}
}
template<class Allocator>
inline void Write(u32 name, const AZStd::basic_string<AZStd::wstring::value_type, AZStd::wstring::traits_type, Allocator>& str)
{
StreamEntry de;
de.name = name;
de.sizeAndFlags = static_cast<AZ::u32>(str.size());
AZ_Assert(de.sizeAndFlags <= StreamEntry::dataSizeMask, "Invalid string length! String is too long, length is limited to %u bytes!", StreamEntry::dataSizeMask);
WriteBinary(de);
WriteBinary(str.data(), de.sizeAndFlags * sizeof(AZStd::wstring::value_type));
}
//////////////////////////////////////////////////////////////////////////
// math types
inline void Write(u32 name, float f)
{
Write(name, &f, static_cast<unsigned int>(sizeof(float)));
}
inline void Write(u32 name, double d)
{
Write(name, &d, static_cast<unsigned int>(sizeof(double)));
}
void Write(u32 name, const AZ::Vector3& v);
void Write(u32 name, const AZ::Vector4& v);
void Write(u32 name, const AZ::Aabb& aabb);
void Write(u32 name, const AZ::Obb& obb);
void Write(u32 name, const AZ::Transform& tm);
void Write(u32 name, const AZ::Matrix3x3& tm);
void Write(u32 name, const AZ::Matrix4x4& tm);
void Write(u32 name, const AZ::Quaternion& tm);
void Write(u32 name, const AZ::Plane& plane);
//////////////////////////////////////////////////////////////////////////
// containers
template<class InputIterator>
inline void Write(u32 name, InputIterator first, InputIterator last)
{
// we can specialize for contiguous_iterator_tag so have only 1 write for all elements
size_t numElements = AZStd::distance(first, last);
size_t elementSize = sizeof(typename AZStd::iterator_traits<InputIterator>::value_type);
unsigned int dataSize = static_cast<unsigned int>(numElements * elementSize);
StreamEntry de;
de.name = name;
de.sizeAndFlags = dataSize;
AZ_Assert(dataSize < StreamEntry::dataSizeMask, "Invalid data size, size is limited to %d bytes!", StreamEntry::dataSizeMask - 1);
WriteBinary(de);
//WriteBinary(data,dataSize); for contiguous_iterator_tag
for (; first != last; ++first)
{
WriteBinary(&*first, static_cast<unsigned int>(elementSize));
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Raw data to the output
template<class T>
inline void WriteBinary(const T& data)
{
WriteBinary(&data, sizeof(T));
}
virtual void WriteBinary(const void* data, unsigned int dataSize) = 0;
//////////////////////////////////////////////////////////////////////////
/**
* Write a time stamp (AZStd::sys_time_t) in millisecond since 1970/01/01 00:00:00 UTC.
* On older windows this function can have ~15 ms resolution, in such cases use \ref GetTimeNowMicroSecond
*/
void WriteTimeUTC(u32 name);
/**
* Write a time stamp (AZStd::sys_time_t) in micriseconds. This function is inaccurate for long periods but it has ms resolution.
* For long periods use \ref WriteTimeUTC.
*/
void WriteTimeMicrosecond(u32 name);
/// Called when the driller is moving on the next frame, so you can flush you current buffer to network/disk.
virtual void OnEndOfFrame() {}
/// Sets the string pool used for this stream. To disable the pool just set it to NULL.
void SetStringPool(DrillerStringPool* stringPool) { m_stringPool = stringPool; }
protected:
/// Write the Stream header structure (should be endianess independent).
void WriteHeader();
DrillerStringPool* m_stringPool; ///< Optional pointer to a string pool.
};
/**
* For efficiency all data read functions are placed with the parsers.
*/
class DrillerInputStream
{
public:
DrillerInputStream(DrillerStringPool* stringPool = NULL)
: m_isEndianSwap(false)
, m_stringPool(stringPool) {}
virtual ~DrillerInputStream() {}
bool IsEndianSwap() const { return m_isEndianSwap; }
/// Reads binary data from a stream to to maxDataSize. Returns 0 if no more data.
virtual unsigned int ReadBinary(void* data, unsigned int maxDataSize) = 0;
/// Sets the string pool used for this stream. To disable the pool just set it to NULL.
void SetStringPool(DrillerStringPool* stringPool) { m_stringPool = stringPool; }
DrillerStringPool* GetStringPool() const { return m_stringPool; }
void SetIdentifier(const char* identifier) { m_streamIdentifier = identifier; }
const char* GetIdentifier() const { return m_streamIdentifier.c_str(); }
protected:
/// Read the Stream header structure
bool ReadHeader();
bool m_isEndianSwap;
DrillerStringPool* m_stringPool; ///< Optional pointer to a string pool.
AZStd::string m_streamIdentifier;
};
/**
* Outputs all stream data into a memory buffer. It will grow automatically.
*/
class DrillerOutputMemoryStream
: public DrillerOutputStream
{
protected:
vector<unsigned char>::type m_data;
public:
AZ_CLASS_ALLOCATOR(DrillerOutputMemoryStream, OSAllocator, 0)
DrillerOutputMemoryStream(size_t memorySize = 2048) { m_data.reserve(memorySize); }
const unsigned char* GetData() const { return m_data.data(); }
unsigned int GetDataSize() const { return static_cast<unsigned int>(m_data.size()); }
inline void Reset() { m_data.clear(); }
void WriteBinary(const void* data, unsigned int dataSize) override
{
m_data.insert(m_data.end(), reinterpret_cast<const unsigned char*>(data), reinterpret_cast<const unsigned char*>(data) + dataSize);
}
};
/**
* Reads data from a memory stream. Data is NOT copied and must be persistent while we are using it.
*/
class DrillerInputMemoryStream
: public DrillerInputStream
{
const unsigned char* m_data;
const unsigned char* m_dataEnd;
public:
AZ_CLASS_ALLOCATOR(DrillerInputMemoryStream, OSAllocator, 0)
DrillerInputMemoryStream(const char* streamIdentifier = "", const void* data = nullptr, unsigned int dataSize = 0)
: DrillerInputStream()
, m_data(nullptr)
, m_dataEnd(nullptr)
{
if (data != nullptr)
{
SetData(streamIdentifier, data, dataSize);
}
}
void SetData(const char* streamIdentifier, const void* data, unsigned int dataSize)
{
SetIdentifier(streamIdentifier);
AZ_Assert(data != nullptr && dataSize > 0, "We must have a valid pointer %p and data size %d !", data, dataSize);
if (m_data == nullptr) // this is the first data chuck, read the platform
{
m_data = reinterpret_cast<const unsigned char*>(data);
m_dataEnd = m_data + dataSize;
ReadHeader();
}
else
{
m_data = reinterpret_cast<const unsigned char*>(data);
m_dataEnd = m_data + dataSize;
}
}
unsigned int GetDataLeft() const { return static_cast<unsigned int>(m_dataEnd - m_data); }
unsigned int ReadBinary(void* data, unsigned int maxDataSize) override
{
AZ_Assert(m_data != nullptr, "You must call SetData function, before you can read data!");
AZ_Assert(data != nullptr && maxDataSize > 0, "We must have a valid pointer and max data size!");
unsigned int dataToCopy = AZStd::GetMin(static_cast<unsigned int>(m_dataEnd - m_data), maxDataSize);
if (dataToCopy)
{
memcpy(data, m_data, dataToCopy);
}
m_data += dataToCopy;
return dataToCopy;
}
};
/**
* Outputs driller data to a file (buffered)
* IMPORTANT: We provide direct IO classes (instead trough Streamer), because the driller
* framework should NOT use engine systems (for example imagine we are drilling the Streamer, using it to
* write the drilled data will invalidate all the results as the streamer is unaware which data is driller data and which not)
*/
class DrillerOutputFileStream
: public IO::SystemFile
, public DrillerOutputStream
{
ZLib* m_zlib;
vector<unsigned char>::type m_compressionBuffer;
vector<unsigned char>::type m_dataBuffer;
public:
AZ_CLASS_ALLOCATOR(DrillerOutputFileStream, OSAllocator, 0)
DrillerOutputFileStream();
~DrillerOutputFileStream();
bool Open(const char* fileName, int mode, int platformFlags = 0);
void Close();
void WriteBinary(const void* data, unsigned int dataSize) override;
};
/**
* Reads driller data from a file.
*/
class DrillerInputFileStream
: public AZ::IO::SystemFile
, public DrillerInputStream
{
ZLib* m_zlib;
vector<unsigned char>::type m_compressedData;
public:
AZ_CLASS_ALLOCATOR(DrillerInputFileStream, OSAllocator, 0)
DrillerInputFileStream();
~DrillerInputFileStream();
bool Open(const char* fileName, int mode, int platformFlags = 0);
unsigned int ReadBinary(void* data, unsigned int maxDataSize) override;
void Close();
};
/**
* SAX like stream parser for driller data. We can stream the data
* and we will trigger events as tags and data (attributes) arrive. We use less memory this way.
* \note SAX is used as reference name, we are NOT trying to compatible with
* any specs. (not that SAX has specs)
* IMPORTANT: All data callbacks (tag and data) are called in the order they were at store. You can
* use this order as event index.
*/
class DrillerSAXParser
{
public:
struct Data
{
u32 m_name; ///< Crc name of the data entry.
void* m_data; ///< Pointer to copy if the loaded data.
unsigned int m_dataSize; ///< Data size in bytes.
mutable bool m_isEndianSwap; ///< True if the user will need to swap the endian when he access the data. We swap the data is the storage so we can read it multiple times without swap.
DrillerStringPool* m_stringPool; ///< Pointer to optional data string pool.
bool m_isPooledString; ///< True if we have a pooled string (stored in the stringPool already).
bool m_isPooledStringCrc32; ///< True is we have stored a crc32 (4 bytes) which refers to a string from the String Pool.
//////////////////////////////////////////////////////////////////////////
// Generic
template<class T>
inline void Read(T& t) const
{
static_assert(AZStd::is_pod<T>::value, "T must be plain-old-data");
AZ_Assert(sizeof(t) >= m_dataSize, "You are about to lose some data, this is wrong.");
if (m_dataSize == sizeof(t))
{
// do a memcpy as alignment might be required for some data types! This is not performance critical as we usually load drill files on x86/x64
// which doesn't care about alignment.
memcpy(&t, m_data, m_dataSize);
}
else
{
AZ_Assert(AZStd::is_pointer<T>::value || AZStd::is_integral<T>::value, "We support extending only for integral types, float and pointers up to 8 bytes!");
if (AZStd::is_signed<T>::value)
{
switch (m_dataSize)
{
case 1:
t = static_cast<T>(*reinterpret_cast<s8*>(m_data));
break;
case 2:
t = static_cast<T>(*reinterpret_cast<s16*>(m_data));
break;
case 4:
t = static_cast<T>(*reinterpret_cast<s32*>(m_data));
break;
default:
AZ_Assert(false, "Source data size unsupported... we can extend only 1,2,4 bytes into 2,4,8 bytes integrals");
}
}
else
{
switch (m_dataSize)
{
case 1:
t = static_cast<T>(*reinterpret_cast<u8*>(m_data));
break;
case 2:
t = static_cast<T>(*reinterpret_cast<u16*>(m_data));
break;
case 4:
t = static_cast<T>(*reinterpret_cast<u32*>(m_data));
break;
default:
AZ_Assert(false, "Source data size unsupported... we can extend only 1,2,4 bytes into 2,4,8 bytes integrals");
}
}
}
if (m_isEndianSwap)
{
AZStd::endian_swap(t);
}
}
inline void Read(bool& b) const
{
u8* data = reinterpret_cast<u8*>(m_data);
b = false;
for (unsigned int i = 0; i < m_dataSize; ++i)
{
if (data[i] != 0)
{
b = true;
return;
}
}
}
//////////////////////////////////////////////////////////////////////////
// Binary and strings
inline unsigned int Read(void* buffer, unsigned int bufferSize) const
{
unsigned int dataToCopy = AZStd::GetMin(m_dataSize, bufferSize);
memcpy(buffer, m_data, dataToCopy);
// no data swap
return dataToCopy;
}
// a call avilable only when we use a string pool, it will return the pointer of string in the pool, so you don't need to copy it or do any fancy procedures.
inline const char* ReadPooledString() const
{
AZ_Assert(m_stringPool != nullptr, "This read type is supported only when we use string pool!");
unsigned int srcDataSize;
return PrepareString(srcDataSize);
}
inline unsigned int Read(char* string, unsigned int maxNumChars) const
{
unsigned int srcDataSize;
const char* srcData = PrepareString(srcDataSize);
unsigned int dataToCopy = AZStd::GetMin(maxNumChars - 1, srcDataSize);
memcpy(string, srcData, dataToCopy);
string[dataToCopy] = '\0';
return dataToCopy;
}
template<class Allocator>
inline unsigned int Read(AZStd::basic_string<AZStd::string::value_type, AZStd::string::traits_type, Allocator>& str) const
{
unsigned int srcDataSize;
const char* srcData = PrepareString(srcDataSize);
str = AZStd::basic_string<AZStd::string::value_type, AZStd::string::traits_type, Allocator>(static_cast<const AZStd::string::value_type*>(srcData), srcDataSize);
return m_dataSize;
}
template<class Allocator>
inline unsigned int Read(AZStd::basic_string<AZStd::wstring::value_type, AZStd::wstring::traits_type, Allocator>& str) const
{
// wstring pooling not supported yet
str = AZStd::basic_string<AZStd::wstring::value_type, AZStd::wstring::traits_type, Allocator>(static_cast<const AZStd::wstring::value_type*>(m_data), m_dataSize / 2);
if (m_isEndianSwap)
{
AZStd::endian_swap(str.begin(), str.end());
}
return m_dataSize;
}
//////////////////////////////////////////////////////////////////////////
// math types
void Read(AZ::Vector3& v) const;
void Read(AZ::Vector4& v) const;
void Read(AZ::Aabb& aabb) const;
void Read(AZ::Obb& obb) const;
void Read(AZ::Transform& tm) const;
void Read(AZ::Matrix3x3& tm) const;
void Read(AZ::Matrix4x4& tm) const;
void Read(AZ::Quaternion& tm) const;
void Read(AZ::Plane& plane) const;
//////////////////////////////////////////////////////////////////////////
// containers
template<class Container>
inline void Read(AZStd::insert_iterator<Container>& iter) const
{
typedef typename AZStd::insert_iterator<Container> InsertIterator;
// we can specialize for contiguous_iterator_tag so have only 1 write for all elements
const size_t elementSize = sizeof(InsertIterator::container_type::value_type);
size_t numElements = m_dataSize / elementSize;
AZ_Assert(m_dataSize % elementSize == 0, "Stored elements size doesn't match the read parameters!");
Data elementEntry = *this;
elementEntry.m_dataSize = elementSize;
char* dataPtr = reinterpret_cast<char*>(m_data);
for (size_t i = 0; i < numElements; ++i, ++iter)
{
typename InsertIterator::container_type::value_type value;
elementEntry.m_data = dataPtr;
Read(elementEntry, value);
iter = value;
dataPtr += elementSize;
}
}
//////////////////////////////////////////////////////////////////////////
private:
const char* PrepareString(unsigned int& stringLength) const;
};
typedef AZStd::delegate<void (u32 /*name*/, bool /*isOpen*/)> TagCallbackType;
typedef AZStd::delegate<void (const Data&)> DataCallbackType;
AZ_CLASS_ALLOCATOR(DrillerSAXParser, OSAllocator, 0)
DrillerSAXParser(const TagCallbackType& tcb, const DataCallbackType& dcb);
/// Processes an input stream until all data is consumed (read returns 0 bytes).
void ProcessStream(DrillerInputStream& stream);
protected:
typedef vector<char>::type BufferType;
BufferType m_buffer;
TagCallbackType m_tagCallback;
DataCallbackType m_dataCallback;
};
/**
* DOM like parser, we will load the entire stream in memory (ProcessStream function).
* Depending on the data size this can be very memory consuming.
* \note DOM is used as reference we are NOT compliant with the DOM specs in any way.
* IMPORTANT: All data is stored (for parsing) in the same order the events occurred
* or the remote machine. Each next tad or data was recorded in the way. You can use
* this as an event index.
*/
class DrillerDOMParser
: public DrillerSAXParser
{
public:
struct Node
{
typedef forward_list<Data>::type DataListType;
typedef forward_list<Node>::type NodeListType;
u32 m_name;
Node* m_parent;
DataListType m_data;
NodeListType m_tags;
/// Return a pointer to the first tag with specific name.
const Node* GetTag(u32 tagName) const;
/// Returns pointer to the first data entry with specific name. NULL if not data has been found.
const Data* GetData(u32 dataName) const;
/// Returns pointer to the first data entry with specific name. If it can't be found it will assert
const Data* GetDataRequired(u32 dataName) const
{
const Data* dataNode = GetData(dataName);
AZ_Assert(dataNode != NULL, "Data node in tag 0x%08x with name 0x%08x is required but missing!", m_name, dataName);
return dataNode;
}
};
AZ_CLASS_ALLOCATOR(DrillerDOMParser, OSAllocator, 0)
DrillerDOMParser(bool isPersistentInputData = false);
~DrillerDOMParser();
/// return true if we are at top level of the tree and we can parse the data safely (there may be still more data, but it's top level only).
bool CanParse() const { return m_topNode == &m_root; }
const Node* GetRootNode() const { return &m_root; }
protected:
Node m_root;
Node* m_topNode;
bool m_isPersistentInputData; ///< true if data that we process is persistent so we don't need to copy it internally, false otherwise.
void OnTag(u32 name, bool isOpen);
void OnData(const Data& data);
void DeleteNode(Node& node);
};
/**
* Base class for handling a Tag with a specific name. Handlers are kept in a hierarchy
* with one required by DrillerSAXParserHandler to be able to handle tags at a root
* level for the driller data stream.
*/
class DrillerHandlerParser
{
public:
DrillerHandlerParser(bool isWarnOnUnsupportedTags = true)
: m_isWarnOnUnsupportedTags(isWarnOnUnsupportedTags) {}
virtual ~DrillerHandlerParser() {}
/// Enumerate all the child tags that we support for the tag we are handling. If the tag is not know you should return NULL
virtual DrillerHandlerParser* OnEnterTag(u32 tagName) { (void)tagName; return NULL; }
/// Exit tag you are not required to implement this, we always exist tags in order FILO.
virtual void OnExitTag(DrillerHandlerParser* handler, u32 tagName) { (void)handler; (void)tagName; }
/// Handle that data for the tag we are handling.
virtual void OnData(const DrillerSAXParser::Data& dataNode) { (void)dataNode; }
/// Return the warning state on unsupported tags (sometime you might want to warn usually) and sometimes not (if you load newer drills, etc.)
inline bool IsWarnOnUnsupportedTags() const { return m_isWarnOnUnsupportedTags; }
protected:
bool m_isWarnOnUnsupportedTags;
};
/**
* Processes a driller driller and dispatches the data based on the
* the DrillerHandlerParser (handlers) and their ability to handle specific tags.
* If a tag is NOT found as a child of the current one it will display a warning with the tag name
* (useless it's allowed by DrillerHandlerParser::IsWarnOnUnsupportedTags) and process the stream
* is a safe manner by skipping all the data and tags we can't handle.
*/
class DrillerSAXParserHandler
: public DrillerSAXParser
{
public:
AZ_CLASS_ALLOCATOR(DrillerSAXParserHandler, OSAllocator, 0)
DrillerSAXParserHandler(DrillerHandlerParser* rootHandler);
protected:
/// Called from DrillerSAXParser when we have an open tag.
void OnTag(u32 name, bool isOpen);
/// Called from DrillerSAXParser when we have data, which will be forwarded to the handler.
void OnData(const DrillerSAXParser::Data& data);
typedef vector<DrillerHandlerParser*>::type DrillerHandlerStackType;
DrillerHandlerStackType m_stack;
};
} // namespace Debug
} // namespace AZ
#endif // AZCORE_DRILLER_STREAM_H
#pragma once
@@ -1,72 +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
*
*/
#ifndef AZCORE_SYSTEM_FILE_BUS_H
#define AZCORE_SYSTEM_FILE_BUS_H
#include <AzCore/IO/SystemFile.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/parallel/mutex.h>
namespace AZ
{
namespace IO
{
/**
* File IO interface. All events return true if we executed the
* specific operation and no other code will be executed. If we return false
* the normal code for the specific event will be executed.
* IMPORTANT: We support multiple listeners with the idea that many systems can listen
* for event. This interface allows to actually perform the operations, in such cases make
* sure only one of the listeners provides this service (otherwise depending on registration
* order service providers may change)
* IMPORTANT: We don't provide any sync for the FileIOBus. We do that for a couple of reasons.
* 1. If you will handle file IO youself or keeptrack of statistics you code will most likely already do that
* 2. It is NOT safe to BusConnect/BusDisconnect while the FileIO is in use (this is why you should connect in advance)
* otherwise if you provide service you can end up connecting in a middle of reads/writes/etc.
*/
class FileIO
: public AZ::EBusTraits
{
public:
virtual ~FileIO() {}
virtual bool OnOpen(SystemFile& file, const char* fileName, int mode, int platformFlags, bool& isFileOpened) = 0;
virtual bool OnClose(SystemFile& file) = 0;
virtual bool OnSeek(SystemFile& file, SystemFile::SizeType offset, SystemFile::SeekMode mode) = 0;
virtual bool OnRead(SystemFile& file, SystemFile::SizeType byteSize, void* buffer, SystemFile::SizeType& numRead) = 0;
virtual bool OnWrite(SystemFile& file, const void* buffer, SystemFile::SizeType byteSize, SystemFile::SizeType& numWritten) = 0;
};
typedef AZ::EBus<FileIO> FileIOBus;
/**
* Interface for handling file io events. All events are syncronized
*/
class FileIOEvents
: public AZ::EBusTraits
{
public:
virtual ~FileIOEvents() {}
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
//TODO rbbaklov or zolniery look into why a recursive lock was not needed previously
typedef AZStd::recursive_mutex MutexType; //< make sure all file events are thread safe as they will called from many threads
//////////////////////////////////////////////////////////////////////////
/**
* You will either have a file (SystemFile) pointer or fileName pointer to the file name.
* \param fileName is provided when there is NO SystemFile object (when you call static functions).
*/
virtual void OnError(const SystemFile* file, const char* fileName, int errorCode) = 0;
};
typedef AZ::EBus<FileIOEvents> FileIOEventBus;
}
}
#endif // AZCORE_SYSTEM_FILE_BUS_H
#pragma once
@@ -8,7 +8,6 @@
#include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileIOEventBus.h>
#include <AzCore/PlatformIncl.h>
#include <AzCore/std/functional.h>
@@ -101,7 +100,6 @@ bool SystemFile::Open(const char* fileName, int mode, int platformFlags)
{
if (strlen(fileName) > m_fileName.max_size())
{
EBUS_EVENT(FileIOEventBus, OnError, this, nullptr, 0);
return false;
}
@@ -109,17 +107,6 @@ bool SystemFile::Open(const char* fileName, int mode, int platformFlags)
m_fileName = fileName;
}
if (FileIOBus::HasHandlers())
{
bool isOpen = false;
bool isHandled = false;
EBUS_EVENT_RESULT(isHandled, FileIOBus, OnOpen, *this, m_fileName.c_str(), mode, platformFlags, isOpen);
if (isHandled)
{
return isOpen;
}
}
AZ_Assert(!IsOpen(), "This file (%s) is already open!", m_fileName.c_str());
return PlatformOpen(mode, platformFlags);
@@ -133,31 +120,11 @@ bool SystemFile::ReOpen(int mode, int platformFlags)
void SystemFile::Close()
{
if (FileIOBus::HasHandlers())
{
bool isHandled = false;
EBUS_EVENT_RESULT(isHandled, FileIOBus, OnClose, *this);
if (isHandled)
{
return;
}
}
PlatformClose();
}
void SystemFile::Seek(SeekSizeType offset, SeekMode mode)
{
if (FileIOBus::HasHandlers())
{
bool isHandled = false;
EBUS_EVENT_RESULT(isHandled, FileIOBus, OnSeek, *this, offset, mode);
if (isHandled)
{
return;
}
}
Platform::Seek(m_handle, this, offset, mode);
}
@@ -178,33 +145,11 @@ AZ::u64 SystemFile::ModificationTime()
SystemFile::SizeType SystemFile::Read(SizeType byteSize, void* buffer)
{
if (FileIOBus::HasHandlers())
{
SizeType numRead = 0;
bool isHandled = false;
EBUS_EVENT_RESULT(isHandled, FileIOBus, OnRead, *this, byteSize, buffer, numRead);
if (isHandled)
{
return numRead;
}
}
return Platform::Read(m_handle, this, byteSize, buffer);
}
SystemFile::SizeType SystemFile::Write(const void* buffer, SizeType byteSize)
{
if (FileIOBus::HasHandlers())
{
SizeType numWritten = 0;
bool isHandled = false;
EBUS_EVENT_RESULT(isHandled, FileIOBus, OnWrite, *this, buffer, byteSize, numWritten);
if (isHandled)
{
return numWritten;
}
}
return Platform::Write(m_handle, this, buffer, byteSize);
}
@@ -9,10 +9,10 @@
#include <AzCore/PlatformIncl.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/Memory/AllocatorManager.h>
#include <AzCore/Driller/DrillerBus.h>
#include <AzCore/std/time.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/parallel/scoped_lock.h>
#include <AzCore/Debug/StackTracer.h>
@@ -26,30 +26,25 @@ using namespace AZ::Debug;
// AllocationRecords
// [9/16/2009]
//=========================================================================
AllocationRecords::AllocationRecords(unsigned char stackRecordLevels, bool isMemoryGuard, bool isMarkUnallocatedMemory, const char* allocatorName)
AllocationRecords::AllocationRecords(unsigned char stackRecordLevels, [[maybe_unused]] bool isMemoryGuard, bool isMarkUnallocatedMemory, const char* allocatorName)
: m_mode(AllocatorManager::Instance().m_defaultTrackingRecordMode)
, m_isAutoIntegrityCheck(false)
, m_isMarkUnallocatedMemory(isMarkUnallocatedMemory)
, m_saveNames(false)
, m_decodeImmediately(false)
, m_numStackLevels(stackRecordLevels)
#if defined(ENABLE_MEMORY_GUARD)
, m_memoryGuardSize(isMemoryGuard ? sizeof(Debug::GuardValue) : 0)
#else
, m_memoryGuardSize(0)
#endif
, m_requestedAllocs(0)
, m_requestedBytes(0)
, m_requestedBytesPeak(0)
, m_allocatorName(allocatorName)
{
#if defined(ENABLE_MEMORY_GUARD)
m_memoryGuardSize = isMemoryGuard ? sizeof(Debug::GuardValue) : 0;
#else
(void)isMemoryGuard;
m_memoryGuardSize = 0;
#endif
#if AZ_TRAIT_OS_HAS_CRITICAL_SECTION_SPIN_COUNT
SetCriticalSectionSpinCount(DrillerEBusMutex::GetMutex().native_handle(), 4000);
#endif
// preallocate some buckets
//m_records.rehash(20000);
};
}
//=========================================================================
// ~AllocationRecords
@@ -73,7 +68,7 @@ AllocationRecords::~AllocationRecords()
void
AllocationRecords::lock()
{
DrillerEBusMutex::GetMutex().lock();
m_recordsMutex.lock();
}
//=========================================================================
@@ -82,7 +77,7 @@ AllocationRecords::lock()
//=========================================================================
bool AllocationRecords::try_lock()
{
return DrillerEBusMutex::GetMutex().try_lock();
return m_recordsMutex.try_lock();
}
//=========================================================================
@@ -92,7 +87,7 @@ bool AllocationRecords::try_lock()
void
AllocationRecords::unlock()
{
DrillerEBusMutex::GetMutex().unlock();
m_recordsMutex.unlock();
}
//=========================================================================
@@ -117,7 +112,7 @@ AllocationRecords::RegisterAllocation(void* address, size_t byteSize, size_t ali
{
if (m_isAutoIntegrityCheck)
{
IntegrityCheckNoLock();
IntegrityCheck();
}
AZ_Assert(byteSize>sizeof(Debug::GuardValue), "Did you forget to add the extra MemoryGuardSize() bytes?");
@@ -125,7 +120,11 @@ AllocationRecords::RegisterAllocation(void* address, size_t byteSize, size_t ali
new(reinterpret_cast<char*>(address)+byteSize) Debug::GuardValue();
}
Debug::AllocationRecordsType::pair_iter_bool iterBool = m_records.insert_key(address);
Debug::AllocationRecordsType::pair_iter_bool iterBool;
{
AZStd::scoped_lock lock(m_recordsMutex);
iterBool = m_records.insert_key(address);
}
if (!iterBool.second)
{
@@ -210,7 +209,15 @@ AllocationRecords::RegisterAllocation(void* address, size_t byteSize, size_t ali
// statistics
m_requestedBytes += byteSize;
m_requestedBytesPeak = AZStd::GetMax(m_requestedBytesPeak, m_requestedBytes);
size_t currentRequestedBytePeak;
size_t newRequestedBytePeak;
do
{
currentRequestedBytePeak = m_requestedBytesPeak.load(std::memory_order::memory_order_relaxed);
newRequestedBytePeak = AZStd::GetMax(currentRequestedBytePeak, m_requestedBytes.load(std::memory_order::memory_order_relaxed));
} while (!m_requestedBytesPeak.compare_exchange_weak(currentRequestedBytePeak, newRequestedBytePeak));
++m_requestedAllocs;
return &ai;
@@ -220,8 +227,7 @@ AllocationRecords::RegisterAllocation(void* address, size_t byteSize, size_t ali
// UnregisterAllocation
// [9/11/2009]
//=========================================================================
void
AllocationRecords::UnregisterAllocation(void* address, size_t byteSize, size_t alignment, AllocationInfo* info)
void AllocationRecords::UnregisterAllocation(void* address, size_t byteSize, size_t alignment, AllocationInfo* info)
{
if (m_mode == RECORD_NO_RECORDS)
{
@@ -232,24 +238,38 @@ AllocationRecords::UnregisterAllocation(void* address, size_t byteSize, size_t a
return;
}
Debug::AllocationRecordsType::iterator iter = m_records.find(address);
// We cannot assert if an allocation does not exist because our allocators start up way before the driller is started and the Allocator Records would be created.
// It is currently impossible to actually track all allocations that happen before a certain point
//AZ_Assert(iter!=m_records.end(), "Could not find address 0x%p in the allocator!", address);
if (iter == m_records.end())
AllocationInfo allocationInfo;
{
return;
AZStd::scoped_lock lock(m_recordsMutex);
Debug::AllocationRecordsType::iterator iter = m_records.find(address);
// We cannot assert if an allocation does not exist because allocations may have been made before tracking was enabled.
// It is currently impossible to actually track all allocations that happen before a certain point
// AZ_Assert(iter!=m_records.end(), "Could not find address 0x%p in the allocator!", address);
if (iter == m_records.end())
{
return;
}
allocationInfo = iter->second;
m_records.erase(iter);
// try to be more aggressive and keep the memory footprint low.
// \todo store the load factor at the last rehash to avoid unnecessary rehash
if (m_records.load_factor() < 0.9f)
{
m_records.rehash(0);
}
}
AllocatorManager::Instance().DebugBreak(address, iter->second);
AllocatorManager::Instance().DebugBreak(address, allocationInfo);
(void)byteSize;
(void)alignment;
AZ_Assert(byteSize==0||byteSize==iter->second.m_byteSize, "Mismatched byteSize at deallocation! You supplied an invalid value!");
AZ_Assert(alignment==0||alignment==iter->second.m_alignment, "Mismatched alignment at deallocation! You supplied an invalid value!");
AZ_Assert(byteSize==0||byteSize==allocationInfo.m_byteSize, "Mismatched byteSize at deallocation! You supplied an invalid value!");
AZ_Assert(alignment==0||alignment==allocationInfo.m_alignment, "Mismatched alignment at deallocation! You supplied an invalid value!");
// statistics
m_requestedBytes -= iter->second.m_byteSize;
m_requestedBytes -= allocationInfo.m_byteSize;
#if defined(ENABLE_MEMORY_GUARD)
// memory guard
@@ -258,18 +278,18 @@ AllocationRecords::UnregisterAllocation(void* address, size_t byteSize, size_t a
if (m_isAutoIntegrityCheck)
{
// full integrity check
IntegrityCheckNoLock();
IntegrityCheck();
}
else
{
// check current allocation
char* guardAddress = reinterpret_cast<char*>(address)+iter->second.m_byteSize;
char* guardAddress = reinterpret_cast<char*>(address)+allocationInfo.m_byteSize;
Debug::GuardValue* guard = reinterpret_cast<Debug::GuardValue*>(guardAddress);
if (!guard->Validate())
{
AZ_Printf("Memory", "Memory stomp located at address %p, part of allocation:", guardAddress);
PrintAllocationsCB printAlloc(true);
printAlloc(address, iter->second, m_numStackLevels);
printAlloc(address, allocationInfo, m_numStackLevels);
AZ_Assert(false, "MEMORY STOMP DETECTED!!!");
}
guard->~GuardValue();
@@ -278,33 +298,26 @@ AllocationRecords::UnregisterAllocation(void* address, size_t byteSize, size_t a
#endif
// delete allocation record
if (iter->second.m_namesBlock)
if (allocationInfo.m_namesBlock)
{
m_records.get_allocator().deallocate(iter->second.m_namesBlock, iter->second.m_namesBlockSize, 1);
iter->second.m_namesBlock = nullptr;
iter->second.m_namesBlockSize = 0;
iter->second.m_name = nullptr;
iter->second.m_fileName = nullptr;
m_records.get_allocator().deallocate(allocationInfo.m_namesBlock, allocationInfo.m_namesBlockSize, 1);
allocationInfo.m_namesBlock = nullptr;
allocationInfo.m_namesBlockSize = 0;
allocationInfo.m_name = nullptr;
allocationInfo.m_fileName = nullptr;
}
if (iter->second.m_stackFrames)
if (allocationInfo.m_stackFrames)
{
m_records.get_allocator().deallocate(iter->second.m_stackFrames, sizeof(AZ::Debug::StackFrame)*m_numStackLevels, 1);
iter->second.m_stackFrames = nullptr;
m_records.get_allocator().deallocate(allocationInfo.m_stackFrames, sizeof(AZ::Debug::StackFrame)*m_numStackLevels, 1);
allocationInfo.m_stackFrames = nullptr;
}
if (info)
{
*info = iter->second;
*info = allocationInfo;
}
m_records.erase(iter);
// try to be more aggressive and keep the memory footprint low.
// \todo store the load factor at the last rehash to avoid unnecessary rehash
if (m_records.load_factor()<0.9f)
{
m_records.rehash(0);
}
// if requested set memory to a specific value.
if (m_isMarkUnallocatedMemory)
@@ -325,9 +338,14 @@ AllocationRecords::ResizeAllocation(void* address, size_t newSize)
return;
}
Debug::AllocationRecordsType::iterator iter = m_records.find(address);
AZ_Assert(iter!=m_records.end(), "Could not find address 0x%p in the allocator!", address);
AllocatorManager::Instance().DebugBreak(address, iter->second);
AllocationInfo* allocationInfo;
{
AZStd::scoped_lock lock(m_recordsMutex);
Debug::AllocationRecordsType::iterator iter = m_records.find(address);
AZ_Assert(iter != m_records.end(), "Could not find address 0x%p in the allocator!", address);
allocationInfo = &iter->second;
}
AllocatorManager::Instance().DebugBreak(address, *allocationInfo);
#if defined(ENABLE_MEMORY_GUARD)
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
@@ -335,12 +353,12 @@ AllocationRecords::ResizeAllocation(void* address, size_t newSize)
if (m_isAutoIntegrityCheck)
{
// full integrity check
IntegrityCheckNoLock();
IntegrityCheck();
}
else
{
// check memory guard
char* guardAddress = reinterpret_cast<char*>(address)+iter->second.m_byteSize;
char* guardAddress = reinterpret_cast<char*>(address) + allocationInfo->m_byteSize;
Debug::GuardValue* guard = reinterpret_cast<Debug::GuardValue*>(guardAddress);
if (!guard->Validate())
{
@@ -358,13 +376,19 @@ AllocationRecords::ResizeAllocation(void* address, size_t newSize)
#endif
// statistics
m_requestedBytes -= iter->second.m_byteSize;
m_requestedBytes -= allocationInfo->m_byteSize;
m_requestedBytes += newSize;
m_requestedBytesPeak = AZStd::GetMax(m_requestedBytesPeak, m_requestedBytes);
size_t currentRequestedBytePeak;
size_t newRequestedBytePeak;
do
{
currentRequestedBytePeak = m_requestedBytesPeak.load(std::memory_order::memory_order_relaxed);
newRequestedBytePeak = AZStd::GetMax(currentRequestedBytePeak, m_requestedBytes.load(std::memory_order::memory_order_relaxed));
} while (!m_requestedBytesPeak.compare_exchange_weak(currentRequestedBytePeak, newRequestedBytePeak));
++m_requestedAllocs;
// update allocation size
iter->second.m_byteSize = newSize;
allocationInfo->m_byteSize = newSize;
}
//=========================================================================
@@ -374,21 +398,20 @@ AllocationRecords::ResizeAllocation(void* address, size_t newSize)
void
AllocationRecords::SetMode(Mode mode)
{
DrillerEBusMutex::GetMutex().lock();
if (mode==RECORD_NO_RECORDS)
if (mode == RECORD_NO_RECORDS)
{
m_records.clear();
{
AZStd::scoped_lock lock(m_recordsMutex);
m_records.clear();
}
m_requestedBytes = 0;
m_requestedBytesPeak = 0;
m_requestedAllocs = 0;
}
AZ_Warning("Memory", m_mode!=RECORD_NO_RECORDS||mode==RECORD_NO_RECORDS, "Records recording was disabled and now it's enabled! You might get assert when you free memory, if a you have allocations which were not recorded!");
AZ_Warning("Memory", m_mode != RECORD_NO_RECORDS || mode == RECORD_NO_RECORDS, "Records recording was disabled and now it's enabled! You might get assert when you free memory, if a you have allocations which were not recorded!");
m_mode = mode;
DrillerEBusMutex::GetMutex().unlock();
}
//=========================================================================
@@ -398,11 +421,14 @@ AllocationRecords::SetMode(Mode mode)
void
AllocationRecords::EnumerateAllocations(AllocationInfoCBType cb)
{
DrillerEBusMutex::GetMutex().lock();
// enumerate all allocations and stop if requested.
// Since allocations can change during the iteration (code that prints out the records could allocate, which will
// mutate m_records), we are going to make a copy and iterate the copy.
const Debug::AllocationRecordsType recordsCopy = m_records;
Debug::AllocationRecordsType recordsCopy;
{
AZStd::scoped_lock lock(m_recordsMutex);
recordsCopy = m_records;
}
for (Debug::AllocationRecordsType::const_iterator iter = recordsCopy.begin(); iter != recordsCopy.end(); ++iter)
{
if (!cb(iter->first, iter->second, m_numStackLevels))
@@ -410,7 +436,6 @@ AllocationRecords::EnumerateAllocations(AllocationInfoCBType cb)
break;
}
}
DrillerEBusMutex::GetMutex().unlock();
}
//=========================================================================
@@ -420,38 +445,29 @@ AllocationRecords::EnumerateAllocations(AllocationInfoCBType cb)
void
AllocationRecords::IntegrityCheck() const
{
#if defined(ENABLE_MEMORY_GUARD)
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
{
DrillerEBusMutex::GetMutex().lock();
IntegrityCheckNoLock();
DrillerEBusMutex::GetMutex().unlock();
}
}
//=========================================================================
// IntegrityCheckNoLock
// [9/13/2011]
//=========================================================================
void
AllocationRecords::IntegrityCheckNoLock() const
{
#if defined(ENABLE_MEMORY_GUARD)
for (Debug::AllocationRecordsType::const_iterator iter = m_records.begin(); iter != m_records.end(); ++iter)
{
// check memory guard
const char* guardAddress = reinterpret_cast<const char*>(iter->first)+ iter->second.m_byteSize;
if (!reinterpret_cast<const Debug::GuardValue*>(guardAddress)->Validate())
Debug::AllocationRecordsType recordsCopy;
{
// We have to turn off the integrity check at this point if we want to succesfully report the memory
// stomp we just found. If we don't turn this off, the printf just winds off the stack as each memory
// allocation done therein recurses this same code.
*const_cast<bool*>(&m_isAutoIntegrityCheck) = false;
AZ_Printf("Memory", "Memory stomp located at address %p, part of allocation:", guardAddress);
PrintAllocationsCB printAlloc(true);
printAlloc(iter->first, iter->second, m_numStackLevels);
AZ_Error("Memory", false, "MEMORY STOMP DETECTED!!!");
AZStd::scoped_lock lock(m_recordsMutex);
recordsCopy = m_records;
}
for (Debug::AllocationRecordsType::const_iterator iter = recordsCopy.begin(); iter != recordsCopy.end(); ++iter)
{
// check memory guard
const char* guardAddress = reinterpret_cast<const char*>(iter->first)+ iter->second.m_byteSize;
if (!reinterpret_cast<const Debug::GuardValue*>(guardAddress)->Validate())
{
// We have to turn off the integrity check at this point if we want to succesfully report the memory
// stomp we just found. If we don't turn this off, the printf just winds off the stack as each memory
// allocation done therein recurses this same code.
*const_cast<bool*>(&m_isAutoIntegrityCheck) = false;
AZ_Printf("Memory", "Memory stomp located at address %p, part of allocation:", guardAddress);
PrintAllocationsCB printAlloc(true);
printAlloc(iter->first, iter->second, m_numStackLevels);
AZ_Error("Memory", false, "MEMORY STOMP DETECTED!!!");
}
}
}
#endif
@@ -120,10 +120,9 @@ namespace AZ
*/
class AllocationRecords
{
friend class MemoryDriller;
public:
AZ_CLASS_ALLOCATOR(AllocationRecords, OSAllocator, 0);
public:
enum Mode : int
{
RECORD_NO_RECORDS, ///< Never record any information.
@@ -178,7 +177,7 @@ namespace AZ
/// Returns peak of requested memory. IMPORTANT: This is user requested memory! Any allocator overhead is NOT included.
size_t RequestedBytesPeak() const { return m_requestedBytesPeak; }
/// Reset the peak allocation to the current requested memory.
void ResetPeakBytes() { m_requestedBytesPeak = m_requestedBytes; }
void ResetPeakBytes() { m_requestedBytesPeak.store(m_requestedBytes); }
/// Return requested user bytes. IMPORTANT: This is user requested memory! Any allocator overhead is NOT included.
size_t RequestedBytes() const { return m_requestedBytes; }
/// Returns total number of requested allocations.
@@ -186,8 +185,6 @@ namespace AZ
const char* GetAllocatorName() const { return m_allocatorName; }
protected:
// @{ Allocation tracking management - we assume this functions are called with the lock locked.
const AllocationInfo* RegisterAllocation(void* address, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, unsigned int stackSuppressCount);
void UnregisterAllocation(void* address, size_t byteSize, size_t alignment, AllocationInfo* info);
@@ -195,9 +192,9 @@ namespace AZ
void ResizeAllocation(void* address, size_t newSize);
// @}
void IntegrityCheckNoLock() const;
protected:
Debug::AllocationRecordsType m_records;
AZStd::spin_mutex m_recordsMutex;
Mode m_mode;
bool m_isAutoIntegrityCheck;
bool m_isMarkUnallocatedMemory; ///< True if we want to set value 0xcd in unallocated memory.
@@ -205,9 +202,9 @@ namespace AZ
bool m_decodeImmediately;
unsigned char m_numStackLevels;
unsigned int m_memoryGuardSize;
size_t m_requestedAllocs;
size_t m_requestedBytes;
size_t m_requestedBytesPeak;
AZStd::atomic<size_t> m_requestedAllocs;
AZStd::atomic<size_t> m_requestedBytes;
AZStd::atomic<size_t> m_requestedBytesPeak;
const char* m_allocatorName;
};
@@ -8,7 +8,6 @@
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/AllocatorManager.h>
#include <AzCore/Memory/MemoryDrillerBus.h>
using namespace AZ;
@@ -117,16 +116,24 @@ void AllocatorBase::PostCreate()
}
}
#if PLATFORM_MEMORY_INSTRUMENTATION_ENABLED
m_platformMemoryInstrumentationGroupId = AZ::PlatformMemoryInstrumentation::GetNextGroupId();
AZ::PlatformMemoryInstrumentation::RegisterGroup(m_platformMemoryInstrumentationGroupId, GetDescription(), AZ::PlatformMemoryInstrumentation::m_groupRoot);
#endif
const auto debugConfig = GetDebugConfig();
if (!debugConfig.m_excludeFromDebugging)
{
SetRecords(aznew Debug::AllocationRecords((unsigned char)debugConfig.m_stackRecordLevels, debugConfig.m_usesMemoryGuards, debugConfig.m_marksUnallocatedMemory, GetName()));
}
m_isReady = true;
}
void AllocatorBase::PreDestroy()
{
Debug::AllocationRecords* allocatorRecords = GetRecords();
if(allocatorRecords)
{
delete allocatorRecords;
SetRecords(nullptr);
}
if (m_registrationEnabled && AZ::AllocatorManager::IsReady())
{
AllocatorManager::Instance().UnRegisterAllocator(this);
@@ -173,11 +180,11 @@ void AllocatorBase::ProfileAllocation(void* ptr, size_t byteSize, size_t alignme
if (m_isProfilingActive)
{
#if PLATFORM_MEMORY_INSTRUMENTATION_ENABLED
AZ::PlatformMemoryInstrumentation::Alloc(ptr, byteSize, 0, m_platformMemoryInstrumentationGroupId);
#else
EBUS_EVENT(AZ::Debug::MemoryDrillerBus, RegisterAllocation, this, ptr, byteSize, alignment, name, fileName, lineNum, suppressStackRecord);
#endif
auto records = GetRecords();
if (records)
{
records->RegisterAllocation(ptr, byteSize, alignment, name, fileName, lineNum, suppressStackRecord + 1);
}
}
#if RECORDING_ENABLED
@@ -195,11 +202,11 @@ void AllocatorBase::ProfileDeallocation(void* ptr, size_t byteSize, size_t align
{
if (m_isProfilingActive)
{
#if PLATFORM_MEMORY_INSTRUMENTATION_ENABLED
AZ::PlatformMemoryInstrumentation::Free(ptr);
#else
EBUS_EVENT(AZ::Debug::MemoryDrillerBus, UnregisterAllocation, this, ptr, byteSize, alignment, info);
#endif
auto records = GetRecords();
if (records)
{
records->UnregisterAllocation(ptr, byteSize, alignment, info);
}
}
#if RECORDING_ENABLED
{
@@ -212,29 +219,17 @@ void AllocatorBase::ProfileDeallocation(void* ptr, size_t byteSize, size_t align
#endif
}
void AllocatorBase::ProfileReallocationBegin(void* ptr, size_t newSize)
void AllocatorBase::ProfileReallocationBegin([[maybe_unused]] void* ptr, [[maybe_unused]] size_t newSize)
{
if (m_isProfilingActive)
{
#if PLATFORM_MEMORY_INSTRUMENTATION_ENABLED
AZ::PlatformMemoryInstrumentation::ReallocBegin(ptr, newSize, m_platformMemoryInstrumentationGroupId);
#else
// Driller API intensionally not called, only End is required.
AZ_UNUSED(ptr);
AZ_UNUSED(newSize);
#endif
}
}
void AllocatorBase::ProfileReallocationEnd(void* ptr, void* newPtr, size_t newSize, size_t newAlignment)
{
if (m_isProfilingActive)
{
#if PLATFORM_MEMORY_INSTRUMENTATION_ENABLED
AZ::PlatformMemoryInstrumentation::ReallocEnd(newPtr, newSize, 0);
#else
EBUS_EVENT(AZ::Debug::MemoryDrillerBus, ReallocateAllocation, this, ptr, newPtr, newSize, newAlignment);
#endif
Debug::AllocationInfo info;
ProfileDeallocation(ptr, 0, 0, &info);
ProfileAllocation(newPtr, newSize, newAlignment, info.m_name, info.m_fileName, info.m_lineNum, 0);
}
#if RECORDING_ENABLED
{
@@ -257,7 +252,11 @@ void AllocatorBase::ProfileResize(void* ptr, size_t newSize)
{
if (newSize && m_isProfilingActive)
{
EBUS_EVENT(AZ::Debug::MemoryDrillerBus, ResizeAllocation, this, ptr, newSize);
auto records = GetRecords();
if (records)
{
records->ResizeAllocation(ptr, newSize);
}
}
#if RECORDING_ENABLED
{
@@ -8,7 +8,6 @@
#pragma once
#include <AzCore/Memory/IAllocator.h>
#include <AzCore/Memory/PlatformMemoryInstrumentation.h>
namespace AZ
{
@@ -103,16 +102,13 @@ namespace AZ
const char* m_name = nullptr;
const char* m_desc = nullptr;
Debug::AllocationRecords* m_records = nullptr; // Cached pointer to allocation records. Works together with the MemoryDriller.
Debug::AllocationRecords* m_records = nullptr; // Cached pointer to allocation records
size_t m_memoryGuardSize = 0;
bool m_isLazilyCreated = false;
bool m_isProfilingActive = false;
bool m_isReady = false;
bool m_canBeOverridden = true;
bool m_registrationEnabled = true;
#if PLATFORM_MEMORY_INSTRUMENTATION_ENABLED
uint16_t m_platformMemoryInstrumentationGroupId = 0;
#endif
};
namespace Internal {
@@ -14,7 +14,6 @@
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/Memory/AllocatorOverrideShim.h>
#include <AzCore/Memory/MallocSchema.h>
#include <AzCore/Memory/MemoryDrillerBus.h>
#include <AzCore/std/parallel/lock.h>
#include <AzCore/std/smart_ptr/make_shared.h>
@@ -215,8 +214,6 @@ AllocatorManager::RegisterAllocator(class IAllocator* alloc)
#ifdef AZCORE_MEMORY_ENABLE_OVERRIDES
ConfigureAllocatorOverrides(alloc);
#endif
EBUS_EVENT(Debug::MemoryDrillerBus, RegisterAllocator, alloc);
}
//=========================================================================
@@ -319,11 +316,6 @@ AllocatorManager::UnRegisterAllocator(class IAllocator* alloc)
{
AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
if (alloc->GetRecords())
{
EBUS_EVENT(Debug::MemoryDrillerBus, UnregisterAllocator, alloc);
}
for (int i = 0; i < m_numAllocators; ++i)
{
if (m_allocators[i] == alloc)
@@ -10,7 +10,6 @@
#include <AzCore/Memory/BestFitExternalMapSchema.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/Memory/MemoryDrillerBus.h>
#include <AzCore/std/functional.h>
@@ -79,8 +78,14 @@ AllocatorDebugConfig BestFitExternalMapAllocator::GetDebugConfig()
// Allocate
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::pointer_type
BestFitExternalMapAllocator::Allocate(size_type byteSize, size_type alignment, int flags, const char* name, const char* fileName, int lineNum, unsigned int suppressStackRecord)
BestFitExternalMapAllocator::pointer_type BestFitExternalMapAllocator::Allocate(
size_type byteSize,
size_type alignment,
int flags,
[[maybe_unused]] const char* name,
[[maybe_unused]] const char* fileName,
[[maybe_unused]] int lineNum,
unsigned int suppressStackRecord)
{
(void)suppressStackRecord;
@@ -89,17 +94,6 @@ BestFitExternalMapAllocator::Allocate(size_type byteSize, size_type alignment, i
byteSize = MemorySizeAdjustedUp(byteSize);
BestFitExternalMapAllocator::pointer_type address = m_schema->Allocate(byteSize, alignment, flags);
if (address == nullptr)
{
if (!OnOutOfMemory(byteSize, alignment, flags, name, fileName, lineNum))
{
if (GetRecords())
{
EBUS_EVENT(Debug::MemoryDrillerBus, DumpAllAllocations);
}
}
}
AZ_Assert(address != nullptr, "BestFitExternalMapAllocator: Failed to allocate %d bytes aligned on %d (flags: 0x%08x) %s : %s (%d)!", byteSize, alignment, flags, name ? name : "(no name)", fileName ? fileName : "(no file name)", lineNum);
AZ_MEMORY_PROFILE(ProfileAllocation(address, byteSize, alignment, name, fileName, lineNum, suppressStackRecord + 1));
@@ -14,7 +14,6 @@ namespace AZ
namespace Debug
{
class AllocationRecords;
class MemoryDriller;
}
namespace AllocatorStorage
@@ -83,7 +82,7 @@ namespace AZ
/// Sets the number of entries to omit from the top of the callstack when recording stack traces.
AllocatorDebugConfig& StackRecordLevels(int levels) { m_stackRecordLevels = levels; return *this; }
/// Set to true if this allocator should not have its records recorded and analyzed by systems like the MemoryDriller.
/// Set to true if this allocator should not have its records recorded and analyzed.
AllocatorDebugConfig& ExcludeFromDebugging(bool exclude = true) { m_excludeFromDebugging = exclude; return *this; }
/// Set to true if this allocator expands allocations with guard sections to detect overruns.
@@ -207,8 +206,6 @@ namespace AZ
template<class Allocator>
friend class AllocatorWrapper;
friend class Debug::MemoryDriller;
};
}
@@ -1,291 +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
*
*/
#include <AzCore/Memory/MemoryDriller.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Memory/AllocatorManager.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/Debug/StackTracer.h>
namespace AZ::Debug
{
//=========================================================================
// MemoryDriller
// [2/6/2013]
//=========================================================================
MemoryDriller::MemoryDriller(const Descriptor& desc)
{
(void)desc;
BusConnect();
AllocatorManager::Instance().EnterProfilingMode();
{
// Register all allocators that were created before the driller existed
auto allocatorLock = AllocatorManager::Instance().LockAllocators();
for (int i = 0; i < AllocatorManager::Instance().GetNumAllocators(); ++i)
{
IAllocator* allocator = AllocatorManager::Instance().GetAllocator(i);
RegisterAllocator(allocator);
}
}
}
//=========================================================================
// ~MemoryDriller
// [2/6/2013]
//=========================================================================
MemoryDriller::~MemoryDriller()
{
BusDisconnect();
AllocatorManager::Instance().ExitProfilingMode();
}
//=========================================================================
// Start
// [2/6/2013]
//=========================================================================
void MemoryDriller::Start(const Param* params, int numParams)
{
(void)params;
(void)numParams;
// dump current allocations for all allocators with tracking
auto allocatorLock = AllocatorManager::Instance().LockAllocators();
for (int i = 0; i < AllocatorManager::Instance().GetNumAllocators(); ++i)
{
IAllocator* allocator = AllocatorManager::Instance().GetAllocator(i);
if (auto records = allocator->GetRecords())
{
RegisterAllocatorOutput(allocator);
const AllocationRecordsType& allocMap = records->GetMap();
for (AllocationRecordsType::const_iterator allocIt = allocMap.begin(); allocIt != allocMap.end(); ++allocIt)
{
RegisterAllocationOutput(allocator, allocIt->first, &allocIt->second);
}
}
}
}
//=========================================================================
// Stop
// [2/6/2013]
//=========================================================================
void MemoryDriller::Stop()
{
}
//=========================================================================
// RegisterAllocator
// [2/6/2013]
//=========================================================================
void MemoryDriller::RegisterAllocator(IAllocator* allocator)
{
// Ignore if our allocator is already registered
if (allocator->GetRecords() != nullptr)
{
return;
}
auto debugConfig = allocator->GetDebugConfig();
if (!debugConfig.m_excludeFromDebugging)
{
allocator->SetRecords(aznew Debug::AllocationRecords((unsigned char)debugConfig.m_stackRecordLevels, debugConfig.m_usesMemoryGuards, debugConfig.m_marksUnallocatedMemory, allocator->GetName()));
m_allAllocatorRecords.push_back(allocator->GetRecords());
if (m_output == nullptr)
{
return; // we have no active output
}
RegisterAllocatorOutput(allocator);
}
}
//=========================================================================
// RegisterAllocatorOutput
// [2/6/2013]
//=========================================================================
void MemoryDriller::RegisterAllocatorOutput(IAllocator* allocator)
{
auto records = allocator->GetRecords();
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
m_output->BeginTag(AZ_CRC("RegisterAllocator", 0x19f08114));
m_output->Write(AZ_CRC("Name", 0x5e237e06), allocator->GetName());
m_output->Write(AZ_CRC("Id", 0xbf396750), allocator);
m_output->Write(AZ_CRC("Capacity", 0xb5e8b174), allocator->GetAllocationSource()->Capacity());
m_output->Write(AZ_CRC("RecordsId", 0x7caaca88), records);
if (records)
{
m_output->Write(AZ_CRC("RecordsMode", 0x764c147a), (char)records->GetMode());
m_output->Write(AZ_CRC("NumStackLevels", 0xad9cff15), records->GetNumStackLevels());
}
m_output->EndTag(AZ_CRC("RegisterAllocator", 0x19f08114));
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
}
//=========================================================================
// UnregisterAllocator
// [2/6/2013]
//=========================================================================
void MemoryDriller::UnregisterAllocator(IAllocator* allocator)
{
auto allocatorRecords = allocator->GetRecords();
AZ_Assert(allocatorRecords, "This allocator is not registered with the memory driller!");
for (auto records : m_allAllocatorRecords)
{
if (records == allocatorRecords)
{
m_allAllocatorRecords.remove(records);
break;
}
}
delete allocatorRecords;
allocator->SetRecords(nullptr);
if (m_output == nullptr)
{
return; // we have no active output
}
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
m_output->Write(AZ_CRC("UnregisterAllocator", 0xb2b54f93), allocator);
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
}
//=========================================================================
// RegisterAllocation
// [2/6/2013]
//=========================================================================
void MemoryDriller::RegisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, unsigned int stackSuppressCount)
{
auto records = allocator->GetRecords();
if (records)
{
const AllocationInfo* info = records->RegisterAllocation(address, byteSize, alignment, name, fileName, lineNum, stackSuppressCount + 1);
if (m_output == nullptr)
{
return; // we have no active output
}
RegisterAllocationOutput(allocator, address, info);
}
}
//=========================================================================
// RegisterAllocationOutput
// [2/6/2013]
//=========================================================================
void MemoryDriller::RegisterAllocationOutput(IAllocator* allocator, void* address, const AllocationInfo* info)
{
auto records = allocator->GetRecords();
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
m_output->BeginTag(AZ_CRC("RegisterAllocation", 0x992a9780));
m_output->Write(AZ_CRC("RecordsId", 0x7caaca88), records);
m_output->Write(AZ_CRC("Address", 0x0d4e6f81), address);
if (info)
{
if (info->m_name)
{
m_output->Write(AZ_CRC("Name", 0x5e237e06), info->m_name);
}
m_output->Write(AZ_CRC("Alignment", 0x2cce1e5c), info->m_alignment);
m_output->Write(AZ_CRC("Size", 0xf7c0246a), info->m_byteSize);
if (info->m_fileName)
{
m_output->Write(AZ_CRC("FileName", 0x3c0be965), info->m_fileName);
m_output->Write(AZ_CRC("FileLine", 0xb33c2395), info->m_lineNum);
}
// copy the stack frames directly, resolving the stack should happen later as this is a SLOW procedure.
if (info->m_stackFrames)
{
m_output->Write(AZ_CRC("Stack", 0x41a87b6a), info->m_stackFrames, info->m_stackFrames + records->GetNumStackLevels());
}
}
m_output->EndTag(AZ_CRC("RegisterAllocation", 0x992a9780));
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
}
//=========================================================================
// UnRegisterAllocation
// [2/6/2013]
//=========================================================================
void MemoryDriller::UnregisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, AllocationInfo* info)
{
auto records = allocator->GetRecords();
if (records)
{
records->UnregisterAllocation(address, byteSize, alignment, info);
if (m_output == nullptr)
{
return; // we have no active output
}
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
m_output->BeginTag(AZ_CRC("UnRegisterAllocation", 0xea5dc4cd));
m_output->Write(AZ_CRC("RecordsId", 0x7caaca88), records);
m_output->Write(AZ_CRC("Address", 0x0d4e6f81), address);
m_output->EndTag(AZ_CRC("UnRegisterAllocation", 0xea5dc4cd));
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
}
}
//=========================================================================
// ReallocateAllocation
// [10/1/2018]
//=========================================================================
void MemoryDriller::ReallocateAllocation(IAllocator* allocator, void* prevAddress, void* newAddress, size_t newByteSize, size_t newAlignment)
{
AllocationInfo info;
UnregisterAllocation(allocator, prevAddress, 0, 0, &info);
RegisterAllocation(allocator, newAddress, newByteSize, newAlignment, info.m_name, info.m_fileName, info.m_lineNum, 0);
}
//=========================================================================
// ResizeAllocation
// [2/6/2013]
//=========================================================================
void MemoryDriller::ResizeAllocation(IAllocator* allocator, void* address, size_t newSize)
{
auto records = allocator->GetRecords();
if (records)
{
records->ResizeAllocation(address, newSize);
if (m_output == nullptr)
{
return; // we have no active output
}
m_output->BeginTag(AZ_CRC("MemoryDriller", 0x1b31269d));
m_output->BeginTag(AZ_CRC("ResizeAllocation", 0x8a9c78dc));
m_output->Write(AZ_CRC("RecordsId", 0x7caaca88), records);
m_output->Write(AZ_CRC("Address", 0x0d4e6f81), address);
m_output->Write(AZ_CRC("Size", 0xf7c0246a), newSize);
m_output->EndTag(AZ_CRC("ResizeAllocation", 0x8a9c78dc));
m_output->EndTag(AZ_CRC("MemoryDriller", 0x1b31269d));
}
}
void MemoryDriller::DumpAllAllocations()
{
// Create a copy so allocations done during the printing dont end up affecting the container
const AZStd::list<Debug::AllocationRecords*, OSStdAllocator> allocationRecords = m_allAllocatorRecords;
for (auto records : allocationRecords)
{
// Skip if we have had no allocations made
if (records->RequestedAllocs())
{
records->EnumerateAllocations(AZ::Debug::PrintAllocationsCB(true, true));
}
}
}
} // namespace AZ::Debug
@@ -1,71 +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
*
*/
#ifndef AZCORE_MEMORY_DRILLER_H
#define AZCORE_MEMORY_DRILLER_H 1
#include <AzCore/Driller/Driller.h>
#include <AzCore/Memory/MemoryDrillerBus.h>
namespace AZ
{
namespace Debug
{
struct StackFrame;
/**
* Trace messages driller class
*/
class MemoryDriller
: public Driller
, public MemoryDrillerBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(MemoryDriller, OSAllocator, 0)
// TODO: Centralized settings for memory tracking.
struct Descriptor
{
};
MemoryDriller(const Descriptor& desc = Descriptor());
~MemoryDriller();
protected:
//////////////////////////////////////////////////////////////////////////
// Driller
const char* GroupName() const override { return "SystemDrillers"; }
const char* GetName() const override { return "MemoryDriller"; }
const char* GetDescription() const override { return "Reports all allocators and memory allocations."; }
void Start(const Param* params = NULL, int numParams = 0) override;
void Stop() override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// MemoryDrillerBus
void RegisterAllocator(IAllocator* allocator) override;
void UnregisterAllocator(IAllocator* allocator) override;
void RegisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, unsigned int stackSuppressCount) override;
void UnregisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, AllocationInfo* info) override;
void ReallocateAllocation(IAllocator* allocator, void* prevAddress, void* newAddress, size_t newByteSize, size_t newAlignment) override;
void ResizeAllocation(IAllocator* allocator, void* address, size_t newSize) override;
void DumpAllAllocations() override;
//////////////////////////////////////////////////////////////////////////
void RegisterAllocatorOutput(IAllocator* allocator);
void RegisterAllocationOutput(IAllocator* allocator, void* address, const AllocationInfo* info);
private:
// Store a list of all of our allocator records so we can dump them all without having to know about the allocators
AZStd::list<Debug::AllocationRecords*, OSStdAllocator> m_allAllocatorRecords;
};
} // namespace Debug
} // namespace AZ
#endif // AZCORE_MEMORY_DRILLER_H
#pragma once
@@ -1,52 +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
*
*/
#ifndef AZCORE_MEMORY_DRILLER_BUS_H
#define AZCORE_MEMORY_DRILLER_BUS_H 1
#include <AzCore/Driller/DrillerBus.h>
namespace AZ
{
class IAllocator;
namespace Debug
{
//class AllocationRecords;
struct AllocationInfo;
/**
* Memory allocations driller message.
*
* We use a driller bus so all messages are sending in exclusive matter no other driller messages
* can be triggered at that moment, so we already preserve the calling order. You can assume
* all access code in the driller framework in guarded. You can manually lock the driller mutex are you
* use by using \ref AZ::Debug::DrillerEBusMutex.
*/
class MemoryDrillerMessages
: public AZ::Debug::DrillerEBusTraits
{
public:
virtual ~MemoryDrillerMessages() {}
/// Register allocation (with customizable tracking settings - TODO: we should centralize this settings and remove them from here)
virtual void RegisterAllocator(IAllocator* allocator) = 0;
virtual void UnregisterAllocator(IAllocator* allocator) = 0;
virtual void RegisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, unsigned int stackSuppressCount) = 0;
virtual void UnregisterAllocation(IAllocator* allocator, void* address, size_t byteSize, size_t alignment, AllocationInfo* info) = 0;
virtual void ReallocateAllocation(IAllocator* allocator, void* prevAddress, void* newAddress, size_t newByteSize, size_t newAlignment) = 0;
virtual void ResizeAllocation(IAllocator* allocator, void* address, size_t newSize) = 0;
virtual void DumpAllAllocations() = 0;
};
typedef AZ::EBus<MemoryDrillerMessages> MemoryDrillerBus;
} // namespace Debug
} // namespace AZ
#endif // AZCORE_MEMORY_DRILLER_BUS_H
#pragma once
@@ -20,7 +20,7 @@ namespace AZ
* OS allocator should be used for direct OS allocations (C heap)
* It's memory usage is NOT tracked. If you don't create this allocator, it will be implicitly
* created by the SystemAllocator when it is needed. In addition this allocator is used for
* debug data (like drillers, memory trackng, etc.)
* debug data (like memory tracking, etc.)
*/
class OSAllocator
: public AllocatorBase
@@ -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
#include <AzCore/AzCore_Traits_Platform.h>
#if AZ_TRAIT_OS_MEMORY_INSTRUMENTATION && !defined(_RELEASE)
#define PLATFORM_MEMORY_INSTRUMENTATION_ENABLED 1
#else
#define PLATFORM_MEMORY_INSTRUMENTATION_ENABLED 0
#endif
#if PLATFORM_MEMORY_INSTRUMENTATION_ENABLED
#include <AzCore/base.h>
#include <AzCore/std/parallel/mutex.h>
namespace AZ
{
/**
* PlatformMemoryInstrumentation - Abstraction layer for platform specific memory instrumentation.
*/
class PlatformMemoryInstrumentation
{
public:
static uint16_t GetNextGroupId() { return m_nextGroupId++; };
static void RegisterGroup(uint16_t id, const char* name, uint16_t parentGroup);
static void Alloc(const void* ptr, uint64_t size, uint32_t padding, uint16_t group);
static void Free(const void* ptr);
static void ReallocBegin(const void* origPtr, uint64_t size, uint16_t group);
static void ReallocEnd(const void* newPtr, uint64_t size, uint32_t padding);
static const uint16_t m_groupRoot;
static uint16_t m_nextGroupId;
};
}
#endif // PLATFORM_MEMORY_INSTRUMENTATION_ENABLED
@@ -12,9 +12,6 @@
#include <AzCore/Memory/PoolSchema.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/Memory/MemoryDrillerBus.h>
namespace AZ
{
template<class Allocator>
@@ -11,7 +11,6 @@
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/Memory/MemoryDrillerBus.h>
#include <AzCore/std/functional.h>
@@ -232,14 +231,6 @@ SystemAllocator::Allocate(size_type byteSize, size_type alignment, int flags, co
if (address == nullptr)
{
byteSize = MemorySizeAdjustedDown(byteSize); // restore original size
if (!OnOutOfMemory(byteSize, alignment, flags, name, fileName, lineNum))
{
if (GetRecords())
{
EBUS_EVENT(Debug::MemoryDrillerBus, DumpAllAllocations);
}
}
}
AZ_Assert(address != nullptr, "SystemAllocator: Failed to allocate %d bytes aligned on %d (flags: 0x%08x) %s : %s (%d)!", byteSize, alignment, flags, name ? name : "(no name)", fileName ? fileName : "(no file name)", lineNum);
@@ -5073,13 +5073,26 @@ LUA_API const Node* lua_getDummyNode()
// Check all constructors if they have use ScriptDataContext and if so choose this one
if (!customConstructorMethod)
{
int overrideIndex = -1;
AZ::AttributeReader(nullptr, FindAttribute
( Script::Attributes::DefaultConstructorOverrideIndex, behaviorClass->m_attributes)).Read<int>(overrideIndex);
int methodIndex = 0;
for (BehaviorMethod* method : behaviorClass->m_constructors)
{
if (methodIndex == overrideIndex)
{
customConstructorMethod = method;
break;
}
if (method->GetNumArguments() && method->GetArgument(method->GetNumArguments() - 1)->m_typeId == AZ::AzTypeInfo<ScriptDataContext>::Uuid())
{
customConstructorMethod = method;
break;
}
++methodIndex;
}
}
@@ -21,6 +21,7 @@ namespace AZ
static constexpr AZ::Crc32 ClassNameOverride = AZ_CRC_CE("ScriptClassNameOverride"); ///< Provide a custom name for script reflection, that doesn't match the behavior Context name
static constexpr AZ::Crc32 MethodOverride = AZ_CRC_CE("ScriptFunctionOverride"); ///< Use a custom function in the attribute instead of the function
static constexpr AZ::Crc32 ConstructorOverride = AZ_CRC_CE("ConstructorOverride"); ///< You can provide a custom constructor to be called when created from Lua script
static constexpr AZ::Crc32 DefaultConstructorOverrideIndex = AZ_CRC_CE("DefaultConstructorOverrideIndex"); ///< Use a different class constructor as the default constructor in Lua
static constexpr AZ::Crc32 EventHandlerCreationFunction = AZ_CRC_CE("EventHandlerCreationFunction"); ///< helps create a handler for any script target so that script functions can be used for AZ::Event signals
static constexpr AZ::Crc32 GenericConstructorOverride = AZ_CRC_CE("GenericConstructorOverride"); ///< You can provide a custom constructor to be called when creating a script
static constexpr AZ::Crc32 ReaderWriterOverride = AZ_CRC_CE("ReaderWriterOverride"); ///< paired with \ref ScriptContext::CustomReaderWriter allows you to customize read/write to Lua VM
@@ -133,6 +133,8 @@ namespace AZ
const static AZ::Crc32 AllowClearAsset = AZ_CRC("AllowClearAsset", 0x24827182);
// Show the name of the asset that was produced from the source asset
const static AZ::Crc32 ShowProductAssetFileName = AZ_CRC("ShowProductAssetFileName");
//! Regular expression pattern filter for source files
const static AZ::Crc32 SourceAssetFilterPattern = AZ_CRC_CE("SourceAssetFilterPattern");
//! Component icon attributes
const static AZ::Crc32 Icon = AZ_CRC("Icon", 0x659429db);
@@ -204,6 +204,22 @@ namespace AZ
// BaseJsonSerializer
//
JsonSerializationResult::Result BaseJsonSerializer::Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
JsonSerializationResult::ResultCode result(JsonSerializationResult::Tasks::ReadField);
result.Combine(ContinueLoading(outputValue, outputValueTypeId, inputValue, context, ContinuationFlags::IgnoreTypeSerializer));
return context.Report(result, "Ignoring custom serialization during load");
}
JsonSerializationResult::Result BaseJsonSerializer::Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context)
{
JsonSerializationResult::ResultCode result(JsonSerializationResult::Tasks::WriteValue);
result.Combine(ContinueStoring(outputValue, inputValue, defaultValue, valueTypeId, context, ContinuationFlags::IgnoreTypeSerializer));
return context.Report(result, "Ignoring custom serialization during store");
}
BaseJsonSerializer::OperationFlags BaseJsonSerializer::GetOperationsFlags() const
{
return OperationFlags::None;
@@ -180,13 +180,16 @@ namespace AZ
//! Transforms the data from the rapidjson Value to outputValue, if the conversion is possible and supported.
//! The serializer is responsible for casting to the proper type and safely writing to the outputValue memory.
//! \note The default implementation is to load the object ignoring a custom serializers for the type, which allows for custom serializers
//! to modify the object after all default loading has occurred.
virtual JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) = 0;
JsonDeserializerContext& context);
//! Write the input value to a rapidjson value if the default value is not null and doesn't match the input value, otherwise
//! an error is returned and sets the rapidjson value to a null value.
//! \note The default implementation is to store the object ignoring custom serializers.
virtual JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) = 0;
const Uuid& valueTypeId, JsonSerializerContext& context);
//! Returns the operation flags which tells the Json Serialization how this custom json serializer can be used.
virtual OperationFlags GetOperationsFlags() const;
@@ -23,7 +23,7 @@ namespace AZ::SettingsRegistryConsoleUtils
inline constexpr const char* SettingsRegistryRemove = "sr_regremove";
inline constexpr const char* SettingsRegistryDump = "sr_regdump";
inline constexpr const char* SettingsRegistryDumpAll = "sr_regdumpall";
inline constexpr const char* SettingsRegistryMergeFile = "sr_regset-file";
inline constexpr const char* SettingsRegistryMergeFile = "sr_regset_file";
// RAII structure which owns the instances of the Settings Registry Console commands
// registered with an AZ Console
@@ -53,7 +53,7 @@ namespace AZ::SettingsRegistryConsoleUtils
//! "sr_regdumpall" accepts 0 arguments and dumps the entire settings registry
//! NOTE: this might result in a large amount of output to the console
//!
//! "sr_regset-file" accepts 1 or 2 arguments - <file-path> [<anchor json path>]
//! "sr_regset_file" accepts 1 or 2 arguments - <file-path> [<anchor json path>]
//! Merges the json formatted file <file path> into the settings registry underneath the root anchor ""
//! or <anchor json path> if supplied
[[nodiscard]] ConsoleFunctorHandle RegisterAzConsoleCommands(SettingsRegistryInterface& registry, AZ::IConsole& azConsole);
@@ -13,7 +13,6 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Debug/AssetTracking.h>
namespace AZ
{
@@ -16,6 +16,7 @@
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/parallel/binary_semaphore.h>
#include <AzCore/RTTI/RTTI.h>
namespace AZ
{
@@ -13,8 +13,6 @@
#include <AzCore/Debug/BudgetTracker.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Driller/Driller.h>
#include <AzCore/Memory/MemoryDriller.h>
#include <AzCore/Memory/AllocationRecords.h>
#if defined(HAVE_BENCHMARK)
@@ -39,7 +37,6 @@ namespace UnitTest
*/
class AllocatorsBase
{
AZ::Debug::DrillerManager* m_drillerManager;
bool m_ownsAllocator{};
public:
@@ -47,8 +44,7 @@ namespace UnitTest
void SetupAllocator(const AZ::SystemAllocator::Descriptor& allocatorDesc = {})
{
m_drillerManager = AZ::Debug::DrillerManager::Create();
m_drillerManager->Register(aznew AZ::Debug::MemoryDriller);
AZ::AllocatorManager::Instance().EnterProfilingMode();
AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_FULL);
// Only create the SystemAllocator if it s not ready
@@ -68,9 +64,9 @@ namespace UnitTest
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
}
m_ownsAllocator = false;
AZ::Debug::DrillerManager::Destroy(m_drillerManager);
AZ::AllocatorManager::Instance().SetDefaultTrackingMode(AZ::Debug::AllocationRecords::RECORD_NO_RECORDS);
AZ::AllocatorManager::Instance().ExitProfilingMode();
}
};
@@ -93,8 +89,7 @@ namespace UnitTest
* Helper class to handle the boiler plate of setting up a test fixture that uses the system allocators
* If you wish to do additional setup and tear down be sure to call the base class SetUp first and TearDown
* last.
* By default memory tracking through driller is enabled.
* Defaults to a heap size of 15 MB
* By default memory tracking is enabled.
*/
class AllocatorsTestFixture
@@ -123,8 +118,7 @@ namespace UnitTest
* Helper class to handle the boiler plate of setting up a benchmark fixture that uses the system allocators
* If you wish to do additional setup and tear down be sure to call the base class SetUp first and TearDown
* last.
* By default memory tracking through driller is disabled.
* Defaults to a heap size of 15 MB
* By default memory tracking is enabled.
*/
class AllocatorsBenchmarkFixture
: public ::benchmark::Fixture
+2 -3
View File
@@ -165,13 +165,12 @@ namespace AZ::Utils
}
Container fileContent;
fileContent.resize(length);
fileContent.resize_no_construct(length);
AZ::IO::SizeType bytesRead = file.Read(length, fileContent.data());
file.Close();
// Resize again just in case bytesRead is less than length for some reason
fileContent.resize(bytesRead);
fileContent.resize_no_construct(bytesRead);
return AZ::Success(AZStd::move(fileContent));
}
@@ -92,10 +92,6 @@ set(FILES
Compression/Compression.h
Compression/zstd_compression.cpp
Compression/zstd_compression.h
Debug/AssetTracking.cpp
Debug/AssetTracking.h
Debug/AssetTrackingTypesImpl.h
Debug/AssetTrackingTypes.h
Debug/Budget.h
Debug/Budget.cpp
Debug/BudgetTracker.h
@@ -111,30 +107,21 @@ set(FILES
Debug/ProfilerReflection.cpp
Debug/ProfilerReflection.h
Debug/StackTracer.h
Debug/EventTrace.h
Debug/EventTrace.cpp
Debug/EventTraceDriller.h
Debug/EventTraceDriller.cpp
Debug/EventTraceDrillerBus.h
Debug/Timer.h
Debug/Trace.cpp
Debug/Trace.h
Debug/TraceMessageBus.h
Debug/TraceMessagesDriller.cpp
Debug/TraceMessagesDriller.h
Debug/TraceMessagesDrillerBus.h
Debug/TraceReflection.cpp
Debug/TraceReflection.h
DOM/DomBackend.cpp
DOM/DomBackend.h
DOM/DomUtils.cpp
DOM/DomUtils.h
DOM/DomVisitor.cpp
DOM/DomVisitor.h
Driller/DefaultStringPool.h
Driller/Driller.cpp
Driller/Driller.h
Driller/DrillerBus.cpp
Driller/DrillerBus.h
Driller/DrillerRootHandler.h
Driller/Stream.cpp
Driller/Stream.h
DOM/Backends/JSON/JsonBackend.h
DOM/Backends/JSON/JsonSerializationUtils.cpp
DOM/Backends/JSON/JsonSerializationUtils.h
EBus/BusImpl.h
EBus/EBus.h
EBus/EBusEnvironment.cpp
@@ -171,7 +158,6 @@ set(FILES
IO/CompressorZStd.h
IO/FileIO.cpp
IO/FileIO.h
IO/FileIOEventBus.h
IO/FileReader.cpp
IO/FileReader.h
IO/IOUtils.h
@@ -397,16 +383,12 @@ set(FILES
Memory/Memory.h
Memory/MemoryComponent.cpp
Memory/MemoryComponent.h
Memory/MemoryDriller.cpp
Memory/MemoryDriller.h
Memory/MemoryDrillerBus.h
Memory/nedmalloc.inl
Memory/NewAndDelete.inl
Memory/OSAllocator.cpp
Memory/OSAllocator.h
Memory/OverrunDetectionAllocator.cpp
Memory/OverrunDetectionAllocator.h
Memory/PlatformMemoryInstrumentation.h
Memory/PoolAllocator.h
Memory/PoolSchema.cpp
Memory/PoolSchema.h
@@ -8,9 +8,9 @@
#ifndef AZSTD_THREAD_BUS_H
#define AZSTD_THREAD_BUS_H 1
#include <AzCore/Driller/DrillerBus.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/EBus/EBus.h>
namespace AZStd
{
@@ -32,24 +32,7 @@ namespace AZStd
virtual void OnThreadExit(const AZStd::thread::id& id) = 0;
};
//! Thread events driller bus - only "drillers" (profilers) should connect to this.
//! A global mutex that includes a lock on the memory manager and other driller busses
//! is held during dispatch, and listeners are expected to do no allocation
//! or thread workloads or blocking or mutex operations of their own - only dump the data to
//! network or file ASAP.
//! DO NOT USE this bus unless you are a profiler capture system, use the ThreadEvents / ThreadBus instead
class ThreadDrillerEvents
: public AZ::Debug::DrillerEBusTraits
{
public:
/// Called when we enter a thread, optional thread_desc is provided when the use provides one.
virtual void OnThreadEnter(const AZStd::thread::id& id, const AZStd::thread_desc* desc) = 0;
/// Called when we exit a thread.
virtual void OnThreadExit(const AZStd::thread::id& id) = 0;
};
typedef AZ::EBus<ThreadEvents> ThreadEventBus;
typedef AZ::EBus<ThreadDrillerEvents> ThreadDrillerEventBus;
}
#endif // AZSTD_THREAD_BUS_H
@@ -8,7 +8,6 @@
#include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileIOEventBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Casting/numeric_cast.h>
@@ -87,7 +86,6 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags)
}
else
{
EBUS_EVENT(FileIOEventBus, OnError, this, nullptr, EINVAL);
return false;
}
@@ -103,7 +101,6 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags)
{
if (isApkFile)
{
EBUS_EVENT(FileIOEventBus, OnError, this, nullptr, ENOSPC);
return false;
}
@@ -125,7 +122,6 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags)
if (m_handle == PlatformSpecificInvalidHandle)
{
EBUS_EVENT(FileIOEventBus, OnError, this, nullptr, errorCode);
return false;
}
@@ -168,22 +164,8 @@ namespace Platform::Internal
entry = readdir(dir);
}
int lastError = errno;
if (lastError != 0)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, filter, lastError);
}
closedir(dir);
}
else
{
int lastError = errno;
if (lastError != ENOENT)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, filter, 0);
}
}
}
void FindFilesInApk(const char* filter, const SystemFile::FindFileCB& cb)
@@ -233,11 +215,7 @@ namespace Platform
{
if (handle != PlatformSpecificInvalidHandle)
{
off_t result = fseeko(handle, static_cast<off_t>(offset), mode);
if (result != 0)
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, errno);
}
fseeko(handle, static_cast<off_t>(offset), mode);
}
}
@@ -248,7 +226,6 @@ namespace Platform
off_t result = ftello(handle);
if (result == (off_t)-1)
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, errno);
return 0;
}
return aznumeric_cast<SizeType>(result);
@@ -292,7 +269,6 @@ namespace Platform
if (bytesRead != bytesToRead && ferror(handle))
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, errno);
return 0;
}
@@ -311,7 +287,6 @@ namespace Platform
if (bytesWritten != bytesToWrite && ferror(handle))
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, errno);
return 0;
}
@@ -325,10 +300,7 @@ namespace Platform
{
if (handle != PlatformSpecificInvalidHandle)
{
if (fflush(handle) != 0)
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, errno);
}
fflush(handle);
}
}
@@ -347,7 +319,6 @@ namespace Platform
struct stat fileStat;
if (stat(fileName, &fileStat) < 0)
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, 0);
return 0;
}
return static_cast<SizeType>(fileStat.st_size);
@@ -7,9 +7,9 @@
*/
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileIOEventBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/string/wildcard.h>
#include <../Common/UnixLike/AzCore/IO/Internal/SystemFileUtils_UnixLike.h>
@@ -44,21 +44,7 @@ namespace AZ::IO::Platform
entry = readdir(dir);
}
int lastError = errno;
if (lastError != 0)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, filter, lastError);
}
closedir(dir);
}
else
{
int lastError = errno;
if (lastError != ENOENT)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, filter, 0);
}
}
}
}
@@ -8,7 +8,6 @@
#include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileIOEventBus.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Debug/Profiler.h>
@@ -130,7 +129,6 @@ namespace Platform
int result = remove(fileName);
if (result != 0)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, fileName, result);
return false;
}
@@ -142,7 +140,6 @@ namespace Platform
int result = rename(sourceFileName, targetFileName);
if (result)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, sourceFileName, result);
return false;
}
@@ -198,10 +195,6 @@ namespace Platform
}
azstrcpy(dirPath, AZ_MAX_PATH_LEN, dirName);
bool success = CreateDirRecursive(dirPath);
if (!success)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, dirName, errno);
}
return success;
}
return false;
@@ -35,7 +35,6 @@ namespace AZStd
destroy_thread_info(ti);
ThreadEventBus::Broadcast(&ThreadEventBus::Events::OnThreadExit, this_thread::get_id());
ThreadDrillerEventBus::Broadcast(&ThreadDrillerEventBus::Events::OnThreadExit, this_thread::get_id());
pthread_exit(nullptr);
return nullptr;
}
@@ -88,7 +87,6 @@ namespace AZStd
Platform::PostCreateThread(tId, name, cpuId);
ThreadEventBus::Broadcast(&ThreadEventBus::Events::OnThreadEnter, thread::id(tId), desc);
ThreadDrillerEventBus::Broadcast(&ThreadDrillerEventBus::Events::OnThreadEnter, thread::id(tId), desc);
return tId;
}
}
@@ -8,7 +8,6 @@
#include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileIOEventBus.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/PlatformIncl.h>
@@ -61,7 +60,6 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags)
}
else
{
EBUS_EVENT(FileIOEventBus, OnError, this, nullptr, 0);
return false;
}
@@ -88,7 +86,6 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags)
if (m_handle == PlatformSpecificInvalidHandle)
{
EBUS_EVENT(FileIOEventBus, OnError, this, nullptr, errno);
return false;
}
else
@@ -119,11 +116,7 @@ namespace Platform
{
if (handle != PlatformSpecificInvalidHandle)
{
int result = lseek(handle, offset, mode);
if (result == -1)
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, errno);
}
lseek(handle, offset, mode);
}
}
@@ -132,10 +125,6 @@ namespace Platform
if (handle != PlatformSpecificInvalidHandle)
{
off_t result = lseek(handle, 0, SEEK_CUR);
if (result == (off_t)-1)
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, errno);
}
return aznumeric_cast<SizeType>(result);
}
@@ -149,14 +138,12 @@ namespace Platform
off_t current = lseek(handle, 0, SEEK_CUR);
if (current == (off_t)-1)
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, current);
return false;
}
off_t end = lseek(handle, 0, SEEK_END);
if (end == (off_t)-1)
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, end);
return false;
}
@@ -191,7 +178,6 @@ namespace Platform
ssize_t bytesRead = read(handle, buffer, byteSize);
if (bytesRead == -1)
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, errno);
return 0;
}
return bytesRead;
@@ -207,7 +193,6 @@ namespace Platform
ssize_t result = write(handle, buffer, byteSize);
if (result == -1)
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, errno);
return 0;
}
return result;
@@ -221,10 +206,7 @@ namespace Platform
if (handle != PlatformSpecificInvalidHandle)
{
#if AZ_TRAIT_SYSTEMFILE_FSYNC_IS_DEFINED
if (fsync(handle) != 0)
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, errno);
}
fsync(handle);
#endif
}
}
@@ -236,7 +218,6 @@ namespace Platform
struct stat stat;
if (fstat(handle, &stat) < 0)
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, 0);
return 0;
}
return stat.st_size;
@@ -11,7 +11,6 @@
#include <AzCore/base.h>
#include <AzCore/PlatformIncl.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/Debug/TraceMessagesDrillerBus.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/string/fixed_string.h>
@@ -186,8 +185,6 @@ namespace AZ::Debug
Debug::Trace::Instance().PrintCallstack(nullptr, 0, ExceptionInfo->ContextRecord);
EBUS_EVENT(Debug::TraceMessageDrillerBus, OnException, message);
bool result = false;
EBUS_EVENT_RESULT(result, Debug::TraceMessageBus, OnException, message);
if (result)
@@ -8,7 +8,6 @@
#include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileIOEventBus.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/std/string/conversions.h>
@@ -142,7 +141,6 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags)
if (m_handle == INVALID_HANDLE_VALUE)
{
EBUS_EVENT(FileIOEventBus, OnError, this, nullptr, (int)GetLastError());
return false;
}
else
@@ -160,10 +158,7 @@ void SystemFile::PlatformClose()
{
if (m_handle != PlatformSpecificInvalidHandle)
{
if (!CloseHandle(m_handle))
{
EBUS_EVENT(FileIOEventBus, OnError, this, nullptr, (int)GetLastError());
}
CloseHandle(m_handle);
m_handle = INVALID_HANDLE_VALUE;
}
}
@@ -177,7 +172,7 @@ namespace Platform
{
using FileHandleType = AZ::IO::SystemFile::FileHandleType;
void Seek(FileHandleType handle, const SystemFile* systemFile, SystemFile::SeekSizeType offset, SystemFile::SeekMode mode)
void Seek(FileHandleType handle, [[maybe_unused]] const SystemFile* systemFile, SystemFile::SeekSizeType offset, SystemFile::SeekMode mode)
{
if (handle != PlatformSpecificInvalidHandle)
{
@@ -185,14 +180,11 @@ namespace Platform
LARGE_INTEGER distToMove;
distToMove.QuadPart = offset;
if (!SetFilePointerEx(handle, distToMove, 0, dwMoveMethod))
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, (int)GetLastError());
}
SetFilePointerEx(handle, distToMove, 0, dwMoveMethod);
}
}
SystemFile::SizeType Tell(FileHandleType handle, const SystemFile* systemFile)
SystemFile::SizeType Tell(FileHandleType handle, [[maybe_unused]] const SystemFile* systemFile)
{
if (handle != PlatformSpecificInvalidHandle)
{
@@ -202,7 +194,6 @@ namespace Platform
LARGE_INTEGER newFilePtr;
if (!SetFilePointerEx(handle, distToMove, &newFilePtr, FILE_CURRENT))
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, (int)GetLastError());
return 0;
}
@@ -212,7 +203,7 @@ namespace Platform
return 0;
}
bool Eof(FileHandleType handle, const SystemFile* systemFile)
bool Eof(FileHandleType handle, [[maybe_unused]] const SystemFile* systemFile)
{
if (handle != PlatformSpecificInvalidHandle)
{
@@ -222,14 +213,12 @@ namespace Platform
LARGE_INTEGER currentFilePtr;
if (!SetFilePointerEx(handle, zero, &currentFilePtr, FILE_CURRENT))
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, (int)GetLastError());
return false;
}
FILE_STANDARD_INFO fileInfo;
if (!GetFileInformationByHandleEx(handle, FileStandardInfo, &fileInfo, sizeof(fileInfo)))
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, (int)GetLastError());
return false;
}
@@ -239,14 +228,13 @@ namespace Platform
return false;
}
AZ::u64 ModificationTime(FileHandleType handle, const SystemFile* systemFile)
AZ::u64 ModificationTime(FileHandleType handle, [[maybe_unused]] const SystemFile* systemFile)
{
if (handle != PlatformSpecificInvalidHandle)
{
FILE_BASIC_INFO fileInfo;
if (!GetFileInformationByHandleEx(handle, FileBasicInfo, &fileInfo, sizeof(fileInfo)))
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, (int)GetLastError());
return 0;
}
@@ -263,7 +251,7 @@ namespace Platform
return 0;
}
SystemFile::SizeType Read(FileHandleType handle, const SystemFile* systemFile, SizeType byteSize, void* buffer)
SystemFile::SizeType Read(FileHandleType handle, [[maybe_unused]] const SystemFile* systemFile, SizeType byteSize, void* buffer)
{
if (handle != PlatformSpecificInvalidHandle)
{
@@ -271,7 +259,6 @@ namespace Platform
DWORD nNumberOfBytesToRead = (DWORD)byteSize;
if (!ReadFile(handle, buffer, nNumberOfBytesToRead, &dwNumBytesRead, 0))
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, (int)GetLastError());
return 0;
}
return static_cast<SizeType>(dwNumBytesRead);
@@ -280,7 +267,7 @@ namespace Platform
return 0;
}
SystemFile::SizeType Write(FileHandleType handle, const SystemFile* systemFile, const void* buffer, SizeType byteSize)
SystemFile::SizeType Write(FileHandleType handle, [[maybe_unused]] const SystemFile* systemFile, const void* buffer, SizeType byteSize)
{
if (handle != PlatformSpecificInvalidHandle)
{
@@ -288,7 +275,6 @@ namespace Platform
DWORD nNumberOfBytesToWrite = (DWORD)byteSize;
if (!WriteFile(handle, buffer, nNumberOfBytesToWrite, &dwNumBytesWritten, 0))
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, (int)GetLastError());
return 0;
}
return static_cast<SizeType>(dwNumBytesWritten);
@@ -297,25 +283,21 @@ namespace Platform
return 0;
}
void Flush(FileHandleType handle, const SystemFile* systemFile)
void Flush(FileHandleType handle, [[maybe_unused]] const SystemFile* systemFile)
{
if (handle != PlatformSpecificInvalidHandle)
{
if (!FlushFileBuffers(handle))
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, (int)GetLastError());
}
FlushFileBuffers(handle);
}
}
SystemFile::SizeType Length(FileHandleType handle, const SystemFile* systemFile)
SystemFile::SizeType Length(FileHandleType handle, [[maybe_unused]] const SystemFile* systemFile)
{
if (handle != PlatformSpecificInvalidHandle)
{
LARGE_INTEGER size;
if (!GetFileSizeEx(handle, &size))
{
EBUS_EVENT(FileIOEventBus, OnError, systemFile, nullptr, (int)GetLastError());
return 0;
}
@@ -341,7 +323,6 @@ namespace Platform
{
WIN32_FIND_DATA fd;
HANDLE hFile;
int lastError;
AZ::IO::FixedMaxPathWString filterW;
AZStd::to_wstring(filterW, filter);
@@ -367,20 +348,7 @@ namespace Platform
cb(fileName, (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
}
lastError = (int)GetLastError();
FindClose(hFile);
if (lastError != ERROR_NO_MORE_FILES)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, fileName, lastError);
}
}
else
{
lastError = (int)GetLastError();
if (lastError != ERROR_FILE_NOT_FOUND)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, filter, lastError);
}
}
}
@@ -394,15 +362,11 @@ namespace Platform
if (handle == INVALID_HANDLE_VALUE)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, fileName, (int)GetLastError());
return 0;
}
FILE_BASIC_INFO fileInfo{};
if (!GetFileInformationByHandleEx(handle, FileBasicInfo, &fileInfo, sizeof(fileInfo)))
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, fileName, (int)GetLastError());
}
GetFileInformationByHandleEx(handle, FileBasicInfo, &fileInfo, sizeof(fileInfo));
CloseHandle(handle);
@@ -434,10 +398,6 @@ namespace Platform
fileSize.HighPart = data.nFileSizeHigh;
len = aznumeric_cast<SizeType>(fileSize.QuadPart);
}
else
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, fileName, (int)GetLastError());
}
return len;
}
@@ -448,7 +408,6 @@ namespace Platform
AZStd::to_wstring(fileNameW, fileName);
if (DeleteFileW(fileNameW.c_str()) == 0)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, fileName, (int)GetLastError());
return false;
}
@@ -463,7 +422,6 @@ namespace Platform
AZStd::to_wstring(targetFileNameW, targetFileName);
if (MoveFileExW(sourceFileNameW.c_str(), targetFileNameW.c_str(), overwrite ? MOVEFILE_REPLACE_EXISTING : 0) == 0)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, sourceFileName, (int)GetLastError());
return false;
}
@@ -503,10 +461,6 @@ namespace Platform
AZ::IO::FixedMaxPathWString dirNameW;
AZStd::to_wstring(dirNameW, dirName);
bool success = CreateDirRecursive(dirNameW);
if (!success)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, dirName, (int)GetLastError());
}
return success;
}
return false;
@@ -38,7 +38,6 @@ namespace AZStd
destroy_thread_info(ti);
ThreadEventBus::Broadcast(&ThreadEventBus::Events::OnThreadExit, this_thread::get_id()); // goes to client listeners
ThreadDrillerEventBus::Broadcast(&ThreadDrillerEventBus::Events::OnThreadExit, this_thread::get_id()); // goes to the profiler.
return Platform::PostThreadRun();
}
@@ -73,7 +72,6 @@ namespace AZStd
}
ThreadEventBus::Broadcast(&ThreadEventBus::Events::OnThreadEnter, thread::id(*id), desc);
ThreadDrillerEventBus::Broadcast(&ThreadDrillerEventBus::Events::OnThreadEnter, thread::id(*id), desc);
::ResumeThread(hThread);
@@ -7,9 +7,9 @@
*/
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileIOEventBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/string/wildcard.h>
#include <../Common/UnixLike/AzCore/IO/Internal/SystemFileUtils_UnixLike.h>
@@ -44,21 +44,7 @@ namespace AZ::IO::Platform
entry = readdir(dir);
}
int lastError = errno;
if (lastError != 0)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, filter, lastError);
}
closedir(dir);
}
else
{
int lastError = errno;
if (lastError != ENOENT)
{
EBUS_EVENT(FileIOEventBus, OnError, nullptr, filter, 0);
}
}
}
}
+9 -20
View File
@@ -1392,7 +1392,7 @@ namespace UnitTest
EXPECT_TRUE(done);
}
// Fixture for thread-driller-bus related calls
// Fixture for thread-event-bus related calls
// exists only to categorize the tests.
class ThreadEventsBus :
public AllocatorsFixture
@@ -1438,44 +1438,33 @@ namespace UnitTest
TEST_F(ThreadEventsBus, Broadcasts_BothBusses)
{
ThreadEventCounter<AZStd::ThreadEventBus::Handler> eventBusCounter;
ThreadEventCounter<AZStd::ThreadDrillerEventBus::Handler> drillerBusCounter;
auto thread_function = [&]()
{
; // intentionally left blank
};
eventBusCounter.Connect();
drillerBusCounter.Connect();
AZStd::thread starter = AZStd::thread(thread_function);
starter.join();
EXPECT_EQ(drillerBusCounter.m_enterCount, 1);
EXPECT_EQ(drillerBusCounter.m_exitCount, 1);
EXPECT_EQ(eventBusCounter.m_enterCount, 1);
EXPECT_EQ(eventBusCounter.m_exitCount, 1);
eventBusCounter.Disconnect();
drillerBusCounter.Disconnect();
}
// this class tests for deadlocks caused by interactions between the thread
// driller bus and the other driller busses.
// Client code (ie, not part of the driller system) can connec to the
// ThreadEventBus and be told when threads are started and stopped
// However, if they instead listen to the ThreadDrillerEventBus, a deadlock condition
// could be caused if they lock a mutex that another thread needs in order to proceed.
// This test makes sure that using the ThreadEventBus (ie, the one meant for client code)
// instead of the ThreadDrillerEventBus (the one meant only for profilers) does NOT cause
// a deadlock.
// This class tests for deadlocks caused by multiple threads interacting with the ThreadEventBus.
// Client code can connect to the ThreadEventBus and be told when threads are started and stopped.
// A deadlock condition could be caused if they lock a mutex that another thread needs in order to proceed.
// This test makes sure that using the ThreadEventBus does NOT cause a deadlock.
// We will simulate this series of events by doing the following
// 1. Main thread listens on the ThreadEventBus
// 2. OnThreadExit will lock a mutex, perform an allocation, unlock a mutex
// 3. The thread itself will lock the mutex, perform an allocation, unlock the mutex.
// As long as there is no cross talk between the client and the driller busses, the
// above operation should not deadlock.
// but if there is, then a deadlock can occur where one thread will be unable to perform
// its allocation because the other is in OnThreadExit()
// and the other will not be able to perform OnThreadExit() because it cannot lock the mutex.
// As long as there is no cross talk between threads, the above operation should not deadlock.
// If there is, then a deadlock can occur where one thread will be unable to perform
// its allocation because the other is in OnThreadExit() and the other will not be able to perform
// OnThreadExit() because it cannot lock the mutex.
class ThreadEventsDeathTest :
public AllocatorsFixture

Some files were not shown because too many files have changed in this diff Show More