From 8f98047bfeba471f6c5532d46583f1c6415d6d9a Mon Sep 17 00:00:00 2001
From: RikuTheKiller <88713943+RikuTheKiller@users.noreply.github.com>
Date: Sat, 17 Jan 2026 00:18:41 +0200
Subject: [PATCH] [TM FIRST] Literally just halves the performance cost of
cameras (#94522)
## About The Pull Request
Title, I used an alist and some inlined list operation tricks to make
camera chunk updates twice as fast.
The data in the screenshots is from me updating every single camera
chunk on Meta. I did also test on Icebox and it scaled just as well
there as it did on Meta, which means that multi-z doesn't cause any
issues for these changes.
The correct way to compare these costs is to take the total cost, divide
it by the count, and then compare them relatively to see which is
higher/lower and by how much.
Before:
After (combined keys):
The value of "update visible turfs" here is just a merged version of
"collect turfs" + "the & operator" + "update visible turfs"
After 2 (separate keys, but fucked up counts):
Not as directly comparable cuz the counts are fucked up, gotta do some
math to figure it out.
The biggest remaining performance bottleneck, which now accounts for
~70% of all the performance cost of camera chunks, is camera.can_see()
being slow as balls. You can see this in the After 2 image.
The only cost of doing this is that the format of the list is now
"alist[turf] = null" instead of "list[i] = turf", which makes checking
for individual turfs cumbersome. Entirely possible and and still
relatively performant, but the syntax is just cumbersome. Said single
checks are only used by SyndEye as of now.
## Why It's Good For The Game
On live, it seems that camera chunks are consistently the biggest and
fattest source of performance loss and overtime in the entire game.
Obviously halving that cost is good for the game.
## Changelog
:cl:
fix: Camera chunks, the biggest source of lag in the entire game, now
run twice as fast.
/:cl:
---
code/game/machinery/camera/camera.dm | 16 +++++++++++-----
.../mob/living/silicon/ai/freelook/chunk.dm | 6 +++---
.../file_system/programs/secureye.dm | 2 +-
3 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index b88acdcbc01..38bdd0e2f00 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -401,19 +401,24 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
return FALSE
return TRUE
-/// Returns a list of turfs in this camera's view.
-/// This includes turfs that are "obscured by darkness" from the camera's POV.
+/// Returns an alist of turfs in this camera's view. This includes turfs that are "obscured by darkness" from the camera's POV.
+/// Format is "alist[turf] = null", if you need to check individual objects use "length(can_see & list(turf))". Only "&" and "in" work for checking contents, but "in" is much slower.
+/// Always have the return value of can_see as the left-hand operand, otherwise it uses list checks instead of alist checks and your CPU time gets thrown in a blender.
/obj/machinery/camera/proc/can_see()
- var/list/see = null
+ var/alist/see = alist()
var/turf/pos = get_turf(src)
var/turf/directly_above = GET_TURF_ABOVE(pos)
var/check_lower = pos != get_lowest_turf(pos)
var/check_higher = directly_above && istransparentturf(directly_above) && (pos != get_highest_turf(pos))
if(isXRay())
- see = RANGE_TURFS(view_range, pos)
+ see += RANGE_TURFS(view_range, pos)
else
- see = get_hear_turfs(view_range, pos)
+ var/lum = pos.luminosity
+ pos.luminosity = 6
+ for(var/turf/turf in view(view_range, pos))
+ see += turf
+ pos.luminosity = lum
if(check_lower || check_higher)
// Haha datum var access KILL ME
@@ -428,6 +433,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
while(above && istransparentturf(above))
see += RANGE_TURFS(1, above)
above = GET_TURF_ABOVE(above)
+
return see
/obj/machinery/camera/proc/Togglelight(on=0)
diff --git a/code/modules/mob/living/silicon/ai/freelook/chunk.dm b/code/modules/mob/living/silicon/ai/freelook/chunk.dm
index 23098ab15e9..5a9c093a31b 100644
--- a/code/modules/mob/living/silicon/ai/freelook/chunk.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/chunk.dm
@@ -121,7 +121,8 @@
if(get_dist(point, current_camera) > CHUNK_SIZE + (CHUNK_SIZE / 2))
continue
- for(var/turf/vis_turf as anything in turfs & current_camera.can_see())
+ // The return value of can_see being the left-hand operand here is a load-bearing performance pillar
+ for(var/turf/vis_turf as anything in current_camera.can_see() & turfs)
updated_visible_turfs[vis_turf] = vis_turf
///new turfs that we couldnt see last update but can now
@@ -164,7 +165,6 @@
client.images += active_static_images
-
/// Create a new camera chunk, since the chunks are made as they are needed.
/datum/camerachunk/New(x, y, lower_z)
x = GET_CHUNK_COORD(x)
@@ -206,7 +206,7 @@
if(!camera.can_use())
continue
- for(var/turf/vis_turf as anything in turfs & camera.can_see())
+ for(var/turf/vis_turf as anything in camera.can_see() & turfs)
visibleTurfs[vis_turf] = vis_turf
for(var/turf/obscured_turf as anything in turfs - visibleTurfs)
diff --git a/code/modules/modular_computers/file_system/programs/secureye.dm b/code/modules/modular_computers/file_system/programs/secureye.dm
index 7a91b9f00aa..491d1402512 100644
--- a/code/modules/modular_computers/file_system/programs/secureye.dm
+++ b/code/modules/modular_computers/file_system/programs/secureye.dm
@@ -174,7 +174,7 @@
CRASH("[src] was able to track [target] through /datum/trackable, but was not on a visible turf to cameras.")
for(var/obj/machinery/camera/cameras as anything in target_camerachunk.cameras[target.z])
// We need to find a particular camera that can see this turf
- if(!(target_turf in cameras.can_see()))
+ if(length(cameras.can_see() & list(target_turf)))
continue
var/new_camera = WEAKREF(cameras)
if(camera_ref == new_camera)