+98
-115
@@ -8,58 +8,43 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
import logging
|
||||
import os
|
||||
import pytest
|
||||
import time
|
||||
import typing
|
||||
|
||||
from datetime import datetime
|
||||
import ly_test_tools.log.log_monitor
|
||||
|
||||
# fixture imports
|
||||
from AWS.Windows.resource_mappings.resource_mappings import resource_mappings
|
||||
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor
|
||||
from .aws_metrics_utils import aws_metrics_utils
|
||||
from .aws_metrics_custom_thread import AWSMetricsThread
|
||||
|
||||
AWS_METRICS_FEATURE_NAME = 'AWSMetrics'
|
||||
GAME_LOG_NAME = 'Game.log'
|
||||
CONTEXT_VARIABLE = ['-c', 'batch_processing=true']
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup(launcher: ly_test_tools.launchers.Launcher,
|
||||
cdk: pytest.fixture,
|
||||
asset_processor: asset_processor,
|
||||
resource_mappings: resource_mappings,
|
||||
context_variable: str = '') -> typing.Tuple[ly_test_tools.log.log_monitor.LogMonitor, str, str]:
|
||||
def setup(launcher: pytest.fixture,
|
||||
asset_processor: pytest.fixture) -> pytest.fixture:
|
||||
"""
|
||||
Set up the CDK application and start the log monitor.
|
||||
Set up the resource mapping configuration and start the log monitor.
|
||||
:param launcher: Client launcher for running the test level.
|
||||
:param cdk: CDK application for deploying the AWS resources.
|
||||
:param asset_processor: asset_processor fixture.
|
||||
:param resource_mappings: resource_mappings fixture.
|
||||
:param context_variable: context_variable for enable optional CDK feature.
|
||||
:return log monitor object, metrics file path and the metrics stack name.
|
||||
"""
|
||||
logger.info(f'Cdk stack names:\n{cdk.list()}')
|
||||
stacks = cdk.deploy(context_variable=context_variable)
|
||||
resource_mappings.populate_output_keys(stacks)
|
||||
|
||||
asset_processor.start()
|
||||
asset_processor.wait_for_idle()
|
||||
|
||||
metrics_file_path = os.path.join(launcher.workspace.paths.project(), 'user',
|
||||
AWS_METRICS_FEATURE_NAME, 'metrics.json')
|
||||
remove_file(metrics_file_path)
|
||||
|
||||
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), GAME_LOG_NAME)
|
||||
remove_file(file_to_monitor)
|
||||
|
||||
# Initialize the log monitor.
|
||||
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor)
|
||||
|
||||
return log_monitor, metrics_file_path, stacks[0]
|
||||
return log_monitor
|
||||
|
||||
|
||||
def monitor_metrics_submission(log_monitor: ly_test_tools.log.log_monitor.LogMonitor) -> None:
|
||||
def monitor_metrics_submission(log_monitor: pytest.fixture) -> None:
|
||||
"""
|
||||
Monitor the messages and notifications for submitting metrics.
|
||||
:param log_monitor: Log monitor to check the log messages.
|
||||
@@ -67,6 +52,7 @@ def monitor_metrics_submission(log_monitor: ly_test_tools.log.log_monitor.LogMon
|
||||
expected_lines = [
|
||||
'(Script) - Submitted metrics without buffer.',
|
||||
'(Script) - Submitted metrics with buffer.',
|
||||
'(Script) - Flushed the buffered metrics.',
|
||||
'(Script) - Metrics is sent successfully.'
|
||||
]
|
||||
|
||||
@@ -87,87 +73,132 @@ def monitor_metrics_submission(log_monitor: ly_test_tools.log.log_monitor.LogMon
|
||||
f'unexpected_lines values: {unexpected_lines}')
|
||||
|
||||
|
||||
def remove_file(file_path: str) -> None:
|
||||
def query_metrics_from_s3(aws_metrics_utils: pytest.fixture, stack_name: str) -> None:
|
||||
"""
|
||||
Remove a local file and its directory.
|
||||
:param file_path: Path to the local file.
|
||||
Verify that the metrics events are delivered to the S3 bucket and can be queried.
|
||||
aws_metrics_utils: aws_metrics_utils fixture.
|
||||
stack_name: name of the CloudFormation stack.
|
||||
"""
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
analytics_bucket_name = aws_metrics_utils.get_analytics_bucket_name(stack_name)
|
||||
aws_metrics_utils.verify_s3_delivery(analytics_bucket_name)
|
||||
logger.info('Metrics are sent to S3.')
|
||||
|
||||
file_dir = os.path.dirname(file_path)
|
||||
if os.path.exists(file_dir) and len(os.listdir(file_dir)) == 0:
|
||||
os.rmdir(file_dir)
|
||||
aws_metrics_utils.run_glue_crawler(f'{stack_name}-EventsCrawler')
|
||||
aws_metrics_utils.run_named_queries(f'{stack_name}-AthenaWorkGroup')
|
||||
logger.info('Query metrics from S3 successfully.')
|
||||
|
||||
# Empty the S3 bucket. S3 buckets can only be deleted successfully when it doesn't contain any object.
|
||||
aws_metrics_utils.empty_batch_analytics_bucket(analytics_bucket_name)
|
||||
|
||||
|
||||
def verify_operational_metrics(aws_metrics_utils: pytest.fixture, stack_name: str, start_time: datetime) -> None:
|
||||
"""
|
||||
Verify that operational health metrics are delivered to CloudWatch.
|
||||
aws_metrics_utils: aws_metrics_utils fixture.
|
||||
stack_name: name of the CloudFormation stack.
|
||||
start_time: Time when the game launcher starts.
|
||||
"""
|
||||
aws_metrics_utils.verify_cloud_watch_delivery(
|
||||
'AWS/Lambda',
|
||||
'Invocations',
|
||||
[{'Name': 'FunctionName',
|
||||
'Value': f'{stack_name}-AnalyticsProcessingLambdaName'}],
|
||||
start_time)
|
||||
logger.info('AnalyticsProcessingLambda metrics are sent to CloudWatch.')
|
||||
|
||||
aws_metrics_utils.verify_cloud_watch_delivery(
|
||||
'AWS/Lambda',
|
||||
'Invocations',
|
||||
[{'Name': 'FunctionName',
|
||||
'Value': f'{stack_name}-EventsProcessingLambda'}],
|
||||
start_time)
|
||||
logger.info('EventsProcessingLambda metrics are sent to CloudWatch.')
|
||||
|
||||
|
||||
def start_kinesis_analytics_application(aws_metrics_utils: pytest.fixture, stack_name: str) -> None:
|
||||
"""
|
||||
Start the Kinesis analytics application for real-time analytics.
|
||||
aws_metrics_utils: aws_metrics_utils fixture.
|
||||
stack_name: name of the CloudFormation stack.
|
||||
"""
|
||||
analytics_application_name = f'{stack_name}-AnalyticsApplication'
|
||||
aws_metrics_utils.start_kinesis_data_analytics_application(analytics_application_name)
|
||||
|
||||
@pytest.mark.SUITE_periodic
|
||||
@pytest.mark.usefixtures('automatic_process_killer')
|
||||
@pytest.mark.parametrize('project', ['AutomatedTesting'])
|
||||
@pytest.mark.parametrize('level', ['AWS/Metrics'])
|
||||
@pytest.mark.parametrize('feature_name', [AWS_METRICS_FEATURE_NAME])
|
||||
@pytest.mark.usefixtures('resource_mappings')
|
||||
@pytest.mark.parametrize('resource_mappings_filename', ['default_aws_resource_mappings.json'])
|
||||
@pytest.mark.usefixtures('aws_credentials')
|
||||
@pytest.mark.parametrize('profile_name', ['AWSAutomationTest'])
|
||||
@pytest.mark.parametrize('region_name', ['us-west-2'])
|
||||
@pytest.mark.parametrize('assume_role_arn', ['arn:aws:iam::645075835648:role/o3de-automation-tests'])
|
||||
@pytest.mark.usefixtures('cdk')
|
||||
@pytest.mark.parametrize('session_name', ['o3de-Automation-session'])
|
||||
@pytest.mark.parametrize('deployment_params', [CONTEXT_VARIABLE])
|
||||
class TestAWSMetricsWindows(object):
|
||||
def test_realtime_analytics_metrics_sent_to_cloudwatch(self,
|
||||
level: str,
|
||||
launcher: ly_test_tools.launchers.Launcher,
|
||||
asset_processor: pytest.fixture,
|
||||
workspace: pytest.fixture,
|
||||
aws_utils: pytest.fixture,
|
||||
aws_credentials: pytest.fixture,
|
||||
resource_mappings: pytest.fixture,
|
||||
cdk: pytest.fixture,
|
||||
aws_metrics_utils: aws_metrics_utils,
|
||||
):
|
||||
"""
|
||||
Tests that the submitted metrics are sent to CloudWatch for real-time analytics.
|
||||
"""
|
||||
log_monitor, metrics_file_path, stack_name = setup(launcher, cdk, asset_processor, resource_mappings)
|
||||
"""
|
||||
Test class to verify the real-time and batch analytics for metrics.
|
||||
"""
|
||||
|
||||
# Start the Kinesis Data Analytics application for real-time analytics.
|
||||
analytics_application_name = f'{stack_name}-AnalyticsApplication'
|
||||
aws_metrics_utils.start_kinesis_data_analytics_application(analytics_application_name)
|
||||
@pytest.mark.parametrize('destroy_stacks_on_teardown', [False])
|
||||
def test_realtime_and_batch_analytics(self,
|
||||
level: str,
|
||||
launcher: pytest.fixture,
|
||||
asset_processor: pytest.fixture,
|
||||
workspace: pytest.fixture,
|
||||
aws_utils: pytest.fixture,
|
||||
cdk: pytest.fixture,
|
||||
aws_metrics_utils: pytest.fixture):
|
||||
"""
|
||||
Verify that the metrics events are sent to CloudWatch and S3 for analytics.
|
||||
"""
|
||||
# Start Kinesis analytics application on a separate thread to avoid blocking the test.
|
||||
kinesis_analytics_application_thread = AWSMetricsThread(target=start_kinesis_analytics_application,
|
||||
args=(aws_metrics_utils, cdk.stacks[0]))
|
||||
kinesis_analytics_application_thread.start()
|
||||
log_monitor = setup(launcher, asset_processor)
|
||||
|
||||
# Kinesis analytics application needs to be in the running state before we start the game launcher.
|
||||
kinesis_analytics_application_thread.join()
|
||||
launcher.args = ['+LoadLevel', level]
|
||||
launcher.args.extend(['-rhi=null'])
|
||||
|
||||
start_time = datetime.utcnow()
|
||||
with launcher.start(launch_ap=False):
|
||||
start_time = datetime.utcnow()
|
||||
monitor_metrics_submission(log_monitor)
|
||||
# Verify that operational health metrics are delivered to CloudWatch.
|
||||
aws_metrics_utils.verify_cloud_watch_delivery(
|
||||
'AWS/Lambda',
|
||||
'Invocations',
|
||||
[{'Name': 'FunctionName',
|
||||
'Value': f'{stack_name}-AnalyticsProcessingLambdaName'}],
|
||||
start_time)
|
||||
logger.info('Operational health metrics sent to CloudWatch.')
|
||||
|
||||
# Verify that real-time analytics metrics are delivered to CloudWatch.
|
||||
aws_metrics_utils.verify_cloud_watch_delivery(
|
||||
AWS_METRICS_FEATURE_NAME,
|
||||
'TotalLogins',
|
||||
[],
|
||||
start_time)
|
||||
logger.info('Real-time metrics sent to CloudWatch.')
|
||||
logger.info('Real-time metrics are sent to CloudWatch.')
|
||||
|
||||
# Stop the Kinesis Data Analytics application.
|
||||
aws_metrics_utils.stop_kinesis_data_analytics_application(analytics_application_name)
|
||||
# Run time-consuming verifications on separate threads to avoid blocking the test.
|
||||
verification_threads = list()
|
||||
verification_threads.append(
|
||||
AWSMetricsThread(target=query_metrics_from_s3, args=(aws_metrics_utils, cdk.stacks[0])))
|
||||
verification_threads.append(
|
||||
AWSMetricsThread(target=verify_operational_metrics, args=(aws_metrics_utils, cdk.stacks[0], start_time)))
|
||||
for thread in verification_threads:
|
||||
thread.start()
|
||||
for thread in verification_threads:
|
||||
thread.join()
|
||||
|
||||
@pytest.mark.parametrize('destroy_stacks_on_teardown', [True])
|
||||
def test_unauthorized_user_request_rejected(self,
|
||||
level: str,
|
||||
launcher: ly_test_tools.launchers.Launcher,
|
||||
cdk: pytest.fixture,
|
||||
aws_credentials: pytest.fixture,
|
||||
launcher: pytest.fixture,
|
||||
asset_processor: pytest.fixture,
|
||||
resource_mappings: pytest.fixture,
|
||||
workspace: pytest.fixture):
|
||||
"""
|
||||
Tests that unauthorized users cannot send metrics events to the AWS backed backend.
|
||||
Verify that unauthorized users cannot send metrics events to the AWS backed backend.
|
||||
"""
|
||||
log_monitor, metrics_file_path, stack_name = setup(launcher, cdk, asset_processor, resource_mappings)
|
||||
log_monitor = setup(launcher, asset_processor)
|
||||
|
||||
# Set invalid AWS credentials.
|
||||
launcher.args = ['+LoadLevel', level, '+cl_awsAccessKey', 'AKIAIOSFODNN7EXAMPLE',
|
||||
'+cl_awsSecretKey', 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY']
|
||||
@@ -180,51 +211,3 @@ class TestAWSMetricsWindows(object):
|
||||
halt_on_unexpected=True)
|
||||
assert result, 'Metrics events are sent successfully by unauthorized user'
|
||||
logger.info('Unauthorized user is rejected to send metrics.')
|
||||
|
||||
def test_batch_analytics_metrics_delivered_to_s3(self,
|
||||
level: str,
|
||||
launcher: ly_test_tools.launchers.Launcher,
|
||||
cdk: pytest.fixture,
|
||||
aws_credentials: pytest.fixture,
|
||||
asset_processor: pytest.fixture,
|
||||
resource_mappings: pytest.fixture,
|
||||
aws_utils: pytest.fixture,
|
||||
aws_metrics_utils: aws_metrics_utils,
|
||||
workspace: pytest.fixture):
|
||||
"""
|
||||
Tests that the submitted metrics are sent to the data lake for batch analytics.
|
||||
"""
|
||||
log_monitor, metrics_file_path, stack_name = setup(launcher, cdk, asset_processor, resource_mappings,
|
||||
context_variable='batch_processing=true')
|
||||
|
||||
analytics_bucket_name = aws_metrics_utils.get_analytics_bucket_name(stack_name)
|
||||
|
||||
launcher.args = ['+LoadLevel', level]
|
||||
launcher.args.extend(['-rhi=null'])
|
||||
|
||||
with launcher.start(launch_ap=False):
|
||||
start_time = datetime.utcnow()
|
||||
monitor_metrics_submission(log_monitor)
|
||||
# Verify that operational health metrics are delivered to CloudWatch.
|
||||
aws_metrics_utils.verify_cloud_watch_delivery(
|
||||
'AWS/Lambda',
|
||||
'Invocations',
|
||||
[{'Name': 'FunctionName',
|
||||
'Value': f'{stack_name}-EventsProcessingLambda'}],
|
||||
start_time)
|
||||
logger.info('Operational health metrics sent to CloudWatch.')
|
||||
|
||||
aws_metrics_utils.verify_s3_delivery(analytics_bucket_name)
|
||||
logger.info('Metrics sent to S3.')
|
||||
|
||||
# Run the glue crawler to populate the AWS Glue Data Catalog with tables.
|
||||
aws_metrics_utils.run_glue_crawler(f'{stack_name}-EventsCrawler')
|
||||
# Run named queries on the table to verify the batch analytics.
|
||||
aws_metrics_utils.run_named_queries(f'{stack_name}-AthenaWorkGroup')
|
||||
logger.info('Query metrics from S3 successfully.')
|
||||
|
||||
# Kinesis Data Firehose buffers incoming data before it delivers it to Amazon S3. Sleep for the
|
||||
# default interval (60s) to make sure that all the metrics are sent to the bucket before cleanup.
|
||||
time.sleep(60)
|
||||
# Empty the S3 bucket. S3 buckets can only be deleted successfully when it doesn't contain any object.
|
||||
aws_metrics_utils.empty_s3_bucket(analytics_bucket_name)
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
from threading import Thread
|
||||
|
||||
|
||||
class AWSMetricsThread(Thread):
|
||||
"""
|
||||
Custom thread for raising assertion errors on the main thread.
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._error = None
|
||||
|
||||
def run(self) -> None:
|
||||
try:
|
||||
super().run()
|
||||
except AssertionError as e:
|
||||
self._error = e
|
||||
|
||||
def join(self, **kwargs) -> None:
|
||||
super().join(**kwargs)
|
||||
|
||||
if self._error:
|
||||
raise AssertionError(self._error)
|
||||
@@ -13,7 +13,6 @@ import typing
|
||||
from datetime import datetime
|
||||
from botocore.exceptions import WaiterError
|
||||
|
||||
from AWS.common.aws_utils import AwsUtils
|
||||
from .aws_metrics_waiters import KinesisAnalyticsApplicationUpdatedWaiter, \
|
||||
CloudWatchMetricsDeliveredWaiter, DataLakeMetricsDeliveredWaiter, GlueCrawlerReadyWaiter
|
||||
|
||||
@@ -29,7 +28,7 @@ class AWSMetricsUtils:
|
||||
Provide utils functions for the AWSMetrics gem to interact with the deployed resources.
|
||||
"""
|
||||
|
||||
def __init__(self, aws_utils: AwsUtils):
|
||||
def __init__(self, aws_utils: pytest.fixture):
|
||||
self._aws_util = aws_utils
|
||||
|
||||
def start_kinesis_data_analytics_application(self, application_name: str) -> None:
|
||||
@@ -199,14 +198,13 @@ class AWSMetricsUtils:
|
||||
|
||||
assert state == 'SUCCEEDED', f'Failed to run the named query {named_query.get("Name", {})}'
|
||||
|
||||
def empty_s3_bucket(self, bucket_name: str) -> None:
|
||||
def empty_batch_analytics_bucket(self, bucket_name: str) -> None:
|
||||
"""
|
||||
Empty the S3 bucket following:
|
||||
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/migrations3.html
|
||||
|
||||
:param bucket_name: Name of the S3 bucket.
|
||||
"""
|
||||
|
||||
s3 = self._aws_util.resource('s3')
|
||||
bucket = s3.Bucket(bucket_name)
|
||||
|
||||
|
||||
@@ -72,15 +72,14 @@ class Cdk:
|
||||
f'\nError:{error.stderr}')
|
||||
|
||||
def setup(self, cdk_path: str, project: str, account_id: str,
|
||||
workspace: pytest.fixture, session: boto3.session.Session, bootstrap_required: bool):
|
||||
workspace: pytest.fixture, session: boto3.session.Session):
|
||||
"""
|
||||
:param cdk_path: Path where cdk app.py is stored.
|
||||
:param project: Project name used for cdk project name env variable.
|
||||
:param account_id: AWS account id to use with cdk application.
|
||||
:param workspace: ly_test_tools workspace fixture.
|
||||
:param workspace: bootstrap_required deploys bootstrap stack.
|
||||
:param session: Current boto3 session, provides credentials and region.
|
||||
"""
|
||||
|
||||
self._cdk_env = os.environ.copy()
|
||||
unique_id = uuid.uuid4().hex[-4:]
|
||||
self._cdk_env['O3DE_AWS_PROJECT_NAME'] = project[:4] + unique_id if len(project) > 4 else project + unique_id
|
||||
@@ -104,8 +103,7 @@ class Cdk:
|
||||
|
||||
logger.info(f'Installing cdk python dependencies: {output}')
|
||||
|
||||
if bootstrap_required:
|
||||
self.bootstrap()
|
||||
self.bootstrap()
|
||||
|
||||
def bootstrap(self) -> None:
|
||||
"""
|
||||
@@ -124,15 +122,19 @@ class Cdk:
|
||||
logger.warning(f'Failed creating Bootstrap stack {BOOTSTRAP_STACK_NAME} not found. '
|
||||
f'\nError:{clientError["Error"]["Message"]}')
|
||||
|
||||
def list(self) -> List[str]:
|
||||
def list(self, deployment_params: List[str] = None) -> List[str]:
|
||||
"""
|
||||
lists cdk stack names
|
||||
:return List of cdk stack names
|
||||
lists cdk stack names.
|
||||
:param deployment_params: Deployment parameters like --all can be passed in this way.
|
||||
:return List of cdk stack names.
|
||||
"""
|
||||
if not self._cdk_path:
|
||||
return []
|
||||
|
||||
list_cdk_application_cmd = ['cdk', 'list']
|
||||
if deployment_params:
|
||||
list_cdk_application_cmd.extend(deployment_params)
|
||||
|
||||
output = process_utils.check_output(
|
||||
list_cdk_application_cmd,
|
||||
cwd=self._cdk_path,
|
||||
@@ -141,36 +143,36 @@ class Cdk:
|
||||
|
||||
return output.splitlines()
|
||||
|
||||
def synthesize(self) -> None:
|
||||
def synthesize(self, deployment_params: List[str] = None) -> None:
|
||||
"""
|
||||
Synthesizes all cdk stacks
|
||||
Synthesizes all cdk stacks.
|
||||
:param deployment_params: Deployment parameters like --all can be passed in this way.
|
||||
"""
|
||||
if not self._cdk_path:
|
||||
return
|
||||
|
||||
list_cdk_application_cmd = ['cdk', 'synth']
|
||||
synth_cdk_application_cmd = ['cdk', 'synth']
|
||||
if deployment_params:
|
||||
synth_cdk_application_cmd.extend(deployment_params)
|
||||
|
||||
process_utils.check_output(
|
||||
list_cdk_application_cmd,
|
||||
synth_cdk_application_cmd,
|
||||
cwd=self._cdk_path,
|
||||
env=self._cdk_env,
|
||||
shell=True)
|
||||
|
||||
def deploy(self, context_variable: str = '', additonal_params: List[str] = None) -> List[str]:
|
||||
def deploy(self, deployment_params: List[str] = None) -> List[str]:
|
||||
"""
|
||||
Deploys all the CDK stacks.
|
||||
:param context_variable: Context variable for enabling optional features.
|
||||
:param additonal_params: Additonal parameters like --all can be passed in this way.
|
||||
:param deployment_params: Deployment parameters like --all can be passed in this way.
|
||||
:return List of deployed stack arns.
|
||||
"""
|
||||
if not self._cdk_path:
|
||||
return []
|
||||
|
||||
deploy_cdk_application_cmd = ['cdk', 'deploy', '--require-approval', 'never']
|
||||
if additonal_params:
|
||||
deploy_cdk_application_cmd.extend(additonal_params)
|
||||
if context_variable:
|
||||
deploy_cdk_application_cmd.extend(['-c', f'{context_variable}'])
|
||||
if deployment_params:
|
||||
deploy_cdk_application_cmd.extend(deployment_params)
|
||||
|
||||
output = process_utils.check_output(
|
||||
deploy_cdk_application_cmd,
|
||||
@@ -178,21 +180,23 @@ class Cdk:
|
||||
env=self._cdk_env,
|
||||
shell=True)
|
||||
|
||||
stacks = []
|
||||
for line in output.splitlines():
|
||||
line_sections = line.split('/')
|
||||
assert len(line_sections), 3
|
||||
stacks.append(line.split('/')[-2])
|
||||
self._stacks.append(line.split('/')[-2])
|
||||
|
||||
return stacks
|
||||
return self._stacks
|
||||
|
||||
def destroy(self) -> None:
|
||||
def destroy(self, deployment_params: List[str] = None) -> None:
|
||||
"""
|
||||
Destroys the cdk application.
|
||||
:param deployment_params: Deployment parameters like --all can be passed in this way.
|
||||
"""
|
||||
|
||||
logger.info(f'CDK Path {self._cdk_path}')
|
||||
destroy_cdk_application_cmd = ['cdk', 'destroy', '--all', '-f']
|
||||
destroy_cdk_application_cmd = ['cdk', 'destroy', '-f']
|
||||
if deployment_params:
|
||||
destroy_cdk_application_cmd.extend(deployment_params)
|
||||
|
||||
try:
|
||||
process_utils.check_output(
|
||||
@@ -238,3 +242,7 @@ class Cdk:
|
||||
# self._session.client('cloudformation').delete_stack(
|
||||
# StackName=BOOTSTRAP_STACK_NAME
|
||||
# )
|
||||
|
||||
@property
|
||||
def stacks(self):
|
||||
return self._stacks
|
||||
|
||||
+41
-9
@@ -10,10 +10,7 @@ import logging
|
||||
import ly_test_tools.log.log_monitor
|
||||
|
||||
# fixture imports
|
||||
from AWS.Windows.resource_mappings.resource_mappings import resource_mappings
|
||||
from AWS.Windows.cdk.cdk_utils import Cdk
|
||||
from AWS.common.aws_utils import AwsUtils
|
||||
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
|
||||
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor
|
||||
|
||||
AWS_PROJECT_NAME = 'AWS-AutomationTest'
|
||||
AWS_CLIENT_AUTH_FEATURE_NAME = 'AWSClientAuth'
|
||||
@@ -37,11 +34,47 @@ logger = logging.getLogger(__name__)
|
||||
@pytest.mark.parametrize('region_name', ['us-west-2'])
|
||||
@pytest.mark.parametrize('assume_role_arn', ['arn:aws:iam::645075835648:role/o3de-automation-tests'])
|
||||
@pytest.mark.parametrize('session_name', ['o3de-Automation-session'])
|
||||
class TestAWSClientAuthPasswordSignIn(object):
|
||||
@pytest.mark.usefixtures('cdk')
|
||||
@pytest.mark.parametrize('deployment_params', [[]])
|
||||
class TestAWSClientAuthWindows(object):
|
||||
"""
|
||||
Test class to verify AWS Cognito IDP Password sign in and Cognito Identity pool authenticated authorization.
|
||||
Test class to verify AWS Client Auth gem features on Windows.
|
||||
"""
|
||||
|
||||
@pytest.mark.parametrize('level', ['AWS/ClientAuth'])
|
||||
@pytest.mark.parametrize('destroy_stacks_on_teardown', [False])
|
||||
def test_anonymous_credentials(self,
|
||||
level: str,
|
||||
launcher: pytest.fixture,
|
||||
resource_mappings: pytest.fixture,
|
||||
workspace: pytest.fixture,
|
||||
asset_processor: pytest.fixture
|
||||
):
|
||||
"""
|
||||
Test to verify AWS Cognito Identity pool anonymous authorization.
|
||||
|
||||
Setup: Deploys cdk and updates resource mapping file.
|
||||
Tests: Getting credentials when no credentials are configured
|
||||
Verification: Log monitor looks for success credentials log.
|
||||
"""
|
||||
asset_processor.start()
|
||||
asset_processor.wait_for_idle()
|
||||
|
||||
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), GAME_LOG_NAME)
|
||||
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor)
|
||||
|
||||
launcher.args = ['+LoadLevel', level]
|
||||
launcher.args.extend(['-rhi=null'])
|
||||
|
||||
with launcher.start(launch_ap=False):
|
||||
result = log_monitor.monitor_log_for_lines(
|
||||
expected_lines=['(Script) - Success anonymous credentials'],
|
||||
unexpected_lines=['(Script) - Fail anonymous credentials'],
|
||||
halt_on_unexpected=True,
|
||||
)
|
||||
assert result, 'Anonymous credentials fetched successfully.'
|
||||
|
||||
@pytest.mark.parametrize('destroy_stacks_on_teardown', [True])
|
||||
def test_password_signin_credentials(self,
|
||||
launcher: pytest.fixture,
|
||||
cdk: pytest.fixture,
|
||||
@@ -51,13 +84,12 @@ class TestAWSClientAuthPasswordSignIn(object):
|
||||
aws_utils: pytest.fixture
|
||||
):
|
||||
"""
|
||||
Test to verify AWS Cognito IDP Password sign in and Cognito Identity pool authenticated authorization.
|
||||
|
||||
Setup: Deploys cdk and updates resource mapping file.
|
||||
Tests: Sign up new test user, admin confirm the user, sign in and get aws credentials.
|
||||
Verification: Log monitor looks for success credentials log.
|
||||
"""
|
||||
logger.info(f'Cdk stack names:\n{cdk.list()}')
|
||||
stacks = cdk.deploy()
|
||||
resource_mappings.populate_output_keys(stacks)
|
||||
asset_processor.start()
|
||||
asset_processor.wait_for_idle()
|
||||
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
import pytest
|
||||
import os
|
||||
import logging
|
||||
import ly_test_tools.log.log_monitor
|
||||
|
||||
# fixture imports
|
||||
from AWS.Windows.resource_mappings.resource_mappings import resource_mappings
|
||||
from AWS.Windows.cdk.cdk_utils import Cdk
|
||||
from AWS.common.aws_utils import AwsUtils
|
||||
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
|
||||
|
||||
AWS_PROJECT_NAME = 'AWS-AutomationTest'
|
||||
AWS_CLIENT_AUTH_FEATURE_NAME = 'AWSClientAuth'
|
||||
AWS_CLIENT_AUTH_DEFAULT_PROFILE_NAME = 'default'
|
||||
|
||||
GAME_LOG_NAME = 'Game.log'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@pytest.mark.SUITE_periodic
|
||||
@pytest.mark.usefixtures('automatic_process_killer')
|
||||
@pytest.mark.usefixtures('asset_processor')
|
||||
@pytest.mark.usefixtures('workspace')
|
||||
@pytest.mark.parametrize('project', ['AutomatedTesting'])
|
||||
@pytest.mark.parametrize('level', ['AWS/ClientAuth'])
|
||||
@pytest.mark.usefixtures('cdk')
|
||||
@pytest.mark.parametrize('feature_name', [AWS_CLIENT_AUTH_FEATURE_NAME])
|
||||
@pytest.mark.usefixtures('resource_mappings')
|
||||
@pytest.mark.parametrize('resource_mappings_filename', ['default_aws_resource_mappings.json'])
|
||||
@pytest.mark.usefixtures('aws_utils')
|
||||
@pytest.mark.parametrize('region_name', ['us-west-2'])
|
||||
@pytest.mark.parametrize('assume_role_arn', ['arn:aws:iam::645075835648:role/o3de-automation-tests'])
|
||||
@pytest.mark.parametrize('session_name', ['o3de-Automation-session'])
|
||||
class TestAWSClientAuthAnonymousCredentials(object):
|
||||
"""
|
||||
Test class to verify AWS Cognito Identity pool anonymous authorization.
|
||||
"""
|
||||
|
||||
def test_anonymous_credentials(self,
|
||||
level: str,
|
||||
launcher: pytest.fixture,
|
||||
cdk: pytest.fixture,
|
||||
resource_mappings: pytest.fixture,
|
||||
workspace: pytest.fixture,
|
||||
asset_processor: pytest.fixture
|
||||
):
|
||||
"""
|
||||
Setup: Deploys cdk and updates resource mapping file.
|
||||
Tests: Getting AWS credentials for no signed in user.
|
||||
Verification: Log monitor looks for success credentials log.
|
||||
"""
|
||||
logger.info(f'Cdk stack names:\n{cdk.list()}')
|
||||
stacks = cdk.deploy()
|
||||
resource_mappings.populate_output_keys(stacks)
|
||||
asset_processor.start()
|
||||
asset_processor.wait_for_idle()
|
||||
|
||||
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), GAME_LOG_NAME)
|
||||
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor)
|
||||
|
||||
launcher.args = ['+LoadLevel', level]
|
||||
launcher.args.extend(['-rhi=null'])
|
||||
|
||||
with launcher.start(launch_ap=False):
|
||||
result = log_monitor.monitor_log_for_lines(
|
||||
expected_lines=['(Script) - Success anonymous credentials'],
|
||||
unexpected_lines=['(Script) - Fail anonymous credentials'],
|
||||
halt_on_unexpected=True,
|
||||
)
|
||||
assert result, 'Anonymous credentials fetched successfully.'
|
||||
+73
-115
@@ -7,7 +7,8 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
import os
|
||||
import logging
|
||||
import time
|
||||
import typing
|
||||
import shutil
|
||||
|
||||
import pytest
|
||||
import ly_test_tools
|
||||
@@ -16,7 +17,6 @@ import ly_test_tools.environment.process_utils as process_utils
|
||||
import ly_test_tools.o3de.asset_processor_utils as asset_processor_utils
|
||||
|
||||
from botocore.exceptions import ClientError
|
||||
from AWS.Windows.resource_mappings.resource_mappings import resource_mappings
|
||||
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor
|
||||
|
||||
AWS_CORE_FEATURE_NAME = 'AWSCore'
|
||||
@@ -29,18 +29,49 @@ GAME_LOG_NAME = 'Game.log'
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup(launcher: pytest.fixture, cdk: pytest.fixture, resource_mappings: pytest.fixture, asset_processor: pytest.fixture):
|
||||
def setup(launcher: pytest.fixture, asset_processor: pytest.fixture) -> typing.Tuple[pytest.fixture, str]:
|
||||
"""
|
||||
Set up the resource mapping configuration and start the log monitor.
|
||||
:param launcher: Client launcher for running the test level.
|
||||
:param asset_processor: asset_processor fixture.
|
||||
:return log monitor object, metrics file path and the metrics stack name.
|
||||
"""
|
||||
# Create the temporary directory for downloading test file from S3.
|
||||
user_dir = os.path.join(launcher.workspace.paths.project(), 'user')
|
||||
s3_download_dir = os.path.join(user_dir, 's3_download')
|
||||
if not os.path.exists(s3_download_dir):
|
||||
os.makedirs(s3_download_dir)
|
||||
|
||||
asset_processor_utils.kill_asset_processor()
|
||||
logger.info(f'Cdk stack names:\n{cdk.list()}')
|
||||
stacks = cdk.deploy(additonal_params=['--all'])
|
||||
resource_mappings.populate_output_keys(stacks)
|
||||
asset_processor.start()
|
||||
asset_processor.wait_for_idle()
|
||||
|
||||
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), GAME_LOG_NAME)
|
||||
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor)
|
||||
|
||||
return log_monitor
|
||||
return log_monitor, s3_download_dir
|
||||
|
||||
|
||||
def write_test_data_to_dynamodb_table(resource_mappings: pytest.fixture, aws_utils: pytest.fixture) -> None:
|
||||
"""
|
||||
Write test data to the DynamoDB table created by the CDK application.
|
||||
:param resource_mappings: resource_mappings fixture.
|
||||
:param aws_utils: aws_utils fixture.
|
||||
"""
|
||||
table_name = resource_mappings.get_resource_name_id("AWSCore.ExampleDynamoTableOutput")
|
||||
try:
|
||||
aws_utils.client('dynamodb').put_item(
|
||||
TableName=table_name,
|
||||
Item={
|
||||
'id': {
|
||||
'S': 'Item1'
|
||||
}
|
||||
}
|
||||
)
|
||||
logger.info(f'Loaded data into table {table_name}')
|
||||
except ClientError:
|
||||
logger.exception(f'Failed to load data into table {table_name}')
|
||||
raise
|
||||
|
||||
|
||||
@pytest.mark.SUITE_periodic
|
||||
@@ -58,130 +89,57 @@ def setup(launcher: pytest.fixture, cdk: pytest.fixture, resource_mappings: pyte
|
||||
@pytest.mark.parametrize('resource_mappings_filename', [AWS_RESOURCE_MAPPING_FILE_NAME])
|
||||
@pytest.mark.usefixtures('aws_credentials')
|
||||
@pytest.mark.parametrize('profile_name', ['AWSAutomationTest'])
|
||||
@pytest.mark.usefixtures('cdk')
|
||||
@pytest.mark.parametrize('deployment_params', [['--all']])
|
||||
@pytest.mark.parametrize('destroy_stacks_on_teardown', [True])
|
||||
class TestAWSCoreAWSResourceInteraction(object):
|
||||
"""
|
||||
Test class to verify AWSCore can downloading a file from S3.
|
||||
Test class to verify the scripting behavior for the AWSCore gem.
|
||||
"""
|
||||
def test_download_from_s3(self,
|
||||
level: str,
|
||||
launcher: pytest.fixture,
|
||||
cdk: pytest.fixture,
|
||||
workspace: pytest.fixture,
|
||||
asset_processor: pytest.fixture,
|
||||
resource_mappings: pytest.fixture
|
||||
):
|
||||
|
||||
@pytest.mark.parametrize('expected_lines', [
|
||||
['(Script) - [S3] Head object request is done',
|
||||
'(Script) - [S3] Head object success: Object example.txt is found.',
|
||||
'(Script) - [S3] Get object success: Object example.txt is downloaded.',
|
||||
'(Script) - [Lambda] Completed Invoke',
|
||||
'(Script) - [Lambda] Invoke success: {"statusCode": 200, "body": {}}',
|
||||
'(Script) - [DynamoDB] Results finished']])
|
||||
@pytest.mark.parametrize('unexpected_lines', [
|
||||
['(Script) - [S3] Head object error: No response body.',
|
||||
'(Script) - [S3] Get object error: Request validation failed, output file directory doesn\'t exist.',
|
||||
'(Script) - Request validation failed, output file miss full path.',
|
||||
'(Script) - ']])
|
||||
def test_scripting_behavior(self,
|
||||
level: str,
|
||||
launcher: pytest.fixture,
|
||||
workspace: pytest.fixture,
|
||||
asset_processor: pytest.fixture,
|
||||
resource_mappings: pytest.fixture,
|
||||
aws_utils: pytest.fixture,
|
||||
expected_lines: typing.List[str],
|
||||
unexpected_lines: typing.List[str]):
|
||||
"""
|
||||
Setup: Deploys cdk and updates resource mapping file.
|
||||
Tests: Getting AWS credentials for no signed in user.
|
||||
Verification: Log monitor looks for success download. The existence and contents of the file are also verified.
|
||||
Tests: Interact with AWS S3, DynamoDB and Lambda services.
|
||||
Verification: Script canvas nodes can communicate with AWS services successfully.
|
||||
"""
|
||||
|
||||
log_monitor = setup(launcher, cdk, resource_mappings, asset_processor)
|
||||
log_monitor, s3_download_dir = setup(launcher, asset_processor)
|
||||
write_test_data_to_dynamodb_table(resource_mappings, aws_utils)
|
||||
|
||||
launcher.args = ['+LoadLevel', level]
|
||||
launcher.args.extend(['-rhi=null'])
|
||||
|
||||
user_dir = os.path.join(workspace.paths.project(), 'user')
|
||||
download_dir = os.path.join(user_dir, 's3_download')
|
||||
if not os.path.exists(download_dir):
|
||||
os.makedirs(download_dir)
|
||||
|
||||
with launcher.start(launch_ap=False):
|
||||
result = log_monitor.monitor_log_for_lines(
|
||||
expected_lines=['(Script) - [S3] Head object request is done',
|
||||
'(Script) - [S3] Head object success: Object example.txt is found.',
|
||||
'(Script) - [S3] Get object success: Object example.txt is downloaded.'],
|
||||
unexpected_lines=['(Script) - [S3] Head object error: No response body.',
|
||||
'(Script) - [S3] Get object error: Request validation failed, output file directory doesn\'t exist.'],
|
||||
expected_lines=expected_lines,
|
||||
unexpected_lines=unexpected_lines,
|
||||
halt_on_unexpected=True
|
||||
)
|
||||
|
||||
assert result, "Expected lines weren't found."
|
||||
|
||||
download_path = os.path.join(download_dir, 'output.txt')
|
||||
|
||||
file_was_downloaded = os.path.exists(download_path)
|
||||
assert os.path.exists(os.path.join(s3_download_dir, 'output.txt')), \
|
||||
'The expected file wasn\'t successfully downloaded.'
|
||||
# clean up the file directories.
|
||||
if file_was_downloaded:
|
||||
os.remove(download_path)
|
||||
os.rmdir(download_dir)
|
||||
|
||||
assert file_was_downloaded, 'The expected file wasn\'t successfully downloaded'
|
||||
|
||||
def test_invoke_lambda(self,
|
||||
level: str,
|
||||
launcher: pytest.fixture,
|
||||
cdk: pytest.fixture,
|
||||
resource_mappings: pytest.fixture,
|
||||
workspace: pytest.fixture,
|
||||
asset_processor: pytest.fixture
|
||||
):
|
||||
"""
|
||||
Setup: Deploys the CDK.
|
||||
Tests: Runs the test level.
|
||||
Verification: Searches the logs for the expected output from the example lambda.
|
||||
"""
|
||||
|
||||
log_monitor = setup(launcher, cdk, resource_mappings, asset_processor)
|
||||
|
||||
launcher.args = ['+LoadLevel', level]
|
||||
launcher.args.extend(['-rhi=null'])
|
||||
|
||||
with launcher.start(launch_ap=False):
|
||||
result = log_monitor.monitor_log_for_lines(
|
||||
expected_lines=['(Script) - [Lambda] Completed Invoke',
|
||||
'(Script) - [Lambda] Invoke success: {"statusCode": 200, "body": {}}'],
|
||||
unexpected_lines=['(Script) - Request validation failed, output file miss full path.',
|
||||
'(Script) - '],
|
||||
halt_on_unexpected=True
|
||||
)
|
||||
|
||||
assert result
|
||||
|
||||
def test_get_dynamodb_value(self,
|
||||
level: str,
|
||||
launcher: pytest.fixture,
|
||||
cdk: pytest.fixture,
|
||||
resource_mappings: pytest.fixture,
|
||||
workspace: pytest.fixture,
|
||||
asset_processor: pytest.fixture,
|
||||
aws_utils: pytest.fixture,
|
||||
):
|
||||
"""
|
||||
Setup: Deploys the CDK application
|
||||
Test: Runs a launcher with a level that loads a scriptcanvas that pulls a DynamoDB table value.
|
||||
Verification: The value is output in the logs and verified by the test.
|
||||
"""
|
||||
|
||||
def write_test_table_data():
|
||||
client = aws_utils.client('dynamodb')
|
||||
table_name = resource_mappings.get_resource_name_id("AWSCore.ExampleDynamoTableOutput")
|
||||
try:
|
||||
client.put_item(
|
||||
TableName=table_name,
|
||||
Item={
|
||||
'id': {
|
||||
'S': 'Item1'
|
||||
}
|
||||
}
|
||||
)
|
||||
logger.info(f'Loaded data into table {table_name}')
|
||||
except ClientError:
|
||||
logger.exception(f'Failed to load data into table {table_name}')
|
||||
raise
|
||||
|
||||
log_monitor = setup(launcher, cdk, resource_mappings, asset_processor)
|
||||
write_test_table_data()
|
||||
|
||||
launcher.args = ['+LoadLevel', level]
|
||||
launcher.args.extend(['-rhi=null'])
|
||||
|
||||
with launcher.start(launch_ap=False):
|
||||
result = log_monitor.monitor_log_for_lines(
|
||||
expected_lines=['(Script) - [DynamoDB] Results finished'],
|
||||
unexpected_lines=['(Script) - Request validation failed, output file miss full path.',
|
||||
'(Script) - '],
|
||||
halt_on_unexpected=True
|
||||
)
|
||||
|
||||
assert result
|
||||
shutil.rmtree(s3_download_dir)
|
||||
|
||||
@@ -6,7 +6,6 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
import os
|
||||
from os.path import abspath
|
||||
import pytest
|
||||
import json
|
||||
import logging
|
||||
@@ -107,38 +106,3 @@ class ResourceMappings:
|
||||
|
||||
def get_resource_name_id(self, resource_key: str):
|
||||
return self._resource_mappings[AWS_RESOURCE_MAPPINGS_KEY][resource_key]['Name/ID']
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def resource_mappings(
|
||||
request: pytest.fixture,
|
||||
project: str,
|
||||
feature_name: str,
|
||||
resource_mappings_filename: str,
|
||||
workspace: pytest.fixture,
|
||||
aws_utils: pytest.fixture) -> ResourceMappings:
|
||||
"""
|
||||
Fixture for setting up resource mappings file.
|
||||
:param request: _pytest.fixtures.SubRequest class that handles getting
|
||||
a pytest fixture from a pytest function/fixture.
|
||||
:param project: Project to find resource mapping file.
|
||||
:param feature_name: AWS Gem name that is prepended to resource mapping keys.
|
||||
:param resource_mappings_filename: Name of resource mapping file.
|
||||
:param workspace: ly_test_tools workspace fixture.
|
||||
:param aws_utils: AWS utils fixture.
|
||||
:return: ResourceMappings class object.
|
||||
"""
|
||||
|
||||
path = f'{workspace.paths.engine_root()}/{project}/Config/{resource_mappings_filename}'
|
||||
logger.info(f'Resource mapping path : {path}')
|
||||
logger.info(f'Resource mapping resolved path : {abspath(path)}')
|
||||
resource_mappings_obj = ResourceMappings(abspath(path), aws_utils.assume_session().region_name, feature_name,
|
||||
aws_utils.assume_account_id(), workspace,
|
||||
aws_utils.client('cloudformation'))
|
||||
|
||||
def teardown():
|
||||
resource_mappings_obj.clear_output_keys()
|
||||
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
return resource_mappings_obj
|
||||
|
||||
@@ -4,11 +4,15 @@ For complete copyright and license terms please see the LICENSE at the root of t
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
import pytest
|
||||
import logging
|
||||
from os.path import abspath
|
||||
import pytest
|
||||
import typing
|
||||
|
||||
from AWS.common.aws_utils import AwsUtils
|
||||
from AWS.common.aws_credentials import AwsCredentials
|
||||
from AWS.Windows.cdk.cdk_utils import Cdk
|
||||
from AWS.Windows.resource_mappings.resource_mappings import ResourceMappings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -42,6 +46,41 @@ def aws_utils(
|
||||
pytest.cdk_obj = None
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def resource_mappings(
|
||||
request: pytest.fixture,
|
||||
project: str,
|
||||
feature_name: str,
|
||||
resource_mappings_filename: str,
|
||||
workspace: pytest.fixture,
|
||||
aws_utils: pytest.fixture) -> ResourceMappings:
|
||||
"""
|
||||
Fixture for setting up resource mappings file.
|
||||
:param request: _pytest.fixtures.SubRequest class that handles getting
|
||||
a pytest fixture from a pytest function/fixture.
|
||||
:param project: Project to find resource mapping file.
|
||||
:param feature_name: AWS Gem name that is prepended to resource mapping keys.
|
||||
:param resource_mappings_filename: Name of resource mapping file.
|
||||
:param workspace: ly_test_tools workspace fixture.
|
||||
:param aws_utils: AWS utils fixture.
|
||||
:return: ResourceMappings class object.
|
||||
"""
|
||||
|
||||
path = f'{workspace.paths.engine_root()}/{project}/Config/{resource_mappings_filename}'
|
||||
logger.info(f'Resource mapping path : {path}')
|
||||
logger.info(f'Resource mapping resolved path : {abspath(path)}')
|
||||
resource_mappings_obj = ResourceMappings(abspath(path), aws_utils.assume_session().region_name, feature_name,
|
||||
aws_utils.assume_account_id(), workspace,
|
||||
aws_utils.client('cloudformation'))
|
||||
|
||||
def teardown():
|
||||
resource_mappings_obj.clear_output_keys()
|
||||
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
return resource_mappings_obj
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def cdk(
|
||||
request: pytest.fixture,
|
||||
@@ -49,8 +88,9 @@ def cdk(
|
||||
feature_name: str,
|
||||
workspace: pytest.fixture,
|
||||
aws_utils: pytest.fixture,
|
||||
bootstrap_required: bool = True,
|
||||
destroy_stacks_on_teardown: bool = True) -> Cdk:
|
||||
resource_mappings: pytest.fixture,
|
||||
deployment_params: typing.List[str],
|
||||
destroy_stacks_on_teardown: bool) -> Cdk:
|
||||
"""
|
||||
Fixture for setting up a Cdk
|
||||
:param request: _pytest.fixtures.SubRequest class that handles getting
|
||||
@@ -59,8 +99,8 @@ def cdk(
|
||||
:param feature_name: Feature gem name to expect cdk folder in.
|
||||
:param workspace: ly_test_tools workspace fixture.
|
||||
:param aws_utils: aws_utils fixture.
|
||||
:param bootstrap_required: Whether the bootstrap stack needs to be created to
|
||||
provision resources the AWS CDK needs to perform the deployment.
|
||||
:param resource_mappings: resource_mappings fixture.
|
||||
:param deployment_params: Parameters for the CDK application deployment.
|
||||
:param destroy_stacks_on_teardown: option to control calling destroy ot the end of test.
|
||||
:return Cdk class object.
|
||||
"""
|
||||
@@ -70,22 +110,27 @@ def cdk(
|
||||
|
||||
if pytest.cdk_obj is None:
|
||||
pytest.cdk_obj = Cdk()
|
||||
pytest.cdk_obj.setup(cdk_path, project, aws_utils.assume_account_id(), workspace, aws_utils.assume_session())
|
||||
|
||||
stacks = pytest.cdk_obj.deploy(deployment_params=deployment_params)
|
||||
|
||||
logger.info(f'Cdk stack names:\n{stacks}')
|
||||
resource_mappings.populate_output_keys(stacks)
|
||||
|
||||
pytest.cdk_obj.setup(cdk_path, project, aws_utils.assume_account_id(), workspace, aws_utils.assume_session(),
|
||||
bootstrap_required)
|
||||
def teardown():
|
||||
if destroy_stacks_on_teardown:
|
||||
pytest.cdk_obj.destroy()
|
||||
pytest.cdk_obj.destroy(deployment_params=deployment_params)
|
||||
# Enable after https://github.com/aws/aws-cdk/issues/986 is fixed.
|
||||
# Until then clean the bootstrap bucket manually.
|
||||
# cdk_obj.remove_bootstrap_stack()
|
||||
# pytest.cdk_obj.remove_bootstrap_stack()
|
||||
|
||||
pytest.cdk_obj = None
|
||||
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
return pytest.cdk_obj
|
||||
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def aws_credentials(request: pytest.fixture, aws_utils: pytest.fixture, profile_name: str):
|
||||
"""
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f0f4d4e0155feaa76c80a14128000a0fd9570ab76e79f4847eaef9006324a4d2
|
||||
size 9084
|
||||
oid sha256:19f2c4454bb395cdc0a36d1e45e6a384bbd23037af1a2fb93e088ecfa0f10e5b
|
||||
size 9343
|
||||
|
||||
+1009
-1011
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
<Environment />
|
||||
@@ -1 +0,0 @@
|
||||
<TimeOfDay Time="12"/>
|
||||
@@ -1,6 +1,6 @@
|
||||
<download name="ClientAuth" type="Map">
|
||||
<index src="filelist.xml" dest="filelist.xml"/>
|
||||
<files>
|
||||
<file src="level.pak" dest="level.pak" size="ED0" md5="dbf5115226e4b0ea38ebdc3967ba3aa9"/>
|
||||
<file src="level.pak" dest="level.pak" size="E1A" md5="fecbc160ebc2186184504482e3e2eba3"/>
|
||||
</files>
|
||||
</download>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:da041115014f11696d5878d5c21247c17b8d694fa9674e30692259261a7223a2
|
||||
size 3792
|
||||
oid sha256:8a674e05824e5ceec13a0487b318923568710bc8269e5be84adad59c495a7ceb
|
||||
size 3610
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:43b1a23b62fe2ffa05545ac99524f40b6fff49d6e35925b9d6138c00d8082e86
|
||||
size 9073
|
||||
oid sha256:a1c0b621525b8e88c3775ea4c60c2197d1e1b060ace9bad9d6efcb0532817e44
|
||||
size 9356
|
||||
|
||||
+4224
-4238
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
<download name="ClientAuthPasswordSignIn" type="Map">
|
||||
<index src="filelist.xml" dest="filelist.xml"/>
|
||||
<files>
|
||||
<file src="level.pak" dest="level.pak" size="DDF" md5="ebe91ae5f1ea1ec735b6650b14f3f95b"/>
|
||||
<file src="level.pak" dest="level.pak" size="E53" md5="12728d49c7efe0c8e83e3651a1d13ca6"/>
|
||||
</files>
|
||||
</download>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a58292341785cb260dc0ccf346259e35e2817ee48fc401a21ab528f6afb97b52
|
||||
size 3551
|
||||
oid sha256:f318a1787069385de291660f79e350cea2ca2c3ef3b5e0576686066bd9c49395
|
||||
size 3667
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f3d5121b26608b02747e245071ccff29ac57358cb6349ec9495a7a003ac12467
|
||||
size 8942
|
||||
oid sha256:afc5d665128738e6bea09e78a16ee38acc923a8ecefff90d987858ce72c395fa
|
||||
size 9360
|
||||
|
||||
+1546
-1554
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
<download name="ClientAuthPasswordSignUp" type="Map">
|
||||
<index src="filelist.xml" dest="filelist.xml"/>
|
||||
<files>
|
||||
<file src="level.pak" dest="level.pak" size="DDA" md5="aa6df891d505d6d9175beee4b55626db"/>
|
||||
<file src="level.pak" dest="level.pak" size="E44" md5="acfaa325178533f135cd683d3782cc8e"/>
|
||||
</files>
|
||||
</download>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:605391d415b828b100bada11d108099520c0b6a020f17588887b610475805d90
|
||||
size 3546
|
||||
oid sha256:87882b64688a77815d93c6973929fa21b89dc6c13d4866c710124ce2cd0f411e
|
||||
size 3652
|
||||
|
||||
@@ -30,9 +30,6 @@ function metrics:OnSendMetricsFailure(requestId, errorMessage)
|
||||
end
|
||||
|
||||
function metrics:OnDeactivate()
|
||||
AWSMetricsRequestBus.Broadcast.FlushMetrics()
|
||||
Debug.Log("Stop generating new test events and flushed the buffered metrics.")
|
||||
|
||||
self.tickBusHandler:Disconnect()
|
||||
self.metricsNotificationHandler:Disconnect()
|
||||
end
|
||||
@@ -40,7 +37,7 @@ end
|
||||
function metrics:OnTick(deltaTime, timePoint)
|
||||
self.tickTime = self.tickTime + deltaTime
|
||||
|
||||
if self.tickTime > 2.0 then
|
||||
if self.tickTime > 5.0 then
|
||||
defaultAttribute = AWSMetrics_MetricsAttribute()
|
||||
defaultAttribute:SetName("event_name")
|
||||
defaultAttribute:SetStrValue("login")
|
||||
@@ -57,6 +54,9 @@ function metrics:OnTick(deltaTime, timePoint)
|
||||
if self.numSubmittedMetricsEvents % 2 == 0 then
|
||||
if AWSMetricsRequestBus.Broadcast.SubmitMetrics(attributeList.attributes, 0, "lua", false) then
|
||||
Debug.Log("Submitted metrics without buffer.")
|
||||
|
||||
AWSMetricsRequestBus.Broadcast.FlushMetrics()
|
||||
Debug.Log("Flushed the buffered metrics.")
|
||||
else
|
||||
Debug.Log("Failed to Submit metrics without buffer.")
|
||||
end
|
||||
@@ -67,7 +67,7 @@ function metrics:OnTick(deltaTime, timePoint)
|
||||
Debug.Log("Failed to Submit metrics with buffer.")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
self.numSubmittedMetricsEvents = self.numSubmittedMetricsEvents + 1
|
||||
self.tickTime = 0
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -8,7 +8,7 @@ To contribute, please review our [Code of Conduct](https://github.com/o3de/o3de/
|
||||
|
||||
## Making contributions with the Developer Certificate of Origin (DCO)
|
||||
|
||||
When contributing, your pull requests will require that you have agreed to our DCO found here: [Devloper Certificate of Origin](https://developercertificate.org/)
|
||||
When contributing, your pull requests will require that you have agreed to our DCO found here: [Developer Certificate of Origin](https://developercertificate.org/). All commits require the --signoff flag to show DCO compliance.
|
||||
|
||||
You can do this by using the -s option in git.
|
||||
Example: ```git commit -s -m 'my commit message'```
|
||||
@@ -89,15 +89,6 @@ namespace AZ
|
||||
result.Combine(componentLoadResult);
|
||||
}
|
||||
|
||||
{
|
||||
JSR::ResultCode dependencyReadyLoadResult =
|
||||
ContinueLoadingFromJsonObjectField(&entityInstance->m_isDependencyReady,
|
||||
azrtti_typeid<decltype(entityInstance->m_isDependencyReady)>(),
|
||||
inputValue, "IsDependencyReady", context);
|
||||
|
||||
result.Combine(dependencyReadyLoadResult);
|
||||
}
|
||||
|
||||
{
|
||||
JSR::ResultCode runtimeActiveLoadResult =
|
||||
ContinueLoadingFromJsonObjectField(&entityInstance->m_isRuntimeActiveByDefault,
|
||||
@@ -184,20 +175,6 @@ namespace AZ
|
||||
result.Combine(resultComponents);
|
||||
}
|
||||
|
||||
{
|
||||
AZ::ScopedContextPath subPathDependencyReady(context, "m_isDependencyReady");
|
||||
const bool* dependencyReady = &entityInstance->m_isDependencyReady;
|
||||
const bool* dependencyReadyDefault =
|
||||
defaultEntityInstance ? &defaultEntityInstance->m_isDependencyReady : nullptr;
|
||||
|
||||
JSR::ResultCode resultDependencyReady =
|
||||
ContinueStoringToJsonObjectField(outputValue, "IsDependencyReady",
|
||||
dependencyReady, dependencyReadyDefault,
|
||||
azrtti_typeid<decltype(entityInstance->m_isDependencyReady)>(), context);
|
||||
|
||||
result.Combine(resultDependencyReady);
|
||||
}
|
||||
|
||||
{
|
||||
AZ::ScopedContextPath subPathRuntimeActive(context, "m_isRuntimeActiveByDefault");
|
||||
const bool* runtimeActive = &entityInstance->m_isRuntimeActiveByDefault;
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/RTTI/TypeInfo.h>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/containers/stack.h>
|
||||
#include <AzCore/std/function/function_fwd.h>
|
||||
#include <AzCore/std/function/function_template.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/Math/Plane.h>
|
||||
#include <AzCore/Math/SimdMath.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
@@ -13,9 +13,32 @@
|
||||
#include <AzCore/Math/Obb.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/MathScriptHelpers.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace
|
||||
{
|
||||
class TransformSerializer
|
||||
: public SerializeContext::IDataSerializer
|
||||
{
|
||||
public:
|
||||
// number of floats in the serialized representation, 4 for rotation, 1 for scale and 3 for translation
|
||||
static constexpr int NumFloats = 8;
|
||||
|
||||
// number of floats in version 1, which used 4 for rotation, 3 for scale and 3 for translation
|
||||
static constexpr int NumFloatsVersion1 = 10;
|
||||
|
||||
// number of floats in version 0, which stored a 3x4 matrix
|
||||
static constexpr int NumFloatsVersion0 = 12;
|
||||
|
||||
size_t Save(const void* classPtr, IO::GenericStream& stream, bool isDataBigEndian) override;
|
||||
size_t DataToText(IO::GenericStream& in, IO::GenericStream& out, bool isDataBigEndian) override;
|
||||
size_t TextToData(const char* text, unsigned int textVersion, IO::GenericStream& stream, bool isDataBigEndian) override;
|
||||
bool Load(void* classPtr, IO::GenericStream& stream, unsigned int version, bool isDataBigEndian) override;
|
||||
bool CompareValueData(const void* lhs, const void* rhs) override;
|
||||
};
|
||||
}
|
||||
namespace Internal
|
||||
{
|
||||
void TransformDefaultConstructor(Transform* thisPtr)
|
||||
|
||||
@@ -13,30 +13,9 @@
|
||||
#include <AzCore/Math/Vector4.h>
|
||||
#include <AzCore/Math/Quaternion.h>
|
||||
#include <AzCore/Math/MathUtils.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class TransformSerializer
|
||||
: public SerializeContext::IDataSerializer
|
||||
{
|
||||
public:
|
||||
// number of floats in the serialized representation, 4 for rotation, 1 for scale and 3 for translation
|
||||
static constexpr int NumFloats = 8;
|
||||
|
||||
// number of floats in version 1, which used 4 for rotation, 3 for scale and 3 for translation
|
||||
static constexpr int NumFloatsVersion1 = 10;
|
||||
|
||||
// number of floats in version 0, which stored a 3x4 matrix
|
||||
static constexpr int NumFloatsVersion0 = 12;
|
||||
|
||||
size_t Save(const void* classPtr, IO::GenericStream& stream, bool isDataBigEndian) override;
|
||||
size_t DataToText(IO::GenericStream& in, IO::GenericStream& out, bool isDataBigEndian) override;
|
||||
size_t TextToData(const char* text, unsigned int textVersion, IO::GenericStream& stream, bool isDataBigEndian) override;
|
||||
bool Load(void* classPtr, IO::GenericStream& stream, unsigned int version, bool isDataBigEndian) override;
|
||||
bool CompareValueData(const void* lhs, const void* rhs) override;
|
||||
};
|
||||
|
||||
//! Limits for transform scale values.
|
||||
//! The scale should not be zero to avoid problems with inverting.
|
||||
//! @{
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
#include <AzCore/Math/Internal/MathTypes.h>
|
||||
#include <AzCore/RTTI/TypeInfo.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/stack.h>
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
|
||||
namespace UnitTest
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <AzFramework/Visibility/OctreeSystemComponent.h>
|
||||
#include <AzCore/Math/ShapeIntersection.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
|
||||
+6
-1
@@ -9,7 +9,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class ReflectContext;
|
||||
} // namespace Az
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
namespace AzFramework
|
||||
|
||||
+28
-1
@@ -6,6 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/PlatformIncl.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
@@ -13,6 +14,9 @@
|
||||
|
||||
#include <Psapi.h>
|
||||
|
||||
AZ_CVAR(bool, ap_tether_lifetime, false, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
"If enabled, a parent process that launches the AP will terminate the AP on exit");
|
||||
|
||||
namespace AzFramework::AssetSystem::Platform
|
||||
{
|
||||
void AllowAssetProcessorToForeground()
|
||||
@@ -100,6 +104,21 @@ namespace AzFramework::AssetSystem::Platform
|
||||
fullLaunchCommand += '"';
|
||||
}
|
||||
|
||||
// Create or retrieve the job handle associated with the asset processor
|
||||
HANDLE apJob = nullptr;
|
||||
|
||||
if (ap_tether_lifetime)
|
||||
{
|
||||
apJob = ::CreateJobObjectA(nullptr, "AssetProcessorJob");
|
||||
if (apJob && GetLastError() != ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
// We're creating the job for the first time. Configure it to close child processes when this process exits.
|
||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION info = {};
|
||||
info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||
::SetInformationJobObject(apJob, JobObjectExtendedLimitInformation, &info, sizeof(info));
|
||||
}
|
||||
}
|
||||
|
||||
STARTUPINFO si;
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
@@ -107,6 +126,14 @@ namespace AzFramework::AssetSystem::Platform
|
||||
si.wShowWindow = SW_MINIMIZE;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
return ::CreateProcessA(nullptr, fullLaunchCommand.data(), nullptr, nullptr, FALSE, 0, nullptr, AZ::IO::FixedMaxPathString{ executableDirectory }.c_str(), &si, &pi) != 0;
|
||||
bool createResult = ::CreateProcessA(nullptr, fullLaunchCommand.data(), nullptr, nullptr, FALSE, 0, nullptr, AZ::IO::FixedMaxPathString{ executableDirectory }.c_str(), &si, &pi) != 0;
|
||||
|
||||
if (ap_tether_lifetime && apJob && createResult)
|
||||
{
|
||||
// Save process and thread handle to terminate AP when the parent process exits
|
||||
::AssignProcessToJobObject(apJob, pi.hProcess);
|
||||
}
|
||||
|
||||
return createResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ class QString;
|
||||
|
||||
namespace AzToolsFramework::AssetUtils
|
||||
{
|
||||
static constexpr const char* AssetImporterSettingsKey{ "/O3DE/SceneAPI/AssetImporter" };
|
||||
static constexpr const char* AssetImporterSupportedFileTypeKey{ "SupportedFileTypeExtensions" };
|
||||
|
||||
//! Reads the "/Amazon/AssetProcessor/Settings/Platforms" entry from the settings registry
|
||||
//! to retrieve all enabled platforms
|
||||
void ReadEnabledPlatformsFromSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
-20
@@ -67,14 +67,6 @@ namespace AzToolsFramework
|
||||
result.Combine(isStaticLoadResult);
|
||||
}
|
||||
|
||||
{
|
||||
JSR::ResultCode netSyncEnabledLoadResult = ContinueLoadingFromJsonObjectField(
|
||||
&transformComponentInstance->m_netSyncEnabled, azrtti_typeid<decltype(transformComponentInstance->m_netSyncEnabled)>(),
|
||||
inputValue, "Sync Enabled", context);
|
||||
|
||||
result.Combine(netSyncEnabledLoadResult);
|
||||
}
|
||||
|
||||
{
|
||||
JSR::ResultCode interpolatePositionLoadResult = ContinueLoadingFromJsonObjectField(
|
||||
&transformComponentInstance->m_interpolatePosition, azrtti_typeid<decltype(transformComponentInstance->m_interpolatePosition)>(),
|
||||
@@ -172,18 +164,6 @@ namespace AzToolsFramework
|
||||
result.Combine(resultIsStatic);
|
||||
}
|
||||
|
||||
{
|
||||
AZ::ScopedContextPath subPathName(context, "m_netSyncEnabled");
|
||||
const bool* netSyncEnabled = &transformComponentInstance->m_netSyncEnabled;
|
||||
const bool* defaultNetSyncEnabled = defaultTransformComponentInstance ? &defaultTransformComponentInstance->m_netSyncEnabled : nullptr;
|
||||
|
||||
JSR::ResultCode resultNetSyncEnabled = ContinueStoringToJsonObjectField(
|
||||
outputValue, "Sync Enabled", netSyncEnabled, defaultNetSyncEnabled, azrtti_typeid<decltype(transformComponentInstance->m_netSyncEnabled)>(),
|
||||
context);
|
||||
|
||||
result.Combine(resultNetSyncEnabled);
|
||||
}
|
||||
|
||||
{
|
||||
AZ::ScopedContextPath subPathName(context, "m_interpolatePosition");
|
||||
const AZ::InterpolationMode* interpolatePosition = &transformComponentInstance->m_interpolatePosition;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <Prefab/PrefabTestComponent.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
|
||||
@@ -238,6 +238,14 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
OUTPUT_SUBDIRECTORY
|
||||
testdata/DummyProject
|
||||
)
|
||||
ly_add_target_files(
|
||||
TARGETS
|
||||
AssetProcessor.Tests
|
||||
FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testdata/config_metadata/AssetProcessorPlatformConfig.setreg
|
||||
OUTPUT_SUBDIRECTORY
|
||||
testdata/config_metadata
|
||||
)
|
||||
|
||||
# Have the AssetProcessorTest use the LY_CMAKE_TARGET define of AssetProcessorBatch for the purpose
|
||||
# of looking up the generated cmake build dependencies settings registry .setreg file
|
||||
|
||||
@@ -12,6 +12,7 @@ set(FILES
|
||||
testdata/config_broken_noscans/AssetProcessorPlatformConfig.setreg
|
||||
testdata/config_broken_recognizers/AssetProcessorPlatformConfig.setreg
|
||||
testdata/config_regular/AssetProcessorPlatformConfig.setreg
|
||||
testdata/config_metadata/AssetProcessorPlatformConfig.setreg
|
||||
testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg
|
||||
testdata/EmptyDummyProject/AssetProcessorGamePlatformConfig.setreg
|
||||
testdata/DummyProject/AssetProcessorGamePlatformConfig.setreg
|
||||
|
||||
@@ -263,9 +263,15 @@ namespace AssetProcessor
|
||||
return;
|
||||
}
|
||||
|
||||
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
|
||||
// Placeholder Key used by the local Settings Registry for storing all Gems SourcePaths
|
||||
// array entries.
|
||||
constexpr auto PlaceholderGemKey = FixedValueString(AZ::SettingsRegistryMergeUtils::OrganizationRootKey)
|
||||
+ "/Gems/__SettingsRegistryBuilderPlaceholder";
|
||||
|
||||
AZ::SettingsRegistryImpl registry;
|
||||
|
||||
// Seed the local settings registry using the AssetProcessor settings registry
|
||||
// Seed the local settings registry using the AssetProcessor Settings Registry
|
||||
if (auto settingsRegistry = AZ::Interface<AZ::SettingsRegistryInterface>::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
AZStd::array settingsToCopy{
|
||||
@@ -279,18 +285,55 @@ namespace AssetProcessor
|
||||
|
||||
for (const auto& settingsKey : settingsToCopy)
|
||||
{
|
||||
AZ::SettingsRegistryInterface::FixedValueString settingsValue;
|
||||
FixedValueString settingsValue;
|
||||
[[maybe_unused]] bool settingsCopied = settingsRegistry->Get(settingsValue, settingsKey)
|
||||
&& registry.Set(settingsKey, settingsValue);
|
||||
AZ_Warning("Settings Registry Builder", settingsCopied, "Unable to copy setting %s from AssetProcessor settings registry"
|
||||
" to local settings registry", settingsKey.c_str());
|
||||
}
|
||||
|
||||
// The purpose of this section is to copy the Gem's SourcePaths from the Global Settings Registry
|
||||
// the local SettingsRegistry. The reason this is needed is so that the call to
|
||||
// `MergeSettingsToRegistry_GemRegistries` below is able to locate each gem's "<gem-root>/Registry" folder
|
||||
// that will be merged into the bootstrap.game.<configuration>.<platform>.setreg file
|
||||
// This is used by the GameLauncher applications to read from a single merged .setreg file
|
||||
// containing the settings needed to run a game/simulation without have access to the source code base registry
|
||||
AZStd::vector<AzFramework::GemInfo> gemInfos;
|
||||
size_t pathIndex{};
|
||||
if (AzFramework::GetGemsInfo(gemInfos, *settingsRegistry))
|
||||
{
|
||||
AZStd::vector<AZ::IO::PathView> sourcePaths;
|
||||
for (const AzFramework::GemInfo& gemInfo : gemInfos)
|
||||
{
|
||||
for (const AZ::IO::Path& absoluteSourcePath : gemInfo.m_absoluteSourcePaths)
|
||||
{
|
||||
if (auto foundIt = AZStd::find(sourcePaths.begin(), sourcePaths.end(), absoluteSourcePath);
|
||||
foundIt == sourcePaths.end())
|
||||
{
|
||||
sourcePaths.emplace_back(absoluteSourcePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const AZ::IO::Path& sourcePath : sourcePaths)
|
||||
{
|
||||
// Use JSON Pointer to append elements to the SourcePaths array
|
||||
registry.Set(FixedValueString::format("%s/SourcePaths/%zu", PlaceholderGemKey.c_str(), pathIndex++),
|
||||
sourcePath.Native());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_EngineRegistry(registry, platform, specialization, &scratchBuffer);
|
||||
// This function iterates over each path for each the "/Amazon/Gems/<gem-name>/SourcePaths" key and attempts
|
||||
// to merge the "Registry" directory in each path.
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_GemRegistries(registry, platform, specialization, &scratchBuffer);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectRegistry(registry, platform, specialization, &scratchBuffer);
|
||||
|
||||
// The Placeholder Key is removed now that each gem's "<gem-root>/Registry" directory have been merged to
|
||||
// the local Settings Registry instance via `MergeSettingsToRegistry_GemRegistries`
|
||||
registry.Remove(PlaceholderGemKey);
|
||||
|
||||
// Merge the Project User and User home settings registry only in non-release builds
|
||||
constexpr bool executeRegDumpCommands = false;
|
||||
AZ::CommandLine* commandLine{};
|
||||
|
||||
@@ -76,6 +76,7 @@ public:
|
||||
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_ModifyMetadataFile);
|
||||
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_DeleteFile);
|
||||
friend class GTEST_TEST_CLASS_NAME_(DeleteTest, DeleteFolderSharedAcrossTwoScanFolders_CorrectFileAndFolderAreDeletedFromCache);
|
||||
friend class GTEST_TEST_CLASS_NAME_(MetadataFileTest, MetadataFile_SourceFileExtensionDifferentCase);
|
||||
|
||||
friend class AssetProcessorManagerTest;
|
||||
friend struct ModtimeScanningTest;
|
||||
@@ -5241,3 +5242,59 @@ void DuplicateProcessTest::SetUp()
|
||||
m_sharedConnection = m_assetProcessorManager->m_stateData.get();
|
||||
ASSERT_TRUE(m_sharedConnection);
|
||||
}
|
||||
|
||||
void MetadataFileTest::SetUp()
|
||||
{
|
||||
AssetProcessorManagerTest::SetUp();
|
||||
m_config->AddMetaDataType("foo", "txt");
|
||||
}
|
||||
|
||||
TEST_F(MetadataFileTest, MetadataFile_SourceFileExtensionDifferentCase)
|
||||
{
|
||||
|
||||
using namespace AzToolsFramework::AssetSystem;
|
||||
using namespace AssetProcessor;
|
||||
|
||||
QDir tempPath(m_tempDir.path());
|
||||
|
||||
QString relFileName("Dummy.TXT");
|
||||
QString absPath(tempPath.absoluteFilePath("subfolder1/Dummy.TXT"));
|
||||
QString watchFolder = tempPath.absoluteFilePath("subfolder1");
|
||||
UnitTestUtils::CreateDummyFile(absPath, "dummy");
|
||||
|
||||
JobEntry entry;
|
||||
entry.m_watchFolderPath = watchFolder;
|
||||
entry.m_databaseSourceName = entry.m_pathRelativeToWatchFolder = relFileName;
|
||||
entry.m_jobKey = "txt";
|
||||
entry.m_platformInfo = { "pc", {"host", "renderer", "desktop"} };
|
||||
entry.m_jobRunKey = 1;
|
||||
|
||||
QString productPath(m_normalizedCacheRootDir.absoluteFilePath("outputfile.TXT"));
|
||||
UnitTestUtils::CreateDummyFile(productPath);
|
||||
|
||||
AssetBuilderSDK::ProcessJobResponse jobResponse;
|
||||
jobResponse.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
jobResponse.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(productPath.toUtf8().data()));
|
||||
|
||||
QMetaObject::invokeMethod(m_assetProcessorManager.get(), "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, entry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, jobResponse));
|
||||
|
||||
ASSERT_TRUE(BlockUntilIdle(5000));
|
||||
|
||||
// Creating a metadata file for the source assets
|
||||
// APM should process the source asset if a metadafile is detected
|
||||
// We are intentionally having a source file with a different file extension casing than the one specified in the metadata rule.
|
||||
QString metadataFile(tempPath.absoluteFilePath("subfolder1/Dummy.foo"));
|
||||
UnitTestUtils::CreateDummyFile(metadataFile, "dummy");
|
||||
|
||||
// Capture the job details as the APM inspects the file.
|
||||
JobDetails jobDetails;
|
||||
auto connection = QObject::connect(m_assetProcessorManager.get(), &AssetProcessorManager::AssetToProcess, [&jobDetails](JobDetails job)
|
||||
{
|
||||
jobDetails = job;
|
||||
});
|
||||
|
||||
m_assetProcessorManager->AssessAddedFile(tempPath.absoluteFilePath(metadataFile));
|
||||
|
||||
ASSERT_TRUE(BlockUntilIdle(5000));
|
||||
ASSERT_EQ(jobDetails.m_jobEntry.m_pathRelativeToWatchFolder, relFileName);
|
||||
}
|
||||
|
||||
@@ -179,6 +179,13 @@ struct ModtimeScanningTest
|
||||
AZStd::unique_ptr<StaticData> m_data;
|
||||
};
|
||||
|
||||
|
||||
struct MetadataFileTest
|
||||
: public AssetProcessorManagerTest
|
||||
{
|
||||
void SetUp() override;
|
||||
};
|
||||
|
||||
struct FingerprintTest
|
||||
: public AssetProcessorManagerTest
|
||||
{
|
||||
|
||||
+22
@@ -21,6 +21,7 @@ class UnitTestPlatformConfiguration : public AssetProcessor::PlatformConfigurati
|
||||
{
|
||||
friend class GTEST_TEST_CLASS_NAME_(PlatformConfigurationUnitTests, Test_GemHandling);
|
||||
friend class GTEST_TEST_CLASS_NAME_(PlatformConfigurationUnitTests, Test_MetaFileTypes);
|
||||
friend class GTEST_TEST_CLASS_NAME_(PlatformConfigurationUnitTests, Test_MetaFileTypes_AssetImporterExtensions);
|
||||
protected:
|
||||
};
|
||||
|
||||
@@ -665,3 +666,24 @@ TEST_F(PlatformConfigurationUnitTests, PlatformConfigFile_IsPresent_Found)
|
||||
ASSERT_TRUE(config.AddPlatformConfigFilePaths(platformConfigList));
|
||||
ASSERT_EQ(platformConfigList.size(), 1);
|
||||
}
|
||||
|
||||
TEST_F(PlatformConfigurationUnitTests, Test_MetaFileTypes_AssetImporterExtensions)
|
||||
{
|
||||
using namespace AssetProcessor;
|
||||
|
||||
const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot);
|
||||
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_metadata");
|
||||
ASSERT_TRUE(configRoot);
|
||||
UnitTestPlatformConfiguration config;
|
||||
m_absorber.Clear();
|
||||
ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
|
||||
ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0);
|
||||
ASSERT_TRUE(config.MetaDataFileTypesCount() == 2);
|
||||
|
||||
QStringList entriesToTest{ "aaa", "bbb" };
|
||||
for (int idx = 0; idx < entriesToTest.size(); idx++)
|
||||
{
|
||||
ASSERT_EQ(config.GetMetaDataFileTypeAt(idx).first, QString("%1.assetinfo").arg(entriesToTest[idx]));
|
||||
ASSERT_EQ(config.GetMetaDataFileTypeAt(idx).second, QString("%1").arg(entriesToTest[idx]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/Gem/GemInfo.h>
|
||||
#include <AzToolsFramework/Asset/AssetUtils.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -23,6 +24,21 @@ namespace
|
||||
|
||||
namespace AssetProcessor
|
||||
{
|
||||
|
||||
void AssetImporterPathsVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type,
|
||||
AZStd::string_view value)
|
||||
{
|
||||
auto found = value.find('.');
|
||||
if (found != AZStd::string::npos)
|
||||
{
|
||||
m_supportedFileExtensions.emplace_back(value.substr(found + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_supportedFileExtensions.emplace_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
struct PlatformsInfoVisitor
|
||||
: AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
@@ -1126,6 +1142,17 @@ namespace AssetProcessor
|
||||
|
||||
MetaDataTypesVisitor visitor;
|
||||
settingsRegistry->Visit(visitor, AZ::SettingsRegistryInterface::FixedValueString(AssetProcessorSettingsKey) + "/MetaDataTypes");
|
||||
|
||||
using namespace AzToolsFramework::AssetUtils;
|
||||
AZStd::vector<AZStd::string> supportedFileExtensions;
|
||||
AssetImporterPathsVisitor assetImporterVisitor{ settingsRegistry, supportedFileExtensions };
|
||||
settingsRegistry->Visit(assetImporterVisitor, AZ::SettingsRegistryInterface::FixedValueString(AssetImporterSettingsKey) + "/" + AssetImporterSupportedFileTypeKey);
|
||||
|
||||
for (auto& entry : assetImporterVisitor.m_supportedFileExtensions)
|
||||
{
|
||||
visitor.m_metaDataTypes.push_back({ AZStd::string::format("%s.assetinfo", entry.c_str()), entry });
|
||||
}
|
||||
|
||||
for (const auto& metaDataType : visitor.m_metaDataTypes)
|
||||
{
|
||||
QString fileType = AssetUtilities::NormalizeFilePath(QString::fromUtf8(metaDataType.m_fileType.c_str(),
|
||||
|
||||
@@ -40,6 +40,21 @@ namespace AssetProcessor
|
||||
extern const char AssetConfigPlatformDir[];
|
||||
extern const char AssetProcessorPlatformConfigFileName[];
|
||||
|
||||
struct AssetImporterPathsVisitor
|
||||
: AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
AssetImporterPathsVisitor(AZ::SettingsRegistryInterface* settingsRegistry, AZStd::vector<AZStd::string>& supportedExtension)
|
||||
: m_settingsRegistry(settingsRegistry)
|
||||
, m_supportedFileExtensions(supportedExtension)
|
||||
{
|
||||
}
|
||||
|
||||
void Visit(AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
|
||||
|
||||
AZ::SettingsRegistryInterface* m_settingsRegistry;
|
||||
AZStd::vector<AZStd::string> m_supportedFileExtensions;
|
||||
};
|
||||
|
||||
//! Information for a given recognizer, on a specific platform
|
||||
//! essentially a plain data holder, but with helper funcs
|
||||
class AssetPlatformSpec
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"O3DE":
|
||||
{
|
||||
"SceneAPI":
|
||||
{
|
||||
"AssetImporter":
|
||||
{
|
||||
"SupportedFileTypeExtensions":
|
||||
[
|
||||
".aaa",
|
||||
".bbb"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <AzToolsFramework/Asset/AssetUtils.h>
|
||||
#include <SceneAPI/SceneBuilder/SceneImportRequestHandler.h>
|
||||
#include <SceneAPI/SceneCore/Containers/Scene.h>
|
||||
#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
|
||||
@@ -23,19 +24,21 @@ namespace AZ
|
||||
{
|
||||
void SceneImporterSettings::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
using namespace AzToolsFramework::AssetUtils;
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext)
|
||||
{
|
||||
serializeContext->Class<SceneImporterSettings>()
|
||||
->Version(2)
|
||||
->Field("SupportedFileTypeExtensions", &SceneImporterSettings::m_supportedFileTypeExtensions);
|
||||
->Field(AssetImporterSupportedFileTypeKey, &SceneImporterSettings::m_supportedFileTypeExtensions);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneImportRequestHandler::Activate()
|
||||
{
|
||||
using namespace AzToolsFramework::AssetUtils;
|
||||
if (auto* settingsRegistry = AZ::SettingsRegistry::Get())
|
||||
{
|
||||
settingsRegistry->GetObject(m_settings, "/O3DE/SceneAPI/AssetImporter");
|
||||
settingsRegistry->GetObject(m_settings, AssetImporterSettingsKey);
|
||||
}
|
||||
|
||||
BusConnect();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Math/Quaternion.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <SceneAPI/SceneCore/SceneCoreConfiguration.h>
|
||||
|
||||
namespace AZ::SceneAPI
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include "DebugOutput.h"
|
||||
#include <AzCore/std/optional.h>
|
||||
|
||||
namespace AZ::SceneAPI::Utilities
|
||||
{
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <SceneAPI/SceneCore/SceneCoreConfiguration.h>
|
||||
#include <SceneAPI/SceneCore/DataTypes/MatrixType.h>
|
||||
#include <SceneAPI/SceneCore/Utilities/HashHelper.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <cinttypes>
|
||||
|
||||
namespace AZ::SceneAPI::Utilities
|
||||
|
||||
@@ -305,6 +305,11 @@ namespace AZ::SceneAPI::Behaviors
|
||||
{
|
||||
using namespace AzToolsFramework;
|
||||
|
||||
// This behavior persists on the same AssetBuilder. Clear the script file name so that if
|
||||
// this builder processes a scene file with a script file name, and then later processes
|
||||
// a scene without a script file name, it won't run the old script on the new scene.
|
||||
m_scriptFilename.clear();
|
||||
|
||||
if (action != ManifestAction::Update)
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
{
|
||||
"Type": "JsonSerialization",
|
||||
"Version": 1,
|
||||
"ClassName": "PassAsset",
|
||||
"ClassData": {
|
||||
"PassTemplate": {
|
||||
"Name": "DiffuseGlobalFullscreenPass_nomsaaTemplate",
|
||||
"PassClass": "FullScreenTriangle",
|
||||
"Slots": [
|
||||
{
|
||||
"Name": "AlbedoInput",
|
||||
"SlotType": "Input",
|
||||
"ScopeAttachmentUsage": "Shader"
|
||||
},
|
||||
{
|
||||
"Name": "NormalInput",
|
||||
"SlotType": "Input",
|
||||
"ScopeAttachmentUsage": "Shader"
|
||||
},
|
||||
{
|
||||
"Name": "DepthStencilTextureInput",
|
||||
"SlotType": "Input",
|
||||
"ScopeAttachmentUsage": "Shader",
|
||||
"ImageViewDesc": {
|
||||
"AspectFlags": [
|
||||
"Depth"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "DiffuseInputOutput",
|
||||
"SlotType": "InputOutput",
|
||||
"ScopeAttachmentUsage": "RenderTarget"
|
||||
},
|
||||
{
|
||||
"Name": "DepthStencilInputOutput",
|
||||
"SlotType": "InputOutput",
|
||||
"ScopeAttachmentUsage": "DepthStencil"
|
||||
}
|
||||
],
|
||||
"PassData": {
|
||||
"$type": "FullscreenTrianglePassData",
|
||||
"ShaderAsset": {
|
||||
"FilePath": "Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen_nomsaa.shader"
|
||||
},
|
||||
"StencilRef": 128, // See RenderCommon.h and DiffuseGlobalFullscreen.shader
|
||||
"PipelineViewTag": "MainCamera"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
{
|
||||
"Type": "JsonSerialization",
|
||||
"Version": 1,
|
||||
"ClassName": "PassAsset",
|
||||
"ClassData": {
|
||||
"PassTemplate": {
|
||||
"Name": "LightCullingTilePrepareTemplate",
|
||||
"PassClass": "LightCullingTilePreparePass",
|
||||
"Slots": [
|
||||
{
|
||||
"Name": "TileLightData",
|
||||
"SlotType": "Output",
|
||||
"ShaderInputName": "m_tileLightData",
|
||||
"ScopeAttachmentUsage": "Shader"
|
||||
},
|
||||
{
|
||||
"Name": "Depth",
|
||||
"SlotType": "Input",
|
||||
"ShaderInputName": "m_depthBuffer",
|
||||
"ScopeAttachmentUsage": "Shader",
|
||||
"ImageViewDesc": {
|
||||
"AspectFlags": [
|
||||
"Depth"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"ImageAttachments": [
|
||||
{
|
||||
"Name": "TileLightData",
|
||||
"SizeSource": {
|
||||
"Source": {
|
||||
"Pass": "This",
|
||||
"Attachment": "Depth"
|
||||
},
|
||||
"Multipliers": {
|
||||
"WidthMultiplier": 0.0625,
|
||||
"HeightMultiplier": 0.0625
|
||||
}
|
||||
},
|
||||
"ImageDescriptor": {
|
||||
"Format": "R32G32B32A32_UINT"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Connections": [
|
||||
{
|
||||
"LocalSlot": "TileLightData",
|
||||
"AttachmentRef": {
|
||||
"Pass": "This",
|
||||
"Attachment": "TileLightData"
|
||||
}
|
||||
}
|
||||
],
|
||||
"PassData": {
|
||||
"$type": "ComputePassData",
|
||||
"ShaderAsset": {
|
||||
"FilePath": "Shaders/LightCulling/LightCullingTilePrepare.shader"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,10 +212,6 @@
|
||||
"Name": "LightCullingTemplate",
|
||||
"Path": "Passes/LightCulling.pass"
|
||||
},
|
||||
{
|
||||
"Name": "LightCullingTilePrepareTemplate",
|
||||
"Path": "Passes/LightCullingTilePrepare.pass"
|
||||
},
|
||||
{
|
||||
"Name": "LightCullingTilePrepareMSAATemplate",
|
||||
"Path": "Passes/LightCullingTilePrepareMSAA.pass"
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
{
|
||||
"Type": "JsonSerialization",
|
||||
"Version": 1,
|
||||
"ClassName": "PassAsset",
|
||||
"ClassData": {
|
||||
"PassTemplate": {
|
||||
"Name": "ReflectionGlobalFullscreenPass_nomsaaTemplate",
|
||||
"PassClass": "FullScreenTriangle",
|
||||
"Slots": [
|
||||
{
|
||||
"Name": "DepthStencilTextureInput",
|
||||
"SlotType": "Input",
|
||||
"ScopeAttachmentUsage": "Shader",
|
||||
"ImageViewDesc": {
|
||||
"AspectFlags": [
|
||||
"Depth"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "NormalInput",
|
||||
"SlotType": "Input",
|
||||
"ScopeAttachmentUsage": "Shader"
|
||||
},
|
||||
{
|
||||
"Name": "SpecularF0Input",
|
||||
"SlotType": "Input",
|
||||
"ScopeAttachmentUsage": "Shader"
|
||||
},
|
||||
{
|
||||
"Name": "ReflectionBlendWeightInput",
|
||||
"SlotType": "Input",
|
||||
"ScopeAttachmentUsage": "Shader"
|
||||
},
|
||||
{
|
||||
"Name": "BRDFTextureInput",
|
||||
"ShaderInputName": "m_brdfMap",
|
||||
"SlotType": "Input",
|
||||
"ScopeAttachmentUsage": "Shader"
|
||||
},
|
||||
{
|
||||
"Name": "DepthStencilInput",
|
||||
"SlotType": "Input",
|
||||
"ScopeAttachmentUsage": "DepthStencil",
|
||||
"ImageViewDesc": {
|
||||
"AspectFlags": [
|
||||
"Stencil"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "ReflectionOutput",
|
||||
"SlotType": "Output",
|
||||
"ScopeAttachmentUsage": "RenderTarget",
|
||||
"LoadStoreAction": {
|
||||
"ClearValue": {
|
||||
"Value": [
|
||||
0.4000000059604645,
|
||||
0.4000000059604645,
|
||||
0.4000000059604645,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
"LoadAction": "Clear"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ImageAttachments": [
|
||||
{
|
||||
"Name": "ReflectionImage",
|
||||
"SizeSource": {
|
||||
"Source": {
|
||||
"Pass": "This",
|
||||
"Attachment": "SpecularF0Input"
|
||||
}
|
||||
},
|
||||
"MultisampleSource": {
|
||||
"Pass": "This",
|
||||
"Attachment": "SpecularF0Input"
|
||||
},
|
||||
"ImageDescriptor": {
|
||||
"Format": "R16G16B16A16_FLOAT",
|
||||
"SharedQueueMask": "Graphics"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "BRDFTexture",
|
||||
"Lifetime": "Imported",
|
||||
"AssetRef": {
|
||||
"FilePath": "Textures/BRDFTexture.attimage"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Connections": [
|
||||
{
|
||||
"LocalSlot": "ReflectionOutput",
|
||||
"AttachmentRef": {
|
||||
"Pass": "This",
|
||||
"Attachment": "ReflectionImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"LocalSlot": "BRDFTextureInput",
|
||||
"AttachmentRef": {
|
||||
"Pass": "This",
|
||||
"Attachment": "BRDFTexture"
|
||||
}
|
||||
}
|
||||
],
|
||||
"PassData": {
|
||||
"$type": "FullscreenTrianglePassData",
|
||||
"ShaderAsset": {
|
||||
"FilePath": "Shaders/Reflections/ReflectionGlobalFullscreen_nomsaa.shader"
|
||||
},
|
||||
"PipelineViewTag": "MainCamera"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
{
|
||||
"Source" : "DiffuseComposite_nomsaa",
|
||||
|
||||
"RasterState" :
|
||||
{
|
||||
"CullMode" : "Back"
|
||||
},
|
||||
|
||||
"DepthStencilState" :
|
||||
{
|
||||
"Depth" :
|
||||
{
|
||||
"Enable" : false
|
||||
},
|
||||
"Stencil" :
|
||||
{
|
||||
"Enable" : true,
|
||||
"ReadMask" : "0x80",
|
||||
"WriteMask" : "0x00",
|
||||
"FrontFace" :
|
||||
{
|
||||
"Func" : "Equal",
|
||||
"DepthFailOp" : "Keep",
|
||||
"FailOp" : "Keep",
|
||||
"PassOp" : "Keep"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"BlendState" : {
|
||||
"Enable" : true,
|
||||
"BlendSource" : "One",
|
||||
"BlendDest" : "One",
|
||||
"BlendOp" : "Add",
|
||||
"BlendAlphaSource" : "Zero",
|
||||
"BlendAlphaDest" : "One",
|
||||
"BlendAlphaOp" : "Add"
|
||||
},
|
||||
|
||||
"DrawList" : "forward",
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "MainVS",
|
||||
"type": "Vertex"
|
||||
},
|
||||
{
|
||||
"name": "MainPS",
|
||||
"type": "Fragment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
{
|
||||
"Source" : "DiffuseGlobalFullscreen_nomsaa",
|
||||
|
||||
"RasterState" :
|
||||
{
|
||||
"CullMode" : "Back"
|
||||
},
|
||||
|
||||
"DepthStencilState" :
|
||||
{
|
||||
"Depth" :
|
||||
{
|
||||
"Enable" : false
|
||||
},
|
||||
"Stencil" :
|
||||
{
|
||||
"Enable" : true,
|
||||
"ReadMask" : "0x80",
|
||||
"WriteMask" : "0x00",
|
||||
"FrontFace" :
|
||||
{
|
||||
"Func" : "Equal",
|
||||
"DepthFailOp" : "Keep",
|
||||
"FailOp" : "Keep",
|
||||
"PassOp" : "Keep"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"BlendState" : {
|
||||
"Enable" : true,
|
||||
"BlendSource" : "One",
|
||||
"BlendDest" : "One",
|
||||
"BlendOp" : "Add",
|
||||
"BlendAlphaSource" : "Zero",
|
||||
"BlendAlphaDest" : "One",
|
||||
"BlendAlphaOp" : "Add"
|
||||
},
|
||||
|
||||
"DrawList" : "forward",
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "MainVS",
|
||||
"type": "Vertex"
|
||||
},
|
||||
{
|
||||
"name": "MainPS",
|
||||
"type": "Fragment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"Source" : "DiffuseProbeGridDownsample_nomsaa",
|
||||
|
||||
"RasterState" :
|
||||
{
|
||||
"CullMode" : "Back"
|
||||
},
|
||||
|
||||
"DepthStencilState" :
|
||||
{
|
||||
"Depth" :
|
||||
{
|
||||
"Enable" : true, // required to bind the depth buffer SRV
|
||||
"CompareFunc" : "Always"
|
||||
}
|
||||
},
|
||||
|
||||
"DrawList" : "forward",
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "MainVS",
|
||||
"type": "Vertex"
|
||||
},
|
||||
{
|
||||
"name": "MainPS",
|
||||
"type": "Fragment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -66,9 +66,7 @@ void StoreTransparentMinMaxIntoSharedMemory(float2 data)
|
||||
|
||||
ShaderResourceGroup PassSrg : SRG_PerPass
|
||||
{
|
||||
// We will use one or the other depending upon if MSAA is enabled or not
|
||||
Texture2D<float4> m_depthBuffer;
|
||||
Texture2DMS<float4> m_depthBufferMSAA;
|
||||
Texture2DMS<float4> m_depthBufferMSAA;
|
||||
|
||||
// Depth buffer where we rendered the nearest pixels of transparent objects
|
||||
Texture2D<float4> m_depthBufferTransparentMin;
|
||||
@@ -282,9 +280,19 @@ void MainCS(
|
||||
|
||||
float2 minmaxDepth_transparent = ReadTransparentMinMaxMSAA(dispatchThreadID.xy);
|
||||
|
||||
if (o_msaaMode == MsaaMode::Msaa2x)
|
||||
if (o_msaaMode == MsaaMode::None || o_msaaMode == MsaaMode::Msaa2x)
|
||||
{
|
||||
float2 opaqueDepthSamples = ReadOpaqueDepthSamples2xMSAA(dispatchThreadID.xy);
|
||||
float2 opaqueDepthSamples;
|
||||
if (o_msaaMode == MsaaMode::None)
|
||||
{
|
||||
float depth = PassSrg::m_depthBufferMSAA.Load(dispatchThreadID.xy, 0).x;
|
||||
opaqueDepthSamples = float2(depth, depth);
|
||||
}
|
||||
else if (o_msaaMode == MsaaMode::Msaa2x)
|
||||
{
|
||||
opaqueDepthSamples = ReadOpaqueDepthSamples2xMSAA(dispatchThreadID.xy);
|
||||
}
|
||||
|
||||
// Transparent geometry can't be behind opaque geometry. Just pick the first MSAA sample since we are trying to reduce reading from MSAA buffer
|
||||
minmaxDepth_transparent = DEPTH_MIN(minmaxDepth_transparent, opaqueDepthSamples.x);
|
||||
opaqueDepthSamples = ReplaceSkyPixelsWithFurthestPixelsFromTransparentObjects2x(opaqueDepthSamples, minmaxDepth_transparent);
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
{
|
||||
"Source" : "ReflectionComposite_nomsaa",
|
||||
|
||||
"RasterState" :
|
||||
{
|
||||
"CullMode" : "Back"
|
||||
},
|
||||
|
||||
"DepthStencilState" :
|
||||
{
|
||||
"Depth" :
|
||||
{
|
||||
"Enable" : false,
|
||||
"WriteMask" : "Zero",
|
||||
"CompareFunc" : "GreaterEqual"
|
||||
},
|
||||
"Stencil" :
|
||||
{
|
||||
"Enable" : true,
|
||||
"ReadMask" : "0xFF",
|
||||
"WriteMask" : "0x00",
|
||||
"FrontFace" :
|
||||
{
|
||||
"Func" : "LessEqual",
|
||||
"DepthFailOp" : "Keep",
|
||||
"FailOp" : "Keep",
|
||||
"PassOp" : "Keep"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"BlendState" : {
|
||||
"Enable" : true,
|
||||
"BlendSource" : "One",
|
||||
"BlendDest" : "One",
|
||||
"BlendOp" : "Add"
|
||||
},
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "MainVS",
|
||||
"type": "Vertex"
|
||||
},
|
||||
{
|
||||
"name": "MainPS",
|
||||
"type": "Fragment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"Source" : "ReflectionGlobalFullscreen_nomsaa",
|
||||
|
||||
"RasterState" :
|
||||
{
|
||||
"CullMode" : "Back"
|
||||
},
|
||||
|
||||
"DepthStencilState" :
|
||||
{
|
||||
"Depth" :
|
||||
{
|
||||
"Enable" : false
|
||||
},
|
||||
"Stencil" :
|
||||
{
|
||||
"Enable" : true,
|
||||
"ReadMask" : "0xFF",
|
||||
"WriteMask" : "0x00",
|
||||
"FrontFace" :
|
||||
{
|
||||
"Func" : "Equal",
|
||||
"DepthFailOp" : "Keep",
|
||||
"FailOp" : "Keep",
|
||||
"PassOp" : "Keep"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "MainVS",
|
||||
"type": "Vertex"
|
||||
},
|
||||
{
|
||||
"name": "MainPS",
|
||||
"type": "Fragment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,6 @@ set(FILES
|
||||
Passes/DepthUpsample.pass
|
||||
Passes/DiffuseComposite.pass
|
||||
Passes/DiffuseGlobalFullscreen.pass
|
||||
Passes/DiffuseGlobalFullscreen_nomsaa.pass
|
||||
Passes/DiffuseGlobalIllumination.pass
|
||||
Passes/DiffuseProbeGridBlendDistance.pass
|
||||
Passes/DiffuseProbeGridBlendIrradiance.pass
|
||||
@@ -152,7 +151,6 @@ set(FILES
|
||||
Passes/LightCullingHeatmap.pass
|
||||
Passes/LightCullingParent.pass
|
||||
Passes/LightCullingRemap.pass
|
||||
Passes/LightCullingTilePrepare.pass
|
||||
Passes/LightCullingTilePrepareMSAA.pass
|
||||
Passes/LookModificationComposite.pass
|
||||
Passes/LookModificationTransform.pass
|
||||
@@ -176,7 +174,6 @@ set(FILES
|
||||
Passes/ReflectionComposite.pass
|
||||
Passes/ReflectionCopyFrameBuffer.pass
|
||||
Passes/ReflectionGlobalFullscreen.pass
|
||||
Passes/ReflectionGlobalFullscreen_nomsaa.pass
|
||||
Passes/ReflectionProbeBlendWeight.pass
|
||||
Passes/ReflectionProbeRenderInner.pass
|
||||
Passes/ReflectionProbeRenderOuter.pass
|
||||
@@ -190,7 +187,6 @@ set(FILES
|
||||
Passes/ReflectionScreenSpaceComposite.pass
|
||||
Passes/ReflectionScreenSpaceMobile.pass
|
||||
Passes/ReflectionScreenSpaceTrace.pass
|
||||
Passes/Reflections_nomsaa.pass
|
||||
Passes/ShadowParent.pass
|
||||
Passes/Skinning.pass
|
||||
Passes/SkyBox.pass
|
||||
@@ -320,16 +316,10 @@ set(FILES
|
||||
Shaders/Depth/DepthPassTransparentMin.shader
|
||||
Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl
|
||||
Shaders/DiffuseGlobalIllumination/DiffuseComposite.shader
|
||||
Shaders/DiffuseGlobalIllumination/DiffuseComposite_nomsaa.azsl
|
||||
Shaders/DiffuseGlobalIllumination/DiffuseComposite_nomsaa.shader
|
||||
Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen.azsl
|
||||
Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen.shader
|
||||
Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen_nomsaa.azsl
|
||||
Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen_nomsaa.shader
|
||||
Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl
|
||||
Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.shader
|
||||
Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample_nomsaa.azsl
|
||||
Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample_nomsaa.shader
|
||||
Shaders/ImGui/ImGui.azsl
|
||||
Shaders/ImGui/ImGui.shader
|
||||
Shaders/LightCulling/LightCulling.azsl
|
||||
@@ -442,12 +432,8 @@ set(FILES
|
||||
Shaders/Reflections/ReflectionCommon.azsli
|
||||
Shaders/Reflections/ReflectionComposite.azsl
|
||||
Shaders/Reflections/ReflectionComposite.shader
|
||||
Shaders/Reflections/ReflectionComposite_nomsaa.azsl
|
||||
Shaders/Reflections/ReflectionComposite_nomsaa.shader
|
||||
Shaders/Reflections/ReflectionGlobalFullscreen.azsl
|
||||
Shaders/Reflections/ReflectionGlobalFullscreen.shader
|
||||
Shaders/Reflections/ReflectionGlobalFullscreen_nomsaa.azsl
|
||||
Shaders/Reflections/ReflectionGlobalFullscreen_nomsaa.shader
|
||||
Shaders/Reflections/ReflectionProbeBlendWeight.azsl
|
||||
Shaders/Reflections/ReflectionProbeBlendWeight.shader
|
||||
Shaders/Reflections/ReflectionProbeRenderCommon.azsli
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace AZ
|
||||
|
||||
static Name GetLightCullingTilePreparePassTemplateName()
|
||||
{
|
||||
return AZ::Name("LightCullingTilePrepareTemplate");
|
||||
return AZ::Name("LightCullingTilePrepareMSAATemplate");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#include <Atom/RHI.Reflect/Format.h>
|
||||
#include <Atom/RHI.Reflect/AttachmentEnums.h>
|
||||
|
||||
#include <AzCore/Preprocessor/Enum.h>
|
||||
#include <AzCore/Utils/TypeHash.h>
|
||||
|
||||
namespace AZ
|
||||
@@ -22,47 +24,44 @@ namespace AZ
|
||||
* A set of combinable flags which inform the system how a buffer is to be
|
||||
* bound to the pipeline at all stages of its lifetime.
|
||||
*/
|
||||
enum class BufferBindFlags : uint32_t
|
||||
{
|
||||
None = 0,
|
||||
AZ_ENUM_CLASS_WITH_UNDERLYING_TYPE(BufferBindFlags, uint32_t,
|
||||
(None , 0),
|
||||
|
||||
/// Supports input assembly access through a IndexBufferView or StreamBufferView. This flag is for buffers that are not updated often
|
||||
InputAssembly = AZ_BIT(0),
|
||||
(InputAssembly , AZ_BIT(0)),
|
||||
|
||||
/// Supports input assembly access through a IndexBufferView or StreamBufferView. This flag is for buffers that are updated frequently
|
||||
DynamicInputAssembly = AZ_BIT(1),
|
||||
(DynamicInputAssembly , AZ_BIT(1)),
|
||||
|
||||
/// Supports constant access through a ShaderResourceGroup.
|
||||
Constant = AZ_BIT(2),
|
||||
(Constant , AZ_BIT(2)),
|
||||
|
||||
/// Supports read access through a ShaderResourceGroup.
|
||||
ShaderRead = AZ_BIT(3),
|
||||
(ShaderRead , AZ_BIT(3)),
|
||||
|
||||
/// Supports write access through ShaderResourceGroup.
|
||||
ShaderWrite = AZ_BIT(4),
|
||||
(ShaderWrite , AZ_BIT(4)),
|
||||
|
||||
/// Supports read-write access through a ShaderResourceGroup.
|
||||
ShaderReadWrite = ShaderRead | ShaderWrite,
|
||||
(ShaderReadWrite , ShaderRead | ShaderWrite),
|
||||
|
||||
/// Supports read access for GPU copy operations.
|
||||
CopyRead = AZ_BIT(5),
|
||||
(CopyRead , AZ_BIT(5)),
|
||||
|
||||
/// Supports write access for GPU copy operations.
|
||||
CopyWrite = AZ_BIT(6),
|
||||
(CopyWrite , AZ_BIT(6)),
|
||||
|
||||
/// Supports predication access for conditional rendering.
|
||||
Predication = AZ_BIT(7),
|
||||
(Predication , AZ_BIT(7)),
|
||||
|
||||
/// Supports indirect buffer access for indirect draw/dispatch.
|
||||
Indirect = AZ_BIT(8),
|
||||
(Indirect , AZ_BIT(8)),
|
||||
|
||||
/// Supports ray tracing acceleration structure usage.
|
||||
RayTracingAccelerationStructure = AZ_BIT(9),
|
||||
(RayTracingAccelerationStructure , AZ_BIT(9)),
|
||||
|
||||
/// Supports ray tracing shader table usage.
|
||||
RayTracingShaderTable = AZ_BIT(10)
|
||||
|
||||
};
|
||||
(RayTracingShaderTable , AZ_BIT(10)));
|
||||
|
||||
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::RHI::BufferBindFlags);
|
||||
|
||||
|
||||
@@ -9,43 +9,43 @@
|
||||
|
||||
#include <Atom/RHI.Reflect/Base.h>
|
||||
|
||||
#include <AzCore/Preprocessor/Enum.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RHI
|
||||
{
|
||||
//! A set of combinable flags which inform the system how an image is to be
|
||||
//! bound to the pipeline at all stages of its lifetime.
|
||||
enum class ImageBindFlags : uint32_t
|
||||
{
|
||||
None = 0,
|
||||
AZ_ENUM_CLASS_WITH_UNDERLYING_TYPE(ImageBindFlags, uint32_t,
|
||||
(None, 0),
|
||||
|
||||
/// Supports read access through a ShaderResourceGroup.
|
||||
ShaderRead = AZ_BIT(0),
|
||||
(ShaderRead, AZ_BIT(0)),
|
||||
|
||||
/// Supports write access through a ShaderResourceGroup.
|
||||
ShaderWrite = AZ_BIT(1),
|
||||
(ShaderWrite, AZ_BIT(1)),
|
||||
|
||||
/// Supports read-write access through a ShaderResourceGroup.
|
||||
ShaderReadWrite = ShaderRead | ShaderWrite,
|
||||
(ShaderReadWrite, ShaderRead | ShaderWrite),
|
||||
|
||||
/// Supports use as a color attachment on a scope.
|
||||
Color = AZ_BIT(2),
|
||||
(Color, AZ_BIT(2)),
|
||||
|
||||
/// Supports use as depth attachment on a scope.
|
||||
Depth = AZ_BIT(3),
|
||||
(Depth, AZ_BIT(3)),
|
||||
|
||||
/// Supports use as stencil attachment on a scope.
|
||||
Stencil = AZ_BIT(4),
|
||||
(Stencil, AZ_BIT(4)),
|
||||
|
||||
/// Supports use as a depth stencil attachment on a scope.
|
||||
DepthStencil = Depth | Stencil,
|
||||
(DepthStencil, Depth | Stencil),
|
||||
|
||||
/// Supports read access for GPU copy operations.
|
||||
CopyRead = AZ_BIT(5),
|
||||
(CopyRead, AZ_BIT(5)),
|
||||
|
||||
/// Supports write access for GPU copy operations.
|
||||
CopyWrite = AZ_BIT(6),
|
||||
};
|
||||
(CopyWrite, AZ_BIT(6)));
|
||||
|
||||
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::RHI::ImageBindFlags);
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <Atom/RHI.Reflect/Handle.h>
|
||||
#include <Atom/RHI.Reflect/Limits.h>
|
||||
#include <Atom/RHI/StreamBufferView.h>
|
||||
#include <Atom/RHI/IndexBufferView.h>
|
||||
@@ -23,6 +22,10 @@ namespace AZ
|
||||
class ShaderResourceGroup;
|
||||
struct Scissor;
|
||||
struct Viewport;
|
||||
struct DefaultNamespaceType;
|
||||
// Forward declaration to
|
||||
template <typename T , typename NamespaceType>
|
||||
struct Handle;
|
||||
|
||||
struct DrawLinear
|
||||
{
|
||||
@@ -149,13 +152,13 @@ namespace AZ
|
||||
};
|
||||
|
||||
using DrawItemSortKey = int64_t;
|
||||
|
||||
|
||||
// A filter associate to a DrawItem which can be used to filter the DrawItem when submitting to command list
|
||||
using DrawFilterTag = Handle<uint8_t>;
|
||||
using DrawFilterTag = Handle<uint8_t, DefaultNamespaceType>;
|
||||
using DrawFilterMask = uint32_t; // AZStd::bitset's impelmentation is too expensive.
|
||||
constexpr uint32_t DrawFilterMaskDefaultValue = uint32_t(-1); // Default all bit to 1.
|
||||
static_assert(sizeof(DrawFilterMask) * 8 >= Limits::Pipeline::DrawFilterTagCountMax, "DrawFilterMask doesn't have enough bits for maximum tag count");
|
||||
|
||||
|
||||
struct DrawItemProperties
|
||||
{
|
||||
DrawItemProperties() = default;
|
||||
|
||||
@@ -51,6 +51,7 @@ namespace AZ
|
||||
void ModifyFrameSchedulerStatisticsFlags(RHI::FrameSchedulerStatisticsFlags statisticsFlags, bool enableFlags) override;
|
||||
const RHI::CpuTimingStatistics* GetCpuTimingStatistics() const override;
|
||||
const RHI::TransientAttachmentStatistics* GetTransientAttachmentStatistics() const override;
|
||||
const RHI::MemoryStatistics* GetMemoryStatistics() const override;
|
||||
const RHI::TransientAttachmentPoolDescriptor* GetTransientAttachmentPoolDescriptor() const override;
|
||||
ConstPtr<PlatformLimitsDescriptor> GetPlatformLimitsDescriptor() const override;
|
||||
void QueueRayTracingShaderTableForBuild(RayTracingShaderTable* rayTracingShaderTable) override;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <Atom/RHI.Reflect/FrameSchedulerEnums.h>
|
||||
#include <Atom/RHI.Reflect/MemoryStatistics.h>
|
||||
#include <Atom/RHI/DrawListTagRegistry.h>
|
||||
|
||||
namespace AZ
|
||||
@@ -55,6 +56,8 @@ namespace AZ
|
||||
|
||||
virtual const RHI::TransientAttachmentStatistics* GetTransientAttachmentStatistics() const = 0;
|
||||
|
||||
virtual const RHI::MemoryStatistics* GetMemoryStatistics() const = 0;
|
||||
|
||||
virtual const RHI::TransientAttachmentPoolDescriptor* GetTransientAttachmentPoolDescriptor() const = 0;
|
||||
|
||||
virtual ConstPtr<PlatformLimitsDescriptor> GetPlatformLimitsDescriptor() const = 0;
|
||||
|
||||
@@ -281,6 +281,11 @@ namespace AZ
|
||||
return m_frameScheduler.GetTransientAttachmentStatistics();
|
||||
}
|
||||
|
||||
const RHI::MemoryStatistics* RHISystem::GetMemoryStatistics() const
|
||||
{
|
||||
return m_frameScheduler.GetMemoryStatistics();
|
||||
}
|
||||
|
||||
const AZ::RHI::TransientAttachmentPoolDescriptor* RHISystem::GetTransientAttachmentPoolDescriptor() const
|
||||
{
|
||||
return m_frameScheduler.GetTransientAttachmentPoolDescriptor();
|
||||
|
||||
@@ -122,11 +122,6 @@ namespace Platform
|
||||
return reinterpret_cast<RHIMetalView*>([nativeWindow.contentViewController view]);
|
||||
}
|
||||
|
||||
void ApplyTileDimentions(MTLRenderPassDescriptor* mtlRenderPassDescriptor)
|
||||
{
|
||||
AZ_UNUSED(mtlRenderPassDescriptor);
|
||||
}
|
||||
|
||||
void SynchronizeBufferOnCPU(id<MTLBuffer> mtlBuffer, size_t bufferOffset, size_t bufferSize)
|
||||
{
|
||||
if(mtlBuffer.storageMode == MTLStorageModeManaged)
|
||||
|
||||
@@ -88,17 +88,6 @@ namespace Platform
|
||||
return reinterpret_cast<RHIMetalView*>([nativeWindow.rootViewController view]);
|
||||
}
|
||||
|
||||
void ApplyTileDimentions(MTLRenderPassDescriptor* mtlRenderPassDescriptor)
|
||||
{
|
||||
//Metal driver has a bug where if the tile dimensions changes between passes it will
|
||||
//generate incorrect vertex positions (possible vertex invariance). For example vertex invariance was
|
||||
//observed between Depth pass and forward pass. Hence for now we are setting global tile dimentions across all passes.
|
||||
//For performance sake we should eventually remove this once this bug is addressed.
|
||||
//[GFX_TODO][ATOM-13440] - Remove once driver bug is addressed.
|
||||
mtlRenderPassDescriptor.tileWidth = 16;
|
||||
mtlRenderPassDescriptor.tileHeight = 16;
|
||||
}
|
||||
|
||||
void SynchronizeBufferOnCPU(id<MTLBuffer> mtlBuffer, size_t bufferOffset, size_t bufferSize)
|
||||
{
|
||||
//No synchronization needed as ios uses shared memory and does not support MTLStorageModeManaged
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
#include <RHI/Scope.h>
|
||||
#include <Atom/RHI/SwapChainFrameAttachment.h>
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
void ApplyTileDimentions(MTLRenderPassDescriptor* mtlRenderPassDescriptor);
|
||||
}
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -88,7 +84,6 @@ namespace AZ
|
||||
{
|
||||
AZ_Assert(m_renderPassDescriptor == nil, "m_renderPassDescriptor should be null");
|
||||
m_renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
Platform::ApplyTileDimentions(m_renderPassDescriptor);
|
||||
}
|
||||
|
||||
if(GetEstimatedItemCount())
|
||||
|
||||
@@ -815,11 +815,12 @@ namespace AZ
|
||||
createInfo.size = descriptor.m_byteCount;
|
||||
createInfo.usage = GetBufferUsageFlagBitsUnderRestrictions(descriptor.m_bindFlags);
|
||||
// Trying to guess here if the buffers are going to be used as attachments. Maybe it would be better to add an explicit flag in the descriptor.
|
||||
createInfo.sharingMode =
|
||||
RHI::CheckBitsAny(
|
||||
descriptor.m_bindFlags,
|
||||
RHI::BufferBindFlags::ShaderWrite | RHI::BufferBindFlags::Predication | RHI::BufferBindFlags::Indirect)
|
||||
? VK_SHARING_MODE_EXCLUSIVE
|
||||
createInfo.sharingMode =
|
||||
(RHI::CheckBitsAny(
|
||||
descriptor.m_bindFlags,
|
||||
RHI::BufferBindFlags::ShaderWrite | RHI::BufferBindFlags::Predication | RHI::BufferBindFlags::Indirect) ||
|
||||
(queueFamilies.size()) <= 1)
|
||||
? VK_SHARING_MODE_EXCLUSIVE
|
||||
: VK_SHARING_MODE_CONCURRENT;
|
||||
createInfo.queueFamilyIndexCount = static_cast<uint32_t>(queueFamilies.size());
|
||||
createInfo.pQueueFamilyIndices = queueFamilies.empty() ? nullptr : queueFamilies.data();
|
||||
|
||||
@@ -23,7 +23,11 @@ namespace AZ
|
||||
namespace RHI
|
||||
{
|
||||
class ScopeAttachment;
|
||||
enum class BufferBindFlags : uint32_t;
|
||||
// NOTE: see BufferDescriptor.h, AZ_ENUM... macro wraps enum within an outer inline namespace.
|
||||
inline namespace BufferBindFlagsNamespace
|
||||
{
|
||||
enum class BufferBindFlags : uint32_t;
|
||||
}
|
||||
class BufferView;
|
||||
class ImageView;
|
||||
struct BufferSubresourceRange;
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace AZ
|
||||
{
|
||||
return RHI::ResultCode::Fail;
|
||||
}
|
||||
m_shaderResourceGroup->SetName(srgName);
|
||||
m_shaderResourceGroup->SetName(m_pool->GetRHIPool()->GetName());
|
||||
m_data = RHI::ShaderResourceGroupData(m_layout);
|
||||
m_asset = { &shaderAsset, AZ::Data::AssetLoadBehavior::PreLoad };
|
||||
|
||||
|
||||
@@ -8,9 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Atom/RHI.Reflect/MemoryStatistics.h>
|
||||
#include <Atom/RPI.Public/GpuQuery/GpuQueryTypes.h>
|
||||
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <AzCore/std/containers/variant.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AZ
|
||||
@@ -250,6 +253,44 @@ namespace AZ
|
||||
|
||||
};
|
||||
|
||||
class ImGuiGpuMemoryView
|
||||
{
|
||||
public:
|
||||
// Draw the overall GPU memory profiling window.
|
||||
void DrawGpuMemoryWindow(bool& draw);
|
||||
|
||||
private:
|
||||
// Draw the heap usage pie chart
|
||||
void DrawPieChart(const AZ::RHI::MemoryStatistics::Heap& heap);
|
||||
|
||||
// Update the saved pointers in m_tableRows according to new data/filters
|
||||
void UpdateTableRows();
|
||||
|
||||
void DrawTable();
|
||||
|
||||
// Sort the table according to the appropriate column.
|
||||
void SortTable(ImGuiTableSortSpecs* sortSpecs);
|
||||
|
||||
struct TableRow
|
||||
{
|
||||
Name m_parentPoolName;
|
||||
Name m_bufImgName;
|
||||
size_t m_sizeInBytes = 0;
|
||||
AZStd::string m_bindFlags;
|
||||
};
|
||||
|
||||
// Table settings
|
||||
bool m_includeBuffers = true;
|
||||
bool m_includeImages = true;
|
||||
bool m_includeTransientAttachments = true;
|
||||
|
||||
ImGuiTextFilter m_nameFilter;
|
||||
|
||||
AZStd::vector<TableRow> m_tableRows;
|
||||
AZStd::vector<AZ::RHI::MemoryStatistics::Pool> m_savedPools;
|
||||
AZStd::vector<AZ::RHI::MemoryStatistics::Heap> m_savedHeaps;
|
||||
};
|
||||
|
||||
class ImGuiGpuProfiler
|
||||
{
|
||||
public:
|
||||
@@ -275,9 +316,11 @@ namespace AZ
|
||||
|
||||
bool m_drawTimestampView = false;
|
||||
bool m_drawPipelineStatisticsView = false;
|
||||
bool m_drawGpuMemoryView = false;
|
||||
|
||||
ImGuiTimestampView m_timestampView;
|
||||
ImGuiPipelineStatisticsView m_pipelineStatisticsView;
|
||||
ImGuiGpuMemoryView m_gpuMemoryView;
|
||||
};
|
||||
} //namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Atom/RHI/RHISystemInterface.h>
|
||||
#include <Atom/RPI.Public/Pass/ParentPass.h>
|
||||
#include <Atom/RPI.Public/Pass/RenderPass.h>
|
||||
#include <Atom/RPI.Public/RenderPipeline.h>
|
||||
#include <Atom/RPI.Public/RPISystemInterface.h>
|
||||
#include <Atom/RPI.Public/Scene.h>
|
||||
|
||||
#include <AzCore/std/sort.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
namespace AZ
|
||||
@@ -87,6 +90,38 @@ namespace AZ
|
||||
}
|
||||
drawList->AddText(font, font->FontSize, pos, ImGui::GetColorU32(ImGuiCol_Text), text, nullptr, size.x);
|
||||
}
|
||||
|
||||
inline static AZStd::string GetImageBindStrings(AZ::RHI::ImageBindFlags imageBindFlags)
|
||||
{
|
||||
AZStd::string imageBindStrings;
|
||||
for (const auto& flag : AZ::RHI::ImageBindFlagsMembers)
|
||||
{
|
||||
if (flag.m_value != AZ::RHI::ImageBindFlags::None && AZ::RHI::CheckBitsAll(imageBindFlags, flag.m_value))
|
||||
{
|
||||
imageBindStrings.append(flag.m_string);
|
||||
imageBindStrings.append(", ");
|
||||
}
|
||||
}
|
||||
return imageBindStrings;
|
||||
}
|
||||
|
||||
inline static AZStd::string GetBufferBindStrings(AZ::RHI::BufferBindFlags bufferBindFlags)
|
||||
{
|
||||
AZStd::string bufferBindStrings;
|
||||
for (const auto& flag : AZ::RHI::BufferBindFlagsMembers)
|
||||
{
|
||||
if (flag.m_value != AZ::RHI::BufferBindFlags::None && AZ::RHI::CheckBitsAll(bufferBindFlags, flag.m_value))
|
||||
{
|
||||
bufferBindStrings.append(flag.m_string);
|
||||
bufferBindStrings.append(", ");
|
||||
}
|
||||
}
|
||||
return bufferBindStrings;
|
||||
}
|
||||
|
||||
static constexpr u64 KB = 1024;
|
||||
static constexpr u64 MB = 1024 * KB;
|
||||
static constexpr u64 GB = 1024 * MB;
|
||||
} // namespace GpuProfilerImGuiHelper
|
||||
|
||||
// --- PassEntry ---
|
||||
@@ -1025,6 +1060,219 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
// --- ImGuiGpuMemoryView ---
|
||||
|
||||
inline void ImGuiGpuMemoryView::SortTable(ImGuiTableSortSpecs* sortSpecs)
|
||||
{
|
||||
const bool ascending = sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending;
|
||||
const ImS16 columnToSort = sortSpecs->Specs->ColumnIndex;
|
||||
|
||||
// Sort by the appropriate column in the table
|
||||
switch (columnToSort)
|
||||
{
|
||||
case (0): // Sorting by parent pool name
|
||||
AZStd::sort(m_tableRows.begin(), m_tableRows.end(),
|
||||
[ascending](const TableRow& lhs, const TableRow& rhs)
|
||||
{
|
||||
const auto lhsParentPool = lhs.m_parentPoolName.GetStringView();
|
||||
const auto rhsParentPool = rhs.m_parentPoolName.GetStringView();
|
||||
return ascending ? lhsParentPool < rhsParentPool : lhsParentPool > rhsParentPool;
|
||||
});
|
||||
break;
|
||||
case (1): // Sort by buffer/image name
|
||||
AZStd::sort(m_tableRows.begin(), m_tableRows.end(),
|
||||
[ascending](const TableRow& lhs, const TableRow& rhs)
|
||||
{
|
||||
const auto lhsName = lhs.m_bufImgName.GetStringView();
|
||||
const auto rhsName = rhs.m_bufImgName.GetStringView();
|
||||
return ascending ? lhsName < rhsName : lhsName > rhsName;
|
||||
});
|
||||
break;
|
||||
case (2): // Sort by memory usage
|
||||
AZStd::sort(m_tableRows.begin(), m_tableRows.end(),
|
||||
[ascending](const TableRow& lhs, const TableRow& rhs)
|
||||
{
|
||||
const float lhsSize = lhs.m_sizeInBytes;
|
||||
const float rhsSize = rhs.m_sizeInBytes;
|
||||
return ascending ? lhsSize < rhsSize : lhsSize > rhsSize;
|
||||
});
|
||||
break;
|
||||
}
|
||||
sortSpecs->SpecsDirty = false;
|
||||
}
|
||||
|
||||
inline void ImGuiGpuMemoryView::DrawTable()
|
||||
{
|
||||
if (ImGui::BeginTable("Table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Sortable | ImGuiTableFlags_Resizable))
|
||||
{
|
||||
ImGui::TableSetupColumn("Parent pool");
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Size (MB)", 0, 100.0f);
|
||||
ImGui::TableSetupColumn("BindFlags", ImGuiTableColumnFlags_NoSort);
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGuiTableSortSpecs* sortSpecs = ImGui::TableGetSortSpecs();
|
||||
if (sortSpecs && sortSpecs->SpecsDirty)
|
||||
{
|
||||
SortTable(sortSpecs);
|
||||
}
|
||||
|
||||
// Draw each row in the table
|
||||
for (const auto& tableRow : m_tableRows)
|
||||
{
|
||||
// Don't draw the row if none of the row's text fields pass the filter
|
||||
if (!m_nameFilter.PassFilter(tableRow.m_parentPoolName.GetCStr())
|
||||
&& !m_nameFilter.PassFilter(tableRow.m_bufImgName.GetCStr())
|
||||
&& !m_nameFilter.PassFilter(tableRow.m_bindFlags.c_str()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ImGui::Text(tableRow.m_parentPoolName.GetCStr());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(tableRow.m_bufImgName.GetCStr());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%.2f", 1.0f * tableRow.m_sizeInBytes / GpuProfilerImGuiHelper::MB);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(tableRow.m_bindFlags.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
inline void ImGuiGpuMemoryView::UpdateTableRows()
|
||||
{
|
||||
// Update the table according to the latest filters applied
|
||||
m_tableRows.clear();
|
||||
for (const auto& pool : m_savedPools)
|
||||
{
|
||||
Name poolName = pool.m_name.IsEmpty() ? Name("Unnamed pool") : pool.m_name;
|
||||
|
||||
// Ignore transient pools
|
||||
if (!m_includeTransientAttachments && pool.m_name.GetStringView().contains("Transient"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_includeBuffers)
|
||||
{
|
||||
for (const auto& buf : pool.m_buffers)
|
||||
{
|
||||
const Name bufName = buf.m_name.IsEmpty() ? Name("Unnamed Buffer") : buf.m_name;
|
||||
const AZStd::string flags = GpuProfilerImGuiHelper::GetBufferBindStrings(buf.m_bindFlags);
|
||||
m_tableRows.push_back({ poolName, bufName, buf.m_sizeInBytes, flags });
|
||||
}
|
||||
}
|
||||
|
||||
if (m_includeImages)
|
||||
{
|
||||
for (const auto& img : pool.m_images)
|
||||
{
|
||||
const Name imgName = img.m_name.IsEmpty() ? Name("Unnamed Image") : img.m_name;
|
||||
const AZStd::string flags = GpuProfilerImGuiHelper::GetImageBindStrings(img.m_bindFlags);
|
||||
m_tableRows.push_back({ poolName, imgName, img.m_sizeInBytes, flags });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void ImGuiGpuMemoryView::DrawPieChart(const AZ::RHI::MemoryStatistics::Heap& heap)
|
||||
{
|
||||
if (ImGui::BeginChild("PieChart", {150, 150}, true))
|
||||
{
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
const auto [wx, wy] = ImGui::GetWindowPos();
|
||||
const auto [windowWidth, windowHeight] = ImGui::GetWindowSize();
|
||||
const ImVec2 center = { wx + windowWidth / 2, wy + windowHeight / 2 };
|
||||
const float radius = windowWidth / 2 - 10;
|
||||
|
||||
// Draw the pie chart
|
||||
drawList->AddCircleFilled(center, radius, ImGui::GetColorU32({.3, .3, .3, 1}));
|
||||
const float usagePercent = 1.0f * heap.m_memoryUsage.m_residentInBytes / heap.m_memoryUsage.m_budgetInBytes;
|
||||
drawList->PathArcTo(center, radius, 0, AZ::Constants::TwoPi * usagePercent); // Clockwise starting from rightmost point
|
||||
drawList->PathArcTo(center, 0, 0, 0); // To center
|
||||
drawList->PathArcTo(center, radius, 0, 0); // Back to starting position
|
||||
drawList->PathFillConvex(ImGui::GetColorU32({ .039, .8, 0.556, 1 }));
|
||||
ImGui::Text("%.2f%%", usagePercent * 100);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
inline void ImGuiGpuMemoryView::DrawGpuMemoryWindow(bool& draw)
|
||||
{
|
||||
// Enable GPU memory instrumentation while the window is open. Called every draw frame, but just a bitwise operation so overhead should be low.
|
||||
auto* rhiSystem = AZ::RHI::RHISystemInterface::Get();
|
||||
AZ_Assert(rhiSystem != nullptr, "Error in drawing GPU memory window: RHI System Interface was nullptr");
|
||||
rhiSystem->ModifyFrameSchedulerStatisticsFlags(AZ::RHI::FrameSchedulerStatisticsFlags::GatherMemoryStatistics, draw);
|
||||
|
||||
if (!draw)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSize({ 600, 600 }, ImGuiCond_Once);
|
||||
if (ImGui::Begin("Gpu Memory Profiler", &draw, ImGuiViewportFlags_None))
|
||||
{
|
||||
if (ImGui::Button("Capture"))
|
||||
{
|
||||
// Collect and save new GPU memory usage data
|
||||
const auto* memoryStatistics = rhiSystem->GetMemoryStatistics();
|
||||
if (memoryStatistics)
|
||||
{
|
||||
m_tableRows.clear();
|
||||
m_savedPools = memoryStatistics->m_pools;
|
||||
m_savedHeaps = memoryStatistics->m_heaps;
|
||||
|
||||
// Collect the data into TableRows, ignoring depending on flags
|
||||
UpdateTableRows();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox("Show buffers", &m_includeBuffers)
|
||||
|| ImGui::Checkbox("Show images", &m_includeImages)
|
||||
|| ImGui::Checkbox("Show transient attachments", &m_includeTransientAttachments))
|
||||
{
|
||||
UpdateTableRows();
|
||||
}
|
||||
|
||||
ImGui::Text("Overall heap usage:");
|
||||
for (const auto& savedHeap : m_savedHeaps)
|
||||
{
|
||||
if (ImGui::BeginChild(savedHeap.m_name.GetCStr(), { ImGui::GetWindowWidth() / m_savedHeaps.size(), 250 }), ImGuiWindowFlags_NoScrollbar)
|
||||
{
|
||||
ImGui::Text(savedHeap.m_name.GetCStr());
|
||||
ImGui::Columns(2, "HeapData", true);
|
||||
|
||||
ImGui::Text("Resident (MB): ");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%.2f", 1.0 * savedHeap.m_memoryUsage.m_residentInBytes.load() / GpuProfilerImGuiHelper::MB);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::Text("Reserved (MB): ");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%.2f", 1.0 * savedHeap.m_memoryUsage.m_reservedInBytes.load() / GpuProfilerImGuiHelper::MB);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::Text("Budget (MB): ");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%.2f", 1.0 * savedHeap.m_memoryUsage.m_budgetInBytes / GpuProfilerImGuiHelper::MB);
|
||||
|
||||
ImGui::Columns(1, "PieChartColumn");
|
||||
DrawPieChart(savedHeap);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() / m_savedHeaps.size());
|
||||
}
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
|
||||
m_nameFilter.Draw("Search");
|
||||
DrawTable();
|
||||
}
|
||||
}
|
||||
|
||||
// --- ImGuiGpuProfiler ---
|
||||
|
||||
inline void ImGuiGpuProfiler::Draw(bool& draw, RHI::Ptr<RPI::ParentPass> rootPass)
|
||||
@@ -1045,6 +1293,8 @@ namespace AZ
|
||||
{
|
||||
rootPass->SetPipelineStatisticsQueryEnabled(m_drawPipelineStatisticsView);
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::Checkbox("Enable GpuMemoryView", &m_drawGpuMemoryView);
|
||||
});
|
||||
|
||||
// Draw the PipelineStatistics window.
|
||||
@@ -1053,6 +1303,9 @@ namespace AZ
|
||||
// Draw the PipelineStatistics window.
|
||||
m_pipelineStatisticsView.DrawPipelineStatisticsWindow(m_drawPipelineStatisticsView, rootPassEntryRef, m_passEntryDatabase, rootPass);
|
||||
|
||||
// Draw the GpuMemory window.
|
||||
m_gpuMemoryView.DrawGpuMemoryWindow(m_drawGpuMemoryView);
|
||||
|
||||
//closing window
|
||||
if (wasDraw && !draw)
|
||||
{
|
||||
|
||||
+2
@@ -6,7 +6,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <PostProcess/GradientWeightModifier/GradientWeightModifierController.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
+1
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <PostProcess/RadiusWeightModifier/RadiusWeightModifierComponentController.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
+1
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <PostProcess/ShapeWeightModifier/ShapeWeightModifierComponentController.h>
|
||||
#include <LmbrCentral/Shape/ShapeComponentBus.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
+1
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <Scripting/EntityReferenceComponentController.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
+1
@@ -9,6 +9,7 @@
|
||||
#include <AudioAllocators.h>
|
||||
#include <AudioLogger.h>
|
||||
#include <SoundCVars.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
|
||||
namespace Audio::Platform
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "EMotionFXConfig.h"
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include "BaseObject.h"
|
||||
#include "VertexAttributeLayer.h"
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/std/containers/map.h>
|
||||
|
||||
namespace ImGui
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "AzCore/Asset/AssetManager.h"
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Jobs/JobFunction.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace LmbrCentral
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <LyShine/Bus/UiGameEntityContextBus.h>
|
||||
#include <LyShine/Bus/UiElementBus.h>
|
||||
@@ -130,6 +131,29 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
static bool UiFlipbookAnimationComponentVersionConverter(AZ::SerializeContext& context,
|
||||
AZ::SerializeContext::DataElementNode& classElement)
|
||||
{
|
||||
// conversion from version 2:
|
||||
// - Rename "frame delay" to "framerate"
|
||||
// - Set "framerate unit" to seconds (default moving forward is FPS, but we use seconds for legacy compatibility)
|
||||
if (classElement.GetVersion() <= 2)
|
||||
{
|
||||
if (!ConvertFrameDelayToFramerate(context, classElement))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ConvertFramerateUnitToSeconds(context, classElement))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void UiFlipbookAnimationComponent::Reflect(AZ::ReflectContext* context)
|
||||
@@ -138,7 +162,7 @@ void UiFlipbookAnimationComponent::Reflect(AZ::ReflectContext* context)
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<UiFlipbookAnimationComponent, AZ::Component>()
|
||||
->Version(3, &VersionConverter)
|
||||
->Version(3, &UiFlipbookAnimationComponentVersionConverter)
|
||||
->Field("Start Frame", &UiFlipbookAnimationComponent::m_startFrame)
|
||||
->Field("End Frame", &UiFlipbookAnimationComponent::m_endFrame)
|
||||
->Field("Loop Start Frame", &UiFlipbookAnimationComponent::m_loopStartFrame)
|
||||
@@ -268,29 +292,6 @@ void UiFlipbookAnimationComponent::Reflect(AZ::ReflectContext* context)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
bool UiFlipbookAnimationComponent::VersionConverter(AZ::SerializeContext& context,
|
||||
AZ::SerializeContext::DataElementNode& classElement)
|
||||
{
|
||||
// conversion from version 2:
|
||||
// - Rename "frame delay" to "framerate"
|
||||
// - Set "framerate unit" to seconds (default moving forward is FPS, but we use seconds for legacy compatibility)
|
||||
if (classElement.GetVersion() <= 2)
|
||||
{
|
||||
if (!ConvertFrameDelayToFramerate(context, classElement))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ConvertFramerateUnitToSeconds(context, classElement))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
AZ::u32 UiFlipbookAnimationComponent::GetMaxFrame() const
|
||||
{
|
||||
|
||||
@@ -107,9 +107,6 @@ protected: // static functions
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static bool VersionConverter(AZ::SerializeContext& context,
|
||||
AZ::SerializeContext::DataElementNode& classElement);
|
||||
|
||||
protected: // functions
|
||||
|
||||
//! Returns a string representation of the indices used to index sprite-sheet types.
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Multiplayer
|
||||
virtual bool ReplicationSetUpdateReady() = 0;
|
||||
virtual const ReplicationSet& GetReplicationSet() const = 0;
|
||||
//! Max number of entities we can send updates for in one frame
|
||||
virtual uint32_t GetMaxEntityReplicatorSendCount() const = 0;
|
||||
virtual uint32_t GetMaxProxyEntityReplicatorSendCount() const = 0;
|
||||
virtual bool IsInWindow(const ConstNetworkEntityHandle& entityPtr, NetEntityRole& outNetworkRole) const = 0;
|
||||
virtual void UpdateWindow() = 0;
|
||||
virtual void DebugDraw() const = 0;
|
||||
|
||||
@@ -459,6 +459,7 @@ namespace {{ Component.attrib['Namespace'] }}
|
||||
|
||||
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Server', false)|indent(8) -}}
|
||||
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', false)|indent(8) -}}
|
||||
{{ DeclareNetworkPropertyGetters(Component, 'Autonomous', 'Authority', false)|indent(8) -}}
|
||||
{{ DeclareArchetypePropertyGetters(Component)|indent(8) -}}
|
||||
{{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) -}}
|
||||
{{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Authority', 'Client')|indent(8) -}}
|
||||
@@ -483,6 +484,7 @@ namespace {{ Component.attrib['Namespace'] }}
|
||||
|
||||
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Server', true)|indent(8) -}}
|
||||
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', true)|indent(8) -}}
|
||||
{{ DeclareNetworkPropertyGetters(Component, 'Autonomous', 'Authority', true)|indent(8) -}}
|
||||
{{ DeclareRpcInvocations(Component, 'Server', 'Authority', true)|indent(8) -}}
|
||||
{{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Client', false)|indent(8) -}}
|
||||
{{ AutoComponentMacros.DeclareRpcSignals(Component, 'Authority', 'Client')|indent(8) -}}
|
||||
|
||||
@@ -1548,8 +1548,10 @@ namespace {{ Component.attrib['Namespace'] }}
|
||||
|
||||
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', false, ComponentBaseName)|indent(4) -}}
|
||||
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Client', false, ComponentBaseName)|indent(4) -}}
|
||||
{{ DefineNetworkPropertyGets(Component, 'Autonomous', 'Authority', false, ComponentBaseName)|indent(4) -}}
|
||||
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', true, ComponentBaseName)|indent(4) -}}
|
||||
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Client', true, ComponentBaseName)|indent(4) }}
|
||||
{{ DefineNetworkPropertyGets(Component, 'Autonomous', 'Authority', true, ComponentBaseName)|indent(4) }}
|
||||
{{ DefineArchetypePropertyGets(Component, ClassType, ComponentBaseName)|indent(4) -}}
|
||||
{{ DefineRpcInvocations(Component, ComponentBaseName, 'Server', 'Authority', false)|indent(4) -}}
|
||||
{{ DefineRpcInvocations(Component, ComponentBaseName, 'Server', 'Authority', true)|indent(4) }}
|
||||
|
||||
@@ -660,6 +660,7 @@ namespace Multiplayer
|
||||
|
||||
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<NullReplicationWindow>();
|
||||
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->GetReplicationManager().SetEntityActivationTimeSliceMs(cl_defaultNetworkEntityActivationTimeSliceMs);
|
||||
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-13
@@ -185,16 +185,14 @@ namespace Multiplayer
|
||||
// Generate a list of all our entities that need updates
|
||||
EntityReplicatorList toSendList;
|
||||
|
||||
uint32_t elementsAdded = 0;
|
||||
for (auto iter = m_replicatorsPendingSend.begin(); iter != m_replicatorsPendingSend.end() && elementsAdded < m_replicationWindow->GetMaxEntityReplicatorSendCount(); )
|
||||
uint32_t proxySendCount = 0;
|
||||
for (auto iter = m_replicatorsPendingSend.begin(); iter != m_replicatorsPendingSend.end();)
|
||||
{
|
||||
EntityReplicator* replicator = GetEntityReplicator(*iter);
|
||||
bool clearPendingSend = true;
|
||||
if (replicator)
|
||||
if (EntityReplicator* replicator = GetEntityReplicator(*iter))
|
||||
{
|
||||
NetEntityId entityId = replicator->GetEntityHandle().GetNetEntityId();
|
||||
PropertyPublisher* propPublisher = replicator->GetPropertyPublisher();
|
||||
if (propPublisher)
|
||||
if (PropertyPublisher* propPublisher = replicator->GetPropertyPublisher())
|
||||
{
|
||||
// don't have too many replicators pending creation outstanding at a time
|
||||
bool canSend = true;
|
||||
@@ -220,19 +218,17 @@ namespace Multiplayer
|
||||
m_remoteEntitiesPendingCreation.insert(entityId);
|
||||
}
|
||||
|
||||
if (replicator->GetRemoteNetworkRole() == NetEntityRole::Autonomous)
|
||||
if (replicator->GetRemoteNetworkRole() == NetEntityRole::Autonomous ||
|
||||
replicator->GetBoundLocalNetworkRole() == NetEntityRole::Autonomous)
|
||||
{
|
||||
toSendList.push_back(replicator);
|
||||
}
|
||||
else
|
||||
else if (proxySendCount < m_replicationWindow->GetMaxProxyEntityReplicatorSendCount())
|
||||
{
|
||||
if (elementsAdded < m_replicationWindow->GetMaxEntityReplicatorSendCount())
|
||||
{
|
||||
toSendList.push_back(replicator);
|
||||
}
|
||||
toSendList.push_back(replicator);
|
||||
++proxySendCount;
|
||||
}
|
||||
}
|
||||
++elementsAdded;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -305,16 +305,12 @@ namespace Multiplayer
|
||||
|
||||
bool EntityReplicator::RemoteManagerOwnsEntityLifetime() const
|
||||
{
|
||||
bool ret(false);
|
||||
bool isServer = (GetBoundLocalNetworkRole() == NetEntityRole::Server)
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Authority);
|
||||
bool isClient = (GetBoundLocalNetworkRole() == NetEntityRole::Client)
|
||||
|| (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous);
|
||||
if (isServer || isClient)
|
||||
{
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
|
||||
return isServer || isClient;
|
||||
}
|
||||
|
||||
void EntityReplicator::MarkForRemoval()
|
||||
|
||||
@@ -22,6 +22,14 @@ namespace Multiplayer
|
||||
, m_pendingRecord(remoteNetworkRole)
|
||||
, m_sentRecords(net_EntityReplicatorRecordsMax)
|
||||
{
|
||||
if ( ownsLifetime == OwnsLifetime::False )
|
||||
{
|
||||
// This entity is owned by some other authority; this publisher will only be used for updating (not creating).
|
||||
// Since this replicator does not own it's lifetime, the remote replicator must exist (otherwise, we would never have created a replicator that doesn't own its lifetime).
|
||||
m_remoteReplicatorEstablished = true;
|
||||
m_replicatorState = EntityReplicatorState::Updating;
|
||||
}
|
||||
|
||||
AZ_Assert(m_netBindComponent, "NetBindComponent is nullptr");
|
||||
m_pendingRecord.SetRemoteNetworkRole(remoteNetworkRole);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user