Merge branch 'development' of https://github.com/o3de/o3de into cgalvan/FixedTrackViewNodeCrash

monroegm-disable-blank-issue-2
Chris Galvan 4 years ago
commit d64c034fee

@ -0,0 +1,9 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
ly_install_directory(DIRECTORIES .)

@ -19,6 +19,19 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
COMPONENT
Physics
)
ly_add_pytest(
NAME AutomatedTesting::PhysicsTests_Main_Optimized
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_Optimized.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Physics
)
ly_add_pytest(
NAME AutomatedTesting::PhysicsTests_Periodic
TEST_SUITE periodic

@ -0,0 +1,349 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import pytest
import os
import sys
import inspect
from ly_test_tools import LAUNCHERS
from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite
from .FileManagement import FileManagement as fm
# Custom test spec, it provides functionality to override files
class EditorSingleTest_WithFileOverrides(EditorSingleTest):
# Specify here what files to override, [(original, override), ...]
files_to_override = [()]
# Base directory of the files (Default path is {ProjectName})
base_dir = None
# True will will search sub-directories for the files in base
search_subdirs = False
@classmethod
def wrap_run(cls, instance, request, workspace, editor, editor_test_results, launcher_platform):
root_path = cls.base_dir
if root_path is not None:
root_path = os.path.join(workspace.paths.engine_root(), root_path)
else:
# Default to project folder
root_path = workspace.paths.project()
# Try to locate both target and source files
original_file_list, override_file_list = zip(*cls.files_to_override)
try:
file_list = fm._find_files(original_file_list + override_file_list, root_path, cls.search_subdirs)
except RuntimeWarning as w:
assert False, (
w.message
+ " Please check use of search_subdirs; make sure you are using the correct parent directory."
)
for f in original_file_list:
fm._restore_file(f, file_list[f])
fm._backup_file(f, file_list[f])
for original, override in cls.files_to_override:
fm._copy_file(override, file_list[override], original, file_list[override])
yield # Run Test
for f in original_file_list:
fm._restore_file(f, file_list[f])
@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarly.")
@pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
@staticmethod
def get_number_parallel_editors():
return 16
#########################################
# Non-atomic tests: These need to be run in a single editor because they have custom setup and teardown
class C4044459_Material_DynamicFriction(EditorSingleTest_WithFileOverrides):
from . import C4044459_Material_DynamicFriction as test_module
files_to_override = [
('physxsystemconfiguration.setreg', 'C4044459_Material_DynamicFriction.setreg_override')
]
base_dir = "AutomatedTesting/Registry"
class C4982593_PhysXCollider_CollisionLayerTest(EditorSingleTest_WithFileOverrides):
from . import C4982593_PhysXCollider_CollisionLayerTest as test_module
files_to_override = [
('physxsystemconfiguration.setreg', 'C4982593_PhysXCollider_CollisionLayer.setreg_override')
]
base_dir = "AutomatedTesting/Registry"
#########################################
class C111111_RigidBody_EnablingGravityWorksUsingNotificationsPoC(EditorSharedTest):
from . import C111111_RigidBody_EnablingGravityWorksUsingNotificationsPoC as test_module
class C5932041_PhysXForceRegion_LocalSpaceForceOnRigidBodies(EditorSharedTest):
from . import C5932041_PhysXForceRegion_LocalSpaceForceOnRigidBodies as test_module
class C15425929_Undo_Redo(EditorSharedTest):
from . import C15425929_Undo_Redo as test_module
class C4976243_Collision_SameCollisionGroupDiffCollisionLayers(EditorSharedTest):
from . import C4976243_Collision_SameCollisionGroupDiffCollisionLayers as test_module
class C14654881_CharacterController_SwitchLevels(EditorSharedTest):
from . import C14654881_CharacterController_SwitchLevels as test_module
class C17411467_AddPhysxRagdollComponent(EditorSharedTest):
from . import C17411467_AddPhysxRagdollComponent as test_module
class C12712453_ScriptCanvas_MultipleRaycastNode(EditorSharedTest):
from . import C12712453_ScriptCanvas_MultipleRaycastNode as test_module
class C18243586_Joints_HingeLeadFollowerCollide(EditorSharedTest):
from . import C18243586_Joints_HingeLeadFollowerCollide as test_module
class C4982803_Enable_PxMesh_Option(EditorSharedTest):
from . import C4982803_Enable_PxMesh_Option as test_module
class C24308873_CylinderShapeCollider_CollidesWithPhysXTerrain(EditorSharedTest):
from . import C24308873_CylinderShapeCollider_CollidesWithPhysXTerrain as test_module
class C3510642_Terrain_NotCollideWithTerrain(EditorSharedTest):
from . import C3510642_Terrain_NotCollideWithTerrain as test_module
class C4976195_RigidBodies_InitialLinearVelocity(EditorSharedTest):
from . import C4976195_RigidBodies_InitialLinearVelocity as test_module
class C4976206_RigidBodies_GravityEnabledActive(EditorSharedTest):
from . import C4976206_RigidBodies_GravityEnabledActive as test_module
class C4976207_PhysXRigidBodies_KinematicBehavior(EditorSharedTest):
from . import C4976207_PhysXRigidBodies_KinematicBehavior as test_module
class C5932042_PhysXForceRegion_LinearDamping(EditorSharedTest):
from . import C5932042_PhysXForceRegion_LinearDamping as test_module
class C5932043_ForceRegion_SimpleDragOnRigidBodies(EditorSharedTest):
from . import C5932043_ForceRegion_SimpleDragOnRigidBodies as test_module
class C5959760_PhysXForceRegion_PointForceExertion(EditorSharedTest):
from . import C5959760_PhysXForceRegion_PointForceExertion as test_module
class C5959764_ForceRegion_ForceRegionImpulsesCapsule(EditorSharedTest):
from . import C5959764_ForceRegion_ForceRegionImpulsesCapsule as test_module
class C5340400_RigidBody_ManualMomentOfInertia(EditorSharedTest):
from . import C5340400_RigidBody_ManualMomentOfInertia as test_module
class C4976210_COM_ManualSetting(EditorSharedTest):
from . import C4976210_COM_ManualSetting as test_module
class C4976194_RigidBody_PhysXComponentIsValid(EditorSharedTest):
from . import C4976194_RigidBody_PhysXComponentIsValid as test_module
class C5932045_ForceRegion_Spline(EditorSharedTest):
from . import C5932045_ForceRegion_Spline as test_module
class C4982797_Collider_ColliderOffset(EditorSharedTest):
from . import C4982797_Collider_ColliderOffset as test_module
class C4976200_RigidBody_AngularDampingObjectRotation(EditorSharedTest):
from . import C4976200_RigidBody_AngularDampingObjectRotation as test_module
class C5689529_Verify_Terrain_RigidBody_Collider_Mesh(EditorSharedTest):
from . import C5689529_Verify_Terrain_RigidBody_Collider_Mesh as test_module
class C5959810_ForceRegion_ForceRegionCombinesForces(EditorSharedTest):
from . import C5959810_ForceRegion_ForceRegionCombinesForces as test_module
class C5959765_ForceRegion_AssetGetsImpulsed(EditorSharedTest):
from . import C5959765_ForceRegion_AssetGetsImpulsed as test_module
class C6274125_ScriptCanvas_TriggerEvents(EditorSharedTest):
from . import C6274125_ScriptCanvas_TriggerEvents as test_module
# needs to be updated to log for unexpected lines
# expected_lines = test_module.LogLines.expected_lines
class C6090554_ForceRegion_PointForceNegative(EditorSharedTest):
from . import C6090554_ForceRegion_PointForceNegative as test_module
class C6090550_ForceRegion_WorldSpaceForceNegative(EditorSharedTest):
from . import C6090550_ForceRegion_WorldSpaceForceNegative as test_module
class C6090552_ForceRegion_LinearDampingNegative(EditorSharedTest):
from . import C6090552_ForceRegion_LinearDampingNegative as test_module
class C5968760_ForceRegion_CheckNetForceChange(EditorSharedTest):
from . import C5968760_ForceRegion_CheckNetForceChange as test_module
class C12712452_ScriptCanvas_CollisionEvents(EditorSharedTest):
from . import C12712452_ScriptCanvas_CollisionEvents as test_module
class C12868578_ForceRegion_DirectionHasNoAffectOnMagnitude(EditorSharedTest):
from . import C12868578_ForceRegion_DirectionHasNoAffectOnMagnitude as test_module
class C4976204_Verify_Start_Asleep_Condition(EditorSharedTest):
from . import C4976204_Verify_Start_Asleep_Condition as test_module
class C6090546_ForceRegion_SliceFileInstantiates(EditorSharedTest):
from . import C6090546_ForceRegion_SliceFileInstantiates as test_module
class C6090551_ForceRegion_LocalSpaceForceNegative(EditorSharedTest):
from . import C6090551_ForceRegion_LocalSpaceForceNegative as test_module
class C6090553_ForceRegion_SimpleDragForceOnRigidBodies(EditorSharedTest):
from . import C6090553_ForceRegion_SimpleDragForceOnRigidBodies as test_module
class C4976209_RigidBody_ComputesCOM(EditorSharedTest):
from . import C4976209_RigidBody_ComputesCOM as test_module
class C4976201_RigidBody_MassIsAssigned(EditorSharedTest):
from . import C4976201_RigidBody_MassIsAssigned as test_module
class C12868580_ForceRegion_SplineModifiedTransform(EditorSharedTest):
from . import C12868580_ForceRegion_SplineModifiedTransform as test_module
class C12712455_ScriptCanvas_ShapeCastVerification(EditorSharedTest):
from . import C12712455_ScriptCanvas_ShapeCastVerification as test_module
class C4976197_RigidBodies_InitialAngularVelocity(EditorSharedTest):
from . import C4976197_RigidBodies_InitialAngularVelocity as test_module
class C6090555_ForceRegion_SplineFollowOnRigidBodies(EditorSharedTest):
from . import C6090555_ForceRegion_SplineFollowOnRigidBodies as test_module
class C6131473_StaticSlice_OnDynamicSliceSpawn(EditorSharedTest):
from . import C6131473_StaticSlice_OnDynamicSliceSpawn as test_module
class C5959808_ForceRegion_PositionOffset(EditorSharedTest):
from . import C5959808_ForceRegion_PositionOffset as test_module
@pytest.mark.xfail(reason="Something with the CryRenderer disabling is causing this test to fail now.")
class C13895144_Ragdoll_ChangeLevel(EditorSharedTest):
from . import C13895144_Ragdoll_ChangeLevel as test_module
class C5968759_ForceRegion_ExertsSeveralForcesOnRigidBody(EditorSharedTest):
from . import C5968759_ForceRegion_ExertsSeveralForcesOnRigidBody as test_module
@pytest.mark.xfail(reason="This test will sometimes fail as the ball will continue to roll before the timeout is reached.")
class C4976202_RigidBody_StopsWhenBelowKineticThreshold(EditorSharedTest):
from . import C4976202_RigidBody_StopsWhenBelowKineticThreshold as test_module
class C13351703_COM_NotIncludeTriggerShapes(EditorSharedTest):
from . import C13351703_COM_NotIncludeTriggerShapes as test_module
class C5296614_PhysXMaterial_ColliderShape(EditorSharedTest):
from . import C5296614_PhysXMaterial_ColliderShape as test_module
class C4982595_Collider_TriggerDisablesCollision(EditorSharedTest):
from . import C4982595_Collider_TriggerDisablesCollision as test_module
class C14976307_Gravity_SetGravityWorks(EditorSharedTest):
from . import C14976307_Gravity_SetGravityWorks as test_module
class C4044694_Material_EmptyLibraryUsesDefault(EditorSharedTest):
from . import C4044694_Material_EmptyLibraryUsesDefault as test_module
class C15845879_ForceRegion_HighLinearDampingForce(EditorSharedTest):
from . import C15845879_ForceRegion_HighLinearDampingForce as test_module
class C4976218_RigidBodies_InertiaObjectsNotComputed(EditorSharedTest):
from . import C4976218_RigidBodies_InertiaObjectsNotComputed as test_module
class C14902098_ScriptCanvas_PostPhysicsUpdate(EditorSharedTest):
from . import C14902098_ScriptCanvas_PostPhysicsUpdate as test_module
# Note: Test needs to be updated to log for unexpected lines
# unexpected_lines = ["Assert"] + test_module.Lines.unexpected
class C5959761_ForceRegion_PhysAssetExertsPointForce(EditorSharedTest):
from . import C5959761_ForceRegion_PhysAssetExertsPointForce as test_module
# Marking the Test as expected to fail using the xfail decorator due to sporadic failure on Automated Review: SPEC-3146
# The test still runs, but a failure of the test doesn't result in the test run failing
@pytest.mark.xfail(reason="Test Sporadically fails with message [ NOT FOUND ] Success: Bar1 : Expected angular velocity")
class C13352089_RigidBodies_MaxAngularVelocity(EditorSharedTest):
from . import C13352089_RigidBodies_MaxAngularVelocity as test_module
class C18243584_Joints_HingeSoftLimitsConstrained(EditorSharedTest):
from . import C18243584_Joints_HingeSoftLimitsConstrained as test_module
class C18243589_Joints_BallSoftLimitsConstrained(EditorSharedTest):
from . import C18243589_Joints_BallSoftLimitsConstrained as test_module
class C18243591_Joints_BallLeadFollowerCollide(EditorSharedTest):
from . import C18243591_Joints_BallLeadFollowerCollide as test_module
class C19578018_ShapeColliderWithNoShapeComponent(EditorSharedTest):
from . import C19578018_ShapeColliderWithNoShapeComponent as test_module
class C14861500_DefaultSetting_ColliderShape(EditorSharedTest):
from . import C14861500_DefaultSetting_ColliderShape as test_module
class C19723164_ShapeCollider_WontCrashEditor(EditorSharedTest):
from . import C19723164_ShapeColliders_WontCrashEditor as test_module
class C4982800_PhysXColliderShape_CanBeSelected(EditorSharedTest):
from . import C4982800_PhysXColliderShape_CanBeSelected as test_module
class C4982801_PhysXColliderShape_CanBeSelected(EditorSharedTest):
from . import C4982801_PhysXColliderShape_CanBeSelected as test_module
class C4982802_PhysXColliderShape_CanBeSelected(EditorSharedTest):
from . import C4982802_PhysXColliderShape_CanBeSelected as test_module
class C12905528_ForceRegion_WithNonTriggerCollider(EditorSharedTest):
from . import C12905528_ForceRegion_WithNonTriggerCollider as test_module
# Fixme: expected_lines = ["[Warning] (PhysX Force Region) - Please ensure collider component marked as trigger exists in entity"]
class C5932040_ForceRegion_CubeExertsWorldForce(EditorSharedTest):
from . import C5932040_ForceRegion_CubeExertsWorldForce as test_module
class C5932044_ForceRegion_PointForceOnRigidBody(EditorSharedTest):
from . import C5932044_ForceRegion_PointForceOnRigidBody as test_module
class C5959759_RigidBody_ForceRegionSpherePointForce(EditorSharedTest):
from . import C5959759_RigidBody_ForceRegionSpherePointForce as test_module
class C5959809_ForceRegion_RotationalOffset(EditorSharedTest):
from . import C5959809_ForceRegion_RotationalOffset as test_module
class C15096740_Material_LibraryUpdatedCorrectly(EditorSharedTest):
from . import C15096740_Material_LibraryUpdatedCorrectly as test_module
class C4976236_AddPhysxColliderComponent(EditorSharedTest):
from . import C4976236_AddPhysxColliderComponent as test_module
@pytest.mark.xfail(reason="This will fail due to this issue ATOM-15487.")
class C14861502_PhysXCollider_AssetAutoAssigned(EditorSharedTest):
from . import C14861502_PhysXCollider_AssetAutoAssigned as test_module
class C14861501_PhysXCollider_RenderMeshAutoAssigned(EditorSharedTest):
from . import C14861501_PhysXCollider_RenderMeshAutoAssigned as test_module
class C4044695_PhysXCollider_AddMultipleSurfaceFbx(EditorSharedTest):
from . import C4044695_PhysXCollider_AddMultipleSurfaceFbx as test_module
class C14861504_RenderMeshAsset_WithNoPxAsset(EditorSharedTest):
from . import C14861504_RenderMeshAsset_WithNoPxAsset as test_module
class C100000_RigidBody_EnablingGravityWorksPoC(EditorSharedTest):
from . import C100000_RigidBody_EnablingGravityWorksPoC as test_module
class C4982798_Collider_ColliderRotationOffset(EditorSharedTest):
from . import C4982798_Collider_ColliderRotationOffset as test_module
class C15308217_NoCrash_LevelSwitch(EditorSharedTest):
from . import C15308217_NoCrash_LevelSwitch as test_module
class C6090547_ForceRegion_ParentChildForceRegions(EditorSharedTest):
from . import C6090547_ForceRegion_ParentChildForceRegions as test_module
class C19578021_ShapeCollider_CanBeAdded(EditorSharedTest):
from . import C19578021_ShapeCollider_CanBeAdded as test_module
class C15425929_Undo_Redo(EditorSharedTest):
from . import C15425929_Undo_Redo as test_module

@ -1,105 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import pytest
import os
import sys
import inspect
from ly_test_tools import LAUNCHERS
from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite
from .FileManagement import FileManagement as fm
# Custom test spec, it provides functionality to override files
class EditorSingleTest_WithFileOverrides(EditorSingleTest):
# Specify here what files to override, [(original, override), ...]
files_to_override = [()]
# Base directory of the files (Default path is {ProjectName})
base_dir = None
# True will will search sub-directories for the files in base
search_subdirs = False
@classmethod
def wrap_run(cls, instance, request, workspace, editor, editor_test_results, launcher_platform):
root_path = cls.base_dir
if root_path is not None:
root_path = os.path.join(workspace.paths.engine_root(), root_path)
else:
# Default to project folder
root_path = workspace.paths.project()
# Try to locate both target and source files
original_file_list, override_file_list = zip(*cls.files_to_override)
try:
file_list = fm._find_files(original_file_list + override_file_list, root_path, cls.search_subdirs)
except RuntimeWarning as w:
assert False, (
w.message
+ " Please check use of search_subdirs; make sure you are using the correct parent directory."
)
for f in original_file_list:
fm._restore_file(f, file_list[f])
fm._backup_file(f, file_list[f])
for original, override in cls.files_to_override:
fm._copy_file(override, file_list[override], original, file_list[override])
yield # Run Test
for f in original_file_list:
fm._restore_file(f, file_list[f])
@pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
class C4044459_Material_DynamicFriction(EditorSingleTest_WithFileOverrides):
from . import C4044459_Material_DynamicFriction as test_module
files_to_override = [
('physxsystemconfiguration.setreg', 'C4044459_Material_DynamicFriction.setreg_override')
]
base_dir = "AutomatedTesting/Registry"
class C4982593_PhysXCollider_CollisionLayerTest(EditorSingleTest_WithFileOverrides):
from . import C4982593_PhysXCollider_CollisionLayerTest as test_module
files_to_override = [
('physxsystemconfiguration.setreg', 'C4982593_PhysXCollider_CollisionLayer.setreg_override')
]
base_dir = "AutomatedTesting/Registry"
class C111111_RigidBody_EnablingGravityWorksUsingNotificationsPoC(EditorSharedTest):
from . import C111111_RigidBody_EnablingGravityWorksUsingNotificationsPoC as test_module
class C5932041_PhysXForceRegion_LocalSpaceForceOnRigidBodies(EditorSharedTest):
from . import C5932041_PhysXForceRegion_LocalSpaceForceOnRigidBodies as test_module
class C15425929_Undo_Redo(EditorSharedTest):
from . import C15425929_Undo_Redo as test_module
class C4976243_Collision_SameCollisionGroupDiffCollisionLayers(EditorSharedTest):
from . import C4976243_Collision_SameCollisionGroupDiffCollisionLayers as test_module
class C14654881_CharacterController_SwitchLevels(EditorSharedTest):
from . import C14654881_CharacterController_SwitchLevels as test_module
class C17411467_AddPhysxRagdollComponent(EditorSharedTest):
from . import C17411467_AddPhysxRagdollComponent as test_module
class C12712453_ScriptCanvas_MultipleRaycastNode(EditorSharedTest):
from . import C12712453_ScriptCanvas_MultipleRaycastNode as test_module
class C18243586_Joints_HingeLeadFollowerCollide(EditorSharedTest):
from . import C18243586_Joints_HingeLeadFollowerCollide as test_module
class C4982803_Enable_PxMesh_Option(EditorSharedTest):
from . import C4982803_Enable_PxMesh_Option as test_module
class C24308873_CylinderShapeCollider_CollidesWithPhysXTerrain(EditorSharedTest):
from . import C24308873_CylinderShapeCollider_CollidesWithPhysXTerrain as test_module

@ -64,14 +64,13 @@ include(cmake/Projects.cmake)
if(NOT INSTALLED_ENGINE)
# Add the rest of the targets
add_subdirectory(Assets)
add_subdirectory(Code)
add_subdirectory(python)
add_subdirectory(Registry)
add_subdirectory(scripts)
# SPEC-1417 will investigate and fix this
if(NOT PAL_PLATFORM_NAME STREQUAL "Mac")
add_subdirectory(Tools/LyTestTools/tests/)
add_subdirectory(Tools/RemoteConsole/ly_remote_console/tests/)
endif()
add_subdirectory(Templates)
add_subdirectory(Tools)
# Add external subdirectories listed in the engine.json. LY_EXTERNAL_SUBDIRS is a cache variable so the user can add extra
# external subdirectories

@ -3910,11 +3910,19 @@ void CCryEditApp::OpenLUAEditor(const char* files)
AZStd::string_view exePath;
AZ::ComponentApplicationBus::BroadcastResult(exePath, &AZ::ComponentApplicationRequests::GetExecutableFolder);
AZStd::string process = AZStd::string::format("\"%.*s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "LuaIDE"
#if defined(AZ_PLATFORM_LINUX)
// On Linux platforms, launching a process is not done through a shell and its arguments are passed in
// separately. There is no need to wrap the process path in case of spaces in the path
constexpr const char* argumentQuoteString = "";
#else
constexpr const char* argumentQuoteString = "\"";
#endif
AZStd::string process = AZStd::string::format("%s%.*s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "LuaIDE"
#if defined(AZ_PLATFORM_WINDOWS)
".exe"
#endif
"\"", aznumeric_cast<int>(exePath.size()), exePath.data());
"%s", argumentQuoteString, aznumeric_cast<int>(exePath.size()), exePath.data(), argumentQuoteString);
AZStd::string processArgs = AZStd::string::format("%s -engine-path \"%s\"", args.c_str(), engineRoot);
StartProcessDetached(process.c_str(), processArgs.c_str());

@ -1047,7 +1047,7 @@ static bool TryRenameFile(const QString& oldPath, const QString& newPath, int re
bool CCryEditDoc::SaveLevel(const QString& filename)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
AZ_PROFILE_FUNCTION(Editor);
QWaitCursor wait;
CAutoCheckOutDialogEnableForAll enableForAll;
@ -1067,7 +1067,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
{
AZ_PROFILE_SCOPE(AzToolsFramework, "CCryEditDoc::SaveLevel BackupBeforeSave");
AZ_PROFILE_SCOPE(Editor, "CCryEditDoc::SaveLevel BackupBeforeSave");
BackupBeforeSave();
}
@ -1178,7 +1178,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
CPakFile pakFile;
{
AZ_PROFILE_SCOPE(AzToolsFramework, "CCryEditDoc::SaveLevel Open PakFile");
AZ_PROFILE_SCOPE(Editor, "CCryEditDoc::SaveLevel Open PakFile");
if (!pakFile.Open(tempSaveFile.toUtf8().data(), false))
{
gEnv->pLog->LogWarning("Unable to open pack file %s for writing", tempSaveFile.toUtf8().data());
@ -1209,7 +1209,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
AZ::IO::ByteContainerStream<AZStd::vector<char>> entitySaveStream(&entitySaveBuffer);
{
AZ_PROFILE_SCOPE(AzToolsFramework, "CCryEditDoc::SaveLevel Save Entities To Stream");
AZ_PROFILE_SCOPE(Editor, "CCryEditDoc::SaveLevel Save Entities To Stream");
EBUS_EVENT_RESULT(
savedEntities, AzToolsFramework::EditorEntityContextRequestBus, SaveToStreamForEditor, entitySaveStream, layerEntities,
instancesInLayers);

@ -8,8 +8,6 @@
#pragma once
#ifndef CRYINCLUDE_EDITOR_EDITORDEFS_H
#define CRYINCLUDE_EDITOR_EDITORDEFS_H
#include <AzCore/PlatformDef.h>
@ -186,5 +184,3 @@
#endif
#endif
#endif // CRYINCLUDE_EDITOR_EDITORDEFS_H

@ -33,6 +33,7 @@ namespace SandboxEditor
constexpr AZStd::string_view CameraTranslateSmoothnessSetting = "/Amazon/Preferences/Editor/Camera/TranslateSmoothness";
constexpr AZStd::string_view CameraTranslateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/TranslateSmoothing";
constexpr AZStd::string_view CameraRotateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/RotateSmoothing";
constexpr AZStd::string_view CameraCaptureCursorLookSetting = "/Amazon/Preferences/Editor/Camera/CaptureCursorLook";
constexpr AZStd::string_view CameraTranslateForwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateForwardId";
constexpr AZStd::string_view CameraTranslateBackwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateBackwardId";
constexpr AZStd::string_view CameraTranslateLeftIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateLeftId";
@ -60,7 +61,7 @@ namespace SandboxEditor
AZStd::remove_cvref_t<T> GetRegistry(const AZStd::string_view setting, T&& defaultValue)
{
AZStd::remove_cvref_t<T> value = AZStd::forward<T>(defaultValue);
if (auto* registry = AZ::SettingsRegistry::Get())
if (const auto* registry = AZ::SettingsRegistry::Get())
{
registry->Get(value, setting);
}
@ -281,6 +282,16 @@ namespace SandboxEditor
SetRegistry(CameraTranslateSmoothingSetting, enabled);
}
bool CameraCaptureCursorForLook()
{
return GetRegistry(CameraCaptureCursorLookSetting, true);
}
void SetCameraCaptureCursorForLook(const bool capture)
{
SetRegistry(CameraCaptureCursorLookSetting, capture);
}
AzFramework::InputChannelId CameraTranslateForwardChannelId()
{
return AzFramework::InputChannelId(

@ -86,6 +86,9 @@ namespace SandboxEditor
SANDBOX_API bool CameraTranslateSmoothingEnabled();
SANDBOX_API void SetCameraTranslateSmoothingEnabled(bool enabled);
SANDBOX_API bool CameraCaptureCursorForLook();
SANDBOX_API void SetCameraCaptureCursorForLook(bool capture);
SANDBOX_API AzFramework::InputChannelId CameraTranslateForwardChannelId();
SANDBOX_API void SetCameraTranslateForwardChannelId(AZStd::string_view cameraTranslateForwardId);

@ -104,7 +104,6 @@
AZ_CVAR(
bool, ed_visibility_logTiming, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Output the timing of the new IVisibilitySystem query");
AZ_CVAR(bool, ed_showCursorCameraLook, true, nullptr, AZ::ConsoleFunctorFlags::Null, "Show the cursor when using free look with the new camera system");
EditorViewportWidget* EditorViewportWidget::m_pPrimaryViewport = nullptr;
@ -394,8 +393,6 @@ void EditorViewportWidget::UpdateContent(int flags)
//////////////////////////////////////////////////////////////////////////
void EditorViewportWidget::Update()
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
if (Editor::EditorQtApplication::instance()->isMovingOrResizing())
{
return;
@ -957,15 +954,11 @@ AzFramework::CameraState EditorViewportWidget::GetCameraState()
AZ::Vector3 EditorViewportWidget::PickTerrain(const AzFramework::ScreenPoint& point)
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
return LYVec3ToAZVec3(ViewToWorld(AzToolsFramework::ViewportInteraction::QPointFromScreenPoint(point), nullptr, true));
}
AZ::EntityId EditorViewportWidget::PickEntity(const AzFramework::ScreenPoint& point)
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
PreWidgetRendering();
AZ::EntityId entityId;
@ -992,8 +985,6 @@ float EditorViewportWidget::TerrainHeight(const AZ::Vector2& position)
void EditorViewportWidget::FindVisibleEntities(AZStd::vector<AZ::EntityId>& visibleEntitiesOut)
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
visibleEntitiesOut.assign(m_entityVisibilityQuery.Begin(), m_entityVisibilityQuery.End());
}
@ -1078,14 +1069,20 @@ AZStd::shared_ptr<AtomToolsFramework::ModularViewportCameraController> CreateMod
[viewportId](AzFramework::Cameras& cameras)
{
const auto hideCursor = [viewportId]
{
if (SandboxEditor::CameraCaptureCursorForLook())
{
AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Event(
viewportId, &AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Events::BeginCursorCapture);
}
};
const auto showCursor = [viewportId]
{
if (SandboxEditor::CameraCaptureCursorForLook())
{
AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Event(
viewportId, &AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Events::EndCursorCapture);
}
};
auto firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(SandboxEditor::CameraFreeLookChannelId());
@ -1094,12 +1091,10 @@ AZStd::shared_ptr<AtomToolsFramework::ModularViewportCameraController> CreateMod
return SandboxEditor::CameraRotateSpeed();
};
if (!ed_showCursorCameraLook)
{
// default behavior is to hide the cursor but this can be disabled (useful for remote desktop)
// note: See CaptureCursorLook in the Settings Registry
firstPersonRotateCamera->SetActivationBeganFn(hideCursor);
firstPersonRotateCamera->SetActivationEndedFn(showCursor);
}
auto firstPersonPanCamera =
AZStd::make_shared<AzFramework::PanCameraInput>(SandboxEditor::CameraFreePanChannelId(), AzFramework::LookPan);

@ -6,9 +6,6 @@
*
*/
#ifndef CRYINCLUDE_EDITOR_IEDITOR_H
#define CRYINCLUDE_EDITOR_IEDITOR_H
#pragma once
#ifdef PLUGIN_EXPORTS
@ -25,6 +22,7 @@
#include <WinWidgetId.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Debug/Budget.h>
class QMenu;
@ -738,4 +736,5 @@ struct IInitializeUIInfo
virtual void SetInfoText(const char* text) = 0;
};
#endif // CRYINCLUDE_EDITOR_IEDITOR_H
AZ_DECLARE_BUDGET(Editor);

@ -405,8 +405,6 @@ void CEditorImpl::Update()
// Make sure this is not called recursively
m_bUpdates = false;
FUNCTION_PROFILER(GetSystem(), PROFILE_EDITOR);
//@FIXME: Restore this latter.
//if (GetGameEngine() && GetGameEngine()->IsLevelLoaded())
{

@ -7,6 +7,7 @@
*/
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
#include <AzCore/Settings/SettingsRegistryImpl.h>
#include <AzFramework/Viewport/ViewportControllerList.h>
#include <AzToolsFramework/Input/QtEventToAzInputManager.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
@ -95,10 +96,16 @@ namespace UnitTest
m_controllerList->RegisterViewportContext(TestViewportId);
m_inputChannelMapper = AZStd::make_unique<AzToolsFramework::QtEventToAzInputMapper>(m_rootWidget.get(), TestViewportId);
m_settingsRegistry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
AZ::SettingsRegistry::Register(m_settingsRegistry.get());
}
void TearDown() override
{
AZ::SettingsRegistry::Unregister(m_settingsRegistry.get());
m_settingsRegistry.reset();
m_inputChannelMapper.reset();
m_controllerList->UnregisterViewportContext(TestViewportId);
@ -170,7 +177,7 @@ namespace UnitTest
void RepeatDiagonalMouseMovements(const AZStd::function<float()>& deltaTimeFn)
{
// move to the center of the screen
auto start = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
const auto start = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
MouseMove(m_rootWidget.get(), start, QPoint(0, 0));
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTimeFn()), AZ::ScriptTimePoint() });
@ -204,12 +211,15 @@ namespace UnitTest
::testing::NiceMock<MockWindowRequests> m_mockWindowRequests;
ViewportMouseCursorRequestImpl m_viewportMouseCursorRequests;
AtomToolsFramework::ModularCameraViewportContext* m_cameraViewportContextView = nullptr;
AZStd::unique_ptr<AZ::SettingsRegistryInterface> m_settingsRegistry;
};
const AzFramework::ViewportId ModularViewportCameraControllerFixture::TestViewportId = AzFramework::ViewportId(0);
TEST_F(ModularViewportCameraControllerFixture, MouseMovementDoesNotAccumulateExcessiveDriftInModularViewportCameraWithVaryingDeltaTime)
{
SandboxEditor::SetCameraCaptureCursorForLook(false);
// Given
PrepareCollaborators();
@ -242,6 +252,8 @@ namespace UnitTest
ModularViewportCameraControllerDeltaTimeParamFixture,
MouseMovementDoesNotAccumulateExcessiveDriftInModularViewportCameraWithFixedDeltaTime)
{
SandboxEditor::SetCameraCaptureCursorForLook(false);
// Given
PrepareCollaborators();
@ -263,4 +275,92 @@ namespace UnitTest
INSTANTIATE_TEST_CASE_P(
All, ModularViewportCameraControllerDeltaTimeParamFixture, testing::Values(1.0f / 60.0f, 1.0f / 50.0f, 1.0f / 30.0f));
TEST_F(ModularViewportCameraControllerFixture, MouseMovementOrientatesCameraWhenCursorIsCaptured)
{
// Given
PrepareCollaborators();
// ensure cursor is captured
SandboxEditor::SetCameraCaptureCursorForLook(true);
const float deltaTime = 1.0f / 60.0f;
// When
// move to the center of the screen
auto start = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
MouseMove(m_rootWidget.get(), start, QPoint(0, 0));
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
const auto mouseDelta = QPoint(5, 0);
// initial movement to begin the camera behavior
MousePressAndMove(m_rootWidget.get(), start, mouseDelta, Qt::MouseButton::RightButton);
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
// move the cursor right
for (int i = 0; i < 50; ++i)
{
MousePressAndMove(m_rootWidget.get(), start + mouseDelta, mouseDelta, Qt::MouseButton::RightButton);
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
}
// move the cursor left (do an extra iteration moving left to account for the initial dead-zone)
for (int i = 0; i < 51; ++i)
{
MousePressAndMove(m_rootWidget.get(), start + mouseDelta, -mouseDelta, Qt::MouseButton::RightButton);
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
}
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::RightButton, Qt::KeyboardModifier::NoModifier, start + mouseDelta);
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
// Then
// retrieve the amount of yaw rotation
const AZ::Quaternion cameraRotation = m_cameraViewportContextView->GetCameraTransform().GetRotation();
const auto eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromQuaternion(cameraRotation));
// camera should be back at the center (no yaw)
using ::testing::FloatNear;
EXPECT_THAT(eulerAngles.GetZ(), FloatNear(0.0f, 0.001f));
// Clean-up
HaltCollaborators();
}
TEST_F(ModularViewportCameraControllerFixture, CameraDoesNotContinueToRotateGivenNoInputWhenCaptured)
{
// Given
PrepareCollaborators();
SandboxEditor::SetCameraCaptureCursorForLook(true);
const float deltaTime = 1.0f / 60.0f;
// When
// move to the center of the screen
auto start = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
MouseMove(m_rootWidget.get(), start, QPoint(0, 0));
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
// will move a small amount initially
const auto mouseDelta = QPoint(5, 0);
MousePressAndMove(m_rootWidget.get(), start, mouseDelta, Qt::MouseButton::RightButton);
// ensure further updates to not continue to rotate
for (int i = 0; i < 50; ++i)
{
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
}
// Then
// ensure the camera rotation is no longer the identity
const AZ::Quaternion cameraRotation = m_cameraViewportContextView->GetCameraTransform().GetRotation();
const auto eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromQuaternion(cameraRotation));
// initial amount of rotation after first mouse move
using ::testing::FloatNear;
EXPECT_THAT(eulerAngles.GetZ(), FloatNear(-0.025f, 0.001f));
// Clean-up
HaltCollaborators();
}
} // namespace UnitTest

@ -38,7 +38,6 @@
// To use the Andrew's algorithm in order to make convex hull from the points, this header is needed.
#include "Util/GeometryUtil.h"
namespace {
QColor kLinkColorParent = QColor(0, 255, 255);
QColor kLinkColorChild = QColor(0, 0, 255);
@ -1928,7 +1927,7 @@ bool CBaseObject::HitTestRectBounds(HitContext& hc, const AABB& box)
//////////////////////////////////////////////////////////////////////////
bool CBaseObject::HitTestRect(HitContext& hc)
{
AZ_PROFILE_FUNCTION(Entity);
AZ_PROFILE_FUNCTION(Editor);
AABB box;
@ -1965,7 +1964,7 @@ bool CBaseObject::HitHelperTest(HitContext& hc)
//////////////////////////////////////////////////////////////////////////
bool CBaseObject::HitHelperAtTest(HitContext& hc, const Vec3& pos)
{
AZ_PROFILE_FUNCTION(Entity);
AZ_PROFILE_FUNCTION(Editor);
bool bResult = false;

@ -1261,10 +1261,6 @@ void DisplayContext::DrawTextureLabel(const Vec3& pos, int nWidth, int nHeight,
//////////////////////////////////////////////////////////////////////////
void DisplayContext::Flush2D()
{
#ifndef PHYSICS_EDITOR
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
#endif
if (m_textureLabels.empty())
{
return;

@ -497,7 +497,7 @@ bool CEntityObject::HitTestRect(HitContext& hc)
//////////////////////////////////////////////////////////////////////////
int CEntityObject::MouseCreateCallback(CViewport* view, EMouseEvent event, QPoint& point, int flags)
{
AZ_PROFILE_FUNCTION(Editor);
AZ_PROFILE_FUNCTION(Entity);
if (event == eMouseMove || event == eMouseLDown)
{

@ -18,8 +18,6 @@
//////////////////////////////////////////////////////////////////////////
void CGizmoManager::Display(DisplayContext& dc)
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
AABB bbox;
std::vector<CGizmo*> todelete;
for (Gizmos::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)

@ -37,7 +37,6 @@ AZ_CVAR(
bool, ed_visibility_use, true, nullptr, AZ::ConsoleFunctorFlags::Null,
"Enable/disable using the new IVisibilitySystem for Entity visibility determination");
/*!
* Class Description used for object templates.
* This description filled from Xml template files.

@ -19,7 +19,6 @@
#include "Objects/ObjectLoader.h"
#include "Objects/SelectionGroup.h"
//////////////////////////////////////////////////////////////////////////
// CUndoBaseObjectNew implementation.
//////////////////////////////////////////////////////////////////////////

@ -31,6 +31,7 @@
#include <QMessageBox>
#include <QPushButton>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/smart_ptr/make_shared.h>

@ -13,6 +13,7 @@
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneUI/CommonWidgets/OverlayWidget.h>
#include <SceneAPI/SceneUI/SceneWidgets/ManifestWidget.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Serialization/SerializeContext.h>
ImporterRootDisplay::ImporterRootDisplay(AZ::SerializeContext* serializeContext, QWidget* parent)

@ -148,8 +148,6 @@ void QTopRendererWnd::UpdateContent(int flags)
//////////////////////////////////////////////////////////////////////////
void QTopRendererWnd::Draw([[maybe_unused]] DisplayContext& dc)
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
////////////////////////////////////////////////////////////////////////
// Perform the rendering for this window
////////////////////////////////////////////////////////////////////////

@ -41,7 +41,6 @@ struct SPointSorter
//===================================================================
void ConvexHull2DGraham(std::vector<Vec3>& ptsOut, const std::vector<Vec3>& ptsIn)
{
FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
const unsigned nPtsIn = ptsIn.size();
if (nPtsIn < 3)
{
@ -66,7 +65,6 @@ void ConvexHull2DGraham(std::vector<Vec3>& ptsOut, const std::vector<Vec3>& ptsI
std::swap(ptsSorted[0], ptsSorted[iBotRight]);
{
FRAME_PROFILER("SORT Graham", gEnv->pSystem, PROFILE_AI)
std::sort(ptsSorted.begin() + 1, ptsSorted.end(), SPointSorter(ptsSorted[0]));
}
ptsSorted.erase(std::unique(ptsSorted.begin(), ptsSorted.end(), ptEqual), ptsSorted.end());
@ -196,7 +194,6 @@ inline bool PointSorterAndrew(const Vec3& lhs, const Vec3& rhs)
//===================================================================
SANDBOX_API void ConvexHull2DAndrew(std::vector<Vec3>& ptsOut, const std::vector<Vec3>& ptsIn)
{
FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
const int n = (int)ptsIn.size();
if (n < 3)
{
@ -206,7 +203,6 @@ SANDBOX_API void ConvexHull2DAndrew(std::vector<Vec3>& ptsOut, const std::vector
std::vector<Vec3> P = ptsIn;
{
FRAME_PROFILER("SORT Andrew", gEnv->pSystem, PROFILE_AI)
std::sort(P.begin(), P.end(), PointSorterAndrew);
}

@ -400,7 +400,6 @@ void QtViewport::UpdateContent(int flags)
//////////////////////////////////////////////////////////////////////////
void QtViewport::Update()
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
m_viewportUi.Update();
m_bAdvancedSelectMode = false;
@ -1436,9 +1435,6 @@ bool QtViewport::MouseCallback(EMouseEvent event, const QPoint& point, Qt::Keybo
//////////////////////////////////////////////////////////////////////////
void QtViewport::ProcessRenderLisneters(DisplayContext& rstDisplayContext)
{
FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR);
size_t nCount(0);
size_t nTotal(0);

@ -12,6 +12,7 @@
#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>
@ -112,9 +113,9 @@ namespace SandboxEditor
AzFramework::WindowRequestBus::EventResult(
windowSize, event.m_windowHandle, &AzFramework::WindowRequestBus::Events::GetClientAreaSize);
auto screenPoint = AzFramework::ScreenPoint(
static_cast<int>(position->m_normalizedPosition.GetX() * windowSize.m_width),
static_cast<int>(position->m_normalizedPosition.GetY() * windowSize.m_height));
const auto screenPoint = AzFramework::ScreenPoint(
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;
@ -207,20 +208,27 @@ namespace SandboxEditor
? &InteractionBus::Events::InternalHandleMouseManipulatorInteraction
: &InteractionBus::Events::InternalHandleMouseViewportInteraction;
const auto mouseInteractionEvent = [mouseInteraction, event = eventType.value(), wheelDelta] {
auto currentCursorState = AzFramework::SystemCursorState::Unknown;
AzFramework::InputSystemCursorRequestBus::EventResult(
currentCursorState, event.m_inputChannel.GetInputDevice().GetInputDeviceId(),
&AzFramework::InputSystemCursorRequestBus::Events::GetSystemCursorState);
const auto mouseInteractionEvent = [mouseInteraction, event = eventType.value(), wheelDelta,
cursorCaptured = currentCursorState == AzFramework::SystemCursorState::ConstrainedAndHidden]
{
switch (event)
{
case MouseEvent::Up:
case MouseEvent::Down:
case MouseEvent::Move:
case MouseEvent::DoubleClick:
return MouseInteractionEvent(AZStd::move(mouseInteraction), event);
return MouseInteractionEvent(AZStd::move(mouseInteraction), event, cursorCaptured);
case MouseEvent::Wheel:
return MouseInteractionEvent(AZStd::move(mouseInteraction), wheelDelta);
}
AZ_Assert(false, "Unhandled MouseEvent");
return MouseInteractionEvent(MouseInteraction{}, MouseEvent::Up);
return MouseInteractionEvent(MouseInteraction{}, MouseEvent::Up, false);
}();
InteractionBus::EventResult(

@ -13,8 +13,6 @@ set(FILES
Instance/InstanceData.h
Instance/InstanceData.cpp
Instance/InstanceDatabase.h
Serialization/Json/JsonUtils.h
Serialization/Json/JsonUtils.cpp
std/containers/array_view.h
std/containers/fixed_vector_set.h
std/containers/lru_cache.h

@ -10,7 +10,6 @@ set(FILES
ArrayView.cpp
ConcurrencyCheckerTests.cpp
InstanceDatabase.cpp
JsonSerializationUtilsTests.cpp
lru_cache.cpp
Main.cpp
vector_set.cpp

@ -10,7 +10,6 @@
// Component includes
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/Debug/FrameProfilerComponent.h>
#include <AzCore/IO/Streamer/StreamerComponent.h>
#include <AzCore/Jobs/JobManagerComponent.h>
#include <AzCore/Serialization/Json/JsonSystemComponent.h>
@ -36,7 +35,6 @@ namespace AZ
JsonSystemComponent::CreateDescriptor(),
AssetManagerComponent::CreateDescriptor(),
UserSettingsComponent::CreateDescriptor(),
Debug::FrameProfilerComponent::CreateDescriptor(),
SliceComponent::CreateDescriptor(),
SliceSystemComponent::CreateDescriptor(),
SliceMetadataInfoComponent::CreateDescriptor(),

@ -51,7 +51,6 @@
#include <AzCore/Driller/Driller.h>
#include <AzCore/Memory/MemoryDriller.h>
#include <AzCore/Debug/TraceMessagesDriller.h>
#include <AzCore/Debug/ProfilerDriller.h>
#include <AzCore/Debug/EventTraceDriller.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Script/ScriptSystemBus.h>
@ -546,6 +545,10 @@ namespace AZ
m_entityActivatedEvent.DisconnectAllHandlers();
m_entityDeactivatedEvent.DisconnectAllHandlers();
#if !defined(_RELEASE)
m_budgetTracker.Reset();
#endif
DestroyAllocator();
}
@ -594,6 +597,10 @@ namespace AZ
CreateOSAllocator();
CreateSystemAllocator();
#if !defined(_RELEASE)
m_budgetTracker.Init();
#endif
// This can be moved to the ComponentApplication constructor if need be
// This is reading the *.setreg files using SystemFile and merging the settings
// to the settings registry.
@ -625,8 +632,6 @@ namespace AZ
m_eventLogger->Start(outputPath.Native(), baseFileName);
}
CreateDrillers();
Sfmt::Create();
CreateReflectionManager();
@ -746,12 +751,6 @@ namespace AZ
ComponentApplicationBus::Handler::BusDisconnect();
TickRequestBus::Handler::BusDisconnect();
if (m_drillerManager)
{
Debug::DrillerManager::Destroy(m_drillerManager);
m_drillerManager = nullptr;
}
m_eventLogger->Stop();
// Clear the descriptor to deallocate all strings (owned by ModuleDescriptor)
@ -899,33 +898,6 @@ namespace AZ
allocatorManager.FinalizeConfiguration();
}
//=========================================================================
// CreateDrillers
// [2/20/2013]
//=========================================================================
void ComponentApplication::CreateDrillers()
{
// Create driller manager and register drillers if requested
if (m_descriptor.m_enableDrilling)
{
m_drillerManager = Debug::DrillerManager::Create();
// Memory driller is responsible for tracking allocations.
// Tracking type and overhead is determined by app configuration.
// Only one MemoryDriller is supported at a time
// Only create the memory driller if there is no handlers connected to the MemoryDrillerBus
if (!Debug::MemoryDrillerBus::HasHandlers())
{
m_drillerManager->Register(aznew Debug::MemoryDriller);
}
// Profiler driller will consume resources only when started.
m_drillerManager->Register(aznew Debug::ProfilerDriller);
// Trace messages driller will consume resources only when started.
m_drillerManager->Register(aznew Debug::TraceMessagesDriller);
m_drillerManager->Register(aznew Debug::EventTraceDriller);
}
}
void ComponentApplication::MergeSettingsToRegistry(SettingsRegistryInterface& registry)
{
SettingsRegistryInterface::Specializations specializations;
@ -1416,10 +1388,6 @@ namespace AZ
EBUS_EVENT(TickBus, OnTick, m_deltaTime, ScriptTimePoint(now));
}
}
if (m_drillerManager)
{
m_drillerManager->FrameUpdate();
}
}
//=========================================================================

@ -12,6 +12,7 @@
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/Debug/BudgetTracker.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Module/DynamicModuleHandle.h>
#include <AzCore/Module/ModuleManager.h>
@ -225,11 +226,6 @@ namespace AZ
/// Returns the path to the folder the executable is in.
const char* GetExecutableFolder() const override { return m_exeDirectory.c_str(); }
/// Returns pointer to the driller manager if it's enabled, otherwise NULL.
Debug::DrillerManager* GetDrillerManager() override { return m_drillerManager; }
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
/// TickRequestBus
float GetTickDeltaTime() override;
@ -324,9 +320,6 @@ namespace AZ
/// Create the system allocator using the data in the m_descriptor
void CreateSystemAllocator();
/// Create the drillers
void CreateDrillers();
virtual void MergeSettingsToRegistry(SettingsRegistryInterface& registry);
//! Sets the specializations that will be used when loading the Settings Registry. Extend this in derived
@ -402,6 +395,10 @@ namespace AZ
// from the m_console member when it goes out of scope
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle m_settingsRegistryConsoleFunctors;
#if !defined(_RELEASE)
Debug::BudgetTracker m_budgetTracker;
#endif
// this is used when no argV/ArgC is supplied.
// in order to have the same memory semantics (writable, non-const)
// we create a buffer that can be written to (up to AZ_MAX_PATH_LEN) and then
@ -409,8 +406,6 @@ namespace AZ
char m_commandLineBuffer[AZ_MAX_PATH_LEN];
char* m_commandLineBufferAddress{ m_commandLineBuffer };
Debug::DrillerManager* m_drillerManager{ nullptr };
StartupParameters m_startupParameters;
char** m_argV{ nullptr };

@ -187,11 +187,6 @@ namespace AZ
//! @return a pointer to the name of the path that contains the application's executable.
virtual const char* GetExecutableFolder() const = 0;
//! Returns a pointer to the driller manager, if driller is enabled.
//! The driller manager manages all active driller sessions and driller factories.
//! @return A pointer to the driller manager. If driller is not enabled, this function returns null.
virtual Debug::DrillerManager* GetDrillerManager() = 0;
//! ResolveModulePath is called whenever LoadDynamicModule wants to resolve a module in order to actually load it.
//! You can override this if you need to load modules from a different path or hijack module loading in some other way.
//! If you do, ensure that you use platform-specific conventions to do so, as this is called by multiple platforms.

@ -16,6 +16,7 @@
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Debug/Budget.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/EBus/Event.h>
#include <AzCore/std/string/string.h>
@ -438,3 +439,4 @@ namespace AZ
return component;
}
} // namespace AZ

@ -81,10 +81,15 @@ namespace AZ
azrtti_typeid<decltype(componentMap)>(),
inputValue, "Components", context);
static TypeId genericComponentWrapperTypeId("{68D358CA-89B9-4730-8BA6-E181DEA28FDE}");
for (auto& [componentKey, component] : componentMap)
{
// if underlying type is genericComponentWrapperTypeId, the template is null and the component should not be addded
if (component->GetUnderlyingComponentType() != genericComponentWrapperTypeId)
{
entityInstance->m_components.emplace_back(component);
}
}
result.Combine(componentLoadResult);
}

@ -0,0 +1,74 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "Budget.h"
#include <AzCore/Module/Environment.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Memory/SystemAllocator.h>
AZ_DEFINE_BUDGET(Animation);
AZ_DEFINE_BUDGET(Audio);
AZ_DEFINE_BUDGET(AzCore);
AZ_DEFINE_BUDGET(Editor);
AZ_DEFINE_BUDGET(Entity);
AZ_DEFINE_BUDGET(Game);
AZ_DEFINE_BUDGET(System);
AZ_DEFINE_BUDGET(Physics);
namespace AZ::Debug
{
struct BudgetImpl
{
AZ_CLASS_ALLOCATOR(BudgetImpl, AZ::SystemAllocator, 0);
// TODO: Budget implementation for tracking budget wall time per-core, memory, etc.
};
Budget::Budget(const char* name)
: m_name{ name }
, m_crc{ Crc32(name) }
{
}
Budget::Budget(const char* name, uint32_t crc)
: m_name{ name }
, m_crc{ crc }
{
m_impl = aznew BudgetImpl;
}
Budget::~Budget()
{
if (m_impl)
{
delete m_impl;
}
}
// TODO:Budgets Methods below are stubbed pending future work to both update budget data and visualize it
void Budget::PerFrameReset()
{
}
void Budget::BeginProfileRegion()
{
}
void Budget::EndProfileRegion()
{
}
void Budget::TrackAllocation(uint64_t)
{
}
void Budget::UntrackAllocation(uint64_t)
{
}
} // namespace AZ::Debug

@ -0,0 +1,88 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Debug/BudgetTracker.h>
#include <AzCore/Math/Crc.h>
namespace AZ::Debug
{
// A budget collates per-frame resource utilization and memory for a particular category
class Budget final
{
public:
explicit Budget(const char* name);
Budget(const char* name, uint32_t crc);
~Budget();
void PerFrameReset();
void BeginProfileRegion();
void EndProfileRegion();
void TrackAllocation(uint64_t bytes);
void UntrackAllocation(uint64_t bytes);
const char* Name() const
{
return m_name;
}
uint32_t Crc() const
{
return m_crc;
}
private:
const char* m_name;
const uint32_t m_crc;
struct BudgetImpl* m_impl = nullptr;
};
} // namespace AZ::Debug
// The budget is usable in the same file it was defined without needing an additional declaration.
// If you encounter a linker error complaining that this function is not defined, you have likely forgotten to either
// define or declare the budget used in a profile or memory marker. See AZ_DEFINE_BUDGET and AZ_DECLARE_BUDGET below
// for usage.
#define AZ_BUDGET_GETTER(name) GetAzBudget##name
#if defined(_RELEASE)
#define AZ_DEFINE_BUDGET(name) \
::AZ::Debug::Budget* AZ_BUDGET_GETTER(name)() \
{ \
return nullptr; \
}
#else
// Usage example:
// In a single C++ source file:
// AZ_DEFINE_BUDGET(AzCore);
//
// Anywhere the budget is used, the budget must be declared (either in a header or in the source file itself)
// AZ_DECLARE_BUDGET(AzCore);
#define AZ_DEFINE_BUDGET(name) \
::AZ::Debug::Budget* AZ_BUDGET_GETTER(name)() \
{ \
constexpr static uint32_t crc = AZ_CRC_CE(#name); \
static ::AZ::Debug::Budget* budget = ::AZ::Debug::BudgetTracker::GetBudgetFromEnvironment(#name, crc); \
return budget; \
}
#endif
// If using a budget defined in a different C++ source file, add AZ_DECLARE_BUDGET(yourBudget); somewhere in your source file at namespace
// scope Alternatively, AZ_DECLARE_BUDGET can be used in a header to declare the budget for use across any users of the header
#define AZ_DECLARE_BUDGET(name) ::AZ::Debug::Budget* AZ_BUDGET_GETTER(name)()
// Declare budgets that are core engine budgets, or may be shared/needed across multiple external gems
// You should NOT need to declare user-space or budgets with isolated usage here. Prefer declaring them local to the module(s) that use
// the budget and defining them within a single module to avoid needing to recompile the entire engine.
AZ_DECLARE_BUDGET(Animation);
AZ_DECLARE_BUDGET(Audio);
AZ_DECLARE_BUDGET(AzCore);
AZ_DECLARE_BUDGET(Editor);
AZ_DECLARE_BUDGET(Entity);
AZ_DECLARE_BUDGET(Game);
AZ_DECLARE_BUDGET(System);
AZ_DECLARE_BUDGET(Physics);

@ -0,0 +1,72 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Debug/BudgetTracker.h>
#include <AzCore/base.h>
#include <AzCore/Debug/Budget.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/parallel/scoped_lock.h>
namespace AZ::Debug
{
constexpr static const char* BudgetTrackerEnvName = "budgetTrackerEnv";
struct BudgetTracker::BudgetTrackerImpl
{
AZStd::unordered_map<const char*, Budget> m_budgets;
};
Budget* BudgetTracker::GetBudgetFromEnvironment(const char* budgetName, uint32_t crc)
{
BudgetTracker* tracker = Interface<BudgetTracker>::Get();
if (tracker)
{
return &tracker->GetBudget(budgetName, crc);
}
return nullptr;
}
BudgetTracker::~BudgetTracker()
{
Reset();
}
bool BudgetTracker::Init()
{
if (Interface<BudgetTracker>::Get())
{
return false;
}
Interface<BudgetTracker>::Register(this);
m_impl = new BudgetTrackerImpl;
return true;
}
void BudgetTracker::Reset()
{
if (m_impl)
{
Interface<BudgetTracker>::Unregister(this);
delete m_impl;
m_impl = nullptr;
}
}
Budget& BudgetTracker::GetBudget(const char* budgetName, uint32_t crc)
{
AZStd::scoped_lock lock{ m_mutex };
auto it = m_impl->m_budgets.try_emplace(budgetName, budgetName, crc).first;
return it->second;
}
} // namespace AZ::Debug

@ -0,0 +1,43 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Module/Environment.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/parallel/mutex.h>
namespace AZ::Debug
{
class Budget;
class BudgetTracker
{
public:
AZ_RTTI(BudgetTracker, "{E14A746D-BFFE-4C02-90FB-4699B79864A5}");
static Budget* GetBudgetFromEnvironment(const char* budgetName, uint32_t crc);
~BudgetTracker();
// Returns false if the budget tracker was already present in the environment (initialized already elsewhere)
bool Init();
void Reset();
Budget& GetBudget(const char* budgetName, uint32_t crc);
private:
struct BudgetTrackerImpl;
AZStd::mutex m_mutex;
// The BudgetTracker is likely included in proportionally high number of files throughout the
// engine, so indirection is used here to avoid imposing excessive recompilation in periods
// while the budget system is iterated on.
BudgetTrackerImpl* m_impl = nullptr;
};
} // namespace AZ::Debug

@ -1,63 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_FRAME_PROFILER_H
#define AZCORE_FRAME_PROFILER_H
#include <AzCore/Driller/DrillerBus.h>
#include <AzCore/std/containers/ring_buffer.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/std/parallel/config.h>
#include <AzCore/std/containers/unordered_map.h>
namespace AZ
{
namespace Debug
{
namespace FrameProfiler
{
/**
* This structure is used for frame data history, make sure it's memory efficient.
*/
struct FrameData
{
unsigned int m_frameId; ///< Id of the frame this data belongs to.
union
{
ProfilerRegister::TimeData m_timeData;
ProfilerRegister::ValuesData m_userValues;
};
};
struct RegisterData
{
//////////////////////////////////////////////////////////////////////////
// Profile register snapshot
/// data that doesn't change
const char* m_name; ///< Name of the profiler register.
const char* m_function; ///< Function name in the code.
int m_line; ///< Line number if the code.
AZ::u32 m_systemId; ///< Register system id.
ProfilerRegister::Type m_type;
RegisterData* m_lastParent; ///< Pointer to the last parent register data.
AZStd::ring_buffer<FrameData> m_frames; ///< History of all frame deltas (basically the data you want to display)
};
struct ThreadData
{
typedef AZStd::unordered_map<const ProfilerRegister*, RegisterData> RegistersMap;
AZStd::thread_id m_id; ///< Thread id (same as AZStd::thread::id)
RegistersMap m_registers; ///< Map with all the registers (with history)
};
typedef AZStd::fixed_vector<ThreadData, Profiler::m_maxNumberOfThreads> ThreadDataArray; ///< Array with samplers for all threads
} // namespace FrameProfiler
} // namespace Debug
} // namespace AZ
#endif // AZCORE_FRAME_PROFILER_H
#pragma once

@ -1,38 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_FRAME_PROFILER_BUS_H
#define AZCORE_FRAME_PROFILER_BUS_H
#include <AzCore/EBus/EBus.h>
#include <AzCore/Debug/FrameProfiler.h>
namespace AZ
{
namespace Debug
{
class FrameProfilerComponent;
/**
* Interface class for frame profiler events.
*/
class FrameProfilerEvents
: public AZ::EBusTraits
{
public:
virtual ~FrameProfilerEvents() {}
/// Called when the frame profiler has computed a new frame (even is there is no new data).
virtual void OnFrameProfilerData(const FrameProfiler::ThreadDataArray& data) = 0;
};
typedef AZ::EBus<FrameProfilerEvents> FrameProfilerBus;
} // namespace Debug
} // namespace AZ
#endif // AZCORE_FRAME_PROFILER_BUS_H
#pragma once

@ -1,250 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Debug/FrameProfilerComponent.h>
#include <AzCore/Debug/FrameProfilerBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Debug/Profiler.h>
namespace AZ
{
namespace Debug
{
//=========================================================================
// FrameProfilerComponent
// [12/5/2012]
//=========================================================================
FrameProfilerComponent::FrameProfilerComponent()
: m_numFramesStored(2)
, m_frameId(0)
, m_pauseOnFrame(0)
, m_currentThreadData(NULL)
{
}
//=========================================================================
// ~FrameProfilerComponent
// [12/5/2012]
//=========================================================================
FrameProfilerComponent::~FrameProfilerComponent()
{
}
//=========================================================================
// Activate
// [12/5/2012]
//=========================================================================
void FrameProfilerComponent::Activate()
{
if (!Profiler::IsReady())
{
Profiler::Create();
}
Profiler::AddReference();
TickBus::Handler::BusConnect();
AZ_Assert(m_numFramesStored >= 1, "We must have at least one frame to store, otherwise this component is useless!");
}
//=========================================================================
// Deactivate
// [12/5/2012]
//=========================================================================
void FrameProfilerComponent::Deactivate()
{
TickBus::Handler::BusDisconnect();
Profiler::ReleaseReference();
}
//=========================================================================
// OnTick
// [12/5/2012]
//=========================================================================
void FrameProfilerComponent::OnTick(float deltaTime, ScriptTimePoint time)
{
(void)deltaTime;
(void)time;
++m_frameId;
AZ_Error("Profiler", m_frameId != m_pauseOnFrame, "Triggered user pause/error on this frame! Check FrameProfilerComponent pauseOnFrame value!");
if (!Profiler::IsReady())
{
return; // we can't sample registers without profiler
}
// collect data from the profiler
m_currentThreadData = NULL;
Profiler::Instance().ReadRegisterValues(AZStd::bind(&FrameProfilerComponent::ReadProfilerRegisters, this, AZStd::placeholders::_1, AZStd::placeholders::_2));
// process all the resulting data here, not while reading the registers
for (size_t iThread = 0; iThread < m_threads.size(); ++iThread)
{
FrameProfiler::ThreadData& td = m_threads[iThread];
FrameProfiler::ThreadData::RegistersMap::iterator it = td.m_registers.begin();
FrameProfiler::ThreadData::RegistersMap::iterator last = td.m_registers.end();
for (; it != last; ++it)
{
// fix up parents
FrameProfiler::RegisterData& rd = it->second;
if (rd.m_type == ProfilerRegister::PRT_TIME)
{
const FrameProfiler::FrameData& fd = rd.m_frames.back();
if (fd.m_timeData.m_lastParent != nullptr)
{
FrameProfiler::ThreadData::RegistersMap::iterator parentIt = td.m_registers.find(fd.m_timeData.m_lastParent);
AZ_Assert(parentIt != td.m_registers.end(), "We have a parent register that is not in our register map. This should not happen!");
rd.m_lastParent = &parentIt->second;
}
else
{
rd.m_lastParent = NULL;
}
}
}
}
// send an even to whomever cares
EBUS_EVENT(FrameProfilerBus, OnFrameProfilerData, m_threads);
}
int FrameProfilerComponent::GetTickOrder()
{
// Even it's not critical we should tick last to capture the current frame
// so TICK_LAST (since it's not the last int +1 is a valid assumption)
return TICK_LAST + 1;
}
//=========================================================================
// ReadRegisterCallback
// [12/5/2012]
//=========================================================================
bool FrameProfilerComponent::ReadProfilerRegisters(const ProfilerRegister& reg, const AZStd::thread_id& id)
{
if (m_currentThreadData == NULL || m_currentThreadData->m_id != id)
{
m_currentThreadData = NULL;
// find the thread and cache it, as we will received registers thread by thread... so we don't search.
for (size_t i = 0; i < m_threads.size(); ++i)
{
FrameProfiler::ThreadData* td = &m_threads[i];
if (td->m_id == id)
{
m_currentThreadData = td;
break;
}
}
if (m_currentThreadData == NULL)
{
m_threads.push_back();
m_currentThreadData = &m_threads.back();
m_currentThreadData->m_id = id;
}
}
const ProfilerRegister* profReg = &reg;
FrameProfiler::ThreadData::RegistersMap::pair_iter_bool pairIterBool = m_currentThreadData->m_registers.insert_key(profReg);
FrameProfiler::RegisterData& regData = pairIterBool.first->second;
// now update dynamic data with as little as possible computation (we must be fast)
FrameProfiler::FrameData fd; // we can actually move this computation (FrameData and push) for later but we will need to use more memory
fd.m_frameId = m_frameId;
if (pairIterBool.second)
{
// when insert copy the static data only once
regData.m_name = profReg->m_name;
regData.m_function = profReg->m_function;
regData.m_line = profReg->m_line;
regData.m_systemId = profReg->m_systemId;
regData.m_frames.set_capacity(m_numFramesStored);
regData.m_type = static_cast<ProfilerRegister::Type>(profReg->m_type);
}
switch (regData.m_type)
{
case ProfilerRegister::PRT_TIME:
{
fd.m_timeData.m_time = profReg->m_timeData.m_time;
fd.m_timeData.m_childrenTime = profReg->m_timeData.m_childrenTime;
fd.m_timeData.m_calls = profReg->m_timeData.m_calls;
fd.m_timeData.m_childrenCalls = profReg->m_timeData.m_childrenCalls;
fd.m_timeData.m_lastParent = profReg->m_timeData.m_lastParent;
} break;
case ProfilerRegister::PRT_VALUE:
{
fd.m_userValues.m_value1 = profReg->m_userValues.m_value1;
fd.m_userValues.m_value2 = profReg->m_userValues.m_value2;
fd.m_userValues.m_value3 = profReg->m_userValues.m_value3;
fd.m_userValues.m_value4 = profReg->m_userValues.m_value4;
fd.m_userValues.m_value5 = profReg->m_userValues.m_value5;
} break;
}
regData.m_frames.push_back(fd);
return true;
}
//=========================================================================
// GetProvidedServices
//=========================================================================
void FrameProfilerComponent::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("FrameProfilerService", 0x05d1bb90));
}
//=========================================================================
// GetIncompatibleServices
//=========================================================================
void FrameProfilerComponent::GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("FrameProfilerService", 0x05d1bb90));
}
//=========================================================================
// GetDependentServices
//=========================================================================
void FrameProfilerComponent::GetDependentServices(ComponentDescriptor::DependencyArrayType& dependent)
{
dependent.push_back(AZ_CRC("MemoryService", 0x5c4d473c));
}
//=========================================================================
// Reflect
//=========================================================================
void FrameProfilerComponent::Reflect(ReflectContext* context)
{
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
{
serializeContext->Class<FrameProfilerComponent, AZ::Component>()
->Version(1)
->Field("numFramesStored", &FrameProfilerComponent::m_numFramesStored)
->Field("pauseOnFrame", &FrameProfilerComponent::m_pauseOnFrame)
;
if (EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<FrameProfilerComponent>(
"Frame Profiler", "Performs per frame profiling (FPS counter, registers, etc.)")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Profiling")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
->DataElement(AZ::Edit::UIHandlers::SpinBox, &FrameProfilerComponent::m_numFramesStored, "Number of Frames", "How many frames we will keep with the RUNTIME buffers.")
->Attribute(AZ::Edit::Attributes::Min, 1)
->DataElement(AZ::Edit::UIHandlers::SpinBox, &FrameProfilerComponent::m_pauseOnFrame, "Pause on frame", "Paused the engine (debug break) on a specific frame. 0 means no pause!")
;
}
}
}
} // namespace Debug
} // namespace AZ

@ -1,75 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_FRAME_PROFILER_COMPONENT_H
#define AZCORE_FRAME_PROFILER_COMPONENT_H
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Debug/FrameProfiler.h>
#include <AzCore/std/parallel/threadbus.h>
#include <AzCore/Math/Crc.h>
namespace AZ
{
namespace Debug
{
/**
* Frame profiler component provides a frame profiling information
* (from FPS counter to profiler registers manipulation and so on).
* It's a debug system so it should not be active in release
*/
class FrameProfilerComponent
: public Component
, public AZ::TickBus::Handler
{
public:
AZ_COMPONENT(AZ::Debug::FrameProfilerComponent, "{B81739EF-ED77-4F67-9D05-6ADF94F0431A}")
FrameProfilerComponent();
virtual ~FrameProfilerComponent();
private:
//////////////////////////////////////////////////////////////////////////
// Component base
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Tick bus
void OnTick(float deltaTime, ScriptTimePoint time) override;
int GetTickOrder() override;
//////////////////////////////////////////////////////////////////////////
/// \ref ComponentDescriptor::GetProvidedServices
static void GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided);
/// \ref ComponentDescriptor::GetIncompatibleServices
static void GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible);
/// \ref ComponentDescriptor::GetDependentServices
static void GetDependentServices(ComponentDescriptor::DependencyArrayType& dependent);
/// \red ComponentDescriptor::Reflect
static void Reflect(ReflectContext* reflection);
/// callback for reading profiler registers
bool ReadProfilerRegisters(const ProfilerRegister& reg, const AZStd::thread_id& id);
// Keep in mind memory usage, increases quickly. Prefer remote tools (where the history is kept on the PC) instead of keeping long history
unsigned int m_numFramesStored; ///< Number of frames that we will store in history buffers. >= 1
unsigned int m_frameId; ///< Frame id (it's just counted from the start).
unsigned int m_pauseOnFrame; ///< Allows you to specify a frame the code will pause onto.
FrameProfiler::ThreadDataArray m_threads; ///< Array with samplers for all threads
FrameProfiler::ThreadData* m_currentThreadData; ///< Cached pointer to the last accessed thread data.
};
}
}
#endif // AZCORE_FRAME_PROFILER_COMPONENT_H
#pragma once

