Ported the core of the overlays management subsystem from /tg

- Added SSoverlays subsystem for compiling overlay lists and applying them to atoms in a controlled anti-lag subsystem.
- Added vars and procs to atom which should eventually replace all direct interaction with BYOND's /atom/overlays var outside the subsystem.
- Added OVERLAY_QUEUED flag to var/atom/flags bitfield.
- Added small framework for subsystem performance tracking. So far used only by SSoverlays
- Added admin debug command "Display overlay Log" to see performance stats mid-round.
This commit is contained in:
Leshana
2018-02-24 17:40:06 -05:00
parent 9b36c4da49
commit f2f630fa89
10 changed files with 343 additions and 5 deletions

View File

@@ -36,7 +36,7 @@
#define ACCESSORY_SLOT_ARMOR_M "Misc armor" #define ACCESSORY_SLOT_ARMOR_M "Misc armor"
#define ACCESSORY_SLOT_HELM_C "Helmet cover" #define ACCESSORY_SLOT_HELM_C "Helmet cover"
// Flags bitmasks. // Flags bitmasks. - Used in /atom/var/flags
#define NOBLUDGEON 0x1 // When an item has this it produces no "X has been hit by Y with Z" message with the default handler. #define NOBLUDGEON 0x1 // When an item has this it produces no "X has been hit by Y with Z" message with the default handler.
#define CONDUCT 0x2 // Conducts electricity. (metal etc.) #define CONDUCT 0x2 // Conducts electricity. (metal etc.)
#define ON_BORDER 0x4 // Item has priority to check when entering or leaving. #define ON_BORDER 0x4 // Item has priority to check when entering or leaving.
@@ -45,8 +45,9 @@
#define PHORONGUARD 0x20 // Does not get contaminated by phoron. #define PHORONGUARD 0x20 // Does not get contaminated by phoron.
#define NOREACT 0x40 // Reagents don't react inside this container. #define NOREACT 0x40 // Reagents don't react inside this container.
#define PROXMOVE 0x80 // Does this object require proximity checking in Enter()? #define PROXMOVE 0x80 // Does this object require proximity checking in Enter()?
#define OVERLAY_QUEUED 0x100 // Atom queued to SSoverlay for COMPILE_OVERLAYS
//Flags for items (equipment) //Flags for items (equipment) - Used in /obj/item/var/item_flags
#define THICKMATERIAL 0x1 // Prevents syringes, parapens and hyposprays if equipped to slot_suit or slot_head. #define THICKMATERIAL 0x1 // Prevents syringes, parapens and hyposprays if equipped to slot_suit or slot_head.
#define STOPPRESSUREDAMAGE 0x2 // Counts towards pressure protection. Note that like temperature protection, body_parts_covered is considered here as well. #define STOPPRESSUREDAMAGE 0x2 // Counts towards pressure protection. Note that like temperature protection, body_parts_covered is considered here as well.
#define AIRTIGHT 0x4 // Functions with internals. #define AIRTIGHT 0x4 // Functions with internals.
@@ -54,13 +55,13 @@
#define BLOCK_GAS_SMOKE_EFFECT 0x10 // Blocks the effect that chemical clouds would have on a mob -- glasses, mask and helmets ONLY! (NOTE: flag shared with ONESIZEFITSALL) #define BLOCK_GAS_SMOKE_EFFECT 0x10 // Blocks the effect that chemical clouds would have on a mob -- glasses, mask and helmets ONLY! (NOTE: flag shared with ONESIZEFITSALL)
#define FLEXIBLEMATERIAL 0x20 // At the moment, masks with this flag will not prevent eating even if they are covering your face. #define FLEXIBLEMATERIAL 0x20 // At the moment, masks with this flag will not prevent eating even if they are covering your face.
// Flags for pass_flags. // Flags for pass_flags. - Used in /atom/var/pass_flags
#define PASSTABLE 0x1 #define PASSTABLE 0x1
#define PASSGLASS 0x2 #define PASSGLASS 0x2
#define PASSGRILLE 0x4 #define PASSGRILLE 0x4
#define PASSBLOB 0x8 #define PASSBLOB 0x8
// Bitmasks for the flags_inv variable. These determine when a piece of clothing hides another, i.e. a helmet hiding glasses. // Bitmasks for the /obj/item/var/flags_inv variable. These determine when a piece of clothing hides another, i.e. a helmet hiding glasses.
// WARNING: The following flags apply only to the external suit! // WARNING: The following flags apply only to the external suit!
#define HIDEGLOVES 0x1 #define HIDEGLOVES 0x1
#define HIDESUITSTORAGE 0x2 #define HIDESUITSTORAGE 0x2

