From 615d90c36f61d438fbba2efa750ab65307f1ebe7 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Wed, 26 Nov 2025 23:36:08 +0100 Subject: [PATCH] draw rect optimization --- implementations/Crafter.Graphics-Window.cpp | 27 ++++- .../Crafter.Graphics-Window_wayland.cpp | 113 ++++++++++++++---- interfaces/Crafter.Graphics-Window.cppm | 3 +- 3 files changed, 113 insertions(+), 30 deletions(-) diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index 35d0a32..d53570f 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -111,10 +111,27 @@ void Window::AddDirtyRect(ScaleData scale) { return; } - //merging logic should work so that no pixel is drawn twice, and that no pixel not marked dirty is drawn. - //so lets say there is already an existing horizontal bar and the new rect is a vertical bar making a cross shape, the center of the cross will currently be drawn twice - //so we need to turn it into 3 rects, the top part of the vertical bar, the horizontal bar, and the bottom part of the vertical bar - //in this way no pixel is drawn twice and no area not marked dirty is included - + for(ClipRect existing : dirtyRects) { + //fully enclosed + if(rect.left >= existing.left && rect.right <= existing.right && rect.top >= existing.top && rect.bottom <= existing.bottom) { + return; + } + + //horizontal line + if(rect.top == existing.top && rect.bottom == existing.bottom) { + existing.left = std::min(rect.left, existing.left); + existing.right = std::max(rect.right, existing.right); + return; + } + + //vertical line + if(rect.left == existing.left && rect.right == existing.right) { + existing.top = std::min(rect.top, existing.top); + existing.bottom = std::max(rect.bottom, existing.bottom); + return; + } + } + + //no overlap dirtyRects.push_back(rect); } \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Window_wayland.cpp b/implementations/Crafter.Graphics-Window_wayland.cpp index d321e1a..4c8a533 100644 --- a/implementations/Crafter.Graphics-Window_wayland.cpp +++ b/implementations/Crafter.Graphics-Window_wayland.cpp @@ -212,38 +212,105 @@ void WindowWayland::Render() { elements.erase(std::remove(elements.begin(), elements.end(), static_cast(nullptr)), elements.end()); std::sort(elements.begin(), elements.end(), [](Transform* a, Transform* b){ return a->z < b->z; }); - for(uint_fast32_t i = 0; i < width*height; i++) { - framebuffer[i] = {0, 0, 0, 255}; - } + std::vector newClip; - for (ClipRect rect : dirtyRects) { - //std::cout << std::format("{}, {}, {}, {}", rect.left, rect.top, rect.right, rect.bottom) << std::endl; - for (std::int_fast32_t y = rect.top; y < rect.bottom; y++) { - for (std::int_fast32_t x = rect.left; x < rect.right; x++) { - framebuffer[y * width + x].r += 30; + for (std::uint_fast32_t i = 0; i < dirtyRects.size(); i++) { + ClipRect rect = dirtyRects[i]; + for (std::uint_fast32_t i2 = i + 1; i2 < dirtyRects.size(); i2++) { + ClipRect existing = dirtyRects[i2]; + if(rect.bottom >= existing.top && rect.top <= existing.top) { + newClip.push_back({ + .left = rect.left, + .right = rect.right, + .top = rect.top, + .bottom = existing.top, + }); + //-| shape + if(rect.right > existing.right) { + newClip.push_back({ + .left = existing.right, + .right = rect.right, + .top = existing.top, + .bottom = existing.bottom, + }); + } + //|- shape + if(rect.left < existing.left) { + newClip.push_back({ + .left = rect.left, + .right = existing.left, + .top = existing.top, + .bottom = existing.bottom, + }); + } + //-| or |- shape where rect extends further down + if(rect.bottom > existing.bottom) { + newClip.push_back({ + .left = rect.left, + .right = rect.right, + .top = existing.bottom, + .bottom = rect.bottom, + }); + } + goto inner; } + if (rect.left <= existing.right && rect.right >= existing.left) { + newClip.push_back({ + .left = rect.left, + .right = existing.left, + .top = rect.top, + .bottom = rect.bottom, + }); + if (rect.right > existing.right) { + newClip.push_back({ + .left = existing.right, + .right = rect.right, + .top = rect.top, + .bottom = rect.bottom, + }); + } + goto inner; + } } + newClip.push_back(rect); + inner:; } - // if (!dirtyRects.empty()) { - // for (ClipRect rect : dirtyRects) { - // for (std::int_fast32_t y = rect.top; y < rect.bottom; y++) { - // for (std::int_fast32_t x = rect.left; x < rect.right; x++) { - // framebuffer[y * width + x] = {0, 0, 0, 0}; - // } + dirtyRects = std::move(newClip); + + // for(uint_fast32_t i = 0; i < width*height; i++) { + // framebuffer[i] = {0, 0, 0, 255}; + // } + + // std::cout << dirtyRects.size() << std::endl; + // for (ClipRect rect : dirtyRects) { + // //std::cout << std::format("{}, {}, {}, {}", rect.left, rect.top, rect.right, rect.bottom) << std::endl; + // for (std::int_fast32_t y = rect.top; y < rect.bottom; y++) { + // for (std::int_fast32_t x = rect.left; x < rect.right; x++) { + // framebuffer[y * width + x].r += 30; // } // } - - // for(Transform* child : elements) { - // RenderElement(child); - // } - - // for (ClipRect rect : dirtyRects) { - // wl_surface_damage(surface, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top); - // } - // dirtyRects.clear(); // } + if (!dirtyRects.empty()) { + for (ClipRect rect : dirtyRects) { + for (std::int_fast32_t y = rect.top; y < rect.bottom; y++) { + for (std::int_fast32_t x = rect.left; x < rect.right; x++) { + framebuffer[y * width + x] = {0, 0, 0, 0}; + } + } + } + + for(Transform* child : elements) { + RenderElement(child); + } + + for (ClipRect rect : dirtyRects) { + wl_surface_damage(surface, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top); + } + dirtyRects.clear(); + } + dirtyRects.clear(); wl_surface_damage(surface, 0, 0, width, height); diff --git a/interfaces/Crafter.Graphics-Window.cppm b/interfaces/Crafter.Graphics-Window.cppm index 2d75bba..43a09c3 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -56,9 +56,7 @@ export namespace Crafter { Event onUpdate; bool open = true; bool updating = false; - std::vector dirtyRects; - void AddDirtyRect(ScaleData rect); Window() = default; Window(std::int_fast32_t width, std::int_fast32_t height); @@ -66,6 +64,7 @@ export namespace Crafter { Window(Window&&) = delete; virtual ~Window() = default; Window& operator=(const Window&) = delete; + void AddDirtyRect(ScaleData rect); virtual void StartSync() = 0; virtual void StartUpdate() = 0; virtual void StopUpdate() = 0;