@ -7,664 +7,4 @@
*/
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Debug/ProfilerDrillerBus.h>
#include <AzCore/std/containers/list.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/std/parallel/shared_spin_mutex.h>
#include <AzCore/std/parallel/lock.h>
#include <AzCore/std/functional.h>
#include <AzCore/Math/Crc.h>
namespace AZ
{
uint32_t ProfileScope::GetSystemID(const char* system)
{
// TODO: stable ids for registered budgets
return AZ::Crc32(system);
}
namespace Debug
{
//////////////////////////////////////////////////////////////////////////
// Globals
AZStd::chrono::microseconds ProfilerRegister::TimeData::s_startStopOverheadPer1000Calls(0);
Profiler* Profiler::s_instance = nullptr;
u64 Profiler::s_id = 0;
int Profiler::s_useCount = 0;
//////////////////////////////////////////////////////////////////////////
/**
* Profile data stored per thread.
*/
struct ProfilerThreadData
{
static const int m_maxStackSize = 32;
typedef AZStd::list<ProfilerRegister, OSStdAllocator> ProfilerRegisterList;
typedef AZStd::fixed_vector<ProfilerSection*, m_maxStackSize> ProfilerSectionStack;
AZStd::thread::id m_id; ///< Thread id.
ProfilerRegisterList m_registers; ///< Thread profiler registers (for this thread).
mutable AZStd::shared_spin_mutex m_registersLock; ///< Lock for accessing thread profiler entries. Sadly the only reason for this to exists is so we can read safe the register counters.
ProfilerSectionStack m_stack; ///< Current active sections stack.
};
struct ProfilerSystemData
{
AZ::u32 m_id;
const char* m_name;
bool m_isActive;
};
/**
* Profiler class data (hidden in from the header file)
*/
struct ProfilerData
{
AZ_CLASS_ALLOCATOR(ProfilerData, OSAllocator, 0);
AZStd::fixed_vector<ProfilerThreadData, Profiler::m_maxNumberOfThreads> m_threads; ///< Array with thread with all belonging information.
AZStd::shared_spin_mutex m_threadDataMutex; ///< Spin read/write lock (shared_mutex) for access to the m_threads.
AZStd::fixed_vector<ProfilerSystemData, Profiler::m_maxNumberOfSystems> m_systems; ///< Array with systems (profiler/timer groups) that you can enable/disable.
};
//=========================================================================
// Profiler
// [12/3/2012]
//=========================================================================
Profiler::Profiler(const Descriptor& desc)
{
(void)desc;
m_data = aznew ProfilerData;
// we can periodically call this function (like the end of every frame to refresh the current estimation).
ProfilerRegister::TimerComputeStartStopOverhead();
// use a timestamp as and id.
s_id = AZStd::GetTimeUTCMilliSecond();
}
//=========================================================================
// ~Profiler
// [12/3/2012]
//=========================================================================
Profiler::~Profiler()
{
AZ_Assert(s_useCount == 0, "You deleted the profiler while it's still in use.");
s_id = 0;
delete m_data;
}
//=========================================================================
// Create
// [12/3/2012]
//=========================================================================
bool Profiler::Create(const Descriptor& desc)
{
AZ_Assert(s_instance == nullptr, "Profiler is already created!");
if (s_instance != nullptr)
{
return false;
}
s_instance = azcreate(Profiler, (desc), AZ::OSAllocator, "Profiler", 0);
return true;
}
//=========================================================================
// Destroy
// [12/3/2012]
//=========================================================================
void Profiler::Destroy()
{
AZ_Assert(s_instance != nullptr, "Profiler not created");
if (s_instance)
{
azdestroy(s_instance, AZ::OSAllocator);
s_instance = nullptr;
}
}
//=========================================================================
// AddReference
// [5/24/2013]
//=========================================================================
void Profiler::AddReference()
{
++s_useCount;
}
//=========================================================================
//
// [5/24/2013]
//=========================================================================
void Profiler::ReleaseReference()
{
AZ_Assert(s_useCount > 0, "Use count is already 0, you can't release it!");
--s_useCount;
if (s_useCount == 0)
{
Destroy();
}
}
//=========================================================================
// RegisterSystem
// [12/3/2012]
//=========================================================================
bool Profiler::RegisterSystem(AZ::u32 systemId, const char* name, bool isActive)
{
for (size_t i = 0; i < m_data->m_systems.size(); ++i)
{
if (m_data->m_systems[i].m_id == systemId)
{
return false;
}
}
ProfilerSystemData sd;
sd.m_id = systemId;
sd.m_isActive = isActive;
sd.m_name = name;
m_data->m_systems.push_back(sd);
return true;
}
//=========================================================================
// UnregisterSystem
// [12/3/2012]
//=========================================================================
bool Profiler::UnregisterSystem(AZ::u32 systemId)
{
size_t i = 0;
for (; i < m_data->m_systems.size(); ++i)
{
ProfilerSystemData& sd = m_data->m_systems[i];
if (sd.m_id == systemId)
{
if (sd.m_isActive)
{
DeactivateSystem(sd.m_name);
}
// Make sure we triggest driller message when the m_threadDataMutex is NOT locked
// as we lock them in reverse order when we update the profile driller.
AZ_Assert(false, "Currently this code is unused. If we do use it, we should make call EBUS even outside of this function. Where m_threadDataMutex is NOT locked!");
//EBUS_DBG_EVENT(ProfilerDrillerBus,OnUnregisterSystem,systemId);
break;
}
}
if (i < m_data->m_systems.size())
{
m_data->m_systems.erase(m_data->m_systems.begin() + i);
return true;
}
return false;
}
//=========================================================================
// ActivateSystem
// [12/3/2012]
//=========================================================================
bool Profiler::SetSystemState(AZ::u32 systemId, bool isActive)
{
for (size_t i = 0; i < m_data->m_systems.size(); ++i)
{
ProfilerSystemData& sd = m_data->m_systems[i];
if (sd.m_id == systemId)
{
if (sd.m_isActive != isActive)
{
sd.m_isActive = isActive;
size_t numThreads = m_data->m_threads.size();
for (size_t j = 0; j < numThreads; ++j)
{
ProfilerThreadData& data = m_data->m_threads[j];
ProfilerThreadData::ProfilerRegisterList::iterator it = data.m_registers.begin();
ProfilerThreadData::ProfilerRegisterList::iterator end = data.m_registers.end();
for (; it != end; ++it)
{
ProfilerRegister& reg = *it;
// This is a big question since this is the only writer
// and the timers are readers and value should be read atomically (1 byte)
// we should be safe without a synchronization here (as data should be written as we exit)
// at worst we can put a volatile in front of the bool. Either way this should not cause
// crashes or anything
if (reg.m_systemId == systemId)
{
reg.m_isActive = isActive ? 1 : 0;
}
}
}
}
return true;
}
}
return false;
}
//=========================================================================
// ActivateSystem
// [5/24/2013]
//=========================================================================
void Profiler::ActivateSystem(const char* systemName)
{
AZ::u32 systemId = AZ::Crc32(systemName);
bool isNewSystem = false;
{
AZStd::unique_lock<AZStd::shared_spin_mutex> writeLock(m_data->m_threadDataMutex);
if (!SetSystemState(systemId, true))
{
// if the system is new add it
RegisterSystem(systemId, systemName, true);
isNewSystem = true;
}
}
if (isNewSystem)
{
// Make sure we triggest driller message when the m_threadDataMutex is NOT locked
// as we lock them in reverse order when we update the profile driller.
EBUS_DBG_EVENT(ProfilerDrillerBus, OnRegisterSystem, systemId, systemName);
}
}
//=========================================================================
// DeactivateSystem
// [5/24/2013]
//=========================================================================
void Profiler::DeactivateSystem(const char* systemName)
{
AZ::u32 systemId = AZ::Crc32(systemName);
bool isNewSystem = false;
{
AZStd::unique_lock<AZStd::shared_spin_mutex> writeLock(m_data->m_threadDataMutex);
if (!SetSystemState(systemId, false))
{
// if the system is new add it
RegisterSystem(systemId, systemName, false);
isNewSystem = true;
}
}
if (isNewSystem)
{
// Make sure we triggest driller message when the m_threadDataMutex is NOT locked
// as we lock them in reverse order when we update the profile driller.
EBUS_DBG_EVENT(ProfilerDrillerBus, OnRegisterSystem, systemId, systemName);
}
}
//=========================================================================
// IsSystemActive
// [5/24/2013]
//=========================================================================
bool Profiler::IsSystemActive(const char* systemName) const
{
return IsSystemActive(AZ::Crc32(systemName));
}
//=========================================================================
// IsSystemActive
// [12/3/2012]
//=========================================================================
bool Profiler::IsSystemActive(AZ::u32 systemId) const
{
for (size_t i = 0; i < m_data->m_systems.size(); ++i)
{
if (m_data->m_systems[i].m_id == systemId)
{
return m_data->m_systems[i].m_isActive != 0;
}
}
return false;
}
//=========================================================================
// GetNumberOfSystems
// [12/3/2012]
//=========================================================================
int Profiler::GetNumberOfSystems() const
{
return static_cast<int>(m_data->m_systems.size());
}
//=========================================================================
// GetSystemName
// [12/3/2012]
//=========================================================================
const char* Profiler::GetSystemName(int index) const
{
return m_data->m_systems[index].m_name;
}
//=========================================================================
// GetSystemName
// [12/3/2012]
//=========================================================================
const char* Profiler::GetSystemName(AZ::u32 systemId) const
{
for (size_t i = 0; i < m_data->m_systems.size(); ++i)
{
if (m_data->m_systems[i].m_id == systemId)
{
return m_data->m_systems[i].m_name;
}
}
return NULL;
}
//=========================================================================
// RemoveThreadData
// [12/3/2012]
//=========================================================================
void Profiler::RemoveThreadData(AZStd::thread_id id)
{
// this is very tricky we must be sure that thread is no longer operational
// otherwise we will crash badly. We can do only because nobody should
// reference this registers but the thread local data.
AZStd::unique_lock<AZStd::shared_spin_mutex> writeLock(m_data->m_threadDataMutex);
size_t numThreads = m_data->m_threads.size();
ProfilerThreadData* threadData = NULL;
for (size_t i = 0; i < numThreads; ++i)
{
ProfilerThreadData& data = m_data->m_threads[i];
if (data.m_id == id)
{
threadData = &data;
break;
}
}
if (threadData)
{
// delete all registers, we can remove the thread data too, but we will need to switch the structure to list
// so far this is super minimal overhead, registers are more.
threadData->m_registers.clear();
}
}
//=========================================================================
// ReadRegisterValues
// [12/3/2012]
//=========================================================================
void Profiler::ReadRegisterValues(const ReadProfileRegisterCB& callback, AZ::u32 systemFilter, const AZStd::thread_id* threadFilter) const
{
AZStd::shared_lock<AZStd::shared_spin_mutex> readLock(s_instance->m_data->m_threadDataMutex);
size_t numThreads = Profiler::s_instance->m_data->m_threads.size();
for (size_t i = 0; i < numThreads; ++i)
{
const ProfilerThreadData& data = s_instance->m_data->m_threads[i];
if (threadFilter && *threadFilter != data.m_id)
{
continue;
}
{
AZStd::shared_lock<AZStd::shared_spin_mutex> registersLock(data.m_registersLock);
ProfilerThreadData::ProfilerRegisterList::const_iterator it = data.m_registers.begin();
ProfilerThreadData::ProfilerRegisterList::const_iterator end = data.m_registers.end();
for (; it != end; ++it)
{
const ProfilerRegister& reg = *it;
if (!reg.m_isActive || (systemFilter != 0 && systemFilter != reg.m_systemId))
{
continue;
}
if (!callback(reg, data.m_id))
{
return;
}
}
}
}
}
//=========================================================================
// ResetRegisters
// [12/4/2012]
//=========================================================================
void Profiler::ResetRegisters()
{
AZStd::unique_lock<AZStd::shared_spin_mutex> writeLock(s_instance->m_data->m_threadDataMutex);
size_t numThreads = Profiler::s_instance->m_data->m_threads.size();
for (size_t i = 0; i < numThreads; ++i)
{
ProfilerThreadData& data = s_instance->m_data->m_threads[i];
{
AZStd::unique_lock<AZStd::shared_spin_mutex> registersLock(data.m_registersLock);
ProfilerThreadData::ProfilerRegisterList::iterator it = data.m_registers.begin();
ProfilerThreadData::ProfilerRegisterList::iterator end = data.m_registers.end();
for (; it != end; ++it)
{
ProfilerRegister& reg = *it;
reg.Reset();
}
}
}
// Reset registers event
}
//=========================================================================
// CreateRegister
// [6/28/2013]
//=========================================================================
ProfilerRegister*
ProfilerRegister::CreateRegister(const char* systemName, const char* name, const char* function, int line, ProfilerRegister::Type type)
{
static AZ_THREAD_LOCAL ProfilerThreadData* threadData = nullptr;
static AZ_THREAD_LOCAL u64 profilerId = 0;
if (profilerId != Profiler::s_id)
{
threadData = nullptr; // profiler has changed
profilerId = Profiler::s_id;
}
AZ::u32 systemId = AZ::Crc32(systemName);
ProfilerRegister* reg;
{
AZStd::unique_lock<AZStd::shared_spin_mutex> writeLock(Profiler::s_instance->m_data->m_threadDataMutex);
// make sure we have the system registered. This function will just return false if the system exists.
if (systemName)
{
Profiler::s_instance->RegisterSystem(systemId, systemName, true);
}
if (threadData == nullptr) // if this is a new thread add the data
{
AZStd::thread::id threadId = AZStd::this_thread::get_id();
Profiler::s_instance->m_data->m_threads.push_back();
threadData = &Profiler::s_instance->m_data->m_threads.back();
threadData->m_id = threadId;
}
threadData->m_registers.push_back();
reg = &threadData->m_registers.back();
reg->m_name = name;
reg->m_function = function;
reg->m_line = line;
reg->m_systemId = systemId;
reg->m_isActive = Profiler::s_instance->IsSystemActive(systemId) ? 1 : 0;
reg->m_type = type;
reg->m_threadData = threadData;
reg->Reset();
}
// Make sure we triggest driller message when the m_threadDataMutex is NOT locked
// as we lock them in reverse order when we update the profile driller.
EBUS_DBG_EVENT(ProfilerDrillerBus, OnNewRegister, *reg, threadData->m_id);
return reg;
}
//=========================================================================
// TimerCreateAndStart
// [11/30/2012]
//=========================================================================
ProfilerRegister*
ProfilerRegister::TimerCreateAndStart(const char* systemName, const char* name, ProfilerSection * section, const char* function, int line)
{
AZStd::chrono::system_clock::time_point start = AZStd::chrono::system_clock::now();
ProfilerRegister* reg = CreateRegister(systemName, name, function, line, ProfilerRegister::PRT_TIME);
AZStd::chrono::system_clock::time_point end = AZStd::chrono::system_clock::now();
// adjust the parent timer with the overhead we incur during timer operations. (TODO with TLS this is so fast that we might not need to do it)
if (!reg->m_threadData->m_stack.empty()) // if we are not he last element
{
AZStd::chrono::microseconds elapsed = end - start;
reg->m_threadData->m_stack.back()->m_childTime += elapsed; // no need to check if we go in the future as this will happen on Stop
}
if (reg->m_isActive)
{
section->m_register = reg;
section->m_start = end;
reg->m_threadData->m_stack.push_back(section);
}
else
{
section->m_register = nullptr;
}
return reg;
}
//=========================================================================
// ValueCreate
// [6/28/2013]
//=========================================================================
ProfilerRegister*
ProfilerRegister::ValueCreate(const char* systemName, const char* name, const char* function, int line)
{
return CreateRegister(systemName, name, function, line, ProfilerRegister::PRT_VALUE);
}
//=========================================================================
// TimerStart
// [11/29/2012]
//=========================================================================
void ProfilerRegister::TimerStart(ProfilerSection* section)
{
ProfilerRegister* reg = this;
if (reg->m_isActive)
{
section->m_register = reg;
reg->m_threadData->m_stack.push_back(section);
section->m_start = AZStd::chrono::system_clock::now();
}
else
{
section->m_register = nullptr;
}
}
//=========================================================================
// TimerStop
// [11/29/2012]
//=========================================================================
void ProfilerRegister::TimerStop()
{
AZStd::chrono::system_clock::time_point end = AZStd::chrono::system_clock::now();
ProfilerSection* section = m_threadData->m_stack.back();
AZStd::chrono::microseconds elapsedTime = end - section->m_start;
{
m_threadData->m_registersLock.lock(); // lock for write
++m_timeData.m_calls;
m_timeData.m_time += elapsedTime.count();
m_timeData.m_childrenTime += section->m_childTime.count();
m_timeData.m_childrenCalls += section->m_childCalls;
m_threadData->m_registersLock.unlock(); // unlock
}
m_threadData->m_stack.pop_back();
// adjust the parent timer with the overhead we incur during timer operations.
if (!m_threadData->m_stack.empty())
{
ProfilerSection* parent = m_threadData->m_stack.back();
m_timeData.m_lastParent = parent->m_register;
parent->m_childTime += elapsedTime /*+ s_startStopOverhead*/; // add the overhead since most of it is in Stop()
++parent->m_childCalls;
}
}
//=========================================================================
// Reset
// [12/4/2012]
//=========================================================================
void ProfilerRegister::Reset()
{
switch (m_type)
{
case PRT_TIME:
{
m_timeData.m_time = 0;
m_timeData.m_childrenTime = 0;
m_timeData.m_calls = 0;
m_timeData.m_childrenCalls = 0;
m_timeData.m_lastParent = nullptr;
} break;
case PRT_VALUE:
{
m_userValues.m_value1 = 0;
m_userValues.m_value2 = 0;
m_userValues.m_value3 = 0;
m_userValues.m_value4 = 0;
m_userValues.m_value5 = 0;
} break;
}
}
//=========================================================================
// ComputeStartStopOverhead
// [12/3/2012]
//=========================================================================
void ProfilerRegister::TimerComputeStartStopOverhead()
{
// compute default thread start stop overhead
ProfilerThreadData sampleThreadData;
sampleThreadData.m_id = AZStd::this_thread::get_id();
sampleThreadData.m_registers.push_back();
ProfilerRegister& sampleRegister = sampleThreadData.m_registers.back();
sampleRegister.m_isActive = true;
sampleRegister.m_name = nullptr;
sampleRegister.m_systemId = 0;
sampleRegister.m_threadData = &sampleThreadData;
const int numSamples = 1000;
for (int iRepetition = 0; iRepetition < 1000; ++iRepetition) // just for test
{
ProfilerSection section;
sampleRegister.TimerStart(&section);
AZStd::chrono::system_clock::time_point start = AZStd::chrono::system_clock::now();
for (int i = 0; i < numSamples; ++i)
{
static AZ::Debug::ProfilerRegister* sampleRegisterPtr = &sampleRegister; // the creation is timed differently
ProfilerSection subSection;
if (sampleRegisterPtr != NULL)
{
sampleRegister.TimerStart(&subSection);
}
}
AZStd::chrono::microseconds elapsed = (AZStd::chrono::system_clock::now() - start);
if (TimeData::s_startStopOverheadPer1000Calls.count() == 0) // if first time set otherwise smooth average
{
TimeData::s_startStopOverheadPer1000Calls = elapsed;
}
else
{
float fNew = static_cast<float>(elapsed.count());
float fCurrent = static_cast<float>(TimeData::s_startStopOverheadPer1000Calls.count());
int deltaValue = static_cast<int>((fNew - fCurrent) * 0.1f);
if (deltaValue < 0)
{
TimeData::s_startStopOverheadPer1000Calls -= AZStd::chrono::microseconds(-deltaValue);
}
else
{
TimeData::s_startStopOverheadPer1000Calls += AZStd::chrono::microseconds(deltaValue);
}
}
}
//AZ_TracePrintf("Profiler","Overhead %d microseconds per 1000 profile calls!\n",TimeData::s_startStopOverheadPer1000Calls.count());
}
}
} // namespace AZ

