From c9df651a14d7d9d6a45984a56c99fec33dbdfc85 Mon Sep 17 00:00:00 2001 From: Daemo <97889325+DaemonicGh@users.noreply.github.com> Date: Fri, 7 Aug 2026 12:31:13 +0200 Subject: [PATCH] Optimized region drawing and fixed incorrect region clipping (#240) Made small optimizations to Texture's SetRegion, SetLinearRegion, GetRegion and Clear. They might not be super effective depending on how the compiler already optimized them. The optimizations mainly focus on removing the multiplication when transforming a position to an index by using the row index rather than the y coordinate during iteration. Also fixed regions not drawing properly when clipping against the sides of a window and made GetRegion reject any region that isn't fully inside of the window (previously accepted regions that clipped outside of the right and bottom side, possibly leading to incorrect reads on the user end due to desynchronized region dimensions) --- runtime/Sources/Renderer/Image.cpp | 77 ++++++++++++++++-------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/runtime/Sources/Renderer/Image.cpp b/runtime/Sources/Renderer/Image.cpp index 30c8ff8..f4766c8 100644 --- a/runtime/Sources/Renderer/Image.cpp +++ b/runtime/Sources/Renderer/Image.cpp @@ -226,25 +226,34 @@ namespace mlx void Texture::SetRegion(int x, int y, int w, int h, mlx_color* pixels) noexcept { MLX_PROFILE_FUNCTION(); - if(x < 0 || y < 0 || static_cast(x) >= m_width || static_cast(y) >= m_height) - return; - if(w < 0 || h < 0) + if(w < 0 || h < 0 || x < -w || y < -h + || x >= static_cast(m_width) || y >= static_cast(m_height)) return; if(!m_staging_buffer.has_value()) OpenCPUBuffer(); - for(std::uint32_t i = 0, moving_x = x, moving_y = y;; i++, moving_x++) + const int + start_x = std::max(x, 0), + start_y = std::max(y, 0), + start_row = start_y * m_width, + start_i = (start_y - y) * w + (start_x - x), + end_x = std::min(x + w, m_width), + end_y = std::min(y + h, m_height), + end_row = end_y * m_width, + incr = (x + w) - end_x + (start_x - x); + for(int i = start_i, dx = start_x, row = start_row;; i++, dx++) { - if(moving_x >= static_cast(x + w) || moving_x >= m_width) + if(dx >= end_x) { - moving_x = x; - moving_y++; - if(moving_y >= static_cast(y + h) || moving_y >= m_height) + i += incr; + dx = start_x; + row += m_width; + if(row >= end_row) break; } if constexpr(std::endian::native == std::endian::little) - m_staging_buffer->GetMap()[(moving_y * m_width) + moving_x] = ReverseColor(pixels[i]); + m_staging_buffer->GetMap()[row + dx] = ReverseColor(pixels[i]); else - m_staging_buffer->GetMap()[(moving_y * m_width) + moving_x] = pixels[i]; + m_staging_buffer->GetMap()[row + dx] = pixels[i]; } m_has_been_modified = true; } @@ -252,23 +261,25 @@ namespace mlx void Texture::SetLinearRegion(int x, int y, std::size_t len, mlx_color* pixels) noexcept { MLX_PROFILE_FUNCTION(); - if(x < 0 || y < 0 || static_cast(x) >= m_width || static_cast(y) >= m_height) + if(x >= static_cast(m_width) || y >= static_cast(m_height)) return; if(!m_staging_buffer.has_value()) OpenCPUBuffer(); + int + start = y * m_width + x, + dest_start = std::max(start, 0), + src_start = dest_start - start, + dest_end = std::min(start + len, m_width * m_height); if constexpr(std::endian::native == std::endian::little) { - for(std::size_t i = 0; i < len && (y * m_width) + x + i < m_width * m_height; i++) - m_staging_buffer->GetMap()[(y * m_width) + x + i] = ReverseColor(pixels[i]); + for(int i = dest_start, j = src_start; i < dest_end; i++, j++) + m_staging_buffer->GetMap()[i] = ReverseColor(pixels[j]); } else { - std::size_t len_guard; - if((y * m_width + x + len) < m_width * m_height) - len_guard = len; - else - len_guard = len - (m_width * m_height - (y * m_width + x + len)); - std::memcpy(&m_staging_buffer->GetMap()[(y * m_width) + x], pixels, len_guard); + std::memcpy( + &m_staging_buffer->GetMap()[dest_start], + &pixels[src_start], dest_end - dest_start); } m_has_been_modified = true; } @@ -289,23 +300,23 @@ namespace mlx void Texture::GetRegion(int x, int y, int w, int h, mlx_color* dst) noexcept { MLX_PROFILE_FUNCTION(); - if(x < 0 || y < 0 || static_cast(x) >= m_width || static_cast(y) >= m_height) + if(w < 0 || h < 0 || x < 0 || y < 0 || static_cast(x + w) >= m_width || static_cast(y + h) >= m_height) return; if(!m_staging_buffer.has_value()) OpenCPUBuffer(); - for(std::uint32_t i = 0, moving_x = x, moving_y = y;; i++, moving_x++) + for(std::uint32_t i = 0, dx = x, row = y * m_width;; i++, dx++) { - if(moving_x >= static_cast(x + w) || moving_x >= m_width) + if(dx >= m_width) { - moving_x = x; - moving_y++; - if(moving_y >= static_cast(y + h) || moving_y >= m_height) + dx = x; + row += m_width; + if(row >= m_height * m_width) break; } if constexpr(std::endian::native == std::endian::little) - dst[i] = ReverseColor(m_staging_buffer->GetMap()[(moving_y * m_width) + moving_x]); + dst[i] = ReverseColor(m_staging_buffer->GetMap()[row + dx]); else - dst[i] = m_staging_buffer->GetMap()[(moving_y * m_width) + moving_x]; + dst[i] = m_staging_buffer->GetMap()[row + dx]; } } @@ -320,16 +331,8 @@ namespace mlx processed_color.g = static_cast(color.g * 255.f); processed_color.b = static_cast(color.b * 255.f); processed_color.a = static_cast(color.a * 255.f); - if(processed_color.r == 0 && processed_color.g == 0 && processed_color.b == 0) - std::memset(m_staging_buffer->GetMap(), processed_color.a, m_staging_buffer->GetSize()); - else - { - for(std::size_t y = 0; y < m_height; y++) - { - for(std::size_t x = 0; x < m_width; x++) - m_staging_buffer->GetMap()[y * m_width + x] = processed_color; - } - } + for(std::size_t i = 0; i < m_width * m_height; i++) + m_staging_buffer->GetMap()[i] = processed_color; } }