From f6c4eeb9f31b718317cb9a6d23d40bfc471f76d0 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Tue, 27 Jan 2026 12:40:39 +0300 Subject: [PATCH] Reorganize cache code and call flushAndSubmit in main --- src/DrawingProgram/DrawingProgram.cpp | 22 +------------ src/DrawingProgram/DrawingProgram.hpp | 4 --- src/DrawingProgram/DrawingProgramCache.cpp | 38 +++++++++++++++++----- src/DrawingProgram/DrawingProgramCache.hpp | 7 +++- src/Toolbar.cpp | 12 +++---- src/main.cpp | 2 +- 6 files changed, 43 insertions(+), 42 deletions(-) diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index f1dde26..c065f40 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -36,9 +36,6 @@ #include "Tools/EraserTool.hpp" -size_t DrawingProgram::MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH = 33; // Around 30FPS -size_t DrawingProgram::MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH = 400; - DrawingProgram::DrawingProgram(World& initWorld): world(initWorld), drawCache(*this), @@ -238,23 +235,6 @@ void DrawingProgram::modify_grid(const NetworkingObjects::NetObjWeakPtr std::chrono::milliseconds(MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH)) { - if(!badFrametimeTimePoint) - badFrametimeTimePoint = std::chrono::steady_clock::now(); - else if(unorderedObjectsExistTimePoint && std::chrono::steady_clock::now() - badFrametimeTimePoint.value() >= std::chrono::milliseconds(MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH) && std::chrono::steady_clock::now() - unorderedObjectsExistTimePoint.value() >= std::chrono::milliseconds(MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH)) { - rebuild_cache(); - unorderedObjectsExistTimePoint = std::nullopt; - badFrametimeTimePoint = std::nullopt; - } - } - else - badFrametimeTimePoint = std::nullopt; - - if(!drawCache.unsorted_components_exist()) - unorderedObjectsExistTimePoint = std::nullopt; - else if(!unorderedObjectsExistTimePoint) - unorderedObjectsExistTimePoint = std::chrono::steady_clock::now(); - tool_temporary_switch_update(); controls.cursorHoveringOverCanvas = !world.main.toolbar.io->hoverObstructed; @@ -340,7 +320,7 @@ void DrawingProgram::update() { update_downloading_dropped_files(); check_updateable_components(); - if(drawCache.should_rebuild()) + if(drawCache.check_rebuild_needed_from_framerate() || drawCache.should_rebuild()) rebuild_cache(); } diff --git a/src/DrawingProgram/DrawingProgram.hpp b/src/DrawingProgram/DrawingProgram.hpp index 86a8632..094b2ca 100644 --- a/src/DrawingProgram/DrawingProgram.hpp +++ b/src/DrawingProgram/DrawingProgram.hpp @@ -22,8 +22,6 @@ class World; class DrawingProgram { public: - static size_t MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH; - static size_t MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH; DrawingProgram(World& initWorld); void server_init_no_file(); void toolbar_gui(); @@ -65,8 +63,6 @@ class DrawingProgram { void check_updateable_components(); void update_downloading_dropped_files(); - std::optional badFrametimeTimePoint; - std::optional unorderedObjectsExistTimePoint; bool selection_action_menu(Vector2f popupPos); void rebuild_cache(); diff --git a/src/DrawingProgram/DrawingProgramCache.cpp b/src/DrawingProgram/DrawingProgramCache.cpp index 34fd12a..3bec0dc 100644 --- a/src/DrawingProgram/DrawingProgramCache.cpp +++ b/src/DrawingProgram/DrawingProgramCache.cpp @@ -13,8 +13,14 @@ size_t DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD = 1000; size_t DrawingProgramCache::MAXIMUM_COMPONENTS_IN_SINGLE_NODE = 50; -size_t DrawingProgramCache::MAXIMUM_DRAW_CACHE_SURFACES = 40; +#ifdef __EMSCRIPTEN__ + size_t DrawingProgramCache::MAXIMUM_DRAW_CACHE_SURFACES = 40; // Use less VRAM in web build +#else + size_t DrawingProgramCache::MAXIMUM_DRAW_CACHE_SURFACES = 96; +#endif size_t DrawingProgramCache::CACHE_NODE_RESOLUTION = 2048; +size_t DrawingProgramCache::MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH = 33; // Around 30FPS +size_t DrawingProgramCache::MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH = 5000; // Should be a bit long to prevent objects that are being updated, like brush strokes, from constantly refreshing the cache. std::unordered_map, DrawingProgramCache::NodeCache> DrawingProgramCache::nodeCacheMap; DrawingProgramCache::WindowCache DrawingProgramCache::windowCache; @@ -64,8 +70,25 @@ bool DrawingProgramCache::should_rebuild() const { return (unsortedComponents.size() >= MINIMUM_COMPONENTS_TO_START_REBUILD); } -bool DrawingProgramCache::unsorted_components_exist() const { - return !unsortedComponents.empty(); +bool DrawingProgramCache::check_rebuild_needed_from_framerate() { + if(drawP.world.main.window.lastFrameTime > std::chrono::milliseconds(MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH)) { + if(!badFrametimeTimePoint) + badFrametimeTimePoint = std::chrono::steady_clock::now(); + else if(unorderedObjectsExistTimePoint && std::chrono::steady_clock::now() - badFrametimeTimePoint.value() >= std::chrono::milliseconds(MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH) && std::chrono::steady_clock::now() - unorderedObjectsExistTimePoint.value() >= std::chrono::milliseconds(MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH)) { + unorderedObjectsExistTimePoint = std::nullopt; + badFrametimeTimePoint = std::nullopt; + return true; + } + } + else + badFrametimeTimePoint = std::nullopt; + + if(unsortedComponents.empty()) + unorderedObjectsExistTimePoint = std::nullopt; + else if(!unorderedObjectsExistTimePoint) + unorderedObjectsExistTimePoint = std::chrono::steady_clock::now(); + + return false; } void DrawingProgramCache::build(const std::unordered_set& objsToExclude) { @@ -202,18 +225,13 @@ void DrawingProgramCache::build_bvh_node_coords_and_resolution(DrawingProgramCac } 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) { if(node && node->coords.inverseScale <= drawData.cam.c.inverseScale) { - auto it = nodeCacheMap.find(node); - if((it == nodeCacheMap.end()) || it->second.invalidBounds.has_value()) - nodeFlatList.emplace_front(node); + refresh_draw_cache(node, drawData); return false; } return true; }); - if(!nodeFlatList.empty()) - refresh_draw_cache(nodeFlatList.front(), drawData); } void DrawingProgramCache::traverse_bvh_run_function(const SCollision::AABB& aabb, std::function& node)> f) { @@ -243,6 +261,8 @@ void DrawingProgramCache::refresh_draw_cache(const std::shared_ptrsecond; + if(!nodeCache.invalidBounds.has_value()) // Node is cached already, and doesn't have invalid areas, so there's nothing to do + return; nodeCacheMap.erase(bvhNode); // Ensure nodeCache is erased, so that the draw_components_to_canvas function doesnt use it while drawing } else { diff --git a/src/DrawingProgram/DrawingProgramCache.hpp b/src/DrawingProgram/DrawingProgramCache.hpp index c7ebe45..0258230 100644 --- a/src/DrawingProgram/DrawingProgramCache.hpp +++ b/src/DrawingProgram/DrawingProgramCache.hpp @@ -19,6 +19,8 @@ class DrawingProgramCache { static size_t MAXIMUM_COMPONENTS_IN_SINGLE_NODE; static size_t MAXIMUM_DRAW_CACHE_SURFACES; static size_t CACHE_NODE_RESOLUTION; + static size_t MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH; + static size_t MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH; DrawingProgramCache(DrawingProgram& initDrawP); void add_component(CanvasComponentContainer::ObjInfo* c); @@ -32,9 +34,9 @@ class DrawingProgramCache { void node_loop_erase_if_components(const std::shared_ptr& bvhNode, std::function f); void node_loop_components(const std::shared_ptr& bvhNode, std::function f); bool should_rebuild() const; + bool check_rebuild_needed_from_framerate(); void update_and_draw_cached_canvas(SkCanvas* canvas, const DrawData& drawData); void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::optional>& drawBounds); - bool unsorted_components_exist() const; void invalidate_cache_at_aabb(const SCollision::AABB& aabb); void set_clear_window_cache(); @@ -69,6 +71,9 @@ class DrawingProgramCache { void draw_cache_image_to_canvas(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& bvhNode); void recursive_draw_layer_item_to_canvas(const DrawingProgramLayerListItem& layerListItem, SkCanvas* canvas, const DrawData& drawData, const std::optional>& drawBounds, const std::vector>& nodesToDraw); + std::optional badFrametimeTimePoint; + std::optional unorderedObjectsExistTimePoint; + std::shared_ptr bvhRoot; std::vector unsortedComponents; DrawingProgram& drawP; diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index 74117fd..4bed725 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -285,8 +285,8 @@ nlohmann::json Toolbar::get_config_json() { debugJson["maxCacheNodes"] = DrawingProgramCache::MAXIMUM_DRAW_CACHE_SURFACES; debugJson["maxComponentsInNode"] = DrawingProgramCache::MAXIMUM_COMPONENTS_IN_SINGLE_NODE; debugJson["componentCountToForceCacheRebuild"] = DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD; - debugJson["maximumFrameTimeToForceCacheRebuild"] = DrawingProgram::MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH; - debugJson["millisecondMinimumTimeToCheckForCacheRebuild"] = DrawingProgram::MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH; + debugJson["maximumFrameTimeToForceCacheRebuild"] = DrawingProgramCache::MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH; + debugJson["millisecondMinimumTimeToCheckForCacheRebuild"] = DrawingProgramCache::MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH; toRet["debug"] = debugJson; return toRet; @@ -354,8 +354,8 @@ void Toolbar::set_config_json(const nlohmann::json& j, VersionNumber version) { try{j.at("debug").at("maxCacheNodes").get_to(DrawingProgramCache::MAXIMUM_DRAW_CACHE_SURFACES);} catch(...) {} try{j.at("debug").at("maxComponentsInNode").get_to(DrawingProgramCache::MAXIMUM_COMPONENTS_IN_SINGLE_NODE);} catch(...) {} try{j.at("debug").at("componentCountToForceCacheRebuild").get_to(DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD);} catch(...) {} - try{j.at("debug").at("maximumFrameTimeToForceCacheRebuild").get_to(DrawingProgram::MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH);} catch(...) {} - try{j.at("debug").at("millisecondMinimumTimeToCheckForCacheRebuild").get_to(DrawingProgram::MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH);} catch(...) {} + try{j.at("debug").at("maximumFrameTimeToForceCacheRebuild").get_to(DrawingProgramCache::MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH);} catch(...) {} + try{j.at("debug").at("millisecondMinimumTimeToCheckForCacheRebuild").get_to(DrawingProgramCache::MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH);} catch(...) {} main.update_display_names(); } @@ -1860,8 +1860,8 @@ void Toolbar::options_menu() { gui.text_label_light("Cache max VRAM consumption (MB): " + std::to_string(cacheVRAMConsumptionInMB)); gui.input_scalar_field("max components in node", "Maximum components in single node", &DrawingProgramCache::MAXIMUM_COMPONENTS_IN_SINGLE_NODE, 2, 10000); gui.input_scalar_field("components to force cache rebuild", "Number of components to force cache rebuild", &DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD, 1, 1000000); - gui.input_scalar_field("maximum frame time to force cache rebuild", "Maximum frame time to force cache rebuild (ms)", &DrawingProgram::MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH, 1, 1000000); - gui.input_scalar_field("minimum time to force cache rebuild", "Minimum time to check cache rebuild (ms)", &DrawingProgram::MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH, 1, 1000000); + gui.input_scalar_field("maximum frame time to force cache rebuild", "Maximum frame time to force cache rebuild (ms)", &DrawingProgramCache::MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH, 1, 1000000); + gui.input_scalar_field("minimum time to force cache rebuild", "Minimum time to check cache rebuild (ms)", &DrawingProgramCache::MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH, 1, 1000000); gui.pop_id(); break; } diff --git a/src/main.cpp b/src/main.cpp index 2bafa8a..6342b71 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -608,7 +608,7 @@ SDL_AppResult SDL_AppIterate(void *appstate) { mS.canvas->drawImage(mS.intermediateSurface->makeTemporaryImage(), 0, 0); - mS.ctx->flush(); + mS.ctx->flushAndSubmit(); SDL_GL_SwapWindow(mS.window); #endif