View File

@@ -9,6 +9,8 @@
#define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK)) #define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK))
#define MIDNIGHT_ROLLOVER_CHECK ( rollovercheck_last_timeofday != world.timeofday ? update_midnight_rollover() : midnight_rollovers ) #define MIDNIGHT_ROLLOVER_CHECK ( rollovercheck_last_timeofday != world.timeofday ? update_midnight_rollover() : midnight_rollovers )
#define SHORT_REAL_LIMIT 16777216 // 2^24 - Maximum integer that can be exactly represented in a float (BYOND num var)
#define CEILING(x, y) ( -round(-(x) / (y)) * (y) ) #define CEILING(x, y) ( -round(-(x) / (y)) * (y) )
// round() acts like floor(x, 1) by default but can't handle other values // round() acts like floor(x, 1) by default but can't handle other values
#define FLOOR(x, y) ( round((x) / (y)) * (y) ) #define FLOOR(x, y) ( round((x) / (y)) * (y) )

View File

@@ -0,0 +1,17 @@
//
// Defines used for advanced performance profiling of subsystems.
// Currently used only by SSoverlays (2018-02-24 ~Leshana)
//
#define STAT_ENTRY_TIME 1
#define STAT_ENTRY_COUNT 2
#define STAT_ENTRY_LENGTH 2
#define STAT_START_STOPWATCH var/STAT_STOP_WATCH = TICK_USAGE
#define STAT_STOP_STOPWATCH var/STAT_TIME = TICK_USAGE_TO_MS(STAT_STOP_WATCH)
#define STAT_LOG_ENTRY(entrylist, entryname) \
var/list/STAT_ENTRY = entrylist[entryname] || (entrylist[entryname] = new /list(STAT_ENTRY_LENGTH));\
STAT_ENTRY[STAT_ENTRY_TIME] += STAT_TIME;\
var/STAT_INCR_AMOUNT = min(1, 2**round((STAT_ENTRY[STAT_ENTRY_COUNT] || 0)/SHORT_REAL_LIMIT));\
if (STAT_INCR_AMOUNT == 1 || prob(100/STAT_INCR_AMOUNT)) {\
STAT_ENTRY[STAT_ENTRY_COUNT] += STAT_INCR_AMOUNT;\
};\

View File

@@ -29,5 +29,35 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
#define INIT_ORDER_MACHINES 10 #define INIT_ORDER_MACHINES 10
#define INIT_ORDER_SHUTTLES 3 #define INIT_ORDER_SHUTTLES 3
#define INIT_ORDER_LIGHTING 0 #define INIT_ORDER_LIGHTING 0
#define INIT_ORDER_AIR -1 #define INIT_ORDER_AIR -1
#define INIT_ORDER_OVERLAY -6
#define INIT_ORDER_XENOARCH -20 #define INIT_ORDER_XENOARCH -20
// Subsystem fire priority, from lowest to highest priority
// If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child)
#define FIRE_PRIORITY_OVERLAYS 500
// Macro defining the actual code applying our overlays lists to the BYOND overlays list. (I guess a macro for speed)
// TODO - I don't really like the location of this macro define. Consider it. ~Leshana
#define COMPILE_OVERLAYS(A)\
if (TRUE) {\
var/list/oo = A.our_overlays;\
var/list/po = A.priority_overlays;\
if(LAZYLEN(po)){\
if(LAZYLEN(oo)){\
A.overlays = oo + po;\
}\
else{\
A.overlays = po;\
}\
}\
else if(LAZYLEN(oo)){\
A.overlays = oo;\
}\
else{\
A.overlays.Cut();\
}\
A.flags &= ~OVERLAY_QUEUED;\
}

View File

@@ -36,3 +36,9 @@
. = b.head_position - a.head_position . = b.head_position - a.head_position
if (. == 0) //Already in head/nothead spot, sort by name if (. == 0) //Already in head/nothead spot, sort by name
. = sorttext(b.title, a.title) . = sorttext(b.title, a.title)
// Sorts entries in a performance stats list.
/proc/cmp_generic_stat_item_time(list/A, list/B)
. = B[STAT_ENTRY_TIME] - A[STAT_ENTRY_TIME]
if (!.)
. = B[STAT_ENTRY_COUNT] - A[STAT_ENTRY_COUNT]

