Merge branch 'development' into Atom/santorac/FixSceneSrgTime
This commit is contained in:
@@ -12,6 +12,7 @@ Editor/EditorEventLog.xml
|
||||
Editor/EditorLayout.xml
|
||||
**/*egg-info/**
|
||||
**/*egg-link
|
||||
**/[Rr]estricted
|
||||
UserSettings.xml
|
||||
[Uu]ser/
|
||||
FrameCapture/**
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace AutomatedTesting
|
||||
|
||||
void AutomatedTestingSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
AZ_UNUSED(required);
|
||||
required.push_back(AZ_CRC_CE("MultiplayerService"));
|
||||
}
|
||||
|
||||
void AutomatedTestingSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
|
||||
|
||||
@@ -20,19 +20,6 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
COMPONENT
|
||||
Atom
|
||||
)
|
||||
ly_add_pytest(
|
||||
NAME AutomatedTesting::Atom_TestSuite_Main_Optimized
|
||||
TEST_SUITE main
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_Optimized.py
|
||||
TEST_SERIAL
|
||||
TIMEOUT 600
|
||||
RUNTIME_DEPENDENCIES
|
||||
AssetProcessor
|
||||
AutomatedTesting.Assets
|
||||
Editor
|
||||
COMPONENT
|
||||
Atom
|
||||
)
|
||||
ly_add_pytest(
|
||||
NAME AutomatedTesting::Atom_TestSuite_Sandbox
|
||||
TEST_SUITE sandbox
|
||||
|
||||
@@ -4,252 +4,95 @@ For complete copyright and license terms please see the LICENSE at the root of t
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
import ly_test_tools.environment.file_system as file_system
|
||||
import editor_python_test_tools.hydra_test_utils as hydra
|
||||
from Atom.atom_utils.atom_constants import LIGHT_TYPES
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests")
|
||||
from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
|
||||
|
||||
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
@pytest.mark.parametrize("level", ["auto_test"])
|
||||
class TestAtomEditorComponentsMain(object):
|
||||
"""Holds tests for Atom components."""
|
||||
class TestAutomation(EditorTestSuite):
|
||||
|
||||
@pytest.mark.test_case_id("C32078118") # Decal
|
||||
@pytest.mark.test_case_id("C32078119") # DepthOfField
|
||||
@pytest.mark.test_case_id("C32078120") # Directional Light
|
||||
@pytest.mark.test_case_id("C32078121") # Exposure Control
|
||||
@pytest.mark.test_case_id("C32078115") # Global Skylight (IBL)
|
||||
@pytest.mark.test_case_id("C32078125") # Physical Sky
|
||||
@pytest.mark.test_case_id("C32078127") # PostFX Layer
|
||||
@pytest.mark.test_case_id("C32078131") # PostFX Radius Weight Modifier
|
||||
@pytest.mark.test_case_id("C32078117") # Light
|
||||
@pytest.mark.test_case_id("C36525660") # Display Mapper
|
||||
def test_AtomEditorComponents_AddedToEntity(self, request, editor, level, workspace, project, launcher_platform):
|
||||
"""
|
||||
Please review the hydra script run by this test for more specific test info.
|
||||
Tests the Atom components & verifies all "expected_lines" appear in Editor.log
|
||||
"""
|
||||
cfg_args = [level]
|
||||
@pytest.mark.test_case_id("C36525657")
|
||||
class AtomEditorComponents_BloomAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_BloomAdded as test_module
|
||||
|
||||
expected_lines = [
|
||||
# Decal Component
|
||||
"Decal Entity successfully created",
|
||||
"Decal_test: Component added to the entity: True",
|
||||
"Decal_test: Component removed after UNDO: True",
|
||||
"Decal_test: Component added after REDO: True",
|
||||
"Decal_test: Entered game mode: True",
|
||||
"Decal_test: Exit game mode: True",
|
||||
"Decal Controller|Configuration|Material: SUCCESS",
|
||||
"Decal_test: Entity is hidden: True",
|
||||
"Decal_test: Entity is shown: True",
|
||||
"Decal_test: Entity deleted: True",
|
||||
"Decal_test: UNDO entity deletion works: True",
|
||||
"Decal_test: REDO entity deletion works: True",
|
||||
# DepthOfField Component
|
||||
"DepthOfField Entity successfully created",
|
||||
"DepthOfField_test: Component added to the entity: True",
|
||||
"DepthOfField_test: Component removed after UNDO: True",
|
||||
"DepthOfField_test: Component added after REDO: True",
|
||||
"DepthOfField_test: Entered game mode: True",
|
||||
"DepthOfField_test: Exit game mode: True",
|
||||
"DepthOfField_test: Entity disabled initially: True",
|
||||
"DepthOfField_test: Entity enabled after adding required components: True",
|
||||
"DepthOfField Controller|Configuration|Camera Entity: SUCCESS",
|
||||
"DepthOfField_test: Entity is hidden: True",
|
||||
"DepthOfField_test: Entity is shown: True",
|
||||
"DepthOfField_test: Entity deleted: True",
|
||||
"DepthOfField_test: UNDO entity deletion works: True",
|
||||
"DepthOfField_test: REDO entity deletion works: True",
|
||||
# Directional Light Component
|
||||
"Directional Light Entity successfully created",
|
||||
"Directional Light_test: Component added to the entity: True",
|
||||
"Directional Light_test: Component removed after UNDO: True",
|
||||
"Directional Light_test: Component added after REDO: True",
|
||||
"Directional Light_test: Entered game mode: True",
|
||||
"Directional Light_test: Exit game mode: True",
|
||||
"Directional Light_test: Entity is hidden: True",
|
||||
"Directional Light_test: Entity is shown: True",
|
||||
"Directional Light_test: Entity deleted: True",
|
||||
"Directional Light_test: UNDO entity deletion works: True",
|
||||
"Directional Light_test: REDO entity deletion works: True",
|
||||
# Exposure Control Component
|
||||
"Exposure Control Entity successfully created",
|
||||
"Exposure Control_test: Component added to the entity: True",
|
||||
"Exposure Control_test: Component removed after UNDO: True",
|
||||
"Exposure Control_test: Component added after REDO: True",
|
||||
"Exposure Control_test: Entered game mode: True",
|
||||
"Exposure Control_test: Exit game mode: True",
|
||||
"Exposure Control_test: Entity disabled initially: True",
|
||||
"Exposure Control_test: Entity enabled after adding required components: True",
|
||||
"Exposure Control_test: Entity is hidden: True",
|
||||
"Exposure Control_test: Entity is shown: True",
|
||||
"Exposure Control_test: Entity deleted: True",
|
||||
"Exposure Control_test: UNDO entity deletion works: True",
|
||||
"Exposure Control_test: REDO entity deletion works: True",
|
||||
# Global Skylight (IBL) Component
|
||||
"Global Skylight (IBL) Entity successfully created",
|
||||
"Global Skylight (IBL)_test: Component added to the entity: True",
|
||||
"Global Skylight (IBL)_test: Component removed after UNDO: True",
|
||||
"Global Skylight (IBL)_test: Component added after REDO: True",
|
||||
"Global Skylight (IBL)_test: Entered game mode: True",
|
||||
"Global Skylight (IBL)_test: Exit game mode: True",
|
||||
"Global Skylight (IBL) Controller|Configuration|Diffuse Image: SUCCESS",
|
||||
"Global Skylight (IBL) Controller|Configuration|Specular Image: SUCCESS",
|
||||
"Global Skylight (IBL)_test: Entity is hidden: True",
|
||||
"Global Skylight (IBL)_test: Entity is shown: True",
|
||||
"Global Skylight (IBL)_test: Entity deleted: True",
|
||||
"Global Skylight (IBL)_test: UNDO entity deletion works: True",
|
||||
"Global Skylight (IBL)_test: REDO entity deletion works: True",
|
||||
# Physical Sky Component
|
||||
"Physical Sky Entity successfully created",
|
||||
"Physical Sky component was added to entity",
|
||||
"Entity has a Physical Sky component",
|
||||
"Physical Sky_test: Component added to the entity: True",
|
||||
"Physical Sky_test: Component removed after UNDO: True",
|
||||
"Physical Sky_test: Component added after REDO: True",
|
||||
"Physical Sky_test: Entered game mode: True",
|
||||
"Physical Sky_test: Exit game mode: True",
|
||||
"Physical Sky_test: Entity is hidden: True",
|
||||
"Physical Sky_test: Entity is shown: True",
|
||||
"Physical Sky_test: Entity deleted: True",
|
||||
"Physical Sky_test: UNDO entity deletion works: True",
|
||||
"Physical Sky_test: REDO entity deletion works: True",
|
||||
# PostFX Layer Component
|
||||
"PostFX Layer Entity successfully created",
|
||||
"PostFX Layer_test: Component added to the entity: True",
|
||||
"PostFX Layer_test: Component removed after UNDO: True",
|
||||
"PostFX Layer_test: Component added after REDO: True",
|
||||
"PostFX Layer_test: Entered game mode: True",
|
||||
"PostFX Layer_test: Exit game mode: True",
|
||||
"PostFX Layer_test: Entity is hidden: True",
|
||||
"PostFX Layer_test: Entity is shown: True",
|
||||
"PostFX Layer_test: Entity deleted: True",
|
||||
"PostFX Layer_test: UNDO entity deletion works: True",
|
||||
"PostFX Layer_test: REDO entity deletion works: True",
|
||||
# PostFX Radius Weight Modifier Component
|
||||
"PostFX Radius Weight Modifier Entity successfully created",
|
||||
"PostFX Radius Weight Modifier_test: Component added to the entity: True",
|
||||
"PostFX Radius Weight Modifier_test: Component removed after UNDO: True",
|
||||
"PostFX Radius Weight Modifier_test: Component added after REDO: True",
|
||||
"PostFX Radius Weight Modifier_test: Entered game mode: True",
|
||||
"PostFX Radius Weight Modifier_test: Exit game mode: True",
|
||||
"PostFX Radius Weight Modifier_test: Entity is hidden: True",
|
||||
"PostFX Radius Weight Modifier_test: Entity is shown: True",
|
||||
"PostFX Radius Weight Modifier_test: Entity deleted: True",
|
||||
"PostFX Radius Weight Modifier_test: UNDO entity deletion works: True",
|
||||
"PostFX Radius Weight Modifier_test: REDO entity deletion works: True",
|
||||
# Light Component
|
||||
"Light Entity successfully created",
|
||||
"Light_test: Component added to the entity: True",
|
||||
"Light_test: Component removed after UNDO: True",
|
||||
"Light_test: Component added after REDO: True",
|
||||
"Light_test: Entered game mode: True",
|
||||
"Light_test: Exit game mode: True",
|
||||
"Light_test: Entity is hidden: True",
|
||||
"Light_test: Entity is shown: True",
|
||||
"Light_test: Entity deleted: True",
|
||||
"Light_test: UNDO entity deletion works: True",
|
||||
"Light_test: REDO entity deletion works: True",
|
||||
# Display Mapper Component
|
||||
"Display Mapper Entity successfully created",
|
||||
"Display Mapper_test: Component added to the entity: True",
|
||||
"Display Mapper_test: Component removed after UNDO: True",
|
||||
"Display Mapper_test: Component added after REDO: True",
|
||||
"Display Mapper_test: Entered game mode: True",
|
||||
"Display Mapper_test: Exit game mode: True",
|
||||
"Display Mapper_test: Entity is hidden: True",
|
||||
"Display Mapper_test: Entity is shown: True",
|
||||
"Display Mapper_test: Entity deleted: True",
|
||||
"Display Mapper_test: UNDO entity deletion works: True",
|
||||
"Display Mapper_test: REDO entity deletion works: True",
|
||||
]
|
||||
@pytest.mark.test_case_id("C32078118")
|
||||
class AtomEditorComponents_DecalAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_DecalAdded as test_module
|
||||
|
||||
unexpected_lines = [
|
||||
"Trace::Assert",
|
||||
"Trace::Error",
|
||||
"Traceback (most recent call last):",
|
||||
]
|
||||
@pytest.mark.test_case_id("C36525658")
|
||||
class AtomEditorComponents_DeferredFogAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_DeferredFogAdded as test_module
|
||||
|
||||
hydra.launch_and_validate_results(
|
||||
request,
|
||||
TEST_DIRECTORY,
|
||||
editor,
|
||||
"hydra_AtomEditorComponents_AddedToEntity.py",
|
||||
timeout=120,
|
||||
expected_lines=expected_lines,
|
||||
unexpected_lines=unexpected_lines,
|
||||
halt_on_unexpected=True,
|
||||
null_renderer=True,
|
||||
cfg_args=cfg_args,
|
||||
)
|
||||
@pytest.mark.test_case_id("C32078119")
|
||||
class AtomEditorComponents_DepthOfFieldAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_DepthOfFieldAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C34525095")
|
||||
def test_AtomEditorComponents_LightComponent(
|
||||
self, request, editor, workspace, project, launcher_platform, level):
|
||||
"""
|
||||
Please review the hydra script run by this test for more specific test info.
|
||||
Tests that the Light component has the expected property options available to it.
|
||||
"""
|
||||
cfg_args = [level]
|
||||
@pytest.mark.test_case_id("C32078120")
|
||||
class AtomEditorComponents_DirectionalLightAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_DirectionalLightAdded as test_module
|
||||
|
||||
expected_lines = [
|
||||
"light_entity Entity successfully created",
|
||||
"Entity has a Light component",
|
||||
"light_entity_test: Component added to the entity: True",
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['sphere']} which matches {LIGHT_TYPES['sphere']}",
|
||||
"Controller|Configuration|Shadows|Enable shadow set to True",
|
||||
"light_entity Controller|Configuration|Shadows|Shadowmap size: SUCCESS",
|
||||
"Controller|Configuration|Shadows|Shadow filter method set to 1", # PCF
|
||||
"Controller|Configuration|Shadows|Filtering sample count set to 4",
|
||||
"Controller|Configuration|Shadows|Filtering sample count set to 64",
|
||||
"Controller|Configuration|Shadows|Shadow filter method set to 2", # ESM
|
||||
"Controller|Configuration|Shadows|ESM exponent set to 50.0",
|
||||
"Controller|Configuration|Shadows|ESM exponent set to 5000.0",
|
||||
"Controller|Configuration|Shadows|Shadow filter method set to 3", # ESM+PCF
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['spot_disk']} which matches {LIGHT_TYPES['spot_disk']}",
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['capsule']} which matches {LIGHT_TYPES['capsule']}",
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['quad']} which matches {LIGHT_TYPES['quad']}",
|
||||
"light_entity Controller|Configuration|Fast approximation: SUCCESS",
|
||||
"light_entity Controller|Configuration|Both directions: SUCCESS",
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['polygon']} which matches {LIGHT_TYPES['polygon']}",
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['simple_point']} "
|
||||
f"which matches {LIGHT_TYPES['simple_point']}",
|
||||
"Controller|Configuration|Attenuation radius|Mode set to 0",
|
||||
"Controller|Configuration|Attenuation radius|Radius set to 100.0",
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['simple_spot']} "
|
||||
f"which matches {LIGHT_TYPES['simple_spot']}",
|
||||
"Controller|Configuration|Shutters|Outer angle set to 45.0",
|
||||
"Controller|Configuration|Shutters|Outer angle set to 90.0",
|
||||
"light_entity_test: Component added to the entity: True",
|
||||
"Light component test (non-GPU) completed.",
|
||||
]
|
||||
@pytest.mark.test_case_id("C36525660")
|
||||
class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module
|
||||
|
||||
unexpected_lines = [
|
||||
"Trace::Assert",
|
||||
"Trace::Error",
|
||||
"Traceback (most recent call last):",
|
||||
]
|
||||
@pytest.mark.test_case_id("C32078121")
|
||||
class AtomEditorComponents_ExposureControlAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_ExposureControlAdded as test_module
|
||||
|
||||
hydra.launch_and_validate_results(
|
||||
request,
|
||||
TEST_DIRECTORY,
|
||||
editor,
|
||||
"hydra_AtomEditorComponents_LightComponent.py",
|
||||
timeout=120,
|
||||
expected_lines=expected_lines,
|
||||
unexpected_lines=unexpected_lines,
|
||||
halt_on_unexpected=True,
|
||||
null_renderer=True,
|
||||
cfg_args=cfg_args,
|
||||
)
|
||||
@pytest.mark.test_case_id("C32078115")
|
||||
class AtomEditorComponents_GlobalSkylightIBLAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_GlobalSkylightIBLAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078122")
|
||||
class AtomEditorComponents_GridAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_GridAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C36525671")
|
||||
class AtomEditorComponents_HDRColorGradingAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_HDRColorGradingAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078117")
|
||||
class AtomEditorComponents_LightAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_LightAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078123")
|
||||
class AtomEditorComponents_MaterialAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_MaterialAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078124")
|
||||
class AtomEditorComponents_MeshAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_MeshAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C36525663")
|
||||
class AtomEditorComponents_OcclusionCullingPlaneAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_OcclusionCullingPlaneAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078125")
|
||||
class AtomEditorComponents_PhysicalSkyAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_PhysicalSkyAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C36525664")
|
||||
class AtomEditorComponents_PostFXGradientWeightModifierAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078127")
|
||||
class AtomEditorComponents_PostFXLayerAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_PostFXLayerAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078131")
|
||||
class AtomEditorComponents_PostFXRadiusWeightModifierAdded(EditorSharedTest):
|
||||
from Atom.tests import (
|
||||
hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded as test_module)
|
||||
|
||||
@pytest.mark.test_case_id("C36525665")
|
||||
class AtomEditorComponents_PostFXShapeWeightModifierAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078128")
|
||||
class AtomEditorComponents_ReflectionProbeAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_ReflectionProbeAdded as test_module
|
||||
|
||||
class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest):
|
||||
from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module
|
||||
|
||||
@@ -13,10 +13,10 @@ import zipfile
|
||||
import pytest
|
||||
|
||||
import ly_test_tools.environment.file_system as file_system
|
||||
from ly_test_tools.image.screenshot_compare_qssim import qssim as compare_screenshots
|
||||
from ly_test_tools.benchmark.data_aggregator import BenchmarkDataAggregator
|
||||
|
||||
import editor_python_test_tools.hydra_test_utils as hydra
|
||||
from .atom_utils.atom_component_helper import compare_screenshot_similarity, ImageComparisonTestFailure
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots'
|
||||
@@ -69,7 +69,7 @@ def create_screenshots_archive(screenshot_path):
|
||||
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("launcher_platform", ["windows_editor"])
|
||||
@pytest.mark.parametrize("level", ["auto_test"])
|
||||
@pytest.mark.parametrize("level", ["Base"])
|
||||
class TestAllComponentsIndepthTests(object):
|
||||
|
||||
@pytest.mark.parametrize("screenshot_name", ["AtomBasicLevelSetup.ppm"])
|
||||
@@ -91,12 +91,7 @@ class TestAllComponentsIndepthTests(object):
|
||||
"Viewport is set to the expected size: True",
|
||||
"Exited game mode"
|
||||
]
|
||||
unexpected_lines = [
|
||||
"Trace::Assert",
|
||||
"Trace::Error",
|
||||
"Traceback (most recent call last):",
|
||||
"Screenshot failed"
|
||||
]
|
||||
unexpected_lines = ["Traceback (most recent call last):"]
|
||||
|
||||
hydra.launch_and_validate_results(
|
||||
request,
|
||||
@@ -111,10 +106,12 @@ class TestAllComponentsIndepthTests(object):
|
||||
null_renderer=False,
|
||||
)
|
||||
|
||||
for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images):
|
||||
compare_screenshots(test_screenshot, golden_screenshot)
|
||||
|
||||
create_screenshots_archive(screenshot_directory)
|
||||
similarity_threshold = 0.99
|
||||
for test_screenshot, golden_image in zip(test_screenshots, golden_images):
|
||||
screenshot_comparison_result = compare_screenshot_similarity(
|
||||
test_screenshot, golden_image, similarity_threshold, True, screenshot_directory)
|
||||
if screenshot_comparison_result != "Screenshots match":
|
||||
raise Exception(f"Screenshot test failed: {screenshot_comparison_result}")
|
||||
|
||||
@pytest.mark.test_case_id("C34525095")
|
||||
def test_LightComponent_ScreenshotMatchesGoldenImage(
|
||||
@@ -149,12 +146,7 @@ class TestAllComponentsIndepthTests(object):
|
||||
golden_images.append(golden_image_path)
|
||||
|
||||
expected_lines = ["spot_light Controller|Configuration|Shadows|Shadowmap size: SUCCESS"]
|
||||
unexpected_lines = [
|
||||
"Trace::Assert",
|
||||
"Trace::Error",
|
||||
"Traceback (most recent call last):",
|
||||
"Screenshot failed",
|
||||
]
|
||||
unexpected_lines = ["Traceback (most recent call last):"]
|
||||
hydra.launch_and_validate_results(
|
||||
request,
|
||||
TEST_DIRECTORY,
|
||||
@@ -168,10 +160,12 @@ class TestAllComponentsIndepthTests(object):
|
||||
null_renderer=False,
|
||||
)
|
||||
|
||||
for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images):
|
||||
compare_screenshots(test_screenshot, golden_screenshot)
|
||||
|
||||
create_screenshots_archive(screenshot_directory)
|
||||
similarity_threshold = 0.99
|
||||
for test_screenshot, golden_image in zip(test_screenshots, golden_images):
|
||||
screenshot_comparison_result = compare_screenshot_similarity(
|
||||
test_screenshot, golden_image, similarity_threshold, True, screenshot_directory)
|
||||
if screenshot_comparison_result != "Screenshots match":
|
||||
raise ImageComparisonTestFailure(f"Screenshot test failed: {screenshot_comparison_result}")
|
||||
|
||||
|
||||
@pytest.mark.parametrize('rhi', ['dx12', 'vulkan'])
|
||||
@@ -219,7 +213,6 @@ class TestPerformanceBenchmarkSuite(object):
|
||||
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_generic'])
|
||||
@pytest.mark.system
|
||||
class TestMaterialEditor(object):
|
||||
|
||||
@pytest.mark.parametrize("cfg_args,expected_lines", [
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.")
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
class TestAutomation(EditorTestSuite):
|
||||
|
||||
@pytest.mark.test_case_id("C32078118")
|
||||
class AtomEditorComponents_DecalAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_DecalAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078119")
|
||||
class AtomEditorComponents_DepthOfFieldAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_DepthOfFieldAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078120")
|
||||
class AtomEditorComponents_DirectionalLightAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_DirectionalLightAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C36525660")
|
||||
class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078121")
|
||||
class AtomEditorComponents_ExposureControlAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_ExposureControlAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078115")
|
||||
class AtomEditorComponents_GlobalSkylightIBLAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_GlobalSkylightIBLAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078122")
|
||||
class AtomEditorComponents_GridAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_GridAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078117")
|
||||
class AtomEditorComponents_LightAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_LightAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078123")
|
||||
class AtomEditorComponents_MaterialAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_MaterialAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078124")
|
||||
class AtomEditorComponents_MeshAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_MeshAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078125")
|
||||
class AtomEditorComponents_PhysicalSkyAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_PhysicalSkyAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C36525664")
|
||||
class AtomEditorComponents_PostFXGradientWeightModifierAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078127")
|
||||
class AtomEditorComponents_PostFXLayerAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_PostFXLayerAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078131")
|
||||
class AtomEditorComponents_PostFXRadiusWeightModifierAdded(EditorSharedTest):
|
||||
from Atom.tests import (
|
||||
hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded as test_module)
|
||||
|
||||
@pytest.mark.test_case_id("C36525665")
|
||||
class AtomEditorComponents_PostFXShapeWeightModifierAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C32078128")
|
||||
class AtomEditorComponents_ReflectionProbeAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponents_ReflectionProbeAdded as test_module
|
||||
|
||||
class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest):
|
||||
from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module
|
||||
@@ -9,63 +9,84 @@ import os
|
||||
|
||||
import pytest
|
||||
|
||||
import ly_test_tools.environment.file_system as file_system
|
||||
import editor_python_test_tools.hydra_test_utils as hydra
|
||||
|
||||
from Atom.atom_utils.atom_constants import LIGHT_TYPES
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests")
|
||||
|
||||
|
||||
class TestAtomEditorComponentsSandbox(object):
|
||||
|
||||
# It requires at least one test
|
||||
def test_Dummy(self, request, editor, level, workspace, project, launcher_platform):
|
||||
pass
|
||||
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
@pytest.mark.parametrize("level", ["auto_test"])
|
||||
class TestAtomEditorComponentsMain(object):
|
||||
"""Holds tests for Atom components."""
|
||||
|
||||
@pytest.mark.test_case_id("C32078128")
|
||||
def test_AtomEditorComponents_ReflectionProbeAddedToEntity(
|
||||
self, request, editor, level, workspace, project, launcher_platform):
|
||||
"""
|
||||
Please review the hydra script run by this test for more specific test info.
|
||||
Tests the following Atom components and verifies all "expected_lines" appear in Editor.log:
|
||||
1. Reflection Probe
|
||||
"""
|
||||
cfg_args = [level]
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
@pytest.mark.parametrize("level", ["auto_test"])
|
||||
class TestAtomEditorComponentsMain(object):
|
||||
"""Holds tests for Atom components."""
|
||||
|
||||
expected_lines = [
|
||||
# Reflection Probe Component
|
||||
"Reflection Probe Entity successfully created",
|
||||
"Reflection Probe_test: Component added to the entity: True",
|
||||
"Reflection Probe_test: Component removed after UNDO: True",
|
||||
"Reflection Probe_test: Component added after REDO: True",
|
||||
"Reflection Probe_test: Entered game mode: True",
|
||||
"Reflection Probe_test: Exit game mode: True",
|
||||
"Reflection Probe_test: Entity disabled initially: True",
|
||||
"Reflection Probe_test: Entity enabled after adding required components: True",
|
||||
"Reflection Probe_test: Cubemap is generated: True",
|
||||
"Reflection Probe_test: Entity is hidden: True",
|
||||
"Reflection Probe_test: Entity is shown: True",
|
||||
"Reflection Probe_test: Entity deleted: True",
|
||||
"Reflection Probe_test: UNDO entity deletion works: True",
|
||||
"Reflection Probe_test: REDO entity deletion works: True",
|
||||
]
|
||||
@pytest.mark.test_case_id("C34525095")
|
||||
def test_AtomEditorComponents_LightComponent(
|
||||
self, request, editor, workspace, project, launcher_platform, level):
|
||||
"""
|
||||
Please review the hydra script run by this test for more specific test info.
|
||||
Tests that the Light component has the expected property options available to it.
|
||||
"""
|
||||
cfg_args = [level]
|
||||
|
||||
expected_lines = [
|
||||
"light_entity Entity successfully created",
|
||||
"Entity has a Light component",
|
||||
"light_entity_test: Component added to the entity: True",
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['sphere']} which matches {LIGHT_TYPES['sphere']}",
|
||||
"Controller|Configuration|Shadows|Enable shadow set to True",
|
||||
"light_entity Controller|Configuration|Shadows|Shadowmap size: SUCCESS",
|
||||
"Controller|Configuration|Shadows|Shadow filter method set to 1", # PCF
|
||||
"Controller|Configuration|Shadows|Filtering sample count set to 4",
|
||||
"Controller|Configuration|Shadows|Filtering sample count set to 64",
|
||||
"Controller|Configuration|Shadows|Shadow filter method set to 2", # ESM
|
||||
"Controller|Configuration|Shadows|ESM exponent set to 50.0",
|
||||
"Controller|Configuration|Shadows|ESM exponent set to 5000.0",
|
||||
"Controller|Configuration|Shadows|Shadow filter method set to 3", # ESM+PCF
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['spot_disk']} which matches {LIGHT_TYPES['spot_disk']}",
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['capsule']} which matches {LIGHT_TYPES['capsule']}",
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['quad']} which matches {LIGHT_TYPES['quad']}",
|
||||
"light_entity Controller|Configuration|Fast approximation: SUCCESS",
|
||||
"light_entity Controller|Configuration|Both directions: SUCCESS",
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['polygon']} which matches {LIGHT_TYPES['polygon']}",
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['simple_point']} "
|
||||
f"which matches {LIGHT_TYPES['simple_point']}",
|
||||
"Controller|Configuration|Attenuation radius|Mode set to 0",
|
||||
"Controller|Configuration|Attenuation radius|Radius set to 100.0",
|
||||
f"light_entity_test: Property value is {LIGHT_TYPES['simple_spot']} "
|
||||
f"which matches {LIGHT_TYPES['simple_spot']}",
|
||||
"Controller|Configuration|Shutters|Outer angle set to 45.0",
|
||||
"Controller|Configuration|Shutters|Outer angle set to 90.0",
|
||||
"light_entity_test: Component added to the entity: True",
|
||||
"Light component test (non-GPU) completed.",
|
||||
]
|
||||
|
||||
unexpected_lines = ["Traceback (most recent call last):"]
|
||||
|
||||
hydra.launch_and_validate_results(
|
||||
request,
|
||||
TEST_DIRECTORY,
|
||||
editor,
|
||||
"hydra_AtomEditorComponents_LightComponent.py",
|
||||
timeout=120,
|
||||
expected_lines=expected_lines,
|
||||
unexpected_lines=unexpected_lines,
|
||||
halt_on_unexpected=True,
|
||||
null_renderer=True,
|
||||
cfg_args=cfg_args,
|
||||
)
|
||||
|
||||
hydra.launch_and_validate_results(
|
||||
request,
|
||||
TEST_DIRECTORY,
|
||||
editor,
|
||||
"hydra_AtomEditorComponents_AddedToEntity.py",
|
||||
timeout=120,
|
||||
expected_lines=expected_lines,
|
||||
unexpected_lines=[],
|
||||
halt_on_unexpected=True,
|
||||
null_renderer=True,
|
||||
cfg_args=cfg_args,
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_generic'])
|
||||
@@ -119,8 +140,6 @@ class TestMaterialEditorBasicTests(object):
|
||||
"Save All worked as expected: True",
|
||||
]
|
||||
unexpected_lines = [
|
||||
# "Trace::Assert",
|
||||
# "Trace::Error",
|
||||
"Traceback (most recent call last):"
|
||||
]
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -8,12 +9,19 @@ import datetime
|
||||
import os
|
||||
import zipfile
|
||||
|
||||
from ly_test_tools.image.screenshot_compare_qssim import qssim as compare_screenshots
|
||||
|
||||
|
||||
class ImageComparisonTestFailure(Exception):
|
||||
"""Custom test failure for failed image comparisons."""
|
||||
pass
|
||||
|
||||
|
||||
def create_screenshots_archive(screenshot_path):
|
||||
"""
|
||||
Creates a new zip file archive at archive_path containing all files listed within archive_path.
|
||||
:param screenshot_path: location containing the files to archive, the zip archive file will also be saved here.
|
||||
:return: None, but creates a new zip file archive inside path containing all of the files inside archive_path.
|
||||
:return: path to the created .zip file archive.
|
||||
"""
|
||||
files_to_archive = []
|
||||
|
||||
@@ -27,14 +35,16 @@ def create_screenshots_archive(screenshot_path):
|
||||
# Setup variables for naming the zip archive file.
|
||||
timestamp = datetime.datetime.now().timestamp()
|
||||
formatted_timestamp = datetime.datetime.utcfromtimestamp(timestamp).strftime("%Y-%m-%d_%H-%M-%S")
|
||||
screenshots_file = os.path.join(screenshot_path, f'screenshots_{formatted_timestamp}.zip')
|
||||
screenshots_zip_file = os.path.join(screenshot_path, f'screenshots_{formatted_timestamp}.zip')
|
||||
|
||||
# Write all of the valid .png and .ppm files to the archive file.
|
||||
with zipfile.ZipFile(screenshots_file, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as zip_archive:
|
||||
with zipfile.ZipFile(screenshots_zip_file, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as zip_archive:
|
||||
for file_path in files_to_archive:
|
||||
file_name = os.path.basename(file_path)
|
||||
zip_archive.write(file_path, file_name)
|
||||
|
||||
return screenshots_zip_file
|
||||
|
||||
|
||||
def golden_images_directory():
|
||||
"""
|
||||
@@ -53,6 +63,36 @@ def golden_images_directory():
|
||||
return golden_images_dir
|
||||
|
||||
|
||||
def compare_screenshot_similarity(
|
||||
test_screenshot, golden_image, similarity_threshold, create_zip_archive=False, screenshot_directory=""):
|
||||
"""
|
||||
Compares the similarity between a test screenshot and a golden image.
|
||||
It returns a "Screenshots match" string if the comparison mean value is higher than the similarity threshold.
|
||||
Otherwise, it returns an error string.
|
||||
:param test_screenshot: path to the test screenshot to compare.
|
||||
:param golden_image: path to the golden image to compare.
|
||||
:param similarity_threshold: value for the comparison mean value to be asserted against.
|
||||
:param create_zip_archive: toggle to create a zip archive containing the screenshots if the assert check fails.
|
||||
:param screenshot_directory: directory containing screenshots to create zip archive from.
|
||||
:return: Error string if compared mean value < similarity threshold or screenshot_directory is missing for .zip,
|
||||
otherwise it returns a "Screenshots match" string.
|
||||
"""
|
||||
result = "Screenshots match"
|
||||
if create_zip_archive and not screenshot_directory:
|
||||
result = 'You must specify a screenshot_directory in order to create a zip archive.\n'
|
||||
|
||||
mean_similarity = compare_screenshots(test_screenshot, golden_image)
|
||||
if not mean_similarity > similarity_threshold:
|
||||
if create_zip_archive:
|
||||
create_screenshots_archive(screenshot_directory)
|
||||
result = (
|
||||
f"When comparing the test_screenshot: '{test_screenshot}' "
|
||||
f"to golden_image: '{golden_image}' the mean similarity of '{mean_similarity}' "
|
||||
f"was lower than the similarity threshold of '{similarity_threshold}'. ")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def create_basic_atom_level(level_name):
|
||||
"""
|
||||
Creates a new level inside the Editor matching level_name & adds the following:
|
||||
@@ -76,29 +116,11 @@ def create_basic_atom_level(level_name):
|
||||
|
||||
helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper")
|
||||
|
||||
# Create a new level.
|
||||
new_level_name = level_name
|
||||
heightmap_resolution = 512
|
||||
heightmap_meters_per_pixel = 1
|
||||
terrain_texture_resolution = 412
|
||||
use_terrain = False
|
||||
|
||||
# Return codes are ECreateLevelResult defined in CryEdit.h
|
||||
return_code = general.create_level_no_prompt(
|
||||
new_level_name, heightmap_resolution, heightmap_meters_per_pixel, terrain_texture_resolution, use_terrain)
|
||||
if return_code == 1:
|
||||
general.log(f"{new_level_name} level already exists")
|
||||
elif return_code == 2:
|
||||
general.log("Failed to create directory")
|
||||
elif return_code == 3:
|
||||
general.log("Directory length is too long")
|
||||
elif return_code != 0:
|
||||
general.log("Unknown error, failed to create level")
|
||||
else:
|
||||
general.log(f"{new_level_name} level created successfully")
|
||||
|
||||
# Enable idle and update viewport.
|
||||
# Wait for Editor idle loop before executing Python hydra scripts.
|
||||
general.idle_enable(True)
|
||||
|
||||
# Basic setup for opened level.
|
||||
helper.open_level(level_name="Base")
|
||||
general.idle_wait(1.0)
|
||||
general.update_viewport()
|
||||
general.idle_wait(0.5) # half a second is more than enough for updating the viewport.
|
||||
@@ -165,24 +187,25 @@ def create_basic_atom_level(level_name):
|
||||
components=["Material"],
|
||||
parent_id=default_level.id)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalUniformScale", ground_plane.id, 32.0)
|
||||
ground_plane_material_asset_path = os.path.join(
|
||||
"Materials", "Presets", "PBR", "metal_chrome.azmaterial")
|
||||
ground_plane_material_asset_value = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False)
|
||||
ground_plane.get_set_test(0, "Default Material|Material Asset", ground_plane_material_asset_value)
|
||||
|
||||
# Work around to add the correct Atom Mesh component
|
||||
# Work around to add the correct Atom Mesh component and asset.
|
||||
mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId
|
||||
ground_plane.components.append(
|
||||
editor.EditorComponentAPIBus(
|
||||
bus.Broadcast, "AddComponentsOfType", ground_plane.id, [mesh_type_id]
|
||||
).GetValue()[0]
|
||||
)
|
||||
ground_plane_mesh_asset_path = os.path.join("Models", "plane.azmodel")
|
||||
ground_plane_mesh_asset_path = os.path.join("TestData", "Objects", "plane.azmodel")
|
||||
ground_plane_mesh_asset_value = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", ground_plane_mesh_asset_path, math.Uuid(), False)
|
||||
ground_plane.get_set_test(1, "Controller|Configuration|Mesh Asset", ground_plane_mesh_asset_value)
|
||||
|
||||
# Add Atom Material component and asset.
|
||||
ground_plane_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_chrome.azmaterial")
|
||||
ground_plane_material_asset_value = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False)
|
||||
ground_plane.get_set_test(0, "Default Material|Material Asset", ground_plane_material_asset_value)
|
||||
|
||||
# Create directional_light entity and set the properties
|
||||
directional_light = hydra.Entity("directional_light")
|
||||
directional_light.create_entity(
|
||||
@@ -199,12 +222,8 @@ def create_basic_atom_level(level_name):
|
||||
entity_position=math.Vector3(0.0, 0.0, 1.0),
|
||||
components=["Material"],
|
||||
parent_id=default_level.id)
|
||||
sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial")
|
||||
sphere_material_asset_value = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False)
|
||||
sphere_entity.get_set_test(0, "Default Material|Material Asset", sphere_material_asset_value)
|
||||
|
||||
# Work around to add the correct Atom Mesh component
|
||||
# Work around to add the correct Atom Mesh component and asset.
|
||||
sphere_entity.components.append(
|
||||
editor.EditorComponentAPIBus(
|
||||
bus.Broadcast, "AddComponentsOfType", sphere_entity.id, [mesh_type_id]
|
||||
@@ -215,6 +234,12 @@ def create_basic_atom_level(level_name):
|
||||
bus.Broadcast, "GetAssetIdByPath", sphere_mesh_asset_path, math.Uuid(), False)
|
||||
sphere_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", sphere_mesh_asset_value)
|
||||
|
||||
# Add Atom Material component and asset.
|
||||
sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial")
|
||||
sphere_material_asset_value = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False)
|
||||
sphere_entity.get_set_test(0, "Default Material|Material Asset", sphere_material_asset_value)
|
||||
|
||||
# Create camera component and set the properties
|
||||
camera_entity = hydra.Entity("camera")
|
||||
camera_entity.create_entity(
|
||||
|
||||
@@ -42,12 +42,14 @@ class AtomComponentProperties:
|
||||
Bloom component properties. Requires PostFX Layer component.
|
||||
- 'requires' a list of component names as strings required by this component.
|
||||
Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n
|
||||
- 'Enable Bloom' Toggle active state of the component True/False
|
||||
:param property: From the last element of the property tree path. Default 'name' for component name string.
|
||||
:return: Full property path OR component name if no property specified.
|
||||
"""
|
||||
properties = {
|
||||
'name': 'Bloom',
|
||||
'requires': [AtomComponentProperties.postfx_layer()],
|
||||
'Enable Bloom': 'Controller|Configuration|Enable Bloom',
|
||||
}
|
||||
return properties[property]
|
||||
|
||||
@@ -83,12 +85,14 @@ class AtomComponentProperties:
|
||||
Deferred Fog component properties. Requires PostFX Layer component.
|
||||
- 'requires' a list of component names as strings required by this component.
|
||||
Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n
|
||||
- 'Enable Deferred Fog' Toggle active state of the component True/False
|
||||
:param property: From the last element of the property tree path. Default 'name' for component name string.
|
||||
:return: Full property path OR component name if no property specified.
|
||||
"""
|
||||
properties = {
|
||||
'name': 'Deferred Fog',
|
||||
'requires': [AtomComponentProperties.postfx_layer()],
|
||||
'Enable Deferred Fog': 'Controller|Configuration|Enable Deferred Fog',
|
||||
}
|
||||
return properties[property]
|
||||
|
||||
@@ -212,12 +216,14 @@ class AtomComponentProperties:
|
||||
HDR Color Grading component properties. Requires PostFX Layer component.
|
||||
- 'requires' a list of component names as strings required by this component.
|
||||
Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n
|
||||
- 'Enable HDR color grading' Toggle active state of the component True/False
|
||||
:param property: From the last element of the property tree path. Default 'name' for component name string.
|
||||
:return: Full property path OR component name if no property specified.
|
||||
"""
|
||||
properties = {
|
||||
'name': 'HDR Color Grading',
|
||||
'requires': [AtomComponentProperties.postfx_layer()],
|
||||
'Enable HDR color grading': 'Controller|Configuration|Enable HDR color grading',
|
||||
}
|
||||
return properties[property]
|
||||
|
||||
|
||||
-238
@@ -1,238 +0,0 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import azlmbr.math as math
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.paths
|
||||
import azlmbr.asset as asset
|
||||
import azlmbr.entity as entity
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.editor as editor
|
||||
import azlmbr.render as render
|
||||
|
||||
sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
|
||||
|
||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
||||
from editor_python_test_tools.utils import TestHelper
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Summary:
|
||||
The below common tests are done for each of the components.
|
||||
1) Addition of component to the entity
|
||||
2) UNDO/REDO of addition of component
|
||||
3) Enter/Exit game mode
|
||||
4) Hide/Show entity containing component
|
||||
5) Deletion of component
|
||||
6) UNDO/REDO of deletion of component
|
||||
Some additional tests for specific components include
|
||||
1) Assigning value to some properties of each component
|
||||
2) Verifying if the component is activated only when the required components are added
|
||||
|
||||
Expected Result:
|
||||
1) Component can be added to an entity.
|
||||
2) The addition of component can be undone and redone.
|
||||
3) Game mode can be entered/exited without issue.
|
||||
4) Entity with component can be hidden/shown.
|
||||
5) Component can be deleted.
|
||||
6) The deletion of component can be undone and redone.
|
||||
7) Component is activated only when the required components are added
|
||||
8) Values can be assigned to the properties of the component
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
def create_entity_undo_redo_component_addition(component_name):
|
||||
new_entity = hydra.Entity(f"{component_name}")
|
||||
new_entity.create_entity(math.Vector3(512.0, 512.0, 34.0), [component_name])
|
||||
general.log(f"{component_name}_test: Component added to the entity: "
|
||||
f"{hydra.has_components(new_entity.id, [component_name])}")
|
||||
|
||||
# undo component addition
|
||||
general.undo()
|
||||
TestHelper.wait_for_condition(lambda: not hydra.has_components(new_entity.id, [component_name]), 2.0)
|
||||
general.log(f"{component_name}_test: Component removed after UNDO: "
|
||||
f"{not hydra.has_components(new_entity.id, [component_name])}")
|
||||
|
||||
# redo component addition
|
||||
general.redo()
|
||||
TestHelper.wait_for_condition(lambda: hydra.has_components(new_entity.id, [component_name]), 2.0)
|
||||
general.log(f"{component_name}_test: Component added after REDO: "
|
||||
f"{hydra.has_components(new_entity.id, [component_name])}")
|
||||
|
||||
return new_entity
|
||||
|
||||
def verify_enter_exit_game_mode(component_name):
|
||||
general.enter_game_mode()
|
||||
TestHelper.wait_for_condition(lambda: general.is_in_game_mode(), 2.0)
|
||||
general.log(f"{component_name}_test: Entered game mode: {general.is_in_game_mode()}")
|
||||
general.exit_game_mode()
|
||||
TestHelper.wait_for_condition(lambda: not general.is_in_game_mode(), 2.0)
|
||||
general.log(f"{component_name}_test: Exit game mode: {not general.is_in_game_mode()}")
|
||||
|
||||
def verify_hide_unhide_entity(component_name, entity_obj):
|
||||
|
||||
def is_entity_hidden(entity_id):
|
||||
return editor.EditorEntityInfoRequestBus(bus.Event, "IsHidden", entity_id)
|
||||
|
||||
editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", entity_obj.id, False)
|
||||
general.idle_wait_frames(1)
|
||||
general.log(f"{component_name}_test: Entity is hidden: {is_entity_hidden(entity_obj.id)}")
|
||||
editor.EditorEntityAPIBus(bus.Event, "SetVisibilityState", entity_obj.id, True)
|
||||
general.idle_wait_frames(1)
|
||||
general.log(f"{component_name}_test: Entity is shown: {not is_entity_hidden(entity_obj.id)}")
|
||||
|
||||
def verify_deletion_undo_redo(component_name, entity_obj):
|
||||
editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntityById", entity_obj.id)
|
||||
TestHelper.wait_for_condition(lambda: not hydra.find_entity_by_name(entity_obj.name), 2.0)
|
||||
general.log(f"{component_name}_test: Entity deleted: {not hydra.find_entity_by_name(entity_obj.name)}")
|
||||
|
||||
general.undo()
|
||||
TestHelper.wait_for_condition(lambda: hydra.find_entity_by_name(entity_obj.name) is not None, 2.0)
|
||||
general.log(f"{component_name}_test: UNDO entity deletion works: "
|
||||
f"{hydra.find_entity_by_name(entity_obj.name) is not None}")
|
||||
|
||||
general.redo()
|
||||
TestHelper.wait_for_condition(lambda: not hydra.find_entity_by_name(entity_obj.name), 2.0)
|
||||
general.log(f"{component_name}_test: REDO entity deletion works: "
|
||||
f"{not hydra.find_entity_by_name(entity_obj.name)}")
|
||||
|
||||
def verify_required_component_addition(entity_obj, components_to_add, component_name):
|
||||
|
||||
def is_component_enabled(entity_componentid_pair):
|
||||
return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", entity_componentid_pair)
|
||||
|
||||
general.log(
|
||||
f"{component_name}_test: Entity disabled initially: "
|
||||
f"{not is_component_enabled(entity_obj.components[0])}")
|
||||
for component in components_to_add:
|
||||
entity_obj.add_component(component)
|
||||
TestHelper.wait_for_condition(lambda: is_component_enabled(entity_obj.components[0]), 2.0)
|
||||
general.log(
|
||||
f"{component_name}_test: Entity enabled after adding "
|
||||
f"required components: {is_component_enabled(entity_obj.components[0])}"
|
||||
)
|
||||
|
||||
def verify_set_property(entity_obj, path, value):
|
||||
entity_obj.get_set_test(0, path, value)
|
||||
|
||||
# Verify cubemap generation
|
||||
def verify_cubemap_generation(component_name, entity_obj):
|
||||
# Initially Check if the component has Reflection Probe component
|
||||
if not hydra.has_components(entity_obj.id, ["Reflection Probe"]):
|
||||
raise ValueError(f"Given entity {entity_obj.name} has no Reflection Probe component")
|
||||
render.EditorReflectionProbeBus(azlmbr.bus.Event, "BakeReflectionProbe", entity_obj.id)
|
||||
|
||||
def get_value():
|
||||
hydra.get_component_property_value(entity_obj.components[0], "Cubemap|Baked Cubemap Path")
|
||||
|
||||
TestHelper.wait_for_condition(lambda: get_value() != "", 20.0)
|
||||
general.log(f"{component_name}_test: Cubemap is generated: {get_value() != ''}")
|
||||
|
||||
# Wait for Editor idle loop before executing Python hydra scripts.
|
||||
TestHelper.init_idle()
|
||||
|
||||
# Delete all existing entities initially
|
||||
search_filter = azlmbr.entity.SearchFilter()
|
||||
all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter)
|
||||
editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities)
|
||||
|
||||
class ComponentTests:
|
||||
"""Test launcher for each component."""
|
||||
def __init__(self, component_name, *additional_tests):
|
||||
self.component_name = component_name
|
||||
self.additional_tests = additional_tests
|
||||
self.run_component_tests()
|
||||
|
||||
def run_component_tests(self):
|
||||
# Run common and additional tests
|
||||
entity_obj = create_entity_undo_redo_component_addition(self.component_name)
|
||||
|
||||
# Enter/Exit game mode test
|
||||
verify_enter_exit_game_mode(self.component_name)
|
||||
|
||||
# Any additional tests are executed here
|
||||
for test in self.additional_tests:
|
||||
test(entity_obj)
|
||||
|
||||
# Hide/Unhide entity test
|
||||
verify_hide_unhide_entity(self.component_name, entity_obj)
|
||||
|
||||
# Deletion/Undo/Redo test
|
||||
verify_deletion_undo_redo(self.component_name, entity_obj)
|
||||
|
||||
# DepthOfField Component
|
||||
camera_entity = hydra.Entity("camera_entity")
|
||||
camera_entity.create_entity(math.Vector3(512.0, 512.0, 34.0), ["Camera"])
|
||||
depth_of_field = "DepthOfField"
|
||||
ComponentTests(
|
||||
depth_of_field,
|
||||
lambda entity_obj: verify_required_component_addition(entity_obj, ["PostFX Layer"], depth_of_field),
|
||||
lambda entity_obj: verify_set_property(
|
||||
entity_obj, "Controller|Configuration|Camera Entity", camera_entity.id))
|
||||
|
||||
# Decal Component
|
||||
material_asset_path = os.path.join("AutomatedTesting", "Materials", "basic_grey.material")
|
||||
material_asset = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", material_asset_path, math.Uuid(), False)
|
||||
ComponentTests(
|
||||
"Decal", lambda entity_obj: verify_set_property(
|
||||
entity_obj, "Controller|Configuration|Material", material_asset))
|
||||
|
||||
# Directional Light Component
|
||||
ComponentTests(
|
||||
"Directional Light",
|
||||
lambda entity_obj: verify_set_property(
|
||||
entity_obj, "Controller|Configuration|Shadow|Camera", camera_entity.id))
|
||||
|
||||
# Exposure Control Component
|
||||
ComponentTests(
|
||||
"Exposure Control", lambda entity_obj: verify_required_component_addition(
|
||||
entity_obj, ["PostFX Layer"], "Exposure Control"))
|
||||
|
||||
# Global Skylight (IBL) Component
|
||||
diffuse_image_path = os.path.join("LightingPresets", "greenwich_park_02_4k_iblskyboxcm.exr.streamingimage")
|
||||
diffuse_image_asset = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", diffuse_image_path, math.Uuid(), False)
|
||||
specular_image_path = os.path.join("LightingPresets", "greenwich_park_02_4k_iblskyboxcm.exr.streamingimage")
|
||||
specular_image_asset = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", specular_image_path, math.Uuid(), False)
|
||||
ComponentTests(
|
||||
"Global Skylight (IBL)",
|
||||
lambda entity_obj: verify_set_property(
|
||||
entity_obj, "Controller|Configuration|Diffuse Image", diffuse_image_asset),
|
||||
lambda entity_obj: verify_set_property(
|
||||
entity_obj, "Controller|Configuration|Specular Image", specular_image_asset))
|
||||
|
||||
# Physical Sky Component
|
||||
ComponentTests("Physical Sky")
|
||||
|
||||
# PostFX Layer Component
|
||||
ComponentTests("PostFX Layer")
|
||||
|
||||
# PostFX Radius Weight Modifier Component
|
||||
ComponentTests("PostFX Radius Weight Modifier")
|
||||
|
||||
# Light Component
|
||||
ComponentTests("Light")
|
||||
|
||||
# Display Mapper Component
|
||||
ComponentTests("Display Mapper")
|
||||
|
||||
# Reflection Probe Component
|
||||
reflection_probe = "Reflection Probe"
|
||||
ComponentTests(
|
||||
reflection_probe,
|
||||
lambda entity_obj: verify_required_component_addition(entity_obj, ["Box Shape"], reflection_probe),
|
||||
lambda entity_obj: verify_cubemap_generation(reflection_probe, entity_obj),)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@@ -0,0 +1,189 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
class Tests:
|
||||
creation_undo = (
|
||||
"UNDO Entity creation success",
|
||||
"UNDO Entity creation failed")
|
||||
creation_redo = (
|
||||
"REDO Entity creation success",
|
||||
"REDO Entity creation failed")
|
||||
bloom_creation = (
|
||||
"Bloom Entity successfully created",
|
||||
"Bloom Entity failed to be created")
|
||||
bloom_component = (
|
||||
"Entity has a Bloom component",
|
||||
"Entity failed to find Bloom component")
|
||||
bloom_disabled = (
|
||||
"Bloom component disabled",
|
||||
"Bloom component was not disabled")
|
||||
postfx_layer_component = (
|
||||
"Entity has a PostFX Layer component",
|
||||
"Entity did not have an PostFX Layer component")
|
||||
bloom_enabled = (
|
||||
"Bloom component enabled",
|
||||
"Bloom component was not enabled")
|
||||
enable_bloom_parameter_enabled = (
|
||||
"Enable Bloom parameter enabled",
|
||||
"Enable Bloom parameter was not enabled")
|
||||
enter_game_mode = (
|
||||
"Entered game mode",
|
||||
"Failed to enter game mode")
|
||||
exit_game_mode = (
|
||||
"Exited game mode",
|
||||
"Couldn't exit game mode")
|
||||
is_visible = (
|
||||
"Entity is visible",
|
||||
"Entity was not visible")
|
||||
is_hidden = (
|
||||
"Entity is hidden",
|
||||
"Entity was not hidden")
|
||||
entity_deleted = (
|
||||
"Entity deleted",
|
||||
"Entity was not deleted")
|
||||
deletion_undo = (
|
||||
"UNDO deletion success",
|
||||
"UNDO deletion failed")
|
||||
deletion_redo = (
|
||||
"REDO deletion success",
|
||||
"REDO deletion failed")
|
||||
|
||||
|
||||
def AtomEditorComponents_Bloom_AddedToEntity():
|
||||
"""
|
||||
Summary:
|
||||
Tests the Bloom component can be added to an entity and has the expected functionality.
|
||||
|
||||
Test setup:
|
||||
- Wait for Editor idle loop.
|
||||
- Open the "Base" level.
|
||||
|
||||
Expected Behavior:
|
||||
The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
|
||||
Creation and deletion undo/redo should also work.
|
||||
|
||||
Test Steps:
|
||||
1) Create an Bloom entity with no components.
|
||||
2) Add Bloom component to Bloom entity.
|
||||
3) UNDO the entity creation and component addition.
|
||||
4) REDO the entity creation and component addition.
|
||||
5) Verify Bloom component not enabled.
|
||||
6) Add PostFX Layer component since it is required by the Bloom component.
|
||||
7) Verify Bloom component is enabled.
|
||||
8) Enable the "Enable Bloom" parameter.
|
||||
9) Enter/Exit game mode.
|
||||
10) Test IsHidden.
|
||||
11) Test IsVisible.
|
||||
12) Delete Bloom entity.
|
||||
13) UNDO deletion.
|
||||
14) REDO deletion.
|
||||
15) Look for errors.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.utils import Report, Tracer, TestHelper
|
||||
from Atom.atom_utils.atom_constants import AtomComponentProperties
|
||||
|
||||
with Tracer() as error_tracer:
|
||||
# Test setup begins.
|
||||
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
|
||||
TestHelper.init_idle()
|
||||
TestHelper.open_level("", "Base")
|
||||
|
||||
# Test steps begin.
|
||||
# 1. Create an Bloom entity with no components.
|
||||
bloom_entity = EditorEntity.create_editor_entity(AtomComponentProperties.bloom())
|
||||
Report.critical_result(Tests.bloom_creation, bloom_entity.exists())
|
||||
|
||||
# 2. Add Bloom component to Bloom entity.
|
||||
bloom_component = bloom_entity.add_component(AtomComponentProperties.bloom())
|
||||
Report.critical_result(Tests.bloom_component, bloom_entity.has_component(AtomComponentProperties.bloom()))
|
||||
|
||||
# 3. UNDO the entity creation and component addition.
|
||||
# -> UNDO component addition.
|
||||
general.undo()
|
||||
# -> UNDO naming entity.
|
||||
general.undo()
|
||||
# -> UNDO selecting entity.
|
||||
general.undo()
|
||||
# -> UNDO entity creation.
|
||||
general.undo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_undo, not bloom_entity.exists())
|
||||
|
||||
# 4. REDO the entity creation and component addition.
|
||||
# -> REDO entity creation.
|
||||
general.redo()
|
||||
# -> REDO selecting entity.
|
||||
general.redo()
|
||||
# -> REDO naming entity.
|
||||
general.redo()
|
||||
# -> REDO component addition.
|
||||
general.redo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_redo, bloom_entity.exists())
|
||||
|
||||
# 5. Verify Bloom component not enabled.
|
||||
Report.result(Tests.bloom_disabled, not bloom_component.is_enabled())
|
||||
|
||||
# 6. Add PostFX Layer component since it is required by the Bloom component.
|
||||
bloom_entity.add_component(AtomComponentProperties.postfx_layer())
|
||||
Report.result(
|
||||
Tests.postfx_layer_component,
|
||||
bloom_entity.has_component(AtomComponentProperties.postfx_layer()))
|
||||
|
||||
# 7. Verify Bloom component is enabled.
|
||||
Report.result(Tests.bloom_enabled, bloom_component.is_enabled())
|
||||
|
||||
# 8. Enable the "Enable Bloom" parameter.
|
||||
bloom_component.set_component_property_value(AtomComponentProperties.bloom('Enable Bloom'), True)
|
||||
Report.result(
|
||||
Tests.enable_bloom_parameter_enabled,
|
||||
bloom_component.get_component_property_value(AtomComponentProperties.bloom('Enable Bloom')) is True)
|
||||
|
||||
# 9. Enter/Exit game mode.
|
||||
TestHelper.enter_game_mode(Tests.enter_game_mode)
|
||||
general.idle_wait_frames(1)
|
||||
TestHelper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
# 10. Test IsHidden.
|
||||
bloom_entity.set_visibility_state(False)
|
||||
Report.result(Tests.is_hidden, bloom_entity.is_hidden() is True)
|
||||
|
||||
# 11. Test IsVisible.
|
||||
bloom_entity.set_visibility_state(True)
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.is_visible, bloom_entity.is_visible() is True)
|
||||
|
||||
# 12. Delete Bloom entity.
|
||||
bloom_entity.delete()
|
||||
Report.result(Tests.entity_deleted, not bloom_entity.exists())
|
||||
|
||||
# 13. UNDO deletion.
|
||||
general.undo()
|
||||
Report.result(Tests.deletion_undo, bloom_entity.exists())
|
||||
|
||||
# 14. REDO deletion.
|
||||
general.redo()
|
||||
Report.result(Tests.deletion_redo, not bloom_entity.exists())
|
||||
|
||||
# 15. Look for errors and asserts.
|
||||
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
|
||||
for error_info in error_tracer.errors:
|
||||
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
|
||||
for assert_info in error_tracer.asserts:
|
||||
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(AtomEditorComponents_Bloom_AddedToEntity)
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
class Tests:
|
||||
creation_undo = (
|
||||
"UNDO Entity creation success",
|
||||
"UNDO Entity creation failed")
|
||||
creation_redo = (
|
||||
"REDO Entity creation success",
|
||||
"REDO Entity creation failed")
|
||||
deferred_fog_creation = (
|
||||
"Deferred Fog Entity successfully created",
|
||||
"Deferred Fog Entity failed to be created")
|
||||
deferred_fog_component = (
|
||||
"Entity has a Deferred Fog component",
|
||||
"Entity failed to find Deferred Fog component")
|
||||
deferred_fog_disabled = (
|
||||
"Deferred Fog component disabled",
|
||||
"Deferred Fog component was not disabled")
|
||||
postfx_layer_component = (
|
||||
"Entity has a PostFX Layer component",
|
||||
"Entity did not have an PostFX Layer component")
|
||||
deferred_fog_enabled = (
|
||||
"Deferred Fog component enabled",
|
||||
"Deferred Fog component was not enabled")
|
||||
enable_deferred_fog_parameter_enabled = (
|
||||
"Enable Deferred Fog parameter enabled",
|
||||
"Enable Deferred Fog parameter was not enabled")
|
||||
enter_game_mode = (
|
||||
"Entered game mode",
|
||||
"Failed to enter game mode")
|
||||
exit_game_mode = (
|
||||
"Exited game mode",
|
||||
"Couldn't exit game mode")
|
||||
is_visible = (
|
||||
"Entity is visible",
|
||||
"Entity was not visible")
|
||||
is_hidden = (
|
||||
"Entity is hidden",
|
||||
"Entity was not hidden")
|
||||
entity_deleted = (
|
||||
"Entity deleted",
|
||||
"Entity was not deleted")
|
||||
deletion_undo = (
|
||||
"UNDO deletion success",
|
||||
"UNDO deletion failed")
|
||||
deletion_redo = (
|
||||
"REDO deletion success",
|
||||
"REDO deletion failed")
|
||||
|
||||
|
||||
def AtomEditorComponents_DeferredFog_AddedToEntity():
|
||||
"""
|
||||
Summary:
|
||||
Tests the Deferred Fog component can be added to an entity and has the expected functionality.
|
||||
|
||||
Test setup:
|
||||
- Wait for Editor idle loop.
|
||||
- Open the "Base" level.
|
||||
|
||||
Expected Behavior:
|
||||
The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
|
||||
Creation and deletion undo/redo should also work.
|
||||
|
||||
Test Steps:
|
||||
1) Create an Deferred Fog entity with no components.
|
||||
2) Add Deferred Fog component to Deferred Fog entity.
|
||||
3) UNDO the entity creation and component addition.
|
||||
4) REDO the entity creation and component addition.
|
||||
5) Verify Deferred Fog component not enabled.
|
||||
6) Add PostFX Layer component since it is required by the Deferred Fog component.
|
||||
7) Verify Deferred Fog component is enabled.
|
||||
8) Enable the "Enable Deferred Fog" parameter.
|
||||
9) Enter/Exit game mode.
|
||||
10) Test IsHidden.
|
||||
11) Test IsVisible.
|
||||
12) Delete Deferred Fog entity.
|
||||
13) UNDO deletion.
|
||||
14) REDO deletion.
|
||||
15) Look for errors.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.utils import Report, Tracer, TestHelper
|
||||
from Atom.atom_utils.atom_constants import AtomComponentProperties
|
||||
|
||||
with Tracer() as error_tracer:
|
||||
# Test setup begins.
|
||||
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
|
||||
TestHelper.init_idle()
|
||||
TestHelper.open_level("", "Base")
|
||||
|
||||
# Test steps begin.
|
||||
# 1. Create an Deferred Fog entity with no components.
|
||||
deferred_fog_entity = EditorEntity.create_editor_entity(AtomComponentProperties.deferred_fog())
|
||||
Report.critical_result(Tests.deferred_fog_creation, deferred_fog_entity.exists())
|
||||
|
||||
# 2. Add Deferred Fog component to Deferred Fog entity.
|
||||
deferred_fog_component = deferred_fog_entity.add_component(
|
||||
AtomComponentProperties.deferred_fog())
|
||||
Report.critical_result(
|
||||
Tests.deferred_fog_component,
|
||||
deferred_fog_entity.has_component(AtomComponentProperties.deferred_fog()))
|
||||
|
||||
# 3. UNDO the entity creation and component addition.
|
||||
# -> UNDO component addition.
|
||||
general.undo()
|
||||
# -> UNDO naming entity.
|
||||
general.undo()
|
||||
# -> UNDO selecting entity.
|
||||
general.undo()
|
||||
# -> UNDO entity creation.
|
||||
general.undo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_undo, not deferred_fog_entity.exists())
|
||||
|
||||
# 4. REDO the entity creation and component addition.
|
||||
# -> REDO entity creation.
|
||||
general.redo()
|
||||
# -> REDO selecting entity.
|
||||
general.redo()
|
||||
# -> REDO naming entity.
|
||||
general.redo()
|
||||
# -> REDO component addition.
|
||||
general.redo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_redo, deferred_fog_entity.exists())
|
||||
|
||||
# 5. Verify Deferred Fog component not enabled.
|
||||
Report.result(Tests.deferred_fog_disabled, not deferred_fog_component.is_enabled())
|
||||
|
||||
# 6. Add PostFX Layer component since it is required by the Deferred Fog component.
|
||||
deferred_fog_entity.add_component(AtomComponentProperties.postfx_layer())
|
||||
Report.result(
|
||||
Tests.postfx_layer_component,
|
||||
deferred_fog_entity.has_component(AtomComponentProperties.postfx_layer()))
|
||||
|
||||
# 7. Verify Deferred Fog component is enabled.
|
||||
Report.result(Tests.deferred_fog_enabled, deferred_fog_component.is_enabled())
|
||||
|
||||
# 8. Enable the "Enable Deferred Fog" parameter.
|
||||
deferred_fog_component.set_component_property_value(
|
||||
AtomComponentProperties.deferred_fog('Enable Deferred Fog'), True)
|
||||
Report.result(Tests.enable_deferred_fog_parameter_enabled,
|
||||
deferred_fog_component.get_component_property_value(
|
||||
AtomComponentProperties.deferred_fog('Enable Deferred Fog')) is True)
|
||||
|
||||
# 9. Enter/Exit game mode.
|
||||
TestHelper.enter_game_mode(Tests.enter_game_mode)
|
||||
general.idle_wait_frames(1)
|
||||
TestHelper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
# 10. Test IsHidden.
|
||||
deferred_fog_entity.set_visibility_state(False)
|
||||
Report.result(Tests.is_hidden, deferred_fog_entity.is_hidden() is True)
|
||||
|
||||
# 11. Test IsVisible.
|
||||
deferred_fog_entity.set_visibility_state(True)
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.is_visible, deferred_fog_entity.is_visible() is True)
|
||||
|
||||
# 12. Delete Deferred Fog entity.
|
||||
deferred_fog_entity.delete()
|
||||
Report.result(Tests.entity_deleted, not deferred_fog_entity.exists())
|
||||
|
||||
# 13. UNDO deletion.
|
||||
general.undo()
|
||||
Report.result(Tests.deletion_undo, deferred_fog_entity.exists())
|
||||
|
||||
# 14. REDO deletion.
|
||||
general.redo()
|
||||
Report.result(Tests.deletion_redo, not deferred_fog_entity.exists())
|
||||
|
||||
# 15. Look for errors and asserts.
|
||||
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
|
||||
for error_info in error_tracer.errors:
|
||||
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
|
||||
for assert_info in error_tracer.asserts:
|
||||
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(AtomEditorComponents_DeferredFog_AddedToEntity)
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
class Tests:
|
||||
creation_undo = (
|
||||
"UNDO Entity creation success",
|
||||
"UNDO Entity creation failed")
|
||||
creation_redo = (
|
||||
"REDO Entity creation success",
|
||||
"REDO Entity creation failed")
|
||||
hdr_color_grading_creation = (
|
||||
"HDR Color Grading Entity successfully created",
|
||||
"HDR Color Grading Entity failed to be created")
|
||||
hdr_color_grading_component = (
|
||||
"Entity has an HDR Color Grading component",
|
||||
"Entity failed to find HDR Color Grading component")
|
||||
hdr_color_grading_disabled = (
|
||||
"HDR Color Grading component disabled",
|
||||
"HDR Color Grading component was not disabled")
|
||||
postfx_layer_component = (
|
||||
"Entity has a PostFX Layer component",
|
||||
"Entity did not have an PostFX Layer component")
|
||||
hdr_color_grading_enabled = (
|
||||
"HDR Color Grading component enabled",
|
||||
"HDR Color Grading component was not enabled")
|
||||
enable_hdr_color_grading_parameter_enabled = (
|
||||
"Enable HDR Color Grading parameter enabled",
|
||||
"Enable HDR Color Grading parameter was not enabled")
|
||||
enter_game_mode = (
|
||||
"Entered game mode",
|
||||
"Failed to enter game mode")
|
||||
exit_game_mode = (
|
||||
"Exited game mode",
|
||||
"Couldn't exit game mode")
|
||||
is_visible = (
|
||||
"Entity is visible",
|
||||
"Entity was not visible")
|
||||
is_hidden = (
|
||||
"Entity is hidden",
|
||||
"Entity was not hidden")
|
||||
entity_deleted = (
|
||||
"Entity deleted",
|
||||
"Entity was not deleted")
|
||||
deletion_undo = (
|
||||
"UNDO deletion success",
|
||||
"UNDO deletion failed")
|
||||
deletion_redo = (
|
||||
"REDO deletion success",
|
||||
"REDO deletion failed")
|
||||
|
||||
|
||||
def AtomEditorComponents_HDRColorGrading_AddedToEntity():
|
||||
"""
|
||||
Summary:
|
||||
Tests the HDR Color Grading component can be added to an entity and has the expected functionality.
|
||||
|
||||
Test setup:
|
||||
- Wait for Editor idle loop.
|
||||
- Open the "Base" level.
|
||||
|
||||
Expected Behavior:
|
||||
The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
|
||||
Creation and deletion undo/redo should also work.
|
||||
|
||||
Test Steps:
|
||||
1) Create an HDR Color Grading entity with no components.
|
||||
2) Add HDR Color Grading component to HDR Color Grading entity.
|
||||
3) UNDO the entity creation and component addition.
|
||||
4) REDO the entity creation and component addition.
|
||||
5) Verify HDR Color Grading component not enabled.
|
||||
6) Add PostFX Layer component since it is required by the HDR Color Grading component.
|
||||
7) Verify HDR Color Grading component is enabled.
|
||||
8) Enable the "Enable HDR Color Grading" parameter.
|
||||
9) Enter/Exit game mode.
|
||||
10) Test IsHidden.
|
||||
11) Test IsVisible.
|
||||
12) Delete HDR Color Grading entity.
|
||||
13) UNDO deletion.
|
||||
14) REDO deletion.
|
||||
15) Look for errors.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.utils import Report, Tracer, TestHelper
|
||||
from Atom.atom_utils.atom_constants import AtomComponentProperties
|
||||
|
||||
with Tracer() as error_tracer:
|
||||
# Test setup begins.
|
||||
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
|
||||
TestHelper.init_idle()
|
||||
TestHelper.open_level("", "Base")
|
||||
|
||||
# Test steps begin.
|
||||
# 1. Create an HDR Color Grading entity with no components.
|
||||
hdr_color_grading_entity = EditorEntity.create_editor_entity(AtomComponentProperties.hdr_color_grading())
|
||||
Report.critical_result(Tests.hdr_color_grading_creation, hdr_color_grading_entity.exists())
|
||||
|
||||
# 2. Add HDR Color Grading component to HDR Color Grading entity.
|
||||
hdr_color_grading_component = hdr_color_grading_entity.add_component(
|
||||
AtomComponentProperties.hdr_color_grading())
|
||||
Report.critical_result(
|
||||
Tests.hdr_color_grading_component,
|
||||
hdr_color_grading_entity.has_component(AtomComponentProperties.hdr_color_grading()))
|
||||
|
||||
# 3. UNDO the entity creation and component addition.
|
||||
# -> UNDO component addition.
|
||||
general.undo()
|
||||
# -> UNDO naming entity.
|
||||
general.undo()
|
||||
# -> UNDO selecting entity.
|
||||
general.undo()
|
||||
# -> UNDO entity creation.
|
||||
general.undo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_undo, not hdr_color_grading_entity.exists())
|
||||
|
||||
# 4. REDO the entity creation and component addition.
|
||||
# -> REDO entity creation.
|
||||
general.redo()
|
||||
# -> REDO selecting entity.
|
||||
general.redo()
|
||||
# -> REDO naming entity.
|
||||
general.redo()
|
||||
# -> REDO component addition.
|
||||
general.redo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_redo, hdr_color_grading_entity.exists())
|
||||
|
||||
# 5. Verify HDR Color Grading component not enabled.
|
||||
Report.result(Tests.hdr_color_grading_disabled, not hdr_color_grading_component.is_enabled())
|
||||
|
||||
# 6. Add PostFX Layer component since it is required by the HDR Color Grading component.
|
||||
hdr_color_grading_entity.add_component(AtomComponentProperties.postfx_layer())
|
||||
Report.result(
|
||||
Tests.postfx_layer_component,
|
||||
hdr_color_grading_entity.has_component(AtomComponentProperties.postfx_layer()))
|
||||
|
||||
# 7. Verify HDR Color Grading component is enabled.
|
||||
Report.result(Tests.hdr_color_grading_enabled, hdr_color_grading_component.is_enabled())
|
||||
|
||||
# 8. Enable the "Enable HDR Color Grading" parameter.
|
||||
hdr_color_grading_component.set_component_property_value(
|
||||
AtomComponentProperties.hdr_color_grading('Enable HDR color grading'), True)
|
||||
Report.result(Tests.enable_hdr_color_grading_parameter_enabled,
|
||||
hdr_color_grading_component.get_component_property_value(
|
||||
AtomComponentProperties.hdr_color_grading('Enable HDR color grading')) is True)
|
||||
|
||||
# 9. Enter/Exit game mode.
|
||||
TestHelper.enter_game_mode(Tests.enter_game_mode)
|
||||
general.idle_wait_frames(1)
|
||||
TestHelper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
# 10. Test IsHidden.
|
||||
hdr_color_grading_entity.set_visibility_state(False)
|
||||
Report.result(Tests.is_hidden, hdr_color_grading_entity.is_hidden() is True)
|
||||
|
||||
# 11. Test IsVisible.
|
||||
hdr_color_grading_entity.set_visibility_state(True)
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.is_visible, hdr_color_grading_entity.is_visible() is True)
|
||||
|
||||
# 12. Delete HDR Color Grading entity.
|
||||
hdr_color_grading_entity.delete()
|
||||
Report.result(Tests.entity_deleted, not hdr_color_grading_entity.exists())
|
||||
|
||||
# 13. UNDO deletion.
|
||||
general.undo()
|
||||
Report.result(Tests.deletion_undo, hdr_color_grading_entity.exists())
|
||||
|
||||
# 14. REDO deletion.
|
||||
general.redo()
|
||||
Report.result(Tests.deletion_redo, not hdr_color_grading_entity.exists())
|
||||
|
||||
# 15. Look for errors and asserts.
|
||||
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
|
||||
for error_info in error_tracer.errors:
|
||||
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
|
||||
for assert_info in error_tracer.asserts:
|
||||
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(AtomEditorComponents_HDRColorGrading_AddedToEntity)
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
class Tests:
|
||||
creation_undo = (
|
||||
"UNDO Entity creation success",
|
||||
"UNDO Entity creation failed")
|
||||
creation_redo = (
|
||||
"REDO Entity creation success",
|
||||
"REDO Entity creation failed")
|
||||
occlusion_culling_plane_entity_creation = (
|
||||
"Occlusion Culling Plane Entity successfully created",
|
||||
"Occlusion Culling Plane Entity failed to be created")
|
||||
occlusion_culling_plane_component_added = (
|
||||
"Entity has a Occlusion Culling Plane component",
|
||||
"Entity failed to find Occlusion Culling Plane component")
|
||||
enter_game_mode = (
|
||||
"Entered game mode",
|
||||
"Failed to enter game mode")
|
||||
exit_game_mode = (
|
||||
"Exited game mode",
|
||||
"Couldn't exit game mode")
|
||||
is_visible = (
|
||||
"Entity is visible",
|
||||
"Entity was not visible")
|
||||
is_hidden = (
|
||||
"Entity is hidden",
|
||||
"Entity was not hidden")
|
||||
entity_deleted = (
|
||||
"Entity deleted",
|
||||
"Entity was not deleted")
|
||||
deletion_undo = (
|
||||
"UNDO deletion success",
|
||||
"UNDO deletion failed")
|
||||
deletion_redo = (
|
||||
"REDO deletion success",
|
||||
"REDO deletion failed")
|
||||
|
||||
|
||||
def AtomEditorComponents_OcclusionCullingPlane_AddedToEntity():
|
||||
"""
|
||||
Summary:
|
||||
Tests the occlusion culling plane component can be added to an entity and has the expected functionality.
|
||||
|
||||
Test setup:
|
||||
- Wait for Editor idle loop.
|
||||
- Open the "Base" level.
|
||||
|
||||
Expected Behavior:
|
||||
The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
|
||||
Creation and deletion undo/redo should also work.
|
||||
|
||||
Test Steps:
|
||||
1) Create a Occlusion Culling Plane entity with no components.
|
||||
2) Add a Occlusion Culling Plane component to Occlusion Culling Plane entity.
|
||||
3) UNDO the entity creation and component addition.
|
||||
4) REDO the entity creation and component addition.
|
||||
5) Enter/Exit game mode.
|
||||
6) Test IsHidden.
|
||||
7) Test IsVisible.
|
||||
8) Delete Occlusion Culling Plane entity.
|
||||
9) UNDO deletion.
|
||||
10) REDO deletion.
|
||||
11) Look for errors.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.utils import Report, Tracer, TestHelper
|
||||
from Atom.atom_utils.atom_constants import AtomComponentProperties
|
||||
|
||||
with Tracer() as error_tracer:
|
||||
# Test setup begins.
|
||||
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
|
||||
TestHelper.init_idle()
|
||||
TestHelper.open_level("", "Base")
|
||||
|
||||
# Test steps begin.
|
||||
# 1. Create a occlusion culling plane entity with no components.
|
||||
occlusion_culling_plane_entity = EditorEntity.create_editor_entity(
|
||||
AtomComponentProperties.occlusion_culling_plane())
|
||||
Report.critical_result(Tests.occlusion_culling_plane_entity_creation,
|
||||
occlusion_culling_plane_entity.exists())
|
||||
|
||||
# 2. Add a occlusion culling plane component to occlusion culling plane entity.
|
||||
occlusion_culling_plane_component = occlusion_culling_plane_entity.add_component(
|
||||
AtomComponentProperties.occlusion_culling_plane())
|
||||
Report.critical_result(
|
||||
Tests.occlusion_culling_plane_component_added,
|
||||
occlusion_culling_plane_entity.has_component(AtomComponentProperties.occlusion_culling_plane()))
|
||||
|
||||
# 3. UNDO the entity creation and component addition.
|
||||
# -> UNDO component addition.
|
||||
general.undo()
|
||||
# -> UNDO naming entity.
|
||||
general.undo()
|
||||
# -> UNDO selecting entity.
|
||||
general.undo()
|
||||
# -> UNDO entity creation.
|
||||
general.undo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_undo, not occlusion_culling_plane_entity.exists())
|
||||
|
||||
# 4. REDO the entity creation and component addition.
|
||||
# -> REDO entity creation.
|
||||
general.redo()
|
||||
# -> REDO selecting entity.
|
||||
general.redo()
|
||||
# -> REDO naming entity.
|
||||
general.redo()
|
||||
# -> REDO component addition.
|
||||
general.redo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_redo, occlusion_culling_plane_entity.exists())
|
||||
|
||||
# 5. Enter/Exit game mode.
|
||||
TestHelper.enter_game_mode(Tests.enter_game_mode)
|
||||
general.idle_wait_frames(1)
|
||||
TestHelper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
# 6. Test IsHidden.
|
||||
occlusion_culling_plane_entity.set_visibility_state(False)
|
||||
Report.result(Tests.is_hidden, occlusion_culling_plane_entity.is_hidden() is True)
|
||||
|
||||
# 7. Test IsVisible.
|
||||
occlusion_culling_plane_entity.set_visibility_state(True)
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.is_visible, occlusion_culling_plane_entity.is_visible() is True)
|
||||
|
||||
# 8. Delete occlusion_culling_plane entity.
|
||||
occlusion_culling_plane_entity.delete()
|
||||
Report.result(Tests.entity_deleted, not occlusion_culling_plane_entity.exists())
|
||||
|
||||
# 9. UNDO deletion.
|
||||
general.undo()
|
||||
Report.result(Tests.deletion_undo, occlusion_culling_plane_entity.exists())
|
||||
|
||||
# 10. REDO deletion.
|
||||
general.redo()
|
||||
Report.result(Tests.deletion_redo, not occlusion_culling_plane_entity.exists())
|
||||
|
||||
# 11. Look for errors or asserts.
|
||||
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
|
||||
for error_info in error_tracer.errors:
|
||||
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
|
||||
for assert_info in error_tracer.asserts:
|
||||
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(AtomEditorComponents_OcclusionCullingPlane_AddedToEntity)
|
||||
@@ -6,18 +6,6 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import azlmbr.asset as asset
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.camera
|
||||
import azlmbr.entity as entity
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.math as math
|
||||
import azlmbr.paths
|
||||
import azlmbr.editor as editor
|
||||
|
||||
sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
|
||||
|
||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
||||
from editor_python_test_tools.editor_test_helper import EditorTestHelper
|
||||
@@ -45,9 +33,18 @@ def run():
|
||||
11. Adds a "camera" entity to "default_level" & adds a Camera component with 80 degree FOV and Transform values:
|
||||
Translate - x:5.5m, y:-12.0m, z:9.0m
|
||||
Rotate - x:-27.0, y:-12.0, z:25.0
|
||||
12. Finally enters game mode, takes a screenshot, exits game mode, & saves the level.
|
||||
12. Finally enters game mode, takes a screenshot, & exits game mode.
|
||||
:return: None
|
||||
"""
|
||||
import azlmbr.asset as asset
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.camera as camera
|
||||
import azlmbr.entity as entity
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.math as math
|
||||
import azlmbr.paths
|
||||
import azlmbr.editor as editor
|
||||
|
||||
def initial_viewport_setup(screen_width, screen_height):
|
||||
general.set_viewport_size(screen_width, screen_height)
|
||||
general.update_viewport()
|
||||
@@ -84,33 +81,11 @@ def run():
|
||||
general.run_console("r_displayInfo=0")
|
||||
general.idle_wait(1.0)
|
||||
|
||||
return True
|
||||
|
||||
# Wait for Editor idle loop before executing Python hydra scripts.
|
||||
general.idle_enable(True)
|
||||
|
||||
# Open the auto_test level.
|
||||
new_level_name = "auto_test" # Specified in class TestAllComponentsIndepthTests()
|
||||
heightmap_resolution = 512
|
||||
heightmap_meters_per_pixel = 1
|
||||
terrain_texture_resolution = 412
|
||||
use_terrain = False
|
||||
|
||||
# Return codes are ECreateLevelResult defined in CryEdit.h
|
||||
return_code = general.create_level_no_prompt(
|
||||
new_level_name, heightmap_resolution, heightmap_meters_per_pixel, terrain_texture_resolution, use_terrain)
|
||||
if return_code == 1:
|
||||
general.log(f"{new_level_name} level already exists")
|
||||
elif return_code == 2:
|
||||
general.log("Failed to create directory")
|
||||
elif return_code == 3:
|
||||
general.log("Directory length is too long")
|
||||
elif return_code != 0:
|
||||
general.log("Unknown error, failed to create level")
|
||||
else:
|
||||
general.log(f"{new_level_name} level created successfully")
|
||||
|
||||
# Basic setup for newly created level.
|
||||
# Basic setup for opened level.
|
||||
helper.open_level(level_name="Base")
|
||||
after_level_load()
|
||||
initial_viewport_setup(SCREEN_WIDTH, SCREEN_HEIGHT)
|
||||
|
||||
@@ -147,22 +122,25 @@ def run():
|
||||
parent_id=default_level.id
|
||||
)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalUniformScale", ground_plane.id, 32.0)
|
||||
ground_plane_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_chrome.azmaterial")
|
||||
ground_plane_material_asset = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False)
|
||||
ground_plane.get_set_test(0, "Default Material|Material Asset", ground_plane_material_asset)
|
||||
# Work around to add the correct Atom Mesh component
|
||||
|
||||
# Work around to add the correct Atom Mesh component and asset.
|
||||
mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId
|
||||
ground_plane.components.append(
|
||||
editor.EditorComponentAPIBus(
|
||||
bus.Broadcast, "AddComponentsOfType", ground_plane.id, [mesh_type_id]
|
||||
).GetValue()[0]
|
||||
)
|
||||
ground_plane_mesh_asset_path = os.path.join("Objects", "plane.azmodel")
|
||||
ground_plane_mesh_asset_path = os.path.join("TestData", "Objects", "plane.azmodel")
|
||||
ground_plane_mesh_asset = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", ground_plane_mesh_asset_path, math.Uuid(), False)
|
||||
hydra.get_set_test(ground_plane, 1, "Controller|Configuration|Mesh Asset", ground_plane_mesh_asset)
|
||||
|
||||
# Add Atom Material component and asset.
|
||||
ground_plane_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_chrome.azmaterial")
|
||||
ground_plane_material_asset = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False)
|
||||
ground_plane.get_set_test(0, "Default Material|Material Asset", ground_plane_material_asset)
|
||||
|
||||
# Create directional_light entity and set the properties
|
||||
directional_light = hydra.Entity("directional_light")
|
||||
directional_light.create_entity(
|
||||
@@ -180,11 +158,8 @@ def run():
|
||||
components=["Material"],
|
||||
parent_id=default_level.id
|
||||
)
|
||||
sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial")
|
||||
sphere_material_asset = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False)
|
||||
sphere.get_set_test(0, "Default Material|Material Asset", sphere_material_asset)
|
||||
# Work around to add the correct Atom Mesh component
|
||||
|
||||
# Work around to add the correct Atom Mesh component and asset.
|
||||
sphere.components.append(
|
||||
editor.EditorComponentAPIBus(
|
||||
bus.Broadcast, "AddComponentsOfType", sphere.id, [mesh_type_id]
|
||||
@@ -195,6 +170,12 @@ def run():
|
||||
bus.Broadcast, "GetAssetIdByPath", sphere_mesh_asset_path, math.Uuid(), False)
|
||||
hydra.get_set_test(sphere, 1, "Controller|Configuration|Mesh Asset", sphere_mesh_asset)
|
||||
|
||||
# Add Atom Material component and asset.
|
||||
sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial")
|
||||
sphere_material_asset = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False)
|
||||
sphere.get_set_test(0, "Default Material|Material Asset", sphere_material_asset)
|
||||
|
||||
# Create camera component and set the properties
|
||||
camera_entity = hydra.Entity("camera")
|
||||
position = math.Vector3(5.5, -12.0, 9.0)
|
||||
@@ -204,10 +185,9 @@ def run():
|
||||
)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", camera_entity.id, rotation)
|
||||
camera_entity.get_set_test(0, "Controller|Configuration|Field of view", 60.0)
|
||||
azlmbr.camera.EditorCameraViewRequestBus(azlmbr.bus.Event, "ToggleCameraAsActiveView", camera_entity.id)
|
||||
camera.EditorCameraViewRequestBus(azlmbr.bus.Event, "ToggleCameraAsActiveView", camera_entity.id)
|
||||
|
||||
# Save level, enter game mode, take screenshot, & exit game mode.
|
||||
general.save_level()
|
||||
# Enter game mode, take screenshot, & exit game mode.
|
||||
general.idle_wait(0.5)
|
||||
general.enter_game_mode()
|
||||
general.idle_wait(1.0)
|
||||
|
||||
@@ -22,7 +22,7 @@ from editor_python_test_tools.editor_test_helper import EditorTestHelper
|
||||
|
||||
helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper")
|
||||
|
||||
LEVEL_NAME = "auto_test"
|
||||
LEVEL_NAME = "Base"
|
||||
LIGHT_COMPONENT = "Light"
|
||||
LIGHT_TYPE_PROPERTY = 'Controller|Configuration|Light type'
|
||||
DEGREE_RADIAN_FACTOR = 0.0174533
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_add_pytest(
|
||||
NAME AutomatedTesting::MultiplayerTests_Main
|
||||
TEST_SUITE main
|
||||
NAME AutomatedTesting::MultiplayerTests_Sandbox
|
||||
TEST_SUITE sandbox
|
||||
TEST_SERIAL
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Sandbox.py
|
||||
RUNTIME_DEPENDENCIES
|
||||
Legacy::Editor
|
||||
AZ::AssetProcessor
|
||||
|
||||
@@ -27,7 +27,3 @@ class TestAutomation(TestAutomationBase):
|
||||
batch_mode=batch_mode,
|
||||
autotest_mode=autotest_mode)
|
||||
|
||||
def test_Multiplayer_AutoComponent_NetworkInput(self, request, workspace, editor, launcher_platform):
|
||||
from .tests import Multiplayer_AutoComponent_NetworkInput as test_module
|
||||
self._run_prefab_test(request, workspace, editor, test_module)
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
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 suite consists of all test cases that are under development and have not been verified yet.
|
||||
# Once they are verified, please move them to TestSuite_Active.py
|
||||
|
||||
import pytest
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
|
||||
|
||||
from base import TestAutomationBase
|
||||
|
||||
@pytest.mark.SUITE_sandbox
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
class TestAutomation(TestAutomationBase):
|
||||
def _run_prefab_test(self, request, workspace, editor, test_module, batch_mode=True, autotest_mode=True):
|
||||
self._run_test(request, workspace, editor, test_module,
|
||||
extra_cmdline_args=["--regset=/Amazon/Preferences/EnablePrefabSystem=true"],
|
||||
batch_mode=batch_mode,
|
||||
autotest_mode=autotest_mode)
|
||||
|
||||
## Seems to be flaky, need to investigate
|
||||
def test_Multiplayer_AutoComponent_NetworkInput(self, request, workspace, editor, launcher_platform):
|
||||
from .tests import Multiplayer_AutoComponent_NetworkInput as test_module
|
||||
self._run_prefab_test(request, workspace, editor, test_module)
|
||||
|
||||
+7
-14
@@ -329,7 +329,7 @@ class TestsAssetProcessorBatch_AllPlatforms(object):
|
||||
# or an expected behavior has changed. Processing bootstrap.cfg sometimes but not other times should not
|
||||
# cause a failure in this test.
|
||||
num_processed_assets = asset_processor_utils.get_num_processed_assets(output)
|
||||
assert num_processed_assets >= 8, f'Wrong number of successfully processed assets found in output: '\
|
||||
assert num_processed_assets >= 6, f'Wrong number of successfully processed assets found in output: '\
|
||||
'{num_processed_assets}'
|
||||
|
||||
missing_assets, _ = asset_processor.compare_assets_with_cache()
|
||||
@@ -585,20 +585,18 @@ class TestsAssetProcessorBatch_AllPlatforms(object):
|
||||
3. Verify that logs exist for both AP Batch & AP GUI
|
||||
"""
|
||||
asset_processor.create_temp_asset_root()
|
||||
asset_processor.create_temp_log_root()
|
||||
LOG_PATH = {
|
||||
"batch_log": workspace.paths.ap_batch_log(),
|
||||
"gui_log": workspace.paths.ap_gui_log(),
|
||||
"job_logs": workspace.paths.ap_job_logs(),
|
||||
"gui_log": workspace.paths.ap_gui_log()
|
||||
}
|
||||
|
||||
class LogTimes:
|
||||
batch_log_start_time = 0
|
||||
gui_log_start_time = 0
|
||||
job_logs_start_time = 0
|
||||
|
||||
batch_log_final_time = 0
|
||||
gui_log_final_time = 0
|
||||
job_logs_final_time = 0
|
||||
|
||||
@staticmethod
|
||||
def Report():
|
||||
@@ -607,12 +605,10 @@ class TestsAssetProcessorBatch_AllPlatforms(object):
|
||||
Original Times:
|
||||
Batch: {LogTimes.batch_log_start_time}
|
||||
GUI: {LogTimes.gui_log_start_time}
|
||||
JobLogs:{LogTimes.job_logs_start_time}
|
||||
|
||||
Post-Run Times:
|
||||
Batch: {LogTimes.batch_log_final_time}
|
||||
GUI: {LogTimes.gui_log_final_time}
|
||||
JobLogs:{LogTimes.job_logs_final_time}
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -629,23 +625,19 @@ class TestsAssetProcessorBatch_AllPlatforms(object):
|
||||
LogTimes.Report()
|
||||
|
||||
def check_existence(name, path):
|
||||
assert os.path.exists(path), f"{name} could not be located after running the AP."
|
||||
assert os.path.exists(path), f"{name} could not be located {path} after running the AP."
|
||||
|
||||
# Check if log files previously exist and grab their modification times
|
||||
update_times("_start_time")
|
||||
|
||||
# Run the Batch process
|
||||
assert asset_processor.batch_process(), "Batch process failed to successfully terminate"
|
||||
assert asset_processor.batch_process(create_temp_log=False), "Batch process failed to successfully terminate"
|
||||
|
||||
asset_processor.gui_process(quitonidle=True)
|
||||
asset_processor.gui_process(quitonidle=True, create_temp_log=False)
|
||||
|
||||
# Check that the Logs directory exists (C1564055)
|
||||
check_existence("Logs Directory", workspace.paths.ap_log_dir())
|
||||
|
||||
# Check that the logs and JobLogs directory have updated modified times (C1564056)
|
||||
for key in LOG_PATH.keys():
|
||||
check_existence(key, LOG_PATH[key])
|
||||
|
||||
update_times("_final_time")
|
||||
|
||||
for key in LOG_PATH.keys():
|
||||
@@ -708,6 +700,7 @@ class TestsAssetProcessorBatch_AllPlatforms(object):
|
||||
|
||||
@pytest.mark.BAT
|
||||
@pytest.mark.assetpipeline
|
||||
@pytest.mark.skip(reason="need to change assets from .slice files to an asset type that can have nested dependencies")
|
||||
def test_validateNestedPreloadDependency_Found(self, asset_processor, ap_setup_fixture, workspace):
|
||||
"""
|
||||
Tests processing of a nested circular dependency and verifies that Asset Processor will return an error
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7932fada1523fad4eb6ad5c13111bb4c00d6b0584ec8641060bf25f306b17e69
|
||||
size 84632
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bf94b548eccb78432db65077124cadf20de975919b181d712fde081f59edd353
|
||||
size 38848
|
||||
+1
@@ -0,0 +1 @@
|
||||
{}
|
||||
+1
@@ -0,0 +1 @@
|
||||
to be deleted
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1675471085483e0bd252a5bdd4db1b365c66f16ac32a6e6dd70bb1c23df83fa5
|
||||
size 24160
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1675471085483e0bd252a5bdd4db1b365c66f16ac32a6e6dd70bb1c23df83fa5
|
||||
size 24160
|
||||
+1
@@ -0,0 +1 @@
|
||||
{}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:21df6ab62f2572daa6d0710ad6819a728ee751a62170903a6773563d614aa51f
|
||||
size 15191
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:21df6ab62f2572daa6d0710ad6819a728ee751a62170903a6773563d614aa51f
|
||||
size 15191
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f0b4750147acbcc6229a1043ed47ee48e7703a5241f27fc72976e95741144369
|
||||
size 2372
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a036c3763b96079dde8505ad6995f8b717a777c78d7a434ac8de6f1ccb5d8dd1
|
||||
size 4327
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cf55a4372d82d70d8cdcc95468367cd4fad7649d3fb99c4d85049977b506885c
|
||||
size 13429
|
||||
+1
@@ -0,0 +1 @@
|
||||
{}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1821d000b583821fd36ec73f91073d51ae90762225a34a81a74771c36236b5e1
|
||||
size 12963
|
||||
@@ -548,7 +548,6 @@ public:
|
||||
{ "BatchMode", m_bConsoleMode },
|
||||
{ "NullRenderer", m_bNullRenderer },
|
||||
{ "devmode", m_bDeveloperMode },
|
||||
{ "VTUNE", dummy },
|
||||
{ "runpython", m_bRunPythonScript },
|
||||
{ "runpythontest", m_bRunPythonTestScript },
|
||||
{ "version", m_bShowVersionInfo },
|
||||
|
||||
@@ -60,15 +60,6 @@
|
||||
#include <LmbrCentral/Audio/AudioSystemComponentBus.h>
|
||||
#include <LmbrCentral/Rendering/EditorLightComponentBus.h> // for LmbrCentral::EditorLightComponentRequestBus
|
||||
|
||||
//#define PROFILE_LOADING_WITH_VTUNE
|
||||
|
||||
// profilers api.
|
||||
//#include "pure.h"
|
||||
#ifdef PROFILE_LOADING_WITH_VTUNE
|
||||
#include "C:\Program Files\Intel\Vtune\Analyzer\Include\VTuneApi.h"
|
||||
#pragma comment(lib,"C:\\Program Files\\Intel\\Vtune\\Analyzer\\Lib\\VTuneApi.lib")
|
||||
#endif
|
||||
|
||||
static const char* kAutoBackupFolder = "_autobackup";
|
||||
static const char* kHoldFolder = "$tmp_hold"; // conform to the ignored file types $tmp[0-9]*_ regex
|
||||
static const char* kSaveBackupFolder = "_savebackup";
|
||||
@@ -408,9 +399,6 @@ void CCryEditDoc::Load(TDocMultiArchive& arrXmlAr, const QString& szFilename)
|
||||
|
||||
int t0 = GetTickCount();
|
||||
|
||||
#ifdef PROFILE_LOADING_WITH_VTUNE
|
||||
VTResume();
|
||||
#endif
|
||||
// Load level-specific audio data.
|
||||
AZStd::string levelFileName{ fileName.toUtf8().constData() };
|
||||
AZStd::to_lower(levelFileName.begin(), levelFileName.end());
|
||||
@@ -484,10 +472,6 @@ void CCryEditDoc::Load(TDocMultiArchive& arrXmlAr, const QString& szFilename)
|
||||
|
||||
CSurfaceTypeValidator().Validate();
|
||||
|
||||
#ifdef PROFILE_LOADING_WITH_VTUNE
|
||||
VTPause();
|
||||
#endif
|
||||
|
||||
LogLoadTime(GetTickCount() - t0);
|
||||
// Loaded with success, remove event from log file
|
||||
GetIEditor()->GetSettingsManager()->UnregisterEvent(loadEvent);
|
||||
|
||||
@@ -11,7 +11,6 @@ OutlinerWidget #m_display_options
|
||||
{
|
||||
qproperty-icon: url(:/Menu/menu.svg);
|
||||
qproperty-iconSize: 16px 16px;
|
||||
qproperty-flat: true;
|
||||
}
|
||||
|
||||
OutlinerWidget QWidget[PulseHighlight="true"]
|
||||
|
||||
@@ -457,8 +457,6 @@ void JobManagerWorkStealing::ProcessJobsInternal(ThreadInfo* info, Job* suspende
|
||||
else
|
||||
{
|
||||
//attempt to steal a job from another thread's queue
|
||||
AZ_PROFILE_SCOPE(AzCore, "JobManagerWorkStealing::ProcessJobsInternal:WorkStealing");
|
||||
|
||||
unsigned int numStealAttempts = 0;
|
||||
const unsigned int maxStealAttempts = (unsigned int)m_workerThreads.size() * 3; //try every thread a few times before giving up
|
||||
while (!job)
|
||||
|
||||
+19
-13
@@ -10,6 +10,8 @@
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
|
||||
#include <AzFramework/Process/ProcessWatcher.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -24,14 +26,20 @@ namespace AzFramework::AssetSystem::Platform
|
||||
AZ::IO::FixedMaxPath assetProcessorPath{ executableDirectory };
|
||||
// In Mac the Editor and game is within a bundle, so the path to the sibling app
|
||||
// has to go up from the Contents/MacOS folder the binary is in
|
||||
assetProcessorPath /= "../../../AssetProcessor.app";
|
||||
assetProcessorPath /= "../../../AssetProcessor.app/Contents/MacOS/AssetProcessor";
|
||||
assetProcessorPath = assetProcessorPath.LexicallyNormal();
|
||||
|
||||
if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str()))
|
||||
{
|
||||
// Check for existence of one under a "bin" directory, i.e. engineRoot is an SDK structure.
|
||||
assetProcessorPath =
|
||||
AZ::IO::FixedMaxPath{engineRoot} / "bin" / AZ_TRAIT_OS_PLATFORM_NAME / AZ_BUILD_CONFIGURATION_TYPE / "AssetProcessor.app";
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if (AZ::IO::FixedMaxPath installedBinariesPath;
|
||||
settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder))
|
||||
{
|
||||
// Check for existence of one under a "bin" directory, i.e. engineRoot is an SDK structure.
|
||||
assetProcessorPath = AZ::IO::FixedMaxPath{ engineRoot } / installedBinariesPath / "AssetProcessor.app/Contents/MacOS/AssetProcessor";
|
||||
}
|
||||
}
|
||||
|
||||
if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str()))
|
||||
{
|
||||
@@ -39,23 +47,21 @@ namespace AzFramework::AssetSystem::Platform
|
||||
}
|
||||
}
|
||||
|
||||
auto fullLaunchCommand = AZ::IO::FixedMaxPathString::format(R"(open -g "%s" --args --start-hidden)", assetProcessorPath.c_str());
|
||||
AZStd::string commandLineParams;
|
||||
// Add the engine path to the launch command if not empty
|
||||
if (!engineRoot.empty())
|
||||
{
|
||||
fullLaunchCommand += R"( --engine-path=")";
|
||||
fullLaunchCommand += engineRoot;
|
||||
fullLaunchCommand += '"';
|
||||
commandLineParams += AZStd::string::format("\"--engine-path=\"%s\"\"", engineRoot.data());
|
||||
}
|
||||
|
||||
// Add the active project path to the launch command if not empty
|
||||
if (!projectPath.empty())
|
||||
{
|
||||
fullLaunchCommand += R"( --project-path=")";
|
||||
fullLaunchCommand += projectPath;
|
||||
fullLaunchCommand += '"';
|
||||
commandLineParams += AZStd::string::format(" \"--regset=/Amazon/AzCore/Bootstrap/project_path=\"%s\"\"", projectPath.data());
|
||||
}
|
||||
|
||||
return system(fullLaunchCommand.c_str()) == 0;
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
processLaunchInfo.m_processExecutableString = AZStd::move(assetProcessorPath.Native());
|
||||
processLaunchInfo.m_commandlineParameters = commandLineParams;
|
||||
return AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo);
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -54,6 +54,7 @@ namespace AzFramework
|
||||
RECT m_windowRectToRestoreOnFullScreenExit; //!< The position and size of the window to restore when exiting full screen.
|
||||
UINT m_windowStyleToRestoreOnFullScreenExit; //!< The style(s) of the window to restore when exiting full screen.
|
||||
bool m_isInBorderlessWindowFullScreenState = false; //!< Was a borderless window used to enter full screen state?
|
||||
bool m_shouldEnterFullScreenStateOnActivate = false; //!< Should we enter full screen state when the window is activated?
|
||||
|
||||
using GetDpiForWindowType = UINT(HWND hwnd);
|
||||
GetDpiForWindowType* m_getDpiFunction = nullptr;
|
||||
@@ -249,6 +250,28 @@ namespace AzFramework
|
||||
AzFramework::RawInputNotificationBusWindows::Broadcast(&AzFramework::RawInputNotificationsWindows::OnRawInputCodeUnitUTF16Event, codeUnitUTF16);
|
||||
break;
|
||||
}
|
||||
case WM_ACTIVATE:
|
||||
{
|
||||
// Alt-tabbing out of the app while it is in a full screen state does not
|
||||
// work unless we explicitly exit the full screen state upon deactivation,
|
||||
// in which case we want to enter full screen state again upon activation.
|
||||
const bool windowIsNowInactive = (LOWORD(wParam) == WA_INACTIVE);
|
||||
const bool windowFullScreenState = nativeWindowImpl->GetFullScreenState();
|
||||
if (windowIsNowInactive &&
|
||||
windowFullScreenState)
|
||||
{
|
||||
nativeWindowImpl->m_shouldEnterFullScreenStateOnActivate = true;
|
||||
nativeWindowImpl->SetFullScreenState(false);
|
||||
}
|
||||
else if (!windowIsNowInactive &&
|
||||
!windowFullScreenState &&
|
||||
nativeWindowImpl->m_shouldEnterFullScreenStateOnActivate)
|
||||
{
|
||||
nativeWindowImpl->m_shouldEnterFullScreenStateOnActivate = false;
|
||||
nativeWindowImpl->SetFullScreenState(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_SYSKEYDOWN:
|
||||
{
|
||||
// Handle ALT+ENTER to toggle full screen unless exclsuive full screen
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
<Member Type="AzNetworking::DisconnectReason" Name="disconnectReason" Init="AzNetworking::DisconnectReason::None" />
|
||||
</Packet>
|
||||
|
||||
<Packet Name="HeartbeatPacket" Desc="This packet is used to keep an established connection alive" />
|
||||
<Packet Name="HeartbeatPacket" Desc="This packet is used to keep an established connection alive">
|
||||
<Member Type="bool" Name="requestResponse" Init="false" />
|
||||
</Packet>
|
||||
|
||||
<Packet Name="FragmentedPacket" Desc="This packet is used to segment a packet that exceeds a connections MTU">
|
||||
<Member Type="AzNetworking::SequenceId" Name="unfragmentedSequence" Init="AzNetworking::InvalidSequenceId" />
|
||||
|
||||
@@ -122,10 +122,4 @@ namespace AzNetworking
|
||||
m_timeoutItemMap.erase(itemTimeoutId);
|
||||
}
|
||||
}
|
||||
|
||||
void TimeoutQueue::UpdateTimeouts(ITimeoutHandler& timeoutHandler, int32_t maxTimeouts)
|
||||
{
|
||||
TimeoutHandler handler([&timeoutHandler](TimeoutQueue::TimeoutItem& item) { return timeoutHandler.HandleTimeout(item); });
|
||||
UpdateTimeouts(handler, maxTimeouts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ namespace AzNetworking
|
||||
Delete
|
||||
};
|
||||
|
||||
class ITimeoutHandler;
|
||||
|
||||
//! @class TimeoutQueue
|
||||
//! @brief class for managing timeout items.
|
||||
class TimeoutQueue
|
||||
@@ -70,11 +68,6 @@ namespace AzNetworking
|
||||
using TimeoutHandler = AZStd::function<TimeoutResult(TimeoutQueue::TimeoutItem&)>;
|
||||
void UpdateTimeouts(const TimeoutHandler& timeoutHandler, int32_t maxTimeouts = -1);
|
||||
|
||||
//! Updates timeouts for all items, invokes timeout handlers if required.
|
||||
//! @param timeoutHandler listener instance to call back on for timeouts
|
||||
//! @param maxTimeouts the maximum number of timeouts to process before breaking iteration
|
||||
void UpdateTimeouts(ITimeoutHandler& timeoutHandler, int32_t maxTimeouts = -1);
|
||||
|
||||
private:
|
||||
|
||||
struct TimeoutQueueItem
|
||||
@@ -94,19 +87,6 @@ namespace AzNetworking
|
||||
TimeoutItemMap m_timeoutItemMap;
|
||||
TimeoutItemQueue m_timeoutItemQueue;
|
||||
};
|
||||
|
||||
//! @class ITimeoutHandler
|
||||
//! @brief interface class for managing timeout items.
|
||||
class ITimeoutHandler
|
||||
{
|
||||
public:
|
||||
virtual ~ITimeoutHandler() = default;
|
||||
|
||||
//! Handler callback for timed out items.
|
||||
//! @param item containing registered timeout details
|
||||
//! @return ETimeoutResult for whether to re-register or discard the timeout params
|
||||
virtual TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#include <AzNetworking/DataStructures/TimeoutQueue.inl>
|
||||
|
||||
@@ -27,13 +27,11 @@ namespace AzNetworking
|
||||
ConnectionId connectionId,
|
||||
const IpAddress& remoteAddress,
|
||||
TcpNetworkInterface& networkInterface,
|
||||
TcpSocket& socket,
|
||||
TimeoutId timeoutId
|
||||
TcpSocket& socket
|
||||
)
|
||||
: IConnection(connectionId, remoteAddress)
|
||||
, m_networkInterface(networkInterface)
|
||||
, m_socket(socket.CloneAndTakeOwnership())
|
||||
, m_timeoutId(timeoutId)
|
||||
, m_state(m_socket->IsOpen() ? ConnectionState::Connecting : ConnectionState::Disconnected)
|
||||
, m_connectionRole(ConnectionRole::Acceptor)
|
||||
, m_registeredSocketFd(InvalidSocketFd)
|
||||
@@ -163,13 +161,6 @@ namespace AzNetworking
|
||||
break;
|
||||
}
|
||||
|
||||
TimeoutQueue::TimeoutItem* timeoutItem = m_networkInterface.m_connectionTimeoutQueue.RetrieveItem(GetTimeoutId());
|
||||
if (timeoutItem == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
timeoutItem->UpdateTimeoutTime(startTimeMs);
|
||||
|
||||
NetworkOutputSerializer serializer(buffer.GetBuffer(), static_cast<uint32_t>(buffer.GetSize()));
|
||||
if (m_state == ConnectionState::Connecting)
|
||||
{
|
||||
|
||||
@@ -38,14 +38,12 @@ namespace AzNetworking
|
||||
//! @param remoteAddress IP address of the remote endpoint
|
||||
//! @param networkInterface TcpNetworkInterface that owns this connection instance
|
||||
//! @param socket TCP socket to take ownership of and use for sending and receiving data
|
||||
//! @param timeoutId timeout identifier of this connection instance
|
||||
TcpConnection
|
||||
(
|
||||
ConnectionId connectionId,
|
||||
const IpAddress& remoteAddress,
|
||||
TcpNetworkInterface& networkInterface,
|
||||
TcpSocket& socket,
|
||||
TimeoutId timeoutId
|
||||
TcpSocket& socket
|
||||
);
|
||||
|
||||
//! Construct a new socket with optional encryption, used when initiating a new connection
|
||||
@@ -69,14 +67,6 @@ namespace AzNetworking
|
||||
//! @return the TcpSocket bound to this TcpConnection
|
||||
TcpSocket* GetTcpSocket() const;
|
||||
|
||||
//! Sets the timeout identifier for this TcpConnection.
|
||||
//! @param timeoutId the timeout identifier to use for this TcpConnection
|
||||
void SetTimeoutId(TimeoutId timeoutId);
|
||||
|
||||
//! Returns the timeout identifier for this TcpConnection.
|
||||
//! @return the timeout identifier for this TcpConnection
|
||||
TimeoutId GetTimeoutId() const;
|
||||
|
||||
//! Returns true if this connection instance is in an open state, and is capable of actively sending and receiving packets.
|
||||
//! @return boolean true if this connection instance is in an open state
|
||||
bool IsOpen() const;
|
||||
@@ -142,7 +132,6 @@ namespace AzNetworking
|
||||
AZStd::unique_ptr<TcpSocket> m_socket;
|
||||
AZStd::unique_ptr<ICompressor> m_compressor;
|
||||
|
||||
TimeoutId m_timeoutId;
|
||||
PacketId m_lastSentPacketId = InvalidPacketId;
|
||||
ConnectionState m_state = ConnectionState::Disconnected;
|
||||
ConnectionRole m_connectionRole = ConnectionRole::Connector;
|
||||
|
||||
@@ -15,16 +15,6 @@ namespace AzNetworking
|
||||
return m_socket.get();
|
||||
}
|
||||
|
||||
inline void TcpConnection::SetTimeoutId(TimeoutId timeoutId)
|
||||
{
|
||||
m_timeoutId = timeoutId;
|
||||
}
|
||||
|
||||
inline TimeoutId TcpConnection::GetTimeoutId() const
|
||||
{
|
||||
return m_timeoutId;
|
||||
}
|
||||
|
||||
inline bool TcpConnection::IsOpen() const
|
||||
{
|
||||
return m_socket->IsOpen();
|
||||
|
||||
@@ -21,16 +21,11 @@ namespace AzNetworking
|
||||
static const bool net_TcpUseEncryption = false;
|
||||
#endif
|
||||
|
||||
AZ_CVAR(bool, net_TcpTimeoutConnections, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Boolean value on whether we should timeout Tcp connections");
|
||||
AZ_CVAR(AZ::TimeMs, net_TcpHeartbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Tcp connection heartbeat frequency");
|
||||
AZ_CVAR(AZ::TimeMs, net_TcpDefaultTimeoutMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Tcp connection");
|
||||
|
||||
TcpNetworkInterface::TcpNetworkInterface(AZ::Name name, IConnectionListener& connectionListener, TrustZone trustZone, TcpListenThread& listenThread)
|
||||
: m_name(name)
|
||||
, m_trustZone(trustZone)
|
||||
, m_connectionListener(connectionListener)
|
||||
, m_listenThread(listenThread)
|
||||
, m_timeoutMs(net_TcpDefaultTimeoutMs)
|
||||
{
|
||||
;
|
||||
}
|
||||
@@ -98,8 +93,6 @@ namespace AzNetworking
|
||||
}
|
||||
|
||||
AZLOG_INFO("Adding new socket %d", static_cast<int32_t>(tcpSocket->GetSocketFd()));
|
||||
const TimeoutId newTimeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast<uint64_t>(tcpSocket->GetSocketFd()), net_TcpHeartbeatTimeMs);
|
||||
connection->SetTimeoutId(newTimeoutId);
|
||||
connection->SendReliablePacket(CorePackets::InitiateConnectionPacket());
|
||||
m_connectionListener.OnConnect(connection.get());
|
||||
m_connectionSet.AddConnection(AZStd::move(connection));
|
||||
@@ -110,12 +103,6 @@ namespace AzNetworking
|
||||
{
|
||||
const AZ::TimeMs startTimeMs = AZ::GetElapsedTimeMs();
|
||||
|
||||
// Time out any stale connections
|
||||
{
|
||||
ConnectionTimeoutFunctor functor(*this);
|
||||
m_connectionTimeoutQueue.UpdateTimeouts(functor);
|
||||
}
|
||||
|
||||
AcceptNewConnections();
|
||||
|
||||
auto readCallback = [this, startTimeMs](SocketFd socketFd) { HandleConnectionRecv(socketFd, startTimeMs); };
|
||||
@@ -258,8 +245,7 @@ namespace AzNetworking
|
||||
return;
|
||||
}
|
||||
AZLOG(NET_TcpTraffic, "Adding new socket %d", static_cast<int32_t>(tcpSocket.GetSocketFd()));
|
||||
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast<uint64_t>(tcpSocket.GetSocketFd()), m_timeoutMs);
|
||||
AZStd::unique_ptr<TcpConnection> connection = AZStd::make_unique<TcpConnection>(connectionId, remoteAddress, *this, tcpSocket, timeoutId);
|
||||
AZStd::unique_ptr<TcpConnection> connection = AZStd::make_unique<TcpConnection>(connectionId, remoteAddress, *this, tcpSocket);
|
||||
AZ_Assert(connection->GetConnectionRole() == ConnectionRole::Acceptor, "Invalid role for connection");
|
||||
GetConnectionListener().OnConnect(connection.get());
|
||||
m_connectionSet.AddConnection(AZStd::move(connection));
|
||||
@@ -286,7 +272,6 @@ namespace AzNetworking
|
||||
m_pendingRemoves.resize_no_construct(0);
|
||||
}
|
||||
|
||||
|
||||
TcpNetworkInterface::PendingConnection::PendingConnection(SocketFd socketFd, uint32_t remoteIpAddress, uint16_t remotePort, uint16_t listenPort)
|
||||
: m_socketFd(socketFd)
|
||||
, m_remoteIpAddress(remoteIpAddress)
|
||||
@@ -295,34 +280,4 @@ namespace AzNetworking
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
TcpNetworkInterface::ConnectionTimeoutFunctor::ConnectionTimeoutFunctor(TcpNetworkInterface& networkInterface)
|
||||
: m_networkInterface(networkInterface)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
TimeoutResult TcpNetworkInterface::ConnectionTimeoutFunctor::HandleTimeout(TimeoutQueue::TimeoutItem& item)
|
||||
{
|
||||
const SocketFd socketFd = static_cast<SocketFd>(item.m_userData);
|
||||
TcpConnection* tcpConnection = m_networkInterface.m_connectionSet.GetConnection(socketFd);
|
||||
|
||||
if (tcpConnection == nullptr)
|
||||
{
|
||||
// We've already deleted this connection
|
||||
return TimeoutResult::Delete;
|
||||
}
|
||||
|
||||
if (tcpConnection->GetConnectionRole() == ConnectionRole::Connector)
|
||||
{
|
||||
tcpConnection->SendReliablePacket(CorePackets::HeartbeatPacket());
|
||||
}
|
||||
else if (net_TcpTimeoutConnections && (m_networkInterface.GetTimeoutMs() > AZ::TimeMs{ 0 }))
|
||||
{
|
||||
tcpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local);
|
||||
return TimeoutResult::Delete;
|
||||
}
|
||||
|
||||
return TimeoutResult::Refresh;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,16 +137,6 @@ namespace AzNetworking
|
||||
|
||||
AZ_DISABLE_COPY_MOVE(TcpNetworkInterface);
|
||||
|
||||
struct ConnectionTimeoutFunctor final
|
||||
: public ITimeoutHandler
|
||||
{
|
||||
ConnectionTimeoutFunctor(TcpNetworkInterface& networkInterface);
|
||||
TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) override;
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(ConnectionTimeoutFunctor);
|
||||
TcpNetworkInterface& m_networkInterface;
|
||||
};
|
||||
|
||||
struct PendingRemove
|
||||
{
|
||||
SocketFd m_socketFd;
|
||||
@@ -162,7 +152,6 @@ namespace AzNetworking
|
||||
TcpSocketManager m_tcpSocketManager;
|
||||
AZ::ThreadSafeDeque<PendingConnection> m_pendingConnections;
|
||||
AZStd::vector<PendingRemove> m_pendingRemoves;
|
||||
TimeoutQueue m_connectionTimeoutQueue;
|
||||
TcpListenThread& m_listenThread;
|
||||
|
||||
friend class TcpConnection; // For access to private RequestDisconnect() method
|
||||
|
||||
@@ -79,7 +79,8 @@ namespace AzNetworking
|
||||
AZLOG(NET_Acks, "Unacked packet count exceeded, sending client heartbeat (curr %u : max %u)", m_unackedPacketCount, static_cast<uint32_t>(net_UdpMaxUnackedPacketCount));
|
||||
// This simply times out unreliable chunks that haven't completed within our timeout delay
|
||||
m_fragmentQueue.Update();
|
||||
SendUnreliablePacket(CorePackets::HeartbeatPacket());
|
||||
// This heartbeat is sent to minimize the time the remote endpoint spends waiting for ack vector replication, we don't require a response
|
||||
SendUnreliablePacket(CorePackets::HeartbeatPacket(false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +290,11 @@ namespace AzNetworking
|
||||
{
|
||||
return PacketDispatchResult::Failure;
|
||||
}
|
||||
// Do nothing, we've already processed our ack packets
|
||||
if (packet.GetRequestResponse())
|
||||
{
|
||||
// We're replying to a heartbeat request, we don't want a response
|
||||
SendUnreliablePacket(CorePackets::HeartbeatPacket(false));
|
||||
}
|
||||
return PacketDispatchResult::Success;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -136,12 +136,12 @@ namespace AzNetworking
|
||||
AZ_DISABLE_COPY_MOVE(UdpConnection);
|
||||
|
||||
UdpNetworkInterface& m_networkInterface;
|
||||
UdpPacketTracker m_packetTracker;
|
||||
UdpReliableQueue m_reliableQueue;
|
||||
UdpFragmentQueue m_fragmentQueue;
|
||||
ConnectionState m_state = ConnectionState::Disconnected;
|
||||
ConnectionRole m_connectionRole = ConnectionRole::Connector;
|
||||
DtlsEndpoint m_dtlsEndpoint;
|
||||
UdpPacketTracker m_packetTracker;
|
||||
UdpReliableQueue m_reliableQueue;
|
||||
UdpFragmentQueue m_fragmentQueue;
|
||||
ConnectionState m_state = ConnectionState::Disconnected;
|
||||
ConnectionRole m_connectionRole = ConnectionRole::Connector;
|
||||
DtlsEndpoint m_dtlsEndpoint;
|
||||
|
||||
AZ::TimeMs m_lastSentPacketMs;
|
||||
uint32_t m_unackedPacketCount = 0;
|
||||
|
||||
@@ -20,7 +20,13 @@ namespace AzNetworking
|
||||
|
||||
void UdpFragmentQueue::Update()
|
||||
{
|
||||
m_timeoutQueue.UpdateTimeouts(*this);
|
||||
m_timeoutQueue.UpdateTimeouts([this](TimeoutQueue::TimeoutItem& item)
|
||||
{
|
||||
const SequenceId fragmentSequence = static_cast<SequenceId>(item.m_userData & 0xFF);
|
||||
AZLOG(NET_FragmentQueue, "Timing out unreliable fragmented packet %u", static_cast<uint32_t>(fragmentSequence));
|
||||
m_packetFragments.erase(fragmentSequence);
|
||||
return TimeoutResult::Delete;
|
||||
});
|
||||
}
|
||||
|
||||
void UdpFragmentQueue::Reset()
|
||||
@@ -163,12 +169,4 @@ namespace AzNetworking
|
||||
|
||||
return handledPacket;
|
||||
}
|
||||
|
||||
TimeoutResult UdpFragmentQueue::HandleTimeout(TimeoutQueue::TimeoutItem& item)
|
||||
{
|
||||
const SequenceId fragmentSequence = static_cast<SequenceId>(item.m_userData & 0xFF);
|
||||
AZLOG(NET_FragmentQueue, "Timing out unreliable fragmented packet %u", static_cast<uint32_t>(fragmentSequence));
|
||||
m_packetFragments.erase(fragmentSequence);
|
||||
return TimeoutResult::Delete;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace AzNetworking
|
||||
//! @class UdpFragmentQueue
|
||||
//! @brief Class for reconstructing packet chunks into the original unsegmented packet.
|
||||
class UdpFragmentQueue
|
||||
: public ITimeoutHandler
|
||||
{
|
||||
|
||||
public:
|
||||
@@ -51,11 +50,6 @@ namespace AzNetworking
|
||||
|
||||
private:
|
||||
|
||||
//! Handler callback for timed out items.
|
||||
//! @param item containing registered timeout details
|
||||
//! @return ETimeoutResult for whether to re-register or discard the timeout params
|
||||
TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) override;
|
||||
|
||||
TimeoutQueue m_timeoutQueue;
|
||||
SequenceGenerator m_sequenceGenerator;
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace AzNetworking
|
||||
|
||||
AZ_CVAR(bool, net_UdpTimeoutConnections, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Boolean value on whether we should timeout Udp connections");
|
||||
AZ_CVAR(AZ::TimeMs, net_UdpPacketTimeSliceMs, AZ::TimeMs{ 8 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The number of milliseconds to allow for packet processing");
|
||||
AZ_CVAR(AZ::TimeMs, net_UdpHeartbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Udp connection heartbeat frequency");
|
||||
AZ_CVAR(uint32_t, net_UdpUnackedHeartbeats, 5, nullptr, AZ::ConsoleFunctorFlags::Null, "The number of heartbeats to attempt to send to keep a connection alive before giving up");
|
||||
AZ_CVAR(AZ::TimeMs, net_UdpDefaultTimeoutMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Udp connection");
|
||||
AZ_CVAR(AZ::TimeMs, net_MinPacketTimeoutMs, AZ::TimeMs{ 200 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Minimum time to wait before timing out an unacked packet");
|
||||
AZ_CVAR(int32_t, net_MaxTimeoutsPerFrame, 1000, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of packet timeouts to allow to process in a single frame");
|
||||
@@ -139,7 +139,8 @@ namespace AzNetworking
|
||||
}
|
||||
|
||||
const ConnectionId connectionId = m_connectionSet.GetNextConnectionId();
|
||||
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast<uint64_t>(connectionId), m_timeoutMs);
|
||||
const AZ::TimeMs timeoutTimeMs = m_timeoutMs / static_cast<AZ::TimeMs>(static_cast<int32_t>(net_UdpUnackedHeartbeats));
|
||||
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast<uint64_t>(connectionId), timeoutTimeMs);
|
||||
|
||||
AZStd::unique_ptr<UdpConnection> connection = AZStd::make_unique<UdpConnection>(connectionId, remoteAddress, *this, ConnectionRole::Connector);
|
||||
UdpPacketEncodingBuffer dtlsData;
|
||||
@@ -277,6 +278,7 @@ namespace AzNetworking
|
||||
}
|
||||
|
||||
timeoutItem->UpdateTimeoutTime(startTimeMs);
|
||||
connection->m_timeoutCounter = 0;
|
||||
|
||||
PacketDispatchResult handledPacket = PacketDispatchResult::Failure;
|
||||
if (header.GetPacketType() < aznumeric_cast<PacketType>(CorePackets::PacketType::MAX))
|
||||
@@ -319,16 +321,10 @@ namespace AzNetworking
|
||||
const AZ::TimeMs receiveTimeMs = AZ::GetElapsedTimeMs() - startTimeMs;
|
||||
|
||||
// Time out any stale client connections
|
||||
{
|
||||
ConnectionTimeoutFunctor functor(*this);
|
||||
m_connectionTimeoutQueue.UpdateTimeouts(functor);
|
||||
}
|
||||
m_connectionTimeoutQueue.UpdateTimeouts([this](TimeoutQueue::TimeoutItem& item) { return HandleConnectionTimeout(item); });
|
||||
|
||||
// Time out any packets that haven't been acked within our timeout window
|
||||
{
|
||||
PacketTimeoutFunctor functor(*this);
|
||||
m_packetTimeoutQueue.UpdateTimeouts(functor, static_cast<int32_t>(net_MaxTimeoutsPerFrame));
|
||||
}
|
||||
m_packetTimeoutQueue.UpdateTimeouts([this](TimeoutQueue::TimeoutItem& item) { return HandlePacketTimeout(item); }, static_cast<int32_t>(net_MaxTimeoutsPerFrame));
|
||||
|
||||
// Delete any connections we've disconnected
|
||||
for (RemovedConnection& removedConnection : m_removedConnections)
|
||||
@@ -709,21 +705,14 @@ namespace AzNetworking
|
||||
{
|
||||
// Packets involved in handshake are InitiateConnection, ConnectionHandshake and FragmentedPackets of ConnectionHandshake
|
||||
return packetType == aznumeric_cast<PacketType>(CorePackets::PacketType::InitiateConnectionPacket) ||
|
||||
packetType == aznumeric_cast<PacketType>(CorePackets::PacketType::ConnectionHandshakePacket) ||
|
||||
(packetType == aznumeric_cast<PacketType>(CorePackets::PacketType::FragmentedPacket) && endpoint.IsConnecting());
|
||||
packetType == aznumeric_cast<PacketType>(CorePackets::PacketType::ConnectionHandshakePacket) ||
|
||||
(packetType == aznumeric_cast<PacketType>(CorePackets::PacketType::FragmentedPacket) && endpoint.IsConnecting());
|
||||
}
|
||||
|
||||
|
||||
UdpNetworkInterface::ConnectionTimeoutFunctor::ConnectionTimeoutFunctor(UdpNetworkInterface& networkInterface)
|
||||
: m_networkInterface(networkInterface)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
TimeoutResult UdpNetworkInterface::ConnectionTimeoutFunctor::HandleTimeout(TimeoutQueue::TimeoutItem& item)
|
||||
TimeoutResult UdpNetworkInterface::HandleConnectionTimeout(TimeoutQueue::TimeoutItem& item)
|
||||
{
|
||||
const ConnectionId connectionId = ConnectionId(aznumeric_cast<uint32_t>(item.m_userData));
|
||||
UdpConnection* udpConnection = static_cast<UdpConnection*>(m_networkInterface.m_connectionSet.GetConnection(connectionId));
|
||||
UdpConnection* udpConnection = static_cast<UdpConnection*>(m_connectionSet.GetConnection(connectionId));
|
||||
|
||||
if (udpConnection == nullptr)
|
||||
{
|
||||
@@ -731,22 +720,23 @@ namespace AzNetworking
|
||||
return TimeoutResult::Delete;
|
||||
}
|
||||
|
||||
if (udpConnection->GetConnectionState() == ConnectionState::Connecting)
|
||||
if ((udpConnection->GetConnectionState() == ConnectionState::Connecting)
|
||||
&& udpConnection->GetDtlsEndpoint().IsConnecting())
|
||||
{
|
||||
if (udpConnection->GetDtlsEndpoint().IsConnecting())
|
||||
{
|
||||
// DTLS prefers we resend data lost over the wire with fresh SSL IDs so account for that here
|
||||
UdpPacketEncodingBuffer dtlsData;
|
||||
udpConnection->ProcessHandshakeData(dtlsData);
|
||||
return TimeoutResult::Refresh;
|
||||
}
|
||||
// DTLS prefers we resend data lost over the wire with fresh SSL IDs so account for that here
|
||||
UdpPacketEncodingBuffer dtlsData;
|
||||
udpConnection->ProcessHandshakeData(dtlsData);
|
||||
return TimeoutResult::Refresh;
|
||||
}
|
||||
|
||||
if (udpConnection->GetConnectionRole() == ConnectionRole::Connector)
|
||||
if ((udpConnection->GetConnectionRole() == ConnectionRole::Connector)
|
||||
&& (udpConnection->m_timeoutCounter < net_UdpUnackedHeartbeats))
|
||||
{
|
||||
udpConnection->SendUnreliablePacket(CorePackets::HeartbeatPacket());
|
||||
// Set the request response flag to true since we want a response to keep the connection alive
|
||||
udpConnection->SendUnreliablePacket(CorePackets::HeartbeatPacket(true));
|
||||
++udpConnection->m_timeoutCounter;
|
||||
}
|
||||
else if (net_UdpTimeoutConnections && (m_networkInterface.GetTimeoutMs() > AZ::TimeMs{ 0 }))
|
||||
else if (net_UdpTimeoutConnections && (GetTimeoutMs() > AZ::TimeMs{ 0 }))
|
||||
{
|
||||
udpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local);
|
||||
return TimeoutResult::Delete;
|
||||
@@ -755,19 +745,13 @@ namespace AzNetworking
|
||||
return TimeoutResult::Refresh;
|
||||
}
|
||||
|
||||
UdpNetworkInterface::PacketTimeoutFunctor::PacketTimeoutFunctor(UdpNetworkInterface& networkInterface)
|
||||
: m_networkInterface(networkInterface)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
TimeoutResult UdpNetworkInterface::PacketTimeoutFunctor::HandleTimeout(TimeoutQueue::TimeoutItem& item)
|
||||
TimeoutResult UdpNetworkInterface::HandlePacketTimeout(TimeoutQueue::TimeoutItem& item)
|
||||
{
|
||||
ConnectionId connectionId;
|
||||
PacketId packetId;
|
||||
ReliabilityType reliability;
|
||||
DecodeTimeoutId(item.m_userData, connectionId, packetId, reliability);
|
||||
UdpConnection* connection = static_cast<UdpConnection*>(m_networkInterface.m_connectionSet.GetConnection(connectionId));
|
||||
UdpConnection* connection = static_cast<UdpConnection*>(m_connectionSet.GetConnection(connectionId));
|
||||
|
||||
if (connection == nullptr)
|
||||
{
|
||||
@@ -782,16 +766,14 @@ namespace AzNetworking
|
||||
case PacketTimeoutResult::Acked:
|
||||
// Packet was already acked, just discard this timeout entry
|
||||
return TimeoutResult::Delete;
|
||||
|
||||
case PacketTimeoutResult::Pending:
|
||||
// Packet timed out before we received any info about it's sequence from the remote endpoint
|
||||
// The connection latency may have increased, and our Rtt metrics may still be adjusting..
|
||||
// Just throw it back into the timeout queue
|
||||
return TimeoutResult::Refresh;
|
||||
|
||||
case PacketTimeoutResult::Lost:
|
||||
// Packet timed out and was not acked, so we consider it lost
|
||||
m_networkInterface.m_connectionListener.OnPacketLost(connection, packetId);
|
||||
m_connectionListener.OnPacketLost(connection, packetId);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -149,34 +149,24 @@ namespace AzNetworking
|
||||
//! @param endpoint whether the disconnection was initiated locally or remotely
|
||||
void RequestDisconnect(UdpConnection* connection, DisconnectReason reason, TerminationEndpoint endpoint);
|
||||
|
||||
//! Internal helper to check if a packet's type is for connection handshake
|
||||
//! Internal helper to check if a packet's type is for connection handshake.
|
||||
//! @param endpoint DTLS endpoint participating in the handshake
|
||||
//! @param packetType type of the packet
|
||||
//! @return if the packet is for handshake
|
||||
bool IsHandshakePacket(const DtlsEndpoint& endpoint, AzNetworking::PacketType packetType) const;
|
||||
|
||||
//! Internal helper to manage connection timeout behaviour.
|
||||
//! @param item the timeout item corresponding to the timed out connection
|
||||
//! @return whether to delete or persist the timeout item
|
||||
TimeoutResult HandleConnectionTimeout(TimeoutQueue::TimeoutItem& item);
|
||||
|
||||
//! Internal helper to manage packet timeout behaviour.
|
||||
//! @param item the timeout item corresponding to the timed out packet
|
||||
//! @return whether to delete or persist the timeout item
|
||||
TimeoutResult HandlePacketTimeout(TimeoutQueue::TimeoutItem& item);
|
||||
|
||||
AZ_DISABLE_COPY_MOVE(UdpNetworkInterface);
|
||||
|
||||
struct ConnectionTimeoutFunctor final
|
||||
: public ITimeoutHandler
|
||||
{
|
||||
ConnectionTimeoutFunctor(UdpNetworkInterface& networkInterface);
|
||||
TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) override;
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(ConnectionTimeoutFunctor);
|
||||
UdpNetworkInterface& m_networkInterface;
|
||||
};
|
||||
|
||||
struct PacketTimeoutFunctor final
|
||||
: public ITimeoutHandler
|
||||
{
|
||||
PacketTimeoutFunctor(UdpNetworkInterface& networkInterface);
|
||||
TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) override;
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(PacketTimeoutFunctor);
|
||||
UdpNetworkInterface& m_networkInterface;
|
||||
};
|
||||
|
||||
AZ::Name m_name;
|
||||
TrustZone m_trustZone;
|
||||
uint16_t m_port = 0;
|
||||
|
||||
@@ -657,13 +657,18 @@ bool SpinBoxWatcher::handleMouseDragStepping(QAbstractSpinBox* spinBox, QEvent*
|
||||
QPoint screenPos = mouseEvent->screenPos().toPoint();
|
||||
const int xPos = screenPos.x();
|
||||
int newXPos = xPos;
|
||||
// cursor bounces on the left and right side of the screen
|
||||
// looks like buggy behaviour so mouse cursor is wrapped
|
||||
// around to the other side of the screen.
|
||||
if (xPos >= screenRect.right())
|
||||
{
|
||||
newXPos = screenRect.right() - 1;
|
||||
// wraps mouse cursor around to the left side of the screen
|
||||
newXPos = screenRect.left() + 1;
|
||||
}
|
||||
else if (xPos <= screenRect.left())
|
||||
{
|
||||
newXPos = screenRect.left() + 1;
|
||||
// wraps mouse cursor around to the right side of the screen
|
||||
newXPos = screenRect.right() - 1;
|
||||
}
|
||||
|
||||
if (newXPos != xPos)
|
||||
|
||||
@@ -10,7 +10,6 @@ AzToolsFramework--EntityOutlinerWidget #m_display_options
|
||||
{
|
||||
qproperty-icon: url(:/stylesheet/img/UI20/menu-centered.svg);
|
||||
qproperty-iconSize: 16px 16px;
|
||||
qproperty-flat: true;
|
||||
}
|
||||
|
||||
AzToolsFramework--EntityOutlinerWidget QTreeView
|
||||
|
||||
@@ -739,24 +739,6 @@ public:
|
||||
#undef GetUserName
|
||||
#endif
|
||||
|
||||
|
||||
struct IProfilingSystem
|
||||
{
|
||||
// <interfuscator:shuffle>
|
||||
virtual ~IProfilingSystem() {}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// VTune Profiling interface.
|
||||
|
||||
// Summary:
|
||||
// Resumes vtune data collection.
|
||||
virtual void VTuneResume() = 0;
|
||||
// Summary:
|
||||
// Pauses vtune data collection.
|
||||
virtual void VTunePause() = 0;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// </interfuscator:shuffle>
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Description:
|
||||
@@ -851,7 +833,6 @@ struct ISystem
|
||||
virtual IMovieSystem* GetIMovieSystem() = 0;
|
||||
virtual ::IConsole* GetIConsole() = 0;
|
||||
virtual IRemoteConsole* GetIRemoteConsole() = 0;
|
||||
virtual IProfilingSystem* GetIProfilingSystem() = 0;
|
||||
virtual ISystemEventDispatcher* GetISystemEventDispatcher() = 0;
|
||||
|
||||
virtual ITimer* GetITimer() = 0;
|
||||
@@ -1121,8 +1102,8 @@ inline ISystem* GetISystem()
|
||||
|
||||
// Description:
|
||||
// This function must be called once by each module at the beginning, to setup global pointers.
|
||||
extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, const char* moduleName);
|
||||
extern "C" AZ_DLL_EXPORT void ModuleShutdownISystem(ISystem* pSystem);
|
||||
void ModuleInitISystem(ISystem* pSystem, const char* moduleName);
|
||||
void ModuleShutdownISystem(ISystem* pSystem);
|
||||
extern "C" AZ_DLL_EXPORT void InjectEnvironment(void* env);
|
||||
extern "C" AZ_DLL_EXPORT void DetachEnvironment();
|
||||
|
||||
|
||||
@@ -76,8 +76,6 @@ public:
|
||||
::IConsole * ());
|
||||
MOCK_METHOD0(GetIRemoteConsole,
|
||||
IRemoteConsole * ());
|
||||
MOCK_METHOD0(GetIProfilingSystem,
|
||||
IProfilingSystem * ());
|
||||
MOCK_METHOD0(GetISystemEventDispatcher,
|
||||
ISystemEventDispatcher * ());
|
||||
MOCK_METHOD0(GetITimer,
|
||||
|
||||
@@ -96,47 +96,6 @@ unsigned countElements (const std::vector<T>& arrT, const T& x)
|
||||
*/
|
||||
namespace stl
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Compare member of class/struct.
|
||||
//
|
||||
// e.g. Sort Vec3s by x component
|
||||
//
|
||||
// std::sort(vec3s.begin(), vec3s.end(), stl::member_compare<Vec3, float, &Vec3::x>());
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename OWNER_TYPE, typename MEMBER_TYPE, MEMBER_TYPE OWNER_TYPE::* MEMBER_PTR, typename EQUALITY = std::less<MEMBER_TYPE> >
|
||||
struct member_compare
|
||||
{
|
||||
inline bool operator () (const OWNER_TYPE& lhs, const OWNER_TYPE& rhs) const
|
||||
{
|
||||
return EQUALITY()(lhs.*MEMBER_PTR, rhs.*MEMBER_PTR);
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Compare member of class/struct against parameter.
|
||||
//
|
||||
// e.g. Find Vec3 with x component less than 1.0
|
||||
//
|
||||
// std::find_if(vec3s.begin(), vec3s.end(), stl::member_compare_param<Vec3, float, &Vec3::x>(1.0f));
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename OWNER_TYPE, typename MEMBER_TYPE, MEMBER_TYPE OWNER_TYPE::* MEMBER_PTR, typename EQUALITY = std::less<MEMBER_TYPE> >
|
||||
struct member_compare_param
|
||||
{
|
||||
inline member_compare_param(const MEMBER_TYPE& _value)
|
||||
: value(_value)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool operator () (const OWNER_TYPE& rhs) const
|
||||
{
|
||||
return EQUALITY()(rhs.*MEMBER_PTR, value);
|
||||
}
|
||||
|
||||
const MEMBER_TYPE& value;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Searches the given entry in the map by key, and if there is none, returns the default value
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -154,48 +113,6 @@ namespace stl
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Inserts and returns a reference to the given value in the map, or returns the current one if it's already there.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename Map>
|
||||
inline typename Map::mapped_type& map_insert_or_get(Map& mapKeyToValue, const typename Map::key_type& key, const typename Map::mapped_type& defValue = typename Map::mapped_type())
|
||||
{
|
||||
auto&& iresult = mapKeyToValue.insert(typename Map::value_type(key, defValue));
|
||||
return iresult.first->second;
|
||||
}
|
||||
|
||||
// searches the given entry in the map by key, and if there is none, returns the default value
|
||||
// The values are taken/returned in REFERENCEs rather than values
|
||||
template <typename Key, typename mapped_type, typename Traits, typename Allocator>
|
||||
inline mapped_type& find_in_map_ref(std::map<Key, mapped_type, Traits, Allocator>& mapKeyToValue, const Key& key, mapped_type& valueDefault)
|
||||
{
|
||||
typedef std::map<Key, mapped_type, Traits, Allocator> Map;
|
||||
typename Map::iterator it = mapKeyToValue.find (key);
|
||||
if (it == mapKeyToValue.end())
|
||||
{
|
||||
return valueDefault;
|
||||
}
|
||||
else
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Key, typename mapped_type, typename Traits, typename Allocator>
|
||||
inline const mapped_type& find_in_map_ref(const std::map<Key, mapped_type, Traits, Allocator>& mapKeyToValue, const Key& key, const mapped_type& valueDefault)
|
||||
{
|
||||
typedef std::map<Key, mapped_type, Traits, Allocator> Map;
|
||||
typename Map::const_iterator it = mapKeyToValue.find (key);
|
||||
if (it == mapKeyToValue.end())
|
||||
{
|
||||
return valueDefault;
|
||||
}
|
||||
else
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Fills vector with contents of map.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -210,20 +127,6 @@ namespace stl
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Fills vector with contents of set.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <class Set, class Vector>
|
||||
inline void set_to_vector(const Set& theSet, Vector& array)
|
||||
{
|
||||
array.resize(0);
|
||||
array.reserve(theSet.size());
|
||||
for (typename Set::const_iterator it = theSet.begin(); it != theSet.end(); ++it)
|
||||
{
|
||||
array.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Find and erase element from container.
|
||||
// @return true if item was find and erased, false if item not found.
|
||||
@@ -312,48 +215,6 @@ namespace stl
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Push back to container unique element.
|
||||
// @return true if item added, false overwise.
|
||||
template <class CONTAINER, class PREDICATE, typename VALUE>
|
||||
inline bool push_back_unique_if(CONTAINER& container, const PREDICATE& predicate, const VALUE& value)
|
||||
{
|
||||
typename CONTAINER::iterator end = container.end();
|
||||
|
||||
if (AZStd::find_if(container.begin(), end, predicate) == end)
|
||||
{
|
||||
container.push_back(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Push back to container contents of another container
|
||||
template <class Container, class Iter>
|
||||
inline void push_back_range(Container& container, Iter begin, Iter end)
|
||||
{
|
||||
for (Iter it = begin; it != end; ++it)
|
||||
{
|
||||
container.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Push back to container contents of another container, if not already present
|
||||
template <class Container, class Iter>
|
||||
inline void push_back_range_unique(Container& container, Iter begin, Iter end)
|
||||
{
|
||||
for (Iter it = begin; it != end; ++it)
|
||||
{
|
||||
push_back_unique(container, *it);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Find element in container.
|
||||
// @return true if item found.
|
||||
@@ -373,107 +234,6 @@ namespace stl
|
||||
return (it == last || value != *it) ? last : it;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Find element in a sorted container using binary search with logarithmic efficiency.
|
||||
// @return true if item was inserted.
|
||||
template <class Container, class Value>
|
||||
inline bool binary_insert_unique(Container& container, const Value& value)
|
||||
{
|
||||
typename Container::iterator it = std::lower_bound(container.begin(), container.end(), value);
|
||||
if (it != container.end())
|
||||
{
|
||||
if (*it == value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
container.insert(it, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
container.insert(container.end(), value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Find element in a sorted container using binary search with logarithmic efficiency.
|
||||
// and erases if element found.
|
||||
// @return true if item was erased.
|
||||
template <class Container, class Value>
|
||||
inline bool binary_erase(Container& container, const Value& value)
|
||||
{
|
||||
typename Container::iterator it = std::lower_bound(container.begin(), container.end(), value);
|
||||
if (it != container.end() && *it == value)
|
||||
{
|
||||
container.erase(it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename ItT, typename Func>
|
||||
ItT remove_from_heap(ItT begin, ItT end, ItT at, Func order)
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
--end;
|
||||
if (at == end)
|
||||
{
|
||||
return at;
|
||||
}
|
||||
|
||||
size_t idx = std::distance(begin, at);
|
||||
swap(*end, *at);
|
||||
|
||||
size_t length = std::distance(begin, end);
|
||||
size_t parent, child;
|
||||
|
||||
if (idx > 0 && order(*(begin + idx / 2), *(begin + idx)))
|
||||
{
|
||||
do
|
||||
{
|
||||
parent = idx / 2;
|
||||
swap(*(begin + idx), *(begin + parent));
|
||||
idx = parent;
|
||||
|
||||
if (idx == 0 || order(*(begin + idx), *(begin + idx / 2)))
|
||||
{
|
||||
return end;
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
child = idx * 2 + 1;
|
||||
if (child >= length)
|
||||
{
|
||||
return end;
|
||||
}
|
||||
|
||||
ItT left = begin + child;
|
||||
ItT right = begin + child + 1;
|
||||
|
||||
if (right < end && order(*left, *right))
|
||||
{
|
||||
++child;
|
||||
}
|
||||
|
||||
if (order(*(begin + child), *(begin + idx)))
|
||||
{
|
||||
return end;
|
||||
}
|
||||
|
||||
swap(*(begin + child), *(begin + idx));
|
||||
idx = child;
|
||||
}
|
||||
while (true);
|
||||
}
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
struct container_object_deleter
|
||||
{
|
||||
template<typename T>
|
||||
@@ -506,18 +266,6 @@ namespace stl
|
||||
return type.c_str();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Case sensetive less key for any type convertable to const char*.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <class Type>
|
||||
struct less_strcmp
|
||||
{
|
||||
bool operator()(const Type& left, const Type& right) const
|
||||
{
|
||||
return strcmp(constchar_cast(left), constchar_cast(right)) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Case insensetive less key for any type convertable to const char*.
|
||||
template <class Type>
|
||||
@@ -690,89 +438,4 @@ namespace stl
|
||||
stl::free_container(container);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, size_t Length, typename Func>
|
||||
inline void for_each_array(T (&buffer)[Length], Func func)
|
||||
{
|
||||
std::for_each(&buffer[0], &buffer[Length], func);
|
||||
}
|
||||
|
||||
template <typename T, typename D, size_t Length, typename Func>
|
||||
inline void for_each_array(StaticInstance<T, D>(&buffer)[Length], Func func)
|
||||
{
|
||||
for (size_t idx = 0; idx < Length; ++idx)
|
||||
{
|
||||
func(*buffer[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void destruct(T* p)
|
||||
{
|
||||
p->~T();
|
||||
}
|
||||
}
|
||||
|
||||
#define DEFINE_INTRUSIVE_LINKED_LIST(Class) \
|
||||
template<> \
|
||||
Class * stl::intrusive_linked_list_node<Class>::m_root_intrusive = nullptr;
|
||||
|
||||
// define the maplikestruct, used to approximate the memory requirements for a map node
|
||||
namespace stl
|
||||
{
|
||||
struct MapLikeStruct
|
||||
{
|
||||
bool color;
|
||||
void* parent;
|
||||
void* left;
|
||||
void* right;
|
||||
};
|
||||
}
|
||||
template <class Map>
|
||||
unsigned sizeOfMap(Map& map)
|
||||
{
|
||||
unsigned size = 0;
|
||||
for (typename Map::iterator it = map.begin(); it != map.end(); it++)
|
||||
{
|
||||
typename Map::mapped_type& T = it->second;
|
||||
size += T.Size();
|
||||
}
|
||||
size += map.size() * sizeof(stl::MapLikeStruct);
|
||||
return size;
|
||||
}
|
||||
template <class Map>
|
||||
unsigned sizeOfMapStr(Map& map)
|
||||
{
|
||||
unsigned size = 0;
|
||||
for (typename Map::iterator it = map.begin(); it != map.end(); it++)
|
||||
{
|
||||
typename Map::mapped_type& T = it->second;
|
||||
size += T.capacity();
|
||||
}
|
||||
size += map.size() * sizeof(stl::MapLikeStruct);
|
||||
return size;
|
||||
}
|
||||
template <class Map>
|
||||
unsigned sizeOfMapP(Map& map)
|
||||
{
|
||||
unsigned size = 0;
|
||||
for (typename Map::iterator it = map.begin(); it != map.end(); it++)
|
||||
{
|
||||
typename Map::mapped_type& T = it->second;
|
||||
size += T->Size();
|
||||
}
|
||||
size += map.size() * sizeof(stl::MapLikeStruct);
|
||||
return size;
|
||||
}
|
||||
template <class Map>
|
||||
unsigned sizeOfMapS(Map& map)
|
||||
{
|
||||
unsigned size = 0;
|
||||
for (typename Map::iterator it = map.begin(); it != map.end(); it++)
|
||||
{
|
||||
typename Map::mapped_type& T = it->second;
|
||||
size += sizeof(T);
|
||||
}
|
||||
size += map.size() * sizeof(stl::MapLikeStruct);
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ void InitCRTHandlers() {}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// This is an entry to DLL initialization function that must be called for each loaded module
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, [[maybe_unused]] const char* moduleName)
|
||||
void ModuleInitISystem(ISystem* pSystem, [[maybe_unused]] const char* moduleName)
|
||||
{
|
||||
if (gEnv) // Already registered.
|
||||
{
|
||||
@@ -96,7 +96,7 @@ extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, [[maybe_unused
|
||||
} // if pSystem
|
||||
}
|
||||
|
||||
extern "C" AZ_DLL_EXPORT void ModuleShutdownISystem([[maybe_unused]] ISystem* pSystem)
|
||||
void ModuleShutdownISystem([[maybe_unused]] ISystem* pSystem)
|
||||
{
|
||||
// Unregister with AZ environment.
|
||||
AZ::Environment::Detach();
|
||||
|
||||
@@ -143,9 +143,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
|
||||
#include <AzFramework/Input/Buses/Notifications/RawInputNotificationBus_Platform.h>
|
||||
|
||||
// To enable profiling with vtune (https://software.intel.com/en-us/intel-vtune-amplifier-xe), make sure the line below is not commented out
|
||||
//#define PROFILE_WITH_VTUNE
|
||||
|
||||
#include <process.h>
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
@@ -154,10 +151,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
|
||||
// profilers api.
|
||||
VTuneFunction VTResume = NULL;
|
||||
VTuneFunction VTPause = NULL;
|
||||
|
||||
// Define global cvars.
|
||||
SSystemCVars g_cvars;
|
||||
|
||||
@@ -516,8 +509,6 @@ void CSystem::ShutDown()
|
||||
|
||||
ShutdownFileSystem();
|
||||
|
||||
ShutdownModuleLibraries();
|
||||
|
||||
EBUS_EVENT(CrySystemEventBus, OnCrySystemPostShutdown);
|
||||
}
|
||||
|
||||
@@ -697,31 +688,6 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
|
||||
m_bPaused = false;
|
||||
}
|
||||
|
||||
#ifdef PROFILE_WITH_VTUNE
|
||||
if (m_bInDevMode)
|
||||
{
|
||||
if (VTPause != NULL && VTResume != NULL)
|
||||
{
|
||||
static bool bVtunePaused = true;
|
||||
|
||||
const AzFramework::InputChannel* inputChannelScrollLock = AzFramework::InputChannelRequests::FindInputChannel(AzFramework::InputDeviceKeyboard::Key::WindowsSystemScrollLock);
|
||||
const bool bPaused = (inputChannelScrollLock ? inputChannelScrollLock->IsActive() : false);
|
||||
|
||||
{
|
||||
if (bVtunePaused && !bPaused)
|
||||
{
|
||||
GetIProfilingSystem()->VTuneResume();
|
||||
}
|
||||
if (!bVtunePaused && bPaused)
|
||||
{
|
||||
GetIProfilingSystem()->VTunePause();
|
||||
}
|
||||
bVtunePaused = bPaused;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //PROFILE_WITH_VTUNE
|
||||
|
||||
#ifndef EXCLUDE_UPDATE_ON_CONSOLE
|
||||
if (m_bIgnoreUpdates)
|
||||
{
|
||||
@@ -1255,30 +1221,6 @@ CPNoise3* CSystem::GetNoiseGen()
|
||||
return &m_pNoiseGen;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CProfilingSystem::VTuneResume()
|
||||
{
|
||||
#ifdef PROFILE_WITH_VTUNE
|
||||
if (VTResume)
|
||||
{
|
||||
CryLogAlways("VTune Resume");
|
||||
VTResume();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CProfilingSystem::VTunePause()
|
||||
{
|
||||
#ifdef PROFILE_WITH_VTUNE
|
||||
if (VTPause)
|
||||
{
|
||||
VTPause();
|
||||
CryLogAlways("VTune Pause");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void CSystem::OnLanguageCVarChanged(ICVar* language)
|
||||
{
|
||||
|
||||
@@ -105,10 +105,6 @@ struct IDataProbe;
|
||||
|
||||
#define PHSYICS_OBJECT_ENTITY 0
|
||||
|
||||
using VTuneFunction = void (__cdecl *)(void);
|
||||
extern VTuneFunction VTResume;
|
||||
extern VTuneFunction VTPause;
|
||||
|
||||
#define MAX_STREAMING_POOL_INDEX 6
|
||||
#define MAX_THREAD_POOL_INDEX 6
|
||||
|
||||
@@ -139,7 +135,6 @@ struct SSystemCVars
|
||||
int sys_ai;
|
||||
int sys_entitysystem;
|
||||
int sys_trackview;
|
||||
int sys_vtune;
|
||||
float sys_update_profile_time;
|
||||
int sys_limit_phys_thread_count;
|
||||
int sys_MaxFPS;
|
||||
@@ -169,21 +164,6 @@ extern SSystemCVars g_cvars;
|
||||
|
||||
class CSystem;
|
||||
|
||||
struct CProfilingSystem
|
||||
: public IProfilingSystem
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// VTune Profiling interface.
|
||||
|
||||
// Summary:
|
||||
// Resumes vtune data collection.
|
||||
void VTuneResume() override;
|
||||
// Summary:
|
||||
// Pauses vtune data collection.
|
||||
void VTunePause() override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
};
|
||||
|
||||
class AssetSystem;
|
||||
|
||||
/*
|
||||
@@ -262,7 +242,6 @@ public:
|
||||
IViewSystem* GetIViewSystem() override;
|
||||
ILevelSystem* GetILevelSystem() override;
|
||||
ISystemEventDispatcher* GetISystemEventDispatcher() override { return m_pSystemEventDispatcher; }
|
||||
IProfilingSystem* GetIProfilingSystem() override { return &m_ProfilingSystem; }
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// retrieves the perlin noise singleton instance
|
||||
CPNoise3* GetNoiseGen() override;
|
||||
@@ -324,8 +303,6 @@ public:
|
||||
void SetVersionInfo(const char* const szVersion);
|
||||
#endif
|
||||
|
||||
void ShutdownModuleLibraries();
|
||||
|
||||
#if defined(WIN32)
|
||||
friend LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
#endif
|
||||
@@ -344,8 +321,6 @@ private:
|
||||
// Release all resources.
|
||||
void ShutDown();
|
||||
|
||||
bool LoadEngineDLLs();
|
||||
|
||||
//! @name Initialization routines
|
||||
//@{
|
||||
bool InitConsole();
|
||||
@@ -361,11 +336,8 @@ private:
|
||||
void CreateSystemVars();
|
||||
void CreateAudioVars();
|
||||
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle> LoadDLL(const char* dllName);
|
||||
|
||||
void FreeLib(AZStd::unique_ptr<AZ::DynamicModuleHandle>& hLibModule);
|
||||
|
||||
bool UnloadDLL(const char* dllName);
|
||||
void QueryVersionInfo();
|
||||
void LogVersion();
|
||||
void LogBuildInfo();
|
||||
@@ -380,8 +352,6 @@ private:
|
||||
|
||||
void AddCVarGroupDirectory(const AZStd::string& sPath) override;
|
||||
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle> LoadDynamiclibrary(const char* dllName) const;
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEM_H_SECTION_3
|
||||
#include AZ_RESTRICTED_FILE(System_h)
|
||||
@@ -437,9 +407,6 @@ private: // ------------------------------------------------------
|
||||
bool m_bDrawConsole; //!< Set to true if OK to draw the console.
|
||||
bool m_bDrawUI; //!< Set to true if OK to draw UI.
|
||||
|
||||
|
||||
std::map<AZ::Crc32, AZStd::unique_ptr<AZ::DynamicModuleHandle> > m_moduleDLLHandles;
|
||||
|
||||
//! current active process
|
||||
IProcess* m_pProcess;
|
||||
|
||||
@@ -564,8 +531,6 @@ private: // ------------------------------------------------------
|
||||
ESystemConfigSpec m_nMaxConfigSpec;
|
||||
ESystemConfigPlatform m_ConfigPlatform;
|
||||
|
||||
CProfilingSystem m_ProfilingSystem;
|
||||
|
||||
// Pause mode.
|
||||
bool m_bPaused;
|
||||
bool m_bNoUpdate;
|
||||
@@ -588,8 +553,6 @@ public:
|
||||
const SFileVersion& GetProductVersion() override;
|
||||
const SFileVersion& GetBuildVersion() override;
|
||||
|
||||
bool InitVTuneProfiler();
|
||||
|
||||
void OpenPlatformPaks();
|
||||
void OpenLanguagePak(const char* sLanguage);
|
||||
void OpenLanguageAudioPak(const char* sLanguage);
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM) || defined(AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS)
|
||||
#undef AZ_RESTRICTED_SECTION
|
||||
#define SYSTEMINIT_CPP_SECTION_1 1
|
||||
#define SYSTEMINIT_CPP_SECTION_2 2
|
||||
#define SYSTEMINIT_CPP_SECTION_3 3
|
||||
#define SYSTEMINIT_CPP_SECTION_4 4
|
||||
@@ -70,9 +69,6 @@
|
||||
#include "windows.h"
|
||||
#include <float.h>
|
||||
|
||||
// To enable profiling with vtune (https://software.intel.com/en-us/intel-vtune-amplifier-xe), make sure the line below is not commented out
|
||||
//#define PROFILE_WITH_VTUNE
|
||||
|
||||
#endif //WIN32
|
||||
|
||||
#include <IRenderer.h>
|
||||
@@ -171,34 +167,6 @@ void CryEngineSignalHandler(int signal)
|
||||
|
||||
#define LOCALIZATION_TRANSLATIONS_LIST_FILE_NAME "Libs/Localization/localization.xml"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#if defined(WIN32) || defined(LINUX) || defined(APPLE)
|
||||
# define DLL_MODULE_INIT_ISYSTEM "ModuleInitISystem"
|
||||
# define DLL_MODULE_SHUTDOWN_ISYSTEM "ModuleShutdownISystem"
|
||||
# define DLL_INITFUNC_RENDERER "PackageRenderConstructor"
|
||||
# define DLL_INITFUNC_SOUND "CreateSoundSystem"
|
||||
# define DLL_INITFUNC_FONT "CreateCryFontInterface"
|
||||
# define DLL_INITFUNC_3DENGINE "CreateCry3DEngine"
|
||||
# define DLL_INITFUNC_UI "CreateLyShineInterface"
|
||||
#define AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#elif defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEMINIT_CPP_SECTION_1
|
||||
#include AZ_RESTRICTED_FILE(SystemInit_cpp)
|
||||
#endif
|
||||
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
||||
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#else
|
||||
# define DLL_MODULE_INIT_ISYSTEM (LPCSTR)2
|
||||
# define DLL_MODULE_SHUTDOWN_ISYSTEM (LPCSTR)3
|
||||
# define DLL_INITFUNC_RENDERER (LPCSTR)1
|
||||
# define DLL_INITFUNC_RENDERER (LPCSTR)1
|
||||
# define DLL_INITFUNC_SOUND (LPCSTR)1
|
||||
# define DLL_INITFUNC_PHYSIC (LPCSTR)1
|
||||
# define DLL_INITFUNC_FONT (LPCSTR)1
|
||||
# define DLL_INITFUNC_3DENGINE (LPCSTR)1
|
||||
# define DLL_INITFUNC_UI (LPCSTR)1
|
||||
#endif
|
||||
|
||||
#define AZ_TRACE_SYSTEM_WINDOW AZ::Debug::Trace::GetDefaultSystemWindow()
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -292,96 +260,6 @@ static void CmdCrashTest(IConsoleCmdArgs* pArgs)
|
||||
}
|
||||
AZ_POP_DISABLE_WARNING
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
struct SysSpecOverrideSink
|
||||
: public ILoadConfigurationEntrySink
|
||||
{
|
||||
virtual void OnLoadConfigurationEntry(const char* szKey, const char* szValue, const char* szGroup)
|
||||
{
|
||||
ICVar* pCvar = gEnv->pConsole->GetCVar(szKey);
|
||||
|
||||
if (pCvar)
|
||||
{
|
||||
const bool wasNotInConfig = ((pCvar->GetFlags() & VF_WASINCONFIG) == 0);
|
||||
bool applyCvar = wasNotInConfig;
|
||||
if (applyCvar == false)
|
||||
{
|
||||
// Special handling for sys_spec_full
|
||||
if (azstricmp(szKey, "sys_spec_full") == 0)
|
||||
{
|
||||
// If it is set to 0 then ignore this request to set to something else
|
||||
// If it is set to 0 then the user wants to changes system spec settings in system.cfg
|
||||
if (pCvar->GetIVal() != 0)
|
||||
{
|
||||
applyCvar = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This could bypass the restricted cvar checks that exist elsewhere depending on
|
||||
// the calling code so we also need check here before setting.
|
||||
bool isConst = pCvar->IsConstCVar();
|
||||
bool isCheat = ((pCvar->GetFlags() & (VF_CHEAT | VF_CHEAT_NOCHECK | VF_CHEAT_ALWAYS_CHECK)) != 0);
|
||||
bool isReadOnly = ((pCvar->GetFlags() & VF_READONLY) != 0);
|
||||
bool isDeprecated = ((pCvar->GetFlags() & VF_DEPRECATED) != 0);
|
||||
bool allowApplyCvar = true;
|
||||
|
||||
if ((isConst || isCheat || isReadOnly) || isDeprecated)
|
||||
{
|
||||
allowApplyCvar = !isDeprecated && (gEnv->pSystem->IsDevMode()) || (gEnv->IsEditor());
|
||||
}
|
||||
|
||||
if ((allowApplyCvar) || ALLOW_CONST_CVAR_MODIFICATIONS)
|
||||
{
|
||||
applyCvar = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (applyCvar)
|
||||
{
|
||||
pCvar->Set(szValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
CryLogAlways("NOT VF_WASINCONFIG Ignoring cvar '%s' new value '%s' old value '%s' group '%s'", szKey, szValue, pCvar->GetString(), szGroup);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CryLogAlways("Can't find cvar '%s' value '%s' group '%s'", szKey, szValue, szGroup);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#if !defined(CONSOLE)
|
||||
struct SysSpecOverrideSinkConsole
|
||||
: public ILoadConfigurationEntrySink
|
||||
{
|
||||
virtual void OnLoadConfigurationEntry(const char* szKey, const char* szValue, const char* szGroup)
|
||||
{
|
||||
// Ignore platform-specific cvars that should just be executed on the console
|
||||
if (azstricmp(szGroup, "Platform") == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ICVar* pCvar = gEnv->pConsole->GetCVar(szKey);
|
||||
if (pCvar)
|
||||
{
|
||||
pCvar->Set(szValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the cvar doesn't exist, calling this function only saves the value in case it's registered later where
|
||||
// at that point it will be set from the stored value. This is required because otherwise registering the
|
||||
// cvar bypasses any callbacks and uses values directly from the cvar group files.
|
||||
gEnv->pConsole->LoadConfigVar(szKey, szValue);
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
static ESystemConfigPlatform GetDevicePlatform()
|
||||
{
|
||||
#if defined(AZ_PLATFORM_WINDOWS) || defined(AZ_PLATFORM_LINUX)
|
||||
@@ -405,117 +283,6 @@ static ESystemConfigPlatform GetDevicePlatform()
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(AZ_MONOLITHIC_BUILD)
|
||||
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle> CSystem::LoadDynamiclibrary(const char* dllName) const
|
||||
{
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle> handle = AZ::DynamicModuleHandle::Create(dllName);
|
||||
|
||||
bool libraryLoaded = handle->Load(false);
|
||||
// We need to inject the environment first thing so that allocators are available immediately
|
||||
InjectEnvironmentFunction injectEnv = handle->GetFunction<InjectEnvironmentFunction>(INJECT_ENVIRONMENT_FUNCTION);
|
||||
if (injectEnv)
|
||||
{
|
||||
auto env = AZ::Environment::GetInstance();
|
||||
injectEnv(env);
|
||||
}
|
||||
|
||||
if (!libraryLoaded)
|
||||
{
|
||||
handle.release();
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle> CSystem::LoadDLL(const char* dllName)
|
||||
{
|
||||
AZ_TracePrintf(AZ_TRACE_SYSTEM_WINDOW, "Loading DLL: %s", dllName);
|
||||
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle> handle = LoadDynamiclibrary(dllName);
|
||||
|
||||
if (!handle)
|
||||
{
|
||||
#if defined(LINUX) || defined(APPLE)
|
||||
AZ_Assert(false, "Error loading dylib: %s, error : %s\n", dllName, dlerror());
|
||||
#else
|
||||
AZ_Assert(false, "Error loading dll: %s, error code %d", dllName, GetLastError());
|
||||
#endif
|
||||
return handle;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// After loading DLL initialize it by calling ModuleInitISystem
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
AZStd::string moduleName = PathUtil::GetFileName(dllName);
|
||||
|
||||
typedef void*(*PtrFunc_ModuleInitISystem)(ISystem* pSystem, const char* moduleName);
|
||||
PtrFunc_ModuleInitISystem pfnModuleInitISystem = handle->GetFunction<PtrFunc_ModuleInitISystem>(DLL_MODULE_INIT_ISYSTEM);
|
||||
if (pfnModuleInitISystem)
|
||||
{
|
||||
pfnModuleInitISystem(this, moduleName.c_str());
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
// TODO:DLL #endif //#if defined(AZ_HAS_DLL_SUPPORT) && !defined(AZ_MONOLITHIC_BUILD)
|
||||
#endif //if !defined(AZ_MONOLITHIC_BUILD)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CSystem::LoadEngineDLLs()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CSystem::UnloadDLL(const char* dllName)
|
||||
{
|
||||
bool isSuccess = false;
|
||||
|
||||
AZ::Crc32 key(dllName);
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle> empty;
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle>& hModule = stl::find_in_map_ref(m_moduleDLLHandles, key, empty);
|
||||
if ((hModule) && (hModule->IsLoaded()))
|
||||
{
|
||||
DetachEnvironmentFunction detachEnv = hModule->GetFunction<DetachEnvironmentFunction>(DETACH_ENVIRONMENT_FUNCTION);
|
||||
if (detachEnv)
|
||||
{
|
||||
detachEnv();
|
||||
}
|
||||
|
||||
isSuccess = hModule->Unload();
|
||||
hModule.release();
|
||||
}
|
||||
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CSystem::ShutdownModuleLibraries()
|
||||
{
|
||||
#if !defined(AZ_MONOLITHIC_BUILD)
|
||||
for (auto iterator = m_moduleDLLHandles.begin(); iterator != m_moduleDLLHandles.end(); ++iterator)
|
||||
{
|
||||
typedef void*( * PtrFunc_ModuleShutdownISystem )(ISystem* pSystem);
|
||||
|
||||
PtrFunc_ModuleShutdownISystem pfnModuleShutdownISystem = iterator->second->GetFunction<PtrFunc_ModuleShutdownISystem>(DLL_MODULE_SHUTDOWN_ISYSTEM);
|
||||
if (pfnModuleShutdownISystem)
|
||||
{
|
||||
pfnModuleShutdownISystem(this);
|
||||
}
|
||||
if (iterator->second->IsLoaded())
|
||||
{
|
||||
iterator->second->Unload();
|
||||
}
|
||||
iterator->second.release();
|
||||
}
|
||||
|
||||
m_moduleDLLHandles.clear();
|
||||
|
||||
#endif // !defined(AZ_MONOLITHIC_BUILD)
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
bool CSystem::InitConsole()
|
||||
@@ -704,33 +471,6 @@ bool CSystem::InitAudioSystem(const SSystemInitParams& initParams)
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CSystem::InitVTuneProfiler()
|
||||
{
|
||||
#ifdef PROFILE_WITH_VTUNE
|
||||
|
||||
WIN_HMODULE hModule = LoadDLL("VTuneApi.dll");
|
||||
if (!hModule)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
VTPause = (VTuneFunction) CryGetProcAddress(hModule, "VTPause");
|
||||
VTResume = (VTuneFunction) CryGetProcAddress(hModule, "VTResume");
|
||||
if (!VTPause || !VTResume)
|
||||
{
|
||||
AZ_Assert(false, "VTune did not initialize correctly.")
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_TracePrintf(AZ_TRACE_SYSTEM_WINDOW, "VTune API Initialized");
|
||||
}
|
||||
#endif //PROFILE_WITH_VTUNE
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CSystem::InitLocalization()
|
||||
{
|
||||
@@ -1705,8 +1445,6 @@ void CSystem::CreateSystemVars()
|
||||
m_sys_memory_debug = REGISTER_INT("sys_memory_debug", 0, VF_CHEAT,
|
||||
"Enables to activate low memory situation is specific places in the code (argument defines which place), 0=off");
|
||||
|
||||
REGISTER_CVAR2("sys_vtune", &g_cvars.sys_vtune, 0, VF_NULL, "");
|
||||
|
||||
#if defined(AZ_RESTRICTED_PLATFORM)
|
||||
#define AZ_RESTRICTED_SECTION SYSTEMINIT_CPP_SECTION_17
|
||||
#include AZ_RESTRICTED_FILE(SystemInit_cpp)
|
||||
|
||||
@@ -4140,11 +4140,19 @@ struct LockedFileTest
|
||||
switch (message.GetMessageType())
|
||||
{
|
||||
case SourceFileNotificationMessage::MessageType:
|
||||
if (const auto sourceFileMessage = azrtti_cast<const SourceFileNotificationMessage*>(&message);
|
||||
sourceFileMessage != nullptr && sourceFileMessage->m_type == SourceFileNotificationMessage::NotificationType::FileRemoved
|
||||
&& m_callback)
|
||||
if (const auto sourceFileMessage = azrtti_cast<const SourceFileNotificationMessage*>(&message); sourceFileMessage != nullptr &&
|
||||
sourceFileMessage->m_type == SourceFileNotificationMessage::NotificationType::FileRemoved)
|
||||
{
|
||||
m_callback();
|
||||
// The File Remove message will occur before an attempt to delete the file
|
||||
// Wait for more than 1 File Remove message.
|
||||
// This indicates the AP has attempted to delete the file once, failed to do so and is now retrying
|
||||
++m_deleteCounter;
|
||||
|
||||
if(m_deleteCounter > 1 && m_callback)
|
||||
{
|
||||
m_callback();
|
||||
m_callback = {}; // Unset it to be safe, we only intend to run the callback once
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -4168,6 +4176,7 @@ struct LockedFileTest
|
||||
ModtimeScanningTest::TearDown();
|
||||
}
|
||||
|
||||
AZStd::atomic_int m_deleteCounter{ 0 };
|
||||
AZStd::function<void()> m_callback;
|
||||
};
|
||||
|
||||
@@ -4207,6 +4216,10 @@ TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeleteFails)
|
||||
|
||||
TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeletesWhenReleased)
|
||||
{
|
||||
// This test is intended to verify the AP will successfully retry deleting a source asset
|
||||
// when one of its product assets is locked temporarily
|
||||
// We'll lock the file by holding it open
|
||||
|
||||
auto theFile = m_data->m_absolutePath[1].toUtf8();
|
||||
const char* theFileString = theFile.constData();
|
||||
auto [sourcePath, productPath] = *m_data->m_productPaths.find(theFileString);
|
||||
@@ -4219,19 +4232,22 @@ TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeletesWhenReleased)
|
||||
ASSERT_GT(m_data->m_productPaths.size(), 0);
|
||||
QFile product(productPath);
|
||||
|
||||
// Open the file and keep it open to lock it
|
||||
// We'll start a thread later to unlock the file
|
||||
// This will allow us to test how AP handles trying to delete a locked file
|
||||
ASSERT_TRUE(product.open(QIODevice::ReadOnly));
|
||||
|
||||
// Check if we can delete the file now, if we can't, proceed with the test
|
||||
// If we can, it means the OS running this test doesn't lock open files so there's nothing to test
|
||||
if (!AZ::IO::SystemFile::Delete(productPath.toUtf8().constData()))
|
||||
{
|
||||
AZStd::thread workerThread;
|
||||
m_deleteCounter = 0;
|
||||
|
||||
m_callback = [&product, &workerThread]() {
|
||||
workerThread = AZStd::thread([&product]() {
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(60));
|
||||
product.close();
|
||||
});
|
||||
// Set up a callback which will fire after at least 1 retry
|
||||
// Unlock the file at that point so AP can successfully delete it
|
||||
m_callback = [&product]()
|
||||
{
|
||||
product.close();
|
||||
};
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
@@ -4241,8 +4257,9 @@ TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeletesWhenReleased)
|
||||
|
||||
EXPECT_FALSE(QFile::exists(productPath));
|
||||
EXPECT_EQ(m_data->m_deletedSources.size(), 1);
|
||||
|
||||
workerThread.join();
|
||||
|
||||
EXPECT_GT(m_deleteCounter, 1); // Make sure the AP tried more than once to delete the file
|
||||
m_errorAbsorber->ExpectAsserts(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -49,6 +49,12 @@ int main(int argc, char* argv[])
|
||||
AZStd::unique_ptr<AzFramework::ProcessWatcher> shellProcess(AzFramework::ProcessWatcher::LaunchProcess(shellProcessLaunch, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE));
|
||||
shellProcess->WaitForProcessToExit(120);
|
||||
shellProcess.reset();
|
||||
|
||||
parameters = AZStd::string::format("-c \"%s/scripts/o3de.sh register --this-engine\"", enginePath.c_str());
|
||||
shellProcessLaunch.m_commandlineParameters = parameters;
|
||||
shellProcess.reset(AzFramework::ProcessWatcher::LaunchProcess(shellProcessLaunch, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE));
|
||||
shellProcess->WaitForProcessToExit(120);
|
||||
shellProcess.reset();
|
||||
|
||||
AZ::IO::FixedMaxPath projectManagerPath = installedBinariesFolder/"o3de.app"/"Contents"/"MacOS"/"o3de";
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
|
||||
@@ -35,3 +35,5 @@ ly_add_target(
|
||||
AZ::AzCore
|
||||
AZ::GridMate
|
||||
)
|
||||
|
||||
ly_add_dependencies(LuaIDE GridHub)
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <QProcessEnvironment>
|
||||
#include <QDir>
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
@@ -94,5 +96,10 @@ namespace O3DE::ProjectManager
|
||||
QProcessEnvironment::systemEnvironment(),
|
||||
QObject::tr("Running get_python script..."));
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath GetEditorDirectory()
|
||||
{
|
||||
return AZ::Utils::GetExecutableDirectory();
|
||||
}
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
@@ -104,5 +107,35 @@ namespace O3DE::ProjectManager
|
||||
QProcessEnvironment::systemEnvironment(),
|
||||
QObject::tr("Running get_python script..."));
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath GetEditorDirectory()
|
||||
{
|
||||
AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory();
|
||||
AZ::IO::FixedMaxPath editorPath{ executableDirectory };
|
||||
editorPath /= "../../../Editor.app/Contents/MacOS";
|
||||
editorPath = editorPath.LexicallyNormal();
|
||||
if (!AZ::IO::SystemFile::IsDirectory(editorPath.c_str()))
|
||||
{
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if (AZ::IO::FixedMaxPath installedBinariesPath;
|
||||
settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder))
|
||||
{
|
||||
if (AZ::IO::FixedMaxPath engineRootFolder;
|
||||
settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
|
||||
{
|
||||
editorPath = engineRootFolder / installedBinariesPath / "Editor.app/Contents/MacOS";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!AZ::IO::SystemFile::IsDirectory(editorPath.c_str()))
|
||||
{
|
||||
AZ_Error("ProjectManager", false, "Unable to find the Editor app bundle!");
|
||||
}
|
||||
}
|
||||
|
||||
return editorPath;
|
||||
}
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
@@ -139,5 +141,10 @@ namespace O3DE::ProjectManager
|
||||
QProcessEnvironment::systemEnvironment(),
|
||||
QObject::tr("Running get_python script..."));
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath GetEditorDirectory()
|
||||
{
|
||||
return AZ::Utils::GetExecutableDirectory();
|
||||
}
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <QWidget>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
#include <AzCore/IO/Path/Path_fwd.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
@@ -67,7 +68,8 @@ namespace O3DE::ProjectManager
|
||||
AZ::Outcome<QString, QString> GetProjectBuildPath(const QString& projectPath);
|
||||
AZ::Outcome<void, QString> OpenCMakeGUI(const QString& projectPath);
|
||||
AZ::Outcome<QString, QString> RunGetPythonScript(const QString& enginePath);
|
||||
|
||||
|
||||
AZ::IO::FixedMaxPath GetEditorDirectory();
|
||||
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -392,11 +392,11 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
if (!WarnIfInBuildQueue(projectPath))
|
||||
{
|
||||
AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory();
|
||||
AZ::IO::FixedMaxPath executableDirectory = ProjectUtils::GetEditorDirectory();
|
||||
AZStd::string executableFilename = "Editor";
|
||||
AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION);
|
||||
auto cmdPath = AZ::IO::FixedMaxPathString::format(
|
||||
"%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(),
|
||||
"%s --regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(),
|
||||
projectPath.toStdString().c_str());
|
||||
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
|
||||
@@ -659,16 +659,17 @@ namespace AZ
|
||||
|
||||
AuxGeomIndex vertexOffset = aznumeric_cast<AuxGeomIndex>(primBuffer.m_vertexBuffer.size());
|
||||
AuxGeomIndex indexOffset = aznumeric_cast<AuxGeomIndex>(primBuffer.m_indexBuffer.size());
|
||||
const size_t vertexCountTotal = aznumeric_cast<size_t>(vertexOffset) + vertexCount;
|
||||
|
||||
if (aznumeric_cast<size_t>(vertexOffset) + vertexCount > MaxDynamicVertexCount)
|
||||
if (vertexCountTotal > MaxDynamicVertexCount)
|
||||
{
|
||||
AZ_WarningOnce("AuxGeom", false, "Draw function ignored, would exceed maximum allowed index of %d", MaxDynamicVertexCount);
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::Vector3 center(0.0f, 0.0f, 0.0f);
|
||||
primBuffer.m_vertexBuffer.reserve(vertexCount);
|
||||
primBuffer.m_indexBuffer.reserve(vertexCount);
|
||||
primBuffer.m_vertexBuffer.reserve(vertexCountTotal);
|
||||
primBuffer.m_indexBuffer.reserve(vertexCountTotal);
|
||||
for (uint32_t vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex)
|
||||
{
|
||||
AZ::u32 packedColor = packedColorFunction(vertexIndex);
|
||||
@@ -734,15 +735,16 @@ namespace AZ
|
||||
|
||||
AuxGeomIndex vertexOffset = aznumeric_cast<AuxGeomIndex>(primBuffer.m_vertexBuffer.size());
|
||||
AuxGeomIndex indexOffset = aznumeric_cast<AuxGeomIndex>(primBuffer.m_indexBuffer.size());
|
||||
const size_t vertexCountTotal = aznumeric_cast<size_t>(vertexOffset) + vertexCount;
|
||||
|
||||
if (aznumeric_cast<size_t>(vertexOffset) + vertexCount > MaxDynamicVertexCount)
|
||||
if (vertexCountTotal > MaxDynamicVertexCount)
|
||||
{
|
||||
AZ_WarningOnce("AuxGeom", false, "Draw function ignored, would exceed maximum allowed index of %d", MaxDynamicVertexCount);
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::Vector3 center(0.0f, 0.0f, 0.0f);
|
||||
primBuffer.m_vertexBuffer.reserve(vertexCount);
|
||||
primBuffer.m_vertexBuffer.reserve(vertexCountTotal);
|
||||
for (uint32_t vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex)
|
||||
{
|
||||
AZ::u32 packedColor = packedColorFunction(vertexIndex);
|
||||
@@ -753,7 +755,7 @@ namespace AZ
|
||||
}
|
||||
center /= aznumeric_cast<float>(vertexCount);
|
||||
|
||||
primBuffer.m_indexBuffer.reserve(indexCount);
|
||||
primBuffer.m_indexBuffer.reserve(indexCount + indexOffset);
|
||||
for (uint32_t index = 0; index < indexCount; ++index)
|
||||
{
|
||||
primBuffer.m_indexBuffer.push_back(vertexOffset + indexFunction(index));
|
||||
|
||||
@@ -152,8 +152,6 @@ namespace AZ
|
||||
|
||||
ResultCode FrameScheduler::ImportScopeProducer(ScopeProducer& scopeProducer)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RHI, "FrameScheduler: ImportScopeProducer");
|
||||
|
||||
if (!ValidateIsProcessing())
|
||||
{
|
||||
return RHI::ResultCode::InvalidOperation;
|
||||
@@ -266,7 +264,6 @@ namespace AZ
|
||||
|
||||
// Execute all queued resource invalidations, which will mark SRG's for compilation.
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RHI, "Invalidate Resources");
|
||||
ResourceInvalidateBus::ExecuteQueuedEvents();
|
||||
}
|
||||
|
||||
|
||||
@@ -82,9 +82,6 @@ namespace AZ
|
||||
// ALT+ENTER fullscreen switching using IDXGIFactory::MakeWindowAssociation (see also implementation of SwapChain::PresentInternal).
|
||||
// You must call the MakeWindowAssociation method after the creation of the swap chain, and on the factory object associated with the
|
||||
// target HWND swap chain, which you can guarantee by calling the IDXGIObject::GetParent method on the swap chain to locate the factory.
|
||||
//
|
||||
// ToDo: ATOM-14673 We should handle ALT+ENTER in the windows message loop and call AzFramework::NativeWindow::ToggleFullScreenState in
|
||||
// response, but that will have to wait until the WndProc function moves out of CrySystem (ideally into AzFramework::ApplicationWindows).
|
||||
IDXGIFactoryX* parentFactory = nullptr;
|
||||
m_swapChain->GetParent(__uuidof(IDXGIFactoryX), (void **)&parentFactory);
|
||||
DX12::AssertSuccess(parentFactory->MakeWindowAssociation(reinterpret_cast<HWND>(window), DXGI_MWA_NO_ALT_ENTER));
|
||||
|
||||
@@ -160,6 +160,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
Gem::Atom_RPI.Public
|
||||
Gem::Atom_RHI.Public
|
||||
Gem::Atom_RPI.Edit
|
||||
Gem::Atom_Utils.TestUtils.Static
|
||||
)
|
||||
ly_add_googletest(
|
||||
NAME Gem::Atom_RPI.Tests
|
||||
|
||||
@@ -209,10 +209,6 @@ namespace AZ
|
||||
//! Traversal will stop once all properties have been enumerated or the callback function returns false
|
||||
void EnumeratePropertiesInDisplayOrder(const EnumeratePropertiesCallback& callback) const;
|
||||
|
||||
//! Convert the property value into the format that will be stored in the source data
|
||||
//! This is primarily needed to support conversions of special types like enums and images
|
||||
bool ConvertPropertyValueToSourceDataFormat(const PropertyDefinition& propertyDefinition, MaterialPropertyValue& propertyValue) const;
|
||||
|
||||
Outcome<Data::Asset<MaterialTypeAsset>> CreateMaterialTypeAsset(Data::AssetId assetId, AZStd::string_view materialTypeSourceFilePath = "", bool elevateWarnings = true) const;
|
||||
|
||||
//! Possibly renames @propertyId based on the material version update steps.
|
||||
|
||||
@@ -154,6 +154,8 @@ namespace AZ
|
||||
|
||||
ConstPtr<RHI::PipelineLibraryData> LoadPipelineLibrary() const;
|
||||
void SavePipelineLibrary() const;
|
||||
|
||||
const ShaderVariant& GetVariantInternal(ShaderVariantStableId shaderVariantStableId);
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
/// AssetBus overrides
|
||||
|
||||
@@ -24,6 +24,9 @@ namespace AZ
|
||||
class ShaderReloadDebugTracker final
|
||||
{
|
||||
public:
|
||||
static void Init();
|
||||
static void Shutdown();
|
||||
|
||||
static bool IsEnabled();
|
||||
|
||||
//! Begin a code section. Will print a "[BEGIN] <sectionName>" header, and all subsequent calls will be indented.
|
||||
@@ -34,8 +37,8 @@ namespace AZ
|
||||
if (IsEnabled())
|
||||
{
|
||||
const AZStd::string sectionName = AZStd::string::format(sectionNameFormat, args...);
|
||||
AZ_TracePrintf("ShaderReloadDebug", "%*s [BEGIN] %s \n", s_indent, "", sectionName.c_str());
|
||||
s_indent += IndentSpaces;
|
||||
AZ_TracePrintf("ShaderReloadDebug", "%*s [BEGIN] %s \n", GetIndent(), "", sectionName.c_str());
|
||||
AddIndent();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -48,8 +51,8 @@ namespace AZ
|
||||
if (IsEnabled())
|
||||
{
|
||||
const AZStd::string sectionName = AZStd::string::format(sectionNameFormat, args...);
|
||||
s_indent -= IndentSpaces;
|
||||
AZ_TracePrintf("ShaderReloadDebug", "%*s [_END_] %s \n", s_indent, "", sectionName.c_str());
|
||||
RemoveIndent();
|
||||
AZ_TracePrintf("ShaderReloadDebug", "%*s [_END_] %s \n", GetIndent(), "", sectionName.c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -63,7 +66,7 @@ namespace AZ
|
||||
{
|
||||
const AZStd::string message = AZStd::string::format(format, args...);
|
||||
|
||||
AZ_TracePrintf("ShaderReloadDebug", "%*s %s \n", s_indent, "", message.c_str());
|
||||
AZ_TracePrintf("ShaderReloadDebug", "%*s %s \n", GetIndent(), "", message.c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -86,9 +89,12 @@ namespace AZ
|
||||
};
|
||||
|
||||
private:
|
||||
static bool s_enabled;
|
||||
static int s_indent;
|
||||
static constexpr int IndentSpaces = 4;
|
||||
|
||||
static void MakeReady();
|
||||
static void AddIndent();
|
||||
static void RemoveIndent();
|
||||
static int GetIndent();
|
||||
};
|
||||
|
||||
} // namespace RPI
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace AZ
|
||||
|
||||
//! Return the timestamp when the shader asset was built.
|
||||
//! This is used to synchronize versions of the ShaderAsset and ShaderVariantTreeAsset, especially during hot-reload.
|
||||
AZStd::sys_time_t GetShaderAssetBuildTimestamp() const;
|
||||
AZStd::sys_time_t GetBuildTimestamp() const;
|
||||
|
||||
//! Returns the shader option group layout.
|
||||
const ShaderOptionGroupLayout* GetShaderOptionGroupLayout() const;
|
||||
@@ -297,7 +297,7 @@ namespace AZ
|
||||
Name m_drawListName;
|
||||
|
||||
//! Use to synchronize versions of the ShaderAsset and ShaderVariantTreeAsset, especially during hot-reload.
|
||||
AZStd::sys_time_t m_shaderAssetBuildTimestamp = 0;
|
||||
AZStd::sys_time_t m_buildTimestamp = 0;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -300,48 +300,6 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
bool MaterialTypeSourceData::ConvertPropertyValueToSourceDataFormat(const PropertyDefinition& propertyDefinition, MaterialPropertyValue& propertyValue) const
|
||||
{
|
||||
if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Enum && propertyValue.Is<uint32_t>())
|
||||
{
|
||||
const uint32_t index = propertyValue.GetValue<uint32_t>();
|
||||
if (index >= propertyDefinition.m_enumValues.size())
|
||||
{
|
||||
AZ_Error("Material source data", false, "Invalid value for material enum property: '%s'.", propertyDefinition.m_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
propertyValue = propertyDefinition.m_enumValues[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Image asset references must be converted from asset IDs to a relative source file path
|
||||
if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Image && propertyValue.Is<Data::Asset<ImageAsset>>())
|
||||
{
|
||||
const Data::Asset<ImageAsset>& imageAsset = propertyValue.GetValue<Data::Asset<ImageAsset>>();
|
||||
|
||||
Data::AssetInfo imageAssetInfo;
|
||||
if (imageAsset.GetId().IsValid())
|
||||
{
|
||||
bool result = false;
|
||||
AZStd::string rootFilePath;
|
||||
const AZStd::string platformName = ""; // Empty for default
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAssetInfoById,
|
||||
imageAsset.GetId(), imageAsset.GetType(), platformName, imageAssetInfo, rootFilePath);
|
||||
if (!result)
|
||||
{
|
||||
AZ_Error("Material source data", false, "Image asset could not be found for property: '%s'.", propertyDefinition.m_name.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
propertyValue = imageAssetInfo.m_relativePath;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Outcome<Data::Asset<MaterialTypeAsset>> MaterialTypeSourceData::CreateMaterialTypeAsset(Data::AssetId assetId, AZStd::string_view materialTypeSourceFilePath, bool elevateWarnings) const
|
||||
{
|
||||
MaterialTypeAssetCreator materialTypeAssetCreator;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <Atom/RPI.Public/Pass/FullscreenTrianglePass.h>
|
||||
#include <Atom/RPI.Public/Pass/PassUtils.h>
|
||||
#include <Atom/RPI.Public/RPIUtils.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Pass/FullscreenTrianglePassData.h>
|
||||
#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
|
||||
@@ -46,16 +47,19 @@ namespace AZ
|
||||
|
||||
void FullscreenTrianglePass::OnShaderReinitialized(const Shader&)
|
||||
{
|
||||
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->FullscreenTrianglePass::OnShaderReinitialized", this);
|
||||
LoadShader();
|
||||
}
|
||||
|
||||
void FullscreenTrianglePass::OnShaderAssetReinitialized(const Data::Asset<ShaderAsset>&)
|
||||
{
|
||||
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->FullscreenTrianglePass::OnShaderAssetReinitialized", this);
|
||||
LoadShader();
|
||||
}
|
||||
|
||||
void FullscreenTrianglePass::OnShaderVariantReinitialized(const ShaderVariant&)
|
||||
{
|
||||
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->FullscreenTrianglePass::OnShaderVariantReinitialized", this);
|
||||
LoadShader();
|
||||
}
|
||||
|
||||
@@ -129,6 +133,8 @@ namespace AZ
|
||||
void FullscreenTrianglePass::InitializeInternal()
|
||||
{
|
||||
RenderPass::InitializeInternal();
|
||||
|
||||
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->FullscreenTrianglePass::InitializeInternal", this);
|
||||
|
||||
// This draw item purposefully does not reference any geometry buffers.
|
||||
// Instead it's expected that the extended class uses a vertex shader
|
||||
|
||||
@@ -216,8 +216,6 @@ namespace AZ
|
||||
|
||||
void RasterPass::CompileResources(const RHI::FrameGraphCompileContext& context)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "RasterPass: CompileResources");
|
||||
|
||||
if (m_shaderResourceGroup == nullptr)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -320,6 +320,30 @@ namespace AZ
|
||||
}
|
||||
|
||||
const ShaderVariant& Shader::GetVariant(ShaderVariantStableId shaderVariantStableId)
|
||||
{
|
||||
const ShaderVariant& variant = GetVariantInternal(shaderVariantStableId);
|
||||
|
||||
if (ShaderReloadDebugTracker::IsEnabled())
|
||||
{
|
||||
auto makeTimeString = [](AZStd::sys_time_t timestamp, AZStd::sys_time_t now)
|
||||
{
|
||||
AZStd::sys_time_t elapsedMicroseconds = now - timestamp;
|
||||
double elapsedSeconds = aznumeric_cast<double>(elapsedMicroseconds / 1'000'000);
|
||||
AZStd::string timeString = AZStd::string::format("%lld (%f seconds ago)", timestamp, elapsedSeconds);
|
||||
return timeString;
|
||||
};
|
||||
|
||||
AZStd::sys_time_t now = AZStd::GetTimeNowMicroSecond();
|
||||
|
||||
ShaderReloadDebugTracker::Printf("{%p}->Shader::GetVariant for shader '%s' [build time %s] found variant '%s' [build time %s]", this,
|
||||
m_asset.GetHint().c_str(), makeTimeString(m_asset->GetBuildTimestamp(), now).c_str(),
|
||||
variant.GetShaderVariantAsset().GetHint().c_str(), makeTimeString(variant.GetShaderVariantAsset()->GetBuildTimestamp(), now).c_str());
|
||||
}
|
||||
|
||||
return variant;
|
||||
}
|
||||
|
||||
const ShaderVariant& Shader::GetVariantInternal(ShaderVariantStableId shaderVariantStableId)
|
||||
{
|
||||
if (!shaderVariantStableId.IsValid() || shaderVariantStableId == ShaderAsset::RootShaderVariantStableId)
|
||||
{
|
||||
@@ -336,7 +360,7 @@ namespace AZ
|
||||
// reloaded, but some (or all) shader variants haven't been built yet. Since we want to use the latest version of the
|
||||
// shader code, ignore the old variants and fall back to the newer root variant instead. There's no need to report a
|
||||
// warning here because m_asset->GetVariant below will report one.
|
||||
if (findIt->second.GetBuildTimestamp() >= m_asset->GetShaderAssetBuildTimestamp())
|
||||
if (findIt->second.GetBuildTimestamp() >= m_asset->GetBuildTimestamp())
|
||||
{
|
||||
return findIt->second;
|
||||
}
|
||||
@@ -359,7 +383,7 @@ namespace AZ
|
||||
auto findIt = m_shaderVariants.find(shaderVariantStableId);
|
||||
if (findIt != m_shaderVariants.end())
|
||||
{
|
||||
if (findIt->second.GetBuildTimestamp() >= m_asset->GetShaderAssetBuildTimestamp())
|
||||
if (findIt->second.GetBuildTimestamp() >= m_asset->GetBuildTimestamp())
|
||||
{
|
||||
return findIt->second;
|
||||
}
|
||||
|
||||
@@ -7,24 +7,75 @@
|
||||
*/
|
||||
|
||||
#include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
|
||||
#include <AzCore/Module/Environment.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
bool ShaderReloadDebugTracker::s_enabled = false;
|
||||
int ShaderReloadDebugTracker::s_indent = 0;
|
||||
namespace ShaderReloadDebugTrackerInternal
|
||||
{
|
||||
static const char EnabledVariableName[] = "ShaderReloadDebugTracker enabled";
|
||||
static const char IndentVariableName[] = "ShaderReloadDebugTracker indent";
|
||||
|
||||
static EnvironmentVariable<bool> s_enabled;
|
||||
static EnvironmentVariable<int> s_indent;
|
||||
}
|
||||
|
||||
void ShaderReloadDebugTracker::Init()
|
||||
{
|
||||
ShaderReloadDebugTrackerInternal::s_enabled = AZ::Environment::CreateVariable<bool>(ShaderReloadDebugTrackerInternal::EnabledVariableName);
|
||||
ShaderReloadDebugTrackerInternal::s_indent = AZ::Environment::CreateVariable<int>(ShaderReloadDebugTrackerInternal::IndentVariableName);
|
||||
|
||||
ShaderReloadDebugTrackerInternal::s_enabled.Get() = false;
|
||||
ShaderReloadDebugTrackerInternal::s_indent.Get() = 0;
|
||||
}
|
||||
|
||||
void ShaderReloadDebugTracker::Shutdown()
|
||||
{
|
||||
ShaderReloadDebugTrackerInternal::s_enabled.Reset();
|
||||
ShaderReloadDebugTrackerInternal::s_indent.Reset();
|
||||
}
|
||||
|
||||
void ShaderReloadDebugTracker::MakeReady()
|
||||
{
|
||||
if (!ShaderReloadDebugTrackerInternal::s_enabled.IsValid())
|
||||
{
|
||||
ShaderReloadDebugTrackerInternal::s_enabled = AZ::Environment::FindVariable<bool>(ShaderReloadDebugTrackerInternal::EnabledVariableName);
|
||||
ShaderReloadDebugTrackerInternal::s_indent = AZ::Environment::FindVariable<int>(ShaderReloadDebugTrackerInternal::IndentVariableName);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderReloadDebugTracker::IsEnabled()
|
||||
{
|
||||
#ifdef AZ_ENABLE_SHADER_RELOAD_DEBUG_TRACKER
|
||||
MakeReady();
|
||||
|
||||
// Set this to true in the debugger to turn on hot reload tracing.
|
||||
// If needed, we could hook this up to a CVar.
|
||||
return s_enabled;
|
||||
return ShaderReloadDebugTrackerInternal::s_enabled.Get();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ShaderReloadDebugTracker::AddIndent()
|
||||
{
|
||||
MakeReady();
|
||||
ShaderReloadDebugTrackerInternal::s_indent.Get() += IndentSpaces;
|
||||
}
|
||||
|
||||
void ShaderReloadDebugTracker::RemoveIndent()
|
||||
{
|
||||
MakeReady();
|
||||
ShaderReloadDebugTrackerInternal::s_indent.Get() -= IndentSpaces;
|
||||
}
|
||||
|
||||
int ShaderReloadDebugTracker::GetIndent()
|
||||
{
|
||||
MakeReady();
|
||||
return ShaderReloadDebugTrackerInternal::s_indent.Get();
|
||||
}
|
||||
|
||||
ShaderReloadDebugTracker::ScopedSection::~ScopedSection()
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <Atom/RPI.Public/Shader/Shader.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroupPool.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Asset/AssetHandler.h>
|
||||
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
|
||||
@@ -86,10 +87,13 @@ namespace AZ
|
||||
};
|
||||
Data::InstanceDatabase<ShaderResourceGroupPool>::Create(azrtti_typeid<ShaderResourceGroupPool>(), handler, false);
|
||||
}
|
||||
|
||||
ShaderReloadDebugTracker::Init();
|
||||
}
|
||||
|
||||
void ShaderSystem::Shutdown()
|
||||
{
|
||||
ShaderReloadDebugTracker::Shutdown();
|
||||
Data::InstanceDatabase<Shader>::Destroy();
|
||||
Data::InstanceDatabase<ShaderResourceGroup>::Destroy();
|
||||
Data::InstanceDatabase<ShaderResourceGroupPool>::Destroy();
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace AZ
|
||||
->Field("pipelineStateType", &ShaderAsset::m_pipelineStateType)
|
||||
->Field("shaderOptionGroupLayout", &ShaderAsset::m_shaderOptionGroupLayout)
|
||||
->Field("drawListName", &ShaderAsset::m_drawListName)
|
||||
->Field("shaderAssetBuildTimestamp", &ShaderAsset::m_shaderAssetBuildTimestamp)
|
||||
->Field("shaderAssetBuildTimestamp", &ShaderAsset::m_buildTimestamp)
|
||||
->Field("perAPIShaderData", &ShaderAsset::m_perAPIShaderData)
|
||||
;
|
||||
}
|
||||
@@ -134,11 +134,11 @@ namespace AZ
|
||||
return m_drawListName;
|
||||
}
|
||||
|
||||
AZStd::sys_time_t ShaderAsset::GetShaderAssetBuildTimestamp() const
|
||||
AZStd::sys_time_t ShaderAsset::GetBuildTimestamp() const
|
||||
{
|
||||
return m_shaderAssetBuildTimestamp;
|
||||
return m_buildTimestamp;
|
||||
}
|
||||
|
||||
|
||||
void ShaderAsset::SetReady()
|
||||
{
|
||||
m_status = AssetStatus::Ready;
|
||||
@@ -256,7 +256,7 @@ namespace AZ
|
||||
}
|
||||
return GetRootVariant(supervariantIndex);
|
||||
}
|
||||
else if (variant->GetBuildTimestamp() >= m_shaderAssetBuildTimestamp)
|
||||
else if (variant->GetBuildTimestamp() >= m_buildTimestamp)
|
||||
{
|
||||
return variant;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace AZ
|
||||
{
|
||||
if (ValidateIsReady())
|
||||
{
|
||||
m_asset->m_shaderAssetBuildTimestamp = shaderAssetBuildTimestamp;
|
||||
m_asset->m_buildTimestamp = shaderAssetBuildTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ namespace AZ
|
||||
m_asset->m_pipelineStateType = sourceShaderAsset.m_pipelineStateType;
|
||||
m_asset->m_drawListName = sourceShaderAsset.m_drawListName;
|
||||
m_asset->m_shaderOptionGroupLayout = sourceShaderAsset.m_shaderOptionGroupLayout;
|
||||
m_asset->m_shaderAssetBuildTimestamp = sourceShaderAsset.m_shaderAssetBuildTimestamp;
|
||||
m_asset->m_buildTimestamp = sourceShaderAsset.m_buildTimestamp;
|
||||
|
||||
// copy root variant assets
|
||||
for (auto& perAPIShaderData : sourceShaderAsset.m_perAPIShaderData)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <Common/AssetManagerTestFixture.h>
|
||||
#include <Common/RHI/Stubs.h>
|
||||
#include <Common/RHI/Factory.h>
|
||||
#include <Common/AssetSystemStub.h>
|
||||
#include <Atom/Utils/TestUtils/AssetSystemStub.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
|
||||
@@ -10,8 +10,6 @@ set(FILES
|
||||
Tests/Buffer/BufferTests.cpp
|
||||
Tests/Common/AssetManagerTestFixture.cpp
|
||||
Tests/Common/AssetManagerTestFixture.h
|
||||
Tests/Common/AssetSystemStub.cpp
|
||||
Tests/Common/AssetSystemStub.h
|
||||
Tests/Common/ErrorMessageFinder.cpp
|
||||
Tests/Common/ErrorMessageFinder.h
|
||||
Tests/Common/ErrorMessageFinderTests.cpp
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0a1f8d75dcd85e8b4aa57f6c0c81af0300ff96915ba3c2b591095c215d5e1d8c
|
||||
size 12072
|
||||
@@ -61,3 +61,31 @@ ly_add_target(
|
||||
PRIVATE
|
||||
Gem::AtomToolsFramework.Static
|
||||
)
|
||||
|
||||
################################################################################
|
||||
# Tests
|
||||
################################################################################
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
|
||||
ly_add_target(
|
||||
NAME AtomToolsFramework.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
atomtoolsframework_tests_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
.
|
||||
Tests
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
AZ::AzTestShared
|
||||
Gem::AtomToolsFramework.Static
|
||||
Gem::Atom_Utils.TestUtils.Static
|
||||
)
|
||||
|
||||
ly_add_googletest(
|
||||
NAME Gem::AtomToolsFramework.Tests
|
||||
)
|
||||
|
||||
endif()
|
||||
+23
-3
@@ -7,11 +7,12 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/any.h>
|
||||
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
|
||||
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialPropertyValue.h>
|
||||
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/std/any.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -35,12 +36,31 @@ namespace AtomToolsFramework
|
||||
//! Convert and assign material property meta data fields to editor dynamic property configuration
|
||||
void ConvertToPropertyConfig(AtomToolsFramework::DynamicPropertyConfig& propertyConfig, const AZ::RPI::MaterialPropertyDynamicMetadata& propertyMetaData);
|
||||
|
||||
//! Convert and assign editor dynamic property configuration fields to material property meta data
|
||||
//! Convert and assign editor dynamic property configuration fields to material property meta data
|
||||
void ConvertToPropertyMetaData(AZ::RPI::MaterialPropertyDynamicMetadata& propertyMetaData, const AtomToolsFramework::DynamicPropertyConfig& propertyConfig);
|
||||
|
||||
//! Compare equality of data types and values of editor property stored in AZStd::any
|
||||
bool ArePropertyValuesEqual(const AZStd::any& valueA, const AZStd::any& valueB);
|
||||
|
||||
//! Convert the property value into the format that will be stored in the source data
|
||||
//! This is primarily needed to support conversions of special types like enums and images
|
||||
//! @param exportPath absolute path of the file being saved
|
||||
//! @param propertyDefinition describes type information and other details about propertyValue
|
||||
//! @param propertyValue the value being converted before saving
|
||||
bool ConvertToExportFormat(
|
||||
const AZStd::string& exportPath,
|
||||
const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition,
|
||||
AZ::RPI::MaterialPropertyValue& propertyValue);
|
||||
|
||||
//! Generate a file path from the exported file to the external reference.
|
||||
//! This function returns a relative path from the export file to the reference file.
|
||||
//! If the relative path is too different or distant from the export path then we return the asset folder relative path.
|
||||
//! @param exportPath absolute path of the file being saved
|
||||
//! @param referencePath absolute path of a file that will be treated as an external reference
|
||||
//! @param maxPathDepth the maximum relative depth or number of parent or child folders between the export path and the reference path
|
||||
AZStd::string GetExteralReferencePath(
|
||||
const AZStd::string& exportPath, const AZStd::string& referencePath, const uint32_t maxPathDepth = 2);
|
||||
|
||||
//! Traverse up the instance data node hierarchy to find the containing dynamic property object
|
||||
const AtomToolsFramework::DynamicProperty* FindDynamicPropertyForInstanceDataNode(const AzToolsFramework::InstanceDataNode* pNode);
|
||||
} // namespace AtomToolsFramework
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
|
||||
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
|
||||
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
|
||||
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RPI.Reflect/Image/ImageAsset.h>
|
||||
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
@@ -18,6 +19,7 @@
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Math/Vector4.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
|
||||
|
||||
namespace AtomToolsFramework
|
||||
@@ -163,6 +165,88 @@ namespace AtomToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConvertToExportFormat(
|
||||
const AZStd::string& exportPath,
|
||||
const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition,
|
||||
AZ::RPI::MaterialPropertyValue& propertyValue)
|
||||
{
|
||||
if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Enum && propertyValue.Is<uint32_t>())
|
||||
{
|
||||
const uint32_t index = propertyValue.GetValue<uint32_t>();
|
||||
if (index >= propertyDefinition.m_enumValues.size())
|
||||
{
|
||||
AZ_Error("AtomToolsFramework", false, "Invalid value for material enum property: '%s'.", propertyDefinition.m_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
propertyValue = propertyDefinition.m_enumValues[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Image asset references must be converted from asset IDs to a relative source file path
|
||||
if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Image)
|
||||
{
|
||||
if (propertyValue.Is<AZ::Data::Asset<AZ::RPI::ImageAsset>>())
|
||||
{
|
||||
const auto& imageAsset = propertyValue.GetValue<AZ::Data::Asset<AZ::RPI::ImageAsset>>();
|
||||
const auto& imagePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(imageAsset.GetId());
|
||||
propertyValue = GetExteralReferencePath(exportPath, imagePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyValue.Is<AZ::Data::Instance<AZ::RPI::Image>>())
|
||||
{
|
||||
const auto& image = propertyValue.GetValue<AZ::Data::Instance<AZ::RPI::Image>>();
|
||||
const auto& imagePath = image ? AZ::RPI::AssetUtils::GetSourcePathByAssetId(image->GetAssetId()) : "";
|
||||
propertyValue = GetExteralReferencePath(exportPath, imagePath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AZStd::string GetExteralReferencePath(const AZStd::string& exportPath, const AZStd::string& referencePath, const uint32_t maxPathDepth)
|
||||
{
|
||||
if (referencePath.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
AZ::IO::BasicPath<AZStd::string> exportFolder(exportPath);
|
||||
exportFolder.RemoveFilename();
|
||||
|
||||
const AZStd::string relativePath = AZ::IO::PathView(referencePath).LexicallyRelative(exportFolder).StringAsPosix();
|
||||
|
||||
// Count the difference in depth between the export file path and the referenced file path.
|
||||
uint32_t parentFolderCount = 0;
|
||||
AZStd::string::size_type pos = 0;
|
||||
const AZStd::string parentFolderToken = "..";
|
||||
while ((pos = relativePath.find(parentFolderToken, pos)) != AZStd::string::npos)
|
||||
{
|
||||
parentFolderCount++;
|
||||
pos += parentFolderToken.length();
|
||||
}
|
||||
|
||||
// If the difference in depth is too great then revert to using the asset folder relative path.
|
||||
// We could change this to only use relative paths for references in subfolders.
|
||||
if (parentFolderCount > maxPathDepth)
|
||||
{
|
||||
AZStd::string watchFolder;
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
bool sourceInfoFound = false;
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
|
||||
sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, referencePath.c_str(),
|
||||
assetInfo, watchFolder);
|
||||
if (sourceInfoFound)
|
||||
{
|
||||
return assetInfo.m_relativePath;
|
||||
}
|
||||
}
|
||||
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
const AtomToolsFramework::DynamicProperty* FindDynamicPropertyForInstanceDataNode(const AzToolsFramework::InstanceDataNode* pNode)
|
||||
{
|
||||
// Traverse up the hierarchy from the input node to search for an instance corresponding to material inspector property
|
||||
@@ -172,7 +256,8 @@ namespace AtomToolsFramework
|
||||
const AZ::SerializeContext::ClassData* classData = currentNode->GetClassMetadata();
|
||||
if (context && classData)
|
||||
{
|
||||
if (context->CanDowncast(classData->m_typeId, azrtti_typeid<AtomToolsFramework::DynamicProperty>(), classData->m_azRtti, nullptr))
|
||||
if (context->CanDowncast(
|
||||
classData->m_typeId, azrtti_typeid<AtomToolsFramework::DynamicProperty>(), classData->m_azRtti, nullptr))
|
||||
{
|
||||
return static_cast<const AtomToolsFramework::DynamicProperty*>(currentNode->FirstInstance());
|
||||
}
|
||||
|
||||
@@ -7,25 +7,71 @@
|
||||
*/
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <Atom/Utils/TestUtils/AssetSystemStub.h>
|
||||
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
|
||||
|
||||
class AtomToolsFrameworkTest
|
||||
: public ::testing::Test
|
||||
namespace UnitTest
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
class AtomToolsFrameworkTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
if (!AZ::AllocatorInstance<AZ::SystemAllocator>::IsReady())
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Create(AZ::SystemAllocator::Descriptor());
|
||||
}
|
||||
|
||||
m_assetSystemStub.Activate();
|
||||
|
||||
RegisterSourceAsset("objects/upgrades/materials/supercondor.material");
|
||||
RegisterSourceAsset("materials/condor.material");
|
||||
RegisterSourceAsset("materials/talisman.material");
|
||||
RegisterSourceAsset("materials/city.material");
|
||||
RegisterSourceAsset("materials/totem.material");
|
||||
RegisterSourceAsset("textures/orange.png");
|
||||
RegisterSourceAsset("textures/red.png");
|
||||
RegisterSourceAsset("textures/gold.png");
|
||||
RegisterSourceAsset("textures/fuzz.png");
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_assetSystemStub.Deactivate();
|
||||
|
||||
if (AZ::AllocatorInstance<AZ::SystemAllocator>::IsReady())
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterSourceAsset(const AZStd::string& path)
|
||||
{
|
||||
const AZ::IO::BasicPath assetRootPath = AZ::IO::PathView(m_assetRoot).LexicallyNormal();
|
||||
const AZ::IO::BasicPath normalizedPath = AZ::IO::BasicPath(assetRootPath).Append(path).LexicallyNormal();
|
||||
|
||||
AZ::Data::AssetInfo assetInfo = {};
|
||||
assetInfo.m_assetId = AZ::Uuid::CreateRandom();
|
||||
assetInfo.m_relativePath = normalizedPath.LexicallyRelative(assetRootPath).StringAsPosix();
|
||||
m_assetSystemStub.RegisterSourceInfo(normalizedPath.StringAsPosix().c_str(), assetInfo, assetRootPath.StringAsPosix().c_str());
|
||||
}
|
||||
|
||||
static constexpr const char* m_assetRoot = "d:/project/assets/";
|
||||
AssetSystemStub m_assetSystemStub;
|
||||
};
|
||||
|
||||
TEST_F(AtomToolsFrameworkTest, GetExteralReferencePath_Succeeds)
|
||||
{
|
||||
ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("", "", 2), "");
|
||||
ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/materials/condor.material", "", 2), "");
|
||||
ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/materials/talisman.material", "", 2), "");
|
||||
ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/materials/talisman.material", "d:/project/assets/textures/gold.png", 2), "../textures/gold.png");
|
||||
ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/materials/talisman.material", "d:/project/assets/textures/gold.png", 0), "textures/gold.png");
|
||||
ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/objects/upgrades/materials/supercondor.material", "d:/project/assets/materials/condor.material", 3), "../../../materials/condor.material");
|
||||
ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/objects/upgrades/materials/supercondor.material", "d:/project/assets/materials/condor.material", 2), "materials/condor.material");
|
||||
ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/objects/upgrades/materials/supercondor.material", "d:/project/assets/materials/condor.material", 1), "materials/condor.material");
|
||||
ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/objects/upgrades/materials/supercondor.material", "d:/project/assets/materials/condor.material", 0), "materials/condor.material");
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(AtomToolsFrameworkTest, SanityTest)
|
||||
{
|
||||
ASSERT_TRUE(true);
|
||||
}
|
||||
|
||||
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);
|
||||
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# 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
|
||||
Tests/AtomToolsFrameworkTest.cpp
|
||||
)
|
||||
@@ -230,18 +230,13 @@ namespace MaterialEditor
|
||||
|
||||
// create source data from properties
|
||||
MaterialSourceData sourceData;
|
||||
sourceData.m_materialType = m_materialSourceData.m_materialType;
|
||||
sourceData.m_parentMaterial = m_materialSourceData.m_parentMaterial;
|
||||
|
||||
AZ_Assert(m_materialAsset && m_materialAsset->GetMaterialTypeAsset(), "When IsOpen() is true, these assets should not be null.");
|
||||
sourceData.m_materialTypeVersion = m_materialAsset->GetMaterialTypeAsset()->GetVersion();
|
||||
|
||||
// Force save data to store forward slashes
|
||||
AzFramework::StringFunc::Replace(sourceData.m_materialType, "\\", "/");
|
||||
AzFramework::StringFunc::Replace(sourceData.m_parentMaterial, "\\", "/");
|
||||
sourceData.m_materialType = AtomToolsFramework::GetExteralReferencePath(m_absolutePath, m_materialSourceData.m_materialType);
|
||||
sourceData.m_parentMaterial = AtomToolsFramework::GetExteralReferencePath(m_absolutePath, m_materialSourceData.m_parentMaterial);
|
||||
|
||||
// populate sourceData with modified or overwritten properties
|
||||
const bool savedProperties = SavePropertiesToSourceData(sourceData, [](const AtomToolsFramework::DynamicProperty& property) {
|
||||
const bool savedProperties = SavePropertiesToSourceData(m_absolutePath, sourceData, [](const AtomToolsFramework::DynamicProperty& property)
|
||||
{
|
||||
return !AtomToolsFramework::ArePropertyValuesEqual(property.GetValue(), property.GetConfig().m_parentValue);
|
||||
});
|
||||
|
||||
@@ -304,18 +299,13 @@ namespace MaterialEditor
|
||||
|
||||
// create source data from properties
|
||||
MaterialSourceData sourceData;
|
||||
sourceData.m_materialType = m_materialSourceData.m_materialType;
|
||||
sourceData.m_parentMaterial = m_materialSourceData.m_parentMaterial;
|
||||
|
||||
AZ_Assert(m_materialAsset && m_materialAsset->GetMaterialTypeAsset(), "When IsOpen() is true, these assets should not be null.");
|
||||
sourceData.m_materialTypeVersion = m_materialAsset->GetMaterialTypeAsset()->GetVersion();
|
||||
|
||||
// Force save data to store forward slashes
|
||||
AzFramework::StringFunc::Replace(sourceData.m_materialType, "\\", "/");
|
||||
AzFramework::StringFunc::Replace(sourceData.m_parentMaterial, "\\", "/");
|
||||
sourceData.m_materialType = AtomToolsFramework::GetExteralReferencePath(normalizedSavePath, m_materialSourceData.m_materialType);
|
||||
sourceData.m_parentMaterial = AtomToolsFramework::GetExteralReferencePath(normalizedSavePath, m_materialSourceData.m_parentMaterial);
|
||||
|
||||
// populate sourceData with modified or overwritten properties
|
||||
const bool savedProperties = SavePropertiesToSourceData(sourceData, [](const AtomToolsFramework::DynamicProperty& property) {
|
||||
const bool savedProperties = SavePropertiesToSourceData(normalizedSavePath, sourceData, [](const AtomToolsFramework::DynamicProperty& property)
|
||||
{
|
||||
return !AtomToolsFramework::ArePropertyValuesEqual(property.GetValue(), property.GetConfig().m_parentValue);
|
||||
});
|
||||
|
||||
@@ -377,23 +367,18 @@ namespace MaterialEditor
|
||||
|
||||
// create source data from properties
|
||||
MaterialSourceData sourceData;
|
||||
sourceData.m_materialType = m_materialSourceData.m_materialType;
|
||||
|
||||
AZ_Assert(m_materialAsset && m_materialAsset->GetMaterialTypeAsset(), "When IsOpen() is true, these assets should not be null.");
|
||||
sourceData.m_materialTypeVersion = m_materialAsset->GetMaterialTypeAsset()->GetVersion();
|
||||
sourceData.m_materialType = AtomToolsFramework::GetExteralReferencePath(normalizedSavePath, m_materialSourceData.m_materialType);
|
||||
|
||||
// Only assign a parent path if the source was a .material
|
||||
if (AzFramework::StringFunc::Path::IsExtension(m_relativePath.c_str(), MaterialSourceData::Extension))
|
||||
{
|
||||
sourceData.m_parentMaterial = m_relativePath;
|
||||
sourceData.m_parentMaterial = AtomToolsFramework::GetExteralReferencePath(normalizedSavePath, m_absolutePath);
|
||||
}
|
||||
|
||||
// Force save data to store forward slashes
|
||||
AzFramework::StringFunc::Replace(sourceData.m_materialType, "\\", "/");
|
||||
AzFramework::StringFunc::Replace(sourceData.m_parentMaterial, "\\", "/");
|
||||
|
||||
// populate sourceData with modified properties
|
||||
const bool savedProperties = SavePropertiesToSourceData(sourceData, [](const AtomToolsFramework::DynamicProperty& property) {
|
||||
const bool savedProperties = SavePropertiesToSourceData(normalizedSavePath, sourceData, [](const AtomToolsFramework::DynamicProperty& property)
|
||||
{
|
||||
return !AtomToolsFramework::ArePropertyValuesEqual(property.GetValue(), property.GetConfig().m_originalValue);
|
||||
});
|
||||
|
||||
@@ -590,7 +575,8 @@ namespace MaterialEditor
|
||||
}
|
||||
}
|
||||
|
||||
bool MaterialDocument::SavePropertiesToSourceData(AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const
|
||||
bool MaterialDocument::SavePropertiesToSourceData(
|
||||
const AZStd::string& exportPath, AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const
|
||||
{
|
||||
using namespace AZ;
|
||||
using namespace RPI;
|
||||
@@ -598,7 +584,7 @@ namespace MaterialEditor
|
||||
bool result = true;
|
||||
|
||||
// populate sourceData with properties that meet the filter
|
||||
m_materialTypeSourceData.EnumerateProperties([this, &sourceData, &propertyFilter, &result](const AZStd::string& groupName, const AZStd::string& propertyName, const auto& propertyDefinition) {
|
||||
m_materialTypeSourceData.EnumerateProperties([&](const AZStd::string& groupName, const AZStd::string& propertyName, const auto& propertyDefinition) {
|
||||
|
||||
const MaterialPropertyId propertyId(groupName, propertyName);
|
||||
|
||||
@@ -608,7 +594,7 @@ namespace MaterialEditor
|
||||
MaterialPropertyValue propertyValue = AtomToolsFramework::ConvertToRuntimeType(it->second.GetValue());
|
||||
if (propertyValue.IsValid())
|
||||
{
|
||||
if (!m_materialTypeSourceData.ConvertPropertyValueToSourceDataFormat(propertyDefinition, propertyValue))
|
||||
if (!AtomToolsFramework::ConvertToExportFormat(exportPath, propertyDefinition, propertyValue))
|
||||
{
|
||||
AZ_Error("MaterialDocument", false, "Material document property could not be converted: '%s' in '%s'.", propertyId.GetFullName().GetCStr(), m_absolutePath.c_str());
|
||||
result = false;
|
||||
@@ -663,8 +649,6 @@ namespace MaterialEditor
|
||||
return false;
|
||||
}
|
||||
|
||||
AZStd::string materialTypeSourceFilePath;
|
||||
|
||||
// The material document and inspector are constructed from source data
|
||||
if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), MaterialSourceData::Extension))
|
||||
{
|
||||
@@ -675,13 +659,24 @@ namespace MaterialEditor
|
||||
return false;
|
||||
}
|
||||
|
||||
// We must also always load the material type data for a complete, ordered set of the
|
||||
// groups and properties that will be needed for comparison and building the inspector
|
||||
materialTypeSourceFilePath = AssetUtils::ResolvePathReference(m_absolutePath, m_materialSourceData.m_materialType);
|
||||
auto materialTypeOutcome = MaterialUtils::LoadMaterialTypeSourceData(materialTypeSourceFilePath);
|
||||
// We always need the absolute path for the material type and parent material to load source data and resolving
|
||||
// relative paths when saving. This will convert and store them as absolute paths for use within the document.
|
||||
if (!m_materialSourceData.m_parentMaterial.empty())
|
||||
{
|
||||
m_materialSourceData.m_parentMaterial =
|
||||
AssetUtils::ResolvePathReference(m_absolutePath, m_materialSourceData.m_parentMaterial);
|
||||
}
|
||||
|
||||
if (!m_materialSourceData.m_materialType.empty())
|
||||
{
|
||||
m_materialSourceData.m_materialType = AssetUtils::ResolvePathReference(m_absolutePath, m_materialSourceData.m_materialType);
|
||||
}
|
||||
|
||||
// Load the material type source data which provides the layout and default values of all of the properties
|
||||
auto materialTypeOutcome = MaterialUtils::LoadMaterialTypeSourceData(m_materialSourceData.m_materialType);
|
||||
if (!materialTypeOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error("MaterialDocument", false, "Material type source data could not be loaded: '%s'.", materialTypeSourceFilePath.c_str());
|
||||
AZ_Error("MaterialDocument", false, "Material type source data could not be loaded: '%s'.", m_materialSourceData.m_materialType.c_str());
|
||||
return false;
|
||||
}
|
||||
m_materialTypeSourceData = materialTypeOutcome.GetValue();
|
||||
@@ -694,10 +689,10 @@ namespace MaterialEditor
|
||||
}
|
||||
else if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), MaterialTypeSourceData::Extension))
|
||||
{
|
||||
materialTypeSourceFilePath = m_absolutePath;
|
||||
|
||||
// Load the material type source data, which will be used for enumerating properties and building material source data
|
||||
auto materialTypeOutcome = MaterialUtils::LoadMaterialTypeSourceData(materialTypeSourceFilePath);
|
||||
// A material document can be created or loaded from material or material type source data. If we are attempting to load
|
||||
// material type source data then the material source data object can be created just by referencing the document path as the
|
||||
// material type path.
|
||||
auto materialTypeOutcome = MaterialUtils::LoadMaterialTypeSourceData(m_absolutePath);
|
||||
if (!materialTypeOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error("MaterialDocument", false, "Material type source data could not be loaded: '%s'.", m_absolutePath.c_str());
|
||||
@@ -705,9 +700,8 @@ namespace MaterialEditor
|
||||
}
|
||||
m_materialTypeSourceData = materialTypeOutcome.GetValue();
|
||||
|
||||
// The document represents a material, not a material type.
|
||||
// If the input data is a material type file we have to generate the material source data by referencing it.
|
||||
m_materialSourceData.m_materialType = m_relativePath;
|
||||
// We are storing absolute paths in the loaded version of the source data so that the files can be resolved at all times.
|
||||
m_materialSourceData.m_materialType = m_absolutePath;
|
||||
m_materialSourceData.m_parentMaterial.clear();
|
||||
}
|
||||
else
|
||||
@@ -870,7 +864,8 @@ namespace MaterialEditor
|
||||
m_properties[propertyConfig.m_id] = AtomToolsFramework::DynamicProperty(propertyConfig);
|
||||
}
|
||||
|
||||
const MaterialFunctorSourceData::EditorContext editorContext = MaterialFunctorSourceData::EditorContext(materialTypeSourceFilePath, m_materialAsset->GetMaterialPropertiesLayout());
|
||||
const MaterialFunctorSourceData::EditorContext editorContext =
|
||||
MaterialFunctorSourceData::EditorContext(m_materialSourceData.m_materialType, m_materialAsset->GetMaterialPropertiesLayout());
|
||||
for (Ptr<MaterialFunctorSourceDataHolder> functorData : m_materialTypeSourceData.m_materialFunctorSourceData)
|
||||
{
|
||||
MaterialFunctorSourceData::FunctorResult result2 = functorData->CreateFunctor(editorContext);
|
||||
|
||||
@@ -110,7 +110,8 @@ namespace MaterialEditor
|
||||
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool SavePropertiesToSourceData(AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const;
|
||||
bool SavePropertiesToSourceData(
|
||||
const AZStd::string& exportPath, AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const;
|
||||
|
||||
bool OpenInternal(AZStd::string_view loadPath);
|
||||
|
||||
|
||||
@@ -27,6 +27,27 @@ ly_add_target(
|
||||
3rdParty::libpng
|
||||
)
|
||||
|
||||
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
|
||||
ly_add_target(
|
||||
NAME Atom_Utils.TestUtils.Static STATIC
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
atom_utils_editor_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Source
|
||||
PUBLIC
|
||||
Include
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AtomCore
|
||||
AZ::AzCore
|
||||
AZ::AzFramework
|
||||
AZ::AzToolsFramework
|
||||
)
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
# Tests
|
||||
################################################################################
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Common/AssetSystemStub.h>
|
||||
#include <Atom/Utils/TestUtils/AssetSystemStub.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
namespace UnitTest
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user