The AFCM was developed to have better control about memory budget and on time data delivery in the game environment. The concept of the AFCM is to move as much audio data into memory as possible to take load away from the streaming engine for fast audio data delivery and playback on demand. This allows for great performance (no memcopies necessary) and efficient memory utilization (less fragmentation). The AFCM also strongly encourages sound designers to build their FMOD projects and corresponding soundbanks as slim as possible to ensure no precious memory is wasted, especially on consoles. This in turns means that when your product is ready for release even the data that needs to get burned to the release media or uploaded to the clients is at its minimum and therefore saving disk space and/or bandwidth.
Features:
\Libs\GameAudio
).The idea behind the AFCM is to efficiently load and store data for fast in memory access. This allows for immediate availability of sound data without the need for additional copy procedures from different medias.
To achieve that we allow to load audio data in form of entire soundbanks and assign them to corresponding logical groups:
Globals
This section allows for holding audio data for the entire duration of the game. Here you want to store data that every level needs to access, e.g. player, physics or GUI.
Level Specifics
In this section you want to load data that is specific only to the currently loaded level and needed throughout the entire duration of it. This can be data such as vehicles or certain player classes. Should you have no levels then "Globals" and "Level Specifics" are the same.
Game Hints
Here you want to specify your "Game Hints". These are logical compilations of soundbanks such as weapons, vehicles or area based (Sound:AudioFileCache can be used here) collections. Game Hints are meant to be dynamically loaded and unloaded during game play but prevent rapid loading and unloading and try to find rather generally calm sections to prepare or remove the audio data. This has been extensively used in the Crysis games for example for many sound emitting entity types that dynamically come and go. Crysis' weapon pickup system or proximity mines for example made use of this. Also GameHints can be more manually handled through the Sound:AudioFileCache node in FlowGraph.
Partially Cached
This is used rather internally only to identify streamed soundbanks. Whenever a streamed FMOD event starts the first 256KiB of the corresponding streaming soundbank get cached as every newly started stream issues a number of reads to the soundbank's header. This considerably reduces the number of asynchronous read requests to CryENGINE's internal streaming system which is occupied by other systems as well. To reduce memory occupied by these type of caches we advise to organize all of your level's streams into 1 single streamed soundbank only.
Music
When enabled (see CVar s_AllowMusicFileCaching) music files that are compiled into the *.fsb container and fit into memory get automatically precached to the AFCM. This means that even data of currently playing music tracks wouldn't need to get copied from the medias such as HDD or DVD. This system works fully automatic meaning music files are treated as fully volatile files. Once the memory occupied by music files is needed for more urgent data such as Game Hints those files get thrown out to make room. As you can see a lot of loading/unloading could be going on here hence fragmenting the memory quite heavily. Also please note that this works currently only with *.fsb type music files!
It is important that you do NOT cache streamed soundbanks to the AFCM! In general streamed soundbanks are meant to be streamed from and not cached into memory.
As mentioned above the the AFCM manages soundbank cache entries into 5 different groups:
Programmers ideally want to only manipulate the Game Hint entries during runtime as the "Globals" and "Level Specifics" sections are meant to form a nice and compact memory block to minimize fragmentation and maximize utilization of the available AFCM memory block on the PS3.
The picture below gives a visual impression of this fact.
So the only "dynamic" memory parts here should be the "Game Hints" and "Music" groups.
The AudioFileCacheManager has to be setup via the AudioPreloads.xml under \Libs\GameAudio
<?xml version="1.0" encoding="utf-8"?>
<PreloadData>
<Platform PC="1" Xbox360="0" PS3="0">
<Global>
<File name="sounds/dialog/dialog_background.fsb" stage="1" />
<File name="sounds/interface/interface_menu.fsb" stage="1" />
<File name="sounds/interface/interface_player.fsb" stage="1" />
<File name="sounds/physics/physics_bullets.fsb" stage="1" />
<File name="sounds/physics/physics_collisions.fsb" stage="1" />
<File name="sounds/physics/physics_destructibles.fsb" stage="1" />
<File name="sounds/physics/physics_foleys.fsb" stage="1" />
<File name="sounds/physics/physics_footsteps.fsb" stage="1" />
<File name="sounds/physics/physics_objects.fsb" stage="1" />
<File name="sounds/physics/physics_particles.fsb" stage="1" />
</Global>
<Level name="forest">
<File name="sounds/environment/environment_forest.fsb" />
</Level>
</Platform>
<Platform PC="0" Xbox360="1" PS3="1">
<Global>
<File name="sounds/dialog/dialog_background.fsb" stage="1" />
<File name="sounds/interface/interface_menu.fsb" stage="1" />
<File name="sounds/interface/interface_player.fsb" stage="1" />
<File name="sounds/physics/physics_bullets.fsb" stage="1" />
<File name="sounds/physics/physics_collisions.fsb" stage="1" />
<File name="sounds/physics/physics_destructibles.fsb" stage="1" />
<File name="sounds/physics/physics_foleys.fsb" stage="1" />
<File name="sounds/physics/physics_footsteps.fsb" stage="1" />
<File name="sounds/physics/physics_objects.fsb" stage="1" />
<File name="sounds/physics/physics_particles.fsb" stage="1" />
</Global>
<Level name="forest">
<File name="sounds/environment/environment_forest.fsb" />
</Level>
</Platform>
<GameHints>
<!-- WEAPONS -->
<scar>
<File name="sounds/weapons/weapons_scar.fsb" />
<File name="sounds/weapons/weapons_accessories.fsb" />
</scar>
<!-- VEHICLES -->
<abrams>
<File name="sounds/vehicles/vehicles_abrams.fsb" />
<File name="sounds/vehicles/vehicles_accesories.fsb" />
</abrams>
<hummwv>
<File name="sounds/vehicles/vehicles_hummwv.fsb" />
<File name="sounds/vehicles/vehicles_accesories.fsb" />
</hummwv>
</GameHints>
</PreloadData>
In case of having cutscenes use the following setup:
<!-- CUTSCENES -->
<anim_sequence_cine_3_findingAztec>
<File name="sounds/cutscenes/cutscenes_island_aztec.fsb" />
<File name="sounds/vehicles/vehicles_accesories.fsb" />
<File name="sounds/weapons/w_heavymachinegun_dryfire.fsb" />
</anim_sequence_cine_3_findingAztec>
For preloading dialog voice line type *.fsb files use the following setup:
<!-- Global localization file preloading -->
<Global>
<File name="sounds/menu/main_menu.fsb" stage="1" />
<File name="sounds/physics/world_collisions.fsb" stage="2" />
<Localization>
<!-- Preload the entire folder -->
<Folder name="ai_barbarian_01" />
<!-- Preload only a single voice line -->
<File name="ai_barbarian_02/attack_heavy_01" />
</Localization>
</Global>
<!-- Level specific localization file preloading -->
<Level name="Horde_Black_Forest">
<File name="sounds/boids/boids.fsb" />
<Localization>
<!-- Preload the entire folder -->
<Folder name="ai_barbarian_01" />
<!-- Preload only a single voice line -->
<File name="ai_barbarian_02/attack_heavy_01" />
</Localization>
</Level>
<!-- Game Hint localization file preloading -->
<GameHints>
<enter_cave>
<File name="sounds/w_ballista/w_ballista.fsb" />
<Localization>
<!-- Preload the entire folder -->
<Folder name="ai_barbarian_01" />
<!-- Preload only a single voice line -->
<File name="ai_barbarian_02/attack_heavy_01" />
</Localization>
</enter_cave>
</GameHints>
Gamehints can be loaded and unloaded via Flowgraph using the Sound:AudioFileCache node.
Keep in mind that it is not possible to unload data via a node if you haven't loaded any before.
Loading and unloading of game hints is simple and straight forward. To load a game hint in code simply call:
gEnv->pSoundSystem->CacheAudioFile("path of file or name of GameHint", AudioFileCacheType);
To unload call:
gEnv->pSoundSystem->RemoveCachedAudioFile("path of file or name of GameHint", bNow);
In the "RemoveCachedAudioFile" call you have the option to pass a boolean parameter called bNow. This simply controls whether a GameHint will be completely removed once its used count reached 0 or remains in memory.
Additionally you can issue a call to clear out certain audio file cache types via:
gEnv->pSoundSystem->ClearAudioDataCache(AudioFileCacheType);
For the parameter AudioFileCacheType type you can use exactly the ones you use during caching as mentioned in the list below plus 1 additional one: eAFCT_ALL - All currently cached files will be removed.
To manually preload Global type entries that are a stage other than 1:
gEnv->pSoundSystem->CacheAudioData(NULL, 2 /*or whatever stage you need to preload*/);
The AFCM internally loads stage 1 by default during initialization.
Level specific entries can be manually cached via:
gEnv->pSoundSystem->CacheAudioData("level name or path", 0);
The parameter passed is the stage used when loading Global type entries. As it's ignored on Level Specific type entries we must pass 0 here.
Currently there are 5 types of files caches:
eAFCT_GLOBAL | Pass this to cache a file into the "Globals" group. |
eAFCT_LEVEL_SPECIFIC | Pass this to cache a file into the "Level Specifics" group. |
eAFCT_GAME_HINT | Pass this to cache a file/GameHint into the "Game Hints" group. |
eAFCT_MUSIC | Pass this to cache a file into the "Music" group. This is currently done automatically on consoles if enabled via s_AllowMusicFileCaching. |
eAFCT_FSB_HEADER | This is used rather internally only to identify streamed soundbanks. Whenever a streamed FMOD event starts the first 256KiB of the corresponding streaming soundbank get cached as every newly started stream issues a number of reads to the soundbank's header. This considerably reduces the number of asynchronous read requests to CryENGINE's internal streaming system which is occupied by other systems as well. These type of files will be listed under "Partially Cached". |
Following example shows a Level of Crysis 2 before the AFCM optimization. The color coding helps to define problematic areas as well as keeping an overview.
Left Column:
Center Column:
Right Column:
The numbers in the middle column in front and behind (Game Hints only) indicate the following:
In Front
How often data was actually requested from this soundbank. Gives a good indication of a soundbank's overall workload and whether it makes sense to AFCM cache it or not. These numbers will not be shown on platforms other than PS3 as on those we "preload" that data to FMOD after caching which allows for even faster playback. To disable that and reintroduce the access counter see s_AllowSoundBankPatching.
Behind
This is basically a "Game Hint reference count" which indicates how often this soundbank is currently referenced. For example 3 AI soldier that requested Game Hint "Weapon_AI" 3 times would cause a "[3]" to be displayed behind the files associated with that Game Hint. Once this number reaches 0 again the particular entry turns white, not to be mistaken with a missing entry under the "Not Cached Requests" column to the right. The color white here indicates that this file could get unloaded by the system in case this room is needed.
Also you should know that cutscenes designed in TrackView always issue Game Hint load/unload requests to the AFCM. Load when they start and unload when they finished. For that TrackView sequences prefix "anim_sequence_" to their name and then send that to the AFCM. So let's say you designed a TrackView sequence called "BridgeCollapse" the Game Hint name send to the AFCM will be "anim_sequence_BridgeCollapse". If you designed a Game Hint named like that in the AudioPreloads.xml the corresponding files get loaded and displayed. Keep in mind that for the AFCM all names and paths are converted to lower case internally so the behavior is case insensitive.
Following example shows a Crysis 2 level before AFCM optimization:
After optimization the AFCM should look like following screenshot:
The very top shows current memory budget of the Eventsystem and the Projects being loaded (left column), the size of the FileCacheManager devided into Globals , Level Specifics, Gamehints and Music (centre column) as well as Not Cached Requests (right column).
Similar to s_soundinfo 11 but showing also the Eventgroups of each loaded FMOD projects.
Similar to s_soundinfo 12 but showing every single loaded Event and Eventgroup of the loaded Project. Since the amount of Events exceeds the screen size the list can be dumped using following CVar: s_SoundInfoLogFile = [ ]
Defining the name of the logfile will create a txt file in the root directory showing all registered events.
Here are the CVars that specifically interact with the AFCM:
s_FileCacheManagerSize
Sets the size in MiB the file cache manager will have available. Usage: s_FileCacheManagerSize n ; 0:auto, recommended values PC: 80, PS3: 23, XBox360: 23 Default is 0 <auto>.
s_AudioPreloadsFile
Sets the file name for the AudioFileCacheManager to parse. Usage: s_AudioPreloadsFile <file name without extension> Default: AudioPreloads
s_AllowNotCachedAccess
Controls whether to allow sound load requests that are not cached in the AFCM or not. Usage: s_AllowNotCachedAccess 0/1 0: OFF (not allowing access to not cached data) Default PC: 1, PS3: 1, XBox360:1
s_AllowSoundBankPatching
Controls whether to allow SoundBanks to be FMOD patched or not.
This is only relevant for the PC and XBox360 platform as the PS3 naturally does not patch to FMOD. In more detail it means that data from non-preloaded soundbanks needs to be copied to main memory which currently defaults to 12MiB on the PS3 for example. This is a bad performance impact but allows for somewhat simulating PS3 behavior on the PC and Xbox360 platforms. Additionally it will reintroduce the access counter in front of a soundbank entry to give you an idea about the work load of a soundbank. This in terms helps determining whether it makes sense to preload/cache a soundbank or not.
Usage: s_AllowSoundBankPatching (0/1)
0: OFF (not allowing patching to FMOD)
Default PC: 1, PS3: 0, XBox360: 1
s_AllowMusicFileCaching
Controls whether to allow music files to be AFCM cached or not. This is only relevant for consoles at the moment! Usage: s_AllowMusicFileCaching [0/1] 0: OFF (not allowing caching of music files to the AFCM) Default: 1
s_AllowStreamFileCaching
Controls whether to allow streamed files to be AFCM cached or not. This applies only to the first 256KiB of a streamed soundbank! Usage: s_AllowStreamFileCaching [0/1] 0: OFF (not allowing caching of streamed files' headers to the AFCM) Default: 1
s_FileCacheManagerDebugFilter
Allows for filtered display of the different AFCM entries such as Globals, Level Specifics, Game Hints and so on. Additionally the memory fragmentation can be displayed as well (PS3 only).
Usage: s_FileCacheManagerDebugFilter x(flags can be combined)
Default: 0 (all)
a: Globals
b: Level Specifics
c: Game Hints
d: Partially Cached
e: Music
f: Not Cached
g: Fragmentation
Structuring FMOD Projects
For optimal flexibility and constant overview about the memory budget it is highly recommended to set up the FMOD projects into small efficient logical groups like one project per level, one project for each weapon/vehicle/enemy type, etc.
Also grouping of "global" sounds such as physics, player sounds or particles which will occur throughout the whole Game into separate projects has been proven to work well.
Structuring Levels
Splitting Levels into several soundbanks depending on the Player progress helps to stay in the memory budget. Level Design can help with Transition Areas to safely unload/load Gamehints, place Backgates where the Player can't go into a previous area resulting in completely unloading those elements, controlled usage of Weapons/Vehicles as well as the placement of cinematics and scripted sequences to have memory budget left to support it accordingly.
Structuring Streaming Sounds
A tolerable amount of simultaneously playing audio streams depends very much on the work load of CryENGINE's internal streaming engine. As a guideline the maximum of three streams simultaneously (plus eventually dialog and music) has been proven to be safe. Using just one FMOD event group of sounds as streamed soundbanks such as ambiences helps to keep track.
Loading and Unloading of Gamehints
When loading a Gamehint with certain Soundbanks attached do notice that depending on size and overall streaming engine work load this can take a few milliseconds. Therefore try to find a good spot to load/unload Gamehints. Also avoid playing a Sound from a freshly loaded Gamehint right away (e.g. Area Trigger loads Gamehint and triggers Explosion from that Gamehint in the exact same frame). It is also useful to first unload not used Gamehints before loading new ones.