[ATOM-15618] Shader Build Pipeline: Add UnitTest To Validate Shader C… (#918)

* [ATOM-15618] Shader Build Pipeline: Add UnitTest To Validate Shader Compiler
Argument Processing Introduced With The New Supervariant System

- Added new test suite in
  Gems/Atom/Asset/Shader/Code/Tests/SupervariantCmdArgumentTests.cpp

- Refactored and improved the previously existing classes:
  GlobalBuildOptions, PreprocessorOptions and ShaderCompilerArguments
  to work well with the new ShaderSourceData::SupervariantInfo.

- Moved command line argument processing function out of
ShaderCompilerArguments and into its own utility namespace in
Atom/RHI.Edit/Utils.h

Signed-off-by: garrieta <garrieta@amazon.com>
This commit is contained in:
galibzon
2021-05-26 15:23:11 -05:00
committed by GitHub
parent da24f4ccde
commit 0678dec64e
13 changed files with 820 additions and 74 deletions
@@ -101,3 +101,36 @@ ly_add_target(
3rdParty::SPIRVCross
3rdParty::azslc
)
################################################################################
# Tests
################################################################################
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_target(
NAME Atom_Asset_Shader.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Gem
FILES_CMAKE
atom_asset_shader_builders_tests_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
Source/Editor
Tests
BUILD_DEPENDENCIES
PRIVATE
AZ::AtomCore
AZ::AzTest
AZ::AzFramework
AZ::AzToolsFramework
Legacy::CryCommon
Gem::Atom_RPI.Public
Gem::Atom_RHI.Public
Gem::Atom_RPI.Edit
Gem::Atom_Asset_Shader.Static
)
ly_add_googletest(
NAME Gem::Atom_Asset_Shader.Tests
)
endif()
@@ -60,33 +60,31 @@ namespace AZ
void PreprocessorOptions::RemovePredefinedMacros(const AZStd::vector<AZStd::string>& macroNames)
{
for (const auto& macroName : macroNames)
{
m_predefinedMacros.erase(
AZStd::remove_if(
m_predefinedMacros.begin(), m_predefinedMacros.end(),
[&](const AZStd::string& predefinedMacro)
{
for (const auto& macroName : macroNames)
[&](const AZStd::string& predefinedMacro) {
// Haystack, needle, bCaseSensitive
if (!AzFramework::StringFunc::StartsWith(predefinedMacro, macroName, true))
{
// 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;
}
// 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;
}),
m_predefinedMacros.end());
}
}
//! Binder helper to Matsui C-Pre-Processor library
@@ -344,8 +344,7 @@ namespace AZ
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.
// The specific error is already reported by RHI::PrependFile().
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return;
}
@@ -0,0 +1,41 @@
/*
* 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 "ShaderBuilderTestFixture.h"
#include <AzCore/Memory/PoolAllocator.h>
#include <AzCore/Name/NameDictionary.h>
namespace UnitTest
{
void ShaderBuilderTestFixture::SetUp()
{
AllocatorsTestFixture::SetUp();
AZ::AllocatorInstance<AZ::PoolAllocator>::Create();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
AZ::NameDictionary::Create();
}
void ShaderBuilderTestFixture::TearDown()
{
AZ::NameDictionary::Destroy();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy();
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
AllocatorsTestFixture::TearDown();
}
}
@@ -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/UnitTest/TestTypes.h>
namespace UnitTest
{
/**
* Unit test fixture for setting up memory allocation pools and the AZ::Name dictionary.
* In the future will be extended as needed.
*/
class ShaderBuilderTestFixture
: public AllocatorsTestFixture
{
protected:
///////////////////////////////////////////////////////////////////////
// AllocatorsTestFixture overrides
void SetUp() override;
void TearDown() override;
///////////////////////////////////////////////////////////////////////
};
} // namespace UnitTest
@@ -0,0 +1,523 @@
/*
* 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 <AzTest/AzTest.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/algorithm.h>
#include <Atom/RPI.Edit/Common/JsonUtils.h>
#include <Atom/RPI.Edit/Shader/ShaderSourceData.h>
#include <Atom/RHI.Edit/Utils.h>
#include <CommonFiles/GlobalBuildOptions.h>
#include "Common/ShaderBuilderTestFixture.h"
namespace UnitTest
{
using namespace AZ;
struct KeyValueView
{
AZStd::string_view m_key;
AZStd::string_view m_value;
};
class SupervariantCmdArgumentTests : public ShaderBuilderTestFixture
{
protected:
static constexpr char MCPP_MACRO1[] = "MACRO1";
static constexpr char MCPP_VALUE1[] = "VALUE1a";
static constexpr char MCPP_NEW_VALUE1[] = "VALUE1b"; // Missing A is not a typo
static constexpr char MCPP_MACRO2[] = "MACRO2";
static constexpr char MCPP_VALUE2[] = "VALUE2";
static constexpr char MCPP_MACRO3[] = "MACRO3";
static constexpr char MCPP_VALUE3[] = "VALUE3a";
static constexpr char MCPP_NEW_VALUE3[] = "VALUE3b";
static constexpr char MCPP_MACRO4[] = "MACRO4";
static constexpr char MCPP_MACRO5[] = "MACRO5";
static constexpr char MCPP_MACRO6[] = "MACRO6";
static constexpr char MCPP_VALUE6[] = "VALUE6";
static constexpr char AZSLC_ARG1[] = "--azsl1";
static constexpr char AZSLC_ARG2[] = "--azsl2";
static constexpr char AZSLC_VAL2[] = "open,source";
static constexpr char AZSLC_NEW_VAL2a[] = "closed,binary";
static constexpr char AZSLC_NEW_VAL2b[] = "closed,source";
static constexpr char AZSLC_ARG3[] = "--azsl3";
static constexpr char AZSLC_VAL3[] = "blue";
static constexpr char AZSLC_ARG4[] = "-azsl4";
static constexpr char AZSLC_ARG5[] = "--azsl5";
static constexpr char AZSLC_VAL5[] = "smith,wick,john,45,-1,-1";
static constexpr char AZSLC_NEW_VAL5[] = "apple,seed,crisp,-1,2,0";
static constexpr char AZSLC_ARG6[] = "--azsl6";
static constexpr char AZSLC_ARG7[] = "--azsl7";
//! Helper function.
//! Given an input list of {Key, Value} pairs returns a list of strings where each string is of the form: "Key=Value".
AZStd::vector<AZStd::string> CreateListOfStringsFromListOfKeyValues(AZStd::array_view<KeyValueView> listOfKeyValues) const
{
AZStd::vector<AZStd::string> listOfStrings;
for (const auto& keyValue : listOfKeyValues)
{
if (keyValue.m_value.empty())
{
listOfStrings.push_back(keyValue.m_key);
}
else
{
listOfStrings.push_back(AZStd::string::format("%s=%s", keyValue.m_key.data(), keyValue.m_value.data()));
}
}
return listOfStrings;
}
//! Helper function.
//! Given an input list of {Key, Value} pairs returns a list of strings where each string is of the form: "Key1", "Value1", "Key2", "Value2".
AZStd::vector<AZStd::string> CreateListOfSingleStringsFromListOfKeyValues(AZStd::array_view<KeyValueView> listOfKeyValues) const
{
AZStd::vector<AZStd::string> listOfStrings;
for (const auto& keyValue : listOfKeyValues)
{
listOfStrings.push_back(keyValue.m_key);
if (!keyValue.m_value.empty())
{
listOfStrings.push_back(keyValue.m_value);
}
}
return listOfStrings;
}
//! Helper function.
//! @param outputString: [out] The string " @argName" gets appended to it (The space is intentional).
//! Alternatively, if @argValue is NOT empty, then the string " @argName=@argValue" is
//! appended to it.
//! @param argName: A typical command line argument. "-p" or "--some".
//! @param argValue: A string representing the value that should be appended to @argName.
void AppendCmdLineArgument(AZStd::string& outputString, AZStd::string_view argName, AZStd::string_view argValue) const
{
if (argValue.empty())
{
outputString += AZStd::string::format(" %s", argName.data());
}
else
{
outputString += AZStd::string::format(" %s=%s", argName.data(), argValue.data());
}
}
//! Helper function.
//! Similar to above, but assumes that @argName refers to just the name of a macro definition so the appended string will always start
//! with "-D".
void AppendMacroDefinitionArgument(AZStd::string& outputString, AZStd::string_view argName, AZStd::string_view argValue) const
{
AppendCmdLineArgument(outputString, AZStd::string::format("-D%s", argName.data()), argValue);
}
//! A helper made of helpers.
//! Returns a command line string that results of concatenating the input list of {Key, Value} pairs (with '=').
//! Example of a returned string:
//! "key1=value1 key2 key3 key4=value"
AZStd::string CreateCmdLineStringFromListOfKeyValues(AZStd::array_view<KeyValueView> listOfKeyValues) const
{
AZStd::string cmdLineString;
for (const auto& keyValueView : listOfKeyValues)
{
AppendCmdLineArgument(cmdLineString, keyValueView.m_key, keyValueView.m_value);
}
return cmdLineString;
}
//! A helper made of helpers.
//! Returns a command line string of macro definitions that results of concatenating the input list of {Key, Value} pairs.
//! Example of a returned string:
//! "-Dkey1=value1 -Dkey2 -Dkey3 -Dkey4=value"
AZStd::string CreateMacroDefinitionCmdLineStringFromListOfKeyValues(AZStd::array_view<KeyValueView> listOfKeyValues) const
{
AZStd::string cmdLineString;
for (const auto& keyValueView : listOfKeyValues)
{
AppendMacroDefinitionArgument(cmdLineString, keyValueView.m_key, keyValueView.m_value);
}
return cmdLineString;
}
//! @param includePaths A List of folder paths
//! @param predefinedMacros A List of strings with format: "name[=value]"
ShaderBuilder::PreprocessorOptions CreatePreprocessorOptions(
AZStd::array_view<AZStd::string> includePaths, AZStd::array_view<AZStd::string> predefinedMacros) const
{
ShaderBuilder::PreprocessorOptions preprocessorOptions;
preprocessorOptions.m_projectIncludePaths.reserve(includePaths.size());
for (const auto& path : includePaths)
{
preprocessorOptions.m_projectIncludePaths.push_back(path);
}
preprocessorOptions.m_predefinedMacros.reserve(predefinedMacros.size());
for (const auto& macro : predefinedMacros)
{
preprocessorOptions.m_predefinedMacros.push_back(macro);
}
return preprocessorOptions;
}
//! @param azslcAdditionalFreeArguments: A string representing series of command line arguments for AZSLc.
//! @param dxcAdditionalFreeArguments: A string representing series of command line arguments for DXC.
RHI::ShaderCompilerArguments CreateShaderCompilerArguments(
AZStd::string_view azslcAdditionalFreeArguments, AZStd::string_view dxcAdditionalFreeArguments) const
{
RHI::ShaderCompilerArguments shaderCompilerArguments;
shaderCompilerArguments.m_azslcWarningLevel = 1;
shaderCompilerArguments.m_azslcAdditionalFreeArguments = azslcAdditionalFreeArguments;
shaderCompilerArguments.m_defaultMatrixOrder = RHI::MatrixOrder::Row;
shaderCompilerArguments.m_dxcAdditionalFreeArguments = dxcAdditionalFreeArguments;
return shaderCompilerArguments;
}
//! @param includePaths A List of folder paths
//! @param predefinedMacros A List of strings with format: "name[=value]"
//! @param azslcAdditionalFreeArguments A string representing series of command line arguments for AZSLc.
//! @param dxcAdditionalFreeArguments: A string representing series of command line arguments for DXC.
ShaderBuilder::GlobalBuildOptions CreateGlobalBuildOptions(
AZStd::array_view<AZStd::string> includePaths,
AZStd::array_view<AZStd::string> predefinedMacros,
AZStd::string_view azslcAdditionalFreeArguments,
AZStd::string_view dxcAdditionalFreeArguments) const
{
ShaderBuilder::GlobalBuildOptions globalBuildOptions;
globalBuildOptions.m_preprocessorSettings = CreatePreprocessorOptions(includePaths, predefinedMacros);
globalBuildOptions.m_compilerArguments =
CreateShaderCompilerArguments(azslcAdditionalFreeArguments, dxcAdditionalFreeArguments);
return globalBuildOptions;
}
//! @param name Name of the supervariant.
//! @param plusArguments A string with command line arguments that contains both C-preprocessor macro definitions
//! and other command line arguments for AZSLc.
//! @param minusArguments A string with command line arguments that should be removed from the finalized command line arguments.
//! it can contain both, C-preprocessor macro definitions and other command line arguments for AZSLc.
RPI::ShaderSourceData::SupervariantInfo CreateSupervariantInfo(
AZStd::string_view name, AZStd::string_view plusArguments, AZStd::string_view minusArguments) const
{
RPI::ShaderSourceData::SupervariantInfo supervariantInfo;
supervariantInfo.m_name = name;
supervariantInfo.m_plusArguments = plusArguments;
supervariantInfo.m_minusArguments = minusArguments;
return supervariantInfo;
}
bool StringContainsAllSubstrings(AZStd::string_view haystack, AZStd::array_view<AZStd::string> substrings)
{
return AZStd::all_of(AZ_BEGIN_END(substrings),
[&](AZStd::string_view needle) -> bool
{
return (haystack.find(needle) != AZStd::string::npos);
}
);
}
bool StringDoesNotContainAnyOneOfTheSubstrings(AZStd::string_view haystack, AZStd::array_view<AZStd::string> substrings)
{
return AZStd::all_of(AZ_BEGIN_END(substrings), [&](AZStd::string_view needle) -> bool {
return (haystack.find(needle) == AZStd::string::npos);
});
}
//! @returns: True if all strings in @substring appear in @vectorOfString.
//! @remark: Keep in mind that this is not the same as saying that all strings in @vectorOfStrings appear in @substrings.
bool VectorContainsAllSubstrings(
AZStd::array_view<AZStd::string> vectorOfStrings, AZStd::array_view<AZStd::string> substrings)
{
return AZStd::all_of(
AZ_BEGIN_END(substrings),
[&](AZStd::string_view needle) -> bool {
bool res = AZStd::any_of(AZ_BEGIN_END(vectorOfStrings),
[&](AZStd::string_view haystack) -> bool
{
return haystack.find(needle) != AZStd::string::npos;
}
);
return res;
}
);
}
//! @returns: True only if None of the strings in @vectorOfStrings contains any of the strings in @substrings.
bool VectorDoesNotContainAnyOneOfTheSubstrings(AZStd::array_view<AZStd::string> vectorOfStrings, AZStd::array_view<AZStd::string> substrings)
{
return AZStd::all_of(AZ_BEGIN_END(vectorOfStrings), [&](AZStd::string_view haystack) -> bool {
return StringDoesNotContainAnyOneOfTheSubstrings(haystack, substrings);
});
}
}; // class SupervariantCmdArgumentTests
TEST_F(SupervariantCmdArgumentTests, CommandLineArgumentUtils_ValidateHelperFunctions)
{
// In this test the idea is to validate the static helper functions in AZ::RHI::ShaderCompilerArguments class
// that are useful for command line argument manipulation, etc.
AZStd::vector<KeyValueView> argumentList = {
{AZSLC_ARG1, ""}, {AZSLC_ARG2, AZSLC_VAL2}, {AZSLC_ARG3, AZSLC_VAL3}, {AZSLC_ARG4, ""}, {AZSLC_ARG5, AZSLC_VAL5},
};
auto argumentsAsString = CreateCmdLineStringFromListOfKeyValues(argumentList);
auto listOfArgumentNames = AZ::RHI::CommandLineArgumentUtils::GetListOfArgumentNames(argumentsAsString);
EXPECT_TRUE(AZStd::all_of(AZ_BEGIN_END(argumentList), [&](const KeyValueView& needle) -> bool {
return (AZStd::find(AZ_BEGIN_END(listOfArgumentNames), needle.m_key) != listOfArgumentNames.end()) &&
// Make sure the values did not make into the expected list of keys.
(AZStd::find(AZ_BEGIN_END(listOfArgumentNames), needle.m_value) == listOfArgumentNames.end());
}));
AZStd::vector<AZStd::string> listOfArgumentsToRemove = { AZSLC_ARG4, AZSLC_ARG2 };
auto stringWithRemovedArguments =
AZ::RHI::CommandLineArgumentUtils::RemoveArgumentsFromCommandLineString(listOfArgumentsToRemove, argumentsAsString);
EXPECT_TRUE(AZStd::all_of(AZ_BEGIN_END(listOfArgumentsToRemove), [&](const AZStd::string& needle) -> bool {
return stringWithRemovedArguments.find(needle) == AZStd::string::npos;
}));
AZStd::vector<AZStd::string> listOfSurvivingArguments = {AZSLC_ARG1, AZSLC_ARG3, AZSLC_ARG5};
EXPECT_TRUE(AZStd::all_of(AZ_BEGIN_END(listOfSurvivingArguments), [&](const AZStd::string& needle) -> bool {
return stringWithRemovedArguments.find(needle) != AZStd::string::npos;
}));
auto stringWithoutExtraSpaces =
AZ::RHI::CommandLineArgumentUtils::RemoveExtraSpaces(" --arg1 -arg2 --arg3=foo --arg4=bar ");
EXPECT_EQ(stringWithoutExtraSpaces, AZStd::string("--arg1 -arg2 --arg3=foo --arg4=bar"));
auto stringAsMergedArguments =
AZ::RHI::CommandLineArgumentUtils::MergeCommandLineArguments("--arg1 -arg2 --arg3=foo", "--arg3=bar --arg4");
EXPECT_EQ(stringAsMergedArguments, AZStd::string("--arg1 -arg2 --arg3=bar --arg4"));
EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("-DMACRO"));
EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("-D MACRO"));
EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -D MACRO"));
EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p -DMACRO --more"));
EXPECT_TRUE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p -D MACRO=VALUE --more"));
EXPECT_FALSE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p --more"));
EXPECT_FALSE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--help -p --more --DFAKE"));
EXPECT_FALSE(AZ::RHI::CommandLineArgumentUtils::HasMacroDefinitions("--DFAKE1 --help -p --more --D FAKE2"));
}
TEST_F(SupervariantCmdArgumentTests, ShaderCompilerArguments_ValidateCommandLineArgumentsMerge)
{
// In this test we validate that AZ::RHI::ShaderCompilerArguments::Merge() works as expected
// by merging AZSLC & DXC arguments giving higher priority to the arguments in the "right".
auto shaderCompilerArgumentsLeft = CreateShaderCompilerArguments(
"--azsl1 --azsl2=avalue2a -azsl3 --azsl4=avalue4a",
"--dxc1=dvalue1a -dxc2 --dxc3=dvalue3a --dxc4");
auto shaderCompilerArgumentsRight = CreateShaderCompilerArguments(
"--azsl1 --azsl2=avalue2b -azsl3 --azsl4=avalue4a --azsl5",
"--dxc1=dvalue1a -dxc2 --dxc3=dvalue3b --dxc4 --dxc5=dvalue5a");
shaderCompilerArgumentsLeft.Merge(shaderCompilerArgumentsRight);
EXPECT_EQ(shaderCompilerArgumentsLeft.m_azslcAdditionalFreeArguments, "--azsl1 --azsl2=avalue2b -azsl3 --azsl4=avalue4a --azsl5");
EXPECT_EQ(shaderCompilerArgumentsLeft.m_dxcAdditionalFreeArguments, "--dxc1=dvalue1a -dxc2 --dxc3=dvalue3b --dxc4 --dxc5=dvalue5a");
}
TEST_F(SupervariantCmdArgumentTests, SupervariantInfo_ValidateMemberFunctions)
{
// In this test all member functions of the ShaderSourceData::SupervariantInfo class
// are validated.
AZStd::vector<KeyValueView> mcppMacrosList = {
{MCPP_MACRO1, MCPP_VALUE1},
{MCPP_MACRO2, MCPP_VALUE2},
{MCPP_MACRO3, MCPP_VALUE3},
{MCPP_MACRO4, ""},
};
AZStd::string argumentsToAddOrReplace;
AppendMacroDefinitionArgument(argumentsToAddOrReplace, MCPP_MACRO3, MCPP_NEW_VALUE3);
AppendCmdLineArgument(argumentsToAddOrReplace, AZSLC_ARG2, AZSLC_NEW_VAL2a);
AppendMacroDefinitionArgument(argumentsToAddOrReplace, MCPP_MACRO1, MCPP_NEW_VALUE1);
AppendCmdLineArgument(argumentsToAddOrReplace, AZSLC_ARG5, AZSLC_NEW_VAL5);
AppendMacroDefinitionArgument(argumentsToAddOrReplace, MCPP_MACRO5, "");
AppendCmdLineArgument(argumentsToAddOrReplace, AZSLC_ARG6, "");
AZStd::string argumentsToRemove;
AppendCmdLineArgument(argumentsToRemove, AZSLC_ARG3, "");
AppendMacroDefinitionArgument(argumentsToRemove, MCPP_MACRO2, "");
AppendCmdLineArgument(argumentsToRemove, AZSLC_ARG4, "");
AppendMacroDefinitionArgument(argumentsToRemove, MCPP_MACRO4, "");
auto supervariantInfo = CreateSupervariantInfo("Dummy", argumentsToAddOrReplace, argumentsToRemove);
auto macroListToRemove = supervariantInfo.GetCombinedListOfMacroDefinitionNamesToRemove();
AZStd::vector<AZStd::string> macroNamesToRemoveThatMustBePresent = { MCPP_MACRO1, MCPP_MACRO2, MCPP_MACRO3, MCPP_MACRO4, MCPP_MACRO5 };
EXPECT_EQ(macroListToRemove.size(), macroNamesToRemoveThatMustBePresent.size());
EXPECT_TRUE(
VectorContainsAllSubstrings(macroListToRemove, macroNamesToRemoveThatMustBePresent)
);
auto macroListToAdd = supervariantInfo.GetMacroDefinitionsToAdd();
AZStd::vector<AZStd::string> macroNamesToAddThatMustBePresent = {MCPP_MACRO1, MCPP_MACRO3, MCPP_MACRO5};
EXPECT_EQ(macroListToAdd.size(), macroNamesToAddThatMustBePresent.size());
EXPECT_TRUE(VectorContainsAllSubstrings(macroListToAdd, macroNamesToAddThatMustBePresent));
// The result of GetCustomizedArgumentsForAzslc() is the most important value to test
AZStd::vector<KeyValueView> freeAzslcArgumentList = {
{AZSLC_ARG1, ""}, {AZSLC_ARG2, AZSLC_VAL2}, {AZSLC_ARG3, AZSLC_VAL3}, {AZSLC_ARG4, ""}, {AZSLC_ARG5, AZSLC_VAL5},
};
AZStd::string azslcArgs = CreateCmdLineStringFromListOfKeyValues(freeAzslcArgumentList);
AZStd::string customizedAzslcArgs = supervariantInfo.GetCustomizedArgumentsForAzslc(azslcArgs);
AZStd::vector<AZStd::string> stringsThatMustBePresent = {
AZSLC_ARG1, AZSLC_ARG2, AZSLC_NEW_VAL2a, AZSLC_ARG5, AZSLC_NEW_VAL5, AZSLC_ARG6};
EXPECT_TRUE(StringContainsAllSubstrings(customizedAzslcArgs, stringsThatMustBePresent));
AZStd::vector<AZStd::string> stringsThatCanNotBePresent = { AZSLC_ARG3, AZSLC_VAL3, AZSLC_ARG4,
// Because GetCustomizedArgumentsForAzslc() only returns arguments for AZSLc, none of the macro related
// arguments can be present
MCPP_MACRO1, MCPP_VALUE1, MCPP_NEW_VALUE1,
MCPP_MACRO2, MCPP_VALUE2,
MCPP_MACRO3, MCPP_VALUE3, MCPP_NEW_VALUE3,
MCPP_MACRO4,
MCPP_MACRO5
};
EXPECT_TRUE(
StringDoesNotContainAnyOneOfTheSubstrings(customizedAzslcArgs, stringsThatCanNotBePresent)
);
}
TEST_F(SupervariantCmdArgumentTests, ShaderAssetBuilder_ValidateInfluenceOfSupervariantInfoOnGlobalBuildOptions)
{
// In this test we validate how the ShaderAssetBuilder configure the commmand line arguments it passes
// to MCPP, AZSLc & DXC. It basically starts with a GlobalBuildOptions, that gets further customized by
// the ShaderCompilerArguments from ShaderSourceData(.shader file) and later further customized
// by each SupervariantInfo in ShaderSourceData.
// The first step is to define the initial values of the GlobalBuildOptions.
AZStd::vector<KeyValueView> globalMcppMacrosList = {
{MCPP_MACRO1, MCPP_VALUE1},
{MCPP_MACRO2, MCPP_VALUE2},
{MCPP_MACRO3, MCPP_VALUE3},
{MCPP_MACRO4, ""},
};
AZStd::vector<KeyValueView> globalAzslArguments = {
{AZSLC_ARG1, ""},
{AZSLC_ARG2, AZSLC_VAL2},
{AZSLC_ARG3, AZSLC_VAL3},
{AZSLC_ARG4, ""},
{AZSLC_ARG5, AZSLC_VAL5},
};
auto globalBuildOptions = CreateGlobalBuildOptions(
AZStd::vector<AZStd::string>(), CreateListOfStringsFromListOfKeyValues(globalMcppMacrosList),
CreateCmdLineStringFromListOfKeyValues(globalAzslArguments),
"" /* Don't care about DXC in this test */);
// The second step is to load the Shader Compiler Arguments from the .shader file.
// These arguments will be merged in @globalBuildOptions, but the .shader arguments have
// higher priority.
AZStd::vector<KeyValueView> shaderAzslArguments = {
{AZSLC_ARG2, AZSLC_NEW_VAL2a},
{AZSLC_ARG6, ""},
};
auto shaderCompilerArguments = CreateShaderCompilerArguments(
CreateCmdLineStringFromListOfKeyValues(shaderAzslArguments), "" /* Don't care about DXC in this test */);
globalBuildOptions.m_compilerArguments.Merge(shaderCompilerArguments);
// Let's create the dummy supervariant. It will have some MCPP & AZSLc arguments to be added/replaced AND other MCPP & AZSLc arguments to be removed.
AZStd::vector<KeyValueView> supervariantAzslArgumentsToAdd = {
{AZSLC_ARG2, AZSLC_NEW_VAL2b},
{AZSLC_ARG7, ""},
};
AZStd::vector<KeyValueView> supervariantMacroDefinitionsToAdd = {
{MCPP_MACRO1, MCPP_NEW_VALUE1},
{MCPP_MACRO3, MCPP_NEW_VALUE3},
{MCPP_MACRO5, ""},
};
auto supervariantArgumentsToAdd = CreateCmdLineStringFromListOfKeyValues(supervariantAzslArgumentsToAdd) +
CreateMacroDefinitionCmdLineStringFromListOfKeyValues(supervariantMacroDefinitionsToAdd);
AZStd::vector<KeyValueView> supervariantAzslArgumentsToRemove = {
{AZSLC_ARG4, ""},
{AZSLC_ARG1, ""},
};
AZStd::vector<KeyValueView> supervariantMacrosToRemove = {
{MCPP_MACRO2, ""},
{MCPP_MACRO4, ""},
};
auto supervariantArgumentsToRemove = CreateCmdLineStringFromListOfKeyValues(supervariantAzslArgumentsToRemove) +
CreateMacroDefinitionCmdLineStringFromListOfKeyValues(supervariantMacrosToRemove);
//CreateMacroDefinitionCmdLineStringFromListOfKeyValues
auto supervariantInfo = CreateSupervariantInfo("Dummy",
supervariantArgumentsToAdd, // These arguments will be added or replace existing ones.
supervariantArgumentsToRemove); // These arguments must be removed.
AZStd::vector<AZStd::string> macroDefinitionNamesToRemove = supervariantInfo.GetCombinedListOfMacroDefinitionNamesToRemove();
globalBuildOptions.m_preprocessorSettings.RemovePredefinedMacros(macroDefinitionNamesToRemove);
AZStd::vector<AZStd::string> macroDefinitionsToAdd = supervariantInfo.GetMacroDefinitionsToAdd();
globalBuildOptions.m_preprocessorSettings.m_predefinedMacros.insert(
globalBuildOptions.m_preprocessorSettings.m_predefinedMacros.end(), macroDefinitionsToAdd.begin(), macroDefinitionsToAdd.end());
// Validate macro definitions that must be present.
EXPECT_TRUE(
VectorContainsAllSubstrings(
globalBuildOptions.m_preprocessorSettings.m_predefinedMacros,
AZStd::vector<AZStd::string>({MCPP_MACRO1, MCPP_NEW_VALUE1, MCPP_MACRO3, MCPP_NEW_VALUE3, MCPP_MACRO5}))
);
// Validate macro definitions that can't be present.
EXPECT_TRUE(
VectorDoesNotContainAnyOneOfTheSubstrings(
globalBuildOptions.m_preprocessorSettings.m_predefinedMacros,
AZStd::vector<AZStd::string>({MCPP_MACRO2, MCPP_VALUE3, MCPP_MACRO4}))
);
AZStd::string azslcArgsFromGlobalBuildOptions = globalBuildOptions.m_compilerArguments.MakeAdditionalAzslcCommandLineString();
// The result of GetCustomizedArgumentsForAzslc() is the most important value to test
AZStd::string customizedAzslcArgs = supervariantInfo.GetCustomizedArgumentsForAzslc(azslcArgsFromGlobalBuildOptions);
EXPECT_TRUE(
StringContainsAllSubstrings(customizedAzslcArgs, CreateListOfSingleStringsFromListOfKeyValues(supervariantAzslArgumentsToAdd))
);
EXPECT_TRUE(
StringDoesNotContainAnyOneOfTheSubstrings(customizedAzslcArgs, CreateListOfSingleStringsFromListOfKeyValues(supervariantAzslArgumentsToRemove))
);
EXPECT_TRUE(
StringContainsAllSubstrings(customizedAzslcArgs, AZStd::vector<AZStd::string>({AZSLC_ARG3, AZSLC_VAL3, AZSLC_ARG5, AZSLC_VAL5}))
);
}
} //namespace UnitTest
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);
@@ -0,0 +1,16 @@
#
# 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.
#
set(FILES
Tests/Common/ShaderBuilderTestFixture.h
Tests/Common/ShaderBuilderTestFixture.cpp
Tests/SupervariantCmdArgumentTests.cpp
)
@@ -13,6 +13,8 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Preprocessor/Enum.h>
#include <AzCore/std/string/string.h>
#include <AtomCore/std/containers/array_view.h>
namespace AZ
{
@@ -30,9 +32,16 @@ namespace AZ
static void Reflect(ReflectContext* context);
//! Returns true if either @m_azslcAdditionalFreeArguments or @m_dxcAdditionalFreeArguments contain
//! macro definitions, e.g. "-D MACRO" or "-D MACRO=VALUE" or "-DMACRO", "-DMACRO=VALUE".
//! It is used for validation to forbid macro definitions, because the idea is that this struct
//! is used inside GlobalBuildOptions which has a dedicated variable for macro definitions.
bool HasMacroDefinitionsInCommandLineArguments();
//! Mix two instances of arguments, by or-ing bools, or by "if different, right hand side wins"
void Merge(const ShaderCompilerArguments& right);
//! [GFX TODO] [ATOM-15472] Remove this function.
//! Determine whether there is a rebuild-worthy difference in arguments for AZSLc
bool HasDifferentAzslcArguments(const ShaderCompilerArguments& right) const;
@@ -110,6 +110,46 @@ namespace AZ
AZStd::string BuildFileNameWithExtension(const AZStd::string& shaderSourceFile,
const AZStd::string& tempFolder,
const char* outputExtension);
namespace CommandLineArgumentUtils
{
//! @param commandLineString: A string with command line arguments of the form:
//! "-<arg1> --<arg2> --<arg3>[=<value3>] ..."
//! Example: "--use-spaces --namespace=vk -W1"
//! Returns: A list with just the [-|--]<argument name>:
//! ["-<arg1>", "--<arg2>", "--arg3"]
//! For the example shown above it will return this vector:
//! ["--use-spaces", "--namespace", "-W1]
AZStd::vector<AZStd::string> GetListOfArgumentNames(AZStd::string_view commandLineString);
//! Takes a list of names of command line arguments and removes those arguments from @commandLineString.
//! The core functionality of this function is that it searches by name in @commandLineString and removes
//! name and value if the name is found.
//! @param listOfArguments: This is a list of strings, usually generated by the helper function
//! ShaderCompilerArguments::GetListOfArgumentNames()
//! @param commandLineString: A single string made of several command line arguments
//! @returns A new string based on @commandLineString but with the matching arguments and their values
//! removed from it.
AZStd::string RemoveArgumentsFromCommandLineString(
AZStd::array_view<AZStd::string> listOfArguments, AZStd::string_view commandLineString);
//! @param commandLineString: " --arg1 -arg2 --arg3=foo --arg4=bar "
//! @returns "--arg1 -arg2 --arg3=foo --arg4=bar"
AZStd::string RemoveExtraSpaces(AZStd::string_view commandLineString);
//! Accepts two arbitrary strings that contain typical command line arguments and returns
//! a new string that combines the arguments were the arguments on the @right have precedence.
//! Example:
//! @param left: "--arg1 -arg2 --arg3=foo"
//! @param right: "--arg3=bar --arg4"
//! @returns: "--arg1 -arg2 --arg3=bar --arg4"
AZStd::string MergeCommandLineArguments(AZStd::string_view left, AZStd::string_view right);
//! @param commandLineString: A string that contains a series of command line arguments.
//! @returns: true if @commandLineString contains macro definitions, e.g:
//! "-D MACRO" or "-D MACRO=VALUE" or "-DMACRO", "-DMACRO=VALUE".
bool HasMacroDefinitions(AZStd::string_view commandLineString);
}
}
}
@@ -12,6 +12,9 @@
#include <Atom/RHI.Edit/ShaderCompilerArguments.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzCore/std/string/regex.h>
#include <Atom/RHI.Edit/Utils.h>
namespace AZ
{
@@ -49,6 +52,12 @@ namespace AZ
}
}
bool ShaderCompilerArguments::HasMacroDefinitionsInCommandLineArguments()
{
return CommandLineArgumentUtils::HasMacroDefinitions(m_azslcAdditionalFreeArguments) ||
CommandLineArgumentUtils::HasMacroDefinitions(m_dxcAdditionalFreeArguments);
}
void ShaderCompilerArguments::Merge(const ShaderCompilerArguments& right)
{
if (right.m_azslcWarningLevel != LevelUnset)
@@ -56,7 +65,7 @@ namespace AZ
m_azslcWarningLevel = right.m_azslcWarningLevel;
}
m_azslcWarningAsError = m_azslcWarningAsError || right.m_azslcWarningAsError;
m_azslcAdditionalFreeArguments += " " + right.m_azslcAdditionalFreeArguments;
m_azslcAdditionalFreeArguments = CommandLineArgumentUtils::MergeCommandLineArguments(m_azslcAdditionalFreeArguments, right.m_azslcAdditionalFreeArguments);
m_dxcDisableWarnings = m_dxcDisableWarnings || right.m_dxcDisableWarnings;
m_dxcWarningAsError = m_dxcWarningAsError || right.m_dxcWarningAsError;
m_dxcDisableOptimizations = m_dxcDisableOptimizations || right.m_dxcDisableOptimizations;
@@ -65,13 +74,14 @@ namespace AZ
{
m_dxcOptimizationLevel = right.m_dxcOptimizationLevel;
}
m_dxcAdditionalFreeArguments += " " + right.m_dxcAdditionalFreeArguments;
m_dxcAdditionalFreeArguments = CommandLineArgumentUtils::MergeCommandLineArguments(m_dxcAdditionalFreeArguments, right.m_dxcAdditionalFreeArguments);
if (right.m_defaultMatrixOrder != MatrixOrder::Default)
{
m_defaultMatrixOrder = right.m_defaultMatrixOrder;
}
}
//! [GFX TODO] [ATOM-15472] Remove this function.
bool ShaderCompilerArguments::HasDifferentAzslcArguments(const ShaderCompilerArguments& right) const
{
auto isSet = +[](uint8_t level) { return level != LevelUnset; };
@@ -494,5 +494,64 @@ namespace AZ
AzFramework::StringFunc::Path::ReplaceExtension(outputFile, outputExtension);
return outputFile;
}
namespace CommandLineArgumentUtils
{
AZStd::vector<AZStd::string> GetListOfArgumentNames(AZStd::string_view commandLineString)
{
AZStd::vector<AZStd::string> listOfTokens;
AzFramework::StringFunc::Tokenize(commandLineString, listOfTokens, " \t\n");
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 RemoveArgumentsFromCommandLineString(
AZStd::array_view<AZStd::string> listOfArgumentsToRemove, AZStd::string_view commandLineString)
{
AZStd::string customizedArguments = commandLineString;
for (const AZStd::string& azslcArgumentName : listOfArgumentsToRemove)
{
AZStd::string regexStr = AZStd::string::format("%s(=\\S+)?", azslcArgumentName.c_str());
AZStd::regex replaceRegex(regexStr, AZStd::regex::ECMAScript);
customizedArguments = AZStd::regex_replace(customizedArguments, replaceRegex, "");
}
return customizedArguments;
}
AZStd::string RemoveExtraSpaces(AZStd::string_view commandLineString)
{
AZStd::vector<AZStd::string> argumentList;
AzFramework::StringFunc::Tokenize(commandLineString, argumentList, " \t\n");
AZStd::string cleanStringWithArguments;
AzFramework::StringFunc::Join(cleanStringWithArguments, argumentList.begin(), argumentList.end(), " ");
return cleanStringWithArguments;
}
AZStd::string MergeCommandLineArguments(AZStd::string_view left, AZStd::string_view right)
{
auto listOfArgumentNamesFromRight = GetListOfArgumentNames(right);
auto leftWithRightArgumentsRemoved = RemoveArgumentsFromCommandLineString(listOfArgumentNamesFromRight, left);
AZStd::string combinedArguments = AZStd::string::format("%s %s", leftWithRightArgumentsRemoved.c_str(), right.data());
return RemoveExtraSpaces(combinedArguments);
}
bool HasMacroDefinitions(AZStd::string_view commandLineString)
{
const AZStd::regex macroRegex(R"((^-D\s*(\w+))|(\s+-D\s*(\w+)))", AZStd::regex::ECMAScript);
AZStd::smatch match;
if (AZStd::regex_search(commandLineString.data(), match, macroRegex))
{
return (match.size() >= 1);
}
return false;
}
} //namespace CommandLineArgumentUtils
} // namespace RHI
} // namespace AZ
+1
View File
@@ -69,6 +69,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
PRIVATE
AZ::AtomCore
AZ::AzToolsFramework
Gem::Atom_RHI.Edit
Gem::Atom_RPI.Public
)
@@ -11,6 +11,8 @@
*/
#include <Atom/RPI.Edit/Shader/ShaderSourceData.h>
#include <Atom/RHI.Edit/Utils.h>
#include <Atom/RHI.Edit/ShaderCompilerArguments.h>
#include <AzCore/std/string/regex.h>
#include <AzFramework/StringFunc/StringFunc.h>
@@ -57,7 +59,7 @@ namespace AZ
bool ShaderSourceData::IsRhiBackendDisabled(const AZ::Name& rhiName) const
{
return AZStd::any_of(m_disabledRhiBackends.begin(), m_disabledRhiBackends.end(), [&](const AZStd::string& currentRhiName)
return AZStd::any_of(AZ_BEGIN_END(m_disabledRhiBackends), [&](const AZStd::string& currentRhiName)
{
return currentRhiName == rhiName.GetStringView();
});
@@ -72,19 +74,32 @@ namespace AZ
static void GetListOfMacroDefinitionNames(
const AZStd::string& stringWithArguments, AZStd::vector<AZStd::string>& macroDefinitionNames)
{
static const AZStd::regex macroRegex("-D\\s*(\\w+)", AZStd::regex::ECMAScript);
const AZStd::regex macroRegex(R"(-D\s*(\w+))", AZStd::regex::ECMAScript);
AZStd::cmatch match;
if (AZStd::regex_search(stringWithArguments.c_str(), match, macroRegex))
AZStd::string hayStack(stringWithArguments);
AZStd::smatch match;
while (AZStd::regex_search(hayStack.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::string macroToAdd(match[i].str().c_str());
const bool isPresent = AZStd::any_of(AZ_BEGIN_END(macroDefinitionNames),
[&](AZStd::string_view macroName) -> bool
{
return macroToAdd == macroName;
}
);
if (isPresent)
{
continue;
}
macroDefinitionNames.push_back(macroToAdd);
}
}
hayStack = match.suffix();
}
}
@@ -103,19 +118,22 @@ namespace AZ
static void GetListOfMacroDefinitions(
const AZStd::string& stringWithArguments, AZStd::vector<AZStd::string>& macroDefinitions)
{
static const AZStd::regex macroRegex("-D\\s*(\\w+(=\\w+)?)", AZStd::regex::ECMAScript);
const AZStd::regex macroRegex(R"(-D\s*(\w+)(=\w+)?)", AZStd::regex::ECMAScript);
AZStd::cmatch match;
if (AZStd::regex_search(stringWithArguments.c_str(), match, macroRegex))
AZStd::string hayStack(stringWithArguments);
AZStd::smatch match;
while (AZStd::regex_search(hayStack.c_str(), match, macroRegex))
{
// First pattern is always the entire string
for (unsigned i = 1; i < match.size(); ++i)
if (match.size() > 1)
{
if (match[i].matched)
AZStd::string macro(match[1].str().c_str());
if (match.size() > 2)
{
macroDefinitions.push_back(match[i].str().c_str());
macro += match[2].str().c_str();
}
macroDefinitions.push_back(macro);
}
hayStack = match.suffix();
}
}
@@ -126,62 +144,27 @@ namespace AZ
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);
const AZStd::regex macroRegex(R"(-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);
AZStd::vector<AZStd::string> azslcArgumentNamesToRemove = RHI::CommandLineArgumentUtils::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, "");
}
AZStd::string customizedArguments = RHI::CommandLineArgumentUtils::RemoveArgumentsFromCommandLineString(
azslcArgumentNamesToRemove, initialAzslcCompilerArguments);
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;
return RHI::CommandLineArgumentUtils::RemoveExtraSpaces(customizedArguments);
}