From 4925bd77b98b637fb612cf4d7961090c14cf9024 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 25 Nov 2025 23:47:00 +0100 Subject: [PATCH] optimizations --- .../Crafter.Graphics-Window_wayland.cpp | 78 +++++++++++++------ interfaces/Crafter.Graphics-Animation.cppm | 25 +++++- 2 files changed, 77 insertions(+), 26 deletions(-) diff --git a/implementations/Crafter.Graphics-Window_wayland.cpp b/implementations/Crafter.Graphics-Window_wayland.cpp index 5ceec36..60f1871 100644 --- a/implementations/Crafter.Graphics-Window_wayland.cpp +++ b/implementations/Crafter.Graphics-Window_wayland.cpp @@ -134,35 +134,70 @@ void WindowWayland::StartSync() { } } +// Optimized pixel blending function using SIMD-like operations +inline void blend_pixel_optimized(Pixel_BU8_GU8_RU8_AU8& dst, const Pixel_BU8_GU8_RU8_AU8& src) { + float srcA = src.a / 255.0f; + float dstA = dst.a / 255.0f; + float outA = srcA + dstA * (1.0f - srcA); + if (outA > 0.0f) { + dst = { + static_cast((src.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA), + static_cast((src.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA), + static_cast((src.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA), + static_cast(outA * 255) + }; + } +} +// 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 - for (std::int_fast32_t x = element->scaled.x; x - element->scaled.x < element->scaled.width; x++) { - for (std::int_fast32_t y = element->scaled.y; y - element->scaled.y < element->scaled.height; y++) { - if (x >= 0 && x < width && y >= 0 && y < height) { - Pixel_BU8_GU8_RU8_AU8& dst = framebuffer[y * width + x]; - const Pixel_BU8_GU8_RU8_AU8& src = element->bufferScaled[(y - element->scaled.y) * element->scaled.width + (x - element->scaled.x)]; - - float srcA = src.a / 255.0f; - float dstA = dst.a / 255.0f; - - float outA = srcA + dstA * (1.0f - srcA); - if (outA > 0.0f) { - dst = { - static_cast((src.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA), - static_cast((src.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA), - static_cast((src.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA), - static_cast(outA * 255) - }; - } + + // 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 + 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; + + // 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; + + // Bounds check for source buffer + if (src_x >= 0 && src_x < static_cast(src_width) && src_y >= 0 && src_y < static_cast(src_height)) { + + // Get pixel indices + std::uint_fast32_t dst_idx = y * width + x; + std::uint_fast32_t src_idx = src_y * src_width + src_x; + + // Blend pixels + blend_pixel_optimized(framebuffer[dst_idx], src_buffer[src_idx]); } } } + #ifdef CRAFTER_TIMING auto end = std::chrono::high_resolution_clock::now(); renderTimings.push_back({element, element->scaled.width, element->scaled.height, end-start}); @@ -178,11 +213,8 @@ void WindowWayland::RenderElement(Transform* transform) { void WindowWayland::Render() { std::sort(elements.begin(), elements.end(), [](Transform* a, Transform* b){ return a->z < b->z; }); - for (std::uint_fast32_t x = 0; x < width; x++) { - for (std::uint_fast32_t y = 0; y < height; y++) { - framebuffer[y * width + x] = {0,0,0,0}; - } - } + // Clear screen efficiently using memset + memset(framebuffer, 0, width * height * sizeof(Pixel_BU8_GU8_RU8_AU8)); for(Transform* child : elements) { RenderElement(child); diff --git a/interfaces/Crafter.Graphics-Animation.cppm b/interfaces/Crafter.Graphics-Animation.cppm index 31d24c5..b1e88ac 100644 --- a/interfaces/Crafter.Graphics-Animation.cppm +++ b/interfaces/Crafter.Graphics-Animation.cppm @@ -61,12 +61,27 @@ namespace Crafter { std::vector> keyframes; std::uint_fast32_t currentFrame; std::chrono::time_point startedAt; - Animation(std::vector>&& keyframes) : keyframes(std::move(keyframes)) {} + mutable T cachedValue; // Cache the last computed value + mutable std::chrono::time_point lastTime; // Track when cached value was computed + + Animation(std::vector>&& keyframes) : keyframes(std::move(keyframes)), currentFrame(0) {} + void Start(std::chrono::time_point time) { currentFrame = 0; startedAt = time; + // Invalidate cache + lastTime = std::chrono::time_point(); } + T Play(std::chrono::time_point time) { + // Check if we can reuse cached value + if (lastTime != std::chrono::time_point()) { + // Only use cached value if we haven't moved forward in time + if (time <= lastTime) { + return cachedValue; + } + } + std::chrono::duration elapsed = time - startedAt; // elapsed time since animation started std::chrono::duration accumulated(0); @@ -77,17 +92,21 @@ namespace Crafter { auto t = (elapsed - frameStartTime) / keyframes[i+1].duration.count(); currentFrame = i; - return LerpTuple( + cachedValue = LerpTuple( keyframes[i].values, keyframes[i + 1].values, t.count() ); + lastTime = time; + return cachedValue; } } // If we get here, we're past the last keyframe currentFrame = keyframes.size() - 1; - return keyframes.back().values; + cachedValue = keyframes.back().values; + lastTime = time; + return cachedValue; } }; } \ No newline at end of file