@ -7,42 +7,49 @@
*/
#pragma once
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/function/function_fwd.h>
#include <AzCore/Debug/Budget.h>
#ifdef USE_PIX
#include <AzCore/PlatformIncl.h>
#include <WinPixEventRuntime/pix3.h>
#endif
#if defined(AZ_PROFILER_MACRO_DISABLE) // by default we never disable the profiler registers as their overhead should be minimal, you can still do that for your code though.
# define AZ_PROFILE_SCOPE(...)
# define AZ_PROFILE_FUNCTION(...)
# define AZ_PROFILE_BEGIN(...)
# define AZ_PROFILE_END(...)
#if defined(AZ_PROFILER_MACRO_DISABLE) // by default we never disable the profiler registers as their overhead should be minimal, you can
// still do that for your code though.
#define AZ_PROFILE_SCOPE(...)
#define AZ_PROFILE_FUNCTION(...)
#define AZ_PROFILE_BEGIN(...)
#define AZ_PROFILE_END(...)
#else
/**
* Macro to declare a profile section for the current scope { }.
* format is: AZ_PROFILE_SCOPE(categoryName, const char* formatStr, ...)
*/
# define AZ_PROFILE_SCOPE(category, ...) ::AZ::ProfileScope AZ_JOIN(azProfileScope, __LINE__){ #category, __VA_ARGS__ }
# define AZ_PROFILE_FUNCTION(category) AZ_PROFILE_SCOPE(category, AZ_FUNCTION_SIGNATURE)
#define AZ_PROFILE_SCOPE(budget, ...) \
::AZ::Debug::ProfileScope AZ_JOIN(azProfileScope, __LINE__) \
{ \
AZ_BUDGET_GETTER(budget)(), __VA_ARGS__ \
}
#define AZ_PROFILE_FUNCTION(category) AZ_PROFILE_SCOPE(category, AZ_FUNCTION_SIGNATURE)
// Prefer using the scoped macros which automatically end the event (AZ_PROFILE_SCOPE/AZ_PROFILE_FUNCTION)
# define AZ_PROFILE_BEGIN(category, ...) ::AZ::ProfileScope::BeginRegion(#category, __VA_ARGS__)
# define AZ_PROFILE_END() ::AZ::ProfileScope::EndRegion()
#define AZ_PROFILE_BEGIN(budget, ...) ::AZ::Debug::ProfileScope::BeginRegion(AZ_BUDGET_GETTER(budget)(), __VA_ARGS__)
#define AZ_PROFILE_END(budget) ::AZ::Debug::ProfileScope::EndRegion(AZ_BUDGET_GETTER(budget)())
#endif // AZ_PROFILER_MACRO_DISABLE
#ifndef AZ_PROFILE_INTERVAL_START
# define AZ_PROFILE_INTERVAL_START(...)
# define AZ_PROFILE_INTERVAL_START_COLORED(...)
# define AZ_PROFILE_INTERVAL_END(...)
# define AZ_PROFILE_INTERVAL_SCOPED(...)
#define AZ_PROFILE_INTERVAL_START(...)
#define AZ_PROFILE_INTERVAL_START_COLORED(...)
#define AZ_PROFILE_INTERVAL_END(...)
#define AZ_PROFILE_INTERVAL_SCOPED(...)
#endif
#ifndef AZ_PROFILE_DATAPOINT
# define AZ_PROFILE_DATAPOINT(...)
# define AZ_PROFILE_DATAPOINT_PERCENT(...)
#define AZ_PROFILE_DATAPOINT(...)
#define AZ_PROFILE_DATAPOINT_PERCENT(...)
#endif
namespace AZStd
@ -50,340 +57,25 @@ namespace AZStd
struct thread_id; // forward declare. This is the same type as AZStd::thread::id
}
namespace AZ
namespace AZ::Debug
{
class ProfileScope
{
public:
static uint32_t GetSystemID(const char* system);
template<typename... T>
static void BeginRegion([[maybe_unused]] const char* system, [[maybe_unused]] const char* eventName, [[maybe_unused]] T const&... args)
{
// TODO: Verification that the supplied system name corresponds to a known budget
#if defined(USE_PIX)
PIXBeginEvent(PIX_COLOR_INDEX(GetSystemID(system) & 0xff), eventName, args...);
#endif
// TODO: injecting instrumentation for other profilers
// NOTE: external profiler registration won't occur inline in a header necessarily in this manner, but the exact mechanism
// will be introduced in a future PR
}
static void BeginRegion([[maybe_unused]] Budget* budget, [[maybe_unused]] const char* eventName, [[maybe_unused]] T const&... args);
static void EndRegion()
{
#if defined(USE_PIX)
PIXEndEvent();
#endif
}
static void EndRegion([[maybe_unused]] Budget* budget);
template<typename... T>
ProfileScope(const char* system, char const* eventName, T const&... args)
{
BeginRegion(system, eventName, args...);
}
~ProfileScope()
{
EndRegion();
}
};
namespace Debug
{
class ProfilerSection;
class ProfilerRegister;
struct ProfilerThreadData;
struct ProfilerData;
/**
*
*/
class Profiler
{
friend class ProfilerRegister;
friend struct ProfilerData;
public:
/// Max number of threads supported by the profiler.
static const int m_maxNumberOfThreads = 32;
/// Max number of systems supported by the profiler. (We can switch this container if needed)
static const int m_maxNumberOfSystems = 64;
~Profiler();
struct Descriptor
{
};
static bool Create(const Descriptor& desc = Descriptor());
static void Destroy();
static bool IsReady() { return s_instance != NULL; }
static Profiler& Instance() { return *s_instance; }
static u64 GetId() { return s_id; }
ProfileScope(Budget* budget, char const* eventName, T const&... args);
/// Increment the use count.
static void AddReference();
/// Release the use count if 0 a Destroy will be called automatically.
static void ReleaseReference();
void ActivateSystem(const char* systemName);
void DeactivateSystem(const char* systemName);
bool IsSystemActive(const char* systemName) const;
bool IsSystemActive(AZ::u32 systemId) const;
int GetNumberOfSystems() const;
const char* GetSystemName(int index) const;
const char* GetSystemName(AZ::u32 systemId) const;
/** Callback to read a single register. Make sure you read the data as fast as possible. Don't compute inside the callback
* it will lock and hold all the registers that we process. The best is just to read the value and push it into a
* history buffer.
*/
typedef AZStd::function<bool (const ProfilerRegister&, const AZStd::thread_id&)> ReadProfileRegisterCB;
/**
* Read register values, make sure the code here is fast and efficient as we are holding a lock.
* provide a callback that will be called for each register.
* You can choose to filter your values by thread or a system. Use this filter only to narrow you samples. Don't
* use it for multiple calls to sort your counters, use the history data.
* It addition keep in mind that you can run this function is parallel, as we only read the values.
*/
void ReadRegisterValues(const ReadProfileRegisterCB& callback, AZ::u32 systemFilter = 0, const AZStd::thread_id* threadFilter = NULL) const;
/**
* This is slow operation that will cause contention try to avoid using it. A better way will be each frame instead of reset to read and store
* register values (this is good for history too) and make the difference that way.
*/
void ResetRegisters();
/// You can remove thread data ONLY IF YOU ARE SURE THIS THREAD IS NO LONGER ACTIVE! This will work only is specific cases.
void RemoveThreadData(AZStd::thread_id id);
~ProfileScope();
private:
/// Register a new system in the profiler. Make sure the proper locks are LOCKED when calling this function (m_threadDataMutex)
bool RegisterSystem(AZ::u32 systemId, const char* name, bool isActive);
/// Unregister a system. Make sure the proper locks are LOCKED when calling this function (m_threadDataMutex)
bool UnregisterSystem(AZ::u32 systemId);
/// Sets the system active/inactive state. Make sure the proper locks are LOCKED when calling this function (m_threadDataMutex)
bool SetSystemState(AZ::u32 systemId, bool isActive);
Profiler(const Descriptor& desc);
Profiler& operator=(const Profiler&);
ProfilerData* m_data; ///< Hidden data to reduce the number of header files included;
static Profiler* s_instance; ///< The only instance of the profiler.
static u64 s_id; ///< Profiler unique (over time) id (don't use the pointer as it might be reused).
static int s_useCount;
};
/**
* A profiler "virtual" register that contains data about a certain place in the code.
*/
class ProfilerRegister
{
friend class Profiler;
public:
ProfilerRegister()
{}
enum Type
{
PRT_TIME = 0, ///< Time (members m_time,m_childrenTime,m_calls, m_childrenCalls and m_lastParant are used) register.
PRT_VALUE, ///< Value register
};
/// Time register data.
struct TimeData
{
AZ::u64 m_time; ///< Total inclusive time current and children in microseconds.
AZ::u64 m_childrenTime; ///< Time taken by child profilers in microseconds.
AZ::s64 m_calls; ///< Number of calls for this register.
AZ::s64 m_childrenCalls;///< Number of children calls.
ProfilerRegister* m_lastParent; ///< Pointer to the last parent register.
static AZStd::chrono::microseconds s_startStopOverheadPer1000Calls; ///< Static constant representing a standard start stop overhead per 1000 calls. You can use this to adjust timings.
};
/// Value register data.
struct ValuesData
{
AZ::s64 m_value1;
AZ::s64 m_value2;
AZ::s64 m_value3;
AZ::s64 m_value4;
AZ::s64 m_value5;
};
static ProfilerRegister* TimerCreateAndStart(const char* systemName, const char* name, ProfilerSection* section, const char* function, int line);
static ProfilerRegister* ValueCreate(const char* systemName, const char* name, const char* function, int line);
void TimerStart(ProfilerSection* section);
void ValueSet(const AZ::s64& v1);
void ValueSet(const AZ::s64& v1, const AZ::s64& v2);
void ValueSet(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3);
void ValueSet(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4);
void ValueSet(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4, const AZ::s64& v5);
void ValueAdd(const AZ::s64& v1);
void ValueAdd(const AZ::s64& v1, const AZ::s64& v2);
void ValueAdd(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3);
void ValueAdd(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4);
void ValueAdd(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4, const AZ::s64& v5);
// Dynamic register data
union
{
TimeData m_timeData;
ValuesData m_userValues;
Budget* m_budget;
};
// Static value data.
const char* m_name; ///< Name of the profiler register.
const char* m_function; ///< Function name in the code.
int m_line; ///< Line number if the code.
AZ::u32 m_systemId; ///< ID of the system this profiler belongs to.
unsigned char m_type : 7; ///< Register type
unsigned char m_isActive : 1; ///< Flag if the profiler is active.
private:
friend class ProfilerSection;
static ProfilerRegister* CreateRegister(const char* systemName, const char* name, const char* function, int line, ProfilerRegister::Type type);
/// Compute static start/stop overhead approximation. You can call this periodically (or not) to update the overhead.
static void TimerComputeStartStopOverhead();
void TimerStop();
void Reset();
ProfilerRegister* GetValueRegisterForThisThread();
ProfilerThreadData* m_threadData; ///< Pointer to this entry thread data.
};
/**
* Scoped stop register count on destruction.
*/
class ProfilerSection
{
friend class ProfilerRegister;
public:
ProfilerSection()
: m_register(nullptr)
, m_profilerId(AZ::Debug::Profiler::GetId())
, m_childTime(0)
, m_childCalls(0)
{}
~ProfilerSection()
{
// If we have a valid register and the profiler did not change while we were active stop the register.
if (m_register && m_profilerId == AZ::Debug::Profiler::GetId())
{
m_register->TimerStop();
}
}
void Stop()
{
// If we have a valid register and the profiler did not change while we were active stop the register.
if (m_register && m_profilerId == AZ::Debug::Profiler::GetId())
{
m_register->TimerStop();
}
m_register = nullptr;
}
private:
ProfilerRegister* m_register; ///< Pointer to the owning profiler register.
u64 m_profilerId; ///< Id of the profiler when we started this section.
AZStd::chrono::system_clock::time_point m_start; ///< Start mark.
AZStd::chrono::microseconds m_childTime; ///< Time spent in child profilers.
int m_childCalls; ///< Number of children calls.
};
AZ_FORCE_INLINE ProfilerRegister* ProfilerRegister::GetValueRegisterForThisThread()
{
return this;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueSet(const AZ::s64& v1)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 = v1;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueSet(const AZ::s64& v1, const AZ::s64& v2)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 = v1;
reg->m_userValues.m_value2 = v2;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueSet(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 = v1;
reg->m_userValues.m_value2 = v2;
reg->m_userValues.m_value3 = v3;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueSet(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 = v1;
reg->m_userValues.m_value2 = v2;
reg->m_userValues.m_value3 = v3;
reg->m_userValues.m_value4 = v4;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueSet(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4, const AZ::s64& v5)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 = v1;
reg->m_userValues.m_value2 = v2;
reg->m_userValues.m_value3 = v3;
reg->m_userValues.m_value4 = v4;
reg->m_userValues.m_value5 = v5;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueAdd(const AZ::s64& v1)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 += v1;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueAdd(const AZ::s64& v1, const AZ::s64& v2)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 += v1;
reg->m_userValues.m_value2 += v2;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueAdd(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 += v1;
reg->m_userValues.m_value2 += v2;
reg->m_userValues.m_value3 += v3;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueAdd(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 += v1;
reg->m_userValues.m_value2 += v2;
reg->m_userValues.m_value3 += v3;
reg->m_userValues.m_value4 += v4;
}
AZ_FORCE_INLINE void ProfilerRegister::ValueAdd(const AZ::s64& v1, const AZ::s64& v2, const AZ::s64& v3, const AZ::s64& v4, const AZ::s64& v5)
{
ProfilerRegister* reg = GetValueRegisterForThisThread();
reg->m_userValues.m_value1 += v1;
reg->m_userValues.m_value2 += v2;
reg->m_userValues.m_value3 += v3;
reg->m_userValues.m_value4 += v4;
reg->m_userValues.m_value5 += v5;
}
} // namespace Debug
namespace Internal
{
struct RegisterData
{
AZ::Debug::ProfilerRegister* m_register; ///< Pointer to the register data.
AZ::u64 m_profilerId; ///< Profiler ID which create the \ref register data.
};
}
} // namespace AZ
} // namespace AZ::Debug
#ifdef USE_PIX
// The pix3 header unfortunately brings in other Windows macros we need to undef
@ -391,3 +83,5 @@ namespace AZ
#undef LoadImage
#undef GetCurrentTime
#endif
#include <AzCore/Debug/Profiler.inl>

@ -0,0 +1,57 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
namespace AZ::Debug
{
template<typename... T>
void ProfileScope::BeginRegion(
[[maybe_unused]] Budget* budget, [[maybe_unused]] const char* eventName, [[maybe_unused]] T const&... args)
{
if (!budget)
{
return;
}
#if !defined(_RELEASE)
// TODO: Verification that the supplied system name corresponds to a known budget
#if defined(USE_PIX)
PIXBeginEvent(PIX_COLOR_INDEX(budget->Crc() & 0xff), eventName, args...);
#endif
budget->BeginProfileRegion();
// TODO: injecting instrumentation for other profilers
// NOTE: external profiler registration won't occur inline in a header necessarily in this manner, but the exact mechanism
// will be introduced in a future PR
#endif
}
inline void ProfileScope::EndRegion([[maybe_unused]] Budget* budget)
{
if (!budget)
{
return;
}
#if !defined(_RELEASE)
budget->EndProfileRegion();
#if defined(USE_PIX)
PIXEndEvent();
#endif
#endif
}
template<typename... T>
ProfileScope::ProfileScope(Budget* budget, char const* eventName, T const&... args)
: m_budget{ budget }
{
BeginRegion(budget, eventName, args...);
}
inline ProfileScope::~ProfileScope()
{
EndRegion(m_budget);
}
} // namespace AZ::Debug

@ -1,310 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Debug/ProfilerDriller.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/bind/bind.h>
namespace AZ
{
namespace Debug
{
//=========================================================================
// ProfilerDriller
// [7/9/2013]
//=========================================================================
ProfilerDriller::ProfilerDriller()
{
AZStd::ThreadDrillerEventBus::Handler::BusConnect();
}
//=========================================================================
// ~ProfilerDriller
// [7/9/2013]
//=========================================================================
ProfilerDriller::~ProfilerDriller()
{
AZStd::ThreadDrillerEventBus::Handler::BusDisconnect();
}
//=========================================================================
// Start
// [5/24/2013]
//=========================================================================
void ProfilerDriller::Start(const Param* params, int numParams)
{
for (int i = 0; i < m_numberOfSystemFilters; ++i)
{
m_systemFilters[i].desc = "SystemID of the system which counters we are interested in";
m_systemFilters[i].type = Param::PT_INT;
m_systemFilters[i].value = 0;
}
// Copy valid filters.
m_numberOfValidFilters = 0;
if (params)
{
for (int i = 0; i < numParams; ++i)
{
if (params[i].type == Param::PT_INT && params[i].value != 0)
{
m_systemFilters[m_numberOfValidFilters++].value = params[i].value;
}
}
}
// output current threads
for (ThreadArrayType::iterator it = m_threads.begin(); it != m_threads.end(); ++it)
{
OutputThreadEnter(*it);
}
ProfilerDrillerBus::Handler::BusConnect();
if (!Profiler::IsReady())
{
Profiler::Create();
}
Profiler::AddReference();
}
//=========================================================================
// Stop
// [5/24/2013]
//=========================================================================
void ProfilerDriller::Stop()
{
Profiler::ReleaseReference();
ProfilerDrillerBus::Handler::BusDisconnect();
}
//=========================================================================
// OnError
// [2/8/2013]
//=========================================================================
void ProfilerDriller::Update()
{
// \note We could add thread_id in addition to the System ID, but I can't foresee many cases where we would like to profile only a specific thread.
if (m_numberOfValidFilters)
{
for (int iFilter = 0; iFilter < m_numberOfValidFilters; ++iFilter)
{
AZ::u32 systemFilter = *reinterpret_cast<AZ::u32*>(&m_systemFilters[iFilter].value);
Profiler::Instance().ReadRegisterValues(AZStd::bind(&ProfilerDriller::ReadProfilerRegisters, this, AZStd::placeholders::_1, AZStd::placeholders::_2), systemFilter);
}
}
else
{
Profiler::Instance().ReadRegisterValues(AZStd::bind(&ProfilerDriller::ReadProfilerRegisters, this, AZStd::placeholders::_1, AZStd::placeholders::_2), 0);
}
}
//=========================================================================
// ReadProfilerRegisters
// [2/11/2013]
//=========================================================================
bool ProfilerDriller::ReadProfilerRegisters(const ProfilerRegister& reg, const AZStd::thread_id& id)
{
(void)id;
m_output->BeginTag(AZ_CRC("ProfilerDriller", 0x172c5268));
m_output->BeginTag(AZ_CRC("UpdateRegister", 0x6c00b890));
m_output->Write(AZ_CRC("Id", 0xbf396750), &reg);
// Send only the data which is changing
switch (reg.m_type)
{
case ProfilerRegister::PRT_TIME:
{
m_output->Write(AZ_CRC("Time", 0x6f949845), reg.m_timeData.m_time);
m_output->Write(AZ_CRC("ChildrenTime", 0x46162d3f), reg.m_timeData.m_childrenTime);
m_output->Write(AZ_CRC("Calls", 0xdaa35c8f), reg.m_timeData.m_calls);
m_output->Write(AZ_CRC("ChildrenCalls", 0x6a5a4618), reg.m_timeData.m_childrenCalls);
m_output->Write(AZ_CRC("ParentId", 0x856a684c), reg.m_timeData.m_lastParent);
} break;
case ProfilerRegister::PRT_VALUE:
{
m_output->Write(AZ_CRC("Value1", 0xa2756c5a), reg.m_userValues.m_value1);
m_output->Write(AZ_CRC("Value2", 0x3b7c3de0), reg.m_userValues.m_value2);
m_output->Write(AZ_CRC("Value3", 0x4c7b0d76), reg.m_userValues.m_value3);
m_output->Write(AZ_CRC("Value4", 0xd21f98d5), reg.m_userValues.m_value4);
m_output->Write(AZ_CRC("Value5", 0xa518a843), reg.m_userValues.m_value5);
} break;
}
m_output->EndTag(AZ_CRC("UpdateRegister", 0x6c00b890));
m_output->EndTag(AZ_CRC("ProfilerDriller", 0x172c5268));
return true;
}
//=========================================================================
// OnThreadEnter
// [5/31/2013]
//=========================================================================
void ProfilerDriller::OnThreadEnter(const AZStd::thread_id& id, const AZStd::thread_desc* desc)
{
m_threads.push_back();
ThreadInfo& info = m_threads.back();
info.m_id = (size_t)id.m_id;
if (desc)
{
info.m_name = desc->m_name;
info.m_cpuId = desc->m_cpuId;
info.m_priority = desc->m_priority;
info.m_stackSize = desc->m_stackSize;
}
else
{
info.m_name = nullptr;
info.m_cpuId = -1;
info.m_priority = -100000;
info.m_stackSize = 0;
}
if (m_output)
{
OutputThreadEnter(info);
}
}
//=========================================================================
// OnThreadExit
// [5/31/2013]
//=========================================================================
void ProfilerDriller::OnThreadExit(const AZStd::thread_id& id)
{
ThreadArrayType::iterator it = m_threads.begin();
while (it != m_threads.end())
{
if (it->m_id == (size_t)id.m_id)
{
break;
}
++it;
}
if (it != m_threads.end())
{
if (m_output)
{
OutputThreadExit(*it);
}
m_threads.erase(it);
}
}
//=========================================================================
// OutputThreadEnter
// [7/9/2013]
//=========================================================================
void ProfilerDriller::OutputThreadEnter(const ThreadInfo& threadInfo)
{
m_output->BeginTag(AZ_CRC("ProfilerDriller", 0x172c5268));
m_output->BeginTag(AZ_CRC("ThreadEnter", 0x60e4acfb));
m_output->Write(AZ_CRC("Id", 0xbf396750), threadInfo.m_id);
if (threadInfo.m_name)
{
m_output->Write(AZ_CRC("Name", 0x5e237e06), threadInfo.m_name);
}
m_output->Write(AZ_CRC("CpuId", 0xdf558508), threadInfo.m_cpuId);
m_output->Write(AZ_CRC("Priority", 0x62a6dc27), threadInfo.m_priority);
m_output->Write(AZ_CRC("StackSize", 0x9cfaf35b), threadInfo.m_stackSize);
m_output->EndTag(AZ_CRC("ThreadEnter", 0x60e4acfb));
m_output->EndTag(AZ_CRC("ProfilerDriller", 0x172c5268));
}
//=========================================================================
// OutputThreadExit
// [7/9/2013]
//=========================================================================
void ProfilerDriller::OutputThreadExit(const ThreadInfo& threadInfo)
{
m_output->BeginTag(AZ_CRC("ProfilerDriller", 0x172c5268));
m_output->BeginTag(AZ_CRC("OnThreadExit", 0x16042db9));
m_output->Write(AZ_CRC("Id", 0xbf396750), threadInfo.m_id);
m_output->EndTag(AZ_CRC("OnThreadExit", 0x16042db9));
m_output->EndTag(AZ_CRC("ProfilerDriller", 0x172c5268));
}
//=========================================================================
// OnRegisterSystem
// [5/31/2013]
//=========================================================================
void ProfilerDriller::OnRegisterSystem(AZ::u32 id, const char* name)
{
m_output->BeginTag(AZ_CRC("ProfilerDriller", 0x172c5268));
m_output->BeginTag(AZ_CRC("RegisterSystem", 0x957739ef));
m_output->Write(AZ_CRC("Id", 0xbf396750), id);
m_output->Write(AZ_CRC("Name", 0x5e237e06), name);
m_output->EndTag(AZ_CRC("RegisterSystem", 0x957739ef));
m_output->EndTag(AZ_CRC("ProfilerDriller", 0x172c5268));
}
//=========================================================================
// OnUnregisterSystem
// [5/31/2013]
//=========================================================================
void ProfilerDriller::OnUnregisterSystem(AZ::u32 id)
{
m_output->BeginTag(AZ_CRC("ProfilerDriller", 0x172c5268));
m_output->BeginTag(AZ_CRC("UnregisterSystem", 0xa20538e4));
m_output->Write(AZ_CRC("Id", 0xbf396750), id);
m_output->EndTag(AZ_CRC("UnregisterSystem", 0xa20538e4));
m_output->EndTag(AZ_CRC("ProfilerDriller", 0x172c5268));
}
//=========================================================================
// OnNewRegister
// [5/31/2013]
//=========================================================================
void ProfilerDriller::OnNewRegister(const ProfilerRegister& reg, const AZStd::thread_id& threadId)
{
m_output->BeginTag(AZ_CRC("ProfilerDriller", 0x172c5268));
m_output->BeginTag(AZ_CRC("NewRegister", 0xf0f2f287));
m_output->Write(AZ_CRC("Id", 0xbf396750), &reg);
m_output->Write(AZ_CRC("ThreadId", 0xd0fd9043), threadId.m_id);
if (reg.m_name)
{
m_output->Write(AZ_CRC("Name", 0x5e237e06), reg.m_name);
}
if (reg.m_function)
{
m_output->Write(AZ_CRC("Function", 0xcaae163d), reg.m_function);
}
m_output->Write(AZ_CRC("Line", 0xd114b4f6), reg.m_line);
m_output->Write(AZ_CRC("SystemId", 0x0dfecf6f), reg.m_systemId);
m_output->Write(AZ_CRC("Type", 0x8cde5729), reg.m_type);
switch (reg.m_type)
{
case ProfilerRegister::PRT_TIME:
{
m_output->Write(AZ_CRC("Time", 0x6f949845), reg.m_timeData.m_time);
m_output->Write(AZ_CRC("ChildrenTime", 0x46162d3f), reg.m_timeData.m_childrenTime);
m_output->Write(AZ_CRC("Calls", 0xdaa35c8f), reg.m_timeData.m_calls);
m_output->Write(AZ_CRC("ChildrenCalls", 0x6a5a4618), reg.m_timeData.m_childrenCalls);
m_output->Write(AZ_CRC("ParentId", 0x856a684c), reg.m_timeData.m_lastParent);
} break;
case ProfilerRegister::PRT_VALUE:
{
m_output->Write(AZ_CRC("Value1", 0xa2756c5a), reg.m_userValues.m_value1);
m_output->Write(AZ_CRC("Value2", 0x3b7c3de0), reg.m_userValues.m_value2);
m_output->Write(AZ_CRC("Value3", 0x4c7b0d76), reg.m_userValues.m_value3);
m_output->Write(AZ_CRC("Value4", 0xd21f98d5), reg.m_userValues.m_value4);
m_output->Write(AZ_CRC("Value5", 0xa518a843), reg.m_userValues.m_value5);
} break;
}
m_output->EndTag(AZ_CRC("NewRegister", 0xf0f2f287));
m_output->EndTag(AZ_CRC("ProfilerDriller", 0x172c5268));
}
} // namespace Debug
} // namespace AZ

@ -1,102 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_PROFILER_DRILLER_H
#define AZCORE_PROFILER_DRILLER_H 1
#include <AzCore/Driller/Driller.h>
#include <AzCore/Debug/ProfilerDrillerBus.h>
#include <AzCore/std/parallel/threadbus.h>
namespace AZStd
{
struct thread_id;
struct thread_desc;
}
namespace AZ
{
namespace Debug
{
struct ProfilerSystemData;
class ProfilerRegister;
/**
* ProfilerDriller or we can just make a Profiler driller and read the registers ourself.
*/
class ProfilerDriller
: public Driller
, public ProfilerDrillerBus::Handler
, public AZStd::ThreadDrillerEventBus::Handler
{
struct ThreadInfo
{
AZ::u64 m_id;
AZ::u32 m_stackSize;
AZ::s32 m_priority;
AZ::s32 m_cpuId;
const char* m_name;
};
typedef vector<ThreadInfo>::type ThreadArrayType;
public:
AZ_CLASS_ALLOCATOR(ProfilerDriller, OSAllocator, 0)
ProfilerDriller();
virtual ~ProfilerDriller();
protected:
//////////////////////////////////////////////////////////////////////////
// Driller
virtual const char* GroupName() const { return "SystemDrillers"; }
virtual const char* GetName() const { return "ProfilerDriller"; }
virtual const char* GetDescription() const { return "Collects data from all available profile registers."; }
virtual int GetNumParams() const { return m_numberOfSystemFilters; }
virtual const Param* GetParam(int index) const { AZ_Assert(index >= 0 && index < m_numberOfSystemFilters, "Invalid index"); return &m_systemFilters[index]; }
virtual void Start(const Param* params = NULL, int numParams = 0);
virtual void Stop();
virtual void Update();
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Thread driller event bus
/// Called when we enter a thread, optional thread_desc is provided when the use provides one.
virtual void OnThreadEnter(const AZStd::thread_id& id, const AZStd::thread_desc* desc);
/// Called when we exit a thread.
virtual void OnThreadExit(const AZStd::thread_id& id);
/// Output thread enter to stream.
void OutputThreadEnter(const ThreadInfo& threadInfo);
/// Output thread exit to stream.
void OutputThreadExit(const ThreadInfo& threadInfo);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Profiler Driller bus
virtual void OnRegisterSystem(AZ::u32 id, const char* name);
virtual void OnUnregisterSystem(AZ::u32 id);
virtual void OnNewRegister(const ProfilerRegister& reg, const AZStd::thread_id& threadId);
//////////////////////////////////////////////////////////////////////////
/// Read profile registers callback.
bool ReadProfilerRegisters(const ProfilerRegister& reg, const AZStd::thread_id& id);
static const int m_numberOfSystemFilters = 16;
int m_numberOfValidFilters = 0 ; ///< Number of valid filter set when the driller was created.
Param m_systemFilters[m_numberOfSystemFilters]; ///< If != 0, it's a ID of specific System we would like to drill.
ThreadArrayType m_threads;
};
}
} // namespace AZ
#endif // AZCORE_PROFILER_DRILLER_H
#pragma once

@ -1,45 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_PROFILER_DRILLER_BUS_H
#define AZCORE_PROFILER_DRILLER_BUS_H
#include <AzCore/Driller/DrillerBus.h>
namespace AZStd
{
struct thread_id;
}
namespace AZ
{
namespace Debug
{
class ProfilerRegister;
/**
* ProfilerDrillerInterface driller profiler event interface, that records events from the profiler system.
*/
class ProfilerDrillerInterface
: public DrillerEBusTraits
{
public:
virtual ~ProfilerDrillerInterface() {}
virtual void OnRegisterSystem(AZ::u32 id, const char* name) = 0;
virtual void OnUnregisterSystem(AZ::u32 id) = 0;
virtual void OnNewRegister(const ProfilerRegister& reg, const AZStd::thread_id& threadId) = 0;
};
typedef AZ::EBus<ProfilerDrillerInterface> ProfilerDrillerBus;
}
}
#endif // AZCORE_PROFILER_DRILLER_BUS_H
#pragma once

@ -426,7 +426,6 @@ namespace AZ
void FileIOStream::Seek(OffsetType bytes, SeekMode mode)
{
AZ_PROFILE_SCOPE(AzCore, "FileIO Seek: %s", m_filename.c_str());
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
AZ_Assert(IsOpen(), "Cannot seek on a FileIOStream that is not open.");
@ -454,7 +453,6 @@ namespace AZ
SizeType FileIOStream::Read(SizeType bytes, void* oBuffer)
{
AZ_PROFILE_SCOPE(AzCore, "FileIO Read: %s", m_filename.c_str());
AZ_Assert(FileIOBase::GetInstance(), "FileIO is not initialized.");
AZ_Assert(IsOpen(), "Cannot read from a FileIOStream that is not open.");

@ -15,7 +15,7 @@ namespace AZ
{
inline namespace PlatformDefaults
{
static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformAndroid, PlatformIOS, PlatformMac, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient };
static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformLinux, PlatformAndroid, PlatformIOS, PlatformMac, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient };
const char* PlatformIdToPalFolder(AZ::PlatformId platform)
{
@ -27,6 +27,8 @@ namespace AZ
{
case AZ::PC:
return "PC";
case AZ::LINUX_ID:
return "Linux";
case AZ::ANDROID_ID:
return "Android";
case AZ::IOS:
@ -56,10 +58,14 @@ namespace AZ
const char* OSPlatformToDefaultAssetPlatform(AZStd::string_view osPlatform)
{
if (osPlatform == PlatformCodeNameWindows || osPlatform == PlatformCodeNameLinux)
if (osPlatform == PlatformCodeNameWindows)
{
return PlatformPC;
}
if (osPlatform == PlatformCodeNameLinux)
{
return PlatformLinux;
}
else if (osPlatform == PlatformCodeNameMac)
{
return PlatformMac;
@ -201,6 +207,8 @@ namespace AZ
{
case PlatformId::PC:
platformCodes.emplace_back(PlatformCodeNameWindows);
break;
case PlatformId::LINUX_ID:
platformCodes.emplace_back(PlatformCodeNameLinux);
break;
case PlatformId::ANDROID_ID:

@ -23,6 +23,7 @@ namespace AZ
inline namespace PlatformDefaults
{
constexpr char PlatformPC[] = "pc";
constexpr char PlatformLinux[] = "linux";
constexpr char PlatformAndroid[] = "android";
constexpr char PlatformIOS[] = "ios";
constexpr char PlatformMac[] = "mac";
@ -50,6 +51,7 @@ namespace AZ
AZ_ENUM_WITH_UNDERLYING_TYPE(PlatformId, int,
(Invalid, -1),
PC,
LINUX_ID,
ANDROID_ID,
IOS,
MAC_ID,
@ -63,12 +65,13 @@ namespace AZ
// Add new platforms above this
NumPlatformIds
);
constexpr int NumClientPlatforms = 7;
constexpr int NumClientPlatforms = 8;
constexpr int NumPlatforms = NumClientPlatforms + 1; // 1 "Server" platform currently
enum class PlatformFlags : AZ::u32
{
Platform_NONE = 0x00,
Platform_PC = 1 << PlatformId::PC,
Platform_LINUX = 1 << PlatformId::LINUX_ID,
Platform_ANDROID = 1 << PlatformId::ANDROID_ID,
Platform_IOS = 1 << PlatformId::IOS,
Platform_MAC = 1 << PlatformId::MAC_ID,
@ -83,7 +86,7 @@ namespace AZ
// A special platform that will always correspond to all non-server platforms, even if new ones are added
Platform_ALL_CLIENT = 1ULL << 31,
AllNamedPlatforms = Platform_PC | Platform_ANDROID | Platform_IOS | Platform_MAC | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER,
AllNamedPlatforms = Platform_PC | Platform_LINUX | Platform_ANDROID | Platform_IOS | Platform_MAC | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER,
};
AZ_DEFINE_ENUM_BITWISE_OPERATORS(PlatformFlags);

@ -209,13 +209,8 @@ namespace AZ
//! performance sensitive code.
AZ_INLINE bool CompareAnyValue(const AZStd::any& lhs, const AZStd::any& rhs)
{
bool isEqual = false;
if (lhs.type() != rhs.type())
if (lhs.type() == rhs.type())
{
return false;
}
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
@ -224,7 +219,7 @@ namespace AZ
{
if (classData->m_serializer)
{
isEqual = classData->m_serializer->CompareValueData(AZStd::any_cast<void>(&lhs), AZStd::any_cast<void>(&rhs));
return classData->m_serializer->CompareValueData(AZStd::any_cast<void>(&lhs), AZStd::any_cast<void>(&rhs));
}
else
{
@ -237,11 +232,12 @@ namespace AZ
AZ::IO::ByteContainerStream<decltype(otherData)> otherDataStream(&otherData);
AZ::Utils::SaveObjectToStream(otherDataStream, AZ::ObjectStream::ST_BINARY, AZStd::any_cast<void>(&rhs), rhs.type());
isEqual = (myData.size() == otherData.size()) && (memcmp(myData.data(), otherData.data(), myData.size()) == 0);
return (myData.size() == otherData.size()) && (memcmp(myData.data(), otherData.data(), myData.size()) == 0);
}
}
}
return isEqual;
return false;
}
}
}

@ -213,9 +213,13 @@ namespace AZ
void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context, ContinuationFlags flags)
{
bool loadAsNewInstance = (flags & ContinuationFlags::LoadAsNewInstance) == ContinuationFlags::LoadAsNewInstance;
JsonDeserializer::UseTypeDeserializer useCustom = (flags & ContinuationFlags::IgnoreTypeSerializer) == ContinuationFlags::IgnoreTypeSerializer
? JsonDeserializer::UseTypeDeserializer::No
: JsonDeserializer::UseTypeDeserializer::Yes;
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer
? JsonDeserializer::LoadToPointer(object, typeId, value, context)
: JsonDeserializer::Load(object, typeId, value, loadAsNewInstance, context);
? JsonDeserializer::LoadToPointer(object, typeId, value, useCustom, context)
: JsonDeserializer::Load(object, typeId, value, loadAsNewInstance, useCustom, context);
}
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring(
@ -224,11 +228,15 @@ namespace AZ
{
using namespace JsonSerializationResult;
JsonSerializer::UseTypeSerializer useCustom = (flags & ContinuationFlags::IgnoreTypeSerializer) == ContinuationFlags::IgnoreTypeSerializer
? JsonSerializer::UseTypeSerializer::No
: JsonSerializer::UseTypeSerializer::Yes;
if ((flags & ContinuationFlags::ReplaceDefault) == ContinuationFlags::ReplaceDefault && !context.ShouldKeepDefaults())
{
if ((flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer)
{
return JsonSerializer::StoreFromPointer(output, object, nullptr, typeId, context);
return JsonSerializer::StoreFromPointer(output, object, nullptr, typeId, useCustom, context);
}
else
{
@ -241,19 +249,19 @@ namespace AZ
{
return result;
}
return result.Combine(JsonSerializer::Store(output, object, nullptr, typeId, context));
return result.Combine(JsonSerializer::Store(output, object, nullptr, typeId, useCustom, context));
}
else
{
void* defaultObjectPtr = AZStd::any_cast<void>(&newDefaultObject);
return JsonSerializer::Store(output, object, defaultObjectPtr, typeId, context);
return JsonSerializer::Store(output, object, defaultObjectPtr, typeId, useCustom, context);
}
}
}
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer ?
JsonSerializer::StoreFromPointer(output, object, defaultObject, typeId, context) :
JsonSerializer::Store(output, object, defaultObject, typeId, context);
JsonSerializer::StoreFromPointer(output, object, defaultObject, typeId, useCustom, context) :
JsonSerializer::Store(output, object, defaultObject, typeId, useCustom, context);
}
JsonSerializationResult::ResultCode BaseJsonSerializer::LoadTypeId(Uuid& typeId, const rapidjson::Value& input,

@ -162,9 +162,10 @@ namespace AZ
None = 0, //! No extra flags.
ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance.
ReplaceDefault = 1 << 1, //! The default value provided for storing will be replaced with a newly created one.
LoadAsNewInstance = 1 << 2 //! Treats the value as if it's a newly created instance. This may trigger serializers marked with
LoadAsNewInstance = 1 << 2, //! Treats the value as if it's a newly created instance. This may trigger serializers marked with
//! OperationFlags::InitializeNewInstance. Used for instance by pointers or new instances added to
//! an array.
IgnoreTypeSerializer = 1 << 3, //! Ignore the custom/specific serializer for the TypeId
};
enum class OperationFlags

@ -38,7 +38,8 @@ namespace AZ
}
JsonSerializationResult::ResultCode JsonDeserializer::Load(
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context)
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, UseTypeDeserializer custom,
JsonDeserializerContext& context)
{
using namespace AZ::JsonSerializationResult;
@ -48,8 +49,8 @@ namespace AZ
"Target object for Json Serialization is pointing to nothing during loading.");
}
BaseJsonSerializer* serializer = context.GetRegistrationContext()->GetSerializerForType(typeId);
if (serializer)
if (BaseJsonSerializer* serializer
= (custom == UseTypeDeserializer::Yes ? context.GetRegistrationContext()->GetSerializerForType(typeId) : nullptr))
{
return DeserializerDefaultCheck(serializer, object, typeId, value, isNewInstance, context);
}
@ -70,8 +71,11 @@ namespace AZ
// type itself has not been reflected using EnumBuilder. Treat it as an enum.
return LoadEnum(object, *classData, value, context);
}
serializer = context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId());
if (serializer)
if (BaseJsonSerializer* serializer
= (custom == UseTypeDeserializer::Yes)
? context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId())
: nullptr)
{
return DeserializerDefaultCheck(serializer, object, typeId, value, isNewInstance, context);
}
@ -101,7 +105,7 @@ namespace AZ
}
JsonSerializationResult::ResultCode JsonDeserializer::LoadToPointer(void* object, const Uuid& typeId,
const rapidjson::Value& value, JsonDeserializerContext& context)
const rapidjson::Value& value, UseTypeDeserializer useCustom, JsonDeserializerContext& context)
{
using namespace JsonSerializationResult;
@ -134,7 +138,7 @@ namespace AZ
const SerializeContext::ClassData* resolvedClassData = context.GetSerializeContext()->FindClassData(resolvedTypeId);
if (resolvedClassData)
{
status = JsonDeserializer::Load(*objectPtr, resolvedTypeId, value, true, context);
status = JsonDeserializer::Load(*objectPtr, resolvedTypeId, value, true, useCustom, context);
*objectPtr = resolvedClassData->m_azRtti->Cast(*objectPtr, typeId);
@ -171,11 +175,11 @@ namespace AZ
}
AZ_Assert(classElement.m_azRtti->GetTypeId() == classElement.m_typeId,
"Type id mismatch during deserialization of a json file. (%s vs %s)");
return LoadToPointer(object, classElement.m_typeId, value, context);
return LoadToPointer(object, classElement.m_typeId, value, UseTypeDeserializer::Yes, context);
}
else
{
return Load(object, classElement.m_typeId, value, false, context);
return Load(object, classElement.m_typeId, value, false, UseTypeDeserializer::Yes, context);
}
}
@ -571,10 +575,22 @@ namespace AZ
if (loadedTypeId.m_determination == TypeIdDetermination::FailedToDetermine ||
loadedTypeId.m_determination == TypeIdDetermination::FailedDueToMultipleTypeIds)
{
AZStd::string_view message = loadedTypeId.m_determination == TypeIdDetermination::FailedDueToMultipleTypeIds ?
"Unable to resolve provided type because the same name points to multiple types." :
"Unable to resolve provided type.";
auto typeField = pointerData.FindMember(JsonSerialization::TypeIdFieldIdentifier);
if (typeField != pointerData.MemberEnd() && typeField->value.IsString())
{
const char* format = loadedTypeId.m_determination == TypeIdDetermination::FailedToDetermine ?
"Unable to resolve provided type: %.*s." :
"Unable to resolve provided type %.*s because the same name points to multiple types.";
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown,
AZStd::string::format(format, typeField->value.GetStringLength(), typeField->value.GetString()));
}
else
{
const char* message = loadedTypeId.m_determination == TypeIdDetermination::FailedToDetermine ?
"Unable to resolve provided type." :
"Unable to resolve provided type because the same name points to multiple types.";
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown, message);
}
return ResolvePointerResult::FullyProcessed;
}

