Merging last dev

Signed-off-by: Gene Walters <genewalt@amazon.com>
This commit is contained in:
Gene Walters
2021-11-19 23:12:59 -08:00
2657 changed files with 249197 additions and 30535 deletions
+3 -28
View File
@@ -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
-3
View File
@@ -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;
+1 -1
View File
@@ -125,7 +125,7 @@
</size>
</property>
<property name="text">
<string>General Availability</string>
<string>development</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
-9
View File
@@ -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);
-1
View File
@@ -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
+14 -9
View File
@@ -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
View File
@@ -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();
+1
View File
@@ -212,6 +212,7 @@ public:
void OnEditFetch();
void OnFileExportToGameNoSurfaceTexture();
void OnViewSwitchToGame();
void OnViewSwitchToGameFullScreen();
void OnViewDeploy();
void DeleteSelectedEntities(bool includeDescendants);
void OnMoveObject();
+9 -4
View File
@@ -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
+2 -2
View File
@@ -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");
}
//////////////////////////////////////////////////////////////////////////
-1
View File
@@ -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);
}
}
+27 -114
View File
@@ -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())
{
+4 -7
View File
@@ -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;
+2 -2
View File
@@ -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);
}
}
-4
View File
@@ -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;
-3
View File
@@ -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>
-3
View File
@@ -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
View File
@@ -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);
+3 -43
View File
@@ -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;
}
-5
View File
@@ -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);
-2
View File
@@ -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;
}
+6 -18
View File
@@ -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");
}
-1
View File
@@ -131,7 +131,6 @@ public:
enum EAttachmentType
{
eAT_Pivot,
eAT_GeomCacheNode,
eAT_CharacterBone,
};
-8
View File
@@ -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());
}
+3 -7
View File
@@ -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);
}
+1
View File
@@ -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
+6 -6
View File
@@ -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();
}
}
//////////////////////////////////////////////////////////////////////////
+1 -2
View File
@@ -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;
+1 -1
View File
@@ -103,7 +103,7 @@
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>General Availability</string>
<string>development</string>
</property>
</widget>
</item>
+35 -4
View File
@@ -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()
+6
View File
@@ -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 */)
+4 -140
View File
@@ -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)
-5
View File
@@ -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);
-10
View File
@@ -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);
-2
View File
@@ -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
+2 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+33 -23
View File
@@ -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
+10 -2
View File
@@ -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
-34
View File
@@ -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)
-11
View File
@@ -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);
-1
View File
@@ -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>
+414 -417
View File
@@ -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
+33 -2
View File
@@ -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
+236 -239
View File
@@ -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
+22 -25
View File
@@ -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
+53 -60
View File
@@ -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
+393 -396
View File
@@ -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)
+392 -395
View File
@@ -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
+52 -57
View File
@@ -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