diff --git a/Outputs/DisplayMetrics.cpp b/Outputs/DisplayMetrics.cpp index 7e9300258..3814298d2 100644 --- a/Outputs/DisplayMetrics.cpp +++ b/Outputs/DisplayMetrics.cpp @@ -8,13 +8,72 @@ #include "DisplayMetrics.hpp" +#include + using namespace Outputs::Display; +// MARK: Frame size estimation. + void Metrics::announce_event(ScanTarget::Event event) { + switch(event) { + case ScanTarget::Event::EndHorizontalRetrace: + ++lines_this_frame_; + break; + case ScanTarget::Event::EndVerticalRetrace: + add_line_total(lines_this_frame_); + lines_this_frame_ = 0; + break; + default: break; + } } +void Metrics::add_line_total(int total) { +// std::cout << total << '\n'; +} + +float Metrics::lines_per_frame_estimate() { + return 1.0f; // TODO. +} + +// MARK: GPU processing speed decisions. + void Metrics::announce_did_resize() { + frames_missed_ = frames_hit_ = 0; } void Metrics::announce_draw_status(size_t lines, std::chrono::high_resolution_clock::duration duration, bool complete) { + if(!complete) { + ++frames_missed_; + } else { + ++frames_hit_; + } + + // Don't allow the record of history to extend too far into the past. + if(frames_hit_ + frames_missed_ > 200) { + // Subtract from whichever wasn't just incremented, to ensure the + // most recent information is more important than the historic stuff. + if(!complete) { + --frames_hit_; + } else { + --frames_missed_; + } + + // Rebalance if either thing has gone negative. + if(frames_hit_ < 0) { + frames_missed_ += frames_hit_; + frames_hit_ = 0; + } + if(frames_missed_ < 0) { + frames_hit_ += frames_missed_; + frames_missed_ = 0; + } + } +} + +bool Metrics::should_lower_resolution() { + // If less than 100 frames are on record, return no opinion; otherwise + // suggest a lower resolution if more than 10 frames in the last 100-200 + // took too long to produce. + if(frames_hit_ + frames_missed_ < 100) return false; + return frames_missed_ > 10; } diff --git a/Outputs/DisplayMetrics.hpp b/Outputs/DisplayMetrics.hpp index cba50a16b..77eccad1a 100644 --- a/Outputs/DisplayMetrics.hpp +++ b/Outputs/DisplayMetrics.hpp @@ -26,6 +26,16 @@ class Metrics { void announce_did_resize(); void announce_draw_status(size_t lines, std::chrono::high_resolution_clock::duration duration, bool complete); + + bool should_lower_resolution(); + float lines_per_frame_estimate(); + + private: + int lines_this_frame_ = 0; + void add_line_total(int); + + int frames_hit_ = 0; + int frames_missed_ = 0; }; } diff --git a/Outputs/OpenGL/Primitives/TextureTarget.cpp b/Outputs/OpenGL/Primitives/TextureTarget.cpp index 0ffed6a6f..9382f062a 100644 --- a/Outputs/OpenGL/Primitives/TextureTarget.cpp +++ b/Outputs/OpenGL/Primitives/TextureTarget.cpp @@ -33,7 +33,7 @@ TextureTarget::TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit, bind_texture(); // Set dimensions and set the user-supplied magnification filter. - test_gl(glTexImage2D, GL_TEXTURE_2D, 0, GL_RGBA, GLsizei(expanded_width_), GLsizei(expanded_height_), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + test_gl(glTexImage2D, GL_TEXTURE_2D, 0, GL_RGB, GLsizei(expanded_width_), GLsizei(expanded_height_), 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); test_gl(glTexParameteri, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); test_gl(glTexParameteri, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index c7a72765b..303c51f88 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -13,6 +13,7 @@ #include #include +#include using namespace Outputs::Display::OpenGL; @@ -550,13 +551,26 @@ void ScanTarget::update(int output_width, int output_height) { test_gl(glDrawArraysInstanced, GL_TRIANGLE_STRIP, 0, 4, GLsizei(new_scans)); } - // Ensure the accumulation buffer is properly sized. - // TODO: based on a decision about host speed, potentially switch to the std::min fragment as shown below, - // which would limit total output buffer size to 1440x1080. - const int framebuffer_height = output_height;//std::min(output_height, 1080); + // Logic for reducing resolution: start doing so if the metrics object reports that + // it's a good idea. Go up to a quarter of the requested resolution, subject to + // clamping at each stage. If the output resolution changes, or anything else about + // the output pipeline, just start trying the highest size again. + if(display_metrics_.should_lower_resolution()) { + resolution_reduction_level_ = std::min(resolution_reduction_level_+1, 4); + } + if(output_height_ != output_height || did_setup_pipeline) { + resolution_reduction_level_ = 1; + output_height_ = output_height; + } + const int min_framebuffer_height = (resolution_reduction_level_ > 1) ? 2160 / resolution_reduction_level_ : std::numeric_limits::max(); + + // Ensure the accumulation buffer is properly sized, allowing for the metrics object's + // feelings about whether too high a resolution is being used. + int framebuffer_height = std::min(output_height / resolution_reduction_level_, min_framebuffer_height); const int proportional_width = (framebuffer_height * 4) / 3; - const bool did_create_accumulation_texture = !accumulation_texture_ || ( /* !synchronous && */ (accumulation_texture_->get_width() != proportional_width || accumulation_texture_->get_height() != framebuffer_height)); + const bool did_create_accumulation_texture = !accumulation_texture_ || ( (accumulation_texture_->get_width() != proportional_width || accumulation_texture_->get_height() != framebuffer_height)); if(did_create_accumulation_texture) { + LOG("Changed output resolution to " << proportional_width << " by " << framebuffer_height); display_metrics_.announce_did_resize(); std::unique_ptr new_framebuffer( new TextureTarget( diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index bf4db9167..fa2a5ebf7 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -84,6 +84,8 @@ class ScanTarget: public Outputs::Display::ScanTarget { bool output_is_visible_ = false; Metrics display_metrics_; + int resolution_reduction_level_ = 1; + int output_height_ = 0; size_t lines_submitted_ = 0; std::chrono::high_resolution_clock::time_point line_submission_begin_time_;