Merge remote-tracking branch 'upstream/development' into nvsickle/GenericDomDocument

This commit is contained in:
Nicholas Van Sickle
2021-12-20 11:51:15 -08:00
328 changed files with 119945 additions and 6470 deletions
+3 -1
View File
@@ -538,6 +538,7 @@ void LevelEditorMenuHandler::PopulateEditMenu(ActionManager::MenuWrapper& editMe
auto snapMenu = modifyMenu.AddMenu(tr("Snap"));
snapMenu.AddAction(AzToolsFramework::SnapAngle);
snapMenu.AddAction(AzToolsFramework::SnapToGrid);
auto transformModeMenu = modifyMenu.AddMenu(tr("Transform Mode"));
transformModeMenu.AddAction(AzToolsFramework::EditModeMove);
@@ -723,7 +724,8 @@ QMenu* LevelEditorMenuHandler::CreateViewMenu()
// MISSING AVIRECORDER
viewportViewsMenuWrapper.AddSeparator();
viewportViewsMenuWrapper.AddAction(ID_DISPLAY_SHOWHELPERS);
viewportViewsMenuWrapper.AddAction(AzToolsFramework::Helpers);
viewportViewsMenuWrapper.AddAction(AzToolsFramework::Icons);
// Refresh Style
viewMenu.AddAction(ID_SKINS_REFRESH);
-7
View File
@@ -445,7 +445,6 @@ void CCryEditApp::RegisterActionHandlers()
ON_COMMAND(ID_OPEN_ASSET_BROWSER, OnOpenAssetBrowserView)
ON_COMMAND(ID_OPEN_AUDIO_CONTROLS_BROWSER, OnOpenAudioControlsEditor)
ON_COMMAND(ID_DISPLAY_SHOWHELPERS, OnShowHelpers)
ON_COMMAND(ID_OPEN_TRACKVIEW, OnOpenTrackView)
ON_COMMAND(ID_OPEN_UICANVASEDITOR, OnOpenUICanvasEditor)
@@ -2617,12 +2616,6 @@ void CCryEditApp::OnUpdateSelected(QAction* action)
action->setEnabled(!GetIEditor()->GetSelection()->IsEmpty());
}
void CCryEditApp::OnShowHelpers()
{
GetIEditor()->GetDisplaySettings()->DisplayHelpers(!GetIEditor()->GetDisplaySettings()->IsDisplayHelpers());
GetIEditor()->Notify(eNotify_OnDisplayRenderUpdate);
}
//////////////////////////////////////////////////////////////////////////
void CCryEditApp::OnEditLevelData()
{
-1
View File
@@ -237,7 +237,6 @@ public:
void OnSyncPlayerUpdate(QAction* action);
void OnResourcesReduceworkingset();
void OnDummyCommand() {};
void OnShowHelpers();
void OnFileSave();
void OnUpdateDocumentReady(QAction* action);
void OnUpdateFileOpen(QAction* action);
-2
View File
@@ -68,8 +68,6 @@ void CDisplaySettings::SetObjectHideMask(int hideMask)
m_objectHideMask = hideMask;
gSettings.objectHideMask = m_objectHideMask;
GetIEditor()->Notify(eNotify_OnDisplayRenderUpdate);
};
//////////////////////////////////////////////////////////////////////////
+13 -2
View File
@@ -46,6 +46,7 @@
#include <AzToolsFramework/API/EditorCameraBus.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/Viewport/ViewportSettings.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
@@ -1621,14 +1622,14 @@ Vec3 EditorViewportWidget::ViewToWorld(
auto ray = m_renderViewport->ViewportScreenToWorldRay(AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(vp));
const float maxDistance = 10000.f;
Vec3 v = AZVec3ToLYVec3(ray.direction) * maxDistance;
Vec3 v = AZVec3ToLYVec3(ray.m_direction) * maxDistance;
if (!_finite(v.x) || !_finite(v.y) || !_finite(v.z))
{
return Vec3(0, 0, 0);
}
Vec3 colp = AZVec3ToLYVec3(ray.origin) + 0.002f * v;
Vec3 colp = AZVec3ToLYVec3(ray.m_origin) + 0.002f * v;
return colp;
}
@@ -2420,6 +2421,16 @@ AZ::Vector3 EditorViewportSettings::DefaultEditorCameraPosition() const
return SandboxEditor::CameraDefaultEditorPosition();
}
bool EditorViewportSettings::IconsVisible() const
{
return AzToolsFramework::IconsVisible();
}
bool EditorViewportSettings::HelpersVisible() const
{
return AzToolsFramework::HelpersVisible();
}
AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once);
bool EditorViewportWidget::ShouldPreviewFullscreen() const
+2
View File
@@ -79,6 +79,8 @@ struct EditorViewportSettings : public AzToolsFramework::ViewportInteraction::Vi
float ManipulatorCircleBoundWidth() const override;
bool StickySelectEnabled() const override;
AZ::Vector3 DefaultEditorCameraPosition() const override;
bool IconsVisible() const override;
bool HelpersVisible() const override;
};
// EditorViewportWidget window
-2
View File
@@ -168,8 +168,6 @@ enum EEditorNotifyEvent
eNotify_OnVegetationObjectSelection, // When vegetation objects selection change.
eNotify_OnVegetationPanelUpdate, // When vegetation objects selection change.
eNotify_OnDisplayRenderUpdate, // Sent when editor finish terrain texture generation.
eNotify_OnDataBaseUpdate, // DataBase Library was modified.
eNotify_OnLayerImportBegin, //layer import was started
+65 -19
View File
@@ -47,6 +47,7 @@ AZ_POP_DISABLE_WARNING
#include <AzToolsFramework/API/EditorAnimationSystemRequestBus.h>
#include <AzToolsFramework/SourceControl/QtSourceControlNotificationHandler.h>
#include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
#include <AzToolsFramework/Viewport/ViewportSettings.h>
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
// AzQtComponents
@@ -445,11 +446,11 @@ void MainWindow::Initialize()
{
m_viewPaneManager->SetMainWindow(m_viewPaneHost, &m_settings, /*unused*/ QByteArray());
InitActions();
RegisterStdViewClasses();
InitCentralWidget();
InitActions();
// load toolbars ("shelves") and macros
GetIEditor()->GetToolBoxManager()->Load(m_actionManager);
@@ -798,27 +799,40 @@ void MainWindow::InitActions()
EditorTransformComponentSelectionRequests::Mode::Scale);
});
am->AddAction(AzToolsFramework::SnapToGrid, tr("Snap to grid"))
am->AddAction(AzToolsFramework::SnapToGrid, tr("Grid snapping"))
.SetIcon(Style::icon("Grid"))
.SetStatusTip(tr("Toggle grid snapping"))
.SetShortcut(tr("G"))
.SetToolTip(tr("Snap to grid (G)"))
.SetStatusTip(tr("Toggles snap to grid"))
.SetCheckable(true)
.RegisterUpdateCallback([](QAction* action) {
Q_ASSERT(action->isCheckable());
action->setChecked(SandboxEditor::GridSnappingEnabled());
})
.Connect(&QAction::triggered, []() { SandboxEditor::SetGridSnapping(!SandboxEditor::GridSnappingEnabled()); });
.RegisterUpdateCallback(
[](QAction* action)
{
Q_ASSERT(action->isCheckable());
action->setChecked(SandboxEditor::GridSnappingEnabled());
})
.Connect(
&QAction::triggered,
[]
{
SandboxEditor::SetGridSnapping(!SandboxEditor::GridSnappingEnabled());
});
am->AddAction(AzToolsFramework::SnapAngle, tr("Snap angle"))
am->AddAction(AzToolsFramework::SnapAngle, tr("Angle snapping"))
.SetIcon(Style::icon("Angle"))
.SetStatusTip(tr("Snap angle"))
.SetStatusTip(tr("Toggle angle snapping"))
.SetCheckable(true)
.RegisterUpdateCallback([](QAction* action) {
Q_ASSERT(action->isCheckable());
action->setChecked(SandboxEditor::AngleSnappingEnabled());
})
.Connect(&QAction::triggered, []() { SandboxEditor::SetAngleSnapping(!SandboxEditor::AngleSnappingEnabled()); });
.RegisterUpdateCallback(
[](QAction* action)
{
Q_ASSERT(action->isCheckable());
action->setChecked(SandboxEditor::AngleSnappingEnabled());
})
.Connect(
&QAction::triggered,
[]
{
SandboxEditor::SetAngleSnapping(!SandboxEditor::AngleSnappingEnabled());
});
// Display actions
am->AddAction(ID_SWITCHCAMERA_DEFAULTCAMERA, tr("Default Camera")).SetCheckable(true)
@@ -918,9 +932,41 @@ void MainWindow::InitActions()
.SetStatusTip(tr("Cycle 2D Viewport"))
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateNonGameMode);
#endif
am->AddAction(ID_DISPLAY_SHOWHELPERS, tr("Show/Hide Helpers"))
am->AddAction(AzToolsFramework::Helpers, tr("Show Helpers"))
.SetShortcut(tr("Shift+Space"))
.SetToolTip(tr("Show/Hide Helpers (Shift+Space)"));
.SetToolTip(tr("Show/Hide Helpers (Shift+Space)"))
.SetCheckable(true)
.RegisterUpdateCallback(
[](QAction* action)
{
Q_ASSERT(action->isCheckable());
action->setChecked(AzToolsFramework::HelpersVisible());
})
.Connect(
&QAction::triggered,
[]()
{
AzToolsFramework::SetHelpersVisible(!AzToolsFramework::HelpersVisible());
AzToolsFramework::ViewportInteraction::ViewportSettingsNotificationBus::Broadcast(
&AzToolsFramework::ViewportInteraction::ViewportSettingNotifications::OnDrawHelpersChanged,
AzToolsFramework::HelpersVisible());
});
am->AddAction(AzToolsFramework::Icons, tr("Show Icons"))
.SetShortcut(tr("Ctrl+Space"))
.SetToolTip(tr("Show/Hide Icons (Ctrl+Space)"))
.SetCheckable(true)
.RegisterUpdateCallback(
[](QAction* action)
{
Q_ASSERT(action->isCheckable());
action->setChecked(AzToolsFramework::IconsVisible());
})
.Connect(
&QAction::triggered,
[]()
{
AzToolsFramework::SetIconsVisible(!AzToolsFramework::IconsVisible());
});
// Audio actions
am->AddAction(ID_SOUND_STOPALLSOUNDS, tr("Stop All Sounds"))
@@ -1986,8 +1986,3 @@ void SandboxIntegrationManager::BrowseForAssets(AssetSelectionModel& selection)
{
AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, GetMainWindow());
}
bool SandboxIntegrationManager::DisplayHelpersVisible()
{
return GetIEditor()->GetDisplaySettings()->IsDisplayHelpers();
}
@@ -165,7 +165,6 @@ private:
void InstantiateSliceFromAssetId(const AZ::Data::AssetId& assetId) override;
void ClearRedoStack() override;
int GetIconTextureIdFromEntityIconPath(const AZStd::string& entityIconPath) override;
bool DisplayHelpersVisible() override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
-1
View File
@@ -191,7 +191,6 @@
#define ID_BRUSH_CSGSUBSTRUCT 33837
#define ID_MATERIAL_PICKTOOL 33842
#define ID_MODIFY_AIPOINT_PICKIMPASSLINK 33865
#define ID_DISPLAY_SHOWHELPERS 33871
#define ID_FILE_EXPORTSELECTION 33875
#define ID_EDIT_PASTE_WITH_LINKS 33893
#define ID_FILE_EXPORT_TERRAINAREA 33904
@@ -129,8 +129,8 @@ namespace SandboxEditor
ViewportInteractionRequestBus::EventResult(
ray, GetViewportId(), &ViewportInteractionRequestBus::Events::ViewportScreenToWorldRay, screenPoint);
m_mouseInteraction.m_mousePick.m_rayOrigin = ray.origin;
m_mouseInteraction.m_mousePick.m_rayDirection = ray.direction;
m_mouseInteraction.m_mousePick.m_rayOrigin = ray.m_origin;
m_mouseInteraction.m_mousePick.m_rayDirection = ray.m_direction;
m_mouseInteraction.m_mousePick.m_screenCoordinates = screenPoint;
}
+71 -52
View File
@@ -6,7 +6,6 @@
*
*/
// Description : CViewportTitleDlg implementation file
#if !defined(Q_MOC_RUN)
@@ -42,38 +41,18 @@
#include <AzCore/std/algorithm.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/Viewport/ViewportSettings.h>
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
#include <LmbrCentral/Audio/AudioSystemComponentBus.h>
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include "ui_ViewportTitleDlg.h"
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
#endif //!defined(Q_MOC_RUN)
#endif //! defined(Q_MOC_RUN)
// CViewportTitleDlg dialog
inline namespace Helpers
{
void ToggleHelpers()
{
const bool newValue = !GetIEditor()->GetDisplaySettings()->IsDisplayHelpers();
GetIEditor()->GetDisplaySettings()->DisplayHelpers(newValue);
GetIEditor()->Notify(eNotify_OnDisplayRenderUpdate);
if (newValue == false)
{
GetIEditor()->GetObjectManager()->SendEvent(EVENT_HIDE_HELPER);
}
AzToolsFramework::ViewportInteraction::ViewportSettingsNotificationBus::Broadcast(
&AzToolsFramework::ViewportInteraction::ViewportSettingNotifications::OnDrawHelpersChanged, newValue);
}
bool IsHelpersShown()
{
return GetIEditor()->GetDisplaySettings()->IsDisplayHelpers();
}
}
namespace
{
class CViewportTitleDlgDisplayInfoHelper
@@ -98,7 +77,7 @@ namespace
emit ViewportInfoStatusUpdated(static_cast<int>(state));
}
};
} //end anonymous namespace
} // end anonymous namespace
CViewportTitleDlg::CViewportTitleDlg(QWidget* pParent)
: QWidget(pParent)
@@ -215,13 +194,65 @@ void CViewportTitleDlg::SetupViewportInformationMenu()
m_ui->m_debugInformationMenu->setMenu(GetViewportInformationMenu());
connect(m_ui->m_debugInformationMenu, &QToolButton::clicked, this, &CViewportTitleDlg::OnToggleDisplayInfo);
m_ui->m_debugInformationMenu->setPopupMode(QToolButton::MenuButtonPopup);
}
void CViewportTitleDlg::SetupHelpersButton()
{
connect(m_ui->m_helpers, &QToolButton::clicked, this, &CViewportTitleDlg::OnToggleHelpers);
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
if (m_helpersMenu == nullptr)
{
m_helpersMenu = new QMenu("Helpers State", this);
auto helperAction = MainWindow::instance()->GetActionManager()->GetAction(AzToolsFramework::Helpers);
connect(
helperAction, &QAction::triggered, this,
[this]
{
m_ui->m_helpers->setChecked(AzToolsFramework::HelpersVisible() || AzToolsFramework::IconsVisible());
});
auto iconAction = MainWindow::instance()->GetActionManager()->GetAction(AzToolsFramework::Icons);
connect(
iconAction, &QAction::triggered, this,
[this]
{
m_ui->m_helpers->setChecked(AzToolsFramework::HelpersVisible() || AzToolsFramework::IconsVisible());
});
m_helpersAction = new QAction(tr("Helpers"), m_helpersMenu);
m_helpersAction->setCheckable(true);
connect(
m_helpersAction, &QAction::triggered, this,
[helperAction]
{
helperAction->trigger();
});
m_iconsAction = new QAction(tr("Icons"), m_helpersMenu);
m_iconsAction->setCheckable(true);
connect(
m_iconsAction, &QAction::triggered, this,
[iconAction]
{
iconAction->trigger();
});
m_helpersMenu->addAction(m_helpersAction);
m_helpersMenu->addAction(m_iconsAction);
connect(
m_helpersMenu, &QMenu::aboutToShow, this,
[this]
{
m_helpersAction->setChecked(AzToolsFramework::HelpersVisible());
m_iconsAction->setChecked(AzToolsFramework::IconsVisible());
});
m_ui->m_helpers->setCheckable(true);
m_ui->m_helpers->setMenu(m_helpersMenu);
m_ui->m_helpers->setPopupMode(QToolButton::InstantPopup);
}
m_ui->m_helpers->setChecked(AzToolsFramework::HelpersVisible() || AzToolsFramework::IconsVisible());
}
void CViewportTitleDlg::SetupOverflowMenu()
@@ -279,15 +310,20 @@ void CViewportTitleDlg::SetupOverflowMenu()
UpdateMuteActionText();
}
//////////////////////////////////////////////////////////////////////////
void CViewportTitleDlg::SetViewPane(CLayoutViewPane* pViewPane)
{
if (m_pViewPane)
{
m_pViewPane->disconnect(this);
}
m_pViewPane = pViewPane;
if (m_pViewPane)
{
connect(this, &QWidget::customContextMenuRequested, m_pViewPane, &CLayoutViewPane::ShowTitleMenu);
}
}
//////////////////////////////////////////////////////////////////////////
@@ -318,7 +354,6 @@ void CViewportTitleDlg::OnInitDialog()
m_ui->m_prefabFocusPath->hide();
m_ui->m_prefabFocusBackButton->hide();
}
}
//////////////////////////////////////////////////////////////////////////
@@ -336,13 +371,6 @@ void CViewportTitleDlg::OnMaximize()
}
}
//////////////////////////////////////////////////////////////////////////
void CViewportTitleDlg::OnToggleHelpers()
{
Helpers::ToggleHelpers();
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
}
void CViewportTitleDlg::SetNoViewportInfo()
{
AZ::AtomBridge::AtomViewportInfoDisplayRequestBus::Broadcast(
@@ -367,7 +395,6 @@ void CViewportTitleDlg::SetCompactViewportInfo()
&AZ::AtomBridge::AtomViewportInfoDisplayRequestBus::Events::SetDisplayState, AZ::AtomBridge::ViewportInfoDisplayState::CompactInfo);
}
//////////////////////////////////////////////////////////////////////////
void CViewportTitleDlg::UpdateDisplayInfo()
{
@@ -783,9 +810,6 @@ void CViewportTitleDlg::OnEditorNotifyEvent(EEditorNotifyEvent event)
{
switch (event)
{
case eNotify_OnDisplayRenderUpdate:
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
break;
case eNotify_OnBeginGameMode:
case eNotify_OnEndGameMode:
UpdateMuteActionText();
@@ -997,24 +1021,18 @@ void CViewportTitleDlg::UpdateOverFlowMenuState()
m_angleSizeActionWidget->setEnabled(angleSnappingActive);
}
namespace
namespace
{
void PyToggleHelpers()
{
GetIEditor()->GetDisplaySettings()->DisplayHelpers(!GetIEditor()->GetDisplaySettings()->IsDisplayHelpers());
GetIEditor()->Notify(eNotify_OnDisplayRenderUpdate);
if (GetIEditor()->GetDisplaySettings()->IsDisplayHelpers() == false)
{
GetIEditor()->GetObjectManager()->SendEvent(EVENT_HIDE_HELPER);
}
AzToolsFramework::SetHelpersVisible(!AzToolsFramework::HelpersVisible());
}
bool PyIsHelpersShown()
{
return GetIEditor()->GetDisplaySettings()->IsDisplayHelpers();
return AzToolsFramework::HelpersVisible();
}
}
} // namespace
namespace AzToolsFramework
{
@@ -1029,11 +1047,12 @@ namespace AzToolsFramework
->Attribute(AZ::Script::Attributes::Category, "Legacy/Editor")
->Attribute(AZ::Script::Attributes::Module, "legacy.general");
};
addLegacyGeneral(behaviorContext->Method("toggle_helpers", PyToggleHelpers, nullptr, "Toggles the display of helpers."));
addLegacyGeneral(behaviorContext->Method("is_helpers_shown", PyIsHelpersShown, nullptr, "Gets the display state of helpers."));
}
}
}
} // namespace AzToolsFramework
#include "ViewportTitleDlg.moc"
#include <moc_ViewportTitleDlg.cpp>
+3 -1
View File
@@ -80,7 +80,6 @@ protected:
void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam) override;
void OnMaximize();
void OnToggleHelpers();
void UpdateDisplayInfo();
void SetupCameraDropdownMenu();
@@ -153,6 +152,9 @@ protected:
QMenu* m_aspectMenu = nullptr;
QMenu* m_resolutionMenu = nullptr;
QMenu* m_viewportInformationMenu = nullptr;
QMenu* m_helpersMenu = nullptr;
QAction* m_helpersAction = nullptr;
QAction* m_iconsAction = nullptr;
QAction* m_noInformationAction = nullptr;
QAction* m_normalInformationAction = nullptr;
QAction* m_fullInformationAction = nullptr;
@@ -254,13 +254,13 @@ namespace AZ
Method("CreateFromMatrix3x3", &Quaternion::CreateFromMatrix3x3)->
Method("CreateFromMatrix4x4", &Quaternion::CreateFromMatrix4x4)->
Method("CreateFromAxisAngle", &Quaternion::CreateFromAxisAngle)->
Method("CreateFromScaledAxisAngle", &Quaternion::CreateFromScaledAxisAngle)->
Method("CreateShortestArc", &Quaternion::CreateShortestArc)->
Method("CreateFromEulerAnglesDegrees", &Quaternion::CreateFromEulerAnglesDegrees)
;
}
}
Quaternion Quaternion::CreateFromMatrix3x3(const Matrix3x3& m)
{
return CreateFromBasis(m.GetBasisX(), m.GetBasisY(), m.GetBasisZ());
@@ -430,4 +430,24 @@ namespace AZ
outAngle = 0.0f;
}
}
Vector3 Quaternion::ConvertToScaledAxisAngle() const
{
// Take the log of the quaternion to convert it to the exponential map
// and multiply it by 2.0 to bring it into the scaled axis-angle representation.
const AZ::Vector3 imaginary = GetImaginary();
const float length = imaginary.GetLength();
if (length < AZ::Constants::FloatEpsilon)
{
return imaginary * 2.0f;
}
else
{
const float halfAngle = acosf(AZ::GetClamp(GetW(), -1.0f, 1.0f));
// Multiply by 2.0 to convert the half angle into the full one.
return halfAngle * 2.0f * (imaginary / length);
}
}
}
@@ -77,6 +77,9 @@ namespace AZ
static Quaternion CreateFromAxisAngle(const Vector3& axis, float angle);
//! Create a quaternion from a scaled axis-angle representation.
static Quaternion CreateFromScaledAxisAngle(const Vector3& scaledAxisAngle);
static Quaternion CreateShortestArc(const Vector3& v1, const Vector3& v2);
//! Creates a quaternion using rotation in degrees about the axes. First rotated about the X axis, followed by the Y axis, then the Z axis.
@@ -231,6 +234,9 @@ namespace AZ
//! @param[out] outAngle A float rotation angle around the axis in radians.
void ConvertToAxisAngle(Vector3& outAxis, float& outAngle) const;
//! Convert the quaternion into scaled axis-angle representation.
Vector3 ConvertToScaledAxisAngle() const;
//! Returns the imaginary (X/Y/Z) portion of the quaternion.
Vector3 GetImaginary() const;
@@ -109,6 +109,24 @@ namespace AZ
}
AZ_MATH_INLINE Quaternion Quaternion::CreateFromScaledAxisAngle(const Vector3& scaledAxisAngle)
{
const AZ::Vector3 exponentialMap = scaledAxisAngle / 2.0f;
const float halfAngle = exponentialMap.GetLength();
if (halfAngle < AZ::Constants::FloatEpsilon)
{
return AZ::Quaternion::CreateFromVector3AndValue(exponentialMap, 1.0f).GetNormalized();
}
else
{
float sin, cos;
SinCos(halfAngle, sin, cos);
return AZ::Quaternion::CreateFromVector3AndValue((sin / halfAngle) * exponentialMap, cos);
}
}
AZ_MATH_INLINE void Quaternion::StoreToFloat4(float* values) const
{
Simd::Vec4::StoreUnaligned(values, m_value);
@@ -16,438 +16,421 @@
#include <AzCore/Debug/StackTracer.h>
using namespace AZ;
using namespace AZ::Debug;
namespace AZ::Debug
{
// Many PC tools break with alloc/free size mismatches when the memory guard is enabled. Disable for now
//#define ENABLE_MEMORY_GUARD
// Many PC tools break with alloc/free size mismatches when the memory guard is enabled. Disable for now
//#define ENABLE_MEMORY_GUARD
//=========================================================================
// AllocationRecords
// [9/16/2009]
//=========================================================================
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)
//=========================================================================
// AllocationRecords
// [9/16/2009]
//=========================================================================
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)
, m_memoryGuardSize(isMemoryGuard ? sizeof(Debug::GuardValue) : 0)
#else
, m_memoryGuardSize(0)
, m_memoryGuardSize(0)
#endif
, m_requestedAllocs(0)
, m_requestedBytes(0)
, m_requestedBytesPeak(0)
, m_allocatorName(allocatorName)
{
}
//=========================================================================
// ~AllocationRecords
// [9/16/2009]
//=========================================================================
AllocationRecords::~AllocationRecords()
{
if (!AllocatorManager::Instance().m_isAllocatorLeaking)
, m_requestedAllocs(0)
, m_requestedBytes(0)
, m_requestedBytesPeak(0)
, m_allocatorName(allocatorName)
{
// dump all allocation (we should not have any at this point).
bool includeNameAndFilename = (m_saveNames || m_mode == RECORD_FULL);
EnumerateAllocations(PrintAllocationsCB(true, includeNameAndFilename));
AZ_Error("Memory", m_records.empty(), "We still have %d allocations on record! They must be freed prior to destroy!", m_records.size());
}
}
//=========================================================================
// lock
// [9/16/2009]
//=========================================================================
void
AllocationRecords::lock()
{
m_recordsMutex.lock();
}
//=========================================================================
// try_lock
// [9/16/2009]
//=========================================================================
bool AllocationRecords::try_lock()
{
return m_recordsMutex.try_lock();
}
//=========================================================================
// unlock
// [9/16/2009]
//=========================================================================
void
AllocationRecords::unlock()
{
m_recordsMutex.unlock();
}
//=========================================================================
// RegisterAllocation
// [9/11/2009]
//=========================================================================
const AllocationInfo*
AllocationRecords::RegisterAllocation(void* address, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, unsigned int stackSuppressCount)
{
(void)stackSuppressCount;
if (m_mode == RECORD_NO_RECORDS)
{
return nullptr;
}
if (address == nullptr)
{
return nullptr;
}
// memory guard
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
//=========================================================================
// ~AllocationRecords
// [9/16/2009]
//=========================================================================
AllocationRecords::~AllocationRecords()
{
if (m_isAutoIntegrityCheck)
if (!AllocatorManager::Instance().m_isAllocatorLeaking)
{
IntegrityCheck();
// dump all allocation (we should not have any at this point).
bool includeNameAndFilename = (m_saveNames || m_mode == RECORD_FULL);
EnumerateAllocations(PrintAllocationsCB(true, includeNameAndFilename));
AZ_Error(
"Memory", m_records.empty(), "We still have %d allocations on record! They must be freed prior to destroy!",
m_records.size());
}
}
//=========================================================================
// lock
// [9/16/2009]
//=========================================================================
void AllocationRecords::lock()
{
m_recordsMutex.lock();
}
//=========================================================================
// try_lock
// [9/16/2009]
//=========================================================================
bool AllocationRecords::try_lock()
{
return m_recordsMutex.try_lock();
}
//=========================================================================
// unlock
// [9/16/2009]
//=========================================================================
void AllocationRecords::unlock()
{
m_recordsMutex.unlock();
}
//=========================================================================
// RegisterAllocation
// [9/11/2009]
//=========================================================================
const AllocationInfo* AllocationRecords::RegisterAllocation(
void* address,
size_t byteSize,
size_t alignment,
const char* name,
const char* fileName,
int lineNum,
unsigned int stackSuppressCount)
{
(void)stackSuppressCount;
if (m_mode == RECORD_NO_RECORDS)
{
return nullptr;
}
if (address == nullptr)
{
return nullptr;
}
AZ_Assert(byteSize>sizeof(Debug::GuardValue), "Did you forget to add the extra MemoryGuardSize() bytes?");
byteSize -= sizeof(Debug::GuardValue);
new(reinterpret_cast<char*>(address)+byteSize) Debug::GuardValue();
}
Debug::AllocationRecordsType::pair_iter_bool iterBool;
{
AZStd::scoped_lock lock(m_recordsMutex);
iterBool = m_records.insert_key(address);
}
if (!iterBool.second)
{
// If that memory address was already registered, print the stack trace of the previous registration
PrintAllocationsCB(true, (m_saveNames || m_mode == RECORD_FULL))(address, iterBool.first->second, m_numStackLevels);
AZ_Assert(iterBool.second, "Memory address 0x%p is already allocated and in the records!", address);
}
Debug::AllocationInfo& ai = iterBool.first->second;
ai.m_byteSize = byteSize;
ai.m_alignment = static_cast<unsigned int>(alignment);
if ((m_saveNames || m_mode == RECORD_FULL) && name && fileName)
{
// In RECORD_FULL mode or when specifically enabled in app descriptor with
// m_allocationRecordsSaveNames, we allocate our own memory to save off name and fileName.
// When testing for memory leaks, on process shutdown AllocationRecords::~AllocationRecords
// gets called to enumerate the remaining (leaked) allocations. Unfortunately, any names
// referenced in dynamic module memory whose modules are unloaded won't be valid
// references anymore and we won't get useful information from the enumeration print.
// This code block ensures we keep our name/fileName valid for when we need it.
const size_t nameLength = strlen(name);
const size_t fileNameLength = strlen(fileName);
const size_t totalLength = nameLength + fileNameLength + 2; // + 2 for terminating null characters
ai.m_namesBlock = m_records.get_allocator().allocate(totalLength, 1);
ai.m_namesBlockSize = totalLength;
char* savedName = reinterpret_cast<char*>(ai.m_namesBlock);
char* savedFileName = savedName + nameLength + 1;
memcpy(reinterpret_cast<void*>(savedName), reinterpret_cast<const void*>(name), nameLength + 1);
memcpy(reinterpret_cast<void*>(savedFileName), reinterpret_cast<const void*>(fileName), fileNameLength + 1);
ai.m_name = savedName;
ai.m_fileName = savedFileName;
}
else
{
ai.m_name = name;
ai.m_fileName = fileName;
ai.m_namesBlock = nullptr;
ai.m_namesBlockSize = 0;
}
ai.m_lineNum = lineNum;
ai.m_timeStamp = AZStd::GetTimeNowMicroSecond();
// if we don't have a fileName,lineNum record the stack or if the user requested it.
if ((fileName == nullptr && m_mode == RECORD_STACK_IF_NO_FILE_LINE) || m_mode == RECORD_FULL)
{
ai.m_stackFrames = m_numStackLevels ? reinterpret_cast<AZ::Debug::StackFrame*>(m_records.get_allocator().allocate(sizeof(AZ::Debug::StackFrame)*m_numStackLevels, 1)) : nullptr;
if (ai.m_stackFrames)
// memory guard
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
{
Debug::StackRecorder::Record(ai.m_stackFrames, m_numStackLevels, stackSuppressCount + 1);
if (m_decodeImmediately)
if (m_isAutoIntegrityCheck)
{
// OPTIONAL DEBUGGING CODE - enable in app descriptor m_allocationRecordsAttemptDecodeImmediately
// This is optionally-enabled code for tracking down memory allocations
// that fail to be decoded. DecodeFrames() typically runs at the end of
// your application when leaks were found. Sometimes you have stack prints
// full of "(module-name not available)" and "(function-name not available)"
// that are not actionable. If you have those, enable this code. It'll slow
// down your process significantly because for every allocation recorded
// we get the stack trace on the spot. Put a breakpoint in DecodeFrames()
// at the "(module-name not available)" and "(function-name not available)"
// locations and now at the moment those allocations happen you'll have the
// full stack trace available and the ability to debug what could be causing it
IntegrityCheck();
}
AZ_Assert(byteSize > sizeof(Debug::GuardValue), "Did you forget to add the extra MemoryGuardSize() bytes?");
byteSize -= sizeof(Debug::GuardValue);
new (reinterpret_cast<char*>(address) + byteSize) Debug::GuardValue();
}
Debug::AllocationRecordsType::pair_iter_bool iterBool;
{
AZStd::scoped_lock lock(m_recordsMutex);
iterBool = m_records.insert_key(address);
}
if (!iterBool.second)
{
// If that memory address was already registered, print the stack trace of the previous registration
PrintAllocationsCB(true, (m_saveNames || m_mode == RECORD_FULL))(address, iterBool.first->second, m_numStackLevels);
AZ_Assert(iterBool.second, "Memory address 0x%p is already allocated and in the records!", address);
}
Debug::AllocationInfo& ai = iterBool.first->second;
ai.m_byteSize = byteSize;
ai.m_alignment = static_cast<unsigned int>(alignment);
if ((m_saveNames || m_mode == RECORD_FULL) && name && fileName)
{
// In RECORD_FULL mode or when specifically enabled in app descriptor with
// m_allocationRecordsSaveNames, we allocate our own memory to save off name and fileName.
// When testing for memory leaks, on process shutdown AllocationRecords::~AllocationRecords
// gets called to enumerate the remaining (leaked) allocations. Unfortunately, any names
// referenced in dynamic module memory whose modules are unloaded won't be valid
// references anymore and we won't get useful information from the enumeration print.
// This code block ensures we keep our name/fileName valid for when we need it.
const size_t nameLength = strlen(name);
const size_t fileNameLength = strlen(fileName);
const size_t totalLength = nameLength + fileNameLength + 2; // + 2 for terminating null characters
ai.m_namesBlock = m_records.get_allocator().allocate(totalLength, 1);
ai.m_namesBlockSize = totalLength;
char* savedName = reinterpret_cast<char*>(ai.m_namesBlock);
char* savedFileName = savedName + nameLength + 1;
memcpy(reinterpret_cast<void*>(savedName), reinterpret_cast<const void*>(name), nameLength + 1);
memcpy(reinterpret_cast<void*>(savedFileName), reinterpret_cast<const void*>(fileName), fileNameLength + 1);
ai.m_name = savedName;
ai.m_fileName = savedFileName;
}
else
{
ai.m_name = name;
ai.m_fileName = fileName;
ai.m_namesBlock = nullptr;
ai.m_namesBlockSize = 0;
}
ai.m_lineNum = lineNum;
ai.m_timeStamp = AZStd::GetTimeNowMicroSecond();
// if we don't have a fileName,lineNum record the stack or if the user requested it.
if ((fileName == nullptr && m_mode == RECORD_STACK_IF_NO_FILE_LINE) || m_mode == RECORD_FULL)
{
ai.m_stackFrames = m_numStackLevels ? reinterpret_cast<AZ::Debug::StackFrame*>(m_records.get_allocator().allocate(
sizeof(AZ::Debug::StackFrame) * m_numStackLevels, 1))
: nullptr;
if (ai.m_stackFrames)
{
Debug::StackRecorder::Record(ai.m_stackFrames, m_numStackLevels, stackSuppressCount + 1);
if (m_decodeImmediately)
{
const unsigned char decodeStep = 40;
Debug::SymbolStorage::StackLine lines[decodeStep];
unsigned char iFrame = 0;
unsigned char numStackLevels = m_numStackLevels;
while (numStackLevels > 0)
// OPTIONAL DEBUGGING CODE - enable in app descriptor m_allocationRecordsAttemptDecodeImmediately
// This is optionally-enabled code for tracking down memory allocations
// that fail to be decoded. DecodeFrames() typically runs at the end of
// your application when leaks were found. Sometimes you have stack prints
// full of "(module-name not available)" and "(function-name not available)"
// that are not actionable. If you have those, enable this code. It'll slow
// down your process significantly because for every allocation recorded
// we get the stack trace on the spot. Put a breakpoint in DecodeFrames()
// at the "(module-name not available)" and "(function-name not available)"
// locations and now at the moment those allocations happen you'll have the
// full stack trace available and the ability to debug what could be causing it
{
unsigned char numToDecode = AZStd::GetMin(decodeStep, numStackLevels);
Debug::SymbolStorage::DecodeFrames(&ai.m_stackFrames[iFrame], numToDecode, lines);
numStackLevels -= numToDecode;
iFrame += numToDecode;
const unsigned char decodeStep = 40;
Debug::SymbolStorage::StackLine lines[decodeStep];
unsigned char iFrame = 0;
unsigned char numStackLevels = m_numStackLevels;
while (numStackLevels > 0)
{
unsigned char numToDecode = AZStd::GetMin(decodeStep, numStackLevels);
Debug::SymbolStorage::DecodeFrames(&ai.m_stackFrames[iFrame], numToDecode, lines);
numStackLevels -= numToDecode;
iFrame += numToDecode;
}
}
}
}
}
AllocatorManager::Instance().DebugBreak(address, ai);
// statistics
m_requestedBytes += byteSize;
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;
}
AllocatorManager::Instance().DebugBreak(address, ai);
// statistics
m_requestedBytes += byteSize;
size_t currentRequestedBytePeak;
size_t newRequestedBytePeak;
do
//=========================================================================
// UnregisterAllocation
// [9/11/2009]
//=========================================================================
void AllocationRecords::UnregisterAllocation(void* address, size_t byteSize, size_t alignment, AllocationInfo* info)
{
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;
}
//=========================================================================
// UnregisterAllocation
// [9/11/2009]
//=========================================================================
void AllocationRecords::UnregisterAllocation(void* address, size_t byteSize, size_t alignment, AllocationInfo* info)
{
if (m_mode == RECORD_NO_RECORDS)
{
return;
}
if (address == nullptr)
{
return;
}
AllocationInfo allocationInfo;
{
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())
if (m_mode == RECORD_NO_RECORDS)
{
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)
if (address == nullptr)
{
m_records.rehash(0);
return;
}
}
AllocatorManager::Instance().DebugBreak(address, allocationInfo);
(void)byteSize;
(void)alignment;
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 -= allocationInfo.m_byteSize;
#if defined(ENABLE_MEMORY_GUARD)
// memory guard
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
{
if (m_isAutoIntegrityCheck)
{
// full integrity check
IntegrityCheck();
}
else
{
// check current allocation
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, allocationInfo, m_numStackLevels);
AZ_Assert(false, "MEMORY STOMP DETECTED!!!");
}
guard->~GuardValue();
}
}
#endif
// delete allocation record
if (allocationInfo.m_namesBlock)
{
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 (allocationInfo.m_stackFrames)
{
m_records.get_allocator().deallocate(allocationInfo.m_stackFrames, sizeof(AZ::Debug::StackFrame)*m_numStackLevels, 1);
allocationInfo.m_stackFrames = nullptr;
}
if (info)
{
*info = allocationInfo;
}
// if requested set memory to a specific value.
if (m_isMarkUnallocatedMemory)
{
memset(address, GetUnallocatedMarkValue(), byteSize);
}
}
//=========================================================================
// ResizeAllocation
// [9/20/2009]
//=========================================================================
void
AllocationRecords::ResizeAllocation(void* address, size_t newSize)
{
if (m_mode == RECORD_NO_RECORDS)
{
return;
}
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))
{
if (m_isAutoIntegrityCheck)
{
// full integrity check
IntegrityCheck();
}
else
{
// check memory guard
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);
AZ_Assert(false, "MEMORY STOMP DETECTED!!!");
}
guard->~GuardValue();
}
// init the new memory guard
newSize -= sizeof(Debug::GuardValue);
new(reinterpret_cast<char*>(address)+newSize) Debug::GuardValue();
}
#endif
// statistics
m_requestedBytes -= allocationInfo->m_byteSize;
m_requestedBytes += newSize;
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
allocationInfo->m_byteSize = newSize;
}
//=========================================================================
// EnumerateAllocations
// [9/29/2009]
//=========================================================================
void
AllocationRecords::SetMode(Mode mode)
{
if (mode == RECORD_NO_RECORDS)
{
AllocationInfo allocationInfo;
{
AZStd::scoped_lock lock(m_recordsMutex);
m_records.clear();
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);
}
}
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!");
AllocatorManager::Instance().DebugBreak(address, allocationInfo);
m_mode = mode;
}
(void)byteSize;
(void)alignment;
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!");
//=========================================================================
// EnumerateAllocations
// [9/29/2009]
//=========================================================================
void
AllocationRecords::EnumerateAllocations(AllocationInfoCBType cb)
{
// 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.
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))
{
break;
}
}
}
// statistics
m_requestedBytes -= allocationInfo.m_byteSize;
//=========================================================================
// IntegrityCheck
// [9/9/2011]
//=========================================================================
void
AllocationRecords::IntegrityCheck() const
{
#if defined(ENABLE_MEMORY_GUARD)
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
// memory guard
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
{
if (m_isAutoIntegrityCheck)
{
// full integrity check
IntegrityCheck();
}
else
{
// check current allocation
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, allocationInfo, m_numStackLevels);
AZ_Assert(false, "MEMORY STOMP DETECTED!!!");
}
guard->~GuardValue();
}
}
#endif
// delete allocation record
if (allocationInfo.m_namesBlock)
{
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 (allocationInfo.m_stackFrames)
{
m_records.get_allocator().deallocate(allocationInfo.m_stackFrames, sizeof(AZ::Debug::StackFrame) * m_numStackLevels, 1);
allocationInfo.m_stackFrames = nullptr;
}
if (info)
{
*info = allocationInfo;
}
// if requested set memory to a specific value.
if (m_isMarkUnallocatedMemory)
{
memset(address, GetUnallocatedMarkValue(), byteSize);
}
}
//=========================================================================
// ResizeAllocation
// [9/20/2009]
//=========================================================================
void AllocationRecords::ResizeAllocation(void* address, size_t newSize)
{
if (m_mode == RECORD_NO_RECORDS)
{
return;
}
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))
{
if (m_isAutoIntegrityCheck)
{
// full integrity check
IntegrityCheck();
}
else
{
// check memory guard
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);
AZ_Assert(false, "MEMORY STOMP DETECTED!!!");
}
guard->~GuardValue();
}
// init the new memory guard
newSize -= sizeof(Debug::GuardValue);
new (reinterpret_cast<char*>(address) + newSize) Debug::GuardValue();
}
#endif
// statistics
m_requestedBytes -= allocationInfo->m_byteSize;
m_requestedBytes += newSize;
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
allocationInfo->m_byteSize = newSize;
}
//=========================================================================
// EnumerateAllocations
// [9/29/2009]
//=========================================================================
void AllocationRecords::SetMode(Mode mode)
{
if (mode == RECORD_NO_RECORDS)
{
{
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!");
m_mode = mode;
}
//=========================================================================
// EnumerateAllocations
// [9/29/2009]
//=========================================================================
void AllocationRecords::EnumerateAllocations(AllocationInfoCBType cb)
{
// 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.
Debug::AllocationRecordsType recordsCopy;
{
AZStd::scoped_lock lock(m_recordsMutex);
@@ -455,67 +438,93 @@ AllocationRecords::IntegrityCheck() const
}
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())
if (!cb(iter->first, iter->second, m_numStackLevels))
{
// 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!!!");
break;
}
}
}
#endif
}
//=========================================================================
// operator()
// [9/29/2009]
//=========================================================================
bool
PrintAllocationsCB::operator()(void* address, const AllocationInfo& info, unsigned char numStackLevels)
{
if (m_includeNameAndFilename && info.m_name)
//=========================================================================
// IntegrityCheck
// [9/9/2011]
//=========================================================================
void AllocationRecords::IntegrityCheck() const
{
AZ_Printf("Memory", "Allocation Name: \"%s\" Addr: 0%p Size: %d Alignment: %d\n", info.m_name, address, info.m_byteSize, info.m_alignment);
}
else
{
AZ_Printf("Memory", "Allocation Addr: 0%p Size: %d Alignment: %d\n", address, info.m_byteSize, info.m_alignment);
}
if (m_isDetailed)
{
if (!info.m_stackFrames)
#if defined(ENABLE_MEMORY_GUARD)
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
{
AZ_Printf("Memory", " %s (%d)\n", info.m_fileName, info.m_lineNum);
Debug::AllocationRecordsType recordsCopy;
{
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
}
//=========================================================================
// operator()
// [9/29/2009]
//=========================================================================
bool PrintAllocationsCB::operator()(void* address, const AllocationInfo& info, unsigned char numStackLevels)
{
if (m_includeNameAndFilename && info.m_name)
{
AZ_Printf(
"Memory", "Allocation Name: \"%s\" Addr: 0%p Size: %d Alignment: %d\n", info.m_name, address, info.m_byteSize,
info.m_alignment);
}
else
{
// Allocation callstack
const unsigned char decodeStep = 40;
Debug::SymbolStorage::StackLine lines[decodeStep];
unsigned char iFrame = 0;
while (numStackLevels>0)
AZ_Printf("Memory", "Allocation Addr: 0%p Size: %d Alignment: %d\n", address, info.m_byteSize, info.m_alignment);
}
if (m_isDetailed)
{
if (!info.m_stackFrames)
{
unsigned char numToDecode = AZStd::GetMin(decodeStep, numStackLevels);
Debug::SymbolStorage::DecodeFrames(&info.m_stackFrames[iFrame], numToDecode, lines);
for (unsigned char i = 0; i < numToDecode; ++i)
AZ_Printf("Memory", " %s (%d)\n", info.m_fileName, info.m_lineNum);
}
else
{
// Allocation callstack
const unsigned char decodeStep = 40;
Debug::SymbolStorage::StackLine lines[decodeStep];
unsigned char iFrame = 0;
while (numStackLevels > 0)
{
if (info.m_stackFrames[iFrame+i].IsValid())
unsigned char numToDecode = AZStd::GetMin(decodeStep, numStackLevels);
Debug::SymbolStorage::DecodeFrames(&info.m_stackFrames[iFrame], numToDecode, lines);
for (unsigned char i = 0; i < numToDecode; ++i)
{
AZ_Printf("Memory", " %s\n", lines[i]);
if (info.m_stackFrames[iFrame + i].IsValid())
{
AZ_Printf("Memory", " %s\n", lines[i]);
}
}
numStackLevels -= numToDecode;
iFrame += numToDecode;
}
numStackLevels -= numToDecode;
iFrame += numToDecode;
}
}
return true; // continue enumerating
}
return true; // continue enumerating
}
} // namespace AZ::Debug
@@ -6,194 +6,203 @@
*
*/
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/AllocatorManager.h>
#include <AzCore/Memory/Memory.h>
using namespace AZ;
AllocatorBase::AllocatorBase(IAllocatorAllocate* allocationSource, const char* name, const char* desc) :
IAllocator(allocationSource),
m_name(name),
m_desc(desc)
namespace AZ
{
}
AllocatorBase::~AllocatorBase()
{
AZ_Assert(!m_isReady, "Allocator %s (%s) is being destructed without first having gone through proper calls to PreDestroy() and Destroy(). Use AllocatorInstance<> for global allocators or AllocatorWrapper<> for local allocators.", m_name, m_desc);
}
const char* AllocatorBase::GetName() const
{
return m_name;
}
const char* AllocatorBase::GetDescription() const
{
return m_desc;
}
IAllocatorAllocate* AllocatorBase::GetSchema()
{
return nullptr;
}
Debug::AllocationRecords* AllocatorBase::GetRecords()
{
return m_records;
}
void AllocatorBase::SetRecords(Debug::AllocationRecords* records)
{
m_records = records;
m_memoryGuardSize = records ? records->MemoryGuardSize() : 0;
}
bool AllocatorBase::IsReady() const
{
return m_isReady;
}
bool AllocatorBase::CanBeOverridden() const
{
return m_canBeOverridden;
}
void AllocatorBase::PostCreate()
{
if (m_registrationEnabled)
AllocatorBase::AllocatorBase(IAllocatorAllocate* allocationSource, const char* name, const char* desc)
: IAllocator(allocationSource)
, m_name(name)
, m_desc(desc)
{
if (AZ::Environment::IsReady())
}
AllocatorBase::~AllocatorBase()
{
AZ_Assert(
!m_isReady,
"Allocator %s (%s) is being destructed without first having gone through proper calls to PreDestroy() and Destroy(). Use "
"AllocatorInstance<> for global allocators or AllocatorWrapper<> for local allocators.",
m_name, m_desc);
}
const char* AllocatorBase::GetName() const
{
return m_name;
}
const char* AllocatorBase::GetDescription() const
{
return m_desc;
}
IAllocatorAllocate* AllocatorBase::GetSchema()
{
return nullptr;
}
Debug::AllocationRecords* AllocatorBase::GetRecords()
{
return m_records;
}
void AllocatorBase::SetRecords(Debug::AllocationRecords* records)
{
m_records = records;
m_memoryGuardSize = records ? records->MemoryGuardSize() : 0;
}
bool AllocatorBase::IsReady() const
{
return m_isReady;
}
bool AllocatorBase::CanBeOverridden() const
{
return m_canBeOverridden;
}
void AllocatorBase::PostCreate()
{
if (m_registrationEnabled)
{
AllocatorManager::Instance().RegisterAllocator(this);
if (AZ::Environment::IsReady())
{
AllocatorManager::Instance().RegisterAllocator(this);
}
else
{
AllocatorManager::PreRegisterAllocator(this);
}
}
else
const auto debugConfig = GetDebugConfig();
if (!debugConfig.m_excludeFromDebugging)
{
AllocatorManager::PreRegisterAllocator(this);
SetRecords(aznew Debug::AllocationRecords(
(unsigned char)debugConfig.m_stackRecordLevels, debugConfig.m_usesMemoryGuards, debugConfig.m_marksUnallocatedMemory,
GetName()));
}
m_isReady = true;
}
const auto debugConfig = GetDebugConfig();
if (!debugConfig.m_excludeFromDebugging)
void AllocatorBase::PreDestroy()
{
SetRecords(aznew Debug::AllocationRecords((unsigned char)debugConfig.m_stackRecordLevels, debugConfig.m_usesMemoryGuards, debugConfig.m_marksUnallocatedMemory, GetName()));
Debug::AllocationRecords* allocatorRecords = GetRecords();
if (allocatorRecords)
{
delete allocatorRecords;
SetRecords(nullptr);
}
if (m_registrationEnabled && AZ::AllocatorManager::IsReady())
{
AllocatorManager::Instance().UnRegisterAllocator(this);
}
m_isReady = false;
}
m_isReady = true;
}
void AllocatorBase::PreDestroy()
{
Debug::AllocationRecords* allocatorRecords = GetRecords();
if(allocatorRecords)
void AllocatorBase::SetLazilyCreated(bool lazy)
{
delete allocatorRecords;
SetRecords(nullptr);
m_isLazilyCreated = lazy;
}
if (m_registrationEnabled && AZ::AllocatorManager::IsReady())
bool AllocatorBase::IsLazilyCreated() const
{
AllocatorManager::Instance().UnRegisterAllocator(this);
return m_isLazilyCreated;
}
m_isReady = false;
}
void AllocatorBase::SetProfilingActive(bool active)
{
m_isProfilingActive = active;
}
void AllocatorBase::SetLazilyCreated(bool lazy)
{
m_isLazilyCreated = lazy;
}
bool AllocatorBase::IsProfilingActive() const
{
return m_isProfilingActive;
}
bool AllocatorBase::IsLazilyCreated() const
{
return m_isLazilyCreated;
}
void AllocatorBase::DisableOverriding()
{
m_canBeOverridden = false;
}
void AllocatorBase::SetProfilingActive(bool active)
{
m_isProfilingActive = active;
}
void AllocatorBase::DisableRegistration()
{
m_registrationEnabled = false;
}
bool AllocatorBase::IsProfilingActive() const
{
return m_isProfilingActive;
}
void AllocatorBase::DisableOverriding()
{
m_canBeOverridden = false;
}
void AllocatorBase::DisableRegistration()
{
m_registrationEnabled = false;
}
void AllocatorBase::ProfileAllocation(void* ptr, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, int suppressStackRecord)
{
void AllocatorBase::ProfileAllocation(
void* ptr, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, int suppressStackRecord)
{
#if defined(AZ_HAS_VARIADIC_TEMPLATES) && defined(AZ_DEBUG_BUILD)
++suppressStackRecord; // one more for the fact the ebus is a function
++suppressStackRecord; // one more for the fact the ebus is a function
#endif // AZ_HAS_VARIADIC_TEMPLATES
if (m_isProfilingActive)
{
auto records = GetRecords();
if (records)
if (m_isProfilingActive)
{
records->RegisterAllocation(ptr, byteSize, alignment, name, fileName, lineNum, suppressStackRecord + 1);
auto records = GetRecords();
if (records)
{
records->RegisterAllocation(ptr, byteSize, alignment, name, fileName, lineNum, suppressStackRecord + 1);
}
}
}
}
void AllocatorBase::ProfileDeallocation(void* ptr, size_t byteSize, size_t alignment, Debug::AllocationInfo* info)
{
if (m_isProfilingActive)
void AllocatorBase::ProfileDeallocation(void* ptr, size_t byteSize, size_t alignment, Debug::AllocationInfo* info)
{
auto records = GetRecords();
if (records)
if (m_isProfilingActive)
{
records->UnregisterAllocation(ptr, byteSize, alignment, info);
auto records = GetRecords();
if (records)
{
records->UnregisterAllocation(ptr, byteSize, alignment, info);
}
}
}
}
void AllocatorBase::ProfileReallocationBegin([[maybe_unused]] void* ptr, [[maybe_unused]] size_t newSize)
{
}
void AllocatorBase::ProfileReallocationEnd(void* ptr, void* newPtr, size_t newSize, size_t newAlignment)
{
if (m_isProfilingActive)
void AllocatorBase::ProfileReallocationBegin([[maybe_unused]] void* ptr, [[maybe_unused]] size_t newSize)
{
Debug::AllocationInfo info;
ProfileDeallocation(ptr, 0, 0, &info);
ProfileAllocation(newPtr, newSize, newAlignment, info.m_name, info.m_fileName, info.m_lineNum, 0);
}
}
void AllocatorBase::ProfileReallocation(void* ptr, void* newPtr, size_t newSize, size_t newAlignment)
{
ProfileReallocationEnd(ptr, newPtr, newSize, newAlignment);
}
void AllocatorBase::ProfileResize(void* ptr, size_t newSize)
{
if (newSize && m_isProfilingActive)
void AllocatorBase::ProfileReallocationEnd(void* ptr, void* newPtr, size_t newSize, size_t newAlignment)
{
auto records = GetRecords();
if (records)
if (m_isProfilingActive)
{
records->ResizeAllocation(ptr, newSize);
Debug::AllocationInfo info;
ProfileDeallocation(ptr, 0, 0, &info);
ProfileAllocation(newPtr, newSize, newAlignment, info.m_name, info.m_fileName, info.m_lineNum, 0);
}
}
}
bool AllocatorBase::OnOutOfMemory(size_t byteSize, size_t alignment, int flags, const char* name, const char* fileName, int lineNum)
{
if (AllocatorManager::IsReady() && AllocatorManager::Instance().m_outOfMemoryListener)
void AllocatorBase::ProfileReallocation(void* ptr, void* newPtr, size_t newSize, size_t newAlignment)
{
AllocatorManager::Instance().m_outOfMemoryListener(this, byteSize, alignment, flags, name, fileName, lineNum);
return true;
ProfileReallocationEnd(ptr, newPtr, newSize, newAlignment);
}
return false;
}
void AllocatorBase::ProfileResize(void* ptr, size_t newSize)
{
if (newSize && m_isProfilingActive)
{
auto records = GetRecords();
if (records)
{
records->ResizeAllocation(ptr, newSize);
}
}
}
bool AllocatorBase::OnOutOfMemory(size_t byteSize, size_t alignment, int flags, const char* name, const char* fileName, int lineNum)
{
if (AllocatorManager::IsReady() && AllocatorManager::Instance().m_outOfMemoryListener)
{
AllocatorManager::Instance().m_outOfMemoryListener(this, byteSize, alignment, flags, name, fileName, lineNum);
return true;
}
return false;
}
} // namespace AZ
@@ -13,186 +13,182 @@
#include <AzCore/std/functional.h>
using namespace AZ;
//=========================================================================
// BestFitExternalMapAllocator
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::BestFitExternalMapAllocator()
: AllocatorBase(this, "BestFitExternalMapAllocator", "Best fit allocator with external tracking storage!")
, m_schema(nullptr)
{}
//=========================================================================
// Create
// [1/28/2011]
//=========================================================================
bool
BestFitExternalMapAllocator::Create(const Descriptor& desc)
namespace AZ
{
AZ_Assert(IsReady() == false, "BestFitExternalMapAllocator was already created!");
if (IsReady())
//=========================================================================
// BestFitExternalMapAllocator
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::BestFitExternalMapAllocator()
: AllocatorBase(this, "BestFitExternalMapAllocator", "Best fit allocator with external tracking storage!")
, m_schema(nullptr)
{
return false;
}
bool isReady = true;
m_desc = desc;
BestFitExternalMapSchema::Descriptor schemaDesc;
schemaDesc.m_mapAllocator = desc.m_mapAllocator;
schemaDesc.m_memoryBlock = desc.m_memoryBlock;
schemaDesc.m_memoryBlockByteSize = desc.m_memoryBlockByteSize;
m_schema = azcreate(BestFitExternalMapSchema, (schemaDesc), SystemAllocator);
if (m_schema == nullptr)
//=========================================================================
// Create
// [1/28/2011]
//=========================================================================
bool BestFitExternalMapAllocator::Create(const Descriptor& desc)
{
isReady = false;
AZ_Assert(IsReady() == false, "BestFitExternalMapAllocator was already created!");
if (IsReady())
{
return false;
}
bool isReady = true;
m_desc = desc;
BestFitExternalMapSchema::Descriptor schemaDesc;
schemaDesc.m_mapAllocator = desc.m_mapAllocator;
schemaDesc.m_memoryBlock = desc.m_memoryBlock;
schemaDesc.m_memoryBlockByteSize = desc.m_memoryBlockByteSize;
m_schema = azcreate(BestFitExternalMapSchema, (schemaDesc), SystemAllocator);
if (m_schema == nullptr)
{
isReady = false;
}
return isReady;
}
return isReady;
}
//=========================================================================
// Destroy
// [1/28/2011]
//=========================================================================
void BestFitExternalMapAllocator::Destroy()
{
azdestroy(m_schema, SystemAllocator);
m_schema = nullptr;
}
//=========================================================================
// Destroy
// [1/28/2011]
//=========================================================================
void
BestFitExternalMapAllocator::Destroy()
{
azdestroy(m_schema, SystemAllocator);
m_schema = nullptr;
}
AllocatorDebugConfig BestFitExternalMapAllocator::GetDebugConfig()
{
return AllocatorDebugConfig()
.ExcludeFromDebugging(!m_desc.m_allocationRecords)
.StackRecordLevels(m_desc.m_stackRecordLevels)
.MarksUnallocatedMemory(false)
.UsesMemoryGuards(false);
}
AllocatorDebugConfig BestFitExternalMapAllocator::GetDebugConfig()
{
return AllocatorDebugConfig()
.ExcludeFromDebugging(!m_desc.m_allocationRecords)
.StackRecordLevels(m_desc.m_stackRecordLevels)
.MarksUnallocatedMemory(false)
.UsesMemoryGuards(false);
}
//=========================================================================
// Allocate
// [1/28/2011]
//=========================================================================
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;
//=========================================================================
// Allocate
// [1/28/2011]
//=========================================================================
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;
AZ_Assert(byteSize > 0, "You can not allocate 0 bytes!");
AZ_Assert((alignment & (alignment - 1)) == 0, "Alignment must be power of 2!");
byteSize = MemorySizeAdjustedUp(byteSize);
AZ_Assert(byteSize > 0, "You can not allocate 0 bytes!");
AZ_Assert((alignment & (alignment - 1)) == 0, "Alignment must be power of 2!");
byteSize = MemorySizeAdjustedUp(byteSize);
BestFitExternalMapAllocator::pointer_type address = m_schema->Allocate(byteSize, alignment, flags);
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));
BestFitExternalMapAllocator::pointer_type address = m_schema->Allocate(byteSize, alignment, flags);
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));
return address;
}
return address;
}
//=========================================================================
// DeAllocate
// [1/28/2011]
//=========================================================================
void BestFitExternalMapAllocator::DeAllocate(pointer_type ptr, size_type byteSize, size_type alignment)
{
byteSize = MemorySizeAdjustedUp(byteSize);
AZ_MEMORY_PROFILE(ProfileDeallocation(ptr, byteSize, alignment, nullptr));
//=========================================================================
// DeAllocate
// [1/28/2011]
//=========================================================================
void
BestFitExternalMapAllocator::DeAllocate(pointer_type ptr, size_type byteSize, size_type alignment)
{
byteSize = MemorySizeAdjustedUp(byteSize);
AZ_MEMORY_PROFILE(ProfileDeallocation(ptr, byteSize, alignment, nullptr));
(void)byteSize;
(void)alignment;
m_schema->DeAllocate(ptr);
}
(void)byteSize;
(void)alignment;
m_schema->DeAllocate(ptr);
}
//=========================================================================
// Resize
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::size_type BestFitExternalMapAllocator::Resize(pointer_type ptr, size_type newSize)
{
(void)ptr;
(void)newSize;
/* todo */
return 0;
}
//=========================================================================
// Resize
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::size_type
BestFitExternalMapAllocator::Resize(pointer_type ptr, size_type newSize)
{
(void)ptr;
(void)newSize;
/* todo */
return 0;
}
//=========================================================================
// ReAllocate
// [9/13/2011]
//=========================================================================
BestFitExternalMapAllocator::pointer_type BestFitExternalMapAllocator::ReAllocate(
pointer_type ptr, size_type newSize, size_type newAlignment)
{
(void)ptr;
(void)newSize;
(void)newAlignment;
AZ_Assert(false, "Not supported!");
return nullptr;
}
//=========================================================================
// ReAllocate
// [9/13/2011]
//=========================================================================
BestFitExternalMapAllocator::pointer_type
BestFitExternalMapAllocator::ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment)
{
(void)ptr;
(void)newSize;
(void)newAlignment;
AZ_Assert(false, "Not supported!");
return nullptr;
}
//=========================================================================
// AllocationSize
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::size_type BestFitExternalMapAllocator::AllocationSize(pointer_type ptr)
{
return MemorySizeAdjustedDown(m_schema->AllocationSize(ptr));
}
//=========================================================================
// AllocationSize
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::size_type
BestFitExternalMapAllocator::AllocationSize(pointer_type ptr)
{
return MemorySizeAdjustedDown(m_schema->AllocationSize(ptr));
}
//=========================================================================
// NumAllocatedBytes
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::size_type BestFitExternalMapAllocator::NumAllocatedBytes() const
{
return m_schema->NumAllocatedBytes();
}
//=========================================================================
// NumAllocatedBytes
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::size_type
BestFitExternalMapAllocator::NumAllocatedBytes() const
{
return m_schema->NumAllocatedBytes();
}
//=========================================================================
// Capacity
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::size_type BestFitExternalMapAllocator::Capacity() const
{
return m_schema->Capacity();
}
//=========================================================================
// Capacity
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::size_type
BestFitExternalMapAllocator::Capacity() const
{
return m_schema->Capacity();
}
//=========================================================================
// GetMaxAllocationSize
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::size_type BestFitExternalMapAllocator::GetMaxAllocationSize() const
{
return m_schema->GetMaxAllocationSize();
}
//=========================================================================
// GetMaxAllocationSize
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::size_type
BestFitExternalMapAllocator::GetMaxAllocationSize() const
{
return m_schema->GetMaxAllocationSize();
}
auto BestFitExternalMapAllocator::GetMaxContiguousAllocationSize() const -> size_type
{
return m_schema->GetMaxContiguousAllocationSize();
}
auto BestFitExternalMapAllocator::GetMaxContiguousAllocationSize() const -> size_type
{
return m_schema->GetMaxContiguousAllocationSize();
}
//=========================================================================
// GetSubAllocator
// [1/28/2011]
//=========================================================================
IAllocatorAllocate* BestFitExternalMapAllocator::GetSubAllocator()
{
return m_schema->GetSubAllocator();
}
//=========================================================================
// GetSubAllocator
// [1/28/2011]
//=========================================================================
IAllocatorAllocate*
BestFitExternalMapAllocator::GetSubAllocator()
{
return m_schema->GetSubAllocator();
}
} // namespace AZ
@@ -5,8 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZ_BEST_FIT_EXT_MAP_ALLOCATOR_H
#define AZ_BEST_FIT_EXT_MAP_ALLOCATOR_H
#pragma once
#include <AzCore/Memory/Memory.h>
@@ -76,7 +75,3 @@ namespace AZ
};
}
#endif // AZ_BEST_FIT_EXT_MAP_ALLOCATOR_H
#pragma once
@@ -9,194 +9,199 @@
#include <AzCore/Memory/BestFitExternalMapSchema.h>
#include <AzCore/Memory/SystemAllocator.h>
using namespace AZ;
//=========================================================================
// BestFitExternalMapSchema
// [1/28/2011]
//=========================================================================
BestFitExternalMapSchema::BestFitExternalMapSchema(const Descriptor& desc)
: m_desc(desc)
, m_used(0)
, m_freeChunksMap(FreeMapType::key_compare(), AZStdIAllocator(desc.m_mapAllocator != nullptr ? desc.m_mapAllocator : &AllocatorInstance<SystemAllocator>::Get()))
, m_allocChunksMap(AllocMapType::hasher(), AllocMapType::key_eq(), AZStdIAllocator(desc.m_mapAllocator != nullptr ? desc.m_mapAllocator : &AllocatorInstance<SystemAllocator>::Get()))
namespace AZ
{
if (m_desc.m_mapAllocator == nullptr)
//=========================================================================
// BestFitExternalMapSchema
// [1/28/2011]
//=========================================================================
BestFitExternalMapSchema::BestFitExternalMapSchema(const Descriptor& desc)
: m_desc(desc)
, m_used(0)
, m_freeChunksMap(
FreeMapType::key_compare(),
AZStdIAllocator(desc.m_mapAllocator != nullptr ? desc.m_mapAllocator : &AllocatorInstance<SystemAllocator>::Get()))
, m_allocChunksMap(
AllocMapType::hasher(),
AllocMapType::key_eq(),
AZStdIAllocator(desc.m_mapAllocator != nullptr ? desc.m_mapAllocator : &AllocatorInstance<SystemAllocator>::Get()))
{
m_desc.m_mapAllocator = &AllocatorInstance<SystemAllocator>::Get(); // used as our sub allocator
}
AZ_Assert(m_desc.m_memoryBlockByteSize > 0, "You must provide memory block size!");
AZ_Assert(m_desc.m_memoryBlock != nullptr, "You must provide memory block allocated as you with!");
//if( m_desc.m_memoryBlock == NULL) there is no point to automate this cause we need to flag this memory special, otherwise there is no point to use this allocator at all
// m_desc.m_memoryBlock = azmalloc(SystemAllocator,m_desc.m_memoryBlockByteSize,16);
m_freeChunksMap.insert(AZStd::make_pair(m_desc.m_memoryBlockByteSize, reinterpret_cast<char*>(m_desc.m_memoryBlock)));
}
//=========================================================================
// Allocate
// [1/28/2011]
//=========================================================================
BestFitExternalMapSchema::pointer_type
BestFitExternalMapSchema::Allocate(size_type byteSize, size_type alignment, int flags)
{
(void)flags;
char* address = nullptr;
AZ_Assert(alignment > 0 && (alignment & (alignment - 1)) == 0, "Alignment must be >0 and power of 2!");
for (int i = 0; i < 2; ++i) // max 2 attempts to allocate
{
FreeMapType::iterator iter = m_freeChunksMap.find(byteSize);
size_t blockSize = 0;
char* blockAddress = nullptr;
size_t preAllocBlockSize = 0;
while (iter != m_freeChunksMap.end())
if (m_desc.m_mapAllocator == nullptr)
{
blockSize = iter->first;
blockAddress = iter->second;
char* alignedAddr = PointerAlignUp(blockAddress, alignment);
preAllocBlockSize = alignedAddr - blockAddress;
if (preAllocBlockSize + byteSize <= blockSize)
{
m_freeChunksMap.erase(iter); // we have our allocation
m_used += byteSize;
address = alignedAddr;
m_allocChunksMap.insert(AZStd::make_pair(address, byteSize));
break;
}
++iter;
m_desc.m_mapAllocator = &AllocatorInstance<SystemAllocator>::Get(); // used as our sub allocator
}
if (address != nullptr)
AZ_Assert(m_desc.m_memoryBlockByteSize > 0, "You must provide memory block size!");
AZ_Assert(m_desc.m_memoryBlock != nullptr, "You must provide memory block allocated as you with!");
// if( m_desc.m_memoryBlock == NULL) there is no point to automate this cause we need to flag this memory special, otherwise there
// is no point to use this allocator at all
// m_desc.m_memoryBlock = azmalloc(SystemAllocator,m_desc.m_memoryBlockByteSize,16);
m_freeChunksMap.insert(AZStd::make_pair(m_desc.m_memoryBlockByteSize, reinterpret_cast<char*>(m_desc.m_memoryBlock)));
}
//=========================================================================
// Allocate
// [1/28/2011]
//=========================================================================
BestFitExternalMapSchema::pointer_type BestFitExternalMapSchema::Allocate(size_type byteSize, size_type alignment, int flags)
{
(void)flags;
char* address = nullptr;
AZ_Assert(alignment > 0 && (alignment & (alignment - 1)) == 0, "Alignment must be >0 and power of 2!");
for (int i = 0; i < 2; ++i) // max 2 attempts to allocate
{
// split blocks
if (preAllocBlockSize) // if we have a block before the alignment
FreeMapType::iterator iter = m_freeChunksMap.find(byteSize);
size_t blockSize = 0;
char* blockAddress = nullptr;
size_t preAllocBlockSize = 0;
while (iter != m_freeChunksMap.end())
{
m_freeChunksMap.insert(AZStd::make_pair(preAllocBlockSize, blockAddress));
}
size_t postAllocBlockSize = blockSize - preAllocBlockSize - byteSize;
if (postAllocBlockSize)
{
m_freeChunksMap.insert(AZStd::make_pair(postAllocBlockSize, address + byteSize));
}
break;
}
else
{
GarbageCollect();
}
}
return address;
}
//=========================================================================
// DeAllocate
// [1/28/2011]
//=========================================================================
void
BestFitExternalMapSchema::DeAllocate(pointer_type ptr)
{
if (ptr == nullptr)
{
return;
}
AllocMapType::iterator iter = m_allocChunksMap.find(reinterpret_cast<char*>(ptr));
if (iter != m_allocChunksMap.end())
{
m_used -= iter->second;
m_freeChunksMap.insert(AZStd::make_pair(iter->second, iter->first));
m_allocChunksMap.erase(iter);
}
}
//=========================================================================
// AllocationSize
// [1/28/2011]
//=========================================================================
BestFitExternalMapSchema::size_type
BestFitExternalMapSchema::AllocationSize(pointer_type ptr)
{
AllocMapType::iterator iter = m_allocChunksMap.find(reinterpret_cast<char*>(ptr));
if (iter != m_allocChunksMap.end())
{
return iter->second;
}
return 0;
}
//=========================================================================
// GetMaxAllocationSize
// [1/28/2011]
//=========================================================================
BestFitExternalMapSchema::size_type
BestFitExternalMapSchema::GetMaxAllocationSize() const
{
if (!m_freeChunksMap.empty())
{
return m_freeChunksMap.rbegin()->first;
}
return 0;
}
auto BestFitExternalMapSchema::GetMaxContiguousAllocationSize() const -> size_type
{
// Return the maximum size of any single allocation
return AZ_CORE_MAX_ALLOCATOR_SIZE;
}
//=========================================================================
// GarbageCollect
// [1/28/2011]
//=========================================================================
void
BestFitExternalMapSchema::GarbageCollect()
{
for (FreeMapType::iterator curBlock = m_freeChunksMap.begin(); curBlock != m_freeChunksMap.end(); )
{
char* curStart = curBlock->second;
char* curEnd = curStart + curBlock->first;
bool isMerge = false;
for (FreeMapType::iterator nextBlock = curBlock++; nextBlock != m_freeChunksMap.end(); )
{
char* nextStart = nextBlock->second;
char* nextEnd = nextStart + nextBlock->first;
if (curStart == nextEnd)
{
// merge
size_t newBlockSize = curBlock->first + nextBlock->first;
char* newBlockAddress = nextStart;
m_freeChunksMap.erase(nextBlock);
FreeMapType::iterator toErase = curBlock;
++curBlock;
m_freeChunksMap.erase(toErase);
FreeMapType::iterator newBlock = m_freeChunksMap.insert(AZStd::make_pair(newBlockSize, newBlockAddress)).first;
if (curBlock != m_freeChunksMap.end() && newBlockSize < curBlock->first) // if the newBlock in before the next in the list, update next in the list to current
blockSize = iter->first;
blockAddress = iter->second;
char* alignedAddr = PointerAlignUp(blockAddress, alignment);
preAllocBlockSize = alignedAddr - blockAddress;
if (preAllocBlockSize + byteSize <= blockSize)
{
curBlock = newBlock;
m_freeChunksMap.erase(iter); // we have our allocation
m_used += byteSize;
address = alignedAddr;
m_allocChunksMap.insert(AZStd::make_pair(address, byteSize));
break;
}
isMerge = true;
break;
++iter;
}
else if (curEnd == nextStart)
if (address != nullptr)
{
// merge
size_t newBlockSize = curBlock->first + nextBlock->first;
char* newBlockAddress = curStart;
m_freeChunksMap.erase(nextBlock);
FreeMapType::iterator toErase = curBlock;
++curBlock;
m_freeChunksMap.erase(toErase);
FreeMapType::iterator newBlock = m_freeChunksMap.insert(AZStd::make_pair(newBlockSize, newBlockAddress)).first;
if (curBlock != m_freeChunksMap.end() && newBlockSize < curBlock->first) // if the newBlock in before the next in the list, update next in the list to current
// split blocks
if (preAllocBlockSize) // if we have a block before the alignment
{
curBlock = newBlock;
m_freeChunksMap.insert(AZStd::make_pair(preAllocBlockSize, blockAddress));
}
isMerge = true;
size_t postAllocBlockSize = blockSize - preAllocBlockSize - byteSize;
if (postAllocBlockSize)
{
m_freeChunksMap.insert(AZStd::make_pair(postAllocBlockSize, address + byteSize));
}
break;
}
++nextBlock;
else
{
GarbageCollect();
}
}
if (!isMerge)
return address;
}
//=========================================================================
// DeAllocate
// [1/28/2011]
//=========================================================================
void BestFitExternalMapSchema::DeAllocate(pointer_type ptr)
{
if (ptr == nullptr)
{
++curBlock;
return;
}
AllocMapType::iterator iter = m_allocChunksMap.find(reinterpret_cast<char*>(ptr));
if (iter != m_allocChunksMap.end())
{
m_used -= iter->second;
m_freeChunksMap.insert(AZStd::make_pair(iter->second, iter->first));
m_allocChunksMap.erase(iter);
}
}
}
//=========================================================================
// AllocationSize
// [1/28/2011]
//=========================================================================
BestFitExternalMapSchema::size_type BestFitExternalMapSchema::AllocationSize(pointer_type ptr)
{
AllocMapType::iterator iter = m_allocChunksMap.find(reinterpret_cast<char*>(ptr));
if (iter != m_allocChunksMap.end())
{
return iter->second;
}
return 0;
}
//=========================================================================
// GetMaxAllocationSize
// [1/28/2011]
//=========================================================================
BestFitExternalMapSchema::size_type BestFitExternalMapSchema::GetMaxAllocationSize() const
{
if (!m_freeChunksMap.empty())
{
return m_freeChunksMap.rbegin()->first;
}
return 0;
}
auto BestFitExternalMapSchema::GetMaxContiguousAllocationSize() const -> size_type
{
// Return the maximum size of any single allocation
return AZ_CORE_MAX_ALLOCATOR_SIZE;
}
//=========================================================================
// GarbageCollect
// [1/28/2011]
//=========================================================================
void BestFitExternalMapSchema::GarbageCollect()
{
for (FreeMapType::iterator curBlock = m_freeChunksMap.begin(); curBlock != m_freeChunksMap.end();)
{
char* curStart = curBlock->second;
char* curEnd = curStart + curBlock->first;
bool isMerge = false;
for (FreeMapType::iterator nextBlock = curBlock++; nextBlock != m_freeChunksMap.end();)
{
char* nextStart = nextBlock->second;
char* nextEnd = nextStart + nextBlock->first;
if (curStart == nextEnd)
{
// merge
size_t newBlockSize = curBlock->first + nextBlock->first;
char* newBlockAddress = nextStart;
m_freeChunksMap.erase(nextBlock);
FreeMapType::iterator toErase = curBlock;
++curBlock;
m_freeChunksMap.erase(toErase);
FreeMapType::iterator newBlock = m_freeChunksMap.insert(AZStd::make_pair(newBlockSize, newBlockAddress)).first;
// if the newBlock in before the next in the list, update next in the list to current
if (curBlock != m_freeChunksMap.end() && newBlockSize < curBlock->first)
{
curBlock = newBlock;
}
isMerge = true;
break;
}
else if (curEnd == nextStart)
{
// merge
size_t newBlockSize = curBlock->first + nextBlock->first;
char* newBlockAddress = curStart;
m_freeChunksMap.erase(nextBlock);
FreeMapType::iterator toErase = curBlock;
++curBlock;
m_freeChunksMap.erase(toErase);
FreeMapType::iterator newBlock = m_freeChunksMap.insert(AZStd::make_pair(newBlockSize, newBlockAddress)).first;
// if the newBlock in before the next in the list, update next in the list to current
if (curBlock != m_freeChunksMap.end() && newBlockSize < curBlock->first)
{
curBlock = newBlock;
}
isMerge = true;
break;
}
++nextBlock;
}
if (!isMerge)
{
++curBlock;
}
}
}
} // namespace AZ
@@ -5,8 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZ_BEST_FIT_EXT_MAP_ALLOCATION_SCHEME_H
#define AZ_BEST_FIT_EXT_MAP_ALLOCATION_SCHEME_H
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Memory/Memory.h>
@@ -77,8 +76,3 @@ namespace AZ
AllocMapType m_allocChunksMap;
};
}
#endif // AZ_BEST_FIT_EXT_MAP_ALLOCATION_SCHEME_H
#pragma once
File diff suppressed because it is too large Load Diff
@@ -5,8 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZ_POOL_ALLOCATION_SCHEME_H
#define AZ_POOL_ALLOCATION_SCHEME_H
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
@@ -164,8 +163,3 @@ namespace AZ
template<class Allocator>
AZ_THREAD_LOCAL ThreadPoolData* ThreadPoolSchemaHelper<Allocator>::m_threadData = 0;
}
#endif // AZ_POOL_ALLOCATION_SCHEME_H
#pragma once
@@ -21,8 +21,8 @@
#define AZCORE_SYSTEM_ALLOCATOR_HEAP 3
#if !defined(AZCORE_SYSTEM_ALLOCATOR)
// define the default
#define AZCORE_SYSTEM_ALLOCATOR AZCORE_SYSTEM_ALLOCATOR_HPHA
// define the default
#define AZCORE_SYSTEM_ALLOCATOR AZCORE_SYSTEM_ALLOCATOR_HPHA
#endif
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
@@ -35,12 +35,11 @@
#error "Invalid allocator selected for SystemAllocator"
#endif
using namespace AZ;
//////////////////////////////////////////////////////////////////////////
// Globals - we use global storage for the first memory schema, since we can't use dynamic memory!
static bool g_isSystemSchemaUsed = false;
namespace AZ
{
//////////////////////////////////////////////////////////////////////////
// Globals - we use global storage for the first memory schema, since we can't use dynamic memory!
static bool g_isSystemSchemaUsed = false;
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
static AZStd::aligned_storage<sizeof(HphaSchema), AZStd::alignment_of<HphaSchema>::value>::type g_systemSchema;
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
@@ -49,268 +48,275 @@ static bool g_isSystemSchemaUsed = false;
static AZStd::aligned_storage<sizeof(HeapSchema), AZStd::alignment_of<HeapSchema>::value>::type g_systemSchema;
#endif
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//=========================================================================
// SystemAllocator
// [9/2/2009]
//=========================================================================
SystemAllocator::SystemAllocator()
: AllocatorBase(this, "SystemAllocator", "Fundamental generic memory allocator")
, m_isCustom(false)
, m_allocator(nullptr)
, m_ownsOSAllocator(false)
{
}
//=========================================================================
// ~SystemAllocator
//=========================================================================
SystemAllocator::~SystemAllocator()
{
if (IsReady())
//=========================================================================
// SystemAllocator
// [9/2/2009]
//=========================================================================
SystemAllocator::SystemAllocator()
: AllocatorBase(this, "SystemAllocator", "Fundamental generic memory allocator")
, m_isCustom(false)
, m_allocator(nullptr)
, m_ownsOSAllocator(false)
{
Destroy();
}
}
//=========================================================================
// ~Create
// [9/2/2009]
//=========================================================================
bool
SystemAllocator::Create(const Descriptor& desc)
{
AZ_Assert(IsReady() == false, "System allocator was already created!");
if (IsReady())
{
return false;
}
m_desc = desc;
if (!AllocatorInstance<OSAllocator>::IsReady())
//=========================================================================
// ~SystemAllocator
//=========================================================================
SystemAllocator::~SystemAllocator()
{
m_ownsOSAllocator = true;
AllocatorInstance<OSAllocator>::Create();
}
bool isReady = false;
if (desc.m_custom)
{
m_isCustom = true;
m_allocator = desc.m_custom;
isReady = true;
}
else
{
m_isCustom = false;
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
HphaSchema::Descriptor heapDesc;
heapDesc.m_pageSize = desc.m_heap.m_pageSize;
heapDesc.m_poolPageSize = desc.m_heap.m_poolPageSize;
AZ_Assert(desc.m_heap.m_numFixedMemoryBlocks <= 1, "We support max1 memory block at the moment!");
if (desc.m_heap.m_numFixedMemoryBlocks > 0)
if (IsReady())
{
heapDesc.m_fixedMemoryBlock = desc.m_heap.m_fixedMemoryBlocks[0];
heapDesc.m_fixedMemoryBlockByteSize = desc.m_heap.m_fixedMemoryBlocksByteSize[0];
Destroy();
}
heapDesc.m_subAllocator = desc.m_heap.m_subAllocator;
heapDesc.m_isPoolAllocations = desc.m_heap.m_isPoolAllocations;
// Fix SystemAllocator from growing in small chunks
heapDesc.m_systemChunkSize = desc.m_heap.m_systemChunkSize;
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
MallocSchema::Descriptor heapDesc;
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
HeapSchema::Descriptor heapDesc;
memcpy(heapDesc.m_memoryBlocks, desc.m_heap.m_memoryBlocks, sizeof(heapDesc.m_memoryBlocks));
memcpy(heapDesc.m_memoryBlocksByteSize, desc.m_heap.m_memoryBlocksByteSize, sizeof(heapDesc.m_memoryBlocksByteSize));
heapDesc.m_numMemoryBlocks = desc.m_heap.m_numMemoryBlocks;
#endif
if (&AllocatorInstance<SystemAllocator>::Get() == this) // if we are the system allocator
{
AZ_Assert(!g_isSystemSchemaUsed, "AZ::SystemAllocator MUST be created first! It's the source of all allocations!");
}
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
m_allocator = new(&g_systemSchema)HphaSchema(heapDesc);
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
m_allocator = new(&g_systemSchema)MallocSchema(heapDesc);
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
m_allocator = new(&g_systemSchema)HeapSchema(heapDesc);
#endif
g_isSystemSchemaUsed = true;
//=========================================================================
// ~Create
// [9/2/2009]
//=========================================================================
bool SystemAllocator::Create(const Descriptor& desc)
{
AZ_Assert(IsReady() == false, "System allocator was already created!");
if (IsReady())
{
return false;
}
m_desc = desc;
if (!AllocatorInstance<OSAllocator>::IsReady())
{
m_ownsOSAllocator = true;
AllocatorInstance<OSAllocator>::Create();
}
bool isReady = false;
if (desc.m_custom)
{
m_isCustom = true;
m_allocator = desc.m_custom;
isReady = true;
}
else
{
// this class should be inheriting from SystemAllocator
AZ_Assert(AllocatorInstance<SystemAllocator>::IsReady(), "System allocator must be created before any other allocator! They allocate from it.");
m_isCustom = false;
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
HphaSchema::Descriptor heapDesc;
heapDesc.m_pageSize = desc.m_heap.m_pageSize;
heapDesc.m_poolPageSize = desc.m_heap.m_poolPageSize;
AZ_Assert(desc.m_heap.m_numFixedMemoryBlocks <= 1, "We support max1 memory block at the moment!");
if (desc.m_heap.m_numFixedMemoryBlocks > 0)
{
heapDesc.m_fixedMemoryBlock = desc.m_heap.m_fixedMemoryBlocks[0];
heapDesc.m_fixedMemoryBlockByteSize = desc.m_heap.m_fixedMemoryBlocksByteSize[0];
}
heapDesc.m_subAllocator = desc.m_heap.m_subAllocator;
heapDesc.m_isPoolAllocations = desc.m_heap.m_isPoolAllocations;
// Fix SystemAllocator from growing in small chunks
heapDesc.m_systemChunkSize = desc.m_heap.m_systemChunkSize;
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
MallocSchema::Descriptor heapDesc;
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
HeapSchema::Descriptor heapDesc;
memcpy(heapDesc.m_memoryBlocks, desc.m_heap.m_memoryBlocks, sizeof(heapDesc.m_memoryBlocks));
memcpy(heapDesc.m_memoryBlocksByteSize, desc.m_heap.m_memoryBlocksByteSize, sizeof(heapDesc.m_memoryBlocksByteSize));
heapDesc.m_numMemoryBlocks = desc.m_heap.m_numMemoryBlocks;
#endif
if (&AllocatorInstance<SystemAllocator>::Get() == this) // if we are the system allocator
{
AZ_Assert(!g_isSystemSchemaUsed, "AZ::SystemAllocator MUST be created first! It's the source of all allocations!");
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
m_allocator = azcreate(HphaSchema, (heapDesc), SystemAllocator);
m_allocator = new (&g_systemSchema) HphaSchema(heapDesc);
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
m_allocator = azcreate(MallocSchema, (heapDesc), SystemAllocator);
m_allocator = new (&g_systemSchema) MallocSchema(heapDesc);
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
m_allocator = azcreate(HeapSchema, (heapDesc), SystemAllocator);
m_allocator = new (&g_systemSchema) HeapSchema(heapDesc);
#endif
if (m_allocator == nullptr)
{
isReady = false;
g_isSystemSchemaUsed = true;
isReady = true;
}
else
{
isReady = true;
// this class should be inheriting from SystemAllocator
AZ_Assert(
AllocatorInstance<SystemAllocator>::IsReady(),
"System allocator must be created before any other allocator! They allocate from it.");
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
m_allocator = azcreate(HphaSchema, (heapDesc), SystemAllocator);
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
m_allocator = azcreate(MallocSchema, (heapDesc), SystemAllocator);
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
m_allocator = azcreate(HeapSchema, (heapDesc), SystemAllocator);
#endif
if (m_allocator == nullptr)
{
isReady = false;
}
else
{
isReady = true;
}
}
}
return isReady;
}
return isReady;
}
//=========================================================================
// Allocate
// [9/2/2009]
//=========================================================================
void
SystemAllocator::Destroy()
{
if (g_isSystemSchemaUsed)
//=========================================================================
// Allocate
// [9/2/2009]
//=========================================================================
void SystemAllocator::Destroy()
{
int dummy;
(void)dummy;
}
if (!m_isCustom)
{
if ((void*)m_allocator == (void*)&g_systemSchema)
if (g_isSystemSchemaUsed)
{
int dummy;
(void)dummy;
}
if (!m_isCustom)
{
if ((void*)m_allocator == (void*)&g_systemSchema)
{
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
static_cast<HphaSchema*>(m_allocator)->~HphaSchema();
static_cast<HphaSchema*>(m_allocator)->~HphaSchema();
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
static_cast<MallocSchema*>(m_allocator)->~MallocSchema();
static_cast<MallocSchema*>(m_allocator)->~MallocSchema();
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
static_cast<HeapSchema*>(m_allocator)->~HeapSchema();
static_cast<HeapSchema*>(m_allocator)->~HeapSchema();
#endif
g_isSystemSchemaUsed = false;
g_isSystemSchemaUsed = false;
}
else
{
azdestroy(m_allocator);
}
}
else
if (m_ownsOSAllocator)
{
azdestroy(m_allocator);
AllocatorInstance<OSAllocator>::Destroy();
m_ownsOSAllocator = false;
}
}
if (m_ownsOSAllocator)
AllocatorDebugConfig SystemAllocator::GetDebugConfig()
{
AllocatorInstance<OSAllocator>::Destroy();
m_ownsOSAllocator = false;
}
}
AllocatorDebugConfig SystemAllocator::GetDebugConfig()
{
return AllocatorDebugConfig()
.StackRecordLevels(m_desc.m_stackRecordLevels)
.UsesMemoryGuards(!m_isCustom)
.MarksUnallocatedMemory(!m_isCustom)
.ExcludeFromDebugging(!m_desc.m_allocationRecords);
}
IAllocatorAllocate* SystemAllocator::GetSchema()
{
return m_allocator;
}
//=========================================================================
// Allocate
// [9/2/2009]
//=========================================================================
SystemAllocator::pointer_type
SystemAllocator::Allocate(size_type byteSize, size_type alignment, int flags, const char* name, const char* fileName, int lineNum, unsigned int suppressStackRecord)
{
if (byteSize == 0)
{
return nullptr;
}
AZ_Assert(byteSize > 0, "You can not allocate 0 bytes!");
AZ_Assert((alignment & (alignment - 1)) == 0, "Alignment must be power of 2!");
byteSize = MemorySizeAdjustedUp(byteSize);
SystemAllocator::pointer_type address = m_allocator->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord + 1);
if (address == nullptr)
{
// Free all memory we can and try again!
AllocatorManager::Instance().GarbageCollect();
address = m_allocator->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord + 1);
return AllocatorDebugConfig()
.StackRecordLevels(m_desc.m_stackRecordLevels)
.UsesMemoryGuards(!m_isCustom)
.MarksUnallocatedMemory(!m_isCustom)
.ExcludeFromDebugging(!m_desc.m_allocationRecords);
}
if (address == nullptr)
IAllocatorAllocate* SystemAllocator::GetSchema()
{
byteSize = MemorySizeAdjustedDown(byteSize); // restore original size
return m_allocator;
}
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);
//=========================================================================
// Allocate
// [9/2/2009]
//=========================================================================
SystemAllocator::pointer_type SystemAllocator::Allocate(
size_type byteSize,
size_type alignment,
int flags,
const char* name,
const char* fileName,
int lineNum,
unsigned int suppressStackRecord)
{
if (byteSize == 0)
{
return nullptr;
}
AZ_Assert(byteSize > 0, "You can not allocate 0 bytes!");
AZ_Assert((alignment & (alignment - 1)) == 0, "Alignment must be power of 2!");
AZ_PROFILE_MEMORY_ALLOC_EX(MemoryReserved, fileName, lineNum, address, byteSize, name);
AZ_MEMORY_PROFILE(ProfileAllocation(address, byteSize, alignment, name, fileName, lineNum, suppressStackRecord + 1));
byteSize = MemorySizeAdjustedUp(byteSize);
SystemAllocator::pointer_type address =
m_allocator->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord + 1);
return address;
}
if (address == nullptr)
{
// Free all memory we can and try again!
AllocatorManager::Instance().GarbageCollect();
//=========================================================================
// DeAllocate
// [9/2/2009]
//=========================================================================
void
SystemAllocator::DeAllocate(pointer_type ptr, size_type byteSize, size_type alignment)
{
byteSize = MemorySizeAdjustedUp(byteSize);
AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
AZ_MEMORY_PROFILE(ProfileDeallocation(ptr, byteSize, alignment, nullptr));
m_allocator->DeAllocate(ptr, byteSize, alignment);
}
address = m_allocator->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord + 1);
}
//=========================================================================
// ReAllocate
// [9/13/2011]
//=========================================================================
SystemAllocator::pointer_type
SystemAllocator::ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment)
{
newSize = MemorySizeAdjustedUp(newSize);
if (address == nullptr)
{
byteSize = MemorySizeAdjustedDown(byteSize); // restore original size
}
AZ_MEMORY_PROFILE(ProfileReallocationBegin(ptr, newSize));
AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
pointer_type newAddress = m_allocator->ReAllocate(ptr, newSize, newAlignment);
AZ_PROFILE_MEMORY_ALLOC(MemoryReserved, newAddress, newSize, "SystemAllocator realloc");
AZ_MEMORY_PROFILE(ProfileReallocationEnd(ptr, newAddress, newSize, newAlignment));
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);
return newAddress;
}
AZ_PROFILE_MEMORY_ALLOC_EX(MemoryReserved, fileName, lineNum, address, byteSize, name);
AZ_MEMORY_PROFILE(ProfileAllocation(address, byteSize, alignment, name, fileName, lineNum, suppressStackRecord + 1));
//=========================================================================
// Resize
// [8/12/2011]
//=========================================================================
SystemAllocator::size_type
SystemAllocator::Resize(pointer_type ptr, size_type newSize)
{
newSize = MemorySizeAdjustedUp(newSize);
size_type resizedSize = m_allocator->Resize(ptr, newSize);
return address;
}
AZ_MEMORY_PROFILE(ProfileResize(ptr, resizedSize));
//=========================================================================
// DeAllocate
// [9/2/2009]
//=========================================================================
void SystemAllocator::DeAllocate(pointer_type ptr, size_type byteSize, size_type alignment)
{
byteSize = MemorySizeAdjustedUp(byteSize);
AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
AZ_MEMORY_PROFILE(ProfileDeallocation(ptr, byteSize, alignment, nullptr));
m_allocator->DeAllocate(ptr, byteSize, alignment);
}
return MemorySizeAdjustedDown(resizedSize);
}
//=========================================================================
// ReAllocate
// [9/13/2011]
//=========================================================================
SystemAllocator::pointer_type SystemAllocator::ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment)
{
newSize = MemorySizeAdjustedUp(newSize);
//=========================================================================
//
// [8/12/2011]
//=========================================================================
SystemAllocator::size_type
SystemAllocator::AllocationSize(pointer_type ptr)
{
size_type allocSize = MemorySizeAdjustedDown(m_allocator->AllocationSize(ptr));
AZ_MEMORY_PROFILE(ProfileReallocationBegin(ptr, newSize));
AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
pointer_type newAddress = m_allocator->ReAllocate(ptr, newSize, newAlignment);
AZ_PROFILE_MEMORY_ALLOC(MemoryReserved, newAddress, newSize, "SystemAllocator realloc");
AZ_MEMORY_PROFILE(ProfileReallocationEnd(ptr, newAddress, newSize, newAlignment));
return allocSize;
}
return newAddress;
}
//=========================================================================
// Resize
// [8/12/2011]
//=========================================================================
SystemAllocator::size_type SystemAllocator::Resize(pointer_type ptr, size_type newSize)
{
newSize = MemorySizeAdjustedUp(newSize);
size_type resizedSize = m_allocator->Resize(ptr, newSize);
AZ_MEMORY_PROFILE(ProfileResize(ptr, resizedSize));
return MemorySizeAdjustedDown(resizedSize);
}
//=========================================================================
//
// [8/12/2011]
//=========================================================================
SystemAllocator::size_type SystemAllocator::AllocationSize(pointer_type ptr)
{
size_type allocSize = MemorySizeAdjustedDown(m_allocator->AllocationSize(ptr));
return allocSize;
}
} // namespace AZ
@@ -5,8 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_SYS_ALLOCATOR_H
#define AZCORE_SYS_ALLOCATOR_H
#pragma once
#include <AzCore/Memory/Memory.h>
@@ -120,7 +119,5 @@ namespace AZ
};
}
#endif // AZCORE_SYS_ALLOCATOR_H
#pragma once
@@ -23,11 +23,11 @@ namespace AZ
}
}
void cvar_t_simulationTickDeltaOverride_Changed(const float& value)
void cvar_t_simulationTickDeltaOverride_Changed(const int64_t& value)
{
if (auto* timeSystem = AZ::Interface<ITime>::Get())
{
timeSystem->SetSimulationTickDeltaOverride(AZ::SecondsToTimeMs(value));
timeSystem->SetSimulationTickDeltaOverride(static_cast<AZ::TimeMs>(value));
}
}
@@ -44,8 +44,8 @@ namespace AZ
AZ_CVAR(float, t_simulationTickScale, 1.0f, cvar_t_simulationTickScale_Changed, AZ::ConsoleFunctorFlags::Null,
"A scalar amount to adjust time passage by, 1.0 == realtime, 0.5 == half realtime, 2.0 == doubletime");
AZ_CVAR(float, t_simulationTickDeltaOverride, 0.0f, cvar_t_simulationTickDeltaOverride_Changed, AZ::ConsoleFunctorFlags::Null,
"If > 0, overrides the simulation tick delta time with the provided value (Seconds) and ignores any t_simulationTickScale value.");
AZ_CVAR(int64_t, t_simulationTickDeltaOverride, 0, cvar_t_simulationTickDeltaOverride_Changed, AZ::ConsoleFunctorFlags::Null,
"If > 0, overrides the simulation tick delta time with the provided value (Milliseconds) and ignores any t_simulationTickScale value.");
AZ_CVAR(int, t_simulationTickRate, 0, cvar_t_simulationTickRate_Changed, AZ::ConsoleFunctorFlags::Null,
"The minimum rate to force the game simulation tick to run. 0 for as fast as possible. 30 = ~33ms, 60 = ~16ms");
@@ -176,7 +176,7 @@ namespace AZ
if (timeUs != m_simulationTickDeltaOverride)
{
m_simulationTickDeltaOverride = timeUs;
t_simulationTickDeltaOverride = AZ::TimeUsToSeconds(timeUs); //update the cvar
t_simulationTickDeltaOverride = static_cast <int64_t>(timeMs); // update the cvar
}
}
@@ -408,4 +408,106 @@ namespace UnitTest
Matrix4x4 m = Matrix4x4::CreateFromQuaternion(rotQuat);
AZ_TEST_ASSERT(m.IsClose(rotMatrix));
}
class QuaternionScaledAxisAngleConversionFixture
: public ::testing::TestWithParam<AZ::Quaternion>
{
public:
AZ::Quaternion GetAbs(const AZ::Quaternion& in)
{
// Take the shortest path for quaternions containing rotations bigger than 180.0°.
if (in.GetW() < 0.0f)
{
return -in;
}
return in;
}
};
static const AZ::Quaternion RotationRepresentationConversionTestQuats[] =
{
AZ::Quaternion::CreateIdentity(),
-AZ::Quaternion::CreateIdentity(),
AZ::Quaternion::CreateRotationX(AZ::Constants::TwoPi),
AZ::Quaternion::CreateRotationY(AZ::Constants::Pi),
AZ::Quaternion::CreateRotationZ(AZ::Constants::HalfPi),
AZ::Quaternion::CreateRotationX(AZ::Constants::QuarterPi),
AZ::Quaternion(0.64f, 0.36f, 0.48f, 0.48f),
AZ::Quaternion(0.70f, -0.34f, 0.10f, 0.62f),
AZ::Quaternion(-0.38f, 0.34f, 0.70f, -0.50f),
AZ::Quaternion(0.70f, -0.34f, -0.38f, 0.50f),
AZ::Quaternion(0.00f, 0.00f, -0.28f, 0.96f),
AZ::Quaternion(0.24f, -0.64f, 0.72f, 0.12f),
AZ::Quaternion(-0.66f, 0.62f, 0.42f, 0.06f)
};
TEST_P(QuaternionScaledAxisAngleConversionFixture, ScaledAxisAngleQuatRoundtripTests)
{
const AZ::Quaternion testQuat = GetAbs(GetParam());
// Convert test quaternion to scaled axis-angle representation.
const AZ::Vector3 scaledAxisAngle = testQuat.ConvertToScaledAxisAngle();
// Convert the scaled axis-angle back into a quaternion.
AZ::Quaternion backFromScaledAxisAngle = AZ::Quaternion::CreateFromScaledAxisAngle(scaledAxisAngle);
// Compare the original quaternion with the one after the conversion.
EXPECT_TRUE(testQuat.IsClose(backFromScaledAxisAngle, 1e-6f));
}
TEST_P(QuaternionScaledAxisAngleConversionFixture, AxisAngleQuatRoundtripTests)
{
const AZ::Quaternion testQuat = GetAbs(GetParam());
// Convert test quaternion to axis-angle representation.
AZ::Vector3 axis;
float angle;
testQuat.ConvertToAxisAngle(axis, angle);
// Convert the axis-angle back into a quaternion and compare the original quaternion with the one after the conversion.
const AZ::Quaternion backFromAxisAngle = AZ::Quaternion::CreateFromAxisAngle(axis, angle);
EXPECT_TRUE(testQuat.IsClose(backFromAxisAngle, 1e-6f));
}
TEST_P(QuaternionScaledAxisAngleConversionFixture, CompareAxisAngleConversionTests)
{
const AZ::Quaternion testQuat = GetAbs(GetParam());
// Convert test quaternion to scaled axis-angle representation.
const AZ::Vector3 scaledAxisAngle = testQuat.ConvertToScaledAxisAngle();
// Convert test quaternion to axis-angle representation and scale it manually.
AZ::Vector3 axis;
float angle;
testQuat.ConvertToAxisAngle(axis, angle);
// Compare the scaled result to the version from the helper that directly converts it to scaled axis-angle.
AZ::Vector3 scaledResult = axis*angle;
EXPECT_TRUE(scaledResult.IsClose(scaledAxisAngle, 1e-5f));
}
TEST_P(QuaternionScaledAxisAngleConversionFixture, CompareScaledAxisAngleConversionTests)
{
const AZ::Quaternion testQuat = GetAbs(GetParam());
// Convert test quaternion to axis-angle representation and scale it manually.
AZ::Vector3 axis;
float angle;
testQuat.ConvertToAxisAngle(axis, angle);
AZ::Vector3 scaledResult = axis*angle;
// Special case handling for identity rotation.
AZ::Vector3 axisFromScaledResult = scaledResult.GetNormalized();
float angleFromScaledResult = scaledResult.GetLength();
if (AZ::IsClose(angleFromScaledResult, 0.0f))
{
axisFromScaledResult = AZ::Vector3::CreateAxisY();
}
const AZ::Quaternion backFromAxisAngle = AZ::Quaternion::CreateFromAxisAngle(axisFromScaledResult, angleFromScaledResult);
EXPECT_TRUE(testQuat.IsClose(backFromAxisAngle, 1e-6f));
}
INSTANTIATE_TEST_CASE_P(MATH_Quaternion, QuaternionScaledAxisAngleConversionFixture, ::testing::ValuesIn(RotationRepresentationConversionTestQuats));
}
+190 -3
View File
@@ -11,8 +11,7 @@
namespace UnitTest
{
class TimeTests
: public AllocatorsFixture
class TimeTests : public AllocatorsFixture
{
public:
void SetUp() override
@@ -77,4 +76,192 @@ namespace UnitTest
int64_t delta = static_cast<int64_t>(timeMs) - static_cast<int64_t>(timeUsToMs);
EXPECT_LT(abs(delta), 1);
}
}
class TimeSystemTests : public AllocatorsTestFixture
{
public:
void SetUp() override
{
SetupAllocator();
m_controlTime = static_cast<AZ::TimeUs>(AZStd::GetTimeNowMicroSecond());
m_timeSystem = AZStd::make_unique<AZ::TimeSystem>();
}
void TearDown() override
{
m_controlTime = AZ::Time::ZeroTimeUs;
m_timeSystem.reset();
TeardownAllocator();
}
AZ::TimeUs GetDiff(AZ::TimeUs time1, AZ::TimeUs time2) const
{
// AZ::TimeUs is unsigned so make sure to not underflow.
return time1 > time2 ? time1 - time2 : time2 - time1;
}
AZ::TimeUs m_controlTime;
AZStd::unique_ptr<AZ::TimeSystem> m_timeSystem;
};
TEST_F(TimeSystemTests, GetRealElapsedTimeUs)
{
// sleep for a bit to advance time.
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(2));
// find the delta for the control and from GetRealElapsedTimeUs
const AZ::TimeUs baseline = static_cast<AZ::TimeUs>(AZStd::GetTimeNowMicroSecond()) - m_controlTime;
const AZ::TimeUs elapsedTime = m_timeSystem->GetRealElapsedTimeUs();
const AZ::TimeUs diff = GetDiff(baseline, elapsedTime);
// elapsedTime should be within 10 microseconds from baseline.
EXPECT_LT(diff, AZ::TimeUs{ 10 });
}
TEST_F(TimeSystemTests, GetElapsedTimeUs)
{
// sleep for a bit to advance time.
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(2));
// find the delta for the control and from GetElapsedTimeUs
const AZ::TimeUs baseline = static_cast<AZ::TimeUs>(AZStd::GetTimeNowMicroSecond()) - m_controlTime;
const AZ::TimeUs elapsedTime = m_timeSystem->GetElapsedTimeUs();
const AZ::TimeUs diff = GetDiff(baseline, elapsedTime);
// elapsedTime should be within 10 microseconds from baseline.
EXPECT_LT(diff, AZ::TimeUs{ 10 });
}
TEST_F(TimeSystemTests, ElapsedTimeScales)
{
// slow down 'time'
m_timeSystem->SetSimulationTickScale(0.5f);
// sleep for a bit to advance time.
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(2));
// find the delta for the control and from GetElapsedTimeUs
const AZ::TimeUs baseline = static_cast<AZ::TimeUs>(AZStd::GetTimeNowMicroSecond()) - m_controlTime;
const AZ::TimeUs elapsedTime = m_timeSystem->GetElapsedTimeUs();
const AZ::TimeUs halfBaseline = (baseline / AZ::TimeUs{ 2 });
// elapsedTime should be about half of the control.
const AZ::TimeUs diff = GetDiff(halfBaseline, elapsedTime);
// elapsedTime should be within 10 microseconds from baseline.
EXPECT_LT(diff, AZ::TimeUs{ 10 });
// reset time scale
m_timeSystem->SetSimulationTickScale(1.0f);
}
TEST_F(TimeSystemTests, AdvanceTickDeltaTimes)
{
// advance the tick delta to get a clean base.
m_timeSystem->AdvanceTickDeltaTimes();
const AZ::TimeUs baselineStart = static_cast<AZ::TimeUs>(AZStd::GetTimeNowMicroSecond());
// sleep for a bit to advance time.
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(2));
// advance the tick delta.
const AZ::TimeUs delta = m_timeSystem->AdvanceTickDeltaTimes();
const AZ::TimeUs baselineDelta = static_cast<AZ::TimeUs>(AZStd::GetTimeNowMicroSecond()) - baselineStart;
// the delta should be close to the baselineDelta.
const AZ::TimeUs diff = GetDiff(delta, baselineDelta);
EXPECT_LT(diff, AZ::TimeUs{ 10 });
}
TEST_F(TimeSystemTests, SimulationAndRealTickDeltaTimesWithNoTimeScale)
{
// advance the tick delta to get a clean base.
m_timeSystem->AdvanceTickDeltaTimes();
const AZ::TimeUs baselineStart = static_cast<AZ::TimeUs>(AZStd::GetTimeNowMicroSecond());
// sleep for a bit to advance time.
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(2));
// advance the tick delta.
const AZ::TimeUs delta = m_timeSystem->AdvanceTickDeltaTimes();
const AZ::TimeUs baselineDelta = static_cast<AZ::TimeUs>(AZStd::GetTimeNowMicroSecond()) - baselineStart;
// the delta should be close to the baselineDelta.
AZ::TimeUs diff = GetDiff(delta, baselineDelta);
EXPECT_LT(diff, AZ::TimeUs{ 10 });
// the delta should be the same as GetSimulationTickDeltaTimeUs and near GetRealTickDeltaTimeUs
const AZ::TimeUs simDeltaTime = m_timeSystem->GetSimulationTickDeltaTimeUs();
EXPECT_EQ(delta, simDeltaTime);
const AZ::TimeUs realDeltaTime = m_timeSystem->GetRealTickDeltaTimeUs();
diff = GetDiff(delta, realDeltaTime);
EXPECT_LT(diff, AZ::TimeUs{ 10 });
}
TEST_F(TimeSystemTests, SimulationAndRealTickDeltaTimesWithTimeScale)
{
// advance the tick delta to get a clean base.
m_timeSystem->AdvanceTickDeltaTimes();
const AZ::TimeUs baselineStart = static_cast<AZ::TimeUs>(AZStd::GetTimeNowMicroSecond());
// slow down 'time';
m_timeSystem->SetSimulationTickScale(0.5f);
// sleep for a bit to advance time.
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(2));
// advance the tick delta.
const AZ::TimeUs delta = m_timeSystem->AdvanceTickDeltaTimes();
const AZ::TimeUs baselineDelta = static_cast<AZ::TimeUs>(AZStd::GetTimeNowMicroSecond()) - baselineStart;
const AZ::TimeUs halfBaselineDelta = (baselineDelta / AZ::TimeUs{ 2 });
// the delta should be half the baselineDelta
AZ::TimeUs diff = GetDiff(delta, halfBaselineDelta);
EXPECT_LT(diff, AZ::TimeUs{ 10 });
// the delta should be the same as GetSimulationTickDeltaTimeUs
const AZ::TimeUs simDeltaTime = m_timeSystem->GetSimulationTickDeltaTimeUs();
EXPECT_EQ(delta, simDeltaTime);
// the delta should be near half the GetRealTickDeltaTimeUs
const AZ::TimeUs realDeltaTime = m_timeSystem->GetRealTickDeltaTimeUs();
const AZ::TimeUs halfRealDeltaTime = (realDeltaTime / AZ::TimeUs{ 2 });
diff = GetDiff(delta, halfRealDeltaTime);
EXPECT_LT(diff, AZ::TimeUs{ 10 });
// reset time scale
m_timeSystem->SetSimulationTickScale(1.0f);
}
TEST_F(TimeSystemTests, SimulationTickDeltaOverride)
{
// advance the tick delta to get a clean base.
m_timeSystem->AdvanceTickDeltaTimes();
const AZ::TimeUs baselineStart = static_cast<AZ::TimeUs>(AZStd::GetTimeNowMicroSecond());
// set the tick delta override
const AZ::TimeMs tickOverride = AZ::TimeMs{ 3462 };
m_timeSystem->SetSimulationTickDeltaOverride(tickOverride);
// sleep for a bit to advance time.
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(2));
// advance the tick delta.
const AZ::TimeUs delta = m_timeSystem->AdvanceTickDeltaTimes();
const AZ::TimeUs baselineDelta = static_cast<AZ::TimeUs>(AZStd::GetTimeNowMicroSecond()) - baselineStart;
// the delta should be equal to the tickOverride
EXPECT_EQ(delta, AZ::TimeMsToUs(tickOverride));
// real tick delta should be near the baselineDelta
const AZ::TimeUs realDeltaTime = m_timeSystem->GetRealTickDeltaTimeUs();
const AZ::TimeUs diff = GetDiff(realDeltaTime, baselineDelta);
EXPECT_LT(diff, AZ::TimeUs{ 10 });
// reset the tick delta override
m_timeSystem->SetSimulationTickDeltaOverride(AZ::Time::ZeroTimeMs);
}
} // namespace UnitTest
@@ -213,27 +213,33 @@ namespace AzFramework
Camera Cameras::StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, const float scrollDelta, const float deltaTime)
{
for (int i = 0; i < m_idleCameraInputs.size();)
for (int idleIndex = 0; idleIndex < m_idleCameraInputs.size();)
{
auto& cameraInput = m_idleCameraInputs[i];
auto& cameraInput = m_idleCameraInputs[idleIndex];
const bool canBegin = cameraInput->Beginning() &&
AZStd::all_of(m_activeCameraInputs.cbegin(), m_activeCameraInputs.cend(),
[](const auto& input)
{
return !input->Exclusive();
}) &&
(!cameraInput->Exclusive() || (cameraInput->Exclusive() && m_activeCameraInputs.empty()));
(!cameraInput->Exclusive() || m_activeCameraInputs.empty());
if (canBegin)
{
m_activeCameraInputs.push_back(cameraInput);
using AZStd::swap;
swap(m_idleCameraInputs[i], m_idleCameraInputs[m_idleCameraInputs.size() - 1]);
swap(m_idleCameraInputs[idleIndex], m_idleCameraInputs[m_idleCameraInputs.size() - 1]);
m_idleCameraInputs.pop_back();
}
else
{
i++;
// if a camera attempted to start but was not allowed to, ensure activation is cancelled
if (!cameraInput->Idle())
{
cameraInput->CancelActivation();
}
idleIndex++;
}
}
@@ -245,21 +251,21 @@ namespace AzFramework
return acc;
});
for (int i = 0; i < m_activeCameraInputs.size();)
for (int activeIndex = 0; activeIndex < m_activeCameraInputs.size();)
{
auto& cameraInput = m_activeCameraInputs[i];
auto& cameraInput = m_activeCameraInputs[activeIndex];
if (cameraInput->Ending())
{
cameraInput->ClearActivation();
m_idleCameraInputs.push_back(cameraInput);
using AZStd::swap;
swap(m_activeCameraInputs[i], m_activeCameraInputs[m_activeCameraInputs.size() - 1]);
swap(m_activeCameraInputs[activeIndex], m_activeCameraInputs[m_activeCameraInputs.size() - 1]);
m_activeCameraInputs.pop_back();
}
else
{
cameraInput->ContinueActivation();
i++;
activeIndex++;
}
}
@@ -470,9 +476,10 @@ namespace AzFramework
{
if (input->m_state == InputChannel::State::Began)
{
m_translation |= TranslationFromKey(input->m_channelId, m_translateCameraInputChannelIds);
if (m_translation != TranslationType::Nil)
if (auto translation = TranslationFromKey(input->m_channelId, m_translateCameraInputChannelIds);
translation != TranslationType::Nil)
{
m_translation |= translation;
BeginActivation();
}
@@ -484,11 +491,16 @@ namespace AzFramework
// ensure we don't process end events in the idle state
else if (input->m_state == InputChannel::State::Ended && !Idle())
{
m_translation &= ~(TranslationFromKey(input->m_channelId, m_translateCameraInputChannelIds));
if (m_translation == TranslationType::Nil)
if (auto translation = TranslationFromKey(input->m_channelId, m_translateCameraInputChannelIds);
translation != TranslationType::Nil)
{
EndActivation();
m_translation &= ~translation;
if (m_translation == TranslationType::Nil)
{
EndActivation();
}
}
if (input->m_channelId == m_translateCameraInputChannelIds.m_boostChannelId)
{
m_boost = false;
@@ -185,6 +185,11 @@ namespace AzFramework
m_activation = Activation::Ending;
}
void CancelActivation()
{
m_activation = Activation::Idle;
}
void ContinueActivation()
{
// continue activation is called after the first step of the camera input,
@@ -10,6 +10,7 @@
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/RTTI/TypeInfoSimple.h>
#include <AzCore/base.h>
@@ -203,6 +204,12 @@ namespace AzFramework
return AZ::Vector2(aznumeric_cast<float>(screenPoint.m_x), aznumeric_cast<float>(screenPoint.m_y));
}
//! Return an AZ::Vector3 from a ScreenPoint (including z/depth value, defaulting to 0.0f).
inline AZ::Vector3 Vector3FromScreenPoint(const ScreenPoint& screenPoint, const float z = 0.0f)
{
return AZ::Vector3(aznumeric_cast<float>(screenPoint.m_x), aznumeric_cast<float>(screenPoint.m_y), z);
}
//! Return an AZ::Vector2 from a ScreenVector.
inline AZ::Vector2 Vector2FromScreenVector(const ScreenVector& screenVector)
{
@@ -416,4 +416,78 @@ namespace UnitTest
using ::testing::FloatNear;
EXPECT_THAT(m_camera.m_pitch, FloatNear(expectedPitch, 0.001f));
}
TEST_F(CameraInputFixture, InvalidTranslationInputKeyCannotBeginTranslateCameraInputAgain)
{
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId,
AzFramework::InputChannel::State::Began });
const bool consumed =
m_cameraSystem->HandleEvents(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
using ::testing::IsFalse;
using ::testing::IsTrue;
EXPECT_THAT(consumed, IsTrue());
EXPECT_THAT(m_firstPersonTranslateCamera->Beginning(), IsFalse());
EXPECT_THAT(m_firstPersonTranslateCamera->Active(), IsTrue());
}
TEST_F(CameraInputFixture, InvalidTranslationInputKeyDownCannotBeginTranslateCameraInputAgain)
{
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId,
AzFramework::InputChannel::State::Began });
const bool consumed =
m_cameraSystem->HandleEvents(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
using ::testing::IsFalse;
using ::testing::IsTrue;
EXPECT_THAT(consumed, IsTrue());
EXPECT_THAT(m_firstPersonTranslateCamera->Beginning(), IsFalse());
EXPECT_THAT(m_firstPersonTranslateCamera->Active(), IsTrue());
}
TEST_F(CameraInputFixture, InvalidTranslationInputKeyUpDoesNotAffectTranslateCameraInputEnd)
{
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId,
AzFramework::InputChannel::State::Began });
const bool consumed =
m_cameraSystem->HandleEvents(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId,
AzFramework::InputChannel::State::Ended });
using ::testing::IsFalse;
using ::testing::IsTrue;
EXPECT_THAT(consumed, IsTrue());
EXPECT_THAT(m_firstPersonTranslateCamera->Idle(), IsTrue());
}
TEST_F(CameraInputFixture, OrbitCameraInputCannotBeLeftInInvalidStateIfItCannotFullyBeginAfterInputChannelBegin)
{
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId,
AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
using ::testing::IsFalse;
using ::testing::IsTrue;
EXPECT_THAT(m_orbitCamera->Beginning(), IsFalse());
EXPECT_THAT(m_orbitCamera->Idle(), IsTrue());
}
TEST_F(CameraInputFixture, OrbitCameraInputCannotBeLeftInInvalidStateIfItCannotFullyBeginAfterInputChannelBeginAndEnd)
{
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId,
AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Ended });
using ::testing::IsFalse;
using ::testing::IsTrue;
EXPECT_THAT(m_orbitCamera->Ending(), IsFalse());
EXPECT_THAT(m_orbitCamera->Idle(), IsTrue());
}
} // namespace UnitTest
@@ -26,29 +26,33 @@ namespace AzManipulatorTestFramework
{
public:
virtual ~ViewportInteractionInterface() = default;
//! Return the camera state.
//! Returns the camera state.
virtual AzFramework::CameraState GetCameraState() = 0;
//! Set the camera state.
//! Sets the camera state.
virtual void SetCameraState(const AzFramework::CameraState& cameraState) = 0;
//! Retrieve the debug display.
//! Retrieves the debug display.
virtual AzFramework::DebugDisplayRequests& GetDebugDisplay() = 0;
//! Set if grid snapping is enabled or not.
//! Sets if grid snapping is enabled or not.
virtual void SetGridSnapping(bool enabled) = 0;
//! Set if angular snapping is enabled or not.
//! Sets if angular snapping is enabled or not.
virtual void SetAngularSnapping(bool enabled) = 0;
//! Set the grid size.
//! Sets the grid size.
virtual void SetGridSize(float size) = 0;
//! Set the angular step.
//! Sets the angular step.
virtual void SetAngularStep(float step) = 0;
//! Get the viewport id.
//! Gets the viewport id.
virtual AzFramework::ViewportId GetViewportId() const = 0;
//! Updates the visibility state.
//! Updates which entities are currently visible given the current camera state.
virtual void UpdateVisibility() = 0;
//! Set if sticky select is enabled or not.
//! Sets if sticky select is enabled or not.
virtual void SetStickySelect(bool enabled) = 0;
//! Get default Editor Camera Position.
//! Gets default Editor Camera Position.
virtual AZ::Vector3 DefaultEditorCameraPosition() const = 0;
//! Sets if icons are visible in the viewport.
virtual void SetIconsVisible(bool visible) = 0;
//! Sets if helpers are visible in the viewport.
virtual void SetHelpersVisible(bool visible) = 0;
};
//! This interface is used to simulate the manipulator manager while the manipulators are under test.
@@ -26,6 +26,7 @@ namespace UnitTest
using IndirectCallManipulatorViewportInteraction = AzManipulatorTestFramework::IndirectCallManipulatorViewportInteraction;
using ImmediateModeActionDispatcher = AzManipulatorTestFramework::ImmediateModeActionDispatcher;
public:
void SetUpEditorFixtureImpl() override
{
ToolsApplicationFixtureT::SetUpEditorFixtureImpl();
@@ -43,7 +44,6 @@ namespace UnitTest
ToolsApplicationFixtureT::TearDownEditorFixtureImpl();
}
public:
AzFramework::CameraState m_cameraState;
AZStd::unique_ptr<ImmediateModeActionDispatcher> m_actionDispatcher;
AZStd::unique_ptr<IndirectCallManipulatorViewportInteraction> m_viewportManipulatorInteraction;
@@ -27,29 +27,6 @@ namespace AzManipulatorTestFramework
const AZ::Vector3& position = AZ::Vector3::CreateZero(),
float radius = 1.0f);
//! Create a mouse pick from the specified ray and screen point.
AzToolsFramework::ViewportInteraction::MousePick CreateMousePick(
const AZ::Vector3& origin, const AZ::Vector3& direction, const AzFramework::ScreenPoint& screenPoint);
//! Build a mouse pick from the specified mouse position and camera state.
AzToolsFramework::ViewportInteraction::MousePick BuildMousePick(
const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState);
//! Create a mouse interaction from the specified pick, buttons, interaction id and keyboard modifiers.
AzToolsFramework::ViewportInteraction::MouseInteraction CreateMouseInteraction(
const AzToolsFramework::ViewportInteraction::MousePick& mousePick,
AzToolsFramework::ViewportInteraction::MouseButtons buttons,
AzToolsFramework::ViewportInteraction::InteractionId interactionId,
AzToolsFramework::ViewportInteraction::KeyboardModifiers modifiers);
//! Create a mouse buttons from the specified mouse button.
AzToolsFramework::ViewportInteraction::MouseButtons CreateMouseButtons(AzToolsFramework::ViewportInteraction::MouseButton button);
//! Create a mouse interaction event from the specified interaction and event.
AzToolsFramework::ViewportInteraction::MouseInteractionEvent CreateMouseInteractionEvent(
const AzToolsFramework::ViewportInteraction::MouseInteraction& mouseInteraction,
AzToolsFramework::ViewportInteraction::MouseEvent event);
//! Dispatch a mouse event to the main manipulator manager via a bus call.
void DispatchMouseInteractionEvent(const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& event);
@@ -24,9 +24,12 @@ namespace AzManipulatorTestFramework
explicit IndirectCallManipulatorViewportInteraction(AZStd::shared_ptr<AzFramework::DebugDisplayRequests> debugDisplayRequests);
~IndirectCallManipulatorViewportInteraction();
// ManipulatorViewportInteractionInterface ...
// ManipulatorViewportInteraction overrides ...
const ViewportInteractionInterface& GetViewportInteraction() const override;
const ManipulatorManagerInterface& GetManipulatorManager() const override;
// make non-const overloads visible
using ManipulatorViewportInteraction::GetViewportInteraction;
using ManipulatorViewportInteraction::GetManipulatorManager;
private:
AZStd::unique_ptr<ViewportInteraction> m_viewportInteraction;
@@ -39,7 +39,8 @@ namespace AzManipulatorTestFramework
AzFramework::ViewportId GetViewportId() const override;
void UpdateVisibility() override;
void SetStickySelect(bool enabled) override;
AZ::Vector3 DefaultEditorCameraPosition() const override;
void SetIconsVisible(bool visible) override;
void SetHelpersVisible(bool visible) override;
// ViewportInteractionRequestBus overrides ...
AzFramework::CameraState GetCameraState() override;
@@ -58,6 +59,9 @@ namespace AzManipulatorTestFramework
float ManipulatorLineBoundWidth() const override;
float ManipulatorCircleBoundWidth() const override;
bool StickySelectEnabled() const override;
AZ::Vector3 DefaultEditorCameraPosition() const override;
bool IconsVisible() const override;
bool HelpersVisible() const override;
// EditorEntityViewportInteractionRequestBus overrides ...
void FindVisibleEntities(AZStd::vector<AZ::EntityId>& visibleEntities) override;
@@ -68,10 +72,12 @@ namespace AzManipulatorTestFramework
AzFramework::EntityVisibilityQuery m_entityVisibilityQuery;
AZStd::shared_ptr<AzFramework::DebugDisplayRequests> m_debugDisplayRequests;
AzFramework::CameraState m_cameraState;
float m_gridSize = 1.0f;
float m_angularStep = 0.0f;
bool m_gridSnapping = false;
bool m_angularSnapping = false;
bool m_stickySelect = true;
float m_gridSize = 1.0f;
float m_angularStep = 0.0f;
bool m_iconsVisible = true;
bool m_helpersVisible = true;
};
} // namespace AzManipulatorTestFramework
@@ -82,49 +82,6 @@ namespace AzManipulatorTestFramework
return manipulator;
}
AzToolsFramework::ViewportInteraction::MousePick CreateMousePick(
const AZ::Vector3& origin, const AZ::Vector3& direction, const AzFramework::ScreenPoint& screenPoint)
{
return { origin, direction, screenPoint };
}
AzToolsFramework::ViewportInteraction::MousePick BuildMousePick(
const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState)
{
const auto nearPlaneWorldPosition = AzFramework::ScreenToWorld(screenPoint, cameraState);
AzToolsFramework::ViewportInteraction::MousePick mousePick;
mousePick.m_screenCoordinates = screenPoint;
mousePick.m_rayOrigin = nearPlaneWorldPosition;
mousePick.m_rayDirection = (nearPlaneWorldPosition - cameraState.m_position).GetNormalized();
return mousePick;
}
MouseInteraction CreateMouseInteraction(
const MousePick& mousePick, MouseButtons buttons, InteractionId interactionId, KeyboardModifiers modifiers)
{
AzToolsFramework::ViewportInteraction::MouseInteraction interaction;
interaction.m_mousePick = mousePick;
interaction.m_mouseButtons = buttons;
interaction.m_interactionId = interactionId;
interaction.m_keyboardModifiers = modifiers;
return interaction;
}
MouseButtons CreateMouseButtons(MouseButton button)
{
MouseButtons buttons;
buttons.m_mouseButtons = static_cast<AZ::u32>(button);
return buttons;
}
MouseInteractionEvent CreateMouseInteractionEvent(const MouseInteraction& mouseInteraction, MouseEvent event)
{
return MouseInteractionEvent(mouseInteraction, event, /*captured=*/false);
}
void DispatchMouseInteractionEvent(const MouseInteractionEvent& event)
{
AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus::Event(
@@ -120,7 +120,8 @@ namespace AzManipulatorTestFramework
void ImmediateModeActionDispatcher::MousePositionImpl(const AzFramework::ScreenPoint& position)
{
const auto cameraState = m_manipulatorViewportInteraction.GetViewportInteraction().GetCameraState();
GetMouseInteractionEvent()->m_mouseInteraction.m_mousePick = BuildMousePick(position, cameraState);
GetMouseInteractionEvent()->m_mouseInteraction.m_mousePick =
AzToolsFramework::ViewportInteraction::BuildMousePick(cameraState, position);
GetMouseInteractionEvent()->m_mouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent::Move;
m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(*m_event);
}
@@ -10,6 +10,7 @@
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzManipulatorTestFramework/ViewportInteraction.h>
#include <AzToolsFramework/Manipulators/ManipulatorBus.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
namespace AzManipulatorTestFramework
{
@@ -19,6 +20,9 @@ namespace AzManipulatorTestFramework
AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler::BusConnect(m_viewportId);
AzToolsFramework::ViewportInteraction::ViewportSettingsRequestBus::Handler::BusConnect(m_viewportId);
AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusConnect(m_viewportId);
m_cameraState =
AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize);
}
ViewportInteraction::~ViewportInteraction()
@@ -113,6 +117,16 @@ namespace AzManipulatorTestFramework
m_stickySelect = enabled;
}
void ViewportInteraction::SetIconsVisible(const bool visible)
{
m_iconsVisible = visible;
}
void ViewportInteraction::SetHelpersVisible(const bool visible)
{
m_helpersVisible = visible;
}
AZ::Vector3 ViewportInteraction::DefaultEditorCameraPosition() const
{
return {};
@@ -148,4 +162,14 @@ namespace AzManipulatorTestFramework
{
return 1.0f;
}
bool ViewportInteraction::IconsVisible() const
{
return m_iconsVisible;
}
bool ViewportInteraction::HelpersVisible() const
{
return m_helpersVisible;
}
} // namespace AzManipulatorTestFramework
@@ -8,21 +8,22 @@
#pragma once
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
#include <AzTest/AzTest.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
namespace UnitTest
{
class LinearManipulatorTestFixture
: public ToolsApplicationFixture
class LinearManipulatorTestFixture : public ToolsApplicationFixture
{
protected:
LinearManipulatorTestFixture(const AzToolsFramework::ManipulatorManagerId& manipulatorManagerId)
: m_manipulatorManagerId(manipulatorManagerId) {}
: m_manipulatorManagerId(manipulatorManagerId)
{
}
void SetUpEditorFixtureImpl() override
{
{
m_linearManipulator = AzManipulatorTestFramework::CreateLinearManipulator(
m_manipulatorManagerId,
/*position=*/AZ::Vector3::CreateZero(),
@@ -31,21 +32,21 @@ namespace UnitTest
// default sanity check call backs
m_linearManipulator->InstallLeftMouseDownCallback(
[this](const AzToolsFramework::LinearManipulator::Action& /*action*/)
{
m_receivedLeftMouseDown = true;
});
{
m_receivedLeftMouseDown = true;
});
m_linearManipulator->InstallMouseMoveCallback(
[this](const AzToolsFramework::LinearManipulator::Action& /*action*/)
{
m_receivedMouseMove = true;
});
{
m_receivedMouseMove = true;
});
m_linearManipulator->InstallLeftMouseUpCallback(
[this](const AzToolsFramework::LinearManipulator::Action& /*action*/)
{
m_receivedLeftMouseUp = true;
});
{
m_receivedLeftMouseUp = true;
});
}
void TearDownEditorFixtureImpl() override
@@ -63,16 +64,16 @@ namespace UnitTest
bool m_receivedLeftMouseUp = false;
// initial world space starting position for mouse interaction
const AzToolsFramework::ViewportInteraction::MousePick m_mouseStartingPositionRay =
AzManipulatorTestFramework::CreateMousePick(
AZ::Vector3(0.0f, -2.0f, 0.0f), AZ::Vector3(0.0f, 1.0f, 0.0f), AzFramework::ScreenPoint( 0,0 ));
const AzToolsFramework::ViewportInteraction::MousePick m_mouseStartingPositionRay{ AZ::Vector3(0.0f, -2.0f, 0.0f),
AZ::Vector3(0.0f, 1.0f, 0.0f),
AzFramework::ScreenPoint(0, 0) };
// left mouse down ray in world space 2 units back from origin looking down +y axis with a null interaction
// id and no keyboard modifiers
AzToolsFramework::ViewportInteraction::MouseInteraction m_interaction =
AzManipulatorTestFramework::CreateMouseInteraction(
AzToolsFramework::ViewportInteraction::BuildMouseInteraction(
m_mouseStartingPositionRay,
AzManipulatorTestFramework::CreateMouseButtons(AzToolsFramework::ViewportInteraction::MouseButton::Left),
AzToolsFramework::ViewportInteraction::BuildMouseButtons(AzToolsFramework::ViewportInteraction::MouseButton::Left),
AzToolsFramework::ViewportInteraction::InteractionId(AZ::EntityId(0), 0),
AzToolsFramework::ViewportInteraction::KeyboardModifiers(0));
};
@@ -7,6 +7,8 @@
*/
#include "AzManipulatorTestFrameworkTestFixtures.h"
#include <AzToolsFramework/Viewport/ViewportTypes.h>
#include <AzToolsFramework/ViewportSelection/EditorDefaultSelection.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
@@ -34,8 +36,8 @@ namespace UnitTest
TEST_F(AzManipulatorTestFrameworkBusCallTestFixture, ConsumeViewportLeftMouseClick)
{
// given a left mouse down ray in world space
auto event =
AzManipulatorTestFramework::CreateMouseInteractionEvent(m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down);
auto event = AzToolsFramework::ViewportInteraction::BuildMouseInteractionEvent(
m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down);
// consume the mouse down and up events
AzManipulatorTestFramework::DispatchMouseInteractionEvent(event);
@@ -53,8 +55,8 @@ namespace UnitTest
TEST_F(AzManipulatorTestFrameworkBusCallTestFixture, ConsumeViewportMouseMoveHover)
{
// given a left mouse down ray in world space
const auto event =
AzManipulatorTestFramework::CreateMouseInteractionEvent(m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Move);
const auto event = AzToolsFramework::ViewportInteraction::BuildMouseInteractionEvent(
m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Move);
// consume the mouse move event
AzManipulatorTestFramework::DispatchMouseInteractionEvent(event);
@@ -72,8 +74,8 @@ namespace UnitTest
TEST_F(AzManipulatorTestFrameworkBusCallTestFixture, ConsumeViewportMouseMoveActive)
{
// given a left mouse down ray in world space
auto event =
AzManipulatorTestFramework::CreateMouseInteractionEvent(m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down);
auto event = AzToolsFramework::ViewportInteraction::BuildMouseInteractionEvent(
m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down);
// consume the mouse down event
AzManipulatorTestFramework::DispatchMouseInteractionEvent(event);
@@ -113,8 +115,8 @@ namespace UnitTest
});
// given a left mouse down ray in world space
auto event =
AzManipulatorTestFramework::CreateMouseInteractionEvent(m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down);
auto event = AzToolsFramework::ViewportInteraction::BuildMouseInteractionEvent(
m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down);
// consume the mouse down event
AzManipulatorTestFramework::DispatchMouseInteractionEvent(event);
+13 -2
View File
@@ -5,9 +5,12 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <ostream>
#include <AzCore/Component/EntityId.h>
#include <AzCore/IO/Path/Path.h>
#include <ostream>
namespace AZ::IO
{
void PrintTo(const AZ::IO::PathView& path, ::std::ostream* os)
@@ -24,4 +27,12 @@ namespace AZ::IO
{
*os << "path: " << AZ::IO::FixedMaxPath(path.Native(), AZ::IO::PosixPathSeparator).MakePreferred().c_str();
}
}
} // namespace AZ::IO
namespace AZ
{
void PrintTo(const AZ::EntityId entityId, ::std::ostream* os)
{
*os << entityId.ToString().c_str();
}
} // namespace AZ
+8
View File
@@ -5,6 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <iosfwd>
@@ -37,4 +38,11 @@ namespace AZ::IO
void PrintTo(const AZ::IO::FixedMaxPath& path, ::std::ostream* os);
}
namespace AZ
{
class EntityId;
void PrintTo(AZ::EntityId entityId, ::std::ostream* os);
}
#include <AzTest/Printers.inl>
@@ -878,9 +878,6 @@ namespace AzToolsFramework
/// Return the icon texture id (from internal IconManager) for a given entity icon path.
/// This can be passed to DrawTextureLabel to draw an entity icon.
virtual int GetIconTextureIdFromEntityIconPath(const AZStd::string& entityIconPath) = 0;
/// Returns if the Display Helpers option is toggled on in the Editor.
virtual bool DisplayHelpersVisible() = 0;
};
using EditorRequestBus = AZ::EBus<EditorRequests>;
@@ -24,7 +24,7 @@ namespace AzToolsFramework
{
if (auto serializationContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializationContext->Class<ProceduralPrefabSystemComponent>();
serializationContext->Class<ProceduralPrefabSystemComponent, AZ::Component>();
}
}
@@ -89,8 +89,8 @@ namespace AzToolsFramework
const float rayLength)
{
AZ_Assert(rayLength > 0.0f, "Invalid ray length passed to RefreshRayRequest");
rayRequest.m_startWorldPosition = viewportRay.origin;
rayRequest.m_endWorldPosition = viewportRay.origin + viewportRay.direction * rayLength;
rayRequest.m_startWorldPosition = viewportRay.m_origin;
rayRequest.m_endWorldPosition = viewportRay.m_origin + viewportRay.m_direction * rayLength;
}
AZ::Vector3 FindClosestPickIntersection(
@@ -15,7 +15,6 @@
#include <AzFramework/Viewport/CameraState.h>
#include <AzFramework/Viewport/ClickDetector.h>
#include <AzFramework/Viewport/ViewportId.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Viewport/ViewportTypes.h>
@@ -151,13 +150,6 @@ namespace AzToolsFramework
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
};
//! A ray projection, originating from a point and extending in a direction specified as a normal.
struct ProjectedViewportRay
{
AZ::Vector3 origin;
AZ::Vector3 direction;
};
//! Requests that can be made to the viewport to query and modify its state.
class ViewportInteractionRequests
{
@@ -183,15 +175,6 @@ namespace AzToolsFramework
//! Type to inherit to implement ViewportInteractionRequests.
using ViewportInteractionRequestBus = AZ::EBus<ViewportInteractionRequests, ViewportEBusTraits>;
//! Utility function to return a viewport ray.
inline ProjectedViewportRay ViewportScreenToWorldRay(
const AzFramework::CameraState& cameraState, const AzFramework::ScreenPoint& screenPoint)
{
const AZ::Vector3 rayOrigin = AzFramework::ScreenToWorld(screenPoint, cameraState);
const AZ::Vector3 rayDirection = (rayOrigin - cameraState.m_position).GetNormalized();
return AzToolsFramework::ViewportInteraction::ProjectedViewportRay{ rayOrigin, rayDirection };
}
//! Utility function to return a viewport ray using the ViewportInteractionRequestBus.
inline ProjectedViewportRay ViewportScreenToWorldRay(
const AzFramework::ViewportId viewportId, const AzFramework::ScreenPoint& screenPoint)
@@ -225,6 +208,10 @@ namespace AzToolsFramework
virtual bool StickySelectEnabled() const = 0;
//! Returns the default viewport camera position.
virtual AZ::Vector3 DefaultEditorCameraPosition() const = 0;
//! Returns if icons are visible in the viewport.
virtual bool IconsVisible() const = 0;
//! Returns if viewport helpers (additional debug drawing) are visible in the viewport.
virtual bool HelpersVisible() const = 0;
protected:
~ViewportSettingsRequests() = default;
@@ -20,6 +20,8 @@ namespace AzToolsFramework
constexpr AZStd::string_view ScaleManipulatorBoxHalfExtentSetting = "/Amazon/Preferences/Editor/Manipulator/ScaleManipulatorBoxHalfExtent";
constexpr AZStd::string_view RotationManipulatorRadiusSetting = "/Amazon/Preferences/Editor/Manipulator/RotationManipulatorRadius";
constexpr AZStd::string_view ManipulatorViewBaseScaleSetting = "/Amazon/Preferences/Editor/Manipulator/ViewBaseScale";
constexpr AZStd::string_view IconsVisibleSetting = "/Amazon/Preferences/Editor/IconsVisible";
constexpr AZStd::string_view HelpersVisibleSetting = "/Amazon/Preferences/Editor/HelpersVisible";
bool FlipManipulatorAxesTowardsView()
{
@@ -120,4 +122,24 @@ namespace AzToolsFramework
{
SetRegistry(ManipulatorViewBaseScaleSetting, scale);
}
bool IconsVisible()
{
return GetRegistry(IconsVisibleSetting, true);
}
void SetIconsVisible(const bool visible)
{
SetRegistry(IconsVisibleSetting, visible);
}
bool HelpersVisible()
{
return GetRegistry(HelpersVisibleSetting, true);
}
void SetHelpersVisible(const bool visible)
{
SetRegistry(HelpersVisibleSetting, visible);
}
} // namespace AzToolsFramework
@@ -66,4 +66,10 @@ namespace AzToolsFramework
float ManipulatorViewBaseScale();
void SetManipulatorViewBaseScale(float scale);
bool IconsVisible();
void SetIconsVisible(bool visible);
bool HelpersVisible();
void SetHelpersVisible(bool visible);
} // namespace AzToolsFramework
@@ -49,5 +49,22 @@ namespace AzToolsFramework
->Field("MouseEvent", &MouseInteractionEvent::m_mouseEvent)
->Field("WheelDelta", &MouseInteractionEvent::m_wheelDelta);
}
MouseInteraction BuildMouseInteraction(
const MousePick& mousePick, const MouseButtons buttons, const InteractionId interactionId, const KeyboardModifiers modifiers)
{
MouseInteraction interaction;
interaction.m_mousePick = mousePick;
interaction.m_mouseButtons = buttons;
interaction.m_interactionId = interactionId;
interaction.m_keyboardModifiers = modifiers;
return interaction;
}
MouseInteractionEvent BuildMouseInteractionEvent(
const MouseInteraction& mouseInteraction, const MouseEvent event, const bool cursorCaptured /*= false*/)
{
return MouseInteractionEvent(mouseInteraction, event, cursorCaptured);
}
} // namespace ViewportInteraction
} // namespace AzToolsFramework
@@ -8,11 +8,11 @@
#pragma once
#include "AzFramework/Viewport/ScreenGeometry.h"
#include <AzCore/Component/EntityId.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzFramework/Viewport/CameraState.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <QPoint>
@@ -20,7 +20,7 @@ namespace AZ
{
class ReflectContext;
class SerializeContext;
}
} // namespace AZ
namespace AzToolsFramework
{
@@ -178,6 +178,12 @@ namespace AzToolsFramework
//! @cond
AZ_TYPE_INFO(MousePick, "{A69B9562-FC8C-4DE7-9137-0FF867B1513D}");
MousePick() = default;
MousePick(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, const AzFramework::ScreenPoint& screenPoint)
: m_rayOrigin(rayOrigin)
, m_rayDirection(rayDirection)
, m_screenCoordinates(screenPoint)
{
}
//! @endcond
AZ::Vector3 m_rayOrigin = AZ::Vector3::CreateZero(); //!< World space.
@@ -249,6 +255,22 @@ namespace AzToolsFramework
return mouseInteractionEvent.m_wheelDelta;
}
//! A ray projection, originating from a point and extending in a direction specified as a normal.
struct ProjectedViewportRay
{
AZ::Vector3 m_origin;
AZ::Vector3 m_direction;
};
//! Utility function to return a viewport ray.
inline ProjectedViewportRay ViewportScreenToWorldRay(
const AzFramework::CameraState& cameraState, const AzFramework::ScreenPoint& screenPoint)
{
const AZ::Vector3 rayOrigin = AzFramework::ScreenToWorld(screenPoint, cameraState);
const AZ::Vector3 rayDirection = (rayOrigin - cameraState.m_position).GetNormalized();
return ProjectedViewportRay{ rayOrigin, rayDirection };
}
//! Return QPoint from AzFramework::ScreenPoint.
inline QPoint QPointFromScreenPoint(const AzFramework::ScreenPoint& screenPoint)
{
@@ -301,6 +323,27 @@ namespace AzToolsFramework
return mouseButtons;
}
//! Build a mouse pick from the specified mouse position and camera state.
inline MousePick BuildMousePick(const AzFramework::CameraState& cameraState, const AzFramework::ScreenPoint& screenPoint)
{
const auto ray = ViewportScreenToWorldRay(cameraState, screenPoint);
return MousePick(ray.m_origin, ray.m_direction, screenPoint);
}
//! Create a mouse interaction from the specified pick, buttons, interaction id and keyboard modifiers.
MouseInteraction BuildMouseInteraction(
const MousePick& mousePick, MouseButtons buttons, InteractionId interactionId, KeyboardModifiers modifiers);
//! Create a mouse buttons from the specified mouse button.
inline MouseButtons BuildMouseButtons(const MouseButton button)
{
return MouseButtons(aznumeric_cast<AZ::u32>(button));
}
//! Create a mouse interaction event from the specified interaction and event.
MouseInteractionEvent BuildMouseInteractionEvent(
const MouseInteraction& mouseInteraction, MouseEvent event, bool cursorCaptured = false);
//! Reflect all viewport related types.
void ViewportInteractionReflect(AZ::ReflectContext* context);
} // namespace ViewportInteraction
@@ -52,34 +52,45 @@ AZ_CVAR(
AZ::ConsoleFunctorFlags::Null,
"Use a lock icon when the cursor is over entities that cannot be interacted with");
AZ_CVAR(float, ed_iconMinScale, 0.1f, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum scale for icons in the distance");
AZ_CVAR(float, ed_iconMaxScale, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum scale for icons near the camera");
AZ_CVAR(float, ed_iconCloseDist, 3.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Distance at which icons are at maximum scale");
AZ_CVAR(float, ed_iconFarDist, 40.f, nullptr, AZ::ConsoleFunctorFlags::Null, "Distance at which icons are at minimum scale");
namespace AzToolsFramework
{
AZ_CLASS_ALLOCATOR_IMPL(EditorHelpers, AZ::SystemAllocator, 0)
static const int s_iconSize = 36; // icon display size (in pixels)
static const float s_iconMinScale = 0.1f; // minimum scale for icons in the distance
static const float s_iconMaxScale = 1.0f; // maximum scale for icons near the camera
static const float s_iconCloseDist = 3.f; // distance at which icons are at maximum scale
static const float s_iconFarDist = 40.f; // distance at which icons are at minimum scale
static const int IconSize = 36; // icon display size (in pixels)
// helper function to wrap EBus call to check if helpers are being displayed
// note: the ['?'] icon in the top right of the editor
static bool HelpersVisible()
static bool HelpersVisible(const AzFramework::ViewportId viewportId)
{
bool helpersVisible = false;
EditorRequestBus::BroadcastResult(helpersVisible, &EditorRequests::DisplayHelpersVisible);
ViewportInteraction::ViewportSettingsRequestBus::EventResult(
helpersVisible, viewportId, &ViewportInteraction::ViewportSettingsRequestBus::Events::HelpersVisible);
return helpersVisible;
}
// calculate the icon scale based on how far away it is (distanceSq) from a given point
// note: this is mostly likely distance from the camera
static float GetIconScale(const float distSq)
// helper function to wrap EBus call to check if icons are being displayed
static bool IconsVisible(const AzFramework::ViewportId viewportId)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
bool iconsVisible = false;
ViewportInteraction::ViewportSettingsRequestBus::EventResult(
iconsVisible, viewportId, &ViewportInteraction::ViewportSettingsRequestBus::Events::IconsVisible);
return iconsVisible;
}
return s_iconMinScale +
(s_iconMaxScale - s_iconMinScale) *
(1.0f - AZ::GetClamp(AZ::GetMax(0.0f, sqrtf(distSq) - s_iconCloseDist) / s_iconFarDist, 0.0f, 1.0f));
float GetIconScale(const float distance)
{
return ed_iconMinScale +
(ed_iconMaxScale - ed_iconMinScale) *
(1.0f - AZ::GetClamp(AZ::GetMax(0.0f, distance - ed_iconCloseDist) / (ed_iconFarDist - ed_iconCloseDist), 0.0f, 1.0f));
}
float GetIconSize(const float distance)
{
return GetIconScale(distance) * IconSize;
}
static void DisplayComponents(
@@ -171,11 +182,14 @@ namespace AzToolsFramework
const int viewportId = mouseInteraction.m_mouseInteraction.m_interactionId.m_viewportId;
const bool helpersVisible = HelpersVisible();
const bool iconsVisible = IconsVisible(viewportId);
const AZ::Matrix3x4 cameraView = AzFramework::CameraView(cameraState);
const AZ::Matrix4x4 cameraProjection = AzFramework::CameraProjection(cameraState);
// selecting new entities
AZ::EntityId entityIdUnderCursor;
float closestDistance = std::numeric_limits<float>::max();
float closestDistance = AZStd::numeric_limits<float>::max();
for (size_t entityCacheIndex = 0; entityCacheIndex < m_entityDataCache->VisibleEntityDataCount(); ++entityCacheIndex)
{
const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex);
@@ -185,8 +199,7 @@ namespace AzToolsFramework
continue;
}
// 2d screen space selection - did we click an icon
if (helpersVisible)
if (iconsVisible)
{
// some components choose to hide their icons (e.g. meshes)
// we also do not want to test against icons that may not be showing as they're inside a 'closed' entity container
@@ -197,17 +210,23 @@ namespace AzToolsFramework
const AZ::Vector3& entityPosition = m_entityDataCache->GetVisibleEntityPosition(entityCacheIndex);
// selecting based on 2d icon - should only do it when visible and not selected
const AzFramework::ScreenPoint screenPosition = AzFramework::WorldToScreen(entityPosition, cameraState);
const AZ::Vector3 ndcPoint = AzFramework::WorldToScreenNdc(entityPosition, cameraView, cameraProjection);
const AzFramework::ScreenPoint screenPosition =
AzFramework::ScreenPointFromNdc(AZ::Vector3ToVector2(ndcPoint), cameraState.m_viewportSize);
const float distSqFromCamera = cameraState.m_position.GetDistanceSq(entityPosition);
const auto iconRange = static_cast<float>(GetIconScale(distSqFromCamera) * s_iconSize * 0.5f);
const float distanceFromCamera = cameraState.m_position.GetDistance(entityPosition);
const auto iconRange = GetIconSize(distanceFromCamera) * 0.5f;
const auto screenCoords = mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates;
// 2d screen space selection - did we click an icon
if (screenCoords.m_x >= screenPosition.m_x - iconRange && screenCoords.m_x <= screenPosition.m_x + iconRange &&
screenCoords.m_y >= screenPosition.m_y - iconRange && screenCoords.m_y <= screenPosition.m_y + iconRange)
screenCoords.m_y >= screenPosition.m_y - iconRange && screenCoords.m_y <= screenPosition.m_y + iconRange &&
ndcPoint.GetZ() < closestDistance)
{
// use ndc z value for distance here which is in 0-1 range so will most likely 'win' when it comes to the
// distance check (this is what we want as the cursor should always favor icons if they are hovered)
closestDistance = ndcPoint.GetZ();
entityIdUnderCursor = entityId;
break;
}
}
}
@@ -220,9 +239,14 @@ namespace AzToolsFramework
if (AabbIntersectMouseRay(mouseInteraction.m_mouseInteraction, aabb))
{
// if success, pick against specific component
if (PickEntity(entityId, mouseInteraction.m_mouseInteraction, closestDistance, viewportId))
float closestBoundDifference = AZStd::numeric_limits<float>::max();
if (PickEntity(entityId, mouseInteraction.m_mouseInteraction, closestBoundDifference, viewportId))
{
entityIdUnderCursor = entityId;
if (closestBoundDifference < closestDistance)
{
closestDistance = closestBoundDifference;
entityIdUnderCursor = entityId;
}
}
}
}
@@ -276,55 +300,78 @@ namespace AzToolsFramework
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
if (HelpersVisible())
const bool iconsVisible = IconsVisible(viewportInfo.m_viewportId);
const bool helpersVisible = HelpersVisible(viewportInfo.m_viewportId);
auto displayCheck = [this](const size_t entityCacheIndex, const AZ::EntityId entityId)
{
if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex) || !IsSelectableInViewport(entityId))
{
return false;
}
return true;
};
if (helpersVisible)
{
for (size_t entityCacheIndex = 0; entityCacheIndex < m_entityDataCache->VisibleEntityDataCount(); ++entityCacheIndex)
{
const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex);
if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex) || !IsSelectableInViewport(entityId))
if (const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex);
displayCheck(entityCacheIndex, entityId))
{
continue;
// notify components to display
DisplayComponents(entityId, viewportInfo, debugDisplay);
}
}
}
// notify components to display
DisplayComponents(entityId, viewportInfo, debugDisplay);
if (iconsVisible)
{
auto editorViewportIconDisplay = EditorViewportIconDisplay::Get();
if (!editorViewportIconDisplay)
{
return;
}
if (m_entityDataCache->IsVisibleEntityIconHidden(entityCacheIndex) ||
(m_entityDataCache->IsVisibleEntitySelected(entityCacheIndex) && !showIconCheck(entityId)))
for (size_t entityCacheIndex = 0; entityCacheIndex < m_entityDataCache->VisibleEntityDataCount(); ++entityCacheIndex)
{
if (const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex);
displayCheck(entityCacheIndex, entityId))
{
continue;
}
int iconTextureId = 0;
EditorEntityIconComponentRequestBus::EventResult(
iconTextureId, entityId, &EditorEntityIconComponentRequests::GetEntityIconTextureId);
const AZ::Vector3& entityPosition = m_entityDataCache->GetVisibleEntityPosition(entityCacheIndex);
const float distSqFromCamera = cameraState.m_position.GetDistanceSq(entityPosition);
const float iconScale = GetIconScale(distSqFromCamera);
const float iconSize = s_iconSize * iconScale;
using ComponentEntityAccentType = Components::EditorSelectionAccentSystemComponent::ComponentEntityAccentType;
const AZ::Color iconHighlight = [this, entityCacheIndex]()
{
if (m_entityDataCache->IsVisibleEntityLocked(entityCacheIndex))
if (m_entityDataCache->IsVisibleEntityIconHidden(entityCacheIndex) ||
(m_entityDataCache->IsVisibleEntitySelected(entityCacheIndex) && !showIconCheck(entityId)))
{
return AZ::Color(AZ::u8(100), AZ::u8(100), AZ::u8(100), AZ::u8(255));
continue;
}
if (m_entityDataCache->GetVisibleEntityAccent(entityCacheIndex) == ComponentEntityAccentType::Hover)
int iconTextureId = 0;
EditorEntityIconComponentRequestBus::EventResult(
iconTextureId, entityId, &EditorEntityIconComponentRequests::GetEntityIconTextureId);
using ComponentEntityAccentType = Components::EditorSelectionAccentSystemComponent::ComponentEntityAccentType;
const AZ::Color iconHighlight = [this, entityCacheIndex]()
{
return AZ::Color(AZ::u8(255), AZ::u8(120), AZ::u8(0), AZ::u8(204));
}
if (m_entityDataCache->IsVisibleEntityLocked(entityCacheIndex))
{
return AZ::Color(AZ::u8(100), AZ::u8(100), AZ::u8(100), AZ::u8(255));
}
return AZ::Color(1.0f, 1.0f, 1.0f, 1.0f);
}();
if (m_entityDataCache->GetVisibleEntityAccent(entityCacheIndex) == ComponentEntityAccentType::Hover)
{
return AZ::Color(AZ::u8(255), AZ::u8(120), AZ::u8(0), AZ::u8(204));
}
EditorViewportIconDisplay::Get()->DrawIcon({ viewportInfo.m_viewportId, iconTextureId, iconHighlight, entityPosition,
EditorViewportIconDisplayInterface::CoordinateSpace::WorldSpace,
AZ::Vector2{ iconSize, iconSize } });
return AZ::Color(1.0f, 1.0f, 1.0f, 1.0f);
}();
const AZ::Vector3& entityPosition = m_entityDataCache->GetVisibleEntityPosition(entityCacheIndex);
const float distanceFromCamera = cameraState.m_position.GetDistance(entityPosition);
const float iconSize = GetIconSize(distanceFromCamera);
editorViewportIconDisplay->DrawIcon({ viewportInfo.m_viewportId, iconTextureId, iconHighlight, entityPosition,
EditorViewportIconDisplayInterface::CoordinateSpace::WorldSpace,
AZ::Vector2{ iconSize, iconSize } });
}
}
}
}
@@ -106,4 +106,12 @@ namespace AzToolsFramework
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers.
const FocusModeInterface* m_focusModeInterface = nullptr; //!< API to interact with focus mode functionality.
};
//! Calculate the icon scale based on how far away it is from a given point.
//! @note This is mostly likely distance from the camera.
float GetIconScale(float distance);
//! Calculate the icon size based on how far away it is from a given point.
//! @note This is the base icon size multiplied by the icon scale to give a final viewport size.
float GetIconSize(float distance);
} // namespace AzToolsFramework
@@ -2521,10 +2521,9 @@ namespace AzToolsFramework
AZStd::erase_if(
m_selectedEntityIds,
[readOnlyEntityPublicInterface](auto entityId)
{
return readOnlyEntityPublicInterface->IsReadOnly(entityId);
}
);
{
return readOnlyEntityPublicInterface->IsReadOnly(entityId);
});
}
// note: create/destroy pattern to be addressed
@@ -3285,9 +3284,9 @@ namespace AzToolsFramework
QObject::connect(
action, &QAction::triggered, action,
[this]
{
ToggleCenterPivotSelection();
});
{
ToggleCenterPivotSelection();
});
}
}
@@ -32,6 +32,8 @@ namespace AzToolsFramework
constexpr inline AZ::Crc32 EditReset = AZ_CRC_CE("com.o3de.action.editortransform.editreset");
constexpr inline AZ::Crc32 EditResetManipulator = AZ_CRC_CE("com.o3de.action.editortransform.editresetmanipulator");
constexpr inline AZ::Crc32 ViewportUiVisible = AZ_CRC_CE("com.o3de.action.editortransform.viewportuivisible");
constexpr inline AZ::Crc32 Helpers = AZ_CRC_CE("com.o3de.action.editor.helpers");
constexpr inline AZ::Crc32 Icons = AZ_CRC_CE("com.o3de.action.editor.icons");
//@}
//! Provide interface for EditorTransformComponentSelection requests.
@@ -40,16 +40,12 @@
#include <Tests/BoundsTestComponent.h>
namespace AZ
{
std::ostream& operator<<(std::ostream& os, const EntityId entityId)
{
return os << entityId.ToString().c_str();
}
} // namespace AZ
namespace UnitTest
{
using AzToolsFramework::ViewportInteraction::BuildMouseButtons;
using AzToolsFramework::ViewportInteraction::BuildMouseInteraction;
using AzToolsFramework::ViewportInteraction::BuildMousePick;
AzToolsFramework::EntityIdList SelectedEntities()
{
AzToolsFramework::EntityIdList selectedEntitiesBefore;
@@ -137,6 +133,18 @@ namespace UnitTest
AzToolsFramework::EntityIdList m_entityIds;
};
AZ::EntityId CreateEntityWithBounds(const char* entityName)
{
AZ::Entity* entity = nullptr;
AZ::EntityId entityId = CreateDefaultEditorEntity(entityName, &entity);
entity->Deactivate();
entity->CreateComponent<BoundsTestComponent>();
entity->Activate();
return entityId;
}
class EditorTransformComponentSelectionViewportPickingFixture : public ToolsApplicationFixture
{
public:
@@ -146,21 +154,9 @@ namespace UnitTest
// register a simple component implementing BoundsRequestBus and EditorComponentSelectionRequestsBus
app->RegisterComponentDescriptor(BoundsTestComponent::CreateDescriptor());
auto createEntityWithBoundsFn = [](const char* entityName)
{
AZ::Entity* entity = nullptr;
AZ::EntityId entityId = CreateDefaultEditorEntity(entityName, &entity);
entity->Deactivate();
entity->CreateComponent<BoundsTestComponent>();
entity->Activate();
return entityId;
};
m_entityId1 = createEntityWithBoundsFn("Entity1");
m_entityId2 = createEntityWithBoundsFn("Entity2");
m_entityId3 = createEntityWithBoundsFn("Entity3");
m_entityId1 = CreateEntityWithBounds("Entity1");
m_entityId2 = CreateEntityWithBounds("Entity2");
m_entityId3 = CreateEntityWithBounds("Entity3");
}
void PositionEntities()
@@ -959,6 +955,9 @@ namespace UnitTest
const auto entity2ScreenPosition = AzFramework::WorldToScreen(AzToolsFramework::GetWorldTranslation(m_entityId2), m_cameraState);
// ensure icons are not enabled to avoid them interfering with bound detection
m_viewportManipulatorInteraction->GetViewportInteraction().SetIconsVisible(false);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(true)
->CameraState(m_cameraState)
@@ -974,6 +973,137 @@ namespace UnitTest
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
}
// entity can be selected using icon
TEST_F(EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, CursorOverEntityIconReturnsThatEntityId)
{
const AZ::EntityId boundlessEntityId = CreateDefaultEditorEntity("BoundlessEntity");
// camera (go to position format) -5.00, -8.00, 5.00, 0.00, 0.00
AzFramework::SetCameraTransform(m_cameraState, AZ::Transform::CreateTranslation(AZ::Vector3(-5.0f, -8.0f, 5.0f)));
// position entity in the world
AZ::TransformBus::Event(boundlessEntityId, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3(-5.0f, -1.0f, 5.0f));
const float distanceFromCamera = m_cameraState.m_position.GetDistance(AzToolsFramework::GetWorldTranslation(boundlessEntityId));
const auto quaterIconSize = AzToolsFramework::GetIconSize(distanceFromCamera) * 0.25f;
const auto entity1ScreenPosition =
AzFramework::WorldToScreen(AzToolsFramework::GetWorldTranslation(boundlessEntityId), m_cameraState) +
AzFramework::ScreenVectorFromVector2(AZ::Vector2(quaterIconSize));
AzToolsFramework::EditorVisibleEntityDataCache editorVisibleEntityDataCache;
AzToolsFramework::EditorHelpers editorHelpers(&editorVisibleEntityDataCache);
const auto viewportId = m_viewportManipulatorInteraction->GetViewportInteraction().GetViewportId();
const auto mousePick = BuildMousePick(m_cameraState, entity1ScreenPosition);
const auto mouseInteraction = BuildMouseInteraction(
mousePick, BuildMouseButtons(AzToolsFramework::ViewportInteraction::MouseButton::None),
AzToolsFramework::ViewportInteraction::InteractionId(AZ::EntityId(), viewportId),
AzToolsFramework::ViewportInteraction::KeyboardModifiers());
const auto mouseInteractionEvent = AzToolsFramework::ViewportInteraction::BuildMouseInteractionEvent(
mouseInteraction, AzToolsFramework::ViewportInteraction::MouseEvent::Move, false);
// mimic mouse move
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(entity1ScreenPosition);
// simulate hovering over an icon in the viewport
editorVisibleEntityDataCache.CalculateVisibleEntityDatas(AzFramework::ViewportInfo{ viewportId });
auto entityIdUnderCursor = editorHelpers.FindEntityIdUnderCursor(m_cameraState, mouseInteractionEvent);
using ::testing::Eq;
EXPECT_THAT(entityIdUnderCursor.EntityIdUnderCursor(), Eq(boundlessEntityId));
}
// overlapping icons, nearest is detected
TEST_F(EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, CursorOverOverlappingEntityIconsReturnsClosestEntityId)
{
const AZ::EntityId boundlessEntityId1 = CreateDefaultEditorEntity("BoundlessEntity1");
const AZ::EntityId boundlessEntityId2 = CreateDefaultEditorEntity("BoundlessEntity2");
// camera (go to position format) -5.00, -8.00, 5.00, 0.00, 0.00
AzFramework::SetCameraTransform(m_cameraState, AZ::Transform::CreateTranslation(AZ::Vector3(-5.0f, -8.0f, 5.0f)));
// position entities in the world
AZ::TransformBus::Event(boundlessEntityId1, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3(-5.0f, -1.0f, 5.0f));
// note: boundlessEntityId2 is closer to the camera
AZ::TransformBus::Event(boundlessEntityId2, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3(-5.0f, -3.0f, 5.0f));
const float distanceFromCamera = m_cameraState.m_position.GetDistance(AzToolsFramework::GetWorldTranslation(boundlessEntityId2));
const auto quaterIconSize = AzToolsFramework::GetIconSize(distanceFromCamera) * 0.25f;
const auto entity2ScreenPosition =
AzFramework::WorldToScreen(AzToolsFramework::GetWorldTranslation(boundlessEntityId2), m_cameraState) +
AzFramework::ScreenVectorFromVector2(AZ::Vector2(quaterIconSize));
AzToolsFramework::EditorVisibleEntityDataCache editorVisibleEntityDataCache;
AzToolsFramework::EditorHelpers editorHelpers(&editorVisibleEntityDataCache);
const auto viewportId = m_viewportManipulatorInteraction->GetViewportInteraction().GetViewportId();
const auto mousePick = BuildMousePick(m_cameraState, entity2ScreenPosition);
const auto mouseInteraction = BuildMouseInteraction(
mousePick, BuildMouseButtons(AzToolsFramework::ViewportInteraction::MouseButton::None),
AzToolsFramework::ViewportInteraction::InteractionId(AZ::EntityId(), viewportId),
AzToolsFramework::ViewportInteraction::KeyboardModifiers());
const auto mouseInteractionEvent = AzToolsFramework::ViewportInteraction::BuildMouseInteractionEvent(
mouseInteraction, AzToolsFramework::ViewportInteraction::MouseEvent::Move, false);
// mimic mouse move
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(entity2ScreenPosition);
// simulate hovering over an icon in the viewport
editorVisibleEntityDataCache.CalculateVisibleEntityDatas(AzFramework::ViewportInfo{ viewportId });
auto entityIdUnderCursor = editorHelpers.FindEntityIdUnderCursor(m_cameraState, mouseInteractionEvent);
using ::testing::Eq;
EXPECT_THAT(entityIdUnderCursor.EntityIdUnderCursor(), Eq(boundlessEntityId2));
}
// if an entity with an icon is behind an entity with a bound, the entity with the icon will be selected
// even if the bound is closer (this is because icons are treated as if they are on the near clip plane)
TEST_F(
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, FurtherAwayEntityWithIconReturnedWhenBoundEntityIsInFront)
{
const AZ::EntityId boundEntityId = CreateEntityWithBounds("BoundEntity");
const AZ::EntityId boundlessEntityId = CreateDefaultEditorEntity("BoundlessEntity");
auto* boundTestComponent = AzToolsFramework::GetEntityById(boundEntityId)->FindComponent<BoundsTestComponent>();
boundTestComponent->m_localBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.5f, -0.5f, -0.5f), AZ::Vector3(1.5f, 0.5, 0.5f));
// camera (go to position format) -5.00, -8.00, 5.00, 0.00, 0.00
AzFramework::SetCameraTransform(m_cameraState, AZ::Transform::CreateTranslation(AZ::Vector3(-5.0f, -8.0f, 5.0f)));
// position entities in the world
AZ::TransformBus::Event(boundEntityId, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3(-4.0f, -3.0f, 5.0f));
// note: boundlessEntityId2 is closer to the camera
AZ::TransformBus::Event(boundlessEntityId, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3(-5.0f, -1.0f, 5.0f));
const float distanceFromCamera = m_cameraState.m_position.GetDistance(AzToolsFramework::GetWorldTranslation(boundlessEntityId));
const auto quaterIconSize = AzToolsFramework::GetIconSize(distanceFromCamera) * 0.25f;
const auto entity2ScreenPosition =
AzFramework::WorldToScreen(AzToolsFramework::GetWorldTranslation(boundlessEntityId), m_cameraState) +
AzFramework::ScreenVectorFromVector2(AZ::Vector2(quaterIconSize));
AzToolsFramework::EditorVisibleEntityDataCache editorVisibleEntityDataCache;
AzToolsFramework::EditorHelpers editorHelpers(&editorVisibleEntityDataCache);
const auto viewportId = m_viewportManipulatorInteraction->GetViewportInteraction().GetViewportId();
const auto mousePick = BuildMousePick(m_cameraState, entity2ScreenPosition);
const auto mouseInteraction = BuildMouseInteraction(
mousePick, BuildMouseButtons(AzToolsFramework::ViewportInteraction::MouseButton::None),
AzToolsFramework::ViewportInteraction::InteractionId(AZ::EntityId(), viewportId),
AzToolsFramework::ViewportInteraction::KeyboardModifiers());
const auto mouseInteractionEvent = AzToolsFramework::ViewportInteraction::BuildMouseInteractionEvent(
mouseInteraction, AzToolsFramework::ViewportInteraction::MouseEvent::Move, false);
// mimic mouse move
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(entity2ScreenPosition);
// simulate hovering over an icon in the viewport
editorVisibleEntityDataCache.CalculateVisibleEntityDatas(AzFramework::ViewportInfo{ viewportId });
auto entityIdUnderCursor = editorHelpers.FindEntityIdUnderCursor(m_cameraState, mouseInteractionEvent);
using ::testing::Eq;
EXPECT_THAT(entityIdUnderCursor.EntityIdUnderCursor(), Eq(boundlessEntityId));
}
class EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam
: public EditorTransformComponentSelectionViewportPickingManipulatorTestFixture
, public ::testing::WithParamInterface<bool>
@@ -1709,8 +1839,9 @@ namespace UnitTest
using MouseInteractionResult = AzToolsFramework::ViewportInteraction::MouseInteractionResult;
public:
WheelEventWidget(QWidget* parent = nullptr)
WheelEventWidget(const AzFramework::ViewportId viewportId, QWidget* parent = nullptr)
: QWidget(parent)
, m_viewportId(viewportId)
{
}
@@ -1719,7 +1850,7 @@ namespace UnitTest
namespace vi = AzToolsFramework::ViewportInteraction;
vi::MouseInteraction mouseInteraction;
mouseInteraction.m_interactionId.m_cameraId = AZ::EntityId();
mouseInteraction.m_interactionId.m_viewportId = 0;
mouseInteraction.m_interactionId.m_viewportId = m_viewportId;
mouseInteraction.m_mouseButtons = vi::BuildMouseButtons(ev->buttons());
mouseInteraction.m_mousePick = vi::MousePick();
mouseInteraction.m_keyboardModifiers = vi::BuildKeyboardModifiers(ev->modifiers());
@@ -1731,15 +1862,15 @@ namespace UnitTest
}
MouseInteractionResult m_mouseInteractionResult;
AzFramework::ViewportId m_viewportId;
};
TEST_F(EditorTransformComponentSelectionFixture, MouseScrollWheelSwitchesTransformMode)
TEST_F(EditorTransformComponentSelectionManipulatorTestFixture, MouseScrollWheelSwitchesTransformMode)
{
using ::testing::Eq;
namespace vi = AzToolsFramework::ViewportInteraction;
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
const auto transformMode = []()
const auto transformMode = []
{
EditorTransformComponentSelectionRequestBus::Events::Mode transformMode;
EditorTransformComponentSelectionRequestBus::EventResult(
@@ -1752,7 +1883,7 @@ namespace UnitTest
// preconditions
EXPECT_THAT(transformMode(), EditorTransformComponentSelectionRequestBus::Events::Mode::Translation);
auto wheelEventWidget = WheelEventWidget();
auto wheelEventWidget = WheelEventWidget(m_viewportManipulatorInteraction->GetViewportInteraction().GetViewportId());
// attach the global event filter to the placeholder widget
AzQtComponents::GlobalEventFilter globalEventFilter(QApplication::instance());
wheelEventWidget.installEventFilter(&globalEventFilter);
@@ -1768,6 +1899,7 @@ namespace UnitTest
// then
// transform mode has changed and mouse event was handled
using ::testing::Eq;
EXPECT_THAT(transformMode(), Eq(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation));
EXPECT_THAT(wheelEventWidget.m_mouseInteractionResult, Eq(vi::MouseInteractionResult::Viewport));
}
@@ -3100,7 +3232,7 @@ namespace UnitTest
const AZ::Transform finalEntityTransform = AzToolsFramework::GetWorldTransform(m_entityIdBox);
const auto viewportRay = AzToolsFramework::ViewportInteraction::ViewportScreenToWorldRay(m_cameraState, initialPositionScreen);
const auto distanceAway = (finalEntityTransform.GetTranslation() - viewportRay.origin).GetLength();
const auto distanceAway = (finalEntityTransform.GetTranslation() - viewportRay.m_origin).GetLength();
// ensure final world positions match
EXPECT_THAT(finalEntityTransform, IsCloseTolerance(finalTransformWorld, 0.01f));
@@ -62,7 +62,6 @@ namespace UnitTest
void BrowseForAssets(AssetBrowser::AssetSelectionModel& /*selection*/) override {}
int GetIconTextureIdFromEntityIconPath(const AZStd::string& entityIconPath) override { AZ_UNUSED(entityIconPath); return 0; }
bool DisplayHelpersVisible() override { return false; }
void GoToSelectedEntitiesInViewports() override
{
@@ -8,12 +8,12 @@
#include <Tests/FocusMode/EditorFocusModeSelectionFixture.h>
namespace AzToolsFramework
namespace UnitTest
{
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithNoContainers)
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionFindHighestSelectableEntityWithNoContainers)
{
// When no containers are in the way, the function will just return the entityId of the entity that was clicked.
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
@@ -23,7 +23,7 @@ namespace AzToolsFramework
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithClosedContainer)
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionFindHighestSelectableEntityWithClosedContainer)
{
// If a closed container is an ancestor of the queried entity, the closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); // Containers are closed by default
@@ -40,7 +40,7 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithOpenContainer)
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionFindHighestSelectableEntityWithOpenContainer)
{
// If a closed container is an ancestor of the queried entity, the closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
@@ -58,7 +58,7 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithMultipleClosedContainers)
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionFindHighestSelectableEntityWithMultipleClosedContainers)
{
// If multiple closed containers are ancestors of the queried entity, the highest closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
@@ -77,7 +77,7 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithMultipleContainers)
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionFindHighestSelectableEntityWithMultipleContainers)
{
// If multiple containers are ancestors of the queried entity, the highest closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
@@ -96,4 +96,4 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
}
}
} // namespace UnitTest
@@ -8,9 +8,9 @@
#include <Tests/FocusMode/EditorFocusModeFixture.h>
namespace AzToolsFramework
namespace UnitTest
{
TEST_F(EditorFocusModeFixture, ContainerEntityTests_Register)
TEST_F(EditorFocusModeFixture, ContainerEntityRegister)
{
// Registering an entity is successful.
auto outcome = m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
@@ -20,7 +20,7 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_RegisterTwice)
TEST_F(EditorFocusModeFixture, ContainerEntityRegisterTwice)
{
// Registering an entity twice fails.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
@@ -31,7 +31,7 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_Unregister)
TEST_F(EditorFocusModeFixture, ContainerEntityUnregister)
{
// Unregistering a container entity is successful.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
@@ -39,21 +39,21 @@ namespace AzToolsFramework
EXPECT_TRUE(outcome.IsSuccess());
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_UnregisterRegularEntity)
TEST_F(EditorFocusModeFixture, ContainerEntityUnregisterRegularEntity)
{
// Unregistering an entity that was not previously registered fails.
auto outcome = m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
EXPECT_FALSE(outcome.IsSuccess());
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_UnregisterTwice)
TEST_F(EditorFocusModeFixture, ContainerEntityUnregisterTwice)
{
// Unregistering a container entity twice fails.
auto outcome = m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
EXPECT_FALSE(outcome.IsSuccess());
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnRegularEntity)
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOnRegularEntity)
{
// If a regular entity is passed, IsContainer returns false.
// Note that we use a different entity than the tests above to validate a completely new EntityId.
@@ -61,7 +61,7 @@ namespace AzToolsFramework
EXPECT_FALSE(isContainer);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnRegisteredContainer)
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOnRegisteredContainer)
{
// If a container entity is passed, IsContainer returns true.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
@@ -72,7 +72,7 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnUnRegisteredContainer)
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOnUnRegisteredContainer)
{
// If an entity that was previously a container but was then unregistered is passed, IsContainer returns false.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
@@ -82,14 +82,14 @@ namespace AzToolsFramework
EXPECT_FALSE(isContainer);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpenOnRegularEntity)
TEST_F(EditorFocusModeFixture, ContainerEntitySetContainerOpenOnRegularEntity)
{
// Setting a regular entity to open should return a failure.
auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
EXPECT_FALSE(outcome.IsSuccess());
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpen)
TEST_F(EditorFocusModeFixture, ContainerEntitySetContainerOpen)
{
// Set a container entity to open, and verify the operation was successful.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
@@ -100,7 +100,7 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpenTwice)
TEST_F(EditorFocusModeFixture, ContainerEntitySetContainerOpenTwice)
{
// Set a container entity to open twice, and verify that does not cause a failure (as intended).
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
@@ -112,7 +112,7 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerClosed)
TEST_F(EditorFocusModeFixture, ContainerEntitySetContainerClosed)
{
// Set a container entity to closed, and verify the operation was successful.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
@@ -122,16 +122,16 @@ namespace AzToolsFramework
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnRegularEntity)
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOpenOnRegularEntity)
{
// Query open state on a regular entity, and verify it returns true.
// Open containers behave exactly as regular entities, so this is the expected return value.
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]);
EXPECT_TRUE(isOpen);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnDefaultContainerEntity)
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOpenOnDefaultContainerEntity)
{
// Query open state on a newly registered container entity, and verify it returns false.
// Containers are registered closed by default.
@@ -142,8 +142,8 @@ namespace AzToolsFramework
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnOpenContainerEntity)
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOpenOnOpenContainerEntity)
{
// Query open state on a container entity that was opened, and verify it returns true.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
@@ -154,8 +154,8 @@ namespace AzToolsFramework
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnClosedContainerEntity)
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOpenOnClosedContainerEntity)
{
// Query open state on a container entity that was opened and then closed, and verify it returns false.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
@@ -167,8 +167,8 @@ namespace AzToolsFramework
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ContainerOpenStateIsPreserved)
TEST_F(EditorFocusModeFixture, ContainerEntityContainerOpenStateIsPreserved)
{
// Register an entity as container, open it, then unregister it.
// When the entity is registered again, the open state should be preserved.
@@ -184,15 +184,15 @@ namespace AzToolsFramework
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearSucceeds)
TEST_F(EditorFocusModeFixture, ContainerEntityClearSucceeds)
{
// The Clear function works if no container is registered.
auto outcome = m_containerEntityInterface->Clear(m_editorEntityContextId);
EXPECT_TRUE(outcome.IsSuccess());
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearFailsIfContainersAreStillRegistered)
TEST_F(EditorFocusModeFixture, ContainerEntityClearFailsIfContainersAreStillRegistered)
{
// The Clear function fails if a container is registered.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
@@ -202,8 +202,8 @@ namespace AzToolsFramework
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearSucceedsIfContainersAreUnregistered)
TEST_F(EditorFocusModeFixture, ContainerEntityClearSucceedsIfContainersAreUnregistered)
{
// The Clear function fails if a container is registered.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
@@ -212,7 +212,7 @@ namespace AzToolsFramework
EXPECT_TRUE(outcome.IsSuccess());
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearDeletesPreservedOpenStates)
TEST_F(EditorFocusModeFixture, ContainerEntityClearDeletesPreservedOpenStates)
{
// Register an entity as container, open it, unregister it, then call clear.
// When the entity is registered again, the open state should not be preserved.
@@ -230,14 +230,14 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithNoContainers)
TEST_F(EditorFocusModeFixture, ContainerEntityFindHighestSelectableEntityWithNoContainers)
{
// When no containers are in the way, the function will just return the entityId that was passed to it.
AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
EXPECT_EQ(selectedEntityId, m_entityMap[Passenger2EntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithClosedContainer)
TEST_F(EditorFocusModeFixture, ContainerEntityFindHighestSelectableEntityWithClosedContainer)
{
// If a closed container is an ancestor of the queried entity, the closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]); // Containers are closed by default
@@ -248,7 +248,7 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithOpenContainer)
TEST_F(EditorFocusModeFixture, ContainerEntityFindHighestSelectableEntityWithOpenContainer)
{
// If an open container is an ancestor of the queried entity, it is ignored.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
@@ -261,7 +261,7 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithMultipleClosedContainers)
TEST_F(EditorFocusModeFixture, ContainerEntityFindHighestSelectableEntityWithMultipleClosedContainers)
{
// If multiple closed containers are ancestors of the queried entity, the highest closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
@@ -275,7 +275,7 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithMultipleContainers)
TEST_F(EditorFocusModeFixture, ContainerEntityFindHighestSelectableEntityWithMultipleContainers)
{
// If multiple containers are ancestors of the queried entity, the highest closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
@@ -289,5 +289,4 @@ namespace AzToolsFramework
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
}
}
} // namespace UnitTest
@@ -12,7 +12,7 @@
#include <Tests/BoundsTestComponent.h>
namespace AzToolsFramework
namespace UnitTest
{
void ClearSelectedEntities()
{
@@ -31,14 +31,14 @@ namespace AzToolsFramework
void EditorFocusModeFixture::SetUpEditorFixtureImpl()
{
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
m_containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
m_containerEntityInterface = AZ::Interface<AzToolsFramework::ContainerEntityInterface>::Get();
ASSERT_TRUE(m_containerEntityInterface != nullptr);
m_focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
m_focusModeInterface = AZ::Interface<AzToolsFramework::FocusModeInterface>::Get();
ASSERT_TRUE(m_focusModeInterface != nullptr);
// register a simple component implementing BoundsRequestBus and EditorComponentSelectionRequestsBus
@@ -68,26 +68,26 @@ namespace AzToolsFramework
ClearSelectedEntities();
}
void EditorFocusModeFixture::GenerateTestHierarchy()
void EditorFocusModeFixture::GenerateTestHierarchy()
{
/*
* City
* |_ Street
* |_ Car
* | |_ Passenger
* |_ SportsCar
* |_ Passenger
*/
* City
* |_ Street
* |_ Car
* | |_ Passenger
* |_ SportsCar
* |_ Passenger
*/
m_entityMap[CityEntityName] = CreateEditorEntity(CityEntityName, AZ::EntityId());
m_entityMap[StreetEntityName] = CreateEditorEntity(StreetEntityName, m_entityMap[CityEntityName]);
m_entityMap[CarEntityName] = CreateEditorEntity(CarEntityName, m_entityMap[StreetEntityName]);
m_entityMap[Passenger1EntityName] = CreateEditorEntity(Passenger1EntityName, m_entityMap[CarEntityName]);
m_entityMap[SportsCarEntityName] = CreateEditorEntity(SportsCarEntityName, m_entityMap[StreetEntityName]);
m_entityMap[Passenger2EntityName] = CreateEditorEntity(Passenger2EntityName, m_entityMap[SportsCarEntityName]);
m_entityMap[CityEntityName] = CreateEditorEntity(CityEntityName, AZ::EntityId());
m_entityMap[StreetEntityName] = CreateEditorEntity(StreetEntityName, m_entityMap[CityEntityName]);
m_entityMap[CarEntityName] = CreateEditorEntity(CarEntityName, m_entityMap[StreetEntityName]);
m_entityMap[Passenger1EntityName] = CreateEditorEntity(Passenger1EntityName, m_entityMap[CarEntityName]);
m_entityMap[SportsCarEntityName] = CreateEditorEntity(SportsCarEntityName, m_entityMap[StreetEntityName]);
m_entityMap[Passenger2EntityName] = CreateEditorEntity(Passenger2EntityName, m_entityMap[SportsCarEntityName]);
// Add a BoundsTestComponent to the Car entity.
AZ::Entity* entity = GetEntityById(m_entityMap[CarEntityName]);
AZ::Entity* entity = AzToolsFramework::GetEntityById(m_entityMap[CarEntityName]);
entity->Deactivate();
entity->CreateComponent<UnitTest::BoundsTestComponent>();
@@ -113,4 +113,4 @@ namespace AzToolsFramework
return entity->GetId();
}
}
} // namespace UnitTest
@@ -18,10 +18,9 @@
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
namespace AzToolsFramework
namespace UnitTest
{
class EditorFocusModeFixture
: public UnitTest::ToolsApplicationFixture
class EditorFocusModeFixture : public ToolsApplicationFixture
{
protected:
void SetUpEditorFixtureImpl() override;
@@ -32,8 +31,8 @@ namespace AzToolsFramework
AZStd::unordered_map<AZStd::string, AZ::EntityId> m_entityMap;
ContainerEntityInterface* m_containerEntityInterface = nullptr;
FocusModeInterface* m_focusModeInterface = nullptr;
AzToolsFramework::ContainerEntityInterface* m_containerEntityInterface = nullptr;
AzToolsFramework::FocusModeInterface* m_focusModeInterface = nullptr;
public:
AzToolsFramework::EntityIdList GetSelectedEntities();
@@ -53,4 +52,4 @@ namespace AzToolsFramework
inline static AZ::Vector3 WorldCarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f);
};
}
} // namespace UnitTest
@@ -26,11 +26,17 @@
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
namespace AzToolsFramework
namespace UnitTest
{
class EditorFocusModeSelectionFixture : public UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>
class EditorFocusModeSelectionFixture : public IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>
{
public:
void SetUpEditorFixtureImpl() override
{
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>::SetUpEditorFixtureImpl();
m_viewportManipulatorInteraction->GetViewportInteraction().SetIconsVisible(false);
}
void ClickAtWorldPositionOnViewport(const AZ::Vector3& worldPosition)
{
// Calculate the world position in screen space
@@ -40,4 +46,4 @@ namespace AzToolsFramework
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(carScreenPosition)->MouseLButtonDown()->MouseLButtonUp();
}
};
} // namespace AzToolsFramework
} // namespace UnitTest
@@ -8,9 +8,9 @@
#include <Tests/FocusMode/EditorFocusModeSelectionFixture.h>
namespace AzToolsFramework
namespace UnitTest
{
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnLevel)
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionSelectEntityWithFocusOnLevel)
{
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
@@ -21,7 +21,7 @@ namespace AzToolsFramework
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnAncestor)
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionSelectEntityWithFocusOnAncestor)
{
// Set the focus on the Street Entity (parent of the test entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
@@ -35,7 +35,7 @@ namespace AzToolsFramework
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnItself)
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionSelectEntityWithFocusOnItself)
{
// Set the focus on the Car Entity (test entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
@@ -49,7 +49,7 @@ namespace AzToolsFramework
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnSibling)
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionSelectEntityWithFocusOnSibling)
{
// Set the focus on the SportsCar Entity (sibling of the test entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[SportsCarEntityName]);
@@ -62,7 +62,7 @@ namespace AzToolsFramework
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnDescendant)
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionSelectEntityWithFocusOnDescendant)
{
// Set the focus on the Passenger1 Entity (child of the entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger1EntityName]);
@@ -74,4 +74,4 @@ namespace AzToolsFramework
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
}
}
} // namespace UnitTest
@@ -8,9 +8,9 @@
#include <Tests/FocusMode/EditorFocusModeFixture.h>
namespace AzToolsFramework
namespace UnitTest
{
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_SetFocus)
TEST_F(EditorFocusModeFixture, SetFocus)
{
// When an entity is set as the focus root, GetFocusRoot should return its EntityId.
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
@@ -20,7 +20,7 @@ namespace AzToolsFramework
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
}
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_ClearFocus)
TEST_F(EditorFocusModeFixture, ClearFocus)
{
// Change the value from the default.
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
@@ -30,7 +30,7 @@ namespace AzToolsFramework
EXPECT_EQ(m_focusModeInterface->GetFocusRoot(m_editorEntityContextId), AZ::EntityId());
}
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_AncestorsDescendants)
TEST_F(EditorFocusModeFixture, IsInFocusSubTreeAncestorsDescendants)
{
// When the focus is set to an entity, all its descendants are in the focus subtree while the ancestors aren't.
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
@@ -43,7 +43,7 @@ namespace AzToolsFramework
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
}
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Siblings)
TEST_F(EditorFocusModeFixture, IsInFocusSubTreeSiblings)
{
// If the root entity has siblings, they are also outside of the focus subtree.
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
@@ -56,7 +56,7 @@ namespace AzToolsFramework
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), false);
}
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Leaf)
TEST_F(EditorFocusModeFixture, IsInFocusSubTreeLeaf)
{
// If the root is a leaf, then the focus subtree will consists of just that entity.
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger2EntityName]);
@@ -69,7 +69,7 @@ namespace AzToolsFramework
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
}
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Clear)
TEST_F(EditorFocusModeFixture, IsInFocusSubTreeClear)
{
// Change the value from the default.
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
@@ -84,4 +84,4 @@ namespace AzToolsFramework
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
}
}
} // namespace UnitTest
@@ -137,7 +137,6 @@ namespace UnitTest
void CreateEditorRepresentation(AZ::Entity* entity) override;
void BrowseForAssets(AzToolsFramework::AssetBrowser::AssetSelectionModel& selection) override { AZ_UNUSED(selection); }
int GetIconTextureIdFromEntityIconPath(const AZStd::string& entityIconPath) override { AZ_UNUSED(entityIconPath); return 0; }
bool DisplayHelpersVisible() override { return false; }
/*
* AssetSystemRequestBus
@@ -74,10 +74,6 @@ namespace UnitTest
AZStd::unique_ptr<AzToolsFramework::AssetBrowser::AssetBrowserFilterModel> m_filterModel;
AZStd::unique_ptr<AzToolsFramework::AssetBrowser::AssetBrowserTableModel> m_tableModel;
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTesterAssetBrowser;
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTesterFilterModel;
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTesterTableModel;
QVector<int> m_folderIds = { 13, 14, 15 };
QVector<int> m_sourceIDs = { 1, 2, 3, 4, 5 };
QVector<int> m_productIDs = { 1, 2, 3, 4, 5 };
@@ -96,9 +92,6 @@ namespace UnitTest
m_filterModel->setSourceModel(m_assetBrowserComponent->GetAssetBrowserModel());
m_tableModel->setSourceModel(m_filterModel.get());
m_modelTesterAssetBrowser = AZStd::make_unique<QAbstractItemModelTester>(m_assetBrowserComponent->GetAssetBrowserModel());
m_modelTesterFilterModel = AZStd::make_unique<QAbstractItemModelTester>(m_filterModel.get());
m_modelTesterTableModel = AZStd::make_unique<QAbstractItemModelTester>(m_tableModel.get());
m_searchWidget = AZStd::make_unique<AzToolsFramework::AssetBrowser::SearchWidget>();
// Setup String filters
@@ -110,10 +103,6 @@ namespace UnitTest
void AssetBrowserTest::TearDownEditorFixtureImpl()
{
m_modelTesterAssetBrowser.reset();
m_modelTesterFilterModel.reset();
m_modelTesterTableModel.reset();
m_tableModel.reset();
m_filterModel.reset();
m_assetBrowserComponent->Deactivate();
@@ -243,7 +243,6 @@ namespace UnitTest
// These are required by implementing the EditorRequestBus
void BrowseForAssets(AssetBrowser::AssetSelectionModel& /*selection*/) override {}
int GetIconTextureIdFromEntityIconPath([[maybe_unused]] const AZStd::string& entityIconPath) override { return 0; }
bool DisplayHelpersVisible() override { return false; }
public:
EntityPropertyEditor* m_levelEditor;
@@ -15,7 +15,7 @@
#include <CryCommon/CryListenerSet.h>
#include <CryCommon/StaticInstance.h>
#if !defined(RELEASE) || defined(RELEASE_LOGGING) || defined(ENABLE_PROFILING_CODE)
#if (!defined(RELEASE) || defined(RELEASE_LOGGING) || defined(ENABLE_PROFILING_CODE)) && !defined(AZ_LEGACY_CRYSYSTEM_TRAIT_REMOTE_CONSOLE_UNSUPPORTED)
#define USE_REMOTE_CONSOLE
struct SRemoteServer;
@@ -41,17 +41,17 @@ void CRemoteConsole::Stop()
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::AddLogMessage(const char* log)
void CRemoteConsole::AddLogMessage(const char*)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::AddLogWarning(const char* log)
void CRemoteConsole::AddLogWarning(const char*)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::AddLogError(const char* log)
void CRemoteConsole::AddLogError(const char*)
{
}
@@ -61,11 +61,11 @@ void CRemoteConsole::Update()
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::RegisterListener(IRemoteConsoleListener* pListener, const char* name)
void CRemoteConsole::RegisterListener(IRemoteConsoleListener*, const char*)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::UnregisterListener(IRemoteConsoleListener* pListener)
void CRemoteConsole::UnregisterListener(IRemoteConsoleListener*)
{
}
@@ -610,7 +610,7 @@ namespace LUAEditor
{
m_resultList[qAssetName].m_assetId = docInfo.m_assetId;
}
entry.m_lineNumber = line;
entry.m_lineNumber = line + 1;
entry.m_lineText = entry.m_lineText.trimmed();
while (index > -1)