Skip to content

Commit

Permalink
Assorted atmos performance improvements. (ParadiseSS13#27966)
Browse files Browse the repository at this point in the history
  • Loading branch information
FunnyMan3595 authored Jan 20, 2025
1 parent 8954a8e commit fdbd2b2
Show file tree
Hide file tree
Showing 13 changed files with 274 additions and 233 deletions.
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

0 comments on commit fdbd2b2

Please sign in to comment.