Merge branch 'development' into memory/overrideshim_removal
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> # Conflicts: # Code/Framework/AzCore/AzCore/Memory/AllocatorBase.cpp # Code/Framework/AzCore/AzCore/Memory/BestFitExternalMapAllocator.cpp # Code/Framework/AzCore/AzCore/Memory/BestFitExternalMapSchema.cpp # Code/Framework/AzCore/AzCore/Memory/PoolSchema.cpp # Code/Framework/AzCore/AzCore/Memory/SystemAllocator.cpp # Code/Framework/AzCore/Tests/Memory/AllocatorBenchmarks.cpp
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -30,8 +30,6 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void OnCustomerAgreement();
|
||||
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
|
||||
@@ -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><a href="https://www.o3debinaries.org/license">Terms of Use</a></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
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:58ef978b31b31df9aaf715a0e9b006fde414a17a3ff15a3bf680eaad7418867a
|
||||
size 364
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:98a681ec3d89ee57c5d1057fe984dcf8ad45721f47ae4df57fa358fbee85e616
|
||||
size 385
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:24a2b2c9242a841c20e7815dab0d80a575844055328aea413d28b7283b65a92e
|
||||
size 386
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ce23a276fec849b8f832fab96d3b738793335c27d37ae3813158387f3415b508
|
||||
size 377
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c03befab41765200f4f28dbf1e0b2a702d2244bfa79b0d463f5d58d0a26095fc
|
||||
size 386
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:418c3f0f27854b3795841359014d87686a7bf94daf2568d9cfd3ffac22675f69
|
||||
size 386
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d3a831f34ac53c9b1f20037290e8a2b62a3cfb8a4f86467591f44fd2a0e3c15b
|
||||
size 379
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4267102ca7a889c34eff905480a68878d4d56e15bc723a5b0575cd472e259f5d
|
||||
size 389
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a0df013dd102b87348fba18b4da5443591309e9c40166d27ae928636924154ea
|
||||
size 388
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e713076ab5abbbb2cf28da431a339e9905acc790e35295f025aa2e79e1c04141
|
||||
size 376
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9e5af9d62ceafc3b8a1dfc36772350cd623fcc86c68711b299e143ff133f79b6
|
||||
size 387
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:17c5fb3d7b87ea87a98934954c721573c641bc44005a34f1e16589d7f39b71e8
|
||||
size 409
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6f5c78d9f764b62fb7dcf400c91c1edea9d7f88a426ba513fbf70825c6bcd2ac
|
||||
size 383
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:376b549602afffca407525b77c1a9821bf6a0e279792ae2e52fe0a4f7c3c5bd4
|
||||
size 364
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e7dc48f8d324b7563b168f27ebde1e00ee2bd11ba462f114a05b297913e285c5
|
||||
size 374
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:66b73afbd6dba1caaedfaae161b277b460b5198f7fc00bec414530116c567276
|
||||
size 375
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ae6e6714acf495246f4e59f6e5640f3a4417ea50100d37a116950d2b859aed0c
|
||||
size 417
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
@@ -538,6 +544,7 @@ void LevelEditorMenuHandler::PopulateEditMenu(ActionManager::MenuWrapper& editMe
|
||||
auto snapMenu = modifyMenu.AddMenu(tr("Snap"));
|
||||
|
||||
snapMenu.AddAction(AzToolsFramework::SnapAngle);
|
||||
snapMenu.AddAction(AzToolsFramework::SnapToGrid);
|
||||
|
||||
auto transformModeMenu = modifyMenu.AddMenu(tr("Transform Mode"));
|
||||
transformModeMenu.AddAction(AzToolsFramework::EditModeMove);
|
||||
@@ -723,7 +730,8 @@ QMenu* LevelEditorMenuHandler::CreateViewMenu()
|
||||
// MISSING AVIRECORDER
|
||||
|
||||
viewportViewsMenuWrapper.AddSeparator();
|
||||
viewportViewsMenuWrapper.AddAction(ID_DISPLAY_SHOWHELPERS);
|
||||
viewportViewsMenuWrapper.AddAction(AzToolsFramework::Helpers);
|
||||
viewportViewsMenuWrapper.AddAction(AzToolsFramework::Icons);
|
||||
|
||||
// Refresh Style
|
||||
viewMenu.AddAction(ID_SKINS_REFRESH);
|
||||
|
||||
@@ -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
|
||||
@@ -445,7 +445,6 @@ void CCryEditApp::RegisterActionHandlers()
|
||||
ON_COMMAND(ID_OPEN_ASSET_BROWSER, OnOpenAssetBrowserView)
|
||||
ON_COMMAND(ID_OPEN_AUDIO_CONTROLS_BROWSER, OnOpenAudioControlsEditor)
|
||||
|
||||
ON_COMMAND(ID_DISPLAY_SHOWHELPERS, OnShowHelpers)
|
||||
ON_COMMAND(ID_OPEN_TRACKVIEW, OnOpenTrackView)
|
||||
ON_COMMAND(ID_OPEN_UICANVASEDITOR, OnOpenUICanvasEditor)
|
||||
|
||||
@@ -2617,12 +2616,6 @@ void CCryEditApp::OnUpdateSelected(QAction* action)
|
||||
action->setEnabled(!GetIEditor()->GetSelection()->IsEmpty());
|
||||
}
|
||||
|
||||
void CCryEditApp::OnShowHelpers()
|
||||
{
|
||||
GetIEditor()->GetDisplaySettings()->DisplayHelpers(!GetIEditor()->GetDisplaySettings()->IsDisplayHelpers());
|
||||
GetIEditor()->Notify(eNotify_OnDisplayRenderUpdate);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CCryEditApp::OnEditLevelData()
|
||||
{
|
||||
@@ -2636,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;
|
||||
@@ -2766,6 +2760,7 @@ void CCryEditApp::OnFileResaveSlices()
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CCryEditApp::OnFileEditEditorini()
|
||||
|
||||
@@ -237,7 +237,6 @@ public:
|
||||
void OnSyncPlayerUpdate(QAction* action);
|
||||
void OnResourcesReduceworkingset();
|
||||
void OnDummyCommand() {};
|
||||
void OnShowHelpers();
|
||||
void OnFileSave();
|
||||
void OnUpdateDocumentReady(QAction* action);
|
||||
void OnUpdateFileOpen(QAction* action);
|
||||
|
||||
@@ -68,8 +68,6 @@ void CDisplaySettings::SetObjectHideMask(int hideMask)
|
||||
m_objectHideMask = hideMask;
|
||||
|
||||
gSettings.objectHideMask = m_objectHideMask;
|
||||
|
||||
GetIEditor()->Notify(eNotify_OnDisplayRenderUpdate);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -112,6 +112,31 @@ void EditorPreferencesDialog::showEvent(QShowEvent* event)
|
||||
QDialog::showEvent(event);
|
||||
}
|
||||
|
||||
void WidgetHandleKeyPressEvent(QWidget* widget, QKeyEvent* event)
|
||||
{
|
||||
// If the enter key is pressed during any text input, the dialog box will close
|
||||
// making it inconvenient to do multiple edits. This routine captures the
|
||||
// Key_Enter or Key_Return and clears the focus to give a visible cue that
|
||||
// editing of that field has finished and then doesn't propogate it.
|
||||
if (event->key() != Qt::Key::Key_Enter && event->key() != Qt::Key::Key_Return)
|
||||
{
|
||||
QApplication::sendEvent(widget, event);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (QWidget* editWidget = QApplication::focusWidget())
|
||||
{
|
||||
editWidget->clearFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EditorPreferencesDialog::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
WidgetHandleKeyPressEvent(this, event);
|
||||
}
|
||||
|
||||
void EditorPreferencesDialog::OnTreeCurrentItemChanged()
|
||||
{
|
||||
QTreeWidgetItem* currentItem = ui->pageTree->currentItem();
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace Ui
|
||||
|
||||
class EditorPreferencesTreeWidgetItem;
|
||||
|
||||
void WidgetHandleKeyPressEvent(QWidget* widget, QKeyEvent* event);
|
||||
|
||||
class EditorPreferencesDialog
|
||||
: public QDialog
|
||||
, public AzToolsFramework::IPropertyEditorNotify
|
||||
@@ -36,6 +38,7 @@ public:
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
|
||||
private:
|
||||
void CreateImages();
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <AzToolsFramework/API/EditorCameraBus.h>
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportSettings.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
|
||||
|
||||
@@ -665,13 +666,7 @@ void EditorViewportWidget::OnBeginPrepareRender()
|
||||
RenderAll();
|
||||
|
||||
// Draw 2D helpers.
|
||||
#ifdef LYSHINE_ATOM_TODO
|
||||
TransformationMatrices backupSceneMatrices;
|
||||
#endif
|
||||
m_debugDisplay->DepthTestOff();
|
||||
#ifdef LYSHINE_ATOM_TODO
|
||||
m_renderer->Set2DMode(m_rcClient.right(), m_rcClient.bottom(), backupSceneMatrices);
|
||||
#endif
|
||||
auto prevState = m_debugDisplay->GetState();
|
||||
m_debugDisplay->SetState(e_Mode3D | e_AlphaBlended | e_FillModeSolid | e_CullModeBack | e_DepthWriteOn | e_DepthTestOn);
|
||||
|
||||
@@ -1627,14 +1622,14 @@ Vec3 EditorViewportWidget::ViewToWorld(
|
||||
auto ray = m_renderViewport->ViewportScreenToWorldRay(AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(vp));
|
||||
|
||||
const float maxDistance = 10000.f;
|
||||
Vec3 v = AZVec3ToLYVec3(ray.direction) * maxDistance;
|
||||
Vec3 v = AZVec3ToLYVec3(ray.m_direction) * maxDistance;
|
||||
|
||||
if (!_finite(v.x) || !_finite(v.y) || !_finite(v.z))
|
||||
{
|
||||
return Vec3(0, 0, 0);
|
||||
}
|
||||
|
||||
Vec3 colp = AZVec3ToLYVec3(ray.origin) + 0.002f * v;
|
||||
Vec3 colp = AZVec3ToLYVec3(ray.m_origin) + 0.002f * v;
|
||||
|
||||
return colp;
|
||||
}
|
||||
@@ -2426,6 +2421,16 @@ AZ::Vector3 EditorViewportSettings::DefaultEditorCameraPosition() const
|
||||
return SandboxEditor::CameraDefaultEditorPosition();
|
||||
}
|
||||
|
||||
bool EditorViewportSettings::IconsVisible() const
|
||||
{
|
||||
return AzToolsFramework::IconsVisible();
|
||||
}
|
||||
|
||||
bool EditorViewportSettings::HelpersVisible() const
|
||||
{
|
||||
return AzToolsFramework::HelpersVisible();
|
||||
}
|
||||
|
||||
AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once);
|
||||
|
||||
bool EditorViewportWidget::ShouldPreviewFullscreen() const
|
||||
|
||||
@@ -79,6 +79,8 @@ struct EditorViewportSettings : public AzToolsFramework::ViewportInteraction::Vi
|
||||
float ManipulatorCircleBoundWidth() const override;
|
||||
bool StickySelectEnabled() const override;
|
||||
AZ::Vector3 DefaultEditorCameraPosition() const override;
|
||||
bool IconsVisible() const override;
|
||||
bool HelpersVisible() const override;
|
||||
};
|
||||
|
||||
// EditorViewportWidget window
|
||||
|
||||
@@ -442,7 +442,7 @@ AZ::Outcome<void, AZStd::string> CGameEngine::Init(
|
||||
REGISTER_COMMAND("quit", CGameEngine::HandleQuitRequest, VF_RESTRICTEDMODE, "Quit/Shutdown the engine");
|
||||
|
||||
EBUS_EVENT(CrySystemEventBus, OnCryEditorInitialized);
|
||||
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
@@ -465,7 +465,7 @@ void CGameEngine::SetLevelPath(const QString& path)
|
||||
const char* oldExtension = EditorUtils::LevelFile::GetOldCryFileExtension();
|
||||
const char* defaultExtension = EditorUtils::LevelFile::GetDefaultFileExtension();
|
||||
|
||||
// Store off if
|
||||
// Store off if
|
||||
if (QFileInfo(path + oldExtension).exists())
|
||||
{
|
||||
m_levelExtension = oldExtension;
|
||||
|
||||
@@ -69,7 +69,6 @@ class CSelectionTreeManager;
|
||||
struct SEditorSettings;
|
||||
class CGameExporter;
|
||||
class IAWSResourceManager;
|
||||
struct IEditorPanelUtils;
|
||||
|
||||
namespace WinWidget
|
||||
{
|
||||
@@ -168,8 +167,6 @@ enum EEditorNotifyEvent
|
||||
eNotify_OnVegetationObjectSelection, // When vegetation objects selection change.
|
||||
eNotify_OnVegetationPanelUpdate, // When vegetation objects selection change.
|
||||
|
||||
eNotify_OnDisplayRenderUpdate, // Sent when editor finish terrain texture generation.
|
||||
|
||||
eNotify_OnDataBaseUpdate, // DataBase Library was modified.
|
||||
|
||||
eNotify_OnLayerImportBegin, //layer import was started
|
||||
@@ -528,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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Debug/TraceMessageBus.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
|
||||
#include <AzToolsFramework/Application/ToolsApplication.h>
|
||||
@@ -63,6 +64,11 @@ namespace DisplaySettingsPythonBindingsUnitTests
|
||||
|
||||
m_app.Start(appDesc);
|
||||
m_app.RegisterComponentDescriptor(AzToolsFramework::DisplaySettingsComponent::CreateDescriptor());
|
||||
|
||||
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
|
||||
// in the unit tests.
|
||||
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
|
||||
+68
-20
@@ -47,6 +47,7 @@ AZ_POP_DISABLE_WARNING
|
||||
#include <AzToolsFramework/API/EditorAnimationSystemRequestBus.h>
|
||||
#include <AzToolsFramework/SourceControl/QtSourceControlNotificationHandler.h>
|
||||
#include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportSettings.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
|
||||
|
||||
// AzQtComponents
|
||||
@@ -445,11 +446,11 @@ void MainWindow::Initialize()
|
||||
{
|
||||
m_viewPaneManager->SetMainWindow(m_viewPaneHost, &m_settings, /*unused*/ QByteArray());
|
||||
|
||||
InitActions();
|
||||
|
||||
RegisterStdViewClasses();
|
||||
InitCentralWidget();
|
||||
|
||||
InitActions();
|
||||
|
||||
// load toolbars ("shelves") and macros
|
||||
GetIEditor()->GetToolBoxManager()->Load(m_actionManager);
|
||||
|
||||
@@ -642,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()
|
||||
@@ -676,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..."));
|
||||
@@ -798,27 +801,40 @@ void MainWindow::InitActions()
|
||||
EditorTransformComponentSelectionRequests::Mode::Scale);
|
||||
});
|
||||
|
||||
am->AddAction(AzToolsFramework::SnapToGrid, tr("Snap to grid"))
|
||||
am->AddAction(AzToolsFramework::SnapToGrid, tr("Grid snapping"))
|
||||
.SetIcon(Style::icon("Grid"))
|
||||
.SetStatusTip(tr("Toggle grid snapping"))
|
||||
.SetShortcut(tr("G"))
|
||||
.SetToolTip(tr("Snap to grid (G)"))
|
||||
.SetStatusTip(tr("Toggles snap to grid"))
|
||||
.SetCheckable(true)
|
||||
.RegisterUpdateCallback([](QAction* action) {
|
||||
Q_ASSERT(action->isCheckable());
|
||||
action->setChecked(SandboxEditor::GridSnappingEnabled());
|
||||
})
|
||||
.Connect(&QAction::triggered, []() { SandboxEditor::SetGridSnapping(!SandboxEditor::GridSnappingEnabled()); });
|
||||
.RegisterUpdateCallback(
|
||||
[](QAction* action)
|
||||
{
|
||||
Q_ASSERT(action->isCheckable());
|
||||
action->setChecked(SandboxEditor::GridSnappingEnabled());
|
||||
})
|
||||
.Connect(
|
||||
&QAction::triggered,
|
||||
[]
|
||||
{
|
||||
SandboxEditor::SetGridSnapping(!SandboxEditor::GridSnappingEnabled());
|
||||
});
|
||||
|
||||
am->AddAction(AzToolsFramework::SnapAngle, tr("Snap angle"))
|
||||
am->AddAction(AzToolsFramework::SnapAngle, tr("Angle snapping"))
|
||||
.SetIcon(Style::icon("Angle"))
|
||||
.SetStatusTip(tr("Snap angle"))
|
||||
.SetStatusTip(tr("Toggle angle snapping"))
|
||||
.SetCheckable(true)
|
||||
.RegisterUpdateCallback([](QAction* action) {
|
||||
Q_ASSERT(action->isCheckable());
|
||||
action->setChecked(SandboxEditor::AngleSnappingEnabled());
|
||||
})
|
||||
.Connect(&QAction::triggered, []() { SandboxEditor::SetAngleSnapping(!SandboxEditor::AngleSnappingEnabled()); });
|
||||
.RegisterUpdateCallback(
|
||||
[](QAction* action)
|
||||
{
|
||||
Q_ASSERT(action->isCheckable());
|
||||
action->setChecked(SandboxEditor::AngleSnappingEnabled());
|
||||
})
|
||||
.Connect(
|
||||
&QAction::triggered,
|
||||
[]
|
||||
{
|
||||
SandboxEditor::SetAngleSnapping(!SandboxEditor::AngleSnappingEnabled());
|
||||
});
|
||||
|
||||
// Display actions
|
||||
am->AddAction(ID_SWITCHCAMERA_DEFAULTCAMERA, tr("Default Camera")).SetCheckable(true)
|
||||
@@ -918,9 +934,41 @@ void MainWindow::InitActions()
|
||||
.SetStatusTip(tr("Cycle 2D Viewport"))
|
||||
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateNonGameMode);
|
||||
#endif
|
||||
am->AddAction(ID_DISPLAY_SHOWHELPERS, tr("Show/Hide Helpers"))
|
||||
am->AddAction(AzToolsFramework::Helpers, tr("Show Helpers"))
|
||||
.SetShortcut(tr("Shift+Space"))
|
||||
.SetToolTip(tr("Show/Hide Helpers (Shift+Space)"));
|
||||
.SetToolTip(tr("Show/Hide Helpers (Shift+Space)"))
|
||||
.SetCheckable(true)
|
||||
.RegisterUpdateCallback(
|
||||
[](QAction* action)
|
||||
{
|
||||
Q_ASSERT(action->isCheckable());
|
||||
action->setChecked(AzToolsFramework::HelpersVisible());
|
||||
})
|
||||
.Connect(
|
||||
&QAction::triggered,
|
||||
[]()
|
||||
{
|
||||
AzToolsFramework::SetHelpersVisible(!AzToolsFramework::HelpersVisible());
|
||||
AzToolsFramework::ViewportInteraction::ViewportSettingsNotificationBus::Broadcast(
|
||||
&AzToolsFramework::ViewportInteraction::ViewportSettingNotifications::OnDrawHelpersChanged,
|
||||
AzToolsFramework::HelpersVisible());
|
||||
});
|
||||
am->AddAction(AzToolsFramework::Icons, tr("Show Icons"))
|
||||
.SetShortcut(tr("Ctrl+Space"))
|
||||
.SetToolTip(tr("Show/Hide Icons (Ctrl+Space)"))
|
||||
.SetCheckable(true)
|
||||
.RegisterUpdateCallback(
|
||||
[](QAction* action)
|
||||
{
|
||||
Q_ASSERT(action->isCheckable());
|
||||
action->setChecked(AzToolsFramework::IconsVisible());
|
||||
})
|
||||
.Connect(
|
||||
&QAction::triggered,
|
||||
[]()
|
||||
{
|
||||
AzToolsFramework::SetIconsVisible(!AzToolsFramework::IconsVisible());
|
||||
});
|
||||
|
||||
// Audio actions
|
||||
am->AddAction(ID_SOUND_STOPALLSOUNDS, tr("Stop All Sounds"))
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -1986,8 +1986,3 @@ void SandboxIntegrationManager::BrowseForAssets(AssetSelectionModel& selection)
|
||||
{
|
||||
AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, GetMainWindow());
|
||||
}
|
||||
|
||||
bool SandboxIntegrationManager::DisplayHelpersVisible()
|
||||
{
|
||||
return GetIEditor()->GetDisplaySettings()->IsDisplayHelpers();
|
||||
}
|
||||
|
||||
@@ -165,7 +165,6 @@ private:
|
||||
void InstantiateSliceFromAssetId(const AZ::Data::AssetId& assetId) override;
|
||||
void ClearRedoStack() override;
|
||||
int GetIconTextureIdFromEntityIconPath(const AZStd::string& entityIconPath) override;
|
||||
bool DisplayHelpersVisible() override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1281,6 +1281,8 @@ void OutlinerWidget::OnSearchTextChanged(const QString& activeTextFilter)
|
||||
|
||||
m_listModel->SearchStringChanged(filterString);
|
||||
m_proxyModel->UpdateFilter();
|
||||
|
||||
m_gui->m_objectTree->expandAll();
|
||||
}
|
||||
|
||||
void OutlinerWidget::OnFilterChanged(const AzQtComponents::SearchTypeFilterList& activeTypeFilters)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -191,7 +191,6 @@
|
||||
#define ID_BRUSH_CSGSUBSTRUCT 33837
|
||||
#define ID_MATERIAL_PICKTOOL 33842
|
||||
#define ID_MODIFY_AIPOINT_PICKIMPASSLINK 33865
|
||||
#define ID_DISPLAY_SHOWHELPERS 33871
|
||||
#define ID_FILE_EXPORTSELECTION 33875
|
||||
#define ID_EDIT_PASTE_WITH_LINKS 33893
|
||||
#define ID_FILE_EXPORT_TERRAINAREA 33904
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -129,8 +129,8 @@ namespace SandboxEditor
|
||||
ViewportInteractionRequestBus::EventResult(
|
||||
ray, GetViewportId(), &ViewportInteractionRequestBus::Events::ViewportScreenToWorldRay, screenPoint);
|
||||
|
||||
m_mouseInteraction.m_mousePick.m_rayOrigin = ray.origin;
|
||||
m_mouseInteraction.m_mousePick.m_rayDirection = ray.direction;
|
||||
m_mouseInteraction.m_mousePick.m_rayOrigin = ray.m_origin;
|
||||
m_mouseInteraction.m_mousePick.m_rayDirection = ray.m_direction;
|
||||
m_mouseInteraction.m_mousePick.m_screenCoordinates = screenPoint;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// Description : CViewportTitleDlg implementation file
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
@@ -42,38 +41,18 @@
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportSettings.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
|
||||
|
||||
#include <LmbrCentral/Audio/AudioSystemComponentBus.h>
|
||||
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
#include "ui_ViewportTitleDlg.h"
|
||||
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
#endif //!defined(Q_MOC_RUN)
|
||||
#endif //! defined(Q_MOC_RUN)
|
||||
|
||||
// CViewportTitleDlg dialog
|
||||
|
||||
inline namespace Helpers
|
||||
{
|
||||
void ToggleHelpers()
|
||||
{
|
||||
const bool newValue = !GetIEditor()->GetDisplaySettings()->IsDisplayHelpers();
|
||||
GetIEditor()->GetDisplaySettings()->DisplayHelpers(newValue);
|
||||
GetIEditor()->Notify(eNotify_OnDisplayRenderUpdate);
|
||||
|
||||
if (newValue == false)
|
||||
{
|
||||
GetIEditor()->GetObjectManager()->SendEvent(EVENT_HIDE_HELPER);
|
||||
}
|
||||
AzToolsFramework::ViewportInteraction::ViewportSettingsNotificationBus::Broadcast(
|
||||
&AzToolsFramework::ViewportInteraction::ViewportSettingNotifications::OnDrawHelpersChanged, newValue);
|
||||
}
|
||||
|
||||
bool IsHelpersShown()
|
||||
{
|
||||
return GetIEditor()->GetDisplaySettings()->IsDisplayHelpers();
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class CViewportTitleDlgDisplayInfoHelper
|
||||
@@ -98,7 +77,7 @@ namespace
|
||||
emit ViewportInfoStatusUpdated(static_cast<int>(state));
|
||||
}
|
||||
};
|
||||
} //end anonymous namespace
|
||||
} // end anonymous namespace
|
||||
|
||||
CViewportTitleDlg::CViewportTitleDlg(QWidget* pParent)
|
||||
: QWidget(pParent)
|
||||
@@ -215,13 +194,65 @@ void CViewportTitleDlg::SetupViewportInformationMenu()
|
||||
m_ui->m_debugInformationMenu->setMenu(GetViewportInformationMenu());
|
||||
connect(m_ui->m_debugInformationMenu, &QToolButton::clicked, this, &CViewportTitleDlg::OnToggleDisplayInfo);
|
||||
m_ui->m_debugInformationMenu->setPopupMode(QToolButton::MenuButtonPopup);
|
||||
|
||||
}
|
||||
|
||||
void CViewportTitleDlg::SetupHelpersButton()
|
||||
{
|
||||
connect(m_ui->m_helpers, &QToolButton::clicked, this, &CViewportTitleDlg::OnToggleHelpers);
|
||||
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
|
||||
if (m_helpersMenu == nullptr)
|
||||
{
|
||||
m_helpersMenu = new QMenu("Helpers State", this);
|
||||
|
||||
auto helperAction = MainWindow::instance()->GetActionManager()->GetAction(AzToolsFramework::Helpers);
|
||||
connect(
|
||||
helperAction, &QAction::triggered, this,
|
||||
[this]
|
||||
{
|
||||
m_ui->m_helpers->setChecked(AzToolsFramework::HelpersVisible() || AzToolsFramework::IconsVisible());
|
||||
});
|
||||
|
||||
auto iconAction = MainWindow::instance()->GetActionManager()->GetAction(AzToolsFramework::Icons);
|
||||
connect(
|
||||
iconAction, &QAction::triggered, this,
|
||||
[this]
|
||||
{
|
||||
m_ui->m_helpers->setChecked(AzToolsFramework::HelpersVisible() || AzToolsFramework::IconsVisible());
|
||||
});
|
||||
|
||||
m_helpersAction = new QAction(tr("Helpers"), m_helpersMenu);
|
||||
m_helpersAction->setCheckable(true);
|
||||
connect(
|
||||
m_helpersAction, &QAction::triggered, this,
|
||||
[helperAction]
|
||||
{
|
||||
helperAction->trigger();
|
||||
});
|
||||
|
||||
m_iconsAction = new QAction(tr("Icons"), m_helpersMenu);
|
||||
m_iconsAction->setCheckable(true);
|
||||
connect(
|
||||
m_iconsAction, &QAction::triggered, this,
|
||||
[iconAction]
|
||||
{
|
||||
iconAction->trigger();
|
||||
});
|
||||
|
||||
m_helpersMenu->addAction(m_helpersAction);
|
||||
m_helpersMenu->addAction(m_iconsAction);
|
||||
|
||||
connect(
|
||||
m_helpersMenu, &QMenu::aboutToShow, this,
|
||||
[this]
|
||||
{
|
||||
m_helpersAction->setChecked(AzToolsFramework::HelpersVisible());
|
||||
m_iconsAction->setChecked(AzToolsFramework::IconsVisible());
|
||||
});
|
||||
|
||||
m_ui->m_helpers->setCheckable(true);
|
||||
m_ui->m_helpers->setMenu(m_helpersMenu);
|
||||
m_ui->m_helpers->setPopupMode(QToolButton::InstantPopup);
|
||||
}
|
||||
|
||||
m_ui->m_helpers->setChecked(AzToolsFramework::HelpersVisible() || AzToolsFramework::IconsVisible());
|
||||
}
|
||||
|
||||
void CViewportTitleDlg::SetupOverflowMenu()
|
||||
@@ -279,15 +310,20 @@ void CViewportTitleDlg::SetupOverflowMenu()
|
||||
UpdateMuteActionText();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CViewportTitleDlg::SetViewPane(CLayoutViewPane* pViewPane)
|
||||
{
|
||||
if (m_pViewPane)
|
||||
{
|
||||
m_pViewPane->disconnect(this);
|
||||
}
|
||||
|
||||
m_pViewPane = pViewPane;
|
||||
|
||||
if (m_pViewPane)
|
||||
{
|
||||
connect(this, &QWidget::customContextMenuRequested, m_pViewPane, &CLayoutViewPane::ShowTitleMenu);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -318,7 +354,6 @@ void CViewportTitleDlg::OnInitDialog()
|
||||
m_ui->m_prefabFocusPath->hide();
|
||||
m_ui->m_prefabFocusBackButton->hide();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -336,13 +371,6 @@ void CViewportTitleDlg::OnMaximize()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CViewportTitleDlg::OnToggleHelpers()
|
||||
{
|
||||
Helpers::ToggleHelpers();
|
||||
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
|
||||
}
|
||||
|
||||
void CViewportTitleDlg::SetNoViewportInfo()
|
||||
{
|
||||
AZ::AtomBridge::AtomViewportInfoDisplayRequestBus::Broadcast(
|
||||
@@ -367,7 +395,6 @@ void CViewportTitleDlg::SetCompactViewportInfo()
|
||||
&AZ::AtomBridge::AtomViewportInfoDisplayRequestBus::Events::SetDisplayState, AZ::AtomBridge::ViewportInfoDisplayState::CompactInfo);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CViewportTitleDlg::UpdateDisplayInfo()
|
||||
{
|
||||
@@ -783,9 +810,6 @@ void CViewportTitleDlg::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case eNotify_OnDisplayRenderUpdate:
|
||||
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
|
||||
break;
|
||||
case eNotify_OnBeginGameMode:
|
||||
case eNotify_OnEndGameMode:
|
||||
UpdateMuteActionText();
|
||||
@@ -997,24 +1021,18 @@ void CViewportTitleDlg::UpdateOverFlowMenuState()
|
||||
m_angleSizeActionWidget->setEnabled(angleSnappingActive);
|
||||
}
|
||||
|
||||
namespace
|
||||
namespace
|
||||
{
|
||||
void PyToggleHelpers()
|
||||
{
|
||||
GetIEditor()->GetDisplaySettings()->DisplayHelpers(!GetIEditor()->GetDisplaySettings()->IsDisplayHelpers());
|
||||
GetIEditor()->Notify(eNotify_OnDisplayRenderUpdate);
|
||||
|
||||
if (GetIEditor()->GetDisplaySettings()->IsDisplayHelpers() == false)
|
||||
{
|
||||
GetIEditor()->GetObjectManager()->SendEvent(EVENT_HIDE_HELPER);
|
||||
}
|
||||
AzToolsFramework::SetHelpersVisible(!AzToolsFramework::HelpersVisible());
|
||||
}
|
||||
|
||||
bool PyIsHelpersShown()
|
||||
{
|
||||
return GetIEditor()->GetDisplaySettings()->IsDisplayHelpers();
|
||||
return AzToolsFramework::HelpersVisible();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -1029,11 +1047,12 @@ namespace AzToolsFramework
|
||||
->Attribute(AZ::Script::Attributes::Category, "Legacy/Editor")
|
||||
->Attribute(AZ::Script::Attributes::Module, "legacy.general");
|
||||
};
|
||||
|
||||
addLegacyGeneral(behaviorContext->Method("toggle_helpers", PyToggleHelpers, nullptr, "Toggles the display of helpers."));
|
||||
addLegacyGeneral(behaviorContext->Method("is_helpers_shown", PyIsHelpersShown, nullptr, "Gets the display state of helpers."));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
#include "ViewportTitleDlg.moc"
|
||||
#include <moc_ViewportTitleDlg.cpp>
|
||||
|
||||
@@ -80,7 +80,6 @@ protected:
|
||||
void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam) override;
|
||||
|
||||
void OnMaximize();
|
||||
void OnToggleHelpers();
|
||||
void UpdateDisplayInfo();
|
||||
|
||||
void SetupCameraDropdownMenu();
|
||||
@@ -153,6 +152,9 @@ protected:
|
||||
QMenu* m_aspectMenu = nullptr;
|
||||
QMenu* m_resolutionMenu = nullptr;
|
||||
QMenu* m_viewportInformationMenu = nullptr;
|
||||
QMenu* m_helpersMenu = nullptr;
|
||||
QAction* m_helpersAction = nullptr;
|
||||
QAction* m_iconsAction = nullptr;
|
||||
QAction* m_noInformationAction = nullptr;
|
||||
QAction* m_normalInformationAction = nullptr;
|
||||
QAction* m_fullInformationAction = nullptr;
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -168,7 +168,7 @@ namespace AZ
|
||||
virtual bool IsRegisterReadonlyAndShareable() { return true; }
|
||||
|
||||
/**
|
||||
* Override this function to control automatic reload behavior.
|
||||
* Override this function to control automatic reload behavior.
|
||||
* By default, the asset will reload automatically.
|
||||
* Return false to disable automatic reload. Potential use cases include:
|
||||
* 1, If an asset is dependent on a parent asset(i.e.both assets need to be reloaded as a group) the parent asset can explicitly reload the child.
|
||||
@@ -200,10 +200,10 @@ namespace AZ
|
||||
|
||||
AssetHandler* m_registeredHandler{ nullptr };
|
||||
|
||||
// This is used to identify a unique asset and should only be set by the asset manager
|
||||
// This is used to identify a unique asset and should only be set by the asset manager
|
||||
// and therefore does not need to be atomic.
|
||||
// All shared copy of an asset should have the same identifier and therefore
|
||||
// should not be modified while making copy of an existing asset.
|
||||
// should not be modified while making copy of an existing asset.
|
||||
int m_creationToken = s_defaultCreationToken;
|
||||
// General purpose flags that should only be accessed within the asset mutex
|
||||
AZStd::bitset<32> m_flags;
|
||||
@@ -430,7 +430,7 @@ namespace AZ
|
||||
*/
|
||||
void UpgradeAssetInfo();
|
||||
|
||||
/**
|
||||
/**
|
||||
* for debugging purposes - creates a string that represents the assets id, subid, hint, and name.
|
||||
* You should use this function for any time you want to show the full details of an asset in a log message
|
||||
* as it will always produce a consistent output string. By convention, don't surround the output of this call
|
||||
@@ -586,26 +586,26 @@ namespace AZ
|
||||
|
||||
/// Called when an asset is loaded, patched and ready to be used.
|
||||
virtual void OnAssetReady(Asset<AssetData> asset) { (void)asset; }
|
||||
|
||||
|
||||
/// Called when an asset has been moved (usually due to de-fragmentation/compaction), if possible the only data pointer is provided otherwise NULL.
|
||||
virtual void OnAssetMoved(Asset<AssetData> asset, void* oldDataPointer) { (void)asset; (void)oldDataPointer; }
|
||||
|
||||
|
||||
/// Called before an asset reload has started.
|
||||
virtual void OnAssetPreReload(Asset<AssetData> asset) { (void)asset; }
|
||||
|
||||
|
||||
/// Called when an asset has been reloaded (usually in tool mode and loose more). It should not be called in final build.
|
||||
virtual void OnAssetReloaded(Asset<AssetData> asset) { (void)asset; }
|
||||
|
||||
|
||||
/// Called when an asset failed to reload.
|
||||
virtual void OnAssetReloadError(Asset<AssetData> asset) { (void)asset; }
|
||||
|
||||
|
||||
/// Called when an asset has been saved. In general most assets can't be saved (in a game) so make sure you check the flag.
|
||||
virtual void OnAssetSaved(Asset<AssetData> asset, bool isSuccessful) { (void)asset; (void)isSuccessful; }
|
||||
|
||||
|
||||
/// Called when an asset is unloaded.
|
||||
virtual void OnAssetUnloaded(const AssetId assetId, const AssetType assetType) { (void)assetId; (void)assetType; }
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Called when an error happened with an asset. When this message is received the asset should be considered broken by default.
|
||||
* Note that this can happen when the asset errors during load, but also happens when the asset is missing (not in catalog etc.)
|
||||
* in the case of an asset that is completely missing, the Asset<T> passed in here will have no hint or other information about
|
||||
@@ -1094,7 +1094,7 @@ namespace AZ
|
||||
// if we are a different asset (or being swapped with a empty) then we just swap as usual.
|
||||
AZStd::swap(m_assetHint, rhs.m_assetHint);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
@@ -1218,7 +1218,7 @@ namespace AZ
|
||||
/// Indiscriminately skips all asset references.
|
||||
bool AssetFilterNoAssetLoading(const AssetFilterInfo& filterInfo);
|
||||
|
||||
// Shared ProductDependency concepts between AP and LY
|
||||
// Shared ProductDependency concepts between AP and LY
|
||||
namespace ProductDependencyInfo
|
||||
{
|
||||
//! Corresponds to all ProductDependencyFlags, not just LoadBehaviors
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace AZ::Data
|
||||
AZ_PROFILE_SCOPE(AzCore, "AZ::Data::LoadAssetDataStreamCallback %s",
|
||||
m_filePath.c_str());
|
||||
|
||||
// Get the results
|
||||
// Get the results
|
||||
auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
|
||||
AZ::u64 bytesRead = 0;
|
||||
streamer->GetReadRequestResult(fileHandle, m_buffer, bytesRead,
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace AZ::Data
|
||||
//! The path and file name of the asset being loaded
|
||||
AZStd::string m_filePath;
|
||||
|
||||
//! The offset into the file to start loading at.
|
||||
//! The offset into the file to start loading at.
|
||||
size_t m_fileOffset{ 0 };
|
||||
|
||||
//! The amount of data that's expected to be loaded.
|
||||
|
||||
@@ -811,12 +811,12 @@ namespace AZ
|
||||
if (behaviorContext)
|
||||
{
|
||||
behaviorContext->Class<EntityId>()
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Module, "entity")
|
||||
->Method("IsValid", &EntityId::IsValid)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
->Method("ToString", &EntityId::ToString)
|
||||
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
|
||||
@@ -243,7 +243,7 @@ namespace AZ
|
||||
|
||||
if (StringFunc::StartsWith(curr->m_name, command, false))
|
||||
{
|
||||
AZLOG_INFO("- %s : %s\n", curr->m_name, curr->m_desc);
|
||||
AZLOG_INFO("- %s : %s", curr->m_name, curr->m_desc);
|
||||
|
||||
if (commandSubset.size() < MaxConsoleCommandPlusArgsLength)
|
||||
{
|
||||
@@ -433,29 +433,29 @@ namespace AZ
|
||||
{
|
||||
if ((curr->GetFlags() & requiredSet) != requiredSet)
|
||||
{
|
||||
AZLOG_WARN("%s failed required set flag check\n", curr->m_name);
|
||||
AZLOG_WARN("%s failed required set flag check", curr->m_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((curr->GetFlags() & requiredClear) != ConsoleFunctorFlags::Null)
|
||||
{
|
||||
AZLOG_WARN("%s failed required clear flag check\n", curr->m_name);
|
||||
AZLOG_WARN("%s failed required clear flag check", curr->m_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((curr->GetFlags() & ConsoleFunctorFlags::IsCheat) != ConsoleFunctorFlags::Null)
|
||||
{
|
||||
AZLOG_WARN("%s is marked as a cheat\n", curr->m_name);
|
||||
AZLOG_WARN("%s is marked as a cheat", curr->m_name);
|
||||
}
|
||||
|
||||
if ((curr->GetFlags() & ConsoleFunctorFlags::IsDeprecated) != ConsoleFunctorFlags::Null)
|
||||
{
|
||||
AZLOG_WARN("%s is marked as deprecated\n", curr->m_name);
|
||||
AZLOG_WARN("%s is marked as deprecated", curr->m_name);
|
||||
}
|
||||
|
||||
if ((curr->GetFlags() & ConsoleFunctorFlags::NeedsReload) != ConsoleFunctorFlags::Null)
|
||||
{
|
||||
AZLOG_WARN("Changes to %s will only take effect after level reload\n", curr->m_name);
|
||||
AZLOG_WARN("Changes to %s will only take effect after level reload", curr->m_name);
|
||||
}
|
||||
|
||||
// Letting this intentionally fall-through, since in editor we can register common variables multiple times
|
||||
@@ -468,7 +468,7 @@ namespace AZ
|
||||
{
|
||||
CVarFixedString value;
|
||||
curr->GetValue(value);
|
||||
AZLOG_INFO("> %s : %s\n", curr->GetName(), value.empty() ? "<empty>" : value.c_str());
|
||||
AZLOG_INFO("> %s : %s", curr->GetName(), value.empty() ? "<empty>" : value.c_str());
|
||||
}
|
||||
flags = curr->GetFlags();
|
||||
}
|
||||
|
||||
@@ -119,25 +119,21 @@ namespace AZ
|
||||
void LoggerSystemComponent::LogInternalV(LogLevel level, const char* format, const char* file, const char* function, int32_t line, va_list args)
|
||||
{
|
||||
constexpr AZStd::size_t MaxLogBufferSize = 1000;
|
||||
char buffer[MaxLogBufferSize];
|
||||
auto buffer = AZStd::fixed_string<MaxLogBufferSize>::format_arg(format, args);
|
||||
m_logEvent.Signal(level, buffer.c_str(), file, function, line);
|
||||
buffer += '\n';
|
||||
|
||||
const AZStd::size_t length = azvsnprintf(buffer, MaxLogBufferSize, format, args);
|
||||
buffer[AZStd::min<AZStd::size_t>(length + 1, MaxLogBufferSize - 1)] = '\0';
|
||||
m_logEvent.Signal(level, buffer, file, function, line);
|
||||
|
||||
// Force a new-line before calling the AZ::Debug::Trace functions, as they assume a newline is present
|
||||
buffer[AZStd::min<AZStd::size_t>(length + 1, MaxLogBufferSize - 2)] = '\n';
|
||||
switch (level)
|
||||
{
|
||||
case LogLevel::Warn:
|
||||
AZ_Warning("Logger", true, buffer);
|
||||
AZ_Warning("Logger", true, buffer.c_str());
|
||||
break;
|
||||
case LogLevel::Error:
|
||||
AZ_Error("Logger", true, buffer);
|
||||
AZ_Error("Logger", true, buffer.c_str());
|
||||
break;
|
||||
default:
|
||||
// Catch all else with trace
|
||||
AZ::Debug::Trace::Output("Logger", buffer);
|
||||
AZ::Debug::Trace::Output("Logger", buffer.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,144 +19,144 @@
|
||||
#include <AzCore/std/containers/deque.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
namespace AZ::IO
|
||||
{
|
||||
struct BlockCacheConfig final :
|
||||
public IStreamerStackConfig
|
||||
{
|
||||
AZ_RTTI(AZ::IO::BlockCacheConfig, "{70120525-88A4-40B6-A75B-BAA7E8FD77F3}", IStreamerStackConfig);
|
||||
AZ_CLASS_ALLOCATOR(BlockCacheConfig, AZ::SystemAllocator, 0);
|
||||
|
||||
~BlockCacheConfig() override = default;
|
||||
AZStd::shared_ptr<StreamStackEntry> AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent) override;
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
//! Dynamic options for the blocks size.
|
||||
//! It's possible to set static sizes or use the names from this enum to have AZ::IO::Streamer automatically fill in the sizes.
|
||||
//! Fixed sizes are set through the Settings Registry with "BlockSize": 524288, while dynamic values are set like
|
||||
//! "BlockSize": "MemoryAlignment". In the latter case AZ::IO::Streamer will use the available hardware information and fill
|
||||
//! in the actual value.
|
||||
enum BlockSize : u32
|
||||
{
|
||||
MaxTransfer = AZStd::numeric_limits<u32>::max(), //!< The largest possible block size.
|
||||
MemoryAlignment = MaxTransfer - 1, //!< The size of the minimal memory requirement of the storage device.
|
||||
SizeAlignment = MemoryAlignment - 1 //!< The minimal read size required by the storage device.
|
||||
};
|
||||
|
||||
//! The overall size of the cache in megabytes.
|
||||
u32 m_cacheSizeMib{ 8 };
|
||||
//! The size of the individual blocks inside the cache.
|
||||
BlockSize m_blockSize{ BlockSize::MemoryAlignment };
|
||||
};
|
||||
|
||||
class BlockCache
|
||||
: public StreamStackEntry
|
||||
{
|
||||
public:
|
||||
BlockCache(u64 cacheSize, u32 blockSize, u32 alignment, bool onlyEpilogWrites);
|
||||
BlockCache(BlockCache&& rhs) = delete;
|
||||
BlockCache(const BlockCache& rhs) = delete;
|
||||
~BlockCache() override;
|
||||
|
||||
BlockCache& operator=(BlockCache&& rhs) = delete;
|
||||
BlockCache& operator=(const BlockCache& rhs) = delete;
|
||||
|
||||
void QueueRequest(FileRequest* request) override;
|
||||
bool ExecuteRequests() override;
|
||||
|
||||
void UpdateStatus(Status& status) const override;
|
||||
void UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now, AZStd::vector<FileRequest*>& internalPending,
|
||||
StreamerContext::PreparedQueue::iterator pendingBegin, StreamerContext::PreparedQueue::iterator pendingEnd) override;
|
||||
void AddDelayedRequests(AZStd::vector<FileRequest*>& internalPending);
|
||||
void UpdatePendingRequestEstimations();
|
||||
|
||||
void FlushCache(const RequestPath& filePath);
|
||||
void FlushEntireCache();
|
||||
|
||||
void CollectStatistics(AZStd::vector<Statistic>& statistics) const override;
|
||||
|
||||
double CalculateHitRatePercentage() const;
|
||||
double CalculateCacheableRatePercentage() const;
|
||||
s32 CalculateAvailableRequestSlots() const;
|
||||
|
||||
protected:
|
||||
static constexpr u32 s_fileNotCached = static_cast<u32>(-1);
|
||||
|
||||
enum class CacheResult
|
||||
{
|
||||
ReadFromCache, //!< Data was found in the cache and reused.
|
||||
CacheMiss, //!< Data wasn't found in the cache and no sub request was queued.
|
||||
Queued, //!< A sub request was created or appended and queued for processing on the next entry in the streamer stack.
|
||||
Delayed //!< There's no more room to queue a new request, so delay the request until a slot becomes available.
|
||||
};
|
||||
|
||||
struct Section
|
||||
{
|
||||
u8* m_output{ nullptr }; //!< The buffer to write the data to.
|
||||
FileRequest* m_parent{ nullptr }; //!< If set, the file request that is split up by this section.
|
||||
FileRequest* m_wait{ nullptr }; //!< If set, this contains a "wait"-operation that blocks an operation chain from continuing until this section has been loaded.
|
||||
u64 m_readOffset{ 0 }; //!< Offset into the file to start reading from.
|
||||
u64 m_readSize{ 0 }; //!< Number of bytes to read from file.
|
||||
u64 m_blockOffset{ 0 }; //!< Offset into the cache block to start copying from.
|
||||
u64 m_copySize{ 0 }; //!< Number of bytes to copy from cache.
|
||||
u32 m_cacheBlockIndex{ s_fileNotCached }; //!< If assigned, the index of the cache block assigned to this section.
|
||||
bool m_used{ false }; //!< Whether or not this section is used in further processing.
|
||||
|
||||
// Add the provided section in front of this one.
|
||||
void Prefix(const Section& section);
|
||||
};
|
||||
|
||||
using TimePoint = AZStd::chrono::system_clock::time_point;
|
||||
|
||||
void ReadFile(FileRequest* request, FileRequest::ReadData& data);
|
||||
void ContinueReadFile(FileRequest* request, u64 fileLength);
|
||||
CacheResult ReadFromCache(FileRequest* request, Section& section, const RequestPath& filePath);
|
||||
CacheResult ReadFromCache(FileRequest* request, Section& section, u32 cacheBlock);
|
||||
CacheResult ServiceFromCache(FileRequest* request, Section& section, const RequestPath& filePath, bool sharedRead);
|
||||
void CompleteRead(FileRequest& request);
|
||||
bool SplitRequest(Section& prolog, Section& main, Section& epilog, const RequestPath& filePath, u64 fileLength,
|
||||
u64 offset, u64 size, u8* buffer) const;
|
||||
|
||||
u8* GetCacheBlockData(u32 index);
|
||||
void TouchBlock(u32 index);
|
||||
AZ::u32 RecycleOldestBlock(const RequestPath& filePath, u64 offset);
|
||||
u32 FindInCache(const RequestPath& filePath, u64 offset) const;
|
||||
bool IsCacheBlockInFlight(u32 index) const;
|
||||
void ResetCacheEntry(u32 index);
|
||||
void ResetCache();
|
||||
|
||||
//! Map of the file requests that are being processed and the sections of the parent requests they'll complete.
|
||||
AZStd::unordered_multimap<FileRequest*, Section> m_pendingRequests;
|
||||
//! List of file sections that were delayed because the cache was full.
|
||||
AZStd::deque<Section> m_delayedSections;
|
||||
|
||||
AZ::Statistics::RunningStatistic m_hitRateStat;
|
||||
AZ::Statistics::RunningStatistic m_cacheableStat;
|
||||
|
||||
u8* m_cache;
|
||||
u64 m_cacheSize;
|
||||
u32 m_blockSize;
|
||||
u32 m_alignment;
|
||||
u32 m_numBlocks;
|
||||
s32 m_numInFlightRequests{ 0 };
|
||||
//! The file path associated with a cache block.
|
||||
AZStd::unique_ptr<RequestPath[]> m_cachedPaths; // Array of m_numBlocks size.
|
||||
//! The offset into the file the cache blocks starts at.
|
||||
AZStd::unique_ptr<u64[]> m_cachedOffsets; // Array of m_numBlocks size.
|
||||
//! The last time the cache block was read from.
|
||||
AZStd::unique_ptr<TimePoint[]> m_blockLastTouched; // Array of m_numBlocks size.
|
||||
//! The file request that's currently read data into the cache block. If null, the block has been read.
|
||||
AZStd::unique_ptr<FileRequest*[]> m_inFlightRequests; // Array of m_numbBlocks size.
|
||||
|
||||
//! The number of requests waiting for meta data to be retrieved.
|
||||
s32 m_numMetaDataRetrievalInProgress{ 0 };
|
||||
//! Whether or not only the epilog ever writes to the cache.
|
||||
bool m_onlyEpilogWrites;
|
||||
};
|
||||
} // namespace AZ::IO
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace IO
|
||||
{
|
||||
struct BlockCacheConfig final :
|
||||
public IStreamerStackConfig
|
||||
{
|
||||
AZ_RTTI(AZ::IO::BlockCacheConfig, "{70120525-88A4-40B6-A75B-BAA7E8FD77F3}", IStreamerStackConfig);
|
||||
AZ_CLASS_ALLOCATOR(BlockCacheConfig, AZ::SystemAllocator, 0);
|
||||
|
||||
~BlockCacheConfig() override = default;
|
||||
AZStd::shared_ptr<StreamStackEntry> AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent) override;
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
//! Dynamic options for the blocks size.
|
||||
//! It's possible to set static sizes or use the names from this enum to have AZ::IO::Streamer automatically fill in the sizes.
|
||||
//! Fixed sizes are set through the Settings Registry with "BlockSize": 524288, while dynamic values are set like
|
||||
//! "BlockSize": "MemoryAlignment". In the latter case AZ::IO::Streamer will use the available hardware information and fill
|
||||
//! in the actual value.
|
||||
enum BlockSize : u32
|
||||
{
|
||||
MaxTransfer = AZStd::numeric_limits<u32>::max(), //!< The largest possible block size.
|
||||
MemoryAlignment = MaxTransfer - 1, //!< The size of the minimal memory requirement of the storage device.
|
||||
SizeAlignment = MemoryAlignment - 1 //!< The minimal read size required by the storage device.
|
||||
};
|
||||
|
||||
//! The overall size of the cache in megabytes.
|
||||
u32 m_cacheSizeMib{ 8 };
|
||||
//! The size of the individual blocks inside the cache.
|
||||
BlockSize m_blockSize{ BlockSize::MemoryAlignment };
|
||||
};
|
||||
|
||||
class BlockCache
|
||||
: public StreamStackEntry
|
||||
{
|
||||
public:
|
||||
BlockCache(u64 cacheSize, u32 blockSize, u32 alignment, bool onlyEpilogWrites);
|
||||
BlockCache(BlockCache&& rhs) = delete;
|
||||
BlockCache(const BlockCache& rhs) = delete;
|
||||
~BlockCache() override;
|
||||
|
||||
BlockCache& operator=(BlockCache&& rhs) = delete;
|
||||
BlockCache& operator=(const BlockCache& rhs) = delete;
|
||||
|
||||
void QueueRequest(FileRequest* request) override;
|
||||
bool ExecuteRequests() override;
|
||||
|
||||
void UpdateStatus(Status& status) const override;
|
||||
void UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now, AZStd::vector<FileRequest*>& internalPending,
|
||||
StreamerContext::PreparedQueue::iterator pendingBegin, StreamerContext::PreparedQueue::iterator pendingEnd) override;
|
||||
void AddDelayedRequests(AZStd::vector<FileRequest*>& internalPending);
|
||||
void UpdatePendingRequestEstimations();
|
||||
|
||||
void FlushCache(const RequestPath& filePath);
|
||||
void FlushEntireCache();
|
||||
|
||||
void CollectStatistics(AZStd::vector<Statistic>& statistics) const override;
|
||||
|
||||
double CalculateHitRatePercentage() const;
|
||||
double CalculateCacheableRatePercentage() const;
|
||||
s32 CalculateAvailableRequestSlots() const;
|
||||
|
||||
protected:
|
||||
static constexpr u32 s_fileNotCached = static_cast<u32>(-1);
|
||||
|
||||
enum class CacheResult
|
||||
{
|
||||
ReadFromCache, //!< Data was found in the cache and reused.
|
||||
CacheMiss, //!< Data wasn't found in the cache and no sub request was queued.
|
||||
Queued, //!< A sub request was created or appended and queued for processing on the next entry in the streamer stack.
|
||||
Delayed //!< There's no more room to queue a new request, so delay the request until a slot becomes available.
|
||||
};
|
||||
|
||||
struct Section
|
||||
{
|
||||
u8* m_output{ nullptr }; //!< The buffer to write the data to.
|
||||
FileRequest* m_parent{ nullptr }; //!< If set, the file request that is split up by this section.
|
||||
FileRequest* m_wait{ nullptr }; //!< If set, this contains a "wait"-operation that blocks an operation chain from continuing until this section has been loaded.
|
||||
u64 m_readOffset{ 0 }; //!< Offset into the file to start reading from.
|
||||
u64 m_readSize{ 0 }; //!< Number of bytes to read from file.
|
||||
u64 m_blockOffset{ 0 }; //!< Offset into the cache block to start copying from.
|
||||
u64 m_copySize{ 0 }; //!< Number of bytes to copy from cache.
|
||||
u32 m_cacheBlockIndex{ s_fileNotCached }; //!< If assigned, the index of the cache block assigned to this section.
|
||||
bool m_used{ false }; //!< Whether or not this section is used in further processing.
|
||||
|
||||
// Add the provided section in front of this one.
|
||||
void Prefix(const Section& section);
|
||||
};
|
||||
|
||||
using TimePoint = AZStd::chrono::system_clock::time_point;
|
||||
|
||||
void ReadFile(FileRequest* request, FileRequest::ReadData& data);
|
||||
void ContinueReadFile(FileRequest* request, u64 fileLength);
|
||||
CacheResult ReadFromCache(FileRequest* request, Section& section, const RequestPath& filePath);
|
||||
CacheResult ReadFromCache(FileRequest* request, Section& section, u32 cacheBlock);
|
||||
CacheResult ServiceFromCache(FileRequest* request, Section& section, const RequestPath& filePath, bool sharedRead);
|
||||
void CompleteRead(FileRequest& request);
|
||||
bool SplitRequest(Section& prolog, Section& main, Section& epilog, const RequestPath& filePath, u64 fileLength,
|
||||
u64 offset, u64 size, u8* buffer) const;
|
||||
|
||||
u8* GetCacheBlockData(u32 index);
|
||||
void TouchBlock(u32 index);
|
||||
AZ::u32 RecycleOldestBlock(const RequestPath& filePath, u64 offset);
|
||||
u32 FindInCache(const RequestPath& filePath, u64 offset) const;
|
||||
bool IsCacheBlockInFlight(u32 index) const;
|
||||
void ResetCacheEntry(u32 index);
|
||||
void ResetCache();
|
||||
|
||||
//! Map of the file requests that are being processed and the sections of the parent requests they'll complete.
|
||||
AZStd::unordered_multimap<FileRequest*, Section> m_pendingRequests;
|
||||
//! List of file sections that were delayed because the cache was full.
|
||||
AZStd::deque<Section> m_delayedSections;
|
||||
|
||||
AZ::Statistics::RunningStatistic m_hitRateStat;
|
||||
AZ::Statistics::RunningStatistic m_cacheableStat;
|
||||
|
||||
u8* m_cache;
|
||||
u64 m_cacheSize;
|
||||
u32 m_blockSize;
|
||||
u32 m_alignment;
|
||||
u32 m_numBlocks;
|
||||
s32 m_numInFlightRequests{ 0 };
|
||||
//! The file path associated with a cache block.
|
||||
AZStd::unique_ptr<RequestPath[]> m_cachedPaths; // Array of m_numBlocks size.
|
||||
//! The offset into the file the cache blocks starts at.
|
||||
AZStd::unique_ptr<u64[]> m_cachedOffsets; // Array of m_numBlocks size.
|
||||
//! The last time the cache block was read from.
|
||||
AZStd::unique_ptr<TimePoint[]> m_blockLastTouched; // Array of m_numBlocks size.
|
||||
//! The file request that's currently read data into the cache block. If null, the block has been read.
|
||||
AZStd::unique_ptr<FileRequest*[]> m_inFlightRequests; // Array of m_numbBlocks size.
|
||||
|
||||
//! The number of requests waiting for meta data to be retrieved.
|
||||
s32 m_numMetaDataRetrievalInProgress{ 0 };
|
||||
//! Whether or not only the epilog ever writes to the cache.
|
||||
bool m_onlyEpilogWrites;
|
||||
};
|
||||
} // namespace IO
|
||||
|
||||
AZ_TYPE_INFO_SPECIALIZE(AZ::IO::BlockCacheConfig::BlockSize, "{5D4D597D-4605-462D-A27D-8046115C5381}");
|
||||
} // namespace AZ
|
||||
|
||||
@@ -18,77 +18,74 @@
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
struct DedicatedCacheConfig final :
|
||||
public IStreamerStackConfig
|
||||
{
|
||||
struct DedicatedCacheConfig final :
|
||||
public IStreamerStackConfig
|
||||
{
|
||||
AZ_RTTI(AZ::IO::DedicatedCacheConfig, "{DF0F6029-02B0-464C-9846-524654335BCC}", IStreamerStackConfig);
|
||||
AZ_CLASS_ALLOCATOR(DedicatedCacheConfig, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(AZ::IO::DedicatedCacheConfig, "{DF0F6029-02B0-464C-9846-524654335BCC}", IStreamerStackConfig);
|
||||
AZ_CLASS_ALLOCATOR(DedicatedCacheConfig, AZ::SystemAllocator, 0);
|
||||
|
||||
~DedicatedCacheConfig() override = default;
|
||||
AZStd::shared_ptr<StreamStackEntry> AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent) override;
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
~DedicatedCacheConfig() override = default;
|
||||
AZStd::shared_ptr<StreamStackEntry> AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent) override;
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
//! The size of the individual blocks inside the cache.
|
||||
BlockCacheConfig::BlockSize m_blockSize{ BlockCacheConfig::BlockSize::MemoryAlignment };
|
||||
//! The overall size of the cache in megabytes.
|
||||
u32 m_cacheSizeMib{ 8 };
|
||||
//! If true, only the epilog is written otherwise the prolog and epilog are written. In either case both prolog and epilog are read.
|
||||
//! For uses of the cache that read mostly sequentially this flag should be set to true. If reads are more random than it's better
|
||||
//! to set this flag to false.
|
||||
bool m_writeOnlyEpilog{ true };
|
||||
};
|
||||
//! The size of the individual blocks inside the cache.
|
||||
BlockCacheConfig::BlockSize m_blockSize{ BlockCacheConfig::BlockSize::MemoryAlignment };
|
||||
//! The overall size of the cache in megabytes.
|
||||
u32 m_cacheSizeMib{ 8 };
|
||||
//! If true, only the epilog is written otherwise the prolog and epilog are written. In either case both prolog and epilog are read.
|
||||
//! For uses of the cache that read mostly sequentially this flag should be set to true. If reads are more random than it's better
|
||||
//! to set this flag to false.
|
||||
bool m_writeOnlyEpilog{ true };
|
||||
};
|
||||
|
||||
class DedicatedCache
|
||||
: public StreamStackEntry
|
||||
{
|
||||
public:
|
||||
DedicatedCache(u64 cacheSize, u32 blockSize, u32 alignment, bool onlyEpilogWrites);
|
||||
|
||||
void SetNext(AZStd::shared_ptr<StreamStackEntry> next) override;
|
||||
void SetContext(StreamerContext& context) override;
|
||||
class DedicatedCache
|
||||
: public StreamStackEntry
|
||||
{
|
||||
public:
|
||||
DedicatedCache(u64 cacheSize, u32 blockSize, u32 alignment, bool onlyEpilogWrites);
|
||||
|
||||
void PrepareRequest(FileRequest* request) override;
|
||||
void QueueRequest(FileRequest* request) override;
|
||||
bool ExecuteRequests() override;
|
||||
void SetNext(AZStd::shared_ptr<StreamStackEntry> next) override;
|
||||
void SetContext(StreamerContext& context) override;
|
||||
|
||||
void UpdateStatus(Status& status) const override;
|
||||
void PrepareRequest(FileRequest* request) override;
|
||||
void QueueRequest(FileRequest* request) override;
|
||||
bool ExecuteRequests() override;
|
||||
|
||||
void UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now, AZStd::vector<FileRequest*>& internalPending,
|
||||
StreamerContext::PreparedQueue::iterator pendingBegin, StreamerContext::PreparedQueue::iterator pendingEnd) override;
|
||||
void UpdateStatus(Status& status) const override;
|
||||
|
||||
void CollectStatistics(AZStd::vector<Statistic>& statistics) const override;
|
||||
void UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now, AZStd::vector<FileRequest*>& internalPending,
|
||||
StreamerContext::PreparedQueue::iterator pendingBegin, StreamerContext::PreparedQueue::iterator pendingEnd) override;
|
||||
|
||||
private:
|
||||
void CreateDedicatedCache(FileRequest* request, FileRequest::CreateDedicatedCacheData& data);
|
||||
void DestroyDedicatedCache(FileRequest* request, FileRequest::DestroyDedicatedCacheData& data);
|
||||
void CollectStatistics(AZStd::vector<Statistic>& statistics) const override;
|
||||
|
||||
void ReadFile(FileRequest* request, FileRequest::ReadData& data);
|
||||
size_t FindCache(const RequestPath& filename, FileRange range);
|
||||
size_t FindCache(const RequestPath& filename, u64 offset);
|
||||
private:
|
||||
void CreateDedicatedCache(FileRequest* request, FileRequest::CreateDedicatedCacheData& data);
|
||||
void DestroyDedicatedCache(FileRequest* request, FileRequest::DestroyDedicatedCacheData& data);
|
||||
|
||||
void FlushCache(const RequestPath& filePath);
|
||||
void FlushEntireCache();
|
||||
void ReadFile(FileRequest* request, FileRequest::ReadData& data);
|
||||
size_t FindCache(const RequestPath& filename, FileRange range);
|
||||
size_t FindCache(const RequestPath& filename, u64 offset);
|
||||
|
||||
AZStd::vector<RequestPath> m_cachedFileNames;
|
||||
AZStd::vector<FileRange> m_cachedFileRanges;
|
||||
AZStd::vector<AZStd::unique_ptr<BlockCache>> m_cachedFileCaches;
|
||||
AZStd::vector<size_t> m_cachedFileRefCounts;
|
||||
void FlushCache(const RequestPath& filePath);
|
||||
void FlushEntireCache();
|
||||
|
||||
AZ::Statistics::RunningStatistic m_usagePercentageStat;
|
||||
AZStd::vector<RequestPath> m_cachedFileNames;
|
||||
AZStd::vector<FileRange> m_cachedFileRanges;
|
||||
AZStd::vector<AZStd::unique_ptr<BlockCache>> m_cachedFileCaches;
|
||||
AZStd::vector<size_t> m_cachedFileRefCounts;
|
||||
|
||||
AZ::Statistics::RunningStatistic m_usagePercentageStat;
|
||||
#if AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
|
||||
AZ::Statistics::RunningStatistic m_overallHitRateStat;
|
||||
AZ::Statistics::RunningStatistic m_overallCacheableRateStat;
|
||||
AZ::Statistics::RunningStatistic m_overallHitRateStat;
|
||||
AZ::Statistics::RunningStatistic m_overallCacheableRateStat;
|
||||
#endif
|
||||
|
||||
u64 m_cacheSize;
|
||||
u32 m_alignment;
|
||||
u32 m_blockSize;
|
||||
bool m_onlyEpilogWrites;
|
||||
};
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
u64 m_cacheSize;
|
||||
u32 m_alignment;
|
||||
u32 m_blockSize;
|
||||
bool m_onlyEpilogWrites;
|
||||
};
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -21,403 +21,401 @@
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
class StreamStackEntry;
|
||||
class ExternalFileRequest;
|
||||
|
||||
using FileRequestPtr = AZStd::intrusive_ptr<ExternalFileRequest>;
|
||||
|
||||
class FileRequest final
|
||||
{
|
||||
class StreamStackEntry;
|
||||
class ExternalFileRequest;
|
||||
public:
|
||||
inline constexpr static AZStd::chrono::system_clock::time_point s_noDeadlineTime = AZStd::chrono::system_clock::time_point::max();
|
||||
|
||||
using FileRequestPtr = AZStd::intrusive_ptr<ExternalFileRequest>;
|
||||
|
||||
class FileRequest final
|
||||
friend class StreamerContext;
|
||||
friend class ExternalFileRequest;
|
||||
|
||||
//! Stores a reference to the external request so it stays alive while the request is being processed.
|
||||
//! This is needed because Streamer supports fire-and-forget requests since completion can be handled by
|
||||
//! registering a callback.
|
||||
struct ExternalRequestData
|
||||
{
|
||||
public:
|
||||
inline constexpr static AZStd::chrono::system_clock::time_point s_noDeadlineTime = AZStd::chrono::system_clock::time_point::max();
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
friend class StreamerContext;
|
||||
friend class ExternalFileRequest;
|
||||
explicit ExternalRequestData(FileRequestPtr&& request);
|
||||
|
||||
//! Stores a reference to the external request so it stays alive while the request is being processed.
|
||||
//! This is needed because Streamer supports fire-and-forget requests since completion can be handled by
|
||||
//! registering a callback.
|
||||
struct ExternalRequestData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
FileRequestPtr m_request; //!< The request that was send to Streamer.
|
||||
};
|
||||
|
||||
explicit ExternalRequestData(FileRequestPtr&& request);
|
||||
|
||||
FileRequestPtr m_request; //!< The request that was send to Streamer.
|
||||
};
|
||||
//! Stores an instance of a RequestPath. To reduce copying instances of a RequestPath functions that
|
||||
//! need a path take them by reference to the original request. In some cases a path originates from
|
||||
//! within in the stack and temporary storage is needed. This struct allows for that temporary storage
|
||||
//! so it can be safely referenced later.
|
||||
struct RequestPathStoreData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
//! Stores an instance of a RequestPath. To reduce copying instances of a RequestPath functions that
|
||||
//! need a path take them by reference to the original request. In some cases a path originates from
|
||||
//! within in the stack and temporary storage is needed. This struct allows for that temporary storage
|
||||
//! so it can be safely referenced later.
|
||||
struct RequestPathStoreData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
explicit RequestPathStoreData(RequestPath path);
|
||||
|
||||
explicit RequestPathStoreData(RequestPath path);
|
||||
RequestPath m_path;
|
||||
};
|
||||
|
||||
RequestPath m_path;
|
||||
};
|
||||
//! Request to read data. This is an untranslated request and holds a relative path. The Scheduler
|
||||
//! will translate this to the appropriate ReadData or CompressedReadData.
|
||||
struct ReadRequestData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
//! Request to read data. This is an untranslated request and holds a relative path. The Scheduler
|
||||
//! will translate this to the appropriate ReadData or CompressedReadData.
|
||||
struct ReadRequestData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
ReadRequestData(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority);
|
||||
ReadRequestData(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority);
|
||||
~ReadRequestData();
|
||||
|
||||
RequestPath m_path; //!< Relative path to the target file.
|
||||
IStreamerTypes::RequestMemoryAllocator* m_allocator; //!< Allocator used to manage the memory for this request.
|
||||
AZStd::chrono::system_clock::time_point m_deadline; //!< Time by which this request should have been completed.
|
||||
void* m_output; //!< The memory address assigned (during processing) to store the read data to.
|
||||
u64 m_outputSize; //!< The memory size of the addressed used to store the read data.
|
||||
u64 m_offset; //!< The offset in bytes into the file.
|
||||
u64 m_size; //!< The number of bytes to read from the file.
|
||||
IStreamerTypes::Priority m_priority; //!< Priority used for ordering requests. This is used when requests have the same deadline.
|
||||
IStreamerTypes::MemoryType m_memoryType; //!< The type of memory provided by the allocator if used.
|
||||
};
|
||||
|
||||
//! Request to read data. This is a translated request and holds an absolute path and has been
|
||||
//! resolved to the archive file if needed.
|
||||
struct ReadData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead);
|
||||
|
||||
const RequestPath& m_path; //!< The path to the file that contains the requested data.
|
||||
void* m_output; //!< Target output to write the read data to.
|
||||
u64 m_outputSize; //!< Size of memory m_output points to. This needs to be at least as big as m_size, but can be bigger.
|
||||
u64 m_offset; //!< The offset in bytes into the file.
|
||||
u64 m_size; //!< The number of bytes to read from the file.
|
||||
bool m_sharedRead; //!< True if other code will be reading from the file or the stack entry can exclusively lock.
|
||||
};
|
||||
|
||||
//! Request to read and decompress data.
|
||||
struct CompressedReadData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize);
|
||||
|
||||
CompressionInfo m_compressionInfo;
|
||||
void* m_output; //!< Target output to write the read data to.
|
||||
u64 m_readOffset; //!< The offset into the decompressed to start copying from.
|
||||
u64 m_readSize; //!< Number of bytes to read from the decompressed file.
|
||||
};
|
||||
|
||||
//! Holds the progress of an operation chain until this request is explicitly completed.
|
||||
struct WaitData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
};
|
||||
|
||||
//! Checks to see if any node in the stack can find a file at the provided path.
|
||||
struct FileExistsCheckData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit FileExistsCheckData(const RequestPath& path);
|
||||
|
||||
const RequestPath& m_path;
|
||||
bool m_found{ false };
|
||||
};
|
||||
|
||||
//! Searches for a file in the stack and retrieves the meta data. This may be slower than a file exists
|
||||
//! check.
|
||||
struct FileMetaDataRetrievalData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit FileMetaDataRetrievalData(const RequestPath& path);
|
||||
|
||||
const RequestPath& m_path;
|
||||
u64 m_fileSize{ 0 };
|
||||
bool m_found{ false };
|
||||
};
|
||||
|
||||
//! Cancels a request in the stream stack, if possible.
|
||||
struct CancelData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHighest;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit CancelData(FileRequestPtr target);
|
||||
|
||||
FileRequestPtr m_target; //!< The request that will be canceled.
|
||||
};
|
||||
|
||||
//! Updates the priority and deadline of a request that has not been queued yet.
|
||||
struct RescheduleData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
RescheduleData(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline, IStreamerTypes::Priority newPriority);
|
||||
|
||||
FileRequestPtr m_target; //!< The request that will be rescheduled.
|
||||
AZStd::chrono::system_clock::time_point m_newDeadline; //!< The new deadline for the request.
|
||||
IStreamerTypes::Priority m_newPriority; //!< The new priority for the request.
|
||||
};
|
||||
|
||||
//! Flushes all references to the provided file in the streaming stack.
|
||||
struct FlushData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit FlushData(RequestPath path);
|
||||
|
||||
RequestPath m_path;
|
||||
};
|
||||
|
||||
//! Flushes all caches in the streaming stack.
|
||||
struct FlushAllData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
};
|
||||
|
||||
//! Creates a cache dedicated to a single file. This is best used for files where blocks are read from
|
||||
//! periodically such as audio banks of video files.
|
||||
struct CreateDedicatedCacheData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
CreateDedicatedCacheData(RequestPath path, const FileRange& range);
|
||||
|
||||
RequestPath m_path;
|
||||
FileRange m_range;
|
||||
};
|
||||
|
||||
//! Destroys a cache dedicated to a single file that was previously created by CreateDedicatedCache
|
||||
struct DestroyDedicatedCacheData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
DestroyDedicatedCacheData(RequestPath path, const FileRange& range);
|
||||
|
||||
RequestPath m_path;
|
||||
FileRange m_range;
|
||||
};
|
||||
|
||||
struct ReportData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityLow;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
enum class ReportType
|
||||
{
|
||||
FileLocks
|
||||
};
|
||||
|
||||
explicit ReportData(ReportType reportType);
|
||||
|
||||
ReportType m_reportType;
|
||||
};
|
||||
|
||||
//! Data for a custom command. This can be used by nodes added extensions that need data that can't be stored
|
||||
//! in the already provided data.
|
||||
struct CustomData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
|
||||
CustomData(AZStd::any data, bool failWhenUnhandled);
|
||||
|
||||
AZStd::any m_data; //!< The data for the custom request.
|
||||
bool m_failWhenUnhandled; //!< Whether or not the request is marked as failed or success when no node process it.
|
||||
};
|
||||
|
||||
using CommandVariant = AZStd::variant<AZStd::monostate, ExternalRequestData, RequestPathStoreData, ReadRequestData, ReadData,
|
||||
CompressedReadData, WaitData, FileExistsCheckData, FileMetaDataRetrievalData, CancelData, RescheduleData, FlushData,
|
||||
FlushAllData, CreateDedicatedCacheData, DestroyDedicatedCacheData, ReportData, CustomData>;
|
||||
using OnCompletionCallback = AZStd::function<void(FileRequest& request)>;
|
||||
|
||||
AZ_CLASS_ALLOCATOR(FileRequest, SystemAllocator, 0);
|
||||
|
||||
enum class Usage : u8
|
||||
{
|
||||
Internal,
|
||||
External
|
||||
};
|
||||
|
||||
void CreateRequestLink(FileRequestPtr&& request);
|
||||
void CreateRequestPathStore(FileRequest* parent, RequestPath path);
|
||||
void CreateReadRequest(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size,
|
||||
ReadRequestData(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority);
|
||||
void CreateReadRequest(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator, u64 offset, u64 size,
|
||||
ReadRequestData(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority);
|
||||
void CreateRead(FileRequest* parent, void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead = false);
|
||||
void CreateCompressedRead(FileRequest* parent, const CompressionInfo& compressionInfo, void* output,
|
||||
u64 readOffset, u64 readSize);
|
||||
void CreateCompressedRead(FileRequest* parent, CompressionInfo&& compressionInfo, void* output,
|
||||
u64 readOffset, u64 readSize);
|
||||
void CreateWait(FileRequest* parent);
|
||||
void CreateFileExistsCheck(const RequestPath& path);
|
||||
void CreateFileMetaDataRetrieval(const RequestPath& path);
|
||||
void CreateCancel(FileRequestPtr target);
|
||||
void CreateReschedule(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline, IStreamerTypes::Priority newPriority);
|
||||
void CreateFlush(RequestPath path);
|
||||
void CreateFlushAll();
|
||||
void CreateDedicatedCacheCreation(RequestPath path, const FileRange& range = {}, FileRequest* parent = nullptr);
|
||||
void CreateDedicatedCacheDestruction(RequestPath path, const FileRange& range = {}, FileRequest* parent = nullptr);
|
||||
void CreateReport(ReportData::ReportType reportType);
|
||||
void CreateCustom(AZStd::any data, bool failWhenUnhandled = true, FileRequest* parent = nullptr);
|
||||
~ReadRequestData();
|
||||
|
||||
void SetCompletionCallback(OnCompletionCallback callback);
|
||||
|
||||
CommandVariant& GetCommand();
|
||||
const CommandVariant& GetCommand() const;
|
||||
|
||||
IStreamerTypes::RequestStatus GetStatus() const;
|
||||
void SetStatus(IStreamerTypes::RequestStatus newStatus);
|
||||
FileRequest* GetParent();
|
||||
const FileRequest* GetParent() const;
|
||||
size_t GetNumDependencies() const;
|
||||
static constexpr size_t GetMaxNumDependencies();
|
||||
//! Whether or not this request should fail if no node in the chain has picked up the request.
|
||||
bool FailsWhenUnhandled() const;
|
||||
|
||||
//! Checks the chain of request for the provided command. Returns the command if found, otherwise null.
|
||||
template<typename T> T* GetCommandFromChain();
|
||||
//! Checks the chain of request for the provided command. Returns the command if found, otherwise null.
|
||||
template<typename T> const T* GetCommandFromChain() const;
|
||||
|
||||
//! Determines if this request is contributing to the external request.
|
||||
bool WorksOn(FileRequestPtr& request) const;
|
||||
|
||||
//! Returns the id that's assigned to the request when it was added to the pending queue.
|
||||
//! The id will always increment so a smaller id means it was originally queued earlier.
|
||||
size_t GetPendingId() const;
|
||||
|
||||
//! Set the estimated completion time for this request and it's immediate parent. The general approach
|
||||
//! to getting the final estimation is to bubble up the estimation, with ever entry in the stack adding
|
||||
//! it's own additional delay.
|
||||
void SetEstimatedCompletion(AZStd::chrono::system_clock::time_point time);
|
||||
AZStd::chrono::system_clock::time_point GetEstimatedCompletion() const;
|
||||
|
||||
private:
|
||||
explicit FileRequest(Usage usage = Usage::Internal);
|
||||
~FileRequest();
|
||||
|
||||
void Reset();
|
||||
void SetOptionalParent(FileRequest* parent);
|
||||
|
||||
inline static void OnCompletionPlaceholder(const FileRequest& /*request*/) {}
|
||||
|
||||
//! Command and parameters for the request.
|
||||
CommandVariant m_command;
|
||||
|
||||
//! Status of the request.
|
||||
AZStd::atomic<IStreamerTypes::RequestStatus> m_status{ IStreamerTypes::RequestStatus::Pending };
|
||||
|
||||
//! Called once the request has completed. This will always be called from the Streamer thread
|
||||
//! and thread safety is the responsibility of called function. When assigning a lambda avoid
|
||||
//! capturing a FileRequestPtr by value as this will cause a circular reference which causes
|
||||
//! the FileRequestPtr to never be released and causes a memory leak. This call will
|
||||
//! block the main Streamer thread until it returns so callbacks should be kept short. If
|
||||
//! a longer running task is needed consider using a job to do the work.
|
||||
OnCompletionCallback m_onCompletion;
|
||||
|
||||
//! Estimated time this request will complete. This is an estimation and depends on many
|
||||
//! factors which can cause it to change drastically from moment to moment.
|
||||
AZStd::chrono::system_clock::time_point m_estimatedCompletion;
|
||||
|
||||
//! The file request that has a dependency on this one. This can be null if there are no
|
||||
//! other request depending on this one to complete.
|
||||
FileRequest* m_parent{ nullptr };
|
||||
|
||||
//! Id assigned when the request is added to the pending queue.
|
||||
size_t m_pendingId{ 0 };
|
||||
|
||||
//! The number of dependent file request that need to complete before this one is done.
|
||||
u16 m_dependencies{ 0 };
|
||||
|
||||
//! Internal request. If this is true the request is created inside the streaming stack and never
|
||||
//! leaves it. If true it will automatically be maintained by the scheduler, if false than it's
|
||||
//! up to the owner to recycle this request.
|
||||
Usage m_usage{ Usage::Internal };
|
||||
|
||||
//! Whether or not this request is currently in a recycle bin. This allows detecting double deletes.
|
||||
bool m_inRecycleBin{ false };
|
||||
RequestPath m_path; //!< Relative path to the target file.
|
||||
IStreamerTypes::RequestMemoryAllocator* m_allocator; //!< Allocator used to manage the memory for this request.
|
||||
AZStd::chrono::system_clock::time_point m_deadline; //!< Time by which this request should have been completed.
|
||||
void* m_output; //!< The memory address assigned (during processing) to store the read data to.
|
||||
u64 m_outputSize; //!< The memory size of the addressed used to store the read data.
|
||||
u64 m_offset; //!< The offset in bytes into the file.
|
||||
u64 m_size; //!< The number of bytes to read from the file.
|
||||
IStreamerTypes::Priority m_priority; //!< Priority used for ordering requests. This is used when requests have the same deadline.
|
||||
IStreamerTypes::MemoryType m_memoryType; //!< The type of memory provided by the allocator if used.
|
||||
};
|
||||
|
||||
class StreamerContext;
|
||||
class FileRequestHandle;
|
||||
|
||||
//! ExternalFileRequest is a wrapper around the FileRequest so it's safe to use outside the
|
||||
//! Streaming Stack. The main differences are that ExternalFileRequest is used in a thread-safe
|
||||
//! context and it doesn't get automatically destroyed upon completion. Instead intrusive_ptr is
|
||||
//! used to handle clean up.
|
||||
class ExternalFileRequest final
|
||||
//! Request to read data. This is a translated request and holds an absolute path and has been
|
||||
//! resolved to the archive file if needed.
|
||||
struct ReadData
|
||||
{
|
||||
friend struct AZStd::IntrusivePtrCountPolicy<ExternalFileRequest>;
|
||||
friend class FileRequestHandle;
|
||||
friend class FileRequest;
|
||||
friend class Streamer;
|
||||
friend class StreamerContext;
|
||||
friend class Scheduler;
|
||||
friend class Device;
|
||||
friend bool operator==(const FileRequestHandle& lhs, const FileRequestPtr& rhs);
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ExternalFileRequest, SystemAllocator, 0);
|
||||
ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead);
|
||||
|
||||
explicit ExternalFileRequest(StreamerContext* owner);
|
||||
|
||||
private:
|
||||
void add_ref();
|
||||
void release();
|
||||
|
||||
FileRequest m_request;
|
||||
AZStd::atomic_uint64_t m_refCount{ 0 };
|
||||
StreamerContext* m_owner;
|
||||
const RequestPath& m_path; //!< The path to the file that contains the requested data.
|
||||
void* m_output; //!< Target output to write the read data to.
|
||||
u64 m_outputSize; //!< Size of memory m_output points to. This needs to be at least as big as m_size, but can be bigger.
|
||||
u64 m_offset; //!< The offset in bytes into the file.
|
||||
u64 m_size; //!< The number of bytes to read from the file.
|
||||
bool m_sharedRead; //!< True if other code will be reading from the file or the stack entry can exclusively lock.
|
||||
};
|
||||
|
||||
class FileRequestHandle
|
||||
//! Request to read and decompress data.
|
||||
struct CompressedReadData
|
||||
{
|
||||
public:
|
||||
friend class Streamer;
|
||||
friend bool operator==(const FileRequestHandle& lhs, const FileRequestPtr& rhs);
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
// Intentional cast operator.
|
||||
FileRequestHandle(FileRequest& request)
|
||||
: m_request(&request)
|
||||
{}
|
||||
CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize);
|
||||
|
||||
// Intentional cast operator.
|
||||
FileRequestHandle(const FileRequestPtr& request)
|
||||
: m_request(request ? &request->m_request : nullptr)
|
||||
{}
|
||||
|
||||
private:
|
||||
FileRequest* m_request;
|
||||
CompressionInfo m_compressionInfo;
|
||||
void* m_output; //!< Target output to write the read data to.
|
||||
u64 m_readOffset; //!< The offset into the decompressed to start copying from.
|
||||
u64 m_readSize; //!< Number of bytes to read from the decompressed file.
|
||||
};
|
||||
|
||||
bool operator==(const FileRequestHandle& lhs, const FileRequestPtr& rhs);
|
||||
bool operator==(const FileRequestPtr& lhs, const FileRequestHandle& rhs);
|
||||
bool operator!=(const FileRequestHandle& lhs, const FileRequestPtr& rhs);
|
||||
bool operator!=(const FileRequestPtr& lhs, const FileRequestHandle& rhs);
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
//! Holds the progress of an operation chain until this request is explicitly completed.
|
||||
struct WaitData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
};
|
||||
|
||||
//! Checks to see if any node in the stack can find a file at the provided path.
|
||||
struct FileExistsCheckData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit FileExistsCheckData(const RequestPath& path);
|
||||
|
||||
const RequestPath& m_path;
|
||||
bool m_found{ false };
|
||||
};
|
||||
|
||||
//! Searches for a file in the stack and retrieves the meta data. This may be slower than a file exists
|
||||
//! check.
|
||||
struct FileMetaDataRetrievalData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit FileMetaDataRetrievalData(const RequestPath& path);
|
||||
|
||||
const RequestPath& m_path;
|
||||
u64 m_fileSize{ 0 };
|
||||
bool m_found{ false };
|
||||
};
|
||||
|
||||
//! Cancels a request in the stream stack, if possible.
|
||||
struct CancelData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHighest;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit CancelData(FileRequestPtr target);
|
||||
|
||||
FileRequestPtr m_target; //!< The request that will be canceled.
|
||||
};
|
||||
|
||||
//! Updates the priority and deadline of a request that has not been queued yet.
|
||||
struct RescheduleData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
RescheduleData(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline, IStreamerTypes::Priority newPriority);
|
||||
|
||||
FileRequestPtr m_target; //!< The request that will be rescheduled.
|
||||
AZStd::chrono::system_clock::time_point m_newDeadline; //!< The new deadline for the request.
|
||||
IStreamerTypes::Priority m_newPriority; //!< The new priority for the request.
|
||||
};
|
||||
|
||||
//! Flushes all references to the provided file in the streaming stack.
|
||||
struct FlushData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit FlushData(RequestPath path);
|
||||
|
||||
RequestPath m_path;
|
||||
};
|
||||
|
||||
//! Flushes all caches in the streaming stack.
|
||||
struct FlushAllData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
};
|
||||
|
||||
//! Creates a cache dedicated to a single file. This is best used for files where blocks are read from
|
||||
//! periodically such as audio banks of video files.
|
||||
struct CreateDedicatedCacheData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
CreateDedicatedCacheData(RequestPath path, const FileRange& range);
|
||||
|
||||
RequestPath m_path;
|
||||
FileRange m_range;
|
||||
};
|
||||
|
||||
//! Destroys a cache dedicated to a single file that was previously created by CreateDedicatedCache
|
||||
struct DestroyDedicatedCacheData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
DestroyDedicatedCacheData(RequestPath path, const FileRange& range);
|
||||
|
||||
RequestPath m_path;
|
||||
FileRange m_range;
|
||||
};
|
||||
|
||||
struct ReportData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityLow;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
enum class ReportType
|
||||
{
|
||||
FileLocks
|
||||
};
|
||||
|
||||
explicit ReportData(ReportType reportType);
|
||||
|
||||
ReportType m_reportType;
|
||||
};
|
||||
|
||||
//! Data for a custom command. This can be used by nodes added extensions that need data that can't be stored
|
||||
//! in the already provided data.
|
||||
struct CustomData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
|
||||
CustomData(AZStd::any data, bool failWhenUnhandled);
|
||||
|
||||
AZStd::any m_data; //!< The data for the custom request.
|
||||
bool m_failWhenUnhandled; //!< Whether or not the request is marked as failed or success when no node process it.
|
||||
};
|
||||
|
||||
using CommandVariant = AZStd::variant<AZStd::monostate, ExternalRequestData, RequestPathStoreData, ReadRequestData, ReadData,
|
||||
CompressedReadData, WaitData, FileExistsCheckData, FileMetaDataRetrievalData, CancelData, RescheduleData, FlushData,
|
||||
FlushAllData, CreateDedicatedCacheData, DestroyDedicatedCacheData, ReportData, CustomData>;
|
||||
using OnCompletionCallback = AZStd::function<void(FileRequest& request)>;
|
||||
|
||||
AZ_CLASS_ALLOCATOR(FileRequest, SystemAllocator, 0);
|
||||
|
||||
enum class Usage : u8
|
||||
{
|
||||
Internal,
|
||||
External
|
||||
};
|
||||
|
||||
void CreateRequestLink(FileRequestPtr&& request);
|
||||
void CreateRequestPathStore(FileRequest* parent, RequestPath path);
|
||||
void CreateReadRequest(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority);
|
||||
void CreateReadRequest(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority);
|
||||
void CreateRead(FileRequest* parent, void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead = false);
|
||||
void CreateCompressedRead(FileRequest* parent, const CompressionInfo& compressionInfo, void* output,
|
||||
u64 readOffset, u64 readSize);
|
||||
void CreateCompressedRead(FileRequest* parent, CompressionInfo&& compressionInfo, void* output,
|
||||
u64 readOffset, u64 readSize);
|
||||
void CreateWait(FileRequest* parent);
|
||||
void CreateFileExistsCheck(const RequestPath& path);
|
||||
void CreateFileMetaDataRetrieval(const RequestPath& path);
|
||||
void CreateCancel(FileRequestPtr target);
|
||||
void CreateReschedule(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline, IStreamerTypes::Priority newPriority);
|
||||
void CreateFlush(RequestPath path);
|
||||
void CreateFlushAll();
|
||||
void CreateDedicatedCacheCreation(RequestPath path, const FileRange& range = {}, FileRequest* parent = nullptr);
|
||||
void CreateDedicatedCacheDestruction(RequestPath path, const FileRange& range = {}, FileRequest* parent = nullptr);
|
||||
void CreateReport(ReportData::ReportType reportType);
|
||||
void CreateCustom(AZStd::any data, bool failWhenUnhandled = true, FileRequest* parent = nullptr);
|
||||
|
||||
void SetCompletionCallback(OnCompletionCallback callback);
|
||||
|
||||
CommandVariant& GetCommand();
|
||||
const CommandVariant& GetCommand() const;
|
||||
|
||||
IStreamerTypes::RequestStatus GetStatus() const;
|
||||
void SetStatus(IStreamerTypes::RequestStatus newStatus);
|
||||
FileRequest* GetParent();
|
||||
const FileRequest* GetParent() const;
|
||||
size_t GetNumDependencies() const;
|
||||
static constexpr size_t GetMaxNumDependencies();
|
||||
//! Whether or not this request should fail if no node in the chain has picked up the request.
|
||||
bool FailsWhenUnhandled() const;
|
||||
|
||||
//! Checks the chain of request for the provided command. Returns the command if found, otherwise null.
|
||||
template<typename T> T* GetCommandFromChain();
|
||||
//! Checks the chain of request for the provided command. Returns the command if found, otherwise null.
|
||||
template<typename T> const T* GetCommandFromChain() const;
|
||||
|
||||
//! Determines if this request is contributing to the external request.
|
||||
bool WorksOn(FileRequestPtr& request) const;
|
||||
|
||||
//! Returns the id that's assigned to the request when it was added to the pending queue.
|
||||
//! The id will always increment so a smaller id means it was originally queued earlier.
|
||||
size_t GetPendingId() const;
|
||||
|
||||
//! Set the estimated completion time for this request and it's immediate parent. The general approach
|
||||
//! to getting the final estimation is to bubble up the estimation, with ever entry in the stack adding
|
||||
//! it's own additional delay.
|
||||
void SetEstimatedCompletion(AZStd::chrono::system_clock::time_point time);
|
||||
AZStd::chrono::system_clock::time_point GetEstimatedCompletion() const;
|
||||
|
||||
private:
|
||||
explicit FileRequest(Usage usage = Usage::Internal);
|
||||
~FileRequest();
|
||||
|
||||
void Reset();
|
||||
void SetOptionalParent(FileRequest* parent);
|
||||
|
||||
inline static void OnCompletionPlaceholder(const FileRequest& /*request*/) {}
|
||||
|
||||
//! Command and parameters for the request.
|
||||
CommandVariant m_command;
|
||||
|
||||
//! Status of the request.
|
||||
AZStd::atomic<IStreamerTypes::RequestStatus> m_status{ IStreamerTypes::RequestStatus::Pending };
|
||||
|
||||
//! Called once the request has completed. This will always be called from the Streamer thread
|
||||
//! and thread safety is the responsibility of called function. When assigning a lambda avoid
|
||||
//! capturing a FileRequestPtr by value as this will cause a circular reference which causes
|
||||
//! the FileRequestPtr to never be released and causes a memory leak. This call will
|
||||
//! block the main Streamer thread until it returns so callbacks should be kept short. If
|
||||
//! a longer running task is needed consider using a job to do the work.
|
||||
OnCompletionCallback m_onCompletion;
|
||||
|
||||
//! Estimated time this request will complete. This is an estimation and depends on many
|
||||
//! factors which can cause it to change drastically from moment to moment.
|
||||
AZStd::chrono::system_clock::time_point m_estimatedCompletion;
|
||||
|
||||
//! The file request that has a dependency on this one. This can be null if there are no
|
||||
//! other request depending on this one to complete.
|
||||
FileRequest* m_parent{ nullptr };
|
||||
|
||||
//! Id assigned when the request is added to the pending queue.
|
||||
size_t m_pendingId{ 0 };
|
||||
|
||||
//! The number of dependent file request that need to complete before this one is done.
|
||||
u16 m_dependencies{ 0 };
|
||||
|
||||
//! Internal request. If this is true the request is created inside the streaming stack and never
|
||||
//! leaves it. If true it will automatically be maintained by the scheduler, if false than it's
|
||||
//! up to the owner to recycle this request.
|
||||
Usage m_usage{ Usage::Internal };
|
||||
|
||||
//! Whether or not this request is currently in a recycle bin. This allows detecting double deletes.
|
||||
bool m_inRecycleBin{ false };
|
||||
};
|
||||
|
||||
class StreamerContext;
|
||||
class FileRequestHandle;
|
||||
|
||||
//! ExternalFileRequest is a wrapper around the FileRequest so it's safe to use outside the
|
||||
//! Streaming Stack. The main differences are that ExternalFileRequest is used in a thread-safe
|
||||
//! context and it doesn't get automatically destroyed upon completion. Instead intrusive_ptr is
|
||||
//! used to handle clean up.
|
||||
class ExternalFileRequest final
|
||||
{
|
||||
friend struct AZStd::IntrusivePtrCountPolicy<ExternalFileRequest>;
|
||||
friend class FileRequestHandle;
|
||||
friend class FileRequest;
|
||||
friend class Streamer;
|
||||
friend class StreamerContext;
|
||||
friend class Scheduler;
|
||||
friend class Device;
|
||||
friend bool operator==(const FileRequestHandle& lhs, const FileRequestPtr& rhs);
|
||||
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ExternalFileRequest, SystemAllocator, 0);
|
||||
|
||||
explicit ExternalFileRequest(StreamerContext* owner);
|
||||
|
||||
private:
|
||||
void add_ref();
|
||||
void release();
|
||||
|
||||
FileRequest m_request;
|
||||
AZStd::atomic_uint64_t m_refCount{ 0 };
|
||||
StreamerContext* m_owner;
|
||||
};
|
||||
|
||||
class FileRequestHandle
|
||||
{
|
||||
public:
|
||||
friend class Streamer;
|
||||
friend bool operator==(const FileRequestHandle& lhs, const FileRequestPtr& rhs);
|
||||
|
||||
// Intentional cast operator.
|
||||
FileRequestHandle(FileRequest& request)
|
||||
: m_request(&request)
|
||||
{}
|
||||
|
||||
// Intentional cast operator.
|
||||
FileRequestHandle(const FileRequestPtr& request)
|
||||
: m_request(request ? &request->m_request : nullptr)
|
||||
{}
|
||||
|
||||
private:
|
||||
FileRequest* m_request;
|
||||
};
|
||||
|
||||
bool operator==(const FileRequestHandle& lhs, const FileRequestPtr& rhs);
|
||||
bool operator==(const FileRequestPtr& lhs, const FileRequestHandle& rhs);
|
||||
bool operator!=(const FileRequestHandle& lhs, const FileRequestPtr& rhs);
|
||||
bool operator!=(const FileRequestPtr& lhs, const FileRequestHandle& rhs);
|
||||
|
||||
} // namespace AZ::IO
|
||||
|
||||
#include <AzCore/IO/Streamer/FileRequest.inl>
|
||||
|
||||
@@ -19,118 +19,115 @@
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/Statistics/RunningStatistic.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
struct FullFileDecompressorConfig final :
|
||||
public IStreamerStackConfig
|
||||
{
|
||||
struct FullFileDecompressorConfig final :
|
||||
public IStreamerStackConfig
|
||||
AZ_RTTI(AZ::IO::FullFileDecompressorConfig, "{C96B7EC1-8C73-4493-A7CB-66F5D550FC3A}", IStreamerStackConfig);
|
||||
AZ_CLASS_ALLOCATOR(FullFileDecompressorConfig, AZ::SystemAllocator, 0);
|
||||
|
||||
~FullFileDecompressorConfig() override = default;
|
||||
AZStd::shared_ptr<StreamStackEntry> AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent) override;
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
//! Maximum number of reads that are kept in flight.
|
||||
u32 m_maxNumReads{ 2 };
|
||||
//! Maximum number of decompression jobs that can run simultaneously.
|
||||
u32 m_maxNumJobs{ 2 };
|
||||
};
|
||||
|
||||
//! Entry in the streaming stack that decompresses files from an archive that are stored
|
||||
//! as single files and without equally distributed seek points.
|
||||
//! Because the target archive has compressed the entire file, it needs to be decompressed
|
||||
//! completely, so even if the file is partially read, it needs to be fully loaded. This
|
||||
//! also means that there's no upper limit to the memory so every decompression job will
|
||||
//! need to allocate memory as a temporary buffer (in-place decompression is not supported).
|
||||
//! Finally, the lack of an upper limit also means that the duration of the decompression job
|
||||
//! can vary largely so a dedicated job system is used to decompress on to avoid blocking
|
||||
//! the main job system from working.
|
||||
class FullFileDecompressor
|
||||
: public StreamStackEntry
|
||||
{
|
||||
public:
|
||||
FullFileDecompressor(u32 maxNumReads, u32 maxNumJobs, u32 alignment);
|
||||
~FullFileDecompressor() override = default;
|
||||
|
||||
void PrepareRequest(FileRequest* request) override;
|
||||
void QueueRequest(FileRequest* request) override;
|
||||
bool ExecuteRequests() override;
|
||||
|
||||
void UpdateStatus(Status& status) const override;
|
||||
void UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now, AZStd::vector<FileRequest*>& internalPending,
|
||||
StreamerContext::PreparedQueue::iterator pendingBegin, StreamerContext::PreparedQueue::iterator pendingEnd) override;
|
||||
|
||||
void CollectStatistics(AZStd::vector<Statistic>& statistics) const override;
|
||||
|
||||
private:
|
||||
using Buffer = u8*;
|
||||
|
||||
enum class ReadBufferStatus : uint8_t
|
||||
{
|
||||
AZ_RTTI(AZ::IO::FullFileDecompressorConfig, "{C96B7EC1-8C73-4493-A7CB-66F5D550FC3A}", IStreamerStackConfig);
|
||||
AZ_CLASS_ALLOCATOR(FullFileDecompressorConfig, AZ::SystemAllocator, 0);
|
||||
|
||||
~FullFileDecompressorConfig() override = default;
|
||||
AZStd::shared_ptr<StreamStackEntry> AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent) override;
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
//! Maximum number of reads that are kept in flight.
|
||||
u32 m_maxNumReads{ 2 };
|
||||
//! Maximum number of decompression jobs that can run simultaneously.
|
||||
u32 m_maxNumJobs{ 2 };
|
||||
Unused,
|
||||
ReadInFlight,
|
||||
PendingDecompression
|
||||
};
|
||||
|
||||
//! Entry in the streaming stack that decompresses files from an archive that are stored
|
||||
//! as single files and without equally distributed seek points.
|
||||
//! Because the target archive has compressed the entire file, it needs to be decompressed
|
||||
//! completely, so even if the file is partially read, it needs to be fully loaded. This
|
||||
//! also means that there's no upper limit to the memory so every decompression job will
|
||||
//! need to allocate memory as a temporary buffer (in-place decompression is not supported).
|
||||
//! Finally, the lack of an upper limit also means that the duration of the decompression job
|
||||
//! can vary largely so a dedicated job system is used to decompress on to avoid blocking
|
||||
//! the main job system from working.
|
||||
class FullFileDecompressor
|
||||
: public StreamStackEntry
|
||||
struct DecompressionInformation
|
||||
{
|
||||
public:
|
||||
FullFileDecompressor(u32 maxNumReads, u32 maxNumJobs, u32 alignment);
|
||||
~FullFileDecompressor() override = default;
|
||||
bool IsProcessing() const;
|
||||
|
||||
void PrepareRequest(FileRequest* request) override;
|
||||
void QueueRequest(FileRequest* request) override;
|
||||
bool ExecuteRequests() override;
|
||||
|
||||
void UpdateStatus(Status& status) const override;
|
||||
void UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now, AZStd::vector<FileRequest*>& internalPending,
|
||||
StreamerContext::PreparedQueue::iterator pendingBegin, StreamerContext::PreparedQueue::iterator pendingEnd) override;
|
||||
|
||||
void CollectStatistics(AZStd::vector<Statistic>& statistics) const override;
|
||||
AZStd::chrono::high_resolution_clock::time_point m_queueStartTime;
|
||||
AZStd::chrono::high_resolution_clock::time_point m_jobStartTime;
|
||||
Buffer m_compressedData{ nullptr };
|
||||
FileRequest* m_waitRequest{ nullptr };
|
||||
u32 m_alignmentOffset{ 0 };
|
||||
};
|
||||
|
||||
private:
|
||||
using Buffer = u8*;
|
||||
bool IsIdle() const;
|
||||
|
||||
enum class ReadBufferStatus : uint8_t
|
||||
{
|
||||
Unused,
|
||||
ReadInFlight,
|
||||
PendingDecompression
|
||||
};
|
||||
void PrepareReadRequest(FileRequest* request, FileRequest::ReadRequestData& data);
|
||||
void PrepareDedicatedCache(FileRequest* request, const RequestPath& path);
|
||||
void FileExistsCheck(FileRequest* checkRequest);
|
||||
|
||||
struct DecompressionInformation
|
||||
{
|
||||
bool IsProcessing() const;
|
||||
void EstimateCompressedReadRequest(FileRequest* request, AZStd::chrono::microseconds& cumulativeDelay,
|
||||
AZStd::chrono::microseconds decompressionDelay, double totalDecompressionDurationUs, double totalBytesDecompressed) const;
|
||||
|
||||
AZStd::chrono::high_resolution_clock::time_point m_queueStartTime;
|
||||
AZStd::chrono::high_resolution_clock::time_point m_jobStartTime;
|
||||
Buffer m_compressedData{ nullptr };
|
||||
FileRequest* m_waitRequest{ nullptr };
|
||||
u32 m_alignmentOffset{ 0 };
|
||||
};
|
||||
void StartArchiveRead(FileRequest* compressedReadRequest);
|
||||
void FinishArchiveRead(FileRequest* readRequest, u32 readSlot);
|
||||
bool StartDecompressions();
|
||||
void FinishDecompression(FileRequest* waitRequest, u32 jobSlot);
|
||||
|
||||
bool IsIdle() const;
|
||||
static void FullDecompression(StreamerContext* context, DecompressionInformation& info);
|
||||
static void PartialDecompression(StreamerContext* context, DecompressionInformation& info);
|
||||
|
||||
void PrepareReadRequest(FileRequest* request, FileRequest::ReadRequestData& data);
|
||||
void PrepareDedicatedCache(FileRequest* request, const RequestPath& path);
|
||||
void FileExistsCheck(FileRequest* checkRequest);
|
||||
AZStd::deque<FileRequest*> m_pendingReads;
|
||||
AZStd::deque<FileRequest*> m_pendingFileExistChecks;
|
||||
|
||||
void EstimateCompressedReadRequest(FileRequest* request, AZStd::chrono::microseconds& cumulativeDelay,
|
||||
AZStd::chrono::microseconds decompressionDelay, double totalDecompressionDurationUs, double totalBytesDecompressed) const;
|
||||
|
||||
void StartArchiveRead(FileRequest* compressedReadRequest);
|
||||
void FinishArchiveRead(FileRequest* readRequest, u32 readSlot);
|
||||
bool StartDecompressions();
|
||||
void FinishDecompression(FileRequest* waitRequest, u32 jobSlot);
|
||||
|
||||
static void FullDecompression(StreamerContext* context, DecompressionInformation& info);
|
||||
static void PartialDecompression(StreamerContext* context, DecompressionInformation& info);
|
||||
|
||||
AZStd::deque<FileRequest*> m_pendingReads;
|
||||
AZStd::deque<FileRequest*> m_pendingFileExistChecks;
|
||||
|
||||
AverageWindow<size_t, double, s_statisticsWindowSize> m_decompressionJobDelayMicroSec;
|
||||
AverageWindow<size_t, double, s_statisticsWindowSize> m_decompressionDurationMicroSec;
|
||||
AverageWindow<size_t, double, s_statisticsWindowSize> m_bytesDecompressed;
|
||||
AverageWindow<size_t, double, s_statisticsWindowSize> m_decompressionJobDelayMicroSec;
|
||||
AverageWindow<size_t, double, s_statisticsWindowSize> m_decompressionDurationMicroSec;
|
||||
AverageWindow<size_t, double, s_statisticsWindowSize> m_bytesDecompressed;
|
||||
#if AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
|
||||
AZ::Statistics::RunningStatistic m_decompressionBoundStat;
|
||||
AZ::Statistics::RunningStatistic m_readBoundStat;
|
||||
AZ::Statistics::RunningStatistic m_decompressionBoundStat;
|
||||
AZ::Statistics::RunningStatistic m_readBoundStat;
|
||||
#endif
|
||||
|
||||
AZStd::unique_ptr<Buffer[]> m_readBuffers;
|
||||
// Nullptr if not reading, the read request if reading the file and the wait request for decompression when waiting on decompression.
|
||||
AZStd::unique_ptr<FileRequest*[]> m_readRequests;
|
||||
AZStd::unique_ptr<ReadBufferStatus[]> m_readBufferStatus;
|
||||
|
||||
AZStd::unique_ptr<DecompressionInformation[]> m_processingJobs;
|
||||
AZStd::unique_ptr<JobManager> m_decompressionJobManager;
|
||||
AZStd::unique_ptr<JobContext> m_decompressionjobContext;
|
||||
AZStd::unique_ptr<Buffer[]> m_readBuffers;
|
||||
// Nullptr if not reading, the read request if reading the file and the wait request for decompression when waiting on decompression.
|
||||
AZStd::unique_ptr<FileRequest*[]> m_readRequests;
|
||||
AZStd::unique_ptr<ReadBufferStatus[]> m_readBufferStatus;
|
||||
|
||||
size_t m_memoryUsage{ 0 }; //!< Amount of memory used for buffers by the decompressor.
|
||||
u32 m_maxNumReads{ 2 };
|
||||
u32 m_numInFlightReads{ 0 };
|
||||
u32 m_numPendingDecompression{ 0 };
|
||||
u32 m_maxNumJobs{ 1 };
|
||||
u32 m_numRunningJobs{ 0 };
|
||||
u32 m_alignment{ 0 };
|
||||
};
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
AZStd::unique_ptr<DecompressionInformation[]> m_processingJobs;
|
||||
AZStd::unique_ptr<JobManager> m_decompressionJobManager;
|
||||
AZStd::unique_ptr<JobContext> m_decompressionjobContext;
|
||||
|
||||
size_t m_memoryUsage{ 0 }; //!< Amount of memory used for buffers by the decompressor.
|
||||
u32 m_maxNumReads{ 2 };
|
||||
u32 m_numInFlightReads{ 0 };
|
||||
u32 m_numPendingDecompression{ 0 };
|
||||
u32 m_maxNumJobs{ 1 };
|
||||
u32 m_numRunningJobs{ 0 };
|
||||
u32 m_alignment{ 0 };
|
||||
};
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -227,7 +227,7 @@ namespace AZ::IO
|
||||
{
|
||||
auto parentReadRequest = next->GetCommandFromChain<FileRequest::ReadRequestData>();
|
||||
AZ_Assert(parentReadRequest != nullptr, "The issued read request can't be found for the (compressed) read command.");
|
||||
|
||||
|
||||
size_t size = parentReadRequest->m_size;
|
||||
if (parentReadRequest->m_output == nullptr)
|
||||
{
|
||||
@@ -266,7 +266,7 @@ namespace AZ::IO
|
||||
m_processingStartTime = AZStd::chrono::system_clock::now();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
{
|
||||
m_threadData.m_lastFilePath = args.m_path;
|
||||
@@ -411,7 +411,7 @@ namespace AZ::IO
|
||||
++pendingIt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_threadData.m_streamStack->QueueRequest(request);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
namespace AZ::IO
|
||||
{
|
||||
class FileRequest;
|
||||
|
||||
|
||||
class Scheduler final
|
||||
{
|
||||
public:
|
||||
@@ -63,7 +63,7 @@ namespace AZ::IO
|
||||
void Thread_ProcessTillIdle();
|
||||
void Thread_ProcessCancelRequest(FileRequest* request, FileRequest::CancelData& data);
|
||||
void Thread_ProcessRescheduleRequest(FileRequest* request, FileRequest::RescheduleData& data);
|
||||
|
||||
|
||||
enum class Order
|
||||
{
|
||||
FirstRequest, //< The first request is the most important to process next.
|
||||
|
||||
@@ -16,454 +16,451 @@
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/std/typetraits/decay.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
AZStd::shared_ptr<StreamStackEntry> StorageDriveConfig::AddStreamStackEntry(
|
||||
[[maybe_unused]] const HardwareInformation& hardware, [[maybe_unused]] AZStd::shared_ptr<StreamStackEntry> parent)
|
||||
{
|
||||
AZStd::shared_ptr<StreamStackEntry> StorageDriveConfig::AddStreamStackEntry(
|
||||
[[maybe_unused]] const HardwareInformation& hardware, [[maybe_unused]] AZStd::shared_ptr<StreamStackEntry> parent)
|
||||
{
|
||||
return AZStd::make_shared<StorageDrive>(m_maxFileHandles);
|
||||
}
|
||||
return AZStd::make_shared<StorageDrive>(m_maxFileHandles);
|
||||
}
|
||||
|
||||
void StorageDriveConfig::Reflect(AZ::ReflectContext* context)
|
||||
void StorageDriveConfig::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
serializeContext->Class<StorageDriveConfig, IStreamerStackConfig>()
|
||||
->Version(1)
|
||||
->Field("MaxFileHandles", &StorageDriveConfig::m_maxFileHandles);
|
||||
}
|
||||
}
|
||||
|
||||
const AZStd::chrono::microseconds StorageDrive::s_averageSeekTime =
|
||||
AZStd::chrono::milliseconds(9) + // Common average seek time for desktop hdd drives.
|
||||
AZStd::chrono::milliseconds(3); // Rotational latency for a 7200RPM disk
|
||||
|
||||
StorageDrive::StorageDrive(u32 maxFileHandles)
|
||||
: StreamStackEntry("Storage drive (generic)")
|
||||
{
|
||||
m_fileLastUsed.resize(maxFileHandles, AZStd::chrono::system_clock::time_point::min());
|
||||
m_filePaths.resize(maxFileHandles);
|
||||
m_fileHandles.resize(maxFileHandles);
|
||||
|
||||
// Add initial dummy values to the stats to avoid division by zero later on and avoid needing branches.
|
||||
m_readSizeAverage.PushEntry(1);
|
||||
m_readTimeAverage.PushEntry(AZStd::chrono::microseconds(1));
|
||||
}
|
||||
|
||||
void StorageDrive::SetNext(AZStd::shared_ptr<StreamStackEntry> /*next*/)
|
||||
{
|
||||
AZ_Assert(false, "StorageDrive isn't allowed to have a node to forward requests to.");
|
||||
}
|
||||
|
||||
void StorageDrive::PrepareRequest(FileRequest* request)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
AZ_Assert(request, "PrepareRequest was provided a null request.");
|
||||
|
||||
if (AZStd::holds_alternative<FileRequest::ReadRequestData>(request->GetCommand()))
|
||||
{
|
||||
auto& readRequest = AZStd::get<FileRequest::ReadRequestData>(request->GetCommand());
|
||||
|
||||
FileRequest* read = m_context->GetNewInternalRequest();
|
||||
read->CreateRead(request, readRequest.m_output, readRequest.m_outputSize, readRequest.m_path,
|
||||
readRequest.m_offset, readRequest.m_size);
|
||||
m_context->PushPreparedRequest(read);
|
||||
return;
|
||||
}
|
||||
StreamStackEntry::PrepareRequest(request);
|
||||
}
|
||||
|
||||
void StorageDrive::QueueRequest(FileRequest* request)
|
||||
{
|
||||
AZ_Assert(request, "QueueRequest was provided a null request.");
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData> ||
|
||||
AZStd::is_same_v<Command, FileRequest::FileExistsCheckData> ||
|
||||
AZStd::is_same_v<Command, FileRequest::FileMetaDataRetrievalData>)
|
||||
{
|
||||
serializeContext->Class<StorageDriveConfig, IStreamerStackConfig>()
|
||||
->Version(1)
|
||||
->Field("MaxFileHandles", &StorageDriveConfig::m_maxFileHandles);
|
||||
}
|
||||
}
|
||||
|
||||
const AZStd::chrono::microseconds StorageDrive::s_averageSeekTime =
|
||||
AZStd::chrono::milliseconds(9) + // Common average seek time for desktop hdd drives.
|
||||
AZStd::chrono::milliseconds(3); // Rotational latency for a 7200RPM disk
|
||||
|
||||
StorageDrive::StorageDrive(u32 maxFileHandles)
|
||||
: StreamStackEntry("Storage drive (generic)")
|
||||
{
|
||||
m_fileLastUsed.resize(maxFileHandles, AZStd::chrono::system_clock::time_point::min());
|
||||
m_filePaths.resize(maxFileHandles);
|
||||
m_fileHandles.resize(maxFileHandles);
|
||||
|
||||
// Add initial dummy values to the stats to avoid division by zero later on and avoid needing branches.
|
||||
m_readSizeAverage.PushEntry(1);
|
||||
m_readTimeAverage.PushEntry(AZStd::chrono::microseconds(1));
|
||||
}
|
||||
|
||||
void StorageDrive::SetNext(AZStd::shared_ptr<StreamStackEntry> /*next*/)
|
||||
{
|
||||
AZ_Assert(false, "StorageDrive isn't allowed to have a node to forward requests to.");
|
||||
}
|
||||
|
||||
void StorageDrive::PrepareRequest(FileRequest* request)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
AZ_Assert(request, "PrepareRequest was provided a null request.");
|
||||
|
||||
if (AZStd::holds_alternative<FileRequest::ReadRequestData>(request->GetCommand()))
|
||||
{
|
||||
auto& readRequest = AZStd::get<FileRequest::ReadRequestData>(request->GetCommand());
|
||||
|
||||
FileRequest* read = m_context->GetNewInternalRequest();
|
||||
read->CreateRead(request, readRequest.m_output, readRequest.m_outputSize, readRequest.m_path,
|
||||
readRequest.m_offset, readRequest.m_size);
|
||||
m_context->PushPreparedRequest(read);
|
||||
m_pendingRequests.push_back(request);
|
||||
return;
|
||||
}
|
||||
StreamStackEntry::PrepareRequest(request);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CancelData>)
|
||||
{
|
||||
CancelRequest(request, args.m_target);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::FlushData>)
|
||||
{
|
||||
FlushCache(args.m_path);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FlushAllData>)
|
||||
{
|
||||
FlushEntireCache();
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::ReportData>)
|
||||
{
|
||||
Report(args);
|
||||
}
|
||||
StreamStackEntry::QueueRequest(request);
|
||||
}
|
||||
}, request->GetCommand());
|
||||
}
|
||||
|
||||
void StorageDrive::QueueRequest(FileRequest* request)
|
||||
bool StorageDrive::ExecuteRequests()
|
||||
{
|
||||
if (!m_pendingRequests.empty())
|
||||
{
|
||||
AZ_Assert(request, "QueueRequest was provided a null request.");
|
||||
FileRequest* request = m_pendingRequests.front();
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData> ||
|
||||
AZStd::is_same_v<Command, FileRequest::FileExistsCheckData> ||
|
||||
AZStd::is_same_v<Command, FileRequest::FileMetaDataRetrievalData>)
|
||||
{
|
||||
m_pendingRequests.push_back(request);
|
||||
return;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CancelData>)
|
||||
{
|
||||
CancelRequest(request, args.m_target);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::FlushData>)
|
||||
{
|
||||
FlushCache(args.m_path);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FlushAllData>)
|
||||
{
|
||||
FlushEntireCache();
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::ReportData>)
|
||||
{
|
||||
Report(args);
|
||||
}
|
||||
StreamStackEntry::QueueRequest(request);
|
||||
}
|
||||
}, request->GetCommand());
|
||||
}
|
||||
|
||||
bool StorageDrive::ExecuteRequests()
|
||||
{
|
||||
if (!m_pendingRequests.empty())
|
||||
{
|
||||
FileRequest* request = m_pendingRequests.front();
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
{
|
||||
ReadFile(request);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FileExistsCheckData>)
|
||||
{
|
||||
FileExistsRequest(request);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FileMetaDataRetrievalData>)
|
||||
{
|
||||
FileMetaDataRetrievalRequest(request);
|
||||
}
|
||||
}, request->GetCommand());
|
||||
m_pendingRequests.pop_front();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void StorageDrive::UpdateStatus(Status& status) const
|
||||
{
|
||||
// Only participate if there are actually any reads done.
|
||||
if (m_fileOpenCloseTimeAverage.GetNumRecorded() > 0)
|
||||
{
|
||||
s32 availableSlots = s_maxRequests - aznumeric_cast<s32>(m_pendingRequests.size());
|
||||
StreamStackEntry::UpdateStatus(status);
|
||||
status.m_numAvailableSlots = AZStd::min(status.m_numAvailableSlots, availableSlots);
|
||||
status.m_isIdle = status.m_isIdle && m_pendingRequests.empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
status.m_numAvailableSlots = AZStd::min(status.m_numAvailableSlots, s_maxRequests);
|
||||
}
|
||||
}
|
||||
|
||||
void StorageDrive::UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now,
|
||||
AZStd::vector<FileRequest*>& internalPending, StreamerContext::PreparedQueue::iterator pendingBegin,
|
||||
StreamerContext::PreparedQueue::iterator pendingEnd)
|
||||
{
|
||||
StreamStackEntry::UpdateCompletionEstimates(now, internalPending, pendingBegin, pendingEnd);
|
||||
|
||||
const RequestPath* activeFile = nullptr;
|
||||
if (m_activeCacheSlot != s_fileNotFound)
|
||||
{
|
||||
activeFile = &m_filePaths[m_activeCacheSlot];
|
||||
}
|
||||
u64 activeOffset = m_activeOffset;
|
||||
|
||||
// Estimate requests in this stack entry.
|
||||
for (FileRequest* request : m_pendingRequests)
|
||||
{
|
||||
EstimateCompletionTimeForRequest(request, now, activeFile, activeOffset);
|
||||
}
|
||||
|
||||
// Estimate internally pending requests. Because this call will go from the top of the stack to the bottom,
|
||||
// but estimation is calculated from the bottom to the top, this list should be processed in reverse order.
|
||||
for (auto requestIt = internalPending.rbegin(); requestIt != internalPending.rend(); ++requestIt)
|
||||
{
|
||||
EstimateCompletionTimeForRequest(*requestIt, now, activeFile, activeOffset);
|
||||
}
|
||||
|
||||
// Estimate pending requests that have not been queued yet.
|
||||
for (auto requestIt = pendingBegin; requestIt != pendingEnd; ++requestIt)
|
||||
{
|
||||
EstimateCompletionTimeForRequest(*requestIt, now, activeFile, activeOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void StorageDrive::EstimateCompletionTimeForRequest(FileRequest* request, AZStd::chrono::system_clock::time_point& startTime,
|
||||
const RequestPath*& activeFile, u64& activeOffset) const
|
||||
{
|
||||
u64 readSize = 0;
|
||||
u64 offset = 0;
|
||||
const RequestPath* targetFile = nullptr;
|
||||
|
||||
AZStd::visit([&](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
{
|
||||
targetFile = &args.m_path;
|
||||
readSize = args.m_size;
|
||||
offset = args.m_offset;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CompressedReadData>)
|
||||
{
|
||||
targetFile = &args.m_compressionInfo.m_archiveFilename;
|
||||
readSize = args.m_compressionInfo.m_compressedSize;
|
||||
offset = args.m_compressionInfo.m_offset;
|
||||
ReadFile(request);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FileExistsCheckData>)
|
||||
{
|
||||
readSize = 0;
|
||||
AZStd::chrono::microseconds averageTime = m_getFileExistsTimeAverage.CalculateAverage();
|
||||
startTime += averageTime;
|
||||
FileExistsRequest(request);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FileMetaDataRetrievalData>)
|
||||
{
|
||||
readSize = 0;
|
||||
AZStd::chrono::microseconds averageTime = m_getFileMetaDataTimeAverage.CalculateAverage();
|
||||
startTime += averageTime;
|
||||
FileMetaDataRetrievalRequest(request);
|
||||
}
|
||||
}, request->GetCommand());
|
||||
m_pendingRequests.pop_front();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (readSize > 0)
|
||||
{
|
||||
if (activeFile && activeFile != targetFile)
|
||||
{
|
||||
if (FindFileInCache(*targetFile) == s_fileNotFound)
|
||||
{
|
||||
AZStd::chrono::microseconds fileOpenCloseTimeAverage = m_fileOpenCloseTimeAverage.CalculateAverage();
|
||||
startTime += fileOpenCloseTimeAverage;
|
||||
}
|
||||
startTime += s_averageSeekTime;
|
||||
activeOffset = std::numeric_limits<u64>::max();
|
||||
}
|
||||
else if (activeOffset != offset)
|
||||
{
|
||||
startTime += s_averageSeekTime;
|
||||
}
|
||||
void StorageDrive::UpdateStatus(Status& status) const
|
||||
{
|
||||
// Only participate if there are actually any reads done.
|
||||
if (m_fileOpenCloseTimeAverage.GetNumRecorded() > 0)
|
||||
{
|
||||
s32 availableSlots = s_maxRequests - aznumeric_cast<s32>(m_pendingRequests.size());
|
||||
StreamStackEntry::UpdateStatus(status);
|
||||
status.m_numAvailableSlots = AZStd::min(status.m_numAvailableSlots, availableSlots);
|
||||
status.m_isIdle = status.m_isIdle && m_pendingRequests.empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
status.m_numAvailableSlots = AZStd::min(status.m_numAvailableSlots, s_maxRequests);
|
||||
}
|
||||
}
|
||||
|
||||
u64 totalBytesRead = m_readSizeAverage.GetTotal();
|
||||
double totalReadTimeUSec = aznumeric_caster(m_readTimeAverage.GetTotal().count());
|
||||
startTime += AZStd::chrono::microseconds(aznumeric_cast<u64>((readSize * totalReadTimeUSec) / totalBytesRead));
|
||||
activeOffset = offset + readSize;
|
||||
}
|
||||
request->SetEstimatedCompletion(startTime);
|
||||
void StorageDrive::UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now,
|
||||
AZStd::vector<FileRequest*>& internalPending, StreamerContext::PreparedQueue::iterator pendingBegin,
|
||||
StreamerContext::PreparedQueue::iterator pendingEnd)
|
||||
{
|
||||
StreamStackEntry::UpdateCompletionEstimates(now, internalPending, pendingBegin, pendingEnd);
|
||||
|
||||
const RequestPath* activeFile = nullptr;
|
||||
if (m_activeCacheSlot != s_fileNotFound)
|
||||
{
|
||||
activeFile = &m_filePaths[m_activeCacheSlot];
|
||||
}
|
||||
u64 activeOffset = m_activeOffset;
|
||||
|
||||
// Estimate requests in this stack entry.
|
||||
for (FileRequest* request : m_pendingRequests)
|
||||
{
|
||||
EstimateCompletionTimeForRequest(request, now, activeFile, activeOffset);
|
||||
}
|
||||
|
||||
void StorageDrive::ReadFile(FileRequest* request)
|
||||
// Estimate internally pending requests. Because this call will go from the top of the stack to the bottom,
|
||||
// but estimation is calculated from the bottom to the top, this list should be processed in reverse order.
|
||||
for (auto requestIt = internalPending.rbegin(); requestIt != internalPending.rend(); ++requestIt)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&request->GetCommand());
|
||||
AZ_Assert(data, "FileRequest queued on StorageDrive to be read didn't contain read data.");
|
||||
|
||||
SystemFile* file = nullptr;
|
||||
|
||||
// If the file is already open, use that file handle and update it's last touched time.
|
||||
size_t cacheIndex = FindFileInCache(data->m_path);
|
||||
if (cacheIndex != s_fileNotFound)
|
||||
{
|
||||
file = m_fileHandles[cacheIndex].get();
|
||||
m_fileLastUsed[cacheIndex] = AZStd::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
// If the file is not open, eject the entry from the cache that hasn't been used for the longest time
|
||||
// and open the file for reading.
|
||||
if (!file)
|
||||
{
|
||||
AZStd::chrono::system_clock::time_point oldest = m_fileLastUsed[0];
|
||||
cacheIndex = 0;
|
||||
size_t numFiles = m_filePaths.size();
|
||||
for (size_t i = 1; i < numFiles; ++i)
|
||||
{
|
||||
if (m_fileLastUsed[i] < oldest)
|
||||
{
|
||||
oldest = m_fileLastUsed[i];
|
||||
cacheIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
TIMED_AVERAGE_WINDOW_SCOPE(m_fileOpenCloseTimeAverage);
|
||||
AZStd::unique_ptr<SystemFile> newFile = AZStd::make_unique<SystemFile>();
|
||||
bool isOpen = newFile->Open(data->m_path.GetAbsolutePath(), SystemFile::OpenMode::SF_OPEN_READ_ONLY);
|
||||
if (!isOpen)
|
||||
{
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Failed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
return;
|
||||
}
|
||||
|
||||
file = newFile.get();
|
||||
m_fileLastUsed[cacheIndex] = AZStd::chrono::high_resolution_clock::now();
|
||||
m_fileHandles[cacheIndex] = AZStd::move(newFile);
|
||||
m_filePaths[cacheIndex] = data->m_path;
|
||||
}
|
||||
|
||||
AZ_Assert(file, "While searching for file '%s' StorageDevice::ReadFile failed to detect a problem.", data->m_path.GetRelativePath());
|
||||
u64 bytesRead = 0;
|
||||
{
|
||||
TIMED_AVERAGE_WINDOW_SCOPE(m_readTimeAverage);
|
||||
if (file->Tell() != data->m_offset)
|
||||
{
|
||||
file->Seek(data->m_offset, SystemFile::SeekMode::SF_SEEK_BEGIN);
|
||||
}
|
||||
bytesRead = file->Read(data->m_size, data->m_output);
|
||||
}
|
||||
m_readSizeAverage.PushEntry(bytesRead);
|
||||
|
||||
m_activeCacheSlot = cacheIndex;
|
||||
m_activeOffset = data->m_offset + bytesRead;
|
||||
|
||||
request->SetStatus(bytesRead == data->m_size ? IStreamerTypes::RequestStatus::Completed : IStreamerTypes::RequestStatus::Failed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
EstimateCompletionTimeForRequest(*requestIt, now, activeFile, activeOffset);
|
||||
}
|
||||
|
||||
void StorageDrive::CancelRequest(FileRequest* cancelRequest, FileRequestPtr& target)
|
||||
// Estimate pending requests that have not been queued yet.
|
||||
for (auto requestIt = pendingBegin; requestIt != pendingEnd; ++requestIt)
|
||||
{
|
||||
for (auto it = m_pendingRequests.begin(); it != m_pendingRequests.end();)
|
||||
EstimateCompletionTimeForRequest(*requestIt, now, activeFile, activeOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void StorageDrive::EstimateCompletionTimeForRequest(FileRequest* request, AZStd::chrono::system_clock::time_point& startTime,
|
||||
const RequestPath*& activeFile, u64& activeOffset) const
|
||||
{
|
||||
u64 readSize = 0;
|
||||
u64 offset = 0;
|
||||
const RequestPath* targetFile = nullptr;
|
||||
|
||||
AZStd::visit([&](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
{
|
||||
if ((*it)->WorksOn(target))
|
||||
{
|
||||
(*it)->SetStatus(IStreamerTypes::RequestStatus::Canceled);
|
||||
m_context->MarkRequestAsCompleted(*it);
|
||||
it = m_pendingRequests.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
targetFile = &args.m_path;
|
||||
readSize = args.m_size;
|
||||
offset = args.m_offset;
|
||||
}
|
||||
cancelRequest->SetStatus(IStreamerTypes::RequestStatus::Completed);
|
||||
m_context->MarkRequestAsCompleted(cancelRequest);
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CompressedReadData>)
|
||||
{
|
||||
targetFile = &args.m_compressionInfo.m_archiveFilename;
|
||||
readSize = args.m_compressionInfo.m_compressedSize;
|
||||
offset = args.m_compressionInfo.m_offset;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FileExistsCheckData>)
|
||||
{
|
||||
readSize = 0;
|
||||
AZStd::chrono::microseconds averageTime = m_getFileExistsTimeAverage.CalculateAverage();
|
||||
startTime += averageTime;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FileMetaDataRetrievalData>)
|
||||
{
|
||||
readSize = 0;
|
||||
AZStd::chrono::microseconds averageTime = m_getFileMetaDataTimeAverage.CalculateAverage();
|
||||
startTime += averageTime;
|
||||
}
|
||||
}, request->GetCommand());
|
||||
|
||||
if (readSize > 0)
|
||||
{
|
||||
if (activeFile && activeFile != targetFile)
|
||||
{
|
||||
if (FindFileInCache(*targetFile) == s_fileNotFound)
|
||||
{
|
||||
AZStd::chrono::microseconds fileOpenCloseTimeAverage = m_fileOpenCloseTimeAverage.CalculateAverage();
|
||||
startTime += fileOpenCloseTimeAverage;
|
||||
}
|
||||
startTime += s_averageSeekTime;
|
||||
activeOffset = std::numeric_limits<u64>::max();
|
||||
}
|
||||
else if (activeOffset != offset)
|
||||
{
|
||||
startTime += s_averageSeekTime;
|
||||
}
|
||||
|
||||
u64 totalBytesRead = m_readSizeAverage.GetTotal();
|
||||
double totalReadTimeUSec = aznumeric_caster(m_readTimeAverage.GetTotal().count());
|
||||
startTime += AZStd::chrono::microseconds(aznumeric_cast<u64>((readSize * totalReadTimeUSec) / totalBytesRead));
|
||||
activeOffset = offset + readSize;
|
||||
}
|
||||
request->SetEstimatedCompletion(startTime);
|
||||
}
|
||||
|
||||
void StorageDrive::ReadFile(FileRequest* request)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&request->GetCommand());
|
||||
AZ_Assert(data, "FileRequest queued on StorageDrive to be read didn't contain read data.");
|
||||
|
||||
SystemFile* file = nullptr;
|
||||
|
||||
// If the file is already open, use that file handle and update it's last touched time.
|
||||
size_t cacheIndex = FindFileInCache(data->m_path);
|
||||
if (cacheIndex != s_fileNotFound)
|
||||
{
|
||||
file = m_fileHandles[cacheIndex].get();
|
||||
m_fileLastUsed[cacheIndex] = AZStd::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void StorageDrive::FileExistsRequest(FileRequest* request)
|
||||
// If the file is not open, eject the entry from the cache that hasn't been used for the longest time
|
||||
// and open the file for reading.
|
||||
if (!file)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
TIMED_AVERAGE_WINDOW_SCOPE(m_getFileExistsTimeAverage);
|
||||
|
||||
auto& fileExists = AZStd::get<FileRequest::FileExistsCheckData>(request->GetCommand());
|
||||
size_t cacheIndex = FindFileInCache(fileExists.m_path);
|
||||
if (cacheIndex != s_fileNotFound)
|
||||
AZStd::chrono::system_clock::time_point oldest = m_fileLastUsed[0];
|
||||
cacheIndex = 0;
|
||||
size_t numFiles = m_filePaths.size();
|
||||
for (size_t i = 1; i < numFiles; ++i)
|
||||
{
|
||||
fileExists.m_found = true;
|
||||
if (m_fileLastUsed[i] < oldest)
|
||||
{
|
||||
oldest = m_fileLastUsed[i];
|
||||
cacheIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
TIMED_AVERAGE_WINDOW_SCOPE(m_fileOpenCloseTimeAverage);
|
||||
AZStd::unique_ptr<SystemFile> newFile = AZStd::make_unique<SystemFile>();
|
||||
bool isOpen = newFile->Open(data->m_path.GetAbsolutePath(), SystemFile::OpenMode::SF_OPEN_READ_ONLY);
|
||||
if (!isOpen)
|
||||
{
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Failed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
return;
|
||||
}
|
||||
|
||||
file = newFile.get();
|
||||
m_fileLastUsed[cacheIndex] = AZStd::chrono::high_resolution_clock::now();
|
||||
m_fileHandles[cacheIndex] = AZStd::move(newFile);
|
||||
m_filePaths[cacheIndex] = data->m_path;
|
||||
}
|
||||
|
||||
AZ_Assert(file, "While searching for file '%s' StorageDevice::ReadFile failed to detect a problem.", data->m_path.GetRelativePath());
|
||||
u64 bytesRead = 0;
|
||||
{
|
||||
TIMED_AVERAGE_WINDOW_SCOPE(m_readTimeAverage);
|
||||
if (file->Tell() != data->m_offset)
|
||||
{
|
||||
file->Seek(data->m_offset, SystemFile::SeekMode::SF_SEEK_BEGIN);
|
||||
}
|
||||
bytesRead = file->Read(data->m_size, data->m_output);
|
||||
}
|
||||
m_readSizeAverage.PushEntry(bytesRead);
|
||||
|
||||
m_activeCacheSlot = cacheIndex;
|
||||
m_activeOffset = data->m_offset + bytesRead;
|
||||
|
||||
request->SetStatus(bytesRead == data->m_size ? IStreamerTypes::RequestStatus::Completed : IStreamerTypes::RequestStatus::Failed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
}
|
||||
|
||||
void StorageDrive::CancelRequest(FileRequest* cancelRequest, FileRequestPtr& target)
|
||||
{
|
||||
for (auto it = m_pendingRequests.begin(); it != m_pendingRequests.end();)
|
||||
{
|
||||
if ((*it)->WorksOn(target))
|
||||
{
|
||||
(*it)->SetStatus(IStreamerTypes::RequestStatus::Canceled);
|
||||
m_context->MarkRequestAsCompleted(*it);
|
||||
it = m_pendingRequests.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileExists.m_found = SystemFile::Exists(fileExists.m_path.GetAbsolutePath());
|
||||
++it;
|
||||
}
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
}
|
||||
cancelRequest->SetStatus(IStreamerTypes::RequestStatus::Completed);
|
||||
m_context->MarkRequestAsCompleted(cancelRequest);
|
||||
}
|
||||
|
||||
void StorageDrive::FileMetaDataRetrievalRequest(FileRequest* request)
|
||||
void StorageDrive::FileExistsRequest(FileRequest* request)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
TIMED_AVERAGE_WINDOW_SCOPE(m_getFileExistsTimeAverage);
|
||||
|
||||
auto& fileExists = AZStd::get<FileRequest::FileExistsCheckData>(request->GetCommand());
|
||||
size_t cacheIndex = FindFileInCache(fileExists.m_path);
|
||||
if (cacheIndex != s_fileNotFound)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
TIMED_AVERAGE_WINDOW_SCOPE(m_getFileMetaDataTimeAverage);
|
||||
fileExists.m_found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fileExists.m_found = SystemFile::Exists(fileExists.m_path.GetAbsolutePath());
|
||||
}
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
}
|
||||
|
||||
auto& command = AZStd::get<FileRequest::FileMetaDataRetrievalData>(request->GetCommand());
|
||||
// If the file is already open, use the file handle which usually is cheaper than asking for the file by name.
|
||||
size_t cacheIndex = FindFileInCache(command.m_path);
|
||||
if (cacheIndex != s_fileNotFound)
|
||||
void StorageDrive::FileMetaDataRetrievalRequest(FileRequest* request)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
TIMED_AVERAGE_WINDOW_SCOPE(m_getFileMetaDataTimeAverage);
|
||||
|
||||
auto& command = AZStd::get<FileRequest::FileMetaDataRetrievalData>(request->GetCommand());
|
||||
// If the file is already open, use the file handle which usually is cheaper than asking for the file by name.
|
||||
size_t cacheIndex = FindFileInCache(command.m_path);
|
||||
if (cacheIndex != s_fileNotFound)
|
||||
{
|
||||
AZ_Assert(m_fileHandles[cacheIndex],
|
||||
"File path '%s' doesn't have an associated file handle.", m_filePaths[cacheIndex].GetRelativePath());
|
||||
command.m_fileSize = m_fileHandles[cacheIndex]->Length();
|
||||
command.m_found = true;
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Completed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The file is not open yet, so try to get the file size by name.
|
||||
u64 size = SystemFile::Length(command.m_path.GetAbsolutePath());
|
||||
if (size != 0) // SystemFile::Length doesn't allow telling a zero-sized file apart from a invalid path.
|
||||
{
|
||||
AZ_Assert(m_fileHandles[cacheIndex],
|
||||
"File path '%s' doesn't have an associated file handle.", m_filePaths[cacheIndex].GetRelativePath());
|
||||
command.m_fileSize = m_fileHandles[cacheIndex]->Length();
|
||||
command.m_fileSize = size;
|
||||
command.m_found = true;
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Completed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The file is not open yet, so try to get the file size by name.
|
||||
u64 size = SystemFile::Length(command.m_path.GetAbsolutePath());
|
||||
if (size != 0) // SystemFile::Length doesn't allow telling a zero-sized file apart from a invalid path.
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Failed);
|
||||
}
|
||||
}
|
||||
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
}
|
||||
|
||||
void StorageDrive::FlushCache(const RequestPath& filePath)
|
||||
{
|
||||
size_t cacheIndex = FindFileInCache(filePath);
|
||||
if (cacheIndex != s_fileNotFound)
|
||||
{
|
||||
m_fileLastUsed[cacheIndex] = AZStd::chrono::system_clock::time_point();
|
||||
m_fileHandles[cacheIndex].reset();
|
||||
m_filePaths[cacheIndex].Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void StorageDrive::FlushEntireCache()
|
||||
{
|
||||
size_t numFiles = m_filePaths.size();
|
||||
for (size_t i = 0; i < numFiles; ++i)
|
||||
{
|
||||
m_fileLastUsed[i] = AZStd::chrono::system_clock::time_point();
|
||||
m_fileHandles[i].reset();
|
||||
m_filePaths[i].Clear();
|
||||
}
|
||||
}
|
||||
|
||||
size_t StorageDrive::FindFileInCache(const RequestPath& filePath) const
|
||||
{
|
||||
size_t numFiles = m_filePaths.size();
|
||||
for (size_t i = 0; i < numFiles; ++i)
|
||||
{
|
||||
if (m_filePaths[i] == filePath)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return s_fileNotFound;
|
||||
}
|
||||
|
||||
void StorageDrive::CollectStatistics(AZStd::vector<Statistic>& statistics) const
|
||||
{
|
||||
constexpr double bytesToMB = (1024.0 * 1024.0);
|
||||
using DoubleSeconds = AZStd::chrono::duration<double>;
|
||||
|
||||
double totalBytesReadMB = m_readSizeAverage.GetTotal() / bytesToMB;
|
||||
double totalReadTimeSec = AZStd::chrono::duration_cast<DoubleSeconds>(m_readTimeAverage.GetTotal()).count();
|
||||
if (m_readSizeAverage.GetTotal() > 1) // A default value is always added.
|
||||
{
|
||||
statistics.push_back(Statistic::CreateFloat(m_name, "Read Speed (avg. mbps)", totalBytesReadMB / totalReadTimeSec));
|
||||
}
|
||||
|
||||
if (m_fileOpenCloseTimeAverage.GetNumRecorded() > 0)
|
||||
{
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, "File Open & Close (avg. us)", m_fileOpenCloseTimeAverage.CalculateAverage().count()));
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, "Get file exists (avg. us)", m_getFileExistsTimeAverage.CalculateAverage().count()));
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, "Get file meta data (avg. us)", m_getFileMetaDataTimeAverage.CalculateAverage().count()));
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, "Available slots", s64{ s_maxRequests } - m_pendingRequests.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void StorageDrive::Report(const FileRequest::ReportData& data) const
|
||||
{
|
||||
switch (data.m_reportType)
|
||||
{
|
||||
case FileRequest::ReportData::ReportType::FileLocks:
|
||||
for (u32 i = 0; i < m_fileHandles.size(); ++i)
|
||||
{
|
||||
if (m_fileHandles[i] != nullptr)
|
||||
{
|
||||
command.m_fileSize = size;
|
||||
command.m_found = true;
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Completed);
|
||||
}
|
||||
else
|
||||
{
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Failed);
|
||||
AZ_Printf("Streamer", "File lock in %s : '%s'.\n", m_name.c_str(), m_filePaths[i].GetRelativePath());
|
||||
}
|
||||
}
|
||||
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
void StorageDrive::FlushCache(const RequestPath& filePath)
|
||||
{
|
||||
size_t cacheIndex = FindFileInCache(filePath);
|
||||
if (cacheIndex != s_fileNotFound)
|
||||
{
|
||||
m_fileLastUsed[cacheIndex] = AZStd::chrono::system_clock::time_point();
|
||||
m_fileHandles[cacheIndex].reset();
|
||||
m_filePaths[cacheIndex].Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void StorageDrive::FlushEntireCache()
|
||||
{
|
||||
size_t numFiles = m_filePaths.size();
|
||||
for (size_t i = 0; i < numFiles; ++i)
|
||||
{
|
||||
m_fileLastUsed[i] = AZStd::chrono::system_clock::time_point();
|
||||
m_fileHandles[i].reset();
|
||||
m_filePaths[i].Clear();
|
||||
}
|
||||
}
|
||||
|
||||
size_t StorageDrive::FindFileInCache(const RequestPath& filePath) const
|
||||
{
|
||||
size_t numFiles = m_filePaths.size();
|
||||
for (size_t i = 0; i < numFiles; ++i)
|
||||
{
|
||||
if (m_filePaths[i] == filePath)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return s_fileNotFound;
|
||||
}
|
||||
|
||||
void StorageDrive::CollectStatistics(AZStd::vector<Statistic>& statistics) const
|
||||
{
|
||||
constexpr double bytesToMB = (1024.0 * 1024.0);
|
||||
using DoubleSeconds = AZStd::chrono::duration<double>;
|
||||
|
||||
double totalBytesReadMB = m_readSizeAverage.GetTotal() / bytesToMB;
|
||||
double totalReadTimeSec = AZStd::chrono::duration_cast<DoubleSeconds>(m_readTimeAverage.GetTotal()).count();
|
||||
if (m_readSizeAverage.GetTotal() > 1) // A default value is always added.
|
||||
{
|
||||
statistics.push_back(Statistic::CreateFloat(m_name, "Read Speed (avg. mbps)", totalBytesReadMB / totalReadTimeSec));
|
||||
}
|
||||
|
||||
if (m_fileOpenCloseTimeAverage.GetNumRecorded() > 0)
|
||||
{
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, "File Open & Close (avg. us)", m_fileOpenCloseTimeAverage.CalculateAverage().count()));
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, "Get file exists (avg. us)", m_getFileExistsTimeAverage.CalculateAverage().count()));
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, "Get file meta data (avg. us)", m_getFileMetaDataTimeAverage.CalculateAverage().count()));
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, "Available slots", s64{ s_maxRequests } - m_pendingRequests.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void StorageDrive::Report(const FileRequest::ReportData& data) const
|
||||
{
|
||||
switch (data.m_reportType)
|
||||
{
|
||||
case FileRequest::ReportData::ReportType::FileLocks:
|
||||
for (u32 i = 0; i < m_fileHandles.size(); ++i)
|
||||
{
|
||||
if (m_fileHandles[i] != nullptr)
|
||||
{
|
||||
AZ_Printf("Streamer", "File lock in %s : '%s'.\n", m_name.c_str(), m_filePaths[i].GetRelativePath());
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -16,85 +16,82 @@
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/chrono/clocks.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
struct StorageDriveConfig final :
|
||||
public IStreamerStackConfig
|
||||
{
|
||||
struct StorageDriveConfig final :
|
||||
public IStreamerStackConfig
|
||||
{
|
||||
AZ_RTTI(AZ::IO::StorageDriveConfig, "{3D568902-6C09-4E9E-A4DB-8B561481D298}", IStreamerStackConfig);
|
||||
AZ_CLASS_ALLOCATOR(StorageDriveConfig, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(AZ::IO::StorageDriveConfig, "{3D568902-6C09-4E9E-A4DB-8B561481D298}", IStreamerStackConfig);
|
||||
AZ_CLASS_ALLOCATOR(StorageDriveConfig, AZ::SystemAllocator, 0);
|
||||
|
||||
~StorageDriveConfig() override = default;
|
||||
AZStd::shared_ptr<StreamStackEntry> AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent) override;
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
~StorageDriveConfig() override = default;
|
||||
AZStd::shared_ptr<StreamStackEntry> AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent) override;
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
u32 m_maxFileHandles{1024};
|
||||
};
|
||||
u32 m_maxFileHandles{1024};
|
||||
};
|
||||
|
||||
//! Platform agnostic version of a storage drive, such as hdd, ssd, dvd, etc.
|
||||
//! This stream stack entry is responsible for accessing a storage drive to
|
||||
//! retrieve file information and data.
|
||||
//! This entry is designed as a catch-all for any reads that weren't handled
|
||||
//! by platform specific implementations or the virtual file system. It should
|
||||
//! by the last entry in the stack as it will not forward calls to the next entry.
|
||||
class StorageDrive
|
||||
: public StreamStackEntry
|
||||
{
|
||||
public:
|
||||
explicit StorageDrive(u32 maxFileHandles);
|
||||
~StorageDrive() override = default;
|
||||
//! Platform agnostic version of a storage drive, such as hdd, ssd, dvd, etc.
|
||||
//! This stream stack entry is responsible for accessing a storage drive to
|
||||
//! retrieve file information and data.
|
||||
//! This entry is designed as a catch-all for any reads that weren't handled
|
||||
//! by platform specific implementations or the virtual file system. It should
|
||||
//! by the last entry in the stack as it will not forward calls to the next entry.
|
||||
class StorageDrive
|
||||
: public StreamStackEntry
|
||||
{
|
||||
public:
|
||||
explicit StorageDrive(u32 maxFileHandles);
|
||||
~StorageDrive() override = default;
|
||||
|
||||
void SetNext(AZStd::shared_ptr<StreamStackEntry> next) override;
|
||||
void SetNext(AZStd::shared_ptr<StreamStackEntry> next) override;
|
||||
|
||||
void PrepareRequest(FileRequest* request) override;
|
||||
void QueueRequest(FileRequest* request) override;
|
||||
bool ExecuteRequests() override;
|
||||
void PrepareRequest(FileRequest* request) override;
|
||||
void QueueRequest(FileRequest* request) override;
|
||||
bool ExecuteRequests() override;
|
||||
|
||||
void UpdateStatus(Status& status) const override;
|
||||
void UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now, AZStd::vector<FileRequest*>& internalPending,
|
||||
StreamerContext::PreparedQueue::iterator pendingBegin, StreamerContext::PreparedQueue::iterator pendingEnd) override;
|
||||
void UpdateStatus(Status& status) const override;
|
||||
void UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now, AZStd::vector<FileRequest*>& internalPending,
|
||||
StreamerContext::PreparedQueue::iterator pendingBegin, StreamerContext::PreparedQueue::iterator pendingEnd) override;
|
||||
|
||||
void CollectStatistics(AZStd::vector<Statistic>& statistics) const override;
|
||||
void CollectStatistics(AZStd::vector<Statistic>& statistics) const override;
|
||||
|
||||
protected:
|
||||
static const AZStd::chrono::microseconds s_averageSeekTime;
|
||||
static constexpr s32 s_maxRequests = 1;
|
||||
protected:
|
||||
static const AZStd::chrono::microseconds s_averageSeekTime;
|
||||
static constexpr s32 s_maxRequests = 1;
|
||||
|
||||
size_t FindFileInCache(const RequestPath& filePath) const;
|
||||
void ReadFile(FileRequest* request);
|
||||
void CancelRequest(FileRequest* cancelRequest, FileRequestPtr& target);
|
||||
void FileExistsRequest(FileRequest* request);
|
||||
void FileMetaDataRetrievalRequest(FileRequest* request);
|
||||
void FlushCache(const RequestPath& filePath);
|
||||
void FlushEntireCache();
|
||||
size_t FindFileInCache(const RequestPath& filePath) const;
|
||||
void ReadFile(FileRequest* request);
|
||||
void CancelRequest(FileRequest* cancelRequest, FileRequestPtr& target);
|
||||
void FileExistsRequest(FileRequest* request);
|
||||
void FileMetaDataRetrievalRequest(FileRequest* request);
|
||||
void FlushCache(const RequestPath& filePath);
|
||||
void FlushEntireCache();
|
||||
|
||||
void EstimateCompletionTimeForRequest(FileRequest* request, AZStd::chrono::system_clock::time_point& startTime,
|
||||
const RequestPath*& activeFile, u64& activeOffset) const;
|
||||
void EstimateCompletionTimeForRequest(FileRequest* request, AZStd::chrono::system_clock::time_point& startTime,
|
||||
const RequestPath*& activeFile, u64& activeOffset) const;
|
||||
|
||||
void Report(const FileRequest::ReportData& data) const;
|
||||
void Report(const FileRequest::ReportData& data) const;
|
||||
|
||||
TimedAverageWindow<s_statisticsWindowSize> m_fileOpenCloseTimeAverage;
|
||||
TimedAverageWindow<s_statisticsWindowSize> m_getFileExistsTimeAverage;
|
||||
TimedAverageWindow<s_statisticsWindowSize> m_getFileMetaDataTimeAverage;
|
||||
TimedAverageWindow<s_statisticsWindowSize> m_readTimeAverage;
|
||||
AverageWindow<u64, float, s_statisticsWindowSize> m_readSizeAverage;
|
||||
//! File requests that are queued for processing.
|
||||
AZStd::deque<FileRequest*> m_pendingRequests;
|
||||
TimedAverageWindow<s_statisticsWindowSize> m_fileOpenCloseTimeAverage;
|
||||
TimedAverageWindow<s_statisticsWindowSize> m_getFileExistsTimeAverage;
|
||||
TimedAverageWindow<s_statisticsWindowSize> m_getFileMetaDataTimeAverage;
|
||||
TimedAverageWindow<s_statisticsWindowSize> m_readTimeAverage;
|
||||
AverageWindow<u64, float, s_statisticsWindowSize> m_readSizeAverage;
|
||||
//! File requests that are queued for processing.
|
||||
AZStd::deque<FileRequest*> m_pendingRequests;
|
||||
|
||||
//! The last time a file handle was used to access a file. The handle is stored in m_fileHandles.
|
||||
AZStd::vector<AZStd::chrono::system_clock::time_point> m_fileLastUsed;
|
||||
//! The file path to the file handle. The handle is stored in m_fileHandles.
|
||||
AZStd::vector<RequestPath> m_filePaths;
|
||||
//! A list of file handles that's being cached in case they're needed again in the future.
|
||||
AZStd::vector<AZStd::unique_ptr<SystemFile>> m_fileHandles;
|
||||
//! The last time a file handle was used to access a file. The handle is stored in m_fileHandles.
|
||||
AZStd::vector<AZStd::chrono::system_clock::time_point> m_fileLastUsed;
|
||||
//! The file path to the file handle. The handle is stored in m_fileHandles.
|
||||
AZStd::vector<RequestPath> m_filePaths;
|
||||
//! A list of file handles that's being cached in case they're needed again in the future.
|
||||
AZStd::vector<AZStd::unique_ptr<SystemFile>> m_fileHandles;
|
||||
|
||||
//! The offset into the file that's cached by the active cache slot.
|
||||
u64 m_activeOffset = 0;
|
||||
//! The index into m_fileHandles for the file that's currently being read.
|
||||
size_t m_activeCacheSlot = s_fileNotFound;
|
||||
};
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
//! The offset into the file that's cached by the active cache slot.
|
||||
u64 m_activeOffset = 0;
|
||||
//! The index into m_fileHandles for the file that's currently being read.
|
||||
size_t m_activeCacheSlot = s_fileNotFound;
|
||||
};
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -219,7 +219,7 @@ namespace AZ::IO
|
||||
{
|
||||
AZ_Assert(HasRequestCompleted(request), "Claiming memory from a read request that's still in progress. "
|
||||
"This can lead to crashing if data is still being streamed to the request's buffer.");
|
||||
// The caller has claimed the buffer and is now responsible for clearing it.
|
||||
// The caller has claimed the buffer and is now responsible for clearing it.
|
||||
readRequest->m_allocator->UnlockAllocator();
|
||||
readRequest->m_allocator = nullptr;
|
||||
}
|
||||
@@ -293,7 +293,7 @@ namespace AZ::IO
|
||||
request->m_request.CreateReport(reportType);
|
||||
return request;
|
||||
}
|
||||
|
||||
|
||||
Streamer::Streamer(const AZStd::thread_desc& threadDesc, AZStd::unique_ptr<Scheduler> streamStack)
|
||||
: m_streamStack(AZStd::move(streamStack))
|
||||
{
|
||||
|
||||
@@ -17,116 +17,113 @@
|
||||
#include <AzCore/std/containers/queue.h>
|
||||
#include <AzCore/Statistics/RunningStatistic.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
class StreamerContext
|
||||
{
|
||||
class StreamerContext
|
||||
{
|
||||
public:
|
||||
using PreparedQueue = AZStd::deque<FileRequest*>;
|
||||
public:
|
||||
using PreparedQueue = AZStd::deque<FileRequest*>;
|
||||
|
||||
~StreamerContext();
|
||||
~StreamerContext();
|
||||
|
||||
//! Gets a new file request, either by creating a new instance or
|
||||
//! picking one from the recycle bin. This version should only be used
|
||||
//! by nodes on the streaming stack as it's not thread safe, but faster.
|
||||
//! The scheduler will automatically recycle these requests.
|
||||
FileRequest* GetNewInternalRequest();
|
||||
//! Gets a new file request, either by creating a new instance or
|
||||
//! picking one from the recycle bin. This version is for use by
|
||||
//! any system outside the stream stack and is thread safe. Once the
|
||||
//! reference count in the request hits zero it will automatically be recycled.
|
||||
FileRequestPtr GetNewExternalRequest();
|
||||
//! Gets a batch of new file requests, either by creating new instances or
|
||||
//! picking from the recycle bin. This version is for use by
|
||||
//! any system outside the stream stack and is thread safe. The owner
|
||||
//! needs to manually recycle these requests once they're done. Requests
|
||||
//! with a reference count of zero will automatically be recycled.
|
||||
//! If multiple requests need to be create this is preferable as it only locks the
|
||||
//! recycle bin once.
|
||||
void GetNewExternalRequestBatch(AZStd::vector<FileRequestPtr>& requests, size_t count);
|
||||
//! Gets a new file request, either by creating a new instance or
|
||||
//! picking one from the recycle bin. This version should only be used
|
||||
//! by nodes on the streaming stack as it's not thread safe, but faster.
|
||||
//! The scheduler will automatically recycle these requests.
|
||||
FileRequest* GetNewInternalRequest();
|
||||
//! Gets a new file request, either by creating a new instance or
|
||||
//! picking one from the recycle bin. This version is for use by
|
||||
//! any system outside the stream stack and is thread safe. Once the
|
||||
//! reference count in the request hits zero it will automatically be recycled.
|
||||
FileRequestPtr GetNewExternalRequest();
|
||||
//! Gets a batch of new file requests, either by creating new instances or
|
||||
//! picking from the recycle bin. This version is for use by
|
||||
//! any system outside the stream stack and is thread safe. The owner
|
||||
//! needs to manually recycle these requests once they're done. Requests
|
||||
//! with a reference count of zero will automatically be recycled.
|
||||
//! If multiple requests need to be create this is preferable as it only locks the
|
||||
//! recycle bin once.
|
||||
void GetNewExternalRequestBatch(AZStd::vector<FileRequestPtr>& requests, size_t count);
|
||||
|
||||
//! Gets the number of prepared requests. Prepared requests are requests
|
||||
//! that are ready to be queued up for further processing.
|
||||
size_t GetNumPreparedRequests() const;
|
||||
//! Gets the next prepared request that should be queued. Prepared requests
|
||||
//! are requests that are ready to be queued up for further processing.
|
||||
FileRequest* PopPreparedRequest();
|
||||
//! Adds a prepared request for later queuing and processing.
|
||||
void PushPreparedRequest(FileRequest* request);
|
||||
//! Gets the prepared requests that are queued to be processed.
|
||||
PreparedQueue& GetPreparedRequests();
|
||||
//! Gets the prepared requests that are queued to be processed.
|
||||
const PreparedQueue& GetPreparedRequests() const;
|
||||
//! Gets the number of prepared requests. Prepared requests are requests
|
||||
//! that are ready to be queued up for further processing.
|
||||
size_t GetNumPreparedRequests() const;
|
||||
//! Gets the next prepared request that should be queued. Prepared requests
|
||||
//! are requests that are ready to be queued up for further processing.
|
||||
FileRequest* PopPreparedRequest();
|
||||
//! Adds a prepared request for later queuing and processing.
|
||||
void PushPreparedRequest(FileRequest* request);
|
||||
//! Gets the prepared requests that are queued to be processed.
|
||||
PreparedQueue& GetPreparedRequests();
|
||||
//! Gets the prepared requests that are queued to be processed.
|
||||
const PreparedQueue& GetPreparedRequests() const;
|
||||
|
||||
//! Marks a request as completed so the main thread in Streamer can close it out.
|
||||
//! This can be safely called from multiple threads.
|
||||
void MarkRequestAsCompleted(FileRequest* request);
|
||||
//! Rejects a request by removing it from the chain and recycling it.
|
||||
//! Only requests without children can be rejected. If the rejected request has a parent it might need to be processed
|
||||
//! further.
|
||||
//! @param request The request to remove and recycle.
|
||||
//! @return The parent request of the rejected request or null if there was no parent.
|
||||
FileRequest* RejectRequest(FileRequest* request);
|
||||
//! Adds an old request to the recycle bin so it can be reused later.
|
||||
void RecycleRequest(FileRequest* request);
|
||||
//! Adds an old external request to the recycle bin so it can be reused later.
|
||||
void RecycleRequest(ExternalFileRequest* request);
|
||||
//! Marks a request as completed so the main thread in Streamer can close it out.
|
||||
//! This can be safely called from multiple threads.
|
||||
void MarkRequestAsCompleted(FileRequest* request);
|
||||
//! Rejects a request by removing it from the chain and recycling it.
|
||||
//! Only requests without children can be rejected. If the rejected request has a parent it might need to be processed
|
||||
//! further.
|
||||
//! @param request The request to remove and recycle.
|
||||
//! @return The parent request of the rejected request or null if there was no parent.
|
||||
FileRequest* RejectRequest(FileRequest* request);
|
||||
//! Adds an old request to the recycle bin so it can be reused later.
|
||||
void RecycleRequest(FileRequest* request);
|
||||
//! Adds an old external request to the recycle bin so it can be reused later.
|
||||
void RecycleRequest(ExternalFileRequest* request);
|
||||
|
||||
//! Does the FinalizeRequest callback where appropriate and does some bookkeeping to finalize requests.
|
||||
//! @return True if any requests were finalized, otherwise false.
|
||||
bool FinalizeCompletedRequests();
|
||||
//! Does the FinalizeRequest callback where appropriate and does some bookkeeping to finalize requests.
|
||||
//! @return True if any requests were finalized, otherwise false.
|
||||
bool FinalizeCompletedRequests();
|
||||
|
||||
//! Causes the main thread for streamer to wake up and process any pending requests. If the thread
|
||||
//! is already awake, nothing happens.
|
||||
void WakeUpSchedulingThread();
|
||||
//! If there's no pending messages this will cause the main thread for streamer to go to sleep.
|
||||
void SuspendSchedulingThread();
|
||||
//! Returns the native primitive(s) used to suspend and wake up the scheduling thread and possibly other threads.
|
||||
AZ::Platform::StreamerContextThreadSync& GetStreamerThreadSynchronizer();
|
||||
//! Causes the main thread for streamer to wake up and process any pending requests. If the thread
|
||||
//! is already awake, nothing happens.
|
||||
void WakeUpSchedulingThread();
|
||||
//! If there's no pending messages this will cause the main thread for streamer to go to sleep.
|
||||
void SuspendSchedulingThread();
|
||||
//! Returns the native primitive(s) used to suspend and wake up the scheduling thread and possibly other threads.
|
||||
AZ::Platform::StreamerContextThreadSync& GetStreamerThreadSynchronizer();
|
||||
|
||||
//! Collects statistics recorded during processing. This will only return statistics for the
|
||||
//! context. Use the CollectStatistics on AZ::IO::Streamer to get all statistics.
|
||||
void CollectStatistics(AZStd::vector<Statistic>& statistics);
|
||||
//! Collects statistics recorded during processing. This will only return statistics for the
|
||||
//! context. Use the CollectStatistics on AZ::IO::Streamer to get all statistics.
|
||||
void CollectStatistics(AZStd::vector<Statistic>& statistics);
|
||||
|
||||
private:
|
||||
//! Gets a new FileRequestPtr. This version is for internal use only and is not thread-safe.
|
||||
//! This will be called by GetNewExternalRequest or GetNewExternalRequestBatch which are responsible
|
||||
//! for managing the lock to the recycle bin.
|
||||
FileRequestPtr GetNewExternalRequestUnguarded();
|
||||
private:
|
||||
//! Gets a new FileRequestPtr. This version is for internal use only and is not thread-safe.
|
||||
//! This will be called by GetNewExternalRequest or GetNewExternalRequestBatch which are responsible
|
||||
//! for managing the lock to the recycle bin.
|
||||
FileRequestPtr GetNewExternalRequestUnguarded();
|
||||
|
||||
inline static constexpr size_t s_initialRecycleBinSize = 64;
|
||||
inline static constexpr size_t s_initialRecycleBinSize = 64;
|
||||
|
||||
AZStd::mutex m_externalRecycleBinGuard;
|
||||
AZStd::vector<ExternalFileRequest*> m_externalRecycleBin;
|
||||
AZStd::vector<FileRequest*> m_internalRecycleBin;
|
||||
|
||||
// The completion is guarded so other threads can perform async IO and safely mark requests as completed.
|
||||
AZStd::recursive_mutex m_completedGuard;
|
||||
AZStd::queue<FileRequest*> m_completed;
|
||||
AZStd::mutex m_externalRecycleBinGuard;
|
||||
AZStd::vector<ExternalFileRequest*> m_externalRecycleBin;
|
||||
AZStd::vector<FileRequest*> m_internalRecycleBin;
|
||||
|
||||
// The prepared request queue is not guarded and should only be called from the main Streamer thread.
|
||||
PreparedQueue m_preparedRequests;
|
||||
// The completion is guarded so other threads can perform async IO and safely mark requests as completed.
|
||||
AZStd::recursive_mutex m_completedGuard;
|
||||
AZStd::queue<FileRequest*> m_completed;
|
||||
|
||||
// The prepared request queue is not guarded and should only be called from the main Streamer thread.
|
||||
PreparedQueue m_preparedRequests;
|
||||
|
||||
#if AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
|
||||
//! By how much time the prediction was off. This mostly covers the latter part of scheduling, which
|
||||
//! gets more precise the closer the request gets to completion.
|
||||
AZ::Statistics::RunningStatistic m_predictionAccuracyUsStat;
|
||||
//! By how much time the prediction was off. This mostly covers the latter part of scheduling, which
|
||||
//! gets more precise the closer the request gets to completion.
|
||||
AZ::Statistics::RunningStatistic m_predictionAccuracyUsStat;
|
||||
|
||||
//! Tracks the percentage of requests with late predictions where the request completed earlier than expected,
|
||||
//! versus the requests that completed later than predicted.
|
||||
AZ::Statistics::RunningStatistic m_latePredictionsPercentageStat;
|
||||
//! Tracks the percentage of requests with late predictions where the request completed earlier than expected,
|
||||
//! versus the requests that completed later than predicted.
|
||||
AZ::Statistics::RunningStatistic m_latePredictionsPercentageStat;
|
||||
|
||||
//! Percentage of requests that missed their deadline. If percentage is too high it can indicate that
|
||||
//! there are too many file requests or the deadlines for requests are too tight.
|
||||
AZ::Statistics::RunningStatistic m_missedDeadlinePercentageStat;
|
||||
//! Percentage of requests that missed their deadline. If percentage is too high it can indicate that
|
||||
//! there are too many file requests or the deadlines for requests are too tight.
|
||||
AZ::Statistics::RunningStatistic m_missedDeadlinePercentageStat;
|
||||
#endif // AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
|
||||
|
||||
//! Platform-specific synchronization object used to suspend the Streamer thread and wake it up to resume procesing.
|
||||
AZ::Platform::StreamerContextThreadSync m_threadSync;
|
||||
//! Platform-specific synchronization object used to suspend the Streamer thread and wake it up to resume procesing.
|
||||
AZ::Platform::StreamerContextThreadSync m_threadSync;
|
||||
|
||||
size_t m_pendingIdCounter{ 0 };
|
||||
};
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
size_t m_pendingIdCounter{ 0 };
|
||||
};
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace AZ
|
||||
behaviorContext->Class<Aabb>()
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Module, "math")
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
|
||||
->Attribute(AZ::Script::Attributes::GenericConstructorOverride, &AabbDefaultConstructor)
|
||||
->Property("min", &Aabb::GetMin, &Aabb::SetMin)
|
||||
@@ -112,46 +112,46 @@ namespace AZ
|
||||
->Method("GetCenter", &Aabb::GetCenter)
|
||||
->Method("Set", &Aabb::Set)
|
||||
->Attribute(AZ::Script::Attributes::MethodOverride, &AabbSetGeneric)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
->Method("CreateFromObb", &Aabb::CreateFromObb)
|
||||
->Method("GetXExtent", &Aabb::GetXExtent)
|
||||
->Method("GetYExtent", &Aabb::GetYExtent)
|
||||
->Method("GetZExtent", &Aabb::GetZExtent)
|
||||
->Method("GetAsSphere", &Aabb::GetAsSphere, nullptr, "() -> Vector3(center) and float(radius)")
|
||||
->Attribute(AZ::Script::Attributes::MethodOverride, &AabbGetAsSphereMultipleReturn)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
->Method<bool (Aabb::*)(const Aabb&) const>("Contains", &Aabb::Contains, nullptr, "const Vector3& or const Aabb&")
|
||||
->Attribute(AZ::Script::Attributes::MethodOverride, &AabbContainsGeneric)
|
||||
->Method<bool (Aabb::*)(const Vector3&) const>("ContainsVector3", &Aabb::Contains, nullptr, "const Vector3&")
|
||||
->Attribute(AZ::Script::Attributes::Ignore, 0) // ignore for script since we already got the generic contains above
|
||||
->Method("Overlaps", &Aabb::Overlaps)
|
||||
->Method("Expand", &Aabb::Expand)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
->Method("GetExpanded", &Aabb::GetExpanded)
|
||||
->Method("AddPoint", &Aabb::AddPoint)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
->Method("AddAabb", &Aabb::AddAabb)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
->Method("GetDistance", &Aabb::GetDistance)
|
||||
->Method("GetClamped", &Aabb::GetClamped)
|
||||
->Method("Clamp", &Aabb::Clamp)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
->Method("SetNull", &Aabb::SetNull)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
->Method("Translate", &Aabb::Translate)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
->Method("GetTranslated", &Aabb::GetTranslated)
|
||||
->Method("GetSurfaceArea", &Aabb::GetSurfaceArea)
|
||||
->Method("GetTransformedObb", static_cast<Obb(Aabb::*)(const Transform&) const>(&Aabb::GetTransformedObb))
|
||||
->Method("GetTransformedAabb", static_cast<Aabb(Aabb::*)(const Transform&) const>(&Aabb::GetTransformedAabb))
|
||||
->Method("ApplyTransform", &Aabb::ApplyTransform)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
->Method("Clone", [](const Aabb& rhs) -> Aabb { return rhs; })
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
->Method("IsFinite", &Aabb::IsFinite)
|
||||
->Method("Equal", &Aabb::operator==)
|
||||
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All);
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ namespace AZ
|
||||
behaviorContext->Class<Color>()->
|
||||
Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)->
|
||||
Attribute(AZ::Script::Attributes::Module, "math")->
|
||||
Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)->
|
||||
Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)->
|
||||
Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)->
|
||||
Constructor<float>()->
|
||||
Constructor<float, float, float, float>()->
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -260,7 +260,7 @@ namespace AZ
|
||||
behaviorContext->Class<Matrix3x3>()->
|
||||
Attribute(Script::Attributes::Scope, Script::Attributes::ScopeFlags::Common)->
|
||||
Attribute(Script::Attributes::Module, "math")->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::ListOnly)->
|
||||
Attribute(Script::Attributes::Storage, Script::Attributes::StorageType::Value)->
|
||||
Attribute(AZ::Script::Attributes::ConstructorOverride, &Internal::Matrix3x3ScriptConstructor)->
|
||||
Attribute(Script::Attributes::GenericConstructorOverride, &Internal::Matrix3x3DefaultConstructor)->
|
||||
|
||||
@@ -280,7 +280,7 @@ namespace AZ
|
||||
behaviorContext->Class<Matrix4x4>()->
|
||||
Attribute(Script::Attributes::Scope, Script::Attributes::ScopeFlags::Common)->
|
||||
Attribute(Script::Attributes::Module, "math")->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::ListOnly)->
|
||||
Attribute(Script::Attributes::Storage, Script::Attributes::StorageType::Value)->
|
||||
Attribute(Script::Attributes::GenericConstructorOverride, &Internal::Matrix4x4DefaultConstructor)->
|
||||
Property<Vector4(Matrix4x4::*)() const, void (Matrix4x4::*)(const Vector4&)>("basisX", &Matrix4x4::GetBasisX, &Matrix4x4::SetBasisX)->
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace AZ
|
||||
if (behaviorContext)
|
||||
{
|
||||
behaviorContext->Class<Obb>()->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::ListOnly)->
|
||||
Attribute(Script::Attributes::Storage, Script::Attributes::StorageType::Value)->
|
||||
Attribute(Script::Attributes::GenericConstructorOverride, &Internal::ObbDefaultConstructor)->
|
||||
Property("position", &Obb::GetPosition, &Obb::SetPosition)->
|
||||
|
||||
@@ -142,7 +142,7 @@ namespace AZ
|
||||
if (behaviorContext)
|
||||
{
|
||||
behaviorContext->Class<Plane>()->
|
||||
Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)->
|
||||
Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)->
|
||||
Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)->
|
||||
Attribute(AZ::Script::Attributes::GenericConstructorOverride, &Internal::PlaneDefaultConstructor)->
|
||||
Method("ToString", &Internal::PlaneToString)->
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace AZ
|
||||
behaviorContext->Class<Quaternion>()->
|
||||
Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)->
|
||||
Attribute(AZ::Script::Attributes::Module, "math")->
|
||||
Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)->
|
||||
Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)->
|
||||
Constructor<float>()->
|
||||
Constructor<float, float, float, float>()->
|
||||
Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)->
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user