From 01b3f327df2dc30d847bd335ccce12eaec2dfd39 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sat, 29 Feb 2020 22:04:02 +0100 Subject: Move pixel buffer render code from Window to PixelBuffer for easier testing/benchmarking. --- plugingui/guievent.h | 5 ++ plugingui/pixelbuffer.cc | 141 ++++++++++++++++++++++++++++++++++++++++++ plugingui/pixelbuffer.h | 5 ++ plugingui/window.cc | 136 +--------------------------------------- test/uitests/benchmarktest.cc | 28 +++++++++ 5 files changed, 182 insertions(+), 133 deletions(-) diff --git a/plugingui/guievent.h b/plugingui/guievent.h index fa42483..4ad0798 100644 --- a/plugingui/guievent.h +++ b/plugingui/guievent.h @@ -202,6 +202,11 @@ struct Rect std::size_t y1; std::size_t x2; std::size_t y2; + + bool empty() const + { + return x1 == x2 && y1 == y2; + } }; } // GUI:: diff --git a/plugingui/pixelbuffer.cc b/plugingui/pixelbuffer.cc index 1df1d5a..827d2fc 100644 --- a/plugingui/pixelbuffer.cc +++ b/plugingui/pixelbuffer.cc @@ -104,6 +104,147 @@ void PixelBuffer::writeLine(std::size_t x, std::size_t y, } } +Rect PixelBuffer::updateBuffer(std::vector& pixel_buffers) +{ + bool has_dirty_rect{false}; + Rect dirty_rect; + + for(const auto& pixel_buffer : pixel_buffers) + { + if(pixel_buffer->dirty) + { + auto x1 = (std::size_t)pixel_buffer->x; + auto x2 = (std::size_t)(pixel_buffer->x + pixel_buffer->width); + auto y1 = (std::size_t)pixel_buffer->y; + auto y2 = (std::size_t)(pixel_buffer->y + pixel_buffer->height); + + pixel_buffer->dirty = false; + if(!has_dirty_rect) + { + // Insert this area: + dirty_rect = {x1, y1, x2, y2}; + has_dirty_rect = true; + } + else + { + // Expand existing area: + auto x1_0 = dirty_rect.x1; + auto y1_0 = dirty_rect.y1; + auto x2_0 = dirty_rect.x2; + auto y2_0 = dirty_rect.y2; + dirty_rect = { + (x1_0 < x1) ? x1_0 : x1, + (y1_0 < y1) ? y1_0 : y1, + (x2_0 > x2) ? x2_0 : x2, + (y2_0 > y2) ? y2_0 : y2 + }; + } + } + + if(pixel_buffer->has_last) + { + auto x1 = (std::size_t)pixel_buffer->last_x; + auto x2 = (std::size_t)(pixel_buffer->last_x + pixel_buffer->last_width); + auto y1 = (std::size_t)pixel_buffer->last_y; + auto y2 = (std::size_t)(pixel_buffer->last_y + pixel_buffer->last_height); + + pixel_buffer->has_last = false; + if(!has_dirty_rect) + { + // Insert this area: + dirty_rect = {x1, y1, x2, y2}; + has_dirty_rect = true; + } + else + { + // Expand existing area: + auto x1_0 = dirty_rect.x1; + auto y1_0 = dirty_rect.y1; + auto x2_0 = dirty_rect.x2; + auto y2_0 = dirty_rect.y2; + dirty_rect = { + (x1_0 < x1) ? x1_0 : x1, + (y1_0 < y1) ? y1_0 : y1, + (x2_0 > x2) ? x2_0 : x2, + (y2_0 > y2) ? y2_0 : y2 + }; + } + } + } + + if(!has_dirty_rect) + { + return {}; + } + + for(const auto& pixel_buffer : pixel_buffers) + { + if(!pixel_buffer->visible) + { + continue; + } + + int update_width = pixel_buffer->width; + int update_height = pixel_buffer->height; + + // Skip buffer if not inside window. + if(((int)width < pixel_buffer->x) || + ((int)height < pixel_buffer->y)) + { + continue; + } + + if(update_width > ((int)width - pixel_buffer->x)) + { + update_width = ((int)width - pixel_buffer->x); + } + + if(update_height > ((int)height - pixel_buffer->y)) + { + update_height = ((int)height - pixel_buffer->y); + } + + auto from_x = (int)dirty_rect.x1 - pixel_buffer->x; + from_x = std::max(0, from_x); + auto from_y = (int)dirty_rect.y1 - pixel_buffer->y; + from_y = std::max(0, from_y); + + auto to_x = (int)dirty_rect.x2 - pixel_buffer->x; + to_x = std::min(to_x, (int)update_width); + auto to_y = (int)dirty_rect.y2 - pixel_buffer->y; + to_y = std::min(to_y, (int)update_height); + + if(to_x < from_x) + { + continue; + } + + for(int y = from_y; y < to_y; y++) + { + writeLine(pixel_buffer->x + from_x, + pixel_buffer->y + y, + pixel_buffer->getLine(from_x, y), + to_x - from_x); + } + } + + dirty_rect.x2 = std::min(width, dirty_rect.x2); + dirty_rect.y2 = std::min(height, dirty_rect.y2); + + // Make sure we don't try to paint a rect backwards. + if(dirty_rect.x1 > dirty_rect.x2) + { + std::swap(dirty_rect.x1, dirty_rect.x2); + } + + if(dirty_rect.y1 > dirty_rect.y2) + { + std::swap(dirty_rect.y1, dirty_rect.y2); + } + + return dirty_rect; +} + PixelBufferAlpha::PixelBufferAlpha(std::size_t width, std::size_t height) : managed(true) diff --git a/plugingui/pixelbuffer.h b/plugingui/pixelbuffer.h index 499940d..81921e5 100644 --- a/plugingui/pixelbuffer.h +++ b/plugingui/pixelbuffer.h @@ -29,6 +29,9 @@ #include "colour.h" #include +#include + +#include "guievent.h" namespace GUI { @@ -46,6 +49,8 @@ public: void writeLine(std::size_t x, std::size_t y, const std::uint8_t* line, std::size_t len); + Rect updateBuffer(std::vector& pixel_buffers); + std::uint8_t* buf{nullptr}; std::size_t width{0}; std::size_t height{0}; diff --git a/plugingui/window.cc b/plugingui/window.cc index acde008..5e0ad31 100644 --- a/plugingui/window.cc +++ b/plugingui/window.cc @@ -250,144 +250,14 @@ bool Window::updateBuffer() return false; } - bool has_dirty_rect{false}; - Rect dirty_rect; - auto pixel_buffers = getPixelBuffers(); - for(auto& pixel_buffer : pixel_buffers) - { - if(pixel_buffer->dirty) - { - auto x1 = (std::size_t)pixel_buffer->x; - auto x2 = (std::size_t)(pixel_buffer->x + pixel_buffer->width); - auto y1 = (std::size_t)pixel_buffer->y; - auto y2 = (std::size_t)(pixel_buffer->y + pixel_buffer->height); - - pixel_buffer->dirty = false; - if(!has_dirty_rect) - { - // Insert this area: - dirty_rect = {x1, y1, x2, y2}; - has_dirty_rect = true; - } - else - { - // Expand existing area: - auto x1_0 = dirty_rect.x1; - auto y1_0 = dirty_rect.y1; - auto x2_0 = dirty_rect.x2; - auto y2_0 = dirty_rect.y2; - dirty_rect = { - (x1_0 < x1) ? x1_0 : x1, - (y1_0 < y1) ? y1_0 : y1, - (x2_0 > x2) ? x2_0 : x2, - (y2_0 > y2) ? y2_0 : y2 - }; - } - } - - if(pixel_buffer->has_last) - { - auto x1 = (std::size_t)pixel_buffer->last_x; - auto x2 = (std::size_t)(pixel_buffer->last_x + pixel_buffer->last_width); - auto y1 = (std::size_t)pixel_buffer->last_y; - auto y2 = (std::size_t)(pixel_buffer->last_y + pixel_buffer->last_height); - - pixel_buffer->has_last = false; - if(!has_dirty_rect) - { - // Insert this area: - dirty_rect = {x1, y1, x2, y2}; - has_dirty_rect = true; - } - else - { - // Expand existing area: - auto x1_0 = dirty_rect.x1; - auto y1_0 = dirty_rect.y1; - auto x2_0 = dirty_rect.x2; - auto y2_0 = dirty_rect.y2; - dirty_rect = { - (x1_0 < x1) ? x1_0 : x1, - (y1_0 < y1) ? y1_0 : y1, - (x2_0 > x2) ? x2_0 : x2, - (y2_0 > y2) ? y2_0 : y2 - }; - } - } - } - - if(!has_dirty_rect) - { - return false; - } - for(auto& pixel_buffer : pixel_buffers) - { - if(!pixel_buffer->visible) - { - continue; - } - - int update_width = pixel_buffer->width; - int update_height = pixel_buffer->height; - - // Skip buffer if not inside window. - if(((int)wpixbuf.width < pixel_buffer->x) || - ((int)wpixbuf.height < pixel_buffer->y)) - { - continue; - } - - if(update_width > ((int)wpixbuf.width - pixel_buffer->x)) - { - update_width = ((int)wpixbuf.width - pixel_buffer->x); - } - - if(update_height > ((int)wpixbuf.height - pixel_buffer->y)) - { - update_height = ((int)wpixbuf.height - pixel_buffer->y); - } - - auto from_x = (int)dirty_rect.x1 - pixel_buffer->x; - from_x = std::max(0, from_x); - auto from_y = (int)dirty_rect.y1 - pixel_buffer->y; - from_y = std::max(0, from_y); - - auto to_x = (int)dirty_rect.x2 - pixel_buffer->x; - to_x = std::min(to_x, (int)update_width); - auto to_y = (int)dirty_rect.y2 - pixel_buffer->y; - to_y = std::min(to_y, (int)update_height); - - if(to_x < from_x) - { - continue; - } - - for(int y = from_y; y < to_y; y++) - { - wpixbuf.writeLine(pixel_buffer->x + from_x, - pixel_buffer->y + y, - pixel_buffer->getLine(from_x, y), - to_x - from_x); - } - } + auto dirty_rect = wpixbuf.updateBuffer(pixel_buffers); - dirty_rect.x2 = std::min(wpixbuf.width, dirty_rect.x2); - dirty_rect.y2 = std::min(wpixbuf.height, dirty_rect.y2); - - // Make sure we don't try to paint a rect backwards. - if(dirty_rect.x1 > dirty_rect.x2) + if(!dirty_rect.empty()) { - std::swap(dirty_rect.x1, dirty_rect.x2); + native->redraw(dirty_rect); } - - if(dirty_rect.y1 > dirty_rect.y2) - { - std::swap(dirty_rect.y1, dirty_rect.y2); - } - - native->redraw(dirty_rect); needs_redraw = false; return true; diff --git a/test/uitests/benchmarktest.cc b/test/uitests/benchmarktest.cc index b240388..33defc6 100644 --- a/test/uitests/benchmarktest.cc +++ b/test/uitests/benchmarktest.cc @@ -115,6 +115,34 @@ int main() } } + { + GUI::PixelBuffer wpixbuf(800, 600); + std::vector children; + for(int i = 0; i < 100; ++i) + { + auto child = new GUI::PixelBufferAlpha(300, 300); + child->x = i * 2; + child->y = i * 2; + children.push_back(child); + } + + TimedScope timed("Buffer flattening", 100); + for(int i = 0; i < 100; ++i) + { + for(auto child : children) + { + child->dirty = true; + } + + wpixbuf.updateBuffer(children); + } + + for(auto child : children) + { + delete child; + } + } + { TimedScope timed("Scaled 1:1 no alpha", 1000); for(int i = 0; i < 1000; ++i) -- cgit v1.2.3