Skip to content

Commit

Permalink
Some basic tg lighting patches (TauCetiStation#12852)
Browse files Browse the repository at this point in the history
* Performance Improvements to Lighting Corners (tgstation/tgstation#51546)

* Removes unused lighting var (tgstation/tgstation#51932)

* Move lighting queues inside the lighting subsystem (tgstation/tgstation#43816)

* Speeds up /datum/light_source/proc/update_corners() by 16% or so (tgstation/tgstation#51004)

About The Pull Request

Just moving /turf/proc/get_corners() into its only caller, slightly shuffled.

Also #defining /proc/GetRedPart() etc, which should flatten down to a copytext+text2num operation with #51005. Also defining parse_light_color() since it has two callers and does something trivial (splits #ff0000 into three luminance vars).

cl Naksu
code: Lighting corner updates are ever so slightly faster.
/cl

* Fixes lighting updates from opaque objects changing state (ie doors) (tgstation/tgstation#51943)

* harddels (tg/57736)

* Lighting list cleanup. (tgstation/tgstation#59002)

Removed a bunch of back reference lists that were either entirely unused, or contained references that could be found within the datums contained in other lists.

lighting corner datums now get deleted with unused.

light sources no longer track lighting corners where the appiled light rounds to 0.

Fix lighting on turfs that gained dynamic lighting mid round. lazy init corner datums.

these two are related. by decoupling corner datums from the turfs dynamic lighting state, we can use them to know what level of light a non-dynamic light turf should have once it gains dynamic light.

Also should free up some memory not storing these datums in maint. Corner datums only exist on a turf that has light cast upon it by the dynamic lighting system.

Lighting corners are now lazy inited and deleted. they should always (and only exist) if there is a light source shining on it within range (even if the turf has no dynamic lighting). This is needed to support turfs that become lighting enabled mid round. On the plus side, they will no longer be generated on full dark turfs.

---------

Co-authored-by: Bobbahbrown <[email protected]>
Co-authored-by: Jordan Brown <[email protected]>
Co-authored-by: vuonojenmustaturska <[email protected]>
Co-authored-by: AnturK <[email protected]>
Co-authored-by: Kyle Spier-Swenson <[email protected]>
  • Loading branch information
6 people authored Feb 19, 2024
1 parent 5a88270 commit e6cc8d3
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 270 deletions.
24 changes: 24 additions & 0 deletions code/__DEFINES/lighting.dm
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,27 @@
#define FLASH_LIGHT_DURATION 2
#define FLASH_LIGHT_POWER 3
#define FLASH_LIGHT_RANGE 3.8

/// Returns the red part of a #RRGGBB hex sequence as number
#define GETREDPART(hexa) hex2num(copytext(hexa, 2, 4))

/// Returns the green part of a #RRGGBB hex sequence as number
#define GETGREENPART(hexa) hex2num(copytext(hexa, 4, 6))

/// Returns the blue part of a #RRGGBB hex sequence as number
#define GETBLUEPART(hexa) hex2num(copytext(hexa, 6, 8))

/// Parse the hexadecimal color into lumcounts of each perspective.
#define PARSE_LIGHT_COLOR(source) \
do { \
if (source.light_color) { \
var/__light_color = source.light_color; \
source.lum_r = GETREDPART(__light_color) / 255; \
source.lum_g = GETGREENPART(__light_color) / 255; \
source.lum_b = GETBLUEPART(__light_color) / 255; \
} else { \
source.lum_r = 1; \
source.lum_g = 1; \
source.lum_b = 1; \
}; \
} while (FALSE)
22 changes: 3 additions & 19 deletions code/__HELPERS/game.dm
Original file line number Diff line number Diff line change
Expand Up @@ -426,32 +426,16 @@

return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y)

/proc/GetRedPart(const/hexa)
return hex2num(copytext(hexa,2,4))

/proc/GetGreenPart(const/hexa)
return hex2num(copytext(hexa,4,6))

/proc/GetBluePart(const/hexa)
return hex2num(copytext(hexa,6,8))

/proc/GetHexColors(const/hexa)
return list(
GetRedPart(hexa),
GetGreenPart(hexa),
GetBluePart(hexa)
)

/proc/MixColors(const/list/colors)
var/list/reds = list()
var/list/blues = list()
var/list/greens = list()
var/list/weights = list()

for (var/i = 0, ++i <= colors.len)
reds.Add(GetRedPart(colors[i]))
blues.Add(GetBluePart(colors[i]))
greens.Add(GetGreenPart(colors[i]))
reds.Add(GETREDPART(colors[i]))
blues.Add(GETBLUEPART(colors[i]))
greens.Add(GETGREENPART(colors[i]))
weights.Add(1)

var/r = mixOneColor(weights, reds)
Expand Down
30 changes: 15 additions & 15 deletions code/controllers/subsystem/lighting.dm
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
var/global/list/lighting_update_lights = list() // List of lighting sources queued for update.
var/global/list/lighting_update_corners = list() // List of lighting corners queued for update.
var/global/list/lighting_update_objects = list() // List of lighting objects queued for update.

SUBSYSTEM_DEF(lighting)
name = "Lighting"

Expand All @@ -11,8 +7,12 @@ SUBSYSTEM_DEF(lighting)
flags = SS_TICKER
msg_lobby = "Включаем свет..."

var/static/list/sources_queue = list() // List of lighting sources queued for update.
var/static/list/corners_queue = list() // List of lighting corners queued for update.
var/static/list/objects_queue = list() // List of lighting objects queued for update.

/datum/controller/subsystem/lighting/stat_entry()
..("L:[global.lighting_update_lights.len]|C:[global.lighting_update_corners.len]|O:[global.lighting_update_objects.len]")
..("L:[sources_queue.len]|C:[corners_queue.len]|O:[objects_queue.len]")

/datum/controller/subsystem/lighting/Initialize(timeofday)
if(!initialized)
Expand Down Expand Up @@ -48,9 +48,9 @@ SUBSYSTEM_DEF(lighting)
if(!init_fire)
MC_SPLIT_TICK

while (global.lighting_update_lights.len)
var/datum/light_source/L = global.lighting_update_lights[global.lighting_update_lights.len]
global.lighting_update_lights.len--
while (sources_queue.len)
var/datum/light_source/L = sources_queue[sources_queue.len]
sources_queue.len--

L.update_corners()

Expand All @@ -67,25 +67,25 @@ SUBSYSTEM_DEF(lighting)

var/i = 0

for (i in 1 to global.lighting_update_corners.len)
var/datum/lighting_corner/C = global.lighting_update_corners[i]
for (i in 1 to corners_queue.len)
var/datum/lighting_corner/C = corners_queue[i]

C.update_objects()
C.needs_update = FALSE
C.update_objects()
if(init_fire)
CHECK_TICK
else if (MC_TICK_CHECK)
break
if (i)
global.lighting_update_corners.Cut(1, i+1)
corners_queue.Cut(1, i+1)
i = 0


if(!init_fire)
MC_SPLIT_TICK

for (i in 1 to global.lighting_update_objects.len)
var/atom/movable/lighting_object/O = global.lighting_update_objects[i]
for (i in 1 to objects_queue.len)
var/atom/movable/lighting_object/O = objects_queue[i]

if (QDELETED(O))
continue
Expand All @@ -97,7 +97,7 @@ SUBSYSTEM_DEF(lighting)
else if (MC_TICK_CHECK)
break
if (i)
global.lighting_update_objects.Cut(1, i+1)
objects_queue.Cut(1, i+1)

/datum/controller/subsystem/lighting/Recover()
initialized = SSlighting.initialized
Expand Down
30 changes: 26 additions & 4 deletions code/game/turfs/turf.dm
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/turf
icon = 'icons/turf/floors.dmi'
luminosity = 1

var/turf/basetype = /turf/environment/space
var/can_deconstruct = FALSE

Expand Down Expand Up @@ -39,6 +41,20 @@

var/list/turf_decals

var/dynamic_lighting = TRUE
var/force_lighting_update = FALSE

var/tmp/lighting_corners_initialised = FALSE

///Our lighting object.
var/tmp/atom/movable/lighting_object/lighting_object
///Lighting Corner datums.
var/tmp/datum/lighting_corner/lighting_corner_NE
var/tmp/datum/lighting_corner/lighting_corner_SE
var/tmp/datum/lighting_corner/lighting_corner_SW
var/tmp/datum/lighting_corner/lighting_corner_NW

var/tmp/has_opaque_atom = FALSE // Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile.

/**
* Turf Initialize
Expand Down Expand Up @@ -321,9 +337,11 @@
var/old_opacity = opacity
var/old_dynamic_lighting = dynamic_lighting
var/old_force_lighting_update = force_lighting_update
var/old_affecting_lights = affecting_lights
var/old_lighting_object = lighting_object
var/old_corners = corners
var/old_lighting_corner_NE = lighting_corner_NE
var/old_lighting_corner_SE = lighting_corner_SE
var/old_lighting_corner_SW = lighting_corner_SW
var/old_lighting_corner_NW = lighting_corner_NW

var/old_basetype = basetype
var/old_flooded = flooded
Expand Down Expand Up @@ -400,11 +418,15 @@

queue_smooth_neighbors(W)

lighting_corner_NE = old_lighting_corner_NE
lighting_corner_SE = old_lighting_corner_SE
lighting_corner_SW = old_lighting_corner_SW
lighting_corner_NW = old_lighting_corner_NW

if(SSlighting.initialized)
recalc_atom_opacity()
lighting_object = old_lighting_object
affecting_lights = old_affecting_lights
corners = old_corners

if (force_lighting_update || old_force_lighting_update || old_opacity != opacity || dynamic_lighting != old_dynamic_lighting)
reconsider_lights()

Expand Down
130 changes: 78 additions & 52 deletions code/modules/lighting/lighting_corner.dm
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@
// And corners get shared between multiple turfs (unless you're on the corners of the map, then 1 corner doesn't).
// For the record: these should never ever ever be deleted, even if the turf doesn't have dynamic lighting.

// This list is what the code that assigns corners listens to, the order in this list is the order in which corners are added to the /turf/corners list.
var/global/list/LIGHTING_CORNER_DIAGONAL = list(NORTHEAST, SOUTHEAST, SOUTHWEST, NORTHWEST)

/datum/lighting_corner
var/list/turf/masters
var/list/datum/light_source/affecting // Light sources affecting us.
var/active = FALSE // TRUE if one of our masters has dynamic lighting.

var/x = 0
var/y = 0
var/z = 0

var/turf/master_NE
var/turf/master_SE
var/turf/master_SW
var/turf/master_NW

var/lum_r = 0
var/lum_g = 0
Expand All @@ -27,9 +26,7 @@ var/global/list/LIGHTING_CORNER_DIAGONAL = list(NORTHEAST, SOUTHEAST, SOUTHWEST,

/datum/lighting_corner/New(turf/new_turf, diagonal)
. = ..()
masters = list()
masters[new_turf] = turn(diagonal, 180)
z = new_turf.z
save_master(new_turf, turn(diagonal, 180))

var/vertical = diagonal & ~(diagonal - 1) // The horizontal directions (4 and 8) are bigger than the vertical ones (1 and 2), so we can reliably say the lsb is the horizontal direction.
var/horizontal = diagonal & ~vertical // Now that we know the horizontal one we can get the vertical one.
Expand All @@ -41,53 +38,52 @@ var/global/list/LIGHTING_CORNER_DIAGONAL = list(NORTHEAST, SOUTHEAST, SOUTHWEST,
// Issue being that the only way I could think of doing it was very messy, slow and honestly overengineered.
// So we'll have this hardcode instead.
var/turf/T
var/i

// Diagonal one is easy.
T = get_step(new_turf, diagonal)
if (T) // In case we're on the map's border.
if (!T.corners)
T.corners = list(null, null, null, null)

masters[T] = diagonal
i = global.LIGHTING_CORNER_DIAGONAL.Find(turn(diagonal, 180))
T.corners[i] = src
save_master(T, diagonal)

// Now the horizontal one.
T = get_step(new_turf, horizontal)
if (T) // Ditto.
if (!T.corners)
T.corners = list(null, null, null, null)

masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates.
i = global.LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
T.corners[i] = src
save_master(T, ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH)) // Get the dir based on coordinates.

// And finally the vertical one.
T = get_step(new_turf, vertical)
if (T)
if (!T.corners)
T.corners = list(null, null, null, null)

masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates.
i = global.LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
T.corners[i] = src

update_active()

/datum/lighting_corner/proc/update_active()
active = FALSE
var/turf/T
var/thing
for (thing in masters)
T = thing
if (T.lighting_object)
active = TRUE
save_master(T, ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH)) // Get the dir based on coordinates.

/datum/lighting_corner/proc/save_master(turf/master, dir)
switch (dir)
if (NORTHEAST)
master_NE = master
master.lighting_corner_SW = src
if (SOUTHEAST)
master_SE = master
master.lighting_corner_NW = src
if (SOUTHWEST)
master_SW = master
master.lighting_corner_NE = src
if (NORTHWEST)
master_NW = master
master.lighting_corner_SE = src

/datum/lighting_corner/proc/self_destruct_if_idle()
if (!length(affecting))
qdel(src, force = TRUE)

/datum/lighting_corner/proc/vis_update()
for (var/datum/light_source/light_source as anything in affecting)
light_source.vis_update()

/datum/lighting_corner/proc/full_update()
for (var/datum/light_source/light_source as anything in affecting)
light_source.recalc_corner(src)

// God that was a mess, now to do the rest of the corner code! Hooray!
/datum/lighting_corner/proc/update_lumcount(delta_r, delta_g, delta_b)

if ((abs(delta_r)+abs(delta_g)+abs(delta_b)) == 0)
if (!(delta_r || delta_g || delta_b)) // 0 is falsey ok
return

lum_r += delta_r
Expand All @@ -96,10 +92,10 @@ var/global/list/LIGHTING_CORNER_DIAGONAL = list(NORTHEAST, SOUTHEAST, SOUTHWEST,

if (!needs_update)
needs_update = TRUE
global.lighting_update_corners += src
SSlighting.corners_queue += src

/datum/lighting_corner/proc/update_objects()
// Cache these values a head of time so 4 individual lighting objects don't all calculate them individually.
// Cache these values ahead of time so 4 individual lighting objects don't all calculate them individually.
var/lum_r = src.lum_r
var/lum_g = src.lum_g
var/lum_b = src.lum_b
Expand All @@ -122,22 +118,52 @@ var/global/list/LIGHTING_CORNER_DIAGONAL = list(NORTHEAST, SOUTHEAST, SOUTHWEST,
#endif
cache_mx = round(mx, LIGHTING_ROUND_VALUE)

for (var/TT in masters)
var/turf/T = TT
if (T.lighting_object)
if (!T.lighting_object.needs_update)
T.lighting_object.needs_update = TRUE
global.lighting_update_objects += T.lighting_object

var/atom/movable/lighting_object/lighting_object = master_NE?.lighting_object
if (lighting_object && !lighting_object.needs_update)
lighting_object.needs_update = TRUE
SSlighting.objects_queue += lighting_object

lighting_object = master_SE?.lighting_object
if (lighting_object && !lighting_object.needs_update)
lighting_object.needs_update = TRUE
SSlighting.objects_queue += lighting_object

lighting_object = master_SW?.lighting_object
if (lighting_object && !lighting_object.needs_update)
lighting_object.needs_update = TRUE
SSlighting.objects_queue += lighting_object

lighting_object = master_NW?.lighting_object
if (lighting_object && !lighting_object.needs_update)
lighting_object.needs_update = TRUE
SSlighting.objects_queue += lighting_object

self_destruct_if_idle()

/datum/lighting_corner/dummy/New()
return


/datum/lighting_corner/Destroy(force)
if (!force)
return QDEL_HINT_LETMELIVE

stack_trace("Ok, Look, /tg/, I need you to find whatever fucker decided to call qdel on a fucking lighting corner, then tell him very nicely and politely that he is 100% retarded and needs his head checked. Thanks. Send them my regards by the way.")
for (var/datum/light_source/light_source as anything in affecting)
LAZYREMOVE(light_source.effect_str, src)
affecting = null

if (master_NE)
master_NE.lighting_corner_SW = null
master_NE.lighting_corners_initialised = FALSE
if (master_SE)
master_SE.lighting_corner_NW = null
master_SE.lighting_corners_initialised = FALSE
if (master_SW)
master_SW.lighting_corner_NE = null
master_SW.lighting_corners_initialised = FALSE
if (master_NW)
master_NW.lighting_corner_SE = null
master_NW.lighting_corners_initialised = FALSE
if (needs_update)
SSlighting.corners_queue -= src

return ..()
Loading

0 comments on commit e6cc8d3

Please sign in to comment.