@ -28,6 +28,11 @@ namespace AZ
FullyProcessed,
ContinueProcessing
};
enum class UseTypeDeserializer : bool
{
No,
Yes
};
enum class TypeIdDetermination : u8
{
ExplicitTypeId, // Type id was explicitly defined using "$type".
@ -55,10 +60,11 @@ namespace AZ
JsonDeserializer(JsonDeserializer&& rhs) = delete;
static JsonSerializationResult::ResultCode Load(
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context);
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, UseTypeDeserializer useCustom,
JsonDeserializerContext& context);
static JsonSerializationResult::ResultCode LoadToPointer(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context);
UseTypeDeserializer useCustom, JsonDeserializerContext& context);
static JsonSerializationResult::ResultCode LoadWithClassElement(void* object, const rapidjson::Value& value,
const SerializeContext::ClassElement& classElement, JsonDeserializerContext& context);

@ -245,7 +245,7 @@ namespace AZ
{
StackedString path(StackedString::Format::JsonPointer);
JsonDeserializerContext context(settings);
result = JsonDeserializer::Load(object, objectType, root, false, context);
result = JsonDeserializer::Load(object, objectType, root, false, JsonDeserializer::UseTypeDeserializer::Yes, context);
}
return result;
}
@ -322,7 +322,7 @@ namespace AZ
JsonSerializerContext context(settings, allocator);
StackedString path(StackedString::Format::ContextPath);
result = JsonSerializer::Store(output, object, defaultObject, objectType, context);
result = JsonSerializer::Store(output, object, defaultObject, objectType, JsonSerializer::UseTypeSerializer::Yes, context);
}
return result;
}

