Removes overlay queuing, saves 6/7 seconds of initialize. Lightly modifies stat tracking macros (#69696)

* Removes overlay queuing, saves 6/7 seconds of initialize. Lightly modifies stat tracking macros

So we have this overlay queuing system right? It's build with the assumption
that the "add to overlay list" operation is real expensive, and is
thus useful to queue removals or additions.

It turns out that it just isn't, at least during init. In my testing the
operation of queuing took LONGER then the actual overlay add/remove did.

That's ignoring the cost of the subsystem's work.

I've also modified part of the stat tracking macro, since it took a good
bit of cpu time, and didn't seem to well, do anything. So far as I can
tell it always evaluates to 1
This commit is contained in:
LemonInTheDark
2022-09-26 08:46:46 -07:00
committed by GitHub
parent 7e8f08a842
commit 72a5b79555
22 changed files with 95 additions and 191 deletions
+17 -19
View File
@@ -24,42 +24,40 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define CONDUCT_1 (1<<1)
/// For machines and structures that should not break into parts, eg, holodeck stuff
#define NODECONSTRUCT_1 (1<<2)
/// atom queued to SSoverlay
#define OVERLAY_QUEUED_1 (1<<3)
/// item has priority to check when entering or leaving
#define ON_BORDER_1 (1<<4)
#define ON_BORDER_1 (1<<3)
///Whether or not this atom shows screentips when hovered over
#define NO_SCREENTIPS_1 (1<<5)
#define NO_SCREENTIPS_1 (1<<4)
/// Prevent clicking things below it on the same turf eg. doors/ fulltile windows
#define PREVENT_CLICK_UNDER_1 (1<<6)
#define PREVENT_CLICK_UNDER_1 (1<<5)
///specifies that this atom is a hologram that isnt real
#define HOLOGRAM_1 (1<<7)
#define HOLOGRAM_1 (1<<6)
///Whether /atom/Initialize() has already run for the object
#define INITIALIZED_1 (1<<8)
#define INITIALIZED_1 (1<<7)
/// was this spawned by an admin? used for stat tracking stuff.
#define ADMIN_SPAWNED_1 (1<<9)
#define ADMIN_SPAWNED_1 (1<<8)
/// should not get harmed if this gets caught by an explosion?
#define PREVENT_CONTENTS_EXPLOSION_1 (1<<10)
#define PREVENT_CONTENTS_EXPLOSION_1 (1<<9)
/// Should this object be paintable with very dark colors?
#define ALLOW_DARK_PAINTS_1 (1<<11)
#define ALLOW_DARK_PAINTS_1 (1<<10)
/// Should this object be unpaintable?
#define UNPAINTABLE_1 (1<<12)
#define UNPAINTABLE_1 (1<<11)
/// Is the thing currently spinning?
#define IS_SPINNING_1 (1<<13)
#define IS_SPINNING_1 (1<<12)
/// Is this atom on top of another atom, and as such has click priority?
#define IS_ONTOP_1 (1<<14)
#define IS_ONTOP_1 (1<<13)
/// Is this atom immune to being dusted by the supermatter?
#define SUPERMATTER_IGNORES_1 (1<<15)
#define SUPERMATTER_IGNORES_1 (1<<14)
/// If a turf can be made dirty at roundstart. This is also used in areas.
#define CAN_BE_DIRTY_1 (1<<16)
#define CAN_BE_DIRTY_1 (1<<15)
/// Should we use the initial icon for display? Mostly used by overlay only objects
#define HTML_USE_INITAL_ICON_1 (1<<17)
#define HTML_USE_INITAL_ICON_1 (1<<16)
/// Can players recolor this in-game via vendors (and maybe more if support is added)?
#define IS_PLAYER_COLORABLE_1 (1<<18)
#define IS_PLAYER_COLORABLE_1 (1<<17)
/// Whether or not this atom has contextual screentips when hovered OVER
#define HAS_CONTEXTUAL_SCREENTIPS_1 (1<<19)
#define HAS_CONTEXTUAL_SCREENTIPS_1 (1<<18)
// Whether or not this atom is storing contents for a disassociated storage object
#define HAS_DISASSOCIATED_STORAGE_1 (1<<20)
#define HAS_DISASSOCIATED_STORAGE_1 (1<<19)
// Update flags for [/atom/proc/update_appearance]
/// Update the atom's name
+3 -4
View File
@@ -8,12 +8,11 @@
#define STAT_LOG_ENTRY(entrylist, entryname) \
var/list/STAT_ENTRY = entrylist[entryname] || (entrylist[entryname] = new /list(STAT_ENTRY_LENGTH));\
STAT_ENTRY[STAT_ENTRY_TIME] += STAT_TIME;\
var/STAT_INCR_AMOUNT = min(1, 2**round((STAT_ENTRY[STAT_ENTRY_COUNT] || 0)/SHORT_REAL_LIMIT));\
if (STAT_INCR_AMOUNT == 1 || prob(100/STAT_INCR_AMOUNT)) {\
STAT_ENTRY[STAT_ENTRY_COUNT] += STAT_INCR_AMOUNT;\
};\
STAT_ENTRY[STAT_ENTRY_COUNT] += 1;
// Cost tracking macros, to be used in one proc
// The static lists are under the assumption that costs and counting are global lists, and will therefor
// Break during world init
#define INIT_COST(costs, counting) \
var/list/_costs = costs; \
var/list/_counting = counting; \
+14 -20
View File
@@ -255,28 +255,22 @@
//! ## Overlays subsystem
///Compile all the overlays for an atom from the cache lists
// |= on overlays is not actually guaranteed to not add same appearances but we're optimistically using it anyway.
#define COMPILE_OVERLAYS(A)\
do {\
var/list/ad = A.add_overlays;\
var/list/rm = A.remove_overlays;\
if(LAZYLEN(rm)){\
A.overlays -= rm;\
rm.Cut();\
}\
if(LAZYLEN(ad)){\
A.overlays |= ad;\
ad.Cut();\
}\
for(var/I in A.alternate_appearances){\
var/datum/atom_hud/alternate_appearance/AA = A.alternate_appearances[I];\
#define POST_OVERLAY_CHANGE(changed_on) \
if(length(changed_on.overlays) >= MAX_ATOM_OVERLAYS) { \
var/text_lays = overlays2text(changed_on.overlays); \
stack_trace("Too many overlays on [changed_on.type] - [length(changed_on.overlays)], refusing to update and cutting.\
\n What follows is a printout of all existing overlays at the time of the overflow \n[text_lays]"); \
changed_on.overlays.Cut(); \
changed_on.add_overlay(mutable_appearance('icons/testing/greyscale_error.dmi')); \
} \
if(alternate_appearances) { \
for(var/I in changed_on.alternate_appearances){\
var/datum/atom_hud/alternate_appearance/AA = changed_on.alternate_appearances[I];\
if(AA.transfer_overlays){\
AA.copy_overlays(A, TRUE);\
AA.copy_overlays(changed_on, TRUE);\
}\
}\
A.flags_1 &= ~OVERLAY_QUEUED_1;\
} while (FALSE)
} \
}
/**
Create a new timer and add it to the queue.
-2
View File
@@ -1069,7 +1069,6 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
body.equipOutfit(outfit, TRUE)
var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing")
COMPILE_OVERLAYS(body)
for(var/D in showDirs)
var/icon/partial = getFlatIcon(body, defdir=D)
out_icon.Insert(partial,dir=D)
@@ -1099,7 +1098,6 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
var/initial_human_dir = existing_human.dir
existing_human.dir = SOUTH
var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing")
COMPILE_OVERLAYS(existing_human)
for(var/direction in directions_to_output)
var/icon/partial = getFlatIcon(existing_human, defdir = direction)
out_icon.Insert(partial, dir = direction)
-1
View File
@@ -126,7 +126,6 @@ DEFINE_BITFIELD(flags_1, list(
"NODECONSTRUCT_1" = NODECONSTRUCT_1,
"NO_SCREENTIPS_1" = NO_SCREENTIPS_1,
"ON_BORDER_1" = ON_BORDER_1,
"OVERLAY_QUEUED_1" = OVERLAY_QUEUED_1,
"PREVENT_CLICK_UNDER_1" = PREVENT_CLICK_UNDER_1,
"PREVENT_CONTENTS_EXPLOSION_1" = PREVENT_CONTENTS_EXPLOSION_1,
"SUPERMATTER_IGNORES_1" = SUPERMATTER_IGNORES_1,
+43 -123
View File
@@ -1,71 +1,16 @@
SUBSYSTEM_DEF(overlays)
name = "Overlay"
flags = SS_TICKER
wait = 1
priority = FIRE_PRIORITY_OVERLAYS
init_order = INIT_ORDER_OVERLAY
var/list/queue
flags = SS_NO_FIRE|SS_NO_INIT
var/list/stats
/datum/controller/subsystem/overlays/PreInit()
queue = list()
stats = list()
/datum/controller/subsystem/overlays/Initialize()
initialized = TRUE
fire(mc_check = FALSE)
return SS_INIT_SUCCESS
/datum/controller/subsystem/overlays/stat_entry(msg)
msg = "Ov:[length(queue)]"
return ..()
/datum/controller/subsystem/overlays/Shutdown()
text2file(render_stats(stats), "[GLOB.log_directory]/overlay.log")
/datum/controller/subsystem/overlays/Recover()
queue = SSoverlays.queue
/datum/controller/subsystem/overlays/fire(resumed = FALSE, mc_check = TRUE)
var/list/queue = src.queue
var/static/count = 0
if (count)
var/c = count
count = 0 //so if we runtime on the Cut, we don't try again.
queue.Cut(1,c+1)
for (var/atom/atom_to_compile as anything in queue)
count++
if(!atom_to_compile)
continue
STAT_START_STOPWATCH
COMPILE_OVERLAYS(atom_to_compile)
UNSETEMPTY(atom_to_compile.add_overlays)
UNSETEMPTY(atom_to_compile.remove_overlays)
STAT_STOP_STOPWATCH
STAT_LOG_ENTRY(stats, atom_to_compile.type)
if(length(atom_to_compile.overlays) >= MAX_ATOM_OVERLAYS)
//Break it real GOOD
var/text_lays = overlays2text(atom_to_compile.overlays)
stack_trace("Too many overlays on [atom_to_compile.type] - [length(atom_to_compile.overlays)], refusing to update and cutting.\
\n What follows is a printout of all existing overlays at the time of the overflow \n[text_lays]")
atom_to_compile.overlays.Cut()
//Let them know they fucked up
atom_to_compile.add_overlay(mutable_appearance('icons/testing/greyscale_error.dmi'))
continue
if(mc_check)
if(MC_TICK_CHECK)
break
else
CHECK_TICK
if (count)
queue.Cut(1,count+1)
count = 0
stats = SSoverlays.stats
/// Converts an overlay list into text for debug printing
/// Of note: overlays aren't actually mutable appearances, they're just appearances
@@ -92,13 +37,12 @@ SUBSYSTEM_DEF(overlays)
iconbro.icon = icon
return iconbro.appearance
/atom/proc/build_appearance_list(old_overlays)
var/static/image/appearance_bro = new()
var/list/new_overlays = list()
if (!islist(old_overlays))
old_overlays = list(old_overlays)
for (var/overlay in old_overlays)
/atom/proc/build_appearance_list(build_overlays)
if (!islist(build_overlays))
build_overlays = list(build_overlays)
for (var/overlay in build_overlays)
if(!overlay)
build_overlays -= overlay
continue
if (istext(overlay))
#ifdef UNIT_TESTS
@@ -109,64 +53,37 @@ SUBSYSTEM_DEF(overlays)
stack_trace("Invalid overlay: Icon object '[icon_file]' [REF(icon)] used in '[src]' [type] is missing icon state [overlay].")
continue
#endif
new_overlays += iconstate2appearance(icon, overlay)
build_overlays -= overlay
build_overlays += iconstate2appearance(icon, overlay)
else if(isicon(overlay))
new_overlays += icon2appearance(overlay)
else
if(isloc(overlay))
var/atom/A = overlay
if (A.flags_1 & OVERLAY_QUEUED_1)
COMPILE_OVERLAYS(A)
appearance_bro.appearance = overlay //this works for images and atoms too!
if(!ispath(overlay))
var/image/I = overlay
appearance_bro.dir = I.dir
new_overlays += appearance_bro.appearance
return new_overlays
build_overlays -= overlay
build_overlays += icon2appearance(overlay)
return build_overlays
#define NOT_QUEUED_ALREADY (!(flags_1 & OVERLAY_QUEUED_1))
#define QUEUE_FOR_COMPILE flags_1 |= OVERLAY_QUEUED_1; SSoverlays.queue += src;
/atom/proc/cut_overlays()
LAZYINITLIST(remove_overlays)
remove_overlays = overlays.Copy()
add_overlays = null
STAT_START_STOPWATCH
overlays = null
POST_OVERLAY_CHANGE(src)
STAT_STOP_STOPWATCH
STAT_LOG_ENTRY(SSoverlays.stats, type)
//If not already queued for work and there are overlays to remove
if(NOT_QUEUED_ALREADY && remove_overlays.len)
QUEUE_FOR_COMPILE
/atom/proc/cut_overlay(list/overlays)
/atom/proc/cut_overlay(list/remove_overlays)
if(!overlays)
return
overlays = build_appearance_list(overlays)
LAZYINITLIST(add_overlays)
LAZYINITLIST(remove_overlays)
var/a_len = add_overlays.len
var/r_len = remove_overlays.len
remove_overlays += overlays
add_overlays -= overlays
STAT_START_STOPWATCH
overlays -= build_appearance_list(remove_overlays)
POST_OVERLAY_CHANGE(src)
STAT_STOP_STOPWATCH
STAT_LOG_ENTRY(SSoverlays.stats, type)
var/fa_len = add_overlays.len
var/fr_len = remove_overlays.len
//If not already queued and there is work to be done
if(NOT_QUEUED_ALREADY && (fa_len != a_len || fr_len != r_len ))
QUEUE_FOR_COMPILE
UNSETEMPTY(add_overlays)
/atom/proc/add_overlay(list/overlays)
/atom/proc/add_overlay(list/add_overlays)
if(!overlays)
return
overlays = build_appearance_list(overlays)
LAZYINITLIST(add_overlays) //always initialized after this point
var/a_len = add_overlays.len
add_overlays += overlays
var/fa_len = add_overlays.len
if(NOT_QUEUED_ALREADY && fa_len != a_len)
QUEUE_FOR_COMPILE
STAT_START_STOPWATCH
overlays += build_appearance_list(add_overlays)
POST_OVERLAY_CHANGE(src)
STAT_STOP_STOPWATCH
STAT_LOG_ENTRY(SSoverlays.stats, type)
/atom/proc/copy_overlays(atom/other, cut_old) //copys our_overlays from another atom
if(!other)
@@ -174,18 +91,21 @@ SUBSYSTEM_DEF(overlays)
cut_overlays()
return
STAT_START_STOPWATCH
var/list/cached_other = other.overlays.Copy()
if(cached_other)
if(cut_old || !LAZYLEN(overlays))
remove_overlays = overlays
add_overlays = cached_other
if(NOT_QUEUED_ALREADY)
QUEUE_FOR_COMPILE
else if(cut_old)
cut_overlays()
#undef NOT_QUEUED_ALREADY
#undef QUEUE_FOR_COMPILE
if(cut_old)
if(cached_other)
overlays = cached_other
else
overlays = null
POST_OVERLAY_CHANGE(src)
STAT_STOP_STOPWATCH
STAT_LOG_ENTRY(SSoverlays.stats, type)
else if(cached_other)
overlays += cached_other
POST_OVERLAY_CHANGE(src)
STAT_STOP_STOPWATCH
STAT_LOG_ENTRY(SSoverlays.stats, type)
//TODO: Better solution for these?
/image/proc/add_overlay(x)
-2
View File
@@ -268,8 +268,6 @@ GLOBAL_DATUM_INIT(data_core, /datum/datacore, new)
var/static/record_id_num = 1001
var/id = num2hex(record_id_num++,6)
// We need to compile the overlays now, otherwise we're basically copying an empty icon.
COMPILE_OVERLAYS(H)
var/mutable_appearance/character_appearance = new(H.appearance)
//These records should ~really~ be merged or something
-1
View File
@@ -322,7 +322,6 @@
if(outfit_type)
mannequin.equipOutfit(outfit_type,TRUE)
mannequin.setDir(SOUTH)
COMPILE_OVERLAYS(mannequin)
. = image(mannequin)
unset_busy_human_dummy("HOLODISK_PRESET")
+1 -7
View File
@@ -126,32 +126,26 @@
if(machine_stat & NOPOWER)
return
. += "fire_overlay"
. += mutable_appearance(icon, "fire_overlay")
if(is_station_level(z))
. += "fire_[SSsecurity_level.get_current_level_as_number()]"
. += mutable_appearance(icon, "fire_[SSsecurity_level.get_current_level_as_number()]")
. += emissive_appearance(icon, "fire_[SSsecurity_level.get_current_level_as_number()]", alpha = src.alpha)
else
. += "fire_[SEC_LEVEL_GREEN]"
. += mutable_appearance(icon, "fire_[SEC_LEVEL_GREEN]")
. += emissive_appearance(icon, "fire_[SEC_LEVEL_GREEN]", alpha = src.alpha)
if(!(my_area?.fire || LAZYLEN(my_area?.active_firelocks)))
if(my_area?.fire_detect) //If this is false, leave the green light missing. A good hint to anyone paying attention.
. += "fire_off"
. += mutable_appearance(icon, "fire_off")
. += emissive_appearance(icon, "fire_off", alpha = src.alpha)
else if(obj_flags & EMAGGED)
. += "fire_emagged"
. += mutable_appearance(icon, "fire_emagged")
. += emissive_appearance(icon, "fire_emagged", alpha = src.alpha)
else
. += "fire_on"
. += mutable_appearance(icon, "fire_on")
. += emissive_appearance(icon, "fire_on", alpha = src.alpha)
if(!panel_open && my_area?.fire_detect && my_area?.fire) //It just looks horrible with the panel open
. += "fire_detected"
. += mutable_appearance(icon, "fire_detected")
. += emissive_appearance(icon, "fire_detected", alpha = src.alpha) //Pain
+10
View File
@@ -44,3 +44,13 @@
CRASH("Turf decal initialized in an object/nullspace")
T.AddElement(/datum/element/decal, icon, icon_state, dir, null, null, alpha, color, null, FALSE, null)
return INITIALIZE_HINT_QDEL
#ifdef UNIT_TESTS
// If we don't do this, turf decals will end up stacking up on a tile, and break the overlay limit
// I hate it too bestie
/obj/effect/turf_decal/Destroy()
if(GLOB.running_create_and_destroy)
var/turf/T = loc
T.RemoveElement(/datum/element/decal, icon, icon_state, dir, null, null, alpha, color, null, FALSE, null)
return ..()
#endif
@@ -132,7 +132,6 @@
. = ..()
var/mutable_appearance/arm = mutable_appearance(icon = icon, icon_state = "borg_beaker_apparatus_arm")
if(stored)
COMPILE_OVERLAYS(stored)
stored.pixel_x = 0
stored.pixel_y = 0
var/mutable_appearance/stored_copy = new /mutable_appearance(stored)
@@ -209,7 +208,6 @@
icon_state = null // hides the original icon (otherwise it's drawn underneath)
var/mutable_appearance/bag
if(stored)
COMPILE_OVERLAYS(stored)
var/mutable_appearance/stored_organ = new /mutable_appearance(stored)
stored_organ.layer = FLOAT_LAYER
stored_organ.plane = FLOAT_PLANE
@@ -247,7 +245,6 @@
. = ..()
var/mutable_appearance/arm = mutable_appearance(icon, "borg_hardware_apparatus_arm1")
if(stored)
COMPILE_OVERLAYS(stored)
stored.pixel_x = -3
stored.pixel_y = 0
if(!istype(stored, /obj/item/circuitboard))
@@ -94,7 +94,6 @@
cached_flat_icon = null
if(!front_id)
return
COMPILE_OVERLAYS(front_id)
. += mutable_appearance(front_id.icon, front_id.icon_state)
. += front_id.overlays
. += mutable_appearance(icon, "wallet_overlay")
-1
View File
@@ -46,7 +46,6 @@
equipAntagOnDummy(mannequin, ert)
COMPILE_OVERLAYS(mannequin)
CHECK_TICK
var/icon/preview_icon = icon('icons/effects/effects.dmi', "nothing")
preview_icon.Scale(48+32, 16+32)
-1
View File
@@ -309,7 +309,6 @@ GLOBAL_VAR_INIT(say_disabled, FALSE)
qdel(I)
randomize_human(D)
D.dress_up_as_job(JB, TRUE)
COMPILE_OVERLAYS(D)
var/icon/I = icon(getFlatIcon(D), frame = 1)
final.Insert(I, JB.title)
qdel(D)
@@ -350,7 +350,6 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/render_preview_outfit(datum/outfit/outfit, mob/living/carbon/human/dummy)
dummy = dummy || new /mob/living/carbon/human/dummy/consistent
dummy.equipOutfit(outfit, visualsOnly = TRUE)
COMPILE_OVERLAYS(dummy)
var/icon = getFlatIcon(dummy)
// We don't want to qdel the dummy right away, since its items haven't initialized yet.
@@ -21,7 +21,6 @@
dummy.set_species(species_type)
dummy.equipOutfit(/datum/outfit/job/assistant/consistent, visualsOnly = TRUE)
dummy.dna.species.prepare_human_for_preview(dummy)
COMPILE_OVERLAYS(dummy)
var/icon/dummy_icon = getFlatIcon(dummy)
dummy_icon.Scale(64, 64)
@@ -17,6 +17,8 @@ GLOBAL_LIST_EMPTY(lifts)
canSmoothWith = list(SMOOTH_GROUP_INDUSTRIAL_LIFT)
obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN
appearance_flags = PIXEL_SCALE|KEEP_TOGETHER //no TILE_BOUND since we're potentially multitile
// If we don't do this, we'll build our overlays early, and fuck up how we're rendered
blocks_emissive = 0
///ID used to determine what lift types we can merge with
var/lift_id = BASIC_LIFT_ID
@@ -246,6 +248,8 @@ GLOBAL_LIST_EMPTY(lifts)
forceMove(locate(min_x, min_y, z))//move to the lower left corner
set_movement_registrations(locs - old_loc)
blocks_emissive = EMISSIVE_BLOCK_GENERIC
update_appearance()
return TRUE
///returns an unordered list of all lift platforms adjacent to us. used so our lift_master_datum can control all connected platforms.
@@ -105,5 +105,4 @@
mannequin.job = preview_job.title
mannequin.dress_up_as_job(preview_job, TRUE)
COMPILE_OVERLAYS(mannequin)
return mannequin.appearance
@@ -39,7 +39,6 @@
var/mob/living/carbon/human/dummy/dummy = new
dummy.equipOutfit(outfit)
dummy.set_species(/datum/species/zombie)
COMPILE_OVERLAYS(dummy)
icon = getFlatIcon(dummy)
qdel(dummy)
@@ -3,6 +3,7 @@
//You absolutely must run last
priority = TEST_DEL_WORLD
GLOBAL_VAR_INIT(running_create_and_destroy, FALSE)
/datum/unit_test/create_and_destroy/Run()
//We'll spawn everything here
var/turf/spawn_at = run_loc_floor_bottom_left
@@ -101,6 +102,7 @@
var/list/cached_contents = spawn_at.contents.Copy()
var/baseturf_count = length(spawn_at.baseturfs)
GLOB.running_create_and_destroy = TRUE
for(var/type_path in typesof(/atom/movable, /turf) - ignore) //No areas please
if(ispath(type_path, /turf))
spawn_at.ChangeTurf(type_path, /turf/baseturf_skipover)
@@ -125,6 +127,7 @@
for(var/atom/to_kill in to_del)
qdel(to_kill)
GLOB.running_create_and_destroy = FALSE
//Hell code, we're bound to have ended the round somehow so let's stop if from ending while we work
SSticker.delay_end = TRUE
//Prevent the garbage subsystem from harddeling anything, if only to save time
@@ -29,7 +29,6 @@
/datum/unit_test/screenshot_humanoids/proc/get_flat_icon_for_all_directions(atom/thing)
var/icon/output = icon('icons/effects/effects.dmi', "nothing")
COMPILE_OVERLAYS(thing)
for (var/direction in GLOB.cardinals)
var/icon/partial = getFlatIcon(thing, defdir = direction, no_anim = TRUE)
@@ -14,7 +14,6 @@
/datum/unit_test/screenshot_saturnx/proc/get_flat_icon_for_all_directions(atom/thing)
var/icon/output = icon('icons/effects/effects.dmi', "nothing")
COMPILE_OVERLAYS(thing)
for (var/direction in GLOB.cardinals)
var/icon/partial = getFlatIcon(thing, defdir = direction)