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.
This commit is contained in:
Lohikar
2017-08-28 01:06:17 +03:00
committed by Erki
parent fb3ac0d718
commit f8bcaf4864
10 changed files with 131 additions and 90 deletions
+1 -1
View File
@@ -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"
+3
View File
@@ -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 \
+67 -28
View File
@@ -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
-4
View File
@@ -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)
-6
View File
@@ -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
////////////////////
@@ -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)
// -----------------------------
@@ -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 << "<span class='notice'>[src] cannot hold \the [W].</span>"
@@ -331,7 +327,7 @@
usr << "<span class='notice'>[src] has no more space specifically for \the [W].</span>"
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 << "<span class='notice'>[src] cannot hold [W].</span>"
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)
+1 -1
View File
@@ -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, .)
+19 -7
View File
@@ -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
@@ -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