Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assorted atmos performance improvements. #27966

Merged
merged 2 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 23 additions & 15 deletions code/controllers/subsystem/SSair.dm
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@ SUBSYSTEM_DEF(air)
name = "Atmospherics"
init_order = INIT_ORDER_AIR
priority = FIRE_PRIORITY_AIR
wait = 2
flags = SS_BACKGROUND
// The MC really doesn't like it if we sleep (even though it's supposed to), and ends up running us continuously. Instead, we ask it to run us every tick, and "sleep" by skipping the current tick.
wait = 1
flags = SS_BACKGROUND | SS_TICKER
/// How long we actually wait between ticks. Will round up to the next server tick.
var/self_wait = 0.15 SECONDS
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
offline_implications = "Turfs will no longer process atmos, and all atmospheric machines (including cryotubes) will no longer function. Shuttle call recommended."
cpu_display = SS_CPUDISPLAY_HIGH

/// When did we last finish running a complete tick?
var/last_complete_tick = 0
/// When did we last start a tick?
var/last_tick_start = 0

/// How long we took for a full pass through the subsystem. Custom-tracked version of `cost`.
var/datum/resumable_cost_counter/cost_full = new()
/// How long we spent sleeping while waiting for MILLA to finish the last tick, shown in SS Info's C block as ZZZ.
Expand Down Expand Up @@ -178,26 +186,21 @@ SUBSYSTEM_DEF(air)
currentpart = SSair.currentpart
milla_idle = SSair.milla_idle

#define SLEEPABLE_TIMER (world.time + world.tick_usage * world.tick_lag / 100)
/datum/controller/subsystem/air/fire(resumed = 0)
// All atmos stuff assumes MILLA is synchronous. Ensure it actually is.
if(!milla_idle || length(sleepers) > 0)
var/timer = SLEEPABLE_TIMER

while(!milla_idle || length(sleepers) > 0)
// Sleep for 1ms.
sleep(0.01)
var/new_timer = SLEEPABLE_TIMER
time_slept.record_progress((new_timer - timer) * 100, FALSE)
timer = new_timer
var/now = world.timeofday + (world.tick_lag * world.tick_usage) / 100
var/elapsed = now - last_complete_tick
if(!milla_idle || (elapsed >= 0 && elapsed < self_wait))
return

time_slept.record_progress((SLEEPABLE_TIMER - timer) * 100, TRUE)
if(last_tick_start <= last_complete_tick)
last_tick_start = now
time_slept.record_progress(max(0, elapsed) * 100, TRUE)

// Run the sleepless callbacks again in case more showed up since on_milla_tick_finished()
run_sleepless_callbacks()

fire_sleepless(resumed)
#undef SLEEPABLE_TIMER

/datum/controller/subsystem/air/proc/fire_sleepless(resumed)
// Any proc that wants MILLA to be synchronous should not sleep.
Expand Down Expand Up @@ -317,13 +320,15 @@ SUBSYSTEM_DEF(air)
milla_idle = FALSE

cost_milla_tick = MC_AVERAGE(cost_milla_tick, get_milla_tick_time())
cost_full.record_progress(TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer), state != SS_PAUSED && state != SS_PAUSING)
cost_full.record_progress(TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer), FALSE)
if(state == SS_PAUSED || state == SS_PAUSING)
in_milla_safe_code = FALSE
return
resumed = 0

currentpart = SSAIR_DEFERREDPIPENETS
last_complete_tick = world.timeofday + (world.tick_lag * world.tick_usage) / 100
cost_full.record_progress(0, TRUE)
in_milla_safe_code = FALSE

/datum/controller/subsystem/air/proc/build_pipenets(resumed = 0)
Expand Down Expand Up @@ -624,6 +629,9 @@ SUBSYSTEM_DEF(air)
for(var/turf/T as anything in block(low_corner, high_corner))
T.Initialize_Atmos(times_fired)
milla_load_turfs(low_corner, high_corner)
for(var/turf/T as anything in block(low_corner, high_corner))
T.milla_data.len = 0
T.milla_data = null

