@@ -234,7 +234,7 @@ void Q2DViewport::UpdateContent(int flags)
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void Q2DViewport::OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
void Q2DViewport::OnRButtonDown([[maybe_unused]] Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
{
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
{
|
||||
@@ -246,9 +246,6 @@ void Q2DViewport::OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& p
|
||||
setFocus();
|
||||
}
|
||||
|
||||
// Check Edit Tool.
|
||||
MouseCallback(eMouseRDown, point, modifiers);
|
||||
|
||||
SetCurrentCursor(STD_CURSOR_MOVE, QString());
|
||||
|
||||
// Save the mouse down position
|
||||
@@ -273,17 +270,8 @@ void Q2DViewport::OnRButtonUp([[maybe_unused]] Qt::KeyboardModifiers modifiers,
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void Q2DViewport::OnMButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
void Q2DViewport::OnMButtonDown([[maybe_unused]] Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// User pressed the middle mouse button
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Check Edit Tool.
|
||||
if (MouseCallback(eMouseMDown, point, modifiers))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the mouse down position
|
||||
m_RMouseDownPos = point;
|
||||
|
||||
@@ -300,14 +288,8 @@ void Q2DViewport::OnMButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& p
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void Q2DViewport::OnMButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
void Q2DViewport::OnMButtonUp([[maybe_unused]] Qt::KeyboardModifiers modifiers, [[maybe_unused]] const QPoint& point)
|
||||
{
|
||||
// Check Edit Tool.
|
||||
if (MouseCallback(eMouseMUp, point, modifiers))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetViewMode(NothingMode);
|
||||
|
||||
ReleaseMouse();
|
||||
@@ -547,13 +529,6 @@ QPoint Q2DViewport::WorldToView(const Vec3& wp) const
|
||||
QPoint p = QPoint(static_cast<int>(sp.x), static_cast<int>(sp.y));
|
||||
return p;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QPoint Q2DViewport::WorldToViewParticleEditor(const Vec3& wp, [[maybe_unused]] int width, [[maybe_unused]] int height) const //Eric@conffx implement for the children class of IDisplayViewport
|
||||
{
|
||||
Vec3 sp = m_screenTM.TransformPoint(wp);
|
||||
QPoint p = QPoint(static_cast<int>(sp.x), static_cast<int>(sp.y));
|
||||
return p;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
Vec3 Q2DViewport::ViewToWorld(const QPoint& vp, [[maybe_unused]] bool* collideWithTerrain, [[maybe_unused]] bool onlyTerrain, [[maybe_unused]] bool bSkipVegetation, [[maybe_unused]] bool bTestRenderMesh, [[maybe_unused]] bool* collideWithObject) const
|
||||
|
||||
@@ -50,8 +50,6 @@ public:
|
||||
//! Map world space position to viewport position.
|
||||
QPoint WorldToView(const Vec3& wp) const override;
|
||||
|
||||
QPoint WorldToViewParticleEditor(const Vec3& wp, int width, int height) const override; //Eric@conffx
|
||||
|
||||
//! Map viewport position to world space position.
|
||||
Vec3 ViewToWorld(const QPoint& vp, bool* collideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const override;
|
||||
//! Map viewport position to world space ray from camera.
|
||||
@@ -64,7 +62,6 @@ public:
|
||||
|
||||
// ovverided from CViewport.
|
||||
float GetScreenScaleFactor(const Vec3& worldPoint) const override;
|
||||
float GetScreenScaleFactor([[maybe_unused]] const CCamera& camera, [[maybe_unused]] const Vec3& object_position) override { return 1; } //Eric@conffx
|
||||
|
||||
// Overrided from CViewport.
|
||||
void OnDragSelectRectangle(const QRect &rect, bool bNormalizeRect = false) override;
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>General Availability</string>
|
||||
<string>development</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
|
||||
@@ -152,15 +152,6 @@ ActionManager::ActionWrapper& ActionManager::ActionWrapper::SetMenu(DynamicMenu*
|
||||
return *this;
|
||||
}
|
||||
|
||||
ActionManager::ActionWrapper& ActionManager::ActionWrapper::SetApplyHoverEffect()
|
||||
{
|
||||
// Our standard toolbar icons, when hovered on, get a white color effect.
|
||||
// But for this to work we need .pngs that look good with this effect, so this only works with the standard toolbars
|
||||
// and looks very ugly for other toolbars, including toolbars loaded from XML (which just show a white rectangle)
|
||||
m_action->setProperty("IconHasHoverEffect", true);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ActionManager::ActionWrapper& ActionManager::ActionWrapper::SetReserved()
|
||||
{
|
||||
m_action->setProperty("Reserved", true);
|
||||
|
||||
@@ -151,7 +151,6 @@ public:
|
||||
}
|
||||
|
||||
ActionWrapper& SetMenu(DynamicMenu* menu);
|
||||
ActionWrapper& SetApplyHoverEffect();
|
||||
|
||||
operator QAction*() const {
|
||||
return m_action;
|
||||
|
||||
@@ -10,24 +10,21 @@
|
||||
|
||||
#include "AnimationBipedBoneNames.h"
|
||||
|
||||
namespace EditorAnimationBones
|
||||
namespace EditorAnimationBones::Biped
|
||||
{
|
||||
namespace Biped
|
||||
{
|
||||
const char* Pelvis = "Bip01 Pelvis";
|
||||
const char* Head = "Bip01 Head";
|
||||
const char* Weapon = "weapon_bone";
|
||||
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* 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* 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* 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" };
|
||||
}
|
||||
}
|
||||
const char* RightHeel = "Bip01 R Heel";
|
||||
const char* RightToe[2] = { "Bip01 R Toe0", "Bip01 R Toe1" };
|
||||
} // namespace EditorAnimationBones::Biped
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "Include/IObjectManager.h"
|
||||
#include "Objects/EntityObject.h"
|
||||
|
||||
#include <AzCore/Time/ITime.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Movie Callback.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -499,25 +501,24 @@ void CAnimationContext::Update()
|
||||
return;
|
||||
}
|
||||
|
||||
ITimer* pTimer = GetIEditor()->GetSystem()->GetITimer();
|
||||
const AZ::TimeUs frameDeltaTimeUs = AZ::GetSimulationTickDeltaTimeUs();
|
||||
const float frameDeltaTime = AZ::TimeUsToSeconds(frameDeltaTimeUs);
|
||||
|
||||
if (!m_bAutoRecording)
|
||||
{
|
||||
AnimateActiveSequence();
|
||||
|
||||
float dt = pTimer->GetFrameTime();
|
||||
m_currTime += dt * m_fTimeScale;
|
||||
m_currTime += frameDeltaTime * m_fTimeScale;
|
||||
|
||||
if (!m_recording)
|
||||
{
|
||||
GetIEditor()->GetMovieSystem()->PreUpdate(dt);
|
||||
GetIEditor()->GetMovieSystem()->PostUpdate(dt);
|
||||
GetIEditor()->GetMovieSystem()->PreUpdate(frameDeltaTime);
|
||||
GetIEditor()->GetMovieSystem()->PostUpdate(frameDeltaTime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float dt = pTimer->GetFrameTime();
|
||||
m_fRecordingCurrTime += dt * m_fTimeScale;
|
||||
m_fRecordingCurrTime += frameDeltaTime * m_fTimeScale;
|
||||
if (fabs(m_fRecordingCurrTime - m_currTime) > m_fRecordingTimeStep)
|
||||
{
|
||||
m_currTime += m_fRecordingTimeStep;
|
||||
@@ -644,7 +645,9 @@ void CAnimationContext::OnPostRender()
|
||||
{
|
||||
SAnimContext ac;
|
||||
ac.dt = 0;
|
||||
ac.fps = GetIEditor()->GetSystem()->GetITimer()->GetFrameRate();
|
||||
const AZ::TimeUs frameDeltaTimeUs = AZ::GetSimulationTickDeltaTimeUs();
|
||||
const float frameDeltaTime = AZ::TimeUsToSeconds(frameDeltaTimeUs);
|
||||
ac.fps = 1.0f / frameDeltaTime;
|
||||
ac.time = m_currTime;
|
||||
ac.singleFrame = true;
|
||||
ac.forcePlay = true;
|
||||
@@ -797,7 +800,9 @@ void CAnimationContext::AnimateActiveSequence()
|
||||
|
||||
SAnimContext ac;
|
||||
ac.dt = 0;
|
||||
ac.fps = GetIEditor()->GetSystem()->GetITimer()->GetFrameRate();
|
||||
const AZ::TimeUs frameDeltaTimeUs = AZ::GetSimulationTickDeltaTimeUs();
|
||||
const float frameDeltaTime = AZ::TimeUsToSeconds(frameDeltaTimeUs);
|
||||
ac.fps = 1.0f / frameDeltaTime;
|
||||
ac.time = m_currTime;
|
||||
ac.singleFrame = true;
|
||||
ac.forcePlay = true;
|
||||
|
||||
@@ -140,7 +140,7 @@ bool AssetImporterManager::OnBrowseFiles()
|
||||
bool encounteredCrate = false;
|
||||
QStringList invalidFiles;
|
||||
|
||||
for (QString path : fileDialog.selectedFiles())
|
||||
for (const QString& path : fileDialog.selectedFiles())
|
||||
{
|
||||
QString fileName = GetFileName(path);
|
||||
QFileInfo info(path);
|
||||
|
||||
@@ -199,41 +199,6 @@ namespace AzAssetBrowserRequestHandlerPrivate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper utility - determines if the thing being dragged is a FBX from the scene import pipeline
|
||||
// This is important to differentiate.
|
||||
// when someone drags a MTL file directly into the viewport, even from a FBX, we want to spawn it as a decal
|
||||
// but when someone drags a FBX that contains MTL files, we want only to spawn the meshes.
|
||||
// so we have to specifically differentiate here between the mimeData type that contains the source as the root
|
||||
// (dragging the fbx file itself)
|
||||
// and one which contains the actual product at its root.
|
||||
|
||||
bool IsDragOfFBX(const QMimeData* mimeData)
|
||||
{
|
||||
AZStd::vector<AssetBrowserEntry*> entries;
|
||||
if (!AssetBrowserEntry::FromMimeData(mimeData, entries))
|
||||
{
|
||||
// if mimedata does not even contain entries, no point in proceeding.
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto entry : entries)
|
||||
{
|
||||
if (entry->GetEntryType() != AssetBrowserEntry::AssetEntryType::Source)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// this is a source file. Is it the filetype we're looking for?
|
||||
if (SourceAssetBrowserEntry* source = azrtti_cast<SourceAssetBrowserEntry*>(entry))
|
||||
{
|
||||
if (AzFramework::StringFunc::Equal(source->GetExtension().c_str(), ".fbx", false))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
AzAssetBrowserRequestHandler::AzAssetBrowserRequestHandler()
|
||||
|
||||
@@ -671,7 +671,7 @@ AzToolsFramework::PropertyRowWidget* ReflectedPropertyControl::FindPropertyRowWi
|
||||
return nullptr;
|
||||
}
|
||||
const AzToolsFramework::ReflectedPropertyEditor::WidgetList& widgets = m_editor->GetWidgets();
|
||||
for (auto instance : widgets)
|
||||
for (const auto& instance : widgets)
|
||||
{
|
||||
if (instance.second->label() == item->GetPropertyName())
|
||||
{
|
||||
|
||||
+13
-19
@@ -80,7 +80,6 @@ AZ_POP_DISABLE_WARNING
|
||||
#include <AzQtComponents/Utilities/QtPluginPaths.h>
|
||||
|
||||
// CryCommon
|
||||
#include <CryCommon/ITimer.h>
|
||||
#include <CryCommon/ILevelSystem.h>
|
||||
|
||||
// Editor
|
||||
@@ -371,10 +370,8 @@ void CCryEditApp::RegisterActionHandlers()
|
||||
ON_COMMAND(ID_EDIT_FETCH, OnEditFetch)
|
||||
ON_COMMAND(ID_FILE_EXPORTTOGAMENOSURFACETEXTURE, OnFileExportToGameNoSurfaceTexture)
|
||||
ON_COMMAND(ID_VIEW_SWITCHTOGAME, OnViewSwitchToGame)
|
||||
MainWindow::instance()->GetActionManager()->RegisterActionHandler(ID_VIEW_SWITCHTOGAME_FULLSCREEN, [this]() {
|
||||
ed_previewGameInFullscreen_once = true;
|
||||
OnViewSwitchToGame();
|
||||
});
|
||||
ON_COMMAND(ID_VIEW_SWITCHTOGAME_VIEWPORT, OnViewSwitchToGame)
|
||||
ON_COMMAND(ID_VIEW_SWITCHTOGAME_FULLSCREEN, OnViewSwitchToGameFullScreen)
|
||||
ON_COMMAND(ID_MOVE_OBJECT, OnMoveObject)
|
||||
ON_COMMAND(ID_RENAME_OBJ, OnRenameObj)
|
||||
ON_COMMAND(ID_UNDO, OnUndo)
|
||||
@@ -1361,16 +1358,6 @@ void CCryEditApp::CompileCriticalAssets() const
|
||||
assetsInQueueNotifcation.BusDisconnect();
|
||||
CCryEditApp::OutputStartupMessage(QString("Asset Processor is now ready."));
|
||||
|
||||
// VERY early on, as soon as we can, request that the asset system make sure the following assets take priority over others,
|
||||
// so that by the time we ask for them there is a greater likelihood that they're already good to go.
|
||||
// these can be loaded later but are still important:
|
||||
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, "/texturemsg/");
|
||||
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, "engineassets/materials");
|
||||
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, "engineassets/geomcaches");
|
||||
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, "engineassets/objects");
|
||||
|
||||
// some are specifically extra important and will cause issues if missing completely:
|
||||
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::CompileAssetSync, "engineassets/objects/default.cgf");
|
||||
}
|
||||
|
||||
bool CCryEditApp::ConnectToAssetProcessor() const
|
||||
@@ -2584,6 +2571,12 @@ void CCryEditApp::OnViewSwitchToGame()
|
||||
GetIEditor()->SetInGameMode(inGame);
|
||||
}
|
||||
|
||||
void CCryEditApp::OnViewSwitchToGameFullScreen()
|
||||
{
|
||||
ed_previewGameInFullscreen_once = true;
|
||||
OnViewSwitchToGame();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CCryEditApp::OnExportSelectedObjects()
|
||||
{
|
||||
@@ -3974,9 +3967,8 @@ void CCryEditApp::OpenLUAEditor(const char* files)
|
||||
}
|
||||
}
|
||||
|
||||
const char* engineRoot = nullptr;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
|
||||
AZ_Assert(engineRoot != nullptr, "Unable to communicate to AzFramework::ApplicationRequests::Bus");
|
||||
AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath();
|
||||
AZ_Assert(!engineRoot.empty(), "Unable to query Engine Path");
|
||||
|
||||
AZStd::string_view exePath;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(exePath, &AZ::ComponentApplicationRequests::GetExecutableFolder);
|
||||
@@ -3995,7 +3987,7 @@ void CCryEditApp::OpenLUAEditor(const char* files)
|
||||
#endif
|
||||
"%s", argumentQuoteString, aznumeric_cast<int>(exePath.size()), exePath.data(), argumentQuoteString);
|
||||
|
||||
AZStd::string processArgs = AZStd::string::format("%s -engine-path \"%s\"", args.c_str(), engineRoot);
|
||||
AZStd::string processArgs = AZStd::string::format("%s -engine-path \"%s\"", args.c_str(), engineRoot.c_str());
|
||||
StartProcessDetached(process.c_str(), processArgs.c_str());
|
||||
}
|
||||
|
||||
@@ -4196,6 +4188,8 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[])
|
||||
"\nThis could be because of incorrectly configured components, or missing required gems."
|
||||
"\nSee other errors for more details.");
|
||||
|
||||
AzToolsFramework::EditorEventsBus::Broadcast(&AzToolsFramework::EditorEvents::NotifyEditorInitialized);
|
||||
|
||||
if (didCryEditStart)
|
||||
{
|
||||
app->EnableOnIdle();
|
||||
|
||||
@@ -212,6 +212,7 @@ public:
|
||||
void OnEditFetch();
|
||||
void OnFileExportToGameNoSurfaceTexture();
|
||||
void OnViewSwitchToGame();
|
||||
void OnViewSwitchToGameFullScreen();
|
||||
void OnViewDeploy();
|
||||
void DeleteSelectedEntities(bool includeDescendants);
|
||||
void OnMoveObject();
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Time/ITime.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <MathConversion.h>
|
||||
|
||||
@@ -749,7 +750,9 @@ bool CCryEditDoc::OnOpenDocument(const QString& lpszPathName)
|
||||
|
||||
bool CCryEditDoc::BeforeOpenDocument(const QString& lpszPathName, TOpenDocContext& context)
|
||||
{
|
||||
CTimeValue loading_start_time = gEnv->pTimer->GetAsyncTime();
|
||||
const AZ::TimeMs timeMs = AZ::GetRealElapsedTimeMs();
|
||||
const double timeSec = AZ::TimeMsToSecondsDouble(timeMs);
|
||||
const CTimeValue loading_start_time(timeSec);
|
||||
|
||||
bool usePrefabSystemForLevels = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
@@ -790,7 +793,7 @@ bool CCryEditDoc::BeforeOpenDocument(const QString& lpszPathName, TOpenDocContex
|
||||
|
||||
bool CCryEditDoc::DoOpenDocument(TOpenDocContext& context)
|
||||
{
|
||||
CTimeValue& loading_start_time = context.loading_start_time;
|
||||
const CTimeValue& loading_start_time = context.loading_start_time;
|
||||
|
||||
bool isPrefabEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
@@ -860,7 +863,9 @@ bool CCryEditDoc::DoOpenDocument(TOpenDocContext& context)
|
||||
|
||||
StartStreamingLoad();
|
||||
|
||||
CTimeValue loading_end_time = gEnv->pTimer->GetAsyncTime();
|
||||
const AZ::TimeMs timeMs = AZ::GetRealElapsedTimeMs();
|
||||
const double timeSec = AZ::TimeMsToSecondsDouble(timeMs);
|
||||
const CTimeValue loading_end_time(timeSec);
|
||||
|
||||
CLogFile::FormatLine("-----------------------------------------------------------");
|
||||
CLogFile::FormatLine("Successfully opened document %s", context.absoluteLevelPath.toUtf8().data());
|
||||
@@ -1123,7 +1128,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
|
||||
const QString oldLevelPattern = QDir(oldLevelFolder).absoluteFilePath("*.*");
|
||||
const QString oldLevelName = Path::GetFile(GetLevelPathName());
|
||||
const QString oldLevelXml = Path::ReplaceExtension(oldLevelName, "xml");
|
||||
AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskAndInZips);
|
||||
AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), AZ::IO::FileSearchLocation::Any);
|
||||
if (findHandle)
|
||||
{
|
||||
do
|
||||
|
||||
@@ -40,10 +40,10 @@ AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
namespace
|
||||
{
|
||||
// File name extension for python files
|
||||
const QString s_kPythonFileNameSpec = "*.py";
|
||||
const QString s_kPythonFileNameSpec("*.py");
|
||||
|
||||
// Tree root element name
|
||||
const QString s_kRootElementName = "Python Scripts";
|
||||
const QString s_kRootElementName("Python Scripts");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -105,7 +105,6 @@
|
||||
#include <CryFile.h>
|
||||
#include <ISystem.h>
|
||||
#include <IIndexedMesh.h>
|
||||
#include <ITimer.h>
|
||||
#include <IXml.h>
|
||||
#include <IMovieSystem.h>
|
||||
|
||||
|
||||
@@ -145,6 +145,15 @@ namespace SandboxEditor
|
||||
}
|
||||
};
|
||||
|
||||
const auto trackingTransform = [viewportId = m_viewportId]
|
||||
{
|
||||
bool tracking = false;
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
tracking, viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsTrackingTransform);
|
||||
|
||||
return tracking;
|
||||
};
|
||||
|
||||
m_firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(SandboxEditor::CameraFreeLookChannelId());
|
||||
|
||||
m_firstPersonRotateCamera->m_rotateSpeedFn = []
|
||||
@@ -152,6 +161,11 @@ namespace SandboxEditor
|
||||
return SandboxEditor::CameraRotateSpeed();
|
||||
};
|
||||
|
||||
m_firstPersonRotateCamera->m_constrainPitch = [trackingTransform]
|
||||
{
|
||||
return !trackingTransform();
|
||||
};
|
||||
|
||||
// default behavior is to hide the cursor but this can be disabled (useful for remote desktop)
|
||||
// note: See CaptureCursorLook in the Settings Registry
|
||||
m_firstPersonRotateCamera->SetActivationBeganFn(hideCursor);
|
||||
@@ -255,6 +269,11 @@ namespace SandboxEditor
|
||||
return SandboxEditor::CameraOrbitYawRotationInverted();
|
||||
};
|
||||
|
||||
m_orbitRotateCamera->m_constrainPitch = [trackingTransform]
|
||||
{
|
||||
return !trackingTransform();
|
||||
};
|
||||
|
||||
m_orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
|
||||
translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslateOffsetOrbit);
|
||||
|
||||
@@ -337,12 +356,12 @@ namespace SandboxEditor
|
||||
AZ::TransformBus::EventResult(worldFromLocal, viewEntityId, &AZ::TransformBus::Events::GetWorldTM);
|
||||
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, worldFromLocal);
|
||||
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, worldFromLocal);
|
||||
}
|
||||
else
|
||||
{
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
|
||||
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StopTrackingTransform);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
|
||||
|
||||
// CryCommon
|
||||
#include <CryCommon/HMDBus.h>
|
||||
#include <CryCommon/IRenderAuxGeom.h>
|
||||
|
||||
// AzFramework
|
||||
@@ -299,13 +298,9 @@ AzToolsFramework::ViewportInteraction::MousePick EditorViewportWidget::BuildMous
|
||||
{
|
||||
AzToolsFramework::ViewportInteraction::MousePick mousePick;
|
||||
mousePick.m_screenCoordinates = AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(point);
|
||||
if (const auto& ray = m_renderViewport->ViewportScreenToWorldRay(mousePick.m_screenCoordinates);
|
||||
ray.has_value())
|
||||
{
|
||||
mousePick.m_rayOrigin = ray.value().origin;
|
||||
mousePick.m_rayDirection = ray.value().direction;
|
||||
}
|
||||
|
||||
const auto[origin, direction] = m_renderViewport->ViewportScreenToWorldRay(mousePick.m_screenCoordinates);
|
||||
mousePick.m_rayOrigin = origin;
|
||||
mousePick.m_rayDirection = direction;
|
||||
return mousePick;
|
||||
}
|
||||
|
||||
@@ -556,22 +551,6 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
// this should only occur for the main viewport and no others.
|
||||
ShowCursor();
|
||||
|
||||
// If the user has selected game mode, enable outputting to any attached HMD and properly size the context
|
||||
// to the resolution specified by the VR device.
|
||||
if (gSettings.bEnableGameModeVR)
|
||||
{
|
||||
const AZ::VR::HMDDeviceInfo* deviceInfo = nullptr;
|
||||
EBUS_EVENT_RESULT(deviceInfo, AZ::VR::HMDDeviceRequestBus, GetDeviceInfo);
|
||||
AZ_Warning("Render Viewport", deviceInfo, "No VR device detected");
|
||||
|
||||
if (deviceInfo)
|
||||
{
|
||||
// Note: This may also need to adjust the viewport size
|
||||
SetActiveWindow();
|
||||
SetFocus();
|
||||
SetSelected(true);
|
||||
}
|
||||
}
|
||||
SetCurrentCursor(STD_CURSOR_GAME);
|
||||
|
||||
if (ShouldPreviewFullscreen())
|
||||
@@ -912,23 +891,6 @@ AZ::Vector3 EditorViewportWidget::PickTerrain(const AzFramework::ScreenPoint& po
|
||||
return LYVec3ToAZVec3(ViewToWorld(AzToolsFramework::ViewportInteraction::QPointFromScreenPoint(point), nullptr, true));
|
||||
}
|
||||
|
||||
AZ::EntityId EditorViewportWidget::PickEntity(const AzFramework::ScreenPoint& point)
|
||||
{
|
||||
AZ::EntityId entityId;
|
||||
HitContext hitInfo;
|
||||
hitInfo.view = this;
|
||||
if (HitTest(AzToolsFramework::ViewportInteraction::QPointFromScreenPoint(point), hitInfo))
|
||||
{
|
||||
if (hitInfo.object && (hitInfo.object->GetType() == OBJTYPE_AZENTITY))
|
||||
{
|
||||
auto entityObject = static_cast<CComponentEntityObject*>(hitInfo.object);
|
||||
entityId = entityObject->GetAssociatedEntityId();
|
||||
}
|
||||
}
|
||||
|
||||
return entityId;
|
||||
}
|
||||
|
||||
float EditorViewportWidget::TerrainHeight(const AZ::Vector2& position)
|
||||
{
|
||||
return GetIEditor()->GetTerrainElevation(position.GetX(), position.GetY());
|
||||
@@ -1653,16 +1615,15 @@ void EditorViewportWidget::RenderSelectedRegion()
|
||||
Vec3 EditorViewportWidget::WorldToView3D(const Vec3& wp, [[maybe_unused]] int nFlags) const
|
||||
{
|
||||
Vec3 out(0, 0, 0);
|
||||
float x, y, z;
|
||||
float x, y;
|
||||
|
||||
ProjectToScreen(wp.x, wp.y, wp.z, &x, &y, &z);
|
||||
if (_finite(x) && _finite(y) && _finite(z))
|
||||
ProjectToScreen(wp.x, wp.y, wp.z, &x, &y);
|
||||
if (_finite(x) && _finite(y))
|
||||
{
|
||||
out.x = (x / 100) * m_rcClient.width();
|
||||
out.y = (y / 100) * m_rcClient.height();
|
||||
out.x /= static_cast<float>(QHighDpiScaling::factor(windowHandle()->screen()));
|
||||
out.y /= static_cast<float>(QHighDpiScaling::factor(windowHandle()->screen()));
|
||||
out.z = z;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -1672,24 +1633,6 @@ QPoint EditorViewportWidget::WorldToView(const Vec3& wp) const
|
||||
{
|
||||
return AzToolsFramework::ViewportInteraction::QPointFromScreenPoint(m_renderViewport->ViewportWorldToScreen(LYVec3ToAZVec3(wp)));
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QPoint EditorViewportWidget::WorldToViewParticleEditor(const Vec3& wp, int width, int height) const
|
||||
{
|
||||
QPoint p;
|
||||
float x, y, z;
|
||||
|
||||
ProjectToScreen(wp.x, wp.y, wp.z, &x, &y, &z);
|
||||
if (_finite(x) || _finite(y))
|
||||
{
|
||||
p.rx() = static_cast<int>((x / 100) * width);
|
||||
p.ry() = static_cast<int>((y / 100) * height);
|
||||
}
|
||||
else
|
||||
{
|
||||
QPoint(0, 0);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
Vec3 EditorViewportWidget::ViewToWorld(
|
||||
@@ -1705,20 +1648,16 @@ Vec3 EditorViewportWidget::ViewToWorld(
|
||||
AZ_UNUSED(collideWithObject);
|
||||
|
||||
auto ray = m_renderViewport->ViewportScreenToWorldRay(AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(vp));
|
||||
if (!ray.has_value())
|
||||
{
|
||||
return Vec3(0, 0, 0);
|
||||
}
|
||||
|
||||
const float maxDistance = 10000.f;
|
||||
Vec3 v = AZVec3ToLYVec3(ray.value().direction) * maxDistance;
|
||||
Vec3 v = AZVec3ToLYVec3(ray.direction) * maxDistance;
|
||||
|
||||
if (!_finite(v.x) || !_finite(v.y) || !_finite(v.z))
|
||||
{
|
||||
return Vec3(0, 0, 0);
|
||||
}
|
||||
|
||||
Vec3 colp = AZVec3ToLYVec3(ray.value().origin) + 0.002f * v;
|
||||
Vec3 colp = AZVec3ToLYVec3(ray.origin) + 0.002f * v;
|
||||
|
||||
return colp;
|
||||
}
|
||||
@@ -1757,21 +1696,19 @@ bool EditorViewportWidget::RayRenderMeshIntersection(IRenderMesh* pRenderMesh, c
|
||||
return bRes;*/
|
||||
}
|
||||
|
||||
void EditorViewportWidget::UnProjectFromScreen(float sx, float sy, float sz, float* px, float* py, float* pz) const
|
||||
void EditorViewportWidget::UnProjectFromScreen(float sx, float sy, float* px, float* py, float* pz) const
|
||||
{
|
||||
AZ::Vector3 wp;
|
||||
wp = m_renderViewport->ViewportScreenToWorld(AzFramework::ScreenPoint{(int)sx, m_rcClient.bottom() - ((int)sy)}, sz).value_or(wp);
|
||||
const AZ::Vector3 wp = m_renderViewport->ViewportScreenToWorld(AzFramework::ScreenPoint{(int)sx, m_rcClient.bottom() - ((int)sy)});
|
||||
*px = wp.GetX();
|
||||
*py = wp.GetY();
|
||||
*pz = wp.GetZ();
|
||||
}
|
||||
|
||||
void EditorViewportWidget::ProjectToScreen(float ptx, float pty, float ptz, float* sx, float* sy, float* sz) const
|
||||
void EditorViewportWidget::ProjectToScreen(float ptx, float pty, float ptz, float* sx, float* sy) const
|
||||
{
|
||||
AzFramework::ScreenPoint screenPosition = m_renderViewport->ViewportWorldToScreen(AZ::Vector3{ptx, pty, ptz});
|
||||
*sx = static_cast<float>(screenPosition.m_x);
|
||||
*sy = static_cast<float>(screenPosition.m_y);
|
||||
*sz = 0.f;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -1781,32 +1718,22 @@ void EditorViewportWidget::ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3&
|
||||
|
||||
Vec3 pos0, pos1;
|
||||
float wx, wy, wz;
|
||||
UnProjectFromScreen(static_cast<float>(vp.x()), static_cast<float>(rc.bottom() - vp.y()), 0.0f, &wx, &wy, &wz);
|
||||
if (!_finite(wx) || !_finite(wy) || !_finite(wz))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (fabs(wx) > 1000000 || fabs(wy) > 1000000 || fabs(wz) > 1000000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pos0(wx, wy, wz);
|
||||
UnProjectFromScreen(static_cast<float>(vp.x()), static_cast<float>(rc.bottom() - vp.y()), 1.0f, &wx, &wy, &wz);
|
||||
if (!_finite(wx) || !_finite(wy) || !_finite(wz))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (fabs(wx) > 1000000 || fabs(wy) > 1000000 || fabs(wz) > 1000000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pos1(wx, wy, wz);
|
||||
UnProjectFromScreen(static_cast<float>(vp.x()), static_cast<float>(rc.bottom() - vp.y()), &wx, &wy, &wz);
|
||||
|
||||
Vec3 v = (pos1 - pos0);
|
||||
v = v.GetNormalized();
|
||||
if (!_finite(wx) || !_finite(wy) || !_finite(wz))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (fabs(wx) > 1000000 || fabs(wy) > 1000000 || fabs(wz) > 1000000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pos0(wx, wy, wz);
|
||||
|
||||
raySrc = pos0;
|
||||
rayDir = v;
|
||||
rayDir = (pos0 - AZVec3ToLYVec3(m_renderViewport->GetCameraState().m_position)).GetNormalized();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -1815,13 +1742,6 @@ float EditorViewportWidget::GetScreenScaleFactor([[maybe_unused]] const Vec3& wo
|
||||
AZ_Error("CryLegacy", false, "EditorViewportWidget::GetScreenScaleFactor not implemented");
|
||||
return 1.f;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
float EditorViewportWidget::GetScreenScaleFactor(const CCamera& camera, const Vec3& object_position)
|
||||
{
|
||||
Vec3 camPos = camera.GetPosition();
|
||||
float dist = camPos.GetDistance(object_position);
|
||||
return dist;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool EditorViewportWidget::CheckRespondToInput() const
|
||||
@@ -1842,7 +1762,6 @@ bool EditorViewportWidget::CheckRespondToInput() const
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool EditorViewportWidget::HitTest(const QPoint& point, HitContext& hitInfo)
|
||||
{
|
||||
hitInfo.camera = nullptr;
|
||||
hitInfo.pExcludedObject = GetCameraObject();
|
||||
return QtViewport::HitTest(point, hitInfo);
|
||||
}
|
||||
@@ -2363,10 +2282,10 @@ void* EditorViewportWidget::GetSystemCursorConstraintWindow() const
|
||||
return systemCursorConstrained ? renderOverlayHWND() : nullptr;
|
||||
}
|
||||
|
||||
void EditorViewportWidget::BuildDragDropContext(AzQtComponents::ViewportDragContext& context, const QPoint& pt)
|
||||
void EditorViewportWidget::BuildDragDropContext(
|
||||
AzQtComponents::ViewportDragContext& context, const AzFramework::ViewportId viewportId, const QPoint& point)
|
||||
{
|
||||
const auto scaledPoint = WidgetToViewport(pt);
|
||||
QtViewport::BuildDragDropContext(context, scaledPoint);
|
||||
QtViewport::BuildDragDropContext(context, viewportId, point);
|
||||
}
|
||||
|
||||
void EditorViewportWidget::RestoreViewportAfterGameMode()
|
||||
@@ -2547,12 +2466,6 @@ bool EditorViewportWidget::ShouldPreviewFullscreen() const
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not supported in VR
|
||||
if (gSettings.bEnableGameModeVR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If level not loaded, don't preview in fullscreen (preview shouldn't work at all without a level, but it does)
|
||||
if (auto ge = GetIEditor()->GetGameEngine())
|
||||
{
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <Cry_Camera.h>
|
||||
|
||||
#include <QSet>
|
||||
|
||||
@@ -166,13 +165,11 @@ private:
|
||||
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, const QPoint& point) override;
|
||||
void SetViewportId(int id) override;
|
||||
QPoint WorldToView(const Vec3& wp) const override;
|
||||
QPoint WorldToViewParticleEditor(const Vec3& wp, int width, int height) const override;
|
||||
Vec3 WorldToView3D(const Vec3& wp, int nFlags = 0) const override;
|
||||
Vec3 ViewToWorld(const QPoint& vp, bool* collideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const override;
|
||||
void ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3& rayDir) const override;
|
||||
Vec3 ViewToWorldNormal(const QPoint& vp, bool onlyTerrain, bool bTestRenderMesh = false) override;
|
||||
float GetScreenScaleFactor(const Vec3& worldPoint) const override;
|
||||
float GetScreenScaleFactor(const CCamera& camera, const Vec3& object_position) override;
|
||||
float GetAspectRatio() const override;
|
||||
bool HitTest(const QPoint& point, HitContext& hitInfo) override;
|
||||
bool IsBoundsVisible(const AABB& box) const override;
|
||||
@@ -208,7 +205,6 @@ private:
|
||||
void* GetSystemCursorConstraintWindow() const override;
|
||||
|
||||
// AzToolsFramework::MainEditorViewportInteractionRequestBus overrides ...
|
||||
AZ::EntityId PickEntity(const AzFramework::ScreenPoint& point) override;
|
||||
AZ::Vector3 PickTerrain(const AzFramework::ScreenPoint& point) override;
|
||||
float TerrainHeight(const AZ::Vector2& position) override;
|
||||
bool ShowingWorldSpace() override;
|
||||
@@ -275,7 +271,8 @@ private:
|
||||
|
||||
bool CheckRespondToInput() const;
|
||||
|
||||
void BuildDragDropContext(AzQtComponents::ViewportDragContext& context, const QPoint& pt) override;
|
||||
void BuildDragDropContext(
|
||||
AzQtComponents::ViewportDragContext& context, AzFramework::ViewportId viewportId, const QPoint& point) override;
|
||||
|
||||
void SetAsActiveViewport();
|
||||
void PushDisableRendering();
|
||||
@@ -306,8 +303,8 @@ private:
|
||||
const DisplayContext& GetDisplayContext() const { return m_displayContext; }
|
||||
CBaseObject* GetCameraObject() const;
|
||||
|
||||
void UnProjectFromScreen(float sx, float sy, float sz, float* px, float* py, float* pz) const;
|
||||
void ProjectToScreen(float ptx, float pty, float ptz, float* sx, float* sy, float* sz) const;
|
||||
void UnProjectFromScreen(float sx, float sy, float* px, float* py, float* pz) const;
|
||||
void ProjectToScreen(float ptx, float pty, float ptz, float* sx, float* sy) const;
|
||||
|
||||
AZ::RPI::ViewPtr GetCurrentAtomView() const;
|
||||
|
||||
|
||||
@@ -822,7 +822,7 @@ void CGameEngine::Update()
|
||||
if (gEnv->pSystem)
|
||||
{
|
||||
gEnv->pSystem->UpdatePreTickBus();
|
||||
componentApplication->Tick(gEnv->pTimer->GetFrameTime(ITimer::ETIMER_GAME));
|
||||
componentApplication->Tick();
|
||||
gEnv->pSystem->UpdatePostTickBus();
|
||||
}
|
||||
|
||||
@@ -838,7 +838,7 @@ void CGameEngine::Update()
|
||||
unsigned int updateFlags = ESYSUPDATE_EDITOR;
|
||||
GetIEditor()->GetAnimation()->Update();
|
||||
GetIEditor()->GetSystem()->UpdatePreTickBus(updateFlags);
|
||||
componentApplication->Tick(gEnv->pTimer->GetFrameTime(ITimer::ETIMER_GAME));
|
||||
componentApplication->Tick();
|
||||
GetIEditor()->GetSystem()->UpdatePostTickBus(updateFlags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ class CBaseObject;
|
||||
struct IDisplayViewport;
|
||||
class CDeepSelection;
|
||||
struct AABB;
|
||||
class CCamera;
|
||||
|
||||
#include <QRect>
|
||||
#include <platform.h>
|
||||
@@ -68,8 +67,6 @@ struct HitContext
|
||||
QRect rect;
|
||||
//! Optional limiting bounding box for hit testing.
|
||||
AABB* bounds;
|
||||
//! Optional camera for culling perspective viewports.
|
||||
CCamera* camera;
|
||||
|
||||
//! Testing performed in 2D viewport.
|
||||
bool b2DViewport;
|
||||
@@ -120,7 +117,6 @@ struct HitContext
|
||||
rect = QRect();
|
||||
b2DViewport = false;
|
||||
view = 0;
|
||||
camera = 0;
|
||||
point2d = QPoint();
|
||||
axis = 0;
|
||||
distanceTolerance = 0;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
struct DisplayContext;
|
||||
class CBaseObjectsCache;
|
||||
class QPoint;
|
||||
class CCamera;
|
||||
struct AABB;
|
||||
class CViewport;
|
||||
|
||||
@@ -23,7 +22,6 @@ struct IDisplayViewport
|
||||
{
|
||||
virtual void Update() = 0;
|
||||
virtual float GetScreenScaleFactor(const Vec3& position) const = 0;
|
||||
virtual float GetScreenScaleFactor(const CCamera& camera, const Vec3& object_position) = 0;
|
||||
virtual bool HitTestLine(const Vec3& lineP1, const Vec3& lineP2, const QPoint& hitpoint, int pixelRadius, float* pToCameraDistance = 0) const = 0;
|
||||
|
||||
/**
|
||||
@@ -47,7 +45,6 @@ struct IDisplayViewport
|
||||
virtual const Matrix34& GetViewTM() const = 0;
|
||||
virtual const Matrix34& GetScreenTM() const = 0;
|
||||
virtual QPoint WorldToView(const Vec3& worldPoint) const = 0;
|
||||
virtual QPoint WorldToViewParticleEditor(const Vec3& worldPoint, int width, int height) const = 0;
|
||||
virtual Vec3 WorldToView3D(const Vec3& worldPoint, int flags = 0) const = 0;
|
||||
virtual Vec3 ViewToWorld(const QPoint& vp, bool* collideWithTerrain = nullptr, bool onlyTerrain = false, bool bSkipVegetation = false, bool bTestRenderMesh = false, bool* collideWithObject = nullptr) const = 0;
|
||||
virtual void ViewToWorldRay(const QPoint& vp, Vec3& raySrc, Vec3& rayDir) const = 0;
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
#define CRYINCLUDE_EDITOR_MATERIAL_IEDITORMATERIALMANAGER_H
|
||||
#pragma once
|
||||
|
||||
#define MATERIAL_FILE_EXT ".mtl"
|
||||
#define DCC_MATERIAL_FILE_EXT ".dccmtl"
|
||||
#define MATERIALS_PATH "materials/"
|
||||
|
||||
#include <Include/IBaseLibraryManager.h>
|
||||
#include <IMaterial.h>
|
||||
|
||||
|
||||
@@ -60,7 +60,6 @@ struct IFileUtil
|
||||
EFILE_TYPE_GEOMETRY,
|
||||
EFILE_TYPE_TEXTURE,
|
||||
EFILE_TYPE_SOUND,
|
||||
EFILE_TYPE_GEOMCACHE,
|
||||
EFILE_TYPE_LAST,
|
||||
};
|
||||
|
||||
@@ -114,9 +113,7 @@ struct IFileUtil
|
||||
|
||||
virtual void ShowInExplorer(const QString& path) = 0;
|
||||
|
||||
virtual bool CompileLuaFile(const char* luaFilename) = 0;
|
||||
virtual bool ExtractFile(QString& file, bool bMsgBoxAskForExtraction = true, const char* pDestinationFilename = nullptr) = 0;
|
||||
virtual void EditTextFile(const char* txtFile, int line = 0, ETextFileType fileType = FILE_TYPE_SCRIPT) = 0;
|
||||
virtual void EditTextureFile(const char* txtureFile, bool bUseGameFolder) = 0;
|
||||
|
||||
//! dcc filename calculation and extraction sub-routines
|
||||
|
||||
@@ -27,7 +27,9 @@ namespace UnitTest
|
||||
AZ::Entity* m_entity = nullptr;
|
||||
AZ::ComponentDescriptor* m_transformComponent = nullptr;
|
||||
|
||||
static const AzFramework::ViewportId TestViewportId;
|
||||
static inline constexpr AzFramework::ViewportId TestViewportId = 2345;
|
||||
static inline constexpr float HalfInterpolateToTransformDuration =
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequests::InterpolateToTransformDuration * 0.5f;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
@@ -76,8 +78,6 @@ namespace UnitTest
|
||||
}
|
||||
};
|
||||
|
||||
const AzFramework::ViewportId EditorCameraFixture::TestViewportId = AzFramework::ViewportId(1337);
|
||||
|
||||
TEST_F(EditorCameraFixture, ModularViewportCameraControllerReferenceFrameUpdatedWhenViewportEntityisChanged)
|
||||
{
|
||||
// Given
|
||||
@@ -91,8 +91,8 @@ namespace UnitTest
|
||||
&Camera::EditorCameraNotificationBus::Events::OnViewportViewEntityChanged, m_entity->GetId());
|
||||
|
||||
// ensure the viewport updates after the viewport view entity change
|
||||
const float deltaTime = 1.0f / 60.0f;
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
|
||||
// note: do a large step to ensure smoothing finishes (e.g. not 1.0f/60.0f)
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(2.0f), AZ::ScriptTimePoint() });
|
||||
|
||||
// retrieve updated camera transform
|
||||
const AZ::Transform cameraTransform = m_cameraViewportContextView->GetCameraTransform();
|
||||
@@ -102,61 +102,40 @@ namespace UnitTest
|
||||
EXPECT_THAT(cameraTransform, IsClose(entityTransform));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, ReferenceFrameRemainsIdentityAfterExternalCameraTransformChangeWhenNotSet)
|
||||
TEST_F(EditorCameraFixture, TrackingTransformIsTrueAfterTransformIsTracked)
|
||||
{
|
||||
// Given
|
||||
m_cameraViewportContextView->SetCameraTransform(AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f)));
|
||||
// Given/When
|
||||
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, referenceFrame);
|
||||
|
||||
// When
|
||||
AZ::Transform referenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
bool trackingTransform = false;
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
referenceFrame, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
trackingTransform, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsTrackingTransform);
|
||||
|
||||
// Then
|
||||
// reference frame is still the identity
|
||||
EXPECT_THAT(referenceFrame, IsClose(AZ::Transform::CreateIdentity()));
|
||||
EXPECT_THAT(trackingTransform, ::testing::IsTrue());
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, ExternalCameraTransformChangeWhenReferenceFrameIsSetUpdatesReferenceFrame)
|
||||
TEST_F(EditorCameraFixture, TrackingTransformIsFalseAfterTransformIsStoppedBeingTracked)
|
||||
{
|
||||
// Given
|
||||
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
|
||||
|
||||
const AZ::Transform nextTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f));
|
||||
m_cameraViewportContextView->SetCameraTransform(nextTransform);
|
||||
|
||||
// When
|
||||
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
currentReferenceFrame, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(currentReferenceFrame, IsClose(nextTransform));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, ReferenceFrameReturnedToIdentityAfterClear)
|
||||
{
|
||||
// Given
|
||||
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, referenceFrame);
|
||||
|
||||
// When
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
|
||||
|
||||
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
currentReferenceFrame, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StopTrackingTransform);
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity()));
|
||||
bool trackingTransform = false;
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
trackingTransform, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsTrackingTransform);
|
||||
|
||||
EXPECT_THAT(trackingTransform, ::testing::IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, InterpolateToTransform)
|
||||
@@ -169,8 +148,10 @@ namespace UnitTest
|
||||
transformToInterpolateTo);
|
||||
|
||||
// simulate interpolation
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
m_controllerList->UpdateViewport(
|
||||
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
|
||||
m_controllerList->UpdateViewport(
|
||||
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
|
||||
|
||||
const auto finalTransform = m_cameraViewportContextView->GetCameraTransform();
|
||||
|
||||
@@ -184,7 +165,7 @@ namespace UnitTest
|
||||
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, referenceFrame);
|
||||
|
||||
AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f));
|
||||
@@ -195,18 +176,85 @@ namespace UnitTest
|
||||
transformToInterpolateTo);
|
||||
|
||||
// simulate interpolation
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
|
||||
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
currentReferenceFrame, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
m_controllerList->UpdateViewport(
|
||||
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
|
||||
m_controllerList->UpdateViewport(
|
||||
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
|
||||
|
||||
const auto finalTransform = m_cameraViewportContextView->GetCameraTransform();
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo));
|
||||
EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity()));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, BeginningCameraInterpolationReturnsTrue)
|
||||
{
|
||||
// Given/When
|
||||
bool interpolationBegan = false;
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
interpolationBegan, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(interpolationBegan, ::testing::IsTrue());
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, CameraInterpolationDoesNotBeginDuringAnExistingInterpolation)
|
||||
{
|
||||
// Given/When
|
||||
bool initialInterpolationBegan = false;
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
initialInterpolationBegan, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
|
||||
|
||||
m_controllerList->UpdateViewport(
|
||||
{ TestViewportId, AzFramework::FloatSeconds(HalfInterpolateToTransformDuration), AZ::ScriptTimePoint() });
|
||||
|
||||
bool nextInterpolationBegan = true;
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
nextInterpolationBegan, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
|
||||
|
||||
bool interpolating = false;
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
interpolating, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsInterpolating);
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(initialInterpolationBegan, ::testing::IsTrue());
|
||||
EXPECT_THAT(nextInterpolationBegan, ::testing::IsFalse());
|
||||
EXPECT_THAT(interpolating, ::testing::IsTrue());
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, CameraInterpolationCanBeginAfterAnInterpolationCompletes)
|
||||
{
|
||||
// Given/When
|
||||
bool initialInterpolationBegan = false;
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
initialInterpolationBegan, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
|
||||
|
||||
m_controllerList->UpdateViewport(
|
||||
{ TestViewportId,
|
||||
AzFramework::FloatSeconds(AtomToolsFramework::ModularViewportCameraControllerRequests::InterpolateToTransformDuration + 0.5f),
|
||||
AZ::ScriptTimePoint() });
|
||||
|
||||
bool interpolating = true;
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
interpolating, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsInterpolating);
|
||||
|
||||
bool nextInterpolationBegan = false;
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
nextInterpolationBegan, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 10.0f)));
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(initialInterpolationBegan, ::testing::IsTrue());
|
||||
EXPECT_THAT(interpolating, ::testing::IsFalse());
|
||||
EXPECT_THAT(nextInterpolationBegan, ::testing::IsTrue());
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <Mocks/ISystemMock.h>
|
||||
#include <Mocks/IConsoleMock.h>
|
||||
#include <Mocks/ILogMock.h>
|
||||
#include <Mocks/ITimerMock.h>
|
||||
#include <Mocks/IConsoleMock.h>
|
||||
#include "IEditorMock.h"
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace UnitTest
|
||||
class ModularViewportCameraControllerFixture : public AllocatorsTestFixture
|
||||
{
|
||||
public:
|
||||
static const AzFramework::ViewportId TestViewportId;
|
||||
static inline constexpr AzFramework::ViewportId TestViewportId = 1234;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
@@ -146,6 +146,17 @@ namespace UnitTest
|
||||
controller->SetCameraPropsBuilderCallback(
|
||||
[](AzFramework::CameraProps& cameraProps)
|
||||
{
|
||||
// note: rotateSmoothness is also used for roll (not related to camera input directly)
|
||||
cameraProps.m_rotateSmoothnessFn = []
|
||||
{
|
||||
return 5.0f;
|
||||
};
|
||||
|
||||
cameraProps.m_translateSmoothnessFn = []
|
||||
{
|
||||
return 5.0f;
|
||||
};
|
||||
|
||||
cameraProps.m_rotateSmoothingEnabledFn = []
|
||||
{
|
||||
return false;
|
||||
@@ -209,8 +220,6 @@ namespace UnitTest
|
||||
AZStd::unique_ptr<SandboxEditor::EditorModularViewportCameraComposer> m_editorModularViewportCameraComposer;
|
||||
};
|
||||
|
||||
const AzFramework::ViewportId ModularViewportCameraControllerFixture::TestViewportId = AzFramework::ViewportId(0);
|
||||
|
||||
TEST_F(ModularViewportCameraControllerFixture, MouseMovementDoesNotAccumulateExcessiveDriftInModularViewportCameraWithVaryingDeltaTime)
|
||||
{
|
||||
SandboxEditor::SetCameraCaptureCursorForLook(false);
|
||||
@@ -380,6 +389,7 @@ namespace UnitTest
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
|
||||
|
||||
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::RightButton, Qt::NoModifier, start + mouseDelta);
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
|
||||
|
||||
// update the position of the widget
|
||||
const auto offset = QPoint(500, 500);
|
||||
|
||||
@@ -94,6 +94,9 @@ namespace TrackViewPythonBindingsUnitTests
|
||||
|
||||
m_app.Start(appDesc);
|
||||
m_app.RegisterComponentDescriptor(AzToolsFramework::TrackViewComponent::CreateDescriptor());
|
||||
|
||||
// Disable saving global user settings to prevent failure due to detecting file updates
|
||||
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
|
||||
#include <Editor/ViewportManipulatorController.h>
|
||||
#include <Mocks/MockWindowRequests.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
@@ -77,14 +78,15 @@ namespace UnitTest
|
||||
class ViewportManipulatorControllerFixture : public AllocatorsTestFixture
|
||||
{
|
||||
public:
|
||||
static const AzFramework::ViewportId TestViewportId;
|
||||
static inline constexpr AzFramework::ViewportId TestViewportId = 1234;
|
||||
static inline const QSize WidgetSize = QSize(1920, 1080);
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
AllocatorsTestFixture::SetUp();
|
||||
|
||||
m_rootWidget = AZStd::make_unique<QWidget>();
|
||||
m_rootWidget->setFixedSize(QSize(100, 100));
|
||||
m_rootWidget->setFixedSize(WidgetSize);
|
||||
QApplication::setActiveWindow(m_rootWidget.get());
|
||||
|
||||
m_controllerList = AZStd::make_shared<AzFramework::ViewportControllerList>();
|
||||
@@ -111,8 +113,6 @@ namespace UnitTest
|
||||
AZStd::unique_ptr<AzToolsFramework::QtEventToAzInputMapper> m_inputChannelMapper;
|
||||
};
|
||||
|
||||
const AzFramework::ViewportId ViewportManipulatorControllerFixture::TestViewportId = AzFramework::ViewportId(0);
|
||||
|
||||
TEST_F(ViewportManipulatorControllerFixture, AnEventIsNotPropagatedToTheViewportWhenAManipulatorHandlesItFirst)
|
||||
{
|
||||
// forward input events to our controller list
|
||||
@@ -227,4 +227,74 @@ namespace UnitTest
|
||||
// the key was released (cleared)
|
||||
EXPECT_TRUE(endedEvent);
|
||||
}
|
||||
|
||||
TEST_F(ViewportManipulatorControllerFixture, DoubleClickIsNotRegisteredIfMouseDeltaHasMovedMoreThanDeadzoneInClickInterval)
|
||||
{
|
||||
AzFramework::NativeWindowHandle nativeWindowHandle = nullptr;
|
||||
|
||||
// forward input events to our controller list
|
||||
QObject::connect(
|
||||
m_inputChannelMapper.get(), &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, m_rootWidget.get(),
|
||||
[this, nativeWindowHandle](const AzFramework::InputChannel* inputChannel, [[maybe_unused]] QEvent* event)
|
||||
{
|
||||
m_controllerList->HandleInputChannelEvent(
|
||||
AzFramework::ViewportControllerInputEvent{ TestViewportId, nativeWindowHandle, *inputChannel });
|
||||
});
|
||||
|
||||
::testing::NiceMock<MockWindowRequests> mockWindowRequests;
|
||||
mockWindowRequests.Connect(nativeWindowHandle);
|
||||
|
||||
using ::testing::Return;
|
||||
// note: WindowRequests is used internally by ViewportManipulatorController
|
||||
ON_CALL(mockWindowRequests, GetClientAreaSize())
|
||||
.WillByDefault(Return(AzFramework::WindowSize(WidgetSize.width(), WidgetSize.height())));
|
||||
|
||||
EditorInteractionViewportSelectionFake editorInteractionViewportFake;
|
||||
editorInteractionViewportFake.m_internalHandleMouseManipulatorInteraction = [](const MouseInteractionEvent&)
|
||||
{
|
||||
// report the event was not handled (manipulator was not interacted with)
|
||||
return false;
|
||||
};
|
||||
|
||||
bool doubleClickDetected = false;
|
||||
editorInteractionViewportFake.m_internalHandleMouseViewportInteraction =
|
||||
[&doubleClickDetected](const MouseInteractionEvent& mouseInteractionEvent)
|
||||
{
|
||||
// ensure no double click event is detected with the given inputs below
|
||||
if (mouseInteractionEvent.m_mouseEvent == AzToolsFramework::ViewportInteraction::MouseEvent::DoubleClick)
|
||||
{
|
||||
doubleClickDetected = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
editorInteractionViewportFake.Connect();
|
||||
|
||||
m_controllerList->Add(AZStd::make_shared<SandboxEditor::ViewportManipulatorController>());
|
||||
|
||||
// simulate a click, move, click
|
||||
MouseMove(m_rootWidget.get(), QPoint(0, 0), QPoint(10, 10));
|
||||
MousePressAndMove(m_rootWidget.get(), QPoint(10, 10), QPoint(0, 0), Qt::MouseButton::LeftButton);
|
||||
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier, QPoint(10, 10));
|
||||
MouseMove(m_rootWidget.get(), QPoint(10, 10), QPoint(20, 20));
|
||||
MousePressAndMove(m_rootWidget.get(), QPoint(20, 20), QPoint(0, 0), Qt::MouseButton::LeftButton);
|
||||
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier, QPoint(20, 20));
|
||||
|
||||
// ensure no double click was detected
|
||||
EXPECT_FALSE(doubleClickDetected);
|
||||
|
||||
// simulate double click (sanity check it still is detected correctly with no movement)
|
||||
MouseMove(m_rootWidget.get(), QPoint(0, 0), QPoint(10, 10));
|
||||
MousePressAndMove(m_rootWidget.get(), QPoint(10, 10), QPoint(0, 0), Qt::MouseButton::LeftButton);
|
||||
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier, QPoint(10, 10));
|
||||
MousePressAndMove(m_rootWidget.get(), QPoint(10, 10), QPoint(0, 0), Qt::MouseButton::LeftButton);
|
||||
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier, QPoint(10, 10));
|
||||
|
||||
// ensure a double click was detected
|
||||
EXPECT_TRUE(doubleClickDetected);
|
||||
|
||||
mockWindowRequests.Disconnect();
|
||||
editorInteractionViewportFake.Disconnect();
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
+17
-31
@@ -519,7 +519,7 @@ MainWindow* MainWindow::instance()
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
gSettings.Save();
|
||||
gSettings.Save(true);
|
||||
|
||||
AzFramework::SystemCursorState currentCursorState;
|
||||
bool isInGameMode = false;
|
||||
@@ -708,14 +708,10 @@ void MainWindow::InitActions()
|
||||
.SetShortcut(QKeySequence::Undo)
|
||||
.SetReserved()
|
||||
.SetStatusTip(tr("Undo last operation"))
|
||||
//.SetMenu(new QMenu("FIXME"))
|
||||
.SetApplyHoverEffect()
|
||||
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateUndo);
|
||||
am->AddAction(ID_REDO, tr("&Redo"))
|
||||
.SetShortcut(AzQtComponents::RedoKeySequence)
|
||||
.SetReserved()
|
||||
//.SetMenu(new QMenu("FIXME"))
|
||||
.SetApplyHoverEffect()
|
||||
.SetStatusTip(tr("Redo last undo operation"))
|
||||
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateRedo);
|
||||
|
||||
@@ -731,7 +727,6 @@ void MainWindow::InitActions()
|
||||
// Modify actions
|
||||
am->AddAction(AzToolsFramework::EditModeMove, tr("Move"))
|
||||
.SetIcon(Style::icon("Move"))
|
||||
.SetApplyHoverEffect()
|
||||
.SetShortcut(tr("1"))
|
||||
.SetToolTip(tr("Move (1)"))
|
||||
.SetCheckable(true)
|
||||
@@ -757,7 +752,6 @@ void MainWindow::InitActions()
|
||||
});
|
||||
am->AddAction(AzToolsFramework::EditModeRotate, tr("Rotate"))
|
||||
.SetIcon(Style::icon("Translate"))
|
||||
.SetApplyHoverEffect()
|
||||
.SetShortcut(tr("2"))
|
||||
.SetToolTip(tr("Rotate (2)"))
|
||||
.SetCheckable(true)
|
||||
@@ -783,7 +777,6 @@ void MainWindow::InitActions()
|
||||
});
|
||||
am->AddAction(AzToolsFramework::EditModeScale, tr("Scale"))
|
||||
.SetIcon(Style::icon("Scale"))
|
||||
.SetApplyHoverEffect()
|
||||
.SetShortcut(tr("3"))
|
||||
.SetToolTip(tr("Scale (3)"))
|
||||
.SetCheckable(true)
|
||||
@@ -808,7 +801,6 @@ void MainWindow::InitActions()
|
||||
|
||||
am->AddAction(AzToolsFramework::SnapToGrid, tr("Snap to grid"))
|
||||
.SetIcon(Style::icon("Grid"))
|
||||
.SetApplyHoverEffect()
|
||||
.SetShortcut(tr("G"))
|
||||
.SetToolTip(tr("Snap to grid (G)"))
|
||||
.SetStatusTip(tr("Toggles snap to grid"))
|
||||
@@ -821,7 +813,6 @@ void MainWindow::InitActions()
|
||||
|
||||
am->AddAction(AzToolsFramework::SnapAngle, tr("Snap angle"))
|
||||
.SetIcon(Style::icon("Angle"))
|
||||
.SetApplyHoverEffect()
|
||||
.SetStatusTip(tr("Snap angle"))
|
||||
.SetCheckable(true)
|
||||
.RegisterUpdateCallback([](QAction* action) {
|
||||
@@ -939,29 +930,28 @@ void MainWindow::InitActions()
|
||||
.Connect(&QAction::triggered, this, &MainWindow::OnRefreshAudioSystem);
|
||||
|
||||
// Game actions
|
||||
am->AddAction(ID_VIEW_SWITCHTOGAME, tr("Play &Game"))
|
||||
am->AddAction(ID_VIEW_SWITCHTOGAME, tr("Play Game"))
|
||||
.SetIcon(QIcon(":/stylesheet/img/UI20/toolbar/Play.svg"))
|
||||
.SetToolTip(tr("Play Game"))
|
||||
.SetStatusTip(tr("Activate the game input mode"))
|
||||
.SetCheckable(true)
|
||||
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdatePlayGame);
|
||||
am->AddAction(ID_VIEW_SWITCHTOGAME_VIEWPORT, tr("Play Game"))
|
||||
.SetShortcut(tr("Ctrl+G"))
|
||||
.SetToolTip(tr("Play Game (Ctrl+G)"))
|
||||
.SetStatusTip(tr("Activate the game input mode"))
|
||||
.SetApplyHoverEffect()
|
||||
.SetCheckable(true)
|
||||
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdatePlayGame);
|
||||
am->AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, tr("Play &Game (Maximized)"))
|
||||
am->AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, tr("Play Game (Maximized)"))
|
||||
.SetShortcut(tr("Ctrl+Shift+G"))
|
||||
.SetStatusTip(tr("Activate the game input mode (maximized)"))
|
||||
.SetIcon(Style::icon("Play"))
|
||||
.SetApplyHoverEffect()
|
||||
.SetCheckable(true);
|
||||
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdatePlayGame);
|
||||
am->AddAction(ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL, tr("Play Controls"))
|
||||
.SetText(tr("Play Controls"));
|
||||
am->AddAction(ID_SWITCH_PHYSICS, tr("Simulate"))
|
||||
.SetIcon(QIcon(":/stylesheet/img/UI20/toolbar/Simulate_Physics.svg"))
|
||||
.SetShortcut(tr("Ctrl+P"))
|
||||
.SetToolTip(tr("Simulate (Ctrl+P)"))
|
||||
.SetCheckable(true)
|
||||
.SetStatusTip(tr("Enable processing of Physics and AI."))
|
||||
.SetApplyHoverEffect()
|
||||
.SetCheckable(true)
|
||||
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnSwitchPhysicsUpdate);
|
||||
am->AddAction(ID_GAME_SYNCPLAYER, tr("Move Player and Camera Separately")).SetCheckable(true)
|
||||
@@ -1051,8 +1041,7 @@ void MainWindow::InitActions()
|
||||
|
||||
// Editors Toolbar actions
|
||||
am->AddAction(ID_OPEN_ASSET_BROWSER, tr("Asset browser"))
|
||||
.SetToolTip(tr("Open Asset Browser"))
|
||||
.SetApplyHoverEffect();
|
||||
.SetToolTip(tr("Open Asset Browser"));
|
||||
|
||||
AZ::EBusReduceResult<bool, AZStd::logical_or<bool>> emfxEnabled(false);
|
||||
using AnimationRequestBus = AzToolsFramework::EditorAnimationSystemRequestsBus;
|
||||
@@ -1062,8 +1051,7 @@ void MainWindow::InitActions()
|
||||
{
|
||||
QAction* action = am->AddAction(ID_OPEN_EMOTIONFX_EDITOR, tr("Animation Editor"))
|
||||
.SetToolTip(tr("Open Animation Editor"))
|
||||
.SetIcon(QIcon(":/EMotionFX/EMFX_icon_32x32.png"))
|
||||
.SetApplyHoverEffect();
|
||||
.SetIcon(QIcon(":/EMotionFX/EMFX_icon_32x32.png"));
|
||||
QObject::connect(action, &QAction::triggered, this, []() {
|
||||
QtViewPaneManager::instance()->OpenPane(LyViewPane::AnimationEditor);
|
||||
});
|
||||
@@ -1071,12 +1059,10 @@ void MainWindow::InitActions()
|
||||
|
||||
am->AddAction(ID_OPEN_AUDIO_CONTROLS_BROWSER, tr("Audio Controls Editor"))
|
||||
.SetToolTip(tr("Open Audio Controls Editor"))
|
||||
.SetIcon(Style::icon("Audio"))
|
||||
.SetApplyHoverEffect();
|
||||
.SetIcon(Style::icon("Audio"));
|
||||
|
||||
am->AddAction(ID_OPEN_UICANVASEDITOR, tr(LyViewPane::UiEditor))
|
||||
.SetToolTip(tr("Open UI Editor"))
|
||||
.SetApplyHoverEffect();
|
||||
.SetToolTip(tr("Open UI Editor"));
|
||||
|
||||
// Edit Mode Toolbar Actions
|
||||
am->AddAction(IDC_SELECTION_MASK, tr("Selected Object Types"));
|
||||
@@ -1089,12 +1075,10 @@ void MainWindow::InitActions()
|
||||
// Object Toolbar Actions
|
||||
am->AddAction(ID_GOTO_SELECTED, tr("Go to selected object"))
|
||||
.SetIcon(Style::icon("select_object"))
|
||||
.SetApplyHoverEffect()
|
||||
.Connect(&QAction::triggered, this, &MainWindow::OnGotoSelected);
|
||||
|
||||
// Misc Toolbar Actions
|
||||
am->AddAction(ID_OPEN_SUBSTANCE_EDITOR, tr("Open Substance Editor"))
|
||||
.SetApplyHoverEffect();
|
||||
am->AddAction(ID_OPEN_SUBSTANCE_EDITOR, tr("Open Substance Editor"));
|
||||
}
|
||||
|
||||
void MainWindow::InitToolActionHandlers()
|
||||
@@ -1266,7 +1250,9 @@ void MainWindow::OnGameModeChanged(bool inGameMode)
|
||||
// block signals on the switch to game actions before setting the checked state, as
|
||||
// setting the checked state triggers the action, which will re-enter this function
|
||||
// and result in an infinite loop
|
||||
AZStd::vector<QAction*> actions = { m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME), m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN) };
|
||||
AZStd::vector<QAction*> actions = { m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_VIEWPORT),
|
||||
m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN),
|
||||
m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME)};
|
||||
for (auto action : actions)
|
||||
{
|
||||
action->blockSignals(true);
|
||||
|
||||
@@ -953,14 +953,8 @@ void CBaseObject::DrawTextureIcon(DisplayContext& dc, [[maybe_unused]] const Vec
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CBaseObject::DrawWarningIcons(DisplayContext& dc, const Vec3& pos)
|
||||
void CBaseObject::DrawWarningIcons(DisplayContext& dc, const Vec3&)
|
||||
{
|
||||
// Don't draw warning icons if they are beyond draw distance
|
||||
if ((dc.camera->GetPosition() - pos).GetLength() > gSettings.viewports.fWarningIconsDrawDistance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (gSettings.viewports.bShowIcons || gSettings.viewports.bShowSizeBasedIcons)
|
||||
{
|
||||
const int warningIconSizeX = OBJECT_TEXTURE_ICON_SIZEX / 2;
|
||||
@@ -1010,11 +1004,8 @@ void CBaseObject::DrawLabel(DisplayContext& dc, const Vec3& pos, const QColor& l
|
||||
labelColor = QColor(0, 0, 0);
|
||||
}
|
||||
|
||||
float camDist = dc.camera->GetPosition().GetDistance(pos);
|
||||
float maxDist = dc.settings->GetLabelsDistance();
|
||||
if (camDist < dc.settings->GetLabelsDistance() || (dc.flags & DISPLAY_SELECTION_HELPERS))
|
||||
if (dc.flags & DISPLAY_SELECTION_HELPERS)
|
||||
{
|
||||
float range = maxDist / 2.0f;
|
||||
Vec3 c(static_cast<f32>(labelColor.redF()), static_cast<f32>(labelColor.greenF()), static_cast<f32>(labelColor.redF()));
|
||||
if (IsSelected())
|
||||
{
|
||||
@@ -1032,10 +1023,6 @@ void CBaseObject::DrawLabel(DisplayContext& dc, const Vec3& pos, const QColor& l
|
||||
col[1] = c.y;
|
||||
col[2] = c.z;
|
||||
}
|
||||
else if (camDist > range)
|
||||
{
|
||||
col[3] = col[3] * (1.0f - (camDist - range) / range);
|
||||
}
|
||||
|
||||
dc.SetColor(col[0], col[1], col[2], col[3] * alpha);
|
||||
dc.DrawTextLabel(pos, size, GetName().toUtf8().data());
|
||||
@@ -1196,33 +1183,6 @@ bool CBaseObject::CanBeDrawn(const DisplayContext& dc, bool& outDisplaySelection
|
||||
return bResult;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBaseObject::IsInCameraView(const CCamera& camera)
|
||||
{
|
||||
AABB bbox;
|
||||
GetBoundBox(bbox);
|
||||
return (camera.IsAABBVisible_F(AABB(bbox.min, bbox.max)));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
float CBaseObject::GetCameraVisRatio(const CCamera& camera)
|
||||
{
|
||||
AABB bbox;
|
||||
GetBoundBox(bbox);
|
||||
|
||||
static const float defaultVisRatio = 1000.0f;
|
||||
|
||||
const float objectHeightSq = max(1.0f, (bbox.max - bbox.min).GetLengthSquared());
|
||||
const float camdistSq = (bbox.min - camera.GetPosition()).GetLengthSquared();
|
||||
float visRatio = defaultVisRatio;
|
||||
if (camdistSq > FLT_EPSILON)
|
||||
{
|
||||
visRatio = objectHeightSq / camdistSq;
|
||||
}
|
||||
|
||||
return visRatio;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
int CBaseObject::MouseCreateCallback(CViewport* view, EMouseEvent event, QPoint& point, int flags)
|
||||
{
|
||||
@@ -1689,7 +1649,7 @@ QString CBaseObject::GetTypeName() const
|
||||
}
|
||||
|
||||
QString name;
|
||||
name.append(className.mid(0, className.length() - subClassName.length()));
|
||||
name.append(className.midRef(0, className.length() - subClassName.length()));
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
@@ -674,11 +674,6 @@ protected:
|
||||
//! Returns if the object can be drawn, and if its selection helper should also be drawn.
|
||||
bool CanBeDrawn(const DisplayContext& dc, bool& outDisplaySelectionHelper) const;
|
||||
|
||||
//! Returns if object is in the camera view.
|
||||
virtual bool IsInCameraView(const CCamera& camera);
|
||||
//! Returns vis ratio of object in camera
|
||||
virtual float GetCameraVisRatio(const CCamera& camera);
|
||||
|
||||
// Do basic intersection tests
|
||||
virtual bool IntersectRectBounds(const AABB& bbox);
|
||||
virtual bool IntersectRayBounds(const Ray& ray);
|
||||
|
||||
@@ -31,7 +31,6 @@ struct IRenderer;
|
||||
struct IRenderAuxGeom;
|
||||
struct IIconManager;
|
||||
class CDisplaySettings;
|
||||
class CCamera;
|
||||
class QPoint;
|
||||
|
||||
enum DisplayFlags
|
||||
@@ -66,7 +65,6 @@ struct SANDBOX_API DisplayContext
|
||||
IDisplayViewport* view;
|
||||
IRenderAuxGeom* pRenderAuxGeom;
|
||||
IIconManager* pIconManager;
|
||||
CCamera* camera;
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
AABB box; // Bounding box of volume that need to be repainted.
|
||||
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
|
||||
@@ -1138,10 +1138,6 @@ bool DisplayContext::IsVisible(const AABB& bounds)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return camera->IsAABBVisible_F(AABB(bounds.min, bounds.max));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -592,11 +592,11 @@ void CEntityObject::AdjustLightProperties(CVarBlockPtr& properties, const char*
|
||||
if (IVariable* pCastShadowVarLegacy = FindVariableInSubBlock(properties, pSubBlockVar, "bCastShadow"))
|
||||
{
|
||||
pCastShadowVarLegacy->SetFlags(pCastShadowVarLegacy->GetFlags() | IVariable::UI_INVISIBLE);
|
||||
|
||||
if (pCastShadowVarLegacy->GetDisplayValue()[0] != '0')
|
||||
const QString zeroPrefix("0");
|
||||
if (!pCastShadowVarLegacy->GetDisplayValue().startsWith(zeroPrefix))
|
||||
{
|
||||
bCastShadowLegacy = true;
|
||||
pCastShadowVarLegacy->SetDisplayValue("0");
|
||||
pCastShadowVarLegacy->SetDisplayValue(zeroPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -956,11 +956,7 @@ void CEntityObject::Serialize(CObjectArchive& ar)
|
||||
QString attachmentType;
|
||||
xmlNode->getAttr("AttachmentType", attachmentType);
|
||||
|
||||
if (attachmentType == "GeomCacheNode")
|
||||
{
|
||||
m_attachmentType = eAT_GeomCacheNode;
|
||||
}
|
||||
else if (attachmentType == "CharacterBone")
|
||||
if (attachmentType == "CharacterBone")
|
||||
{
|
||||
m_attachmentType = eAT_CharacterBone;
|
||||
}
|
||||
@@ -987,11 +983,7 @@ void CEntityObject::Serialize(CObjectArchive& ar)
|
||||
{
|
||||
if (m_attachmentType != eAT_Pivot)
|
||||
{
|
||||
if (m_attachmentType == eAT_GeomCacheNode)
|
||||
{
|
||||
xmlNode->setAttr("AttachmentType", "GeomCacheNode");
|
||||
}
|
||||
else if (m_attachmentType == eAT_CharacterBone)
|
||||
if (m_attachmentType == eAT_CharacterBone)
|
||||
{
|
||||
xmlNode->setAttr("AttachmentType", "CharacterBone");
|
||||
}
|
||||
@@ -1091,11 +1083,7 @@ XmlNodeRef CEntityObject::Export([[maybe_unused]] const QString& levelPath, XmlN
|
||||
objNode->setAttr("ParentId", parentEntity->GetEntityId());
|
||||
if (m_attachmentType != eAT_Pivot)
|
||||
{
|
||||
if (m_attachmentType == eAT_GeomCacheNode)
|
||||
{
|
||||
objNode->setAttr("AttachmentType", "GeomCacheNode");
|
||||
}
|
||||
else if (m_attachmentType == eAT_CharacterBone)
|
||||
if (m_attachmentType == eAT_CharacterBone)
|
||||
{
|
||||
objNode->setAttr("AttachmentType", "CharacterBone");
|
||||
}
|
||||
|
||||
@@ -131,7 +131,6 @@ public:
|
||||
enum EAttachmentType
|
||||
{
|
||||
eAT_Pivot,
|
||||
eAT_GeomCacheNode,
|
||||
eAT_CharacterBone,
|
||||
};
|
||||
|
||||
|
||||
@@ -98,16 +98,8 @@ void CLineGizmo::Display(DisplayContext& dc)
|
||||
Vec3 pos = 0.5f * (m_point[0] + m_point[1]);
|
||||
//dc.renderer->DrawLabelEx( p3+Vec3(0,0,0.3f),1.2f,col,true,true,m_name );
|
||||
|
||||
float camDist = dc.camera->GetPosition().GetDistance(pos);
|
||||
float maxDist = dc.settings->GetLabelsDistance();
|
||||
if (camDist < dc.settings->GetLabelsDistance())
|
||||
{
|
||||
float range = maxDist / 2.0f;
|
||||
float col[4] = { m_color[0].r, m_color[0].g, m_color[0].b, m_color[0].a };
|
||||
if (camDist > range)
|
||||
{
|
||||
col[3] = col[3] * (1.0f - (camDist - range) / range);
|
||||
}
|
||||
dc.SetColor(col[0], col[1], col[2], col[3]);
|
||||
dc.DrawTextLabel(pos + Vec3(0, 0, 0.2f), 1.2f, m_name.toUtf8().data());
|
||||
}
|
||||
|
||||
@@ -608,7 +608,7 @@ bool CObjectManager::AddObject(CBaseObject* obj)
|
||||
if (CEntityObject* entityObj = qobject_cast<CEntityObject*>(obj))
|
||||
{
|
||||
CEntityObject::EAttachmentType attachType = entityObj->GetAttachType();
|
||||
if (attachType == CEntityObject::EAttachmentType::eAT_GeomCacheNode || attachType == CEntityObject::EAttachmentType::eAT_CharacterBone)
|
||||
if (attachType == CEntityObject::EAttachmentType::eAT_CharacterBone)
|
||||
{
|
||||
m_animatedAttachedEntities.insert(entityObj);
|
||||
}
|
||||
@@ -828,7 +828,7 @@ void CObjectManager::ShowLastHiddenObject()
|
||||
{
|
||||
uint64 mostRecentID = CBaseObject::s_invalidHiddenID;
|
||||
CBaseObject* mostRecentObject = nullptr;
|
||||
for (auto it : m_objects)
|
||||
for (const auto& it : m_objects)
|
||||
{
|
||||
CBaseObject* obj = it.second;
|
||||
|
||||
@@ -1488,11 +1488,7 @@ bool CObjectManager::HitTestObject(CBaseObject* obj, HitContext& hc)
|
||||
if (!bSelectionHelperHit)
|
||||
{
|
||||
// Fast checking.
|
||||
if (hc.camera && !obj->IsInCameraView(*hc.camera))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (hc.bounds && !obj->IntersectRectBounds(*hc.bounds))
|
||||
if (hc.bounds && !obj->IntersectRectBounds(*hc.bounds))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/RTTI/AttributeReader.h>
|
||||
@@ -57,6 +56,7 @@
|
||||
#include <AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx>
|
||||
#include <AzToolsFramework/UI/Layer/NameConflictWarning.hxx>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorHelpers.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
#include <MathConversion.h>
|
||||
|
||||
#include <Atom/RPI.Public/ViewportContext.h>
|
||||
@@ -1394,13 +1394,13 @@ void SandboxIntegrationManager::ContextMenu_NewEntity()
|
||||
{
|
||||
AZ::Vector3 worldPosition = AZ::Vector3::CreateZero();
|
||||
|
||||
CViewport* view = GetIEditor()->GetViewManager()->GetGameViewport();
|
||||
// If we don't have a viewport active to aid in placement, the object
|
||||
// will be created at the origin.
|
||||
if (view)
|
||||
if (CViewport* view = GetIEditor()->GetViewManager()->GetGameViewport())
|
||||
{
|
||||
const QPoint viewPoint(static_cast<int>(m_contextMenuViewPoint.GetX()), static_cast<int>(m_contextMenuViewPoint.GetY()));
|
||||
worldPosition = view->GetHitLocation(viewPoint);
|
||||
worldPosition = AzToolsFramework::FindClosestPickIntersection(
|
||||
view->GetViewportId(), AzFramework::ScreenPointFromVector2(m_contextMenuViewPoint), AzToolsFramework::EditorPickRayLength,
|
||||
GetDefaultEntityPlacementDistance());
|
||||
}
|
||||
|
||||
CreateNewEntityAtPosition(worldPosition);
|
||||
@@ -1675,6 +1675,12 @@ void SandboxIntegrationManager::GoToEntitiesInViewports(const AzToolsFramework::
|
||||
if (auto viewportContext = viewportContextManager->GetViewportContextById(viewIndex))
|
||||
{
|
||||
const AZ::Transform cameraTransform = viewportContext->GetCameraTransform();
|
||||
// do not attempt to interpolate to where we currently are
|
||||
if (cameraTransform.GetTranslation().IsClose(center))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const AZ::Vector3 forward = (center - cameraTransform.GetTranslation()).GetNormalized();
|
||||
|
||||
// move camera 25% further back than required
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include <LmbrCentral/Rendering/LensFlareAsset.h>
|
||||
#include <LmbrCentral/Rendering/MeshAsset.h>
|
||||
#include <LmbrCentral/Rendering/MaterialAsset.h>
|
||||
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/RTTI/TypeInfo.h>
|
||||
@@ -136,14 +135,6 @@ AssetCatalogModel::AssetCatalogModel(QObject* parent)
|
||||
}
|
||||
}
|
||||
|
||||
// Special cases for SimpleAssets. If these get full-fledged AssetData types, these cases can be removed.
|
||||
QString textureExtensions = LmbrCentral::TextureAsset::GetFileFilter();
|
||||
m_extensionToAssetType.insert(AZStd::make_pair(textureExtensions.replace("*", "").replace(" ", "").toStdString().c_str(), AZStd::vector<AZ::Uuid> { AZ::AzTypeInfo<LmbrCentral::TextureAsset>::Uuid() }));
|
||||
QString materialExtensions = LmbrCentral::MaterialAsset::GetFileFilter();
|
||||
m_extensionToAssetType.insert(AZStd::make_pair(materialExtensions.replace("*", "").replace(" ", "").toStdString().c_str(), AZStd::vector<AZ::Uuid> { AZ::AzTypeInfo<LmbrCentral::MaterialAsset>::Uuid() }));
|
||||
QString dccMaterialExtensions = LmbrCentral::DccMaterialAsset::GetFileFilter();
|
||||
m_extensionToAssetType.insert(AZStd::make_pair(dccMaterialExtensions.replace("*", "").replace(" ", "").toStdString().c_str(), AZStd::vector<AZ::Uuid> { AZ::AzTypeInfo<LmbrCentral::DccMaterialAsset>::Uuid() }));
|
||||
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
EBUS_EVENT_RESULT(serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
|
||||
AZ_Assert(serializeContext, "Failed to acquire application serialize context.");
|
||||
|
||||
@@ -2640,7 +2640,7 @@ QSize OutlinerItemDelegate::sizeHint(const QStyleOptionViewItem& option, const Q
|
||||
m_cachedBoundingRectOfTallCharacter = QRect();
|
||||
};
|
||||
|
||||
QTimer::singleShot(0, resetFunction);
|
||||
QTimer::singleShot(0, this, resetFunction);
|
||||
}
|
||||
|
||||
// And add 8 to it gives the outliner roughly the visible spacing we're looking for.
|
||||
|
||||
@@ -121,6 +121,18 @@ namespace
|
||||
SortEntityChildrenRecursively(childId, comparer);
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex nextIndexForTree(bool direction, OutlinerTreeView *tree, QModelIndex current)
|
||||
{
|
||||
if (direction)
|
||||
{
|
||||
return tree->indexAbove(current);
|
||||
}
|
||||
else
|
||||
{
|
||||
return tree->indexBelow(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OutlinerWidget::OutlinerWidget(QWidget* pParent, Qt::WindowFlags flags)
|
||||
@@ -891,9 +903,7 @@ void OutlinerWidget::DoSelectSliceRootNextToSelection(bool isTraversalUpwards)
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::function<QModelIndex(QModelIndex)> getNextIdxFunction =
|
||||
AZStd::bind(isTraversalUpwards ? &QTreeView::indexAbove : &QTreeView::indexBelow, treeView, AZStd::placeholders::_1);
|
||||
QModelIndex nextIdx = getNextIdxFunction(currentIdx);
|
||||
QModelIndex nextIdx = nextIndexForTree(isTraversalUpwards,treeView,currentIdx);
|
||||
bool foundSliceRoot = false;
|
||||
|
||||
while (nextIdx.isValid() && !foundSliceRoot)
|
||||
@@ -904,7 +914,7 @@ void OutlinerWidget::DoSelectSliceRootNextToSelection(bool isTraversalUpwards)
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
|
||||
foundSliceRoot, &AzToolsFramework::ToolsApplicationRequests::IsSliceRootEntity, currentEntityId);
|
||||
|
||||
nextIdx = getNextIdxFunction(currentIdx);
|
||||
nextIdx = nextIndexForTree(isTraversalUpwards, treeView, currentIdx);
|
||||
}
|
||||
|
||||
if (foundSliceRoot)
|
||||
@@ -934,13 +944,10 @@ void OutlinerWidget::DoSelectEdgeSliceRoot(bool shouldSelectTopMostSlice)
|
||||
}
|
||||
|
||||
QModelIndex currentIdx;
|
||||
AZStd::function<QModelIndex(QModelIndex)> getNextIdxFunction;
|
||||
|
||||
if (shouldSelectTopMostSlice)
|
||||
{
|
||||
currentIdx = itemModel->index(0, OutlinerListModel::ColumnName);
|
||||
|
||||
getNextIdxFunction =
|
||||
AZStd::bind(&QTreeView::indexBelow, treeView, AZStd::placeholders::_1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -949,9 +956,6 @@ void OutlinerWidget::DoSelectEdgeSliceRoot(bool shouldSelectTopMostSlice)
|
||||
{
|
||||
currentIdx = itemModel->index(itemModel->rowCount(currentIdx) - 1, OutlinerListModel::ColumnName, currentIdx);
|
||||
}
|
||||
|
||||
getNextIdxFunction =
|
||||
AZStd::bind(&QTreeView::indexAbove, treeView, AZStd::placeholders::_1);
|
||||
}
|
||||
|
||||
QModelIndex nextIdx = currentIdx;
|
||||
@@ -964,7 +968,7 @@ void OutlinerWidget::DoSelectEdgeSliceRoot(bool shouldSelectTopMostSlice)
|
||||
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
|
||||
foundSliceRoot, &AzToolsFramework::ToolsApplicationRequests::IsSliceRootEntity, currentEntityId);
|
||||
nextIdx = getNextIdxFunction(currentIdx);
|
||||
nextIdx = nextIndexForTree(shouldSelectTopMostSlice,treeView,currentIdx);
|
||||
} while (nextIdx.isValid() && !foundSliceRoot);
|
||||
|
||||
if (foundSliceRoot)
|
||||
@@ -1416,7 +1420,10 @@ void OutlinerWidget::SortContent()
|
||||
}
|
||||
m_entitiesToSort.clear();
|
||||
|
||||
auto comparer = AZStd::bind(&CompareEntitiesForSorting, AZStd::placeholders::_1, AZStd::placeholders::_2, m_sortMode);
|
||||
auto comparer = [sortMode = m_sortMode](AZ::EntityId left, AZ::EntityId right) -> bool
|
||||
{
|
||||
return CompareEntitiesForSorting(left, right, sortMode);
|
||||
};
|
||||
for (const AZ::EntityId& entityId : parentsToSort)
|
||||
{
|
||||
SortEntityChildren(entityId, comparer);
|
||||
@@ -1433,7 +1440,10 @@ void OutlinerWidget::OnSortModeChanged(EntityOutliner::DisplaySortMode sortMode)
|
||||
if (sortMode != EntityOutliner::DisplaySortMode::Manually)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
auto comparer = AZStd::bind(&CompareEntitiesForSorting, AZStd::placeholders::_1, AZStd::placeholders::_2, sortMode);
|
||||
auto comparer = [sortMode = m_sortMode](AZ::EntityId left, AZ::EntityId right) -> bool
|
||||
{
|
||||
return CompareEntitiesForSorting(left, right, sortMode);
|
||||
};
|
||||
SortEntityChildrenRecursively(AZ::EntityId(), comparer);
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@
|
||||
#define ID_FILE_EXPORTTOGAMENOSURFACETEXTURE 33473
|
||||
#define ID_VIEW_SWITCHTOGAME 33477
|
||||
#define ID_VIEW_SWITCHTOGAME_FULLSCREEN 33478
|
||||
#define ID_VIEW_SWITCHTOGAME_VIEWPORT 33479
|
||||
#define ID_MOVE_OBJECT 33481
|
||||
#define ID_RENAME_OBJ 33483
|
||||
#define ID_FETCH 33496
|
||||
|
||||
@@ -112,8 +112,6 @@ SEditorSettings::SEditorSettings()
|
||||
m_showCircularDependencyError = true;
|
||||
bAutoloadLastLevelAtStartup = false;
|
||||
bMuteAudio = false;
|
||||
bEnableGameModeVR = false;
|
||||
|
||||
|
||||
objectHideMask = 0;
|
||||
objectSelectMask = 0xFFFFFFFF; // Initially all selectable.
|
||||
@@ -473,7 +471,7 @@ void SEditorSettings::LoadValue(const char* sSection, const char* sKey, ESystemC
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void SEditorSettings::Save()
|
||||
void SEditorSettings::Save(bool isEditorClosing)
|
||||
{
|
||||
QString strStringPlaceholder;
|
||||
|
||||
@@ -640,14 +638,16 @@ void SEditorSettings::Save()
|
||||
// --- Settings Registry values
|
||||
|
||||
// Prefab System UI
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(
|
||||
&AzFramework::ApplicationRequests::SetPrefabSystemEnabled, prefabSystem);
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::SetPrefabSystemEnabled, prefabSystem);
|
||||
|
||||
AzToolsFramework::Prefab::PrefabLoaderInterface* prefabLoaderInterface =
|
||||
AZ::Interface<AzToolsFramework::Prefab::PrefabLoaderInterface>::Get();
|
||||
prefabLoaderInterface->SetSaveAllPrefabsPreference(levelSaveSettings.saveAllPrefabsPreference);
|
||||
|
||||
SaveSettingsRegistryFile();
|
||||
if (!isEditorClosing)
|
||||
{
|
||||
SaveSettingsRegistryFile();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -267,7 +267,7 @@ struct SANDBOX_API SEditorSettings
|
||||
AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
|
||||
SEditorSettings();
|
||||
~SEditorSettings() = default;
|
||||
void Save();
|
||||
void Save(bool isEditorClosing = false);
|
||||
void Load();
|
||||
void LoadCloudSettings();
|
||||
|
||||
@@ -305,7 +305,6 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
|
||||
bool m_showCircularDependencyError;
|
||||
bool bAutoloadLastLevelAtStartup;
|
||||
bool bMuteAudio;
|
||||
bool bEnableGameModeVR;
|
||||
|
||||
//! Speed of camera movement.
|
||||
float cameraMoveSpeed;
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>General Availability</string>
|
||||
<string>development</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -590,6 +590,16 @@ AmazonToolbar ToolbarManager::GetObjectToolbar() const
|
||||
return t;
|
||||
}
|
||||
|
||||
QMenu* ToolbarManager::CreatePlayButtonMenu() const
|
||||
{
|
||||
QMenu* playButtonMenu = new QMenu("Play Game");
|
||||
|
||||
playButtonMenu->addAction(m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_VIEWPORT));
|
||||
playButtonMenu->addAction(m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN));
|
||||
|
||||
return playButtonMenu;
|
||||
}
|
||||
|
||||
AmazonToolbar ToolbarManager::GetPlayConsoleToolbar() const
|
||||
{
|
||||
AmazonToolbar t = AmazonToolbar("PlayConsole", QObject::tr("Play Controls"));
|
||||
@@ -598,8 +608,17 @@ AmazonToolbar ToolbarManager::GetPlayConsoleToolbar() const
|
||||
t.AddAction(ID_TOOLBAR_WIDGET_SPACER_RIGHT, ORIGINAL_TOOLBAR_VERSION);
|
||||
t.AddAction(ID_TOOLBAR_SEPARATOR, ORIGINAL_TOOLBAR_VERSION);
|
||||
t.AddAction(ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL, ORIGINAL_TOOLBAR_VERSION);
|
||||
t.AddAction(ID_VIEW_SWITCHTOGAME, TOOLBARS_WITH_PLAY_GAME);
|
||||
t.AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, TOOLBARS_WITH_PLAY_GAME);
|
||||
|
||||
QAction* playAction = m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME);
|
||||
QToolButton* playButton = new QToolButton(t.Toolbar());
|
||||
|
||||
QMenu* menu = CreatePlayButtonMenu();
|
||||
menu->setParent(t.Toolbar());
|
||||
playAction->setMenu(menu);
|
||||
|
||||
playButton->setDefaultAction(playAction);
|
||||
t.AddWidget(playButton, ID_VIEW_SWITCHTOGAME, ORIGINAL_TOOLBAR_VERSION);
|
||||
|
||||
t.AddAction(ID_TOOLBAR_SEPARATOR, ORIGINAL_TOOLBAR_VERSION);
|
||||
t.AddAction(ID_SWITCH_PHYSICS, TOOLBARS_WITH_PLAY_GAME);
|
||||
return t;
|
||||
@@ -728,7 +747,14 @@ void AmazonToolbar::SetActionsOnInternalToolbar(ActionManager* actionManager)
|
||||
{
|
||||
if (actionManager->HasAction(actionId))
|
||||
{
|
||||
m_toolbar->addAction(actionManager->GetAction(actionId));
|
||||
if (actionData.widget != nullptr)
|
||||
{
|
||||
m_toolbar->addWidget(actionData.widget);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_toolbar->addAction(actionManager->GetAction(actionId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1367,7 +1393,12 @@ void AmazonToolbar::InstantiateToolbar(QMainWindow* mainWindow, ToolbarManager*
|
||||
|
||||
void AmazonToolbar::AddAction(int actionId, int toolbarVersionAdded)
|
||||
{
|
||||
m_actions.push_back({ actionId, toolbarVersionAdded });
|
||||
AddWidget(nullptr, actionId, toolbarVersionAdded);
|
||||
}
|
||||
|
||||
void AmazonToolbar::AddWidget(QWidget* widget, int actionId, int toolbarVersionAdded)
|
||||
{
|
||||
m_actions.push_back({ actionId, toolbarVersionAdded, widget });
|
||||
}
|
||||
|
||||
void AmazonToolbar::Clear()
|
||||
|
||||
@@ -87,6 +87,7 @@ public:
|
||||
const QString& GetTranslatedName() const { return m_translatedName; }
|
||||
|
||||
void AddAction(int actionId, int toolbarVersionAdded = 0);
|
||||
void AddWidget(QWidget* widget, int actionId, int toolbarVersionAdded = 0);
|
||||
|
||||
QToolBar* Toolbar() const { return m_toolbar; }
|
||||
|
||||
@@ -117,6 +118,7 @@ private:
|
||||
{
|
||||
int actionId;
|
||||
int toolbarVersionAdded;
|
||||
QWidget* widget;
|
||||
|
||||
bool operator ==(const AmazonToolbar::ActionData& other) const
|
||||
{
|
||||
@@ -133,7 +135,9 @@ private:
|
||||
class AmazonToolBarExpanderWatcher;
|
||||
|
||||
class ToolbarManager
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ToolbarManager(ActionManager* actionManager, MainWindow* mainWindow);
|
||||
~ToolbarManager();
|
||||
@@ -178,6 +182,8 @@ private:
|
||||
void UpdateAllowedAreas(QToolBar* toolbar);
|
||||
bool IsDirty(const AmazonToolbar& toolbar) const;
|
||||
|
||||
QMenu* CreatePlayButtonMenu() const;
|
||||
|
||||
const AmazonToolbar* FindDefaultToolbar(const QString& toolbarName) const;
|
||||
AmazonToolbar* FindToolbar(const QString& toolbarName);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <Atom/RPI.Public/Scene.h>
|
||||
#include <Atom/RPI.Public/View.h>
|
||||
#include <Atom/RPI.Reflect/System/RenderPipelineDescriptor.h>
|
||||
#include <PostProcess/PostProcessFeatureProcessor.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Math/MatrixUtils.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
@@ -47,18 +48,42 @@ namespace TrackView
|
||||
AZ::Name viewName = AZ::Name("MainCamera");
|
||||
m_view = AZ::RPI::View::CreateView(viewName, AZ::RPI::View::UsageCamera);
|
||||
m_renderPipeline->SetDefaultView(m_view);
|
||||
m_targetView = scene.GetDefaultRenderPipeline()->GetDefaultView();
|
||||
if (AZ::Render::PostProcessFeatureProcessor* fp = scene.GetFeatureProcessor<AZ::Render::PostProcessFeatureProcessor>())
|
||||
{
|
||||
// This will be set again to mimic the active camera in UpdateView
|
||||
fp->SetViewAlias(m_view, m_targetView);
|
||||
}
|
||||
}
|
||||
|
||||
void AtomOutputFrameCapture::DestroyPipeline(AZ::RPI::Scene& scene)
|
||||
{
|
||||
if (AZ::Render::PostProcessFeatureProcessor* fp = scene.GetFeatureProcessor<AZ::Render::PostProcessFeatureProcessor>())
|
||||
{
|
||||
// Remove view alias introduced in CreatePipeline and UpdateView
|
||||
fp->RemoveViewAlias(m_view);
|
||||
}
|
||||
scene.RemoveRenderPipeline(m_renderPipeline->GetId());
|
||||
m_passHierarchy.clear();
|
||||
m_renderPipeline.reset();
|
||||
m_view.reset();
|
||||
m_targetView.reset();
|
||||
}
|
||||
|
||||
void AtomOutputFrameCapture::UpdateView(const AZ::Matrix3x4& cameraTransform, const AZ::Matrix4x4& cameraProjection)
|
||||
void AtomOutputFrameCapture::UpdateView(const AZ::Matrix3x4& cameraTransform, const AZ::Matrix4x4& cameraProjection, const AZ::RPI::ViewPtr targetView)
|
||||
{
|
||||
if (targetView && targetView != m_targetView)
|
||||
{
|
||||
if (AZ::RPI::Scene* scene = SceneFromGameEntityContext())
|
||||
{
|
||||
if (AZ::Render::PostProcessFeatureProcessor* fp = scene->GetFeatureProcessor<AZ::Render::PostProcessFeatureProcessor>())
|
||||
{
|
||||
fp->SetViewAlias(m_view, targetView);
|
||||
m_targetView = targetView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_view->SetCameraTransform(cameraTransform);
|
||||
m_view->SetViewToClipMatrix(cameraProjection);
|
||||
}
|
||||
|
||||
@@ -39,11 +39,12 @@ namespace TrackView
|
||||
CaptureFinishedCallback captureFinishedCallback);
|
||||
|
||||
//! Update the internal view that is associated with the created pipeline.
|
||||
void UpdateView(const AZ::Matrix3x4& cameraTransform, const AZ::Matrix4x4& cameraProjection);
|
||||
void UpdateView(const AZ::Matrix3x4& cameraTransform, const AZ::Matrix4x4& cameraProjection, const AZ::RPI::ViewPtr targetView = nullptr);
|
||||
|
||||
private:
|
||||
AZ::RPI::RenderPipelinePtr m_renderPipeline; //!< The internal render pipeline.
|
||||
AZ::RPI::ViewPtr m_view; //!< The view associated with the render pipeline.
|
||||
AZ::RPI::ViewPtr m_targetView; //!< The view that this render pipeline will mimic.
|
||||
AZStd::vector<AZStd::string> m_passHierarchy; //!< Pass hierarchy (includes pipelineName and CopyToSwapChain).
|
||||
CaptureFinishedCallback m_captureFinishedCallback; //!< Stored callback called from OnCaptureFinished.
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzFramework/Windowing/WindowBus.h>
|
||||
#include <Atom/RPI.Public/ViewProviderBus.h>
|
||||
|
||||
// Qt
|
||||
#include <QAction>
|
||||
@@ -91,9 +92,12 @@ namespace
|
||||
static void UpdateAtomOutputFrameCaptureView(TrackView::AtomOutputFrameCapture& atomOutputFrameCapture, const int width, const int height)
|
||||
{
|
||||
const AZ::EntityId activeCameraEntityId = TrackView::ActiveCameraEntityId();
|
||||
AZ::RPI::ViewPtr view = nullptr;
|
||||
AZ::RPI::ViewProviderBus::EventResult(view, activeCameraEntityId, &AZ::RPI::ViewProvider::GetView);
|
||||
atomOutputFrameCapture.UpdateView(
|
||||
TrackView::TransformFromEntityId(activeCameraEntityId),
|
||||
TrackView::ProjectionFromCameraEntityId(activeCameraEntityId, static_cast<float>(width), static_cast<float>(height)));
|
||||
TrackView::ProjectionFromCameraEntityId(activeCameraEntityId, aznumeric_cast<float>(width), aznumeric_cast<float>(height)),
|
||||
view);
|
||||
}
|
||||
|
||||
CSequenceBatchRenderDialog::CSequenceBatchRenderDialog(float fps, QWidget* pParent /* = nullptr */)
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
// AzCore
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
// AzFramework
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
|
||||
// AzQtComponents
|
||||
#include <AzQtComponents/Utilities/DesktopUtilities.h>
|
||||
@@ -54,92 +54,14 @@
|
||||
#include <Shellapi.h>
|
||||
#endif
|
||||
|
||||
bool CFileUtil::s_singleFileDlgPref[IFileUtil::EFILE_TYPE_LAST] = { true, true, true, true, true };
|
||||
bool CFileUtil::s_multiFileDlgPref[IFileUtil::EFILE_TYPE_LAST] = { true, true, true, true, true };
|
||||
bool CFileUtil::s_singleFileDlgPref[IFileUtil::EFILE_TYPE_LAST] = { true, true, true, true };
|
||||
bool CFileUtil::s_multiFileDlgPref[IFileUtil::EFILE_TYPE_LAST] = { true, true, true, true };
|
||||
|
||||
CAutoRestorePrimaryCDRoot::~CAutoRestorePrimaryCDRoot()
|
||||
{
|
||||
QDir::setCurrent(GetIEditor()->GetPrimaryCDFolder());
|
||||
}
|
||||
|
||||
bool CFileUtil::CompileLuaFile(const char* luaFilename)
|
||||
{
|
||||
QString luaFile = luaFilename;
|
||||
|
||||
if (luaFile.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if this file is in Archive.
|
||||
{
|
||||
CCryFile file;
|
||||
if (file.Open(luaFilename, "rb"))
|
||||
{
|
||||
// Check if in pack.
|
||||
if (file.IsInPak())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
luaFile = Path::GamePathToFullPath(luaFilename);
|
||||
|
||||
// First try compiling script and see if it have any errors.
|
||||
QString LuaCompiler;
|
||||
QString CompilerOutput;
|
||||
|
||||
// Create the filepath of the lua compiler
|
||||
QString szExeFileName = qApp->applicationFilePath();
|
||||
QString exePath = Path::GetPath(szExeFileName);
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
const char* luaCompiler = "LuaCompiler.exe";
|
||||
#else
|
||||
const char* luaCompiler = "lua";
|
||||
#endif
|
||||
LuaCompiler = Path::AddPathSlash(exePath) + luaCompiler + " ";
|
||||
|
||||
AZStd::string path = luaFile.toUtf8().data();
|
||||
EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, path);
|
||||
|
||||
QString finalPath = path.c_str();
|
||||
finalPath = "\"" + finalPath + "\"";
|
||||
|
||||
// Add the name of the Lua file
|
||||
QString cmdLine = LuaCompiler + finalPath;
|
||||
|
||||
// Execute the compiler and capture the output
|
||||
if (!GetIEditor()->ExecuteConsoleApp(cmdLine, CompilerOutput))
|
||||
{
|
||||
QMessageBox::critical(QApplication::activeWindow(), QString(), QObject::tr("Error while executing '%1', make sure the file is in" \
|
||||
" your Primary CD folder !").arg(luaCompiler));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check return string
|
||||
if (!CompilerOutput.isEmpty())
|
||||
{
|
||||
// Errors while compiling file.
|
||||
|
||||
// Show output from Lua compiler
|
||||
if (QMessageBox::critical(QApplication::activeWindow(), QObject::tr("Lua Compiler"),
|
||||
QObject::tr("Error output from Lua compiler:\r\n%1\r\nDo you want to edit the file ?").arg(CompilerOutput), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
|
||||
{
|
||||
int line = 0;
|
||||
int index = CompilerOutput.indexOf("at line");
|
||||
if (index >= 0)
|
||||
{
|
||||
azsscanf(CompilerOutput.mid(index).toUtf8().data(), "at line %d", &line);
|
||||
}
|
||||
// Open the Lua file for editing
|
||||
EditTextFile(luaFile.toUtf8().data(), line);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CFileUtil::ExtractFile(QString& file, bool bMsgBoxAskForExtraction, const char* pDestinationFilename)
|
||||
{
|
||||
@@ -205,7 +127,7 @@ void CFileUtil::EditTextFile(const char* txtFile, int line, IFileUtil::ETextFile
|
||||
{
|
||||
QString file = txtFile;
|
||||
|
||||
QString fullPathName = Path::GamePathToFullPath(file);
|
||||
QString fullPathName = Path::GamePathToFullPath(file);
|
||||
ExtractFile(fullPathName);
|
||||
QString cmd(fullPathName);
|
||||
#if defined (AZ_PLATFORM_WINDOWS)
|
||||
@@ -301,64 +223,6 @@ void CFileUtil::EditTextureFile(const char* textureFile, [[maybe_unused]] bool b
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CFileUtil::EditMayaFile(const char* filepath, const bool bExtractFromPak, const bool bUseGameFolder)
|
||||
{
|
||||
QString dosFilepath = PathUtil::ToDosPath(filepath).c_str();
|
||||
if (bExtractFromPak)
|
||||
{
|
||||
ExtractFile(dosFilepath);
|
||||
}
|
||||
|
||||
if (bUseGameFolder)
|
||||
{
|
||||
const QString sGameFolder = Path::GetEditingGameDataFolder().c_str();
|
||||
int nLength = sGameFolder.toUtf8().count();
|
||||
if (azstrnicmp(filepath, sGameFolder.toUtf8().data(), nLength) != 0)
|
||||
{
|
||||
dosFilepath = sGameFolder + '\\' + filepath;
|
||||
}
|
||||
|
||||
dosFilepath = PathUtil::ToDosPath(dosFilepath.toUtf8().data()).c_str();
|
||||
}
|
||||
|
||||
const char* engineRoot;
|
||||
EBUS_EVENT_RESULT(engineRoot, AzFramework::ApplicationRequests::Bus, GetEngineRoot);
|
||||
|
||||
const QString fullPath = QString(engineRoot) + '\\' + dosFilepath;
|
||||
|
||||
if (gSettings.animEditor.isEmpty())
|
||||
{
|
||||
AzQtComponents::ShowFileOnDesktop(fullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!QProcess::startDetached(gSettings.animEditor, { fullPath }))
|
||||
{
|
||||
CryMessageBox("Can't open the file. You can specify a source editor in Sandbox Preferences or create an association in Windows.", "Cannot open file!", MB_OK | MB_ICONERROR);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CFileUtil::EditFile(const char* filePath, const bool bExtrackFromPak, const bool bUseGameFolder)
|
||||
{
|
||||
QString extension = filePath;
|
||||
extension.remove(0, extension.lastIndexOf('.'));
|
||||
|
||||
if (extension.compare(".ma") == 0)
|
||||
{
|
||||
return EditMayaFile(filePath, bExtrackFromPak, bUseGameFolder);
|
||||
}
|
||||
else if ((extension.compare(".bspace") == 0) || (extension.compare(".comb") == 0))
|
||||
{
|
||||
EditTextFile(filePath, 0, IFileUtil::FILE_TYPE_BSPACE);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CFileUtil::CalculateDccFilename(const QString& assetFilename, QString& dccFilename)
|
||||
|
||||
@@ -25,14 +25,9 @@ public:
|
||||
|
||||
static void ShowInExplorer(const QString& path);
|
||||
|
||||
// Try to compile the given lua file: returns true if compilation succeeded, false on failure.
|
||||
static bool CompileLuaFile(const char* luaFilename);
|
||||
|
||||
static bool ExtractFile(QString& file, bool bMsgBoxAskForExtraction = true, const char* pDestinationFilename = nullptr);
|
||||
static void EditTextFile(const char* txtFile, int line = 0, IFileUtil::ETextFileType fileType = IFileUtil::FILE_TYPE_SCRIPT);
|
||||
static void EditTextureFile(const char* txtureFile, bool bUseGameFolder);
|
||||
static bool EditMayaFile(const char* mayaFile, const bool bExtractFromPak, const bool bUseGameFolder);
|
||||
static bool EditFile(const char* filePath, const bool bExtrackFromPak, const bool bUseGameFolder);
|
||||
|
||||
//! dcc filename calculation and extraction sub-routines
|
||||
static bool CalculateDccFilename(const QString& assetFilename, QString& dccFilename);
|
||||
|
||||
@@ -20,21 +20,11 @@ void CFileUtil_impl::ShowInExplorer(const QString& path)
|
||||
CFileUtil::ShowInExplorer(path);
|
||||
}
|
||||
|
||||
bool CFileUtil_impl::CompileLuaFile(const char* luaFilename)
|
||||
{
|
||||
return CFileUtil::CompileLuaFile(luaFilename);
|
||||
}
|
||||
|
||||
bool CFileUtil_impl::ExtractFile(QString& file, bool bMsgBoxAskForExtraction, const char* pDestinationFilename)
|
||||
{
|
||||
return CFileUtil::ExtractFile(file, bMsgBoxAskForExtraction, pDestinationFilename);
|
||||
}
|
||||
|
||||
void CFileUtil_impl::EditTextFile(const char* txtFile, int line, ETextFileType fileType)
|
||||
{
|
||||
CFileUtil::EditTextFile(txtFile, line, fileType);
|
||||
}
|
||||
|
||||
void CFileUtil_impl::EditTextureFile(const char* txtureFile, bool bUseGameFolder)
|
||||
{
|
||||
CFileUtil::EditTextureFile(txtureFile, bUseGameFolder);
|
||||
|
||||
@@ -36,9 +36,7 @@ public:
|
||||
|
||||
void ShowInExplorer(const QString& path) override;
|
||||
|
||||
bool CompileLuaFile(const char* luaFilename) override;
|
||||
bool ExtractFile(QString& file, bool bMsgBoxAskForExtraction = true, const char* pDestinationFilename = nullptr) override;
|
||||
void EditTextFile(const char* txtFile, int line = 0, ETextFileType fileType = FILE_TYPE_SCRIPT) override;
|
||||
void EditTextureFile(const char* txtureFile, bool bUseGameFolder) override;
|
||||
|
||||
//! dcc filename calculation and extraction sub-routines
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h> // for ebus events
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
|
||||
@@ -175,9 +174,8 @@ namespace Path
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QString GetEngineRootPath()
|
||||
{
|
||||
const char* engineRoot;
|
||||
EBUS_EVENT_RESULT(engineRoot, AzFramework::ApplicationRequests::Bus, GetEngineRoot);
|
||||
return QString(engineRoot);
|
||||
const AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath();
|
||||
return QString::fromUtf8(engineRoot.c_str(), static_cast<int>(engineRoot.size()));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
+91
-212
@@ -14,14 +14,19 @@
|
||||
// Qt
|
||||
#include <QPainter>
|
||||
|
||||
// AzCore
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
|
||||
// AzQtComponents
|
||||
#include <AzQtComponents/DragAndDrop/ViewportDragAndDrop.h>
|
||||
|
||||
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
|
||||
// Editor
|
||||
#include "Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.h"
|
||||
#include "ViewManager.h"
|
||||
#include "Include/ITransformManipulator.h"
|
||||
#include "Include/HitContext.h"
|
||||
@@ -32,22 +37,35 @@
|
||||
#include "GameEngine.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
#ifdef LoadCursor
|
||||
#undef LoadCursor
|
||||
#endif
|
||||
|
||||
AZ_CVAR(
|
||||
float,
|
||||
ed_defaultEntityPlacementDistance,
|
||||
10.0f,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"The default distance to place an entity from the camera if no intersection is found");
|
||||
|
||||
float GetDefaultEntityPlacementDistance()
|
||||
{
|
||||
return ed_defaultEntityPlacementDistance;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Viewport drag and drop support
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
void QtViewport::BuildDragDropContext(AzQtComponents::ViewportDragContext& context, const QPoint& pt)
|
||||
void QtViewport::BuildDragDropContext(
|
||||
AzQtComponents::ViewportDragContext& context, const AzFramework::ViewportId viewportId, const QPoint& point)
|
||||
{
|
||||
context.m_hitLocation = AZ::Vector3::CreateZero();
|
||||
context.m_hitLocation = GetHitLocation(pt);
|
||||
context.m_hitLocation = AzToolsFramework::FindClosestPickIntersection(
|
||||
viewportId, AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(point), AzToolsFramework::EditorPickRayLength,
|
||||
GetDefaultEntityPlacementDistance());
|
||||
}
|
||||
|
||||
|
||||
void QtViewport::dragEnterEvent(QDragEnterEvent* event)
|
||||
{
|
||||
if (!GetIEditor()->GetGameEngine()->IsLevelLoaded())
|
||||
@@ -66,7 +84,7 @@ void QtViewport::dragEnterEvent(QDragEnterEvent* event)
|
||||
// new bus-based way of doing it (install a listener!)
|
||||
using namespace AzQtComponents;
|
||||
ViewportDragContext context;
|
||||
BuildDragDropContext(context, event->pos());
|
||||
BuildDragDropContext(context, GetViewportId(), event->pos());
|
||||
DragAndDropEventsBus::Event(DragAndDropContexts::EditorViewport, &DragAndDropEvents::DragEnter, event, context);
|
||||
}
|
||||
}
|
||||
@@ -89,7 +107,7 @@ void QtViewport::dragMoveEvent(QDragMoveEvent* event)
|
||||
// new bus-based way of doing it (install a listener!)
|
||||
using namespace AzQtComponents;
|
||||
ViewportDragContext context;
|
||||
BuildDragDropContext(context, event->pos());
|
||||
BuildDragDropContext(context, GetViewportId(), event->pos());
|
||||
DragAndDropEventsBus::Event(DragAndDropContexts::EditorViewport, &DragAndDropEvents::DragMove, event, context);
|
||||
}
|
||||
}
|
||||
@@ -112,7 +130,7 @@ void QtViewport::dropEvent(QDropEvent* event)
|
||||
{
|
||||
// new bus-based way of doing it (install a listener!)
|
||||
ViewportDragContext context;
|
||||
BuildDragDropContext(context, event->pos());
|
||||
BuildDragDropContext(context, GetViewportId(), event->pos());
|
||||
DragAndDropEventsBus::Event(DragAndDropContexts::EditorViewport, &DragAndDropEvents::Drop, event, context);
|
||||
}
|
||||
}
|
||||
@@ -340,13 +358,6 @@ void QtViewport::resizeEvent(QResizeEvent* event)
|
||||
Update();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::leaveEvent(QEvent* event)
|
||||
{
|
||||
QWidget::leaveEvent(event);
|
||||
MouseCallback(eMouseLeave, QPoint(), Qt::KeyboardModifiers(), Qt::MouseButtons());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::paintEvent([[maybe_unused]] QPaintEvent* event)
|
||||
{
|
||||
@@ -581,63 +592,7 @@ void QtViewport::keyReleaseEvent(QKeyEvent* event)
|
||||
OnKeyUp(nativeKey, 1, event->nativeModifiers());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::OnLButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
{
|
||||
// Save the mouse down position
|
||||
m_cMouseDownPos = point;
|
||||
|
||||
if (MouseCallback(eMouseLDown, point, modifiers))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::OnLButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
{
|
||||
// Check Edit Tool.
|
||||
MouseCallback(eMouseLUp, point, modifiers);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
{
|
||||
MouseCallback(eMouseRDown, point, modifiers);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::OnRButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
{
|
||||
MouseCallback(eMouseRUp, point, modifiers);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::OnMButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
{
|
||||
// Check Edit Tool.
|
||||
MouseCallback(eMouseMDown, point, modifiers);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::OnMButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
{
|
||||
// Move the viewer to the mouse location.
|
||||
// Check Edit Tool.
|
||||
MouseCallback(eMouseMUp, point, modifiers);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::OnMButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
{
|
||||
MouseCallback(eMouseMDblClick, point, modifiers);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::OnMouseMove(Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons, const QPoint& point)
|
||||
{
|
||||
MouseCallback(eMouseMove, point, modifiers, buttons);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::OnSetCursor()
|
||||
@@ -696,44 +651,6 @@ void QtViewport::OnDragSelectRectangle(const QRect& rect, bool bNormalizeRect)
|
||||
GetIEditor()->SetStatusText(szNewStatusText);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::OnLButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
{
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
{
|
||||
// Ignore double clicks while in game.
|
||||
return;
|
||||
}
|
||||
|
||||
MouseCallback(eMouseLDblClick, point, modifiers);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::OnRButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point)
|
||||
{
|
||||
MouseCallback(eMouseRDblClick, point, modifiers);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::OnKeyDown([[maybe_unused]] UINT nChar, [[maybe_unused]] UINT nRepCnt, [[maybe_unused]] UINT nFlags)
|
||||
{
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
{
|
||||
// Ignore key downs while in game.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::OnKeyUp([[maybe_unused]] UINT nChar, [[maybe_unused]] UINT nRepCnt, [[maybe_unused]] UINT nFlags)
|
||||
{
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
{
|
||||
// Ignore key downs while in game.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::SetCurrentCursor(const QCursor& hCursor, const QString& cursorString)
|
||||
{
|
||||
@@ -1119,29 +1036,6 @@ bool QtViewport::HitTest(const QPoint& point, HitContext& hitInfo)
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::Vector3 QtViewport::GetHitLocation(const QPoint& point)
|
||||
{
|
||||
Vec3 pos = Vec3(ZERO);
|
||||
HitContext hit;
|
||||
if (HitTest(point, hit))
|
||||
{
|
||||
pos = hit.raySrc + hit.rayDir * hit.dist;
|
||||
pos = SnapToGrid(pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hitTerrain;
|
||||
pos = ViewToWorld(point, &hitTerrain);
|
||||
if (hitTerrain)
|
||||
{
|
||||
pos.z = GetIEditor()->GetTerrainElevation(pos.x, pos.y);
|
||||
}
|
||||
pos = SnapToGrid(pos);
|
||||
}
|
||||
|
||||
return AZ::Vector3(pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::SetZoomFactor(float fZoomFactor)
|
||||
{
|
||||
@@ -1315,84 +1209,6 @@ bool QtViewport::GetAdvancedSelectModeFlag()
|
||||
return m_bAdvancedSelectMode;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool QtViewport::MouseCallback(EMouseEvent event, const QPoint& point, Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Editor);
|
||||
|
||||
// Ignore any mouse events in game mode.
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// We must ignore mouse events when we are in the middle of an assert.
|
||||
// Reason: If we have an assert called from an engine module under the editor, if we call this function,
|
||||
// it may call the engine again and cause a deadlock.
|
||||
// Concrete example: CryPhysics called from Trackview causing an assert, and moving the cursor over the viewport
|
||||
// would cause the editor to freeze as it calls CryPhysics again for a raycast while it didn't release the lock.
|
||||
if (gEnv->pSystem->IsAssertDialogVisible())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Hit test gizmo objects.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool bAltClick = (modifiers & Qt::AltModifier);
|
||||
bool bCtrlClick = (modifiers & Qt::ControlModifier);
|
||||
bool bShiftClick = (modifiers & Qt::ShiftModifier);
|
||||
|
||||
int flags = (bCtrlClick ? MK_CONTROL : 0) |
|
||||
(bShiftClick ? MK_SHIFT : 0) |
|
||||
((buttons& Qt::LeftButton) ? MK_LBUTTON : 0) |
|
||||
((buttons& Qt::MiddleButton) ? MK_MBUTTON : 0) |
|
||||
((buttons& Qt::RightButton) ? MK_RBUTTON : 0);
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case eMouseMove:
|
||||
|
||||
if (m_nLastUpdateFrame == m_nLastMouseMoveFrame)
|
||||
{
|
||||
// If mouse move event generated in the same frame, ignore it.
|
||||
return false;
|
||||
}
|
||||
m_nLastMouseMoveFrame = m_nLastUpdateFrame;
|
||||
|
||||
// Skip the marker position update if anything is selected, since it is only used
|
||||
// by the info bar which doesn't show the marker when there is an active selection.
|
||||
// This helps a performance issue when calling ViewToWorld (which calls RayWorldIntersection)
|
||||
// on every mouse movement becomes very expensive in scenes with large amounts of entities.
|
||||
CSelectionGroup* selection = GetIEditor()->GetSelection();
|
||||
if (!(buttons & Qt::RightButton) /* && m_nLastUpdateFrame != m_nLastMouseMoveFrame*/ && (selection && selection->IsEmpty()))
|
||||
{
|
||||
//m_nLastMouseMoveFrame = m_nLastUpdateFrame;
|
||||
Vec3 pos = ViewToWorld(point);
|
||||
GetIEditor()->SetMarkerPosition(pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
QPoint tempPoint(point.x(), point.y());
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Handle viewport manipulators.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
if (!bAltClick)
|
||||
{
|
||||
ITransformManipulator* pManipulator = GetIEditor()->GetTransformManipulator();
|
||||
if (pManipulator)
|
||||
{
|
||||
if (pManipulator->MouseCallback(this, event, tempPoint, flags))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void QtViewport::ProcessRenderLisneters(DisplayContext& rstDisplayContext)
|
||||
{
|
||||
@@ -1407,6 +1223,69 @@ void QtViewport::ProcessRenderLisneters(DisplayContext& rstDisplayContext)
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
// Note: Both CreateAnglesYPR and CreateOrientationYPR were copied verbatim from Cry_Camera.h which has been removed.
|
||||
//
|
||||
// Description
|
||||
// <PRE>
|
||||
// x-YAW
|
||||
// y-PITCH (negative=looking down / positive=looking up)
|
||||
// z-ROLL
|
||||
// </PRE>
|
||||
// Note: If we are looking along the z-axis, its not possible to specify the x and z-angle
|
||||
inline Ang3 CreateAnglesYPR(const Matrix33& m)
|
||||
{
|
||||
assert(m.IsOrthonormal());
|
||||
float l = Vec3(m.m01, m.m11, 0.0f).GetLength();
|
||||
if (l > 0.0001)
|
||||
{
|
||||
return Ang3(atan2f(-m.m01 / l, m.m11 / l), atan2f(m.m21, l), atan2f(-m.m20 / l, m.m22 / l));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Ang3(0, atan2f(m.m21, l), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Description
|
||||
// This function builds a 3x3 orientation matrix using YPR-angles
|
||||
// Rotation order for the orientation-matrix is Z-X-Y. (Zaxis=YAW / Xaxis=PITCH / Yaxis=ROLL)
|
||||
//
|
||||
// <PRE>
|
||||
// COORDINATE-SYSTEM
|
||||
//
|
||||
// z-axis
|
||||
// ^
|
||||
// |
|
||||
// | y-axis
|
||||
// | /
|
||||
// | /
|
||||
// |/
|
||||
// +---------------> x-axis
|
||||
// </PRE>
|
||||
//
|
||||
// Example:
|
||||
// Matrix33 orientation=CreateOrientationYPR( Ang3(1,2,3) );
|
||||
inline Matrix33 CreateOrientationYPR(const Ang3& ypr)
|
||||
{
|
||||
f32 sz, cz;
|
||||
sincos_tpl(ypr.x, &sz, &cz); //Zaxis = YAW
|
||||
f32 sx, cx;
|
||||
sincos_tpl(ypr.y, &sx, &cx); //Xaxis = PITCH
|
||||
f32 sy, cy;
|
||||
sincos_tpl(ypr.z, &sy, &cy); //Yaxis = ROLL
|
||||
Matrix33 c;
|
||||
c.m00 = cy * cz - sy * sz * sx;
|
||||
c.m01 = -sz * cx;
|
||||
c.m02 = sy * cz + cy * sz * sx;
|
||||
c.m10 = cy * sz + sy * sx * cz;
|
||||
c.m11 = cz * cx;
|
||||
c.m12 = sy * sz - cy * sx * cz;
|
||||
c.m20 = -sy * cx;
|
||||
c.m21 = sx;
|
||||
c.m22 = cy * cx;
|
||||
return c;
|
||||
}
|
||||
|
||||
void QtViewport::OnRawInput([[maybe_unused]] UINT wParam, HRAWINPUT lParam)
|
||||
{
|
||||
static C3DConnexionDriver* p3DConnexionDriver = 0;
|
||||
@@ -1450,12 +1329,12 @@ void QtViewport::OnRawInput([[maybe_unused]] UINT wParam, HRAWINPUT lParam)
|
||||
t *= sys_scale3DMouseTranslation->GetFVal();
|
||||
|
||||
float as = 0.001f * gSettings.cameraMoveSpeed;
|
||||
Ang3 ypr = CCamera::CreateAnglesYPR(Matrix33(viewTM));
|
||||
Ang3 ypr = CreateAnglesYPR(Matrix33(viewTM));
|
||||
ypr.x += -all6DOFs[5] * as * fScaleYPR;
|
||||
ypr.y = AZStd::clamp(ypr.y + all6DOFs[3] * as * fScaleYPR, -1.5f, 1.5f); // to keep rotation in reasonable range
|
||||
ypr.z = 0; // to have camera always upward
|
||||
|
||||
viewTM = Matrix34(CCamera::CreateOrientationYPR(ypr), viewTM.GetTranslation());
|
||||
viewTM = Matrix34(CreateOrientationYPR(ypr), viewTM.GetTranslation());
|
||||
viewTM = viewTM * Matrix34::CreateTranslationMat(t);
|
||||
|
||||
SetViewTM(viewTM);
|
||||
|
||||
+20
-23
@@ -6,13 +6,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// Description : interface for the CViewport class.
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzFramework/Viewport/ViewportId.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportTypes.h>
|
||||
#include <AzToolsFramework/ViewportUi/ViewportUiManager.h>
|
||||
#include <Cry_Color.h>
|
||||
@@ -88,6 +87,9 @@ enum EStdCursor
|
||||
STD_CURSOR_LAST,
|
||||
};
|
||||
|
||||
//! The default distance an entity is placed from the camera if there is no intersection
|
||||
SANDBOX_API float GetDefaultEntityPlacementDistance();
|
||||
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
|
||||
class SANDBOX_API CViewport
|
||||
: public IDisplayViewport
|
||||
@@ -201,7 +203,6 @@ public:
|
||||
|
||||
//! Performs hit testing of 2d point in view to find which object hit.
|
||||
virtual bool HitTest(const QPoint& point, HitContext& hitInfo) = 0;
|
||||
virtual AZ::Vector3 GetHitLocation(const QPoint& point) = 0;
|
||||
|
||||
virtual void MakeConstructionPlane(int axis) = 0;
|
||||
|
||||
@@ -432,7 +433,6 @@ public:
|
||||
|
||||
//! Performs hit testing of 2d point in view to find which object hit.
|
||||
bool HitTest(const QPoint& point, HitContext& hitInfo) override;
|
||||
AZ::Vector3 GetHitLocation(const QPoint& point) override;
|
||||
|
||||
//! Do 2D hit testing of line in world space.
|
||||
// pToCameraDistance is an optional output parameter in which distance from the camera to the line is returned.
|
||||
@@ -522,9 +522,6 @@ protected:
|
||||
void setRenderOverlayVisible(bool);
|
||||
bool isRenderOverlayVisible() const;
|
||||
|
||||
// called to process mouse callback inside the viewport.
|
||||
virtual bool MouseCallback(EMouseEvent event, const QPoint& point, Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons = Qt::NoButton);
|
||||
|
||||
void ProcessRenderLisneters(DisplayContext& rstDisplayContext);
|
||||
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
@@ -535,29 +532,29 @@ protected:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void keyReleaseEvent(QKeyEvent* event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void leaveEvent(QEvent* event) override;
|
||||
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
virtual void OnMouseMove(Qt::KeyboardModifiers modifiers, Qt::MouseButtons buttons, const QPoint& point);
|
||||
virtual void OnMouseWheel(Qt::KeyboardModifiers modifiers, short zDelta, const QPoint& pt);
|
||||
virtual void OnLButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point);
|
||||
virtual void OnLButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point);
|
||||
virtual void OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point);
|
||||
virtual void OnRButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point);
|
||||
virtual void OnMButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point);
|
||||
virtual void OnMButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point);
|
||||
virtual void OnMButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point);
|
||||
virtual void OnLButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point);
|
||||
virtual void OnRButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point);
|
||||
virtual void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
|
||||
virtual void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);
|
||||
virtual void OnMouseMove(Qt::KeyboardModifiers, Qt::MouseButtons, const QPoint&) {}
|
||||
virtual void OnMouseWheel(Qt::KeyboardModifiers, short zDelta, const QPoint&);
|
||||
virtual void OnLButtonDown(Qt::KeyboardModifiers, const QPoint&) {}
|
||||
virtual void OnLButtonUp(Qt::KeyboardModifiers, const QPoint&) {}
|
||||
virtual void OnRButtonDown(Qt::KeyboardModifiers, const QPoint&) {}
|
||||
virtual void OnRButtonUp(Qt::KeyboardModifiers, const QPoint&) {}
|
||||
virtual void OnMButtonDblClk(Qt::KeyboardModifiers, const QPoint&) {}
|
||||
virtual void OnMButtonDown(Qt::KeyboardModifiers, const QPoint&) {}
|
||||
virtual void OnMButtonUp(Qt::KeyboardModifiers, const QPoint&) {}
|
||||
virtual void OnLButtonDblClk(Qt::KeyboardModifiers, const QPoint&) {}
|
||||
virtual void OnRButtonDblClk(Qt::KeyboardModifiers, const QPoint&) {}
|
||||
virtual void OnKeyDown([[maybe_unused]] UINT nChar, [[maybe_unused]] UINT nRepCnt, [[maybe_unused]] UINT nFlags) {}
|
||||
virtual void OnKeyUp([[maybe_unused]] UINT nChar, [[maybe_unused]] UINT nRepCnt, [[maybe_unused]] UINT nFlags) {}
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
void OnRawInput(UINT wParam, HRAWINPUT lParam);
|
||||
#endif
|
||||
void OnSetCursor();
|
||||
|
||||
virtual void BuildDragDropContext(AzQtComponents::ViewportDragContext& context, const QPoint& pt);
|
||||
virtual void BuildDragDropContext(
|
||||
AzQtComponents::ViewportDragContext& context, AzFramework::ViewportId viewportId, const QPoint& point);
|
||||
|
||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||
void dragMoveEvent(QDragMoveEvent* event) override;
|
||||
void dragLeaveEvent(QDragLeaveEvent* event) override;
|
||||
|
||||
@@ -8,13 +8,15 @@
|
||||
|
||||
#include "ViewportManipulatorController.h"
|
||||
|
||||
#include <AzCore/Script/ScriptTimePoint.h>
|
||||
#include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
|
||||
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
|
||||
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
|
||||
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
|
||||
#include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
#include <AzCore/Script/ScriptTimePoint.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
@@ -87,8 +89,14 @@ namespace SandboxEditor
|
||||
}
|
||||
|
||||
using InteractionBus = AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
|
||||
using namespace AzToolsFramework::ViewportInteraction;
|
||||
using AzFramework::InputChannel;
|
||||
using AzToolsFramework::ViewportInteraction::KeyboardModifier;
|
||||
using AzToolsFramework::ViewportInteraction::MouseButton;
|
||||
using AzToolsFramework::ViewportInteraction::MouseEvent;
|
||||
using AzToolsFramework::ViewportInteraction::MouseInteraction;
|
||||
using AzToolsFramework::ViewportInteraction::MouseInteractionEvent;
|
||||
using AzToolsFramework::ViewportInteraction::ProjectedViewportRay;
|
||||
using AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus;
|
||||
|
||||
bool interactionHandled = false;
|
||||
float wheelDelta = 0.0f;
|
||||
@@ -117,16 +125,13 @@ namespace SandboxEditor
|
||||
aznumeric_cast<int>(position->m_normalizedPosition.GetX() * windowSize.m_width),
|
||||
aznumeric_cast<int>(position->m_normalizedPosition.GetY() * windowSize.m_height));
|
||||
|
||||
m_mouseInteraction.m_mousePick.m_screenCoordinates = screenPoint;
|
||||
AZStd::optional<ProjectedViewportRay> ray;
|
||||
ProjectedViewportRay ray{};
|
||||
ViewportInteractionRequestBus::EventResult(
|
||||
ray, GetViewportId(), &ViewportInteractionRequestBus::Events::ViewportScreenToWorldRay, screenPoint);
|
||||
|
||||
if (ray.has_value())
|
||||
{
|
||||
m_mouseInteraction.m_mousePick.m_rayOrigin = ray.value().origin;
|
||||
m_mouseInteraction.m_mousePick.m_rayDirection = ray.value().direction;
|
||||
}
|
||||
m_mouseInteraction.m_mousePick.m_rayOrigin = ray.origin;
|
||||
m_mouseInteraction.m_mousePick.m_rayDirection = ray.direction;
|
||||
m_mouseInteraction.m_mousePick.m_screenCoordinates = screenPoint;
|
||||
}
|
||||
|
||||
eventType = MouseEvent::Move;
|
||||
@@ -152,7 +157,7 @@ namespace SandboxEditor
|
||||
// Only insert the double click timing once we're done processing events, to avoid a false IsDoubleClick positive
|
||||
if (finishedProcessingEvents)
|
||||
{
|
||||
m_pendingDoubleClicks[mouseButton] = m_curTime;
|
||||
m_pendingDoubleClicks[mouseButton] = { m_currentTime, m_mouseInteraction.m_mousePick.m_screenCoordinates };
|
||||
}
|
||||
eventType = MouseEvent::Down;
|
||||
}
|
||||
@@ -160,8 +165,8 @@ namespace SandboxEditor
|
||||
else if (state == InputChannel::State::Ended)
|
||||
{
|
||||
// If we've actually logged a mouse down event, forward a mouse up event.
|
||||
// This prevents corner cases like the context menu thinking it should be opened even though no one clicked in this viewport,
|
||||
// due to RenderViewportWidget ensuring all controllers get InputChannel::State::Ended events.
|
||||
// This prevents corner cases like the context menu thinking it should be opened even though no one clicked in this
|
||||
// viewport, due to RenderViewportWidget ensuring all controllers get InputChannel::State::Ended events.
|
||||
if (m_mouseInteraction.m_mouseButtons.m_mouseButtons & mouseButtonValue)
|
||||
{
|
||||
// Erase the button from our state if we're done processing events.
|
||||
@@ -246,17 +251,22 @@ namespace SandboxEditor
|
||||
|
||||
void ViewportManipulatorControllerInstance::UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event)
|
||||
{
|
||||
m_curTime = event.m_time;
|
||||
m_currentTime = event.m_time;
|
||||
}
|
||||
|
||||
bool ViewportManipulatorControllerInstance::IsDoubleClick(AzToolsFramework::ViewportInteraction::MouseButton button) const
|
||||
{
|
||||
auto clickIt = m_pendingDoubleClicks.find(button);
|
||||
if (clickIt == m_pendingDoubleClicks.end())
|
||||
if (auto clickIt = m_pendingDoubleClicks.find(button); clickIt != m_pendingDoubleClicks.end())
|
||||
{
|
||||
return false;
|
||||
const double doubleClickThresholdMilliseconds = qApp->doubleClickInterval();
|
||||
const bool insideTimeThreshold =
|
||||
(m_currentTime.GetMilliseconds() - clickIt->second.m_time.GetMilliseconds()) < doubleClickThresholdMilliseconds;
|
||||
const bool insideDistanceThreshold =
|
||||
AzFramework::ScreenVectorLength(clickIt->second.m_position - m_mouseInteraction.m_mousePick.m_screenCoordinates) <
|
||||
AzFramework::DefaultMouseMoveDeadZone;
|
||||
return insideTimeThreshold && insideDistanceThreshold;
|
||||
}
|
||||
const double doubleClickThresholdMilliseconds = qApp->doubleClickInterval();
|
||||
return (m_curTime.GetMilliseconds() - clickIt->second.GetMilliseconds()) < doubleClickThresholdMilliseconds;
|
||||
|
||||
return false;
|
||||
}
|
||||
} //namespace SandboxEditor
|
||||
} // namespace SandboxEditor
|
||||
|
||||
@@ -39,8 +39,16 @@ namespace SandboxEditor
|
||||
static bool IsMouseMove(const AzFramework::InputChannel& inputChannel);
|
||||
static AzToolsFramework::ViewportInteraction::KeyboardModifier GetKeyboardModifier(const AzFramework::InputChannel& inputChannel);
|
||||
|
||||
//! Represents the time and location of a click.
|
||||
struct ClickEvent
|
||||
{
|
||||
AZ::ScriptTimePoint m_time;
|
||||
AzFramework::ScreenPoint m_position;
|
||||
};
|
||||
|
||||
AzToolsFramework::ViewportInteraction::MouseInteraction m_mouseInteraction;
|
||||
AZStd::unordered_map<AzToolsFramework::ViewportInteraction::MouseButton, AZ::ScriptTimePoint> m_pendingDoubleClicks;
|
||||
AZ::ScriptTimePoint m_curTime;
|
||||
AZStd::unordered_map<AzToolsFramework::ViewportInteraction::MouseButton, ClickEvent> m_pendingDoubleClicks;
|
||||
|
||||
AZ::ScriptTimePoint m_currentTime;
|
||||
};
|
||||
} // namespace SandboxEditor
|
||||
|
||||
@@ -138,14 +138,11 @@ CViewportTitleDlg::CViewportTitleDlg(QWidget* pParent)
|
||||
|
||||
connect(this, &CViewportTitleDlg::ActionTriggered, MainWindow::instance()->GetActionManager(), &ActionManager::ActionTriggered);
|
||||
|
||||
AZ::VR::VREventBus::Handler::BusConnect();
|
||||
|
||||
OnInitDialog();
|
||||
}
|
||||
|
||||
CViewportTitleDlg::~CViewportTitleDlg()
|
||||
{
|
||||
AZ::VR::VREventBus::Handler::BusDisconnect();
|
||||
GetISystem()->GetISystemEventDispatcher()->RemoveListener(this);
|
||||
GetIEditor()->UnregisterNotifyListener(this);
|
||||
|
||||
@@ -236,10 +233,6 @@ void CViewportTitleDlg::SetupOverflowMenu()
|
||||
connect(m_audioMuteAction, &QAction::triggered, this, &CViewportTitleDlg::OnBnClickedMuteAudio);
|
||||
overFlowMenu->addAction(m_audioMuteAction);
|
||||
|
||||
m_enableVRAction = new QAction("Enable VR Preview", overFlowMenu);
|
||||
connect(m_enableVRAction, &QAction::triggered, this, &CViewportTitleDlg::OnBnClickedEnableVR);
|
||||
overFlowMenu->addAction(m_enableVRAction);
|
||||
|
||||
overFlowMenu->addSeparator();
|
||||
|
||||
m_enableGridSnappingAction = new QAction("Enable Grid Snapping", overFlowMenu);
|
||||
@@ -305,16 +298,6 @@ void CViewportTitleDlg::OnInitDialog()
|
||||
connect(displayInfoHelper, &CViewportTitleDlgDisplayInfoHelper::ViewportInfoStatusUpdated, this, &CViewportTitleDlg::UpdateDisplayInfo);
|
||||
UpdateDisplayInfo();
|
||||
|
||||
// This is here just in case this class hasn't been created before
|
||||
// a VR headset was initialized
|
||||
m_enableVRAction->setEnabled(false);
|
||||
if (AZ::VR::HMDDeviceRequestBus::GetTotalNumOfEventHandlers() != 0)
|
||||
{
|
||||
m_enableVRAction->setEnabled(true);
|
||||
}
|
||||
|
||||
AZ::VR::VREventBus::Handler::BusConnect();
|
||||
|
||||
QFontMetrics metrics({});
|
||||
int width = static_cast<int>(metrics.boundingRect("-9999.99").width() * m_fieldWidthMultiplier);
|
||||
|
||||
@@ -931,23 +914,6 @@ void CViewportTitleDlg::UpdateMuteActionText()
|
||||
}
|
||||
}
|
||||
|
||||
void CViewportTitleDlg::OnHMDInitialized()
|
||||
{
|
||||
m_enableVRAction->setEnabled(true);
|
||||
}
|
||||
|
||||
void CViewportTitleDlg::OnHMDShutdown()
|
||||
{
|
||||
m_enableVRAction->setEnabled(false);
|
||||
}
|
||||
|
||||
void CViewportTitleDlg::OnBnClickedEnableVR()
|
||||
{
|
||||
gSettings.bEnableGameModeVR = !gSettings.bEnableGameModeVR;
|
||||
|
||||
m_enableVRAction->setText(gSettings.bEnableGameModeVR ? tr("Disable VR Preview") : tr("Enable VR Preview"));
|
||||
}
|
||||
|
||||
inline double Round(double fVal, double fStep)
|
||||
{
|
||||
if (fStep > 0.f)
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h>
|
||||
#include <AzQtComponents/Components/Widgets/SpinBox.h>
|
||||
|
||||
#include <HMDBus.h>
|
||||
#endif
|
||||
|
||||
// CViewportTitleDlg dialog
|
||||
@@ -44,7 +43,6 @@ class CViewportTitleDlg
|
||||
: public QWidget
|
||||
, public IEditorNotifyListener
|
||||
, public ISystemEventListener
|
||||
, public AZ::VR::VREventBus::Handler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -85,13 +83,6 @@ protected:
|
||||
void OnToggleHelpers();
|
||||
void UpdateDisplayInfo();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// VR Event Bus Implementation
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void OnHMDInitialized() override;
|
||||
void OnHMDShutdown() override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SetupCameraDropdownMenu();
|
||||
void SetupResolutionDropdownMenu();
|
||||
void SetupViewportInformationMenu();
|
||||
@@ -140,7 +131,6 @@ protected:
|
||||
|
||||
void OnBnClickedGotoPosition();
|
||||
void OnBnClickedMuteAudio();
|
||||
void OnBnClickedEnableVR();
|
||||
|
||||
void UpdateMuteActionText();
|
||||
|
||||
@@ -168,7 +158,6 @@ protected:
|
||||
QAction* m_fullInformationAction = nullptr;
|
||||
QAction* m_compactInformationAction = nullptr;
|
||||
QAction* m_audioMuteAction = nullptr;
|
||||
QAction* m_enableVRAction = nullptr;
|
||||
QAction* m_enableGridSnappingAction = nullptr;
|
||||
QAction* m_enableAngleSnappingAction = nullptr;
|
||||
QComboBox* m_cameraSpeed = nullptr;
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
// AzFramework
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
|
||||
// AzToolsFramework
|
||||
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
|
||||
@@ -173,9 +171,6 @@ void WelcomeScreenDialog::SetRecentFileList(RecentFileList* pList)
|
||||
|
||||
m_pRecentList = pList;
|
||||
|
||||
const char* engineRoot;
|
||||
EBUS_EVENT_RESULT(engineRoot, AzFramework::ApplicationRequests::Bus, GetEngineRoot);
|
||||
|
||||
auto projectPath = AZ::Utils::GetProjectPath();
|
||||
QString gamePath{projectPath.c_str()};
|
||||
Path::ConvertSlashToBackSlash(gamePath);
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <AzCore/Debug/Timer.h>
|
||||
#include <AzCore/Debug/TraceMessageBus.h>
|
||||
#include <AzCore/std/typetraits/typetraits.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
|
||||
@@ -14,453 +14,450 @@
|
||||
#include <AzCore/std/parallel/lock.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Data
|
||||
{
|
||||
namespace Data
|
||||
AssetFilterInfo::AssetFilterInfo(const AssetId& id, const AssetType& assetType, AssetLoadBehavior loadBehavior)
|
||||
: m_assetId(id)
|
||||
, m_assetType(assetType)
|
||||
, m_loadBehavior(loadBehavior)
|
||||
{
|
||||
AssetFilterInfo::AssetFilterInfo(const AssetId& id, const AssetType& assetType, AssetLoadBehavior loadBehavior)
|
||||
: m_assetId(id)
|
||||
, m_assetType(assetType)
|
||||
, m_loadBehavior(loadBehavior)
|
||||
}
|
||||
|
||||
AssetFilterInfo::AssetFilterInfo(const Asset<AssetData>& asset)
|
||||
: m_assetId(asset.GetId())
|
||||
, m_assetType(asset.GetType())
|
||||
, m_loadBehavior(asset.GetAutoLoadBehavior())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
AssetId AssetId::CreateString(AZStd::string_view input)
|
||||
{
|
||||
size_t separatorIdx = input.find(':');
|
||||
if (separatorIdx == AZStd::string_view::npos)
|
||||
{
|
||||
return AssetId();
|
||||
}
|
||||
|
||||
AssetFilterInfo::AssetFilterInfo(const Asset<AssetData>& asset)
|
||||
: m_assetId(asset.GetId())
|
||||
, m_assetType(asset.GetType())
|
||||
, m_loadBehavior(asset.GetAutoLoadBehavior())
|
||||
AssetId assetId;
|
||||
assetId.m_guid = Uuid::CreateString(input.data(), separatorIdx);
|
||||
if (assetId.m_guid.IsNull())
|
||||
{
|
||||
return AssetId();
|
||||
}
|
||||
|
||||
assetId.m_subId = strtoul(&input[separatorIdx + 1], nullptr, 16);
|
||||
|
||||
AssetId AssetId::CreateString(AZStd::string_view input)
|
||||
return assetId;
|
||||
}
|
||||
|
||||
void AssetId::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
size_t separatorIdx = input.find(':');
|
||||
if (separatorIdx == AZStd::string_view::npos)
|
||||
{
|
||||
return AssetId();
|
||||
}
|
||||
|
||||
AssetId assetId;
|
||||
assetId.m_guid = Uuid::CreateString(input.data(), separatorIdx);
|
||||
if (assetId.m_guid.IsNull())
|
||||
{
|
||||
return AssetId();
|
||||
}
|
||||
|
||||
assetId.m_subId = strtoul(&input[separatorIdx + 1], nullptr, 16);
|
||||
|
||||
return assetId;
|
||||
serializeContext->Class<Data::AssetId>()
|
||||
->Version(1)
|
||||
->Field("guid", &Data::AssetId::m_guid)
|
||||
->Field("subId", &Data::AssetId::m_subId)
|
||||
;
|
||||
}
|
||||
|
||||
void AssetId::Reflect(AZ::ReflectContext* context)
|
||||
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<Data::AssetId>()
|
||||
->Version(1)
|
||||
->Field("guid", &Data::AssetId::m_guid)
|
||||
->Field("subId", &Data::AssetId::m_subId)
|
||||
;
|
||||
}
|
||||
behaviorContext->Class<Data::AssetId>()
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Asset")
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset")
|
||||
->Constructor()
|
||||
->Constructor<const Uuid&, u32>()
|
||||
->Method("CreateString", &Data::AssetId::CreateString)
|
||||
->Method("IsValid", &Data::AssetId::IsValid)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_valid")
|
||||
->Method("ToString", [](const Data::AssetId* self) { return self->ToString<AZStd::string>(); })
|
||||
->Attribute(AZ::Script::Attributes::Alias, "to_string")
|
||||
->Method("IsEqual", [](const Data::AssetId& self, const Data::AssetId& other) { return self == other; })
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_equal")
|
||||
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal)
|
||||
;
|
||||
|
||||
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<Data::AssetId>()
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Asset")
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset")
|
||||
->Constructor()
|
||||
->Constructor<const Uuid&, u32>()
|
||||
->Method("CreateString", &Data::AssetId::CreateString)
|
||||
->Method("IsValid", &Data::AssetId::IsValid)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_valid")
|
||||
->Method("ToString", [](const Data::AssetId* self) { return self->ToString<AZStd::string>(); })
|
||||
->Attribute(AZ::Script::Attributes::Alias, "to_string")
|
||||
->Method("IsEqual", [](const Data::AssetId& self, const Data::AssetId& other) { return self == other; })
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_equal")
|
||||
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal)
|
||||
;
|
||||
behaviorContext->Class<Data::AssetInfo>()
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Asset")
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset")
|
||||
->Property("assetId", BehaviorValueGetter(&Data::AssetInfo::m_assetId), nullptr)
|
||||
->Property("assetType", BehaviorValueGetter(&Data::AssetInfo::m_assetType), nullptr)
|
||||
->Property("sizeBytes", BehaviorValueGetter(&Data::AssetInfo::m_sizeBytes), nullptr)
|
||||
->Property("relativePath", BehaviorValueGetter(&Data::AssetInfo::m_relativePath), nullptr)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
behaviorContext->Class<Data::AssetInfo>()
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Asset")
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset")
|
||||
->Property("assetId", BehaviorValueGetter(&Data::AssetInfo::m_assetId), nullptr)
|
||||
->Property("assetType", BehaviorValueGetter(&Data::AssetInfo::m_assetType), nullptr)
|
||||
->Property("sizeBytes", BehaviorValueGetter(&Data::AssetInfo::m_sizeBytes), nullptr)
|
||||
->Property("relativePath", BehaviorValueGetter(&Data::AssetInfo::m_relativePath), nullptr)
|
||||
;
|
||||
}
|
||||
namespace AssetInternal
|
||||
{
|
||||
Asset<AssetData> FindOrCreateAsset(const AssetId& id, const AssetType& type, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
return AssetManager::Instance().FindOrCreateAsset(id, type, assetReferenceLoadBehavior);
|
||||
}
|
||||
|
||||
namespace AssetInternal
|
||||
Asset<AssetData> GetAsset(const AssetId& id, const AssetType& type, AssetLoadBehavior assetReferenceLoadBehavior,
|
||||
const AssetLoadParameters& loadParams)
|
||||
{
|
||||
Asset<AssetData> FindOrCreateAsset(const AssetId& id, const AssetType& type, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
return AssetManager::Instance().FindOrCreateAsset(id, type, assetReferenceLoadBehavior);
|
||||
}
|
||||
|
||||
Asset<AssetData> GetAsset(const AssetId& id, const AssetType& type, AssetLoadBehavior assetReferenceLoadBehavior,
|
||||
const AssetLoadParameters& loadParams)
|
||||
{
|
||||
return AssetManager::Instance().GetAsset(id, type, assetReferenceLoadBehavior, loadParams);
|
||||
}
|
||||
|
||||
AssetData::AssetStatus BlockUntilLoadComplete(const Asset<AssetData>& asset)
|
||||
{
|
||||
return AssetManager::Instance().BlockUntilLoadComplete(asset);
|
||||
}
|
||||
|
||||
void UpdateAssetInfo(AssetId& id, AZStd::string& assetHint)
|
||||
{
|
||||
// it is possible that the assetID given is legacy / old and we have a new assetId we can use instead for it.
|
||||
// in that case, upgrade the AssetID to the new one, so that future saves are in the new format.
|
||||
// this function should only be invoked if the feature is turned on in the asset manager as it can be (slightly) expensive
|
||||
|
||||
if ((!AssetManager::IsReady()) || (!AssetManager::Instance().GetAssetInfoUpgradingEnabled()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, id);
|
||||
if (assetInfo.m_assetId.IsValid())
|
||||
{
|
||||
id = assetInfo.m_assetId;
|
||||
if (!assetInfo.m_relativePath.empty())
|
||||
{
|
||||
assetHint = assetInfo.m_relativePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ReloadAsset(AssetData* assetData, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
AssetManager::Instance().ReloadAsset(assetData->GetId(), assetReferenceLoadBehavior);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveAsset(AssetData* assetData, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
AssetManager::Instance().SaveAsset({ assetData, assetReferenceLoadBehavior });
|
||||
return true;
|
||||
}
|
||||
|
||||
Asset<AssetData> GetAssetData(const AssetId& id, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
if (AssetManager::IsReady())
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> assetLock(AssetManager::Instance().m_assetMutex);
|
||||
auto it = AssetManager::Instance().m_assets.find(id);
|
||||
if (it != AssetManager::Instance().m_assets.end())
|
||||
{
|
||||
return { it->second, assetReferenceLoadBehavior };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
AssetId ResolveAssetId(const AssetId& id)
|
||||
{
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, id);
|
||||
if (assetInfo.m_assetId.IsValid())
|
||||
{
|
||||
return assetInfo.m_assetId;
|
||||
}
|
||||
else
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
return AssetManager::Instance().GetAsset(id, type, assetReferenceLoadBehavior, loadParams);
|
||||
}
|
||||
|
||||
AssetData::~AssetData()
|
||||
AssetData::AssetStatus BlockUntilLoadComplete(const Asset<AssetData>& asset)
|
||||
{
|
||||
UnregisterWithHandler();
|
||||
return AssetManager::Instance().BlockUntilLoadComplete(asset);
|
||||
}
|
||||
|
||||
void AssetData::Reflect(AZ::ReflectContext* context)
|
||||
void UpdateAssetInfo(AssetId& id, AZStd::string& assetHint)
|
||||
{
|
||||
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
// it is possible that the assetID given is legacy / old and we have a new assetId we can use instead for it.
|
||||
// in that case, upgrade the AssetID to the new one, so that future saves are in the new format.
|
||||
// this function should only be invoked if the feature is turned on in the asset manager as it can be (slightly) expensive
|
||||
|
||||
if ((!AssetManager::IsReady()) || (!AssetManager::Instance().GetAssetInfoUpgradingEnabled()))
|
||||
{
|
||||
serializeContext->Class<AZ::Data::AssetData>()
|
||||
->Version(1)
|
||||
;
|
||||
}
|
||||
|
||||
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<AssetData>("AssetData")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Asset")
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset")
|
||||
->Method("IsReady", &AssetData::IsReady)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_ready")
|
||||
->Method("IsError", &AssetData::IsError)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_error")
|
||||
->Method("IsLoading", &AssetData::IsLoading)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_loading")
|
||||
->Method("GetId", &AssetData::GetId)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "get_id")
|
||||
->Method("GetUseCount", &AssetData::GetUseCount)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "get_use_count")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void AssetData::Acquire()
|
||||
{
|
||||
AZ_Assert(m_useCount >= 0, "AssetData has been deleted");
|
||||
|
||||
AcquireWeak();
|
||||
++m_useCount;
|
||||
}
|
||||
|
||||
void AssetData::Release()
|
||||
{
|
||||
AZ_Assert(m_useCount > 0, "Usecount is already 0!");
|
||||
|
||||
if (m_useCount.fetch_sub(1) == 1)
|
||||
{
|
||||
if (AssetManager::IsReady())
|
||||
{
|
||||
AssetManager::Instance().OnAssetUnused(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Attempting to release asset after AssetManager has been destroyed!");
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseWeak();
|
||||
}
|
||||
|
||||
void AssetData::AcquireWeak()
|
||||
{
|
||||
AZ_Assert(m_useCount >= 0, "AssetData has been deleted");
|
||||
++m_weakUseCount;
|
||||
}
|
||||
|
||||
void AssetData::ReleaseWeak()
|
||||
{
|
||||
AZ_Assert(m_weakUseCount > 0, "WeakUseCount is already 0");
|
||||
|
||||
AssetId assetId = m_assetId;
|
||||
int creationToken = m_creationToken;
|
||||
AssetType assetType = GetType();
|
||||
bool removeFromHash = IsRegisterReadonlyAndShareable();
|
||||
// default creation token implies that the asset was not created by the asset manager and therefore it cannot be in the asset map.
|
||||
removeFromHash = creationToken == s_defaultCreationToken ? false : removeFromHash;
|
||||
|
||||
if (m_weakUseCount.fetch_sub(1) == 1)
|
||||
{
|
||||
if (AssetManager::IsReady())
|
||||
{
|
||||
AssetManager::Instance().ReleaseAsset(this, assetId, assetType, removeFromHash, creationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Attempting to release asset after AssetManager has been destroyed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetData::IsLoading(bool includeQueued) const
|
||||
{
|
||||
auto curStatus = GetStatus();
|
||||
return(curStatus == AssetStatus::Loading || curStatus == AssetStatus::LoadedPreReady || curStatus==AssetStatus::StreamReady ||
|
||||
(includeQueued && curStatus == AssetStatus::Queued));
|
||||
}
|
||||
|
||||
void AssetData::RegisterWithHandler(AssetHandler* handler)
|
||||
{
|
||||
if (!handler)
|
||||
{
|
||||
AZ_Error("AssetData", false, "No handler to register with");
|
||||
return;
|
||||
}
|
||||
m_registeredHandler = handler;
|
||||
}
|
||||
|
||||
void AssetData::UnregisterWithHandler()
|
||||
{
|
||||
if (m_registeredHandler)
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, id);
|
||||
if (assetInfo.m_assetId.IsValid())
|
||||
{
|
||||
m_registeredHandler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetData::GetFlag(const AssetDataFlags& checkFlag) const
|
||||
{
|
||||
return m_flags[aznumeric_cast<AZStd::size_t>(checkFlag)];
|
||||
}
|
||||
|
||||
void AssetData::SetFlag(const AssetDataFlags& checkFlag, bool setValue)
|
||||
{
|
||||
m_flags.set(aznumeric_cast<AZStd::size_t>(checkFlag), setValue);
|
||||
}
|
||||
|
||||
bool AssetData::GetRequeue() const
|
||||
{
|
||||
return GetFlag(AssetDataFlags::Requeue);
|
||||
}
|
||||
void AssetData::SetRequeue(bool requeue)
|
||||
{
|
||||
SetFlag(AssetDataFlags::Requeue, requeue);
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetCallbacks(const AssetReadyCB& readyCB, const AssetMovedCB& movedCB, const AssetReloadedCB& reloadedCB,
|
||||
const AssetSavedCB& savedCB, const AssetUnloadedCB& unloadedCB, const AssetErrorCB& errorCB, const AssetCanceledCB& cancelCB)
|
||||
{
|
||||
m_onAssetReadyCB = readyCB;
|
||||
m_onAssetMovedCB = movedCB;
|
||||
m_onAssetReloadedCB = reloadedCB;
|
||||
m_onAssetSavedCB = savedCB;
|
||||
m_onAssetUnloadedCB = unloadedCB;
|
||||
m_onAssetErrorCB = errorCB;
|
||||
m_onAssetCanceledCB = cancelCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::ClearCallbacks()
|
||||
{
|
||||
SetCallbacks(AssetBusCallbacks::AssetReadyCB(),
|
||||
AssetBusCallbacks::AssetMovedCB(),
|
||||
AssetBusCallbacks::AssetReloadedCB(),
|
||||
AssetBusCallbacks::AssetSavedCB(),
|
||||
AssetBusCallbacks::AssetUnloadedCB(),
|
||||
AssetBusCallbacks::AssetErrorCB(),
|
||||
AssetBusCallbacks::AssetCanceledCB());
|
||||
}
|
||||
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetReadyCallback(const AssetReadyCB& readyCB)
|
||||
{
|
||||
m_onAssetReadyCB = readyCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetMovedCallback(const AssetMovedCB& movedCB)
|
||||
{
|
||||
m_onAssetMovedCB = movedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetReloadedCallback(const AssetReloadedCB& reloadedCB)
|
||||
{
|
||||
m_onAssetReloadedCB = reloadedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetSavedCallback(const AssetSavedCB& savedCB)
|
||||
{
|
||||
m_onAssetSavedCB = savedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetUnloadedCallback(const AssetUnloadedCB& unloadedCB)
|
||||
{
|
||||
m_onAssetUnloadedCB = unloadedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetErrorCallback(const AssetErrorCB& errorCB)
|
||||
{
|
||||
m_onAssetErrorCB = errorCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetCanceledCallback(const AssetCanceledCB& cancelCB)
|
||||
{
|
||||
m_onAssetCanceledCB = cancelCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetReady(Asset<AssetData> asset)
|
||||
{
|
||||
if (m_onAssetReadyCB)
|
||||
{
|
||||
m_onAssetReadyCB(asset, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetMoved(Asset<AssetData> asset, void* oldDataPointer)
|
||||
{
|
||||
if (m_onAssetMovedCB)
|
||||
{
|
||||
m_onAssetMovedCB(asset, oldDataPointer, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetReloaded(Asset<AssetData> asset)
|
||||
{
|
||||
if (m_onAssetReloadedCB)
|
||||
{
|
||||
m_onAssetReloadedCB(asset, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetSaved(Asset<AssetData> asset, bool isSuccessful)
|
||||
{
|
||||
if (m_onAssetSavedCB)
|
||||
{
|
||||
m_onAssetSavedCB(asset, isSuccessful, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetUnloaded(const AssetId assetId, const AssetType assetType)
|
||||
{
|
||||
if (m_onAssetUnloadedCB)
|
||||
{
|
||||
m_onAssetUnloadedCB(assetId, assetType, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetError(Asset<AssetData> asset)
|
||||
{
|
||||
if (m_onAssetErrorCB)
|
||||
{
|
||||
m_onAssetErrorCB(asset, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetCanceled(const AssetId assetId)
|
||||
{
|
||||
if (m_onAssetCanceledCB)
|
||||
{
|
||||
m_onAssetCanceledCB(assetId, *this);
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ bool AssetFilterNoAssetLoading([[maybe_unused]] const AssetFilterInfo& filterInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
namespace ProductDependencyInfo
|
||||
{
|
||||
AZ::Data::AssetLoadBehavior LoadBehaviorFromFlags(const ProductDependencyFlags& dependencyFlags)
|
||||
{
|
||||
AZ::u8 loadBehaviorValue = 0;
|
||||
for (AZ::u8 thisFlag = aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorLow);
|
||||
thisFlag <= aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorHigh); ++thisFlag)
|
||||
id = assetInfo.m_assetId;
|
||||
if (!assetInfo.m_relativePath.empty())
|
||||
{
|
||||
if (dependencyFlags[thisFlag])
|
||||
{
|
||||
loadBehaviorValue |= (1 << thisFlag);
|
||||
}
|
||||
assetHint = assetInfo.m_relativePath;
|
||||
}
|
||||
return static_cast<AZ::Data::AssetLoadBehavior>(loadBehaviorValue);
|
||||
}
|
||||
|
||||
ProductDependencyFlags CreateFlags(AZ::Data::AssetLoadBehavior autoLoadBehavior)
|
||||
{
|
||||
AZ::Data::ProductDependencyInfo::ProductDependencyFlags returnFlags;
|
||||
AZ::u8 loadBehavior = aznumeric_caster(autoLoadBehavior);
|
||||
for (AZ::u8 thisFlag = aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorLow);
|
||||
thisFlag <= aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorHigh); ++thisFlag)
|
||||
{
|
||||
if (loadBehavior & (1 << thisFlag))
|
||||
{
|
||||
returnFlags[thisFlag] = true;
|
||||
}
|
||||
}
|
||||
return returnFlags;
|
||||
}
|
||||
}
|
||||
} // namespace Data
|
||||
} // namespace AZ
|
||||
|
||||
bool ReloadAsset(AssetData* assetData, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
AssetManager::Instance().ReloadAsset(assetData->GetId(), assetReferenceLoadBehavior);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveAsset(AssetData* assetData, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
AssetManager::Instance().SaveAsset({ assetData, assetReferenceLoadBehavior });
|
||||
return true;
|
||||
}
|
||||
|
||||
Asset<AssetData> GetAssetData(const AssetId& id, AssetLoadBehavior assetReferenceLoadBehavior)
|
||||
{
|
||||
if (AssetManager::IsReady())
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> assetLock(AssetManager::Instance().m_assetMutex);
|
||||
auto it = AssetManager::Instance().m_assets.find(id);
|
||||
if (it != AssetManager::Instance().m_assets.end())
|
||||
{
|
||||
return { it->second, assetReferenceLoadBehavior };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
AssetId ResolveAssetId(const AssetId& id)
|
||||
{
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, id);
|
||||
if (assetInfo.m_assetId.IsValid())
|
||||
{
|
||||
return assetInfo.m_assetId;
|
||||
}
|
||||
else
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
AssetData::~AssetData()
|
||||
{
|
||||
UnregisterWithHandler();
|
||||
}
|
||||
|
||||
void AssetData::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<AZ::Data::AssetData>()
|
||||
->Version(1)
|
||||
;
|
||||
}
|
||||
|
||||
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<AssetData>("AssetData")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Asset")
|
||||
->Attribute(AZ::Script::Attributes::Module, "asset")
|
||||
->Method("IsReady", &AssetData::IsReady)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_ready")
|
||||
->Method("IsError", &AssetData::IsError)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_error")
|
||||
->Method("IsLoading", &AssetData::IsLoading)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "is_loading")
|
||||
->Method("GetId", &AssetData::GetId)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "get_id")
|
||||
->Method("GetUseCount", &AssetData::GetUseCount)
|
||||
->Attribute(AZ::Script::Attributes::Alias, "get_use_count")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void AssetData::Acquire()
|
||||
{
|
||||
AZ_Assert(m_useCount >= 0, "AssetData has been deleted");
|
||||
|
||||
AcquireWeak();
|
||||
++m_useCount;
|
||||
}
|
||||
|
||||
void AssetData::Release()
|
||||
{
|
||||
AZ_Assert(m_useCount > 0, "Usecount is already 0!");
|
||||
|
||||
if (m_useCount.fetch_sub(1) == 1)
|
||||
{
|
||||
if (AssetManager::IsReady())
|
||||
{
|
||||
AssetManager::Instance().OnAssetUnused(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Attempting to release asset after AssetManager has been destroyed!");
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseWeak();
|
||||
}
|
||||
|
||||
void AssetData::AcquireWeak()
|
||||
{
|
||||
AZ_Assert(m_useCount >= 0, "AssetData has been deleted");
|
||||
++m_weakUseCount;
|
||||
}
|
||||
|
||||
void AssetData::ReleaseWeak()
|
||||
{
|
||||
AZ_Assert(m_weakUseCount > 0, "WeakUseCount is already 0");
|
||||
|
||||
AssetId assetId = m_assetId;
|
||||
int creationToken = m_creationToken;
|
||||
AssetType assetType = GetType();
|
||||
bool removeFromHash = IsRegisterReadonlyAndShareable();
|
||||
// default creation token implies that the asset was not created by the asset manager and therefore it cannot be in the asset map.
|
||||
removeFromHash = creationToken == s_defaultCreationToken ? false : removeFromHash;
|
||||
|
||||
if (m_weakUseCount.fetch_sub(1) == 1)
|
||||
{
|
||||
if (AssetManager::IsReady())
|
||||
{
|
||||
AssetManager::Instance().ReleaseAsset(this, assetId, assetType, removeFromHash, creationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Attempting to release asset after AssetManager has been destroyed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetData::IsLoading(bool includeQueued) const
|
||||
{
|
||||
auto curStatus = GetStatus();
|
||||
return(curStatus == AssetStatus::Loading || curStatus == AssetStatus::LoadedPreReady || curStatus==AssetStatus::StreamReady ||
|
||||
(includeQueued && curStatus == AssetStatus::Queued));
|
||||
}
|
||||
|
||||
void AssetData::RegisterWithHandler(AssetHandler* handler)
|
||||
{
|
||||
if (!handler)
|
||||
{
|
||||
AZ_Error("AssetData", false, "No handler to register with");
|
||||
return;
|
||||
}
|
||||
m_registeredHandler = handler;
|
||||
}
|
||||
|
||||
void AssetData::UnregisterWithHandler()
|
||||
{
|
||||
if (m_registeredHandler)
|
||||
{
|
||||
m_registeredHandler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetData::GetFlag(const AssetDataFlags& checkFlag) const
|
||||
{
|
||||
return m_flags[aznumeric_cast<AZStd::size_t>(checkFlag)];
|
||||
}
|
||||
|
||||
void AssetData::SetFlag(const AssetDataFlags& checkFlag, bool setValue)
|
||||
{
|
||||
m_flags.set(aznumeric_cast<AZStd::size_t>(checkFlag), setValue);
|
||||
}
|
||||
|
||||
bool AssetData::GetRequeue() const
|
||||
{
|
||||
return GetFlag(AssetDataFlags::Requeue);
|
||||
}
|
||||
void AssetData::SetRequeue(bool requeue)
|
||||
{
|
||||
SetFlag(AssetDataFlags::Requeue, requeue);
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetCallbacks(const AssetReadyCB& readyCB, const AssetMovedCB& movedCB, const AssetReloadedCB& reloadedCB,
|
||||
const AssetSavedCB& savedCB, const AssetUnloadedCB& unloadedCB, const AssetErrorCB& errorCB, const AssetCanceledCB& cancelCB)
|
||||
{
|
||||
m_onAssetReadyCB = readyCB;
|
||||
m_onAssetMovedCB = movedCB;
|
||||
m_onAssetReloadedCB = reloadedCB;
|
||||
m_onAssetSavedCB = savedCB;
|
||||
m_onAssetUnloadedCB = unloadedCB;
|
||||
m_onAssetErrorCB = errorCB;
|
||||
m_onAssetCanceledCB = cancelCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::ClearCallbacks()
|
||||
{
|
||||
SetCallbacks(AssetBusCallbacks::AssetReadyCB(),
|
||||
AssetBusCallbacks::AssetMovedCB(),
|
||||
AssetBusCallbacks::AssetReloadedCB(),
|
||||
AssetBusCallbacks::AssetSavedCB(),
|
||||
AssetBusCallbacks::AssetUnloadedCB(),
|
||||
AssetBusCallbacks::AssetErrorCB(),
|
||||
AssetBusCallbacks::AssetCanceledCB());
|
||||
}
|
||||
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetReadyCallback(const AssetReadyCB& readyCB)
|
||||
{
|
||||
m_onAssetReadyCB = readyCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetMovedCallback(const AssetMovedCB& movedCB)
|
||||
{
|
||||
m_onAssetMovedCB = movedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetReloadedCallback(const AssetReloadedCB& reloadedCB)
|
||||
{
|
||||
m_onAssetReloadedCB = reloadedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetSavedCallback(const AssetSavedCB& savedCB)
|
||||
{
|
||||
m_onAssetSavedCB = savedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetUnloadedCallback(const AssetUnloadedCB& unloadedCB)
|
||||
{
|
||||
m_onAssetUnloadedCB = unloadedCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetErrorCallback(const AssetErrorCB& errorCB)
|
||||
{
|
||||
m_onAssetErrorCB = errorCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::SetOnAssetCanceledCallback(const AssetCanceledCB& cancelCB)
|
||||
{
|
||||
m_onAssetCanceledCB = cancelCB;
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetReady(Asset<AssetData> asset)
|
||||
{
|
||||
if (m_onAssetReadyCB)
|
||||
{
|
||||
m_onAssetReadyCB(asset, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetMoved(Asset<AssetData> asset, void* oldDataPointer)
|
||||
{
|
||||
if (m_onAssetMovedCB)
|
||||
{
|
||||
m_onAssetMovedCB(asset, oldDataPointer, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetReloaded(Asset<AssetData> asset)
|
||||
{
|
||||
if (m_onAssetReloadedCB)
|
||||
{
|
||||
m_onAssetReloadedCB(asset, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetSaved(Asset<AssetData> asset, bool isSuccessful)
|
||||
{
|
||||
if (m_onAssetSavedCB)
|
||||
{
|
||||
m_onAssetSavedCB(asset, isSuccessful, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetUnloaded(const AssetId assetId, const AssetType assetType)
|
||||
{
|
||||
if (m_onAssetUnloadedCB)
|
||||
{
|
||||
m_onAssetUnloadedCB(assetId, assetType, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetError(Asset<AssetData> asset)
|
||||
{
|
||||
if (m_onAssetErrorCB)
|
||||
{
|
||||
m_onAssetErrorCB(asset, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBusCallbacks::OnAssetCanceled(const AssetId assetId)
|
||||
{
|
||||
if (m_onAssetCanceledCB)
|
||||
{
|
||||
m_onAssetCanceledCB(assetId, *this);
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ bool AssetFilterNoAssetLoading([[maybe_unused]] const AssetFilterInfo& filterInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
namespace ProductDependencyInfo
|
||||
{
|
||||
AZ::Data::AssetLoadBehavior LoadBehaviorFromFlags(const ProductDependencyFlags& dependencyFlags)
|
||||
{
|
||||
AZ::u8 loadBehaviorValue = 0;
|
||||
for (AZ::u8 thisFlag = aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorLow);
|
||||
thisFlag <= aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorHigh); ++thisFlag)
|
||||
{
|
||||
if (dependencyFlags[thisFlag])
|
||||
{
|
||||
loadBehaviorValue |= (1 << thisFlag);
|
||||
}
|
||||
}
|
||||
return static_cast<AZ::Data::AssetLoadBehavior>(loadBehaviorValue);
|
||||
}
|
||||
|
||||
ProductDependencyFlags CreateFlags(AZ::Data::AssetLoadBehavior autoLoadBehavior)
|
||||
{
|
||||
AZ::Data::ProductDependencyInfo::ProductDependencyFlags returnFlags;
|
||||
AZ::u8 loadBehavior = aznumeric_caster(autoLoadBehavior);
|
||||
for (AZ::u8 thisFlag = aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorLow);
|
||||
thisFlag <= aznumeric_cast<AZ::u8>(ProductDependencyFlagBits::LoadBehaviorHigh); ++thisFlag)
|
||||
{
|
||||
if (loadBehavior & (1 << thisFlag))
|
||||
{
|
||||
returnFlags[thisFlag] = true;
|
||||
}
|
||||
}
|
||||
return returnFlags;
|
||||
}
|
||||
}
|
||||
} // namespace AZ::Data
|
||||
|
||||
@@ -556,16 +556,24 @@ namespace AZ
|
||||
Asset<AssetData> assetData(AssetInternal::GetAssetData(actualId, AZ::Data::AssetLoadBehavior::Default));
|
||||
if (assetData)
|
||||
{
|
||||
auto curStatus = assetData->GetStatus();
|
||||
auto isReady = assetData->GetStatus() == AssetData::AssetStatus::Ready;
|
||||
bool isError = assetData->IsError();
|
||||
connectLock.unlock();
|
||||
if (curStatus == AssetData::AssetStatus::Ready)
|
||||
|
||||
if (isReady || isError)
|
||||
{
|
||||
handler->OnAssetReady(assetData);
|
||||
}
|
||||
else if (isError)
|
||||
{
|
||||
handler->OnAssetError(assetData);
|
||||
connectLock.unlock();
|
||||
|
||||
if (isReady)
|
||||
{
|
||||
handler->OnAssetReady(assetData);
|
||||
}
|
||||
else if (isError)
|
||||
{
|
||||
handler->OnAssetError(assetData);
|
||||
}
|
||||
|
||||
// Lock the mutex again since some destructors will be modifying the context afterwards
|
||||
connectLock.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,8 +24,8 @@ namespace AZ
|
||||
|
||||
// AssetContainer loads an asset and all of its dependencies as a collection which is parallellized as much as possible.
|
||||
// With the container, the data will all load in parallel. Dependent asset loads will still obey the expected rules
|
||||
// where PreLoad assets will emit OnAssetReady before the parent does, and QueueLoad assets will emit OnAssetReady in
|
||||
// no guaranteed order. However, the OnAssetContainerReady signals will not emit until all PreLoad and QueueLoad assets
|
||||
// where PreLoad assets will emit OnAssetReady before the parent does, and QueueLoad assets will emit OnAssetReady in
|
||||
// no guaranteed order. However, the OnAssetContainerReady signals will not emit until all PreLoad and QueueLoad assets
|
||||
// are ready. NoLoad dependencies are not loaded by default but can be loaded along with their dependencies using the
|
||||
// same rules as above by using the LoadAll dependency rule.
|
||||
class AssetContainer :
|
||||
@@ -36,7 +36,7 @@ namespace AZ
|
||||
AZ_CLASS_ALLOCATOR(AssetContainer, SystemAllocator, 0);
|
||||
|
||||
AssetContainer() = default;
|
||||
|
||||
|
||||
AssetContainer(Asset<AssetData> asset, const AssetLoadParameters& loadParams);
|
||||
~AssetContainer();
|
||||
|
||||
@@ -81,6 +81,10 @@ namespace AZ
|
||||
// AssetLoadBus
|
||||
void OnAssetDataLoaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
|
||||
protected:
|
||||
|
||||
virtual AZStd::vector<AZStd::pair<AssetInfo, Asset<AssetData>>> CreateAndQueueDependentAssets(
|
||||
const AZStd::vector<AssetInfo>& dependencyInfoList, const AssetLoadParameters& loadParamsCopyWithNoLoadingFilter);
|
||||
|
||||
// Waiting assets are those which have not yet signalled ready. In the case of PreLoad dependencies the data may have completed the load cycle but
|
||||
// the Assets aren't considered "Ready" yet if there are PreLoad dependencies still loading and will still be in the list until the point that asset and
|
||||
// All of its preload dependencies have been loaded, when it signals OnAssetReady
|
||||
@@ -97,7 +101,7 @@ namespace AZ
|
||||
void AddDependency(Asset<AssetData>&& addDependency);
|
||||
|
||||
// Add a "graph section" to our list of dependencies. This checks the catalog for all Pre and Queue load assets which are dependents of the requested asset and kicks off loads
|
||||
// NoLoads which are encounted are placed in another list and can be loaded on demand with the LoadDependency call.
|
||||
// NoLoads which are encounted are placed in another list and can be loaded on demand with the LoadDependency call.
|
||||
void AddDependentAssets(Asset<AssetData> rootAsset, const AssetLoadParameters& loadParams);
|
||||
|
||||
// If "PreLoad" assets are found in the graph these are cached and tracked with both OnAssetReady and OnAssetDataLoaded messages.
|
||||
@@ -117,7 +121,7 @@ namespace AZ
|
||||
// duringInit if we're coming from the checkReady method - containers that start ready don't need to signal
|
||||
void HandleReadyAsset(AZ::Data::Asset<AZ::Data::AssetData> asset);
|
||||
|
||||
// Optimization to save the lookup in the dependencies map
|
||||
// Optimization to save the lookup in the dependencies map
|
||||
AssetInternal::WeakAsset<AssetData> m_rootAsset;
|
||||
|
||||
// The root asset id is stored here semi-redundantly on initialization so that we can still refer to it even if the
|
||||
@@ -136,7 +140,7 @@ namespace AZ
|
||||
AZStd::atomic_bool m_finalNotificationSent{false};
|
||||
|
||||
mutable AZStd::recursive_mutex m_preloadMutex;
|
||||
// AssetId -> List of assets it is still waiting on
|
||||
// AssetId -> List of assets it is still waiting on
|
||||
PreloadAssetListType m_preloadList;
|
||||
|
||||
// AssetId -> List of assets waiting on it
|
||||
|
||||
@@ -12,207 +12,204 @@
|
||||
#include <AzCore/Serialization/Json/StackedString.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Data
|
||||
{
|
||||
namespace Data
|
||||
AZ_CLASS_ALLOCATOR_IMPL(AssetJsonSerializer, SystemAllocator, 0);
|
||||
|
||||
JsonSerializationResult::Result AssetJsonSerializer::Load(void* outputValue, const Uuid& /*outputValueTypeId*/,
|
||||
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(AssetJsonSerializer, SystemAllocator, 0);
|
||||
namespace JSR = JsonSerializationResult;
|
||||
|
||||
JsonSerializationResult::Result AssetJsonSerializer::Load(void* outputValue, const Uuid& /*outputValueTypeId*/,
|
||||
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
|
||||
switch (inputValue.GetType())
|
||||
{
|
||||
namespace JSR = JsonSerializationResult;
|
||||
case rapidjson::kObjectType:
|
||||
return LoadAsset(outputValue, inputValue, context);
|
||||
case rapidjson::kArrayType: // fall through
|
||||
case rapidjson::kNullType: // fall through
|
||||
case rapidjson::kStringType: // fall through
|
||||
case rapidjson::kFalseType: // fall through
|
||||
case rapidjson::kTrueType: // fall through
|
||||
case rapidjson::kNumberType:
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
|
||||
"Unsupported type. Asset<T> can only be read from an object.");
|
||||
|
||||
switch (inputValue.GetType())
|
||||
default:
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown, "Unknown json type encountered for Asset<T>.");
|
||||
}
|
||||
}
|
||||
|
||||
JsonSerializationResult::Result AssetJsonSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
|
||||
const void* defaultValue, const Uuid& /*valueTypeId*/, JsonSerializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult;
|
||||
|
||||
const Asset<AssetData>* instance = reinterpret_cast<const Asset<AssetData>*>(inputValue);
|
||||
const Asset<AssetData>* defaultInstance = reinterpret_cast<const Asset<AssetData>*>(defaultValue);
|
||||
|
||||
JSR::ResultCode result(JSR::Tasks::WriteValue);
|
||||
{
|
||||
ScopedContextPath subPathId(context, "m_assetId");
|
||||
const auto* id = &instance->GetId();
|
||||
const auto* defaultId = defaultInstance ? &defaultInstance->GetId() : nullptr;
|
||||
rapidjson::Value assetIdValue;
|
||||
result = ContinueStoring(assetIdValue, id, defaultId, azrtti_typeid<AssetId>(), context);
|
||||
if (result.GetOutcome() == JSR::Outcomes::Success || result.GetOutcome() == JSR::Outcomes::PartialDefaults)
|
||||
{
|
||||
case rapidjson::kObjectType:
|
||||
return LoadAsset(outputValue, inputValue, context);
|
||||
case rapidjson::kArrayType: // fall through
|
||||
case rapidjson::kNullType: // fall through
|
||||
case rapidjson::kStringType: // fall through
|
||||
case rapidjson::kFalseType: // fall through
|
||||
case rapidjson::kTrueType: // fall through
|
||||
case rapidjson::kNumberType:
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
|
||||
"Unsupported type. Asset<T> can only be read from an object.");
|
||||
|
||||
default:
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown, "Unknown json type encountered for Asset<T>.");
|
||||
if (!outputValue.IsObject())
|
||||
{
|
||||
outputValue.SetObject();
|
||||
}
|
||||
outputValue.AddMember(rapidjson::StringRef("assetId"), AZStd::move(assetIdValue), context.GetJsonAllocator());
|
||||
}
|
||||
}
|
||||
|
||||
JsonSerializationResult::Result AssetJsonSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
|
||||
const void* defaultValue, const Uuid& /*valueTypeId*/, JsonSerializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult;
|
||||
const AZ::Data::AssetLoadBehavior autoLoadBehavior = instance->GetAutoLoadBehavior();
|
||||
const AZ::Data::AssetLoadBehavior defaultAutoLoadBehavior = defaultInstance ?
|
||||
defaultInstance->GetAutoLoadBehavior() : AZ::Data::AssetLoadBehavior::Default;
|
||||
|
||||
const Asset<AssetData>* instance = reinterpret_cast<const Asset<AssetData>*>(inputValue);
|
||||
const Asset<AssetData>* defaultInstance = reinterpret_cast<const Asset<AssetData>*>(defaultValue);
|
||||
|
||||
JSR::ResultCode result(JSR::Tasks::WriteValue);
|
||||
{
|
||||
ScopedContextPath subPathId(context, "m_assetId");
|
||||
const auto* id = &instance->GetId();
|
||||
const auto* defaultId = defaultInstance ? &defaultInstance->GetId() : nullptr;
|
||||
rapidjson::Value assetIdValue;
|
||||
result = ContinueStoring(assetIdValue, id, defaultId, azrtti_typeid<AssetId>(), context);
|
||||
if (result.GetOutcome() == JSR::Outcomes::Success || result.GetOutcome() == JSR::Outcomes::PartialDefaults)
|
||||
{
|
||||
if (!outputValue.IsObject())
|
||||
{
|
||||
outputValue.SetObject();
|
||||
}
|
||||
outputValue.AddMember(rapidjson::StringRef("assetId"), AZStd::move(assetIdValue), context.GetJsonAllocator());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const AZ::Data::AssetLoadBehavior autoLoadBehavior = instance->GetAutoLoadBehavior();
|
||||
const AZ::Data::AssetLoadBehavior defaultAutoLoadBehavior = defaultInstance ?
|
||||
defaultInstance->GetAutoLoadBehavior() : AZ::Data::AssetLoadBehavior::Default;
|
||||
|
||||
result.Combine(
|
||||
ContinueStoringToJsonObjectField(outputValue, "loadBehavior",
|
||||
&autoLoadBehavior, &defaultAutoLoadBehavior,
|
||||
azrtti_typeid<Data::AssetLoadBehavior>(), context));
|
||||
}
|
||||
|
||||
{
|
||||
ScopedContextPath subPathHint(context, "m_assetHint");
|
||||
const AZStd::string* hint = &instance->GetHint();
|
||||
const AZStd::string defaultHint;
|
||||
rapidjson::Value assetHintValue;
|
||||
JSR::ResultCode resultHint = ContinueStoring(assetHintValue, hint, &defaultHint, azrtti_typeid<AZStd::string>(), context);
|
||||
if (resultHint.GetOutcome() == JSR::Outcomes::Success || resultHint.GetOutcome() == JSR::Outcomes::PartialDefaults)
|
||||
{
|
||||
if (!outputValue.IsObject())
|
||||
{
|
||||
outputValue.SetObject();
|
||||
}
|
||||
outputValue.AddMember(rapidjson::StringRef("assetHint"), AZStd::move(assetHintValue), context.GetJsonAllocator());
|
||||
}
|
||||
result.Combine(resultHint);
|
||||
}
|
||||
|
||||
return context.Report(result,
|
||||
result.GetProcessing() == JSR::Processing::Completed ? "Successfully stored Asset<T>." : "Failed to store Asset<T>.");
|
||||
result.Combine(
|
||||
ContinueStoringToJsonObjectField(outputValue, "loadBehavior",
|
||||
&autoLoadBehavior, &defaultAutoLoadBehavior,
|
||||
azrtti_typeid<Data::AssetLoadBehavior>(), context));
|
||||
}
|
||||
|
||||
JsonSerializationResult::Result AssetJsonSerializer::LoadAsset(void* outputValue, const rapidjson::Value& inputValue,
|
||||
JsonDeserializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult;
|
||||
|
||||
Asset<AssetData>* instance = reinterpret_cast<Asset<AssetData>*>(outputValue);
|
||||
AssetId id;
|
||||
JSR::ResultCode result(JSR::Tasks::ReadField);
|
||||
|
||||
SerializedAssetTracker* assetTracker =
|
||||
context.GetMetadata().Find<SerializedAssetTracker>();
|
||||
|
||||
ScopedContextPath subPathHint(context, "m_assetHint");
|
||||
const AZStd::string* hint = &instance->GetHint();
|
||||
const AZStd::string defaultHint;
|
||||
rapidjson::Value assetHintValue;
|
||||
JSR::ResultCode resultHint = ContinueStoring(assetHintValue, hint, &defaultHint, azrtti_typeid<AZStd::string>(), context);
|
||||
if (resultHint.GetOutcome() == JSR::Outcomes::Success || resultHint.GetOutcome() == JSR::Outcomes::PartialDefaults)
|
||||
{
|
||||
Data::AssetLoadBehavior loadBehavior = instance->GetAutoLoadBehavior();
|
||||
|
||||
result =
|
||||
ContinueLoadingFromJsonObjectField(&loadBehavior,
|
||||
azrtti_typeid<Data::AssetLoadBehavior>(),
|
||||
inputValue, "loadBehavior", context);
|
||||
|
||||
instance->SetAutoLoadBehavior(loadBehavior);
|
||||
if (!outputValue.IsObject())
|
||||
{
|
||||
outputValue.SetObject();
|
||||
}
|
||||
outputValue.AddMember(rapidjson::StringRef("assetHint"), AZStd::move(assetHintValue), context.GetJsonAllocator());
|
||||
}
|
||||
result.Combine(resultHint);
|
||||
}
|
||||
|
||||
auto it = inputValue.FindMember("assetId");
|
||||
if (it != inputValue.MemberEnd())
|
||||
return context.Report(result,
|
||||
result.GetProcessing() == JSR::Processing::Completed ? "Successfully stored Asset<T>." : "Failed to store Asset<T>.");
|
||||
}
|
||||
|
||||
JsonSerializationResult::Result AssetJsonSerializer::LoadAsset(void* outputValue, const rapidjson::Value& inputValue,
|
||||
JsonDeserializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult;
|
||||
|
||||
Asset<AssetData>* instance = reinterpret_cast<Asset<AssetData>*>(outputValue);
|
||||
AssetId id;
|
||||
JSR::ResultCode result(JSR::Tasks::ReadField);
|
||||
|
||||
SerializedAssetTracker* assetTracker =
|
||||
context.GetMetadata().Find<SerializedAssetTracker>();
|
||||
|
||||
{
|
||||
Data::AssetLoadBehavior loadBehavior = instance->GetAutoLoadBehavior();
|
||||
|
||||
result =
|
||||
ContinueLoadingFromJsonObjectField(&loadBehavior,
|
||||
azrtti_typeid<Data::AssetLoadBehavior>(),
|
||||
inputValue, "loadBehavior", context);
|
||||
|
||||
instance->SetAutoLoadBehavior(loadBehavior);
|
||||
}
|
||||
|
||||
auto it = inputValue.FindMember("assetId");
|
||||
if (it != inputValue.MemberEnd())
|
||||
{
|
||||
ScopedContextPath subPath(context, "assetId");
|
||||
result.Combine(ContinueLoading(&id, azrtti_typeid<AssetId>(), it->value, context));
|
||||
if (!id.m_guid.IsNull())
|
||||
{
|
||||
ScopedContextPath subPath(context, "assetId");
|
||||
result.Combine(ContinueLoading(&id, azrtti_typeid<AssetId>(), it->value, context));
|
||||
if (!id.m_guid.IsNull())
|
||||
*instance = AssetManager::Instance().FindOrCreateAsset(id, instance->GetType(), instance->GetAutoLoadBehavior());
|
||||
if (!instance->GetId().IsValid())
|
||||
{
|
||||
*instance = AssetManager::Instance().FindOrCreateAsset(id, instance->GetType(), instance->GetAutoLoadBehavior());
|
||||
if (!instance->GetId().IsValid())
|
||||
{
|
||||
// If the asset failed to be created, FindOrCreateAsset returns an asset instance with a null
|
||||
// id. To preserve the asset id in the source json, reset the asset to an empty one, but with
|
||||
// the right id.
|
||||
const auto loadBehavior = instance->GetAutoLoadBehavior();
|
||||
*instance = Asset<AssetData>(id, instance->GetType());
|
||||
instance->SetAutoLoadBehavior(loadBehavior);
|
||||
}
|
||||
// If the asset failed to be created, FindOrCreateAsset returns an asset instance with a null
|
||||
// id. To preserve the asset id in the source json, reset the asset to an empty one, but with
|
||||
// the right id.
|
||||
const auto loadBehavior = instance->GetAutoLoadBehavior();
|
||||
*instance = Asset<AssetData>(id, instance->GetType());
|
||||
instance->SetAutoLoadBehavior(loadBehavior);
|
||||
}
|
||||
|
||||
result.Combine(context.Report(result, "Successfully created Asset<T> with id."));
|
||||
}
|
||||
else if (result.GetProcessing() == JSR::Processing::Completed)
|
||||
{
|
||||
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
|
||||
"Null Asset<T> created."));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Combine(context.Report(result, "Failed to retrieve asset id for Asset<T>."));
|
||||
}
|
||||
result.Combine(context.Report(result, "Successfully created Asset<T> with id."));
|
||||
}
|
||||
else if (result.GetProcessing() == JSR::Processing::Completed)
|
||||
{
|
||||
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
|
||||
"Null Asset<T> created."));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
|
||||
"The asset id is missing, so there's not enough information to create an Asset<T>."));
|
||||
}
|
||||
|
||||
it = inputValue.FindMember("assetHint");
|
||||
if (it != inputValue.MemberEnd())
|
||||
{
|
||||
ScopedContextPath subPath(context, "assetHint");
|
||||
AZStd::string hint;
|
||||
result.Combine(ContinueLoading(&hint, azrtti_typeid<AZStd::string>(), it->value, context));
|
||||
instance->SetHint(AZStd::move(hint));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
|
||||
"The asset hint is missing for Asset<T>, so it will be left empty."));
|
||||
}
|
||||
|
||||
if (assetTracker)
|
||||
{
|
||||
assetTracker->FixUpAsset(*instance);
|
||||
assetTracker->AddAsset(*instance);
|
||||
}
|
||||
|
||||
bool success = result.GetOutcome() <= JSR::Outcomes::PartialSkip;
|
||||
bool defaulted = result.GetOutcome() == JSR::Outcomes::DefaultsUsed || result.GetOutcome() == JSR::Outcomes::PartialDefaults;
|
||||
AZStd::string_view message =
|
||||
success ? "Successfully loaded information and created instance of Asset<T>." :
|
||||
defaulted ? "A default id was provided for Asset<T>, so no instance could be created." :
|
||||
"Not enough information was available to create an instance of Asset<T> or data was corrupted.";
|
||||
return context.Report(result, message);
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::SetAssetFixUp(AssetFixUp assetFixUpCallback)
|
||||
{
|
||||
m_assetFixUpCallback = AZStd::move(assetFixUpCallback);
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::FixUpAsset(Asset<AssetData>& asset)
|
||||
{
|
||||
if (m_assetFixUpCallback)
|
||||
{
|
||||
m_assetFixUpCallback(asset);
|
||||
result.Combine(context.Report(result, "Failed to retrieve asset id for Asset<T>."));
|
||||
}
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::AddAsset(Asset<AssetData> asset)
|
||||
else
|
||||
{
|
||||
m_serializedAssets.emplace_back(asset);
|
||||
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
|
||||
"The asset id is missing, so there's not enough information to create an Asset<T>."));
|
||||
}
|
||||
|
||||
const AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets() const
|
||||
it = inputValue.FindMember("assetHint");
|
||||
if (it != inputValue.MemberEnd())
|
||||
{
|
||||
return m_serializedAssets;
|
||||
ScopedContextPath subPath(context, "assetHint");
|
||||
AZStd::string hint;
|
||||
result.Combine(ContinueLoading(&hint, azrtti_typeid<AZStd::string>(), it->value, context));
|
||||
instance->SetHint(AZStd::move(hint));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
|
||||
"The asset hint is missing for Asset<T>, so it will be left empty."));
|
||||
}
|
||||
|
||||
AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets()
|
||||
if (assetTracker)
|
||||
{
|
||||
return m_serializedAssets;
|
||||
assetTracker->FixUpAsset(*instance);
|
||||
assetTracker->AddAsset(*instance);
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
} // namespace AZ
|
||||
bool success = result.GetOutcome() <= JSR::Outcomes::PartialSkip;
|
||||
bool defaulted = result.GetOutcome() == JSR::Outcomes::DefaultsUsed || result.GetOutcome() == JSR::Outcomes::PartialDefaults;
|
||||
AZStd::string_view message =
|
||||
success ? "Successfully loaded information and created instance of Asset<T>." :
|
||||
defaulted ? "A default id was provided for Asset<T>, so no instance could be created." :
|
||||
"Not enough information was available to create an instance of Asset<T> or data was corrupted.";
|
||||
return context.Report(result, message);
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::SetAssetFixUp(AssetFixUp assetFixUpCallback)
|
||||
{
|
||||
m_assetFixUpCallback = AZStd::move(assetFixUpCallback);
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::FixUpAsset(Asset<AssetData>& asset)
|
||||
{
|
||||
if (m_assetFixUpCallback)
|
||||
{
|
||||
m_assetFixUpCallback(asset);
|
||||
}
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::AddAsset(Asset<AssetData> asset)
|
||||
{
|
||||
m_serializedAssets.emplace_back(asset);
|
||||
}
|
||||
|
||||
const AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets() const
|
||||
{
|
||||
return m_serializedAssets;
|
||||
}
|
||||
|
||||
AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets()
|
||||
{
|
||||
return m_serializedAssets;
|
||||
}
|
||||
|
||||
} // namespace AZ::Data
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -169,14 +169,14 @@ namespace AZ
|
||||
/// Register handler with the system for a particular asset type.
|
||||
/// A handler should be registered for each asset type it handles.
|
||||
/// Please note that all the handlers are registered just once during app startup from the main thread
|
||||
/// and therefore this is not a thread safe method and should not be invoked from different threads.
|
||||
/// and therefore this is not a thread safe method and should not be invoked from different threads.
|
||||
void RegisterHandler(AssetHandler* handler, const AssetType& assetType);
|
||||
/// Unregister handler from the asset system.
|
||||
/// Please note that all the handlers are unregistered just once during app shutdown from the main thread
|
||||
/// and therefore this is not a thread safe method and should not be invoked from different threads.
|
||||
void UnregisterHandler(AssetHandler* handler);
|
||||
// @}
|
||||
|
||||
|
||||
// @{ Asset catalog management
|
||||
/// Register a catalog with the system for a particular asset type.
|
||||
/// A catalog should be registered for each asset type it is responsible for.
|
||||
@@ -295,7 +295,7 @@ namespace AZ
|
||||
/**
|
||||
* Old 'legacy' assetIds and asset hints can be automatically replaced with new ones during deserialize / assignment.
|
||||
* This operation can be somewhat costly, and its only useful if the program subsequently re-saves the files its loading so that
|
||||
* the asset hints and assetIds actually persist. Thus, it can be disabled in situations where you know you are not going to be
|
||||
* the asset hints and assetIds actually persist. Thus, it can be disabled in situations where you know you are not going to be
|
||||
* saving over or creating new source files (for example builders/background apps)
|
||||
* By default, it is enabled.
|
||||
*/
|
||||
@@ -316,7 +316,7 @@ namespace AZ
|
||||
* This method must be invoked before you start unregistering handlers manually and shutting down the asset manager.
|
||||
* This method ensures that all jobs in flight are either canceled or completed.
|
||||
* This method is automatically called in the destructor but if you are unregistering handlers manually,
|
||||
* you must invoke it yourself.
|
||||
* you must invoke it yourself.
|
||||
*/
|
||||
void PrepareShutDown();
|
||||
|
||||
@@ -366,7 +366,7 @@ namespace AZ
|
||||
/**
|
||||
* Creates a new shared AssetContainer with an optional loadFilter
|
||||
* **/
|
||||
AZStd::shared_ptr<AssetContainer> CreateAssetContainer(Asset<AssetData> asset, const AssetLoadParameters& loadParams = AssetLoadParameters{}) const;
|
||||
virtual AZStd::shared_ptr<AssetContainer> CreateAssetContainer(Asset<AssetData> asset, const AssetLoadParameters& loadParams = AssetLoadParameters{}) const;
|
||||
|
||||
|
||||
/**
|
||||
@@ -452,7 +452,7 @@ namespace AZ
|
||||
|
||||
|
||||
// Variant of RegisterAssetLoading used for jobs which have been queued and need to verify the status of the asset
|
||||
// before loading in order to prevent cases where a load is queued, then a blocking load goes through, then the queued
|
||||
// before loading in order to prevent cases where a load is queued, then a blocking load goes through, then the queued
|
||||
// load is processed. This validation step leaves the loaded (And potentially modified) data as is in that case.
|
||||
bool ValidateAndRegisterAssetLoading(const Asset<AssetData>& asset);
|
||||
|
||||
@@ -482,7 +482,7 @@ namespace AZ
|
||||
* the blocking. That will result in a single thread deadlock.
|
||||
*
|
||||
* If you need to queue work, the logic needs to be similar to this:
|
||||
*
|
||||
*
|
||||
AssetHandler::LoadResult MyAssetHandler::LoadAssetData(const Asset<AssetData>& asset, AZStd::shared_ptr<AssetDataStream> stream,
|
||||
const AZ::Data::AssetFilterCB& assetLoadFilterCB)
|
||||
{
|
||||
@@ -496,13 +496,13 @@ namespace AZ
|
||||
}
|
||||
else
|
||||
{
|
||||
// queue job to load asset in thread identified by m_loadingThreadId
|
||||
// queue job to load asset in thread identified by m_loadingThreadId
|
||||
auto* queuedJob = QueueLoadingOnOtherThread(...);
|
||||
|
||||
// block waiting for queued job to complete
|
||||
queuedJob->BlockUntilComplete();
|
||||
}
|
||||
|
||||
|
||||
.
|
||||
.
|
||||
.
|
||||
@@ -525,7 +525,7 @@ namespace AZ
|
||||
//! Result from LoadAssetData - it either finished loading, didn't finish and is waiting for more data, or had an error.
|
||||
enum class LoadResult : u8
|
||||
{
|
||||
|
||||
|
||||
Error, // The provided data failed to load correctly
|
||||
MoreDataRequired, // The provided data loaded correctly, but more data is required to finish the asset load
|
||||
LoadComplete // The provided data loaded correctly, and the asset has been created
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/IO/Path/Path_fwd.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/containers/bitset.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
@@ -129,7 +130,8 @@ namespace AZ
|
||||
/// Remove a catalog from our delta list and rebuild the catalog from remaining items
|
||||
virtual bool RemoveDeltaCatalog(AZStd::shared_ptr<AzFramework::AssetRegistry> /*deltaCatalog*/) { return true; }
|
||||
/// Creates a manifest with the given DeltaCatalog name
|
||||
virtual bool CreateBundleManifest(const AZStd::string& /*deltaCatalogPath*/, const AZStd::vector<AZStd::string>& /*dependentBundleNames*/, const AZStd::string& /*fileDirectory*/, int /*bundleVersion*/, const AZStd::vector<AZStd::string>& /*levelDirs*/) { return false; }
|
||||
virtual bool CreateBundleManifest(const AZStd::string& /*deltaCatalogPath*/, const AZStd::vector<AZStd::string>& /*dependentBundleNames*/,
|
||||
const AZStd::string& /*fileDirectory*/, int /*bundleVersion*/, const AZStd::vector<AZ::IO::Path>& /*levelDirs*/) { return false; }
|
||||
/// Creates an instance of a registry containing info for just the specified files, and writes it out to a file at the specified path
|
||||
virtual bool CreateDeltaCatalog(const AZStd::vector<AZStd::string>& /*files*/, const AZStd::string& /*filePath*/) { return false; }
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include <AzCore/Slice/SliceSystemComponent.h>
|
||||
#include <AzCore/Slice/SliceMetadataInfoComponent.h>
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
#include <AzCore/Time/TimeSystemComponent.h>
|
||||
#include <AzCore/Console/LoggerSystemComponent.h>
|
||||
#include <AzCore/EBus/EventSchedulerSystemComponent.h>
|
||||
#include <AzCore/Task/TaskGraphSystemComponent.h>
|
||||
@@ -40,7 +39,6 @@ namespace AZ
|
||||
SliceComponent::CreateDescriptor(),
|
||||
SliceSystemComponent::CreateDescriptor(),
|
||||
SliceMetadataInfoComponent::CreateDescriptor(),
|
||||
TimeSystemComponent::CreateDescriptor(),
|
||||
LoggerSystemComponent::CreateDescriptor(),
|
||||
EventSchedulerSystemComponent::CreateDescriptor(),
|
||||
TaskGraphSystemComponent::CreateDescriptor(),
|
||||
@@ -59,7 +57,6 @@ namespace AZ
|
||||
{
|
||||
return AZ::ComponentTypeList
|
||||
{
|
||||
azrtti_typeid<TimeSystemComponent>(),
|
||||
azrtti_typeid<LoggerSystemComponent>(),
|
||||
azrtti_typeid<EventSchedulerSystemComponent>(),
|
||||
azrtti_typeid<TaskGraphSystemComponent>(),
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
|
||||
#include <AzCore/Module/Environment.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzCore/Time/TimeSystem.h>
|
||||
|
||||
static void PrintEntityName(const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
@@ -416,6 +417,7 @@ namespace AZ
|
||||
|
||||
ComponentApplication::ComponentApplication(int argC, char** argV)
|
||||
: m_eventLogger{}
|
||||
, m_timeSystem(AZStd::make_unique<TimeSystem>())
|
||||
{
|
||||
if (Interface<ComponentApplicationRequests>::Get() == nullptr)
|
||||
{
|
||||
@@ -485,16 +487,6 @@ namespace AZ
|
||||
constexpr bool executeRegDumpCommands = false;
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
|
||||
|
||||
// Query for the Executable Path using OS specific functions
|
||||
CalculateExecutablePath();
|
||||
|
||||
// Determine the path to the engine
|
||||
CalculateEngineRoot();
|
||||
|
||||
// If the current platform returns an engaged optional from Utils::GetDefaultAppRootPath(), that is used
|
||||
// for the application root.
|
||||
CalculateAppRoot();
|
||||
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(*m_settingsRegistry, AZ_TRAIT_OS_PLATFORM_CODENAME, {});
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
|
||||
@@ -584,7 +576,6 @@ namespace AZ
|
||||
DestroyAllocator();
|
||||
}
|
||||
|
||||
|
||||
void ReportBadEngineRoot()
|
||||
{
|
||||
AZStd::string errorMessage = {"Unable to determine a valid path to the engine.\n"
|
||||
@@ -614,7 +605,8 @@ namespace AZ
|
||||
{
|
||||
AZ_Assert(!m_isStarted, "Component application already started!");
|
||||
|
||||
if (m_engineRoot.empty())
|
||||
using Type = AZ::SettingsRegistryInterface::Type;
|
||||
if (m_settingsRegistry->GetType(SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder) == Type::NoType)
|
||||
{
|
||||
ReportBadEngineRoot();
|
||||
return nullptr;
|
||||
@@ -686,7 +678,6 @@ namespace AZ
|
||||
|
||||
ComponentApplicationBus::Handler::BusConnect();
|
||||
|
||||
m_currentTime = AZStd::chrono::system_clock::now();
|
||||
TickRequestBus::Handler::BusConnect();
|
||||
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
@@ -1180,6 +1171,24 @@ namespace AZ
|
||||
return ReflectionEnvironment::GetReflectionManager() ? ReflectionEnvironment::GetReflectionManager()->GetReflectContext<JsonRegistrationContext>() : nullptr;
|
||||
}
|
||||
|
||||
/// Returns the path to the engine.
|
||||
|
||||
const char* ComponentApplication::GetEngineRoot() const
|
||||
{
|
||||
static IO::FixedMaxPathString engineRoot;
|
||||
engineRoot.clear();
|
||||
m_settingsRegistry->Get(engineRoot, SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
return engineRoot.c_str();
|
||||
}
|
||||
|
||||
const char* ComponentApplication::GetExecutableFolder() const
|
||||
{
|
||||
static IO::FixedMaxPathString exeFolder;
|
||||
exeFolder.clear();
|
||||
m_settingsRegistry->Get(exeFolder, SettingsRegistryMergeUtils::FilePathKey_BinaryFolder);
|
||||
return exeFolder.c_str();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// CreateReflectionManager
|
||||
//=========================================================================
|
||||
@@ -1404,31 +1413,23 @@ namespace AZ
|
||||
#endif
|
||||
}
|
||||
|
||||
void ComponentApplication::Tick(float deltaOverride /*= -1.f*/)
|
||||
void ComponentApplication::Tick()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(System, "Component application simulation tick");
|
||||
|
||||
{
|
||||
AZ_PROFILE_SCOPE(System, "Component application simulation tick");
|
||||
|
||||
AZStd::chrono::system_clock::time_point now = AZStd::chrono::system_clock::now();
|
||||
|
||||
m_deltaTime = 0.0f;
|
||||
|
||||
if (now >= m_currentTime)
|
||||
{
|
||||
AZStd::chrono::duration<float> delta = now - m_currentTime;
|
||||
m_deltaTime = deltaOverride >= 0.f ? deltaOverride : delta.count();
|
||||
}
|
||||
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:ExecuteQueuedEvents");
|
||||
TickBus::ExecuteQueuedEvents();
|
||||
}
|
||||
m_currentTime = now;
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick");
|
||||
EBUS_EVENT(TickBus, OnTick, m_deltaTime, ScriptTimePoint(now));
|
||||
}
|
||||
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:ExecuteQueuedEvents");
|
||||
TickBus::ExecuteQueuedEvents();
|
||||
}
|
||||
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick");
|
||||
const AZ::TimeUs deltaTimeUs = m_timeSystem->AdvanceTickDeltaTimes();
|
||||
const float deltaTimeSeconds = AZ::TimeUsToSeconds(deltaTimeUs);
|
||||
AZ::TickBus::Broadcast(&TickEvents::OnTick, deltaTimeSeconds, GetTimeAtCurrentTick());
|
||||
}
|
||||
|
||||
m_timeSystem->ApplyTickRateLimiterIfNeeded();
|
||||
}
|
||||
|
||||
void ComponentApplication::TickSystem()
|
||||
@@ -1485,27 +1486,6 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// CalculateExecutablePath
|
||||
//=========================================================================
|
||||
void ComponentApplication::CalculateExecutablePath()
|
||||
{
|
||||
m_exeDirectory = Utils::GetExecutableDirectory();
|
||||
}
|
||||
|
||||
void ComponentApplication::CalculateAppRoot()
|
||||
{
|
||||
if (AZStd::optional<AZ::StringFunc::Path::FixedString> appRootPath = Utils::GetDefaultAppRootPath(); appRootPath)
|
||||
{
|
||||
m_appRoot = AZStd::move(*appRootPath);
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentApplication::CalculateEngineRoot()
|
||||
{
|
||||
m_engineRoot = AZ::SettingsRegistryMergeUtils::FindEngineRoot(*m_settingsRegistry).Native();
|
||||
}
|
||||
|
||||
void ComponentApplication::ResolveModulePath([[maybe_unused]] AZ::OSString& modulePath)
|
||||
{
|
||||
// No special parsing of the Module Path is done by the Component Application anymore
|
||||
@@ -1531,13 +1511,10 @@ namespace AZ
|
||||
appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Invalid;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GetFrameTime
|
||||
// [1/22/2016]
|
||||
//=========================================================================
|
||||
float ComponentApplication::GetTickDeltaTime()
|
||||
{
|
||||
return m_deltaTime;
|
||||
const AZ::TimeUs gameTickTime = m_timeSystem->GetSimulationTickDeltaTimeUs();
|
||||
return AZ::TimeUsToSeconds(gameTickTime);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
@@ -1546,7 +1523,8 @@ namespace AZ
|
||||
//=========================================================================
|
||||
ScriptTimePoint ComponentApplication::GetTimeAtCurrentTick()
|
||||
{
|
||||
return ScriptTimePoint(m_currentTime);
|
||||
const AZ::TimeUs lastGameTickTime = m_timeSystem->GetLastSimulationTickTime();
|
||||
return ScriptTimePoint(AZ::TimeUsToChrono(lastGameTickTime));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -30,12 +30,14 @@
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzCore/std/string/osstring.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class BehaviorContext;
|
||||
class IConsole;
|
||||
class Module;
|
||||
class ModuleManager;
|
||||
class TimeSystem;
|
||||
}
|
||||
namespace AZ::Debug
|
||||
{
|
||||
@@ -221,13 +223,10 @@ namespace AZ
|
||||
BehaviorContext* GetBehaviorContext() override;
|
||||
/// Returns the json registration context that has been registered with the app, if there is one.
|
||||
JsonRegistrationContext* GetJsonRegistrationContext() override;
|
||||
/// Returns the working root folder that has been registered with the app, if there is one.
|
||||
/// It's expected that derived applications will implement an application root.
|
||||
const char* GetAppRoot() const override { return m_appRoot.c_str(); }
|
||||
/// Returns the path to the engine.
|
||||
const char* GetEngineRoot() const override { return m_engineRoot.c_str(); }
|
||||
const char* GetEngineRoot() const override;
|
||||
/// Returns the path to the folder the executable is in.
|
||||
const char* GetExecutableFolder() const override { return m_exeDirectory.c_str(); }
|
||||
const char* GetExecutableFolder() const override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// TickRequestBus
|
||||
@@ -240,7 +239,7 @@ namespace AZ
|
||||
/**
|
||||
* Ticks all components using the \ref AZ::TickBus during simulation time. May not tick if the application is not active (i.e. not in focus)
|
||||
*/
|
||||
virtual void Tick(float deltaOverride = -1.f);
|
||||
virtual void Tick();
|
||||
|
||||
/**
|
||||
* Ticks all using the \ref AZ::SystemTickBus at all times. Should always tick even if the application is not active.
|
||||
@@ -352,15 +351,6 @@ namespace AZ
|
||||
/// Adds system components requested by modules and the application to the system entity.
|
||||
void AddRequiredSystemComponents(AZ::Entity* systemEntity);
|
||||
|
||||
/// Calculates the directory the application executable comes from.
|
||||
void CalculateExecutablePath();
|
||||
|
||||
/// Calculates the root directory of the engine.
|
||||
void CalculateEngineRoot();
|
||||
|
||||
/// Deprecated: The term "AppRoot" has no meaning
|
||||
void CalculateAppRoot();
|
||||
|
||||
template<typename Iterator>
|
||||
static void NormalizePath(Iterator begin, Iterator end, bool doLowercase = true)
|
||||
{
|
||||
@@ -371,8 +361,6 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::chrono::system_clock::time_point m_currentTime{ AZStd::chrono::system_clock::time_point::max() };
|
||||
float m_deltaTime{ 0.0f };
|
||||
AZStd::unique_ptr<ModuleManager> m_moduleManager;
|
||||
AZStd::unique_ptr<SettingsRegistryInterface> m_settingsRegistry;
|
||||
EntityAddedEvent m_entityAddedEvent;
|
||||
@@ -388,14 +376,13 @@ namespace AZ
|
||||
void* m_fixedMemoryBlock{ nullptr }; //!< Pointer to the memory block allocator, so we can free it OnDestroy.
|
||||
IAllocatorAllocate* m_osAllocator{ nullptr };
|
||||
EntitySetType m_entities;
|
||||
AZ::IO::FixedMaxPath m_exeDirectory;
|
||||
AZ::IO::FixedMaxPath m_engineRoot;
|
||||
AZ::IO::FixedMaxPath m_appRoot;
|
||||
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectPathChangedHandler;
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectNameChangedHandler;
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_commandLineUpdatedHandler;
|
||||
|
||||
AZStd::unique_ptr<AZ::TimeSystem> m_timeSystem;
|
||||
|
||||
// ConsoleFunctorHandle is responsible for unregistering the Settings Registry Console
|
||||
// from the m_console member when it goes out of scope
|
||||
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle m_settingsRegistryConsoleFunctors;
|
||||
|
||||
@@ -175,10 +175,6 @@ namespace AZ
|
||||
//! the serializers used by the best-effort json serialization.
|
||||
virtual class JsonRegistrationContext* GetJsonRegistrationContext() = 0;
|
||||
|
||||
//! Gets the name of the working root folder that was registered with the app.
|
||||
//! @return a pointer to the name of the app's root folder, if a root folder was registered.
|
||||
virtual const char* GetAppRoot() const = 0;
|
||||
|
||||
//! Gets the path of the working engine folder that the app is a part of.
|
||||
//! @return a pointer to the engine path.
|
||||
virtual const char* GetEngineRoot() const = 0;
|
||||
|
||||
@@ -10,292 +10,289 @@
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/std/containers/fixed_vector.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::EntityUtils
|
||||
{
|
||||
namespace EntityUtils
|
||||
//=========================================================================
|
||||
// Reflect
|
||||
//=========================================================================
|
||||
void Reflect(ReflectContext* context)
|
||||
{
|
||||
//=========================================================================
|
||||
// Reflect
|
||||
//=========================================================================
|
||||
void Reflect(ReflectContext* context)
|
||||
if (auto serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
serializeContext->Class<SerializableEntityContainer>()->
|
||||
Version(1)->
|
||||
Field("Entities", &SerializableEntityContainer::m_entities);
|
||||
}
|
||||
}
|
||||
|
||||
struct StackDataType
|
||||
{
|
||||
const SerializeContext::ClassData* m_classData;
|
||||
const SerializeContext::ClassElement* m_elementData;
|
||||
void* m_dataPtr;
|
||||
bool m_isModifiedContainer;
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
// EnumerateEntityIds
|
||||
//=========================================================================
|
||||
void EnumerateEntityIds(const void* classPtr, const Uuid& classUuid, const EntityIdVisitor& visitor, SerializeContext* context)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
if (!context)
|
||||
{
|
||||
context = GetApplicationSerializeContext();
|
||||
if (!context)
|
||||
{
|
||||
serializeContext->Class<SerializableEntityContainer>()->
|
||||
Version(1)->
|
||||
Field("Entities", &SerializableEntityContainer::m_entities);
|
||||
AZ_Error("Serialization", false, "No serialize context provided! Failed to get component application default serialize context! ComponentApp is not started or input serialize context should not be null!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
AZStd::vector<const SerializeContext::ClassData*> parentStack;
|
||||
parentStack.reserve(30);
|
||||
auto beginCB = [ &](void* ptr, const SerializeContext::ClassData* classData, const SerializeContext::ClassElement* elementData) -> bool
|
||||
{
|
||||
(void)elementData;
|
||||
|
||||
struct StackDataType
|
||||
if (classData->m_typeId == SerializeTypeInfo<EntityId>::GetUuid())
|
||||
{
|
||||
// determine if this is entity ref or just entityId (please refer to the function documentation for more info)
|
||||
bool isEntityId = false;
|
||||
if (!parentStack.empty() && parentStack.back()->m_typeId == SerializeTypeInfo<Entity>::GetUuid())
|
||||
{
|
||||
// our parent in the entity (currently entity has only one EntityId member, but we can check the offset for future proof
|
||||
AZ_Assert(elementData && strcmp(elementData->m_name, "Id") == 0, "class Entity, should have only ONE EntityId member, the actual entity id!");
|
||||
isEntityId = true;
|
||||
}
|
||||
|
||||
EntityId* entityIdPtr = (elementData->m_flags & SerializeContext::ClassElement::FLG_POINTER) ?
|
||||
*reinterpret_cast<EntityId**>(ptr) : reinterpret_cast<EntityId*>(ptr);
|
||||
visitor(*entityIdPtr, isEntityId, elementData);
|
||||
}
|
||||
|
||||
parentStack.push_back(classData);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto endCB = [ &]() -> bool
|
||||
{
|
||||
parentStack.pop_back();
|
||||
return true;
|
||||
};
|
||||
|
||||
SerializeContext::EnumerateInstanceCallContext callContext(
|
||||
beginCB,
|
||||
endCB,
|
||||
context,
|
||||
SerializeContext::ENUM_ACCESS_FOR_READ,
|
||||
nullptr
|
||||
);
|
||||
|
||||
context->EnumerateInstanceConst(
|
||||
&callContext,
|
||||
classPtr,
|
||||
classUuid,
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GetApplicationSerializeContext
|
||||
//=========================================================================
|
||||
SerializeContext* GetApplicationSerializeContext()
|
||||
{
|
||||
SerializeContext* context = nullptr;
|
||||
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
|
||||
return context;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FindFirstDerivedComponent
|
||||
//=========================================================================
|
||||
Component* FindFirstDerivedComponent(const Entity* entity, const Uuid& typeId)
|
||||
{
|
||||
for (AZ::Component* component : entity->GetComponents())
|
||||
{
|
||||
const SerializeContext::ClassData* m_classData;
|
||||
const SerializeContext::ClassElement* m_elementData;
|
||||
void* m_dataPtr;
|
||||
bool m_isModifiedContainer;
|
||||
if (azrtti_istypeof(typeId, component))
|
||||
{
|
||||
return component;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Component* FindFirstDerivedComponent(EntityId entityId, const Uuid& typeId)
|
||||
{
|
||||
Entity* entity{};
|
||||
ComponentApplicationBus::BroadcastResult(entity, &ComponentApplicationRequests::FindEntity, entityId);
|
||||
return entity ? FindFirstDerivedComponent(entity, typeId) : nullptr;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FindDerivedComponents
|
||||
//=========================================================================
|
||||
Entity::ComponentArrayType FindDerivedComponents(const Entity* entity, const Uuid& typeId)
|
||||
{
|
||||
Entity::ComponentArrayType result;
|
||||
for (AZ::Component* component : entity->GetComponents())
|
||||
{
|
||||
if (azrtti_istypeof(typeId, component))
|
||||
{
|
||||
result.push_back(component);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Entity::ComponentArrayType FindDerivedComponents(EntityId entityId, const Uuid& typeId)
|
||||
{
|
||||
Entity* entity{};
|
||||
ComponentApplicationBus::BroadcastResult(entity, &ComponentApplicationRequests::FindEntity, entityId);
|
||||
return entity ? FindDerivedComponents(entity, typeId) : Entity::ComponentArrayType();
|
||||
}
|
||||
|
||||
bool EnumerateBaseRecursive(SerializeContext* context, const EnumerateBaseRecursiveVisitor& baseClassVisitor, const TypeId& typeToExamine)
|
||||
{
|
||||
AZ_Assert(context, "CheckDeclaresSerializeBaseClass called with no serialize context.");
|
||||
if (!context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZStd::fixed_vector<TypeId, 64> knownBaseClasses = { typeToExamine }; // avoid allocating heap here if possible. 64 types are 64*sizeof(Uuid) which is only 1k.
|
||||
bool foundBaseClass = false;
|
||||
auto enumerateBaseVisitor = [&baseClassVisitor, &knownBaseClasses](const AZ::SerializeContext::ClassData* classData, const TypeId& examineTypeId)
|
||||
{
|
||||
if (!classData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AZStd::find(knownBaseClasses.begin(), knownBaseClasses.end(), classData->m_typeId) == knownBaseClasses.end())
|
||||
{
|
||||
if (knownBaseClasses.size() == 64)
|
||||
{
|
||||
// this should be pretty unlikely since a single class would have to have many other classes in its heirarchy
|
||||
// and it'd all have to be basically in one layer, as we are popping as we explore.
|
||||
AZ_WarningOnce("EntityUtils", false, "While trying to find a base class, all available slots were consumed. consider increasing the size of knownBaseClasses.\n");
|
||||
// we cannot continue any further, assume we did not find it.
|
||||
return false;
|
||||
}
|
||||
knownBaseClasses.push_back(classData->m_typeId);
|
||||
}
|
||||
|
||||
return baseClassVisitor(classData, examineTypeId);
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
// EnumerateEntityIds
|
||||
//=========================================================================
|
||||
void EnumerateEntityIds(const void* classPtr, const Uuid& classUuid, const EntityIdVisitor& visitor, SerializeContext* context)
|
||||
while (!knownBaseClasses.empty() && !foundBaseClass)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
TypeId toExamine = knownBaseClasses.back();
|
||||
knownBaseClasses.pop_back();
|
||||
|
||||
if (!context)
|
||||
{
|
||||
context = GetApplicationSerializeContext();
|
||||
if (!context)
|
||||
{
|
||||
AZ_Error("Serialization", false, "No serialize context provided! Failed to get component application default serialize context! ComponentApp is not started or input serialize context should not be null!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
AZStd::vector<const SerializeContext::ClassData*> parentStack;
|
||||
parentStack.reserve(30);
|
||||
auto beginCB = [ &](void* ptr, const SerializeContext::ClassData* classData, const SerializeContext::ClassElement* elementData) -> bool
|
||||
{
|
||||
(void)elementData;
|
||||
|
||||
if (classData->m_typeId == SerializeTypeInfo<EntityId>::GetUuid())
|
||||
{
|
||||
// determine if this is entity ref or just entityId (please refer to the function documentation for more info)
|
||||
bool isEntityId = false;
|
||||
if (!parentStack.empty() && parentStack.back()->m_typeId == SerializeTypeInfo<Entity>::GetUuid())
|
||||
{
|
||||
// our parent in the entity (currently entity has only one EntityId member, but we can check the offset for future proof
|
||||
AZ_Assert(elementData && strcmp(elementData->m_name, "Id") == 0, "class Entity, should have only ONE EntityId member, the actual entity id!");
|
||||
isEntityId = true;
|
||||
}
|
||||
|
||||
EntityId* entityIdPtr = (elementData->m_flags & SerializeContext::ClassElement::FLG_POINTER) ?
|
||||
*reinterpret_cast<EntityId**>(ptr) : reinterpret_cast<EntityId*>(ptr);
|
||||
visitor(*entityIdPtr, isEntityId, elementData);
|
||||
}
|
||||
|
||||
parentStack.push_back(classData);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto endCB = [ &]() -> bool
|
||||
{
|
||||
parentStack.pop_back();
|
||||
return true;
|
||||
};
|
||||
|
||||
SerializeContext::EnumerateInstanceCallContext callContext(
|
||||
beginCB,
|
||||
endCB,
|
||||
context,
|
||||
SerializeContext::ENUM_ACCESS_FOR_READ,
|
||||
nullptr
|
||||
);
|
||||
|
||||
context->EnumerateInstanceConst(
|
||||
&callContext,
|
||||
classPtr,
|
||||
classUuid,
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
context->EnumerateBase(enumerateBaseVisitor, toExamine);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GetApplicationSerializeContext
|
||||
//=========================================================================
|
||||
SerializeContext* GetApplicationSerializeContext()
|
||||
{
|
||||
SerializeContext* context = nullptr;
|
||||
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
|
||||
return context;
|
||||
}
|
||||
return foundBaseClass;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FindFirstDerivedComponent
|
||||
//=========================================================================
|
||||
Component* FindFirstDerivedComponent(const Entity* entity, const Uuid& typeId)
|
||||
bool CheckIfClassIsDeprecated(SerializeContext* context, const TypeId& typeToExamine)
|
||||
{
|
||||
bool isDeprecated = false;
|
||||
auto classVisitorFn = [&isDeprecated](const AZ::SerializeContext::ClassData* classData, const TypeId& /*rttiBase*/)
|
||||
{
|
||||
for (AZ::Component* component : entity->GetComponents())
|
||||
{
|
||||
if (azrtti_istypeof(typeId, component))
|
||||
{
|
||||
return component;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Component* FindFirstDerivedComponent(EntityId entityId, const Uuid& typeId)
|
||||
{
|
||||
Entity* entity{};
|
||||
ComponentApplicationBus::BroadcastResult(entity, &ComponentApplicationRequests::FindEntity, entityId);
|
||||
return entity ? FindFirstDerivedComponent(entity, typeId) : nullptr;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FindDerivedComponents
|
||||
//=========================================================================
|
||||
Entity::ComponentArrayType FindDerivedComponents(const Entity* entity, const Uuid& typeId)
|
||||
{
|
||||
Entity::ComponentArrayType result;
|
||||
for (AZ::Component* component : entity->GetComponents())
|
||||
{
|
||||
if (azrtti_istypeof(typeId, component))
|
||||
{
|
||||
result.push_back(component);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Entity::ComponentArrayType FindDerivedComponents(EntityId entityId, const Uuid& typeId)
|
||||
{
|
||||
Entity* entity{};
|
||||
ComponentApplicationBus::BroadcastResult(entity, &ComponentApplicationRequests::FindEntity, entityId);
|
||||
return entity ? FindDerivedComponents(entity, typeId) : Entity::ComponentArrayType();
|
||||
}
|
||||
|
||||
bool EnumerateBaseRecursive(SerializeContext* context, const EnumerateBaseRecursiveVisitor& baseClassVisitor, const TypeId& typeToExamine)
|
||||
{
|
||||
AZ_Assert(context, "CheckDeclaresSerializeBaseClass called with no serialize context.");
|
||||
if (!context)
|
||||
// Stop iterating once we stop receiving SerializeContext::ClassData*.
|
||||
if (!classData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZStd::fixed_vector<TypeId, 64> knownBaseClasses = { typeToExamine }; // avoid allocating heap here if possible. 64 types are 64*sizeof(Uuid) which is only 1k.
|
||||
bool foundBaseClass = false;
|
||||
auto enumerateBaseVisitor = [&baseClassVisitor, &knownBaseClasses](const AZ::SerializeContext::ClassData* classData, const TypeId& examineTypeId)
|
||||
{
|
||||
if (!classData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AZStd::find(knownBaseClasses.begin(), knownBaseClasses.end(), classData->m_typeId) == knownBaseClasses.end())
|
||||
{
|
||||
if (knownBaseClasses.size() == 64)
|
||||
{
|
||||
// this should be pretty unlikely since a single class would have to have many other classes in its heirarchy
|
||||
// and it'd all have to be basically in one layer, as we are popping as we explore.
|
||||
AZ_WarningOnce("EntityUtils", false, "While trying to find a base class, all available slots were consumed. consider increasing the size of knownBaseClasses.\n");
|
||||
// we cannot continue any further, assume we did not find it.
|
||||
return false;
|
||||
}
|
||||
knownBaseClasses.push_back(classData->m_typeId);
|
||||
}
|
||||
|
||||
return baseClassVisitor(classData, examineTypeId);
|
||||
};
|
||||
|
||||
while (!knownBaseClasses.empty() && !foundBaseClass)
|
||||
{
|
||||
TypeId toExamine = knownBaseClasses.back();
|
||||
knownBaseClasses.pop_back();
|
||||
|
||||
context->EnumerateBase(enumerateBaseVisitor, toExamine);
|
||||
}
|
||||
|
||||
return foundBaseClass;
|
||||
}
|
||||
|
||||
bool CheckIfClassIsDeprecated(SerializeContext* context, const TypeId& typeToExamine)
|
||||
{
|
||||
bool isDeprecated = false;
|
||||
auto classVisitorFn = [&isDeprecated](const AZ::SerializeContext::ClassData* classData, const TypeId& /*rttiBase*/)
|
||||
{
|
||||
// Stop iterating once we stop receiving SerializeContext::ClassData*.
|
||||
if (!classData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stop iterating if we've found that the class is deprecated
|
||||
if (classData->IsDeprecated())
|
||||
{
|
||||
isDeprecated = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // keep iterating
|
||||
};
|
||||
|
||||
// Check if the type is deprecated
|
||||
const AZ::SerializeContext::ClassData* classData = context->FindClassData(typeToExamine);
|
||||
// Stop iterating if we've found that the class is deprecated
|
||||
if (classData->IsDeprecated())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if any of its bases are deprecated
|
||||
EnumerateBaseRecursive(context, classVisitorFn, typeToExamine);
|
||||
|
||||
return isDeprecated;
|
||||
}
|
||||
|
||||
bool CheckDeclaresSerializeBaseClass(SerializeContext* context, const TypeId& typeToFind, const TypeId& typeToExamine)
|
||||
{
|
||||
AZ_Assert(context, "CheckDeclaresSerializeBaseClass called with no serialize context.");
|
||||
if (!context)
|
||||
{
|
||||
isDeprecated = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool foundBaseClass = false;
|
||||
auto baseClassVisitorFn = [&typeToFind, &foundBaseClass](const AZ::SerializeContext::ClassData* reflectedBase, const TypeId& /*rttiBase*/)
|
||||
{
|
||||
if (!reflectedBase)
|
||||
{
|
||||
foundBaseClass = false;
|
||||
return false; // stop iterating
|
||||
}
|
||||
return true; // keep iterating
|
||||
};
|
||||
|
||||
foundBaseClass = (reflectedBase->m_typeId == typeToFind);
|
||||
if (foundBaseClass)
|
||||
{
|
||||
return false; // we have a base, stop iterating
|
||||
}
|
||||
|
||||
return true; // keep iterating
|
||||
};
|
||||
|
||||
EnumerateBaseRecursive(context, baseClassVisitorFn, typeToExamine);
|
||||
|
||||
return foundBaseClass;
|
||||
}
|
||||
|
||||
bool RemoveDuplicateServicesOfAndAfterIterator(
|
||||
const ComponentDescriptor::DependencyArrayType::iterator& iterator,
|
||||
ComponentDescriptor::DependencyArrayType& providedServiceArray,
|
||||
const Entity* entity)
|
||||
// Check if the type is deprecated
|
||||
const AZ::SerializeContext::ClassData* classData = context->FindClassData(typeToExamine);
|
||||
if (classData->IsDeprecated())
|
||||
{
|
||||
// Build types that strip out AZ_Warnings will complain that entity is unused without this.
|
||||
(void)entity;
|
||||
if (iterator == providedServiceArray.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool duplicateFound = false;
|
||||
|
||||
for (ComponentDescriptor::DependencyArrayType::iterator duplicateCheckIter = AZStd::next(iterator);
|
||||
duplicateCheckIter != providedServiceArray.end();)
|
||||
{
|
||||
if (*iterator == *duplicateCheckIter)
|
||||
{
|
||||
AZ_Warning("Entity", false, "Duplicate service %d found on entity %s [%s]",
|
||||
*duplicateCheckIter,
|
||||
entity ? entity->GetName().c_str() : "Entity not provided",
|
||||
entity ? entity->GetId().ToString().c_str() : "");
|
||||
duplicateCheckIter = providedServiceArray.erase(duplicateCheckIter);
|
||||
duplicateFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++duplicateCheckIter;
|
||||
}
|
||||
}
|
||||
return duplicateFound;
|
||||
return true;
|
||||
}
|
||||
} // namespace EntityUtils
|
||||
} // namespace AZ
|
||||
|
||||
// Check if any of its bases are deprecated
|
||||
EnumerateBaseRecursive(context, classVisitorFn, typeToExamine);
|
||||
|
||||
return isDeprecated;
|
||||
}
|
||||
|
||||
bool CheckDeclaresSerializeBaseClass(SerializeContext* context, const TypeId& typeToFind, const TypeId& typeToExamine)
|
||||
{
|
||||
AZ_Assert(context, "CheckDeclaresSerializeBaseClass called with no serialize context.");
|
||||
if (!context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool foundBaseClass = false;
|
||||
auto baseClassVisitorFn = [&typeToFind, &foundBaseClass](const AZ::SerializeContext::ClassData* reflectedBase, const TypeId& /*rttiBase*/)
|
||||
{
|
||||
if (!reflectedBase)
|
||||
{
|
||||
foundBaseClass = false;
|
||||
return false; // stop iterating
|
||||
}
|
||||
|
||||
foundBaseClass = (reflectedBase->m_typeId == typeToFind);
|
||||
if (foundBaseClass)
|
||||
{
|
||||
return false; // we have a base, stop iterating
|
||||
}
|
||||
|
||||
return true; // keep iterating
|
||||
};
|
||||
|
||||
EnumerateBaseRecursive(context, baseClassVisitorFn, typeToExamine);
|
||||
|
||||
return foundBaseClass;
|
||||
}
|
||||
|
||||
bool RemoveDuplicateServicesOfAndAfterIterator(
|
||||
const ComponentDescriptor::DependencyArrayType::iterator& iterator,
|
||||
ComponentDescriptor::DependencyArrayType& providedServiceArray,
|
||||
const Entity* entity)
|
||||
{
|
||||
// Build types that strip out AZ_Warnings will complain that entity is unused without this.
|
||||
(void)entity;
|
||||
if (iterator == providedServiceArray.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool duplicateFound = false;
|
||||
|
||||
for (ComponentDescriptor::DependencyArrayType::iterator duplicateCheckIter = AZStd::next(iterator);
|
||||
duplicateCheckIter != providedServiceArray.end();)
|
||||
{
|
||||
if (*iterator == *duplicateCheckIter)
|
||||
{
|
||||
AZ_Warning("Entity", false, "Duplicate service %d found on entity %s [%s]",
|
||||
*duplicateCheckIter,
|
||||
entity ? entity->GetName().c_str() : "Entity not provided",
|
||||
entity ? entity->GetId().ToString().c_str() : "");
|
||||
duplicateCheckIter = providedServiceArray.erase(duplicateCheckIter);
|
||||
duplicateFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++duplicateCheckIter;
|
||||
}
|
||||
}
|
||||
return duplicateFound;
|
||||
}
|
||||
} // namespace AZ::EntityUtils
|
||||
|
||||
@@ -57,6 +57,7 @@ void ZStd::StartCompressor(unsigned int compressionLevel)
|
||||
ZSTD_customMem customAlloc;
|
||||
customAlloc.customAlloc = reinterpret_cast<ZSTD_allocFunction>(&AllocateMem);
|
||||
customAlloc.customFree = &FreeMem;
|
||||
customAlloc.opaque = nullptr;
|
||||
|
||||
AZ_UNUSED(compressionLevel);
|
||||
m_streamCompression = (ZSTD_createCStream_advanced(customAlloc));
|
||||
|
||||
@@ -14,323 +14,313 @@
|
||||
#include <AzCore/std/containers/map.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
namespace
|
||||
{
|
||||
namespace
|
||||
struct AssetTreeNode;
|
||||
|
||||
// Per-thread data that needs to be stored.
|
||||
struct ThreadData
|
||||
{
|
||||
struct AssetTreeNode;
|
||||
|
||||
// Per-thread data that needs to be stored.
|
||||
struct ThreadData
|
||||
{
|
||||
AZStd::vector<AssetTreeNodeBase*, AZStdAssetTrackingAllocator> m_currentAssetStack;
|
||||
};
|
||||
|
||||
// Access thread data through a virtual function to ensure that the same thread-local data is being shared across DLLs.
|
||||
// Otherwise, the thread_local variables are replicated across DLLs that link the AzCore library, and you'll get a
|
||||
// different version in each module.
|
||||
class ThreadDataProvider
|
||||
{
|
||||
public:
|
||||
virtual ThreadData& GetThreadData() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
class AssetTrackingImpl final :
|
||||
public ThreadDataProvider
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(AssetTrackingImpl, "{01E2A099-3523-40BE-80E0-E0ADD861BEE1}");
|
||||
AZ_CLASS_ALLOCATOR(AssetTrackingImpl, OSAllocator, 0);
|
||||
|
||||
AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable);
|
||||
~AssetTrackingImpl();
|
||||
|
||||
void AssetBegin(const char* id, const char* file, int line);
|
||||
void AssetAttach(void* otherAllocation, const char* file, int line);
|
||||
void AssetEnd();
|
||||
|
||||
ThreadData& GetThreadData() override;
|
||||
|
||||
private:
|
||||
static EnvironmentVariable<AssetTrackingImpl*>& GetEnvironmentVariable();
|
||||
static AssetTrackingImpl* GetSharedInstance();
|
||||
static ThreadData& GetSharedThreadData();
|
||||
|
||||
using PrimaryAssets = AZStd::unordered_map<AssetTrackingId, AssetPrimaryInfo, AZStd::hash<AssetTrackingId>, AZStd::equal_to<AssetTrackingId>, AZStdAssetTrackingAllocator>;
|
||||
using ThreadData = ThreadData;
|
||||
using mutex_type = AZStd::mutex;
|
||||
using lock_type = AZStd::lock_guard<mutex_type>;
|
||||
|
||||
mutex_type m_mutex;
|
||||
PrimaryAssets m_primaryAssets;
|
||||
AssetTreeNodeBase* m_assetRoot = nullptr;
|
||||
AssetAllocationTableBase* m_allocationTable = nullptr;
|
||||
bool m_performingAnalysis = false;
|
||||
|
||||
friend class AssetTracking;
|
||||
friend class AssetTracking::Scope;
|
||||
AZStd::vector<AssetTreeNodeBase*, AZStdAssetTrackingAllocator> m_currentAssetStack;
|
||||
};
|
||||
|
||||
// Access thread data through a virtual function to ensure that the same thread-local data is being shared across DLLs.
|
||||
// Otherwise, the thread_local variables are replicated across DLLs that link the AzCore library, and you'll get a
|
||||
// different version in each module.
|
||||
class ThreadDataProvider
|
||||
{
|
||||
public:
|
||||
virtual ThreadData& GetThreadData() = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class AssetTrackingImpl final :
|
||||
public ThreadDataProvider
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(AssetTrackingImpl, "{01E2A099-3523-40BE-80E0-E0ADD861BEE1}");
|
||||
AZ_CLASS_ALLOCATOR(AssetTrackingImpl, OSAllocator, 0);
|
||||
|
||||
AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable);
|
||||
~AssetTrackingImpl();
|
||||
|
||||
void AssetBegin(const char* id, const char* file, int line);
|
||||
void AssetAttach(void* otherAllocation, const char* file, int line);
|
||||
void AssetEnd();
|
||||
|
||||
ThreadData& GetThreadData() override;
|
||||
|
||||
private:
|
||||
static EnvironmentVariable<AssetTrackingImpl*>& GetEnvironmentVariable();
|
||||
static AssetTrackingImpl* GetSharedInstance();
|
||||
static ThreadData& GetSharedThreadData();
|
||||
|
||||
using PrimaryAssets = AZStd::unordered_map<AssetTrackingId, AssetPrimaryInfo, AZStd::hash<AssetTrackingId>, AZStd::equal_to<AssetTrackingId>, AZStdAssetTrackingAllocator>;
|
||||
using ThreadData = ThreadData;
|
||||
using mutex_type = AZStd::mutex;
|
||||
using lock_type = AZStd::lock_guard<mutex_type>;
|
||||
|
||||
mutex_type m_mutex;
|
||||
PrimaryAssets m_primaryAssets;
|
||||
AssetTreeNodeBase* m_assetRoot = nullptr;
|
||||
AssetAllocationTableBase* m_allocationTable = nullptr;
|
||||
bool m_performingAnalysis = false;
|
||||
|
||||
friend class AssetTracking;
|
||||
friend class AssetTracking::Scope;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AssetTrackingImpl methods
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Debug
|
||||
AssetTrackingImpl::AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable) :
|
||||
m_assetRoot(&assetTree->GetRoot()),
|
||||
m_allocationTable(allocationTable)
|
||||
{
|
||||
AssetTrackingImpl::AssetTrackingImpl(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable) :
|
||||
m_assetRoot(&assetTree->GetRoot()),
|
||||
m_allocationTable(allocationTable)
|
||||
{
|
||||
AZ_Assert(!GetSharedInstance(), "Only one AssetTrackingImpl can exist!");
|
||||
AZ_Assert(!GetSharedInstance(), "Only one AssetTrackingImpl can exist!");
|
||||
|
||||
GetEnvironmentVariable().Set(this);
|
||||
AllocatorManager::Instance().EnterProfilingMode();
|
||||
GetEnvironmentVariable().Set(this);
|
||||
AllocatorManager::Instance().EnterProfilingMode();
|
||||
}
|
||||
|
||||
AssetTrackingImpl::~AssetTrackingImpl()
|
||||
{
|
||||
AllocatorManager::Instance().ExitProfilingMode();
|
||||
GetEnvironmentVariable().Reset();
|
||||
}
|
||||
|
||||
void AssetTrackingImpl::AssetBegin(const char* id, const char* file, int line)
|
||||
{
|
||||
// In the future it may be desirable to organize assets based on where in code the asset was entered into.
|
||||
// For now these are ignored.
|
||||
AZ_UNUSED(file);
|
||||
AZ_UNUSED(line);
|
||||
|
||||
using namespace Internal;
|
||||
|
||||
AssetTrackingId assetId(id);
|
||||
auto& threadData = GetSharedThreadData();
|
||||
AssetTreeNodeBase* parentAsset = threadData.m_currentAssetStack.empty() ? nullptr : threadData.m_currentAssetStack.back();
|
||||
AssetTreeNodeBase* childAsset;
|
||||
AssetPrimaryInfo* assetPrimaryInfo;
|
||||
|
||||
if (!parentAsset)
|
||||
{
|
||||
parentAsset = m_assetRoot;
|
||||
}
|
||||
|
||||
AssetTrackingImpl::~AssetTrackingImpl()
|
||||
{
|
||||
AllocatorManager::Instance().ExitProfilingMode();
|
||||
GetEnvironmentVariable().Reset();
|
||||
}
|
||||
lock_type lock(m_mutex);
|
||||
|
||||
void AssetTrackingImpl::AssetBegin(const char* id, const char* file, int line)
|
||||
{
|
||||
// In the future it may be desirable to organize assets based on where in code the asset was entered into.
|
||||
// For now these are ignored.
|
||||
AZ_UNUSED(file);
|
||||
AZ_UNUSED(line);
|
||||
// Locate or create the primary record for this asset
|
||||
auto primaryItr = m_primaryAssets.find(assetId);
|
||||
|
||||
using namespace Internal;
|
||||
|
||||
AssetTrackingId assetId(id);
|
||||
auto& threadData = GetSharedThreadData();
|
||||
AssetTreeNodeBase* parentAsset = threadData.m_currentAssetStack.empty() ? nullptr : threadData.m_currentAssetStack.back();
|
||||
AssetTreeNodeBase* childAsset;
|
||||
AssetPrimaryInfo* assetPrimaryInfo;
|
||||
|
||||
if (!parentAsset)
|
||||
if (primaryItr != m_primaryAssets.end())
|
||||
{
|
||||
parentAsset = m_assetRoot;
|
||||
}
|
||||
|
||||
{
|
||||
lock_type lock(m_mutex);
|
||||
|
||||
// Locate or create the primary record for this asset
|
||||
auto primaryItr = m_primaryAssets.find(assetId);
|
||||
|
||||
if (primaryItr != m_primaryAssets.end())
|
||||
{
|
||||
assetPrimaryInfo = &primaryItr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto insertResult = m_primaryAssets.emplace(assetId, AssetPrimaryInfo());
|
||||
assetPrimaryInfo = &insertResult.first->second;
|
||||
assetPrimaryInfo->m_id = &insertResult.first->first;
|
||||
}
|
||||
|
||||
// Add this asset to the stack for this thread's context
|
||||
childAsset = parentAsset->FindOrAddChild(assetId, assetPrimaryInfo);
|
||||
}
|
||||
|
||||
threadData.m_currentAssetStack.push_back(childAsset);
|
||||
}
|
||||
|
||||
void AssetTrackingImpl::AssetAttach(void* otherAllocation, const char* file, int line)
|
||||
{
|
||||
AZ_UNUSED(file);
|
||||
AZ_UNUSED(line);
|
||||
|
||||
using namespace Internal;
|
||||
|
||||
AssetTreeNodeBase* assetInfo = m_allocationTable->FindAllocation(otherAllocation);
|
||||
|
||||
// We will push back a nullptr if there is no asset, this is necessary to balance the call to AssetEnd()
|
||||
GetSharedThreadData().m_currentAssetStack.push_back(assetInfo);
|
||||
}
|
||||
|
||||
void AssetTrackingImpl::AssetEnd()
|
||||
{
|
||||
AZ_Assert(!GetSharedThreadData().m_currentAssetStack.empty(), "AssetEnd() called without matching AssetBegin() or AssetAttach. Use the AZ_ASSET_NAMED_SCOPE and AZ_ASSET_ATTACH_TO_SCOPE macros to avoid this!");
|
||||
GetSharedThreadData().m_currentAssetStack.pop_back();
|
||||
}
|
||||
|
||||
AssetTrackingImpl* AssetTrackingImpl::GetSharedInstance()
|
||||
{
|
||||
auto environmentVariable = GetEnvironmentVariable();
|
||||
|
||||
if(environmentVariable)
|
||||
{
|
||||
return *environmentVariable;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ThreadData& AssetTrackingImpl::GetSharedThreadData()
|
||||
{
|
||||
// Cast to the base type so our virtual call doesn't get optimized away. We require GetThreadData() to be executed in the same DLL every time.
|
||||
return static_cast<ThreadDataProvider*>(GetSharedInstance())->GetThreadData();
|
||||
}
|
||||
|
||||
AssetTrackingImpl::ThreadData& AssetTrackingImpl::GetThreadData()
|
||||
{
|
||||
static thread_local ThreadData* data = nullptr;
|
||||
static thread_local typename AZStd::aligned_storage_t<sizeof(ThreadData), alignof(ThreadData)> storage;
|
||||
|
||||
if (!data)
|
||||
{
|
||||
data = new (&storage) ThreadData;
|
||||
}
|
||||
|
||||
return *data;
|
||||
}
|
||||
|
||||
EnvironmentVariable<AssetTrackingImpl*>& AssetTrackingImpl::GetEnvironmentVariable()
|
||||
{
|
||||
static EnvironmentVariable<AssetTrackingImpl*> assetTrackingImpl = Environment::CreateVariable<AssetTrackingImpl*>(AzTypeInfo<AssetTrackingImpl*>::Name());
|
||||
|
||||
return assetTrackingImpl;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AssetTracking::Scope functions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AssetTracking::Scope AssetTracking::Scope::ScopeFromAssetId(const char* file, int line, const char* fmt, ...)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
static const int BUFFER_SIZE = 1024;
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
impl->AssetBegin(buffer, file, line);
|
||||
}
|
||||
|
||||
return Scope();
|
||||
}
|
||||
|
||||
AssetTracking::Scope AssetTracking::Scope::ScopeFromAttachment(void* attachTo, const char* file, int line)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetAttach(attachTo, file, line);
|
||||
}
|
||||
|
||||
return Scope();
|
||||
}
|
||||
|
||||
AssetTracking::Scope::~Scope()
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetEnd();
|
||||
}
|
||||
}
|
||||
|
||||
AssetTracking::Scope::Scope()
|
||||
{
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AssetTracking functions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AssetTracking::EnterScopeByAssetId(const char* file, int line, const char* fmt, ...)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
static const int BUFFER_SIZE = 1024;
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
impl->AssetBegin(buffer, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetTracking::EnterScopeByAttachment(void* attachTo, const char* file, int line)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetAttach(attachTo, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetTracking::ExitScope()
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetEnd();
|
||||
}
|
||||
}
|
||||
|
||||
const char* AssetTracking::GetDebugScope()
|
||||
{
|
||||
// Output debug information about the current asset scope in the current thread.
|
||||
// Do not use in production code.
|
||||
#ifndef RELEASE
|
||||
static const int BUFFER_SIZE = 1024;
|
||||
static char buffer[BUFFER_SIZE];
|
||||
const auto& assetStack = AssetTrackingImpl::GetSharedInstance()->GetThreadData().m_currentAssetStack;
|
||||
|
||||
if (assetStack.empty())
|
||||
{
|
||||
azsnprintf(buffer, BUFFER_SIZE, "<none>");
|
||||
assetPrimaryInfo = &primaryItr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
char* pos = buffer;
|
||||
for (auto itr = assetStack.rbegin(); itr != assetStack.rend(); ++itr)
|
||||
{
|
||||
pos += azsnprintf(pos, BUFFER_SIZE - (pos - buffer), "%s\n", (*itr)->GetAssetPrimaryInfo()->m_id->m_id.c_str());
|
||||
|
||||
if (pos >= buffer + BUFFER_SIZE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto insertResult = m_primaryAssets.emplace(assetId, AssetPrimaryInfo());
|
||||
assetPrimaryInfo = &insertResult.first->second;
|
||||
assetPrimaryInfo->m_id = &insertResult.first->first;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
// Add this asset to the stack for this thread's context
|
||||
childAsset = parentAsset->FindOrAddChild(assetId, assetPrimaryInfo);
|
||||
}
|
||||
|
||||
AssetTracking::AssetTracking(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable)
|
||||
threadData.m_currentAssetStack.push_back(childAsset);
|
||||
}
|
||||
|
||||
void AssetTrackingImpl::AssetAttach(void* otherAllocation, const char* file, int line)
|
||||
{
|
||||
AZ_UNUSED(file);
|
||||
AZ_UNUSED(line);
|
||||
|
||||
using namespace Internal;
|
||||
|
||||
AssetTreeNodeBase* assetInfo = m_allocationTable->FindAllocation(otherAllocation);
|
||||
|
||||
// We will push back a nullptr if there is no asset, this is necessary to balance the call to AssetEnd()
|
||||
GetSharedThreadData().m_currentAssetStack.push_back(assetInfo);
|
||||
}
|
||||
|
||||
void AssetTrackingImpl::AssetEnd()
|
||||
{
|
||||
AZ_Assert(!GetSharedThreadData().m_currentAssetStack.empty(), "AssetEnd() called without matching AssetBegin() or AssetAttach. Use the AZ_ASSET_NAMED_SCOPE and AZ_ASSET_ATTACH_TO_SCOPE macros to avoid this!");
|
||||
GetSharedThreadData().m_currentAssetStack.pop_back();
|
||||
}
|
||||
|
||||
AssetTrackingImpl* AssetTrackingImpl::GetSharedInstance()
|
||||
{
|
||||
auto environmentVariable = GetEnvironmentVariable();
|
||||
|
||||
if(environmentVariable)
|
||||
{
|
||||
m_impl.reset(aznew AssetTrackingImpl(assetTree, allocationTable));
|
||||
return *environmentVariable;
|
||||
}
|
||||
|
||||
AssetTracking::~AssetTracking()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ThreadData& AssetTrackingImpl::GetSharedThreadData()
|
||||
{
|
||||
// Cast to the base type so our virtual call doesn't get optimized away. We require GetThreadData() to be executed in the same DLL every time.
|
||||
return static_cast<ThreadDataProvider*>(GetSharedInstance())->GetThreadData();
|
||||
}
|
||||
|
||||
AssetTrackingImpl::ThreadData& AssetTrackingImpl::GetThreadData()
|
||||
{
|
||||
static thread_local ThreadData* data = nullptr;
|
||||
static thread_local typename AZStd::aligned_storage_t<sizeof(ThreadData), alignof(ThreadData)> storage;
|
||||
|
||||
if (!data)
|
||||
{
|
||||
data = new (&storage) ThreadData;
|
||||
}
|
||||
|
||||
AssetTreeNodeBase* AssetTracking::GetCurrentThreadAsset() const
|
||||
{
|
||||
const auto& assetStack = m_impl->GetThreadData().m_currentAssetStack;
|
||||
AssetTreeNodeBase* result = assetStack.empty() ? nullptr : assetStack.back();
|
||||
return *data;
|
||||
}
|
||||
|
||||
return result;
|
||||
EnvironmentVariable<AssetTrackingImpl*>& AssetTrackingImpl::GetEnvironmentVariable()
|
||||
{
|
||||
static EnvironmentVariable<AssetTrackingImpl*> assetTrackingImpl = Environment::CreateVariable<AssetTrackingImpl*>(AzTypeInfo<AssetTrackingImpl*>::Name());
|
||||
|
||||
return assetTrackingImpl;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AssetTracking::Scope functions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AssetTracking::Scope AssetTracking::Scope::ScopeFromAssetId(const char* file, int line, const char* fmt, ...)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
static const int BUFFER_SIZE = 1024;
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
impl->AssetBegin(buffer, file, line);
|
||||
}
|
||||
|
||||
return Scope();
|
||||
}
|
||||
|
||||
AssetTracking::Scope AssetTracking::Scope::ScopeFromAttachment(void* attachTo, const char* file, int line)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetAttach(attachTo, file, line);
|
||||
}
|
||||
|
||||
return Scope();
|
||||
}
|
||||
|
||||
AssetTracking::Scope::~Scope()
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetEnd();
|
||||
}
|
||||
}
|
||||
|
||||
AssetTracking::Scope::Scope()
|
||||
{
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AssetTracking functions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AssetTracking::EnterScopeByAssetId(const char* file, int line, const char* fmt, ...)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
static const int BUFFER_SIZE = 1024;
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
azvsnprintf(buffer, BUFFER_SIZE, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
impl->AssetBegin(buffer, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetTracking::EnterScopeByAttachment(void* attachTo, const char* file, int line)
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetAttach(attachTo, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetTracking::ExitScope()
|
||||
{
|
||||
if (auto impl = AssetTrackingImpl::GetSharedInstance())
|
||||
{
|
||||
impl->AssetEnd();
|
||||
}
|
||||
}
|
||||
|
||||
const char* AssetTracking::GetDebugScope()
|
||||
{
|
||||
// Output debug information about the current asset scope in the current thread.
|
||||
// Do not use in production code.
|
||||
#ifndef RELEASE
|
||||
static const int BUFFER_SIZE = 1024;
|
||||
static char buffer[BUFFER_SIZE];
|
||||
const auto& assetStack = AssetTrackingImpl::GetSharedInstance()->GetThreadData().m_currentAssetStack;
|
||||
|
||||
if (assetStack.empty())
|
||||
{
|
||||
azsnprintf(buffer, BUFFER_SIZE, "<none>");
|
||||
}
|
||||
else
|
||||
{
|
||||
char* pos = buffer;
|
||||
for (auto itr = assetStack.rbegin(); itr != assetStack.rend(); ++itr)
|
||||
{
|
||||
pos += azsnprintf(pos, BUFFER_SIZE - (pos - buffer), "%s\n", (*itr)->GetAssetPrimaryInfo()->m_id->m_id.c_str());
|
||||
|
||||
if (pos >= buffer + BUFFER_SIZE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
AssetTracking::AssetTracking(AssetTreeBase* assetTree, AssetAllocationTableBase* allocationTable)
|
||||
{
|
||||
m_impl.reset(aznew AssetTrackingImpl(assetTree, allocationTable));
|
||||
}
|
||||
|
||||
AssetTracking::~AssetTracking()
|
||||
{
|
||||
}
|
||||
|
||||
AssetTreeNodeBase* AssetTracking::GetCurrentThreadAsset() const
|
||||
{
|
||||
const auto& assetStack = m_impl->GetThreadData().m_currentAssetStack;
|
||||
AssetTreeNodeBase* result = assetStack.empty() ? nullptr : assetStack.back();
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -11,19 +11,19 @@
|
||||
#include <AzCore/std/time.h>
|
||||
#include <AzCore/std/parallel/thread.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
EventTrace::ScopedSlice::ScopedSlice(const char* name, const char* category)
|
||||
: m_Name(name)
|
||||
, m_Category(category)
|
||||
, m_Time(AZStd::GetTimeNowMicroSecond())
|
||||
{
|
||||
EventTrace::ScopedSlice::ScopedSlice(const char* name, const char* category)
|
||||
: m_Name(name)
|
||||
, m_Category(category)
|
||||
, m_Time(AZStd::GetTimeNowMicroSecond())
|
||||
{}
|
||||
|
||||
EventTrace::ScopedSlice::~ScopedSlice()
|
||||
{
|
||||
EventTraceDrillerBus::TryQueueBroadcast(&EventTraceDrillerInterface::RecordSlice, m_Name, m_Category, AZStd::this_thread::get_id(), m_Time, (uint32_t)(AZStd::GetTimeNowMicroSecond() - m_Time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventTrace::ScopedSlice::~ScopedSlice()
|
||||
{
|
||||
EventTraceDrillerBus::TryQueueBroadcast(
|
||||
&EventTraceDrillerInterface::RecordSlice, m_Name, m_Category, AZStd::this_thread::get_id(), m_Time,
|
||||
(uint32_t)(AZStd::GetTimeNowMicroSecond() - m_Time));
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
|
||||
@@ -11,151 +11,149 @@
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
namespace Crc
|
||||
{
|
||||
namespace Crc
|
||||
constexpr u32 EventTraceDriller = AZ_CRC_CE("EventTraceDriller");
|
||||
constexpr u32 Slice = AZ_CRC_CE("Slice");
|
||||
constexpr u32 ThreadInfo = AZ_CRC_CE("ThreadInfo");
|
||||
constexpr u32 Name = AZ_CRC_CE("Name");
|
||||
constexpr u32 Category = AZ_CRC_CE("Category");
|
||||
constexpr u32 ThreadId = AZ_CRC_CE("ThreadId");
|
||||
constexpr u32 Timestamp = AZ_CRC_CE("Timestamp");
|
||||
constexpr u32 Duration = AZ_CRC_CE("Duration");
|
||||
constexpr u32 Instant = AZ_CRC_CE("Instant");
|
||||
}
|
||||
|
||||
EventTraceDriller::EventTraceDriller()
|
||||
{
|
||||
EventTraceDrillerSetupBus::Handler::BusConnect();
|
||||
AZStd::ThreadDrillerEventBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
EventTraceDriller::~EventTraceDriller()
|
||||
{
|
||||
AZStd::ThreadDrillerEventBus::Handler::BusDisconnect();
|
||||
EventTraceDrillerSetupBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void EventTraceDriller::Start(const Param* params, int numParams)
|
||||
{
|
||||
(void)params;
|
||||
(void)numParams;
|
||||
|
||||
EventTraceDrillerBus::Handler::BusConnect();
|
||||
TickBus::Handler::BusConnect();
|
||||
|
||||
EventTraceDrillerBus::AllowFunctionQueuing(true);
|
||||
}
|
||||
|
||||
void EventTraceDriller::Stop()
|
||||
{
|
||||
EventTraceDrillerBus::AllowFunctionQueuing(false);
|
||||
EventTraceDrillerBus::ClearQueuedEvents();
|
||||
|
||||
EventTraceDrillerBus::Handler::BusDisconnect();
|
||||
TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void EventTraceDriller::OnTick(float deltaTime, ScriptTimePoint time)
|
||||
{
|
||||
(void)deltaTime;
|
||||
(void)time;
|
||||
|
||||
AZ_TRACE_METHOD();
|
||||
RecordThreads();
|
||||
EventTraceDrillerBus::ExecuteQueuedEvents();
|
||||
}
|
||||
|
||||
void EventTraceDriller::SetThreadName(const AZStd::thread_id& id, const char* name)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_ThreadMutex);
|
||||
m_Threads[(size_t)id.m_id] = ThreadData{ name };
|
||||
}
|
||||
|
||||
void EventTraceDriller::OnThreadEnter(const AZStd::thread::id& id, const AZStd::thread_desc* desc)
|
||||
{
|
||||
if (desc && desc->m_name)
|
||||
{
|
||||
const u32 EventTraceDriller = AZ_CRC("EventTraceDriller", 0xf7aeae55);
|
||||
const u32 Slice = AZ_CRC("Slice", 0x3dae78a5);
|
||||
const u32 ThreadInfo = AZ_CRC("ThreadInfo", 0x89bf78be);
|
||||
const u32 Name = AZ_CRC("Name", 0x5e237e06);
|
||||
const u32 Category = AZ_CRC("Category", 0x064c19c1);
|
||||
const u32 ThreadId = AZ_CRC("ThreadId", 0xd0fd9043);
|
||||
const u32 Timestamp = AZ_CRC("Timestamp", 0xa5d6e63e);
|
||||
const u32 Duration = AZ_CRC("Duration", 0x865f80c0);
|
||||
const u32 Instant = AZ_CRC("Instant", 0x0e9047ad);
|
||||
SetThreadName(id, desc->m_name);
|
||||
}
|
||||
}
|
||||
|
||||
EventTraceDriller::EventTraceDriller()
|
||||
void EventTraceDriller::OnThreadExit(const AZStd::thread::id& id)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_ThreadMutex);
|
||||
m_Threads.erase((size_t)id.m_id);
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordThreads()
|
||||
{
|
||||
if (!m_output || m_Threads.empty())
|
||||
{
|
||||
EventTraceDrillerSetupBus::Handler::BusConnect();
|
||||
AZStd::ThreadDrillerEventBus::Handler::BusConnect();
|
||||
return;
|
||||
}
|
||||
// Main bus mutex guards m_output.
|
||||
auto& context = EventTraceDrillerBus::GetOrCreateContext();
|
||||
|
||||
EventTraceDriller::~EventTraceDriller()
|
||||
{
|
||||
AZStd::ThreadDrillerEventBus::Handler::BusDisconnect();
|
||||
EventTraceDrillerSetupBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void EventTraceDriller::Start(const Param* params, int numParams)
|
||||
{
|
||||
(void)params;
|
||||
(void)numParams;
|
||||
|
||||
EventTraceDrillerBus::Handler::BusConnect();
|
||||
TickBus::Handler::BusConnect();
|
||||
|
||||
EventTraceDrillerBus::AllowFunctionQueuing(true);
|
||||
}
|
||||
|
||||
void EventTraceDriller::Stop()
|
||||
{
|
||||
EventTraceDrillerBus::AllowFunctionQueuing(false);
|
||||
EventTraceDrillerBus::ClearQueuedEvents();
|
||||
|
||||
EventTraceDrillerBus::Handler::BusDisconnect();
|
||||
TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void EventTraceDriller::OnTick(float deltaTime, ScriptTimePoint time)
|
||||
{
|
||||
(void)deltaTime;
|
||||
(void)time;
|
||||
|
||||
AZ_TRACE_METHOD();
|
||||
RecordThreads();
|
||||
EventTraceDrillerBus::ExecuteQueuedEvents();
|
||||
}
|
||||
|
||||
void EventTraceDriller::SetThreadName(const AZStd::thread_id& id, const char* name)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_ThreadMutex);
|
||||
m_Threads[(size_t)id.m_id] = ThreadData{ name };
|
||||
}
|
||||
|
||||
void EventTraceDriller::OnThreadEnter(const AZStd::thread::id& id, const AZStd::thread_desc* desc)
|
||||
{
|
||||
if (desc && desc->m_name)
|
||||
{
|
||||
SetThreadName(id, desc->m_name);
|
||||
}
|
||||
}
|
||||
|
||||
void EventTraceDriller::OnThreadExit(const AZStd::thread::id& id)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_ThreadMutex);
|
||||
m_Threads.erase((size_t)id.m_id);
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordThreads()
|
||||
{
|
||||
if (m_output && m_Threads.size())
|
||||
{
|
||||
// Main bus mutex guards m_output.
|
||||
auto& context = EventTraceDrillerBus::GetOrCreateContext();
|
||||
|
||||
AZStd::scoped_lock<decltype(context.m_contextMutex), decltype(m_ThreadMutex)> lock(context.m_contextMutex, m_ThreadMutex);
|
||||
for (const auto& keyValue : m_Threads)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::ThreadInfo);
|
||||
m_output->Write(Crc::ThreadId, keyValue.first);
|
||||
m_output->Write(Crc::Name, keyValue.second.name);
|
||||
m_output->EndTag(Crc::ThreadInfo);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordSlice(
|
||||
const char* name,
|
||||
const char* category,
|
||||
const AZStd::thread_id threadId,
|
||||
AZ::u64 timestamp,
|
||||
AZ::u32 duration)
|
||||
AZStd::scoped_lock<decltype(context.m_contextMutex), decltype(m_ThreadMutex)> lock(context.m_contextMutex, m_ThreadMutex);
|
||||
for (const auto& keyValue : m_Threads)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::Slice);
|
||||
m_output->Write(Crc::Name, name);
|
||||
m_output->Write(Crc::Category, category);
|
||||
m_output->Write(Crc::ThreadId, (size_t)threadId.m_id);
|
||||
m_output->Write(Crc::Timestamp, timestamp);
|
||||
m_output->Write(Crc::Duration, std::max(duration, 1u));
|
||||
m_output->EndTag(Crc::Slice);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordInstantGlobal(
|
||||
const char* name,
|
||||
const char* category,
|
||||
AZ::u64 timestamp)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::Instant);
|
||||
m_output->Write(Crc::Name, name);
|
||||
m_output->Write(Crc::Category, category);
|
||||
m_output->Write(Crc::Timestamp, timestamp);
|
||||
m_output->EndTag(Crc::Instant);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordInstantThread(
|
||||
const char* name,
|
||||
const char* category,
|
||||
const AZStd::thread_id threadId,
|
||||
AZ::u64 timestamp)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::Instant);
|
||||
m_output->Write(Crc::Name, name);
|
||||
m_output->Write(Crc::Category, category);
|
||||
m_output->Write(Crc::ThreadId, (size_t)threadId.m_id);
|
||||
m_output->Write(Crc::Timestamp, timestamp);
|
||||
m_output->EndTag(Crc::Instant);
|
||||
m_output->BeginTag(Crc::ThreadInfo);
|
||||
m_output->Write(Crc::ThreadId, keyValue.first);
|
||||
m_output->Write(Crc::Name, keyValue.second.name);
|
||||
m_output->EndTag(Crc::ThreadInfo);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordSlice(
|
||||
const char* name,
|
||||
const char* category,
|
||||
const AZStd::thread_id threadId,
|
||||
AZ::u64 timestamp,
|
||||
AZ::u32 duration)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::Slice);
|
||||
m_output->Write(Crc::Name, name);
|
||||
m_output->Write(Crc::Category, category);
|
||||
m_output->Write(Crc::ThreadId, (size_t)threadId.m_id);
|
||||
m_output->Write(Crc::Timestamp, timestamp);
|
||||
m_output->Write(Crc::Duration, std::max(duration, 1u));
|
||||
m_output->EndTag(Crc::Slice);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordInstantGlobal(
|
||||
const char* name,
|
||||
const char* category,
|
||||
AZ::u64 timestamp)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::Instant);
|
||||
m_output->Write(Crc::Name, name);
|
||||
m_output->Write(Crc::Category, category);
|
||||
m_output->Write(Crc::Timestamp, timestamp);
|
||||
m_output->EndTag(Crc::Instant);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
|
||||
void EventTraceDriller::RecordInstantThread(
|
||||
const char* name,
|
||||
const char* category,
|
||||
const AZStd::thread_id threadId,
|
||||
AZ::u64 timestamp)
|
||||
{
|
||||
m_output->BeginTag(Crc::EventTraceDriller);
|
||||
m_output->BeginTag(Crc::Instant);
|
||||
m_output->Write(Crc::Name, name);
|
||||
m_output->Write(Crc::Category, category);
|
||||
m_output->Write(Crc::ThreadId, (size_t)threadId.m_id);
|
||||
m_output->Write(Crc::Timestamp, timestamp);
|
||||
m_output->EndTag(Crc::Instant);
|
||||
m_output->EndTag(Crc::EventTraceDriller);
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
|
||||
@@ -79,6 +79,7 @@ namespace AZ::Debug
|
||||
constexpr LogLevel DefaultLogLevel = LogLevel::Info;
|
||||
|
||||
AZ_CVAR_SCOPED(int, bg_traceLogLevel, DefaultLogLevel, nullptr, ConsoleFunctorFlags::Null, "Enable trace message logging in release mode. 0=disabled, 1=errors, 2=warnings, 3=info.");
|
||||
AZ_CVAR_SCOPED(bool, bg_alwaysShowCallstack, false, nullptr, ConsoleFunctorFlags::Null, "Force stack trace output without allowing ebus interception.");
|
||||
|
||||
/**
|
||||
* If any listener returns true, store the result so we don't outputs detailed information.
|
||||
@@ -280,6 +281,13 @@ namespace AZ::Debug
|
||||
|
||||
TraceMessageResult result;
|
||||
EBUS_EVENT_RESULT(result, TraceMessageBus, OnPreAssert, fileName, line, funcName, message);
|
||||
|
||||
if (bg_alwaysShowCallstack)
|
||||
{
|
||||
// If we're always showing the callstack, print it now before there's any chance of an ebus handler interrupting
|
||||
PrintCallstack(g_dbgSystemWnd, 1);
|
||||
}
|
||||
|
||||
if (result.m_value)
|
||||
{
|
||||
g_alreadyHandlingAssertOrFatal = false;
|
||||
@@ -305,7 +313,10 @@ namespace AZ::Debug
|
||||
}
|
||||
|
||||
Output(g_dbgSystemWnd, "------------------------------------------------\n");
|
||||
PrintCallstack(g_dbgSystemWnd, 1);
|
||||
if (!bg_alwaysShowCallstack)
|
||||
{
|
||||
PrintCallstack(g_dbgSystemWnd, 1);
|
||||
}
|
||||
Output(g_dbgSystemWnd, "==================================================================\n");
|
||||
|
||||
char dialogBoxText[g_maxMessageLength];
|
||||
@@ -530,6 +541,16 @@ namespace AZ::Debug
|
||||
}*/
|
||||
}
|
||||
|
||||
RawOutput(window, message);
|
||||
}
|
||||
|
||||
void Trace::RawOutput(const char* window, const char* message)
|
||||
{
|
||||
if (!window)
|
||||
{
|
||||
window = g_dbgSystemWnd;
|
||||
}
|
||||
|
||||
// printf on Windows platforms seem to have a buffer length limit of 4096 characters
|
||||
// Therefore fwrite is used directly to write the window and message to stdout
|
||||
AZStd::string_view windowView{ window };
|
||||
@@ -574,9 +595,19 @@ namespace AZ::Debug
|
||||
}
|
||||
|
||||
azstrcat(lines[i], AZ_ARRAY_SIZE(lines[i]), "\n");
|
||||
|
||||
// Use Output instead of AZ_Printf to be consistent with the exception output code and avoid
|
||||
// this accidentally being suppressed as a normal message
|
||||
Output(window, lines[i]);
|
||||
|
||||
if (bg_alwaysShowCallstack)
|
||||
{
|
||||
// Use Raw Output as this cannot be suppressed
|
||||
RawOutput(window, lines[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Output(window, lines[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,9 @@ namespace AZ
|
||||
|
||||
static void Output(const char* window, const char* message);
|
||||
|
||||
/// Called by output to handle the actual output, does not interact with ebus or allow interception
|
||||
static void RawOutput(const char* window, const char* message);
|
||||
|
||||
static void PrintCallstack(const char* window, unsigned int suppressCount = 0, void* nativeContext = 0);
|
||||
|
||||
/// PEXCEPTION_POINTERS on Windows, always NULL on other platforms
|
||||
|
||||
@@ -9,94 +9,91 @@
|
||||
#include <AzCore/Debug/TraceMessagesDriller.h>
|
||||
#include <AzCore/Math/Crc.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
//=========================================================================
|
||||
// Start
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::Start(const Param* params, int numParams)
|
||||
{
|
||||
//=========================================================================
|
||||
// Start
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::Start(const Param* params, int numParams)
|
||||
{
|
||||
(void)params;
|
||||
(void)numParams;
|
||||
BusConnect();
|
||||
}
|
||||
(void)params;
|
||||
(void)numParams;
|
||||
BusConnect();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Stop
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::Stop()
|
||||
{
|
||||
BusDisconnect();
|
||||
}
|
||||
//=========================================================================
|
||||
// Stop
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::Stop()
|
||||
{
|
||||
BusDisconnect();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// OnAssert
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnAssert(const char* message)
|
||||
{
|
||||
// Not sure if we can really capture assert since the code will stop executing very soon.
|
||||
m_output->BeginTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
m_output->Write(AZ_CRC("OnAssert", 0xb74db4ce), message);
|
||||
m_output->EndTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
}
|
||||
//=========================================================================
|
||||
// OnAssert
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnAssert(const char* message)
|
||||
{
|
||||
// Not sure if we can really capture assert since the code will stop executing very soon.
|
||||
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
m_output->Write(AZ_CRC_CE("OnAssert"), message);
|
||||
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// OnException
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnException(const char* message)
|
||||
{
|
||||
// Not sure if we can really capture exception since the code will stop executing very soon.
|
||||
m_output->BeginTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
m_output->Write(AZ_CRC("OnException", 0xfe457d12), message);
|
||||
m_output->EndTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
}
|
||||
//=========================================================================
|
||||
// OnException
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnException(const char* message)
|
||||
{
|
||||
// Not sure if we can really capture exception since the code will stop executing very soon.
|
||||
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
m_output->Write(AZ_CRC_CE("OnException"), message);
|
||||
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// OnError
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnError(const char* window, const char* message)
|
||||
{
|
||||
m_output->BeginTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
m_output->BeginTag(AZ_CRC("OnError", 0x4993c634));
|
||||
m_output->Write(AZ_CRC("Window", 0x8be4f9dd), window);
|
||||
m_output->Write(AZ_CRC("Message", 0xb6bd307f), message);
|
||||
m_output->EndTag(AZ_CRC("OnError", 0x4993c634));
|
||||
m_output->EndTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
}
|
||||
//=========================================================================
|
||||
// OnError
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnError(const char* window, const char* message)
|
||||
{
|
||||
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
m_output->BeginTag(AZ_CRC_CE("OnError"));
|
||||
m_output->Write(AZ_CRC_CE("Window"), window);
|
||||
m_output->Write(AZ_CRC_CE("Message"), message);
|
||||
m_output->EndTag(AZ_CRC_CE("OnError"));
|
||||
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// OnWarning
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnWarning(const char* window, const char* message)
|
||||
{
|
||||
m_output->BeginTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
m_output->BeginTag(AZ_CRC("OnWarning", 0x7d90abea));
|
||||
m_output->Write(AZ_CRC("Window", 0x8be4f9dd), window);
|
||||
m_output->Write(AZ_CRC("Message", 0xb6bd307f), message);
|
||||
m_output->EndTag(AZ_CRC("OnWarning", 0x7d90abea));
|
||||
m_output->EndTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
}
|
||||
//=========================================================================
|
||||
// OnWarning
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnWarning(const char* window, const char* message)
|
||||
{
|
||||
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
m_output->BeginTag(AZ_CRC_CE("OnWarning"));
|
||||
m_output->Write(AZ_CRC_CE("Window"), window);
|
||||
m_output->Write(AZ_CRC_CE("Message"), message);
|
||||
m_output->EndTag(AZ_CRC_CE("OnWarning"));
|
||||
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// OnPrintf
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnPrintf(const char* window, const char* message)
|
||||
{
|
||||
m_output->BeginTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
m_output->BeginTag(AZ_CRC("OnPrintf", 0xd4b5c294));
|
||||
m_output->Write(AZ_CRC("Window", 0x8be4f9dd), window);
|
||||
m_output->Write(AZ_CRC("Message", 0xb6bd307f), message);
|
||||
m_output->EndTag(AZ_CRC("OnPrintf", 0xd4b5c294));
|
||||
m_output->EndTag(AZ_CRC("TraceMessagesDriller", 0xa61d1b00));
|
||||
}
|
||||
} // namespace Debug
|
||||
//=========================================================================
|
||||
// OnPrintf
|
||||
// [2/6/2013]
|
||||
//=========================================================================
|
||||
void TraceMessagesDriller::OnPrintf(const char* window, const char* message)
|
||||
{
|
||||
m_output->BeginTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
m_output->BeginTag(AZ_CRC_CE("OnPrintf"));
|
||||
m_output->Write(AZ_CRC_CE("Window"), window);
|
||||
m_output->Write(AZ_CRC_CE("Message"), message);
|
||||
m_output->EndTag(AZ_CRC_CE("OnPrintf"));
|
||||
m_output->EndTag(AZ_CRC_CE("TraceMessagesDriller"));
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
@@ -12,283 +12,280 @@
|
||||
#include <AzCore/Script/ScriptContext.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
//! Trace Message Event Handler for Automation.
|
||||
//! Since TraceMessageBus will be called from multiple threads and
|
||||
//! python interpreter is single threaded, all the bus calls are
|
||||
//! queued into a list and called at the end of the frame in the main thread.
|
||||
//! @note this class is not using the usual AZ_EBUS_BEHAVIOR_BINDER
|
||||
//! macro as the signature needs to be changed to connect to Tick bus.
|
||||
class TraceMessageBusHandler
|
||||
: public AZ::Debug::TraceMessageBus::Handler
|
||||
, public AZ::BehaviorEBusHandler
|
||||
, public AZ::TickBus::Handler
|
||||
{
|
||||
//! Trace Message Event Handler for Automation.
|
||||
//! Since TraceMessageBus will be called from multiple threads and
|
||||
//! python interpreter is single threaded, all the bus calls are
|
||||
//! queued into a list and called at the end of the frame in the main thread.
|
||||
//! @note this class is not using the usual AZ_EBUS_BEHAVIOR_BINDER
|
||||
//! macro as the signature needs to be changed to connect to Tick bus.
|
||||
class TraceMessageBusHandler
|
||||
: public AZ::Debug::TraceMessageBus::Handler
|
||||
, public AZ::BehaviorEBusHandler
|
||||
, public AZ::TickBus::Handler
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(TraceMessageBusHandler, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(TraceMessageBusHandler, "{5CDBAF09-5EB0-48AC-B327-2AF8601BB550}", AZ::BehaviorEBusHandler);
|
||||
|
||||
TraceMessageBusHandler();
|
||||
|
||||
using EventFunctionsParameterPack = AZStd::Internal::pack_traits_arg_sequence<
|
||||
decltype(&TraceMessageBusHandler::OnPreAssert),
|
||||
decltype(&TraceMessageBusHandler::OnPreError),
|
||||
decltype(&TraceMessageBusHandler::OnPreWarning),
|
||||
decltype(&TraceMessageBusHandler::OnAssert),
|
||||
decltype(&TraceMessageBusHandler::OnError),
|
||||
decltype(&TraceMessageBusHandler::OnWarning),
|
||||
decltype(&TraceMessageBusHandler::OnException),
|
||||
decltype(&TraceMessageBusHandler::OnPrintf),
|
||||
decltype(&TraceMessageBusHandler::OnOutput)
|
||||
>;
|
||||
|
||||
enum
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(TraceMessageBusHandler, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(TraceMessageBusHandler, "{5CDBAF09-5EB0-48AC-B327-2AF8601BB550}", AZ::BehaviorEBusHandler);
|
||||
|
||||
TraceMessageBusHandler();
|
||||
|
||||
using EventFunctionsParameterPack = AZStd::Internal::pack_traits_arg_sequence<
|
||||
decltype(&TraceMessageBusHandler::OnPreAssert),
|
||||
decltype(&TraceMessageBusHandler::OnPreError),
|
||||
decltype(&TraceMessageBusHandler::OnPreWarning),
|
||||
decltype(&TraceMessageBusHandler::OnAssert),
|
||||
decltype(&TraceMessageBusHandler::OnError),
|
||||
decltype(&TraceMessageBusHandler::OnWarning),
|
||||
decltype(&TraceMessageBusHandler::OnException),
|
||||
decltype(&TraceMessageBusHandler::OnPrintf),
|
||||
decltype(&TraceMessageBusHandler::OnOutput)
|
||||
>;
|
||||
|
||||
enum
|
||||
{
|
||||
FN_OnPreAssert = 0,
|
||||
FN_OnPreError,
|
||||
FN_OnPreWarning,
|
||||
FN_OnAssert,
|
||||
FN_OnError,
|
||||
FN_OnWarning,
|
||||
FN_OnException,
|
||||
FN_OnPrintf,
|
||||
FN_OnOutput,
|
||||
FN_MAX
|
||||
};
|
||||
|
||||
static inline constexpr const char* m_functionNames[FN_MAX] =
|
||||
{
|
||||
"OnPreAssert",
|
||||
"OnPreError",
|
||||
"OnPreWarning",
|
||||
"OnAssert",
|
||||
"OnError",
|
||||
"OnWarning",
|
||||
"OnException",
|
||||
"OnPrintf",
|
||||
"OnOutput"
|
||||
};
|
||||
|
||||
// AZ::BehaviorEBusHandler overrides...
|
||||
int GetFunctionIndex(const char* functionName) const override;
|
||||
void Disconnect() override;
|
||||
bool Connect(AZ::BehaviorValueParameter* id = nullptr) override;
|
||||
bool IsConnected() override;
|
||||
bool IsConnectedId(AZ::BehaviorValueParameter* id) override;
|
||||
|
||||
// TraceMessageBus
|
||||
/*
|
||||
* Note: Since at editor runtime there is already have a handler, for automation (OnPreAssert, OnPreWarning, OnPreWarning)
|
||||
* must be used instead of (OnAssert, OnWarning, OnError)
|
||||
*/
|
||||
bool OnPreAssert(const char* fileName, int line, const char* func, const char* message) override;
|
||||
bool OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message) override;
|
||||
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override;
|
||||
bool OnAssert(const char* message) override;
|
||||
bool OnError(const char* window, const char* message) override;
|
||||
bool OnWarning(const char* window, const char* message) override;
|
||||
bool OnException(const char* message) override;
|
||||
bool OnPrintf(const char* window, const char* message) override;
|
||||
bool OnOutput(const char* window, const char* message) override;
|
||||
|
||||
// AZ::TickBus::Handler overrides ...
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
int GetTickOrder() override;
|
||||
|
||||
private:
|
||||
void QueueMessageCall(AZStd::function<void()> messageCall);
|
||||
void FlushMessageCalls();
|
||||
|
||||
AZStd::list<AZStd::function<void()>> m_messageCalls;
|
||||
AZStd::mutex m_messageCallsLock;
|
||||
FN_OnPreAssert = 0,
|
||||
FN_OnPreError,
|
||||
FN_OnPreWarning,
|
||||
FN_OnAssert,
|
||||
FN_OnError,
|
||||
FN_OnWarning,
|
||||
FN_OnException,
|
||||
FN_OnPrintf,
|
||||
FN_OnOutput,
|
||||
FN_MAX
|
||||
};
|
||||
|
||||
TraceMessageBusHandler::TraceMessageBusHandler()
|
||||
static inline constexpr const char* m_functionNames[FN_MAX] =
|
||||
{
|
||||
m_events.resize(FN_MAX);
|
||||
"OnPreAssert",
|
||||
"OnPreError",
|
||||
"OnPreWarning",
|
||||
"OnAssert",
|
||||
"OnError",
|
||||
"OnWarning",
|
||||
"OnException",
|
||||
"OnPrintf",
|
||||
"OnOutput"
|
||||
};
|
||||
|
||||
SetEvent(&TraceMessageBusHandler::OnPreAssert, m_functionNames[FN_OnPreAssert]);
|
||||
SetEvent(&TraceMessageBusHandler::OnPreError, m_functionNames[FN_OnPreError]);
|
||||
SetEvent(&TraceMessageBusHandler::OnPreWarning, m_functionNames[FN_OnPreWarning]);
|
||||
SetEvent(&TraceMessageBusHandler::OnAssert, m_functionNames[FN_OnAssert]);
|
||||
SetEvent(&TraceMessageBusHandler::OnError, m_functionNames[FN_OnError]);
|
||||
SetEvent(&TraceMessageBusHandler::OnWarning, m_functionNames[FN_OnWarning]);
|
||||
SetEvent(&TraceMessageBusHandler::OnException, m_functionNames[FN_OnException]);
|
||||
SetEvent(&TraceMessageBusHandler::OnPrintf, m_functionNames[FN_OnPrintf]);
|
||||
SetEvent(&TraceMessageBusHandler::OnOutput, m_functionNames[FN_OnOutput]);
|
||||
}
|
||||
// AZ::BehaviorEBusHandler overrides...
|
||||
int GetFunctionIndex(const char* functionName) const override;
|
||||
void Disconnect() override;
|
||||
bool Connect(AZ::BehaviorValueParameter* id = nullptr) override;
|
||||
bool IsConnected() override;
|
||||
bool IsConnectedId(AZ::BehaviorValueParameter* id) override;
|
||||
|
||||
int TraceMessageBusHandler::GetFunctionIndex(const char* functionName) const
|
||||
// TraceMessageBus
|
||||
/*
|
||||
* Note: Since at editor runtime there is already have a handler, for automation (OnPreAssert, OnPreWarning, OnPreWarning)
|
||||
* must be used instead of (OnAssert, OnWarning, OnError)
|
||||
*/
|
||||
bool OnPreAssert(const char* fileName, int line, const char* func, const char* message) override;
|
||||
bool OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message) override;
|
||||
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override;
|
||||
bool OnAssert(const char* message) override;
|
||||
bool OnError(const char* window, const char* message) override;
|
||||
bool OnWarning(const char* window, const char* message) override;
|
||||
bool OnException(const char* message) override;
|
||||
bool OnPrintf(const char* window, const char* message) override;
|
||||
bool OnOutput(const char* window, const char* message) override;
|
||||
|
||||
// AZ::TickBus::Handler overrides ...
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
int GetTickOrder() override;
|
||||
|
||||
private:
|
||||
void QueueMessageCall(AZStd::function<void()> messageCall);
|
||||
void FlushMessageCalls();
|
||||
|
||||
AZStd::list<AZStd::function<void()>> m_messageCalls;
|
||||
AZStd::mutex m_messageCallsLock;
|
||||
};
|
||||
|
||||
TraceMessageBusHandler::TraceMessageBusHandler()
|
||||
{
|
||||
m_events.resize(FN_MAX);
|
||||
|
||||
SetEvent(&TraceMessageBusHandler::OnPreAssert, m_functionNames[FN_OnPreAssert]);
|
||||
SetEvent(&TraceMessageBusHandler::OnPreError, m_functionNames[FN_OnPreError]);
|
||||
SetEvent(&TraceMessageBusHandler::OnPreWarning, m_functionNames[FN_OnPreWarning]);
|
||||
SetEvent(&TraceMessageBusHandler::OnAssert, m_functionNames[FN_OnAssert]);
|
||||
SetEvent(&TraceMessageBusHandler::OnError, m_functionNames[FN_OnError]);
|
||||
SetEvent(&TraceMessageBusHandler::OnWarning, m_functionNames[FN_OnWarning]);
|
||||
SetEvent(&TraceMessageBusHandler::OnException, m_functionNames[FN_OnException]);
|
||||
SetEvent(&TraceMessageBusHandler::OnPrintf, m_functionNames[FN_OnPrintf]);
|
||||
SetEvent(&TraceMessageBusHandler::OnOutput, m_functionNames[FN_OnOutput]);
|
||||
}
|
||||
|
||||
int TraceMessageBusHandler::GetFunctionIndex(const char* functionName) const
|
||||
{
|
||||
for (int i = 0; i < FN_MAX; ++i)
|
||||
{
|
||||
for (int i = 0; i < FN_MAX; ++i)
|
||||
if (azstricmp(functionName, m_functionNames[i]) == 0)
|
||||
{
|
||||
if (azstricmp(functionName, m_functionNames[i]) == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TraceMessageBusHandler::Disconnect()
|
||||
void TraceMessageBusHandler::Disconnect()
|
||||
{
|
||||
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
bool TraceMessageBusHandler::Connect(AZ::BehaviorValueParameter* id)
|
||||
{
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
return AZ::Internal::EBusConnector<AZ::Debug::TraceMessageBus::Handler>::Connect(this, id);
|
||||
}
|
||||
|
||||
bool TraceMessageBusHandler::IsConnected()
|
||||
{
|
||||
return AZ::Internal::EBusConnector<AZ::Debug::TraceMessageBus::Handler>::IsConnected(this);
|
||||
}
|
||||
|
||||
bool TraceMessageBusHandler::IsConnectedId(AZ::BehaviorValueParameter* id)
|
||||
{
|
||||
return AZ::Internal::EBusConnector<AZ::Debug::TraceMessageBus::Handler>::IsConnectedId(this, id);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TraceMessageBusHandler Implementation
|
||||
inline bool TraceMessageBusHandler::OnPreAssert(const char* fileName, int line, const char* func, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, fileNameString = AZStd::string(fileName), line, funcString = AZStd::string(func), messageString = AZStd::string(message)]()
|
||||
{
|
||||
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
Call(FN_OnPreAssert, fileNameString.c_str(), line, funcString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TraceMessageBusHandler::Connect(AZ::BehaviorValueParameter* id)
|
||||
inline bool TraceMessageBusHandler::OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), fileNameString = AZStd::string(fileName), line, funcString = AZStd::string(func), messageString = AZStd::string(message)]()
|
||||
{
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
return AZ::Internal::EBusConnector<AZ::Debug::TraceMessageBus::Handler>::Connect(this, id);
|
||||
}
|
||||
Call(FN_OnPreError, windowString.c_str(), fileNameString.c_str(), line, funcString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TraceMessageBusHandler::IsConnected()
|
||||
inline bool TraceMessageBusHandler::OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), fileNameString = AZStd::string(fileName), line, funcString = AZStd::string(func), messageString = AZStd::string(message)]()
|
||||
{
|
||||
return AZ::Internal::EBusConnector<AZ::Debug::TraceMessageBus::Handler>::IsConnected(this);
|
||||
}
|
||||
return Call(FN_OnPreWarning, windowString.c_str(), fileNameString.c_str(), line, funcString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TraceMessageBusHandler::IsConnectedId(AZ::BehaviorValueParameter* id)
|
||||
inline bool TraceMessageBusHandler::OnAssert(const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, messageString = AZStd::string(message)]()
|
||||
{
|
||||
return AZ::Internal::EBusConnector<AZ::Debug::TraceMessageBus::Handler>::IsConnectedId(this, id);
|
||||
}
|
||||
return Call(FN_OnAssert, messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TraceMessageBusHandler Implementation
|
||||
inline bool TraceMessageBusHandler::OnPreAssert(const char* fileName, int line, const char* func, const char* message)
|
||||
inline bool TraceMessageBusHandler::OnError(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, fileNameString = AZStd::string(fileName), line, funcString = AZStd::string(func), messageString = AZStd::string(message)]()
|
||||
{
|
||||
Call(FN_OnPreAssert, fileNameString.c_str(), line, funcString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return Call(FN_OnError, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message)
|
||||
inline bool TraceMessageBusHandler::OnWarning(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), fileNameString = AZStd::string(fileName), line, funcString = AZStd::string(func), messageString = AZStd::string(message)]()
|
||||
{
|
||||
Call(FN_OnPreError, windowString.c_str(), fileNameString.c_str(), line, funcString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return Call(FN_OnWarning, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message)
|
||||
inline bool TraceMessageBusHandler::OnException(const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, messageString = AZStd::string(message)]()
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), fileNameString = AZStd::string(fileName), line, funcString = AZStd::string(func), messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnPreWarning, windowString.c_str(), fileNameString.c_str(), line, funcString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return Call(FN_OnException, messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnAssert(const char* message)
|
||||
inline bool TraceMessageBusHandler::OnPrintf(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnAssert, messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return Call(FN_OnPrintf, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnError(const char* window, const char* message)
|
||||
inline bool TraceMessageBusHandler::OnOutput(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnError, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return Call(FN_OnOutput, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnWarning(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnWarning, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
void TraceMessageBusHandler::OnTick(
|
||||
[[maybe_unused]] float deltaTime,
|
||||
[[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
FlushMessageCalls();
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnException(const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnException, messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
int TraceMessageBusHandler::GetTickOrder()
|
||||
{
|
||||
return AZ::TICK_LAST;
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnPrintf(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnPrintf, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
void TraceMessageBusHandler::QueueMessageCall(AZStd::function<void()> messageCall)
|
||||
{
|
||||
AZStd::lock_guard<decltype(m_messageCallsLock)> lock(m_messageCallsLock);
|
||||
m_messageCalls.emplace_back(messageCall);
|
||||
}
|
||||
|
||||
inline bool TraceMessageBusHandler::OnOutput(const char* window, const char* message)
|
||||
{
|
||||
QueueMessageCall(
|
||||
[this, windowString = AZStd::string(window), messageString = AZStd::string(message)]()
|
||||
{
|
||||
return Call(FN_OnOutput, windowString.c_str(), messageString.c_str());
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
void TraceMessageBusHandler::OnTick(
|
||||
[[maybe_unused]] float deltaTime,
|
||||
[[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
FlushMessageCalls();
|
||||
}
|
||||
|
||||
int TraceMessageBusHandler::GetTickOrder()
|
||||
{
|
||||
return AZ::TICK_LAST;
|
||||
}
|
||||
|
||||
void TraceMessageBusHandler::QueueMessageCall(AZStd::function<void()> messageCall)
|
||||
void TraceMessageBusHandler::FlushMessageCalls()
|
||||
{
|
||||
AZStd::list<AZStd::function<void()>> messageCalls;
|
||||
{
|
||||
AZStd::lock_guard<decltype(m_messageCallsLock)> lock(m_messageCallsLock);
|
||||
m_messageCalls.push_back(messageCall);
|
||||
m_messageCalls.swap(messageCalls); // Move calls to a new list to release the lock as soon as possible
|
||||
}
|
||||
|
||||
void TraceMessageBusHandler::FlushMessageCalls()
|
||||
for (auto& messageCall : messageCalls)
|
||||
{
|
||||
AZStd::list<AZStd::function<void()>> messageCalls;
|
||||
{
|
||||
AZStd::lock_guard<decltype(m_messageCallsLock)> lock(m_messageCallsLock);
|
||||
m_messageCalls.swap(messageCalls); // Move calls to a new list to release the lock as soon as possible
|
||||
}
|
||||
|
||||
for (auto& messageCall : messageCalls)
|
||||
{
|
||||
messageCall();
|
||||
}
|
||||
}
|
||||
|
||||
void TraceReflect(ReflectContext* context)
|
||||
{
|
||||
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->EBus<AZ::Debug::TraceMessageBus>("TraceMessageBus")
|
||||
->Attribute(AZ::Script::Attributes::Module, "debug")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Handler<TraceMessageBusHandler>()
|
||||
;
|
||||
}
|
||||
messageCall();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceReflect(ReflectContext* context)
|
||||
{
|
||||
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->EBus<AZ::Debug::TraceMessageBus>("TraceMessageBus")
|
||||
->Attribute(AZ::Script::Attributes::Module, "debug")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Handler<TraceMessageBusHandler>()
|
||||
;
|
||||
}
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
|
||||
@@ -14,291 +14,288 @@
|
||||
#include <AzCore/Math/Crc.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
class DrillerManagerImpl
|
||||
: public DrillerManager
|
||||
{
|
||||
class DrillerManagerImpl
|
||||
: public DrillerManager
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(DrillerManagerImpl, OSAllocator, 0);
|
||||
|
||||
using SessionListType = forward_list<DrillerSession>::type;
|
||||
SessionListType m_sessions;
|
||||
using DrillerArrayType = vector<Driller*>::type;
|
||||
DrillerArrayType m_drillers;
|
||||
|
||||
~DrillerManagerImpl() override;
|
||||
|
||||
void Register(Driller* factory) override;
|
||||
void Unregister(Driller* factory) override;
|
||||
|
||||
void FrameUpdate() override;
|
||||
|
||||
DrillerSession* Start(DrillerOutputStream& output, const DrillerListType& drillerList, int numFrames = -1) override;
|
||||
void Stop(DrillerSession* session) override;
|
||||
|
||||
int GetNumDrillers() const override { return static_cast<int>(m_drillers.size()); }
|
||||
Driller* GetDriller(int index) override { return m_drillers[index]; }
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Driller
|
||||
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
AZ::u32 Driller::GetId() const
|
||||
{
|
||||
return AZ::Crc32(GetName());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Driller Manager
|
||||
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
DrillerManager* DrillerManager::Create(/*const Descriptor& desc*/)
|
||||
{
|
||||
const bool createAllocator = !AZ::AllocatorInstance<OSAllocator>::IsReady();
|
||||
if (createAllocator)
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(DrillerManagerImpl, OSAllocator, 0);
|
||||
|
||||
typedef forward_list<DrillerSession>::type SessionListType;
|
||||
SessionListType m_sessions;
|
||||
typedef vector<Driller*>::type DrillerArrayType;
|
||||
DrillerArrayType m_drillers;
|
||||
|
||||
~DrillerManagerImpl() override;
|
||||
|
||||
void Register(Driller* factory) override;
|
||||
void Unregister(Driller* factory) override;
|
||||
|
||||
void FrameUpdate() override;
|
||||
|
||||
DrillerSession* Start(DrillerOutputStream& output, const DrillerListType& drillerList, int numFrames = -1) override;
|
||||
void Stop(DrillerSession* session) override;
|
||||
|
||||
int GetNumDrillers() const override { return static_cast<int>(m_drillers.size()); }
|
||||
Driller* GetDriller(int index) override { return m_drillers[index]; }
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Driller
|
||||
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
AZ::u32 Driller::GetId() const
|
||||
{
|
||||
return AZ::Crc32(GetName());
|
||||
AZ::AllocatorInstance<OSAllocator>::Create();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
DrillerManagerImpl* impl = aznew DrillerManagerImpl;
|
||||
impl->m_ownsOSAllocator = createAllocator;
|
||||
return impl;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Driller Manager
|
||||
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
DrillerManager* DrillerManager::Create(/*const Descriptor& desc*/)
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void DrillerManager::Destroy(DrillerManager* manager)
|
||||
{
|
||||
const bool allocatorCreated = manager->m_ownsOSAllocator;
|
||||
delete manager;
|
||||
if (allocatorCreated)
|
||||
{
|
||||
const bool createAllocator = !AZ::AllocatorInstance<OSAllocator>::IsReady();
|
||||
if (createAllocator)
|
||||
{
|
||||
AZ::AllocatorInstance<OSAllocator>::Create();
|
||||
}
|
||||
AZ::AllocatorInstance<OSAllocator>::Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
DrillerManagerImpl* impl = aznew DrillerManagerImpl;
|
||||
impl->m_ownsOSAllocator = createAllocator;
|
||||
return impl;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DrillerManagerImpl
|
||||
|
||||
//=========================================================================
|
||||
// ~DrillerManagerImpl
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
DrillerManagerImpl::~DrillerManagerImpl()
|
||||
{
|
||||
while (!m_sessions.empty())
|
||||
{
|
||||
Stop(&m_sessions.front());
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void DrillerManager::Destroy(DrillerManager* manager)
|
||||
while (!m_drillers.empty())
|
||||
{
|
||||
const bool allocatorCreated = manager->m_ownsOSAllocator;
|
||||
delete manager;
|
||||
if (allocatorCreated)
|
||||
{
|
||||
AZ::AllocatorInstance<OSAllocator>::Destroy();
|
||||
}
|
||||
Driller* driller = m_drillers[0];
|
||||
Unregister(driller);
|
||||
delete driller;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DrillerManagerImpl
|
||||
|
||||
//=========================================================================
|
||||
// ~DrillerManagerImpl
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
DrillerManagerImpl::~DrillerManagerImpl()
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::Register(Driller* driller)
|
||||
{
|
||||
AZ_Assert(driller, "You must provide a valid factory!");
|
||||
for (size_t i = 0; i < m_drillers.size(); ++i)
|
||||
{
|
||||
while (!m_sessions.empty())
|
||||
{
|
||||
Stop(&m_sessions.front());
|
||||
}
|
||||
|
||||
while (!m_drillers.empty())
|
||||
{
|
||||
Driller* driller = m_drillers[0];
|
||||
Unregister(driller);
|
||||
delete driller;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Register
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::Register(Driller* driller)
|
||||
{
|
||||
AZ_Assert(driller, "You must provide a valid factory!");
|
||||
for (size_t i = 0; i < m_drillers.size(); ++i)
|
||||
{
|
||||
if (m_drillers[i]->GetId() == driller->GetId())
|
||||
{
|
||||
AZ_Error("Debug", false, "Driller with id %08x has already been registered! You can't have two factory instances for the same driller type", driller->GetId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_drillers.push_back(driller);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Unregister
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::Unregister(Driller* driller)
|
||||
{
|
||||
AZ_Assert(driller, "You must provide a valid factory!");
|
||||
for (DrillerArrayType::iterator iter = m_drillers.begin(); iter != m_drillers.end(); ++iter)
|
||||
{
|
||||
if ((*iter)->GetId() == driller->GetId())
|
||||
{
|
||||
m_drillers.erase(iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AZ_Error("Debug", false, "Failed to find driller factory with id %08x", driller->GetId());
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FrameUpdate
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::FrameUpdate()
|
||||
{
|
||||
if (m_sessions.empty())
|
||||
if (m_drillers[i]->GetId() == driller->GetId())
|
||||
{
|
||||
AZ_Error("Debug", false, "Driller with id %08x has already been registered! You can't have two factory instances for the same driller type", driller->GetId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_drillers.push_back(driller);
|
||||
}
|
||||
|
||||
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex()); ///< Make sure no driller is writing to the stream
|
||||
for (SessionListType::iterator sessionIter = m_sessions.begin(); sessionIter != m_sessions.end(); )
|
||||
//=========================================================================
|
||||
// Unregister
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::Unregister(Driller* driller)
|
||||
{
|
||||
AZ_Assert(driller, "You must provide a valid factory!");
|
||||
for (DrillerArrayType::iterator iter = m_drillers.begin(); iter != m_drillers.end(); ++iter)
|
||||
{
|
||||
if ((*iter)->GetId() == driller->GetId())
|
||||
{
|
||||
DrillerSession& s = *sessionIter;
|
||||
|
||||
// tick the drillers directly if they care.
|
||||
for (size_t i = 0; i < s.drillers.size(); ++i)
|
||||
{
|
||||
s.drillers[i]->Update();
|
||||
}
|
||||
|
||||
s.output->EndTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
|
||||
s.output->OnEndOfFrame();
|
||||
|
||||
s.curFrame++;
|
||||
|
||||
if (s.numFrames != -1)
|
||||
{
|
||||
if (s.curFrame == s.numFrames)
|
||||
{
|
||||
Stop(&s);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
s.output->BeginTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
s.output->Write(AZ_CRC("FrameNum", 0x85a1a919), s.curFrame);
|
||||
|
||||
++sessionIter;
|
||||
m_drillers.erase(iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Start
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
DrillerSession*
|
||||
DrillerManagerImpl::Start(DrillerOutputStream& output, const DrillerListType& drillerList, int numFrames)
|
||||
AZ_Error("Debug", false, "Failed to find driller factory with id %08x", driller->GetId());
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FrameUpdate
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::FrameUpdate()
|
||||
{
|
||||
if (m_sessions.empty())
|
||||
{
|
||||
if (drillerList.empty())
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex()); ///< Make sure no driller is writing to the stream
|
||||
for (SessionListType::iterator sessionIter = m_sessions.begin(); sessionIter != m_sessions.end(); )
|
||||
{
|
||||
DrillerSession& s = *sessionIter;
|
||||
|
||||
// tick the drillers directly if they care.
|
||||
for (size_t i = 0; i < s.drillers.size(); ++i)
|
||||
{
|
||||
return nullptr;
|
||||
s.drillers[i]->Update();
|
||||
}
|
||||
|
||||
m_sessions.push_back();
|
||||
DrillerSession& s = m_sessions.back();
|
||||
s.curFrame = 0;
|
||||
s.numFrames = numFrames;
|
||||
s.output = &output;
|
||||
s.output->EndTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
|
||||
s.output->WriteHeader(); // first write the header in the stream
|
||||
s.output->OnEndOfFrame();
|
||||
|
||||
s.output->BeginTag(AZ_CRC("StartData", 0xecf3f53f));
|
||||
s.output->Write(AZ_CRC("Platform", 0x3952d0cb), (unsigned int)g_currentPlatform);
|
||||
for (DrillerListType::const_iterator iDriller = drillerList.begin(); iDriller != drillerList.end(); ++iDriller)
|
||||
s.curFrame++;
|
||||
|
||||
if (s.numFrames != -1)
|
||||
{
|
||||
const DrillerInfo& di = *iDriller;
|
||||
s.output->BeginTag(AZ_CRC("Driller", 0xa6e1fb73));
|
||||
s.output->Write(AZ_CRC("Name", 0x5e237e06), di.id);
|
||||
for (int iParam = 0; iParam < (int)di.params.size(); ++iParam)
|
||||
if (s.curFrame == s.numFrames)
|
||||
{
|
||||
s.output->BeginTag(AZ_CRC("Param", 0xa4fa7c89));
|
||||
s.output->Write(AZ_CRC("Name", 0x5e237e06), di.params[iParam].name);
|
||||
s.output->Write(AZ_CRC("Description", 0x6de44026), di.params[iParam].desc);
|
||||
s.output->Write(AZ_CRC("Type", 0x8cde5729), di.params[iParam].type);
|
||||
s.output->Write(AZ_CRC("Value", 0x1d775834), di.params[iParam].value);
|
||||
s.output->EndTag(AZ_CRC("Param", 0xa4fa7c89));
|
||||
Stop(&s);
|
||||
continue;
|
||||
}
|
||||
s.output->EndTag(AZ_CRC("Driller", 0xa6e1fb73));
|
||||
}
|
||||
s.output->EndTag(AZ_CRC("StartData", 0xecf3f53f));
|
||||
|
||||
s.output->BeginTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
s.output->Write(AZ_CRC("FrameNum", 0x85a1a919), s.curFrame);
|
||||
|
||||
{
|
||||
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex()); ///< Make sure no driller is writing to the stream
|
||||
for (DrillerListType::const_iterator iDriller = drillerList.begin(); iDriller != drillerList.end(); ++iDriller)
|
||||
{
|
||||
Driller* driller = nullptr;
|
||||
const DrillerInfo& di = *iDriller;
|
||||
for (size_t iDesc = 0; iDesc < m_drillers.size(); ++iDesc)
|
||||
{
|
||||
if (m_drillers[iDesc]->GetId() == di.id)
|
||||
{
|
||||
driller = m_drillers[iDesc];
|
||||
AZ_Assert(driller->m_output == nullptr, "Driller with id %08x is already have an output stream %p (currently we support only 1 at a time)", di.id, driller->m_output);
|
||||
driller->m_output = &output;
|
||||
driller->Start(di.params.data(), static_cast<unsigned int>(di.params.size()));
|
||||
s.drillers.push_back(driller);
|
||||
break;
|
||||
}
|
||||
}
|
||||
AZ_Warning("Driller", driller != nullptr, "We can't start a driller with id %d!", di.id);
|
||||
}
|
||||
}
|
||||
return &s;
|
||||
++sessionIter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Stop
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::Stop(DrillerSession* session)
|
||||
//=========================================================================
|
||||
// Start
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
DrillerSession*
|
||||
DrillerManagerImpl::Start(DrillerOutputStream& output, const DrillerListType& drillerList, int numFrames)
|
||||
{
|
||||
if (drillerList.empty())
|
||||
{
|
||||
SessionListType::iterator iter;
|
||||
for (iter = m_sessions.begin(); iter != m_sessions.end(); ++iter)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_sessions.push_back();
|
||||
DrillerSession& s = m_sessions.back();
|
||||
s.curFrame = 0;
|
||||
s.numFrames = numFrames;
|
||||
s.output = &output;
|
||||
|
||||
s.output->WriteHeader(); // first write the header in the stream
|
||||
|
||||
s.output->BeginTag(AZ_CRC("StartData", 0xecf3f53f));
|
||||
s.output->Write(AZ_CRC("Platform", 0x3952d0cb), (unsigned int)g_currentPlatform);
|
||||
for (DrillerListType::const_iterator iDriller = drillerList.begin(); iDriller != drillerList.end(); ++iDriller)
|
||||
{
|
||||
const DrillerInfo& di = *iDriller;
|
||||
s.output->BeginTag(AZ_CRC("Driller", 0xa6e1fb73));
|
||||
s.output->Write(AZ_CRC("Name", 0x5e237e06), di.id);
|
||||
for (int iParam = 0; iParam < (int)di.params.size(); ++iParam)
|
||||
{
|
||||
if (&*iter == session)
|
||||
{
|
||||
break;
|
||||
}
|
||||
s.output->BeginTag(AZ_CRC("Param", 0xa4fa7c89));
|
||||
s.output->Write(AZ_CRC("Name", 0x5e237e06), di.params[iParam].name);
|
||||
s.output->Write(AZ_CRC("Description", 0x6de44026), di.params[iParam].desc);
|
||||
s.output->Write(AZ_CRC("Type", 0x8cde5729), di.params[iParam].type);
|
||||
s.output->Write(AZ_CRC("Value", 0x1d775834), di.params[iParam].value);
|
||||
s.output->EndTag(AZ_CRC("Param", 0xa4fa7c89));
|
||||
}
|
||||
s.output->EndTag(AZ_CRC("Driller", 0xa6e1fb73));
|
||||
}
|
||||
s.output->EndTag(AZ_CRC("StartData", 0xecf3f53f));
|
||||
|
||||
AZ_Assert(iter != m_sessions.end(), "We did not find session ID 0x%08x in the list!", session);
|
||||
if (iter != m_sessions.end())
|
||||
s.output->BeginTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
s.output->Write(AZ_CRC("FrameNum", 0x85a1a919), s.curFrame);
|
||||
|
||||
{
|
||||
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex()); ///< Make sure no driller is writing to the stream
|
||||
for (DrillerListType::const_iterator iDriller = drillerList.begin(); iDriller != drillerList.end(); ++iDriller)
|
||||
{
|
||||
DrillerSession& s = *session;
|
||||
|
||||
Driller* driller = nullptr;
|
||||
const DrillerInfo& di = *iDriller;
|
||||
for (size_t iDesc = 0; iDesc < m_drillers.size(); ++iDesc)
|
||||
{
|
||||
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex());
|
||||
for (size_t i = 0; i < s.drillers.size(); ++i)
|
||||
if (m_drillers[iDesc]->GetId() == di.id)
|
||||
{
|
||||
s.drillers[i]->Stop();
|
||||
s.drillers[i]->m_output = nullptr;
|
||||
driller = m_drillers[iDesc];
|
||||
AZ_Assert(driller->m_output == nullptr, "Driller with id %08x is already have an output stream %p (currently we support only 1 at a time)", di.id, driller->m_output);
|
||||
driller->m_output = &output;
|
||||
driller->Start(di.params.data(), static_cast<unsigned int>(di.params.size()));
|
||||
s.drillers.push_back(driller);
|
||||
break;
|
||||
}
|
||||
}
|
||||
s.output->EndTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
m_sessions.erase(iter);
|
||||
AZ_Warning("Driller", driller != nullptr, "We can't start a driller with id %d!", di.id);
|
||||
}
|
||||
}
|
||||
} // namespace Debug
|
||||
} // namespace AZ
|
||||
return &s;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Stop
|
||||
// [3/17/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
DrillerManagerImpl::Stop(DrillerSession* session)
|
||||
{
|
||||
SessionListType::iterator iter;
|
||||
for (iter = m_sessions.begin(); iter != m_sessions.end(); ++iter)
|
||||
{
|
||||
if (&*iter == session)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AZ_Assert(iter != m_sessions.end(), "We did not find session ID 0x%08x in the list!", session);
|
||||
if (iter != m_sessions.end())
|
||||
{
|
||||
DrillerSession& s = *session;
|
||||
|
||||
{
|
||||
AZStd::lock_guard<DrillerEBusMutex::MutexType> lock(DrillerEBusMutex::GetMutex());
|
||||
for (size_t i = 0; i < s.drillers.size(); ++i)
|
||||
{
|
||||
s.drillers[i]->Stop();
|
||||
s.drillers[i]->m_output = nullptr;
|
||||
}
|
||||
}
|
||||
s.output->EndTag(AZ_CRC("Frame", 0xb5f83ccd));
|
||||
m_sessions.erase(iter);
|
||||
}
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
|
||||
@@ -12,57 +12,54 @@
|
||||
#include <AzCore/Module/Environment.h>
|
||||
#include <AzCore/std/parallel/mutex.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Globals
|
||||
// We need to synchronize all driller evens, so we have proper order, and access to the data
|
||||
// We use a global mutex which should be used for all driller operations.
|
||||
// The mutex is held in an environment variable so it works across DLLs.
|
||||
EnvironmentVariable<AZStd::recursive_mutex> s_drillerGlobalMutex;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// lock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
void DrillerEBusMutex::lock()
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Globals
|
||||
// We need to synchronize all driller evens, so we have proper order, and access to the data
|
||||
// We use a global mutex which should be used for all driller operations.
|
||||
// The mutex is held in an environment variable so it works across DLLs.
|
||||
EnvironmentVariable<AZStd::recursive_mutex> s_drillerGlobalMutex;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
GetMutex().lock();
|
||||
}
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// lock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
void DrillerEBusMutex::lock()
|
||||
{
|
||||
GetMutex().lock();
|
||||
}
|
||||
//=========================================================================
|
||||
// try_lock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
bool DrillerEBusMutex::try_lock()
|
||||
{
|
||||
return GetMutex().try_lock();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// try_lock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
bool DrillerEBusMutex::try_lock()
|
||||
{
|
||||
return GetMutex().try_lock();
|
||||
}
|
||||
//=========================================================================
|
||||
// unlock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
void DrillerEBusMutex::unlock()
|
||||
{
|
||||
GetMutex().unlock();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// unlock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
void DrillerEBusMutex::unlock()
|
||||
//=========================================================================
|
||||
// unlock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
AZStd::recursive_mutex& DrillerEBusMutex::GetMutex()
|
||||
{
|
||||
if (!s_drillerGlobalMutex)
|
||||
{
|
||||
GetMutex().unlock();
|
||||
s_drillerGlobalMutex = Environment::CreateVariable<AZStd::recursive_mutex>(AZ_FUNCTION_SIGNATURE);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// unlock
|
||||
// [4/11/2011]
|
||||
//=========================================================================
|
||||
AZStd::recursive_mutex& DrillerEBusMutex::GetMutex()
|
||||
{
|
||||
if (!s_drillerGlobalMutex)
|
||||
{
|
||||
s_drillerGlobalMutex = Environment::CreateVariable<AZStd::recursive_mutex>(AZ_FUNCTION_SIGNATURE);
|
||||
}
|
||||
return *s_drillerGlobalMutex;
|
||||
}
|
||||
} // namespace Debug
|
||||
} // namespace AZ
|
||||
return *s_drillerGlobalMutex;
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -60,7 +60,7 @@ namespace AZ
|
||||
|
||||
void EventSchedulerSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
TimeMs startTime = GetElapsedTimeMs();
|
||||
TimeMs startTime = AZ::GetElapsedTimeMs();
|
||||
bool usingTimeslice = bg_maxScheduledEventProcessTimeMs != TimeMs{ 0 };
|
||||
|
||||
while (!m_queue.empty())
|
||||
@@ -76,7 +76,7 @@ namespace AZ
|
||||
|
||||
while (!m_pendingQueue.empty())
|
||||
{
|
||||
if (usingTimeslice && (GetElapsedTimeMs() - startTime > bg_maxScheduledEventProcessTimeMs))
|
||||
if (usingTimeslice && (AZ::GetElapsedTimeMs() - startTime > bg_maxScheduledEventProcessTimeMs))
|
||||
{
|
||||
AZLOG_WARN("Failed to trigger all pending scheduled events, %u events remain on the pending queue", aznumeric_cast<uint32_t>(m_pendingQueue.size()));
|
||||
break;
|
||||
@@ -103,7 +103,7 @@ namespace AZ
|
||||
durationMs = TimeMs{ 0 };
|
||||
}
|
||||
|
||||
TimeMs currentMilliseconds = GetElapsedTimeMs();
|
||||
TimeMs currentMilliseconds = AZ::GetElapsedTimeMs();
|
||||
if (timedEvent->m_handle == nullptr)
|
||||
{
|
||||
timedEvent->m_handle = AllocateHandle();
|
||||
@@ -122,7 +122,7 @@ namespace AZ
|
||||
durationMs = TimeMs{ 0 };
|
||||
}
|
||||
|
||||
TimeMs currentMilliseconds = GetElapsedTimeMs();
|
||||
TimeMs currentMilliseconds = AZ::GetElapsedTimeMs();
|
||||
ScheduledEvent* timedEvent = AllocateManagedEvent(callback, eventName);
|
||||
const bool ownsScheduledEvent = true;
|
||||
*(timedEvent->m_handle) = ScheduledEventHandle(TimeMs(currentMilliseconds + durationMs), durationMs, timedEvent, ownsScheduledEvent);
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace AZ
|
||||
|
||||
TimeMs ScheduledEvent::TimeInQueueMs() const
|
||||
{
|
||||
return GetElapsedTimeMs() - m_timeInserted;
|
||||
return AZ::GetElapsedTimeMs() - m_timeInserted;
|
||||
}
|
||||
|
||||
TimeMs ScheduledEvent::RemainingTimeInQueueMs() const
|
||||
|
||||
@@ -8,38 +8,35 @@
|
||||
|
||||
#include <AzCore/IO/CompressionBus.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
CompressionInfo::CompressionInfo(CompressionInfo&& rhs)
|
||||
{
|
||||
CompressionInfo::CompressionInfo(CompressionInfo&& rhs)
|
||||
{
|
||||
*this = AZStd::move(rhs);
|
||||
}
|
||||
*this = AZStd::move(rhs);
|
||||
}
|
||||
|
||||
CompressionInfo& CompressionInfo::operator=(CompressionInfo&& rhs)
|
||||
{
|
||||
m_decompressor = AZStd::move(rhs.m_decompressor);
|
||||
m_archiveFilename = AZStd::move(rhs.m_archiveFilename);
|
||||
m_compressionTag = rhs.m_compressionTag;
|
||||
m_offset = rhs.m_offset;
|
||||
m_compressedSize = rhs.m_compressedSize;
|
||||
m_uncompressedSize = rhs.m_uncompressedSize;
|
||||
m_conflictResolution = rhs.m_conflictResolution;
|
||||
m_isCompressed = rhs.m_isCompressed;
|
||||
m_isSharedPak = rhs.m_isSharedPak;
|
||||
CompressionInfo& CompressionInfo::operator=(CompressionInfo&& rhs)
|
||||
{
|
||||
m_decompressor = AZStd::move(rhs.m_decompressor);
|
||||
m_archiveFilename = AZStd::move(rhs.m_archiveFilename);
|
||||
m_compressionTag = rhs.m_compressionTag;
|
||||
m_offset = rhs.m_offset;
|
||||
m_compressedSize = rhs.m_compressedSize;
|
||||
m_uncompressedSize = rhs.m_uncompressedSize;
|
||||
m_conflictResolution = rhs.m_conflictResolution;
|
||||
m_isCompressed = rhs.m_isCompressed;
|
||||
m_isSharedPak = rhs.m_isSharedPak;
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace CompressionUtils
|
||||
namespace CompressionUtils
|
||||
{
|
||||
bool FindCompressionInfo(CompressionInfo& info, const AZStd::string_view filename)
|
||||
{
|
||||
bool FindCompressionInfo(CompressionInfo& info, const AZStd::string_view filename)
|
||||
{
|
||||
bool result = false;
|
||||
CompressionBus::Broadcast(&CompressionBus::Events::FindCompressionInfo, result, info, filename);
|
||||
return result;
|
||||
}
|
||||
bool result = false;
|
||||
CompressionBus::Broadcast(&CompressionBus::Events::FindCompressionInfo, result, info, filename);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -10,32 +10,29 @@
|
||||
#include <AzCore/IO/Compressor.h>
|
||||
#include <AzCore/IO/CompressorStream.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
//=========================================================================
|
||||
// WriteHeaderAndData
|
||||
// [12/13/2012]
|
||||
//=========================================================================
|
||||
bool Compressor::WriteHeaderAndData(CompressorStream* compressorStream)
|
||||
{
|
||||
//=========================================================================
|
||||
// WriteHeaderAndData
|
||||
// [12/13/2012]
|
||||
//=========================================================================
|
||||
bool Compressor::WriteHeaderAndData(CompressorStream* compressorStream)
|
||||
AZ_Assert(compressorStream->CanWrite(), "Stream is not open for write!");
|
||||
AZ_Assert(compressorStream->GetCompressorData(), "Stream doesn't have attached compressor, call WriteCompressed first!");
|
||||
AZ_Assert(compressorStream->GetCompressorData()->m_compressor == this, "Invalid compressor data! Data belongs to a different compressor");
|
||||
CompressorHeader header;
|
||||
header.SetAZCS();
|
||||
header.m_compressorId = GetTypeId();
|
||||
header.m_uncompressedSize = compressorStream->GetCompressorData()->m_uncompressedSize;
|
||||
AZStd::endian_swap(header.m_compressorId);
|
||||
AZStd::endian_swap(header.m_uncompressedSize);
|
||||
GenericStream* baseStream = compressorStream->GetWrappedStream();
|
||||
if (baseStream->WriteAtOffset(sizeof(CompressorHeader), &header, 0U) == sizeof(CompressorHeader))
|
||||
{
|
||||
AZ_Assert(compressorStream->CanWrite(), "Stream is not open for write!");
|
||||
AZ_Assert(compressorStream->GetCompressorData(), "Stream doesn't have attached compressor, call WriteCompressed first!");
|
||||
AZ_Assert(compressorStream->GetCompressorData()->m_compressor == this, "Invalid compressor data! Data belongs to a different compressor");
|
||||
CompressorHeader header;
|
||||
header.SetAZCS();
|
||||
header.m_compressorId = GetTypeId();
|
||||
header.m_uncompressedSize = compressorStream->GetCompressorData()->m_uncompressedSize;
|
||||
AZStd::endian_swap(header.m_compressorId);
|
||||
AZStd::endian_swap(header.m_uncompressedSize);
|
||||
GenericStream* baseStream = compressorStream->GetWrappedStream();
|
||||
if (baseStream->WriteAtOffset(sizeof(CompressorHeader), &header, 0U) == sizeof(CompressorHeader))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -5,74 +5,67 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZCORE_IO_COMPRESSOR_H
|
||||
#define AZCORE_IO_COMPRESSOR_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
class CompressorStream;
|
||||
|
||||
/**
|
||||
* Compressor/Decompressor base interface.
|
||||
* Used for all stream compressors.
|
||||
*/
|
||||
class Compressor
|
||||
{
|
||||
class CompressorStream;
|
||||
public:
|
||||
typedef AZ::u64 SizeType;
|
||||
static const int m_maxHeaderSize = 4096; /// When we open a stream to check if it's compressed we read the first m_maxHeaderSize bytes.
|
||||
|
||||
/**
|
||||
* Compressor/Decompressor base interface.
|
||||
* Used for all stream compressors.
|
||||
*/
|
||||
class Compressor
|
||||
{
|
||||
public:
|
||||
typedef AZ::u64 SizeType;
|
||||
static const int m_maxHeaderSize = 4096; /// When we open a stream to check if it's compressed we read the first m_maxHeaderSize bytes.
|
||||
virtual ~Compressor() {}
|
||||
/// Return compressor type id.
|
||||
virtual AZ::u32 GetTypeId() const = 0;
|
||||
/// Called when we open a stream to Read for the first time. Data contains the first. dataSize <= m_maxHeaderSize.
|
||||
virtual bool ReadHeaderAndData(CompressorStream* stream, AZ::u8* data, unsigned int dataSize) = 0;
|
||||
/// Called when we are about to start writing to a compressed stream. (Must be called first to write compressor header)
|
||||
virtual bool WriteHeaderAndData(CompressorStream* stream);
|
||||
/// Forwarded function from the Device when we from a compressed stream.
|
||||
virtual SizeType Read(CompressorStream* stream, SizeType byteSize, SizeType offset, void* buffer) = 0;
|
||||
/// Forwarded function from the Device when we write to a compressed stream.
|
||||
virtual SizeType Write(CompressorStream* stream, SizeType byteSize, const void* data, SizeType offset = SizeType(-1)) = 0;
|
||||
/// Write a seek point.
|
||||
virtual bool WriteSeekPoint(CompressorStream* stream) { (void)stream; return false; }
|
||||
/// Initializes Compressor for writing data.
|
||||
virtual bool StartCompressor(CompressorStream* stream, int compressionLevel, SizeType autoSeekDataSize) { (void)stream; (void)compressionLevel; (void)autoSeekDataSize; return false; }
|
||||
/// Called just before we close the stream. All compression data will be flushed and finalized. (You can't add data afterwards).
|
||||
virtual bool Close(CompressorStream* stream) = 0;
|
||||
};
|
||||
|
||||
virtual ~Compressor() {}
|
||||
/// Return compressor type id.
|
||||
virtual AZ::u32 GetTypeId() const = 0;
|
||||
/// Called when we open a stream to Read for the first time. Data contains the first. dataSize <= m_maxHeaderSize.
|
||||
virtual bool ReadHeaderAndData(CompressorStream* stream, AZ::u8* data, unsigned int dataSize) = 0;
|
||||
/// Called when we are about to start writing to a compressed stream. (Must be called first to write compressor header)
|
||||
virtual bool WriteHeaderAndData(CompressorStream* stream);
|
||||
/// Forwarded function from the Device when we from a compressed stream.
|
||||
virtual SizeType Read(CompressorStream* stream, SizeType byteSize, SizeType offset, void* buffer) = 0;
|
||||
/// Forwarded function from the Device when we write to a compressed stream.
|
||||
virtual SizeType Write(CompressorStream* stream, SizeType byteSize, const void* data, SizeType offset = SizeType(-1)) = 0;
|
||||
/// Write a seek point.
|
||||
virtual bool WriteSeekPoint(CompressorStream* stream) { (void)stream; return false; }
|
||||
/// Initializes Compressor for writing data.
|
||||
virtual bool StartCompressor(CompressorStream* stream, int compressionLevel, SizeType autoSeekDataSize) { (void)stream; (void)compressionLevel; (void)autoSeekDataSize; return false; }
|
||||
/// Called just before we close the stream. All compression data will be flushed and finalized. (You can't add data afterwards).
|
||||
virtual bool Close(CompressorStream* stream) = 0;
|
||||
};
|
||||
/**
|
||||
* Base compressor data assigned for all compressors.
|
||||
*/
|
||||
class CompressorData
|
||||
{
|
||||
public:
|
||||
virtual ~CompressorData() {}
|
||||
|
||||
/**
|
||||
* Base compressor data assigned for all compressors.
|
||||
*/
|
||||
class CompressorData
|
||||
{
|
||||
public:
|
||||
virtual ~CompressorData() {}
|
||||
Compressor* m_compressor;
|
||||
AZ::u64 m_uncompressedSize;
|
||||
};
|
||||
|
||||
Compressor* m_compressor;
|
||||
AZ::u64 m_uncompressedSize;
|
||||
};
|
||||
/**
|
||||
* All data is stored in network order (big endian).
|
||||
*/
|
||||
struct CompressorHeader
|
||||
{
|
||||
CompressorHeader() { m_azcs[0] = 0; m_azcs[1] = 0; m_azcs[2] = 0; m_azcs[3] = 0; }
|
||||
|
||||
/**
|
||||
* All data is stored in network order (big endian).
|
||||
*/
|
||||
struct CompressorHeader
|
||||
{
|
||||
CompressorHeader() { m_azcs[0] = 0; m_azcs[1] = 0; m_azcs[2] = 0; m_azcs[3] = 0; }
|
||||
bool IsValid() const { return (m_azcs[0] == 'A' && m_azcs[1] == 'Z' && m_azcs[2] == 'C' && m_azcs[3] == 'S'); }
|
||||
void SetAZCS() { m_azcs[0] = 'A'; m_azcs[1] = 'Z'; m_azcs[2] = 'C'; m_azcs[3] = 'S'; }
|
||||
|
||||
inline bool IsValid() const { return (m_azcs[0] == 'A' && m_azcs[1] == 'Z' && m_azcs[2] == 'C' && m_azcs[3] == 'S'); }
|
||||
void SetAZCS() { m_azcs[0] = 'A'; m_azcs[1] = 'Z'; m_azcs[2] = 'C'; m_azcs[3] = 'S'; }
|
||||
|
||||
char m_azcs[4]; ///< String contains 'AZCS' AmaZon Compressed Stream
|
||||
AZ::u32 m_compressorId; ///< Compression method.
|
||||
AZ::u64 m_uncompressedSize; ///< Uncompressed file size.
|
||||
};
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
|
||||
#endif // AZCORE_IO_COMPRESSOR_H
|
||||
#pragma once
|
||||
char m_azcs[4]; ///< String contains 'AZCS' AmaZon Compressed Stream
|
||||
AZ::u32 m_compressorId; ///< Compression method.
|
||||
AZ::u64 m_uncompressedSize; ///< Uncompressed file size.
|
||||
};
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace IO
|
||||
namespace AZ::IO
|
||||
{
|
||||
/*!
|
||||
\brief Constructs a compressor stream using the supplied filename and OpenFlags to open a file on disk
|
||||
@@ -300,7 +298,4 @@ Compressor* CompressorStream::CreateCompressor(AZ::u32 compressorId)
|
||||
return m_compressor.get();
|
||||
}
|
||||
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
|
||||
|
||||
} // namespace AZ::IO
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,478 +14,475 @@
|
||||
#include <AzCore/Math/MathUtils.h>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
CompressorZStd::CompressorZStd(unsigned int decompressionCachePerStream, unsigned int dataBufferSize)
|
||||
: m_compressedDataBufferSize(dataBufferSize)
|
||||
, m_decompressionCachePerStream(decompressionCachePerStream)
|
||||
|
||||
{
|
||||
CompressorZStd::CompressorZStd(unsigned int decompressionCachePerStream, unsigned int dataBufferSize)
|
||||
: m_compressedDataBufferSize(dataBufferSize)
|
||||
, m_decompressionCachePerStream(decompressionCachePerStream)
|
||||
AZ_Assert((dataBufferSize % (32 * 1024)) == 0, "Data buffer size %d must be multiple of 32 KB.", dataBufferSize);
|
||||
AZ_Assert((decompressionCachePerStream % (32 * 1024)) == 0, "Decompress cache size %d must be multiple of 32 KB.", decompressionCachePerStream);
|
||||
}
|
||||
|
||||
CompressorZStd::~CompressorZStd()
|
||||
{
|
||||
AZ_Warning("IO", m_compressedDataBufferUseCount == 0, "CompressorZStd has it's data buffer still referenced, it means that %d compressed streams have NOT closed. Freeing data...", m_compressedDataBufferUseCount);
|
||||
while (m_compressedDataBufferUseCount)
|
||||
{
|
||||
AZ_Assert((dataBufferSize % (32 * 1024)) == 0, "Data buffer size %d must be multiple of 32 KB.", dataBufferSize);
|
||||
AZ_Assert((decompressionCachePerStream % (32 * 1024)) == 0, "Decompress cache size %d must be multiple of 32 KB.", decompressionCachePerStream);
|
||||
ReleaseDataBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
CompressorZStd::~CompressorZStd()
|
||||
AZ::u32 CompressorZStd::TypeId()
|
||||
{
|
||||
return AZ_CRC("ZStd", 0x72fd505e);
|
||||
}
|
||||
|
||||
bool CompressorZStd::ReadHeaderAndData(CompressorStream* stream, AZ::u8* data, unsigned int dataSize)
|
||||
{
|
||||
if (stream->GetCompressorData() != nullptr) // we already have compressor data
|
||||
{
|
||||
AZ_Warning("IO", m_compressedDataBufferUseCount == 0, "CompressorZStd has it's data buffer still referenced, it means that %d compressed streams have NOT closed. Freeing data...", m_compressedDataBufferUseCount);
|
||||
while (m_compressedDataBufferUseCount)
|
||||
{
|
||||
ReleaseDataBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
AZ::u32 CompressorZStd::TypeId()
|
||||
{
|
||||
return AZ_CRC("ZStd", 0x72fd505e);
|
||||
}
|
||||
|
||||
bool CompressorZStd::ReadHeaderAndData(CompressorStream* stream, AZ::u8* data, unsigned int dataSize)
|
||||
{
|
||||
if (stream->GetCompressorData() != nullptr) // we already have compressor data
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the ZStd header should be after the default compression header...
|
||||
// We should not be in this function otherwise.
|
||||
if (dataSize < sizeof(CompressorZStdHeader) + sizeof(ZStd::Header))
|
||||
{
|
||||
AZ_Error("CompressorZStd", false, "We did not read enough data, we have only %d bytes left in the buffer and we need %d.", dataSize, sizeof(CompressorZStdHeader) + sizeof(ZStd::Header));
|
||||
return false;
|
||||
}
|
||||
|
||||
AcquireDataBuffer();
|
||||
|
||||
CompressorZStdHeader* hdr = reinterpret_cast<CompressorZStdHeader*>(data);
|
||||
dataSize -= sizeof(CompressorZStdHeader);
|
||||
data += sizeof(CompressorZStdHeader);
|
||||
|
||||
AZStd::unique_ptr<CompressorZStdData> zstdData = AZStd::make_unique<CompressorZStdData>();
|
||||
zstdData->m_compressor = this;
|
||||
zstdData->m_uncompressedSize = 0;
|
||||
zstdData->m_zstdHeader = *reinterpret_cast<ZStd::Header*>(data);
|
||||
dataSize -= sizeof(zstdData->m_zstdHeader);
|
||||
data += sizeof(zstdData->m_zstdHeader);
|
||||
zstdData->m_decompressNextOffset = sizeof(CompressorHeader) + sizeof(CompressorZStdHeader) + sizeof(ZStd::Header); // start after the headers
|
||||
|
||||
AZ_Error("CompressorZStd", hdr->m_numSeekPoints > 0, "We should have at least one seek point for the entire stream.");
|
||||
|
||||
// go the end of the file and read all sync points.
|
||||
SizeType compressedFileEnd = stream->GetLength();
|
||||
if (compressedFileEnd == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
zstdData->m_seekPoints.resize(hdr->m_numSeekPoints);
|
||||
SizeType dataToRead = sizeof(CompressorZStdSeekPoint) * static_cast<SizeType>(hdr->m_numSeekPoints);
|
||||
SizeType seekPointOffset = compressedFileEnd - dataToRead;
|
||||
|
||||
if (seekPointOffset > compressedFileEnd)
|
||||
{
|
||||
AZ_Error("CompressorZStd", false, "We have an invalid archive, this is impossible.");
|
||||
return false;
|
||||
}
|
||||
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
if (baseStream->ReadAtOffset(dataToRead, zstdData->m_seekPoints.data(), seekPointOffset) != dataToRead)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_decompressionCachePerStream)
|
||||
{
|
||||
zstdData->m_decompressedCache = reinterpret_cast<unsigned char*>(azmalloc(m_decompressionCachePerStream, m_CompressedDataBufferAlignment, AZ::SystemAllocator, "CompressorZStd"));
|
||||
}
|
||||
|
||||
zstdData->m_decompressLastOffset = seekPointOffset; // set the start address of the seek points as the last valid read address for the compressed stream.
|
||||
|
||||
zstdData->m_zstd.StartDecompressor();
|
||||
|
||||
stream->SetCompressorData(zstdData.release());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompressorZStd::WriteHeaderAndData(CompressorStream* stream)
|
||||
{
|
||||
if (!Compressor::WriteHeaderAndData(stream))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CompressorZStdData* compressorData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
CompressorZStdHeader header;
|
||||
header.m_numSeekPoints = aznumeric_caster(compressorData->m_seekPoints.size());
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
if (baseStream->WriteAtOffset(sizeof(header), &header, sizeof(CompressorHeader)) == sizeof(header))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline CompressorZStd::SizeType CompressorZStd::FillFromDecompressCache(CompressorZStdData* zstdData, void*& buffer, SizeType& byteSize, SizeType& offset)
|
||||
// Read the ZStd header should be after the default compression header...
|
||||
// We should not be in this function otherwise.
|
||||
if (dataSize < sizeof(CompressorZStdHeader) + sizeof(ZStd::Header))
|
||||
{
|
||||
SizeType firstOffsetInCache = zstdData->m_decompressedCacheOffset;
|
||||
SizeType lastOffsetInCache = firstOffsetInCache + zstdData->m_decompressedCacheDataSize;
|
||||
SizeType firstDataOffset = offset;
|
||||
SizeType lastDataOffset = offset + byteSize;
|
||||
SizeType numCopied = 0;
|
||||
if (firstOffsetInCache < lastDataOffset && lastOffsetInCache >= firstDataOffset) // check if there is data in the cache
|
||||
{
|
||||
size_t copyOffsetStart = 0;
|
||||
size_t copyOffsetEnd = zstdData->m_decompressedCacheDataSize;
|
||||
|
||||
size_t bufferCopyOffset = 0;
|
||||
|
||||
if (firstOffsetInCache < firstDataOffset)
|
||||
{
|
||||
copyOffsetStart = aznumeric_caster(firstDataOffset - firstOffsetInCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
bufferCopyOffset = aznumeric_caster(firstOffsetInCache - firstDataOffset);
|
||||
}
|
||||
|
||||
if (lastOffsetInCache >= lastDataOffset)
|
||||
{
|
||||
copyOffsetEnd -= static_cast<size_t>(lastOffsetInCache - lastDataOffset);
|
||||
}
|
||||
else if (bufferCopyOffset > 0) // the cache block is in the middle of the data, we can't use it (since we need to split buffer request into 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
numCopied = copyOffsetEnd - copyOffsetStart;
|
||||
memcpy(static_cast<char*>(buffer) + bufferCopyOffset, zstdData->m_decompressedCache + copyOffsetStart, static_cast<size_t>(numCopied));
|
||||
|
||||
// adjust pointers and sizes
|
||||
byteSize -= numCopied;
|
||||
if (bufferCopyOffset == 0)
|
||||
{
|
||||
// copied in the start
|
||||
buffer = reinterpret_cast<char*>(buffer) + numCopied;
|
||||
offset += numCopied;
|
||||
}
|
||||
}
|
||||
|
||||
return numCopied;
|
||||
AZ_Error("CompressorZStd", false, "We did not read enough data, we have only %d bytes left in the buffer and we need %d.", dataSize, sizeof(CompressorZStdHeader) + sizeof(ZStd::Header));
|
||||
return false;
|
||||
}
|
||||
|
||||
inline CompressorZStd::SizeType CompressorZStd::FillCompressedBuffer(CompressorStream* stream)
|
||||
AcquireDataBuffer();
|
||||
|
||||
CompressorZStdHeader* hdr = reinterpret_cast<CompressorZStdHeader*>(data);
|
||||
dataSize -= sizeof(CompressorZStdHeader);
|
||||
data += sizeof(CompressorZStdHeader);
|
||||
|
||||
AZStd::unique_ptr<CompressorZStdData> zstdData = AZStd::make_unique<CompressorZStdData>();
|
||||
zstdData->m_compressor = this;
|
||||
zstdData->m_uncompressedSize = 0;
|
||||
zstdData->m_zstdHeader = *reinterpret_cast<ZStd::Header*>(data);
|
||||
dataSize -= sizeof(zstdData->m_zstdHeader);
|
||||
data += sizeof(zstdData->m_zstdHeader);
|
||||
zstdData->m_decompressNextOffset = sizeof(CompressorHeader) + sizeof(CompressorZStdHeader) + sizeof(ZStd::Header); // start after the headers
|
||||
|
||||
AZ_Error("CompressorZStd", hdr->m_numSeekPoints > 0, "We should have at least one seek point for the entire stream.");
|
||||
|
||||
// go the end of the file and read all sync points.
|
||||
SizeType compressedFileEnd = stream->GetLength();
|
||||
if (compressedFileEnd == 0)
|
||||
{
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
SizeType dataFromBuffer = 0;
|
||||
if (stream == m_lastReadStream) // if the buffer is filled with data from the current stream, try to reuse
|
||||
{
|
||||
if (zstdData->m_decompressNextOffset > m_lastReadStreamOffset)
|
||||
{
|
||||
SizeType offsetInCache = zstdData->m_decompressNextOffset - m_lastReadStreamOffset;
|
||||
if (offsetInCache < m_lastReadStreamSize) // last check if there is data overlap
|
||||
{
|
||||
// copy the usable part at the start of the buffer
|
||||
SizeType toMove = m_lastReadStreamSize - offsetInCache;
|
||||
memcpy(m_compressedDataBuffer, &m_compressedDataBuffer[static_cast<size_t>(offsetInCache)], static_cast<size_t>(toMove));
|
||||
dataFromBuffer += toMove;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SizeType toReadFromStream = m_compressedDataBufferSize - dataFromBuffer;
|
||||
SizeType readOffset = zstdData->m_decompressNextOffset + dataFromBuffer;
|
||||
if (readOffset + toReadFromStream > zstdData->m_decompressLastOffset)
|
||||
{
|
||||
// don't read past the end
|
||||
AZ_Assert(readOffset <= zstdData->m_decompressLastOffset, "Read offset should always be before the end of stream.");
|
||||
toReadFromStream = zstdData->m_decompressLastOffset - readOffset;
|
||||
}
|
||||
|
||||
SizeType numReadFromStream = 0;
|
||||
if (toReadFromStream) // if we did not reuse the whole buffer, read some data from the stream
|
||||
{
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
numReadFromStream = baseStream->ReadAtOffset(toReadFromStream, &m_compressedDataBuffer[static_cast<size_t>(dataFromBuffer)], readOffset);
|
||||
}
|
||||
|
||||
// update what's actually in the read data buffer.
|
||||
m_lastReadStream = stream;
|
||||
m_lastReadStreamOffset = zstdData->m_decompressNextOffset;
|
||||
m_lastReadStreamSize = dataFromBuffer + numReadFromStream;
|
||||
return m_lastReadStreamSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ZStdCompareUpper
|
||||
zstdData->m_seekPoints.resize(hdr->m_numSeekPoints);
|
||||
SizeType dataToRead = sizeof(CompressorZStdSeekPoint) * static_cast<SizeType>(hdr->m_numSeekPoints);
|
||||
SizeType seekPointOffset = compressedFileEnd - dataToRead;
|
||||
|
||||
if (seekPointOffset > compressedFileEnd)
|
||||
{
|
||||
bool operator()(const AZ::u64& offset, const CompressorZStdSeekPoint& sp) const
|
||||
{
|
||||
return offset < sp.m_uncompressedOffset;
|
||||
}
|
||||
};
|
||||
AZ_Error("CompressorZStd", false, "We have an invalid archive, this is impossible.");
|
||||
return false;
|
||||
}
|
||||
|
||||
CompressorZStd::SizeType CompressorZStd::Read(CompressorStream* stream, SizeType byteSize, SizeType offset, void* buffer)
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
if (baseStream->ReadAtOffset(dataToRead, zstdData->m_seekPoints.data(), seekPointOffset) != dataToRead)
|
||||
{
|
||||
AZ_Assert(stream->GetCompressorData(), "This stream doesn't have decompression enabled.");
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
AZ_Assert(!zstdData->m_zstd.IsCompressorStarted(), "You can't read/decompress while writing a compressed stream %s.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if the request can be finished from the decompressed cache
|
||||
SizeType numRead = FillFromDecompressCache(zstdData, buffer, byteSize, offset);
|
||||
if (byteSize == 0) // are we done
|
||||
if (m_decompressionCachePerStream)
|
||||
{
|
||||
zstdData->m_decompressedCache = reinterpret_cast<unsigned char*>(azmalloc(m_decompressionCachePerStream, m_CompressedDataBufferAlignment, AZ::SystemAllocator, "CompressorZStd"));
|
||||
}
|
||||
|
||||
zstdData->m_decompressLastOffset = seekPointOffset; // set the start address of the seek points as the last valid read address for the compressed stream.
|
||||
|
||||
zstdData->m_zstd.StartDecompressor();
|
||||
|
||||
stream->SetCompressorData(zstdData.release());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompressorZStd::WriteHeaderAndData(CompressorStream* stream)
|
||||
{
|
||||
if (!Compressor::WriteHeaderAndData(stream))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CompressorZStdData* compressorData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
CompressorZStdHeader header;
|
||||
header.m_numSeekPoints = aznumeric_caster(compressorData->m_seekPoints.size());
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
if (baseStream->WriteAtOffset(sizeof(header), &header, sizeof(CompressorHeader)) == sizeof(header))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline CompressorZStd::SizeType CompressorZStd::FillFromDecompressCache(CompressorZStdData* zstdData, void*& buffer, SizeType& byteSize, SizeType& offset)
|
||||
{
|
||||
SizeType firstOffsetInCache = zstdData->m_decompressedCacheOffset;
|
||||
SizeType lastOffsetInCache = firstOffsetInCache + zstdData->m_decompressedCacheDataSize;
|
||||
SizeType firstDataOffset = offset;
|
||||
SizeType lastDataOffset = offset + byteSize;
|
||||
SizeType numCopied = 0;
|
||||
if (firstOffsetInCache < lastDataOffset && lastOffsetInCache >= firstDataOffset) // check if there is data in the cache
|
||||
{
|
||||
size_t copyOffsetStart = 0;
|
||||
size_t copyOffsetEnd = zstdData->m_decompressedCacheDataSize;
|
||||
|
||||
size_t bufferCopyOffset = 0;
|
||||
|
||||
if (firstOffsetInCache < firstDataOffset)
|
||||
{
|
||||
return numRead;
|
||||
copyOffsetStart = aznumeric_caster(firstDataOffset - firstOffsetInCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
bufferCopyOffset = aznumeric_caster(firstOffsetInCache - firstDataOffset);
|
||||
}
|
||||
|
||||
// find the best seek point for current offset
|
||||
CompressorZStdData::SeekPointArray::iterator it = AZStd::upper_bound(zstdData->m_seekPoints.begin(), zstdData->m_seekPoints.end(), offset, ZStdCompareUpper());
|
||||
AZ_Assert(it != zstdData->m_seekPoints.begin(), "This should be impossible, we should always have a valid seek point at 0 offset.");
|
||||
const CompressorZStdSeekPoint& bestSeekPoint = *(--it); // get the previous (so it includes the current offset)
|
||||
|
||||
// if read is continuous continue with decompression
|
||||
bool isJumpToSeekPoint = false;
|
||||
SizeType lastOffsetInCache = zstdData->m_decompressedCacheOffset + zstdData->m_decompressedCacheDataSize;
|
||||
if (bestSeekPoint.m_uncompressedOffset > lastOffsetInCache) // if the best seek point is forward, jump forward to it.
|
||||
if (lastOffsetInCache >= lastDataOffset)
|
||||
{
|
||||
isJumpToSeekPoint = true;
|
||||
copyOffsetEnd -= static_cast<size_t>(lastOffsetInCache - lastDataOffset);
|
||||
}
|
||||
else if (offset < lastOffsetInCache) // if the seek point is in the past and the requested offset is not in the cache jump back to it.
|
||||
else if (bufferCopyOffset > 0) // the cache block is in the middle of the data, we can't use it (since we need to split buffer request into 2)
|
||||
{
|
||||
isJumpToSeekPoint = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (isJumpToSeekPoint)
|
||||
{
|
||||
zstdData->m_decompressNextOffset = bestSeekPoint.m_compressedOffset; // set next read point
|
||||
zstdData->m_decompressedCacheOffset = bestSeekPoint.m_uncompressedOffset; // set uncompressed offset
|
||||
zstdData->m_decompressedCacheDataSize = 0; // invalidate the cache
|
||||
zstdData->m_zstd.ResetDecompressor(&zstdData->m_zstdHeader); // reset decompressor and setup the header.
|
||||
}
|
||||
numCopied = copyOffsetEnd - copyOffsetStart;
|
||||
memcpy(static_cast<char*>(buffer) + bufferCopyOffset, zstdData->m_decompressedCache + copyOffsetStart, static_cast<size_t>(numCopied));
|
||||
|
||||
// decompress and move forward until the request is done
|
||||
while (byteSize > 0)
|
||||
// adjust pointers and sizes
|
||||
byteSize -= numCopied;
|
||||
if (bufferCopyOffset == 0)
|
||||
{
|
||||
// fill buffer with compressed data
|
||||
SizeType compressedDataSize = FillCompressedBuffer(stream);
|
||||
if (compressedDataSize == 0)
|
||||
// copied in the start
|
||||
buffer = reinterpret_cast<char*>(buffer) + numCopied;
|
||||
offset += numCopied;
|
||||
}
|
||||
}
|
||||
|
||||
return numCopied;
|
||||
}
|
||||
|
||||
inline CompressorZStd::SizeType CompressorZStd::FillCompressedBuffer(CompressorStream* stream)
|
||||
{
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
SizeType dataFromBuffer = 0;
|
||||
if (stream == m_lastReadStream) // if the buffer is filled with data from the current stream, try to reuse
|
||||
{
|
||||
if (zstdData->m_decompressNextOffset > m_lastReadStreamOffset)
|
||||
{
|
||||
SizeType offsetInCache = zstdData->m_decompressNextOffset - m_lastReadStreamOffset;
|
||||
if (offsetInCache < m_lastReadStreamSize) // last check if there is data overlap
|
||||
{
|
||||
return numRead; // we are done reading and obviously we did not managed to read all data
|
||||
// copy the usable part at the start of the buffer
|
||||
SizeType toMove = m_lastReadStreamSize - offsetInCache;
|
||||
memcpy(m_compressedDataBuffer, &m_compressedDataBuffer[static_cast<size_t>(offsetInCache)], static_cast<size_t>(toMove));
|
||||
dataFromBuffer += toMove;
|
||||
}
|
||||
unsigned int processedCompressedData = 0;
|
||||
while (byteSize > 0 && processedCompressedData < compressedDataSize) // decompressed data either until we are done with the request (byteSize == 0) or we need to fill the compression buffer again.
|
||||
{
|
||||
// if we have data in the cache move to the next offset, we always move forward by default.
|
||||
zstdData->m_decompressedCacheOffset += zstdData->m_decompressedCacheDataSize;
|
||||
|
||||
// decompress in the cache buffer
|
||||
u32 availDecompressedCacheSize = m_decompressionCachePerStream; // reset buffer size
|
||||
size_t nextBlockSize;
|
||||
unsigned int processed = zstdData->m_zstd.Decompress(&m_compressedDataBuffer[processedCompressedData],
|
||||
static_cast<unsigned int>(compressedDataSize) - processedCompressedData,
|
||||
zstdData->m_decompressedCache,
|
||||
availDecompressedCacheSize,
|
||||
&nextBlockSize);
|
||||
zstdData->m_decompressedCacheDataSize = m_decompressionCachePerStream - availDecompressedCacheSize;
|
||||
if (processed == 0)
|
||||
{
|
||||
break; // we processed everything we could, load more compressed data.
|
||||
}
|
||||
processedCompressedData += processed;
|
||||
// fill what we can from the cache
|
||||
numRead += FillFromDecompressCache(zstdData, buffer, byteSize, offset);
|
||||
}
|
||||
// update next read position the the compressed stream
|
||||
zstdData->m_decompressNextOffset += processedCompressedData;
|
||||
}
|
||||
}
|
||||
|
||||
SizeType toReadFromStream = m_compressedDataBufferSize - dataFromBuffer;
|
||||
SizeType readOffset = zstdData->m_decompressNextOffset + dataFromBuffer;
|
||||
if (readOffset + toReadFromStream > zstdData->m_decompressLastOffset)
|
||||
{
|
||||
// don't read past the end
|
||||
AZ_Assert(readOffset <= zstdData->m_decompressLastOffset, "Read offset should always be before the end of stream.");
|
||||
toReadFromStream = zstdData->m_decompressLastOffset - readOffset;
|
||||
}
|
||||
|
||||
SizeType numReadFromStream = 0;
|
||||
if (toReadFromStream) // if we did not reuse the whole buffer, read some data from the stream
|
||||
{
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
numReadFromStream = baseStream->ReadAtOffset(toReadFromStream, &m_compressedDataBuffer[static_cast<size_t>(dataFromBuffer)], readOffset);
|
||||
}
|
||||
|
||||
// update what's actually in the read data buffer.
|
||||
m_lastReadStream = stream;
|
||||
m_lastReadStreamOffset = zstdData->m_decompressNextOffset;
|
||||
m_lastReadStreamSize = dataFromBuffer + numReadFromStream;
|
||||
return m_lastReadStreamSize;
|
||||
}
|
||||
|
||||
struct ZStdCompareUpper
|
||||
{
|
||||
bool operator()(const AZ::u64& offset, const CompressorZStdSeekPoint& sp) const
|
||||
{
|
||||
return offset < sp.m_uncompressedOffset;
|
||||
}
|
||||
};
|
||||
|
||||
CompressorZStd::SizeType CompressorZStd::Read(CompressorStream* stream, SizeType byteSize, SizeType offset, void* buffer)
|
||||
{
|
||||
AZ_Assert(stream->GetCompressorData(), "This stream doesn't have decompression enabled.");
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
AZ_Assert(!zstdData->m_zstd.IsCompressorStarted(), "You can't read/decompress while writing a compressed stream %s.");
|
||||
|
||||
// check if the request can be finished from the decompressed cache
|
||||
SizeType numRead = FillFromDecompressCache(zstdData, buffer, byteSize, offset);
|
||||
if (byteSize == 0) // are we done
|
||||
{
|
||||
return numRead;
|
||||
}
|
||||
|
||||
CompressorZStd::SizeType CompressorZStd::Write(CompressorStream* stream, SizeType byteSize, const void* data, SizeType offset)
|
||||
// find the best seek point for current offset
|
||||
CompressorZStdData::SeekPointArray::iterator it = AZStd::upper_bound(zstdData->m_seekPoints.begin(), zstdData->m_seekPoints.end(), offset, ZStdCompareUpper());
|
||||
AZ_Assert(it != zstdData->m_seekPoints.begin(), "This should be impossible, we should always have a valid seek point at 0 offset.");
|
||||
const CompressorZStdSeekPoint& bestSeekPoint = *(--it); // get the previous (so it includes the current offset)
|
||||
|
||||
// if read is continuous continue with decompression
|
||||
bool isJumpToSeekPoint = false;
|
||||
SizeType lastOffsetInCache = zstdData->m_decompressedCacheOffset + zstdData->m_decompressedCacheDataSize;
|
||||
if (bestSeekPoint.m_uncompressedOffset > lastOffsetInCache) // if the best seek point is forward, jump forward to it.
|
||||
{
|
||||
AZ_UNUSED(offset);
|
||||
isJumpToSeekPoint = true;
|
||||
}
|
||||
else if (offset < lastOffsetInCache) // if the seek point is in the past and the requested offset is not in the cache jump back to it.
|
||||
{
|
||||
isJumpToSeekPoint = true;
|
||||
}
|
||||
|
||||
AZ_Assert(stream && stream->GetCompressorData(), "This stream doesn't have compression enabled. Call Stream::WriteCompressed after you create the file.");
|
||||
AZ_Assert(offset == SizeType(-1) || offset == stream->GetCurPos(), "We can write compressed data only at the end of the stream.");
|
||||
if (isJumpToSeekPoint)
|
||||
{
|
||||
zstdData->m_decompressNextOffset = bestSeekPoint.m_compressedOffset; // set next read point
|
||||
zstdData->m_decompressedCacheOffset = bestSeekPoint.m_uncompressedOffset; // set uncompressed offset
|
||||
zstdData->m_decompressedCacheDataSize = 0; // invalidate the cache
|
||||
zstdData->m_zstd.ResetDecompressor(&zstdData->m_zstdHeader); // reset decompressor and setup the header.
|
||||
}
|
||||
|
||||
m_lastReadStream = nullptr; // invalidate last read position, otherwise m_dataBuffer will be corrupted (as we are about to write in it).
|
||||
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
AZ_Assert(!zstdData->m_zstd.IsDecompressorStarted(), "You can't write while reading/decompressing a compressed stream.");
|
||||
|
||||
const u8* bytes = reinterpret_cast<const u8*>(data);
|
||||
unsigned int dataToCompress = aznumeric_caster(byteSize);
|
||||
while (dataToCompress != 0)
|
||||
// decompress and move forward until the request is done
|
||||
while (byteSize > 0)
|
||||
{
|
||||
// fill buffer with compressed data
|
||||
SizeType compressedDataSize = FillCompressedBuffer(stream);
|
||||
if (compressedDataSize == 0)
|
||||
{
|
||||
unsigned int oldDataToCompress = dataToCompress;
|
||||
unsigned int compressedSize = zstdData->m_zstd.Compress(bytes, dataToCompress, m_compressedDataBuffer, m_compressedDataBufferSize);
|
||||
if (compressedSize)
|
||||
{
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
SizeType numWritten = baseStream->Write(compressedSize, m_compressedDataBuffer);
|
||||
if (numWritten != compressedSize)
|
||||
{
|
||||
return numWritten; // error we could not write all data
|
||||
}
|
||||
}
|
||||
bytes += oldDataToCompress - dataToCompress;
|
||||
return numRead; // we are done reading and obviously we did not managed to read all data
|
||||
}
|
||||
zstdData->m_uncompressedSize += byteSize;
|
||||
|
||||
if (zstdData->m_autoSeekSize > 0)
|
||||
unsigned int processedCompressedData = 0;
|
||||
while (byteSize > 0 && processedCompressedData < compressedDataSize) // decompressed data either until we are done with the request (byteSize == 0) or we need to fill the compression buffer again.
|
||||
{
|
||||
// insert a seek point if needed.
|
||||
if (zstdData->m_seekPoints.empty())
|
||||
// if we have data in the cache move to the next offset, we always move forward by default.
|
||||
zstdData->m_decompressedCacheOffset += zstdData->m_decompressedCacheDataSize;
|
||||
|
||||
// decompress in the cache buffer
|
||||
u32 availDecompressedCacheSize = m_decompressionCachePerStream; // reset buffer size
|
||||
size_t nextBlockSize;
|
||||
unsigned int processed = zstdData->m_zstd.Decompress(&m_compressedDataBuffer[processedCompressedData],
|
||||
static_cast<unsigned int>(compressedDataSize) - processedCompressedData,
|
||||
zstdData->m_decompressedCache,
|
||||
availDecompressedCacheSize,
|
||||
&nextBlockSize);
|
||||
zstdData->m_decompressedCacheDataSize = m_decompressionCachePerStream - availDecompressedCacheSize;
|
||||
if (processed == 0)
|
||||
{
|
||||
if (zstdData->m_uncompressedSize >= zstdData->m_autoSeekSize)
|
||||
{
|
||||
WriteSeekPoint(stream);
|
||||
}
|
||||
break; // we processed everything we could, load more compressed data.
|
||||
}
|
||||
else if ((zstdData->m_uncompressedSize - zstdData->m_seekPoints.back().m_uncompressedOffset) > zstdData->m_autoSeekSize)
|
||||
processedCompressedData += processed;
|
||||
// fill what we can from the cache
|
||||
numRead += FillFromDecompressCache(zstdData, buffer, byteSize, offset);
|
||||
}
|
||||
// update next read position the the compressed stream
|
||||
zstdData->m_decompressNextOffset += processedCompressedData;
|
||||
}
|
||||
return numRead;
|
||||
}
|
||||
|
||||
CompressorZStd::SizeType CompressorZStd::Write(CompressorStream* stream, SizeType byteSize, const void* data, SizeType offset)
|
||||
{
|
||||
AZ_UNUSED(offset);
|
||||
|
||||
AZ_Assert(stream && stream->GetCompressorData(), "This stream doesn't have compression enabled. Call Stream::WriteCompressed after you create the file.");
|
||||
AZ_Assert(offset == SizeType(-1) || offset == stream->GetCurPos(), "We can write compressed data only at the end of the stream.");
|
||||
|
||||
m_lastReadStream = nullptr; // invalidate last read position, otherwise m_dataBuffer will be corrupted (as we are about to write in it).
|
||||
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
AZ_Assert(!zstdData->m_zstd.IsDecompressorStarted(), "You can't write while reading/decompressing a compressed stream.");
|
||||
|
||||
const u8* bytes = reinterpret_cast<const u8*>(data);
|
||||
unsigned int dataToCompress = aznumeric_caster(byteSize);
|
||||
while (dataToCompress != 0)
|
||||
{
|
||||
unsigned int oldDataToCompress = dataToCompress;
|
||||
unsigned int compressedSize = zstdData->m_zstd.Compress(bytes, dataToCompress, m_compressedDataBuffer, m_compressedDataBufferSize);
|
||||
if (compressedSize)
|
||||
{
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
SizeType numWritten = baseStream->Write(compressedSize, m_compressedDataBuffer);
|
||||
if (numWritten != compressedSize)
|
||||
{
|
||||
return numWritten; // error we could not write all data
|
||||
}
|
||||
}
|
||||
bytes += oldDataToCompress - dataToCompress;
|
||||
}
|
||||
zstdData->m_uncompressedSize += byteSize;
|
||||
|
||||
if (zstdData->m_autoSeekSize > 0)
|
||||
{
|
||||
// insert a seek point if needed.
|
||||
if (zstdData->m_seekPoints.empty())
|
||||
{
|
||||
if (zstdData->m_uncompressedSize >= zstdData->m_autoSeekSize)
|
||||
{
|
||||
WriteSeekPoint(stream);
|
||||
}
|
||||
}
|
||||
return byteSize;
|
||||
else if ((zstdData->m_uncompressedSize - zstdData->m_seekPoints.back().m_uncompressedOffset) > zstdData->m_autoSeekSize)
|
||||
{
|
||||
WriteSeekPoint(stream);
|
||||
}
|
||||
}
|
||||
return byteSize;
|
||||
}
|
||||
|
||||
bool CompressorZStd::WriteSeekPoint(CompressorStream* stream)
|
||||
bool CompressorZStd::WriteSeekPoint(CompressorStream* stream)
|
||||
{
|
||||
AZ_Assert(stream && stream->GetCompressorData(), "This stream doesn't have compression enabled. Call Stream::WriteCompressed after you create the file.");
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
|
||||
m_lastReadStream = nullptr; // invalidate last read position, otherwise m_dataBuffer will be corrupted (as we are about to write in it).
|
||||
|
||||
unsigned int compressedSize;
|
||||
unsigned int dataToCompress = 0;
|
||||
do
|
||||
{
|
||||
AZ_Assert(stream && stream->GetCompressorData(), "This stream doesn't have compression enabled. Call Stream::WriteCompressed after you create the file.");
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
compressedSize = zstdData->m_zstd.Compress(nullptr, dataToCompress, m_compressedDataBuffer, m_compressedDataBufferSize, ZStd::FT_FULL_FLUSH);
|
||||
if (compressedSize)
|
||||
{
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
SizeType numWritten = baseStream->Write(compressedSize, m_compressedDataBuffer);
|
||||
if (numWritten != compressedSize)
|
||||
{
|
||||
return false; // error we wrote less than than requested!
|
||||
}
|
||||
}
|
||||
} while (dataToCompress != 0);
|
||||
|
||||
CompressorZStdSeekPoint sp;
|
||||
sp.m_compressedOffset = stream->GetLength();
|
||||
sp.m_uncompressedOffset = zstdData->m_uncompressedSize;
|
||||
zstdData->m_seekPoints.push_back(sp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompressorZStd::StartCompressor(CompressorStream* stream, int compressionLevel, SizeType autoSeekDataSize)
|
||||
{
|
||||
AZ_Assert(stream && !stream->GetCompressorData(), "Stream has compressor already enabled.");
|
||||
|
||||
AcquireDataBuffer();
|
||||
|
||||
CompressorZStdData* zstdData = aznew CompressorZStdData;
|
||||
zstdData->m_compressor = this;
|
||||
zstdData->m_zstdHeader = 0; // not used for compression
|
||||
zstdData->m_uncompressedSize = 0;
|
||||
zstdData->m_autoSeekSize = autoSeekDataSize;
|
||||
compressionLevel = AZ::GetClamp(compressionLevel, 1, 9); // remap to zlib levels
|
||||
|
||||
zstdData->m_zstd.StartCompressor(compressionLevel);
|
||||
|
||||
stream->SetCompressorData(zstdData);
|
||||
|
||||
if (WriteHeaderAndData(stream))
|
||||
{
|
||||
// add the first and always present seek point at the start of the compressed stream
|
||||
CompressorZStdSeekPoint sp;
|
||||
sp.m_compressedOffset = sizeof(CompressorHeader) + sizeof(CompressorZStdHeader) + sizeof(zstdData->m_zstdHeader);
|
||||
sp.m_uncompressedOffset = 0;
|
||||
zstdData->m_seekPoints.push_back(sp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CompressorZStd::Close(CompressorStream* stream)
|
||||
{
|
||||
AZ_Assert(stream->IsOpen(), "Stream is not open to be closed.");
|
||||
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
|
||||
bool result = true;
|
||||
if (zstdData->m_zstd.IsCompressorStarted())
|
||||
{
|
||||
m_lastReadStream = nullptr; // invalidate last read position, otherwise m_dataBuffer will be corrupted (as we are about to write in it).
|
||||
|
||||
// flush all compressed data
|
||||
unsigned int compressedSize;
|
||||
unsigned int dataToCompress = 0;
|
||||
do
|
||||
{
|
||||
compressedSize = zstdData->m_zstd.Compress(nullptr, dataToCompress, m_compressedDataBuffer, m_compressedDataBufferSize, ZStd::FT_FULL_FLUSH);
|
||||
compressedSize = zstdData->m_zstd.Compress(nullptr, dataToCompress, m_compressedDataBuffer, m_compressedDataBufferSize, ZStd::FT_FINISH);
|
||||
if (compressedSize)
|
||||
{
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
SizeType numWritten = baseStream->Write(compressedSize, m_compressedDataBuffer);
|
||||
if (numWritten != compressedSize)
|
||||
{
|
||||
return false; // error we wrote less than than requested!
|
||||
}
|
||||
baseStream->Write(compressedSize, m_compressedDataBuffer);
|
||||
}
|
||||
} while (dataToCompress != 0);
|
||||
|
||||
CompressorZStdSeekPoint sp;
|
||||
sp.m_compressedOffset = stream->GetLength();
|
||||
sp.m_uncompressedOffset = zstdData->m_uncompressedSize;
|
||||
zstdData->m_seekPoints.push_back(sp);
|
||||
return true;
|
||||
result = WriteHeaderAndData(stream);
|
||||
if (result)
|
||||
{
|
||||
SizeType dataToWrite = zstdData->m_seekPoints.size() * sizeof(CompressorZStdSeekPoint);
|
||||
baseStream->Seek(0U, GenericStream::SeekMode::ST_SEEK_END);
|
||||
result = (baseStream->Write(dataToWrite, zstdData->m_seekPoints.data()) == dataToWrite);
|
||||
}
|
||||
}
|
||||
|
||||
bool CompressorZStd::StartCompressor(CompressorStream* stream, int compressionLevel, SizeType autoSeekDataSize)
|
||||
else
|
||||
{
|
||||
AZ_Assert(stream && !stream->GetCompressorData(), "Stream has compressor already enabled.");
|
||||
|
||||
AcquireDataBuffer();
|
||||
|
||||
CompressorZStdData* zstdData = aznew CompressorZStdData;
|
||||
zstdData->m_compressor = this;
|
||||
zstdData->m_zstdHeader = 0; // not used for compression
|
||||
zstdData->m_uncompressedSize = 0;
|
||||
zstdData->m_autoSeekSize = autoSeekDataSize;
|
||||
compressionLevel = AZ::GetClamp(compressionLevel, 1, 9); // remap to zlib levels
|
||||
|
||||
zstdData->m_zstd.StartCompressor(compressionLevel);
|
||||
|
||||
stream->SetCompressorData(zstdData);
|
||||
|
||||
if (WriteHeaderAndData(stream))
|
||||
if (m_lastReadStream == stream)
|
||||
{
|
||||
// add the first and always present seek point at the start of the compressed stream
|
||||
CompressorZStdSeekPoint sp;
|
||||
sp.m_compressedOffset = sizeof(CompressorHeader) + sizeof(CompressorZStdHeader) + sizeof(zstdData->m_zstdHeader);
|
||||
sp.m_uncompressedOffset = 0;
|
||||
zstdData->m_seekPoints.push_back(sp);
|
||||
return true;
|
||||
m_lastReadStream = nullptr; // invalidate the data in m_dataBuffer if it was from the current stream.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CompressorZStd::Close(CompressorStream* stream)
|
||||
// if we have decompressor cache delete it
|
||||
if (zstdData->m_decompressedCache)
|
||||
{
|
||||
AZ_Assert(stream->IsOpen(), "Stream is not open to be closed.");
|
||||
|
||||
CompressorZStdData* zstdData = static_cast<CompressorZStdData*>(stream->GetCompressorData());
|
||||
GenericStream* baseStream = stream->GetWrappedStream();
|
||||
|
||||
bool result = true;
|
||||
if (zstdData->m_zstd.IsCompressorStarted())
|
||||
{
|
||||
m_lastReadStream = nullptr; // invalidate last read position, otherwise m_dataBuffer will be corrupted (as we are about to write in it).
|
||||
|
||||
// flush all compressed data
|
||||
unsigned int compressedSize;
|
||||
unsigned int dataToCompress = 0;
|
||||
do
|
||||
{
|
||||
compressedSize = zstdData->m_zstd.Compress(nullptr, dataToCompress, m_compressedDataBuffer, m_compressedDataBufferSize, ZStd::FT_FINISH);
|
||||
if (compressedSize)
|
||||
{
|
||||
baseStream->Write(compressedSize, m_compressedDataBuffer);
|
||||
}
|
||||
} while (dataToCompress != 0);
|
||||
|
||||
result = WriteHeaderAndData(stream);
|
||||
if (result)
|
||||
{
|
||||
SizeType dataToWrite = zstdData->m_seekPoints.size() * sizeof(CompressorZStdSeekPoint);
|
||||
baseStream->Seek(0U, GenericStream::SeekMode::ST_SEEK_END);
|
||||
result = (baseStream->Write(dataToWrite, zstdData->m_seekPoints.data()) == dataToWrite);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_lastReadStream == stream)
|
||||
{
|
||||
m_lastReadStream = nullptr; // invalidate the data in m_dataBuffer if it was from the current stream.
|
||||
}
|
||||
}
|
||||
|
||||
// if we have decompressor cache delete it
|
||||
if (zstdData->m_decompressedCache)
|
||||
{
|
||||
azfree(zstdData->m_decompressedCache, AZ::SystemAllocator, m_decompressionCachePerStream, m_CompressedDataBufferAlignment);
|
||||
}
|
||||
|
||||
ReleaseDataBuffer();
|
||||
|
||||
// last step reset strream compressor data.
|
||||
stream->SetCompressorData(nullptr);
|
||||
return result;
|
||||
azfree(zstdData->m_decompressedCache, AZ::SystemAllocator, m_decompressionCachePerStream, m_CompressedDataBufferAlignment);
|
||||
}
|
||||
|
||||
void CompressorZStd::AcquireDataBuffer()
|
||||
ReleaseDataBuffer();
|
||||
|
||||
// last step reset strream compressor data.
|
||||
stream->SetCompressorData(nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
void CompressorZStd::AcquireDataBuffer()
|
||||
{
|
||||
if (m_compressedDataBuffer == nullptr)
|
||||
{
|
||||
if (m_compressedDataBuffer == nullptr)
|
||||
{
|
||||
AZ_Assert(m_compressedDataBufferUseCount == 0, "Buffer usecount should be 0 if the buffer is NULL");
|
||||
m_compressedDataBuffer = reinterpret_cast<unsigned char*>(azmalloc(m_compressedDataBufferSize, m_CompressedDataBufferAlignment, AZ::SystemAllocator, "CompressorZStd"));
|
||||
m_lastReadStream = nullptr; // reset the cache info in the m_dataBuffer
|
||||
}
|
||||
++m_compressedDataBufferUseCount;
|
||||
AZ_Assert(m_compressedDataBufferUseCount == 0, "Buffer usecount should be 0 if the buffer is NULL");
|
||||
m_compressedDataBuffer = reinterpret_cast<unsigned char*>(azmalloc(m_compressedDataBufferSize, m_CompressedDataBufferAlignment, AZ::SystemAllocator, "CompressorZStd"));
|
||||
m_lastReadStream = nullptr; // reset the cache info in the m_dataBuffer
|
||||
}
|
||||
++m_compressedDataBufferUseCount;
|
||||
}
|
||||
|
||||
void CompressorZStd::ReleaseDataBuffer()
|
||||
void CompressorZStd::ReleaseDataBuffer()
|
||||
{
|
||||
--m_compressedDataBufferUseCount;
|
||||
if (m_compressedDataBufferUseCount == 0)
|
||||
{
|
||||
--m_compressedDataBufferUseCount;
|
||||
if (m_compressedDataBufferUseCount == 0)
|
||||
{
|
||||
AZ_Assert(m_compressedDataBuffer, "Invalid data buffer. We should have a non null pointer.");
|
||||
azfree(m_compressedDataBuffer, AZ::SystemAllocator, m_compressedDataBufferSize, m_CompressedDataBufferAlignment);
|
||||
m_compressedDataBuffer = nullptr;
|
||||
m_lastReadStream = nullptr; // reset the cache info in the m_dataBuffer
|
||||
}
|
||||
AZ_Assert(m_compressedDataBuffer, "Invalid data buffer. We should have a non null pointer.");
|
||||
azfree(m_compressedDataBuffer, AZ::SystemAllocator, m_compressedDataBufferSize, m_CompressedDataBufferAlignment);
|
||||
m_compressedDataBuffer = nullptr;
|
||||
m_lastReadStream = nullptr; // reset the cache info in the m_dataBuffer
|
||||
}
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
#endif // #if !defined(AZCORE_EXCLUDE_ZSTD)
|
||||
|
||||
@@ -21,485 +21,482 @@
|
||||
# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
|
||||
#endif
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
static EnvironmentVariable<FileIOBase*> g_fileIOInstance;
|
||||
static EnvironmentVariable<FileIOBase*> g_directFileIOInstance;
|
||||
static const char* s_EngineFileIOName = "EngineFileIO";
|
||||
static const char* s_DirectFileIOName = "DirectFileIO";
|
||||
|
||||
FileIOBase* FileIOBase::GetInstance()
|
||||
{
|
||||
static EnvironmentVariable<FileIOBase*> g_fileIOInstance;
|
||||
static EnvironmentVariable<FileIOBase*> g_directFileIOInstance;
|
||||
static const char* s_EngineFileIOName = "EngineFileIO";
|
||||
static const char* s_DirectFileIOName = "DirectFileIO";
|
||||
|
||||
FileIOBase* FileIOBase::GetInstance()
|
||||
if (!g_fileIOInstance)
|
||||
{
|
||||
if (!g_fileIOInstance)
|
||||
{
|
||||
g_fileIOInstance = Environment::FindVariable<FileIOBase*>(s_EngineFileIOName);
|
||||
}
|
||||
|
||||
return g_fileIOInstance ? (*g_fileIOInstance) : nullptr;
|
||||
g_fileIOInstance = Environment::FindVariable<FileIOBase*>(s_EngineFileIOName);
|
||||
}
|
||||
|
||||
void FileIOBase::SetInstance(FileIOBase* instance)
|
||||
return g_fileIOInstance ? (*g_fileIOInstance) : nullptr;
|
||||
}
|
||||
|
||||
void FileIOBase::SetInstance(FileIOBase* instance)
|
||||
{
|
||||
if (!g_fileIOInstance)
|
||||
{
|
||||
if (!g_fileIOInstance)
|
||||
{
|
||||
g_fileIOInstance = Environment::CreateVariable<FileIOBase*>(s_EngineFileIOName);
|
||||
(*g_fileIOInstance) = nullptr;
|
||||
}
|
||||
|
||||
// at this point we're guaranteed to have g_fileIOInstance. Its value might be null.
|
||||
|
||||
if ((instance) && (g_fileIOInstance) && (*g_fileIOInstance))
|
||||
{
|
||||
AZ_Error("FileIO", false, "FileIOBase::SetInstance was called without first destroying the old instance and setting it to nullptr");
|
||||
}
|
||||
|
||||
(*g_fileIOInstance) = instance;
|
||||
g_fileIOInstance = Environment::CreateVariable<FileIOBase*>(s_EngineFileIOName);
|
||||
(*g_fileIOInstance) = nullptr;
|
||||
}
|
||||
|
||||
FileIOBase* FileIOBase::GetDirectInstance()
|
||||
// at this point we're guaranteed to have g_fileIOInstance. Its value might be null.
|
||||
|
||||
if ((instance) && (g_fileIOInstance) && (*g_fileIOInstance))
|
||||
{
|
||||
if (!g_directFileIOInstance)
|
||||
{
|
||||
g_directFileIOInstance = Environment::FindVariable<FileIOBase*>(s_DirectFileIOName);
|
||||
}
|
||||
|
||||
// for backwards compatibilty, return the regular instance if this is not attached
|
||||
if (!g_directFileIOInstance)
|
||||
{
|
||||
return GetInstance();
|
||||
}
|
||||
|
||||
return g_directFileIOInstance ? (*g_directFileIOInstance) : nullptr;
|
||||
AZ_Error("FileIO", false, "FileIOBase::SetInstance was called without first destroying the old instance and setting it to nullptr");
|
||||
}
|
||||
|
||||
void FileIOBase::SetDirectInstance(FileIOBase* instance)
|
||||
(*g_fileIOInstance) = instance;
|
||||
}
|
||||
|
||||
FileIOBase* FileIOBase::GetDirectInstance()
|
||||
{
|
||||
if (!g_directFileIOInstance)
|
||||
{
|
||||
if (!g_directFileIOInstance)
|
||||
{
|
||||
g_directFileIOInstance = Environment::CreateVariable<FileIOBase*>(s_DirectFileIOName);
|
||||
(*g_directFileIOInstance) = nullptr;
|
||||
}
|
||||
|
||||
// at this point we're guaranteed to have g_directFileIOInstance. Its value might be null.
|
||||
|
||||
if ((instance) && (g_directFileIOInstance) && (*g_directFileIOInstance))
|
||||
{
|
||||
AZ_Error("FileIO", false, "FileIOBase::SetDirectInstance was called without first destroying the old instance and setting it to nullptr");
|
||||
}
|
||||
|
||||
(*g_directFileIOInstance) = instance;
|
||||
g_directFileIOInstance = Environment::FindVariable<FileIOBase*>(s_DirectFileIOName);
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPath> FileIOBase::ConvertToAlias(const AZ::IO::PathView& path) const
|
||||
// for backwards compatibilty, return the regular instance if this is not attached
|
||||
if (!g_directFileIOInstance)
|
||||
{
|
||||
AZ::IO::FixedMaxPath convertedPath;
|
||||
if (ConvertToAlias(convertedPath, path))
|
||||
{
|
||||
return convertedPath;
|
||||
}
|
||||
|
||||
return AZStd::nullopt;
|
||||
return GetInstance();
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPath> FileIOBase::ResolvePath(const AZ::IO::PathView& path) const
|
||||
{
|
||||
AZ::IO::FixedMaxPath resolvedPath;
|
||||
if (ResolvePath(resolvedPath, path))
|
||||
{
|
||||
return resolvedPath;
|
||||
}
|
||||
return g_directFileIOInstance ? (*g_directFileIOInstance) : nullptr;
|
||||
}
|
||||
|
||||
return AZStd::nullopt;
|
||||
void FileIOBase::SetDirectInstance(FileIOBase* instance)
|
||||
{
|
||||
if (!g_directFileIOInstance)
|
||||
{
|
||||
g_directFileIOInstance = Environment::CreateVariable<FileIOBase*>(s_DirectFileIOName);
|
||||
(*g_directFileIOInstance) = nullptr;
|
||||
}
|
||||
|
||||
SeekType GetSeekTypeFromFSeekMode(int mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case SEEK_SET:
|
||||
return SeekType::SeekFromStart;
|
||||
case SEEK_CUR:
|
||||
return SeekType::SeekFromCurrent;
|
||||
case SEEK_END:
|
||||
return SeekType::SeekFromEnd;
|
||||
}
|
||||
// at this point we're guaranteed to have g_directFileIOInstance. Its value might be null.
|
||||
|
||||
// Must have some default, hitting here means some random int mode
|
||||
if ((instance) && (g_directFileIOInstance) && (*g_directFileIOInstance))
|
||||
{
|
||||
AZ_Error("FileIO", false, "FileIOBase::SetDirectInstance was called without first destroying the old instance and setting it to nullptr");
|
||||
}
|
||||
|
||||
(*g_directFileIOInstance) = instance;
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPath> FileIOBase::ConvertToAlias(const AZ::IO::PathView& path) const
|
||||
{
|
||||
AZ::IO::FixedMaxPath convertedPath;
|
||||
if (ConvertToAlias(convertedPath, path))
|
||||
{
|
||||
return convertedPath;
|
||||
}
|
||||
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPath> FileIOBase::ResolvePath(const AZ::IO::PathView& path) const
|
||||
{
|
||||
AZ::IO::FixedMaxPath resolvedPath;
|
||||
if (ResolvePath(resolvedPath, path))
|
||||
{
|
||||
return resolvedPath;
|
||||
}
|
||||
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
SeekType GetSeekTypeFromFSeekMode(int mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case SEEK_SET:
|
||||
return SeekType::SeekFromStart;
|
||||
case SEEK_CUR:
|
||||
return SeekType::SeekFromCurrent;
|
||||
case SEEK_END:
|
||||
return SeekType::SeekFromEnd;
|
||||
}
|
||||
|
||||
int GetFSeekModeFromSeekType(SeekType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SeekType::SeekFromStart:
|
||||
return SEEK_SET;
|
||||
case SeekType::SeekFromCurrent:
|
||||
return SEEK_CUR;
|
||||
case SeekType::SeekFromEnd:
|
||||
return SEEK_END;
|
||||
}
|
||||
// Must have some default, hitting here means some random int mode
|
||||
return SeekType::SeekFromStart;
|
||||
}
|
||||
|
||||
int GetFSeekModeFromSeekType(SeekType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SeekType::SeekFromStart:
|
||||
return SEEK_SET;
|
||||
case SeekType::SeekFromCurrent:
|
||||
return SEEK_CUR;
|
||||
case SeekType::SeekFromEnd:
|
||||
return SEEK_END;
|
||||
}
|
||||
|
||||
void UpdateOpenModeForReading(OpenMode& openMode)
|
||||
return SEEK_SET;
|
||||
}
|
||||
|
||||
void UpdateOpenModeForReading(OpenMode& openMode)
|
||||
{
|
||||
if (AnyFlag(openMode & OpenMode::ModeRead))
|
||||
{
|
||||
if (AnyFlag(openMode & OpenMode::ModeRead))
|
||||
if (AnyFlag(openMode & OpenMode::ModeText))
|
||||
{
|
||||
if (AnyFlag(openMode & OpenMode::ModeText))
|
||||
{
|
||||
OpenMode extraModes = openMode & (OpenMode::ModeUpdate | OpenMode::ModeAppend);
|
||||
openMode = OpenMode::ModeRead | OpenMode::ModeBinary | extraModes;
|
||||
}
|
||||
else if (!AnyFlag(openMode & OpenMode::ModeBinary))
|
||||
{
|
||||
// if you haven't supplied any flag, supply binary
|
||||
openMode = openMode | OpenMode::ModeBinary;
|
||||
}
|
||||
OpenMode extraModes = openMode & (OpenMode::ModeUpdate | OpenMode::ModeAppend);
|
||||
openMode = OpenMode::ModeRead | OpenMode::ModeBinary | extraModes;
|
||||
}
|
||||
else if (!AnyFlag(openMode & OpenMode::ModeBinary))
|
||||
{
|
||||
// if you haven't supplied any flag, supply binary
|
||||
openMode = openMode | OpenMode::ModeBinary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OpenMode GetOpenModeFromStringMode(const char* mode)
|
||||
OpenMode GetOpenModeFromStringMode(const char* mode)
|
||||
{
|
||||
OpenMode openMode = OpenMode::Invalid;
|
||||
|
||||
if (strstr(mode, "w"))
|
||||
{
|
||||
OpenMode openMode = OpenMode::Invalid;
|
||||
|
||||
if (strstr(mode, "w"))
|
||||
{
|
||||
openMode |= OpenMode::ModeWrite;
|
||||
}
|
||||
|
||||
if (strstr(mode, "r"))
|
||||
{
|
||||
openMode |= OpenMode::ModeRead;
|
||||
}
|
||||
|
||||
if (strstr(mode, "a"))
|
||||
{
|
||||
openMode |= OpenMode::ModeAppend;
|
||||
}
|
||||
|
||||
if (strstr(mode, "b"))
|
||||
{
|
||||
openMode |= OpenMode::ModeBinary;
|
||||
}
|
||||
|
||||
if (strstr(mode, "t"))
|
||||
{
|
||||
openMode |= OpenMode::ModeText;
|
||||
}
|
||||
|
||||
if (strstr(mode, "+"))
|
||||
{
|
||||
openMode |= OpenMode::ModeUpdate;
|
||||
}
|
||||
|
||||
UpdateOpenModeForReading(openMode);
|
||||
|
||||
return openMode;
|
||||
openMode |= OpenMode::ModeWrite;
|
||||
}
|
||||
|
||||
const char* GetStringModeFromOpenMode(OpenMode mode)
|
||||
if (strstr(mode, "r"))
|
||||
{
|
||||
UpdateOpenModeForReading(mode);
|
||||
// Append is highest priority, followed by write and then read
|
||||
// APPEND
|
||||
if (AnyFlag(mode & OpenMode::ModeAppend))
|
||||
openMode |= OpenMode::ModeRead;
|
||||
}
|
||||
|
||||
if (strstr(mode, "a"))
|
||||
{
|
||||
openMode |= OpenMode::ModeAppend;
|
||||
}
|
||||
|
||||
if (strstr(mode, "b"))
|
||||
{
|
||||
openMode |= OpenMode::ModeBinary;
|
||||
}
|
||||
|
||||
if (strstr(mode, "t"))
|
||||
{
|
||||
openMode |= OpenMode::ModeText;
|
||||
}
|
||||
|
||||
if (strstr(mode, "+"))
|
||||
{
|
||||
openMode |= OpenMode::ModeUpdate;
|
||||
}
|
||||
|
||||
UpdateOpenModeForReading(openMode);
|
||||
|
||||
return openMode;
|
||||
}
|
||||
|
||||
const char* GetStringModeFromOpenMode(OpenMode mode)
|
||||
{
|
||||
UpdateOpenModeForReading(mode);
|
||||
// Append is highest priority, followed by write and then read
|
||||
// APPEND
|
||||
if (AnyFlag(mode & OpenMode::ModeAppend))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeUpdate))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeUpdate))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "a+b";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "a+t";
|
||||
}
|
||||
return "a+";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "ab";
|
||||
return "a+b";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "at";
|
||||
return "a+t";
|
||||
}
|
||||
return "a";
|
||||
return "a+";
|
||||
}
|
||||
|
||||
// WRITE
|
||||
if (AnyFlag(mode & OpenMode::ModeWrite))
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "ab";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "at";
|
||||
}
|
||||
return "a";
|
||||
}
|
||||
|
||||
// WRITE
|
||||
if (AnyFlag(mode & OpenMode::ModeWrite))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeUpdate))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeUpdate))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "w+b";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "w+t";
|
||||
}
|
||||
return "w+";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "wb";
|
||||
return "w+b";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "wt";
|
||||
return "w+t";
|
||||
}
|
||||
return "w";
|
||||
return "w+";
|
||||
}
|
||||
|
||||
// READ
|
||||
if (AnyFlag(mode & OpenMode::ModeRead))
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "wb";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "wt";
|
||||
}
|
||||
return "w";
|
||||
}
|
||||
|
||||
// READ
|
||||
if (AnyFlag(mode & OpenMode::ModeRead))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeUpdate))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeUpdate))
|
||||
{
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "r+b";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "r+t";
|
||||
}
|
||||
return "r+";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "rb";
|
||||
return "r+b";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "rt";
|
||||
return "r+t";
|
||||
}
|
||||
return "r";
|
||||
return "r+";
|
||||
}
|
||||
|
||||
// Bad open mode passed in
|
||||
AZ_Error("FileIO", false, "A bad open mode was sent to GetStringModeFromOpenMode()");
|
||||
return "";
|
||||
if (AnyFlag(mode & OpenMode::ModeBinary))
|
||||
{
|
||||
return "rb";
|
||||
}
|
||||
if (AnyFlag(mode & OpenMode::ModeText))
|
||||
{
|
||||
return "rt";
|
||||
}
|
||||
return "r";
|
||||
}
|
||||
|
||||
bool NameMatchesFilter(const char* name, const char* filter)
|
||||
// Bad open mode passed in
|
||||
AZ_Error("FileIO", false, "A bad open mode was sent to GetStringModeFromOpenMode()");
|
||||
return "";
|
||||
}
|
||||
|
||||
bool NameMatchesFilter(const char* name, const char* filter)
|
||||
{
|
||||
return AZStd::wildcard_match(filter, name);
|
||||
}
|
||||
|
||||
FileIOStream::FileIOStream()
|
||||
: m_handle(InvalidHandle)
|
||||
, m_mode(OpenMode::Invalid)
|
||||
, m_ownsHandle(true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FileIOStream::FileIOStream(HandleType fileHandle, AZ::IO::OpenMode mode, bool ownsHandle)
|
||||
: m_handle(fileHandle)
|
||||
, m_mode(mode)
|
||||
, m_ownsHandle(ownsHandle)
|
||||
{
|
||||
|
||||
FileIOBase* fileIO = FileIOBase::GetInstance();
|
||||
AZ_Assert(fileIO, "FileIO is not initialized.");
|
||||
AZStd::array<char, MaxPathLength> resolvedPath{ {0} };
|
||||
fileIO->GetFilename(m_handle, resolvedPath.data(), resolvedPath.size() - 1);
|
||||
m_filename = resolvedPath.data();
|
||||
}
|
||||
|
||||
FileIOStream::FileIOStream(const char* path, AZ::IO::OpenMode mode, bool errorOnFailure)
|
||||
: m_handle(InvalidHandle)
|
||||
, m_mode(mode)
|
||||
, m_errorOnFailure(errorOnFailure)
|
||||
{
|
||||
Open(path, mode);
|
||||
}
|
||||
|
||||
FileIOStream::~FileIOStream()
|
||||
{
|
||||
if (m_ownsHandle)
|
||||
{
|
||||
return AZStd::wildcard_match(filter, name);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
FileIOStream::FileIOStream()
|
||||
: m_handle(InvalidHandle)
|
||||
, m_mode(OpenMode::Invalid)
|
||||
, m_ownsHandle(true)
|
||||
bool FileIOStream::Open(const char* path, OpenMode mode)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
FileIOBase* fileIO = FileIOBase::GetInstance();
|
||||
|
||||
Close();
|
||||
|
||||
const Result result = fileIO->Open(path, mode, m_handle);
|
||||
m_ownsHandle = IsOpen();
|
||||
m_mode = mode;
|
||||
|
||||
if (IsOpen())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FileIOStream::FileIOStream(HandleType fileHandle, AZ::IO::OpenMode mode, bool ownsHandle)
|
||||
: m_handle(fileHandle)
|
||||
, m_mode(mode)
|
||||
, m_ownsHandle(ownsHandle)
|
||||
{
|
||||
|
||||
FileIOBase* fileIO = FileIOBase::GetInstance();
|
||||
AZ_Assert(fileIO, "FileIO is not initialized.");
|
||||
// Not using supplied path parameter as it may be unresolved
|
||||
AZStd::array<char, MaxPathLength> resolvedPath{ {0} };
|
||||
fileIO->GetFilename(m_handle, resolvedPath.data(), resolvedPath.size() - 1);
|
||||
m_filename = resolvedPath.data();
|
||||
}
|
||||
|
||||
FileIOStream::FileIOStream(const char* path, AZ::IO::OpenMode mode, bool errorOnFailure)
|
||||
: m_handle(InvalidHandle)
|
||||
, m_mode(mode)
|
||||
, m_errorOnFailure(errorOnFailure)
|
||||
else
|
||||
{
|
||||
Open(path, mode);
|
||||
// remember the file name so you can try again with ReOpen
|
||||
m_filename = path;
|
||||
}
|
||||
|
||||
FileIOStream::~FileIOStream()
|
||||
{
|
||||
if (m_ownsHandle)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
AZ_PROFILE_INTERVAL_START_COLORED(AzCore, &m_filename, 0xff0000ff, "FileIO: %s", m_filename.c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
bool FileIOStream::Open(const char* path, OpenMode mode)
|
||||
bool FileIOStream::ReOpen()
|
||||
{
|
||||
Close();
|
||||
return (m_mode != OpenMode::Invalid) ? Open(m_filename.data(), m_mode) : false;
|
||||
}
|
||||
|
||||
void FileIOStream::Close()
|
||||
{
|
||||
if (m_handle != InvalidHandle)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
FileIOBase* fileIO = FileIOBase::GetInstance();
|
||||
|
||||
Close();
|
||||
|
||||
const Result result = fileIO->Open(path, mode, m_handle);
|
||||
m_ownsHandle = IsOpen();
|
||||
m_mode = mode;
|
||||
|
||||
if (IsOpen())
|
||||
{
|
||||
// Not using supplied path parameter as it may be unresolved
|
||||
AZStd::array<char, MaxPathLength> resolvedPath{ {0} };
|
||||
fileIO->GetFilename(m_handle, resolvedPath.data(), resolvedPath.size() - 1);
|
||||
m_filename = resolvedPath.data();
|
||||
}
|
||||
else
|
||||
{
|
||||
// remember the file name so you can try again with ReOpen
|
||||
m_filename = path;
|
||||
}
|
||||
|
||||
AZ_PROFILE_INTERVAL_START_COLORED(AzCore, &m_filename, 0xff0000ff, "FileIO: %s", m_filename.c_str());
|
||||
return result;
|
||||
FileIOBase::GetInstance()->Close(m_handle);
|
||||
m_handle = InvalidHandle;
|
||||
m_ownsHandle = false;
|
||||
AZ_PROFILE_INTERVAL_END(AzCore, &m_filename);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileIOStream::ReOpen()
|
||||
bool FileIOStream::IsOpen() const
|
||||
{
|
||||
return (m_handle != InvalidHandle);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Retrieves underlying FileIO Handle from file stream
|
||||
\return HandleType
|
||||
*/
|
||||
HandleType FileIOStream::GetHandle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Retrieves filename
|
||||
\return const char*
|
||||
*/
|
||||
const char* FileIOStream::GetFilename() const
|
||||
{
|
||||
return m_filename.data();
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Retrieves OpenMode flags used to open this file
|
||||
\return OpenMode
|
||||
*/
|
||||
AZ::IO::OpenMode FileIOStream::GetModeFlags() const
|
||||
{
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
bool FileIOStream::CanSeek() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileIOStream::CanRead() const
|
||||
{
|
||||
return (m_mode & (OpenMode::ModeRead | OpenMode::ModeUpdate)) != OpenMode::Invalid;
|
||||
}
|
||||
|
||||
bool FileIOStream::CanWrite() const
|
||||
{
|
||||
return (m_mode & (OpenMode::ModeWrite | OpenMode::ModeAppend | OpenMode::ModeUpdate)) != OpenMode::Invalid;
|
||||
}
|
||||
|
||||
void FileIOStream::Seek(OffsetType bytes, SeekMode mode)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot seek on a FileIOStream that is not open.");
|
||||
|
||||
SeekType seekType = SeekType::SeekFromCurrent;
|
||||
switch (mode)
|
||||
{
|
||||
Close();
|
||||
return (m_mode != OpenMode::Invalid) ? Open(m_filename.data(), m_mode) : false;
|
||||
case GenericStream::ST_SEEK_BEGIN:
|
||||
seekType = SeekType::SeekFromStart;
|
||||
break;
|
||||
case GenericStream::ST_SEEK_CUR:
|
||||
seekType = SeekType::SeekFromCurrent;
|
||||
break;
|
||||
case GenericStream::ST_SEEK_END:
|
||||
seekType = SeekType::SeekFromEnd;
|
||||
break;
|
||||
default:
|
||||
seekType = SeekType::SeekFromCurrent;
|
||||
break;
|
||||
}
|
||||
|
||||
void FileIOStream::Close()
|
||||
const Result result = FileIOBase::GetInstance()->Seek(m_handle, static_cast<AZ::s64>(bytes), seekType);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "Seek failed.");
|
||||
}
|
||||
|
||||
SizeType FileIOStream::Read(SizeType bytes, void* oBuffer)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot read from a FileIOStream that is not open.");
|
||||
|
||||
AZ::u64 bytesRead = 0;
|
||||
const Result result = FileIOBase::GetInstance()->Read(m_handle, oBuffer, bytes, m_errorOnFailure, &bytesRead);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "Read failed in file %s.", m_filename.empty() ? "NULL" : m_filename.c_str());
|
||||
return static_cast<SizeType>(bytesRead);
|
||||
}
|
||||
|
||||
SizeType FileIOStream::Write(SizeType bytes, const void* iBuffer)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot write to a FileIOStream that is not open.");
|
||||
|
||||
AZ::u64 bytesWritten = 0;
|
||||
const Result result = FileIOBase::GetInstance()->Write(m_handle, iBuffer, bytes, &bytesWritten);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "Write failed.");
|
||||
return static_cast<SizeType>(bytesWritten);
|
||||
}
|
||||
|
||||
SizeType FileIOStream::GetCurPos() const
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot use a FileIOStream that is not open.");
|
||||
|
||||
AZ::u64 currentPosition = 0;
|
||||
const Result result = FileIOBase::GetInstance()->Tell(m_handle, currentPosition);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "GetCurPos failed.");
|
||||
return static_cast<SizeType>(currentPosition);
|
||||
}
|
||||
|
||||
SizeType FileIOStream::GetLength() const
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot use a FileIOStream that is not open.");
|
||||
|
||||
SizeType fileLengthBytes = 0;
|
||||
if (!FileIOBase::GetInstance()->Size(m_handle, fileLengthBytes))
|
||||
{
|
||||
if (m_handle != InvalidHandle)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
|
||||
FileIOBase::GetInstance()->Close(m_handle);
|
||||
m_handle = InvalidHandle;
|
||||
m_ownsHandle = false;
|
||||
AZ_PROFILE_INTERVAL_END(AzCore, &m_filename);
|
||||
}
|
||||
AZ_Error("FileIOStream", false, "GetLength failed.");
|
||||
}
|
||||
|
||||
bool FileIOStream::IsOpen() const
|
||||
{
|
||||
return (m_handle != InvalidHandle);
|
||||
}
|
||||
return fileLengthBytes;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Retrieves underlying FileIO Handle from file stream
|
||||
\return HandleType
|
||||
*/
|
||||
HandleType FileIOStream::GetHandle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Retrieves filename
|
||||
\return const char*
|
||||
*/
|
||||
const char* FileIOStream::GetFilename() const
|
||||
{
|
||||
return m_filename.data();
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Retrieves OpenMode flags used to open this file
|
||||
\return OpenMode
|
||||
*/
|
||||
AZ::IO::OpenMode FileIOStream::GetModeFlags() const
|
||||
{
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
bool FileIOStream::CanSeek() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileIOStream::CanRead() const
|
||||
{
|
||||
return (m_mode & (OpenMode::ModeRead | OpenMode::ModeUpdate)) != OpenMode::Invalid;
|
||||
}
|
||||
|
||||
bool FileIOStream::CanWrite() const
|
||||
{
|
||||
return (m_mode & (OpenMode::ModeWrite | OpenMode::ModeAppend | OpenMode::ModeUpdate)) != OpenMode::Invalid;
|
||||
}
|
||||
|
||||
void FileIOStream::Seek(OffsetType bytes, SeekMode mode)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot seek on a FileIOStream that is not open.");
|
||||
|
||||
SeekType seekType = SeekType::SeekFromCurrent;
|
||||
switch (mode)
|
||||
{
|
||||
case GenericStream::ST_SEEK_BEGIN:
|
||||
seekType = SeekType::SeekFromStart;
|
||||
break;
|
||||
case GenericStream::ST_SEEK_CUR:
|
||||
seekType = SeekType::SeekFromCurrent;
|
||||
break;
|
||||
case GenericStream::ST_SEEK_END:
|
||||
seekType = SeekType::SeekFromEnd;
|
||||
break;
|
||||
default:
|
||||
seekType = SeekType::SeekFromCurrent;
|
||||
break;
|
||||
}
|
||||
|
||||
const Result result = FileIOBase::GetInstance()->Seek(m_handle, static_cast<AZ::s64>(bytes), seekType);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "Seek failed.");
|
||||
}
|
||||
|
||||
SizeType FileIOStream::Read(SizeType bytes, void* oBuffer)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot read from a FileIOStream that is not open.");
|
||||
|
||||
AZ::u64 bytesRead = 0;
|
||||
const Result result = FileIOBase::GetInstance()->Read(m_handle, oBuffer, bytes, m_errorOnFailure, &bytesRead);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "Read failed in file %s.", m_filename.empty() ? "NULL" : m_filename.c_str());
|
||||
return static_cast<SizeType>(bytesRead);
|
||||
}
|
||||
|
||||
SizeType FileIOStream::Write(SizeType bytes, const void* iBuffer)
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot write to a FileIOStream that is not open.");
|
||||
|
||||
AZ::u64 bytesWritten = 0;
|
||||
const Result result = FileIOBase::GetInstance()->Write(m_handle, iBuffer, bytes, &bytesWritten);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "Write failed.");
|
||||
return static_cast<SizeType>(bytesWritten);
|
||||
}
|
||||
|
||||
SizeType FileIOStream::GetCurPos() const
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot use a FileIOStream that is not open.");
|
||||
|
||||
AZ::u64 currentPosition = 0;
|
||||
const Result result = FileIOBase::GetInstance()->Tell(m_handle, currentPosition);
|
||||
(void)result;
|
||||
AZ_Error("FileIOStream", result.GetResultCode() == ResultCode::Success, "GetCurPos failed.");
|
||||
return static_cast<SizeType>(currentPosition);
|
||||
}
|
||||
|
||||
SizeType FileIOStream::GetLength() const
|
||||
{
|
||||
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
|
||||
AZ_Assert(IsOpen(), "Cannot use a FileIOStream that is not open.");
|
||||
|
||||
SizeType fileLengthBytes = 0;
|
||||
if (!FileIOBase::GetInstance()->Size(m_handle, fileLengthBytes))
|
||||
{
|
||||
AZ_Error("FileIOStream", false, "GetLength failed.");
|
||||
}
|
||||
|
||||
return fileLengthBytes;
|
||||
}
|
||||
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -10,69 +10,64 @@
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/std/parallel/thread.h> /// this_thread sleep_for.
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
{
|
||||
int TranslateOpenModeToSystemFileMode(const char* path, OpenMode mode)
|
||||
int TranslateOpenModeToSystemFileMode(const char* path, OpenMode mode)
|
||||
{
|
||||
int systemFileMode = 0;
|
||||
bool read = AnyFlag(mode & OpenMode::ModeRead) || AnyFlag(mode & OpenMode::ModeUpdate);
|
||||
bool write = AnyFlag(mode & OpenMode::ModeWrite) || AnyFlag(mode & OpenMode::ModeUpdate) || AnyFlag(mode & OpenMode::ModeAppend);
|
||||
if (write)
|
||||
{
|
||||
int systemFileMode = 0;
|
||||
bool read = AnyFlag(mode & OpenMode::ModeRead) || AnyFlag(mode & OpenMode::ModeUpdate);
|
||||
bool write = AnyFlag(mode & OpenMode::ModeWrite) || AnyFlag(mode & OpenMode::ModeUpdate) || AnyFlag(mode & OpenMode::ModeAppend);
|
||||
if (write)
|
||||
// If writing the file, create the file in all cases (except r+)
|
||||
if (!SystemFile::Exists(path) && !(AnyFlag(mode & OpenMode::ModeRead) && AnyFlag(mode & OpenMode::ModeUpdate)))
|
||||
{
|
||||
// If writing the file, create the file in all cases (except r+)
|
||||
if (!SystemFile::Exists(path) && !(AnyFlag(mode & OpenMode::ModeRead) && AnyFlag(mode & OpenMode::ModeUpdate)))
|
||||
{
|
||||
// LocalFileIO creates by default
|
||||
systemFileMode |= SystemFile::SF_OPEN_CREATE;
|
||||
}
|
||||
|
||||
if (AnyFlag(mode & OpenMode::ModeCreatePath))
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_CREATE_PATH;
|
||||
}
|
||||
|
||||
// If appending, append.
|
||||
if (AnyFlag(mode & OpenMode::ModeAppend))
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_APPEND;
|
||||
}
|
||||
// If writing and not appending, empty the file
|
||||
else if (AnyFlag(mode & OpenMode::ModeWrite))
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_TRUNCATE;
|
||||
}
|
||||
|
||||
// If reading, set read/write, otherwise just write
|
||||
if (read)
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_READ_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_WRITE_ONLY;
|
||||
}
|
||||
}
|
||||
else if (read)
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_READ_ONLY;
|
||||
// LocalFileIO creates by default
|
||||
systemFileMode |= SystemFile::SF_OPEN_CREATE;
|
||||
}
|
||||
|
||||
return systemFileMode;
|
||||
if (AnyFlag(mode & OpenMode::ModeCreatePath))
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_CREATE_PATH;
|
||||
}
|
||||
|
||||
// If appending, append.
|
||||
if (AnyFlag(mode & OpenMode::ModeAppend))
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_APPEND;
|
||||
}
|
||||
// If writing and not appending, empty the file
|
||||
else if (AnyFlag(mode & OpenMode::ModeWrite))
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_TRUNCATE;
|
||||
}
|
||||
|
||||
// If reading, set read/write, otherwise just write
|
||||
if (read)
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_READ_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_WRITE_ONLY;
|
||||
}
|
||||
}
|
||||
else if (read)
|
||||
{
|
||||
systemFileMode |= SystemFile::SF_OPEN_READ_ONLY;
|
||||
}
|
||||
|
||||
bool RetryOpenStream(FileIOStream& stream, int numRetries, int delayBetweenRetry)
|
||||
return systemFileMode;
|
||||
}
|
||||
|
||||
bool RetryOpenStream(FileIOStream& stream, int numRetries, int delayBetweenRetry)
|
||||
{
|
||||
while ((!stream.IsOpen()) && (numRetries > 0))
|
||||
{
|
||||
while ((!stream.IsOpen()) && (numRetries > 0))
|
||||
{
|
||||
numRetries--;
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(delayBetweenRetry));
|
||||
stream.ReOpen();
|
||||
}
|
||||
return stream.IsOpen();
|
||||
numRetries--;
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(delayBetweenRetry));
|
||||
stream.ReOpen();
|
||||
}
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
|
||||
|
||||
return stream.IsOpen();
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,320 +12,317 @@
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
AZStd::shared_ptr<StreamStackEntry> DedicatedCacheConfig::AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent)
|
||||
{
|
||||
AZStd::shared_ptr<StreamStackEntry> DedicatedCacheConfig::AddStreamStackEntry(
|
||||
const HardwareInformation& hardware, AZStd::shared_ptr<StreamStackEntry> parent)
|
||||
size_t blockSize;
|
||||
switch (m_blockSize)
|
||||
{
|
||||
size_t blockSize;
|
||||
switch (m_blockSize)
|
||||
case BlockCacheConfig::BlockSize::MaxTransfer:
|
||||
blockSize = hardware.m_maxTransfer;
|
||||
break;
|
||||
case BlockCacheConfig::BlockSize::MemoryAlignment:
|
||||
blockSize = hardware.m_maxPhysicalSectorSize;
|
||||
break;
|
||||
case BlockCacheConfig::BlockSize::SizeAlignment:
|
||||
blockSize = hardware.m_maxLogicalSectorSize;
|
||||
break;
|
||||
default:
|
||||
blockSize = m_blockSize;
|
||||
break;
|
||||
}
|
||||
|
||||
u32 cacheSize = static_cast<AZ::u32>(m_cacheSizeMib * 1_mib);
|
||||
if (blockSize > cacheSize)
|
||||
{
|
||||
AZ_Warning("Streamer", false, "Size (%u) for DedicatedCache isn't big enough to hold at least one cache blocks of size (%zu). "
|
||||
"The cache size will be increased to fit one cache block.", cacheSize, blockSize);
|
||||
cacheSize = aznumeric_caster(blockSize);
|
||||
}
|
||||
|
||||
auto stackEntry = AZStd::make_shared<DedicatedCache>(
|
||||
cacheSize, aznumeric_cast<AZ::u32>(blockSize), aznumeric_cast<AZ::u32>(hardware.m_maxPhysicalSectorSize), m_writeOnlyEpilog);
|
||||
stackEntry->SetNext(AZStd::move(parent));
|
||||
return stackEntry;
|
||||
}
|
||||
|
||||
void DedicatedCacheConfig::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
{
|
||||
serializeContext->Class<DedicatedCacheConfig, IStreamerStackConfig>()
|
||||
->Version(1)
|
||||
->Field("CacheSizeMib", &DedicatedCacheConfig::m_cacheSizeMib)
|
||||
->Field("BlockSize", &DedicatedCacheConfig::m_blockSize)
|
||||
->Field("WriteOnlyEpilog", &DedicatedCacheConfig::m_writeOnlyEpilog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// DedicatedCache
|
||||
//
|
||||
|
||||
DedicatedCache::DedicatedCache(u64 cacheSize, u32 blockSize, u32 alignment, bool onlyEpilogWrites)
|
||||
: StreamStackEntry("Dedicated cache")
|
||||
, m_cacheSize(cacheSize)
|
||||
, m_alignment(alignment)
|
||||
, m_blockSize(blockSize)
|
||||
, m_onlyEpilogWrites(onlyEpilogWrites)
|
||||
{
|
||||
}
|
||||
|
||||
void DedicatedCache::SetNext(AZStd::shared_ptr<StreamStackEntry> next)
|
||||
{
|
||||
m_next = AZStd::move(next);
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->SetNext(m_next);
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::SetContext(StreamerContext& context)
|
||||
{
|
||||
StreamStackEntry::SetContext(context);
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->SetContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::PrepareRequest(FileRequest* request)
|
||||
{
|
||||
AZ_Assert(request, "PrepareRequest was provided a null request.");
|
||||
|
||||
// Claim the requests so other entries can't claim it and make updates.
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::CreateDedicatedCacheData>)
|
||||
{
|
||||
case BlockCacheConfig::BlockSize::MaxTransfer:
|
||||
blockSize = hardware.m_maxTransfer;
|
||||
break;
|
||||
case BlockCacheConfig::BlockSize::MemoryAlignment:
|
||||
blockSize = hardware.m_maxPhysicalSectorSize;
|
||||
break;
|
||||
case BlockCacheConfig::BlockSize::SizeAlignment:
|
||||
blockSize = hardware.m_maxLogicalSectorSize;
|
||||
break;
|
||||
default:
|
||||
blockSize = m_blockSize;
|
||||
break;
|
||||
args.m_range = FileRange::CreateRangeForEntireFile();
|
||||
m_context->PushPreparedRequest(request);
|
||||
}
|
||||
|
||||
u32 cacheSize = static_cast<AZ::u32>(m_cacheSizeMib * 1_mib);
|
||||
if (blockSize > cacheSize)
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::DestroyDedicatedCacheData>)
|
||||
{
|
||||
AZ_Warning("Streamer", false, "Size (%u) for DedicatedCache isn't big enough to hold at least one cache blocks of size (%zu). "
|
||||
"The cache size will be increased to fit one cache block.", cacheSize, blockSize);
|
||||
cacheSize = aznumeric_caster(blockSize);
|
||||
}
|
||||
|
||||
auto stackEntry = AZStd::make_shared<DedicatedCache>(
|
||||
cacheSize, aznumeric_cast<AZ::u32>(blockSize), aznumeric_cast<AZ::u32>(hardware.m_maxPhysicalSectorSize), m_writeOnlyEpilog);
|
||||
stackEntry->SetNext(AZStd::move(parent));
|
||||
return stackEntry;
|
||||
}
|
||||
|
||||
void DedicatedCacheConfig::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
{
|
||||
serializeContext->Class<DedicatedCacheConfig, IStreamerStackConfig>()
|
||||
->Version(1)
|
||||
->Field("CacheSizeMib", &DedicatedCacheConfig::m_cacheSizeMib)
|
||||
->Field("BlockSize", &DedicatedCacheConfig::m_blockSize)
|
||||
->Field("WriteOnlyEpilog", &DedicatedCacheConfig::m_writeOnlyEpilog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// DedicatedCache
|
||||
//
|
||||
|
||||
DedicatedCache::DedicatedCache(u64 cacheSize, u32 blockSize, u32 alignment, bool onlyEpilogWrites)
|
||||
: StreamStackEntry("Dedicated cache")
|
||||
, m_cacheSize(cacheSize)
|
||||
, m_alignment(alignment)
|
||||
, m_blockSize(blockSize)
|
||||
, m_onlyEpilogWrites(onlyEpilogWrites)
|
||||
{
|
||||
}
|
||||
|
||||
void DedicatedCache::SetNext(AZStd::shared_ptr<StreamStackEntry> next)
|
||||
{
|
||||
m_next = AZStd::move(next);
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->SetNext(m_next);
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::SetContext(StreamerContext& context)
|
||||
{
|
||||
StreamStackEntry::SetContext(context);
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->SetContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::PrepareRequest(FileRequest* request)
|
||||
{
|
||||
AZ_Assert(request, "PrepareRequest was provided a null request.");
|
||||
|
||||
// Claim the requests so other entries can't claim it and make updates.
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::CreateDedicatedCacheData>)
|
||||
{
|
||||
args.m_range = FileRange::CreateRangeForEntireFile();
|
||||
m_context->PushPreparedRequest(request);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::DestroyDedicatedCacheData>)
|
||||
{
|
||||
args.m_range = FileRange::CreateRangeForEntireFile();
|
||||
m_context->PushPreparedRequest(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
StreamStackEntry::PrepareRequest(request);
|
||||
}
|
||||
}, request->GetCommand());
|
||||
}
|
||||
|
||||
void DedicatedCache::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>)
|
||||
{
|
||||
ReadFile(request, args);
|
||||
return;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CreateDedicatedCacheData>)
|
||||
{
|
||||
CreateDedicatedCache(request, args);
|
||||
return;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::DestroyDedicatedCacheData>)
|
||||
{
|
||||
DestroyDedicatedCache(request, args);
|
||||
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();
|
||||
}
|
||||
StreamStackEntry::QueueRequest(request);
|
||||
}
|
||||
}, request->GetCommand());
|
||||
}
|
||||
|
||||
bool DedicatedCache::ExecuteRequests()
|
||||
{
|
||||
bool hasProcessedRequest = false;
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
hasProcessedRequest = cache->ExecuteRequests() || hasProcessedRequest;
|
||||
}
|
||||
return StreamStackEntry::ExecuteRequests() || hasProcessedRequest;
|
||||
}
|
||||
|
||||
void DedicatedCache::UpdateStatus(Status& status) const
|
||||
{
|
||||
// Available slots are not updated because the dedicated caches are often
|
||||
// small and specific to a tiny subset of files that are loaded. It would therefore
|
||||
// return a small number of slots that would needlessly hamper streaming as it doesn't
|
||||
// apply to the majority of files.
|
||||
|
||||
bool isIdle = true;
|
||||
for (auto& cache : m_cachedFileCaches)
|
||||
{
|
||||
Status blockStatus;
|
||||
cache->UpdateStatus(blockStatus);
|
||||
isIdle = isIdle && blockStatus.m_isIdle;
|
||||
}
|
||||
status.m_isIdle = status.m_isIdle && isIdle;
|
||||
StreamStackEntry::UpdateStatus(status);
|
||||
}
|
||||
|
||||
void DedicatedCache::UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now,
|
||||
AZStd::vector<FileRequest*>& internalPending, StreamerContext::PreparedQueue::iterator pendingBegin,
|
||||
StreamerContext::PreparedQueue::iterator pendingEnd)
|
||||
{
|
||||
for (auto& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->AddDelayedRequests(internalPending);
|
||||
}
|
||||
|
||||
StreamStackEntry::UpdateCompletionEstimates(now, internalPending, pendingBegin, pendingEnd);
|
||||
|
||||
for (auto& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->UpdatePendingRequestEstimations();
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::ReadFile(FileRequest* request, FileRequest::ReadData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_offset);
|
||||
if (index == s_fileNotFound)
|
||||
{
|
||||
m_usagePercentageStat.PushSample(0.0);
|
||||
if (m_next)
|
||||
{
|
||||
m_next->QueueRequest(request);
|
||||
}
|
||||
args.m_range = FileRange::CreateRangeForEntireFile();
|
||||
m_context->PushPreparedRequest(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_usagePercentageStat.PushSample(1.0);
|
||||
BlockCache& cache = *m_cachedFileCaches[index];
|
||||
cache.QueueRequest(request);
|
||||
#if AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
|
||||
m_overallHitRateStat.PushSample(cache.CalculateHitRatePercentage());
|
||||
m_overallCacheableRateStat.PushSample(cache.CalculateCacheableRatePercentage());
|
||||
#endif
|
||||
StreamStackEntry::PrepareRequest(request);
|
||||
}
|
||||
}
|
||||
}, request->GetCommand());
|
||||
}
|
||||
|
||||
void DedicatedCache::FlushCache(const RequestPath& filePath)
|
||||
void DedicatedCache::QueueRequest(FileRequest* request)
|
||||
{
|
||||
AZ_Assert(request, "QueueRequest was provided a null request.");
|
||||
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
size_t count = m_cachedFileNames.size();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
{
|
||||
if (m_cachedFileNames[i] == filePath)
|
||||
{
|
||||
// Flush the entire block cache as it's entirely dedicated to the found file.
|
||||
m_cachedFileCaches[i]->FlushEntireCache();
|
||||
}
|
||||
ReadFile(request, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::FlushEntireCache()
|
||||
{
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CreateDedicatedCacheData>)
|
||||
{
|
||||
cache->FlushEntireCache();
|
||||
CreateDedicatedCache(request, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::CollectStatistics(AZStd::vector<Statistic>& statistics) const
|
||||
{
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, "Reads from dedicated cache", m_usagePercentageStat.GetAverage()));
|
||||
#if AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, "Overall cacheable rate", m_overallCacheableRateStat.GetAverage()));
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, "Overall hit rate", m_overallHitRateStat.GetAverage()));
|
||||
#endif
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, "Num dedicated caches", aznumeric_caster(m_cachedFileNames.size())));
|
||||
StreamStackEntry::CollectStatistics(statistics);
|
||||
}
|
||||
|
||||
void DedicatedCache::CreateDedicatedCache(FileRequest* request, FileRequest::CreateDedicatedCacheData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_range);
|
||||
if (index == s_fileNotFound)
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::DestroyDedicatedCacheData>)
|
||||
{
|
||||
index = m_cachedFileCaches.size();
|
||||
m_cachedFileNames.push_back(data.m_path);
|
||||
m_cachedFileRanges.push_back(data.m_range);
|
||||
m_cachedFileCaches.push_back(AZStd::make_unique<BlockCache>(m_cacheSize, m_blockSize, m_alignment, m_onlyEpilogWrites));
|
||||
m_cachedFileCaches[index]->SetNext(m_next);
|
||||
m_cachedFileCaches[index]->SetContext(*m_context);
|
||||
m_cachedFileRefCounts.push_back(1);
|
||||
DestroyDedicatedCache(request, args);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
++m_cachedFileRefCounts[index];
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::FlushData>)
|
||||
{
|
||||
FlushCache(args.m_path);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FlushAllData>)
|
||||
{
|
||||
FlushEntireCache();
|
||||
}
|
||||
StreamStackEntry::QueueRequest(request);
|
||||
}
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Completed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
}, request->GetCommand());
|
||||
}
|
||||
|
||||
bool DedicatedCache::ExecuteRequests()
|
||||
{
|
||||
bool hasProcessedRequest = false;
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
hasProcessedRequest = cache->ExecuteRequests() || hasProcessedRequest;
|
||||
}
|
||||
return StreamStackEntry::ExecuteRequests() || hasProcessedRequest;
|
||||
}
|
||||
|
||||
void DedicatedCache::UpdateStatus(Status& status) const
|
||||
{
|
||||
// Available slots are not updated because the dedicated caches are often
|
||||
// small and specific to a tiny subset of files that are loaded. It would therefore
|
||||
// return a small number of slots that would needlessly hamper streaming as it doesn't
|
||||
// apply to the majority of files.
|
||||
|
||||
bool isIdle = true;
|
||||
for (auto& cache : m_cachedFileCaches)
|
||||
{
|
||||
Status blockStatus;
|
||||
cache->UpdateStatus(blockStatus);
|
||||
isIdle = isIdle && blockStatus.m_isIdle;
|
||||
}
|
||||
status.m_isIdle = status.m_isIdle && isIdle;
|
||||
StreamStackEntry::UpdateStatus(status);
|
||||
}
|
||||
|
||||
void DedicatedCache::UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now,
|
||||
AZStd::vector<FileRequest*>& internalPending, StreamerContext::PreparedQueue::iterator pendingBegin,
|
||||
StreamerContext::PreparedQueue::iterator pendingEnd)
|
||||
{
|
||||
for (auto& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->AddDelayedRequests(internalPending);
|
||||
}
|
||||
|
||||
void DedicatedCache::DestroyDedicatedCache(FileRequest* request, FileRequest::DestroyDedicatedCacheData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_range);
|
||||
if (index != s_fileNotFound)
|
||||
{
|
||||
if (m_cachedFileRefCounts[index] > 0)
|
||||
{
|
||||
--m_cachedFileRefCounts[index];
|
||||
if (m_cachedFileRefCounts[index] == 0)
|
||||
{
|
||||
m_cachedFileNames.erase(m_cachedFileNames.begin() + index);
|
||||
m_cachedFileRanges.erase(m_cachedFileRanges.begin() + index);
|
||||
m_cachedFileCaches.erase(m_cachedFileCaches.begin() + index);
|
||||
m_cachedFileRefCounts.erase(m_cachedFileRefCounts.begin() + index);
|
||||
}
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Completed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Failed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
}
|
||||
StreamStackEntry::UpdateCompletionEstimates(now, internalPending, pendingBegin, pendingEnd);
|
||||
|
||||
size_t DedicatedCache::FindCache(const RequestPath& filename, FileRange range)
|
||||
for (auto& cache : m_cachedFileCaches)
|
||||
{
|
||||
size_t count = m_cachedFileNames.size();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (m_cachedFileNames[i] == filename && m_cachedFileRanges[i] == range)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return s_fileNotFound;
|
||||
cache->UpdatePendingRequestEstimations();
|
||||
}
|
||||
}
|
||||
|
||||
size_t DedicatedCache::FindCache(const RequestPath& filename, u64 offset)
|
||||
void DedicatedCache::ReadFile(FileRequest* request, FileRequest::ReadData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_offset);
|
||||
if (index == s_fileNotFound)
|
||||
{
|
||||
size_t count = m_cachedFileNames.size();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
m_usagePercentageStat.PushSample(0.0);
|
||||
if (m_next)
|
||||
{
|
||||
if (m_cachedFileNames[i] == filename && m_cachedFileRanges[i].IsInRange(offset))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
m_next->QueueRequest(request);
|
||||
}
|
||||
return s_fileNotFound;
|
||||
}
|
||||
} // namespace IO
|
||||
} // namespace AZ
|
||||
else
|
||||
{
|
||||
m_usagePercentageStat.PushSample(1.0);
|
||||
BlockCache& cache = *m_cachedFileCaches[index];
|
||||
cache.QueueRequest(request);
|
||||
#if AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
|
||||
m_overallHitRateStat.PushSample(cache.CalculateHitRatePercentage());
|
||||
m_overallCacheableRateStat.PushSample(cache.CalculateCacheableRatePercentage());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::FlushCache(const RequestPath& filePath)
|
||||
{
|
||||
size_t count = m_cachedFileNames.size();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (m_cachedFileNames[i] == filePath)
|
||||
{
|
||||
// Flush the entire block cache as it's entirely dedicated to the found file.
|
||||
m_cachedFileCaches[i]->FlushEntireCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::FlushEntireCache()
|
||||
{
|
||||
for (AZStd::unique_ptr<BlockCache>& cache : m_cachedFileCaches)
|
||||
{
|
||||
cache->FlushEntireCache();
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::CollectStatistics(AZStd::vector<Statistic>& statistics) const
|
||||
{
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, "Reads from dedicated cache", m_usagePercentageStat.GetAverage()));
|
||||
#if AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, "Overall cacheable rate", m_overallCacheableRateStat.GetAverage()));
|
||||
statistics.push_back(Statistic::CreatePercentage(m_name, "Overall hit rate", m_overallHitRateStat.GetAverage()));
|
||||
#endif
|
||||
statistics.push_back(Statistic::CreateInteger(m_name, "Num dedicated caches", aznumeric_caster(m_cachedFileNames.size())));
|
||||
StreamStackEntry::CollectStatistics(statistics);
|
||||
}
|
||||
|
||||
void DedicatedCache::CreateDedicatedCache(FileRequest* request, FileRequest::CreateDedicatedCacheData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_range);
|
||||
if (index == s_fileNotFound)
|
||||
{
|
||||
index = m_cachedFileCaches.size();
|
||||
m_cachedFileNames.push_back(data.m_path);
|
||||
m_cachedFileRanges.push_back(data.m_range);
|
||||
m_cachedFileCaches.push_back(AZStd::make_unique<BlockCache>(m_cacheSize, m_blockSize, m_alignment, m_onlyEpilogWrites));
|
||||
m_cachedFileCaches[index]->SetNext(m_next);
|
||||
m_cachedFileCaches[index]->SetContext(*m_context);
|
||||
m_cachedFileRefCounts.push_back(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
++m_cachedFileRefCounts[index];
|
||||
}
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Completed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
}
|
||||
|
||||
void DedicatedCache::DestroyDedicatedCache(FileRequest* request, FileRequest::DestroyDedicatedCacheData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_range);
|
||||
if (index != s_fileNotFound)
|
||||
{
|
||||
if (m_cachedFileRefCounts[index] > 0)
|
||||
{
|
||||
--m_cachedFileRefCounts[index];
|
||||
if (m_cachedFileRefCounts[index] == 0)
|
||||
{
|
||||
m_cachedFileNames.erase(m_cachedFileNames.begin() + index);
|
||||
m_cachedFileRanges.erase(m_cachedFileRanges.begin() + index);
|
||||
m_cachedFileCaches.erase(m_cachedFileCaches.begin() + index);
|
||||
m_cachedFileRefCounts.erase(m_cachedFileRefCounts.begin() + index);
|
||||
}
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Completed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
request->SetStatus(IStreamerTypes::RequestStatus::Failed);
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
}
|
||||
|
||||
size_t DedicatedCache::FindCache(const RequestPath& filename, FileRange range)
|
||||
{
|
||||
size_t count = m_cachedFileNames.size();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (m_cachedFileNames[i] == filename && m_cachedFileRanges[i] == range)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return s_fileNotFound;
|
||||
}
|
||||
|
||||
size_t DedicatedCache::FindCache(const RequestPath& filename, u64 offset)
|
||||
{
|
||||
size_t count = m_cachedFileNames.size();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (m_cachedFileNames[i] == filename && m_cachedFileRanges[i].IsInRange(offset))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return s_fileNotFound;
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
@@ -8,104 +8,101 @@
|
||||
|
||||
#include <AzCore/IO/Streamer/FileRange.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace IO
|
||||
FileRange FileRange::CreateRange(u64 offset, u64 size)
|
||||
{
|
||||
FileRange FileRange::CreateRange(u64 offset, u64 size)
|
||||
{
|
||||
FileRange result;
|
||||
result.m_hasOffsetEndSet = true;
|
||||
result.m_isEntireFile = false;
|
||||
result.m_offsetBegin = offset;
|
||||
result.m_offsetEnd = offset + size;
|
||||
return result;
|
||||
}
|
||||
FileRange result;
|
||||
result.m_hasOffsetEndSet = true;
|
||||
result.m_isEntireFile = false;
|
||||
result.m_offsetBegin = offset;
|
||||
result.m_offsetEnd = offset + size;
|
||||
return result;
|
||||
}
|
||||
|
||||
FileRange FileRange::CreateRangeForEntireFile()
|
||||
{
|
||||
FileRange result;
|
||||
result.m_hasOffsetEndSet = false;
|
||||
result.m_isEntireFile = true;
|
||||
result.m_offsetBegin = 0;
|
||||
result.m_offsetEnd = (static_cast<u64>(1) << 63) - 1;
|
||||
return result;
|
||||
}
|
||||
FileRange FileRange::CreateRangeForEntireFile()
|
||||
{
|
||||
FileRange result;
|
||||
result.m_hasOffsetEndSet = false;
|
||||
result.m_isEntireFile = true;
|
||||
result.m_offsetBegin = 0;
|
||||
result.m_offsetEnd = (static_cast<u64>(1) << 63) - 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
FileRange FileRange::CreateRangeForEntireFile(u64 fileSize)
|
||||
{
|
||||
FileRange result;
|
||||
result.m_hasOffsetEndSet = true;
|
||||
result.m_isEntireFile = true;
|
||||
result.m_offsetBegin = 0;
|
||||
result.m_offsetEnd = fileSize;
|
||||
return result;
|
||||
}
|
||||
FileRange FileRange::CreateRangeForEntireFile(u64 fileSize)
|
||||
{
|
||||
FileRange result;
|
||||
result.m_hasOffsetEndSet = true;
|
||||
result.m_isEntireFile = true;
|
||||
result.m_offsetBegin = 0;
|
||||
result.m_offsetEnd = fileSize;
|
||||
return result;
|
||||
}
|
||||
|
||||
FileRange::FileRange()
|
||||
: m_isEntireFile(false)
|
||||
, m_offsetBegin(0)
|
||||
, m_hasOffsetEndSet(false)
|
||||
, m_offsetEnd(0)
|
||||
{
|
||||
}
|
||||
FileRange::FileRange()
|
||||
: m_isEntireFile(false)
|
||||
, m_offsetBegin(0)
|
||||
, m_hasOffsetEndSet(false)
|
||||
, m_offsetEnd(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool FileRange::operator==(const FileRange& rhs) const
|
||||
bool FileRange::operator==(const FileRange& rhs) const
|
||||
{
|
||||
if (m_isEntireFile)
|
||||
{
|
||||
if (m_isEntireFile)
|
||||
{
|
||||
return rhs.m_isEntireFile && m_offsetBegin == rhs.m_offsetBegin;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_offsetBegin == rhs.m_offsetBegin && m_offsetEnd == rhs.m_offsetEnd;
|
||||
}
|
||||
return rhs.m_isEntireFile && m_offsetBegin == rhs.m_offsetBegin;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_offsetBegin == rhs.m_offsetBegin && m_offsetEnd == rhs.m_offsetEnd;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileRange::operator!=(const FileRange& rhs) const
|
||||
bool FileRange::operator!=(const FileRange& rhs) const
|
||||
{
|
||||
if (m_isEntireFile)
|
||||
{
|
||||
if (m_isEntireFile)
|
||||
{
|
||||
return !rhs.m_isEntireFile || m_offsetBegin != rhs.m_offsetBegin;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_offsetBegin != rhs.m_offsetBegin || m_offsetEnd != rhs.m_offsetEnd;
|
||||
}
|
||||
return !rhs.m_isEntireFile || m_offsetBegin != rhs.m_offsetBegin;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_offsetBegin != rhs.m_offsetBegin || m_offsetEnd != rhs.m_offsetEnd;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileRange::IsEntireFile() const
|
||||
{
|
||||
return m_isEntireFile != 0;
|
||||
}
|
||||
bool FileRange::IsEntireFile() const
|
||||
{
|
||||
return m_isEntireFile != 0;
|
||||
}
|
||||
|
||||
bool FileRange::IsSizeKnown() const
|
||||
{
|
||||
// m_hasOffsetEndSet being zero has the special meaning that the file size has not
|
||||
// specifically been set yet.
|
||||
return m_hasOffsetEndSet != 0;
|
||||
}
|
||||
bool FileRange::IsSizeKnown() const
|
||||
{
|
||||
// m_hasOffsetEndSet being zero has the special meaning that the file size has not
|
||||
// specifically been set yet.
|
||||
return m_hasOffsetEndSet != 0;
|
||||
}
|
||||
|
||||
bool FileRange::IsInRange(u64 offset) const
|
||||
{
|
||||
return m_offsetBegin <= offset && offset < m_offsetEnd;
|
||||
}
|
||||
bool FileRange::IsInRange(u64 offset) const
|
||||
{
|
||||
return m_offsetBegin <= offset && offset < m_offsetEnd;
|
||||
}
|
||||
|
||||
u64 FileRange::GetOffset() const
|
||||
{
|
||||
return m_offsetBegin;
|
||||
}
|
||||
u64 FileRange::GetOffset() const
|
||||
{
|
||||
return m_offsetBegin;
|
||||
}
|
||||
|
||||
u64 FileRange::GetSize() const
|
||||
{
|
||||
AZ_Assert(m_hasOffsetEndSet, "Calling GetSize on a FileRange that doesn't have a size specified.");
|
||||
return m_offsetEnd - m_offsetBegin;
|
||||
}
|
||||
u64 FileRange::GetSize() const
|
||||
{
|
||||
AZ_Assert(m_hasOffsetEndSet, "Calling GetSize on a FileRange that doesn't have a size specified.");
|
||||
return m_offsetEnd - m_offsetBegin;
|
||||
}
|
||||
|
||||
u64 FileRange::GetEndPoint() const
|
||||
{
|
||||
AZ_Assert(m_hasOffsetEndSet, "Calling GetEndPoint on a FileRange that doesn't have an end offset specified.");
|
||||
return m_offsetEnd;
|
||||
}
|
||||
} // namespace IO
|
||||
} // namesapce AZ
|
||||
u64 FileRange::GetEndPoint() const
|
||||
{
|
||||
AZ_Assert(m_hasOffsetEndSet, "Calling GetEndPoint on a FileRange that doesn't have an end offset specified.");
|
||||
return m_offsetEnd;
|
||||
}
|
||||
} // namespace AZ::IO
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user