@ -20,7 +20,7 @@
namespace AZ
{
JsonSerializationResult::ResultCode JsonSerializer::Store(rapidjson::Value& output, const void* object, const void* defaultObject,
const Uuid& typeId, JsonSerializerContext& context)
const Uuid& typeId, UseTypeSerializer custom, JsonSerializerContext& context)
{
using namespace JsonSerializationResult;
@ -32,8 +32,8 @@ namespace AZ
// First check if there's a generic serializer registered for this. This makes it possible to use serializers that
// are not (directly) registered with the Serialize Context.
auto serializer = context.GetRegistrationContext()->GetSerializerForType(typeId);
if (serializer)
if (BaseJsonSerializer* serializer
= (custom == UseTypeSerializer::Yes ? context.GetRegistrationContext()->GetSerializerForType(typeId) : nullptr))
{
// Start by setting the object to be an explicit default.
output.SetObject();
@ -57,17 +57,18 @@ namespace AZ
"No factory available to create a default object for comparison.");
}
void* defaultObjectPtr = AZStd::any_cast<void>(&defaultObjectInstance);
ResultCode conversionResult = StoreWithClassData(output, object, defaultObjectPtr, *classData, StoreTypeId::No, context);
ResultCode conversionResult = StoreWithClassData(output, object, defaultObjectPtr, *classData, StoreTypeId::No
, UseTypeSerializer::Yes, context);
return ResultCode::Combine(result, conversionResult);
}
else
{
return StoreWithClassData(output, object, defaultObject, *classData, StoreTypeId::No, context);
return StoreWithClassData(output, object, defaultObject, *classData, StoreTypeId::No, custom, context);
}
}
JsonSerializationResult::ResultCode JsonSerializer::StoreFromPointer(rapidjson::Value& output, const void* object,
const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context)
const void* defaultObject, const Uuid& typeId, UseTypeSerializer custom, JsonSerializerContext& context)
{
using namespace JsonSerializationResult;
@ -85,19 +86,21 @@ namespace AZ
AZ_Assert(classData->m_azRtti->GetTypeId() == typeId, "Type id mismatch in '%s' during serialization to a json file. (%s vs %s)",
classData->m_name, classData->m_azRtti->GetTypeId().ToString<AZStd::string>().c_str(), typeId.ToString<AZStd::string>().c_str());
return StoreWithClassDataFromPointer(output, object, defaultObject, *classData, context);
return StoreWithClassDataFromPointer(output, object, defaultObject, *classData, custom, context);
}
JsonSerializationResult::ResultCode JsonSerializer::StoreWithClassData(rapidjson::Value& node, const void* object,
const void* defaultObject, const SerializeContext::ClassData& classData, StoreTypeId storeTypeId,
JsonSerializerContext& context)
UseTypeSerializer custom, JsonSerializerContext& context)
{
using namespace JsonSerializationResult;
// Start by setting the object to be an explicit default.
node.SetObject();
auto serializer = context.GetRegistrationContext()->GetSerializerForType(classData.m_typeId);
auto serializer = custom == UseTypeSerializer::Yes
? context.GetRegistrationContext()->GetSerializerForType(classData.m_typeId) : nullptr;
if (serializer)
{
ResultCode result = serializer->Store(node, object, defaultObject, classData.m_typeId, context);
@ -153,7 +156,7 @@ namespace AZ
}
JsonSerializationResult::ResultCode JsonSerializer::StoreWithClassDataFromPointer(rapidjson::Value& output, const void* object,
const void* defaultObject, const SerializeContext::ClassData& classData, JsonSerializerContext& context)
const void* defaultObject, const SerializeContext::ClassData& classData, UseTypeSerializer custom, JsonSerializerContext& context)
{
using namespace JsonSerializationResult;
@ -176,7 +179,7 @@ namespace AZ
}
else
{
return StoreWithClassData(output, object, defaultObject, *resolvedClassData, storeTypeId, context);
return StoreWithClassData(output, object, defaultObject, *resolvedClassData, storeTypeId, custom, context);
}
}
@ -221,8 +224,8 @@ namespace AZ
{
rapidjson::Value value;
ResultCode result = classElement.m_flags & SerializeContext::ClassElement::FLG_POINTER ?
StoreWithClassDataFromPointer(value, object, defaultObject, *elementClassData, context):
StoreWithClassData(value, object, defaultObject, *elementClassData, StoreTypeId::No, context);
StoreWithClassDataFromPointer(value, object, defaultObject, *elementClassData, UseTypeSerializer::Yes, context):
StoreWithClassData(value, object, defaultObject, *elementClassData, StoreTypeId::No, UseTypeSerializer::Yes, context);
if (result.GetProcessing() != Processing::Halted)
{
if (parentNode.IsObject())

@ -26,6 +26,11 @@ namespace AZ
No,
Yes
};
enum class UseTypeSerializer : bool
{
No,
Yes
};
enum class ResolvePointerResult
{
FullyProcessed,
@ -41,16 +46,18 @@ namespace AZ
JsonSerializer(JsonSerializer&& rhs) = delete;
static JsonSerializationResult::ResultCode Store(rapidjson::Value& output, const void* object, const void* defaultObject,
const Uuid& typeId, JsonSerializerContext& context);
const Uuid& typeId, UseTypeSerializer useCustom, JsonSerializerContext& context);
static JsonSerializationResult::ResultCode StoreFromPointer(rapidjson::Value& output, const void* object, const void* defaultObject,
const Uuid& typeId, JsonSerializerContext& context);
const Uuid& typeId, UseTypeSerializer custom, JsonSerializerContext& context);
static JsonSerializationResult::ResultCode StoreWithClassData(rapidjson::Value& node, const void* object, const void* defaultObject,
const SerializeContext::ClassData& classData, StoreTypeId storeTypeId, JsonSerializerContext& context);
const SerializeContext::ClassData& classData, StoreTypeId storeTypeId, UseTypeSerializer custom,
JsonSerializerContext& context);
static JsonSerializationResult::ResultCode StoreWithClassDataFromPointer(rapidjson::Value& output, const void* object,
const void* defaultObject, const SerializeContext::ClassData& classData, JsonSerializerContext& context);
const void* defaultObject, const SerializeContext::ClassData& classData, UseTypeSerializer custom,
JsonSerializerContext& context);
static JsonSerializationResult::ResultCode StoreWithClassElement(rapidjson::Value& parentNode, const void* object,
const void* defaultObject, const SerializeContext::ClassElement& classElement, JsonSerializerContext& context);

@ -6,7 +6,6 @@
*
*/
#include <AtomCore/Serialization/Json/JsonUtils.h>
#include <AzCore/Utils/Utils.h>
#include <AzCore/base.h>
#include <AzCore/Component/ComponentApplicationBus.h>
@ -23,6 +22,8 @@
#include <AzCore/Serialization/Utils.h>
#include <AzCore/Utils/Utils.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
namespace AZ
{
namespace JsonSerializationUtils
@ -332,6 +333,11 @@ namespace AZ
// validate class name
auto classData = loadSettings.m_serializeContext->FindClassData(classId);
if (!classData)
{
return AZ::Failure(AZStd::string::format("Try to load class from Id %s", classId.ToString<AZStd::string>().c_str()));
}
if (azstricmp(classData->m_name, className) != 0)
{
return AZ::Failure(AZStd::string::format("Try to load class %s from class %s data", classData->m_name, className));
@ -343,10 +349,10 @@ namespace AZ
{
return AZ::Failure(deserializeErrors);
}
return AZ::Success();
}
AZ::Outcome<AZStd::any, AZStd::string> LoadAnyObjectFromStream(IO::GenericStream& stream, const JsonDeserializerSettings* settings)
{
JsonDeserializerSettings loadSettings;

@ -44,7 +44,6 @@ namespace UnitTest
MOCK_CONST_METHOD0(GetAppRoot, const char* ());
MOCK_CONST_METHOD0(GetEngineRoot, const char* ());
MOCK_CONST_METHOD0(GetExecutableFolder, const char* ());
MOCK_METHOD0(GetDrillerManager, AZ::Debug::DrillerManager* ());
MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&));
};
} // namespace UnitTest

@ -11,6 +11,7 @@
#include <AzCore/base.h>
#include <AzCore/UnitTest/UnitTest.h>
#include <AzCore/Debug/BudgetTracker.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Driller/Driller.h>
#include <AzCore/Memory/MemoryDriller.h>