/datum/controller/subsystem/air/proc/setup_write_to_milla()
var/watch = start_watch()
Expand Down
4 changes: 2 additions & 2 deletions code/game/area/areas.dm
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@
var/list/firealarms
var/firedoors_last_closed_on = 0

/// The air alarm to use for atmos_alert consoles
var/obj/machinery/alarm/master_air_alarm
/// The air alarms present in this area.
var/list/air_alarms = list()
/// The list of vents in our area.
var/list/obj/machinery/atmospherics/unary/vent_pump/vents = list()
/// The list of scrubbers in our area.
Expand Down
41 changes: 22 additions & 19 deletions code/game/machinery/computer/atmos_alert.dm
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,29 @@
parent_area_type = machine_area.get_top_parent_type()

/obj/machinery/computer/atmos_alert/process()
// This is relatively cheap because the areas list is pretty small
for(var/obj/machinery/alarm/air_alarm as anything in GLOB.air_alarms)
if(!((get_area(air_alarm)).type in typesof(parent_area_type)) || air_alarm.z != z)
continue // Not an area we monitor, or outside our z-level
if(!air_alarm.report_danger_level)
alarm_cache = list()
alarm_cache["priority"] = list()
alarm_cache["minor"] = list()
alarm_cache["mode"] = list()
for(var/area/A in GLOB.all_areas)
if(!istype(A, parent_area_type))
continue
switch(air_alarm.alarm_area.atmosalm)
if(ATMOS_ALARM_DANGER)
alarm_cache["priority"] |= air_alarm.alarm_area.name
alarm_cache["minor"] -= air_alarm.alarm_area.name
if(ATMOS_ALARM_WARNING)
alarm_cache["priority"] -= air_alarm.alarm_area.name
alarm_cache["minor"] |= air_alarm.alarm_area.name
else
alarm_cache["priority"] -= air_alarm.alarm_area.name
alarm_cache["minor"] -= air_alarm.alarm_area.name
if(air_alarm.mode == AALARM_MODE_FILTERING)
alarm_cache["mode"] -= air_alarm.alarm_area.name
else
alarm_cache["mode"][air_alarm.alarm_area.name] = GLOB.aalarm_modes["[air_alarm.mode]"]
var/alarm_level = null
for(var/obj/machinery/alarm/air_alarm in A.air_alarms)
if(!istype(air_alarm))
continue
if(!air_alarm.report_danger_level)
continue
switch(air_alarm.alarm_area.atmosalm)
if(ATMOS_ALARM_DANGER)
alarm_level = "priority"
if(ATMOS_ALARM_WARNING)
if(isnull(alarm_level))
alarm_level = "minor"
if(!isnull(alarm_level))
alarm_cache[alarm_level] += A.name
if(air_alarm.mode != AALARM_MODE_FILTERING)
alarm_cache["mode"][A.name] = GLOB.aalarm_modes["[air_alarm.mode]"]

update_icon()

Expand Down
8 changes: 1 addition & 7 deletions code/game/turfs/turf.dm
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,7 @@
/// The effect used to render a pressure overlay from this tile.
var/obj/effect/pressure_overlay/pressure_overlay

var/list/milla_atmos_airtight = list(FALSE, FALSE, FALSE, FALSE)
var/list/milla_superconductivity = list(
OPEN_HEAT_TRANSFER_COEFFICIENT,
OPEN_HEAT_TRANSFER_COEFFICIENT,
OPEN_HEAT_TRANSFER_COEFFICIENT,
OPEN_HEAT_TRANSFER_COEFFICIENT)
var/list/milla_data = list()
var/list/milla_data = null

new_attack_chain = TRUE

Expand Down
22 changes: 12 additions & 10 deletions code/modules/atmospherics/environmental/LINDA_turf_tile.dm
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,9 @@

