Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust-g IconForge: Lightning fast spritesheet generation #10404

Merged
merged 38 commits into from
Jan 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
887a003
Initial testing
itsmeow Dec 20, 2023
a5bafab
Rustg icon generator
itsmeow Dec 21, 2023
899ed14
this thing.. fast....
itsmeow Dec 23, 2023
bafa00b
Fix
itsmeow Dec 23, 2023
c0615f3
Finish porting spritesheets and add GAGS support
itsmeow Dec 23, 2023
026d0f5
Remove 'moving' attribute
itsmeow Dec 25, 2023
5321962
Refactor the DM component
itsmeow Dec 26, 2023
4413ea3
Update rustg DM and add cleanup call
itsmeow Dec 26, 2023
f60a705
vendor_icon_preview -> icon_state_preview
itsmeow Dec 26, 2023
5dceaca
Revert tracy change
itsmeow Dec 26, 2023
f59000c
Re-add GAGS to design spritesheet
itsmeow Dec 26, 2023
b33a063
Fix crafting spritesheet size
itsmeow Dec 26, 2023
969834e
Correct webroot args
itsmeow Dec 26, 2023
ead71c1
Adds caching
itsmeow Dec 27, 2023
51f040e
Reset rustg
itsmeow Dec 27, 2023
2078b1a
Move cleanup step where it belongs
itsmeow Dec 27, 2023
0d989c9
Add crosscompiled rustg dll
itsmeow Dec 27, 2023
8e8730b
Log cache invalidations
itsmeow Dec 27, 2023
de746ef
Fix player panel and orbit spritesheets
itsmeow Dec 27, 2023
6df6fe6
Fix reading legacy sheets from cache
itsmeow Dec 28, 2023
cd80e28
Enables local caching of legacy assets, adds Regenerate Asset Cache d…
itsmeow Dec 28, 2023
c7f3329
Fix universal_icon creation being slow
itsmeow Dec 28, 2023
9ecada2
Protect against cache deletion
itsmeow Dec 29, 2023
f661c88
Fix hand icons not displaying properly
itsmeow Dec 29, 2023
5e48eb6
Remove this very incorrect assumption
itsmeow Dec 29, 2023
87705ef
Restore rust_g to master
itsmeow Dec 29, 2023
2f46fdc
Update defs
itsmeow Dec 29, 2023
a12ed71
Add rustg version checker to smart cache
itsmeow Dec 29, 2023
a6ec368
Move legacy cache to data/spritesheets so it persists on prod
itsmeow Dec 29, 2023
0d7bd3b
Update config comment to match
itsmeow Dec 29, 2023
c91d736
Copy all icons so CI works
itsmeow Dec 29, 2023
2e22800
Try fix CI again
itsmeow Dec 29, 2023
19de46e
Fix chat and designs not displaying properly when loaded from cache
itsmeow Dec 31, 2023
0c904c7
Merge remote-tracking branch 'origin/master' into asset-rustg
itsmeow Dec 31, 2023
24b3ed3
Fix mistake
itsmeow Dec 31, 2023
d211ffa
Fix double encoding
itsmeow Dec 31, 2023
d440ae4
Inline uni_icon, saving unnecessary proc-call overhead
itsmeow Jan 8, 2024
639fc4f
Create a unit test for the smart cache
itsmeow Jan 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions beestation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "code\__DEFINES\areas.dm"
#include "code\__DEFINES\armor.dm"
#include "code\__DEFINES\art.dm"
#include "code\__DEFINES\assets.dm"
#include "code\__DEFINES\async.dm"
#include "code\__DEFINES\atmospherics.dm"
#include "code\__DEFINES\atom_hud.dm"
Expand Down Expand Up @@ -391,6 +392,7 @@
#include "code\controllers\subsystem\area_contents.dm"
#include "code\controllers\subsystem\asset_loading.dm"
#include "code\controllers\subsystem\assets.dm"
#include "code\controllers\subsystem\async_map_generator.dm"
#include "code\controllers\subsystem\atoms.dm"
#include "code\controllers\subsystem\augury.dm"
#include "code\controllers\subsystem\autotransfer.dm"
Expand Down Expand Up @@ -418,7 +420,6 @@
#include "code\controllers\subsystem\language.dm"
#include "code\controllers\subsystem\lighting.dm"
#include "code\controllers\subsystem\machines.dm"
#include "code\controllers\subsystem\async_map_generator.dm"
#include "code\controllers\subsystem\mapping.dm"
#include "code\controllers\subsystem\materials.dm"
#include "code\controllers\subsystem\metrics.dm"
Expand Down Expand Up @@ -1431,8 +1432,8 @@
#include "code\game\objects\items\food\pizza.dm"
#include "code\game\objects\items\food\salad.dm"
#include "code\game\objects\items\food\sandwichtoast.dm"
#include "code\game\objects\items\food\spaghetti.dm"
#include "code\game\objects\items\food\soup.dm"
#include "code\game\objects\items\food\spaghetti.dm"
#include "code\game\objects\items\food\sweets.dm"
#include "code\game\objects\items\grenades\_grenade.dm"
#include "code\game\objects\items\grenades\antigravity.dm"
Expand Down Expand Up @@ -2133,6 +2134,8 @@
#include "code\modules\asset_cache\asset_cache_item.dm"
#include "code\modules\asset_cache\asset_list.dm"
#include "code\modules\asset_cache\asset_list_items.dm"
#include "code\modules\asset_cache\spritesheet\batched\batched_spritesheet.dm"
#include "code\modules\asset_cache\spritesheet\batched\universal_icon.dm"
#include "code\modules\asset_cache\transports\asset_transport.dm"
#include "code\modules\asset_cache\transports\webroot_transport.dm"
#include "code\modules\atmospherics\auxgm\breathing_classes.dm"
Expand Down
16 changes: 16 additions & 0 deletions code/__DEFINES/assets.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#define ASSET_CROSS_ROUND_CACHE_DIRECTORY "data/spritesheets/legacy_cache"
#define ASSET_CROSS_ROUND_SMART_CACHE_DIRECTORY "data/spritesheets/smart_cache"