@ -93,20 +93,18 @@ set(FILES
Debug/AssetTracking.h
Debug/AssetTrackingTypesImpl.h
Debug/AssetTrackingTypes.h
Debug/Budget.h
Debug/Budget.cpp
Debug/BudgetTracker.h
Debug/BudgetTracker.cpp
Debug/LocalFileEventLogger.h
Debug/LocalFileEventLogger.cpp
Debug/FrameProfiler.h
Debug/FrameProfilerBus.h
Debug/FrameProfilerComponent.cpp
Debug/FrameProfilerComponent.h
Debug/IEventLogger.h
Debug/MemoryProfiler.h
Debug/Profiler.cpp
Debug/Profiler.inl
Debug/Profiler.h
Debug/ProfilerBus.h
Debug/ProfilerDriller.cpp
Debug/ProfilerDriller.h
Debug/ProfilerDrillerBus.h
Debug/StackTracer.h
Debug/EventTrace.h
Debug/EventTrace.cpp
@ -531,6 +529,8 @@ set(FILES
Serialization/Json/JsonStringConversionUtils.h
Serialization/Json/JsonSystemComponent.h
Serialization/Json/JsonSystemComponent.cpp
Serialization/Json/JsonUtils.h
Serialization/Json/JsonUtils.cpp
Serialization/Json/MapSerializer.h
Serialization/Json/MapSerializer.cpp
Serialization/Json/RegistrationContext.h
@ -570,8 +570,6 @@ set(FILES
Statistics/StatisticalProfilerProxySystemComponent.cpp
Statistics/StatisticalProfilerProxySystemComponent.h
Statistics/StatisticsManager.h
Statistics/TimeDataStatisticsManager.cpp
Statistics/TimeDataStatisticsManager.h
StringFunc/StringFunc.cpp
StringFunc/StringFunc.h
UserSettings/UserSettings.cpp

@ -22,6 +22,8 @@ namespace AZStd
using std::exp2;
using std::floor;
using std::fmod;
using std::llround;
using std::lround;
using std::pow;
using std::round;
using std::sin;

@ -62,7 +62,6 @@ namespace UnitTest
const char* GetAppRoot() const override { return nullptr; }
const char* GetEngineRoot() const override { return nullptr; }
const char* GetExecutableFolder() const override { return nullptr; }
AZ::Debug::DrillerManager* GetDrillerManager() override { return nullptr; }
void EnumerateEntities(const EntityCallback& /*callback*/) override {}
void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override {}
////

@ -22,8 +22,6 @@
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Debug/FrameProfilerBus.h>
#include <AzCore/Debug/FrameProfilerComponent.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/UnitTest/TestTypes.h>

@ -1,698 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <time.h>
#include <AzCore/IO/FileIOEventBus.h>
#include <AzCore/Driller/Driller.h>
#include <AzCore/Driller/DrillerBus.h>
#include <AzCore/Driller/DrillerRootHandler.h>
#include <AzCore/Driller/DefaultStringPool.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Memory/MemoryComponent.h>
#include <AzCore/IO/Streamer/StreamerComponent.h>
#include <AzCore/UnitTest/TestTypes.h>
//#define AZ_CORE_DRILLER_COMPARE_TEST
#if defined(AZ_CORE_DRILLER_COMPARE_TEST)
# include <AzCore/IO/GenericStreams.h>
# include <AzCore/Serialization/ObjectStream.h>
# include <AzCore/Serialization/SerializeContext.h>
#endif
#include <AZTestShared/Utils/Utils.h>
using namespace AZ;
using namespace AZ::Debug;
namespace UnitTest
{
/**
* MyDriller event bus...
*/
class MyDrillerInterface
: public AZ::Debug::DrillerEBusTraits
{
public:
virtual ~MyDrillerInterface() {}
// define one event X
virtual void OnEventX(int data) = 0;
// define a string event
virtual void OnStringEvent() = 0;
};
class MyDrillerCommandInterface
: public AZ::Debug::DrillerEBusTraits
{
public:
virtual ~MyDrillerCommandInterface() {}
virtual class MyDrilledObject* RequestDrilledObject() = 0;
};
typedef AZ::EBus<MyDrillerInterface> MyDrillerBus;
typedef AZ::EBus<MyDrillerCommandInterface> MyDrillerCommandBus;
class MyDrilledObject
: public MyDrillerCommandBus::Handler
{
int i;
public:
MyDrilledObject()
: i(0)
{
BusConnect();
}
~MyDrilledObject() override
{
BusDisconnect();
}
//////////////////////////////////////////////////////////////////////////
// MyDrillerCommandBus
MyDrilledObject* RequestDrilledObject() override
{
return this;
}
//////////////////////////////////////////////////////////////////////////
void OnEventX()
{
EBUS_EVENT(MyDrillerBus, OnEventX, i);
++i;
}
void OnStringEvent()
{
EBUS_DBG_EVENT(MyDrillerBus, OnStringEvent);
}
};
/**
* My driller implements the driller interface and an handles the MyDrillerBus events...
*/
class MyDriller
: public Driller
, public MyDrillerBus::Handler
{
bool m_isDetailedCapture;
class MyDrilledObject* drilledObject;
typedef vector<Param>::type ParamArrayType;
ParamArrayType m_params;
public:
AZ_CLASS_ALLOCATOR(MyDriller, OSAllocator, 0);
const char* GroupName() const override { return "TestDrillers"; }
const char* GetName() const override { return "MyTestDriller"; }
const char* GetDescription() const override { return "MyTestDriller description...."; }
int GetNumParams() const override { return static_cast<int>(m_params.size()); }
const Param* GetParam(int index) const override { return &m_params[index]; }
MyDriller()
: m_isDetailedCapture(false)
, drilledObject(NULL)
{
Param isDetailed;
isDetailed.desc = "IsDetailedDrill";
isDetailed.name = AZ_CRC("IsDetailedDrill", 0x2155cef2);
isDetailed.type = Param::PT_BOOL;
isDetailed.value = 0;
m_params.push_back(isDetailed);
}
void Start(const Param* params = NULL, int numParams = 0) override
{
m_isDetailedCapture = m_params[0].value != 0;
if (params)
{
for (int i = 0; i < numParams; i++)
{
if (params[i].name == m_params[0].name)
{
m_isDetailedCapture = params[i].value != 0;
}
}
}
EBUS_EVENT_RESULT(drilledObject, MyDrillerCommandBus, RequestDrilledObject);
AZ_TEST_ASSERT(drilledObject != NULL); /// Make sure we have our object by the time we started the driller
m_output->BeginTag(AZ_CRC("MyDriller", 0xc3b7dceb));
m_output->Write(AZ_CRC("OnStart", 0x8b372fca), m_isDetailedCapture);
// write drilled object initial state
m_output->EndTag(AZ_CRC("MyDriller", 0xc3b7dceb));
BusConnect();
}
void Stop() override
{
drilledObject = NULL;
m_output->BeginTag(AZ_CRC("MyDriller", 0xc3b7dceb));
m_output->Write(AZ_CRC("OnStop", 0xf6701caa), m_isDetailedCapture);
m_output->EndTag(AZ_CRC("MyDriller", 0xc3b7dceb));
BusDisconnect();
}
void OnEventX(int data) override
{
void* ptr = AZ_INVALID_POINTER;
float f = 3.2f;
m_output->BeginTag(AZ_CRC("MyDriller", 0xc3b7dceb));
m_output->Write(AZ_CRC("EventX", 0xc4558ec2), data);
m_output->Write(AZ_CRC("Pointer", 0x320468a8), ptr);
m_output->Write(AZ_CRC("Float", 0xc9a55e95), f);
m_output->EndTag(AZ_CRC("MyDriller", 0xc3b7dceb));
}
void OnStringEvent() override
{
m_output->BeginTag(AZ_CRC("MyDriller", 0xc3b7dceb));
m_output->BeginTag(AZ_CRC("StringEvent", 0xd1e005df));
m_output->Write(AZ_CRC("StringOne", 0x56efb231), "This is copied string");
m_output->Write(AZ_CRC("StringTwo", 0x3d49bea6), "This is referenced string", false); // don't copy the string if we use string pool, this will be faster as we don't delete the string
m_output->EndTag(AZ_CRC("StringEvent", 0xd1e005df));
m_output->EndTag(AZ_CRC("MyDriller", 0xc3b7dceb));
}
};
/**
*
*/
class FileStreamDrillerTest
: public AllocatorsFixture
{
DrillerManager* m_drillerManager = nullptr;
MyDriller* m_driller = nullptr;
public:
void SetUp() override
{
AllocatorsFixture::SetUp();
m_drillerManager = DrillerManager::Create();
m_driller = aznew MyDriller;
// Register driller descriptor
m_drillerManager->Register(m_driller);
// check that our driller descriptor is registered
AZ_TEST_ASSERT(m_drillerManager->GetNumDrillers() == 1);
}
void TearDown() override
{
// remove our driller descriptor
m_drillerManager->Unregister(m_driller);
AZ_TEST_ASSERT(m_drillerManager->GetNumDrillers() == 0);
DrillerManager::Destroy(m_drillerManager);
AllocatorsFixture::TearDown();
}
/**
* My Driller data handler.
*/
class MyDrillerHandler
: public DrillerHandlerParser
{
public:
static const bool s_isWarnOnMissingDrillers = true;
int m_lastData;
MyDrillerHandler()
: m_lastData(-1) {}
// From the template query
DrillerHandlerParser* FindDrillerHandler(u32 drillerId)
{
if (drillerId == AZ_CRC("MyDriller", 0xc3b7dceb))
{
return this;
}
return NULL;
}
DrillerHandlerParser* OnEnterTag(u32 tagName) override
{
(void)tagName;
return NULL;
}
void OnData(const DrillerSAXParser::Data& dataNode) override
{
if (dataNode.m_name == AZ_CRC("OnStart", 0x8b372fca) || dataNode.m_name == AZ_CRC("OnStop", 0xf6701caa))
{
bool isDetailedCapture;
dataNode.Read(isDetailedCapture);
AZ_TEST_ASSERT(isDetailedCapture == true);
}
else if (dataNode.m_name == AZ_CRC("EventX", 0xc4558ec2))
{
int data;
dataNode.Read(data);
AZ_TEST_ASSERT(data > m_lastData);
m_lastData = data;
}
else if (dataNode.m_name == AZ_CRC("Pointer", 0x320468a8))
{
AZ::u64 pointer = 0; //< read pointers in u64 to cover all platforms
dataNode.Read(pointer);
AZ_TEST_ASSERT(pointer == 0x0badf00dul);
}
else if (dataNode.m_name == AZ_CRC("Float", 0xc9a55e95))
{
float f;
dataNode.Read(f);
AZ_TEST_ASSERT(f == 3.2f);
}
}
};
//////////////////////////////////////////////////////////////////////////
void run()
{
// get our driller descriptor
Driller* driller = m_drillerManager->GetDriller(0);
AZ_TEST_ASSERT(driller != NULL);
AZ_TEST_ASSERT(strcmp(driller->GetName(), "MyTestDriller") == 0);
AZ_TEST_ASSERT(driller->GetNumParams() == 1);
// read the default params and make a copy...
Driller::Param param = *driller->GetParam(0);
AZ_TEST_ASSERT(strcmp(param.desc, "IsDetailedDrill") == 0);
AZ_TEST_ASSERT(param.name == AZ_CRC("IsDetailedDrill", 0x2155cef2));
AZ_TEST_ASSERT(param.type == Driller::Param::PT_BOOL);
// tweak the default params by enabling detailed drilling
param.value = 1;
// create a list of driller we what to drill
DrillerManager::DrillerListType dillersToDrill;
DrillerManager::DrillerInfo di;
di.id = driller->GetId(); // set driller id
di.params.push_back(param); // set driller custom params
dillersToDrill.push_back(di);
// open a driller output file stream
// open a driller output file stream
AZStd::string testFileName = GetTestFolderPath() + "drilltest.dat";
DrillerOutputFileStream drillerOutputStream;
drillerOutputStream.Open(testFileName.c_str(), IO::SystemFile::SF_OPEN_CREATE | IO::SystemFile::SF_OPEN_WRITE_ONLY);
//////////////////////////////////////////////////////////////////////////
// Drill an object
MyDrilledObject myDrilledObject;
clock_t st = clock();
// start a driller session with the file stream and the list of drillers
DrillerSession* drillerSession = m_drillerManager->Start(drillerOutputStream, dillersToDrill);
// update for N frames
for (int i = 0; i < AZ_TRAIT_UNIT_TEST_DILLER_TRIGGER_EVENT_COUNT; ++i)
{
// trigger event X that we want to drill...
myDrilledObject.OnEventX();
m_drillerManager->FrameUpdate();
}
// stop the drillers
m_drillerManager->Stop(drillerSession);
// Stop writing and flush all data
drillerOutputStream.Close();
AZ_Printf("Driller", "Compression time %.09f seconds\n", (double)(clock() - st) / CLOCKS_PER_SEC);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// try to load the drill data
DrillerInputFileStream drillerInputStream;
drillerInputStream.Open(testFileName.c_str(), IO::SystemFile::SF_OPEN_READ_ONLY);
DrillerDOMParser dp;
AZ_TEST_ASSERT(dp.CanParse() == true);
dp.ProcessStream(drillerInputStream);
AZ_TEST_ASSERT(dp.CanParse() == true);
drillerInputStream.Close();
u32 startDataId = AZ_CRC("StartData", 0xecf3f53f);
u32 frameId = AZ_CRC("Frame", 0xb5f83ccd);
//////////////////////////////////////////////////////////////////////////
// read all data
const DrillerDOMParser::Node* root = dp.GetRootNode();
int lastFrame = -1;
int lastData = -1;
for (DrillerDOMParser::Node::NodeListType::const_iterator iter = root->m_tags.begin(); iter != root->m_tags.end(); ++iter)
{
const DrillerDOMParser::Node* node = &*iter;
u32 name = node->m_name;
AZ_TEST_ASSERT(name == startDataId || name == frameId);
if (name == startDataId)
{
unsigned int currentPlatform;
node->GetDataRequired(AZ_CRC("Platform", 0x3952d0cb))->Read(currentPlatform);
AZ_TEST_ASSERT(currentPlatform == static_cast<int>(AZ::g_currentPlatform));
const DrillerDOMParser::Node* drillerNode = node->GetTag(AZ_CRC("Driller", 0xa6e1fb73));
AZ_TEST_ASSERT(drillerNode != NULL);
AZ::u32 drillerName;
drillerNode->GetDataRequired(AZ_CRC("Name", 0x5e237e06))->Read(drillerName);
AZ_TEST_ASSERT(drillerName == m_driller->GetId());
const DrillerDOMParser::Node* paramNode = drillerNode->GetTag(AZ_CRC("Param", 0xa4fa7c89));
AZ_TEST_ASSERT(paramNode != NULL);
u32 paramName;
char paramDesc[128];
int paramType;
int paramValue;
paramNode->GetDataRequired(AZ_CRC("Name", 0x5e237e06))->Read(paramName);
AZ_TEST_ASSERT(paramName == param.name);
paramNode->GetDataRequired(AZ_CRC("Description", 0x6de44026))->Read(paramDesc, AZ_ARRAY_SIZE(paramDesc));
AZ_TEST_ASSERT(strcmp(paramDesc, param.desc) == 0);
paramNode->GetDataRequired(AZ_CRC("Type", 0x8cde5729))->Read(paramType);
AZ_TEST_ASSERT(paramType == param.type);
paramNode->GetDataRequired(AZ_CRC("Value", 0x1d775834))->Read(paramValue);
AZ_TEST_ASSERT(paramValue == param.value);
}
else
{
int curFrame;
node->GetDataRequired(AZ_CRC("FrameNum", 0x85a1a919))->Read(curFrame);
AZ_TEST_ASSERT(curFrame > lastFrame); // check order
lastFrame = curFrame;
const DrillerDOMParser::Node* myDrillerNode = node->GetTag(AZ_CRC("MyDriller", 0xc3b7dceb));
AZ_TEST_ASSERT(myDrillerNode != NULL);
const DrillerDOMParser::Data* dataEntry;
dataEntry = myDrillerNode->GetData(AZ_CRC("EventX", 0xc4558ec2));
if (dataEntry)
{
int data;
dataEntry->Read(data);
AZ_TEST_ASSERT(data > lastData);
lastData = data;
dataEntry = myDrillerNode->GetData(AZ_CRC("Pointer", 0x320468a8));
AZ_TEST_ASSERT(dataEntry);
unsigned int ptr;
dataEntry->Read(ptr);
AZ_TEST_ASSERT(static_cast<size_t>(ptr) == reinterpret_cast<size_t>(AZ_INVALID_POINTER));
float f;
dataEntry = myDrillerNode->GetData(AZ_CRC("Float", 0xc9a55e95));
AZ_TEST_ASSERT(dataEntry);
dataEntry->Read(f);
AZ_TEST_ASSERT(f == 3.2f);
}
else
{
bool isDetailedCapture;
dataEntry = myDrillerNode->GetData(AZ_CRC("OnStart", 0x8b372fca));
if (dataEntry)
{
dataEntry->Read(isDetailedCapture);
}
else
{
myDrillerNode->GetDataRequired(AZ_CRC("OnStop", 0xf6701caa))->Read(isDetailedCapture);
}
AZ_TEST_ASSERT(isDetailedCapture == true);
}
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Read that with Tag Handlers
drillerInputStream.Open(testFileName.c_str(), IO::SystemFile::SF_OPEN_READ_ONLY);
DrillerRootHandler<MyDrillerHandler> rootHandler;
DrillerSAXParserHandler dhp(&rootHandler);
dhp.ProcessStream(drillerInputStream);
// Verify that Default templates forked fine...
AZ_TEST_ASSERT(rootHandler.m_drillerSessionInfo.m_platform == static_cast<uint32_t>(AZ::g_currentPlatform));
AZ_TEST_ASSERT(rootHandler.m_drillerSessionInfo.m_drillers.size() == 1);
{
const DrillerManager::DrillerInfo& dinfo = rootHandler.m_drillerSessionInfo.m_drillers.front();
AZ_TEST_ASSERT(dinfo.id == AZ_CRC("MyTestDriller", 0x5cc4edf5));
AZ_TEST_ASSERT(dinfo.params.size() == 1);
AZ_TEST_ASSERT(strcmp(param.desc, "IsDetailedDrill") == 0);
AZ_TEST_ASSERT(param.name == AZ_CRC("IsDetailedDrill", 0x2155cef2));
AZ_TEST_ASSERT(param.type == Driller::Param::PT_BOOL);
// tweak the default params by enabling detailed drilling
param.value = 1;
AZ_TEST_ASSERT(dinfo.params[0].name == AZ_CRC("IsDetailedDrill", 0x2155cef2));
AZ_TEST_ASSERT(dinfo.params[0].desc == NULL); // ignored for now
AZ_TEST_ASSERT(dinfo.params[0].type == Driller::Param::PT_BOOL);
AZ_TEST_ASSERT(dinfo.params[0].value == 1);
}
drillerInputStream.Close();
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////
}
};
TEST_F(FileStreamDrillerTest, Test)
{
run();
}
/**
*
*/
class StringPoolDrillerTest
: public AllocatorsFixture
{
DrillerManager* m_drillerManager = nullptr;
MyDriller* m_driller = nullptr;
public:
void SetUp() override
{
AllocatorsFixture::SetUp();
m_drillerManager = DrillerManager::Create();
m_driller = aznew MyDriller;
// Register driller descriptor
m_drillerManager->Register(m_driller);
// check that our driller descriptor is registered
AZ_TEST_ASSERT(m_drillerManager->GetNumDrillers() == 1);
}
void TearDown() override
{
// remove our driller descriptor
m_drillerManager->Unregister(m_driller);
AZ_TEST_ASSERT(m_drillerManager->GetNumDrillers() == 0);
DrillerManager::Destroy(m_drillerManager);
AllocatorsFixture::TearDown();
}
/**
* My Driller data handler.
*/
class MyDrillerHandler
: public DrillerHandlerParser
{
public:
static const bool s_isWarnOnMissingDrillers = true;
MyDrillerHandler() {}
// From the template query
DrillerHandlerParser* FindDrillerHandler(u32 drillerId)
{
if (drillerId == AZ_CRC("MyDriller", 0xc3b7dceb))
{
return this;
}
return NULL;
}
DrillerHandlerParser* OnEnterTag(u32 tagName) override
{
if (tagName == AZ_CRC("StringEvent", 0xd1e005df))
{
return this;
}
return NULL;
}
void OnData(const DrillerSAXParser::Data& dataNode) override
{
if (dataNode.m_name == AZ_CRC("OnStart", 0x8b372fca) || dataNode.m_name == AZ_CRC("OnStop", 0xf6701caa))
{
bool isDetailedCapture;
dataNode.Read(isDetailedCapture);
AZ_TEST_ASSERT(isDetailedCapture == true);
}
else if (dataNode.m_name == AZ_CRC("StringOne", 0x56efb231))
{
// read string as a copy
char stringCopy[256];
dataNode.Read(stringCopy, AZ_ARRAY_SIZE(stringCopy));
AZ_TEST_ASSERT(strcmp(stringCopy, "This is copied string") == 0);
}
else if (dataNode.m_name == AZ_CRC("StringTwo", 0x3d49bea6))
{
// read string as reference if possible, otherwise read it as a copy
const char* stringRef = dataNode.ReadPooledString();
AZ_TEST_ASSERT(strcmp(stringRef, "This is referenced string") == 0);
}
}
};
void run()
{
// get our driller descriptor
Driller* driller = m_drillerManager->GetDriller(0);
Driller::Param param = *driller->GetParam(0);
param.value = 1;
// create a list of driller we what to drill
DrillerManager::DrillerListType dillersToDrill;
DrillerManager::DrillerInfo di;
di.id = driller->GetId(); // set driller id
di.params.push_back(param); // set driller custom params
dillersToDrill.push_back(di);
MyDrilledObject myDrilledObject;
// open a driller output file stream
AZStd::string testFileName = GetTestFolderPath() + "stringpooldrilltest.dat";
DrillerOutputFileStream drillerOutputStream;
DrillerInputFileStream drillerInputStream;
DrillerDefaultStringPool stringPool;
DrillerSession* drillerSession;
DrillerRootHandler<MyDrillerHandler> rootHandler;
DrillerSAXParserHandler dhp(&rootHandler);
//////////////////////////////////////////////////////////////////////////
// Drill an object without string pools
drillerOutputStream.Open(testFileName.c_str(), IO::SystemFile::SF_OPEN_CREATE | IO::SystemFile::SF_OPEN_WRITE_ONLY);
// start a driller session with the file stream and the list of drillers
drillerSession = m_drillerManager->Start(drillerOutputStream, dillersToDrill);
// update for N frames
for (int i = 0; i < AZ_TRAIT_UNIT_TEST_DILLER_TRIGGER_EVENT_COUNT; ++i)
{
myDrilledObject.OnStringEvent();
m_drillerManager->FrameUpdate();
}
// stop the drillers
m_drillerManager->Stop(drillerSession);
// Stop writing and flush all data
drillerOutputStream.Close();
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Read all data that was written without string pool, in a stream that uses one.
drillerInputStream.Open(testFileName.c_str(), IO::SystemFile::SF_OPEN_READ_ONLY);
drillerInputStream.SetStringPool(&stringPool);
dhp.ProcessStream(drillerInputStream);
drillerInputStream.Close();
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Drill an object without string pools
drillerOutputStream.Open(testFileName.c_str(), IO::SystemFile::SF_OPEN_CREATE | IO::SystemFile::SF_OPEN_WRITE_ONLY);
stringPool.Reset();
drillerOutputStream.SetStringPool(&stringPool); // set the string pool on save
// start a driller session with the file stream and the list of drillers
drillerSession = m_drillerManager->Start(drillerOutputStream, dillersToDrill);
// update for N frames
for (int i = 0; i < AZ_TRAIT_UNIT_TEST_DILLER_TRIGGER_EVENT_COUNT; ++i)
{
myDrilledObject.OnStringEvent();
m_drillerManager->FrameUpdate();
}
// stop the drillers
m_drillerManager->Stop(drillerSession);
// Stop writing and flush all data
drillerOutputStream.Close();
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Read all data that was written without string pool, in a stream that uses one.
stringPool.Reset();
drillerInputStream.Open(testFileName.c_str(), IO::SystemFile::SF_OPEN_READ_ONLY);
drillerInputStream.SetStringPool(&stringPool);
dhp.ProcessStream(drillerInputStream);
drillerInputStream.Close();
//////////////////////////////////////////////////////////////////////////
}
};
TEST_F(StringPoolDrillerTest, Test)
{
run();
}
/**
*
*/
class DrillFileStreamCheck
{
public:
void run()
{
// open and read drilled file
}
};
/**
* Driller application test
*/
TEST(DrillerApplication, Test)
{
ComponentApplication app;
//////////////////////////////////////////////////////////////////////////
// Create application environment code driven
ComponentApplication::Descriptor appDesc;
appDesc.m_memoryBlocksByteSize = 10 * 1024 * 1024;
appDesc.m_enableDrilling = true;
Entity* systemEntity = app.Create(appDesc);
systemEntity->CreateComponent<MemoryComponent>();
systemEntity->CreateComponent<StreamerComponent>(); // note that this component is what registers the streamer driller
systemEntity->Init();
systemEntity->Activate();
{
// open a driller output file stream
char testFileName[AZ_MAX_PATH_LEN];
MakePathFromTestFolder(testFileName, AZ_MAX_PATH_LEN, "drillapptest.dat");
DrillerOutputFileStream fs;
fs.Open(testFileName, IO::SystemFile::SF_OPEN_CREATE | IO::SystemFile::SF_OPEN_WRITE_ONLY);
// create a list of driller we what to drill
DrillerManager::DrillerListType drillersToDrill;
DrillerManager::DrillerInfo di;
di.id = AZ_CRC("TraceMessagesDriller", 0xa61d1b00);
drillersToDrill.push_back(di);
di.id = AZ_CRC("MemoryDriller", 0x1b31269d);
drillersToDrill.push_back(di);
ASSERT_NE(nullptr, app.GetDrillerManager());
DrillerSession* drillerSession = app.GetDrillerManager()->Start(fs, drillersToDrill);
ASSERT_NE(nullptr, drillerSession);
const int numOfFrames = 10000;
void* memory = NULL;
for (int i = 0; i < numOfFrames; ++i)
{
memory = azmalloc(rand() % 2048 + 1);
azfree(memory);
app.Tick();
}
app.GetDrillerManager()->Stop(drillerSession); // stop session manually
fs.Close(); // close the file with driller info
}
app.Destroy();
//////////////////////////////////////////////////////////////////////////
}
}

@ -1242,7 +1242,6 @@ namespace UnitTest
const char* GetAppRoot() const override { return nullptr; }
const char* GetEngineRoot() const override { return nullptr; }
const char* GetExecutableFolder() const override { return nullptr; }
Debug::DrillerManager* GetDrillerManager() override { return nullptr; }
void EnumerateEntities(const EntityCallback& /*callback*/) override {}
void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override {}
//////////////////////////////////////////////////////////////////////////

@ -13,7 +13,7 @@
#include <AzTest/AzTest.h>
#include <AtomCore/Serialization/Json/JsonUtils.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
namespace UnitTest
{

@ -1,208 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UnitTest/UnitTest.h>
#include <AzTest/AzTest.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/Statistics/TimeDataStatisticsManager.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Component/EntityUtils.h>
#include <AzCore/Debug/FrameProfilerBus.h>
#include <AzCore/Debug/FrameProfilerComponent.h>
using namespace AZ;
using namespace Debug;
namespace UnitTest
{
/**
* Validate functionality of the convenience class TimeDataStatisticsManager.
* It is a specialized version of RunningStatisticsManager that works with Timer type
* of registers that can be captured with the FrameProfilerBus::OnFrameProfilerData()
*/
class TimeDataStatisticsManagerTest
: public AllocatorsFixture
, public FrameProfilerBus::Handler
{
static constexpr const char* PARENT_TIMER_STAT = "ParentStat";
static constexpr const char* CHILD_TIMER_STAT0 = "ChildStat0";
static constexpr const char* CHILD_TIMER_STAT1 = "ChildStat1";
public:
TimeDataStatisticsManagerTest()
: AllocatorsFixture()
{
}
void SetUp() override
{
AllocatorsFixture::SetUp();
m_statsManager = AZStd::make_unique<Statistics::TimeDataStatisticsManager>();
}
void TearDown() override
{
m_statsManager = nullptr;
AllocatorsFixture::TearDown();
}
//////////////////////////////////////////////////////////////////////////
// FrameProfilerBus
virtual void OnFrameProfilerData(const FrameProfiler::ThreadDataArray& data)
{
for (size_t iThread = 0; iThread < data.size(); ++iThread)
{
const FrameProfiler::ThreadData& td = data[iThread];
FrameProfiler::ThreadData::RegistersMap::const_iterator regIt = td.m_registers.begin();
for (; regIt != td.m_registers.end(); ++regIt)
{
const FrameProfiler::RegisterData& rd = regIt->second;
u32 unitTestCrc = AZ_CRC("UnitTest", 0x8089cea8);
if (unitTestCrc != rd.m_systemId)
{
continue; //Not for us.
}
ASSERT_EQ(ProfilerRegister::PRT_TIME, rd.m_type);
const FrameProfiler::FrameData& fd = rd.m_frames.back();
m_statsManager->PushTimeDataSample(rd.m_name, fd.m_timeData);
}
}
}
//////////////////////////////////////////////////////////////////////////
int ChildFunction0(int numIterations, int sleepTimeMilliseconds)
{
AZ_PROFILE_SCOPE(UnitTest, CHILD_TIMER_STAT0);
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(sleepTimeMilliseconds));
int result = 5;
for (int i = 0; i < numIterations; ++i)
{
result += i % 3;
}
return result;
}
int ChildFunction1(int numIterations, int sleepTimeMilliseconds)
{
AZ_PROFILE_SCOPE(UnitTest, CHILD_TIMER_STAT1);
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(sleepTimeMilliseconds));
int result = 5;
for (int i = 0; i < numIterations; ++i)
{
result += i % 3;
}
return result;
}
int ParentFunction(int numIterations, int sleepTimeMilliseconds)
{
AZ_PROFILE_SCOPE(UnitTest, PARENT_TIMER_STAT);
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(sleepTimeMilliseconds));
int result = 0;
result += ChildFunction0(numIterations, sleepTimeMilliseconds);
result += ChildFunction1(numIterations, sleepTimeMilliseconds);
return result;
}
void run()
{
Debug::FrameProfilerBus::Handler::BusConnect();
ComponentApplication app;
ComponentApplication::Descriptor desc;
desc.m_useExistingAllocator = true;
desc.m_enableDrilling = false; // we already created a memory driller for the test (AllocatorsFixture)
ComponentApplication::StartupParameters startupParams;
startupParams.m_allocator = &AllocatorInstance<SystemAllocator>::Get();
Entity* systemEntity = app.Create(desc, startupParams);
systemEntity->CreateComponent<FrameProfilerComponent>();
systemEntity->Init();
systemEntity->Activate(); // start frame component
const int sleepTimeAllFuncsMillis = 1;
const int numIterations = 10;
for (int iterationCounter = 0; iterationCounter < numIterations; ++iterationCounter)
{
ParentFunction(numIterations, sleepTimeAllFuncsMillis);
//Collect all samples.
app.Tick();
}
//Verify we have three running stats.
{
AZStd::vector<Statistics::NamedRunningStatistic*> allStats;
m_statsManager->GetAllStatistics(allStats);
EXPECT_EQ(allStats.size(), 3);
}
AZStd::string parentStatName(PARENT_TIMER_STAT);
AZStd::string child0StatName(CHILD_TIMER_STAT0);
AZStd::string child1StatName(CHILD_TIMER_STAT1);
ASSERT_TRUE(m_statsManager->GetStatistic(parentStatName) != nullptr);
ASSERT_TRUE(m_statsManager->GetStatistic(child0StatName) != nullptr);
ASSERT_TRUE(m_statsManager->GetStatistic(child1StatName) != nullptr);
EXPECT_EQ(m_statsManager->GetStatistic(parentStatName)->GetNumSamples(), numIterations);
EXPECT_EQ(m_statsManager->GetStatistic(child0StatName)->GetNumSamples(), numIterations);
EXPECT_EQ(m_statsManager->GetStatistic(child1StatName)->GetNumSamples(), numIterations);
const double minimumExpectDurationOfChildFunctionMicros = 1;
const double minimumExpectDurationOfParentFunctionMicros = 1;
EXPECT_GE(m_statsManager->GetStatistic(parentStatName)->GetMinimum(), minimumExpectDurationOfParentFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(parentStatName)->GetAverage(), minimumExpectDurationOfParentFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(parentStatName)->GetMaximum(), minimumExpectDurationOfParentFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(child0StatName)->GetMinimum(), minimumExpectDurationOfChildFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(child0StatName)->GetAverage(), minimumExpectDurationOfChildFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(child0StatName)->GetMaximum(), minimumExpectDurationOfChildFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(child1StatName)->GetMinimum(), minimumExpectDurationOfChildFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(child1StatName)->GetAverage(), minimumExpectDurationOfChildFunctionMicros);
EXPECT_GE(m_statsManager->GetStatistic(child1StatName)->GetMaximum(), minimumExpectDurationOfChildFunctionMicros);
//Let's validate TimeDataStatisticsManager::RemoveStatistics()
m_statsManager->RemoveStatistic(child1StatName);
ASSERT_TRUE(m_statsManager->GetStatistic(parentStatName) != nullptr);
ASSERT_TRUE(m_statsManager->GetStatistic(child0StatName) != nullptr);
EXPECT_EQ(m_statsManager->GetStatistic(child1StatName), nullptr);
//Let's store the sample count for both parentStatName and child0StatName.
const AZ::u64 numSamplesParent = m_statsManager->GetStatistic(parentStatName)->GetNumSamples();
const AZ::u64 numSamplesChild0 = m_statsManager->GetStatistic(child0StatName)->GetNumSamples();
//Let's call child1 function again and call app.Tick(). child1StatName should be readded to m_statsManager.
ChildFunction1(numIterations, sleepTimeAllFuncsMillis);
app.Tick();
ASSERT_TRUE(m_statsManager->GetStatistic(child1StatName) != nullptr);
EXPECT_EQ(m_statsManager->GetStatistic(parentStatName)->GetNumSamples(), numSamplesParent);
EXPECT_EQ(m_statsManager->GetStatistic(child0StatName)->GetNumSamples(), numSamplesChild0);
EXPECT_EQ(m_statsManager->GetStatistic(child1StatName)->GetNumSamples(), 1);
Debug::FrameProfilerBus::Handler::BusDisconnect();
app.Destroy();
}
AZStd::unique_ptr<Statistics::TimeDataStatisticsManager> m_statsManager;
};//class TimeDataStatisticsManagerTest
// TODO:BUDGETS disabled until profiler budgets system comes online
// TEST_F(TimeDataStatisticsManagerTest, Test)
// {
// run();
// }
//End of all Tests of TimeDataStatisticsManagerTest
}//namespace UnitTest

@ -29,7 +29,6 @@ set(FILES
Console/ConsoleTests.cpp
Debug.cpp
DLL.cpp
Driller.cpp
EBus.cpp
EntityIdTests.cpp
EntityTests.cpp
@ -66,7 +65,6 @@ set(FILES
SystemFile.cpp
TaskTests.cpp
TickBusTest.cpp
TimeDataStatistics.cpp
UUIDTests.cpp
XML.cpp
Debug/AssetTracking.cpp
@ -104,6 +102,7 @@ set(FILES
Serialization/Json/JsonSerializationResultTests.cpp
Serialization/Json/JsonSerializationTests.h
Serialization/Json/JsonSerializationTests.cpp
Serialization/Json/JsonSerializationUtilsTests.cpp
Serialization/Json/JsonSerializerConformityTests.h
Serialization/Json/JsonSerializerMock.h
Serialization/Json/MapSerializerTests.cpp

@ -25,7 +25,6 @@
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/string/regex.h>
#include <AzCore/Serialization/DataPatch.h>
#include <AzCore/Debug/FrameProfilerComponent.h>
#include <AzCore/NativeUI/NativeUISystemComponent.h>
#include <AzCore/Module/ModuleManagerBus.h>
#include <AzCore/Interface/Interface.h>
@ -60,7 +59,6 @@
#include <AzFramework/StreamingInstall/StreamingInstall.h>
#include <AzFramework/TargetManagement/TargetManagementComponent.h>
#include <AzFramework/Viewport/CameraState.h>
#include <AzFramework/Driller/RemoteDrillerInterface.h>
#include <AzFramework/Metrics/MetricsPlainTextNameRegistration.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
@ -296,7 +294,6 @@ namespace AzFramework
azrtti_typeid<AZ::JobManagerComponent>(),
azrtti_typeid<AZ::AssetManagerComponent>(),
azrtti_typeid<AZ::UserSettingsComponent>(),
azrtti_typeid<AZ::Debug::FrameProfilerComponent>(),
azrtti_typeid<AZ::SliceComponent>(),
azrtti_typeid<AZ::SliceSystemComponent>(),
@ -312,7 +309,6 @@ namespace AzFramework
#endif
azrtti_typeid<AzFramework::AssetSystem::AssetSystemComponent>(),
azrtti_typeid<AzFramework::InputSystemComponent>(),
azrtti_typeid<AzFramework::DrillerNetworkAgentComponent>(),
#if !defined(AZCORE_EXCLUDE_LUA)
azrtti_typeid<AZ::ScriptSystemComponent>(),
@ -374,7 +370,6 @@ namespace AzFramework
azrtti_typeid<AzFramework::RenderGeometry::GameIntersectorComponent>(),
azrtti_typeid<AzFramework::AssetSystem::AssetSystemComponent>(),
azrtti_typeid<AzFramework::InputSystemComponent>(),
azrtti_typeid<AzFramework::DrillerNetworkAgentComponent>(),
azrtti_typeid<AzFramework::StreamingInstall::StreamingInstallSystemComponent>(),
azrtti_typeid<AzFramework::SpawnableSystemComponent>(),
AZ::Uuid("{624a7be2-3c7e-4119-aee2-1db2bdb6cc89}"), // ScriptDebugAgent

@ -191,3 +191,5 @@ namespace AzFramework
};
} // namespace AzFramework
AZ_DECLARE_BUDGET(AzFramework);

@ -14,7 +14,6 @@
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Components/NonUniformScaleComponent.h>
#include <AzFramework/Components/AzFrameworkConfigurationSystemComponent.h>
#include <AzFramework/Driller/RemoteDrillerInterface.h>
#include <AzFramework/Entity/GameEntityContextComponent.h>
#include <AzFramework/FileTag/FileTagComponent.h>
#include <AzFramework/Input/System/InputSystemComponent.h>
@ -27,6 +26,8 @@
#include <AzFramework/TargetManagement/TargetManagementComponent.h>
#include <AzFramework/Visibility/OctreeSystemComponent.h>
AZ_DEFINE_BUDGET(AzFramework);
namespace AzFramework
{
AzFrameworkModule::AzFrameworkModule()
@ -46,7 +47,6 @@ namespace AzFramework
AzFramework::CreateScriptDebugAgentFactory(),
AzFramework::AssetSystem::AssetSystemComponent::CreateDescriptor(),
AzFramework::InputSystemComponent::CreateDescriptor(),
AzFramework::DrillerNetworkAgentComponent::CreateDescriptor(),
#if !defined(AZCORE_EXCLUDE_LUA)
AzFramework::ScriptComponent::CreateDescriptor(),

@ -1,197 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzFramework/Driller/DrillToFileComponent.h>
#include <AzCore/Driller/Driller.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/IO/FileIO.h>
namespace AzFramework
{
void DrillToFileComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<DrillToFileComponent, AZ::Component>()
;
if (serialize->FindClassData(DrillerInfo::RTTI_Type()) == nullptr)
{
serialize->Class<DrillerInfo>()
->Field("Id", &DrillerInfo::m_id)
->Field("GroupName", &DrillerInfo::m_groupName)
->Field("Name", &DrillerInfo::m_name)
->Field("Description", &DrillerInfo::m_description);
}
}
}
void DrillToFileComponent::Activate()
{
m_drillerSession = nullptr;
DrillerConsoleCommandBus::Handler::BusConnect();
}
void DrillToFileComponent::Deactivate()
{
DrillerConsoleCommandBus::Handler::BusDisconnect();
StopDrillerSession(reinterpret_cast<AZ::u64>(this));
}
void DrillToFileComponent::WriteBinary(const void* data, unsigned int dataSize)
{
if (dataSize > 0)
{
m_frameBuffer.insert(m_frameBuffer.end(), reinterpret_cast<const AZ::u8*>(data), reinterpret_cast<const AZ::u8*>(data) + dataSize);
}
}
void DrillToFileComponent::OnEndOfFrame()
{
AZStd::lock_guard<AZStd::mutex> lock(m_writerMutex);
m_writeQueue.push_back();
m_writeQueue.back().swap(m_frameBuffer);
m_signal.notify_all();
}
void DrillToFileComponent::EnumerateAvailableDrillers()
{
DrillerInfoListType availableDrillers;
AZ::Debug::DrillerManager* mgr = NULL;
EBUS_EVENT_RESULT(mgr, AZ::ComponentApplicationBus, GetDrillerManager);
if (mgr)
{
for (int i = 0; i < mgr->GetNumDrillers(); ++i)
{
AZ::Debug::Driller* driller = mgr->GetDriller(i);
AZ_Assert(driller, "DrillerManager returned a NULL driller. This is not legal!");
availableDrillers.push_back();
availableDrillers.back().m_id = driller->GetId();
availableDrillers.back().m_groupName = driller->GroupName();
availableDrillers.back().m_name = driller->GetName();
availableDrillers.back().m_description = driller->GetDescription();
}
}
EBUS_EVENT(DrillerConsoleEventBus, OnDrillersEnumerated, availableDrillers);
}
void DrillToFileComponent::StartDrillerSession(const AZ::Debug::DrillerManager::DrillerListType& requestedDrillers, AZ::u64 sessionId)
{
if (!m_drillerSession)
{
AZ_Assert(m_writeQueue.empty(), "write queue is not empty!");
m_sessionId = sessionId;
AZ::Debug::DrillerManager* mgr = nullptr;
EBUS_EVENT_RESULT(mgr, AZ::ComponentApplicationBus, GetDrillerManager);
if (mgr)
{
SetStringPool(&m_stringPool);;
m_drillerSession = mgr->Start(*this, requestedDrillers);
AZStd::unique_lock<AZStd::mutex> signalLock(m_writerMutex);
m_isWriterEnabled = true;
AZStd::thread_desc td;
td.m_name = "DrillToFileComponent Writer Thread";
m_writerThread = AZStd::thread(AZStd::bind(&DrillToFileComponent::AsyncWritePump, this), &td);
m_signal.wait(signalLock);
EBUS_EVENT(DrillerConsoleEventBus, OnDrillerSessionStarted, sessionId);
}
}
}
void DrillToFileComponent::StopDrillerSession(AZ::u64 sessionId)
{
if (sessionId == m_sessionId)
{
if (m_drillerSession)
{
AZ::Debug::DrillerManager* mgr = NULL;
EBUS_EVENT_RESULT(mgr, AZ::ComponentApplicationBus, GetDrillerManager);
if (mgr)
{
mgr->Stop(m_drillerSession);
}
m_drillerSession = nullptr;
EBUS_EVENT(DrillerConsoleEventBus, OnDrillerSessionStopped, reinterpret_cast<AZ::u64>(this));
}
m_isWriterEnabled = false;
if (m_writerThread.joinable())
{
m_writerMutex.lock();
m_signal.notify_all();
m_writerMutex.unlock();
m_writerThread.join();
}
SetStringPool(nullptr);
m_stringPool.Reset();
m_frameBuffer.clear(); // there may be pending data but we don't want to write it because it's an incomplete frame.
}
}
void DrillToFileComponent::AsyncWritePump()
{
AZStd::unique_lock<AZStd::mutex> signalLock(m_writerMutex);
AZStd::basic_string<char, AZStd::char_traits<char>, AZ::OSStdAllocator> drillerOutputPath;
// Try the log path first
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
if (fileIO)
{
const char* logLocation = fileIO->GetAlias("@log@");
if (logLocation)
{
drillerOutputPath = logLocation;
drillerOutputPath.append("/");
}
}
// Try the executable path
if (drillerOutputPath.empty())
{
EBUS_EVENT_RESULT(drillerOutputPath, AZ::ComponentApplicationBus, GetExecutableFolder);
drillerOutputPath.append("/");
}
drillerOutputPath.append("drillerdata.drl");
AZ::IO::SystemFile output;
output.Open(drillerOutputPath.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY);
AZ_Assert(output.IsOpen(), "Failed to open driller output file!");
m_signal.notify_all();
while (true)
{
while (!m_writeQueue.empty())
{
AZStd::vector<AZ::u8, AZ::OSStdAllocator> outBuffer;
outBuffer.swap(m_writeQueue.front());
m_writeQueue.pop_front();
signalLock.unlock();
output.Write(outBuffer.data(), outBuffer.size());
output.Flush();
signalLock.lock();
}
if (!m_isWriterEnabled)
{
break;
}
m_signal.wait(signalLock);
}
output.Close();
}
} // namespace AzFramework

@ -1,74 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzFramework/Driller/DrillerConsoleAPI.h>
#include <AzCore/Driller/Driller.h>
#include <AzCore/Driller/DefaultStringPool.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/containers/deque.h>
#include <AzCore/std/parallel/condition_variable.h>
//#define ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
namespace AZ
{
struct ClassDataReflection;
}
namespace AzFramework
{
/**
* Runs on the machine being drilled and is responsible for communications
* with the DrillerNetworkConsole running on the tool side as well as
* creating DrillerNetSessionStreams for each driller session being started.
*/
class DrillToFileComponent
: public AZ::Component
, public AZ::Debug::DrillerOutputStream
, public DrillerConsoleCommandBus::Handler
{
public:
AZ_COMPONENT(DrillToFileComponent, "{42BAA25D-7CEB-4A37-8BD4-4A1FE2253894}")
//////////////////////////////////////////////////////////////////////////
// AZ::Component
static void Reflect(AZ::ReflectContext* context);
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// DrillerOutputStream
void WriteBinary(const void* data, unsigned int dataSize) override;
void OnEndOfFrame() override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// DrillerConsoleCommandBus
void EnumerateAvailableDrillers() override;
void StartDrillerSession(const AZ::Debug::DrillerManager::DrillerListType& requestedDrillers, AZ::u64 sessionId) override;
void StopDrillerSession(AZ::u64 sessionId) override;
//////////////////////////////////////////////////////////////////////////
protected:
void AsyncWritePump();
AZ::u64 m_sessionId;
AZ::Debug::DrillerSession* m_drillerSession;
AZ::Debug::DrillerDefaultStringPool m_stringPool;
AZStd::vector<AZ::u8, AZ::OSStdAllocator> m_frameBuffer;
AZStd::deque<AZStd::vector<AZ::u8, AZ::OSStdAllocator>, AZ::OSStdAllocator> m_writeQueue;
AZStd::mutex m_writerMutex;
AZStd::condition_variable m_signal;
AZStd::thread m_writerThread;
bool m_isWriterEnabled;
};
} // namespace AzFramework

@ -1,79 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Driller/Driller.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/EBus/EBus.h>
namespace AzFramework
{
/*
* Descriptors for drillers available on the target machine.
*/
struct DrillerInfo final
{
AZ_RTTI(DrillerInfo, "{197AC318-B65C-4B36-A109-BD25422BF7D0}");
AZ::u32 m_id;
AZStd::string m_groupName;
AZStd::string m_name;
AZStd::string m_description;
};
typedef AZStd::vector<DrillerInfo> DrillerInfoListType;
typedef AZStd::vector<AZ::u32> DrillerListType;
/**
* Driller clients interested in receiving notification events from the
* console should implement this interface.
*/
class DrillerConsoleEvents
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
typedef AZ::OSStdAllocator AllocatorType;
//////////////////////////////////////////////////////////////////////////
virtual ~DrillerConsoleEvents() {}
// A list of available drillers has been received from the target machine.
virtual void OnDrillersEnumerated(const DrillerInfoListType& availableDrillers) = 0;
virtual void OnDrillerSessionStarted(AZ::u64 sessionId) = 0;
virtual void OnDrillerSessionStopped(AZ::u64 sessionId) = 0;
};
typedef AZ::EBus<DrillerConsoleEvents> DrillerConsoleEventBus;
/**
* Commands can be sent to the driller through this interface.
*/
class DrillerConsoleCommands
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
typedef AZ::OSStdAllocator AllocatorType;
// there's only one driller console instance allowed
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
virtual ~DrillerConsoleCommands() {}
// Request an enumeration of available drillers from the target machine
virtual void EnumerateAvailableDrillers() = 0;
// Start a drilling session. This function is normally called internally by DrillerRemoteSession
virtual void StartDrillerSession(const AZ::Debug::DrillerManager::DrillerListType& requestedDrillers, AZ::u64 sessionId) = 0;
// Stop a drilling session. This function is normally called internally by DrillerRemoteSession
virtual void StopDrillerSession(AZ::u64 sessionId) = 0;
};
typedef AZ::EBus<DrillerConsoleCommands> DrillerConsoleCommandBus;
} // namespace AzFramework

@ -1,740 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzFramework/Driller/RemoteDrillerInterface.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Driller/Driller.h>
#include <AzCore/Driller/Stream.h>
#include <AzCore/Driller/DefaultStringPool.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Component/TickBus.h>
namespace AzFramework
{
//---------------------------------------------------------------------
// TEMP FOR DEBUGGING ONLY!!!
//---------------------------------------------------------------------
class DebugDrillerRemoteSession
: public DrillerRemoteSession
{
public:
AZ_CLASS_ALLOCATOR(DebugDrillerRemoteSession, AZ::OSAllocator, 0);
DebugDrillerRemoteSession()
{
AZStd::string filename = AZStd::string::format("remotedrill_%llu", static_cast<AZ::u64>(reinterpret_cast<size_t>(static_cast<DrillerRemoteSession*>(this))));
m_file.Open(filename.c_str(), AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY | AZ::IO::SystemFile::SF_OPEN_CREATE);
}
~DebugDrillerRemoteSession()
{
m_file.Close();
}
virtual void ProcessIncomingDrillerData(const char* streamIdentifier, const void* data, size_t dataSize)
{
(void)streamIdentifier;
m_file.Write(data, dataSize);
}
virtual void OnDrillerConnectionLost()
{
delete this;
}
AZ::IO::SystemFile m_file;
};
//---------------------------------------------------------------------
/**
* These are the different synchronization messages that are used.
*/
namespace NetworkDrillerSyncMsgId
{
static const AZ::Crc32 NetDrillMsg_RequestDrillerEnum = AZ_CRC("NetDrillMsg_RequestEnum", 0x517cca25);
static const AZ::Crc32 NetDrillMsg_RequestStartSession = AZ_CRC("NetDrillMsg_RequestStartSession", 0x5238b5fe);
static const AZ::Crc32 NetDrillMsg_RequestStopSession = AZ_CRC("NetDrillMsg_RequestStopSession", 0x1abe6888);
static const AZ::Crc32 NetDrillMsg_DrillerEnum = AZ_CRC("NetDrillMsg_Enum", 0x3d0a0f76);
};
struct NetDrillerStartSessionRequest
: public TmMsg
{
AZ_CLASS_ALLOCATOR(NetDrillerStartSessionRequest, AZ::OSAllocator, 0);
AZ_RTTI(NetDrillerStartSessionRequest, "{FF899D61-A445-44B5-9B67-8319ACC8BB06}");
NetDrillerStartSessionRequest()
: TmMsg(NetworkDrillerSyncMsgId::NetDrillMsg_RequestStartSession) {}
// TODO: Replace this with the DrillerListType from driller.h
DrillerListType m_drillerIds;
AZ::u64 m_sessionId;
};
struct NetDrillerStopSessionRequest
: public TmMsg
{
AZ_CLASS_ALLOCATOR(NetDrillerStopSessionRequest, AZ::OSAllocator, 0);
AZ_RTTI(NetDrillerStopSessionRequest, "{BCC6524F-287F-48D2-A21A-029215DB24DD}");
NetDrillerStopSessionRequest(AZ::u64 sessionId = 0)
: TmMsg(NetworkDrillerSyncMsgId::NetDrillMsg_RequestStopSession)
, m_sessionId(sessionId) {}
AZ::u64 m_sessionId;
};
struct NetDrillerEnumeration
: public TmMsg
{
AZ_CLASS_ALLOCATOR(NetDrillerEnumeration, AZ::OSAllocator, 0);
AZ_RTTI(NetDrillerEnumeration, "{60E5BED2-F492-4A55-8EF6-2628CD390991}");
NetDrillerEnumeration()
: TmMsg(NetworkDrillerSyncMsgId::NetDrillMsg_DrillerEnum) {}
DrillerInfoListType m_enumeration;
};
//---------------------------------------------------------------------
// DrillerRemoteSession
//---------------------------------------------------------------------
DrillerRemoteSession::DrillerRemoteSession()
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
: m_decompressor(&AZ::AllocatorInstance<AZ::OSAllocator>::Get())
#endif
{
}
//---------------------------------------------------------------------
DrillerRemoteSession::~DrillerRemoteSession()
{
}
//---------------------------------------------------------------------
void DrillerRemoteSession::StartDrilling(const DrillerListType& drillers, const char* captureFile)
{
if (captureFile)
{
m_captureFile.Open(captureFile, AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY);
AZ_Warning("DrillerRemoteSession", m_captureFile.IsOpen(), "Failed to open %s. Driller data will not be saved.", captureFile);
}
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
m_decompressor.StartDecompressor();
#endif
BusConnect(static_cast<AZ::u64>(reinterpret_cast<size_t>(this)));
EBUS_EVENT(DrillerNetworkConsoleCommandBus, StartRemoteDrillerSession, drillers, this);
}
//---------------------------------------------------------------------
void DrillerRemoteSession::StopDrilling()
{
EBUS_EVENT(DrillerNetworkConsoleCommandBus, StopRemoteDrillerSession, static_cast<AZ::u64>(reinterpret_cast<size_t>(this)));
BusDisconnect();
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
if (m_decompressor.IsDecompressorStarted())
{
m_decompressor.StopDecompressor();
}
#endif
m_captureFile.Close();
}
//---------------------------------------------------------------------
void DrillerRemoteSession::LoadCaptureData(const char* fileName)
{
m_captureFile.Open(fileName, AZ::IO::SystemFile::SF_OPEN_READ_ONLY);
AZ_Warning("DrillerRemoteSession", m_captureFile.IsOpen(), "Failed to open %s. No driller data could be loaded.", fileName);
if (m_captureFile.IsOpen())
{
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
m_decompressor.StartDecompressor();
#endif
AZ::IO::SystemFile::SizeType bytesRemaining = m_captureFile.Length();
AZ::IO::SystemFile::SizeType maxReadChunkSize = 1024 * 1024;
AZStd::vector<char> readBuffer;
readBuffer.resize_no_construct(static_cast<size_t>(maxReadChunkSize));
while (bytesRemaining > 0)
{
AZ::IO::SystemFile::SizeType bytesToRead = bytesRemaining < maxReadChunkSize ? bytesRemaining : maxReadChunkSize;
if (m_captureFile.Read(bytesToRead, readBuffer.data()) != bytesToRead)
{
AZ_Warning("DrillerRemoteSession", false, "Failed reading driller data. No more driller data can be read.");
break;
}
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
Decompress(readBuffer.data(), static_cast<size_t>(bytesToRead));
ProcessIncomingDrillerData(fileName, m_uncompressedMsgBuffer.data(), m_uncompressedMsgBuffer.size());
#else
ProcessIncomingDrillerData(fileName, readBuffer.data(), readBuffer.size());
#endif
bytesRemaining -= bytesToRead;
}
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
m_decompressor.StopDecompressor();
#endif
m_captureFile.Close();
}
}
//---------------------------------------------------------------------
void DrillerRemoteSession::OnReceivedMsg(TmMsgPtr msg)
{
AZ_Assert(msg->GetCustomBlob(), "Missing driller frame data!");
if (msg->GetCustomBlobSize() == 0)
{
return;
}
if (m_captureFile.IsOpen())
{
if (m_captureFile.Write(msg->GetCustomBlob(), msg->GetCustomBlobSize()) != msg->GetCustomBlobSize())
{
AZ_Warning("DrillerRemoteSession", false, "Failed writing capture data to %s, no more data will be written out.", m_captureFile.Name());
m_captureFile.Close();
}
}
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
Decompress(msg->GetCustomBlob(), msg->GetCustomBlobSize());
ProcessIncomingDrillerData(m_captureFile.Name(),m_uncompressedMsgBuffer.data(), m_uncompressedMsgBuffer.size());
#else
ProcessIncomingDrillerData(m_captureFile.Name(),msg->GetCustomBlob(), msg->GetCustomBlobSize());
#endif
}
//---------------------------------------------------------------------
void DrillerRemoteSession::Decompress(const void* compressedBuffer, size_t compressedBufferSize)
{
m_uncompressedMsgBuffer.clear();
if (m_uncompressedMsgBuffer.capacity() < compressedBufferSize * 10)
{
m_uncompressedMsgBuffer.reserve(compressedBufferSize * 10);
}
#if defined(ENABLE_COMPRESSION_FOR_REMOTE_DRILLER)
unsigned int compressedBytesRemaining = static_cast<unsigned int>(compressedBufferSize);
unsigned int decompressedBytes = 0;
while (compressedBytesRemaining > 0)
{
unsigned int uncompressedBytes = c_decompressionBufferSize;
unsigned int bytesConsumed = m_decompressor.Decompress(reinterpret_cast<const char*>(compressedBuffer) + decompressedBytes, compressedBytesRemaining, m_decompressionBuffer, uncompressedBytes);
decompressedBytes += bytesConsumed;
compressedBytesRemaining -= bytesConsumed;
m_uncompressedMsgBuffer.insert(m_uncompressedMsgBuffer.end(), &m_decompressionBuffer[0], &m_decompressionBuffer[uncompressedBytes]);
}
#else
m_uncompressedMsgBuffer.insert(m_uncompressedMsgBuffer.end(), &((char*)compressedBuffer)[0], &((char*)compressedBuffer)[compressedBufferSize]);
#endif
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// DrillerNetSessionStream
//---------------------------------------------------------------------
/**
* Represents a driller session on the target machine.
* It is responsible for listening for driller events and forwarding
* them to the console machine.
*/
class DrillerNetSessionStream
: public AZ::Debug::DrillerOutputStream
, AZ::SystemTickBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(DrillerNetSessionStream, AZ::OSAllocator, 0);
DrillerNetSessionStream(AZ::u64 sessionId);
~DrillerNetSessionStream();
//---------------------------------------------------------------------
// DrillerOutputStream
//---------------------------------------------------------------------
virtual void WriteBinary(const void* data, unsigned int dataSize);
virtual void OnEndOfFrame();
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// AZ::SystemTickBus
//---------------------------------------------------------------------
void OnSystemTick() override;
//---------------------------------------------------------------------
static const size_t c_defaultUncompressedBufferSize = 256 * 1024;
static const size_t c_defaultCompressedBufferSize = 32 * 1024;
static const size_t c_bufferCount = 2;
AZ::Debug::DrillerSession* m_session;
AZ::u64 m_sessionId;
TargetInfo m_requestor;
size_t m_activeBuffer;
AZStd::vector<char, AZ::OSStdAllocator> m_uncompressedBuffer[c_bufferCount];
AZStd::vector<char, AZ::OSStdAllocator> m_compressedBuffer[c_bufferCount];
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
// Compression
AZ::ZLib m_compressor;
AZStd::fixed_vector<char, c_defaultCompressedBufferSize> m_compressionBuffer;
#endif
// String Pooling
AZ::Debug::DrillerDefaultStringPool m_stringPool;
// TEMP Debug
//AZ::IO::SystemFile m_file;
};
DrillerNetSessionStream::DrillerNetSessionStream(AZ::u64 sessionId)
: m_session(NULL)
, m_sessionId(sessionId)
, m_activeBuffer(0)
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
, m_compressor(&AZ::AllocatorInstance<AZ::OSAllocator>::Get())
#endif
{
for (size_t i = 0; i < c_bufferCount; ++i)
{
m_uncompressedBuffer[i].reserve(c_defaultUncompressedBufferSize);
m_compressedBuffer[i].reserve(c_defaultCompressedBufferSize);
}
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
// Level 3 compression seems to give pretty good compression at decent speed.
// Speed is paramount for us because initial driller packets can be huge and
// we need to be able to compress the data within the driller report call
// without blocking for too long.
m_compressor.StartCompressor(3);
#endif
SetStringPool(&m_stringPool);
AZ::SystemTickBus::Handler::BusConnect();
}
//---------------------------------------------------------------------
DrillerNetSessionStream::~DrillerNetSessionStream()
{
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
m_compressor.StopCompressor();
#endif
// Debug
//m_file.Close();
}
//---------------------------------------------------------------------
void DrillerNetSessionStream::WriteBinary(const void* data, unsigned int dataSize)
{
size_t activeBuffer = m_activeBuffer;
if (dataSize > 0)
{
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
// Only do the compression when the buffer is full so we don't run the compression all the time
if (m_uncompressedBuffer[activeBuffer].size() + dataSize > c_defaultUncompressedBufferSize)
{
// compress
unsigned int curDataSize = static_cast<unsigned int>(m_uncompressedBuffer[activeBuffer].size());
unsigned int remaining = curDataSize;
while (remaining > 0)
{
unsigned int processedBytes = curDataSize - remaining;
unsigned int compressedBytes = m_compressor.Compress(m_uncompressedBuffer[activeBuffer].data() + processedBytes, remaining, m_compressionBuffer.data(), static_cast<unsigned int>(c_defaultCompressedBufferSize));
if (compressedBytes > 0)
{
m_compressedBuffer[activeBuffer].insert(m_compressedBuffer[activeBuffer].end(), m_compressionBuffer.data(), m_compressionBuffer.data() + compressedBytes);
}
}
m_uncompressedBuffer[activeBuffer].clear();
}
m_uncompressedBuffer[activeBuffer].insert(m_uncompressedBuffer[activeBuffer].end(), reinterpret_cast<const char*>(data), reinterpret_cast<const char*>(data) + dataSize);
#else
// Since we are not compressing, transfer the input directly into our compressed buffer
m_compressedBuffer[activeBuffer].insert(m_compressedBuffer[activeBuffer].end(), reinterpret_cast<const char*>(data), reinterpret_cast<const char*>(data) + dataSize);
#endif
}
}
//---------------------------------------------------------------------
void DrillerNetSessionStream::OnEndOfFrame()
{
size_t activeBuffer = m_activeBuffer;
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
// Write whatever data has not yet been compressed and flush the compressor
unsigned int curDataSize = static_cast<unsigned int>(m_uncompressedBuffer[activeBuffer].size());
unsigned int remaining = curDataSize;
unsigned int compressedBytes = 0;
do
{
unsigned int processedBytes = curDataSize - remaining;
compressedBytes = m_compressor.Compress(m_uncompressedBuffer[activeBuffer].data() + processedBytes, remaining, m_compressionBuffer.data(), static_cast<unsigned int>(c_defaultCompressedBufferSize), AZ::ZLib::FT_SYNC_FLUSH);
if (compressedBytes > 0)
{
m_compressedBuffer[activeBuffer].insert(m_compressedBuffer[activeBuffer].end(), m_compressionBuffer.data(), m_compressionBuffer.data() + compressedBytes);
}
} while (compressedBytes > 0 || remaining > 0);
#endif
m_activeBuffer = (activeBuffer + 1) % 2; // switch buffers
}
//-------------------------------------------------------------------------
void DrillerNetSessionStream::OnSystemTick()
{
// The buffer index we want to send is the one we wrote to in the previous frame.
size_t bufferIndex = (m_activeBuffer + 1) % 2;
if (m_compressedBuffer[bufferIndex].empty())
{
return;
}
TmMsg msg(m_sessionId);
msg.AddCustomBlob(m_compressedBuffer[bufferIndex].data(), m_compressedBuffer[bufferIndex].size());
EBUS_EVENT(TargetManager::Bus, SendTmMessage, m_requestor, msg);
// Debug
//if (!m_file.IsOpen())
//{
// AZStd::string filename = AZStd::string::format("localdrill_%llu", m_sessionId);
// m_file.Open(filename.c_str(), AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY | AZ::IO::SystemFile::SF_OPEN_CREATE);
//}
//m_file.Write(msg.GetCustomBlob(), msg.GetCustomBlobSize());
// Reset buffers
m_uncompressedBuffer[bufferIndex].clear();
m_compressedBuffer[bufferIndex].clear();
// Buffers may grow during exceptional circumstances. Re-shrink them to their default sizes
// so we don't keep holding on to the memory.
m_uncompressedBuffer[bufferIndex].reserve(c_defaultUncompressedBufferSize);
m_compressedBuffer[bufferIndex].reserve(c_defaultCompressedBufferSize);
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// DrillerNetworkAgent
//---------------------------------------------------------------------
void DrillerNetworkAgentComponent::Init()
{
m_cbDrillerEnumRequest = TmMsgCallback(AZStd::bind(&DrillerNetworkAgentComponent::OnRequestDrillerEnum, this, AZStd::placeholders::_1));
m_cbDrillerStartRequest = TmMsgCallback(AZStd::bind(&DrillerNetworkAgentComponent::OnRequestDrillerStart, this, AZStd::placeholders::_1));
m_cbDrillerStopRequest = TmMsgCallback(AZStd::bind(&DrillerNetworkAgentComponent::OnRequestDrillerStop, this, AZStd::placeholders::_1));
}
//---------------------------------------------------------------------
void DrillerNetworkAgentComponent::Activate()
{
m_cbDrillerEnumRequest.BusConnect(NetworkDrillerSyncMsgId::NetDrillMsg_RequestDrillerEnum);
m_cbDrillerStartRequest.BusConnect(NetworkDrillerSyncMsgId::NetDrillMsg_RequestStartSession);
m_cbDrillerStopRequest.BusConnect(NetworkDrillerSyncMsgId::NetDrillMsg_RequestStopSession);
TargetManagerClient::Bus::Handler::BusConnect();
}
//---------------------------------------------------------------------
void DrillerNetworkAgentComponent::Deactivate()
{
TargetManagerClient::Bus::Handler::BusDisconnect();
m_cbDrillerEnumRequest.BusDisconnect(NetworkDrillerSyncMsgId::NetDrillMsg_RequestDrillerEnum);
m_cbDrillerStartRequest.BusDisconnect(NetworkDrillerSyncMsgId::NetDrillMsg_RequestStartSession);
m_cbDrillerStopRequest.BusDisconnect(NetworkDrillerSyncMsgId::NetDrillMsg_RequestStopSession);
AZ::Debug::DrillerManager* mgr = NULL;
EBUS_EVENT_RESULT(mgr, AZ::ComponentApplicationBus, GetDrillerManager);
for (size_t i = 0; i < m_activeSessions.size(); ++i)
{
if (mgr)
{
mgr->Stop(m_activeSessions[i]->m_session);
}
delete m_activeSessions[i];
}
m_activeSessions.clear();
}
//---------------------------------------------------------------------
void DrillerNetworkAgentComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("DrillerNetworkAgentService", 0xcd2ab821));
}
//---------------------------------------------------------------------
void DrillerNetworkAgentComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("DrillerNetworkAgentService", 0xcd2ab821));
}
//---------------------------------------------------------------------
void DrillerNetworkAgentComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<DrillerNetworkAgentComponent, AZ::Component>()
->Version(1)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<DrillerNetworkAgentComponent>(
"Driller Network Agent", "Runs on the machine being drilled and communicates with tools")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Profiling")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
;
}
ReflectNetDrillerClasses(context);
}
}
//---------------------------------------------------------------------
void DrillerNetworkAgentComponent::TargetLeftNetwork(TargetInfo info)
{
AZ::Debug::DrillerManager* mgr = NULL;
EBUS_EVENT_RESULT(mgr, AZ::ComponentApplicationBus, GetDrillerManager);
for (AZStd::vector<DrillerNetSessionStream*>::iterator it = m_activeSessions.begin(); it != m_activeSessions.end(); )
{
if ((*it)->m_requestor.GetNetworkId() == info.GetNetworkId())
{
if (mgr)
{
mgr->Stop((*it)->m_session);
}
delete *it;
it = m_activeSessions.erase(it);
}
else
{
++it;
}
}
}
//---------------------------------------------------------------------
void DrillerNetworkAgentComponent::OnRequestDrillerEnum(TmMsgPtr msg)
{
AZ::Debug::DrillerManager* mgr = NULL;
EBUS_EVENT_RESULT(mgr, AZ::ComponentApplicationBus, GetDrillerManager);
if (!mgr)
{
return;
}
TargetInfo sendTo;
EBUS_EVENT_RESULT(sendTo, TargetManager::Bus, GetTargetInfo, msg->GetSenderTargetId());
NetDrillerEnumeration drillerEnum;
for (int i = 0; i < mgr->GetNumDrillers(); ++i)
{
AZ::Debug::Driller* driller = mgr->GetDriller(i);
AZ_Assert(driller, "DrillerManager returned a NULL driller. This is not legal!");
drillerEnum.m_enumeration.push_back();
drillerEnum.m_enumeration.back().m_id = driller->GetId();
drillerEnum.m_enumeration.back().m_groupName = driller->GroupName();
drillerEnum.m_enumeration.back().m_name = driller->GetName();
drillerEnum.m_enumeration.back().m_description = driller->GetDescription();
}
EBUS_EVENT(TargetManager::Bus, SendTmMessage, sendTo, drillerEnum);
}
//---------------------------------------------------------------------
void DrillerNetworkAgentComponent::OnRequestDrillerStart(TmMsgPtr msg)
{
AZ::Debug::DrillerManager* mgr = NULL;
EBUS_EVENT_RESULT(mgr, AZ::ComponentApplicationBus, GetDrillerManager);
if (!mgr)
{
return;
}
NetDrillerStartSessionRequest* request = azdynamic_cast<NetDrillerStartSessionRequest*>(msg.get());
AZ_Assert(request, "Not a NetDrillerStartSessionRequest msg!");
AZ::Debug::DrillerManager::DrillerListType drillers;
for (size_t i = 0; i < request->m_drillerIds.size(); ++i)
{
AZ::Debug::DrillerManager::DrillerInfo di;
di.id = request->m_drillerIds[i];
drillers.push_back(di);
}
DrillerNetSessionStream* session = aznew DrillerNetSessionStream(request->m_sessionId);
EBUS_EVENT_RESULT(session->m_requestor, TargetManager::Bus, GetTargetInfo, msg->GetSenderTargetId());
m_activeSessions.push_back(session);
session->m_session = mgr->Start(*session, drillers);
}
//---------------------------------------------------------------------
void DrillerNetworkAgentComponent::OnRequestDrillerStop(TmMsgPtr msg)
{
NetDrillerStopSessionRequest* request = azdynamic_cast<NetDrillerStopSessionRequest*>(msg.get());
for (AZStd::vector<DrillerNetSessionStream*>::iterator it = m_activeSessions.begin(); it != m_activeSessions.end(); ++it)
{
if ((*it)->m_sessionId == request->m_sessionId)
{
AZ::Debug::DrillerManager* mgr = NULL;
EBUS_EVENT_RESULT(mgr, AZ::ComponentApplicationBus, GetDrillerManager);
if (mgr)
{
mgr->Stop((*it)->m_session);
}
delete *it;
m_activeSessions.erase(it);
return;
}
}
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// DrillerRemoteConsole
//---------------------------------------------------------------------
void DrillerNetworkConsoleComponent::Init()
{
m_cbDrillerEnum = TmMsgCallback(AZStd::bind(&DrillerNetworkConsoleComponent::OnReceivedDrillerEnum, this, AZStd::placeholders::_1));
}
//---------------------------------------------------------------------
void DrillerNetworkConsoleComponent::Activate()
{
m_cbDrillerEnum.BusConnect(NetworkDrillerSyncMsgId::NetDrillMsg_DrillerEnum);
DrillerNetworkConsoleCommandBus::Handler::BusConnect();
TargetManagerClient::Bus::Handler::BusConnect();
}
//---------------------------------------------------------------------
void DrillerNetworkConsoleComponent::Deactivate()
{
TargetManagerClient::Bus::Handler::BusDisconnect();
DrillerNetworkConsoleCommandBus::Handler::BusDisconnect();
m_cbDrillerEnum.BusDisconnect(NetworkDrillerSyncMsgId::NetDrillMsg_DrillerEnum);
for (size_t i = 0; i < m_activeSessions.size(); ++i)
{
EBUS_EVENT(TargetManager::Bus, SendTmMessage, m_curTarget, NetDrillerStopSessionRequest(static_cast<AZ::u64>(reinterpret_cast<size_t>(m_activeSessions[i]))));
m_activeSessions[i]->OnDrillerConnectionLost();
}
m_activeSessions.clear();
}
//---------------------------------------------------------------------
void DrillerNetworkConsoleComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("DrillerNetworkConsoleService", 0x2286125d));
}
//---------------------------------------------------------------------
void DrillerNetworkConsoleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("DrillerNetworkConsoleService", 0x2286125d));
}
//---------------------------------------------------------------------
void DrillerNetworkConsoleComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<DrillerNetworkConsoleComponent, AZ::Component>()
->Version(1)
;
if (AZ::EditContext* editContext = serialize->GetEditContext())
{
editContext->Class<DrillerNetworkConsoleComponent>(
"Driller Network Console", "Runs on the tool machine and is responsible for communications with the DrillerNetworkAgent")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Profiling")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
;
}
ReflectNetDrillerClasses(context);
}
}
//---------------------------------------------------------------------
void DrillerNetworkConsoleComponent::EnumerateAvailableDrillers()
{
EBUS_EVENT(TargetManager::Bus, SendTmMessage, m_curTarget, TmMsg(NetworkDrillerSyncMsgId::NetDrillMsg_RequestDrillerEnum));
}
//---------------------------------------------------------------------
void DrillerNetworkConsoleComponent::StartRemoteDrillerSession(const DrillerListType& drillers, DrillerRemoteSession* handler)
{
NetDrillerStartSessionRequest request;
request.m_drillerIds = drillers;
request.m_sessionId = static_cast<AZ::u64>(reinterpret_cast<size_t>(handler));
m_activeSessions.push_back(handler);
EBUS_EVENT(TargetManager::Bus, SendTmMessage, m_curTarget, request);
}
//---------------------------------------------------------------------
void DrillerNetworkConsoleComponent::StopRemoteDrillerSession(AZ::u64 sessionId)
{
for (size_t i = 0; i < m_activeSessions.size(); ++i)
{
if (sessionId == static_cast<AZ::u64>(reinterpret_cast<size_t>(m_activeSessions[i])))
{
EBUS_EVENT(TargetManager::Bus, SendTmMessage, m_curTarget, NetDrillerStopSessionRequest(sessionId));
m_activeSessions[i] = m_activeSessions.back();
m_activeSessions.pop_back();
}
}
}
//---------------------------------------------------------------------
void DrillerNetworkConsoleComponent::DesiredTargetConnected(bool connected)
{
if (connected)
{
EBUS_EVENT_RESULT(m_curTarget, TargetManager::Bus, GetDesiredTarget);
EBUS_EVENT(DrillerNetworkConsoleCommandBus, EnumerateAvailableDrillers);
}
else
{
for (size_t i = 0; i < m_activeSessions.size(); ++i)
{
m_activeSessions[i]->OnDrillerConnectionLost();
}
m_activeSessions.clear();
EBUS_EVENT(DrillerNetworkConsoleEventBus, OnReceivedDrillerEnumeration, DrillerInfoListType());
}
}
//---------------------------------------------------------------------
void DrillerNetworkConsoleComponent::DesiredTargetChanged(AZ::u32 newTargetID, AZ::u32 oldTargetID)
{
(void)oldTargetID;
(void)newTargetID;
EBUS_EVENT(DrillerNetworkConsoleEventBus, OnReceivedDrillerEnumeration, DrillerInfoListType());
for (size_t i = 0; i < m_activeSessions.size(); ++i)
{
EBUS_EVENT(TargetManager::Bus, SendTmMessage, m_curTarget, NetDrillerStopSessionRequest(static_cast<AZ::u64>(reinterpret_cast<size_t>(m_activeSessions[i]))));
m_activeSessions[i]->OnDrillerConnectionLost();
}
m_activeSessions.clear();
}
//---------------------------------------------------------------------
void DrillerNetworkConsoleComponent::OnReceivedDrillerEnum(TmMsgPtr msg)
{
NetDrillerEnumeration* drillerEnum = azdynamic_cast<NetDrillerEnumeration*>(msg.get());
AZ_Assert(drillerEnum, "No NetDrillerEnumeration message!");
EBUS_EVENT(DrillerNetworkConsoleEventBus, OnReceivedDrillerEnumeration, drillerEnum->m_enumeration);
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// ReflectNetDrillerClasses
//---------------------------------------------------------------------
void ReflectNetDrillerClasses(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
// Assume no one else will register our classes.
if (serialize->FindClassData(DrillerInfo::RTTI_Type()) == nullptr)
{
serialize->Class<DrillerInfo>()
->Field("Id", &DrillerInfo::m_id)
->Field("GroupName", &DrillerInfo::m_groupName)
->Field("Name", &DrillerInfo::m_name)
->Field("Description", &DrillerInfo::m_description);
serialize->Class<NetDrillerStartSessionRequest, TmMsg>()
->Field("DrillerIds", &NetDrillerStartSessionRequest::m_drillerIds)
->Field("SessionId", &NetDrillerStartSessionRequest::m_sessionId);
serialize->Class<NetDrillerStopSessionRequest, TmMsg>()
->Field("SessionId", &NetDrillerStopSessionRequest::m_sessionId);
serialize->Class<NetDrillerEnumeration, TmMsg>()
->Field("Enumeration", &NetDrillerEnumeration::m_enumeration);
}
}
}
//---------------------------------------------------------------------
} // namespace AzFramework