/turf/proc/Initialize_Atmos(times_fired)
// This is one of two places expected to call this otherwise-unsafe method.
private_unsafe_recalculate_atmos_connectivity()
var/list/connectivity = private_unsafe_recalculate_atmos_connectivity()
var/list/air = list(oxygen, carbon_dioxide, nitrogen, toxins, sleeping_agent, agent_b, temperature)
milla_data = milla_atmos_airtight + list(atmos_mode, SSmapping.environments[atmos_environment]) + air + milla_superconductivity
milla_data = connectivity[1] + list(atmos_mode, SSmapping.environments[atmos_environment]) + air + connectivity[2]

/turf/proc/recalculate_atmos_connectivity()
var/datum/milla_safe/recalculate_atmos_connectivity/milla = new()
Expand All @@ -188,26 +188,26 @@
return

// This is one of two places expected to call this otherwise-unsafe method.
T.private_unsafe_recalculate_atmos_connectivity()
var/list/connectivity = T.private_unsafe_recalculate_atmos_connectivity()

set_tile_airtight(T, T.milla_atmos_airtight)
set_tile_airtight(T, connectivity[1])
reset_superconductivity(T)
reduce_superconductivity(T, T.milla_superconductivity)
reduce_superconductivity(T, connectivity[2])

/// This method is unsafe to use because it only updates milla_* properties, but does not write them to MILLA. Use recalculate_atmos_connectivity() instead.
/turf/proc/private_unsafe_recalculate_atmos_connectivity()
if(blocks_air)
milla_atmos_airtight = list(TRUE, TRUE, TRUE, TRUE)
milla_superconductivity = list(0, 0, 0, 0)
return
var/milla_atmos_airtight = list(TRUE, TRUE, TRUE, TRUE)
var/milla_superconductivity = list(0, 0, 0, 0)
return list(milla_atmos_airtight, milla_superconductivity)

