- EditorActorLayer now tracks previews on map with a SpatiallyPartitioned instead of a Dictionary. This allows the copy-paste logic to call an efficient PreviewsInCellRegion method, instead of asking for previews cell-by-cell.
- EditorActorPreview subscribes to the CellEntryChanged methods on the map. Previously the preview was refreshed regardless of which cell changed. Now the preview only regenerates if the preview's footprint has been affected.
- Use error.Message to reports errors, as error.ToString isn't overridden.
- Ensure multiple translation languages are handled correctly, we need to use a list to maintain English being the first one.
For indexed PNGs, we only need to allocate a palette large enough to accommodate the number of indexed colours in the image. For images that don't use all 256 colours, this slightly reduces the memory usage for these images.
When LoadFromManifest is called, trim the various backing collections. These backing collections tend to live a long time without further modifications.
As the SpriteCache is used as a one-shot operation in practise, holding on to the capacity of backing collections is not required. Memory usage can be reduced by allowing the capacity to be reset after the SpriteCache has resolved items.
- Once LoadReservations is called, reset the reservation dictionaries so their backing collections can be reclaimed.
- When ResolveSprites is called, shrink the resolved dictionary as resolutions take place.
In a3d0a50f4d, SpriteCache is updated to sort sprites by height before adding them onto the sheet. This improves packing by reducing wasted space as the sprites are packed onto the sheet. D2kSpriteSequence does not fully benefit from this change, as it creates additional sprites afterwards in the ResolveSprites method. These are not sorted, so they often waste space due to height changes between adjacent sprites and cause an inefficient packing. Sorting them in place is insufficient, as each sequence performs the operation independently. So sets of sprites across different sequences end up with poor packing overall. We need all the sprites to be collected together and sorted in one place for best effect.
We restructure SpriteCache to allow a frame mutation function to be provided when reserving sprites. This removes the need for the ReserveFrames and ResolveFrames methods in SpriteCache. D2kSpriteSequence can use this new function to pass in the required modification, and no longer has to add frames to the sheet builder itself. Now the SpriteCache can apply the desired frame mutations, it can batch together these mutated frames with the other frames and sort them all as a single batch. With all frames sorted together the maximum benefit of this packing approach is realised.
This reduces the number of BGRA sheets required for the d2k mod from 3 to 2.
When sheet builders are adding sprites to a sheet, they work left to right along each row. They reserve height for the highest sprite seen along that row, resetting the height reservation when the row runs out of space and it moves down to the next row.
As the SpriteCache adds the sprites in a giant batch, it can optimise this operation by ordering the sprites by their height. This reduces wastage where shorter sprites don't use the the full height reserved within the row. The reduced wastage can help the sheet builder allocate fewer sheets, improving load times and improving GPU memory usage as less texture memory is required.
The assets for the Tiberian Dawn HD mod are much larger than assets for the default mods, causing a lot of load time to be spent in Util.FastCopyIntoChannel.
We can provide a special case for the SpriteFrameType.Bgra32 format, which is the same format as the destination buffer. In this scenario we can just perform memory copies between the source and destination. Additionally, whilst the default mods require all their assets to get their alpha premultiplied, many of the Tiberian Dawn assets are already premultiplied. Being able to skip this step for these assets saves us having to interpret the bytes into colors and back again.
For the default mods, there isn't a noticeable timing difference. For Tiberian Dawn HD or other mods with modern assets sizes, a large speedup is achieved.
This is a more natural representation than int that allows removal of casts in many places that require uint. Additionally, we can change the internal representation from long to uint, making the Color struct smaller. Since arrays of colors are common, this can save on memory.
- Rename the filename parameter to name and make it mandatory. Review all callers and ensure a useful string is provided as input, to ensure sufficient context is included for logging and debugging. This can be a filename, url, or any arbitrary text so include whatever context seems reasonable.
- When several MiniYamls are created that have similar content, provide a shared string pool. This allows strings that are common between all the yaml to be shared, reducing long term memory usage. We also change the pool from a dictionary to a set. Originally a Dictionary had to be used so we could call TryGetValue to get a reference to the pooled string. Now that more recent versions of dotnet provide a TryGetValue on HashSet, we can use a set directly without the memory wasted by having to store both keys and values in a dictionary.
An empty MiniYaml comment that was indented was previously not recognized, and instead parsed as a key named '#'. Now, indented comments are recognized as comments, which matches the behaviour for unindented lines.
This command allows either unpacking oramap files into folders, or packing folders into oramap files.
Example invocations:
"d2k --unpack-map unpack" to unpack maps of the d2k mod into folders.
"cnc --unpack-map repack" to repack maps of the cnc mod into oramap files (but will only pack folders that were unpacked previously).
The PNG decoder, when dealing when indexed images with a palette, could only decode a bit depth of 8. Teach it to decode depths of 1, 2 and 4 as well. As the palette data is exposed to consumers of the PNG class, unpack the data into a 8 bit depth so consumers don't need to also handle the new bit depths.
This allows the LINQ spelling to be used, but benefits from the performance improvement of the specific methods for these classes that provide the same result.
This rule no longer appears to be buggy, so enforce it. Some of the automated fixes are adjusted in order to improve the result. #pragma directives have no option to control indentation, so remove them where possible.
Previously the StartGameNotification and MusicPlaylist traits used the IWorldLoaded interface to play an audio notification and begin music when the game started. However this interface is used by many traits to perform initial loading whilst the load screen was visible, and this loading can take time. Since the traits could run in any order, then audio notification might fire before another trait with a long loading time. This is not ideal as we want the time between the audio notification occurring and the player being able to interact to be as short and reliable as possible.
Now, we introduce a new IPostWorldLoaded which runs after all other loading activity, and we switch StartGameNotification and MusicPlaylist to use it. This allows timing sensitive traits that want to run right at the end of loading to fire reliably and with minimal delay. The player perception of hearing the notification and being able to interact is now much snappier.
Ensure stale perf history data, to ensure the data is useful and the perf graph widget displays useful information.
- Remove stale data from the previous game when starting a new game. This avoids the graph showing values from the previous game when a new game starts.
- Remove data that was collected during loading. This avoids displaying data points that were collected whilst the loading screen was visible. Data collected whilst loading is not relevant to the in-game performance graph.
The performance graph when starting a new game will now display accurate information from the first tick of the game, whereas previously it displayed some stale information as well.