Integrating latest from github/staging

Integrating up through commit 5e1bdae
main
alexpete 5 years ago
parent 9c54341af8
commit 36c4e827bd

@ -1,30 +0,0 @@
*.ilk
*.suo
*.user
*.o
*.temp
*.bootstrap.digests
*.log
*.exp
*.vssettings
*.exportlog
*.mayaSwatches
*.ma.swatches
*.bak
*.bak2
*.akd
Solutions
BinTemp
*.options
*.pyc
*.db
Cache
AssetProcessor_tmp.exe
Builders_Temp
Bin64vc*
$tmp*
.pytest_cache
__pycache__
# Python Module egg file
*.egg
*.egg-link

@ -1,590 +0,0 @@
; ---- Enable/Disable platforms for the entire project. AssetProcessor will automatically add the current platform by default.
; PLATFORM DEFINITIONS
; [Platform (unique identifier)]
; tags=(comma-seperated-tags)
;
; note: the 'identifier' of a platform is the word(s) following the "Platform" keyword (so [Platform pc] means identifier
; is 'pc' for example. This is used to name its assets folder in the cache and should be used in your bootstrap.cfg
; or your main.cpp to choose what assets to load for that particular platform.
; Its primary use is to enable additional non-host platforms (Ios, android...) that are not the current platform.
; note: 'tags' is a comma-seperated list of tags to tag the platform with that builders can inspect to decide what to do.
; while builders can accept any tags you add in order to make decisions, common tags are
; tools - this platform can host the tools and editor and such
; renderer - this platform runs the client engine and renders on a GPU. If missing we could be on a server-only platform
; mobile - a mobile platform such as a set top box or phone with limited resources
; console - a console platform
; server - a server platform of some kind, usually headless, no renderer.
[Platform pc]
tags=tools,renderer,dx12,vulkan
[Platform es3]
tags=android,mobile,renderer,vulkan
[Platform ios]
tags=mobile,renderer,metal
[Platform osx_gl]
tags=tools,renderer,metal
; this is an example of a headless platform that has no renderer.
; To use this you would still need to make sure 'assetplatform' in your startup params in your main() chooses this 'server' platform as your server 'assets' flavor
[Platform server]
tags=server,dx12,vulkan
; this section allows you to turn on various platforms in addition to the host platform you're running on
; 'enabled' is AUTOMATICALLY TRUE for the current platform that you are running on, so it is not necessary to force it to true for that platform
; To enable any additional platform, just uncomment the appropriate line below.
[Platforms]
;pc=enabled
;es3=enabled
;ios=enabled
;osx_gl=enabled
;jasper=enabled
;provo=enabled
;salem=enabled
;server=enabled
; ---- The number of worker jobs, 0 means use the number of Logical Cores
[Jobs]
minJobs=1
maxJobs=0
; cacheServerAddress is the location of the asset server cache.
; Currently for a network share server this would be the absolute file path to the network share folder.
[Server]
;cacheServerAddress=
; ---- add any metadata file type here that needs to be monitored by the AssetProcessor.
; Modifying these meta file will cause the source asset to re-compile again.
; They are specified in the following format
; metadata extension=original extension to replace
; if the metadata extension does not replace the original, then the original can be blank
; so for example if your normal file is blah.tif and your metafile for that file is blah.tif.exportsettings
; then your declaration would be exportsettings= ; ie, it would be blank
; however if your metafile REPLACES the extension (for example, if you have the file blah.i_caf and its metafile is blah.exportsettings)
; then you specify the original extension here to narrow the scope.
; If a relative path to a specific file is provided instead of an extension, a change to the file will change all files
; with the associated extension (e.g. Animations/SkeletonList.xml=i_caf will cause all i_caf files to recompile when
; Animations/SkeletonList.xml within the current game project changes)
[MetaDataTypes]
exportsettings=
animsettings=i_caf
Animations/SkeletonList.xml=i_caf
cbc=abc
fbx.assetinfo=fbx
; ---- add any folders to scan here. The priority order is the order they appear here
; available macros are
; @ROOT@ - the location of engineroot.txt
; @GAMENAME@ - the name of the current game project, for example 'RPGSample'
; note that they are sorted by their 'order' value, and the lower the order the more important an asset is
; lower order numbers override higher ones.
; If specified, output will be prepended to every path found in that recognizer's watch folder.
; Note that you can also make the scan folder platform specific by using the keywords include and exclude.
; Both include and exclude can contain either platform tags, platform identifiers or both.
; if no include is specified, all currently enabled platforms are included by default.
; If includes ARE specified, it will be filtered down by the list of currently enabled platforms.
; [ScanFolder (unique identifier)]
; include = (comma seperated platform tags or identifiers)
; exclude = (comma seperated platform tags or identifiers)
; For example if you want to include a scan folder only for platforms that have the platform tags tools and renderer
; but omit it for platform osx_gl, you will have a scanfolder rule like
; [ScanFolder (unique identifier)]
; watch=@ROOT@/foo
; include = tools, renderer
; exclude = osx_gl
[ScanFolder Game]
watch=@ROOT@/@GAMENAME@
display=@GAMENAME@
recursive=1
order=0
; gems will be auto-added from 100 onwards
[ScanFolder Root]
watch=@ROOT@
recursive=0
order=10000
[ScanFolder Engine]
watch=@ENGINEROOT@/Engine
recursive=1
order=20000
[ScanFolder Editor]
watch=@ENGINEROOT@/Editor
output=editor
recursive=1
order=30000
include=tools,renderer
;Excludes files that match the pattern or glob
; if you use a pattern, remember to escape your backslashes (\\)
[Exclude _LevelBackups]
pattern=.*\\/Levels\\/.*\\/_savebackup\\/.*
[Exclude _LevelAutoBackups]
pattern=.*\\/Levels\\/.*\\/_autobackup\\/.*
[Exclude HoldFiles]
pattern=.*\\/Levels\\/.*_hold\\/.*
[Exclude TempFiles]
; note that $ has meaning to regex, so we escape it.
pattern=.*\\/\\$tmp[0-9]*_.*
[Exclude AlembicCompressionTemplates]
pattern=.*\\/Presets\\/GeomCache\\/.*
[Exclude TmpAnimationCompression]
pattern=.*\\/Editor\\/Tmp\\/AnimationCompression\\/.*
[Exclude EventLog]
pattern=.*\\/Editor\\/.*eventlog\\.xml
[Exclude GameGemsCode]
pattern=.*\\/Gem\\/Code\\/.*
[Exclude GameGemsResources]
pattern=.*\\/Gem\\/Resources\\/.*
[Exclude Private Certs]
pattern=.*\DynamicContent\\/Certificates\\/Private\\/.*
[Exclude CMakeLists]
pattern=.*\\/CMakeLists.txt
[Exclude CMakeFiles]
pattern=.*\\/.*\\.cmake
;------------------------------------------------------------------------------
; Large Worlds Test
;------------------------------------------------------------------------------
[Exclude Work In Progress Folders]
pattern=.*\\/WIP\\/.*
[Exclude Content Source Folders]
pattern=.*\\/CONTENT_SOURCE\\/.*
[Exclude Art Source Folders]
pattern=.*\\/ArtSource\\/.*
;------------------------------------------------------------------------------
; RC params mapping examples
;------------------------------------------------------------------------------
; note that productAssetType is a means of setting the output asset Type (as in AZ::Data::AssetType) of a simple job
; and is the recommended way to specify that a certain kind of file (such as '*.myextension') becomes registered as the
; actual UUID of that type in the engine itself.
; Use a regex for matching files, same params for all platforms
;[RC TGAs]
;pattern=.+\\.tga$
;params=/tga /texture
; Use a glob, have special params for es3 platform
;[RC TIFFs]
;glob=*.tif
;params=/texture
;es3=/pvrt
; You can also modify a version to compile all matching files again
; By default the version is empty
;[RC tif]
;glob=*.tif
;params = \\someparams
;version =1.0
; This will make the AssetProcessor compile all the .tif files again
; you can also optionally supply a priority.
; this is used to sort jobs when no other external circumstance sorts them
; for example, copy jobs will be higher in priority than other jobs that are not copy jobs
; however if they're both copy jobs or both not, and no other circumstances apply, then priority will be used.
; default priority is zero if not specified
; you can specify an option to skip processing for a file type based on the platform.
; for example, if you dont want to process tif files for ios, you can make tif files
; process on any platform except for ios:
;[RC tif]
;glob=*.tif
;params = \\someparams
;ios=skip
; you can specify an option to output product dependencies for a copy job.
; please note that you only need to set this option when cry code is required to parse the asset.
; otherwise product dependencies will be output automatically by the CopyDependencyBuilder.
; for example, if you want to output the product dependencies for font assets:
;[RC font]
;glob=*.font
;params=copy
;outputProductDependencies=true
; you can also specify an option to make all jobs critical that matches some pattern/glob.
; for example, if you want to make all png files critical than set critical to true.
; Note that by default all copy jobs are critical.
; Critical jobs are processed before non critical jobs and also prevent the runtime or editor from starting until they are all complete.
;[RC png]
;glob=*.png
;params = \\someparams
;critical=true
; you can also specify an option to make all the job store in the asset server cache location if you are running AP in server mode.
; For example, if you want to store all png jobs in the asset server cache location including their logs, you can set checkServer = true.
; The client(i.e if you are running AP in non-server mode) will also check for this flag to know which jobs to fetch from the asset server cache location.
; if unsucessful, it will process the job locally as usual.
;[RC png]
;glob=*.png
;params = \\someparams
;critical=true
;checkServer=true
; note that the FULL PATH to the file will be used as the match, not the relative path
; so ensure start your patterns with .* or as appropriate.
; Also, any rules which match will apply - so if you have two rules which both apply to PNG files for example
; but you only want one, you might want to use exclusion patterns:
;Example: process everything EXCEPT the ones in the libs/ui folder with these params
;[RC png-normal]
;pattern=(?!.*libs\\/ui\\/).*\\.png
;params=/imagecompressor=CTSquish /streaming=0
;lockSource=true
;Example: Process everything in the libs/ui folder with linear color space
;[RC png-ui]
;pattern=(.*libs\\/ui\\/).*\\.png
;params=/imagecompressor=CTSquish /streaming=0 /colorspace=linear,linear
;lockSource=true
; More example Regexes:
; pattern=(?!(.*libs\\/ui\\/)|(.*editor\\/).*\\.png
; This pattern will not match anything with editor/ or libs/ui/ in it
; pattern=((.*libs\\/ui\\/)|(.*editor\\/).*\\.png
; This pattern will only match anything with editor/ or libs/ui/ in it
;Give every [Section Name] its own unique Name or else they will overwrite each other!
[RC i_caf]
glob=*.i_caf
params=/cafAlignTracks=1 /animConfigFolder=Animations /skipdba=1 /refresh=1
; force server to send the 'pc' platform to RC.EXE so it compiles the same as PC.
server=/cafAlignTracks=1 /animConfigFolder=Animations /skipdba=1 /refresh=1 /p=pc
priority=5
productAssetType={6023CFF8-FCBA-4528-A8BF-6E0E10B9AB9C}
[RC caf]
glob=*.caf
params=copy
productAssetType={6023CFF8-FCBA-4528-A8BF-6E0E10B9AB9C}
; same as above
[RC mp4]
glob=*.mp4
params=copy
productAssetType={DDFEE0B2-9E5A-412C-8C77-AB100E24C1DF}
[RC mkv]
glob=*.mkv
params=copy
productAssetType={DDFEE0B2-9E5A-412C-8C77-AB100E24C1DF}
; same as above
[RC webm]
glob=*.webm
params=copy
productAssetType={DDFEE0B2-9E5A-412C-8C77-AB100E24C1DF}
; same as above
[RC mov]
glob=*.mov
params=copy
productAssetType={DDFEE0B2-9E5A-412C-8C77-AB100E24C1DF}
; same as above
[RC bk2]
glob=*.bk2
params=copy
productAssetType={BF4879B9-B893-41D2-80E9-24A7BDCD2B50}
[RC img]
glob=*.img
params=copy
[RC dba]
glob=*.dba
params=copy
productAssetType={511562BE-65A5-4538-A5F1-AC685366243E}
version=2
[RC cgf]
glob=*.cgf
params=/VertexPositionFormat=exporter /VertexIndexFormat=u32
; on server, feed rc.exe the param /p=pc to force it to compile assets for server platform in pc format.
server=/VertexPositionFormat=exporter /VertexIndexFormat=u32 /p=pc
lockSource=true
priority=10
; allow CGF files to compile first, so untextured models appear before their textures for faster startup
; other available params: /SplitLODs=1
[RC surfaceTagNameList]
glob=*.surfaceTagNameList
params=copy
productAssetType={A471B2A9-85FC-4993-842D-1881CBC03A2B}
[RC gradImageSettings]
glob=*.gradimagesettings
params=copy
productAssetType={B36FEB5C-41B6-4B58-A212-21EF5AEF523C}
[RC fbx]
glob=*.fbx
; Priority set to 9 so its "before" things like materials but after things like actors and motions (which build using a proper AssetBuilderSDK builder and thus are not in this file)
priority=9
version=5
[RC chr]
glob=*.chr
params=copy
productAssetType={60161B46-21F0-4396-A4F0-F2CCF0664CDE}
version=2
[RC skin]
glob=*.skin
params=copy
[RC cfi]
glob=*.cfi
params=copy
[RC cfx]
glob=*.cfx
params=copy
[RC cfr]
glob=*.cfr
params=copy
; Warning: If you change the VertexIndexFormat, make sure you update the vtx_idx typedef in Code\CryEngine\CryCommon\ProjectDefines.h
[RC abc]
glob=*.abc
params=/SkipFilesWithoutBuildConfig=0 /VertexIndexFormat=u32
console=/SkipFilesWithoutBuildConfig=0 /VertexIndexFormat=u32
mobile=/SkipFilesWithoutBuildConfig=0 /VertexIndexFormat=u16
version=3
server=skip
[RC png-entityicon]
pattern=(.*EntityIcons\\/).*\\.png
productAssetType={3436C30E-E2C5-4C3B-A7B9-66C94A28701B}
params=skip ; only tools-supporting platforms should copy this file. Everyone else can skip.
tools=copy
[RC usm]
glob=*.usm
params=copy
server=skip
[RC animevents]
glob=*.animevents
params=copy
productAssetType={C1D209C1-F81A-4586-A34E-1615995F9F3F}
version=2
[RC adb]
glob=*.adb
params=copy
productAssetType={50908273-CA36-4668-9828-15DD5092F973}
[RC bspace]
glob=*.bspace
params=copy
[RC cdf]
glob=*.cdf
params=copy
productAssetType={DF036C63-9AE6-4AC3-A6AC-8A1D76126C01}
; note - this used to be skinnedMeshAsset but its now Character Definition File specific.
; .skin has its own type.
[RC chrparams]
glob=*.chrparams
params=copy
productAssetType={4BBB785A-6824-4803-A607-F9323E7BEEF1}
version=2
[RC comb]
glob=*.comb
params=copy
[RC dlg]
glob=*.dlg
params=copy
[RC csv]
glob=*.csv
params=copy
[RC json]
glob=*.json
params=copy
[RC lmg]
glob=*.lmg
params=copy
[RC smtl]
glob=*.smtl
params=copy
[RC sub]
glob=*.sub
params=copy
productAssetType={71F9D30E-13F7-40D6-A3C9-E5358004B31F}
version=2
[RC sbsar]
glob=*.sbsar
params=copy
[RC loc.agsxml]
glob=*.loc.agsxml
params=copy
version=1
[RC node]
glob=*.node
params=copy
[RC veg]
glob=*.veg
params=copy
[RC dat]
glob=*.dat
params=copy
[RC lut]
glob=*.lut
params=copy
[RC txt]
pattern=^(?!.*PreloadLibs.txt).*\\.txt
params=copy
[RC cal]
glob=*.cal
params=copy
[RC grp]
glob=*.grp
params=copy
productAssetType={7629EDD3-A361-49A2-B271-252127097D81}
version=2
[RC xls]
glob=*.xls
params=copy
[RC ini]
glob=*.ini
params=copy
[RC ttf]
glob=*.ttf
params=copy
[RC otf]
glob=*.otf
params=copy
[RC ext]
glob=*.ext
params=copy
[RC pak]
; Copy all pak files except level.pak, level.pak has its own builder.
pattern=^((?!\\/level\\.pak).)*\\.pak$
params=copy
[RC ctc]
glob=*.ctc
params=copy
[RC uiprefab]
glob=*.uiprefab
params=copy
server=skip
[RC sprite]
glob=*.sprite
params=copy
server=skip
[RC bin]
glob=*.bin
params=copy
[RC inputbindings]
glob=*.inputbindings
params=copy
productAssetType={25971C7A-26E2-4D08-A146-2EFCC1C36B0C}
[RC physmaterial]
glob=*.physmaterial
params=copy
productAssetType={9E366D8C-33BB-4825-9A1F-FA3ADBE11D0F}
[RC ocm]
glob=*.ocm
params=copy
; Feature tests use the raw .tif files for the golden image comparison
[RC goldenimages]
pattern=.*GoldenImages\\/.*\\.tif
params=copy
server=skip
; Copy over certificates for use with FileDataSource
[RC CertificatePEM]
glob=*.pem
params=copy
; Copy over certificates for use with Dynamic Content
[RC CertificateDER]
glob=*.der
params=copy
[RC PhysXMeshAsset]
glob=*.pxmesh
params=copy
productAssetType={7A2871B9-5EAB-4DE0-A901-B0D2C6920DDB}
; Copy over cooked PhysX heightfield
[RC PhysX HeightField]
glob=*.pxheightfield
params=copy
productAssetType={B61189FE-B2D7-4AF1-8951-CB5C0F7834FC}
[RC filetag]
glob=*.filetag
params=copy
productAssetType={F3BE5CAB-85B7-44B7-9495-863863F6B267}
; Precompiled shader srg
[RC azsrg]
glob=*.azsrg
params=copy
productAssetType={F8C9F4AE-3F6A-45AD-B4FB-0CA415FCC2E1}
; Precompiled shader variant
[RC azshadervariant]
glob=*.azshadervariant
params=copy
productAssetType={9F4D654B-4439-4C61-8DCD-F1C7C5560768}

@ -1,4 +1,23 @@
{
// ---- Enable/Disable platforms for the entire project. AssetProcessor will automatically add the current platform by default.
// PLATFORM DEFINITIONS
// [Platform (unique identifier)]
// tags=(comma-seperated-tags)
//
// note: the 'identifier' of a platform is the word(s) following the "Platform" keyword (so [Platform pc] means identifier
// is 'pc' for example. This is used to name its assets folder in the cache and should be used in your bootstrap.cfg
// or your main.cpp to choose what assets to load for that particular platform.
// Its primary use is to enable additional non-host platforms (Ios, android...) that are not the current platform.
// note: 'tags' is a comma-seperated list of tags to tag the platform with that builders can inspect to decide what to do.
// while builders can accept any tags you add in order to make decisions, common tags are
// tools - this platform can host the tools and editor and such
// renderer - this platform runs the client engine and renders on a GPU. If missing we could be on a server-only platform
// mobile - a mobile platform such as a set top box or phone with limited resources
// console - a console platform
// server - a server platform of some kind, usually headless, no renderer.
"Amazon": {
"AssetProcessor": {
"Settings": {
@ -14,27 +33,82 @@
"Platform osx_gl": {
"tags": "tools,renderer,metal"
},
// this is an example of a headless platform that has no renderer.
// To use this you would still need to make sure 'assetplatform' in your startup params in your main() chooses this 'server' platform as your server 'assets' flavor
"Platform server": {
"tags": "server,dx12,vulkan"
},
// this section allows you to turn on various platforms in addition to the host platform you're running on
// 'enabled' is AUTOMATICALLY TRUE for the current platform that you are running on, so it is not necessary to force it to true for that platform
// To enable any additional platform, just uncomment the appropriate line below.
"Platforms": {
//"pc": "enabled",
//"es3": "enabled",
//"ios": "enabled",
//"osx_gl": "enabled",
//"server": "enabled"
},
// ---- The number of worker jobs, 0 means use the number of Logical Cores
"Jobs": {
"minJobs": 1,
"maxJobs": 0
},
// cacheServerAddress is the location of the asset server cache.
// Currently for a network share server this would be the absolute file path to the network share folder.
"Server": {
//"cacheServerAddress": ""
},
// ---- add any metadata file type here that needs to be monitored by the AssetProcessor.
// Modifying these meta file will cause the source asset to re-compile again.
// They are specified in the following format
// metadata extension=original extension to replace
// if the metadata extension does not replace the original, then the original can be blank
// so for example if your normal file is blah.tif and your metafile for that file is blah.tif.exportsettings
// then your declaration would be exportsettings= ; ie, it would be blank
// however if your metafile REPLACES the extension (for example, if you have the file blah.i_caf and its metafile is blah.exportsettings)
// then you specify the original extension here to narrow the scope.
// If a relative path to a specific file is provided instead of an extension, a change to the file will change all files
// with the associated extension (e.g. Animations/SkeletonList.xml=i_caf will cause all i_caf files to recompile when
// Animations/SkeletonList.xml within the current game project changes)
"MetaDataTypes": {
"animsettings": "i_caf",
"Animations": {
"SkeletonList.xml": "i_caf"
},
"Animations/SkeletonList.xml": "i_caf",
"cbc": "abc",
"fbx.assetinfo": "fbx"
},
// ---- add any folders to scan here. The priority order is the order they appear here
// available macros are
// @ROOT@ - the location of asset root
// @PROJECTROOT@ - the location of the project root, for example 'Q:\MyProjects\RPGSample'
// note that they are sorted by their 'order' value, and the lower the order the more important an asset is
// lower order numbers override higher ones.
// If specified, output will be prepended to every path found in that recognizer's watch folder.
// Note that you can also make the scan folder platform specific by using the keywords include and exclude.
// Both include and exclude can contain either platform tags, platform identifiers or both.
// if no include is specified, all currently enabled platforms are included by default.
// If includes ARE specified, it will be filtered down by the list of currently enabled platforms.
// "ScanFolder (unique identifier)": {
// "include": "(comma seperated platform tags or identifiers)",
// "exclude": "(comma seperated platform tags or identifiers)"
// }
// For example if you want to include a scan folder only for platforms that have the platform tags tools and renderer
// but omit it for platform osx_gl, you will have a scanfolder rule like
// "ScanFolder (unique identifier)": {
// "watch": "@ROOT@/foo",
// "include": "tools, renderer",
// "exclude": "osx_gl"
// }
"ScanFolder Game": {
"watch": "@ROOT@/@GAMENAME@",
"display": "@GAMENAME@",
"watch": "@PROJECTROOT@",
"display": "@PROJECTROOT@",
"recursive": 1,
"order": 0
},
// gems will be auto-added from 100 onwards
"ScanFolder Root": {
"watch": "@ROOT@",
"recursive": 0,
@ -52,6 +126,9 @@
"order": 30000,
"include": "tools,renderer"
},
// Excludes files that match the pattern or glob
// if you use a pattern, remember to escape your backslashes (\\)
"Exclude _LevelBackups": {
"pattern": ".*\\\\/Levels\\\\/.*\\\\/_savebackup\\\\/.*"
},
@ -61,6 +138,7 @@
"Exclude HoldFiles": {
"pattern": ".*\\\\/Levels\\\\/.*_hold\\\\/.*"
},
// note that $ has meaning to regex, so we escape it.
"Exclude TempFiles": {
"pattern": ".*\\\\/\\\\$tmp[0-9]*_.*"
},
@ -88,6 +166,16 @@
"Exclude CMakeFiles": {
"pattern": ".*\\\\/.*\\\\.cmake"
},
"Exclude User": {
"pattern": ".*/[Uu]ser/.*"
},
"Exclude Build": {
"pattern": ".*/[Bb]uild/.*"
},
// ------------------------------------------------------------------------------
// Large Worlds Test
// ------------------------------------------------------------------------------
"Exclude Work In Progress Folders": {
"pattern": ".*\\\\/WIP\\\\/.*"
},
@ -97,9 +185,112 @@
"Exclude Art Source Folders": {
"pattern": ".*\\\\/ArtSource\\\\/.*"
},
//------------------------------------------------------------------------------
// RC params mapping examples
//------------------------------------------------------------------------------
// note that productAssetType is a means of setting the output asset Type (as in AZ::Data::AssetType) of a simple job
// and is the recommended way to specify that a certain kind of file (such as '*.myextension') becomes registered as the
// actual UUID of that type in the engine itself.
// Use a regex for matching files, same params for all platforms
// "RC TGAs": {
// "pattern": ".+\\\\.tga$",
// "params": "/tga /texture"
//}
// Use a glob, have special params for es3 platform
// "RC TIFFs": {
// "glob": "*.tif",
// "params": "/texture",
// "es3": "/pvrt"
//}
// You can also modify a version to compile all matching files again
// By default the version is empty
// "RC tif": {
// "glob": "*.tif",
// "params": "\\someparams",
// "version": 1.0
//}
// This will make the AssetProcessor compile all the .tif files again
// you can also optionally supply a priority.
// this is used to sort jobs when no other external circumstance sorts them
// for example, copy jobs will be higher in priority than other jobs that are not copy jobs
// however if they're both copy jobs or both not, and no other circumstances apply, then priority will be used.
// default priority is zero if not specified
// you can specify an option to skip processing for a file type based on the platform.
// for example, if you dont want to process tif files for ios, you can make tif files
// process on any platform except for ios:
// "RC tif": {
// "glob": "*.tif",
// "params": "\\someparams",
// "ios": "skip"
//}
// you can specify an option to output product dependencies for a copy job.
// please note that you only need to set this option when cry code is required to parse the asset.
// otherwise product dependencies will be output automatically by the CopyDependencyBuilder.
// for example, if you want to output the product dependencies for font assets:
// "RC font": {
// "glob": "*.font",
// "params": "copy",
// "outputProductDependencies": true
//}
// you can also specify an option to make all jobs critical that matches some pattern/glob.
// for example, if you want to make all png files critical than set critical to true.
// Note that by default all copy jobs are critical.
// Critical jobs are processed before non critical jobs and also prevent the runtime or editor from starting until they are all complete.
// "RC png": {
// "glob": "*.png",
// "params": "\\someparams",
// "critical": true
//}
// you can also specify an option to make all the job store in the asset server cache location if you are running AP in server mode.
// For example, if you want to store all png jobs in the asset server cache location including their logs, you can set checkServer = true.
// The client(i.e if you are running AP in non-server mode) will also check for this flag to know which jobs to fetch from the asset server cache location.
// if unsucessful, it will process the job locally as usual.
// "RC png": {
// "glob": "*.png",
// "params": "\\someparams",
// "critical": true,
// "checkServer": true
//}
// note that the FULL PATH to the file will be used as the match, not the relative path
// so ensure start your patterns with .* or as appropriate.
// Also, any rules which match will apply - so if you have two rules which both apply to PNG files for example
// but you only want one, you might want to use exclusion patterns:
//Example: process everything EXCEPT the ones in the libs/ui folder with these params
// "RC png-normal": {
// "pattern": "(?!.*libs\\\\/ui\\\\/).*\\.png",
// "params": "/imagecompressor=CTSquish /streaming=0",
// "lockSource": true
//}
//Example: Process everything in the libs/ui folder with linear color space
// "RC png-ui": {
// "pattern": "(.*libs\\\\/ui\\\\/).*\\.png",
// "params": "/imagecompressor=CTSquish /streaming=0 /colorspace=linear,linear",
// "lockSource": true
//}
// More example Regexes:
// "pattern": "(?!(.*libs\\\\/ui\\\\/)|(.*editor\\\\/).*\\\\.png"
// This pattern will not match anything with editor/ or libs/ui/ in it
// "pattern": "((.*libs\\\\/ui\\\\/)|(.*editor\\\\/).*\\\\.png"
// This pattern will only match anything with editor/ or libs/ui/ in it
// Give every [Section Name] its own unique Name or else they will overwrite each other!
"RC i_caf": {
"glob": "*.i_caf",
"params": "/cafAlignTracks=1 /animConfigFolder=Animations /skipdba=1 /refresh=1",
// force server to send the 'pc' platform to RC.EXE so it compiles the same as PC.
"server": "/cafAlignTracks=1 /animConfigFolder=Animations /skipdba=1 /refresh=1 /p=pc",
"priority": 5,
"productAssetType": "{6023CFF8-FCBA-4528-A8BF-6E0E10B9AB9C}"
@ -147,9 +338,12 @@
"RC cgf": {
"glob": "*.cgf",
"params": "/VertexPositionFormat=exporter /VertexIndexFormat=u32",
// on server, feed rc.exe the param /p=pc to force it to compile assets for server platform in pc format.
"server": "/VertexPositionFormat=exporter /VertexIndexFormat=u32 /p=pc",
"lockSource": true,
"priority": 10
// allow CGF files to compile first, so untextured models appear before their textures for faster startup
// other available params: /SplitLODs=1
},
"RC surfaceTagNameList": {
"glob": "*.surfaceTagNameList",
@ -163,8 +357,9 @@
},
"RC fbx": {
"glob": "*.fbx",
// Priority set to 9 so its "before" things like materials but after things like actors and motions (which build using a proper AssetBuilderSDK builder and thus are not in this file)
"priority": 9,
"version": 4
"version": 5
},
"RC chr": {
"glob": "*.chr",
@ -188,6 +383,7 @@
"glob": "*.cfr",
"params": "copy"
},
// Warning: If you change the VertexIndexFormat, make sure you update the vtx_idx typedef in Code\CryEngine\CryCommon\ProjectDefines.h
"RC abc": {
"glob": "*.abc",
"params": "/SkipFilesWithoutBuildConfig=0 /VertexIndexFormat=u32",
@ -199,7 +395,7 @@
"RC png-entityicon": {
"pattern": "(.*EntityIcons\\\\/).*\\\\.png",
"productAssetType": "{3436C30E-E2C5-4C3B-A7B9-66C94A28701B}",
"params": "skip ; only tools-supporting platforms should copy this file. Everyone else can skip.",
"params": "skip",
"tools": "copy"
},
"RC usm": {
@ -227,6 +423,9 @@
"params": "copy",
"productAssetType": "{DF036C63-9AE6-4AC3-A6AC-8A1D76126C01}"
},
// note - this used to be skinnedMeshAsset but its now Character Definition File specific.
// .skin has its own type.
"RC chrparams": {
"glob": "*.chrparams",
"params": "copy",
@ -272,13 +471,6 @@
"params": "copy",
"version": 1
},
"RC Editor Slice Copy": {
"glob": "*.slice",
"params": "copy",
"critical": true,
"productAssetType": "{C62C7A87-9C09-4148-A985-12F2C99C0A45}",
"priority": 2
},
"RC node": {
"glob": "*.node",
"params": "copy"
@ -329,6 +521,7 @@
"glob": "*.ext",
"params": "copy"
},
// Copy all pak files except level.pak, level.pak has its own builder.
"RC pak": {
"pattern": "^((?!\\\\/level\\\\.pak).)*\\\\.pak$",
"params": "copy"
@ -365,15 +558,18 @@
"glob": "*.ocm",
"params": "copy"
},
// Feature tests use the raw .tif files for the golden image comparison
"RC goldenimages": {
"pattern": ".*GoldenImages\\\\/.*\\\\.tif",
"params": "copy",
"server": "skip"
},
// Copy over certificates for use with FileDataSource
"RC CertificatePEM": {
"glob": "*.pem",
"params": "copy"
},
// Copy over certificates for use with Dynamic Content
"RC CertificateDER": {
"glob": "*.der",
"params": "copy"
@ -383,6 +579,7 @@
"params": "copy",
"productAssetType": "{7A2871B9-5EAB-4DE0-A901-B0D2C6920DDB}"
},
// Copy over cooked PhysX heightfield
"RC PhysX HeightField": {
"glob": "*.pxheightfield",
"params": "copy",
@ -392,8 +589,20 @@
"glob": "*.filetag",
"params": "copy",
"productAssetType": "{F3BE5CAB-85B7-44B7-9495-863863F6B267}"
},
// Precompiled shader srg
"RC azsrg": {
"glob": "*.azsrg",
"params": "copy",
"productAssetType": "{F8C9F4AE-3F6A-45AD-B4FB-0CA415FCC2E1}"
},
// Precompiled shader variant
"RC azshadervariant": {
"glob": "*.azshadervariant",
"params": "copy",
"productAssetType": "{9F4D654B-4439-4C61-8DCD-F1C7C5560768}"
}
}
}
}
}
}

@ -119,6 +119,7 @@ def LoadPipelineConfig(String pipelineName, String branchName) {
PullFilesFromGit(PIPELINE_CONFIG_FILE, branchName)
def pipelineConfig = {}
pipelineConfig = readJSON file: PIPELINE_CONFIG_FILE
palRm(PIPELINE_CONFIG_FILE)
pipelineConfig.platforms = EMPTY_JSON
// Load the pipeline configs per platform
@ -137,6 +138,7 @@ def LoadPipelineConfig(String pipelineName, String branchName) {
pipelineConfig.platforms[platform] = EMPTY_JSON
pipelineConfig.platforms[platform].PIPELINE_ENV = readJSON file: pipeline_config_path.toString()
}
palRm(pipeline_config_path.toString())
}
}
@ -155,6 +157,7 @@ def LoadPipelineConfig(String pipelineName, String branchName) {
if(platform) {
pipelineConfig.platforms[platform].build_types = readJSON file: build_config_path.toString()
}
palRm(build_config_path.toString())
}
}
return pipelineConfig
@ -202,39 +205,45 @@ def PullFilesFromGit(String filenamePath, String branchName, boolean failIfNotFo
folderPathParts.remove(folderPathParts.size()-1) // remove the filename
def folderPath = folderPathParts.join('/')
if (folderPath.contains('*')) {
def currentPath = ''
for (int i = 0; i < folderPathParts.size(); i++) {
if (folderPathParts[i] == '*') {
palMkdir(currentPath)
retry(3) { palSh("aws codecommit get-folder --repository-name ${repositoryName} --commit-specifier ${branchName} --folder-path ${currentPath} > ${currentPath}/.codecommit", "GetFolder ${currentPath}") }
def folderInfo = readJSON file: "${currentPath}/.codecommit"
folderInfo.subFolders.each { folder ->
def newSubPath = currentPath + '/' + folder.relativePath
for (int j = i+1; j < folderPathParts.size(); j++) {
newSubPath = newSubPath + '/' + folderPathParts[j]
try {
def currentPath = ''
for (int i = 0; i < folderPathParts.size(); i++) {
if (folderPathParts[i] == '*') {
palMkdir(currentPath)
retry(3) { palSh("aws codecommit get-folder --repository-name ${repositoryName} --commit-specifier ${branchName} --folder-path ${currentPath} > ${currentPath}/.codecommit", "GetFolder ${currentPath}") }
def folderInfo = readJSON file: "${currentPath}/.codecommit"
folderInfo.subFolders.each { folder ->
def newSubPath = currentPath + '/' + folder.relativePath
for (int j = i+1; j < folderPathParts.size(); j++) {
newSubPath = newSubPath + '/' + folderPathParts[j]
}
newSubPath = newSubPath + '/' + filename
PullFilesFromGit(newSubPath, branchName, false, repositoryName)
}
newSubPath = newSubPath + '/' + filename
PullFilesFromGit(newSubPath, branchName, false, repositoryName)
palRm("${currentPath}/.codecommit")
}
if (i == 0) {
currentPath = folderPathParts[i]
} else {
currentPath = currentPath + '/' + folderPathParts[i]
}
palRm("${currentPath}/.codecommit")
}
if (i == 0) {
currentPath = folderPathParts[i]
} else {
currentPath = currentPath + '/' + folderPathParts[i]
}
} catch(Exception e) {
}
} else if (filename.contains('*')) {
palMkdir(folderPath)
retry(3) { palSh("aws codecommit get-folder --repository-name ${repositoryName} --commit-specifier ${branchName} --folder-path ${folderPath} > ${folderPath}/.codecommit", "GetFolder ${folderPath}") }
def folderInfo = readJSON file: "${folderPath}/.codecommit"
folderInfo.files.each { file ->
PullFilesFromGit("${folderPath}/${filename}", branchName, false, repositoryName)
try {
palMkdir(folderPath)
retry(3) { palSh("aws codecommit get-folder --repository-name ${repositoryName} --commit-specifier ${branchName} --folder-path ${folderPath} > ${folderPath}/.codecommit", "GetFolder ${folderPath}") }
def folderInfo = readJSON file: "${folderPath}/.codecommit"
folderInfo.files.each { file ->
PullFilesFromGit("${folderPath}/${filename}", branchName, false, repositoryName)
}
palRm("${folderPath}/.codecommit")
} catch(Exception e) {
}
palRm("${folderPath}/.codecommit")
} else {
@ -271,18 +280,7 @@ def PullFilesFromGit(String filenamePath, String branchName, boolean failIfNotFo
}
}
def SetLfsCredentials(cmd, lbl = '') {
if (env.IS_UNIX) {
sh label: lbl,
script: cmd
} else {
bat label: lbl,
script: cmd
}
}
def CheckoutRepo(boolean disableSubmodules = false) {
palSh('git lfs uninstall', 'Git LFS Uninstall') // Prevent git from pulling lfs objects during checkout
if(fileExists('.git')) {
// If the repository after checkout is locked, likely we took a snapshot while git was running,
@ -292,7 +290,7 @@ def CheckoutRepo(boolean disableSubmodules = false) {
palSh('git gc', 'Git GarbageCollect')
}
if(fileExists(indexLockFile)) { // if it is still there, remove it
palRm(indexLockFile, 'Remove index.lock')
palRm(indexLockFile)
}
palSh('git remote prune origin', 'Git reset')
}
@ -329,14 +327,6 @@ def CheckoutRepo(boolean disableSubmodules = false) {
}
}
// Run lfs in a separate step. Jenkins is unable to load the credentials for the custom LFS endpoint
withCredentials([usernamePassword(credentialsId: "${env.GITHUB_USER}", passwordVariable: 'accesstoken', usernameVariable: 'username')]) {
SetLfsCredentials("git config -f .lfsconfig lfs.url https://${username}:${accesstoken}@${env.LFS_URL}", 'Set credentials')
}
palSh('git lfs install', 'Git LFS Install')
palSh('git lfs pull', 'Git LFS Pull')
}
// CHANGE_ID is used by some scripts to identify uniquely the current change (usually metric jobs)
palSh('git rev-parse HEAD > commitid', 'Getting commit id')
env.CHANGE_ID = readFile file: 'commitid'
@ -406,6 +396,26 @@ def Build(Map options, String platform, String type, String workspace) {
}
}
def TestMetrics(Map options, Map buildType, String workspace, String branchName, String repoName) {
catchError(buildResult: null, stageResult: null) {
def cmakeBuildDir = [workspace, buildType.value.PARAMETERS.OUTPUT_DIRECTORY].join('/')
def command = "${options.PYTHON_DIR}/python.cmd -u mars/scripts/python/ctest_test_metric_scraper.py -e jenkins.creds.user ${username} -e jenkins.creds.pass ${apitoken} ${cmakeBuildDir} ${branchName} %BUILD_NUMBER% AR ${buildType.value.PARAMETERS.CONFIGURATION} ${repoName} "
if (params.DESTINATION_BRANCH)
command += '--destination-branch "$DESTINATION_BRANCH" '
dir(workspace) {
checkout scm: [
$class: 'GitSCM',
extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'mars']],
userRemoteConfigs: [[url: "${env.MARS_REPO}", name: 'mars']]
]
withCredentials([usernamePassword(credentialsId: "${env.SERVICE_USER}", passwordVariable: 'apitoken', usernameVariable: 'username')]) {
bat label: "Publishing ${buildType.key} Test Metrics",
script: command
}
}
}
}
def PostBuildCommonSteps(String workspace, boolean mount = true) {
echo 'Starting post-build common steps...'
@ -448,6 +458,14 @@ def CreateBuildStage(Map pipelineConfig, String platformName, String jobName, Ma
}
}
def CreateTestMetricsStage(Map pipelineConfig, Map buildJob, String branchName, Map environmentVars) {
return {
stage("${buildJob.key}") {
TestMetrics(pipelineConfig, buildJob, environmentVars['WORKSPACE'], branchName, env.DEFAULT_REPOSITORY_NAME)
}
}
}
def CreateTeardownStage(Map environmentVars) {
return {
stage("Teardown") {
@ -538,6 +556,9 @@ try {
} else {
CreateBuildStage(pipelineConfig, platform.key, build_job.key, envVars).call()
}
if (env.MARS_REPO && platform.key == 'Windows' && build_job.key.startsWith('test')) {
CreateTestMetricsStage(pipelineConfig, build_job, branchName, envVars).call()
}
}
catch(Exception e) {
// https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/Result.java

@ -9,4 +9,12 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
file(READ "${CMAKE_CURRENT_LIST_DIR}/project.json" project_json)
string(JSON project_target_name ERROR_VARIABLE json_error GET ${project_json} "project_name")
if(${json_error})
message(FATAL_ERROR "Unable to read key 'project_name' from 'project.json'")
endif()
set_property(GLOBAL APPEND PROPERTY LY_PROJECTS_TARGET_NAME ${project_target_name})
add_subdirectory(Gem)

@ -1,197 +0,0 @@
<ObjectStream version="1">
<Class name="ComponentApplication::Descriptor" version="2" type="{70277A3E-2AF5-4309-9BBF-6161AFBDE792}">
<Class name="bool" field="useExistingAllocator" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="grabAllMemory" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="allocationRecords" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="allocationRecordsSaveNames" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="allocationRecordsAttemptDecodeImmediately" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="int" field="recordingMode" value="2" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
<Class name="AZ::u64" field="stackRecordLevels" value="5" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
<Class name="bool" field="autoIntegrityCheck" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="markUnallocatedMemory" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="doNotUsePools" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="enableScriptReflection" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="unsigned int" field="pageSize" value="65536" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="unsigned int" field="poolPageSize" value="4096" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="unsigned int" field="blockAlignment" value="65536" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="AZ::u64" field="blockSize" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
<Class name="AZ::u64" field="reservedOS" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
<Class name="AZ::u64" field="reservedDebug" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
<Class name="bool" field="enableDrilling" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="useOverrunDetection" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="useMalloc" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="AZStd::vector" field="allocatorRemappings" type="{82897F6E-6389-5BEF-B427-761DB35AC1CC}"/>
<Class name="AZStd::vector" field="modules" type="{8E779F80-AEAA-565B-ABB1-DE10B18CF995}">
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Maestro.Editor.3b9a978ed6f742a1acb99f74379a342c.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.TextureAtlas.5a149b6b3c964064bd4970f0e92f72e2.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.LmbrCentral.Editor.ff06785f7145416b9d46fde39098cb0c.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.LyShine.Editor.0fefab3f13364722b2eab3b96ce2bf20.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.HttpRequestor.28479e255bde466e91fc34eec808d9c7.v1.0.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ScriptEvents.Editor.32d8ba21703e4bbbb08487366e48dd69.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ExpressionEvaluation.4c6f9df57ca2468f93c8d860ee6a1167.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.AssetValidation.5a5c3c10b91d4b4ea8baef474c5b5d49.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Gestures.6056556b6088413984309c4a413593ad.v1.0.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.CertificateManager.659cffff33b14a10835bafc6ea623f98.v0.0.1" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.DebugDraw.Editor.66239f50bf754354b514c850c8b841fb.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.AudioSystem.Editor.6f63f2b6d07f4b89b4b7c86ebee7feb8.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.GameLift.76de765796504906b73be7365a9bff06.v2.0.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.SceneProcessing.Editor.7c2578f634df4345aca98d671e39b8ab.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.GraphCanvas.Editor.875b6fcbdeea44deaae7984ad9bb6cdc.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.InAppPurchases.92fe57eae7d3402a90761973678c079a.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.AutomatedTesting.afc25e1593194d6283d9ff744ab6d5a1.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.EditorPythonBindings.Editor.b658359393884c4381c2fe2952b1472a.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Metastream.c02d7efe05134983b5699d9ee7594c3a.v1.0.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.WhiteBox.Editor.c5833dbda2e045d3a5f16b7414280c27.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ImageProcessing.Editor.eeffbd9211cf4ce0b5cc73696b427cbe.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.TestAssetBuilder.Editor.f5c92f1560714010ba30467d93feecef.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Camera.Editor.f910686b6725452fbfc4671f95f733c6.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.EMotionFX.Editor.044a63ea67d04479aa5daf62ded9d9ca.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.GraphModel.Editor.0844f64a3acf4f5abf3a535dc9b63bc9.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.PhysX.Editor.4e08125824434932a0fe3717259caa47.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.CameraFramework.54f2763fe191432fa681ce4a354eedf5.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.SurfaceData.Editor.5de82d29d6094bfe97c1a4d35fcd5fbe.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.AudioEngineWwise.Editor.67a80e2ac865406c990f2715feb55f7f.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.StartingPointMovement.73d8779dc28a4123b7c9ed76217464af.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.StartingPointCamera.834070b9537d44df83559e2045c3859f.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ScriptCanvasGem.Editor.869a0d0ec11a45c299917d45c81555e6.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.GradientSignal.Editor.8825563d9d964ec3be3bab681f3bd9f2.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ImGui.Editor.bab8807a1bc646b3909f3cc200ffeedf.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ChatPlay.bfbc60c63ffd4b00927003735b26ce99.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.TouchBending.Editor.c58d2057f3724b22ae0df0be68a4e316.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.FastNoise.Editor.c5f23032407f49ca8d8de1733423565c.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.LyShineExamples.c7935ecf5e8047fe8ca947b34b11cadb.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Vegetation.Editor.f394e7cf54424bba89615381bba9511b.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.StartingPointInput.Editor.09f4bedeee614358bc36788e77f97e51.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.LandscapeCanvas.Editor.19c2b2d5018940108baf252934b8e6bf.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ScriptCanvasPhysics.1c27519a4dda4ffaaeebf91514e5b1e8.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.PhysXDebug.Editor.516145e2d9904b13813f1b54605e26a6.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
</Class>
</Class>
<Class name="AZ::Entity" version="2" type="{75651658-8663-478D-9090-2432DFCAFA44}">
<Class name="EntityId" field="Id" version="1" type="{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}">
<Class name="AZ::u64" field="id" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
<Class name="AZStd::string" field="Name" value="SystemEntity" type="{EF8FF807-DDEE-4EB0-B678-4CA3A2C490A4}"/>
<Class name="bool" field="IsDependencyReady" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="AZStd::vector" field="Components" type="{2BADE35A-6F1B-4698-B2BC-3373D010020C}">
<Class name="PhysicsSystemComponent" field="element" value="" version="1" type="{1586DBA1-F5F0-49AB-9F59-AE62C0E60AE0}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="5881970355815182227" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
</Class>
<Class name="NavigationSystemComponent" field="element" value="" version="1" type="{3D27484B-00C4-4F3F-9605-2BF3E5C317FF}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="3408791856679455729" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
</Class>
<Class name="EditorSelectionAccentSystemComponent" field="element" value="" type="{6E0F0E2C-1FE5-4AFB-9672-DC92B3D2D844}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="4281709857282575192" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
</Class>
<Class name="AudioSystemComponent" field="element" version="1" type="{666E28D2-FC99-4D41-861D-3758C5070653}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="4028172539943260142" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
</Class>
<Class name="LyShineSystemComponent" field="element" value="" type="{B0C78B8D-1E5B-47D7-95D0-EC69C0513804}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="13584318858523138243" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
</Class>
<Class name="LyEditorMetricsSystemComponent" field="element" value="" type="{B8C74085-F6B7-4E2F-8135-78C991CC53C5}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="8970396848016072155" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
</Class>
<Class name="StereoRendererComponent" field="element" version="1" type="{BBFE0965-5564-4739-8219-AFE8209A5E57}"/>
</Class>
</Class>
</ObjectStream>

@ -1,179 +0,0 @@
<ObjectStream version="1">
<Class name="ComponentApplication::Descriptor" version="2" type="{70277A3E-2AF5-4309-9BBF-6161AFBDE792}">
<Class name="bool" field="useExistingAllocator" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="grabAllMemory" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="allocationRecords" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="allocationRecordsSaveNames" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="allocationRecordsAttemptDecodeImmediately" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="int" field="recordingMode" value="2" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
<Class name="AZ::u64" field="stackRecordLevels" value="5" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
<Class name="bool" field="autoIntegrityCheck" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="markUnallocatedMemory" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="doNotUsePools" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="enableScriptReflection" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="unsigned int" field="pageSize" value="65536" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="unsigned int" field="poolPageSize" value="4096" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="unsigned int" field="blockAlignment" value="65536" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="AZ::u64" field="blockSize" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
<Class name="AZ::u64" field="reservedOS" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
<Class name="AZ::u64" field="reservedDebug" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
<Class name="bool" field="enableDrilling" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="useOverrunDetection" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="useMalloc" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="AZStd::vector" field="allocatorRemappings" type="{82897F6E-6389-5BEF-B427-761DB35AC1CC}"/>
<Class name="AZStd::vector" field="modules" type="{8E779F80-AEAA-565B-ABB1-DE10B18CF995}">
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Maestro.3b9a978ed6f742a1acb99f74379a342c.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.TextureAtlas.5a149b6b3c964064bd4970f0e92f72e2.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.LmbrCentral.ff06785f7145416b9d46fde39098cb0c.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.LyShine.0fefab3f13364722b2eab3b96ce2bf20.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.HttpRequestor.28479e255bde466e91fc34eec808d9c7.v1.0.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ScriptEvents.32d8ba21703e4bbbb08487366e48dd69.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ExpressionEvaluation.4c6f9df57ca2468f93c8d860ee6a1167.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.AssetValidation.5a5c3c10b91d4b4ea8baef474c5b5d49.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Gestures.6056556b6088413984309c4a413593ad.v1.0.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.CertificateManager.659cffff33b14a10835bafc6ea623f98.v0.0.1" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.DebugDraw.66239f50bf754354b514c850c8b841fb.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.AudioSystem.6f63f2b6d07f4b89b4b7c86ebee7feb8.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.GameLift.76de765796504906b73be7365a9bff06.v2.0.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.GraphCanvas.875b6fcbdeea44deaae7984ad9bb6cdc.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.InAppPurchases.92fe57eae7d3402a90761973678c079a.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.AutomatedTesting.afc25e1593194d6283d9ff744ab6d5a1.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Metastream.c02d7efe05134983b5699d9ee7594c3a.v1.0.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.WhiteBox.c5833dbda2e045d3a5f16b7414280c27.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Camera.f910686b6725452fbfc4671f95f733c6.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.EMotionFX.044a63ea67d04479aa5daf62ded9d9ca.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.PhysX.4e08125824434932a0fe3717259caa47.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.CameraFramework.54f2763fe191432fa681ce4a354eedf5.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.SurfaceData.5de82d29d6094bfe97c1a4d35fcd5fbe.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.AudioEngineWwise.67a80e2ac865406c990f2715feb55f7f.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.StartingPointMovement.73d8779dc28a4123b7c9ed76217464af.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.StartingPointCamera.834070b9537d44df83559e2045c3859f.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ScriptCanvasGem.869a0d0ec11a45c299917d45c81555e6.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.GradientSignal.8825563d9d964ec3be3bab681f3bd9f2.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ImGui.bab8807a1bc646b3909f3cc200ffeedf.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ChatPlay.bfbc60c63ffd4b00927003735b26ce99.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.TouchBending.c58d2057f3724b22ae0df0be68a4e316.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.FastNoise.c5f23032407f49ca8d8de1733423565c.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.LyShineExamples.c7935ecf5e8047fe8ca947b34b11cadb.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Vegetation.f394e7cf54424bba89615381bba9511b.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.StartingPointInput.09f4bedeee614358bc36788e77f97e51.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ScriptCanvasPhysics.1c27519a4dda4ffaaeebf91514e5b1e8.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.PhysXDebug.516145e2d9904b13813f1b54605e26a6.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
</Class>
</Class>
<Class name="AZ::Entity" version="2" type="{75651658-8663-478D-9090-2432DFCAFA44}">
<Class name="EntityId" field="Id" version="1" type="{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}">
<Class name="AZ::u64" field="id" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
<Class name="AZStd::string" field="Name" value="SystemEntity" type="{EF8FF807-DDEE-4EB0-B678-4CA3A2C490A4}"/>
<Class name="bool" field="IsDependencyReady" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="AZStd::vector" field="Components" type="{2BADE35A-6F1B-4698-B2BC-3373D010020C}">
<Class name="PhysicsSystemComponent" field="element" value="" version="1" type="{1586DBA1-F5F0-49AB-9F59-AE62C0E60AE0}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="14545574550664822512" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
</Class>
<Class name="NavigationSystemComponent" field="element" value="" version="1" type="{3D27484B-00C4-4F3F-9605-2BF3E5C317FF}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="9809863829757322975" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
</Class>
<Class name="LyShineSystemComponent" field="element" value="" type="{B0C78B8D-1E5B-47D7-95D0-EC69C0513804}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="3706545825063126910" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
</Class>
<Class name="AudioSystemComponent" field="element" version="1" type="{666E28D2-FC99-4D41-861D-3758C5070653}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="17265601292746528732" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
</Class>
<Class name="StereoRendererComponent" field="element" version="1" type="{BBFE0965-5564-4739-8219-AFE8209A5E57}"/>
<Class name="LmbrCentralSystemComponent" field="element" value="" version="1" type="{CE249D37-C1D6-4A64-932D-C937B0EC2B8C}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="17265313386362322770" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
</Class>
<Class name="AssetDatabaseComponent" field="element" value="" version="1" type="{D5A73BCC-0098-4D1E-8FE4-C86101E374AC}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="18338415854038604706" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
</Class>
</Class>
</Class>
</ObjectStream>

@ -1,136 +0,0 @@
<ObjectStream version="3">
<Class name="ComponentApplication::Descriptor" version="2" type="{70277A3E-2AF5-4309-9BBF-6161AFBDE792}">
<Class name="bool" field="useExistingAllocator" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="grabAllMemory" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="allocationRecords" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="allocationRecordsSaveNames" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="allocationRecordsAttemptDecodeImmediately" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="int" field="recordingMode" value="2" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
<Class name="AZ::u64" field="stackRecordLevels" value="5" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
<Class name="bool" field="autoIntegrityCheck" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="markUnallocatedMemory" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="doNotUsePools" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="enableScriptReflection" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="unsigned int" field="pageSize" value="65536" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="unsigned int" field="poolPageSize" value="4096" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="unsigned int" field="blockAlignment" value="65536" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="AZ::u64" field="blockSize" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
<Class name="AZ::u64" field="reservedOS" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
<Class name="AZ::u64" field="reservedDebug" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
<Class name="bool" field="enableDrilling" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="useOverrunDetection" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="useMalloc" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="AZStd::vector" field="allocatorRemappings" type="{82897F6E-6389-5BEF-B427-761DB35AC1CC}"/>
<Class name="AZStd::vector" field="modules" type="{8E779F80-AEAA-565B-ABB1-DE10B18CF995}">
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Maestro.3b9a978ed6f742a1acb99f74379a342c.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.TextureAtlas.5a149b6b3c964064bd4970f0e92f72e2.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.LmbrCentral.ff06785f7145416b9d46fde39098cb0c.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.LyShine.0fefab3f13364722b2eab3b96ce2bf20.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.HttpRequestor.28479e255bde466e91fc34eec808d9c7.v1.0.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ScriptEvents.32d8ba21703e4bbbb08487366e48dd69.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ExpressionEvaluation.4c6f9df57ca2468f93c8d860ee6a1167.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.AssetValidation.5a5c3c10b91d4b4ea8baef474c5b5d49.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Gestures.6056556b6088413984309c4a413593ad.v1.0.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.CertificateManager.659cffff33b14a10835bafc6ea623f98.v0.0.1" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.DebugDraw.66239f50bf754354b514c850c8b841fb.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.AudioSystem.6f63f2b6d07f4b89b4b7c86ebee7feb8.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.GameLift.76de765796504906b73be7365a9bff06.v2.0.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.GraphCanvas.875b6fcbdeea44deaae7984ad9bb6cdc.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.InAppPurchases.92fe57eae7d3402a90761973678c079a.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.AutomatedTesting.afc25e1593194d6283d9ff744ab6d5a1.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Metastream.c02d7efe05134983b5699d9ee7594c3a.v1.0.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.WhiteBox.c5833dbda2e045d3a5f16b7414280c27.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Camera.f910686b6725452fbfc4671f95f733c6.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.EMotionFX.044a63ea67d04479aa5daf62ded9d9ca.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.PhysX.4e08125824434932a0fe3717259caa47.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.PhysXCharacters.50f9ae1e09ac471bbd9d86ca8063ddf9.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.CameraFramework.54f2763fe191432fa681ce4a354eedf5.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.AudioEngineWwise.67a80e2ac865406c990f2715feb55f7f.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.StartingPointMovement.73d8779dc28a4123b7c9ed76217464af.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.StartingPointCamera.834070b9537d44df83559e2045c3859f.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ScriptCanvasGem.869a0d0ec11a45c299917d45c81555e6.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ImGui.bab8807a1bc646b3909f3cc200ffeedf.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ChatPlay.bfbc60c63ffd4b00927003735b26ce99.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.LyShineExamples.c7935ecf5e8047fe8ca947b34b11cadb.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.StartingPointInput.09f4bedeee614358bc36788e77f97e51.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ScriptCanvasPhysics.1c27519a4dda4ffaaeebf91514e5b1e8.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.PhysXDebug.516145e2d9904b13813f1b54605e26a6.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
</Class>
</Class>
</Class>
<Class name="AZ::Entity" version="2" type="{75651658-8663-478D-9090-2432DFCAFA44}">
<Class name="EntityId" field="Id" version="1" type="{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}">
<Class name="AZ::u64" field="id" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
<Class name="AZStd::string" field="Name" value="SystemEntity" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="AZStd::vector" field="Components" type="{13D58FF9-1088-5C69-9A1F-C2A144B57B78}"/>
<Class name="bool" field="IsDependencyReady" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="IsRuntimeActive" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
</Class>
</ObjectStream>

@ -1,16 +0,0 @@
; The PythonTest folder contains benchmarksettings test assets that should only get processed when
; the test is run, and the assets are temporarily copied to a separate folder.
[Exclude PythonTest Benchmark Settings Assets]
pattern=.*\\/PythonTests\\/.*benchmarksettings
[Exclude fbx_tests]
pattern=.*\\/fbx_tests\\/assets\\/.*
[Exclude wwise_bank_dependency_tests]
pattern=.*\\/wwise_bank_dependency_tests\\/assets\\/.*
[Exclude AssetProcessorTestAssets]
pattern=.*\\/asset_processor_tests\\/assets\\/.*
[Exclude Restricted AssetProcessorTestAssets]
pattern=.*\\/asset_processor_tests\\/restricted\\/.*

@ -0,0 +1,23 @@
{
"Amazon": {
"AssetProcessor": {
"Settings": {
"Exclude PythonTest Benchmark Settings Assets": {
"pattern": ".*\\\\/PythonTests\\\\/.*benchmarksettings"
},
"Exclude fbx_tests": {
"pattern": ".*\\\\/fbx_tests\\\\/assets\\\\/.*"
},
"Exclude wwise_bank_dependency_tests": {
"pattern": ".*\\\\/wwise_bank_dependency_tests\\\\/assets\\\\/.*"
},
"Exclude AssetProcessorTestAssets": {
"pattern": ".*\\\\/asset_processor_tests\\\\/assets\\\\/.*"
},
"Exclude Restricted AssetProcessorTestAssets": {
"pattern": ".*\\\\/asset_processor_tests\\\\/restricted\\\\/.*"
}
}
}
}
}

@ -30,6 +30,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
Legacy::CryRenderNULL
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Physics
)
ly_add_pytest(
NAME AutomatedTesting::PhysicsTests_Sandbox
@ -42,6 +44,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
Legacy::CryRenderNULL
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Physics
)
endif()
@ -49,7 +53,7 @@ endif()
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::ScriptCanvasTests
TEST_SUITE main
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/scripting/TestSuite_Active.py
TIMEOUT 3600
@ -58,6 +62,20 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
Legacy::CryRenderNULL
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
ScriptCanvas
)
ly_add_pytest(
NAME AutomatedTesting::ScriptCanvasTests_Sandbox
TEST_SUITE sandbox
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/scripting/TestSuite_Sandbox.py
TIMEOUT 3600
RUNTIME_DEPENDENCIES
Legacy::Editor
Legacy::CryRenderNULL
AZ::AssetProcessor
AutomatedTesting.Assets
)
endif()
@ -74,6 +92,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
Legacy::CryRenderNULL
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
WhiteBox
)
endif()
@ -108,7 +128,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
AZ::AssetProcessor
AutomatedTesting.Assets
Gem::EditorPythonBindings.Editor
COMPONENT TestTools
COMPONENT TestTools
)
endif()
@ -136,7 +156,6 @@ include(${pal_dir}/PAL_traits_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_LARGE_WORLDS_TEST_SUPPORTED)
## DynVeg ##
ly_add_pytest(
NAME DynamicVegetationTests_Main_NoGPU
TEST_SERIAL
@ -150,6 +169,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
AutomatedTesting.GameLauncher
AutomatedTesting.Assets
Legacy::CryRenderNULL
COMPONENT
LargeWorlds
)
ly_add_pytest(
@ -165,6 +186,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
AutomatedTesting.GameLauncher
AutomatedTesting.Assets
Legacy::CryRenderNULL
COMPONENT
LargeWorlds
)
ly_add_pytest(
@ -179,10 +202,11 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
Legacy::Editor
AutomatedTesting.Assets
Legacy::CryRenderNULL
COMPONENT
LargeWorlds
)
## LandscapeCanvas ##
ly_add_pytest(
NAME LandscapeCanvasTests_Main
TEST_SERIAL
@ -194,6 +218,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
Legacy::Editor
AutomatedTesting.Assets
Legacy::CryRenderNULL
COMPONENT
LargeWorlds
)
## GradientSignal ##
@ -209,6 +235,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
Legacy::Editor
AutomatedTesting.Assets
Legacy::CryRenderNULL
COMPONENT
LargeWorlds
)
ly_add_pytest(
@ -222,6 +250,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
Legacy::Editor
AutomatedTesting.Assets
Legacy::CryRenderNULL
COMPONENT
LargeWorlds
)
endif()
@ -239,6 +269,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
Legacy::CryRenderNULL
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Editor
)
endif()

@ -24,7 +24,7 @@ from . import ap_setup_fixture as ap_setup_fixture
def ap_all_platforms_setup_fixture(request, workspace, ap_setup_fixture) -> Dict[str, str]:
dev_dir = os.path.join(workspace.paths.dev())
cache_dir = os.path.join(dev_dir, "Cache")
cache_dir = workspace.paths.cache()
# add some useful locations
resources = ap_setup_fixture
@ -33,11 +33,11 @@ def ap_all_platforms_setup_fixture(request, workspace, ap_setup_fixture) -> Dict
resources["platform_cache"] = os.path.join(workspace.paths.platform_cache(), workspace.project.lower())
# Specific platform cache locations
resources["pc_cache_location"] = os.path.join(cache_dir, workspace.project, "pc")
resources["es3_cache_location"] = os.path.join(cache_dir, workspace.project, "es3")
resources["ios_cache_location"] = os.path.join(cache_dir, workspace.project, "ios")
resources["osx_gl_cache_location"] = os.path.join(cache_dir, workspace.project, "osx_gl")
resources["provo_cache_location"] = os.path.join(cache_dir, workspace.project, "provo")
resources["pc_cache_location"] = os.path.join(cache_dir, "pc")
resources["es3_cache_location"] = os.path.join(cache_dir, "es3")
resources["ios_cache_location"] = os.path.join(cache_dir, "ios")
resources["osx_gl_cache_location"] = os.path.join(cache_dir, "osx_gl")
resources["provo_cache_location"] = os.path.join(cache_dir, "provo")
resources["all_platforms"] = ["pc", "es3", "ios", "osx_gl", "provo"]
return resources

@ -52,7 +52,7 @@ def ap_external_project_setup_fixture(request, workspace) -> Dict:
paths = mock()
paths.asset_processor = lambda: resources["ap_path"]
paths.asset_processor_batch = lambda: resources["ap_batch_path"]
paths.asset_processor_config_file = lambda: os.path.join(resources["project_dir"], "AssetProcessorConfig.ini")
paths.asset_processor_config_file = lambda: os.path.join(resources["project_dir"], "AssetProcessorConfig.setreg")
mock_workspace.paths = paths
resources["external_workspace"] = mock_workspace

@ -163,7 +163,7 @@ class TestsAssetProcessorBatch_AllPlatforms(object):
assert errors == 0, f"There were {errors} asset processing errors"
# Check that project cache was created (DNE until AP makes it)
project_cache = os.path.join(external_resources["project_dir"], "Cache", external_resources["project_name"])
project_cache = os.path.join(external_resources["project_dir"], "Cache")
assert os.path.exists(project_cache), f"{project_cache} was not created by AP"
# Clean up external project
@ -273,7 +273,8 @@ class TestsAssetProcessorBatch_Windows(object):
"AssetProcessor: Error: Platform in config file or command line 'notaplatform'" should be present in the logs
"""
asset_processor.create_temp_asset_root()
error_search_terms = "AssetProcessor: Error: Platform in config file or command line 'notaplatform'"
error_search_terms = 'AssetProcessor: Error: The list of enabled platforms in the settings registry does not contain platform ' \
'"notaplatform"'
# Run APBatch expecting it to fail
asset_processor.run_and_check_output(True, error_search_terms, platforms='notaplatform')

@ -25,7 +25,7 @@ import ly_test_tools.environment.waiter as waiter
import ly_test_tools.environment.file_system as fs
import ly_test_tools.environment.process_utils as process_utils
import ly_test_tools.launchers.launcher_helper as launcher_helper
from ly_test_tools.lumberyard.asset_processor import ASSET_PROCESSOR_PLATFORM_MAP
from ly_test_tools.lumberyard.asset_processor import ASSET_PROCESSOR_PLATFORM_MAP, ASSET_PROCESSOR_SETTINGS_ROOT_KEY
# Import fixtures
from ..ap_fixtures.asset_processor_fixture import asset_processor as asset_processor
@ -136,7 +136,7 @@ class TestsAssetProcessorGUI_WindowsAndMac(object):
# Validate that no fatal errors (crashes) are reported within a certain time frame (10 seconds timeout)
# This applies to AP and GameLauncher.exe
time.sleep(CHECK_ALIVE_SECONDS)
launcher_name = f"{workspace.project.title()}Launcher"
launcher_name = f"{workspace.project.title()}.GameLauncher"
# fmt:off
assert process_utils.process_exists(launcher_name, ignore_extensions=True), \
f"{launcher_name} was not live during the check."
@ -221,23 +221,20 @@ class TestsAssetProcessorGUI_AllPlatforms(object):
assert not test_assets_added_to_cache(), "Test assets are present in cache before adding scan folder"
# Add test assets folder in dev to AP config file (AssetProcessorPlatformConfig.ini) to be scanned
ap_config_file = os.path.join(asset_processor.temp_asset_root(), 'AssetProcessorPlatformConfig.ini')
config = configparser.ConfigParser()
config.read(ap_config_file)
config["ScanFolder C4874115"] = {
"watch": "@ROOT@/C4874115",
"output": "C4874115",
"recursive": "1",
"order": "5000",
}
with open(ap_config_file, "w") as configfile:
config.write(configfile)
# Supply an additional scan folder for the test via the settings registry regset parameters
test_scan_folder_params = []
test_scan_folder_root_key = f"{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/ScanFolder C4874115"
test_scan_folder_params.append(f'--regset="{test_scan_folder_root_key}/watch=@ROOT@/C4874115"')
test_scan_folder_params.append(f'--regset="{test_scan_folder_root_key}/output=C4874115"')
test_scan_folder_params.append(f'--regset="{test_scan_folder_root_key}/recursive=1"')
test_scan_folder_params.append(f'--regset="{test_scan_folder_root_key}/order=5000"')
# Run AP GUI and read the config file we just modified to pick up our scan folder
# Pass in a pattern so we don't spend time processing unrelated folders
result, _ = asset_processor.gui_process(quitonidle=True, add_config_scan_folders=True,
scan_folder_pattern="*C4874115*")
scan_folder_pattern="*C4874115*",
extra_params=test_scan_folder_params)
assert result, "AP GUI failed"
# Verify test assets processed into cache after adding scan folder
@ -255,10 +252,9 @@ class TestsAssetProcessorGUI_AllPlatforms(object):
test_ip_address = "1.1.1.1" # an IP address without Asset Processor
asset_processor.create_temp_asset_root()
# Edit remote_ip setting in bootstrap.cfg to an IP address without Asset Processor
workspace.settings.modify_bootstrap_setting("remote_ip", test_ip_address,
bootstrap_path=os.path.join(asset_processor.temp_asset_root(),
'bootstrap.cfg'))
# Set the remote_ip setting through the settings registry to an IP address without Asset Processor
extra_params = []
extra_params.append(f'--regset="/Amazon/AzCore/Bootstrap/remote_ip={test_ip_address}"')
# Run AP Gui to verify that assets process regardless of the new address
result, _ = asset_processor.gui_process(quitonidle=True)

@ -64,7 +64,7 @@ class TestsMissingDependencies_WindowsAndMac(object):
self._missing_dep_helper = missing_dep_helper
self._asset_processor.create_temp_asset_root()
self._asset_processor.add_source_folder_assets(f"AutomatedTesting\\TestAssets")
missing_dep_helper.asset_db = os.path.join(asset_processor.temp_asset_root(), "Cache", workspace.project,
missing_dep_helper.asset_db = os.path.join(asset_processor.temp_asset_root(), "Cache",
"assetdb.sqlite")
self._asset_processor.add_source_folder_assets(f"{self._workspace.project}\\Slices")
self._asset_processor.add_source_folder_assets(f"{self._workspace.project}\\Materials")

@ -597,9 +597,9 @@ class TestsFBX_AllPlatforms(object):
# Load the asset database.
db_path = os.path.join(asset_processor.temp_asset_root(), "Cache",
workspace.project, "assetdb.sqlite")
"assetdb.sqlite")
cache_root = os.path.dirname(os.path.join(asset_processor.temp_asset_root(), "Cache",
workspace.project, ASSET_PROCESSOR_PLATFORM_MAP[workspace.asset_processor_platform]))
ASSET_PROCESSOR_PLATFORM_MAP[workspace.asset_processor_platform]))
if blackbox_params.scene_debug_file:
debug_graph_path = os.path.join(asset_processor.project_test_cache_folder(), blackbox_params.scene_debug_file)

@ -131,8 +131,9 @@ class TestAutomationBase:
crash_info = f.read()
except Exception as ex:
crash_info += f"\n{str(ex)}"
error_str = f"Editor.exe crashed, return code: 0x{return_code:0X}\n\nCrash log:\n{crash_info}"
return_code_str = f"0x{return_code:0X}" if isinstance(return_code, int) else "None"
error_str = f"Editor.exe crashed, return code: {return_code_str}\n\nCrash log:\n{crash_info}"
errors.append(TestRunError("CRASH", error_str))
self.test_times[testcase_name] = time.time() - test_starttime

@ -19,16 +19,11 @@ sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesti
from base import TestAutomationBase
@pytest.mark.SUITE_main
@pytest.mark.SUITE_periodic
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def test_Opening_Closing_Pane(self, request, workspace, editor, launcher_platform):
from . import Opening_Closing_Pane as test_module
self._run_test(request, workspace, editor, test_module)
def test_Docking_Pane(self, request, workspace, editor, launcher_platform):
from . import Docking_Pane as test_module
self._run_test(request, workspace, editor, test_module)
@ -36,4 +31,3 @@ class TestAutomation(TestAutomationBase):
def test_Resizing_Pane(self, request, workspace, editor, launcher_platform):
from . import Resizing_Pane as test_module
self._run_test(request, workspace, editor, test_module)

@ -0,0 +1,30 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import pytest
import os
import sys
from ly_test_tools import LAUNCHERS
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
@pytest.mark.SUITE_sandbox
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def test_Opening_Closing_Pane(self, request, workspace, editor, launcher_platform):
from . import Opening_Closing_Pane as test_module
self._run_test(request, workspace, editor, test_module)

@ -3,15 +3,45 @@
{
"Gems":
{
"AutomatedTesting.Assets":
"DevTextures":
{
"SourcePaths":
[
"Gems/DevTextures"
]
},
"PBSreferenceMaterials":
{
"SourcePaths":
[
"Gems/PBSreferenceMaterials"
]
},
"PhysicsEntities":
{
"SourcePaths":
[
"Gems/PhysicsEntities"
]
},
"PhysXSamples":
{
"SourcePaths":
[
"Gems/PhysXSamples"
]
},
"PrimitiveAssets":
{
"SourcePaths":
[
"Gems/PrimitiveAssets"
]
},
"UiBasics":
{
"SourcePaths":
[
"Gems/DevTextures",
"Gems/PBSreferenceMaterials",
"Gems/PhysicsEntities",
"Gems/PhysXSamples",
"Gems/PrimitiveAssets",
"Gems/UiBasics"
]
}

@ -10,6 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import azlmbr.scene as sceneApi
from scene_api import scene_data as sceneData
import uuid
def update_manifest(scene):
graph = sceneData.SceneGraph(scene.graph)
@ -27,7 +28,9 @@ def update_manifest(scene):
sceneManifest = sceneData.SceneManifest()
for activeMeshIndex in range(len(chunkNameList)):
meshGroup = sceneManifest.add_mesh_group(chunkNameList[activeMeshIndex])
chunkName = chunkNameList[activeMeshIndex]
meshGroup = sceneManifest.add_mesh_group(chunkName)
meshGroup['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, scene.sourceFilename + chunkName)) + '}'
sceneManifest.mesh_group_add_comment(meshGroup, 'auto generated by test_chunks_builder')
sceneManifest.mesh_group_set_origin(meshGroup, None, 0, 0, 0, 1.0)
for meshIndex in range(len(chunkNameList)):

@ -1,2 +1,2 @@
../Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/*map.tif
@engroot@/Gems/LyShineExamples/Assets/UI/Textures/LyShineExamples/*map.tif

@ -1,298 +0,0 @@
{
"GemListFormatVersion": 2,
"Gems": [
{
"Path": "Gems/EMotionFX",
"Uuid": "044a63ea67d04479aa5daf62ded9d9ca",
"Version": "0.1.0",
"_comment": "EMotionFX"
},
{
"Path": "Gems/PBSreferenceMaterials",
"Uuid": "07375b61b1a2424bb03088bbdf28b2c8",
"Version": "0.1.0",
"_comment": "PBSreferenceMaterials"
},
{
"Path": "Gems/GraphModel",
"Uuid": "0844f64a3acf4f5abf3a535dc9b63bc9",
"Version": "0.1.0",
"_comment": "GraphModel"
},
{
"Path": "Gems/StartingPointInput",
"Uuid": "09f4bedeee614358bc36788e77f97e51",
"Version": "0.1.0",
"_comment": "StartingPointInput"
},
{
"Path": "Gems/LyShine",
"Uuid": "0fefab3f13364722b2eab3b96ce2bf20",
"Version": "0.1.0",
"_comment": "LyShine"
},
{
"Path": "Gems/LandscapeCanvas",
"Uuid": "19c2b2d5018940108baf252934b8e6bf",
"Version": "0.1.0",
"_comment": "LandscapeCanvas"
},
{
"Path": "Gems/ScriptCanvasPhysics",
"Uuid": "1c27519a4dda4ffaaeebf91514e5b1e8",
"Version": "0.1.0",
"_comment": "ScriptCanvasPhysics"
},
{
"Path": "Gems/HttpRequestor",
"Uuid": "28479e255bde466e91fc34eec808d9c7",
"Version": "1.0.0",
"_comment": "HttpRequestor"
},
{
"Path": "Gems/DevTextures",
"Uuid": "2c227161447b4d77a5b07c093e214fe3",
"Version": "0.1.0",
"_comment": "DevTextures"
},
{
"Path": "Gems/ScriptEvents",
"Uuid": "32d8ba21703e4bbbb08487366e48dd69",
"Version": "0.1.0",
"_comment": "ScriptEvents"
},
{
"Path": "Gems/Maestro",
"Uuid": "3b9a978ed6f742a1acb99f74379a342c",
"Version": "0.1.0",
"_comment": "Maestro"
},
{
"Path": "Gems/ExpressionEvaluation",
"Uuid": "4c6f9df57ca2468f93c8d860ee6a1167",
"Version": "0.1.0",
"_comment": "ExpressionEvaluation"
},
{
"Path": "Gems/PhysX",
"Uuid": "4e08125824434932a0fe3717259caa47",
"Version": "0.1.0",
"_comment": "PhysX"
},
{
"Path": "Gems/PhysXDebug",
"Uuid": "516145e2d9904b13813f1b54605e26a6",
"Version": "0.1.0",
"_comment": "PhysXDebug"
},
{
"Path": "Gems/CameraFramework",
"Uuid": "54f2763fe191432fa681ce4a354eedf5",
"Version": "0.1.0",
"_comment": "CameraFramework"
},
{
"Path": "Gems/TextureAtlas",
"Uuid": "5a149b6b3c964064bd4970f0e92f72e2",
"Version": "0.1.0",
"_comment": "TextureAtlas"
},
{
"Path": "Gems/SurfaceData",
"Uuid": "5de82d29d6094bfe97c1a4d35fcd5fbe",
"Version": "0.1.0",
"_comment": "SurfaceData"
},
{
"Path": "Gems/AssetValidation",
"Uuid": "5a5c3c10b91d4b4ea8baef474c5b5d49",
"Version": "0.1.0",
"_comment": "AssetValidation"
},
{
"Path": "Gems/Gestures",
"Uuid": "6056556b6088413984309c4a413593ad",
"Version": "1.0.0",
"_comment": "Gestures"
},
{
"Path": "Gems/CertificateManager",
"Uuid": "659cffff33b14a10835bafc6ea623f98",
"Version": "0.0.1",
"_comment": "CertificateManager"
},
{
"Path": "Gems/DebugDraw",
"Uuid": "66239f50bf754354b514c850c8b841fb",
"Version": "0.1.0",
"_comment": "DebugDraw"
},
{
"Path": "Gems/UiBasics",
"Uuid": "6bb61c9e547043f0afc5019d6d460b78",
"Version": "0.1.0",
"_comment": "UiBasics"
},
{
"Path": "Gems/AudioSystem",
"Uuid": "6f63f2b6d07f4b89b4b7c86ebee7feb8",
"Version": "0.1.0",
"_comment": "AudioSystem"
},
{
"Path": "Gems/StartingPointMovement",
"Uuid": "73d8779dc28a4123b7c9ed76217464af",
"Version": "0.1.0",
"_comment": "StartingPointMovement"
},
{
"Path": "Gems/GameLift",
"Uuid": "76de765796504906b73be7365a9bff06",
"Version": "2.0.0",
"_comment": "GameLift"
},
{
"Path": "Gems/SceneProcessing",
"Uuid": "7c2578f634df4345aca98d671e39b8ab",
"Version": "0.1.0",
"_comment": "SceneProcessing"
},
{
"Path": "Gems/StartingPointCamera",
"Uuid": "834070b9537d44df83559e2045c3859f",
"Version": "0.1.0",
"_comment": "StartingPointCamera"
},
{
"Path": "Gems/ScriptCanvas",
"Uuid": "869a0d0ec11a45c299917d45c81555e6",
"Version": "0.1.0",
"_comment": "ScriptCanvasGem"
},
{
"Path": "Gems/GraphCanvas",
"Uuid": "875b6fcbdeea44deaae7984ad9bb6cdc",
"Version": "0.1.0",
"_comment": "GraphCanvas"
},
{
"Path": "Gems/GradientSignal",
"Uuid": "8825563d9d964ec3be3bab681f3bd9f2",
"Version": "0.1.0",
"_comment": "GradientSignal"
},
{
"Path": "Gems/InAppPurchases",
"Uuid": "92fe57eae7d3402a90761973678c079a",
"Version": "0.1.0",
"_comment": "InAppPurchases"
},
{
"Path": "Gems/PhysicsEntities",
"Uuid": "99ea531451fc4f64a5a9fe8f385e8a76",
"Version": "0.1.0",
"_comment": "PhysicsEntities"
},
{
"Path": "AutomatedTesting/Gem",
"Uuid": "afc25e1593194d6283d9ff744ab6d5a1",
"Version": "0.1.0"
},
{
"Path": "Gems/EditorPythonBindings",
"Uuid": "b658359393884c4381c2fe2952b1472a",
"Version": "0.1.0",
"_comment": "EditorPythonBindings"
},
{
"Path": "Gems/ImGui",
"Uuid": "bab8807a1bc646b3909f3cc200ffeedf",
"Version": "0.1.0",
"_comment": "ImGui"
},
{
"Path": "Gems/ChatPlay",
"Uuid": "bfbc60c63ffd4b00927003735b26ce99",
"Version": "0.1.0",
"_comment": "ChatPlay"
},
{
"Path": "Gems/Metastream",
"Uuid": "c02d7efe05134983b5699d9ee7594c3a",
"Version": "1.0.0",
"_comment": "Metastream"
},
{
"Path": "Gems/PhysXSamples",
"Uuid": "c4a4aadba44241ae9f0141e145def7f7",
"Version": "0.1.0",
"_comment": "PhysXSamples"
},
{
"Path": "Gems/Water",
"Uuid": "c5083fcf89b24ab68fb0611c01a07b1d",
"Version": "0.1.0",
"_comment": "Water"
},
{
"Path": "Gems/WhiteBox",
"Uuid": "c5833dbda2e045d3a5f16b7414280c27",
"Version": "0.1.0",
"_comment": "WhiteBox"
},
{
"Path": "Gems/TouchBending",
"Uuid": "c58d2057f3724b22ae0df0be68a4e316",
"Version": "0.1.0",
"_comment": "TouchBending"
},
{
"Path": "Gems/FastNoise",
"Uuid": "c5f23032407f49ca8d8de1733423565c",
"Version": "0.1.0",
"_comment": "FastNoise"
},
{
"Path": "Gems/LyShineExamples",
"Uuid": "c7935ecf5e8047fe8ca947b34b11cadb",
"Version": "0.1.0",
"_comment": "LyShineExamples"
},
{
"Path": "Gems/PrimitiveAssets",
"Uuid": "ed07631f95fb4be1bd10cd37298ec697",
"Version": "1.0.0",
"_comment": "PrimitiveAssets"
},
{
"Path": "Gems/ImageProcessing",
"Uuid": "eeffbd9211cf4ce0b5cc73696b427cbe",
"Version": "0.1.0",
"_comment": "ImageProcessing"
},
{
"Path": "Gems/Vegetation",
"Uuid": "f394e7cf54424bba89615381bba9511b",
"Version": "0.1.0",
"_comment": "Vegetation"
},
{
"Path": "Gems/TestAssetBuilder",
"Uuid": "f5c92f1560714010ba30467d93feecef",
"Version": "0.1.0",
"_comment": "TestAssetBuilder"
},
{
"Path": "Gems/Camera",
"Uuid": "f910686b6725452fbfc4671f95f733c6",
"Version": "0.1.0",
"_comment": "Camera"
},
{
"Path": "Gems/LmbrCentral",
"Uuid": "ff06785f7145416b9d46fde39098cb0c",
"Version": "0.1.0",
"_comment": "LmbrCentral"
}
]
}

@ -24,12 +24,12 @@ def printMessage(message):
def getCurrentProject():
bootstrap = open("bootstrap.cfg", "r")
gameProjectRegex = re.compile("^sys_game_folder\s*=\s*(.*)")
gameProjectRegex = re.compile(r"^project_path\s*=\s*(.*)")
for line in bootstrap:
gameFolderMatch = gameProjectRegex.match(line)
if gameFolderMatch:
return gameFolderMatch.group(1)
return os.path.basename(gameFolderMatch.group(1))
return None

@ -24,7 +24,7 @@ endif()
include(cmake/Version.cmake)
if(NOT PROJECT_NAME)
project(Lumberyard
project(O3DE
LANGUAGES C CXX
VERSION ${LY_VERSION_STRING}
)
@ -83,13 +83,30 @@ endif()
# Post-processing
################################################################################
# Loop over the additional external subdirectories and invoke add_subdirectory on them
foreach(external_directory ${LY_EXTERNAL_SUBDIRS})
# Hash the extenal_directory name and append it to the Binary Directory section of add_subdirectory
# This is to deal with potential situations where multiple external directories has the same last directory name
# For example if D:/Company1/RayTracingGem and F:/Company2/Path/RayTracingGem were both added as a subdirectory
file(REAL_PATH ${external_directory} full_directory_path)
string(SHA256 full_directory_hash ${full_directory_path})
# Truncate the full_directory_hash down to 8 characters to avoid hitting the Windows 260 character path limit
# when the external subdirectory contains relative paths of significant length
string(SUBSTRING ${full_directory_hash} 0 8 full_directory_hash)
# Use the last directory as the suffix path to use for the Binary Directory
get_filename_component(directory_name ${external_directory} NAME)
add_subdirectory(${external_directory} ${CMAKE_BINARY_DIR}/${directory_name}-${full_directory_hash})
endforeach()
# The following steps have to be done after all targets were registered:
# 1. link targets where the dependency was yet not declared, we need to have the declaration so we do different
# linking logic depending on the type of target
ly_delayed_target_link_libraries()
# 2. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls
# 1. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls
# to provide applications with the filenames of gem modules to load
# This must be done before ly_delayed_target_link_libraries() as that inserts BUILD_DEPENDENCIE as MANUALLY_ADDED_DEPENDENCIES
# if the build dependency is a MODULE_LIBRARY. That would cause a false load dependency to be generated
ly_delayed_generate_settings_registry()
# 2. link targets where the dependency was yet not declared, we need to have the declaration so we do different
# linking logic depending on the type of target
ly_delayed_target_link_libraries()
# 3. generate a registry file for unit testing for platforms that support unit testing
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_delayed_generate_unit_test_module_registry()

@ -1080,7 +1080,7 @@ void C3DEngine::WorldStreamUpdate()
if (GetCVars()->e_StreamSaveStartupResultsIntoXML)
{
const char* testResultsFile = "@cache@/TestResults/Streaming_Level_Start_Throughput.xml";
const char* testResultsFile = "@usercache@/TestResults/Streaming_Level_Start_Throughput.xml";
AZ::IO::HandleType resultsFile = gEnv->pCryPak->FOpen(testResultsFile, "wb");
if (resultsFile != AZ::IO::InvalidHandle)

@ -952,7 +952,7 @@ void C3DEngine::ProcessStreamingLatencyTest(const CCamera& camIn, CCamera& camOu
if (GetCVars()->e_SQTestCount == 0)
{
const char* testResultsFile = "@cache@/TestResults/Streaming_Latency_Test.xml";
const char* testResultsFile = "@usercache@/TestResults/Streaming_Latency_Test.xml";
AZ::IO::HandleType resultsFile = gEnv->pCryPak->FOpen(testResultsFile, "wb");
if (resultsFile != AZ::IO::InvalidHandle)

@ -461,7 +461,7 @@ void C3DEngine::UnloadLevel()
m_ptexIconLowMemoryUsage = nullptr;
m_ptexIconHighMemoryUsage = nullptr;
m_ptexIconEditorConnectedToConsole = nullptr;
if (m_pOpticsManager && !gEnv->IsEditor())
{
m_pOpticsManager->Reset();
@ -656,7 +656,7 @@ bool C3DEngine::LoadLevel(const char* szFolderName, const char* szMissionName)
// set default render parameters.
// for some reason this is not done later???
m_pSkyLightManager->UpdateRenderParams();
}
}
// Load LevelData.xml File.
XmlNodeRef xmlLevelData = GetSystem()->LoadXmlFromFile(GetLevelFilePath(LEVEL_DATA_FILE));
@ -687,17 +687,7 @@ bool C3DEngine::LoadLevel(const char* szFolderName, const char* szMissionName)
// preload level cgfs
if (GetCVars()->e_StatObjPreload && !gEnv->IsEditor())
{
if (GetCVars()->e_StatObjPreload == 2)
{
GetSystem()->OutputLoadingTimeStats();
}
m_pObjManager->PreloadLevelObjects();
if (GetCVars()->e_StatObjPreload == 2)
{
GetSystem()->OutputLoadingTimeStats();
}
}
std::vector<struct IStatObj*>* pStatObjTable = NULL;

@ -317,7 +317,8 @@ int CLightEntity::UpdateGSMLightSourceDynamicShadowFrustum(int nDynamicLodCount,
if (bDoGSM)
{
Vec3 vSunDir = Get3DEngine()->GetSunDir().GetNormalized(); // todo: remove GetNormalized() once GetSunDir() returns the normalized value
//Vec3 vSunDir = Get3DEngine()->GetSunDir().GetNormalized(); // todo: remove GetNormalized() once GetSunDir() returns the normalized value
Vec3 vSunDir = Vec3(1.0f, 0.0f, 0.0f);
Vec3 vCameraDirWithoutDepth = vCameraDir - vCameraDir.Dot(vSunDir) * vSunDir;
Vec3 vFocusPos = passInfo.GetCamera().GetPosition() + vCameraDirWithoutDepth * fGSMBoxSize;

@ -117,10 +117,6 @@ CMatInfo::CMatInfo()
ZeroStruct(m_streamZoneInfo);
#ifdef TRACE_MATERIAL_LEAKS
m_sLoadingCallstack = GetSystem()->GetLoadingProfilerCallstack();
#endif
// Used to know when a .dccmtl file has been changed,
// requiring the source material to be updated
m_dccMaterialHash = 0;
@ -341,7 +337,7 @@ ISurfaceType* CMatInfo::GetSurfaceType()
//////////////////////////////////////////////////////////////////////////
void CMatInfo::SetSubMtlCount(int numSubMtl)
{
AUTO_LOCK(GetSubMaterialResizeLock());
AUTO_LOCK(GetSubMaterialResizeLock());
if (numSubMtl > 0)
{
m_Flags |= MTL_FLAG_MULTI_SUBMTL;
@ -1112,15 +1108,6 @@ void CMatInfo::SetTexelDensityDebug([[maybe_unused]] int mode)
#endif
}
const char* CMatInfo::GetLoadingCallstack()
{
#ifdef TRACE_MATERIAL_LEAKS
return m_sLoadingCallstack.c_str();
#else
return "";
#endif
}
void CMatInfo::PrecacheMaterial(const float _fEntDistance, IRenderMesh* pRenderMesh, bool bFullUpdate, bool bDrawNear)
{
// FUNCTION_PROFILER_3DENGINE;

@ -18,7 +18,6 @@
#include <IMaterial.h>
#if !defined(CONSOLE)
# define TRACE_MATERIAL_LEAKS
# define SUPPORT_MATERIAL_EDITING
#endif
@ -203,7 +202,7 @@ public:
bool SetGetMaterialParamFloat(const char* sParamName, float& v, bool bGet, bool allowShaderParam = false, int materialIndex = 0) override;
bool SetGetMaterialParamVec3(const char* sParamName, Vec3& v, bool bGet, bool allowShaderParam = false, int materialIndex = 0) override;
bool SetGetMaterialParamVec4(const char* sParamName, Vec4& v, bool bGet, bool allowShaderParam = false, int materialIndex = 0) override;
void SetDirty(bool dirty = true) override;
bool IsDirty() const override;
@ -264,11 +263,6 @@ public:
bool IsForwardRenderingRequired();
bool IsNearestCubemapRequired();
//////////////////////////////////////////////////////////////////////////
// Debug routines
//////////////////////////////////////////////////////////////////////////
virtual const char* GetLoadingCallstack(); // trace leaking materials by callstack
void DisableTextureStreaming() override;
virtual void RequestTexturesLoading(const float fMipFactor);
@ -286,13 +280,6 @@ public:
void SetDccMaterialHash(uint32 hash) override { m_dccMaterialHash = hash; }
virtual CryCriticalSection& GetSubMaterialResizeLock();
public:
//////////////////////////////////////////////////////////////////////////
// for debug purposes
//////////////////////////////////////////////////////////////////////////
#ifdef TRACE_MATERIAL_LEAKS
string m_sLoadingCallstack;
#endif
private:
friend class CMatMan;

@ -16,7 +16,6 @@
#pragma once
#if !defined(CONSOLE)
# define TRACE_CGF_LEAKS
# define SUPPORT_TERRAIN_AO_PRE_COMPUTATIONS
#endif
@ -337,13 +336,6 @@ public:
std::vector<uint16> m_chunkBoneIds;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// for debug purposes
//////////////////////////////////////////////////////////////////////////
#ifdef TRACE_CGF_LEAKS
string m_sLoadingCallstack;
#endif
private:
//////////////////////////////////////////////////////////////////////////
// Sub objects.
@ -419,7 +411,7 @@ public:
virtual unsigned int GetBreakableByGame() { return m_bBreakableByGame; };
//Note: This function checks both the children and root data
//It should really be 'has any deformable objects'
//It should really be 'has any deformable objects'
//Should eventually be refactored as part of an eventual statobj refactor.
virtual bool IsDeformable() override;

@ -56,10 +56,6 @@ CStatObj::CStatObj()
m_fLodDistance = 0.0f;
Init();
#ifdef TRACE_CGF_LEAKS
m_sLoadingCallstack = GetSystem()->GetLoadingProfilerCallstack();
#endif
}
//////////////////////////////////////////////////////////////////////////

@ -1064,7 +1064,7 @@ namespace Audio
///////////////////////////////////////////////////////////////////////////////////////////
// Interface methods
virtual bool Initialize(const SSystemInitParams* initParams) = 0;
virtual bool Initialize() = 0;
virtual void Release() = 0;
};

@ -392,7 +392,7 @@ struct IMaterial
virtual void SetDirty(bool dirty = true) = 0;
virtual bool IsDirty() const = 0;
//! Returns true if the material is the parent of a group of materials
//! Returns true if the material is the parent of a group of materials
virtual bool IsMaterialGroup() const = 0;
//! Returns true if the material is a single material belongs to a material group
@ -411,11 +411,6 @@ struct IMaterial
// - 2, fast sketch mode.
virtual void SetSketchMode(int mode) = 0;
//////////////////////////////////////////////////////////////////////////
// Debug routines
//////////////////////////////////////////////////////////////////////////
virtual const char* GetLoadingCallstack() = 0; // trace leaking materials by callstack
// Sets FT_DONT_STREAM flag for all textures used by the material
// If a stream is already in process, this will stop the stream and flush the device texture
virtual void DisableTextureStreaming() = 0;

@ -119,8 +119,6 @@ namespace AZ
class IResourceCompilerHelper;
class CBootProfilerRecord;
namespace Serialization {
struct IArchiveHost;
}
@ -629,15 +627,7 @@ struct SSystemInitParams
void* hWnd; //
void* hWndForInputSystem; // the HWND for the input devices, distinct from the hWnd, which the rendering system overrides anyways
char remoteIP[256];
int remotePort;
bool remoteFileIO;
bool remoteResourceCompiler;
bool connectToRemote;
bool waitForConnection; // if true, wait for the remote connection to be established before proceeding to system init.
char assetsPlatform[64]; // what flavor of assets to load. Corresponds to those in rc.ini and asset processor ini
char gameFolderName[256]; // just the name. Not the full path.
char branchToken[12]; // information written by the assetprocessor which help determine whether the game/editor are running from the same branch or not
ILog* pLog; // You can specify your own ILog to be used by System.
ILogCallback* pLogCallback; // You can specify your own ILogCallback to be added on log creation (used by Editor).
@ -648,45 +638,6 @@ struct SSystemInitParams
IOutputPrintSink* pPrintSync; // Print Sync which can be used to catch all output from engine
char szSystemCmdLine[2048]; // Command line.
// set some paths before you create the system.
// rootPath - (REQUIRED) folder containing root. Must contain system.cfg or bootstrap.cfg basically.
// the remainder are optional and if specified should contain prefixes that can be prepended to any file to get to that location:
// READ ONLY!
char rootPath[256];
char rootPathCache[256];
// assetsPath - (REQUIRED) - where you assets live. The engine config parser will default this to @root@/gamename
// READ ONLY!
char assetsPath[256];
char assetsPathCache[256];
// userPath - (OPTIONAL) User path contains a folder for preferences persistent storage. May be persisted to the cloud (by things like IOS)
// If not specified, this is assumed @root@/User/
// WRITABLE
char userPath[256];
// cachePath - (OPTIONAL) a temporary store that can be erased at any time and does not need to be persisted
// on the cloud or anything like that. if not specified, this will be @user@/Cache
// WRITABLE
char cachePath[256];
// logPath - (OPTIONAL) a log path folder.
// If not specified, it will be @cache@/Logs
// WRITABLE
char logPath[256];
// the game should never use these values instead, the game should be using crypak or fileio with aliases:
// @root@ To get to the folder where system.cfg lives
// @assets@ to get to the folder containing game assets (textures and such) - by default, this is @root@/Gamename/
// @devroot@ to get to source files that are checked into source control (PC EDITOR ONLY!)
// @engroot@ to get to path to the engine root folder
// @user@ to access user store
// @cache@ to access temporary cache
// @log@ to access log file and other forensic storage
char szBinariesDir[256];
bool bEditor; // When running in Editor mode.
bool bPreview; // When running in Preview mode (Minimal initialization).
bool bTestMode; // When running in Automated testing mode.
@ -730,26 +681,7 @@ struct SSystemInitParams
hWnd = NULL;
hWndForInputSystem = NULL;
memset(rootPath, 0, sizeof(rootPath));
memset(rootPathCache, 0, sizeof(rootPathCache));
memset(userPath, 0, sizeof(userPath));
memset(assetsPath, 0, sizeof(assetsPath));
memset(assetsPathCache, 0, sizeof(assetsPathCache));
memset(cachePath, 0, sizeof(cachePath));
memset(logPath, 0, sizeof(logPath));
memset(gameFolderName, 0, sizeof(gameFolderName));
memset(branchToken, 0, sizeof(branchToken));
memset(remoteIP, 0, sizeof(remoteIP));
azstrcpy(remoteIP, sizeof(remoteIP), "127.0.0.1");
memset(assetsPlatform, 0, sizeof(assetsPlatform));
azstrcpy(assetsPlatform, sizeof(assetsPlatform), "pc");
remotePort = 45643;
remoteFileIO = false;
remoteResourceCompiler = false;
connectToRemote = false;
waitForConnection = false;
pLog = NULL;
pLogCallback = NULL;
@ -762,7 +694,6 @@ struct SSystemInitParams
pValidator = NULL;
pPrintSync = NULL;
memset(szSystemCmdLine, 0, sizeof(szSystemCmdLine));
memset(szBinariesDir, 0, sizeof(szBinariesDir));
bEditor = false;
bPreview = false;
@ -798,17 +729,6 @@ struct SSystemInitParams
pSharedEnvironment = nullptr;
}
bool UseAssetCache() const
{
#if defined(AZ_PLATFORM_WINDOWS) || defined(AZ_PLATFORM_MAC) || defined(AZ_PLATFORM_LINUX)
char checkPath[AZ_MAX_PATH_LEN] = { 0 };
azsnprintf(checkPath, AZ_MAX_PATH_LEN, "%s/engine.json", rootPathCache);
return AZ::IO::SystemFile::Exists(checkPath);
#else
return false;
#endif // defined(AZ_PLATFORM_WINDOWS) || AZ_TRAIT_OS_PLATFORM_APPLE
}
};
// Notes:
@ -1228,11 +1148,6 @@ struct ISystem
// Gets number of CPUs
virtual int GetLogicalCPUCount() = 0;
//! Get the 'kind' of assets you need to load - this describes the flavor of assets you are going to load
//! based on the platform you're on - so for example, android on ES3 will be 'es3' but android on opengl might load PC assets or others...
//! This is defined in bootstrap.cfg and is read-only during runtime.
virtual const char* GetAssetsPlatform() const = 0;
// Summary:
// Return the rendering driver name. GL or Metal
virtual const char* GetRenderingDriverName() const = 0;
@ -1406,43 +1321,6 @@ struct ISystem
// True if system running in Test mode.
virtual bool IsTestMode() const = 0;
//////////////////////////////////////////////////////////////////////////
// Loading time/memory profiling
//////////////////////////////////////////////////////////////////////////
// Summary:
// Starts function loading stats profiling.
virtual struct SLoadingTimeContainer* StartLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler, const char* szFuncName) = 0;
// Summary:
// Ends function loading stats profiling.
virtual void EndLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler) = 0;
// Summary:
// Starts function profiling with bootprofiler (session must be started).
virtual CBootProfilerRecord* StartBootSectionProfiler(const char* name, const char* args) = 0;
// Summary:
// Ends function profiling with bootprofiler.
virtual void StopBootSectionProfiler(CBootProfilerRecord* record) = 0;
// Summary:
// Starts frame session
virtual void StartBootProfilerSessionFrames(const char* pName) = 0;
// Summary:
// Stops frame session
virtual void StopBootProfilerSessionFrames() = 0;
// Summary:
// Prints loading stats into log.
virtual void OutputLoadingTimeStats() = 0;
// Summary:
// Starts function loading stats profiling.
virtual const char* GetLoadingProfilerCallstack() = 0;
//////////////////////////////////////////////////////////////////////////
// File version.
//////////////////////////////////////////////////////////////////////////
@ -1581,10 +1459,10 @@ struct ISystem
//////////////////////////////////////////////////////////////////////////
// Summary:
// Enable/Disable drawing the console
// Enable/Disable drawing the console
virtual void SetConsoleDrawEnabled(bool enabled) = 0;
// Enable/Disable drawing the UI
// Enable/Disable drawing the UI
virtual void SetUIDrawEnabled(bool enabled) = 0;
// Summary:
@ -1772,79 +1650,19 @@ struct DiskOperationInfo
#endif
#if defined(ENABLE_LOADING_PROFILER)
struct CLoadingTimeProfiler
{
CLoadingTimeProfiler(ISystem* pSystem, const char* szFuncName)
: m_pSystem(pSystem)
{
m_pSystem = pSystem;
m_pTimeContainer = m_pSystem->StartLoadingSectionProfiling(this, szFuncName);
}
~CLoadingTimeProfiler()
{
m_pSystem->EndLoadingSectionProfiling(this);
}
struct SLoadingTimeContainer* m_pTimeContainer;
double m_fConstructorTime;
double m_fConstructorMemUsage;
DiskOperationInfo m_constructorInfo;
ISystem* m_pSystem;
};
class CSYSBootProfileBlock
{
ISystem* m_pSystem;
CBootProfilerRecord* m_pRecord;
public:
CSYSBootProfileBlock(ISystem* pSystem, const char* name, const char* args = NULL)
: m_pSystem(pSystem)
{
if (m_pSystem)
{
m_pRecord = m_pSystem->StartBootSectionProfiler(name, args);
}
}
~CSYSBootProfileBlock()
{
if (m_pSystem)
{
m_pSystem->StopBootSectionProfiler(m_pRecord);
}
}
};
#ifdef AZ_PROFILE_TELEMETRY
#if defined(ENABLE_LOADING_PROFILER) && AZ_PROFILE_TELEMETRY
#define LOADING_TIME_PROFILE_SECTION AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzCore)
#define LOADING_TIME_PROFILE_SECTION_ARGS(...) AZ_PROFILE_SCOPE_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, __VA_ARGS__)
#define LOADING_TIME_PROFILE_SECTION_NAMED(sectionName) AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzCore, sectionName)
#define LOADING_TIME_PROFILE_SECTION_NAMED_ARGS(sectionName, args) AZ_PROFILE_SCOPE_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, sectionName, args)
#else
#define LOADING_TIME_PROFILE_SECTION CSYSBootProfileBlock AZ_JOIN(_profileBlockLine, __LINE__)(gEnv && gEnv->pSystem ? gEnv->pSystem : nullptr, __FUNCTION__);
#define LOADING_TIME_PROFILE_SECTION_ARGS(args) CSYSBootProfileBlock AZ_JOIN(_profileBlockLine, __LINE__)(gEnv->pSystem, __FUNCTION__, args);
#define LOADING_TIME_PROFILE_SECTION_NAMED(sectionName) CSYSBootProfileBlock AZ_JOIN(_profileBlockLine, __LINE__)(gEnv->pSystem, sectionName);
#define LOADING_TIME_PROFILE_SECTION_NAMED_ARGS(sectionName, args) CSYSBootProfileBlock AZ_JOIN(_profileBlockLine, __LINE__)(gEnv->pSystem, sectionName, args);
#endif // AZ_PROFILE_TELEMETRY
#define LOADING_TIME_PROFILE_SECTION_NAMED_ARGS(sectionName, ...) AZ_PROFILE_SCOPE_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, sectionName, __VA_ARGS__)
#else
#define LOADING_TIME_PROFILE_SECTION
#define LOADING_TIME_PROFILE_SECTION_ARGS(args)
#define LOADING_TIME_PROFILE_SECTION_ARGS(...)
#define LOADING_TIME_PROFILE_SECTION_NAMED(sectionName)
#define LOADING_TIME_PROFILE_SECTION_NAMED_ARGS(sectionName, args)
#define LOADING_TIME_PROFILE_SESSION_SECTION(sessionName)
#define LOADING_TIME_PROFILE_SESSION_START(sessionName)
#define LOADING_TIME_PROFILE_SESSION_STOP(sessionName)
#define LOADING_TIME_PROFILE_SECTION_NAMED_ARGS(sectionName, ...)
#endif
@ -1865,7 +1683,7 @@ extern SC_API SSystemGlobalEnvironment* gEnv;
inline ISystem* GetISystem()
{
// Some unit tests temporarily install and then uninstall ISystem* mocks.
// It is generally okay for runtime and tool systems which call this function to cache the returned pointer,
// It is generally okay for runtime and tool systems which call this function to cache the returned pointer,
// because their lifetime is usually shorter than the lifetime of the ISystem* implementation.
// It is NOT safe for this function to cache it as a static itself, though, as the static it would cache
// it inside may outlive the the actual instance implementing ISystem* when unit tests are torn down and then restarted.

@ -15,10 +15,12 @@
#include <AzCore/base.h>
#include <AzCore/IO/SystemFile.h> // for max path len
#include <ISystem.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <ISystem.h>
namespace MaterialUtils
{
//! UnifyMaterialName - given a non-unified material name, remove the extension, unify the slashes
@ -75,14 +77,11 @@ namespace MaterialUtils
static char cachedGameName[AZ_MAX_PATH_LEN] = { 0 };
if (!removals[removalSize - 1])
{
if ((gEnv) && (gEnv->pConsole))
auto projectName = AZ::Utils::GetProjectName();
if (!projectName.empty())
{
ICVar* pGameNameCVar = gEnv->pConsole->GetCVar("sys_game_folder");
if (pGameNameCVar)
{
azstrcpy(cachedGameName, AZ_MAX_PATH_LEN, pGameNameCVar->GetString());
azstrcat(cachedGameName, AZ_MAX_PATH_LEN, "/");
}
azstrcpy(cachedGameName, AZ_MAX_PATH_LEN, projectName.c_str());
azstrcat(cachedGameName, AZ_MAX_PATH_LEN, "/");
}
if (cachedGameName[0] == 0)

@ -55,8 +55,6 @@ public:
int());
MOCK_METHOD0(GetLogicalCPUCount,
int());
MOCK_CONST_METHOD0(GetAssetsPlatform,
const char*());
MOCK_CONST_METHOD0(GetRenderingDriverName,
const char*());
MOCK_METHOD1(DumpMemoryUsageStatistics,
@ -206,22 +204,6 @@ public:
bool());
MOCK_METHOD3(SetFrameProfiler,
void(bool on, bool display, char* prefix));
MOCK_METHOD2(StartLoadingSectionProfiling,
struct SLoadingTimeContainer*(CLoadingTimeProfiler * pProfiler, const char* szFuncName));
MOCK_METHOD1(EndLoadingSectionProfiling,
void(CLoadingTimeProfiler * pProfiler));
MOCK_METHOD2(StartBootSectionProfiler,
CBootProfilerRecord * (const char* name, const char* args));
MOCK_METHOD1(StopBootSectionProfiler,
void(CBootProfilerRecord * record));
MOCK_METHOD1(StartBootProfilerSessionFrames,
void(const char* pName));
MOCK_METHOD0(StopBootProfilerSessionFrames,
void());
MOCK_METHOD0(OutputLoadingTimeStats,
void());
MOCK_METHOD0(GetLoadingProfilerCallstack,
const char*());
MOCK_METHOD0(GetFileVersion,
const SFileVersion&());
MOCK_METHOD0(GetProductVersion,

@ -1,160 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include "ISystem.h"
#include <AzCore/base.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/Platform/PlatformDefaults.h>
// any of the following tags can be present in the bootstrap.cfg
// you can also prefix it with a platform.
// so for example, you can specify remote_ip alone to specify it for all platforms
// or you could specify android_remote_ip to change it for android only.
// the instructions are executed in the order that they appear, so you can set the default
// by using the non-platform-specific version, and then later on in the file you
// can override specific platforms.
#define CONFIG_KEY_FOR_REMOTEIP "remote_ip"
#define CONFIG_KEY_FOR_REMOTEPORT "remote_port"
#define CONFIG_KEY_FOR_GAMEFOLDER "sys_game_folder"
#define CONFIG_KEY_FOR_REMOTEFILEIO "remote_filesystem"
#define CONFIG_KEY_FOR_CONNECTTOREMOTE "connect_to_remote"
#define CONFIG_KEY_WAIT_FOR_CONNECT "wait_for_connect"
#define DEFAULT_GAMEDLL "EmptyTemplate"
#define DEFAULT_GAMEFOLDER "EmptyTemplate"
#define DEFAULT_REMOTEIP "127.0.0.1"
#define DEFAULT_REMOTEPORT 45643
#define CONFIG_KEY_FOR_ASSETS "assets"
#define CONFIG_KEY_FOR_BRANCHTOKEN "assetProcessor_branch_token"
//////////////////////////////////////////////////////////////////////////
class CEngineConfig
{
public:
string m_gameFolder; // folder only ("MyGame")
string m_assetPlatform; // what platform folder assets are from if more than one is available or using VFS ("pc" / "es3")
bool m_connectToRemote;
bool m_remoteFileIO;
bool m_waitForConnect;
string m_remoteIP;
int m_remotePort;
string m_rootFolder; // The engine root folder
string m_branchToken;
CEngineConfig([[maybe_unused]] const char** sourcePaths = nullptr, [[maybe_unused]] size_t numSearchPaths = 0, [[maybe_unused]] size_t numLevelsUp = 3)
: m_gameFolder(DEFAULT_GAMEFOLDER)
, m_connectToRemote(false)
, m_remoteFileIO(false)
, m_remotePort(DEFAULT_REMOTEPORT)
, m_waitForConnect(false)
, m_remoteIP(DEFAULT_REMOTEIP)
{
m_assetPlatform = AzFramework::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
AZ::SettingsRegistryInterface::FixedValueString gameFolder;
auto gameFolderKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, CONFIG_KEY_FOR_GAMEFOLDER);
if (settingsRegistry->Get(gameFolder, gameFolderKey))
{
m_gameFolder.assign(gameFolder.c_str(), gameFolder.size());
}
AZ::SettingsRegistryInterface::FixedValueString engineRoot;
if (settingsRegistry->Get(engineRoot, AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
{
m_rootFolder.assign(engineRoot.c_str(), engineRoot.size());
}
}
OnLoadSettings();
}
void CopyToStartupParams(SSystemInitParams& startupParams) const
{
startupParams.remoteFileIO = m_remoteFileIO;
startupParams.remotePort = m_remotePort;
startupParams.connectToRemote = m_connectToRemote;
startupParams.waitForConnection = m_waitForConnect;
azstrncpy(startupParams.remoteIP, sizeof(startupParams.remoteIP), m_remoteIP.c_str(), m_remoteIP.length() + 1); // +1 for the null terminator
azstrncpy(startupParams.assetsPlatform, sizeof(startupParams.assetsPlatform), m_assetPlatform.c_str(), m_assetPlatform.length() + 1); // +1 for the null terminator
azstrncpy(startupParams.rootPath, sizeof(startupParams.rootPath), m_rootFolder.c_str(), m_rootFolder.length() + 1); // +1 for the null terminator
azstrncpy(startupParams.gameFolderName, sizeof(startupParams.gameFolderName), m_gameFolder.c_str(), m_gameFolder.length() + 1); // +1 for the null terminator
azstrncpy(startupParams.branchToken, sizeof(startupParams.branchToken), m_branchToken.c_str(), m_branchToken.length() + 1); // +1 for the null terminator
// compute assets path based on game folder name
string gameFolderLower(m_gameFolder);
gameFolderLower.MakeLower();
azsnprintf(startupParams.assetsPath, sizeof(startupParams.assetsPath), "%s/%s", startupParams.rootPath, gameFolderLower.c_str());
// compute where the cache should be located
azsnprintf(startupParams.rootPathCache, sizeof(startupParams.rootPathCache), "%s/Cache/%s/%s", m_rootFolder.c_str(), m_gameFolder.c_str(), m_assetPlatform.c_str());
azsnprintf(startupParams.assetsPathCache, sizeof(startupParams.assetsPathCache), "%s/%s", startupParams.rootPathCache, gameFolderLower.c_str());
}
protected:
void OnLoadSettings()
{
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry == nullptr)
{
AZ_Warning("ParseEngineConfig", false, "Attempting to load configuration data while SettingsRegistry does not exist");
return;
}
AZ::SettingsRegistryInterface::FixedValueString settingsKeyPrefix = AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey;
AZ::SettingsRegistryInterface::FixedValueString settingsValueString;
AZ::s64 settingsValueInt{};
if (AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, settingsValueInt, settingsKeyPrefix, CONFIG_KEY_FOR_REMOTEFILEIO))
{
m_remoteFileIO = settingsValueInt != 0;
}
if (AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, settingsValueInt, settingsKeyPrefix, CONFIG_KEY_WAIT_FOR_CONNECT))
{
m_waitForConnect = settingsValueInt != 0;
}
if (AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, settingsValueInt, settingsKeyPrefix, CONFIG_KEY_FOR_CONNECTTOREMOTE))
{
m_connectToRemote = settingsValueInt != 0;
}
if (AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, settingsValueInt, settingsKeyPrefix, CONFIG_KEY_FOR_REMOTEPORT))
{
m_remotePort = aznumeric_cast<AZ::u16>(settingsValueInt);
}
if (settingsValueString = {};
AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, settingsValueString, settingsKeyPrefix, CONFIG_KEY_FOR_REMOTEIP))
{
m_remoteIP.assign(settingsValueString.c_str(), settingsValueString.size());
}
if (settingsValueString = {};
AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, settingsValueString, settingsKeyPrefix, CONFIG_KEY_FOR_ASSETS))
{
m_assetPlatform.assign(settingsValueString.c_str(), settingsValueString.size());
}
if (settingsValueString = {};
settingsRegistry->Get(settingsValueString, settingsKeyPrefix + "/" + CONFIG_KEY_FOR_BRANCHTOKEN))
{
m_branchToken.assign(settingsValueString.c_str(), settingsValueString.size());
}
}
};

@ -293,7 +293,7 @@ typedef uint32 vtx_idx;
#if defined(ENABLE_PROFILING_CODE)
# define USE_DISK_PROFILER
//# define ENABLE_LOADING_PROFILER // Not guaranteed to have enough slots for all the threads in the system
# define ENABLE_LOADING_PROFILER // requires AZ_PROFILE_TELEMETRY to also be defined
#endif
#if defined(SOFTCODE_ENABLED)

@ -110,7 +110,6 @@ set(FILES
AzDXGIFormat.h
SFunctor.h
FunctorBaseFunction.h
ParseEngineConfig.h
CustomMemoryHeap.h
FunctorBaseMember.h
stridedptr.h

@ -1,630 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#if defined(ENABLE_LOADING_PROFILER)
#include "BootProfiler.h"
#include "ThreadInfo.h"
#include <stack>
#include <AzFramework/IO/FileOperations.h>
namespace
{
StaticInstance<CBootProfiler, AZStd::no_destruct<CBootProfiler>> gProfilerInstance;
enum
{
eMAX_THREADS_TO_PROFILE = 128,
eNUM_RECORDS_PER_POOL = 2048, // so, eNUM_RECORDS_PER_POOL * sizeof(CBootProfilerRecord) == mem consumed by pool item
// sizeof(CProfileBlockTimes)==152,
// poolmem = 304Kb for 1 pool per thread
};
}
int CBootProfiler::CV_sys_bp_frames = 0;
float CBootProfiler::CV_sys_bp_time_threshold = 0;
class CProfileBlockTimes
{
protected:
LARGE_INTEGER m_startTimeStamp;
LARGE_INTEGER m_stopTimeStamp;
LARGE_INTEGER m_freq;
CProfileBlockTimes()
{
memset(&m_startTimeStamp, 0, sizeof(m_startTimeStamp));
memset(&m_stopTimeStamp, 0, sizeof(m_stopTimeStamp));
memset(&m_freq, 0, sizeof(m_freq));
}
};
class CBootProfilerRecord
{
public:
const char* m_label;
LARGE_INTEGER m_startTimeStamp;
LARGE_INTEGER m_stopTimeStamp;
LARGE_INTEGER m_freq;
CBootProfilerRecord* m_pParent;
typedef AZStd::vector<CBootProfilerRecord*> ChildVector;
ChildVector m_Childs;
CryFixedStringT<256> m_args;
ILINE CBootProfilerRecord(const char* label, LARGE_INTEGER timestamp, LARGE_INTEGER freq, const char* args)
: m_label(label)
, m_startTimeStamp(timestamp)
, m_freq(freq)
, m_pParent(NULL)
{
memset(&m_stopTimeStamp, 0, sizeof(m_stopTimeStamp));
if (args)
{
m_args = args;
}
}
ILINE ~CBootProfilerRecord()
{
// childs are allocated via pool as well, the destructors of each child
// is called explicitly, for the purpose of freeing memory occupied by
// m_Child vector. Otherwise there will be a memory leak.
ChildVector::iterator it = m_Childs.begin();
while (it != m_Childs.end())
{
(*it)->~CBootProfilerRecord();
++it;
}
}
void Print(AZ::IO::HandleType fileHandle, char* buf, size_t buf_size, size_t depth, LARGE_INTEGER stopTime, const char* threadName, const float timeThreshold)
{
if (m_stopTimeStamp.QuadPart == 0)
{
m_stopTimeStamp = stopTime;
}
const float time = (float)(m_stopTimeStamp.QuadPart - m_startTimeStamp.QuadPart) * 1000.f / (float)m_freq.QuadPart;
if (timeThreshold > 0.0f && time < timeThreshold)
{
return;
}
string tabs; //tabs(depth++, '\t')
tabs.insert(0, depth++, '\t');
{
string label = m_label;
label.replace("&", "&amp;");
label.replace("<", "&lt;");
label.replace(">", "&gt;");
label.replace("\"", "&quot;");
label.replace("'", "&apos;");
if (m_args.size() > 0)
{
m_args.replace("&", "&amp;");
m_args.replace("<", "&lt;");
m_args.replace(">", "&gt;");
m_args.replace("\"", "&quot;");
m_args.replace("'", "&apos;");
m_args.replace("%", "&#37;");
}
sprintf_s(buf, buf_size, "%s<block name=\"%s\" totalTimeMS=\"%f\" startTime=\"%" PRIu64 "\" stopTime=\"%" PRIu64 "\" args=\"%s\"> \n",
tabs.c_str(), label.c_str(), time, m_startTimeStamp.QuadPart, m_stopTimeStamp.QuadPart, m_args.c_str());
AZ::IO::Print(fileHandle, buf);
}
const size_t childsSize = m_Childs.size();
for (size_t i = 0; i < childsSize; ++i)
{
CBootProfilerRecord* record = m_Childs[i];
assert(record);
record->Print(fileHandle, buf, buf_size, depth, stopTime, threadName, timeThreshold);
}
sprintf_s(buf, buf_size, "%s</block>\n", tabs.c_str());
AZ::IO::Print(fileHandle, buf);
}
};
//////////////////////////////////////////////////////////////////////////
class CProfileInfo
{
friend class CBootProfilerSession;
private:
CBootProfilerRecord* m_pRoot;
CBootProfilerRecord* m_pCurrent;
public:
CProfileInfo()
: m_pRoot(NULL)
, m_pCurrent(NULL) {}
};
class CBootProfilerThreadsInterface
{
protected:
CBootProfilerThreadsInterface()
{
memset(m_threadInfo, 0, sizeof(m_threadInfo));
m_threadCounter = 0;
}
unsigned int GetThreadIndexByID(unsigned int threadID);
const char* GetThreadNameByIndex(unsigned int threadIndex);
int m_threadCounter;
private:
unsigned int m_threadInfo[eMAX_THREADS_TO_PROFILE]; //threadIDs
};
//////////////////////////////////////////////////////////////////////////
ILINE unsigned int CBootProfilerThreadsInterface::GetThreadIndexByID(unsigned int threadID)
{
for (int i = 0; i < eMAX_THREADS_TO_PROFILE; ++i)
{
if (m_threadInfo[i] == 0)
{
break;
}
if (m_threadInfo[i] == threadID)
{
return i;
}
}
unsigned int counter = CryInterlockedIncrement(&m_threadCounter) - 1; //count to index
m_threadInfo[counter] = threadID;
return counter;
}
ILINE const char* CBootProfilerThreadsInterface::GetThreadNameByIndex(unsigned int threadIndex)
{
assert(threadIndex < m_threadCounter);
const char* threadName = CryThreadGetName(m_threadInfo[threadIndex]);
return threadName;
}
class CRecordPool
{
public:
CRecordPool()
: m_baseAddr(NULL)
, m_allocCounter(0)
, m_next(NULL)
{
m_baseAddr = (CBootProfilerRecord*)CryModuleMemalign(eNUM_RECORDS_PER_POOL * sizeof(CBootProfilerRecord), 16);
}
~CRecordPool()
{
CryModuleMemalignFree(m_baseAddr);
delete m_next;
}
ILINE CBootProfilerRecord* allocateRecord()
{
if (m_allocCounter < eNUM_RECORDS_PER_POOL)
{
CBootProfilerRecord* newRecord = m_baseAddr + m_allocCounter;
++m_allocCounter;
return newRecord;
}
else
{
return NULL;
}
}
ILINE void setNextPool(CRecordPool* pool) { m_next = pool; }
private:
CBootProfilerRecord* m_baseAddr;
uint32 m_allocCounter;
CRecordPool* m_next;
};
class CBootProfilerSession
: public CBootProfilerThreadsInterface
, protected CProfileBlockTimes
{
public:
CBootProfilerSession();
~CBootProfilerSession();
void Start();
void Stop();
CBootProfilerRecord* StartBlock(const char* name, const char* args);
void StopBlock(CBootProfilerRecord* record);
void CollectResults(const char* filename, const float timeThreshold);
private:
string m_name;
CProfileInfo m_threadsProfileInfo[eMAX_THREADS_TO_PROFILE];
CRecordPool* m_threadsRecordsPool[eMAX_THREADS_TO_PROFILE]; //head
CRecordPool* m_threadsCurrentPools[eMAX_THREADS_TO_PROFILE]; //current
};
//////////////////////////////////////////////////////////////////////////
CBootProfilerSession::CBootProfilerSession()
{
memset(m_threadsProfileInfo, 0, sizeof(m_threadsProfileInfo));
memset(m_threadsRecordsPool, 0, sizeof(m_threadsRecordsPool));
memset(m_threadsCurrentPools, 0, sizeof(m_threadsCurrentPools));
}
CBootProfilerSession::~CBootProfilerSession()
{
for (unsigned int i = 0; i < m_threadCounter; ++i)
{
CProfileInfo& profile = m_threadsProfileInfo[i];
// Since m_pRoot is allocated using memory pool (line 296),
// its destructor is called explicitly to free the memory of
// m_Childs and each of its child.
if (profile.m_pRoot)
{
profile.m_pRoot->~CBootProfilerRecord();
}
delete m_threadsRecordsPool[i];
}
}
void CBootProfilerSession::Start()
{
LARGE_INTEGER time, freq;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&time);
m_startTimeStamp = time;
m_freq = freq;
}
void CBootProfilerSession::Stop()
{
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
m_stopTimeStamp = time;
}
CBootProfilerRecord* CBootProfilerSession::StartBlock(const char* name, const char* args)
{
const unsigned int curThread = CryGetCurrentThreadId();
const unsigned int threadIndex = GetThreadIndexByID(curThread);
assert(threadIndex < eMAX_THREADS_TO_PROFILE);
CProfileInfo& profile = m_threadsProfileInfo[threadIndex];
CRecordPool* pool = m_threadsCurrentPools[threadIndex];
if (!profile.m_pRoot)
{
if (!pool)
{
pool = new CRecordPool;
m_threadsRecordsPool[threadIndex] = pool;
m_threadsCurrentPools[threadIndex] = pool;
}
CBootProfilerRecord* rec = pool->allocateRecord();
profile.m_pRoot = profile.m_pCurrent = new(rec)CBootProfilerRecord("root", m_startTimeStamp, m_freq, args);
}
assert(pool);
LARGE_INTEGER time, freq;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&time);
CBootProfilerRecord* pParent = profile.m_pCurrent;
assert(pParent);
assert(profile.m_pRoot);
CBootProfilerRecord* rec = pool->allocateRecord();
if (!rec)
{
//pool is full, create a new one
pool = new CRecordPool;
m_threadsCurrentPools[threadIndex]->setNextPool(pool);
m_threadsCurrentPools[threadIndex] = pool;
rec = pool->allocateRecord();
}
profile.m_pCurrent = new(rec)CBootProfilerRecord(name, time, freq, args);
profile.m_pCurrent->m_pParent = pParent;
pParent->m_Childs.push_back(profile.m_pCurrent);
return profile.m_pCurrent;
}
void CBootProfilerSession::StopBlock(CBootProfilerRecord* record)
{
if (record)
{
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
record->m_stopTimeStamp = time;
unsigned int curThread = CryGetCurrentThreadId();
unsigned int threadIndex = GetThreadIndexByID(curThread);
assert(threadIndex < eMAX_THREADS_TO_PROFILE);
CProfileInfo& profile = m_threadsProfileInfo[threadIndex];
profile.m_pCurrent = record->m_pParent;
}
}
void CBootProfilerSession::CollectResults(const char* filename, const float timeThreshold)
{
if (!gEnv || !gEnv->pCryPak)
{
AZ_Warning("BootProfiler", false, "CryPak not set - skipping CollectResults");
return;
}
static const char* szTestResults = "@cache@\\TestResults";
string filePath = string(szTestResults) + "\\" + "bp_" + filename + ".xml";
char path[AZ::IO::IArchive::MaxPath] = "";
gEnv->pCryPak->AdjustFileName(filePath.c_str(), path, AZ_ARRAY_SIZE(path), AZ::IO::IArchive::FLAGS_PATH_REAL | AZ::IO::IArchive::FLAGS_FOR_WRITING);
gEnv->pCryPak->MakeDir(szTestResults);
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
gEnv->pFileIO->Open(path, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary, fileHandle);
if (fileHandle == AZ::IO::InvalidHandle)
{
return;
}
char buf[512];
const unsigned int buf_size = sizeof(buf);
sprintf_s(buf, buf_size, "<root>\n");
AZ::IO::Print(fileHandle, buf);
const size_t numThreads = m_threadCounter;
for (size_t i = 0; i < numThreads; ++i)
{
CBootProfilerRecord* pRoot = m_threadsProfileInfo[i].m_pRoot;
if (pRoot)
{
pRoot->m_stopTimeStamp = m_stopTimeStamp;
const char* threadName = GetThreadNameByIndex(i);
if (!threadName)
{
threadName = "UNKNOWN";
}
const float time = (float)(pRoot->m_stopTimeStamp.QuadPart - pRoot->m_startTimeStamp.QuadPart) * 1000.f / (float)pRoot->m_freq.QuadPart;
sprintf_s(buf, buf_size, "\t<thread name=\"%s\" totalTimeMS=\"%f\" startTime=\"%" PRIu64 "\" stopTime=\"%" PRIu64 "\" > \n", threadName, time,
pRoot->m_startTimeStamp.QuadPart, pRoot->m_stopTimeStamp.QuadPart);
AZ::IO::Print(fileHandle, buf);
for (size_t recordIdx = 0; recordIdx < pRoot->m_Childs.size(); ++recordIdx)
{
CBootProfilerRecord* record = pRoot->m_Childs[recordIdx];
assert(record);
record->Print(fileHandle, buf, buf_size, 2, m_stopTimeStamp, threadName, timeThreshold);
}
sprintf_s(buf, buf_size, "\t</thread>\n");
AZ::IO::Print(fileHandle, buf);
}
}
sprintf_s(buf, buf_size, "</root>\n");
AZ::IO::Print(fileHandle, buf);
gEnv->pFileIO->Close(fileHandle);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
CBootProfiler& CBootProfiler::GetInstance()
{
return gProfilerInstance;
}
CBootProfiler::CBootProfiler()
: m_pCurrentSession(NULL)
, m_pFrameRecord(NULL)
, m_levelLoadAdditionalFrames(0)
{
}
CBootProfiler::~CBootProfiler()
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
for (TSessionMap::iterator it = m_sessions.begin(); it != m_sessions.end(); ++it)
{
CBootProfilerSession* session = it->second;
delete session;
}
}
// start session
void CBootProfiler::StartSession(const char* sessionName)
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
TSessionMap::const_iterator it = m_sessions.find(sessionName);
if (it == m_sessions.end())
{
m_pCurrentSession = new CBootProfilerSession();
m_sessions[sessionName] = m_pCurrentSession;
m_pCurrentSession->Start();
}
}
// stop session
void CBootProfiler::StopSession(const char* sessionName)
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
if (m_pCurrentSession)
{
TSessionMap::iterator it = m_sessions.find(sessionName);
if (it != m_sessions.end())
{
if (m_pCurrentSession == it->second)
{
CBootProfilerSession* session = m_pCurrentSession;
m_pCurrentSession = NULL;
session->Stop();
session->CollectResults(sessionName, CV_sys_bp_time_threshold);
delete session;
}
m_sessions.erase(it);
}
}
}
CBootProfilerRecord* CBootProfiler::StartBlock(const char* name, const char* args)
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
if (m_pCurrentSession)
{
return m_pCurrentSession->StartBlock(name, args);
}
return NULL;
}
void CBootProfiler::StopBlock(CBootProfilerRecord* record)
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
if (m_pCurrentSession)
{
m_pCurrentSession->StopBlock(record);
}
}
void CBootProfiler::StartFrame(const char* name)
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
if (CV_sys_bp_frames)
{
StartSession("frames");
m_pFrameRecord = StartBlock(name, NULL);
}
}
void CBootProfiler::StopFrame()
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
if (m_pCurrentSession && CV_sys_bp_frames)
{
StopBlock(m_pFrameRecord);
m_pFrameRecord = NULL;
--CV_sys_bp_frames;
if (0 == CV_sys_bp_frames)
{
StopSession("frames");
}
}
if (m_pCurrentSession && m_levelLoadAdditionalFrames)
{
--m_levelLoadAdditionalFrames;
if (0 == m_levelLoadAdditionalFrames)
{
StopSession("level");
}
}
}
void CBootProfiler::Init(ISystem* pSystem)
{
//REGISTER_CVAR(sys_BootProfiler, 1, VF_DEV_ONLY,
// "Collect and output session statistics into TestResults/bp_(session_name).xml \n"
// "0 = Disabled\n"
// "1 = Enabled\n");
pSystem->GetISystemEventDispatcher()->RegisterListener(this);
StartSession("boot");
}
void CBootProfiler::RegisterCVars()
{
REGISTER_CVAR2("sys_bp_frames", &CV_sys_bp_frames, 0, VF_DEV_ONLY, "Starts frame profiling for specified number of frames using BootProfiler");
REGISTER_CVAR2("sys_bp_time_threshold", &CV_sys_bp_time_threshold, 0.1f, VF_DEV_ONLY, "If greater than 0 don't write blocks that took less time (default 0.1 ms)");
}
void CBootProfiler::OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam)
{
switch (event)
{
case ESYSTEM_EVENT_GAME_POST_INIT_DONE:
{
StopSession("boot");
break;
}
case ESYSTEM_EVENT_GAME_MODE_SWITCH_START:
{
break;
}
case ESYSTEM_EVENT_GAME_MODE_SWITCH_END:
{
break;
}
case ESYSTEM_EVENT_LEVEL_LOAD_START:
{
break;
}
case ESYSTEM_EVENT_LEVEL_LOAD_PREPARE:
{
StartSession("level");
break;
}
case ESYSTEM_EVENT_LEVEL_LOAD_END:
{
StopSession("level");
break;
}
case ESYSTEM_EVENT_LEVEL_PRECACHE_END:
{
//level loading can be stopped here, or m_levelLoadAdditionalFrames can be used to prolong dump for this amount of frames
//StopSession("level");
m_levelLoadAdditionalFrames = 20;
break;
}
}
}
void CBootProfiler::SetFrameCount(int frameCount)
{
CV_sys_bp_frames = frameCount;
}
#endif

@ -1,67 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_BOOTPROFILER_H
#define CRYINCLUDE_CRYSYSTEM_BOOTPROFILER_H
#pragma once
#if defined(ENABLE_LOADING_PROFILER)
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/parallel/mutex.h>
class CBootProfilerRecord;
class CBootProfilerSession;
class CBootProfiler
: public ISystemEventListener
{
friend class CBootProfileBLock;
public:
CBootProfiler();
~CBootProfiler();
static CBootProfiler& GetInstance();
void Init(ISystem* pSystem);
void RegisterCVars();
void StartSession(const char* sessionName);
void StopSession(const char* sessionName);
CBootProfilerRecord* StartBlock(const char* name, const char* args);
void StopBlock(CBootProfilerRecord* record);
void StartFrame(const char* name);
void StopFrame();
protected:
// === ISystemEventListener
virtual void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam);
void SetFrameCount(int frameCount);
private:
CBootProfilerSession* m_pCurrentSession;
typedef AZStd::unordered_map<AZStd::string, CBootProfilerSession*> TSessionMap;
TSessionMap m_sessions;
static int CV_sys_bp_frames;
static float CV_sys_bp_time_threshold;
CBootProfilerRecord* m_pFrameRecord;
AZStd::recursive_mutex m_recordMutex;
int m_levelLoadAdditionalFrames;
};
#endif
#endif // CRYINCLUDE_CRYSYSTEM_BOOTPROFILER_H

@ -277,17 +277,6 @@ int DebugCallStack::handleException(EXCEPTION_POINTERS* exception_pointer)
sprintf_s(excCode, "0x%08X", exception_pointer->ExceptionRecord->ExceptionCode);
WriteLineToLog("Exception: %s, at Address: %s", excCode, excAddr);
if (CSystem* pSystem = (CSystem*)GetSystem())
{
if (const char* pLoadingProfilerCallstack = pSystem->GetLoadingProfilerCallstack())
{
if (pLoadingProfilerCallstack[0])
{
WriteLineToLog("<CrySystem> LoadingProfilerCallstack: %s", pLoadingProfilerCallstack);
}
}
}
{
IMemoryManager::SProcessMemInfo memInfo;
if (gEnv->pSystem->GetIMemoryManager()->GetProcessMemInfo(memInfo))
@ -593,7 +582,7 @@ void DebugCallStack::LogExceptionInfo(EXCEPTION_POINTERS* pex)
AZ::Debug::SymbolStorage::DecodeFrames(frames, numFrames, lines);
for (unsigned int i2 = 0; i2 < numFrames; ++i2)
{
fprintf(f, "%2d) %s\n", numFrames - i2, lines[i2]);
fprintf(f, "%2d) %s\n", numFrames - i2, lines[i2]);
}
}
}

@ -20,8 +20,8 @@
#include "System.h"
#include <AzFramework/IO/FileOperations.h>
#include <AzCore/NativeUI/NativeUIRequests.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
//#if !defined(LINUX)
#include <ISystem.h>
@ -186,21 +186,17 @@ AZ_PUSH_DISABLE_WARNING(4996, "-Wunknown-warning-option")
}
}
if (gEnv->pConsole)
{
if (ICVar* pCVarGameDir = gEnv->pConsole->GetCVar("sys_game_folder"))
{
sprintf(s, "GameDir: %s\n", pCVarGameDir->GetString());
azstrcat(str, length, s);
}
}
AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
azstrcat(str, length, "ProjectDir: ");
azstrcat(str, length, projectPath.c_str());
azstrcat(str, length, "\n");
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_APPEND_MODULENAME
GetModuleFileNameA(NULL, s, sizeof(s));
// Log EXE filename only if possible (not full EXE path which could contain sensitive info)
AZStd::string exeName;
if (AzFramework::StringFunc::Path::GetFullFileName(s, exeName))
if (AZ::StringFunc::Path::GetFullFileName(s, exeName))
{
azstrcat(str, length, "Executable: ");
azstrcat(str, length, exeName.c_str());

@ -1,647 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#if defined(ENABLE_LOADING_PROFILER)
#include "System.h"
#include "LoadingProfiler.h"
#define LOADING_TIME_CONTAINER_MAX_TEXT_SIZE 1024
#define MAX_LOADING_TIME_PROFILER_STACK_DEPTH 16
//#define SAVE_SAVELEVELSTATS_IN_ROOT
struct SLoadingTimeContainer
: public _i_reference_target_t
{
SLoadingTimeContainer() {}
SLoadingTimeContainer(SLoadingTimeContainer* pParent, const char* pPureFuncName, const int nRootIndex)
{
m_dSelfMemUsage = m_dTotalMemUsage = m_dSelfTime = m_dTotalTime = 0;
m_nCounter = 1;
m_pFuncName = pPureFuncName;
m_pParent = pParent;
m_nRootIndex = nRootIndex;
}
static int Cmp_SLoadingTimeContainer_Time(const void* v1, const void* v2)
{
SLoadingTimeContainer* pChunk1 = (SLoadingTimeContainer*)v1;
SLoadingTimeContainer* pChunk2 = (SLoadingTimeContainer*)v2;
if (pChunk1->m_dSelfTime > pChunk2->m_dSelfTime)
{
return -1;
}
else if (pChunk1->m_dSelfTime < pChunk2->m_dSelfTime)
{
return 1;
}
return 0;
}
static int Cmp_SLoadingTimeContainer_MemUsage(const void* v1, const void* v2)
{
SLoadingTimeContainer* pChunk1 = (SLoadingTimeContainer*)v1;
SLoadingTimeContainer* pChunk2 = (SLoadingTimeContainer*)v2;
if (pChunk1->m_dSelfMemUsage > pChunk2->m_dSelfMemUsage)
{
return -1;
}
else if (pChunk1->m_dSelfMemUsage < pChunk2->m_dSelfMemUsage)
{
return 1;
}
return 0;
}
static double GetUsedMemory(ISystem* pSysytem)
{
static IMemoryManager::SProcessMemInfo processMemInfo;
pSysytem->GetIMemoryManager()->GetProcessMemInfo(processMemInfo);
return double(processMemInfo.PagefileUsage) / double(1024 * 1024);
}
void Clear()
{
for (size_t i = 0, end = m_pChilds.size(); i < end; ++i)
{
delete m_pChilds[i];
}
}
~SLoadingTimeContainer()
{
Clear();
}
double m_dSelfTime, m_dTotalTime;
double m_dSelfMemUsage, m_dTotalMemUsage;
uint32 m_nCounter;
const char* m_pFuncName;
SLoadingTimeContainer* m_pParent;
int m_nRootIndex;
std::vector<SLoadingTimeContainer*> m_pChilds;
DiskOperationInfo m_selfInfo;
DiskOperationInfo m_totalInfo;
bool m_bUsed;
};
bool operator== (const SLoadingTimeContainer& a, const SLoadingTimeContainer& b)
{
return b.m_pFuncName == a.m_pFuncName;
}
bool operator== (const SLoadingTimeContainer& a, const char* b)
{
return b == a.m_pFuncName;
}
SLoadingTimeContainer* CLoadingProfilerSystem::m_pCurrentLoadingTimeContainer = 0;
SLoadingTimeContainer* CLoadingProfilerSystem::m_pRoot[2] = {0, 0};
int CLoadingProfilerSystem::m_iActiveRoot = 0;
ICVar* CLoadingProfilerSystem::m_pEnableProfile = 0;
int CLoadingProfilerSystem::nLoadingProfileMode = 1;
int CLoadingProfilerSystem::nLoadingProfilerNotTrackedAllocations = -1;
CryCriticalSection CLoadingProfilerSystem::csLock;
//////////////////////////////////////////////////////////////////////////
void CLoadingProfilerSystem::OutputLoadingTimeStats(ILog* pLog, int nMode)
{
nLoadingProfileMode = nMode;
PodArray<SLoadingTimeContainer> arrNoStack;
CreateNoStackList(arrNoStack);
if (nLoadingProfileMode > 0)
{ // loading mem stats per func
pLog->Log("------ Level loading memory allocations (MB) by function ------------");
pLog->Log(" ||Self | Total | Calls | Function (%d MB lost)||", nLoadingProfilerNotTrackedAllocations);
pLog->Log("---------------------------------------------------------------------");
qsort(arrNoStack.GetElements(), arrNoStack.Count(), sizeof(arrNoStack[0]), SLoadingTimeContainer::Cmp_SLoadingTimeContainer_MemUsage);
for (int i = 0; i < arrNoStack.Count(); i++)
{
const SLoadingTimeContainer* pTimeContainer = &arrNoStack[i];
pLog->Log("|%6.1f | %6.1f | %6d | %s|",
pTimeContainer->m_dSelfMemUsage, pTimeContainer->m_dTotalMemUsage, (int)pTimeContainer->m_nCounter, pTimeContainer->m_pFuncName);
}
pLog->Log("---------------------------------------------------------------------");
}
if (nLoadingProfileMode > 0)
{ // loading time stats per func
pLog->Log("----------- Level loading time (sec) by function --------------------");
pLog->Log(" ||Self | Total | Calls | Function||");
pLog->Log("---------------------------------------------------------------------");
qsort(arrNoStack.GetElements(), arrNoStack.Count(), sizeof(arrNoStack[0]), SLoadingTimeContainer::Cmp_SLoadingTimeContainer_Time);
for (int i = 0; i < arrNoStack.Count(); i++)
{
const SLoadingTimeContainer* pTimeContainer = &arrNoStack[i];
pLog->Log("|%6.1f | %6.1f | %6d | %s|",
pTimeContainer->m_dSelfTime, pTimeContainer->m_dTotalTime, (int)pTimeContainer->m_nCounter, pTimeContainer->m_pFuncName);
}
if (nLoadingProfileMode == 1)
{
pLog->Log("----- ( Use sys_ProfileLevelLoading 2 for more detailed stats ) -----");
}
else
{
pLog->Log("---------------------------------------------------------------------");
}
}
if (nLoadingProfileMode > 0)
{ // file info
pLog->Log("----------------------------- Level file information by function --------------------------------");
pLog->Log("|| Self | Total |Bandwith| Calls | Function||");
pLog->Log("|| Seeks |FileOpen|FileRead| Seeks |FileOpen|FileRead| Kb/s | | ||");
qsort(arrNoStack.GetElements(), arrNoStack.Count(), sizeof(arrNoStack[0]), SLoadingTimeContainer::Cmp_SLoadingTimeContainer_Time);
for (int i = 0; i < arrNoStack.Count(); i++)
{
const SLoadingTimeContainer* pTimeContainer = &arrNoStack[i];
double bandwidth = pTimeContainer->m_dSelfTime > 0 ? (pTimeContainer->m_selfInfo.m_dOperationSize / pTimeContainer->m_dSelfTime / 1024.0) : 0.;
pLog->Log("|%6d | %6d | %6d |%6d | %6d | %6d | %6.1f | %6d | %s|",
pTimeContainer->m_selfInfo.m_nSeeksCount, pTimeContainer->m_selfInfo.m_nFileOpenCount, pTimeContainer->m_selfInfo.m_nFileReadCount,
pTimeContainer->m_totalInfo.m_nSeeksCount, pTimeContainer->m_totalInfo.m_nFileOpenCount, pTimeContainer->m_totalInfo.m_nFileReadCount,
bandwidth, (int)pTimeContainer->m_nCounter, pTimeContainer->m_pFuncName);
}
if (nLoadingProfileMode == 1)
{
pLog->Log("----- ( Use sys_ProfileLevelLoading 2 for more detailed stats ) -----");
}
else
{
pLog->Log("---------------------------------------------------------------------");
}
}
}
struct CSystemEventListner_LoadingProfiler
: public ISystemEventListener
{
private:
CLoadingTimeProfiler* m_pPrecacheProfiler;
ESystemEvent lastEvent;
public:
CSystemEventListner_LoadingProfiler()
: m_pPrecacheProfiler(NULL) {}
virtual void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam)
{
switch (event)
{
case ESYSTEM_EVENT_GAME_MODE_SWITCH_START:
{
CLoadingProfilerSystem::Clean();
if (m_pPrecacheProfiler == NULL)
{
m_pPrecacheProfiler = new CLoadingTimeProfiler(gEnv->pSystem, "ModeSwitch");
}
break;
}
case ESYSTEM_EVENT_GAME_MODE_SWITCH_END:
{
SAFE_DELETE(m_pPrecacheProfiler);
CLoadingProfilerSystem::SaveTimeContainersToFile(gEnv->bMultiplayer == true ? "mode_switch_mp.lmbrlp" : "mode_switch_sp.lmbrlp", 0.0, true);
}
case ESYSTEM_EVENT_LEVEL_LOAD_PREPARE:
{
CLoadingProfilerSystem::Clean();
if (m_pPrecacheProfiler == NULL)
{
m_pPrecacheProfiler = new CLoadingTimeProfiler(gEnv->pSystem, "LevelLoading");
}
break;
}
case ESYSTEM_EVENT_LEVEL_LOAD_END:
{
delete m_pPrecacheProfiler;
m_pPrecacheProfiler = new CLoadingTimeProfiler(gEnv->pSystem, "Precache");
break;
}
case ESYSTEM_EVENT_LEVEL_PRECACHE_END:
{
if (lastEvent == ESYSTEM_EVENT_LEVEL_PRECACHE_FIRST_FRAME)
{
SAFE_DELETE(m_pPrecacheProfiler);
string levelName = "no_level";
ICVar* sv_map = gEnv->pConsole->GetCVar("sv_map");
if (sv_map)
{
levelName = sv_map->GetString();
}
string levelNameFullProfile = levelName + "_LP.lmbrlp";
string levelNameThreshold = levelName + "_LP_OneSec.lmbrlp";
CLoadingProfilerSystem::SaveTimeContainersToFile(levelNameFullProfile.c_str(), 0.0, false);
CLoadingProfilerSystem::SaveTimeContainersToFile(levelNameThreshold.c_str(), 1.0, true);
}
break;
}
case ESYSTEM_EVENT_LEVEL_POST_UNLOAD:
{
// Ensure that the precache profiler is dead
SAFE_DELETE(m_pPrecacheProfiler);
break;
}
}
if (event != ESYSTEM_EVENT_RANDOM_SEED)
{
lastEvent = event;
}
}
};
static CSystemEventListner_LoadingProfiler g_system_event_listener_loadingProfiler;
void CLoadingProfilerSystem::Init()
{
gEnv->pSystem->GetISystemEventDispatcher()->RegisterListener(&g_system_event_listener_loadingProfiler);
}
//////////////////////////////////////////////////////////////////////////
void CLoadingProfilerSystem::ShutDown()
{
if (gEnv && gEnv->pSystem && gEnv->pSystem->GetISystemEventDispatcher())
{
gEnv->pSystem->GetISystemEventDispatcher()->RemoveListener(&g_system_event_listener_loadingProfiler);
}
}
//////////////////////////////////////////////////////////////////////////
SLoadingTimeContainer* CLoadingProfilerSystem::StartLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler, const char* szFuncName)
{
if (!nLoadingProfileMode || !gEnv->pConsole)
{
return NULL;
}
DWORD threadID = GetCurrentThreadId();
static DWORD dwMainThreadId = GetCurrentThreadId();
if (threadID != dwMainThreadId)
{
return NULL;
}
if (!m_pEnableProfile)
{
if (gEnv->pConsole)
{
m_pEnableProfile = gEnv->pConsole->GetCVar("sys_ProfileLevelLoading");
if (!m_pEnableProfile)
{
return 0;
}
}
else
{
return 0;
}
}
if (m_pEnableProfile->GetIVal() <= 0)
{
return 0;
}
//if (m_pCurrentLoadingTimeContainer == m_pRoot && strstr(szFuncName,"Open"))
//{
// pProfiler->m_constructorInfo.m_nFileOpenCount +=1;
//}
CryAutoCriticalSection lock(csLock);
if (true /*pProfiler && pProfiler->m_pSystem*/)
{
ITimer* pTimer = pProfiler->m_pSystem->GetITimer();
pProfiler->m_fConstructorTime = pTimer->GetAsyncTime().GetSeconds();
pProfiler->m_fConstructorMemUsage = SLoadingTimeContainer::GetUsedMemory(pProfiler->m_pSystem);
DiskOperationInfo info;
pProfiler->m_constructorInfo = info;
if (nLoadingProfilerNotTrackedAllocations < 0)
{
nLoadingProfilerNotTrackedAllocations = (int)pProfiler->m_fConstructorMemUsage;
}
}
SLoadingTimeContainer* pParent = m_pCurrentLoadingTimeContainer;
if (!pParent)
{
pParent = m_pCurrentLoadingTimeContainer = m_pRoot[m_iActiveRoot] = new SLoadingTimeContainer(0, "Root", m_iActiveRoot);
}
for (size_t i = 0, end = m_pCurrentLoadingTimeContainer->m_pChilds.size(); i < end; ++i)
{
if (m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_pFuncName == szFuncName)
{
assert(m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_pParent == m_pCurrentLoadingTimeContainer);
assert(!m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_bUsed);
m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_bUsed = true;
m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_nCounter++;
m_pCurrentLoadingTimeContainer = m_pCurrentLoadingTimeContainer->m_pChilds[i];
return m_pCurrentLoadingTimeContainer;
}
}
m_pCurrentLoadingTimeContainer = new SLoadingTimeContainer(pParent, szFuncName, pParent->m_nRootIndex);
m_pCurrentLoadingTimeContainer->m_bUsed = true;
{
// Need to iterate from the end than
pParent->m_pChilds.push_back(m_pCurrentLoadingTimeContainer);
}
return m_pCurrentLoadingTimeContainer;
}
void CLoadingProfilerSystem::EndLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler)
{
if (!nLoadingProfileMode)
{
return;
}
static DWORD dwMainThreadId = GetCurrentThreadId();
if (GetCurrentThreadId() != dwMainThreadId)
{
return;
}
if (!pProfiler->m_pTimeContainer)
{
return;
}
CryAutoCriticalSection lock(csLock);
if (true /*pProfiler && pProfiler->m_pSystem*/)
{
ITimer* pTimer = pProfiler->m_pSystem->GetITimer();
double fSelfTime = pTimer->GetAsyncTime().GetSeconds() - pProfiler->m_fConstructorTime;
double fMemUsage = SLoadingTimeContainer::GetUsedMemory(pProfiler->m_pSystem);
double fSelfMemUsage = fMemUsage - pProfiler->m_fConstructorMemUsage;
if (fSelfTime < 0.0)
{
assert(0);
}
pProfiler->m_pTimeContainer->m_dSelfTime += fSelfTime;
pProfiler->m_pTimeContainer->m_dTotalTime += fSelfTime;
pProfiler->m_pTimeContainer->m_dSelfMemUsage += fSelfMemUsage;
pProfiler->m_pTimeContainer->m_dTotalMemUsage += fSelfMemUsage;
DiskOperationInfo info;
info -= pProfiler->m_constructorInfo;
pProfiler->m_pTimeContainer->m_totalInfo += info;
pProfiler->m_pTimeContainer->m_selfInfo += info;
pProfiler->m_pTimeContainer->m_bUsed = false;
SLoadingTimeContainer* pParent = pProfiler->m_pTimeContainer->m_pParent;
pParent->m_selfInfo -= info;
pParent->m_dSelfTime -= fSelfTime;
pParent->m_dSelfMemUsage -= fSelfMemUsage;
if (pProfiler->m_pTimeContainer->m_pParent && pProfiler->m_pTimeContainer->m_pParent->m_nRootIndex == m_iActiveRoot)
{
m_pCurrentLoadingTimeContainer = pProfiler->m_pTimeContainer->m_pParent;
}
}
}
const char* CLoadingProfilerSystem::GetLoadingProfilerCallstack()
{
CryAutoCriticalSection lock(csLock);
static char szStack[1024];
szStack[0] = 0;
SLoadingTimeContainer* pC = m_pCurrentLoadingTimeContainer;
PodArray<SLoadingTimeContainer*> arrItems;
while (pC)
{
arrItems.Add(pC);
pC = pC->m_pParent;
}
for (int i = arrItems.Count() - 1; i >= 0; i--)
{
cry_strcat(szStack, " > ");
cry_strcat(szStack, arrItems[i]->m_pFuncName);
}
return &szStack[0];
}
void CLoadingProfilerSystem::FillProfilersList(AZStd::vector<SLoadingProfilerInfo>& profilers)
{
UpdateSelfStatistics(m_pRoot[m_iActiveRoot]);
PodArray<SLoadingTimeContainer> arrNoStack;
CreateNoStackList(arrNoStack);
//qsort(arrNoStack.GetElements(), arrNoStack.Count(), sizeof(arrNoStack[0]), SLoadingTimeContainer::Cmp_SLoadingTimeContainer_Time);
uint32 count = arrNoStack.Size();
profilers.resize(count);
for (uint32 i = 0; i < count; ++i)
{
profilers[i].name = arrNoStack[i].m_pFuncName;
profilers[i].selfTime = arrNoStack[i].m_dSelfTime;
profilers[i].callsTotal = arrNoStack[i].m_nCounter;
profilers[i].totalTime = arrNoStack[i].m_dTotalTime;
profilers[i].memorySize = arrNoStack[i].m_dTotalMemUsage;
profilers[i].selfInfo = arrNoStack[i].m_selfInfo;
profilers[i].totalInfo = arrNoStack[i].m_totalInfo;
}
}
void CLoadingProfilerSystem::AddTimeContainerFunction(PodArray<SLoadingTimeContainer>& arrNoStack, SLoadingTimeContainer* node)
{
if (!node)
{
return;
}
SLoadingTimeContainer* it = std::find(arrNoStack.begin(), arrNoStack.end(), node->m_pFuncName);
if (it == arrNoStack.end())
{
arrNoStack.push_back(*node);
}
else
{
it->m_dSelfMemUsage += node->m_dSelfMemUsage;
it->m_dSelfTime += node->m_dSelfTime;
it->m_dTotalMemUsage += node->m_dTotalMemUsage;
it->m_dTotalTime += node->m_dTotalTime;
it->m_nCounter += node->m_nCounter;
it->m_selfInfo += node->m_selfInfo;
it->m_totalInfo += node->m_totalInfo;
}
for (size_t i = 0, end = node->m_pChilds.size(); i < end; ++i)
{
AddTimeContainerFunction(arrNoStack, node->m_pChilds[i]);
}
}
void CLoadingProfilerSystem::CreateNoStackList(PodArray<SLoadingTimeContainer>& arrNoStack)
{
AddTimeContainerFunction(arrNoStack, m_pRoot[m_iActiveRoot]);
}
#define g_szTestResults "@cache@\\TestResults"
void CLoadingProfilerSystem::SaveTimeContainersToFile(const char* name, double fMinTotalTime, bool bClean)
{
if (m_pRoot[m_iActiveRoot])
{
const char* levelName = name;
//Ignore any folders in the input name
const char* folder = strrchr(name, '/');
if (folder != NULL)
{
levelName = folder + 1;
}
char path[AZ::IO::IArchive::MaxPath];
path[sizeof(path) - 1] = 0;
gEnv->pCryPak->AdjustFileName(string(string(g_szTestResults) + "\\" + levelName).c_str(), path, AZ_ARRAY_SIZE(path), AZ::IO::IArchive::FLAGS_PATH_REAL | AZ::IO::IArchive::FLAGS_FOR_WRITING);
gEnv->pCryPak->MakeDir(g_szTestResults);
AZ::IO::HandleType handle = AZ::IO::InvalidHandle;
AZ::IO::Result f = AZ::IO::FileIOBase::GetInstance()->Open(path,AZ::IO::OpenMode::ModeWrite, handle);
if (handle != AZ::IO::InvalidHandle)
{
UpdateSelfStatistics(m_pRoot[m_iActiveRoot]);
WriteTimeContainerToFile(m_pRoot[m_iActiveRoot], handle, 0, fMinTotalTime);
AZ::IO::FileIOBase::GetInstance()->Close(handle);
}
if (bClean)
{
Clean();
}
}
}
void CLoadingProfilerSystem::WriteTimeContainerToFile(SLoadingTimeContainer* p, AZ::IO::HandleType &handle, unsigned int depth, double fMinTotalTime)
{
if (p == NULL)
{
return;
}
if (p->m_dTotalTime < fMinTotalTime)
{
return;
}
CryFixedStringT<MAX_LOADING_TIME_PROFILER_STACK_DEPTH> sDepth;
for (unsigned int i = 0; i < depth; i++)
{
sDepth += "\t";
}
CryFixedStringT<128> str(p->m_pFuncName);
str.replace(':', '_');
char data[4096];
AZ::u64 bytesWritten;
azsnprintf(data, sizeof(data), "%s<%s selfTime='%f' selfMemory='%f' totalTime='%f' totalMemory='%f' count='%i' totalSeeks='%i' totalReads='%i' totalOpens='%i' totalDiskSize='%f' selfSeeks='%i' selfReads='%i' selfOpens='%i' selfDiskSize='%f'>\n",
sDepth.c_str(), str.c_str(), p->m_dSelfTime, p->m_dSelfMemUsage, p->m_dTotalTime, p->m_dTotalMemUsage, p->m_nCounter,
p->m_totalInfo.m_nSeeksCount, p->m_totalInfo.m_nFileReadCount, p->m_totalInfo.m_nFileOpenCount, p->m_totalInfo.m_dOperationSize,
p->m_selfInfo.m_nSeeksCount, p->m_selfInfo.m_nFileReadCount, p->m_selfInfo.m_nFileOpenCount, p->m_selfInfo.m_dOperationSize);
AZ::IO::FileIOBase::GetInstance()->Write(handle, data, strlen(data), &bytesWritten);
for (size_t i = 0, end = p->m_pChilds.size(); i < end; ++i)
{
WriteTimeContainerToFile(p->m_pChilds[i], handle, depth + 1, fMinTotalTime);
}
azsnprintf(data, sizeof(data), "%s</%s>\n", sDepth.c_str(), str.c_str());
AZ::IO::FileIOBase::GetInstance()->Write(handle, data, strlen(data), &bytesWritten);
}
void CLoadingProfilerSystem::UpdateSelfStatistics(SLoadingTimeContainer* p)
{
if (p == NULL)
{
return;
}
p->m_dSelfMemUsage = 0;
p->m_dSelfTime = 0;
p->m_nCounter = 1;
p->m_selfInfo.m_dOperationSize = 0;
p->m_selfInfo.m_nFileOpenCount = 0;
p->m_selfInfo.m_nFileReadCount = 0;
p->m_selfInfo.m_nSeeksCount = 0;
for (size_t i = 0, end = p->m_pChilds.size(); i < end; ++i)
{
p->m_dTotalMemUsage += p->m_pChilds[i]->m_dTotalMemUsage;
p->m_dTotalTime += p->m_pChilds[i]->m_dTotalTime;
p->m_totalInfo += p->m_pChilds[i]->m_totalInfo;
}
}
void CLoadingProfilerSystem::Clean()
{
m_iActiveRoot = (m_iActiveRoot + 1) % 2;
if (m_pRoot[m_iActiveRoot])
{
delete m_pRoot[m_iActiveRoot];
}
m_pCurrentLoadingTimeContainer = m_pRoot[m_iActiveRoot] = 0;
}
#endif

@ -1,69 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_LOADINGPROFILER_H
#define CRYINCLUDE_CRYSYSTEM_LOADINGPROFILER_H
#pragma once
#if defined(ENABLE_LOADING_PROFILER)
struct SLoadingTimeContainer;
struct SLoadingProfilerInfo
{
string name;
double selfTime;
double totalTime;
uint32 callsTotal;
double memorySize;
DiskOperationInfo selfInfo;
DiskOperationInfo totalInfo;
};
class CLoadingProfilerSystem
{
public:
static void Init();
static void ShutDown();
static void CreateNoStackList(PodArray<SLoadingTimeContainer>&);
static void OutputLoadingTimeStats(ILog* pLog, int nMode);
static SLoadingTimeContainer* StartLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler, const char* szFuncName);
static void EndLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler);
static const char* GetLoadingProfilerCallstack();
static void FillProfilersList(AZStd::vector<SLoadingProfilerInfo>& profilers);
static void FlushTimeContainers();
static void SaveTimeContainersToFile(const char*, double fMinTotalTime, bool bClean);
static void WriteTimeContainerToFile(SLoadingTimeContainer* p, AZ::IO::HandleType &handle, unsigned int depth, double fMinTotalTime);
static void UpdateSelfStatistics(SLoadingTimeContainer* p);
static void Clean();
protected:
static void AddTimeContainerFunction(PodArray<SLoadingTimeContainer>&, SLoadingTimeContainer*);
protected:
static int nLoadingProfileMode;
static int nLoadingProfilerNotTrackedAllocations;
static CryCriticalSection csLock;
static int m_iMaxArraySize;
static SLoadingTimeContainer* m_pCurrentLoadingTimeContainer;
static SLoadingTimeContainer* m_pRoot[2];
static int m_iActiveRoot;
static ICVar* m_pEnableProfile;
};
#endif
#endif // CRYINCLUDE_CRYSYSTEM_LOADINGPROFILER_H

@ -16,6 +16,7 @@
#include <ISystem.h>
#include <AzCore/Socket/AzSocket.h>
#include <AzCore/Utils/Utils.h>
#undef LockDebug
//#define LockDebug(str1,str2) {string strMessage;strMessage.Format(str1,str2);if (m_clients.size()) OutputDebugString(strMessage.c_str());}
@ -46,16 +47,13 @@ public:
const char* path = nullptr; // Don't call GetGameFolder here, it returns a full absolute path and we just really want the game name
if (ICVar* pVar = gEnv->pConsole->GetCVar("sys_game_folder"))
{
path = pVar->GetString();
}
if (!path)
AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
if (projectPath.empty())
{
return;
}
pNotificationNetwork->Send("SystemInfo", path, ::strlen(path) + 1);
pNotificationNetwork->Send("SystemInfo", projectPath.c_str(), projectPath.size());
}
} g_queryNotification;

@ -1243,7 +1243,7 @@ namespace
{
char path[AZ::IO::IArchive::MaxPath];
path[sizeof(path) - 1] = 0;
gEnv->pCryPak->AdjustFileName("@cache@\\TestResults\\StreamingLog.txt", path, AZ_ARRAY_SIZE(path), AZ::IO::IArchive::FLAGS_PATH_REAL | AZ::IO::IArchive::FLAGS_FOR_WRITING);
gEnv->pCryPak->AdjustFileName("@usercache@\\TestResults\\StreamingLog.txt", path, AZ_ARRAY_SIZE(path), AZ::IO::IArchive::FLAGS_PATH_REAL | AZ::IO::IArchive::FLAGS_FOR_WRITING);
sFileName = path;
}
AZ::IO::HandleType fileHandle = fxopen(sFileName, (bFirstTime) ? "wt" : "at");

@ -157,7 +157,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
#include "ServerThrottle.h"
#include "ILocalMemoryUsage.h"
#include "ResourceManager.h"
#include "LoadingProfiler.h"
#include "HMDBus.h"
#include "OverloadSceneManager/OverloadSceneManager.h"
#include <IThreadManager.h>
@ -168,7 +167,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
#include "IZStdDecompressor.h"
#include "zlib.h"
#include "RemoteConsole/RemoteConsole.h"
#include "BootProfiler.h"
#include <PNoise3.h>
#include <StringUtils.h>
@ -320,10 +318,6 @@ CSystem::CSystem(SharedEnvironmentInstance* pSharedEnvironment)
m_bIsAsserting = false;
m_pSystemEventDispatcher = new CSystemEventDispatcher(); // Must be first.
#if defined(ENABLE_LOADING_PROFILER)
CBootProfiler::GetInstance().Init(this);
#endif
if (m_pSystemEventDispatcher)
{
m_pSystemEventDispatcher->RegisterListener(this);
@ -422,7 +416,6 @@ CSystem::CSystem(SharedEnvironmentInstance* pSharedEnvironment)
// m_sys_filecache = NULL;
m_gpu_particle_physics = NULL;
m_pCpu = NULL;
m_sys_game_folder = NULL;
m_bInitializedSuccessfully = false;
m_bShaderCacheGenMode = false;
@ -634,10 +627,6 @@ void CSystem::ShutDown()
EBUS_EVENT(CrySystemEventBus, OnCrySystemShutdown, *this);
}
#if defined(ENABLE_LOADING_PROFILER)
CLoadingProfilerSystem::ShutDown();
#endif
if (m_pUserCallback)
{
m_pUserCallback->OnShutdown();
@ -657,7 +646,7 @@ void CSystem::ShutDown()
SAFE_DELETE(m_pTextModeConsole);
KillPhysicsThread();
if (m_sys_firstlaunch)
{
m_sys_firstlaunch->Set("0");
@ -828,7 +817,7 @@ void CSystem::ShutDown()
void CSystem::Quit()
{
CryLogAlways("CSystem::Quit invoked from thread %" PRI_THREADID " (main is %" PRI_THREADID ")", GetCurrentThreadId(), gEnv->mMainThreadId);
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::ExitMainLoop);
// If this was set from anywhere but the main thread, bail and let the main thread handle shutdown
@ -857,13 +846,13 @@ void CSystem::Quit()
/*
* TODO: This call to _exit, _Exit, TerminateProcess etc. needs to
* eventually be removed. This causes an extremely early exit before we
* actually perform cleanup. When this gets called most managers are
* eventually be removed. This causes an extremely early exit before we
* actually perform cleanup. When this gets called most managers are
* simply never deleted and we leave it to the OS to clean up our mess
* which is just really bad practice. However there are LOTS of issues
* with shutdown at the moment. Removing this will simply cause
* a crash when either the Editor or Launcher initiate shutdown. Both
* applications crash differently too. Bugs will be logged about those
* with shutdown at the moment. Removing this will simply cause
* a crash when either the Editor or Launcher initiate shutdown. Both
* applications crash differently too. Bugs will be logged about those
* issues.
*/
#if defined(AZ_RESTRICTED_PLATFORM)
@ -1117,7 +1106,7 @@ void CSystem::CreatePhysicsThread()
#include AZ_RESTRICTED_FILE(System_cpp)
#endif
{
{
m_PhysThread = new CPhysicsThreadTask;
GetIThreadTaskManager()->RegisterTask(m_PhysThread, threadParams);
}
@ -1412,7 +1401,7 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
ti.xscale = ti.yscale = 1.2f;
const int viewportHeight = GetViewCamera().GetViewSurfaceZ();
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEM_CPP_SECTION_8
#include AZ_RESTRICTED_FILE(System_cpp)
@ -1428,7 +1417,7 @@ bool CSystem::UpdatePreTickBus(int updateFlags, int nPauseMode)
switch (stat.GetType())
{
case AZ::IO::Statistic::Type::FloatingPoint:
gEnv->pRenderer->DrawTextQueued(Vec3(10, y, 1.0f), ti,
gEnv->pRenderer->DrawTextQueued(Vec3(10, y, 1.0f), ti,
AZStd::string::format("%s/%s: %.3f", stat.GetOwner().data(), stat.GetName().data(), stat.GetFloatValue()).c_str());
break;
case AZ::IO::Statistic::Type::Integer:
@ -2744,10 +2733,10 @@ bool CSystem::HandleMessage([[maybe_unused]] HWND hWnd, UINT uMsg, WPARAM wParam
{
// System event translation
case WM_CLOSE:
/*
/*
Trigger CSystem to call Quit() the next time
it calls Update(). HandleMessages can get messages
pumped to it from SyncMainWithRender which would
pumped to it from SyncMainWithRender which would
be called recurively by Quit(). Doing so would
cause the render thread to deadlock and the main
thread to spin in SRenderThread::WaitFlushFinishedCond.
@ -2897,11 +2886,6 @@ std::shared_ptr<AZ::IO::FileIOBase> CSystem::CreateLocalFileIO()
return std::make_shared<AZ::IO::LocalFileIO>();
}
const char* CSystem::GetAssetsPlatform() const
{
return m_assetPlatform.c_str();
}
IViewSystem* CSystem::GetIViewSystem()
{
return m_pViewSystem;

@ -45,7 +45,6 @@ struct IConsoleCmdArgs;
class CServerThrottle;
struct ICryFactoryRegistryImpl;
struct IZLibCompressor;
class CLoadingProfilerSystem;
class CWatchdogThread;
class CThreadManager;
@ -437,11 +436,6 @@ public:
uint32 GetUsedMemory();
//! For asset processor, we need to know what kind of assets we're loading. This comes all the way from bootstrap.cfg
//! It will be a string like "pc" or "es3" or such and controls what kind of assets we have access to (its used when for example
//! attempting to open a file when multiple different assets for different platforms are available.)
const char* GetAssetsPlatform() const;
virtual void DumpMemoryUsageStatistics(bool bUseKB);
virtual void DumpMemoryCoverage();
void CollectMemInfo(SCryEngineStatsGlobalMemInfo&);
@ -685,7 +679,7 @@ private:
bool InitRenderer(WIN_HINSTANCE hinst, WIN_HWND hwnd, const SSystemInitParams& initParams);
bool InitFont(const SSystemInitParams& initParams);
bool InitFileSystem(const SSystemInitParams& initParams);
bool InitFileSystem();
bool InitFileSystem_LoadEngineFolders(const SSystemInitParams& initParams);
bool InitStreamEngine();
bool Init3DEngine(const SSystemInitParams& initParams);
@ -792,7 +786,7 @@ public:
const CTimeValue& GetLastTickTime(void) const { return m_lastTickTime; }
const ICVar* GetDedicatedMaxRate(void) const { return m_svDedicatedMaxRate; }
const char* GetRenderingDriverName(void) const
{
if(m_rDriver)
@ -802,7 +796,7 @@ public:
return nullptr;
}
std::shared_ptr<AZ::IO::FileIOBase> CreateLocalFileIO();
// Gets the dimensions (in pixels) of the primary physical display.
@ -914,7 +908,6 @@ private: // ------------------------------------------------------
// DLL names
ICVar* m_sys_dll_response_system;
ICVar* m_sys_game_folder;
#if !defined(_RELEASE)
ICVar* m_sys_resource_cache_folder;
#endif
@ -950,7 +943,6 @@ private: // ------------------------------------------------------
ICVar* m_rFullscreenWindow;
ICVar* m_rFullscreenNativeRes;
ICVar* m_rDriver;
ICVar* m_cvGameName;
ICVar* m_rDisplayInfo;
ICVar* m_rOverscanBordersDrawDebugView;
ICVar* m_sysNoUpdate;
@ -1040,8 +1032,6 @@ private: // ------------------------------------------------------
uint64 m_nUpdateCounter;
int sys_ProfileLevelLoading, sys_ProfileLevelLoadingDump;
bool m_executedCommandLine = false;
AZStd::unique_ptr<AzFramework::MissingAssetLogger> m_missingAssetLogger;
@ -1072,18 +1062,6 @@ public:
void CloseLanguageAudioPak(const char* sLanguage);
void UpdateMovieSystem(const int updateFlags, const float fFrameTime, const bool bPreUpdate);
// level loading profiling
virtual void OutputLoadingTimeStats();
virtual struct SLoadingTimeContainer* StartLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler, const char* szFuncName);
virtual void EndLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler);
virtual const char* GetLoadingProfilerCallstack();
//////////////////////////////////////////////////////////////////////////
virtual CBootProfilerRecord* StartBootSectionProfiler(const char* name, const char* args);
virtual void StopBootSectionProfiler(CBootProfilerRecord* record);
virtual void StartBootProfilerSessionFrames(const char* pName);
virtual void StopBootProfilerSessionFrames();
//////////////////////////////////////////////////////////////////////////
// CryAssert and error related.
virtual bool RegisterErrorObserver(IErrorObserver* errorObserver);
@ -1139,17 +1117,9 @@ protected: // -------------------------------------------------------------
ITextModeConsole* m_pTextModeConsole;
INotificationNetwork* m_pNotificationNetwork;
string m_binariesDir;
string m_currentLanguageAudio;
string m_assetPlatform; // ("es3" / "pc" / etc) describes the KIND of assets we load and controls where they're loaded from
string m_systemConfigName; // computed from system_(hardwareplatform)_(assetsPlatform) - eg, system_android_es3.cfg or system_android_opengl.cfg or system_windows_pc.cfg
// the following variables capture what was set up in the systeminitparams after/during InitFileSystem
// They are not actually to be used except to establish aliases like @user@
string m_userRootDir;
string m_cacheDir;
string m_logsDir;
std::vector< std::pair<CTimeValue, float> > m_updateTimes;
CMemoryFragmentationProfiler m_MemoryFragmentationProfiler;

@ -23,6 +23,7 @@
#include <AzCore/Console/IConsole.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include "SystemCFG.h"
@ -252,15 +253,8 @@ void CSystem::LogVersion()
//////////////////////////////////////////////////////////////////////////
void CSystem::LogBuildInfo()
{
ICVar* pGameName = m_env.pConsole->GetCVar("sys_game_name");
if (pGameName)
{
CryLogAlways("GameName: %s", pGameName->GetString());
}
else
{
CryLogAlways("Couldn't find game name in cvar sys_game_name");
}
auto projectName = AZ::Utils::GetProjectName();
CryLogAlways("GameName: %s", projectName.c_str());
CryLogAlways("BuildTime: " __DATE__ " " __TIME__);
}

@ -68,6 +68,7 @@
#include <LoadScreenBus.h>
#include <LyShine/Bus/UiSystemBus.h>
#include <AzFramework/Logging/MissingAssetLogger.h>
#include <AzFramework/Platform/PlatformDefaults.h>
#include <AzFramework/API/AtomActiveInterface.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Utils/Utils.h>
@ -115,8 +116,6 @@
#include "SystemCFG.h"
#include "AutoDetectSpec.h"
#include "ResourceManager.h"
#include "LoadingProfiler.h"
#include "BootProfiler.h"
#include "VisRegTest.h"
#include "MTSafeAllocator.h"
#include "NotificationNetwork.h"
@ -498,7 +497,7 @@ struct SysSpecOverrideSinkConsole
else
{
// If the cvar doesn't exist, calling this function only saves the value in case it's registered later where
// at that point it will be set from the stored value. This is required because otherwise registering the
// at that point it will be set from the stored value. This is required because otherwise registering the
// cvar bypasses any callbacks and uses values directly from the cvar group files.
gEnv->pConsole->LoadConfigVar(szKey, szValue);
}
@ -618,13 +617,13 @@ static void LoadDetectedSpec(ICVar* pVar)
if (gEnv->IsEditor())
{
ESystemConfigPlatform configPlatform = GetISystem()->GetConfigPlatform();
// Check if the config platform is set first.
// Check if the config platform is set first.
if (configPlatform != CONFIG_INVALID_PLATFORM)
{
platform = configPlatform;
}
}
AZStd::string configFile;
GetSpecConfigFileToLoad(pVar, configFile, platform);
if (configFile.length())
@ -779,7 +778,7 @@ static void LoadDetectedSpec(ICVar* pVar)
MobileSysInspect::GetSpecForGPUAndAPI(adapterDesc, apiver, gpuConfigFile);
GetISystem()->LoadConfiguration(gpuConfigFile.c_str(), pSysSpecOverrideSinkConsole);
}
#endif
#endif
}
if (bMultiGPUEnabled)
{
@ -832,23 +831,7 @@ AZStd::unique_ptr<AZ::DynamicModuleHandle> CSystem::LoadDynamiclibrary(const cha
{
AZStd::unique_ptr<AZ::DynamicModuleHandle> handle = AZ::DynamicModuleHandle::Create(dllName);
bool libraryLoaded = false;
#ifdef WIN32
if (m_binariesDir.empty())
{
libraryLoaded = handle->Load(false);
}
else
{
char currentDirectory[1024];
AZ::Utils::GetExecutableDirectory(currentDirectory, AZ_ARRAY_SIZE(currentDirectory));
SetCurrentDirectory(m_binariesDir.c_str());
libraryLoaded = handle->Load(false);
SetCurrentDirectory(currentDirectory);
}
#else
libraryLoaded = handle->Load(false);
#endif
bool libraryLoaded = handle->Load(false);
// We need to inject the environment first thing so that allocators are available immediately
InjectEnvironmentFunction injectEnv = handle->GetFunction<InjectEnvironmentFunction>(INJECT_ENVIRONMENT_FUNCTION);
if (injectEnv)
@ -974,7 +957,7 @@ bool CSystem::InitializeEngineModule(const char* dllName, const char* moduleClas
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
#else
dllfile.append(dllName);
@ -1007,7 +990,7 @@ bool CSystem::InitializeEngineModule(const char* dllName, const char* moduleClas
if (CryCreateClassInstance(moduleClassName, pModule))
{
bResult = pModule->Initialize(m_env, initParams);
// After initializing the module, give it a chance to register any AZ console vars
// declared within the module.
pModule->RegisterConsoleVars();
@ -1453,7 +1436,7 @@ bool CSystem::InitRenderer(WIN_HINSTANCE hinst, WIN_HWND hwnd, const SSystemInit
displayWidth *= scaleFactor;
displayHeight *= scaleFactor;
const int maxWidth = m_rMaxWidth->GetIVal();
if (maxWidth > 0 && maxWidth < displayWidth)
{
@ -1524,7 +1507,7 @@ bool CSystem::InitRenderer(WIN_HINSTANCE hinst, WIN_HWND hwnd, const SSystemInit
/////////////////////////////////////////////////////////////////////////////////
bool CSystem::InitFileSystem(const SSystemInitParams& initParams)
bool CSystem::InitFileSystem()
{
LOADING_TIME_PROFILE_SECTION;
using namespace AzFramework::AssetSystem;
@ -1549,136 +1532,6 @@ bool CSystem::InitFileSystem(const SSystemInitParams& initParams)
}
#endif // !defined(_RELEASE)
bool usingAssetCache = initParams.UseAssetCache();
const char* rootPath = usingAssetCache ? initParams.rootPathCache : initParams.rootPath;
const char* assetsPath = usingAssetCache ? initParams.assetsPathCache : initParams.assetsPath;
if (rootPath == 0)
{
AZ_Assert(false, "No root path specified in SystemInitParams");
return false;
}
if (assetsPath == 0)
{
AZ_Assert(false, "No assets path specified in SystemInitParams");
return false;
}
// establish the root folder and assets folder immediately.
// Other folders that can be computed from the root can be specified later.
m_env.pFileIO->SetAlias("@root@", rootPath);
m_env.pFileIO->SetAlias("@assets@", assetsPath);
if (initParams.userPath[0] == 0)
{
string outPath = PathUtil::Make(m_env.pFileIO->GetAlias("@root@"), "user");
m_env.pFileIO->SetAlias("@user@", outPath.c_str());
}
else
{
m_env.pFileIO->SetAlias("@user@", initParams.userPath);
}
if (initParams.logPath[0] == 0)
{
char resolveBuffer[AZ_MAX_PATH_LEN] = { 0 };
m_env.pFileIO->ResolvePath("@user@", resolveBuffer, AZ_MAX_PATH_LEN);
string outPath = PathUtil::Make(resolveBuffer, "log");
m_env.pFileIO->SetAlias("@log@", outPath.c_str());
}
else
{
m_env.pFileIO->SetAlias("@log@", initParams.logPath);
}
m_env.pFileIO->CreatePath("@root@");
m_env.pFileIO->CreatePath("@user@");
m_env.pFileIO->CreatePath("@log@");
if ((!m_env.IsInToolMode()) || (m_bShaderCacheGenMode)) // in tool mode, the promise is that you won't access @cache@!
{
string finalCachePath;
if (initParams.cachePath[0] == 0)
{
char resolveBuffer[AZ_MAX_PATH_LEN] = { 0 };
m_env.pFileIO->ResolvePath("@user@", resolveBuffer, AZ_MAX_PATH_LEN);
finalCachePath = PathUtil::Make(resolveBuffer, "cache");
}
else
{
finalCachePath = initParams.cachePath;
}
#if defined(AZ_PLATFORM_WINDOWS)
// Search for a non-locked cache directory because shaders require separate caches for each running instance.
// We only need to do this check for Windows, because consoles can't have multiple instances running simultaneously.
// Ex: running editor and game, running multiple games, or multiple non-interactive editor instances
// for parallel level exports.
string originalPath = finalCachePath;
#if defined(REMOTE_ASSET_PROCESSOR)
bool allowEngineConnection = !initParams.bToolMode && !initParams.bTestMode;
bool allowRemoteIO = allowEngineConnection && initParams.remoteFileIO && !initParams.bEditor;
if (!allowRemoteIO) // not running on VFS
#endif
{
int attemptNumber = 0;
// The number of max attempts ultimately dictates the number of Lumberyard instances that can run
// simultaneously. This should be a reasonably high number so that it doesn't artificially limit
// the number of instances (ex: parallel level exports via multiple Editor runs). It also shouldn't
// be set *infinitely* high - each cache folder is GBs in size, and finding a free directory is a
// linear search, so the more instances we allow, the longer the search will take.
// 128 seems like a reasonable compromise.
constexpr int maxAttempts = 128;
char workBuffer[AZ_MAX_PATH_LEN] = { 0 };
while (attemptNumber < maxAttempts)
{
finalCachePath = originalPath;
if (attemptNumber != 0)
{
azsnprintf(workBuffer, AZ_MAX_PATH_LEN, "%s%i", originalPath.c_str(), attemptNumber);
finalCachePath = workBuffer;
}
else
{
finalCachePath = originalPath;
}
++attemptNumber; // do this here so we don't forget
m_env.pFileIO->CreatePath(finalCachePath.c_str());
// if the directory already exists, check for locked file
string outLockPath = PathUtil::Make(finalCachePath.c_str(), "lockfile.txt");
// note, the zero here after GENERIC_READ|GENERIC_WRITE indicates no share access at all
g_cacheLock = CreateFileA(outLockPath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, 0);
if (g_cacheLock != INVALID_HANDLE_VALUE)
{
break;
}
}
if (attemptNumber >= maxAttempts)
{
AZ_Assert(false, "Couldn't find a valid asset cache folder for the Asset Processor after %i attempts.", attemptNumber);
AZ_Printf("FileSystem", "Couldn't find a valid asset cache folder for the Asset Processor after %i attempts.", attemptNumber);
return false;
}
}
#endif // defined(AZ_PLATFORM_WINDOWS)
AZ_Printf("FileSystem", "Using %s folder for asset cache.\n", finalCachePath.c_str());
m_env.pFileIO->SetAlias("@cache@", finalCachePath.c_str());
m_env.pFileIO->CreatePath("@cache@");
}
m_env.pCryPak = AZ::Interface<AZ::IO::IArchive>::Get();
m_env.pFileIO = AZ::IO::FileIOBase::GetInstance();
AZ_Assert(m_env.pCryPak, "CryPak has not been initialized on AZ::Interface");
@ -1755,7 +1608,6 @@ void CSystem::ShutdownFileSystem()
bool CSystem::InitFileSystem_LoadEngineFolders(const SSystemInitParams& initParams)
{
LOADING_TIME_PROFILE_SECTION;
// Load value of sys_game_folder from system.cfg into the sys_game_folder console variable
{
ILoadConfigurationEntrySink* pCVarsWhiteListConfigSink = GetCVarsWhiteListConfigSink();
LoadConfiguration(m_systemConfigName.c_str(), pCVarsWhiteListConfigSink);
@ -1767,17 +1619,19 @@ bool CSystem::InitFileSystem_LoadEngineFolders(const SSystemInitParams& initPara
#endif
GetISystem()->SetConfigPlatform(GetDevicePlatform());
#if defined(CRY_ENABLE_RC_HELPER)
if (!m_env.pResourceCompilerHelper)
{
m_env.pResourceCompilerHelper = new CResourceCompilerHelper();
}
#endif
// you may not set these in game.cfg or in system.cfg
m_sys_game_folder->ForceSet(initParams.gameFolderName);
AZ_Printf(AZ_TRACE_SYSTEM_WINDOW, "GameDir: %s\n", m_sys_game_folder->GetString());
auto projectPath = AZ::Utils::GetProjectPath();
AZ_Printf(AZ_TRACE_SYSTEM_WINDOW, "Project Path: %s\n", projectPath.empty() ? "None specified" : projectPath.c_str());
auto projectName = AZ::Utils::GetProjectName();
AZ_Printf(AZ_TRACE_SYSTEM_WINDOW, "Project Name: %s\n", projectName.empty() ? "None specified" : projectName.c_str());
// simply open all paks if fast load pak can't be found
if (!m_pResourceManager->LoadFastLoadPaks(true))
@ -1857,7 +1711,7 @@ bool CSystem::InitFont(const SSystemInitParams& initParams)
bool CSystem::Init3DEngine(const SSystemInitParams& initParams)
{
LOADING_TIME_PROFILE_SECTION(GetISystem());
if (!InitializeEngineModule(DLL_3DENGINE, "EngineModule_Cry3DEngine", initParams))
{
return false;
@ -1930,7 +1784,7 @@ bool CSystem::InitVTuneProfiler()
LOADING_TIME_PROFILE_SECTION(GetISystem());
#ifdef PROFILE_WITH_VTUNE
WIN_HMODULE hModule = LoadDLL("VTuneApi.dll");
if (!hModule)
{
@ -2033,7 +1887,7 @@ void CSystem::OpenBasicPaks()
bBasicPaksLoaded = true;
LOADING_TIME_PROFILE_SECTION;
// open pak files
constexpr AZStd::string_view paksFolder = "@assets@/*.pak"; // (@assets@ assumed)
m_env.pCryPak->OpenPacks(paksFolder);
@ -2188,12 +2042,6 @@ string GetUniqueLogFileName(string logFileName)
}
void OnLevelLoadingDump([[maybe_unused]] ICVar* pArgs)
{
gEnv->pSystem->OutputLoadingTimeStats();
}
#if defined(WIN32) || defined(WIN64)
static wstring GetErrorStringUnsupportedCPU()
{
@ -2415,8 +2263,8 @@ bool CSystem::Init(const SSystemInitParams& startupParams)
signal(SIGILL, CryEngineSignalHandler);
#endif // AZ_TRAIT_USE_CRY_SIGNAL_HANDLER
// Temporary Fix for an issue accessing gEnv from this object instance. The gEnv is not resolving to the
// global gEnv, instead its resolving an some uninitialized gEnv elsewhere (NULL). Since gEnv is
// Temporary Fix for an issue accessing gEnv from this object instance. The gEnv is not resolving to the
// global gEnv, instead its resolving an some uninitialized gEnv elsewhere (NULL). Since gEnv is
// initialized to this instance's SSystemGlobalEnvironment (m_env), we will force set it again here
// to m_env
if (!gEnv)
@ -2451,7 +2299,7 @@ bool CSystem::Init(const SSystemInitParams& startupParams)
// Linux is all console for now and so no room for dialog boxes!
m_env.bNoAssertDialog = true;
#endif
m_pCmdLine = new CCmdLine(startupParams.szSystemCmdLine);
AZCoreLogSink::Connect();
@ -2461,25 +2309,25 @@ bool CSystem::Init(const SSystemInitParams& startupParams)
{
azConsole->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
}
m_assetPlatform = startupParams.assetsPlatform;
// compute system config name
if (m_assetPlatform.empty())
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry)
{
AZ_Error(AZ_TRACE_SYSTEM_WINDOW, false, R"(A valid asset platform is missing in "%s/assets" key in the SettingsRegistry.)""\n"
R"(This can be set via the via the --regset "%s/assets=<value>" command line option)"
R"(, by setting value at the "%s/assets path" within a *.setreg file that is loaded by the application)"
R"( or by setting the "assets" field in the bootstrap.cfg.)",
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey,
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey,
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey);
return false;
}
AZ::SettingsRegistryInterface::FixedValueString assetPlatform;
if (!AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, assetPlatform,
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, "assets"))
{
assetPlatform = AzFramework::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
AZ_Warning(AZ_TRACE_SYSTEM_WINDOW, false, R"(A valid asset platform is missing in "%s/assets" key in the SettingsRegistry.)""\n"
R"(This typically done by setting he "assets" field in the bootstrap.cfg for within a .setreg file)""\n"
R"(A fallback of %s will be used.)",
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey,
assetPlatform.c_str());
}
m_systemConfigName = "system_" AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER "_";
m_systemConfigName += m_assetPlatform;
m_systemConfigName += ".cfg";
m_systemConfigName = "system_" AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER "_";
m_systemConfigName += assetPlatform.c_str();
m_systemConfigName += ".cfg";
}
AZ_Assert(CryMemory::IsHeapValid(), "Memory heap must be valid before continuing SystemInit.");
@ -2518,11 +2366,6 @@ AZ_POP_DISABLE_WARNING
m_hInst = (WIN_HINSTANCE)startupParams.hInstance;
m_hWnd = (WIN_HWND)startupParams.hWnd;
m_userRootDir = startupParams.userPath;
m_logsDir = startupParams.logPath;
m_cacheDir = startupParams.cachePath;
m_binariesDir = startupParams.szBinariesDir;
m_bEditor = startupParams.bEditor;
m_bPreviewMode = startupParams.bPreview;
m_bTestMode = startupParams.bTestMode;
@ -2666,17 +2509,13 @@ AZ_POP_DISABLE_WARNING
//////////////////////////////////////////////////////////////////////////
// File system, must be very early
//////////////////////////////////////////////////////////////////////////
if (!InitFileSystem(startupParams))
if (!InitFileSystem())
{
return false;
}
//////////////////////////////////////////////////////////////////////////
InlineInitializationProcessing("CSystem::Init InitFileSystem");
#if defined(ENABLE_LOADING_PROFILER)
CLoadingProfilerSystem::Init();
#endif
m_missingAssetLogger = AZStd::make_unique<AzFramework::MissingAssetLogger>();
//////////////////////////////////////////////////////////////////////////
@ -2786,10 +2625,6 @@ AZ_POP_DISABLE_WARNING
GetIRemoteConsole()->RegisterConsoleVariables();
#ifdef ENABLE_LOADING_PROFILER
CBootProfiler::GetInstance().RegisterCVars();
#endif
if (!startupParams.bSkipConsole)
{
// Register system console variables.
@ -2827,7 +2662,7 @@ AZ_POP_DISABLE_WARNING
m_env.pCryPak->OpenPack("@assets@", "Engine.pak");
#if defined(AZ_PLATFORM_ANDROID) || defined(AZ_PLATFORM_IOS)
MobileSysInspect::LoadDeviceSpecMapping();
#endif
#endif
InitFileSystem_LoadEngineFolders(startupParams);
@ -3472,10 +3307,6 @@ AZ_POP_DISABLE_WARNING
MarkThisThreadForDebugging("Main");
}
#if defined(ENABLE_LOADING_PROFILER)
CLoadingProfilerSystem::SaveTimeContainersToFile("EngineStart.crylp", 0.0, true);
#endif
InlineInitializationProcessing("CSystem::Init End");
#if defined(IS_PROSDK)
@ -4188,7 +4019,7 @@ static void ScreenshotCmd(IConsoleCmdArgs* pParams)
}
}
// Helper to maintain backwards compatibility with our CVar but not force our new code to
// Helper to maintain backwards compatibility with our CVar but not force our new code to
// pull in CryCommon by routing through an environment variable
void CmdSetAwsLogLevel(IConsoleCmdArgs* pArgs)
{
@ -4424,7 +4255,6 @@ void CSystem::CreateSystemVars()
// Register DLL names as cvars before we load them
//
EVarFlags dllFlags = (EVarFlags)0;
m_sys_game_folder = REGISTER_STRING("sys_game_folder", "EmptyTemplate", VF_READONLY, "Specifies the game folder to read all data from. Can be fully pathed for external folders or relative path for folders inside the root.");
m_sys_dll_response_system = REGISTER_STRING("sys_dll_response_system", 0, dllFlags, "Specifies the DLL to load for the dynamic response system");
m_sys_initpreloadpacks = REGISTER_STRING("sys_initpreloadpacks", "", 0, "Specifies the paks for an engine initialization");
@ -4447,8 +4277,6 @@ void CSystem::CreateSystemVars()
m_level_load_screen_minimum_time = REGISTER_FLOAT("level_load_screen_minimum_time", 0.0f, 0, "Minimum amount of time to show the level load screen. Important to prevent short loads from flashing the load screen. 0 means there is no limit.");
#endif // if AZ_LOADSCREENCOMPONENT_ENABLED
m_cvGameName = REGISTER_STRING("sys_game_name", "Lumberyard", VF_DUMPTODISK, "Specifies the name to be displayed in the Launcher window title bar");
REGISTER_INT("cvDoVerboseWindowTitle", 0, VF_NULL, "");
m_pCVarQuit = REGISTER_INT("ExitOnQuit", 1, VF_NULL, "");
@ -4495,7 +4323,7 @@ void CSystem::CreateSystemVars()
}
#endif
attachVariable("sys_PakReadSlice", &g_cvars.archiveVars.nReadSlice, "If non-0, means number of kilobytes to use to read files in portions. Should only be used on Win9x kernels");
attachVariable("sys_PakInMemorySizeLimit", &g_cvars.archiveVars.nInMemoryPerPakSizeLimit, "Individual pak size limit for being loaded into memory (MB)");
@ -4818,15 +4646,6 @@ void CSystem::CreateSystemVars()
"e.g. LoadConfig lowspec.cfg\n"
"Usage: LoadConfig <filename>");
REGISTER_CVAR(sys_ProfileLevelLoading, 0, VF_CHEAT,
"Output level loading stats into log\n"
"0 = Off\n"
"1 = Output basic info about loading time per function\n"
"2 = Output full statistics including loading time and memory allocations with call stack info");
REGISTER_CVAR_CB(sys_ProfileLevelLoadingDump, 0, VF_CHEAT, "Output level loading dump stats into log\n", OnLevelLoadingDump);
assert(m_env.pConsole);
m_env.pConsole->CreateKeyBind("alt_keyboard_key_function_F12", "Screenshot");
m_env.pConsole->CreateKeyBind("alt_keyboard_key_function_F11", "RecordClip");
@ -4871,7 +4690,7 @@ void CSystem::CreateSystemVars()
// adding CVAR to toggle assert verbosity level
const int defaultAssertValue = 1;
REGISTER_CVAR2_CB("sys_asserts", &g_cvars.sys_asserts, defaultAssertValue, VF_CHEAT,
REGISTER_CVAR2_CB("sys_asserts", &g_cvars.sys_asserts, defaultAssertValue, VF_CHEAT,
"0 = Suppress Asserts\n"
"1 = Log Asserts\n"
"2 = Show Assert Dialog\n"
@ -4957,78 +4776,6 @@ void CSystem::AddCVarGroupDirectory(const string& sPath)
gEnv->pCryPak->FindClose(handle);
}
void CSystem::OutputLoadingTimeStats()
{
#if defined(ENABLE_LOADING_PROFILER)
if (GetIConsole())
{
if (ICVar* pVar = GetIConsole()->GetCVar("sys_ProfileLevelLoading"))
{
CLoadingProfilerSystem::OutputLoadingTimeStats(GetILog(), pVar->GetIVal());
}
}
#endif
}
SLoadingTimeContainer* CSystem::StartLoadingSectionProfiling([[maybe_unused]] CLoadingTimeProfiler* pProfiler, [[maybe_unused]] const char* szFuncName)
{
#if defined(ENABLE_LOADING_PROFILER)
return CLoadingProfilerSystem::StartLoadingSectionProfiling(pProfiler, szFuncName);
#else
return 0;
#endif
}
void CSystem::EndLoadingSectionProfiling([[maybe_unused]] CLoadingTimeProfiler* pProfiler)
{
#if defined(ENABLE_LOADING_PROFILER)
CLoadingProfilerSystem::EndLoadingSectionProfiling(pProfiler);
#endif
}
const char* CSystem::GetLoadingProfilerCallstack()
{
#if defined(ENABLE_LOADING_PROFILER)
return CLoadingProfilerSystem::GetLoadingProfilerCallstack();
#else
return nullptr;
#endif
}
CBootProfilerRecord* CSystem::StartBootSectionProfiler([[maybe_unused]] const char* name, [[maybe_unused]] const char* args)
{
#if defined(ENABLE_LOADING_PROFILER)
CBootProfiler& profiler = CBootProfiler::GetInstance();
return profiler.StartBlock(name, args);
#else
return nullptr;
#endif
}
void CSystem::StopBootSectionProfiler([[maybe_unused]] CBootProfilerRecord* record)
{
#if defined(ENABLE_LOADING_PROFILER)
CBootProfiler& profiler = CBootProfiler::GetInstance();
profiler.StopBlock(record);
#endif
}
void CSystem::StartBootProfilerSessionFrames([[maybe_unused]] const char* pName)
{
#if defined(ENABLE_LOADING_PROFILER)
CBootProfiler& profiler = CBootProfiler::GetInstance();
profiler.StartFrame(pName);
#endif
}
void CSystem::StopBootProfilerSessionFrames()
{
#if defined(ENABLE_LOADING_PROFILER)
CBootProfiler& profiler = CBootProfiler::GetInstance();
profiler.StopFrame();
#endif
}
bool CSystem::RegisterErrorObserver(IErrorObserver* errorObserver)
{
return stl::push_back_unique(m_errorObservers, errorObserver);

@ -92,13 +92,6 @@ static AZStd::vector<AZStd::string> GetModuleNames()
moduleNames.push_back("CryFont" MODULE_EXTENSION);
moduleNames.push_back("CrySystem" MODULE_EXTENSION);
if (gEnv && gEnv->pConsole)
{
string gameModuleNameRaw = gEnv->pConsole->GetCVar("sys_dll_game")->GetString();
gameModuleNameRaw.append(MODULE_EXTENSION);
moduleNames.push_back(gameModuleNameRaw.c_str());
}
#undef MODULE_EXTENSION
# if defined(LINUX)
@ -1076,14 +1069,6 @@ void CSystem::FatalError(const char* format, ...)
CryLogAlways("<CrySystem> Last System Error: %s", szSysErrorMessage);
}
if (const char* pLoadingProfilerCallstack = GetLoadingProfilerCallstack())
{
if (pLoadingProfilerCallstack[0])
{
CryLogAlways("<CrySystem> LoadingProfilerCallstack: %s", pLoadingProfilerCallstack);
}
}
if (GetUserCallback())
{
GetUserCallback()->OnError(szBuffer);

@ -1,115 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "CrySystem_precompiled.h"
#include <AzTest/AzTest.h>
#include <AzCore/UnitTest/UnitTest.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/Memory/AllocatorScope.h>
#include <AzCore/std/string/string.h>
#include <BootProfiler.h>
#if defined(ENABLE_LOADING_PROFILER)
namespace UnitTests
{
using BootProfilerTestAllocatorScope = AZ::AllocatorScope<AZ::LegacyAllocator, CryStringAllocator>;
class BootProfilerTest :
public ::testing::Test,
BootProfilerTestAllocatorScope,
UnitTest::TraceBusRedirector
{
public:
BootProfilerTest()
{
BootProfilerTestAllocatorScope::ActivateAllocators();
UnitTest::TraceBusRedirector::BusConnect();
}
~BootProfilerTest()
{
UnitTest::TraceBusRedirector::BusDisconnect();
BootProfilerTestAllocatorScope::DeactivateAllocators();
}
void SetUp() override
{
}
void TearDown() override
{
}
};
TEST_F(BootProfilerTest, BootProfilerTest_StartStopBlocksInThreads_Success)
{
CBootProfiler testProfiler;
const char scopeName[] = "TestScope";
const char blockArg[] = "TestArg";
const int numAttempts = 1000;
const int numThreads = 10;
auto switchSessionFunc = [&]() {
for (int sessionNum = 0; sessionNum < numAttempts; ++sessionNum)
{
auto sessionName = AZStd::string::format("TestSession%d", sessionNum);
testProfiler.StartSession(sessionName.c_str());
testProfiler.StopSession(sessionName.c_str());
}
};
auto testProfileFunc = [&]() {
for (int blockNum = 0; blockNum < numAttempts; ++blockNum)
{
auto someBlock = testProfiler.StartBlock(scopeName, blockArg);
testProfiler.StopBlock(someBlock);
}
};
AZStd::thread threadArray[numThreads];
AZStd::thread sessionThread = AZStd::thread(switchSessionFunc);
for (int i = 0; i < numThreads; ++i)
{
threadArray[i] = AZStd::thread(testProfileFunc);
}
for (int i = 0; i < numThreads; ++i)
{
threadArray[i].join();
}
sessionThread.join();
}
class FrameTestBootProfiler : public CBootProfiler
{
public:
FrameTestBootProfiler(int frameCount) : CBootProfiler()
{
SetFrameCount(frameCount);
}
};
TEST_F(BootProfilerTest, BootProfilerTest_FrameStartStop_Success)
{
const int numTestFrames = 10;
FrameTestBootProfiler testProfiler(numTestFrames);
for (int i = 0; i < numTestFrames; ++i)
{
testProfiler.StartFrame("TestFrame");
testProfiler.StopFrame();
}
}
} // namespace UnitTests
#endif

@ -75,15 +75,10 @@ TEST(CrySystemMaterialUtilsTests, MaterialUtilsTestPrefixes)
TEST(CrySystemMaterialUtilsTests, MaterialUtilsTestGameName)
{
char tempBuffer[AZ_MAX_PATH_LEN];
ICVar* pGameNameCVar = nullptr;
if ((gEnv)&&(gEnv->pConsole))
{
pGameNameCVar = gEnv->pConsole->GetCVar("sys_game_folder");
}
azsnprintf(tempBuffer, AZ_MAX_PATH_LEN, ".\\%s\\materials\\blahblah.mat.mat.abc.test", pGameNameCVar ? pGameNameCVar->GetString() : "SamplesProject");
auto projectName = AZ::Utils::GetProjectName();
azsnprintf(tempBuffer, AZ_MAX_PATH_LEN, ".\\%s\\materials\\blahblah.mat.mat.abc.test", projectName.c_str());
MaterialUtils::UnifyMaterialName(tempBuffer);
EXPECT_TRUE(strcmp(tempBuffer, "materials/blahblah.mat.mat.abc") == 0);
}
}

@ -60,7 +60,7 @@ namespace CryPakUnitTests
AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
ASSERT_NE(nullptr, fileIo);
constexpr const char* testPakPath = "@cache@/archivecontainerlevel.pak";
constexpr const char* testPakPath = "@usercache@/archivecontainerlevel.pak";
char resolvedArchivePath[AZ_MAX_PATH_LEN] = { 0 };
EXPECT_TRUE(fileIo->ResolvePath(testPakPath, resolvedArchivePath, AZ_MAX_PATH_LEN));
@ -116,7 +116,7 @@ namespace CryPakUnitTests
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds{ 100 });
// helper paths and strings
AZStd::string gameFolder = fileIo->GetAlias("@cache@");
AZStd::string gameFolder = fileIo->GetAlias("@usercache@");
AZStd::string testFile = "unittest.bin";
AZStd::string testFilePath = gameFolder + "\\" + testFile;

@ -297,7 +297,7 @@ void CVisRegTest::CaptureSample(const SCmd& cmd)
if (m_cmdFreq == 1) // Final sample
{
// Screenshot
stack_string filename("@cache@/TestResults/VisReg/"); // the default unaliased assets folder is read-only!
stack_string filename("@usercache@/TestResults/VisReg/"); // the default unaliased assets folder is read-only!
filename += m_testName + "/" + cmd.args.c_str();
gEnv->pRenderer->ScreenShot(filename);
@ -335,7 +335,7 @@ void CVisRegTest::Finish()
bool CVisRegTest::WriteResults()
{
stack_string filename("@cache@/TestResults/VisReg/");
stack_string filename("@usercache@/TestResults/VisReg/");
filename += m_testName + "/visreg_results.xml";
AZ::IO::HandleType fileHandle = fxopen(filename.c_str(), "wb");

@ -26,7 +26,6 @@ set(FILES
IDebugCallStack.cpp
AsyncPakManager.cpp
Log.cpp
BootProfiler.cpp
SystemRender.cpp
NotificationNetwork.cpp
PhysRenderer.cpp
@ -91,7 +90,6 @@ set(FILES
WindowsConsole.h
XConsole.h
XConsoleVariable.h
BootProfiler.h
crash_face.bmp
ImageHandler.h
ImageHandler.cpp
@ -121,11 +119,9 @@ set(FILES
XML/WriteXMLSource.cpp
ZipFile.h
ZipFileFormat_info.h
LoadingProfiler.cpp
PerfHUD.cpp
ProfileLogSystem.cpp
Sampler.cpp
LoadingProfiler.h
PerfHUD.h
ProfileLogSystem.h
Sampler.h

@ -902,7 +902,7 @@ void ShadersOptimizeHelper(CallableT setupParserBin, const char* logString)
{
setupParserBin();
CryLogAlways("\nStarting shaders optimizing for %s...", logString);
AZStd::string str = "@cache@/" + gRenDev->m_cEF.m_ShadersCache;
AZStd::string str = "@usercache@/" + gRenDev->m_cEF.m_ShadersCache;
iLog->Log("Optimize shader cache folder: '%s'", gRenDev->m_cEF.m_ShadersCache.c_str());
gRenDev->m_cEF.mfOptimiseShaders(str.c_str(), false);
}
@ -2396,7 +2396,7 @@ void CRenderer::InitRenderer()
CV_r_ShaderCompilerFolderSuffix = REGISTER_STRING("r_ShaderCompilerFolderSuffix", "", VF_NULL,
"Usage: r_ShaderCompilerFolderSuffix suffix \n"
"Default is empty. Set to some other value to append this suffix to the sys_game_folder when compiling shaders");
"Default is empty. Set to some other value to append this suffix to the project name when compiling shaders");
{
const SFileVersion& ver = gEnv->pSystem->GetFileVersion();

@ -20,6 +20,7 @@
#include <AzCore/Socket/AzSocket.h>
#include <AzCore/NativeUI/NativeUIRequests.h>
#include <AzCore/PlatformId/PlatformId.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/Network/SocketConnection.h>
#include <AzFramework/Asset/AssetSystemTypes.h>
@ -306,23 +307,20 @@ namespace NRemoteCompiler
m_RequestLineRootFolder = "";
ICVar* pGameFolder = gEnv->pConsole->GetCVar("sys_game_folder");
auto projectName = AZ::Utils::GetProjectName();
ICVar* pCompilerFolderSuffix = CRenderer::CV_r_ShaderCompilerFolderSuffix;
if (pGameFolder)
if (!projectName.empty())
{
string folder = pGameFolder->GetString();
folder.Trim();
if (!folder.empty())
if (pCompilerFolderSuffix)
{
if (pCompilerFolderSuffix)
{
string suffix = pCompilerFolderSuffix->GetString();
suffix.Trim();
folder.append(suffix);
}
m_RequestLineRootFolder = folder + string("/");
string suffix = pCompilerFolderSuffix->GetString();
suffix.Trim();
projectName.append(suffix);
}
projectName.append("/");
m_RequestLineRootFolder.assign(projectName.c_str(), projectName.size());
}
if (m_RequestLineRootFolder.empty())

@ -13,16 +13,61 @@
#include <AzTest/AzTest.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/Memory/AllocatorScope.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/UnitTest/UnitTest.h>
#include "Mocks/IConsoleMock.h"
#include "Mocks/ICVarMock.h"
#include "Mocks/ISystemMock.h"
#include "RemoteCompiler.h"
namespace AZ
{
class SettingsRegistrySimpleMock;
using NiceSettingsRegistrySimpleMock = ::testing::NiceMock<SettingsRegistrySimpleMock>;
class SettingsRegistrySimpleMock : public AZ::SettingsRegistryInterface
{
public:
MOCK_CONST_METHOD1(GetType, Type(AZStd::string_view));
MOCK_CONST_METHOD2(Visit, bool(Visitor&, AZStd::string_view));
MOCK_CONST_METHOD2(Visit, bool(const VisitorCallback&, AZStd::string_view));
MOCK_METHOD1(RegisterNotifier, NotifyEventHandler(const NotifyCallback&));
MOCK_METHOD1(RegisterNotifier, NotifyEventHandler(NotifyCallback&&));
MOCK_CONST_METHOD2(Get, bool(bool&, AZStd::string_view));
MOCK_CONST_METHOD2(Get, bool(s64&, AZStd::string_view));
MOCK_CONST_METHOD2(Get, bool(u64&, AZStd::string_view));
MOCK_CONST_METHOD2(Get, bool(double&, AZStd::string_view));
MOCK_CONST_METHOD2(Get, bool(AZStd::string&, AZStd::string_view));
MOCK_CONST_METHOD2(Get, bool(FixedValueString&, AZStd::string_view));
MOCK_CONST_METHOD3(GetObject, bool(void*, Uuid, AZStd::string_view));
MOCK_METHOD2(Set, bool(AZStd::string_view, bool));
MOCK_METHOD2(Set, bool(AZStd::string_view, s64));
MOCK_METHOD2(Set, bool(AZStd::string_view, u64));
MOCK_METHOD2(Set, bool(AZStd::string_view, double));
MOCK_METHOD2(Set, bool(AZStd::string_view, AZStd::string_view));
MOCK_METHOD2(Set, bool(AZStd::string_view, const char*));
MOCK_METHOD3(SetObject, bool(AZStd::string_view, const void*, Uuid));
MOCK_METHOD1(Remove, bool(AZStd::string_view));
MOCK_METHOD3(MergeCommandLineArgument, bool(AZStd::string_view, AZStd::string_view, const CommandLineArgumentSettings&));
MOCK_METHOD2(MergeSettings, bool(AZStd::string_view, Format));
MOCK_METHOD4(MergeSettingsFile, bool(AZStd::string_view, Format, AZStd::string_view, AZStd::vector<char>*));
MOCK_METHOD5(
MergeSettingsFolder,
bool(AZStd::string_view, const Specializations&, AZStd::string_view, AZStd::string_view, AZStd::vector<char>*));
};
} // namespace AZ
namespace NRemoteCompiler
{
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::DoAll;
using SystemAllocatorScope = AZ::AllocatorScope<AZ::LegacyAllocator, CryStringAllocator>;
@ -65,9 +110,12 @@ namespace NRemoteCompiler
SystemAllocatorScope::ActivateAllocators();
m_priorEnv = gEnv;
m_priorSettingsRegistry = AZ::SettingsRegistry::Get();
m_data.reset(new DataMembers);
AZ::SettingsRegistry::Register(&m_data->m_settings);
ON_CALL(m_data->m_console, GetCVar(_))
.WillByDefault(Return(&m_data->m_cvarMock));
@ -89,6 +137,11 @@ namespace NRemoteCompiler
void TearDown() override
{
gEnv = m_priorEnv;
AZ::SettingsRegistry::Unregister(&m_data->m_settings);
if (m_priorSettingsRegistry)
{
AZ::SettingsRegistry::Register(m_priorSettingsRegistry);
}
m_data.reset();
SystemAllocatorScope::DeactivateAllocators();
AllocatorsTestFixture::TearDown();
@ -99,12 +152,14 @@ namespace NRemoteCompiler
NiceMock<SystemMock> m_system;
NiceMock<ConsoleMock> m_console;
NiceMock<CVarMock> m_cvarMock;
AZ::NiceSettingsRegistrySimpleMock m_settings;
SSystemGlobalEnvironment m_stubEnv;
};
AZStd::unique_ptr<DataMembers> m_data;
SSystemGlobalEnvironment* m_priorEnv = nullptr;
AZ::SettingsRegistryInterface* m_priorSettingsRegistry = nullptr;
};
// allow punch through to PRIVATE functions so that they do not need to be made PUBLIC.
@ -130,7 +185,9 @@ namespace NRemoteCompiler
TEST_F(RemoteCompilerTest, CShaderSrv_Constructor_WithNoGameName_Fails)
{
EXPECT_CALL(m_data->m_cvarMock, GetString());
using namespace ::testing;
AZ::SettingsRegistryInterface::FixedValueString regResult;
EXPECT_CALL(m_data->m_settings, Get(regResult, _));
AZ_TEST_START_TRACE_SUPPRESSION;
ShaderSrvUnitTestAccessor srv;
@ -140,8 +197,10 @@ namespace NRemoteCompiler
TEST_F(RemoteCompilerTest, CShaderSrv_Constructor_WithValidGameName_Succeeds)
{
// when we construct the server it calls get on the game name
EXPECT_CALL(m_data->m_cvarMock, GetString())
.WillOnce(Return("StarterGame"));
using namespace ::testing;
AZ::SettingsRegistryInterface::FixedValueString projectName;
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
ShaderSrvUnitTestAccessor srv;
}
@ -149,11 +208,12 @@ namespace NRemoteCompiler
TEST_F(RemoteCompilerTest, CShaderSrv_EncapsulateRequestInEngineConnectionProtocol_EmptyData_Fails)
{
// when we construct the server it calls get on the game name
EXPECT_CALL(m_data->m_cvarMock, GetString())
.WillOnce(Return("StarterGame"));
using namespace ::testing;
AZ::SettingsRegistryInterface::FixedValueString projectName;
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
ShaderSrvUnitTestAccessor srv;
std::vector<uint8> testVector;
@ -165,11 +225,12 @@ namespace NRemoteCompiler
TEST_F(RemoteCompilerTest, CShaderSrv_EncapsulateRequestInEngineConnectionProtocol_ValidData_EmptyServerList_Fails)
{
// when we construct the server it calls get on the game name
EXPECT_CALL(m_data->m_cvarMock, GetString())
.WillOnce(Return("StarterGame"));
using namespace ::testing;
AZ::SettingsRegistryInterface::FixedValueString projectName;
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
ShaderSrvUnitTestAccessor srv;
EXPECT_CALL(m_data->m_cvarMock, GetString())
.WillRepeatedly(Return("")); // empty server list
@ -186,11 +247,12 @@ namespace NRemoteCompiler
TEST_F(RemoteCompilerTest, CShaderSrv_EncapsulateRequestInEngineConnectionProtocol_ValidInputs_Succeeds)
{
// when we construct the server it calls get on the game name
EXPECT_CALL(m_data->m_cvarMock, GetString())
.WillOnce(Return("StarterGame"));
using namespace ::testing;
AZ::SettingsRegistryInterface::FixedValueString projectName;
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
ShaderSrvUnitTestAccessor srv;
// After this, it will repeatedly call get cvar to get the server address:
const char* testList = "10.20.30.40";
@ -207,11 +269,12 @@ namespace NRemoteCompiler
TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_EmptyData_Fails)
{
// when we construct the server it calls get on the game name
EXPECT_CALL(m_data->m_cvarMock, GetString())
.WillOnce(Return("StarterGame"));
using namespace ::testing;
AZ::SettingsRegistryInterface::FixedValueString projectName;
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
ShaderSrvUnitTestAccessor srv;
// After this, it will repeatedly call get cvar to get the server address:
const char* testList = "10.20.30.40";
@ -221,7 +284,7 @@ namespace NRemoteCompiler
std::vector<uint8> testVector;
std::string testString("empty");
// test for empty data - recvfailed expected (error emitted)
// test for empty data - RecvFailed expected (error emitted)
AZ_TEST_START_TRACE_SUPPRESSION;
testString = "empty";
testVector.assign(testString.begin(), testString.end());
@ -232,11 +295,12 @@ namespace NRemoteCompiler
TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_IncompleteData_Fails)
{
// when we construct the server it calls get on the game name
EXPECT_CALL(m_data->m_cvarMock, GetString())
.WillOnce(Return("StarterGame"));
using namespace ::testing;
AZ::SettingsRegistryInterface::FixedValueString projectName;
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
ShaderSrvUnitTestAccessor srv;
// After this, it will repeatedly call get cvar to get the server address:
const char* testList = "10.20.30.40";
@ -247,7 +311,7 @@ namespace NRemoteCompiler
std::string testString("incomplete");
testVector.assign(testString.begin(), testString.end());
// test for incomplete data - recvfailed expected
// test for incomplete data - RecvFailed expected
AZ_TEST_START_TRACE_SUPPRESSION;
EXPECT_EQ(srv.SendRequestViaEngineConnection(testVector), EServerError::ESRecvFailed);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
@ -256,11 +320,12 @@ namespace NRemoteCompiler
TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_CorruptData_Fails)
{
// when we construct the server it calls get on the game name
EXPECT_CALL(m_data->m_cvarMock, GetString())
.WillOnce(Return("StarterGame"));
using namespace ::testing;
AZ::SettingsRegistryInterface::FixedValueString projectName;
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
ShaderSrvUnitTestAccessor srv;
// After this, it will repeatedly call get cvar to get the server address:
const char* testList = "10.20.30.40";
@ -271,7 +336,7 @@ namespace NRemoteCompiler
std::string testString("corrupt");
testVector.assign(testString.begin(), testString.end());
// test for incomplete data - recvfailed expected
// test for incomplete data - RecvFailed expected
AZ_TEST_START_TRACE_SUPPRESSION;
EXPECT_EQ(srv.SendRequestViaEngineConnection(testVector), EServerError::ESRecvFailed);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
@ -280,11 +345,12 @@ namespace NRemoteCompiler
TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_CompileError_Fails_ReturnsText)
{
// when we construct the server it calls get on the game name
EXPECT_CALL(m_data->m_cvarMock, GetString())
.WillOnce(Return("StarterGame"));
using namespace ::testing;
AZ::SettingsRegistryInterface::FixedValueString projectName;
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
ShaderSrvUnitTestAccessor srv;
// After this, it will repeatedly call get cvar to get the server address:
const char* testList = "10.20.30.40";
@ -294,11 +360,11 @@ namespace NRemoteCompiler
std::vector<uint8> testVector;
std::string testString("corrupt");
testVector.assign(testString.begin(), testString.end());
// test for an actual compile error - decompressed compile erro rexpected to be attached.
// test for an actual compile error - decompressed compile error expected to be attached.
testString = "compile_failure";
testVector.assign(testString.begin(), testString.end());
EXPECT_EQ(srv.SendRequestViaEngineConnection(testVector), EServerError::ESCompileError);
// validate hte compile erorr decompressed successfully
// validate the compile error decompressed successfully
const char* expected_decode = "decompressed_plaintext";
EXPECT_EQ(testVector.size(), strlen(expected_decode));
EXPECT_EQ(memcmp(testVector.data(), expected_decode, strlen(expected_decode)), 0);
@ -307,11 +373,12 @@ namespace NRemoteCompiler
TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_ValidInput_Succeeds_ReturnsText)
{
// when we construct the server it calls get on the game name
EXPECT_CALL(m_data->m_cvarMock, GetString())
.WillOnce(Return("StarterGame"));
using namespace ::testing;
AZ::SettingsRegistryInterface::FixedValueString projectName;
EXPECT_CALL(m_data->m_settings, Get(projectName, _))
.WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
ShaderSrvUnitTestAccessor srv;
// After this, it will repeatedly call get cvar to get the server address:
const char* testList = "10.20.30.40";

@ -375,7 +375,7 @@ void CShaderMan::mfInitShadersCacheMissLog()
// create valid path
gEnv->pCryPak->MakeDir(g_szTestResults);
m_ShaderCacheMissPath = string("@cache@\\Shaders\\ShaderCacheMisses.txt"); // do we want this here, or maybe in @log@ ?
m_ShaderCacheMissPath = string("@usercache@\\Shaders\\ShaderCacheMisses.txt"); // do we want this here, or maybe in @log@ ?
// load data which is already stored
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
@ -2175,9 +2175,10 @@ void CShaderMan::mfOptimiseShaders(const char* szFolder, bool bForce)
for (i = 0; i < Names.size(); i++)
{
const char* szName = Names[i].c_str();
if (!strncmp(szName, "@cache@/", 7))
constexpr AZStd::string_view userCache = "@usercache@/";
if (szName == userCache)
{
szName += 7;
szName += userCache.size();
}
pCache = CHWShader::mfInitCache(szName, NULL, false, 0, false);
if (!pCache || !pCache->m_pRes[CACHE_USER])

@ -1589,7 +1589,7 @@ void CShaderMan::mfInit (void)
#else
m_ShadersCache = CONCAT_PATHS(g_shaderCache, "D3D11");
#endif
m_szCachePath = "@cache@/";
m_szCachePath = "@usercache@/";
if (CRenderer::CV_r_shadersImport == 3)
{

@ -162,7 +162,7 @@ bool CShaderSerialize::OpenSResource(const char* szName, SSShaderRes* pSR, CSha
stack_string szReadOnly = szName;
// ShaderCacheGen behavior:
// CACHE_READONLY is not really used when exporting the .fxb, so we append the @cache@ alias to the relative shader path
// CACHE_READONLY is not really used when exporting the .fxb, so we append the @usercache@ alias to the relative shader path
// here as well. We cannot just leave this as the relative Shaders/Cache/Foo.fxb value because then it creates a new
// file in the asset cache as @assets@/Shaders/Cache/Foo.fxb, which is illegal (since only AP has the authority to write here)
// Game runtime behavior:

@ -3490,7 +3490,7 @@ void CHWShader_D3D::mfSaveCGFile(const char* scr, const char* path)
}
else
{
sprintf_s(name, "@cache@/shaders/fxerror/%s(GL%llx)/(LT%x)(RT%llx)/(MD%x)(MDV%x)(PSS%llx)(ST%llx).cg", GetName(), m_pCurInst->m_Ident.m_GLMask, m_pCurInst->m_Ident.m_LightMask, m_pCurInst->m_Ident.m_RTMask, m_pCurInst->m_Ident.m_MDMask, m_pCurInst->m_Ident.m_MDVMask, m_pCurInst->m_Ident.m_pipelineState.opaque, m_pCurInst->m_Ident.m_STMask);
sprintf_s(name, "@usercache@/shaders/fxerror/%s(GL%llx)/(LT%x)(RT%llx)/(MD%x)(MDV%x)(PSS%llx)(ST%llx).cg", GetName(), m_pCurInst->m_Ident.m_GLMask, m_pCurInst->m_Ident.m_LightMask, m_pCurInst->m_Ident.m_RTMask, m_pCurInst->m_Ident.m_MDMask, m_pCurInst->m_Ident.m_MDVMask, m_pCurInst->m_Ident.m_pipelineState.opaque, m_pCurInst->m_Ident.m_STMask);
}
AZ::IO::HandleType fileHandle;
@ -4672,7 +4672,7 @@ bool CAsyncShaderTask::CompileAsyncShader(SShaderAsyncInfo* pAsync)
CryFixedStringT<1024> hlslPath;
// Create a directory for this shader type, strip the .fxcb extension from the folder name
shaderSourceOutputFolder.Format("@cache@/%s",pAsync->m_pShader->m_pDevCache->m_Name.c_str());
shaderSourceOutputFolder.Format("@usercache@/%s",pAsync->m_pShader->m_pDevCache->m_Name.c_str());
PathUtil::RemoveExtension(shaderSourceOutputFolder);
gEnv->pFileIO->CreatePath(shaderSourceOutputFolder);

@ -70,6 +70,7 @@
#endif
#include "../Common/RenderCapabilities.h"
#include <AzCore/Utils/Utils.h>
#ifdef WIN32
// Count monitors helper
@ -1634,8 +1635,8 @@ WIN_HWND CD3D9Renderer::Init([[maybe_unused]] int x, [[maybe_unused]] int y, int
iLog->Log ("Direct3D driver is creating...");
iLog->Log ("Crytek Direct3D driver version %4.2f (%s <%s>)", VERSION_D3D, __DATE__, __TIME__);
const char* sGameName = iConsole->GetCVar("sys_game_name")->GetString();
cry_strcpy(m_WinTitle, sGameName);
auto projectName = AZ::Utils::GetProjectName();
cry_strcpy(m_WinTitle, projectName.c_str());
iLog->Log ("Creating window called '%s' (%dx%d)", m_WinTitle, width, height);

@ -20,6 +20,8 @@
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Debug/LocalFileEventLogger.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/Memory/OverrunDetectionAllocator.h>
@ -99,6 +101,18 @@ namespace AZ
return environment ? environment->Get() : nullptr;
}
ComponentApplication::EventLoggerDeleter::EventLoggerDeleter() noexcept= default;
ComponentApplication::EventLoggerDeleter::EventLoggerDeleter(bool skipDelete) noexcept
: m_skipDelete{skipDelete}
{}
void ComponentApplication::EventLoggerDeleter::operator()(AZ::Debug::LocalFileEventLogger* ptr)
{
if (!m_skipDelete)
{
delete ptr;
}
}
//=========================================================================
// ComponentApplication::Descriptor
// [5/30/2012]
@ -159,6 +173,80 @@ namespace AZ
return true;
};
//! SettingsRegistry notifier handler which updates relevant registry settings based
//! on an update to '/Amazon/AzCore/Bootstrap/project_path' key.
struct UpdateProjectSettingsEventHandler
{
UpdateProjectSettingsEventHandler(AZ::SettingsRegistryInterface& registry)
: m_registry{ registry }
{
}
void operator()(AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
UpdateProjectSpecializationInRegistry(path);
}
//! Add the project name as a specialization underneath the /Amazon/AzCore/Settings/Specializations path
//! and remove the current project name specialization if one exists.
void UpdateProjectSpecializationInRegistry(AZStd::string_view path)
{
auto projectPathKey =
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
+ "/project_path";
if (path == projectPathKey)
{
AZ::SettingsRegistryInterface::FixedValueString newProjectPath;
if (m_registry.Get(newProjectPath, path) && !newProjectPath.empty())
{
// Make the path absolute by appending to app root, in case project path is relative.
// If the project path is already absolute it will remain the same.
// If we turn it from a relative path to an absolute path, write-back the absolute path to the registry.
AZ::IO::FixedMaxPath projectPath = AZ::SettingsRegistryMergeUtils::FindEngineRoot(m_registry) / newProjectPath;
if (projectPath.Compare(newProjectPath.c_str()))
{
m_registry.Set(path, projectPath.Native());
}
// Merge the project.json file into settings registry under ProjectSettingsRootKey path.
AZ::IO::FixedMaxPath projectMetadataFile{ projectPath };
projectMetadataFile /= "project.json";
m_registry.MergeSettingsFile(projectMetadataFile.Native(),
AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey);
// Get the 'project_name' value from what was in the 'project.json' file...
auto projectNameKey =
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey)
+ "/project_name";
AZ::SettingsRegistryInterface::FixedValueString projectSpecialization;
if (m_registry.Get(projectSpecialization, projectNameKey))
{
auto specializationKey = AZ::SettingsRegistryInterface::FixedValueString::format(
"%s/%s", AZ::SettingsRegistryMergeUtils::SpecializationsRootKey, projectSpecialization.c_str());
if (m_currentSpecialization != specializationKey)
{
m_registry.Set(specializationKey, true);
if (!m_currentSpecialization.empty())
{
// Remove the previous Project Name from the specialization path if it was set.
m_registry.Remove(m_currentSpecialization);
}
m_currentSpecialization = specializationKey;
// Update all the runtime file paths based on the new "project_path" value.
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry);
}
}
}
}
}
private:
AZ::SettingsRegistryInterface::FixedValueString m_currentSpecialization;
AZ::SettingsRegistryInterface& m_registry;
};
void ComponentApplication::Descriptor::AllocatorRemapping::Reflect(ReflectContext* context, ComponentApplication* app)
{
(void)app;
@ -276,6 +364,7 @@ namespace AZ
}
ComponentApplication::ComponentApplication(int argC, char** argV)
: m_eventLogger{}
{
if (argV)
{
@ -284,11 +373,23 @@ namespace AZ
}
else
{
azstrcpy(m_commandLineBuffer, AZ_ARRAY_SIZE(m_commandLineBuffer), "no_argv_supplied");
azstrcpy(m_commandLineBuffer, AZ_ARRAY_SIZE(m_commandLineBuffer), "no_argv_supplied");
// use a "valid" value here. This is because Qt and potentially other third party libraries require
// that ArgC be 'at least 1' and that (*argV)[0] be a valid pointer to a real null terminated string.
m_argC = 1;
m_argV = &m_commandLineBufferAddress;
m_argC = 1;
m_argV = &m_commandLineBufferAddress;
}
// Create the Event logger if it doesn't exist, otherwise reuse the one registered
// with the AZ::Interface
if (AZ::Interface<AZ::Debug::IEventLogger>::Get() == nullptr)
{
m_eventLogger.reset(new AZ::Debug::LocalFileEventLogger);
}
else
{
m_eventLogger = EventLoggerPtr(static_cast<AZ::Debug::LocalFileEventLogger*>(AZ::Interface<AZ::Debug::IEventLogger>::Get()),
EventLoggerDeleter{ true });
}
// Initializes the OSAllocator and SystemAllocator as soon as possible
@ -297,6 +398,7 @@ namespace AZ
// Now that the Allocators are initialized, the Command Line parameters can be parsed
m_commandLine.Parse(m_argC, m_argV);
ParseCommandLine(m_commandLine);
// Create the settings registry and register it with the AZ interface system
// This is done after the AppRoot has been calculated so that the Bootstrap.cfg
@ -304,62 +406,34 @@ namespace AZ
m_settingsRegistry = AZStd::make_unique<SettingsRegistryImpl>();
// Register the Settings Registry with the AZ Interface if there isn't one registered already
if (AZ::SettingsRegistry::Get() == nullptr)
if (SettingsRegistry::Get() == nullptr)
{
SettingsRegistry::Register(m_settingsRegistry.get());
}
// Add the Command Line arguments into the SettingsRegistry
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_StoreCommandLine(*m_settingsRegistry, m_commandLine);
SettingsRegistryMergeUtils::StoreCommandLineToRegistry(*m_settingsRegistry, m_commandLine);
// Merge Command Line arguments
constexpr bool executeRegDumpCommands = false;
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
// Query for the Executable Path using OS specific functions
CalculateExecutablePath();
// If the current platform returns an engaged optional from Utils::GetDefaultAppRootPath(), that is used
// for the application root other the application root is found by scanning upwards from the Executable Directory
// for a bootstrap.cfg file
CalculateAppRoot(nullptr);
// Add a notifier to update the /Amazon/AzCore/Settings/Specializations
// when the sys_game_folder property changes within the SettingsRegistry
// currentGameName is bound by value in order to allow the lambda to have a member variable that
// can track the current game specialization before it changes
AZ::SettingsRegistryInterface::FixedValueString currentGameSpecialization;
auto GameProjectChanged = [currentGameSpecialization](AZStd::string_view path, AZ::SettingsRegistryInterface::Type type) mutable
{
constexpr auto projectKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)
+ "/sys_game_folder";
if (projectKey == path && type == AZ::SettingsRegistryInterface::Type::String)
{
auto registry = AZ::SettingsRegistry::Get();
AZ::SettingsRegistryInterface::FixedValueString newGameName;
if (registry && registry->Get(newGameName, path) && !newGameName.empty())
{
auto specializationKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s",
AZ::SettingsRegistryMergeUtils::SpecializationsRootKey, newGameName.c_str());
// Determine the path to the engine
CalculateEngineRoot();
if (currentGameSpecialization != specializationKey)
{
registry->Set(specializationKey, true);
if (!currentGameSpecialization.empty())
{
// Remove the previous Game Name from the specialization path if it was set
registry->Remove(currentGameSpecialization);
}
// Update the currentGameSpecialization
currentGameSpecialization = specializationKey;
// If the current platform returns an engaged optional from Utils::GetDefaultAppRootPath(), that is used
// for the application root.
CalculateAppRoot();
// Update all the runtime filepaths based on the new "sys_game_folder" value
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
}
}
}
};
m_gameProjectChangedHandler = m_settingsRegistry->RegisterNotifier(AZStd::move(GameProjectChanged));
// Add a notifier to update the /Amazon/AzCore/Settings/Specializations
// when the 'project_path' property changes within the SettingsRegistry
m_projectChangedHandler = m_settingsRegistry->RegisterNotifier(UpdateProjectSettingsEventHandler{ *m_settingsRegistry });
// Merge the bootstrap.cfg file into the Settings Registry as soon as the OSAllocator has been created.
SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(*m_settingsRegistry);
constexpr bool executeRegDumpCommands = false;
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
@ -391,11 +465,11 @@ namespace AZ
Destroy();
}
// The m_gameProjectChangedHandler stores an AZStd::function internally
// The m_projectChangedHandler stores an AZStd::function internally
// which allocates using the AZ SystemAllocator
// m_gameProjectChangedHandler is being default value initialized
// m_projectChangedHandler is being default value initialized
// to clear out the AZStd::function
m_gameProjectChangedHandler = {};
m_projectChangedHandler = {};
// Delete the AZ::IConsole if it was created by this application instance
if (m_ownsConsole)
@ -412,6 +486,10 @@ namespace AZ
}
m_settingsRegistry.reset();
// Set AZ::CommandLine to an empty object to clear out allocated memory before the allocators
// are destroyed
m_commandLine = {};
DestroyAllocator();
}
@ -421,17 +499,6 @@ namespace AZ
AZ_Assert(!m_isStarted, "Component application already started!");
m_startupParameters = startupParameters;
// Invokes CalculateAppRoot() again this time with the appRootOverride startup parameter
// supplied in order to allow overriding the AppRoot calculated in the constructor
if (m_startupParameters.m_appRootOverride)
{
CalculateAppRoot(m_startupParameters.m_appRootOverride);
// Re-check for the bootstrap.cfg file again using the appRoot override and update the file paths
SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(*m_settingsRegistry);
constexpr bool executeRegDumpCommands = false;
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
}
m_descriptor = descriptor;
@ -461,20 +528,14 @@ namespace AZ
void ComponentApplication::CreateCommon()
{
{
AZ::SettingsRegistryInterface::FixedValueString registryValue;
m_settingsRegistry->Get(registryValue, AZ::SettingsRegistryMergeUtils::FilePathKey_DevWriteStorage);
AZ::IO::FixedMaxPath outputPath{ registryValue };
AZ::IO::FixedMaxPath outputPath;
m_settingsRegistry->Get(outputPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_DevWriteStorage);
outputPath /= "eventlogger";
registryValue.clear();
AZ::IO::FixedMaxPathString baseFileName{ "EventLog" }; // default name
if (m_settingsRegistry->Get(registryValue, AZ::SettingsRegistryMergeUtils::BuildTargetNameKey))
{
baseFileName = registryValue;
}
m_settingsRegistry->Get(baseFileName, AZ::SettingsRegistryMergeUtils::BuildTargetNameKey);
m_eventLogger.Start(outputPath.c_str(), baseFileName.c_str());
m_eventLogger->Start(outputPath.Native(), baseFileName);
}
CreateDrillers();
@ -515,7 +576,9 @@ namespace AZ
LoadModules();
// Execute user.cfg after modules have been loaded but before processing any command-line overrides
m_console->ExecuteConfigFile("@root@/user.cfg");
AZ::IO::FixedMaxPath platformCachePath;
m_settingsRegistry->Get(platformCachePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder);
m_console->ExecuteConfigFile((platformCachePath / "user.cfg").Native());
// Parse the command line parameters for console commands after modules have loaded
m_console->ExecuteCommandLine(m_commandLine);
@ -601,7 +664,7 @@ namespace AZ
m_drillerManager = nullptr;
}
m_eventLogger.Stop();
m_eventLogger->Stop();
// Clear the descriptor to deallocate all strings (owned by ModuleDescriptor)
m_descriptor = Descriptor();
@ -775,6 +838,46 @@ namespace AZ
}
}
void ComponentApplication::ParseCommandLine(const AZ::CommandLine& commandLine)
{
struct OptionKeyToRegsetKey
{
AZStd::string_view m_optionKey;
AZStd::string m_regsetKey;
};
// Provide overrides for the engine root, the project root and the project cache root
AZStd::array commandOptions = {
OptionKeyToRegsetKey{ "engine-path", AZStd::string::format("%s/engine_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) },
OptionKeyToRegsetKey{ "project-path", AZStd::string::format("%s/project_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) },
OptionKeyToRegsetKey{ "project-cache-path", AZStd::string::format("%s/project_cache_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) }
};
AZStd::fixed_vector<AZStd::string, commandOptions.size()> overrideArgs;
for (auto&& [optionKey, regsetKey] : commandOptions)
{
if (size_t optionCount = commandLine.GetNumSwitchValues(optionKey); optionCount > 0)
{
// Use the last supplied command option value to override previous values
auto overrideArg = AZStd::string::format(R"(--regset="%s=%s")", regsetKey.c_str(),
commandLine.GetSwitchValue(optionKey, optionCount - 1).c_str());
overrideArgs.emplace_back(AZStd::move(overrideArg));
}
}
if (!overrideArgs.empty())
{
// Dump the input command line, add the additional option overrides
// and Parse the new command line into the Component Application command line
AZ::CommandLine::ParamContainer commandLineArgs;
commandLine.Dump(commandLineArgs);
commandLineArgs.insert(commandLineArgs.end(), AZStd::make_move_iterator(overrideArgs.begin()),
AZStd::make_move_iterator(overrideArgs.end()));
m_commandLine.Parse(commandLineArgs);
}
}
void ComponentApplication::MergeSettingsToRegistry(SettingsRegistryInterface& registry)
{
SettingsRegistryInterface::Specializations specializations;
@ -788,14 +891,14 @@ namespace AZ
// In development builds apply the developer registry and the command line to allow early overrides. This will
// allow developers to override things like default paths or Asset Processor connection settings. Any additional
// values will be replaced by later loads, so this step will happen again at the end of loading.
SettingsRegistryMergeUtils::MergeSettingsToRegistry_DevRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_UserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false);
#endif
SettingsRegistryMergeUtils::MergeSettingsToRegistry_EngineRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_GemRegistries(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
#if defined(AZ_DEBUG_BUILD) || defined(AZ_PROFILE_BUILD)
SettingsRegistryMergeUtils::MergeSettingsToRegistry_DevRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_UserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, true);
#endif
}
@ -1013,7 +1116,7 @@ namespace AZ
struct GemModuleLoadData
{
AZ::OSString m_gemName;
AZ::OSString m_dynamicLibraryPath;
AZStd::vector<AZ::OSString> m_dynamicLibraryPaths;
bool m_autoLoad{ true };
};
@ -1069,19 +1172,18 @@ namespace AZ
}
}
void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override
void Visit(AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override
{
if (valueName == "Module" && !value.empty())
// Remove last path segment and check if the key corresponds to the Modules array
AZStd::optional<AZStd::string_view> moduleIndex = AZ::StringFunc::TokenizeLast(path, "/");
if (path.ends_with("/Modules"))
{
// Strip off the Module entry from the path
auto moduleKey = AZ::StringFunc::TokenizeLast(path, "/");
if (!moduleKey)
{
return;
}
// Remove the "Modules" path segment to be at the GemName key
AZ::StringFunc::TokenizeLast(path, "/");
if (auto moduleLoadData = FindGemModuleEntry(path); moduleLoadData != nullptr)
{
moduleLoadData->m_dynamicLibraryPath = value;
// Just use Json Serialization to load all the array elements
moduleLoadData->m_dynamicLibraryPaths.emplace_back(value);
}
}
}
@ -1111,30 +1213,41 @@ namespace AZ
}
};
constexpr size_t RegistryKeySize = 64;
auto gemModuleKey = AZStd::fixed_string<RegistryKeySize>::format("%s/Gems", AZ::SettingsRegistryMergeUtils::OrganizationRootKey);
auto gemModuleKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/Gems", AZ::SettingsRegistryMergeUtils::OrganizationRootKey);
ModuleDescriptorList gemModules;
{
GemModuleVisitor moduleVisitor;
m_settingsRegistry->Visit(moduleVisitor, gemModuleKey);
m_settingsRegistry->Visit(moduleVisitor, gemModuleKey);
for (GemModuleLoadData& moduleLoadData : moduleVisitor.m_modulesLoadData)
{
// Add all auto loadable non-asset gems to the list of gem modules to load
if (moduleLoadData.m_autoLoad && !moduleLoadData.m_dynamicLibraryPath.empty())
if (!moduleLoadData.m_autoLoad)
{
gemModules.emplace_back(DynamicModuleDescriptor{ AZStd::move(moduleLoadData.m_dynamicLibraryPath) });
break;
}
for (AZ::OSString& dynamicLibraryPath : moduleLoadData.m_dynamicLibraryPaths)
{
auto CompareDynamicModuleDescriptor = [&dynamicLibraryPath](const DynamicModuleDescriptor& entry)
{
return entry.m_dynamicLibraryPath.contains(dynamicLibraryPath);
};
if (auto moduleIter = AZStd::find_if(gemModules.begin(), gemModules.end(), CompareDynamicModuleDescriptor);
moduleIter == gemModules.end())
{
gemModules.emplace_back(DynamicModuleDescriptor{ AZStd::move(dynamicLibraryPath) });
}
}
}
}
// The settings registry in the settings registry are prioritized to load before the modules in the ComponetnApplication descriptor
// The modules in the settings registry are prioritized to load before the modules in the ComponentApplication descriptor
// in the order in which they were found
for (auto&& moduleDescriptor : m_descriptor.m_modules)
{
// Append new dynamic library modules to the descriptor array
auto CompareDynamicModuleDescriptor = [&moduleDescriptor](const DynamicModuleDescriptor& entry)
{
return entry.m_dynamicLibraryPath.find(moduleDescriptor.m_dynamicLibraryPath) != AZStd::string_view::npos;
return entry.m_dynamicLibraryPath.contains(moduleDescriptor.m_dynamicLibraryPath);
};
if (auto foundModuleIter = AZStd::find_if(gemModules.begin(), gemModules.end(), CompareDynamicModuleDescriptor);
foundModuleIter == gemModules.end())
@ -1260,17 +1373,8 @@ namespace AZ
m_exeDirectory.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
}
void ComponentApplication::CalculateAppRoot(const char* appRootOverride)
void ComponentApplication::CalculateAppRoot()
{
if (appRootOverride)
{
m_appRoot = appRootOverride;
if (!m_appRoot.empty() && !m_appRoot.ends_with(AZ_CORRECT_FILESYSTEM_SEPARATOR))
{
m_appRoot.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
}
return;
}
if (AZStd::optional<AZ::StringFunc::Path::FixedString> appRootPath = Utils::GetDefaultAppRootPath(); appRootPath)
{
m_appRoot = AZStd::move(*appRootPath);
@ -1279,26 +1383,14 @@ namespace AZ
m_appRoot.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
}
}
else
{
m_appRoot = AZ::SettingsRegistryMergeUtils::GetAppRoot(m_settingsRegistry.get()).Native();
m_appRoot.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
}
}
//=========================================================================
// CheckEngineMarkerFile
//=========================================================================
bool ComponentApplication::CheckPathForEngineMarker(const char* fullPath) const
void ComponentApplication::CalculateEngineRoot()
{
static const char* engineMarkerFileName = "engine.json";
char engineMarkerFullPathToCheck[AZ_MAX_PATH_LEN] = "";
azstrcpy(engineMarkerFullPathToCheck, AZ_ARRAY_SIZE(engineMarkerFullPathToCheck), fullPath);
azstrcat(engineMarkerFullPathToCheck, AZ_ARRAY_SIZE(engineMarkerFullPathToCheck), "/");
azstrcat(engineMarkerFullPathToCheck, AZ_ARRAY_SIZE(engineMarkerFullPathToCheck), engineMarkerFileName);
return AZ::IO::SystemFile::Exists(engineMarkerFullPathToCheck);
if (m_engineRoot = AZ::SettingsRegistryMergeUtils::FindEngineRoot(*m_settingsRegistry).Native(); !m_engineRoot.empty())
{
m_engineRoot.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
}
}
void ComponentApplication::ResolveModulePath([[maybe_unused]] AZ::OSString& modulePath)

@ -9,15 +9,13 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#ifndef AZCORE_COMPONENT_APPLICATION_H
#define AZCORE_COMPONENT_APPLICATION_H
#pragma once
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Debug/ProfileModuleInit.h>
#include <AzCore/Debug/LocalFileEventLogger.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Module/DynamicModuleHandle.h>
@ -40,12 +38,15 @@ namespace AZ
class IConsole;
class Module;
class ModuleManager;
}
namespace AZ::Debug
{
class DrillerManager;
class LocalFileEventLogger;
}
namespace Debug
{
class DrillerManager;
}
namespace AZ
{
class ReflectionEnvironment
{
public:
@ -167,15 +168,6 @@ namespace AZ
//! \note Dynamic AZ::Modules are specified in the ComponentApplication::Descriptor.
CreateStaticModulesCallback m_createStaticModulesCallback = nullptr;
//! If set, this is used as the app root folder instead of it being calculated.
const char* m_appRootOverride = nullptr;
//! The path to root of the asset cache folder. For instance: ./cache/<project>/pc
const char* m_cacheRootPath = nullptr;
//! The path to the project in the asset cache folder. For instance: ./cache/<project>/pc/<project>
const char* m_cacheProjectPath = nullptr;
//! Specifies which system components to create & activate. If no tags specified, all system components are used. Specify as comma separated list.
const char* m_systemComponentTags = nullptr;
@ -226,6 +218,8 @@ namespace AZ
/// Returns the working root folder that has been registered with the app, if there is one.
/// It's expected that derived applications will implement an application root.
const char* GetAppRoot() const override { return m_appRoot.c_str(); }
/// Returns the path to the engine.
const char* GetEngineRoot() const override { return m_engineRoot.c_str(); }
/// Returns the path to the folder the executable is in.
const char* GetExecutableFolder() const override { return m_exeDirectory.c_str(); }
@ -331,6 +325,9 @@ namespace AZ
/// Create the drillers
void CreateDrillers();
/// Parse ComponentApplication specific command line arguments
void ParseCommandLine(const AZ::CommandLine& commandLine);
virtual void MergeSettingsToRegistry(SettingsRegistryInterface& registry);
//! Sets the specializations that will be used when loading the Settings Registry. Extend this in derived
@ -363,17 +360,11 @@ namespace AZ
/// Calculates the directory the application executable comes from.
void CalculateExecutablePath();
/// Calculates the directory where the bootstrap.cfg file resides.
void CalculateAppRoot(const char* appRootOverride = {});
/// Calculates the root directory of the engine.
void CalculateEngineRoot();
/**
* Check/verify a given path for the engine marker (file) so that we can identify that
* a given path is the engine root. This is only valid for target platforms that are built
* for the host platform and not deployable (ie windows, mac).
* @param fullPath The full path to look for the engine marker
* @return true if the input path contains the engine marker file, false if not
*/
virtual bool CheckPathForEngineMarker(const char* fullPath) const;
/// Calculates the directory where the bootstrap.cfg file resides.
void CalculateAppRoot();
template<typename Iterator>
static void NormalizePath(Iterator begin, Iterator end, bool doLowercase = true)
@ -398,10 +389,11 @@ namespace AZ
void* m_fixedMemoryBlock{ nullptr }; //!< Pointer to the memory block allocator, so we can free it OnDestroy.
IAllocatorAllocate* m_osAllocator{ nullptr };
EntitySetType m_entities;
AZ::StringFunc::Path::FixedString m_exeDirectory;
AZ::StringFunc::Path::FixedString m_appRoot;
AZ::IO::FixedMaxPathString m_exeDirectory;
AZ::IO::FixedMaxPathString m_engineRoot;
AZ::IO::FixedMaxPathString m_appRoot;
AZ::SettingsRegistryInterface::NotifyEventHandler m_gameProjectChangedHandler;
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectChangedHandler;
// ConsoleFunctorHandle is responsible for unregistering the Settings Registry Console
// from the m_console member when it goes out of scope
@ -427,9 +419,15 @@ namespace AZ
// Created early to allow events to be logged before anything else. These will be kept in memory until
// a file is associated with the logger. The internal buffer is limited to 64kb and once full unexpected
// behavior may happen. The LocalFileEventLogger will register itself automatically with AZ::Interface<IEventLogger>.
AZ::Debug::LocalFileEventLogger m_eventLogger;
struct EventLoggerDeleter
{
EventLoggerDeleter() noexcept;
EventLoggerDeleter(bool skipDelete) noexcept;
void operator()(AZ::Debug::LocalFileEventLogger* ptr);
bool m_skipDelete{};
};
using EventLoggerPtr = AZStd::unique_ptr<AZ::Debug::LocalFileEventLogger, EventLoggerDeleter>;
EventLoggerPtr m_eventLogger;
};
}
#endif // AZCORE_COMPONENT_APPLICATION_H
#pragma once

@ -183,6 +183,11 @@ namespace AZ
* @return A pointer to the name of the app's root folder, if a root folder was registered.
*/
virtual const char* GetAppRoot() const = 0;
/**
* Gets the path of the working engine folder that the app is a part of.
* @return A pointer to the engine path.
*/
virtual const char* GetEngineRoot() const = 0;
/**
* Gets the path to the directory that contains the application's executable.
* @return A pointer to the name of the path that contains the application's executable.

@ -161,9 +161,11 @@ namespace AZ
void Console::ExecuteCommandLine(const AZ::CommandLine& commandLine)
{
for (const auto& [switchKey, switchValues] : commandLine.GetSwitchList())
for (auto&& commandArgument : commandLine)
{
ConsoleCommandContainer commandArgs(switchValues.begin(), switchValues.end());
const auto& switchKey = commandArgument.m_option;
const auto& switchValue = commandArgument.m_value;
ConsoleCommandContainer commandArgs{ switchValue };
PerformCommand(switchKey, commandArgs, ConsoleSilentMode::NotSilent, ConsoleInvokedFrom::AzConsole, ConsoleFunctorFlags::Null, ConsoleFunctorFlags::Null);
}
}

@ -67,25 +67,7 @@ namespace AZ
return a != OpenMode::Invalid;
}
inline OpenMode operator | (OpenMode a, OpenMode b)
{
return static_cast<OpenMode>(static_cast<AZ::u32>(a) | static_cast<AZ::u32>(b));
}
inline OpenMode operator & (OpenMode a, OpenMode b)
{
return static_cast<OpenMode>(static_cast<AZ::u32>(a) & static_cast<AZ::u32>(b));
}
inline OpenMode& operator |= (OpenMode& a, OpenMode b)
{
return a = a | b;
}
inline OpenMode& operator &= (OpenMode& a, OpenMode b)
{
return a = a & b;
}
AZ_DEFINE_ENUM_BITWISE_OPERATORS(OpenMode)
OpenMode GetOpenModeFromStringMode(const char* mode);
@ -250,12 +232,14 @@ namespace AZ
virtual bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const = 0;
AZStd::optional<AZ::IO::FixedMaxPath> ConvertToAlias(const AZ::IO::PathView& path) const;
/// ResolvePath - Replaces any aliases in path with their values and stores the result in resolvedPath,
/// also ensures that the path is absolute
/// returns true if path was resolved, false otherwise
/// note that all of the above file-finding and opening functions automatically resolve the path before operating
/// so you should not need to call this except in very exceptional circumstances where you absolutely need to
/// hit a physical file and don't want to use SystemFile
//! ResolvePath - Replaces any aliases in path with their values and stores the result in resolvedPath,
//! also ensures that the path is absolute
//! NOTE: If the path does not start with an alias then the resolved value of the @assets@ is used
//! which has the effect of making the path relative to the @assets@/ folder
//! returns true if path was resolved, false otherwise
//! note that all of the above file-finding and opening functions automatically resolve the path before operating
//! so you should not need to call this except in very exceptional circumstances where you absolutely need to
//! hit a physical file and don't want to use SystemFile
virtual bool ResolvePath(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize) const = 0;
//! ResolvePath - Replaces any @ aliases in the supplied path with their the resolved alias values
@ -265,6 +249,11 @@ namespace AZ
virtual bool ResolvePath(AZ::IO::FixedMaxPath& resolvedPath, const AZ::IO::PathView& path) const = 0;
AZStd::optional<AZ::IO::FixedMaxPath> ResolvePath(const AZ::IO::PathView& path) const;
//! ReplaceAliases - If the path starts with an @...@ alias it is substituted with the alias value
//! otherwise the path is copied as is to the resolvedAlias path value
//! returns true if the resulting path can fit within AZ::IO::FixedMaxPath buffer
virtual bool ReplaceAlias(AZ::IO::FixedMaxPath& replacedAliasPath, const AZ::IO::PathView& path) const = 0;
/// Divulge the filename used to originally open that handle.
virtual bool GetFilename(HandleType fileHandle, char* filename, AZ::u64 filenameSize) const = 0;

@ -170,7 +170,7 @@ namespace AZ::IO
//! Check whether the path is not absolute
[[nodiscard]] constexpr bool IsRelative() const;
//! Check whether the path is relative to the base path
[[nodiscard]] constexpr bool IsRelativeTo(const PathView & base) const;
[[nodiscard]] constexpr bool IsRelativeTo(const PathView& base) const;
//! Normalizes a path in a purely lexical manner.
//! # Path separators are converted to their preferred path separator
@ -517,7 +517,7 @@ namespace AZ::IO
//! Checks if the path has a root directory
[[nodiscard]] constexpr bool HasRootDirectory() const;
//! Checks whether the entire root path portion of the path is empty
//! The root portion ofthe path is made up of root_name() / root_directory()
//! The root portion of the path is made up of root_name() / root_directory()
[[nodiscard]] constexpr bool HasRootPath() const;
//! checks whether the relative part of path is empty
//! (C:\\ lumberyard\dev\)
@ -539,8 +539,8 @@ namespace AZ::IO
[[nodiscard]] constexpr bool IsAbsolute() const;
//! Check whether the path is not absolute
[[nodiscard]] constexpr bool IsRelative() const;
//! Check whether the path is relative to the input path
[[nodiscard]] constexpr bool IsRelativeTo() const;
//! Check whether the path is relative to the base path
[[nodiscard]] constexpr bool IsRelativeTo(const PathView& base) const;
// decomposition
//! Given a windows path of "C:\lumberyard\foo\bar\name.txt" and a posix path of

@ -1035,7 +1035,7 @@ namespace AZ::IO
// move the parser from the end to a valid filename by decrementing
for(--pathParserEnd, --patternParserEnd; pathParserEnd && patternParserEnd; --pathParserEnd, --patternParserEnd)
{
if (!AZStd::wildcard_match(*patternParserEnd, *pathParserEnd))
if (!AZStd::wildcard_match_case(*patternParserEnd, *pathParserEnd))
{
return false;
}
@ -1830,9 +1830,9 @@ namespace AZ::IO
}
template <typename StringType>
[[nodiscard]] constexpr bool BasicPath<StringType>::IsRelativeTo() const
[[nodiscard]] constexpr bool BasicPath<StringType>::IsRelativeTo(const PathView& base) const
{
return static_cast<PathView>(*this).IsRelative();
return static_cast<PathView>(*this).IsRelativeTo(base);
}
template <typename StringType>

@ -369,6 +369,7 @@ namespace AZ
context.Serializer<JsonVector2Serializer>()->HandlesType<Vector2>();
context.Serializer<JsonVector3Serializer>()->HandlesType<Vector3>();
context.Serializer<JsonVector4Serializer>()->HandlesType<Vector4>();
context.Serializer<JsonQuaternionSerializer>()->HandlesType<Quaternion>();
}
void MathReflect(ReflectContext* context)

@ -14,6 +14,7 @@
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/MathVectorSerializer.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
@ -238,4 +239,56 @@ namespace AZ
{
return JsonMathVectorSerializerInternal::Store<Vector4, 4>(outputValue, inputValue, defaultValue, valueTypeId, context);
}
// Quaternion
AZ_CLASS_ALLOCATOR_IMPL(JsonQuaternionSerializer, SystemAllocator, 0);
JsonSerializationResult::Result JsonQuaternionSerializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
// check for "yaw, pitch, roll" object
if (inputValue.IsObject())
{
if (inputValue.GetObject().ObjectEmpty())
{
Quaternion* outQuaternion = reinterpret_cast<Quaternion*>(outputValue);
*outQuaternion = Quaternion::CreateIdentity();
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Using identity quaternion for empty object.");
}
AZ::BaseJsonSerializer* floatSerializer = context.GetRegistrationContext()->GetSerializerForType(azrtti_typeid<float>());
if (!floatSerializer)
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Catastrophic, "Failed to find the json float serializer.");
}
constexpr const char* names[3] = {"yaw", "pitch", "roll"};
float values[3];
int i = 0;
for (auto itr = inputValue.MemberBegin(); itr != inputValue.MemberEnd(); ++i, ++itr)
{
ScopedContextPath subPath(context, names[i]);
JSR::Result intermediate = floatSerializer->Load(values + i, azrtti_typeid<float>(), itr->value, context);
if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediate;
}
}
auto eulerAnglesDegrees = Vector3::CreateFromFloat3(values);
reinterpret_cast<Quaternion*>(outputValue)->SetFromEulerDegrees(eulerAnglesDegrees);
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Successfully read quaternion.");
}
return JsonMathVectorSerializerInternal::Load<Quaternion, 4>(outputValue, outputValueTypeId, inputValue, context);
}
JsonSerializationResult::Result JsonQuaternionSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
{
return JsonMathVectorSerializerInternal::Store<Quaternion, 4>(outputValue, inputValue, defaultValue, valueTypeId, context);
}
}

@ -51,4 +51,16 @@ namespace AZ
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
class JsonQuaternionSerializer
: public BaseJsonSerializer
{
public:
AZ_RTTI(JsonQuaternionSerializer, "{18604375-3606-49AC-B366-0F6DF9149FF3}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
}

@ -10,11 +10,11 @@
*
*/
#include <AzCore/Settings/CommandLine.h>
#include <AzCore/AzCore_Traits_Platform.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/Settings/CommandLine.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/AzCore_Traits_Platform.h>
#include <AzCore/std/tuple.h>
namespace AZ
{
@ -29,6 +29,21 @@ namespace AZ
return lowerStr;
}
AZStd::string_view UnquoteArgument(AZStd::string_view arg)
{
if (arg.size() < 2)
{
return arg;
}
return arg.front() == '"' && arg.back() == '"' ? AZStd::string_view{ arg.begin() + 1, arg.end() - 1 } : arg;
}
AZStd::string QuoteArgument(AZStd::string_view arg)
{
return !arg.empty() ? AZStd::string::format(R"("%.*s")", aznumeric_cast<int>(arg.size()), arg.data()) : AZStd::string{ arg };
}
}
CommandLine::CommandLine()
@ -41,15 +56,62 @@ namespace AZ
{
}
void CommandLine::AddArgument(AZStd::string currentArg, AZStd::string& currentSwitch)
void CommandLine::ParseOptionArgument(AZStd::string_view newOption, AZStd::string_view newValue,
CommandArgument* inProgressArgument)
{
// Allow argument values wrapped in quotes at both ends to become the value within the quotes
AZStd::string_view unquotedValue = UnquoteArgument(newValue);
if (unquotedValue != newValue)
{
// Update the inProgressArgument before adding new arguments
if(inProgressArgument)
{
inProgressArgument->m_value = unquotedValue;
// Set the inProgressArgument to nullptr to indicate that the inProgressArgument has been fulfilled
inProgressArgument = nullptr;
}
else
{
m_allValues.push_back({ newOption, unquotedValue });
}
}
else
{
AZStd::vector<AZStd::string_view> tokens;
auto splitArgument = [&tokens](AZStd::string_view token)
{
tokens.emplace_back(AZ::StringFunc::StripEnds(token));
};
AZ::StringFunc::TokenizeVisitor(newValue, splitArgument, ",;");
// we do it this way because you are allowed to do odd things like
// -root=abc -root=hij,klm -root whee -root fun;days
// and roots value should be { abc, hij, klm, whee, fun, days }
for (AZStd::string_view optionValue : tokens)
{
if (inProgressArgument)
{
inProgressArgument->m_value = optionValue;
// Set the inProgressArgument to nullptr to indicate that the inProgressArgument has been fulfilled
inProgressArgument = nullptr;
}
else
{
m_allValues.push_back({ newOption, optionValue });
}
}
}
}
void CommandLine::AddArgument(AZStd::string_view currentArg, AZStd::string& currentSwitch)
{
StringFunc::Strip(currentArg, " ", false, true, true);
currentArg = AZ::StringFunc::StripEnds(currentArg);
if (!currentArg.empty())
{
if (AZStd::string_view(currentArg.begin(), currentArg.begin() + 1).find_first_of(m_commandLineOptionPrefix) != AZStd::string_view::npos)
if (m_commandLineOptionPrefix.contains(currentArg.front()))
{
// its possible that its a key-value-pair like /blah=whatever
// we support this too, for compatibilty.
// its possible that its a key-value-pair like -blah=whatever
// we support this too, for compatibility.
currentArg = currentArg.substr(1);
if (currentArg[0] == '-') // for -- extra
@ -57,93 +119,41 @@ namespace AZ
currentArg = currentArg.substr(1);
}
AZStd::size_t foundPos = StringFunc::Find(currentArg.c_str(), '=');
AZStd::size_t foundPos = AZ::StringFunc::Find(currentArg, "=");
if (foundPos != AZStd::string::npos)
{
// Allow argument values wrapped in quotes at both ends to become the value within the quotes
if (currentArg.length() > (foundPos + 2) && currentArg[foundPos + 1] == '"' && currentArg[currentArg.length() - 1] == '"')
{
AZStd::string argName = currentArg.substr(0, foundPos);
argName = ToLower(argName);
StringFunc::Strip(argName, " ", false, true, true);
m_switches[argName].emplace_back(currentArg.substr(foundPos + 2, currentArg.length() - (foundPos + 2) - 1));
currentSwitch.clear();
return;
}
// its in '=' format
AZStd::vector<AZStd::string> tokens;
StringFunc::Tokenize(currentArg.substr(foundPos + 1).c_str(), tokens, ",;");
currentArg.resize(foundPos);
currentArg = ToLower(currentArg);
StringFunc::Strip(currentArg, " ", false, true, true);
m_switches.insert_key(currentArg); // returns pair<iter, bool>
// we do it this way because you are allowed to do odd things like
// /root=abc /root=hij,klm /root whee /root fun;days
// and roots value should be { abc, hij, klm, whee, fun, days }
for (AZStd::string& switchValue : tokens)
{
StringFunc::Strip(switchValue, " ", false, true, true);
m_switches[currentArg].push_back(switchValue);
}
AZStd::string_view argumentView{ currentArg };
AZStd::string_view option = AZ::StringFunc::StripEnds(argumentView.substr(0, foundPos));
AZStd::string_view value = AZ::StringFunc::StripEnds(argumentView.substr(foundPos + 1));
ParseOptionArgument(ToLower(option), value, nullptr);
currentSwitch.clear();
}
else
{
// its in this format /switchName switchvalue
// its in this format -switchName switchvalue
// (no equals)
currentSwitch = currentArg;
currentSwitch = ToLower(currentSwitch);
m_switches.insert_key(currentSwitch);
currentSwitch = ToLower(currentArg);
m_allValues.push_back({ currentSwitch, "" });
}
}
else
{
if (currentSwitch.empty())
{
m_miscValues.push_back(currentArg);
m_allValues.push_back({ "", UnquoteArgument(currentArg) });
}
else
{
// Allow argument values wrapped in quotes at both ends to become the value within the quotes
if (currentArg[0] == '"' && currentArg.length() >= 2 && currentArg[currentArg.length() - 1] == '"')
{
m_switches[currentSwitch].emplace_back(currentArg.substr(1, currentArg.length() - 2));
currentSwitch.clear();
return;
}
AZStd::vector<AZStd::string> tokens;
StringFunc::Tokenize(currentArg.c_str(), tokens, ",;");
for (AZStd::string& switchValue : tokens)
{
StringFunc::Strip(switchValue, " ", false, true, true);
m_switches[currentSwitch].push_back(switchValue);
}
ParseOptionArgument(currentSwitch, currentArg, &m_allValues.back());
currentSwitch.clear();
}
currentSwitch.clear();
}
}
}
void CommandLine::Parse(const ParamContainer& commandLine)
{
m_switches.clear();
m_miscValues.clear();
AZStd::string currentSwitch;
for (int i = 1; i < commandLine.size(); ++i)
{
AddArgument(commandLine[i], currentSwitch);
}
}
void CommandLine::Parse(int argc, char** argv)
{
m_switches.clear();
m_miscValues.clear();
m_allValues.clear();
AZStd::string currentSwitch;
// Start on 1 because 0 is the executable name
@ -157,91 +167,145 @@ namespace AZ
}
}
void CommandLine::Dump(ParamContainer& commandLineDumpOutput)
void CommandLine::Parse(const ParamContainer& commandLine)
{
// Push back an empty argument as the Parse function always skips parsing the first argument
commandLineDumpOutput.emplace_back(AZStd::string());
for (const AZStd::string& miscValue : m_miscValues)
m_allValues.clear();
// This version of Parse does not skip over 0th index
AZStd::string currentSwitch;
for (int i = 0; i < commandLine.size(); ++i)
{
commandLineDumpOutput.push_back(miscValue);
AddArgument(commandLine[i], currentSwitch);
}
}
void CommandLine::Dump(ParamContainer& commandLineDumpOutput) const
{
AZ_Error("CommandLine", !m_commandLineOptionPrefix.empty(),
"Cannot dump command line switches from a command line with an empty option prefix");
if (!m_commandLineOptionPrefix.empty())
for (const CommandArgument& argument : m_allValues)
{
for (const auto& [switchKey, switchValues] : m_switches)
if (!argument.m_option.empty())
{
AZStd::string prefixSwitchKey = m_commandLineOptionPrefix.front() + switchKey;
if (switchValues.empty())
{
// There are no value for the switch so just push it back of the command line dump output
commandLineDumpOutput.emplace_back(prefixSwitchKey);
}
else
{
for (const auto& switchValue : switchValues)
{
commandLineDumpOutput.emplace_back(prefixSwitchKey);
commandLineDumpOutput.emplace_back(switchValue);
}
}
commandLineDumpOutput.emplace_back(m_commandLineOptionPrefix.front() + argument.m_option);
}
if (!argument.m_value.empty())
{
commandLineDumpOutput.emplace_back(QuoteArgument(argument.m_value));
}
}
else
{
AZ_Error("CommandLine", false, "Cannot dump command line switches from a command line with an empty option prefix");
}
}
bool CommandLine::HasSwitch(AZStd::string_view switchName) const
{
return m_switches.find(ToLower(switchName)) != m_switches.end();
auto commandArgumentIter = AZStd::find_if(m_allValues.begin(), m_allValues.end(),
[optionName = ToLower(switchName)](const CommandArgument& argument) { return argument.m_option == optionName; });
return commandArgumentIter != m_allValues.end();
}
AZStd::size_t CommandLine::GetNumSwitchValues(AZStd::string_view switchName) const
{
ParamMap::const_iterator switchFound = m_switches.find(ToLower(switchName));
if (switchFound == m_switches.end())
{
return 0;
}
return switchFound->second.size();
return AZStd::count_if(m_allValues.begin(), m_allValues.end(),
[optionName = ToLower(switchName)](const CommandArgument& argument) { return argument.m_option == optionName; });
}
const AZStd::string& CommandLine::GetSwitchValue(AZStd::string_view switchName, AZStd::size_t index) const
{
ParamMap::const_iterator switchFound = m_switches.find(ToLower(switchName));
if (switchFound == m_switches.end())
AZStd::string optionName = ToLower(switchName);
size_t currentPosIndex{};
auto findArgumentAt = [&optionName, index, &currentPosIndex](const CommandArgument& argument)
{
return m_emptyValue;
}
if (argument.m_option == optionName)
{
return currentPosIndex++ == index;
}
AZ_Assert(index < switchFound->second.size(), "Invalid Command line switch lookup");
if (index >= switchFound->second.size())
return false;
};
auto commandArgumentIter = AZStd::find_if(m_allValues.begin(), m_allValues.end(), findArgumentAt);
if (!optionName.empty())
{
AZ_Assert(index < currentPosIndex, R"(Invalid Command line optional argument lookup of "%s" at index %zu)",
optionName.c_str(), index);
}
else
{
AZ_Assert(index < currentPosIndex, R"(Invalid Command line positional argument lookup at index %zu)", index);
}
if (commandArgumentIter == m_allValues.end())
{
return m_emptyValue;
}
return switchFound->second.at(index);
return commandArgumentIter->m_value;
}
AZStd::size_t CommandLine::GetNumMiscValues() const
{
return m_miscValues.size();
return GetNumSwitchValues("");
}
const AZStd::string& CommandLine::GetMiscValue(AZStd::size_t index) const
{
AZ_Assert(index < m_miscValues.size(), "Invalid Command line lookup");
if (index >= m_miscValues.size())
{
return m_emptyValue;
}
return m_miscValues[index];
// Positional arguments option value is an empty string
return GetSwitchValue("", index);
}
const CommandLine::ParamMap& CommandLine::GetSwitchList() const
[[nodiscard]] bool CommandLine::empty() const
{
return m_allValues.empty();
}
auto CommandLine::size() const -> ArgumentVector::size_type
{
return m_allValues.size();
}
auto CommandLine::begin() -> ArgumentVector::iterator
{
return m_allValues.begin();
}
auto CommandLine::begin() const -> ArgumentVector::const_iterator
{
return m_allValues.begin();
}
auto CommandLine::cbegin() const -> ArgumentVector::const_iterator
{
return m_allValues.cbegin();
}
auto CommandLine::end() -> ArgumentVector::iterator
{
return m_allValues.end();
}
auto CommandLine::end() const -> ArgumentVector::const_iterator
{
return m_allValues.end();
}
auto CommandLine::cend() const -> ArgumentVector::const_iterator
{
return m_allValues.cend();
}
auto CommandLine::rbegin() -> ArgumentVector::reverse_iterator
{
return m_allValues.rbegin();
}
auto CommandLine::rbegin() const -> ArgumentVector::const_reverse_iterator
{
return m_allValues.rbegin();
}
auto CommandLine::crbegin() const -> ArgumentVector::const_reverse_iterator
{
return m_allValues.crbegin();
}
auto CommandLine::rend() -> ArgumentVector::reverse_iterator
{
return m_allValues.rend();
}
auto CommandLine::rend() const -> ArgumentVector::const_reverse_iterator
{
return m_allValues.rend();
}
auto CommandLine::crend() const -> ArgumentVector::const_reverse_iterator
{
return m_switches;
return m_allValues.crend();
}
}

@ -36,36 +36,47 @@ namespace AZ
public:
AZ_CLASS_ALLOCATOR(CommandLine, AZ::SystemAllocator, 0);
using ParamContainer = AZStd::vector<AZStd::string>;
struct CommandArgument
{
AZStd::string m_option;
AZStd::string m_value;
};
using ArgumentVector = AZStd::vector<CommandArgument>;
CommandLine();
/**
* Initializes a CommandLine instance which uses the provided commandLineOptionPreix for parsing switches
*/
CommandLine(AZStd::string_view commandLineOptionPrefix);
using ParamContainer = AZStd::vector<AZStd::string>;
using ParamMap = AZStd::unordered_map<AZStd::string, ParamContainer>;
/**
* Construct a command line parser.
* It will load parameters from the given ARGC/ARGV parameters instead of process command line.
* Skips over the first parameter as it assumes it is the executable name
*/
void Parse(int argc, char** argv);
/**
* Parses each element of the command line as parameter.
* Unlike the ARGC/ARGV version above, this function doesn't skip over the first parameter
* It allows for round trip conversion with the Dump() method
*/
void Parse(const ParamContainer& commandLine);
/**
* Will dump command line parameters from the CommandLine in a format such that switches
* are prefixed with the option prefix followed by their value(s) which are comma separated
* The result of this function can be supplied to Parse() to re-create an eqiuvlanet command line obect
* The result of this function can be supplied to Parse() to re-create an equivalent command line object
* Ex, If the command line has the current list of parsed miscellaneous values and switches of
* MiscValue = ["Foo", "Bat"]
* Switches = ["GameFolder" : [], "RemoteIp" : ["10.0.0.1"], "ScanFolders" : ["\a\b\c", "\d\e\f"]
* CommandLineOptionPrefix = "-/"
*
* Then the resulting dumped value would be
* Dump = ["", "Foo", "Bat", "-GameFolder", "-RemoteIp", "10.0.0.1", "-ScanFolders", "\a\b\c", "-ScanFolders", "\d\e\f"]
* NOTE: The first parameter is always empty string as the Parse function skips over it
* Dump = ["Foo", "Bat", "-GameFolder", "-RemoteIp", "10.0.0.1", "-ScanFolders", "\a\b\c", "-ScanFolders", "\d\e\f"]
*/
void Dump(ParamContainer& commandLineDumpOutput);
void Dump(ParamContainer& commandLineDumpOutput) const;
/**
* Determines whether a switch is present in the command line
@ -97,16 +108,28 @@ namespace AZ
*/
const AZStd::string& GetMiscValue(AZStd::size_t index) const;
/*
* Return the list of parsed switches
*/
const ParamMap& GetSwitchList() const;
// Range accessors
[[nodiscard]] bool empty() const;
auto size() const -> ArgumentVector::size_type;
auto begin() -> ArgumentVector::iterator;
auto begin() const -> ArgumentVector::const_iterator;
auto cbegin() const -> ArgumentVector::const_iterator;
auto end() -> ArgumentVector::iterator;
auto end() const -> ArgumentVector::const_iterator;
auto cend() const -> ArgumentVector::const_iterator;
auto rbegin() -> ArgumentVector::reverse_iterator;
auto rbegin() const -> ArgumentVector::const_reverse_iterator;
auto crbegin() const -> ArgumentVector::const_reverse_iterator;
auto rend() -> ArgumentVector::reverse_iterator;
auto rend() const -> ArgumentVector::const_reverse_iterator;
auto crend() const -> ArgumentVector::const_reverse_iterator;
private:
void AddArgument(AZStd::string currentArg, AZStd::string& currentSwitch);
void AddArgument(AZStd::string_view currentArg, AZStd::string& currentSwitch);
void ParseOptionArgument(AZStd::string_view newOption, AZStd::string_view newValue, CommandArgument* inProgressArgument);
ParamMap m_switches;
ParamContainer m_miscValues;
ArgumentVector m_allValues;
AZStd::string m_emptyValue;
inline static constexpr size_t MaxCommandOptionPrefixes = 8;

@ -173,7 +173,15 @@ namespace AZ
path.remove_prefix(1); // Remove the leading slash as the StackedString will add this back in.
jsonPath.Push(path);
}
Visit(visitor, jsonPath, "", *value);
// Extract the last token of the JSON pointer to use as the valueName
AZStd::string_view valueName;
size_t pointerTokenCount = pointer.GetTokenCount();
if (pointerTokenCount > 0)
{
const rapidjson::Pointer::Token& lastToken = pointer.GetTokens()[pointerTokenCount - 1];
valueName = AZStd::string_view(lastToken.name, lastToken.length);
}
Visit(visitor, jsonPath, valueName, *value);
return true;
}
}

@ -41,68 +41,197 @@ namespace AZ::Internal
return value;
}
}
namespace AZ::SettingsRegistryMergeUtils
{
AZ::IO::FixedMaxPath GetAppRoot(SettingsRegistryInterface* settingsRegistry)
AZ::SettingsRegistryInterface::FixedValueString GetEngineMonikerForProject(
SettingsRegistryInterface& settingsRegistry, const AZ::IO::FixedMaxPath& projectPath)
{
constexpr size_t MaxAppRootPathsToScan = 16;
AZStd::fixed_vector<AZStd::string_view, MaxAppRootPathsToScan> appRootScanPaths;
// Check the commandLine for --app-root parameter
// If it exist use that instead
auto appRootOverrideKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/app-root", AZ::SettingsRegistryMergeUtils::CommandLineSwitchRootKey);
if (settingsRegistry)
// projectPath needs to be an absolute path here.
using namespace AZ::SettingsRegistryMergeUtils;
bool projectJsonMerged = false;
auto projectJsonPath = projectPath / "project.json";
if (AZ::IO::SystemFile::Exists(projectJsonPath.c_str()))
{
AZ::IO::FixedMaxPath appRootOverridePath;
if (settingsRegistry->Get(appRootOverridePath.Native(), appRootOverrideKey))
{
return appRootOverridePath;
}
projectJsonMerged = settingsRegistry.MergeSettingsFile(
projectJsonPath.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ProjectSettingsRootKey);
}
AZStd::optional<AZStd::fixed_string<AZ::IO::MaxPathLength>> appRootPath = Utils::GetDefaultAppRootPath();
if (appRootPath.has_value())
AZ::SettingsRegistryInterface::FixedValueString engineMoniker;
if (projectJsonMerged)
{
appRootScanPaths.push_back(*appRootPath);
// In project.json look for the "engine" key.
auto engineMonikerKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/engine", ProjectSettingsRootKey);
settingsRegistry.Get(engineMoniker, engineMonikerKey);
}
AZStd::fixed_string<AZ::IO::MaxPathLength> executableDir;
AZ::Utils::ExecutablePathResult pathResult = Utils::GetExecutableDirectory(executableDir.data(), executableDir.capacity());
if (pathResult == Utils::ExecutablePathResult::Success)
return engineMoniker;
}
AZ::IO::FixedMaxPath ReconcileEngineRootFromProjectPath(SettingsRegistryInterface& settingsRegistry, const AZ::IO::FixedMaxPath& projectPath)
{
// Find the engine root via the engine manifest file and project.json
// Locate the engine manifest file and merge it to settings registry.
// Visit over the engine paths list and merge the engine.json files to settings registry.
// Merge project.json to settings registry. That will give us an "engine" key.
// When we find a match for "engine_name" value against the "engine" value from before, we can stop and use that engine root.
// Finally set the BootstrapSettingsRootKey/engine_path setting so that subsequent calls to GetEngineRoot will use that
// and avoid all this logic.
using namespace AZ::SettingsRegistryMergeUtils;
AZ::IO::FixedMaxPath engineRoot;
if (auto engineManifestPath = AZ::Utils::GetEngineManifestPath(); !engineManifestPath.empty())
{
// Update the size value of the executable directory fixed string to correctly be the length of the null-terminated string stored within it
executableDir.resize_no_construct(AZStd::char_traits<char>::length(executableDir.data()));
if (auto foundIt = AZStd::find(appRootScanPaths.begin(), appRootScanPaths.end(), executableDir);
foundIt == appRootScanPaths.end())
bool manifestLoaded{false};
if (AZ::IO::SystemFile::Exists(engineManifestPath.c_str()))
{
appRootScanPaths.push_back(executableDir);
manifestLoaded = settingsRegistry.MergeSettingsFile(
engineManifestPath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, EngineManifestRootKey);
}
}
for (AZStd::string_view appRootScanPath : appRootScanPaths)
{
AZ::IO::FixedMaxPath appRootCandidate{ appRootScanPath };
// Search for the application root
bool rootPathVisited = false;
do
struct EngineInfo
{
AZ::IO::FixedMaxPath m_path;
AZ::SettingsRegistryInterface::FixedValueString m_moniker;
};
struct EnginePathsVisitor : public AZ::SettingsRegistryInterface::Visitor
{
if (AZ::IO::SystemFile::Exists((appRootCandidate / "bootstrap.cfg").c_str()))
void Visit(
[[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName,
[[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override
{
return appRootCandidate;
m_enginePaths.emplace_back(EngineInfo{AZ::IO::FixedMaxPath{value}.LexicallyNormal(), {}});
}
// Validate that the parent directory isn't itself, that would imply
// that it is the root path
AZ::IO::PathView parentPath = appRootCandidate.ParentPath();
rootPathVisited = appRootCandidate == parentPath;
// Recurse upwards one directory
appRootCandidate = AZStd::move(parentPath);
AZStd::vector<EngineInfo> m_enginePaths{};
};
EnginePathsVisitor pathVisitor;
if (manifestLoaded)
{
auto enginePathsKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/engines", EngineManifestRootKey);
settingsRegistry.Visit(pathVisitor, enginePathsKey);
}
const auto engineMonikerKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/engine_name", EngineSettingsRootKey);
for (EngineInfo& engineInfo : pathVisitor.m_enginePaths)
{
AZ::IO::FixedMaxPath engineSettingsPath{engineInfo.m_path};
engineSettingsPath /= "engine.json";
if (AZ::IO::SystemFile::Exists(engineSettingsPath.c_str()))
{
if (settingsRegistry.MergeSettingsFile(
engineSettingsPath.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, EngineSettingsRootKey))
{
settingsRegistry.Get(engineInfo.m_moniker, engineMonikerKey);
}
}
auto engineMoniker = Internal::GetEngineMonikerForProject(settingsRegistry, engineInfo.m_path / projectPath);
if (!engineMoniker.empty() && engineMoniker == engineInfo.m_moniker)
{
engineRoot = engineInfo.m_path;
break;
}
}
}
return engineRoot;
}
AZ::IO::FixedMaxPath ScanUpRootLocator(AZStd::string_view rootFileToLocate)
{
AZStd::fixed_string<AZ::IO::MaxPathLength> executableDir;
if (Utils::GetExecutableDirectory(executableDir.data(), executableDir.capacity()) == Utils::ExecutablePathResult::Success)
{
// Update the size value of the executable directory fixed string to correctly be the length of the null-terminated string
// stored within it
executableDir.resize_no_construct(AZStd::char_traits<char>::length(executableDir.data()));
}
AZ::IO::FixedMaxPath engineRootCandidate{ executableDir };
bool rootPathVisited = false;
do
{
if (AZ::IO::SystemFile::Exists((engineRootCandidate / rootFileToLocate).c_str()))
{
return engineRootCandidate;
}
} while (!rootPathVisited);
// Note for posix filesystems the parent directory of '/' is '/' and for windows
// the parent directory of 'C:\\' is 'C:\\'
// Validate that the parent directory isn't itself, that would imply
// that it is the filesystem root path
AZ::IO::PathView parentPath = engineRootCandidate.ParentPath();
rootPathVisited = (engineRootCandidate == parentPath);
// Recurse upwards one directory
engineRootCandidate = AZStd::move(parentPath);
} while (!rootPathVisited);
return {};
}
} // namespace AZ::Internal
namespace AZ::SettingsRegistryMergeUtils
{
AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry)
{
AZ::IO::FixedMaxPath engineRoot;
// This is the 'external' engine root key, as in passed from command-line or .setreg files.
auto engineRootKey = SettingsRegistryInterface::FixedValueString::format("%s/engine_path", BootstrapSettingsRootKey);
if (settingsRegistry.Get(engineRoot.Native(), engineRootKey); !engineRoot.empty())
{
return engineRoot;
}
// We can scan up from exe directory to find engine.json, use that for engine root if it exists.
if (engineRoot = Internal::ScanUpRootLocator("engine.json"); !engineRoot.empty())
{
settingsRegistry.Set(engineRootKey, engineRoot.c_str());
return engineRoot;
}
AZ::IO::FixedMaxPath projectRoot = FindProjectRoot(settingsRegistry);
if (projectRoot.empty())
{
return {};
}
// Use the project.json and engine manifest to locate the engine root.
if (engineRoot = Internal::ReconcileEngineRootFromProjectPath(settingsRegistry, projectRoot); !engineRoot.empty())
{
settingsRegistry.Set(engineRootKey, engineRoot.c_str());
return engineRoot;
}
return {};
}
AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry)
{
AZ::IO::FixedMaxPath projectRoot;
// This is the 'external' project root key, as in passed from command-line or .setreg files.
auto projectRootKey = SettingsRegistryInterface::FixedValueString::format("%s/project_path", BootstrapSettingsRootKey);
if (settingsRegistry.Get(projectRoot.Native(), projectRootKey))
{
return projectRoot;
}
if (projectRoot = Internal::ScanUpRootLocator("project.json"); !projectRoot.empty())
{
settingsRegistry.Set(projectRootKey, projectRoot.c_str());
return projectRoot;
}
return {};
}
@ -111,9 +240,9 @@ namespace AZ::SettingsRegistryMergeUtils
constexpr AZStd::string_view commentPrefixes = ";#";
for (char commentPrefix : commentPrefixes)
{
if (line.starts_with(commentPrefix))
if (size_t commentOffset = line.find(commentPrefix); commentOffset != AZStd::string_view::npos)
{
return {};
return line.substr(0, commentOffset);
}
}
@ -123,7 +252,7 @@ namespace AZ::SettingsRegistryMergeUtils
AZStd::string_view ConfigParserSettings::DefaultSectionHeaderFilter(AZStd::string_view line)
{
AZStd::string_view sectionName;
constexpr char sectionHeaderStart= '[';
constexpr char sectionHeaderStart = '[';
constexpr char sectionHeaderEnd = ']';
if (line.starts_with(sectionHeaderStart) && line.ends_with(sectionHeaderEnd))
{
@ -184,7 +313,7 @@ namespace AZ::SettingsRegistryMergeUtils
void QuerySpecializationsFromRegistry(SettingsRegistryInterface& registry, SettingsRegistryInterface::Specializations& specializations)
{
// Append any specializations stored in the registry
// Append any specializations stored in the registry
struct SpecializationsVisitor
: AZ::SettingsRegistryInterface::Visitor
{
@ -222,7 +351,7 @@ namespace AZ::SettingsRegistryMergeUtils
bool MergeSettingsToRegistry_ConfigFile(SettingsRegistryInterface& registry, AZStd::string_view filePath,
const ConfigParserSettings& configParserSettings)
{
auto configPath = GetAppRoot(&registry) / filePath;
auto configPath = FindEngineRoot(registry) / filePath;
IO::SystemFile configFile;
if (!configFile.Open(configPath.c_str(), IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
{
@ -348,9 +477,13 @@ namespace AZ::SettingsRegistryMergeUtils
ConfigParserSettings parserSettings;
parserSettings.m_commentPrefixFunc = [](AZStd::string_view line) -> AZStd::string_view
{
if (line.starts_with("--") || line.starts_with(';') || line.starts_with('#'))
constexpr AZStd::string_view commentPrefixes[]{ "--", ";","#" };
for (AZStd::string_view commentPrefix : commentPrefixes)
{
return {};
if (size_t commentOffset = line.find(commentPrefix); commentOffset != AZStd::string_view::npos)
{
return line.substr(0, commentOffset);
}
}
return line;
};
@ -365,71 +498,104 @@ namespace AZ::SettingsRegistryMergeUtils
registry.Set(FilePathKey_BinaryFolder, path.LexicallyNormal().Native());
// Engine root folder - corresponds to the @engroot@ and @devroot@ aliases
AZ::IO::FixedMaxPath appRoot = GetAppRoot(&registry);
registry.Set(FilePathKey_EngineRootFolder, appRoot.LexicallyNormal().Native());
AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry);
registry.Set(FilePathKey_EngineRootFolder, engineRoot.LexicallyNormal().Native());
constexpr size_t bufferSize = 64;
auto buffer = AZStd::fixed_string<bufferSize>::format("%s/sys_game_folder", BootstrapSettingsRootKey);
auto buffer = AZStd::fixed_string<bufferSize>::format("%s/project_path", BootstrapSettingsRootKey);
AZStd::string_view projectPath(buffer);
AZ::SettingsRegistryInterface::FixedValueString projectPathKey(buffer);
SettingsRegistryInterface::FixedValueString projectPathValue;
if (registry.Get(projectPathValue, projectPath))
if (registry.Get(projectPathValue, projectPathKey))
{
// Cache folder
// Get the name of the asset platform assigned by the bootstrap. First check for platform version such as "windows_assets"
// and if that's missing just get "asset".
// and if that's missing just get "assets".
constexpr char platformName[] = AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER;
SettingsRegistryInterface::FixedValueString assetPlatform;
buffer = AZStd::fixed_string<bufferSize>::format("%s/%s_assets", BootstrapSettingsRootKey, platformName);
AZStd::string_view assetPlatformatPath(buffer);
if (!registry.Get(assetPlatform, assetPlatformatPath))
AZStd::string_view assetPlatformKey(buffer);
if (!registry.Get(assetPlatform, assetPlatformKey))
{
buffer = AZStd::fixed_string<bufferSize>::format("%s/assets", BootstrapSettingsRootKey);
assetPlatformatPath = AZStd::string_view(buffer);
if (!registry.Get(assetPlatform, assetPlatformatPath))
{
return;
}
assetPlatformKey = AZStd::string_view(buffer);
registry.Get(assetPlatform, assetPlatformKey);
}
// Source game folder - corresponds to the @devassets@ alias
path = appRoot / projectPathValue;
// Project path - corresponds to the @devassets@ alias
// NOTE: Here we append to engineRoot, but if projectPathValue is absolute then engineRoot is discarded.
path = engineRoot / projectPathValue;
AZ_Warning("SettingsRegistryMergeUtils", AZ::IO::SystemFile::Exists(path.c_str()),
R"(Project Path "%s" does not exist. Is the "sys_game_folder" entry set to valid project folder?)"
, path.c_str());
registry.Set(FilePathKey_SourceGameFolder, path.LexicallyNormal().Native());
// Source game name - The filename of the project directory and the name of the project
registry.Set(FilePathKey_SourceGameName, path.Filename().Native());
// Cache root folder - corresponds to the @root@ alias
#if AZ_TRAIT_USE_ASSET_CACHE_FOLDER
path = appRoot / "Cache" / projectPathValue / assetPlatform;
#else
// Use the Engine Root/App Root as the Asset root directory in this case
path = appRoot;
#endif
registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native());
// check for a default write storage path, fall back to the cache root if not
R"(Project path "%s" does not exist. Is the "%.*s" registry setting set to valid absolute path?)"
, path.c_str(), aznumeric_cast<int>(projectPathKey.size()), projectPathKey.data());
AZ::IO::FixedMaxPath normalizedProjectPath = path.LexicallyNormal();
registry.Set(FilePathKey_ProjectPath, normalizedProjectPath.Native());
// Add an alias to the project "user" directory
AZ::IO::FixedMaxPath projectUserPath = (normalizedProjectPath / "user").LexicallyNormal();
registry.Set(FilePathKey_ProjectUserPath, projectUserPath.Native());
// check for a default write storage path, fall back to the project's user/ directory if not
AZStd::optional<AZ::IO::FixedMaxPathString> devWriteStorage = Utils::GetDevWriteStoragePath();
registry.Set(FilePathKey_DevWriteStorage,
devWriteStorage.has_value() ?
devWriteStorage.value() :
path.LexicallyNormal().Native());
// Cache game folder - corresponds to the @assets@ alias
AZStd::to_lower(projectPathValue.begin(), projectPathValue.end());
path /= projectPathValue;
registry.Set(FilePathKey_CacheGameFolder, path.LexicallyNormal().Native());
registry.Set(FilePathKey_DevWriteStorage, devWriteStorage.has_value()
? devWriteStorage.value()
: projectUserPath.Native());
// Project name - if it was set via merging project.json use that value, otherwise use the project path's folder name.
auto projectNameKey =
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey)
+ "/project_name";
AZ::SettingsRegistryInterface::FixedValueString projectName;
if (!registry.Get(projectName, projectNameKey))
{
projectName = path.Filename().Native();
registry.Set(projectNameKey, projectName);
}
// Cache folders - sets up various paths in registry for the cache.
// Make sure the asset platform is set before setting these cache paths.
if (!assetPlatform.empty())
{
// Cache: project root - no corresponding fileIO alias, but this is where the asset database lives.
// A registry override is accepted using the "project_cache_path" key.
buffer = AZStd::fixed_string<bufferSize>::format("%s/project_cache_path", BootstrapSettingsRootKey);
AZStd::string_view projectCacheRootOverrideKey(buffer);
// Clear path to make sure that the `project_cache_path` value isn't concatenated to the project path
path.clear();
if (registry.Get(path.Native(), projectCacheRootOverrideKey))
{
registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native());
path /= assetPlatform;
registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native());
}
else
{
// Cache: root - same as the @root@ alias, this is the starting path for cache files.
path = normalizedProjectPath / "Cache";
registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native());
path /= assetPlatform;
registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native());
}
}
}
else
{
AZ_TracePrintf("SettingsRegistryMergeUtils",
R"(Current Project game folder "%.*s" isn't set in the Settings Registry. Project-related filepaths will not be set)" "\n",
aznumeric_cast<int>(projectPath.size()), projectPath.data());
R"(Project path isn't set in the Settings Registry at "%.*s". Project-related filepaths will not be set)" "\n",
aznumeric_cast<int>(projectPathKey.size()), projectPathKey.data());
}
#if !AZ_TRAIT_USE_ASSET_CACHE_FOLDER
// Setup the cache and user paths for Platforms where the Asset Cache Folder isn't used
path = engineRoot;
registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native());
registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native());
registry.Set(FilePathKey_DevWriteStorage, path.LexicallyNormal().Native());
registry.Set(FilePathKey_ProjectUserPath, (path / "user").LexicallyNormal().Native());
#endif // AZ_TRAIT_USE_ASSET_CACHE_FOLDER
}
void MergeSettingsToRegistry_TargetBuildDependencyRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
@ -535,7 +701,7 @@ namespace AZ::SettingsRegistryMergeUtils
const SettingsRegistryInterface::Specializations& specializations, AZStd::vector<char>* scratchBuffer)
{
AZ::SettingsRegistryInterface::FixedValueString sourceGamePath;
if (registry.Get(sourceGamePath, FilePathKey_SourceGameFolder))
if (registry.Get(sourceGamePath, FilePathKey_ProjectPath))
{
AZ::IO::FixedMaxPath mergePath{ sourceGamePath };
mergePath /= SettingsRegistryInterface::RegistryFolder;
@ -543,26 +709,41 @@ namespace AZ::SettingsRegistryMergeUtils
}
}
void MergeSettingsToRegistry_DevRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
void MergeSettingsToRegistry_UserRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
const SettingsRegistryInterface::Specializations& specializations, AZStd::vector<char>* scratchBuffer)
{
// Unlike other paths, the path can't be overwritten by the dev settings because that would create a circular dependency.
auto mergePath = GetAppRoot(&registry);
mergePath /= SettingsRegistryInterface::DevUserRegistryFolder;
registry.MergeSettingsFolder(mergePath.Native(), specializations, platform, "", scratchBuffer);
AZ::IO::FixedMaxPath projectUserPath;
if (registry.Get(projectUserPath.Native(), FilePathKey_ProjectPath))
{
projectUserPath /= SettingsRegistryInterface::DevUserRegistryFolder;
registry.MergeSettingsFolder(projectUserPath.Native(), specializations, platform, "", scratchBuffer);
}
}
void MergeSettingsToRegistry_CommandLine(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine, bool executeCommands)
{
const size_t regsetSwitchValues = commandLine.GetNumSwitchValues("regset");
for (size_t regsetIndex = 0; regsetIndex < regsetSwitchValues; ++regsetIndex)
// Iterate over all the command line options in order to parse the --regset and --regremove
// arguments in the order they were supplied
for (const CommandLine::CommandArgument& commandArgument : commandLine)
{
AZStd::string_view regsetValue = commandLine.GetSwitchValue("regset", regsetIndex);
if (!registry.MergeCommandLineArgument(regsetValue, AZStd::string_view{}))
if (commandArgument.m_option == "regset")
{
AZ_Warning("SettingsRegistryMergeUtils", false, "Unable to parse argument for --regset with value of %.*s.",
aznumeric_cast<int>(regsetValue.size()), regsetValue.data());
continue;
if (!registry.MergeCommandLineArgument(commandArgument.m_value, AZStd::string_view{}))
{
AZ_Warning("SettingsRegistryMergeUtils", false, "Unable to parse argument for --regset with value of %s.",
commandArgument.m_value.c_str());
continue;
}
}
if (commandArgument.m_option == "regremove")
{
if (!registry.Remove(commandArgument.m_value))
{
AZ_Warning("SettingsRegistryMergeUtils", false, "Unable to remove value at JSON Pointer %s for --regremove.",
commandArgument.m_value.data());
continue;
}
}
}
@ -585,16 +766,8 @@ namespace AZ::SettingsRegistryMergeUtils
DumpSettingsRegistryToStream(registry, regdumpValue, outputStream, dumperSettings);
}
const size_t regdumpallSwitchValues = commandLine.GetNumSwitchValues("regdumpall");
if (regdumpallSwitchValues > 0)
if (commandLine.HasSwitch("regdumpall"))
{
AZStd::string_view regdumpallValue = commandLine.GetSwitchValue("regdumpall", 0);
if (regdumpallValue.empty())
{
AZ_Warning("SettingsRegistryMergeUtils", false, "Unable to parse argument for --regdumpall with value of %.*s.",
aznumeric_cast<int>(regdumpallValue.size()), regdumpallValue.data());
}
DumperSettings dumperSettings{ prettifyOutput };
AZ::IO::StdoutStream outputStream;
DumpSettingsRegistryToStream(registry, "", outputStream, dumperSettings);
@ -602,45 +775,54 @@ namespace AZ::SettingsRegistryMergeUtils
}
}
void MergeSettingsToRegistry_StoreCommandLine(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine)
void StoreCommandLineToRegistry(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine)
{
// Clear out any existing CommandLine settings
registry.Remove(CommandLineRootKey);
// Add the positional arguments into the Settings Registry
AZ::SettingsRegistryInterface::FixedValueString miscValueKey{ CommandLineMiscValuesRootKey };
size_t miscKeyRootSize = miscValueKey.size();
for (size_t miscIndex = 0; miscIndex < commandLine.GetNumMiscValues(); ++miscIndex)
{
// Push an array for each positional entry
miscValueKey += AZ::SettingsRegistryInterface::FixedValueString::format("/%zu", miscIndex);
registry.Set(miscValueKey, commandLine.GetMiscValue(miscIndex));
miscValueKey.resize(miscKeyRootSize);
AZ::SettingsRegistryInterface::FixedValueString commandLinePath{ CommandLineRootKey };
const size_t commandLineRootSize = commandLinePath.size();
size_t argumentIndex{};
for (const CommandLine::CommandArgument& commandArgument : commandLine)
{
commandLinePath += AZ::SettingsRegistryInterface::FixedValueString::format("/%zu", argumentIndex);
registry.Set(commandLinePath + "/Option", commandArgument.m_option);
registry.Set(commandLinePath + "/Value", commandArgument.m_value);
++argumentIndex;
commandLinePath.resize(commandLineRootSize);
}
// Add the option arguments into the SettingsRegistry
for (const auto& [commandOption, value] : commandLine.GetSwitchList())
{
AZ::SettingsRegistryInterface::FixedValueString switchKey{ CommandLineSwitchRootKey };
switchKey += '/';
switchKey += commandOption;
size_t switchKeyRootSize = switchKey.size();
// Associate an empty array with the commandOption by default
rapidjson::Document commandSwitchDocument;
rapidjson::Pointer pointer(switchKey.c_str(), switchKey.length());
pointer.Set(commandSwitchDocument, rapidjson::Value(rapidjson::kArrayType));;
rapidjson::StringBuffer documentBuffer;
rapidjson::Writer documentWriter(documentBuffer);
commandSwitchDocument.Accept(documentWriter);
registry.MergeSettings(AZStd::string_view{ documentBuffer.GetString(), documentBuffer.GetSize() },
AZ::SettingsRegistryInterface::Format::JsonMergePatch);
for (size_t switchIndex = 0; switchIndex < value.size(); ++switchIndex)
{
// Push an array for each positional entry
switchKey += AZ::SettingsRegistryInterface::FixedValueString::format("/%zu", switchIndex);
registry.Set(switchKey, value[switchIndex]);
switchKey.resize(switchKeyRootSize);
}
bool GetCommandLineFromRegistry(SettingsRegistryInterface& registry, AZ::CommandLine& commandLine)
{
struct CommandLineVisitor
: AZ::SettingsRegistryInterface::Visitor
{
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type
, AZStd::string_view value) override
{
if (valueName == "Option" && !value.empty())
{
m_arguments.push_back(AZStd::string::format("--%.*s", aznumeric_cast<int>(value.size()), value.data()));
}
else if (valueName == "Value" && !value.empty())
{
m_arguments.push_back(value);
}
}
// The first parameter is skipped by the ComamndLine::Parse function so initialize
// the container with one empty element
AZ::CommandLine::ParamContainer m_arguments{ 1 };
};
CommandLineVisitor commandLineVisitor;
if (!registry.Visit(commandLineVisitor, AZ::SettingsRegistryMergeUtils::CommandLineRootKey))
{
return false;
}
commandLine.Parse(commandLineVisitor.m_arguments);
return true;
}
bool DumpSettingsRegistryToStream(SettingsRegistryInterface& registry, AZStd::string_view key,
@ -649,38 +831,21 @@ namespace AZ::SettingsRegistryMergeUtils
struct SettingsExportVisitor
: SettingsRegistryInterface::Visitor
{
using SettingsWriter = AZStd::variant<
rapidjson::PrettyWriter<AZ::IO::RapidJSONStreamWriter>,
rapidjson::Writer<AZ::IO::RapidJSONStreamWriter>>;
SettingsWriter m_writer;
AZ::IO::RapidJSONStreamWriter m_rapidJsonStream;
DumperSettings m_dumperSettings;
AZStd::stack<bool> m_includeNameStack;
bool m_includeName{};
bool m_result{ true };
SettingsExportVisitor(AZ::IO::GenericStream& stream, const DumperSettings& dumperSettings)
: m_rapidJsonStream{ &stream }
, m_dumperSettings{ dumperSettings }
, m_writer{ m_stringBuffer }
{
if (m_dumperSettings.m_prettifyOutput)
{
m_writer.emplace<rapidjson::PrettyWriter<AZ::IO::RapidJSONStreamWriter>>(m_rapidJsonStream);
}
else
{
m_writer.emplace<rapidjson::Writer<AZ::IO::RapidJSONStreamWriter>>(m_rapidJsonStream);
}
}
void WriteName(AZStd::string_view name)
bool WriteName(AZStd::string_view name)
{
if (m_includeName)
// Write the Key if the include name stack the top element is true
if (!m_includeNameStack.empty() && m_includeNameStack.top())
{
AZStd::visit([&name](auto&& writer)
{
writer.Key(name.data(), aznumeric_caster(name.size()));
}, m_writer);
return m_writer.Key(name.data(), aznumeric_caster(name.size()), false);
}
return true;
}
AZ::SettingsRegistryInterface::VisitResponse Traverse(
AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::VisitAction action,
@ -694,60 +859,43 @@ namespace AZ::SettingsRegistryMergeUtils
if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
{
AZ_Assert(type == AZ::SettingsRegistryInterface::Type::Object || type == AZ::SettingsRegistryInterface::Type::Array,
"Unexpected type visited: %i.", type);
WriteName(valueName);
m_result = m_result && WriteName(valueName);
if (type == AZ::SettingsRegistryInterface::Type::Object)
{
auto StartObject = [](auto&& writer)
m_result = m_result && m_writer.StartObject();
if (m_result)
{
return writer.StartObject();
};
m_result = m_result && AZStd::visit(StartObject, m_writer);
m_includeNameStack.push(true);
m_includeName = true;
m_includeNameStack.push(true);
}
}
else
{
auto StartArray = [](auto&& writer)
m_result = m_result && m_writer.StartArray();
if (m_result)
{
return writer.StartArray();
};
m_result = m_result && AZStd::visit(StartArray, m_writer);
m_includeNameStack.push(false);
m_includeName = false;
m_includeNameStack.push(false);
}
}
}
else if (action == AZ::SettingsRegistryInterface::VisitAction::End)
{
if (type == AZ::SettingsRegistryInterface::Type::Object)
{
auto EndObject = [](auto&& writer)
{
return writer.EndObject();
};
m_result = m_result && AZStd::visit(EndObject, m_writer);
m_result = m_result && m_writer.EndObject();
}
else
{
auto EndArray = [](auto&& writer)
{
return writer.EndArray();
};
m_result = m_result && AZStd::visit(EndArray, m_writer);
m_result = m_result && m_writer.EndArray();
}
AZ_Assert(!m_includeNameStack.empty(), "Attempting to close a json array or object that wasn't started.");
m_includeNameStack.pop();
m_includeName = !m_includeNameStack.empty() ? m_includeNameStack.top() : true;
}
else if (type == AZ::SettingsRegistryInterface::Type::Null)
else
{
WriteName(valueName);
auto WriteNull = [](auto&& writer)
if (type == AZ::SettingsRegistryInterface::Type::Null)
{
return writer.Null();
};
m_result = m_result && AZStd::visit(WriteNull, m_writer);
m_result = m_result && WriteName(valueName) && m_writer.Null();
}
}
return m_result ?
@ -757,42 +905,27 @@ namespace AZ::SettingsRegistryMergeUtils
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value)
{
WriteName(valueName);
auto WriteBool = [value](auto&& writer)
{
return writer.Bool(value);
};
m_result = m_result && AZStd::visit(WriteBool, m_writer);
m_result = m_result && WriteName(valueName) && m_writer.Bool(value);
}
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value)
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override
{
WriteName(valueName);
auto WriteInt64 = [value](auto&& writer)
{
return writer.Int64(value);
};
m_result = m_result && AZStd::visit(WriteInt64, m_writer);
m_result = m_result && WriteName(valueName) && m_writer.Int64(value);
}
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::u64 value) override
{
m_result = m_result && WriteName(valueName) && m_writer.Uint64(value);
}
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, double value)
{
WriteName(valueName);
auto WriteDouble = [value](auto&& writer)
{
return writer.Double(value);
};
m_result = m_result && AZStd::visit(WriteDouble, m_writer);
m_result = m_result && WriteName(valueName) && m_writer.Double(value);
}
void Visit(AZStd::string_view, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value)
{
WriteName(valueName);
auto WriteString = [&value](auto&& writer)
{
return writer.String(value.data(), aznumeric_caster(value.size()));
};
m_result = m_result && AZStd::visit(WriteString, m_writer);
m_result = m_result && WriteName(valueName) && m_writer.String(value.data(), aznumeric_caster(value.size()));
}
bool Finalize()
@ -802,8 +935,46 @@ namespace AZ::SettingsRegistryMergeUtils
AZ_Assert(false, "m_includeNameStack is expected to be empty. This means that there was an object or array what wasn't closed.");
return false;
}
return m_result;
// Root the JSON document underneath the JSON pointer prefix if non-empty
// Parse non-anchored JSON data into a document and then re-root
// that document under the prefix value
rapidjson::Document document;
document.Parse(m_stringBuffer.GetString(), m_stringBuffer.GetSize());
rapidjson::Document rootDocument;
AZStd::string_view jsonPrefix{ m_dumperSettings.m_jsonPointerPrefix };
if (!jsonPrefix.empty())
{
rapidjson::Pointer rootPointer(jsonPrefix.data(), jsonPrefix.size());
rapidjson::SetValueByPointer(rootDocument, rootPointer, document);
}
else
{
rootDocument = AZStd::move(document);
}
if (m_dumperSettings.m_prettifyOutput)
{
rapidjson::PrettyWriter settingsWriter(m_rapidJsonStream);
rootDocument.Accept(settingsWriter);
}
else
{
rapidjson::Writer settingsWriter(m_rapidJsonStream);
rootDocument.Accept(settingsWriter);
}
return true;
}
rapidjson::StringBuffer m_stringBuffer;
rapidjson::Writer<rapidjson::StringBuffer> m_writer;
AZ::IO::RapidJSONStreamWriter m_rapidJsonStream;
DumperSettings m_dumperSettings;
AZStd::stack<bool> m_includeNameStack;
bool m_result{ true };
};
SettingsExportVisitor visitor(stream, dumperSettings);

@ -36,27 +36,45 @@ namespace AZ::SettingsRegistryMergeUtils
inline static constexpr char FilePathsRootKey[] = "/Amazon/AzCore/Runtime/FilePaths";
inline static constexpr char FilePathKey_BinaryFolder[] = "/Amazon/AzCore/Runtime/FilePaths/BinaryFolder";
inline static constexpr char FilePathKey_EngineRootFolder[] = "/Amazon/AzCore/Runtime/FilePaths/EngineRootFolder";
//! Stores the absolute path to root of a project's cache. No asset platform in this path, this is where the asset database file lives.
//! i.e. <ProjectPath>/Cache
inline static constexpr char FilePathKey_CacheProjectRootFolder[] = "/Amazon/AzCore/Runtime/FilePaths/CacheProjectRootFolder";
//! Stores the absolute path to the cache root for an asset platform. This is the @root@ alias.
//! i.e. <ProjectPath>/Cache/<assetplatform>
inline static constexpr char FilePathKey_CacheRootFolder[] = "/Amazon/AzCore/Runtime/FilePaths/CacheRootFolder";
inline static constexpr char FilePathKey_CacheGameFolder[] = "/Amazon/AzCore/Runtime/FilePaths/CacheGameFolder";
inline static constexpr char FilePathKey_SourceGameFolder[] = "/Amazon/AzCore/Runtime/FilePaths/SourceGameFolder";
//! Stores the filename of the Game Project Directory which is equivalent to the project name
inline static constexpr char FilePathKey_SourceGameName[] = "/Amazon/AzCore/Runtime/FilePaths/SourceGameName";
//! Stores the absolute path of the Game Project Directory
inline static constexpr char FilePathKey_ProjectPath[] = "/Amazon/AzCore/Runtime/FilePaths/SourceProjectPath";
//! Store the absolute path to the Projects "user" directory, which is a transient directory where per user
//! project settings can be stored
inline static constexpr char FilePathKey_ProjectUserPath[] = "/Amazon/AzCore/Runtime/FilePaths/SourceProjectUserPath";
//! Development write storage path may be considered temporary or cache storage on some platforms
inline static constexpr char FilePathKey_DevWriteStorage[] = "/Amazon/AzCore/Runtime/FilePaths/DevWriteStorage";
//! Root key for where command line are stored at witin the settings registry
//! Root key for where command line are stored at within the settings registry
inline static constexpr char CommandLineRootKey[] = "/Amazon/AzCore/Runtime/CommandLine";
//! Root key for command line switches(arguments that start with "-" or "--")
inline static constexpr char CommandLineSwitchRootKey[] = "/Amazon/AzCore/Runtime/CommandLine/Switches";
//! Root key for command line positional arguments
inline static constexpr char CommandLineMiscValuesRootKey[] = "/Amazon/AzCore/Runtime/CommandLine/MiscValues";
//! Examines the Settings Registry for a "/Amazon/CommandLine/Switches/app-root" key
//! to use as an override for the Application Root.
//! If that key is not found, it then checks the AZ::Utils::GetDefaultAppRootPath and returns that if it is value
//! Root key where raw project settings (project.json) file is merged to settings registry
inline static constexpr char ProjectSettingsRootKey[] = "/Amazon/Project/Settings";
//! Root key where raw engine manifest (o3de_manifest.json) file is merged to settings registry
inline static constexpr char EngineManifestRootKey[] = "/Amazon/Engine/Manifest";
//! Root key where raw engine settings (engine.json) file is merged to settings registry
inline static constexpr char EngineSettingsRootKey[] = "/Amazon/Engine/Settings";
//! Examines the Settings Registry for a "${BootstrapSettingsRootKey}/engine_path" key
//! to use as an override for the Engine Root.
//! Otherwise a directory walk upwards from the executable directory is performed
//! to find the boostrap.cfg file which will be used as the app root
AZ::IO::FixedMaxPath GetAppRoot(SettingsRegistryInterface* settingsRegistry = nullptr);
//! to find the engine.json file which will be used as the engine root
//! If it's still not found, attempt to find the project (by similar means) then reconcile the
//! engine root by inspecting project.json and the engine manifest file.
AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry);
AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry);
//! Query the specializations that will be used when loading the Settings Registry.
//! The SpecializationsRootKey is visited to retrieve any specializations stored within that section of that registry
@ -151,16 +169,18 @@ namespace AZ::SettingsRegistryMergeUtils
void MergeSettingsToRegistry_ProjectRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
const SettingsRegistryInterface::Specializations& specializations, AZStd::vector<char>* scratchBuffer = nullptr);
//! Adds the development settings added by individual users to the Settings Registry.
//! Adds the development settings added by individual users of the project to the Settings Registry.
//! Note that this function is only called in development builds and is compiled out in release builds.
void MergeSettingsToRegistry_DevRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
void MergeSettingsToRegistry_UserRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
const SettingsRegistryInterface::Specializations& specializations, AZStd::vector<char>* scratchBuffer = nullptr);
//! Adds the settings set through the command line to the Settings Registry. This will also execute any Settings
//! Registry related arguments. Note that --set will be run first and all other commands are run afterwards and only
//! if executeCommands is true. The following options are supported:
//! Registry related arguments. Note that --regset and -regremove will run in the order in which they are parsed
//! --regset <arg> Sets a value in the registry. See MergeCommandLineArgument for options for <arg>
//! example: --regset "/My/String/Value=String value set"
//! --regremove <arg> Removes a value in the registry
//! example: --regremove "/My/String/Value"
//! only when executeCommands is true are the following options supported:
//! --regdump <path> Dumps the content of the key at path and all it's content/children to output.
//! example: --regdump /My/Array/With/Objects
//! --regdumpall Dumps the entire settings registry to output.
@ -169,7 +189,11 @@ namespace AZ::SettingsRegistryMergeUtils
//! Stores the command line settings into the Setting Registry
//! The arguments can be used later anywhere the command line is needed
void MergeSettingsToRegistry_StoreCommandLine(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine);
void StoreCommandLineToRegistry(SettingsRegistryInterface& registry, const AZ::CommandLine& commandLine);
//! Query the command line settings from the Setting Registry and stores them
//! into the AZ::CommandLine instance
bool GetCommandLineFromRegistry(SettingsRegistryInterface& registry, AZ::CommandLine& commandLine);
//! Structure for configuring how values should be dumped from the Settings Registry
struct DumperSettings
@ -179,14 +203,25 @@ namespace AZ::SettingsRegistryMergeUtils
//! Include filter which is used to indicate which paths of the Settings Registry
//! should be traversed.
//! If the include filter is empty then all paths underneath the JSON pointer path are included
//! otherwise the include filter invoked and if it returns true does it proceed with traversal continues down the path
//! otherwise the include filter invoked and if it returns true does it proceed with traversal down the path
//! The supplied JSON pointer will be a complete path from the root of the registry
AZStd::function<bool(AZStd::string_view path)> m_includeFilter;
//! JSON pointer prefix to dump all settings underneath
//! For example if the prefix is "/Amazon/Settings", then the dumped settings will be placed underneath
//! an object at that path
//! """
//! {
//! "Amazon":{
//! "Settings":{ <Dumped values> }
//! }
//! }
AZStd::string_view m_jsonPointerPrefix;
};
//! Dumps supplied settings registry from the path specified by key if it exist the the AZ::IO::GenericStream
//! key is a JSON pointer path to dumping settings recursively from
//! stream is an AZ::IO::GenericStream that supports writing
//! dumperSettings are used to determine how to format the dumped output
//! @param key is a JSON pointer to recursively dump settings from
//! @param stream is an AZ::IO::GenericStream that supports writing
//! @param dumperSettings are used to determine how to format the dumped output
bool DumpSettingsRegistryToStream(SettingsRegistryInterface& registry, AZStd::string_view key,
AZ::IO::GenericStream& stream, const DumperSettings& dumperSettings);

@ -418,36 +418,37 @@ namespace AZ::StringFunc::Internal
return Strip(inout, { &stripCharacter, 1 }, bCaseSensitive, bStripBeginning, bStripEnding);
}
AZStd::string_view LStrip(AZStd::string_view in, AZStd::string_view stripCharacters)
}
namespace AZ
{
namespace StringFunc
{
if (size_t pos = in.find_first_not_of(stripCharacters); pos != AZStd::string_view::npos)
AZStd::string_view LStrip(AZStd::string_view in, AZStd::string_view stripCharacters)
{
return in.substr(pos);
}
if (size_t pos = in.find_first_not_of(stripCharacters); pos != AZStd::string_view::npos)
{
return in.substr(pos);
}
return {};
};
return {};
};
AZStd::string_view RStrip(AZStd::string_view in, AZStd::string_view stripCharacters)
{
if (size_t pos = in.find_last_not_of(stripCharacters); pos != AZStd::string_view::npos)
AZStd::string_view RStrip(AZStd::string_view in, AZStd::string_view stripCharacters)
{
return in.substr(0, pos < in.size() ? pos + 1 : pos);
}
if (size_t pos = in.find_last_not_of(stripCharacters); pos != AZStd::string_view::npos)
{
return in.substr(0, pos < in.size() ? pos + 1 : pos);
}
return {};
};
return {};
};
AZStd::string_view StripEnds(AZStd::string_view in, AZStd::string_view stripCharacters)
{
return LStrip(RStrip(in, stripCharacters), stripCharacters);
};
}
AZStd::string_view StripEnds(AZStd::string_view in, AZStd::string_view stripCharacters)
{
return LStrip(RStrip(in, stripCharacters), stripCharacters);
};
namespace AZ
{
namespace StringFunc
{
bool Equal(const char* inA, const char* inB, bool bCaseSensitive /*= false*/, size_t n /*= 0*/)
{
if (!inA || !inB)
@ -483,6 +484,14 @@ namespace AZ
}
}
}
bool Equal(AZStd::string_view inA, AZStd::string_view inB, bool bCaseSensitive)
{
const size_t maxCharsToCompare = inA.size();
return inA.size() == inB.size() && (bCaseSensitive
? strncmp(inA.data(), inB.data(), maxCharsToCompare) == 0
: azstrnicmp(inA.data(), inB.data(), maxCharsToCompare) == 0);
}
bool StartsWith(AZStd::string_view sourceValue, AZStd::string_view prefixValue, bool bCaseSensitive)
{
@ -496,9 +505,18 @@ namespace AZ
&& Equal(sourceValue.substr(sourceValue.size() - suffixValue.size(), AZStd::string_view::npos).data(), suffixValue.data(), bCaseSensitive, suffixValue.size());
}
size_t Find(const char* in, char c, size_t pos /*= 0*/, bool bReverse /*= false*/, bool bCaseSensitive /*= false*/)
bool Contains(AZStd::string_view in, char ch, bool bCaseSensitive)
{
if (!in)
return Find(in, ch, 0, false, bCaseSensitive) != AZStd::string_view::npos;
}
bool Contains(AZStd::string_view in, AZStd::string_view sv, bool bCaseSensitive)
{
return Find(in, sv, 0, false, bCaseSensitive) != AZStd::string_view::npos;
}
size_t Find(AZStd::string_view in, char c, size_t pos /*= 0*/, bool bReverse /*= false*/, bool bCaseSensitive /*= false*/)
{
if (in.empty())
{
return AZStd::string::npos;
}
@ -508,7 +526,7 @@ namespace AZ
pos = 0;
}
size_t inLen = strlen(in);
size_t inLen = in.size();
if (inLen < pos)
{
return AZStd::string::npos;
@ -557,7 +575,13 @@ namespace AZ
size_t Find(AZStd::string_view in, AZStd::string_view s, size_t offset /*= 0*/, bool bReverse /*= false*/, bool bCaseSensitive /*= false*/)
{
if (in.empty() || s.empty())
// Formally an empty string matches at the offset if it is <= to the size of the input string
if (s.empty() && offset <= in.size())
{
return offset;
}
if (in.empty())
{
return AZStd::string::npos;
}
@ -773,7 +797,7 @@ namespace AZ
bool bIsSpaces = false;
if (!bIsEmpty)
{
AZStd::string_view strippedNextToken = Internal::StripEnds(*nextToken, " ");
AZStd::string_view strippedNextToken = StripEnds(*nextToken, " ");
bIsSpaces = strippedNextToken.empty();
}
@ -805,7 +829,7 @@ namespace AZ
bool bIsSpaces = false;
if (!bIsEmpty)
{
AZStd::string_view strippedNextToken = Internal::StripEnds(*nextToken, " ");
AZStd::string_view strippedNextToken = StripEnds(*nextToken, " ");
bIsSpaces = strippedNextToken.empty();
}

@ -12,6 +12,7 @@
#pragma once
#include <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/std/function/function_fwd.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzCore/std/string/string.h>
@ -107,6 +108,22 @@ namespace AZ
StringFunc::Equal("Hello World", "Hello", true, 3) = true
*/
bool Equal(const char* inA, const char* inB, bool bCaseSensitive = false, size_t n = 0);
bool Equal(AZStd::string_view inA, AZStd::string_view inB, bool bCaseSensitive = false);
//! Contains
/*! Checks if the supplied character or string is contained within the @in parameter
*
Example: Case Insensitive contains finds character
StringFunc::Contains("Hello", 'L') == true
Example: Case Sensitive contains finds character
StringFunc::Contains("Hello", 'l', true) = true
Example: Case Insensitive contains does not find string
StringFunc::Contains("Well Hello", "Mello") == false
Example: Case Sensitive contains does not find character
StringFunc::Contains("HeLlo", 'h', true) == false
*/
bool Contains(AZStd::string_view in, char ch, bool bCaseSensitive = false);
bool Contains(AZStd::string_view in, AZStd::string_view sv, bool bCaseSensitive = false);
//! Find
/*! Find for non AZStd::strings. Ease of use to find the first or last occurrence of a character or substring in a c-string with case sensitivity.
@ -119,7 +136,7 @@ namespace AZ
Example: Case Sensitive find first occurrence of substring "Hello" a in a c-string
StringFunc::Find("Well Hello", "Hello", false, true) == 5
*/
size_t Find(const char* in, char c, size_t pos = 0, bool bReverse = false, bool bCaseSensitive = false);
size_t Find(AZStd::string_view in, char c, size_t pos = 0, bool bReverse = false, bool bCaseSensitive = false);
size_t Find(AZStd::string_view in, AZStd::string_view str, size_t pos = 0, bool bReverse = false, bool bCaseSensitive = false);
//! First and Last Character
@ -196,6 +213,33 @@ namespace AZ
*/
AZStd::string& TrimWhiteSpace(AZStd::string& value, bool leading, bool trailing);
//! LStrip
/*! Strips leading characters in the stripCharacters set
* Example
* Example: Case Insensitive Strip leading 'a' characters
* StringFunc::LStrip(s = "Abracadabra", 'a'); s == "bracadabra"
* Example: Case Sensitive Strip leading 'a' characters
* StringFunc::LStrip(s = "Abracadabra", 'a'); s == "Abracadabra"
*/
//! RStrip
/*! Strips trailing characters in the stripCharacters set
* Example
* Example: Case Insensitive Strip trailing 'a' characters
* StringFunc::RStrip(s = "AbracadabrA", 'a'); s == "Abracadabr"
* Example: Case Sensitive Strip trailing 'a' characters
* StringFunc::RStrip(s = "AbracadabrA", 'a'); s == "AbracadabrA"
*/
//! StripEnds
/*! Strips leading and trailing characters in the stripCharacters set
Example: Case Insensitive Strip all 'a' characters
StringFunc::StripEnds(s = "Abracadabra", 'a'); s == "bracadabr"
Example: Case Sensitive Strip all 'a' characters
StringFunc::StripEnds(s = "Abracadabra", 'a'); s == "Abracadabr"
*/
AZStd::string_view LStrip(AZStd::string_view in, AZStd::string_view stripCharacters = " ");
AZStd::string_view RStrip(AZStd::string_view in, AZStd::string_view stripCharacters = " ");
AZStd::string_view StripEnds(AZStd::string_view in, AZStd::string_view stripCharacters = " ");
//! Strip
/*! Strip away the leading, trailing or all character(s) or substring(s) in a AZStd::string with
*! case sensitivity.
@ -669,8 +713,7 @@ namespace AZ
*/
namespace Path
{
inline constexpr size_t MaxPathLength = 1024;
using FixedString = AZStd::fixed_string<MaxPathLength>;
using FixedString = AZ::IO::FixedMaxPathString;
//! Normalize
/*! Normalizes a path and returns returns StringFunc::Path::IsValid()
*! strips all AZ_FILESYSTEM_INVALID_CHARACTERS
@ -791,7 +834,7 @@ namespace AZ
//! StripFullName
/*! gets rid of the full file name if it has one, returns if it removed one or not
*! EX: StringFunc::Path::StripFullName(a="C:\\p4\\game\\info\\some.file") == true; a=="C:\\p4\\game\\info\\"
*! EX: StringFunc::Path::StripFullName(a="C:\\p4\\game\\info\\some.file") == true; a=="C:\\p4\\game\\info"
*/
void StripFullName(AZStd::string& out);

@ -39,6 +39,7 @@ namespace UnitTest
MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ());
MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ());
MOCK_CONST_METHOD0(GetAppRoot, const char* ());
MOCK_CONST_METHOD0(GetEngineRoot, const char* ());
MOCK_CONST_METHOD0(GetExecutableFolder, const char* ());
MOCK_METHOD0(GetDrillerManager, AZ::Debug::DrillerManager* ());
MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&));

@ -60,6 +60,7 @@ namespace AZ
MOCK_CONST_METHOD2(ConvertToAlias, bool(AZ::IO::FixedMaxPath& aliasPath, const AZ::IO::PathView& path));
MOCK_CONST_METHOD3(ResolvePath, bool(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize));
MOCK_CONST_METHOD2(ResolvePath, bool(AZ::IO::FixedMaxPath& resolvedPath, const AZ::IO::PathView& path));
MOCK_CONST_METHOD2(ReplaceAlias, bool(AZ::IO::FixedMaxPath& replacedAliasPath, const AZ::IO::PathView& path));
MOCK_CONST_METHOD3(GetFilename, bool(HandleType fileHandle, char* filename, AZ::u64 filenameSize));
using FileIOBase::ConvertToAlias;
using FileIOBase::ResolvePath;

@ -16,6 +16,8 @@
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/StringFunc/StringFunc.h>
namespace AZ::Utils
@ -40,6 +42,47 @@ namespace AZ::Utils
return result.m_pathStored;
}
AZ::IO::FixedMaxPathString GetEnginePath()
{
if (auto registry = AZ::SettingsRegistry::Get(); registry != nullptr)
{
AZ::SettingsRegistryInterface::FixedValueString settingsValue;
if (registry->Get(settingsValue, AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
{
return AZ::IO::FixedMaxPathString{settingsValue};
}
}
return {};
}
AZ::IO::FixedMaxPathString GetProjectPath()
{
if (auto registry = AZ::SettingsRegistry::Get(); registry != nullptr)
{
AZ::SettingsRegistryInterface::FixedValueString settingsValue;
if (registry->Get(settingsValue, AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
{
return AZ::IO::FixedMaxPathString{settingsValue};
}
}
return {};
}
AZ::SettingsRegistryInterface::FixedValueString GetProjectName()
{
if (auto registry = AZ::SettingsRegistry::Get(); registry != nullptr)
{
AZ::SettingsRegistryInterface::FixedValueString projectNameKey{ AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey };
projectNameKey += "/project_name";
AZ::SettingsRegistryInterface::FixedValueString settingsValue;
if (registry->Get(settingsValue, projectNameKey))
{
return settingsValue;
}
}
return {};
}
AZ::Outcome<void, AZStd::string> WriteFile(AZStd::string_view content, AZStd::string_view filePath)
{
AZ::IO::FixedMaxPath filePathFixed = filePath; // Because FileIOStream requires a null-terminated string

@ -16,6 +16,8 @@
#include <AzCore/base.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/string/fixed_string.h>
@ -24,9 +26,6 @@ namespace AZ
{
namespace Utils
{
// Common cross platform Utils go here
inline constexpr size_t MaxPathLength = 1024;
//! Protects from allocating too much memory. The choice of a 1MB threshold is arbitrary.
//! If you need to work with larger files, please use AZ::IO directly instead of these utility functions.
inline constexpr size_t DefaultMaxFileSize = 1024 * 1024;
@ -77,10 +76,22 @@ namespace AZ
//! @returns a result object that indicates if the executable directory was able to be stored within the buffer
ExecutablePathResult GetExecutableDirectory(char* exeStorageBuffer, size_t exeStorageSize);
//! Retrieves the full path to the engine from settings registry
AZ::IO::FixedMaxPathString GetEnginePath();
//! Retrieves the full path to the project from settings registry
AZ::IO::FixedMaxPathString GetProjectPath();
//! Retrieves the project name from the settings registry
AZ::SettingsRegistryInterface::FixedValueString GetProjectName();
//! Retrieves the full path where the manifest file lives, i.e. "<userhome>/.o3de/o3de_manifest.json"
AZ::IO::FixedMaxPathString GetEngineManifestPath();
//! Retrieves the App root path to use on the current platform
//! If the optional is not engaged the AppRootPath should be calculated based
//! on the location of the bootstrap.cfg file
AZStd::optional<AZStd::fixed_string<MaxPathLength>> GetDefaultAppRootPath();
AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultAppRootPath();
//! Retrieves the development write storage path to use on the current platform, may be considered
//! temporary or cache storage
@ -88,7 +99,7 @@ namespace AZ
// Attempts the supplied path to an absolute path.
//! Returns nullopt if path cannot be converted to an absolute path
AZStd::optional<AZStd::fixed_string<MaxPathLength>> ConvertToAbsolutePath(AZStd::string_view path);
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path);
//! Save a string to a file. Otherwise returns a failure with error message.
AZ::Outcome<void, AZStd::string> WriteFile(AZStd::string_view content, AZStd::string_view filePath);

@ -249,6 +249,11 @@ namespace AZStd
constexpr auto swap(basic_fixed_string& rhs) -> void;
// C++23 contains
constexpr auto contains(const basic_fixed_string& other) const -> bool;
constexpr auto contains(Element ch) const -> bool;
constexpr auto contains(const_pointer s) const -> bool;
constexpr auto find(const basic_fixed_string& rhs, size_type offset = 0) const -> size_type;
constexpr auto find(const_pointer ptr, size_type offset, size_type count) const -> size_type;
constexpr auto find(const_pointer ptr, size_type offset = 0) const -> size_type;

@ -1072,6 +1072,23 @@ namespace AZStd
Traits::assign(rhs.m_buffer[rhs.m_size], Element{ 0 });
}
// C++23 contains
template<class Element, size_t MaxElementCount, class Traits>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::contains(const basic_fixed_string& other) const -> bool
{
return find(other) != npos;
}
template<class Element, size_t MaxElementCount, class Traits>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::contains(value_type c) const -> bool
{
return find(c) != npos;
}
template<class Element, size_t MaxElementCount, class Traits>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::contains(const_pointer s) const -> bool
{
return find(s) != npos;
}
template<class Element, size_t MaxElementCount, class Traits>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::find(const basic_fixed_string& rhs, size_type offset) const -> size_type
{

@ -961,6 +961,22 @@ namespace AZStd
}
}
// C++23 contains
bool contains(const basic_string& other) const
{
return find(other) != npos;
}
bool contains(value_type c) const
{
return find(c) != npos;
}
bool contains(const_pointer s) const
{
return find(s) != npos;
}
inline size_type find(const this_type& rhs, size_type offset = 0) const
{
const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer;

@ -669,6 +669,22 @@ namespace AZStd
return ends_with(basic_string_view(suffix));
}
// C++23 contains
constexpr bool contains(basic_string_view other) const
{
return find(other) != npos;
}
constexpr bool contains(value_type c) const
{
return find(c) != npos;
}
constexpr bool contains(const_pointer s) const
{
return find(s) != npos;
}
// find
constexpr size_type find(basic_string_view other, size_type pos = 0) const
{

@ -52,10 +52,10 @@ namespace AZ
// in non-release builds.
// If a bootstrap.cfg file is not found in the public storage, it is then searched for
// within the APK itself
AZStd::optional<AZStd::fixed_string<MaxPathLength>> GetDefaultAppRootPath()
AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultAppRootPath()
{
const char* appRoot = AZ::Android::Utils::FindAssetsDirectory();
return appRoot ? AZStd::make_optional<AZStd::fixed_string<MaxPathLength>>(appRoot) : AZStd::nullopt;
return appRoot ? AZStd::make_optional<AZ::IO::FixedMaxPathString>(appRoot) : AZStd::nullopt;
}
AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath()
@ -64,10 +64,10 @@ namespace AZ
return writeStorage ? AZStd::make_optional<AZ::IO::FixedMaxPathString>(writeStorage) : AZStd::nullopt;
}
AZStd::optional<AZStd::fixed_string<MaxPathLength>> ConvertToAbsolutePath(AZStd::string_view path)
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
{
AZStd::fixed_string<MaxPathLength> absolutePath;
AZStd::fixed_string<MaxPathLength> srcPath{ path };
AZ::IO::FixedMaxPathString absolutePath;
AZ::IO::FixedMaxPathString srcPath{ path };
if (AZ::Android::Utils::IsApkPath(srcPath.c_str()))
{
return srcPath;

@ -64,6 +64,7 @@ set(FILES
AzCore/Socket/AzSocket_Platform.h
../Common/UnixLike/AzCore/std/time_UnixLike.cpp
AzCore/Utils/Utils_Android.cpp
../Common/Unimplemented/AzCore/Utils/Utils_Unimplemented.cpp
../../AzCore/Android/AndroidEnv.cpp
../../AzCore/Android/AndroidEnv.h
../../AzCore/Android/APKFileHandler.cpp

@ -0,0 +1,22 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Utils/Utils.h>
namespace AZ::Utils
{
AZ::IO::FixedMaxPathString GetEngineManifestPath()
{
return {};
}
} // namespace AZ::Utils

@ -12,7 +12,7 @@
#include <AzCore/Utils/Utils.h>
#include <stdlib.h>
#include <cstdlib>
namespace AZ
{
@ -25,10 +25,25 @@ namespace AZ
void NativeErrorMessageBox(const char*, const char*) {}
AZStd::optional<AZStd::fixed_string<MaxPathLength>> ConvertToAbsolutePath(AZStd::string_view path)
AZ::IO::FixedMaxPathString GetEngineManifestPath()
{
AZStd::fixed_string<MaxPathLength> absolutePath;
AZStd::fixed_string<MaxPathLength> srcPath{ path };
if (const char* homePath = std::getenv("HOME"); homePath != nullptr)
{
AZ::IO::FixedMaxPath path{homePath};
if (!path.empty())
{
path /= ".o3de";
path /= "o3de_manifest.json";
}
return path.Native();
}
return {};
}
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
{
AZ::IO::FixedMaxPathString absolutePath;
AZ::IO::FixedMaxPathString srcPath{ path };
if (char* result = realpath(srcPath.c_str(), absolutePath.data()); result)
{

@ -47,7 +47,7 @@ namespace AZ
return result;
}
AZStd::optional<AZStd::fixed_string<MaxPathLength>> GetDefaultAppRootPath()
AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultAppRootPath()
{
return AZStd::nullopt;
}
@ -57,10 +57,10 @@ namespace AZ
return AZStd::nullopt;
}
AZStd::optional<AZStd::fixed_string<MaxPathLength>> ConvertToAbsolutePath(AZStd::string_view path)
AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
{
AZStd::fixed_string<MaxPathLength> absolutePath;
AZStd::fixed_string<MaxPathLength> srcPath{ path };
AZ::IO::FixedMaxPathString absolutePath;
AZ::IO::FixedMaxPathString srcPath{ path };
char* result = _fullpath(absolutePath.data(), srcPath.c_str(), absolutePath.capacity());
// Force update of the fixed_string size() value
absolutePath.resize_no_construct(AZStd::char_traits<char>::length(absolutePath.data()));

@ -45,7 +45,7 @@ namespace AZ
return result;
}
AZStd::optional<AZStd::fixed_string<MaxPathLength>> GetDefaultAppRootPath()
AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultAppRootPath()
{
return AZStd::nullopt;
}

@ -16,7 +16,7 @@
namespace AZ::Utils
{
AZStd::optional<AZStd::fixed_string<MaxPathLength>> GetDefaultAppRootPath()
AZStd::optional<AZ::IO::FixedMaxPathString> GetDefaultAppRootPath()
{
return AZStd::nullopt;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save