Files
RikuTheKillerandGitHub 8f98047bfe [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:
<img width="415" height="306" alt="image"
src="https://github.com/user-attachments/assets/c3be5986-e405-412d-a9e0-cc34014a7dab"
/>

After (combined keys):
The value of "update visible turfs" here is just a merged version of
"collect turfs" + "the & operator" + "update visible turfs"
<img width="356" height="271" alt="image"
src="https://github.com/user-attachments/assets/93ca0e9a-bf17-4b87-a4f0-c70c70148ab0"
/>

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.
<img width="414" height="293" alt="image"
src="https://github.com/user-attachments/assets/205d4842-4294-4130-a7dd-69d196d318e9"
/>

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
🆑
fix: Camera chunks, the biggest source of lag in the entire game, now
run twice as fast.
/🆑
2026-01-16 17:18:41 -05:00

242 lines
8.0 KiB
Plaintext

#define DEFAULT_MAP_SIZE 15
/datum/computer_file/program/secureye
filename = "secureye"
filedesc = "SecurEye"
downloader_category = PROGRAM_CATEGORY_SECURITY
ui_header = "borg_mon.gif"
program_open_overlay = "generic"
extended_desc = "This program allows access to standard security camera networks."
program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
download_access = list(ACCESS_SECURITY)
can_run_on_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP
size = 5
tgui_id = "NtosSecurEye"
program_icon = "eye"
always_update_ui = TRUE
///Boolean on whether or not the app will make noise when flipping around the channels.
var/spying = FALSE
var/list/network = list(CAMERANET_NETWORK_SS13)
///List of weakrefs of all users watching the program.
var/list/concurrent_users = list()
/// Weakref to the active camera
var/datum/weakref/camera_ref
/// The turf where the camera was last updated.
var/turf/last_camera_turf
// Stuff needed to render the map
var/atom/movable/screen/map_view/camera/cam_screen
///Internal tracker used to find a specific person and keep them on cameras, only used if this is a 'spying' console.
var/datum/trackable/internal_tracker
///Syndicate subtype that has no access restrictions and is available on Syndinet
/datum/computer_file/program/secureye/syndicate
filename = "syndeye"
filedesc = "SyndEye"
extended_desc = "This program allows for illegal access to security camera networks."
download_access = null
can_run_on_flags = PROGRAM_ALL
program_flags = PROGRAM_ON_SYNDINET_STORE | PROGRAM_UNIQUE_COPY
network = list(
CAMERANET_NETWORK_SS13,
CAMERANET_NETWORK_MINE,
CAMERANET_NETWORK_RD,
CAMERANET_NETWORK_LABOR,
CAMERANET_NETWORK_ORDNANCE,
CAMERANET_NETWORK_MINISAT,
)
spying = TRUE
///Human AI subtype that has access to most networks on the station and can't be copied.
/datum/computer_file/program/secureye/human_ai
filename = "Overseer"
filedesc = "OverSeer"
run_access = list(ACCESS_MINISAT)
can_run_on_flags = PROGRAM_PDA
program_flags = PROGRAM_UNIQUE_COPY
network = list(
CAMERANET_NETWORK_SS13,
CAMERANET_NETWORK_MINE,
CAMERANET_NETWORK_RD,
CAMERANET_NETWORK_LABOR,
CAMERANET_NETWORK_ORDNANCE,
CAMERANET_NETWORK_MINISAT,
)
spying = TRUE
/datum/computer_file/program/secureye/on_install(datum/computer_file/source, obj/item/modular_computer/computer_installing, mob/user)
. = ..()
// Map name has to start and end with an A-Z character,
// and definitely NOT with a square bracket or even a number.
var/map_name = "camera_console_[REF(src)]_map"
// Convert networks to lowercase
for(var/i in network)
network -= i
network += LOWER_TEXT(i)
// Initialize map objects
cam_screen = new
cam_screen.generate_view(map_name)
/datum/computer_file/program/secureye/Destroy()
QDEL_NULL(cam_screen)
QDEL_NULL(internal_tracker)
last_camera_turf = null
return ..()
/datum/computer_file/program/secureye/kill_program(mob/user)
if(user)
ui_close(user)
return ..()
/datum/computer_file/program/secureye/ui_interact(mob/user, datum/tgui/ui)
// Update the camera, showing static if necessary and updating data if the location has moved.
update_active_camera_screen()
var/user_ref = REF(user)
var/is_living = isliving(user)
// Ghosts shouldn't count towards concurrent users, which produces
// an audible terminal_on click.
if(is_living)
concurrent_users += user_ref
// Register map objects
cam_screen.display_to(user, ui.window)
/datum/computer_file/program/secureye/ui_status(mob/user, datum/ui_state/state)
. = ..()
if(. == UI_DISABLED)
return UI_CLOSE
return .
/datum/computer_file/program/secureye/ui_data()
var/list/data = list()
data["activeCamera"] = null
var/obj/machinery/camera/active_camera = camera_ref?.resolve()
if(active_camera)
data["activeCamera"] = list(
name = active_camera.c_tag,
ref = REF(active_camera),
status = active_camera.camera_enabled,
)
return data
/datum/computer_file/program/secureye/ui_static_data(mob/user)
var/list/data = list()
data["network"] = network
data["mapRef"] = cam_screen.assigned_map
data["can_spy"] = !!spying
data["cameras"] = SScameras.get_available_cameras_data(network)
return data
/datum/computer_file/program/secureye/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
switch(action)
if("switch_camera")
var/obj/machinery/camera/active_camera = camera_ref?.resolve()
if(!spying && active_camera)
active_camera.on_stop_watching(src)
if(!spying)
playsound(computer, SFX_TERMINAL_TYPE, 25, FALSE)
var/obj/machinery/camera/selected_camera = locate(params["camera"]) in SScameras.cameras
if(selected_camera)
camera_ref = WEAKREF(selected_camera)
else
camera_ref = null
return TRUE
if(!spying)
selected_camera.on_start_watching(src)
if(internal_tracker)
internal_tracker.reset_tracking()
update_active_camera_screen()
return TRUE
if("start_tracking")
if(!internal_tracker)
internal_tracker = new(src)
RegisterSignal(internal_tracker, COMSIG_TRACKABLE_TRACKING_TARGET, PROC_REF(on_track_target))
internal_tracker.track_input(usr)
return TRUE
/datum/computer_file/program/secureye/proc/on_track_target(datum/trackable/source, mob/living/target)
SIGNAL_HANDLER
var/target_turf = get_turf(target)
var/datum/camerachunk/target_camerachunk = SScameras.get_turf_camera_chunk(target_turf)
if(!target_camerachunk)
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(length(cameras.can_see() & list(target_turf)))
continue
var/new_camera = WEAKREF(cameras)
if(camera_ref == new_camera)
return
camera_ref = new_camera
update_active_camera_screen()
return
/datum/computer_file/program/secureye/ui_close(mob/user)
. = ..()
//don't track anyone while we're shutting off.
if(internal_tracker)
internal_tracker.reset_tracking()
var/user_ref = REF(user)
var/is_living = isliving(user)
// Living creature or not, we remove you anyway.
concurrent_users -= user_ref
// Unregister map objects
cam_screen.hide_from(user)
// Turn off the console
if(length(concurrent_users) == 0 && is_living)
var/obj/machinery/camera/active_camera = camera_ref?.resolve()
if(!spying && active_camera)
active_camera.on_stop_watching(src)
camera_ref = null
last_camera_turf = null
if(!spying)
playsound(computer, 'sound/machines/terminal/terminal_off.ogg', 25, FALSE)
/datum/computer_file/program/secureye/proc/update_active_camera_screen()
var/obj/machinery/camera/active_camera = camera_ref?.resolve()
// Show static if can't use the camera
if(!active_camera?.can_use())
cam_screen.show_camera_static()
return
var/list/visible_turfs = list()
// Get the camera's turf to correctly gather what's visible from its turf, in case it's located in a moving object (borgs / mechs)
var/new_cam_turf = get_turf(active_camera)
// If we're not forcing an update for some reason and the cameras are in the same location,
// we don't need to update anything.
// Most security cameras will end here as they're not moving.
if(last_camera_turf == new_cam_turf)
return
// Cameras that get here are moving, and are likely attached to some moving atom such as cyborgs.
last_camera_turf = new_cam_turf
//Here we gather what's visible from the camera's POV based on its view_range and xray modifier if present
var/list/visible_things = active_camera.isXRay(ignore_malf_upgrades = TRUE) ? range(active_camera.view_range, new_cam_turf) : view(active_camera.view_range, new_cam_turf)
for(var/turf/visible_turf in visible_things)
visible_turfs += visible_turf
//Get coordinates for a rectangle area that contains the turfs we see so we can then clear away the static in the resulting rectangle area
var/list/bbox = get_bbox_of_atoms(visible_turfs)
var/size_x = bbox[3] - bbox[1] + 1
var/size_y = bbox[4] - bbox[2] + 1
cam_screen.show_camera(visible_turfs, size_x, size_y)
#undef DEFAULT_MAP_SIZE