@ -1,217 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZFRAMEWORK_REMOTE_DRILLER_INTERFACE_H
#define AZFRAMEWORK_REMOTE_DRILLER_INTERFACE_H
#include <AzCore/Driller/Driller.h>
#include <AzCore/Compression/Compression.h>
#include <AzCore/Component/Component.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Driller/DrillerConsoleAPI.h>
#include <AzFramework/TargetManagement/TargetManagementAPI.h>
//#define ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
namespace AZ
{
struct ClassDataReflection;
}
namespace AzFramework
{
/**
* Represents a remote driller session on the tool machine.
* It is responsible for receiving and processing remote driller data.
* Driller clients should derive from this class and implement the virtual interfaces.
*/
class DrillerRemoteSession
: public TmMsgBus::Handler
{
public:
DrillerRemoteSession();
~DrillerRemoteSession();
// Called when new driller data arrives
virtual void ProcessIncomingDrillerData(const char* streamIdentifier, const void* data, size_t dataSize) = 0;
// Called when the connection to the driller is lost. The session should be deleted in response to this message
virtual void OnDrillerConnectionLost() = 0;
// Start drilling the selected drillers as part of this session
void StartDrilling(const DrillerListType& drillers, const char* captureFile);
// Stop this drill session
void StopDrilling();
// Replay a previously captured driller session from file
void LoadCaptureData(const char* fileName);
protected:
//---------------------------------------------------------------------
// TmMsgBus
//---------------------------------------------------------------------
virtual void OnReceivedMsg(TmMsgPtr msg);
//---------------------------------------------------------------------
void Decompress(const void* compressedBuffer, size_t compressedBufferSize);
static const AZ::u32 c_decompressionBufferSize = 128 * 1024;
AZStd::vector<char> m_uncompressedMsgBuffer;
#ifdef ENABLE_COMPRESSION_FOR_REMOTE_DRILLER
AZ::ZLib m_decompressor;
char m_decompressionBuffer[c_decompressionBufferSize];
#endif
AZ::IO::SystemFile m_captureFile;
};
/**
* Driller clients interested in receiving notification events from the
* network console should implement this interface.
*/
class DrillerNetworkConsoleEvents
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
typedef AZ::OSStdAllocator AllocatorType;
//////////////////////////////////////////////////////////////////////////
virtual ~DrillerNetworkConsoleEvents() {}
// A list of available drillers has been received from the target machine.
virtual void OnReceivedDrillerEnumeration(const DrillerInfoListType& availableDrillers) = 0;
};
typedef AZ::EBus<DrillerNetworkConsoleEvents> DrillerNetworkConsoleEventBus;
/**
* The network driller console implements this interface.
* Commands can be sent to the network console through this interface.
*/
class DrillerNetworkConsoleCommands
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
typedef AZ::OSStdAllocator AllocatorType;
// there's only one driller console instance allowed
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
virtual ~DrillerNetworkConsoleCommands() {}
// Request an enumeration of available drillers from the target machine
virtual void EnumerateAvailableDrillers() = 0;
// Start a drilling session. This function is normally called internally by DrillerRemoteSession
virtual void StartRemoteDrillerSession(const DrillerListType& drillers, DrillerRemoteSession* handler) = 0;
// Stop a drilling session. This function is normally called internally by DrillerRemoteSession
virtual void StopRemoteDrillerSession(AZ::u64 sessionId) = 0;
};
typedef AZ::EBus<DrillerNetworkConsoleCommands> DrillerNetworkConsoleCommandBus;
class DrillerNetSessionStream;
/**
* Runs on the machine being drilled and is responsible for communications
* with the DrillerNetworkConsole running on the tool side as well as
* creating DrillerNetSessionStreams for each driller session being started.
*/
class DrillerNetworkAgentComponent
: public AZ::Component
, public TargetManagerClient::Bus::Handler
{
public:
AZ_COMPONENT(DrillerNetworkAgentComponent, "{B587A74D-6190-4149-91CB-0EA69936BD59}")
//////////////////////////////////////////////////////////////////////////
// AZ::Component
virtual void Init();
virtual void Activate();
virtual void Deactivate();
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void Reflect(AZ::ReflectContext* context);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// TargetManagerClient
virtual void TargetLeftNetwork(TargetInfo info);
//////////////////////////////////////////////////////////////////////////
protected:
//////////////////////////////////////////////////////////////////////////
// TmMsg handlers
virtual void OnRequestDrillerEnum(TmMsgPtr msg);
virtual void OnRequestDrillerStart(TmMsgPtr msg);
virtual void OnRequestDrillerStop(TmMsgPtr msg);
//////////////////////////////////////////////////////////////////////////
TmMsgCallback m_cbDrillerEnumRequest;
TmMsgCallback m_cbDrillerStartRequest;
TmMsgCallback m_cbDrillerStopRequest;
AZStd::vector<DrillerNetSessionStream*> m_activeSessions;
};
/**
* Runs on the tool machine and is responsible for communications with the
* DrillerNetworkAgent.
*/
class DrillerNetworkConsoleComponent
: public AZ::Component
, public DrillerNetworkConsoleCommandBus::Handler
, public TargetManagerClient::Bus::Handler
{
public:
AZ_COMPONENT(DrillerNetworkConsoleComponent, "{78ACADA4-F2C7-4320-8E97-59DD8B9BE33A}")
//////////////////////////////////////////////////////////////////////////
// AZ::Component
virtual void Init();
virtual void Activate();
virtual void Deactivate();
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void Reflect(AZ::ReflectContext* context);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// DrillerNetworkConsoleCommandBus
virtual void EnumerateAvailableDrillers();
virtual void StartRemoteDrillerSession(const DrillerListType& drillers, DrillerRemoteSession* handler);
virtual void StopRemoteDrillerSession(AZ::u64 sessionId);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// TargetManagerClient
virtual void DesiredTargetConnected(bool connected);
virtual void DesiredTargetChanged(AZ::u32 newTargetID, AZ::u32 oldTargetID);
//////////////////////////////////////////////////////////////////////////
protected:
//////////////////////////////////////////////////////////////////////////
// TmMsg handlers
virtual void OnReceivedDrillerEnum(TmMsgPtr msg);
//////////////////////////////////////////////////////////////////////////
typedef AZStd::vector<DrillerRemoteSession*> ActiveSessionListType;
ActiveSessionListType m_activeSessions;
TargetInfo m_curTarget;
TmMsgCallback m_cbDrillerEnum;
};
void ReflectNetDrillerClasses(AZ::ReflectContext* context);
} // namespace AzFramework
#endif // AZFRAMEWORK_REMOTE_DRILLER_INTERFACE_H
#pragma once

