Adaptive lighting updates (#1679)

This PR simplifies the lighting update system (removes update_type) and instead makes the lighting system decide which update method to use based on server load.

Instant updates are used during low-load, scheduled during high.

Also removes: update_lights_now() (redundant), diff_light() (unused).
This commit is contained in:
Lohikar
2017-01-31 00:37:56 +02:00
committed by skull132
parent 51f9c63589
commit 1d5287dad0
9 changed files with 90 additions and 102 deletions
+15 -30
View File
@@ -12,24 +12,8 @@
// Nonesensical value for l_color default, so we can detect if it gets set to null.
#define NONSENSICAL_VALUE -99999
#define SET_LIGHT set_light(l_range,l_power,l_color,uv,update_type);return;
// Same as set_light(), but only does something if there's actually a change in state.
/atom/proc/diff_light(/var/l_range, var/l_power, var/l_color = NONSENSICAL_VALUE, var/uv = NONSENSICAL_VALUE, var/update_type = UPDATE_SCHEDULE)
if (l_range != light_range)
SET_LIGHT
if (l_power && l_power != light_power)
SET_LIGHT
if (l_color != NONSENSICAL_VALUE && l_color != light_color)
SET_LIGHT
if (uv != NONSENSICAL_VALUE)
SET_LIGHT
if (update_type != UPDATE_SCHEDULE)
SET_LIGHT
#undef SET_LIGHT
// The proc you should always use to set the light of this atom.
/atom/proc/set_light(var/l_range, var/l_power, var/l_color = NONSENSICAL_VALUE, var/uv = NONSENSICAL_VALUE, var/update_type = UPDATE_SCHEDULE)
/atom/proc/set_light(var/l_range, var/l_power, var/l_color = NONSENSICAL_VALUE, var/uv = NONSENSICAL_VALUE, var/no_update = FALSE)
lprof_write(src, "atom_setlight")
if(l_range > 0 && l_range < MINIMUM_USEFUL_LIGHT_RANGE)
@@ -44,28 +28,29 @@
light_color = l_color
if (uv != NONSENSICAL_VALUE)
set_uv(uv, update_type = UPDATE_NONE)
set_uv(uv, no_update = TRUE)
switch (update_type)
if (UPDATE_SCHEDULE)
update_light()
if (UPDATE_NOW)
update_light(TRUE)
if (no_update)
return
update_light()
#undef NONSENSICAL_VALUE
/atom/proc/set_uv(var/intensity, var/update_type = UPDATE_SCHEDULE)
/atom/proc/set_uv(var/intensity, var/no_update)
if (intensity < 0 || intensity > 255)
intensity = min(max(intensity, 255), 0)
uv_intensity = intensity
if (update_type != UPDATE_NONE)
update_light(update_type)
if (no_update)
return
update_light()
// Will update the light (duh).
// Creates or destroys it if needed, makes it update values, makes sure it's got the correct source turf...
/atom/proc/update_light(var/update_type = UPDATE_SCHEDULE)
/atom/proc/update_light()
set waitfor = FALSE
if (gcDestroyed)
return
@@ -83,7 +68,7 @@
. = loc
if (light) // Update the light or create it if it does not exist.
light.update(., update_type)
light.update(.)
else
light = new/datum/light_source(src, .)
@@ -143,11 +128,11 @@
if (Obj && OldLoc != src)
for (var/datum/light_source/L in Obj.light_sources) // Cycle through the light sources on this atom and tell them to update.
L.source_atom.update_light(update_type = UPDATE_NOW)
L.source_atom.update_light()
/atom/Exited(var/atom/movable/Obj, var/atom/newloc)
. = ..()
if (!newloc && Obj && newloc != src) // Incase the atom is being moved to nullspace, we handle queuing for a lighting update here.
for (var/datum/light_source/L in Obj.light_sources) // Cycle through the light sources on this atom and tell them to update.
L.source_atom.update_light(update_type = UPDATE_NOW)
L.source_atom.update_light()
+12 -10
View File
@@ -90,20 +90,23 @@
active = TRUE
// God that was a mess, now to do the rest of the corner code! Hooray!
/datum/lighting_corner/proc/update_lumcount(var/delta_r, var/delta_g, var/delta_b, var/delta_u, var/update_type = UPDATE_SCHEDULE)
/datum/lighting_corner/proc/update_lumcount(var/delta_r, var/delta_g, var/delta_b, var/delta_u, var/now = FALSE)
lum_r += delta_r
lum_g += delta_g
lum_b += delta_b
lum_u += delta_u
if (update_type == UPDATE_SCHEDULE && !needs_update)
needs_update = TRUE
update_overlays(update_type)
lighting_update_corners += src
else if (!needs_update)
update_overlays(UPDATE_NOW)
if (needs_update)
return
/datum/lighting_corner/proc/update_overlays(var/update_type = UPDATE_SCHEDULE)
if (!now)
needs_update = TRUE
update_overlays(FALSE)
lighting_update_corners += src
else
update_overlays(TRUE)
/datum/lighting_corner/proc/update_overlays(var/now = FALSE)
// Cache these values a head of time so 4 individual lighting overlays don't all calculate them individually.
var/mx = max(lum_r, lum_g, lum_b) // Scale it so 1 is the strongest lum, if it is above 1.
@@ -123,9 +126,8 @@
for (var/TT in masters)
var/turf/T = TT
if (T.lighting_overlay)
if (update_type == UPDATE_NOW) // UPDATE_NONE is meaningless here.
if (now)
T.lighting_overlay.update_overlay()
else if (!T.lighting_overlay.needs_update)
T.lighting_overlay.needs_update = TRUE
lighting_update_overlays += T.lighting_overlay
+48 -42
View File
@@ -33,7 +33,7 @@
var/destroyed // Whether we are destroyed and need to stop emitting light.
var/force_update
/datum/light_source/New(var/atom/owner, var/atom/top, var/update_t = UPDATE_SCHEDULE)
/datum/light_source/New(var/atom/owner, var/atom/top)
source_atom = owner // Set our new owner.
if (!source_atom.light_sources)
source_atom.light_sources = list()
@@ -57,7 +57,7 @@
effect_str = list()
affecting_turfs = list()
update(update_type = update_t)
update()
lprof_write(src, "source_new")
@@ -73,32 +73,40 @@
if (top_atom && top_atom.light_sources)
top_atom.light_sources -= src
/datum/light_source/proc/effect_update_now()
lprof_write(src, "source_updatenow")
if (check() || destroyed || force_update)
remove_lum(TRUE)
if (!destroyed)
apply_lum(update_type = UPDATE_NOW)
// Process the light RIGHT NOW.
#define DO_UPDATE \
if (destroyed || check() || force_update) { \
remove_lum(TRUE); \
if (!destroyed) { \
apply_lum(TRUE); \
} \
} \
else if (vis_update) { \
smart_vis_update(TRUE); \
} \
vis_update = FALSE; \
force_update = FALSE; \
needs_update = FALSE;
else if (vis_update) // We smartly update only tiles that became (in) visible to use.
smart_vis_update(update_type = UPDATE_NOW)
vis_update = FALSE
force_update = FALSE
needs_update = FALSE
// Call it dirty, I don't care.
// This is here so there's no performance loss on non-instant updates from the fact that the engine can also do instant updates.
// If you're wondering what's with the "BYOND" argument: BYOND won't let me have a () macro that has no arguments :|.
#define effect_update(BYOND) \
// Queue an update.
#define QUEUE_UPDATE \
if (!needs_update) \
{ \
lighting_update_lights += src; \
needs_update = TRUE; \
}
// Picks either scheduled or instant updates based on current server load.
#define INTELLIGENT_UPDATE \
if (world.tick_usage > TICK_LIMIT || !ticker) { \
QUEUE_UPDATE; \
} \
else { \
DO_UPDATE; \
}
// This proc will cause the light source to update the top atom, and add itself to the update queue.
/datum/light_source/proc/update(var/atom/new_top_atom, var/update_type = UPDATE_SCHEDULE)
/datum/light_source/proc/update(var/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.
@@ -114,23 +122,20 @@
lprof_write(src, "source_update")
if (update_type == UPDATE_NOW)
effect_update_now()
else if (update_type == UPDATE_SCHEDULE) // I don't know why you would call this with UPDATE_NONE, but hey.
effect_update(null)
INTELLIGENT_UPDATE
// Will force an update without checking if it's actually needed.
/datum/light_source/proc/force_update()
lprof_write(src, "source_forceupdate")
force_update = 1
effect_update(null)
INTELLIGENT_UPDATE
// Will cause the light source to recalculate turfs that were removed or added to visibility only.
/datum/light_source/proc/vis_update()
vis_update = 1
effect_update(null)
INTELLIGENT_UPDATE
// Will check if we actually need to update, and update any variables that may need to be updated.
/datum/light_source/proc/check()
@@ -188,7 +193,7 @@
// As such this all gets counted as a single line.
// The braces and semicolons are there to be able to do this on a single line.
#define APPLY_CORNER(C) \
#define APPLY_CORNER(C,now) \
. = LUM_FALLOFF(C, source_turf); \
\
. *= light_power; \
@@ -201,11 +206,11 @@
. * applied_lum_g, \
. * applied_lum_b, \
. * applied_lum_u, \
update_type \
now \
);
// I don't need to explain what this does, do I?
#define REMOVE_CORNER(C) \
#define REMOVE_CORNER(C,now) \
. = -effect_str[C]; \
C.update_lumcount \
( \
@@ -213,13 +218,13 @@
. * applied_lum_g, \
. * applied_lum_b, \
. * applied_lum_u, \
update_type \
now \
);
// This is the define used to calculate falloff.
#define LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
/datum/light_source/proc/apply_lum(var/update_type = UPDATE_SCHEDULE)
/datum/light_source/proc/apply_lum(var/now = FALSE)
var/static/update_gen = 1
applied = 1
@@ -244,7 +249,7 @@
effect_str[C] = 0
continue
APPLY_CORNER(C)
APPLY_CORNER(C, now)
if (!T.affecting_lights)
T.affecting_lights = list()
@@ -254,7 +259,7 @@
update_gen++
/datum/light_source/proc/remove_lum(var/update_type = UPDATE_SCHEDULE)
/datum/light_source/proc/remove_lum(var/now = FALSE)
applied = FALSE
for (var/turf/T in affecting_turfs)
@@ -266,20 +271,19 @@
affecting_turfs.Cut()
for (var/datum/lighting_corner/C in effect_str)
REMOVE_CORNER(C)
REMOVE_CORNER(C,now)
C.affecting -= src
effect_str.Cut()
/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C)
var/update_type = UPDATE_SCHEDULE // for (APPLY|REMOVE)_CORNER.
/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C, var/now = FALSE)
if (effect_str.Find(C)) // Already have one.
REMOVE_CORNER(C)
REMOVE_CORNER(C,now)
APPLY_CORNER(C)
APPLY_CORNER(C,now)
/datum/light_source/proc/smart_vis_update(var/update_type = UPDATE_SCHEDULE)
/datum/light_source/proc/smart_vis_update(var/now = FALSE)
var/list/datum/lighting_corner/corners = list()
var/list/turf/turfs = list()
FOR_DVIEW(var/turf/T, light_range, source_turf, 0)
@@ -307,14 +311,16 @@
effect_str[C] = 0
continue
APPLY_CORNER(C)
APPLY_CORNER(C,now)
for (var/datum/lighting_corner/C in effect_str - corners) // Old, now gone, corners.
REMOVE_CORNER(C)
REMOVE_CORNER(C,now)
C.affecting -= src
effect_str -= C
#undef effect_update
#undef QUEUE_UPDATE
#undef DO_UPDATE
#undef INTELLIGENT_UPDATE
#undef LUM_FALLOFF
#undef REMOVE_CORNER
#undef APPLY_CORNER
+2 -8
View File
@@ -21,12 +21,6 @@
for (var/datum/light_source/L in affecting_lights)
L.vis_update()
// Avoid calling this if you can, bypasses the lighting scheduler (potentially creating lag).
/turf/proc/update_lights_now()
lprof_write(src, "turf_updatenow")
for (var/datum/light_source/L in affecting_lights)
L.update(update_type = UPDATE_NOW)
/turf/proc/lighting_clear_overlay()
if (lighting_overlay)
returnToPool(lighting_overlay)
@@ -50,7 +44,7 @@
if (!C.active) // We would activate the corner, calculate the lighting for it.
for (var/L in C.affecting)
var/datum/light_source/S = L
S.recalc_corner(C)
S.recalc_corner(C, TRUE)
C.active = TRUE
@@ -71,7 +65,7 @@
return CLAMP01(totallums)
// Gets the current UV illumination of the turf. Always 100% for space.
// Gets the current UV illumination of the turf. Always 100% for space & other static-lit tiles.
/turf/proc/get_uv_lumcount(var/minlum = 0, var/maxlum = 1)
if (!lighting_overlay)
return 1