diff --git a/Assets/Engine/SeedAssetList.seed b/Assets/Engine/SeedAssetList.seed
index 77ec509721..579fd3c444 100644
--- a/Assets/Engine/SeedAssetList.seed
+++ b/Assets/Engine/SeedAssetList.seed
@@ -64,30 +64,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -160,686 +136,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1384,166 +680,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1632,46 +768,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/AutomatedTesting/AutomatedTesting_Dependencies.xml b/AutomatedTesting/AutomatedTesting_Dependencies.xml
index 50a5caea73..98e00a2914 100644
--- a/AutomatedTesting/AutomatedTesting_Dependencies.xml
+++ b/AutomatedTesting/AutomatedTesting_Dependencies.xml
@@ -1,5 +1,4 @@
-
\ No newline at end of file
diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py
index 249b9c7096..3403d8e9b1 100644
--- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py
+++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py
@@ -226,9 +226,9 @@ class TestMaterialEditor(object):
self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name, cfg_args):
"""
Tests each valid RHI option (Null RHI excluded) can be launched with the MaterialEditor.
- Checks for the "Finished loading viewport configurtions." success message post lounch.
+ Checks for the "Finished loading viewport configurations." success message post launch.
"""
- expected_lines = ["Finished loading viewport configurtions."]
+ expected_lines = ["Finished loading viewport configurations."]
unexpected_lines = [
# "Trace::Assert",
# "Trace::Error",
@@ -241,7 +241,7 @@ class TestMaterialEditor(object):
generic_launcher,
editor_script="",
run_python="--runpython",
- timeout=30,
+ timeout=60,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=False,
diff --git a/AutomatedTesting/Gem/PythonTests/prefab/Prefab.py b/AutomatedTesting/Gem/PythonTests/prefab/Prefab.py
index 14a1ab62f0..9b4d2d1393 100644
--- a/AutomatedTesting/Gem/PythonTests/prefab/Prefab.py
+++ b/AutomatedTesting/Gem/PythonTests/prefab/Prefab.py
@@ -25,28 +25,39 @@ import prefab.Prefab_Test_Utils as prefab_test_utils
# This is a helper class which contains some of the useful information about a prefab instance.
class PrefabInstance:
- def __init__(self, name: str=None, prefab_file_name: str=None, container_entity: EditorEntity=EntityId()):
- self.name = name
+ def __init__(self, prefab_file_name: str=None, container_entity: EditorEntity=EntityId()):
self.prefab_file_name: str = prefab_file_name
self.container_entity: EditorEntity = container_entity
+ def __eq__(self, other):
+ return other and self.container_entity.id == other.container_entity.id
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return hash(self.container_entity.id)
+
"""
See if this instance is valid to be used with other prefab operations.
:return: Whether the target instance is valid or not.
"""
def is_valid() -> bool:
- return self.container_entity.id.IsValid() and self.name is not None and self.prefab_file_name in Prefab.existing_prefabs
+ return self.container_entity.id.IsValid() and self.prefab_file_name in Prefab.existing_prefabs
-
"""
Reparent this instance to target parent entity.
The function will also check pop up dialog ui in editor to see if there's prefab cyclical dependency error while reparenting prefabs.
:param parent_entity_id: The id of the entity this instance should be a child of in the transform hierarchy next.
"""
async def ui_reparent_prefab_instance(self, parent_entity_id: EntityId):
- container_entity_name = self.container_entity.get_name()
- current_children_entity_ids_having_prefab_name = prefab_test_utils.get_children_ids_by_name(parent_entity_id, container_entity_name)
- Report.info(f'current_children_entity_ids_having_prefab_name: {current_children_entity_ids_having_prefab_name}')
+ container_entity_id_before_reparent = self.container_entity.id
+
+ original_parent = EditorEntity(self.container_entity.get_parent_id())
+ original_parent_before_reparent_children_ids = set(original_parent.get_children_ids())
+
+ new_parent = EditorEntity(parent_entity_id)
+ new_parent_before_reparent_children_ids = set(new_parent.get_children_ids())
pyside_utils.run_soon(lambda: self.container_entity.set_parent_entity(parent_entity_id))
pyside_utils.run_soon(lambda: prefab_test_utils.wait_for_propagation())
@@ -60,18 +71,23 @@ class PrefabInstance:
except pyside_utils.EventLoopTimeoutException:
pass
- updated_children_entity_ids_having_prefab_name = prefab_test_utils.get_children_ids_by_name(parent_entity_id, container_entity_name)
- Report.info(f'updated_children_entity_ids_having_prefab_name: {updated_children_entity_ids_having_prefab_name}')
- new_child_with_reparented_prefab_name_added = len(updated_children_entity_ids_having_prefab_name) == len(current_children_entity_ids_having_prefab_name) + 1
- assert new_child_with_reparented_prefab_name_added, "No entity with reparented prefab name become a child of target parent entity"
+ original_parent_after_reparent_children_ids = set(original_parent.get_children_ids())
+ assert len(original_parent_after_reparent_children_ids) == len(original_parent_before_reparent_children_ids) - 1, \
+ "The children count of the Prefab Instance's original parent should be decreased by 1."
+ assert not container_entity_id_before_reparent in original_parent_after_reparent_children_ids, \
+ "This Prefab Instance is still a child entity of its original parent entity."
+
+ new_parent_after_reparent_children_ids = set(new_parent.get_children_ids())
+ assert len(new_parent_after_reparent_children_ids) == len(new_parent_before_reparent_children_ids) + 1, \
+ "The children count of the Prefab Instance's new parent should be increased by 1."
- updated_container_entity_id = set(updated_children_entity_ids_having_prefab_name).difference(current_children_entity_ids_having_prefab_name).pop()
- updated_container_entity = EditorEntity(updated_container_entity_id)
- updated_container_entity_parent_id = updated_container_entity.get_parent_id()
- has_correct_parent = updated_container_entity_parent_id.ToString() == parent_entity_id.ToString()
- assert has_correct_parent, "Prefab reparented is *not* under the expected parent entity"
+ container_entity_id_after_reparent = set(new_parent_after_reparent_children_ids).difference(new_parent_before_reparent_children_ids).pop()
+ reparented_container_entity = EditorEntity(container_entity_id_after_reparent)
+ reparented_container_entity_parent_id = reparented_container_entity.get_parent_id()
+ has_correct_parent = reparented_container_entity_parent_id.ToString() == parent_entity_id.ToString()
+ assert has_correct_parent, "Prefab Instance reparented is *not* under the expected parent entity"
- self.container_entity = EditorEntity(updated_container_entity_id)
+ self.container_entity = reparented_container_entity
# This is a helper class which contains some of the useful information about a prefab template.
class Prefab:
@@ -81,7 +97,7 @@ class Prefab:
def __init__(self, file_name: str):
self.file_name:str = file_name
self.file_path: str = prefab_test_utils.get_prefab_file_path(file_name)
- self.instances: dict = {}
+ self.instances: set[PrefabInstance] = set()
"""
Check if a prefab is ready to be used to generate its instances.
@@ -122,10 +138,10 @@ class Prefab:
:param entities: The entities that should form the new prefab (along with their descendants).
:param file_name: A unique file name of new prefab.
:param prefab_instance_name: A name for the very first instance generated while prefab creation. The default instance name is the same as file_name.
- :return: An outcome object with an entityId of the new prefab's container entity; on failure, it comes with an error message detailing the cause of the error.
+ :return: Created Prefab object and the very first PrefabInstance object owned by the prefab.
"""
@classmethod
- def create_prefab(cls, entities: list[EditorEntity], file_name: str, prefab_instance_name: str=None) -> Prefab:
+ def create_prefab(cls, entities: list[EditorEntity], file_name: str, prefab_instance_name: str=None) -> (Prefab, PrefabInstance):
assert not Prefab.is_prefab_loaded(file_name), f"Can't create Prefab '{file_name}' since the prefab already exists"
new_prefab = Prefab(file_name)
@@ -133,18 +149,18 @@ class Prefab:
create_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'CreatePrefabInMemory', entity_ids, new_prefab.file_path)
assert create_prefab_result.IsSuccess(), f"Prefab operation 'CreatePrefab' failed. Error: {create_prefab_result.GetError()}"
- container_entity = EditorEntity(create_prefab_result.GetValue())
+ container_entity_id = create_prefab_result.GetValue()
+ container_entity = EditorEntity(container_entity_id)
if prefab_instance_name:
container_entity.set_name(prefab_instance_name)
- else:
- prefab_instance_name = file_name
prefab_test_utils.wait_for_propagation()
- container_entity_id = prefab_test_utils.find_entity_by_unique_name(prefab_instance_name)
- new_prefab.instances[prefab_instance_name] = PrefabInstance(prefab_instance_name, file_name, EditorEntity(container_entity_id))
+
+ new_prefab_instance = PrefabInstance(file_name, EditorEntity(container_entity_id))
+ new_prefab.instances.add(new_prefab_instance)
Prefab.existing_prefabs[file_name] = new_prefab
- return new_prefab
+ return new_prefab, new_prefab_instance
"""
Remove target prefab instances.
@@ -152,22 +168,15 @@ class Prefab:
"""
@classmethod
def remove_prefabs(cls, prefab_instances: list[PrefabInstance]):
- instances_to_remove_name_counts = Counter()
- instances_removed_expected_name_counts = Counter()
-
- entities_to_remove = [prefab_instance.container_entity for prefab_instance in prefab_instances]
- while entities_to_remove:
- entity = entities_to_remove.pop(-1)
- entity_name = entity.get_name()
- instances_to_remove_name_counts[entity_name] += 1
-
+ entity_ids_to_remove = []
+ entity_id_queue = [prefab_instance.container_entity for prefab_instance in prefab_instances]
+ while entity_id_queue:
+ entity = entity_id_queue.pop(0)
children_entity_ids = entity.get_children_ids()
for child_entity_id in children_entity_ids:
- entities_to_remove.append(EditorEntity(child_entity_id))
+ entity_id_queue.append(EditorEntity(child_entity_id))
- for entity_name, entity_count in instances_to_remove_name_counts.items():
- entities = prefab_test_utils.find_entities_by_name(entity_name)
- instances_removed_expected_name_counts[entity_name] = len(entities) - entity_count
+ entity_ids_to_remove.append(entity.id)
container_entity_ids = [prefab_instance.container_entity.id for prefab_instance in prefab_instances]
delete_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'DeleteEntitiesAndAllDescendantsInInstance', container_entity_ids)
@@ -175,28 +184,24 @@ class Prefab:
prefab_test_utils.wait_for_propagation()
- prefab_entities_deleted = True
- for entity_name, expected_entity_count in instances_removed_expected_name_counts.items():
- actual_entity_count = len(prefab_test_utils.find_entities_by_name(entity_name))
- if actual_entity_count is not expected_entity_count:
- prefab_entities_deleted = False
- break
-
- assert prefab_entities_deleted, "Not all entities and descendants in target prefabs are deleted."
+ entity_ids_after_delete = set(prefab_test_utils.get_all_entities())
+ for entity_id_removed in entity_ids_to_remove:
+ if entity_id_removed in entity_ids_after_delete:
+ assert prefab_entities_deleted, "Not all entities and descendants in target prefabs are deleted."
for instance in prefab_instances:
instance_deleted_prefab = Prefab.get_prefab(instance.prefab_file_name)
- instance_deleted_prefab.instances.pop(instance.name)
+ instance_deleted_prefab.instances.remove(instance)
instance = PrefabInstance()
"""
Instantiate an instance of this prefab.
- :param name: A name for newly instantiated prefab instance. The default instance name is the same as this prefab's file name.
:param parent_entity: The entity the prefab should be a child of in the transform hierarchy.
+ :param name: A name for newly instantiated prefab instance. The default instance name is the same as this prefab's file name.
:param prefab_position: The position in world space the prefab should be instantiated in.
- :return: An outcome object with an entityId of the new prefab's container entity; on failure, it comes with an error message detailing the cause of the error.
+ :return: Instantiated PrefabInstance object owned by this prefab.
"""
- def instantiate(self, name: str=None, parent_entity: EditorEntity=None, prefab_position: Vector3=Vector3()) -> PrefabInstance:
+ def instantiate(self, parent_entity: EditorEntity=None, name: str=None, prefab_position: Vector3=Vector3()) -> PrefabInstance:
parent_entity_id = parent_entity.id if parent_entity is not None else EntityId()
instantiate_prefab_result = prefab.PrefabPublicRequestBus(
@@ -209,13 +214,13 @@ class Prefab:
if name:
container_entity.set_name(name)
- else:
- name = self.file_name
prefab_test_utils.wait_for_propagation()
- container_entity_id = prefab_test_utils.find_entity_by_unique_name(name)
- self.instances[name] = PrefabInstance(name, self.file_name, EditorEntity(container_entity_id))
+
+ new_prefab_instance = PrefabInstance(self.file_name, EditorEntity(container_entity_id))
+ assert not new_prefab_instance in self.instances, "This prefab instance is already existed before this instantiation."
+ self.instances.add(new_prefab_instance)
prefab_test_utils.check_entity_at_position(container_entity_id, prefab_position)
- return container_entity_id
+ return new_prefab_instance
diff --git a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndDeletePrefab.py b/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndDeletePrefab.py
index c6e0daa4dd..f3fbcfa6ad 100644
--- a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndDeletePrefab.py
+++ b/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndDeletePrefab.py
@@ -22,11 +22,10 @@ def Prefab_BasicWorkflow_CreateAndDeletePrefab():
car_prefab_entities = [car_entity]
# Checks for prefab creation passed or not
- car_prefab = Prefab.create_prefab(
+ _, car = Prefab.create_prefab(
car_prefab_entities, CAR_PREFAB_FILE_NAME)
# Checks for prefab deletion passed or not
- car = car_prefab.instances[CAR_PREFAB_FILE_NAME]
Prefab.remove_prefabs([car])
if __name__ == "__main__":
diff --git a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndReparentPrefab.py b/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndReparentPrefab.py
index 04f7b97628..e5a9d9930a 100644
--- a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndReparentPrefab.py
+++ b/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_CreateAndReparentPrefab.py
@@ -28,7 +28,7 @@ def Prefab_BasicWorkflow_CreateAndReparentPrefab():
car_prefab_entities = [car_entity]
# Checks for prefab creation passed or not
- car_prefab = Prefab.create_prefab(
+ _, car = Prefab.create_prefab(
car_prefab_entities, CAR_PREFAB_FILE_NAME)
# Creates another new Entity at the root level
@@ -36,12 +36,10 @@ def Prefab_BasicWorkflow_CreateAndReparentPrefab():
wheel_prefab_entities = [wheel_entity]
# Checks for wheel prefab creation passed or not
- wheel_prefab = Prefab.create_prefab(
+ _, wheel = Prefab.create_prefab(
wheel_prefab_entities, WHEEL_PREFAB_FILE_NAME)
# Checks for prefab reparenting passed or not
- car = car_prefab.instances[CAR_PREFAB_FILE_NAME]
- wheel = wheel_prefab.instances[WHEEL_PREFAB_FILE_NAME]
await wheel.ui_reparent_prefab_instance(car.container_entity.id)
run_test()
diff --git a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_InstantiatePrefab.py b/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_InstantiatePrefab.py
index b9015ab556..46be669697 100644
--- a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_InstantiatePrefab.py
+++ b/AutomatedTesting/Gem/PythonTests/prefab/Prefab_BasicWorkflow_InstantiatePrefab.py
@@ -22,11 +22,11 @@ def Prefab_BasicWorkflow_InstantiatePrefab():
# Checks for prefab instantiation passed or not
test_prefab = Prefab.get_prefab(EXISTING_TEST_PREFAB_FILE_NAME)
- instantiated_test_container_entity_id = test_prefab.instantiate(
+ test_instance = test_prefab.instantiate(
prefab_position=INSTANTIATED_TEST_PREFAB_POSITION)
prefab_test_utils.check_entity_children_count(
- instantiated_test_container_entity_id,
+ test_instance.container_entity.id,
EXPECTED_TEST_PREFAB_CHILDREN_COUNT)
if __name__ == "__main__":
diff --git a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_Test_Utils.py b/AutomatedTesting/Gem/PythonTests/prefab/Prefab_Test_Utils.py
index 8cd59ed077..3e19911449 100644
--- a/AutomatedTesting/Gem/PythonTests/prefab/Prefab_Test_Utils.py
+++ b/AutomatedTesting/Gem/PythonTests/prefab/Prefab_Test_Utils.py
@@ -29,20 +29,8 @@ def find_entities_by_name(entity_name):
searchFilter.names = [entity_name]
return entity.SearchBus(bus.Broadcast, 'SearchEntities', searchFilter)
-def find_entity_by_unique_name(entity_name):
- unique_name_entity_found_result = (
- "Entity with a unique name found",
- "Entity with a unique name *not* found")
-
- entities = find_entities_by_name(entity_name)
- unique_name_entity_found = len(entities) == 1
- Report.result(unique_name_entity_found_result, unique_name_entity_found)
-
- if unique_name_entity_found:
- return entities[0]
- else:
- Report.info(f"{len(entities)} entities with name '{entity_name}' found")
- return EntityId()
+def get_all_entities():
+ return entity.SearchBus(bus.Broadcast, 'SearchEntities', entity.SearchFilter())
def check_entity_at_position(entity_id, expected_entity_position):
entity_at_expected_position_result = (
diff --git a/Code/Editor/Include/IDisplayViewport.h b/Code/Editor/Include/IDisplayViewport.h
index 813c54355b..c7dff33e50 100644
--- a/Code/Editor/Include/IDisplayViewport.h
+++ b/Code/Editor/Include/IDisplayViewport.h
@@ -35,8 +35,6 @@ struct IDisplayViewport
*/
virtual float GetDistanceToLine(const Vec3& lineP1, const Vec3& lineP2, const QPoint& point) const = 0;
- virtual CBaseObjectsCache* GetVisibleObjectsCache() = 0;
-
enum EAxis
{
AXIS_NONE,
diff --git a/Code/Editor/Objects/ObjectManager.cpp b/Code/Editor/Objects/ObjectManager.cpp
index 06aa8866b3..f67cf24553 100644
--- a/Code/Editor/Objects/ObjectManager.cpp
+++ b/Code/Editor/Objects/ObjectManager.cpp
@@ -33,10 +33,6 @@
AZ_CVAR_EXTERNED(bool, ed_visibility_logTiming);
-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.
@@ -76,17 +72,6 @@ public:
int GameCreationOrder() override { return superType->GameCreationOrder(); };
};
-void CBaseObjectsCache::AddObject(CBaseObject* object)
-{
- m_objects.push_back(object);
- if (object->GetType() == OBJTYPE_AZENTITY)
- {
- auto componentEntityObject = static_cast(object);
- m_entityIds.push_back(componentEntityObject->GetAssociatedEntityId());
- }
-}
-
-
//////////////////////////////////////////////////////////////////////////
// CObjectManager implementation.
//////////////////////////////////////////////////////////////////////////
@@ -1267,25 +1252,8 @@ void CObjectManager::Display(DisplayContext& dc)
UpdateVisibilityList();
}
- bool viewIsDirty = dc.settings->IsDisplayHelpers(); // displaying helpers require computing all the bound boxes and things anyway.
-
- if (!viewIsDirty)
+ if (dc.settings->IsDisplayHelpers())
{
- if (CBaseObjectsCache* cache = dc.view->GetVisibleObjectsCache())
- {
- // if the current rendering viewport has an out-of-date cache serial number, it needs to be refreshed too.
- // views set their cache empty when they indicate they need to force a refresh.
- if ((cache->GetObjectCount() == 0) || (cache->GetSerialNumber() != m_visibilitySerialNumber))
- {
- viewIsDirty = true;
- }
- }
- }
-
- if (viewIsDirty)
- {
- FindDisplayableObjects(dc, true); // this also actually draws the helpers.
-
// Also broadcast for anyone else that needs to draw global debug to do so now
AzFramework::DebugDisplayEventBus::Broadcast(&AzFramework::DebugDisplayEvents::DrawGlobalDebugInfo);
}
@@ -1296,94 +1264,14 @@ void CObjectManager::Display(DisplayContext& dc)
}
}
-void CObjectManager::ForceUpdateVisibleObjectCache(DisplayContext& dc)
+void CObjectManager::ForceUpdateVisibleObjectCache([[maybe_unused]] DisplayContext& dc)
{
- FindDisplayableObjects(dc, false);
+ AZ_Assert(false, "CObjectManager::ForceUpdateVisibleObjectCache is legacy/deprecated and should not be used.");
}
-void CObjectManager::FindDisplayableObjects(DisplayContext& dc, [[maybe_unused]] bool bDisplay)
+void CObjectManager::FindDisplayableObjects([[maybe_unused]] DisplayContext& dc, [[maybe_unused]] bool bDisplay)
{
- // if the new IVisibilitySystem is being used, do not run this logic
- if (ed_visibility_use)
- {
- return;
- }
-
- AZ_PROFILE_FUNCTION(Editor);
-
- auto start = std::chrono::steady_clock::now();
- CBaseObjectsCache* pDispayedViewObjects = dc.view->GetVisibleObjectsCache();
- if (!pDispayedViewObjects)
- {
- return;
- }
-
- pDispayedViewObjects->SetSerialNumber(m_visibilitySerialNumber); // update viewport to be latest serial number
-
- AABB bbox;
- bbox.min.zero();
- bbox.max.zero();
-
- pDispayedViewObjects->ClearObjects();
- pDispayedViewObjects->Reserve(static_cast(m_visibleObjects.size()));
-
- if (dc.flags & DISPLAY_2D)
- {
- int numVis = static_cast(m_visibleObjects.size());
- for (int i = 0; i < numVis; i++)
- {
- CBaseObject* obj = m_visibleObjects[i];
-
- obj->GetBoundBox(bbox);
- if (dc.box.IsIntersectBox(bbox))
- {
- pDispayedViewObjects->AddObject(obj);
- }
- }
- }
- else
- {
- CSelectionGroup* pSelection = GetSelection();
- if (pSelection && pSelection->GetCount() > 1)
- {
- AABB mergedAABB;
- mergedAABB.Reset();
- for (int i = 0, iCount(pSelection->GetCount()); i < iCount; ++i)
- {
- CBaseObject* pObj(pSelection->GetObject(i));
- if (pObj == nullptr)
- {
- continue;
- }
- AABB aabb;
- pObj->GetBoundBox(aabb);
- mergedAABB.Add(aabb);
- }
-
- pSelection->GetObject(0)->CBaseObject::DrawDimensions(dc, &mergedAABB);
- }
-
- int numVis = static_cast(m_visibleObjects.size());
- for (int i = 0; i < numVis; i++)
- {
- CBaseObject* obj = m_visibleObjects[i];
-
- if (obj)
- {
- if ((dc.flags & DISPLAY_SELECTION_HELPERS) || obj->IsSelected())
- {
- pDispayedViewObjects->AddObject(obj);
- }
- }
- }
- }
-
- if (ed_visibility_logTiming && !ed_visibility_use)
- {
- auto stop = std::chrono::steady_clock::now();
- std::chrono::duration diff = stop - start;
- AZ_Printf("Visibility", "FindDisplayableObjects (old) - Duration: %f", diff);
- }
+ AZ_Assert(false, "CObjectManager::FindDisplayableObjects is legacy/deprecated and should not be used.");
}
void CObjectManager::BeginEditParams(CBaseObject* obj, int flags)
@@ -1630,214 +1518,24 @@ bool CObjectManager::HitTestObject(CBaseObject* obj, HitContext& hc)
return (bSelectionHelperHit || obj->HitTest(hc));
}
-
//////////////////////////////////////////////////////////////////////////
-bool CObjectManager::HitTest(HitContext& hitInfo)
+bool CObjectManager::HitTest([[maybe_unused]] HitContext& hitInfo)
{
- AZ_PROFILE_FUNCTION(Editor);
-
- hitInfo.object = nullptr;
- hitInfo.dist = FLT_MAX;
- hitInfo.axis = 0;
- hitInfo.manipulatorMode = 0;
-
- HitContext hcOrg = hitInfo;
- if (hcOrg.view)
- {
- hcOrg.view->GetPerpendicularAxis(nullptr, &hcOrg.b2DViewport);
- }
- hcOrg.rayDir = hcOrg.rayDir.GetNormalized();
-
- HitContext hc = hcOrg;
-
- float mindist = FLT_MAX;
-
- if (!hitInfo.bIgnoreAxis && !hc.bUseSelectionHelpers)
- {
- // Test gizmos.
- if (m_gizmoManager->HitTest(hc))
- {
- if (hc.axis != 0)
- {
- hitInfo.object = hc.object;
- hitInfo.gizmo = hc.gizmo;
- hitInfo.axis = hc.axis;
- hitInfo.manipulatorMode = hc.manipulatorMode;
- hitInfo.dist = hc.dist;
- return true;
- }
- }
- }
-
- if (hitInfo.bOnlyGizmo)
- {
- return false;
- }
-
- // Only HitTest objects, that where previously Displayed.
- CBaseObjectsCache* pDispayedViewObjects = hitInfo.view->GetVisibleObjectsCache();
-
- const bool iconsPrioritized = true; // Force icons to always be prioritized over other things you hit. Can change to be a configurable option in the future.
-
- CBaseObject* selected = nullptr;
- const char* name = nullptr;
- bool iconHit = false;
- int numVis = pDispayedViewObjects->GetObjectCount();
- for (int i = 0; i < numVis; i++)
- {
- CBaseObject* obj = pDispayedViewObjects->GetObject(i);
-
- if (obj == hitInfo.pExcludedObject)
- {
- continue;
- }
-
- if (HitTestObject(obj, hc))
- {
- if (m_selectCallback && !m_selectCallback->CanSelectObject(obj))
- {
- continue;
- }
-
- // Check if this object is nearest.
- if (hc.axis != 0)
- {
- hitInfo.object = obj;
- hitInfo.axis = hc.axis;
- hitInfo.dist = hc.dist;
- return true;
- }
-
- // When prioritizing icons, we don't allow non-icon hits to beat icon hits
- if (iconsPrioritized && iconHit && !hc.iconHit)
- {
- continue;
- }
-
- if (hc.dist < mindist || (!iconHit && hc.iconHit))
- {
- if (hc.iconHit)
- {
- iconHit = true;
- }
-
- mindist = hc.dist;
- name = hc.name;
- selected = obj;
- }
-
- // Clear the object pointer if an object was hit, not just if the collision
- // was closer than any previous. Not all paths from HitTestObject set the object pointer and so you could get
- // an object from a previous (rejected) result but with collision information about a closer hit.
- hc.object = nullptr;
- hc.iconHit = false;
-
- // If use deep selection
- if (hitInfo.pDeepSelection)
- {
- hitInfo.pDeepSelection->AddObject(hc.dist, obj);
- }
- }
- }
-
- if (selected)
- {
- hitInfo.object = selected;
- hitInfo.dist = mindist;
- hitInfo.name = name;
- hitInfo.iconHit = iconHit;
- return true;
- }
+ AZ_Assert(false, "CObjectManager::HitTest is legacy/deprecated and should not be used.");
return false;
}
-void CObjectManager::FindObjectsInRect(CViewport* view, const QRect& rect, std::vector& guids)
+
+void CObjectManager::FindObjectsInRect(
+ [[maybe_unused]] CViewport* view, [[maybe_unused]] const QRect& rect, [[maybe_unused]] std::vector& guids)
{
- AZ_PROFILE_FUNCTION(Editor);
-
- if (rect.width() < 1 || rect.height() < 1)
- {
- return;
- }
-
- HitContext hc;
- hc.view = view;
- hc.b2DViewport = view->GetType() != ET_ViewportCamera;
- hc.rect = rect;
- hc.bUseSelectionHelpers = view->GetAdvancedSelectModeFlag();
-
- guids.clear();
-
- CBaseObjectsCache* pDispayedViewObjects = view->GetVisibleObjectsCache();
-
- int numVis = pDispayedViewObjects->GetObjectCount();
- for (int i = 0; i < numVis; ++i)
- {
- CBaseObject* pObj = pDispayedViewObjects->GetObject(i);
-
- HitTestObjectAgainstRect(pObj, view, hc, guids);
- }
+ AZ_Assert(false, "CObjectManager::FindObjectsInRect is legacy/deprecated and should not be used.");
}
//////////////////////////////////////////////////////////////////////////
-void CObjectManager::SelectObjectsInRect(CViewport* view, const QRect& rect, bool bSelect)
+void CObjectManager::SelectObjectsInRect(
+ [[maybe_unused]] CViewport* view, [[maybe_unused]] const QRect& rect, [[maybe_unused]] bool bSelect)
{
- AZ_PROFILE_FUNCTION(Editor);
-
- // Ignore too small rectangles.
- if (rect.width() < 1 || rect.height() < 1)
- {
- return;
- }
-
- CUndo undo("Select Object(s)");
-
- HitContext hc;
- hc.view = view;
- hc.b2DViewport = view->GetType() != ET_ViewportCamera;
- hc.rect = rect;
- hc.bUseSelectionHelpers = view->GetAdvancedSelectModeFlag();
-
- bool isUndoRecording = GetIEditor()->IsUndoRecording();
- if (isUndoRecording)
- {
- m_processingBulkSelect = true;
- }
-
- CBaseObjectsCache* displayedViewObjects = view->GetVisibleObjectsCache();
- int numVis = displayedViewObjects->GetObjectCount();
-
- // Tracking the previous selection allows proper undo/redo functionality of additional
- // selections (CTRL + drag select)
- AZStd::unordered_set previousSelection;
-
- for (int i = 0; i < numVis; ++i)
- {
- CBaseObject* object = displayedViewObjects->GetObject(i);
-
- if (object->IsSelected())
- {
- previousSelection.insert(object);
- }
- else
- {
- // This will update m_currSelection
- SelectObjectInRect(object, view, hc, bSelect);
-
- // Legacy undo/redo does not go through the Ebus system and must be done individually
- if (isUndoRecording && object->GetType() != OBJTYPE_AZENTITY)
- {
- GetIEditor()->RecordUndo(new CUndoBaseObjectSelect(object, true));
- }
- }
- }
-
- if (isUndoRecording && m_currSelection)
- {
- // Component Entities can handle undo/redo in bulk due to Ebuses
- GetIEditor()->RecordUndo(new CUndoBaseObjectBulkSelect(previousSelection, *m_currSelection));
- }
-
- m_processingBulkSelect = false;
+ AZ_Assert(false, "CObjectManager::SelectObjectsInRect is legacy/deprecated and should not be used.");
}
//////////////////////////////////////////////////////////////////////////
@@ -3011,6 +2709,4 @@ namespace AzToolsFramework
}
}
-}
-
-
+} // namespace AzToolsFramework
diff --git a/Code/Editor/Objects/ObjectManager.h b/Code/Editor/Objects/ObjectManager.h
index 4ffa5e9a07..0ad5d8323e 100644
--- a/Code/Editor/Objects/ObjectManager.h
+++ b/Code/Editor/Objects/ObjectManager.h
@@ -52,40 +52,6 @@ public:
}
};
-//////////////////////////////////////////////////////////////////////////
-// Array of editor objects.
-//////////////////////////////////////////////////////////////////////////
-class CBaseObjectsCache
-{
-public:
- int GetObjectCount() const { return static_cast(m_objects.size()); }
- CBaseObject* GetObject(int nIndex) const { return m_objects[nIndex]; }
- void AddObject(CBaseObject* object);
-
- void ClearObjects()
- {
- m_objects.clear();
- m_entityIds.clear();
- }
-
- void Reserve(int nCount)
- {
- m_objects.reserve(nCount);
- m_entityIds.reserve(nCount);
- }
-
- const AZStd::vector& GetEntityIdCache() const { return m_entityIds; }
-
- /// Checksum is used as a dirty flag.
- unsigned int GetSerialNumber() { return m_serialNumber; }
- void SetSerialNumber(unsigned int serialNumber) { m_serialNumber = serialNumber; }
-private:
- //! List of objects that was displayed at last frame.
- std::vector<_smart_ptr > m_objects;
- AZStd::vector m_entityIds;
- unsigned int m_serialNumber = 0;
-};
-
/*!
* CObjectManager is a singleton object that
* manages global set of objects in level.
diff --git a/Code/Editor/Viewport.cpp b/Code/Editor/Viewport.cpp
index 2d41538d92..5b4f4a4df3 100644
--- a/Code/Editor/Viewport.cpp
+++ b/Code/Editor/Viewport.cpp
@@ -173,8 +173,6 @@ QtViewport::QtViewport(QWidget* parent)
m_bAdvancedSelectMode = false;
- m_pVisibleObjectsCache = new CBaseObjectsCache;
-
m_constructionPlane.SetPlane(Vec3_OneZ, Vec3_Zero);
m_constructionPlaneAxisX = Vec3_Zero;
m_constructionPlaneAxisY = Vec3_Zero;
@@ -204,8 +202,6 @@ QtViewport::QtViewport(QWidget* parent)
//////////////////////////////////////////////////////////////////////////
QtViewport::~QtViewport()
{
- delete m_pVisibleObjectsCache;
-
GetIEditor()->GetViewManager()->UnregisterViewport(this);
}
@@ -376,11 +372,6 @@ void QtViewport::OnDeactivate()
void QtViewport::ResetContent()
{
m_pMouseOverObject = nullptr;
-
- // Need to clear visual object cache.
- // Right after loading new level, some code(e.g. OnMouseMove) access invalid
- // previous level object before cache updated.
- GetVisibleObjectsCache()->ClearObjects();
}
//////////////////////////////////////////////////////////////////////////
@@ -398,11 +389,8 @@ void QtViewport::Update()
m_viewportUi.Update();
m_bAdvancedSelectMode = false;
- bool bSpaceClick = false;
- {
- bSpaceClick = CheckVirtualKey(Qt::Key_Space) & !CheckVirtualKey(Qt::Key_Shift) /*& !CheckVirtualKey(Qt::Key_Control)*/;
- }
- if (bSpaceClick && hasFocus())
+
+ if (CheckVirtualKey(Qt::Key_Space) && !CheckVirtualKey(Qt::Key_Shift) && hasFocus())
{
m_bAdvancedSelectMode = true;
}
diff --git a/Code/Editor/Viewport.h b/Code/Editor/Viewport.h
index 60c1306420..fdb8332479 100644
--- a/Code/Editor/Viewport.h
+++ b/Code/Editor/Viewport.h
@@ -491,10 +491,6 @@ public:
void ResetCursor() override;
void SetSupplementaryCursorStr(const QString& str) override;
- //////////////////////////////////////////////////////////////////////////
- // Return visble objects cache.
- CBaseObjectsCache* GetVisibleObjectsCache() override { return m_pVisibleObjectsCache; };
-
void RegisterRenderListener(IRenderListener* piListener) override;
bool UnregisterRenderListener(IRenderListener* piListener) override;
bool IsRenderListenerRegistered(IRenderListener* piListener) override;
@@ -612,8 +608,6 @@ protected:
int m_nLastUpdateFrame;
int m_nLastMouseMoveFrame;
- CBaseObjectsCache* m_pVisibleObjectsCache;
-
QRect m_rcClient;
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp b/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp
index 8eb620f69e..06bb0b0cac 100644
--- a/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp
+++ b/Code/Framework/AzCore/AzCore/Asset/AssetManager.cpp
@@ -551,8 +551,6 @@ namespace AZ
{
PrepareShutDown();
- DispatchEvents();
-
// Acquire the asset lock to make sure nobody else is trying to do anything fancy with assets
AZStd::scoped_lock assetLock(m_assetMutex);
@@ -575,7 +573,10 @@ namespace AZ
{
AZ_PROFILE_FUNCTION(AzCore);
AssetManagerNotificationBus::Broadcast(&AssetManagerNotificationBus::Events::OnAssetEventsDispatchBegin);
- AssetBus::ExecuteQueuedEvents();
+ while (AssetBus::QueuedEventCount())
+ {
+ AssetBus::ExecuteQueuedEvents();
+ }
AssetManagerNotificationBus::Broadcast(&AssetManagerNotificationBus::Events::OnAssetEventsDispatchEnd);
}
diff --git a/Code/Framework/AzCore/AzCore/AzCoreModule.cpp b/Code/Framework/AzCore/AzCore/AzCoreModule.cpp
index c60ea1bd72..ec45335f95 100644
--- a/Code/Framework/AzCore/AzCore/AzCoreModule.cpp
+++ b/Code/Framework/AzCore/AzCore/AzCoreModule.cpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
namespace AZ
{
@@ -41,6 +42,7 @@ namespace AZ
TimeSystemComponent::CreateDescriptor(),
LoggerSystemComponent::CreateDescriptor(),
EventSchedulerSystemComponent::CreateDescriptor(),
+ TaskGraphSystemComponent::CreateDescriptor(),
#if !defined(AZCORE_EXCLUDE_LUA)
ScriptSystemComponent::CreateDescriptor(),
@@ -55,6 +57,7 @@ namespace AZ
azrtti_typeid(),
azrtti_typeid(),
azrtti_typeid(),
+ azrtti_typeid(),
};
}
}
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
index 168807cd97..7e9accffd7 100644
--- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
@@ -1367,9 +1367,6 @@ namespace AZ
#endif
}
- //=========================================================================
- // Tick
- //=========================================================================
void ComponentApplication::Tick(float deltaOverride /*= -1.f*/)
{
{
@@ -1397,9 +1394,6 @@ namespace AZ
}
}
- //=========================================================================
- // Tick
- //=========================================================================
void ComponentApplication::TickSystem()
{
AZ_PROFILE_SCOPE(System, "Component application tick");
@@ -1547,5 +1541,4 @@ namespace AZ
AZ::SettingsRegistryScriptUtils::ReflectSettingsRegistryToBehaviorContext(*behaviorContext);
}
}
-
} // namespace AZ
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
index bfc541ca09..8e95ff5fa3 100644
--- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
@@ -5,6 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
+
#pragma once
#include
diff --git a/Code/Framework/AzCore/AzCore/Debug/Trace.h b/Code/Framework/AzCore/AzCore/Debug/Trace.h
index a1334d334e..507ba48e53 100644
--- a/Code/Framework/AzCore/AzCore/Debug/Trace.h
+++ b/Code/Framework/AzCore/AzCore/Debug/Trace.h
@@ -262,17 +262,17 @@ namespace AZ
#else // !AZ_ENABLE_TRACING
- #define AZ_Assert(...) AZ_UNUSED(__VA_ARGS__);
- #define AZ_Error(...) AZ_UNUSED(__VA_ARGS__);
- #define AZ_ErrorOnce(...) AZ_UNUSED(__VA_ARGS__);
- #define AZ_Warning(...) AZ_UNUSED(__VA_ARGS__);
- #define AZ_WarningOnce(...) AZ_UNUSED(__VA_ARGS__);
- #define AZ_TracePrintf(...) AZ_UNUSED(__VA_ARGS__);
- #define AZ_TracePrintfOnce(...) AZ_UNUSED(__VA_ARGS__);
+ #define AZ_Assert(...)
+ #define AZ_Error(...)
+ #define AZ_ErrorOnce(...)
+ #define AZ_Warning(...)
+ #define AZ_WarningOnce(...)
+ #define AZ_TracePrintf(...)
+ #define AZ_TracePrintfOnce(...)
- #define AZ_Verify(...) AZ_UNUSED(__VA_ARGS__);
- #define AZ_VerifyError(...) AZ_UNUSED(__VA_ARGS__);
- #define AZ_VerifyWarning(...) AZ_UNUSED(__VA_ARGS__);
+ #define AZ_Verify(expression, ...) AZ_UNUSED(expression)
+ #define AZ_VerifyError(window, expression, ...) AZ_UNUSED(expression)
+ #define AZ_VerifyWarning(window, expression, ...) AZ_UNUSED(expression)
#endif // AZ_ENABLE_TRACING
diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.cpp
index 9b4996ad49..9ee0fefc99 100644
--- a/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.cpp
+++ b/Code/Framework/AzCore/AzCore/IO/Streamer/Scheduler.cpp
@@ -43,10 +43,12 @@ namespace AZ::IO
m_mainLoopDesc = threadDesc;
m_mainLoopDesc.m_name = "IO Scheduler";
- m_mainLoop = AZStd::thread([this]()
- {
- Thread_MainLoop();
- }, &m_mainLoopDesc);
+ m_mainLoop = AZStd::thread(
+ m_mainLoopDesc,
+ [this]()
+ {
+ Thread_MainLoop();
+ });
}
}
diff --git a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.cpp b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.cpp
index f76946a667..230bf959f6 100644
--- a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.cpp
+++ b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.cpp
@@ -644,11 +644,11 @@ JobManagerWorkStealing::ThreadList JobManagerWorkStealing::CreateWorkerThreads(c
}
info->m_thread = AZStd::thread(
+ threadDesc,
[this, info]()
{
this->ProcessJobsWorker(info);
- },
- &threadDesc
+ }
);
info->m_threadId = info->m_thread.get_id();
diff --git a/Code/Framework/AzCore/AzCore/Math/Aabb.h b/Code/Framework/AzCore/AzCore/Math/Aabb.h
index 488808bc4c..438d95622e 100644
--- a/Code/Framework/AzCore/AzCore/Math/Aabb.h
+++ b/Code/Framework/AzCore/AzCore/Math/Aabb.h
@@ -5,7 +5,6 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
-
#pragma once
#include
diff --git a/Code/Framework/AzCore/AzCore/Math/Vector2.h b/Code/Framework/AzCore/AzCore/Math/Vector2.h
index b2b1ceeb4d..7c37d74135 100644
--- a/Code/Framework/AzCore/AzCore/Math/Vector2.h
+++ b/Code/Framework/AzCore/AzCore/Math/Vector2.h
@@ -180,6 +180,13 @@ namespace AZ
bool IsGreaterEqualThan(const Vector2& v) const;
//! @}
+ //! Floor/Ceil/Round functions, operate on each component individually, result will be a new Vector2.
+ //! @{
+ Vector2 GetFloor() const;
+ Vector2 GetCeil() const;
+ Vector2 GetRound() const; // Ties to even (banker's rounding)
+ //! @}
+
//! Min/Max functions, operate on each component individually, result will be a new Vector2.
//! @{
Vector2 GetMin(const Vector2& v) const;
diff --git a/Code/Framework/AzCore/AzCore/Math/Vector2.inl b/Code/Framework/AzCore/AzCore/Math/Vector2.inl
index 80b2a43475..086be2bbc3 100644
--- a/Code/Framework/AzCore/AzCore/Math/Vector2.inl
+++ b/Code/Framework/AzCore/AzCore/Math/Vector2.inl
@@ -398,6 +398,24 @@ namespace AZ
}
+ AZ_MATH_INLINE Vector2 Vector2::GetFloor() const
+ {
+ return Vector2(Simd::Vec2::Floor(m_value));
+ }
+
+
+ AZ_MATH_INLINE Vector2 Vector2::GetCeil() const
+ {
+ return Vector2(Simd::Vec2::Ceil(m_value));
+ }
+
+
+ AZ_MATH_INLINE Vector2 Vector2::GetRound() const
+ {
+ return Vector2(Simd::Vec2::Round(m_value));
+ }
+
+
AZ_MATH_INLINE Vector2 Vector2::GetMin(const Vector2& v) const
{
#if AZ_TRAIT_USE_PLATFORM_SIMD_SCALAR
diff --git a/Code/Framework/AzCore/AzCore/Math/Vector3.h b/Code/Framework/AzCore/AzCore/Math/Vector3.h
index 4bf0a18894..821dc8292c 100644
--- a/Code/Framework/AzCore/AzCore/Math/Vector3.h
+++ b/Code/Framework/AzCore/AzCore/Math/Vector3.h
@@ -211,6 +211,13 @@ namespace AZ
bool IsGreaterEqualThan(const Vector3& rhs) const;
//! @}
+ //! Floor/Ceil/Round functions, operate on each component individually, result will be a new Vector3.
+ //! @{
+ Vector3 GetFloor() const;
+ Vector3 GetCeil() const;
+ Vector3 GetRound() const; // Ties to even (banker's rounding)
+ //! @}
+
//! Min/Max functions, operate on each component individually, result will be a new Vector3.
//! @{
Vector3 GetMin(const Vector3& v) const;
diff --git a/Code/Framework/AzCore/AzCore/Math/Vector3.inl b/Code/Framework/AzCore/AzCore/Math/Vector3.inl
index 56baac5aa1..879ade38cf 100644
--- a/Code/Framework/AzCore/AzCore/Math/Vector3.inl
+++ b/Code/Framework/AzCore/AzCore/Math/Vector3.inl
@@ -481,6 +481,24 @@ namespace AZ
}
+ AZ_MATH_INLINE Vector3 Vector3::GetFloor() const
+ {
+ return Vector3(Simd::Vec3::Floor(m_value));
+ }
+
+
+ AZ_MATH_INLINE Vector3 Vector3::GetCeil() const
+ {
+ return Vector3(Simd::Vec3::Ceil(m_value));
+ }
+
+
+ AZ_MATH_INLINE Vector3 Vector3::GetRound() const
+ {
+ return Vector3(Simd::Vec3::Round(m_value));
+ }
+
+
AZ_MATH_INLINE Vector3 Vector3::GetMin(const Vector3& v) const
{
#if AZ_TRAIT_USE_PLATFORM_SIMD_SCALAR
diff --git a/Code/Framework/AzCore/AzCore/Math/Vector4.h b/Code/Framework/AzCore/AzCore/Math/Vector4.h
index 6bd67e8831..eaa18bbaff 100644
--- a/Code/Framework/AzCore/AzCore/Math/Vector4.h
+++ b/Code/Framework/AzCore/AzCore/Math/Vector4.h
@@ -189,6 +189,13 @@ namespace AZ
bool IsGreaterEqualThan(const Vector4& rhs) const;
//! @}
+ //! Floor/Ceil/Round functions, operate on each component individually, result will be a new Vector4.
+ //! @{
+ Vector4 GetFloor() const;
+ Vector4 GetCeil() const;
+ Vector4 GetRound() const; // Ties to even (banker's rounding)
+ //! @}
+
//! Min/Max functions, operate on each component individually, result will be a new Vector4.
//! @{
Vector4 GetMin(const Vector4& v) const;
diff --git a/Code/Framework/AzCore/AzCore/Math/Vector4.inl b/Code/Framework/AzCore/AzCore/Math/Vector4.inl
index 4b4cf2f84f..001dfb6a4d 100644
--- a/Code/Framework/AzCore/AzCore/Math/Vector4.inl
+++ b/Code/Framework/AzCore/AzCore/Math/Vector4.inl
@@ -464,6 +464,24 @@ namespace AZ
}
+ AZ_MATH_INLINE Vector4 Vector4::GetFloor() const
+ {
+ return Vector4(Simd::Vec4::Floor(m_value));
+ }
+
+
+ AZ_MATH_INLINE Vector4 Vector4::GetCeil() const
+ {
+ return Vector4(Simd::Vec4::Ceil(m_value));
+ }
+
+
+ AZ_MATH_INLINE Vector4 Vector4::GetRound() const
+ {
+ return Vector4(Simd::Vec4::Round(m_value));
+ }
+
+
AZ_MATH_INLINE Vector4 Vector4::GetMin(const Vector4& v) const
{
#if AZ_TRAIT_USE_PLATFORM_SIMD_SCALAR
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
index da7110e36e..113fdd433e 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
@@ -717,7 +717,9 @@ namespace AZ::SettingsRegistryMergeUtils
if (registry.Get(cacheRootPath, FilePathKey_CacheRootFolder))
{
mergePath = AZStd::move(cacheRootPath);
- mergePath /= SettingsRegistryInterface::RegistryFolder;
+ AZStd::fixed_string<32> registryFolderLower(SettingsRegistryInterface::RegistryFolder);
+ AZStd::to_lower(registryFolderLower.begin(), registryFolderLower.end());
+ mergePath /= registryFolderLower;
registry.MergeSettingsFolder(mergePath.Native(), specializations, platform, "", scratchBuffer);
}
diff --git a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp
index 2bb88fbfa2..7da04d7301 100644
--- a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp
+++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp
@@ -190,11 +190,13 @@ namespace AZ
class TaskWorker
{
public:
- void Spawn(::AZ::TaskExecutor& executor, size_t id, AZStd::semaphore& initSemaphore, bool affinitize)
+ static thread_local TaskWorker* t_worker;
+
+ void Spawn(::AZ::TaskExecutor& executor, uint32_t id, AZStd::semaphore& initSemaphore, bool affinitize)
{
m_executor = &executor;
- AZStd::string threadName = AZStd::string::format("TaskWorker %zu", id);
+ AZStd::string threadName = AZStd::string::format("TaskWorker %u", id);
AZStd::thread_desc desc = {};
desc.m_name = threadName.c_str();
if (affinitize)
@@ -203,12 +205,29 @@ namespace AZ
}
m_active.store(true, AZStd::memory_order_release);
- m_thread = AZStd::thread{ [this, &initSemaphore]
+ m_thread = AZStd::thread{ desc,
+ [this, &initSemaphore]
{
+ t_worker = this;
initSemaphore.release();
Run();
- },
- &desc };
+ } };
+ }
+
+ // Threads that wait on a graph to complete are disqualified from receiving tasks until the wait finishes
+ void Disable()
+ {
+ m_enabled = false;
+ }
+
+ void Enable()
+ {
+ m_enabled = true;
+ }
+
+ bool Enabled() const
+ {
+ return m_enabled;
}
void Join()
@@ -222,11 +241,7 @@ namespace AZ
{
m_queue.Enqueue(task);
- if (!m_busy.exchange(true))
- {
- // The worker was idle prior to enqueueing the task, release the semaphore
- m_semaphore.release();
- }
+ m_semaphore.release();
}
private:
@@ -234,7 +249,6 @@ namespace AZ
{
while (m_active)
{
- m_busy = false;
m_semaphore.acquire();
if (!m_active)
@@ -242,8 +256,6 @@ namespace AZ
return;
}
- m_busy = true;
-
Task* task = m_queue.TryDequeue();
while (task)
{
@@ -271,12 +283,15 @@ namespace AZ
AZStd::thread m_thread;
AZStd::atomic m_active;
- AZStd::atomic m_busy;
+ AZStd::atomic m_enabled = true;
AZStd::binary_semaphore m_semaphore;
::AZ::TaskExecutor* m_executor;
TaskQueue m_queue;
+ friend class ::AZ::TaskExecutor;
};
+
+ thread_local TaskWorker* TaskWorker::t_worker = nullptr;
} // namespace Internal
static EnvironmentVariable s_executor;
@@ -291,13 +306,16 @@ namespace AZ
return **s_executor;
}
- // TODO: Create the default executor as part of a component (as in TaskManagerComponent)
void TaskExecutor::SetInstance(TaskExecutor* executor)
{
- AZ_Assert(!s_executor, "Attempting to set the global task executor more than once");
-
- s_executor = AZ::Environment::CreateVariable("GlobalTaskExecutor");
- s_executor.Set(executor);
+ if (!executor)
+ {
+ s_executor.Reset();
+ }
+ else if (!s_executor) // ignore any calls to set after the first (this happens in unit tests that create new system entities)
+ {
+ s_executor = AZ::Environment::CreateVariable(s_executorName, executor);
+ }
}
TaskExecutor::TaskExecutor(uint32_t threadCount)
@@ -307,14 +325,12 @@ namespace AZ
m_workers = reinterpret_cast(azmalloc(m_threadCount * sizeof(Internal::TaskWorker)));
- bool affinitize = m_threadCount == AZStd::thread::hardware_concurrency();
-
AZStd::semaphore initSemaphore;
- for (size_t i = 0; i != m_threadCount; ++i)
+ for (uint32_t i = 0; i != m_threadCount; ++i)
{
new (m_workers + i) Internal::TaskWorker{};
- m_workers[i].Spawn(*this, i, initSemaphore, affinitize);
+ m_workers[i].Spawn(*this, i, initSemaphore, false);
}
for (size_t i = 0; i != m_threadCount; ++i)
@@ -334,9 +350,21 @@ namespace AZ
azfree(m_workers);
}
- void TaskExecutor::Submit(Internal::CompiledTaskGraph& graph)
+ Internal::TaskWorker* TaskExecutor::GetTaskWorker()
+ {
+ if (Internal::TaskWorker::t_worker && Internal::TaskWorker::t_worker->m_executor == this)
+ {
+ return Internal::TaskWorker::t_worker;
+ }
+ return nullptr;
+ }
+
+ void TaskExecutor::Submit(Internal::CompiledTaskGraph& graph, TaskGraphEvent* event)
{
++m_graphsRemaining;
+
+ event->m_executor = this; // Used to validate event is not waited for inside a job
+
// Submit all tasks that have no inbound edges
for (Internal::Task& task : graph.Tasks())
{
@@ -352,11 +380,24 @@ namespace AZ
// TODO: Something more sophisticated is likely needed here.
// First, we are completely ignoring affinity.
// Second, some heuristics on core availability will help distribute work more effectively
- m_workers[++m_lastSubmission % m_threadCount].Enqueue(&task);
+ uint32_t nextWorker = ++m_lastSubmission % m_threadCount;
+ while (!m_workers[nextWorker].Enabled())
+ {
+ // Graphs that are waiting for the completion of a task graph cannot enqueue tasks onto
+ // the thread issuing the wait.
+ nextWorker = ++m_lastSubmission % m_threadCount;
+ }
+
+ m_workers[nextWorker].Enqueue(&task);
}
void TaskExecutor::ReleaseGraph()
{
--m_graphsRemaining;
}
+
+ void TaskExecutor::ReactivateTaskWorker()
+ {
+ GetTaskWorker()->Enable();
+ }
} // namespace AZ
diff --git a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h
index dc2fa5a4c8..7e1ff80902 100644
--- a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h
+++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h
@@ -72,14 +72,19 @@ namespace AZ
explicit TaskExecutor(uint32_t threadCount = 0);
~TaskExecutor();
- void Submit(Internal::CompiledTaskGraph& graph);
+ // Submit a task graph for execution. Waitable task graphs cannot enqueue work on the task thread
+ // that is currently active
+ void Submit(Internal::CompiledTaskGraph& graph, TaskGraphEvent* event);
void Submit(Internal::Task& task);
private:
friend class Internal::TaskWorker;
+ friend class TaskGraphEvent;
+ Internal::TaskWorker* GetTaskWorker();
void ReleaseGraph();
+ void ReactivateTaskWorker();
Internal::TaskWorker* m_workers;
uint32_t m_threadCount = 0;
diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp b/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp
index 3fb93903c9..f57b06890a 100644
--- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp
+++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp
@@ -14,6 +14,12 @@ namespace AZ
{
using Internal::CompiledTaskGraph;
+ void TaskGraphEvent::Wait()
+ {
+ AZ_Assert(m_executor->GetTaskWorker() == nullptr, "Waiting in a task is unsupported");
+ m_semaphore.acquire();
+ }
+
void TaskToken::PrecedesInternal(TaskToken& comesAfter)
{
AZ_Assert(!m_parent.m_submitted, "Cannot mutate a TaskGraph that was previously submitted.");
@@ -71,7 +77,7 @@ namespace AZ
m_compiledTaskGraph->m_tasks[i].Init();
}
- executor.Submit(*m_compiledTaskGraph);
+ executor.Submit(*m_compiledTaskGraph, waitEvent);
if (m_retained)
{
diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.h b/Code/Framework/AzCore/AzCore/Task/TaskGraph.h
index 4b454c63de..9553013a4b 100644
--- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.h
+++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.h
@@ -22,10 +22,19 @@ namespace AZ
namespace Internal
{
class CompiledTaskGraph;
+ class TaskWorker;
}
class TaskExecutor;
class TaskGraph;
+ class TaskGraphActiveInterface
+ {
+ public:
+ AZ_RTTI(TaskGraphActiveInterface, "{08118074-B139-4EF9-B8FD-29F1D6DC9233}");
+
+ virtual bool IsTaskGraphActive() const = 0;
+ };
+
// A TaskToken is returned each time a Task is added to the TaskGraph. TaskTokens are used to
// express dependencies between tasks within the graph, and have no purpose after the graph
// is submitted (simply let them go out of scope)
@@ -70,9 +79,12 @@ namespace AZ
private:
friend class ::AZ::Internal::CompiledTaskGraph;
friend class TaskGraph;
+ friend class TaskExecutor;
+
void Signal();
AZStd::binary_semaphore m_semaphore;
+ TaskExecutor* m_executor = nullptr;
};
// The TaskGraph encapsulates a set of tasks and their interdependencies. After adding
@@ -89,6 +101,9 @@ namespace AZ
// Reset the state of the task graph to begin recording tasks and edges again
// NOTE: Graph must be in a "settled" state (cannot be in-flight)
void Reset();
+
+ // Returns false if 1 or more tasks have been added to the graph
+ bool IsEmpty();
// Add a task to the graph, retrieiving a token that can be used to express dependencies
// between tasks. The first argument specifies the TaskKind, used for tracking the task.
diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl b/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl
index e0ac74ba9d..7b2f0cefdc 100644
--- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl
+++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl
@@ -33,11 +33,6 @@ namespace AZ
return m_semaphore.try_acquire_for(AZStd::chrono::milliseconds{ 0 });
}
- inline void TaskGraphEvent::Wait()
- {
- m_semaphore.acquire();
- }
-
inline void TaskGraphEvent::Signal()
{
m_semaphore.release();
@@ -59,6 +54,11 @@ namespace AZ
return { AddTask(descriptor, AZStd::forward(lambdas))... };
}
+ inline bool TaskGraph::IsEmpty()
+ {
+ return m_tasks.empty();
+ }
+
inline void TaskGraph::Detach()
{
m_retained = false;
diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp
new file mode 100644
index 0000000000..eed461ecb4
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp
@@ -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
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+// Create a cvar as a central location for experimentation with switching from the Job system to TaskGraph system.
+AZ_CVAR(bool, cl_activateTaskGraph, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Flag clients of TaskGraph to switch between jobs/taskgraph (Note does not disable task graph system)");
+static constexpr uint32_t TaskExecutorServiceCrc = AZ_CRC_CE("TaskExecutorService");
+
+namespace AZ
+{
+ void TaskGraphSystemComponent::Activate()
+ {
+ AZ_Assert(m_taskExecutor == nullptr, "Error multiple activation of the TaskGraphSystemComponent");
+
+ if (Interface::Get() == nullptr)
+ {
+ Interface::Register(this);
+ m_taskExecutor = aznew TaskExecutor();
+ TaskExecutor::SetInstance(m_taskExecutor);
+ }
+ }
+
+ void TaskGraphSystemComponent::Deactivate()
+ {
+ if (&TaskExecutor::Instance() == m_taskExecutor) // check that our instance is the global instance (not always true in unit tests)
+ {
+ m_taskExecutor->SetInstance(nullptr);
+ }
+ if (m_taskExecutor)
+ {
+ azdestroy(m_taskExecutor);
+ m_taskExecutor = nullptr;
+ }
+ if (Interface::Get() == this)
+ {
+ Interface::Unregister(this);
+ }
+ }
+
+ void TaskGraphSystemComponent::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
+ {
+ provided.push_back(TaskExecutorServiceCrc);
+ }
+
+ void TaskGraphSystemComponent::GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible)
+ {
+ incompatible.push_back(TaskExecutorServiceCrc);
+ }
+
+ void TaskGraphSystemComponent::GetDependentServices([[maybe_unused]] ComponentDescriptor::DependencyArrayType& dependent)
+ {
+ }
+
+ void TaskGraphSystemComponent::Reflect(ReflectContext* context)
+ {
+ if (SerializeContext* serializeContext = azrtti_cast(context))
+ {
+ serializeContext->Class()
+ ->Version(1)
+ ;
+
+ if (AZ::EditContext* ec = serializeContext->GetEditContext())
+ {
+ ec->Class
+ ("TaskGraph", "System component to create the default executor")
+ ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+ ->Attribute(AZ::Edit::Attributes::Category, "Engine")
+ ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
+ ;
+ }
+ }
+ }
+
+ bool TaskGraphSystemComponent::IsTaskGraphActive() const
+ {
+ return cl_activateTaskGraph;
+ }
+} // namespace AZ
diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.h b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.h
new file mode 100644
index 0000000000..a4c6da9539
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.h
@@ -0,0 +1,47 @@
+/*
+ * 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
+#include
+#include
+#include
+
+namespace AZ
+{
+ class TaskGraphSystemComponent
+ : public Component
+ , public TaskGraphActiveInterface
+ {
+ public:
+ AZ_COMPONENT(AZ::TaskGraphSystemComponent, "{5D56B829-1FEB-43D5-A0BD-E33C0497EFE2}")
+
+ TaskGraphSystemComponent() = default;
+
+ // Implement TaskGraphActiveInterface
+ bool IsTaskGraphActive() const override;
+
+ private:
+ //////////////////////////////////////////////////////////////////////////
+ // Component base
+ void Activate() override;
+ void Deactivate() 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);
+
+ AZ::TaskExecutor* m_taskExecutor = nullptr;
+ };
+}
diff --git a/Code/Framework/AzCore/AzCore/Time/ITime.h b/Code/Framework/AzCore/AzCore/Time/ITime.h
index 845017bd14..a97ba2319a 100644
--- a/Code/Framework/AzCore/AzCore/Time/ITime.h
+++ b/Code/Framework/AzCore/AzCore/Time/ITime.h
@@ -13,14 +13,26 @@
#include
#include
#include
+#include
namespace AZ
{
//! This is a strong typedef for representing a millisecond value since application start.
AZ_TYPE_SAFE_INTEGRAL(TimeMs, int64_t);
+ //! This is a strong typedef for representing a microsecond value since application start.
+ //! Using int64_t as the underlying type, this is good to represent approximately 292,471 years
+ AZ_TYPE_SAFE_INTEGRAL(TimeUs, int64_t);
+
//! @class ITime
//! @brief This is an AZ::Interface<> for managing time related operations.
+ //! AZ::ITime and associated types may not operate in realtime. These abstractions are to allow our application
+ //! simulation to operate both slower and faster than realtime in a well defined and user controllable manner
+ //! The rate at which time passes for AZ::ITime is controlled by the cvar t_scale
+ //! t_scale == 0 means simulation time should halt
+ //! 0 < t_scale < 1 will cause time to pass slower than realtime, with t_scale 0.1 being roughly 1/10th realtime
+ //! t_scale == 1 will cause time to pass at roughly realtime
+ //! t_scale > 1 will cause time to pass faster than normal, with t_scale 10 being roughly 10x realtime
class ITime
{
public:
@@ -33,6 +45,10 @@ namespace AZ
//! @return the number of milliseconds that have elapsed since application start
virtual TimeMs GetElapsedTimeMs() const = 0;
+ //! Returns the number of microseconds since application start.
+ //! @return the number of microseconds that have elapsed since application start
+ virtual TimeUs GetElapsedTimeUs() const = 0;
+
AZ_DISABLE_COPY_MOVE(ITime);
};
@@ -51,6 +67,53 @@ namespace AZ
{
return AZ::Interface::Get()->GetElapsedTimeMs();
}
-}
+
+ //! This is a simple convenience wrapper
+ inline TimeUs GetElapsedTimeUs()
+ {
+ return AZ::Interface::Get()->GetElapsedTimeUs();
+ }
+
+ //! Converts from milliseconds to microseconds
+ inline TimeUs TimeMsToUs(TimeMs value)
+ {
+ return static_cast(value * static_cast(1000));
+ }
+
+ //! Converts from microseconds to milliseconds
+ inline TimeMs TimeUsToMs(TimeUs value)
+ {
+ return static_cast(value / static_cast(1000));
+ }
+
+ //! Converts from milliseconds to seconds
+ inline float TimeMsToSeconds(TimeMs value)
+ {
+ return static_cast(value) / 1000.0f;
+ }
+
+ //! Converts from microseconds to seconds
+ inline float TimeUsToSeconds(TimeUs value)
+ {
+ return static_cast(value) / 1000000.0f;
+ }
+
+ //! Converts from milliseconds to AZStd::chrono::time_point
+ inline auto TimeMsToChrono(TimeMs value)
+ {
+ auto epoch = AZStd::chrono::time_point();
+ auto chronoValue = AZStd::chrono::milliseconds(aznumeric_cast(value));
+ return epoch + chronoValue;
+ }
+
+ //! Converts from microseconds to AZStd::chrono::time_point
+ inline auto TimeUsToChrono(TimeUs value)
+ {
+ auto epoch = AZStd::chrono::time_point();
+ auto chronoValue = AZStd::chrono::microseconds(aznumeric_cast(value));
+ return epoch + chronoValue;
+ }
+} // namespace AZ
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(AZ::TimeMs);
+AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(AZ::TimeUs);
diff --git a/Code/Framework/AzCore/AzCore/Time/TimeSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Time/TimeSystemComponent.cpp
index a2e7733222..99145d377a 100644
--- a/Code/Framework/AzCore/AzCore/Time/TimeSystemComponent.cpp
+++ b/Code/Framework/AzCore/AzCore/Time/TimeSystemComponent.cpp
@@ -35,7 +35,7 @@ namespace AZ
TimeSystemComponent::TimeSystemComponent()
{
- m_lastInvokedTimeMs = static_cast(AZStd::GetTimeNowMicroSecond() / 1000);
+ m_lastInvokedTimeUs = static_cast(AZStd::GetTimeNowMicroSecond());
AZ::Interface::Register(this);
ITimeRequestBus::Handler::BusConnect();
}
@@ -58,18 +58,23 @@ namespace AZ
TimeMs TimeSystemComponent::GetElapsedTimeMs() const
{
- TimeMs currentTime = static_cast(AZStd::GetTimeNowMicroSecond() / 1000);
- TimeMs deltaTime = currentTime - m_lastInvokedTimeMs;
+ return TimeUsToMs(GetElapsedTimeUs());
+ }
+
+ TimeUs TimeSystemComponent::GetElapsedTimeUs() const
+ {
+ TimeUs currentTime = static_cast(AZStd::GetTimeNowMicroSecond());
+ TimeUs deltaTime = currentTime - m_lastInvokedTimeUs;
if (t_scale != 1.0f)
{
float floatDelta = static_cast(deltaTime) * t_scale;
- deltaTime = static_cast(static_cast(floatDelta));
+ deltaTime = static_cast(static_cast(floatDelta));
}
- m_accumulatedTimeMs += deltaTime;
- m_lastInvokedTimeMs = currentTime;
+ m_accumulatedTimeUs += deltaTime;
+ m_lastInvokedTimeUs = currentTime;
- return m_accumulatedTimeMs;
+ return m_accumulatedTimeUs;
}
}
diff --git a/Code/Framework/AzCore/AzCore/Time/TimeSystemComponent.h b/Code/Framework/AzCore/AzCore/Time/TimeSystemComponent.h
index adf576becb..3ab3dbc234 100644
--- a/Code/Framework/AzCore/AzCore/Time/TimeSystemComponent.h
+++ b/Code/Framework/AzCore/AzCore/Time/TimeSystemComponent.h
@@ -39,11 +39,12 @@ namespace AZ
//! ITime overrides.
//! @{
TimeMs GetElapsedTimeMs() const override;
+ TimeUs GetElapsedTimeUs() const override;
//! @}
private:
- mutable TimeMs m_lastInvokedTimeMs = TimeMs{0};
- mutable TimeMs m_accumulatedTimeMs = TimeMs{0};
+ mutable TimeUs m_lastInvokedTimeUs = TimeUs{0};
+ mutable TimeUs m_accumulatedTimeUs = TimeUs{0};
};
}
diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake
index aa07959997..14579cbf33 100644
--- a/Code/Framework/AzCore/AzCore/azcore_files.cmake
+++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake
@@ -633,6 +633,8 @@ set(FILES
Task/TaskGraph.cpp
Task/TaskGraph.h
Task/TaskGraph.inl
+ Task/TaskGraphSystemComponent.h
+ Task/TaskGraphSystemComponent.cpp
Threading/ThreadSafeDeque.h
Threading/ThreadSafeDeque.inl
Threading/ThreadSafeObject.h
diff --git a/Code/Framework/AzCore/AzCore/std/parallel/thread.h b/Code/Framework/AzCore/AzCore/std/parallel/thread.h
index 15d8c9dc8e..eef269c8ac 100644
--- a/Code/Framework/AzCore/AzCore/std/parallel/thread.h
+++ b/Code/Framework/AzCore/AzCore/std/parallel/thread.h
@@ -87,12 +87,6 @@ namespace AZStd
// construct/copy/destroy:
thread();
- /**
- * \note thread_desc is AZStd extension.
- */
- template
- explicit thread(F&& f, const thread_desc* desc = 0);
-
~thread();
thread(thread&& rhs)
@@ -108,6 +102,15 @@ namespace AZStd
return *this;
}
+ template, thread_desc>>>
+ explicit thread(F&& f, Args&&... args);
+
+ /**
+ * \note thread_desc is AZStd extension.
+ */
+ template
+ thread(const thread_desc& desc, F&& f, Args&&... args);
+
// Till we fully have RVALUES
template
explicit thread(Internal::thread_move_t f);
@@ -138,8 +141,8 @@ namespace AZStd
//thread(AZStd::delegate d,const thread_desc* desc = 0);
private:
- thread(thread&);
- thread& operator=(thread&);
+ thread(const thread&) = delete;
+ thread& operator=(const thread&) = delete;
native_thread_data_type m_thread;
};
diff --git a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/std/parallel/internal/thread_UnixLike.h b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/std/parallel/internal/thread_UnixLike.h
index 499caebac0..d9a4982a0a 100644
--- a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/std/parallel/internal/thread_UnixLike.h
+++ b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/std/parallel/internal/thread_UnixLike.h
@@ -10,6 +10,8 @@
#include
#include
+#include
+
namespace AZStd
{
namespace Internal
@@ -22,12 +24,20 @@ namespace AZStd
//////////////////////////////////////////////////////////////////////////
// thread
- template
- inline thread::thread(F&& f, const thread_desc* desc)
+ template
+ thread::thread(F&& f, Args&&... args)
+ : thread(thread_desc{}, AZStd::forward(f), AZStd::forward(args)...)
+ {}
+
+ template
+ thread::thread(const thread_desc& desc, F&& f, Args&&... args)
{
- Internal::thread_info* ti = Internal::create_thread_info(AZStd::forward(f));
- ti->m_name = desc ? desc->m_name : nullptr;
- m_thread = Internal::create_thread(desc, ti);
+ auto threadfunc = [fn = AZStd::forward(f), argsTuple = AZStd::make_tuple(AZStd::forward(args)...)]() mutable -> void
+ {
+ AZStd::apply(AZStd::move(fn), AZStd::move(argsTuple));
+ };
+ Internal::thread_info* ti = Internal::create_thread_info(AZStd::move(threadfunc));
+ m_thread = Internal::create_thread(&desc, ti);
}
inline bool thread::joinable() const
diff --git a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/std/parallel/internal/thread_WinAPI.h b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/std/parallel/internal/thread_WinAPI.h
index 46986521e7..c79381a74a 100644
--- a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/std/parallel/internal/thread_WinAPI.h
+++ b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/std/parallel/internal/thread_WinAPI.h
@@ -18,6 +18,8 @@ extern "C"
AZ_DLL_IMPORT unsigned long __stdcall GetCurrentThreadId(void);
}
+#include
+
namespace AZStd
{
namespace Internal
@@ -30,11 +32,20 @@ namespace AZStd
//////////////////////////////////////////////////////////////////////////
// thread
- template
- inline thread::thread(F&& f, const thread_desc* desc)
+ template
+ thread::thread(F&& f, Args&&... args)
+ : thread(thread_desc{}, AZStd::forward(f), AZStd::forward(args)...)
+ {}
+
+ template
+ thread::thread(const thread_desc& desc, F&& f, Args&&... args)
{
- Internal::thread_info* ti = Internal::create_thread_info(AZStd::forward(f));
- m_thread.m_handle = Internal::create_thread(desc, ti, &m_thread.m_id);
+ auto threadfunc = [fn = AZStd::forward(f), argsTuple = AZStd::make_tuple(AZStd::forward(args)...)]() mutable -> void
+ {
+ AZStd::apply(AZStd::move(fn), AZStd::move(argsTuple));
+ };
+ Internal::thread_info* ti = Internal::create_thread_info(AZStd::move(threadfunc));
+ m_thread.m_handle = Internal::create_thread(&desc, ti, &m_thread.m_id);
}
inline bool thread::joinable() const
diff --git a/Code/Framework/AzCore/Tests/AZStd/Parallel.cpp b/Code/Framework/AzCore/Tests/AZStd/Parallel.cpp
index f3d4f58250..407cd3c258 100644
--- a/Code/Framework/AzCore/Tests/AZStd/Parallel.cpp
+++ b/Code/Framework/AzCore/Tests/AZStd/Parallel.cpp
@@ -195,18 +195,18 @@ namespace UnitTest
void test_thread_id_for_running_thread_is_not_default_constructed_id()
{
- const thread_desc* desc = m_numThreadDesc ? &m_desc[0] : nullptr;
- AZStd::thread t(AZStd::bind(&Parallel_Thread::do_nothing, this), desc);
+ const thread_desc desc = m_numThreadDesc ? m_desc[0] : thread_desc{};
+ AZStd::thread t(desc, AZStd::bind(&Parallel_Thread::do_nothing, this));
AZ_TEST_ASSERT(t.get_id() != AZStd::thread::id());
t.join();
}
void test_different_threads_have_different_ids()
{
- const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
- const thread_desc* desc2 = m_numThreadDesc ? &m_desc[1] : nullptr;
- AZStd::thread t(AZStd::bind(&Parallel_Thread::do_nothing, this), desc1);
- AZStd::thread t2(AZStd::bind(&Parallel_Thread::do_nothing, this), desc2);
+ const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
+ const thread_desc desc2 = m_numThreadDesc ? m_desc[1] : thread_desc{};
+ AZStd::thread t(desc1, AZStd::bind(&Parallel_Thread::do_nothing, this));
+ AZStd::thread t2(desc2, AZStd::bind(&Parallel_Thread::do_nothing, this));
AZ_TEST_ASSERT(t.get_id() != t2.get_id());
t.join();
t2.join();
@@ -214,13 +214,13 @@ namespace UnitTest
void test_thread_ids_have_a_total_order()
{
- const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
- const thread_desc* desc2 = m_numThreadDesc ? &m_desc[1] : nullptr;
- const thread_desc* desc3 = m_numThreadDesc ? &m_desc[2] : nullptr;
+ const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
+ const thread_desc desc2 = m_numThreadDesc ? m_desc[1] : thread_desc{};
+ const thread_desc desc3 = m_numThreadDesc ? m_desc[2] : thread_desc{};
- AZStd::thread t(AZStd::bind(&Parallel_Thread::do_nothing, this), desc1);
- AZStd::thread t2(AZStd::bind(&Parallel_Thread::do_nothing, this), desc2);
- AZStd::thread t3(AZStd::bind(&Parallel_Thread::do_nothing, this), desc3);
+ AZStd::thread t(desc1, AZStd::bind(&Parallel_Thread::do_nothing, this));
+ AZStd::thread t2(desc2, AZStd::bind(&Parallel_Thread::do_nothing, this));
+ AZStd::thread t3(desc3, AZStd::bind(&Parallel_Thread::do_nothing, this));
AZ_TEST_ASSERT(t.get_id() != t2.get_id());
AZ_TEST_ASSERT(t.get_id() != t3.get_id());
AZ_TEST_ASSERT(t2.get_id() != t3.get_id());
@@ -313,10 +313,10 @@ namespace UnitTest
void test_thread_id_of_running_thread_returned_by_this_thread_get_id()
{
- const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
+ const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
AZStd::thread::id id;
- AZStd::thread t(AZStd::bind(&Parallel_Thread::get_thread_id, this, &id), desc1);
+ AZStd::thread t(desc1, AZStd::bind(&Parallel_Thread::get_thread_id, this, &id));
AZStd::thread::id t_id = t.get_id();
t.join();
AZ_TEST_ASSERT(id == t_id);
@@ -366,10 +366,10 @@ namespace UnitTest
void test_move_on_construction()
{
- const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
+ const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
AZStd::thread::id the_id;
AZStd::thread x;
- x = AZStd::thread(AZStd::bind(&Parallel_Thread::do_nothing_id, this, &the_id), desc1);
+ x = AZStd::thread(desc1, AZStd::bind(&Parallel_Thread::do_nothing_id, this, &the_id));
AZStd::thread::id x_id = x.get_id();
x.join();
AZ_TEST_ASSERT(the_id == x_id);
@@ -377,8 +377,8 @@ namespace UnitTest
AZStd::thread make_thread(AZStd::thread::id* the_id)
{
- const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
- return AZStd::thread(AZStd::bind(&Parallel_Thread::do_nothing_id, this, the_id), desc1);
+ const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
+ return AZStd::thread(desc1, AZStd::bind(&Parallel_Thread::do_nothing_id, this, the_id));
}
void test_move_from_function_return()
@@ -430,9 +430,9 @@ namespace UnitTest
void do_test_creation()
{
- const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
+ const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
m_data = 0;
- AZStd::thread t(AZStd::bind(&Parallel_Thread::simple_thread, this), desc1);
+ AZStd::thread t(desc1, AZStd::bind(&Parallel_Thread::simple_thread, this));
t.join();
AZ_TEST_ASSERT(m_data == 999);
}
@@ -445,9 +445,9 @@ namespace UnitTest
void do_test_id_comparison()
{
- const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
+ const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
AZStd::thread::id self = this_thread::get_id();
- AZStd::thread thrd(AZStd::bind(&Parallel_Thread::comparison_thread, this, self), desc1);
+ AZStd::thread thrd(desc1, AZStd::bind(&Parallel_Thread::comparison_thread, this, self));
thrd.join();
}
@@ -476,10 +476,10 @@ namespace UnitTest
void do_test_creation_through_reference_wrapper()
{
- const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
+ const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
non_copyable_functor f;
- AZStd::thread thrd(AZStd::ref(f), desc1);
+ AZStd::thread thrd(desc1, AZStd::ref(f));
thrd.join();
AZ_TEST_ASSERT(f.value == 999);
}
@@ -491,10 +491,10 @@ namespace UnitTest
void test_swap()
{
- const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
- const thread_desc* desc2 = m_numThreadDesc ? &m_desc[1] : nullptr;
- AZStd::thread t(AZStd::bind(&Parallel_Thread::simple_thread, this), desc1);
- AZStd::thread t2(AZStd::bind(&Parallel_Thread::simple_thread, this), desc2);
+ const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
+ const thread_desc desc2 = m_numThreadDesc ? m_desc[1] : thread_desc{};
+ AZStd::thread t(desc1, AZStd::bind(&Parallel_Thread::simple_thread, this));
+ AZStd::thread t2(desc2, AZStd::bind(&Parallel_Thread::simple_thread, this));
AZStd::thread::id id1 = t.get_id();
AZStd::thread::id id2 = t2.get_id();
@@ -512,7 +512,7 @@ namespace UnitTest
void run()
{
- const thread_desc* desc1 = m_numThreadDesc ? &m_desc[0] : nullptr;
+ const thread_desc desc1 = m_numThreadDesc ? m_desc[0] : thread_desc{};
// We need to have at least one processor
AZ_TEST_ASSERT(AZStd::thread::hardware_concurrency() >= 1);
@@ -520,18 +520,18 @@ namespace UnitTest
// Create thread to increment data till we need to
m_data = 0;
m_dataMax = 10;
- AZStd::thread tr(AZStd::bind(&Parallel_Thread::increment_data, this), desc1);
+ AZStd::thread tr(desc1, AZStd::bind(&Parallel_Thread::increment_data, this));
tr.join();
AZ_TEST_ASSERT(m_data == m_dataMax);
m_data = 0;
- AZStd::thread trDel(make_delegate(this, &Parallel_Thread::increment_data), desc1);
+ AZStd::thread trDel(desc1, make_delegate(this, &Parallel_Thread::increment_data));
trDel.join();
AZ_TEST_ASSERT(m_data == m_dataMax);
chrono::system_clock::time_point startTime = chrono::system_clock::now();
{
- AZStd::thread tr1(AZStd::bind(&Parallel_Thread::sleep_thread, this, chrono::milliseconds(100)), desc1);
+ AZStd::thread tr1(desc1, AZStd::bind(&Parallel_Thread::sleep_thread, this, chrono::milliseconds(100)));
tr1.join();
}
auto sleepTime = chrono::system_clock::now() - startTime;
@@ -563,71 +563,71 @@ namespace UnitTest
{
MfTest x;
AZStd::function func = AZStd::bind(&MfTest::f0, &x);
- AZStd::thread(func, desc1).join();
+ AZStd::thread(desc1, func).join();
func = AZStd::bind(&MfTest::f0, AZStd::ref(x));
- AZStd::thread(func, desc1).join();
+ AZStd::thread(desc1, func).join();
func = AZStd::bind(&MfTest::g0, &x);
- AZStd::thread(func, desc1).join();
+ AZStd::thread(desc1, func).join();
func = AZStd::bind(&MfTest::g0, x);
- AZStd::thread(func, desc1).join();
+ AZStd::thread(desc1, func).join();
func = AZStd::bind(&MfTest::g0, AZStd::ref(x));
- AZStd::thread(func, desc1).join();
+ AZStd::thread(desc1, func).join();
//// 1
- //thread( AZStd::bind(&MfTest::f1, &x, 1) , desc1).join();
- //thread( AZStd::bind(&MfTest::f1, AZStd::ref(x), 1) , desc1).join();
- //thread( AZStd::bind(&MfTest::g1, &x, 1) , desc1).join();
- //thread( AZStd::bind(&MfTest::g1, x, 1) , desc1).join();
- //thread( AZStd::bind(&MfTest::g1, AZStd::ref(x), 1) , desc1).join();
+ //thread( AZStd::bind(desc1, &MfTest::f1, &x, 1)).join();
+ //thread( AZStd::bind(desc1, &MfTest::f1, AZStd::ref(x), 1)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g1, &x, 1)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g1, x, 1)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g1, AZStd::ref(x), 1)).join();
//// 2
- //thread( AZStd::bind(&MfTest::f2, &x, 1, 2) , desc1).join();
- //thread( AZStd::bind(&MfTest::f2, AZStd::ref(x), 1, 2) , desc1).join();
- //thread( AZStd::bind(&MfTest::g2, &x, 1, 2) , desc1).join();
- //thread( AZStd::bind(&MfTest::g2, x, 1, 2) , desc1).join();
- //thread( AZStd::bind(&MfTest::g2, AZStd::ref(x), 1, 2) , desc1).join();
+ //thread( AZStd::bind(desc1, &MfTest::f2, &x, 1, 2)).join();
+ //thread( AZStd::bind(desc1, &MfTest::f2, AZStd::ref(x), 1, 2)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g2, &x, 1, 2)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g2, x, 1, 2)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g2, AZStd::ref(x), 1, 2)).join();
//// 3
- //thread( AZStd::bind(&MfTest::f3, &x, 1, 2, 3) , desc1).join();
- //thread( AZStd::bind(&MfTest::f3, AZStd::ref(x), 1, 2, 3) , desc1).join();
- //thread( AZStd::bind(&MfTest::g3, &x, 1, 2, 3) , desc1).join();
- //thread( AZStd::bind(&MfTest::g3, x, 1, 2, 3) , desc1).join();
- //thread( AZStd::bind(&MfTest::g3, AZStd::ref(x), 1, 2, 3) , desc1).join();
+ //thread( AZStd::bind(desc1, &MfTest::f3, &x, 1, 2, 3)).join();
+ //thread( AZStd::bind(desc1, &MfTest::f3, AZStd::ref(x), 1, 2, 3)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g3, &x, 1, 2, 3)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g3, x, 1, 2, 3)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g3, AZStd::ref(x), 1, 2, 3)).join();
//// 4
- //thread( AZStd::bind(&MfTest::f4, &x, 1, 2, 3, 4) , desc1).join();
- //thread( AZStd::bind(&MfTest::f4, AZStd::ref(x), 1, 2, 3, 4) , desc1).join();
- //thread( AZStd::bind(&MfTest::g4, &x, 1, 2, 3, 4) , desc1).join();
- //thread( AZStd::bind(&MfTest::g4, x, 1, 2, 3, 4) , desc1).join();
- //thread( AZStd::bind(&MfTest::g4, AZStd::ref(x), 1, 2, 3, 4) , desc1).join();
+ //thread( AZStd::bind(desc1, &MfTest::f4, &x, 1, 2, 3, 4)).join();
+ //thread( AZStd::bind(desc1, &MfTest::f4, AZStd::ref(x), 1, 2, 3, 4)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g4, &x, 1, 2, 3, 4)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g4, x, 1, 2, 3, 4)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g4, AZStd::ref(x), 1, 2, 3, 4)).join();
//// 5
- //thread( AZStd::bind(&MfTest::f5, &x, 1, 2, 3, 4, 5) , desc1).join();
- //thread( AZStd::bind(&MfTest::f5, AZStd::ref(x), 1, 2, 3, 4, 5) , desc1).join();
- //thread( AZStd::bind(&MfTest::g5, &x, 1, 2, 3, 4, 5) , desc1).join();
- //thread( AZStd::bind(&MfTest::g5, x, 1, 2, 3, 4, 5) , desc1).join();
- //thread( AZStd::bind(&MfTest::g5, AZStd::ref(x), 1, 2, 3, 4, 5) , desc1).join();
+ //thread( AZStd::bind(desc1, &MfTest::f5, &x, 1, 2, 3, 4, 5)).join();
+ //thread( AZStd::bind(desc1, &MfTest::f5, AZStd::ref(x), 1, 2, 3, 4, 5)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g5, &x, 1, 2, 3, 4, 5)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g5, x, 1, 2, 3, 4, 5)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g5, AZStd::ref(x), 1, 2, 3, 4, 5)).join();
//// 6
- //thread( AZStd::bind(&MfTest::f6, &x, 1, 2, 3, 4, 5, 6) , desc1).join();
- //thread( AZStd::bind(&MfTest::f6, AZStd::ref(x), 1, 2, 3, 4, 5, 6) , desc1).join();
- //thread( AZStd::bind(&MfTest::g6, &x, 1, 2, 3, 4, 5, 6) , desc1).join();
- //thread( AZStd::bind(&MfTest::g6, x, 1, 2, 3, 4, 5, 6) , desc1).join();
- //thread( AZStd::bind(&MfTest::g6, AZStd::ref(x), 1, 2, 3, 4, 5, 6) , desc1).join();
+ //thread( AZStd::bind(desc1, &MfTest::f6, &x, 1, 2, 3, 4, 5, 6)).join();
+ //thread( AZStd::bind(desc1, &MfTest::f6, AZStd::ref(x), 1, 2, 3, 4, 5, 6)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g6, &x, 1, 2, 3, 4, 5, 6)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g6, x, 1, 2, 3, 4, 5, 6)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g6, AZStd::ref(x), 1, 2, 3, 4, 5, 6)).join();
//// 7
- //thread( AZStd::bind(&MfTest::f7, &x, 1, 2, 3, 4, 5, 6, 7), desc1).join();
- //thread( AZStd::bind(&MfTest::f7, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7), desc1).join();
- //thread( AZStd::bind(&MfTest::g7, &x, 1, 2, 3, 4, 5, 6, 7), desc1).join();
- //thread( AZStd::bind(&MfTest::g7, x, 1, 2, 3, 4, 5, 6, 7), desc1).join();
- //thread( AZStd::bind(&MfTest::g7, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7), desc1).join();
+ //thread( AZStd::bind(desc1, &MfTest::f7, &x, 1, 2, 3, 4, 5, 6, 7)).join();
+ //thread( AZStd::bind(desc1, &MfTest::f7, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g7, &x, 1, 2, 3, 4, 5, 6, 7)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g7, x, 1, 2, 3, 4, 5, 6, 7)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g7, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7)).join();
//// 8
- //thread( AZStd::bind(&MfTest::f8, &x, 1, 2, 3, 4, 5, 6, 7, 8) , desc1).join();
- //thread( AZStd::bind(&MfTest::f8, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7, 8) , desc1).join();
- //thread( AZStd::bind(&MfTest::g8, &x, 1, 2, 3, 4, 5, 6, 7, 8) , desc1).join();
- //thread( AZStd::bind(&MfTest::g8, x, 1, 2, 3, 4, 5, 6, 7, 8) , desc1).join();
- //thread( AZStd::bind(&MfTest::g8, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7, 8) , desc1).join();
+ //thread( AZStd::bind(desc1, &MfTest::f8, &x, 1, 2, 3, 4, 5, 6, 7, 8)).join();
+ //thread( AZStd::bind(desc1, &MfTest::f8, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7, 8)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g8, &x, 1, 2, 3, 4, 5, 6, 7, 8)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g8, x, 1, 2, 3, 4, 5, 6, 7, 8)).join();
+ //thread( AZStd::bind(desc1, &MfTest::g8, AZStd::ref(x), 1, 2, 3, 4, 5, 6, 7, 8)).join();
AZ_TEST_ASSERT(x.m_hash == 1366);
}
diff --git a/Code/Framework/AzCore/Tests/Memory.cpp b/Code/Framework/AzCore/Tests/Memory.cpp
index eb854050b6..5a483c1ed1 100644
--- a/Code/Framework/AzCore/Tests/Memory.cpp
+++ b/Code/Framework/AzCore/Tests/Memory.cpp
@@ -151,7 +151,7 @@ namespace UnitTest
AZStd::thread m_threads[m_maxNumThreads];
for (unsigned int i = 0; i < m_maxNumThreads; ++i)
{
- m_threads[i] = AZStd::thread(AZStd::bind(&SystemAllocatorTest::ThreadFunc, this), &m_desc[i]);
+ m_threads[i] = AZStd::thread(m_desc[i], AZStd::bind(&SystemAllocatorTest::ThreadFunc, this));
// give some time offset to the threads so we can test alloc and dealloc at the same time.
//AZStd::this_thread::sleep_for(AZStd::chrono::microseconds(500));
}
@@ -286,7 +286,7 @@ namespace UnitTest
AZStd::thread m_threads[m_maxNumThreads];
for (unsigned int i = 0; i < m_maxNumThreads; ++i)
{
- m_threads[i] = AZStd::thread(AZStd::bind(&SystemAllocatorTest::ThreadFunc, this), &m_desc[i]);
+ m_threads[i] = AZStd::thread(m_desc[i], AZStd::bind(&SystemAllocatorTest::ThreadFunc, this));
// give some time offset to the threads so we can test alloc and dealloc at the same time.
AZStd::this_thread::sleep_for(AZStd::chrono::microseconds(500));
}
@@ -724,7 +724,7 @@ namespace UnitTest
AZStd::thread m_threads[m_maxNumThreads];
for (unsigned int i = 0; i < m_maxNumThreads; ++i)
{
- m_threads[i] = AZStd::thread(AZStd::bind(&ThreadPoolAllocatorTest::AllocDeallocFunc, this), &m_desc[i]);
+ m_threads[i] = AZStd::thread(m_desc[i], AZStd::bind(&ThreadPoolAllocatorTest::AllocDeallocFunc, this));
}
for (unsigned int i = 0; i < m_maxNumThreads; ++i)
@@ -743,12 +743,12 @@ namespace UnitTest
for (unsigned int i = m_maxNumThreads/2; i ::Create();
AZ::AllocatorInstance::Create();
- m_executor = aznew TaskExecutor(4);
+ m_executor = aznew TaskExecutor();
}
void TearDown() override
@@ -236,6 +236,82 @@ namespace UnitTest
EXPECT_EQ(x, 1);
}
+ TEST_F(TaskGraphTestFixture, SingleTask)
+ {
+ AZStd::atomic_int32_t x = 0;
+
+ TaskGraph graph;
+ graph.AddTask(
+ defaultTD,
+ [&x]
+ {
+ x = 1;
+ });
+
+ TaskGraphEvent ev;
+ graph.SubmitOnExecutor(*m_executor, &ev);
+ ev.Wait();
+
+ EXPECT_EQ(1, x);
+ }
+
+
+ TEST_F(TaskGraphTestFixture, SingleTaskChain)
+ {
+ AZStd::atomic_int32_t x = 0;
+
+ TaskGraph graph;
+ auto a = graph.AddTask(
+ defaultTD,
+ [&x]
+ {
+ x += 1;
+ });
+ auto b = graph.AddTask(
+ defaultTD,
+ [&x]
+ {
+ x += 1;
+ });
+ b.Precedes(a);
+
+ TaskGraphEvent ev;
+ graph.SubmitOnExecutor(*m_executor, &ev);
+ ev.Wait();
+
+ EXPECT_EQ(2, x);
+ }
+
+ TEST_F(TaskGraphTestFixture, MultipleIndependentTaskChains)
+ {
+ AZStd::atomic_int32_t x = 0;
+ constexpr int numChains = 5;
+
+ TaskGraph graph;
+ for( int i = 0; i < numChains; ++i)
+ {
+ auto a = graph.AddTask(
+ defaultTD,
+ [&x]
+ {
+ x += 1;
+ });
+ auto b = graph.AddTask(
+ defaultTD,
+ [&x]
+ {
+ x += 1;
+ });
+ b.Precedes(a);
+ }
+
+ TaskGraphEvent ev;
+ graph.SubmitOnExecutor(*m_executor, &ev);
+ ev.Wait();
+
+ EXPECT_EQ(2*numChains, x);
+ }
+
TEST_F(TaskGraphTestFixture, VariadicInterface)
{
int x = 0;
@@ -388,6 +464,7 @@ namespace UnitTest
EXPECT_EQ(3, x);
}
+ // Waiting inside a task is disallowed , test that it fails correctly
TEST_F(TaskGraphTestFixture, SpawnSubgraph)
{
AZStd::atomic x = 0;
@@ -434,7 +511,10 @@ namespace UnitTest
f.Precedes(g);
TaskGraphEvent ev;
subgraph.SubmitOnExecutor(*m_executor, &ev);
+ // TaskGraphEvent::Wait asserts if called on a worker thread, suppress & validate assert
+ AZ_TEST_START_TRACE_SUPPRESSION;
ev.Wait();
+ AZ_TEST_STOP_TRACE_SUPPRESSION(1);
});
auto d = graph.AddTask(
defaultTD,
@@ -464,8 +544,6 @@ namespace UnitTest
TaskGraphEvent ev;
graph.SubmitOnExecutor(*m_executor, &ev);
ev.Wait();
-
- EXPECT_EQ(3 | 0b100000, x);
}
TEST_F(TaskGraphTestFixture, RetainedGraph)
diff --git a/Code/Framework/AzCore/Tests/Time/TimeTests.cpp b/Code/Framework/AzCore/Tests/Time/TimeTests.cpp
new file mode 100644
index 0000000000..6727ef1501
--- /dev/null
+++ b/Code/Framework/AzCore/Tests/Time/TimeTests.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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
+#include
+
+namespace UnitTest
+{
+ class TimeTests
+ : public AllocatorsFixture
+ {
+ public:
+ void SetUp() override
+ {
+ SetupAllocator();
+ m_timeComponent = new AZ::TimeSystemComponent;
+ }
+
+ void TearDown() override
+ {
+ delete m_timeComponent;
+ TeardownAllocator();
+ }
+
+ AZ::TimeSystemComponent* m_timeComponent = nullptr;
+ };
+
+ TEST_F(TimeTests, TestConversionUsToMs)
+ {
+ AZ::TimeUs timeUs = AZ::TimeUs{ 1000 };
+ AZ::TimeMs timeMs = AZ::TimeUsToMs(timeUs);
+ EXPECT_EQ(timeMs, AZ::TimeMs{ 1 });
+ }
+
+ TEST_F(TimeTests, TestConversionMsToUs)
+ {
+ AZ::TimeMs timeMs = AZ::TimeMs{ 1000 };
+ AZ::TimeUs timeUs = AZ::TimeMsToUs(timeMs);
+ EXPECT_EQ(timeUs, AZ::TimeUs{ 1000000 });
+ }
+
+ TEST_F(TimeTests, TestClocks)
+ {
+ AZ::TimeUs timeUs = AZ::GetElapsedTimeUs();
+ AZ::TimeMs timeMs = AZ::GetElapsedTimeMs();
+
+ AZ::TimeMs timeUsToMs = AZ::TimeUsToMs(timeUs);
+ int64_t delta = static_cast(timeMs) - static_cast(timeUsToMs);
+ EXPECT_LT(abs(delta), 1);
+ }
+}
diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake
index c36d37d874..a111af0353 100644
--- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake
+++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake
@@ -127,6 +127,7 @@ set(FILES
Serialization/Json/UnorderedSetSerializerTests.cpp
Serialization/Json/UnsupportedTypesSerializerTests.cpp
Serialization/Json/UuidSerializerTests.cpp
+ Time/TimeTests.cpp
Math/AabbTests.cpp
Math/ColorTests.cpp
Math/CrcTests.cpp
diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp
index 12974d03cf..e72e2de472 100644
--- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
#include
#include
@@ -295,6 +296,7 @@ namespace AzFramework
azrtti_typeid(),
azrtti_typeid(),
azrtti_typeid(),
+ azrtti_typeid(),
azrtti_typeid(),
azrtti_typeid(),
@@ -477,14 +479,16 @@ namespace AzFramework
newThreadDesc.m_cpuId = AFFINITY_MASK_USERTHREADS;
newThreadDesc.m_name = newThreadName;
AZStd::binary_semaphore binarySemaphore;
- AZStd::thread newThread([&workForNewThread, &binarySemaphore, &newThreadName]
- {
- AZ_PROFILE_SCOPE(AzFramework,
- "Application::PumpSystemEventLoopWhileDoingWorkInNewThread:ThreadWorker %s", newThreadName);
+ AZStd::thread newThread(
+ newThreadDesc,
+ [&workForNewThread, &binarySemaphore, &newThreadName]
+ {
+ AZ_PROFILE_SCOPE(AzFramework,
+ "Application::PumpSystemEventLoopWhileDoingWorkInNewThread:ThreadWorker %s", newThreadName);
- workForNewThread();
- binarySemaphore.release();
- }, &newThreadDesc);
+ workForNewThread();
+ binarySemaphore.release();
+ });
while (!binarySemaphore.try_acquire_for(eventPumpFrequency))
{
PumpSystemEventLoopUntilEmpty();
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp
index d2a81102dc..8064ba6669 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp
@@ -1631,7 +1631,20 @@ namespace AZ::IO
return nullptr;
}
- ZipDir::CacheFactory factory(ZipDir::ZD_INIT_FAST, nFactoryFlags);
+ ZipDir::InitMethod initType = ZipDir::InitMethod::Default;
+ if (!ZipDir::IsReleaseConfig)
+ {
+ if ((nFlags & INestedArchive::FLAGS_FULL_VALIDATE) != 0)
+ {
+ initType = ZipDir::InitMethod::FullValidation;
+ }
+ else if ((nFlags & INestedArchive::FLAGS_VALIDATE_HEADERS) != 0)
+ {
+ initType = ZipDir::InitMethod::ValidateHeaders;
+ }
+ }
+
+ ZipDir::CacheFactory factory(initType, nFactoryFlags);
ZipDir::CachePtr cache = factory.New(szFullPath->c_str());
if (cache)
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/INestedArchive.h b/Code/Framework/AzFramework/AzFramework/Archive/INestedArchive.h
index f85fd273ce..e89d16ee5d 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/INestedArchive.h
+++ b/Code/Framework/AzFramework/AzFramework/Archive/INestedArchive.h
@@ -11,7 +11,9 @@
#include
#include
+#include
#include
+#include
#include
namespace AZ::IO
@@ -71,6 +73,13 @@ namespace AZ::IO
// multiple times
FLAGS_DONT_COMPACT = 1 << 5,
+ // if this is set, validate header data when opening the archive
+ FLAGS_VALIDATE_HEADERS = 1 << 9,
+
+ // if this is set, validate header data when opening the archive and validate CRCs when decompressing
+ // & reading files.
+ FLAGS_FULL_VALIDATE = 1 << 10,
+
// Disable a pak file without unloading it, this flag is used in combination with patches and multiplayer
// to ensure that specific paks stay in the position(to keep the same priority) but being disabled
// when running multiplayer
@@ -128,6 +137,10 @@ namespace AZ::IO
// Deletes all files and directories in the archive.
virtual int RemoveAll() = 0;
+ // Summary:
+ // Lists all the files in the archive.
+ virtual int ListAllFiles(AZStd::vector& outFileEntries) = 0;
+
// Summary:
// Finds the file; you don't have to close the returned handle.
// Returns:
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.cpp
index 1e0f237df5..49a44b76fa 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.cpp
@@ -89,11 +89,51 @@ namespace AZ::IO
return m_pCache->RemoveDir(fullPath);
}
+ //////////////////////////////////////////////////////////////////////////
int NestedArchive::RemoveAll()
{
return m_pCache->RemoveAll();
}
+ //////////////////////////////////////////////////////////////////////////
+ // Helper for 'ListAllFiles' to recursively traverse the FileEntryTree and gather all the files
+ void EnumerateFilesRecursive(AZ::IO::Path currentPath, ZipDir::FileEntryTree* currentTree, AZStd::vector& fileList)
+ {
+ // Drill down directories first...
+ for (auto dirIter = currentTree->GetDirBegin(); dirIter != currentTree->GetDirEnd(); ++dirIter)
+ {
+ if (ZipDir::FileEntryTree* subTree = currentTree->GetDirEntry(dirIter);
+ subTree != nullptr)
+ {
+ EnumerateFilesRecursive(currentPath / currentTree->GetDirName(dirIter), subTree, fileList);
+ }
+ }
+
+ // Then enumerate the files in current directory...
+ for (auto fileIter = currentTree->GetFileBegin(); fileIter != currentTree->GetFileEnd(); ++fileIter)
+ {
+ fileList.emplace_back(currentPath / currentTree->GetFileName(fileIter));
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // lists all files in the archive
+ int NestedArchive::ListAllFiles(AZStd::vector& outFileEntries)
+ {
+ AZStd::vector filesInArchive;
+
+ ZipDir::FileEntryTree* tree = m_pCache->GetRoot();
+ if (!tree)
+ {
+ return ZipDir::ZD_ERROR_UNEXPECTED;
+ }
+
+ EnumerateFilesRecursive(AZ::IO::Path{ AZ::IO::PosixPathSeparator }, tree, filesInArchive);
+
+ AZStd::swap(outFileEntries, filesInArchive);
+ return ZipDir::ZD_ERROR_SUCCESS;
+ }
+
//////////////////////////////////////////////////////////////////////////
// Adds a new file to the zip or update an existing one
// adds a directory (creates several nested directories if needed)
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.h b/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.h
index 34bbcdc201..59722703f2 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.h
+++ b/Code/Framework/AzFramework/AzFramework/Archive/NestedArchive.h
@@ -39,7 +39,7 @@ namespace AZ::IO
NestedArchive(IArchive* pArchive, AZStd::string_view strBindRoot, ZipDir::CachePtr pCache, uint32_t nFlags = 0);
~NestedArchive() override;
-
+
auto GetRootFolderHandle() -> Handle override;
// Adds a new file to the zip or update an existing one
@@ -68,6 +68,9 @@ namespace AZ::IO
// deletes all files from the archive
int RemoveAll() override;
+ // lists all the files in the archive
+ int ListAllFiles(AZStd::vector& outFileEntries) override;
+
// finds the file; you don't have to close the returned handle
Handle FindFile(AZStd::string_view szRelativePath) override;
@@ -79,7 +82,6 @@ namespace AZ::IO
// returns the full path to the archive file
AZ::IO::PathView GetFullPath() const override;
- ZipDir::Cache* GetCache();
uint32_t GetFlags() const override;
bool SetFlags(uint32_t nFlagsToSet) override;
@@ -87,12 +89,15 @@ namespace AZ::IO
bool SetPackAccessible(bool bAccessible) override;
+ ZipDir::Cache* GetCache();
+
protected:
// returns the pointer to the relative file path to be passed
// to the underlying Cache pointer. Uses the given buffer to construct the path.
// returns nullptr if the file path is invalid
AZ::IO::FixedMaxPathString AdjustPath(AZStd::string_view szRelativePath);
+
ZipDir::CachePtr m_pCache;
// the binding root may be empty string - in this case, the absolute path binding won't work
AZ::IO::Path m_strBindRoot;
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.cpp
index d17dbd0837..13d5b0f723 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCache.cpp
@@ -101,10 +101,11 @@ namespace AZ::IO::ZipDir
FileEntry* operator -> () { return m_pFileEntry; }
FileEntryTransactionAdd(Cache* pCache, AZStd::string_view szRelativePath)
: m_pCache(pCache)
+ , m_szRelativePath(AZ::IO::PosixPathSeparator)
, m_bCommitted(false)
{
// Update the cache string pool with the relative path to the file
- auto pathIt = m_pCache->m_relativePathPool.emplace(AZ::IO::PathView(szRelativePath).LexicallyNormal());
+ auto pathIt = m_pCache->m_relativePathPool.emplace(AZ::IO::PathView(szRelativePath, AZ::IO::PosixPathSeparator).LexicallyNormal());
m_szRelativePath = *pathIt.first;
// this is the name of the directory - create it or find it
m_pFileEntry = m_pCache->GetRoot()->Add(m_szRelativePath.Native());
@@ -740,6 +741,16 @@ namespace AZ::IO::ZipDir
{
return ZD_ERROR_CORRUPTED_DATA;
}
+ if (pFileEntry->bCheckCRCNextRead)
+ {
+ pFileEntry->bCheckCRCNextRead = false;
+ uLong uCRC32 = AZ::Crc32((Bytef*)pUncompressed, nSizeUncompressed);
+ if (uCRC32 != pFileEntry->desc.lCRC32)
+ {
+ AZ_Warning("Archive", false, "ZD_ERROR_CRC32_CHECK: Uncompressed stream CRC32 check failed");
+ return ZD_ERROR_CRC32_CHECK;
+ }
+ }
}
}
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.cpp
index 5c5e93d441..d5bd4d2840 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.cpp
@@ -29,7 +29,7 @@ namespace AZ::IO::ZipDir
// this sets the window size of the blocks of data read from the end of the file to find the Central Directory Record
// since normally there are no
static constexpr size_t CDRSearchWindowSize = 0x100;
- CacheFactory::CacheFactory(InitMethodEnum nInitMethod, uint32_t nFlags)
+ CacheFactory::CacheFactory(InitMethod nInitMethod, uint32_t nFlags)
{
m_nCDREndPos = 0;
m_bBuildFileEntryMap = false; // we only need it for validation/debugging
@@ -448,7 +448,6 @@ namespace AZ::IO::ZipDir
// builds up the m_mapFileEntries
bool CacheFactory::BuildFileEntryMap()
{
-
Seek(m_CDREnd.lCDROffset);
if (m_CDREnd.lCDRSize == 0)
@@ -530,14 +529,6 @@ namespace AZ::IO::ZipDir
{
// Add this file entry.
char* str = reinterpret_cast(pFileName);
- for (int i = 0; i < pFile->nFileNameLength; i++)
- {
- str[i] = std::tolower(str[i], std::locale());
- if (str[i] == AZ_WRONG_FILESYSTEM_SEPARATOR)
- {
- str[i] = AZ_CORRECT_FILESYSTEM_SEPARATOR;
- }
- }
str[pFile->nFileNameLength] = 0; // Not standard!, may overwrite signature of the next memory record data in zip.
AddFileEntry(str, pFile, extra);
}
@@ -574,11 +565,7 @@ namespace AZ::IO::ZipDir
FileEntryBase fileEntry(*pFileHeader, extra);
- // when using encrypted headers we should always initialize data offsets from CDR
- if ((m_encryptedHeaders != ZipFile::HEADERS_NOT_ENCRYPTED || m_nInitMethod >= ZD_INIT_FULL) && pFileHeader->desc.lSizeCompressed)
- {
- InitDataOffset(fileEntry, pFileHeader);
- }
+ InitDataOffset(fileEntry, pFileHeader);
if (m_bBuildFileEntryMap)
{
@@ -606,142 +593,81 @@ namespace AZ::IO::ZipDir
{
Seek(pFileHeader->lLocalHeaderOffset);
- // read the local file header and the name (for validation) into the buffer
- AZStd::vectorpBuffer;
- uint32_t nBufferLength = sizeof(ZipFile::LocalFileHeader) + pFileHeader->nFileNameLength;
- pBuffer.resize(nBufferLength);
- Read(&pBuffer[0], nBufferLength);
+ // Read only the LocalFileHeader w/ no additional bytes ('name' or 'extra' fields)
+ AZStd::vector buffer;
+ uint32_t bufferLen = sizeof(ZipFile::LocalFileHeader);
+ buffer.resize_no_construct(bufferLen);
+ Read(buffer.data(), bufferLen);
- // validate the local file header (compare with the CDR file header - they should contain basically the same information)
- const auto* pLocalFileHeader = reinterpret_cast(&pBuffer[0]);
- if (pFileHeader->desc != pLocalFileHeader->desc
- || pFileHeader->nMethod != pLocalFileHeader->nMethod
- || pFileHeader->nFileNameLength != pLocalFileHeader->nFileNameLength
- // for a tough validation, we can compare the timestamps of the local and central directory entries
- // but we won't do that for backward compatibility with ZipDir
- //|| pFileHeader->nLastModDate != pLocalFileHeader->nLastModDate
- //|| pFileHeader->nLastModTime != pLocalFileHeader->nLastModTime
- )
+ const auto* localFileHeader = reinterpret_cast(buffer.data());
+
+ // set the correct file data offset...
+ fileEntry.nFileDataOffset = pFileHeader->lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) +
+ localFileHeader->nFileNameLength + localFileHeader->nExtraFieldLength;
+
+ fileEntry.nEOFOffset = fileEntry.nFileDataOffset + fileEntry.desc.lSizeCompressed;
+
+ if (m_nInitMethod != ZipDir::InitMethod::Default)
{
- AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED:"
- " The local file header descriptor doesn't match the basic parameters declared in the global file header in the file."
- " The archive content is misconsistent and may be damaged. Please try to repair the archive");
- return;
+ if (m_nInitMethod == ZipDir::InitMethod::FullValidation)
+ {
+ // Mark the FileEntry to check CRC when the next read occurs
+ fileEntry.bCheckCRCNextRead = true;
+ }
+
+ // Timestamps
+ if (pFileHeader->nLastModDate != localFileHeader->nLastModDate
+ || pFileHeader->nLastModTime != localFileHeader->nLastModTime)
+ {
+ AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED: (%s)\n"
+ " The local file header's modification timestamps don't match that of the global file header in the archive."
+ " The archive timestamps are inconsistent and may be damaged. Check the archive file.", m_szFilename.c_str());
+ // don't return here, it may be ok.
+ }
+
+ // Validate data
+ if (pFileHeader->desc != localFileHeader->desc // this checks CRCs and compressed/uncompressed sizes
+ || pFileHeader->nMethod != localFileHeader->nMethod
+ || pFileHeader->nFileNameLength != localFileHeader->nFileNameLength)
+ {
+ AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED: (%s)\n"
+ " The local file header descriptor doesn't match basic parameters declared in the global file header in the file."
+ " The archive content is inconsistent and may be damaged. Please try to repair the archive.", m_szFilename.c_str());
+ // return here because further checks aren't worse than this.
+ return;
+ }
+
+ // Read extra data
+ uint32_t extraDataLen = localFileHeader->nFileNameLength + localFileHeader->nExtraFieldLength;
+ buffer.resize_no_construct(buffer.size() + extraDataLen);
+ Read(buffer.data() + buffer.size(), extraDataLen);
+
+ // Compare local file name with the CDR file name, they should match
+ AZStd::string_view zipFileName{ buffer.data() + sizeof(ZipFile::LocalFileHeader), localFileHeader->nFileNameLength };
+ AZStd::string_view cdrFileName{ reinterpret_cast(pFileHeader + 1), pFileHeader->nFileNameLength };
+ if (zipFileName != cdrFileName)
+ {
+ AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED: (%s)\n"
+ " The file name in the local file header doesn't match the name in the global file header."
+ " The archive content is inconsisten with the directory. Please check the archive.", m_szFilename.c_str());
+ }
+
+ // CDR and local "extra field" lengths may be different, should we compare them if they are equal?
+
+ // make sure it's the same file and the fileEntry structure is properly initialized
+ AZ_Assert(fileEntry.nFileHeaderOffset == pFileHeader->lLocalHeaderOffset,
+ "The file entry header offset doesn't match the file header local offst (%s)", m_szFilename.c_str());
+
+ if (fileEntry.nFileDataOffset >= m_nCDREndPos)
+ {
+ AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED: (%s)\n"
+ " The global file header declares the file which crosses the boundaries of the archive."
+ " The archive is either corrupted or truncated, please try to repair it", m_szFilename.c_str());
+ }
+
+ // End Validation
}
-
- // now compare the local file name with the one recorded in CDR: they must match.
- auto CompareNoCase = [](const char lhs, const char rhs) { return std::tolower(lhs, std::locale()) == std::tolower(rhs, std::locale()); };
- auto zipFileDataBegin = pBuffer.begin() + sizeof(ZipFile::LocalFileHeader);
- auto zipFileDataEnd = zipFileDataBegin + pFileHeader->nFileNameLength;
- if (!AZStd::equal(zipFileDataBegin, zipFileDataEnd, reinterpret_cast(pFileHeader + 1), CompareNoCase))
- {
- // either file name, or the extra field do not match
- AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED:"
- " The local file header contains file name which does not match the file name of the global file header."
- " The archive content is misconsistent with its directory. Please repair the archive");
- return;
- }
-
- fileEntry.nFileDataOffset = pFileHeader->lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + pLocalFileHeader->nFileNameLength + pLocalFileHeader->nExtraFieldLength;
}
-
- // make sure it's the same file and the fileEntry structure is properly initialized
- AZ_Assert(fileEntry.nFileHeaderOffset == pFileHeader->lLocalHeaderOffset, "The file entry header offset doesn't match the file header local offst");
-
- fileEntry.nEOFOffset = fileEntry.nFileDataOffset + fileEntry.desc.lSizeCompressed;
-
- if (fileEntry.nFileDataOffset >= m_nCDREndPos)
- {
- AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED:"
- " The global file header declares the file which crosses the boundaries of the archive."
- " The archive is either corrupted or truncated, please try to repair it");
- return;
- }
-
- if (m_nInitMethod >= ZD_INIT_VALIDATE)
- {
- Validate(fileEntry);
- }
- }
-
- //////////////////////////////////////////////////////////////////////////
- // reads the file pointed by the given header and entry (they must be coherent)
- // and decompresses it; then calculates and validates its CRC32
- void CacheFactory::Validate(const FileEntryBase& fileEntry)
- {
- AZStd::vector pBuffer;
- // validate the file contents
- // allocate memory for both the compressed data and uncompressed data
- pBuffer.resize(fileEntry.desc.lSizeCompressed + fileEntry.desc.lSizeUncompressed);
- char* pUncompressed = &pBuffer[fileEntry.desc.lSizeCompressed];
- char* pCompressed = &pBuffer[0];
-
- AZ_Assert(fileEntry.nFileDataOffset != FileEntry::INVALID_DATA_OFFSET, "File entry has invalid data offset of %" PRIx32, FileEntry::INVALID_DATA_OFFSET);
- Seek(fileEntry.nFileDataOffset);
-
- Read(pCompressed, fileEntry.desc.lSizeCompressed);
-
- size_t nDestSize = fileEntry.desc.lSizeUncompressed;
- int nError = Z_OK;
- if (fileEntry.nMethod)
- {
- nError = ZipRawUncompress(pUncompressed, &nDestSize, pCompressed, fileEntry.desc.lSizeCompressed);
- }
- else
- {
- AZ_Assert(fileEntry.desc.lSizeCompressed == fileEntry.desc.lSizeUncompressed, "Uncompressed file does not have the same commpressed %u and uncompressed file sizes %u",
- fileEntry.desc.lSizeCompressed, fileEntry.desc.lSizeUncompressed);
- memcpy(pUncompressed, pCompressed, fileEntry.desc.lSizeUncompressed);
- }
- switch (nError)
- {
- case Z_OK:
- break;
- case Z_MEM_ERROR:
- AZ_Warning("Archive", false, "ZD_ERROR_ZLIB_NO_MEMORY: ZLib reported out-of-memory error");
- return;
- case Z_BUF_ERROR:
- AZ_Warning("Archive", false, "ZD_ERROR_ZLIB_CORRUPTED_DATA: ZLib reported compressed stream buffer error");
- return;
- case Z_DATA_ERROR:
- AZ_Warning("Archive", false, "ZD_ERROR_ZLIB_CORRUPTED_DATA: ZLib reported compressed stream data error");
- return;
- default:
- AZ_Warning("Archive", false, "ZD_ERROR_ZLIB_FAILED: ZLib reported an unexpected unknown error");
- return;
- }
-
- if (nDestSize != fileEntry.desc.lSizeUncompressed)
- {
- AZ_Warning("Archive", false, "ZD_ERROR_CORRUPTED_DATA: Uncompressed stream doesn't match the size of uncompressed file stored in the archive file headers");
- return;
- }
-
- uLong uCRC32 = AZ::Crc32((Bytef*)pUncompressed, nDestSize);
- if (uCRC32 != fileEntry.desc.lCRC32)
- {
- AZ_Warning("Archive", false, "ZD_ERROR_CRC32_CHECK: Uncompressed stream CRC32 check failed");
- return;
- }
- }
-
-
- //////////////////////////////////////////////////////////////////////////
- // extracts the file path from the file header with subsequent information
- // may, or may not, put all letters to lower-case (depending on whether the system is to be case-sensitive or not)
- // it's the responsibility of the caller to ensure that the file name is in readable valid memory
- char* CacheFactory::GetFilePath(const char* pFileName, uint16_t nFileNameLength)
- {
- static char strResult[AZ_MAX_PATH_LEN];
- AZ_Assert(nFileNameLength < AZ_MAX_PATH_LEN, "Only filenames shorter than %zu can be copied from filename parameter", AZ_MAX_PATH_LEN);
- memcpy(strResult, pFileName, nFileNameLength);
- strResult[nFileNameLength] = 0;
- for (int i = 0; i < nFileNameLength; i++)
- {
- strResult[i] = std::tolower(strResult[i], std::locale{});
- }
-
- return strResult;
}
// seeks in the file relative to the starting position
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.h b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.h
index c31d4d7dfd..1612829f13 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.h
+++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirCacheFactory.h
@@ -39,7 +39,7 @@ namespace AZ::IO::ZipDir
// initializes the internal structures
// nFlags can have FLAGS_READ_ONLY flag, in this case the object will be opened only for reading
- CacheFactory(InitMethodEnum nInitMethod, uint32_t nFlags = 0);
+ CacheFactory(InitMethod nInitMethod, uint32_t nFlags = 0);
~CacheFactory();
// the new function creates a new cache
@@ -66,28 +66,6 @@ namespace AZ::IO::ZipDir
// This function can actually modify strFilePath variable, make sure you use a copy of the real path.
void AddFileEntry(char* strFilePath, const ZipFile::CDRFileHeader* pFileHeader, const SExtraZipFileData& extra);// throw (ErrorEnum);
- // extracts the file path from the file header with subsequent information
- // may, or may not, put all letters to lower-case (depending on whether the system is to be case-sensitive or not)
- // it's the responsibility of the caller to ensure that the file name is in readable valid memory
- char* GetFilePath(const ZipFile::CDRFileHeader* pFileHeader)
- {
- return GetFilePath((const char*)(pFileHeader + 1), pFileHeader->nFileNameLength);
- }
- // extracts the file path from the file header with subsequent information
- // may, or may not, put all letters to lower-case (depending on whether the system is to be case-sensitive or not)
- // it's the responsibility of the caller to ensure that the file name is in readable valid memory
- char* GetFilePath(const ZipFile::LocalFileHeader* pFileHeader)
- {
- return GetFilePath((const char*)(pFileHeader + 1), pFileHeader->nFileNameLength);
- }
- // extracts the file path from the file header with subsequent information
- // may, or may not, put all letters to lower-case (depending on whether the system is to be case-sensitive or not)
- // it's the responsibility of the caller to ensure that the file name is in readable valid memory
- char* GetFilePath(const char* pFileName, uint16_t nFileNameLength);
-
- // validates (if the init method has the corresponding value) the given file/header
- void Validate(const FileEntryBase& fileEntry);
-
// initializes the actual data offset in the file in the fileEntry structure
// searches to the local file header, reads it and calculates the actual offset in the file
void InitDataOffset(FileEntryBase& fileEntry, const ZipFile::CDRFileHeader* pFileHeader);
@@ -104,7 +82,7 @@ namespace AZ::IO::ZipDir
AZStd::string m_szFilename;
CZipFile m_fileExt;
- InitMethodEnum m_nInitMethod;
+ InitMethod m_nInitMethod;
uint32_t m_nFlags;
ZipFile::CDREnd m_CDREnd;
@@ -129,7 +107,7 @@ namespace AZ::IO::ZipDir
ZipFile::CryCustomEncryptionHeader m_headerEncryption;
ZipFile::CrySignedCDRHeader m_headerSignature;
ZipFile::CryCustomExtendedHeader m_headerExtended;
-
};
+
}
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirList.cpp b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirList.cpp
index 729f394b9d..ab9c356d7f 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirList.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirList.cpp
@@ -68,14 +68,14 @@ namespace AZ::IO::ZipDir
{
for (FileEntryTree::SubdirMap::iterator it = pTree->GetDirBegin(); it != pTree->GetDirEnd(); ++it)
{
- AddAllFiles(it->second.get(), (AZ::IO::Path(strRoot) / it->first).Native());
+ AddAllFiles(it->second.get(), (AZ::IO::Path(strRoot, AZ::IO::PosixPathSeparator) / it->first).Native());
}
for (FileEntryTree::FileMap::iterator it = pTree->GetFileBegin(); it != pTree->GetFileEnd(); ++it)
{
FileRecord rec;
rec.pFileEntryBase = pTree->GetFileEntry(it);
- rec.strPath = (AZ::IO::Path(strRoot) / it->first).Native();
+ rec.strPath = (AZ::IO::Path(strRoot, AZ::IO::PosixPathSeparator) / it->first).Native();
push_back(rec);
}
}
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirStructures.h b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirStructures.h
index 9295a7dd95..c890d498e4 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/ZipDirStructures.h
+++ b/Code/Framework/AzFramework/AzFramework/Archive/ZipDirStructures.h
@@ -119,19 +119,28 @@ namespace AZ::IO::ZipDir
const char* m_szDescription;
};
+#if defined(_RELEASE)
+ inline static constexpr bool IsReleaseConfig{ true };
+#else
+ inline static constexpr bool IsReleaseConfig{};
+#endif // _RELEASE
+
// possible initialization methods
- enum InitMethodEnum
+ enum class InitMethod
{
- // initialize as fast as possible, with minimal validation
- ZD_INIT_FAST,
- // after initialization, scan through all file headers, precache the actual file data offset values and validate the headers
- ZD_INIT_FULL,
- // scan all file headers and try to decompress the data, searching for corrupted files
- ZD_INIT_VALIDATE_IN_MEMORY,
- // store archive in memory
- ZD_INIT_VALIDATE,
- // maximum level of validation, checks for integrity of the archive
- ZD_INIT_VALIDATE_MAX = ZD_INIT_VALIDATE
+ // initializes without any sort of extra validation steps
+ Default,
+
+ // initializes with extra validation steps
+ // not available in RELEASE
+ // will check CDR and local headers data match
+ ValidateHeaders,
+
+ // initializes with extra validation steps
+ // not available in RELEASE
+ // will check CDR and local headers data match
+ // will check file data CRC matches (when file is read)
+ FullValidation,
};
// Uncompresses raw (without wrapping) data that is compressed with method 8 (deflated) in the Zip file
@@ -184,7 +193,11 @@ namespace AZ::IO::ZipDir
// the offset to the start of the next file's header - this
// can be used to calculate the available space in zip file
uint32_t nEOFOffset{};
+
+ // whether to check the CRC upon the next data read
+ bool bCheckCRCNextRead{};
};
+
// this is the record about the file in the Zip file.
struct FileEntry
: FileEntryBase
diff --git a/Code/Framework/AzFramework/AzFramework/Network/AssetProcessorConnection.cpp b/Code/Framework/AzFramework/AzFramework/Network/AssetProcessorConnection.cpp
index 2e1cde443c..74b92a5681 100644
--- a/Code/Framework/AzFramework/AzFramework/Network/AssetProcessorConnection.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Network/AssetProcessorConnection.cpp
@@ -593,7 +593,7 @@ namespace AzFramework
DebugMessage("StartThread: Starting %s", thread.m_desc.m_name);
thread.m_join = false;
- thread.m_thread = AZStd::thread(thread.m_main, &thread.m_desc);
+ thread.m_thread = AZStd::thread(thread.m_desc, thread.m_main);
}
void AssetProcessorConnection::JoinThread(ThreadState& thread, AZStd::condition_variable* wakeUpCondition /* = nullptr */)
diff --git a/Code/Framework/AzFramework/AzFramework/TargetManagement/TargetManagementComponent.cpp b/Code/Framework/AzFramework/AzFramework/TargetManagement/TargetManagementComponent.cpp
index 1c26cbc744..8e6bacda2b 100644
--- a/Code/Framework/AzFramework/AzFramework/TargetManagement/TargetManagementComponent.cpp
+++ b/Code/Framework/AzFramework/AzFramework/TargetManagement/TargetManagementComponent.cpp
@@ -319,7 +319,7 @@ namespace AzFramework
AZStd::thread_desc td;
td.m_name = "TargetManager Thread";
td.m_cpuId = AFFINITY_MASK_USERTHREADS;
- m_threadHandle = AZStd::thread(AZStd::bind(&TargetManagementComponent::TickThread, this), &td);
+ m_threadHandle = AZStd::thread(td, AZStd::bind(&TargetManagementComponent::TickThread, this));
}
void TargetManagementComponent::Deactivate()
diff --git a/Code/Framework/AzFramework/AzFramework/Visibility/EntityBoundsUnionBus.h b/Code/Framework/AzFramework/AzFramework/Visibility/EntityBoundsUnionBus.h
index b6c26e3e9e..862dea38cc 100644
--- a/Code/Framework/AzFramework/AzFramework/Visibility/EntityBoundsUnionBus.h
+++ b/Code/Framework/AzFramework/AzFramework/Visibility/EntityBoundsUnionBus.h
@@ -28,9 +28,12 @@ namespace AzFramework
//! @note This is used to drive event driven updates to the visibility system.
virtual void RefreshEntityLocalBoundsUnion(AZ::EntityId entityId) = 0;
- //! Returns the cached union of all component Aabbs.
+ //! Returns the cached union of all component Aabbs in local entity space.
virtual AZ::Aabb GetEntityLocalBoundsUnion(AZ::EntityId entityId) const = 0;
+ //! Returns the cached union of all component Aabbs in world space.
+ virtual AZ::Aabb GetEntityWorldBoundsUnion(AZ::EntityId entityId) const = 0;
+
//! Writes the current changes made to all entities (transforms and bounds) to the visibility system.
//! @note During normal operation this is called every frame in OnTick but can
//! also be called explicitly (e.g. For testing purposes).
diff --git a/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.cpp b/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.cpp
index 826570fee5..1963411147 100644
--- a/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.cpp
@@ -128,6 +128,24 @@ namespace AzFramework
return AZ::Aabb::CreateNull();
}
+ AZ::Aabb EntityVisibilityBoundsUnionSystem::GetEntityWorldBoundsUnion(const AZ::EntityId entityId) const
+ {
+ AZ::Entity* entity = AZ::Interface::Get()->FindEntity(entityId);
+ if (entity != nullptr)
+ {
+ // if the entity is not found in the mapping then return a null Aabb, this is to mimic
+ // as closely as possible the behavior of an individual GetLocalBounds call to an Entity that
+ // had been deleted (there would be no response, leaving the default value assigned)
+ if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entity);
+ instance_it != m_entityVisibilityBoundsUnionInstanceMapping.end())
+ {
+ return instance_it->second.m_localEntityBoundsUnion.GetTranslated(entity->GetTransform()->GetWorldTranslation());
+ }
+ }
+
+ return AZ::Aabb::CreateNull();
+ }
+
void EntityVisibilityBoundsUnionSystem::ProcessEntityBoundsUnionRequests()
{
AZ_PROFILE_FUNCTION(AzFramework);
diff --git a/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.h b/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.h
index 03109f0dd2..1a3531e506 100644
--- a/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.h
+++ b/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.h
@@ -31,6 +31,7 @@ namespace AzFramework
// EntityBoundsUnionRequestBus overrides ...
void RefreshEntityLocalBoundsUnion(AZ::EntityId entityId) override;
AZ::Aabb GetEntityLocalBoundsUnion(AZ::EntityId entityId) const override;
+ AZ::Aabb GetEntityWorldBoundsUnion(AZ::EntityId entityId) const override;
void ProcessEntityBoundsUnionRequests() override;
void OnTransformUpdated(AZ::Entity* entity) override;
diff --git a/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp b/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp
index 3f2b29a7bb..57b37037ae 100644
--- a/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp
+++ b/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp
@@ -239,6 +239,8 @@ namespace AzFramework
RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes;
AzFramework::RawInputNotificationBusWindows::Broadcast(
&AzFramework::RawInputNotificationBusWindows::Events::OnRawInputEvent, *rawInput);
+
+ delete [] rawInputBytes;
break;
}
case WM_CHAR:
diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h
index 76defd0b32..239af5be0e 100644
--- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h
+++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h
@@ -94,6 +94,7 @@ namespace AzNetworking
class ITimeoutHandler
{
public:
+ virtual ~ITimeoutHandler() = default;
//! Handler callback for timed out items.
//! @param item containing registered timeout details
diff --git a/Code/Framework/AzNetworking/AzNetworking/Framework/INetworkInterface.h b/Code/Framework/AzNetworking/AzNetworking/Framework/INetworkInterface.h
index 09aa62f7d7..3a37a44a02 100644
--- a/Code/Framework/AzNetworking/AzNetworking/Framework/INetworkInterface.h
+++ b/Code/Framework/AzNetworking/AzNetworking/Framework/INetworkInterface.h
@@ -103,13 +103,13 @@ namespace AzNetworking
//! @return boolean true on success
virtual bool Disconnect(ConnectionId connectionId, DisconnectReason reason) = 0;
- //! Sets whether this connection interface can disconnect by virtue of a timeout
- //! @param timeoutEnabled If this connection interface will automatically disconnect due to a timeout
- virtual void SetTimeoutEnabled(bool timeoutEnabled) = 0;
+ //! Sets the timeout time in milliseconds, 0 ms means timeouts are disabled.
+ //! @param timeoutMs the number of milliseconds with no traffic before we timeout and close a connection
+ virtual void SetTimeoutMs(AZ::TimeMs timeoutMs) = 0;
- //! Whether this connection interface will disconnect by virtue of a time out (does not account for cvars affecting all connections)
- //! @return boolean true if this connection will not disconnect on timeout (does not account for cvars affecting all connections)
- virtual bool IsTimeoutEnabled() const = 0;
+ //! Retrieves the timeout time in milliseconds for this network interface, 0 ms means timeouts are disabled.
+ //! @return the timeout time in milliseconds for this network interface, 0 ms means timeouts are disabled
+ virtual AZ::TimeMs GetTimeoutMs() const = 0;
//! Const access to the metrics tracked by this network interface.
//! @return const reference to the metrics tracked by this network interface
diff --git a/Code/Framework/AzNetworking/AzNetworking/Serialization/AzContainerSerializers.h b/Code/Framework/AzNetworking/AzNetworking/Serialization/AzContainerSerializers.h
index 59e0cc8235..e99144acbf 100644
--- a/Code/Framework/AzNetworking/AzNetworking/Serialization/AzContainerSerializers.h
+++ b/Code/Framework/AzNetworking/AzNetworking/Serialization/AzContainerSerializers.h
@@ -321,4 +321,19 @@ namespace AzNetworking
return serializer.IsValid();
}
};
+
+ template <>
+ struct SerializeObjectHelper
+ {
+ static bool SerializeObject(ISerializer& serializer, AZ::Aabb& value)
+ {
+ AZ::Vector3 minValue = value.GetMin();
+ AZ::Vector3 maxValue = value.GetMax();
+ serializer.Serialize(minValue, "minValue");
+ serializer.Serialize(maxValue, "maxValue");
+ value.SetMin(minValue);
+ value.SetMax(maxValue);
+ return serializer.IsValid();
+ }
+ };
}
diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.cpp b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.cpp
index 62335a9b39..1ccff7be50 100644
--- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.cpp
@@ -22,14 +22,15 @@ namespace AzNetworking
#endif
AZ_CVAR(bool, net_TcpTimeoutConnections, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Boolean value on whether we should timeout Tcp connections");
- AZ_CVAR(AZ::TimeMs, net_TcpHearthbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Tcp connection heartbeat frequency");
- AZ_CVAR(AZ::TimeMs, net_TcpTimeoutTimeMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Tcp connection");
+ AZ_CVAR(AZ::TimeMs, net_TcpHeartbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Tcp connection heartbeat frequency");
+ AZ_CVAR(AZ::TimeMs, net_TcpDefaultTimeoutMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Tcp connection");
TcpNetworkInterface::TcpNetworkInterface(AZ::Name name, IConnectionListener& connectionListener, TrustZone trustZone, TcpListenThread& listenThread)
: m_name(name)
, m_trustZone(trustZone)
, m_connectionListener(connectionListener)
, m_listenThread(listenThread)
+ , m_timeoutMs(net_TcpDefaultTimeoutMs)
{
;
}
@@ -97,7 +98,7 @@ namespace AzNetworking
}
AZLOG_INFO("Adding new socket %d", static_cast(tcpSocket->GetSocketFd()));
- const TimeoutId newTimeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast(tcpSocket->GetSocketFd()), net_TcpHearthbeatTimeMs);
+ const TimeoutId newTimeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast(tcpSocket->GetSocketFd()), net_TcpHeartbeatTimeMs);
connection->SetTimeoutId(newTimeoutId);
connection->SendReliablePacket(CorePackets::InitiateConnectionPacket());
m_connectionListener.OnConnect(connection.get());
@@ -174,14 +175,14 @@ namespace AzNetworking
return connection->Disconnect(reason, TerminationEndpoint::Local);
}
- void TcpNetworkInterface::SetTimeoutEnabled(bool timeoutEnabled)
+ void TcpNetworkInterface::SetTimeoutMs(AZ::TimeMs timeoutMs)
{
- m_timeoutEnabled = timeoutEnabled;
+ m_timeoutMs = timeoutMs;
}
- bool TcpNetworkInterface::IsTimeoutEnabled() const
+ AZ::TimeMs TcpNetworkInterface::GetTimeoutMs() const
{
- return m_timeoutEnabled;
+ return m_timeoutMs;
}
void TcpNetworkInterface::QueueNewConnection(const PendingConnection& pendingConnection)
@@ -257,7 +258,7 @@ namespace AzNetworking
return;
}
AZLOG(NET_TcpTraffic, "Adding new socket %d", static_cast(tcpSocket.GetSocketFd()));
- const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast(tcpSocket.GetSocketFd()), net_TcpTimeoutTimeMs);
+ const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast(tcpSocket.GetSocketFd()), m_timeoutMs);
AZStd::unique_ptr connection = AZStd::make_unique(connectionId, remoteAddress, *this, tcpSocket, timeoutId);
AZ_Assert(connection->GetConnectionRole() == ConnectionRole::Acceptor, "Invalid role for connection");
GetConnectionListener().OnConnect(connection.get());
@@ -316,7 +317,7 @@ namespace AzNetworking
{
tcpConnection->SendReliablePacket(CorePackets::HeartbeatPacket());
}
- else if (net_TcpTimeoutConnections && m_networkInterface.IsTimeoutEnabled())
+ else if (net_TcpTimeoutConnections && (m_networkInterface.GetTimeoutMs() > AZ::TimeMs{ 0 }))
{
tcpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local);
return TimeoutResult::Delete;
diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.h b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.h
index b9ea88974d..d8f5d1b62b 100644
--- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.h
+++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.h
@@ -99,8 +99,8 @@ namespace AzNetworking
bool WasPacketAcked(ConnectionId connectionId, PacketId packetId) override;
bool StopListening() override;
bool Disconnect(ConnectionId connectionId, DisconnectReason reason) override;
- void SetTimeoutEnabled(bool timeoutEnabled) override;
- bool IsTimeoutEnabled() const override;
+ void SetTimeoutMs(AZ::TimeMs timeoutMs) override;
+ AZ::TimeMs GetTimeoutMs() const override;
//! @}
//! Queues a new incoming connection for this network interface.
@@ -156,7 +156,7 @@ namespace AzNetworking
AZ::Name m_name;
TrustZone m_trustZone;
uint16_t m_port = 0;
- bool m_timeoutEnabled = true;
+ AZ::TimeMs m_timeoutMs = AZ::TimeMs{ 0 };
IConnectionListener& m_connectionListener;
TcpConnectionSet m_connectionSet;
TcpSocketManager m_tcpSocketManager;
diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocket.cpp b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocket.cpp
index 272683febc..e469b70640 100644
--- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocket.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocket.cpp
@@ -53,18 +53,11 @@ namespace AzNetworking
{
Close();
- if (!SocketCreateInternal())
- {
- return false;
- }
-
- if (!BindSocketForListenInternal(port))
- {
- return false;
- }
-
- if (!(SetSocketNonBlocking(m_socketFd) && SetSocketNoDelay(m_socketFd)))
+ if (!SocketCreateInternal()
+ || !BindSocketForListenInternal(port)
+ || !(SetSocketNonBlocking(m_socketFd) && SetSocketNoDelay(m_socketFd)))
{
+ Close();
return false;
}
@@ -75,18 +68,11 @@ namespace AzNetworking
{
Close();
- if (!SocketCreateInternal())
- {
- return false;
- }
-
- if (!BindSocketForConnectInternal(address))
- {
- return false;
- }
-
- if (!(SetSocketNonBlocking(m_socketFd) && SetSocketNoDelay(m_socketFd)))
+ if (!SocketCreateInternal()
+ || !BindSocketForConnectInternal(address)
+ || !(SetSocketNonBlocking(m_socketFd) && SetSocketNoDelay(m_socketFd)))
{
+ Close();
return false;
}
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp
index bf01ece458..b0e64f93e3 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp
@@ -31,8 +31,8 @@ namespace AzNetworking
AZ_CVAR(bool, net_UdpTimeoutConnections, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Boolean value on whether we should timeout Udp connections");
AZ_CVAR(AZ::TimeMs, net_UdpPacketTimeSliceMs, AZ::TimeMs{ 8 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The number of milliseconds to allow for packet processing");
- AZ_CVAR(AZ::TimeMs, net_UdpHearthbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Udp connection heartbeat frequency");
- AZ_CVAR(AZ::TimeMs, net_UdpTimeoutTimeMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Udp connection");
+ AZ_CVAR(AZ::TimeMs, net_UdpHeartbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Udp connection heartbeat frequency");
+ AZ_CVAR(AZ::TimeMs, net_UdpDefaultTimeoutMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Udp connection");
AZ_CVAR(AZ::TimeMs, net_MinPacketTimeoutMs, AZ::TimeMs{ 200 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Minimum time to wait before timing out an unacked packet");
AZ_CVAR(int32_t, net_MaxTimeoutsPerFrame, 1000, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of packet timeouts to allow to process in a single frame");
AZ_CVAR(float, net_RttFudgeScalar, 2.0f, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Scalar value to multiply computed Rtt by to determine an optimal packet timeout threshold");
@@ -61,6 +61,7 @@ namespace AzNetworking
, m_connectionListener(connectionListener)
, m_socket(net_UdpUseEncryption ? new DtlsSocket() : new UdpSocket())
, m_readerThread(readerThread)
+ , m_timeoutMs(net_UdpDefaultTimeoutMs)
{
const AZ::CVarFixedString compressor = static_cast(net_UdpCompressor);
const AZ::Name compressorName = AZ::Name(compressor);
@@ -138,7 +139,7 @@ namespace AzNetworking
}
const ConnectionId connectionId = m_connectionSet.GetNextConnectionId();
- const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast(connectionId), net_UdpHearthbeatTimeMs);
+ const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast(connectionId), m_timeoutMs);
AZStd::unique_ptr connection = AZStd::make_unique(connectionId, remoteAddress, *this, ConnectionRole::Connector);
UdpPacketEncodingBuffer dtlsData;
@@ -403,14 +404,14 @@ namespace AzNetworking
return connection->Disconnect(reason, TerminationEndpoint::Local);
}
- void UdpNetworkInterface::SetTimeoutEnabled(bool timeoutEnabled)
+ void UdpNetworkInterface::SetTimeoutMs(AZ::TimeMs timeoutMs)
{
- m_timeoutEnabled = timeoutEnabled;
+ m_timeoutMs = timeoutMs;
}
- bool UdpNetworkInterface::IsTimeoutEnabled() const
+ AZ::TimeMs UdpNetworkInterface::GetTimeoutMs() const
{
- return m_timeoutEnabled;
+ return m_timeoutMs;
}
bool UdpNetworkInterface::IsEncrypted() const
@@ -681,7 +682,7 @@ namespace AzNetworking
// How long should we sit in the timeout queue before heartbeating or disconnecting
const ConnectionId connectionId = m_connectionSet.GetNextConnectionId();
- const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast(connectionId), net_UdpTimeoutTimeMs);
+ const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast(connectionId), m_timeoutMs);
AZLOG(Debug_UdpConnect, "Accepted new Udp Connection");
AZStd::unique_ptr connection = AZStd::make_unique(connectionId, connectPacket.m_address, *this, ConnectionRole::Acceptor);
@@ -745,7 +746,7 @@ namespace AzNetworking
{
udpConnection->SendUnreliablePacket(CorePackets::HeartbeatPacket());
}
- else if (net_UdpTimeoutConnections && m_networkInterface.IsTimeoutEnabled())
+ else if (net_UdpTimeoutConnections && (m_networkInterface.GetTimeoutMs() > AZ::TimeMs{ 0 }))
{
udpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local);
return TimeoutResult::Delete;
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.h b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.h
index 949914da91..8f827c74c4 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.h
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.h
@@ -104,8 +104,8 @@ namespace AzNetworking
bool WasPacketAcked(ConnectionId connectionId, PacketId packetId) override;
bool StopListening() override;
bool Disconnect(ConnectionId connectionId, DisconnectReason reason) override;
- void SetTimeoutEnabled(bool timeoutEnabled) override;
- bool IsTimeoutEnabled() const override;
+ void SetTimeoutMs(AZ::TimeMs timeoutMs) override;
+ AZ::TimeMs GetTimeoutMs() const override;
//! @}
//! Returns true if this is an encrypted socket, false if not.
@@ -181,7 +181,7 @@ namespace AzNetworking
TrustZone m_trustZone;
uint16_t m_port = 0;
bool m_allowIncomingConnections = false;
- bool m_timeoutEnabled = true;
+ AZ::TimeMs m_timeoutMs = AZ::TimeMs{ 0 };
IConnectionListener& m_connectionListener;
UdpConnectionSet m_connectionSet;
TimeoutQueue m_connectionTimeoutQueue;
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp
index 44f6562c84..300b3527fa 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp
@@ -79,17 +79,15 @@ namespace AzNetworking
{
const int32_t error = GetLastNetworkError();
AZLOG_ERROR("Failed to bind UDP socket to port %u (%d:%s)", uint32_t(port), error, GetNetworkErrorDesc(error));
+ Close();
return false;
}
}
- if (!SetSocketBufferSizes(m_socketFd, net_UdpSendBufferSize, net_UdpRecvBufferSize))
- {
- return false;
- }
-
- if (!SetSocketNonBlocking(m_socketFd))
+ if (!SetSocketBufferSizes(m_socketFd, net_UdpSendBufferSize, net_UdpRecvBufferSize)
+ || !SetSocketNonBlocking(m_socketFd))
{
+ Close();
return false;
}
diff --git a/Code/Framework/AzNetworking/AzNetworking/Utilities/TimedThread.cpp b/Code/Framework/AzNetworking/AzNetworking/Utilities/TimedThread.cpp
index c85d15ae45..565b37202a 100644
--- a/Code/Framework/AzNetworking/AzNetworking/Utilities/TimedThread.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/Utilities/TimedThread.cpp
@@ -26,27 +26,29 @@ namespace AzNetworking
{
m_running = true;
m_joinable = true;
- m_thread = AZStd::thread([this]()
- {
- OnStart();
- while (m_running)
+ m_thread = AZStd::thread(
+ m_threadDesc,
+ [this]()
{
- const AZ::TimeMs startTimeMs = AZ::GetElapsedTimeMs();
- OnUpdate(m_updateRate);
- const AZ::TimeMs updateTimeMs = AZ::GetElapsedTimeMs() - startTimeMs;
+ OnStart();
+ while (m_running)
+ {
+ const AZ::TimeMs startTimeMs = AZ::GetElapsedTimeMs();
+ OnUpdate(m_updateRate);
+ const AZ::TimeMs updateTimeMs = AZ::GetElapsedTimeMs() - startTimeMs;
- if (m_updateRate > updateTimeMs)
- {
- AZStd::chrono::milliseconds sleepTimeMs(static_cast(m_updateRate - updateTimeMs));
- AZStd::this_thread::sleep_for(sleepTimeMs);
+ if (m_updateRate > updateTimeMs)
+ {
+ AZStd::chrono::milliseconds sleepTimeMs(static_cast(m_updateRate - updateTimeMs));
+ AZStd::this_thread::sleep_for(sleepTimeMs);
+ }
+ else if (m_updateRate < updateTimeMs)
+ {
+ AZLOG(NET_TimedThread, "TimedThread bled %d ms", aznumeric_cast(updateTimeMs - m_updateRate));
+ }
}
- else if (m_updateRate < updateTimeMs)
- {
- AZLOG(NET_TimedThread, "TimedThread bled %d ms", aznumeric_cast(updateTimeMs - m_updateRate));
- }
- }
- OnStop();
- }, &m_threadDesc);
+ OnStop();
+ });
}
void TimedThread::Stop()
diff --git a/Code/Framework/AzNetworking/Tests/TcpTransport/TcpTransportTests.cpp b/Code/Framework/AzNetworking/Tests/TcpTransport/TcpTransportTests.cpp
index 3031f66774..77632da572 100644
--- a/Code/Framework/AzNetworking/Tests/TcpTransport/TcpTransportTests.cpp
+++ b/Code/Framework/AzNetworking/Tests/TcpTransport/TcpTransportTests.cpp
@@ -149,8 +149,9 @@ namespace UnitTest
EXPECT_EQ(testServer.m_serverNetworkInterface->GetConnectionSet().GetConnectionCount(), 1);
EXPECT_EQ(testClient.m_clientNetworkInterface->GetConnectionSet().GetConnectionCount(), 1);
- testClient.m_clientNetworkInterface->SetTimeoutEnabled(true);
- EXPECT_TRUE(testClient.m_clientNetworkInterface->IsTimeoutEnabled());
+ const AZ::TimeMs timeoutMs = AZ::TimeMs{ 100 };
+ testClient.m_clientNetworkInterface->SetTimeoutMs(timeoutMs);
+ EXPECT_EQ(testClient.m_clientNetworkInterface->GetTimeoutMs(), timeoutMs);
EXPECT_TRUE(testServer.m_serverNetworkInterface->StopListening());
}
diff --git a/Code/Framework/AzNetworking/Tests/UdpTransport/UdpTransportTests.cpp b/Code/Framework/AzNetworking/Tests/UdpTransport/UdpTransportTests.cpp
index 91db3b4549..65c2cfa2b5 100644
--- a/Code/Framework/AzNetworking/Tests/UdpTransport/UdpTransportTests.cpp
+++ b/Code/Framework/AzNetworking/Tests/UdpTransport/UdpTransportTests.cpp
@@ -279,8 +279,9 @@ namespace UnitTest
EXPECT_EQ(testServer.m_serverNetworkInterface->GetConnectionSet().GetConnectionCount(), 1);
EXPECT_EQ(testClient.m_clientNetworkInterface->GetConnectionSet().GetConnectionCount(), 1);
- testClient.m_clientNetworkInterface->SetTimeoutEnabled(true);
- EXPECT_TRUE(testClient.m_clientNetworkInterface->IsTimeoutEnabled());
+ const AZ::TimeMs timeoutMs = AZ::TimeMs{ 100 };
+ testClient.m_clientNetworkInterface->SetTimeoutMs(timeoutMs);
+ EXPECT_EQ(testClient.m_clientNetworkInterface->GetTimeoutMs(), timeoutMs);
EXPECT_FALSE(dynamic_cast(testClient.m_clientNetworkInterface)->IsEncrypted());
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerInterface.h
index bcc8afbe6a..9da8d97990 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerInterface.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerInterface.h
@@ -22,22 +22,22 @@ namespace AzToolsFramework
virtual ~ViewportEditorModeTrackerInterface() = default;
- //! Activates the specified editor mode for the specified viewport.
+ //! Activates the specified editor mode for the specified viewport editor mode tracker.
virtual AZ::Outcome ActivateMode(
- const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) = 0;
+ const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo, ViewportEditorMode mode) = 0;
- //! Deactivates the specified editor mode for the specified viewport.
+ //! Deactivates the specified editor mode for the specified viewport editor mode tracker.
virtual AZ::Outcome DeactivateMode(
- const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) = 0;
+ const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo, ViewportEditorMode mode) = 0;
- //! Attempts to retrieve the editor mode state for the specified viewport, otherwise returns nullptr.
- virtual const ViewportEditorModesInterface* GetViewportEditorModes(const ViewportEditorModeInfo& viewportEditorModeInfo) const = 0;
+ //! Attempts to retrieve the editor mode state for the specified viewport editor mode tracker, otherwise returns nullptr.
+ virtual const ViewportEditorModesInterface* GetViewportEditorModes(const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo) const = 0;
- //! Returns the number of viewports currently being tracked.
+ //! Returns the number of viewport editor mode trackers.
virtual size_t GetTrackedViewportCount() const = 0;
- //! Returns true if the specified viewport is being tracked, otherwise false.
- virtual bool IsViewportModeTracked(const ViewportEditorModeInfo& viewportEditorModeInfo) const = 0;
+ //! Returns true if viewport editor modes are being tracked for the specified od, otherwise false.
+ virtual bool IsViewportModeTracked(const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo) const = 0;
};
} // namespace AzToolsFramework
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h
index 42a1cb0113..4fcb891e61 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h
@@ -9,7 +9,7 @@
#pragma once
#include
-#include
+#include
#include
namespace AzToolsFramework
@@ -23,11 +23,11 @@ namespace AzToolsFramework
Pick
};
- //! Viewport identifier and other relevant viewport data.
- struct ViewportEditorModeInfo
+ //! Viewport editor mode tracker identifier and other relevant data.
+ struct ViewportEditorModeTrackerInfo
{
- using IdType = AzFramework::ViewportId;
- IdType m_id = ViewportUi::DefaultViewportId; //!< The unique identifier for a given viewport.
+ using IdType = AzFramework::EntityContextId;
+ IdType m_id = AzFramework::EntityContextId::CreateNull(); //!< The unique identifier for a given viewport editor mode tracker.
};
//! Interface for the editor modes of a given viewport.
@@ -49,7 +49,7 @@ namespace AzToolsFramework
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
- using BusIdType = ViewportEditorModeInfo::IdType;
+ using BusIdType = ViewportEditorModeTrackerInfo::IdType;
//////////////////////////////////////////////////////////////////////////
//! Notifies subscribers of the a given viewport to the activation of the specified editor mode.
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Archive/ArchiveAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Archive/ArchiveAPI.h
index fa8c2c14ff..11c4c85722 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Archive/ArchiveAPI.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Archive/ArchiveAPI.h
@@ -12,95 +12,84 @@
#include
#include
#include
+#include
namespace AzToolsFramework
{
- // use bind if you need additional context.
- // Parameters:
- // bool - If the archive command was successful or not.
- typedef AZStd::function ArchiveResponseCallback;
- // bool - If the archive command was successful or not.
- // AZStd::string - The console output from the command.
- typedef AZStd::function ArchiveResponseOutputCallback;
-
-
//! ArchiveCommands
//! This bus handles messages relating to archive commands
//! archive commands are ASYNCHRONOUS
//! archive formats officially supported are .zip
- //! do not block the main thread waiting for a response, it is not okay
- //! you will not get a message delivered unless you tick the tickbus anyway!
class ArchiveCommands
: public AZ::EBusTraits
{
public:
-
- using Bus = AZ::EBus;
-
+ // EBus Traits
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
typedef AZStd::recursive_mutex MutexType;
static const bool LocklessDispatch = true;
- virtual ~ArchiveCommands() {}
- //! Start an async task to extract an archive to the target directory
- //! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
- //! Multiple tasks can be associated with the same handle
- virtual void ExtractArchive(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseCallback& respCallback) = 0;
- virtual void ExtractArchiveOutput(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
- // Maintaining backwards API compatibility - ExtractArchiveBlocking below passes in extractWithRoot as an option
- virtual void ExtractArchiveWithoutRoot(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
+ virtual ~ArchiveCommands() = default;
- //! Start a sync task to extract an archive to the target directory
- //! If you do not want to extract the root folder then set extractWithRootDirectory to false.
- virtual bool ExtractArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool extractWithRootDirectory) = 0;
+ //! Create an archive of the target directory (all files and subdirectories)
+ //! @param archivePath The path of the archive to create
+ //! @dirToArchive The directory to be added to the archive
+ //! @return Future (bool) which can obtain the success value of the operation
+ [[nodiscard]] virtual std::future CreateArchive(
+ const AZStd::string& archivePath,
+ const AZStd::string& dirToArchive) = 0;
- //! Extract a single file asynchronously from the archive to the destination.
- //! Uses cwd if destinationPath empty. overWrite = true for overwrite existing files, false for skipExisting
- //! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
- //! Multiple tasks can be associated with the same handle
- virtual void ExtractFile(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
+ //! Extract an archive to the target directory
+ //! @param archivePath The path of the archive to extract
+ //! @param destinationPath The directory where files will be extracted to
+ //! @return Future (bool) which can obtain the success value of the operation
+ [[nodiscard]] virtual std::future ExtractArchive(
+ const AZStd::string& archivePath,
+ const AZStd::string& destinationPath) = 0;
- //! Extract a single file from the archive to the destination and block until finished.
- //! Uses cwd if destinationPath empty. overWrite = true for overwrite existing files, false for skipExisting
- virtual bool ExtractFileBlocking(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite) = 0;
+ //! Extract a single file from the archive to the destination
+ //! Destination path should not be empty
+ //! @param archivePath The path of the archive to extract from
+ //! @param fileInArchive A path to a file, relative to root of archive
+ //! @param destinationPath The directory where file will be extracted to
+ //! @return Future (bool) which can obtain the success value of the operation
+ [[nodiscard]] virtual std::future ExtractFile(
+ const AZStd::string& archivePath,
+ const AZStd::string& fileInArchive,
+ const AZStd::string& destinationPath) = 0;
- //! Start an async task to create an archive of the target directory (recursively)
- //! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
- //! Multiple tasks can be associated with the same handle.
- virtual void CreateArchive(const AZStd::string& archivePath, const AZStd::string& dirToArchive, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
+ //! Retrieve the list of files contained in an archive (all files and subdirectories)
+ //! @param archivePath The path of the archive to list
+ //! @param outFileEntries An out parameter that will contain the file paths found
+ //! @return True if successful, false otherwise
+ virtual bool ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector& outFileEntries) = 0;
- //! Start a sync task to create an archive of the target directory (recursively)
- virtual bool CreateArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& dirToArchive) = 0;
-
- //! Start an async task to retrieve the list of files and their relative paths within an archive (recursively)
- //! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
- //! Multiple tasks can be associated with the same handle.
- virtual void ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector& fileEntries, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
+ //! Add a file to an archive
+ //! The archive might not exist yet
+ //! The file path relative to the working directory will be replicated in the archive
+ //! @param archivePath The path of the archive to add to
+ //! @param workingDirectory A directory that will be the starting path of the file to be added
+ //! @param fileToAdd A path to the file relative to the working directory
+ //! @return Future (bool) which can obtain the success value of the operation
+ [[nodiscard]] virtual std::future AddFileToArchive(
+ const AZStd::string& archivePath,
+ const AZStd::string& workingDirectory,
+ const AZStd::string& fileToAdd) = 0;
- //! Start a sync task to retrieve the list of files and their relative paths within an archive (recursively)
- virtual bool ListFilesInArchiveBlocking(const AZStd::string& archivePath, AZStd::vector& fileEntries) = 0;
-
- //! Start an async task to add a file to a preexisting archive.
- //! fileToAdd must be a relative path to the file from the working directory. The path to the file from the root of the archive will be the same as the relative path to the file on disk.
- //! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
- //! Multiple tasks can be associated with the same handle.
- virtual void AddFileToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
-
- //! Start a sync task to add a file to a preexisting archive.
- //! fileToAdd must be a relative path to the file from the working directory. The path to the file from the root of the archive will be the same as the relative path to the file on disk.
- virtual bool AddFileToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd) = 0;
-
- //! Start an async task to add files to a archive.
- //! File paths inside the list file must either be a relative path from the working directory or an absolute path.
- virtual void AddFilesToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
-
- //! Start a sync task to add files to an archive.
- //! File paths inside the list file must either be a relative path from the working directory or an absolute path.
- virtual bool AddFilesToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath) = 0;
-
- //! Cancels tasks associtated with the given handle. Blocks until all tasks are cancelled.
- virtual void CancelTasks(AZ::Uuid taskHandle) = 0;
+ //! Add files to an archive provided from a file listing
+ //! The archive might not exist yet
+ //! File paths in the file list should be relative to root of the archive
+ //! @param archivePath The path of the archive to add to
+ //! @param workingDirectory A directory that will be the starting path of the list of files to add
+ //! @param listFilePath Full path to a text file that contains the list of files to add
+ //! @return Future (bool) which can obtain the success value of the operation
+ [[nodiscard]] virtual std::future AddFilesToArchive(
+ const AZStd::string& archivePath,
+ const AZStd::string& workingDirectory,
+ const AZStd::string& listFilePath) = 0;
};
+
using ArchiveCommandsBus = AZ::EBus;
+
}; // namespace AzToolsFramework
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Archive/ArchiveComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Archive/ArchiveComponent.cpp
index 5004108132..3d0299667c 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Archive/ArchiveComponent.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Archive/ArchiveComponent.cpp
@@ -12,118 +12,98 @@
#include
#include
-#include
-
+#include
+#include
#include
#include
#include
+
namespace AzToolsFramework
{
- // Forward declare platform specific functions
- namespace Platform
+ [[maybe_unused]] constexpr const char s_traceName[] = "ArchiveComponent";
+ constexpr AZ::u32 s_compressionMethod = AZ::IO::INestedArchive::METHOD_DEFLATE;
+ constexpr AZ::s32 s_compressionLevel = AZ::IO::INestedArchive::LEVEL_NORMAL;
+ constexpr CompressionCodec::Codec s_compressionCodec = CompressionCodec::Codec::ZLIB;
+
+ namespace ArchiveUtils
{
- AZStd::string GetZipExePath();
- AZStd::string GetUnzipExePath();
-
- AZStd::string GetCreateArchiveCommand(const AZStd::string& archivePath, const AZStd::string& dirToArchive);
- AZStd::string GetExtractArchiveCommand(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool includeRoot);
- AZStd::string GetAddFileToArchiveCommand(const AZStd::string& archivePath, const AZStd::string& file);
- AZStd::string GetAddFilesToArchiveCommand(const AZStd::string& archivePath, const AZStd::string& listFilePath);
- AZStd::string GetExtractFileCommand(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite);
- AZStd::string GetListFilesInArchiveCommand(const AZStd::string& archivePath);
- void ParseConsoleOutputFromListFilesInArchive(const AZStd::string& consoleOutput, AZStd::vector& fileEntries);
- }
-
- const char s_traceName[] = "ArchiveComponent";
- const unsigned int g_sleepDuration = 1;
-
- // Echoes all results of stdout and stderr to console and never blocks
- class ConsoleEchoCommunicator
- {
- public:
- ConsoleEchoCommunicator(AzFramework::ProcessCommunicator* communicator)
- : m_communicator(communicator)
+ // Read a file's contents into a provided buffer.
+ // Does not add a zero byte at the end of the buffer.
+ // returns true if read was successful, false otherwise.
+ bool ReadFile(const AZ::IO::Path& filePath, AZ::IO::OpenMode openMode, AZStd::vector& outBuffer)
{
- }
-
- ~ConsoleEchoCommunicator()
- {
- }
-
- // Call this periodically to drain the buffers
- void Pump()
- {
- if (m_communicator->IsValid())
+ auto fileIO = AZ::IO::FileIOBase::GetDirectInstance();
+ if (!fileIO)
{
- AZ::u32 readBufferSize = 0;
- AZStd::string readBuffer;
- // Don't call readOutput unless there is output or else it will block...
- readBufferSize = m_communicator->PeekOutput();
- if (readBufferSize)
- {
- readBuffer.resize_no_construct(readBufferSize + 1);
- readBuffer[readBufferSize] = '\0';
- m_communicator->ReadOutput(readBuffer.data(), readBufferSize);
- EchoBuffer(readBuffer);
- }
- readBufferSize = m_communicator->PeekError();
- if (readBufferSize)
- {
- readBuffer.resize_no_construct(readBufferSize + 1);
- readBuffer[readBufferSize] = '\0';
- m_communicator->ReadError(readBuffer.data(), readBufferSize);
- EchoBuffer(readBuffer);
- }
+ return false;
}
- }
- private:
- void EchoBuffer(const AZStd::string& buffer)
- {
- size_t startIndex = 0;
- size_t endIndex = 0;
- const size_t bufferSize = buffer.size();
- for (size_t i = 0; i < bufferSize; ++i)
+ bool success = false;
+ AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
+ if (fileIO->Open(filePath.c_str(), openMode, fileHandle))
{
- if (buffer[i] == '\n' || buffer[i] == '\0')
+ AZ::u64 fileSize = 0;
+ if (fileIO->Size(fileHandle, fileSize) && fileSize != 0)
{
- endIndex = i;
- bool isEmptyMessage = (endIndex - startIndex == 1) && (buffer[startIndex] == '\r');
- if (!isEmptyMessage)
+ outBuffer.resize_no_construct(fileSize);
+
+ AZ::u64 bytesRead = 0;
+ if (fileIO->Read(fileHandle, outBuffer.data(), fileSize, true, &bytesRead))
{
- AZ_Printf(s_traceName, "%s", buffer.substr(startIndex, endIndex - startIndex).c_str());
+ success = (fileSize == bytesRead);
}
- startIndex = endIndex + 1;
}
+
+ fileIO->Close(fileHandle);
+ }
+
+ return success;
+ }
+
+ // Reads a text file that contains a list of file paths.
+ // Tokenize the file by lines.
+ // Calls the lineVisitor function for each line of the file.
+ void ProcessFileList(const AZ::IO::Path& filePath, AZStd::function lineVisitor)
+ {
+ AZStd::vector fileBuffer;
+ if (ReadFile(filePath, AZ::IO::OpenMode::ModeText | AZ::IO::OpenMode::ModeRead, fileBuffer))
+ {
+ AZ::StringFunc::TokenizeVisitor(AZStd::string_view{ fileBuffer.data(), fileBuffer.size() }, lineVisitor, "\n");
}
}
- AzFramework::ProcessCommunicator* m_communicator = nullptr;
- };
+ } // namespace ArchiveUtils
void ArchiveComponent::Activate()
{
- m_zipExePath = Platform::GetZipExePath();
- m_unzipExePath = Platform::GetUnzipExePath();
+ m_fileIO = AZ::IO::FileIOBase::GetDirectInstance();
+ if (m_fileIO == nullptr)
+ {
+ AZ_Error(s_traceName, false, "Failed to create a LocalFileIO instance!");
+ }
- ArchiveCommands::Bus::Handler::BusConnect();
+ m_archive = AZ::Interface::Get();
+ if (m_archive == nullptr)
+ {
+ AZ_Error(s_traceName, false, "Failed to get IArchive interface!");
+ }
+
+ ArchiveCommandsBus::Handler::BusConnect();
}
void ArchiveComponent::Deactivate()
{
- ArchiveCommands::Bus::Handler::BusDisconnect();
+ ArchiveCommandsBus::Handler::BusDisconnect();
- AZStd::unique_lock lock(m_threadControlMutex);
- for (auto pair : m_threadInfoMap)
+ m_fileIO = nullptr;
+ m_archive = nullptr;
+
+ for (AZStd::thread& t : m_threads)
{
- ThreadInfo& info = pair.second;
- info.shouldStop = true;
- m_cv.wait(lock, [&info]() {
- return info.threads.size() == 0;
- });
+ t.join();
}
- m_threadInfoMap.clear();
+ m_threads = {};
}
void ArchiveComponent::Reflect(AZ::ReflectContext * context)
@@ -132,7 +112,7 @@ namespace AzToolsFramework
{
serializeContext->Class()
->Version(2)
- ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector({ AZ_CRC("AssetBuilder", 0xc739c7d7) }))
+ ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector({ AZ_CRC_CE("AssetBuilder") }))
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
@@ -141,320 +121,480 @@ namespace AzToolsFramework
"Archive", "Handles creation and extraction of zip archives.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Editor")
- ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
+ ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
;
}
}
}
- void ArchiveComponent::CreateArchive(const AZStd::string& archivePath, const AZStd::string& dirToArchive, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
+ std::future ArchiveComponent::CreateArchive(
+ const AZStd::string& archivePath,
+ const AZStd::string& dirToArchive)
{
- AZStd::string commandLineArgs = AZStd::string::format(R"(a -tzip -mx=1 "%s" -r "%s\*")", archivePath.c_str(), dirToArchive.c_str());
- LaunchZipExe(m_zipExePath, commandLineArgs, respCallback, taskHandle);
- }
-
- bool ArchiveComponent::CreateArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& dirToArchive)
- {
- bool success = false;
- auto createArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
- success = result;
- };
-
- AZStd::string commandLineArgs = Platform::GetCreateArchiveCommand(archivePath, dirToArchive);
-
- if (commandLineArgs.empty())
+ if (!CheckParamsForCreate(archivePath, dirToArchive))
{
- // The platform-specific implementation has already thrown its own error, no need to throw another one
- return false;
+ std::promise p;
+ p.set_value(false);
+ return p.get_future();
}
- LaunchZipExe(m_zipExePath, commandLineArgs, createArchiveCallback, AZ::Uuid::CreateNull(), dirToArchive, false);
- return success;
- }
-
- void ArchiveComponent::ExtractArchive(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseCallback& respCallback)
- {
- ArchiveResponseOutputCallback responseHandler = [respCallback](bool result, AZStd::string /*outputStr*/) { respCallback(result); };
- ExtractArchiveOutput(archivePath, destinationPath, taskHandle, responseHandler);
- }
-
- void ArchiveComponent::ExtractArchiveOutput(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
- {
- AZStd::string commandLineArgs = Platform::GetExtractArchiveCommand(archivePath, destinationPath, true);
-
- if (commandLineArgs.empty())
+ auto FnCreateArchive = [this, archivePath, dirToArchive](std::promise&& p) -> void
{
- // The platform-specific implementation has already thrown its own error, no need to throw another one
- return;
- }
-
- LaunchZipExe(m_unzipExePath, commandLineArgs, respCallback, taskHandle);
- }
-
- void ArchiveComponent::ExtractArchiveWithoutRoot(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
- {
- AZStd::string commandLineArgs = Platform::GetExtractArchiveCommand(archivePath, destinationPath, false);
-
- if (commandLineArgs.empty())
- {
- // The platform-specific implementation has already thrown its own error, no need to throw another one
- return;
- }
-
- LaunchZipExe(m_unzipExePath, commandLineArgs, respCallback, taskHandle);
- }
-
- void ArchiveComponent::ExtractFile(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
- {
- AZStd::string commandLineArgs = AzToolsFramework::Platform::GetExtractFileCommand(archivePath, fileInArchive, destinationPath, overWrite);
- if (commandLineArgs.empty())
- {
- // The platform-specific implementation has already thrown its own error, no need to throw another one
- return;
- }
- LaunchZipExe(m_unzipExePath, commandLineArgs, respCallback, taskHandle);
- }
-
- bool ArchiveComponent::ExtractFileBlocking(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite)
- {
- AZStd::string commandLineArgs = AzToolsFramework::Platform::GetExtractFileCommand(archivePath, fileInArchive, destinationPath, overWrite);
- if (commandLineArgs.empty())
- {
- // The platform-specific implementation has already thrown its own error, no need to throw another one
- return false;
- }
-
- bool success = false;
- auto createArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
- success = result;
- };
- LaunchZipExe(m_unzipExePath, commandLineArgs, createArchiveCallback);
- return success;
- }
-
- void ArchiveComponent::ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector& fileEntries, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
- {
- AZStd::string commandLineArgs = Platform::GetListFilesInArchiveCommand(archivePath);
-
- auto parseOutput = [respCallback, &fileEntries](bool exitCode, AZStd::string consoleOutput)
- {
- Platform::ParseConsoleOutputFromListFilesInArchive(consoleOutput, fileEntries);
- AZ::TickBus::QueueFunction(respCallback, exitCode, AZStd::move(consoleOutput));
- };
- LaunchZipExe(m_unzipExePath, commandLineArgs, parseOutput, taskHandle, "", true);
- }
-
- bool ArchiveComponent::ListFilesInArchiveBlocking(const AZStd::string& archivePath, AZStd::vector& fileEntries)
- {
- AZStd::string listOutput;
- AZStd::string commandLineArgs = Platform::GetListFilesInArchiveCommand(archivePath.c_str());
- bool success = false;
-
- auto parseOutput = [&success, &fileEntries](bool result, AZStd::string consoleOutput)
- {
- Platform::ParseConsoleOutputFromListFilesInArchive(consoleOutput, fileEntries);
- success = result;
- };
- LaunchZipExe(m_unzipExePath, commandLineArgs, parseOutput, AZ::Uuid::CreateNull(), "", true);
- return success;
- }
-
- void ArchiveComponent::AddFileToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
- {
- AZStd::string commandLineArgs = Platform::GetAddFileToArchiveCommand(archivePath, fileToAdd);
- if (commandLineArgs.empty())
- {
- // The platform-specific implementation has already thrown its own error, no need to throw another one
- return;
- }
-
- LaunchZipExe(m_zipExePath, commandLineArgs, respCallback, taskHandle, workingDirectory);
- }
-
- bool ArchiveComponent::AddFileToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd)
- {
- AZStd::string commandLineArgs = Platform::GetAddFileToArchiveCommand(archivePath, fileToAdd);
- if (commandLineArgs.empty())
- {
- // The platform-specific implementation has already thrown its own error, no need to throw another one
- return false;
- }
- bool success = false;
- auto addFileToArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
- success = result;
- };
-
- LaunchZipExe(m_zipExePath, commandLineArgs, addFileToArchiveCallback, AZ::Uuid::CreateNull(), workingDirectory);
- return success;
- }
-
- bool ArchiveComponent::AddFilesToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath)
- {
- bool success = false;
-
- auto addFileToArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
- success = result;
- };
-
- AZStd::string commandLineArgs = Platform::GetAddFilesToArchiveCommand(archivePath.c_str(), listFilePath.c_str());
-
- if (commandLineArgs.empty())
- {
- // The platform-specific implementation has already thrown its own error, no need to throw another one
- return false;
- }
- LaunchZipExe(m_zipExePath, commandLineArgs, addFileToArchiveCallback, AZ::Uuid::CreateNull(), workingDirectory);
- return success;
- }
-
- void ArchiveComponent::AddFilesToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
- {
- AZStd::string commandLineArgs = Platform::GetAddFilesToArchiveCommand(archivePath, listFilePath);
- if (commandLineArgs.empty())
- {
- // The platform-specific implementation has already thrown its own error, no need to throw another one
- return;
- }
-
- LaunchZipExe(m_zipExePath, commandLineArgs, respCallback, taskHandle, workingDirectory);
- }
-
-
- bool ArchiveComponent::ExtractArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool extractWithRootDirectory)
- {
- AZStd::string commandLineArgs = Platform::GetExtractArchiveCommand(archivePath, destinationPath, extractWithRootDirectory);
-
- if (commandLineArgs.empty())
- {
- // The platform-specific implementation has already thrown its own error, no need to throw another one
- return false;
- }
-
- bool success = false;
- auto extractArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
- success = result;
- };
-
- LaunchZipExe(m_unzipExePath, commandLineArgs, extractArchiveCallback);
- return success;
- }
-
- void ArchiveComponent::CancelTasks(AZ::Uuid taskHandle)
- {
- AZStd::unique_lock lock(m_threadControlMutex);
-
- auto it = m_threadInfoMap.find(taskHandle);
- if (it == m_threadInfoMap.end())
- {
- return;
- }
-
- ThreadInfo& info = it->second;
- info.shouldStop = true;
- m_cv.wait(lock, [&info]() {
- return info.threads.size() == 0;
- });
- m_threadInfoMap.erase(it);
- }
-
- void ArchiveComponent::LaunchZipExe(const AZStd::string& exePath, const AZStd::string& commandLineArgs, const ArchiveResponseOutputCallback& respCallback, AZ::Uuid taskHandle, const AZStd::string& workingDir, bool captureOutput)
- {
- auto sevenZJob = [=]()
- {
- if (!taskHandle.IsNull())
+ auto archive = m_archive->OpenArchive(archivePath, {}, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
+ if (!archive)
{
- AZStd::unique_lock lock(m_threadControlMutex);
- m_threadInfoMap[taskHandle].threads.insert(AZStd::this_thread::get_id());
- m_cv.notify_all();
+ AZ_Error(s_traceName, false, "Failed to create archive file '%s'", archivePath.c_str());
+ p.set_value(false);
+ return;
}
- AzFramework::ProcessLauncher::ProcessLaunchInfo info;
- info.m_commandlineParameters = exePath + " " + commandLineArgs;
-
- info.m_showWindow = false;
- if (!workingDir.empty())
+ auto foundFiles = AzFramework::FileFunc::FindFilesInPath(dirToArchive, "*", true);
+ if (!foundFiles.IsSuccess())
{
- info.m_workingDirectory = workingDir;
+ AZ_Error(s_traceName, false, "Failed to find file listing under directory '%d'", dirToArchive.c_str());
+ p.set_value(false);
+ return;
}
- AZStd::unique_ptr watcher(AzFramework::ProcessWatcher::LaunchProcess(info, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT));
- AZStd::string consoleOutput;
- AZ::u32 exitCode = static_cast(SevenZipExitCode::UserStoppedProcess);
- if (watcher)
+ bool success = true;
+ AZStd::vector fileBuffer;
+ const AZ::IO::Path workingPath{ dirToArchive };
+
+ for (const auto& fileName : foundFiles.GetValue())
{
- // callback requires output captured from 7z
- if (captureOutput)
+ bool thisSuccess = false;
+
+ AZ::IO::PathView relativePath = AZ::IO::PathView{ fileName }.LexicallyRelative(workingPath);
+
+ AZ::IO::Path fullPath = (workingPath / relativePath);
+ if (ArchiveUtils::ReadFile(fullPath, AZ::IO::OpenMode::ModeRead, fileBuffer))
{
- AZStd::string consoleBuffer;
- while (watcher->IsProcessRunning(&exitCode))
- {
- if (!taskHandle.IsNull())
- {
- AZStd::unique_lock lock(m_threadControlMutex);
- if (m_threadInfoMap[taskHandle].shouldStop)
- {
- watcher->TerminateProcess(static_cast(SevenZipExitCode::UserStoppedProcess));
- }
- }
- watcher->WaitForProcessToExit(g_sleepDuration, &exitCode);
- AZ::u32 outputSize = watcher->GetCommunicator()->PeekOutput();
- if (outputSize)
- {
- consoleBuffer.resize(outputSize);
- watcher->GetCommunicator()->ReadOutput(consoleBuffer.data(), outputSize);
- consoleOutput += consoleBuffer;
- }
- }
+ int result = archive->UpdateFile(
+ relativePath.Native(), fileBuffer.data(), fileBuffer.size(), s_compressionMethod,
+ s_compressionLevel, s_compressionCodec);
+
+ thisSuccess = (result == AZ::IO::ZipDir::ZD_ERROR_SUCCESS);
+ AZ_Error(
+ s_traceName, thisSuccess, "Error %d encountered while adding '%s' to archive '%.*s'", result, fileName.c_str(),
+ AZ_STRING_ARG(archive->GetFullPath().Native()));
}
else
{
- ConsoleEchoCommunicator echoCommunicator(watcher->GetCommunicator());
- while (watcher->IsProcessRunning(&exitCode))
- {
- if (!taskHandle.IsNull())
- {
- AZStd::unique_lock lock(m_threadControlMutex);
- if (m_threadInfoMap[taskHandle].shouldStop)
- {
- watcher->TerminateProcess(static_cast(SevenZipExitCode::UserStoppedProcess));
- }
- }
- watcher->WaitForProcessToExit(g_sleepDuration, &exitCode);
- echoCommunicator.Pump();
- }
+ AZ_Error(
+ s_traceName, false, "Error encountered while reading '%s' to add to archive '%.*s'", fileName.c_str(),
+ AZ_STRING_ARG(archive->GetFullPath().Native()));
}
+
+ success = (success && thisSuccess);
}
- if (taskHandle.IsNull())
+ archive.reset();
+ p.set_value(success);
+ };
+
+ // Async task...
+ std::promise p;
+ std::future f = p.get_future();
+
+ AZStd::thread_desc threadDesc;
+ threadDesc.m_name = "Archive Task (Create)";
+ m_threads.emplace_back(threadDesc, FnCreateArchive, AZStd::move(p));
+ return f;
+ }
+
+
+ std::future ArchiveComponent::ExtractArchive(
+ const AZStd::string& archivePath,
+ const AZStd::string& destinationPath)
+ {
+ if (!CheckParamsForExtract(archivePath, destinationPath))
+ {
+ std::promise p;
+ p.set_value(false);
+ return p.get_future();
+ }
+
+ auto FnExtractArchive = [this, archivePath, destinationPath](std::promise&& p) -> void
+ {
+ auto archive = m_archive->OpenArchive(archivePath, {}, AZ::IO::INestedArchive::FLAGS_READ_ONLY);
+ if (!archive)
{
- respCallback(exitCode == static_cast(SevenZipExitCode::NoError), AZStd::move(consoleOutput));
+ AZ_Error(s_traceName, false, "Failed to open archive file '%s'", archivePath.c_str());
+ p.set_value(false);
+ return;
+ }
+
+ AZStd::vector filesInArchive;
+ if (int result = archive->ListAllFiles(filesInArchive); result != AZ::IO::ZipDir::ZD_ERROR_SUCCESS)
+ {
+ AZ_Error(s_traceName, false, "Failed to get list of files in archive '%s'", archivePath.c_str());
+ p.set_value(false);
+ return;
+ }
+
+ AZStd::vector fileBuffer;
+ AZ::IO::Path destination{ destinationPath };
+ AZ::u64 fileSize = 0;
+ AZ::u64 numFilesWritten = 0;
+ AZ::u64 bytesWritten = 0;
+ AZ::IO::INestedArchive::Handle srcHandle{};
+ AZ::IO::HandleType dstHandle = AZ::IO::InvalidHandle;
+ constexpr AZ::IO::OpenMode openMode =
+ (AZ::IO::OpenMode::ModeCreatePath | AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeUpdate);
+
+ for (const auto& filePath : filesInArchive)
+ {
+ srcHandle = archive->FindFile(filePath.Native());
+ AZ_Assert(srcHandle != nullptr, "File '%s' does not exist inside archive '%s'", filePath.c_str(), archivePath.c_str());
+
+ fileSize = (srcHandle != nullptr) ? archive->GetFileSize(srcHandle) : 0;
+ fileBuffer.resize_no_construct(fileSize);
+ if (auto result = archive->ReadFile(srcHandle, fileBuffer.data()); result != AZ::IO::ZipDir::ZD_ERROR_SUCCESS)
+ {
+ AZ_Error(
+ s_traceName, false, "Failed to read file '%s' in archive '%s' with error %d", filePath.c_str(), archivePath.c_str(),
+ result);
+ continue;
+ }
+
+ AZ::IO::Path destinationFile = destination / filePath;
+ if (!m_fileIO->Open(destinationFile.c_str(), openMode, dstHandle))
+ {
+ AZ_Error(s_traceName, false, "Failed to open '%s' for writing", destinationFile.c_str());
+ continue;
+ }
+
+ if (!m_fileIO->Write(dstHandle, fileBuffer.data(), fileSize, &bytesWritten))
+ {
+ AZ_Error(s_traceName, false, "Failed to write destination file '%s'", destinationFile.c_str());
+ }
+ else if (bytesWritten == fileSize)
+ {
+ ++numFilesWritten;
+ }
+
+ m_fileIO->Close(dstHandle);
+ }
+
+ p.set_value(numFilesWritten == filesInArchive.size());
+ };
+
+ // Async task...
+ std::promise p;
+ std::future f = p.get_future();
+
+ AZStd::thread_desc threadDesc;
+ threadDesc.m_name = "Archive Task (Extract)";
+ m_threads.emplace_back(threadDesc, FnExtractArchive, AZStd::move(p));
+ return f;
+ }
+
+
+ std::future ArchiveComponent::ExtractFile(
+ const AZStd::string& archivePath,
+ const AZStd::string& fileInArchive,
+ const AZStd::string& destinationPath)
+ {
+ if (!CheckParamsForExtract(archivePath, destinationPath))
+ {
+ std::promise p;
+ p.set_value(false);
+ return p.get_future();
+ }
+
+ auto FnExtractFile = [this, archivePath, fileInArchive, destinationPath](std::promise&& p) -> void
+ {
+ auto archive = m_archive->OpenArchive(archivePath, {}, AZ::IO::INestedArchive::FLAGS_READ_ONLY);
+ if (!archive)
+ {
+ AZ_Error(s_traceName, false, "Failed to open archive file '%s'", archivePath.c_str());
+ p.set_value(false);
+ return;
+ }
+
+ AZ::IO::INestedArchive::Handle fileHandle = archive->FindFile(fileInArchive);
+ if (!fileHandle)
+ {
+ AZ_Error(s_traceName, false, "File '%s' does not exist inside archive '%s'", fileInArchive.c_str(), archivePath.c_str());
+ p.set_value(false);
+ return;
+ }
+
+ AZ::u64 fileSize = archive->GetFileSize(fileHandle);
+ AZStd::vector fileBuffer;
+ fileBuffer.resize_no_construct(fileSize);
+
+ if (auto result = archive->ReadFile(fileHandle, fileBuffer.data()); result != AZ::IO::ZipDir::ZD_ERROR_SUCCESS)
+ {
+ AZ_Error(
+ s_traceName, false, "Failed to read file '%s' in archive '%s' with error %d", fileInArchive.c_str(),
+ archivePath.c_str(), result);
+ p.set_value(false);
+ return;
+ }
+
+ AZ::IO::HandleType destFileHandle = AZ::IO::InvalidHandle;
+ AZ::IO::Path destinationFile{ destinationPath };
+ destinationFile /= fileInArchive;
+ AZ::IO::OpenMode openMode = (AZ::IO::OpenMode::ModeCreatePath | AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeUpdate);
+ if (!m_fileIO->Open(destinationFile.c_str(), openMode, destFileHandle))
+ {
+ AZ_Error(s_traceName, false, "Failed to open destination file '%s' for writing", destinationFile.c_str());
+ p.set_value(false);
+ return;
+ }
+
+ AZ::u64 bytesWritten = 0;
+ if (!m_fileIO->Write(destFileHandle, fileBuffer.data(), fileSize, &bytesWritten))
+ {
+ AZ_Error(s_traceName, false, "Failed to write destination file '%s'", destinationFile.c_str());
+ }
+
+ m_fileIO->Close(destFileHandle);
+ p.set_value(bytesWritten == fileSize);
+ };
+
+ // Async task...
+ std::promise p;
+ std::future f = p.get_future();
+
+ AZStd::thread_desc threadDesc;
+ threadDesc.m_name = "Archive Task (Extract Single)";
+ m_threads.emplace_back(threadDesc, FnExtractFile, AZStd::move(p));
+ return f;
+ }
+
+
+ bool ArchiveComponent::ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector& outFileEntries)
+ {
+ if (!m_fileIO || !m_archive)
+ {
+ return false;
+ }
+
+ if (!m_fileIO->Exists(archivePath.c_str()))
+ {
+ AZ_Error(s_traceName, false, "Archive '%s' does not exist!", archivePath.c_str());
+ return false;
+ }
+
+ auto archive = m_archive->OpenArchive(archivePath, {}, AZ::IO::INestedArchive::FLAGS_READ_ONLY);
+ if (!archive)
+ {
+ AZ_Error(s_traceName, false, "Failed to open archive file '%s'", archivePath.c_str());
+ return false;
+ }
+
+ AZStd::vector fileEntries;
+ int result = archive->ListAllFiles(fileEntries);
+ outFileEntries.clear();
+ for (const auto& path : fileEntries)
+ {
+ outFileEntries.emplace_back(path.String());
+ }
+ return (result == AZ::IO::ZipDir::ZD_ERROR_SUCCESS);
+ }
+
+
+ std::future ArchiveComponent::AddFileToArchive(
+ const AZStd::string& archivePath,
+ const AZStd::string& workingDirectory,
+ const AZStd::string& fileToAdd)
+ {
+ if (!CheckParamsForAdd(workingDirectory, fileToAdd))
+ {
+ std::promise p;
+ p.set_value(false);
+ return p.get_future();
+ }
+
+ auto FnAddFileToArchive = [this, archivePath, workingDirectory, fileToAdd](std::promise&& p) -> void
+ {
+ auto archive = m_archive->OpenArchive(archivePath);
+ if (!archive)
+ {
+ AZ_Error(s_traceName, false, "Failed to open archive file '%s'", archivePath.c_str());
+ p.set_value(false);
+ return;
+ }
+
+ AZ::IO::Path workingPath{ workingDirectory };
+ AZ::IO::Path fullPath = workingPath / fileToAdd;
+ AZ::IO::PathView relativePath = AZ::IO::PathView{ fullPath }.LexicallyRelative(workingPath);
+
+ AZStd::vector fileBuffer;
+ bool success = false;
+ if (ArchiveUtils::ReadFile(fullPath, AZ::IO::OpenMode::ModeRead, fileBuffer))
+ {
+ int result = archive->UpdateFile(
+ relativePath.Native(), fileBuffer.data(), fileBuffer.size(), s_compressionMethod,
+ s_compressionLevel, s_compressionCodec);
+
+ success = (result == AZ::IO::ZipDir::ZD_ERROR_SUCCESS);
+ AZ_Error(
+ s_traceName, success, "Error %d encountered while adding '%s' to archive '%.*s'", result, fileToAdd.c_str(),
+ AZ_STRING_ARG(archive->GetFullPath().Native()));
}
else
{
- AZ::TickBus::QueueFunction(respCallback, (exitCode == static_cast(SevenZipExitCode::NoError)), AZStd::move(consoleOutput));
+ AZ_Error(
+ s_traceName, false, "Error encountered while reading '%s' to add to archive '%.*s'", fileToAdd.c_str(),
+ AZ_STRING_ARG(archive->GetFullPath().Native()));
}
- if (!taskHandle.IsNull())
- {
- AZStd::unique_lock lock(m_threadControlMutex);
- ThreadInfo& tInfo = m_threadInfoMap[taskHandle];
- tInfo.threads.erase(AZStd::this_thread::get_id());
- m_cv.notify_all();
- }
+ archive.reset();
+ p.set_value(success);
};
- if (!taskHandle.IsNull())
- {
- AZStd::thread processThread(sevenZJob);
- AZStd::unique_lock lock(m_threadControlMutex);
- ThreadInfo& info = m_threadInfoMap[taskHandle];
- m_cv.wait(lock, [&info, &processThread]() {
- return info.threads.find(processThread.get_id()) != info.threads.end();
- });
- processThread.detach();
- }
- else
- {
- sevenZJob();
- }
+
+ // Async task...
+ std::promise p;
+ std::future f = p.get_future();
+
+ AZStd::thread_desc threadDesc;
+ threadDesc.m_name = "Archive Task (Add Single)";
+ m_threads.emplace_back(threadDesc, FnAddFileToArchive, AZStd::move(p));
+ return f;
}
+
+
+ std::future ArchiveComponent::AddFilesToArchive(
+ const AZStd::string& archivePath,
+ const AZStd::string& workingDirectory,
+ const AZStd::string& listFilePath)
+ {
+ if (!CheckParamsForAdd(workingDirectory, listFilePath))
+ {
+ std::promise p;
+ p.set_value(false);
+ return p.get_future();
+ }
+
+ auto FnAddFilesToArchive = [this, archivePath, workingDirectory, listFilePath](std::promise&& p) -> void
+ {
+ auto archive = m_archive->OpenArchive(archivePath);
+ if (!archive)
+ {
+ AZ_Error(s_traceName, false, "Failed to open archive file '%s'", archivePath.c_str());
+ p.set_value(false);
+ return;
+ }
+
+ bool success = true; // starts true and turns false when any error is encountered.
+ AZ::IO::Path basePath{ workingDirectory };
+
+ auto PerLineCallback = [&success, &basePath, &archive](AZStd::string_view filePathLine) -> void
+ {
+ AZStd::vector fileBuffer;
+ AZ::IO::Path fullPath = (basePath / filePathLine);
+ if (ArchiveUtils::ReadFile(fullPath, AZ::IO::OpenMode::ModeRead, fileBuffer))
+ {
+ int result = archive->UpdateFile(
+ filePathLine, fileBuffer.data(), fileBuffer.size(), s_compressionMethod,
+ s_compressionLevel, s_compressionCodec);
+
+ bool thisSuccess = (result == AZ::IO::ZipDir::ZD_ERROR_SUCCESS);
+ success = (success && thisSuccess);
+ AZ_Error(
+ s_traceName, thisSuccess, "Error %d encountered while adding '%.*s' to archive '%.*s'", result,
+ AZ_STRING_ARG(filePathLine), AZ_STRING_ARG(archive->GetFullPath().Native()));
+ }
+ else
+ {
+ AZ_Error(
+ s_traceName, false, "Error encountered while reading '%.*s' to add to archive '%.*s'", AZ_STRING_ARG(filePathLine),
+ AZ_STRING_ARG(archive->GetFullPath().Native()));
+ }
+ };
+
+ ArchiveUtils::ProcessFileList(listFilePath, PerLineCallback);
+
+ archive.reset();
+ p.set_value(success);
+ };
+
+ // Async task...
+ std::promise p;
+ std::future f = p.get_future();
+
+ AZStd::thread_desc threadDesc;
+ threadDesc.m_name = "Archive Task (Add)";
+ m_threads.emplace_back(threadDesc, FnAddFilesToArchive, AZStd::move(p));
+ return f;
+ }
+
+
+ bool ArchiveComponent::CheckParamsForAdd(const AZStd::string& directory, const AZStd::string& file)
+ {
+ if (!m_fileIO || !m_archive)
+ {
+ return false;
+ }
+
+ if (!m_fileIO->IsDirectory(directory.c_str()))
+ {
+ AZ_Error(
+ s_traceName, false, "Working directory '%s' is not a directory or doesn't exist!", directory.c_str());
+ return false;
+ }
+
+ if (!file.empty())
+ {
+ auto filePath = AZ::IO::Path{ directory } / file;
+ if (!m_fileIO->Exists(filePath.c_str()) || m_fileIO->IsDirectory(filePath.c_str()))
+ {
+ AZ_Error(s_traceName, false, "File list '%s' is a directory or doesn't exist!", filePath.c_str());
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool ArchiveComponent::CheckParamsForExtract(const AZStd::string& archive, const AZStd::string& directory)
+ {
+ if (!m_fileIO || !m_archive)
+ {
+ return false;
+ }
+
+ if (!m_fileIO->Exists(archive.c_str()))
+ {
+ AZ_Error(s_traceName, false, "Archive '%s' does not exist!", archive.c_str());
+ return false;
+ }
+
+ if (!m_fileIO->Exists(directory.c_str()))
+ {
+ if (!m_fileIO->CreatePath(directory.c_str()))
+ {
+ AZ_Error(s_traceName, false, "Failed to create destination directory '%s'", directory.c_str());
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool ArchiveComponent::CheckParamsForCreate(const AZStd::string& archive, const AZStd::string& directory)
+ {
+ if (!m_fileIO || !m_archive)
+ {
+ return false;
+ }
+
+ if (m_fileIO->Exists(archive.c_str()))
+ {
+ AZ_Error(s_traceName, false, "Archive file '%s' already exists, cannot create a new archive there!");
+ return false;
+ }
+
+ if (!m_fileIO->IsDirectory(directory.c_str()))
+ {
+ AZ_Error(s_traceName, false, "Directory '%s' is not a directory or doesn't exist!", directory.c_str());
+ return false;
+ }
+
+ return true;
+ }
+
} // namespace AzToolsFramework
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Archive/ArchiveComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Archive/ArchiveComponent.h
index fc344fc5a6..e2b437bcf2 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Archive/ArchiveComponent.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Archive/ArchiveComponent.h
@@ -10,80 +10,76 @@
#include
#include
+#include
#include
#include