Merge branch 'development' into cmake/SPEC-2513_w4267
This commit is contained in:
@@ -163,6 +163,8 @@ ly_add_target(
|
||||
editor_files.cmake
|
||||
PLATFORM_INCLUDE_FILES
|
||||
Platform/${PAL_PLATFORM_NAME}/editor_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
|
||||
TARGET_PROPERTIES
|
||||
LY_INSTALL_GENERATE_RUN_TARGET TRUE
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
3rdParty::Qt::Core
|
||||
|
||||
@@ -415,33 +415,37 @@ namespace Editor
|
||||
}
|
||||
|
||||
// Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system.
|
||||
// These events are now consumed both in and out of game mode.
|
||||
if (msg->message == WM_INPUT)
|
||||
// These events are only broadcast in game mode. In Editor mode, RenderViewportWidget creates synthetic
|
||||
// keyboard and mouse events via Qt.
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
{
|
||||
UINT rawInputSize;
|
||||
const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER);
|
||||
GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, NULL, &rawInputSize, rawInputHeaderSize);
|
||||
|
||||
AZStd::array<BYTE, sizeof(RAWINPUT)> rawInputBytesArray;
|
||||
LPBYTE rawInputBytes = rawInputBytesArray.data();
|
||||
|
||||
const UINT bytesCopied = GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize);
|
||||
CRY_ASSERT(bytesCopied == rawInputSize);
|
||||
|
||||
RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes;
|
||||
CRY_ASSERT(rawInput);
|
||||
|
||||
AzFramework::RawInputNotificationBusWindows::Broadcast(&AzFramework::RawInputNotificationsWindows::OnRawInputEvent, *rawInput);
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (msg->message == WM_DEVICECHANGE)
|
||||
{
|
||||
if (msg->wParam == 0x0007) // DBT_DEVNODES_CHANGED
|
||||
if (msg->message == WM_INPUT)
|
||||
{
|
||||
AzFramework::RawInputNotificationBusWindows::Broadcast(&AzFramework::RawInputNotificationsWindows::OnRawInputDeviceChangeEvent);
|
||||
UINT rawInputSize;
|
||||
const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER);
|
||||
GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, NULL, &rawInputSize, rawInputHeaderSize);
|
||||
|
||||
AZStd::array<BYTE, sizeof(RAWINPUT)> rawInputBytesArray;
|
||||
LPBYTE rawInputBytes = rawInputBytesArray.data();
|
||||
|
||||
const UINT bytesCopied = GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize);
|
||||
CRY_ASSERT(bytesCopied == rawInputSize);
|
||||
|
||||
RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes;
|
||||
CRY_ASSERT(rawInput);
|
||||
|
||||
AzFramework::RawInputNotificationBusWindows::Broadcast(&AzFramework::RawInputNotificationsWindows::OnRawInputEvent, *rawInput);
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (msg->message == WM_DEVICECHANGE)
|
||||
{
|
||||
if (msg->wParam == 0x0007) // DBT_DEVNODES_CHANGED
|
||||
{
|
||||
AzFramework::RawInputNotificationBusWindows::Broadcast(&AzFramework::RawInputNotificationsWindows::OnRawInputDeviceChangeEvent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
+1
-30
@@ -321,42 +321,13 @@ bool CToolBoxManager::SetMacroTitle(int index, const QString& title, bool bToolb
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CToolBoxManager::Load(ActionManager* actionManager)
|
||||
void CToolBoxManager::Load([[maybe_unused]] ActionManager* actionManager)
|
||||
{
|
||||
Clear();
|
||||
|
||||
QString path;
|
||||
GetSaveFilePath(path);
|
||||
Load(path, nullptr, true, nullptr);
|
||||
|
||||
if (actionManager)
|
||||
{
|
||||
auto engineSourceAssetPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / "Assets";
|
||||
LoadShelves((engineSourceAssetPath / "Editor" / "Scripts").c_str(),
|
||||
(engineSourceAssetPath / "Editor" / "Scripts" / "Shelves").c_str(), actionManager);
|
||||
}
|
||||
}
|
||||
|
||||
void CToolBoxManager::LoadShelves(QString scriptPath, QString shelvesPath, ActionManager* actionManager)
|
||||
{
|
||||
IFileUtil::FileArray files;
|
||||
CFileUtil::ScanDirectory(shelvesPath, "*.xml", files);
|
||||
|
||||
const int shelfCount = files.size();
|
||||
for (int idx = 0; idx < shelfCount; ++idx)
|
||||
{
|
||||
if (Path::GetExt(files[idx].filename) != "xml")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QString shelfName(PathUtil::GetFileName(files[idx].filename.toUtf8().data()));
|
||||
|
||||
AmazonToolbar toolbar(shelfName, shelfName);
|
||||
Load(shelvesPath + QString("/") + files[idx].filename, &toolbar, false, actionManager);
|
||||
|
||||
m_toolbars.push_back(toolbar);
|
||||
}
|
||||
}
|
||||
|
||||
void CToolBoxManager::Load(QString xmlpath, AmazonToolbar* pToolbar, bool bToolbox, ActionManager* actionManager)
|
||||
|
||||
@@ -129,7 +129,6 @@ public:
|
||||
void Save() const;
|
||||
// Load macros configuration from registry.
|
||||
void Load(ActionManager* actionManager = nullptr);
|
||||
void LoadShelves(QString scriptPath, QString shelvesPath, ActionManager* actionManager);
|
||||
|
||||
//! Get the number of managed macros.
|
||||
int GetMacroCount(bool bToolbox) const;
|
||||
|
||||
@@ -20,11 +20,11 @@ struct AffineParts
|
||||
Vec3 scale; //!< Stretch factors.
|
||||
float fDet; //!< Sign of determinant.
|
||||
|
||||
/** Decompose matrix to its affnie parts.
|
||||
/** Decompose matrix to its affine parts.
|
||||
*/
|
||||
void Decompose(const Matrix34& mat);
|
||||
|
||||
/** Decompose matrix to its affnie parts.
|
||||
/** Decompose matrix to its affine parts.
|
||||
Assume there`s no stretch rotation.
|
||||
*/
|
||||
void SpectralDecompose(const Matrix34& mat);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup>
|
||||
<DisableFastUpToDateCheck>True</DisableFastUpToDateCheck>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,378 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import errno
|
||||
import shutil
|
||||
import fnmatch
|
||||
import filecmp
|
||||
import fileinput
|
||||
import importlib
|
||||
import argparse
|
||||
import hashlib
|
||||
from xml.sax.saxutils import escape, unescape, quoteattr
|
||||
|
||||
# Maximum number of errors before bailing on AutoGen
|
||||
MAX_ERRORS = 100
|
||||
errorCount = 0
|
||||
|
||||
def PrintError(*objs):
|
||||
print(*objs, file=sys.stderr)
|
||||
global errorCount
|
||||
errorCount += 1
|
||||
if errorCount > MAX_ERRORS:
|
||||
print("Maximum errors exceeded (%d) please check the tty for errors" % MAX_ERRORS, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def PrintUnhandledExcptionInfo():
|
||||
print("An unexpected error occurred, please report the error you encountered and include your build output", file=sys.stderr)
|
||||
|
||||
def TransformEscape(string):
|
||||
return escape(quoteattr(unescape(string)))
|
||||
|
||||
def BooleanTrue(string):
|
||||
testString = string.lower().strip()
|
||||
return testString == "true" or testString == "1"
|
||||
|
||||
def CamelToHuman(string):
|
||||
return string[0].upper() + re.sub(r'((?<=[a-z])[A-Z]|(?<!\A)[A-Z](?=[a-z]))', r' \1', string[1:])
|
||||
|
||||
def StripFloat(string):
|
||||
return re.sub(r'(\d+(\.\d*)?|\.\d+)f', r'\g<1>0', string)
|
||||
|
||||
def CreateHashGuid(string):
|
||||
hash = hashlib.new('md5')
|
||||
hash.update(string.encode('utf-8'))
|
||||
hashStr = hash.hexdigest()
|
||||
return ("{" + hashStr[0:8] + "-" + hashStr[8:12] + "-" + hashStr[12:16] + "-" + hashStr[16:20] + "-" + hashStr[20:] + "}").upper()
|
||||
|
||||
def EtreeToString(xmlNode):
|
||||
return etree.tostring(xmlNode)
|
||||
|
||||
def SanitizePath(path):
|
||||
return (path or '').replace('\\', '/').replace('//', '/')
|
||||
|
||||
def SearchPaths(filename, paths=[]):
|
||||
if len(paths) > 0:
|
||||
for path in paths:
|
||||
testFile = os.path.join(path, filename)
|
||||
if os.path.exists(testFile):
|
||||
return os.path.abspath(testFile)
|
||||
if os.path.exists(filename):
|
||||
return os.path.abspath(filename)
|
||||
return None
|
||||
|
||||
def ComputeOutputPath(inputFiles, projectDir, outputDir):
|
||||
commonInputPath = os.path.commonprefix(inputFiles) # If we've globbed many source files, this finds the common prefix
|
||||
if os.path.isfile(commonInputPath): # If the commonInputPath resolves to an actual file, slice off the filename
|
||||
commonInputPath = os.path.dirname(commonInputPath)
|
||||
commonPath = os.path.commonprefix([commonInputPath, projectDir]) # Finds the common path between the data source files and our project directory (//depot/dev/Code/Framework/AzCore/)
|
||||
inputRelativePath = os.path.relpath(commonInputPath, commonPath) # Computes the relative path for the project source directory (Code/Framework/AzCore/AutoGen/)
|
||||
return os.path.join(outputDir, inputRelativePath) # Returns a suitable output directory (//depot/dev/Generated/Code/Framework/AzCore/AutoGen/)
|
||||
|
||||
def ProcessTemplateConversion(dataInputSet, dataInputFiles, templateFile, outputFile, templateCache, dryrun, verbose):
|
||||
if dryrun or not dataInputFiles:
|
||||
return
|
||||
try:
|
||||
outputFile = os.path.abspath(outputFile)
|
||||
outputPath = os.path.dirname(outputFile)
|
||||
treeRoots = []
|
||||
for dataInputFile in sorted(dataInputFiles):
|
||||
try:
|
||||
if dataInputFile in dataInputSet.keys():
|
||||
treeRoots.append(dataInputSet.get(dataInputFile))
|
||||
elif os.path.splitext(dataInputFile)[1] == ".xml":
|
||||
xml = etree.parse(dataInputFile)
|
||||
# xml.xinclude()
|
||||
xmlroot = xml.getroot()
|
||||
# look for an xml schema link for this document
|
||||
# xmlSchema = None
|
||||
# if 'xsi' in xmlroot.nsmap:
|
||||
# XMLSchemaNamespace = xmlroot.nsmap['xsi']
|
||||
# schemaLink = xmlroot.get('{' + XMLSchemaNamespace + '}schemaLocation')
|
||||
# if schemaLink is None:
|
||||
# schemaLink = xmlroot.attrib['{' + XMLSchemaNamespace + '}noNamespaceSchemaLocation']
|
||||
# if schemaLink:
|
||||
# # if we have a schemaLink, then we need to strip off the relative pathing and use our search paths
|
||||
# # relative pathing on the xml file itself is purely a nicety for Visual Studio to find the correct XSD for inline validation
|
||||
# xmlSchema = os.path.basename(schemaLink)
|
||||
# if xmlSchema:
|
||||
# # check the template directory, the template include dir, and the folder that houses the nvdef file, and the xml's location for the xsd
|
||||
# searchPaths = [os.path.dirname(templateFile)]
|
||||
# searchPaths += [os.path.dirname(dataInputFile)]
|
||||
# xmlShemaLoc = SearchPaths(xmlSchema, searchPaths)
|
||||
# try:
|
||||
# xmlSchemaDoc = etree.parse(xmlShemaLoc)
|
||||
# xmlSchemaObj = etree.XMLSchema(xmlSchemaDoc, attribute_defaults=True)
|
||||
# xmlSchemaObj.assertValid(xmlroot)
|
||||
# except etree.DocumentInvalid as e:
|
||||
# for error in e.error_log:
|
||||
# PrintError('%s(%d) : error InvalidXML %s' % (os.path.abspath(dataInputFile), error.line, error.message))
|
||||
# except IOError as e:
|
||||
# PrintError('%s(%s) : %s' % (os.path.abspath(dataInputFile), str(1), e.message))
|
||||
xmlroot = xml.getroot()
|
||||
dataInputSet[dataInputFile] = xml.getroot()
|
||||
treeRoots.append(xml.getroot())
|
||||
else:
|
||||
with open(dataInputFile) as jsonFile:
|
||||
jsonData = json.load(jsonFile)
|
||||
dataInputSet[dataInputFile] = jsonData
|
||||
treeRoots.append(jsonData)
|
||||
except IOError as e:
|
||||
PrintError('%s(%s) : %s' % (fileinput.filename(), str(fileinput.filelineno()), e.message))
|
||||
# except etree.XMLSyntaxError as e:
|
||||
# for error in e.error_log:
|
||||
# PrintError('%s(%s) : error XMLSyntaxError %s' % (os.path.abspath(dataInputFile), error.line, error.message))
|
||||
compareFD = io.StringIO()
|
||||
|
||||
searchPaths = [os.path.dirname(templateFile)]
|
||||
templateLoader = jinja2.FileSystemLoader(searchpath = searchPaths)
|
||||
templateEnv = jinja2.Environment(bytecode_cache = templateCache, loader = templateLoader, trim_blocks = True, extensions = ["jinja2.ext.do",])
|
||||
templateEnv.filters['relpath' ] = lambda x: os.path.relpath(x, outputPath)
|
||||
templateEnv.filters['dirname' ] = os.path.dirname
|
||||
templateEnv.filters['basename' ] = os.path.basename
|
||||
templateEnv.filters['splitext' ] = os.path.splitext
|
||||
templateEnv.filters['split' ] = os.path.split
|
||||
templateEnv.filters['startswith' ] = str.startswith
|
||||
templateEnv.filters['int' ] = int
|
||||
templateEnv.filters['str' ] = str
|
||||
templateEnv.filters['escape' ] = TransformEscape
|
||||
templateEnv.filters['len' ] = len
|
||||
templateEnv.filters['range' ] = range
|
||||
templateEnv.filters['stripFloat' ] = StripFloat
|
||||
templateEnv.filters['camelToHuman' ] = CamelToHuman
|
||||
templateEnv.filters['booleanTrue' ] = BooleanTrue
|
||||
templateEnv.filters['createHashGuid'] = CreateHashGuid
|
||||
templateEnv.filters['etreeToString' ] = EtreeToString
|
||||
templateJinja = templateEnv.get_template(os.path.basename(templateFile))
|
||||
templateVars = \
|
||||
{ \
|
||||
"dataFiles" : treeRoots, \
|
||||
"dataFileNames" : dataInputFiles, \
|
||||
"templateName" : templateFile, \
|
||||
"outputFile" : outputFile, \
|
||||
"filename" : os.path.splitext(os.path.basename(outputFile))[0], \
|
||||
}
|
||||
try:
|
||||
outputExtension = os.path.splitext(outputFile)[1]
|
||||
if outputExtension == ".xml" or outputExtension == ".xhtml" or outputExtension == ".xsd":
|
||||
compareFD.write('<?xml version="1.0"?>\n')
|
||||
compareFD.write('<!-- Copyright (c) Contributors to the Open 3D Engine Project. -->\n')
|
||||
compareFD.write('<!-- For complete copyright and license terms please see the LICENSE at the root of this distribution. -->\n')
|
||||
compareFD.write('\n')
|
||||
compareFD.write('<!-- SPDX-License-Identifier: Apache-2.0 OR MIT -->\n')
|
||||
compareFD.write('\n')
|
||||
compareFD.write('<!-- This file is generated automatically at compile time, DO NOT EDIT BY HAND -->\n')
|
||||
compareFD.write('<!-- Template Source {0}; XML Sources {1}-->\n'.format(templateFile, ', '.join(dataInputFiles)))
|
||||
compareFD.write('\n')
|
||||
elif outputExtension == ".lua":
|
||||
compareFD.write('-- Copyright (c) Contributors to the Open 3D Engine Project.\n')
|
||||
compareFD.write('-- For complete copyright and license terms please see the LICENSE at the root of this distribution.\n')
|
||||
compareFD.write('\n')
|
||||
compareFD.write('-- SPDX-License-Identifier: Apache-2.0 OR MIT\n')
|
||||
compareFD.write('\n')
|
||||
compareFD.write('-- This file is generated automatically at compile time, DO NOT EDIT BY HAND\n')
|
||||
compareFD.write('-- Template Source {0}; XML Sources {1}\n'.format(templateFile, ', '.join(dataInputFiles)))
|
||||
compareFD.write('\n')
|
||||
elif outputExtension == ".h" or outputExtension == ".hpp" or outputExtension == ".inl" or outputExtension == ".c" or outputExtension == ".cpp":
|
||||
compareFD.write('/*\n')
|
||||
compareFD.write(' * Copyright (c) Contributors to the Open 3D Engine Project.\n')
|
||||
compareFD.write(' * For complete copyright and license terms please see the LICENSE at the root of this distribution.\n')
|
||||
compareFD.write(' *\n')
|
||||
compareFD.write(' * SPDX-License-Identifier: Apache-2.0 OR MIT\n')
|
||||
compareFD.write(' *\n')
|
||||
compareFD.write(' * This file is generated automatically at compile time, DO NOT EDIT BY HAND\n')
|
||||
compareFD.write(' * Template Source {0}; Data Sources {1}\n'.format(templateFile, ', '.join(dataInputFiles)))
|
||||
compareFD.write(' */\n')
|
||||
compareFD.write('\n')
|
||||
compareFD.write(templateJinja.render(templateVars))
|
||||
compareFD.write('\n')
|
||||
except jinja2.exceptions.TemplateNotFound as e:
|
||||
PrintError('%s(1) : error TemplateNotFound %s' % (os.path.abspath(templateFile), e.message))
|
||||
except IOError as e:
|
||||
PrintError('%s(%s) : error I/O(%s) accessing %s : %s' % (fileinput.filename(), str(fileinput.filelineno()), e.errno, e.filename, e.strerror))
|
||||
except jinja2.exceptions.TemplateSyntaxError as e:
|
||||
PrintError('%s(%s) : error Template processing error: %s' % (os.path.abspath(e.filename), e.lineno, e.message))
|
||||
except jinja2.exceptions.UndefinedError as e:
|
||||
# Sadly, jinja doesn't provide the exact line of the template that had this error since the template is compiled directly to python code
|
||||
PrintError('%s(1) : error Template processing error: %s with %s' % (os.path.abspath(templateFile), e.message, ', '.join([os.path.basename(dataInputFile) for dataInputFile in dataInputFiles])))
|
||||
try:
|
||||
os.makedirs(os.path.dirname(outputFile))
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST:
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
try:
|
||||
if os.path.isfile(outputFile):
|
||||
with open(outputFile, 'r+') as currentFile:
|
||||
currentFileStringData = currentFile.read()
|
||||
if currentFileStringData == compareFD.getvalue():
|
||||
if verbose == True:
|
||||
print('Generated file %s is unchanged, skipping' % (outputFile))
|
||||
else:
|
||||
currentFile.truncate()
|
||||
with open(outputFile, 'w+') as currentFile:
|
||||
currentFile.write(compareFD.getvalue())
|
||||
print('Generating %s with template %s and inputs %s' % (outputFile, templateFile, ", ".join(dataInputFiles)))
|
||||
else:
|
||||
with open(outputFile, 'w+') as outputFD:
|
||||
outputFD.write(compareFD.getvalue())
|
||||
print('Generating %s using template %s and inputs %s' % (outputFile, templateFile, ", ".join(dataInputFiles)))
|
||||
except IOError as e:
|
||||
PrintError('%s(%s) : error I/O(%s) accessing %s : %s' % (fileinput.filename(), str(fileinput.filelineno()), e.errno, e.filename, e.strerror))
|
||||
except:
|
||||
PrintError('%s(%s) : error Processing: %s' % (fileinput.filename(), str(fileinput.filelineno()), line))
|
||||
PrintUnhandledExcptionInfo()
|
||||
raise
|
||||
compareFD.close()
|
||||
|
||||
def ProcessExpansionRule(sourceFiles, templateFiles, templateCache, outputDir, projectDir, expansionRule, dryrun, verbose, dataInputSet, outputFiles):
|
||||
try:
|
||||
# should be of the format inputFile(s),templateFile,outputFile, where inputFile and outputFile are subject to wildcarding and substitutions
|
||||
expansionRuleSet = expansionRule.split(",")
|
||||
inputFiles = expansionRuleSet[0]
|
||||
templateFile = None
|
||||
outputFile = expansionRuleSet[2]
|
||||
for fullPathTemplate in templateFiles:
|
||||
if expansionRuleSet[1] in fullPathTemplate:
|
||||
templateFile = fullPathTemplate
|
||||
break
|
||||
if templateFile is None:
|
||||
print("No matching template file found for %s, template may be missing from your _files.cmake" % expansionRuleSet[1])
|
||||
return
|
||||
# We have a few potential modes of input to output mapping that we'll have to handle depending on how the user formatted their azdef expansion rule
|
||||
# if the data input file was explicit
|
||||
# then output a single file for that explicit data
|
||||
# else the data is wildcarded
|
||||
# if the output contains $file or $fileprefix
|
||||
# then we can generate a *unique* name for each data input, we're in one-to-one mapping mode, create a unique output for each input
|
||||
# else if the output contains $path
|
||||
# then we can generate a unique name for each *directory* of data inputs, we're in many-to-one mapping mode, create a unique output for each directory
|
||||
# else the output is explicit, not wildcarded
|
||||
# generate a single output file containing all matching data file's
|
||||
# endif
|
||||
# endif
|
||||
testSingle = os.path.join(projectDir, inputFiles)
|
||||
if os.path.isfile(testSingle):
|
||||
# If we specified an *explicit* file to be processed (no wildcards for the data input file foo.json not *.foo.json), this is the branch that handles this case
|
||||
# This is explicitly one-to-one mapping
|
||||
dataInputFiles = [os.path.abspath(testSingle)]
|
||||
outputFileAbsolute = outputFile.replace("$path", ComputeOutputPath(dataInputFiles, projectDir, outputDir))
|
||||
outputFileAbsolute = outputFileAbsolute.replace("$fileprefix", os.path.splitext(os.path.basename(testSingle))[0].split(".")[0])
|
||||
outputFileAbsolute = outputFileAbsolute.replace("$file", os.path.splitext(os.path.basename(testSingle))[0])
|
||||
outputFileAbsolute = SanitizePath(outputFileAbsolute)
|
||||
ProcessTemplateConversion(dataInputSet, dataInputFiles, templateFile, outputFileAbsolute, templateCache, dryrun, verbose)
|
||||
outputFiles.append(outputFileAbsolute)
|
||||
else:
|
||||
# We've wildcarded the data input field, so we may have to handle one-to-one mapping of data files to output, or many-to-one mapping of data files to output
|
||||
if "$fileprefix" in outputFile or "$file" in outputFile:
|
||||
# Due to the wildcards in the output file, we've determined we'll do a one-to-one mapping of data files to output
|
||||
for filename in fnmatch.filter(sourceFiles, inputFiles):
|
||||
dataInputFiles = [os.path.abspath(filename)]
|
||||
outputFileAbsolute = outputFile.replace("$path", ComputeOutputPath(dataInputFiles, projectDir, outputDir))
|
||||
outputFileAbsolute = outputFileAbsolute.replace("$fileprefix", os.path.splitext(os.path.basename(filename))[0].split(".")[0])
|
||||
outputFileAbsolute = outputFileAbsolute.replace("$file", os.path.splitext(os.path.basename(filename))[0])
|
||||
outputFileAbsolute = SanitizePath(outputFileAbsolute)
|
||||
ProcessTemplateConversion(dataInputSet, dataInputFiles, templateFile, outputFileAbsolute, templateCache, dryrun, verbose)
|
||||
outputFiles.append(outputFileAbsolute)
|
||||
else:
|
||||
# Process all matches in one batch
|
||||
# Due to the lack of wildcards in the output file, we've determined we'll glob all matching input files into the template conversion
|
||||
for filename in fnmatch.filter(sourceFiles, inputFiles):
|
||||
dataInputFiles = [os.path.abspath(file) for file in fnmatch.filter(sourceFiles, inputFiles)]
|
||||
outputFileAbsolute = outputFile.replace("$path", ComputeOutputPath(dataInputFiles, projectDir, outputDir))
|
||||
outputFileAbsolute = SanitizePath(outputFileAbsolute)
|
||||
ProcessTemplateConversion(dataInputSet, dataInputFiles, templateFile, outputFileAbsolute, templateCache, dryrun, verbose)
|
||||
outputFiles.append(outputFileAbsolute)
|
||||
except IOError as e:
|
||||
PrintError('%s : error I/O(%s) accessing %s : %s' % (expansionRule, e.errno, e.filename, e.strerror))
|
||||
except:
|
||||
PrintError('%s : error Processing expansion rule' % expansionRule)
|
||||
PrintUnhandledExcptionInfo()
|
||||
raise
|
||||
|
||||
def ExecuteExpansionRules(cacheDir, outputDir, projectDir, inputFiles, expansionRules, dryrun, verbose, dataInputSet, outputFiles):
|
||||
# Get Globals
|
||||
global MAX_ERRORS
|
||||
global errorCount
|
||||
currentPath = os.getcwd()
|
||||
startTime = time.time()
|
||||
# Ensure jinja2 template cache dir actually exists...
|
||||
try:
|
||||
os.makedirs(cacheDir)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST:
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
sourceFiles = []
|
||||
templateFiles = []
|
||||
for inputFile in inputFiles:
|
||||
if inputFile.endswith(".xml") or inputFile.endswith(".json"):
|
||||
sourceFiles.append(os.path.join(projectDir, inputFile))
|
||||
elif inputFile.endswith(".jinja"):
|
||||
templateFiles.append(os.path.join(projectDir, inputFile))
|
||||
templateCache = jinja2.FileSystemBytecodeCache(cacheDir)
|
||||
for expansionRule in expansionRules:
|
||||
ProcessExpansionRule(sourceFiles, templateFiles, templateCache, outputDir, projectDir, expansionRule, dryrun, verbose, dataInputSet, outputFiles)
|
||||
if not dryrun:
|
||||
elapsedTime = time.time() - startTime
|
||||
millis = int(round(elapsedTime * 10))
|
||||
m, s = divmod(elapsedTime, 60)
|
||||
h, m = divmod(m, 60)
|
||||
print('Total Time %d:%02d:%02d.%02d' % (h, m, s, millis))
|
||||
# Return true on success
|
||||
return errorCount == 0
|
||||
|
||||
# Main Function
|
||||
if __name__ == '__main__':
|
||||
# setup our command syntax
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("cacheDir", help="location to store jinja template cache files")
|
||||
parser.add_argument("outputDir", help="location to output generated files")
|
||||
parser.add_argument("projectDir", help="location to build directory against")
|
||||
parser.add_argument("inputFiles", help="set of files to run azcg expansion rules against")
|
||||
parser.add_argument("expansionRules", help="set of azcg expansion rules for matching data files to template files")
|
||||
parser.add_argument("-n", "--dryrun", action='store_true', help="does not execute autogen, only outputs the set of files that autogen would generate")
|
||||
parser.add_argument("-v", "--verbose", action='store_true', help="output only the set of files that would be generated by an expansion run")
|
||||
parser.add_argument("-p", "--pythonPaths", action='append', nargs='+', default=[""], help="set of additional python paths to use for module imports")
|
||||
|
||||
args = parser.parse_args()
|
||||
pythonPaths = args.pythonPaths
|
||||
cacheDir = args.cacheDir
|
||||
outputDir = args.outputDir
|
||||
projectDir = args.projectDir
|
||||
inputFiles = args.inputFiles.split(";")
|
||||
expansionRules = args.expansionRules.split(";")
|
||||
dryrun = args.dryrun
|
||||
verbose = args.verbose
|
||||
cacheDir = os.path.abspath(SanitizePath(cacheDir))
|
||||
outputDir = os.path.abspath(SanitizePath(outputDir))
|
||||
projectDir = os.path.abspath(SanitizePath(projectDir))
|
||||
|
||||
# Import 3rd party modules
|
||||
for pythonPath in pythonPaths:
|
||||
sys.path.append(pythonPath)
|
||||
import jinja2
|
||||
#from lxml import etree
|
||||
import xml.etree.cElementTree as etree
|
||||
import json
|
||||
|
||||
dataInputSet = {}
|
||||
outputFiles = []
|
||||
autoGenResult = ExecuteExpansionRules(cacheDir, outputDir, projectDir, inputFiles, expansionRules, dryrun, verbose, dataInputSet, outputFiles)
|
||||
if dryrun:
|
||||
print("%s" % ';'.join(outputFiles))
|
||||
if autoGenResult:
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
||||
@@ -1,14 +0,0 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
ly_add_target(
|
||||
NAME AzAutoGen HEADERONLY
|
||||
NAMESPACE AZ
|
||||
FILES_CMAKE
|
||||
azautogen_files.cmake
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
AzAutoGen.py
|
||||
)
|
||||
@@ -84,7 +84,7 @@ namespace AZ
|
||||
else
|
||||
{
|
||||
AZ::Debug::Trace::Instance().Assert(__FILE__, __LINE__, AZ_FUNCTION_SIGNATURE,
|
||||
"Bus has multiple threads in its callstack records. Configure MutexType on the bus, or don't send to it from multiple threads");
|
||||
"Bus %s has multiple threads in its callstack records. Configure MutexType on the bus, or don't send to it from multiple threads", BusType::GetName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ namespace AZ
|
||||
m_messages.pop();
|
||||
if (numMessages == 1)
|
||||
{
|
||||
m_messages.get_container().clear(); // If it was the last message, free all memory.
|
||||
m_messages = {};
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -280,7 +280,7 @@ namespace AZ
|
||||
void Clear()
|
||||
{
|
||||
AZStd::lock_guard<MutexType> lock(m_messagesMutex);
|
||||
m_messages.get_container().clear();
|
||||
m_messages = {};
|
||||
}
|
||||
|
||||
void SetActive(bool isActive)
|
||||
@@ -289,7 +289,7 @@ namespace AZ
|
||||
m_isActive = isActive;
|
||||
if (!m_isActive)
|
||||
{
|
||||
m_messages.get_container().clear();
|
||||
m_messages = {};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Jobs/Job.h>
|
||||
#include <AzCore/Jobs/JobCancelGroup.h>
|
||||
#include <AzCore/Jobs/JobContext.h>
|
||||
#include <AzCore/Jobs/JobManager.h>
|
||||
#include <AzCore/std/parallel/atomic.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
Job::Job(bool isAutoDelete, AZ::JobContext* context, bool isCompletion, AZ::s8 priority)
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
m_context = context;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_context = JobContext::GetParentContext();
|
||||
}
|
||||
|
||||
unsigned int countAndFlags = 1;
|
||||
if (isAutoDelete)
|
||||
{
|
||||
countAndFlags |= (unsigned int)FLAG_AUTO_DELETE;
|
||||
}
|
||||
if (isCompletion)
|
||||
{
|
||||
countAndFlags |= (unsigned int)FLAG_COMPLETION;
|
||||
}
|
||||
countAndFlags |= (unsigned int)((priority << FLAG_PRIORITY_START_BIT) & FLAG_PRIORITY_MASK);
|
||||
SetDependentCountAndFlags(countAndFlags);
|
||||
StoreDependent(NULL);
|
||||
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
SetState(STATE_SETUP);
|
||||
#endif // AZ_DEBUG_JOB_STATE
|
||||
}
|
||||
|
||||
void Job::Start()
|
||||
{
|
||||
//jobs are created with a count set to 1, we remove that count to allow the job to start
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Jobs must be in the setup state before they can be started"));
|
||||
SetState(STATE_STARTED);
|
||||
#endif
|
||||
DecrementDependentCount();
|
||||
}
|
||||
|
||||
void Job::Reset(bool isClearDependent)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert((m_state == STATE_SETUP) || (m_state == STATE_PROCESSING), "Jobs must not be running when they are reset");
|
||||
SetState(STATE_SETUP);
|
||||
#endif
|
||||
unsigned int countAndFlags = GetDependentCountAndFlags();
|
||||
AZ_Assert((countAndFlags & (unsigned int)FLAG_AUTO_DELETE) == 0, "You can't call reset on AutoDelete jobs!");
|
||||
// Remove the FLAG_DEPENDENTCOUNT_MASK and FLAG_CHILD_JOBS flags
|
||||
countAndFlags = (countAndFlags & (~(FLAG_DEPENDENTCOUNT_MASK) & ~(FLAG_CHILD_JOBS))) | 1;
|
||||
SetDependentCountAndFlags(countAndFlags);
|
||||
if (isClearDependent)
|
||||
{
|
||||
StoreDependent(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
Job* dependent = GetDependent();
|
||||
if (dependent)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(dependent->m_state == STATE_SETUP, ("Dependent must be in setup state before it can be re-initialized"));
|
||||
#endif
|
||||
dependent->IncrementDependentCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Job::SetDependent(Job* dependent)
|
||||
{
|
||||
AZ_Assert(!GetDependent(), ("Job already has a dependent, should be cleared after the job is done"));
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Dependent can only be set before the jobs are started"));
|
||||
AZ_Assert(dependent->m_state == STATE_SETUP, ("Dependent must be in the setup state"));
|
||||
#endif
|
||||
dependent->IncrementDependentCount();
|
||||
StoreDependent(dependent);
|
||||
}
|
||||
|
||||
void Job::SetDependentStarted(Job* dependent)
|
||||
{
|
||||
AZ_Assert(!GetDependent(), ("Job already has a dependent, should be cleared after the job is done"));
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Dependent can only be set before the jobs are started"));
|
||||
//We don't require the dependent to be in STATE_SETUP, the user can call this from a context where they
|
||||
//know the dependent has not started yet, although it is in STATE_STARTED already, e.g. if SetDependent
|
||||
//is called from a job which the dependent is already dependent on.
|
||||
//Note that if the user gets this wrong, the dependent may start before this job is finished, and the asserts
|
||||
//may not even trigger due to race conditions. Hence why this function is 'experts only'.
|
||||
AZ_Assert((dependent->m_state == STATE_SETUP) || (dependent->m_state == STATE_STARTED)
|
||||
|| (dependent->m_state == STATE_SUSPENDED), "Dependent must be in the setup, started, or suspended state");
|
||||
#endif
|
||||
dependent->IncrementDependentCount();
|
||||
StoreDependent(dependent);
|
||||
}
|
||||
|
||||
void Job::SetDependentChild(Job* dependent)
|
||||
{
|
||||
AZ_Assert(!GetDependent(), ("Job already has a dependent, should be cleared after the job is done"));
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Dependent can only be set before the jobs are started"));
|
||||
AZ_Assert(dependent->m_state == STATE_PROCESSING, "Dependent must be processing to add a child");
|
||||
#endif
|
||||
dependent->IncrementDependentCountAndSetChildFlag();
|
||||
StoreDependent(dependent);
|
||||
}
|
||||
|
||||
void Job::SetContinuation(Job* continuationJob)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_PROCESSING, "Continuation jobs can only be set while we are processing, otherwise a regular dependent should be used");
|
||||
#endif
|
||||
Job* dependent = GetDependent();
|
||||
if (dependent) //nothing to do if there is no dependent... doesn't usually happen, except with synchronous processing and assists
|
||||
{
|
||||
continuationJob->SetDependentStarted(dependent);
|
||||
}
|
||||
}
|
||||
|
||||
void Job::StartAsChild(Job* childJob)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_PROCESSING, "Child jobs can only be added while we are processing");
|
||||
#endif
|
||||
childJob->SetDependentChild(this);
|
||||
childJob->Start();
|
||||
}
|
||||
|
||||
void Job::WaitForChildren()
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_PROCESSING, "We must be currently processing in order to suspend");
|
||||
#endif
|
||||
if (GetDependentCount() != 0)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
SetState(STATE_SUSPENDED);
|
||||
#endif // AZ_DEBUG_JOB_STATE
|
||||
m_context->GetJobManager().SuspendJobUntilReady(this);
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
SetState(STATE_PROCESSING);
|
||||
#endif // AZ_DEBUG_JOB_STATE
|
||||
}
|
||||
AZ_Assert(GetDependentCount() == 0, "Suspended job has resumed, but still has non-zero dependent count, bug in JobManager?");
|
||||
}
|
||||
|
||||
bool Job::IsCancelled() const
|
||||
{
|
||||
JobCancelGroup* cancelGroup = m_context->GetCancelGroup();
|
||||
if (cancelGroup && cancelGroup->IsCancelled())
|
||||
{
|
||||
if (!IsCompletion()) // always run completion jobs, as they can be holding a synchronization primitive
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Job::IsAutoDelete() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() & (unsigned int)FLAG_AUTO_DELETE) ? true : false;
|
||||
}
|
||||
|
||||
bool Job::IsCompletion() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() & (unsigned int)FLAG_COMPLETION) ? true : false;
|
||||
}
|
||||
|
||||
void Job::StartAndAssistUntilComplete()
|
||||
{
|
||||
m_context->GetJobManager().StartJobAndAssistUntilComplete(this);
|
||||
}
|
||||
|
||||
void Job::StartAndWaitForCompletion()
|
||||
{
|
||||
//check if we are in a worker thread or a general user thread
|
||||
Job* currentJob = m_context->GetJobManager().GetCurrentJob();
|
||||
if (currentJob)
|
||||
{
|
||||
//worker thread, so just suspend this current job until the empty job completes
|
||||
currentJob->StartAsChild(this);
|
||||
currentJob->WaitForChildren();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartAndAssistUntilComplete();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Job::GetDependentCount() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() & FLAG_DEPENDENTCOUNT_MASK);
|
||||
}
|
||||
|
||||
void Job::IncrementDependentCount()
|
||||
{
|
||||
AZ_Assert(GetDependentCount() < FLAG_DEPENDENTCOUNT_MASK, "Dependent count overflow");
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
++m_dependentCountAndFlags;
|
||||
#else
|
||||
m_dependentCountAndFlags.fetch_add(1, AZStd::memory_order_acq_rel);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Job::IncrementDependentCountAndSetChildFlag()
|
||||
{
|
||||
AZ_Assert(GetDependentCount() < FLAG_DEPENDENTCOUNT_MASK, "Dependent count overflow");
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
int oldCount = m_dependentCountAndFlags & FLAG_DEPENDENTCOUNT_MASK;
|
||||
m_dependentCountAndFlags = (m_dependentCountAndFlags & ~FLAG_DEPENDENTCOUNT_MASK) | (oldCount + 1) | FLAG_CHILD_JOBS;
|
||||
#else
|
||||
//use a single atomic operation to increment the count and set the child flag if possible
|
||||
unsigned int oldCountAndFlags, newCountAndFlags;
|
||||
do
|
||||
{
|
||||
oldCountAndFlags = m_dependentCountAndFlags.load(AZStd::memory_order_acquire);
|
||||
int oldCount = oldCountAndFlags & FLAG_DEPENDENTCOUNT_MASK;
|
||||
newCountAndFlags = (oldCountAndFlags & ~FLAG_DEPENDENTCOUNT_MASK) | (oldCount + 1) | FLAG_CHILD_JOBS;
|
||||
} while (!m_dependentCountAndFlags.compare_exchange_weak(oldCountAndFlags, newCountAndFlags, AZStd::memory_order_acq_rel, AZStd::memory_order_acquire));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Job::DecrementDependentCount()
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert((m_state == STATE_SETUP) || (m_state == STATE_STARTED)
|
||||
|| (m_state == STATE_PROCESSING) || (m_state == STATE_SUSPENDED), //child jobs
|
||||
"Job dependent count should not be decremented after job is already pending");
|
||||
#endif
|
||||
AZ_Assert(GetDependentCount() > 0, ("Job dependent count is already zero"));
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
unsigned int countAndFlags = m_dependentCountAndFlags--;
|
||||
#else
|
||||
unsigned int countAndFlags = m_dependentCountAndFlags.fetch_sub(1, AZStd::memory_order_acq_rel);
|
||||
#endif
|
||||
unsigned int count = countAndFlags & FLAG_DEPENDENTCOUNT_MASK;
|
||||
if (count == 1)
|
||||
{
|
||||
if (!(countAndFlags & FLAG_CHILD_JOBS))
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_STARTED, "Job has not been started but the dependent count is zero, must be a dependency error");
|
||||
SetState(STATE_PENDING);
|
||||
#endif
|
||||
m_context->GetJobManager().AddPendingJob(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZ::s8 Job::GetPriority() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() >> FLAG_PRIORITY_START_BIT) & 0xff;
|
||||
}
|
||||
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
void Job::StoreDependent(Job* job)
|
||||
{
|
||||
m_dependent = job;
|
||||
}
|
||||
|
||||
Job* Job::GetDependent() const
|
||||
{
|
||||
return m_dependent;
|
||||
}
|
||||
|
||||
void Job::SetDependentCountAndFlags(unsigned int countAndFlags)
|
||||
{
|
||||
m_dependentCountAndFlags = countAndFlags;
|
||||
}
|
||||
|
||||
unsigned int Job::GetDependentCountAndFlags() const
|
||||
{
|
||||
return m_dependentCountAndFlags;
|
||||
}
|
||||
#else
|
||||
void Job::StoreDependent(Job* job)
|
||||
{
|
||||
m_dependent.store(job, AZStd::memory_order_release);
|
||||
}
|
||||
|
||||
Job* Job::GetDependent() const
|
||||
{
|
||||
return m_dependent.load(AZStd::memory_order_acquire);
|
||||
}
|
||||
|
||||
void Job::SetDependentCountAndFlags(unsigned int countAndFlags)
|
||||
{
|
||||
m_dependentCountAndFlags.store(countAndFlags, AZStd::memory_order_release);
|
||||
}
|
||||
|
||||
unsigned int Job::GetDependentCountAndFlags() const
|
||||
{
|
||||
return m_dependentCountAndFlags.load(AZStd::memory_order_acquire);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -5,15 +5,14 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZCORE_JOBS_JOB_H
|
||||
#define AZCORE_JOBS_JOB_H 1
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Jobs/JobCancelGroup.h>
|
||||
#include <AzCore/Jobs/JobContext.h>
|
||||
#include <AzCore/Jobs/JobManager.h>
|
||||
#include <AzCore/std/parallel/atomic.h>
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Jobs/JobCancelGroup.h>
|
||||
#include <AzCore/Jobs/JobContext.h>
|
||||
#include <AzCore/Jobs/JobManager.h>
|
||||
#include <AzCore/std/parallel/atomic.h>
|
||||
|
||||
#include <AzCore/Memory/PoolAllocator.h>
|
||||
|
||||
#if defined(_DEBUG)
|
||||
@@ -234,319 +233,22 @@ namespace AZ
|
||||
//would require atomic ops to set/read it, so not really worth it.
|
||||
int m_state;
|
||||
};
|
||||
|
||||
//============================================================================================================
|
||||
//============================================================================================================
|
||||
//============================================================================================================
|
||||
|
||||
inline Job::Job(bool isAutoDelete, JobContext* context, bool isCompletion, AZ::s8 priority)
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
m_context = context;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_context = JobContext::GetParentContext();
|
||||
}
|
||||
|
||||
unsigned int countAndFlags = 1;
|
||||
if (isAutoDelete)
|
||||
{
|
||||
countAndFlags |= (unsigned int)FLAG_AUTO_DELETE;
|
||||
}
|
||||
if (isCompletion)
|
||||
{
|
||||
countAndFlags |= (unsigned int)FLAG_COMPLETION;
|
||||
}
|
||||
countAndFlags |= (unsigned int)((priority << FLAG_PRIORITY_START_BIT) & FLAG_PRIORITY_MASK);
|
||||
SetDependentCountAndFlags(countAndFlags);
|
||||
StoreDependent(NULL);
|
||||
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
SetState(STATE_SETUP);
|
||||
#endif // AZ_DEBUG_JOB_STATE
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::Start()
|
||||
{
|
||||
//jobs are created with a count set to 1, we remove that count to allow the job to start
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Jobs must be in the setup state before they can be started"));
|
||||
SetState(STATE_STARTED);
|
||||
#endif
|
||||
DecrementDependentCount();
|
||||
}
|
||||
|
||||
inline void Job::Reset(bool isClearDependent)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert((m_state == STATE_SETUP) || (m_state == STATE_PROCESSING), "Jobs must not be running when they are reset");
|
||||
SetState(STATE_SETUP);
|
||||
#endif
|
||||
unsigned int countAndFlags = GetDependentCountAndFlags();
|
||||
AZ_Assert((countAndFlags & (unsigned int)FLAG_AUTO_DELETE) == 0, "You can't call reset on AutoDelete jobs!");
|
||||
// Remove the FLAG_DEPENDENTCOUNT_MASK and FLAG_CHILD_JOBS flags
|
||||
countAndFlags = (countAndFlags & (~(FLAG_DEPENDENTCOUNT_MASK) & ~(FLAG_CHILD_JOBS))) | 1;
|
||||
SetDependentCountAndFlags(countAndFlags);
|
||||
if (isClearDependent)
|
||||
{
|
||||
StoreDependent(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
Job* dependent = GetDependent();
|
||||
if (dependent)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(dependent->m_state == STATE_SETUP, ("Dependent must be in setup state before it can be re-initialized"));
|
||||
#endif
|
||||
dependent->IncrementDependentCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::SetDependent(Job* dependent)
|
||||
{
|
||||
AZ_Assert(!GetDependent(), ("Job already has a dependent, should be cleared after the job is done"));
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Dependent can only be set before the jobs are started"));
|
||||
AZ_Assert(dependent->m_state == STATE_SETUP, ("Dependent must be in the setup state"));
|
||||
#endif
|
||||
dependent->IncrementDependentCount();
|
||||
StoreDependent(dependent);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::SetDependentStarted(Job* dependent)
|
||||
{
|
||||
AZ_Assert(!GetDependent(), ("Job already has a dependent, should be cleared after the job is done"));
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Dependent can only be set before the jobs are started"));
|
||||
//We don't require the dependent to be in STATE_SETUP, the user can call this from a context where they
|
||||
//know the dependent has not started yet, although it is in STATE_STARTED already, e.g. if SetDependent
|
||||
//is called from a job which the dependent is already dependent on.
|
||||
//Note that if the user gets this wrong, the dependent may start before this job is finished, and the asserts
|
||||
//may not even trigger due to race conditions. Hence why this function is 'experts only'.
|
||||
AZ_Assert((dependent->m_state == STATE_SETUP) || (dependent->m_state == STATE_STARTED)
|
||||
|| (dependent->m_state == STATE_SUSPENDED), "Dependent must be in the setup, started, or suspended state");
|
||||
#endif
|
||||
dependent->IncrementDependentCount();
|
||||
StoreDependent(dependent);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::SetDependentChild(Job* dependent)
|
||||
{
|
||||
AZ_Assert(!GetDependent(), ("Job already has a dependent, should be cleared after the job is done"));
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Dependent can only be set before the jobs are started"));
|
||||
AZ_Assert(dependent->m_state == STATE_PROCESSING, "Dependent must be processing to add a child");
|
||||
#endif
|
||||
dependent->IncrementDependentCountAndSetChildFlag();
|
||||
StoreDependent(dependent);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::SetContinuation(Job* continuationJob)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_PROCESSING, "Continuation jobs can only be set while we are processing, otherwise a regular dependent should be used");
|
||||
#endif
|
||||
Job* dependent = GetDependent();
|
||||
if (dependent) //nothing to do if there is no dependent... doesn't usually happen, except with synchronous processing and assists
|
||||
{
|
||||
continuationJob->SetDependentStarted(dependent);
|
||||
}
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::StartAsChild(Job* childJob)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_PROCESSING, "Child jobs can only be added while we are processing");
|
||||
#endif
|
||||
childJob->SetDependentChild(this);
|
||||
childJob->Start();
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::WaitForChildren()
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_PROCESSING, "We must be currently processing in order to suspend");
|
||||
#endif
|
||||
if (GetDependentCount() != 0)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
SetState(STATE_SUSPENDED);
|
||||
#endif // AZ_DEBUG_JOB_STATE
|
||||
m_context->GetJobManager().SuspendJobUntilReady(this);
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
SetState(STATE_PROCESSING);
|
||||
#endif // AZ_DEBUG_JOB_STATE
|
||||
}
|
||||
AZ_Assert(GetDependentCount() == 0, "Suspended job has resumed, but still has non-zero dependent count, bug in JobManager?");
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE bool Job::IsCancelled() const
|
||||
{
|
||||
JobCancelGroup* cancelGroup = m_context->GetCancelGroup();
|
||||
if (cancelGroup && cancelGroup->IsCancelled())
|
||||
{
|
||||
if (!IsCompletion()) // always run completion jobs, as they can be holding a synchronization primitive
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE bool Job::IsAutoDelete() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() & (unsigned int)FLAG_AUTO_DELETE) ? true : false;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE bool Job::IsCompletion() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() & (unsigned int)FLAG_COMPLETION) ? true : false;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::StartAndAssistUntilComplete()
|
||||
{
|
||||
m_context->GetJobManager().StartJobAndAssistUntilComplete(this);
|
||||
}
|
||||
|
||||
inline void Job::StartAndWaitForCompletion()
|
||||
{
|
||||
//check if we are in a worker thread or a general user thread
|
||||
Job* currentJob = m_context->GetJobManager().GetCurrentJob();
|
||||
if (currentJob)
|
||||
{
|
||||
//worker thread, so just suspend this current job until the empty job completes
|
||||
currentJob->StartAsChild(this);
|
||||
currentJob->WaitForChildren();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartAndAssistUntilComplete();
|
||||
}
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE JobContext* Job::GetContext() const
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Inline implementations
|
||||
inline JobContext* Job::GetContext() const
|
||||
{
|
||||
return m_context;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE unsigned int Job::GetDependentCount() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() & FLAG_DEPENDENTCOUNT_MASK);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::IncrementDependentCount()
|
||||
{
|
||||
AZ_Assert(GetDependentCount() < FLAG_DEPENDENTCOUNT_MASK, "Dependent count overflow");
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
++m_dependentCountAndFlags;
|
||||
#else
|
||||
m_dependentCountAndFlags.fetch_add(1, AZStd::memory_order_acq_rel);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void Job::IncrementDependentCountAndSetChildFlag()
|
||||
{
|
||||
AZ_Assert(GetDependentCount() < FLAG_DEPENDENTCOUNT_MASK, "Dependent count overflow");
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
int oldCount = m_dependentCountAndFlags & FLAG_DEPENDENTCOUNT_MASK;
|
||||
m_dependentCountAndFlags = (m_dependentCountAndFlags & ~FLAG_DEPENDENTCOUNT_MASK) | (oldCount + 1) | FLAG_CHILD_JOBS;
|
||||
#else
|
||||
//use a single atomic operation to increment the count and set the child flag if possible
|
||||
unsigned int oldCountAndFlags, newCountAndFlags;
|
||||
do
|
||||
{
|
||||
oldCountAndFlags = m_dependentCountAndFlags.load(AZStd::memory_order_acquire);
|
||||
int oldCount = oldCountAndFlags & FLAG_DEPENDENTCOUNT_MASK;
|
||||
newCountAndFlags = (oldCountAndFlags & ~FLAG_DEPENDENTCOUNT_MASK) | (oldCount + 1) | FLAG_CHILD_JOBS;
|
||||
} while (!m_dependentCountAndFlags.compare_exchange_weak(oldCountAndFlags, newCountAndFlags, AZStd::memory_order_acq_rel, AZStd::memory_order_acquire));
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void Job::DecrementDependentCount()
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert((m_state == STATE_SETUP) || (m_state == STATE_STARTED)
|
||||
|| (m_state == STATE_PROCESSING) || (m_state == STATE_SUSPENDED), //child jobs
|
||||
"Job dependent count should not be decremented after job is already pending");
|
||||
#endif
|
||||
AZ_Assert(GetDependentCount() > 0, ("Job dependent count is already zero"));
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
unsigned int countAndFlags = m_dependentCountAndFlags--;
|
||||
#else
|
||||
unsigned int countAndFlags = m_dependentCountAndFlags.fetch_sub(1, AZStd::memory_order_acq_rel);
|
||||
#endif
|
||||
unsigned int count = countAndFlags & FLAG_DEPENDENTCOUNT_MASK;
|
||||
if (count == 1)
|
||||
{
|
||||
if (!(countAndFlags & FLAG_CHILD_JOBS))
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_STARTED, "Job has not been started but the dependent count is zero, must be a dependency error");
|
||||
SetState(STATE_PENDING);
|
||||
#endif
|
||||
m_context->GetJobManager().AddPendingJob(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline AZ::s8 Job::GetPriority() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() >> FLAG_PRIORITY_START_BIT) & 0xff;
|
||||
}
|
||||
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_FORCE_INLINE void Job::SetState(int state)
|
||||
inline void Job::SetState(int state)
|
||||
{
|
||||
m_state = state;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
AZ_FORCE_INLINE void Job::StoreDependent(Job* job)
|
||||
{
|
||||
m_dependent = job;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE Job* Job::GetDependent() const
|
||||
{
|
||||
return m_dependent;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::SetDependentCountAndFlags(unsigned int countAndFlags)
|
||||
{
|
||||
m_dependentCountAndFlags = countAndFlags;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE unsigned int Job::GetDependentCountAndFlags() const
|
||||
{
|
||||
return m_dependentCountAndFlags;
|
||||
}
|
||||
#else
|
||||
AZ_FORCE_INLINE void Job::StoreDependent(Job* job)
|
||||
{
|
||||
m_dependent.store(job, AZStd::memory_order_release);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE Job* Job::GetDependent() const
|
||||
{
|
||||
return m_dependent.load(AZStd::memory_order_acquire);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::SetDependentCountAndFlags(unsigned int countAndFlags)
|
||||
{
|
||||
m_dependentCountAndFlags.store(countAndFlags, AZStd::memory_order_release);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE unsigned int Job::GetDependentCountAndFlags() const
|
||||
{
|
||||
return m_dependentCountAndFlags.load(AZStd::memory_order_acquire);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -42,8 +42,6 @@ namespace AZStd
|
||||
class unordered_multiset;
|
||||
template<AZStd::size_t NumBits>
|
||||
class bitset;
|
||||
template<class T, class Container/* = AZStd::deque<T>*/ >
|
||||
class stack;
|
||||
|
||||
template<class T>
|
||||
class intrusive_ptr;
|
||||
|
||||
@@ -236,6 +236,17 @@ namespace AZ
|
||||
*/
|
||||
ClassBuilder* ClassElement(Crc32 elementIdCrc, const char* description);
|
||||
|
||||
|
||||
/**
|
||||
* Declare element with attributes that belong to the class SerializeContext::Class, this is a logical structure, you can have one or more GroupElementToggles.
|
||||
* T must be a boolean variable that will enable and disable each DataElement attached to this structure.
|
||||
* \param description - Descriptive name of the field that will typically appear in a tooltip.
|
||||
* \param memberVariable - reference to the member variable so we can bind to serialization data.
|
||||
*/
|
||||
template<class T>
|
||||
ClassBuilder* GroupElementToggle(const char* description, T memberVariable);
|
||||
|
||||
|
||||
/**
|
||||
* Declare element with an associated UI handler that does not represent a specific class member variable.
|
||||
* \param uiId - name of a UI handler used to display the element
|
||||
@@ -515,6 +526,15 @@ namespace AZ
|
||||
return this;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ClassElement
|
||||
//=========================================================================
|
||||
template<class T>
|
||||
inline EditContext::ClassBuilder* EditContext::ClassBuilder::GroupElementToggle(const char* name, T memberVariable)
|
||||
{
|
||||
return DataElement(AZ::Edit::ClassElements::Group, memberVariable, name, name, "");
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// UIElement
|
||||
//=========================================================================
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace AZ::Internal
|
||||
// and avoid all this logic.
|
||||
|
||||
using namespace AZ::SettingsRegistryMergeUtils;
|
||||
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
|
||||
|
||||
AZ::IO::FixedMaxPath engineRoot;
|
||||
if (auto engineManifestPath = AZ::Utils::GetEngineManifestPath(); !engineManifestPath.empty())
|
||||
@@ -72,45 +73,16 @@ namespace AZ::Internal
|
||||
struct EngineInfo
|
||||
{
|
||||
AZ::IO::FixedMaxPath m_path;
|
||||
AZ::SettingsRegistryInterface::FixedValueString m_moniker;
|
||||
FixedValueString m_moniker;
|
||||
};
|
||||
|
||||
struct EnginePathsVisitor : public AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
void Visit(
|
||||
[[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName,
|
||||
[[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName,
|
||||
[[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override
|
||||
{
|
||||
m_enginePaths.emplace_back(EngineInfo{AZ::IO::FixedMaxPath{value}.LexicallyNormal(), {}});
|
||||
}
|
||||
|
||||
AZ::SettingsRegistryInterface::VisitResponse Traverse(
|
||||
[[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName,
|
||||
AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type type) override
|
||||
{
|
||||
auto response = AZ::SettingsRegistryInterface::VisitResponse::Continue;
|
||||
if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
|
||||
{
|
||||
if (type == AZ::SettingsRegistryInterface::Type::Array)
|
||||
{
|
||||
if (valueName.compare("engines") != 0)
|
||||
{
|
||||
response = AZ::SettingsRegistryInterface::VisitResponse::Skip;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (action == AZ::SettingsRegistryInterface::VisitAction::Value)
|
||||
{
|
||||
if (type == AZ::SettingsRegistryInterface::Type::String)
|
||||
{
|
||||
if (valueName.compare("path") != 0)
|
||||
{
|
||||
response = AZ::SettingsRegistryInterface::VisitResponse::Skip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
m_enginePaths.emplace_back(EngineInfo{ AZ::IO::FixedMaxPath{value}.LexicallyNormal(), FixedValueString{valueName} });
|
||||
}
|
||||
|
||||
AZStd::vector<EngineInfo> m_enginePaths{};
|
||||
@@ -119,11 +91,11 @@ namespace AZ::Internal
|
||||
EnginePathsVisitor pathVisitor;
|
||||
if (manifestLoaded)
|
||||
{
|
||||
auto enginePathsKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/engines", EngineManifestRootKey);
|
||||
auto enginePathsKey = FixedValueString::format("%s/engines_path", EngineManifestRootKey);
|
||||
settingsRegistry.Visit(pathVisitor, enginePathsKey);
|
||||
}
|
||||
|
||||
const auto engineMonikerKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/engine_name", EngineSettingsRootKey);
|
||||
const auto engineMonikerKey = FixedValueString::format("%s/engine_name", EngineSettingsRootKey);
|
||||
|
||||
AZStd::set<AZ::IO::FixedMaxPath> projectPathsNotFound;
|
||||
|
||||
@@ -135,7 +107,15 @@ namespace AZ::Internal
|
||||
if (settingsRegistry.MergeSettingsFile(
|
||||
engineSettingsPath.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, EngineSettingsRootKey))
|
||||
{
|
||||
settingsRegistry.Get(engineInfo.m_moniker, engineMonikerKey);
|
||||
FixedValueString engineName;
|
||||
settingsRegistry.Get(engineName, engineMonikerKey);
|
||||
AZ_Warning("SettingsRegistryMergeUtils",engineInfo.m_moniker == engineName,
|
||||
R"(The engine name key "%s" mapped to engine path "%s" within the global manifest of "%s")"
|
||||
R"( does not match the "engine_name" field "%s" in the engine.json)" "\n"
|
||||
"This engine should be re-registered.",
|
||||
engineInfo.m_moniker.c_str(), engineInfo.m_path.c_str(), engineManifestPath.c_str(),
|
||||
engineName.c_str())
|
||||
engineInfo.m_moniker = engineName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -221,6 +221,7 @@ set(FILES
|
||||
Jobs/Internal/JobManagerWorkStealing.cpp
|
||||
Jobs/Internal/JobManagerWorkStealing.h
|
||||
Jobs/Internal/JobNotify.h
|
||||
Jobs/Job.cpp
|
||||
Jobs/Job.h
|
||||
Jobs/JobCancelGroup.h
|
||||
Jobs/JobCompletion.h
|
||||
|
||||
@@ -5,206 +5,17 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZSTD_QUEUE_H
|
||||
#define AZSTD_QUEUE_H 1
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/deque.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/functional_basic.h>
|
||||
#include <queue>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
/**
|
||||
* FIFO queue complaint with \ref CStd (23.2.3.1)
|
||||
* The only extension we have is that we allow access
|
||||
* to the underlying container via: get_container function.
|
||||
* Check the queue \ref AZStdExamples.
|
||||
*/
|
||||
template<class T, class Container = AZStd::deque<T> >
|
||||
class queue
|
||||
{
|
||||
enum
|
||||
{
|
||||
CONTAINER_VERSION = 1
|
||||
};
|
||||
public:
|
||||
typedef queue<T, Container> this_type;
|
||||
typedef Container container_type;
|
||||
typedef typename Container::value_type value_type;
|
||||
typedef typename Container::size_type size_type;
|
||||
typedef typename Container::reference reference;
|
||||
typedef typename Container::const_reference const_reference;
|
||||
|
||||
AZ_FORCE_INLINE queue() {}
|
||||
|
||||
AZ_FORCE_INLINE explicit queue(const container_type& container)
|
||||
: m_container(container) {}
|
||||
AZ_FORCE_INLINE bool empty() const { return m_container.empty(); }
|
||||
AZ_FORCE_INLINE size_type size() const { return m_container.size(); }
|
||||
AZ_FORCE_INLINE reference front() { return m_container.front(); }
|
||||
AZ_FORCE_INLINE const_reference front() const { return m_container.front(); }
|
||||
AZ_FORCE_INLINE reference back() { return m_container.back(); }
|
||||
AZ_FORCE_INLINE const_reference back() const { return m_container.back(); }
|
||||
AZ_FORCE_INLINE void push(const value_type& value) { m_container.push_back(value); }
|
||||
AZ_FORCE_INLINE void pop() { m_container.pop_front(); }
|
||||
|
||||
AZ_FORCE_INLINE void push() { m_container.push_back(); }
|
||||
|
||||
AZ_FORCE_INLINE queue(this_type&& rhs)
|
||||
: m_container(AZStd::move(rhs.m_container)) {}
|
||||
AZ_FORCE_INLINE explicit queue(Container&& container)
|
||||
: m_container(AZStd::move(container)) {}
|
||||
this_type& operator=(this_type&& rhs)
|
||||
{
|
||||
m_container = AZStd::move(rhs.m_container);
|
||||
return (*this);
|
||||
}
|
||||
void push(value_type&& value) { m_container.push_back(AZStd::move(value)); }
|
||||
template<class... Args>
|
||||
void emplace(Args&&... args) { m_container.emplace_back(AZStd::forward<Args>(args)...); }
|
||||
void swap(this_type& rhs) { AZStd::swap(m_container, rhs.m_container); }
|
||||
|
||||
AZ_FORCE_INLINE Container& get_container() { return m_container; }
|
||||
AZ_FORCE_INLINE const Container& get_container() const { return m_container; }
|
||||
|
||||
protected:
|
||||
Container m_container;
|
||||
};
|
||||
|
||||
// queue TEMPLATE FUNCTIONS
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator==(const AZStd::queue<T, Container>& left, const AZStd::queue<T, Container>& right)
|
||||
{
|
||||
return left.get_container() == right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator!=(const AZStd::queue<T, Container>& left, const AZStd::queue<T, Container>& right)
|
||||
{
|
||||
return left.get_container() != right.get_container();
|
||||
}
|
||||
|
||||
/* template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator<(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() < right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator>(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() > right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE operator<=(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() <= right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator>=(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() >= right.get_container();
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Priority queue is complaint with \ref CStd (23.2.3.2)
|
||||
* The only extension we have is that we allow access
|
||||
* to the underlying container via: get_container function.
|
||||
* Check the priority_queue \ref AZStdExamples.
|
||||
*/
|
||||
template<class T, class Container = AZStd::vector<T>, class Predicate = AZStd::less<typename Container::value_type> >
|
||||
class priority_queue
|
||||
{
|
||||
enum
|
||||
{
|
||||
CONTAINER_VERSION = 1
|
||||
};
|
||||
public:
|
||||
typedef priority_queue<T, Container, Predicate> this_type;
|
||||
typedef Container container_type;
|
||||
typedef typename Container::value_type value_type;
|
||||
typedef typename Container::size_type size_type;
|
||||
typedef typename Container::reference reference;
|
||||
typedef typename Container::const_reference const_reference;
|
||||
|
||||
AZ_FORCE_INLINE priority_queue() {}
|
||||
AZ_FORCE_INLINE explicit priority_queue(const Predicate& comp)
|
||||
: m_comp(comp) {}
|
||||
AZ_FORCE_INLINE priority_queue(const Predicate& comp, const container_type& container)
|
||||
: m_container(container)
|
||||
, m_comp(comp)
|
||||
{
|
||||
// construct by copying specified container, comparator
|
||||
AZStd::make_heap(m_container.begin(), m_container.end(), comp);
|
||||
}
|
||||
template<class InputIterator>
|
||||
AZ_FORCE_INLINE priority_queue(InputIterator first, InputIterator last)
|
||||
: m_container(first, last)
|
||||
, m_comp()
|
||||
{
|
||||
AZStd::make_heap(m_container.begin(), m_container.end(), m_comp);
|
||||
}
|
||||
|
||||
template<class InputIterator>
|
||||
AZ_FORCE_INLINE priority_queue(InputIterator first, InputIterator last, const Predicate& comp)
|
||||
: m_container(first, last)
|
||||
, m_comp(comp)
|
||||
{ // construct by copying [_First, _Last), specified comparator
|
||||
AZStd::make_heap(m_container.begin(), m_container.end(), m_comp);
|
||||
}
|
||||
|
||||
template<class InputIterator>
|
||||
AZ_FORCE_INLINE priority_queue(InputIterator first, InputIterator last, const Predicate& comp, const container_type& container)
|
||||
: m_container(container)
|
||||
, m_comp(comp)
|
||||
{ // construct by copying [_First, _Last), container, and comparator
|
||||
m_container.insert(m_container.end(), first, last);
|
||||
AZStd::make_heap(m_container.begin(), m_container.end(), m_comp);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE bool empty() const { return m_container.empty(); }
|
||||
AZ_FORCE_INLINE size_type size() const { return m_container.size(); }
|
||||
AZ_FORCE_INLINE const_reference top() const { return m_container.front(); }
|
||||
AZ_FORCE_INLINE reference top() { return m_container.front(); }
|
||||
AZ_FORCE_INLINE void push(const value_type& value)
|
||||
{
|
||||
m_container.push_back(value);
|
||||
AZStd::push_heap(m_container.begin(), m_container.end(), m_comp);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void pop()
|
||||
{
|
||||
AZStd::pop_heap(m_container.begin(), m_container.end(), m_comp);
|
||||
m_container.pop_back();
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE priority_queue(this_type&& rhs)
|
||||
: m_container(AZStd::move(rhs.m_container))
|
||||
, m_comp(AZStd::move(rhs.m_comp)) {}
|
||||
AZ_FORCE_INLINE explicit priority_queue(const Predicate& pred, Container&& container)
|
||||
: m_container(AZStd::move(container))
|
||||
, m_comp(pred) {}
|
||||
this_type& operator=(this_type&& rhs)
|
||||
{
|
||||
m_container = AZStd::move(rhs.m_container);
|
||||
m_comp = AZStd::move(rhs.m_comp);
|
||||
return (*this);
|
||||
}
|
||||
void push(value_type&& value) { m_container.push_back(AZStd::move(value)); AZStd::push_heap(m_container.begin(), m_container.end(), m_comp); }
|
||||
template<class Args>
|
||||
void emplace(Args&& args) { m_container.emplace_back(AZStd::forward<Args>(args)); AZStd::push_heap(m_container.begin(), m_container.end(), m_comp); }
|
||||
void swap(this_type& rhs) { AZStd::swap(m_container, rhs.m_container); AZStd::swap(m_comp, rhs.m_comp); }
|
||||
|
||||
AZ_FORCE_INLINE Container& get_container() { return m_container; }
|
||||
AZ_FORCE_INLINE const Container& get_container() const { return m_container; }
|
||||
|
||||
protected:
|
||||
Container m_container;
|
||||
Predicate m_comp;
|
||||
};
|
||||
template<class T, class Container = AZStd::deque<T>>
|
||||
using queue = std::queue<T, Container>;
|
||||
template<class T, class Container = AZStd::vector<T>, class Compare = AZStd::less<typename Container::value_type>>
|
||||
using priority_queue = std::priority_queue<T, Container, Compare>;
|
||||
}
|
||||
|
||||
#endif // AZSTD_QUEUE_H
|
||||
#pragma once
|
||||
|
||||
@@ -5,103 +5,13 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZSTD_STACK_H
|
||||
#define AZSTD_STACK_H 1
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/deque.h>
|
||||
#include <stack>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
/**
|
||||
* Stack container is complaint with \ref CStd (23.2.3.3)
|
||||
* The only extension we have is that we allow access
|
||||
* to the underlying container via: get_container function.
|
||||
* Check the stack \ref AZStdExamples.
|
||||
*/
|
||||
template<class T, class Container = AZStd::deque<T> >
|
||||
class stack
|
||||
{
|
||||
enum
|
||||
{
|
||||
CONTAINER_VERSION = 1
|
||||
};
|
||||
public:
|
||||
typedef stack<T, Container> this_type;
|
||||
typedef Container container_type;
|
||||
typedef typename Container::value_type value_type;
|
||||
typedef typename Container::size_type size_type;
|
||||
typedef typename Container::reference reference;
|
||||
typedef typename Container::const_reference const_reference;
|
||||
|
||||
AZ_FORCE_INLINE stack() {}
|
||||
AZ_FORCE_INLINE explicit stack(const container_type& container)
|
||||
: m_container(container) {}
|
||||
AZ_FORCE_INLINE bool empty() const { return m_container.empty(); }
|
||||
AZ_FORCE_INLINE size_type size() const { return m_container.size(); }
|
||||
AZ_FORCE_INLINE reference top() { return m_container.back(); }
|
||||
AZ_FORCE_INLINE const_reference top() const { return m_container.back(); }
|
||||
AZ_FORCE_INLINE reference back() { return m_container.back(); }
|
||||
AZ_FORCE_INLINE const_reference back() const { return m_container.back(); }
|
||||
AZ_FORCE_INLINE void push(const value_type& value) { m_container.push_back(value); }
|
||||
AZ_FORCE_INLINE void pop() { m_container.pop_back(); }
|
||||
AZ_FORCE_INLINE void push() { m_container.push_back(); }
|
||||
|
||||
AZ_FORCE_INLINE stack(this_type&& rhs)
|
||||
: m_container(AZStd::move(rhs.m_container)) {}
|
||||
AZ_FORCE_INLINE explicit stack(Container&& container)
|
||||
: m_container(AZStd::move(container)) {}
|
||||
this_type& operator=(this_type&& rhs) { m_container = AZStd::move(rhs.m_container); return *this; }
|
||||
void push(value_type&& value) { m_container.push_back(AZStd::move(value)); }
|
||||
template<class Args>
|
||||
void emplace(Args&& args) { m_container.emplace_back(AZStd::forward<Args>(args)); }
|
||||
void swap(this_type&& rhs) { m_container.swap(AZStd::move(rhs.m_container)); }
|
||||
|
||||
void swap(this_type& rhs) { AZStd::swap(m_container, rhs.m_container); }
|
||||
|
||||
AZ_FORCE_INLINE Container& get_container() { return m_container; }
|
||||
AZ_FORCE_INLINE const Container& get_container() const { return m_container; }
|
||||
|
||||
protected:
|
||||
Container m_container;
|
||||
};
|
||||
|
||||
// queue TEMPLATE FUNCTIONS
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator==(const AZStd::stack<T, Container>& left, const AZStd::stack<T, Container>& right)
|
||||
{
|
||||
return left.get_container() == right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator!=(const AZStd::stack<T, Container>& left, const AZStd::stack<T, Container>& right)
|
||||
{
|
||||
return left.get_container() != right.get_container();
|
||||
}
|
||||
|
||||
/* template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator<(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() < right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator>(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() > right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE operator<=(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() <= right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator>=(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() >= right.get_container();
|
||||
}*/
|
||||
template<class T, class Container = AZStd::deque<T>>
|
||||
using stack = std::stack<T, Container>;
|
||||
}
|
||||
|
||||
#endif // AZSTD_STACK_H
|
||||
#pragma once
|
||||
|
||||
@@ -298,7 +298,7 @@ namespace UnitTest
|
||||
AZ_TEST_ASSERT(int_queue.empty());
|
||||
AZ_TEST_ASSERT(int_queue.size() == 0);
|
||||
|
||||
// Queue uses deque as default container, so try to contruct to queue from a deque.
|
||||
// Queue uses deque as default container, so try to construct to queue from a deque.
|
||||
deque<int> container(40, 10);
|
||||
int_queue_type int_queue2(container);
|
||||
AZ_TEST_ASSERT(!int_queue2.empty());
|
||||
@@ -324,7 +324,7 @@ namespace UnitTest
|
||||
AZ_TEST_ASSERT(int_queue2.size() == 40);
|
||||
AZ_TEST_ASSERT(int_queue2.back() == 20);
|
||||
|
||||
int_queue.push();
|
||||
int_queue.emplace();
|
||||
AZ_TEST_ASSERT(!int_queue.empty());
|
||||
AZ_TEST_ASSERT(int_queue.size() == 1);
|
||||
|
||||
@@ -423,7 +423,7 @@ namespace UnitTest
|
||||
AZ_TEST_ASSERT(int_stack2.size() == 40);
|
||||
AZ_TEST_ASSERT(int_stack2.top() == 10);
|
||||
|
||||
int_stack.push();
|
||||
int_stack.emplace();
|
||||
AZ_TEST_ASSERT(!int_stack.empty());
|
||||
AZ_TEST_ASSERT(int_stack.size() == 1);
|
||||
// StackContainerTest-End
|
||||
@@ -669,4 +669,19 @@ namespace UnitTest
|
||||
++iteration;
|
||||
}
|
||||
}
|
||||
|
||||
using StackContainerTestFixture = ScopedAllocatorSetupFixture;
|
||||
|
||||
TEST_F(StackContainerTestFixture, StackEmplaceOperator_SupportsZeroOrMoreArguments)
|
||||
{
|
||||
using TestPairType = AZStd::pair<int, int>;
|
||||
AZStd::stack<TestPairType> testStack;
|
||||
testStack.emplace();
|
||||
testStack.emplace(1);
|
||||
testStack.emplace(2, 3);
|
||||
|
||||
using ContainerType = typename AZStd::stack<TestPairType>::container_type;
|
||||
AZStd::stack<TestPairType> expectedStack(ContainerType{ TestPairType{ 0, 0 }, TestPairType{ 1, 0 }, TestPairType{ 2, 3 } });
|
||||
EXPECT_EQ(expectedStack, testStack);
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -123,6 +123,12 @@ namespace AzPhysics
|
||||
->Field("Kinematic", &RigidBodyConfiguration::m_kinematic)
|
||||
->Field("CCD Enabled", &RigidBodyConfiguration::m_ccdEnabled)
|
||||
->Field("Compute Mass", &RigidBodyConfiguration::m_computeMass)
|
||||
->Field("Lock Linear X", &RigidBodyConfiguration::m_lockLinearX)
|
||||
->Field("Lock Linear Y", &RigidBodyConfiguration::m_lockLinearY)
|
||||
->Field("Lock Linear Z", &RigidBodyConfiguration::m_lockLinearZ)
|
||||
->Field("Lock Angular X", &RigidBodyConfiguration::m_lockAngularX)
|
||||
->Field("Lock Angular Y", &RigidBodyConfiguration::m_lockAngularY)
|
||||
->Field("Lock Angular Z", &RigidBodyConfiguration::m_lockAngularZ)
|
||||
->Field("Mass", &RigidBodyConfiguration::m_mass)
|
||||
->Field("Compute COM", &RigidBodyConfiguration::m_computeCenterOfMass)
|
||||
->Field("Centre of mass offset", &RigidBodyConfiguration::m_centerOfMassOffset)
|
||||
|
||||
@@ -62,6 +62,16 @@ namespace AzPhysics
|
||||
bool m_computeInertiaTensor = true;
|
||||
bool m_computeMass = true;
|
||||
|
||||
// Flags to restrict motion along specific world-space axes.
|
||||
bool m_lockLinearX = false;
|
||||
bool m_lockLinearY = false;
|
||||
bool m_lockLinearZ = false;
|
||||
|
||||
// Flags to restrict rotation around specific world-space axes.
|
||||
bool m_lockAngularX = false;
|
||||
bool m_lockAngularY = false;
|
||||
bool m_lockAngularZ = false;
|
||||
|
||||
//! If set, non-simulated shapes will also be included in the mass properties calculation.
|
||||
bool m_includeAllShapesInMassCalculation = false;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace UnitTest
|
||||
|
||||
void TestDebugDisplayRequests::DrawWireBox(const AZ::Vector3& min, const AZ::Vector3& max)
|
||||
{
|
||||
const AZ::Transform& tm = m_transforms.back();
|
||||
const AZ::Transform& tm = m_transforms.top();
|
||||
m_points.push_back(tm.TransformPoint(AZ::Vector3(min.GetX(), min.GetY(), min.GetZ())));
|
||||
m_points.push_back(tm.TransformPoint(AZ::Vector3(min.GetX(), min.GetY(), max.GetZ())));
|
||||
m_points.push_back(tm.TransformPoint(AZ::Vector3(min.GetX(), max.GetY(), min.GetZ())));
|
||||
@@ -50,7 +50,7 @@ namespace UnitTest
|
||||
|
||||
void TestDebugDisplayRequests::DrawWireQuad(float width, float height)
|
||||
{
|
||||
const AZ::Transform& tm = m_transforms.back();
|
||||
const AZ::Transform& tm = m_transforms.top();
|
||||
m_points.push_back(tm.TransformPoint(AZ::Vector3(-0.5f * width, 0.0f, -0.5f * height)));
|
||||
m_points.push_back(tm.TransformPoint(AZ::Vector3(-0.5f * width, 0.0f, 0.5f * height)));
|
||||
m_points.push_back(tm.TransformPoint(AZ::Vector3(0.5f * width, 0.0f, -0.5f * height)));
|
||||
@@ -64,7 +64,7 @@ namespace UnitTest
|
||||
|
||||
void TestDebugDisplayRequests::DrawPoints(const AZStd::vector<AZ::Vector3>& points)
|
||||
{
|
||||
const AZ::Transform& tm = m_transforms.back();
|
||||
const AZ::Transform& tm = m_transforms.top();
|
||||
for (const auto& point : points)
|
||||
{
|
||||
m_points.push_back(tm.TransformPoint(point));
|
||||
@@ -100,7 +100,7 @@ namespace UnitTest
|
||||
|
||||
void TestDebugDisplayRequests::PushMatrix(const AZ::Transform& tm)
|
||||
{
|
||||
m_transforms.push(m_transforms.back() * tm);
|
||||
m_transforms.push(m_transforms.top() * tm);
|
||||
}
|
||||
|
||||
void TestDebugDisplayRequests::PopMatrix()
|
||||
|
||||
@@ -481,7 +481,7 @@ namespace AzFramework
|
||||
if (!m_freeOctreeNodes.empty())
|
||||
{
|
||||
// Take a free block of child nodes from our free list
|
||||
ExtractPageAndOffsetFromIndex(m_freeOctreeNodes.back(), nextChildPage, nextChildOffset);
|
||||
ExtractPageAndOffsetFromIndex(m_freeOctreeNodes.top(), nextChildPage, nextChildOffset);
|
||||
m_freeOctreeNodes.pop();
|
||||
}
|
||||
else
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace AzFramework
|
||||
class LinuxXcbConnectionManager
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(LinuxXcbConnectionManager, "{649951316-3626-4C9D-9DCA-2E7ABF84C0A9}");
|
||||
AZ_RTTI(LinuxXcbConnectionManager, "{1F756E14-8D74-42FD-843C-4863307710DB}");
|
||||
|
||||
virtual ~LinuxXcbConnectionManager() = default;
|
||||
|
||||
|
||||
+1
-1
@@ -177,7 +177,7 @@ namespace AzToolsFramework
|
||||
PrefabDomUtils::ApplyPatches(templateDomReference, templateDomReference.GetAllocator(), providedPatch);
|
||||
|
||||
//trigger propagation
|
||||
if (result.GetOutcome() != AZ::JsonSerializationResult::Outcomes::Success)
|
||||
if (result.GetProcessing() != AZ::JsonSerializationResult::Processing::Completed)
|
||||
{
|
||||
AZ_Error("Prefab", false, "Patch was not successfully applied.");
|
||||
return false;
|
||||
|
||||
@@ -90,10 +90,11 @@ namespace AzToolsFramework
|
||||
AZStd::unordered_map<Instance*, PrefabDom> nestedInstanceLinkPatchesMap;
|
||||
|
||||
// Retrieve all entities affected and identify Instances
|
||||
if (!RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonRootEntityOwningInstance->get(), entities, instances))
|
||||
PrefabOperationResult retrieveEntitiesAndInstancesOutcome = RetrieveAndSortPrefabEntitiesAndInstances(
|
||||
inputEntityList, commonRootEntityOwningInstance->get(), entities, instances);
|
||||
if (!retrieveEntitiesAndInstancesOutcome.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(
|
||||
AZStd::string("Could not create a new prefab out of the entities provided - invalid selection."));
|
||||
return retrieveEntitiesAndInstancesOutcome;
|
||||
}
|
||||
|
||||
AZStd::unordered_map<AZ::EntityId, AZStd::string> oldEntityAliases;
|
||||
@@ -646,7 +647,12 @@ namespace AzToolsFramework
|
||||
{
|
||||
// Retrieve all nested instances that are part of the subtree under the current entity.
|
||||
EntityList entities;
|
||||
RetrieveAndSortPrefabEntitiesAndInstances({ entity }, beforeOwningInstance->get(), entities, instancesInvolved);
|
||||
PrefabOperationResult retrieveEntitiesAndInstancesOutcome = RetrieveAndSortPrefabEntitiesAndInstances(
|
||||
{ entity }, beforeOwningInstance->get(), entities, instancesInvolved);
|
||||
if (!retrieveEntitiesAndInstancesOutcome.IsSuccess())
|
||||
{
|
||||
return retrieveEntitiesAndInstancesOutcome;
|
||||
}
|
||||
}
|
||||
|
||||
for (Instance* instance : instancesInvolved)
|
||||
@@ -748,7 +754,9 @@ namespace AzToolsFramework
|
||||
AZStd::vector<Instance*> instances;
|
||||
|
||||
// Retrieve all descendant entities and instances of this entity that belonged to the same owning instance.
|
||||
RetrieveAndSortPrefabEntitiesAndInstances({ entity }, beforeOwningInstance->get(), entities, instances);
|
||||
PrefabOperationResult retrieveEntitiesAndInstancesOutcome = RetrieveAndSortPrefabEntitiesAndInstances(
|
||||
{ entity }, beforeOwningInstance->get(), entities, instances);
|
||||
AZ_Error("Prefab", retrieveEntitiesAndInstancesOutcome.IsSuccess(), retrieveEntitiesAndInstancesOutcome.GetError().data());
|
||||
|
||||
AZStd::vector<AZStd::unique_ptr<Instance>> instanceUniquePtrs;
|
||||
AZStd::vector<AZStd::pair<Instance*, PrefabDom>> instancePatches;
|
||||
@@ -981,11 +989,12 @@ namespace AzToolsFramework
|
||||
AZStd::vector<Instance*> instances;
|
||||
|
||||
EntityList inputEntityList = EntityIdSetToEntityList(duplicationSet);
|
||||
bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entities, instances);
|
||||
PrefabOperationResult retrieveEntitiesAndInstancesOutcome =
|
||||
RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entities, instances);
|
||||
|
||||
if (!success)
|
||||
if (!retrieveEntitiesAndInstancesOutcome.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Failed to retrieve entities and instances from the given list of entity ids for duplication"));
|
||||
return AZStd::move(retrieveEntitiesAndInstancesOutcome);
|
||||
}
|
||||
|
||||
// Take a snapshot of the instance DOM before we manipulate it
|
||||
@@ -1128,11 +1137,12 @@ namespace AzToolsFramework
|
||||
AZStd::vector<AZ::Entity*> entities;
|
||||
AZStd::vector<Instance*> instances;
|
||||
|
||||
bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entities, instances);
|
||||
PrefabOperationResult retrieveEntitiesAndInstancesOutcome =
|
||||
RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entities, instances);
|
||||
|
||||
if (!success)
|
||||
if (!retrieveEntitiesAndInstancesOutcome.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("DeleteEntitiesAndAllDescendantsInInstance"));
|
||||
return AZStd::move(retrieveEntitiesAndInstancesOutcome);
|
||||
}
|
||||
|
||||
for (AZ::Entity* entity : entities)
|
||||
@@ -1405,13 +1415,16 @@ namespace AzToolsFramework
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool PrefabPublicHandler::RetrieveAndSortPrefabEntitiesAndInstances(
|
||||
const EntityList& inputEntities, Instance& commonRootEntityOwningInstance,
|
||||
EntityList& outEntities, AZStd::vector<Instance*>& outInstances) const
|
||||
PrefabOperationResult PrefabPublicHandler::RetrieveAndSortPrefabEntitiesAndInstances(
|
||||
const EntityList& inputEntities,
|
||||
Instance& commonRootEntityOwningInstance,
|
||||
EntityList& outEntities,
|
||||
AZStd::vector<Instance*>& outInstances) const
|
||||
{
|
||||
if (inputEntities.size() == 0)
|
||||
{
|
||||
return false;
|
||||
return AZ::Failure(
|
||||
AZStd::string("An empty list of input entities is provided to retrieve the prefab entities and instances."));
|
||||
}
|
||||
|
||||
AZStd::queue<AZ::Entity*> entityQueue;
|
||||
@@ -1438,8 +1451,8 @@ namespace AzToolsFramework
|
||||
AZ_Assert(
|
||||
owningInstance.has_value(),
|
||||
"An error occurred while retrieving entities and prefab instances : "
|
||||
"Owning instance of entity with id '%llu' couldn't be found",
|
||||
entity->GetId());
|
||||
"Owning instance of entity with name '%s' and id '%llu' couldn't be found",
|
||||
entity->GetName().c_str(), static_cast<AZ::u64>(entity->GetId()));
|
||||
|
||||
// Check if this entity is owned by the same instance owning the root.
|
||||
if (&owningInstance->get() == &commonRootEntityOwningInstance)
|
||||
@@ -1480,7 +1493,10 @@ namespace AzToolsFramework
|
||||
else
|
||||
{
|
||||
// This can only happen if one entity does not share the common root!
|
||||
return false;
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Entity with name '%s' and id '%llu' has an owning instance that doesn't belong to the instance "
|
||||
"hierarchy of the selected entities.",
|
||||
entity->GetName().c_str(), static_cast<AZ::u64>(entity->GetId())));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1501,7 +1517,12 @@ namespace AzToolsFramework
|
||||
outInstances.push_back(instancePtr);
|
||||
}
|
||||
|
||||
return (outEntities.size() + outInstances.size()) > 0;
|
||||
if ((outEntities.size() + outInstances.size()) == 0)
|
||||
{
|
||||
return AZ::Failure(
|
||||
AZStd::string("An empty list of entities and prefab instances were retrieved from the selected entities"));
|
||||
}
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
EntityIdList PrefabPublicHandler::GenerateEntityIdListWithoutLevelInstance(
|
||||
|
||||
@@ -64,8 +64,11 @@ namespace AzToolsFramework
|
||||
|
||||
private:
|
||||
PrefabOperationResult DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants);
|
||||
bool RetrieveAndSortPrefabEntitiesAndInstances(const EntityList& inputEntities, Instance& commonRootEntityOwningInstance,
|
||||
EntityList& outEntities, AZStd::vector<Instance*>& outInstances) const;
|
||||
PrefabOperationResult RetrieveAndSortPrefabEntitiesAndInstances(
|
||||
const EntityList& inputEntities,
|
||||
Instance& commonRootEntityOwningInstance,
|
||||
EntityList& outEntities,
|
||||
AZStd::vector<Instance*>& outInstances) const;
|
||||
EntityIdList GenerateEntityIdListWithoutLevelInstance(const EntityIdList& entityIds) const;
|
||||
|
||||
InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const;
|
||||
|
||||
+4
-3
@@ -546,7 +546,7 @@ namespace AzToolsFramework
|
||||
|
||||
for (auto& element : nodeEditData->m_elements)
|
||||
{
|
||||
if (element.IsClassElement() && element.m_elementId == AZ::Edit::ClassElements::Group)
|
||||
if (element.m_elementId == AZ::Edit::ClassElements::Group)
|
||||
{
|
||||
groupData = (element.m_description && element.m_description[0]) ? &element : nullptr;
|
||||
continue;
|
||||
@@ -1112,13 +1112,14 @@ namespace AzToolsFramework
|
||||
const AZ::Edit::ElementData* groupData = nullptr;
|
||||
for (const AZ::Edit::ElementData& elementData : parentEditData->m_elements)
|
||||
{
|
||||
if (node->m_elementEditData == &elementData) // this element matches this node
|
||||
// this element matches this node
|
||||
if ((node->m_elementEditData == &elementData) && (elementData.m_elementId != AZ::Edit::ClassElements::Group))
|
||||
{
|
||||
// Record the last found group data
|
||||
node->m_groupElementData = groupData;
|
||||
break;
|
||||
}
|
||||
else if (elementData.IsClassElement() && elementData.m_elementId == AZ::Edit::ClassElements::Group)
|
||||
else if (elementData.m_elementId == AZ::Edit::ClassElements::Group)
|
||||
{
|
||||
if (!elementData.m_description || !elementData.m_description[0])
|
||||
{ // close the group
|
||||
|
||||
+38
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
|
||||
#include <AzToolsFramework/UI/PropertyEditor/PropertyCheckBoxCtrl.hxx>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4244 4251 4800, "-Wunknown-warning-option") // 4244: conversion from 'int' to 'float', possible loss of data
|
||||
// 4251: class '...' needs to have dll-interface to be used by clients of class 'QInputEvent'
|
||||
@@ -141,6 +142,11 @@ namespace AzToolsFramework
|
||||
m_treeDepth = 0;
|
||||
|
||||
delete m_dropDownArrow;
|
||||
if (m_toggleSwitch != nullptr)
|
||||
{
|
||||
m_handler->DestroyGUI(m_toggleSwitch);
|
||||
m_toggleSwitch = nullptr;
|
||||
}
|
||||
|
||||
if (m_childWidget)
|
||||
{
|
||||
@@ -387,6 +393,13 @@ namespace AzToolsFramework
|
||||
setUpdatesEnabled(true);
|
||||
}
|
||||
|
||||
void PropertyRowWidget::InitializeToggleGroup(const char* groupName, PropertyRowWidget* pParent, int depth, InstanceDataNode* node, int labelWidth)
|
||||
{
|
||||
Initialize(groupName, pParent, depth, labelWidth);
|
||||
ChangeSourceNode(node);
|
||||
CreateGroupToggleSwitch();
|
||||
}
|
||||
|
||||
void PropertyRowWidget::Initialize(const char* groupName, PropertyRowWidget* pParent, int depth, int labelWidth)
|
||||
{
|
||||
Initialize(pParent, nullptr, depth, labelWidth);
|
||||
@@ -1102,6 +1115,19 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyRowWidget::CreateGroupToggleSwitch()
|
||||
{
|
||||
if (m_toggleSwitch == nullptr)
|
||||
{
|
||||
m_handlerName = AZ::Edit::UIHandlers::CheckBox;
|
||||
PropertyTypeRegistrationMessages::Bus::BroadcastResult(m_handler, &PropertyTypeRegistrationMessages::Bus::Events::ResolvePropertyHandler, m_handlerName, azrtti_typeid<bool>());
|
||||
m_toggleSwitch = m_handler->CreateGUI(this);
|
||||
m_middleLayout->insertWidget(0, m_toggleSwitch, 1);
|
||||
auto checkBoxCtrl = static_cast<AzToolsFramework::PropertyCheckBoxCtrl*>(m_toggleSwitch);
|
||||
QObject::connect(checkBoxCtrl, &AzToolsFramework::PropertyCheckBoxCtrl::valueChanged, this, &PropertyRowWidget::OnClickedToggleButton);
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyRowWidget::SetIndentSize(int w)
|
||||
{
|
||||
m_indent->changeSize(w, 1, QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
@@ -1110,6 +1136,18 @@ namespace AzToolsFramework
|
||||
m_leftHandSideLayout->activate();
|
||||
}
|
||||
|
||||
void PropertyRowWidget::OnClickedToggleButton(bool checked)
|
||||
{
|
||||
if (m_expanded != checked)
|
||||
{
|
||||
DoExpandOrContract(!IsExpanded(), 0 != (QGuiApplication::keyboardModifiers() & Qt::ControlModifier));
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyRowWidget::ChangeSourceNode(InstanceDataNode* node)
|
||||
{
|
||||
m_sourceNode = node;
|
||||
}
|
||||
|
||||
void PropertyRowWidget::SetExpanded(bool expanded)
|
||||
{
|
||||
|
||||
+9
@@ -50,6 +50,7 @@ namespace AzToolsFramework
|
||||
|
||||
virtual void Initialize(PropertyRowWidget* pParent, InstanceDataNode* dataNode, int depth, int labelWidth = 200);
|
||||
virtual void Initialize(const char* groupName, PropertyRowWidget* pParent, int depth, int labelWidth = 200);
|
||||
virtual void InitializeToggleGroup(const char* groupName, PropertyRowWidget* pParent, int depth, InstanceDataNode* node, int labelWidth = 200);
|
||||
virtual void Clear(); // for pooling
|
||||
|
||||
// --- NOT A UNIQUE IDENTIFIER ---
|
||||
@@ -143,11 +144,14 @@ namespace AzToolsFramework
|
||||
QVBoxLayout* GetLeftHandSideLayoutParent() { return m_leftHandSideLayoutParent; }
|
||||
QToolButton* GetIndicatorButton() { return m_indicatorButton; }
|
||||
QLabel* GetNameLabel() { return m_nameLabel; }
|
||||
QWidget* GetToggle() { return m_toggleSwitch; }
|
||||
const QWidget* GetToggle() const { return m_toggleSwitch; }
|
||||
void SetIndentSize(int w);
|
||||
void SetAsCustom(bool custom) { m_custom = custom; }
|
||||
|
||||
bool CanChildrenBeReordered() const;
|
||||
bool CanBeReordered() const;
|
||||
|
||||
protected:
|
||||
int CalculateLabelWidth() const;
|
||||
|
||||
@@ -177,6 +181,8 @@ namespace AzToolsFramework
|
||||
QLabel* m_defaultLabel; // if there is no handler, we use a m_defaultLabel label
|
||||
InstanceDataNode* m_sourceNode;
|
||||
|
||||
QWidget* m_toggleSwitch = nullptr;
|
||||
|
||||
QString m_currentFilterString;
|
||||
|
||||
struct ChangeNotification
|
||||
@@ -241,6 +247,8 @@ namespace AzToolsFramework
|
||||
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
||||
|
||||
void UpdateDropDownArrow();
|
||||
void CreateGroupToggleSwitch();
|
||||
void ChangeSourceNode(InstanceDataNode* node);
|
||||
void UpdateDefaultLabel(InstanceDataNode* node);
|
||||
|
||||
void createContainerButtons();
|
||||
@@ -259,6 +267,7 @@ namespace AzToolsFramework
|
||||
private slots:
|
||||
void OnClickedExpansionButton();
|
||||
|
||||
void OnClickedToggleButton(bool checked);
|
||||
void OnClickedAddElementButton();
|
||||
void OnClickedRemoveElementButton();
|
||||
void OnClickedClearContainerButton();
|
||||
|
||||
+65
-30
@@ -169,6 +169,8 @@ namespace AzToolsFramework
|
||||
InstanceDataHierarchyList m_instances; ///< List of instance sets to display, other one can aggregate other instances.
|
||||
InstanceDataHierarchy::ValueComparisonFunction m_valueComparisonFunction;
|
||||
ReflectedPropertyEditor::WidgetList m_widgets;
|
||||
ReflectedPropertyEditor::WidgetList m_specialGroupWidgets;
|
||||
InstanceDataNode* groupSourceNode = nullptr;
|
||||
RowContainerType m_widgetsInDisplayOrder;
|
||||
UserWidgetToDataMap m_userWidgetsToData;
|
||||
VisibilityCallback m_visibilityCallback;
|
||||
@@ -501,6 +503,7 @@ namespace AzToolsFramework
|
||||
// if the node is in a group then create the widget for the group
|
||||
if (groupElementData)
|
||||
{
|
||||
bool isToggleGroup = false;
|
||||
const char* groupName = groupElementData->m_description;
|
||||
PropertyRowWidget*& widgetEntry = m_groupWidgets[{parent, groupName}];
|
||||
|
||||
@@ -509,14 +512,34 @@ namespace AzToolsFramework
|
||||
{
|
||||
widgetEntry = CreateOrPullFromPool();
|
||||
widgetEntry->SetFilterString(m_editor->GetFilterString());
|
||||
widgetEntry->Initialize(groupName, parent, depth, m_propertyLabelWidth);
|
||||
|
||||
// Initialized normally if the group does not have a member variable attached to it,
|
||||
// otherwise initialize it as a group that will have a toggle switch.
|
||||
if (groupElementData->IsClassElement())
|
||||
{
|
||||
widgetEntry->Initialize(groupName, parent, depth, m_propertyLabelWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
widgetEntry->InitializeToggleGroup(groupName, parent, depth, groupSourceNode, m_propertyLabelWidth);
|
||||
QWidget* toggleSwitch = widgetEntry->GetToggle();
|
||||
PropertyHandlerBase* pHandler = widgetEntry->GetHandler();
|
||||
m_userWidgetsToData[toggleSwitch] = groupSourceNode;
|
||||
m_specialGroupWidgets[groupSourceNode] = widgetEntry;
|
||||
pHandler->ConsumeAttributes_Internal(toggleSwitch, groupSourceNode);
|
||||
pHandler->ReadValuesIntoGUI_Internal(toggleSwitch, groupSourceNode);
|
||||
widgetEntry->OnValuesUpdated();
|
||||
isToggleGroup = true;
|
||||
}
|
||||
|
||||
widgetEntry->SetLeafIndentation(m_leafIndentation);
|
||||
widgetEntry->SetTreeIndentation(m_treeIndentation);
|
||||
widgetEntry->setObjectName(groupName);
|
||||
|
||||
for (const AZ::Edit::AttributePair& attribute : groupElementData->m_attributes)
|
||||
{
|
||||
PropertyAttributeReader reader(node->GetParent()->FirstInstance(), attribute.second);
|
||||
InstanceDataNode* readerNode = (isToggleGroup) ? groupSourceNode : node;
|
||||
PropertyAttributeReader reader(readerNode->GetParent()->FirstInstance(), attribute.second);
|
||||
QString descriptionOut;
|
||||
bool foundDescription = false;
|
||||
widgetEntry->ConsumeAttribute(attribute.first, reader, true, &descriptionOut, &foundDescription);
|
||||
@@ -608,7 +631,7 @@ namespace AzToolsFramework
|
||||
// creates and populates the GUI to edit the property if not already created
|
||||
void ReflectedPropertyEditor::Impl::CreateEditorWidget(PropertyRowWidget* pWidget)
|
||||
{
|
||||
if (!pWidget->HasChildWidgetAlready())
|
||||
if (!pWidget->HasChildWidgetAlready() && !pWidget->GetToggle())
|
||||
{
|
||||
PropertyHandlerBase* pHandler = pWidget->GetHandler();
|
||||
if (pHandler)
|
||||
@@ -735,36 +758,44 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pWidget = CreateOrPullFromPool();
|
||||
pWidget->show();
|
||||
|
||||
pWidget->SetFilterString(m_editor->GetFilterString());
|
||||
pWidget->Initialize(pParent, node, depth, m_propertyLabelWidth);
|
||||
|
||||
if (labelOverride != "")
|
||||
if (!node->GetElementEditMetadata() || (node->GetElementEditMetadata()->m_elementId != AZ::Edit::ClassElements::Group))
|
||||
{
|
||||
pWidget->SetNameLabel(labelOverride.data());
|
||||
pWidget = CreateOrPullFromPool();
|
||||
pWidget->show();
|
||||
|
||||
pWidget->SetFilterString(m_editor->GetFilterString());
|
||||
pWidget->Initialize(pParent, node, depth, m_propertyLabelWidth);
|
||||
|
||||
if (labelOverride != "")
|
||||
{
|
||||
pWidget->SetNameLabel(labelOverride.data());
|
||||
}
|
||||
|
||||
pWidget->setObjectName(pWidget->label());
|
||||
pWidget->SetSelectionEnabled(m_selectionEnabled);
|
||||
pWidget->SetLeafIndentation(m_leafIndentation);
|
||||
pWidget->SetTreeIndentation(m_treeIndentation);
|
||||
|
||||
m_widgets[node] = pWidget;
|
||||
m_widgetsInDisplayOrder.insert(widgetDisplayOrder, pWidget);
|
||||
|
||||
if (pParent)
|
||||
{
|
||||
pParent->AddedChild(pWidget);
|
||||
}
|
||||
|
||||
if (pParent || !m_hideRootProperties)
|
||||
{
|
||||
depth += 1;
|
||||
}
|
||||
pParent = pWidget;
|
||||
}
|
||||
|
||||
pWidget->setObjectName(pWidget->label());
|
||||
pWidget->SetSelectionEnabled(m_selectionEnabled);
|
||||
pWidget->SetLeafIndentation(m_leafIndentation);
|
||||
pWidget->SetTreeIndentation(m_treeIndentation);
|
||||
|
||||
m_widgets[node] = pWidget;
|
||||
m_widgetsInDisplayOrder.insert(widgetDisplayOrder, pWidget);
|
||||
|
||||
if (pParent)
|
||||
// Save the last InstanceDataNode that is a Group ClassElement so that we can use it as the source node for its widget.
|
||||
if (node->GetElementEditMetadata() && (node->GetElementEditMetadata()->m_elementId == AZ::Edit::ClassElements::Group))
|
||||
{
|
||||
pParent->AddedChild(pWidget);
|
||||
groupSourceNode = node;
|
||||
}
|
||||
|
||||
if (pParent || !m_hideRootProperties)
|
||||
{
|
||||
depth += 1;
|
||||
}
|
||||
pParent = pWidget;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1356,9 +1387,13 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
// get the property editor
|
||||
// Get the property editor from either the widget map or the special toggle group widgets
|
||||
auto rowWidget = m_widgets.find(it->second);
|
||||
if (rowWidget != m_widgets.end())
|
||||
if (rowWidget == m_widgets.end())
|
||||
{
|
||||
rowWidget = m_specialGroupWidgets.find(it->second);
|
||||
}
|
||||
if (rowWidget != m_widgets.end() || rowWidget != m_specialGroupWidgets.end())
|
||||
{
|
||||
InstanceDataNode* node = rowWidget->first;
|
||||
PropertyRowWidget* widget = rowWidget->second;
|
||||
|
||||
+3
@@ -51,6 +51,8 @@ namespace AzToolsFramework
|
||||
|
||||
typedef AZStd::unordered_map<InstanceDataNode*, PropertyRowWidget*> WidgetList;
|
||||
|
||||
ReflectedPropertyEditor::WidgetList m_specialGroupWidgets;
|
||||
|
||||
ReflectedPropertyEditor(QWidget* pParent);
|
||||
virtual ~ReflectedPropertyEditor();
|
||||
|
||||
@@ -62,6 +64,7 @@ namespace AzToolsFramework
|
||||
bool AddInstance(void* instance, const AZ::Uuid& classId, void* aggregateInstance = nullptr, void* compareInstance = nullptr);
|
||||
void SetCompareInstance(void* instance, const AZ::Uuid& classId);
|
||||
void ClearInstances();
|
||||
void ReadValuesIntoGui(QWidget* widget, InstanceDataNode* node);
|
||||
template<class T>
|
||||
bool AddInstance(T* instance, void* aggregateInstance = nullptr, void* compareInstance = nullptr)
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <AzCore/Serialization/Utils.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <random>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
using namespace AZ;
|
||||
@@ -727,6 +728,153 @@ namespace UnitTest
|
||||
|
||||
};
|
||||
|
||||
class GroupTestComponent : public AZ::Component
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(GroupTestComponent, "{C088C81D-D59D-43F1-85F8-B2E591BABA36}")
|
||||
|
||||
GroupTestComponent() = default;
|
||||
|
||||
struct SubData
|
||||
{
|
||||
AZ_TYPE_INFO(SubData, "{983316B5-17C0-476E-9CEB-CA749B3ABE5D}");
|
||||
AZ_CLASS_ALLOCATOR(SubData, AZ::SystemAllocator, 0);
|
||||
|
||||
SubData() {}
|
||||
explicit SubData(int v) : m_int(v) {}
|
||||
explicit SubData(bool b) : m_bool(b) {}
|
||||
explicit SubData(float f) : m_float(f) {}
|
||||
~SubData() = default;
|
||||
|
||||
float m_float = 0.f;
|
||||
int m_int = 0;
|
||||
bool m_bool = true;
|
||||
};
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<SubData>()
|
||||
->Version(1)
|
||||
->Field("SubInt", &SubData::m_int)
|
||||
->Field("SubToggle", &SubData::m_bool)
|
||||
->Field("SubFloat", &SubData::m_float)
|
||||
;
|
||||
|
||||
serializeContext->Class<GroupTestComponent, AZ::Component>()
|
||||
->Version(1)
|
||||
->Field("Float", &GroupTestComponent::m_float)
|
||||
->Field("GroupToggle", &GroupTestComponent::m_groupToggle)
|
||||
->Field("GroupFloat", &GroupTestComponent::m_groupFloat)
|
||||
->Field("ToggleGroupInt", &GroupTestComponent::m_toggleGroupInt)
|
||||
->Field("SubDataNormal", &GroupTestComponent::m_subGroupForNormal)
|
||||
->Field("SubDataToggle", &GroupTestComponent::m_subGroupForToggle)
|
||||
;
|
||||
|
||||
if (AZ::EditContext* edit = serializeContext->GetEditContext())
|
||||
{
|
||||
edit->Class<GroupTestComponent>("Group Test Component", "Testing normal groups and toggle groups")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->DataElement(0, &GroupTestComponent::m_float, "Float Field", "A float field")
|
||||
->ClassElement(AZ::Edit::ClassElements::Group, "Normal Group")
|
||||
->DataElement(0, &GroupTestComponent::m_groupFloat, "Float Field", "A float field")
|
||||
->DataElement(0, &GroupTestComponent::m_subGroupForNormal, "Struct Field", "A sub data type")
|
||||
->GroupElementToggle("Group Toggle", &GroupTestComponent::m_groupToggle)
|
||||
->DataElement(0, &GroupTestComponent::m_toggleGroupInt, "Normal Integer", "An Integer")
|
||||
->DataElement(0, &GroupTestComponent::m_subGroupForToggle, "Struct Field", "A sub data type")
|
||||
;
|
||||
|
||||
edit->Class<SubData>("SubGroup Test Component", "Testing nested normal groups and toggle groups")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->ClassElement(AZ::Edit::ClassElements::Group, "Normal SubGroup")
|
||||
->DataElement(0, &SubData::m_int, "SubGroup Int Field", "An int")
|
||||
->GroupElementToggle("SubGroup Toggle", &SubData::m_bool)
|
||||
->DataElement(0, &SubData::m_float, "SubGroup Float Field", "An int")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Activate() override
|
||||
{
|
||||
}
|
||||
|
||||
void Deactivate() override
|
||||
{
|
||||
}
|
||||
|
||||
float m_float = 0.f;
|
||||
float m_groupFloat = 0.f;
|
||||
int m_toggleGroupInt = 0;
|
||||
AZStd::string m_string;
|
||||
bool m_groupToggle = false;
|
||||
|
||||
SubData m_subGroupForNormal;
|
||||
SubData m_subGroupForToggle;
|
||||
};
|
||||
|
||||
class InstanceDataHierarchyGroupTestFixture : public AllocatorsFixture
|
||||
{
|
||||
public:
|
||||
InstanceDataHierarchyGroupTestFixture() = default;
|
||||
|
||||
AZStd::unique_ptr<SerializeContext> m_serializeContext;
|
||||
AZStd::unique_ptr<AZ::Entity> testEntity1;
|
||||
AzToolsFramework::InstanceDataHierarchy* instanceDataHierarchy;
|
||||
AzToolsFramework::InstanceDataNode* componentNode1 = nullptr;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
AllocatorsFixture::SetUp();
|
||||
|
||||
using AzToolsFramework::InstanceDataHierarchy;
|
||||
using AzToolsFramework::InstanceDataNode;
|
||||
|
||||
AZ::AllocatorInstance<AZ::PoolAllocator>::Create();
|
||||
|
||||
m_serializeContext.reset(aznew AZ::SerializeContext());
|
||||
m_serializeContext.get()->CreateEditContext();
|
||||
Entity::Reflect(m_serializeContext.get());
|
||||
GroupTestComponent::Reflect(m_serializeContext.get());
|
||||
|
||||
testEntity1.reset(new AZ::Entity());
|
||||
testEntity1->CreateComponent<GroupTestComponent>();
|
||||
|
||||
instanceDataHierarchy = aznew InstanceDataHierarchy();
|
||||
instanceDataHierarchy->AddRootInstance(testEntity1.get());
|
||||
instanceDataHierarchy->Build(m_serializeContext.get(), 0);
|
||||
|
||||
// Adding the nodes to a node stack
|
||||
auto rootNode = instanceDataHierarchy->GetRootNode();
|
||||
AZStd::stack<InstanceDataNode*> nodeStack;
|
||||
nodeStack.push(rootNode);
|
||||
while (!nodeStack.empty())
|
||||
{
|
||||
InstanceDataNode* node = nodeStack.top();
|
||||
nodeStack.pop();
|
||||
if (node->GetClassMetadata()->m_typeId == AZ::AzTypeInfo<GroupTestComponent>::Uuid())
|
||||
{
|
||||
componentNode1 = node;
|
||||
break;
|
||||
}
|
||||
for (InstanceDataNode& child : node->GetChildren())
|
||||
{
|
||||
nodeStack.push(&child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_serializeContext.reset();
|
||||
testEntity1.reset();
|
||||
delete instanceDataHierarchy;
|
||||
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
|
||||
AllocatorsFixture::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
class InstanceDataHierarchyKeyedContainerTest
|
||||
: public AllocatorsFixture
|
||||
{
|
||||
@@ -1315,4 +1463,108 @@ namespace UnitTest
|
||||
run();
|
||||
}
|
||||
|
||||
// Test to validate that the only ClassElement::Group nodes are ToggleGroups
|
||||
TEST_F(InstanceDataHierarchyGroupTestFixture, GroupToggleIsClassElementGroup)
|
||||
{
|
||||
using AzToolsFramework::InstanceDataHierarchy;
|
||||
using AzToolsFramework::InstanceDataNode;
|
||||
|
||||
for (auto child : componentNode1->GetChildren())
|
||||
{
|
||||
AZStd::string childName(child.GetElementMetadata()->m_name);
|
||||
if (childName.compare("GroupToggle") == 0)
|
||||
{
|
||||
EXPECT_EQ(child.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group);
|
||||
}
|
||||
if ((childName.compare("SubDataNormal") == 0) || (childName.compare("SubDataToggle") == 0))
|
||||
{
|
||||
for (auto subChild : child.GetChildren())
|
||||
{
|
||||
childName = subChild.GetElementMetadata()->m_name;
|
||||
if (childName.compare("SubToggle") == 0)
|
||||
{
|
||||
EXPECT_EQ(subChild.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group);
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_NE(subChild.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test to ensure that each node has been assigned under the proper group and the group hierarchy is structured correctly
|
||||
TEST_F(InstanceDataHierarchyGroupTestFixture, ValidatingGroupAndSubGroupHierarchy)
|
||||
{
|
||||
using AzToolsFramework::InstanceDataHierarchy;
|
||||
using AzToolsFramework::InstanceDataNode;
|
||||
|
||||
for (auto child : componentNode1->GetChildren())
|
||||
{
|
||||
AZStd::string childName(child.GetElementMetadata()->m_name);
|
||||
if (childName.compare("GroupFloat") == 0)
|
||||
{
|
||||
EXPECT_EQ(child.GetGroupElementMetadata()->m_description, "Normal Group");
|
||||
}
|
||||
if (childName.compare("ToggleGroupInt") == 0)
|
||||
{
|
||||
EXPECT_EQ(child.GetGroupElementMetadata()->m_description, "Group Toggle");
|
||||
}
|
||||
if ((childName.compare("SubDataNormal") == 0) || (childName.compare("SubDataToggle") == 0))
|
||||
{
|
||||
for (auto subChild : child.GetChildren())
|
||||
{
|
||||
childName = subChild.GetElementMetadata()->m_name;
|
||||
if (childName.compare("SubInt") == 0)
|
||||
{
|
||||
EXPECT_EQ(subChild.GetGroupElementMetadata()->m_description, "Normal SubGroup");
|
||||
}
|
||||
if (childName.compare("SubFloat") == 0)
|
||||
{
|
||||
EXPECT_EQ(subChild.GetGroupElementMetadata()->m_description, "SubGroup Toggle");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InstanceDataHierarchyGroupTestFixtureParameterized
|
||||
: public InstanceDataHierarchyGroupTestFixture
|
||||
, public ::testing::WithParamInterface<const char*>
|
||||
{
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
InstanceDataHierarchyGroupTestFixture,
|
||||
InstanceDataHierarchyGroupTestFixtureParameterized,
|
||||
::testing::Values("GroupFloat", "GroupToggle", "ToggleGroupInt", "SubInt", "SubToggle", "SubFloat"));
|
||||
|
||||
// Test to validate that each node in a group and Subgroup has the correct parent
|
||||
TEST_P(InstanceDataHierarchyGroupTestFixtureParameterized, ValidatingGroupAndSubGroupParents)
|
||||
{
|
||||
using AzToolsFramework::InstanceDataHierarchy;
|
||||
using AzToolsFramework::InstanceDataNode;
|
||||
|
||||
const char* paramName = GetParam();
|
||||
for (auto child : componentNode1->GetChildren())
|
||||
{
|
||||
AZStd::string childName(child.GetElementMetadata()->m_name);
|
||||
if (childName.compare(paramName) == 0)
|
||||
{
|
||||
EXPECT_EQ(child.GetParent()->GetClassMetadata()->m_name, "GroupTestComponent");
|
||||
}
|
||||
if ((childName.compare("SubDataNormal") == 0) || (childName.compare("SubDataToggle") == 0))
|
||||
{
|
||||
for (auto subChild : child.GetChildren())
|
||||
{
|
||||
childName = subChild.GetElementMetadata()->m_name;
|
||||
if (childName.compare(paramName) == 0)
|
||||
{
|
||||
EXPECT_EQ(subChild.GetParent()->GetClassMetadata()->m_name, "SubData");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#
|
||||
#
|
||||
|
||||
add_subdirectory(AzAutoGen)
|
||||
add_subdirectory(AtomCore)
|
||||
add_subdirectory(AzCore)
|
||||
add_subdirectory(AzQtComponents)
|
||||
|
||||
@@ -1688,12 +1688,12 @@ namespace GridMate
|
||||
return; //No connections to update
|
||||
}
|
||||
bool updateRate = false;
|
||||
AZ::u32 minRateBytesPerSecond = m_connByCongestionState.top().m_rate;
|
||||
AZ::u32 minRateBytesPerSecond = m_connByCongestionState.front().m_rate;
|
||||
//const AZ::u32 old = minRateBytesPerSecond; //For debugging
|
||||
|
||||
auto connIt = AZStd::find(m_connByCongestionState.get_container().begin(), m_connByCongestionState.get_container().end(), id);
|
||||
auto connIt = AZStd::find(m_connByCongestionState.begin(), m_connByCongestionState.end(), id);
|
||||
|
||||
if ( connIt == m_connByCongestionState.get_container().end())
|
||||
if ( connIt == m_connByCongestionState.end())
|
||||
{
|
||||
return; //Already disconnected
|
||||
}
|
||||
@@ -1708,11 +1708,11 @@ namespace GridMate
|
||||
|
||||
//If new min or old min increased, rebuild the heap and send an update
|
||||
if (bytesPerSecond < minRateBytesPerSecond
|
||||
|| (id == m_connByCongestionState.top().m_connection && bytesPerSecond > minRateBytesPerSecond))
|
||||
|| (id == m_connByCongestionState.front().m_connection && bytesPerSecond > minRateBytesPerSecond))
|
||||
{
|
||||
updateRate = true;
|
||||
minRateBytesPerSecond = bytesPerSecond;
|
||||
AZStd::make_heap(m_connByCongestionState.get_container().begin(), m_connByCongestionState.get_container().end());
|
||||
AZStd::make_heap(m_connByCongestionState.begin(), m_connByCongestionState.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -459,7 +459,7 @@ namespace GridMate
|
||||
}
|
||||
};
|
||||
static bool k_enableBackPressure;
|
||||
AZStd::priority_queue<RateConnectionPair> m_connByCongestionState; ///< Connections priority queue sorted by congestion window
|
||||
AZStd::vector<RateConnectionPair> m_connByCongestionState; ///< Connections priority queue sorted by congestion window
|
||||
/***
|
||||
* Updates connection's rate in priority and updates send limit
|
||||
*
|
||||
@@ -479,7 +479,9 @@ namespace GridMate
|
||||
}
|
||||
AZ_Assert(carrier, "NULL carrier!");
|
||||
|
||||
m_connByCongestionState.emplace(RateConnectionPair(AZ::u32(1500), id)); //default to 1500Bps (ex 1 Ethernet frame/second minimum)
|
||||
m_connByCongestionState.emplace_back(AZ::u32(1500), id); //default to 1500Bps (ex 1 Ethernet frame/second minimum)
|
||||
// Restore the heap property after pushing back another element
|
||||
AZStd::push_heap(m_connByCongestionState.begin(), m_connByCongestionState.end());
|
||||
}
|
||||
void OnDisconnect(Carrier* carrier, ConnectionID id, CarrierDisconnectReason reason) override
|
||||
{
|
||||
@@ -490,17 +492,17 @@ namespace GridMate
|
||||
}
|
||||
AZ_Assert(carrier, "NULL carrier!");
|
||||
|
||||
auto connIt = AZStd::find(m_connByCongestionState.get_container().begin(), m_connByCongestionState.get_container().end(), id);
|
||||
if (connIt != m_connByCongestionState.get_container().end())
|
||||
auto connIt = AZStd::find(m_connByCongestionState.begin(), m_connByCongestionState.end(), id);
|
||||
if (connIt != m_connByCongestionState.end())
|
||||
{
|
||||
//Since we are using a weakly sorted heap, we need to re-generate when the top is removed
|
||||
bool remake = (connIt == m_connByCongestionState.get_container().begin());
|
||||
bool remake = (connIt == m_connByCongestionState.begin());
|
||||
|
||||
m_connByCongestionState.get_container().erase(connIt);
|
||||
m_connByCongestionState.erase(connIt);
|
||||
|
||||
if (remake)
|
||||
{
|
||||
AZStd::make_heap(m_connByCongestionState.get_container().begin(), m_connByCongestionState.get_container().end());
|
||||
AZStd::make_heap(m_connByCongestionState.begin(), m_connByCongestionState.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3072,6 +3072,7 @@ namespace AssetProcessor
|
||||
|
||||
QElapsedTimer elapsedTimer;
|
||||
elapsedTimer.start();
|
||||
|
||||
for (auto jobIter = m_jobsToProcess.begin(); jobIter != m_jobsToProcess.end();)
|
||||
{
|
||||
JobDetails& job = *jobIter;
|
||||
@@ -3082,7 +3083,7 @@ namespace AssetProcessor
|
||||
jobIter = m_jobsToProcess.erase(jobIter);
|
||||
m_numOfJobsToAnalyze--;
|
||||
|
||||
// Update the remaining job status occasionally
|
||||
// Update the remaining job status occasionally
|
||||
if (elapsedTimer.elapsed() >= MILLISECONDS_BETWEEN_PROCESS_JOBS_STATUS_UPDATE)
|
||||
{
|
||||
Q_EMIT NumRemainingJobsChanged(m_activeFiles.size() + m_filesToExamine.size() + m_numOfJobsToAnalyze);
|
||||
@@ -3102,7 +3103,8 @@ namespace AssetProcessor
|
||||
// Process the first job if no jobs were analyzed.
|
||||
auto jobIter = m_jobsToProcess.begin();
|
||||
JobDetails& job = *jobIter;
|
||||
AZ_Warning(AssetProcessor::DebugChannel, false, " Cyclic job dependency detected. Processing job (%s, %s, %s, %s) to unblock.",
|
||||
AZ_Warning(
|
||||
AssetProcessor::DebugChannel, false, " Cyclic job dependency detected. Processing job (%s, %s, %s, %s) to unblock.",
|
||||
job.m_jobEntry.m_databaseSourceName.toUtf8().data(), job.m_jobEntry.m_jobKey.toUtf8().data(),
|
||||
job.m_jobEntry.m_platformInfo.m_identifier.c_str(), job.m_jobEntry.m_builderGuid.ToString<AZStd::string>().c_str());
|
||||
ProcessJob(job);
|
||||
|
||||
@@ -207,6 +207,11 @@ namespace AssetProcessor
|
||||
//! or a job dependency and we can only resolve these dependencies once all the create jobs are completed.
|
||||
struct JobToProcessEntry
|
||||
{
|
||||
bool operator<(const JobToProcessEntry& other)
|
||||
{
|
||||
return m_sourceFileInfo.m_pathRelativeToScanFolder < other.m_sourceFileInfo.m_pathRelativeToScanFolder;
|
||||
}
|
||||
|
||||
SourceFileInfo m_sourceFileInfo;
|
||||
AZStd::vector<JobDetails> m_jobsToAnalyze;
|
||||
// a vector of pairs of <builder which emitted it, the dependency>
|
||||
|
||||
@@ -244,6 +244,11 @@ namespace AssetProcessor
|
||||
m_jobEntry.m_builderGuid == rhs.m_jobEntry.m_builderGuid);
|
||||
}
|
||||
|
||||
static bool DatabaseSourceLexCompare(const JobDetails& left, const JobDetails& right)
|
||||
{
|
||||
return left.m_jobEntry.m_databaseSourceName <= right.m_jobEntry.m_databaseSourceName;
|
||||
}
|
||||
|
||||
JobDetails() = default;
|
||||
};
|
||||
|
||||
|
||||
@@ -197,10 +197,20 @@ namespace AssetProcessor
|
||||
{
|
||||
return priorityLeft > priorityRight;
|
||||
}
|
||||
|
||||
// Optionally stabilize queue order on the source name.
|
||||
// This is used in automated tests, to allow tests to have a stable
|
||||
// order that jobs with otherwise equal priority run, so tests process
|
||||
// assets in the same order each time they are run.
|
||||
if (m_sortQueueOnDBSourceName)
|
||||
{
|
||||
return leftJob->GetJobEntry().m_databaseSourceName < rightJob->GetJobEntry().m_databaseSourceName;
|
||||
}
|
||||
|
||||
// if we get all the way down here it means we're dealing with two assets which are not
|
||||
// in any compile groups, not a priority platform, not a priority type, priority platform, etc.
|
||||
// we can arrange these any way we want, but must pick at least a stable order.
|
||||
|
||||
return leftJob->GetJobEntry().m_jobRunKey < rightJob->GetJobEntry().m_jobRunKey;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,10 @@ namespace AssetProcessor
|
||||
void AddJobIdEntry(AssetProcessor::RCJob* rcJob);
|
||||
void RemoveJobIdEntry(AssetProcessor::RCJob* rcJob);
|
||||
|
||||
void SetQueueSortOnDBSourceName()
|
||||
{
|
||||
m_sortQueueOnDBSourceName = true;
|
||||
}
|
||||
|
||||
// implement QSortFilteRProxyModel:
|
||||
bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override;
|
||||
@@ -68,6 +72,11 @@ namespace AssetProcessor
|
||||
QSet<QString> m_currentlyConnectedPlatforms;
|
||||
bool m_dirtyNeedsResort = false; // instead of constantly resorting, we resort only when someone wants to pull an element from us
|
||||
|
||||
// By default, jobs with equal priority and escalation sort on the job run key. This flag changes
|
||||
// jobs to sort on the database source name. This is used for testing, to guarantee jobs run in the same
|
||||
// order for those tests each time they are run.
|
||||
bool m_sortQueueOnDBSourceName = false;
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// AssetProcessorPlatformBus::Handler
|
||||
void AssetProcessorPlatformConnected(const AZStd::string platform) override;
|
||||
|
||||
@@ -163,6 +163,11 @@ namespace AssetProcessor
|
||||
return ((!m_RCQueueSortModel.GetNextPendingJob()) && (m_RCJobListModel.jobsInFlight() == 0));
|
||||
}
|
||||
|
||||
void RCController::SetQueueSortOnDBSourceName()
|
||||
{
|
||||
m_RCQueueSortModel.SetQueueSortOnDBSourceName();
|
||||
}
|
||||
|
||||
void RCController::JobSubmitted(JobDetails details)
|
||||
{
|
||||
AssetProcessor::QueueElementID checkFile(details.m_jobEntry.m_databaseSourceName, details.m_jobEntry.m_platformInfo.m_identifier.c_str(), details.m_jobEntry.m_jobKey);
|
||||
|
||||
@@ -54,10 +54,11 @@ namespace AssetProcessor
|
||||
void StartJob(AssetProcessor::RCJob* rcJob);
|
||||
int NumberOfPendingCriticalJobsPerPlatform(QString platform);
|
||||
|
||||
void SetSystemRoot(const QDir& systemRoot);
|
||||
int NumberOfPendingJobsPerPlatform(QString platform);
|
||||
bool IsIdle();
|
||||
bool IsPriorityCopyJob(AssetProcessor::RCJob* rcJob);
|
||||
|
||||
void SetQueueSortOnDBSourceName();
|
||||
|
||||
Q_SIGNALS:
|
||||
void FileCompiled(JobEntry entry, AssetBuilderSDK::ProcessJobResponse response);
|
||||
void FileFailed(JobEntry entry);
|
||||
|
||||
@@ -49,8 +49,6 @@ static const qint64 s_ReservedDiskSpaceInBytes = 256 * 1024;
|
||||
//! Maximum number of temp folders allowed
|
||||
static const int s_MaximumTempFolders = 10000;
|
||||
|
||||
const char AdditionalScanFolders[] = "additionalScanFolders";
|
||||
|
||||
ApplicationManagerBase::ApplicationManagerBase(int* argc, char*** argv, QObject* parent)
|
||||
: ApplicationManager(argc, argv, parent)
|
||||
{
|
||||
@@ -155,55 +153,90 @@ void ApplicationManagerBase::InitAssetProcessorManager()
|
||||
const AzFramework::CommandLine* commandLine = nullptr;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(commandLine, &AzFramework::ApplicationRequests::GetCommandLine);
|
||||
|
||||
if(commandLine->HasSwitch("zeroAnalysisMode"))
|
||||
struct APCommandLineSwitch
|
||||
{
|
||||
APCommandLineSwitch(const char* switchTitle, const char* helpText)
|
||||
: m_switch(switchTitle)
|
||||
, m_helpText(helpText)
|
||||
{
|
||||
|
||||
}
|
||||
const char* m_switch;
|
||||
const char* m_helpText;
|
||||
};
|
||||
|
||||
const APCommandLineSwitch Command_waitOnLaunch("waitOnLaunch", "Briefly pauses Asset Processor during initializiation. Useful if you want to attach a debugger.");
|
||||
const APCommandLineSwitch Command_zeroAnalysisMode("zeroAnalysisMode", "Enables using file modification time when examining source assets for processing.");
|
||||
const APCommandLineSwitch Command_enableQueryLogging("enableQueryLogging", "Enables logging database queries.");
|
||||
const APCommandLineSwitch Command_dependencyScanPattern("dependencyScanPattern", "Scans assets that match the given pattern for missing product dependencies.");
|
||||
const APCommandLineSwitch Command_dsp("dsp", Command_dependencyScanPattern.m_helpText);
|
||||
const APCommandLineSwitch Command_fileDependencyScanPattern("fileDependencyScanPattern", "Used with dependencyScanPattern to farther filter the scan.");
|
||||
const APCommandLineSwitch Command_fdsp("fdsp", Command_fileDependencyScanPattern.m_helpText);
|
||||
const APCommandLineSwitch Command_additionalScanFolders("additionalScanFolders", "Used with dependencyScanPattern to farther filter the scan.");
|
||||
const APCommandLineSwitch Command_dependencyScanMaxIteration("dependencyScanMaxIteration", "Used to limit the number of recursive searches per line when running dependencyScanPattern.");
|
||||
const APCommandLineSwitch Command_warningLevel("warningLevel", "Configure the error and warning reporting level for AssetProcessor. Pass in 1 for fatal errors, 2 for fatal errors and warnings.");
|
||||
const APCommandLineSwitch Command_acceptInput("acceptInput", "Enable external control messaging via the ControlRequestHandler, used with automated tests.");
|
||||
const APCommandLineSwitch Command_debugOutput("debugOutput", "When enabled, builders that support it will output debug information as product assets. Used primarily with scene files.");
|
||||
const APCommandLineSwitch Command_sortJobsByDBSourceName("sortJobsByDBSourceName", "When enabled, sorts pending jobs with equal priority and dependencies by database source name instead of job ID. Useful for automated tests to process assets in the same order each time.");
|
||||
const APCommandLineSwitch Command_truncatefingerprint("truncatefingerprint", "Truncates the fingerprint used for processed assets. Useful if you plan to compress product assets to share on another machine because some compression formats like zip will truncate file mod timestamps.");
|
||||
const APCommandLineSwitch Command_help("help", "Displays this message.");
|
||||
const APCommandLineSwitch Command_h("h", Command_help.m_helpText);
|
||||
|
||||
if (commandLine->HasSwitch(Command_waitOnLaunch.m_switch))
|
||||
{
|
||||
// Useful for attaching the debugger, this forces a short pause.
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::seconds(20));
|
||||
}
|
||||
|
||||
if (commandLine->HasSwitch(Command_zeroAnalysisMode.m_switch))
|
||||
{
|
||||
m_assetProcessorManager->SetEnableModtimeSkippingFeature(true);
|
||||
}
|
||||
|
||||
if(commandLine->HasSwitch("enableQueryLogging"))
|
||||
if (commandLine->HasSwitch(Command_enableQueryLogging.m_switch))
|
||||
{
|
||||
m_assetProcessorManager->SetQueryLogging(true);
|
||||
}
|
||||
|
||||
if (commandLine->HasSwitch("dependencyScanPattern"))
|
||||
if (commandLine->HasSwitch(Command_dependencyScanPattern.m_switch))
|
||||
{
|
||||
m_dependencyScanPattern = commandLine->GetSwitchValue("dependencyScanPattern", 0).c_str();
|
||||
m_dependencyScanPattern = commandLine->GetSwitchValue(Command_dependencyScanPattern.m_switch, 0).c_str();
|
||||
}
|
||||
else if (commandLine->HasSwitch("dsp"))
|
||||
else if (commandLine->HasSwitch(Command_dsp.m_switch))
|
||||
{
|
||||
m_dependencyScanPattern = commandLine->GetSwitchValue("dsp", 0).c_str();
|
||||
m_dependencyScanPattern = commandLine->GetSwitchValue(Command_dsp.m_switch, 0).c_str();
|
||||
}
|
||||
|
||||
m_fileDependencyScanPattern = "*";
|
||||
|
||||
if (commandLine->HasSwitch("fileDependencyScanPattern"))
|
||||
if (commandLine->HasSwitch(Command_fileDependencyScanPattern.m_switch))
|
||||
{
|
||||
m_fileDependencyScanPattern = commandLine->GetSwitchValue("fileDependencyScanPattern", 0).c_str();
|
||||
m_fileDependencyScanPattern = commandLine->GetSwitchValue(Command_fileDependencyScanPattern.m_switch, 0).c_str();
|
||||
}
|
||||
else if (commandLine->HasSwitch("fdsp"))
|
||||
else if (commandLine->HasSwitch(Command_fdsp.m_switch))
|
||||
{
|
||||
m_fileDependencyScanPattern = commandLine->GetSwitchValue("fdsp", 0).c_str();
|
||||
m_fileDependencyScanPattern = commandLine->GetSwitchValue(Command_fdsp.m_switch, 0).c_str();
|
||||
}
|
||||
|
||||
if (commandLine->HasSwitch(AdditionalScanFolders))
|
||||
if (commandLine->HasSwitch(Command_additionalScanFolders.m_switch))
|
||||
{
|
||||
for (size_t idx = 0; idx < commandLine->GetNumSwitchValues(AdditionalScanFolders); idx++)
|
||||
for (size_t idx = 0; idx < commandLine->GetNumSwitchValues(Command_additionalScanFolders.m_switch); idx++)
|
||||
{
|
||||
AZStd::string value = commandLine->GetSwitchValue(AdditionalScanFolders, idx);
|
||||
AZStd::string value = commandLine->GetSwitchValue(Command_additionalScanFolders.m_switch, idx);
|
||||
m_dependencyAddtionalScanFolders.emplace_back(AZStd::move(value));
|
||||
}
|
||||
}
|
||||
|
||||
if (commandLine->HasSwitch("dependencyScanMaxIteration"))
|
||||
if (commandLine->HasSwitch(Command_dependencyScanMaxIteration.m_switch))
|
||||
{
|
||||
AZStd::string maxIterationAsString = commandLine->GetSwitchValue("dependencyScanMaxIteration", 0);
|
||||
AZStd::string maxIterationAsString = commandLine->GetSwitchValue(Command_dependencyScanMaxIteration.m_switch, 0);
|
||||
m_dependencyScanMaxIteration = AZStd::stoi(maxIterationAsString);
|
||||
}
|
||||
|
||||
if (commandLine->HasSwitch("warningLevel"))
|
||||
if (commandLine->HasSwitch(Command_warningLevel.m_switch))
|
||||
{
|
||||
using namespace AssetProcessor;
|
||||
const AZStd::string& levelString = commandLine->GetSwitchValue("warningLevel", 0);
|
||||
const AZStd::string& levelString = commandLine->GetSwitchValue(Command_warningLevel.m_switch, 0);
|
||||
WarningLevel warningLevel = WarningLevel::Default;
|
||||
|
||||
switch(AZStd::stoi(levelString))
|
||||
@@ -217,26 +250,30 @@ void ApplicationManagerBase::InitAssetProcessorManager()
|
||||
}
|
||||
AssetProcessor::JobDiagnosticRequestBus::Broadcast(&AssetProcessor::JobDiagnosticRequestBus::Events::SetWarningLevel, warningLevel);
|
||||
}
|
||||
if (commandLine->HasSwitch("acceptInput"))
|
||||
if (commandLine->HasSwitch(Command_acceptInput.m_switch))
|
||||
{
|
||||
InitControlRequestHandler();
|
||||
}
|
||||
|
||||
if (commandLine->HasSwitch("debugOutput"))
|
||||
if (commandLine->HasSwitch(Command_debugOutput.m_switch))
|
||||
{
|
||||
m_assetProcessorManager->SetBuilderDebugFlag(true);
|
||||
}
|
||||
|
||||
constexpr char truncateFingerprintSwitch[] = "truncatefingerprint";
|
||||
if(commandLine->HasSwitch(truncateFingerprintSwitch))
|
||||
if (commandLine->HasSwitch(Command_sortJobsByDBSourceName.m_switch))
|
||||
{
|
||||
m_sortJobsByDBSourceName = true;
|
||||
}
|
||||
|
||||
if (commandLine->HasSwitch(Command_truncatefingerprint.m_switch))
|
||||
{
|
||||
// Zip archive format uses 2 second precision truncated
|
||||
const int ArchivePrecision = 2000;
|
||||
int precision = ArchivePrecision;
|
||||
|
||||
if(commandLine->GetNumSwitchValues(truncateFingerprintSwitch) > 0)
|
||||
if (commandLine->GetNumSwitchValues(Command_truncatefingerprint.m_switch) > 0)
|
||||
{
|
||||
precision = AZStd::stoi(commandLine->GetSwitchValue(truncateFingerprintSwitch, 0));
|
||||
precision = AZStd::stoi(commandLine->GetSwitchValue(Command_truncatefingerprint.m_switch, 0));
|
||||
|
||||
if(precision < 1)
|
||||
{
|
||||
@@ -246,6 +283,31 @@ void ApplicationManagerBase::InitAssetProcessorManager()
|
||||
|
||||
AssetUtilities::SetTruncateFingerprintTimestamp(precision);
|
||||
}
|
||||
|
||||
if (commandLine->HasSwitch(Command_help.m_switch) || commandLine->HasSwitch(Command_h.m_switch))
|
||||
{
|
||||
// Other O3DE tools have a more full featured system for registering command flags
|
||||
// that includes help output, but right now the AssetProcessor just checks strings
|
||||
// via HasSwitch. This means this help output has to be updated manually.
|
||||
AZ_TracePrintf("AssetProcessor", "Asset Processor Command Line Flags:\n");
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_waitOnLaunch.m_switch, Command_waitOnLaunch.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_zeroAnalysisMode.m_switch, Command_zeroAnalysisMode.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_enableQueryLogging.m_switch, Command_enableQueryLogging.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_dependencyScanPattern.m_switch, Command_dependencyScanPattern.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_dsp.m_switch, Command_dsp.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_fileDependencyScanPattern.m_switch, Command_fileDependencyScanPattern.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_fdsp.m_switch, Command_fdsp.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_additionalScanFolders.m_switch, Command_additionalScanFolders.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_dependencyScanMaxIteration.m_switch, Command_dependencyScanMaxIteration.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_warningLevel.m_switch, Command_warningLevel.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_acceptInput.m_switch, Command_acceptInput.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_debugOutput.m_switch, Command_debugOutput.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_sortJobsByDBSourceName.m_switch, Command_sortJobsByDBSourceName.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_truncatefingerprint.m_switch, Command_truncatefingerprint.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_help.m_switch, Command_help.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\t%s : %s\n", Command_h.m_switch, Command_h.m_helpText);
|
||||
AZ_TracePrintf("AssetProcessor", "\tregset : set the given registry key to the given value.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationManagerBase::Rescan()
|
||||
@@ -281,6 +343,11 @@ void ApplicationManagerBase::InitRCController()
|
||||
{
|
||||
m_rcController = new AssetProcessor::RCController(m_platformConfiguration->GetMinJobs(), m_platformConfiguration->GetMaxJobs());
|
||||
|
||||
if (m_sortJobsByDBSourceName)
|
||||
{
|
||||
m_rcController->SetQueueSortOnDBSourceName();
|
||||
}
|
||||
|
||||
QObject::connect(m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssetToProcess, m_rcController, &AssetProcessor::RCController::JobSubmitted);
|
||||
QObject::connect(m_rcController, &AssetProcessor::RCController::FileCompiled, m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssetProcessed, Qt::UniqueConnection);
|
||||
QObject::connect(m_rcController, &AssetProcessor::RCController::FileFailed, m_assetProcessorManager, &AssetProcessor::AssetProcessorManager::AssetFailed);
|
||||
@@ -1807,4 +1874,3 @@ void ApplicationManagerBase::OnActiveJobsCountChanged(unsigned int count)
|
||||
AssetProcessor::AssetProcessorStatusEntry entry(AssetProcessor::AssetProcessorStatus::Processing_Jobs, count);
|
||||
Q_EMIT AssetProcessorStatusChanged(entry);
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +236,11 @@ protected:
|
||||
int m_remainingAPMJobs = 0;
|
||||
bool m_assetProcessorManagerIsReady = false;
|
||||
|
||||
// When job priority and escalation is equal, jobs sort in order by job key.
|
||||
// This switches that behavior to instead sort by the DB source name, which
|
||||
// allows automated tests to get deterministic behavior out of Asset Processor.
|
||||
bool m_sortJobsByDBSourceName = false;
|
||||
|
||||
unsigned int m_highestConnId = 0;
|
||||
AzToolsFramework::Ticker* m_ticker = nullptr; // for ticking the tickbus.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user