From 81ac5a23020562ea3f05406251cda6724dd0be24 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Sun, 3 Aug 2025 12:31:30 +0300 Subject: [PATCH] Cache surfaces only update in locations that require updating --- include/Helpers/SCollision.hpp | 7 ++ src/DrawingProgram/DrawingProgramCache.cpp | 85 +++++++++++++++------- src/DrawingProgram/DrawingProgramCache.hpp | 6 +- 3 files changed, 67 insertions(+), 31 deletions(-) diff --git a/include/Helpers/SCollision.hpp b/include/Helpers/SCollision.hpp index c56af14..85abbbb 100644 --- a/include/Helpers/SCollision.hpp +++ b/include/Helpers/SCollision.hpp @@ -27,6 +27,13 @@ namespace SCollision { Vector dim() const { return max - min; } T width() const { return max.x() - min.x(); } T height() const { return max.y() - min.y(); } + // Do not try to get intersection between AABBs unless you know they collide + AABB get_intersection_between_aabbs(const AABB& other) { + return { + cwise_vec_max(other.min, min), + cwise_vec_min(other.max, max) + }; + } void include_aabb_in_bounds(const AABB& other) { min = cwise_vec_min(other.min, min); max = cwise_vec_max(other.max, max); diff --git a/src/DrawingProgram/DrawingProgramCache.cpp b/src/DrawingProgram/DrawingProgramCache.cpp index 6e04dd2..ca17de7 100644 --- a/src/DrawingProgram/DrawingProgramCache.cpp +++ b/src/DrawingProgram/DrawingProgramCache.cpp @@ -4,6 +4,7 @@ #include "../MainProgram.hpp" #include "Helpers/Parallel.hpp" #include +#include #ifdef USE_SKIA_BACKEND_GRAPHITE #include @@ -268,8 +269,20 @@ void DrawingProgramCache::invalidate_cache_at_optional_aabb_before_pos(const std void DrawingProgramCache::invalidate_cache_at_aabb_before_pos(const SCollision::AABB& aabb, uint64_t placementToInvalidateAt) { std::erase_if(nodesWithCachedSurfaces, [&](auto& n) { if(placementToInvalidateAt <= n->drawCache.value().lastDrawnComponentPlacement && SCollision::collide(aabb, n->bounds)) { - n->drawCache = std::nullopt; - return true; + if(aabb.fully_contains_aabb(n->bounds)) { + n->drawCache = std::nullopt; + return true; + } + else { + auto& dCache = n->drawCache.value(); + if(dCache.invalidBounds.has_value()) { + auto& iBounds = dCache.invalidBounds.value(); + iBounds.include_aabb_in_bounds(aabb); + iBounds = n->bounds.get_intersection_between_aabbs(iBounds); + } + else + dCache.invalidBounds = n->bounds.get_intersection_between_aabbs(aabb); + } } return false; }); @@ -279,13 +292,8 @@ void DrawingProgramCache::refresh_all_draw_cache(const DrawData& drawData) { std::deque> nodeFlatList; // We want to render children before parents, so that parents can make use of the cached children traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](std::shared_ptr node, const std::vector& comps) { if(node && node->coords.inverseScale <= drawData.cam.c.inverseScale) { - if(!node->drawCache) { + if(!node->drawCache || node->drawCache.value().invalidBounds) nodeFlatList.emplace_front(node); - for(auto& nodeChild : node->children) { - if(!nodeChild->drawCache) - nodeFlatList.emplace_front(nodeChild); - } - } return false; } return true; @@ -298,23 +306,24 @@ void DrawingProgramCache::refresh_draw_cache(const std::shared_ptrchildren.empty() && bvhNode->components.empty()) return; - bvhNode->drawCache = std::nullopt; DrawingProgramCacheBVHNode::DrawCacheData drawCache; + if(bvhNode->drawCache) { + drawCache = bvhNode->drawCache.value(); + bvhNode->drawCache = std::nullopt; // Ensure drawCache is nullopt, so that the draw_components_to_canvas function doesnt use it while drawing + } + else { + SkImageInfo imgInfo = SkImageInfo::MakeN32Premul(bvhNode->resolution.x(), bvhNode->resolution.y()); + #ifdef USE_SKIA_BACKEND_GRAPHITE + drawCache.surface = SkSurfaces::RenderTarget(drawP.world.main.window.recorder(), imgInfo, skgpu::Mipmapped::kNo, drawP.world.main.window.defaultMSAASurfaceProps); + #elif USE_SKIA_BACKEND_GANESH + drawCache.surface = SkSurfaces::RenderTarget(drawP.world.main.window.ctx.get(), skgpu::Budgeted::kNo, imgInfo, drawP.world.main.window.defaultMSAASampleCount, &drawP.world.main.window.defaultMSAASurfaceProps); + #endif - SkImageInfo imgInfo = SkImageInfo::MakeN32Premul(bvhNode->resolution.x(), bvhNode->resolution.y()); - sk_sp drawCacheSurface; - #ifdef USE_SKIA_BACKEND_GRAPHITE - drawCacheSurface = SkSurfaces::RenderTarget(drawP.world.main.window.recorder(), imgInfo, skgpu::Mipmapped::kNo, drawP.world.main.window.defaultMSAASurfaceProps); - #elif USE_SKIA_BACKEND_GANESH - drawCacheSurface = SkSurfaces::RenderTarget(drawP.world.main.window.ctx.get(), skgpu::Budgeted::kNo, imgInfo, drawP.world.main.window.defaultMSAASampleCount, &drawP.world.main.window.defaultMSAASurfaceProps); - #endif + if(!drawCache.surface) + throw std::runtime_error("[DrawingProgramCache::refresh_draw_cache] Could not make cache surface"); + } - if(!drawCacheSurface) - throw std::runtime_error("[DrawingProgramCache::refresh_draw_cache] Could not make cache surface"); - - SkCanvas* cacheCanvas = drawCacheSurface->getCanvas(); - - cacheCanvas->clear(SkColor4f{0, 0, 0, 0}); + SkCanvas* cacheCanvas = drawCache.surface->getCanvas(); DrawData cacheDrawData = drawData; cacheDrawData.cam.c = bvhNode->coords; @@ -322,8 +331,28 @@ void DrawingProgramCache::refresh_draw_cache(const std::shared_ptrresolution.cast()); cacheDrawData.refresh_draw_optimizing_values(); - draw_components_to_canvas(cacheCanvas, cacheDrawData, &drawCache.lastDrawnComponentPlacement); - drawCache.img = drawCacheSurface->makeImageSnapshot()->withDefaultMipmaps(); + if(drawCache.invalidBounds) { + auto& iBounds = drawCache.invalidBounds.value(); + WorldVec bDim = bvhNode->bounds.dim(); + //std::cout << "\niMin: " << iBounds.min.x() << " " << iBounds.min.y() << "\niMax: " << iBounds.max.x() << " " << iBounds.max.y() << "\nbMin: " << bvhNode->bounds.min.x() << " " << bvhNode->bounds.min.y() << "\nbMax: " << bvhNode->bounds.max.x() << " " << bvhNode->bounds.max.y() << "\nbDim: " << bDim.x() << " " << bDim.y(); + Vector2f clipBoundMin{static_cast((iBounds.min.x() - bvhNode->bounds.min.x()) / bDim.x()) * bvhNode->resolution.x(), + static_cast((iBounds.min.y() - bvhNode->bounds.min.y()) / bDim.y()) * bvhNode->resolution.y()}; + Vector2f clipBoundMax{static_cast((iBounds.max.x() - bvhNode->bounds.min.x()) / bDim.x()) * bvhNode->resolution.x(), + static_cast((iBounds.max.y() - bvhNode->bounds.min.y()) / bDim.y()) * bvhNode->resolution.y()}; + SkRect clipRect = SkRect::MakeLTRB(clipBoundMin.x(), clipBoundMin.y(), clipBoundMax.x(), clipBoundMax.y()); + //std::cout << "\ncMin: " << clipBoundMin.x() << " " << clipBoundMin.y() << "\ncMax: " << clipBoundMax.x() << " " << clipBoundMax.y() << std::endl; + cacheCanvas->save(); + cacheCanvas->clipRect(clipRect); + cacheCanvas->clear(SkColor4f{0, 0, 0, 0}); + draw_components_to_canvas(cacheCanvas, cacheDrawData, &drawCache.lastDrawnComponentPlacement, drawCache.invalidBounds); + cacheCanvas->restore(); + drawCache.invalidBounds = std::nullopt; + } + else { + cacheCanvas->clear(SkColor4f{0, 0, 0, 0}); + draw_components_to_canvas(cacheCanvas, cacheDrawData, &drawCache.lastDrawnComponentPlacement, drawCache.invalidBounds); + } + drawCache.lastRenderTime = std::chrono::steady_clock::now(); drawCache.attachedCache = this; @@ -342,14 +371,14 @@ void DrawingProgramCache::refresh_draw_cache(const std::shared_ptr> drawBounds) { uint64_t lastComponentDrawn = 0; std::vector> cachedNodesToDraw; std::vector uncachedCompsToDraw; - traverse_bvh_run_function(drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr& node, const std::vector& comps) { - if(node && node->drawCache && node->coords.inverseScale <= drawData.cam.c.inverseScale) { + traverse_bvh_run_function(drawBounds.has_value() ? drawBounds.value() : drawData.cam.viewingAreaGenerousCollider, [&](const std::shared_ptr& node, const std::vector& comps) { + if(node && node->drawCache && !node->drawCache.value().invalidBounds && node->coords.inverseScale <= drawData.cam.c.inverseScale) { cachedNodesToDraw.emplace_back(node); return false; } @@ -396,7 +425,7 @@ uint64_t DrawingProgramCache::draw_cache_image_to_canvas(SkCanvas* canvas, const SkPaint srcPaint; srcPaint.setBlendMode(SkBlendMode::kSrc); - canvas->drawImage(drawCache.img, 0, 0, {SkFilterMode::kLinear, SkMipmapMode::kLinear}, &srcPaint); + canvas->drawImage(drawCache.surface->makeTemporaryImage(), 0, 0, {SkFilterMode::kLinear, SkMipmapMode::kLinear}, &srcPaint); canvas->restore(); diff --git a/src/DrawingProgram/DrawingProgramCache.hpp b/src/DrawingProgram/DrawingProgramCache.hpp index c1c3c22..1c1c538 100644 --- a/src/DrawingProgram/DrawingProgramCache.hpp +++ b/src/DrawingProgram/DrawingProgramCache.hpp @@ -19,10 +19,10 @@ class DrawingProgramCacheBVHNode { Vector2i resolution; struct DrawCacheData { uint64_t lastDrawnComponentPlacement; - uint64_t firstDrawnComponentPlacement; - sk_sp img; + sk_sp surface; std::chrono::steady_clock::time_point lastRenderTime; DrawingProgramCache* attachedCache; + std::optional> invalidBounds; }; std::optional drawCache; @@ -57,7 +57,7 @@ class DrawingProgramCache { void invalidate_cache_at_aabb_before_pos(const SCollision::AABB& aabb, uint64_t placementToInvalidateAt); void invalidate_cache_at_optional_aabb_before_pos(const std::optional>& aabb, uint64_t placementToInvalidateAt); void clear(); - void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, uint64_t* lastDrawnComponentPlacement = nullptr); + void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, uint64_t* lastDrawnComponentPlacement = nullptr, std::optional> drawBounds = std::nullopt); const std::vector& get_unsorted_component_list() const; const std::chrono::steady_clock::time_point& get_last_bvh_build_time() const; void clear_own_cached_surfaces();