/// When sending mutiple assets, how many before we give the client a quaint little sending resources message
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8

/// How many assets can be sent at once during legacy asset transport
#define SLOW_ASSET_SEND_RATE 6

/// Constructs a universal icon. This is done in the same manner as the icon() BYOND proc.
/// "color" will not do anything if a transform is provided. Blend it yourself or use color_transform().
/// Do note that transforms are NOT COPIED, and are internally lists. So take care not to re-use transforms.
/// This is a DEFINE for performance reasons.
/// Parameters (in order):
/// icon_file, icon_state, dir, frame, transform, color
#define uni_icon(I, icon_state, rest...) new /datum/universal_icon(I, icon_state, ##rest)
15 changes: 15 additions & 0 deletions code/__HELPERS/_lists.dm
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,21 @@
.[i] = key
.[key] = value

/// A version of deep_copy_list that actually supports associative list nesting: list(list(list("a" = "b"))) will actually copy correctly.
/proc/deep_copy_list_alt(list/inserted_list)
if(!islist(inserted_list))
return inserted_list
var/copied_list = inserted_list.Copy()
. = copied_list
for(var/key_or_value in inserted_list)
if(isnum_safe(key_or_value) || !inserted_list[key_or_value])
continue
var/value = inserted_list[key_or_value]
var/new_value = value
if(islist(value))
new_value = deep_copy_list_alt(value)
copied_list[key_or_value] = new_value

