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 end
def draw(camera, lights, entities) def draw(camera, lights, entities)
Stats.frame.start_timing(:opengl_renderer)
Stats.frame.start_timing(:opengl_model_renderer)
@opengl_renderer.render(camera, lights, entities) @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 end
def canvas_size_changed def canvas_size_changed

View File

@@ -1,20 +1,91 @@
module CyberarmEngine module CyberarmEngine
class Stats class Stats
@@hash = { @frames = []
gui_recalculations_last_frame: 0 @frame_index = -1
@max_frame_history = 1024
def self.new_frame
if @frames.size < @max_frame_history
@frames << Frame.new
else
@frames[@frame_index] = Frame.new
end
end
def self.frame
@frames[@frame_index]
end
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
} }
def self.get(key) @timings = {}
@@hash.dig(key)
end end
def self.increment(key, n) def increment(key, number = 1)
@@hash[key] += n @counters[key] ||= 0
@counters[key] += number
end end
def self.clear def start_timing(key)
@@hash.each do |key, _value| raise "key must be a symbol!" unless key.is_a?(Symbol)
@@hash[key] = 0 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 end
end end

View File

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

View File

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

View File

@@ -8,14 +8,14 @@ module CyberarmEngine
attr_accessor :show_cursor attr_accessor :show_cursor
attr_writer :exit_on_opengl_error attr_writer :exit_on_opengl_error
attr_reader :last_frame_time, :states attr_reader :last_frame_time, :delta_time, :states
def self.now def self.now
Gosu.milliseconds Gosu.milliseconds
end end
def self.dt def self.dt
instance.last_frame_time / 1000.0 instance.dt
end end
def self.instance=(window) def self.instance=(window)
@@ -37,6 +37,7 @@ module CyberarmEngine
@last_frame_time = Gosu.milliseconds - 1 @last_frame_time = Gosu.milliseconds - 1
@current_frame_time = Gosu.milliseconds @current_frame_time = Gosu.milliseconds
@delta_time = @last_frame_time
self.caption = "CyberarmEngine #{CyberarmEngine::VERSION} #{Gosu.user_languages.join(', ')}" self.caption = "CyberarmEngine #{CyberarmEngine::VERSION} #{Gosu.user_languages.join(', ')}"
@states = [] @states = []
@@ -50,16 +51,32 @@ module CyberarmEngine
end end
def draw def draw
Stats.frame.start_timing(:draw)
current_state&.draw current_state&.draw
Stats.frame.end_timing(:draw)
Stats.frame.start_timing(:interframe_sleep)
end end
def update 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 @last_frame_time = Gosu.milliseconds - @current_frame_time
@current_frame_time = Gosu.milliseconds @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 end
def needs_cursor? def needs_cursor?