View File

@@ -0,0 +1,253 @@
SUBSYSTEM_DEF(overlays)
name = "Overlay"
flags = SS_TICKER
wait = 1
priority = FIRE_PRIORITY_OVERLAYS
init_order = INIT_ORDER_OVERLAY
var/initialized = FALSE
var/list/queue // Queue of atoms needing overlay compiling (TODO-VERIFY!)
var/list/stats
var/list/overlay_icon_state_caches // Cache thing
var/list/overlay_icon_cache // Cache thing
/datum/controller/subsystem/overlays/PreInit()
overlay_icon_state_caches = list()
overlay_icon_cache = list()
queue = list()
stats = list()
/datum/controller/subsystem/overlays/Initialize()
initialized = TRUE
fire(mc_check = FALSE)
..()
/datum/controller/subsystem/overlays/stat_entry()
..("Ov:[length(queue)]")
/datum/controller/subsystem/overlays/Shutdown()
//text2file(render_stats(stats), "[GLOB.log_directory]/overlay.log")
var/date_string = time2text(world.realtime, "YYYY/MM-Month/DD-Day")
text2file(render_stats(stats), "data/logs/[date_string]-overlay.log")
/datum/controller/subsystem/overlays/Recover()
overlay_icon_state_caches = SSoverlays.overlay_icon_state_caches
overlay_icon_cache = SSoverlays.overlay_icon_cache
queue = SSoverlays.queue
/datum/controller/subsystem/overlays/fire(resumed = FALSE, mc_check = TRUE)
var/list/queue = src.queue
var/static/count = 0
if (count)
var/c = count
count = 0 //so if we runtime on the Cut, we don't try again.
queue.Cut(1,c+1)
for (var/thing in queue)
count++
if(thing)
STAT_START_STOPWATCH
var/atom/A = thing
COMPILE_OVERLAYS(A)
STAT_STOP_STOPWATCH
STAT_LOG_ENTRY(stats, A.type)
if(mc_check)
if(MC_TICK_CHECK)
break
else
CHECK_TICK
if (count)
queue.Cut(1,count+1)
count = 0
/proc/iconstate2appearance(icon, iconstate)
var/static/image/stringbro = new()
var/list/icon_states_cache = SSoverlays.overlay_icon_state_caches
var/list/cached_icon = icon_states_cache[icon]
if (cached_icon)
var/cached_appearance = cached_icon["[iconstate]"]
if (cached_appearance)
return cached_appearance
stringbro.icon = icon
stringbro.icon_state = iconstate
if (!cached_icon) //not using the macro to save an associated lookup
cached_icon = list()
icon_states_cache[icon] = cached_icon
var/cached_appearance = stringbro.appearance
cached_icon["[iconstate]"] = cached_appearance
return cached_appearance
/proc/icon2appearance(icon)
var/static/image/iconbro = new()
var/list/icon_cache = SSoverlays.overlay_icon_cache
. = icon_cache[icon]
if (!.)
iconbro.icon = icon
. = iconbro.appearance
icon_cache[icon] = .
/atom/proc/build_appearance_list(old_overlays)
var/static/image/appearance_bro = new()
var/list/new_overlays = list()
if (!islist(old_overlays))
old_overlays = list(old_overlays)
for (var/overlay in old_overlays)
if(!overlay)
continue
if (istext(overlay))
new_overlays += iconstate2appearance(icon, overlay)
else if(isicon(overlay))
new_overlays += icon2appearance(overlay)
else
if(isloc(overlay))
var/atom/A = overlay
if (A.flags & OVERLAY_QUEUED)
COMPILE_OVERLAYS(A)
appearance_bro.appearance = overlay //this works for images and atoms too!
if(!ispath(overlay))
var/image/I = overlay
appearance_bro.dir = I.dir
new_overlays += appearance_bro.appearance
return new_overlays
#define NOT_QUEUED_ALREADY (!(flags & OVERLAY_QUEUED))
#define QUEUE_FOR_COMPILE flags |= OVERLAY_QUEUED; SSoverlays.queue += src;
/**
* Cut all of atom's normal overlays. Usually leaves "priority" overlays untouched.
*
* @param priority If true, also will cut priority overlays.
*/
/atom/proc/cut_overlays(priority = FALSE)
var/list/cached_overlays = our_overlays
var/list/cached_priority = priority_overlays
var/need_compile = FALSE
if(LAZYLEN(cached_overlays)) //don't queue empty lists, don't cut priority overlays
cached_overlays.Cut() //clear regular overlays
need_compile = TRUE
if(priority && LAZYLEN(cached_priority))
cached_priority.Cut()
need_compile = TRUE
if(NOT_QUEUED_ALREADY && need_compile)
QUEUE_FOR_COMPILE
/**
* Removes specific overlay(s) from the atom. Usually does not remove them from "priority" overlays.
*
* @param overlays The overlays to removed, type can be anything that is allowed for add_overlay().
* @param priority If true, also will remove them from the "priority" overlays.
*/
/atom/proc/cut_overlay(list/overlays, priority)
if(!overlays)
return
overlays = build_appearance_list(overlays)
var/list/cached_overlays = our_overlays //sanic
var/list/cached_priority = priority_overlays
var/init_o_len = LAZYLEN(cached_overlays)
var/init_p_len = LAZYLEN(cached_priority) //starter pokemon
LAZYREMOVE(cached_overlays, overlays)
if(priority)
LAZYREMOVE(cached_priority, overlays)
if(NOT_QUEUED_ALREADY && ((init_o_len != LAZYLEN(cached_overlays)) || (init_p_len != LAZYLEN(cached_priority))))
QUEUE_FOR_COMPILE
/**
* Adds specific overlay(s) to the atom.
* It is designed so any of the types allowed to be added to /atom/overlays can be added here too. More details below.
*
* @param overlays The overlay(s) to add. These may be
* - A string: In which case it is treated as an icon_state of the atom's icon.
* - An icon: It is treated as an icon.
* - An atom: Its own overlays are compiled and then it's appearance is added. (Meaning its current apperance is frozen).
* - An image: Image's apperance is added (i.e. subsequently editing the image will not edit the overlay)
* - A type path: Added to overlays as is. Does whatever it is BYOND does when you add paths to overlays.
* - Or a list containing any of the above.
* @param priority The overlays are added to the "priority" list istead of the normal one.
*/
/atom/proc/add_overlay(list/overlays, priority = FALSE)
if(!overlays)
return
overlays = build_appearance_list(overlays)
LAZYINITLIST(our_overlays) //always initialized after this point
LAZYINITLIST(priority_overlays)
var/list/cached_overlays = our_overlays //sanic
var/list/cached_priority = priority_overlays
var/init_o_len = cached_overlays.len
var/init_p_len = cached_priority.len //starter pokemon
var/need_compile
if(priority)
cached_priority += overlays //or in the image. Can we use [image] = image?
need_compile = init_p_len != cached_priority.len
else
cached_overlays += overlays
need_compile = init_o_len != cached_overlays.len
if(NOT_QUEUED_ALREADY && need_compile) //have we caught more pokemon?
QUEUE_FOR_COMPILE
/**
* Copy the overlays from another atom, either replacing all of ours or appending to our existing overlays.
* Note: This copies only the normal overlays, not the "priority" overlays.
*
* @param other The atom to copy overlays from.
* @param cut_old If true, all of our overlays will be *replaced* by the other's. If other is null, that means cutting all ours.
*/
/atom/proc/copy_overlays(atom/other, cut_old) //copys our_overlays from another atom
if(!other)
if(cut_old)
cut_overlays()
return
var/list/cached_other = other.our_overlays
if(cached_other)
if(cut_old || !LAZYLEN(our_overlays))
our_overlays = cached_other.Copy()
else
our_overlays |= cached_other
if(NOT_QUEUED_ALREADY)
QUEUE_FOR_COMPILE
else if(cut_old)
cut_overlays()
#undef NOT_QUEUED_ALREADY
#undef QUEUE_FOR_COMPILE
//TODO: Better solution for these?
/image/proc/add_overlay(x)
overlays += x
/image/proc/cut_overlay(x)
overlays -= x
/image/proc/cut_overlays(x)
overlays.Cut()
/image/proc/copy_overlays(atom/other, cut_old)
if(!other)
if(cut_old)
cut_overlays()
return
var/list/cached_other = other.our_overlays
if(cached_other)
if(cut_old || !overlays.len)
overlays = cached_other.Copy()
else
overlays |= cached_other
else if(cut_old)
cut_overlays()

