Merge remote-tracking branch 'upstream/development' into ReturnCachedGemJsons
This commit is contained in:
@@ -54,4 +54,5 @@ set(ENABLED_GEMS
|
||||
AWSMetrics
|
||||
PrefabBuilder
|
||||
AudioSystem
|
||||
Profiler
|
||||
)
|
||||
|
||||
+2
-3
@@ -204,7 +204,7 @@ namespace PythonCoverage
|
||||
return coveringModuleOutputNames;
|
||||
}
|
||||
|
||||
void PythonCoverageEditorSystemComponent::OnStartExecuteByFilenameAsTest(AZStd::string_view filename, AZStd::string_view testCase, [[maybe_unused]] const AZStd::vector<AZStd::string_view>& args)
|
||||
void PythonCoverageEditorSystemComponent::OnStartExecuteByFilenameAsTest([[maybe_unused]]AZStd::string_view filename, AZStd::string_view testCase, [[maybe_unused]] const AZStd::vector<AZStd::string_view>& args)
|
||||
{
|
||||
if (m_coverageState == CoverageState::Disabled)
|
||||
{
|
||||
@@ -226,8 +226,7 @@ namespace PythonCoverage
|
||||
return;
|
||||
}
|
||||
|
||||
const AZStd::string scriptName = AZ::IO::Path(filename).Stem().Native();
|
||||
const auto coverageFile = m_coverageDir / AZStd::string::format("%s.pycoverage", scriptName.c_str());
|
||||
const auto coverageFile = m_coverageDir / AZStd::string::format("%.*s.pycoverage", AZ_STRING_ARG(testCase));
|
||||
|
||||
// If this is a different python script we clear the existing entity components and start afresh
|
||||
if (m_coverageFile != coverageFile)
|
||||
|
||||
+26
-6
@@ -122,17 +122,32 @@ class EditorEntity:
|
||||
|
||||
# Creation functions
|
||||
@classmethod
|
||||
def find_editor_entity(cls, entity_name: str) -> EditorEntity:
|
||||
def find_editor_entity(cls, entity_name: str, must_be_unique : bool = False) -> EditorEntity:
|
||||
"""
|
||||
Given Entity name, outputs entity object
|
||||
:param entity_name: Name of entity to find
|
||||
:return: EditorEntity class object
|
||||
"""
|
||||
entity_id = general.find_editor_entity(entity_name)
|
||||
assert entity_id.IsValid(), f"Failure: Couldn't find entity with name: '{entity_name}'"
|
||||
entity = cls(entity_id)
|
||||
entities = cls.find_editor_entities([entity_name])
|
||||
assert len(entities) != 0, f"Failure: Couldn't find entity with name: '{entity_name}'"
|
||||
if must_be_unique:
|
||||
assert len(entities) == 1, f"Failure: Multiple entities with name: '{entity_name}' when expected only one"
|
||||
|
||||
entity = cls(entities[0])
|
||||
return entity
|
||||
|
||||
@classmethod
|
||||
def find_editor_entities(cls, entity_names: List[str]) -> EditorEntity:
|
||||
"""
|
||||
Given Entities names, returns a list of EditorEntity
|
||||
:param entity_name: Name of entity to find
|
||||
:return: List[EditorEntity] class object
|
||||
"""
|
||||
searchFilter = azlmbr.entity.SearchFilter()
|
||||
searchFilter.names = entity_names
|
||||
ids = azlmbr.entity.SearchBus(bus.Broadcast, 'SearchEntities', searchFilter)
|
||||
return [cls(id) for id in ids]
|
||||
|
||||
@classmethod
|
||||
def create_editor_entity(cls, name: str = None, parent_id=None) -> EditorEntity:
|
||||
"""
|
||||
@@ -157,8 +172,7 @@ class EditorEntity:
|
||||
cls,
|
||||
entity_position: Union[List, Tuple, math.Vector3],
|
||||
name: str = None,
|
||||
parent_id: azlmbr.entity.EntityId = None,
|
||||
) -> EditorEntity:
|
||||
parent_id: azlmbr.entity.EntityId = None) -> EditorEntity:
|
||||
"""
|
||||
Used to create entity at position using 'CreateNewEntityAtPosition' Bus.
|
||||
:param entity_position: World Position(X, Y, Z) of entity in viewport.
|
||||
@@ -227,6 +241,12 @@ class EditorEntity:
|
||||
"""
|
||||
return editor.EditorEntityInfoRequestBus(bus.Event, "GetChildren", self.id)
|
||||
|
||||
def get_children(self) -> List[EditorEntity]:
|
||||
"""
|
||||
:return: List of EditorEntity children. Type: [EditorEntity]
|
||||
"""
|
||||
return [EditorEntity(child_id) for child_id in self.get_children_ids()]
|
||||
|
||||
def add_component(self, component_name: str) -> EditorComponent:
|
||||
"""
|
||||
Used to add new component to Entity.
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ def launch_and_validate_results(request, test_directory, editor, editor_script,
|
||||
logger.debug("Running automated test: {}".format(editor_script))
|
||||
editor.args.extend(["--skipWelcomeScreenDialog", "--regset=/Amazon/Settings/EnableSourceControl=false",
|
||||
"--regset=/Amazon/Preferences/EnablePrefabSystem=false", run_python, test_case,
|
||||
f"--pythontestcase={request.node.originalname}", "--runpythonargs", " ".join(cfg_args)])
|
||||
f"--pythontestcase={request.node.name}", "--runpythonargs", " ".join(cfg_args)])
|
||||
if auto_test_mode:
|
||||
editor.args.extend(["--autotest_mode"])
|
||||
if null_renderer:
|
||||
|
||||
+40
-23
@@ -12,20 +12,39 @@ from os import path
|
||||
|
||||
from PySide2 import QtWidgets
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
from azlmbr.entity import EntityId
|
||||
from azlmbr.math import Vector3
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.utils import Report
|
||||
|
||||
import azlmbr.entity as entity
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.prefab as prefab
|
||||
import editor_python_test_tools.pyside_utils as pyside_utils
|
||||
import prefab.Prefab_Test_Utils as prefab_test_utils
|
||||
|
||||
|
||||
def get_prefab_file_path(prefab_path):
|
||||
if not path.isabs(prefab_path):
|
||||
prefab_path = path.join(general.get_file_alias("@projectroot@"), prefab_path)
|
||||
|
||||
# Append prefab if it doesn't contain .prefab on it
|
||||
name, ext = path.splitext(prefab_path)
|
||||
if ext != ".prefab":
|
||||
prefab_path = name + ".prefab"
|
||||
return prefab_path
|
||||
|
||||
|
||||
def get_all_entity_ids():
|
||||
return entity.SearchBus(bus.Broadcast, 'SearchEntities', entity.SearchFilter())
|
||||
|
||||
def wait_for_propagation():
|
||||
general.idle_wait_frames(1)
|
||||
|
||||
# This is a helper class which contains some of the useful information about a prefab instance.
|
||||
class PrefabInstance:
|
||||
|
||||
def __init__(self, prefab_file_name: str=None, container_entity: EditorEntity=EntityId()):
|
||||
def __init__(self, prefab_file_name: str = None, container_entity: EditorEntity = None):
|
||||
self.prefab_file_name: str = prefab_file_name
|
||||
self.container_entity: EditorEntity = container_entity
|
||||
|
||||
@@ -42,7 +61,7 @@ class PrefabInstance:
|
||||
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:
|
||||
def is_valid(self) -> bool:
|
||||
return self.container_entity.id.IsValid() and self.prefab_file_name in Prefab.existing_prefabs
|
||||
|
||||
"""
|
||||
@@ -60,7 +79,7 @@ class PrefabInstance:
|
||||
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())
|
||||
pyside_utils.run_soon(lambda: wait_for_propagation())
|
||||
|
||||
try:
|
||||
active_modal_widget = await pyside_utils.wait_for_modal_widget()
|
||||
@@ -94,19 +113,18 @@ class Prefab:
|
||||
|
||||
existing_prefabs = {}
|
||||
|
||||
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)
|
||||
def __init__(self, file_path: str):
|
||||
self.file_path: str = get_prefab_file_path(file_path)
|
||||
self.instances: set[PrefabInstance] = set()
|
||||
|
||||
"""
|
||||
Check if a prefab is ready to be used to generate its instances.
|
||||
:param file_name: A unique file name of the target prefab.
|
||||
:param file_path: A unique file path of the target prefab.
|
||||
:return: Whether the target prefab is loaded or not.
|
||||
"""
|
||||
@classmethod
|
||||
def is_prefab_loaded(cls, file_name: str) -> bool:
|
||||
return file_name in Prefab.existing_prefabs
|
||||
def is_prefab_loaded(cls, file_path: str) -> bool:
|
||||
return file_path in Prefab.existing_prefabs
|
||||
|
||||
"""
|
||||
Check if a prefab exists in the directory for files of prefab tests.
|
||||
@@ -114,9 +132,8 @@ class Prefab:
|
||||
:return: Whether the target prefab exists or not.
|
||||
"""
|
||||
@classmethod
|
||||
def prefab_exists(cls, file_name: str) -> bool:
|
||||
file_path = prefab_test_utils.get_prefab_file_path(file_name)
|
||||
return path.exists(file_path)
|
||||
def prefab_exists(cls, file_path: str) -> bool:
|
||||
return path.exists(get_prefab_file_path(file_path))
|
||||
|
||||
"""
|
||||
Return a prefab which can be used immediately.
|
||||
@@ -125,10 +142,11 @@ class Prefab:
|
||||
"""
|
||||
@classmethod
|
||||
def get_prefab(cls, file_name: str) -> Prefab:
|
||||
assert file_name, "Received an empty file_name"
|
||||
if Prefab.is_prefab_loaded(file_name):
|
||||
return Prefab.existing_prefabs[file_name]
|
||||
else:
|
||||
assert Prefab.prefab_exists(file_name), f"Attempted to get a prefab {file_name} that doesn't exist"
|
||||
assert Prefab.prefab_exists(file_name), f"Attempted to get a prefab \"{file_name}\" that doesn't exist"
|
||||
new_prefab = Prefab(file_name)
|
||||
Prefab.existing_prefabs[file_name] = Prefab(file_name)
|
||||
return new_prefab
|
||||
@@ -141,7 +159,7 @@ class Prefab:
|
||||
: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, PrefabInstance):
|
||||
def create_prefab(cls, entities: list[EditorEntity], file_name: str, prefab_instance_name: str=None) -> tuple(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)
|
||||
@@ -155,7 +173,7 @@ class Prefab:
|
||||
if prefab_instance_name:
|
||||
container_entity.set_name(prefab_instance_name)
|
||||
|
||||
prefab_test_utils.wait_for_propagation()
|
||||
wait_for_propagation()
|
||||
|
||||
new_prefab_instance = PrefabInstance(file_name, EditorEntity(container_entity_id))
|
||||
new_prefab.instances.add(new_prefab_instance)
|
||||
@@ -182,12 +200,13 @@ class Prefab:
|
||||
delete_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'DeleteEntitiesAndAllDescendantsInInstance', container_entity_ids)
|
||||
assert delete_prefab_result.IsSuccess(), f"Prefab operation 'DeleteEntitiesAndAllDescendantsInInstance' failed. Error: {delete_prefab_result.GetError()}"
|
||||
|
||||
prefab_test_utils.wait_for_propagation()
|
||||
wait_for_propagation()
|
||||
|
||||
entity_ids_after_delete = set(prefab_test_utils.get_all_entities())
|
||||
entity_ids_after_delete = set(get_all_entity_ids())
|
||||
|
||||
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."
|
||||
assert False, "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)
|
||||
@@ -215,12 +234,10 @@ class Prefab:
|
||||
if name:
|
||||
container_entity.set_name(name)
|
||||
|
||||
prefab_test_utils.wait_for_propagation()
|
||||
wait_for_propagation()
|
||||
|
||||
new_prefab_instance = PrefabInstance(self.file_name, EditorEntity(container_entity_id))
|
||||
new_prefab_instance = PrefabInstance(self.file_path, 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 new_prefab_instance
|
||||
+1
-1
@@ -126,7 +126,7 @@ def ForceRegion_LinearDampingForceOnRigidBodies():
|
||||
|
||||
# Constants
|
||||
CLOSE_ENOUGH = 0.001
|
||||
TIME_OUT = 3.0
|
||||
TIME_OUT = 10.0
|
||||
INITIAL_VELOCITY = azmath.Vector3(0.0, 0.0, -10.0)
|
||||
|
||||
# 1) Open level / Enter game mode
|
||||
|
||||
@@ -90,7 +90,7 @@ class TestAutomationBase:
|
||||
editor_starttime = time.time()
|
||||
self.logger.debug("Running automated test")
|
||||
testcase_module_filepath = self._get_testcase_module_filepath(testcase_module)
|
||||
pycmd = ["--runpythontest", testcase_module_filepath, f"-pythontestcase={request.node.originalname}"]
|
||||
pycmd = ["--runpythontest", testcase_module_filepath, f"-pythontestcase={request.node.name}"]
|
||||
if use_null_renderer:
|
||||
pycmd += ["-rhi=null"]
|
||||
if batch_mode:
|
||||
|
||||
@@ -29,21 +29,21 @@ class TestAutomation(TestAutomationBase):
|
||||
autotest_mode=autotest_mode)
|
||||
|
||||
def test_PrefabLevel_OpensLevelWithEntities(self, request, workspace, editor, launcher_platform):
|
||||
from . import PrefabLevel_OpensLevelWithEntities as test_module
|
||||
from .tests import PrefabLevel_OpensLevelWithEntities as test_module
|
||||
self._run_prefab_test(request, workspace, editor, test_module)
|
||||
|
||||
def test_Prefab_BasicWorkflow_CreatePrefab(self, request, workspace, editor, launcher_platform):
|
||||
from . import Prefab_BasicWorkflow_CreatePrefab as test_module
|
||||
def test_PrefabBasicWorkflow_CreatePrefab(self, request, workspace, editor, launcher_platform):
|
||||
from .tests import PrefabBasicWorkflow_CreatePrefab as test_module
|
||||
self._run_prefab_test(request, workspace, editor, test_module)
|
||||
|
||||
def test_Prefab_BasicWorkflow_InstantiatePrefab(self, request, workspace, editor, launcher_platform):
|
||||
from . import Prefab_BasicWorkflow_InstantiatePrefab as test_module
|
||||
def test_PrefabBasicWorkflow_InstantiatePrefab(self, request, workspace, editor, launcher_platform):
|
||||
from .tests import PrefabBasicWorkflow_InstantiatePrefab as test_module
|
||||
self._run_prefab_test(request, workspace, editor, test_module)
|
||||
|
||||
def test_Prefab_BasicWorkflow_CreateAndDeletePrefab(self, request, workspace, editor, launcher_platform):
|
||||
from . import Prefab_BasicWorkflow_CreateAndDeletePrefab as test_module
|
||||
def test_PrefabBasicWorkflow_CreateAndDeletePrefab(self, request, workspace, editor, launcher_platform):
|
||||
from .tests import PrefabBasicWorkflow_CreateAndDeletePrefab as test_module
|
||||
self._run_prefab_test(request, workspace, editor, test_module)
|
||||
|
||||
def test_Prefab_BasicWorkflow_CreateAndReparentPrefab(self, request, workspace, editor, launcher_platform):
|
||||
from . import Prefab_BasicWorkflow_CreateAndReparentPrefab as test_module
|
||||
def test_PrefabBasicWorkflow_CreateAndReparentPrefab(self, request, workspace, editor, launcher_platform):
|
||||
from .tests import PrefabBasicWorkflow_CreateAndReparentPrefab as test_module
|
||||
self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False)
|
||||
|
||||
+6
-6
@@ -5,14 +5,14 @@ For complete copyright and license terms please see the LICENSE at the root of t
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
def Prefab_BasicWorkflow_CreateAndDeletePrefab():
|
||||
def PrefabBasicWorkflow_CreateAndDeletePrefab():
|
||||
|
||||
CAR_PREFAB_FILE_NAME = 'car_prefab'
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from prefab.Prefab import Prefab
|
||||
from editor_python_test_tools.prefab_utils import Prefab
|
||||
|
||||
import prefab.Prefab_Test_Utils as prefab_test_utils
|
||||
import PrefabTestUtils as prefab_test_utils
|
||||
|
||||
prefab_test_utils.open_base_tests_level()
|
||||
|
||||
@@ -21,13 +21,13 @@ def Prefab_BasicWorkflow_CreateAndDeletePrefab():
|
||||
car_entity = EditorEntity.create_editor_entity()
|
||||
car_prefab_entities = [car_entity]
|
||||
|
||||
# Checks for prefab creation passed or not
|
||||
# Asserts if prefab creation doesn't succeeds
|
||||
_, car = Prefab.create_prefab(
|
||||
car_prefab_entities, CAR_PREFAB_FILE_NAME)
|
||||
|
||||
# Checks for prefab deletion passed or not
|
||||
# Asserts if prefab deletion fails
|
||||
Prefab.remove_prefabs([car])
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(Prefab_BasicWorkflow_CreateAndDeletePrefab)
|
||||
Report.start_test(PrefabBasicWorkflow_CreateAndDeletePrefab)
|
||||
+4
-4
@@ -5,7 +5,7 @@ For complete copyright and license terms please see the LICENSE at the root of t
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
def Prefab_BasicWorkflow_CreateAndReparentPrefab():
|
||||
def PrefabBasicWorkflow_CreateAndReparentPrefab():
|
||||
|
||||
CAR_PREFAB_FILE_NAME = 'car_prefab'
|
||||
WHEEL_PREFAB_FILE_NAME = 'wheel_prefab'
|
||||
@@ -16,9 +16,9 @@ def Prefab_BasicWorkflow_CreateAndReparentPrefab():
|
||||
async def run_test():
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from prefab.Prefab import Prefab
|
||||
from editor_python_test_tools.prefab_utils import Prefab
|
||||
|
||||
import prefab.Prefab_Test_Utils as prefab_test_utils
|
||||
import PrefabTestUtils as prefab_test_utils
|
||||
|
||||
prefab_test_utils.open_base_tests_level()
|
||||
|
||||
@@ -46,4 +46,4 @@ def Prefab_BasicWorkflow_CreateAndReparentPrefab():
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(Prefab_BasicWorkflow_CreateAndReparentPrefab)
|
||||
Report.start_test(PrefabBasicWorkflow_CreateAndReparentPrefab)
|
||||
+4
-4
@@ -5,15 +5,15 @@ For complete copyright and license terms please see the LICENSE at the root of t
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
def Prefab_BasicWorkflow_CreatePrefab():
|
||||
def PrefabBasicWorkflow_CreatePrefab():
|
||||
|
||||
CAR_PREFAB_FILE_NAME = 'car_prefab'
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.utils import Report
|
||||
from prefab.Prefab import Prefab
|
||||
from editor_python_test_tools.prefab_utils import Prefab
|
||||
|
||||
import prefab.Prefab_Test_Utils as prefab_test_utils
|
||||
import PrefabTestUtils as prefab_test_utils
|
||||
|
||||
prefab_test_utils.open_base_tests_level()
|
||||
|
||||
@@ -27,4 +27,4 @@ def Prefab_BasicWorkflow_CreatePrefab():
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(Prefab_BasicWorkflow_CreatePrefab)
|
||||
Report.start_test(PrefabBasicWorkflow_CreatePrefab)
|
||||
+5
-5
@@ -5,17 +5,17 @@ For complete copyright and license terms please see the LICENSE at the root of t
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
def Prefab_BasicWorkflow_InstantiatePrefab():
|
||||
def PrefabBasicWorkflow_InstantiatePrefab():
|
||||
|
||||
from azlmbr.math import Vector3
|
||||
|
||||
EXISTING_TEST_PREFAB_FILE_NAME = "Test"
|
||||
EXISTING_TEST_PREFAB_FILE_NAME = "Gem/PythonTests/Prefab/data/Test.prefab"
|
||||
INSTANTIATED_TEST_PREFAB_POSITION = Vector3(10.00, 20.0, 30.0)
|
||||
EXPECTED_TEST_PREFAB_CHILDREN_COUNT = 1
|
||||
|
||||
from prefab.Prefab import Prefab
|
||||
from editor_python_test_tools.prefab_utils import Prefab
|
||||
|
||||
import prefab.Prefab_Test_Utils as prefab_test_utils
|
||||
import PrefabTestUtils as prefab_test_utils
|
||||
|
||||
prefab_test_utils.open_base_tests_level()
|
||||
|
||||
@@ -31,4 +31,4 @@ def Prefab_BasicWorkflow_InstantiatePrefab():
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(Prefab_BasicWorkflow_InstantiatePrefab)
|
||||
Report.start_test(PrefabBasicWorkflow_InstantiatePrefab)
|
||||
+3
-3
@@ -7,9 +7,9 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
# fmt:off
|
||||
class Tests():
|
||||
find_empty_entity = ("Entity: 'EmptyEntity' found", "Entity: 'EmptyEntity' *not* found in level")
|
||||
empty_entity_pos = ("'EmptyEntity' position is at the expected position", "'EmptyEntity' position is *not* at the expected position")
|
||||
find_pxentity = ("Entity: 'EntityWithPxCollider' found", "Entity: 'EntityWithPxCollider' *not* found in level")
|
||||
find_empty_entity = ("Entity: 'EmptyEntity' found", "Entity: 'EmptyEntity' *not* found in level")
|
||||
empty_entity_pos = ("'EmptyEntity' position is at the expected position", "'EmptyEntity' position is *not* at the expected position")
|
||||
find_pxentity = ("Entity: 'EntityWithPxCollider' found", "Entity: 'EntityWithPxCollider' *not* found in level")
|
||||
pxentity_component = ("Entity: 'EntityWithPxCollider' has a Physx Collider", "Entity: 'EntityWithPxCollider' does *not* have a Physx Collider")
|
||||
|
||||
# fmt:on
|
||||
-17
@@ -18,20 +18,6 @@ import azlmbr.components as components
|
||||
import azlmbr.entity as entity
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
def get_prefab_file_name(prefab_name):
|
||||
return prefab_name + ".prefab"
|
||||
|
||||
def get_prefab_file_path(prefab_name):
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), get_prefab_file_name(prefab_name))
|
||||
|
||||
def find_entities_by_name(entity_name):
|
||||
searchFilter = entity.SearchFilter()
|
||||
searchFilter.names = [entity_name]
|
||||
return entity.SearchBus(bus.Broadcast, 'SearchEntities', searchFilter)
|
||||
|
||||
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 = (
|
||||
"entity is at expected position",
|
||||
@@ -74,9 +60,6 @@ def get_children_ids_by_name(entity_id, entity_name):
|
||||
|
||||
return result
|
||||
|
||||
def wait_for_propagation():
|
||||
general.idle_wait_frames(1)
|
||||
|
||||
def open_base_tests_level():
|
||||
helper.init_idle()
|
||||
helper.open_level("Prefab", "Base")
|
||||
@@ -1,44 +0,0 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
|
||||
UI Apps: AutomatedTesting.GameLauncher
|
||||
Launch AutomatedTesting.GameLauncher with Simple level
|
||||
Test should run in both gpu and non gpu
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import psutil
|
||||
|
||||
import ly_test_tools.environment.waiter as waiter
|
||||
import editor_python_test_tools.hydra_test_utils as editor_test_utils
|
||||
from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole
|
||||
from ly_remote_console.remote_console_commands import (
|
||||
send_command_and_expect_response as send_command_and_expect_response,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("launcher_platform", ["windows"])
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("level", ["Simple"])
|
||||
@pytest.mark.SUITE_smoke
|
||||
class TestRemoteConsoleLoadLevelWorks(object):
|
||||
@pytest.fixture
|
||||
def remote_console_instance(self, request):
|
||||
console = RemoteConsole()
|
||||
|
||||
def teardown():
|
||||
if console.connected:
|
||||
console.stop()
|
||||
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
return console
|
||||
|
||||
def test_RemoteConsole_LoadLevel_Works(self, launcher, level, remote_console_instance, launcher_platform):
|
||||
expected_lines = ['Level system is loading "Simple"']
|
||||
|
||||
editor_test_utils.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines, null_renderer=True)
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e4937547ca4c486ef59656314401933217e0e0401fec103e1fb91c25ec60a177
|
||||
size 2806
|
||||
oid sha256:a5f9e27e0f22c31ca61d866fb594c6fde5b8ceb891e17dda075fa1e0033ec2b9
|
||||
size 1666
|
||||
|
||||
+18
-16
@@ -287,21 +287,22 @@ bool CCryDocManager::DoPromptFileName(QString& fileName, [[maybe_unused]] UINT n
|
||||
|
||||
return false;
|
||||
}
|
||||
CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* lpszFileName, bool bAddToMRU)
|
||||
CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions)
|
||||
{
|
||||
assert(lpszFileName != nullptr);
|
||||
assert(filename != nullptr);
|
||||
|
||||
const bool reopenIfSame = openSameLevelOptions == COpenSameLevelOptions::ReopenLevelIfSame;
|
||||
// find the highest confidence
|
||||
auto pos = m_templateList.begin();
|
||||
CCrySingleDocTemplate::Confidence bestMatch = CCrySingleDocTemplate::noAttempt;
|
||||
CCrySingleDocTemplate* pBestTemplate = nullptr;
|
||||
CCryEditDoc* pOpenDocument = nullptr;
|
||||
|
||||
if (lpszFileName[0] == '\"')
|
||||
if (filename[0] == '\"')
|
||||
{
|
||||
++lpszFileName;
|
||||
++filename;
|
||||
}
|
||||
QString szPath = QString::fromUtf8(lpszFileName);
|
||||
QString szPath = QString::fromUtf8(filename);
|
||||
if (szPath.endsWith('"'))
|
||||
{
|
||||
szPath.remove(szPath.length() - 1, 1);
|
||||
@@ -325,7 +326,7 @@ CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* lpszFileName, bool bAd
|
||||
}
|
||||
}
|
||||
|
||||
if (pOpenDocument != nullptr)
|
||||
if (!reopenIfSame && pOpenDocument != nullptr)
|
||||
{
|
||||
return pOpenDocument;
|
||||
}
|
||||
@@ -336,7 +337,7 @@ CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* lpszFileName, bool bAd
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pBestTemplate->OpenDocumentFile(szPath.toUtf8().data(), bAddToMRU, false);
|
||||
return pBestTemplate->OpenDocumentFile(szPath.toUtf8().data(), addToMostRecentFileList, false);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -513,7 +514,7 @@ public:
|
||||
QString m_appRoot;
|
||||
QString m_logFile;
|
||||
QString m_pythonArgs;
|
||||
QString m_pythontTestCase;
|
||||
QString m_pythonTestCase;
|
||||
QString m_execFile;
|
||||
QString m_execLineCmd;
|
||||
|
||||
@@ -562,7 +563,7 @@ public:
|
||||
const std::vector<std::pair<CommandLineStringOption, QString&> > stringOptions = {
|
||||
{{"logfile", "File name of the log file to write out to.", "logfile"}, m_logFile},
|
||||
{{"runpythonargs", "Command-line argument string to pass to the python script if --runpython or --runpythontest was used.", "runpythonargs"}, m_pythonArgs},
|
||||
{{"pythontestcase", "Test case name of python test script if --runpythontest was used.", "pythontestcase"}, m_pythontTestCase},
|
||||
{{"pythontestcase", "Test case name of python test script if --runpythontest was used.", "pythontestcase"}, m_pythonTestCase},
|
||||
{{"exec", "cfg file to run on startup, used for systems like automation", "exec"}, m_execFile},
|
||||
{{"rhi", "Command-line argument to force which rhi to use", "dummyString"}, dummyString },
|
||||
{{"rhi-device-validation", "Command-line argument to configure rhi validation", "dummyString"}, dummyString },
|
||||
@@ -818,7 +819,7 @@ CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, b
|
||||
return OpenDocumentFile(lpszPathName, true, bMakeVisible);
|
||||
}
|
||||
|
||||
CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, bool bAddToMRU, [[maybe_unused]] bool bMakeVisible)
|
||||
CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, bool addToMostRecentFileList, [[maybe_unused]] bool bMakeVisible)
|
||||
{
|
||||
CCryEditDoc* pCurDoc = GetIEditor()->GetDocument();
|
||||
|
||||
@@ -848,7 +849,7 @@ CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, b
|
||||
{
|
||||
pCurDoc->OnOpenDocument(lpszPathName);
|
||||
pCurDoc->SetPathName(lpszPathName);
|
||||
if (bAddToMRU)
|
||||
if (addToMostRecentFileList)
|
||||
{
|
||||
CCryEditApp::instance()->AddToRecentFileList(lpszPathName);
|
||||
}
|
||||
@@ -1535,11 +1536,12 @@ void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo)
|
||||
{
|
||||
// Multiple testcases can be specified them with ';', these should match the files to run
|
||||
AZStd::vector<AZStd::string_view> testcaseList;
|
||||
QByteArray pythonTestCase = cmdInfo.m_pythonTestCase.toUtf8();
|
||||
testcaseList.resize(fileList.size());
|
||||
{
|
||||
int i = 0;
|
||||
AzFramework::StringFunc::TokenizeVisitor(
|
||||
fileStr.constData(),
|
||||
pythonTestCase.constData(),
|
||||
[&i, &testcaseList](AZStd::string_view elem)
|
||||
{
|
||||
testcaseList[i++] = (elem);
|
||||
@@ -2630,7 +2632,7 @@ void CCryEditApp::OnShowHelpers()
|
||||
void CCryEditApp::OnEditLevelData()
|
||||
{
|
||||
auto dir = QFileInfo(GetIEditor()->GetDocument()->GetLevelPathName()).dir();
|
||||
CFileUtil::EditTextFile(dir.absoluteFilePath("LevelData.xml").toUtf8().data());
|
||||
CFileUtil::EditTextFile(dir.absoluteFilePath("leveldata.xml").toUtf8().data());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -3364,7 +3366,7 @@ void CCryEditApp::OnOpenSlice()
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* lpszFileName)
|
||||
CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions)
|
||||
{
|
||||
if (m_openingLevel)
|
||||
{
|
||||
@@ -3404,9 +3406,9 @@ CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* lpszFileName)
|
||||
openDocTraceHandler.SetShowWindow(false);
|
||||
}
|
||||
|
||||
// in this case, we set bAddToMRU to always be true because adding files to the MRU list
|
||||
// in this case, we set addToMostRecentFileList to always be true because adding files to the MRU list
|
||||
// automatically culls duplicate and normalizes paths anyway
|
||||
m_pDocManager->OpenDocumentFile(lpszFileName, true);
|
||||
m_pDocManager->OpenDocumentFile(filename, addToMostRecentFileList, openSameLevelOptions);
|
||||
|
||||
if (openDocTraceHandler.HasAnyErrors())
|
||||
{
|
||||
|
||||
+11
-3
@@ -85,6 +85,12 @@ public:
|
||||
|
||||
using EditorIdleProcessingBus = AZ::EBus<EditorIdleProcessing>;
|
||||
|
||||
enum class COpenSameLevelOptions
|
||||
{
|
||||
ReopenLevelIfSame,
|
||||
NotReopenIfSame
|
||||
};
|
||||
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
class SANDBOX_API CCryEditApp
|
||||
@@ -174,7 +180,9 @@ public:
|
||||
virtual bool InitInstance();
|
||||
virtual int ExitInstance(int exitCode = 0);
|
||||
virtual bool OnIdle(LONG lCount);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszFileName);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* filename,
|
||||
bool addToMostRecentFileList=true,
|
||||
COpenSameLevelOptions openSameLevelOptions = COpenSameLevelOptions::NotReopenIfSame);
|
||||
|
||||
CCryDocManager* GetDocManager() { return m_pDocManager; }
|
||||
|
||||
@@ -448,7 +456,7 @@ public:
|
||||
~CCrySingleDocTemplate() {};
|
||||
// avoid creating another CMainFrame
|
||||
// close other type docs before opening any things
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszPathName, bool bAddToMRU, bool bMakeVisible);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszPathName, bool addToMostRecentFileList, bool bMakeVisible);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszPathName, bool bMakeVisible = TRUE);
|
||||
virtual Confidence MatchDocType(const char* lpszPathName, CCryEditDoc*& rpDocMatch);
|
||||
|
||||
@@ -468,7 +476,7 @@ public:
|
||||
virtual void OnFileNew();
|
||||
virtual bool DoPromptFileName(QString& fileName, UINT nIDSTitle,
|
||||
DWORD lFlags, bool bOpenFileDialog, CDocTemplate* pTemplate);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszFileName, bool bAddToMRU);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions = COpenSameLevelOptions::NotReopenIfSame);
|
||||
|
||||
QVector<CCrySingleDocTemplate*> m_templateList;
|
||||
};
|
||||
|
||||
+11
-13
@@ -143,20 +143,11 @@ namespace
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const bool addToMostRecentFileList = false;
|
||||
auto newDocument = CCryEditApp::instance()->OpenDocumentFile(levelPath.toUtf8().data(),
|
||||
addToMostRecentFileList, COpenSameLevelOptions::ReopenLevelIfSame);
|
||||
|
||||
auto previousDocument = GetIEditor()->GetDocument();
|
||||
QString previousPathName = (previousDocument != nullptr) ? previousDocument->GetLevelPathName() : "";
|
||||
auto newDocument = CCryEditApp::instance()->OpenDocumentFile(levelPath.toUtf8().data());
|
||||
|
||||
// the underlying document pointer doesn't change, so we can't check that; use the path name's instead
|
||||
|
||||
bool result = true;
|
||||
if (newDocument == nullptr || newDocument->IsLevelLoadFailed() || (newDocument->GetLevelPathName() == previousPathName))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
return newDocument != nullptr && !newDocument->IsLevelLoadFailed();
|
||||
}
|
||||
|
||||
bool PyOpenLevelNoPrompt(const char* pLevelName)
|
||||
@@ -407,6 +398,11 @@ inline namespace Commands
|
||||
{
|
||||
return AZ::Debug::Trace::WaitForDebugger(timeoutSeconds);
|
||||
}
|
||||
|
||||
AZStd::string PyGetFileAlias(AZStd::string alias)
|
||||
{
|
||||
return AZ::IO::FileIOBase::GetInstance()->GetAlias(alias.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -457,6 +453,8 @@ namespace AzToolsFramework
|
||||
addLegacyGeneral(behaviorContext->Method("attach_debugger", PyAttachDebugger, nullptr, "Prompts for attaching the debugger"));
|
||||
addLegacyGeneral(behaviorContext->Method("wait_for_debugger", PyWaitForDebugger, behaviorContext->MakeDefaultValues(-1.f), "Pauses this thread execution until the debugger has been attached"));
|
||||
|
||||
addLegacyGeneral(behaviorContext->Method("get_file_alias", PyGetFileAlias, nullptr, "Retrieves path for IO alias"));
|
||||
|
||||
// this will put these methods into the 'azlmbr.legacy.checkout_dialog' module
|
||||
auto addCheckoutDialog = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
|
||||
{
|
||||
|
||||
@@ -75,7 +75,10 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
|
||||
->Field("CaptureCursorLook", &CameraMovementSettings::m_captureCursorLook)
|
||||
->Field("OrbitYawRotationInverted", &CameraMovementSettings::m_orbitYawRotationInverted)
|
||||
->Field("PanInvertedX", &CameraMovementSettings::m_panInvertedX)
|
||||
->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY);
|
||||
->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY)
|
||||
->Field("DefaultPositionX", &CameraMovementSettings::m_defaultCameraPositionX)
|
||||
->Field("DefaultPositionY", &CameraMovementSettings::m_defaultCameraPositionY)
|
||||
->Field("DefaultPositionZ", &CameraMovementSettings::m_defaultCameraPositionZ);
|
||||
|
||||
serialize.Class<CameraInputSettings>()
|
||||
->Version(2)
|
||||
@@ -154,7 +157,16 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
|
||||
"Invert direction of pan in local Y axis")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_captureCursorLook, "Camera Capture Look Cursor",
|
||||
"Should the cursor be captured (hidden) while performing free look");
|
||||
"Should the cursor be captured (hidden) while performing free look")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionX, "Default X Camera Position",
|
||||
"Default X Camera Position when a level is opened")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionY, "Default Y Camera Position",
|
||||
"Default Y Camera Position when a level is opened")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionZ, "Default Z Camera Position",
|
||||
"Default Z Camera Position when a level is opened");
|
||||
|
||||
editContext->Class<CameraInputSettings>("Camera Input Settings", "")
|
||||
->DataElement(
|
||||
@@ -271,6 +283,12 @@ void CEditorPreferencesPage_ViewportCamera::OnApply()
|
||||
SandboxEditor::SetCameraOrbitYawRotationInverted(m_cameraMovementSettings.m_orbitYawRotationInverted);
|
||||
SandboxEditor::SetCameraPanInvertedX(m_cameraMovementSettings.m_panInvertedX);
|
||||
SandboxEditor::SetCameraPanInvertedY(m_cameraMovementSettings.m_panInvertedY);
|
||||
SandboxEditor::SetDefaultCameraEditorPosition(
|
||||
AZ::Vector3(
|
||||
m_cameraMovementSettings.m_defaultCameraPositionX,
|
||||
m_cameraMovementSettings.m_defaultCameraPositionY,
|
||||
m_cameraMovementSettings.m_defaultCameraPositionZ
|
||||
));
|
||||
|
||||
SandboxEditor::SetCameraTranslateForwardChannelId(m_cameraInputSettings.m_translateForwardChannelId);
|
||||
SandboxEditor::SetCameraTranslateBackwardChannelId(m_cameraInputSettings.m_translateBackwardChannelId);
|
||||
@@ -308,6 +326,11 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings()
|
||||
m_cameraMovementSettings.m_panInvertedX = SandboxEditor::CameraPanInvertedX();
|
||||
m_cameraMovementSettings.m_panInvertedY = SandboxEditor::CameraPanInvertedY();
|
||||
|
||||
AZ::Vector3 defaultCameraPosition = SandboxEditor::DefaultEditorCameraPosition();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionX = defaultCameraPosition.GetX();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionY = defaultCameraPosition.GetY();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionZ = defaultCameraPosition.GetZ();
|
||||
|
||||
m_cameraInputSettings.m_translateForwardChannelId = SandboxEditor::CameraTranslateForwardChannelId().GetName();
|
||||
m_cameraInputSettings.m_translateBackwardChannelId = SandboxEditor::CameraTranslateBackwardChannelId().GetName();
|
||||
m_cameraInputSettings.m_translateLeftChannelId = SandboxEditor::CameraTranslateLeftChannelId().GetName();
|
||||
|
||||
@@ -57,6 +57,9 @@ private:
|
||||
bool m_orbitYawRotationInverted;
|
||||
bool m_panInvertedX;
|
||||
bool m_panInvertedY;
|
||||
float m_defaultCameraPositionX;
|
||||
float m_defaultCameraPositionY;
|
||||
float m_defaultCameraPositionZ;
|
||||
|
||||
AZ::Crc32 RotateSmoothingVisibility() const
|
||||
{
|
||||
|
||||
@@ -52,6 +52,9 @@ namespace SandboxEditor
|
||||
constexpr AZStd::string_view CameraOrbitDollyIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitDollyId";
|
||||
constexpr AZStd::string_view CameraOrbitPanIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitPanId";
|
||||
constexpr AZStd::string_view CameraFocusIdSetting = "/Amazon/Preferences/Editor/Camera/FocusId";
|
||||
constexpr AZStd::string_view CameraDefaultStartingPositionX = "/Amazon/Preferences/Editor/Camera/DefaultStartingPosition/x";
|
||||
constexpr AZStd::string_view CameraDefaultStartingPositionY = "/Amazon/Preferences/Editor/Camera/DefaultStartingPosition/y";
|
||||
constexpr AZStd::string_view CameraDefaultStartingPositionZ = "/Amazon/Preferences/Editor/Camera/DefaultStartingPosition/z";
|
||||
|
||||
template<typename T>
|
||||
void SetRegistry(const AZStd::string_view setting, T&& value)
|
||||
@@ -111,6 +114,21 @@ namespace SandboxEditor
|
||||
return AZStd::make_unique<EditorViewportSettingsCallbacksImpl>();
|
||||
}
|
||||
|
||||
AZ::Vector3 DefaultEditorCameraPosition()
|
||||
{
|
||||
float xPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionX, 0.0));
|
||||
float yPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionY, -10.0));
|
||||
float zPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionZ, 4.0));
|
||||
return AZ::Vector3(xPosition, yPosition, zPosition);
|
||||
}
|
||||
|
||||
void SetDefaultCameraEditorPosition(const AZ::Vector3 defaultCameraPosition)
|
||||
{
|
||||
SetRegistry(CameraDefaultStartingPositionX, defaultCameraPosition.GetX());
|
||||
SetRegistry(CameraDefaultStartingPositionY, defaultCameraPosition.GetY());
|
||||
SetRegistry(CameraDefaultStartingPositionZ, defaultCameraPosition.GetZ());
|
||||
}
|
||||
|
||||
AZ::u64 MaxItemsShownInAssetBrowserSearch()
|
||||
{
|
||||
return GetRegistry(AssetBrowserMaxItemsShownInSearchSetting, aznumeric_cast<AZ::u64>(50));
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzFramework/Input/Channels/InputChannelId.h>
|
||||
|
||||
namespace SandboxEditor
|
||||
@@ -32,6 +33,9 @@ namespace SandboxEditor
|
||||
//! event will fire when a value in the settings registry (editorpreferences.setreg) is modified.
|
||||
SANDBOX_API AZStd::unique_ptr<EditorViewportSettingsCallbacks> CreateEditorViewportSettingsCallbacks();
|
||||
|
||||
SANDBOX_API AZ::Vector3 DefaultEditorCameraPosition();
|
||||
SANDBOX_API void SetDefaultCameraEditorPosition(AZ::Vector3 defaultCameraPosition);
|
||||
|
||||
SANDBOX_API AZ::u64 MaxItemsShownInAssetBrowserSearch();
|
||||
SANDBOX_API void SetMaxItemsShownInAssetBrowserSearch(AZ::u64 numberOfItemsShown);
|
||||
|
||||
|
||||
@@ -112,7 +112,6 @@ void StartFixedCursorMode(QObject *viewport);
|
||||
|
||||
#define RENDER_MESH_TEST_DISTANCE (0.2f)
|
||||
#define CURSOR_FONT_HEIGHT 8.0f
|
||||
|
||||
namespace AZ::ViewportHelpers
|
||||
{
|
||||
static const char TextCantCreateCameraNoLevel[] = "Cannot create camera when no level is loaded.";
|
||||
@@ -623,16 +622,10 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
PopDisableRendering();
|
||||
|
||||
{
|
||||
AZ::Aabb terrainAabb = AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero());
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(terrainAabb, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb);
|
||||
float sx = terrainAabb.GetXExtent();
|
||||
float sy = terrainAabb.GetYExtent();
|
||||
|
||||
Matrix34 viewTM;
|
||||
viewTM.SetIdentity();
|
||||
// Initial camera will be at middle of the map at the height of 2
|
||||
// meters above the terrain (default terrain height is 32)
|
||||
viewTM.SetTranslation(Vec3(sx * 0.5f, sy * 0.5f, 34.0f));
|
||||
|
||||
viewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition()));
|
||||
SetViewTM(viewTM);
|
||||
|
||||
UpdateScene();
|
||||
@@ -647,16 +640,10 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
PopDisableRendering();
|
||||
|
||||
{
|
||||
AZ::Aabb terrainAabb = AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero());
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(terrainAabb, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb);
|
||||
float sx = terrainAabb.GetXExtent();
|
||||
float sy = terrainAabb.GetYExtent();
|
||||
|
||||
Matrix34 viewTM;
|
||||
viewTM.SetIdentity();
|
||||
// Initial camera will be at middle of the map at the height of 2
|
||||
// meters above the terrain (default terrain height is 32)
|
||||
viewTM.SetTranslation(Vec3(sx * 0.5f, sy * 0.5f, 34.0f));
|
||||
|
||||
viewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition()));
|
||||
SetViewTM(viewTM);
|
||||
}
|
||||
break;
|
||||
@@ -1345,10 +1332,6 @@ void EditorViewportWidget::keyPressEvent(QKeyEvent* event)
|
||||
|
||||
void EditorViewportWidget::SetViewTM(const Matrix34& tm)
|
||||
{
|
||||
if (m_viewSourceType == ViewSourceType::None)
|
||||
{
|
||||
m_defaultViewTM = tm;
|
||||
}
|
||||
SetViewTM(tm, false);
|
||||
}
|
||||
|
||||
@@ -1445,6 +1428,10 @@ void EditorViewportWidget::SetViewTM(const Matrix34& camMatrix, bool bMoveOnly)
|
||||
"Please report this as a bug."
|
||||
);
|
||||
}
|
||||
else if (shouldUpdateObject == ShouldUpdateObject::No)
|
||||
{
|
||||
GetCurrentAtomView()->SetCameraTransform(LYTransformToAZMatrix3x4(camMatrix));
|
||||
}
|
||||
|
||||
if (m_pressedKeyState == KeyPressedState::PressedThisFrame)
|
||||
{
|
||||
@@ -2028,6 +2015,9 @@ void EditorViewportWidget::SetDefaultCamera()
|
||||
m_viewSourceType = ViewSourceType::None;
|
||||
GetViewManager()->SetCameraObjectId(GUID_NULL);
|
||||
SetName(m_defaultViewName);
|
||||
|
||||
// Set the default Editor Camera position.
|
||||
m_defaultViewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition()));
|
||||
SetViewTM(m_defaultViewTM);
|
||||
|
||||
// Synchronize the configured editor viewport FOV to the default camera
|
||||
@@ -2530,6 +2520,11 @@ bool EditorViewportSettings::StickySelectEnabled() const
|
||||
return SandboxEditor::StickySelectEnabled();
|
||||
}
|
||||
|
||||
AZ::Vector3 EditorViewportSettings::DefaultEditorCameraPosition() const
|
||||
{
|
||||
return SandboxEditor::DefaultEditorCameraPosition();
|
||||
}
|
||||
|
||||
AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once);
|
||||
|
||||
bool EditorViewportWidget::ShouldPreviewFullscreen() const
|
||||
@@ -2641,5 +2636,4 @@ void EditorViewportWidget::StopFullscreenPreview()
|
||||
// Show the main window
|
||||
MainWindow::instance()->show();
|
||||
}
|
||||
|
||||
#include <moc_EditorViewportWidget.cpp>
|
||||
|
||||
@@ -78,6 +78,7 @@ struct EditorViewportSettings : public AzToolsFramework::ViewportInteraction::Vi
|
||||
float ManipulatorLineBoundWidth() const override;
|
||||
float ManipulatorCircleBoundWidth() const override;
|
||||
bool StickySelectEnabled() const override;
|
||||
AZ::Vector3 DefaultEditorCameraPosition() const override;
|
||||
};
|
||||
|
||||
// EditorViewportWidget window
|
||||
|
||||
@@ -31,11 +31,11 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#define MUSIC_LEVEL_LIBRARY_FILE "Music.xml"
|
||||
#define MATERIAL_LEVEL_LIBRARY_FILE "Materials.xml"
|
||||
#define RESOURCE_LIST_FILE "ResourceList.txt"
|
||||
#define USED_RESOURCE_LIST_FILE "UsedResourceList.txt"
|
||||
#define SHADER_LIST_FILE "ShadersList.txt"
|
||||
#define MUSIC_LEVEL_LIBRARY_FILE "music.xml"
|
||||
#define MATERIAL_LEVEL_LIBRARY_FILE "materials.xml"
|
||||
#define RESOURCE_LIST_FILE "resourcelist.txt"
|
||||
#define USED_RESOURCE_LIST_FILE "usedresourcelist.txt"
|
||||
#define SHADER_LIST_FILE "shaderslist.txt"
|
||||
|
||||
#define GetAValue(rgb) ((BYTE)((rgb) >> 24))
|
||||
|
||||
@@ -185,9 +185,9 @@ bool CGameExporter::Export(unsigned int flags, [[maybe_unused]] EEndian eExportE
|
||||
ExportOcclusionMesh(sLevelPath.toUtf8().data());
|
||||
|
||||
//! Export Level data.
|
||||
CLogFile::WriteLine("Exporting LevelData.xml");
|
||||
CLogFile::WriteLine("Exporting leveldata.xml");
|
||||
ExportLevelData(sLevelPath);
|
||||
CLogFile::WriteLine("Exporting LevelData.xml done.");
|
||||
CLogFile::WriteLine("Exporting leveldata.xml done.");
|
||||
|
||||
ExportLevelInfo(sLevelPath);
|
||||
|
||||
@@ -266,26 +266,26 @@ void CGameExporter::ExportOcclusionMesh(const char* pszGamePath)
|
||||
void CGameExporter::ExportLevelData(const QString& path, bool /*bExportMission*/)
|
||||
{
|
||||
IEditor* pEditor = GetIEditor();
|
||||
pEditor->SetStatusText(QObject::tr("Exporting LevelData.xml..."));
|
||||
pEditor->SetStatusText(QObject::tr("Exporting leveldata.xml..."));
|
||||
|
||||
char versionString[256];
|
||||
pEditor->GetFileVersion().ToString(versionString);
|
||||
|
||||
XmlNodeRef root = XmlHelpers::CreateXmlNode("LevelData");
|
||||
XmlNodeRef root = XmlHelpers::CreateXmlNode("leveldata");
|
||||
root->setAttr("SandboxVersion", versionString);
|
||||
XmlNodeRef rootAction = XmlHelpers::CreateXmlNode("LevelDataAction");
|
||||
XmlNodeRef rootAction = XmlHelpers::CreateXmlNode("leveldataaction");
|
||||
rootAction->setAttr("SandboxVersion", versionString);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Save Level Data XML
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QString levelDataFile = path + "LevelData.xml";
|
||||
QString levelDataFile = path + "leveldata.xml";
|
||||
XmlString xmlData = root->getXML();
|
||||
CCryMemFile file;
|
||||
file.Write(xmlData.c_str(), static_cast<unsigned int>(xmlData.length()));
|
||||
m_levelPak.m_pakFile.UpdateFile(levelDataFile.toUtf8().data(), file);
|
||||
|
||||
QString levelDataActionFile = path + "LevelDataAction.xml";
|
||||
QString levelDataActionFile = path + "leveldataaction.xml";
|
||||
XmlString xmlDataAction = rootAction->getXML();
|
||||
CCryMemFile fileAction;
|
||||
fileAction.Write(xmlDataAction.c_str(), static_cast<unsigned int>(xmlDataAction.length()));
|
||||
@@ -298,7 +298,7 @@ void CGameExporter::ExportLevelData(const QString& path, bool /*bExportMission*/
|
||||
if (savedEntities)
|
||||
{
|
||||
QString entitiesFile;
|
||||
entitiesFile = QStringLiteral("%1%2.entities_xml").arg(path, "Mission0");
|
||||
entitiesFile = QStringLiteral("%1%2.entities_xml").arg(path, "mission0");
|
||||
m_levelPak.m_pakFile.UpdateFile(entitiesFile.toUtf8().data(), entitySaveBuffer.begin(), static_cast<int>(entitySaveBuffer.size()));
|
||||
}
|
||||
}
|
||||
@@ -326,7 +326,7 @@ void CGameExporter::ExportLevelInfo(const QString& path)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Save LevelInfo file.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QString filename = path + "LevelInfo.xml";
|
||||
QString filename = path + "levelinfo.xml";
|
||||
XmlString xmlData = root->getXML();
|
||||
|
||||
CCryMemFile file;
|
||||
|
||||
@@ -38,6 +38,8 @@ namespace UnitTest
|
||||
void BeginCursorCapture() override;
|
||||
void EndCursorCapture() override;
|
||||
bool IsMouseOver() const override;
|
||||
void SetOverrideCursor(AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride) override;
|
||||
void ClearOverrideCursor() override;
|
||||
|
||||
private:
|
||||
AzToolsFramework::QtEventToAzInputMapper* m_inputChannelMapper = nullptr;
|
||||
@@ -58,6 +60,17 @@ namespace UnitTest
|
||||
return true;
|
||||
}
|
||||
|
||||
void ViewportMouseCursorRequestImpl::SetOverrideCursor(
|
||||
[[maybe_unused]] AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride)
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
void ViewportMouseCursorRequestImpl::ClearOverrideCursor()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
class ModularViewportCameraControllerFixture : public AllocatorsTestFixture
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -577,7 +577,9 @@ namespace AZ
|
||||
}
|
||||
|
||||
azstrcat(lines[i], AZ_ARRAY_SIZE(lines[i]), "\n");
|
||||
AZ_Printf(window, "%s", lines[i]); // feed back into the trace system so that listeners can get it.
|
||||
// Use Output instead of AZ_Printf to be consistent with the exception output code and avoid
|
||||
// this accidentally being suppressed as a normal message
|
||||
Output(window, lines[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -46,6 +46,8 @@ namespace AzManipulatorTestFramework
|
||||
virtual void UpdateVisibility() = 0;
|
||||
//! Set if sticky select is enabled or not.
|
||||
virtual void SetStickySelect(bool enabled) = 0;
|
||||
//! Get default Editor Camera Position.
|
||||
virtual AZ::Vector3 DefaultEditorCameraPosition() const = 0;
|
||||
};
|
||||
|
||||
//! This interface is used to simulate the manipulator manager while the manipulators are under test.
|
||||
|
||||
+1
@@ -36,6 +36,7 @@ namespace AzManipulatorTestFramework
|
||||
int GetViewportId() const override;
|
||||
void UpdateVisibility() override;
|
||||
void SetStickySelect(bool enabled) override;
|
||||
AZ::Vector3 DefaultEditorCameraPosition() const override;
|
||||
|
||||
// ViewportInteractionRequestBus overrides ...
|
||||
AzFramework::CameraState GetCameraState() override;
|
||||
|
||||
@@ -120,6 +120,11 @@ namespace AzManipulatorTestFramework
|
||||
m_stickySelect = enabled;
|
||||
}
|
||||
|
||||
AZ::Vector3 ViewportInteraction::DefaultEditorCameraPosition() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void ViewportInteraction::SetGridSize(float size)
|
||||
{
|
||||
m_gridSize = size;
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace AzNetworking
|
||||
: m_delta(delta)
|
||||
, m_dataSerializer(m_delta.GetBufferPtr(), m_delta.GetBufferCapacity())
|
||||
{
|
||||
m_namePrefix.reserve(128);
|
||||
;
|
||||
}
|
||||
|
||||
DeltaSerializerCreate::~DeltaSerializerCreate()
|
||||
@@ -73,7 +73,7 @@ namespace AzNetworking
|
||||
// Delete any left over records that might be hanging around
|
||||
for (auto iter : m_records)
|
||||
{
|
||||
delete iter.second;
|
||||
delete iter;
|
||||
}
|
||||
m_records.clear();
|
||||
}
|
||||
@@ -160,28 +160,13 @@ namespace AzNetworking
|
||||
return SerializeHelper(buffer, bufferCapacity, isString, outSize, name);
|
||||
}
|
||||
|
||||
AZStd::string DeltaSerializerCreate::GetNextObjectName(const char* name)
|
||||
bool DeltaSerializerCreate::BeginObject([[maybe_unused]] const char* name, [[maybe_unused]] const char* typeName)
|
||||
{
|
||||
AZStd::string objectName = name;
|
||||
objectName += ".";
|
||||
objectName += AZStd::to_string(m_objectCounter);
|
||||
++m_objectCounter;
|
||||
return objectName;
|
||||
}
|
||||
|
||||
bool DeltaSerializerCreate::BeginObject(const char* name, [[maybe_unused]] const char* typeName)
|
||||
{
|
||||
m_nameLengthStack.push_back(m_namePrefix.length());
|
||||
m_namePrefix += GetNextObjectName(name);
|
||||
m_namePrefix += ".";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeltaSerializerCreate::EndObject([[maybe_unused]] const char* name, [[maybe_unused]] const char* typeName)
|
||||
{
|
||||
const size_t prevLen = m_nameLengthStack.back();
|
||||
m_nameLengthStack.pop_back();
|
||||
m_namePrefix.resize(prevLen);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -205,25 +190,15 @@ namespace AzNetworking
|
||||
{
|
||||
typedef AbstractValue::ValueT<T> ValueType;
|
||||
|
||||
const size_t prevLen = m_namePrefix.length();
|
||||
m_namePrefix += GetNextObjectName(name);
|
||||
|
||||
const AZ::HashValue32 nameHash = AZ::TypeHash32(m_namePrefix.c_str());
|
||||
|
||||
m_namePrefix.resize(prevLen);
|
||||
|
||||
AbstractValue::BaseValue*& baseValue = m_records[nameHash];
|
||||
AbstractValue::BaseValue* baseValue = m_records.size() > m_objectCounter ? m_records[m_objectCounter] : nullptr;
|
||||
++m_objectCounter;
|
||||
|
||||
// If we are in the gather records phase, just save off the value records
|
||||
if (m_gatheringRecords)
|
||||
{
|
||||
if (baseValue != nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Duplicate name encountered in delta serializer. This will cause data to be serialized incorrectly.");
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ_Assert(baseValue == nullptr, "Expected to create a new record but found a pre-existing one at index %d", m_objectCounter - 1);
|
||||
baseValue = new ValueType(value);
|
||||
m_records.push_back(baseValue);
|
||||
}
|
||||
else // If we are not gathering records, then we are comparing them
|
||||
{
|
||||
|
||||
@@ -90,8 +90,6 @@ namespace AzNetworking
|
||||
DeltaSerializerCreate(const DeltaSerializerCreate&) = delete;
|
||||
DeltaSerializerCreate& operator=(const DeltaSerializerCreate&) = delete;
|
||||
|
||||
AZStd::string GetNextObjectName(const char* name);
|
||||
|
||||
template <typename T>
|
||||
bool SerializeHelper(T& value, uint32_t bufferCapacity, bool isString, uint32_t& outSize, const char* name);
|
||||
|
||||
@@ -105,9 +103,7 @@ namespace AzNetworking
|
||||
|
||||
bool m_gatheringRecords = false;
|
||||
uint32_t m_objectCounter = 0;
|
||||
AZStd::string m_namePrefix;
|
||||
AZStd::vector<size_t> m_nameLengthStack;
|
||||
AZStd::unordered_map<AZ::HashValue32, AbstractValue::BaseValue*> m_records;
|
||||
AZStd::vector<AbstractValue::BaseValue*> m_records;
|
||||
NetworkInputSerializer m_dataSerializer;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,4 +11,213 @@
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
struct DeltaDataElement
|
||||
{
|
||||
AzNetworking::PacketId m_packetId = AzNetworking::InvalidPacketId;
|
||||
uint32_t m_id = 0;
|
||||
AZ::TimeMs m_timeMs = AZ::TimeMs{ 0 };
|
||||
float m_blendFactor = 0.f;
|
||||
AZStd::vector<int> m_growVector, m_shrinkVector;
|
||||
|
||||
bool Serialize(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
if (!serializer.Serialize(m_packetId, "PacketId")
|
||||
|| !serializer.Serialize(m_id, "Id")
|
||||
|| !serializer.Serialize(m_timeMs, "TimeMs")
|
||||
|| !serializer.Serialize(m_blendFactor, "BlendFactor")
|
||||
|| !serializer.Serialize(m_growVector, "GrowVector")
|
||||
|| !serializer.Serialize(m_shrinkVector, "ShrinkVector"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct DeltaDataContainer
|
||||
{
|
||||
AZStd::string m_containerName;
|
||||
AZStd::array<DeltaDataElement, 32> m_container;
|
||||
|
||||
// This logic is modeled after NetworkInputArray serialization in the Multiplayer Gem
|
||||
bool Serialize(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
// Always serialize the full first element
|
||||
if(!m_container[0].Serialize(serializer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 1; i < m_container.size(); ++i)
|
||||
{
|
||||
if (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject)
|
||||
{
|
||||
AzNetworking::SerializerDelta deltaSerializer;
|
||||
// Read out the delta
|
||||
if (!deltaSerializer.Serialize(serializer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start with previous value
|
||||
m_container[i] = m_container[i - 1];
|
||||
// Then apply delta
|
||||
AzNetworking::DeltaSerializerApply applySerializer(deltaSerializer);
|
||||
if (!applySerializer.ApplyDelta(m_container[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AzNetworking::SerializerDelta deltaSerializer;
|
||||
// Create the delta
|
||||
AzNetworking::DeltaSerializerCreate createSerializer(deltaSerializer);
|
||||
if (!createSerializer.CreateDelta(m_container[i - 1], m_container[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Then write out the delta
|
||||
if (!deltaSerializer.Serialize(serializer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This logic is modeled after NetworkInputArray serialization in the Multiplayer Gem
|
||||
bool SerializeNoDelta(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
for (uint32_t i = 0; i < m_container.size(); ++i)
|
||||
{
|
||||
if(!m_container[i].Serialize(serializer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class DeltaSerializerTests
|
||||
: public UnitTest::AllocatorsTestFixture
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
UnitTest::AllocatorsTestFixture::SetUp();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
UnitTest::AllocatorsTestFixture::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr float BLEND_FACTOR_SCALE = 1.1f;
|
||||
static constexpr uint32_t TIME_SCALE = 10;
|
||||
|
||||
DeltaDataContainer TestDeltaContainer()
|
||||
{
|
||||
DeltaDataContainer testContainer;
|
||||
AZStd::vector<int> growVector, shrinkVector;
|
||||
shrinkVector.resize(testContainer.m_container.array_size);
|
||||
|
||||
testContainer.m_containerName = "TestContainer";
|
||||
for (int i = 0; i < testContainer.m_container.array_size; ++i)
|
||||
{
|
||||
testContainer.m_container[i].m_packetId = AzNetworking::PacketId(i);
|
||||
testContainer.m_container[i].m_id = i;
|
||||
testContainer.m_container[i].m_timeMs = AZ::TimeMs(i * TIME_SCALE);
|
||||
testContainer.m_container[i].m_blendFactor = BLEND_FACTOR_SCALE * i;
|
||||
growVector.push_back(i);
|
||||
testContainer.m_container[i].m_growVector = growVector;
|
||||
shrinkVector.resize(testContainer.m_container.array_size - i);
|
||||
testContainer.m_container[i].m_shrinkVector = shrinkVector;
|
||||
}
|
||||
|
||||
return testContainer;
|
||||
}
|
||||
|
||||
TEST_F(DeltaSerializerTests, DeltaArray)
|
||||
{
|
||||
DeltaDataContainer inContainer = TestDeltaContainer();
|
||||
AZStd::array<uint8_t, 2048> buffer;
|
||||
AzNetworking::NetworkInputSerializer inSerializer(buffer.data(), static_cast<uint32_t>(buffer.size()));
|
||||
|
||||
// Always serialize the full first element
|
||||
EXPECT_TRUE(inContainer.Serialize(inSerializer));
|
||||
|
||||
DeltaDataContainer outContainer;
|
||||
AzNetworking::NetworkOutputSerializer outSerializer(buffer.data(), static_cast<uint32_t>(buffer.size()));
|
||||
|
||||
EXPECT_TRUE(outContainer.Serialize(outSerializer));
|
||||
|
||||
for (uint32_t i = 0; i > outContainer.m_container.size(); ++i)
|
||||
{
|
||||
EXPECT_EQ(inContainer.m_container[i].m_blendFactor, outContainer.m_container[i].m_blendFactor);
|
||||
EXPECT_EQ(inContainer.m_container[i].m_id, outContainer.m_container[i].m_id);
|
||||
EXPECT_EQ(inContainer.m_container[i].m_packetId, outContainer.m_container[i].m_packetId);
|
||||
EXPECT_EQ(inContainer.m_container[i].m_timeMs, outContainer.m_container[i].m_timeMs);
|
||||
EXPECT_EQ(inContainer.m_container[i].m_growVector[i], outContainer.m_container[i].m_growVector[i]);
|
||||
EXPECT_EQ(inContainer.m_container[i].m_growVector.size(), outContainer.m_container[i].m_growVector.size());
|
||||
EXPECT_EQ(inContainer.m_container[i].m_shrinkVector.size(), outContainer.m_container[i].m_shrinkVector.size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DeltaSerializerTests, DeltaSerializerCreateUnused)
|
||||
{
|
||||
// Every function here should return a constant value regardless of inputs
|
||||
AzNetworking::SerializerDelta deltaSerializer;
|
||||
AzNetworking::DeltaSerializerCreate createSerializer(deltaSerializer);
|
||||
|
||||
EXPECT_EQ(createSerializer.GetCapacity(), 0);
|
||||
EXPECT_EQ(createSerializer.GetSize(), 0);
|
||||
EXPECT_EQ(createSerializer.GetBuffer(), nullptr);
|
||||
EXPECT_EQ(createSerializer.GetSerializerMode(), AzNetworking::SerializerMode::ReadFromObject);
|
||||
|
||||
createSerializer.ClearTrackedChangesFlag(); //NO-OP
|
||||
EXPECT_FALSE(createSerializer.GetTrackedChangesFlag());
|
||||
EXPECT_TRUE(createSerializer.BeginObject("CreateSerializer", "Begin"));
|
||||
EXPECT_TRUE(createSerializer.EndObject("CreateSerializer", "End"));
|
||||
}
|
||||
|
||||
TEST_F(DeltaSerializerTests, DeltaArraySize)
|
||||
{
|
||||
DeltaDataContainer deltaContainer = TestDeltaContainer();
|
||||
DeltaDataContainer noDeltaContainer = TestDeltaContainer();
|
||||
|
||||
AZStd::array<uint8_t, 2048> deltaBuffer;
|
||||
AzNetworking::NetworkInputSerializer deltaSerializer(deltaBuffer.data(), static_cast<uint32_t>(deltaBuffer.size()));
|
||||
AZStd::array<uint8_t, 2048> noDeltaBuffer;
|
||||
AzNetworking::NetworkInputSerializer noDeltaSerializer(noDeltaBuffer.data(), static_cast<uint32_t>(noDeltaBuffer.size()));
|
||||
|
||||
EXPECT_TRUE(deltaContainer.Serialize(deltaSerializer));
|
||||
EXPECT_FALSE(noDeltaContainer.SerializeNoDelta(noDeltaSerializer)); // Should run out of space
|
||||
EXPECT_EQ(noDeltaSerializer.GetCapacity(), noDeltaSerializer.GetSize()); // Verify that the serializer filled up
|
||||
EXPECT_FALSE(noDeltaSerializer.IsValid()); // and that it is no longer valid due to lack of space
|
||||
}
|
||||
|
||||
TEST_F(DeltaSerializerTests, DeltaSerializerApplyUnused)
|
||||
{
|
||||
// Every function here should return a constant value regardless of inputs
|
||||
AzNetworking::SerializerDelta deltaSerializer;
|
||||
AzNetworking::DeltaSerializerApply applySerializer(deltaSerializer);
|
||||
|
||||
EXPECT_EQ(applySerializer.GetCapacity(), 0);
|
||||
EXPECT_EQ(applySerializer.GetSize(), 0);
|
||||
EXPECT_EQ(applySerializer.GetBuffer(), nullptr);
|
||||
EXPECT_EQ(applySerializer.GetSerializerMode(), AzNetworking::SerializerMode::WriteToObject);
|
||||
|
||||
applySerializer.ClearTrackedChangesFlag(); //NO-OP
|
||||
EXPECT_FALSE(applySerializer.GetTrackedChangesFlag());
|
||||
EXPECT_TRUE(applySerializer.BeginObject("CreateSerializer", "Begin"));
|
||||
EXPECT_TRUE(applySerializer.EndObject("CreateSerializer", "End"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,6 +244,17 @@ namespace AzToolsFramework
|
||||
|
||||
const auto eventType = event->type();
|
||||
|
||||
if (eventType == QEvent::Type::MouseMove)
|
||||
{
|
||||
// clear override cursor when moving outside of the viewport
|
||||
const auto* mouseEvent = static_cast<const QMouseEvent*>(event);
|
||||
if (m_overrideCursor && !m_sourceWidget->geometry().contains(m_sourceWidget->mapFromGlobal(mouseEvent->globalPos())))
|
||||
{
|
||||
qApp->restoreOverrideCursor();
|
||||
m_overrideCursor = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Only accept mouse & key release events that originate from an object that is not our target widget,
|
||||
// as we don't want to erroneously intercept user input meant for another component.
|
||||
if (object != m_sourceWidget && eventType != QEvent::Type::KeyRelease && eventType != QEvent::Type::MouseButtonRelease)
|
||||
@@ -262,7 +273,7 @@ namespace AzToolsFramework
|
||||
if (eventType == QEvent::FocusIn)
|
||||
{
|
||||
const auto globalCursorPosition = QCursor::pos();
|
||||
if (m_sourceWidget->geometry().contains(globalCursorPosition))
|
||||
if (m_sourceWidget->geometry().contains(m_sourceWidget->mapFromGlobal(globalCursorPosition)))
|
||||
{
|
||||
HandleMouseMoveEvent(globalCursorPosition);
|
||||
}
|
||||
@@ -452,4 +463,32 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Qt::CursorShape QtCursorFromAzCursor(const ViewportInteraction::CursorStyleOverride cursorStyleOverride)
|
||||
{
|
||||
switch (cursorStyleOverride)
|
||||
{
|
||||
case ViewportInteraction::CursorStyleOverride::Forbidden:
|
||||
return Qt::ForbiddenCursor;
|
||||
default:
|
||||
return Qt::ArrowCursor;
|
||||
}
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::SetOverrideCursor(ViewportInteraction::CursorStyleOverride cursorStyleOverride)
|
||||
{
|
||||
ClearOverrideCursor();
|
||||
|
||||
qApp->setOverrideCursor(QtCursorFromAzCursor(cursorStyleOverride));
|
||||
m_overrideCursor = true;
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::ClearOverrideCursor()
|
||||
{
|
||||
if (m_overrideCursor)
|
||||
{
|
||||
qApp->restoreOverrideCursor();
|
||||
m_overrideCursor = false;
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
#include <AzFramework/Input/Channels/InputChannelDeltaWithSharedPosition2D.h>
|
||||
#include <AzFramework/Input/Channels/InputChannelDigitalWithSharedModifierKeyStates.h>
|
||||
#include <AzFramework/Input/Channels/InputChannelDigitalWithSharedPosition2D.h>
|
||||
|
||||
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
|
||||
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
|
||||
#include <QEvent>
|
||||
#include <QObject>
|
||||
#include <QPoint>
|
||||
@@ -55,6 +56,9 @@ namespace AzToolsFramework
|
||||
//! like a dolly or rotation, where mouse movement is important but cursor location is not.
|
||||
void SetCursorCaptureEnabled(bool enabled);
|
||||
|
||||
void SetOverrideCursor(ViewportInteraction::CursorStyleOverride cursorStyleOverride);
|
||||
void ClearOverrideCursor();
|
||||
|
||||
// QObject overrides...
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
@@ -164,6 +168,8 @@ namespace AzToolsFramework
|
||||
bool m_enabled = true;
|
||||
// Flags whether or not the cursor is being constrained to the source widget (for invisible mouse movement).
|
||||
bool m_capturingCursor = false;
|
||||
// Flags whether the cursor has been overridden.
|
||||
bool m_overrideCursor = false;
|
||||
|
||||
// Our viewport-specific AZ devices. We control their internal input channel states.
|
||||
AZStd::unique_ptr<EditorQtMouseDevice> m_mouseDevice;
|
||||
|
||||
@@ -198,6 +198,8 @@ namespace AzToolsFramework
|
||||
virtual float ManipulatorCircleBoundWidth() const = 0;
|
||||
//! Returns if sticky select is enabled or not.
|
||||
virtual bool StickySelectEnabled() const = 0;
|
||||
//! Returns the default viewport camera position.
|
||||
virtual AZ::Vector3 DefaultEditorCameraPosition() const = 0;
|
||||
|
||||
protected:
|
||||
~ViewportSettingsRequests() = default;
|
||||
@@ -300,6 +302,12 @@ namespace AzToolsFramework
|
||||
|
||||
using EditorViewportInputTimeNowRequestBus = AZ::EBus<EditorViewportInputTimeNowRequests>;
|
||||
|
||||
//! The style of cursor override.
|
||||
enum class CursorStyleOverride
|
||||
{
|
||||
Forbidden
|
||||
};
|
||||
|
||||
//! Viewport requests for managing the viewport cursor state.
|
||||
class ViewportMouseCursorRequests
|
||||
{
|
||||
@@ -310,6 +318,10 @@ namespace AzToolsFramework
|
||||
virtual void EndCursorCapture() = 0;
|
||||
//! Is the mouse over the viewport.
|
||||
virtual bool IsMouseOver() const = 0;
|
||||
//! Set the cursor style override.
|
||||
virtual void SetOverrideCursor(CursorStyleOverride cursorStyleOverride) = 0;
|
||||
//! Clear the cursor style override.
|
||||
virtual void ClearOverrideCursor() = 0;
|
||||
|
||||
protected:
|
||||
~ViewportMouseCursorRequests() = default;
|
||||
|
||||
@@ -44,6 +44,13 @@ AZ_CVAR(
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"Display the aggregate world bounds for a given entity (the union of all world component Aabbs)");
|
||||
AZ_CVAR(
|
||||
bool,
|
||||
ed_useCursorLockIconInFocusMode,
|
||||
false,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"Use a lock icon when the cursor is over entities that cannot be interacted with");
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -222,6 +229,13 @@ namespace AzToolsFramework
|
||||
// verify if the entity Id corresponds to an entity that is focused; if not, halt selection.
|
||||
if (entityIdUnderCursor.IsValid() && !IsSelectableAccordingToFocusMode(entityIdUnderCursor))
|
||||
{
|
||||
if (ed_useCursorLockIconInFocusMode)
|
||||
{
|
||||
ViewportInteraction::ViewportMouseCursorRequestBus::Event(
|
||||
viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::SetOverrideCursor,
|
||||
ViewportInteraction::CursorStyleOverride::Forbidden);
|
||||
}
|
||||
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down ||
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::DoubleClick)
|
||||
@@ -232,6 +246,9 @@ namespace AzToolsFramework
|
||||
return CursorEntityIdQuery(AZ::EntityId(), AZ::EntityId());
|
||||
}
|
||||
|
||||
ViewportInteraction::ViewportMouseCursorRequestBus::Event(
|
||||
viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::ClearOverrideCursor);
|
||||
|
||||
// container entity support - if the entity that is being selected is part of a closed container,
|
||||
// change the selection to the container instead.
|
||||
if (ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
|
||||
|
||||
@@ -90,8 +90,7 @@ namespace UnitTest
|
||||
// Update our undo cache entry to include the rename / reparent as one atomic operation.
|
||||
m_prefabPublicInterface->GenerateUndoNodesForEntityChangeAndUpdateCache(entityId, m_undoStack->GetTop());
|
||||
|
||||
// Force a prefab propagation as updates are deferred to the next tick.
|
||||
m_prefabSystemComponent->OnSystemTick();
|
||||
ProcessDeferredUpdates();
|
||||
|
||||
return entityId;
|
||||
}
|
||||
@@ -125,6 +124,30 @@ namespace UnitTest
|
||||
return m_model->index(0, 0);
|
||||
}
|
||||
|
||||
// Kicks off any updates scheduled for the next tick
|
||||
void ProcessDeferredUpdates()
|
||||
{
|
||||
// Force a prefab propagation for updates that are deferred to the next tick.
|
||||
m_prefabSystemComponent->OnSystemTick();
|
||||
|
||||
// Ensure the model process its entity update queue
|
||||
m_model->ProcessEntityUpdates();
|
||||
}
|
||||
|
||||
// Performs an undo operation and ensures the tick-scheduled updates happen
|
||||
void Undo()
|
||||
{
|
||||
m_undoStack->Undo();
|
||||
ProcessDeferredUpdates();
|
||||
}
|
||||
|
||||
// Performs a redo operation and ensures the tick-scheduled updates happen
|
||||
void Redo()
|
||||
{
|
||||
m_undoStack->Redo();
|
||||
ProcessDeferredUpdates();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AzToolsFramework::EntityOutlinerListModel> m_model;
|
||||
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTester;
|
||||
AzToolsFramework::UndoSystem::UndoStack* m_undoStack = nullptr;
|
||||
@@ -139,21 +162,18 @@ namespace UnitTest
|
||||
CreateNamedEntity(AZStd::string::format("Entity%zu", i));
|
||||
EXPECT_EQ(m_model->rowCount(GetRootIndex()), i + 1);
|
||||
}
|
||||
m_model->ProcessEntityUpdates();
|
||||
|
||||
for (int i = entityCount; i > 0; --i)
|
||||
{
|
||||
m_undoStack->Undo();
|
||||
Undo();
|
||||
EXPECT_EQ(m_model->rowCount(GetRootIndex()), i - 1);
|
||||
}
|
||||
m_model->ProcessEntityUpdates();
|
||||
|
||||
for (size_t i = 0; i < entityCount; ++i)
|
||||
{
|
||||
m_undoStack->Redo();
|
||||
Redo();
|
||||
EXPECT_EQ(m_model->rowCount(GetRootIndex()), i + 1);
|
||||
}
|
||||
m_model->ProcessEntityUpdates();
|
||||
}
|
||||
|
||||
TEST_F(EntityOutlinerTest, TestCreateNestedHierarchyUndoAndRedoWorks)
|
||||
@@ -177,21 +197,18 @@ namespace UnitTest
|
||||
{
|
||||
parentId = CreateNamedEntity(AZStd::string::format("EntityDepth%i", i), parentId);
|
||||
EXPECT_EQ(modelDepth(), i + 1);
|
||||
m_model->ProcessEntityUpdates();
|
||||
}
|
||||
|
||||
for (int i = depth - 1; i >= 0; --i)
|
||||
{
|
||||
m_undoStack->Undo();
|
||||
Undo();
|
||||
EXPECT_EQ(modelDepth(), i);
|
||||
m_model->ProcessEntityUpdates();
|
||||
}
|
||||
|
||||
for (int i = 0; i < depth; ++i)
|
||||
{
|
||||
m_undoStack->Redo();
|
||||
Redo();
|
||||
EXPECT_EQ(modelDepth(), i + 1);
|
||||
m_model->ProcessEntityUpdates();
|
||||
}
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -86,7 +86,7 @@ bool CLevelInfo::ReadInfo()
|
||||
usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
|
||||
|
||||
// Set up a default game type for legacy code.
|
||||
m_defaultGameTypeName = "Mission0";
|
||||
m_defaultGameTypeName = "mission0";
|
||||
|
||||
if (usePrefabSystemForLevels)
|
||||
{
|
||||
@@ -96,17 +96,17 @@ bool CLevelInfo::ReadInfo()
|
||||
|
||||
AZStd::string levelPath(m_levelPath);
|
||||
AZStd::string xmlFile(levelPath);
|
||||
xmlFile += "/LevelInfo.xml";
|
||||
xmlFile += "/levelinfo.xml";
|
||||
XmlNodeRef rootNode = GetISystem()->LoadXmlFromFile(xmlFile.c_str());
|
||||
|
||||
if (rootNode)
|
||||
{
|
||||
AZStd::string dataFile(levelPath);
|
||||
dataFile += "/LevelDataAction.xml";
|
||||
dataFile += "/leveldataaction.xml";
|
||||
XmlNodeRef dataNode = GetISystem()->LoadXmlFromFile(dataFile.c_str());
|
||||
if (!dataNode)
|
||||
{
|
||||
dataFile = levelPath + "/LevelData.xml";
|
||||
dataFile = levelPath + "/leveldata.xml";
|
||||
dataNode = GetISystem()->LoadXmlFromFile(dataFile.c_str());
|
||||
}
|
||||
|
||||
@@ -614,7 +614,7 @@ ILevel* CLevelSystem::LoadLevelInternal(const char* _levelName)
|
||||
}
|
||||
|
||||
{
|
||||
AZStd::string missionXml("Mission_");
|
||||
AZStd::string missionXml("mission_");
|
||||
missionXml += pLevelInfo->m_defaultGameTypeName;
|
||||
missionXml += ".xml";
|
||||
AZStd::string xmlFile(pLevelInfo->GetPath());
|
||||
|
||||
@@ -3573,19 +3573,61 @@ namespace AssetProcessor
|
||||
QString knownPathBeforeWildcard = encodedFileData.left(slashBeforeWildcardIndex + 1); // include the slash
|
||||
QString relativeSearch = encodedFileData.mid(slashBeforeWildcardIndex + 1); // skip the slash
|
||||
|
||||
for (int i = 0; i < m_platformConfig->GetScanFolderCount(); ++i)
|
||||
// Absolute path, just check the 1 scan folder
|
||||
if (AZ::IO::PathView(encodedFileData.toUtf8().constData()).IsAbsolute())
|
||||
{
|
||||
const ScanFolderInfo* scanFolderInfo = &m_platformConfig->GetScanFolderAt(i);
|
||||
|
||||
if (!scanFolderInfo->RecurseSubFolders() && encodedFileData.contains("/"))
|
||||
QString scanFolderName;
|
||||
if (!m_platformConfig->ConvertToRelativePath(encodedFileData, resultDatabaseSourceName, scanFolderName))
|
||||
{
|
||||
continue;
|
||||
AZ_Warning(
|
||||
AssetProcessor::ConsoleChannel, false,
|
||||
"'%s' does not appear to be in any input folder. Use relative paths instead.",
|
||||
sourceDependency.m_sourceFileDependencyPath.c_str());
|
||||
}
|
||||
|
||||
QDir rooted(scanFolderInfo->ScanPath());
|
||||
QString absolutePath = rooted.absoluteFilePath(knownPathBeforeWildcard);
|
||||
auto scanFolderInfo = m_platformConfig->GetScanFolderByPath(scanFolderName);
|
||||
|
||||
resolvedDependencyList.append(m_platformConfig->FindWildcardMatches(absolutePath, relativeSearch, false, scanFolderInfo->RecurseSubFolders()));
|
||||
// Make an absolute path that is ScanFolderPath + Part of search path before the wildcard
|
||||
QDir rooted(scanFolderName);
|
||||
QString scanFolderAndKnownSubPath = rooted.absoluteFilePath(knownPathBeforeWildcard);
|
||||
|
||||
resolvedDependencyList.append(m_platformConfig->FindWildcardMatches(
|
||||
scanFolderAndKnownSubPath, relativeSearch, false, scanFolderInfo->RecurseSubFolders()));
|
||||
}
|
||||
else // Relative path, check every scan folder
|
||||
{
|
||||
for (int i = 0; i < m_platformConfig->GetScanFolderCount(); ++i)
|
||||
{
|
||||
const ScanFolderInfo* scanFolderInfo = &m_platformConfig->GetScanFolderAt(i);
|
||||
|
||||
if (!scanFolderInfo->RecurseSubFolders() && encodedFileData.contains("/"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QDir rooted(scanFolderInfo->ScanPath());
|
||||
QString absolutePath = rooted.absoluteFilePath(knownPathBeforeWildcard);
|
||||
|
||||
resolvedDependencyList.append(m_platformConfig->FindWildcardMatches(
|
||||
absolutePath, relativeSearch, false, scanFolderInfo->RecurseSubFolders()));
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to relative paths
|
||||
for (auto dependencyItr = resolvedDependencyList.begin(); dependencyItr != resolvedDependencyList.end();)
|
||||
{
|
||||
QString relativePath, scanFolder;
|
||||
if (m_platformConfig->ConvertToRelativePath(*dependencyItr, relativePath, scanFolder))
|
||||
{
|
||||
*dependencyItr = relativePath;
|
||||
++dependencyItr;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("AssetProcessor", false, "Failed to get relative path for wildcard dependency file %s. Is the file within a scan folder?",
|
||||
dependencyItr->toUtf8().constData());
|
||||
dependencyItr = resolvedDependencyList.erase(dependencyItr);
|
||||
}
|
||||
}
|
||||
|
||||
resultDatabaseSourceName = encodedFileData.replace('\\', '/');
|
||||
|
||||
@@ -1435,32 +1435,35 @@ namespace AssetProcessor
|
||||
return QString();
|
||||
}
|
||||
|
||||
QStringList PlatformConfiguration::FindWildcardMatches(const QString& sourceFolder, QString relativeName, bool includeFolders, bool recursiveSearch) const
|
||||
QStringList PlatformConfiguration::FindWildcardMatches(
|
||||
const QString& sourceFolder, QString relativeName, bool includeFolders, bool recursiveSearch) const
|
||||
{
|
||||
if (relativeName.isEmpty())
|
||||
{
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
const int pathLen = sourceFolder.length() + 1;
|
||||
QDir sourceFolderDir(sourceFolder);
|
||||
|
||||
relativeName.replace('\\', '/');
|
||||
QString posixRelativeName = QDir::fromNativeSeparators(relativeName);
|
||||
|
||||
QStringList returnList;
|
||||
QRegExp nameMatch{ relativeName, Qt::CaseInsensitive, QRegExp::Wildcard };
|
||||
QDirIterator diretoryIterator(sourceFolder, QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot, recursiveSearch ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags);
|
||||
QRegExp nameMatch{ posixRelativeName, Qt::CaseInsensitive, QRegExp::Wildcard };
|
||||
QDirIterator dirIterator(
|
||||
sourceFolderDir.path(), QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot,
|
||||
recursiveSearch ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags);
|
||||
QStringList files;
|
||||
while (diretoryIterator.hasNext())
|
||||
while (dirIterator.hasNext())
|
||||
{
|
||||
diretoryIterator.next();
|
||||
if (!includeFolders && !diretoryIterator.fileInfo().isFile())
|
||||
dirIterator.next();
|
||||
if (!includeFolders && !dirIterator.fileInfo().isFile())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
QString pathMatch{ diretoryIterator.filePath().mid(pathLen) };
|
||||
QString pathMatch{ sourceFolderDir.relativeFilePath(dirIterator.filePath()) };
|
||||
if (nameMatch.exactMatch(pathMatch))
|
||||
{
|
||||
returnList.append(AssetUtilities::NormalizeFilePath(diretoryIterator.filePath()));
|
||||
returnList.append(QDir::fromNativeSeparators(dirIterator.filePath()));
|
||||
}
|
||||
}
|
||||
return returnList;
|
||||
|
||||
@@ -51,4 +51,21 @@ namespace AWSCore
|
||||
"https://o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/advanced-topics/";
|
||||
static constexpr const char AWSMetricsSettingsUrl[] =
|
||||
"https://o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/";
|
||||
|
||||
static constexpr const char AWSGameLiftGemOverviewUrl[] =
|
||||
"https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/";
|
||||
static constexpr const char AWSGameLiftGemSetupUrl[] =
|
||||
"https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/gem-setup/";
|
||||
static constexpr const char AWSGameLiftScriptingUrl[] =
|
||||
"https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/scripting/";
|
||||
static constexpr const char AWSGameLiftAPIReferenceUrl[] =
|
||||
"https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/cpp-api/";
|
||||
static constexpr const char AWSGameLiftAdvancedTopicsUrl[] =
|
||||
"https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/advanced-topics/";
|
||||
static constexpr const char AWSGameLiftLocalTestingUrl[] =
|
||||
"https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/local-testing/";
|
||||
static constexpr const char AWSGameLiftBuildPackagingUrl[] =
|
||||
"https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/build-packaging-for-windows/";
|
||||
static constexpr const char AWSGameLiftResourceManagementUrl[] =
|
||||
"https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/resource-management/";
|
||||
} // namespace AWSCore
|
||||
|
||||
@@ -38,4 +38,14 @@ namespace AWSCore
|
||||
static constexpr const char AWSMetricsAPIReferenceActionText[] = "API reference";
|
||||
static constexpr const char AWSMetricsAdvancedTopicsActionText[] = "Advanced topics";
|
||||
static constexpr const char AWSMetricsSettingsActionText[] = "Metrics settings";
|
||||
|
||||
static constexpr const char AWSGameLiftActionText[] = "GameLift Gem";
|
||||
static constexpr const char AWSGameLiftGemOverviewActionText[] = "Gem overview";
|
||||
static constexpr const char AWSGameLiftGemSetupActionText[] = "Setup";
|
||||
static constexpr const char AWSMGameLiftScriptingActionText[] = "Scripting reference";
|
||||
static constexpr const char AWSGameLiftAPIReferenceActionText[] = "API reference";
|
||||
static constexpr const char AWSGameLiftAdvancedTopicsActionText[] = "Advanced topics";
|
||||
static constexpr const char AWSGameLiftLocalTestingActionText[] = "Local testing";
|
||||
static constexpr const char AWSGameLiftBuildPackagingActionText[] = "Build packaging (Windows)";
|
||||
static constexpr const char AWSGameLiftResourceManagementActionText[] = "Resource Management";
|
||||
} // namespace AWSCore
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace AWSCore
|
||||
// AWSCoreEditorRequestBus interface implementation
|
||||
void SetAWSClientAuthEnabled() override;
|
||||
void SetAWSMetricsEnabled() override;
|
||||
void SetAWSGameLiftEnabled() override;
|
||||
|
||||
QMenu* SetAWSFeatureSubMenu(const AZStd::string& menuText);
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace AWSCore
|
||||
public:
|
||||
virtual void SetAWSClientAuthEnabled() = 0;
|
||||
virtual void SetAWSMetricsEnabled() = 0;
|
||||
virtual void SetAWSGameLiftEnabled() = 0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
|
||||
@@ -156,6 +156,11 @@ namespace AWSCore
|
||||
metrics->setIcon(QIcon(QString(":/Notifications/download.svg")));
|
||||
metrics->setDisabled(true);
|
||||
this->addAction(metrics);
|
||||
|
||||
QAction* gamelift = new QAction(QObject::tr(AWSGameLiftActionText));
|
||||
gamelift->setIcon(QIcon(QString(":/Notifications/download.svg")));
|
||||
gamelift->setDisabled(true);
|
||||
this->addAction(gamelift);
|
||||
}
|
||||
|
||||
void AWSCoreEditorMenu::SetAWSClientAuthEnabled()
|
||||
@@ -181,6 +186,23 @@ namespace AWSCore
|
||||
AddSpaceForIcon(subMenu);
|
||||
}
|
||||
|
||||
void AWSCoreEditorMenu::SetAWSGameLiftEnabled()
|
||||
{
|
||||
// TODO: instead of creating submenu in core editor, aws feature gem should return submenu component directly
|
||||
QMenu* subMenu = SetAWSFeatureSubMenu(AWSGameLiftActionText);
|
||||
|
||||
subMenu->addAction(AddExternalLinkAction(AWSGameLiftGemOverviewActionText, AWSGameLiftGemOverviewUrl, ":/Notifications/link.svg"));
|
||||
subMenu->addAction(AddExternalLinkAction(AWSGameLiftGemSetupActionText, AWSGameLiftGemSetupUrl, ":/Notifications/link.svg"));
|
||||
subMenu->addAction(AddExternalLinkAction(AWSMGameLiftScriptingActionText, AWSGameLiftScriptingUrl, ":/Notifications/link.svg"));
|
||||
subMenu->addAction(AddExternalLinkAction(AWSGameLiftAPIReferenceActionText, AWSGameLiftAPIReferenceUrl, ":/Notifications/link.svg"));
|
||||
subMenu->addAction(AddExternalLinkAction(AWSGameLiftAdvancedTopicsActionText, AWSGameLiftAdvancedTopicsUrl, ":/Notifications/link.svg"));
|
||||
subMenu->addAction(AddExternalLinkAction(AWSGameLiftLocalTestingActionText, AWSGameLiftLocalTestingUrl, ":/Notifications/link.svg"));
|
||||
subMenu->addAction(AddExternalLinkAction(AWSGameLiftBuildPackagingActionText, AWSGameLiftBuildPackagingUrl, ":/Notifications/link.svg"));
|
||||
subMenu->addAction(AddExternalLinkAction(AWSGameLiftResourceManagementActionText, AWSGameLiftResourceManagementUrl, ":/Notifications/link.svg"));
|
||||
|
||||
AddSpaceForIcon(subMenu);
|
||||
}
|
||||
|
||||
void AWSCoreEditorMenu::SetAWSMetricsEnabled()
|
||||
{
|
||||
// TODO: instead of creating submenu in core editor, aws feature gem should return submenu component directly
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
|
||||
using namespace AWSCore;
|
||||
|
||||
static constexpr const int ExpectedActionNumOnWindowsPlatform = 8;
|
||||
static constexpr const int ExpectedActionNumOnOtherPlatform = 6;
|
||||
static constexpr const int ExpectedActionNumOnWindowsPlatform = 9;
|
||||
static constexpr const int ExpectedActionNumOnOtherPlatform = 7;
|
||||
|
||||
class AWSCoreEditorMenuTest
|
||||
: public AWSCoreFixture
|
||||
@@ -60,6 +60,7 @@ TEST_F(AWSCoreEditorMenuTest, AWSCoreEditorMenu_BroadcastFeatureGemsAreEnabled_C
|
||||
|
||||
AWSCoreEditorRequestBus::Broadcast(&AWSCoreEditorRequests::SetAWSClientAuthEnabled);
|
||||
AWSCoreEditorRequestBus::Broadcast(&AWSCoreEditorRequests::SetAWSMetricsEnabled);
|
||||
AWSCoreEditorRequestBus::Broadcast(&AWSCoreEditorRequests::SetAWSGameLiftEnabled);
|
||||
|
||||
QList<QAction*> actualActions = testMenu.actions();
|
||||
for (QList<QAction*>::iterator itr = actualActions.begin(); itr != actualActions.end(); itr++)
|
||||
@@ -73,5 +74,10 @@ TEST_F(AWSCoreEditorMenuTest, AWSCoreEditorMenu_BroadcastFeatureGemsAreEnabled_C
|
||||
{
|
||||
EXPECT_TRUE((*itr)->isEnabled());
|
||||
}
|
||||
|
||||
if (QString::compare((*itr)->text(), AWSGameLiftActionText) == 0)
|
||||
{
|
||||
EXPECT_TRUE((*itr)->isEnabled());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzFramework/Session/SessionConfig.h>
|
||||
|
||||
#include <AWSGameLiftClientLocalTicketTracker.h>
|
||||
#include <AWSCoreBus.h>
|
||||
#include <AWSGameLiftClientManager.h>
|
||||
#include <AWSGameLiftClientSystemComponent.h>
|
||||
#include <Request/AWSGameLiftAcceptMatchRequest.h>
|
||||
@@ -105,6 +106,8 @@ namespace AWSGameLift
|
||||
m_gameliftClient.reset();
|
||||
m_gameliftManager->ActivateManager();
|
||||
m_gameliftTicketTracker->ActivateTracker();
|
||||
|
||||
AWSCore::AWSCoreEditorRequestBus::Broadcast(&AWSCore::AWSCoreEditorRequests::SetAWSGameLiftEnabled);
|
||||
}
|
||||
|
||||
void AWSGameLiftClientSystemComponent::Deactivate()
|
||||
|
||||
@@ -272,7 +272,10 @@ namespace AZ
|
||||
else if (m_componentConfig.m_target)
|
||||
{
|
||||
const auto& viewport = m_componentConfig.m_target->GetViewport();
|
||||
m_aspectRatio = viewport.m_maxX / viewport.m_maxY;
|
||||
if (viewport.m_maxX > 0.0f && viewport.m_maxY > 0.0f)
|
||||
{
|
||||
m_aspectRatio = viewport.m_maxX / viewport.m_maxY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
},
|
||||
{
|
||||
"Name": "m_colorGradingPreSaturation",
|
||||
"Value": 1.0 // -100 ... 100
|
||||
"Value": 1.0 // 0 ... 2
|
||||
},
|
||||
{
|
||||
"Name": "m_colorFilterIntensity",
|
||||
@@ -101,7 +101,7 @@
|
||||
},
|
||||
{
|
||||
"Name": "m_colorGradingPostSaturation",
|
||||
"Value": 1.0 // -100 ... 100
|
||||
"Value": 1.0 // 0 ... 2
|
||||
},
|
||||
{
|
||||
"Name": "m_smhShadowsStart",
|
||||
|
||||
@@ -105,6 +105,11 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "LutGenerationPass",
|
||||
"TemplateName": "LutGenerationTemplate",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "LookModificationTransformPass",
|
||||
"TemplateName": "LookModificationTransformTemplate",
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"Type": "JsonSerialization",
|
||||
"Version": 1,
|
||||
"ClassName": "PassAsset",
|
||||
"ClassData": {
|
||||
"PassTemplate": {
|
||||
"Name": "LutGenerationTemplate",
|
||||
"PassClass": "LutGenerationPass",
|
||||
"Slots": [
|
||||
{
|
||||
"Name": "LutOutput",
|
||||
"SlotType": "Output",
|
||||
"ScopeAttachmentUsage": "RenderTarget",
|
||||
"LoadStoreAction": {
|
||||
"LoadAction": "DontCare"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ImageAttachments": [
|
||||
{
|
||||
"Name": "ColorGradingLut",
|
||||
"ImageDescriptor": {
|
||||
"Format": "R32G32B32A32_FLOAT",
|
||||
"BindFlags": [
|
||||
"ShaderWrite"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"Connections": [
|
||||
{
|
||||
"LocalSlot": "LutOutput",
|
||||
"AttachmentRef": {
|
||||
"Pass": "This",
|
||||
"Attachment": "ColorGradingLut"
|
||||
}
|
||||
}
|
||||
],
|
||||
"PassData": {
|
||||
"$type": "FullscreenTrianglePassData",
|
||||
"ShaderAsset": {
|
||||
"FilePath": "Shaders/ColorGrading/LutGeneration.shader"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -511,7 +511,11 @@
|
||||
{
|
||||
"Name": "HDRColorGradingTemplate",
|
||||
"Path": "Passes/HDRColorGrading.pass"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "LutGenerationTemplate",
|
||||
"Path": "Passes/LutGeneration.pass"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ UNDISCLOSED.
|
||||
|
||||
#pragma once
|
||||
|
||||
static const float HALF_MAX = 65504.0f;
|
||||
#include <Atom/Features/PostProcessing/Aces.azsli>
|
||||
|
||||
float AcesCcToLinear(float value)
|
||||
{
|
||||
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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 <Atom/RPI/Math.azsli>
|
||||
#include <Atom/Features/ColorManagement/TransformColor.azsli>
|
||||
#include <Atom/Features/PostProcessing/AcesColorSpaceConversion.azsli>
|
||||
#include <3rdParty/Features/PostProcessing/PSstyleColorBlends_Separable.azsli>
|
||||
#include <3rdParty/Features/PostProcessing/PSstyleColorBlends_NonSeparable.azsli>
|
||||
#include <3rdParty/Features/PostProcessing/KelvinToRgb.azsli>
|
||||
|
||||
static const float FloatEpsilon = 1.192092896e-07; // 1.0 + FloatEpsilon != 1.0, smallest positive float
|
||||
static const float FloatMin = FLOAT_32_MIN; // Min float number that is positive
|
||||
static const float FloatMax = FLOAT_32_MAX; // Max float number representable
|
||||
|
||||
static const float AcesCcMidGrey = 0.4135884;
|
||||
|
||||
float SaturateWithEpsilon(float value)
|
||||
{
|
||||
return clamp(value, FloatEpsilon, 1.0f);
|
||||
}
|
||||
|
||||
// Below are the color grading functions. These expect the frame color to be in ACEScg space.
|
||||
// Note that some functions may have some quirks in their implementation and is subject to change.
|
||||
float3 ColorGradePostExposure (float3 frameColor, float exposure)
|
||||
{
|
||||
frameColor *= pow(2.0f, exposure);
|
||||
return frameColor;
|
||||
}
|
||||
|
||||
// The contrast equation is performed in ACEScc (logarithmic) color space.
|
||||
float3 ColorGradingContrast (float3 frameColor, float midgrey, float amount)
|
||||
{
|
||||
const float contrastAdjustment = amount * 0.01f + 1.0f;
|
||||
frameColor = TransformColor(frameColor.rgb, ColorSpaceId::ACEScg, ColorSpaceId::ACEScc);
|
||||
frameColor = (frameColor - midgrey) * contrastAdjustment + midgrey;
|
||||
return frameColor = TransformColor(frameColor.rgb, ColorSpaceId::ACEScc, ColorSpaceId::ACEScg);
|
||||
}
|
||||
|
||||
// The swatchColor param expects a linear RGB value.
|
||||
float3 ColorGradeColorFilter (float3 frameColor, float3 swatchColor, float alpha, float colorFilterIntensity)
|
||||
{
|
||||
swatchColor = TransformColor(swatchColor, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
|
||||
swatchColor *= pow(2.0f, colorFilterIntensity);
|
||||
const float3 frameAdjust = frameColor * swatchColor;
|
||||
return frameColor = lerp(frameColor, frameAdjust, alpha);
|
||||
}
|
||||
|
||||
float3 ColorGradeHueShift (float3 frameColor, float amount)
|
||||
{
|
||||
float3 frameHsv = RgbToHsv(frameColor);
|
||||
const float hue = frameHsv.x + amount;
|
||||
frameHsv.x = RotateHue(hue, 0.0, 1.0);
|
||||
return HsvToRgb(frameHsv);
|
||||
}
|
||||
|
||||
float3 ColorGradeSaturation (float3 frameColor, float control)
|
||||
{
|
||||
const float vLuminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg);
|
||||
return (frameColor - vLuminance) * control + vLuminance;
|
||||
}
|
||||
|
||||
float3 ColorGradeKelvinColorTemp(float3 frameColor, float kelvin)
|
||||
{
|
||||
const float3 kColor = TransformColor(KelvinToRgb(kelvin), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
|
||||
const float luminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg);
|
||||
const float3 resHsl = RgbToHsl(frameColor.rgb * kColor.rgb); // Apply Kelvin color and convert to HSL
|
||||
return HslToRgb(float3(resHsl.xy, luminance)); // Preserve luminance
|
||||
}
|
||||
|
||||
// pow(f, e) won't work if f is negative, or may cause inf/NAN.
|
||||
float3 NoNanPow(float3 base, float3 power)
|
||||
{
|
||||
return pow(max(abs(base), float3(FloatEpsilon, FloatEpsilon, FloatEpsilon)), power);
|
||||
}
|
||||
|
||||
float3 ColorGradeSplitTone (
|
||||
float3 frameColor,
|
||||
float balance,
|
||||
float weight,
|
||||
float3 splitToneShadowsColor,
|
||||
float3 splitToneHighlightsColor)
|
||||
{
|
||||
float3 frameSplitTone = NoNanPow(frameColor, 1.0 / 2.2);
|
||||
const float t = SaturateWithEpsilon(CalculateLuminance(SaturateWithEpsilon(frameSplitTone), ColorSpaceId::ACEScg) + balance);
|
||||
const float3 shadows = lerp(0.5, splitToneShadowsColor, 1.0 - t);
|
||||
const float3 highlights = lerp(0.5, splitToneHighlightsColor, t);
|
||||
frameSplitTone = BlendMode_SoftLight(frameSplitTone, shadows);
|
||||
frameSplitTone = BlendMode_SoftLight(frameSplitTone, highlights);
|
||||
frameSplitTone = NoNanPow(frameSplitTone, 2.2);
|
||||
return lerp(frameColor.rgb, frameSplitTone.rgb, weight);
|
||||
}
|
||||
|
||||
float3 ColorGradeChannelMixer (
|
||||
float3 frameColor,
|
||||
float3 channelMixingRed,
|
||||
float3 channelMixingGreen,
|
||||
float3 channelMixingBlue)
|
||||
{
|
||||
return mul(float3x3(channelMixingRed,
|
||||
channelMixingGreen,
|
||||
channelMixingBlue),
|
||||
frameColor);
|
||||
}
|
||||
|
||||
float3 ColorGradeShadowsMidtonesHighlights (float3 frameColor, float shadowsStart, float shadowsEnd,
|
||||
float highlightsStart, float highlightsEnd, float weight,
|
||||
float4 shadowsColor, float4 midtonesColor, float4 highlightsColor)
|
||||
{
|
||||
const float3 shadowsColorACEScg = TransformColor(shadowsColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
|
||||
const float3 midtonesColorACEScg = TransformColor(midtonesColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
|
||||
const float3 highlightsColorACEScg = TransformColor(highlightsColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
|
||||
|
||||
const float cLuminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg);
|
||||
const float shadowsWeight = 1.0 - smoothstep(shadowsStart, shadowsEnd, cLuminance);
|
||||
const float highlightsWeight = smoothstep(highlightsStart, highlightsEnd, cLuminance);
|
||||
const float midtonesWeight = 1.0 - shadowsWeight - highlightsWeight;
|
||||
|
||||
const float3 frameSmh = frameColor * shadowsColorACEScg * shadowsWeight +
|
||||
frameColor * midtonesColorACEScg * midtonesWeight +
|
||||
frameColor * highlightsColorACEScg * highlightsWeight;
|
||||
return lerp(frameColor.rgb, frameSmh.rgb, weight);
|
||||
}
|
||||
|
||||
// perform color grading in ACEScg space
|
||||
float3 ColorGrade(float3 frameColor)
|
||||
{
|
||||
frameColor = lerp(frameColor, ColorGradePostExposure(frameColor, PassSrg::m_colorGradingExposure), PassSrg::m_colorAdjustmentWeight);
|
||||
frameColor = lerp(frameColor, ColorGradeKelvinColorTemp(frameColor, PassSrg::m_whiteBalanceKelvin), PassSrg::m_whiteBalanceWeight);
|
||||
frameColor = lerp(frameColor, ColorGradingContrast(frameColor, AcesCcMidGrey, PassSrg::m_colorGradingContrast), PassSrg::m_colorAdjustmentWeight);
|
||||
frameColor = lerp(frameColor, ColorGradeColorFilter(frameColor, PassSrg::m_colorFilterSwatch.rgb,
|
||||
PassSrg::m_colorFilterMultiply, PassSrg::m_colorFilterIntensity), PassSrg::m_colorAdjustmentWeight);
|
||||
frameColor = max(frameColor, 0.0);
|
||||
frameColor = lerp(frameColor, ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPreSaturation), PassSrg::m_colorAdjustmentWeight);
|
||||
|
||||
frameColor = ColorGradeSplitTone(frameColor, PassSrg::m_splitToneBalance, PassSrg::m_splitToneWeight,
|
||||
PassSrg::m_splitToneShadowsColor, PassSrg::m_splitToneHighlightsColor);
|
||||
frameColor = ColorGradeChannelMixer(frameColor, PassSrg::m_channelMixingRed, PassSrg::m_channelMixingGreen, PassSrg::m_channelMixingBlue);
|
||||
frameColor = max(frameColor, 0.0);
|
||||
frameColor = ColorGradeShadowsMidtonesHighlights(frameColor, PassSrg::m_smhShadowsStart, PassSrg::m_smhShadowsEnd,
|
||||
PassSrg::m_smhHighlightsStart, PassSrg::m_smhHighlightsEnd, PassSrg::m_smhWeight,
|
||||
PassSrg::m_smhShadowsColor, PassSrg::m_smhMidtonesColor, PassSrg::m_smhHighlightsColor);
|
||||
|
||||
|
||||
frameColor = lerp(frameColor, ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPostSaturation), PassSrg::m_finalAdjustmentWeight);
|
||||
frameColor = lerp(frameColor, ColorGradeHueShift(frameColor, PassSrg::m_colorGradingHueShift), PassSrg::m_finalAdjustmentWeight);
|
||||
return max(frameColor.rgb, 0.0);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 <Atom/RPI/Math.azsli>
|
||||
|
||||
#include <Atom/Features/SrgSemantics.azsli>
|
||||
|
||||
#include <Atom/Features/PostProcessing/FullscreenVertex.azsli>
|
||||
|
||||
#include <Atom/Features/PostProcessing/Aces.azsli>
|
||||
#include <Atom/Features/PostProcessing/Shapers.azsli>
|
||||
|
||||
float3 convert2Dto3DLutCoords(float2 uv, float width, float height)
|
||||
{
|
||||
// convert from center pixel uvs to [0,1]
|
||||
float offset = 1.0/height/2.0;
|
||||
float scale = 1.0 - offset*2.0;
|
||||
|
||||
float2 adjustedUv = float2(uv.x * width, uv.y * height);
|
||||
float3 coords = float3(adjustedUv.x%height, 0.5 + int(adjustedUv.x/height), adjustedUv.y)/height;
|
||||
return (coords - offset)/scale;
|
||||
}
|
||||
|
||||
ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
|
||||
{
|
||||
// framebuffer sampler
|
||||
Sampler PointSampler
|
||||
{
|
||||
MinFilter = Point;
|
||||
MagFilter = Point;
|
||||
MipFilter = Point;
|
||||
AddressU = Clamp;
|
||||
AddressV = Clamp;
|
||||
AddressW = Clamp;
|
||||
};
|
||||
|
||||
int m_lutResolution;
|
||||
int m_shaperType;
|
||||
float m_shaperBias;
|
||||
float m_shaperScale;
|
||||
|
||||
float m_colorAdjustmentWeight;
|
||||
float m_colorGradingExposure;
|
||||
float m_colorGradingContrast;
|
||||
float m_colorGradingPreSaturation;
|
||||
float m_colorFilterIntensity;
|
||||
float m_colorFilterMultiply;
|
||||
float4 m_colorFilterSwatch;
|
||||
|
||||
float m_whiteBalanceWeight;
|
||||
float m_whiteBalanceKelvin;
|
||||
float m_whiteBalanceTint;
|
||||
|
||||
float m_splitToneBalance;
|
||||
float m_splitToneWeight;
|
||||
float4 m_splitToneShadowsColor;
|
||||
float4 m_splitToneHighlightsColor;
|
||||
|
||||
float m_smhShadowsStart;
|
||||
float m_smhShadowsEnd;
|
||||
float m_smhHighlightsStart;
|
||||
float m_smhHighlightsEnd;
|
||||
float m_smhWeight;
|
||||
float4 m_smhShadowsColor;
|
||||
float4 m_smhMidtonesColor;
|
||||
float4 m_smhHighlightsColor;
|
||||
|
||||
float3 m_channelMixingRed;
|
||||
float3 m_channelMixingGreen;
|
||||
float3 m_channelMixingBlue;
|
||||
|
||||
float m_finalAdjustmentWeight;
|
||||
float m_colorGradingPostSaturation;
|
||||
float m_colorGradingHueShift;
|
||||
}
|
||||
|
||||
#include <Atom/Features/PostProcessing/HDRColorGradingCommon.azsl>
|
||||
|
||||
struct PSOutput
|
||||
{
|
||||
float4 m_lutOutput : SV_Target0;
|
||||
};
|
||||
|
||||
PSOutput MainPS(VSOutput IN)
|
||||
{
|
||||
ShaperType shaperType = (ShaperType)PassSrg::m_shaperType;
|
||||
int lutResolution = PassSrg::m_lutResolution;
|
||||
|
||||
PSOutput OUT;
|
||||
|
||||
// baseCoords are from 0-1
|
||||
float3 baseCoords = convert2Dto3DLutCoords(IN.m_texCoord, lutResolution*lutResolution, lutResolution);
|
||||
|
||||
float3 linearColor = ShaperToLinear(baseCoords, shaperType, PassSrg::m_shaperBias, PassSrg::m_shaperScale);
|
||||
|
||||
linearColor = TransformColor(linearColor, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
|
||||
float3 gradedColor = ColorGrade(linearColor);
|
||||
gradedColor = TransformColor(gradedColor, ColorSpaceId::ACEScg, ColorSpaceId::LinearSRGB);
|
||||
|
||||
// Bring back coordinates into 0-1
|
||||
float3 shapedColor = LinearToShaper(gradedColor, shaperType, PassSrg::m_shaperBias, PassSrg::m_shaperScale);
|
||||
shapedColor = saturate(shapedColor);
|
||||
|
||||
OUT.m_lutOutput = float4(shapedColor, 1.0);
|
||||
return OUT;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"Source" : "LutGeneration",
|
||||
|
||||
"DepthStencilState" : {
|
||||
"Depth" : { "Enable" : false }
|
||||
},
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "MainVS",
|
||||
"type": "Vertex"
|
||||
},
|
||||
{
|
||||
"name": "MainPS",
|
||||
"type": "Fragment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -13,18 +13,6 @@
|
||||
#include <Atom/Features/PostProcessing/FullscreenPixelInfo.azsli>
|
||||
#include <Atom/Features/PostProcessing/FullscreenVertex.azsli>
|
||||
|
||||
#include <Atom/Features/ColorManagement/TransformColor.azsli>
|
||||
#include <Atom/Features/PostProcessing/AcesColorSpaceConversion.azsli>
|
||||
#include <3rdParty/Features/PostProcessing/PSstyleColorBlends_Separable.azsli>
|
||||
#include <3rdParty/Features/PostProcessing/PSstyleColorBlends_NonSeparable.azsli>
|
||||
#include <3rdParty/Features/PostProcessing/KelvinToRgb.azsli>
|
||||
|
||||
static const float FloatEpsilon = 1.192092896e-07; // 1.0 + FloatEpsilon != 1.0, smallest positive float
|
||||
static const float FloatMin = FLOAT_32_MIN; // Min float number that is positive
|
||||
static const float FloatMax = FLOAT_32_MAX; // Max float number representable
|
||||
|
||||
static const float AcesCcMidGrey = 0.4135884;
|
||||
|
||||
ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
|
||||
{
|
||||
// get the framebuffer
|
||||
@@ -41,153 +29,42 @@ ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback
|
||||
AddressW = Clamp;
|
||||
};
|
||||
|
||||
float m_colorAdjustmentWeight;
|
||||
float m_colorGradingExposure;
|
||||
float m_colorGradingContrast;
|
||||
float m_colorGradingHueShift;
|
||||
float m_colorGradingPreSaturation;
|
||||
float m_colorFilterIntensity;
|
||||
float m_colorFilterMultiply;
|
||||
float4 m_colorFilterSwatch;
|
||||
|
||||
float m_whiteBalanceWeight;
|
||||
float m_whiteBalanceKelvin;
|
||||
float m_whiteBalanceTint;
|
||||
|
||||
float m_splitToneBalance;
|
||||
float m_splitToneWeight;
|
||||
float m_colorGradingPostSaturation;
|
||||
float4 m_splitToneShadowsColor;
|
||||
float4 m_splitToneHighlightsColor;
|
||||
|
||||
float m_smhShadowsStart;
|
||||
float m_smhShadowsEnd;
|
||||
float m_smhHighlightsStart;
|
||||
float m_smhHighlightsEnd;
|
||||
float m_smhWeight;
|
||||
float4 m_smhShadowsColor;
|
||||
float4 m_smhMidtonesColor;
|
||||
float4 m_smhHighlightsColor;
|
||||
|
||||
float3 m_channelMixingRed;
|
||||
float3 m_channelMixingGreen;
|
||||
float3 m_channelMixingBlue;
|
||||
|
||||
float4 m_colorFilterSwatch;
|
||||
float4 m_splitToneShadowsColor;
|
||||
float4 m_splitToneHighlightsColor;
|
||||
|
||||
float4 m_smhShadowsColor;
|
||||
float4 m_smhMidtonesColor;
|
||||
float4 m_smhHighlightsColor;
|
||||
float m_finalAdjustmentWeight;
|
||||
float m_colorGradingPostSaturation;
|
||||
float m_colorGradingHueShift;
|
||||
}
|
||||
|
||||
float SaturateWithEpsilon(float value)
|
||||
{
|
||||
return clamp(value, FloatEpsilon, 1.0f);
|
||||
}
|
||||
|
||||
// Below are the color grading functions. These expect the frame color to be in ACEScg space.
|
||||
// Note that some functions may have some quirks in their implementation and is subject to change.
|
||||
float3 ColorGradePostExposure (float3 frameColor, float exposure)
|
||||
{
|
||||
frameColor *= pow(2.0f, exposure);
|
||||
return frameColor;
|
||||
}
|
||||
|
||||
// The contrast equation is performed in ACEScc (logarithmic) color space.
|
||||
float3 ColorGradingContrast (float3 frameColor, float midgrey, float amount)
|
||||
{
|
||||
const float contrastAdjustment = amount * 0.01f + 1.0f;
|
||||
frameColor = TransformColor(frameColor.rgb, ColorSpaceId::ACEScg, ColorSpaceId::ACEScc);
|
||||
frameColor = (frameColor - midgrey) * contrastAdjustment + midgrey;
|
||||
return frameColor = TransformColor(frameColor.rgb, ColorSpaceId::ACEScc, ColorSpaceId::ACEScg);
|
||||
}
|
||||
|
||||
// The swatchColor param expects a linear RGB value.
|
||||
float3 ColorGradeColorFilter (float3 frameColor, float3 swatchColor, float alpha)
|
||||
{
|
||||
swatchColor = TransformColor(swatchColor, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
|
||||
swatchColor *= pow(2.0f, PassSrg::m_colorFilterIntensity);
|
||||
const float3 frameAdjust = frameColor * swatchColor;
|
||||
return frameColor = lerp(frameColor, frameAdjust, alpha);
|
||||
}
|
||||
|
||||
float3 ColorGradeHueShift (float3 frameColor, float amount)
|
||||
{
|
||||
float3 frameHsv = RgbToHsv(frameColor);
|
||||
const float hue = frameHsv.x + amount;
|
||||
frameHsv.x = RotateHue(hue, 0.0, 1.0);
|
||||
return HsvToRgb(frameHsv);
|
||||
}
|
||||
|
||||
float3 ColorGradeSaturation (float3 frameColor, float control)
|
||||
{
|
||||
const float vLuminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg);
|
||||
return (frameColor - vLuminance) * control + vLuminance;
|
||||
}
|
||||
|
||||
float3 ColorGradeKelvinColorTemp(float3 frameColor, float kelvin)
|
||||
{
|
||||
const float3 kColor = TransformColor(KelvinToRgb(kelvin), ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
|
||||
const float luminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg);
|
||||
const float3 resHsl = RgbToHsl(frameColor.rgb * kColor.rgb); // Apply Kelvin color and convert to HSL
|
||||
return HslToRgb(float3(resHsl.xy, luminance)); // Preserve luminance
|
||||
}
|
||||
|
||||
// pow(f, e) won't work if f is negative, or may cause inf/NAN.
|
||||
float3 NoNanPow(float3 base, float3 power)
|
||||
{
|
||||
return pow(max(abs(base), float3(FloatEpsilon, FloatEpsilon, FloatEpsilon)), power);
|
||||
}
|
||||
|
||||
float3 ColorGradeSplitTone (float3 frameColor, float balance, float weight)
|
||||
{
|
||||
float3 frameSplitTone = NoNanPow(frameColor, 1.0 / 2.2);
|
||||
const float t = SaturateWithEpsilon(CalculateLuminance(SaturateWithEpsilon(frameSplitTone), ColorSpaceId::ACEScg) + balance);
|
||||
const float3 shadows = lerp(0.5, PassSrg::m_splitToneShadowsColor.rgb, 1.0 - t);
|
||||
const float3 highlights = lerp(0.5, PassSrg::m_splitToneHighlightsColor.rgb, t);
|
||||
frameSplitTone = BlendMode_SoftLight(frameSplitTone, shadows);
|
||||
frameSplitTone = BlendMode_SoftLight(frameSplitTone, highlights);
|
||||
frameSplitTone = NoNanPow(frameSplitTone, 2.2);
|
||||
return lerp(frameColor.rgb, frameSplitTone.rgb, weight);
|
||||
}
|
||||
|
||||
float3 ColorGradeChannelMixer (float3 frameColor)
|
||||
{
|
||||
return mul(float3x3(PassSrg::m_channelMixingRed.rgb,
|
||||
PassSrg::m_channelMixingGreen.rgb,
|
||||
PassSrg::m_channelMixingBlue.rgb),
|
||||
frameColor);
|
||||
}
|
||||
|
||||
float3 ColorGradeShadowsMidtonesHighlights (float3 frameColor, float shadowsStart, float shadowsEnd,
|
||||
float highlightsStart, float highlightsEnd, float weight,
|
||||
float4 shadowsColor, float4 midtonesColor, float4 highlightsColor)
|
||||
{
|
||||
const float3 shadowsColorACEScg = TransformColor(shadowsColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
|
||||
const float3 midtonesColorACEScg = TransformColor(midtonesColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
|
||||
const float3 highlightsColorACEScg = TransformColor(highlightsColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
|
||||
|
||||
const float cLuminance = CalculateLuminance(frameColor, ColorSpaceId::ACEScg);
|
||||
const float shadowsWeight = 1.0 - smoothstep(shadowsStart, shadowsEnd, cLuminance);
|
||||
const float highlightsWeight = smoothstep(highlightsStart, highlightsEnd, cLuminance);
|
||||
const float midtonesWeight = 1.0 - shadowsWeight - highlightsWeight;
|
||||
|
||||
const float3 frameSmh = frameColor * shadowsColorACEScg * shadowsWeight +
|
||||
frameColor * midtonesColorACEScg * midtonesWeight +
|
||||
frameColor * highlightsColorACEScg * highlightsWeight;
|
||||
return lerp(frameColor.rgb, frameSmh.rgb, weight);
|
||||
}
|
||||
|
||||
float3 ColorGrade (float3 frameColor)
|
||||
{
|
||||
frameColor = ColorGradePostExposure(frameColor, PassSrg::m_colorGradingExposure);
|
||||
frameColor = ColorGradeKelvinColorTemp(frameColor, PassSrg::m_whiteBalanceKelvin);
|
||||
frameColor = ColorGradingContrast(frameColor, AcesCcMidGrey, PassSrg::m_colorGradingContrast);
|
||||
frameColor = ColorGradeColorFilter(frameColor, PassSrg::m_colorFilterSwatch.rgb,
|
||||
PassSrg::m_colorFilterMultiply);
|
||||
frameColor = max(frameColor, 0.0);
|
||||
frameColor = ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPreSaturation);
|
||||
frameColor = ColorGradeSplitTone(frameColor, PassSrg::m_splitToneBalance, PassSrg::m_splitToneWeight);
|
||||
frameColor = ColorGradeChannelMixer(frameColor);
|
||||
frameColor = max(frameColor, 0.0);
|
||||
frameColor = ColorGradeShadowsMidtonesHighlights(frameColor, PassSrg::m_smhShadowsStart, PassSrg::m_smhShadowsEnd,
|
||||
PassSrg::m_smhHighlightsStart, PassSrg::m_smhHighlightsEnd, PassSrg::m_smhWeight,
|
||||
PassSrg::m_smhShadowsColor, PassSrg::m_smhMidtonesColor, PassSrg::m_smhHighlightsColor);
|
||||
frameColor = ColorGradeHueShift(frameColor, PassSrg::m_colorGradingHueShift);
|
||||
frameColor = ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPostSaturation);
|
||||
return frameColor.rgb;
|
||||
}
|
||||
#include <Atom/Features/PostProcessing/HDRColorGradingCommon.azsl>
|
||||
|
||||
PSOutput MainPS(VSOutput IN)
|
||||
{
|
||||
|
||||
@@ -86,7 +86,6 @@ set(FILES
|
||||
Passes/CascadedShadowmaps.pass
|
||||
Passes/CheckerboardResolveColor.pass
|
||||
Passes/CheckerboardResolveDepth.pass
|
||||
Passes/HDRColorGrading.pass
|
||||
Passes/ContrastAdaptiveSharpening.pass
|
||||
Passes/ConvertToAcescg.pass
|
||||
Passes/DebugOverlayParent.pass
|
||||
@@ -144,6 +143,7 @@ set(FILES
|
||||
Passes/ForwardSubsurfaceMSAA.pass
|
||||
Passes/FullscreenCopy.pass
|
||||
Passes/FullscreenOutputOnly.pass
|
||||
Passes/HDRColorGrading.pass
|
||||
Passes/ImGui.pass
|
||||
Passes/KawaseShadowBlur.pass
|
||||
Passes/LightAdaptationParent.pass
|
||||
@@ -158,6 +158,7 @@ set(FILES
|
||||
Passes/LowEndPipeline.pass
|
||||
Passes/LuminanceHeatmap.pass
|
||||
Passes/LuminanceHistogramGenerator.pass
|
||||
Passes/LutGeneration.pass
|
||||
Passes/MainPipeline.pass
|
||||
Passes/MainPipelineRenderToTexture.pass
|
||||
Passes/ThumbnailPipeline.pass
|
||||
@@ -281,6 +282,7 @@ set(FILES
|
||||
ShaderLib/Atom/Features/PostProcessing/FullscreenVertexUtil.azsli
|
||||
ShaderLib/Atom/Features/PostProcessing/GlyphData.azsli
|
||||
ShaderLib/Atom/Features/PostProcessing/GlyphRender.azsli
|
||||
ShaderLib/Atom/Features/PostProcessing/HDRColorGradingCommon.azsl
|
||||
ShaderLib/Atom/Features/PostProcessing/PostProcessUtil.azsli
|
||||
ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli
|
||||
ShaderLib/Atom/Features/ScreenSpace/ScreenSpaceUtil.azsli
|
||||
@@ -315,6 +317,8 @@ set(FILES
|
||||
Shaders/BRDFTexture/BRDFTextureCS.shader
|
||||
Shaders/Checkerboard/CheckerboardColorResolveCS.azsl
|
||||
Shaders/Checkerboard/CheckerboardColorResolveCS.shader
|
||||
Shaders/ColorGrading/LutGeneration.azsl
|
||||
Shaders/ColorGrading/LutGeneration.shader
|
||||
Shaders/Depth/DepthPass.azsl
|
||||
Shaders/Depth/DepthPass.shader
|
||||
Shaders/Depth/DepthPassTransparentMax.shader
|
||||
|
||||
@@ -39,6 +39,7 @@ ly_add_target(
|
||||
Gem::Atom_Utils.Static
|
||||
Gem::Atom_Feature_Common.Public
|
||||
Gem::ImGui.imguilib
|
||||
3rdParty::TIFF
|
||||
#3rdParty::lux_core # AZ_TRAIT_LUXCORE_SUPPORTED is disabled in every platform, Issue #3915 will remove
|
||||
RUNTIME_DEPENDENCIES
|
||||
Gem::ImGui.imguilib
|
||||
@@ -75,6 +76,7 @@ ly_add_target(
|
||||
FILES_CMAKE
|
||||
atom_feature_common_shared_files.cmake
|
||||
../Assets/atom_feature_common_asset_files.cmake
|
||||
../Editor/atom_feature_common_editor_script_files.cmake
|
||||
PLATFORM_INCLUDE_FILES
|
||||
${pal_source_dir}/runtime_dependencies_clients.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
enum class LutResolution
|
||||
{
|
||||
Lut16x16x16 = 16,
|
||||
Lut32x32x32 = 32,
|
||||
Lut64x64x64 = 64
|
||||
};
|
||||
}
|
||||
}
|
||||
+25
-11
@@ -10,28 +10,42 @@
|
||||
// PARAM(NAME, MEMBER_NAME, DEFAULT_VALUE, ...)
|
||||
|
||||
AZ_GFX_BOOL_PARAM(Enabled, m_enabled, false)
|
||||
AZ_GFX_BOOL_PARAM(GenerateLut, m_generateLut, false)
|
||||
AZ_GFX_FLOAT_PARAM(ColorAdjustmentWeight, m_colorAdjustmentWeight, 1.0)
|
||||
AZ_GFX_FLOAT_PARAM(ColorGradingExposure, m_colorGradingExposure, 0.0)
|
||||
AZ_GFX_FLOAT_PARAM(ColorGradingContrast, m_colorGradingContrast, 0.0)
|
||||
AZ_GFX_FLOAT_PARAM(ColorGradingHueShift, m_colorGradingHueShift, 0.0)
|
||||
AZ_GFX_FLOAT_PARAM(ColorGradingPreSaturation, m_colorGradingPreSaturation, 1.0)
|
||||
AZ_GFX_FLOAT_PARAM(ColorGradingPreSaturation, m_colorGradingPreSaturation, 0.0)
|
||||
AZ_GFX_FLOAT_PARAM(ColorGradingFilterIntensity, m_colorGradingFilterIntensity, 1.0)
|
||||
AZ_GFX_FLOAT_PARAM(ColorGradingFilterMultiply, m_colorGradingFilterMultiply, 0.0)
|
||||
AZ_GFX_FLOAT_PARAM(ColorGradingPostSaturation, m_colorGradingPostSaturation, 1.0)
|
||||
AZ_GFX_VEC3_PARAM(ColorFilterSwatch, m_colorFilterSwatch, AZ::Vector3(1.0f, 0.5f, 0.5f))
|
||||
|
||||
AZ_GFX_FLOAT_PARAM(WhiteBalanceWeight, m_whiteBalanceWeight, 0.0)
|
||||
AZ_GFX_FLOAT_PARAM(WhiteBalanceKelvin, m_whiteBalanceKelvin, 6600.0)
|
||||
AZ_GFX_FLOAT_PARAM(WhiteBalanceTint, m_whiteBalanceTint, 0.0)
|
||||
AZ_GFX_FLOAT_PARAM(SplitToneBalance, m_splitToneBalance, 0.0)
|
||||
|
||||
AZ_GFX_FLOAT_PARAM(SplitToneWeight, m_splitToneWeight, 0.0)
|
||||
AZ_GFX_FLOAT_PARAM(SplitToneBalance, m_splitToneBalance, 0.0)
|
||||
AZ_GFX_VEC3_PARAM(SplitToneShadowsColor, m_splitToneShadowsColor, AZ::Vector3(1.0f, 0.5f, 0.5f))
|
||||
AZ_GFX_VEC3_PARAM(SplitToneHighlightsColor, m_splitToneHighlightsColor, AZ::Vector3(0.1f, 1.0f, 0.1f))
|
||||
|
||||
AZ_GFX_FLOAT_PARAM(SmhWeight, m_smhWeight, 0.0)
|
||||
AZ_GFX_FLOAT_PARAM(SmhShadowsStart, m_smhShadowsStart, 0.0)
|
||||
AZ_GFX_FLOAT_PARAM(SmhShadowsEnd, m_smhShadowsEnd, 0.3)
|
||||
AZ_GFX_FLOAT_PARAM(SmhHighlightsStart, m_smhHighlightsStart, 0.55)
|
||||
AZ_GFX_FLOAT_PARAM(SmhHighlightsEnd, m_smhHighlightsEnd, 1.0)
|
||||
AZ_GFX_FLOAT_PARAM(SmhWeight, m_smhWeight, 0.0)
|
||||
AZ_GFX_VEC3_PARAM(ChannelMixingRed, m_channelMixingRed, AZ::Vector3(1.0f, 0.0f, 0.0f))
|
||||
AZ_GFX_VEC3_PARAM(ChannelMixingGreen, m_channelMixingGreen, AZ::Vector3(0.0f, 1.0f, 0.0f))
|
||||
AZ_GFX_VEC3_PARAM(ChannelMixingBlue, m_channelMixingBlue, AZ::Vector3(0.0f, 0.f, 1.0f))
|
||||
AZ_GFX_VEC3_PARAM(ColorFilterSwatch, m_colorFilterSwatch, AZ::Vector3(1.0f, 0.5f, 0.5f))
|
||||
AZ_GFX_VEC3_PARAM(SplitToneShadowsColor, m_splitToneShadowsColor, AZ::Vector3(1.0f, 0.5f, 0.5f))
|
||||
AZ_GFX_VEC3_PARAM(SplitToneHighlightsColor, m_splitToneHighlightsColor, AZ::Vector3(0.1f, 1.0f, 0.1f))
|
||||
AZ_GFX_VEC3_PARAM(SmhShadowsColor, m_smhShadowsColor, AZ::Vector3(1.0f, 0.25f, 0.25f))
|
||||
AZ_GFX_VEC3_PARAM(SmhMidtonesColor, m_smhMidtonesColor, AZ::Vector3(0.1f, 0.1f, 1.0f))
|
||||
AZ_GFX_VEC3_PARAM(SmhHighlightsColor, m_smhHighlightsColor, AZ::Vector3(1.0f, 0.0f, 1.0f))
|
||||
|
||||
AZ_GFX_VEC3_PARAM(ChannelMixingRed, m_channelMixingRed, AZ::Vector3(1.0f, 0.0f, 0.0f))
|
||||
AZ_GFX_VEC3_PARAM(ChannelMixingGreen, m_channelMixingGreen, AZ::Vector3(0.0f, 1.0f, 0.0f))
|
||||
AZ_GFX_VEC3_PARAM(ChannelMixingBlue, m_channelMixingBlue, AZ::Vector3(0.0f, 0.f, 1.0f))
|
||||
|
||||
AZ_GFX_COMMON_PARAM(AZ::Render::LutResolution, LutResolution, m_lutResolution, AZ::Render::LutResolution::Lut16x16x16)
|
||||
AZ_GFX_COMMON_PARAM(AZ::Render::ShaperPresetType, ShaperPresetType, m_shaperPresetType, AZ::Render::ShaperPresetType::None)
|
||||
AZ_GFX_COMMON_PARAM(float, CustomMinExposure, m_customMinExposure, -6.5)
|
||||
AZ_GFX_COMMON_PARAM(float, CustomMaxExposure, m_customMaxExposure, 6.5)
|
||||
|
||||
AZ_GFX_FLOAT_PARAM(FinalAdjustmentWeight, m_finalAdjustmentWeight, 1.0)
|
||||
AZ_GFX_FLOAT_PARAM(ColorGradingPostSaturation, m_colorGradingPostSaturation, 0.0)
|
||||
AZ_GFX_FLOAT_PARAM(ColorGradingHueShift, m_colorGradingHueShift, 0.0)
|
||||
|
||||
+2
@@ -12,6 +12,8 @@
|
||||
#include <AzCore/Math/Vector4.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <Atom/Feature/ColorGrading/LutResolution.h>
|
||||
#include <ACES/Aces.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
@@ -28,15 +28,6 @@ namespace AZ
|
||||
//! Dump the PipelineStatistics from passes to a json file.
|
||||
virtual bool CapturePassPipelineStatistics(const AZStd::string& outputFilePath) = 0;
|
||||
|
||||
//! Dump a single frame of Cpu profiling data
|
||||
virtual bool CaptureCpuProfilingStatistics(const AZStd::string& outputFilePath) = 0;
|
||||
|
||||
//! Start a multiframe capture of CPU profiling data.
|
||||
virtual bool BeginContinuousCpuProfilingCapture() = 0;
|
||||
|
||||
//! End and dump an in-progress continuous capture.
|
||||
virtual bool EndContinuousCpuProfilingCapture(const AZStd::string& outputFilePath) = 0;
|
||||
|
||||
//! Dump the benchmark metadata to a json file.
|
||||
virtual bool CaptureBenchmarkMetadata(const AZStd::string& benchmarkName, const AZStd::string& outputFilePath) = 0;
|
||||
};
|
||||
@@ -63,11 +54,6 @@ namespace AZ
|
||||
//! @param info The output file path or error information which depends on the return.
|
||||
virtual void OnCaptureQueryPipelineStatisticsFinished(bool result, const AZStd::string& info) = 0;
|
||||
|
||||
//! Notify when the current CpuProfilingStatistics capture is finished
|
||||
//! @param result Set to true if it's finished successfully
|
||||
//! @param info The output file path or error information which depends on the return.
|
||||
virtual void OnCaptureCpuProfilingStatisticsFinished(bool result, const AZStd::string& info) = 0;
|
||||
|
||||
//! Notify when the current BenchmarkMetadata capture is finished
|
||||
//! @param result Set to true if it's finished successfully
|
||||
//! @param info The output file path or error information which depends on the return.
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 <ColorGrading/LutGenerationPass.h>
|
||||
#include <Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h>
|
||||
|
||||
#include <Atom/RPI.Public/Scene.h>
|
||||
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
|
||||
RPI::Ptr<LutGenerationPass> LutGenerationPass::Create(const RPI::PassDescriptor& descriptor)
|
||||
{
|
||||
RPI::Ptr<LutGenerationPass> pass = aznew LutGenerationPass(descriptor);
|
||||
return AZStd::move(pass);
|
||||
}
|
||||
|
||||
LutGenerationPass::LutGenerationPass(const RPI::PassDescriptor& descriptor)
|
||||
: HDRColorGradingPass(descriptor)
|
||||
{
|
||||
}
|
||||
|
||||
void LutGenerationPass::InitializeInternal()
|
||||
{
|
||||
HDRColorGradingPass::InitializeInternal();
|
||||
|
||||
m_lutResolutionIndex.Reset();
|
||||
m_lutShaperTypeIndex.Reset();
|
||||
m_lutShaperScaleIndex.Reset();
|
||||
}
|
||||
|
||||
void LutGenerationPass::FrameBeginInternal(FramePrepareParams params)
|
||||
{
|
||||
const auto* colorGradingSettings = GetHDRColorGradingSettings();
|
||||
if (colorGradingSettings)
|
||||
{
|
||||
m_shaderResourceGroup->SetConstant(m_lutResolutionIndex, colorGradingSettings->GetLutResolution());
|
||||
|
||||
auto shaperParams = AcesDisplayMapperFeatureProcessor::GetShaperParameters(
|
||||
colorGradingSettings->GetShaperPresetType(),
|
||||
colorGradingSettings->GetCustomMinExposure(),
|
||||
colorGradingSettings->GetCustomMaxExposure());
|
||||
m_shaderResourceGroup->SetConstant(m_lutShaperTypeIndex, shaperParams.m_type);
|
||||
m_shaderResourceGroup->SetConstant(m_lutShaperBiasIndex, shaperParams.m_bias);
|
||||
m_shaderResourceGroup->SetConstant(m_lutShaperScaleIndex, shaperParams.m_scale);
|
||||
}
|
||||
|
||||
HDRColorGradingPass::FrameBeginInternal(params);
|
||||
}
|
||||
|
||||
void LutGenerationPass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context)
|
||||
{
|
||||
const auto* colorGradingSettings = GetHDRColorGradingSettings();
|
||||
if (colorGradingSettings)
|
||||
{
|
||||
uint32_t lutResolution = aznumeric_cast<uint32_t>(colorGradingSettings->GetLutResolution());
|
||||
RHI::Size lutSize{ lutResolution * lutResolution, lutResolution, 1 };
|
||||
|
||||
RPI::Ptr<RPI::PassAttachment> attachment = FindOwnedAttachment(Name{ "ColorGradingLut" });
|
||||
RHI::ImageDescriptor& imageDescriptor = attachment->m_descriptor.m_image;
|
||||
imageDescriptor.m_size = lutSize;
|
||||
SetViewportScissorFromImageSize(lutSize);
|
||||
}
|
||||
HDRColorGradingPass::BuildCommandListInternal(context);
|
||||
}
|
||||
|
||||
bool LutGenerationPass::IsEnabled() const
|
||||
{
|
||||
const auto* colorGradingSettings = GetHDRColorGradingSettings();
|
||||
return colorGradingSettings ? colorGradingSettings->GetGenerateLut() : false;
|
||||
}
|
||||
|
||||
void LutGenerationPass::SetViewportScissorFromImageSize(const RHI::Size& imageSize)
|
||||
{
|
||||
const RHI::Viewport viewport(0.f, imageSize.m_width * 1.f, 0.f, imageSize.m_height * 1.f);
|
||||
const RHI::Scissor scissor(0, 0, imageSize.m_width, imageSize.m_height);
|
||||
m_viewportState = viewport;
|
||||
m_scissorState = scissor;
|
||||
}
|
||||
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 <PostProcessing/HDRColorGradingPass.h>
|
||||
#include <Atom/RPI.Public/Shader/Shader.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantKey.h>
|
||||
|
||||
#include <PostProcess/ColorGrading/HDRColorGradingSettings.h>
|
||||
#include <Atom/Feature/DisplayMapper/DisplayMapperFeatureProcessorInterface.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
// Performs color grading on an identity LUT strip
|
||||
class LutGenerationPass
|
||||
: public AZ::Render::HDRColorGradingPass
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(LutGenerationPass, "{C21DABA8-B538-4C80-BA18-5B97CC9259E5}", AZ::RPI::FullscreenTrianglePass);
|
||||
AZ_CLASS_ALLOCATOR(LutGenerationPass, SystemAllocator, 0);
|
||||
|
||||
virtual ~LutGenerationPass() = default;
|
||||
|
||||
//! Creates a ColorGradingPass
|
||||
static RPI::Ptr<LutGenerationPass> Create(const RPI::PassDescriptor& descriptor);
|
||||
|
||||
protected:
|
||||
LutGenerationPass(const RPI::PassDescriptor& descriptor);
|
||||
|
||||
void InitializeInternal() override;
|
||||
void FrameBeginInternal(FramePrepareParams params) override;
|
||||
void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override;
|
||||
bool IsEnabled() const override;
|
||||
private:
|
||||
// Set viewport scissor based on output LUT resolution
|
||||
void SetViewportScissorFromImageSize(const RHI::Size& imageSize);
|
||||
|
||||
RHI::ShaderInputNameIndex m_lutResolutionIndex = "m_lutResolution";
|
||||
RHI::ShaderInputNameIndex m_lutShaperTypeIndex = "m_shaperType";
|
||||
RHI::ShaderInputNameIndex m_lutShaperBiasIndex = "m_shaperBias";
|
||||
RHI::ShaderInputNameIndex m_lutShaperScaleIndex = "m_shaperScale";
|
||||
};
|
||||
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <Atom/Feature/AuxGeom/AuxGeomFeatureProcessor.h>
|
||||
#include <Atom/Feature/Utils/LightingPreset.h>
|
||||
#include <Atom/Feature/Utils/ModelPreset.h>
|
||||
#include <ColorGrading/LutGenerationPass.h>
|
||||
#include <PostProcess/PostProcessFeatureProcessor.h>
|
||||
#include <PostProcessing/BlendColorGradingLutsPass.h>
|
||||
#include <PostProcessing/BloomParentPass.h>
|
||||
@@ -230,6 +231,7 @@ namespace AZ
|
||||
passSystem->AddPassCreator(Name("HDRColorGradingPass"), &HDRColorGradingPass::Create);
|
||||
passSystem->AddPassCreator(Name("LookModificationCompositePass"), &LookModificationCompositePass::Create);
|
||||
passSystem->AddPassCreator(Name("LookModificationTransformPass"), &LookModificationPass::Create);
|
||||
passSystem->AddPassCreator(Name("LutGenerationPass"), &LutGenerationPass::Create);
|
||||
passSystem->AddPassCreator(Name("SMAAEdgeDetectionPass"), &SMAAEdgeDetectionPass::Create);
|
||||
passSystem->AddPassCreator(Name("SMAABlendingWeightCalculationPass"), &SMAABlendingWeightCalculationPass::Create);
|
||||
passSystem->AddPassCreator(Name("SMAANeighborhoodBlendingPass"), &SMAANeighborhoodBlendingPass::Create);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <AzCore/Preprocessor/EnumReflectUtils.h>
|
||||
#include <AzCore/Console/Console.h>
|
||||
|
||||
#include <tiffio.h>
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
@@ -110,6 +111,48 @@ namespace AZ
|
||||
return FrameCaptureOutputResult{FrameCaptureResult::InternalError, "Unable to save frame capture output to '" + outputFilePath + "'"};
|
||||
}
|
||||
|
||||
FrameCaptureOutputResult TiffFrameCaptureOutput(
|
||||
const AZStd::string& outputFilePath, const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
|
||||
{
|
||||
AZStd::shared_ptr<AZStd::vector<uint8_t>> buffer = readbackResult.m_dataBuffer;
|
||||
const uint32_t width = readbackResult.m_imageDescriptor.m_size.m_width;
|
||||
const uint32_t height = readbackResult.m_imageDescriptor.m_size.m_height;
|
||||
const uint32_t numChannels = AZ::RHI::GetFormatComponentCount(readbackResult.m_imageDescriptor.m_format);
|
||||
const uint32_t bytesPerChannel = AZ::RHI::GetFormatSize(readbackResult.m_imageDescriptor.m_format) / numChannels;
|
||||
const uint32_t bitsPerChannel = bytesPerChannel * 8;
|
||||
|
||||
TIFF* out = TIFFOpen(outputFilePath.c_str(), "w");
|
||||
TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width);
|
||||
TIFFSetField(out, TIFFTAG_IMAGELENGTH, height);
|
||||
TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, numChannels);
|
||||
TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, bitsPerChannel);
|
||||
TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
|
||||
TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
||||
TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
||||
TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
|
||||
TIFFSetField(out, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP); // interpret each pixel as a float
|
||||
|
||||
size_t pitch = width * numChannels * bytesPerChannel;
|
||||
AZ_Assert((pitch * height) == buffer->size(), "Image buffer does not match allocated bytes for tiff saving.")
|
||||
unsigned char* raster = (unsigned char*)_TIFFmalloc((tsize_t)(pitch * height));
|
||||
memcpy(raster, buffer->data(), pitch * height);
|
||||
bool success = true;
|
||||
for (uint32_t h = 0; h < height; ++h)
|
||||
{
|
||||
size_t offset = h * pitch;
|
||||
int err = TIFFWriteScanline(out, raster + offset, h, 0);
|
||||
if (err < 0)
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_TIFFfree(raster);
|
||||
TIFFClose(out);
|
||||
return success ? FrameCaptureOutputResult{ FrameCaptureResult::Success, AZStd::nullopt }
|
||||
: FrameCaptureOutputResult{ FrameCaptureResult::InternalError, "Unable to save tif frame capture output to " + outputFilePath };
|
||||
}
|
||||
|
||||
FrameCaptureOutputResult DdsFrameCaptureOutput(
|
||||
const AZStd::string& outputFilePath, const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
|
||||
{
|
||||
@@ -492,6 +535,12 @@ namespace AZ
|
||||
m_result = ddsFrameCapture.m_result;
|
||||
m_latestCaptureInfo = ddsFrameCapture.m_errorMessage.value_or("");
|
||||
}
|
||||
else if (extension == "tiff" || extension == "tif")
|
||||
{
|
||||
const auto tifFrameCapture = TiffFrameCaptureOutput(m_outputFilePath, readbackResult);
|
||||
m_result = tifFrameCapture.m_result;
|
||||
m_latestCaptureInfo = tifFrameCapture.m_errorMessage.value_or("");
|
||||
}
|
||||
else if (extension == "png")
|
||||
{
|
||||
if (readbackResult.m_imageDescriptor.m_format == RHI::Format::R8G8B8A8_UNORM ||
|
||||
|
||||
+1
@@ -33,6 +33,7 @@ namespace AZ
|
||||
target->m_enabled = m_enabled;
|
||||
|
||||
#define AZ_GFX_BOOL_PARAM(NAME, MEMBER_NAME, DefaultValue) ;
|
||||
#define AZ_GFX_COMMON_PARAM(ValueType, Name, MemberName, DefaultValue) ;
|
||||
#define AZ_GFX_FLOAT_PARAM(NAME, MEMBER_NAME, DefaultValue) \
|
||||
{ \
|
||||
target->Set##NAME(AZ::Lerp(target->MEMBER_NAME, MEMBER_NAME, alpha)); \
|
||||
|
||||
+1
@@ -13,6 +13,7 @@
|
||||
#include <Atom/Feature/PostProcess/ColorGrading/HDRColorGradingSettingsInterface.h>
|
||||
|
||||
#include <PostProcess/PostProcessBase.h>
|
||||
#include <ACES/Aces.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
@@ -32,33 +32,39 @@
|
||||
{
|
||||
FullscreenTrianglePass::InitializeInternal();
|
||||
|
||||
m_colorAdjustmentWeightIndex.Reset();
|
||||
m_colorGradingExposureIndex.Reset();
|
||||
m_colorGradingContrastIndex.Reset();
|
||||
m_colorGradingHueShiftIndex.Reset();
|
||||
m_colorGradingPreSaturationIndex.Reset();
|
||||
m_colorFilterIntensityIndex.Reset();
|
||||
m_colorFilterMultiplyIndex.Reset();
|
||||
m_colorFilterSwatchIndex.Reset();
|
||||
|
||||
m_whiteBalanceWeightIndex.Reset();
|
||||
m_whiteBalanceKelvinIndex.Reset();
|
||||
m_whiteBalanceTintIndex.Reset();
|
||||
|
||||
m_splitToneBalanceIndex.Reset();
|
||||
m_splitToneWeightIndex.Reset();
|
||||
m_colorGradingPostSaturationIndex.Reset();
|
||||
m_splitToneShadowsColorIndex.Reset();
|
||||
m_splitToneHighlightsColorIndex.Reset();
|
||||
|
||||
m_smhShadowsStartIndex.Reset();
|
||||
m_smhShadowsEndIndex.Reset();
|
||||
m_smhHighlightsStartIndex.Reset();
|
||||
m_smhHighlightsEndIndex.Reset();
|
||||
m_smhWeightIndex.Reset();
|
||||
m_smhShadowsColorIndex.Reset();
|
||||
m_smhMidtonesColorIndex.Reset();
|
||||
m_smhHighlightsColorIndex.Reset();
|
||||
|
||||
m_channelMixingRedIndex.Reset();
|
||||
m_channelMixingGreenIndex.Reset();
|
||||
m_channelMixingBlueIndex.Reset();
|
||||
|
||||
m_colorFilterSwatchIndex.Reset();
|
||||
m_splitToneShadowsColorIndex.Reset();
|
||||
m_splitToneHighlightsColorIndex.Reset();
|
||||
m_smhShadowsColorIndex.Reset();
|
||||
m_smhMidtonesColorIndex.Reset();
|
||||
m_smhHighlightsColorIndex.Reset();
|
||||
m_finalAdjustmentWeightIndex.Reset();
|
||||
m_colorGradingPostSaturationIndex.Reset();
|
||||
m_colorGradingHueShiftIndex.Reset();
|
||||
}
|
||||
|
||||
void HDRColorGradingPass::FrameBeginInternal(FramePrepareParams params)
|
||||
@@ -79,33 +85,39 @@
|
||||
const HDRColorGradingSettings* settings = GetHDRColorGradingSettings();
|
||||
if (settings)
|
||||
{
|
||||
m_shaderResourceGroup->SetConstant(m_colorAdjustmentWeightIndex, settings->GetColorAdjustmentWeight());
|
||||
m_shaderResourceGroup->SetConstant(m_colorGradingExposureIndex, settings->GetColorGradingExposure());
|
||||
m_shaderResourceGroup->SetConstant(m_colorGradingContrastIndex, settings->GetColorGradingContrast());
|
||||
m_shaderResourceGroup->SetConstant(m_colorGradingHueShiftIndex, settings->GetColorGradingHueShift());
|
||||
m_shaderResourceGroup->SetConstant(m_colorGradingPreSaturationIndex, settings->GetColorGradingPreSaturation());
|
||||
m_shaderResourceGroup->SetConstant(m_colorGradingPreSaturationIndex, settings->GetColorGradingPreSaturation() * 0.01f + 1.0f);
|
||||
m_shaderResourceGroup->SetConstant(m_colorFilterIntensityIndex, settings->GetColorGradingFilterIntensity());
|
||||
m_shaderResourceGroup->SetConstant(m_colorFilterMultiplyIndex, settings->GetColorGradingFilterMultiply());
|
||||
m_shaderResourceGroup->SetConstant(m_colorFilterSwatchIndex, AZ::Vector4(settings->GetColorFilterSwatch()));
|
||||
|
||||
m_shaderResourceGroup->SetConstant(m_whiteBalanceWeightIndex, settings->GetWhiteBalanceWeight());
|
||||
m_shaderResourceGroup->SetConstant(m_whiteBalanceKelvinIndex, settings->GetWhiteBalanceKelvin());
|
||||
m_shaderResourceGroup->SetConstant(m_whiteBalanceTintIndex, settings->GetWhiteBalanceTint());
|
||||
|
||||
m_shaderResourceGroup->SetConstant(m_splitToneBalanceIndex, settings->GetSplitToneBalance());
|
||||
m_shaderResourceGroup->SetConstant(m_splitToneWeightIndex, settings->GetSplitToneWeight());
|
||||
m_shaderResourceGroup->SetConstant(m_colorGradingPostSaturationIndex, settings->GetColorGradingPostSaturation());
|
||||
m_shaderResourceGroup->SetConstant(m_splitToneShadowsColorIndex, AZ::Vector4(settings->GetSplitToneShadowsColor()));
|
||||
m_shaderResourceGroup->SetConstant(m_splitToneHighlightsColorIndex, AZ::Vector4(settings->GetSplitToneHighlightsColor()));
|
||||
|
||||
m_shaderResourceGroup->SetConstant(m_smhShadowsStartIndex, settings->GetSmhShadowsStart());
|
||||
m_shaderResourceGroup->SetConstant(m_smhShadowsEndIndex, settings->GetSmhShadowsEnd());
|
||||
m_shaderResourceGroup->SetConstant(m_smhHighlightsStartIndex, settings->GetSmhHighlightsStart());
|
||||
m_shaderResourceGroup->SetConstant(m_smhHighlightsEndIndex, settings->GetSmhHighlightsEnd());
|
||||
m_shaderResourceGroup->SetConstant(m_smhWeightIndex, settings->GetSmhWeight());
|
||||
m_shaderResourceGroup->SetConstant(m_smhShadowsColorIndex, AZ::Vector4(settings->GetSmhShadowsColor()));
|
||||
m_shaderResourceGroup->SetConstant(m_smhMidtonesColorIndex, AZ::Vector4(settings->GetSmhMidtonesColor()));
|
||||
m_shaderResourceGroup->SetConstant(m_smhHighlightsColorIndex, AZ::Vector4(settings->GetSmhHighlightsColor()));
|
||||
|
||||
m_shaderResourceGroup->SetConstant(m_channelMixingRedIndex, settings->GetChannelMixingRed());
|
||||
m_shaderResourceGroup->SetConstant(m_channelMixingGreenIndex, settings->GetChannelMixingGreen());
|
||||
m_shaderResourceGroup->SetConstant(m_channelMixingBlueIndex, settings->GetChannelMixingBlue());
|
||||
|
||||
m_shaderResourceGroup->SetConstant(m_colorFilterSwatchIndex, AZ::Vector4(settings->GetColorFilterSwatch()));
|
||||
m_shaderResourceGroup->SetConstant(m_splitToneShadowsColorIndex, AZ::Vector4(settings->GetSplitToneShadowsColor()));
|
||||
m_shaderResourceGroup->SetConstant(m_splitToneHighlightsColorIndex, AZ::Vector4(settings->GetSplitToneHighlightsColor()));
|
||||
m_shaderResourceGroup->SetConstant(m_smhShadowsColorIndex, AZ::Vector4(settings->GetSmhShadowsColor()));
|
||||
m_shaderResourceGroup->SetConstant(m_smhMidtonesColorIndex, AZ::Vector4(settings->GetSmhMidtonesColor()));
|
||||
m_shaderResourceGroup->SetConstant(m_smhHighlightsColorIndex, AZ::Vector4(settings->GetSmhHighlightsColor()));
|
||||
m_shaderResourceGroup->SetConstant(m_finalAdjustmentWeightIndex, settings->GetFinalAdjustmentWeight());
|
||||
m_shaderResourceGroup->SetConstant(m_colorGradingPostSaturationIndex, settings->GetColorGradingPostSaturation() * 0.01f + 1.0f);
|
||||
m_shaderResourceGroup->SetConstant(m_colorGradingHueShiftIndex, settings->GetColorGradingHueShift());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,37 +41,42 @@ namespace AZ
|
||||
void FrameBeginInternal(FramePrepareParams params) override;
|
||||
bool IsEnabled() const override;
|
||||
|
||||
private:
|
||||
const HDRColorGradingSettings* GetHDRColorGradingSettings() const;
|
||||
void SetSrgConstants();
|
||||
|
||||
virtual void SetSrgConstants();
|
||||
private:
|
||||
RHI::ShaderInputNameIndex m_colorAdjustmentWeightIndex = "m_colorAdjustmentWeight";
|
||||
RHI::ShaderInputNameIndex m_colorGradingExposureIndex = "m_colorGradingExposure";
|
||||
RHI::ShaderInputNameIndex m_colorGradingContrastIndex = "m_colorGradingContrast";
|
||||
RHI::ShaderInputNameIndex m_colorGradingHueShiftIndex = "m_colorGradingHueShift";
|
||||
RHI::ShaderInputNameIndex m_colorGradingPreSaturationIndex = "m_colorGradingPreSaturation";
|
||||
RHI::ShaderInputNameIndex m_colorFilterIntensityIndex = "m_colorFilterIntensity";
|
||||
RHI::ShaderInputNameIndex m_colorFilterMultiplyIndex = "m_colorFilterMultiply";
|
||||
RHI::ShaderInputNameIndex m_colorFilterSwatchIndex = "m_colorFilterSwatch";
|
||||
|
||||
RHI::ShaderInputNameIndex m_whiteBalanceWeightIndex = "m_whiteBalanceWeight";
|
||||
RHI::ShaderInputNameIndex m_whiteBalanceKelvinIndex = "m_whiteBalanceKelvin";
|
||||
RHI::ShaderInputNameIndex m_whiteBalanceTintIndex = "m_whiteBalanceTint";
|
||||
|
||||
RHI::ShaderInputNameIndex m_splitToneBalanceIndex = "m_splitToneBalance";
|
||||
RHI::ShaderInputNameIndex m_splitToneWeightIndex = "m_splitToneWeight";
|
||||
RHI::ShaderInputNameIndex m_colorGradingPostSaturationIndex = "m_colorGradingPostSaturation";
|
||||
RHI::ShaderInputNameIndex m_splitToneShadowsColorIndex = "m_splitToneShadowsColor";
|
||||
RHI::ShaderInputNameIndex m_splitToneHighlightsColorIndex = "m_splitToneHighlightsColor";
|
||||
|
||||
RHI::ShaderInputNameIndex m_smhShadowsStartIndex = "m_smhShadowsStart";
|
||||
RHI::ShaderInputNameIndex m_smhShadowsEndIndex = "m_smhShadowsEnd";
|
||||
RHI::ShaderInputNameIndex m_smhHighlightsStartIndex = "m_smhHighlightsStart";
|
||||
RHI::ShaderInputNameIndex m_smhHighlightsEndIndex = "m_smhHighlightsEnd";
|
||||
RHI::ShaderInputNameIndex m_smhWeightIndex = "m_smhWeight";
|
||||
RHI::ShaderInputNameIndex m_smhShadowsColorIndex = "m_smhShadowsColor";
|
||||
RHI::ShaderInputNameIndex m_smhMidtonesColorIndex = "m_smhMidtonesColor";
|
||||
RHI::ShaderInputNameIndex m_smhHighlightsColorIndex = "m_smhHighlightsColor";
|
||||
|
||||
RHI::ShaderInputNameIndex m_channelMixingRedIndex = "m_channelMixingRed";
|
||||
RHI::ShaderInputNameIndex m_channelMixingGreenIndex = "m_channelMixingGreen";
|
||||
RHI::ShaderInputNameIndex m_channelMixingBlueIndex = "m_channelMixingBlue";
|
||||
|
||||
RHI::ShaderInputNameIndex m_colorFilterSwatchIndex = "m_colorFilterSwatch";
|
||||
RHI::ShaderInputNameIndex m_splitToneShadowsColorIndex = "m_splitToneShadowsColor";
|
||||
RHI::ShaderInputNameIndex m_splitToneHighlightsColorIndex = "m_splitToneHighlightsColor";
|
||||
RHI::ShaderInputNameIndex m_smhShadowsColorIndex = "m_smhShadowsColor";
|
||||
RHI::ShaderInputNameIndex m_smhMidtonesColorIndex = "m_smhMidtonesColor";
|
||||
RHI::ShaderInputNameIndex m_smhHighlightsColorIndex = "m_smhHighlightsColor";
|
||||
RHI::ShaderInputNameIndex m_finalAdjustmentWeightIndex = "m_finalAdjustmentWeight";
|
||||
RHI::ShaderInputNameIndex m_colorGradingPostSaturationIndex = "m_colorGradingPostSaturation";
|
||||
RHI::ShaderInputNameIndex m_colorGradingHueShiftIndex = "m_colorGradingHueShift";
|
||||
};
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "ProfilingCaptureSystemComponent.h"
|
||||
|
||||
#include <Atom/RHI/CpuProfilerImpl.h>
|
||||
#include <Atom/RHI/RHIUtils.h>
|
||||
#include <Atom/RHI/RHISystemInterface.h>
|
||||
#include <AzCore/Statistics/RunningStatistic.h>
|
||||
@@ -39,7 +38,6 @@ namespace AZ
|
||||
OnCaptureQueryTimestampFinished,
|
||||
OnCaptureCpuFrameTimeFinished,
|
||||
OnCaptureQueryPipelineStatisticsFinished,
|
||||
OnCaptureCpuProfilingStatisticsFinished,
|
||||
OnCaptureBenchmarkMetadataFinished
|
||||
);
|
||||
|
||||
@@ -58,11 +56,6 @@ namespace AZ
|
||||
Call(FN_OnCaptureQueryPipelineStatisticsFinished, result, info);
|
||||
}
|
||||
|
||||
void OnCaptureCpuProfilingStatisticsFinished(bool result, const AZStd::string& info) override
|
||||
{
|
||||
Call(FN_OnCaptureCpuProfilingStatisticsFinished, result, info);
|
||||
}
|
||||
|
||||
void OnCaptureBenchmarkMetadataFinished(bool result, const AZStd::string& info) override
|
||||
{
|
||||
Call(FN_OnCaptureBenchmarkMetadataFinished, result, info);
|
||||
@@ -358,7 +351,6 @@ namespace AZ
|
||||
->Event("CapturePassTimestamp", &ProfilingCaptureRequestBus::Events::CapturePassTimestamp)
|
||||
->Event("CaptureCpuFrameTime", &ProfilingCaptureRequestBus::Events::CaptureCpuFrameTime)
|
||||
->Event("CapturePassPipelineStatistics", &ProfilingCaptureRequestBus::Events::CapturePassPipelineStatistics)
|
||||
->Event("CaptureCpuProfilingStatistics", &ProfilingCaptureRequestBus::Events::CaptureCpuProfilingStatistics)
|
||||
->Event("CaptureBenchmarkMetadata", &ProfilingCaptureRequestBus::Events::CaptureBenchmarkMetadata)
|
||||
;
|
||||
|
||||
@@ -368,7 +360,6 @@ namespace AZ
|
||||
TimestampSerializer::Reflect(context);
|
||||
CpuFrameTimeSerializer::Reflect(context);
|
||||
PipelineStatisticsSerializer::Reflect(context);
|
||||
RHI::CpuProfilingStatisticsSerializer::Reflect(context);
|
||||
BenchmarkMetadataSerializer::Reflect(context);
|
||||
}
|
||||
|
||||
@@ -382,12 +373,6 @@ namespace AZ
|
||||
TickBus::Handler::BusDisconnect();
|
||||
|
||||
ProfilingCaptureRequestBus::Handler::BusDisconnect();
|
||||
|
||||
// Block deactivation until the IO thread has finished serializing the CPU data
|
||||
if (m_cpuDataSerializationThread.joinable())
|
||||
{
|
||||
m_cpuDataSerializationThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
bool ProfilingCaptureSystemComponent::CapturePassTimestamp(const AZStd::string& outputFilePath)
|
||||
@@ -442,16 +427,7 @@ namespace AZ
|
||||
|
||||
bool ProfilingCaptureSystemComponent::CaptureCpuFrameTime(const AZStd::string& outputFilePath)
|
||||
{
|
||||
AZ::RHI::RHISystemInterface::Get()->ModifyFrameSchedulerStatisticsFlags(
|
||||
AZ::RHI::FrameSchedulerStatisticsFlags::GatherCpuTimingStatistics, true
|
||||
);
|
||||
bool wasEnabled = RHI::CpuProfiler::Get()->IsProfilerEnabled();
|
||||
if (!wasEnabled)
|
||||
{
|
||||
RHI::CpuProfiler::Get()->SetProfilerEnabled(true);
|
||||
}
|
||||
|
||||
const bool captureStarted = m_cpuFrameTimeStatisticsCapture.StartCapture([outputFilePath, wasEnabled]()
|
||||
const bool captureStarted = m_cpuFrameTimeStatisticsCapture.StartCapture([outputFilePath]()
|
||||
{
|
||||
JsonSerializerSettings serializationSettings;
|
||||
serializationSettings.m_keepDefaults = true;
|
||||
@@ -472,15 +448,6 @@ namespace AZ
|
||||
AZ_Warning("ProfilingCaptureSystemComponent", false, captureInfo.c_str());
|
||||
}
|
||||
|
||||
// Disable the profiler again
|
||||
if (!wasEnabled)
|
||||
{
|
||||
RHI::CpuProfiler::Get()->SetProfilerEnabled(false);
|
||||
}
|
||||
AZ::RHI::RHISystemInterface::Get()->ModifyFrameSchedulerStatisticsFlags(
|
||||
AZ::RHI::FrameSchedulerStatisticsFlags::GatherCpuTimingStatistics, false
|
||||
);
|
||||
|
||||
// Notify listeners that the Cpu frame time statistics capture has finished.
|
||||
ProfilingCaptureNotificationBus::Broadcast(&ProfilingCaptureNotificationBus::Events::OnCaptureCpuFrameTimeFinished,
|
||||
saveResult.IsSuccess(),
|
||||
@@ -546,116 +513,6 @@ namespace AZ
|
||||
return captureStarted;
|
||||
}
|
||||
|
||||
bool SerializeCpuProfilingData(const AZStd::ring_buffer<RHI::CpuProfiler::TimeRegionMap>& data, AZStd::string outputFilePath, bool wasEnabled)
|
||||
{
|
||||
AZ_TracePrintf("ProfilingCaptureSystemComponent", "Beginning serialization of %zu frames of profiling data\n", data.size());
|
||||
JsonSerializerSettings serializationSettings;
|
||||
serializationSettings.m_keepDefaults = true;
|
||||
|
||||
RHI::CpuProfilingStatisticsSerializer serializer(data);
|
||||
|
||||
const auto saveResult = JsonSerializationUtils::SaveObjectToFile(&serializer,
|
||||
outputFilePath, (RHI::CpuProfilingStatisticsSerializer*)nullptr, &serializationSettings);
|
||||
|
||||
AZStd::string captureInfo = outputFilePath;
|
||||
if (!saveResult.IsSuccess())
|
||||
{
|
||||
captureInfo = AZStd::string::format("Failed to save Cpu Profiling Statistics data to file '%s'. Error: %s",
|
||||
outputFilePath.c_str(),
|
||||
saveResult.GetError().c_str());
|
||||
AZ_Warning("ProfilingCaptureSystemComponent", false, captureInfo.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Printf("ProfilingCaptureSystemComponent", "Cpu profiling statistics was saved to file [%s]\n", outputFilePath.c_str());
|
||||
}
|
||||
|
||||
// Disable the profiler again
|
||||
if (!wasEnabled)
|
||||
{
|
||||
RHI::CpuProfiler::Get()->SetProfilerEnabled(false);
|
||||
}
|
||||
|
||||
// Notify listeners that the pass' PipelineStatistics queries capture has finished.
|
||||
ProfilingCaptureNotificationBus::Broadcast(&ProfilingCaptureNotificationBus::Events::OnCaptureCpuProfilingStatisticsFinished,
|
||||
saveResult.IsSuccess(),
|
||||
captureInfo);
|
||||
return saveResult.IsSuccess();
|
||||
}
|
||||
|
||||
bool ProfilingCaptureSystemComponent::CaptureCpuProfilingStatistics(const AZStd::string& outputFilePath)
|
||||
{
|
||||
// Start the cpu profiling
|
||||
bool wasEnabled = RHI::CpuProfiler::Get()->IsProfilerEnabled();
|
||||
if (!wasEnabled)
|
||||
{
|
||||
RHI::CpuProfiler::Get()->SetProfilerEnabled(true);
|
||||
}
|
||||
|
||||
const bool captureStarted = m_cpuProfilingStatisticsCapture.StartCapture([outputFilePath, wasEnabled]()
|
||||
{
|
||||
// Blocking call for a single frame of data, avoid thread overhead
|
||||
AZStd::ring_buffer<RHI::CpuProfiler::TimeRegionMap> singleFrameData(1);
|
||||
singleFrameData.push_back(RHI::CpuProfiler::Get()->GetTimeRegionMap());
|
||||
SerializeCpuProfilingData(singleFrameData, outputFilePath, wasEnabled);
|
||||
});
|
||||
|
||||
// Start the TickBus.
|
||||
if (captureStarted)
|
||||
{
|
||||
TickBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
return captureStarted;
|
||||
}
|
||||
|
||||
bool ProfilingCaptureSystemComponent::BeginContinuousCpuProfilingCapture()
|
||||
{
|
||||
return AZ::RHI::CpuProfiler::Get()->BeginContinuousCapture();
|
||||
}
|
||||
|
||||
bool ProfilingCaptureSystemComponent::EndContinuousCpuProfilingCapture(const AZStd::string& outputFilePath)
|
||||
{
|
||||
bool expected = false;
|
||||
if (m_cpuDataSerializationInProgress.compare_exchange_strong(expected, true))
|
||||
{
|
||||
AZStd::ring_buffer<RHI::CpuProfiler::TimeRegionMap> captureResult;
|
||||
const bool captureEnded = AZ::RHI::CpuProfiler::Get()->EndContinuousCapture(captureResult);
|
||||
if (!captureEnded)
|
||||
{
|
||||
AZ_TracePrintf("ProfilingCaptureSystemComponent", "Could not end the continuous capture, is one in progress?\n");
|
||||
m_cpuDataSerializationInProgress.store(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// cpuProfilingData could be 1GB+ once saved, so use an IO thread to write it to disk.
|
||||
auto threadIoFunction =
|
||||
[data = AZStd::move(captureResult), filePath = AZStd::string(outputFilePath), &flag = m_cpuDataSerializationInProgress]()
|
||||
{
|
||||
SerializeCpuProfilingData(data, filePath, true);
|
||||
flag.store(false);
|
||||
};
|
||||
|
||||
// If the thread object already exists (ex. we have already serialized data), join. This will not block since
|
||||
// m_cpuDataSerializationInProgress was false, meaning the IO thread has already completed execution.
|
||||
// TODO Use a reusable thread implementation over repeated creation + destruction of threads [ATOM-16214]
|
||||
if (m_cpuDataSerializationThread.joinable())
|
||||
{
|
||||
m_cpuDataSerializationThread.join();
|
||||
}
|
||||
|
||||
auto thread = AZStd::thread(threadIoFunction);
|
||||
m_cpuDataSerializationThread = AZStd::move(thread);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ_TracePrintf(
|
||||
"ProfilingSystemCaptureComponent",
|
||||
"Cannot end a continuous capture - another serialization is currently in progress\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProfilingCaptureSystemComponent::CaptureBenchmarkMetadata(const AZStd::string& benchmarkName, const AZStd::string& outputFilePath)
|
||||
{
|
||||
const bool captureStarted = m_benchmarkMetadataCapture.StartCapture([benchmarkName, outputFilePath]()
|
||||
@@ -734,11 +591,10 @@ namespace AZ
|
||||
m_timestampCapture.UpdateCapture();
|
||||
m_cpuFrameTimeStatisticsCapture.UpdateCapture();
|
||||
m_pipelineStatisticsCapture.UpdateCapture();
|
||||
m_cpuProfilingStatisticsCapture.UpdateCapture();
|
||||
m_benchmarkMetadataCapture.UpdateCapture();
|
||||
|
||||
// Disconnect from the TickBus if all capture states are set to idle.
|
||||
if (m_timestampCapture.IsIdle() && m_pipelineStatisticsCapture.IsIdle() && m_cpuProfilingStatisticsCapture.IsIdle() && m_benchmarkMetadataCapture.IsIdle() && m_cpuFrameTimeStatisticsCapture.IsIdle())
|
||||
if (m_timestampCapture.IsIdle() && m_pipelineStatisticsCapture.IsIdle() && m_benchmarkMetadataCapture.IsIdle() && m_cpuFrameTimeStatisticsCapture.IsIdle())
|
||||
{
|
||||
TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
@@ -70,9 +70,6 @@ namespace AZ
|
||||
bool CapturePassTimestamp(const AZStd::string& outputFilePath) override;
|
||||
bool CaptureCpuFrameTime(const AZStd::string& outputFilePath) override;
|
||||
bool CapturePassPipelineStatistics(const AZStd::string& outputFilePath) override;
|
||||
bool CaptureCpuProfilingStatistics(const AZStd::string& outputFilePath) override;
|
||||
bool BeginContinuousCpuProfilingCapture() override;
|
||||
bool EndContinuousCpuProfilingCapture(const AZStd::string& outputFilePath) override;
|
||||
bool CaptureBenchmarkMetadata(const AZStd::string& benchmarkName, const AZStd::string& outputFilePath) override;
|
||||
|
||||
private:
|
||||
@@ -86,13 +83,7 @@ namespace AZ
|
||||
DelayedQueryCaptureHelper m_timestampCapture;
|
||||
DelayedQueryCaptureHelper m_cpuFrameTimeStatisticsCapture;
|
||||
DelayedQueryCaptureHelper m_pipelineStatisticsCapture;
|
||||
DelayedQueryCaptureHelper m_cpuProfilingStatisticsCapture;
|
||||
DelayedQueryCaptureHelper m_benchmarkMetadataCapture;
|
||||
|
||||
// Flag passed by reference to the CPU profiling data serialization job, blocks new continuous capture requests when set.
|
||||
AZStd::atomic_bool m_cpuDataSerializationInProgress = false;
|
||||
|
||||
AZStd::thread m_cpuDataSerializationThread;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ set(FILES
|
||||
Include/Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h
|
||||
Include/Atom/Feature/Automation/AtomAutomationBus.h
|
||||
Include/Atom/Feature/AuxGeom/AuxGeomFeatureProcessor.h
|
||||
Include/Atom/Feature/ColorGrading/LutResolution.h
|
||||
Include/Atom/Feature/CoreLights/CoreLightsConstants.h
|
||||
Include/Atom/Feature/DisplayMapper/AcesOutputTransformPass.h
|
||||
Include/Atom/Feature/DisplayMapper/AcesOutputTransformLutPass.h
|
||||
@@ -66,6 +67,8 @@ set(FILES
|
||||
Source/AuxGeom/DynamicPrimitiveProcessor.h
|
||||
Source/AuxGeom/FixedShapeProcessor.cpp
|
||||
Source/AuxGeom/FixedShapeProcessor.h
|
||||
Source/ColorGrading/LutGenerationPass.cpp
|
||||
Source/ColorGrading/LutGenerationPass.h
|
||||
Source/CoreLights/CapsuleLightFeatureProcessor.h
|
||||
Source/CoreLights/CapsuleLightFeatureProcessor.cpp
|
||||
Source/CoreLights/CascadedShadowmapsPass.h
|
||||
@@ -212,8 +215,6 @@ set(FILES
|
||||
Source/PostProcessing/BloomCompositePass.cpp
|
||||
Source/PostProcessing/BloomParentPass.h
|
||||
Source/PostProcessing/BloomParentPass.cpp
|
||||
Source/PostProcessing/HDRColorGradingPass.cpp
|
||||
Source/PostProcessing/HDRColorGradingPass.h
|
||||
Source/PostProcessing/DepthOfFieldCompositePass.h
|
||||
Source/PostProcessing/DepthOfFieldCompositePass.cpp
|
||||
Source/PostProcessing/DepthOfFieldBokehBlurPass.h
|
||||
@@ -234,6 +235,8 @@ set(FILES
|
||||
Source/PostProcessing/EyeAdaptationPass.h
|
||||
Source/PostProcessing/FastDepthAwareBlurPasses.cpp
|
||||
Source/PostProcessing/FastDepthAwareBlurPasses.h
|
||||
Source/PostProcessing/HDRColorGradingPass.cpp
|
||||
Source/PostProcessing/HDRColorGradingPass.h
|
||||
Source/PostProcessing/LookModificationCompositePass.cpp
|
||||
Source/PostProcessing/LookModificationCompositePass.h
|
||||
Source/PostProcessing/LookModificationTransformPass.cpp
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
"""
|
||||
This script is used by the Editor HDR Color Grading component to use a stored lut asset path
|
||||
and pass it onto a Look Modification Component.
|
||||
|
||||
The HDR Color Grading component will be disabled as it is not compatible with the Look
|
||||
Modification Component.
|
||||
"""
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
LOOK_MODIFICATION_LUT_PROPERTY_PATH = 'Controller|Configuration|Color Grading LUT'
|
||||
LOOK_MODIFICATION_ENABLE_PROPERTY_PATH = 'Controller|Configuration|Enable look modification'
|
||||
COLOR_GRADING_COMPONENT_ID = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["HDR Color Grading"], 0)
|
||||
LOOK_MODIFICATION_COMPONENT_ID = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Look Modification"], 0)
|
||||
|
||||
|
||||
def disable_hdr_color_grading_component(entity_id):
|
||||
componentOutcome = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'GetComponentOfType', entity_id, COLOR_GRADING_COMPONENT_ID[0])
|
||||
if(componentOutcome.IsSuccess()):
|
||||
azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'DisableComponents', [componentOutcome.GetValue()])
|
||||
|
||||
def add_look_modification_component(entity_id):
|
||||
componentOutcome = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'AddComponentsOfType', entity_id, LOOK_MODIFICATION_COMPONENT_ID)
|
||||
return componentOutcome.GetValue()[0]
|
||||
|
||||
def get_look_modification_component(entity_id):
|
||||
componentOutcome = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'GetComponentOfType', entity_id, LOOK_MODIFICATION_COMPONENT_ID[0])
|
||||
if componentOutcome.IsSuccess():
|
||||
return componentOutcome.GetValue()
|
||||
else:
|
||||
return None
|
||||
|
||||
def activate_look_modification_lut(look_modification_component, asset_relative_path):
|
||||
print(asset_relative_path)
|
||||
asset_id = azlmbr.asset.AssetCatalogRequestBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
'GetAssetIdByPath',
|
||||
asset_relative_path,
|
||||
azlmbr.math.Uuid(),
|
||||
False
|
||||
)
|
||||
azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
'SetComponentProperty',
|
||||
look_modification_component,
|
||||
LOOK_MODIFICATION_LUT_PROPERTY_PATH,
|
||||
asset_id
|
||||
)
|
||||
azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
'SetComponentProperty',
|
||||
look_modification_component,
|
||||
LOOK_MODIFICATION_ENABLE_PROPERTY_PATH,
|
||||
True
|
||||
)
|
||||
|
||||
def activate_lut_asset(entity_id, asset_relative_path):
|
||||
disable_hdr_color_grading_component(entity_id)
|
||||
|
||||
look_modification_component = get_look_modification_component(entity_id)
|
||||
if not look_modification_component:
|
||||
look_modification_component = add_look_modification_component(entity_id)
|
||||
|
||||
general.idle_wait_frames(5)
|
||||
|
||||
if look_modification_component:
|
||||
activate_look_modification_lut(look_modification_component, asset_relative_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser=argparse.ArgumentParser()
|
||||
parser.add_argument('--entityName', type=str, required=True, help='Entity ID to manage')
|
||||
parser.add_argument('--assetRelativePath', type=str, required=True, help='Lut asset relative path to activate')
|
||||
args=parser.parse_args()
|
||||
|
||||
# Get the entity id
|
||||
searchFilter = azlmbr.entity.SearchFilter()
|
||||
searchFilter.names = [args.entityName]
|
||||
entityIdList = azlmbr.entity.SearchBus(azlmbr.bus.Broadcast, 'SearchEntities', searchFilter)
|
||||
|
||||
for entityId in entityIdList:
|
||||
activate_lut_asset(entityId, args.assetRelativePath)
|
||||
@@ -0,0 +1,63 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
import numpy as np
|
||||
import logging as _logging
|
||||
from ColorGrading import get_uv_coord
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
_MODULENAME = 'ColorGrading.azasset_converter_utils'
|
||||
|
||||
_LOGGER = _logging.getLogger(_MODULENAME)
|
||||
_LOGGER.debug('Initializing: {0}.'.format({_MODULENAME}))
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
""" Utility functions for generating LUT azassets """
|
||||
def generate_lut_values(image_spec, image_buffer):
|
||||
lut_size = image_spec.height
|
||||
|
||||
lut_intervals = []
|
||||
lut_values = []
|
||||
|
||||
# First line contains the vertex intervals
|
||||
dv = 1023.0 / float(lut_size-1)
|
||||
for i in range(lut_size):
|
||||
lut_intervals.append(np.uint16(dv * i))
|
||||
# Texels are in R G B per line with indices increasing first with blue, then green, and then red.
|
||||
for r in range(lut_size):
|
||||
for g in range(lut_size):
|
||||
for b in range(lut_size):
|
||||
uv = get_uv_coord(lut_size, r, g, b)
|
||||
px = np.array(image_buffer.getpixel(uv[0], uv[1]), dtype='f')
|
||||
px = np.clip(px, 0.0, 1.0)
|
||||
px = np.uint16(px * 4095)
|
||||
lut_values.append(px)
|
||||
|
||||
return lut_intervals, lut_values
|
||||
|
||||
# To Do: add some input file validation
|
||||
# If the input file doesn't exist, you'll get a LUT with res of 0 x 0 and result in a math error
|
||||
#Resolution is 0 x 0
|
||||
#writing C:\Depot\o3de-engine\Gems\AtomLyIntegration\CommonFeatures\Tools\ColorGrading\TestData\Nuke\HDR\Nuke_Post_grade_LUT.3dl...
|
||||
#Traceback (most recent call last):
|
||||
#File "..\..\Editor\Scripts\ColorGrading\exr_to_3dl_azasset.py", line 103, in <module>
|
||||
#dv = 1023.0 / float(lutSize)
|
||||
# ZeroDivisionError: float division by zero
|
||||
|
||||
def write_3DL(file_path, lut_size, lut_intervals, lut_values):
|
||||
lut_file_path = f'{file_path}.3dl'
|
||||
_LOGGER.info(f"Writing {lut_file_path}...")
|
||||
lut_file = open(lut_file_path, 'w')
|
||||
for i in range(lut_size):
|
||||
lut_file.write(f"{lut_intervals[i]} ")
|
||||
lut_file.write("\n")
|
||||
for px in lut_values:
|
||||
lut_file.write(f"{px[0]} {px[1]} {px[2]}\n")
|
||||
lut_file.close()
|
||||
|
||||
@@ -46,48 +46,7 @@ from ColorGrading.from_3dl_to_azasset import write_azasset
|
||||
|
||||
from ColorGrading import get_uv_coord
|
||||
|
||||
def generate_lut_values(image_spec, image_buffer):
|
||||
lut_size = image_spec.height
|
||||
|
||||
lut_intervals = []
|
||||
lut_values = []
|
||||
|
||||
# First line contains the vertex intervals
|
||||
dv = 1023.0 / float(lut_size-1)
|
||||
for i in range(lut_size):
|
||||
lut_intervals.append(np.uint16(dv * i))
|
||||
# Texels are in R G B per line with indices increasing first with blue, then green, and then red.
|
||||
for r in range(lut_size):
|
||||
for g in range(lut_size):
|
||||
for b in range(lut_size):
|
||||
uv = get_uv_coord(lut_size, r, g, b)
|
||||
px = np.array(image_buffer.getpixel(uv[0], uv[1]), dtype='f')
|
||||
px = np.clip(px, 0.0, 1.0)
|
||||
px = np.uint16(px * 4095)
|
||||
lut_values.append(px)
|
||||
|
||||
return lut_intervals, lut_values
|
||||
|
||||
# To Do: add some input file validation
|
||||
# If the input file doesn't exist, you'll get a LUT with res of 0 x 0 and result in a math error
|
||||
#Resolution is 0 x 0
|
||||
#writing C:\Depot\o3de-engine\Gems\AtomLyIntegration\CommonFeatures\Tools\ColorGrading\TestData\Nuke\HDR\Nuke_Post_grade_LUT.3dl...
|
||||
#Traceback (most recent call last):
|
||||
#File "..\..\Editor\Scripts\ColorGrading\exr_to_3dl_azasset.py", line 103, in <module>
|
||||
#dv = 1023.0 / float(lutSize)
|
||||
# ZeroDivisionError: float division by zero
|
||||
|
||||
def write_3DL(file_path, lut_size, lut_intervals, lut_values):
|
||||
lut_file_path = f'{file_path}.3dl'
|
||||
_LOGGER.info(f"Writing {lut_file_path}...")
|
||||
lut_file = open(lut_file_path, 'w')
|
||||
for i in range(lut_size):
|
||||
lut_file.write(f"{lut_intervals[i]} ")
|
||||
lut_file.write("\n")
|
||||
for px in lut_values:
|
||||
lut_file.write(f"{px[0]} {px[1]} {px[2]}\n")
|
||||
lut_file.close()
|
||||
|
||||
from ColorGrading.azasset_converter_utils import generate_lut_values, write_3DL
|
||||
|
||||
###########################################################################
|
||||
# Main Code Block, runs this script as main (testing)
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# coding:utf-8
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
"""
|
||||
input: a shaped .tiff representing a LUT (for instance coming out of photoshop)
|
||||
output: a inverse shaped LUT as .tiff
|
||||
^ as a .3DL (normalized lut file type)
|
||||
^ as a .azasset (for o3de engine)
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
import math
|
||||
import site
|
||||
import pathlib
|
||||
from pathlib import Path
|
||||
import logging as _logging
|
||||
import numpy as np
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
_MODULENAME = 'ColorGrading.tiff_to_3dl_azasset'
|
||||
|
||||
_LOGGER = _logging.getLogger(_MODULENAME)
|
||||
_LOGGER.debug('Initializing: {0}.'.format({_MODULENAME}))
|
||||
|
||||
import ColorGrading.initialize
|
||||
if ColorGrading.initialize.start():
|
||||
try:
|
||||
import OpenImageIO as oiio
|
||||
pass
|
||||
except ImportError as e:
|
||||
_LOGGER.error(f"invalid import: {e}")
|
||||
sys.exit(1)
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
from ColorGrading.from_3dl_to_azasset import write_azasset
|
||||
|
||||
from ColorGrading.azasset_converter_utils import generate_lut_values, write_3DL
|
||||
|
||||
###########################################################################
|
||||
# Main Code Block, runs this script as main (testing)
|
||||
# -------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
"""Run this file as main"""
|
||||
|
||||
parser=argparse.ArgumentParser()
|
||||
parser.add_argument('--i', type=str, required=True, help='input file')
|
||||
parser.add_argument('--o', type=str, required=True, help='output file')
|
||||
args=parser.parse_args()
|
||||
|
||||
# Read input image
|
||||
image_buffer=oiio.ImageBuf(args.i)
|
||||
image_spec=image_buffer.spec()
|
||||
|
||||
#img = oiio.ImageInput.open(args.i)
|
||||
#_LOGGER.info(f"Resolution is, x: {img.spec().width} and y: {img.spec().height}")
|
||||
|
||||
_LOGGER.info(f"Resolution is, x: {image_buffer.spec().width} and y: {image_buffer.spec().height}")
|
||||
|
||||
if image_spec.width != image_spec.height * image_spec.height:
|
||||
_LOGGER.info(f"invalid input file dimensions. Expect lengthwise LUT with dimension W: s*s X H: s, where s is the size of the LUT")
|
||||
sys.exit(1)
|
||||
|
||||
lut_intervals, lut_values = generate_lut_values(image_spec, image_buffer)
|
||||
|
||||
write_3DL(args.o, image_spec.height, lut_intervals, lut_values)
|
||||
|
||||
# write_azasset(file_path, lut_intervals, lut_values, azasset_json=AZASSET_LUT)
|
||||
write_azasset(args.o, lut_intervals, lut_values)
|
||||
|
||||
# example from command line
|
||||
# python % DCCSI_COLORGRADING_SCRIPTS %\lut_helper.py - -i C: \Depot\o3de\Gems\Atom\Feature\Common\Tools\ColorGrading\Resources\LUTs\linear_32_LUT.tiff - -op pre - grading - -shaper Log2 - 48nits - -o C: \Depot\o3de\Gems\Atom\Feature\Common\Tools\ColorGrading\Resources\LUTs\base_Log2-48nits_32_LUT.exr
|
||||
@@ -0,0 +1,16 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Scripts/ColorGrading/__init__.py
|
||||
Scripts/ColorGrading/initialize.py
|
||||
Scripts/ColorGrading/azasset_converter_utils.py
|
||||
Scripts/ColorGrading/from_3dl_to_azasset.py
|
||||
Scripts/ColorGrading/tiff_to_3dl_azasset.py
|
||||
Scripts/ColorGrading/activate_lut_asset.py
|
||||
)
|
||||
@@ -66,9 +66,6 @@ namespace AZ
|
||||
{
|
||||
None = 0,
|
||||
|
||||
//! Enables gathering of cpu timing statistics.
|
||||
GatherCpuTimingStatistics = AZ_BIT(0),
|
||||
|
||||
//! Enables gathering of transient attachment statistics.
|
||||
GatherTransientAttachmentStatistics = AZ_BIT(2),
|
||||
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Debug/EventTrace.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/std/containers/ring_buffer.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RHI
|
||||
{
|
||||
//! Structure that is used to cache a timed region into the thread's local storage.
|
||||
struct CachedTimeRegion
|
||||
{
|
||||
//! Structure used internally for caching assumed global string pointers (ideally literals) to the marker group/region
|
||||
//! NOTE: When used in a separate shared library, the library mustn't be unloaded before the CpuProfiler is shutdown.
|
||||
struct GroupRegionName
|
||||
{
|
||||
GroupRegionName() = delete;
|
||||
GroupRegionName(const char* const group, const char* const region);
|
||||
|
||||
const char* m_groupName = nullptr;
|
||||
const char* m_regionName = nullptr;
|
||||
|
||||
struct Hash
|
||||
{
|
||||
AZStd::size_t operator()(const GroupRegionName& name) const;
|
||||
};
|
||||
bool operator==(const GroupRegionName& other) const;
|
||||
};
|
||||
|
||||
CachedTimeRegion() = default;
|
||||
CachedTimeRegion(const GroupRegionName& groupRegionName);
|
||||
CachedTimeRegion(const GroupRegionName& groupRegionName, uint16_t stackDepth, uint64_t startTick, uint64_t endTick);
|
||||
|
||||
GroupRegionName m_groupRegionName{nullptr, nullptr};
|
||||
|
||||
uint16_t m_stackDepth = 0u;
|
||||
AZStd::sys_time_t m_startTick = 0;
|
||||
AZStd::sys_time_t m_endTick = 0;
|
||||
};
|
||||
|
||||
//! Interface class of the CpuProfiler
|
||||
class CpuProfiler
|
||||
{
|
||||
public:
|
||||
using ThreadTimeRegionMap = AZStd::unordered_map<AZStd::string, AZStd::vector<CachedTimeRegion>>;
|
||||
using TimeRegionMap = AZStd::unordered_map<AZStd::thread_id, ThreadTimeRegionMap>;
|
||||
|
||||
AZ_RTTI(CpuProfiler, "{127C1D0B-BE05-4E18-A8F6-24F3EED2ECA6}");
|
||||
|
||||
CpuProfiler() = default;
|
||||
virtual ~CpuProfiler() = default;
|
||||
|
||||
AZ_DISABLE_COPY_MOVE(CpuProfiler);
|
||||
|
||||
static CpuProfiler* Get();
|
||||
|
||||
//! Get the last frame's TimeRegionMap
|
||||
virtual const TimeRegionMap& GetTimeRegionMap() const = 0;
|
||||
|
||||
//! Begin a continuous capture. Blocks the profiler from being toggled off until EndContinuousCapture is called.
|
||||
[[nodiscard]] virtual bool BeginContinuousCapture() = 0;
|
||||
|
||||
//! Flush the CPU Profiler's saved data into the passed ring buffer .
|
||||
[[nodiscard]] virtual bool EndContinuousCapture(AZStd::ring_buffer<TimeRegionMap>& flushTarget) = 0;
|
||||
|
||||
virtual bool IsContinuousCaptureInProgress() const = 0;
|
||||
|
||||
//! Enable/Disable the CpuProfiler
|
||||
virtual void SetProfilerEnabled(bool enabled) = 0;
|
||||
|
||||
virtual bool IsProfilerEnabled() const = 0 ;
|
||||
};
|
||||
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <Atom/RHI/CpuProfiler.h>
|
||||
#include <Atom/RHI.Reflect/Base.h>
|
||||
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzCore/std/containers/map.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
#include <AzCore/std/parallel/mutex.h>
|
||||
#include <AzCore/std/parallel/shared_mutex.h>
|
||||
#include <AzCore/std/smart_ptr/intrusive_refcount.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RHI
|
||||
{
|
||||
//! Thread local class to keep track of the thread's cached time regions.
|
||||
//! Each thread keeps track of its own time regions, which is communicated from the CpuProfilerImpl.
|
||||
//! The CpuProfilerImpl is able to request the cached time regions from the CpuTimingLocalStorage.
|
||||
class CpuTimingLocalStorage :
|
||||
public AZStd::intrusive_refcount<AZStd::atomic_uint>
|
||||
{
|
||||
friend class CpuProfilerImpl;
|
||||
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(CpuTimingLocalStorage, AZ::OSAllocator, 0);
|
||||
|
||||
CpuTimingLocalStorage();
|
||||
~CpuTimingLocalStorage();
|
||||
|
||||
private:
|
||||
// Maximum stack size
|
||||
static constexpr uint32_t TimeRegionStackSize = 2048u;
|
||||
|
||||
// Adds a region to the stack, gets called each time a region begins
|
||||
void RegionStackPushBack(CachedTimeRegion& timeRegion);
|
||||
|
||||
// Pops a region from the stack, gets called each time a region ends
|
||||
void RegionStackPopBack();
|
||||
|
||||
// Add a new cached time region. If the stack is empty, flush all entries to the cached map
|
||||
void AddCachedRegion(const CachedTimeRegion& timeRegionCached);
|
||||
|
||||
// Tries to flush the map to the passed parameter, only if the thread's mutex is unlocked
|
||||
void TryFlushCachedMap(CpuProfiler::ThreadTimeRegionMap& cachedRegionMap);
|
||||
|
||||
AZStd::thread_id m_executingThreadId;
|
||||
// Keeps track of the current thread's stack depth
|
||||
uint32_t m_stackLevel = 0u;
|
||||
|
||||
// Cached region map, will be flushed to the system's map when the system requests it
|
||||
CpuProfiler::ThreadTimeRegionMap m_cachedTimeRegionMap;
|
||||
|
||||
// Use fixed vectors to avoid re-allocating new elements
|
||||
// Keeps track of the regions that added and removed using the macro
|
||||
AZStd::fixed_vector<CachedTimeRegion, TimeRegionStackSize> m_timeRegionStack;
|
||||
|
||||
// Keeps track of regions that completed (i.e regions that was pushed and popped from the stack)
|
||||
// Intermediate storage point for the CachedTimeRegions, when the stack is empty, all entries will be
|
||||
// copied to the map.
|
||||
AZStd::fixed_vector<CachedTimeRegion, TimeRegionStackSize> m_cachedTimeRegions;
|
||||
AZStd::mutex m_cachedTimeRegionMutex;
|
||||
|
||||
// Dirty flag which is set when the CpuProfiler's enabled state is set from false to true
|
||||
AZStd::atomic_bool m_clearContainers = false;
|
||||
|
||||
// When the thread is terminated, it will flag itself for deletion
|
||||
AZStd::atomic_bool m_deleteFlag = false;
|
||||
|
||||
// Keep track of the regions that have hit the size limit so we don't have to lock to check
|
||||
AZStd::map<AZStd::string, bool> m_hitSizeLimitMap;
|
||||
};
|
||||
|
||||
//! CpuProfiler will keep track of the registered threads, and
|
||||
//! forwards the request to profile a region to the appropriate thread. The user is able to request all
|
||||
//! cached regions, which are stored on a per thread frequency.
|
||||
class CpuProfilerImpl final
|
||||
: public AZ::Debug::Profiler
|
||||
, public CpuProfiler
|
||||
, public SystemTickBus::Handler
|
||||
{
|
||||
friend class CpuTimingLocalStorage;
|
||||
|
||||
public:
|
||||
AZ_TYPE_INFO(CpuProfilerImpl, "{10E9D394-FC83-4B45-B2B8-807C6BF07BF0}");
|
||||
AZ_CLASS_ALLOCATOR(CpuProfilerImpl, AZ::OSAllocator, 0);
|
||||
|
||||
CpuProfilerImpl() = default;
|
||||
~CpuProfilerImpl() = default;
|
||||
|
||||
//! Registers the CpuProfilerImpl instance to the interface
|
||||
void Init();
|
||||
//! Unregisters the CpuProfilerImpl instance from the interface
|
||||
void Shutdown();
|
||||
|
||||
// SystemTickBus::Handler overrides
|
||||
// When fired, the profiler collects all profiling data from registered threads and updates
|
||||
// m_timeRegionMap so that the next frame has up-to-date profiling data.
|
||||
void OnSystemTick() final override;
|
||||
|
||||
//! AZ::Debug::Profiler overrides...
|
||||
void BeginRegion(const AZ::Debug::Budget* budget, const char* eventName) final override;
|
||||
void EndRegion(const AZ::Debug::Budget* budget) final override;
|
||||
|
||||
//! CpuProfiler overrides...
|
||||
const TimeRegionMap& GetTimeRegionMap() const final override;
|
||||
bool BeginContinuousCapture() final override;
|
||||
bool EndContinuousCapture(AZStd::ring_buffer<TimeRegionMap>& flushTarget) final override;
|
||||
bool IsContinuousCaptureInProgress() const final override;
|
||||
void SetProfilerEnabled(bool enabled) final override;
|
||||
bool IsProfilerEnabled() const final override;
|
||||
|
||||
private:
|
||||
static constexpr AZStd::size_t MaxFramesToSave = 2 * 60 * 120; // 2 minutes of 120fps
|
||||
static constexpr AZStd::size_t MaxRegionStringPoolSize = 16384; // Max amount of unique strings to save in the pool before throwing warnings.
|
||||
|
||||
// Lazily create and register the local thread data
|
||||
void RegisterThreadStorage();
|
||||
|
||||
// ThreadId -> ThreadTimeRegionMap
|
||||
// On the start of each frame, this map will be updated with the last frame's profiling data.
|
||||
TimeRegionMap m_timeRegionMap;
|
||||
|
||||
// Set of registered threads when created
|
||||
AZStd::vector<RHI::Ptr<CpuTimingLocalStorage>, AZ::OSStdAllocator> m_registeredThreads;
|
||||
AZStd::mutex m_threadRegisterMutex;
|
||||
|
||||
// Thread local storage, gets lazily allocated when a thread is created
|
||||
static thread_local CpuTimingLocalStorage* ms_threadLocalStorage;
|
||||
|
||||
// Enable/Disables the threads from profiling
|
||||
AZStd::atomic_bool m_enabled = false;
|
||||
|
||||
// This lock will only be contested when the CpuProfiler's Shutdown() method has been called
|
||||
AZStd::shared_mutex m_shutdownMutex;
|
||||
|
||||
bool m_initialized = false;
|
||||
|
||||
AZStd::mutex m_continuousCaptureEndingMutex;
|
||||
|
||||
AZStd::atomic_bool m_continuousCaptureInProgress;
|
||||
|
||||
// Stores multiple frames of profiling data, size is controlled by MaxFramesToSave. Flushed when EndContinuousCapture is called.
|
||||
// Ring buffer so that we can have fast append of new data + removal of old profiling data with good cache locality.
|
||||
AZStd::ring_buffer<TimeRegionMap> m_continuousCaptureData;
|
||||
};
|
||||
|
||||
// Intermediate class to serialize Cpu TimedRegion data.
|
||||
class CpuProfilingStatisticsSerializer
|
||||
{
|
||||
public:
|
||||
class CpuProfilingStatisticsSerializerEntry
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializerEntry, "{26B78F65-EB96-46E2-BE7E-A1233880B225}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
CpuProfilingStatisticsSerializerEntry() = default;
|
||||
CpuProfilingStatisticsSerializerEntry(const RHI::CachedTimeRegion& cachedTimeRegion, AZStd::thread_id threadId);
|
||||
|
||||
Name m_groupName;
|
||||
Name m_regionName;
|
||||
uint16_t m_stackDepth;
|
||||
AZStd::sys_time_t m_startTick;
|
||||
AZStd::sys_time_t m_endTick;
|
||||
size_t m_threadId;
|
||||
};
|
||||
|
||||
AZ_TYPE_INFO(CpuProfilingStatisticsSerializer, "{D5B02946-0D27-474F-9A44-364C2706DD41}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
CpuProfilingStatisticsSerializer() = default;
|
||||
CpuProfilingStatisticsSerializer(const AZStd::ring_buffer<RHI::CpuProfiler::TimeRegionMap>& continuousData);
|
||||
|
||||
AZStd::vector<CpuProfilingStatisticsSerializerEntry> m_cpuProfilingStatisticsSerializerEntries;
|
||||
};
|
||||
}; // namespace RHI
|
||||
}; // namespace AZ
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Atom/RHI/CpuProfilerImpl.h>
|
||||
#include <Atom/RHI/Device.h>
|
||||
#include <Atom/RHI/DrawListTagRegistry.h>
|
||||
#include <Atom/RHI/FrameScheduler.h>
|
||||
@@ -66,8 +65,6 @@ namespace AZ
|
||||
RHI::Ptr<RHI::PipelineStateCache> m_pipelineStateCache;
|
||||
RHI::FrameScheduler m_frameScheduler;
|
||||
RHI::FrameSchedulerCompileRequest m_compileRequest;
|
||||
|
||||
RHI::CpuProfilerImpl m_cpuProfiler;
|
||||
};
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
|
||||
@@ -1,448 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Atom/RHI/CpuProfilerImpl.h>
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
|
||||
#include <AzCore/Debug/Timer.h>
|
||||
#include <AzCore/Statistics/StatisticalProfilerProxy.h>
|
||||
#include <Atom/RHI/RHIUtils.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RHI
|
||||
{
|
||||
thread_local CpuTimingLocalStorage* CpuProfilerImpl::ms_threadLocalStorage = nullptr;
|
||||
|
||||
// --- CpuProfiler ---
|
||||
|
||||
CpuProfiler* CpuProfiler::Get()
|
||||
{
|
||||
return Interface<CpuProfiler>::Get();
|
||||
}
|
||||
|
||||
// --- CachedTimeRegion ---
|
||||
|
||||
CachedTimeRegion::CachedTimeRegion(const GroupRegionName& groupRegionName)
|
||||
{
|
||||
m_groupRegionName = groupRegionName;
|
||||
}
|
||||
|
||||
CachedTimeRegion::CachedTimeRegion(const GroupRegionName& groupRegionName, uint16_t stackDepth, uint64_t startTick, uint64_t endTick)
|
||||
{
|
||||
m_groupRegionName = groupRegionName;
|
||||
m_stackDepth = stackDepth;
|
||||
m_startTick = startTick;
|
||||
m_endTick = endTick;
|
||||
}
|
||||
|
||||
// --- GroupRegionName ---
|
||||
|
||||
CachedTimeRegion::GroupRegionName::GroupRegionName(const char* const group, const char* const region) :
|
||||
m_groupName(group),
|
||||
m_regionName(region)
|
||||
{
|
||||
}
|
||||
|
||||
AZStd::size_t CachedTimeRegion::GroupRegionName::Hash::operator()(const CachedTimeRegion::GroupRegionName& name) const
|
||||
{
|
||||
AZStd::size_t seed = 0;
|
||||
AZStd::hash_combine(seed, name.m_groupName);
|
||||
AZStd::hash_combine(seed, name.m_regionName);
|
||||
return seed;
|
||||
}
|
||||
|
||||
bool CachedTimeRegion::GroupRegionName::operator==(const GroupRegionName& other) const
|
||||
{
|
||||
return (m_groupName == other.m_groupName) && (m_regionName == other.m_regionName);
|
||||
}
|
||||
|
||||
|
||||
// --- CpuProfilerImpl ---
|
||||
|
||||
void CpuProfilerImpl::Init()
|
||||
{
|
||||
Interface<AZ::Debug::Profiler>::Register(this);
|
||||
Interface<CpuProfiler>::Register(this);
|
||||
m_initialized = true;
|
||||
SystemTickBus::Handler::BusConnect();
|
||||
m_continuousCaptureData.set_capacity(10);
|
||||
|
||||
if (auto statsProfiler = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get(); statsProfiler)
|
||||
{
|
||||
statsProfiler->ActivateProfiler(AZ_CRC_CE("RHI"), true);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuProfilerImpl::Shutdown()
|
||||
{
|
||||
if (!m_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// When this call is made, no more thread profiling calls can be performed anymore
|
||||
Interface<CpuProfiler>::Unregister(this);
|
||||
Interface<AZ::Debug::Profiler>::Unregister(this);
|
||||
|
||||
// Wait for the remaining threads that might still be processing its profiling calls
|
||||
AZStd::unique_lock<AZStd::shared_mutex> shutdownLock(m_shutdownMutex);
|
||||
|
||||
m_enabled = false;
|
||||
|
||||
// Cleanup all TLS
|
||||
m_registeredThreads.clear();
|
||||
m_timeRegionMap.clear();
|
||||
m_initialized = false;
|
||||
m_continuousCaptureInProgress.store(false);
|
||||
m_continuousCaptureData.clear();
|
||||
SystemTickBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void CpuProfilerImpl::BeginRegion(const AZ::Debug::Budget* budget, const char* eventName)
|
||||
{
|
||||
// Try to lock here, the shutdownMutex will only be contested when the CpuProfiler is shutting down.
|
||||
if (m_shutdownMutex.try_lock_shared())
|
||||
{
|
||||
if (m_enabled)
|
||||
{
|
||||
// Lazy initialization, creates an instance of the Thread local data if it's not created, and registers it
|
||||
RegisterThreadStorage();
|
||||
|
||||
// Push it to the stack
|
||||
CachedTimeRegion timeRegion({budget->Name(), eventName});
|
||||
ms_threadLocalStorage->RegionStackPushBack(timeRegion);
|
||||
}
|
||||
|
||||
m_shutdownMutex.unlock_shared();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuProfilerImpl::EndRegion([[maybe_unused]] const AZ::Debug::Budget* budget)
|
||||
{
|
||||
// Try to lock here, the shutdownMutex will only be contested when the CpuProfiler is shutting down.
|
||||
if (m_shutdownMutex.try_lock_shared())
|
||||
{
|
||||
// guard against enabling mid-marker
|
||||
if (m_enabled && ms_threadLocalStorage != nullptr)
|
||||
{
|
||||
ms_threadLocalStorage->RegionStackPopBack();
|
||||
}
|
||||
|
||||
m_shutdownMutex.unlock_shared();
|
||||
}
|
||||
}
|
||||
|
||||
const CpuProfiler::TimeRegionMap& CpuProfilerImpl::GetTimeRegionMap() const
|
||||
{
|
||||
return m_timeRegionMap;
|
||||
}
|
||||
|
||||
bool CpuProfilerImpl::BeginContinuousCapture()
|
||||
{
|
||||
bool expected = false;
|
||||
if (m_continuousCaptureInProgress.compare_exchange_strong(expected, true))
|
||||
{
|
||||
m_enabled = true;
|
||||
AZ_TracePrintf("Profiler", "Continuous capture started\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ_TracePrintf("Profiler", "Attempting to start a continuous capture while one already in progress");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CpuProfilerImpl::EndContinuousCapture(AZStd::ring_buffer<TimeRegionMap>& flushTarget)
|
||||
{
|
||||
if (!m_continuousCaptureInProgress.load())
|
||||
{
|
||||
AZ_TracePrintf("Profiler", "Attempting to end a continuous capture while one not in progress");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_continuousCaptureEndingMutex.try_lock())
|
||||
{
|
||||
m_enabled = false;
|
||||
flushTarget = AZStd::move(m_continuousCaptureData);
|
||||
m_continuousCaptureData.clear();
|
||||
AZ_TracePrintf("Profiler", "Continuous capture ended\n");
|
||||
m_continuousCaptureInProgress.store(false);
|
||||
|
||||
m_continuousCaptureEndingMutex.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CpuProfilerImpl::IsContinuousCaptureInProgress() const
|
||||
{
|
||||
return m_continuousCaptureInProgress.load();
|
||||
}
|
||||
|
||||
void CpuProfilerImpl::SetProfilerEnabled(bool enabled)
|
||||
{
|
||||
AZStd::unique_lock<AZStd::mutex> lock(m_threadRegisterMutex);
|
||||
|
||||
// Early out if the state is already the same or a continuous capture is in progress
|
||||
if (m_enabled == enabled || m_continuousCaptureInProgress.load())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the dirty flag in all the TLS to clear the caches
|
||||
if (enabled)
|
||||
{
|
||||
// Iterate through all the threads, and set the clearing flag
|
||||
for (auto& threadLocal : m_registeredThreads)
|
||||
{
|
||||
threadLocal->m_clearContainers = true;
|
||||
}
|
||||
|
||||
m_enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CpuProfilerImpl::IsProfilerEnabled() const
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
void CpuProfilerImpl::OnSystemTick()
|
||||
{
|
||||
if (!m_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_continuousCaptureInProgress.load() && m_continuousCaptureEndingMutex.try_lock())
|
||||
{
|
||||
if (m_continuousCaptureData.full() && m_continuousCaptureData.size() != MaxFramesToSave)
|
||||
{
|
||||
const AZStd::size_t size = m_continuousCaptureData.size();
|
||||
m_continuousCaptureData.set_capacity(AZStd::min(MaxFramesToSave, size + size / 2));
|
||||
}
|
||||
|
||||
m_continuousCaptureData.push_back(AZStd::move(m_timeRegionMap));
|
||||
m_timeRegionMap.clear();
|
||||
m_continuousCaptureEndingMutex.unlock();
|
||||
}
|
||||
|
||||
AZStd::unique_lock<AZStd::mutex> lock(m_threadRegisterMutex);
|
||||
|
||||
// Iterate through all the threads, and collect the thread's cached time regions
|
||||
TimeRegionMap newMap;
|
||||
for (auto& threadLocal : m_registeredThreads)
|
||||
{
|
||||
ThreadTimeRegionMap& threadMapEntry = newMap[threadLocal->m_executingThreadId];
|
||||
threadLocal->TryFlushCachedMap(threadMapEntry);
|
||||
}
|
||||
|
||||
// Clear all TLS that flagged themselves to be deleted, meaning that the thread is already terminated
|
||||
AZStd::remove_if(m_registeredThreads.begin(), m_registeredThreads.end(), [](const RHI::Ptr<CpuTimingLocalStorage>& thread)
|
||||
{
|
||||
return thread->m_deleteFlag.load();
|
||||
});
|
||||
|
||||
// Update our saved time regions to the last frame's collected data
|
||||
m_timeRegionMap = AZStd::move(newMap);
|
||||
}
|
||||
|
||||
void CpuProfilerImpl::RegisterThreadStorage()
|
||||
{
|
||||
AZStd::unique_lock<AZStd::mutex> lock(m_threadRegisterMutex);
|
||||
if (!ms_threadLocalStorage)
|
||||
{
|
||||
ms_threadLocalStorage = aznew CpuTimingLocalStorage();
|
||||
m_registeredThreads.emplace_back(ms_threadLocalStorage);
|
||||
}
|
||||
}
|
||||
|
||||
// --- CpuTimingLocalStorage ---
|
||||
|
||||
CpuTimingLocalStorage::CpuTimingLocalStorage()
|
||||
{
|
||||
m_executingThreadId = AZStd::this_thread::get_id();
|
||||
}
|
||||
|
||||
CpuTimingLocalStorage::~CpuTimingLocalStorage()
|
||||
{
|
||||
m_deleteFlag = true;
|
||||
}
|
||||
|
||||
void CpuTimingLocalStorage::RegionStackPushBack(CachedTimeRegion& timeRegion)
|
||||
{
|
||||
// If it was (re)enabled, clear the lists first
|
||||
if (m_clearContainers)
|
||||
{
|
||||
m_clearContainers = false;
|
||||
|
||||
m_stackLevel = 0;
|
||||
m_cachedTimeRegionMap.clear();
|
||||
m_timeRegionStack.clear();
|
||||
m_cachedTimeRegions.clear();
|
||||
}
|
||||
|
||||
timeRegion.m_stackDepth = static_cast<uint16_t>(m_stackLevel);
|
||||
|
||||
AZ_Assert(m_timeRegionStack.size() < TimeRegionStackSize, "Adding too many time regions to the stack. Increase the size of TimeRegionStackSize.");
|
||||
m_timeRegionStack.push_back(timeRegion);
|
||||
|
||||
// Increment the stack
|
||||
m_stackLevel++;
|
||||
|
||||
// Set the starting time at the end, to avoid recording the minor overhead
|
||||
m_timeRegionStack.back().m_startTick = AZStd::GetTimeNowTicks();
|
||||
}
|
||||
|
||||
void CpuTimingLocalStorage::RegionStackPopBack()
|
||||
{
|
||||
// Early out when the stack is empty, this might happen when the profiler was enabled while the thread encountered profiling markers
|
||||
if (m_timeRegionStack.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the end timestamp here, to avoid the minor overhead
|
||||
const AZStd::sys_time_t endRegionTime = AZStd::GetTimeNowTicks();
|
||||
|
||||
AZ_Assert(!m_timeRegionStack.empty(), "Trying to pop an element in the stack, but it's empty.");
|
||||
CachedTimeRegion back = m_timeRegionStack.back();
|
||||
m_timeRegionStack.pop_back();
|
||||
|
||||
// Set the ending time
|
||||
back.m_endTick = endRegionTime;
|
||||
|
||||
// Decrement the stack
|
||||
m_stackLevel--;
|
||||
|
||||
// Add an entry to the cached region
|
||||
AddCachedRegion(back);
|
||||
}
|
||||
|
||||
// Gets called when region ends and all data is set
|
||||
void CpuTimingLocalStorage::AddCachedRegion(const CachedTimeRegion& timeRegionCached)
|
||||
{
|
||||
if (m_hitSizeLimitMap[timeRegionCached.m_groupRegionName.m_regionName])
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Add an entry to the cached region
|
||||
m_cachedTimeRegions.push_back(timeRegionCached);
|
||||
|
||||
// If the stack is empty, add it to the local cache map. Only gets called when the stack is empty
|
||||
// NOTE: this is where the largest overhead will be, but due to it only being called when the stack is empty
|
||||
// (i.e when the root region ended), this overhead won't affect any time regions.
|
||||
// The exception being for functions that are being profiled and create/spawn threads that are also profiled. Unfortunately, in this
|
||||
// case, the overhead of the profiled threads will be added to the main thread.
|
||||
if (m_timeRegionStack.empty())
|
||||
{
|
||||
AZStd::unique_lock<AZStd::mutex> lock(m_cachedTimeRegionMutex);
|
||||
|
||||
// Add the cached regions to the map
|
||||
for (auto& cachedTimeRegion : m_cachedTimeRegions)
|
||||
{
|
||||
const AZStd::string regionName = cachedTimeRegion.m_groupRegionName.m_regionName;
|
||||
AZStd::vector<CachedTimeRegion>& regionVec = m_cachedTimeRegionMap[regionName];
|
||||
regionVec.push_back(cachedTimeRegion);
|
||||
if (regionVec.size() >= TimeRegionStackSize)
|
||||
{
|
||||
m_hitSizeLimitMap.insert_or_assign(AZStd::move(regionName), true);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the cached regions
|
||||
m_cachedTimeRegions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuTimingLocalStorage::TryFlushCachedMap(CpuProfiler::ThreadTimeRegionMap& cachedTimeRegionMap)
|
||||
{
|
||||
// Try to lock, if it's already in use (the cached regions in the array are being copied to the map)
|
||||
// it'll show up in the next iteration when the user requests it.
|
||||
if (m_cachedTimeRegionMutex.try_lock())
|
||||
{
|
||||
// Only flush cached time regions if there are entries available
|
||||
if (!m_cachedTimeRegionMap.empty())
|
||||
{
|
||||
cachedTimeRegionMap = AZStd::move(m_cachedTimeRegionMap);
|
||||
m_cachedTimeRegionMap.clear();
|
||||
m_hitSizeLimitMap.clear();
|
||||
}
|
||||
m_cachedTimeRegionMutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// --- CpuProfilingStatisticsSerializer ---
|
||||
|
||||
CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializer(const AZStd::ring_buffer<RHI::CpuProfiler::TimeRegionMap>& continuousData)
|
||||
{
|
||||
// Create serializable entries
|
||||
for (const auto& timeRegionMap : continuousData)
|
||||
{
|
||||
for (const auto& [threadId, regionMap] : timeRegionMap)
|
||||
{
|
||||
for (const auto& [regionName, regionVec] : regionMap)
|
||||
{
|
||||
for (const auto& region : regionVec)
|
||||
{
|
||||
m_cpuProfilingStatisticsSerializerEntries.emplace_back(region, threadId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CpuProfilingStatisticsSerializer::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<CpuProfilingStatisticsSerializer>()
|
||||
->Version(1)
|
||||
->Field("cpuProfilingStatisticsSerializerEntries", &CpuProfilingStatisticsSerializer::m_cpuProfilingStatisticsSerializerEntries)
|
||||
;
|
||||
}
|
||||
|
||||
CpuProfilingStatisticsSerializerEntry::Reflect(context);
|
||||
}
|
||||
|
||||
// --- CpuProfilingStatisticsSerializerEntry ---
|
||||
|
||||
CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializerEntry::CpuProfilingStatisticsSerializerEntry(
|
||||
const RHI::CachedTimeRegion& cachedTimeRegion, AZStd::thread_id threadId)
|
||||
{
|
||||
m_groupName = cachedTimeRegion.m_groupRegionName.m_groupName;
|
||||
m_regionName = cachedTimeRegion.m_groupRegionName.m_regionName;
|
||||
m_stackDepth = cachedTimeRegion.m_stackDepth;
|
||||
m_startTick = cachedTimeRegion.m_startTick;
|
||||
m_endTick = cachedTimeRegion.m_endTick;
|
||||
m_threadId = AZStd::hash<AZStd::thread_id>{}(threadId);
|
||||
}
|
||||
|
||||
void CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializerEntry::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<CpuProfilingStatisticsSerializerEntry>()
|
||||
->Version(1)
|
||||
->Field("groupName", &CpuProfilingStatisticsSerializerEntry::m_groupName)
|
||||
->Field("regionName", &CpuProfilingStatisticsSerializerEntry::m_regionName)
|
||||
->Field("stackDepth", &CpuProfilingStatisticsSerializerEntry::m_stackDepth)
|
||||
->Field("startTick", &CpuProfilingStatisticsSerializerEntry::m_startTick)
|
||||
->Field("endTick", &CpuProfilingStatisticsSerializerEntry::m_endTick)
|
||||
->Field("threadId", &CpuProfilingStatisticsSerializerEntry::m_threadId)
|
||||
;
|
||||
}
|
||||
}
|
||||
} // namespace RHI
|
||||
} // namespace AZ
|
||||
@@ -86,6 +86,8 @@ namespace AZ
|
||||
|
||||
if (auto statsProfiler = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get(); statsProfiler)
|
||||
{
|
||||
statsProfiler->ActivateProfiler(rhiMetricsId, true);
|
||||
|
||||
auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId);
|
||||
rhiMetrics.GetStatsManager().AddStatistic(frameTimeMetricId, frameTimeMetricName, /*units=*/"clocks", /*failIfExist=*/false);
|
||||
}
|
||||
@@ -602,14 +604,11 @@ namespace AZ
|
||||
|
||||
double FrameScheduler::GetCpuFrameTime() const
|
||||
{
|
||||
if (CheckBitsAny(m_compileRequest.m_statisticsFlags, FrameSchedulerStatisticsFlags::GatherCpuTimingStatistics))
|
||||
if (auto statsProfiler = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get(); statsProfiler)
|
||||
{
|
||||
if (auto statsProfiler = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get(); statsProfiler)
|
||||
{
|
||||
auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId);
|
||||
const auto* frameTimeStat = rhiMetrics.GetStatistic(frameTimeMetricId);
|
||||
return (frameTimeStat->GetMostRecentSample() * 1000) / aznumeric_cast<double>(AZStd::GetTimeTicksPerSecond());
|
||||
}
|
||||
auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId);
|
||||
const auto* frameTimeStat = rhiMetrics.GetStatistic(frameTimeMetricId);
|
||||
return (frameTimeStat->GetMostRecentSample() * 1000) / aznumeric_cast<double>(AZStd::GetTimeTicksPerSecond());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -39,8 +39,6 @@ namespace AZ
|
||||
|
||||
void RHISystem::Init()
|
||||
{
|
||||
m_cpuProfiler.Init();
|
||||
|
||||
Ptr<RHI::PlatformLimitsDescriptor> platformLimitsDescriptor = m_device->GetDescriptor().m_platformLimitsDescriptor;
|
||||
|
||||
RHI::FrameSchedulerDescriptor frameSchedulerDescriptor;
|
||||
@@ -187,8 +185,6 @@ namespace AZ
|
||||
AZ_Assert(m_device->use_count()==1, "The ref count for Device is %i but it should be 1 here to ensure all the resources are released", m_device->use_count());
|
||||
m_device = nullptr;
|
||||
}
|
||||
|
||||
m_cpuProfiler.Shutdown();
|
||||
}
|
||||
|
||||
void RHISystem::FrameUpdate(FrameGraphCallback frameGraphCallback)
|
||||
|
||||
@@ -197,8 +197,5 @@ set(FILES
|
||||
Include/Atom/RHI/interval_map.h
|
||||
Include/Atom/RHI/ImageProperty.h
|
||||
Include/Atom/RHI/BufferProperty.h
|
||||
Include/Atom/RHI/CpuProfiler.h
|
||||
Include/Atom/RHI/CpuProfilerImpl.h
|
||||
Source/RHI/CpuProfilerImpl.cpp
|
||||
Include/Atom/RHI/TagRegistry.h
|
||||
)
|
||||
|
||||
+2
@@ -102,6 +102,8 @@ namespace AtomToolsFramework
|
||||
void BeginCursorCapture() override;
|
||||
void EndCursorCapture() override;
|
||||
bool IsMouseOver() const override;
|
||||
void SetOverrideCursor(AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride) override;
|
||||
void ClearOverrideCursor() override;
|
||||
|
||||
// AzFramework::WindowRequestBus::Handler overrides ...
|
||||
void SetWindowTitle(const AZStd::string& title) override;
|
||||
|
||||
+5
-2
@@ -63,8 +63,11 @@ namespace AtomToolsFramework
|
||||
void PreviewRendererSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
|
||||
{
|
||||
AZ::TickBus::QueueFunction([this](){
|
||||
m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer(
|
||||
"PreviewRendererSystemComponent Preview Scene", "PreviewRendererSystemComponent Preview Pipeline"));
|
||||
if (!m_previewRenderer)
|
||||
{
|
||||
m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer(
|
||||
"PreviewRendererSystemComponent Preview Scene", "PreviewRendererSystemComponent Preview Pipeline"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -375,6 +375,16 @@ namespace AtomToolsFramework
|
||||
m_inputChannelMapper->SetCursorCaptureEnabled(false);
|
||||
}
|
||||
|
||||
void RenderViewportWidget::SetOverrideCursor(AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride)
|
||||
{
|
||||
m_inputChannelMapper->SetOverrideCursor(cursorStyleOverride);
|
||||
}
|
||||
|
||||
void RenderViewportWidget::ClearOverrideCursor()
|
||||
{
|
||||
m_inputChannelMapper->ClearOverrideCursor();
|
||||
}
|
||||
|
||||
void RenderViewportWidget::SetWindowTitle(const AZStd::string& title)
|
||||
{
|
||||
setWindowTitle(QString::fromUtf8(title.c_str()));
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <Atom/RHI/RHISystemInterface.h>
|
||||
#include <Atom/RHI/CpuProfiler.h>
|
||||
#include <Atom/RPI.Public/Pass/ParentPass.h>
|
||||
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
|
||||
|
||||
@@ -66,12 +65,6 @@ namespace MaterialEditor
|
||||
AZ_Error("PerformanceMonitorComponent", false, "Failed to find root pass.");
|
||||
}
|
||||
|
||||
AZ::RHI::RHISystemInterface::Get()->ModifyFrameSchedulerStatisticsFlags(
|
||||
AZ::RHI::FrameSchedulerStatisticsFlags::GatherCpuTimingStatistics,
|
||||
enabled);
|
||||
|
||||
AZ::RHI::CpuProfiler::Get()->SetProfilerEnabled(enabled);
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
ResetStats();
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Math/Random.h>
|
||||
|
||||
#include <Atom/RHI/CpuProfiler.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
//! Stores all the data associated with a row in the table.
|
||||
struct TableRow
|
||||
{
|
||||
template <typename T>
|
||||
struct TableRowCompareFunctor
|
||||
{
|
||||
TableRowCompareFunctor(T memberPointer, bool isAscending) : m_memberPointer(memberPointer), m_ascending(isAscending){};
|
||||
|
||||
bool operator()(const TableRow* lhs, const TableRow* rhs)
|
||||
{
|
||||
return m_ascending ? lhs->*m_memberPointer < rhs->*m_memberPointer : lhs->*m_memberPointer > rhs->*m_memberPointer;
|
||||
}
|
||||
|
||||
T m_memberPointer;
|
||||
bool m_ascending;
|
||||
};
|
||||
|
||||
// Update running statistics with new region data
|
||||
void RecordRegion(const AZ::RHI::CachedTimeRegion& region, size_t threadId);
|
||||
|
||||
void ResetPerFrameStatistics();
|
||||
|
||||
// Get a string of all threads that this region executed in during the last frame
|
||||
AZStd::string GetExecutingThreadsLabel() const;
|
||||
|
||||
AZStd::string m_groupName;
|
||||
AZStd::string m_regionName;
|
||||
|
||||
// --- Per frame statistics ---
|
||||
|
||||
u64 m_invocationsLastFrame = 0;
|
||||
|
||||
// NOTE: set over unordered_set so the threads can be shown in increasing order in tooltip.
|
||||
AZStd::set<size_t> m_executingThreads;
|
||||
|
||||
AZStd::sys_time_t m_lastFrameTotalTicks = 0;
|
||||
|
||||
// Maximum execution time of a region in the last frame.
|
||||
AZStd::sys_time_t m_maxTicks = 0;
|
||||
|
||||
// --- Aggregate statistics ---
|
||||
|
||||
u64 m_invocationsTotal = 0;
|
||||
|
||||
// Running average of Mean Time Per Call
|
||||
AZStd::sys_time_t m_runningAverageTicks = 0;
|
||||
};
|
||||
|
||||
//! ImGui widget for examining Atom CPU Profiling instrumentation.
|
||||
//! Offers both a statistical view (with sorting and searching capability) and a visualizer
|
||||
//! similar to RAD and other profiling tools.
|
||||
class ImGuiCpuProfiler
|
||||
: SystemTickBus::Handler
|
||||
{
|
||||
// Region Name -> statistical view row data
|
||||
using RegionRowMap = AZStd::map<AZStd::string, TableRow>;
|
||||
// Group Name -> RegionRowMap
|
||||
using GroupRegionMap = AZStd::map<AZStd::string, RegionRowMap>;
|
||||
|
||||
using TimeRegion = AZ::RHI::CachedTimeRegion;
|
||||
using GroupRegionName = AZ::RHI::CachedTimeRegion::GroupRegionName;
|
||||
|
||||
public:
|
||||
struct CpuTimingEntry
|
||||
{
|
||||
const AZStd::string& m_name;
|
||||
double m_executeDuration;
|
||||
};
|
||||
|
||||
ImGuiCpuProfiler() = default;
|
||||
~ImGuiCpuProfiler() = default;
|
||||
|
||||
//! Draws the overall CPU profiling window, defaults to the statistical view
|
||||
void Draw(bool& keepDrawing);
|
||||
|
||||
private:
|
||||
static constexpr float RowHeight = 35.0;
|
||||
static constexpr int DefaultFramesToCollect = 50;
|
||||
static constexpr float MediumFrameTimeLimit = 16.6; // 60 fps
|
||||
static constexpr float HighFrameTimeLimit = 33.3; // 30 fps
|
||||
|
||||
//! Draws the statistical view of the CPU profiling data.
|
||||
void DrawStatisticsView();
|
||||
|
||||
//! Callback invoked when the "Load File" button is pressed in the file picker.
|
||||
void LoadFile();
|
||||
|
||||
//! Draws the file picker window.
|
||||
void DrawFilePicker();
|
||||
|
||||
//! Draws the CPU profiling visualizer.
|
||||
void DrawVisualizer();
|
||||
|
||||
// Draw the shared header between the two windows.
|
||||
void DrawCommonHeader();
|
||||
|
||||
// Draw the region statistics table in the order specified by the pointers in m_tableData.
|
||||
void DrawTable();
|
||||
|
||||
// Sort the table by a given column, rearranges the pointers in m_tableData.
|
||||
void SortTable(ImGuiTableSortSpecs* sortSpecs);
|
||||
|
||||
// gather the latest timing statistics
|
||||
void CacheCpuTimingStatistics();
|
||||
|
||||
// Get the profiling data from the last frame, only called when the profiler is not paused.
|
||||
void CollectFrameData();
|
||||
|
||||
// Cull old data from internal storage, only called when profiler is not paused.
|
||||
void CullFrameData();
|
||||
|
||||
// Draws a single block onto the timeline into the specified row
|
||||
void DrawBlock(const TimeRegion& block, u64 targetRow);
|
||||
|
||||
// Draw horizontal lines between threads in the timeline
|
||||
void DrawThreadSeparator(u64 threadBoundary, u64 maxDepth);
|
||||
|
||||
// Draw the "Thread XXXXX" label onto the viewport
|
||||
void DrawThreadLabel(u64 baseRow, size_t threadId);
|
||||
|
||||
// Draw the vertical lines separating frames in the timeline
|
||||
void DrawFrameBoundaries();
|
||||
|
||||
// Draw the ruler with frame time labels
|
||||
void DrawRuler();
|
||||
|
||||
// Draw the frame time histogram
|
||||
void DrawFrameTimeHistogram();
|
||||
|
||||
// Converts raw ticks to a pixel value suitable to give to ImDrawList, handles window scrolling
|
||||
float ConvertTickToPixelSpace(AZStd::sys_time_t tick, AZStd::sys_time_t leftBound, AZStd::sys_time_t rightBound) const;
|
||||
|
||||
AZStd::sys_time_t GetViewportTickWidth() const;
|
||||
|
||||
// Gets the color for a block using the GroupRegionName as a key into the cache.
|
||||
// Generates a random ImU32 if the block does not yet have a color.
|
||||
ImU32 GetBlockColor(const TimeRegion& block);
|
||||
|
||||
// System tick bus overrides
|
||||
virtual void OnSystemTick() override;
|
||||
|
||||
// --- Visualizer Members ---
|
||||
|
||||
int m_framesToCollect = DefaultFramesToCollect;
|
||||
|
||||
// Tally of the number of saved profiling events so far
|
||||
u64 m_savedRegionCount = 0;
|
||||
|
||||
// Viewport tick bounds, these are used to convert tick space -> screen space and cull so we only draw onscreen objects
|
||||
AZStd::sys_time_t m_viewportStartTick;
|
||||
AZStd::sys_time_t m_viewportEndTick;
|
||||
|
||||
// Map to store each thread's TimeRegions, individual vectors are sorted by start tick
|
||||
// note: we use size_t as a proxy for thread_id because native_thread_id_type differs differs from
|
||||
// platform to platform, which causes problems when deserializing saved captures.
|
||||
AZStd::unordered_map<size_t, AZStd::vector<TimeRegion>> m_savedData;
|
||||
|
||||
// Region color cache
|
||||
AZStd::unordered_map<GroupRegionName, ImVec4, RHI::CachedTimeRegion::GroupRegionName::Hash> m_regionColorMap;
|
||||
|
||||
// Tracks the frame boundaries
|
||||
AZStd::vector<AZStd::sys_time_t> m_frameEndTicks = { INT64_MIN };
|
||||
|
||||
// Filter for highlighting regions on the visualizer
|
||||
ImGuiTextFilter m_visualizerHighlightFilter;
|
||||
|
||||
// --- Tabular view members ---
|
||||
|
||||
// ImGui filter used to filter TimedRegions.
|
||||
ImGuiTextFilter m_timedRegionFilter;
|
||||
|
||||
// Saves statistical view data organized by group name -> region name -> row data
|
||||
GroupRegionMap m_groupRegionMap;
|
||||
|
||||
// Saves pointers to objects in m_groupRegionMap, order reflects table ordering.
|
||||
// Non-owning, will be cleared when m_groupRegionMap is cleared.
|
||||
AZStd::vector<TableRow*> m_tableData;
|
||||
|
||||
// Pause cpu profiling. The profiler will show the statistics of the last frame before pause.
|
||||
bool m_paused = false;
|
||||
|
||||
// Export the profiling data from a single frame to a local file.
|
||||
bool m_captureToFile = false;
|
||||
|
||||
// Toggle between the normal statistical view and the visual profiling view.
|
||||
bool m_enableVisualizer = false;
|
||||
|
||||
// Last captured CPU timing statistics
|
||||
AZStd::vector<CpuTimingEntry> m_cpuTimingStatisticsWhenPause;
|
||||
AZStd::sys_time_t m_frameToFrameTime{};
|
||||
|
||||
AZStd::string m_lastCapturedFilePath;
|
||||
|
||||
bool m_showFilePicker = false;
|
||||
|
||||
// Cached file paths to previous traces on disk, sorted with the most recent trace at the front.
|
||||
AZStd::vector<IO::Path> m_cachedCapturePaths;
|
||||
|
||||
// Index into the file picker, used to determine which file to load when "Load File" is pressed.
|
||||
int m_currentFileIndex = 0;
|
||||
|
||||
|
||||
// --- Loading capture state ---
|
||||
AZStd::unordered_set<AZStd::string> m_deserializedStringPool;
|
||||
AZStd::unordered_set<RHI::CachedTimeRegion::GroupRegionName, RHI::CachedTimeRegion::GroupRegionName::Hash> m_deserializedGroupRegionNamePool;
|
||||
};
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
#include "ImGuiCpuProfiler.inl"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,8 +9,6 @@
|
||||
set(FILES
|
||||
Include/Atom/Utils/DdsFile.h
|
||||
Include/Atom/Utils/ImageComparison.h
|
||||
Include/Atom/Utils/ImGuiCpuProfiler.h
|
||||
Include/Atom/Utils/ImGuiCpuProfiler.inl
|
||||
Include/Atom/Utils/ImGuiCullingDebug.h
|
||||
Include/Atom/Utils/ImGuiCullingDebug.inl
|
||||
Include/Atom/Utils/ImGuiGpuProfiler.h
|
||||
|
||||
@@ -84,10 +84,6 @@ namespace AtomImGuiTools
|
||||
{
|
||||
m_imguiGpuProfiler.Draw(m_showGpuProfiler, AZ::RPI::PassSystemInterface::Get()->GetRootPass().get());
|
||||
}
|
||||
if (m_showCpuProfiler)
|
||||
{
|
||||
m_imguiCpuProfiler.Draw(m_showCpuProfiler);
|
||||
}
|
||||
if (m_showTransientAttachmentProfiler)
|
||||
{
|
||||
auto* transientStats = AZ::RHI::RHISystemInterface::Get()->GetTransientAttachmentStatistics();
|
||||
@@ -108,12 +104,6 @@ namespace AtomImGuiTools
|
||||
{
|
||||
ImGui::MenuItem("Pass Viewer", "", &m_showPassTree);
|
||||
ImGui::MenuItem("Gpu Profiler", "", &m_showGpuProfiler);
|
||||
if (ImGui::MenuItem("Cpu Profiler", "", &m_showCpuProfiler))
|
||||
{
|
||||
AZ::RHI::RHISystemInterface::Get()->ModifyFrameSchedulerStatisticsFlags(
|
||||
AZ::RHI::FrameSchedulerStatisticsFlags::GatherCpuTimingStatistics, m_showCpuProfiler);
|
||||
AZ::RHI::CpuProfiler::Get()->SetProfilerEnabled(m_showCpuProfiler);
|
||||
}
|
||||
if (ImGui::MenuItem("Transient Attachment Profiler", "", &m_showTransientAttachmentProfiler))
|
||||
{
|
||||
AZ::RHI::RHISystemInterface::Get()->ModifyFrameSchedulerStatisticsFlags(
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#if defined(IMGUI_ENABLED)
|
||||
#include <ImGuiBus.h>
|
||||
#include <imgui/imgui.h>
|
||||
#include <Atom/Utils/ImGuiCpuProfiler.h>
|
||||
#include <Atom/Utils/ImGuiGpuProfiler.h>
|
||||
#include <Atom/Utils/ImGuiPassTree.h>
|
||||
#include <Atom/Utils/ImGuiShaderMetrics.h>
|
||||
@@ -63,9 +62,6 @@ namespace AtomImGuiTools
|
||||
AZ::Render::ImGuiGpuProfiler m_imguiGpuProfiler;
|
||||
bool m_showGpuProfiler = false;
|
||||
|
||||
AZ::Render::ImGuiCpuProfiler m_imguiCpuProfiler;
|
||||
bool m_showCpuProfiler = false;
|
||||
|
||||
AZ::Render::ImGuiTransientAttachmentProfiler m_imguiTransientAttachmentProfiler;
|
||||
bool m_showTransientAttachmentProfiler = false;
|
||||
|
||||
|
||||
+11
-5
@@ -216,11 +216,17 @@ namespace AZ
|
||||
using namespace LyIntegration;
|
||||
|
||||
ThumbnailerRequestsBus::Broadcast(
|
||||
&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(SharedThumbnailCache),
|
||||
ThumbnailContext::DefaultContext);
|
||||
&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(SharedThumbnailCache), ThumbnailContext::DefaultContext);
|
||||
|
||||
m_renderer = AZStd::make_unique<AZ::LyIntegration::SharedThumbnailRenderer>();
|
||||
m_previewerFactory = AZStd::make_unique<LyIntegration::SharedPreviewerFactory>();
|
||||
if (!m_thumbnailRenderer)
|
||||
{
|
||||
m_thumbnailRenderer = AZStd::make_unique<AZ::LyIntegration::SharedThumbnailRenderer>();
|
||||
}
|
||||
|
||||
if (!m_previewerFactory)
|
||||
{
|
||||
m_previewerFactory = AZStd::make_unique<LyIntegration::SharedPreviewerFactory>();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorCommonFeaturesSystemComponent::TeardownThumbnails()
|
||||
@@ -232,7 +238,7 @@ namespace AZ
|
||||
&ThumbnailerRequests::UnregisterThumbnailProvider, SharedThumbnailCache::ProviderName,
|
||||
ThumbnailContext::DefaultContext);
|
||||
|
||||
m_renderer.reset();
|
||||
m_thumbnailRenderer.reset();
|
||||
m_previewerFactory.reset();
|
||||
}
|
||||
} // namespace Render
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user