mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2025-12-21 07:32:02 +00:00
Lighting performance tweaks (#2200)
Port of tgstation/tgstation#26975. Changes the lighting engine to apply lighting changes in one step using stored lum value differences instead of two steps (remove_lum, add_lum). Seems to show a speedup in lighting updates as well as a reduction in server memory usage due to aggressive freeing of unneeded lists. Lighting overlays also default to fullbright instead of fulldark so movable area transitions look less strange.
This commit is contained in:
@@ -25,41 +25,34 @@
|
||||
var/tmp/applied_lum_u
|
||||
|
||||
// Variables used to keep track of the atom's angle.
|
||||
var/tmp/limit_a_x // The first test point's X coord for the cone.
|
||||
var/tmp/limit_a_y // The first test point's Y coord for the cone.
|
||||
var/tmp/limit_a_t // The first test point's angle.
|
||||
var/tmp/limit_b_x // The second test point's X coord for the cone.
|
||||
var/tmp/limit_b_y // The second test point's Y coord for the cone.
|
||||
var/tmp/limit_b_t // The second test point's angle.
|
||||
var/tmp/cached_origin_x // The last known X coord of the origin.
|
||||
var/tmp/cached_origin_y // The last known Y coord of the origin.
|
||||
var/tmp/old_direction // The last known direction of the origin.
|
||||
var/tmp/targ_sign
|
||||
var/tmp/test_x_offset
|
||||
var/tmp/test_y_offset
|
||||
var/tmp/limit_a_x // The first test point's X coord for the cone.
|
||||
var/tmp/limit_a_y // The first test point's Y coord for the cone.
|
||||
var/tmp/limit_a_t // The first test point's angle.
|
||||
var/tmp/limit_b_x // The second test point's X coord for the cone.
|
||||
var/tmp/limit_b_y // The second test point's Y coord for the cone.
|
||||
var/tmp/limit_b_t // The second test point's angle.
|
||||
var/tmp/cached_origin_x // The last known X coord of the origin.
|
||||
var/tmp/cached_origin_y // The last known Y coord of the origin.
|
||||
var/tmp/old_direction // The last known direction of the origin.
|
||||
var/tmp/targ_sign // The sign to test the point against.
|
||||
var/tmp/test_x_offset // How much the X coord should be offset due to direction.
|
||||
var/tmp/test_y_offset // How much the Y coord should be offset due to direction.
|
||||
|
||||
var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners.
|
||||
var/list/turf/affecting_turfs
|
||||
|
||||
var/applied = FALSE // Whether we have applied our light yet or not.
|
||||
|
||||
var/vis_update // Whether we should smartly recalculate visibility. and then only update tiles that became (in)visible to us.
|
||||
var/needs_update // Whether we are queued for an update.
|
||||
var/destroyed // Whether we are destroyed and need to stop emitting light.
|
||||
var/force_update
|
||||
var/needs_update = LIGHTING_NO_UPDATE
|
||||
|
||||
/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()
|
||||
|
||||
source_atom.light_sources += src // Add us to the lights of our owner.
|
||||
LAZYADD(source_atom.light_sources, src)
|
||||
|
||||
top_atom = top
|
||||
if (top_atom != source_atom)
|
||||
if (!top.light_sources)
|
||||
top.light_sources = list()
|
||||
|
||||
top_atom.light_sources += src
|
||||
LAZYADD(top_atom.light_sources, src)
|
||||
|
||||
source_turf = top_atom
|
||||
light_power = source_atom.light_power
|
||||
@@ -70,9 +63,6 @@
|
||||
|
||||
parse_light_color()
|
||||
|
||||
effect_str = list()
|
||||
affecting_turfs = list()
|
||||
|
||||
update()
|
||||
|
||||
L_PROF(source_atom, "source_new")
|
||||
@@ -83,47 +73,31 @@
|
||||
/datum/light_source/Destroy(force)
|
||||
L_PROF(source_atom, "source_destroy")
|
||||
|
||||
force_update()
|
||||
if (source_atom && source_atom.light_sources)
|
||||
source_atom.light_sources -= src
|
||||
remove_lum()
|
||||
if (source_atom)
|
||||
LAZYREMOVE(source_atom.light_sources, src)
|
||||
|
||||
if (top_atom && top_atom.light_sources)
|
||||
top_atom.light_sources -= src
|
||||
if (top_atom)
|
||||
LAZYREMOVE(top_atom.light_sources, src)
|
||||
|
||||
. = ..()
|
||||
if (!force)
|
||||
return QDEL_HINT_IWILLGC
|
||||
|
||||
// Process the light RIGHT NOW.
|
||||
#define DO_UPDATE \
|
||||
if (QDELETED(src) || check() || force_update) { \
|
||||
remove_lum(TRUE); \
|
||||
if (!QDELETED(src)) { \
|
||||
apply_lum(TRUE); \
|
||||
} \
|
||||
} \
|
||||
else if (vis_update) { \
|
||||
smart_vis_update(TRUE); \
|
||||
} \
|
||||
vis_update = FALSE; \
|
||||
force_update = FALSE; \
|
||||
needs_update = FALSE;
|
||||
|
||||
// Queue an update.
|
||||
#define QUEUE_UPDATE \
|
||||
if (!needs_update) \
|
||||
{ \
|
||||
SSlighting.light_queue += src; \
|
||||
needs_update = TRUE; \
|
||||
}
|
||||
|
||||
// Picks either scheduled or instant updates based on current server load.
|
||||
#define INTELLIGENT_UPDATE \
|
||||
if (world.tick_usage > SSlighting.instant_tick_limit || SSlighting.force_queued) { \
|
||||
QUEUE_UPDATE; \
|
||||
} \
|
||||
else { \
|
||||
DO_UPDATE; \
|
||||
#define INTELLIGENT_UPDATE(level) \
|
||||
var/_should_update = needs_update == LIGHTING_NO_UPDATE; \
|
||||
if (needs_update < level) { \
|
||||
needs_update = level; \
|
||||
} \
|
||||
if (_should_update) { \
|
||||
if (world.tick_usage > SSlighting.instant_tick_limit || SSlighting.force_queued) { \
|
||||
SSlighting.light_queue += src; \
|
||||
} \
|
||||
else { \
|
||||
update_corners(TRUE); \
|
||||
needs_update = LIGHTING_NO_UPDATE; \
|
||||
} \
|
||||
}
|
||||
|
||||
// This proc will cause the light source to update the top atom, and add itself to the update queue.
|
||||
@@ -131,7 +105,7 @@
|
||||
// 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.
|
||||
top_atom.light_sources -= src
|
||||
LAZYREMOVE(top_atom.light_sources, src)
|
||||
|
||||
top_atom = new_top_atom
|
||||
|
||||
@@ -143,62 +117,19 @@
|
||||
|
||||
L_PROF(source_atom, "source_update")
|
||||
|
||||
INTELLIGENT_UPDATE
|
||||
INTELLIGENT_UPDATE(LIGHTING_CHECK_UPDATE)
|
||||
|
||||
// Will force an update without checking if it's actually needed.
|
||||
/datum/light_source/proc/force_update()
|
||||
L_PROF(source_atom, "source_forceupdate")
|
||||
force_update = 1
|
||||
|
||||
INTELLIGENT_UPDATE
|
||||
INTELLIGENT_UPDATE(LIGHTING_FORCE_UPDATE)
|
||||
|
||||
// Will cause the light source to recalculate turfs that were removed or added to visibility only.
|
||||
/datum/light_source/proc/vis_update()
|
||||
L_PROF(source_atom, "source_visupdate")
|
||||
vis_update = 1
|
||||
|
||||
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()
|
||||
if (!source_atom || !light_range || !light_power)
|
||||
qdel(src)
|
||||
return 1
|
||||
|
||||
if (!top_atom)
|
||||
top_atom = source_atom
|
||||
. = 1
|
||||
|
||||
if (istype(top_atom, /turf))
|
||||
if (source_turf != top_atom)
|
||||
source_turf = top_atom
|
||||
. = 1
|
||||
else if (top_atom.loc != source_turf)
|
||||
source_turf = top_atom.loc
|
||||
. = 1
|
||||
|
||||
if (source_atom.light_power != light_power)
|
||||
light_power = source_atom.light_power
|
||||
. = 1
|
||||
|
||||
if (source_atom.light_range != light_range)
|
||||
light_range = source_atom.light_range
|
||||
. = 1
|
||||
|
||||
if (light_range && light_power && !applied)
|
||||
. = 1
|
||||
|
||||
if (source_atom.light_color != light_color)
|
||||
light_color = source_atom.light_color
|
||||
parse_light_color()
|
||||
. = 1
|
||||
|
||||
if (top_atom.dir != old_direction && light_angle)
|
||||
. = 1
|
||||
|
||||
if (source_atom.light_wedge != light_angle)
|
||||
light_angle = source_atom.light_wedge
|
||||
. = 1
|
||||
INTELLIGENT_UPDATE(LIGHTING_VIS_UPDATE)
|
||||
|
||||
// Decompile the hexadecimal colour into lumcounts of each perspective.
|
||||
/datum/light_source/proc/parse_light_color()
|
||||
@@ -221,20 +152,21 @@
|
||||
// If you're wondering what's with the backslashes, the backslashes cause BYOND to not automatically end the line.
|
||||
// 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_XY(C,now,Tx,Ty) \
|
||||
. = LUM_FALLOFF_XY(C.x, C.y, Tx, Ty); \
|
||||
\
|
||||
. *= light_power; \
|
||||
\
|
||||
effect_str[C] = .; \
|
||||
\
|
||||
C.update_lumcount \
|
||||
( \
|
||||
. * applied_lum_r, \
|
||||
. * applied_lum_g, \
|
||||
. * applied_lum_b, \
|
||||
. * applied_lum_u, \
|
||||
now \
|
||||
#define APPLY_CORNER_XY(C,now,Tx,Ty) \
|
||||
. = LUM_FALLOFF_XY(C.x, C.y, Tx, Ty); \
|
||||
\
|
||||
. *= light_power; \
|
||||
var/OLD = effect_str[C]; \
|
||||
\
|
||||
effect_str[C] = .; \
|
||||
\
|
||||
C.update_lumcount \
|
||||
( \
|
||||
(. * lum_r) - (OLD * applied_lum_r), \
|
||||
(. * lum_g) - (OLD * applied_lum_g), \
|
||||
(. * lum_b) - (OLD * applied_lum_b), \
|
||||
(. * lum_u) - (OLD * applied_lum_u), \
|
||||
now \
|
||||
);
|
||||
|
||||
#define APPLY_CORNER(C,now) APPLY_CORNER_XY(C,now,source_turf.x,source_turf.y)
|
||||
@@ -255,6 +187,7 @@
|
||||
#define POLAR_TO_CART_Y(R,T) ((R) * sin(T))
|
||||
#define PSEUDO_WEDGE(A_X,A_Y,B_X,B_Y) ((A_X)*(B_Y) - (A_Y)*(B_X))
|
||||
#define MINMAX(NUM) ((NUM) < 0 ? -round(-(NUM)) : round(NUM))
|
||||
#define ARBITRARY_NUMBER 10
|
||||
|
||||
/datum/light_source/proc/update_angle()
|
||||
var/turf/T = get_turf(top_atom)
|
||||
@@ -304,17 +237,19 @@
|
||||
test_x_offset -= 1
|
||||
|
||||
// Convert our angle + range into a vector.
|
||||
limit_a_x = POLAR_TO_CART_X(light_range + 10, limit_a_t)
|
||||
limit_a_x = POLAR_TO_CART_X(light_range + ARBITRARY_NUMBER, limit_a_t)
|
||||
limit_a_x = MINMAX(limit_a_x)
|
||||
limit_a_y = POLAR_TO_CART_Y(light_range + 10, limit_a_t) // 10 is an arbitrary number, yes.
|
||||
limit_a_y = POLAR_TO_CART_Y(light_range + ARBITRARY_NUMBER, limit_a_t)
|
||||
limit_a_y = MINMAX(limit_a_y)
|
||||
limit_b_x = POLAR_TO_CART_X(light_range + 10, limit_b_t)
|
||||
limit_b_x = POLAR_TO_CART_X(light_range + ARBITRARY_NUMBER, limit_b_t)
|
||||
limit_b_x = MINMAX(limit_b_x)
|
||||
limit_b_y = POLAR_TO_CART_Y(light_range + 10, limit_b_t)
|
||||
limit_b_y = POLAR_TO_CART_Y(light_range + ARBITRARY_NUMBER, limit_b_t)
|
||||
limit_b_y = MINMAX(limit_b_y)
|
||||
// This won't change unless the origin or dir changes, might as well do it here.
|
||||
targ_sign = PSEUDO_WEDGE(limit_a_x, limit_a_y, limit_b_x, limit_b_y) > 0
|
||||
|
||||
#undef ARBITRARY_NUMBER
|
||||
|
||||
// I know this is 2D, calling it a cone anyways. Fuck the system.
|
||||
// Returns true if the test point is NOT inside the cone.
|
||||
// Make sure update_angle() is called first if the light's loc or dir have changed.
|
||||
@@ -336,121 +271,174 @@
|
||||
#define LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
|
||||
#define LUM_FALLOFF_XY(Cx,Cy,Tx,Ty) (1 - CLAMP01(sqrt(((Cx) - (Tx)) ** 2 + ((Cy) - (Ty)) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
|
||||
|
||||
/datum/light_source/proc/apply_lum(var/now = FALSE)
|
||||
var/static/update_gen = 1
|
||||
applied = 1
|
||||
/datum/light_source/proc/remove_lum(var/now = FALSE)
|
||||
applied = FALSE
|
||||
|
||||
if (!source_turf)
|
||||
var/thing
|
||||
for (thing in affecting_turfs)
|
||||
var/turf/T = thing
|
||||
LAZYREMOVE(T.affecting_lights, src)
|
||||
|
||||
affecting_turfs = null
|
||||
|
||||
for (thing in effect_str)
|
||||
var/datum/lighting_corner/C = thing
|
||||
REMOVE_CORNER(C,now)
|
||||
|
||||
LAZYREMOVE(C.affecting, src)
|
||||
|
||||
effect_str = null
|
||||
|
||||
/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C, var/now = FALSE)
|
||||
LAZYINITLIST(effect_str)
|
||||
if (effect_str[C]) // Already have one.
|
||||
REMOVE_CORNER(C,now)
|
||||
effect_str[C] = 0
|
||||
|
||||
APPLY_CORNER(C,now)
|
||||
UNSETEMPTY(effect_str)
|
||||
|
||||
/datum/light_source/proc/update_corners(now = FALSE)
|
||||
var/update = FALSE
|
||||
|
||||
if (QDELETED(source_atom))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
var/Tx
|
||||
var/Ty
|
||||
if (source_atom.light_power != light_power)
|
||||
light_power = source_atom.light_power
|
||||
update = TRUE
|
||||
|
||||
if (source_atom.light_range != light_range)
|
||||
light_range = source_atom.light_range
|
||||
update = TRUE
|
||||
|
||||
if (!top_atom)
|
||||
top_atom = source_atom
|
||||
update = TRUE
|
||||
|
||||
if (top_atom.loc != source_turf)
|
||||
source_turf = top_atom.loc
|
||||
update = TRUE
|
||||
|
||||
if (!light_range || !light_power)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
if (isturf(top_atom))
|
||||
if (source_turf != top_atom)
|
||||
source_turf = top_atom
|
||||
update = TRUE
|
||||
else if (top_atom.loc != source_turf)
|
||||
source_turf = top_atom.loc
|
||||
update = TRUE
|
||||
|
||||
if (!source_turf)
|
||||
return // Somehow we've got a light in nullspace, no-op.
|
||||
|
||||
if (light_range && light_power && !applied)
|
||||
update = TRUE
|
||||
|
||||
if (source_atom.light_color != light_color)
|
||||
light_color = source_atom.light_color
|
||||
parse_light_color()
|
||||
update = TRUE
|
||||
|
||||
else if (applied_lum_r != lum_r || applied_lum_g != lum_g || applied_lum_b != lum_b)
|
||||
update = TRUE
|
||||
|
||||
if (source_atom.light_wedge != light_angle)
|
||||
light_angle = source_atom.light_wedge
|
||||
update = TRUE
|
||||
|
||||
if (light_angle && top_atom.dir != old_direction)
|
||||
update = TRUE
|
||||
|
||||
if (update && light_angle)
|
||||
update_angle()
|
||||
|
||||
if (update)
|
||||
needs_update = LIGHTING_CHECK_UPDATE
|
||||
else if (needs_update == LIGHTING_CHECK_UPDATE)
|
||||
return // No change.
|
||||
|
||||
var/list/datum/lighting_corner/corners = list()
|
||||
var/list/turf/turfs = list()
|
||||
var/thing
|
||||
var/datum/lighting_corner/C
|
||||
var/turf/T
|
||||
var/Sx = source_turf.x
|
||||
var/Sy = source_turf.y
|
||||
|
||||
// Keep track of the last applied lum values so that the lighting can be reversed
|
||||
applied_lum_r = lum_r
|
||||
applied_lum_g = lum_g
|
||||
applied_lum_b = lum_b
|
||||
applied_lum_u = lum_u
|
||||
|
||||
if (light_angle)
|
||||
update_angle()
|
||||
|
||||
FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING)
|
||||
Tx = T.x
|
||||
Ty = T.y
|
||||
if (light_angle && check_light_cone(Tx, Ty))
|
||||
FOR_DVIEW(T, Ceiling(light_range), source_turf, 0)
|
||||
if (light_angle && check_light_cone(T.x, T.y))
|
||||
continue
|
||||
|
||||
if (!T.lighting_corners_initialised)
|
||||
T.generate_missing_corners()
|
||||
for (thing in T.get_corners())
|
||||
C = thing
|
||||
corners[C] = 0
|
||||
|
||||
for (var/datum/lighting_corner/C in T.get_corners())
|
||||
if (C.update_gen == update_gen)
|
||||
turfs += T
|
||||
END_FOR_DVIEW
|
||||
|
||||
LAZYINITLIST(affecting_turfs)
|
||||
|
||||
var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them.
|
||||
affecting_turfs += L
|
||||
for (thing in L)
|
||||
T = thing
|
||||
LAZYADD(T.affecting_lights, src)
|
||||
|
||||
L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights.
|
||||
affecting_turfs -= L
|
||||
for (thing in L)
|
||||
T = thing
|
||||
LAZYREMOVE(T.affecting_lights, src)
|
||||
|
||||
LAZYINITLIST(effect_str)
|
||||
if (needs_update == LIGHTING_VIS_UPDATE)
|
||||
for (thing in corners - effect_str)
|
||||
C = thing
|
||||
LAZYADD(C.affecting, src)
|
||||
if (!C.active)
|
||||
effect_str[C] = 0
|
||||
continue
|
||||
|
||||
C.update_gen = update_gen
|
||||
C.affecting += src
|
||||
|
||||
APPLY_CORNER_XY(C, now, Sx, Sy)
|
||||
else
|
||||
L = corners - effect_str
|
||||
for (thing in L)
|
||||
C = thing
|
||||
LAZYADD(C.affecting, src)
|
||||
if (!C.active)
|
||||
effect_str[C] = 0
|
||||
continue
|
||||
|
||||
APPLY_CORNER_XY(C, now, Sx, Sy)
|
||||
|
||||
if (!T.affecting_lights)
|
||||
T.affecting_lights = list()
|
||||
for (thing in corners - L)
|
||||
C = thing
|
||||
if (!C.active)
|
||||
effect_str[C] = 0
|
||||
continue
|
||||
|
||||
T.affecting_lights += src
|
||||
affecting_turfs += T
|
||||
APPLY_CORNER_XY(C, now, Sx, Sy)
|
||||
|
||||
update_gen++
|
||||
L = effect_str - corners
|
||||
for (thing in L)
|
||||
C = thing
|
||||
REMOVE_CORNER(C, now)
|
||||
LAZYREMOVE(C.affecting, src)
|
||||
|
||||
/datum/light_source/proc/remove_lum(var/now = FALSE)
|
||||
applied = FALSE
|
||||
effect_str -= L
|
||||
|
||||
for (var/turf/T in affecting_turfs)
|
||||
if (!T.affecting_lights)
|
||||
T.affecting_lights = list()
|
||||
else
|
||||
T.affecting_lights -= src
|
||||
applied_lum_r = lum_r
|
||||
applied_lum_g = lum_g
|
||||
applied_lum_b = lum_b
|
||||
applied_lum_u = lum_u
|
||||
|
||||
affecting_turfs.Cut()
|
||||
|
||||
for (var/datum/lighting_corner/C in effect_str)
|
||||
REMOVE_CORNER(C,now)
|
||||
|
||||
C.affecting -= src
|
||||
|
||||
effect_str.Cut()
|
||||
|
||||
/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,now)
|
||||
|
||||
APPLY_CORNER(C,now)
|
||||
|
||||
/datum/light_source/proc/smart_vis_update(var/now = FALSE)
|
||||
L_PROF(source_atom, "source_smartvisupdate")
|
||||
var/list/datum/lighting_corner/corners = list()
|
||||
var/list/turf/turfs = list()
|
||||
FOR_DVIEW(var/turf/T, light_range, source_turf, 0)
|
||||
if (!T.lighting_corners_initialised)
|
||||
T.generate_missing_corners()
|
||||
if (light_angle && check_light_cone(T.x, T.y))
|
||||
continue
|
||||
|
||||
corners |= T.get_corners()
|
||||
turfs += T
|
||||
|
||||
var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them.
|
||||
affecting_turfs += L
|
||||
for (var/turf/T in L)
|
||||
if (!T.affecting_lights)
|
||||
T.affecting_lights = list(src)
|
||||
else
|
||||
T.affecting_lights += src
|
||||
|
||||
L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights.
|
||||
affecting_turfs -= L
|
||||
for (var/turf/T in L)
|
||||
if (QDELETED(T))
|
||||
continue
|
||||
T.affecting_lights -= src
|
||||
|
||||
for (var/datum/lighting_corner/C in corners - effect_str) // New corners
|
||||
C.affecting += src
|
||||
if (!C.active || check_light_cone(C.x, C.y))
|
||||
effect_str[C] = 0
|
||||
continue
|
||||
|
||||
APPLY_CORNER(C,now)
|
||||
|
||||
for (var/datum/lighting_corner/C in effect_str - corners) // Old, now gone, corners.
|
||||
REMOVE_CORNER(C,now)
|
||||
C.affecting -= src
|
||||
effect_str -= C
|
||||
UNSETEMPTY(effect_str)
|
||||
UNSETEMPTY(affecting_turfs)
|
||||
|
||||
#undef QUEUE_UPDATE
|
||||
#undef DO_UPDATE
|
||||
|
||||
Reference in New Issue
Block a user