View File

@@ -22,6 +22,10 @@
// replaced by OPENCONTAINER flags and atom/proc/is_open_container() // replaced by OPENCONTAINER flags and atom/proc/is_open_container()
///Chemistry. ///Chemistry.
// Overlays
var/list/our_overlays //our local copy of (non-priority) overlays without byond magic. Use procs in SSoverlays to manipulate
var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4.
//Detective Work, used for the duplicate data points kept in the scanners //Detective Work, used for the duplicate data points kept in the scanners
var/list/original_atom var/list/original_atom
// Track if we are already had initialize() called to prevent double-initialization. // Track if we are already had initialize() called to prevent double-initialization.

View File

@@ -195,6 +195,7 @@ var/list/admin_verbs_debug = list(
/client/proc/cmd_debug_tog_aliens, /client/proc/cmd_debug_tog_aliens,
/client/proc/cmd_display_del_log, /client/proc/cmd_display_del_log,
/client/proc/cmd_display_init_log, /client/proc/cmd_display_init_log,
/client/proc/cmd_display_overlay_log,
/client/proc/air_report, /client/proc/air_report,
/client/proc/reload_admins, /client/proc/reload_admins,
/client/proc/reload_eventMs, /client/proc/reload_eventMs,

View File

@@ -335,6 +335,28 @@
if(!check_rights(R_DEBUG)) return if(!check_rights(R_DEBUG)) return
src << browse(replacetext(SSatoms.InitLog(), "\n", "<br>"), "window=initlog") src << browse(replacetext(SSatoms.InitLog(), "\n", "<br>"), "window=initlog")
/client/proc/cmd_display_overlay_log()
set category = "Debug"
set name = "Display overlay Log"
set desc = "Display SSoverlays log of everything that's passed through it."
if(!check_rights(R_DEBUG)) return
render_stats(SSoverlays.stats, src)
// Render stats list for round-end statistics.
/proc/render_stats(list/stats, user, sort = /proc/cmp_generic_stat_item_time)
sortTim(stats, sort, TRUE)
var/list/lines = list()
for (var/entry in stats)
var/list/data = stats[entry]
lines += "[entry] => [num2text(data[STAT_ENTRY_TIME], 10)]ms ([data[STAT_ENTRY_COUNT]]) (avg:[num2text(data[STAT_ENTRY_TIME]/(data[STAT_ENTRY_COUNT] || 1), 99)])"
if (user)
user << browse("<ol><li>[lines.Join("</li><li>")]</li></ol>", "window=[url_encode("stats:\ref[stats]")]")
else
. = lines.Join("\n")
/client/proc/cmd_admin_grantfullaccess(var/mob/M in mob_list) /client/proc/cmd_admin_grantfullaccess(var/mob/M in mob_list)
set category = "Admin" set category = "Admin"
set name = "Grant Full Access" set name = "Grant Full Access"

View File

@@ -56,6 +56,7 @@
#include "code\__defines\roguemining_vr.dm" #include "code\__defines\roguemining_vr.dm"
#include "code\__defines\species_languages.dm" #include "code\__defines\species_languages.dm"
#include "code\__defines\species_languages_vr.dm" #include "code\__defines\species_languages_vr.dm"
#include "code\__defines\stat_tracking.dm"
#include "code\__defines\subsystems.dm" #include "code\__defines\subsystems.dm"
#include "code\__defines\targeting.dm" #include "code\__defines\targeting.dm"
#include "code\__defines\turfs.dm" #include "code\__defines\turfs.dm"
@@ -202,6 +203,7 @@
#include "code\controllers\subsystems\mapping_vr.dm" #include "code\controllers\subsystems\mapping_vr.dm"
#include "code\controllers\subsystems\mobs.dm" #include "code\controllers\subsystems\mobs.dm"
#include "code\controllers\subsystems\orbits.dm" #include "code\controllers\subsystems\orbits.dm"
#include "code\controllers\subsystems\overlays.dm"
#include "code\controllers\subsystems\persist_vr.dm" #include "code\controllers\subsystems\persist_vr.dm"
#include "code\controllers\subsystems\shuttles.dm" #include "code\controllers\subsystems\shuttles.dm"
#include "code\controllers\subsystems\transcore_vr.dm" #include "code\controllers\subsystems\transcore_vr.dm"