@ -16,11 +16,14 @@
#ifndef AZFRAMEWORK_ENTITYCONTEXTBUS_H
#define AZFRAMEWORK_ENTITYCONTEXTBUS_H
#include <AzCore/Debug/Budget.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/ComponentBus.h>
AZ_DECLARE_BUDGET(AzFramework);
namespace AZ
{
class Entity;

@ -10,6 +10,7 @@
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Debug/Budget.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzFramework/Entity/EntityOwnershipServiceBus.h>
@ -18,6 +19,8 @@ namespace AZ
class Entity;
}
AZ_DECLARE_BUDGET(AzFramework);
namespace AzFramework
{
// Types

@ -33,6 +33,8 @@ extern "C" {
# include <Lua/lauxlib.h>
}
AZ_DEFINE_BUDGET(Script);
namespace ScriptComponentCpp
{
template<typename T>

@ -239,10 +239,11 @@ namespace AzFramework
}
else if (rootSpawnableKeyType == AZ::SettingsRegistryInterface::Type::NoType)
{
AZ_Warning(
// [LYN-4146] - temporarily disabled
/*AZ_Warning(
"Spawnables", false,
"No root spawnable assigned. The root spawnable can be assigned in the Settings Registry under the key '%s'.\n",
RootSpawnableRegistryKey);
RootSpawnableRegistryKey);*/
ReleaseRootSpawnable();
}
}

@ -10,6 +10,7 @@
#define AZFRAMEWORK_TARGETMANAGEMENTAPI_H
#include <AzCore/base.h>
#include <AzCore/Debug/Budget.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/deque.h>
@ -21,6 +22,8 @@
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/Memory/OSAllocator.h>
AZ_DECLARE_BUDGET(AzFramework);
namespace AZ
{
class ReflectContext;

@ -144,6 +144,7 @@ namespace AzFramework
if (const auto& cursor = AZStd::get_if<CursorEvent>(&event))
{
m_cursorState.SetCurrentPosition(cursor->m_position);
m_cursorState.SetCaptured(cursor->m_captured);
}
else if (const auto& horizontalMotion = AZStd::get_if<HorizontalMotionEvent>(&event))
{
@ -790,17 +791,24 @@ namespace AzFramework
const auto* position = inputChannel.GetCustomData<AzFramework::InputChannel::PositionData2D>();
AZ_Assert(position, "Expected PositionData2D but found nullptr");
return CursorEvent{ ScreenPoint(
static_cast<int>(position->m_normalizedPosition.GetX() * windowSize.m_width),
static_cast<int>(position->m_normalizedPosition.GetY() * windowSize.m_height)) };
auto currentCursorState = AzFramework::SystemCursorState::Unknown;
AzFramework::InputSystemCursorRequestBus::EventResult(
currentCursorState, inputDeviceId, &AzFramework::InputSystemCursorRequestBus::Events::GetSystemCursorState);
const auto x = position->m_normalizedPosition.GetX() * aznumeric_cast<float>(windowSize.m_width);
const auto y = position->m_normalizedPosition.GetY() * aznumeric_cast<float>(windowSize.m_height);
return CursorEvent{ ScreenPoint(aznumeric_cast<int>(AZStd::lround(x)), aznumeric_cast<int>(AZStd::lround(y))),
currentCursorState == AzFramework::SystemCursorState::ConstrainedAndHidden };
}
else if (inputChannelId == InputDeviceMouse::Movement::X)
{
return HorizontalMotionEvent{ aznumeric_cast<int>(inputChannel.GetValue()) };
const auto x = inputChannel.GetValue();
return HorizontalMotionEvent{ aznumeric_cast<int>(AZStd::lround(x)) };
}
else if (inputChannelId == InputDeviceMouse::Movement::Y)
{
return VerticalMotionEvent{ aznumeric_cast<int>(inputChannel.GetValue()) };
const auto y = inputChannel.GetValue();
return VerticalMotionEvent{ aznumeric_cast<int>(AZStd::lround(y)) };
}
else if (inputChannelId == InputDeviceMouse::Movement::Z)
{

@ -88,6 +88,7 @@ namespace AzFramework
struct CursorEvent
{
ScreenPoint m_position;
bool m_captured = false;
};
struct ScrollEvent

@ -21,6 +21,8 @@ namespace AzFramework
[[nodiscard]] ScreenVector CursorDelta() const;
//! Call this in a 'handle event' call to update the most recent cursor position.
void SetCurrentPosition(const ScreenPoint& currentPosition);
//! Set whether the cursor is currently being constrained (and hidden).
void SetCaptured(bool captured);
//! Call this in an 'update' call to copy the current cursor position to the last
//! cursor position.
void Update();
@ -28,8 +30,14 @@ namespace AzFramework
private:
AZStd::optional<ScreenPoint> m_lastCursorPosition;
AZStd::optional<ScreenPoint> m_currentCursorPosition;
bool m_captured = false;
};
inline void CursorState::SetCaptured(const bool captured)
{
m_captured = captured;
}
inline void CursorState::SetCurrentPosition(const ScreenPoint& currentPosition)
{
m_currentCursorPosition = currentPosition;
@ -43,10 +51,17 @@ namespace AzFramework
}
inline void CursorState::Update()
{
if (!m_captured)
{
if (m_currentCursorPosition.has_value())
{
m_lastCursorPosition = m_currentCursorPosition;
}
}
else
{
m_currentCursorPosition = m_lastCursorPosition;
}
}
} // namespace AzFramework

@ -12,6 +12,8 @@
#include <AzFramework/Visibility/BoundsBus.h>
#include <cstring>
AZ_DECLARE_BUDGET(AzFramework);
namespace AzFramework
{
EntityVisibilityBoundsUnionSystem::EntityVisibilityBoundsUnionSystem()

@ -123,11 +123,6 @@ set(FILES
Entity/SliceGameEntityOwnershipServiceBus.h
Entity/PrefabEntityOwnershipService.h
Entity/PrefabEntityOwnershipService.cpp
Driller/RemoteDrillerInterface.cpp
Driller/RemoteDrillerInterface.h
Driller/DrillerConsoleAPI.h
Driller/DrillToFileComponent.h
Driller/DrillToFileComponent.cpp
Components/ComponentAdapter.h
Components/ComponentAdapter.inl
Components/ComponentAdapterHelpers.h

@ -38,7 +38,7 @@ TEST_F(PlatformHelperTest, SpecialAllFlag_PlatformId_Valid)
AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_ALL;
auto platforms = AzFramework::PlatformHelper::GetPlatformsInterpreted(platformFlags);
EXPECT_EQ(platforms.size(), AzFramework::NumPlatforms);
EXPECT_THAT(platforms, testing::UnorderedElementsAre("pc", "android", "ios", "mac", "provo", "salem", "jasper", "server"));
EXPECT_THAT(platforms, testing::UnorderedElementsAre("pc", "linux", "android", "ios", "mac", "provo", "salem", "jasper", "server"));
}
TEST_F(PlatformHelperTest, SpecialAllClientFlag_PlatformId_Valid)
@ -46,7 +46,7 @@ TEST_F(PlatformHelperTest, SpecialAllClientFlag_PlatformId_Valid)
AzFramework::PlatformFlags platformFlags = AzFramework::PlatformFlags::Platform_ALL_CLIENT;
auto platforms = AzFramework::PlatformHelper::GetPlatformsInterpreted(platformFlags);
EXPECT_EQ(platforms.size(), AzFramework::NumClientPlatforms);
EXPECT_THAT(platforms, testing::UnorderedElementsAre("pc", "android", "ios", "mac", "provo", "salem", "jasper"));
EXPECT_THAT(platforms, testing::UnorderedElementsAre("pc", "linux", "android", "ios", "mac", "provo", "salem", "jasper"));
}
TEST_F(PlatformHelperTest, InvalidPlatformFlags_PlatformId_Empty)
@ -102,11 +102,7 @@ TEST_F(PlatformHelperTest, AppendPlatformCodeNames_ByValidName_OK)
{
AZStd::fixed_vector<AZStd::string_view, AzFramework::MaxPlatformCodeNames> platformCodes;
AzFramework::PlatformHelper::AppendPlatformCodeNames(platformCodes, AzFramework::PlatformPC);
ASSERT_EQ(2, platformCodes.size());
AZStd::string windows = platformCodes[0];
AZStd::string linux = platformCodes[1];
EXPECT_STRCASEEQ(AzFramework::PlatformCodeNameWindows, windows.c_str());
EXPECT_STRCASEEQ(AzFramework::PlatformCodeNameLinux, linux.c_str());
EXPECT_THAT(platformCodes, testing::Pointwise(testing::Eq(), {AzFramework::PlatformCodeNameWindows}));
}
TEST_F(PlatformHelperTest, AppendPlatformCodeNames_ByInvalidName_OK)
@ -122,11 +118,7 @@ TEST_F(PlatformHelperTest, AppendPlatformCodeNames_ByValidId_OK)
{
AZStd::fixed_vector<AZStd::string_view, AzFramework::MaxPlatformCodeNames> platformCodes;
AzFramework::PlatformHelper::AppendPlatformCodeNames(platformCodes, AzFramework::PlatformId::PC);
ASSERT_EQ(2, platformCodes.size());
AZStd::string windows = platformCodes[0];
AZStd::string linux = platformCodes[1];
EXPECT_STRCASEEQ(AzFramework::PlatformCodeNameWindows, windows.c_str());
EXPECT_STRCASEEQ(AzFramework::PlatformCodeNameLinux, linux.c_str());
EXPECT_THAT(platformCodes, testing::Pointwise(testing::Eq(), {AzFramework::PlatformCodeNameWindows}));
}
TEST_F(PlatformHelperTest, AppendPlatformCodeNames_ByInvalidId_OK)

@ -11,8 +11,6 @@
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/std/string/conversions.h>
#include <AzFramework/Driller/RemoteDrillerInterface.h>
#include <AzFramework/Driller/DrillToFileComponent.h>
#include <GridMate/Drillers/CarrierDriller.h>
#include <GridMate/Drillers/ReplicaDriller.h>
#include <AzFramework/TargetManagement/TargetManagementComponent.h>
@ -37,12 +35,6 @@ namespace AzGameFramework
void GameApplication::StartCommon(AZ::Entity* systemEntity)
{
AzFramework::Application::StartCommon(systemEntity);
if (GetDrillerManager())
{
GetDrillerManager()->Register(aznew GridMate::Debug::CarrierDriller());
GetDrillerManager()->Register(aznew GridMate::Debug::ReplicaDriller());
}
}
void GameApplication::MergeSettingsToRegistry(AZ::SettingsRegistryInterface& registry)
@ -92,10 +84,6 @@ namespace AzGameFramework
components.emplace_back(azrtti_typeid<AzFramework::TargetManagementComponent>());
#endif
// Note that this component is registered by AzFramework.
// It must be registered here instead of in the module so that existence of AzFrameworkModule is guaranteed.
components.emplace_back(azrtti_typeid<AzFramework::DrillerNetworkAgentComponent>());
return components;
}
@ -104,9 +92,6 @@ namespace AzGameFramework
AzFramework::Application::CreateStaticModules(outModules);
outModules.emplace_back(aznew AzGameFrameworkModule());
// have to let the metrics system know that it's ok to send back the name of the DrillerNetworkAgentComponent to Amazon as plain text, without hashing
EBUS_EVENT(AzFramework::MetricsPlainTextNameRegistrationBus, RegisterForNameSending, AZStd::vector<AZ::Uuid>{ azrtti_typeid<AzFramework::DrillerNetworkAgentComponent>() });
}
void GameApplication::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const

@ -7,24 +7,15 @@
*/
#include <AzGameFramework/AzGameFrameworkModule.h>
// Component includes
#include <AzFramework/Driller/RemoteDrillerInterface.h>
#include <AzFramework/Driller/DrillToFileComponent.h>
namespace AzGameFramework
{
AzGameFrameworkModule::AzGameFrameworkModule()
: AZ::Module()
{
m_descriptors.insert(m_descriptors.end(), {
AzFramework::DrillToFileComponent::CreateDescriptor(),
});
}
AZ::ComponentTypeList AzGameFrameworkModule::GetRequiredSystemComponents() const
{
return AZ::ComponentTypeList{
azrtti_typeid<AzFramework::DrillToFileComponent>(),
};
return {};
}
}

@ -122,7 +122,7 @@ namespace AzManipulatorTestFramework
MouseInteractionEvent CreateMouseInteractionEvent(const MouseInteraction& mouseInteraction, MouseEvent event)
{
return MouseInteractionEvent(mouseInteraction, event);
return MouseInteractionEvent(mouseInteraction, event, /*captured=*/false);
}
void DispatchMouseInteractionEvent(const MouseInteractionEvent& event)

@ -312,8 +312,7 @@ namespace AzToolsFramework
#elif defined(AZ_PLATFORM_WINDOWS)
return "pc";
#elif defined(AZ_PLATFORM_LINUX)
// set this to pc because that's what bootstrap.cfg currently defines the platform to "pc", even on Linux
return "pc";
return "linux";
#else
#error Unimplemented Host Asset Platform
#endif

@ -5,14 +5,10 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZTOOLSFRAMEWORK_TOOLSAPPLICATIONAPI_H
#define AZTOOLSFRAMEWORK_TOOLSAPPLICATIONAPI_H
#include <AzCore/base.h>
#pragma once
#include <AzCore/base.h>
#include <AzCore/Debug/Budget.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Uuid.h>
@ -1089,4 +1085,5 @@ namespace AzToolsFramework
}
} // namespace AzToolsFramework
#endif // AZTOOLSFRAMEWORK_TOOLSAPPLICATIONAPI_H
AZ_DECLARE_BUDGET(AzToolsFramework);

@ -52,6 +52,8 @@
#include <AzToolsFramework/AssetBrowser/AssetBrowserComponent.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h>
AZ_DEFINE_BUDGET(AzToolsFramework);
namespace AzToolsFramework
{
AzToolsFrameworkModule::AzToolsFrameworkModule()

@ -158,6 +158,16 @@ namespace AzToolsFramework
SetImplementation(nullptr);
}
void QtEventToAzInputMapper::EditorQtMouseDevice::SetSystemCursorState(const AzFramework::SystemCursorState systemCursorState)
{
m_systemCursorState = systemCursorState;
}
AzFramework::SystemCursorState QtEventToAzInputMapper::EditorQtMouseDevice::GetSystemCursorState() const
{
return m_systemCursorState;
}
QtEventToAzInputMapper::QtEventToAzInputMapper(QWidget* sourceWidget, int syntheticDeviceId)
: QObject(sourceWidget)
, m_sourceWidget(sourceWidget)
@ -210,12 +220,15 @@ namespace AzToolsFramework
if (m_capturingCursor != enabled)
{
m_capturingCursor = enabled;
if (m_capturingCursor)
{
m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::ConstrainedAndHidden);
qApp->setOverrideCursor(Qt::BlankCursor);
}
else
{
m_mouseDevice->SetSystemCursorState(AzFramework::SystemCursorState::UnconstrainedAndVisible);
qApp->restoreOverrideCursor();
}
}
@ -238,10 +251,22 @@ namespace AzToolsFramework
return false;
}
// If our focus changes, go ahead and reset all input devices.
if (eventType == QEvent::FocusIn || eventType == QEvent::FocusOut)
{
// If our focus changes, go ahead and reset all input devices.
HandleFocusChange(event);
// If we focus in on the source widget and the mouse is contained in its
// bounds, refresh the cached cursor position to ensure it is up to date (this
// ensures cursor positions are refreshed correctly with context menu focus changes)
if (eventType == QEvent::FocusIn)
{
const auto widgetCursorPosition = m_sourceWidget->mapFromGlobal(QCursor::pos());
if (m_sourceWidget->geometry().contains(widgetCursorPosition))
{
HandleMouseMoveEvent(widgetCursorPosition);
}
}
}
// Map key events to input channels.
// ShortcutOverride is used in lieu of KeyPress for high priority input channels like Alt
@ -249,7 +274,7 @@ namespace AzToolsFramework
else if (
eventType == QEvent::Type::KeyPress || eventType == QEvent::Type::KeyRelease || eventType == QEvent::Type::ShortcutOverride)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
auto keyEvent = static_cast<QKeyEvent*>(event);
HandleKeyEvent(keyEvent);
}
// Map mouse events to input channels.
@ -257,20 +282,20 @@ namespace AzToolsFramework
eventType == QEvent::Type::MouseButtonPress || eventType == QEvent::Type::MouseButtonRelease ||
eventType == QEvent::Type::MouseButtonDblClick)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
auto mouseEvent = static_cast<QMouseEvent*>(event);
HandleMouseButtonEvent(mouseEvent);
}
// Map mouse movement to the movement input channels.
// This includes SystemCursorPosition alongside Movement::X and Movement::Y.
else if (eventType == QEvent::Type::MouseMove)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
HandleMouseMoveEvent(mouseEvent);
auto mouseEvent = static_cast<QMouseEvent*>(event);
HandleMouseMoveEvent(mouseEvent->pos());
}
// Map wheel events to the mouse Z movement channel.
else if (eventType == QEvent::Type::Wheel)
{
QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event);
auto wheelEvent = static_cast<QWheelEvent*>(event);
HandleWheelEvent(wheelEvent);
}
@ -345,9 +370,8 @@ namespace AzToolsFramework
return QPoint{ denormalizedX, denormalizedY };
}
void QtEventToAzInputMapper::HandleMouseMoveEvent(QMouseEvent* mouseEvent)
void QtEventToAzInputMapper::HandleMouseMoveEvent(const QPoint& cursorPosition)
{
const QPoint cursorPosition = mouseEvent->pos();
const QPoint cursorDelta = cursorPosition - m_previousCursorPosition;
m_mouseDevice->m_cursorPositionData2D->m_normalizedPosition = WidgetPositionToNormalizedPosition(cursorPosition);
@ -357,18 +381,15 @@ namespace AzToolsFramework
if (m_capturingCursor)
{
// Reset our cursor position to the previous point.
const QPoint targetScreenPosition = m_sourceWidget->mapToGlobal(m_previousCursorPosition);
AzQtComponents::SetCursorPos(targetScreenPosition);
// Even though we just set the cursor position, there are edge cases such as remote desktop that will leave
// the cursor position unchanged. For safety, we re-cache our last cursor position for delta generation.
const QPoint actualWidgetPosition = m_sourceWidget->mapFromGlobal(QCursor::pos());
m_mouseDevice->m_cursorPositionData2D->m_normalizedPosition = WidgetPositionToNormalizedPosition(actualWidgetPosition);
// Reset our cursor position to the previous point
const QPoint screenCursorPosition = m_sourceWidget->mapToGlobal(m_previousCursorPosition);
AzQtComponents::SetCursorPos(screenCursorPosition);
}
else
{
m_previousCursorPosition = cursorPosition;
}
}
void QtEventToAzInputMapper::HandleKeyEvent(QKeyEvent* keyEvent)
{

@ -105,7 +105,14 @@ namespace AzToolsFramework
public:
EditorQtMouseDevice(AzFramework::InputDeviceId id);
// AzFramework::InputDeviceMouse overrides ...
void SetSystemCursorState(AzFramework::SystemCursorState systemCursorState) override;
AzFramework::SystemCursorState GetSystemCursorState() const override;
friend class QtEventToAzInputMapper;
private:
AzFramework::SystemCursorState m_systemCursorState = AzFramework::SystemCursorState::UnconstrainedAndVisible;
};
// Emits InputChannelUpdated if channel has transitioned in state (i.e. has gone from active to inactive or vice versa).
@ -122,7 +129,7 @@ namespace AzToolsFramework
// Handle mouse click events.
void HandleMouseButtonEvent(QMouseEvent* mouseEvent);
// Handle mouse move events.
void HandleMouseMoveEvent(QMouseEvent* mouseEvent);
void HandleMouseMoveEvent(const QPoint& cursorPosition);
// Handles key press / release events (or ShortcutOverride events for keys listed in m_highPriorityKeys).
void HandleKeyEvent(QKeyEvent* keyEvent);
// Handles mouse wheel events.

@ -253,6 +253,15 @@ namespace AzToolsFramework
settings.m_metadata.Add(&entityIdMapper);
settings.m_metadata.Create<InstanceEntityScrubber>(newlyAddedEntities);
AZStd::string scratchBuffer;
auto issueReportingCallback = [&scratchBuffer](
AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result,
AZStd::string_view path) -> AZ::JsonSerializationResult::ResultCode
{
return Internal::JsonIssueReporter(scratchBuffer, message, result, path);
};
settings.m_reporting = AZStd::move(issueReportingCallback);
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(instance, prefabDom, settings);
AZ::Data::AssetManager::Instance().ResumeAssetRelease();

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

Loading…
Cancel
Save