diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index f263859..701288f 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -98,30 +98,28 @@ void Window::LogTiming() { } #endif -bool Overlaps(const ScaleData& rect1, const ScaleData& rect2) { - return !(rect1.x + rect1.width <= rect2.x || rect2.x + rect2.width <= rect1.x || rect1.y + rect1.height <= rect2.y || rect2.y + rect2.height <= rect1.y); -} -ScaleData MergeRects(const ScaleData& rect1, const ScaleData& rect2) { - ScaleData merged; - merged.x = std::min(rect1.x, rect2.x); - merged.y = std::min(rect1.y, rect2.y); - merged.width = std::max(rect1.x + rect1.width, rect2.x + rect2.width) - merged.x; - merged.height = std::max(rect1.y + rect1.height, rect2.y + rect2.height) - merged.y; - return merged; -} +void Window::AddDirtyRect(ScaleData scale) { + ClipRect rect { + .left = std::max(scale.x, std::int_fast32_t(0)), + .right = std::min(scale.x + scale.width, width), + .top = std::max(scale.y, std::int_fast32_t(0)), + .bottom = std::min(scale.y + scale.height, height), + }; -void Window::AddDirtyRect(ScaleData rect) { - bool merged = false; - for (auto& existingRect : dirtyRects) { - if (Overlaps(existingRect, rect)) { - existingRect = MergeRects(existingRect, rect); - merged = true; - break; + if (rect.left >= rect.right || rect.top >= rect.bottom) { + return; + } + + for (ClipRect& existingRect : dirtyRects) { + if (rect.left <= existingRect.right && rect.right >= existingRect.left && rect.top <= existingRect.bottom && rect.bottom >= existingRect.top) { + existingRect.left = std::min(existingRect.left, rect.left); + existingRect.right = std::max(existingRect.right, rect.right); + existingRect.top = std::min(existingRect.top, rect.top); + existingRect.bottom = std::max(existingRect.bottom, rect.bottom); + return; } } - if (!merged) { - dirtyRects.push_back(rect); - } + 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 619b457..c79f5b2 100644 --- a/implementations/Crafter.Graphics-Window_wayland.cpp +++ b/implementations/Crafter.Graphics-Window_wayland.cpp @@ -153,62 +153,43 @@ inline void blend_pixel_optimized(Pixel_BU8_GU8_RU8_AU8& dst, const Pixel_BU8_GU } } -// Optimized rendering with bounds checking and early exit void WindowWayland::RenderElement(Transform* transform) { RenderingElement* element = dynamic_cast(transform); if(element) { #ifdef CRAFTER_TIMING auto start = std::chrono::high_resolution_clock::now(); #endif - - // Calculate clipping bounds - std::int_fast32_t clip_left = std::max(element->scaled.x, std::int_fast32_t(0)); - std::int_fast32_t clip_top = std::max(element->scaled.y, std::int_fast32_t(0)); - std::int_fast32_t clip_right = std::min(element->scaled.x + element->scaled.width, static_cast(width)); - std::int_fast32_t clip_bottom = std::min(element->scaled.y + element->scaled.height, static_cast(height)); - - // Early exit if completely outside screen - if (clip_left >= clip_right || clip_top >= clip_bottom) { - #ifdef CRAFTER_TIMING - auto end = std::chrono::high_resolution_clock::now(); - renderTimings.push_back({element, element->scaled.width, element->scaled.height, end-start}); - #endif + + if(element->scaled.width < 1 || element->scaled.height < 1) { return; } - // Get source buffer data - const Pixel_BU8_GU8_RU8_AU8* src_buffer = element->bufferScaled.data(); - std::uint_fast32_t src_width = element->scaled.width; - std::uint_fast32_t src_height = element->scaled.height; - - // If element is opaque, we can simply copy pixels without blending - if (element->opaque) { - // Render clipped region - for (std::int_fast32_t y = clip_top; y < clip_bottom; y++) { - std::int_fast32_t src_y = y - element->scaled.y; - - for (std::int_fast32_t x = clip_left; x < clip_right; x++) { - std::int_fast32_t src_x = x - element->scaled.x; + for(ClipRect dirty : dirtyRects) { + dirty.left = std::max(element->scaled.x, dirty.left); + dirty.top = std::max(element->scaled.y, dirty.top); + dirty.right = std::min(element->scaled.x+element->scaled.width, dirty.right); + dirty.bottom = std::min(element->scaled.y+element->scaled.height, dirty.bottom); + + const Pixel_BU8_GU8_RU8_AU8* src_buffer = element->bufferScaled.data(); + std::int_fast32_t src_width = element->scaled.width; + std::int_fast32_t src_height = element->scaled.height; + + if (element->opaque) { + for (std::int_fast32_t y = dirty.top; y < dirty.bottom; y++) { + std::int_fast32_t src_y = y - element->scaled.y; - // Bounds check for source buffer - if (src_x >= 0 && src_x < static_cast(src_width) && src_y >= 0 && src_y < static_cast(src_height)) { - // Direct copy for opaque elements (skip blending) + for (std::int_fast32_t x = dirty.left; x < dirty.right; x++) { + std::int_fast32_t src_x = x - element->scaled.x; + framebuffer[y * width + x] = src_buffer[src_y * src_width + src_x]; } } - } - } else { - // Render clipped region with blending for non-opaque elements - for (std::int_fast32_t y = clip_top; y < clip_bottom; y++) { - std::int_fast32_t src_y = y - element->scaled.y; - - for (std::int_fast32_t x = clip_left; x < clip_right; x++) { - std::int_fast32_t src_x = x - element->scaled.x; + } else { + for (std::int_fast32_t y = dirty.top; y < dirty.bottom; y++) { + std::int_fast32_t src_y = y - element->scaled.y; - // Bounds check for source buffer - if (src_x >= 0 && src_x < static_cast(src_width) && src_y >= 0 && src_y < static_cast(src_height)) { - - // Blend pixels + for (std::int_fast32_t x = dirty.left; x < dirty.right; x++) { + std::int_fast32_t src_x = x - element->scaled.x; blend_pixel_optimized(framebuffer[y * width + x], src_buffer[src_y * src_width + src_x]); } } @@ -232,9 +213,9 @@ void WindowWayland::Render() { std::sort(elements.begin(), elements.end(), [](Transform* a, Transform* b){ return a->z < b->z; }); if (!dirtyRects.empty()) { - for (const ScaleData& rect : dirtyRects) { - for (std::int_fast32_t y = rect.y; y < rect.y + rect.height && y < height; y++) { - for (std::int_fast32_t x = rect.x; x < rect.x + rect.width && x < width; x++) { + 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}; } } @@ -244,12 +225,14 @@ void WindowWayland::Render() { RenderElement(child); } - for (const ScaleData& rect : dirtyRects) { - wl_surface_damage(surface, rect.x, rect.y, rect.width, rect.height); + 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); wl_surface_attach(surface, buffer, 0, 0); wl_surface_commit(surface); diff --git a/interfaces/Crafter.Graphics-Types.cppm b/interfaces/Crafter.Graphics-Types.cppm index 833ba89..8816309 100644 --- a/interfaces/Crafter.Graphics-Types.cppm +++ b/interfaces/Crafter.Graphics-Types.cppm @@ -39,6 +39,13 @@ namespace Crafter { std::int_fast32_t width; std::int_fast32_t height; }; + + export struct ClipRect { + std::int_fast32_t left; + std::int_fast32_t right; + std::int_fast32_t top; + std::int_fast32_t bottom; + }; export struct __attribute__((packed)) Pixel_BU8_GU8_RU8_AU8 { std::uint8_t b; diff --git a/interfaces/Crafter.Graphics-Window.cppm b/interfaces/Crafter.Graphics-Window.cppm index f508d8e..2d75bba 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -57,7 +57,7 @@ export namespace Crafter { bool open = true; bool updating = false; - std::vector dirtyRects; + std::vector dirtyRects; void AddDirtyRect(ScaleData rect); Window() = default;