From 2c0b76f0b8efda6fa77adfde7612e7b6964ca5bc Mon Sep 17 00:00:00 2001 From: oranges Date: Thu, 31 May 2018 12:39:28 +1200 Subject: [PATCH] Refactors how overlays handle the compile step (#38002) Robustin says: The very heart of our overlay code, a single line that basically boiled down to overlays = new_overlays, was the cause of so much overlay lag. Human overlay code was by far the biggest culprit. Most objects have 0-2 overlays but humans are marching around with 20+ most of the time and the current system was spending a LOT of effort comparing 20+ image with 20+ other images and then apparently rendering them all anyway. Human overlays are at least 10x the cost of any other overlay process and on a busy server the overlay compiling was 2x the cost of any other system. I compared the cost of overlay changes by picking up/dropping a PDA in the dorms 250 times, with a 50% chance to use our current overlay compiler and a 50% chance to use a "direct addition/removal (+=, -=) approach: CURRENT 1120ms 133 (avg:8.4210529327392578) SCRAPS 6ms 112 (avg:0.0535714291036129) Now this PR makes our whole overlay subsystem use that approach for overlay compiling and the early results look incredible. The best part is this is just the START of improvements. Humans benefits the most because their icon system was already designed for small, incremental overlay updates. By moving other code from "Cut everything, then put it all back" to only updating the necessary overlays (cough, APC's), we can see similar improvements. oranges says: I've picked up this PR because robustin vanished, but I do see the value in the approach, only things I added were the checks for the overlay list len changing I also retabbed the entire file because I am a brainlet and did it without thinking --- code/__DEFINES/subsystems.dm | 24 +++--- code/controllers/subsystem/overlays.dm | 83 ++++++++++--------- code/game/atoms.dm | 3 +- code/game/objects/effects/contraband.dm | 2 +- .../food_and_drinks/food/customizables.dm | 4 +- .../food_and_drinks/food/snacks_pastry.dm | 4 +- .../mob/living/carbon/human/species.dm | 1 - .../mob/living/carbon/human/update_icons.dm | 4 +- 8 files changed, 61 insertions(+), 64 deletions(-) diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 1e7f40b0d4e6..8c8d22bb3e30 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -124,21 +124,19 @@ #define COMPILE_OVERLAYS(A)\ if (TRUE) {\ - var/list/oo = A.our_overlays;\ + var/list/ad = A.add_overlays;\ + var/list/rm = A.remove_overlays;\ var/list/po = A.priority_overlays;\ + if(LAZYLEN(rm)){\ + A.overlays -= rm;\ + rm.Cut();\ + }\ + if(LAZYLEN(ad)){\ + A.overlays |= ad;\ + ad.Cut();\ + }\ if(LAZYLEN(po)){\ - if(LAZYLEN(oo)){\ - A.overlays = oo + po;\ - }\ - else{\ - A.overlays = po;\ - }\ - }\ - else if(LAZYLEN(oo)){\ - A.overlays = oo;\ - }\ - else{\ - A.overlays.Cut();\ + A.overlays |= po;\ }\ A.flags_1 &= ~OVERLAY_QUEUED_1;\ } diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm index 2f3b6c9668f3..1a7bd43fd2c7 100644 --- a/code/controllers/subsystem/overlays.dm +++ b/code/controllers/subsystem/overlays.dm @@ -115,38 +115,43 @@ SUBSYSTEM_DEF(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(priority = FALSE) - var/list/cached_overlays = our_overlays - var/list/cached_priority = priority_overlays + LAZYINITLIST(priority_overlays) + LAZYINITLIST(remove_overlays) + LAZYINITLIST(add_overlays) + remove_overlays = overlays.Copy() + add_overlays.Cut() - var/need_compile = FALSE + if(priority) + priority_overlays.Cut() - if(LAZYLEN(cached_overlays)) //don't queue empty lists, don't cut priority overlays - cached_overlays.Cut() //clear regular overlays - need_compile = TRUE - - if(priority && LAZYLEN(cached_priority)) - cached_priority.Cut() - need_compile = TRUE - - if(NOT_QUEUED_ALREADY && need_compile) + //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, priority) if(!overlays) return - overlays = build_appearance_list(overlays) + LAZYINITLIST(add_overlays) //always initialized after this point + LAZYINITLIST(priority_overlays) + LAZYINITLIST(remove_overlays) + var/a_len = add_overlays.len + var/r_len = remove_overlays.len + var/p_len = priority_overlays.len + remove_overlays += overlays + add_overlays -= overlays - var/list/cached_overlays = our_overlays //sanic - var/list/cached_priority = priority_overlays - var/init_o_len = LAZYLEN(cached_overlays) - var/init_p_len = LAZYLEN(cached_priority) //starter pokemon - LAZYREMOVE(cached_overlays, overlays) if(priority) + var/list/cached_priority = priority_overlays LAZYREMOVE(cached_priority, overlays) - if(NOT_QUEUED_ALREADY && ((init_o_len != LAZYLEN(cached_overlays)) || (init_p_len != LAZYLEN(cached_priority)))) + var/fa_len = add_overlays.len + var/fr_len = remove_overlays.len + var/fp_len = priority_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 || fp_len != p_len)) QUEUE_FOR_COMPILE /atom/proc/add_overlay(list/overlays, priority = FALSE) @@ -155,24 +160,21 @@ SUBSYSTEM_DEF(overlays) overlays = build_appearance_list(overlays) - LAZYINITLIST(our_overlays) //always initialized after this point + LAZYINITLIST(add_overlays) //always initialized after this point LAZYINITLIST(priority_overlays) - - var/list/cached_overlays = our_overlays //sanic - var/list/cached_priority = priority_overlays - var/init_o_len = cached_overlays.len - var/init_p_len = cached_priority.len //starter pokemon - var/need_compile + var/a_len = add_overlays.len + var/p_len = priority_overlays.len if(priority) - cached_priority += overlays //or in the image. Can we use [image] = image? - need_compile = init_p_len != cached_priority.len + priority_overlays += overlays //or in the image. Can we use [image] = image? + var/fp_len = priority_overlays.len + if(NOT_QUEUED_ALREADY && fp_len != p_len) + QUEUE_FOR_COMPILE else - cached_overlays += overlays - need_compile = init_o_len != cached_overlays.len - - if(NOT_QUEUED_ALREADY && need_compile) //have we caught more pokemon? - QUEUE_FOR_COMPILE + add_overlays += overlays + var/fa_len = add_overlays.len + if(NOT_QUEUED_ALREADY && fa_len != a_len) + QUEUE_FOR_COMPILE /atom/proc/copy_overlays(atom/other, cut_old) //copys our_overlays from another atom if(!other) @@ -180,12 +182,11 @@ SUBSYSTEM_DEF(overlays) cut_overlays() return - var/list/cached_other = other.our_overlays + var/list/cached_other = other.overlays.Copy() if(cached_other) - if(cut_old || !LAZYLEN(our_overlays)) - our_overlays = cached_other.Copy() - else - our_overlays |= 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) @@ -196,7 +197,7 @@ SUBSYSTEM_DEF(overlays) //TODO: Better solution for these? /image/proc/add_overlay(x) - overlays += x + overlays |= x /image/proc/cut_overlay(x) overlays -= x @@ -210,10 +211,10 @@ SUBSYSTEM_DEF(overlays) cut_overlays() return - var/list/cached_other = other.our_overlays + var/list/cached_other = other.overlays.Copy() if(cached_other) if(cut_old || !overlays.len) - overlays = cached_other.Copy() + overlays = cached_other else overlays |= cached_other else if(cut_old) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 122efe5772d1..9a51b9ba5a00 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -19,8 +19,9 @@ var/list/atom_colours //used to store the different colors on an atom //its inherent color, the colored paint applied on it, special color effect etc... - var/list/our_overlays //our local copy of (non-priority) overlays without byond magic. Use procs in SSoverlays to manipulate var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. + var/list/remove_overlays // a very temporary list of overlays to remove + var/list/add_overlays // a very temporary list of overlays to add var/datum/proximity_monitor/proximity_monitor var/buckle_message_cooldown = 0 diff --git a/code/game/objects/effects/contraband.dm b/code/game/objects/effects/contraband.dm index 31ca7dd3073d..dbfb57c80832 100644 --- a/code/game/objects/effects/contraband.dm +++ b/code/game/objects/effects/contraband.dm @@ -127,7 +127,7 @@ // Deny placing posters on currently-diagonal walls, although the wall may change in the future. if (smooth & SMOOTH_DIAGONAL) - for (var/O in our_overlays) + for (var/O in overlays) var/image/I = O if (copytext(I.icon_state, 1, 3) == "d-") return diff --git a/code/modules/food_and_drinks/food/customizables.dm b/code/modules/food_and_drinks/food/customizables.dm index 353bfc6538c8..6dd9669c4ecb 100644 --- a/code/modules/food_and_drinks/food/customizables.dm +++ b/code/modules/food_and_drinks/food/customizables.dm @@ -118,8 +118,8 @@ if(INGREDIENTS_STACKPLUSTOP) filling.pixel_x = rand(-1,1) filling.pixel_y = 2 * ingredients.len - 1 - if(our_overlays) - our_overlays.Cut(ingredients.len) //???, add overlay calls later in this proc will queue the compile if necessary + if(overlays) + overlays -= overlays[ingredients.len] var/mutable_appearance/TOP = mutable_appearance(icon, "[icon_state]_top") TOP.pixel_y = 2 * ingredients.len + 3 add_overlay(filling) diff --git a/code/modules/food_and_drinks/food/snacks_pastry.dm b/code/modules/food_and_drinks/food/snacks_pastry.dm index 01105cf6d37c..80aa588bae3a 100644 --- a/code/modules/food_and_drinks/food/snacks_pastry.dm +++ b/code/modules/food_and_drinks/food/snacks_pastry.dm @@ -406,8 +406,8 @@ name = "stack of pancakes" else name = initial(name) - if(contents.len < LAZYLEN(our_overlays)) - cut_overlay(our_overlays[our_overlays.len]) + if(contents.len < LAZYLEN(overlays)) + overlays-=overlays[overlays.len] /obj/item/reagent_containers/food/snacks/pancakes/examine(mob/user) var/ingredients_listed = "" diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 5bc1e70cb2eb..eec7337694b9 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -297,7 +297,6 @@ GLOBAL_LIST_EMPTY(roundstart_races) /datum/species/proc/handle_hair(mob/living/carbon/human/H, forced_colour) H.remove_overlay(HAIR_LAYER) - var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD) if(!HD) //Decapitated return diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 63ab63ed5ff8..320faec63276 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -567,9 +567,7 @@ generate/load female uniform sprites matching all previously decided variables if(4) //even = right hands return list("x" = 0, "y" = 16) else //No offsets or Unwritten number of hands - return list("x" = 0, "y" = 0) - - + return list("x" = 0, "y" = 0)//Handle held offsets //produces a key based on the human's limbs /mob/living/carbon/human/generate_icon_render_key()