Refactored CyberarmEngine::Stats to track data for last N frames

This commit is contained in:
2023-04-20 16:08:59 -05:00
parent 5e3e06b74e
commit c26ddeef4d
5 changed files with 123 additions and 17 deletions

View File

@@ -8,8 +8,19 @@ module CyberarmEngine
end
def draw(camera, lights, entities)
Stats.frame.start_timing(:opengl_renderer)
Stats.frame.start_timing(:opengl_model_renderer)
@opengl_renderer.render(camera, lights, entities)
@bounding_box_renderer.render(entities) if @show_bounding_boxes
Stats.frame.end_timing(:opengl_model_renderer)
if @show_bounding_boxes
Stats.frame.start_timing(:opengl_boundingbox_renderer)
@bounding_box_renderer.render(entities)
Stats.frame.end_timing(:opengl_boundingbox_renderer)
end
Stats.frame.end_timing(:opengl_renderer)
end
def canvas_size_changed

View File

@@ -1,20 +1,91 @@
module CyberarmEngine
class Stats
@@hash = {
gui_recalculations_last_frame: 0
}
@frames = []
@frame_index = -1
@max_frame_history = 1024
def self.get(key)
@@hash.dig(key)
def self.new_frame
if @frames.size < @max_frame_history
@frames << Frame.new
else
@frames[@frame_index] = Frame.new
end
end
def self.increment(key, n)
@@hash[key] += n
def self.frame
@frames[@frame_index]
end
def self.clear
@@hash.each do |key, _value|
@@hash[key] = 0
def self.end_frame
frame&.complete
@frame_index += 1
@frame_index %= @max_frame_history
end
def self.frames
if @frames.size < @max_frame_history
@frames
else
@frames.rotate(@frame_index - (@max_frame_history - (@frames.size - 1)))
end
end
def self.frame_index
@frame_index
end
def self.max_frame_history
@max_frame_history
end
class Frame
Timing = Struct.new(:start_time, :end_time, :duration)
attr_reader :frame_timing, :counters, :timings
def initialize
@frame_timing = Timing.new(start_time: Gosu.milliseconds, end_time: -1, duration: -1)
@counters = {
gui_recalculations: 0
}
@timings = {}
end
def increment(key, number = 1)
@counters[key] ||= 0
@counters[key] += number
end
def start_timing(key)
raise "key must be a symbol!" unless key.is_a?(Symbol)
warn "Only one timing per key per frame. (Timing for #{key.inspect} already exists!)" if @timings[key]
@timings[key] = Timing.new(start_time: Gosu.milliseconds, end_time: -1, duration: -1)
end
def end_timing(key)
timing = @timings[key]
warn "Timing #{key.inspect} already ended!" if timing.end_time != -1
timing.end_time = Gosu.milliseconds
timing.duration = timing.end_time - timing.start_time
end
def complete
@frame_timing.end_time = Gosu.milliseconds
@frame_timing.duration = @frame_timing.end_time - @frame_timing.start_time
# Lock data structures
@frame_timing.freeze
@counters.freeze
@timings.freeze
end
def complete?
@frame_timing.duration != -1
end
end
end

View File

@@ -118,7 +118,7 @@ module CyberarmEngine
return unless visible?
Stats.increment(:gui_recalculations_last_frame, 1)
Stats.frame.increment(:gui_recalculations)
stylize
@@ -176,7 +176,7 @@ module CyberarmEngine
child.recalculate
child.reposition # TODO: Implement top,bottom,left,center, and right positioning
Stats.increment(:gui_recalculations_last_frame, 1)
Stats.frame.increment(:gui_recalculations)
child.element_visible = child.x >= @x - child.width && child.x <= @x + width &&
child.y >= @y - child.height && child.y <= @y + height

View File

@@ -79,16 +79,23 @@ module CyberarmEngine
def update
if @pending_recalculate_request
Stats.frame.start_timing(:gui_recalculate)
@root_container.recalculate
@root_container.recalculate
@root_container.recalculate
@pending_recalculate_request = false
Stats.frame.end_timing(:gui_recalculate)
end
Stats.frame.start_timing(:gui_element_recalculate_requests)
@pending_element_recalculate_requests.each(&:recalculate)
@pending_element_recalculate_requests.clear
Stats.frame.end_timing(:gui_element_recalculate_requests)
if @pending_focus_request
@pending_focus_request = false

View File

@@ -8,14 +8,14 @@ module CyberarmEngine
attr_accessor :show_cursor
attr_writer :exit_on_opengl_error
attr_reader :last_frame_time, :states
attr_reader :last_frame_time, :delta_time, :states
def self.now
Gosu.milliseconds
end
def self.dt
instance.last_frame_time / 1000.0
instance.dt
end
def self.instance=(window)
@@ -37,6 +37,7 @@ module CyberarmEngine
@last_frame_time = Gosu.milliseconds - 1
@current_frame_time = Gosu.milliseconds
@delta_time = @last_frame_time
self.caption = "CyberarmEngine #{CyberarmEngine::VERSION} #{Gosu.user_languages.join(', ')}"
@states = []
@@ -50,16 +51,32 @@ module CyberarmEngine
end
def draw
Stats.frame.start_timing(:draw)
current_state&.draw
Stats.frame.end_timing(:draw)
Stats.frame.start_timing(:interframe_sleep)
end
def update
Stats.clear
# Gosu calls update() then (optionally) draw(),
# so always end last frame and start next frame when update() is called.
Stats.frame&.end_timing(:interframe_sleep)
Stats.end_frame
current_state&.update
Stats.new_frame
@delta_time = (Gosu.milliseconds - @current_frame_time) * 0.001
@last_frame_time = Gosu.milliseconds - @current_frame_time
@current_frame_time = Gosu.milliseconds
Stats.frame.start_timing(:update)
current_state&.update
Stats.frame.end_timing(:update)
Stats.frame.start_timing(:interframe_sleep) unless needs_redraw?
end
def needs_cursor?