Merge branch 'development' into Prefabs/ProcessStackAddPrefabBug

This commit is contained in:
AMZN-koppersr
2022-01-05 11:12:57 -08:00
710 changed files with 145396 additions and 19297 deletions
-7
View File
@@ -30,8 +30,6 @@ CAboutDialog::CAboutDialog(QString versionText, QString richTextCopyrightNotice,
m_ui->setupUi(this);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
connect(m_ui->m_transparentAgreement, &QLabel::linkActivated, this, &CAboutDialog::OnCustomerAgreement);
m_ui->m_transparentTrademarks->setText(versionText);
m_ui->m_transparentAllRightReserved->setObjectName("copyrightNotice");
@@ -84,9 +82,4 @@ void CAboutDialog::mouseReleaseEvent(QMouseEvent* event)
QDialog::mouseReleaseEvent(event);
}
void CAboutDialog::OnCustomerAgreement()
{
QDesktopServices::openUrl(QUrl(QStringLiteral("https://www.o3debinaries.org/license")));
}
#include <moc_AboutDialog.cpp>
-2
View File
@@ -30,8 +30,6 @@ public:
private:
void OnCustomerAgreement();
void mouseReleaseEvent(QMouseEvent* event) override;
void paintEvent(QPaintEvent* event) override;
+6 -8
View File
@@ -181,14 +181,17 @@
</spacer>
</item>
<item>
<widget class="ClickableLabel" name="m_transparentAgreement">
<widget class="QLabel" name="m_transparentAgreement">
<property name="text">
<string>Terms of Use</string>
<string>&lt;a href=&quot;https://www.o3debinaries.org/license&quot;&gt;Terms of Use&lt;/a&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="showDecoration" stdset="0">
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
@@ -274,11 +277,6 @@
<extends>QWidget</extends>
<header>qsvgwidget.h</header>
</customwidget>
<customwidget>
<class>ClickableLabel</class>
<extends>QLabel</extends>
<header>QtUI/ClickableLabel.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
@@ -1,30 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "AnimationBipedBoneNames.h"
namespace EditorAnimationBones::Biped
{
const char* Pelvis = "Bip01 Pelvis";
const char* Head = "Bip01 Head";
const char* Weapon = "weapon_bone";
const char* LeftEye = "eye_bone_left";
const char* RightEye = "eye_bone_right";
const char* Spine[5] = { "Bip01 Spine", "Bip01 Spine1", "Bip01 Spine2", "Bip01 Spine3", "Bip01 Spine4" };
const char* Neck[2] = { "Bip01 Neck", "Bip01 Neck1" };
const char* LeftHeel = "Bip01 L Heel";
const char* LeftToe[2] = { "Bip01 L Toe0", "Bip01 L Toe1" };
const char* RightHeel = "Bip01 R Heel";
const char* RightToe[2] = { "Bip01 R Toe0", "Bip01 R Toe1" };
} // namespace EditorAnimationBones::Biped
@@ -1,34 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_ANIMATION_ANIMATIONBIPEDBONENAMES_H
#define CRYINCLUDE_EDITOR_ANIMATION_ANIMATIONBIPEDBONENAMES_H
#pragma once
namespace EditorAnimationBones
{
namespace Biped
{
extern const char* Pelvis;
extern const char* Head;
extern const char* Weapon;
extern const char* Spine[5];
extern const char* Neck[2];
extern const char* LeftEye;
extern const char* RightEye;
extern const char* LeftHeel;
extern const char* RightHeel;
extern const char* LeftToe[2];
extern const char* RightToe[2];
}
}
#endif // CRYINCLUDE_EDITOR_ANIMATION_ANIMATIONBIPEDBONENAMES_H
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:58ef978b31b31df9aaf715a0e9b006fde414a17a3ff15a3bf680eaad7418867a
size 364
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:98a681ec3d89ee57c5d1057fe984dcf8ad45721f47ae4df57fa358fbee85e616
size 385
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:24a2b2c9242a841c20e7815dab0d80a575844055328aea413d28b7283b65a92e
size 386
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ce23a276fec849b8f832fab96d3b738793335c27d37ae3813158387f3415b508
size 377
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c03befab41765200f4f28dbf1e0b2a702d2244bfa79b0d463f5d58d0a26095fc
size 386
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:418c3f0f27854b3795841359014d87686a7bf94daf2568d9cfd3ffac22675f69
size 386
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d3a831f34ac53c9b1f20037290e8a2b62a3cfb8a4f86467591f44fd2a0e3c15b
size 379
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4267102ca7a889c34eff905480a68878d4d56e15bc723a5b0575cd472e259f5d
size 389
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a0df013dd102b87348fba18b4da5443591309e9c40166d27ae928636924154ea
size 388
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e713076ab5abbbb2cf28da431a339e9905acc790e35295f025aa2e79e1c04141
size 376
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9e5af9d62ceafc3b8a1dfc36772350cd623fcc86c68711b299e143ff133f79b6
size 387
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:17c5fb3d7b87ea87a98934954c721573c641bc44005a34f1e16589d7f39b71e8
size 409
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6f5c78d9f764b62fb7dcf400c91c1edea9d7f88a426ba513fbf70825c6bcd2ac
size 383
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:376b549602afffca407525b77c1a9821bf6a0e279792ae2e52fe0a4f7c3c5bd4
size 364
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e7dc48f8d324b7563b168f27ebde1e00ee2bd11ba462f114a05b297913e285c5
size 374
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:66b73afbd6dba1caaedfaae161b277b460b5198f7fc00bec414530116c567276
size 375
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ae6e6714acf495246f4e59f6e5640f3a4417ea50100d37a116950d2b859aed0c
size 417
-111
View File
@@ -1,111 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_CONSOLESCBMFC_H
#define CRYINCLUDE_EDITOR_CONTROLS_CONSOLESCBMFC_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QTextEdit>
#include <QtWidgets/QPushButton>
#include "ConsoleSCB.h"
#endif
class QMenu;
class ConsoleWidget;
class QFocusEvent;
namespace Ui {
class ConsoleMFC;
}
namespace MFC
{
struct ConsoleLine
{
QString text;
bool newLine;
};
typedef std::deque<ConsoleLine> Lines;
class ConsoleLineEdit
: public QLineEdit
{
Q_OBJECT
public:
explicit ConsoleLineEdit(QWidget* parent = nullptr);
protected:
void mousePressEvent(QMouseEvent* ev) override;
void mouseDoubleClickEvent(QMouseEvent* ev) override;
void keyPressEvent(QKeyEvent* ev) override;
bool event(QEvent* ev) override;
signals:
void variableEditorRequested();
void setWindowTitle(const QString&);
private:
void DisplayHistory(bool bForward);
QStringList m_history;
unsigned int m_historyIndex;
bool m_bReusedHistory;
};
class ConsoleTextEdit
: public QTextEdit
{
Q_OBJECT
public:
explicit ConsoleTextEdit(QWidget* parent = nullptr);
};
class CConsoleSCB
: public QWidget
{
Q_OBJECT
public:
explicit CConsoleSCB(QWidget* parent = nullptr);
~CConsoleSCB();
static void RegisterViewClass();
void SetInputFocus();
void AddToConsole(const QString& text, bool bNewLine);
void FlushText();
void showPopupAndSetTitle();
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
static CConsoleSCB* GetCreatedInstance();
static void AddToPendingLines(const QString& text, bool bNewLine); // call this function instead of AddToConsole() until an instance of CConsoleSCB exists to prevent messages from getting lost
public Q_SLOTS:
void OnStyleSettingsChanged();
private Q_SLOTS:
void showVariableEditor();
private:
QScopedPointer<Ui::ConsoleMFC> ui;
int m_richEditTextLength;
Lines m_lines;
static Lines s_pendingLines;
QList<QColor> m_colorTable;
SEditorSettings::ConsoleColorTheme m_backgroundTheme;
};
} // namespace MFC
#endif // CRYINCLUDE_EDITOR_CONTROLS_CONSOLESCB_H
-132
View File
@@ -1,132 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConsoleMFC</class>
<widget class="QWidget" name="Console">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>120</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Console</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QTextEdit" name="textEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="container2" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>20</height>
</size>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="button">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>30</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="MFC::ConsoleLineEdit" name="lineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>MFC::ConsoleLineEdit</class>
<extends>QLineEdit</extends>
<header>ConsoleSCBMFC.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="ConsoleSCB.qrc"/>
</resources>
<connections/>
</ui>
@@ -1,48 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "HotTrackingTreeCtrl.h"
// Qt
#include <QMouseEvent>
CHotTrackingTreeCtrl::CHotTrackingTreeCtrl(QWidget* parent)
: QTreeWidget(parent)
{
setMouseTracking(true);
m_hHoverItem = nullptr;
}
void CHotTrackingTreeCtrl::mouseMoveEvent(QMouseEvent* event)
{
QTreeWidgetItem* hItem = itemAt(event->pos());
if (m_hHoverItem != nullptr)
{
QFont font = m_hHoverItem->font(0);
font.setBold(false);
m_hHoverItem->setFont(0, font);
m_hHoverItem = nullptr;
}
if (hItem != nullptr)
{
QFont font = hItem->font(0);
font.setBold(true);
hItem->setFont(0, font);
m_hHoverItem = hItem;
}
QTreeWidget::mouseMoveEvent(event);
}
#include <Controls/moc_HotTrackingTreeCtrl.cpp>
@@ -1,33 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_HOTTRACKINGTREECTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_HOTTRACKINGTREECTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QTreeWidget>
#endif
class CHotTrackingTreeCtrl
: public QTreeWidget
{
Q_OBJECT
public:
CHotTrackingTreeCtrl(QWidget* parent = 0);
virtual ~CHotTrackingTreeCtrl(){};
protected:
void mouseMoveEvent(QMouseEvent* event) override;
private:
QTreeWidgetItem* m_hHoverItem;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_HOTTRACKINGTREECTRL_H
-567
View File
@@ -1,567 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "ImageListCtrl.h"
// Qt
#include <QPainter>
#include <QScrollBar>
//////////////////////////////////////////////////////////////////////////
CImageListCtrl::CImageListCtrl(QWidget* parent)
: QAbstractItemView(parent)
, m_itemSize(60, 60)
, m_borderSize(4, 4)
, m_style(DefaultStyle)
{
setItemDelegate(new QImageListDelegate(this));
setAutoFillBackground(false);
QPalette p = palette();
p.setColor(QPalette::Highlight, QColor(255, 55, 50));
setPalette(p);
horizontalScrollBar()->setRange(0, 0);
verticalScrollBar()->setRange(0, 0);
}
//////////////////////////////////////////////////////////////////////////
CImageListCtrl::~CImageListCtrl()
{
}
//////////////////////////////////////////////////////////////////////////
CImageListCtrl::ListStyle CImageListCtrl::Style() const
{
return m_style;
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::SetStyle(ListStyle style)
{
m_style = style;
scheduleDelayedItemsLayout();
}
//////////////////////////////////////////////////////////////////////////
const QSize& CImageListCtrl::ItemSize() const
{
return m_itemSize;
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::SetItemSize(QSize size)
{
Q_ASSERT(size.isValid());
m_itemSize = size;
scheduleDelayedItemsLayout();
}
//////////////////////////////////////////////////////////////////////////
const QSize& CImageListCtrl::BorderSize() const
{
return m_borderSize;
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::SetBorderSize(QSize size)
{
Q_ASSERT(size.isValid());
m_borderSize = size;
scheduleDelayedItemsLayout();
}
//////////////////////////////////////////////////////////////////////////
QModelIndexList CImageListCtrl::ItemsInRect(const QRect& rect) const
{
QModelIndexList list;
if (!model())
{
return list;
}
QHash<int, QRect>::const_iterator i;
QHash<int, QRect>::const_iterator c = m_geometry.cend();
for (i = m_geometry.cbegin(); i != c; ++i)
{
if (i.value().intersects(rect))
{
list << model()->index(i.key(), 0, rootIndex());
}
}
return list;
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::paintEvent(QPaintEvent* event)
{
QAbstractItemView::paintEvent(event);
if (!model())
{
return;
}
const int rowCount = model()->rowCount();
if (m_geometry.isEmpty() && rowCount)
{
updateGeometries();
}
QPainter painter(viewport());
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
painter.setBackground(palette().window());
painter.setFont(font());
QStyleOptionViewItem option;
option.palette = palette();
option.font = font();
option.fontMetrics = fontMetrics();
option.decorationAlignment = Qt::AlignCenter;
const QRect visibleRect(QPoint(horizontalOffset(), verticalOffset()), viewport()->contentsRect().size());
painter.translate(-horizontalOffset(), -verticalOffset());
for (int r = 0; r < rowCount; ++r)
{
const QModelIndex& index = model()->index(r, 0, rootIndex());
option.rect = m_geometry.value(r);
if (!option.rect.intersects(visibleRect))
{
continue;
}
option.state = QStyle::State_None;
if (selectionModel()->isSelected(index))
{
option.state |= QStyle::State_Selected;
}
if (currentIndex() == index)
{
option.state |= QStyle::State_HasFocus;
}
QAbstractItemDelegate* idt = itemDelegate(index);
idt->paint(&painter, option, index);
}
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::rowsInserted(const QModelIndex& parent, int start, int end)
{
QAbstractItemView::rowsInserted(parent, start, end);
if (isVisible())
{
scheduleDelayedItemsLayout();
}
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::updateGeometries()
{
ClearItemGeometries();
if (!model())
{
return;
}
const int rowCount = model()->rowCount();
const int nPageHorz = viewport()->width();
const int nPageVert = viewport()->height();
if (nPageHorz == 0 || nPageVert == 0 || rowCount <= 0)
{
return;
}
int x = m_borderSize.width();
int y = m_borderSize.height();
const int nItemWidth = m_itemSize.width() + m_borderSize.width();
if (m_style == HorizontalStyle)
{
for (int row = 0; row < rowCount; ++row)
{
m_geometry.insert(row, QRect(QPoint(x, y), m_itemSize));
x += nItemWidth;
}
horizontalScrollBar()->setPageStep(viewport()->width());
horizontalScrollBar()->setRange(0, x - viewport()->width());
}
else
{
const int nTextHeight = fontMetrics().height();
const int nItemHeight = m_itemSize.height() + m_borderSize.height() + nTextHeight;
int nNumOfHorzItems = nPageHorz / nItemWidth;
if (nNumOfHorzItems <= 0)
{
nNumOfHorzItems = 1;
}
for (int row = 0; row < rowCount; ++row)
{
m_geometry.insert(row, QRect(QPoint(x, y), m_itemSize));
if ((row + 1) % nNumOfHorzItems == 0)
{
y += nItemHeight;
x = m_borderSize.width();
}
else
{
x += nItemWidth;
}
}
verticalScrollBar()->setPageStep(viewport()->height());
verticalScrollBar()->setRange(0, (y + nItemHeight) - viewport()->height());
}
}
//////////////////////////////////////////////////////////////////////////
QModelIndex CImageListCtrl::indexAt(const QPoint& point) const
{
if (!model())
{
return QModelIndex();
}
const QPoint p = point +
QPoint(horizontalOffset(), verticalOffset());
QHash<int, QRect>::const_iterator i;
QHash<int, QRect>::const_iterator c = m_geometry.cend();
for (i = m_geometry.cbegin(); i != c; ++i)
{
if (i.value().contains(p))
{
return model()->index(i.key(), 0, rootIndex());
}
}
return QModelIndex();
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::scrollTo(const QModelIndex& index, ScrollHint hint)
{
if (!index.isValid())
{
return;
}
QRect rect = m_geometry.value(index.row());
switch (hint)
{
case EnsureVisible:
if (horizontalOffset() > rect.right())
{
horizontalScrollBar()->setValue(rect.left());
}
else if ((horizontalOffset() + viewport()->width()) < rect.left())
{
horizontalScrollBar()->setValue(rect.right() - viewport()->width());
}
if (verticalOffset() > rect.bottom())
{
verticalScrollBar()->setValue(rect.top());
}
else if ((verticalOffset() + viewport()->height()) < rect.top())
{
verticalScrollBar()->setValue(rect.bottom() - viewport()->height());
}
break;
case PositionAtTop:
horizontalScrollBar()->setValue(rect.left());
verticalScrollBar()->setValue(rect.top());
break;
case PositionAtBottom:
horizontalScrollBar()->setValue(rect.right() - viewport()->width());
verticalScrollBar()->setValue(rect.bottom() - viewport()->height());
break;
case PositionAtCenter:
horizontalScrollBar()->setValue(rect.center().x() - (viewport()->width() / 2));
verticalScrollBar()->setValue(rect.center().y() - (viewport()->height() / 2));
break;
}
}
//////////////////////////////////////////////////////////////////////////
QRect CImageListCtrl::visualRect(const QModelIndex& index) const
{
if (!index.isValid())
{
return QRect();
}
if (!m_geometry.contains(index.row()))
{
return QRect();
}
return m_geometry.value(index.row())
.translated(-horizontalOffset(), -verticalOffset());
}
//////////////////////////////////////////////////////////////////////////
QRect CImageListCtrl::ItemGeometry(const QModelIndex& index) const
{
Q_ASSERT(index.model() == model());
Q_ASSERT(m_geometry.contains(index.row()));
return m_geometry.value(index.row());
}
void CImageListCtrl::SetItemGeometry(const QModelIndex& index, const QRect& rect)
{
Q_ASSERT(index.model() == model());
m_geometry.insert(index.row(), rect);
update(rect);
}
void CImageListCtrl::ClearItemGeometries()
{
m_geometry.clear();
}
//////////////////////////////////////////////////////////////////////////
int CImageListCtrl::horizontalOffset() const
{
return horizontalScrollBar()->value();
}
//////////////////////////////////////////////////////////////////////////
int CImageListCtrl::verticalOffset() const
{
return verticalScrollBar()->value();
}
//////////////////////////////////////////////////////////////////////////
bool CImageListCtrl::isIndexHidden([[maybe_unused]] const QModelIndex& index) const
{
return false; /* not supported */
}
//////////////////////////////////////////////////////////////////////////
QModelIndex CImageListCtrl::moveCursor(CursorAction cursorAction, [[maybe_unused]] Qt::KeyboardModifiers modifiers)
{
if (!model())
{
return QModelIndex();
}
const int rowCount = model()->rowCount();
if (0 == rowCount)
{
return QModelIndex();
}
switch (cursorAction)
{
case MoveHome:
return model()->index(0, 0, rootIndex());
case MoveEnd:
return model()->index(rowCount - 1, 0, rootIndex());
case MovePrevious:
{
QModelIndex current = currentIndex();
if (current.isValid())
{
return model()->index((current.row() - 1) % rowCount, 0, rootIndex());
}
} break;
case MoveNext:
{
QModelIndex current = currentIndex();
if (current.isValid())
{
return model()->index((current.row() + 1) % rowCount, 0, rootIndex());
}
} break;
case MoveUp:
case MoveDown:
case MoveLeft:
case MoveRight:
case MovePageUp:
case MovePageDown:
/* TODO */
break;
}
return QModelIndex();
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags)
{
if (!model())
{
return;
}
const QRect lrect =
rect.translated(horizontalOffset(), verticalOffset());
QHash<int, QRect>::const_iterator i;
QHash<int, QRect>::const_iterator c = m_geometry.cend();
for (i = m_geometry.cbegin(); i != c; ++i)
{
if (i.value().intersects(lrect))
{
selectionModel()->select(model()->index(i.key(), 0, rootIndex()), flags);
}
}
}
//////////////////////////////////////////////////////////////////////////
QRegion CImageListCtrl::visualRegionForSelection(const QItemSelection& selection) const
{
QRegion region;
foreach(const QModelIndex &index, selection.indexes())
{
region += visualRect(index);
}
return region;
}
//////////////////////////////////////////////////////////////////////////
QImageListDelegate::QImageListDelegate(QObject* parent)
: QAbstractItemDelegate(parent)
{
}
//////////////////////////////////////////////////////////////////////////
void QImageListDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option, const QModelIndex& index) const
{
painter->save();
painter->setFont(option.font);
if (option.rect.isValid())
{
painter->setClipRect(option.rect);
}
QRect innerRect = option.rect.adjusted(1, 1, -1, -1);
QRect textRect(innerRect.left(), innerRect.bottom() - option.fontMetrics.height(),
innerRect.width(), option.fontMetrics.height() + 1);
/* fill item background */
painter->fillRect(option.rect, option.palette.color(QPalette::Base));
/* draw image */
if (index.data(Qt::DecorationRole).isValid())
{
const QPixmap& p = index.data(Qt::DecorationRole).value<QPixmap>();
if (p.isNull() || p.size() == QSize(1, 1))
{
emit InvalidPixmapGenerated(index);
}
else
{
painter->drawPixmap(innerRect, p);
}
}
/* draw text */
const QColor trColor = option.palette.color(QPalette::Shadow);
painter->fillRect(textRect, (option.state & QStyle::State_Selected) ?
trColor.lighter() : trColor);
if (option.state & QStyle::State_Selected)
{
painter->setPen(QPen(option.palette.color(QPalette::HighlightedText)));
QFont f = painter->font();
f.setBold(true);
painter->setFont(f);
}
else
{
painter->setPen(QPen(option.palette.color(QPalette::Text)));
}
painter->drawText(textRect, index.data(Qt::DisplayRole).toString(),
QTextOption(option.decorationAlignment));
painter->setPen(QPen(option.palette.color(QPalette::Shadow)));
painter->drawRect(textRect);
/* draw border */
if (option.state & QStyle::State_Selected)
{
QPen pen(option.palette.color(QPalette::Highlight));
pen.setWidth(2);
painter->setPen(pen);
painter->drawRect(innerRect);
}
else
{
painter->setPen(QPen(option.palette.color(QPalette::Shadow)));
painter->drawRect(option.rect);
}
if (option.state & QStyle::State_HasFocus)
{
QPen pen(Qt::DotLine);
pen.setColor(option.palette.color(QPalette::AlternateBase));
painter->setPen(pen);
painter->drawRect(option.rect);
}
painter->restore();
}
//////////////////////////////////////////////////////////////////////////
QSize QImageListDelegate::sizeHint(const QStyleOptionViewItem& option,
[[maybe_unused]] const QModelIndex& index) const
{
return option.rect.size();
}
//////////////////////////////////////////////////////////////////////////
QVector<int> QImageListDelegate::paintingRoles() const
{
return QVector<int>() << Qt::DecorationRole << Qt::DisplayRole;
}
#include <Controls/moc_ImageListCtrl.cpp>
-97
View File
@@ -1,97 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_IMAGELISTCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_IMAGELISTCTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QAbstractItemView>
#include <QHash>
#endif
//////////////////////////////////////////////////////////////////////////
// Custom control to display list of images.
//////////////////////////////////////////////////////////////////////////
class CImageListCtrl
: public QAbstractItemView
{
Q_OBJECT
public:
enum ListStyle
{
DefaultStyle,
HorizontalStyle
};
public:
CImageListCtrl(QWidget* parent = nullptr);
~CImageListCtrl();
ListStyle Style() const;
void SetStyle(ListStyle style);
const QSize& ItemSize() const;
void SetItemSize(QSize size);
const QSize& BorderSize() const;
void SetBorderSize(QSize size);
// Get all items inside specified rectangle.
QModelIndexList ItemsInRect(const QRect& rect) const;
QModelIndex indexAt(const QPoint& point) const override;
void scrollTo(const QModelIndex& index, ScrollHint hint = EnsureVisible) override;
QRect visualRect(const QModelIndex& index) const override;
protected:
QRect ItemGeometry(const QModelIndex& index) const;
void SetItemGeometry(const QModelIndex& index, const QRect& rect);
void ClearItemGeometries();
int horizontalOffset() const override;
int verticalOffset() const override;
bool isIndexHidden(const QModelIndex& index) const override;
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags) override;
QRegion visualRegionForSelection(const QItemSelection& selection) const override;
void paintEvent(QPaintEvent* event) override;
void rowsInserted(const QModelIndex& parent, int start, int end) override;
void updateGeometries() override;
private:
QHash<int, QRect> m_geometry;
QSize m_itemSize;
QSize m_borderSize;
ListStyle m_style;
};
class QImageListDelegate
: public QAbstractItemDelegate
{
Q_OBJECT
signals:
void InvalidPixmapGenerated(const QModelIndex& index) const;
public:
QImageListDelegate(QObject* parent = nullptr);
void paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QVector<int> paintingRoles() const override;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_IMAGELISTCTRL_H
-67
View File
@@ -1,67 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "MultiMonHelper.h"
// Qt
#include <QScreen>
////////////////////////////////////////////////////////////////////////////
void ClipOrCenterRectToMonitor(QRect *prc, const UINT flags)
{
const QScreen* currentScreen = nullptr;
QRect rc;
Q_ASSERT(prc);
const auto screens = qApp->screens();
for (auto screen : screens)
{
if (screen->geometry().contains(prc->center()))
{
currentScreen = screen;
break;
}
}
if (!currentScreen)
{
return;
}
const int w = prc->width();
const int h = prc->height();
if (flags & MONITOR_WORKAREA)
{
rc = currentScreen->availableGeometry();
}
else
{
rc = currentScreen->geometry();
}
// center or clip the passed rect to the monitor rect
if (flags & MONITOR_CENTER)
{
prc->setLeft(rc.left() + (rc.right() - rc.left() - w) / 2);
prc->setTop(rc.top() + (rc.bottom() - rc.top() - h) / 2);
prc->setRight(prc->left() + w);
prc->setBottom(prc->top() + h);
}
else
{
prc->setLeft(qMax(rc.left(), qMin(rc.right() - w, prc->left())));
prc->setTop(qMax(rc.top(), qMin(rc.bottom() - h, prc->top())));
prc->setRight(prc->left() + w);
prc->setBottom(prc->top() + h);
}
}
-44
View File
@@ -1,44 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_MULTIMONHELPER_H
#define CRYINCLUDE_EDITOR_CONTROLS_MULTIMONHELPER_H
#pragma once
// Taken from: http://msdn.microsoft.com/en-us/library/dd162826(v=vs.85).aspx
#define MONITOR_CENTER 0x0001 // center rect to monitor
#define MONITOR_CLIP 0x0000 // clip rect to monitor
#define MONITOR_WORKAREA 0x0002 // use monitor work area
#define MONITOR_AREA 0x0000 // use monitor entire area
//
// ClipOrCenterRectToMonitor
//
// The most common problem apps have when running on a
// multimonitor system is that they "clip" or "pin" windows
// based on the SM_CXSCREEN and SM_CYSCREEN system metrics.
// Because of app compatibility reasons these system metrics
// return the size of the primary monitor.
//
// This shows how you use the multi-monitor functions
// to do the same thing.
//
// params:
// prc : pointer to QRect to modify
// flags : some combination of the MONITOR_* flags above
//
// example:
//
// ClipOrCenterRectToMonitor(&aRect, MONITOR_CLIP | MONITOR_WORKAREA);
//
// Takes parameter pointer to RECT "aRect" and flags MONITOR_CLIP | MONITOR_WORKAREA
// This will modify aRect without resizing it so that it remains within the on-screen boundaries.
void ClipOrCenterRectToMonitor(QRect *prc, const UINT flags);
#endif // CRYINCLUDE_EDITOR_CONTROLS_MULTIMONHELPER_H
-143
View File
@@ -1,143 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "NumberCtrl.h"
QNumberCtrl::QNumberCtrl(QWidget* parent)
: QDoubleSpinBox(parent)
, m_bMouseDown(false)
, m_bDragged(false)
, m_bUndoEnabled(false)
, m_prevValue(0)
{
connect(this, &QAbstractSpinBox::editingFinished, this, &QNumberCtrl::onEditingFinished);
}
void QNumberCtrl::changeEvent(QEvent* event)
{
if (event->type() == QEvent::EnabledChange)
{
setButtonSymbols(isEnabled() ? UpDownArrows : NoButtons);
}
QDoubleSpinBox::changeEvent(event);
}
void QNumberCtrl::SetRange(double newMin, double newMax)
{
// Avoid setting this value if its close to the current value, because otherwise qt will pump events into the queue to redraw/etc.
if ( (!AZ::IsClose(this->minimum(), newMin, DBL_EPSILON)) || (!AZ::IsClose(this->maximum(), newMax, DBL_EPSILON)) )
{
setRange(newMin, newMax);
}
}
void QNumberCtrl::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
emit mousePressed();
m_bMouseDown = true;
m_bDragged = false;
m_mousePos = event->pos();
if (m_bUndoEnabled && !CUndo::IsRecording())
{
GetIEditor()->BeginUndo();
}
emit dragStarted();
grabMouse();
}
QDoubleSpinBox::mousePressEvent(event);
}
void QNumberCtrl::mouseReleaseEvent(QMouseEvent* event)
{
QDoubleSpinBox::mouseReleaseEvent(event);
if (event->button() == Qt::LeftButton)
{
m_bMouseDown = m_bDragged = false;
emit valueUpdated();
emit valueChanged();
if (m_bUndoEnabled && CUndo::IsRecording())
{
GetIEditor()->AcceptUndo(m_undoText);
}
emit dragFinished();
releaseMouse();
m_prevValue = value();
emit mouseReleased();
}
}
void QNumberCtrl::mouseMoveEvent(QMouseEvent* event)
{
QDoubleSpinBox::mousePressEvent(event);
if (m_bMouseDown)
{
m_bDragged = true;
int dy = event->pos().y() - m_mousePos.y();
setValue(value() - singleStep() * dy);
emit valueUpdated();
m_mousePos = event->pos();
}
}
void QNumberCtrl::EnableUndo(const QString& undoText)
{
m_undoText = undoText;
m_bUndoEnabled = true;
}
void QNumberCtrl::focusInEvent(QFocusEvent* event)
{
m_prevValue = value();
QDoubleSpinBox::focusInEvent(event);
}
void QNumberCtrl::onEditingFinished()
{
bool undo = m_bUndoEnabled && !CUndo::IsRecording() && m_prevValue != value();
if (undo)
{
GetIEditor()->BeginUndo();
}
emit valueUpdated();
emit valueChanged();
if (undo)
{
GetIEditor()->AcceptUndo(m_undoText);
}
m_prevValue = value();
}
#include <Controls/moc_NumberCtrl.cpp>
-64
View File
@@ -1,64 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_NUMBERCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_NUMBERCTRL_H
#pragma once
// NumberCtrl.h : header file
//
#if !defined(Q_MOC_RUN)
#include <QDoubleSpinBox>
#endif
class QNumberCtrl
: public QDoubleSpinBox
{
Q_OBJECT
public:
QNumberCtrl(QWidget* parent = nullptr);
bool IsDragging() const { return m_bDragged; }
//! If called will enable undo with given text when control is modified.
void EnableUndo(const QString& undoText);
void SetRange(double newMin, double maxRange);
Q_SIGNALS:
void dragStarted();
void dragFinished();
void valueUpdated();
void valueChanged();
void mouseReleased();
void mousePressed();
protected:
void changeEvent(QEvent* event) override;
void focusInEvent(QFocusEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
private:
void onEditingFinished();
void onValueChanged(double d);
bool m_bMouseDown;
bool m_bDragged;
QPoint m_mousePos;
bool m_bUndoEnabled;
double m_prevValue;
QString m_undoText;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_NUMBERCTRL_H
-93
View File
@@ -1,93 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TextEditorCtrl.h"
// CTextEditorCtrl
CTextEditorCtrl::CTextEditorCtrl(QWidget* pParent)
: QTextEdit(pParent)
{
m_bModified = true;
QFont font;
font.setFamily("Courier New");
font.setFixedPitch(true);
font.setPointSize(10);
setFont(font);
setLineWrapMode(NoWrap);
connect(this, &QTextEdit::textChanged, this, &CTextEditorCtrl::OnChange);
}
CTextEditorCtrl::~CTextEditorCtrl()
{
}
// CTextEditorCtrl message handlers
void CTextEditorCtrl::LoadFile(const QString& sFileName)
{
if (m_filename == sFileName)
{
return;
}
m_filename = sFileName;
clear();
CCryFile file(sFileName.toUtf8().data(), "rb");
if (file.Open(sFileName.toUtf8().data(), "rb"))
{
size_t length = file.GetLength();
QByteArray text;
text.resize(static_cast<int>(length));
file.ReadRaw(text.data(), length);
setPlainText(text);
}
m_bModified = false;
}
//////////////////////////////////////////////////////////////////////////
void CTextEditorCtrl::SaveFile(const QString& sFileName)
{
if (sFileName.isEmpty())
{
return;
}
if (!CFileUtil::OverwriteFile(sFileName.toUtf8().data()))
{
return;
}
QFile file(sFileName);
file.open(QFile::WriteOnly);
file.write(toPlainText().toUtf8());
m_bModified = false;
}
//////////////////////////////////////////////////////////////////////////
void CTextEditorCtrl::OnChange()
{
m_bModified = true;
}
#include <Controls/moc_TextEditorCtrl.cpp>
-42
View File
@@ -1,42 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_TEXTEDITORCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_TEXTEDITORCTRL_H
#pragma once
// CTextEditorCtrl
#if !defined(Q_MOC_RUN)
#include <QTextEdit>
#endif
class CTextEditorCtrl
: public QTextEdit
{
Q_OBJECT
public:
CTextEditorCtrl(QWidget* pParent = nullptr);
virtual ~CTextEditorCtrl();
void LoadFile(const QString& sFileName);
void SaveFile(const QString& sFileName);
QString GetFilename() const { return m_filename; }
bool IsModified() const { return m_bModified; }
//! Must be called after OnChange message.
void OnChange();
protected:
QString m_filename;
bool m_bModified;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_TEXTEDITORCTRL_H
-322
View File
@@ -1,322 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_CONTROLS_TREECTRLUTILS_H
#define CRYINCLUDE_EDITOR_CONTROLS_TREECTRLUTILS_H
#pragma once
#include <iterator>
namespace TreeCtrlUtils
{
template <typename P>
class TreeItemIterator
: public P
{
public:
typedef P Traits;
//iterator traits, required by STL
typedef ptrdiff_t difference_type;
typedef HTREEITEM value_type;
typedef HTREEITEM* pointer;
typedef HTREEITEM& reference;
typedef std::forward_iterator_tag iterator_category;
TreeItemIterator()
: pCtrl(0)
, hItem(0) {}
explicit TreeItemIterator(const P& traits)
: P(traits)
, pCtrl(0)
, hItem(0) {}
TreeItemIterator(const TreeItemIterator& other)
: P(other)
, pCtrl(other.pCtrl)
, hItem(other.hItem) {}
TreeItemIterator(CTreeCtrl* pCtrl, HTREEITEM hItem)
: pCtrl(pCtrl)
, hItem(hItem) {}
TreeItemIterator(CTreeCtrl* pCtrl, HTREEITEM hItem, const P& traits)
: P(traits)
, pCtrl(pCtrl)
, hItem(hItem) {}
HTREEITEM operator*() {return hItem; }
bool operator==(const TreeItemIterator& other) const {return pCtrl == other.pCtrl && hItem == other.hItem; }
bool operator!=(const TreeItemIterator& other) const {return pCtrl != other.pCtrl || hItem != other.hItem; }
TreeItemIterator& operator++()
{
HTREEITEM hNextItem = 0;
if (RecurseToChildren(hItem))
{
hNextItem = (pCtrl ? pCtrl->GetChildItem(hItem) : 0);
}
while (pCtrl && hItem && !hNextItem)
{
hNextItem = pCtrl->GetNextSiblingItem(hItem);
if (!hNextItem)
{
hItem = pCtrl->GetParentItem(hItem);
}
}
hItem = hNextItem;
return *this;
}
TreeItemIterator operator++(int) {TreeItemIterator old = *this; ++(*this); return old; }
CTreeCtrl* pCtrl;
HTREEITEM hItem;
};
class NonRecursiveTreeItemIteratorTraits
{
public:
bool RecurseToChildren(HTREEITEM hItem) {return false; }
};
typedef TreeItemIterator<NonRecursiveTreeItemIteratorTraits> NonRecursiveTreeItemIterator;
class RecursiveTreeItemIteratorTraits
{
public:
bool RecurseToChildren(HTREEITEM hItem) {return true; }
};
typedef TreeItemIterator<RecursiveTreeItemIteratorTraits> RecursiveTreeItemIterator;
inline RecursiveTreeItemIterator BeginTreeItemsRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
if (hItem == 0)
{
hItem = (pCtrl ? pCtrl->GetRootItem() : 0);
}
return RecursiveTreeItemIterator(pCtrl, hItem);
}
inline RecursiveTreeItemIterator EndTreeItemsRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
HTREEITEM hEndItem = 0;
HTREEITEM hParent = hItem;
do
{
if (hParent)
{
hEndItem = pCtrl->GetNextSiblingItem(hParent);
}
hParent = (pCtrl && hParent ? pCtrl->GetParentItem(hParent) : 0);
}
while (hParent && !hEndItem);
return RecursiveTreeItemIterator(pCtrl, hEndItem);
}
inline NonRecursiveTreeItemIterator BeginTreeItemsNonRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
if (hItem == 0)
{
hItem = (pCtrl ? pCtrl->GetRootItem() : 0);
}
if (hItem)
{
hItem = pCtrl->GetChildItem(hItem);
}
return NonRecursiveTreeItemIterator(pCtrl, hItem);
}
inline NonRecursiveTreeItemIterator EndTreeItemsNonRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
HTREEITEM hEndItem = 0;
HTREEITEM hParent = 0;
while (hParent && !hEndItem)
{
hParent = (pCtrl && hItem ? pCtrl->GetParentItem(hItem) : 0);
if (hParent)
{
hEndItem = pCtrl->GetNextSiblingItem(hParent);
}
}
return NonRecursiveTreeItemIterator(pCtrl, hEndItem);
}
template <typename T, typename P>
class TreeItemDataIterator
{
public:
typedef T Type;
typedef TreeItemIterator<P> InternalIterator;
//iterator traits, required by STL
typedef ptrdiff_t difference_type;
typedef Type* value_type;
typedef Type** pointer;
typedef Type*& reference;
typedef std::forward_iterator_tag iterator_category;
TreeItemDataIterator() {}
TreeItemDataIterator(const TreeItemDataIterator& other)
: iterator(other.iterator) {AdvanceToValidIterator(); }
explicit TreeItemDataIterator(const InternalIterator& iterator)
: iterator(iterator) {AdvanceToValidIterator(); }
Type* operator*() {return reinterpret_cast<Type*>(iterator.pCtrl->GetItemData(iterator.hItem)); }
bool operator==(const TreeItemDataIterator& other) const {return iterator == other.iterator; }
bool operator!=(const TreeItemDataIterator& other) const {return iterator != other.iterator; }
HTREEITEM GetTreeItem() {return iterator.hItem; }
TreeItemDataIterator& operator++()
{
++iterator;
AdvanceToValidIterator();
return *this;
}
TreeItemDataIterator operator++(int) {TreeItemDataIterator old = *this; ++(*this); return old; }
private:
void AdvanceToValidIterator()
{
while (iterator.pCtrl && iterator.hItem && !iterator.pCtrl->GetItemData(iterator.hItem))
{
++iterator;
}
}
InternalIterator iterator;
};
template <typename T>
class RecursiveItemDataIteratorType
{
public: typedef TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits> type;
};
template <typename T>
inline TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits> BeginTreeItemDataRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
return TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits>(BeginTreeItemsRecursive(pCtrl, hItem));
}
template <typename T>
inline TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits> EndTreeItemDataRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
return TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits>(EndTreeItemsRecursive(pCtrl, hItem));
}
template <typename T>
class NonRecursiveItemDataIteratorType
{
typedef TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits> type;
};
template <typename T>
inline TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits> BeginTreeItemDataNonRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
return TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits>(BeginTreeItemsNonRecursive(pCtrl, hItem));
}
template <typename T>
inline TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits> EndTreeItemDataNonRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
return TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits>(EndTreeItemsNonRecursive(pCtrl, hItem));
}
class SelectedTreeItemIterator
{
public:
SelectedTreeItemIterator()
: pCtrl(0)
, hItem(0) {}
SelectedTreeItemIterator(const SelectedTreeItemIterator& other)
: pCtrl(other.pCtrl)
, hItem(other.hItem) {}
SelectedTreeItemIterator(CXTTreeCtrl* pCtrl, HTREEITEM hItem)
: pCtrl(pCtrl)
, hItem(hItem) {}
HTREEITEM operator*() {return hItem; }
bool operator==(const SelectedTreeItemIterator& other) const {return pCtrl == other.pCtrl && hItem == other.hItem; }
bool operator!=(const SelectedTreeItemIterator& other) const {return pCtrl != other.pCtrl || hItem != other.hItem; }
SelectedTreeItemIterator& operator++()
{
hItem = (pCtrl ? pCtrl->GetNextSelectedItem(hItem) : 0);
return *this;
}
SelectedTreeItemIterator operator++(int) {SelectedTreeItemIterator old = *this; ++(*this); return old; }
CXTTreeCtrl* pCtrl;
HTREEITEM hItem;
};
SelectedTreeItemIterator BeginSelectedTreeItems(CXTTreeCtrl* pCtrl)
{
return SelectedTreeItemIterator(pCtrl, (pCtrl ? pCtrl->GetFirstSelectedItem() : 0));
}
SelectedTreeItemIterator EndSelectedTreeItems(CXTTreeCtrl* pCtrl)
{
return SelectedTreeItemIterator(pCtrl, 0);
}
template <typename T>
class SelectedTreeItemDataIterator
{
public:
typedef T Type;
typedef SelectedTreeItemIterator InternalIterator;
SelectedTreeItemDataIterator() {}
SelectedTreeItemDataIterator(const SelectedTreeItemDataIterator& other)
: iterator(other.iterator) {AdvanceToValidIterator(); }
explicit SelectedTreeItemDataIterator(const InternalIterator& iterator)
: iterator(iterator) {AdvanceToValidIterator(); }
Type* operator*() {return reinterpret_cast<Type*>(iterator.pCtrl->GetItemData(iterator.hItem)); }
bool operator==(const SelectedTreeItemDataIterator& other) const {return iterator == other.iterator; }
bool operator!=(const SelectedTreeItemDataIterator& other) const {return iterator != other.iterator; }
HTREEITEM GetTreeItem() {return iterator.hItem; }
SelectedTreeItemDataIterator& operator++()
{
++iterator;
AdvanceToValidIterator();
return *this;
}
SelectedTreeItemDataIterator operator++(int) {SelectedTreeItemDataIterator old = *this; ++(*this); return old; }
private:
void AdvanceToValidIterator()
{
while (iterator.pCtrl && iterator.hItem && !iterator.pCtrl->GetItemData(iterator.hItem))
{
++iterator;
}
}
InternalIterator iterator;
};
template <typename T>
SelectedTreeItemDataIterator<T> BeginSelectedTreeItemData(CXTTreeCtrl* pCtrl)
{
return SelectedTreeItemDataIterator<T>(BeginSelectedTreeItems(pCtrl));
}
template <typename T>
SelectedTreeItemDataIterator<T> EndSelectedTreeItemData(CXTTreeCtrl* pCtrl)
{
return SelectedTreeItemDataIterator<T>(EndSelectedTreeItems(pCtrl));
}
}
#endif // CRYINCLUDE_EDITOR_CONTROLS_TREECTRLUTILS_H
+9 -3
View File
@@ -104,6 +104,11 @@ namespace
}
}
// Currently (December 13, 2021), this function is only used by slice editor code.
// When the slice editor is not enabled, there are no references to the
// HideActionWhileEntitiesDeselected function, causing a compiler warning and
// subsequently a build error.
#ifdef ENABLE_SLICE_EDITOR
void HideActionWhileEntitiesDeselected(QAction* action, EEditorNotifyEvent editorNotifyEvent)
{
if (action == nullptr)
@@ -127,6 +132,7 @@ namespace
break;
}
}
#endif
void DisableActionWhileInSimMode(QAction* action, EEditorNotifyEvent editorNotifyEvent)
{
@@ -374,7 +380,6 @@ QMenu* LevelEditorMenuHandler::CreateFileMenu()
{
DisableActionWhileLevelChanges(fileOpenSlice, e);
}));
#endif
// Save Selected Slice
auto saveSelectedSlice = fileMenu.AddAction(ID_FILE_SAVE_SELECTED_SLICE);
@@ -391,7 +396,7 @@ QMenu* LevelEditorMenuHandler::CreateFileMenu()
{
HideActionWhileEntitiesDeselected(saveSliceToRoot, e);
}));
#endif
// Open Recent
m_mostRecentLevelsMenu = fileMenu.AddMenu(tr("Open Recent"));
connect(m_mostRecentLevelsMenu, &QMenu::aboutToShow, this, &LevelEditorMenuHandler::UpdateMRUFiles);
@@ -439,9 +444,10 @@ QMenu* LevelEditorMenuHandler::CreateFileMenu()
// Show Log File
fileMenu.AddAction(ID_FILE_EDITLOGFILE);
#ifdef ENABLE_SLICE_EDITOR
fileMenu.AddSeparator();
fileMenu.AddAction(ID_FILE_RESAVESLICES);
#endif
fileMenu.AddSeparator();
+3 -1
View File
@@ -380,13 +380,13 @@ void CCryEditApp::RegisterActionHandlers()
ON_COMMAND(ID_IMPORT_ASSET, OnOpenAssetImporter)
ON_COMMAND(ID_EDIT_LEVELDATA, OnEditLevelData)
ON_COMMAND(ID_FILE_EDITLOGFILE, OnFileEditLogFile)
ON_COMMAND(ID_FILE_RESAVESLICES, OnFileResaveSlices)
ON_COMMAND(ID_FILE_EDITEDITORINI, OnFileEditEditorini)
ON_COMMAND(ID_PREFERENCES, OnPreferences)
ON_COMMAND(ID_REDO, OnRedo)
ON_COMMAND(ID_TOOLBAR_WIDGET_REDO, OnRedo)
ON_COMMAND(ID_FILE_OPEN_LEVEL, OnOpenLevel)
#ifdef ENABLE_SLICE_EDITOR
ON_COMMAND(ID_FILE_RESAVESLICES, OnFileResaveSlices)
ON_COMMAND(ID_FILE_NEW_SLICE, OnCreateSlice)
ON_COMMAND(ID_FILE_OPEN_SLICE, OnOpenSlice)
#endif
@@ -2629,6 +2629,7 @@ void CCryEditApp::OnFileEditLogFile()
CFileUtil::EditTextFile(CLogFile::GetLogFileName(), 0, IFileUtil::FILE_TYPE_SCRIPT);
}
#ifdef ENABLE_SLICE_EDITOR
void CCryEditApp::OnFileResaveSlices()
{
AZStd::vector<AZ::Data::AssetInfo> sliceAssetInfos;
@@ -2759,6 +2760,7 @@ void CCryEditApp::OnFileResaveSlices()
}
}
#endif
//////////////////////////////////////////////////////////////////////////
void CCryEditApp::OnFileEditEditorini()
-542
View File
@@ -1,542 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "EditorPanelUtils.h"
#include <AzCore/Utils/Utils.h>
// Qt
#include <QInputDialog>
#include <QFileDialog>
#include <QXmlStreamReader>
// Editor
#include "IEditorPanelUtils.h"
#include "Objects/EntityObject.h"
#include "CryEditDoc.h"
#include "ViewManager.h"
#include "Controls/QToolTipWidget.h"
#include "Objects/SelectionGroup.h"
#ifndef PI
#define PI 3.14159265358979323f
#endif
struct ToolTip
{
bool isValid;
QString title;
QString content;
QString specialContent;
QString disabledContent;
};
// internal implementation for better compile times - should also never be used externally, use IParticleEditorUtils interface for that.
class CEditorPanelUtils_Impl
: public IEditorPanelUtils
{
public:
void SetViewportDragOperation(void(* dropCallback)(CViewport* viewport, int dragPointX, int dragPointY, void* custom), void* custom) override
{
for (int i = 0; i < GetIEditor()->GetViewManager()->GetViewCount(); i++)
{
GetIEditor()->GetViewManager()->GetView(i)->SetGlobalDropCallback(dropCallback, custom);
}
}
public:
int PreviewWindow_GetDisplaySettingsDebugFlags(CDisplaySettings* settings) override
{
CRY_ASSERT(settings);
return settings->GetDebugFlags();
}
void PreviewWindow_SetDisplaySettingsDebugFlags(CDisplaySettings* settings, int flags) override
{
CRY_ASSERT(settings);
settings->SetDebugFlags(flags);
}
protected:
QVector<HotKey> hotkeys;
bool m_hotkeysAreEnabled;
public:
bool HotKey_Import() override
{
QVector<QPair<QString, QString> > keys;
QString filepath = QFileDialog::getOpenFileName(nullptr, "Select shortcut configuration to load",
QString(), "HotKey Config Files (*.hkxml)");
QFile file(filepath);
if (!file.open(QIODevice::ReadOnly))
{
return false;
}
QXmlStreamReader stream(&file);
bool result = true;
while (!stream.isEndDocument())
{
if (stream.isStartElement())
{
if (stream.name() == "HotKey")
{
QPair<QString, QString> key;
QXmlStreamAttributes att = stream.attributes();
for (QXmlStreamAttribute attr : att)
{
if (attr.name().compare(QLatin1String("path"), Qt::CaseInsensitive) == 0)
{
key.first = attr.value().toString();
}
if (attr.name().compare(QLatin1String("sequence"), Qt::CaseInsensitive) == 0)
{
key.second = attr.value().toString();
}
}
if (!key.first.isEmpty())
{
keys.push_back(key); // we allow blank key sequences for unassigned shortcuts
}
else
{
result = false; //but not blank paths!
}
}
}
stream.readNext();
}
file.close();
if (result)
{
HotKey_BuildDefaults();
for (QPair<QString, QString> key : keys)
{
for (int j = 0; j < hotkeys.count(); j++)
{
if (hotkeys[j].path.compare(key.first, Qt::CaseInsensitive) == 0)
{
hotkeys[j].SetPath(key.first.toStdString().c_str());
hotkeys[j].SetSequenceFromString(key.second.toStdString().c_str());
}
}
}
}
return result;
}
void HotKey_Export() override
{
auto settingDir = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / "Editor" / "Plugins" / "ParticleEditorPlugin" / "settings";
QString filepath = QFileDialog::getSaveFileName(nullptr, "Select shortcut configuration to load", settingDir.c_str(), "HotKey Config Files (*.hkxml)");
QFile file(filepath);
if (!file.open(QIODevice::WriteOnly))
{
return;
}
QXmlStreamWriter stream(&file);
stream.setAutoFormatting(true);
stream.writeStartDocument();
stream.writeStartElement("HotKeys");
for (HotKey key : hotkeys)
{
stream.writeStartElement("HotKey");
stream.writeAttribute("path", key.path);
stream.writeAttribute("sequence", key.sequence.toString());
stream.writeEndElement();
}
stream.writeEndElement();
stream.writeEndDocument();
file.close();
}
QKeySequence HotKey_GetShortcut(const char* path) override
{
for (HotKey combo : hotkeys)
{
if (combo.IsMatch(path))
{
return combo.sequence;
}
}
return QKeySequence();
}
bool HotKey_IsPressed(const QKeyEvent* event, const char* path) override
{
if (!m_hotkeysAreEnabled)
{
return false;
}
unsigned int keyInt = 0;
//Capture any modifiers
Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
if (modifiers & Qt::ShiftModifier)
{
keyInt += Qt::SHIFT;
}
if (modifiers & Qt::ControlModifier)
{
keyInt += Qt::CTRL;
}
if (modifiers & Qt::AltModifier)
{
keyInt += Qt::ALT;
}
if (modifiers & Qt::MetaModifier)
{
keyInt += Qt::META;
}
//Capture any key
keyInt += event->key();
QString t0 = QKeySequence(keyInt).toString();
QString t1 = HotKey_GetShortcut(path).toString();
//if strings match then shortcut is pressed
if (t1.compare(t0, Qt::CaseInsensitive) == 0)
{
return true;
}
return false;
}
bool HotKey_IsPressed(const QShortcutEvent* event, const char* path) override
{
if (!m_hotkeysAreEnabled)
{
return false;
}
QString t0 = event->key().toString();
QString t1 = HotKey_GetShortcut(path).toString();
//if strings match then shortcut is pressed
if (t1.compare(t0, Qt::CaseInsensitive) == 0)
{
return true;
}
return false;
}
bool HotKey_LoadExisting() override
{
QSettings settings("O3DE", "O3DE");
QString group = "Hotkeys/";
HotKey_BuildDefaults();
int size = settings.beginReadArray(group);
for (int i = 0; i < size; i++)
{
settings.setArrayIndex(i);
QPair<QString, QString> hotkey;
hotkey.first = settings.value("name").toString();
hotkey.second = settings.value("keySequence").toString();
if (!hotkey.first.isEmpty())
{
for (int j = 0; j < hotkeys.count(); j++)
{
if (hotkeys[j].path.compare(hotkey.first, Qt::CaseInsensitive) == 0)
{
hotkeys[j].SetPath(hotkey.first.toStdString().c_str());
hotkeys[j].SetSequenceFromString(hotkey.second.toStdString().c_str());
}
}
}
}
settings.endArray();
if (hotkeys.isEmpty())
{
return false;
}
return true;
}
void HotKey_SaveCurrent() override
{
QSettings settings("O3DE", "O3DE");
QString group = "Hotkeys/";
settings.remove("Hotkeys/");
settings.sync();
settings.beginWriteArray(group);
int saveIndex = 0;
for (HotKey key : hotkeys)
{
if (!key.path.isEmpty())
{
settings.setArrayIndex(saveIndex++);
settings.setValue("name", key.path);
settings.setValue("keySequence", key.sequence.toString());
}
}
settings.endArray();
settings.sync();
}
void HotKey_BuildDefaults() override
{
m_hotkeysAreEnabled = true;
QVector<QPair<QString, QString> > keys;
while (hotkeys.count() > 0)
{
hotkeys.takeAt(0);
}
//MENU SELECTION SHORTCUTS////////////////////////////////////////////////
keys.push_back(QPair<QString, QString>("Menus.File Menu", "Alt+F"));
keys.push_back(QPair<QString, QString>("Menus.Edit Menu", "Alt+E"));
keys.push_back(QPair<QString, QString>("Menus.View Menu", "Alt+V"));
//FILE MENU SHORTCUTS/////////////////////////////////////////////////////
keys.push_back(QPair<QString, QString>("File Menu.Create new emitter", "Ctrl+N"));
keys.push_back(QPair<QString, QString>("File Menu.Create new library", "Ctrl+Shift+N"));
keys.push_back(QPair<QString, QString>("File Menu.Create new folder", ""));
keys.push_back(QPair<QString, QString>("File Menu.Import", "Ctrl+I"));
keys.push_back(QPair<QString, QString>("File Menu.Import level library", "Ctrl+Shift+I"));
keys.push_back(QPair<QString, QString>("File Menu.Save", "Ctrl+S"));
keys.push_back(QPair<QString, QString>("File Menu.Close", "Ctrl+Q"));
//EDIT MENU SHORTCUTS/////////////////////////////////////////////////////
keys.push_back(QPair<QString, QString>("Edit Menu.Copy", "Ctrl+C"));
keys.push_back(QPair<QString, QString>("Edit Menu.Paste", "Ctrl+V"));
keys.push_back(QPair<QString, QString>("Edit Menu.Duplicate", "Ctrl+D"));
keys.push_back(QPair<QString, QString>("Edit Menu.Undo", "Ctrl+Z"));
keys.push_back(QPair<QString, QString>("Edit Menu.Redo", "Ctrl+Shift+Z"));
keys.push_back(QPair<QString, QString>("Edit Menu.Group", "Ctrl+G"));
keys.push_back(QPair<QString, QString>("Edit Menu.Ungroup", "Ctrl+Shift+G"));
keys.push_back(QPair<QString, QString>("Edit Menu.Rename", "Ctrl+R"));
keys.push_back(QPair<QString, QString>("Edit Menu.Reset", ""));
keys.push_back(QPair<QString, QString>("Edit Menu.Edit Hotkeys", ""));
keys.push_back(QPair<QString, QString>("Edit Menu.Assign to selected", "Ctrl+Space"));
keys.push_back(QPair<QString, QString>("Edit Menu.Insert Comment", "Ctrl+Alt+M"));
keys.push_back(QPair<QString, QString>("Edit Menu.Enable/Disable Emitter", "Ctrl+E"));
keys.push_back(QPair<QString, QString>("File Menu.Enable All", ""));
keys.push_back(QPair<QString, QString>("File Menu.Disable All", ""));
keys.push_back(QPair<QString, QString>("Edit Menu.Delete", "Del"));
//VIEW MENU SHORTCUTS/////////////////////////////////////////////////////
keys.push_back(QPair<QString, QString>("View Menu.Reset Layout", ""));
//PLAYBACK CONTROL////////////////////////////////////////////////////////
keys.push_back(QPair<QString, QString>("Previewer.Play/Pause Toggle", "Space"));
keys.push_back(QPair<QString, QString>("Previewer.Step forward through time", "c"));
keys.push_back(QPair<QString, QString>("Previewer.Loop Toggle", "z"));
keys.push_back(QPair<QString, QString>("Previewer.Reset Playback", "x"));
keys.push_back(QPair<QString, QString>("Previewer.Focus", "Ctrl+F"));
keys.push_back(QPair<QString, QString>("Previewer.Zoom In", "w"));
keys.push_back(QPair<QString, QString>("Previewer.Zoom Out", "s"));
keys.push_back(QPair<QString, QString>("Previewer.Pan Left", "a"));
keys.push_back(QPair<QString, QString>("Previewer.Pan Right", "d"));
for (QPair<QString, QString> key : keys)
{
unsigned int index = hotkeys.count();
hotkeys.push_back(HotKey());
hotkeys[index].SetPath(key.first.toStdString().c_str());
hotkeys[index].SetSequenceFromString(key.second.toStdString().c_str());
}
}
void HotKey_SetKeys(QVector<HotKey> keys) override
{
hotkeys = keys;
}
QVector<HotKey> HotKey_GetKeys() override
{
return hotkeys;
}
QString HotKey_GetPressedHotkey(const QKeyEvent* event) override
{
if (!m_hotkeysAreEnabled)
{
return "";
}
for (HotKey key : hotkeys)
{
if (HotKey_IsPressed(event, key.path.toUtf8()))
{
return key.path;
}
}
return "";
}
QString HotKey_GetPressedHotkey(const QShortcutEvent* event) override
{
if (!m_hotkeysAreEnabled)
{
return "";
}
for (HotKey key : hotkeys)
{
if (HotKey_IsPressed(event, key.path.toUtf8()))
{
return key.path;
}
}
return "";
}
//building the default hotkey list re-enables hotkeys
//do not use this when rebuilding the default list is a possibility.
void HotKey_SetEnabled(bool val) override
{
m_hotkeysAreEnabled = val;
}
bool HotKey_IsEnabled() const override
{
return m_hotkeysAreEnabled;
}
protected:
QMap<QString, ToolTip> m_tooltips;
void ToolTip_ParseNode(XmlNodeRef node)
{
if (QString(node->getTag()).compare("tooltip", Qt::CaseInsensitive) != 0)
{
unsigned int childCount = node->getChildCount();
for (unsigned int i = 0; i < childCount; i++)
{
ToolTip_ParseNode(node->getChild(i));
}
}
QString title = node->getAttr("title");
QString content = node->getAttr("content");
QString specialContent = node->getAttr("special_content");
QString disabledContent = node->getAttr("disabled_content");
QMap<QString, ToolTip>::iterator itr = m_tooltips.insert(node->getAttr("path"), ToolTip());
itr->isValid = true;
itr->title = title;
itr->content = content;
itr->specialContent = specialContent;
itr->disabledContent = disabledContent;
unsigned int childCount = node->getChildCount();
for (unsigned int i = 0; i < childCount; i++)
{
ToolTip_ParseNode(node->getChild(i));
}
}
ToolTip GetToolTip(QString path)
{
if (m_tooltips.contains(path))
{
return m_tooltips[path];
}
ToolTip temp;
temp.isValid = false;
return temp;
}
public:
void ToolTip_LoadConfigXML(QString filepath) override
{
XmlNodeRef node = GetIEditor()->GetSystem()->LoadXmlFromFile(filepath.toStdString().c_str());
ToolTip_ParseNode(node);
}
void ToolTip_BuildFromConfig(IQToolTip* tooltip, QString path, QString option, QString optionalData = "", bool isEnabled = true) override
{
AZ_Assert(tooltip, "tooltip cannot be null");
QString title = ToolTip_GetTitle(path, option);
QString content = ToolTip_GetContent(path, option);
QString specialContent = ToolTip_GetSpecialContentType(path, option);
QString disabledContent = ToolTip_GetDisabledContent(path, option);
// Even if these items are empty, we set them anyway to clear out any data that was left over from when the tooltip was used for a different object.
tooltip->SetTitle(title);
tooltip->SetContent(content);
//this only handles simple creation...if you need complex call this then add specials separate
if (!specialContent.contains("::"))
{
tooltip->AddSpecialContent(specialContent, optionalData);
}
if (!isEnabled) // If disabled, add disabled value
{
tooltip->AppendContent(disabledContent);
}
}
QString ToolTip_GetTitle(QString path, QString option) override
{
if (!option.isEmpty() && GetToolTip(path + "." + option).isValid)
{
return GetToolTip(path + "." + option).title;
}
if (!option.isEmpty() && GetToolTip("Options." + option).isValid)
{
return GetToolTip("Options." + option).title;
}
return GetToolTip(path).title;
}
QString ToolTip_GetContent(QString path, QString option) override
{
if (!option.isEmpty() && GetToolTip(path + "." + option).isValid)
{
return GetToolTip(path + "." + option).content;
}
if (!option.isEmpty() && GetToolTip("Options." + option).isValid)
{
return GetToolTip("Options." + option).content;
}
return GetToolTip(path).content;
}
QString ToolTip_GetSpecialContentType(QString path, QString option) override
{
if (!option.isEmpty() && GetToolTip(path + "." + option).isValid)
{
return GetToolTip(path + "." + option).specialContent;
}
if (!option.isEmpty() && GetToolTip("Options." + option).isValid)
{
return GetToolTip("Options." + option).specialContent;
}
return GetToolTip(path).specialContent;
}
QString ToolTip_GetDisabledContent(QString path, QString option) override
{
if (!option.isEmpty() && GetToolTip(path + "." + option).isValid)
{
return GetToolTip(path + "." + option).disabledContent;
}
if (!option.isEmpty() && GetToolTip("Options." + option).isValid)
{
return GetToolTip("Options." + option).disabledContent;
}
return GetToolTip(path).disabledContent;
}
};
IEditorPanelUtils* CreateEditorPanelUtils()
{
return new CEditorPanelUtils_Impl();
}
-16
View File
@@ -1,16 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#ifndef CRYINCLUDE_CRYEDITOR_EDITORPANELUTILS_H
#define CRYINCLUDE_CRYEDITOR_EDITORPANELUTILS_H
#pragma once
struct IEditorPanelUtils;
IEditorPanelUtils* CreateEditorPanelUtils();
#endif
-3
View File
@@ -69,7 +69,6 @@ class CSelectionTreeManager;
struct SEditorSettings;
class CGameExporter;
class IAWSResourceManager;
struct IEditorPanelUtils;
namespace WinWidget
{
@@ -526,8 +525,6 @@ struct IEditor
virtual IEditorMaterialManager* GetIEditorMaterialManager() = 0; // Vladimir@Conffx
//! Returns IconManager.
virtual IIconManager* GetIconManager() = 0;
//! Get Panel Editor Utilities
virtual IEditorPanelUtils* GetEditorPanelUtils() = 0;
//! Get Music Manager.
virtual CMusicManager* GetMusicManager() = 0;
virtual float GetTerrainElevation(float x, float y) = 0;
+1 -14
View File
@@ -81,9 +81,6 @@ AZ_POP_DISABLE_WARNING
// AWSNativeSDK
#include <AWSNativeSDKInit/AWSNativeSDKInit.h>
#include "IEditorPanelUtils.h"
#include "EditorPanelUtils.h"
#include "Core/QtEditorApplication.h" // for Editor::EditorQtApplication
static CCryEditDoc * theDocument;
@@ -143,7 +140,6 @@ CEditorImpl::CEditorImpl()
, m_QtApplication(static_cast<Editor::EditorQtApplication*>(qApp))
, m_pImageUtil(nullptr)
, m_pLogFile(nullptr)
, m_panelEditorUtils(nullptr)
{
// note that this is a call into EditorCore.dll, which stores the g_pEditorPointer for all shared modules that share EditorCore.dll
// this means that they don't need to do SetIEditor(...) themselves and its available immediately
@@ -167,8 +163,6 @@ CEditorImpl::CEditorImpl()
m_pDisplaySettings->LoadRegistry();
m_pPluginManager = new CPluginManager;
m_panelEditorUtils = CreateEditorPanelUtils();
m_pObjectManager = new CObjectManager;
m_pViewManager = new CViewManager;
m_pIconManager = new CIconManager;
@@ -301,8 +295,6 @@ CEditorImpl::~CEditorImpl()
SAFE_DELETE(m_pViewManager)
SAFE_DELETE(m_pObjectManager) // relies on prefab manager
SAFE_DELETE(m_panelEditorUtils);
// some plugins may be exporter - this must be above plugin manager delete.
SAFE_DELETE(m_pExportManager);
@@ -1445,7 +1437,7 @@ ISourceControl* CEditorImpl::GetSourceControl()
{
IClassDesc* pClass = classes[i];
ISourceControl* pSCM = nullptr;
HRESULT hRes = pClass->QueryInterface(__uuidof(ISourceControl), (void**)&pSCM);
HRESULT hRes = pClass->QueryInterface(__az_uuidof(ISourceControl), (void**)&pSCM);
if (!FAILED(hRes) && pSCM)
{
m_pSourceControl = pSCM;
@@ -1658,8 +1650,3 @@ void CEditorImpl::DestroyQMimeData(QMimeData* data) const
{
delete data;
}
IEditorPanelUtils* CEditorImpl::GetEditorPanelUtils()
{
return m_panelEditorUtils;
}
-2
View File
@@ -298,7 +298,6 @@ public:
IEditorMaterialManager* GetIEditorMaterialManager() override; // Vladimir@Conffx
IImageUtil* GetImageUtil() override; // Vladimir@conffx
SEditorSettings* GetEditorSettings() override;
IEditorPanelUtils* GetEditorPanelUtils() override;
ILogFile* GetLogFile() override { return m_pLogFile; }
void UnloadPlugins() override;
@@ -356,7 +355,6 @@ protected:
CErrorsDlg* m_pErrorsDlg;
//! Source control interface.
ISourceControl* m_pSourceControl;
IEditorPanelUtils* m_panelEditorUtils;
CSelectionTreeManager* m_pSelectionTreeManager;
-131
View File
@@ -1,131 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
#ifndef CRYINCLUDE_CRYEDITOR_IPANELEDITORUTILS_H
#define CRYINCLUDE_CRYEDITOR_IPANELEDITORUTILS_H
#pragma once
#include "Cry_Vector3.h"
#include "DisplaySettings.h"
#include "Include/IDisplayViewport.h"
#include "Include/IIconManager.h"
#include <qstringlist.h>
#include <qkeysequence.h>
#include <qevent.h>
#include <QShortcutEvent>
class CBaseObject;
class CViewport;
class IQToolTip;
struct HotKey
{
HotKey()
: path("")
, sequence(QKeySequence())
{
}
void CopyFrom(const HotKey& other)
{
path = other.path;
sequence = other.sequence;
}
void SetPath(const char* _path)
{
path = QString(_path);
}
void SetSequenceFromString(const char* _sequence)
{
sequence = QKeySequence::fromString(_sequence);
}
void SetSequence(const QKeySequence& other)
{
sequence = other;
}
bool IsMatch(QString _path)
{
return path.compare(_path, Qt::CaseInsensitive) == 0;
}
bool IsMatch(QKeySequence _sequence)
{
return sequence.matches(_sequence);
}
bool operator < (const HotKey& other) const
{
//split the paths into lists compare per level
QStringList m_categories = path.split('.');
QStringList o_categories = other.path.split('.');
int m_catSize = m_categories.size();
int o_catSize = o_categories.size();
int size = (m_catSize < o_catSize) ? m_catSize : o_catSize;
//sort categories to keep them together
for (int i = 0; i < size; i++)
{
if (m_categories[i] < o_categories[i])
{
return true;
}
if (m_categories[i] > o_categories[i])
{
return false;
}
}
//if comparing a category and a item in that category the category is < item
return m_catSize > o_catSize;
}
QKeySequence sequence;
QString path;
};
struct IEditorPanelUtils
{
virtual ~IEditorPanelUtils() {}
virtual void SetViewportDragOperation(void(*)(CViewport* viewport, int dragPointX, int dragPointY, void* custom), void* custom) = 0;
//PREVIEW WINDOW UTILS////////////////////////////////////////////////////
virtual int PreviewWindow_GetDisplaySettingsDebugFlags(CDisplaySettings* settings) = 0;
virtual void PreviewWindow_SetDisplaySettingsDebugFlags(CDisplaySettings* settings, int flags) = 0;
//HOTKEY UTILS////////////////////////////////////////////////////////////
virtual bool HotKey_Import() = 0;
virtual void HotKey_Export() = 0;
virtual QKeySequence HotKey_GetShortcut(const char* path) = 0;
virtual bool HotKey_IsPressed(const QKeyEvent* event, const char* path) = 0;
virtual bool HotKey_IsPressed(const QShortcutEvent* event, const char* path) = 0;
virtual bool HotKey_LoadExisting() = 0;
virtual void HotKey_SaveCurrent() = 0;
virtual void HotKey_BuildDefaults() = 0;
virtual void HotKey_SetKeys(QVector<HotKey> keys) = 0;
virtual QVector<HotKey> HotKey_GetKeys() = 0;
virtual QString HotKey_GetPressedHotkey(const QKeyEvent* event) = 0;
virtual QString HotKey_GetPressedHotkey(const QShortcutEvent* event) = 0;
virtual void HotKey_SetEnabled(bool val) = 0;
virtual bool HotKey_IsEnabled() const = 0;
//TOOLTIP UTILS///////////////////////////////////////////////////////////
//! Loads a table of tooltip configuration data from an xml file.
virtual void ToolTip_LoadConfigXML(QString filepath) = 0;
//! Initializes a QToolTipWidget from loaded configuration data (see ToolTip_LoadConfigXML())
//! \param tooltip Will be initialized using loaded configuration data
//! \param path Variable serialization path. Will be used as the key for looking up data in the configuration table. (ex: "Rotation.Rotation_Rate_X")
//! \param option Name of a sub-option of the variable specified by "path". (ex: "Emitter_Strength" will look up the tooltip data for "Rotation.Rotation_Rate_X.Emitter_Strength")
//! \param optionalData The argument to be used with "special_content" feature. See ToolTip_GetSpecialContentType() and QToolTipWidget::AddSpecialContent().
//! \param isEnabled If false, the tooltip will indicate the reason why the widget is disabled.
virtual void ToolTip_BuildFromConfig(IQToolTip* tooltip, QString path, QString option, QString optionalData = "", bool isEnabled = true) = 0;
virtual QString ToolTip_GetTitle(QString path, QString option = "") = 0;
virtual QString ToolTip_GetContent(QString path, QString option = "") = 0;
virtual QString ToolTip_GetSpecialContentType(QString path, QString option = "") = 0;
virtual QString ToolTip_GetDisabledContent(QString path, QString option = "") = 0;
};
#endif
+2 -5
View File
@@ -31,10 +31,7 @@ struct IUnknown
};
#endif
#ifdef __uuidof
#undef __uuidof
#endif
#define __uuidof(T) T::uuid()
#define __az_uuidof(T) T::uuid()
#if defined(AZ_PLATFORM_LINUX) || defined(AZ_PLATFORM_MAC)
@@ -107,7 +104,7 @@ struct IClassDesc
template<class Q>
HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp)
{
return QueryInterface(__uuidof(Q), (void**)pp);
return QueryInterface(__az_uuidof(Q), (void**)pp);
}
//////////////////////////////////////////////////////////////////////////
+1 -1
View File
@@ -60,7 +60,7 @@ struct IViewPaneClass
//////////////////////////////////////////////////////////////////////////
HRESULT STDMETHODCALLTYPE QueryInterface(const IID& riid, void** ppvObj)
{
if (riid == __uuidof(IViewPaneClass))
if (riid == __az_uuidof(IViewPaneClass))
{
*ppvObj = this;
return S_OK;
-1
View File
@@ -187,6 +187,5 @@ public:
MOCK_METHOD0(UnloadPlugins, void());
MOCK_METHOD0(LoadPlugins, void());
MOCK_METHOD1(GetSearchPath, QString(EEditorPathName));
MOCK_METHOD0(GetEditorPanelUtils, IEditorPanelUtils* ());
};
@@ -1,58 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include <AzTest/AzTest.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <QtUI/ClickableLabel.h>
#include <QApplication>
using namespace AZ;
using namespace ::testing;
namespace UnitTest
{
class TestingClickableLabel
: public ScopedAllocatorSetupFixture
{
public:
ClickableLabel m_clickableLabel;
};
TEST_F(TestingClickableLabel, CursorDoesNotUpdateWhileDisabled)
{
m_clickableLabel.setEnabled(false);
QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
QEnterEvent enterEvent{ QPointF(), QPointF(), QPointF() };
QApplication::sendEvent(&m_clickableLabel, &enterEvent);
const Qt::CursorShape cursorShape = QApplication::overrideCursor()->shape();
EXPECT_THAT(cursorShape, Ne(Qt::PointingHandCursor));
EXPECT_THAT(cursorShape, Eq(Qt::BlankCursor));
}
TEST_F(TestingClickableLabel, DoesNotRespondToDblClickWhileDisabled)
{
m_clickableLabel.setEnabled(false);
bool linkActivated = false;
QObject::connect(&m_clickableLabel, &QLabel::linkActivated, [&linkActivated]()
{
linkActivated = true;
});
QMouseEvent mouseEvent {
QEvent::MouseButtonDblClick, QPointF(),
Qt::LeftButton, Qt::LeftButton, Qt::NoModifier };
QApplication::sendEvent(&m_clickableLabel, &mouseEvent);
EXPECT_THAT(linkActivated, Eq(false));
}
} // namespace UnitTest
+3 -1
View File
@@ -643,11 +643,11 @@ void MainWindow::InitActions()
.SetStatusTip(tr("Create a new slice"));
am->AddAction(ID_FILE_OPEN_SLICE, tr("Open Slice..."))
.SetStatusTip(tr("Open an existing slice"));
#endif
am->AddAction(ID_FILE_SAVE_SELECTED_SLICE, tr("Save selected slice")).SetShortcut(tr("Alt+S"))
.SetStatusTip(tr("Save the selected slice to the first level root"));
am->AddAction(ID_FILE_SAVE_SLICE_TO_ROOT, tr("Save Slice to root")).SetShortcut(tr("Ctrl+Alt+S"))
.SetStatusTip(tr("Save the selected slice to the top level root"));
#endif
am->AddAction(ID_FILE_SAVE_LEVEL, tr("&Save"))
.SetShortcut(tr("Ctrl+S"))
.SetReserved()
@@ -677,7 +677,9 @@ void MainWindow::InitActions()
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateSelected);
am->AddAction(ID_FILE_EXPORTOCCLUSIONMESH, tr("Export Occlusion Mesh"));
am->AddAction(ID_FILE_EDITLOGFILE, tr("Show Log File"));
#ifdef ENABLE_SLICE_EDITOR
am->AddAction(ID_FILE_RESAVESLICES, tr("Resave All Slices"));
#endif
am->AddAction(ID_FILE_PROJECT_MANAGER_SETTINGS, tr("Edit Project Settings..."));
am->AddAction(ID_FILE_PROJECT_MANAGER_NEW, tr("New Project..."));
am->AddAction(ID_FILE_PROJECT_MANAGER_OPEN, tr("Open Project..."));
+1 -1
View File
@@ -155,7 +155,7 @@ IViewPaneClass* CClassFactory::FindViewPaneClassByTitle(const char* pPaneTitle)
{
IViewPaneClass* viewPane = nullptr;
IClassDesc* desc = m_classes[i];
if (SUCCEEDED(desc->QueryInterface(__uuidof(IViewPaneClass), (void**)&viewPane)))
if (SUCCEEDED(desc->QueryInterface(__az_uuidof(IViewPaneClass), (void**)&viewPane)))
{
if (QString::compare(viewPane->GetPaneTitle(), pPaneTitle) == 0)
{
@@ -49,7 +49,7 @@ public:
// from IUnknown
HRESULT STDMETHODCALLTYPE QueryInterface(const IID& riid, void** ppvObj)
{
if (riid == __uuidof(ISourceControl) /* && m_pIntegrator*/)
if (riid == __az_uuidof(ISourceControl) /* && m_pIntegrator*/)
{
*ppvObj = this;
return S_OK;
+1 -1
View File
@@ -50,7 +50,7 @@ CStdPreferencesClassDesc::CStdPreferencesClassDesc()
HRESULT CStdPreferencesClassDesc::QueryInterface(const IID& riid, void** ppvObj)
{
if (riid == __uuidof(IPreferencesPageCreator))
if (riid == __az_uuidof(IPreferencesPageCreator))
{
*ppvObj = (IPreferencesPageCreator*)this;
return S_OK;
-101
View File
@@ -1,101 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "ClickableLabel.h"
ClickableLabel::ClickableLabel(const QString& text, QWidget* parent)
: QLabel(parent)
, m_text(text)
, m_showDecoration(false)
{
setTextFormat(Qt::RichText);
setTextInteractionFlags(Qt::TextBrowserInteraction);
}
ClickableLabel::ClickableLabel(QWidget* parent)
: QLabel(parent)
, m_showDecoration(false)
{
setTextFormat(Qt::RichText);
setTextInteractionFlags(Qt::TextBrowserInteraction);
}
void ClickableLabel::showEvent([[maybe_unused]] QShowEvent* event)
{
updateFormatting(false);
}
void ClickableLabel::enterEvent(QEvent* ev)
{
if (!isEnabled())
{
return;
}
updateFormatting(true);
QApplication::setOverrideCursor(QCursor(Qt::PointingHandCursor));
QLabel::enterEvent(ev);
}
void ClickableLabel::leaveEvent(QEvent* ev)
{
if (!isEnabled())
{
return;
}
updateFormatting(false);
QApplication::restoreOverrideCursor();
QLabel::leaveEvent(ev);
}
void ClickableLabel::setText(const QString& text)
{
m_text = text;
QLabel::setText(text);
updateFormatting(false);
}
void ClickableLabel::setShowDecoration(bool b)
{
m_showDecoration = b;
updateFormatting(false);
}
void ClickableLabel::updateFormatting(bool mouseOver)
{
//FIXME: this should be done differently. Using a style sheet would be easiest.
QColor c = palette().color(QPalette::WindowText);
if (mouseOver || m_showDecoration)
{
QLabel::setText(QString(R"(<a href="dummy" style="color: %1";>%2</a>)").arg(c.name(), m_text));
}
else
{
QLabel::setText(m_text);
}
}
bool ClickableLabel::event(QEvent* e)
{
if (isEnabled())
{
if (e->type() == QEvent::MouseButtonDblClick)
{
emit linkActivated(QString());
return true; //ignore
}
}
return QLabel::event(e);
}
#include <QtUI/moc_ClickableLabel.cpp>
-39
View File
@@ -1,39 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#ifndef CRYINCLUDE_EDITORCOMMON_CLICKABLELABEL_H
#define CRYINCLUDE_EDITORCOMMON_CLICKABLELABEL_H
#if !defined(Q_MOC_RUN)
#include <QLabel>
#endif
class SANDBOX_API ClickableLabel
: public QLabel
{
Q_OBJECT
public:
explicit ClickableLabel(const QString& text, QWidget* parent = nullptr);
explicit ClickableLabel(QWidget* parent = nullptr);
bool event(QEvent* e) override;
void setText(const QString& text);
void setShowDecoration(bool b);
protected:
void showEvent(QShowEvent* event) override;
void enterEvent(QEvent* ev) override;
void leaveEvent(QEvent* ev) override;
private:
void updateFormatting(bool mouseOver);
QString m_text;
bool m_showDecoration;
};
#endif
+1 -1
View File
@@ -251,7 +251,7 @@ bool CFileUtil::ExtractDccFilenameFromAssetDatabase(const QString& assetFilename
for (size_t i = 0; i < assetDatabasePlugins.size(); ++i)
{
if (assetDatabasePlugins[i]->QueryInterface(__uuidof(IAssetItemDatabase), (void**)&pCurrentDatabaseInterface) == S_OK)
if (assetDatabasePlugins[i]->QueryInterface(__az_uuidof(IAssetItemDatabase), (void**)&pCurrentDatabaseInterface) == S_OK)
{
if (!pCurrentDatabaseInterface)
{
-18
View File
@@ -297,8 +297,6 @@ set(FILES
Util/AffineParts.cpp
Objects/BaseObject.cpp
Objects/BaseObject.h
Animation/AnimationBipedBoneNames.cpp
Animation/AnimationBipedBoneNames.h
AnimationContext.cpp
AnimationContext.h
AzAssetBrowser/AzAssetBrowserRequestHandler.cpp
@@ -336,23 +334,12 @@ set(FILES
Controls/ConsoleSCB.qrc
Controls/FolderTreeCtrl.cpp
Controls/FolderTreeCtrl.h
Controls/HotTrackingTreeCtrl.cpp
Controls/HotTrackingTreeCtrl.h
Controls/ImageHistogramCtrl.cpp
Controls/ImageHistogramCtrl.h
Controls/ImageListCtrl.cpp
Controls/ImageListCtrl.h
Controls/MultiMonHelper.cpp
Controls/MultiMonHelper.h
Controls/NumberCtrl.cpp
Controls/NumberCtrl.h
Controls/NumberCtrl.h
Controls/SplineCtrl.cpp
Controls/SplineCtrl.h
Controls/SplineCtrlEx.cpp
Controls/SplineCtrlEx.h
Controls/TextEditorCtrl.cpp
Controls/TextEditorCtrl.h
Controls/TimelineCtrl.cpp
Controls/TimelineCtrl.h
Controls/WndGridHelper.h
@@ -526,8 +513,6 @@ set(FILES
PythonEditorFuncs.h
QtUI/QCollapsibleGroupBox.h
QtUI/QCollapsibleGroupBox.cpp
QtUI/ClickableLabel.h
QtUI/ClickableLabel.cpp
QtUI/PixmapLabelPreview.h
QtUI/PixmapLabelPreview.cpp
QtUI/WaitCursor.h
@@ -800,9 +785,6 @@ set(FILES
ViewportTitleDlg.h
EditorEnvironment.cpp
EditorEnvironment.h
IEditorPanelUtils.h
EditorPanelUtils.h
EditorPanelUtils.cpp
)
-1
View File
@@ -8,7 +8,6 @@
set(FILES
Lib/Tests/IEditorMock.h
Lib/Tests/test_ClickableLabel.cpp
Lib/Tests/test_CryEditPythonBindings.cpp
Lib/Tests/test_CryEditDocPythonBindings.cpp
Lib/Tests/test_EditorPythonBindings.cpp
@@ -484,6 +484,7 @@ namespace AZ::IO
// as_posix
//! Replicates the behavior of the Python pathlib as_posix method
//! by replacing the Windows Path Separator with the Posix Path Seperator
constexpr string_type AsPosix() const;
AZStd::string StringAsPosix() const;
constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathStringAsPosix() const noexcept;
@@ -1043,6 +1043,13 @@ namespace AZ::IO
// as_posix
// Returns a copy of the path with the path separators converted to PosixPathSeparator
template <typename StringType>
constexpr auto BasicPath<StringType>::AsPosix() const -> string_type
{
string_type resultPath(m_path.begin(), m_path.end());
AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator);
return resultPath;
}
template <typename StringType>
AZStd::string BasicPath<StringType>::StringAsPosix() const
{
AZStd::string resultPath(m_path.begin(), m_path.end());
@@ -7,6 +7,8 @@
*/
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Serialization/Json/PathSerializer.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/functional.h>
@@ -35,10 +37,8 @@ namespace AZ::IO
size_t Save(const void* classPtr, IO::GenericStream& stream, bool) override
{
/// Save paths out using the PosixPathSeparator
PathType path(reinterpret_cast<const PathType*>(classPtr)->Native(), AZ::IO::PosixPathSeparator);
path.MakePreferred();
return static_cast<size_t>(stream.Write(path.Native().size(), path.c_str()));
auto posixPathString{ reinterpret_cast<const PathType*>(classPtr)->AsPosix() };
return static_cast<size_t>(stream.Write(posixPathString.size(), posixPathString.c_str()));
}
bool Load(void* classPtr, IO::GenericStream& stream, unsigned int, bool) override
@@ -73,5 +73,11 @@ namespace AZ::IO
AZ::SerializeContext::IDataSerializer::CreateDefaultDeleteDeleter() })
;
}
else if (auto jsonContext = azrtti_cast<JsonRegistrationContext*>(context))
{
jsonContext->Serializer<JsonPathSerializer>()
->HandlesType<Path>()
->HandlesType<FixedMaxPath>();
}
}
}
@@ -109,6 +109,7 @@ namespace AZ
*/
static EnvironmentVariable<T*> s_instance;
static AZStd::shared_mutex s_mutex;
static bool s_instanceAssigned;
};
template <typename T>
@@ -117,6 +118,9 @@ namespace AZ
template <typename T>
AZStd::shared_mutex Interface<T>::s_mutex;
template <typename T>
bool Interface<T>::s_instanceAssigned;
template <typename T>
void Interface<T>::Register(T* type)
{
@@ -135,18 +139,19 @@ namespace AZ
AZStd::unique_lock<AZStd::shared_mutex> lock(s_mutex);
s_instance = Environment::CreateVariable<T*>(GetVariableName());
s_instance.Get() = type;
s_instanceAssigned = true;
}
template <typename T>
void Interface<T>::Unregister(T* type)
{
if (!s_instance || !s_instance.Get())
if (!s_instanceAssigned)
{
AZ_Assert(false, "Interface '%s' not registered on this module!", AzTypeInfo<T>::Name());
return;
}
if (s_instance.Get() != type)
if (s_instance && s_instance.Get() != type)
{
AZ_Assert(false, "Interface '%s' is not the same instance that was registered! [Expected '%p', Found '%p']", AzTypeInfo<T>::Name(), type, s_instance.Get());
return;
@@ -156,6 +161,7 @@ namespace AZ
AZStd::unique_lock<AZStd::shared_mutex> lock(s_mutex);
*s_instance = nullptr;
s_instance.Reset();
s_instanceAssigned = false;
}
template <typename T>
@@ -165,9 +171,9 @@ namespace AZ
// This is the fast path which won't block.
{
AZStd::shared_lock<AZStd::shared_mutex> lock(s_mutex);
if (s_instance)
if (s_instanceAssigned)
{
return s_instance.Get();
return s_instance ? s_instance.Get() : nullptr;
}
}
@@ -175,6 +181,7 @@ namespace AZ
// take the full lock and request it.
AZStd::unique_lock<AZStd::shared_mutex> lock(s_mutex);
s_instance = Environment::FindVariable<T*>(GetVariableName());
s_instanceAssigned = true;
return s_instance ? s_instance.Get() : nullptr;
}
File diff suppressed because it is too large Load Diff
@@ -19,12 +19,14 @@ namespace AZ
AZ_MATH_INLINE Plane Plane::CreateFromNormalAndPoint(const Vector3& normal, const Vector3& point)
{
AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is not normalized");
return Plane(Simd::Vec4::ConstructPlane(normal.GetSimdValue(), point.GetSimdValue()));
}
AZ_MATH_INLINE Plane Plane::CreateFromNormalAndDistance(const Vector3& normal, float dist)
{
AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is not normalized");
Plane result;
result.Set(normal, dist);
return result;
@@ -33,6 +35,7 @@ namespace AZ
AZ_MATH_INLINE Plane Plane::CreateFromCoefficients(const float a, const float b, const float c, const float d)
{
AZ_MATH_ASSERT(Vector3(a, b, c).IsNormalized(), "This normal is notormalized");
Plane result;
result.Set(a, b, c, d);
return result;
@@ -65,18 +68,21 @@ namespace AZ
AZ_MATH_INLINE void Plane::Set(const Vector3& normal, float d)
{
AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is notormalized");
m_plane.Set(normal, d);
}
AZ_MATH_INLINE void Plane::Set(float a, float b, float c, float d)
{
AZ_MATH_ASSERT(Vector3(a, b, c).IsNormalized(), "This normal is notormalized");
m_plane.Set(a, b, c, d);
}
AZ_MATH_INLINE void Plane::SetNormal(const Vector3& normal)
{
AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is notormalized");
m_plane.SetX(normal.GetX());
m_plane.SetY(normal.GetY());
m_plane.SetZ(normal.GetZ());
@@ -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);
}
}
}
+18 -4
View File
@@ -54,11 +54,11 @@ namespace AZ
//! Sets components using a Vector3 for the imaginary part and a float for the real part.
static Quaternion CreateFromVector3AndValue(const Vector3& v, float w);
//! Sets the quaternion to be a rotation around a specified axis.
//! Sets the quaternion to be a rotation around a specified axis in radians.
//! @{
static Quaternion CreateRotationX(float angle);
static Quaternion CreateRotationY(float angle);
static Quaternion CreateRotationZ(float angle);
static Quaternion CreateRotationX(float angleInRadians);
static Quaternion CreateRotationY(float angleInRadians);
static Quaternion CreateRotationZ(float angleInRadians);
//! @}
//! Creates a quaternion from a Matrix3x3
@@ -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.
@@ -165,6 +168,14 @@ namespace AZ
float NormalizeWithLengthEstimate();
//! @}
//! Get the shortest equivalent of the rotation.
//! In case the w component of the quaternion is negative the rotation is > 180° and taking the longer path.
//! The quaternion will be inverted in that case to take the shortest path of rotation.
//! @{
Quaternion GetShortestEquivalent() const;
void ShortestEquivalent();
//! @}
//! Linearly interpolate towards a destination quaternion.
//! @param[in] dest The quaternion to interpolate towards.
//! @param[in] t Normalized interpolation value where 0.0 represents the current and 1.0 the destination value.
@@ -231,6 +242,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;
@@ -73,27 +73,27 @@ namespace AZ
}
AZ_MATH_INLINE Quaternion Quaternion::CreateRotationX(float angle)
AZ_MATH_INLINE Quaternion Quaternion::CreateRotationX(float angleInRadians)
{
const float halfAngle = 0.5f * angle;
const float halfAngle = 0.5f * angleInRadians;
float sin, cos;
SinCos(halfAngle, sin, cos);
return Quaternion(sin, 0.0f, 0.0f, cos);
}
AZ_MATH_INLINE Quaternion Quaternion::CreateRotationY(float angle)
AZ_MATH_INLINE Quaternion Quaternion::CreateRotationY(float angleInRadians)
{
const float halfAngle = 0.5f * angle;
const float halfAngle = 0.5f * angleInRadians;
float sin, cos;
SinCos(halfAngle, sin, cos);
return Quaternion(0.0f, sin, 0.0f, cos);
}
AZ_MATH_INLINE Quaternion Quaternion::CreateRotationZ(float angle)
AZ_MATH_INLINE Quaternion Quaternion::CreateRotationZ(float angleInRadians)
{
const float halfAngle = 0.5f * angle;
const float halfAngle = 0.5f * angleInRadians;
float sin, cos;
SinCos(halfAngle, sin, cos);
return Quaternion(0.0f, 0.0f, sin, cos);
@@ -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);
@@ -327,6 +345,23 @@ namespace AZ
}
AZ_MATH_INLINE Quaternion Quaternion::GetShortestEquivalent() const
{
if (GetW() < 0.0f)
{
return -(*this);
}
return *this;
}
AZ_MATH_INLINE void Quaternion::ShortestEquivalent()
{
*this = GetShortestEquivalent();
}
AZ_MATH_INLINE Quaternion Quaternion::Lerp(const Quaternion& dest, float t) const
{
if (Dot(dest) >= 0.0f)
+269 -275
View File
@@ -9,39 +9,38 @@
#include <AzCore/Math/Sfmt.h>
#include <AzCore/Math/Random.h>
#include <AzCore/std/parallel/lock.h>
#include <AzCore/Module/Environment.h>
#include <AzCore/std/parallel/lock.h>
#include <string.h> // for memset
namespace AZ::SfmtInternal
{
static const int N32 = N * 4;
static const int N64 = N * 2;
static const int POS1 = 122;
static const int SL1 = 18;
static const int SR1 = 11;
static const int SL2 = 1;
static const int SR2 = 1;
static const unsigned int MSK1 = 0xdfffffefU;
static const unsigned int MSK2 = 0xddfecb7fU;
static const unsigned int MSK3 = 0xbffaffffU;
static const unsigned int MSK4 = 0xbffffff6U;
static const unsigned int PARITY1 = 0x00000001U;
static const unsigned int PARITY2 = 0x00000000U;
static const unsigned int PARITY3 = 0x00000000U;
static const unsigned int PARITY4 = 0x13c9e684U;
static const int N32 = N * 4;
static const int N64 = N * 2;
static const int POS1 = 122;
static const int SL1 = 18;
static const int SR1 = 11;
static const int SL2 = 1;
static const int SR2 = 1;
static const unsigned int MSK1 = 0xdfffffefU;
static const unsigned int MSK2 = 0xddfecb7fU;
static const unsigned int MSK3 = 0xbffaffffU;
static const unsigned int MSK4 = 0xbffffff6U;
static const unsigned int PARITY1 = 0x00000001U;
static const unsigned int PARITY2 = 0x00000000U;
static const unsigned int PARITY3 = 0x00000000U;
static const unsigned int PARITY4 = 0x13c9e684U;
/** a parity check vector which certificate the period of 2^{MEXP} */
static unsigned int parity[4] = {PARITY1, PARITY2, PARITY3, PARITY4};
static unsigned int parity[4] = { PARITY1, PARITY2, PARITY3, PARITY4 };
#ifdef ONLY64
# define idxof(_i) (_i ^ 1)
#define idxof(_i) (_i ^ 1)
#else
# define idxof(_i) _i
#define idxof(_i) _i
#endif // ONLY64
#if AZ_TRAIT_USE_PLATFORM_SIMD_SSE
/**
* This function represents the recursion formula.
@@ -52,7 +51,8 @@ namespace AZ::SfmtInternal
* @param mask 128-bit mask
* @return output
*/
AZ_FORCE_INLINE static Simd::Vec4::Int32Type simd_recursion(Simd::Vec4::Int32Type* a, Simd::Vec4::Int32Type* b, Simd::Vec4::Int32Type c, Simd::Vec4::Int32Type d, Simd::Vec4::Int32Type mask)
AZ_FORCE_INLINE static Simd::Vec4::Int32Type simd_recursion(
Simd::Vec4::Int32Type* a, Simd::Vec4::Int32Type* b, Simd::Vec4::Int32Type c, Simd::Vec4::Int32Type d, Simd::Vec4::Int32Type mask)
{
Simd::Vec4::Int32Type v, x, y, z;
x = *a;
@@ -151,7 +151,7 @@ namespace AZ::SfmtInternal
inline void rshift128(w128_t* out, w128_t const* in, int shift)
{
AZ::u64 th, tl, oh, ol;
#ifdef ONLY64
#ifdef ONLY64
th = ((AZ::u64)in->u[2] << 32) | ((AZ::u64)in->u[3]);
tl = ((AZ::u64)in->u[0] << 32) | ((AZ::u64)in->u[1]);
@@ -204,7 +204,7 @@ namespace AZ::SfmtInternal
#endif
}
inline void do_recursion(w128_t* r, w128_t* a, w128_t* b, w128_t* c, w128_t* d)
inline void do_recursion(w128_t* r, w128_t* a, w128_t* b, w128_t* c, w128_t* d)
{
w128_t x;
w128_t y;
@@ -229,7 +229,7 @@ namespace AZ::SfmtInternal
inline void gen_rand_all(Sfmt& g)
{
int i;
w128_t* r1, * r2;
w128_t *r1, *r2;
r1 = &g.m_sfmt[N - 2];
r2 = &g.m_sfmt[N - 1];
@@ -257,7 +257,7 @@ namespace AZ::SfmtInternal
inline void gen_rand_array(Sfmt& g, w128_t* array, int size)
{
int i, j;
w128_t* r1, * r2;
w128_t *r1, *r2;
r1 = &g.m_sfmt[N - 2];
r2 = &g.m_sfmt[N - 1];
@@ -295,82 +295,80 @@ namespace AZ::SfmtInternal
#endif
} // namespace AZ::SfmtInternal
using namespace AZ;
//////////////////////////////////////////////////////////////////////////
// Statics
//////////////////////////////////////////////////////////////////////////
static EnvironmentVariable<AZ::Sfmt> s_sfmt;
static const char* s_globalSfmtName = "GlobalSfmt";
Sfmt& Sfmt::GetInstance()
namespace AZ
{
if (!s_sfmt)
static EnvironmentVariable<AZ::Sfmt> s_sfmt;
static const char* s_globalSfmtName = "GlobalSfmt";
Sfmt& Sfmt::GetInstance()
{
s_sfmt = AZ::Environment::FindVariable<Sfmt>(s_globalSfmtName);
if (!s_sfmt)
{
Sfmt::Create();
s_sfmt = AZ::Environment::FindVariable<Sfmt>(s_globalSfmtName);
if (!s_sfmt)
{
Sfmt::Create();
}
}
return s_sfmt.Get();
}
void Sfmt::Create()
{
if (!s_sfmt)
{
s_sfmt = AZ::Environment::CreateVariable<AZ::Sfmt>(s_globalSfmtName);
}
}
return s_sfmt.Get();
}
void Sfmt::Create()
{
if (!s_sfmt)
void Sfmt::Destroy()
{
s_sfmt = AZ::Environment::CreateVariable<AZ::Sfmt>(s_globalSfmtName);
s_sfmt.Reset();
}
}
void Sfmt::Destroy()
{
s_sfmt.Reset();
}
//=========================================================================
// Sfmt
// [4/10/2012]
//=========================================================================
Sfmt::Sfmt()
{
m_psfmt32 = &m_sfmt[0].u[0];
m_psfmt64 = reinterpret_cast<AZ::u64*>(m_psfmt32);
//=========================================================================
// Sfmt
// [4/10/2012]
//=========================================================================
Sfmt::Sfmt()
{
m_psfmt32 = &m_sfmt[0].u[0];
m_psfmt64 = reinterpret_cast<AZ::u64*>(m_psfmt32);
Seed();
}
Seed();
}
//=========================================================================
// Seed
// [4/10/2012]
//=========================================================================
Sfmt::Sfmt(AZ::u32* keys, int numKeys)
{
m_psfmt32 = &m_sfmt[0].u[0];
m_psfmt64 = reinterpret_cast<AZ::u64*>(m_psfmt32);
//=========================================================================
// Seed
// [4/10/2012]
//=========================================================================
Sfmt::Sfmt(AZ::u32* keys, int numKeys)
{
m_psfmt32 = &m_sfmt[0].u[0];
m_psfmt64 = reinterpret_cast<AZ::u64*>(m_psfmt32);
Seed(keys, numKeys);
}
Seed(keys, numKeys);
}
//=========================================================================
// Seed
// [4/10/2012]
//=========================================================================
void
Sfmt::Seed()
{
// buffer with random values
AZ::u32 buffer[32];
BetterPseudoRandom rnd;
bool result = rnd.GetRandom(buffer, sizeof(buffer));
(void)result;
AZ_Warning("System", result, "Failed to seed properly the Smft generator!");
Seed(buffer, AZ_ARRAY_SIZE(buffer));
}
//=========================================================================
// Seed
// [4/10/2012]
//=========================================================================
void Sfmt::Seed()
{
// buffer with random values
AZ::u32 buffer[32];
BetterPseudoRandom rnd;
bool result = rnd.GetRandom(buffer, sizeof(buffer));
(void)result;
AZ_Warning("System", result, "Failed to seed properly the Smft generator!");
Seed(buffer, AZ_ARRAY_SIZE(buffer));
}
/**
* This function represents a function used in the initialization
@@ -388,226 +386,222 @@ Sfmt::Seed()
*/
#define azsfmt_func2(x) ((x ^ (x >> 27)) * (AZ::u32)1566083941UL)
//=========================================================================
// Seed
// [4/10/2012]
//=========================================================================
void
Sfmt::Seed(AZ::u32* keys, int numKeys)
{
using SfmtInternal::N;
using SfmtInternal::N32;
int i, j, count;
AZ::u32 r;
int lag;
int mid;
int size = N * 4;
//=========================================================================
// Seed
// [4/10/2012]
//=========================================================================
void Sfmt::Seed(AZ::u32* keys, int numKeys)
{
using SfmtInternal::N;
using SfmtInternal::N32;
int i, j, count;
AZ::u32 r;
int lag;
int mid;
int size = N * 4;
if (size >= 623)
{
lag = 11;
}
else if (size >= 68)
{
lag = 7;
}
else if (size >= 39)
{
lag = 5;
}
else
{
lag = 3;
}
mid = (size - lag) / 2;
if (size >= 623)
{
lag = 11;
}
else if (size >= 68)
{
lag = 7;
}
else if (size >= 39)
{
lag = 5;
}
else
{
lag = 3;
}
mid = (size - lag) / 2;
memset(m_sfmt, 0x8b, sizeof(m_sfmt));
if (numKeys + 1 > SfmtInternal::N32)
{
count = numKeys + 1;
}
else
{
count = N32;
}
r = azsfmt_func1((m_psfmt32[idxof(0)] ^ m_psfmt32[idxof(mid)] ^ m_psfmt32[idxof(N32 - 1)]));
m_psfmt32[idxof(mid)] += r;
r += numKeys;
m_psfmt32[idxof(mid + lag)] += r;
m_psfmt32[idxof(0)] = r;
memset(m_sfmt, 0x8b, sizeof(m_sfmt));
if (numKeys + 1 > SfmtInternal::N32)
{
count = numKeys + 1;
}
else
{
count = N32;
}
r = azsfmt_func1((m_psfmt32[idxof(0)] ^ m_psfmt32[idxof(mid)] ^ m_psfmt32[idxof(N32 - 1)]));
m_psfmt32[idxof(mid)] += r;
r += numKeys;
m_psfmt32[idxof(mid + lag)] += r;
m_psfmt32[idxof(0)] = r;
count--;
for (i = 1, j = 0; (j < count) && (j < numKeys); j++)
{
r = azsfmt_func1((m_psfmt32[idxof(i)] ^ m_psfmt32[idxof((i + mid) % N32)] ^ m_psfmt32[idxof((i + N32 - 1) % N32)]));
m_psfmt32[idxof((i + mid) % N32)] += r;
r += keys[j] + i;
m_psfmt32[idxof((i + mid + lag) % N32)] += r;
m_psfmt32[idxof(i)] = r;
i = (i + 1) % N32;
}
for (; j < count; j++)
{
r = azsfmt_func1((m_psfmt32[idxof(i)] ^ m_psfmt32[idxof((i + mid) % N32)] ^ m_psfmt32[idxof((i + N32 - 1) % N32)]));
m_psfmt32[idxof((i + mid) % N32)] += r;
r += i;
m_psfmt32[idxof((i + mid + lag) % N32)] += r;
m_psfmt32[idxof(i)] = r;
i = (i + 1) % N32;
}
for (j = 0; j < N32; j++)
{
r = azsfmt_func2((m_psfmt32[idxof(i)] + m_psfmt32[idxof((i + mid) % N32)] + m_psfmt32[idxof((i + N32 - 1) % N32)]));
m_psfmt32[idxof((i + mid) % N32)] ^= r;
r -= i;
m_psfmt32[idxof((i + mid + lag) % N32)] ^= r;
m_psfmt32[idxof(i)] = r;
i = (i + 1) % N32;
}
count--;
for (i = 1, j = 0; (j < count) && (j < numKeys); j++)
{
r = azsfmt_func1((m_psfmt32[idxof(i)] ^ m_psfmt32[idxof((i + mid) % N32)] ^ m_psfmt32[idxof((i + N32 - 1) % N32)]));
m_psfmt32[idxof((i + mid) % N32)] += r;
r += keys[j] + i;
m_psfmt32[idxof((i + mid + lag) % N32)] += r;
m_psfmt32[idxof(i)] = r;
i = (i + 1) % N32;
}
for (; j < count; j++)
{
r = azsfmt_func1((m_psfmt32[idxof(i)] ^ m_psfmt32[idxof((i + mid) % N32)] ^ m_psfmt32[idxof((i + N32 - 1) % N32)]));
m_psfmt32[idxof((i + mid) % N32)] += r;
r += i;
m_psfmt32[idxof((i + mid + lag) % N32)] += r;
m_psfmt32[idxof(i)] = r;
i = (i + 1) % N32;
}
for (j = 0; j < N32; j++)
{
r = azsfmt_func2((m_psfmt32[idxof(i)] + m_psfmt32[idxof((i + mid) % N32)] + m_psfmt32[idxof((i + N32 - 1) % N32)]));
m_psfmt32[idxof((i + mid) % N32)] ^= r;
r -= i;
m_psfmt32[idxof((i + mid + lag) % N32)] ^= r;
m_psfmt32[idxof(i)] = r;
i = (i + 1) % N32;
}
m_index = N32;
PeriodCertification();
}
m_index = N32;
PeriodCertification();
}
#undef azsfmt_func1
#undef azsfmt_func2
//=========================================================================
// PeriodCertification
// [4/10/2012]
//=========================================================================
void
Sfmt::PeriodCertification()
{
int inner = 0;
int i, j;
AZ::u32 work;
//=========================================================================
// PeriodCertification
// [4/10/2012]
//=========================================================================
void Sfmt::PeriodCertification()
{
int inner = 0;
int i, j;
AZ::u32 work;
for (i = 0; i < 4; i++)
{
inner ^= m_psfmt32[idxof(i)] & SfmtInternal::parity[i];
}
for (i = 16; i > 0; i >>= 1)
{
inner ^= inner >> i;
}
inner &= 1;
/* check OK */
if (inner == 1)
{
return;
}
/* check NG, and modification */
for (i = 0; i < 4; i++)
{
work = 1;
for (j = 0; j < 32; j++)
for (i = 0; i < 4; i++)
{
if ((work & SfmtInternal::parity[i]) != 0)
inner ^= m_psfmt32[idxof(i)] & SfmtInternal::parity[i];
}
for (i = 16; i > 0; i >>= 1)
{
inner ^= inner >> i;
}
inner &= 1;
/* check OK */
if (inner == 1)
{
return;
}
/* check NG, and modification */
for (i = 0; i < 4; i++)
{
work = 1;
for (j = 0; j < 32; j++)
{
m_psfmt32[idxof(i)] ^= work;
return;
if ((work & SfmtInternal::parity[i]) != 0)
{
m_psfmt32[idxof(i)] ^= work;
return;
}
work = work << 1;
}
work = work << 1;
}
}
}
//=========================================================================
// Rand32
// [4/10/2012]
//=========================================================================
AZ::u32 Sfmt::Rand32()
{
int index = m_index.fetch_add(1);
if (index >= SfmtInternal::N32)
//=========================================================================
// Rand32
// [4/10/2012]
//=========================================================================
AZ::u32 Sfmt::Rand32()
{
AZStd::lock_guard<decltype(m_generationMutex)> lock(m_generationMutex);
// if this thread is the one that sets m_index to 0, then this thread
// does the generation
index += 1; // compare against the result of fetch_add(1) above
if (m_index.compare_exchange_strong(index, 0))
int index = m_index.fetch_add(1);
if (index >= SfmtInternal::N32)
{
SfmtInternal::gen_rand_all(*this);
AZStd::lock_guard<decltype(m_generationMutex)> lock(m_generationMutex);
// if this thread is the one that sets m_index to 0, then this thread
// does the generation
index += 1; // compare against the result of fetch_add(1) above
if (m_index.compare_exchange_strong(index, 0))
{
SfmtInternal::gen_rand_all(*this);
}
// try again, with the new table
return Rand32();
}
// try again, with the new table
return Rand32();
return m_psfmt32[index];
}
return m_psfmt32[index];
}
//=========================================================================
// Rand64
// [4/10/2012]
//=========================================================================
AZ::u64 Sfmt::Rand64()
{
int index = m_index.fetch_add(2);
if (index >= (SfmtInternal::N32 - 1))
//=========================================================================
// Rand64
// [4/10/2012]
//=========================================================================
AZ::u64 Sfmt::Rand64()
{
AZStd::lock_guard<decltype(m_generationMutex)> lock(m_generationMutex);
// if this thread is the one that sets m_index to 0, then this thread
// does the generation
index += 2; // compare against the result of fetch_add(2) above
if (m_index.compare_exchange_strong(index, 0))
int index = m_index.fetch_add(2);
if (index >= (SfmtInternal::N32 - 1))
{
SfmtInternal::gen_rand_all(*this);
AZStd::lock_guard<decltype(m_generationMutex)> lock(m_generationMutex);
// if this thread is the one that sets m_index to 0, then this thread
// does the generation
index += 2; // compare against the result of fetch_add(2) above
if (m_index.compare_exchange_strong(index, 0))
{
SfmtInternal::gen_rand_all(*this);
}
// try again, with the new table
return Rand64();
}
// try again, with the new table
return Rand64();
AZ::u64 r;
r = m_psfmt64[index / 2];
return r;
}
AZ::u64 r;
r = m_psfmt64[index / 2];
return r;
}
//=========================================================================
// FillArray32
// [4/10/2012]
//=========================================================================
void Sfmt::FillArray32(AZ::u32* array, int size)
{
AZ_MATH_ASSERT(m_index == SfmtInternal::N32, "Invalid m_index! Reinitialize!");
AZ_MATH_ASSERT(size % 4 == 0, "Size must be multiple of 4!");
AZ_MATH_ASSERT(size >= SfmtInternal::N32, "Size must be bigger than %d GetMinArray32Size()!", SfmtInternal::N32);
//=========================================================================
// FillArray32
// [4/10/2012]
//=========================================================================
void
Sfmt::FillArray32(AZ::u32* array, int size)
{
AZ_MATH_ASSERT(m_index == SfmtInternal::N32, "Invalid m_index! Reinitialize!");
AZ_MATH_ASSERT(size % 4 == 0, "Size must be multiple of 4!");
AZ_MATH_ASSERT(size >= SfmtInternal::N32, "Size must be bigger than %d GetMinArray32Size()!", SfmtInternal::N32);
SfmtInternal::gen_rand_array(*this, (SfmtInternal::w128_t*)array, size / 4);
m_index = SfmtInternal::N32;
}
SfmtInternal::gen_rand_array(*this, (SfmtInternal::w128_t*)array, size / 4);
m_index = SfmtInternal::N32;
}
//=========================================================================
// FillArray64
// [4/10/2012]
//=========================================================================
void Sfmt::FillArray64(AZ::u64* array, int size)
{
AZ_MATH_ASSERT(m_index == SfmtInternal::N32, "Invalid m_index! Reinitialize!");
AZ_MATH_ASSERT(size % 4 == 0, "Size must be multiple of 4!");
AZ_MATH_ASSERT(size >= SfmtInternal::N64, "Size must be bigger than %d GetMinArray64Size()!", SfmtInternal::N64);
//=========================================================================
// FillArray64
// [4/10/2012]
//=========================================================================
void
Sfmt::FillArray64(AZ::u64* array, int size)
{
AZ_MATH_ASSERT(m_index == SfmtInternal::N32, "Invalid m_index! Reinitialize!");
AZ_MATH_ASSERT(size % 4 == 0, "Size must be multiple of 4!");
AZ_MATH_ASSERT(size >= SfmtInternal::N64, "Size must be bigger than %d GetMinArray64Size()!", SfmtInternal::N64);
SfmtInternal::gen_rand_array(*this, (SfmtInternal::w128_t*)array, size / 2);
m_index = SfmtInternal::N32;
}
SfmtInternal::gen_rand_array(*this, (SfmtInternal::w128_t*)array, size / 2);
m_index = SfmtInternal::N32;
}
//=========================================================================
// GetMinArray32Size
// [4/10/2012]
//=========================================================================
int Sfmt::GetMinArray32Size() const
{
return SfmtInternal::N32;
}
//=========================================================================
// GetMinArray32Size
// [4/10/2012]
//=========================================================================
int
Sfmt::GetMinArray32Size() const
{
return SfmtInternal::N32;
}
//=========================================================================
// GetMinArray64Size
// [4/10/2012]
//=========================================================================
int Sfmt::GetMinArray64Size() const
{
return SfmtInternal::N64;
}
//=========================================================================
// GetMinArray64Size
// [4/10/2012]
//=========================================================================
int
Sfmt::GetMinArray64Size() const
{
return SfmtInternal::N64;
}
} // namespace AZ
@@ -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
@@ -9,191 +9,361 @@
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/AllocatorManager.h>
using namespace AZ;
#define RECORDING_ENABLED 0
AllocatorBase::AllocatorBase(IAllocatorAllocate* allocationSource, const char* name, const char* desc) :
IAllocator(allocationSource),
m_name(name),
m_desc(desc)
{
}
#if RECORDING_ENABLED
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);
}
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/parallel/scoped_lock.h>
const char* AllocatorBase::GetName() const
namespace
{
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)
class DebugAllocator
{
if (AZ::Environment::IsReady())
public:
using pointer_type = void*;
using size_type = AZStd::size_t;
using difference_type = AZStd::ptrdiff_t;
using allow_memory_leaks = AZStd::false_type; ///< Regular allocators should not leak.
AZ_FORCE_INLINE pointer_type allocate(size_t byteSize, size_t alignment, int = 0)
{
AllocatorManager::Instance().RegisterAllocator(this);
return AZ_OS_MALLOC(byteSize, alignment);
}
AZ_FORCE_INLINE size_type resize(pointer_type, size_type)
{
return 0;
}
AZ_FORCE_INLINE void deallocate(pointer_type ptr, size_type, size_type)
{
AZ_OS_FREE(ptr);
}
};
#pragma pack(push, 1)
struct alignas(1) AllocatorOperation
{
enum OperationType : size_t
{
ALLOCATE,
DEALLOCATE
};
OperationType m_type: 1;
size_t m_size : 28; // Can represent up to 256Mb requests
size_t m_alignment : 7; // Can represent up to 128 alignment
size_t m_recordId : 28; // Can represent up to 256M simultaneous requests, we reuse ids
};
#pragma pack(pop)
static_assert(sizeof(AllocatorOperation) == 8);
static AZStd::mutex s_operationsMutex = {};
static constexpr size_t s_maxNumberOfAllocationsToRecord = 16384;
static size_t s_numberOfAllocationsRecorded = 0;
static constexpr size_t s_allocationOperationCount = 5 * 1024;
static AZStd::array<AllocatorOperation, s_allocationOperationCount> s_operations = {};
static uint64_t s_operationCounter = 0;
static unsigned int s_nextRecordId = 1;
using AllocatorOperationByAddress = AZStd::unordered_map<void*, AllocatorOperation, AZStd::less<void*>, DebugAllocator>;
static AllocatorOperationByAddress s_allocatorOperationByAddress;
using AvailableRecordIds = AZStd::vector<unsigned int, DebugAllocator>;
AvailableRecordIds s_availableRecordIds;
void RecordAllocatorOperation(AllocatorOperation::OperationType type, void* ptr, size_t size = 0, size_t alignment = 0)
{
AZStd::scoped_lock<AZStd::mutex> lock(s_operationsMutex);
if (s_operationCounter == s_allocationOperationCount)
{
AZ::IO::SystemFile file;
int mode = AZ::IO::SystemFile::OpenMode::SF_OPEN_APPEND | AZ::IO::SystemFile::OpenMode::SF_OPEN_WRITE_ONLY;
if (!file.Exists("memoryrecordings.bin"))
{
mode |= AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE;
}
file.Open("memoryrecordings.bin", mode);
if (file.IsOpen())
{
file.Write(&s_operations, sizeof(AllocatorOperation) * s_allocationOperationCount);
file.Close();
}
s_operationCounter = 0;
}
AllocatorOperation& operation = s_operations[s_operationCounter++];
operation.m_type = type;
if (type == AllocatorOperation::OperationType::ALLOCATE)
{
if (s_numberOfAllocationsRecorded > s_maxNumberOfAllocationsToRecord)
{
// reached limit of allocations, dont record anymore
--s_operationCounter;
return;
}
++s_numberOfAllocationsRecorded;
operation.m_size = size;
operation.m_alignment = alignment;
unsigned int recordId = 0;
if (!s_availableRecordIds.empty())
{
recordId = s_availableRecordIds.back();
s_availableRecordIds.pop_back();
}
else
{
recordId = s_nextRecordId;
++s_nextRecordId;
}
operation.m_recordId = recordId;
auto it = s_allocatorOperationByAddress.emplace(ptr, operation);
if (!it.second)
{
// double alloc or resize, leave the current record and return the id
operation = it.first->second;
s_availableRecordIds.emplace_back(recordId);
}
}
else
{
AllocatorManager::PreRegisterAllocator(this);
if (ptr == nullptr)
{
// common scenario, just record the operation
operation.m_size = 0;
operation.m_alignment = 0;
operation.m_recordId = 0; // recordId = 0 will flag this case
}
else
{
auto it = s_allocatorOperationByAddress.find(ptr);
if (it != s_allocatorOperationByAddress.end())
{
operation.m_size = it->second.m_size;
operation.m_alignment = it->second.m_alignment;
operation.m_recordId = it->second.m_recordId;
s_availableRecordIds.push_back(it->second.m_recordId);
s_allocatorOperationByAddress.erase(it);
}
else
{
// just dont record this operation
--s_operationCounter;
}
}
}
}
}
#endif
const auto debugConfig = GetDebugConfig();
if (!debugConfig.m_excludeFromDebugging)
namespace AZ
{
AllocatorBase::AllocatorBase(IAllocatorAllocate* allocationSource, const char* name, const char* desc)
: IAllocator(allocationSource)
, m_name(name)
, m_desc(desc)
{
SetRecords(aznew Debug::AllocationRecords((unsigned char)debugConfig.m_stackRecordLevels, debugConfig.m_usesMemoryGuards, debugConfig.m_marksUnallocatedMemory, GetName()));
}
m_isReady = true;
}
void AllocatorBase::PreDestroy()
{
Debug::AllocationRecords* allocatorRecords = GetRecords();
if(allocatorRecords)
AllocatorBase::~AllocatorBase()
{
delete allocatorRecords;
SetRecords(nullptr);
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);
}
if (m_registrationEnabled && AZ::AllocatorManager::IsReady())
const char* AllocatorBase::GetName() const
{
AllocatorManager::Instance().UnRegisterAllocator(this);
return m_name;
}
m_isReady = false;
}
const char* AllocatorBase::GetDescription() const
{
return m_desc;
}
void AllocatorBase::SetLazilyCreated(bool lazy)
{
m_isLazilyCreated = lazy;
}
IAllocatorAllocate* AllocatorBase::GetSchema()
{
return nullptr;
}
bool AllocatorBase::IsLazilyCreated() const
{
return m_isLazilyCreated;
}
Debug::AllocationRecords* AllocatorBase::GetRecords()
{
return m_records;
}
void AllocatorBase::SetProfilingActive(bool active)
{
m_isProfilingActive = active;
}
void AllocatorBase::SetRecords(Debug::AllocationRecords* records)
{
m_records = records;
m_memoryGuardSize = records ? records->MemoryGuardSize() : 0;
}
bool AllocatorBase::IsProfilingActive() const
{
return m_isProfilingActive;
}
bool AllocatorBase::IsReady() const
{
return m_isReady;
}
void AllocatorBase::DisableOverriding()
{
m_canBeOverridden = false;
}
bool AllocatorBase::CanBeOverridden() const
{
return m_canBeOverridden;
}
void AllocatorBase::DisableRegistration()
{
m_registrationEnabled = false;
}
void AllocatorBase::PostCreate()
{
if (m_registrationEnabled)
{
if (AZ::Environment::IsReady())
{
AllocatorManager::Instance().RegisterAllocator(this);
}
else
{
AllocatorManager::PreRegisterAllocator(this);
}
}
void AllocatorBase::ProfileAllocation(void* ptr, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, int suppressStackRecord)
{
const auto debugConfig = GetDebugConfig();
if (!debugConfig.m_excludeFromDebugging)
{
SetRecords(aznew Debug::AllocationRecords(
(unsigned char)debugConfig.m_stackRecordLevels, debugConfig.m_usesMemoryGuards, debugConfig.m_marksUnallocatedMemory,
GetName()));
}
m_isReady = true;
}
void AllocatorBase::PreDestroy()
{
Debug::AllocationRecords* allocatorRecords = GetRecords();
if (allocatorRecords)
{
delete allocatorRecords;
SetRecords(nullptr);
}
if (m_registrationEnabled && AZ::AllocatorManager::IsReady())
{
AllocatorManager::Instance().UnRegisterAllocator(this);
}
m_isReady = false;
}
void AllocatorBase::SetLazilyCreated(bool lazy)
{
m_isLazilyCreated = lazy;
}
bool AllocatorBase::IsLazilyCreated() const
{
return m_isLazilyCreated;
}
void AllocatorBase::SetProfilingActive(bool active)
{
m_isProfilingActive = active;
}
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)
{
#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)
#if RECORDING_ENABLED
RecordAllocatorOperation(AllocatorOperation::ALLOCATE, ptr, byteSize, alignment);
#endif
}
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);
}
}
#if RECORDING_ENABLED
RecordAllocatorOperation(AllocatorOperation::DEALLOCATE, ptr, byteSize, alignment);
#endif
}
}
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);
}
#if RECORDING_ENABLED
RecordAllocatorOperation(AllocatorOperation::DEALLOCATE, ptr);
RecordAllocatorOperation(AllocatorOperation::ALLOCATE, newPtr, newSize, newAlignment);
#endif
}
}
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);
}
}
#if RECORDING_ENABLED
RecordAllocatorOperation(AllocatorOperation::ALLOCATE, ptr, newSize);
#endif
}
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
@@ -115,6 +115,7 @@ namespace AZ
m_ownMemoryBlock[i] = false;
}
AZ_Assert(m_desc.m_numMemoryBlocks > 0, "At least one memory block is required");
for (int i = 0; i < m_desc.m_numMemoryBlocks; ++i)
{
if (m_desc.m_memoryBlocks[i] == nullptr) // Allocate memory block if requested!
@@ -131,17 +132,6 @@ namespace AZ
m_capacity += m_desc.m_memoryBlocksByteSize[i];
}
if (m_desc.m_numMemoryBlocks == 0)
{
// Create default memory space if we can to serve for default allocations
m_memSpaces[0] = AZDLMalloc::create_mspace(0, m_desc.m_isMultithreadAlloc);
if (m_memSpaces[0])
{
AZDLMalloc::mspace_az_set_expandable(m_memSpaces[0], true);
m_capacity = Platform::GetHeapCapacity();
}
}
}
HeapSchema::~HeapSchema()
@@ -32,17 +32,11 @@ namespace AZ
*/
struct Descriptor
{
Descriptor()
: m_numMemoryBlocks(0)
, m_isMultithreadAlloc(true)
{}
static const int m_memoryBlockAlignment = 64 * 1024;
static const int m_maxNumBlocks = 5;
int m_numMemoryBlocks; ///< Number of memory blocks to use.
void* m_memoryBlocks[m_maxNumBlocks]; ///< Pointers to provided memory blocks or NULL if you want the system to allocate them for you with the System Allocator.
size_t m_memoryBlocksByteSize[m_maxNumBlocks]; ///< Sizes of different memory blocks, if m_memoryBlock is 0 the block will be allocated for you with the System Allocator.
bool m_isMultithreadAlloc; ///< Set to true to enable multi threading safe allocation.
int m_numMemoryBlocks = 1; ///< Number of memory blocks to use.
void* m_memoryBlocks[m_maxNumBlocks] = {}; ///< Pointers to provided memory blocks or NULL if you want the system to allocate them for you with the System Allocator.
size_t m_memoryBlocksByteSize[m_maxNumBlocks] = {4 * 1024}; ///< Sizes of different memory blocks, if m_memoryBlock is 0 the block will be allocated for you with the System Allocator.
bool m_isMultithreadAlloc = true; ///< Set to true to enable multi threading safe allocation.
};
HeapSchema(const Descriptor& desc);
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
@@ -18,299 +18,289 @@
#define AZCORE_SYSTEM_ALLOCATOR_HPHA 1
#define AZCORE_SYSTEM_ALLOCATOR_MALLOC 2
#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
#include <AzCore/Memory/HphaSchema.h>
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
#include <AzCore/Memory/MallocSchema.h>
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
#include <AzCore/Memory/HeapSchema.h>
#else
#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
static AZStd::aligned_storage<sizeof(MallocSchema), AZStd::alignment_of<MallocSchema>::value>::type g_systemSchema;
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
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;
#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);
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
m_allocator = azcreate(HeapSchema, (heapDesc), SystemAllocator);
m_allocator = new (&g_systemSchema) MallocSchema(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);
#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();
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
static_cast<HeapSchema*>(m_allocator)->~HeapSchema();
static_cast<MallocSchema*>(m_allocator)->~MallocSchema();
#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
@@ -1424,7 +1424,8 @@ namespace AZ
}
}
using namespace AZ;
namespace AZ
{
#ifndef AZ_USE_CUSTOM_SCRIPT_BIND
@@ -2254,6 +2255,7 @@ LUA_API const Node* lua_getDummyNode()
}
#endif // AZ_USE_CUSTOM_SCRIPT_BIND
} // namespace AZ
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
@@ -5825,7 +5827,6 @@ LUA_API const Node* lua_getDummyNode()
AllocatorWrapper<Internal::LuaSystemAllocator> m_luaAllocator;
AZStd::thread::id m_ownerThreadId; // Check if Lua methods (including EBus handlers) are called from background threads.
};
} // namespace AZ
ScriptContext::ScriptContext(ScriptContextId id, IAllocatorAllocate* allocator, lua_State* nativeContext)
{
@@ -6116,5 +6117,6 @@ LUA_API const Node* lua_getDummyNode()
{
return m_impl->ConstructScriptProperty(sdc, valueIndex, name, restrictToPropertyArrays);
}
} // namespace AZ
#undef AZ_DBG_NAME_FIXER
@@ -5,8 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_SCRIPT_CONTEXT_H
#define AZCORE_SCRIPT_CONTEXT_H
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/function/function_fwd.h>
@@ -1032,4 +1031,3 @@ namespace AZ
}
} // namespace AZ
#endif // AZCORE_SCRIPT_CONTEXT_H
@@ -25,10 +25,8 @@ extern "C" {
namespace AZ
{
void LuaHook(lua_State* l, lua_Debug* ar);
}
using namespace AZ;
/**
* A temp class that will override the current script context error handler and store the error (without any messages)
@@ -599,7 +597,7 @@ static ScriptContextDebug::BreakpointId MakeBreakpointId(const char* sourceName,
// LuaHook
// [6/28/2012]
//=========================================================================
void AZ::LuaHook(lua_State* l, lua_Debug* ar)
void LuaHook(lua_State* l, lua_Debug* ar)
{
// Read contexts
lua_rawgeti(l, LUA_REGISTRYINDEX, AZ_LUA_SCRIPT_CONTEXT_REF);
@@ -1543,4 +1541,6 @@ ScriptContextDebug::SetValue(const DebugValue& sourceValue)
return true;
}
} // namespace AZ
#endif // #if !defined(AZCORE_EXCLUDE_LUA)
@@ -5,8 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_SCRIPT_CONTEXT_DEBUG_H
#define AZCORE_SCRIPT_CONTEXT_DEBUG_H
#pragma once
#include <AzCore/Script/ScriptContext.h>
#include <AzCore/std/functional.h>
@@ -213,6 +212,3 @@ namespace AZ
ScriptContext& m_context;
};
}
#endif // AZCORE_SCRIPT_CONTEXT_DEBUG_H
#pragma once
@@ -31,7 +31,8 @@
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/std/string/conversions.h>
using namespace AZ;
namespace AZ
{
/**
* Script lifecycle:
@@ -44,8 +45,7 @@ using namespace AZ;
* If the script was loaded by a ScriptComponent, Load will be called once reload is complete.
*/
namespace
{
namespace LocalTU_ScriptSystemComponent {
// Called when a module has already been loaded
static int LuaRequireLoadedModule(lua_State* l)
{
@@ -54,8 +54,10 @@ namespace
return 1;
}
}
//=========================================================================
// ScriptSystemComponent
// [5/29/2012]
@@ -479,7 +481,7 @@ int ScriptSystemComponent::DefaultRequireHook(lua_State* lua, ScriptContext* con
scriptIt->second.m_scriptNames.emplace(module);
// Push the value to a closure that will just return it
lua_rawgeti(lua, LUA_REGISTRYINDEX, scriptIt->second.m_tableReference);
lua_pushcclosure(lua, LuaRequireLoadedModule, 1);
lua_pushcclosure(lua, LocalTU_ScriptSystemComponent::LuaRequireLoadedModule, 1);
// If asset reference already populated, just return now. Otherwise, capture reference
if (scriptIt->second.m_scriptAsset.GetId().IsValid())
@@ -519,7 +521,7 @@ int ScriptSystemComponent::DefaultRequireHook(lua_State* lua, ScriptContext* con
}
// Push function returning the result
lua_pushcclosure(lua, LuaRequireLoadedModule, 1);
lua_pushcclosure(lua, LocalTU_ScriptSystemComponent::LuaRequireLoadedModule, 1);
// Set asset reference on the loaded script
scriptIt = container->m_loadedScripts.find(scriptId.m_guid);
@@ -565,7 +567,7 @@ int ScriptSystemComponent::InMemoryRequireHook(lua_State* lua, ScriptContext* co
scriptIt->second.m_scriptNames.emplace(module);
// Push the value to a closure that will just return it
lua_rawgeti(lua, LUA_REGISTRYINDEX, scriptIt->second.m_tableReference);
lua_pushcclosure(lua, LuaRequireLoadedModule, 1);
lua_pushcclosure(lua, LocalTU_ScriptSystemComponent::LuaRequireLoadedModule, 1);
// If asset reference already populated, just return now. Otherwise, capture reference
if (scriptIt->second.m_scriptAsset.GetId().IsValid())
@@ -591,7 +593,7 @@ int ScriptSystemComponent::InMemoryRequireHook(lua_State* lua, ScriptContext* co
}
// Push function returning the result
lua_pushcclosure(lua, LuaRequireLoadedModule, 1);
lua_pushcclosure(lua, LocalTU_ScriptSystemComponent::LuaRequireLoadedModule, 1);
// Set asset reference on the loaded script
scriptIt = container->m_loadedScripts.find(scriptId.m_guid);
@@ -996,4 +998,5 @@ void ScriptSystemComponent::Reflect(ReflectContext* reflection)
}
}
} // namespace AZ
#endif // #if !defined(AZCORE_EXCLUDE_LUA)
@@ -5,8 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_SCRIPT_SYSTEM_COMPONENT_H
#define AZCORE_SCRIPT_SYSTEM_COMPONENT_H
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
@@ -182,6 +181,3 @@ namespace AZ
void OnAssetReloaded(Data::Asset<Data::AssetData> asset) override;
};
}
#endif // AZCORE_SCRIPT_SYSTEM_COMPONENT_H
#pragma once
@@ -150,7 +150,7 @@ namespace AZ
{
// Not using InsertTypeId here to avoid needing to create the temporary value and swap it in that call.
node.AddMember(rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier),
StoreTypeName(classData, context), context.GetJsonAllocator());
StoreTypeName(classData, classData.m_typeId, context), context.GetJsonAllocator());
result = ResultCode(Tasks::WriteValue, Outcomes::Success);
}
return result.Combine(StoreClass(node, object, defaultObject, classData, context));
@@ -531,7 +531,7 @@ namespace AZ
return ResolvePointerResult::ContinueProcessing;
}
rapidjson::Value JsonSerializer::StoreTypeName(const SerializeContext::ClassData& classData, JsonSerializerContext& context)
rapidjson::Value JsonSerializer::StoreTypeName(const SerializeContext::ClassData& classData, const Uuid& typeId, JsonSerializerContext& context)
{
rapidjson::Value result;
AZStd::vector<Uuid> ids = context.GetSerializeContext()->FindClassId(Crc32(classData.m_name));
@@ -544,7 +544,7 @@ namespace AZ
// Only write the Uuid for the class if there are multiple classes sharing the same name.
// In this case it wouldn't be enough to determine which class needs to be used. The
// class name is still added as a comment for be friendlier for users to read.
AZStd::string fullName = classData.m_typeId.ToString<AZStd::string>();
AZStd::string fullName = typeId.ToString<AZStd::string>();
fullName += ' ';
fullName += classData.m_name;
result.SetString(fullName.c_str(), aznumeric_caster(fullName.size()), context.GetJsonAllocator());
@@ -560,7 +560,7 @@ namespace AZ
const SerializeContext::ClassData* data = context.GetSerializeContext()->FindClassData(typeId);
if (data)
{
output = JsonSerializer::StoreTypeName(*data, context);
output = JsonSerializer::StoreTypeName(*data, typeId, context);
return context.Report(Tasks::WriteValue, Outcomes::Success, "Type id successfully stored to json value.");
}
else
@@ -580,7 +580,7 @@ namespace AZ
{
rapidjson::Value insertedObject(rapidjson::kObjectType);
insertedObject.AddMember(
rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier), StoreTypeName(classData, context),
rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier), StoreTypeName(classData, classData.m_typeId, context),
context.GetJsonAllocator());
for (auto& element : output.GetObject())
@@ -79,7 +79,7 @@ namespace AZ
const void*& object, const void*& defaultObject, AZStd::any& defaultObjectStorage,
const SerializeContext::ClassData*& elementClassData, const AZ::IRttiHelper& rtti, JsonSerializerContext& context);
static rapidjson::Value StoreTypeName(const SerializeContext::ClassData& classData, JsonSerializerContext& context);
static rapidjson::Value StoreTypeName(const SerializeContext::ClassData& classData, const Uuid& typeId, JsonSerializerContext& context);
static JsonSerializationResult::ResultCode StoreTypeName(rapidjson::Value& output,
const Uuid& typeId, JsonSerializerContext& context);
@@ -0,0 +1,127 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/StackedString.h>
#include <AzCore/Serialization/Json/PathSerializer.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/Memory/SystemAllocator.h>
namespace AZ::JsonPathSerializerInternal
{
template<typename PathType>
static JsonSerializationResult::Result Load(PathType* pathValue, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
AZ_Assert(pathValue, "Expected a valid pointer to load from json value.");
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
case rapidjson::kObjectType:
case rapidjson::kFalseType:
case rapidjson::kTrueType:
case rapidjson::kNumberType:
[[fallthrough]];
case rapidjson::kNullType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. String values can't be read from arrays, objects or null.");
case rapidjson::kStringType:
{
size_t pathLength = inputValue.GetStringLength();
if (pathLength <= pathValue->Native().max_size())
{
*pathValue = PathType(AZStd::string_view(inputValue.GetString(), pathLength)).LexicallyNormal();
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Successfully read path.");
}
using UuidString = AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>;
using ErrorString = AZStd::fixed_string<256>;
return context.Report(JsonSerializationResult::Tasks::ReadField, JSR::Outcomes::Invalid,
ErrorString::format("Json string value is too large to fit within path type %s. It needs to be less than %zu code points",
azrtti_typeid<PathType>().template ToString<UuidString>().c_str(), pathValue->Native().max_size()));
}
default:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown, "Unknown json type encountered for string value.");
}
}
template<typename PathType>
static JsonSerializationResult::Result StoreWithDefault(rapidjson::Value& outputValue, const PathType* pathValue,
const PathType* defaultPathValue, JsonSerializerContext& context)
{
namespace JSR = JsonSerializationResult; // Removes name conflicts in AzCore in uber builds.
if (context.ShouldKeepDefaults() || defaultPathValue == nullptr || *pathValue != *defaultPathValue)
{
auto posixPathString = pathValue->AsPosix();
outputValue.SetString(posixPathString.c_str(), aznumeric_caster(posixPathString.size()), context.GetJsonAllocator());
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Success, "Path successfully stored.");
}
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "Default Path used.");
}
}
namespace AZ
{
AZ_CLASS_ALLOCATOR_IMPL(JsonPathSerializer, SystemAllocator, 0);
JsonSerializationResult::Result JsonPathSerializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
if (outputValueTypeId == azrtti_typeid<AZ::IO::Path>())
{
return JsonPathSerializerInternal::Load(reinterpret_cast<AZ::IO::Path*>(outputValue), inputValue,
context);
}
else if (outputValueTypeId == azrtti_typeid<AZ::IO::FixedMaxPath>())
{
return JsonPathSerializerInternal::Load(reinterpret_cast<AZ::IO::FixedMaxPath*>(outputValue), inputValue,
context);
}
using UuidString = AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>;
auto errorTypeIdString = outputValueTypeId.ToString<UuidString>();
AZ_Assert(false, "Unable to serialize json string"
" to a path of type %s", errorTypeIdString.c_str());
using ErrorString = AZStd::fixed_string<256>;
return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::TypeMismatch,
ErrorString::format("Output value type ID %s is not a valid Path type", errorTypeIdString.c_str()));
}
JsonSerializationResult::Result JsonPathSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
{
if (valueTypeId == azrtti_typeid<AZ::IO::Path>())
{
return JsonPathSerializerInternal::StoreWithDefault(outputValue,
reinterpret_cast<const AZ::IO::Path*>(inputValue),
reinterpret_cast<const AZ::IO::Path*>(defaultValue), context);
}
else if (valueTypeId == azrtti_typeid<AZ::IO::FixedMaxPath>())
{
return JsonPathSerializerInternal::StoreWithDefault(outputValue,
reinterpret_cast<const AZ::IO::FixedMaxPath*>(inputValue),
reinterpret_cast<const AZ::IO::FixedMaxPath*>(defaultValue), context);
}
using UuidString = AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>;
auto errorTypeIdString = valueTypeId.ToString<UuidString>();
AZ_Assert(false, "Unable to serialize path type %s to a json string",
errorTypeIdString.c_str());
using ErrorString = AZStd::fixed_string<256>;
return context.Report(JsonSerializationResult::Tasks::WriteValue, JsonSerializationResult::Outcomes::TypeMismatch,
ErrorString::format("Input value type ID %s is not a valid Path type", errorTypeIdString.c_str()));
}
} // namespace AZ
@@ -0,0 +1,26 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
namespace AZ
{
class JsonPathSerializer
: public BaseJsonSerializer
{
public:
AZ_RTTI(JsonPathSerializer, "{F6FBA901-07E0-4F03-A0B6-72A9A6CE1E96}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
} // namespace AZ
@@ -45,9 +45,8 @@ namespace AZ
}
else
{
SerializerMap::const_iterator serializerIter = m_jsonSerializers.find(typeId);
AZ_Assert(serializerIter != m_jsonSerializers.end(), "Attempting to unregister a serializer that has not been registered yet with typeid %s", typeId.ToString<AZStd::string>().c_str());
m_jsonSerializers.erase(serializerIter);
[[maybe_unused]] size_t erased = m_jsonSerializers.erase(typeId);
AZ_Assert(erased == 1, "Attempting to unregister a serializer that has not been registered yet with typeid %s", typeId.ToString<AZStd::string>().c_str());
return SerializerBuilder(this, m_jsonSerializers.end());
}
}
@@ -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
}
}
@@ -533,6 +533,8 @@ set(FILES
Serialization/Json/JsonUtils.cpp
Serialization/Json/MapSerializer.h
Serialization/Json/MapSerializer.cpp
Serialization/Json/PathSerializer.h
Serialization/Json/PathSerializer.cpp
Serialization/Json/RegistrationContext.h
Serialization/Json/RegistrationContext.cpp
Serialization/Json/SmartPointerSerializer.h
+5
View File
@@ -146,6 +146,11 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
PROPERTY COMPILE_DEFINITIONS
VALUES AZCORETEST_DLL_NAME=\"$<TARGET_FILE_NAME:AzCore.Tests>\"
)
ly_add_target_files(
TARGETS AzCore.Tests
FILES ${CMAKE_CURRENT_SOURCE_DIR}/Tests/Memory/AllocatorBenchmarkRecordings.bin
OUTPUT_SUBDIRECTORY Tests/AzCore/Memory
)
endif()
@@ -11,6 +11,7 @@
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AZTestShared/Math/MathTestHelpers.h>
using namespace AZ;
@@ -408,4 +409,118 @@ 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_THAT(testQuat, IsCloseTolerance(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_THAT(testQuat, IsCloseTolerance(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_THAT(testQuat, IsCloseTolerance(backFromAxisAngle, 1e-6f));
}
INSTANTIATE_TEST_CASE_P(MATH_Quaternion, QuaternionScaledAxisAngleConversionFixture, ::testing::ValuesIn(RotationRepresentationConversionTestQuats));
TEST(MATH_Quaternion, ShortestEquivalent)
{
const AZ::Quaternion testQuat = AZ::Quaternion::CreateRotationX(AZ::Constants::HalfPi * 3.0f);
AZ::Quaternion absQuat = testQuat;
absQuat.ShortestEquivalent();
EXPECT_THAT(testQuat.GetShortestEquivalent(), IsCloseTolerance(absQuat, 1e-6f));
const float angle = absQuat.GetEulerRadians().GetX();
EXPECT_THAT(angle, testing::FloatEq(-AZ::Constants::HalfPi));
}
}
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:281ba03e79ecba90b313a0b17bdba87c57d76b504b6e38d579b5eabd995902cc
size 245760
@@ -0,0 +1,591 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#if defined(HAVE_BENCHMARK)
#include <AzCore/PlatformIncl.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Memory/BestFitExternalMapAllocator.h>
#include <AzCore/Memory/HeapSchema.h>
#include <AzCore/Memory/HphaSchema.h>
#include <AzCore/Memory/MallocSchema.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Memory/PoolSchema.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Utils/Utils.h>
#include <benchmark/benchmark.h>
namespace Benchmark
{
namespace Platform
{
size_t GetProcessMemoryUsageBytes();
size_t GetMemorySize(void* memory);
}
/// <summary>
/// Test allocator wrapper that redirects the calls to the passed TAllocator by using AZ::AllocatorInstance.
/// It also creates/destroys the TAllocator type (to reflect what happens at runtime)
/// </summary>
/// <typeparam name="TAllocator">Allocator type to wrap</typeparam>
template<typename TAllocator>
class TestAllocatorWrapper
{
public:
static void SetUp()
{
AZ::AllocatorInstance<TAllocator>::Create();
}
static void TearDown()
{
AZ::AllocatorInstance<TAllocator>::Destroy();
}
static void* Allocate(size_t byteSize, size_t alignment)
{
return AZ::AllocatorInstance<TAllocator>::Get().Allocate(byteSize, alignment);
}
static void DeAllocate(void* ptr, size_t byteSize = 0)
{
AZ::AllocatorInstance<TAllocator>::Get().DeAllocate(ptr, byteSize);
}
static void* ReAllocate(void* ptr, size_t newSize, size_t newAlignment)
{
return AZ::AllocatorInstance<TAllocator>::Get().ReAllocate(ptr, newSize, newAlignment);
}
static size_t Resize(void* ptr, size_t newSize)
{
return AZ::AllocatorInstance<TAllocator>::Get().Resize(ptr, newSize);
}
static void GarbageCollect()
{
AZ::AllocatorInstance<TAllocator>::Get().GarbageCollect();
}
static size_t NumAllocatedBytes()
{
return AZ::AllocatorInstance<TAllocator>::Get().NumAllocatedBytes() +
AZ::AllocatorInstance<TAllocator>::Get().GetUnAllocatedMemory();
}
static size_t GetSize(void* ptr)
{
return AZ::AllocatorInstance<TAllocator>::Get().AllocationSize(ptr);
}
};
/// <summary>
/// Basic allocator used as a baseline. This allocator is the most basic allocation possible with the OS (AZ_OS_MALLOC).
/// MallocSchema cannot be used here because it has extra logic that we don't want to use as a baseline.
/// </summary>
class RawMallocAllocator {};
template<>
class TestAllocatorWrapper<RawMallocAllocator>
{
public:
TestAllocatorWrapper()
{
s_numAllocatedBytes = 0;
}
static void SetUp()
{
s_numAllocatedBytes = 0;
}
static void TearDown()
{
}
// IAllocatorAllocate
static void* Allocate(size_t byteSize, size_t)
{
s_numAllocatedBytes += byteSize;
// Don't pass an alignment since we wont be able to get the memory size without also passing the alignment
return AZ_OS_MALLOC(byteSize, 1);
}
static void DeAllocate(void* ptr, size_t = 0)
{
s_numAllocatedBytes -= Platform::GetMemorySize(ptr);
AZ_OS_FREE(ptr);
}
static void* ReAllocate(void* ptr, size_t newSize, size_t)
{
s_numAllocatedBytes -= Platform::GetMemorySize(ptr);
AZ_OS_FREE(ptr);
s_numAllocatedBytes += newSize;
return AZ_OS_MALLOC(newSize, 1);
}
static size_t Resize(void* ptr, size_t newSize)
{
AZ_UNUSED(ptr);
AZ_UNUSED(newSize);
return 0;
}
static void GarbageCollect() {}
static size_t NumAllocatedBytes()
{
return s_numAllocatedBytes;
}
static size_t GetSize(void* ptr)
{
return Platform::GetMemorySize(ptr);
}
private:
static size_t s_numAllocatedBytes;
};
size_t TestAllocatorWrapper<RawMallocAllocator>::s_numAllocatedBytes = 0;
// Some allocator are not fully declared, those we simply setup from the schema
class MallocSchemaAllocator : public AZ::SimpleSchemaAllocator<AZ::MallocSchema>
{
public:
AZ_TYPE_INFO(MallocSchemaAllocator, "{3E68224F-E676-402C-8276-CE4B49C05E89}");
MallocSchemaAllocator()
: AZ::SimpleSchemaAllocator<AZ::MallocSchema>("MallocSchemaAllocator", "")
{}
};
// We use both this HphaSchemaAllocator and the SystemAllocator configured with Hpha because the SystemAllocator
// has extra things
class HphaSchemaAllocator : public AZ::SimpleSchemaAllocator<AZ::HphaSchema>
{
public:
AZ_TYPE_INFO(HphaSchemaAllocator, "{6563AB4B-A68E-4499-8C98-D61D640D1F7F}");
HphaSchemaAllocator()
: AZ::SimpleSchemaAllocator<AZ::HphaSchema>("TestHphaSchemaAllocator", "")
{}
};
// For the SystemAllocator we inherit so we have a different stack. The SystemAllocator is used globally so we dont want
// to get that data affecting the benchmark
class TestSystemAllocator : public AZ::SystemAllocator
{
public:
AZ_TYPE_INFO(TestSystemAllocator, "{360D4DAA-D65D-4D5C-A6FA-1A4C5261C35C}");
TestSystemAllocator()
: AZ::SystemAllocator()
{
}
};
// Allocated bytes reported by the allocator
static const char* s_counterAllocatorMemory = "Allocator_Memory";
// Allocated bytes as counted by the benchmark
static const char* s_counterBenchmarkMemory = "Benchmark_Memory";
enum AllocationSize
{
SMALL,
BIG,
MIXED,
COUNT
};
static const size_t s_kiloByte = 1024;
static const size_t s_megaByte = s_kiloByte * s_kiloByte;
using AllocationSizeArray = AZStd::array<size_t, 10>;
static const AZStd::array<AllocationSizeArray, COUNT> s_allocationSizes = {
/* SMALL */ AllocationSizeArray{ 2, 16, 20, 59, 100, 128, 160, 250, 300, 512 },
/* BIG */ AllocationSizeArray{ 513, s_kiloByte, 2 * s_kiloByte, 4 * s_kiloByte, 10 * s_kiloByte, 64 * s_kiloByte, 128 * s_kiloByte, 200 * s_kiloByte, s_megaByte, 2 * s_megaByte },
/* MIXED */ AllocationSizeArray{ 2, s_kiloByte, 59, 4 * s_kiloByte, 128, 200 * s_kiloByte, 250, s_megaByte, 512, 2 * s_megaByte }
};
template <typename TAllocator>
class AllocatorBenchmarkFixture
: public ::benchmark::Fixture
{
protected:
using TestAllocatorType = TestAllocatorWrapper<TAllocator>;
virtual void internalSetUp(const ::benchmark::State& state)
{
if (state.thread_index == 0) // Only setup in the first thread
{
TestAllocatorType::SetUp();
m_allocations.resize(state.threads);
for (auto& perThreadAllocations : m_allocations)
{
perThreadAllocations.resize(state.range(0), nullptr);
}
}
}
virtual void internalTearDown(const ::benchmark::State& state)
{
if (state.thread_index == 0) // Only setup in the first thread
{
m_allocations.clear();
m_allocations.shrink_to_fit();
TestAllocatorType::TearDown();
}
}
AZStd::vector<void*>& GetPerThreadAllocations(size_t threadIndex)
{
return m_allocations[threadIndex];
}
public:
void SetUp(const ::benchmark::State& state) override
{
internalSetUp(state);
}
void SetUp(::benchmark::State& state) override
{
internalSetUp(state);
}
void TearDown(const ::benchmark::State& state) override
{
internalTearDown(state);
}
void TearDown(::benchmark::State& state) override
{
internalTearDown(state);
}
private:
AZStd::vector<AZStd::vector<void*>> m_allocations;
};
template <typename TAllocator, AllocationSize TAllocationSize>
class AllocationBenchmarkFixture
: public AllocatorBenchmarkFixture<TAllocator>
{
using base = AllocatorBenchmarkFixture<TAllocator>;
using TestAllocatorType = typename base::TestAllocatorType;
public:
void Benchmark(benchmark::State& state)
{
for (auto _ : state)
{
state.PauseTiming();
AZStd::vector<void*>& perThreadAllocations = base::GetPerThreadAllocations(state.thread_index);
const size_t numberOfAllocations = perThreadAllocations.size();
size_t totalAllocationSize = 0;
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
{
const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize];
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
totalAllocationSize += allocationSize;
state.ResumeTiming();
perThreadAllocations[allocationIndex] = TestAllocatorType::Allocate(allocationSize, 0);
state.PauseTiming();
}
state.counters[s_counterAllocatorMemory] = benchmark::Counter(static_cast<double>(TestAllocatorType::NumAllocatedBytes()), benchmark::Counter::kDefaults);
state.counters[s_counterBenchmarkMemory] = benchmark::Counter(static_cast<double>(totalAllocationSize), benchmark::Counter::kDefaults);
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
{
const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize];
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
TestAllocatorType::DeAllocate(perThreadAllocations[allocationIndex], allocationSize);
perThreadAllocations[allocationIndex] = nullptr;
}
TestAllocatorType::GarbageCollect();
state.SetItemsProcessed(numberOfAllocations);
}
}
};
template <typename TAllocator, AllocationSize TAllocationSize>
class DeAllocationBenchmarkFixture
: public AllocatorBenchmarkFixture<TAllocator>
{
using base = AllocatorBenchmarkFixture<TAllocator>;
using TestAllocatorType = typename base::TestAllocatorType;
public:
void Benchmark(benchmark::State& state)
{
for (auto _ : state)
{
state.PauseTiming();
AZStd::vector<void*>& perThreadAllocations = base::GetPerThreadAllocations(state.thread_index);
const size_t numberOfAllocations = perThreadAllocations.size();
size_t totalAllocationSize = 0;
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
{
const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize];
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
totalAllocationSize += allocationSize;
perThreadAllocations[allocationIndex] = TestAllocatorType::Allocate(allocationSize, 0);
}
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
{
const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize];
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
state.ResumeTiming();
TestAllocatorType::DeAllocate(perThreadAllocations[allocationIndex], allocationSize);
state.PauseTiming();
perThreadAllocations[allocationIndex] = nullptr;
}
state.counters[s_counterAllocatorMemory] = benchmark::Counter(static_cast<double>(TestAllocatorType::NumAllocatedBytes()), benchmark::Counter::kDefaults);
state.counters[s_counterBenchmarkMemory] = benchmark::Counter(static_cast<double>(totalAllocationSize), benchmark::Counter::kDefaults);
state.SetItemsProcessed(numberOfAllocations);
TestAllocatorType::GarbageCollect();
}
}
};
template<typename TAllocator>
class RecordedAllocationBenchmarkFixture : public ::benchmark::Fixture
{
using TestAllocatorType = TestAllocatorWrapper<TAllocator>;
virtual void internalSetUp()
{
TestAllocatorType::SetUp();
}
void internalTearDown()
{
TestAllocatorType::TearDown();
}
#pragma pack(push, 1)
struct alignas(1) AllocatorOperation
{
enum OperationType : size_t
{
ALLOCATE,
DEALLOCATE
};
OperationType m_type : 1;
size_t m_size : 28; // Can represent up to 256Mb requests
size_t m_alignment : 7; // Can represent up to 128 alignment
size_t m_recordId : 28; // Can represent up to 256M simultaneous requests, we reuse ids
};
#pragma pack(pop)
static_assert(sizeof(AllocatorOperation) == 8);
public:
void SetUp(const ::benchmark::State&) override
{
internalSetUp();
}
void SetUp(::benchmark::State&) override
{
internalSetUp();
}
void TearDown(const ::benchmark::State&) override
{
internalTearDown();
}
void TearDown(::benchmark::State&) override
{
internalTearDown();
}
void Benchmark(benchmark::State& state)
{
for (auto _ : state)
{
state.PauseTiming();
AZStd::unordered_map<size_t, void*> pointerRemapping;
constexpr size_t allocationOperationCount = 5 * 1024;
AZStd::array<AllocatorOperation, allocationOperationCount> m_operations = {};
[[maybe_unused]] const size_t operationSize = sizeof(AllocatorOperation);
size_t totalAllocationSize = 0;
size_t itemsProcessed = 0;
for (size_t i = 0; i < 100; ++i) // play the recording multiple times to get a good stable sample, this way we can keep a smaller recording
{
AZ::IO::SystemFile file;
AZ::IO::FixedMaxPathString filePath = AZ::Utils::GetExecutableDirectory();
filePath += "/Tests/AzCore/Memory/AllocatorBenchmarkRecordings.bin";
if (!file.Open(filePath.c_str(), AZ::IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
{
return;
}
size_t elementsRead =
file.Read(sizeof(AllocatorOperation) * allocationOperationCount, &m_operations) / sizeof(AllocatorOperation);
itemsProcessed += elementsRead;
while (elementsRead > 0)
{
for (size_t operationIndex = 0; operationIndex < elementsRead; ++operationIndex)
{
const AllocatorOperation& operation = m_operations[operationIndex];
if (operation.m_type == AllocatorOperation::ALLOCATE)
{
const auto it = pointerRemapping.emplace(operation.m_recordId, nullptr);
if (it.second) // otherwise already allocated
{
state.ResumeTiming();
void* ptr = TestAllocatorType::Allocate(operation.m_size, operation.m_alignment);
state.PauseTiming();
totalAllocationSize += operation.m_size;
it.first->second = ptr;
}
else
{
// Doing a resize, dont account for this memory change, this operation is rare and we dont have
// the size of the previous allocation
state.ResumeTiming();
TestAllocatorType::Resize(it.first->second, operation.m_size);
state.PauseTiming();
}
}
else // AllocatorOperation::DEALLOCATE:
{
if (operation.m_recordId)
{
const auto ptrIt = pointerRemapping.find(operation.m_recordId);
if (ptrIt != pointerRemapping.end())
{
totalAllocationSize -= operation.m_size;
state.ResumeTiming();
TestAllocatorType::DeAllocate(
ptrIt->second,
/*operation.m_size*/ 0); // size is not correct after a resize, a 0 size deals with it
state.PauseTiming();
pointerRemapping.erase(ptrIt);
}
}
else // deallocate(nullptr) are recorded
{
// Just to account of the call of deallocate(nullptr);
state.ResumeTiming();
TestAllocatorType::DeAllocate(nullptr, /*operation.m_size*/ 0);
state.PauseTiming();
}
}
}
elementsRead =
file.Read(sizeof(AllocatorOperation) * allocationOperationCount, &m_operations) / sizeof(AllocatorOperation);
itemsProcessed += elementsRead;
}
file.Close();
// Deallocate the remainder (since we stopped the recording middle-game)(there are leaks as well)
for (const auto& pointerMapping : pointerRemapping)
{
state.ResumeTiming();
TestAllocatorType::DeAllocate(pointerMapping.second);
state.PauseTiming();
}
itemsProcessed += pointerRemapping.size();
pointerRemapping.clear();
}
state.counters[s_counterAllocatorMemory] = benchmark::Counter(static_cast<double>(TestAllocatorType::NumAllocatedBytes()), benchmark::Counter::kDefaults);
state.counters[s_counterBenchmarkMemory] = benchmark::Counter(static_cast<double>(totalAllocationSize), benchmark::Counter::kDefaults);
state.SetItemsProcessed(itemsProcessed);
TestAllocatorType::GarbageCollect();
}
}
};
// For non-threaded ranges, run 100, 400, 1600 amounts
static void RunRanges(benchmark::internal::Benchmark* b)
{
for (int i = 0; i < 6; i += 2)
{
b->Arg((1 << i) * 100);
}
}
static void RecordedRunRanges(benchmark::internal::Benchmark* b)
{
b->Iterations(1);
}
// For threaded ranges, run just 200, multi-threaded will already multiply by thread
static void ThreadedRunRanges(benchmark::internal::Benchmark* b)
{
b->Arg(100);
}
// Test under and over-subscription of threads vs the amount of CPUs available
static const unsigned int MaxThreadRange = 2 * AZStd::thread::hardware_concurrency();
#define BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME, ...) \
BENCHMARK_TEMPLATE_DEFINE_F(FIXTURE, TESTNAME, __VA_ARGS__)(benchmark::State& state) { Benchmark(state); } \
BENCHMARK_REGISTER_F(FIXTURE, TESTNAME)
// We test small/big/mixed allocations in single-threaded environments. For multi-threaded environments, we test mixed since
// the multi threaded fixture will run multiple passes (1, 2, 4, ... until 2*hardware_concurrency)
#define BM_REGISTER_SIZE_FIXTURES(FIXTURE, TESTNAME, ALLOCATORTYPE) \
BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME##_SMALL, ALLOCATORTYPE, SMALL)->Apply(RunRanges); \
BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME##_BIG, ALLOCATORTYPE, BIG)->Apply(RunRanges); \
BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME##_MIXED, ALLOCATORTYPE, MIXED)->Apply(RunRanges); \
BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME##_MIXED_THREADED, ALLOCATORTYPE, MIXED)->ThreadRange(2, MaxThreadRange)->Apply(ThreadedRunRanges);
#define BM_REGISTER_ALLOCATOR(TESTNAME, ALLOCATORTYPE) \
namespace BM_##TESTNAME \
{ \
BM_REGISTER_SIZE_FIXTURES(AllocationBenchmarkFixture, TESTNAME, ALLOCATORTYPE); \
BM_REGISTER_SIZE_FIXTURES(DeAllocationBenchmarkFixture, TESTNAME, ALLOCATORTYPE); \
BM_REGISTER_TEMPLATE(RecordedAllocationBenchmarkFixture, TESTNAME, ALLOCATORTYPE)->Apply(RecordedRunRanges); \
}
/// Warm up benchmark used to prepare the OS for allocations. Most OS keep allocations for a process somehow
/// reserved. So the first allocations run always get a bigger impact in a process. This warm up allocator runs
/// all the benchmarks and is just used for the the next allocators to report more consistent results.
BM_REGISTER_ALLOCATOR(WarmUpAllocator, RawMallocAllocator);
BM_REGISTER_ALLOCATOR(RawMallocAllocator, RawMallocAllocator);
BM_REGISTER_ALLOCATOR(MallocSchemaAllocator, MallocSchemaAllocator);
BM_REGISTER_ALLOCATOR(HphaSchemaAllocator, HphaSchemaAllocator);
BM_REGISTER_ALLOCATOR(SystemAllocator, TestSystemAllocator);
//BM_REGISTER_ALLOCATOR(BestFitExternalMapAllocator, BestFitExternalMapAllocator); // Requires to pre-allocate blocks and cannot work as a general-purpose allocator
//BM_REGISTER_ALLOCATOR(HeapSchemaAllocator, TestHeapSchemaAllocator); // Requires to pre-allocate blocks and cannot work as a general-purpose allocator
//BM_REGISTER_SCHEMA(PoolSchema); // Requires special alignment requests while allocating
#undef BM_REGISTER_ALLOCATOR
#undef BM_REGISTER_SIZE_FIXTURES
#undef BM_REGISTER_TEMPLATE
} // Benchmark
#endif // HAVE_BENCHMARK
@@ -10,10 +10,6 @@
#include <AzCore/Memory/HphaSchema.h>
#include <AzCore/std/containers/vector.h>
#if defined(HAVE_BENCHMARK)
#include <benchmark/benchmark.h>
#endif // HAVE_BENCHMARK
class HphaSchema_TestAllocator
: public AZ::SimpleSchemaAllocator<AZ::HphaSchema>
{
@@ -112,87 +108,3 @@ namespace UnitTest
HphaSchemaTestFixture,
::testing::ValuesIn(s_mixedInstancesParameters));
}
#if defined(HAVE_BENCHMARK)
namespace Benchmark
{
class HphaSchemaBenchmarkFixture
: public ::benchmark::Fixture
{
void internalSetUp()
{
AZ::AllocatorInstance<HphaSchema_TestAllocator>::Create();
}
void internalTearDown()
{
AZ::AllocatorInstance<HphaSchema_TestAllocator>::Destroy();
}
public:
void SetUp(const benchmark::State&) override
{
internalSetUp();
}
void SetUp(benchmark::State&) override
{
internalSetUp();
}
void TearDown(const benchmark::State&) override
{
internalTearDown();
}
void TearDown(benchmark::State&) override
{
internalTearDown();
}
static void BM_Allocations(benchmark::State& state, const AllocationSizeArray& allocationArray)
{
AZStd::vector<void*> allocations;
while (state.KeepRunning())
{
state.PauseTiming();
const size_t allocationIndex = allocations.size();
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
state.ResumeTiming();
void* allocation = AZ::AllocatorInstance<HphaSchema_TestAllocator>::Get().Allocate(allocationSize, 0);
state.PauseTiming();
allocations.emplace_back(allocation);
state.ResumeTiming();
}
const size_t numberOfAllocations = allocations.size();
state.SetItemsProcessed(numberOfAllocations);
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
{
AZ::AllocatorInstance<HphaSchema_TestAllocator>::Get().DeAllocate(allocations[allocationIndex], allocationArray[allocationIndex % allocationArray.size()]);
}
AZ::AllocatorInstance<HphaSchema_TestAllocator>::Get().GarbageCollect();
}
};
// Small allocations, these are allocations that are going to end up in buckets in the HphaSchema
BENCHMARK_F(HphaSchemaBenchmarkFixture, SmallAllocations)(benchmark::State& state)
{
BM_Allocations(state, s_smallAllocationSizes);
}
BENCHMARK_F(HphaSchemaBenchmarkFixture, BigAllocations)(benchmark::State& state)
{
BM_Allocations(state, s_bigAllocationSizes);
}
BENCHMARK_F(HphaSchemaBenchmarkFixture, MixedAllocations)(benchmark::State& state)
{
BM_Allocations(state, s_mixedAllocationSizes);
}
} // Benchmark
#endif // HAVE_BENCHMARK
@@ -0,0 +1,31 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/PlatformIncl.h>
#include <AzCore/Debug/Trace.h>
#include <malloc.h>
#include <sys/resource.h>
namespace Benchmark
{
namespace Platform
{
size_t GetProcessMemoryUsageBytes()
{
struct rusage rusage;
getrusage(RUSAGE_SELF, &rusage);
return rusage.ru_maxrss * 1024L;
}
size_t GetMemorySize(void* memory)
{
return memory ? malloc_usable_size(memory) : 0;
}
}
}
@@ -8,4 +8,5 @@
set(FILES
Tests/UtilsTests_Android.cpp
Tests/Memory/AllocatorBenchmarks_Android.cpp
)
@@ -0,0 +1,31 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/PlatformIncl.h>
#include <AzCore/Debug/Trace.h>
#include <malloc.h>
#include <sys/resource.h>
namespace Benchmark
{
namespace Platform
{
size_t GetProcessMemoryUsageBytes()
{
struct rusage rusage;
getrusage(RUSAGE_SELF, &rusage);
return rusage.ru_maxrss * 1024L;
}
size_t GetMemorySize(void* memory)
{
return memory ? malloc_usable_size(memory) : 0;
}
}
}
@@ -9,4 +9,5 @@
set(FILES
Tests/UtilsTests_Linux.cpp
../Common/UnixLike/Tests/UtilsTests_UnixLike.cpp
Tests/Memory/AllocatorBenchmarks_Linux.cpp
)

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