/**
takes an input_key, as text, and the list of keys already used, outputting a replacement key in the format of "[input_key] ([number_of_duplicates])" if it finds a duplicate

Expand Down
1 change: 1 addition & 0 deletions code/__HELPERS/files.dm
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ GLOBAL_VAR_INIT(fileaccess_timer, 0)

/// Save file as an external file then md5 it.
/// Used because md5ing files stored in the rsc sometimes gives incorrect md5 results.
/// https://www.byond.com/forum/post/2611357
/proc/md5asfile(file)
var/static/notch = 0
// its importaint this code can handle md5filepath sleeping instead of hard blocking, if it's converted to use rust_g.
Expand Down
2 changes: 2 additions & 0 deletions code/_compile_options.dm
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@
#endif // 1 to use the default behaviour;
// 2 for preloading absolutely everything;

//#define LOWMEMORYMODE
#ifdef LOWMEMORYMODE
#warn WARNING: Compiling with LOWMEMORYMODE.
#define FORCE_MAP "runtimestation"
#endif

Expand Down
2 changes: 2 additions & 0 deletions code/controllers/configuration/entries/resources.dm
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@
return ..(str_var)

/datum/config_entry/flag/cache_assets

/datum/config_entry/flag/smart_cache_assets
6 changes: 6 additions & 0 deletions code/controllers/subsystem/asset_loading.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ SUBSYSTEM_DEF(asset_loading)
flags = SS_NO_INIT
runlevels = RUNLEVEL_LOBBY|RUNLEVELS_DEFAULT
var/list/datum/asset/generate_queue = list()
var/last_queue_len = 0

/datum/controller/subsystem/asset_loading/fire(resumed)
while(length(generate_queue))
Expand All @@ -16,7 +17,12 @@ SUBSYSTEM_DEF(asset_loading)

if(MC_TICK_CHECK)
return
last_queue_len = length(generate_queue)
generate_queue.len--
// We just emptied the queue
if(last_queue_len && !length(generate_queue))
// Clean up cached icons, freeing memory.
rustg_iconforge_cleanup()

/datum/controller/subsystem/asset_loading/proc/queue_asset(datum/asset/queue)
#ifdef DO_NOT_DEFER_ASSETS
Expand Down
10 changes: 10 additions & 0 deletions code/controllers/subsystem/processing/greyscale.dm
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ PROCESSING_SUBSYSTEM_DEF(greyscale)
CRASH("Invalid colors were given to `GetColoredIconByType()`: [colors]")
return configurations[type].Generate(colors)

/datum/controller/subsystem/processing/greyscale/proc/GetColoredIconEntryByType(type, list/colors, target_icon_state)
if(!ispath(type, /datum/greyscale_config))
CRASH("An invalid greyscale configuration was given to `GetColoredIconEntryByType()`: [type]")
type = "[type]"
if(istype(colors)) // It's the color list format
colors = colors.Join()
else if(!istext(colors))
CRASH("Invalid colors were given to `GetColoredIconEntryByType()`: [colors]")
return configurations[type].Generate_entry(colors, target_icon_state)

/datum/controller/subsystem/processing/greyscale/proc/ParseColorString(color_string)
. = list()
var/list/split_colors = splittext(color_string, "#")
Expand Down
3 changes: 3 additions & 0 deletions code/datums/browser.dm
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
if (istype(name, /datum/asset/spritesheet))
var/datum/asset/spritesheet/sheet = name
stylesheets["spritesheet_[sheet.name].css"] = "data/spritesheets/[sheet.name]"
else if (istype(name, /datum/asset/spritesheet_batched))
var/datum/asset/spritesheet_batched/sheet = name
stylesheets["spritesheet_[sheet.name].css"] = "data/spritesheets/[sheet.name]"
else
var/asset_name = "[name].css"

Expand Down
2 changes: 1 addition & 1 deletion code/datums/components/crafting/crafting.dm
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@

/datum/component/personal_crafting/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet/crafting),
get_asset_datum(/datum/asset/spritesheet_batched/crafting),
)

/datum/component/personal_crafting/proc/check_tools(atom/a, datum/crafting_recipe/R, list/contents)
Expand Down
34 changes: 34 additions & 0 deletions code/datums/greyscale/_greyscale_config.dm
Original file line number Diff line number Diff line change
Expand Up @@ -303,4 +303,38 @@
output["icon"] = GenerateBundle(colors, debug_steps)
return output

/datum/greyscale_config/proc/Generate_entry(color_string, target_bundle_state, datum/universal_icon/last_external_icon)
return GenerateBundle_entry(color_string, target_bundle_state, last_external_icon=last_external_icon)

/// Handles the actual icon manipulation to create the spritesheet
/datum/greyscale_config/proc/GenerateBundle_entry(list/colors, target_bundle_state, datum/universal_icon/last_external_icon)
if(!istype(colors))
colors = SSgreyscale.ParseColorString(colors)
if(length(colors) != expected_colors)
CRASH("[DebugName()] expected [expected_colors] color arguments but received [length(colors)]")

if(!(target_bundle_state in icon_states))
CRASH("Invalid target bundle icon_state \"[target_bundle_state]\"! Valid icon_states: [icon_states.Join(", ")]")

var/datum/universal_icon/icon_bundle = GenerateLayerGroup_entry(colors, icon_states[target_bundle_state], last_external_icon) || uni_icon('icons/effects/effects.dmi', "nothing")
icon_bundle.scale(width, height)
return icon_bundle

/// Internal recursive proc to handle nested layer groups
/datum/greyscale_config/proc/GenerateLayerGroup_entry(list/colors, list/group, datum/universal_icon/last_external_icon)
var/datum/universal_icon/new_icon
for(var/datum/greyscale_layer/layer as anything in group)
var/datum/universal_icon/layer_icon
if(islist(layer))
layer_icon = GenerateLayerGroup_entry(colors, layer, new_icon || last_external_icon)
layer = layer[1] // When there are multiple layers in a group like this we use the first one's blend mode
else
layer_icon = layer.Generate_entry(colors, new_icon || last_external_icon)

if(!new_icon)
new_icon = layer_icon
else
new_icon.blend_icon(layer_icon, layer.blend_mode)
return new_icon

#undef MAX_SANE_LAYERS
32 changes: 32 additions & 0 deletions code/datums/greyscale/layer.dm
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,27 @@
var/icon/copy_of_new_icon = icon(new_icon) // Layers shouldn't be modifying it directly, this is just for them to reference
return InternalGenerate(processed_colors, render_steps, copy_of_new_icon)

/// Used to actualy create the layer using the given colors
/// Do not override, use InternalGenerate instead
/datum/greyscale_layer/proc/Generate_entry(list/colors, datum/universal_icon/new_icon)
var/list/processed_colors = list()
for(var/i in color_ids)
if(isnum(i))
processed_colors += colors[i]
else
processed_colors += i
var/datum/universal_icon/copy_of_new_icon = isnull(new_icon) ? uni_icon('icons/effects/effects.dmi', "nothing") : new_icon.copy() // Layers shouldn't be modifying it directly, this is just for them to reference
return InternalGenerate_entry(processed_colors, copy_of_new_icon)

/// Override this to implement layers.
/// The colors var will only contain colors that this layer is configured to use.
/datum/greyscale_layer/proc/InternalGenerate(list/colors, list/render_steps, icon/new_icon)

/// Override this to implement layers.
/// The colors var will only contain colors that this layer is configured to use.
/datum/greyscale_layer/proc/InternalGenerate_entry(list/colors, datum/universal_icon/new_icon)
return new_icon

////////////////////////////////////////////////////////
// Subtypes

Expand All @@ -88,10 +105,12 @@
layer_type = "icon_state"
var/icon_state
var/icon/icon
var/icon_file
var/color_id

/datum/greyscale_layer/icon_state/Initialize(icon_file)
. = ..()
src.icon_file = icon_file
var/list/icon_states = icon_states(icon_file)
if(!(icon_state in icon_states))
CRASH("Configured icon state \[[icon_state]\] was not found in [icon_file]. Double check your json configuration.")
Expand All @@ -111,6 +130,13 @@
generated_icon.Blend(colors[1], ICON_MULTIPLY)
return generated_icon

/datum/greyscale_layer/icon_state/InternalGenerate_entry(list/colors, datum/universal_icon/new_icon)
. = ..()
var/datum/universal_icon/generated_icon = uni_icon(icon_file, icon_state)
if(length(colors))
generated_icon.blend_color(colors[1], ICON_MULTIPLY)
return generated_icon

/// A layer to modify the previous layer's colors with a color matrix
/datum/greyscale_layer/color_matrix
layer_type = "color_matrix"
Expand Down Expand Up @@ -154,3 +180,9 @@
else
generated_icon = reference_type.Generate(colors.Join(), new_icon)
return icon(generated_icon, icon_state)

/datum/greyscale_layer/reference/InternalGenerate_entry(list/colors, datum/universal_icon/new_icon)
var/datum/universal_icon/generated_icon = reference_type.Generate_entry(colors.Join(), new_icon)
generated_icon = generated_icon.copy()
generated_icon.icon_state = icon_state
return generated_icon
2 changes: 0 additions & 2 deletions code/game/atoms.dm
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,6 @@
///Used for changing icon states for different base sprites.
var/base_icon_state

///Used to show a specific icon state in certain situations - i.e.) crafting menu
var/icon_state_preview
// This veriable exists BECAUSE animating sprite (bola) has an issue to render to TGUI crafting window - it shows wrong icons.

///LazyList of all balloon alerts currently on this atom
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/telecomms/computers/message.dm
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@

/obj/machinery/computer/message_monitor/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet/chat),
get_asset_datum(/datum/asset/spritesheet_batched/chat),
)

/obj/machinery/computer/message_monitor/ui_static_data(mob/user)
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items.dm
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
var/canMouseDown = FALSE

///Icons used to show the item in vendors instead of the item's actual icon, drawn from the item's icon file (just chemical.dm for now)
var/vendor_icon_preview = null
var/icon_state_preview = null


/obj/item/Initialize(mapload)
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/RPD.dm
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ GLOBAL_LIST_INIT(fluid_duct_recipes, list(

/obj/item/pipe_dispenser/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet/pipes),
get_asset_datum(/datum/asset/spritesheet_batched/pipes),
)


Expand Down
Loading
Loading