From f8bcaf4864350ee94eb951cf199e80eb5f7383d0 Mon Sep 17 00:00:00 2001 From: Lohikar Date: Sun, 27 Aug 2017 17:06:17 -0500 Subject: [PATCH] Lighting Performance Tweaks & Fixes (#3325) changes: SSlighting is now FIFO - lights are processed in the order they are received instead of by whatever one was queued most recently. Instant lighting updates now check CURRENT_TICKLIMIT instead of ITL. ITL has been removed due to it no longer being used. SSlighting will no longer double-process lighting datums that have already been processed by instant lighting. Instant/Intelligent lighting updates can now be disabled via. compile-time define. Sunlight should no longer shine indoors. Directional lighting now properly updates on direction changes again. /datum/light_source/novis has been renamed to /datum/light_source/sunlight. More lighting microoptimizations. Converted two storage lists to be lazy. --- aurorastation.dme | 2 +- code/__defines/lighting.dm | 3 + code/controllers/subsystems/lighting.dm | 95 +++++++++++++------ code/controllers/subsystems/sunlight.dm | 4 - code/game/area/Space Station 13 areas.dm | 6 -- .../objects/items/weapons/storage/bags.dm | 4 +- .../objects/items/weapons/storage/storage.dm | 42 ++++---- code/modules/lighting/lighting_atom.dm | 2 +- code/modules/lighting/lighting_source.dm | 26 +++-- ...e_novis.dm => lighting_source_sunlight.dm} | 37 ++++---- 10 files changed, 131 insertions(+), 90 deletions(-) rename code/modules/lighting/{lighting_source_novis.dm => lighting_source_sunlight.dm} (84%) diff --git a/aurorastation.dme b/aurorastation.dme index 3e3ba139a9c..56bb6607a9b 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -1366,7 +1366,7 @@ #include "code\modules\lighting\lighting_overlay.dm" #include "code\modules\lighting\lighting_profiler.dm" #include "code\modules\lighting\lighting_source.dm" -#include "code\modules\lighting\lighting_source_novis.dm" +#include "code\modules\lighting\lighting_source_sunlight.dm" #include "code\modules\lighting\lighting_turf.dm" #include "code\modules\lighting\lighting_verbs.dm" #include "code\modules\lighting\~lighting_undefs.dm" diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index a24ef9762f5..69f88c805fe 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -12,6 +12,9 @@ #define LIGHTING_SOFT_THRESHOLD 0.001 // If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting overlays. #define LIGHTING_BLOCKED_FACTOR 0.5 // How much the range of a directional light will be reduced while facing a wall. +// If defined, instant updates will be used whenever server load permits. Otherwise queued updates are always used. +#define USE_INTELLIGENT_LIGHTING_UPDATES + // If I were you I'd leave this alone. #define LIGHTING_BASE_MATRIX \ list \ diff --git a/code/controllers/subsystems/lighting.dm b/code/controllers/subsystems/lighting.dm index 358a16ed033..c9f46643779 100644 --- a/code/controllers/subsystems/lighting.dm +++ b/code/controllers/subsystems/lighting.dm @@ -13,17 +13,20 @@ var/datum/controller/subsystem/lighting/SSlighting var/list/lighting_corners // List of all lighting corners in the world. var/list/light_queue = list() // lighting sources queued for update. + var/lq_idex = 1 var/list/corner_queue = list() // lighting corners queued for update. + var/cq_idex = 1 var/list/overlay_queue = list() // lighting overlays queued for update. + var/oq_idex = 1 var/tmp/processed_lights = 0 var/tmp/processed_corners = 0 var/tmp/processed_overlays = 0 +#ifdef USE_INTELLIGENT_LIGHTING_UPDATES var/force_queued = TRUE var/force_override = FALSE // For admins. - - var/instant_tick_limit = 80 // Tick limit used by instant lighting updates. If world.tick_usage is higher than this when a light updates, it will be updated via. SSlighting. +#endif /datum/controller/subsystem/lighting/New() NEW_SS_GLOBAL(SSlighting) @@ -31,10 +34,14 @@ var/datum/controller/subsystem/lighting/SSlighting LAZYINITLIST(lighting_overlays) /datum/controller/subsystem/lighting/stat_entry() - var/out = "O:[lighting_overlays.len] C:[lighting_corners.len] ITL:[round(instant_tick_limit, 0.1)]%\n" - out += "\tP:{L:[light_queue.len]|C:[corner_queue.len]|O:[overlay_queue.len]}\n" - out += "\tL:{L:[processed_lights]|C:[processed_corners]|O:[processed_overlays]}\n" - ..(out) + var/list/out = list( + "O:[lighting_overlays.len] C:[lighting_corners.len]\n", + "\tP:{L:[light_queue.len - (lq_idex - 1)]|C:[corner_queue.len - (cq_idex - 1)]|O:[overlay_queue.len - (oq_idex - 1)]}\n", + "\tL:{L:[processed_lights]|C:[processed_corners]|O:[processed_overlays]}\n" + ) + ..(out.Join()) + +#ifdef USE_INTELLIGENT_LIGHTING_UPDATES /datum/controller/subsystem/lighting/ExplosionStart() force_queued = TRUE @@ -43,9 +50,12 @@ var/datum/controller/subsystem/lighting/SSlighting if (!force_override) force_queued = FALSE + /datum/controller/subsystem/lighting/proc/handle_roundstart() force_queued = FALSE +#endif + /datum/controller/subsystem/lighting/Initialize(timeofday) var/overlaycount = 0 var/starttime = REALTIMEOFDAY @@ -80,7 +90,9 @@ var/datum/controller/subsystem/lighting/SSlighting log_ss("lighting", "NOv:[overlaycount] L:[processed_lights] C:[processed_corners] O:[processed_overlays]") +#ifdef USE_INTELLIGENT_LIGHTING_UPDATES SSticker.OnRoundstart(CALLBACK(src, .proc/handle_roundstart)) +#endif ..() @@ -89,8 +101,6 @@ var/datum/controller/subsystem/lighting/SSlighting processed_lights = 0 processed_corners = 0 processed_overlays = 0 - - instant_tick_limit = CURRENT_TICKLIMIT * 0.8 MC_SPLIT_TICK_INIT(3) if (!no_mc_tick) @@ -100,59 +110,88 @@ var/datum/controller/subsystem/lighting/SSlighting var/list/curr_corners = corner_queue var/list/curr_overlays = overlay_queue - while (curr_lights.len) - var/datum/light_source/L = curr_lights[curr_lights.len] - curr_lights.len-- + while (lq_idex <= curr_lights.len) + var/datum/light_source/L = curr_lights[lq_idex++] - L.update_corners() + if (L.needs_update != LIGHTING_NO_UPDATE) + L.update_corners() - L.needs_update = LIGHTING_NO_UPDATE + L.needs_update = LIGHTING_NO_UPDATE - processed_lights++ + processed_lights++ if (no_mc_tick) CHECK_TICK else if (MC_TICK_CHECK) break + if (lq_idex > 1) + curr_lights.Cut(1, lq_idex) + lq_idex = 1 + if (!no_mc_tick) MC_SPLIT_TICK - while (curr_corners.len) - var/datum/lighting_corner/C = curr_corners[curr_corners.len] - curr_corners.len-- + while (cq_idex <= curr_corners.len) + var/datum/lighting_corner/C = curr_corners[cq_idex++] - C.update_overlays() + if (C.needs_update) + C.update_overlays() - C.needs_update = FALSE + C.needs_update = FALSE - processed_corners++ + processed_corners++ if (no_mc_tick) CHECK_TICK else if (MC_TICK_CHECK) break + if (cq_idex > 1) + curr_corners.Cut(1, cq_idex) + cq_idex = 1 + if (!no_mc_tick) MC_SPLIT_TICK - while (curr_overlays.len) - var/atom/movable/lighting_overlay/O = curr_overlays[curr_overlays.len] - curr_overlays.len-- + while (oq_idex <= curr_overlays.len) + var/atom/movable/lighting_overlay/O = curr_overlays[oq_idex++] - O.update_overlay() - O.needs_update = FALSE + if (O.needs_update) + O.update_overlay() + O.needs_update = FALSE - processed_overlays++ + processed_overlays++ if (no_mc_tick) CHECK_TICK else if (MC_TICK_CHECK) break + if (oq_idex > 1) + curr_overlays.Cut(1, oq_idex) + oq_idex = 1 + /datum/controller/subsystem/lighting/Recover() + lighting_corners = SSlighting.lighting_corners + lighting_overlays = SSlighting.lighting_overlays + src.light_queue = SSlighting.light_queue src.corner_queue = SSlighting.corner_queue src.overlay_queue = SSlighting.overlay_queue - lighting_corners = SSlighting.lighting_corners - lighting_overlays = SSlighting.lighting_overlays + + lq_idex = SSlighting.lq_idex + cq_idex = SSlighting.cq_idex + oq_idex = SSlighting.oq_idex + + if (lq_idex > 1) + light_queue.Cut(1, lq_idex) + lq_idex = 1 + + if (cq_idex > 1) + corner_queue.Cut(1, cq_idex) + cq_idex = 1 + + if (oq_idex > 1) + overlay_queue.Cut(1, oq_idex) + oq_idex = 1 diff --git a/code/controllers/subsystems/sunlight.dm b/code/controllers/subsystems/sunlight.dm index 4e833639387..8b6f6d3b63c 100644 --- a/code/controllers/subsystems/sunlight.dm +++ b/code/controllers/subsystems/sunlight.dm @@ -41,16 +41,12 @@ /datum/controller/subsystem/sunlight/proc/set_overall_light(...) . = 0 - SSlighting.force_queued = TRUE for (var/thing in light_points) var/atom/movable/AM = thing AM.set_light(arglist(args)) .++ CHECK_TICK - if (!SSlighting.force_override) - SSlighting.force_queued = FALSE - /datum/controller/subsystem/sunlight/proc/apply_sun_state(datum/sun_state/S) log_debug("sunlight: Applying preset [S].") set_overall_light(config.sun_accuracy * 1.2, 1, S.color) diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index b659e180cf5..36673be7d9b 100755 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -429,17 +429,14 @@ area/space/atmosalert() /area/syndicate_station/above name = "\improper Above the Station" icon_state = "northwest" - station_area = 1 /area/syndicate_station/under name = "\improper Under the Station" icon_state = "northeast" - station_area = 1 /area/syndicate_station/caverns name = "\improper Caverns" icon_state = "southeast" - station_area = 1 base_turf = /turf/simulated/floor/asteroid /area/syndicate_station/arrivals_dock @@ -488,17 +485,14 @@ area/space/atmosalert() /area/skipjack_station/above name = "\improper Above the Station" icon_state = "northwest" - station_area = 1 /area/skipjack_station/under name = "\improper Under the Station" icon_state = "northeast" - station_area = 1 /area/skipjack_station/cavern name = "\improper Caverns" icon_state = "southeast" - station_area = 1 base_turf = /turf/simulated/floor/asteroid //////////////////// diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm index bce0d2c663e..c8ded0d3a24 100644 --- a/code/game/objects/items/weapons/storage/bags.dm +++ b/code/game/objects/items/weapons/storage/bags.dm @@ -35,7 +35,7 @@ w_class = 4 max_w_class = 2 - can_hold = list() // any + can_hold = null // any cant_hold = list(/obj/item/weapon/disk/nuclear) /obj/item/weapon/storage/bag/trash/update_icon() @@ -81,7 +81,7 @@ w_class = 4 max_w_class = 2 - can_hold = list() // any + can_hold = null // any cant_hold = list(/obj/item/weapon/disk/nuclear) // ----------------------------- diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index 0e3d4dceb2f..479b081b082 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -16,25 +16,25 @@ name = "storage" icon = 'icons/obj/storage.dmi' w_class = 3 - var/list/can_hold = list() //List of objects which this item can store (if set, it can't store anything else) - var/list/cant_hold = list() //List of objects which this item can't store (in effect only if can_hold isn't set) + var/list/can_hold //List of objects which this item can store (if set, it can't store anything else) + var/list/cant_hold //List of objects which this item can't store (in effect only if can_hold isn't set) var/list/is_seeing //List of mobs which are currently seeing the contents of this item's storage var/max_w_class = 3 //Max size of objects that this object can store (in effect only if can_hold isn't set) var/max_storage_space = 8 //The sum of the storage costs of all the items in this storage item. - var/storage_slots = null //The number of storage slots in this container. - var/obj/screen/storage/boxes = null - var/obj/screen/storage/storage_start = null //storage UI - var/obj/screen/storage/storage_continue = null - var/obj/screen/storage/storage_end = null - var/obj/screen/storage/stored_start = null - var/obj/screen/storage/stored_continue = null - var/obj/screen/storage/stored_end = null - var/obj/screen/close/closer = null + var/storage_slots //The number of storage slots in this container. + var/obj/screen/storage/boxes + var/obj/screen/storage/storage_start //storage UI + var/obj/screen/storage/storage_continue + var/obj/screen/storage/storage_end + var/obj/screen/storage/stored_start + var/obj/screen/storage/stored_continue + var/obj/screen/storage/stored_end + var/obj/screen/close/closer var/use_to_pickup //Set this to make it possible to use this item in an inverse way, so you can have the item in your hand and click items on the floor to pick them up. var/display_contents_with_number //Set this to make the storage item group contents of the same type and display them as a number. var/allow_quick_empty //Set this variable to allow the object to have the 'empty' verb, which dumps all the contents on the floor. var/allow_quick_gather //Set this variable to allow the object to have the 'toggle mode' verb, which quickly collects all items from a tile. - var/collection_mode = 1; //0 = pick one at a time, 1 = pick all on tile + var/collection_mode = 1 //0 = pick one at a time, 1 = pick all on tile var/use_sound = "rustle" //sound played when used. null for no sound. /obj/item/weapon/storage/Destroy() @@ -88,18 +88,14 @@ /obj/item/weapon/storage/proc/return_inv() - - var/list/L = list( ) - - L += src.contents + . = contents.Copy() for(var/obj/item/weapon/storage/S in src) - L += S.return_inv() + . += S.return_inv() for(var/obj/item/weapon/gift/G in src) - L += G.gift + . += G.gift if (istype(G.gift, /obj/item/weapon/storage)) - L += G.gift:return_inv() - return L + . += G.gift:return_inv() /obj/item/weapon/storage/proc/show_to(mob/user as mob) if(user.s_active != src) @@ -320,7 +316,7 @@ if(W.anchored) return 0 - if(can_hold.len) + if(LAZYLEN(can_hold)) if(!is_type_in_list(W, can_hold)) if(!stop_messages && ! istype(W, /obj/item/weapon/hand_labeler)) usr << "[src] cannot hold \the [W]." @@ -331,7 +327,7 @@ usr << "[src] has no more space specifically for \the [W]." return 0 - if(cant_hold.len && is_type_in_list(W, cant_hold)) + if(LAZYLEN(cant_hold) && is_type_in_list(W, cant_hold)) if(!stop_messages) usr << "[src] cannot hold [W]." return 0 @@ -589,7 +585,7 @@ /obj/item/weapon/storage/proc/make_exact_fit() storage_slots = contents.len - can_hold.Cut() + can_hold = list() max_w_class = 0 max_storage_space = 0 for(var/obj/item/I in src) diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index ce81f5e2d55..7ec8b71d177 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -73,7 +73,7 @@ if (light) // Update the light or create it if it does not exist. light.update(.) else if (light_novis) - light = new/datum/light_source/novis(src, .) + light = new/datum/light_source/sunlight(src, .) else light = new/datum/light_source(src, .) diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index 2fbf0a6b89a..f00eac2dab1 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -85,6 +85,7 @@ if (!force) return QDEL_HINT_IWILLGC +#ifdef USE_INTELLIGENT_LIGHTING_UPDATES // Picks either scheduled or instant updates based on current server load. #define INTELLIGENT_UPDATE(level) \ var/_should_update = needs_update == LIGHTING_NO_UPDATE; \ @@ -92,7 +93,7 @@ needs_update = level; \ } \ if (_should_update) { \ - if (world.tick_usage > SSlighting.instant_tick_limit || SSlighting.force_queued) { \ + if (world.tick_usage > CURRENT_TICKLIMIT || SSlighting.force_queued) { \ SSlighting.light_queue += src; \ } \ else { \ @@ -100,6 +101,13 @@ needs_update = LIGHTING_NO_UPDATE; \ } \ } +#else +#define INTELLIGENT_UPDATE(level) \ + if (needs_update == LIGHTING_NO_UPDATE) \ + SSlighting.light_queue += src; \ + if (needs_update < level) \ + needs_update = level; +#endif // This proc will cause the light source to update the top atom, and add itself to the update queue. /datum/light_source/proc/update(atom/new_top_atom) @@ -154,15 +162,18 @@ /datum/light_source/proc/update_angle() var/turf/T = get_turf(top_atom) + var/ndir if (istype(top_atom, /mob) && top_atom:facing_dir) - old_direction = top_atom:facing_dir + ndir = top_atom:facing_dir else - old_direction = top_atom.dir + ndir = top_atom.dir // Don't do anything if nothing is different, trig ain't free. - if (T.x == cached_origin_x && T.y == cached_origin_y && old_direction == top_atom.dir) + if (T.x == cached_origin_x && T.y == cached_origin_y && old_direction == ndir) return + old_direction = ndir + var/turf/front = get_step(T, old_direction) facing_opaque = (front && front.has_opaque_atom) @@ -344,9 +355,10 @@ Tcorners[i] = new /datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[i]) if (!T.has_opaque_atom) - for (thing in Tcorners) - C = thing - corners[C] = 0 + corners[Tcorners[1]] = 0 + corners[Tcorners[2]] = 0 + corners[Tcorners[3]] = 0 + corners[Tcorners[4]] = 0 turfs += T diff --git a/code/modules/lighting/lighting_source_novis.dm b/code/modules/lighting/lighting_source_sunlight.dm similarity index 84% rename from code/modules/lighting/lighting_source_novis.dm rename to code/modules/lighting/lighting_source_sunlight.dm index a6a191b2fe6..b0c6d2252bb 100644 --- a/code/modules/lighting/lighting_source_novis.dm +++ b/code/modules/lighting/lighting_source_sunlight.dm @@ -1,6 +1,6 @@ -/datum/light_source/novis +/datum/light_source/sunlight -/datum/light_source/novis/update_corners() +/datum/light_source/sunlight/update_corners() var/update = FALSE if (QDELETED(source_atom)) @@ -63,11 +63,14 @@ var/list/Tcorners var/Sx = source_turf.x var/Sy = source_turf.y - var/actual_range = light_range // novis sources don't support directional lighting. + var/actual_range = light_range // sunlight sources don't support directional lighting. // We don't need no damn vis checks! for (Tthing in RANGE_TURFS(Ceiling(light_range), source_turf)) T = Tthing + if (the_station_areas[T.loc]) + continue + check_t: if (T.dynamic_lighting || T.light_sources) @@ -86,9 +89,10 @@ Tcorners[i] = new /datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[i]) if (!T.has_opaque_atom) - for (thing in Tcorners) - C = thing - corners[C] = 0 + corners[Tcorners[1]] = 0 + corners[Tcorners[2]] = 0 + corners[Tcorners[3]] = 0 + corners[Tcorners[4]] = 0 turfs += T @@ -157,19 +161,16 @@ UNSETEMPTY(effect_str) UNSETEMPTY(affecting_turfs) -/datum/light_source/novis/update_angle() +/datum/light_source/sunlight/update_angle() return -#define QUEUE_UPDATE(level) \ - var/_should_update = needs_update == LIGHTING_NO_UPDATE; \ - if (needs_update < level) { \ - needs_update = level; \ - } \ - if (_should_update) { \ - SSlighting.light_queue += src; \ - } +#define QUEUE_UPDATE(level) \ + if (needs_update == LIGHTING_NO_UPDATE) \ + SSlighting.light_queue += src; \ + if (needs_update < level) \ + needs_update = level; -/datum/light_source/novis/update(atom/new_top_atom) +/datum/light_source/sunlight/update(atom/new_top_atom) // This top atom is different. if (new_top_atom && new_top_atom != top_atom) if(top_atom != source_atom) // Remove ourselves from the light sources of that top atom. @@ -184,10 +185,10 @@ QUEUE_UPDATE(LIGHTING_CHECK_UPDATE) -/datum/light_source/novis/force_update() +/datum/light_source/sunlight/force_update() QUEUE_UPDATE(LIGHTING_FORCE_UPDATE) -/datum/light_source/novis/vis_update() +/datum/light_source/sunlight/vis_update() QUEUE_UPDATE(LIGHTING_VIS_UPDATE) #undef QUEUE_UPDATE