mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-09 16:05:07 +00:00
IconForge: rust-g GAGS (250x faster edition) (#89590)
## About The Pull Request Offloads GAGS generation to rust-g IconForge. **Key Notes** - The builtin GAGS editor still uses the 'legacy' generation to allow for debugging. - Does not support `color_matrix` layer type, which is currently not used by any GAGS configs. Will do nothing if used. - Does not support `or` blending mode, which is currently not used by any GAGS configs. Will error if used. - Has some 'quirks' compared to BYOND when it comes to mixing icon states with different dir/frame amounts. BYOND will just silently handle these and it's basically undefined behavior because what should you expect BYOND to do? IconForge will spit errors out instead. So this PR also fixes a few of those cases. Functions by writing output to `tmp/gags/gags-[...].dmi`, copying that output into the RSC and assigning the file object to `icon`. Saves ~1.7s init by reducing worst-case GAGS icon generation from 250ms to 1ms. Also optimizes `icon_exists` by using `rustg_dmi_icon_states` for file icons, saving ~60ms. Would have more savings if not for json_decode as well as DMI parsing in rust being somewhat slow. Perhaps having `rustg_dmi_icon_states` share a cache with IconForge could reduce this cost, however I'd still recommend limiting these tests to unit tests (https://github.com/tgstation/dev-cycles-initiative/issues/34), especially for GAGS configs. I'm not sure they're worth 700ms. Saves another ~400ms by replacing `md5asfile` with `rustg_hash_file` in `/datum/greyscale_config/proc/Refresh` Savings are likely even higher when combined with #89478, due to spritesheets sharing a parsed DMI cache with GAGS. This means GAGS will spend less time parsing icons synchronously and can generate output faster. Tracy tests with this combo seem to yield ~2sec savings instead of ~1.7sec Total savings: ~2.16sec to ~2.46sec - Ports https://github.com/BeeStation/BeeStation-Hornet/pull/10455 - Resolves https://github.com/tgstation/dev-cycles-initiative/issues/9 ## Why It's Good For The Game GAGS go zoooom <details> <summary>GAGS Working Ingame</summary>   </details> <details> <summary>GetColoredIconByType</summary>  </details> <details> <summary>icon_exists</summary>  </details> <details> <summary>Refresh</summary>  </details> ## Changelog 🆑 tweak: Optimized GAGS using rust-g IconForge, reducing worst-case generation time to 1ms /🆑
This commit is contained in:
@@ -1195,8 +1195,13 @@ GLOBAL_LIST_EMPTY(transformation_animation_objects)
|
||||
|
||||
if(isnull(icon_states_cache[file]))
|
||||
icon_states_cache[file] = list()
|
||||
for(var/istate in icon_states(file))
|
||||
icon_states_cache[file][istate] = TRUE
|
||||
var/file_string = "[file]"
|
||||
if(length(file_string)) // ensure that it's actually a file, and not a runtime icon
|
||||
for(var/istate in json_decode(rustg_dmi_icon_states(file_string)))
|
||||
icon_states_cache[file][istate] = TRUE
|
||||
else // Otherwise, we have to use the slower BYOND proc
|
||||
for(var/istate in icon_states(file))
|
||||
icon_states_cache[file][istate] = TRUE
|
||||
|
||||
return !isnull(icon_states_cache[file][state])
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/// Disable to use builtin DM-based generation.
|
||||
/// IconForge is 250x times faster but requires storing the icons in tmp/ and may result in higher asset transport.
|
||||
/// Note that the builtin GAGS editor still uses the 'legacy' generation to allow for debugging.
|
||||
/// IconForge also does not support the color matrix layer type or the 'or' blend_mode, however both are currently unused.
|
||||
#define USE_RUSTG_ICONFORGE_GAGS
|
||||
|
||||
PROCESSING_SUBSYSTEM_DEF(greyscale)
|
||||
name = "Greyscale"
|
||||
flags = SS_BACKGROUND
|
||||
@@ -6,6 +12,10 @@ PROCESSING_SUBSYSTEM_DEF(greyscale)
|
||||
|
||||
var/list/datum/greyscale_config/configurations = list()
|
||||
var/list/datum/greyscale_layer/layer_types = list()
|
||||
#ifdef USE_RUSTG_ICONFORGE_GAGS
|
||||
/// Cache containing a list of [UID (config path + colors)] -> [DMI file / RSC object] in the tmp directory from iconforge
|
||||
var/list/gags_cache = list()
|
||||
#endif
|
||||
|
||||
/datum/controller/subsystem/processing/greyscale/Initialize()
|
||||
for(var/datum/greyscale_layer/fake_type as anything in subtypesof(/datum/greyscale_layer))
|
||||
@@ -21,14 +31,35 @@ PROCESSING_SUBSYSTEM_DEF(greyscale)
|
||||
var/datum/greyscale_config/config = configurations[greyscale_type]
|
||||
config.Refresh()
|
||||
|
||||
#ifdef USE_RUSTG_ICONFORGE_GAGS
|
||||
var/list/job_ids = list()
|
||||
#endif
|
||||
|
||||
// This final verification step is for things that need other greyscale configurations to be finished loading
|
||||
for(var/greyscale_type as anything in configurations)
|
||||
CHECK_TICK
|
||||
var/datum/greyscale_config/config = configurations[greyscale_type]
|
||||
config.CrossVerify()
|
||||
#ifdef USE_RUSTG_ICONFORGE_GAGS
|
||||
job_ids += rustg_iconforge_load_gags_config_async(greyscale_type, config.raw_json_string, config.string_icon_file)
|
||||
|
||||
UNTIL(jobs_completed(job_ids))
|
||||
#endif
|
||||
|
||||
return SS_INIT_SUCCESS
|
||||
|
||||
#ifdef USE_RUSTG_ICONFORGE_GAGS
|
||||
/datum/controller/subsystem/processing/greyscale/proc/jobs_completed(list/job_ids)
|
||||
for(var/job in job_ids)
|
||||
var/result = rustg_iconforge_check(job)
|
||||
if(result == RUSTG_JOB_NO_RESULTS_YET)
|
||||
return FALSE
|
||||
if(result != "OK")
|
||||
stack_trace("Error during rustg_iconforge_load_gags_config job: [result]")
|
||||
job_ids -= job
|
||||
return TRUE
|
||||
#endif
|
||||
|
||||
/datum/controller/subsystem/processing/greyscale/proc/RefreshConfigsFromFile()
|
||||
for(var/i in configurations)
|
||||
configurations[i].Refresh(TRUE)
|
||||
@@ -36,12 +67,30 @@ PROCESSING_SUBSYSTEM_DEF(greyscale)
|
||||
/datum/controller/subsystem/processing/greyscale/proc/GetColoredIconByType(type, list/colors)
|
||||
if(!ispath(type, /datum/greyscale_config))
|
||||
CRASH("An invalid greyscale configuration was given to `GetColoredIconByType()`: [type]")
|
||||
if(!initialized)
|
||||
CRASH("GetColoredIconByType() called before greyscale subsystem initialized!")
|
||||
type = "[type]"
|
||||
if(istype(colors)) // It's the color list format
|
||||
colors = colors.Join()
|
||||
else if(!istext(colors))
|
||||
CRASH("Invalid colors were given to `GetColoredIconByType()`: [colors]")
|
||||
#ifdef USE_RUSTG_ICONFORGE_GAGS
|
||||
var/uid = "[replacetext(replacetext(type, "/datum/greyscale_config/", ""), "/", "-")]-[colors]"
|
||||
var/cached_file = gags_cache[uid]
|
||||
if(cached_file)
|
||||
return cached_file
|
||||
var/output_path = "tmp/gags/gags-[uid].dmi"
|
||||
var/iconforge_output = rustg_iconforge_gags(type, colors, output_path)
|
||||
// Handle errors from IconForge
|
||||
if(iconforge_output != "OK")
|
||||
CRASH(iconforge_output)
|
||||
// We'll just explicitly do fcopy_rsc here, so the game doesn't have to do it again later from the cached file.
|
||||
var/rsc_gags_icon = fcopy_rsc(file(output_path))
|
||||
gags_cache[uid] = rsc_gags_icon
|
||||
return rsc_gags_icon
|
||||
#else
|
||||
return configurations[type].Generate(colors)
|
||||
#endif
|
||||
|
||||
/datum/controller/subsystem/processing/greyscale/proc/GetColoredIconByTypeUniversalIcon(type, list/colors, target_icon_state)
|
||||
if(!ispath(type, /datum/greyscale_config))
|
||||
@@ -58,3 +107,5 @@ PROCESSING_SUBSYSTEM_DEF(greyscale)
|
||||
var/list/split_colors = splittext(color_string, "#")
|
||||
for(var/color in 2 to length(split_colors))
|
||||
. += "#[split_colors[color]]"
|
||||
|
||||
#undef USE_RUSTG_ICONFORGE_GAGS
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
/// The md5 file hash for the json configuration. Used to check if the file has changed
|
||||
var/json_config_hash
|
||||
|
||||
/// The raw string contents of the JSON config file.
|
||||
var/raw_json_string
|
||||
|
||||
/// String path to the icon file, used for reloading
|
||||
var/string_icon_file
|
||||
|
||||
@@ -105,13 +108,13 @@
|
||||
var/changed = FALSE
|
||||
|
||||
json_config = file(string_json_config)
|
||||
var/json_hash = md5asfile(json_config)
|
||||
var/json_hash = rustg_hash_file("md5", string_json_config)
|
||||
if(json_config_hash != json_hash)
|
||||
json_config_hash = json_hash
|
||||
changed = TRUE
|
||||
|
||||
icon_file = file(string_icon_file)
|
||||
var/icon_hash = md5asfile(icon_file)
|
||||
var/icon_hash = rustg_hash_file("md5", icon_file)
|
||||
if(icon_file_hash != icon_hash)
|
||||
icon_file_hash = icon_hash
|
||||
changed = TRUE
|
||||
@@ -123,7 +126,8 @@
|
||||
if(!changed)
|
||||
return FALSE
|
||||
|
||||
var/list/raw = json_decode(file2text(json_config))
|
||||
raw_json_string = rustg_file_read(string_json_config)
|
||||
var/list/raw = json_decode(raw_json_string)
|
||||
ReadIconStateConfiguration(raw)
|
||||
|
||||
if(!length(icon_states))
|
||||
|
||||
@@ -136,15 +136,18 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests())
|
||||
var/data_filename = "data/screenshots/[path_prefix]_[name].png"
|
||||
fcopy(icon, data_filename)
|
||||
log_test("\t[path_prefix]_[name] was found, putting in data/screenshots")
|
||||
else if (fexists("code"))
|
||||
// We are probably running in a local build
|
||||
fcopy(icon, filename)
|
||||
TEST_FAIL("Screenshot for [name] did not exist. One has been created.")
|
||||
else
|
||||
// We are probably running in real CI, so just pretend it worked and move on
|
||||
#ifdef CIBUILDING
|
||||
// We are runing in real CI, so just pretend it worked and move on
|
||||
fcopy(icon, "data/screenshots_new/[path_prefix]_[name].png")
|
||||
|
||||
log_test("\t[path_prefix]_[name] was put in data/screenshots_new")
|
||||
#else
|
||||
// We are probably running in a local build
|
||||
fcopy(icon, filename)
|
||||
TEST_FAIL("Screenshot for [name] did not exist. One has been created.")
|
||||
#endif
|
||||
|
||||
|
||||
/// Helper for screenshot tests to take an image of an atom from all directions and insert it into one icon
|
||||
/datum/unit_test/proc/get_flat_icon_for_all_directions(atom/thing, no_anim = TRUE)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 33 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -11,6 +11,7 @@ fi
|
||||
|
||||
mkdir -p \
|
||||
$1/_maps \
|
||||
$1/code/datums/greyscale/json_configs \
|
||||
$1/data/spritesheets \
|
||||
$1/icons \
|
||||
$1/sound/runtime \
|
||||
@@ -25,6 +26,7 @@ fi
|
||||
|
||||
cp tgstation.dmb tgstation.rsc $1/
|
||||
cp -r _maps/* $1/_maps/
|
||||
cp -r code/datums/greyscale/json_configs/* $1/code/datums/greyscale/json_configs/
|
||||
cp -r icons/* $1/icons/
|
||||
cp -r sound/runtime/* $1/sound/runtime/
|
||||
cp -r strings/* $1/strings/
|
||||
|
||||
Reference in New Issue
Block a user