Merge remote-tracking branch 'upstream/main' into nvsickle/BootstrapFixes
This commit is contained in:
@@ -43,6 +43,7 @@ set(GEM_DEPENDENCIES
|
||||
Gem::GradientSignal
|
||||
Gem::Vegetation
|
||||
Gem::Atom_AtomBridge
|
||||
Gem::AtomFont
|
||||
Gem::NvCloth
|
||||
Gem::Blast
|
||||
Gem::AWSCore
|
||||
|
||||
@@ -55,6 +55,7 @@ set(GEM_DEPENDENCIES
|
||||
Gem::Atom_RHI.Private
|
||||
Gem::Atom_Feature_Common.Editor
|
||||
Gem::Atom_AtomBridge.Editor
|
||||
Gem::AtomFont
|
||||
Gem::NvCloth.Editor
|
||||
Gem::Blast.Editor
|
||||
Gem::AWSCore.Editor
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
import pytest
|
||||
import os
|
||||
import logging
|
||||
import ly_test_tools.log.log_monitor
|
||||
|
||||
from AWS.Windows.resource_mappings.resource_mappings import resource_mappings
|
||||
from AWS.Windows.cdk.cdk import cdk
|
||||
from AWS.common.aws_utils import aws_utils
|
||||
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.usefixtures('cdk')
|
||||
@pytest.mark.parametrize('feature_name', [AWS_CLIENT_AUTH_FEATURE_NAME])
|
||||
@pytest.mark.usefixtures('resource_mappings')
|
||||
@pytest.mark.parametrize('resource_mappings_filename', ['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 TestAWSClientAuthPasswordSignIn(object):
|
||||
"""
|
||||
Test class to verify AWS Cognito IDP Password sign in and Cognito Identity pool authenticated authorization.
|
||||
"""
|
||||
|
||||
def test_password_signin_credentials(self,
|
||||
launcher: pytest.fixture,
|
||||
cdk: pytest.fixture,
|
||||
resource_mappings: pytest.fixture,
|
||||
workspace: pytest.fixture,
|
||||
asset_processor: pytest.fixture,
|
||||
aws_utils: pytest.fixture
|
||||
):
|
||||
"""
|
||||
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()
|
||||
|
||||
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', 'AWS/ClientAuthPasswordSignUp']
|
||||
|
||||
with launcher.start(launch_ap=False):
|
||||
result = log_monitor.monitor_log_for_lines(
|
||||
expected_lines=['(Script) - Signup Success'],
|
||||
unexpected_lines=['(Script) - Signup Fail'],
|
||||
halt_on_unexpected=True,
|
||||
)
|
||||
assert result, 'Sign Up Success.'
|
||||
|
||||
launcher.stop()
|
||||
|
||||
cognito_idp = aws_utils.client('cognito-idp')
|
||||
user_pool_id = resource_mappings.get_resource_name_id(f'{AWS_CLIENT_AUTH_FEATURE_NAME}.CognitoUserPoolId')
|
||||
print(f'UserPoolId:{user_pool_id}')
|
||||
cognito_idp.admin_confirm_sign_up(
|
||||
UserPoolId=user_pool_id,
|
||||
Username='test1'
|
||||
)
|
||||
|
||||
launcher.args = ['+LoadLevel', 'AWS/ClientAuthPasswordSignIn']
|
||||
|
||||
with launcher.start(launch_ap=False):
|
||||
result = log_monitor.monitor_log_for_lines(
|
||||
expected_lines=['(Script) - SignIn Success', '(Script) - Success credentials'],
|
||||
unexpected_lines=['(Script) - SignIn Fail', '(Script) - Fail credentials'],
|
||||
halt_on_unexpected=True,
|
||||
)
|
||||
assert result, 'Sign in Success, fetched authenticated AWS temp credentials.'
|
||||
@@ -39,6 +39,7 @@ class ResourceMappings:
|
||||
self._region = region
|
||||
self._feature_name = feature_name
|
||||
self._account_id = account_id
|
||||
self._resource_mappings = {}
|
||||
|
||||
assert os.path.exists(self._resource_mapping_file_path), \
|
||||
f'Invalid resource mapping file path {self._resource_mapping_file_path}'
|
||||
@@ -79,6 +80,7 @@ class ResourceMappings:
|
||||
resource_mappings[AWS_RESOURCE_MAPPINGS_KEY][resource_key]['Name/ID'] = output.get('OutputValue',
|
||||
'InvalidId')
|
||||
|
||||
self._resource_mappings = resource_mappings
|
||||
with open(self._resource_mapping_file_path, 'w') as file_content:
|
||||
json.dump(resource_mappings, file_content, indent=4)
|
||||
|
||||
@@ -103,6 +105,9 @@ class ResourceMappings:
|
||||
self._region = ''
|
||||
self._client = None
|
||||
|
||||
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(
|
||||
|
||||
@@ -20,7 +20,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT
|
||||
TEST_SUITE main
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/test_Atom_MainSuite.py
|
||||
TEST_SERIAL
|
||||
TIMEOUT 300
|
||||
TIMEOUT 400
|
||||
RUNTIME_DEPENDENCIES
|
||||
AssetProcessor
|
||||
AutomatedTesting.Assets
|
||||
@@ -31,7 +31,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT
|
||||
TEST_SUITE sandbox
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/test_Atom_SandboxSuite.py
|
||||
TEST_SERIAL
|
||||
TIMEOUT 300
|
||||
TIMEOUT 400
|
||||
RUNTIME_DEPENDENCIES
|
||||
AssetProcessor
|
||||
AutomatedTesting.Assets
|
||||
|
||||
@@ -27,6 +27,7 @@ TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "atom_hydra_scripts")
|
||||
@pytest.mark.parametrize("level", ["auto_test"])
|
||||
class TestAtomEditorComponentsMain(object):
|
||||
|
||||
@pytest.mark.xfail(reason="Timing out sporadically, LYN-3956")
|
||||
@pytest.mark.test_case_id(
|
||||
"C32078130", # Display Mapper
|
||||
"C32078129", # Light
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:43b1a23b62fe2ffa05545ac99524f40b6fff49d6e35925b9d6138c00d8082e86
|
||||
size 9073
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
<download name="ClientAuthPasswordSignIn" type="Map">
|
||||
<index src="filelist.xml" dest="filelist.xml"/>
|
||||
<files>
|
||||
<file src="level.pak" dest="level.pak" size="DE5" md5="29bd2aae87e99a8fd026fe0cf8156071"/>
|
||||
</files>
|
||||
</download>
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f583e0b1b7016a11583383e6c6fcd29f9e796c1a9cd4b6ddb10f7dc91deec17a
|
||||
size 3557
|
||||
@@ -0,0 +1,12 @@
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3b948461412d201b3a80abafa60e916f860e46e28109333fbd263a2d5fc53c5a
|
||||
size 9103
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
<download name="ClientAuthPasswordSignUp" type="Map">
|
||||
<index src="filelist.xml" dest="filelist.xml"/>
|
||||
<files>
|
||||
<file src="level.pak" dest="level.pak" size="DE1" md5="0795859fc4ac009a50dd08ffabb03dec"/>
|
||||
</files>
|
||||
</download>
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8cdb456f6eb348be27249d80e9d2262e1e0bdabf2c1ff02c1a64a5609dcd823c
|
||||
size 3553
|
||||
@@ -0,0 +1,12 @@
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
0,0,0,0,0,0
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"AWS":
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -193,13 +193,12 @@ namespace AzFramework
|
||||
bool handling = false;
|
||||
for (auto& cameraInput : m_activeCameraInputs)
|
||||
{
|
||||
cameraInput->HandleEvents(event, cursorDelta, scrollDelta);
|
||||
handling = !cameraInput->Idle() || handling;
|
||||
handling = cameraInput->HandleEvents(event, cursorDelta, scrollDelta) || handling;
|
||||
}
|
||||
|
||||
for (auto& cameraInput : m_idleCameraInputs)
|
||||
{
|
||||
cameraInput->HandleEvents(event, cursorDelta, scrollDelta);
|
||||
handling = cameraInput->HandleEvents(event, cursorDelta, scrollDelta) || handling;
|
||||
}
|
||||
|
||||
return handling;
|
||||
@@ -262,17 +261,26 @@ namespace AzFramework
|
||||
{
|
||||
m_activeCameraInputs[i]->Reset();
|
||||
m_idleCameraInputs.push_back(m_activeCameraInputs[i]);
|
||||
m_activeCameraInputs[i] = m_activeCameraInputs[m_activeCameraInputs.size() - 1];
|
||||
using AZStd::swap;
|
||||
swap(m_activeCameraInputs[i], m_activeCameraInputs[m_activeCameraInputs.size() - 1]);
|
||||
m_activeCameraInputs.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void Cameras::Clear()
|
||||
{
|
||||
Reset();
|
||||
AZ_Assert(m_activeCameraInputs.empty(), "Active Camera Inputs is not empty");
|
||||
|
||||
m_idleCameraInputs.clear();
|
||||
}
|
||||
|
||||
RotateCameraInput::RotateCameraInput(const InputChannelId rotateChannelId)
|
||||
: m_rotateChannelId(rotateChannelId)
|
||||
{
|
||||
}
|
||||
|
||||
void RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
bool RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
const ClickDetector::ClickEvent clickEvent = [&event, this] {
|
||||
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
|
||||
@@ -304,6 +312,11 @@ namespace AzFramework
|
||||
// noop
|
||||
break;
|
||||
}
|
||||
|
||||
// note - must also check !ending to ensure the mouse up (release) event
|
||||
// is not consumed and can be propagated to other systems.
|
||||
// (don't swallow mouse up events)
|
||||
return !Idle() && !Ending();
|
||||
}
|
||||
|
||||
Camera RotateCameraInput::StepCamera(
|
||||
@@ -330,7 +343,7 @@ namespace AzFramework
|
||||
{
|
||||
}
|
||||
|
||||
void PanCameraInput::HandleEvents(
|
||||
bool PanCameraInput::HandleEvents(
|
||||
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
|
||||
@@ -347,6 +360,8 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !Idle();
|
||||
}
|
||||
|
||||
Camera PanCameraInput::StepCamera(
|
||||
@@ -411,7 +426,7 @@ namespace AzFramework
|
||||
{
|
||||
}
|
||||
|
||||
void TranslateCameraInput::HandleEvents(
|
||||
bool TranslateCameraInput::HandleEvents(
|
||||
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
|
||||
@@ -429,7 +444,8 @@ namespace AzFramework
|
||||
m_boost = true;
|
||||
}
|
||||
}
|
||||
else if (input->m_state == InputChannel::State::Ended)
|
||||
// ensure we don't process end events in the idle state
|
||||
else if (input->m_state == InputChannel::State::Ended && !Idle())
|
||||
{
|
||||
m_translation &= ~(translationFromKey(input->m_channelId));
|
||||
if (m_translation == TranslationType::Nil)
|
||||
@@ -442,6 +458,8 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !Idle();
|
||||
}
|
||||
|
||||
Camera TranslateCameraInput::StepCamera(
|
||||
@@ -503,7 +521,7 @@ namespace AzFramework
|
||||
m_boost = false;
|
||||
}
|
||||
|
||||
void OrbitCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta)
|
||||
bool OrbitCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta)
|
||||
{
|
||||
if (const auto* input = AZStd::get_if<DiscreteInputEvent>(&event))
|
||||
{
|
||||
@@ -522,8 +540,10 @@ namespace AzFramework
|
||||
|
||||
if (Active())
|
||||
{
|
||||
m_orbitCameras.HandleEvents(event, cursorDelta, scrollDelta);
|
||||
return m_orbitCameras.HandleEvents(event, cursorDelta, scrollDelta);
|
||||
}
|
||||
|
||||
return !Idle();
|
||||
}
|
||||
|
||||
Camera OrbitCameraInput::StepCamera(
|
||||
@@ -533,7 +553,7 @@ namespace AzFramework
|
||||
|
||||
if (Beginning())
|
||||
{
|
||||
const auto hasLookAt = [&nextCamera, &targetCamera, lookAtFn = m_lookAtFn] {
|
||||
const auto hasLookAt = [&nextCamera, &targetCamera, &lookAtFn = m_lookAtFn] {
|
||||
if (lookAtFn)
|
||||
{
|
||||
if (const auto lookAt = lookAtFn())
|
||||
@@ -585,13 +605,15 @@ namespace AzFramework
|
||||
return nextCamera;
|
||||
}
|
||||
|
||||
void OrbitDollyScrollCameraInput::HandleEvents(
|
||||
bool OrbitDollyScrollCameraInput::HandleEvents(
|
||||
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
|
||||
{
|
||||
BeginActivation();
|
||||
}
|
||||
|
||||
return !Idle();
|
||||
}
|
||||
|
||||
Camera OrbitDollyScrollCameraInput::StepCamera(
|
||||
@@ -609,7 +631,7 @@ namespace AzFramework
|
||||
{
|
||||
}
|
||||
|
||||
void OrbitDollyCursorMoveCameraInput::HandleEvents(
|
||||
bool OrbitDollyCursorMoveCameraInput::HandleEvents(
|
||||
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
|
||||
@@ -626,6 +648,8 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !Idle();
|
||||
}
|
||||
|
||||
Camera OrbitDollyCursorMoveCameraInput::StepCamera(
|
||||
@@ -637,13 +661,15 @@ namespace AzFramework
|
||||
return nextCamera;
|
||||
}
|
||||
|
||||
void ScrollTranslationCameraInput::HandleEvents(
|
||||
bool ScrollTranslationCameraInput::HandleEvents(
|
||||
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
|
||||
{
|
||||
BeginActivation();
|
||||
}
|
||||
|
||||
return !Idle();
|
||||
}
|
||||
|
||||
Camera ScrollTranslationCameraInput::StepCamera(
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace AzFramework
|
||||
ResetImpl();
|
||||
}
|
||||
|
||||
virtual void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) = 0;
|
||||
virtual bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) = 0;
|
||||
virtual Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) = 0;
|
||||
|
||||
virtual bool Exclusive() const
|
||||
@@ -171,16 +171,29 @@ namespace AzFramework
|
||||
class Cameras
|
||||
{
|
||||
public:
|
||||
void AddCamera(AZStd::shared_ptr<CameraInput> cameraInput);
|
||||
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta);
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime);
|
||||
|
||||
void AddCamera(AZStd::shared_ptr<CameraInput> cameraInput);
|
||||
//! Reset the state of all cameras.
|
||||
void Reset();
|
||||
//! Remove all cameras that were added.
|
||||
void Clear();
|
||||
//! Is one of the cameras in the active camera inputs marked as 'exclusive'.
|
||||
//! @note This implies no other sibling cameras can begin while the exclusive camera is running.
|
||||
bool Exclusive() const;
|
||||
|
||||
private:
|
||||
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_activeCameraInputs;
|
||||
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_idleCameraInputs;
|
||||
};
|
||||
|
||||
inline bool Cameras::Exclusive() const
|
||||
{
|
||||
return AZStd::any_of(
|
||||
m_activeCameraInputs.begin(), m_activeCameraInputs.end(), [](const auto& cameraInput) { return cameraInput->Exclusive(); });
|
||||
}
|
||||
|
||||
class CameraSystem
|
||||
{
|
||||
public:
|
||||
@@ -200,7 +213,7 @@ namespace AzFramework
|
||||
explicit RotateCameraInput(InputChannelId rotateChannelId);
|
||||
|
||||
// CameraInput overrides ...
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
|
||||
private:
|
||||
@@ -241,7 +254,7 @@ namespace AzFramework
|
||||
PanCameraInput(InputChannelId panChannelId, PanAxesFn panAxesFn);
|
||||
|
||||
// CameraInput overrides ...
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
|
||||
private:
|
||||
@@ -282,7 +295,7 @@ namespace AzFramework
|
||||
explicit TranslateCameraInput(TranslationAxesFn translationAxesFn);
|
||||
|
||||
// CameraInput overrides ...
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
void ResetImpl() override;
|
||||
|
||||
@@ -352,7 +365,7 @@ namespace AzFramework
|
||||
{
|
||||
public:
|
||||
// CameraInput overrides ...
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
};
|
||||
|
||||
@@ -362,7 +375,7 @@ namespace AzFramework
|
||||
explicit OrbitDollyCursorMoveCameraInput(InputChannelId dollyChannelId);
|
||||
|
||||
// CameraInput overrides ...
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
|
||||
private:
|
||||
@@ -373,7 +386,7 @@ namespace AzFramework
|
||||
{
|
||||
public:
|
||||
// CameraInput overrides ...
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
};
|
||||
|
||||
@@ -383,7 +396,7 @@ namespace AzFramework
|
||||
using LookAtFn = AZStd::function<AZStd::optional<AZ::Vector3>()>;
|
||||
|
||||
// CameraInput overrides ...
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
bool Exclusive() const override;
|
||||
|
||||
|
||||
@@ -17,6 +17,17 @@ namespace AzFramework
|
||||
{
|
||||
ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta)
|
||||
{
|
||||
const auto previousDetectionState = m_detectionState;
|
||||
if (previousDetectionState == DetectionState::WaitingForMove)
|
||||
{
|
||||
// only allow the action to begin if the mouse has been moved a small amount
|
||||
m_moveAccumulator += ScreenVectorLength(cursorDelta);
|
||||
if (m_moveAccumulator > m_deadZone)
|
||||
{
|
||||
m_detectionState = DetectionState::Moved;
|
||||
}
|
||||
}
|
||||
|
||||
if (clickEvent == ClickEvent::Down)
|
||||
{
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
@@ -52,15 +63,9 @@ namespace AzFramework
|
||||
return clickOutcome;
|
||||
}
|
||||
|
||||
if (m_detectionState == DetectionState::WaitingForMove)
|
||||
if (previousDetectionState == DetectionState::WaitingForMove && m_detectionState == DetectionState::Moved)
|
||||
{
|
||||
// only allow the action to begin if the mouse has been moved a small amount
|
||||
m_moveAccumulator += ScreenVectorLength(cursorDelta);
|
||||
if (m_moveAccumulator > m_deadZone)
|
||||
{
|
||||
m_detectionState = DetectionState::Moved;
|
||||
return ClickOutcome::Move;
|
||||
}
|
||||
return ClickOutcome::Move;
|
||||
}
|
||||
|
||||
return ClickOutcome::Nil;
|
||||
|
||||
@@ -50,7 +50,11 @@ namespace AzFramework
|
||||
//! Called from any type of 'handle event' function.
|
||||
ClickOutcome DetectClick(ClickEvent clickEvent, const ScreenVector& cursorDelta);
|
||||
|
||||
//! Override the default double click interval.
|
||||
//! @note Default is 400ms - system default.
|
||||
void SetDoubleClickInterval(float doubleClickInterval);
|
||||
//! Override the dead zone before a 'move' outcome will be triggered.
|
||||
void SetDeadZone(float deadZone);
|
||||
|
||||
private:
|
||||
//! Internal state of ClickDetector based on incoming events.
|
||||
@@ -72,4 +76,9 @@ namespace AzFramework
|
||||
{
|
||||
m_doubleClickInterval = doubleClickInterval;
|
||||
}
|
||||
|
||||
inline void ClickDetector::SetDeadZone(const float deadZone)
|
||||
{
|
||||
m_deadZone = deadZone;
|
||||
}
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -84,6 +84,7 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::vector<AZ::Entity*> entities;
|
||||
AZStd::vector<AZStd::unique_ptr<Instance>> instances;
|
||||
AZStd::unordered_map<Instance*, PrefabDom> nestedInstanceLinkPatchesMap;
|
||||
|
||||
// Retrieve all entities affected and identify Instances
|
||||
if (!RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonRootEntityOwningInstance->get(), entities, instances))
|
||||
@@ -96,6 +97,16 @@ namespace AzToolsFramework
|
||||
// target templates of the other instances.
|
||||
for (auto& nestedInstance : instances)
|
||||
{
|
||||
auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstance->GetLinkId());
|
||||
|
||||
if (linkRef.has_value())
|
||||
{
|
||||
PrefabDom oldLinkPatches;
|
||||
oldLinkPatches.CopyFrom(linkRef->get().GetLinkDom(), oldLinkPatches.GetAllocator());
|
||||
|
||||
nestedInstanceLinkPatchesMap.emplace(nestedInstance.get(), AZStd::move(oldLinkPatches));
|
||||
}
|
||||
|
||||
RemoveLink(nestedInstance, commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch());
|
||||
}
|
||||
|
||||
@@ -122,6 +133,9 @@ namespace AzToolsFramework
|
||||
|
||||
AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId();
|
||||
|
||||
// Apply the correct transform to the container for the new instance, and store the patch for use when creating the link.
|
||||
PrefabDom patch = ApplyContainerTransformAndGeneratePatch(containerEntityId, commonRootEntityId, topLevelEntities);
|
||||
|
||||
// Parent the non-container top level entities to the container entity.
|
||||
// Parenting the top level container entities will be done during the creation of links.
|
||||
for (AZ::Entity* topLevelEntity : topLevelEntities)
|
||||
@@ -141,35 +155,55 @@ namespace AzToolsFramework
|
||||
|
||||
instanceToCreate->get().GetNestedInstances([&](AZStd::unique_ptr<Instance>& nestedInstance) {
|
||||
AZ_Assert(nestedInstance, "Invalid nested instance found in the new prefab created.");
|
||||
|
||||
EntityOptionalReference nestedInstanceContainerEntity = nestedInstance->GetContainerEntity();
|
||||
AZ_Assert(
|
||||
nestedInstanceContainerEntity, "Invalid container entity found for the nested instance used in prefab creation.");
|
||||
|
||||
AZ::EntityId parentId;
|
||||
AZ::TransformBus::EventResult(
|
||||
parentId, nestedInstanceContainerEntity->get().GetId(), &AZ::TransformBus::Events::GetParentId);
|
||||
AZ::EntityId nestedInstanceContainerEntityId = nestedInstanceContainerEntity->get().GetId();
|
||||
PrefabDom previousPatch;
|
||||
|
||||
auto entityIterator = AZStd::find_if(
|
||||
entities.begin(), entities.end(), [parentId](AZ::Entity* entity) { return entity->GetId() == parentId; });
|
||||
|
||||
// If the previous parent entity of the nested instance is not part of the entities of the newly created prefab,
|
||||
// then set the parent of the nested prefab as the container entity of the newly created prefab.
|
||||
if (entityIterator == entities.end())
|
||||
// Retrieve the previous patch if it exists
|
||||
if (nestedInstanceLinkPatchesMap.contains(nestedInstance.get()))
|
||||
{
|
||||
parentId = containerEntityId;
|
||||
previousPatch = AZStd::move(nestedInstanceLinkPatchesMap[nestedInstance.get()]);
|
||||
}
|
||||
|
||||
// These link creations shouldn't be undone because that would put the template in a non-usable state if a user
|
||||
// chooses to instantiate the template after undoing the creation.
|
||||
CreateLink(
|
||||
{&nestedInstanceContainerEntity->get()}, *nestedInstance, instanceToCreate->get().GetTemplateId(),
|
||||
undoBatch.GetUndoBatch(), parentId, false);
|
||||
CreateLink(*nestedInstance, instanceToCreate->get().GetTemplateId(), undoBatch.GetUndoBatch(), AZStd::move(previousPatch), false);
|
||||
|
||||
// If this nested instance's container is a top level entity in the new prefab, re-parent it and apply the change.
|
||||
if (AZStd::find(topLevelEntities.begin(), topLevelEntities.end(), &nestedInstanceContainerEntity->get()) != topLevelEntities.end())
|
||||
{
|
||||
Prefab::PrefabDom containerEntityDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *nestedInstanceContainerEntity);
|
||||
|
||||
AZ::TransformBus::Event(nestedInstanceContainerEntityId, &AZ::TransformBus::Events::SetParent, containerEntityId);
|
||||
|
||||
PrefabDom containerEntityDomAfter;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *nestedInstanceContainerEntity);
|
||||
|
||||
PrefabDom reparentPatch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(reparentPatch, containerEntityDomBefore, containerEntityDomAfter);
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(reparentPatch, nestedInstanceContainerEntityId);
|
||||
|
||||
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes as a separate step
|
||||
m_prefabUndoCache.Store(nestedInstanceContainerEntityId, AZStd::move(containerEntityDomAfter));
|
||||
|
||||
// Save these changes as patches to the link
|
||||
PrefabUndoLinkUpdate* linkUpdate = aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast<AZ::u64>(nestedInstanceContainerEntityId)));
|
||||
linkUpdate->SetParent(undoBatch.GetUndoBatch());
|
||||
linkUpdate->Capture(reparentPatch, nestedInstance->GetLinkId());
|
||||
|
||||
linkUpdate->Redo();
|
||||
}
|
||||
});
|
||||
|
||||
// Create a link between the templates of the newly created instance and the instance it's being parented under.
|
||||
CreateLink(
|
||||
topLevelEntities, instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(),
|
||||
undoBatch.GetUndoBatch(), commonRootEntityId);
|
||||
instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch(),
|
||||
AZStd::move(patch));
|
||||
|
||||
for (AZ::Entity* topLevelEntity : topLevelEntities)
|
||||
{
|
||||
@@ -199,6 +233,40 @@ namespace AzToolsFramework
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
PrefabDom PrefabPublicHandler::ApplyContainerTransformAndGeneratePatch(AZ::EntityId containerEntityId, AZ::EntityId parentEntityId, const EntityList& childEntities)
|
||||
{
|
||||
AZ::Entity* containerEntity = GetEntityById(containerEntityId);
|
||||
AZ_Assert(containerEntity, "Invalid container entity passed to ApplyContainerTransformAndGeneratePatch.");
|
||||
|
||||
// Generate the transform for the container entity out of the top level entities, and set it
|
||||
// This step needs to be done before anything is parented to the container, else children position will be wrong
|
||||
Prefab::PrefabDom containerEntityDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity);
|
||||
|
||||
AZ::Vector3 containerEntityTranslation(AZ::Vector3::CreateZero());
|
||||
AZ::Quaternion containerEntityRotation(AZ::Quaternion::CreateZero());
|
||||
|
||||
// Set container entity to be child of common root
|
||||
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, parentEntityId);
|
||||
|
||||
// Set the transform (translation, rotation) of the container entity
|
||||
GenerateContainerEntityTransform(childEntities, containerEntityTranslation, containerEntityRotation);
|
||||
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalTranslation, containerEntityTranslation);
|
||||
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, containerEntityRotation);
|
||||
|
||||
PrefabDom containerEntityDomAfter;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity);
|
||||
|
||||
PrefabDom patch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter);
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
|
||||
|
||||
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes
|
||||
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
|
||||
|
||||
return AZStd::move(patch);
|
||||
}
|
||||
|
||||
PrefabOperationResult PrefabPublicHandler::InstantiatePrefab(
|
||||
AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position)
|
||||
{
|
||||
@@ -249,10 +317,10 @@ namespace AzToolsFramework
|
||||
// Initialize Undo Batch object
|
||||
ScopedUndoBatch undoBatch("Instantiate Prefab");
|
||||
|
||||
// Instantiate the Prefab
|
||||
PrefabDom instanceToParentUnderDomBeforeCreate;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceToParentUnderDomBeforeCreate, instanceToParentUnder->get());
|
||||
|
||||
// Instantiate the Prefab
|
||||
auto instanceToCreate = prefabEditorEntityOwnershipInterface->InstantiatePrefab(relativePath, instanceToParentUnder);
|
||||
|
||||
if (!instanceToCreate)
|
||||
@@ -264,11 +332,32 @@ namespace AzToolsFramework
|
||||
PrefabUndoHelpers::UpdatePrefabInstance(
|
||||
instanceToParentUnder->get(), "Update prefab instance", instanceToParentUnderDomBeforeCreate, undoBatch.GetUndoBatch());
|
||||
|
||||
CreateLink({}, instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), parent);
|
||||
// Create Link with correct container patches
|
||||
AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId();
|
||||
AZ::Entity* containerEntity = GetEntityById(containerEntityId);
|
||||
AZ_Assert(containerEntity, "Invalid container entity detected in InstantiatePrefab.");
|
||||
|
||||
// Apply position
|
||||
Prefab::PrefabDom containerEntityDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity);
|
||||
|
||||
// Set container entity's parent
|
||||
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, parent);
|
||||
|
||||
// Set the position of the container entity
|
||||
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetWorldTranslation, position);
|
||||
|
||||
PrefabDom containerEntityDomAfter;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity);
|
||||
|
||||
// Generate patch to be stored in the link
|
||||
PrefabDom patch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter);
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
|
||||
|
||||
CreateLink(instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), AZStd::move(patch));
|
||||
|
||||
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes
|
||||
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
@@ -335,33 +424,9 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::CreateLink(
|
||||
const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId,
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId, const bool isUndoRedoSupportNeeded)
|
||||
Instance& sourceInstance, TemplateId targetTemplateId,
|
||||
UndoSystem::URSequencePoint* undoBatch, PrefabDom patch, const bool isUndoRedoSupportNeeded)
|
||||
{
|
||||
AZ::EntityId containerEntityId = sourceInstance.GetContainerEntityId();
|
||||
AZ::Entity* containerEntity = GetEntityById(containerEntityId);
|
||||
Prefab::PrefabDom containerEntityDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity);
|
||||
|
||||
AZ::Vector3 containerEntityTranslation(AZ::Vector3::CreateZero());
|
||||
AZ::Quaternion containerEntityRotation(AZ::Quaternion::CreateZero());
|
||||
|
||||
// Set the transform (translation, rotation) of the container entity
|
||||
GenerateContainerEntityTransform(topLevelEntities, containerEntityTranslation, containerEntityRotation);
|
||||
|
||||
// Set container entity to be child of common root
|
||||
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, commonRootEntityId);
|
||||
|
||||
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalTranslation, containerEntityTranslation);
|
||||
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, containerEntityRotation);
|
||||
|
||||
PrefabDom containerEntityDomAfter;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity);
|
||||
|
||||
PrefabDom patch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter);
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
|
||||
|
||||
LinkId linkId;
|
||||
if (isUndoRedoSupportNeeded)
|
||||
{
|
||||
@@ -377,9 +442,6 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
sourceInstance.SetLinkId(linkId);
|
||||
|
||||
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes
|
||||
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::RemoveLink(
|
||||
|
||||
@@ -68,20 +68,32 @@ namespace AzToolsFramework
|
||||
|
||||
InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const;
|
||||
bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const;
|
||||
|
||||
/**
|
||||
* Applies the correct transform changes to the container entity based on the parent and child entities provided, and returns an appropriate patch.
|
||||
* The container will be parented to parentId, moved to the average transform of the future direct children and its cache will be updated.
|
||||
* This helper function won't support undo/redo, update the templates or create any links. All that needs to be done by the caller.
|
||||
*
|
||||
* \param containerEntityId The container to apply the changes to.
|
||||
* \param parentEntityId The id of the entity the container should be parented to.
|
||||
* \param childEntities A list of entities that will subsequently be parented to this container.
|
||||
* \return The PrefabDom containing the patches that should be stored in the parent link.
|
||||
*/
|
||||
PrefabDom ApplyContainerTransformAndGeneratePatch(
|
||||
AZ::EntityId containerEntityId, AZ::EntityId parentEntityId, const EntityList& childEntities);
|
||||
|
||||
/**
|
||||
* Creates a link between the templates of an instance and its parent.
|
||||
*
|
||||
* \param topLevelEntities The list of entities that are immediate children to the container entity of the instance.
|
||||
* \param sourceInstance The instance that corresponds to the source template of the link.
|
||||
* \param targetInstance The id of the target template.
|
||||
* \param undoBatch The undo batch to set as parent for this create link action.
|
||||
* \param commonRootEntityId The id of the entity that the source instance should be parented under.
|
||||
* \param patch The patch to store in the newly created link dom.
|
||||
* \param isUndoRedoSupportNeeded The flag indicating whether the link should be created with undo/redo support or not.
|
||||
*/
|
||||
void CreateLink(
|
||||
const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId,
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId, const bool isUndoRedoSupportNeeded = true);
|
||||
Instance& sourceInstance, TemplateId targetTemplateId, UndoSystem::URSequencePoint* undoBatch,
|
||||
PrefabDom patch, const bool isUndoRedoSupportNeeded = true);
|
||||
|
||||
/**
|
||||
* Removes the link between template of the sourceInstance and the template corresponding to targetTemplateId.
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
#include <AzFramework/Viewport/CameraState.h>
|
||||
#include <AzFramework/Viewport/ViewportId.h>
|
||||
#include <AzFramework/Viewport/ClickDetector.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportTypes.h>
|
||||
|
||||
@@ -304,4 +305,24 @@ namespace AzToolsFramework
|
||||
|
||||
return entityContextId;
|
||||
}
|
||||
|
||||
//! Maps a mouse interaction event to a ClickDetector event.
|
||||
//! @note Function only cares about up or down events, all other events are mapped to Nil (ignored).
|
||||
inline AzFramework::ClickDetector::ClickEvent ClickDetectorEventFromViewportInteraction(
|
||||
const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
{
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left())
|
||||
{
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down)
|
||||
{
|
||||
return AzFramework::ClickDetector::ClickEvent::Down;
|
||||
}
|
||||
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up)
|
||||
{
|
||||
return AzFramework::ClickDetector::ClickEvent::Up;
|
||||
}
|
||||
}
|
||||
return AzFramework::ClickDetector::ClickEvent::Nil;
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+9
-4
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
@@ -27,8 +28,11 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down)
|
||||
m_cursorState.SetCurrentPosition(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates);
|
||||
|
||||
const auto selectClickEvent = ClickDetectorEventFromViewportInteraction(mouseInteraction);
|
||||
const auto clickOutcome = m_clickDetector.DetectClick(selectClickEvent, m_cursorState.CursorDelta());
|
||||
if (clickOutcome == AzFramework::ClickDetector::ClickOutcome::Move)
|
||||
{
|
||||
if (m_leftMouseDown)
|
||||
{
|
||||
@@ -58,8 +62,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up)
|
||||
if (clickOutcome == AzFramework::ClickDetector::ClickOutcome::Release)
|
||||
{
|
||||
if (m_leftMouseUp)
|
||||
{
|
||||
@@ -77,6 +80,8 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
m_cursorState.Update();
|
||||
|
||||
if (m_boxSelectRegion)
|
||||
{
|
||||
debugDisplay.DepthTestOff();
|
||||
|
||||
+21
-17
@@ -14,6 +14,8 @@
|
||||
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzFramework/Viewport/ClickDetector.h>
|
||||
#include <AzFramework/Viewport/CursorState.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportTypes.h>
|
||||
|
||||
#include <QRect>
|
||||
@@ -26,49 +28,49 @@ namespace AzFramework
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
/// Utility to provide box select (click and drag) support for viewport types.
|
||||
/// Users can override the mouse event callbacks and display scene function to customize behavior.
|
||||
//! Utility to provide box select (click and drag) support for viewport types.
|
||||
//! Users can override the mouse event callbacks and display scene function to customize behavior.
|
||||
class EditorBoxSelect
|
||||
{
|
||||
public:
|
||||
EditorBoxSelect() = default;
|
||||
|
||||
/// Return if a box select action is currently taking place.
|
||||
//! Return if a box select action is currently taking place.
|
||||
bool Active() const { return m_boxSelectRegion.has_value(); }
|
||||
|
||||
/// Update the box select for various mouse events.
|
||||
/// Call HandleMouseInteraction from type/system implementing MouseViewportRequests interface.
|
||||
//! Update the box select for various mouse events.
|
||||
//! Call HandleMouseInteraction from type/system implementing MouseViewportRequests interface.
|
||||
void HandleMouseInteraction(
|
||||
const ViewportInteraction::MouseInteractionEvent& mouseInteraction);
|
||||
|
||||
/// Responsible for drawing the 2d box representing the selection in screen space.
|
||||
//! Responsible for drawing the 2d box representing the selection in screen space.
|
||||
void Display2d(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay);
|
||||
|
||||
/// Custom drawing behavior to happen during a box select.
|
||||
//! Custom drawing behavior to happen during a box select.
|
||||
void DisplayScene(
|
||||
const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay);
|
||||
|
||||
/// Set the left mouse down callback.
|
||||
//! Set the left mouse down callback.
|
||||
void InstallLeftMouseDown(
|
||||
const AZStd::function<void(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)>& leftMouseDown);
|
||||
/// Set the mouse move callback.
|
||||
//! Set the mouse move callback.
|
||||
void InstallMouseMove(
|
||||
const AZStd::function<void(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)>& mouseMove);
|
||||
/// Set the left mouse up callback.
|
||||
//! Set the left mouse up callback.
|
||||
void InstallLeftMouseUp(
|
||||
const AZStd::function<void()>& leftMouseUp);
|
||||
/// Set the display scene callback.
|
||||
//! Set the display scene callback.
|
||||
void InstallDisplayScene(
|
||||
const AZStd::function<void(
|
||||
const AzFramework::ViewportInfo& viewportInfo,
|
||||
AzFramework::DebugDisplayRequests& debugDisplay)>& displayScene);
|
||||
|
||||
/// Return the box select region.
|
||||
/// If a box selection is being made, return the current rectangle representing the area.
|
||||
/// If there is currently no active box select, then the Maybe type will be empty (there will be no region/area).
|
||||
//! Return the box select region.
|
||||
//! If a box selection is being made, return the current rectangle representing the area.
|
||||
//! If there is currently no active box select, then the Maybe type will be empty (there will be no region/area).
|
||||
const AZStd::optional<QRect>& BoxRegion() const { return m_boxSelectRegion; }
|
||||
|
||||
/// Return the active modifiers from the previous frame.
|
||||
//! Return the active modifiers from the previous frame.
|
||||
ViewportInteraction::KeyboardModifiers PreviousModifiers() const { return m_previousModifiers; }
|
||||
|
||||
private:
|
||||
@@ -79,7 +81,9 @@ namespace AzToolsFramework
|
||||
AZStd::function<void(
|
||||
const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)> m_displayScene;
|
||||
|
||||
AZStd::optional<QRect> m_boxSelectRegion; ///< Maybe/optional value to store box select region while active.
|
||||
ViewportInteraction::KeyboardModifiers m_previousModifiers; ///< Modifier keys active on the previous frame.
|
||||
AZStd::optional<QRect> m_boxSelectRegion; //!< Maybe/optional value to store box select region while active.
|
||||
ViewportInteraction::KeyboardModifiers m_previousModifiers; //!< Modifier keys active on the previous frame.
|
||||
AzFramework::ClickDetector m_clickDetector; //!< Utility type to detect if a mouse click or move has occurred.
|
||||
AzFramework::CursorState m_cursorState; //!< Utility type to track the current cursor position (and movement/delta).
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+1
-16
@@ -1782,22 +1782,7 @@ namespace AzToolsFramework
|
||||
|
||||
m_cachedEntityIdUnderCursor = m_editorHelpers->HandleMouseInteraction(cameraState, mouseInteraction);
|
||||
|
||||
const AzFramework::ClickDetector::ClickEvent selectClickEvent = [&mouseInteraction] {
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left())
|
||||
{
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down)
|
||||
{
|
||||
return AzFramework::ClickDetector::ClickEvent::Down;
|
||||
}
|
||||
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up)
|
||||
{
|
||||
return AzFramework::ClickDetector::ClickEvent::Up;
|
||||
}
|
||||
}
|
||||
return AzFramework::ClickDetector::ClickEvent::Nil;
|
||||
}();
|
||||
|
||||
const auto selectClickEvent = ClickDetectorEventFromViewportInteraction(mouseInteraction);
|
||||
m_cursorState.SetCurrentPosition(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates);
|
||||
const auto clickOutcome = m_clickDetector.DetectClick(selectClickEvent, m_cursorState.CursorDelta());
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
|
||||
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
#include <AzFramework/Viewport/CameraInput.h>
|
||||
#include <AzFramework/Windowing/WindowBus.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class CameraInputFixture : public AllocatorsTestFixture
|
||||
{
|
||||
public:
|
||||
AzFramework::Camera m_camera;
|
||||
AzFramework::Camera m_targetCamera;
|
||||
AZStd::shared_ptr<AzFramework::CameraSystem> m_cameraSystem;
|
||||
|
||||
bool HandleEventAndUpdate(const AzFramework::InputEvent& event)
|
||||
{
|
||||
constexpr float deltaTime = 0.01666f; // 60fps
|
||||
const bool consumed = m_cameraSystem->HandleEvents(event);
|
||||
m_camera = m_cameraSystem->StepCamera(m_targetCamera, deltaTime);
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
AllocatorsTestFixture::SetUp();
|
||||
|
||||
AzFramework::ReloadCameraKeyBindings();
|
||||
|
||||
m_cameraSystem = AZStd::make_shared<AzFramework::CameraSystem>();
|
||||
|
||||
auto firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Right);
|
||||
auto firstPersonTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::LookTranslation);
|
||||
|
||||
auto orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>();
|
||||
auto orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Left);
|
||||
auto orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::OrbitTranslation);
|
||||
|
||||
orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera);
|
||||
orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera);
|
||||
|
||||
m_cameraSystem->m_cameras.AddCamera(firstPersonRotateCamera);
|
||||
m_cameraSystem->m_cameras.AddCamera(firstPersonTranslateCamera);
|
||||
m_cameraSystem->m_cameras.AddCamera(orbitCamera);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_cameraSystem->m_cameras.Clear();
|
||||
m_cameraSystem.reset();
|
||||
|
||||
AllocatorsTestFixture::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(CameraInputFixture, BeginEndOrbitCameraConsumesCorrectEvents)
|
||||
{
|
||||
// set initial mouse position
|
||||
const bool consumed1 = HandleEventAndUpdate(AzFramework::CursorEvent{AzFramework::ScreenPoint(5, 5)});
|
||||
// begin orbit camera
|
||||
const bool consumed2 = HandleEventAndUpdate(
|
||||
AzFramework::DiscreteInputEvent{AzFramework::InputDeviceKeyboard::Key::ModifierAltL, AzFramework::InputChannel::State::Began});
|
||||
// begin listening for orbit rotate (click detector) - event is not consumed
|
||||
const bool consumed3 = HandleEventAndUpdate(
|
||||
AzFramework::DiscreteInputEvent{AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began});
|
||||
// begin orbit rotate (mouse has moved sufficient distance to initiate)
|
||||
const bool consumed4 = HandleEventAndUpdate(AzFramework::CursorEvent{AzFramework::ScreenPoint(10, 10)});
|
||||
// end orbit (mouse up) - event is not consumed
|
||||
const bool consumed5 = HandleEventAndUpdate(
|
||||
AzFramework::DiscreteInputEvent{AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Ended});
|
||||
|
||||
const auto allConsumed = AZStd::vector<bool>{consumed1, consumed2, consumed3, consumed4, consumed5};
|
||||
|
||||
using ::testing::ElementsAre;
|
||||
EXPECT_THAT(allConsumed, ElementsAre(false, true, false, true, false));
|
||||
}
|
||||
} // namespace UnitTest
|
||||
@@ -139,4 +139,21 @@ namespace UnitTest
|
||||
EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // ignored double click
|
||||
EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered
|
||||
}
|
||||
|
||||
// if the click detector registers a mouse down event, but then all intermediate calls are ignored
|
||||
// (another system may start intercepting events and swallowing them) then when we do receive a mouse
|
||||
// up event we should ensure we take into account the current delta - if the delta is large, then the
|
||||
// outcome will be release
|
||||
TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterIgnoringMouseMovesBeforeMouseUpWithLargeDelta)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
const ClickDetector::ClickOutcome downOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome upOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(50, 50));
|
||||
|
||||
EXPECT_THAT(downOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
EXPECT_THAT(upOutcome, Eq(ClickDetector::ClickOutcome::Release));
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -17,6 +17,7 @@ set(FILES
|
||||
BinToTextEncode.cpp
|
||||
ComponentAddRemove.cpp
|
||||
ComponentAdapterTests.cpp
|
||||
CameraInputTests.cpp
|
||||
ClickDetectorTests.cpp
|
||||
CursorStateTests.cpp
|
||||
EntityContext.cpp
|
||||
|
||||
@@ -97,17 +97,38 @@ namespace SandboxEditor
|
||||
AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
// should the camera system respond to this particular event
|
||||
static bool ShouldHandle(const AzFramework::ViewportControllerPriority priority, const bool exclusive)
|
||||
{
|
||||
// ModernViewportCameraControllerInstance receives events at all priorities, it should only respond
|
||||
// to normal priority events if it is not in 'exclusive' mode and when in 'exclusive' mode it should
|
||||
// only respond to the highest priority events
|
||||
return !exclusive && priority == AzFramework::ViewportControllerPriority::Normal ||
|
||||
exclusive && priority == AzFramework::ViewportControllerPriority::Highest;
|
||||
}
|
||||
|
||||
bool ModernViewportCameraControllerInstance::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event)
|
||||
{
|
||||
AzFramework::WindowSize windowSize;
|
||||
AzFramework::WindowRequestBus::EventResult(
|
||||
windowSize, event.m_windowHandle, &AzFramework::WindowRequestBus::Events::GetClientAreaSize);
|
||||
|
||||
return m_cameraSystem.HandleEvents(AzFramework::BuildInputEvent(event.m_inputChannel, windowSize));
|
||||
if (ShouldHandle(event.m_priority, m_cameraSystem.m_cameras.Exclusive()))
|
||||
{
|
||||
return m_cameraSystem.HandleEvents(AzFramework::BuildInputEvent(event.m_inputChannel, windowSize));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ModernViewportCameraControllerInstance::UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event)
|
||||
{
|
||||
// only update for a single priority (normal is the default)
|
||||
if (event.m_priority != AzFramework::ViewportControllerPriority::Normal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto viewportContext = RetrieveViewportContext(GetViewportId()))
|
||||
{
|
||||
m_updatingTransform = true;
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
namespace SandboxEditor
|
||||
{
|
||||
class ModernViewportCameraControllerInstance;
|
||||
class ModernViewportCameraController : public AzFramework::MultiViewportController<ModernViewportCameraControllerInstance>
|
||||
class ModernViewportCameraController
|
||||
: public AzFramework::MultiViewportController<
|
||||
ModernViewportCameraControllerInstance, AzFramework::ViewportControllerPriority::DispatchToAllPriorities>
|
||||
{
|
||||
public:
|
||||
using CameraListBuilder = AZStd::function<void(AzFramework::Cameras&)>;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.1359 15.3956L19.6728 7.8125L21.087 9.22671L12 18.5245L12 18.5245L2.86273 9.27696L4.27695 7.86275L12.1359 15.3956Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 286 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.2732 10.1288L19.8101 17.7119L21.2243 16.2977L12.1373 6.99994L12.1373 6.99995L3 16.2475L4.41421 17.6617L12.2732 10.1288Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 292 B |
@@ -58,13 +58,6 @@ namespace O3DE::ProjectManager
|
||||
|
||||
hLayout->addWidget(m_gemListView);
|
||||
hLayout->addWidget(m_gemInspector);
|
||||
|
||||
|
||||
// Select the first entry after everything got correctly sized
|
||||
QTimer::singleShot(100, [=]{
|
||||
QModelIndex firstModelIndex = m_gemListView->model()->index(0,0);
|
||||
m_gemListView->selectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect);
|
||||
});
|
||||
}
|
||||
|
||||
QVector<GemInfo> GemCatalogScreen::GenerateTestData()
|
||||
|
||||
@@ -32,21 +32,36 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
switch (platform)
|
||||
{
|
||||
case O3DE::ProjectManager::GemInfo::Android:
|
||||
case Android:
|
||||
return "Android";
|
||||
case O3DE::ProjectManager::GemInfo::iOS:
|
||||
case iOS:
|
||||
return "iOS";
|
||||
case O3DE::ProjectManager::GemInfo::Linux:
|
||||
case Linux:
|
||||
return "Linux";
|
||||
case O3DE::ProjectManager::GemInfo::macOS:
|
||||
case macOS:
|
||||
return "macOS";
|
||||
case O3DE::ProjectManager::GemInfo::Windows:
|
||||
case Windows:
|
||||
return "Windows";
|
||||
default:
|
||||
return "<Unknown Platform>";
|
||||
}
|
||||
}
|
||||
|
||||
QString GemInfo::GetTypeString(Type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Asset:
|
||||
return "Asset";
|
||||
case Code:
|
||||
return "Code";
|
||||
case Tool:
|
||||
return "Tool";
|
||||
default:
|
||||
return "<Unknown Type>";
|
||||
}
|
||||
}
|
||||
|
||||
bool GemInfo::IsPlatformSupported(Platform platform) const
|
||||
{
|
||||
return (m_platforms & platform);
|
||||
|
||||
@@ -36,6 +36,16 @@ namespace O3DE::ProjectManager
|
||||
Q_DECLARE_FLAGS(Platforms, Platform)
|
||||
static QString GetPlatformString(Platform platform);
|
||||
|
||||
enum Type
|
||||
{
|
||||
Asset = 1 << 0,
|
||||
Code = 1 << 1,
|
||||
Tool = 1 << 2,
|
||||
NumTypes = 3
|
||||
};
|
||||
Q_DECLARE_FLAGS(Types, Type)
|
||||
static QString GetTypeString(Type type);
|
||||
|
||||
GemInfo() = default;
|
||||
GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded);
|
||||
bool IsPlatformSupported(Platform platform) const;
|
||||
@@ -50,6 +60,7 @@ namespace O3DE::ProjectManager
|
||||
bool m_isAdded = false; //! Is the gem currently added and enabled in the project?
|
||||
QString m_summary;
|
||||
Platforms m_platforms;
|
||||
Types m_types; //! Asset and/or Code and/or Tool
|
||||
QStringList m_features;
|
||||
QString m_directoryLink;
|
||||
QString m_documentationLink;
|
||||
@@ -62,3 +73,4 @@ namespace O3DE::ProjectManager
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(O3DE::ProjectManager::GemInfo::Platforms)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(O3DE::ProjectManager::GemInfo::Types)
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "GemModel.h"
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <GemCatalog/GemModel.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -32,8 +33,11 @@ namespace O3DE::ProjectManager
|
||||
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
||||
|
||||
item->setData(gemInfo.m_name, RoleName);
|
||||
const QString uuidString = gemInfo.m_uuid.ToString<AZStd::string>().c_str();
|
||||
item->setData(uuidString, RoleUuid);
|
||||
item->setData(gemInfo.m_creator, RoleCreator);
|
||||
item->setData(static_cast<int>(gemInfo.m_platforms), RolePlatforms);
|
||||
item->setData(aznumeric_cast<int>(gemInfo.m_platforms), RolePlatforms);
|
||||
item->setData(aznumeric_cast<int>(gemInfo.m_types), RoleTypes);
|
||||
item->setData(gemInfo.m_summary, RoleSummary);
|
||||
item->setData(gemInfo.m_isAdded, RoleIsAdded);
|
||||
|
||||
@@ -48,6 +52,8 @@ namespace O3DE::ProjectManager
|
||||
item->setData(gemInfo.m_features, RoleFeatures);
|
||||
|
||||
appendRow(item);
|
||||
|
||||
m_uuidToNameMap[uuidString] = gemInfo.m_displayName;
|
||||
}
|
||||
|
||||
void GemModel::Clear()
|
||||
@@ -65,11 +71,21 @@ namespace O3DE::ProjectManager
|
||||
return modelIndex.data(RoleCreator).toString();
|
||||
}
|
||||
|
||||
QString GemModel::GetUuidString(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleUuid).toString();
|
||||
}
|
||||
|
||||
GemInfo::Platforms GemModel::GetPlatforms(const QModelIndex& modelIndex)
|
||||
{
|
||||
return static_cast<GemInfo::Platforms>(modelIndex.data(RolePlatforms).toInt());
|
||||
}
|
||||
|
||||
GemInfo::Types GemModel::GetTypes(const QModelIndex& modelIndex)
|
||||
{
|
||||
return static_cast<GemInfo::Types>(modelIndex.data(RoleTypes).toInt());
|
||||
}
|
||||
|
||||
QString GemModel::GetSummary(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleSummary).toString();
|
||||
@@ -90,9 +106,35 @@ namespace O3DE::ProjectManager
|
||||
return modelIndex.data(RoleDocLink).toString();
|
||||
}
|
||||
|
||||
AZ::Outcome<QString> GemModel::FindGemNameByUuidString(const QString& uuidString) const
|
||||
{
|
||||
const auto iterator = m_uuidToNameMap.find(uuidString);
|
||||
if (iterator != m_uuidToNameMap.end())
|
||||
{
|
||||
return AZ::Success(iterator.value());
|
||||
}
|
||||
|
||||
return AZ::Failure();
|
||||
}
|
||||
|
||||
QStringList GemModel::GetDependingGems(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleDependingGems).toStringList();
|
||||
QStringList result = modelIndex.data(RoleDependingGems).toStringList();
|
||||
if (result.isEmpty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
for (QString& dependingGemString : result)
|
||||
{
|
||||
AZ::Outcome<QString> gemNameOutcome = FindGemNameByUuidString(dependingGemString);
|
||||
if (gemNameOutcome.IsSuccess())
|
||||
{
|
||||
dependingGemString = gemNameOutcome.GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList GemModel::GetConflictingGems(const QModelIndex& modelIndex)
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include "GemInfo.h"
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <GemCatalog/GemInfo.h>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QStandardItemModel>
|
||||
#include <QItemSelectionModel>
|
||||
@@ -33,14 +34,18 @@ namespace O3DE::ProjectManager
|
||||
void AddGem(const GemInfo& gemInfo);
|
||||
void Clear();
|
||||
|
||||
AZ::Outcome<QString> FindGemNameByUuidString(const QString& uuidString) const;
|
||||
QStringList GetDependingGems(const QModelIndex& modelIndex);
|
||||
|
||||
static QString GetName(const QModelIndex& modelIndex);
|
||||
static QString GetCreator(const QModelIndex& modelIndex);
|
||||
static QString GetUuidString(const QModelIndex& modelIndex);
|
||||
static GemInfo::Platforms GetPlatforms(const QModelIndex& modelIndex);
|
||||
static GemInfo::Types GetTypes(const QModelIndex& modelIndex);
|
||||
static QString GetSummary(const QModelIndex& modelIndex);
|
||||
static bool IsAdded(const QModelIndex& modelIndex);
|
||||
static QString GetDirectoryLink(const QModelIndex& modelIndex);
|
||||
static QString GetDocLink(const QModelIndex& modelIndex);
|
||||
static QStringList GetDependingGems(const QModelIndex& modelIndex);
|
||||
static QStringList GetConflictingGems(const QModelIndex& modelIndex);
|
||||
static QString GetVersion(const QModelIndex& modelIndex);
|
||||
static QString GetLastUpdated(const QModelIndex& modelIndex);
|
||||
@@ -51,6 +56,7 @@ namespace O3DE::ProjectManager
|
||||
enum UserRole
|
||||
{
|
||||
RoleName = Qt::UserRole,
|
||||
RoleUuid,
|
||||
RoleCreator,
|
||||
RolePlatforms,
|
||||
RoleSummary,
|
||||
@@ -63,8 +69,10 @@ namespace O3DE::ProjectManager
|
||||
RoleLastUpdated,
|
||||
RoleBinarySize,
|
||||
RoleFeatures,
|
||||
RoleTypes
|
||||
};
|
||||
|
||||
QHash<QString, QString> m_uuidToNameMap;
|
||||
QItemSelectionModel* m_selectionModel = nullptr;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -27,7 +27,12 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void LinkLabel::mousePressEvent([[maybe_unused]] QMouseEvent* event)
|
||||
{
|
||||
QDesktopServices::openUrl(m_url);
|
||||
if (m_url.isValid())
|
||||
{
|
||||
QDesktopServices::openUrl(m_url);
|
||||
}
|
||||
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
void LinkLabel::enterEvent([[maybe_unused]] QEvent* event)
|
||||
|
||||
@@ -26,10 +26,16 @@ namespace O3DE::ProjectManager
|
||||
class LinkLabel
|
||||
: public QLabel
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
LinkLabel(const QString& text, const QUrl& url = {}, QWidget* parent = nullptr);
|
||||
LinkLabel(const QString& text = {}, const QUrl& url = {}, QWidget* parent = nullptr);
|
||||
|
||||
void SetUrl(const QUrl& url);
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
private:
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void enterEvent(QEvent* event) override;
|
||||
|
||||
@@ -426,7 +426,7 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
// required
|
||||
gemInfo.m_name = Py_To_String(data["Name"]);
|
||||
gemInfo.m_uuid = AZ::Uuid(Py_To_String(data["Uuid"]));
|
||||
gemInfo.m_uuid = AZ::Uuid(Py_To_String(data["Uuid"]));
|
||||
|
||||
// optional
|
||||
gemInfo.m_displayName = Py_To_String_Optional(data, "DisplayName", gemInfo.m_name);
|
||||
@@ -437,7 +437,8 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
for (auto dependency : data["Dependencies"])
|
||||
{
|
||||
gemInfo.m_dependingGemUuids.push_back(Py_To_String(dependency["Uuid"]));
|
||||
const AZ::Uuid uuid = Py_To_String(dependency["Uuid"]);
|
||||
gemInfo.m_dependingGemUuids.push_back(uuid.ToString<AZStd::string>().c_str());
|
||||
}
|
||||
}
|
||||
if (data.contains("Tags"))
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
<file>Resources/iOS.svg</file>
|
||||
<file>Resources/Linux.svg</file>
|
||||
<file>Resources/macOS.svg</file>
|
||||
<file>Resources/ArrowDownLine.svg</file>
|
||||
<file>Resources/ArrowUpLine.svg</file>
|
||||
<file>Resources/Backgrounds/FirstTimeBackgroundImage.jpg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -41,18 +41,6 @@ namespace AZ
|
||||
static AZ::SceneAPI::FbxSceneImporter::FbxImportRequestHandler* g_fbxImporter = nullptr;
|
||||
static AZStd::vector<AZ::ComponentDescriptor*> g_componentDescriptors;
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
// Currently it's still needed to explicitly create an instance of this instead of letting
|
||||
// it be a normal component. This is because ResourceCompilerScene needs to return
|
||||
// the list of available extensions before it can start the application.
|
||||
if (!g_fbxImporter)
|
||||
{
|
||||
g_fbxImporter = aznew AZ::SceneAPI::FbxSceneImporter::FbxImportRequestHandler();
|
||||
g_fbxImporter->Activate();
|
||||
}
|
||||
}
|
||||
|
||||
void Reflect(AZ::SerializeContext* /*context*/)
|
||||
{
|
||||
// Descriptor registration is done in Reflect instead of Initialize because the ResourceCompilerScene initializes the libraries before
|
||||
@@ -64,6 +52,7 @@ namespace AZ
|
||||
{
|
||||
// Global importer and behavior
|
||||
g_componentDescriptors.push_back(FbxSceneBuilder::FbxImporter::CreateDescriptor());
|
||||
g_componentDescriptors.push_back(FbxSceneImporter::FbxImportRequestHandler::CreateDescriptor());
|
||||
|
||||
// Node and attribute importers
|
||||
g_componentDescriptors.push_back(AssImpBitangentStreamImporter::CreateDescriptor());
|
||||
@@ -125,7 +114,6 @@ namespace AZ
|
||||
extern "C" AZ_DLL_EXPORT void InitializeDynamicModule(void* env)
|
||||
{
|
||||
AZ::Environment::Attach(static_cast<AZ::EnvironmentInstance>(env));
|
||||
AZ::SceneAPI::FbxSceneBuilder::Initialize();
|
||||
}
|
||||
extern "C" AZ_DLL_EXPORT void Reflect(AZ::SerializeContext* context)
|
||||
{
|
||||
|
||||
@@ -10,12 +10,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
#include <AzCore/Serialization/EditContextConstants.inl>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h>
|
||||
#include <SceneAPI/SceneCore/Containers/Scene.h>
|
||||
#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
|
||||
#include <SceneAPI/SceneCore/Events/ImportEventContext.h>
|
||||
#include <SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -23,10 +27,25 @@ namespace AZ
|
||||
{
|
||||
namespace FbxSceneImporter
|
||||
{
|
||||
const char* FbxImportRequestHandler::s_extension = ".fbx";
|
||||
void SceneImporterSettings::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext)
|
||||
{
|
||||
serializeContext->Class<SceneImporterSettings>()
|
||||
->Version(1)
|
||||
->Field("SupportedFileTypeExtensions", &SceneImporterSettings::m_supportedFileTypeExtensions);
|
||||
}
|
||||
}
|
||||
|
||||
void FbxImportRequestHandler::Activate()
|
||||
{
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
|
||||
if (settingsRegistry)
|
||||
{
|
||||
settingsRegistry->GetObject(m_settings, "/O3DE/SceneAPI/AssetImporter");
|
||||
}
|
||||
|
||||
BusConnect();
|
||||
}
|
||||
|
||||
@@ -37,21 +56,29 @@ namespace AZ
|
||||
|
||||
void FbxImportRequestHandler::Reflect(ReflectContext* context)
|
||||
{
|
||||
SceneImporterSettings::Reflect(context);
|
||||
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<FbxImportRequestHandler, SceneCore::BehaviorComponent>()->Version(1);
|
||||
serializeContext->Class<FbxImportRequestHandler, AZ::Component>()->Version(1)->Attribute(
|
||||
AZ::Edit::Attributes::SystemComponentTags,
|
||||
AZStd::vector<AZ::Crc32>({AssetBuilderSDK::ComponentTags::AssetBuilder}));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void FbxImportRequestHandler::GetSupportedFileExtensions(AZStd::unordered_set<AZStd::string>& extensions)
|
||||
{
|
||||
extensions.insert(s_extension);
|
||||
extensions.insert(m_settings.m_supportedFileTypeExtensions.begin(), m_settings.m_supportedFileTypeExtensions.end());
|
||||
}
|
||||
|
||||
Events::LoadingResult FbxImportRequestHandler::LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid, [[maybe_unused]] RequestingApplication requester)
|
||||
{
|
||||
if (!AzFramework::StringFunc::Path::IsExtension(path.c_str(), s_extension))
|
||||
AZStd::string extension;
|
||||
StringFunc::Path::GetExtension(path.c_str(), extension);
|
||||
|
||||
if (!m_settings.m_supportedFileTypeExtensions.contains(extension))
|
||||
{
|
||||
return Events::LoadingResult::Ignored;
|
||||
}
|
||||
@@ -73,6 +100,11 @@ namespace AZ
|
||||
return Events::LoadingResult::AssetFailure;
|
||||
}
|
||||
}
|
||||
|
||||
void FbxImportRequestHandler::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.emplace_back(AZ_CRC_CE("AssetImportRequestHandler"));
|
||||
}
|
||||
} // namespace Import
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
|
||||
@@ -21,12 +21,21 @@ namespace AZ
|
||||
{
|
||||
namespace FbxSceneImporter
|
||||
{
|
||||
struct SceneImporterSettings
|
||||
{
|
||||
AZ_TYPE_INFO(SceneImporterSettings, "{8BB6C7AD-BF99-44DC-9DA1-E7AD3F03DC10}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
AZStd::unordered_set<AZStd::string> m_supportedFileTypeExtensions;
|
||||
};
|
||||
|
||||
class FbxImportRequestHandler
|
||||
: public SceneCore::BehaviorComponent
|
||||
: public AZ::Component
|
||||
, public Events::AssetImportRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(FbxImportRequestHandler, "{9F4B189C-0A96-4F44-A5F0-E087FF1561F8}", SceneCore::BehaviorComponent);
|
||||
AZ_COMPONENT(FbxImportRequestHandler, "{9F4B189C-0A96-4F44-A5F0-E087FF1561F8}");
|
||||
|
||||
~FbxImportRequestHandler() override = default;
|
||||
|
||||
@@ -38,8 +47,13 @@ namespace AZ
|
||||
Events::LoadingResult LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid,
|
||||
RequestingApplication requester) override;
|
||||
|
||||
static void GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided);
|
||||
|
||||
private:
|
||||
static const char* s_extension;
|
||||
|
||||
SceneImporterSettings m_settings;
|
||||
|
||||
static constexpr const char* SettingsFilename = "AssetImporterSettings.json";
|
||||
};
|
||||
} // namespace FbxSceneImporter
|
||||
} // namespace SceneAPI
|
||||
|
||||
@@ -76,7 +76,7 @@ class CognitoUserPool:
|
||||
scope,
|
||||
'CognitoUserPoolId',
|
||||
description="Cognito User pool id",
|
||||
value=self._user_pool.attr_provider_name)
|
||||
value=self._user_pool.ref)
|
||||
|
||||
core.CfnOutput(
|
||||
scope,
|
||||
|
||||
+41
@@ -13,8 +13,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <Atom/ImageProcessing/ImageObject.h>
|
||||
|
||||
namespace AssetBuilderSDK
|
||||
{
|
||||
struct JobProduct;
|
||||
}
|
||||
|
||||
namespace ImageProcessingAtom
|
||||
{
|
||||
class ImageProcessingRequests
|
||||
@@ -35,4 +41,39 @@ namespace ImageProcessingAtom
|
||||
virtual IImageObjectPtr LoadImagePreview(const AZStd::string& filePath) = 0;
|
||||
};
|
||||
using ImageProcessingRequestBus = AZ::EBus<ImageProcessingRequests>;
|
||||
|
||||
class ImageBuilderRequests
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Create an image object
|
||||
virtual IImageObjectPtr CreateImage(
|
||||
AZ::u32 width,
|
||||
AZ::u32 height,
|
||||
AZ::u32 maxMipCount,
|
||||
EPixelFormat pixelFormat) = 0;
|
||||
|
||||
//! Convert an image and save its products to the specified folder
|
||||
virtual AZStd::vector<AssetBuilderSDK::JobProduct> ConvertImageObject(
|
||||
IImageObjectPtr imageObject,
|
||||
const AZStd::string& presetName,
|
||||
const AZStd::string& platformName,
|
||||
const AZStd::string& outputDir,
|
||||
const AZ::Data::AssetId& sourceAssetId,
|
||||
const AZStd::string& sourceAssetName) = 0;
|
||||
|
||||
//! Return whether the specified platform is supported by the image builder
|
||||
virtual bool DoesSupportPlatform(const AZStd::string& platformId) = 0;
|
||||
|
||||
//! Return whether the specified preset requires an image to be square and a power of 2
|
||||
virtual bool IsPresetFormatSquarePow2(const AZStd::string& presetName, const AZStd::string& platformName) = 0;
|
||||
};
|
||||
|
||||
using ImageBuilderRequestBus = AZ::EBus<ImageBuilderRequests>;
|
||||
} // namespace ImageProcessingAtom
|
||||
|
||||
-2
@@ -13,8 +13,6 @@
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
|
||||
class QString;
|
||||
|
||||
namespace ImageProcessingAtomEditor
|
||||
{
|
||||
class ImageProcessingEditorRequests
|
||||
|
||||
@@ -88,11 +88,13 @@ namespace ImageProcessingAtom
|
||||
m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler<AZ::RPI::StreamingImageAssetHandler>());
|
||||
|
||||
ImageProcessingRequestBus::Handler::BusConnect();
|
||||
ImageBuilderRequestBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
void BuilderPluginComponent::Deactivate()
|
||||
{
|
||||
ImageProcessingRequestBus::Handler::BusDisconnect();
|
||||
ImageBuilderRequestBus::Handler::BusDisconnect();
|
||||
m_imageBuilder.BusDisconnect();
|
||||
BuilderSettingManager::DestroyInstance();
|
||||
CPixelFormats::DestroyInstance();
|
||||
@@ -146,6 +148,78 @@ namespace ImageProcessingAtom
|
||||
return image;
|
||||
}
|
||||
|
||||
IImageObjectPtr BuilderPluginComponent::CreateImage(
|
||||
AZ::u32 width,
|
||||
AZ::u32 height,
|
||||
AZ::u32 maxMipCount,
|
||||
EPixelFormat pixelFormat)
|
||||
{
|
||||
IImageObjectPtr image(IImageObject::CreateImage(width, height, maxMipCount, pixelFormat));
|
||||
return image;
|
||||
}
|
||||
|
||||
AZStd::vector<AssetBuilderSDK::JobProduct> BuilderPluginComponent::ConvertImageObject(
|
||||
IImageObjectPtr imageObject,
|
||||
const AZStd::string& presetName,
|
||||
const AZStd::string& platformName,
|
||||
const AZStd::string& outputDir,
|
||||
const AZ::Data::AssetId& sourceAssetId,
|
||||
const AZStd::string& sourceAssetName)
|
||||
{
|
||||
AZStd::vector<AssetBuilderSDK::JobProduct> outProducts;
|
||||
|
||||
AZStd::string_view presetFilePath;
|
||||
const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(presetName, platformName, &presetFilePath);
|
||||
if (preset == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Cannot find preset with name %s.", presetName.c_str());
|
||||
return outProducts;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<ImageConvertProcessDescriptor> desc = AZStd::make_unique<ImageConvertProcessDescriptor>();
|
||||
TextureSettings& textureSettings = desc->m_textureSetting;
|
||||
textureSettings.m_preset = preset->m_uuid;
|
||||
desc->m_inputImage = imageObject;
|
||||
desc->m_presetSetting = *preset;
|
||||
desc->m_isPreview = false;
|
||||
desc->m_platform = platformName;
|
||||
desc->m_filePath = presetFilePath;
|
||||
desc->m_isStreaming = BuilderSettingManager::Instance()->GetBuilderSetting(platformName)->m_enableStreaming;
|
||||
desc->m_imageName = sourceAssetName;
|
||||
desc->m_outputFolder = outputDir;
|
||||
desc->m_sourceAssetId = sourceAssetId;
|
||||
|
||||
// Create an image convert process
|
||||
ImageConvertProcess process(AZStd::move(desc));
|
||||
process.ProcessAll();
|
||||
bool result = process.IsSucceed();
|
||||
if (result)
|
||||
{
|
||||
process.GetAppendOutputProducts(outProducts);
|
||||
}
|
||||
|
||||
return outProducts;
|
||||
}
|
||||
|
||||
bool BuilderPluginComponent::DoesSupportPlatform(const AZStd::string& platformId)
|
||||
{
|
||||
return ImageProcessingAtom::BuilderSettingManager::Instance()->DoesSupportPlatform(platformId);
|
||||
}
|
||||
|
||||
bool BuilderPluginComponent::IsPresetFormatSquarePow2(const AZStd::string& presetName, const AZStd::string& platformName)
|
||||
{
|
||||
AZStd::string_view filePath;
|
||||
const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(presetName, platformName, &filePath);
|
||||
if (preset == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Cannot find preset with name %s.", presetName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const PixelFormatInfo* info = CPixelFormats::GetInstance().GetPixelFormatInfo(preset->m_pixelFormat);
|
||||
return info->bSquarePow2;
|
||||
}
|
||||
|
||||
void ImageBuilderWorker::ShutDown()
|
||||
{
|
||||
// it is important to note that this will be called on a different thread than your process job thread
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace ImageProcessingAtom
|
||||
class BuilderPluginComponent
|
||||
: public AZ::Component
|
||||
, protected ImageProcessingRequestBus::Handler
|
||||
, protected ImageBuilderRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(BuilderPluginComponent, "{A227F803-D2E4-406E-93EC-121EF45A64A1}")
|
||||
@@ -71,6 +72,24 @@ namespace ImageProcessingAtom
|
||||
IImageObjectPtr LoadImagePreview(const AZStd::string& filePath) override;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// ImageBuilderRequestBus interface implementation
|
||||
IImageObjectPtr CreateImage(
|
||||
AZ::u32 width,
|
||||
AZ::u32 height,
|
||||
AZ::u32 maxMipCount,
|
||||
EPixelFormat pixelFormat) override;
|
||||
AZStd::vector<AssetBuilderSDK::JobProduct> ConvertImageObject(
|
||||
IImageObjectPtr imageObject,
|
||||
const AZStd::string& presetName,
|
||||
const AZStd::string& platformName,
|
||||
const AZStd::string& outputDir,
|
||||
const AZ::Data::AssetId& sourceAssetId,
|
||||
const AZStd::string& sourceAssetName) override;
|
||||
bool DoesSupportPlatform(const AZStd::string& platformId) override;
|
||||
bool IsPresetFormatSquarePow2(const AZStd::string& presetName, const AZStd::string& platformName) override;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private:
|
||||
BuilderPluginComponent(const BuilderPluginComponent&) = delete;
|
||||
|
||||
|
||||
@@ -185,7 +185,8 @@ namespace AZ
|
||||
// we can't use a temporary folder because CreateJobs API does not warrant side effects, and does not prepare a temp folder.
|
||||
// we can't use the OS temp folder anyway, because many includes (eg #include "../RPI/Shadow.h") are relative and will only work from the original location
|
||||
AZStd::string prependedPath = ShaderBuilderUtility::DumpAzslPrependedCode(
|
||||
BuilderName, prependedAzslSourceCode, originalLocation, ShaderBuilderUtility::ExtractStemName(fullPath.c_str()), shaderPlatformInterface->GetAPIName().GetStringView());
|
||||
BuilderName, prependedAzslSourceCode, originalLocation, ShaderBuilderUtility::ExtractStemName(fullPath.c_str()),
|
||||
shaderPlatformInterface->GetAPIName().GetStringView());
|
||||
// run mcpp
|
||||
PreprocessorData preprocessorData = PreprocessSource(prependedPath, fullPath, buildOptions.m_preprocessorSettings);
|
||||
jobDescriptor.m_jobParameters[(u32)JobParameterIndices::PreprocessorError] = preprocessorData.diagnostics; // save for ProcessJob
|
||||
@@ -221,7 +222,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
// eg: ("D:/p/x.a", "D:/p/x.b") -> yes
|
||||
static bool HasSameStemName(const AZStd::string& lhsPath, const AZStd::string& rhsPath)
|
||||
static bool HasSameFileName(const AZStd::string& lhsPath, const AZStd::string& rhsPath)
|
||||
{
|
||||
using namespace StringFunc::Path;
|
||||
AZStd::string stem1;
|
||||
@@ -307,7 +308,8 @@ namespace AZ
|
||||
buildOptions.m_compilerArguments.Merge(shaderAssetSource.m_compiler);
|
||||
|
||||
// Earlier, we declared a job dependency on the .azsl's job, let's access the produced assets:
|
||||
uint32_t subId = ShaderBuilderUtility::MakeAzslBuildProductSubId(RPI::ShaderAssetSubId::GeneratedSource, platformInterface->GetAPIType());
|
||||
uint32_t subId = ShaderBuilderUtility::MakeAzslBuildProductSubId(
|
||||
RPI::ShaderAssetSubId::GeneratedHlslSource, platformInterface->GetAPIType());
|
||||
auto assetIdOutcome = RPI::AssetUtils::MakeAssetId(inputFiles->m_azslSourceFullPath, subId);
|
||||
AZ_Warning(BuilderName, assetIdOutcome.IsSuccess(), "Product of dependency %s not found: this is an oddity but build can continue.", inputFiles->m_azslSourceFullPath.c_str());
|
||||
if (assetIdOutcome.IsSuccess())
|
||||
@@ -325,7 +327,7 @@ namespace AZ
|
||||
AZ_TracePrintf(BuilderName, "Product output already built by %s is not reusable because of incompatible azslc CompilerHints: launching independent build", inputFiles->m_azslSourceFullPath.c_str());
|
||||
}
|
||||
|
||||
if (HasSameStemName(fullSourcePath, inputFiles->m_azslSourceFullPath))
|
||||
if (HasSameFileName(fullSourcePath, inputFiles->m_azslSourceFullPath))
|
||||
{
|
||||
// let's add a "distinguisher" to the names of the outproduct artifacts of this build round.*
|
||||
// Because otherwise the asset processor is not going to accept an overwrite of the ones output by the .azsl job
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <AtomCore/Serialization/Json/JsonUtils.h>
|
||||
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <AzFramework/IO/FileOperations.h> // [GFX TODO] Remove when [ATOM-15472]
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
#include <AzFramework/Process/ProcessCommunicator.h>
|
||||
@@ -122,7 +123,7 @@ namespace AZ
|
||||
|
||||
namespace SubProducts = ShaderBuilderUtility::AzslSubProducts;
|
||||
|
||||
Outcome<SubProducts::Paths> AzslCompiler::EmitFullData(const AZStd::string& parameters, const AZStd::string& outputFile /* = ""*/) const
|
||||
Outcome<SubProducts::Paths> AzslCompiler::EmitFullData(const AZStd::string& parameters, const AZStd::string& outputFile /* = ""*/, const char * addSuffix) const
|
||||
{
|
||||
bool success = Compile("--full " + parameters, outputFile);
|
||||
if (!success)
|
||||
@@ -133,11 +134,22 @@ namespace AZ
|
||||
SubProducts::Paths productPaths = SubProducts::Paths(SubProducts::Paths::capacity());
|
||||
for (auto subProduct : SubProducts::SuffixListMembers)
|
||||
{
|
||||
productPaths[subProduct.m_value] = outputFile.empty() ? m_inputFilePath : outputFile; // that's a reproduction of azslc's behavior (no "-o" = input name is used)
|
||||
AzFramework::StringFunc::Path::ReplaceExtension(productPaths[subProduct.m_value], subProduct.m_string.data());
|
||||
AZStd::string subProductFilePath = outputFile.empty() ? m_inputFilePath : outputFile; // that's a reproduction of azslc's behavior (no "-o" = input name is used)
|
||||
AzFramework::StringFunc::Path::ReplaceExtension(subProductFilePath, subProduct.m_string.data());
|
||||
// append .json if it's one of those subs:
|
||||
auto listOfJsons = { SubProducts::ia, SubProducts::om, SubProducts::srg, SubProducts::options, SubProducts::bindingdep };
|
||||
productPaths[subProduct.m_value] += AZStd::any_of(AZ_BEGIN_END(listOfJsons), [&](auto v) { return v == subProduct.m_value; }) ? ".json" : "";
|
||||
subProductFilePath += AZStd::any_of(AZ_BEGIN_END(listOfJsons), [&](auto v) { return v == subProduct.m_value; }) ? ".json" : "";
|
||||
|
||||
// [GFX TODO] Remove when [ATOM-15472]
|
||||
if (addSuffix)
|
||||
{
|
||||
// Rename the product file.
|
||||
AZStd::string finalSubProductFilePath = AZStd::string::format("%s%s", subProductFilePath.c_str(), addSuffix);
|
||||
AZ::IO::Move(subProductFilePath.c_str(), finalSubProductFilePath.c_str());
|
||||
subProductFilePath = finalSubProductFilePath;
|
||||
}
|
||||
|
||||
productPaths[subProduct.m_value] = subProductFilePath;
|
||||
}
|
||||
productPaths[SubProducts::azslin] = GetInputFilePath(); // post-fixup this one after the loop, because it's not an output of azslc, it's an output of the builder though.
|
||||
return { productPaths };
|
||||
|
||||
@@ -38,8 +38,9 @@ namespace AZ
|
||||
//! @param inputFilePath The target input file to compile. Should be a valid AZSL file with no preprocessing directives.
|
||||
AzslCompiler(const AZStd::string& inputFilePath);
|
||||
|
||||
//! [GFX TODO] Remove @addSuffix when [ATOM-15472]
|
||||
//! compile with --full and generate all .json files
|
||||
Outcome<ShaderBuilderUtility::AzslSubProducts::Paths> EmitFullData(const AZStd::string& parameters, const AZStd::string& outputFile = "") const;
|
||||
Outcome<ShaderBuilderUtility::AzslSubProducts::Paths> EmitFullData(const AZStd::string& parameters, const AZStd::string& outputFile = "", const char * addSuffix = nullptr) const;
|
||||
//! compile to HLSL independently
|
||||
bool EmitShader(AZ::IO::GenericStream& outputStream, const AZStd::string& extraCompilerParams) const;
|
||||
//! compile with --ia independently and populate document @output
|
||||
|
||||
@@ -83,23 +83,41 @@ namespace AZ
|
||||
AZStd::string m_azslFileName; //!< Name for the source .azsl file
|
||||
};
|
||||
|
||||
struct AzslCodeTopData
|
||||
{
|
||||
SrgDataContainer m_srgData;
|
||||
AzslFunctions m_functions;
|
||||
StructContainer m_structs;
|
||||
RootConstantData m_rootConstantData;
|
||||
};
|
||||
|
||||
//! DEPRECATED [ATOM-15472]
|
||||
//! This class is used to collect all the json files produced by the compilation
|
||||
//! of an AZSL file as objects.
|
||||
struct AzslData
|
||||
{
|
||||
AzslData(const AZStd::shared_ptr<ShaderFiles>& a_sources) : m_sources(a_sources) { }
|
||||
|
||||
AZStd::shared_ptr<ShaderFiles> m_sources;
|
||||
AZStd::string m_preprocessedFullPath; // Full path to a preprocessed version of the original AZSL file
|
||||
AZStd::string m_shaderCodePrefix; // AssetProcessor generated shader code which is added to the
|
||||
// AZSLc emitted code prior to invoking the native shader compiler
|
||||
AzslCodeTopData m_topData;
|
||||
AZStd::string m_shaderCodePrefix; // AssetProcessor generated shader code which is added to the
|
||||
// AZSLc emitted code prior to invoking the native shader compiler
|
||||
|
||||
SrgDataContainer m_srgData;
|
||||
AzslFunctions m_functions;
|
||||
StructContainer m_structs;
|
||||
RootConstantData m_rootConstantData;
|
||||
};
|
||||
|
||||
//! This class is used to collect all the json files produced by the compilation
|
||||
//! of an AZSL file as objects.
|
||||
struct AzslData2
|
||||
{
|
||||
AzslData2(const AZStd::shared_ptr<ShaderFiles>& a_sources)
|
||||
: m_sources(a_sources)
|
||||
{
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<ShaderFiles> m_sources;
|
||||
AZStd::string m_preprocessedFullPath; // Full path to a preprocessed version of the original AZSL file
|
||||
|
||||
SrgDataContainer m_srgData;
|
||||
AzslFunctions m_functions;
|
||||
StructContainer m_structs;
|
||||
RootConstantData m_rootConstantData;
|
||||
};
|
||||
} // ShaderBuilder
|
||||
} // AZ
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace AZ
|
||||
// Register AZSL's compilation products Builder
|
||||
AssetBuilderSDK::AssetBuilderDesc azslBuilderDescriptor;
|
||||
azslBuilderDescriptor.m_name = "AZSL Builder";
|
||||
azslBuilderDescriptor.m_version = 7; // LKG Merge
|
||||
azslBuilderDescriptor.m_version = 8; // ATOM-15276
|
||||
// register all extensions thay may carry azsl code. header. main shader. or SRG
|
||||
azslBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
azslBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.azsl", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
@@ -102,7 +102,7 @@ namespace AZ
|
||||
// Register Shader Resource Group Layout Builder
|
||||
AssetBuilderSDK::AssetBuilderDesc srgLayoutBuilderDescriptor;
|
||||
srgLayoutBuilderDescriptor.m_name = "Shader Resource Group Layout Builder";
|
||||
srgLayoutBuilderDescriptor.m_version = 54; // Enable Null Rhi for AutomatedTesting
|
||||
srgLayoutBuilderDescriptor.m_version = 55; // ATOM-15276
|
||||
|
||||
srgLayoutBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.azsl", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
srgLayoutBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.azsli", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
@@ -118,7 +118,7 @@ namespace AZ
|
||||
// Register Shader Asset Builder
|
||||
AssetBuilderSDK::AssetBuilderDesc shaderAssetBuilderDescriptor;
|
||||
shaderAssetBuilderDescriptor.m_name = "Shader Asset Builder";
|
||||
shaderAssetBuilderDescriptor.m_version = 98; // Enable Null Rhi for AutomatedTesting
|
||||
shaderAssetBuilderDescriptor.m_version = 99; // ATOM-15276
|
||||
// .shader file changes trigger rebuilds
|
||||
shaderAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern( AZStd::string::format("*.%s", RPI::ShaderSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
shaderAssetBuilderDescriptor.m_busId = azrtti_typeid<ShaderAssetBuilder>();
|
||||
@@ -133,7 +133,7 @@ namespace AZ
|
||||
shaderVariantAssetBuilderDescriptor.m_name = "Shader Variant Asset Builder";
|
||||
// Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update
|
||||
// ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder".
|
||||
shaderVariantAssetBuilderDescriptor.m_version = 19; // Enable Null Rhi for AutomatedTesting
|
||||
shaderVariantAssetBuilderDescriptor.m_version = 20; // ATOM-15276
|
||||
shaderVariantAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
shaderVariantAssetBuilderDescriptor.m_busId = azrtti_typeid<ShaderVariantAssetBuilder>();
|
||||
shaderVariantAssetBuilderDescriptor.m_createJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::CreateJobs, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
@@ -145,7 +145,7 @@ namespace AZ
|
||||
// Register Precompiled Shader Builder
|
||||
AssetBuilderSDK::AssetBuilderDesc precompiledShaderBuilderDescriptor;
|
||||
precompiledShaderBuilderDescriptor.m_name = "Precompiled Shader Builder";
|
||||
precompiledShaderBuilderDescriptor.m_version = 7; // ATOM-14780
|
||||
precompiledShaderBuilderDescriptor.m_version = 8; // ATOM-15276
|
||||
precompiledShaderBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", AZ::PrecompiledShaderBuilder::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
precompiledShaderBuilderDescriptor.m_busId = azrtti_typeid<PrecompiledShaderBuilder>();
|
||||
precompiledShaderBuilderDescriptor.m_createJobFunction = AZStd::bind(&PrecompiledShaderBuilder::CreateJobs, &m_precompiledShaderBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
@@ -153,6 +153,43 @@ namespace AZ
|
||||
|
||||
m_precompiledShaderBuilder.BusConnect(precompiledShaderBuilderDescriptor.m_busId);
|
||||
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, precompiledShaderBuilderDescriptor);
|
||||
|
||||
// Register Shader Asset Builder 2
|
||||
AssetBuilderSDK::AssetBuilderDesc shaderAssetBuilder2Descriptor;
|
||||
shaderAssetBuilder2Descriptor.m_name = "Shader Asset Builder 2";
|
||||
shaderAssetBuilder2Descriptor.m_version = 1; // ATOM-15276
|
||||
// .shader2 file changes trigger rebuilds
|
||||
shaderAssetBuilder2Descriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(
|
||||
AZStd::string::format("*.%s", RPI::ShaderSourceData::Extension2),
|
||||
AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
shaderAssetBuilder2Descriptor.m_busId = azrtti_typeid<ShaderAssetBuilder2>();
|
||||
shaderAssetBuilder2Descriptor.m_createJobFunction =
|
||||
AZStd::bind(&ShaderAssetBuilder2::CreateJobs, &m_shaderAssetBuilder2, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
shaderAssetBuilder2Descriptor.m_processJobFunction =
|
||||
AZStd::bind(&ShaderAssetBuilder2::ProcessJob, &m_shaderAssetBuilder2, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
|
||||
m_shaderAssetBuilder2.BusConnect(shaderAssetBuilder2Descriptor.m_busId);
|
||||
AssetBuilderSDK::AssetBuilderBus::Broadcast(
|
||||
&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, shaderAssetBuilder2Descriptor);
|
||||
|
||||
// Register Shader Variant Asset Builder 2
|
||||
AssetBuilderSDK::AssetBuilderDesc shaderVariantAssetBuilder2Descriptor;
|
||||
shaderVariantAssetBuilder2Descriptor.m_name = "Shader Variant Asset Builder 2";
|
||||
// Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update
|
||||
// ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder".
|
||||
shaderVariantAssetBuilder2Descriptor.m_version = 1; // ATOM-15276
|
||||
shaderVariantAssetBuilder2Descriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(
|
||||
AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension2),
|
||||
AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
shaderVariantAssetBuilder2Descriptor.m_busId = azrtti_typeid<ShaderVariantAssetBuilder2>();
|
||||
shaderVariantAssetBuilder2Descriptor.m_createJobFunction = AZStd::bind(
|
||||
&ShaderVariantAssetBuilder2::CreateJobs, &m_shaderVariantAssetBuilder2, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
shaderVariantAssetBuilder2Descriptor.m_processJobFunction = AZStd::bind(
|
||||
&ShaderVariantAssetBuilder2::ProcessJob, &m_shaderVariantAssetBuilder2, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
|
||||
m_shaderVariantAssetBuilder2.BusConnect(shaderVariantAssetBuilder2Descriptor.m_busId);
|
||||
AssetBuilderSDK::AssetBuilderBus::Broadcast(
|
||||
&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, shaderVariantAssetBuilder2Descriptor);
|
||||
}
|
||||
|
||||
void AzslShaderBuilderSystemComponent::Deactivate()
|
||||
@@ -161,6 +198,8 @@ namespace AZ
|
||||
m_srgLayoutBuilder.BusDisconnect();
|
||||
m_shaderVariantAssetBuilder.BusDisconnect();
|
||||
m_precompiledShaderBuilder.BusDisconnect();
|
||||
m_shaderAssetBuilder2.BusDisconnect();
|
||||
m_shaderVariantAssetBuilder2.BusDisconnect();
|
||||
|
||||
RHI::ShaderPlatformInterfaceRegisterBus::Handler::BusDisconnect();
|
||||
ShaderPlatformInterfaceRequestBus::Handler::BusDisconnect();
|
||||
|
||||
@@ -18,12 +18,14 @@
|
||||
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
|
||||
#include <AzslBuilder.h>
|
||||
#include <SrgLayoutBuilder.h>
|
||||
#include <ShaderAssetBuilder.h>
|
||||
#include <ShaderVariantAssetBuilder.h>
|
||||
#include <PrecompiledShaderBuilder.h>
|
||||
#include <ShaderPlatformInterfaceRequest.h>
|
||||
#include "AzslBuilder.h"
|
||||
#include "SrgLayoutBuilder.h"
|
||||
#include "ShaderAssetBuilder.h"
|
||||
#include "ShaderVariantAssetBuilder.h"
|
||||
#include "PrecompiledShaderBuilder.h"
|
||||
#include "ShaderPlatformInterfaceRequest.h"
|
||||
#include "ShaderAssetBuilder2.h"
|
||||
#include "ShaderVariantAssetBuilder2.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -71,6 +73,8 @@ namespace AZ
|
||||
ShaderAssetBuilder m_shaderAssetBuilder;
|
||||
ShaderVariantAssetBuilder m_shaderVariantAssetBuilder;
|
||||
PrecompiledShaderBuilder m_precompiledShaderBuilder;
|
||||
ShaderAssetBuilder2 m_shaderAssetBuilder2;
|
||||
ShaderVariantAssetBuilder2 m_shaderVariantAssetBuilder2;
|
||||
|
||||
/// Contains the ShaderPlatformInterface for all registered RHIs
|
||||
AZStd::unordered_map<RHI::APIType, RHI::ShaderPlatformInterface*> m_shaderPlatformInterfaces;
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
GlobalBuildOptions ReadBuildOptions(const char* builderName)
|
||||
GlobalBuildOptions ReadBuildOptions(const char* builderName, const char* optionalIncludeFolder)
|
||||
{
|
||||
GlobalBuildOptions output;
|
||||
// try to parse some config file for eventual additional options
|
||||
@@ -79,7 +79,7 @@ namespace AZ
|
||||
{
|
||||
AZ_TracePrintf(builderName, "config file [%s] not found.", globalBuildOption.c_str());
|
||||
}
|
||||
InitializePreprocessorOptions(output.m_preprocessorSettings, builderName);
|
||||
InitializePreprocessorOptions(output.m_preprocessorSettings, builderName, optionalIncludeFolder);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,9 @@ namespace AZ
|
||||
RHI::ShaderCompilerArguments m_compilerArguments;
|
||||
};
|
||||
|
||||
GlobalBuildOptions ReadBuildOptions(const char* builderName);
|
||||
//! Reads the global options used when compiling shaders. The options are defined in <GameProject>/Config/shader_global_build_options.json
|
||||
//! @param builderName: A string with the name of the builder calling this API. Used for trace debugging.
|
||||
//! @param optionalIncludeFolder: An additional directory to add to the list of include folders for the C-preprocessor.
|
||||
GlobalBuildOptions ReadBuildOptions(const char* builderName, const char* optionalIncludeFolder = nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,37 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void PreprocessorOptions::RemovePredefinedMacros(const AZStd::vector<AZStd::string>& macroNames)
|
||||
{
|
||||
m_predefinedMacros.erase(
|
||||
AZStd::remove_if(
|
||||
m_predefinedMacros.begin(), m_predefinedMacros.end(),
|
||||
[&](const AZStd::string& predefinedMacro)
|
||||
{
|
||||
for (const auto& macroName : macroNames)
|
||||
{
|
||||
// Haystack, needle, bCaseSensitive
|
||||
if (!AzFramework::StringFunc::StartsWith(predefinedMacro, macroName, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// If found, let's make sure it is not just a substring.
|
||||
if (predefinedMacro.size() == macroName.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// The predefinedMacro can be a string like "macro=value". If we find '=' it is a match.
|
||||
if (predefinedMacro.c_str()[macroName.size()] == '=')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
m_predefinedMacros.end());
|
||||
}
|
||||
|
||||
//! Binder helper to Matsui C-Pre-Processor library
|
||||
class McppBinder
|
||||
{
|
||||
@@ -286,7 +317,8 @@ namespace AZ
|
||||
}
|
||||
|
||||
// populate options with scan folders and contents of parsing shader_global_build_options.json
|
||||
void InitializePreprocessorOptions(PreprocessorOptions& options, [[maybe_unused]] const char* builderName)
|
||||
void InitializePreprocessorOptions(
|
||||
PreprocessorOptions& options, [[maybe_unused]] const char* builderName, const char* optionalIncludeFolder)
|
||||
{
|
||||
AZ_TraceContext("Init include-paths lookup options", "preprocessor");
|
||||
|
||||
@@ -303,6 +335,10 @@ namespace AZ
|
||||
// Add the project path to list of include paths
|
||||
AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
|
||||
scanFoldersSet.emplace(projectPath.c_str(), projectPath.size());
|
||||
if (optionalIncludeFolder)
|
||||
{
|
||||
scanFoldersSet.emplace(optionalIncludeFolder, strnlen(optionalIncludeFolder, AZ::IO::MaxPathLength));
|
||||
}
|
||||
// but while we transfer to the set, we're going to keep only folders where +/ShaderLib exists
|
||||
for (AZStd::string folder : scanFoldersVector)
|
||||
{
|
||||
|
||||
@@ -47,9 +47,13 @@ namespace AZ
|
||||
//! folders are relative to the dev folder of the project
|
||||
AZStd::vector<AZStd::string> m_projectIncludePaths;
|
||||
|
||||
//! passed as -D macro1[=value1] -D macro2 ...
|
||||
//! Each string is of the type "name[=value]"
|
||||
//! passed as -Dmacro1[=value1] -Dmacro2 ... to MCPP.
|
||||
AZStd::vector<AZStd::string> m_predefinedMacros;
|
||||
|
||||
//! Removes all macros from @m_predefinedMacros that appear in @macroNames
|
||||
void RemovePredefinedMacros(const AZStd::vector<AZStd::string>& macroNames);
|
||||
|
||||
//! if needed, we may add configurations like
|
||||
//! "keep comments" or "don't predefine non-standard macros"
|
||||
//! or "output diagnostics to std.err" or "enable digraphs/trigraphs"...
|
||||
@@ -59,7 +63,10 @@ namespace AZ
|
||||
//! It will populate your option with a default base of include folders given by the Asset Processor scan folders.
|
||||
//! This is going to look for a Config/shader_global_build_options.json in one of the scan folders
|
||||
//! (that file can specify additional include files and preprocessor macros).
|
||||
void InitializePreprocessorOptions(PreprocessorOptions& options, const char* builderName);
|
||||
//! @param options: Outout parameter, will contain the preprocessor options.
|
||||
//! @param builderName: Used for debugging.
|
||||
//! @param optionalIncludeFolder: If not null, will be added to the list of include folders for the c-preprocessor in @options.
|
||||
void InitializePreprocessorOptions(PreprocessorOptions& options, const char* builderName, const char* optionalIncludeFolder = nullptr);
|
||||
|
||||
/**
|
||||
* Runs the preprocessor on the given source file path, and stores results in outputData.
|
||||
|
||||
@@ -166,17 +166,6 @@ namespace AZ
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
|
||||
}
|
||||
|
||||
static uint32_t GetRootVariantAssetSubId(const RHI::ShaderPlatformInterface& shaderPlatformInterface)
|
||||
{
|
||||
//The 2 Most significant bits encode the the RHI::API unique index.
|
||||
const uint32_t apiUniqueIndex = shaderPlatformInterface.GetAPIUniqueIndex();
|
||||
AZ_Assert(apiUniqueIndex <= RHI::Limits::APIType::PerPlatformApiUniqueIndexMax,
|
||||
"Invalid api unique index [%u] from ShaderPlatformInterface [%s]", apiUniqueIndex, shaderPlatformInterface.GetAPIName().GetCStr());
|
||||
const uint32_t rhiApiSubId = apiUniqueIndex << 30;
|
||||
const uint32_t productSubID = rhiApiSubId | static_cast<uint32_t>(RPI::ShaderAssetSubId::RootShaderVariantAsset);
|
||||
return productSubID;
|
||||
}
|
||||
|
||||
static AssetBuilderSDK::ProcessJobResultCode CompileForAPI(
|
||||
const ShaderBuilderUtility::AzslSubProducts::Paths& pathOfProductFiles,
|
||||
RPI::ShaderAssetCreator& shaderAssetCreator,
|
||||
@@ -201,7 +190,7 @@ namespace AZ
|
||||
if (shaderSourceDataDescriptor.m_programSettings.m_entryPoints.empty())
|
||||
{
|
||||
AZ_TracePrintf(ShaderAssetBuilderName, "ProgramSettings do not specify entry points, will use GetDefaultEntryPointsFromShader()\n");
|
||||
ShaderVariantAssetBuilder::GetDefaultEntryPointsFromAzslData(azslData, shaderEntryPoints);
|
||||
ShaderBuilderUtility::GetDefaultEntryPointsFromFunctionDataList(azslData.m_functions, shaderEntryPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -249,7 +238,9 @@ namespace AZ
|
||||
// so the root ShaderVariantAsset is found when the ShaderAsset is deserialized.
|
||||
AZStd::string fullSourcePath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.c_str(), request.m_sourceFile.c_str(), fullSourcePath, true);
|
||||
const uint32_t productSubID = GetRootVariantAssetSubId(*shaderPlatformInterface);
|
||||
const uint32_t productSubID = RPI::ShaderAsset::MakeAssetProductSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(),
|
||||
aznumeric_cast<uint32_t>(RPI::ShaderAssetSubId::RootShaderVariantAsset));
|
||||
auto assetIdOutcome = RPI::AssetUtils::MakeAssetId(fullSourcePath, productSubID);
|
||||
AZ_Assert(assetIdOutcome.IsSuccess(), "Failed to get AssetId from shader %s", fullSourcePath.c_str());
|
||||
const Data::AssetId variantAssetId = assetIdOutcome.TakeValue();
|
||||
@@ -288,12 +279,13 @@ namespace AZ
|
||||
// add byproducts as job output products:
|
||||
if (variantCreationContext.m_outputByproducts)
|
||||
{
|
||||
uint32_t subProductType = aznumeric_cast<uint32_t>(RPI::ShaderAssetSubId::GeneratedHlslSource) + 1;
|
||||
for (const AZStd::string& byproduct : variantCreationContext.m_outputByproducts->m_intermediatePaths)
|
||||
{
|
||||
AssetBuilderSDK::JobProduct jobProduct;
|
||||
jobProduct.m_productFileName = byproduct;
|
||||
jobProduct.m_productAssetType = Uuid::CreateName("DebugInfoByProduct-PdbOrDxilTxt");
|
||||
jobProduct.m_productSubID = ShaderBuilderUtility::MakeDebugByproductSubId(shaderPlatformInterface->GetAPIType(), byproduct);
|
||||
jobProduct.m_productSubID = RPI::ShaderAsset::MakeAssetProductSubId(shaderPlatformInterface->GetAPIUniqueIndex(), subProductType++);
|
||||
response.m_outputProducts.push_back(AZStd::move(jobProduct));
|
||||
}
|
||||
}
|
||||
@@ -305,12 +297,12 @@ namespace AZ
|
||||
attributeMaps.resize(RHI::ShaderStageCount);
|
||||
for (const auto& shaderEntry : shaderSourceDataDescriptor.m_programSettings.m_entryPoints)
|
||||
{
|
||||
auto findId = AZStd::find_if(AZ_BEGIN_END(azslData.m_topData.m_functions), [&shaderEntry](const auto& func)
|
||||
auto findId = AZStd::find_if(AZ_BEGIN_END(azslData.m_functions), [&shaderEntry](const auto& func)
|
||||
{
|
||||
return func.m_name == shaderEntry.m_name;
|
||||
});
|
||||
|
||||
if (findId == azslData.m_topData.m_functions.end())
|
||||
if (findId == azslData.m_functions.end())
|
||||
{
|
||||
// shaderData.m_functions only contains Vertex, Fragment and Compute entries for now
|
||||
// Tessellation shaders will need to be handled too
|
||||
|
||||
@@ -0,0 +1,684 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include "ShaderAssetBuilder2.h"
|
||||
|
||||
#include <CommonFiles/Preprocessor.h>
|
||||
#include <CommonFiles/GlobalBuildOptions.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAssetCreator2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantKey.h>
|
||||
|
||||
#include <Atom/RHI.Edit/Utils.h>
|
||||
#include <Atom/RHI.Edit/ShaderPlatformInterface.h>
|
||||
#include <Atom/RPI.Edit/Common/JsonReportingHelper.h>
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RHI.Reflect/ConstantsLayout.h>
|
||||
#include <Atom/RHI.Reflect/PipelineLayoutDescriptor.h>
|
||||
#include <Atom/RHI.Reflect/ShaderStageFunction.h>
|
||||
|
||||
#include <AtomCore/Serialization/Json/JsonUtils.h>
|
||||
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <AzFramework/Platform/PlatformDefaults.h>
|
||||
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/JSON/document.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/IOUtils.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
|
||||
#include "AzslBuilder.h"
|
||||
#include "ShaderVariantAssetBuilder2.h"
|
||||
#include "ShaderBuilderUtility.h"
|
||||
#include "ShaderPlatformInterfaceRequest.h"
|
||||
#include "AtomShaderConfig.h"
|
||||
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
#include <AssetBuilderSDK/SerializationDependencies.h>
|
||||
namespace AZ
|
||||
{
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
static constexpr char ShaderAssetBuilder2Name[] = "ShaderAssetBuilder2";
|
||||
static constexpr uint32_t ShaderAssetBuildTimestampParam = 0;
|
||||
|
||||
void ShaderAssetBuilder2::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const
|
||||
{
|
||||
AZStd::string fullPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.data(), request.m_sourceFile.data(), fullPath, true);
|
||||
|
||||
AZ_TracePrintf(ShaderAssetBuilder2Name, "CreateJobs for Shader \"%s\"\n", fullPath.data());
|
||||
|
||||
// Used to synchronize versions of the ShaderAsset and ShaderVariantTreeAsset, especially during hot-reload.
|
||||
// Note it's probably important for this to be set once outside the platform loop so every platform's ShaderAsset
|
||||
// has the same value, because later the ShaderVariantTreeAsset job will fetch this value from the local ShaderAsset
|
||||
// which could cross platforms (i.e. building an android ShaderVariantTreeAsset on PC would fetch the tiemstamp from
|
||||
// the PC's ShaderAsset).
|
||||
AZStd::sys_time_t shaderAssetBuildTimestamp = AZStd::GetTimeNowMicroSecond();
|
||||
|
||||
// Need to get the name of the azsl file from the .shader source asset, to be able to declare a dependency to SRG Layout Job.
|
||||
// and the macro options to preprocess.
|
||||
auto descriptorParseOutcome = ShaderBuilderUtility::LoadShaderDataJson(fullPath);
|
||||
if (!descriptorParseOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilder2Name, false, "Failed to parse Shader Descriptor JSON: %s",
|
||||
descriptorParseOutcome.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
RPI::ShaderSourceData shaderSourceData = descriptorParseOutcome.TakeValue();
|
||||
|
||||
AZStd::string azslFullPath;
|
||||
ShaderBuilderUtility::GetAbsolutePathToAzslFile(fullPath, shaderSourceData.m_source, azslFullPath);
|
||||
if (!IO::FileIOBase::GetInstance()->Exists(azslFullPath.c_str()))
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilder2Name, false, "Shader program listed as the source entry does not exist: %s.", azslFullPath.c_str());
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
GlobalBuildOptions buildOptions = ReadBuildOptions(ShaderAssetBuilder2Name);
|
||||
|
||||
// [GFX TODO] [ATOM-14966] In principle, based on macro definitions, included files can change per supervariant.
|
||||
// So, the list of source asset dependencies must be collected by running MCPP on each supervariant.
|
||||
// For now, we will run MCPP only once because CreateJobs() should be as light as possible.
|
||||
//
|
||||
// Regardless of the PlatformInfo and enabled ShaderPlatformInterfaces, the azsl file will be preprocessed
|
||||
// with the sole purpose of extracting all included files. For each included file a SourceDependency will be declared.
|
||||
PreprocessorData output;
|
||||
buildOptions.m_compilerArguments.Merge(shaderSourceData.m_compiler);
|
||||
PreprocessFile(azslFullPath, output, buildOptions.m_preprocessorSettings, true, true);
|
||||
for (auto includePath : output.includedPaths)
|
||||
{
|
||||
// m_sourceFileDependencyList does not support paths with "." or ".." for relative lookup, but the preprocessor
|
||||
// may produce path strings like "C:/a/b/c/../../d/file.azsli" so we have to normalize
|
||||
AzFramework::StringFunc::Path::Normalize(includePath);
|
||||
|
||||
AssetBuilderSDK::SourceFileDependency includeFileDependency;
|
||||
includeFileDependency.m_sourceFileDependencyPath = includePath;
|
||||
response.m_sourceFileDependencyList.emplace_back(includeFileDependency);
|
||||
}
|
||||
|
||||
{
|
||||
// Add the AZSL as source dependency
|
||||
AssetBuilderSDK::SourceFileDependency azslFileDependency;
|
||||
azslFileDependency.m_sourceFileDependencyPath = azslFullPath;
|
||||
response.m_sourceFileDependencyList.emplace_back(azslFileDependency);
|
||||
}
|
||||
|
||||
for (const AssetBuilderSDK::PlatformInfo& platformInfo : request.m_enabledPlatforms)
|
||||
{
|
||||
AZ_TraceContext("For platform", platformInfo.m_identifier.data());
|
||||
|
||||
// Get the platform interfaces to be able to access the prepend file
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces = ShaderBuilderUtility::DiscoverValidShaderPlatformInterfaces(platformInfo);
|
||||
if (platformInterfaces.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AssetBuilderSDK::JobDescriptor jobDescriptor;
|
||||
jobDescriptor.m_priority = 2;
|
||||
// [GFX TODO][ATOM-2830] Set 'm_critical' back to 'false' once proper fix for Atom startup issues are in
|
||||
jobDescriptor.m_critical = true;
|
||||
jobDescriptor.m_jobKey = ShaderAssetBuilder2JobKey;
|
||||
jobDescriptor.SetPlatformIdentifier(platformInfo.m_identifier.c_str());
|
||||
jobDescriptor.m_jobParameters.emplace(ShaderAssetBuildTimestampParam, AZStd::to_string(shaderAssetBuildTimestamp));
|
||||
|
||||
response.m_createJobOutputs.push_back(jobDescriptor);
|
||||
} // for all request.m_enabledPlatforms
|
||||
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
|
||||
}
|
||||
|
||||
static bool SerializeOutShaderAsset(Data::Asset<RPI::ShaderAsset2> shaderAsset,
|
||||
const AZStd::string& tempDirPath,
|
||||
AssetBuilderSDK::ProcessJobResponse& response)
|
||||
{
|
||||
AZStd::string shaderAssetFileName = AZStd::string::format("%s.%s", shaderAsset->GetName().GetCStr(), RPI::ShaderAsset2::Extension);
|
||||
AZStd::string shaderAssetOutputPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(tempDirPath.data(), shaderAssetFileName.data(), shaderAssetOutputPath, true);
|
||||
|
||||
if (!Utils::SaveObjectToFile(shaderAssetOutputPath, DataStream::ST_BINARY, shaderAsset.Get()))
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilder2Name, false, "Failed to output Shader Descriptor");
|
||||
return false;
|
||||
}
|
||||
|
||||
AssetBuilderSDK::JobProduct shaderJobProduct;
|
||||
if (!AssetBuilderSDK::OutputObject(shaderAsset.Get(), shaderAssetOutputPath, azrtti_typeid<RPI::ShaderAsset2>(),
|
||||
aznumeric_cast<uint32_t>(RPI::ShaderAsset2ProductSubId::ShaderAsset2), shaderJobProduct))
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilder2Name, false, "Failed to output product dependencies.");
|
||||
return false;
|
||||
}
|
||||
response.m_outputProducts.push_back(AZStd::move(shaderJobProduct));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static AZ::Outcome<RHI::ShaderStageAttributeMapList, AZStd::string> BuildAttributesMap(
|
||||
const RHI::ShaderPlatformInterface* shaderPlatformInterface,
|
||||
const AzslData& azslData,
|
||||
const MapOfStringToStageType& shaderEntryPoints,
|
||||
bool& hasRasterProgram)
|
||||
{
|
||||
hasRasterProgram = false;
|
||||
bool hasComputeProgram = false;
|
||||
bool hasRayTracingProgram = false;
|
||||
RHI::ShaderStageAttributeMapList attributeMaps;
|
||||
attributeMaps.resize(RHI::ShaderStageCount);
|
||||
for (const auto& shaderEntryPoint : shaderEntryPoints)
|
||||
{
|
||||
auto shaderEntryName = shaderEntryPoint.first;
|
||||
auto shaderStageType = shaderEntryPoint.second;
|
||||
auto assetBuilderShaderType = ShaderBuilderUtility::ToAssetBuilderShaderType(shaderStageType);
|
||||
hasRasterProgram |= shaderPlatformInterface->IsShaderStageForRaster(assetBuilderShaderType);
|
||||
hasComputeProgram |= shaderPlatformInterface->IsShaderStageForCompute(assetBuilderShaderType);
|
||||
hasRayTracingProgram |= shaderPlatformInterface->IsShaderStageForRayTracing(assetBuilderShaderType);
|
||||
|
||||
auto findId = AZStd::find_if(AZ_BEGIN_END(azslData.m_functions), [&shaderEntryPoint](const auto& func) {
|
||||
return func.m_name == shaderEntryPoint.first;
|
||||
});
|
||||
|
||||
if (findId == azslData.m_functions.end())
|
||||
{
|
||||
// shaderData.m_functions only contains Vertex, Fragment and Compute entries for now
|
||||
// Tessellation shaders will need to be handled too
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto shaderStage = ToRHIShaderStage(assetBuilderShaderType);
|
||||
for (const auto& attr : findId->attributesList)
|
||||
{
|
||||
// Some stages like RHI::ShaderStage::Tessellation are compound and consist of two or more shader entries
|
||||
const Name& attributeName = attr.first;
|
||||
const RHI::ShaderStageAttributeArguments& args = attr.second;
|
||||
const auto stageIndex = static_cast<uint32_t>(shaderStage);
|
||||
AZ_Assert(stageIndex < RHI::ShaderStageCount, "Invalid shader stage specified!");
|
||||
attributeMaps[stageIndex][attributeName] = args;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasRasterProgram && hasComputeProgram)
|
||||
{
|
||||
return AZ::Failure(AZStd::string(" Shader asset descriptor defines both a raster entry point and a compute entry point."));
|
||||
}
|
||||
|
||||
if (!hasRasterProgram && !hasComputeProgram && !hasRayTracingProgram)
|
||||
{
|
||||
AZStd::string entryPointNames = ShaderBuilderUtility::GetAcceptableDefaultEntryPointNames(azslData);
|
||||
return AZ::Failure(
|
||||
AZStd::string::format( "Shader asset descriptor has a program variant that does not define any entry points. Either declare entry "
|
||||
"points in the .shader file, or use one of the available default names (not case-sensitive): [%s]",
|
||||
entryPointNames.c_str()));
|
||||
}
|
||||
|
||||
return AZ::Success(attributeMaps);
|
||||
}
|
||||
|
||||
void ShaderAssetBuilder2::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const
|
||||
{
|
||||
const AZStd::sys_time_t startTime = AZStd::GetTimeNowTicks();
|
||||
AZStd::string shaderFullPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.c_str(), request.m_sourceFile.c_str(), shaderFullPath, true);
|
||||
// Save .shader file name (no extension and no parent directory path)
|
||||
AZStd::string shaderFileName;
|
||||
AzFramework::StringFunc::Path::GetFileName(request.m_sourceFile.c_str(), shaderFileName);
|
||||
|
||||
// No error checking because the same calls were already executed during CreateJobs()
|
||||
auto descriptorParseOutcome = ShaderBuilderUtility::LoadShaderDataJson(shaderFullPath);
|
||||
RPI::ShaderSourceData shaderSourceData = descriptorParseOutcome.TakeValue();
|
||||
AZStd::string azslFullPath;
|
||||
ShaderBuilderUtility::GetAbsolutePathToAzslFile(shaderFullPath, shaderSourceData.m_source, azslFullPath);
|
||||
AZ_TracePrintf(ShaderAssetBuilder2Name, "Original AZSL File: %s \n", azslFullPath.c_str());
|
||||
|
||||
// The directory where the Azsl file was found must be added to the list of include paths
|
||||
AZStd::string azslFolderPath;
|
||||
AzFramework::StringFunc::Path::GetFolderPath(azslFullPath.c_str(), azslFolderPath);
|
||||
GlobalBuildOptions buildOptions = ReadBuildOptions(ShaderAssetBuilder2Name, azslFolderPath.c_str());
|
||||
|
||||
// Request the list of valid shader platform interfaces for the target platform.
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces = ShaderBuilderUtility::DiscoverEnabledShaderPlatformInterfaces(
|
||||
request.m_platformInfo, shaderSourceData);
|
||||
if (platformInterfaces.empty())
|
||||
{
|
||||
//No work to do. Exit gracefully.
|
||||
AZ_TracePrintf(ShaderAssetBuilder2Name,
|
||||
"No azshader is produced on behalf of %s because all valid RHI backends were disabled for this shader.\n",
|
||||
shaderFullPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the time stamp string as sys_time_t, and also convert back to string to make sure it was converted correctly.
|
||||
AZStd::sys_time_t shaderAssetBuildTimestamp = 0;
|
||||
auto shaderAssetBuildTimestampIterator = request.m_jobDescription.m_jobParameters.find(ShaderAssetBuildTimestampParam);
|
||||
if (shaderAssetBuildTimestampIterator != request.m_jobDescription.m_jobParameters.end())
|
||||
{
|
||||
shaderAssetBuildTimestamp = AZStd::stoull(shaderAssetBuildTimestampIterator->second);
|
||||
|
||||
if (AZStd::to_string(shaderAssetBuildTimestamp) != shaderAssetBuildTimestampIterator->second)
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
AZ_Assert(false, "Incorrect conversion of ShaderAssetBuildTimestampParam");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto supervariantList = ShaderBuilderUtility::GetSupervariantListFromShaderSourceData(shaderSourceData);
|
||||
|
||||
RPI::ShaderAssetCreator2 shaderAssetCreator;
|
||||
shaderAssetCreator.Begin(Uuid::CreateRandom());
|
||||
|
||||
shaderAssetCreator.SetName(AZ::Name{shaderFileName.c_str()});
|
||||
shaderAssetCreator.SetDrawListName(Name(shaderSourceData.m_drawListName));
|
||||
shaderAssetCreator.SetShaderAssetBuildTimestamp(shaderAssetBuildTimestamp);
|
||||
|
||||
// The ShaderOptionGroupLayout must be the same across all supervariants because
|
||||
// there can be only a single ShaderVariantTreeAsset per ShaderAsset.
|
||||
// We will store here the one that results when the *.azslin file is
|
||||
// compiled for the default, nameless, supervariant.
|
||||
// For all other supervariants we just make sure the hashes are the same
|
||||
// as this one.
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> finalShaderOptionGroupLayout = nullptr;
|
||||
|
||||
|
||||
// Time to describe the big picture.
|
||||
// 1- Preprocess an AZSL file with MCPP (a C-Preprocessor), and generate a flat AZSL file without #include lines and any macros in it.
|
||||
// Let's call it the Flat-AZSL file. There are two levels of macro definition that need to be merged before we can invoke MCPP:
|
||||
// 1.1- From <GameProject>/Config/shader_global_build_options.json, which we have stored in the local variable @buildOptions.
|
||||
// 1.2- From the "Supervariant" definition key, which can be different for each supervariant.
|
||||
// 2- There will be one Flat-AZSL per supervariant. Each Flat-AZSL will be transpiled to HLSL with AZSLc. This means there will be one HLSL file
|
||||
// per supervariant.
|
||||
// 3- The generated HLSL (one HLSL per supervariant) file may contain C-Preprocessor Macros inserted by AZSLc. And that file will be given to DXC.
|
||||
// DXC has a preprocessor embedded in it. DXC will be executed once for each entry function listed in the .shader file.
|
||||
// There will be one DXIL compiled binary for each entry function. All the DXIL compiled binaries for each supervariant will be combined
|
||||
// in the ROOT ShaderVariantAsset.
|
||||
|
||||
// Remark: In general, the work done by the ShaderVariantAssetBuilder is similar, but it will start from the HLSL file created; in step 2, mentioned above; by this builder,
|
||||
// for each supervariant.
|
||||
|
||||
// At this moment We have global build options that should be merged with the build options that are common
|
||||
// to all the supervariants of this shader.
|
||||
buildOptions.m_compilerArguments.Merge(shaderSourceData.m_compiler);
|
||||
|
||||
for (RHI::ShaderPlatformInterface* shaderPlatformInterface : platformInterfaces)
|
||||
{
|
||||
AZStd::string apiName(shaderPlatformInterface->GetAPIName().GetCStr());
|
||||
AZ_TraceContext("Platform API", apiName);
|
||||
// Signal the begin of shader data for an RHI API.
|
||||
shaderAssetCreator.BeginAPI(shaderPlatformInterface->GetAPIType());
|
||||
|
||||
// Each shaderPlatformInterface has its own azsli header that needs to be prepended to the AZSL file before
|
||||
// preprocessing. We will create a new temporary file that contains the combined data.
|
||||
RHI::PrependArguments args;
|
||||
args.m_sourceFile = azslFullPath.c_str();
|
||||
args.m_prependFile = shaderPlatformInterface->GetAzslHeader(request.m_platformInfo);
|
||||
args.m_addSuffixToFileName = apiName.c_str();
|
||||
args.m_destinationFolder = request.m_tempDirPath.c_str();
|
||||
|
||||
AZStd::string prependedAzslFilePath = RHI::PrependFile(args);
|
||||
if (prependedAzslFilePath == azslFullPath)
|
||||
{
|
||||
// For some reason the combined azsl file was not created in the temporary
|
||||
// directory assigned to this job.
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache common AZSLC invokation arguments related with the current RHI Backend.
|
||||
// Each supervariant can, optionally, remove or add more arguments for AZSLc.
|
||||
AZStd::string commonAzslcCompilerParameters =
|
||||
shaderPlatformInterface->GetAzslCompilerParameters(buildOptions.m_compilerArguments);
|
||||
commonAzslcCompilerParameters += " ";
|
||||
commonAzslcCompilerParameters +=
|
||||
shaderPlatformInterface->GetAzslCompilerWarningParameters(buildOptions.m_compilerArguments);
|
||||
AtomShaderConfig::AddParametersFromConfigFile(commonAzslcCompilerParameters, request.m_platformInfo);
|
||||
|
||||
// The register number only makes sense if the platform uses "spaces",
|
||||
// since the register Id of the resource will not change even if the pipeline layout changes.
|
||||
// We can pass in a default ShaderCompilerArguments because all we care about is whether the shaderPlatformInterface
|
||||
// appends the "--use-spaces" flag.
|
||||
const bool platformUsesRegisterSpaces =
|
||||
(AzFramework::StringFunc::Find(commonAzslcCompilerParameters, "--use-spaces") != AZStd::string::npos);
|
||||
|
||||
uint32_t supervariantIndex = 0;
|
||||
for (const auto& supervariantInfo : supervariantList)
|
||||
{
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
shaderAssetCreator.BeginSupervariant(supervariantInfo.m_name);
|
||||
|
||||
// Let's combine the global macro definitions, with the macro definitions particular to this
|
||||
// supervariant. Two steps:
|
||||
// 1- Supervariants can specify which macros to remove from the global definitions.
|
||||
AZStd::vector<AZStd::string> macroDefinitionNamesToRemove = supervariantInfo.GetCombinedListOfMacroDefinitionNamesToRemove();
|
||||
PreprocessorOptions preprocessorOptions = buildOptions.m_preprocessorSettings;
|
||||
preprocessorOptions.RemovePredefinedMacros(macroDefinitionNamesToRemove);
|
||||
// 2- Supervariants can specify which macros to add.
|
||||
AZStd::vector<AZStd::string> macroDefinitionsToAdd = supervariantInfo.GetMacroDefinitionsToAdd();
|
||||
preprocessorOptions.m_predefinedMacros.insert(
|
||||
preprocessorOptions.m_predefinedMacros.end(), macroDefinitionsToAdd.begin(), macroDefinitionsToAdd.end());
|
||||
// Run the preprocessor.
|
||||
PreprocessorData output;
|
||||
PreprocessFile(prependedAzslFilePath, output, preprocessorOptions, true, true);
|
||||
RHI::ReportErrorMessages(ShaderAssetBuilder2Name, output.diagnostics);
|
||||
// Dump the preprocessed string as a flat AZSL file with extension .azslin, which will be given to AZSLc to generate the HLSL file.
|
||||
AZStd::string superVariantAzslinStemName = shaderFileName;
|
||||
if (!supervariantInfo.m_name.IsEmpty())
|
||||
{
|
||||
superVariantAzslinStemName += AZStd::string::format("-%s", supervariantInfo.m_name.GetCStr());
|
||||
}
|
||||
AZStd::string azslinFullPath = ShaderBuilderUtility::DumpPreprocessedCode(
|
||||
ShaderAssetBuilder2Name, output.code, request.m_tempDirPath, superVariantAzslinStemName,
|
||||
apiName, true /*add2*/);
|
||||
if (azslinFullPath.empty())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
AZ_TracePrintf(ShaderAssetBuilder2Name, "Preprocessed AZSL File: %s \n", prependedAzslFilePath.c_str());
|
||||
|
||||
// Before transpiling the flat-AZSL(.azslin) file into HLSL it is necessary
|
||||
// to setup the AZSLc arguments as required by the current supervariant.
|
||||
AZStd::string azslcCompilerParameters = supervariantInfo.GetCustomizedArgumentsForAzslc(commonAzslcCompilerParameters);
|
||||
|
||||
// Ready to transpile the azslin file into HLSL.
|
||||
ShaderBuilder::AzslCompiler azslc(azslinFullPath);
|
||||
AZStd::string hlslFullPath = AZStd::string::format("%s_%s.hlsl2", superVariantAzslinStemName.c_str(), apiName.c_str());
|
||||
AzFramework::StringFunc::Path::Join(request.m_tempDirPath.c_str(), hlslFullPath.c_str(), hlslFullPath, true);
|
||||
auto emitFullOutcome = azslc.EmitFullData(azslcCompilerParameters, hlslFullPath, "2");
|
||||
if (!emitFullOutcome.IsSuccess())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
ShaderBuilderUtility::AzslSubProducts::Paths subProductsPaths = emitFullOutcome.TakeValue();
|
||||
|
||||
// In addition to the hlsl file, there are other json files that were generated.
|
||||
// Each output file will become a product.
|
||||
for (int i = 0; i < subProductsPaths.size(); ++i)
|
||||
{
|
||||
AssetBuilderSDK::JobProduct jobProduct;
|
||||
jobProduct.m_productFileName = subProductsPaths[i];
|
||||
static const AZ::Uuid AzslOutcomeType = "{6977AEB1-17AD-4992-957B-23BB2E85B18B}";
|
||||
jobProduct.m_productAssetType = AzslOutcomeType;
|
||||
// uint32_t rhiApiUniqueIndex, uint32_t supervariantIndex, uint32_t subProductType
|
||||
jobProduct.m_productSubID = RPI::ShaderAsset2::MakeProductAssetSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), supervariantIndex,
|
||||
aznumeric_cast<uint32_t>(ShaderBuilderUtility::AzslSubProducts::SubList[i]));
|
||||
jobProduct.m_dependenciesHandled = true;
|
||||
// Note that the output products are not traditional product assets that will be used by the game project.
|
||||
// They are artifacts that are produced once, cached, and used later by other AssetBuilders as a way to centralize
|
||||
// build organization.
|
||||
response.m_outputProducts.push_back(AZStd::move(jobProduct));
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<ShaderFiles> files(new ShaderFiles);
|
||||
AzslData azslData(files);
|
||||
azslData.m_preprocessedFullPath = azslinFullPath;
|
||||
RPI::ShaderResourceGroupLayoutList srgLayoutList;
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
|
||||
BindingDependencies bindingDependencies;
|
||||
RootConstantData rootConstantData;
|
||||
AssetBuilderSDK::ProcessJobResultCode azslJsonReadResult = ShaderBuilderUtility::PopulateAzslDataFromJsonFiles(
|
||||
ShaderAssetBuilder2Name, subProductsPaths, platformUsesRegisterSpaces, azslData, srgLayoutList, shaderOptionGroupLayout,
|
||||
bindingDependencies, rootConstantData);
|
||||
if (azslJsonReadResult != AssetBuilderSDK::ProcessJobResult_Success)
|
||||
|
||||
{
|
||||
response.m_resultCode = azslJsonReadResult;
|
||||
return;
|
||||
}
|
||||
|
||||
shaderAssetCreator.SetSrgLayoutList(srgLayoutList);
|
||||
|
||||
if (!finalShaderOptionGroupLayout)
|
||||
{
|
||||
finalShaderOptionGroupLayout = shaderOptionGroupLayout;
|
||||
shaderAssetCreator.SetShaderOptionGroupLayout(finalShaderOptionGroupLayout);
|
||||
const uint32_t usedShaderOptionBits = shaderOptionGroupLayout->GetBitSize();
|
||||
AZ_TracePrintf(
|
||||
ShaderAssetBuilder2Name, "Note: This shader uses %u of %u available shader variant key bits. \n",
|
||||
usedShaderOptionBits, RPI::ShaderVariantKeyBitCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (finalShaderOptionGroupLayout->GetHash() != shaderOptionGroupLayout->GetHash())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilder2Name, false, "Supervariant %s has a different ShaderOptionGroupLayout",
|
||||
supervariantInfo.m_name.GetCStr())
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Discover entry points & type of programs.
|
||||
MapOfStringToStageType shaderEntryPoints;
|
||||
if (shaderSourceData.m_programSettings.m_entryPoints.empty())
|
||||
{
|
||||
AZ_TracePrintf(
|
||||
ShaderAssetBuilder2Name,
|
||||
"ProgramSettings do not specify entry points, will use GetDefaultEntryPointsFromShader()\n");
|
||||
ShaderBuilderUtility::GetDefaultEntryPointsFromFunctionDataList(azslData.m_functions, shaderEntryPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& entryPoint : shaderSourceData.m_programSettings.m_entryPoints)
|
||||
{
|
||||
shaderEntryPoints[entryPoint.m_name] = entryPoint.m_type;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasRasterProgram = false;
|
||||
auto attributeMapsOutcome = BuildAttributesMap(shaderPlatformInterface, azslData, shaderEntryPoints, hasRasterProgram);
|
||||
if (!attributeMapsOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilder2Name, false, "%s\n", attributeMapsOutcome.GetError().c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
shaderAssetCreator.SetShaderStageAttributeMapList(attributeMapsOutcome.TakeValue());
|
||||
|
||||
// Check if we were canceled before we do any heavy processing of
|
||||
// the shader data (compiling the shader kernels, processing SRG
|
||||
// and pipeline layout data, etc.).
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> pipelineLayoutDescriptor =
|
||||
ShaderBuilderUtility::BuildPipelineLayoutDescriptorForApi(
|
||||
ShaderAssetBuilder2Name, srgLayoutList, shaderEntryPoints, buildOptions.m_compilerArguments, rootConstantData,
|
||||
shaderPlatformInterface, bindingDependencies);
|
||||
if (!pipelineLayoutDescriptor)
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilder2Name, false, "Failed to build pipeline layout descriptor for api=[%s]",
|
||||
shaderPlatformInterface->GetAPIName().GetCStr());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
shaderAssetCreator.SetPipelineLayout(pipelineLayoutDescriptor);
|
||||
|
||||
|
||||
RPI::ShaderInputContract shaderInputContract;
|
||||
RPI::ShaderOutputContract shaderOutputContract;
|
||||
size_t colorAttachmentCount = 0;
|
||||
ShaderBuilderUtility::CreateShaderInputAndOutputContracts(
|
||||
azslData, shaderEntryPoints, *shaderOptionGroupLayout.get(),
|
||||
subProductsPaths[ShaderBuilderUtility::AzslSubProducts::om],
|
||||
subProductsPaths[ShaderBuilderUtility::AzslSubProducts::ia],
|
||||
shaderInputContract, shaderOutputContract, colorAttachmentCount);
|
||||
shaderAssetCreator.SetInputContract(shaderInputContract);
|
||||
shaderAssetCreator.SetOutputContract(shaderOutputContract);
|
||||
|
||||
if (hasRasterProgram)
|
||||
{
|
||||
// Set the various states to what is in the descriptor.
|
||||
const RHI::TargetBlendState& targetBlendState = shaderSourceData.m_blendState;
|
||||
RHI::RenderStates renderStates;
|
||||
renderStates.m_rasterState = shaderSourceData.m_rasterState;
|
||||
renderStates.m_depthStencilState = shaderSourceData.m_depthStencilState;
|
||||
// [GFX TODO][ATOM-930] We should support unique blend states per RT
|
||||
for (size_t i = 0; i < colorAttachmentCount; ++i)
|
||||
{
|
||||
renderStates.m_blendState.m_targets[i] = targetBlendState;
|
||||
}
|
||||
|
||||
shaderAssetCreator.SetRenderStates(renderStates);
|
||||
}
|
||||
|
||||
Outcome<AZStd::string, AZStd::string> hlslSourceCodeOutcome = Utils::ReadFile(hlslFullPath);
|
||||
if (!hlslSourceCodeOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilder2Name, false, "Failed to obtain shader source from %s. [%s]", hlslFullPath.c_str(),
|
||||
hlslSourceCodeOutcome.GetError().c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
AZStd::string hlslSourceCode = hlslSourceCodeOutcome.TakeValue();
|
||||
|
||||
// The root ShaderVariantAsset needs to be created with the known uuid of the source .shader asset because
|
||||
// the ShaderAsset owns a Data::Asset<> reference that gets serialized. It must have the correct uuid
|
||||
// so the root ShaderVariantAsset is found when the ShaderAsset is deserialized.
|
||||
uint32_t rootVariantProductSubId = RPI::ShaderAsset2::MakeProductAssetSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), supervariantIndex,
|
||||
aznumeric_cast<uint32_t>(RPI::ShaderAsset2ProductSubId::RootShaderVariantAsset));
|
||||
auto assetIdOutcome = RPI::AssetUtils::MakeAssetId(shaderFullPath, rootVariantProductSubId);
|
||||
AZ_Assert(assetIdOutcome.IsSuccess(), "Failed to get AssetId from shader %s", shaderFullPath.c_str());
|
||||
const Data::AssetId variantAssetId = assetIdOutcome.TakeValue();
|
||||
|
||||
RPI::ShaderVariantListSourceData::VariantInfo rootVariantInfo;
|
||||
ShaderVariantCreationContext2 shaderVariantCreationContext = {
|
||||
*shaderPlatformInterface,
|
||||
request.m_platformInfo,
|
||||
buildOptions.m_compilerArguments,
|
||||
request.m_tempDirPath,
|
||||
startTime,
|
||||
shaderSourceData,
|
||||
*shaderOptionGroupLayout.get(),
|
||||
shaderEntryPoints,
|
||||
variantAssetId,
|
||||
superVariantAzslinStemName,
|
||||
hlslFullPath,
|
||||
hlslSourceCode};
|
||||
|
||||
|
||||
AZStd::optional<RHI::ShaderPlatformInterface::ByProducts> outputByproducts;
|
||||
auto rootShaderVariantAssetOutcome = ShaderVariantAssetBuilder2::CreateShaderVariantAsset(rootVariantInfo, shaderVariantCreationContext, outputByproducts);
|
||||
if (!rootShaderVariantAssetOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderAssetBuilder2Name, false, "%s\n", rootShaderVariantAssetOutcome.GetError().c_str())
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
Data::Asset<RPI::ShaderVariantAsset2> rootShaderVariantAsset = rootShaderVariantAssetOutcome.TakeValue();
|
||||
|
||||
shaderAssetCreator.SetRootShaderVariantAsset(rootShaderVariantAsset);
|
||||
|
||||
if (!shaderAssetCreator.EndSupervariant())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderAssetBuilder2Name, false, "Failed to create shader asset for supervariant [%s]", supervariantInfo.m_name.GetCStr())
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
// Time to save the root variant related assets in the cache.
|
||||
AssetBuilderSDK::JobProduct assetProduct;
|
||||
if (!ShaderVariantAssetBuilder2::SerializeOutShaderVariantAsset(
|
||||
rootShaderVariantAsset, superVariantAzslinStemName, request.m_tempDirPath, *shaderPlatformInterface,
|
||||
rootVariantProductSubId,
|
||||
assetProduct))
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
response.m_outputProducts.push_back(assetProduct);
|
||||
|
||||
if (outputByproducts)
|
||||
{
|
||||
// add byproducts as job output products:
|
||||
uint32_t subProductType = aznumeric_cast<uint32_t>(RPI::ShaderAsset2ProductSubId::FirstByProduct);
|
||||
for (const AZStd::string& byproduct : outputByproducts.value().m_intermediatePaths)
|
||||
{
|
||||
AssetBuilderSDK::JobProduct jobProduct;
|
||||
jobProduct.m_productFileName = byproduct;
|
||||
jobProduct.m_productAssetType = Uuid::CreateName("DebugInfoByProduct-PdbOrDxilTxt");
|
||||
jobProduct.m_productSubID = RPI::ShaderAsset2::MakeProductAssetSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), supervariantIndex,
|
||||
subProductType++);
|
||||
response.m_outputProducts.push_back(AZStd::move(jobProduct));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
supervariantIndex++;
|
||||
|
||||
} // end for the supervariant
|
||||
|
||||
shaderAssetCreator.EndAPI();
|
||||
|
||||
} // end for all ShaderPlatformInterfaces
|
||||
|
||||
Data::Asset<RPI::ShaderAsset2> shaderAsset;
|
||||
if (!shaderAssetCreator.End(shaderAsset))
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SerializeOutShaderAsset(shaderAsset, request.m_tempDirPath, response))
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
|
||||
const AZStd::sys_time_t endTime = AZStd::GetTimeNowTicks();
|
||||
const AZStd::sys_time_t deltaTime = endTime - startTime;
|
||||
const float elapsedTimeSeconds = (float)(deltaTime) / (float)AZStd::GetTimeTicksPerSecond();
|
||||
|
||||
AZ_TracePrintf(ShaderAssetBuilder2Name, "Finished processing %s in %.2f seconds\n", request.m_sourceFile.c_str(), elapsedTimeSeconds);
|
||||
|
||||
ShaderBuilderUtility::LogProfilingData(ShaderAssetBuilder2Name, shaderFileName);
|
||||
}
|
||||
|
||||
} // ShaderBuilder
|
||||
} // AZ
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AssetBuilderSDK/AssetBuilderBusses.h>
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
|
||||
#include <Atom/RHI.Reflect/Base.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Data
|
||||
{
|
||||
class AssetHandler;
|
||||
}
|
||||
|
||||
namespace RHI
|
||||
{
|
||||
class ShaderPlatformInterface;
|
||||
}
|
||||
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
struct AzslData;
|
||||
|
||||
class ShaderAssetBuilder2
|
||||
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(ShaderAssetBuilder2, "{C94DA151-82BC-4475-86FA-E6C92A0BD6F8}");
|
||||
|
||||
static constexpr const char* ShaderAssetBuilder2JobKey = "Shader Asset 2";
|
||||
|
||||
ShaderAssetBuilder2() = default;
|
||||
~ShaderAssetBuilder2() = default;
|
||||
|
||||
// Asset Builder Callback Functions ...
|
||||
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const;
|
||||
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const;
|
||||
|
||||
// AssetBuilderSDK::AssetBuilderCommandBus interface overrides ...
|
||||
void ShutDown() override { };
|
||||
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(ShaderAssetBuilder2);
|
||||
};
|
||||
|
||||
} // ShaderBuilder
|
||||
} // AZ
|
||||
@@ -29,7 +29,8 @@
|
||||
|
||||
#include <Atom/RPI.Edit/Common/JsonReportingHelper.h>
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h> // DEPRECATED - [ATOM-15472]
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
|
||||
|
||||
#include <Atom/RHI.Edit/Utils.h>
|
||||
@@ -41,13 +42,15 @@
|
||||
#include "ShaderPlatformInterfaceRequest.h"
|
||||
#include "AtomShaderConfig.h"
|
||||
|
||||
#include "SrgLayoutUtility.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
namespace ShaderBuilderUtility
|
||||
{
|
||||
static const char* ShaderBuilderUtilityName = "ShaderBuilderUtility";
|
||||
static constexpr char ShaderBuilderUtilityName[] = "ShaderBuilderUtility";
|
||||
|
||||
Outcome<RPI::ShaderSourceData, AZStd::string> LoadShaderDataJson(const AZStd::string& fullPathToJsonFile)
|
||||
{
|
||||
@@ -84,22 +87,8 @@ namespace AZ
|
||||
AzFramework::StringFunc::Path::ReplaceExtension(absoluteAzslPath, "azsl");
|
||||
}
|
||||
|
||||
uint32_t MakeDebugByproductSubId(RHI::APIType apiType, const AZStd::string& productFileName)
|
||||
{
|
||||
// bits: ----- 24 -----|- 4 -|- 4 -
|
||||
// fn hash | id + api | 0xF
|
||||
uint32_t subId = 0xF; // to avoid collisions with subid of other source outputs using RPI::ShaderAssetSubId::GeneratedSource + api
|
||||
uint32_t id_api = static_cast<uint32_t>(RPI::ShaderAssetSubId::DebugByProduct);
|
||||
id_api += apiType;
|
||||
id_api <<= 4;
|
||||
subId |= id_api;
|
||||
size_t fnHash = AZStd::hash<AZStd::string>()(productFileName);
|
||||
subId |= static_cast<uint32_t>(fnHash) & 0xFFFFFF00;
|
||||
return subId;
|
||||
}
|
||||
|
||||
static bool LoadShaderResourceGroupAssets(
|
||||
[[maybe_unused]] const char* BuilderName,
|
||||
[[maybe_unused]] const char* builderName,
|
||||
const SrgDataContainer& resourceGroups,
|
||||
ShaderResourceGroupAssets& srgAssets)
|
||||
{
|
||||
@@ -121,7 +110,7 @@ namespace AZ
|
||||
|
||||
if (!assetFound)
|
||||
{
|
||||
AZ_Error(BuilderName, false, "Could not find asset identified by path '%s'", srgFilePath.c_str());
|
||||
AZ_Error(builderName, false, "Could not find asset identified by path '%s'", srgFilePath.c_str());
|
||||
readSRGsSuccessfuly = false;
|
||||
continue;
|
||||
}
|
||||
@@ -139,7 +128,7 @@ namespace AZ
|
||||
: asset.GetStatus() == Status::ReadyPreNotify ? "ready-pre-notify"
|
||||
: asset.GetStatus() == Status::Error ? "error" : "not-loaded/ready/unknown";
|
||||
|
||||
AZ_Error(BuilderName, false, "Searching SRG [%s]: Could not load SRG asset. (asset status [%s]) AssetId='%s' Path='%s'",
|
||||
AZ_Error(builderName, false, "Searching SRG [%s]: Could not load SRG asset. (asset status [%s]) AssetId='%s' Path='%s'",
|
||||
srgData.m_name.c_str(),
|
||||
statusString.c_str(),
|
||||
assetId.ToString<AZStd::string>().c_str(), srgFilePath.c_str());
|
||||
@@ -148,7 +137,7 @@ namespace AZ
|
||||
}
|
||||
else if (!asset->IsValid())
|
||||
{
|
||||
AZ_Error(BuilderName, false, "SRG asset has no layout information. AssetId='%s' Path='%s'",
|
||||
AZ_Error(builderName, false, "SRG asset has no layout information. AssetId='%s' Path='%s'",
|
||||
assetId.ToString<AZStd::string>().c_str(), srgFilePath.c_str());
|
||||
readSRGsSuccessfuly = false;
|
||||
continue;
|
||||
@@ -182,8 +171,10 @@ namespace AZ
|
||||
return files;
|
||||
}
|
||||
|
||||
|
||||
//! [GFX TODO] [ATOM-15472] Deprecated, remove when this ticket is addressed.
|
||||
AssetBuilderSDK::ProcessJobResultCode PopulateAzslDataFromJsonFiles(
|
||||
const char* BuilderName,
|
||||
const char* builderName,
|
||||
const AzslSubProducts::Paths& pathOfJsonFiles,
|
||||
AzslData& azslData,
|
||||
ShaderResourceGroupAssets& srgAssets,
|
||||
@@ -204,7 +195,7 @@ namespace AZ
|
||||
outcomes[i] = JsonSerializationUtils::ReadJsonFile(pathOfJsonFiles[i]);
|
||||
if (!outcomes[i].IsSuccess())
|
||||
{
|
||||
AZ_Error(BuilderName, false, "%s", outcomes[i].GetError().c_str());
|
||||
AZ_Error(builderName, false, "%s", outcomes[i].GetError().c_str());
|
||||
allReadSuccess = false;
|
||||
}
|
||||
}
|
||||
@@ -215,22 +206,22 @@ namespace AZ
|
||||
|
||||
// Get full list of functions eligible for vertex shader entry points
|
||||
// along with metadata for constructing the InputAssembly for each of them
|
||||
if (!azslc.ParseIaPopulateFunctionData(outcomes[AzslSubProducts::ia].GetValue(), azslData.m_topData.m_functions))
|
||||
if (!azslc.ParseIaPopulateFunctionData(outcomes[AzslSubProducts::ia].GetValue(), azslData.m_functions))
|
||||
{
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// Each SRG is built as a separate asset in the SrgLayoutBuilder, here we just
|
||||
// build the list and load the data from multiple dependency assets.
|
||||
if (!azslc.ParseSrgPopulateSrgData(outcomes[AzslSubProducts::srg].GetValue(), azslData.m_topData.m_srgData))
|
||||
if (!azslc.ParseSrgPopulateSrgData(outcomes[AzslSubProducts::srg].GetValue(), azslData.m_srgData))
|
||||
{
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// Add all Shader Resource Group Assets that were defined in the shader code to the shader asset
|
||||
if (!LoadShaderResourceGroupAssets(BuilderName, azslData.m_topData.m_srgData, srgAssets))
|
||||
if (!LoadShaderResourceGroupAssets(builderName, azslData.m_srgData, srgAssets))
|
||||
{
|
||||
AZ_Error(BuilderName, false, "Failed to obtain shader resource group assets");
|
||||
AZ_Error(builderName, false, "Failed to obtain shader resource group assets");
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
@@ -238,7 +229,7 @@ namespace AZ
|
||||
// for each option and what is its default value.
|
||||
if (!azslc.ParseOptionsPopulateOptionGroupLayout(outcomes[AzslSubProducts::options].GetValue(), shaderOptionGroupLayout))
|
||||
{
|
||||
AZ_Error(BuilderName, false, "Failed to find a valid list of shader options!");
|
||||
AZ_Error(builderName, false, "Failed to find a valid list of shader options!");
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
@@ -246,14 +237,100 @@ namespace AZ
|
||||
// and informs us on register indexes and shader stages using these resources
|
||||
if (!azslc.ParseBindingdepPopulateBindingDependencies(outcomes[AzslSubProducts::bindingdep].GetValue(), bindingDependencies)) // consuming data from binding-dep
|
||||
{
|
||||
AZ_Error(BuilderName, false, "Failed to obtain shader resource binding reflection");
|
||||
AZ_Error(builderName, false, "Failed to obtain shader resource binding reflection");
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// access the root constants reflection
|
||||
if (!azslc.ParseSrgPopulateRootConstantData(outcomes[AzslSubProducts::srg].GetValue(), rootConstantData)) // consuming data from --srg ("InlineConstantBuffer" subjson section)
|
||||
{
|
||||
AZ_Error(BuilderName, false, "Failed to obtain root constant data reflection");
|
||||
AZ_Error(builderName, false, "Failed to obtain root constant data reflection");
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
return AssetBuilderSDK::ProcessJobResult_Success;
|
||||
}
|
||||
|
||||
|
||||
AssetBuilderSDK::ProcessJobResultCode PopulateAzslDataFromJsonFiles(
|
||||
const char* builderName,
|
||||
const AzslSubProducts::Paths& pathOfJsonFiles,
|
||||
const bool platformUsesRegisterSpaces,
|
||||
AzslData& azslData,
|
||||
RPI::ShaderResourceGroupLayoutList& srgLayoutList,
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout,
|
||||
BindingDependencies& bindingDependencies,
|
||||
RootConstantData& rootConstantData)
|
||||
{
|
||||
AzslCompiler azslc(
|
||||
azslData
|
||||
.m_preprocessedFullPath); // set the input file for eventual error messages, but the compiler won't be called on it.
|
||||
bool allReadSuccess = true;
|
||||
// read: input assembly reflection
|
||||
// shader resource group reflection
|
||||
// options reflection
|
||||
// binding dependencies reflection
|
||||
int indicesOfInterest[] = {
|
||||
AzslSubProducts::ia, AzslSubProducts::srg, AzslSubProducts::options, AzslSubProducts::bindingdep};
|
||||
AZStd::unordered_map<int, Outcome<rapidjson::Document, AZStd::string>> outcomes;
|
||||
for (int i : indicesOfInterest)
|
||||
{
|
||||
outcomes[i] = JsonSerializationUtils::ReadJsonFile(pathOfJsonFiles[i]);
|
||||
if (!outcomes[i].IsSuccess())
|
||||
{
|
||||
AZ_Error(builderName, false, "%s", outcomes[i].GetError().c_str());
|
||||
allReadSuccess = false;
|
||||
}
|
||||
}
|
||||
if (!allReadSuccess)
|
||||
{
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// Get full list of functions eligible for vertex shader entry points
|
||||
// along with metadata for constructing the InputAssembly for each of them
|
||||
if (!azslc.ParseIaPopulateFunctionData(outcomes[AzslSubProducts::ia].GetValue(), azslData.m_functions))
|
||||
{
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// Each SRG is built as a separate asset in the SrgLayoutBuilder, here we just
|
||||
// build the list and load the data from multiple dependency assets.
|
||||
if (!azslc.ParseSrgPopulateSrgData(outcomes[AzslSubProducts::srg].GetValue(), azslData.m_srgData))
|
||||
{
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// Add all Shader Resource Group Assets that were defined in the shader code to the shader asset
|
||||
if (!SrgLayoutUtility::LoadShaderResourceGroupLayouts(builderName, azslData.m_srgData, platformUsesRegisterSpaces, srgLayoutList))
|
||||
{
|
||||
AZ_Error(builderName, false, "Failed to obtain shader resource group assets");
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// The shader options define what options are available, what are the allowed values/range
|
||||
// for each option and what is its default value.
|
||||
if (!azslc.ParseOptionsPopulateOptionGroupLayout(outcomes[AzslSubProducts::options].GetValue(), shaderOptionGroupLayout))
|
||||
{
|
||||
AZ_Error(builderName, false, "Failed to find a valid list of shader options!");
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// It analyzes the shader external bindings (all SRG contents)
|
||||
// and informs us on register indexes and shader stages using these resources
|
||||
if (!azslc.ParseBindingdepPopulateBindingDependencies(
|
||||
outcomes[AzslSubProducts::bindingdep].GetValue(), bindingDependencies)) // consuming data from binding-dep
|
||||
{
|
||||
AZ_Error(builderName, false, "Failed to obtain shader resource binding reflection");
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
// access the root constants reflection
|
||||
if (!azslc.ParseSrgPopulateRootConstantData(
|
||||
outcomes[AzslSubProducts::srg].GetValue(),
|
||||
rootConstantData)) // consuming data from --srg ("InlineConstantBuffer" subjson section)
|
||||
{
|
||||
AZ_Error(builderName, false, "Failed to obtain root constant data reflection");
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
@@ -312,7 +389,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> BuildPipelineLayoutDescriptorForApi(
|
||||
[[maybe_unused]] const char* BuilderName,
|
||||
[[maybe_unused]] const char* builderName,
|
||||
RHI::ShaderPlatformInterface* shaderPlatformInterface,
|
||||
BindingDependencies& bindingDependencies /*inout*/,
|
||||
const ShaderResourceGroupAssets& srgAssets,
|
||||
@@ -356,7 +433,7 @@ namespace AZ
|
||||
const BindingDependencies::SrgResources* srgResources = bindingDependencies.GetSrg(srgName);
|
||||
if (!srgResources)
|
||||
{
|
||||
AZ_Error(BuilderName, false, "SRG %s not found in the dependency dataset", srgName.data());
|
||||
AZ_Error(builderName, false, "SRG %s not found in the dependency dataset", srgName.data());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -385,23 +462,21 @@ namespace AZ
|
||||
for (const auto& constantData : rootConstantData->m_constants)
|
||||
{
|
||||
RHI::ShaderInputConstantDescriptor rootConstantDesc(
|
||||
constantData.m_nameId,
|
||||
constantData.m_constantByteOffset,
|
||||
constantData.m_constantByteSize,
|
||||
constantData.m_nameId, constantData.m_constantByteOffset, constantData.m_constantByteSize,
|
||||
rootConstantData->m_bindingInfo.m_registerId);
|
||||
|
||||
|
||||
rootConstantsLayout->AddShaderInput(rootConstantDesc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!rootConstantsLayout->Finalize())
|
||||
{
|
||||
AZ_Error(BuilderName, false, "Failed to finalize root constants layout");
|
||||
AZ_Error(builderName, false, "Failed to finalize root constants layout");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pipelineLayoutDescriptor->SetRootConstantsLayout(*rootConstantsLayout);
|
||||
|
||||
|
||||
RHI::ShaderPlatformInterface::RootConstantsInfo rootConstantInfo;
|
||||
if (rootConstantData)
|
||||
{
|
||||
@@ -415,14 +490,15 @@ namespace AZ
|
||||
rootConstantInfo.m_registerId = dummyRootConstantData.m_bindingInfo.m_registerId;
|
||||
}
|
||||
rootConstantInfo.m_totalSizeInBytes = rootConstantsLayout->GetDataSize();
|
||||
|
||||
|
||||
// Build platform-specific PipelineLayoutDescriptor data, and finalize
|
||||
if (!shaderPlatformInterface->BuildPipelineLayoutDescriptor(pipelineLayoutDescriptor, srgInfos, rootConstantInfo, shaderCompilerArguments))
|
||||
if (!shaderPlatformInterface->BuildPipelineLayoutDescriptor(
|
||||
pipelineLayoutDescriptor, srgInfos, rootConstantInfo, shaderCompilerArguments))
|
||||
{
|
||||
AZ_Error(BuilderName, false, "Failed to build pipeline layout descriptor");
|
||||
AZ_Error(builderName, false, "Failed to build pipeline layout descriptor");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
return pipelineLayoutDescriptor;
|
||||
}
|
||||
|
||||
@@ -442,7 +518,7 @@ namespace AZ
|
||||
}
|
||||
else
|
||||
{
|
||||
formatted = AZStd::string::format("%s.%s.%s", stemName.c_str(), apiTypeString.c_str(), extension.c_str());
|
||||
formatted = AZStd::string::format("%s_%s.%s", stemName.c_str(), apiTypeString.c_str(), extension.c_str());
|
||||
}
|
||||
AzFramework::StringFunc::Path::Join(dumpDirectory.c_str(), formatted.c_str(), finalFilePath, true, true);
|
||||
AZ::IO::FileIOStream outFileStream(finalFilePath.data(), AZ::IO::OpenMode::ModeWrite);
|
||||
@@ -463,14 +539,20 @@ namespace AZ
|
||||
return finalFilePath;
|
||||
}
|
||||
|
||||
AZStd::string DumpPreprocessedCode(const char* builderName, const AZStd::string& preprocessedCode, const AZStd::string& tempDirPath, const AZStd::string& stemName, const AZStd::string& apiTypeString)
|
||||
// [GFX TODO] Remove 'add2' when [ATOM-15472]
|
||||
AZStd::string DumpPreprocessedCode(const char* builderName, const AZStd::string& preprocessedCode, const AZStd::string& tempDirPath, const AZStd::string& stemName, const AZStd::string& apiTypeString, bool add2)
|
||||
{
|
||||
if (add2)
|
||||
{
|
||||
return DumpCode(builderName, preprocessedCode, tempDirPath, stemName, apiTypeString, "azslin2");
|
||||
}
|
||||
|
||||
return DumpCode(builderName, preprocessedCode, tempDirPath, stemName, apiTypeString, "azslin");
|
||||
}
|
||||
|
||||
AZStd::string DumpAzslPrependedCode(const char* builderName, const AZStd::string& nonPreprocessedYetAzslSource, const AZStd::string& tempDirPath, const AZStd::string& stemName, const AZStd::string& apiTypeString)
|
||||
{
|
||||
return DumpCode(builderName, nonPreprocessedYetAzslSource, tempDirPath, stemName, apiTypeString, "azsl.prepend");
|
||||
return DumpCode(builderName, nonPreprocessedYetAzslSource, tempDirPath, stemName, apiTypeString, "azslprepend");
|
||||
}
|
||||
|
||||
AZStd::string ExtractStemName(const char* path)
|
||||
@@ -489,6 +571,83 @@ namespace AZ
|
||||
return platformInterfaces;
|
||||
}
|
||||
|
||||
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> DiscoverEnabledShaderPlatformInterfaces(const AssetBuilderSDK::PlatformInfo& info, const RPI::ShaderSourceData& shaderSourceData)
|
||||
{
|
||||
// Request the list of valid shader platform interfaces for the target platform.
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces;
|
||||
ShaderPlatformInterfaceRequestBus::BroadcastResult(
|
||||
platformInterfaces, &ShaderPlatformInterfaceRequest::GetShaderPlatformInterface, info);
|
||||
|
||||
// Let's remove the unwanted RHI interfaces from the list.
|
||||
platformInterfaces.erase(
|
||||
AZStd::remove_if(AZ_BEGIN_END(platformInterfaces),
|
||||
[&](const RHI::ShaderPlatformInterface* shaderPlatformInterface) {
|
||||
return !shaderPlatformInterface ||
|
||||
shaderSourceData.IsRhiBackendDisabled(shaderPlatformInterface->GetAPIName()) ||
|
||||
(shaderPlatformInterface->GetAPIUniqueIndex() == static_cast<uint32_t>(AZ::RHI::APIIndex::Null));
|
||||
}),
|
||||
platformInterfaces.end());
|
||||
return platformInterfaces;
|
||||
}
|
||||
|
||||
static bool IsValidSupervariantName(const AZStd::string& supervariantName)
|
||||
{
|
||||
return AZStd::all_of(AZ_BEGIN_END(supervariantName),
|
||||
[](AZStd::string::value_type ch)
|
||||
{
|
||||
return AZStd::is_alnum(ch); // allow alpha numeric only
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
AZStd::vector<RPI::ShaderSourceData::SupervariantInfo> GetSupervariantListFromShaderSourceData(
|
||||
const RPI::ShaderSourceData& shaderSourceData)
|
||||
{
|
||||
AZStd::vector<RPI::ShaderSourceData::SupervariantInfo> supervariants;
|
||||
supervariants.reserve(shaderSourceData.m_supervariants.size() + 1);
|
||||
|
||||
// Add the supervariants, always making sure that:
|
||||
// 1- The default, nameless, supervariant goes to the front.
|
||||
// 2- Each supervariant has a unique name
|
||||
AZStd::unordered_set<AZ::Name> uniqueSuperVariants; // This set helps duplicate detection.
|
||||
// Although it is not common, it is possible to declare a nameless supervariant.
|
||||
bool addedNamelessSupervariant = false;
|
||||
for (const auto& supervariantInfo : shaderSourceData.m_supervariants)
|
||||
{
|
||||
if (!IsValidSupervariantName(supervariantInfo.m_name.GetStringView()))
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderBuilderUtilityName, false, "The supervariant name: [%s] contains invalid characters. Only [a-zA-Z0-9] are supported",
|
||||
supervariantInfo.m_name.GetCStr());
|
||||
return {}; // Return an empty vector.
|
||||
}
|
||||
if (uniqueSuperVariants.count(supervariantInfo.m_name))
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderBuilderUtilityName, false, "It is invalid to specify more than one supervariant with the same name: [%s]",
|
||||
supervariantInfo.m_name.GetCStr());
|
||||
return {}; // Return an empty vector.
|
||||
}
|
||||
uniqueSuperVariants.emplace(supervariantInfo.m_name);
|
||||
supervariants.push_back(supervariantInfo);
|
||||
if (supervariantInfo.m_name.IsEmpty())
|
||||
{
|
||||
addedNamelessSupervariant = true;
|
||||
// Always move the default, nameless, variant to the begining of the list.
|
||||
AZStd::swap(supervariants.front(), supervariants.back());
|
||||
}
|
||||
}
|
||||
if (!addedNamelessSupervariant)
|
||||
{
|
||||
supervariants.push_back({});
|
||||
// Always move the default, nameless, variant to the begining of the list.
|
||||
AZStd::swap(supervariants.front(), supervariants.back());
|
||||
}
|
||||
|
||||
return supervariants;
|
||||
}
|
||||
|
||||
static void ReadShaderCompilerProfiling([[maybe_unused]] const char* builderName, RHI::ShaderCompilerProfiling& shaderCompilerProfiling, AZStd::string_view shaderPath)
|
||||
{
|
||||
AZStd::string folderPath;
|
||||
@@ -561,12 +720,64 @@ namespace AZ
|
||||
|
||||
uint32_t MakeAzslBuildProductSubId(RPI::ShaderAssetSubId subId, RHI::APIType apiType)
|
||||
{
|
||||
auto subIdMaxEnumerator = RPI::ShaderAssetSubId::GeneratedSource;
|
||||
auto subIdMaxEnumerator = RPI::ShaderAssetSubId::GeneratedHlslSource;
|
||||
// separate bit space between subid enum, and api-type:
|
||||
int shiftLeft = static_cast<uint32_t>(log2(static_cast<uint32_t>(subIdMaxEnumerator))) + 1;
|
||||
return static_cast<uint32_t>(subId) + (apiType << shiftLeft);
|
||||
}
|
||||
|
||||
Outcome<AZStd::string, AZStd::string> ObtainBuildArtifactPathFromShaderAssetBuilder2(
|
||||
const uint32_t rhiUniqueIndex, const AZStd::string& platformIdentifier, const AZStd::string& shaderJsonPath,
|
||||
const uint32_t supervariantIndex, RPI::ShaderAssetSubId shaderAssetSubId)
|
||||
{
|
||||
// platform id from identifier
|
||||
AzFramework::PlatformId platformId = AzFramework::PlatformId::PC;
|
||||
if (platformIdentifier == "pc")
|
||||
{
|
||||
platformId = AzFramework::PlatformId::PC;
|
||||
}
|
||||
else if (platformIdentifier == "osx_gl")
|
||||
{
|
||||
platformId = AzFramework::PlatformId::OSX;
|
||||
}
|
||||
else if (platformIdentifier == "es3")
|
||||
{
|
||||
platformId = AzFramework::PlatformId::ES3;
|
||||
}
|
||||
else if (platformIdentifier == "ios")
|
||||
{
|
||||
platformId = AzFramework::PlatformId::IOS;
|
||||
}
|
||||
|
||||
uint32_t assetSubId = RPI::ShaderAsset2::MakeProductAssetSubId(rhiUniqueIndex, supervariantIndex, aznumeric_cast<uint32_t>(shaderAssetSubId));
|
||||
auto assetIdOutcome = RPI::AssetUtils::MakeAssetId(shaderJsonPath, assetSubId);
|
||||
if (!assetIdOutcome.IsSuccess())
|
||||
{
|
||||
return Failure(AZStd::string::format(
|
||||
"Missing ShaderAssetBuilder2 product %s, for sub %d", shaderJsonPath.c_str(), (uint32_t)shaderAssetSubId));
|
||||
}
|
||||
|
||||
Data::AssetId assetId = assetIdOutcome.TakeValue();
|
||||
// get the relative path:
|
||||
AZStd::string assetPath;
|
||||
Data::AssetCatalogRequestBus::BroadcastResult(assetPath, &Data::AssetCatalogRequests::GetAssetPathById, assetId);
|
||||
|
||||
// get the root:
|
||||
AZStd::string assetRoot = AzToolsFramework::PlatformAddressedAssetCatalog::GetAssetRootForPlatform(platformId);
|
||||
// join
|
||||
AZStd::string assetFullPath;
|
||||
AzFramework::StringFunc::Path::Join(assetRoot.c_str(), assetPath.c_str(), assetFullPath);
|
||||
bool fileExists = IO::FileIOBase::GetInstance()->Exists(assetFullPath.c_str()) &&
|
||||
!IO::FileIOBase::GetInstance()->IsDirectory(assetFullPath.c_str());
|
||||
if (!fileExists)
|
||||
{
|
||||
return Failure(AZStd::string::format(
|
||||
"asset [%s] from shader source %s and subId %d doesn't exist", assetFullPath.c_str(), shaderJsonPath.c_str(),
|
||||
(uint32_t)shaderAssetSubId));
|
||||
}
|
||||
return AZ::Success(assetFullPath);
|
||||
}
|
||||
|
||||
Outcome<AzslSubProducts::Paths> ObtainBuildArtifactsFromAzslBuilder([[maybe_unused]] const char* builderName, const AZStd::string& sourceFullPath, RHI::APIType apiType, const AZStd::string& platform)
|
||||
{
|
||||
AzslSubProducts::Paths products;
|
||||
@@ -619,6 +830,7 @@ namespace AZ
|
||||
return AZ::Success(products);
|
||||
}
|
||||
|
||||
// DEPRECATED [ATOM-15472]
|
||||
// See header for info.
|
||||
// REMARK: The approach to string searching and matching done in this function is kind of naive
|
||||
// because the strings can match text within a comment block, etc. So it is not 100% fool proof.
|
||||
@@ -672,6 +884,399 @@ namespace AZ
|
||||
|
||||
return SrgSkipFileResult::ContinueProcess;
|
||||
}
|
||||
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> BuildPipelineLayoutDescriptorForApi(
|
||||
const char* builderName, const RPI::ShaderResourceGroupLayoutList& srgLayoutList, const MapOfStringToStageType& shaderEntryPoints,
|
||||
const RHI::ShaderCompilerArguments& shaderCompilerArguments, const RootConstantData& rootConstantData,
|
||||
RHI::ShaderPlatformInterface* shaderPlatformInterface, BindingDependencies& bindingDependencies /*inout*/)
|
||||
{
|
||||
PruneNonEntryFunctions(bindingDependencies, shaderEntryPoints);
|
||||
|
||||
// Translates from a list of function names that use a resource to a shader stage mask.
|
||||
auto getRHIShaderStageMask = [&shaderEntryPoints](const BindingDependencies::FunctionsNameVector& functions) {
|
||||
RHI::ShaderStageMask mask = RHI::ShaderStageMask::None;
|
||||
// Iterate through all the functions that are using the resource.
|
||||
for (const auto& functionName : functions)
|
||||
{
|
||||
// Search the function name into the list of valid entry points into the shader.
|
||||
auto findId =
|
||||
AZStd::find_if(shaderEntryPoints.begin(), shaderEntryPoints.end(), [&functionName, &mask](const auto& item) {
|
||||
return item.first == functionName;
|
||||
});
|
||||
|
||||
if (findId != shaderEntryPoints.end())
|
||||
{
|
||||
// Use the entry point shader stage type to calculate the mask.
|
||||
RHI::ShaderHardwareStage hardwareStage = ToAssetBuilderShaderType(findId->second);
|
||||
mask |= static_cast<RHI::ShaderStageMask>(AZ_BIT(static_cast<uint32_t>(RHI::ToRHIShaderStage(hardwareStage))));
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
};
|
||||
|
||||
// Build general PipelineLayoutDescriptor data that is provided for all platforms
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> pipelineLayoutDescriptor =
|
||||
shaderPlatformInterface->CreatePipelineLayoutDescriptor();
|
||||
RHI::ShaderPlatformInterface::ShaderResourceGroupInfoList srgInfos;
|
||||
for (const auto& srgLayout : srgLayoutList)
|
||||
{
|
||||
// Search the binding info for a Shader Resource Group.
|
||||
AZStd::string_view srgName = srgLayout->GetName().GetStringView();
|
||||
const BindingDependencies::SrgResources* srgResources = bindingDependencies.GetSrg(srgName);
|
||||
if (!srgResources)
|
||||
{
|
||||
AZ_Error(builderName, false, "SRG %s not found in the dependency dataset", srgName.data());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RHI::ShaderResourceGroupBindingInfo srgBindingInfo;
|
||||
srgBindingInfo.m_spaceId = srgResources->m_registerSpace;
|
||||
const RHI::ShaderResourceGroupLayout* layout = srgLayout.get();
|
||||
// Calculate the binding in for the constant data. All constant data share the same binding info.
|
||||
srgBindingInfo.m_constantDataBindingInfo = {
|
||||
getRHIShaderStageMask(srgResources->m_srgConstantsDependencies.m_binding.m_dependentFunctions),
|
||||
srgResources->m_srgConstantsDependencies.m_binding.m_registerId};
|
||||
// Calculate the binding info for each resource of the Shader Resource Group.
|
||||
for (auto const& resource : srgResources->m_resources)
|
||||
{
|
||||
auto const& resourceInfo = resource.second;
|
||||
srgBindingInfo.m_resourcesRegisterMap.insert(
|
||||
{AZ::Name(resourceInfo.m_selfName),
|
||||
RHI::ResourceBindingInfo(
|
||||
getRHIShaderStageMask(resourceInfo.m_dependentFunctions), resourceInfo.m_registerId)});
|
||||
}
|
||||
pipelineLayoutDescriptor->AddShaderResourceGroupLayoutInfo(*layout, srgBindingInfo);
|
||||
srgInfos.push_back(RHI::ShaderPlatformInterface::ShaderResourceGroupInfo{layout, srgBindingInfo});
|
||||
}
|
||||
|
||||
RHI::Ptr<RHI::ConstantsLayout> rootConstantsLayout = RHI::ConstantsLayout::Create();
|
||||
for (const auto& constantData : rootConstantData.m_constants)
|
||||
{
|
||||
RHI::ShaderInputConstantDescriptor rootConstantDesc(
|
||||
constantData.m_nameId, constantData.m_constantByteOffset, constantData.m_constantByteSize,
|
||||
rootConstantData.m_bindingInfo.m_registerId);
|
||||
|
||||
rootConstantsLayout->AddShaderInput(rootConstantDesc);
|
||||
}
|
||||
|
||||
|
||||
if (!rootConstantsLayout->Finalize())
|
||||
{
|
||||
AZ_Error(builderName, false, "Failed to finalize root constants layout");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pipelineLayoutDescriptor->SetRootConstantsLayout(*rootConstantsLayout);
|
||||
|
||||
RHI::ShaderPlatformInterface::RootConstantsInfo rootConstantInfo;
|
||||
rootConstantInfo.m_spaceId = rootConstantData.m_bindingInfo.m_space;
|
||||
rootConstantInfo.m_registerId = rootConstantData.m_bindingInfo.m_registerId;
|
||||
rootConstantInfo.m_totalSizeInBytes = rootConstantsLayout->GetDataSize();
|
||||
|
||||
// Build platform-specific PipelineLayoutDescriptor data, and finalize
|
||||
if (!shaderPlatformInterface->BuildPipelineLayoutDescriptor(
|
||||
pipelineLayoutDescriptor, srgInfos, rootConstantInfo, shaderCompilerArguments))
|
||||
{
|
||||
AZ_Error(builderName, false, "Failed to build pipeline layout descriptor");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pipelineLayoutDescriptor;
|
||||
}
|
||||
|
||||
static bool IsSystemValueSemantic(const AZStd::string_view semantic)
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics#system-value-semantics
|
||||
return AzFramework::StringFunc::StartsWith(semantic, "sv_", false);
|
||||
}
|
||||
|
||||
static bool CreateShaderInputContract(
|
||||
const AzslData& azslData,
|
||||
const AZStd::string& vertexShaderName,
|
||||
const RPI::ShaderOptionGroupLayout& shaderOptionGroupLayout,
|
||||
const AZStd::string& pathToIaJson,
|
||||
RPI::ShaderInputContract& contract)
|
||||
{
|
||||
StructData inputStruct;
|
||||
inputStruct.m_id = "";
|
||||
|
||||
auto jsonOutcome = JsonSerializationUtils::ReadJsonFile(pathToIaJson);
|
||||
if (!jsonOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderBuilderUtilityName, false, "%s", jsonOutcome.GetError().c_str());
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
AzslCompiler azslc(azslData.m_preprocessedFullPath);
|
||||
if (!azslc.ParseIaPopulateStructData(jsonOutcome.GetValue(), vertexShaderName, inputStruct))
|
||||
{
|
||||
AZ_Error(ShaderBuilderUtilityName, false, "Failed to parse input layout\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inputStruct.m_id.empty())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderBuilderUtilityName, false, "Failed to find the input struct for vertex shader %s.",
|
||||
vertexShaderName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& member : inputStruct.m_members)
|
||||
{
|
||||
RHI::ShaderSemantic streamChannelSemantic{Name{member.m_semanticText}, static_cast<uint32_t>(member.m_semanticIndex)};
|
||||
|
||||
// Semantics that represent a system-generated value do not map to an input stream
|
||||
if (IsSystemValueSemantic(streamChannelSemantic.m_name.GetStringView()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
contract.m_streamChannels.push_back();
|
||||
contract.m_streamChannels.back().m_semantic = streamChannelSemantic;
|
||||
|
||||
if (member.m_variable.m_typeModifier == MatrixMajor::ColumnMajor)
|
||||
{
|
||||
contract.m_streamChannels.back().m_componentCount = member.m_variable.m_cols;
|
||||
}
|
||||
else
|
||||
{
|
||||
contract.m_streamChannels.back().m_componentCount = member.m_variable.m_rows;
|
||||
}
|
||||
|
||||
// [GFX_TODO][ATOM-14475]: Come up with a more elegant way to mark optional channels and their corresponding shader
|
||||
// option
|
||||
static const char OptionalInputStreamPrefix[] = "m_optional_";
|
||||
if (AzFramework::StringFunc::StartsWith(member.m_variable.m_name, OptionalInputStreamPrefix, true))
|
||||
{
|
||||
AZStd::string expectedOptionName = AZStd::string::format(
|
||||
"o_%s_isBound", member.m_variable.m_name.substr(strlen(OptionalInputStreamPrefix)).c_str());
|
||||
|
||||
RPI::ShaderOptionIndex shaderOptionIndex = shaderOptionGroupLayout.FindShaderOptionIndex(Name{expectedOptionName});
|
||||
if (!shaderOptionIndex.IsValid())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderBuilderUtilityName, false, "Shader option '%s' not found for optional input stream '%s'",
|
||||
expectedOptionName.c_str(), member.m_variable.m_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const RPI::ShaderOptionDescriptor& option = shaderOptionGroupLayout.GetShaderOption(shaderOptionIndex);
|
||||
if (option.GetType() != RPI::ShaderOptionType::Boolean)
|
||||
{
|
||||
AZ_Error(ShaderBuilderUtilityName, false, "Shader option '%s' must be a bool.", expectedOptionName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (option.GetDefaultValue().GetStringView() != "false")
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderBuilderUtilityName, false, "Shader option '%s' must default to false.",
|
||||
expectedOptionName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
contract.m_streamChannels.back().m_isOptional = true;
|
||||
contract.m_streamChannels.back().m_streamBoundIndicatorIndex = shaderOptionIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CreateShaderOutputContract(
|
||||
const AzslData& azslData,
|
||||
const AZStd::string& fragmentShaderName,
|
||||
const AZStd::string& pathToOmJson,
|
||||
RPI::ShaderOutputContract& contract)
|
||||
{
|
||||
StructData outputStruct;
|
||||
outputStruct.m_id = "";
|
||||
|
||||
auto jsonOutcome = JsonSerializationUtils::ReadJsonFile(pathToOmJson);
|
||||
if (!jsonOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderBuilderUtilityName, false, "%s", jsonOutcome.GetError().c_str());
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
AzslCompiler azslc(azslData.m_preprocessedFullPath);
|
||||
if (!azslc.ParseOmPopulateStructData(jsonOutcome.GetValue(), fragmentShaderName, outputStruct))
|
||||
{
|
||||
AZ_Error(ShaderBuilderUtilityName, false, "Failed to parse output layout\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& member : outputStruct.m_members)
|
||||
{
|
||||
RHI::ShaderSemantic semantic = RHI::ShaderSemantic::Parse(member.m_semanticText);
|
||||
|
||||
bool depthFound = false;
|
||||
|
||||
if (semantic.m_name.GetStringView() == "SV_Target")
|
||||
{
|
||||
contract.m_requiredColorAttachments.push_back();
|
||||
// Render targets only support 1-D vector types and those are always column-major (per DXC)
|
||||
contract.m_requiredColorAttachments.back().m_componentCount = member.m_variable.m_cols;
|
||||
}
|
||||
else if (
|
||||
semantic.m_name.GetStringView() == "SV_Depth" || semantic.m_name.GetStringView() == "SV_DepthGreaterEqual" ||
|
||||
semantic.m_name.GetStringView() == "SV_DepthLessEqual")
|
||||
{
|
||||
if (depthFound)
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderBuilderUtilityName, false,
|
||||
"SV_Depth specified more than once in the fragment shader output structure");
|
||||
return false;
|
||||
}
|
||||
depthFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderBuilderUtilityName, false, "Unsupported shader output semantic '%s'.", semantic.m_name.GetCStr());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateShaderInputAndOutputContracts(
|
||||
const AzslData& azslData,
|
||||
const MapOfStringToStageType& shaderEntryPoints,
|
||||
const RPI::ShaderOptionGroupLayout& shaderOptionGroupLayout,
|
||||
const AZStd::string& pathToOmJson,
|
||||
const AZStd::string& pathToIaJson,
|
||||
RPI::ShaderInputContract& shaderInputContract,
|
||||
RPI::ShaderOutputContract& shaderOutputContract,
|
||||
size_t& colorAttachmentCount)
|
||||
{
|
||||
bool success = true;
|
||||
for (const auto& shaderEntryPoint : shaderEntryPoints)
|
||||
{
|
||||
auto shaderEntryName = shaderEntryPoint.first;
|
||||
auto shaderStageType = shaderEntryPoint.second;
|
||||
|
||||
if (shaderStageType == RPI::ShaderStageType::Vertex)
|
||||
{
|
||||
const bool layoutCreated = CreateShaderInputContract(azslData, shaderEntryName, shaderOptionGroupLayout, pathToIaJson, shaderInputContract);
|
||||
if (!layoutCreated)
|
||||
{
|
||||
success = false;
|
||||
AZ_Error(
|
||||
ShaderBuilderUtilityName, false, "Could not create the input contract for the vertex function %s",
|
||||
shaderEntryName.c_str());
|
||||
continue; // Using continue to report all the errors found
|
||||
}
|
||||
}
|
||||
|
||||
if (shaderStageType == RPI::ShaderStageType::Fragment)
|
||||
{
|
||||
const bool layoutCreated =
|
||||
CreateShaderOutputContract(azslData, shaderEntryName, pathToOmJson, shaderOutputContract);
|
||||
if (!layoutCreated)
|
||||
{
|
||||
success = false;
|
||||
AZ_Error(
|
||||
ShaderBuilderUtilityName, false, "Could not create the output contract for the fragment function %s",
|
||||
shaderEntryName.c_str());
|
||||
continue; // Using continue to report all the errors found
|
||||
}
|
||||
|
||||
colorAttachmentCount = shaderOutputContract.m_requiredColorAttachments.size();
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
//! Returns a list of acceptable default entry point names
|
||||
static void GetAcceptableDefaultEntryPoints(
|
||||
const AZStd::vector<FunctionData>& azslFunctionDataList,
|
||||
AZStd::unordered_map<AZStd::string, RPI::ShaderStageType>& defaultEntryPoints)
|
||||
{
|
||||
for (const auto& func : azslFunctionDataList)
|
||||
{
|
||||
if (!func.m_hasShaderStageVaryings)
|
||||
{
|
||||
// Not declaring any semantics for a shader entry is valid, but unusual.
|
||||
// A shader entry with no semantics must be explicitly listed and won't be selected by default.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (func.m_name.starts_with("VS") || func.m_name.ends_with("VS"))
|
||||
{
|
||||
defaultEntryPoints[func.m_name] = RPI::ShaderStageType::Vertex;
|
||||
AZ_TracePrintf(
|
||||
ShaderBuilderUtilityName, "Assuming \"%s\" is a valid Vertex shader entry point.\n", func.m_name.c_str());
|
||||
}
|
||||
else if (func.m_name.starts_with("PS") || func.m_name.ends_with("PS"))
|
||||
{
|
||||
defaultEntryPoints[func.m_name] = RPI::ShaderStageType::Fragment;
|
||||
AZ_TracePrintf(
|
||||
ShaderBuilderUtilityName, "Assuming \"%s\" is a valid Fragment shader entry point.\n",
|
||||
func.m_name.c_str());
|
||||
}
|
||||
else if (func.m_name.starts_with("CS") || func.m_name.ends_with("CS"))
|
||||
{
|
||||
defaultEntryPoints[func.m_name] = RPI::ShaderStageType::Compute;
|
||||
AZ_TracePrintf(
|
||||
ShaderBuilderUtilityName, "Assuming \"%s\" is a valid Compute shader entry point.\n", func.m_name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// DEPRECATED [ATOM-15472
|
||||
//! Returns a list of acceptable default entry point names
|
||||
//! This function
|
||||
static void GetAcceptableDefaultEntryPoints(
|
||||
const AzslData& azslData, AZStd::unordered_map<AZStd::string, RPI::ShaderStageType>& defaultEntryPoints)
|
||||
{
|
||||
return GetAcceptableDefaultEntryPoints(azslData.m_functions, defaultEntryPoints);
|
||||
}
|
||||
|
||||
|
||||
void GetDefaultEntryPointsFromFunctionDataList(
|
||||
const AZStd::vector<FunctionData> azslFunctionDataList,
|
||||
AZStd::unordered_map<AZStd::string, RPI::ShaderStageType>& shaderEntryPoints)
|
||||
{
|
||||
AZStd::unordered_map<AZStd::string, RPI::ShaderStageType> defaultEntryPoints;
|
||||
GetAcceptableDefaultEntryPoints(azslFunctionDataList, defaultEntryPoints);
|
||||
|
||||
for (const auto& functionData : azslFunctionDataList)
|
||||
{
|
||||
for (const auto& defaultEntryPoint : defaultEntryPoints)
|
||||
{
|
||||
// Equal defaults to case insensitive compares...
|
||||
if (AzFramework::StringFunc::Equal(defaultEntryPoint.first.c_str(), functionData.m_name.c_str()))
|
||||
{
|
||||
shaderEntryPoints[defaultEntryPoint.first] = defaultEntryPoint.second;
|
||||
break; // stop looping default entry points and go to the next shader function
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string GetAcceptableDefaultEntryPointNames(const AzslData& azslData)
|
||||
{
|
||||
AZStd::unordered_map<AZStd::string, RPI::ShaderStageType> defaultEntryPointList;
|
||||
GetAcceptableDefaultEntryPoints(azslData, defaultEntryPointList);
|
||||
|
||||
AZStd::vector<AZStd::string> defaultEntryPointNamesList;
|
||||
for (const auto& shaderEntryPoint : defaultEntryPointList)
|
||||
{
|
||||
defaultEntryPointNamesList.push_back(shaderEntryPoint.first);
|
||||
}
|
||||
AZStd::string shaderEntryPoints;
|
||||
AzFramework::StringFunc::Join(
|
||||
shaderEntryPoints, defaultEntryPointNamesList.begin(), defaultEntryPointNamesList.end(), ", ");
|
||||
return AZStd::move(shaderEntryPoints);
|
||||
}
|
||||
|
||||
} // namespace ShaderBuilderUtility
|
||||
} // namespace ShaderBuilder
|
||||
} // AZ
|
||||
|
||||
@@ -18,8 +18,10 @@
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
|
||||
#include <Atom/RPI.Edit/Shader/ShaderSourceData.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderResourceGroupAsset.h>
|
||||
|
||||
#include "AzslData.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -27,7 +29,6 @@ namespace AZ
|
||||
{
|
||||
class AzslCompiler;
|
||||
struct ShaderFiles;
|
||||
struct AzslData;
|
||||
struct BindingDependencies;
|
||||
struct RootConstantData;
|
||||
|
||||
@@ -40,8 +41,6 @@ namespace AZ
|
||||
|
||||
void GetAbsolutePathToAzslFile(const AZStd::string& shaderTemplatePathAndFile, AZStd::string specifiedShaderPathAndName, AZStd::string& absoluteShaderPath);
|
||||
|
||||
uint32_t MakeDebugByproductSubId(RHI::APIType apiType, const AZStd::string& productFileName);
|
||||
|
||||
//! Opens and read the .shader, returns expanded file paths
|
||||
AZStd::shared_ptr<ShaderFiles> PrepareSourceInput(
|
||||
const char* builderName,
|
||||
@@ -54,13 +53,20 @@ namespace AZ
|
||||
|
||||
using SubId = RPI::ShaderAssetSubId;
|
||||
// product sub id enumerators:
|
||||
static constexpr SubId SubList[] = { SubId::PostPreprocessingPureAzsl, SubId::IaJson, SubId::OmJson, SubId::SrgJson, SubId::OptionsJson, SubId::BindingdepJson, SubId::GeneratedSource };
|
||||
static constexpr SubId SubList[] = {SubId::PostPreprocessingPureAzsl,
|
||||
SubId::IaJson,
|
||||
SubId::OmJson,
|
||||
SubId::SrgJson,
|
||||
SubId::OptionsJson,
|
||||
SubId::BindingdepJson,
|
||||
SubId::GeneratedHlslSource};
|
||||
// in the same order, their file name suffix (they replicate what's in AzslcMain.cpp. and hlsl corresponds to what's in AzslBuilder.cpp)
|
||||
|
||||
// a type to declare variables holding the full paths of their files
|
||||
using Paths = AZStd::fixed_vector<AZStd::string, AZ_ARRAY_SIZE(SubList)>;
|
||||
};
|
||||
|
||||
//! [GFX TODO] [ATOM-15472] Deprecated, remove when this ticket is addressed.
|
||||
//! Collects and generates the necessary data for compiling a shader.
|
||||
//! @azslData must have paths correctly set.
|
||||
//! shaderOptionGroupLayout, azslData, srgAssets get the output data.
|
||||
@@ -74,6 +80,16 @@ namespace AZ
|
||||
RootConstantData& rootConstantData
|
||||
);
|
||||
|
||||
//! Collects all the JSON files generated during AZSL compilation and loads the data as objects.
|
||||
//! @azslData must have paths correctly set.
|
||||
//! @azslData, @srgLayoutList, @shaderOptionGroupLayout, @bindingDependencies and @rootConstantData get the output data.
|
||||
AssetBuilderSDK::ProcessJobResultCode PopulateAzslDataFromJsonFiles(
|
||||
const char* builderName, const AzslSubProducts::Paths& pathOfJsonFiles,
|
||||
const bool platformUsesRegisterSpaces, AzslData& azslData,
|
||||
RPI::ShaderResourceGroupLayoutList& srgLayoutList, RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout,
|
||||
BindingDependencies& bindingDependencies, RootConstantData& rootConstantData);
|
||||
|
||||
|
||||
RHI::ShaderHardwareStage ToAssetBuilderShaderType(RPI::ShaderStageType stageType);
|
||||
|
||||
//! Must be called before shaderPlatformInterface->CompilePlatformInternal()
|
||||
@@ -82,7 +98,7 @@ namespace AZ
|
||||
//! The pipeline layout descriptor is returned, but the same data will also be set into the @shaderPlatformInterface
|
||||
//! object, which is why it is important to call this method before calling shaderPlatformInterface->CompilePlatformInternal().
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> BuildPipelineLayoutDescriptorForApi(
|
||||
const char* BuilderName,
|
||||
const char* builderName,
|
||||
RHI::ShaderPlatformInterface* shaderPlatformInterface,
|
||||
BindingDependencies& bindingDependencies /*inout*/,
|
||||
const ShaderResourceGroupAssets& srgAssets,
|
||||
@@ -91,6 +107,33 @@ namespace AZ
|
||||
const RootConstantData* rootConstantData = nullptr
|
||||
);
|
||||
|
||||
|
||||
//! Must be called before shaderPlatformInterface->CompilePlatformInternal()
|
||||
//! This function will prune non entry functions from BindingDependencies and use the
|
||||
//! rest of input data to create a pipeline layout descriptor.
|
||||
//! The pipeline layout descriptor is returned, but the same data will also be set into the @shaderPlatformInterface
|
||||
//! object, which is why it is important to call this method before calling shaderPlatformInterface->CompilePlatformInternal().
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> BuildPipelineLayoutDescriptorForApi(
|
||||
const char* builderName,
|
||||
const RPI::ShaderResourceGroupLayoutList& srgLayoutList,
|
||||
const MapOfStringToStageType& shaderEntryPoints,
|
||||
const RHI::ShaderCompilerArguments& shaderCompilerArguments,
|
||||
const RootConstantData& rootConstantData,
|
||||
RHI::ShaderPlatformInterface* shaderPlatformInterface,
|
||||
BindingDependencies& bindingDependencies /*inout*/);
|
||||
|
||||
|
||||
bool CreateShaderInputAndOutputContracts(
|
||||
const AzslData& azslData, const MapOfStringToStageType& shaderEntryPoints,
|
||||
const RPI::ShaderOptionGroupLayout& shaderOptionGroupLayout, const AZStd::string& pathToOmJson,
|
||||
const AZStd::string& pathToIaJson, RPI::ShaderInputContract& shaderInputContract,
|
||||
RPI::ShaderOutputContract& shaderOutputContract, size_t& colorAttachmentCount);
|
||||
|
||||
|
||||
//! Returns a list of acceptable default entry point names as a single string for debug messages.
|
||||
AZStd::string GetAcceptableDefaultEntryPointNames(const AzslData& shaderData);
|
||||
|
||||
|
||||
//! Create a file from a string's content.
|
||||
//! That file will be named filename.api.azslin
|
||||
//! This is meant to be used at this stage:
|
||||
@@ -102,7 +145,8 @@ namespace AZ
|
||||
const AZStd::string& preprocessedCode,
|
||||
const AZStd::string& tempDirPath,
|
||||
const AZStd::string& preprocessedFileName,
|
||||
const AZStd::string& apiTypeString = "");
|
||||
const AZStd::string& apiTypeString = "",
|
||||
bool add2 = false); // [GFX TODO] Remove add2 when [ATOM-15472]
|
||||
|
||||
//! Create a file from a string's content.
|
||||
//! That file will be named filename.api.azsl.prepend
|
||||
@@ -121,12 +165,30 @@ namespace AZ
|
||||
AZStd::string ExtractStemName(const char* path);
|
||||
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> DiscoverValidShaderPlatformInterfaces(const AssetBuilderSDK::PlatformInfo& info);
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> DiscoverEnabledShaderPlatformInterfaces(
|
||||
const AssetBuilderSDK::PlatformInfo& info, const RPI::ShaderSourceData& shaderSourceData);
|
||||
|
||||
// The idea is that the "Supervariants" json property is optional in .shader files,
|
||||
// For cases when it is not specified, this function will return a vector with one item, the default, nameless, supervariant.
|
||||
// If "Supervariants" is not empty, then this function will make sure the first supervariant in the list
|
||||
// is the default, nameless, supervariant.
|
||||
AZStd::vector<RPI::ShaderSourceData::SupervariantInfo> GetSupervariantListFromShaderSourceData(
|
||||
const RPI::ShaderSourceData& shaderSourceData);
|
||||
|
||||
void GetDefaultEntryPointsFromFunctionDataList(
|
||||
const AZStd::vector<FunctionData> azslFunctionDataList,
|
||||
AZStd::unordered_map<AZStd::string, RPI::ShaderStageType>& shaderEntryPoints);
|
||||
|
||||
void LogProfilingData(const char* builderName, AZStd::string_view shaderPath);
|
||||
|
||||
//! Job products sub id generation helper for AzslBuilder
|
||||
uint32_t MakeAzslBuildProductSubId(RPI::ShaderAssetSubId subId, RHI::APIType apiType);
|
||||
|
||||
//! Returns the asset path of a product artifact produced by ShaderAssetBuilder2.
|
||||
Outcome<AZStd::string, AZStd::string> ObtainBuildArtifactPathFromShaderAssetBuilder2(
|
||||
const uint32_t rhiUniqueIndex, const AZStd::string& platformIdentifier, const AZStd::string& shaderJsonPath,
|
||||
const uint32_t supervariantIndex, RPI::ShaderAssetSubId shaderAssetSubId);
|
||||
|
||||
//! Reconstructs the expected output product paths of the AzslBuilder (from the 2 arguments @azslSourceFullPath and @apiType)
|
||||
Outcome<AzslSubProducts::Paths> ObtainBuildArtifactsFromAzslBuilder(const char* builderName, const AZStd::string& azslSourceFullPath, RHI::APIType apiType, const AZStd::string& platform);
|
||||
|
||||
|
||||
@@ -276,12 +276,6 @@ namespace AZ
|
||||
return LoadResult{LoadResult::Code::DeferredError, AZStd::string::format("ShaderSourceData file does not exist: %s.", shaderSourceFileFullPath.c_str())};
|
||||
}
|
||||
|
||||
// Let's open the shader source, because We need the source code of its AZSL file
|
||||
auto outcomeShaderData = ShaderBuilderUtility::LoadShaderDataJson(shaderSourceFileFullPath);
|
||||
if (!outcomeShaderData.IsSuccess())
|
||||
{
|
||||
return LoadResult{LoadResult::Code::DeferredError, AZStd::string::format("Failed to parse Shader Descriptor JSON: %s", outcomeShaderData.GetError().c_str())};
|
||||
}
|
||||
return LoadResult{LoadResult::Code::Success};
|
||||
} // LoadShaderVariantListAndAzslSource
|
||||
|
||||
@@ -420,15 +414,6 @@ namespace AZ
|
||||
return;
|
||||
}
|
||||
|
||||
if (jobParameters.find(ShouldExitEarlyFromProcessJobParam) != jobParameters.end())
|
||||
{
|
||||
AZ_TracePrintf(
|
||||
ShaderVariantAssetBuilderName, "Doing nothing on behalf of [%s] because it's been overriden by game project.",
|
||||
jobParameters.at(ShaderVariantLoadErrorParam).c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
@@ -589,7 +574,7 @@ namespace AZ
|
||||
if (shaderSourceDataDescriptor.m_programSettings.m_entryPoints.empty())
|
||||
{
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilderName, "ProgramSettings do not specify entry points, will use GetDefaultEntryPointsFromShader()\n");
|
||||
ShaderVariantAssetBuilder::GetDefaultEntryPointsFromAzslData(azslData, shaderEntryPoints);
|
||||
ShaderBuilderUtility::GetDefaultEntryPointsFromFunctionDataList(azslData.m_functions, shaderEntryPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -778,7 +763,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
// Time to save the asset in the cache tmp folder.
|
||||
const uint32_t productSubID = RPI::ShaderVariantAsset::GetAssetSubId(shaderPlatformInterface->GetAPIUniqueIndex(), shaderVariantAsset->GetStableId());
|
||||
const uint32_t productSubID = RPI::ShaderVariantAsset::MakeAssetProductSubId(shaderPlatformInterface->GetAPIUniqueIndex(), shaderVariantAsset->GetStableId());
|
||||
AssetBuilderSDK::JobProduct assetProduct;
|
||||
if (!SerializeOutShaderVariantAsset(shaderVariantAsset, shaderSourceFileFullPath, request.m_tempDirPath, *shaderPlatformInterface, productSubID, assetProduct))
|
||||
{
|
||||
@@ -788,12 +773,14 @@ namespace AZ
|
||||
response.m_outputProducts.push_back(assetProduct);
|
||||
|
||||
// add byproducts as job output products:
|
||||
uint32_t subProductType = aznumeric_cast<uint32_t>(RPI::ShaderAssetSubId::GeneratedHlslSource) + 1;
|
||||
for (const AZStd::string& byproduct : byproducts.m_intermediatePaths)
|
||||
{
|
||||
AssetBuilderSDK::JobProduct jobProduct;
|
||||
jobProduct.m_productFileName = byproduct;
|
||||
jobProduct.m_productAssetType = Uuid::CreateName("DebugInfoByProduct-PdbOrDxilTxt");
|
||||
jobProduct.m_productSubID = ShaderBuilderUtility::MakeDebugByproductSubId(shaderPlatformInterface->GetAPIType(), byproduct);
|
||||
jobProduct.m_productSubID = RPI::ShaderVariantAsset::MakeAssetProductSubId(
|
||||
shaderPlatformInterface->GetAPIType(), shaderVariantAsset->GetStableId(), subProductType++);
|
||||
response.m_outputProducts.push_back(AZStd::move(jobProduct));
|
||||
}
|
||||
}
|
||||
@@ -801,53 +788,6 @@ namespace AZ
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
}
|
||||
|
||||
|
||||
/// Returns a list of acceptable default entry point names
|
||||
static void GetAcceptableDefaultEntryPoints(const AzslData& shaderData, AZStd::unordered_map<AZStd::string, RPI::ShaderStageType>& defaultEntryPoints)
|
||||
{
|
||||
for (const auto& func : shaderData.m_topData.m_functions)
|
||||
{
|
||||
if (!func.m_hasShaderStageVaryings)
|
||||
{
|
||||
// Not declaring any semantics for a shader entry is valid, but unusual.
|
||||
// A shader entry with no semantics must be explicitly listed and won't be selected by default.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (func.m_name.starts_with("VS") || func.m_name.ends_with("VS"))
|
||||
{
|
||||
defaultEntryPoints[func.m_name] = RPI::ShaderStageType::Vertex;
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilderName, "Assuming \"%s\" is a valid Vertex shader entry point.\n", func.m_name.c_str());
|
||||
}
|
||||
else if (func.m_name.starts_with("PS") || func.m_name.ends_with("PS"))
|
||||
{
|
||||
defaultEntryPoints[func.m_name] = RPI::ShaderStageType::Fragment;
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilderName, "Assuming \"%s\" is a valid Fragment shader entry point.\n", func.m_name.c_str());
|
||||
}
|
||||
else if (func.m_name.starts_with("CS") || func.m_name.ends_with("CS"))
|
||||
{
|
||||
defaultEntryPoints[func.m_name] = RPI::ShaderStageType::Compute;
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilderName, "Assuming \"%s\" is a valid Compute shader entry point.\n", func.m_name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a list of acceptable default entry point names as a single string for messages
|
||||
static AZStd::string GetAcceptableDefaultEntryPointNames(const AzslData& shaderData)
|
||||
{
|
||||
AZStd::unordered_map<AZStd::string, RPI::ShaderStageType> defaultEntryPointList;
|
||||
GetAcceptableDefaultEntryPoints(shaderData, defaultEntryPointList);
|
||||
|
||||
AZStd::vector<AZStd::string> defaultEntryPointNamesList;
|
||||
for (const auto& shaderEntryPoint : defaultEntryPointList)
|
||||
{
|
||||
defaultEntryPointNamesList.push_back(shaderEntryPoint.first);
|
||||
}
|
||||
AZStd::string shaderEntryPoints;
|
||||
AzFramework::StringFunc::Join(shaderEntryPoints, defaultEntryPointNamesList.begin(), defaultEntryPointNamesList.end(), ", ");
|
||||
return AZStd::move(shaderEntryPoints);
|
||||
}
|
||||
|
||||
static bool CreateShaderVariant(
|
||||
ShaderVariantCreationContext& variantCreationContext,
|
||||
const AzslData& azslData,
|
||||
@@ -945,7 +885,7 @@ namespace AZ
|
||||
|
||||
if (!hasRasterProgram && !hasComputeProgram && !hasRayTracingProgram)
|
||||
{
|
||||
AZStd::string entryPointNames = GetAcceptableDefaultEntryPointNames(azslData);
|
||||
AZStd::string entryPointNames = ShaderBuilderUtility::GetAcceptableDefaultEntryPointNames(azslData);
|
||||
|
||||
AZ_Error(ShaderVariantAssetBuilderName, false, "Shader asset descriptor has a program variant that does not define any entry points. Either declare entry points in the .shader file, or use one of the available default names (not case-sensitive): [%s]", entryPointNames.data());
|
||||
|
||||
@@ -990,198 +930,6 @@ namespace AZ
|
||||
return isVariantValid;
|
||||
}
|
||||
|
||||
static bool IsSystemValueSemantic(const AZStd::string_view semantic)
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics#system-value-semantics
|
||||
return AzFramework::StringFunc::StartsWith(semantic, "sv_", false);
|
||||
}
|
||||
|
||||
static bool CreateShaderInputContract(
|
||||
const AzslData& azslData,
|
||||
const AZStd::string& vertexShaderName,
|
||||
const RPI::ShaderOptionGroupLayout& shaderOptionGroupLayout,
|
||||
RPI::ShaderInputContract& contract,
|
||||
const AZStd::string& pathToIaJson)
|
||||
{
|
||||
StructData inputStruct;
|
||||
inputStruct.m_id = "";
|
||||
|
||||
auto jsonOutcome = JsonSerializationUtils::ReadJsonFile(pathToIaJson);
|
||||
if (!jsonOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilderName, false, "%s", jsonOutcome.GetError().c_str());
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
AzslCompiler azslc(azslData.m_preprocessedFullPath);
|
||||
if (!azslc.ParseIaPopulateStructData(jsonOutcome.GetValue(), vertexShaderName, inputStruct))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilderName, false, "Failed to parse input layout\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inputStruct.m_id.empty())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilderName, false, "Failed to find the input struct for vertex shader %s.", vertexShaderName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& member : inputStruct.m_members)
|
||||
{
|
||||
RHI::ShaderSemantic streamChannelSemantic{
|
||||
Name{ member.m_semanticText },
|
||||
static_cast<uint32_t>(member.m_semanticIndex) };
|
||||
|
||||
// Semantics that represent a system-generated value do not map to an input stream
|
||||
if (IsSystemValueSemantic(streamChannelSemantic.m_name.GetStringView()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
contract.m_streamChannels.push_back();
|
||||
contract.m_streamChannels.back().m_semantic = streamChannelSemantic;
|
||||
|
||||
if (member.m_variable.m_typeModifier == MatrixMajor::ColumnMajor)
|
||||
{
|
||||
contract.m_streamChannels.back().m_componentCount = member.m_variable.m_cols;
|
||||
}
|
||||
else
|
||||
{
|
||||
contract.m_streamChannels.back().m_componentCount = member.m_variable.m_rows;
|
||||
}
|
||||
|
||||
// [GFX_TODO][ATOM-14475]: Come up with a more elegant way to mark optional channels and their corresponding shader option
|
||||
static const char OptionalInputStreamPrefix[] = "m_optional_";
|
||||
if (AzFramework::StringFunc::StartsWith(member.m_variable.m_name, OptionalInputStreamPrefix, true))
|
||||
{
|
||||
AZStd::string expectedOptionName = AZStd::string::format("o_%s_isBound", member.m_variable.m_name.substr(strlen(OptionalInputStreamPrefix)).c_str());
|
||||
|
||||
RPI::ShaderOptionIndex shaderOptionIndex = shaderOptionGroupLayout.FindShaderOptionIndex(Name{expectedOptionName});
|
||||
if (!shaderOptionIndex.IsValid())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilderName, false, "Shader option '%s' not found for optional input stream '%s'", expectedOptionName.c_str(), member.m_variable.m_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const RPI::ShaderOptionDescriptor& option = shaderOptionGroupLayout.GetShaderOption(shaderOptionIndex);
|
||||
if (option.GetType() != RPI::ShaderOptionType::Boolean)
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilderName, false, "Shader option '%s' must be a bool.", expectedOptionName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (option.GetDefaultValue().GetStringView() != "false")
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilderName, false, "Shader option '%s' must default to false.", expectedOptionName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
contract.m_streamChannels.back().m_isOptional = true;
|
||||
contract.m_streamChannels.back().m_streamBoundIndicatorIndex = shaderOptionIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CreateShaderOutputContract(
|
||||
const AzslData& azslData,
|
||||
const AZStd::string& fragmentShaderName,
|
||||
RPI::ShaderOutputContract& contract,
|
||||
const AZStd::string& pathToOmJson)
|
||||
{
|
||||
StructData outputStruct;
|
||||
outputStruct.m_id = "";
|
||||
|
||||
auto jsonOutcome = JsonSerializationUtils::ReadJsonFile(pathToOmJson);
|
||||
if (!jsonOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilderName, false, "%s", jsonOutcome.GetError().c_str());
|
||||
return AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
}
|
||||
|
||||
AzslCompiler azslc(azslData.m_preprocessedFullPath);
|
||||
if (!azslc.ParseOmPopulateStructData(jsonOutcome.GetValue(), fragmentShaderName, outputStruct))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilderName, false, "Failed to parse output layout\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& member : outputStruct.m_members)
|
||||
{
|
||||
RHI::ShaderSemantic semantic = RHI::ShaderSemantic::Parse(member.m_semanticText);
|
||||
|
||||
bool depthFound = false;
|
||||
|
||||
if (semantic.m_name.GetStringView() == "SV_Target")
|
||||
{
|
||||
contract.m_requiredColorAttachments.push_back();
|
||||
// Render targets only support 1-D vector types and those are always column-major (per DXC)
|
||||
contract.m_requiredColorAttachments.back().m_componentCount = member.m_variable.m_cols;
|
||||
}
|
||||
else if (semantic.m_name.GetStringView() == "SV_Depth" ||
|
||||
semantic.m_name.GetStringView() == "SV_DepthGreaterEqual" ||
|
||||
semantic.m_name.GetStringView() == "SV_DepthLessEqual")
|
||||
{
|
||||
if (depthFound)
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilderName, false, "SV_Depth specified more than once in the fragment shader output structure");
|
||||
return false;
|
||||
}
|
||||
depthFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilderName, false, "Unsupported shader output semantic '%s'.", semantic.m_name.GetCStr());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CreateShaderInputAndOutputContracts(
|
||||
const AzslData& azslData,
|
||||
const MapOfStringToStageType& shaderEntryPoints,
|
||||
const RPI::ShaderOptionGroupLayout& shaderOptionGroupLayout,
|
||||
RPI::ShaderInputContract& shaderInputContract,
|
||||
RPI::ShaderOutputContract& shaderOutputContract,
|
||||
size_t& colorAttachmentCount,
|
||||
const AZStd::string& pathToOmJson,
|
||||
const AZStd::string& pathToIaJson)
|
||||
{
|
||||
bool success = true;
|
||||
for (const auto& shaderEntryPoint : shaderEntryPoints)
|
||||
{
|
||||
auto shaderEntryName = shaderEntryPoint.first;
|
||||
auto shaderStageType = shaderEntryPoint.second;
|
||||
|
||||
if (shaderStageType == RPI::ShaderStageType::Vertex)
|
||||
{
|
||||
const bool layoutCreated = CreateShaderInputContract(azslData, shaderEntryName, shaderOptionGroupLayout, shaderInputContract, pathToIaJson);
|
||||
if (!layoutCreated)
|
||||
{
|
||||
success = false;
|
||||
AZ_Error(ShaderVariantAssetBuilderName, false, "Could not create the input contract for the vertex function %s", shaderEntryName.c_str());
|
||||
continue; // Using continue to report all the errors found
|
||||
}
|
||||
}
|
||||
|
||||
if (shaderStageType == RPI::ShaderStageType::Fragment)
|
||||
{
|
||||
const bool layoutCreated = CreateShaderOutputContract(azslData, shaderEntryName, shaderOutputContract, pathToOmJson);
|
||||
if (!layoutCreated)
|
||||
{
|
||||
success = false;
|
||||
AZ_Error(ShaderVariantAssetBuilderName, false, "Could not create the output contract for the fragment function %s", shaderEntryName.c_str());
|
||||
continue; // Using continue to report all the errors found
|
||||
}
|
||||
|
||||
colorAttachmentCount = shaderOutputContract.m_requiredColorAttachments.size();
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
AZ::Outcome<Data::Asset<RPI::ShaderVariantAsset>, AZStd::string> ShaderVariantAssetBuilder::CreateShaderVariantAssetForAPI(
|
||||
const RPI::ShaderVariantListSourceData::VariantInfo& variantInfo,
|
||||
@@ -1195,8 +943,8 @@ namespace AZ
|
||||
RPI::ShaderInputContract shaderInputContract;
|
||||
RPI::ShaderOutputContract shaderOutputContract;
|
||||
size_t colorAttachmentCount = 0;
|
||||
CreateShaderInputAndOutputContracts(azslData, variantCreationContext.m_shaderEntryPoints, variantCreationContext.m_shaderOptionGroupLayout,
|
||||
shaderInputContract, shaderOutputContract, colorAttachmentCount, pathToOmJson, pathToIaJson);
|
||||
ShaderBuilderUtility::CreateShaderInputAndOutputContracts(azslData, variantCreationContext.m_shaderEntryPoints, variantCreationContext.m_shaderOptionGroupLayout, pathToOmJson,
|
||||
pathToIaJson, shaderInputContract, shaderOutputContract, colorAttachmentCount);
|
||||
|
||||
const RPI::ShaderOptionGroupLayout& shaderOptionGroupLayout = variantCreationContext.m_shaderOptionGroupLayout;
|
||||
// Temporary structure used for sorting and caching intermediate results
|
||||
@@ -1284,25 +1032,6 @@ namespace AZ
|
||||
return AZ::Success(AZStd::move(shaderVariantAsset));
|
||||
}
|
||||
|
||||
void ShaderVariantAssetBuilder::GetDefaultEntryPointsFromAzslData(const AzslData& shaderData, AZStd::unordered_map<AZStd::string, RPI::ShaderStageType>& shaderEntryPoints)
|
||||
{
|
||||
AZStd::unordered_map<AZStd::string, RPI::ShaderStageType> defaultEntryPoints;
|
||||
GetAcceptableDefaultEntryPoints(shaderData, defaultEntryPoints);
|
||||
|
||||
for (const auto& functionData : shaderData.m_topData.m_functions)
|
||||
{
|
||||
for (const auto& defaultEntryPoint : defaultEntryPoints)
|
||||
{
|
||||
// Equal defaults to case insensitive compares...
|
||||
if (AzFramework::StringFunc::Equal(defaultEntryPoint.first.c_str(), functionData.m_name.c_str()))
|
||||
{
|
||||
shaderEntryPoints[defaultEntryPoint.first] = defaultEntryPoint.second;
|
||||
break; // stop looping default entry points and go to the next shader function
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderVariantAssetBuilder::SerializeOutShaderVariantAsset(const Data::Asset<RPI::ShaderVariantAsset> shaderVariantAsset, const AZStd::string& shaderSourceFileFullPath, const AZStd::string& tempDirPath,
|
||||
const RHI::ShaderPlatformInterface& shaderPlatformInterface, const uint32_t productSubID, AssetBuilderSDK::JobProduct& assetProduct)
|
||||
{
|
||||
|
||||
@@ -73,8 +73,6 @@ namespace AZ
|
||||
static bool SerializeOutShaderVariantAsset(const Data::Asset<RPI::ShaderVariantAsset> shaderVariantAsset, const AZStd::string& shaderFullPath, const AZStd::string& tempDirPath,
|
||||
const RHI::ShaderPlatformInterface& shaderPlatformInterface, const uint32_t productSubID, AssetBuilderSDK::JobProduct& assetProduct);
|
||||
|
||||
static void GetDefaultEntryPointsFromAzslData(const AzslData& shaderData, AZStd::unordered_map<AZStd::string, RPI::ShaderStageType>& shaderEntryPoints);
|
||||
|
||||
// AssetBuilderSDK::AssetBuilderCommandBus interface overrides ...
|
||||
void ShutDown() override { };
|
||||
|
||||
|
||||
@@ -0,0 +1,978 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <ShaderVariantAssetBuilder2.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantTreeAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
|
||||
|
||||
#include <Atom/RPI.Edit/Shader/ShaderVariantListSourceData.h>
|
||||
#include <Atom/RPI.Edit/Shader/ShaderVariantAssetCreator2.h>
|
||||
#include <Atom/RPI.Edit/Shader/ShaderVariantTreeAssetCreator.h>
|
||||
#include <Atom/RPI.Edit/Common/JsonUtils.h>
|
||||
|
||||
#include <AtomCore/Serialization/Json/JsonUtils.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderResourceGroupAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantKey.h>
|
||||
|
||||
#include <Atom/RHI.Edit/Utils.h>
|
||||
#include <Atom/RHI.Edit/ShaderPlatformInterface.h>
|
||||
#include <Atom/RPI.Edit/Common/JsonReportingHelper.h>
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RHI.Reflect/ConstantsLayout.h>
|
||||
#include <Atom/RHI.Reflect/PipelineLayoutDescriptor.h>
|
||||
#include <Atom/RHI.Reflect/ShaderStageFunction.h>
|
||||
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <AzFramework/Platform/PlatformDefaults.h>
|
||||
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/JSON/document.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/IOUtils.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
|
||||
#include "ShaderAssetBuilder2.h"
|
||||
#include "ShaderBuilderUtility.h"
|
||||
#include "AzslData.h"
|
||||
#include "AzslCompiler.h"
|
||||
#include "AzslBuilder.h"
|
||||
#include <CommonFiles/Preprocessor.h>
|
||||
#include <CommonFiles/GlobalBuildOptions.h>
|
||||
#include <ShaderPlatformInterfaceRequest.h>
|
||||
#include "AtomShaderConfig.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
static constexpr char ShaderVariantAssetBuilder2Name[] = "ShaderVariantAssetBuilder2";
|
||||
|
||||
static void AddShaderAssetJobDependency2(
|
||||
AssetBuilderSDK::JobDescriptor& jobDescriptor, const AssetBuilderSDK::PlatformInfo& platformInfo,
|
||||
const AZStd::string& shaderVariantListFilePath, const AZStd::string& shaderFilePath)
|
||||
{
|
||||
AZStd::vector<AZStd::string> possibleDependencies =
|
||||
AZ::RPI::AssetUtils::GetPossibleDepenencyPaths(shaderVariantListFilePath, shaderFilePath);
|
||||
for (auto& file : possibleDependencies)
|
||||
{
|
||||
AssetBuilderSDK::JobDependency jobDependency;
|
||||
jobDependency.m_jobKey = ShaderAssetBuilder2::ShaderAssetBuilder2JobKey;
|
||||
jobDependency.m_platformIdentifier = platformInfo.m_identifier;
|
||||
jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
jobDependency.m_sourceFile.m_sourceFileDependencyPath = file;
|
||||
jobDescriptor.m_jobDependencyList.push_back(jobDependency);
|
||||
}
|
||||
}
|
||||
|
||||
//! Returns true if @sourceFileFullPath starts with a valid asset processor scan folder, false otherwise.
|
||||
//! In case of true, it splits @sourceFileFullPath into @scanFolderFullPath and @filePathFromScanFolder.
|
||||
//! @sourceFileFullPath The full path to a source asset file.
|
||||
//! @scanFolderFullPath [out] Gets the full path of the scan folder where the source file is located.
|
||||
//! @filePathFromScanFolder [out] Get the file path relative to @scanFolderFullPath.
|
||||
static bool SplitSourceAssetPathIntoScanFolderFullPathAndRelativeFilePath2(const AZStd::string& sourceFileFullPath, AZStd::string& scanFolderFullPath, AZStd::string& filePathFromScanFolder)
|
||||
{
|
||||
AZStd::vector<AZStd::string> scanFolders;
|
||||
bool success = false;
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(success, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAssetSafeFolders, scanFolders);
|
||||
if (!success)
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Couldn't get the scan folders");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AZStd::string scanFolder : scanFolders)
|
||||
{
|
||||
AzFramework::StringFunc::Path::Normalize(scanFolder);
|
||||
if (!AZ::StringFunc::StartsWith(sourceFileFullPath, scanFolder))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const size_t scanFolderSize = scanFolder.size();
|
||||
const size_t sourcePathSize = sourceFileFullPath.size();
|
||||
scanFolderFullPath = scanFolder;
|
||||
filePathFromScanFolder = sourceFileFullPath.substr(scanFolderSize + 1, sourcePathSize - scanFolderSize - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Validates if a given .shadervariantlist file is located at the correct path for a given .shader full path.
|
||||
//! There are two valid paths:
|
||||
//! 1- Lower Precedence: The same folder where the .shader file is located.
|
||||
//! 2- Higher Precedence: <DEVROOT>/<GAME>/ShaderVariants/<Same Scan Folder Subpath as the .shader file>.
|
||||
//! The "Higher Precedence" path gives the option to game projects to override what variants to generate. If this
|
||||
//! file exists then the "Lower Precedence" path is disregarded.
|
||||
//! A .shader full path is located under an AP scan folder.
|
||||
//! Example: "<DEVROOT>/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.shader"
|
||||
//! - In this example the Scan Folder is "<DEVROOT>/Gems/Atom/Feature/Common/Assets", while the subfolder is "Materials/Types".
|
||||
//! The "Higher Precedence" expected valid location for the .shadervariantlist would be:
|
||||
//! - <DEVROOT>/<GameProject>/ShaderVariants/Materials/Types/StandardPBR_ForwardPass.shadervariantlist.
|
||||
//! The "Lower Precedence" valid location would be:
|
||||
//! - <DEVROOT>/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.shadervariantlist.
|
||||
//! @shouldExitEarlyFromProcessJob [out] Set to true if ProcessJob should do no work but return successfully.
|
||||
//! Set to false if ProcessJob should do work and create assets.
|
||||
//! When @shaderVariantListFileFullPath is provided by a Gem/Feature instead of the Game Project
|
||||
//! We check if the game project already defined the shader variant list, and if it did it means
|
||||
//! ProcessJob should do no work, but return successfully nonetheless.
|
||||
static bool ValidateShaderVariantListLocation2(const AZStd::string& shaderVariantListFileFullPath,
|
||||
const AZStd::string& shaderFileFullPath, bool& shouldExitEarlyFromProcessJob)
|
||||
{
|
||||
AZStd::string scanFolderFullPath;
|
||||
AZStd::string shaderProductFileRelativePath;
|
||||
if (!SplitSourceAssetPathIntoScanFolderFullPathAndRelativeFilePath2(shaderFileFullPath, scanFolderFullPath, shaderProductFileRelativePath))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Couldn't get the scan folder for shader [%s]", shaderFileFullPath.c_str());
|
||||
return false;
|
||||
}
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "For shader [%s], Scan folder full path [%s], relative file path [%s]", shaderFileFullPath.c_str(), scanFolderFullPath.c_str(), shaderProductFileRelativePath.c_str());
|
||||
|
||||
AZStd::string shaderVariantListFileRelativePath = shaderProductFileRelativePath;
|
||||
AzFramework::StringFunc::Path::ReplaceExtension(shaderVariantListFileRelativePath, RPI::ShaderVariantListSourceData::Extension);
|
||||
|
||||
const char * gameProjectPath = nullptr;
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(gameProjectPath, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAbsoluteDevGameFolderPath);
|
||||
|
||||
AZStd::string expectedHigherPrecedenceFileFullPath;
|
||||
AzFramework::StringFunc::Path::Join(gameProjectPath, RPI::ShaderVariantTreeAsset::CommonSubFolder, expectedHigherPrecedenceFileFullPath, false /* handle directory overlap? */, false /* be case insensitive? */);
|
||||
AzFramework::StringFunc::Path::Join(expectedHigherPrecedenceFileFullPath.c_str(), shaderProductFileRelativePath.c_str(), expectedHigherPrecedenceFileFullPath, false /* handle directory overlap? */, false /* be case insensitive? */);
|
||||
AzFramework::StringFunc::Path::ReplaceExtension(expectedHigherPrecedenceFileFullPath, AZ::RPI::ShaderVariantListSourceData::Extension);
|
||||
AzFramework::StringFunc::Path::Normalize(expectedHigherPrecedenceFileFullPath);
|
||||
|
||||
AZStd::string normalizedShaderVariantListFileFullPath = shaderVariantListFileFullPath;
|
||||
AzFramework::StringFunc::Path::Normalize(normalizedShaderVariantListFileFullPath);
|
||||
|
||||
if (expectedHigherPrecedenceFileFullPath == normalizedShaderVariantListFileFullPath)
|
||||
{
|
||||
// Whenever the Game Project declares a *.shadervariantlist file we always do work.
|
||||
shouldExitEarlyFromProcessJob = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
AZStd::string watchFolder;
|
||||
bool foundHigherPrecedenceAsset = false;
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(foundHigherPrecedenceAsset
|
||||
, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath
|
||||
, expectedHigherPrecedenceFileFullPath.c_str(), assetInfo, watchFolder);
|
||||
if (foundHigherPrecedenceAsset)
|
||||
{
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "The shadervariantlist [%s] has been overriden by the game project with [%s]",
|
||||
normalizedShaderVariantListFileFullPath.c_str(), expectedHigherPrecedenceFileFullPath.c_str());
|
||||
shouldExitEarlyFromProcessJob = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the "Lower Precedence" case, .shader path == .shadervariantlist path.
|
||||
AZStd::string normalizedShaderFileFullPath = shaderFileFullPath;
|
||||
AzFramework::StringFunc::Path::Normalize(normalizedShaderFileFullPath);
|
||||
|
||||
AZStd::string normalizedShaderFileFullPathWithoutExtension = normalizedShaderFileFullPath;
|
||||
AzFramework::StringFunc::Path::StripExtension(normalizedShaderFileFullPathWithoutExtension);
|
||||
|
||||
AZStd::string normalizedShaderVariantListFileFullPathWithoutExtension = normalizedShaderVariantListFileFullPath;
|
||||
AzFramework::StringFunc::Path::StripExtension(normalizedShaderVariantListFileFullPathWithoutExtension);
|
||||
|
||||
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
|
||||
//In certain circumstances, the capitalization of the drive letter may not match
|
||||
const bool caseSensitive = false;
|
||||
#else
|
||||
//On the other platforms there's no drive letter, so it should be a non-issue.
|
||||
const bool caseSensitive = true;
|
||||
#endif
|
||||
if (!StringFunc::Equal(normalizedShaderFileFullPathWithoutExtension.c_str(), normalizedShaderVariantListFileFullPathWithoutExtension.c_str(), caseSensitive))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "For shader file at path [%s], the shader variant list [%s] is expected to be located at [%s.%s] or [%s]"
|
||||
, normalizedShaderFileFullPath.c_str(), normalizedShaderVariantListFileFullPath.c_str(),
|
||||
normalizedShaderFileFullPathWithoutExtension.c_str(), RPI::ShaderVariantListSourceData::Extension,
|
||||
expectedHigherPrecedenceFileFullPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
shouldExitEarlyFromProcessJob = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We treat some issues as warnings and return "Success" from CreateJobs allows us to report the dependency.
|
||||
// If/when a valid dependency file appears, that will trigger the ShaderVariantAssetBuilder2 to run again.
|
||||
// Since CreateJobs will pass, we forward this message to ProcessJob which will report it as an error.
|
||||
struct LoadResult2
|
||||
{
|
||||
enum class Code
|
||||
{
|
||||
Error,
|
||||
DeferredError,
|
||||
Success
|
||||
};
|
||||
|
||||
Code m_code;
|
||||
AZStd::string m_deferredMessage; // Only used when m_code == DeferredError
|
||||
};
|
||||
|
||||
static LoadResult2 LoadShaderVariantList2(const AZStd::string& variantListFullPath, RPI::ShaderVariantListSourceData& shaderVariantList, AZStd::string& shaderSourceFileFullPath,
|
||||
bool& shouldExitEarlyFromProcessJob)
|
||||
{
|
||||
// Need to get the name of the shader file from the template so that we can preprocess the shader data and setup
|
||||
// source file dependencies.
|
||||
if (!RPI::JsonUtils::LoadObjectFromFile(variantListFullPath, shaderVariantList))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Failed to parse Shader Variant List Descriptor JSON from [%s]", variantListFullPath.c_str());
|
||||
return LoadResult2{LoadResult2::Code::Error};
|
||||
}
|
||||
|
||||
const AZStd::string resolvedShaderPath = AZ::RPI::AssetUtils::ResolvePathReference(variantListFullPath, shaderVariantList.m_shaderFilePath);
|
||||
if (!AZ::IO::LocalFileIO::GetInstance()->Exists(resolvedShaderPath.c_str()))
|
||||
{
|
||||
return LoadResult2{LoadResult2::Code::DeferredError, AZStd::string::format("The shader path [%s] was not found.", resolvedShaderPath.c_str())};
|
||||
}
|
||||
|
||||
shaderSourceFileFullPath = resolvedShaderPath;
|
||||
|
||||
if (!ValidateShaderVariantListLocation2(variantListFullPath, shaderSourceFileFullPath, shouldExitEarlyFromProcessJob))
|
||||
{
|
||||
return LoadResult2{LoadResult2::Code::Error};
|
||||
}
|
||||
|
||||
if (shouldExitEarlyFromProcessJob)
|
||||
{
|
||||
return LoadResult2{LoadResult2::Code::Success};
|
||||
}
|
||||
|
||||
auto resultOutcome = RPI::ShaderVariantTreeAssetCreator::ValidateStableIdsAreUnique(shaderVariantList.m_shaderVariants);
|
||||
if (!resultOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Variant info validation error: %s", resultOutcome.GetError().c_str());
|
||||
return LoadResult2{LoadResult2::Code::Error};
|
||||
}
|
||||
|
||||
if (!IO::FileIOBase::GetInstance()->Exists(shaderSourceFileFullPath.c_str()))
|
||||
{
|
||||
return LoadResult2{LoadResult2::Code::DeferredError, AZStd::string::format("ShaderSourceData file does not exist: %s.", shaderSourceFileFullPath.c_str())};
|
||||
}
|
||||
|
||||
return LoadResult2{LoadResult2::Code::Success};
|
||||
} // LoadShaderVariantListAndAzslSource
|
||||
|
||||
void ShaderVariantAssetBuilder2::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const
|
||||
{
|
||||
AZStd::string variantListFullPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.data(), request.m_sourceFile.data(), variantListFullPath, true);
|
||||
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "CreateJobs for Shader Variant List \"%s\"\n", variantListFullPath.data());
|
||||
|
||||
RPI::ShaderVariantListSourceData shaderVariantList;
|
||||
AZStd::string shaderSourceFileFullPath;
|
||||
bool shouldExitEarlyFromProcessJob = false;
|
||||
const LoadResult2 loadResult = LoadShaderVariantList2(variantListFullPath, shaderVariantList, shaderSourceFileFullPath, shouldExitEarlyFromProcessJob);
|
||||
|
||||
if (loadResult.m_code == LoadResult2::Code::Error)
|
||||
{
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadResult.m_code == LoadResult2::Code::DeferredError || shouldExitEarlyFromProcessJob)
|
||||
{
|
||||
for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
|
||||
{
|
||||
// Let's create fake jobs that will fail ProcessJob, but are useful to establish dependency on the shader file.
|
||||
AssetBuilderSDK::JobDescriptor jobDescriptor;
|
||||
|
||||
jobDescriptor.m_priority = -5000;
|
||||
jobDescriptor.m_critical = false;
|
||||
jobDescriptor.m_jobKey = ShaderVariantAssetBuilder2JobKey;
|
||||
jobDescriptor.SetPlatformIdentifier(info.m_identifier.data());
|
||||
|
||||
AddShaderAssetJobDependency2(jobDescriptor, info, variantListFullPath, shaderVariantList.m_shaderFilePath);
|
||||
|
||||
if (loadResult.m_code == LoadResult2::Code::DeferredError)
|
||||
{
|
||||
jobDescriptor.m_jobParameters.emplace(ShaderVariantLoadErrorParam, loadResult.m_deferredMessage);
|
||||
}
|
||||
|
||||
if (shouldExitEarlyFromProcessJob)
|
||||
{
|
||||
// The value doesn't matter, what matters is the presence of the key which will
|
||||
// signal that no assets should be produced on behalf of this shadervariantlist because
|
||||
// the game project overrode it.
|
||||
jobDescriptor.m_jobParameters.emplace(ShouldExitEarlyFromProcessJobParam, variantListFullPath);
|
||||
}
|
||||
|
||||
response.m_createJobOutputs.push_back(jobDescriptor);
|
||||
}
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
|
||||
{
|
||||
AZ_TraceContext("For platform", info.m_identifier.data());
|
||||
|
||||
// First job is for the ShaderVariantTreeAsset.
|
||||
{
|
||||
AssetBuilderSDK::JobDescriptor jobDescriptor;
|
||||
|
||||
// The ShaderVariantTreeAsset is high priority, but must be generated after the ShaderAsset
|
||||
jobDescriptor.m_priority = 1;
|
||||
jobDescriptor.m_critical = false;
|
||||
|
||||
jobDescriptor.m_jobKey = GetShaderVariantTreeAssetJobKey();
|
||||
jobDescriptor.SetPlatformIdentifier(info.m_identifier.data());
|
||||
|
||||
AddShaderAssetJobDependency2(jobDescriptor, info, variantListFullPath, shaderVariantList.m_shaderFilePath);
|
||||
|
||||
jobDescriptor.m_jobParameters.emplace(ShaderSourceFilePathJobParam, shaderSourceFileFullPath);
|
||||
|
||||
response.m_createJobOutputs.push_back(jobDescriptor);
|
||||
}
|
||||
|
||||
// One job for each variant. Each job will produce one ".azshadervariant" per RHI per supervariant.
|
||||
for (const AZ::RPI::ShaderVariantListSourceData::VariantInfo& variantInfo : shaderVariantList.m_shaderVariants)
|
||||
{
|
||||
AZStd::string variantInfoAsJsonString;
|
||||
const bool convertSuccess = AZ::RPI::JsonUtils::SaveObjectToJsonString(variantInfo, variantInfoAsJsonString);
|
||||
AZ_Assert(convertSuccess, "Failed to convert VariantInfo to json string");
|
||||
|
||||
AssetBuilderSDK::JobDescriptor jobDescriptor;
|
||||
|
||||
// There can be tens/hundreds of thousands of shader variants. By default each shader will get
|
||||
// a root variant that can be used at runtime. In order to prevent the AssetProcessor from
|
||||
// being overtaken by shader variant compilation We mark all non-root shader variant generation
|
||||
// as non critical and very low priority.
|
||||
jobDescriptor.m_priority = -5000;
|
||||
jobDescriptor.m_critical = false;
|
||||
|
||||
jobDescriptor.m_jobKey = GetShaderVariantAssetJobKey(RPI::ShaderVariantStableId{variantInfo.m_stableId});
|
||||
jobDescriptor.SetPlatformIdentifier(info.m_identifier.data());
|
||||
|
||||
// The ShaderVariantAssets are job dependent on the ShaderVariantTreeAsset.
|
||||
AssetBuilderSDK::SourceFileDependency fileDependency;
|
||||
fileDependency.m_sourceFileDependencyPath = variantListFullPath;
|
||||
AssetBuilderSDK::JobDependency variantTreeJobDependency;
|
||||
variantTreeJobDependency.m_jobKey = GetShaderVariantTreeAssetJobKey();
|
||||
variantTreeJobDependency.m_platformIdentifier = info.m_identifier;
|
||||
variantTreeJobDependency.m_sourceFile = fileDependency;
|
||||
variantTreeJobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
jobDescriptor.m_jobDependencyList.emplace_back(variantTreeJobDependency);
|
||||
|
||||
jobDescriptor.m_jobParameters.emplace(ShaderVariantJobVariantParam, variantInfoAsJsonString);
|
||||
jobDescriptor.m_jobParameters.emplace(ShaderSourceFilePathJobParam, shaderSourceFileFullPath);
|
||||
|
||||
response.m_createJobOutputs.push_back(jobDescriptor);
|
||||
}
|
||||
|
||||
}
|
||||
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
|
||||
} // CreateJobs
|
||||
|
||||
void ShaderVariantAssetBuilder2::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const
|
||||
{
|
||||
const auto& jobParameters = request.m_jobDescription.m_jobParameters;
|
||||
|
||||
if (jobParameters.find(ShaderVariantLoadErrorParam) != jobParameters.end())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Error during CreateJobs: %s", jobParameters.at(ShaderVariantLoadErrorParam).c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (jobParameters.find(ShouldExitEarlyFromProcessJobParam) != jobParameters.end())
|
||||
{
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Doing nothing on behalf of [%s] because it's been overridden by game project.", jobParameters.at(ShaderVariantLoadErrorParam).c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.m_jobDescription.m_jobKey == GetShaderVariantTreeAssetJobKey())
|
||||
{
|
||||
ProcessShaderVariantTreeJob(request, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessShaderVariantJob(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static RPI::Ptr<RPI::ShaderOptionGroupLayout> LoadShaderOptionsGroupLayoutFromShaderAssetBuilder2(
|
||||
const RHI::ShaderPlatformInterface* shaderPlatformInterface,
|
||||
const AssetBuilderSDK::PlatformInfo& platformInfo,
|
||||
const AzslCompiler& azslCompiler,
|
||||
const AZStd::string& shaderSourceFileFullPath,
|
||||
const RPI::SupervariantIndex supervariantIndex)
|
||||
{
|
||||
auto optionsGroupPathOutcome = ShaderBuilderUtility::ObtainBuildArtifactPathFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), platformInfo.m_identifier, shaderSourceFileFullPath, supervariantIndex.GetIndex(),
|
||||
AZ::RPI::ShaderAssetSubId::OptionsJson);
|
||||
if (!optionsGroupPathOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "%s", optionsGroupPathOutcome.GetError().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
auto optionsGroupJsonPath = optionsGroupPathOutcome.TakeValue();
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = RPI::ShaderOptionGroupLayout::Create();
|
||||
// The shader options define what options are available, what are the allowed values/range
|
||||
// for each option and what is its default value.
|
||||
auto jsonOutcome = JsonSerializationUtils::ReadJsonFile(optionsGroupJsonPath);
|
||||
if (!jsonOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "%s", jsonOutcome.GetError().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
if (!azslCompiler.ParseOptionsPopulateOptionGroupLayout(jsonOutcome.GetValue(), shaderOptionGroupLayout))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Failed to find a valid list of shader options!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return shaderOptionGroupLayout;
|
||||
}
|
||||
|
||||
static void LoadShaderFunctionsFromShaderAssetBuilder2(
|
||||
const RHI::ShaderPlatformInterface* shaderPlatformInterface, const AssetBuilderSDK::PlatformInfo& platformInfo,
|
||||
const AzslCompiler& azslCompiler, const AZStd::string& shaderSourceFileFullPath,
|
||||
const RPI::SupervariantIndex supervariantIndex,
|
||||
AzslFunctions& functions)
|
||||
{
|
||||
auto functionsJsonPathOutcome = ShaderBuilderUtility::ObtainBuildArtifactPathFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), platformInfo.m_identifier, shaderSourceFileFullPath, supervariantIndex.GetIndex(),
|
||||
AZ::RPI::ShaderAssetSubId::IaJson);
|
||||
if (!functionsJsonPathOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "%s", functionsJsonPathOutcome.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto functionsJsonPath = functionsJsonPathOutcome.TakeValue();
|
||||
auto jsonOutcome = JsonSerializationUtils::ReadJsonFile(functionsJsonPath);
|
||||
if (!jsonOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "%s", jsonOutcome.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
if (!azslCompiler.ParseIaPopulateFunctionData(jsonOutcome.GetValue(), functions))
|
||||
{
|
||||
functions.clear();
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Failed to find shader functions.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns the content of the hlsl file for the given supervariant as produced by ShaderAsssetBuilder2.
|
||||
// In addition to the content it also returns the full path of the hlsl file in @hlslSourcePath.
|
||||
static AZStd::string LoadHlslFileFromShaderAssetBuilder2(
|
||||
const RHI::ShaderPlatformInterface* shaderPlatformInterface, const AssetBuilderSDK::PlatformInfo& platformInfo,
|
||||
const AZStd::string& shaderSourceFileFullPath, const RPI::SupervariantIndex supervariantIndex, AZStd::string& hlslSourcePath)
|
||||
{
|
||||
auto hlslSourcePathOutcome = ShaderBuilderUtility::ObtainBuildArtifactPathFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), platformInfo.m_identifier, shaderSourceFileFullPath, supervariantIndex.GetIndex(),
|
||||
AZ::RPI::ShaderAssetSubId::GeneratedHlslSource);
|
||||
if (!hlslSourcePathOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "%s", hlslSourcePathOutcome.GetError().c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
hlslSourcePath = hlslSourcePathOutcome.TakeValue();
|
||||
Outcome<AZStd::string, AZStd::string> hlslSourceOutcome = Utils::ReadFile(hlslSourcePath);
|
||||
if (!hlslSourceOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
ShaderVariantAssetBuilder2Name, false, "Failed to obtain shader source from %s. [%s]", hlslSourcePath.c_str(),
|
||||
hlslSourceOutcome.TakeError().c_str());
|
||||
return "";
|
||||
}
|
||||
return hlslSourceOutcome.TakeValue();
|
||||
}
|
||||
|
||||
void ShaderVariantAssetBuilder2::ProcessShaderVariantTreeJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const
|
||||
{
|
||||
AZStd::string variantListFullPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.data(), request.m_sourceFile.data(), variantListFullPath, true);
|
||||
|
||||
RPI::ShaderVariantListSourceData shaderVariantListDescriptor;
|
||||
if (!RPI::JsonUtils::LoadObjectFromFile(variantListFullPath, shaderVariantListDescriptor))
|
||||
{
|
||||
AZ_Assert(false, "Failed to parse Shader Variant List Descriptor JSON [%s]", variantListFullPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
const AZStd::string& shaderSourceFileFullPath = request.m_jobDescription.m_jobParameters.at(ShaderSourceFilePathJobParam);
|
||||
|
||||
//For debugging purposes will create a dummy azshadervarianttree file.
|
||||
AZStd::string shaderName;
|
||||
AzFramework::StringFunc::Path::GetFileName(shaderSourceFileFullPath.c_str(), shaderName);
|
||||
|
||||
// No error checking because the same calls were already executed during CreateJobs()
|
||||
auto descriptorParseOutcome = ShaderBuilderUtility::LoadShaderDataJson(shaderSourceFileFullPath);
|
||||
RPI::ShaderSourceData shaderSourceDescriptor = descriptorParseOutcome.TakeValue();
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout;
|
||||
|
||||
// Request the list of valid shader platform interfaces for the target platform.
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces =
|
||||
ShaderBuilderUtility::DiscoverEnabledShaderPlatformInterfaces(request.m_platformInfo, shaderSourceDescriptor);
|
||||
if (platformInterfaces.empty())
|
||||
{
|
||||
// No work to do. Exit gracefully.
|
||||
AZ_TracePrintf(
|
||||
ShaderVariantAssetBuilder2Name,
|
||||
"No azshadervarianttree is produced on behalf of %s because all valid RHI backends were disabled for this shader.\n",
|
||||
shaderSourceFileFullPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// set the input file for eventual error messages, but the compiler won't be called on it.
|
||||
AZStd::string azslFullPath;
|
||||
ShaderBuilderUtility::GetAbsolutePathToAzslFile(shaderSourceFileFullPath, shaderSourceDescriptor.m_source, azslFullPath);
|
||||
AzslCompiler azslc(azslFullPath);
|
||||
|
||||
AZStd::string previousLoopApiName;
|
||||
for (RHI::ShaderPlatformInterface* shaderPlatformInterface : platformInterfaces)
|
||||
{
|
||||
auto thisLoopApiName = shaderPlatformInterface->GetAPIName().GetStringView();
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> loopLocal_ShaderOptionGroupLayout =
|
||||
LoadShaderOptionsGroupLayoutFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface, request.m_platformInfo, azslc, shaderSourceFileFullPath, RPI::DefaultSupervariantIndex);
|
||||
if (!loopLocal_ShaderOptionGroupLayout)
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
if (shaderOptionGroupLayout && shaderOptionGroupLayout->GetHash() != loopLocal_ShaderOptionGroupLayout->GetHash())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "There was a discrepancy in shader options between %s and %s", previousLoopApiName.c_str(), thisLoopApiName.data());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
shaderOptionGroupLayout = loopLocal_ShaderOptionGroupLayout;
|
||||
previousLoopApiName = thisLoopApiName;
|
||||
}
|
||||
|
||||
RPI::ShaderVariantTreeAssetCreator shaderVariantTreeAssetCreator;
|
||||
shaderVariantTreeAssetCreator.Begin(Uuid::CreateRandom());
|
||||
shaderVariantTreeAssetCreator.SetShaderOptionGroupLayout(*shaderOptionGroupLayout);
|
||||
shaderVariantTreeAssetCreator.SetVariantInfos(shaderVariantListDescriptor.m_shaderVariants);
|
||||
Data::Asset<RPI::ShaderVariantTreeAsset> shaderVariantTreeAsset;
|
||||
if (!shaderVariantTreeAssetCreator.End(shaderVariantTreeAsset))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Failed to build Shader Variant Tree Asset");
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::string filename = AZStd::string::format("%s.%s", shaderName.c_str(), RPI::ShaderVariantTreeAsset::Extension);
|
||||
AZStd::string assetPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_tempDirPath.c_str(), filename.c_str(), assetPath, true);
|
||||
if (!AZ::Utils::SaveObjectToFile(assetPath, AZ::DataStream::ST_BINARY, shaderVariantTreeAsset.Get()))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Failed to save Shader Variant Tree Asset to \"%s\"", assetPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
AssetBuilderSDK::JobProduct assetProduct;
|
||||
assetProduct.m_productSubID = RPI::ShaderVariantTreeAsset::ProductSubID;
|
||||
assetProduct.m_productFileName = assetPath;
|
||||
assetProduct.m_productAssetType = azrtti_typeid<RPI::ShaderVariantTreeAsset>();
|
||||
assetProduct.m_dependenciesHandled = true; // This builder has no dependencies to output
|
||||
response.m_outputProducts.push_back(assetProduct);
|
||||
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Shader Variant Tree Asset [%s] compiled successfully.\n", assetPath.c_str());
|
||||
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
}
|
||||
|
||||
void ShaderVariantAssetBuilder2::ProcessShaderVariantJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const
|
||||
{
|
||||
const AZStd::sys_time_t startTime = AZStd::GetTimeNowTicks();
|
||||
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
||||
|
||||
AZStd::string fullPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.data(), request.m_sourceFile.data(), fullPath, true);
|
||||
|
||||
const auto& jobParameters = request.m_jobDescription.m_jobParameters;
|
||||
const AZStd::string& shaderSourceFileFullPath = jobParameters.at(ShaderSourceFilePathJobParam);
|
||||
AZStd::string shaderFileName;
|
||||
AzFramework::StringFunc::Path::GetFileName(shaderSourceFileFullPath.c_str(), shaderFileName);
|
||||
|
||||
const AZStd::string& variantJsonString = jobParameters.at(ShaderVariantJobVariantParam);
|
||||
RPI::ShaderVariantListSourceData::VariantInfo variantInfo;
|
||||
const bool fromJsonStringSuccess = AZ::RPI::JsonUtils::LoadObjectFromJsonString(variantJsonString, variantInfo);
|
||||
AZ_Assert(fromJsonStringSuccess, "Failed to convert json string to VariantInfo");
|
||||
|
||||
RPI::ShaderSourceData shaderSourceDescriptor;
|
||||
AZStd::shared_ptr<ShaderFiles> sources = ShaderBuilderUtility::PrepareSourceInput(ShaderVariantAssetBuilder2Name, shaderSourceFileFullPath, shaderSourceDescriptor);
|
||||
|
||||
// set the input file for eventual error messages, but the compiler won't be called on it.
|
||||
AzslCompiler azslc(sources->m_azslSourceFullPath);
|
||||
|
||||
// Request the list of valid shader platform interfaces for the target platform.
|
||||
AZStd::vector<RHI::ShaderPlatformInterface*> platformInterfaces =
|
||||
ShaderBuilderUtility::DiscoverEnabledShaderPlatformInterfaces(request.m_platformInfo, shaderSourceDescriptor);
|
||||
if (platformInterfaces.empty())
|
||||
{
|
||||
// No work to do. Exit gracefully.
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name,
|
||||
"No azshader is produced on behalf of %s because all valid RHI backends were disabled for this shader.\n",
|
||||
shaderSourceFileFullPath.c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
return;
|
||||
}
|
||||
|
||||
auto supervariantList = ShaderBuilderUtility::GetSupervariantListFromShaderSourceData(shaderSourceDescriptor);
|
||||
|
||||
GlobalBuildOptions buildOptions = ReadBuildOptions(ShaderVariantAssetBuilder2Name);
|
||||
// At this moment We have global build options that should be merged with the build options that are common
|
||||
// to all the supervariants of this shader.
|
||||
buildOptions.m_compilerArguments.Merge(shaderSourceDescriptor.m_compiler);
|
||||
|
||||
//! The ShaderOptionGroupLayout is common across all RHIs & Supervariants
|
||||
RPI::Ptr<RPI::ShaderOptionGroupLayout> shaderOptionGroupLayout = nullptr;
|
||||
|
||||
// Generate shaders for each of those ShaderPlatformInterfaces.
|
||||
for (RHI::ShaderPlatformInterface* shaderPlatformInterface : platformInterfaces)
|
||||
{
|
||||
AZ_TraceContext("ShaderPlatformInterface", shaderPlatformInterface->GetAPIName().GetCStr());
|
||||
|
||||
// Loop through all the Supervariants.
|
||||
uint32_t supervariantIndexCounter = 0;
|
||||
for (const auto& supervariantInfo : supervariantList)
|
||||
{
|
||||
RPI::SupervariantIndex supervariantIndex(supervariantIndexCounter);
|
||||
|
||||
// Check if we were canceled before we do any heavy processing of
|
||||
// the shader variant data.
|
||||
if (jobCancelListener.IsCancelled())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::string shaderStemNamePrefix = shaderFileName;
|
||||
if (supervariantIndex.GetIndex() > 0)
|
||||
{
|
||||
shaderStemNamePrefix += supervariantInfo.m_name.GetStringView();
|
||||
}
|
||||
|
||||
// We need these additional pieces of information To build a shader variant asset:
|
||||
// 1- ShaderOptionsGroupLayout (Need to load it once, because it's the same acrosss all supervariants + RHIs)
|
||||
// 2- entryFunctions
|
||||
// 3- hlsl code.
|
||||
|
||||
// 1- ShaderOptionsGroupLayout
|
||||
if (!shaderOptionGroupLayout)
|
||||
{
|
||||
shaderOptionGroupLayout =
|
||||
LoadShaderOptionsGroupLayoutFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface, request.m_platformInfo, azslc, shaderSourceFileFullPath, supervariantIndex);
|
||||
if (!shaderOptionGroupLayout)
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2- entryFunctions.
|
||||
AzslFunctions azslFunctions;
|
||||
LoadShaderFunctionsFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface, request.m_platformInfo, azslc, shaderSourceFileFullPath, supervariantIndex, azslFunctions);
|
||||
if (azslFunctions.empty())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
MapOfStringToStageType shaderEntryPoints;
|
||||
if (shaderSourceDescriptor.m_programSettings.m_entryPoints.empty())
|
||||
{
|
||||
AZ_TracePrintf(
|
||||
ShaderVariantAssetBuilder2Name,
|
||||
"ProgramSettings do not specify entry points, will use GetDefaultEntryPointsFromShader()\n");
|
||||
ShaderBuilderUtility::GetDefaultEntryPointsFromFunctionDataList(azslFunctions, shaderEntryPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& entryPoint : shaderSourceDescriptor.m_programSettings.m_entryPoints)
|
||||
{
|
||||
shaderEntryPoints[entryPoint.m_name] = entryPoint.m_type;
|
||||
}
|
||||
}
|
||||
|
||||
// 3- hlslCode
|
||||
AZStd::string hlslSourcePath;
|
||||
AZStd::string hlslCode = LoadHlslFileFromShaderAssetBuilder2(
|
||||
shaderPlatformInterface, request.m_platformInfo, shaderSourceFileFullPath, supervariantIndex, hlslSourcePath);
|
||||
if (hlslCode.empty() || hlslSourcePath.empty())
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup the shader variant creation context:
|
||||
ShaderVariantCreationContext2 shaderVariantCreationContext =
|
||||
{
|
||||
*shaderPlatformInterface, request.m_platformInfo, buildOptions.m_compilerArguments, request.m_tempDirPath,
|
||||
startTime,
|
||||
shaderSourceDescriptor,
|
||||
*shaderOptionGroupLayout.get(),
|
||||
shaderEntryPoints,
|
||||
Uuid::CreateRandom(),
|
||||
shaderStemNamePrefix,
|
||||
hlslSourcePath, hlslCode
|
||||
};
|
||||
|
||||
AZStd::optional<RHI::ShaderPlatformInterface::ByProducts> outputByproducts;
|
||||
auto shaderVariantAssetOutcome = CreateShaderVariantAsset(variantInfo, shaderVariantCreationContext, outputByproducts);
|
||||
if (!shaderVariantAssetOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "%s\n", shaderVariantAssetOutcome.GetError().c_str());
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
Data::Asset<RPI::ShaderVariantAsset2> shaderVariantAsset = shaderVariantAssetOutcome.TakeValue();
|
||||
|
||||
|
||||
// Time to save the asset in the tmp folder so it ends up in the Cache folder.
|
||||
const uint32_t productSubID = RPI::ShaderVariantAsset2::MakeAssetProductSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), supervariantIndex.GetIndex(),
|
||||
shaderVariantAsset->GetStableId());
|
||||
AssetBuilderSDK::JobProduct assetProduct;
|
||||
if (!SerializeOutShaderVariantAsset(shaderVariantAsset, shaderStemNamePrefix,
|
||||
request.m_tempDirPath, *shaderPlatformInterface, productSubID,
|
||||
assetProduct))
|
||||
{
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
||||
return;
|
||||
}
|
||||
response.m_outputProducts.push_back(assetProduct);
|
||||
|
||||
if (outputByproducts)
|
||||
{
|
||||
// add byproducts as job output products:
|
||||
uint32_t subProductType = RPI::ShaderVariantAsset2::ShaderVariantAsset2SubProductType;
|
||||
for (const AZStd::string& byproduct : outputByproducts.value().m_intermediatePaths)
|
||||
{
|
||||
AssetBuilderSDK::JobProduct jobProduct;
|
||||
jobProduct.m_productFileName = byproduct;
|
||||
jobProduct.m_productAssetType = Uuid::CreateName("DebugInfoByProduct-PdbOrDxilTxt");
|
||||
jobProduct.m_productSubID = RPI::ShaderVariantAsset2::MakeAssetProductSubId(
|
||||
shaderPlatformInterface->GetAPIUniqueIndex(), supervariantIndex.GetIndex(), shaderVariantAsset->GetStableId(),
|
||||
subProductType++);
|
||||
response.m_outputProducts.push_back(AZStd::move(jobProduct));
|
||||
}
|
||||
}
|
||||
supervariantIndexCounter++;
|
||||
} // End of supervariant for block
|
||||
|
||||
}
|
||||
|
||||
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
||||
}
|
||||
|
||||
bool ShaderVariantAssetBuilder2::SerializeOutShaderVariantAsset(
|
||||
const Data::Asset<RPI::ShaderVariantAsset2> shaderVariantAsset, const AZStd::string& shaderStemNamePrefix,
|
||||
const AZStd::string& tempDirPath,
|
||||
const RHI::ShaderPlatformInterface& shaderPlatformInterface, const uint32_t productSubID, AssetBuilderSDK::JobProduct& assetProduct)
|
||||
{
|
||||
AZStd::string filename = AZStd::string::format(
|
||||
"%s_%s_%u.%s", shaderStemNamePrefix.c_str(), shaderPlatformInterface.GetAPIName().GetCStr(),
|
||||
shaderVariantAsset->GetStableId().GetIndex(), RPI::ShaderVariantAsset2::Extension);
|
||||
|
||||
AZStd::string assetPath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(tempDirPath.c_str(), filename.c_str(), assetPath, true);
|
||||
|
||||
if (!AZ::Utils::SaveObjectToFile(assetPath, AZ::DataStream::ST_BINARY, shaderVariantAsset.Get()))
|
||||
{
|
||||
AZ_Error(ShaderVariantAssetBuilder2Name, false, "Failed to save Shader Variant Asset to \"%s\"", assetPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
assetProduct.m_productSubID = productSubID;
|
||||
assetProduct.m_productFileName = assetPath;
|
||||
assetProduct.m_productAssetType = azrtti_typeid<RPI::ShaderVariantAsset2>();
|
||||
assetProduct.m_dependenciesHandled = true; // This builder has no dependencies to output
|
||||
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Shader Variant Asset [%s] compiled successfully.\n", assetPath.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
AZ::Outcome<Data::Asset<RPI::ShaderVariantAsset2>, AZStd::string> ShaderVariantAssetBuilder2::CreateShaderVariantAsset(
|
||||
const RPI::ShaderVariantListSourceData::VariantInfo& shaderVariantInfo,
|
||||
ShaderVariantCreationContext2& creationContext,
|
||||
AZStd::optional<RHI::ShaderPlatformInterface::ByProducts>& outputByproducts)
|
||||
{
|
||||
// Temporary structure used for sorting and caching intermediate results
|
||||
struct OptionCache
|
||||
{
|
||||
AZ::Name m_optionName;
|
||||
AZ::Name m_valueName;
|
||||
RPI::ShaderOptionIndex m_optionIndex; // Cached m_optionName
|
||||
RPI::ShaderOptionValue m_value; // Cached m_valueName
|
||||
};
|
||||
AZStd::vector<OptionCache> optionList;
|
||||
// We can not have more options than the number of options in the layout:
|
||||
optionList.reserve(creationContext.m_shaderOptionGroupLayout.GetShaderOptionCount());
|
||||
|
||||
// This loop will validate and cache the indices for each option value:
|
||||
for (const auto& shaderOption : shaderVariantInfo.m_options)
|
||||
{
|
||||
Name optionName{shaderOption.first};
|
||||
Name optionValue{shaderOption.second};
|
||||
|
||||
RPI::ShaderOptionIndex optionIndex = creationContext.m_shaderOptionGroupLayout.FindShaderOptionIndex(optionName);
|
||||
if (optionIndex.IsNull())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Invalid shader option: %s", optionName.GetCStr()));
|
||||
}
|
||||
|
||||
const RPI::ShaderOptionDescriptor& option = creationContext.m_shaderOptionGroupLayout.GetShaderOption(optionIndex);
|
||||
RPI::ShaderOptionValue value = option.FindValue(optionValue);
|
||||
if (value.IsNull())
|
||||
{
|
||||
return AZ::Failure(
|
||||
AZStd::string::format("Invalid value (%s) for shader option: %s", optionValue.GetCStr(), optionName.GetCStr()));
|
||||
}
|
||||
|
||||
optionList.push_back(OptionCache{optionName, optionValue, optionIndex, value});
|
||||
}
|
||||
|
||||
// Create one instance of the shader variant
|
||||
RPI::ShaderOptionGroup optionGroup(&creationContext.m_shaderOptionGroupLayout);
|
||||
|
||||
//! Contains the series of #define macro values that define a variant. Can be empty (root variant).
|
||||
//! If this string is NOT empty, a new temporary hlsl file will be created that will be the combination
|
||||
//! of this string + @m_hlslSourceContent.
|
||||
AZStd::string hlslCodeToPrependForVariant;
|
||||
|
||||
// We want to go over all options listed in the variant and set their respective values
|
||||
// This loop will populate the optionGroup and m_shaderCodePrefix in order of the option priority
|
||||
for (const auto& optionCache : optionList)
|
||||
{
|
||||
const RPI::ShaderOptionDescriptor& option = creationContext.m_shaderOptionGroupLayout.GetShaderOption(optionCache.m_optionIndex);
|
||||
|
||||
// Assign the option value specified in the variant:
|
||||
option.Set(optionGroup, optionCache.m_value);
|
||||
|
||||
// Populate all shader option defines. We have already confirmed they're valid.
|
||||
hlslCodeToPrependForVariant += AZStd::string::format(
|
||||
"#define %s_OPTION_DEF %s\n", optionCache.m_optionName.GetCStr(), optionCache.m_valueName.GetCStr());
|
||||
}
|
||||
|
||||
AZStd::string variantShaderSourcePath;
|
||||
// Check if we need to prepend any code prefix
|
||||
if (!hlslCodeToPrependForVariant.empty())
|
||||
{
|
||||
// Prepend any shader code prefix that we should apply to this variant
|
||||
// and save it back to a file.
|
||||
AZStd::string variantShaderSourceString(hlslCodeToPrependForVariant);
|
||||
variantShaderSourceString += creationContext.m_hlslSourceContent;
|
||||
|
||||
AZStd::string shaderAssetName = AZStd::string::format(
|
||||
"%s_%s_%u.hlsl", creationContext.m_shaderStemNamePrefix.c_str(),
|
||||
creationContext.m_shaderPlatformInterface.GetAPIName().GetCStr(), shaderVariantInfo.m_stableId);
|
||||
AzFramework::StringFunc::Path::Join(
|
||||
creationContext.m_tempDirPath.c_str(), shaderAssetName.c_str(), variantShaderSourcePath, true, true);
|
||||
|
||||
auto outcome = Utils::WriteFile(variantShaderSourceString, variantShaderSourcePath);
|
||||
if (!outcome.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to create file %s", variantShaderSourcePath.c_str()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
variantShaderSourcePath = creationContext.m_hlslSourcePath;
|
||||
}
|
||||
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Variant StableId: %u", shaderVariantInfo.m_stableId);
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Variant Shader Options: %s", optionGroup.ToString().c_str());
|
||||
|
||||
const RPI::ShaderVariantStableId shaderVariantStableId{shaderVariantInfo.m_stableId};
|
||||
|
||||
// By this time the optionGroup was populated with all option values for the variant and
|
||||
// the m_shaderCodePrefix contains all option related preprocessing macros
|
||||
// Let's add the requested variant:
|
||||
RPI::ShaderVariantAssetCreator2 variantCreator;
|
||||
RPI::ShaderOptionGroup shaderOptions{&creationContext.m_shaderOptionGroupLayout, optionGroup.GetShaderVariantId()};
|
||||
variantCreator.Begin(
|
||||
creationContext.m_shaderVariantAssetId, optionGroup.GetShaderVariantId(), shaderVariantStableId,
|
||||
shaderOptions.IsFullySpecified());
|
||||
|
||||
const AZStd::unordered_map<AZStd::string, RPI::ShaderStageType>& shaderEntryPoints = creationContext.m_shaderEntryPoints;
|
||||
for (const auto& shaderEntryPoint : shaderEntryPoints)
|
||||
{
|
||||
auto shaderEntryName = shaderEntryPoint.first;
|
||||
auto shaderStageType = shaderEntryPoint.second;
|
||||
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Entry Point: %s", shaderEntryName.c_str());
|
||||
AZ_TracePrintf(ShaderVariantAssetBuilder2Name, "Begin compiling shader function \"%s\"", shaderEntryName.c_str());
|
||||
|
||||
auto assetBuilderShaderType = ShaderBuilderUtility::ToAssetBuilderShaderType(shaderStageType);
|
||||
|
||||
// Compile HLSL to the platform specific shader.
|
||||
RHI::ShaderPlatformInterface::StageDescriptor descriptor;
|
||||
bool shaderWasCompiled = creationContext.m_shaderPlatformInterface.CompilePlatformInternal(
|
||||
creationContext.m_platformInfo, variantShaderSourcePath, shaderEntryName, assetBuilderShaderType,
|
||||
creationContext.m_tempDirPath, descriptor, creationContext.m_shaderCompilerArguments);
|
||||
|
||||
if (!shaderWasCompiled)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Could not compile the shader function %s", shaderEntryName.c_str()));
|
||||
}
|
||||
// bubble up the byproducts to the caller by moving them to the context.
|
||||
outputByproducts.emplace(AZStd::move(descriptor.m_byProducts));
|
||||
|
||||
RHI::Ptr<RHI::ShaderStageFunction> shaderStageFunction = creationContext.m_shaderPlatformInterface.CreateShaderStageFunction(descriptor);
|
||||
variantCreator.SetShaderFunction(ToRHIShaderStage(assetBuilderShaderType), shaderStageFunction);
|
||||
|
||||
if (descriptor.m_byProducts.m_dynamicBranchCount != AZ::RHI::ShaderPlatformInterface::ByProducts::UnknownDynamicBranchCount)
|
||||
{
|
||||
AZ_TracePrintf(
|
||||
ShaderVariantAssetBuilder2Name, "Finished compiling shader function. Number of dynamic branches: %u",
|
||||
descriptor.m_byProducts.m_dynamicBranchCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_TracePrintf(
|
||||
ShaderVariantAssetBuilder2Name, "Finished compiling shader function. Number of dynamic branches: unknown");
|
||||
}
|
||||
}
|
||||
|
||||
Data::Asset<RPI::ShaderVariantAsset2> shaderVariantAsset;
|
||||
variantCreator.End(shaderVariantAsset);
|
||||
return AZ::Success(AZStd::move(shaderVariantAsset));
|
||||
}
|
||||
|
||||
} // ShaderBuilder
|
||||
} // AZ
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AssetBuilderSDK/AssetBuilderBusses.h>
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
|
||||
#include <Atom/RHI.Reflect/Base.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Edit/Shader/ShaderSourceData.h>
|
||||
#include <Atom/RPI.Edit/Shader/ShaderVariantListSourceData.h>
|
||||
|
||||
#include "ShaderBuilderUtility.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
struct AzslData;
|
||||
|
||||
//! This is nothing more than a class to help consolidate all
|
||||
//! the data needed to generate a shader variant and prevent
|
||||
//! all the functions involved in the process to have too many
|
||||
//! arguments.
|
||||
struct ShaderVariantCreationContext2
|
||||
{
|
||||
RHI::ShaderPlatformInterface& m_shaderPlatformInterface;
|
||||
const AssetBuilderSDK::PlatformInfo& m_platformInfo;
|
||||
const RHI::ShaderCompilerArguments& m_shaderCompilerArguments;
|
||||
//! Used to write temporary files during shader compilation, like *.hlsl, or *.air, or *.metallib, etc.
|
||||
const AZStd::string& m_tempDirPath;
|
||||
//! Used to synchronize versions of the ShaderAsset and ShaderVariantAsset,
|
||||
//! especially during hot-reload. A (ShaderVariantAsset.timestamp) >= (ShaderAsset.timestamp).
|
||||
const AZStd::sys_time_t m_assetBuildTimestamp;
|
||||
const RPI::ShaderSourceData& m_shaderSourceDataDescriptor;
|
||||
const RPI::ShaderOptionGroupLayout& m_shaderOptionGroupLayout;
|
||||
const MapOfStringToStageType& m_shaderEntryPoints;
|
||||
const Data::AssetId m_shaderVariantAssetId;
|
||||
const AZStd::string& m_shaderStemNamePrefix; //<shaderName>-<supervariantName>
|
||||
const AZStd::string& m_hlslSourcePath;
|
||||
const AZStd::string& m_hlslSourceContent;
|
||||
};
|
||||
|
||||
class ShaderVariantAssetBuilder2
|
||||
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(ShaderVariantAssetBuilder2, "{C959AEC2-2083-4488-AD88-F61B1144535B}");
|
||||
|
||||
static constexpr char ShaderVariantAssetBuilder2JobKey[] = "Shader Variant Asset 2";
|
||||
|
||||
ShaderVariantAssetBuilder2() = default;
|
||||
~ShaderVariantAssetBuilder2() = default;
|
||||
|
||||
// Asset Builder Callback Functions ...
|
||||
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const;
|
||||
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const;
|
||||
|
||||
//! The ShaderVariantAsset returned by this function won't be written to the filesystem.
|
||||
//! You should call SerializeOutShaderVariantAsset to write it to the temp folder assigned
|
||||
//! by the asset processor.
|
||||
static AZ::Outcome<Data::Asset<RPI::ShaderVariantAsset2>, AZStd::string> CreateShaderVariantAsset(
|
||||
const RPI::ShaderVariantListSourceData::VariantInfo& shaderVariantInfo,
|
||||
ShaderVariantCreationContext2& creationContext,
|
||||
AZStd::optional<RHI::ShaderPlatformInterface::ByProducts>& outputByproducts);
|
||||
|
||||
static bool SerializeOutShaderVariantAsset(
|
||||
const Data::Asset<RPI::ShaderVariantAsset2> shaderVariantAsset,
|
||||
const AZStd::string& shaderStemNamePrefix, const AZStd::string& tempDirPath,
|
||||
const RHI::ShaderPlatformInterface& shaderPlatformInterface, const uint32_t productSubID, AssetBuilderSDK::JobProduct& assetProduct);
|
||||
|
||||
// AssetBuilderSDK::AssetBuilderCommandBus interface overrides ...
|
||||
void ShutDown() override { };
|
||||
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(ShaderVariantAssetBuilder2);
|
||||
|
||||
static constexpr uint32_t ShaderVariantLoadErrorParam = 0;
|
||||
static constexpr uint32_t ShaderSourceFilePathJobParam = 2;
|
||||
static constexpr uint32_t ShaderVariantJobVariantParam = 3;
|
||||
static constexpr uint32_t ShouldExitEarlyFromProcessJobParam = 4;
|
||||
|
||||
//! Called from ProcessJob when the job is supposed to create a ShaderVariantTreeAsset.
|
||||
void ProcessShaderVariantTreeJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const;
|
||||
|
||||
//! Called from ProcessJob when the job is supposed to create ShaderVariantAssets. One ShaderVariantAsset will be produced per RHI::APIType
|
||||
//! supported by the platform.
|
||||
void ProcessShaderVariantJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const;
|
||||
|
||||
static AZStd::string GetShaderVariantTreeAssetJobKey() { return AZStd::string::format("%s_varianttree", ShaderVariantAssetBuilder2JobKey); }
|
||||
static AZStd::string GetShaderVariantAssetJobKey(RPI::ShaderVariantStableId variantStableId) { return AZStd::string::format("%s_variant_%u", ShaderVariantAssetBuilder2JobKey, variantStableId.GetIndex()); }
|
||||
|
||||
};
|
||||
|
||||
} // ShaderBuilder
|
||||
} // AZ
|
||||
@@ -345,17 +345,21 @@ namespace AZ
|
||||
for(const SrgDataEntry& srgDataEntry : entry.second)
|
||||
{
|
||||
RHI::ShaderPlatformInterface* shaderPlatformInterface = srgDataEntry.first;
|
||||
|
||||
// The register number only makes sense if the platform uses "spaces",
|
||||
// since the register Id of the resource will not change even if the pipeline layout changes.
|
||||
// We can pass in a default ShaderCompilerArguments because all we care about is whether the shaderPlatformInterface
|
||||
// appends the
|
||||
// "--use-spaces" flag.
|
||||
AZStd::string azslCompilerParameters =
|
||||
shaderPlatformInterface->GetAzslCompilerParameters(RHI::ShaderCompilerArguments{});
|
||||
bool useRegisterId = (AzFramework::StringFunc::Find(azslCompilerParameters, "--use-spaces") != AZStd::string::npos);
|
||||
|
||||
const SrgData& srgData = srgDataEntry.second;
|
||||
|
||||
srgAssetCreator.BeginAPI(shaderPlatformInterface->GetAPIType());
|
||||
srgAssetCreator.SetBindingSlot(srgData.m_bindingSlot.m_index);
|
||||
|
||||
// The register number only makes sense if the platform uses "spaces",
|
||||
// since the register Id of the resource will not change even if the pipeline layout changes.
|
||||
// We can pass in a default ShaderCompilerArguments because all we care about is whether the shaderPlatformInterface appends the "--use-spaces" flag.
|
||||
AZStd::string azslCompilerParameters = shaderPlatformInterface->GetAzslCompilerParameters(RHI::ShaderCompilerArguments{});
|
||||
bool useRegisterId = (AzFramework::StringFunc::Find(azslCompilerParameters, "--use-spaces") != AZStd::string::npos);
|
||||
|
||||
// Samplers
|
||||
for (const SamplerSrgData& samplerData : srgData.m_samplers)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include "SrgLayoutUtility.h"
|
||||
|
||||
#include <Atom/RHI.Edit/Utils.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
namespace SrgLayoutUtility
|
||||
{
|
||||
static constexpr char SrgLayoutUtilityName[] = "SrgLayoutUtility";
|
||||
|
||||
RHI::ShaderInputImageType ToShaderInputImageType(TextureType textureType)
|
||||
{
|
||||
switch (textureType)
|
||||
{
|
||||
case TextureType::Texture1D:
|
||||
return RHI::ShaderInputImageType::Image1D;
|
||||
case TextureType::Texture1DArray:
|
||||
return RHI::ShaderInputImageType::Image1DArray;
|
||||
case TextureType::Texture2D:
|
||||
return RHI::ShaderInputImageType::Image2D;
|
||||
case TextureType::Texture2DArray:
|
||||
return RHI::ShaderInputImageType::Image2DArray;
|
||||
case TextureType::Texture2DMS:
|
||||
return RHI::ShaderInputImageType::Image2DMultisample;
|
||||
case TextureType::Texture2DMSArray:
|
||||
return RHI::ShaderInputImageType::Image2DMultisampleArray;
|
||||
case TextureType::Texture3D:
|
||||
return RHI::ShaderInputImageType::Image3D;
|
||||
case TextureType::TextureCube:
|
||||
return RHI::ShaderInputImageType::ImageCube;
|
||||
case TextureType::RwTexture1D:
|
||||
return RHI::ShaderInputImageType::Image1D;
|
||||
case TextureType::RwTexture1DArray:
|
||||
return RHI::ShaderInputImageType::Image1DArray;
|
||||
case TextureType::RwTexture2D:
|
||||
return RHI::ShaderInputImageType::Image2D;
|
||||
case TextureType::RwTexture2DArray:
|
||||
return RHI::ShaderInputImageType::Image2DArray;
|
||||
case TextureType::RwTexture3D:
|
||||
return RHI::ShaderInputImageType::Image3D;
|
||||
case TextureType::RasterizerOrderedTexture1D:
|
||||
return RHI::ShaderInputImageType::Image1D;
|
||||
case TextureType::RasterizerOrderedTexture1DArray:
|
||||
return RHI::ShaderInputImageType::Image1DArray;
|
||||
case TextureType::RasterizerOrderedTexture2D:
|
||||
return RHI::ShaderInputImageType::Image2D;
|
||||
case TextureType::RasterizerOrderedTexture2DArray:
|
||||
return RHI::ShaderInputImageType::Image2DArray;
|
||||
case TextureType::RasterizerOrderedTexture3D:
|
||||
return RHI::ShaderInputImageType::Image3D;
|
||||
case TextureType::SubpassInput:
|
||||
return RHI::ShaderInputImageType::SubpassInput;
|
||||
default:
|
||||
AZ_Assert(false, "Unhandled TextureType");
|
||||
return RHI::ShaderInputImageType::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
RHI::ShaderInputBufferType ToShaderInputBufferType(BufferType bufferType)
|
||||
{
|
||||
switch (bufferType)
|
||||
{
|
||||
case BufferType::Buffer:
|
||||
case BufferType::RwBuffer:
|
||||
case BufferType::RasterizerOrderedBuffer:
|
||||
return RHI::ShaderInputBufferType::Typed;
|
||||
case BufferType::AppendStructuredBuffer:
|
||||
case BufferType::ConsumeStructuredBuffer:
|
||||
case BufferType::RasterizerOrderedStructuredBuffer:
|
||||
case BufferType::RwStructuredBuffer:
|
||||
case BufferType::StructuredBuffer:
|
||||
return RHI::ShaderInputBufferType::Structured;
|
||||
case BufferType::RasterizerOrderedByteAddressBuffer:
|
||||
case BufferType::ByteAddressBuffer:
|
||||
case BufferType::RwByteAddressBuffer:
|
||||
return RHI::ShaderInputBufferType::Raw;
|
||||
case BufferType::RaytracingAccelerationStructure:
|
||||
return RHI::ShaderInputBufferType::AccelerationStructure;
|
||||
default:
|
||||
AZ_Assert(false, "Unhandled BufferType");
|
||||
return RHI::ShaderInputBufferType::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadShaderResourceGroupLayouts(
|
||||
[[maybe_unused]] const char* builderName, const SrgDataContainer& resourceGroups,
|
||||
const bool platformUsesRegisterSpaces, RPI::ShaderResourceGroupLayoutList& srgLayoutList)
|
||||
{
|
||||
// The register number only makes sense if the platform uses "spaces",
|
||||
// since the register Id of the resource will not change even if the pipeline layout changes.
|
||||
// All we care about is whether the shaderPlatformInterface appends the "--use-spaces" flag.
|
||||
bool useRegisterId = platformUsesRegisterSpaces;
|
||||
|
||||
// Load all SRGs included in source file
|
||||
for (const SrgData& srgData : resourceGroups)
|
||||
{
|
||||
RHI::Ptr<RHI::ShaderResourceGroupLayout> newSrgLayout = RHI::ShaderResourceGroupLayout::Create();
|
||||
newSrgLayout->SetName(AZ::Name{srgData.m_name.c_str()});
|
||||
newSrgLayout->SetBindingSlot(srgData.m_bindingSlot.m_index);
|
||||
|
||||
// Samplers
|
||||
for (const SamplerSrgData& samplerData : srgData.m_samplers)
|
||||
{
|
||||
if (samplerData.m_isDynamic)
|
||||
{
|
||||
newSrgLayout->AddShaderInput(
|
||||
{samplerData.m_nameId, samplerData.m_count,
|
||||
useRegisterId ? samplerData.m_registerId : RHI::UndefinedRegisterSlot});
|
||||
}
|
||||
else
|
||||
{
|
||||
newSrgLayout->AddStaticSampler(
|
||||
{samplerData.m_nameId, samplerData.m_descriptor,
|
||||
useRegisterId ? samplerData.m_registerId : RHI::UndefinedRegisterSlot});
|
||||
}
|
||||
}
|
||||
|
||||
// Images
|
||||
for (const TextureSrgData& textureData : srgData.m_textures)
|
||||
{
|
||||
const RHI::ShaderInputImageAccess imageAccess =
|
||||
textureData.m_isReadOnlyType ? RHI::ShaderInputImageAccess::Read : RHI::ShaderInputImageAccess::ReadWrite;
|
||||
|
||||
const RHI::ShaderInputImageType imageType = SrgLayoutUtility::ToShaderInputImageType(textureData.m_type);
|
||||
|
||||
if (imageType != RHI::ShaderInputImageType::Unknown)
|
||||
{
|
||||
if (textureData.m_count != aznumeric_cast<uint32_t>(-1))
|
||||
{
|
||||
newSrgLayout->AddShaderInput(
|
||||
{textureData.m_nameId, imageAccess, imageType, textureData.m_count,
|
||||
useRegisterId ? textureData.m_registerId : RHI::UndefinedRegisterSlot});
|
||||
}
|
||||
else
|
||||
{
|
||||
// unbounded array
|
||||
newSrgLayout->AddShaderInput(
|
||||
{textureData.m_nameId, imageAccess, imageType,
|
||||
useRegisterId ? textureData.m_registerId : RHI::UndefinedRegisterSlot});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(
|
||||
builderName, false, "Failed to build Shader Resource Group Asset: Image %s has an unknown type.",
|
||||
textureData.m_nameId.GetCStr());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Buffers
|
||||
{
|
||||
for (const ConstantBufferData& cbData : srgData.m_constantBuffers)
|
||||
{
|
||||
newSrgLayout->AddShaderInput(
|
||||
{cbData.m_nameId, RHI::ShaderInputBufferAccess::Constant, RHI::ShaderInputBufferType::Constant,
|
||||
cbData.m_count, cbData.m_strideSize, useRegisterId ? cbData.m_registerId : RHI::UndefinedRegisterSlot});
|
||||
}
|
||||
|
||||
for (const BufferSrgData& bufferData : srgData.m_buffers)
|
||||
{
|
||||
const RHI::ShaderInputBufferAccess bufferAccess =
|
||||
bufferData.m_isReadOnlyType ? RHI::ShaderInputBufferAccess::Read : RHI::ShaderInputBufferAccess::ReadWrite;
|
||||
|
||||
const RHI::ShaderInputBufferType bufferType = SrgLayoutUtility::ToShaderInputBufferType(bufferData.m_type);
|
||||
|
||||
if (bufferType != RHI::ShaderInputBufferType::Unknown)
|
||||
{
|
||||
if (bufferData.m_count != aznumeric_cast<uint32_t>(-1))
|
||||
{
|
||||
newSrgLayout->AddShaderInput(
|
||||
{bufferData.m_nameId, bufferAccess, bufferType, bufferData.m_count, bufferData.m_strideSize,
|
||||
useRegisterId ? bufferData.m_registerId : RHI::UndefinedRegisterSlot});
|
||||
}
|
||||
else
|
||||
{
|
||||
// unbounded array
|
||||
newSrgLayout->AddShaderInput(
|
||||
{bufferData.m_nameId, bufferAccess, bufferType, bufferData.m_strideSize,
|
||||
useRegisterId ? bufferData.m_registerId : RHI::UndefinedRegisterSlot});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(
|
||||
builderName, false,
|
||||
"Failed to build Shader Resource Group Asset: Buffer %s has un unknown type.",
|
||||
bufferData.m_nameId.GetCStr());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SRG Constants
|
||||
uint32_t constantDataRegisterId = useRegisterId ? srgData.m_srgConstantDataRegisterId : RHI::UndefinedRegisterSlot;
|
||||
for (const SrgConstantData& srgConstants : srgData.m_srgConstantData)
|
||||
{
|
||||
newSrgLayout->AddShaderInput(
|
||||
{srgConstants.m_nameId, srgConstants.m_constantByteOffset, srgConstants.m_constantByteSize,
|
||||
constantDataRegisterId});
|
||||
}
|
||||
|
||||
// Shader Variant Key fallback
|
||||
if (srgData.m_fallbackSize > 0)
|
||||
{
|
||||
// Designates this SRG as a ShaderVariantKey fallback
|
||||
newSrgLayout->SetShaderVariantKeyFallback(srgData.m_fallbackName, srgData.m_fallbackSize);
|
||||
}
|
||||
|
||||
srgLayoutList.push_back(newSrgLayout);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SrgLayoutUtility
|
||||
} // namespace ShaderBuilder
|
||||
} // AZ
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
|
||||
#include "CommonFiles/CommonTypes.h"
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include "ShaderBuilderUtility.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace ShaderBuilder
|
||||
{
|
||||
namespace SrgLayoutUtility
|
||||
{
|
||||
|
||||
bool LoadShaderResourceGroupLayouts(
|
||||
[[maybe_unused]] const char* builderName, const SrgDataContainer& resourceGroups, const bool platformUsesRegisterSpaces,
|
||||
RPI::ShaderResourceGroupLayoutList& srgLayoutList);
|
||||
|
||||
} // SrgLayoutUtility namespace
|
||||
} // ShaderBuilder namespace
|
||||
} // AZ
|
||||
@@ -34,8 +34,14 @@ set(FILES
|
||||
Source/Editor/AzslCompiler.h
|
||||
Source/Editor/ShaderVariantAssetBuilder.cpp
|
||||
Source/Editor/ShaderVariantAssetBuilder.h
|
||||
Source/Editor/ShaderVariantAssetBuilder2.cpp
|
||||
Source/Editor/ShaderVariantAssetBuilder2.h
|
||||
Source/Editor/AtomShaderConfig.cpp
|
||||
Source/Editor/AtomShaderConfig.h
|
||||
Source/Editor/PrecompiledShaderBuilder.cpp
|
||||
Source/Editor/PrecompiledShaderBuilder.h
|
||||
Source/Editor/ShaderAssetBuilder2.cpp
|
||||
Source/Editor/ShaderAssetBuilder2.h
|
||||
Source/Editor/SrgLayoutUtility.cpp
|
||||
Source/Editor/SrgLayoutUtility.h
|
||||
)
|
||||
|
||||
@@ -75,6 +75,8 @@ namespace AZ
|
||||
*/
|
||||
bool Finalize();
|
||||
|
||||
void SetName(const Name& name) { m_name = name; }
|
||||
const Name& GetName() const { return m_name; }
|
||||
|
||||
/**
|
||||
* Designates this SRG as ShaderVariantKey fallback by providing the generated
|
||||
@@ -272,6 +274,9 @@ namespace AZ
|
||||
|
||||
AZ_SERIALIZE_FRIEND();
|
||||
|
||||
//! Name of the ShaderResourceGroup as specified in the original *.azsl/*.azsli file.
|
||||
Name m_name;
|
||||
|
||||
AZStd::vector<ShaderInputStaticSamplerDescriptor> m_staticSamplers;
|
||||
|
||||
AZStd::vector<ShaderInputBufferDescriptor> m_inputsForBuffers;
|
||||
|
||||
@@ -22,7 +22,8 @@ namespace AZ
|
||||
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<ShaderResourceGroupLayout>()
|
||||
->Version(6)
|
||||
->Version(7)
|
||||
->Field("m_name", &ShaderResourceGroupLayout::m_name)
|
||||
->Field("m_staticSamplers", &ShaderResourceGroupLayout::m_staticSamplers)
|
||||
->Field("m_inputsForBuffers", &ShaderResourceGroupLayout::m_inputsForBuffers)
|
||||
->Field("m_inputsForImages", &ShaderResourceGroupLayout::m_inputsForImages)
|
||||
|
||||
@@ -66,21 +66,21 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
RHI::ConstPtr<RHI::ShaderResourceGroupLayout> PipelineLayout::MergeShaderResourceGroupLayouts(const AZStd::vector<const RHI::ShaderResourceGroupLayout*>& srgLayouts) const
|
||||
RHI::ConstPtr<RHI::ShaderResourceGroupLayout> PipelineLayout::MergeShaderResourceGroupLayouts(const AZStd::vector<const RHI::ShaderResourceGroupLayout*>& srgLayoutList) const
|
||||
{
|
||||
if (srgLayouts.empty())
|
||||
if (srgLayoutList.empty())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (srgLayouts.size() == 1)
|
||||
if (srgLayoutList.size() == 1)
|
||||
{
|
||||
return srgLayouts.front();
|
||||
return srgLayoutList.front();
|
||||
}
|
||||
|
||||
RHI::Ptr<RHI::ShaderResourceGroupLayout> mergedLayout = RHI::ShaderResourceGroupLayout::Create();
|
||||
mergedLayout->SetBindingSlot(srgLayouts.front()->GetBindingSlot());
|
||||
for (const RHI::ShaderResourceGroupLayout* srgLayout : srgLayouts)
|
||||
mergedLayout->SetBindingSlot(srgLayoutList.front()->GetBindingSlot());
|
||||
for (const RHI::ShaderResourceGroupLayout* srgLayout : srgLayoutList)
|
||||
{
|
||||
const uint32_t bindingSlot = srgLayout->GetBindingSlot();
|
||||
const auto& srgBindingInfo = m_layoutDescriptor->GetShaderResourceGroupBindingInfo(m_layoutDescriptor->GetShaderResourceGroupIndexFromBindingSlot(bindingSlot));
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace AZ
|
||||
RHI::ResultCode BuildMergedShaderResourceGroupPools();
|
||||
|
||||
// Creates a merged SRG layout from a list of SRG layouts.
|
||||
RHI::ConstPtr<RHI::ShaderResourceGroupLayout> MergeShaderResourceGroupLayouts(const AZStd::vector<const RHI::ShaderResourceGroupLayout*>& srgLayouts) const;
|
||||
RHI::ConstPtr<RHI::ShaderResourceGroupLayout> MergeShaderResourceGroupLayouts(const AZStd::vector<const RHI::ShaderResourceGroupLayout*>& srgLayoutList) const;
|
||||
|
||||
VkPipelineLayout m_nativePipelineLayout = VK_NULL_HANDLE;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <Atom/RHI.Edit/ShaderPlatformInterface.h>
|
||||
#include <Atom/RHI.Edit/ShaderCompilerArguments.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderCommonTypes.h>
|
||||
|
||||
#include <Atom/RHI.Reflect/Format.h>
|
||||
#include <Atom/RHI.Reflect/Limits.h>
|
||||
@@ -38,12 +38,13 @@ namespace AZ
|
||||
AZ_TYPE_INFO(AZ::RPI::ShaderSourceData, "{B7F00402-872B-4F82-A210-E1A79A366686}");
|
||||
AZ_CLASS_ALLOCATOR(ShaderSourceData, AZ::SystemAllocator, 0);
|
||||
|
||||
static const char* Extension;
|
||||
static constexpr char Extension[] = "shader";
|
||||
static constexpr char Extension2[] = "shader2";
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
//! Helper function. Returns true if @rhiName is present in m_disabledRhiBackends
|
||||
bool IsRhiBackendDisabled(const AZ::Name& rhiName);
|
||||
bool IsRhiBackendDisabled(const AZ::Name& rhiName) const;
|
||||
|
||||
struct EntryPoint
|
||||
{
|
||||
@@ -71,12 +72,49 @@ namespace AZ
|
||||
RHI::DepthStencilState m_depthStencilState;
|
||||
RHI::RasterState m_rasterState;
|
||||
RHI::TargetBlendState m_blendState;
|
||||
|
||||
// Hints for building the shader option group layout
|
||||
RPI::ShaderOptionGroupHints m_shaderOptionGroupHints;
|
||||
|
||||
//! List of RHI Backends (aka ShaderPlatformInterface) for which this shader should not be compiled.
|
||||
AZStd::vector<AZStd::string> m_disabledRhiBackends;
|
||||
|
||||
struct SupervariantInfo
|
||||
{
|
||||
AZ_TYPE_INFO(AZ::RPI::ShaderSourceData::SupervariantInfo, "{1132CF2A-C8AB-4DD2-AA90-3021D49AB955}");
|
||||
|
||||
//! Unique name of the supervariant.
|
||||
//! If left empty, the data refers to the default supervariant.
|
||||
AZ::Name m_name;
|
||||
|
||||
//! + MCPP Macro definition arguments + AZSLc arguments.
|
||||
//! These arguments are added after shader_global_build_options.json & m_compiler.m_azslcAdditionalFreeArguments.
|
||||
//! Arguments that start with "-D" are given to MCPP.
|
||||
//! Example: "-DMACRO1 -DMACRO2=3".
|
||||
//! all other arguments are given to AZSLc.
|
||||
//! Note the arguments are added in addition to the arguments
|
||||
//! in <GAME_PROJECT>/Config/shader_global_build_options.json
|
||||
AZStd::string m_plusArguments;
|
||||
|
||||
//! Opposite to @m_plusArguments.
|
||||
//! - MCPP Macro definition arguments - AZSLc arguments.
|
||||
//! Because there are global compilation arguments, this one is useful to remove some of those arguments
|
||||
//! in order to customize the compilation of a particular supervariant.
|
||||
AZStd::string m_minusArguments;
|
||||
|
||||
//! Helper function. Parses @m_minusArguments and @m_plusArguments, looks for arguments of type -D<name>[=<value>] and returns
|
||||
//! a list of <name> to remove.
|
||||
AZStd::vector<AZStd::string> GetCombinedListOfMacroDefinitionNamesToRemove() const;
|
||||
|
||||
//! Helper function. Parses @m_plusArguments, looks for arguments of type "-D<name>[=<value>]" and returns
|
||||
//! a list of "<name>[=<value>]".
|
||||
AZStd::vector<AZStd::string> GetMacroDefinitionsToAdd() const;
|
||||
|
||||
//! Helper function. Takes AZSLc arguments from @m_minusArguments and @m_plusArguments, removes them from @initialAzslcCompilerArguments.
|
||||
//! Takes AZSLc arguments from @m_plusArguments and appends them to @initialAzslcCompilerArguments.
|
||||
//! Returns a new string with customized arguments.
|
||||
AZStd::string GetCustomizedArgumentsForAzslc(const AZStd::string& initialAzslcCompilerArguments) const;
|
||||
};
|
||||
|
||||
//! Optional list of supervariants.
|
||||
AZStd::vector<SupervariantInfo> m_supervariants;
|
||||
};
|
||||
|
||||
} // namespace RPI
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <Atom/RPI.Reflect/AssetCreator.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroupLayout.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
//! The "builder" pattern class that creates a ShaderVariantAsset2.
|
||||
class ShaderVariantAssetCreator2 final
|
||||
: public AssetCreator<ShaderVariantAsset2>
|
||||
{
|
||||
public:
|
||||
//! Begins construction of the shader variant asset.
|
||||
//! @param assetId The "initial" assetId that the resulting ShaderVariantAsset will get.
|
||||
//! "initial" was quoted because in the end the asset processor will assign another assetId
|
||||
//! because on the UUID of the source asset (a *.shadervariantlist file) and the product subid
|
||||
//! that gets assign when returning the Job Response.
|
||||
//! It is still useful, because when creating the Root Variant for the ShaderAsset this assetId should
|
||||
//! match the value that will be assigned by the asset processor because the Root Variant is serialized
|
||||
//! as a Data::Asset<ShaderVariantAsset> inside the ShaderAsset.
|
||||
void Begin(const AZ::Data::AssetId& assetId, const ShaderVariantId& shaderVariantId, RPI::ShaderVariantStableId stableId, bool isFullyBaked);
|
||||
|
||||
//! Finalizes and assigns ownership of the asset to result, if successful.
|
||||
//! Otherwise false is returned and result is left untouched.
|
||||
bool End(Data::Asset<ShaderVariantAsset2>& result);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Methods for all shader variant types
|
||||
|
||||
//! Set the timestamp value when the ProcessJob() started.
|
||||
//! This is needed to synchronize between the ShaderAsset and ShaderVariantAsset when hot-reloading shaders.
|
||||
//! The idea is that this timestamp must be greater or equal than the ShaderAsset.
|
||||
void SetBuildTimestamp(AZStd::sys_time_t buildTimestamp);
|
||||
|
||||
//! Assigns a shaderStageFunction, which contains the byte code, to the slot dictated by the shader stage.
|
||||
void SetShaderFunction(RHI::ShaderStage shaderStage, RHI::Ptr<RHI::ShaderStageFunction> shaderStageFunction);
|
||||
|
||||
};
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
@@ -30,6 +30,7 @@ namespace AZ
|
||||
AZ_CLASS_ALLOCATOR(ShaderVariantListSourceData, AZ::SystemAllocator, 0);
|
||||
|
||||
static constexpr const char* Extension = "shadervariantlist";
|
||||
static constexpr const char* Extension2 = "shadervariantlist2";
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <Atom/RPI.Public/Shader/ShaderVariant2.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
|
||||
#include <Atom/RPI.Reflect/Shader/IShaderVariantFinder2.h>
|
||||
|
||||
#include <Atom/RHI/DrawListTagRegistry.h>
|
||||
#include <Atom/RHI/PipelineLibrary.h>
|
||||
|
||||
#include <AtomCore/Instance/InstanceData.h>
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RHI
|
||||
{
|
||||
class PipelineStateCache;
|
||||
}
|
||||
|
||||
namespace RPI
|
||||
{
|
||||
/**
|
||||
* Shader2 is effectively an 'uber-shader' containing a collection of 'variants'. Variants are
|
||||
* designed to be 'variations' on the same core shader technique. To enforce this, every variant
|
||||
* in the shader shares the same pipeline layout (i.e. set of shader resource groups).
|
||||
*
|
||||
* A shader owns a library of pipeline states. When a variant is resolved to a pipeline state, its
|
||||
* lifetime is determined by the lifetime of the Shader2 (unless an explicit reference is taken). If
|
||||
* an asset reload event occurs, the pipeline state cache is reset.
|
||||
*
|
||||
* To use Shader2:
|
||||
* 1) Construct a ShaderOptionGroup instance using CreateShaderOptionGroup.
|
||||
* 2) Configure the group by setting values on shader options.
|
||||
* 3) Find the ShaderVariantStableId using the ShaderVariantId generated from the configured ShaderOptionGroup.
|
||||
* 4) Acquire the ShaderVariant2 instance using the ShaderVariantStableId.
|
||||
* 5) Configure a pipeline state descriptor on the variant; make local overrides as necessary (e.g. to configure runtime render state).
|
||||
* 6) Acquire a RHI::PipelineState instance from the shader using the configured pipeline state descriptor.
|
||||
*
|
||||
* Remember that the returned RHI::PipelineState instance lifetime is tied to the Shader2 lifetime.
|
||||
* If you need guarantee lifetime, it is safe to take a reference on the returned pipeline state.
|
||||
*/
|
||||
class Shader2 final
|
||||
: public Data::InstanceData
|
||||
, public Data::AssetBus::Handler
|
||||
, public ShaderVariantFinderNotificationBus2::Handler
|
||||
{
|
||||
friend class ShaderSystem;
|
||||
public:
|
||||
AZ_INSTANCE_DATA(Shader2, "{232D8BD6-3BD4-4842-ABD2-F380BD5B0863}");
|
||||
AZ_CLASS_ALLOCATOR(Shader2, SystemAllocator, 0);
|
||||
|
||||
/// Returns the shader instance associated with the provided asset.
|
||||
static Data::Instance<Shader2> FindOrCreate(const Data::Asset<ShaderAsset2>& shaderAsset, const Name& supervariantName);
|
||||
|
||||
~Shader2();
|
||||
AZ_DISABLE_COPY_MOVE(Shader2);
|
||||
|
||||
/// Constructs a shader option group suitable to generate a shader variant key for this shader.
|
||||
ShaderOptionGroup CreateShaderOptionGroup() const;
|
||||
|
||||
/// Finds the best matching ShaderVariant2 for the given shaderVariantId,
|
||||
/// If the variant is loaded and ready it will return the corresponding ShaderVariant2.
|
||||
/// If the variant is not yet available it will return the root ShaderVariant2.
|
||||
/// Callers should listen to ShaderReloadNotificationBus to get notified whenever the exact
|
||||
/// variant is loaded and available or if a variant changes, etc.
|
||||
/// This function should be your one stop shop to get a ShaderVariant2 from a ShaderVariantId.
|
||||
/// Alternatively: You can call FindVariantStableId() followed by GetVariant(shaderVariantStableId).
|
||||
const ShaderVariant2& GetVariant(const ShaderVariantId& shaderVariantId);
|
||||
|
||||
/// Finds the best matching shader variant asset and returns its StableId.
|
||||
/// In cases where you can't cache the ShaderVariant2, and recurrently you may need
|
||||
/// the same ShaderVariant2 at different times, then it can be convenient (and more performant) to call
|
||||
/// this method to cache the ShaderVariantStableId and call GetVariant(ShaderVariantStableId)
|
||||
/// when needed.
|
||||
/// If the asset is not immediately found in the file system, it will return the StableId
|
||||
/// of the root variant.
|
||||
/// Callers should listen to ShaderReloadNotificationBus to get notified whenever the exact
|
||||
/// variant is loaded and available or if a variant changes, etc.
|
||||
ShaderVariantSearchResult FindVariantStableId(const ShaderVariantId& shaderVariantId) const;
|
||||
|
||||
/// Returns the variant associated with the provided StableId.
|
||||
/// You should call FindVariantStableId() which caches the variant, later
|
||||
/// when this function is called the variant is fetched from a local map.
|
||||
/// If the variant is not found, the root variant is returned.
|
||||
/// "Alternatively: a more convenient approach is to call GetVariant(ShaderVariantId) which does both, the find and the get."
|
||||
const ShaderVariant2& GetVariant(ShaderVariantStableId shaderVariantStableId);
|
||||
|
||||
/// Convenient function that returns the root variant.
|
||||
const ShaderVariant2& GetRootVariant();
|
||||
|
||||
/// Returns the pipeline state type generated by variants of this shader.
|
||||
RHI::PipelineStateType GetPipelineStateType() const;
|
||||
|
||||
//! Returns the ShaderInputContract which describes which inputs the shader requires
|
||||
const ShaderInputContract& GetInputContract() const;
|
||||
|
||||
//! Returns the ShaderOutputContract which describes which outputs the shader requires
|
||||
const ShaderOutputContract& GetOutputContract() const;
|
||||
|
||||
/// Acquires a pipeline state directly from a descriptor.
|
||||
const RHI::PipelineState* AcquirePipelineState(const RHI::PipelineStateDescriptor& descriptor) const;
|
||||
|
||||
/// Finds and returns the shader resource group asset with the requested name. Returns an empty handle if no matching group was found.
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> FindShaderResourceGroupLayout(const Name& shaderResourceGroupName) const;
|
||||
|
||||
/// Finds and returns the shader resource group asset associated with the requested binding slot. Returns an empty handle if no matching group was found.
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> FindShaderResourceGroupLayout(uint32_t bindingSlot) const;
|
||||
|
||||
/// Finds and returns the shader resource group asset designated as a ShaderVariantKey fallback.
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> FindFallbackShaderResourceGroupLayout() const;
|
||||
|
||||
/// Returns the set of shader resource groups referenced by all variants in the shader asset.
|
||||
AZStd::array_view<RHI::Ptr<RHI::ShaderResourceGroupLayout>> GetShaderResourceGroupLayouts() const;
|
||||
|
||||
/// Returns a reference to the asset used to initialize this shader.
|
||||
const Data::Asset<ShaderAsset2>& GetAsset() const;
|
||||
|
||||
//! Returns the DrawListTag that identifies which Pass and View objects will process this shader.
|
||||
//! This tag corresponds to the ShaderAsset2 object's DrawListName.
|
||||
RHI::DrawListTag GetDrawListTag() const;
|
||||
|
||||
private:
|
||||
Shader2() = default;
|
||||
|
||||
static Data::Instance<Shader2> CreateInternal(ShaderAsset2& shaderAsset);
|
||||
|
||||
bool SelectSupervariant(const Name& supervariantName);
|
||||
|
||||
RHI::ResultCode Init(ShaderAsset2& shaderAsset);
|
||||
|
||||
void Shutdown();
|
||||
|
||||
ConstPtr<RHI::PipelineLibraryData> LoadPipelineLibrary() const;
|
||||
void SavePipelineLibrary() const;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
/// AssetBus overrides
|
||||
void OnAssetReloaded(Data::Asset<Data::AssetData> asset) override;
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
/// ShaderVariantFinderNotificationBus overrides
|
||||
void OnShaderVariantTreeAssetReady(Data::Asset<ShaderVariantTreeAsset> /*shaderVariantTreeAsset*/, bool /*isError*/) override {};
|
||||
void OnShaderVariantAssetReady(Data::Asset<ShaderVariantAsset2> shaderVariantAsset, bool IsError) override;
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Returns the path to the pipeline library cache file.
|
||||
AZStd::string GetPipelineLibraryPath() const;
|
||||
|
||||
//! A strong reference to the shader asset.
|
||||
Data::Asset<ShaderAsset2> m_asset;
|
||||
|
||||
//! Selects current supervariant to be used.
|
||||
//! This value is defined at instantiation.
|
||||
SupervariantIndex m_supervariantIndex;
|
||||
|
||||
//! The pipeline state type required by this shader.
|
||||
RHI::PipelineStateType m_pipelineStateType = RHI::PipelineStateType::Draw;
|
||||
|
||||
//! A cached pointer to the pipeline state cache owned by RHISystem.
|
||||
RHI::PipelineStateCache* m_pipelineStateCache = nullptr;
|
||||
|
||||
//! A handle to the pipeline library in the pipeline state cache.
|
||||
RHI::PipelineLibraryHandle m_pipelineLibraryHandle;
|
||||
|
||||
//! Used for thread safety for FindVariantStableId() and GetVariant().
|
||||
AZStd::shared_mutex m_variantCacheMutex;
|
||||
|
||||
//! The root variant always exist.
|
||||
ShaderVariant2 m_rootVariant;
|
||||
|
||||
//! Local cache of ShaderVariants (except for the root variant), searchable by StableId.
|
||||
//! Gets populated when GetVariant() is called.
|
||||
AZStd::unordered_map<ShaderVariantStableId, ShaderVariant2> m_shaderVariants;
|
||||
|
||||
//! DrawListTag associated with this shader.
|
||||
RHI::DrawListTag m_drawListTag;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
|
||||
//#include <Atom/RPI.Reflect/Shader/ShaderCommonTypes.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantKey.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
class Shader2;
|
||||
class ShaderAsset2;
|
||||
|
||||
/**
|
||||
* Connect to this EBus to get notifications whenever a Data::Instance<Shader> reloads its ShaderAsset.
|
||||
* The bus address is the AssetId of the ShaderAsset.
|
||||
*/
|
||||
class ShaderReloadNotifications2
|
||||
: public EBusTraits
|
||||
{
|
||||
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
typedef Data::AssetId BusIdType;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual ~ShaderReloadNotifications2() {}
|
||||
|
||||
//! Called when the ShaderAsset reinitializes itself in response to another asset being reloaded.
|
||||
virtual void OnShaderAssetReinitialized(const Data::Asset<ShaderAsset2>& shaderAsset) { AZ_UNUSED(shaderAsset); }
|
||||
|
||||
//! Called when the Shader instance reinitializes itself in response to the ShaderAsset being reloaded.
|
||||
virtual void OnShaderReinitialized(const Shader2& shader) { AZ_UNUSED(shader); }
|
||||
|
||||
//! Called when a particular shader variant is reinitialized.
|
||||
virtual void OnShaderVariantReinitialized(const Shader2& shader, const ShaderVariantId& shaderVariantId, ShaderVariantStableId shaderVariantStableId)
|
||||
{ AZ_UNUSED(shader); AZ_UNUSED(shaderVariantId); AZ_UNUSED(shaderVariantStableId) }
|
||||
};
|
||||
|
||||
typedef EBus<ShaderReloadNotifications2> ShaderReloadNotificationBus2;
|
||||
|
||||
} // namespace RPI
|
||||
} //namespace AZ
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
#include <Atom/RPI.Reflect/Base.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderResourceGroupAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantKey.h>
|
||||
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroupPool.h>
|
||||
@@ -63,6 +65,10 @@ namespace AZ
|
||||
/// Instantiates a unique shader resource group instance using its paired asset.
|
||||
static Data::Instance<ShaderResourceGroup> Create(const Data::Asset<ShaderResourceGroupAsset>& srgAsset);
|
||||
|
||||
/// [GFX TODO] [ATOM-15472] Shader Build Pipeline: Remove Deprecated Files And Functions That Predate The Shader Supervariants
|
||||
/// This is a temporary hack to enable integration of the new supervariant system.
|
||||
bool ReplaceSrgLayoutUsingShaderAsset(Data::Asset<ShaderAsset2> shaderAsset, const Name& supervariantName, const Name& srgName);
|
||||
|
||||
/// Queues a request that the underlying hardware shader resource group be compiled.
|
||||
void Compile();
|
||||
|
||||
@@ -278,6 +284,7 @@ namespace AZ
|
||||
ShaderResourceGroup() = default;
|
||||
|
||||
RHI::ResultCode Init(ShaderResourceGroupAsset& shaderResourceGroupAsset);
|
||||
|
||||
static AZ::Data::Instance<ShaderResourceGroup> CreateInternal(ShaderResourceGroupAsset& srgAsset);
|
||||
|
||||
/// A name to be used in error messages
|
||||
@@ -298,9 +305,12 @@ namespace AZ
|
||||
/// The shader resource group that can be submitted to the renderer
|
||||
RHI::Ptr<RHI::ShaderResourceGroup> m_shaderResourceGroup;
|
||||
|
||||
/// A reference to the parent template asset used to initialize and manipulate this group.
|
||||
/// A reference to the SRG asset used to initialize and manipulate this group.
|
||||
AZ::Data::Asset<ShaderResourceGroupAsset> m_asset;
|
||||
|
||||
/// A reference to the shader asset used to initialize and manipulate this group.
|
||||
AZ::Data::Asset<ShaderAsset2> m_shaderAsset;
|
||||
|
||||
/// A pointer to the layout inside of m_srgAsset
|
||||
const RHI::ShaderResourceGroupLayout* m_layout = nullptr;
|
||||
|
||||
|
||||
@@ -61,9 +61,6 @@ namespace AZ
|
||||
const ShaderAsset& shaderAsset,
|
||||
Data::Asset<ShaderVariantAsset> shaderVariantAsset);
|
||||
|
||||
// Returns a shader stage function associated with the provided enum value, or null if no function exists.
|
||||
const RHI::ShaderStageFunction* GetShaderStageFunction(RHI::ShaderStage shaderStage) const;
|
||||
|
||||
// Cached state from the asset to avoid an indirection.
|
||||
RHI::PipelineStateType m_pipelineStateType = RHI::PipelineStateType::Count;
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
|
||||
#include <Atom/RHI/DrawListTagRegistry.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
//! Represents the concrete state to configure a PipelineStateDescriptor. ShaderVariant2's match
|
||||
//! the RHI::PipelineStateType of the parent Shader instance. For shaders on the raster
|
||||
//! pipeline, the RHI::DrawFilterTag is also provided.
|
||||
class ShaderVariant2 final
|
||||
{
|
||||
friend class Shader2;
|
||||
public:
|
||||
ShaderVariant2() = default;
|
||||
AZ_DEFAULT_COPY_MOVE(ShaderVariant2);
|
||||
|
||||
//! Fills a pipeline state descriptor with settings provided by the ShaderVariant2. (Note that
|
||||
//! this does not fill the InputStreamLayout or OutputAttachmentLayout as that also requires
|
||||
//! information from the mesh data and pass system and must be done as a separate step).
|
||||
void ConfigurePipelineState(RHI::PipelineStateDescriptor& descriptor) const;
|
||||
|
||||
const ShaderVariantId& GetShaderVariantId() const { return m_shaderVariantAsset->GetShaderVariantId(); }
|
||||
|
||||
//! Returns whether the variant is fully baked variant (all options are static branches), or false if the
|
||||
//! variant uses dynamic branches for some shader options.
|
||||
//! If the shader variant is not fully baked, the ShaderVariantKeyFallbackValue must be correctly set when drawing.
|
||||
bool IsFullyBaked() const { return m_shaderVariantAsset->IsFullyBaked(); }
|
||||
|
||||
//! Return the timestamp when this asset was built.
|
||||
//! This is used to synchronize versions of the ShaderAsset and ShaderVariantAsset, especially during hot-reload.
|
||||
//! This timestamp must be >= than the ShaderAsset timestamp.
|
||||
AZStd::sys_time_t GetBuildTimestamp() const { return m_shaderVariantAsset->GetBuildTimestamp(); }
|
||||
|
||||
bool IsRootVariant() const { return m_shaderVariantAsset->IsRootVariant(); }
|
||||
|
||||
ShaderVariantStableId GetStableId() const { return m_shaderVariantAsset->GetStableId(); }
|
||||
|
||||
private:
|
||||
// Called by Shader. Initializes runtime data from asset data. Returns whether the call succeeded.
|
||||
bool Init(
|
||||
const ShaderAsset2& shaderAsset,
|
||||
Data::Asset<ShaderVariantAsset2> shaderVariantAsset,
|
||||
SupervariantIndex supervariantIndex);
|
||||
|
||||
// Cached state from the asset to avoid an indirection.
|
||||
RHI::PipelineStateType m_pipelineStateType = RHI::PipelineStateType::Count;
|
||||
|
||||
// State assigned to the pipeline state descriptor.
|
||||
RHI::ConstPtr<RHI::PipelineLayoutDescriptor> m_pipelineLayoutDescriptor;
|
||||
|
||||
Data::Asset<ShaderVariantAsset2> m_shaderVariantAsset;
|
||||
|
||||
const RHI::RenderStates* m_renderStates = nullptr; // Cached from ShaderAsset2.
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderCommonTypes.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantKey.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
class ShaderAsset2;
|
||||
class ShaderVariantTreeAsset;
|
||||
class ShaderVariantAsset2;
|
||||
|
||||
//! This is the AZ::Interface<> declaration for the singleton responsible
|
||||
//! for finding the best ShaderVariantAsset a shader can use.
|
||||
//! This interface is public only to the ShaderAsset class.
|
||||
//! The expectation is that when in need of shader variants the developer
|
||||
//! should use AZ::RPI::Shader::GetVariant().
|
||||
class IShaderVariantFinder2
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(IShaderVariantFinder2, "{4E041C2C-F158-412E-8961-76987EC75692}");
|
||||
|
||||
static constexpr const char* LogName = "IShaderVariantFinder2";
|
||||
|
||||
virtual ~IShaderVariantFinder2() = default;
|
||||
|
||||
//! This function should be your one stop shop.
|
||||
//! It simply queues the request to load a shader variant asset.
|
||||
//! This function will automatically queue the ShaderVariantTreeAsset for loading if not available.
|
||||
//! Afther the ShaderVariantTreeAsset is loaded and ready, it is used to find the best matching ShaderVariantStableId
|
||||
//! from the given ShaderVariantId. If a valid ShaderVariantStableId is found, it will be queued for loading.
|
||||
//! Eventually the caller will be notified via ShaderVariantFinderNotificationBus::OnShaderVariantAssetReady()
|
||||
//! The notification will occur on the Main Thread.
|
||||
virtual bool QueueLoadShaderVariantAssetByVariantId(
|
||||
Data::Asset<ShaderAsset2> shaderAsset, const ShaderVariantId& shaderVariantId, SupervariantIndex supervariantIndex) = 0;
|
||||
|
||||
//! This function does the first half of the work. It simply queues the loading of the ShaderVariantTreeAsset.
|
||||
//! Given the AssetId of a ShaderAsset it will try to find and load its corresponding ShaderVariantTreeAsset from
|
||||
//! the asset cache. If found, the asset will be loaded asynchronously and the caller will be notified via
|
||||
//! ShaderVariantFinderNotificationBus on main thread when the ShaderVariantTreeAsset is fully loaded.
|
||||
//! It is possible the requested ShaderVariantTreeAsset will never come into existence and in such
|
||||
//! case the caller will NEVER be notified.
|
||||
//! Returns true if the request was queued successfully.
|
||||
virtual bool QueueLoadShaderVariantTreeAsset(const Data::AssetId& shaderAssetId) = 0;
|
||||
|
||||
//! This function does the second half of the work.
|
||||
//! Given the AssetId of a ShaderVariantTreeAsset and the stable id of a ShaderVariantAsset it will try to
|
||||
//! find its corresponding ShaderVariantAsset from the asset cache. If found, the asset will be loaded
|
||||
//! asynchronously and the caller will be notified via ShaderVariantFinderNotificationBus on main thread when the
|
||||
//! ShaderVariantAsset is fully loaded.
|
||||
//! Returns true if the request was queued successfully.
|
||||
virtual bool QueueLoadShaderVariantAsset(
|
||||
const Data::AssetId& shaderVariantTreeAssetId, ShaderVariantStableId variantStableId,
|
||||
SupervariantIndex supervariantIndex) = 0;
|
||||
|
||||
//! This is a quick blocking call that will return a valid asset only if it's been fully loaded already,
|
||||
//! Otherwise it returns an invalid asset and the caller is supposed to call QueueLoadShaderVariantAssetByVariantId().
|
||||
virtual Data::Asset<ShaderVariantAsset2> GetShaderVariantAssetByVariantId(
|
||||
Data::Asset<ShaderAsset2> shaderAsset, const ShaderVariantId& shaderVariantId, SupervariantIndex supervariantIndex) = 0;
|
||||
|
||||
virtual Data::Asset<ShaderVariantAsset2> GetShaderVariantAssetByStableId(
|
||||
Data::Asset<ShaderAsset2> shaderAsset, ShaderVariantStableId shaderVariantStableId, SupervariantIndex supervariantIndex) = 0;
|
||||
|
||||
//! This is a quick blocking call that will return a valid asset only if it's been fully loaded already,
|
||||
//! Otherwise it returns an invalid asset and the caller is supposed to call QueueLoadShaderVariantTreeAsset().
|
||||
virtual Data::Asset<ShaderVariantTreeAsset> GetShaderVariantTreeAsset(const Data::AssetId& shaderAssetId) = 0;
|
||||
|
||||
//! This is a quick blocking call that will return a valid asset only if i's been fully loaded already,
|
||||
//! Otherwise it returns an invalid asset and the caller is supposed to call QueueLoadShaderVariantAsset().
|
||||
virtual Data::Asset<ShaderVariantAsset2> GetShaderVariantAsset(
|
||||
const Data::AssetId& shaderVariantTreeAssetId, ShaderVariantStableId variantStableId,
|
||||
SupervariantIndex supervariantIndex) = 0;
|
||||
|
||||
//! Clears the cache of loaded ShaderVariantTreeAsset and ShaderVariantAsset objects.
|
||||
//! This is intended for testing.
|
||||
virtual void Reset() = 0;
|
||||
};
|
||||
|
||||
//! IShaderVariantFinder2 will call on this notification bus on the main thread.
|
||||
//! Only the following classes are supposed to register to this notification bus:
|
||||
//! AZ::RPI::ShaderAsset & AZ::RPI::Shader
|
||||
class ShaderVariantFinderNotification2
|
||||
: public EBusTraits
|
||||
{
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
using MutexType = AZStd::recursive_mutex;
|
||||
typedef Data::AssetId BusIdType; // The AssetId of the shader asset.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual void OnShaderVariantTreeAssetReady(Data::Asset<ShaderVariantTreeAsset> shaderVariantTreeAsset, bool isError) = 0;
|
||||
virtual void OnShaderVariantAssetReady(Data::Asset<ShaderVariantAsset2> shaderVariantAsset, bool isError) = 0;
|
||||
};
|
||||
using ShaderVariantFinderNotificationBus2 = AZ::EBus<ShaderVariantFinderNotification2>;
|
||||
|
||||
} // namespace RPI
|
||||
}// namespace AZ
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantTreeAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderResourceGroupAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderInputContract.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderCommonTypes.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantKey.h>
|
||||
#include <Atom/RPI.Reflect/Shader/IShaderVariantFinder.h>
|
||||
|
||||
@@ -54,6 +55,8 @@ namespace AZ
|
||||
|
||||
//! The default shader variant (i.e. the one without any options set).
|
||||
static const ShaderVariantStableId RootShaderVariantStableId;
|
||||
// @subProductType is one of ShaderAssetSubId, or (ShaderAssetSubId::GeneratedHlslSource + 1)+
|
||||
static uint32_t MakeAssetProductSubId(uint32_t rhiApiUniqueIndex, uint32_t subProductType);
|
||||
|
||||
ShaderAsset() = default;
|
||||
~ShaderAsset();
|
||||
@@ -218,83 +221,21 @@ namespace AZ
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Deprecated System
|
||||
enum class ShaderStageType : uint32_t
|
||||
{
|
||||
Vertex,
|
||||
Geometry,
|
||||
TessellationControl,
|
||||
TessellationEvaluation,
|
||||
Fragment,
|
||||
Compute,
|
||||
RayTracing
|
||||
};
|
||||
|
||||
const char* ToString(ShaderStageType shaderStageType);
|
||||
|
||||
void ReflectShaderStageType(ReflectContext* context);
|
||||
|
||||
enum class ShaderAssetSubId : uint32_t
|
||||
{
|
||||
ShaderAsset = 0,
|
||||
StreamLayout,
|
||||
GraphicsPipelineState,
|
||||
OutputMergerState,
|
||||
RootShaderVariantAsset,
|
||||
//[GFX TODO][LY-82895] (arsentuf) These shader stages are going to get reworked when virtual stages are implemented
|
||||
AzVertexShader,
|
||||
AzGeometryShader,
|
||||
AzTessellationControlShader,
|
||||
AzTessellationEvaluationShader,
|
||||
AzFragmentShader,
|
||||
AzComputeShader,
|
||||
AzRayTracingShader,
|
||||
DebugByProduct,
|
||||
PostPreprocessingPureAzsl, // .azslin
|
||||
IaJson,
|
||||
OmJson,
|
||||
SrgJson,
|
||||
OptionsJson,
|
||||
BindingdepJson,
|
||||
GeneratedSource // This must be last because we use this as a base for adding the RHI::APIType when generating shadersource for multiple RHI APIs.
|
||||
GeneratedHlslSource // This must be last because we use this as a base for adding the RHI::APIType when generating shadersource for multiple RHI APIs.
|
||||
};
|
||||
|
||||
ShaderAssetSubId ShaderStageToSubId(ShaderStageType stageType);
|
||||
|
||||
class ShaderStageDescriptor final
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(ShaderStageDescriptor, "{3E7822F7-B952-4379-B0A0-48507681845A}");
|
||||
AZ_CLASS_ALLOCATOR(ShaderStageDescriptor, AZ::SystemAllocator, 0);
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
ShaderStageType m_stageType;
|
||||
AZStd::vector<uint8_t> m_byteCode;
|
||||
AZStd::vector<char> m_sourceCode;
|
||||
AZStd::string m_entryFunctionName;
|
||||
};
|
||||
|
||||
//[GFX TODO][LY-82803] (arsentuf) Remove this when we've fleshed out Virtual Shader stages
|
||||
class ShaderStageAsset final
|
||||
: public AZ::Data::AssetData
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ShaderStageAsset, "{975F48B5-1577-41C9-B8F5-A1024E2D01F1}", AZ::Data::AssetData);
|
||||
AZ_CLASS_ALLOCATOR(ShaderStageAsset, AZ::SystemAllocator, 0);
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
ShaderStageAsset() = default;
|
||||
ShaderStageAsset(const ShaderStageAsset&);
|
||||
ShaderStageAsset& operator= (const ShaderStageAsset&);
|
||||
ShaderStageAsset(ShaderStageAsset&& rhs);
|
||||
|
||||
AZStd::shared_ptr<ShaderStageDescriptor> m_descriptor;
|
||||
AZStd::vector<Data::AssetId> m_srgLayouts;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
} // namespace RPI
|
||||
|
||||
AZ_TYPE_INFO_SPECIALIZE(RPI::ShaderStageType, "{A6408508-748B-4963-B618-E1E6ECA3629A}");
|
||||
} // namespace AZ
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzCore/EBus/Event.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Asset/AssetHandler.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroupLayout.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantTreeAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderInputContract.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOutputContract.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderCommonTypes.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantKey.h>
|
||||
#include <Atom/RPI.Reflect/Shader/IShaderVariantFinder2.h>
|
||||
|
||||
#include <Atom/RHI/PipelineStateDescriptor.h>
|
||||
|
||||
#include <Atom/RHI.Reflect/ShaderStages.h>
|
||||
#include <Atom/RHI.Reflect/PipelineLayoutDescriptor.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
using ShaderResourceGroupLayoutList = AZStd::fixed_vector<RHI::Ptr<RHI::ShaderResourceGroupLayout>, RHI::Limits::Pipeline::ShaderResourceGroupCountMax>;
|
||||
|
||||
enum class ShaderAsset2ProductSubId : uint32_t
|
||||
{
|
||||
ShaderAsset2 = 0, //!< for .azshader file, One per .shader.
|
||||
RootShaderVariantAsset, //!< for .azshadervariant, one per supervariant and referenced inside the .azshader.
|
||||
AzslFlat, //!< .azslin, this file contains the result of preprocessing an azsl file with MCPP, along with prepending the per-RHI azsli header.
|
||||
IaJson, //!< .ia.json, Input Assembly reflection data.
|
||||
OmJson, //!< .om.json, Output Merger reflection data.
|
||||
SrgJson, //!< .srg.json, Shader Resource Group reflection data.
|
||||
OptionsJson, //!< .options.json, Shader Options reflection data.
|
||||
BindingdepJson, //!<.bindingdep.json, Binding dependencies.
|
||||
GeneratedHlslSource, //!<.hlsl code generated with AZSLc.
|
||||
FirstByProduct, //!< This must be last because we use this as a base for adding all the debug byProducts generated
|
||||
//!< with dxc, or spirv-cross, etc.
|
||||
};
|
||||
|
||||
class ShaderAsset2 final
|
||||
: public Data::AssetData
|
||||
, public ShaderVariantFinderNotificationBus2::Handler
|
||||
, public Data::AssetBus::Handler
|
||||
{
|
||||
friend class ShaderAssetCreator2;
|
||||
friend class ShaderAssetHandler2;
|
||||
friend class ShaderAssetTester2;
|
||||
public:
|
||||
AZ_RTTI(ShaderAsset2, "{823395A3-D570-49F4-99A9-D820CD1DEF98}", Data::AssetData);
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
static constexpr char DisplayName[] = "Shader";
|
||||
static constexpr char Extension[] = "azshader2";
|
||||
static constexpr char Group[] = "Shader";
|
||||
|
||||
//! The default shader variant (i.e. the one without any options set).
|
||||
static const ShaderVariantStableId RootShaderVariantStableId;
|
||||
|
||||
// @subProductType is one of ShaderAsset2ProductSubId, or ShaderAsset2ProductSubId::FirstByProduct+
|
||||
static uint32_t MakeProductAssetSubId(uint32_t rhiApiUniqueIndex, uint32_t supervariantIndex, uint32_t subProductType);
|
||||
static SupervariantIndex GetSupervariantIndexFromProductAssetSubId(uint32_t assetProducSubId);
|
||||
static SupervariantIndex GetSupervariantIndexFromAssetId(const Data::AssetId& assetId);
|
||||
|
||||
|
||||
ShaderAsset2() = default;
|
||||
~ShaderAsset2();
|
||||
|
||||
AZ_DISABLE_COPY_MOVE(ShaderAsset2);
|
||||
|
||||
|
||||
//! Returns the name of the shader.
|
||||
const Name& GetName() const;
|
||||
|
||||
//! Returns the pipeline state type generated by variants of this shader.
|
||||
RHI::PipelineStateType GetPipelineStateType() const;
|
||||
|
||||
//! Returns the draw list tag name.
|
||||
//! To get the corresponding DrawListTag use DrawListTagRegistry's FindTag() or AcquireTag() (see
|
||||
//! RHISystemInterface::GetDrawListTagRegistry()). The DrawListTag is also available in the Shader that corresponds to this
|
||||
//! ShaderAsset2.
|
||||
const Name& GetDrawListName() const;
|
||||
|
||||
//! Return the timestamp when the shader asset was built.
|
||||
//! This is used to synchronize versions of the ShaderAsset2 and ShaderVariantTreeAsset, especially during hot-reload.
|
||||
AZStd::sys_time_t GetShaderAssetBuildTimestamp() const;
|
||||
|
||||
//! Returns the shader option group layout.
|
||||
const ShaderOptionGroupLayout* GetShaderOptionGroupLayout() const;
|
||||
|
||||
SupervariantIndex GetSupervariantIndex(const AZ::Name& supervariantName) const;
|
||||
|
||||
//! This function should be your one stop shop to get a ShaderVariantAsset.
|
||||
//! Finds and returns the best matching ShaderVariantAsset given a ShaderVariantId.
|
||||
//! If the ShaderVariantAsset is not fully loaded and ready at the moment, this function
|
||||
//! will QueueLoad the ShaderVariantTreeAsset and subsequently will QueueLoad the ShaderVariantAsset.
|
||||
//! The called will be notified via the ShaderVariantFinderNotificationBus when the
|
||||
//! ShaderVariantAsset is loaded and ready.
|
||||
//! In the mean time, if the required variant is not available this function
|
||||
//! returns the Root Variant.
|
||||
Data::Asset<ShaderVariantAsset2> GetVariant(
|
||||
const ShaderVariantId& shaderVariantId, SupervariantIndex supervariantIndex);
|
||||
Data::Asset<ShaderVariantAsset2> GetVariant(const ShaderVariantId& shaderVariantId) { return GetVariant(shaderVariantId, DefaultSupervariantIndex); }
|
||||
|
||||
//! Finds the best matching shader variant and returns its StableId.
|
||||
//! This function first loads and caches the ShaderVariantTreeAsset (if not done before).
|
||||
//! If the ShaderVariantTreeAsset is not found (either the AssetProcessor has not generated it yet, or it simply doesn't exist), then
|
||||
//! it returns a search result that identifies the root variant.
|
||||
//! This function is thread safe.
|
||||
ShaderVariantSearchResult FindVariantStableId(const ShaderVariantId& shaderVariantId);
|
||||
|
||||
//! Returns the variant asset associated with the provided StableId.
|
||||
//! The user should call FindVariantStableId() first to get a ShaderVariantStableId from a ShaderVariantId,
|
||||
//! Or better yet, call GetVariant(ShaderVariantId) for maximum convenience.
|
||||
//! If the requested variant is not found, the root variant will be returned AND the requested variant will be queued for loading.
|
||||
//! Next time around if the variant has been loaded this function will return it. Alternatively
|
||||
//! the caller can register with the ShaderVariantFinderNotificationBus to get the asset as soon as is available.
|
||||
//! This function is thread safe.
|
||||
Data::Asset<ShaderVariantAsset2> GetVariant(
|
||||
ShaderVariantStableId shaderVariantStableId, SupervariantIndex supervariantIndex) const;
|
||||
Data::Asset<ShaderVariantAsset2> GetVariant(ShaderVariantStableId shaderVariantStableId) const { return GetVariant(shaderVariantStableId, DefaultSupervariantIndex); }
|
||||
|
||||
Data::Asset<ShaderVariantAsset2> GetRootVariant(SupervariantIndex supervariantIndex) const;
|
||||
Data::Asset<ShaderVariantAsset2> GetRootVariant() const { return GetRootVariant(DefaultSupervariantIndex); }
|
||||
|
||||
|
||||
//! Finds and returns the shader resource group asset with the requested name. Returns an empty handle if no matching group was
|
||||
//! found.
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> FindShaderResourceGroupLayout(
|
||||
const Name& shaderResourceGroupName, SupervariantIndex supervariantIndex) const;
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> FindShaderResourceGroupLayout(const Name& shaderResourceGroupName) const
|
||||
{
|
||||
return FindShaderResourceGroupLayout(shaderResourceGroupName, DefaultSupervariantIndex);
|
||||
}
|
||||
|
||||
//! Finds and returns the shader resource group layout associated with the requested binding slot. Returns an empty handle if no matching srg was found.
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> FindShaderResourceGroupLayout(
|
||||
uint32_t bindingSlot, SupervariantIndex supervariantIndex) const;
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> FindShaderResourceGroupLayout(uint32_t bindingSlot) const
|
||||
{
|
||||
return FindShaderResourceGroupLayout(bindingSlot, DefaultSupervariantIndex);
|
||||
}
|
||||
|
||||
//! Finds and returns the shader resource group layout designated as a ShaderVariantKey fallback.
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> FindFallbackShaderResourceGroupLayout( SupervariantIndex supervariantIndex) const;
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> FindFallbackShaderResourceGroupLayout() const
|
||||
{
|
||||
return FindFallbackShaderResourceGroupLayout(DefaultSupervariantIndex);
|
||||
}
|
||||
|
||||
|
||||
//! Returns the set of shader resource group layouts owned by a given supervariant.
|
||||
AZStd::array_view<RHI::Ptr<RHI::ShaderResourceGroupLayout>> GetShaderResourceGroupLayouts( SupervariantIndex supervariantIndex) const;
|
||||
AZStd::array_view<RHI::Ptr<RHI::ShaderResourceGroupLayout>> GetShaderResourceGroupLayouts() const
|
||||
{
|
||||
return GetShaderResourceGroupLayouts(DefaultSupervariantIndex);
|
||||
}
|
||||
|
||||
//! Returns the pipeline layout descriptor shared by all variants in the asset.
|
||||
const RHI::PipelineLayoutDescriptor* GetPipelineLayoutDescriptor(SupervariantIndex supervariantIndex) const;
|
||||
const RHI::PipelineLayoutDescriptor* GetPipelineLayoutDescriptor() const
|
||||
{
|
||||
return GetPipelineLayoutDescriptor(DefaultSupervariantIndex);
|
||||
}
|
||||
|
||||
//! Returns the shader resource group asset that has per-draw frequency, which is added to every draw packet.
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> GetDrawSrgLayout(SupervariantIndex supervariantIndex) const;
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> GetDrawSrgLayout() const
|
||||
{
|
||||
return GetDrawSrgLayout(DefaultSupervariantIndex);
|
||||
}
|
||||
|
||||
|
||||
//! Returns the ShaderInputContract which describes which inputs the shader requires
|
||||
const ShaderInputContract& GetInputContract(SupervariantIndex supervariantIndex) const;
|
||||
const ShaderInputContract& GetInputContract() const
|
||||
{
|
||||
return GetInputContract(DefaultSupervariantIndex);
|
||||
}
|
||||
|
||||
|
||||
//! Returns the ShaderOuputContract which describes which outputs the shader requires
|
||||
const ShaderOutputContract& GetOutputContract(SupervariantIndex supervariantIndex) const;
|
||||
const ShaderOutputContract& GetOutputContract() const
|
||||
{
|
||||
return GetOutputContract(DefaultSupervariantIndex);
|
||||
}
|
||||
|
||||
|
||||
//! Returns the render states for the draw pipeline. Only used for draw pipelines.
|
||||
const RHI::RenderStates& GetRenderStates(SupervariantIndex supervariantIndex) const;
|
||||
const RHI::RenderStates& GetRenderStates() const
|
||||
{
|
||||
return GetRenderStates(DefaultSupervariantIndex);
|
||||
}
|
||||
|
||||
|
||||
//! Returns a list of arguments for the specified attribute, or nullopt_t if the attribute is not found. The list can be empty which is still valid.
|
||||
AZStd::optional<RHI::ShaderStageAttributeArguments> GetAttribute(
|
||||
const RHI::ShaderStage& shaderStage, const Name& attributeName, SupervariantIndex supervariantIndex) const;
|
||||
AZStd::optional<RHI::ShaderStageAttributeArguments> GetAttribute(
|
||||
const RHI::ShaderStage& shaderStage, const Name& attributeName) const
|
||||
{
|
||||
return GetAttribute(shaderStage, attributeName, DefaultSupervariantIndex);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
///////////////////////////////////////////////////////////////////
|
||||
/// AssetBus overrides
|
||||
void OnAssetReloaded(Data::Asset<Data::AssetData> asset) override;
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
/// ShaderVariantFinderNotificationBus2 overrides
|
||||
void OnShaderVariantTreeAssetReady(Data::Asset<ShaderVariantTreeAsset> shaderVariantTreeAsset, bool isError) override;
|
||||
void OnShaderVariantAssetReady(Data::Asset<ShaderVariantAsset2> /*shaderVariantAsset*/, bool /*isError*/) override {};
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
//! A Supervariant represents a set of static shader compilation parameters.
|
||||
//! Those parameters can be predefined c-preprocessor macros or specific arguments
|
||||
//! for AZSLc.
|
||||
//! For each Supervariant there's a unique Root ShaderVariantAsset, and possibly an N amount
|
||||
//! of ShaderVariantAssets. The 'N' amount is the same across all Supervariants because all Supervariants
|
||||
//! share the same ShaderVariantTreeAsset.
|
||||
struct Supervariant
|
||||
{
|
||||
AZ_TYPE_INFO(Supervariant, "{850826EF-B267-4752-92F6-A85E4175CAB8}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
AZ::Name m_name;
|
||||
ShaderResourceGroupLayoutList m_srgLayoutList;
|
||||
RHI::Ptr<RHI::PipelineLayoutDescriptor> m_pipelineLayoutDescriptor;
|
||||
ShaderInputContract m_inputContract;
|
||||
ShaderOutputContract m_outputContract;
|
||||
RHI::RenderStates m_renderStates;
|
||||
RHI::ShaderStageAttributeMapList m_attributeMaps;
|
||||
Data::Asset<ShaderVariantAsset2> m_rootShaderVariantAsset;
|
||||
};
|
||||
|
||||
//! Container of shader data that is specific to an RHI API.
|
||||
//! A ShaderAsset2 can contain shader data for multiple RHI APIs if
|
||||
//! the platform support multiple RHIs.
|
||||
struct ShaderApiDataContainer
|
||||
{
|
||||
AZ_TYPE_INFO(ShaderApiDataContainer, "{C636722C-60B9-421C-ACAD-9750BF634A27}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
//! RHI API Type for this shader data.
|
||||
RHI::APIType m_APIType;
|
||||
// Index 0, will always be the default Supervariant. (see DefaultSupervariantIndex)
|
||||
AZStd::vector<Supervariant> m_supervariants;
|
||||
};
|
||||
|
||||
bool FinalizeAfterLoad();
|
||||
void SetReady();
|
||||
ShaderApiDataContainer& GetCurrentShaderApiData();
|
||||
const ShaderApiDataContainer& GetCurrentShaderApiData() const;
|
||||
|
||||
//! Returning pointers instead of references to allow for error checking
|
||||
//! and not having to assert.
|
||||
Supervariant* GetSupervariant(SupervariantIndex supervariantIndex);
|
||||
const Supervariant* GetSupervariant(SupervariantIndex supervariantIndex) const;
|
||||
|
||||
|
||||
//! The name is the stem of the source <name>.shader file.
|
||||
Name m_name;
|
||||
|
||||
//! Dictates the type of pipeline state generated by this asset (Draw / Dispatch / etc.).
|
||||
//! All shader variants, across all supervariants, in the asset adhere to this type.
|
||||
RHI::PipelineStateType m_pipelineStateType = RHI::PipelineStateType::Count;
|
||||
|
||||
//! Defines the layout of the shader options in the asset.
|
||||
Ptr<ShaderOptionGroupLayout> m_shaderOptionGroupLayout;
|
||||
|
||||
//! List with shader data per RHI backend.
|
||||
AZStd::vector<ShaderApiDataContainer> m_perAPIShaderData;
|
||||
|
||||
Name m_drawListName;
|
||||
|
||||
//! Use to synchronize versions of the ShaderAsset2 and ShaderVariantTreeAsset, especially during hot-reload.
|
||||
AZStd::sys_time_t m_shaderAssetBuildTimestamp = 0;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//! Do Not Serialize!
|
||||
|
||||
static constexpr size_t InvalidAPITypeIndex = std::numeric_limits<size_t>::max();
|
||||
|
||||
//! Index that indicates which ShaderDataContainer to use.
|
||||
//! At runtime, the asset checks the current active RHI Backend
|
||||
//! and based on the results this variable gets set on asset load.
|
||||
//! The vector @m_perAPIShaderData will be indexed with this variable.
|
||||
size_t m_currentAPITypeIndex = InvalidAPITypeIndex;
|
||||
|
||||
//! We can not know the ShaderVariantTreeAsset by the time this asset is being created.
|
||||
//! This is a value that is discovered at run time. It becomes valid when FindVariantStableId is called at least once.
|
||||
Data::Asset<ShaderVariantTreeAsset> m_shaderVariantTree;
|
||||
|
||||
//! Used for thread safety for FindVariantStableId().
|
||||
mutable AZStd::shared_mutex m_variantTreeMutex;
|
||||
|
||||
bool m_shaderVariantTreeLoadWasRequested = false;
|
||||
};
|
||||
|
||||
class ShaderAssetHandler2 final
|
||||
: public AssetHandler<ShaderAsset2>
|
||||
{
|
||||
using Base = AssetHandler<ShaderAsset2>;
|
||||
public:
|
||||
ShaderAssetHandler2() = default;
|
||||
|
||||
private:
|
||||
Data::AssetHandler::LoadResult LoadAssetData(
|
||||
const Data::Asset<Data::AssetData>& asset,
|
||||
AZStd::shared_ptr<Data::AssetDataStream> stream,
|
||||
const Data::AssetFilterCB& assetLoadFilterCB) override;
|
||||
Data::AssetHandler::LoadResult PostLoadInit(const Data::Asset<Data::AssetData>& asset);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
} // namespace RPI
|
||||
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <Atom/RPI.Reflect/AssetCreator.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
|
||||
#include <AzCore/std/containers/map.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
class ShaderAssetCreator2
|
||||
: public AssetCreator<ShaderAsset2>
|
||||
{
|
||||
public:
|
||||
//! Begins creation of a shader asset.
|
||||
void Begin(const Data::AssetId& assetId);
|
||||
|
||||
//! [Optional] Set the timestamp for when the ShaderAsset build process began.
|
||||
//! This is needed to synchronize between the ShaderAsset and ShaderVariantTreeAsset when hot-reloading shaders.
|
||||
void SetShaderAssetBuildTimestamp(AZStd::sys_time_t shaderAssetBuildTimestamp);
|
||||
|
||||
//! [Optional] Sets the name of the shader asset from content.
|
||||
void SetName(const Name& name);
|
||||
|
||||
//! [Optional] Sets the DrawListTag name associated with this shader.
|
||||
void SetDrawListName(const Name& name);
|
||||
|
||||
//! [Required] Assigns the layout used to construct and parse shader options packed into shader variant keys.
|
||||
//! Requires that the keys assigned to shader variants were constructed using the same layout.
|
||||
void SetShaderOptionGroupLayout(const Ptr<ShaderOptionGroupLayout>& shaderOptionGroupLayout);
|
||||
|
||||
//! Begins the shader creation for a specific RHI API.
|
||||
//! Begin must be called before the BeginAPI function is called.
|
||||
//! @param type The target RHI API type.
|
||||
void BeginAPI(RHI::APIType type);
|
||||
|
||||
//! Begins the creation of a Supervariant for the current RHI::APIType.
|
||||
//! If this is the first supervariant its name must be empty. The first
|
||||
//! supervariant is always the default, nameless, supervariant.
|
||||
void BeginSupervariant(const Name& name);
|
||||
|
||||
void SetSrgLayoutList(const ShaderResourceGroupLayoutList& srgLayoutList);
|
||||
|
||||
//! [Required] Assigns the pipeline layout descriptor shared by all variants in the shader. Shader variants
|
||||
//! embedded in a single shader asset are required to use the same pipeline layout. It is not necessary to call
|
||||
//! Finalize() on the pipeline layout prior to assignment, but still permitted.
|
||||
void SetPipelineLayout(RHI::Ptr<RHI::PipelineLayoutDescriptor> m_pipelineLayoutDescriptor);
|
||||
|
||||
//! Assigns the contract for inputs required by the shader.
|
||||
void SetInputContract(const ShaderInputContract& contract);
|
||||
|
||||
//! Assigns the contract for outputs required by the shader.
|
||||
void SetOutputContract(const ShaderOutputContract& contract);
|
||||
|
||||
//! Assigns the render states for the draw pipeline. Ignored for non-draw pipelines.
|
||||
void SetRenderStates(const RHI::RenderStates& renderStates);
|
||||
|
||||
//! [Optional] Not all shaders have attributes before functions. Some attributes do not exist for all RHI::APIType either.
|
||||
void SetShaderStageAttributeMapList(const RHI::ShaderStageAttributeMapList& shaderStageAttributeMapList);
|
||||
|
||||
//! [Required] There's always a root variant for each supervariant.
|
||||
void SetRootShaderVariantAsset(Data::Asset<ShaderVariantAsset2> shaderVariantAsset);
|
||||
|
||||
bool EndSupervariant();
|
||||
|
||||
bool EndAPI();
|
||||
|
||||
bool End(Data::Asset<ShaderAsset2>& shaderAsset);
|
||||
|
||||
//! Clones an existing ShaderAsset.
|
||||
void Clone(const Data::AssetId& assetId,
|
||||
const ShaderAsset2& sourceShaderAsset);
|
||||
|
||||
private:
|
||||
|
||||
// Shader variants will use this draw list when they don't specify one.
|
||||
Name m_defaultDrawList;
|
||||
|
||||
// The current supervariant is cached here to facilitate asset
|
||||
// construction. Additionally, prevents BeginSupervariant to be called more than once before calling EndSupervariant.
|
||||
ShaderAsset2::Supervariant* m_currentSupervariant = nullptr;
|
||||
|
||||
};
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/RTTI/TypeInfo.h>
|
||||
#include <Atom/RHI.Reflect/Handle.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
// Common bit positions for ShaderAsset2 and ShaderVariantAsset2 product SubIds.
|
||||
static constexpr uint32_t RhiIndexBitPosition = 30;
|
||||
static constexpr uint32_t RhiIndexNumBits = 32 - RhiIndexBitPosition;
|
||||
static constexpr uint32_t RhiIndexMaxValue = (1 << RhiIndexNumBits) - 1;
|
||||
|
||||
static constexpr uint32_t SupervariantIndexBitPosition = 22;
|
||||
static constexpr uint32_t SupervariantIndexNumBits = RhiIndexBitPosition - SupervariantIndexBitPosition;
|
||||
static constexpr uint32_t SupervariantIndexMaxValue = (1 << SupervariantIndexNumBits) - 1;
|
||||
|
||||
//! A wrapper around a supervariant index for type conformity.
|
||||
//! A supervariant index is required to find shader data from
|
||||
//! Shader2 and ShaderAsset2 related APIs.
|
||||
using SupervariantIndex = RHI::Handle<uint32_t, class ShaderAsset2>;
|
||||
static const SupervariantIndex DefaultSupervariantIndex(0);
|
||||
static const SupervariantIndex InvalidSupervariantIndex;
|
||||
|
||||
enum class ShaderStageType : uint32_t
|
||||
{
|
||||
Vertex,
|
||||
Geometry,
|
||||
TessellationControl,
|
||||
TessellationEvaluation,
|
||||
Fragment,
|
||||
Compute,
|
||||
RayTracing
|
||||
};
|
||||
|
||||
const char* ToString(ShaderStageType shaderStageType);
|
||||
|
||||
void ReflectShaderStageType(ReflectContext* context);
|
||||
|
||||
} // namespace RPI
|
||||
|
||||
AZ_TYPE_INFO_SPECIALIZE(RPI::ShaderStageType, "{A6408508-748B-4963-B618-E1E6ECA3629A}");
|
||||
|
||||
} // namespace AZ
|
||||
@@ -43,8 +43,13 @@ namespace AZ
|
||||
static constexpr const char* DisplayName = "ShaderVariant";
|
||||
static constexpr const char* Group = "Shader";
|
||||
|
||||
static constexpr uint32_t ShaderVariantAssetSubProductType = 0;
|
||||
//! @rhiApiUniqueIndex comes from RHI::Factory::GetAPIUniqueIndex()
|
||||
static uint32_t GetAssetSubId(uint32_t rhiApiUniqueIndex, ShaderVariantStableId variantStableId);
|
||||
//! @subProductType is always 0 for a regular ShaderVariantAsset, for all other debug subProducts created
|
||||
//! by ShaderVariantAssetBuilder this is 1+.
|
||||
static uint32_t MakeAssetProductSubId(
|
||||
uint32_t rhiApiUniqueIndex, ShaderVariantStableId variantStableId,
|
||||
uint32_t subProductType = ShaderVariantAssetSubProductType);
|
||||
|
||||
ShaderVariantAsset() = default;
|
||||
~ShaderVariantAsset() = default;
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/array.h>
|
||||
|
||||
#include <Atom/RHI.Reflect/ShaderStageFunction.h>
|
||||
#include <Atom/RPI.Reflect/Asset/AssetHandler.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantKey.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
//! A ShaderVariantAsset2 contains the shader byte code for each shader stage (Vertex, Fragment, Tessellation, etc) for a given RHI::APIType (dx12, vulkan, metal, etc).
|
||||
//! One independent file per RHI::APIType.
|
||||
class ShaderVariantAsset2 final
|
||||
: public Data::AssetData
|
||||
{
|
||||
friend class ShaderVariantAssetHandler2;
|
||||
friend class ShaderVariantAssetCreator2;
|
||||
|
||||
public:
|
||||
AZ_RTTI(ShaderVariantAsset2, "{51BED815-36D8-410E-90F0-1FA9FF765FBA}", Data::AssetData);
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
static constexpr const char* Extension = "azshadervariant2";
|
||||
static constexpr const char* DisplayName = "ShaderVariant";
|
||||
static constexpr const char* Group = "Shader";
|
||||
|
||||
static constexpr uint32_t ShaderVariantAsset2SubProductType = 1;
|
||||
//! @rhiApiUniqueIndex comes from RHI::Factory::GetAPIUniqueIndex()
|
||||
//! @subProductType is always 0 for a regular ShaderVariantAsset2, for all other debug subProducts created
|
||||
//! by ShaderVariantAssetBuilder2 this is 1+.
|
||||
static uint32_t MakeAssetProductSubId(
|
||||
uint32_t rhiApiUniqueIndex, uint32_t supervariantIndex, ShaderVariantStableId variantStableId,
|
||||
uint32_t subProductType = ShaderVariantAsset2SubProductType);
|
||||
|
||||
ShaderVariantAsset2() = default;
|
||||
~ShaderVariantAsset2() = default;
|
||||
|
||||
AZ_DISABLE_COPY_MOVE(ShaderVariantAsset2);
|
||||
|
||||
RPI::ShaderVariantStableId GetStableId() const { return m_stableId; }
|
||||
|
||||
const ShaderVariantId& GetShaderVariantId() const { return m_shaderVariantId; }
|
||||
|
||||
//! Returns the shader stage function associated with the provided stage enum value.
|
||||
const RHI::ShaderStageFunction* GetShaderStageFunction(RHI::ShaderStage shaderStage) const;
|
||||
|
||||
//! Returns whether the variant is fully baked variant (all options are static branches), or false if the
|
||||
//! variant uses dynamic branches for some shader options.
|
||||
//! If the shader variant is not fully baked, the ShaderVariantKeyFallbackValue must be correctly set when drawing.
|
||||
bool IsFullyBaked() const;
|
||||
|
||||
//! Return the timestamp when this asset was built, and it must be >= than the timestamp of the main ShaderAsset.
|
||||
//! This is used to synchronize versions of the ShaderAsset and ShaderVariantAsset2, especially during hot-reload.
|
||||
AZStd::sys_time_t GetBuildTimestamp() const;
|
||||
|
||||
bool IsRootVariant() const { return m_stableId == RPI::RootShaderVariantStableId; }
|
||||
|
||||
private:
|
||||
//! Called by asset creators to assign the asset to a ready state.
|
||||
void SetReady();
|
||||
bool FinalizeAfterLoad();
|
||||
|
||||
//! See AZ::RPI::ShaderVariantListSourceData::VariantInfo::m_stableId for details.
|
||||
RPI::ShaderVariantStableId m_stableId;
|
||||
|
||||
ShaderVariantId m_shaderVariantId;
|
||||
|
||||
bool m_isFullyBaked = false;
|
||||
|
||||
AZStd::array<RHI::Ptr<RHI::ShaderStageFunction>, RHI::ShaderStageCount> m_functionsByStage;
|
||||
|
||||
//! Used to synchronize versions of the ShaderAsset and ShaderVariantAsset2, especially during hot-reload.
|
||||
AZStd::sys_time_t m_buildTimestamp = 0;
|
||||
};
|
||||
|
||||
class ShaderVariantAssetHandler2 final
|
||||
: public AssetHandler<ShaderVariantAsset2>
|
||||
{
|
||||
using Base = AssetHandler<ShaderVariantAsset2>;
|
||||
public:
|
||||
ShaderVariantAssetHandler2() = default;
|
||||
|
||||
private:
|
||||
LoadResult LoadAssetData(const Data::Asset<Data::AssetData>& asset, AZStd::shared_ptr<Data::AssetDataStream> stream, const AZ::Data::AssetFilterCB& assetLoadFilterCB) override;
|
||||
bool PostLoadInit(const Data::Asset<Data::AssetData>& asset);
|
||||
};
|
||||
|
||||
} // namespace RPI
|
||||
|
||||
} // namespace AZ
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <Atom/RPI.Reflect/Asset/AssetHandler.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderResourceGroupAsset.h>
|
||||
#include <Atom/RPI.Reflect/Image/StreamingImagePoolAsset.h>
|
||||
#include <Atom/RPI.Reflect/Buffer/BufferAsset.h>
|
||||
@@ -35,6 +36,7 @@
|
||||
#include <Atom/RPI.Reflect/Model/SkinMetaAsset.h>
|
||||
#include <Atom/RPI.Reflect/Pass/PassAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantTreeAsset.h>
|
||||
|
||||
#include <BuilderComponent.h>
|
||||
@@ -88,6 +90,7 @@ namespace AZ
|
||||
m_assetWorkers.emplace_back(MakeAssetBuilder<PassBuilder>());
|
||||
|
||||
m_assetHandlers.emplace_back(MakeAssetHandler<ShaderAssetHandler>());
|
||||
m_assetHandlers.emplace_back(MakeAssetHandler<ShaderAssetHandler2>());
|
||||
m_assetHandlers.emplace_back(MakeAssetHandler<ShaderResourceGroupAssetHandler>());
|
||||
m_assetHandlers.emplace_back(MakeAssetHandler<MaterialTypeAssetHandler>());
|
||||
m_assetHandlers.emplace_back(MakeAssetHandler<MaterialAssetHandler>());
|
||||
@@ -98,6 +101,7 @@ namespace AZ
|
||||
m_assetHandlers.emplace_back(MakeAssetHandler<ModelAssetHandler>());
|
||||
m_assetHandlers.emplace_back(MakeAssetHandler<PassAssetHandler>());
|
||||
m_assetHandlers.emplace_back(MakeAssetHandler<ShaderVariantAssetHandler>());
|
||||
m_assetHandlers.emplace_back(MakeAssetHandler<ShaderVariantAssetHandler2>());
|
||||
m_assetHandlers.emplace_back(MakeAssetHandler<ShaderVariantTreeAssetHandler>());
|
||||
m_assetHandlers.emplace_back(MakeAssetHandler<SkinMetaAssetHandler>());
|
||||
m_assetHandlers.emplace_back(MakeAssetHandler<MorphTargetMetaAssetHandler>());
|
||||
|
||||
@@ -11,19 +11,19 @@
|
||||
*/
|
||||
|
||||
#include <Atom/RPI.Edit/Shader/ShaderSourceData.h>
|
||||
#include <AzCore/std/string/regex.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
const char* ShaderSourceData::Extension = "shader";
|
||||
|
||||
void ShaderSourceData::Reflect(ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<ShaderSourceData>()
|
||||
->Version(3)
|
||||
->Version(4)
|
||||
->Field("Source", &ShaderSourceData::m_source)
|
||||
->Field("DrawList", &ShaderSourceData::m_drawListName)
|
||||
->Field("DepthStencilState", &ShaderSourceData::m_depthStencilState)
|
||||
@@ -31,8 +31,8 @@ namespace AZ
|
||||
->Field("BlendState", &ShaderSourceData::m_blendState)
|
||||
->Field("ProgramSettings", &ShaderSourceData::m_programSettings)
|
||||
->Field("CompilerHints", &ShaderSourceData::m_compiler)
|
||||
->Field("ShaderVariantHints", &ShaderSourceData::m_shaderOptionGroupHints)
|
||||
->Field("DisabledRHIBackends", &ShaderSourceData::m_disabledRhiBackends)
|
||||
->Field("Supervariants", &ShaderSourceData::m_supervariants)
|
||||
;
|
||||
|
||||
serializeContext->Class<ShaderSourceData::ProgramSettings>()
|
||||
@@ -45,15 +45,145 @@ namespace AZ
|
||||
->Field("Name", &EntryPoint::m_name)
|
||||
->Field("Type", &EntryPoint::m_type)
|
||||
;
|
||||
|
||||
serializeContext->Class<ShaderSourceData::SupervariantInfo>()
|
||||
->Version(1)
|
||||
->Field("Name", &SupervariantInfo::m_name)
|
||||
->Field("PlusArguments", &SupervariantInfo::m_plusArguments)
|
||||
->Field("MinusArguments", &SupervariantInfo::m_minusArguments);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderSourceData::IsRhiBackendDisabled(const AZ::Name& rhiName)
|
||||
bool ShaderSourceData::IsRhiBackendDisabled(const AZ::Name& rhiName) const
|
||||
{
|
||||
return AZStd::any_of(m_disabledRhiBackends.begin(), m_disabledRhiBackends.end(), [&](const AZStd::string& currentRhiName)
|
||||
{
|
||||
return currentRhiName == rhiName.GetStringView();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//! Helper function.
|
||||
//! Parses a string of command line arguments looking for c-preprocessor macro definitions and appends the name of macro definition arguments.
|
||||
//! Example:
|
||||
//! Input string: "--switch1 -DMACRO1 -v -DMACRO2=23"
|
||||
//! append the following items: ["MACRO1", "MACRO2"]
|
||||
static void GetListOfMacroDefinitionNames(
|
||||
const AZStd::string& stringWithArguments, AZStd::vector<AZStd::string>& macroDefinitionNames)
|
||||
{
|
||||
static const AZStd::regex macroRegex("-D\\s*(\\w+)", AZStd::regex::ECMAScript);
|
||||
|
||||
AZStd::cmatch match;
|
||||
if (AZStd::regex_search(stringWithArguments.c_str(), match, macroRegex))
|
||||
{
|
||||
// First pattern is always the entire string
|
||||
for (unsigned i = 1; i < match.size(); ++i)
|
||||
{
|
||||
if (match[i].matched)
|
||||
{
|
||||
macroDefinitionNames.push_back(match[i].str().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::vector<AZStd::string> ShaderSourceData::SupervariantInfo::GetCombinedListOfMacroDefinitionNamesToRemove() const
|
||||
{
|
||||
AZStd::vector<AZStd::string> macroDefinitionNames;
|
||||
GetListOfMacroDefinitionNames(m_minusArguments, macroDefinitionNames);
|
||||
GetListOfMacroDefinitionNames(m_plusArguments, macroDefinitionNames);
|
||||
return macroDefinitionNames;
|
||||
}
|
||||
|
||||
|
||||
//! Helper function.
|
||||
//! Parses a string of command line arguments looking for c-preprocessor macro definitions and appends macro definition
|
||||
//! arguments. Example: Input string: "--switch1 -DMACRO1 -v -DMACRO2=23" append the following items: ["MACRO1", "MACRO2=23"]
|
||||
static void GetListOfMacroDefinitions(
|
||||
const AZStd::string& stringWithArguments, AZStd::vector<AZStd::string>& macroDefinitions)
|
||||
{
|
||||
static const AZStd::regex macroRegex("-D\\s*(\\w+(=\\w+)?)", AZStd::regex::ECMAScript);
|
||||
|
||||
AZStd::cmatch match;
|
||||
if (AZStd::regex_search(stringWithArguments.c_str(), match, macroRegex))
|
||||
{
|
||||
// First pattern is always the entire string
|
||||
for (unsigned i = 1; i < match.size(); ++i)
|
||||
{
|
||||
if (match[i].matched)
|
||||
{
|
||||
macroDefinitions.push_back(match[i].str().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::vector<AZStd::string> ShaderSourceData::SupervariantInfo::GetMacroDefinitionsToAdd() const
|
||||
{
|
||||
AZStd::vector<AZStd::string> parsedMacroDefinitions;
|
||||
GetListOfMacroDefinitions(m_plusArguments, parsedMacroDefinitions);
|
||||
return parsedMacroDefinitions;
|
||||
}
|
||||
|
||||
|
||||
// Helper.
|
||||
// @arguments: A string with command line arguments for a console application of the form:
|
||||
// "-<arg1> --<arg2> --<arg3>[=<value3>] ..."
|
||||
// Example: "--use-spaces --namespace=vk"
|
||||
// Returns: A list with just the [-|--]<argument name>:
|
||||
// ["-<arg1>", "--<arg2>", "--arg3"]
|
||||
// For the example shown above it will return this vector:
|
||||
// ["--use-spaces", "--namespace"]
|
||||
AZStd::vector<AZStd::string> GetListOfArgumentNames(const AZStd::string& arguments)
|
||||
{
|
||||
AZStd::vector<AZStd::string> listOfTokens;
|
||||
AzFramework::StringFunc::Tokenize(arguments, listOfTokens);
|
||||
AZStd::vector<AZStd::string> listOfArguments;
|
||||
for (const AZStd::string& token : listOfTokens)
|
||||
{
|
||||
AZStd::vector<AZStd::string> splitArguments;
|
||||
AzFramework::StringFunc::Tokenize(token, splitArguments, "=");
|
||||
listOfArguments.push_back(splitArguments[0]);
|
||||
}
|
||||
return listOfArguments;
|
||||
}
|
||||
|
||||
AZStd::string ShaderSourceData::SupervariantInfo::GetCustomizedArgumentsForAzslc(
|
||||
const AZStd::string& initialAzslcCompilerArguments) const
|
||||
{
|
||||
static const AZStd::regex macroRegex("-D\\s*(\\w+(=\\S+)?)", AZStd::regex::ECMAScript);
|
||||
|
||||
// We are only concerned with AZSLc arguments. Let's remove the C-Preprocessor macro definitions
|
||||
// from @minusArguments.
|
||||
const AZStd::string minusArguments = AZStd::regex_replace(m_minusArguments, macroRegex, "");
|
||||
const AZStd::string plusArguments = AZStd::regex_replace(m_plusArguments, macroRegex, "");
|
||||
AZStd::string azslcArgumentsToRemove = minusArguments + " " + plusArguments;
|
||||
AZStd::vector<AZStd::string> azslcArgumentNamesToRemove = GetListOfArgumentNames(azslcArgumentsToRemove);
|
||||
|
||||
// At this moment @azslcArgumentsToRemove contains arguments for AZSLc that can be of the form:
|
||||
// -<arg>
|
||||
// --<arg>[=<value>]
|
||||
// We need to remove those from @initialAzslcCompilerArguments.
|
||||
AZStd::string customizedArguments = initialAzslcCompilerArguments;
|
||||
for (const AZStd::string& azslcArgumentName : azslcArgumentNamesToRemove)
|
||||
{
|
||||
AZStd::string regexStr = AZStd::string::format("%s(=\\S+)?", azslcArgumentName.c_str());
|
||||
AZStd::regex replaceRegex(regexStr, AZStd::regex::ECMAScript);
|
||||
customizedArguments = AZStd::regex_replace(customizedArguments, replaceRegex, "");
|
||||
}
|
||||
|
||||
customizedArguments += " " + plusArguments;
|
||||
|
||||
// Will contain the results that will be joined by a space.
|
||||
// This is used to get a clean string to return without excess spaces.
|
||||
AZStd::vector<AZStd::string> argumentList;
|
||||
AzFramework::StringFunc::Tokenize(customizedArguments, argumentList, " \t\n");
|
||||
customizedArguments.clear(); // Need to clear because Join appends.
|
||||
AzFramework::StringFunc::Join(customizedArguments, argumentList.begin(), argumentList.end(), " ");
|
||||
return customizedArguments;
|
||||
}
|
||||
|
||||
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include <Atom/RPI.Edit/Shader/ShaderVariantAssetCreator2.h>
|
||||
|
||||
#include <AzCore/Utils/TypeHash.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderInputContract.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOutputContract.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
void ShaderVariantAssetCreator2::Begin(const AZ::Data::AssetId& assetId, const ShaderVariantId& shaderVariantId, RPI::ShaderVariantStableId stableId, bool isFullyBaked)
|
||||
{
|
||||
BeginCommon(assetId);
|
||||
|
||||
if (ValidateIsReady())
|
||||
{
|
||||
m_asset->m_stableId = stableId;
|
||||
m_asset->m_shaderVariantId = shaderVariantId;
|
||||
m_asset->m_isFullyBaked = isFullyBaked;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderVariantAssetCreator2::End(Data::Asset<ShaderVariantAsset2>& result)
|
||||
{
|
||||
if (!ValidateIsReady())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_asset->FinalizeAfterLoad())
|
||||
{
|
||||
ReportError("Failed to finalize the ShaderResourceGroupAsset.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool foundDrawFunctions = false;
|
||||
bool foundDispatchFunctions = false;
|
||||
|
||||
if (m_asset->GetShaderStageFunction(RHI::ShaderStage::Vertex) ||
|
||||
m_asset->GetShaderStageFunction(RHI::ShaderStage::Tessellation) ||
|
||||
m_asset->GetShaderStageFunction(RHI::ShaderStage::Fragment))
|
||||
{
|
||||
foundDrawFunctions = true;
|
||||
}
|
||||
|
||||
if (m_asset->GetShaderStageFunction(RHI::ShaderStage::Compute))
|
||||
{
|
||||
foundDispatchFunctions = true;
|
||||
}
|
||||
|
||||
|
||||
if (foundDrawFunctions && foundDispatchFunctions)
|
||||
{
|
||||
ReportError("ShaderVariant contains both Draw functions and Dispatch functions.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_asset->GetShaderStageFunction(RHI::ShaderStage::Fragment) &&
|
||||
!m_asset->GetShaderStageFunction(RHI::ShaderStage::Vertex))
|
||||
{
|
||||
ReportError("Shader Variant with StableId '%u' has a fragment function but no vertex function.", m_asset->m_stableId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_asset->GetShaderStageFunction(RHI::ShaderStage::Tessellation) &&
|
||||
!m_asset->GetShaderStageFunction(RHI::ShaderStage::Vertex))
|
||||
{
|
||||
ReportError("Shader Variant with StableId '%u' has a tessellation function but no vertex function.", m_asset->m_stableId);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
m_asset->SetReady();
|
||||
return EndCommon(result);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Methods for all shader variant types
|
||||
|
||||
void ShaderVariantAssetCreator2::SetBuildTimestamp(AZStd::sys_time_t buildTimestamp)
|
||||
{
|
||||
if (ValidateIsReady())
|
||||
{
|
||||
m_asset->m_buildTimestamp = buildTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderVariantAssetCreator2::SetShaderFunction(RHI::ShaderStage shaderStage, RHI::Ptr<RHI::ShaderStageFunction> shaderStageFunction)
|
||||
{
|
||||
if (ValidateIsReady())
|
||||
{
|
||||
m_asset->m_functionsByStage[static_cast<size_t>(shaderStage)] = shaderStageFunction;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include <Atom/RPI.Public/Shader/Shader2.h>
|
||||
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
|
||||
#include <Atom/RHI/RHISystemInterface.h>
|
||||
|
||||
#include <Atom/RHI/PipelineStateCache.h>
|
||||
#include <Atom/RHI/Factory.h>
|
||||
|
||||
#include <AtomCore/Instance/InstanceDatabase.h>
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderReloadNotificationBus2.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
Data::Instance<Shader2> Shader2::FindOrCreate(const Data::Asset<ShaderAsset2>& shaderAsset, const Name& supervariantName)
|
||||
{
|
||||
Data::Instance<Shader2> shaderInstance = Data::InstanceDatabase<Shader2>::Instance().FindOrCreate(
|
||||
Data::InstanceId::CreateFromAssetId(shaderAsset.GetId()),
|
||||
shaderAsset);
|
||||
if (!shaderInstance)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!shaderInstance->SelectSupervariant(supervariantName))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const RHI::ResultCode resultCode = shaderInstance->Init(*shaderAsset.Get());
|
||||
if (resultCode != RHI::ResultCode::Success)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return shaderInstance;
|
||||
}
|
||||
|
||||
Data::Instance<Shader2> Shader2::CreateInternal([[maybe_unused]] ShaderAsset2& shaderAsset)
|
||||
{
|
||||
Data::Instance<Shader2> shader = aznew Shader2();
|
||||
return shader;
|
||||
}
|
||||
|
||||
Shader2::~Shader2()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
bool Shader2::SelectSupervariant(const Name& supervariantName)
|
||||
{
|
||||
if (supervariantName.IsEmpty())
|
||||
{
|
||||
m_supervariantIndex = DefaultSupervariantIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto supervariantIndex = m_asset->GetSupervariantIndex(supervariantName);
|
||||
if (supervariantIndex == InvalidSupervariantIndex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_supervariantIndex = supervariantIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
RHI::ResultCode Shader2::Init(ShaderAsset2& shaderAsset)
|
||||
{
|
||||
AZ_Assert(m_supervariantIndex != InvalidSupervariantIndex, "Invalid supervariant index");
|
||||
|
||||
ShaderVariantFinderNotificationBus2::Handler::BusDisconnect();
|
||||
ShaderVariantFinderNotificationBus2::Handler::BusConnect(shaderAsset.GetId());
|
||||
|
||||
RHI::RHISystemInterface* rhiSystem = RHI::RHISystemInterface::Get();
|
||||
RHI::DrawListTagRegistry* drawListTagRegistry = rhiSystem->GetDrawListTagRegistry();
|
||||
|
||||
m_asset = { &shaderAsset, AZ::Data::AssetLoadBehavior::PreLoad };
|
||||
m_pipelineStateType = shaderAsset.GetPipelineStateType();
|
||||
|
||||
{
|
||||
AZStd::unique_lock<decltype(m_variantCacheMutex)> lock(m_variantCacheMutex);
|
||||
m_shaderVariants.clear();
|
||||
}
|
||||
m_rootVariant.Init(shaderAsset, shaderAsset.GetRootVariant(m_supervariantIndex), m_supervariantIndex);
|
||||
|
||||
if (m_pipelineLibraryHandle.IsNull())
|
||||
{
|
||||
// We set up a pipeline library only once for the lifetime of the Shader2 instance.
|
||||
// This should allow the Shader2 to be reloaded at runtime many times, and cache and reuse PipelineState objects rather than rebuild them.
|
||||
// It also fixes a particular TDR crash that occurred on some hardware when hot-reloading shaders and building pipeline states
|
||||
// in a new pipeline library every time.
|
||||
|
||||
RHI::PipelineStateCache* pipelineStateCache = rhiSystem->GetPipelineStateCache();
|
||||
ConstPtr<RHI::PipelineLibraryData> serializedData = LoadPipelineLibrary();
|
||||
RHI::PipelineLibraryHandle pipelineLibraryHandle = pipelineStateCache->CreateLibrary(serializedData.get());
|
||||
|
||||
if (pipelineLibraryHandle.IsNull())
|
||||
{
|
||||
AZ_Error("Shader2", false, "Failed to create pipeline library from pipeline state cache.");
|
||||
return RHI::ResultCode::Fail;
|
||||
}
|
||||
|
||||
m_pipelineLibraryHandle = pipelineLibraryHandle;
|
||||
m_pipelineStateCache = pipelineStateCache;
|
||||
}
|
||||
|
||||
const Name& drawListName = shaderAsset.GetDrawListName();
|
||||
if (!drawListName.IsEmpty())
|
||||
{
|
||||
m_drawListTag = drawListTagRegistry->AcquireTag(drawListName);
|
||||
if (!m_drawListTag.IsValid())
|
||||
{
|
||||
AZ_Error("Shader2", false, "Failed to acquire a DrawListTag. Entries are full.");
|
||||
}
|
||||
}
|
||||
|
||||
Data::AssetBus::Handler::BusConnect(m_asset.GetId());
|
||||
|
||||
return RHI::ResultCode::Success;
|
||||
}
|
||||
|
||||
void Shader2::Shutdown()
|
||||
{
|
||||
ShaderVariantFinderNotificationBus2::Handler::BusDisconnect();
|
||||
Data::AssetBus::Handler::BusDisconnect();
|
||||
|
||||
if (m_pipelineLibraryHandle.IsValid())
|
||||
{
|
||||
SavePipelineLibrary();
|
||||
|
||||
m_pipelineStateCache->ReleaseLibrary(m_pipelineLibraryHandle);
|
||||
m_pipelineStateCache = nullptr;
|
||||
m_pipelineLibraryHandle = {};
|
||||
}
|
||||
|
||||
if (m_drawListTag.IsValid())
|
||||
{
|
||||
RHI::DrawListTagRegistry* drawListTagRegistry = RHI::RHISystemInterface::Get()->GetDrawListTagRegistry();
|
||||
drawListTagRegistry->ReleaseTag(m_drawListTag);
|
||||
m_drawListTag.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// AssetBus overrides
|
||||
void Shader2::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
|
||||
{
|
||||
ShaderReloadDebugTracker::ScopedSection reloadSection("Shader2::OnAssetReloaded %s", asset.GetHint().c_str());
|
||||
|
||||
if (asset->GetId() == m_asset->GetId())
|
||||
{
|
||||
Data::Asset<ShaderAsset2> newAsset = { asset.GetAs<ShaderAsset2>(), AZ::Data::AssetLoadBehavior::PreLoad };
|
||||
AZ_Assert(newAsset, "Reloaded ShaderAsset2 is null");
|
||||
|
||||
Data::AssetBus::Handler::BusDisconnect();
|
||||
Init(*newAsset.Get());
|
||||
ShaderReloadNotificationBus2::Event(asset.GetId(), &ShaderReloadNotificationBus2::Events::OnShaderReinitialized, *this);
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
/// ShaderVariantFinderNotificationBus2 overrides
|
||||
void Shader2::OnShaderVariantAssetReady(Data::Asset<ShaderVariantAsset2> shaderVariantAsset, bool isError)
|
||||
{
|
||||
AZ_Assert(shaderVariantAsset, "Reloaded ShaderVariantAsset is null");
|
||||
const ShaderVariantStableId stableId = shaderVariantAsset->GetStableId();
|
||||
const ShaderVariantId& shaderVariantId = shaderVariantAsset->GetShaderVariantId();
|
||||
|
||||
if (isError)
|
||||
{
|
||||
//Remark: We do not assert if the stableId == RootShaderVariantStableId, because we can not trust in the asset data
|
||||
//on error. so it is possible that on error the stbleId == RootShaderVariantStableId;
|
||||
if (stableId == RootShaderVariantStableId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
AZStd::unique_lock<decltype(m_variantCacheMutex)> lock(m_variantCacheMutex);
|
||||
m_shaderVariants.erase(stableId);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(stableId != RootShaderVariantStableId,
|
||||
"The root variant is expected to be updated by the ShaderAsset2.");
|
||||
AZStd::unique_lock<decltype(m_variantCacheMutex)> lock(m_variantCacheMutex);
|
||||
|
||||
auto iter = m_shaderVariants.find(stableId);
|
||||
if (iter != m_shaderVariants.end())
|
||||
{
|
||||
ShaderVariant2& shaderVariant = iter->second;
|
||||
|
||||
if (!shaderVariant.Init(*m_asset.Get(), shaderVariantAsset, m_supervariantIndex))
|
||||
{
|
||||
AZ_Error("Shader2", false, "Failed to init shaderVariant with StableId=%u", shaderVariantAsset->GetStableId());
|
||||
m_shaderVariants.erase(stableId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//This is the first time the shader variant asset comes to life.
|
||||
ShaderVariant2 newVariant;
|
||||
newVariant.Init(*m_asset, shaderVariantAsset, m_supervariantIndex);
|
||||
m_shaderVariants.emplace(stableId, newVariant);
|
||||
}
|
||||
}
|
||||
|
||||
//Even if there was an error, the interested parties should be notified.
|
||||
ShaderReloadNotificationBus2::Event(m_asset.GetId(), &ShaderReloadNotificationBus2::Events::OnShaderVariantReinitialized, *this, shaderVariantId, stableId);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
ConstPtr<RHI::PipelineLibraryData> Shader2::LoadPipelineLibrary() const
|
||||
{
|
||||
if (IO::FileIOBase::GetInstance())
|
||||
{
|
||||
return Utils::LoadObjectFromFile<RHI::PipelineLibraryData>(GetPipelineLibraryPath());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Shader2::SavePipelineLibrary() const
|
||||
{
|
||||
if (auto* fileIOBase = IO::FileIOBase::GetInstance())
|
||||
{
|
||||
RHI::ConstPtr<RHI::PipelineLibraryData> serializedData = m_pipelineStateCache->GetLibrarySerializedData(m_pipelineLibraryHandle);
|
||||
if (serializedData)
|
||||
{
|
||||
const AZStd::string pipelineLibraryPath = GetPipelineLibraryPath();
|
||||
|
||||
char pipelineLibraryPathResolved[AZ_MAX_PATH_LEN] = { 0 };
|
||||
fileIOBase->ResolvePath(pipelineLibraryPath.c_str(), pipelineLibraryPathResolved, AZ_MAX_PATH_LEN);
|
||||
Utils::SaveObjectToFile(pipelineLibraryPathResolved, DataStream::ST_BINARY, serializedData.get());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Shader2", false, "FileIOBase is not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string Shader2::GetPipelineLibraryPath() const
|
||||
{
|
||||
const Data::InstanceId& instanceId = GetId();
|
||||
Name platformName = RHI::Factory::Get().GetName();
|
||||
Name shaderName = m_asset->GetName();
|
||||
|
||||
AZStd::string uuidString;
|
||||
instanceId.m_guid.ToString<AZStd::string>(uuidString, false, false);
|
||||
|
||||
return AZStd::string::format("@user@/Atom/PipelineStateCache/%s/%s_%s_%d.bin", platformName.GetCStr(), shaderName.GetCStr(), uuidString.data(), instanceId.m_subId);
|
||||
}
|
||||
|
||||
ShaderOptionGroup Shader2::CreateShaderOptionGroup() const
|
||||
{
|
||||
return ShaderOptionGroup(m_asset->GetShaderOptionGroupLayout());
|
||||
}
|
||||
|
||||
const ShaderVariant2& Shader2::GetVariant(const ShaderVariantId& shaderVariantId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender);
|
||||
Data::Asset<ShaderVariantAsset2> shaderVariantAsset = m_asset->GetVariant(shaderVariantId, m_supervariantIndex);
|
||||
if (!shaderVariantAsset || shaderVariantAsset->IsRootVariant())
|
||||
{
|
||||
return m_rootVariant;
|
||||
}
|
||||
|
||||
return GetVariant(shaderVariantAsset->GetStableId());
|
||||
}
|
||||
|
||||
const ShaderVariant2& Shader2::GetRootVariant()
|
||||
{
|
||||
return m_rootVariant;
|
||||
}
|
||||
|
||||
ShaderVariantSearchResult Shader2::FindVariantStableId(const ShaderVariantId& shaderVariantId) const
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender);
|
||||
ShaderVariantSearchResult variantSearchResult = m_asset->FindVariantStableId(shaderVariantId);
|
||||
return variantSearchResult;
|
||||
}
|
||||
|
||||
const ShaderVariant2& Shader2::GetVariant(ShaderVariantStableId shaderVariantStableId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender);
|
||||
|
||||
if (!shaderVariantStableId.IsValid() || shaderVariantStableId == ShaderAsset2::RootShaderVariantStableId)
|
||||
{
|
||||
return m_rootVariant;
|
||||
}
|
||||
|
||||
{
|
||||
AZStd::shared_lock<decltype(m_variantCacheMutex)> lock(m_variantCacheMutex);
|
||||
|
||||
auto findIt = m_shaderVariants.find(shaderVariantStableId);
|
||||
if (findIt != m_shaderVariants.end())
|
||||
{
|
||||
// When rebuilding shaders we may be in a state where the ShaderAsset2 and root ShaderVariantAsset have been rebuilt and
|
||||
// reloaded, but some (or all) shader variants haven't been built yet. Since we want to use the latest version of the
|
||||
// shader code, ignore the old variants and fall back to the newer root variant instead. There's no need to report a
|
||||
// warning here because m_asset->GetVariant below will report one.
|
||||
if (findIt->second.GetBuildTimestamp() >= m_asset->GetShaderAssetBuildTimestamp())
|
||||
{
|
||||
return findIt->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// By calling GetVariant, an asynchronous asset load request is enqueued if the variant
|
||||
// is not fully ready.
|
||||
Data::Asset<ShaderVariantAsset2> shaderVariantAsset = m_asset->GetVariant(shaderVariantStableId, m_supervariantIndex);
|
||||
if (!shaderVariantAsset || shaderVariantAsset == m_asset->GetRootVariant())
|
||||
{
|
||||
// Return the root variant when the requested variant is not ready.
|
||||
return m_rootVariant;
|
||||
}
|
||||
|
||||
AZStd::unique_lock<decltype(m_variantCacheMutex)> lock(m_variantCacheMutex);
|
||||
|
||||
// For performance reasons We are breaking this function into two locking steps.
|
||||
// which means We must check again if the variant is already in the cache.
|
||||
auto findIt = m_shaderVariants.find(shaderVariantStableId);
|
||||
if (findIt != m_shaderVariants.end())
|
||||
{
|
||||
if (findIt->second.GetBuildTimestamp() >= m_asset->GetShaderAssetBuildTimestamp())
|
||||
{
|
||||
return findIt->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is probably very rare, but if the variant was loaded on another thread and it's out of date
|
||||
// we just return the root variant. Otherwise we could end up replacing the variant in the map below while
|
||||
// it's being used for rendering.
|
||||
AZ_Warning(
|
||||
"Shader2", false,
|
||||
"Detected an uncommon state during shader reload. Returning the root variant instead of replacing the old one.");
|
||||
return m_rootVariant;
|
||||
}
|
||||
}
|
||||
|
||||
ShaderVariant2 newVariant;
|
||||
newVariant.Init(*m_asset, shaderVariantAsset, m_supervariantIndex);
|
||||
m_shaderVariants.emplace(shaderVariantStableId, newVariant);
|
||||
|
||||
return m_shaderVariants.at(shaderVariantStableId);
|
||||
}
|
||||
|
||||
RHI::PipelineStateType Shader2::GetPipelineStateType() const
|
||||
{
|
||||
return m_pipelineStateType;
|
||||
}
|
||||
|
||||
const ShaderInputContract& Shader2::GetInputContract() const
|
||||
{
|
||||
return m_asset->GetInputContract(m_supervariantIndex);
|
||||
}
|
||||
|
||||
const ShaderOutputContract& Shader2::GetOutputContract() const
|
||||
{
|
||||
return m_asset->GetOutputContract(m_supervariantIndex);
|
||||
}
|
||||
|
||||
const RHI::PipelineState* Shader2::AcquirePipelineState(const RHI::PipelineStateDescriptor& descriptor) const
|
||||
{
|
||||
return m_pipelineStateCache->AcquirePipelineState(m_pipelineLibraryHandle, descriptor);
|
||||
}
|
||||
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> Shader2::FindShaderResourceGroupLayout(const Name& shaderResourceGroupName) const
|
||||
{
|
||||
return m_asset->FindShaderResourceGroupLayout(shaderResourceGroupName, m_supervariantIndex);
|
||||
}
|
||||
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> Shader2::FindShaderResourceGroupLayout(uint32_t bindingSlot) const
|
||||
{
|
||||
return m_asset->FindShaderResourceGroupLayout(bindingSlot, m_supervariantIndex);
|
||||
}
|
||||
|
||||
const RHI::Ptr<RHI::ShaderResourceGroupLayout> Shader2::FindFallbackShaderResourceGroupLayout() const
|
||||
{
|
||||
return m_asset->FindFallbackShaderResourceGroupLayout(m_supervariantIndex);
|
||||
}
|
||||
|
||||
AZStd::array_view<RHI::Ptr<RHI::ShaderResourceGroupLayout>> Shader2::GetShaderResourceGroupLayouts() const
|
||||
{
|
||||
return m_asset->GetShaderResourceGroupLayouts(m_supervariantIndex);
|
||||
}
|
||||
|
||||
const Data::Asset<ShaderAsset2>& Shader2::GetAsset() const
|
||||
{
|
||||
return m_asset;
|
||||
}
|
||||
|
||||
RHI::DrawListTag Shader2::GetDrawListTag() const
|
||||
{
|
||||
return m_drawListTag;
|
||||
}
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
@@ -88,6 +88,41 @@ namespace AZ
|
||||
return RHI::ResultCode::Success;
|
||||
}
|
||||
|
||||
bool ShaderResourceGroup::ReplaceSrgLayoutUsingShaderAsset(
|
||||
Data::Asset<ShaderAsset2> shaderAsset, const Name& supervariantName, const Name& srgName)
|
||||
{
|
||||
AZ_TRACE_METHOD();
|
||||
|
||||
SupervariantIndex supervariantIndex = shaderAsset->GetSupervariantIndex(supervariantName);
|
||||
if (supervariantIndex == InvalidSupervariantIndex)
|
||||
{
|
||||
AZ_Assert(
|
||||
false, "Supervariant with name [%s] not found in shader asset [%s]", supervariantName.GetCStr(),
|
||||
shaderAsset->GetName().GetCStr());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_layout = shaderAsset->FindShaderResourceGroupLayout(srgName, supervariantIndex).get();
|
||||
|
||||
if (!m_layout)
|
||||
{
|
||||
AZ_Assert(false, "ShaderResourceGroup cannot be initialized due to invalid ShaderResourceGroupLayout");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_shaderResourceGroup->SetName(m_layout->GetName());
|
||||
m_data = RHI::ShaderResourceGroupData(m_layout);
|
||||
m_shaderAsset = shaderAsset;
|
||||
|
||||
// The RPI groups match the same dimensions as the RHI group.
|
||||
m_imageGroup.clear();
|
||||
m_imageGroup.resize(m_layout->GetGroupSizeForImages());
|
||||
m_bufferGroup.clear();
|
||||
m_bufferGroup.resize(m_layout->GetGroupSizeForBuffers());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderResourceGroup::Compile()
|
||||
{
|
||||
m_shaderResourceGroup->Compile(m_data);
|
||||
|
||||
@@ -12,15 +12,18 @@
|
||||
|
||||
#include <Atom/RPI.Public/Shader/ShaderSystem.h>
|
||||
#include <Atom/RPI.Public/Shader/Shader.h>
|
||||
#include <Atom/RPI.Public/Shader/Shader2.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroupPool.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Asset/AssetHandler.h>
|
||||
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderResourceGroupAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantAsset2.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantTreeAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/PrecompiledShaderAssetSourceData.h>
|
||||
|
||||
@@ -42,9 +45,11 @@ namespace AZ
|
||||
ShaderVariantId::Reflect(context);
|
||||
ShaderVariantStableId::Reflect(context);
|
||||
ShaderAsset::Reflect(context);
|
||||
ShaderAsset2::Reflect(context);
|
||||
ShaderInputContract::Reflect(context);
|
||||
ShaderOutputContract::Reflect(context);
|
||||
ShaderVariantAsset::Reflect(context);
|
||||
ShaderVariantAsset2::Reflect(context);
|
||||
ShaderVariantTreeAsset::Reflect(context);
|
||||
ReflectShaderStageType(context);
|
||||
PrecompiledShaderAssetSourceData::Reflect(context);
|
||||
@@ -58,8 +63,10 @@ namespace AZ
|
||||
void ShaderSystem::GetAssetHandlers(AssetHandlerPtrList& assetHandlers)
|
||||
{
|
||||
assetHandlers.emplace_back(MakeAssetHandler<ShaderAssetHandler>());
|
||||
assetHandlers.emplace_back(MakeAssetHandler<ShaderAssetHandler2>());
|
||||
assetHandlers.emplace_back(MakeAssetHandler<ShaderResourceGroupAssetHandler>());
|
||||
assetHandlers.emplace_back(MakeAssetHandler<ShaderVariantAssetHandler>());
|
||||
assetHandlers.emplace_back(MakeAssetHandler<ShaderVariantAssetHandler2>());
|
||||
assetHandlers.emplace_back(MakeAssetHandler<ShaderVariantTreeAssetHandler>());
|
||||
}
|
||||
|
||||
@@ -78,6 +85,14 @@ namespace AZ
|
||||
Data::InstanceDatabase<Shader>::Create(azrtti_typeid<ShaderAsset>(), handler);
|
||||
}
|
||||
|
||||
{
|
||||
Data::InstanceHandler<Shader2> handler;
|
||||
handler.m_createFunction = [](Data::AssetData* shaderAsset) {
|
||||
return Shader2::CreateInternal(*(azrtti_cast<ShaderAsset2*>(shaderAsset)));
|
||||
};
|
||||
Data::InstanceDatabase<Shader2>::Create(azrtti_typeid<ShaderAsset2>(), handler);
|
||||
}
|
||||
|
||||
{
|
||||
Data::InstanceHandler<ShaderResourceGroup> handler;
|
||||
handler.m_createFunction = [](Data::AssetData* srgAsset)
|
||||
@@ -100,6 +115,7 @@ namespace AZ
|
||||
void ShaderSystem::Shutdown()
|
||||
{
|
||||
Data::InstanceDatabase<Shader>::Destroy();
|
||||
Data::InstanceDatabase<Shader2>::Destroy();
|
||||
Data::InstanceDatabase<ShaderResourceGroup>::Destroy();
|
||||
Data::InstanceDatabase<ShaderResourceGroupPool>::Destroy();
|
||||
Interface<ShaderSystemInterface>::Unregister(this);
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include <Atom/RPI.Public/Shader/ShaderVariant2.h>
|
||||
|
||||
#include <Atom/RHI/DrawListTagRegistry.h>
|
||||
#include <Atom/RHI/RHISystemInterface.h>
|
||||
|
||||
#include <Atom/RHI.Reflect/ShaderStageFunction.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
bool ShaderVariant2::Init(
|
||||
const ShaderAsset2& shaderAsset,
|
||||
Data::Asset<ShaderVariantAsset2> shaderVariantAsset,
|
||||
SupervariantIndex supervariantIndex)
|
||||
{
|
||||
m_pipelineStateType = shaderAsset.GetPipelineStateType();
|
||||
m_pipelineLayoutDescriptor = shaderAsset.GetPipelineLayoutDescriptor(supervariantIndex);
|
||||
m_shaderVariantAsset = shaderVariantAsset;
|
||||
m_renderStates = &shaderAsset.GetRenderStates(supervariantIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderVariant2::ConfigurePipelineState(RHI::PipelineStateDescriptor& descriptor) const
|
||||
{
|
||||
descriptor.m_pipelineLayoutDescriptor = m_pipelineLayoutDescriptor;
|
||||
|
||||
switch (descriptor.GetType())
|
||||
{
|
||||
case RHI::PipelineStateType::Draw:
|
||||
{
|
||||
AZ_Assert(m_pipelineStateType == RHI::PipelineStateType::Draw, "ShaderVariant2 is not intended for the raster pipeline.");
|
||||
AZ_Assert(m_renderStates, "Invalid RenderStates");
|
||||
RHI::PipelineStateDescriptorForDraw& descriptorForDraw = static_cast<RHI::PipelineStateDescriptorForDraw&>(descriptor);
|
||||
descriptorForDraw.m_vertexFunction = m_shaderVariantAsset->GetShaderStageFunction(RHI::ShaderStage::Vertex);
|
||||
descriptorForDraw.m_tessellationFunction = m_shaderVariantAsset->GetShaderStageFunction(RHI::ShaderStage::Tessellation);
|
||||
descriptorForDraw.m_fragmentFunction = m_shaderVariantAsset->GetShaderStageFunction(RHI::ShaderStage::Fragment);
|
||||
descriptorForDraw.m_renderStates = *m_renderStates;
|
||||
break;
|
||||
}
|
||||
|
||||
case RHI::PipelineStateType::Dispatch:
|
||||
{
|
||||
AZ_Assert(m_pipelineStateType == RHI::PipelineStateType::Dispatch, "ShaderVariant2 is not intended for the compute pipeline.");
|
||||
RHI::PipelineStateDescriptorForDispatch& descriptorForDispatch = static_cast<RHI::PipelineStateDescriptorForDispatch&>(descriptor);
|
||||
descriptorForDispatch.m_computeFunction = m_shaderVariantAsset->GetShaderStageFunction(RHI::ShaderStage::Compute);
|
||||
break;
|
||||
}
|
||||
|
||||
case RHI::PipelineStateType::RayTracing:
|
||||
{
|
||||
AZ_Assert(m_pipelineStateType == RHI::PipelineStateType::RayTracing, "ShaderVariant2 is not intended for the ray tracing pipeline.");
|
||||
RHI::PipelineStateDescriptorForRayTracing& descriptorForRayTracing = static_cast<RHI::PipelineStateDescriptorForRayTracing&>(descriptor);
|
||||
descriptorForRayTracing.m_rayTracingFunction = m_shaderVariantAsset->GetShaderStageFunction(RHI::ShaderStage::RayTracing);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
AZ_Assert(false, "Unexpected PipelineStateType");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user