milla_atmos_airtight = list(
var/milla_atmos_airtight = list(
!CanAtmosPass(NORTH, FALSE),
!CanAtmosPass(EAST, FALSE),
!CanAtmosPass(SOUTH, FALSE),
!CanAtmosPass(WEST, FALSE))

milla_superconductivity = list(
var/milla_superconductivity = list(
OPEN_HEAT_TRANSFER_COEFFICIENT,
OPEN_HEAT_TRANSFER_COEFFICIENT,
OPEN_HEAT_TRANSFER_COEFFICIENT,
Expand All @@ -230,6 +230,8 @@
milla_superconductivity[INDEX_SOUTH] = min(milla_superconductivity[INDEX_SOUTH], O.get_superconductivity(SOUTH))
milla_superconductivity[INDEX_WEST] = min(milla_superconductivity[INDEX_WEST], O.get_superconductivity(WEST))

return list(milla_atmos_airtight, milla_superconductivity)

/obj/effect/wind
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
Expand Down
90 changes: 65 additions & 25 deletions code/modules/atmospherics/gasmixtures/gas_mixture.dm
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,10 @@ What are the archived variables for?

/// Calculate moles
/datum/gas_mixture/proc/total_moles()
var/moles = private_oxygen + private_carbon_dioxide + private_nitrogen + private_toxins + private_sleeping_agent + private_agent_b
return moles
return private_oxygen + private_carbon_dioxide + private_nitrogen + private_toxins + private_sleeping_agent + private_agent_b

/datum/gas_mixture/proc/total_trace_moles()
var/moles = private_agent_b
return moles
return private_agent_b

/// Calculate pressure in kilopascals
/datum/gas_mixture/proc/return_pressure()
Expand Down Expand Up @@ -662,23 +660,26 @@ What are the archived variables for?

/proc/share_many_airs(list/mixtures)
var/total_volume = 0
var/total_thermal_energy = 0
var/total_heat_capacity = 0
var/total_oxygen = 0
var/total_nitrogen = 0
var/total_toxins = 0
var/total_carbon_dioxide = 0
var/total_sleeping_agent = 0
var/total_agent_b = 0
var/must_share = FALSE

// Collect all the cheap data and check if there's a significant temperature difference.
var/temperature = null
for(var/datum/gas_mixture/G as anything in mixtures)
if(!istype(G))
stack_trace("share_many_airs had [G] in mixtures ([json_encode(mixtures)])")
continue
total_volume += G.volume
var/heat_capacity = G.heat_capacity()
total_heat_capacity += heat_capacity
total_thermal_energy += G.private_temperature * heat_capacity

if(isnull(temperature))
temperature = G.private_temperature
else if(abs(temperature - G.private_temperature) >= 1)
must_share = TRUE

total_oxygen += G.private_oxygen
total_nitrogen += G.private_nitrogen
Expand All @@ -687,26 +688,65 @@ What are the archived variables for?
total_sleeping_agent += G.private_sleeping_agent
total_agent_b += G.private_agent_b

if(total_volume > 0)
//Calculate temperature
var/temperature = 0

if(total_heat_capacity > 0)
temperature = total_thermal_energy/total_heat_capacity
if(total_volume <= 0)
return

//Update individual gas_mixtures by volume ratio
// If we don't have a significant temperature difference, check for a significant gas amount difference.
if(!must_share)
for(var/datum/gas_mixture/G as anything in mixtures)
if(!istype(G))
continue
G.private_oxygen = total_oxygen * G.volume / total_volume
G.private_nitrogen = total_nitrogen * G.volume / total_volume
G.private_toxins = total_toxins * G.volume / total_volume
G.private_carbon_dioxide = total_carbon_dioxide * G.volume / total_volume
G.private_sleeping_agent = total_sleeping_agent * G.volume / total_volume
G.private_agent_b = total_agent_b * G.volume / total_volume

G.private_temperature = temperature
G.set_dirty()
if(abs(G.private_oxygen - total_oxygen * G.volume / total_volume) > 0.1)
must_share = TRUE
break
if(abs(G.private_nitrogen - total_nitrogen * G.volume / total_volume) > 0.1)
must_share = TRUE
break
if(abs(G.private_toxins - total_toxins * G.volume / total_volume) > 0.1)
must_share = TRUE
break
if(abs(G.private_carbon_dioxide - total_carbon_dioxide * G.volume / total_volume) > 0.1)
must_share = TRUE
break
if(abs(G.private_sleeping_agent - total_sleeping_agent * G.volume / total_volume) > 0.1)
must_share = TRUE
break
if(abs(G.private_agent_b - total_agent_b * G.volume / total_volume) > 0.1)
must_share = TRUE
break

if(!must_share)
// Nothing significant, don't do any more work.
return

// Collect the more expensive data.
var/total_thermal_energy = 0
var/total_heat_capacity = 0
for(var/datum/gas_mixture/G as anything in mixtures)
if(!istype(G))
continue
var/heat_capacity = G.heat_capacity()
total_heat_capacity += heat_capacity
total_thermal_energy += G.private_temperature * heat_capacity

// Calculate shared temperature.
temperature = TCMB
if(total_heat_capacity > 0)
temperature = total_thermal_energy/total_heat_capacity

// Update individual gas_mixtures by volume ratio.
for(var/datum/gas_mixture/G as anything in mixtures)
if(!istype(G))
continue
G.private_oxygen = total_oxygen * G.volume / total_volume
G.private_nitrogen = total_nitrogen * G.volume / total_volume
G.private_toxins = total_toxins * G.volume / total_volume
G.private_carbon_dioxide = total_carbon_dioxide * G.volume / total_volume
G.private_sleeping_agent = total_sleeping_agent * G.volume / total_volume
G.private_agent_b = total_agent_b * G.volume / total_volume

G.private_temperature = temperature
// In theory, we should G.set_dirty() here, but that's only useful for bound mixtures, and these can't be.

/datum/gas_mixture/proc/hotspot_expose(temperature, volume)
return
Expand Down
Loading
Loading