diff --git a/code/__DEFINES/stat_tracking.dm b/code/__DEFINES/stat_tracking.dm index 3936c5451ea..00b2d2c7b18 100644 --- a/code/__DEFINES/stat_tracking.dm +++ b/code/__DEFINES/stat_tracking.dm @@ -36,10 +36,4 @@ } while(FALSE); \ usage = TICK_USAGE; -#define SET_COST_LINE(...) \ - do { \ - var/cost = TICK_USAGE; \ - _costs["[__LINE__ ]"] += TICK_DELTA_TO_MS(cost - usage); \ - _counting["[__LINE__ ]"] += 1; \ - usage = TICK_USAGE; \ - } while(FALSE) +#define SET_COST_LINE(...) SET_COST("[__LINE__]") diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm index 1c4884b3b06..f79136f678b 100644 --- a/code/controllers/subsystem/lighting.dm +++ b/code/controllers/subsystem/lighting.dm @@ -29,13 +29,13 @@ SUBSYSTEM_DEF(lighting) MC_SPLIT_TICK_INIT(3) if(!init_tick_checks) MC_SPLIT_TICK + var/list/queue = sources_queue var/i = 0 for (i in 1 to length(queue)) var/datum/light_source/L = queue[i] L.update_corners() - L.needs_update = LIGHTING_NO_UPDATE if(init_tick_checks) diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm index 4abaa9e640e..5fb2cdf2a3c 100644 --- a/code/datums/mutable_appearance.dm +++ b/code/datums/mutable_appearance.dm @@ -4,10 +4,12 @@ // Mutable appearances are children of images, just so you know. -/mutable_appearance/New() +// Mutable appearances erase template vars on new, because they accept an appearance to copy as an arg +// If we have nothin to copy, we set the float plane +/mutable_appearance/New(mutable_appearance/to_copy) ..() - plane = FLOAT_PLANE // No clue why this is 0 by default yet images are on FLOAT_PLANE - // And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var + if(!to_copy) + plane = FLOAT_PLANE /// Helper similar to image() /proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, plane = FLOAT_PLANE, alpha = 255, appearance_flags = NONE) diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 572cd8b50d7..a72d1c5f5cc 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -29,50 +29,48 @@ ///whether we are to be added to SSlighting's corners_queue list for an update var/needs_update = FALSE -/datum/lighting_corner/New(turf/new_turf, diagonal) +// Takes as an argument the coords to use as the bottom left (south west) of our corner +/datum/lighting_corner/New(x, y, 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. + src.x = x + 0.5 + src.y = y + 0.5 - x = new_turf.x + (horizontal == EAST ? 0.5 : -0.5) - y = new_turf.y + (vertical == NORTH ? 0.5 : -0.5) + // Alright. We're gonna take a set of coords, and from them do a loop clockwise + // To build out the turfs adjacent to us. This is pretty fast + var/turf/process_next = locate(x, y, z) + if(process_next) + master_SW = process_next + process_next.lighting_corner_NE = src + // Now, we go north! + process_next = get_step(process_next, NORTH) + else + // Yes this is slightly slower then having a guarenteeed turf, but there aren't many null turfs + // So this is pretty damn fast + process_next = locate(x, y + 1, z) - // My initial plan was to make this loop through a list of all the dirs (horizontal, vertical, diagonal). - // 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/new_master_turf + // Ok, if we have a north turf, go there. otherwise, onto the next + if(process_next) + master_NW = process_next + process_next.lighting_corner_SE = src + // Now, TO THE EAST + process_next = get_step(process_next, EAST) + else + process_next = locate(x + 1, y + 1, z) - // Diagonal one is easy. - new_master_turf = get_step(new_turf, diagonal) - if (new_master_turf) // In case we're on the map's border. - save_master(new_master_turf, diagonal) + // Etc etc + if(process_next) + master_NE = process_next + process_next.lighting_corner_SW = src + // Now, TO THE SOUTH AGAIN (SE) + process_next = get_step(process_next, SOUTH) + else + process_next = locate(x + 1, y, z) - // Now the horizontal one. - new_master_turf = get_step(new_turf, horizontal) - if (new_master_turf) // Ditto. - save_master(new_master_turf, ((new_master_turf.x > x) ? EAST : WEST) | ((new_master_turf.y > y) ? NORTH : SOUTH)) // Get the dir based on coordinates. - - // And finally the vertical one. - new_master_turf = get_step(new_turf, vertical) - if (new_master_turf) - save_master(new_master_turf, ((new_master_turf.x > x) ? EAST : WEST) | ((new_master_turf.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 + // anddd the last tile + if(process_next) + master_SE = process_next + process_next.lighting_corner_NW = src /datum/lighting_corner/proc/self_destruct_if_idle() if (!LAZYLEN(affecting)) diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm index 9004e35b204..050072f5e78 100644 --- a/code/modules/lighting/lighting_object.dm +++ b/code/modules/lighting/lighting_object.dm @@ -8,14 +8,18 @@ ///the turf that our light is applied to var/turf/affected_turf +// Global list of lighting underlays, indexed by z level +GLOBAL_LIST_EMPTY(default_lighting_underlays_by_z) + /datum/lighting_object/New(turf/source) if(!isturf(source)) qdel(src, force=TRUE) stack_trace("a lighting object was assigned to [source], a non turf! ") return + . = ..() - current_underlay = mutable_appearance(LIGHTING_ICON, "transparent", source.z, LIGHTING_PLANE, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM) + current_underlay = new(GLOB.default_lighting_underlays_by_z[source.z]) affected_turf = source if (affected_turf.lighting_object) @@ -25,8 +29,11 @@ affected_turf.lighting_object = src affected_turf.luminosity = 0 - for(var/turf/open/space/space_tile in RANGE_TURFS(1, affected_turf)) - space_tile.update_starlight() + // This path is really hot. this is faster + // Really this should be a global var or something, but lets not think about that yes? + if(CONFIG_GET(flag/starlight)) + for(var/turf/open/space/space_tile in RANGE_TURFS(1, affected_turf)) + space_tile.update_starlight() needs_update = TRUE SSlighting.objects_queue += src @@ -59,6 +66,7 @@ var/static/datum/lighting_corner/dummy/dummy_lighting_corner = new + var/turf/affected_turf = src.affected_turf var/datum/lighting_corner/red_corner = affected_turf.lighting_corner_SW || dummy_lighting_corner var/datum/lighting_corner/green_corner = affected_turf.lighting_corner_SE || dummy_lighting_corner var/datum/lighting_corner/blue_corner = affected_turf.lighting_corner_NW || dummy_lighting_corner @@ -66,21 +74,6 @@ var/max = max(red_corner.largest_color_luminosity, green_corner.largest_color_luminosity, blue_corner.largest_color_luminosity, alpha_corner.largest_color_luminosity) - var/rr = red_corner.cache_r - var/rg = red_corner.cache_g - var/rb = red_corner.cache_b - - var/gr = green_corner.cache_r - var/gg = green_corner.cache_g - var/gb = green_corner.cache_b - - var/br = blue_corner.cache_r - var/bg = blue_corner.cache_g - var/bb = blue_corner.cache_b - - var/ar = alpha_corner.cache_r - var/ag = alpha_corner.cache_g - var/ab = alpha_corner.cache_b #if LIGHTING_SOFT_THRESHOLD != 0 var/set_luminosity = max > LIGHTING_SOFT_THRESHOLD @@ -90,28 +83,28 @@ var/set_luminosity = max > 1e-6 #endif - if((rr & gr & br & ar) && (rg + gg + bg + ag + rb + gb + bb + ab == 8)) + var/mutable_appearance/current_underlay = src.current_underlay + affected_turf.underlays -= current_underlay + if(red_corner.cache_r & green_corner.cache_r & blue_corner.cache_r & alpha_corner.cache_r && \ + (red_corner.cache_g + green_corner.cache_g + blue_corner.cache_g + alpha_corner.cache_g + \ + red_corner.cache_b + green_corner.cache_b + blue_corner.cache_b + alpha_corner.cache_b == 8)) //anything that passes the first case is very likely to pass the second, and addition is a little faster in this case - affected_turf.underlays -= current_underlay current_underlay.icon_state = "lighting_transparent" current_underlay.color = null - affected_turf.underlays += current_underlay else if(!set_luminosity) - affected_turf.underlays -= current_underlay current_underlay.icon_state = "lighting_dark" current_underlay.color = null - affected_turf.underlays += current_underlay else - affected_turf.underlays -= current_underlay current_underlay.icon_state = null current_underlay.color = list( - rr, rg, rb, 00, - gr, gg, gb, 00, - br, bg, bb, 00, - ar, ag, ab, 00, + red_corner.cache_r, red_corner.cache_g, red_corner.cache_b, 00, + green_corner.cache_r, green_corner.cache_g, green_corner.cache_b, 00, + blue_corner.cache_r, blue_corner.cache_g, blue_corner.cache_b, 00, + alpha_corner.cache_r, alpha_corner.cache_g, alpha_corner.cache_b, 00, 00, 00, 00, 01 ) - affected_turf.underlays += current_underlay - + // Of note. Most of the cost in this proc is here, I think because color matrix'd underlays DO NOT cache well, which is what adding to underlays does + // We use underlays because objects on each tile would fuck with maptick. if that ever changes, use an object for this instead + affected_turf.underlays += current_underlay affected_turf.luminosity = set_luminosity diff --git a/code/modules/lighting/lighting_setup.dm b/code/modules/lighting/lighting_setup.dm index 78fde88899e..ff716ca4bdd 100644 --- a/code/modules/lighting/lighting_setup.dm +++ b/code/modules/lighting/lighting_setup.dm @@ -4,6 +4,7 @@ if(!A.static_lighting) continue + // I hate this so much dude. why do areas not track their turfs lummyyyyyyy for(var/turf/T in A) if(T.always_lit) continue diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index f5a349a7807..69cefd48d14 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -140,31 +140,53 @@ // 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 LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range))) + +// This exists so we can cache the vars used in this macro, and save MASSIVE time :) +// Most of this is saving off datum var accesses, tho some of it does actually cache computation +// You will NEED to call this before you call APPLY_CORNER +#define SETUP_CORNERS_CACHE(lighting_source) \ + var/_turf_x = lighting_source.pixel_turf.x; \ + var/_turf_y = lighting_source.pixel_turf.y; \ + var/_range_divisor = max(1, lighting_source.light_range); \ + var/_light_power = lighting_source.light_power; \ + var/_applied_lum_r = lighting_source.applied_lum_r; \ + var/_applied_lum_g = lighting_source.applied_lum_g; \ + var/_applied_lum_b = lighting_source.applied_lum_b; \ + var/_lum_r = lighting_source.lum_r; \ + var/_lum_g = lighting_source.lum_g; \ + var/_lum_b = lighting_source.lum_b; \ + +#define SETUP_CORNERS_REMOVAL_CACHE(lighting_source) \ + var/_applied_lum_r = lighting_source.applied_lum_r; \ + var/_applied_lum_g = lighting_source.applied_lum_g; \ + var/_applied_lum_b = lighting_source.applied_lum_b; + +#define LUM_FALLOFF(C) (1 - CLAMP01(sqrt((C.x - _turf_x) ** 2 + (C.y - _turf_y) ** 2 + LIGHTING_HEIGHT) / _range_divisor)) #define APPLY_CORNER(C) \ - . = LUM_FALLOFF(C, pixel_turf); \ - . *= light_power; \ + . = LUM_FALLOFF(C); \ + . *= _light_power; \ var/OLD = 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_r) - (OLD * _applied_lum_r), \ + (. * _lum_g) - (OLD * _applied_lum_g), \ + (. * _lum_b) - (OLD * _applied_lum_b) \ ); \ #define REMOVE_CORNER(C) \ . = -effect_str[C]; \ C.update_lumcount \ ( \ - . * applied_lum_r, \ - . * applied_lum_g, \ - . * applied_lum_b \ + . * _applied_lum_r, \ + . * _applied_lum_g, \ + . * _applied_lum_b \ ); /// This is the define used to calculate falloff. /datum/light_source/proc/remove_lum() + SETUP_CORNERS_REMOVAL_CACHE(src) applied = FALSE for (var/datum/lighting_corner/corner as anything in effect_str) REMOVE_CORNER(corner) @@ -173,6 +195,7 @@ effect_str = null /datum/light_source/proc/recalc_corner(datum/lighting_corner/corner) + SETUP_CORNERS_CACHE(src) LAZYINITLIST(effect_str) if (effect_str[corner]) // Already have one. REMOVE_CORNER(corner) @@ -182,6 +205,22 @@ effect_str[corner] = . +// Keep in mind. Lighting corners accept the bottom left (northwest) set of cords to them as input +#define GENERATE_MISSING_CORNERS(gen_for) \ + if (!gen_for.lighting_corner_NE) { \ + gen_for.lighting_corner_NE = new /datum/lighting_corner(gen_for.x, gen_for.y, gen_for.z); \ + } \ + if (!gen_for.lighting_corner_SE) { \ + gen_for.lighting_corner_SE = new /datum/lighting_corner(gen_for.x, gen_for.y - 1, gen_for.z); \ + } \ + if (!gen_for.lighting_corner_SW) { \ + gen_for.lighting_corner_SW = new /datum/lighting_corner(gen_for.x - 1, gen_for.y - 1, gen_for.z); \ + } \ + if (!gen_for.lighting_corner_NW) { \ + gen_for.lighting_corner_NW = new /datum/lighting_corner(gen_for.x - 1, gen_for.y, gen_for.z); \ + } \ + gen_for.lighting_corners_initialised = TRUE; + /datum/light_source/proc/update_corners() var/update = FALSE var/atom/source_atom = src.source_atom @@ -244,24 +283,27 @@ return //nothing's changed var/list/datum/lighting_corner/corners = list() - var/list/turf/turfs = list() if (source_turf) var/oldlum = source_turf.luminosity source_turf.luminosity = CEILING(light_range, 1) for(var/turf/T in view(CEILING(light_range, 1), source_turf)) - if(!IS_OPAQUE_TURF(T)) - if (!T.lighting_corners_initialised) - T.generate_missing_corners() - corners[T.lighting_corner_NE] = 0 - corners[T.lighting_corner_SE] = 0 - corners[T.lighting_corner_SW] = 0 - corners[T.lighting_corner_NW] = 0 - turfs += T + if(IS_OPAQUE_TURF(T)) + continue + if (!T.lighting_corners_initialised) + GENERATE_MISSING_CORNERS(T) + + corners[T.lighting_corner_NE] = 0 + corners[T.lighting_corner_SE] = 0 + corners[T.lighting_corner_SW] = 0 + corners[T.lighting_corner_NW] = 0 source_turf.luminosity = oldlum - var/list/datum/lighting_corner/new_corners = (corners - effect_str) - LAZYINITLIST(effect_str) + SETUP_CORNERS_CACHE(src) + + var/list/datum/lighting_corner/new_corners = (corners - src.effect_str) + LAZYINITLIST(src.effect_str) + var/list/effect_str = src.effect_str if (needs_update == LIGHTING_VIS_UPDATE) for (var/datum/lighting_corner/corner as anything in new_corners) APPLY_CORNER(corner) @@ -274,14 +316,15 @@ if (. != 0) LAZYADD(corner.affecting, src) effect_str[corner] = . - - for (var/datum/lighting_corner/corner as anything in corners - new_corners) // Existing corners - APPLY_CORNER(corner) - if (. != 0) - effect_str[corner] = . - else - LAZYREMOVE(corner.affecting, src) - effect_str -= corner + // New corners are a subset of corners. so if they're both the same length, there are NO old corners! + if(length(corners) != length(new_corners)) + for (var/datum/lighting_corner/corner as anything in corners - new_corners) // Existing corners + APPLY_CORNER(corner) + if (. != 0) + effect_str[corner] = . + else + LAZYREMOVE(corner.affecting, src) + effect_str -= corner var/list/datum/lighting_corner/gone_corners = effect_str - corners for (var/datum/lighting_corner/corner as anything in gone_corners) @@ -293,9 +336,12 @@ applied_lum_g = lum_g applied_lum_b = lum_b - UNSETEMPTY(effect_str) + UNSETEMPTY(src.effect_str) #undef EFFECT_UPDATE #undef LUM_FALLOFF #undef REMOVE_CORNER #undef APPLY_CORNER +#undef SETUP_CORNERS_REMOVAL_CACHE +#undef SETUP_CORNERS_CACHE +#undef GENERATE_MISSING_CORNERS diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index a953bc45f94..f9aba7eadd5 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -100,17 +100,3 @@ else lighting_clear_overlay() -/turf/proc/generate_missing_corners() - if (!lighting_corner_NE) - lighting_corner_NE = new/datum/lighting_corner(src, NORTH|EAST) - - if (!lighting_corner_SE) - lighting_corner_SE = new/datum/lighting_corner(src, SOUTH|EAST) - - if (!lighting_corner_SW) - lighting_corner_SW = new/datum/lighting_corner(src, SOUTH|WEST) - - if (!lighting_corner_NW) - lighting_corner_NW = new/datum/lighting_corner(src, NORTH|WEST) - - lighting_corners_initialised = TRUE diff --git a/code/modules/mapping/space_management/space_level.dm b/code/modules/mapping/space_management/space_level.dm index 5e555ebb41a..6be4f5b8f55 100644 --- a/code/modules/mapping/space_management/space_level.dm +++ b/code/modules/mapping/space_management/space_level.dm @@ -18,4 +18,8 @@ else // in case a single trait is passed in SSmapping.z_trait_levels[new_traits] += list(new_z) + if(length(GLOB.default_lighting_underlays_by_z) < z_value) + GLOB.default_lighting_underlays_by_z.len = z_value + GLOB.default_lighting_underlays_by_z[z_value] = mutable_appearance(LIGHTING_ICON, "transparent", new_z, LIGHTING_PLANE, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM) + set_linkage(new_traits[ZTRAIT_LINKAGE])